USB patches for 3.10-rc1
Here's the big USB pull request for 3.10-rc1. Lots of USB patches here, the majority being USB gadget changes and USB-serial driver cleanups, the rest being ARM build fixes / cleanups, and individual driver updates. We also finally got some chipidea fixes, which have been delayed for a number of kernel releases, as the maintainer has now reappeared. All of these have been in linux-next for a while. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.19 (GNU/Linux) iEYEABECAAYFAlF+md4ACgkQMUfUDdst+ymkSgCfZWIiCtiX/li0yJqSiRB4yYJx Ex0AoNemOOf6ywvSOHPbILTbJ1G+c/PX =JmvB -----END PGP SIGNATURE----- Merge tag 'usb-3.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB patches from Greg Kroah-Hartman: "Here's the big USB pull request for 3.10-rc1. Lots of USB patches here, the majority being USB gadget changes and USB-serial driver cleanups, the rest being ARM build fixes / cleanups, and individual driver updates. We also finally got some chipidea fixes, which have been delayed for a number of kernel releases, as the maintainer has now reappeared. All of these have been in linux-next for a while" * tag 'usb-3.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (568 commits) USB: ehci-msm: USB_MSM_OTG needs USB_PHY USB: OHCI: avoid conflicting platform drivers USB: OMAP: ISP1301 needs USB_PHY USB: lpc32xx: ISP1301 needs USB_PHY USB: ftdi_sio: enable two UART ports on ST Microconnect Lite usb: phy: tegra: don't call into tegra-ehci directly usb: phy: phy core cannot yet be a module USB: Fix initconst in ehci driver usb-storage: CY7C68300A chips do not support Cypress ATACB USB: serial: option: Added support Olivetti Olicard 145 USB: ftdi_sio: correct ST Micro Connect Lite PIDs ARM: mxs_defconfig: add CONFIG_USB_PHY ARM: imx_v6_v7_defconfig: add CONFIG_USB_PHY usb: phy: remove exported function from __init section usb: gadget: zero: put function instances on unbind usb: gadget: f_sourcesink.c: correct a copy-paste misnomer usb: gadget: cdc2: fix error return code in cdc_do_config() usb: gadget: multi: fix error return code in rndis_do_config() usb: gadget: f_obex: fix error return code in obex_bind() USB: storage: convert to use module_usb_driver() ...
This commit is contained in:
Коммит
ec25e246b9
|
@ -32,7 +32,7 @@ Date: January 2008
|
|||
KernelVersion: 2.6.25
|
||||
Contact: Sarah Sharp <sarah.a.sharp@intel.com>
|
||||
Description:
|
||||
If CONFIG_PM and CONFIG_USB_SUSPEND are enabled, then this file
|
||||
If CONFIG_PM_RUNTIME is enabled then this file
|
||||
is present. When read, it returns the total time (in msec)
|
||||
that the USB device has been connected to the machine. This
|
||||
file is read-only.
|
||||
|
@ -45,7 +45,7 @@ Date: January 2008
|
|||
KernelVersion: 2.6.25
|
||||
Contact: Sarah Sharp <sarah.a.sharp@intel.com>
|
||||
Description:
|
||||
If CONFIG_PM and CONFIG_USB_SUSPEND are enabled, then this file
|
||||
If CONFIG_PM_RUNTIME is enabled then this file
|
||||
is present. When read, it returns the total time (in msec)
|
||||
that the USB device has been active, i.e. not in a suspended
|
||||
state. This file is read-only.
|
||||
|
@ -187,7 +187,7 @@ What: /sys/bus/usb/devices/.../power/usb2_hardware_lpm
|
|||
Date: September 2011
|
||||
Contact: Andiry Xu <andiry.xu@amd.com>
|
||||
Description:
|
||||
If CONFIG_USB_SUSPEND is set and a USB 2.0 lpm-capable device
|
||||
If CONFIG_PM_RUNTIME is set and a USB 2.0 lpm-capable device
|
||||
is plugged in to a xHCI host which support link PM, it will
|
||||
perform a LPM test; if the test is passed and host supports
|
||||
USB2 hardware LPM (xHCI 1.0 feature), USB2 hardware LPM will
|
||||
|
|
|
@ -11,6 +11,7 @@ Optional properties:
|
|||
that indicate usb controller index
|
||||
- vbus-supply: regulator for vbus
|
||||
- disable-over-current: disable over current detect
|
||||
- external-vbus-divider: enables off-chip resistor divider for Vbus
|
||||
|
||||
Examples:
|
||||
usb@02184000 { /* USB OTG */
|
||||
|
@ -20,4 +21,5 @@ usb@02184000 { /* USB OTG */
|
|||
fsl,usbphy = <&usbphy1>;
|
||||
fsl,usbmisc = <&usbmisc 0>;
|
||||
disable-over-current;
|
||||
external-vbus-divider;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
OMAP HS USB EHCI controller
|
||||
|
||||
This device is usually the child of the omap-usb-host
|
||||
Documentation/devicetree/bindings/mfd/omap-usb-host.txt
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should be "ti,ehci-omap"
|
||||
- reg: should contain one register range i.e. start and length
|
||||
- interrupts: description of the interrupt line
|
||||
|
||||
Optional properties:
|
||||
|
||||
- phys: list of phandles to PHY nodes.
|
||||
This property is required if at least one of the ports are in
|
||||
PHY mode i.e. OMAP_EHCI_PORT_MODE_PHY
|
||||
|
||||
To specify the port mode, see
|
||||
Documentation/devicetree/bindings/mfd/omap-usb-host.txt
|
||||
|
||||
Example for OMAP4:
|
||||
|
||||
usbhsehci: ehci@4a064c00 {
|
||||
compatible = "ti,ehci-omap", "usb-ehci";
|
||||
reg = <0x4a064c00 0x400>;
|
||||
interrupts = <0 77 0x4>;
|
||||
};
|
||||
|
||||
&usbhsehci {
|
||||
phys = <&hsusb1_phy 0 &hsusb3_phy>;
|
||||
};
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
OMAP HS USB OHCI controller (OMAP3 and later)
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should be "ti,ohci-omap3"
|
||||
- reg: should contain one register range i.e. start and length
|
||||
- interrupts: description of the interrupt line
|
||||
|
||||
Example for OMAP4:
|
||||
|
||||
usbhsohci: ohci@4a064800 {
|
||||
compatible = "ti,ohci-omap3", "usb-ohci";
|
||||
reg = <0x4a064800 0x400>;
|
||||
interrupts = <0 76 0x4>;
|
||||
};
|
|
@ -8,10 +8,10 @@ OMAP MUSB GLUE
|
|||
and disconnect.
|
||||
- multipoint : Should be "1" indicating the musb controller supports
|
||||
multipoint. This is a MUSB configuration-specific setting.
|
||||
- num_eps : Specifies the number of endpoints. This is also a
|
||||
- num-eps : Specifies the number of endpoints. This is also a
|
||||
MUSB configuration-specific setting. Should be set to "16"
|
||||
- ram_bits : Specifies the ram address size. Should be set to "12"
|
||||
- interface_type : This is a board specific setting to describe the type of
|
||||
- ram-bits : Specifies the ram address size. Should be set to "12"
|
||||
- interface-type : This is a board specific setting to describe the type of
|
||||
interface between the controller and the phy. It should be "0" or "1"
|
||||
specifying ULPI and UTMI respectively.
|
||||
- mode : Should be "3" to represent OTG. "1" signifies HOST and "2"
|
||||
|
@ -29,18 +29,46 @@ usb_otg_hs: usb_otg_hs@4a0ab000 {
|
|||
ti,hwmods = "usb_otg_hs";
|
||||
ti,has-mailbox;
|
||||
multipoint = <1>;
|
||||
num_eps = <16>;
|
||||
ram_bits = <12>;
|
||||
num-eps = <16>;
|
||||
ram-bits = <12>;
|
||||
ctrl-module = <&omap_control_usb>;
|
||||
};
|
||||
|
||||
Board specific device node entry
|
||||
&usb_otg_hs {
|
||||
interface_type = <1>;
|
||||
interface-type = <1>;
|
||||
mode = <3>;
|
||||
power = <50>;
|
||||
};
|
||||
|
||||
OMAP DWC3 GLUE
|
||||
- compatible : Should be "ti,dwc3"
|
||||
- ti,hwmods : Should be "usb_otg_ss"
|
||||
- reg : Address and length of the register set for the device.
|
||||
- interrupts : The irq number of this device that is used to interrupt the
|
||||
MPU
|
||||
- #address-cells, #size-cells : Must be present if the device has sub-nodes
|
||||
- utmi-mode : controls the source of UTMI/PIPE status for VBUS and OTG ID.
|
||||
It should be set to "1" for HW mode and "2" for SW mode.
|
||||
- ranges: the child address space are mapped 1:1 onto the parent address space
|
||||
|
||||
Sub-nodes:
|
||||
The dwc3 core should be added as subnode to omap dwc3 glue.
|
||||
- dwc3 :
|
||||
The binding details of dwc3 can be found in:
|
||||
Documentation/devicetree/bindings/usb/dwc3.txt
|
||||
|
||||
omap_dwc3 {
|
||||
compatible = "ti,dwc3";
|
||||
ti,hwmods = "usb_otg_ss";
|
||||
reg = <0x4a020000 0x1ff>;
|
||||
interrupts = <0 93 4>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
utmi-mode = <2>;
|
||||
ranges;
|
||||
};
|
||||
|
||||
OMAP CONTROL USB
|
||||
|
||||
Required properties:
|
||||
|
|
|
@ -1,20 +1,25 @@
|
|||
* Samsung's usb phy transceiver
|
||||
SAMSUNG USB-PHY controllers
|
||||
|
||||
The Samsung's phy transceiver is used for controlling usb phy for
|
||||
s3c-hsotg as well as ehci-s5p and ohci-exynos usb controllers
|
||||
across Samsung SOCs.
|
||||
** Samsung's usb 2.0 phy transceiver
|
||||
|
||||
The Samsung's usb 2.0 phy transceiver is used for controlling
|
||||
usb 2.0 phy for s3c-hsotg as well as ehci-s5p and ohci-exynos
|
||||
usb controllers across Samsung SOCs.
|
||||
TODO: Adding the PHY binding with controller(s) according to the under
|
||||
developement generic PHY driver.
|
||||
|
||||
Required properties:
|
||||
|
||||
Exynos4210:
|
||||
- compatible : should be "samsung,exynos4210-usbphy"
|
||||
- compatible : should be "samsung,exynos4210-usb2phy"
|
||||
- reg : base physical address of the phy registers and length of memory mapped
|
||||
region.
|
||||
- clocks: Clock IDs array as required by the controller.
|
||||
- clock-names: names of clock correseponding IDs clock property as requested
|
||||
by the controller driver.
|
||||
|
||||
Exynos5250:
|
||||
- compatible : should be "samsung,exynos5250-usbphy"
|
||||
- compatible : should be "samsung,exynos5250-usb2phy"
|
||||
- reg : base physical address of the phy registers and length of memory mapped
|
||||
region.
|
||||
|
||||
|
@ -44,12 +49,69 @@ Example:
|
|||
usbphy@125B0000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "samsung,exynos4210-usbphy";
|
||||
compatible = "samsung,exynos4210-usb2phy";
|
||||
reg = <0x125B0000 0x100>;
|
||||
ranges;
|
||||
|
||||
clocks = <&clock 2>, <&clock 305>;
|
||||
clock-names = "xusbxti", "otg";
|
||||
|
||||
usbphy-sys {
|
||||
/* USB device and host PHY_CONTROL registers */
|
||||
reg = <0x10020704 0x8>;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
** Samsung's usb 3.0 phy transceiver
|
||||
|
||||
Starting exynso5250, Samsung's SoC have usb 3.0 phy transceiver
|
||||
which is used for controlling usb 3.0 phy for dwc3-exynos usb 3.0
|
||||
controllers across Samsung SOCs.
|
||||
|
||||
Required properties:
|
||||
|
||||
Exynos5250:
|
||||
- compatible : should be "samsung,exynos5250-usb3phy"
|
||||
- reg : base physical address of the phy registers and length of memory mapped
|
||||
region.
|
||||
- clocks: Clock IDs array as required by the controller.
|
||||
- clock-names: names of clocks correseponding to IDs in the clock property
|
||||
as requested by the controller driver.
|
||||
|
||||
Optional properties:
|
||||
- #address-cells: should be '1' when usbphy node has a child node with 'reg'
|
||||
property.
|
||||
- #size-cells: should be '1' when usbphy node has a child node with 'reg'
|
||||
property.
|
||||
- ranges: allows valid translation between child's address space and parent's
|
||||
address space.
|
||||
|
||||
- The child node 'usbphy-sys' to the node 'usbphy' is for the system controller
|
||||
interface for usb-phy. It should provide the following information required by
|
||||
usb-phy controller to control phy.
|
||||
- reg : base physical address of PHY_CONTROL registers.
|
||||
The size of this register is the total sum of size of all PHY_CONTROL
|
||||
registers that the SoC has. For example, the size will be
|
||||
'0x4' in case we have only one PHY_CONTROL register (e.g.
|
||||
OTHERS register in S3C64XX or USB_PHY_CONTROL register in S5PV210)
|
||||
and, '0x8' in case we have two PHY_CONTROL registers (e.g.
|
||||
USBDEVICE_PHY_CONTROL and USBHOST_PHY_CONTROL registers in exynos4x).
|
||||
and so on.
|
||||
|
||||
Example:
|
||||
usbphy@12100000 {
|
||||
compatible = "samsung,exynos5250-usb3phy";
|
||||
reg = <0x12100000 0x100>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
|
||||
clocks = <&clock 1>, <&clock 286>;
|
||||
clock-names = "ext_xtal", "usbdrd30";
|
||||
|
||||
usbphy-sys {
|
||||
/* USB device and host PHY_CONTROL registers */
|
||||
reg = <0x10040704 0x8>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
USB NOP PHY
|
||||
|
||||
Required properties:
|
||||
- compatible: should be usb-nop-xceiv
|
||||
|
||||
Optional properties:
|
||||
- clocks: phandle to the PHY clock. Use as per Documentation/devicetree
|
||||
/bindings/clock/clock-bindings.txt
|
||||
This property is required if clock-frequency is specified.
|
||||
|
||||
- clock-names: Should be "main_clk"
|
||||
|
||||
- clock-frequency: the clock frequency (in Hz) that the PHY clock must
|
||||
be configured to.
|
||||
|
||||
- vcc-supply: phandle to the regulator that provides RESET to the PHY.
|
||||
|
||||
- reset-supply: phandle to the regulator that provides power to the PHY.
|
||||
|
||||
Example:
|
||||
|
||||
hsusb1_phy {
|
||||
compatible = "usb-nop-xceiv";
|
||||
clock-frequency = <19200000>;
|
||||
clocks = <&osc 0>;
|
||||
clock-names = "main_clk";
|
||||
vcc-supply = <&hsusb1_vcc_regulator>;
|
||||
reset-supply = <&hsusb1_reset_regulator>;
|
||||
};
|
||||
|
||||
hsusb1_phy is a NOP USB PHY device that gets its clock from an oscillator
|
||||
and expects that clock to be configured to 19.2MHz by the NOP PHY driver.
|
||||
hsusb1_vcc_regulator provides power to the PHY and hsusb1_reset_regulator
|
||||
controls RESET.
|
|
@ -131,6 +131,7 @@ Code Seq#(hex) Include File Comments
|
|||
'H' 40-4F sound/hdspm.h conflict!
|
||||
'H' 40-4F sound/hdsp.h conflict!
|
||||
'H' 90 sound/usb/usx2y/usb_stream.h
|
||||
'H' A0 uapi/linux/usb/cdc-wdm.h
|
||||
'H' C0-F0 net/bluetooth/hci.h conflict!
|
||||
'H' C0-DF net/bluetooth/hidp/hidp.h conflict!
|
||||
'H' C0-DF net/bluetooth/cmtp/cmtp.h conflict!
|
||||
|
|
|
@ -33,6 +33,10 @@ built with CONFIG_USB_SUSPEND enabled (which depends on
|
|||
CONFIG_PM_RUNTIME). System PM support is present only if the kernel
|
||||
was built with CONFIG_SUSPEND or CONFIG_HIBERNATION enabled.
|
||||
|
||||
(Starting with the 3.10 kernel release, dynamic PM support for USB is
|
||||
present whenever the kernel was built with CONFIG_PM_RUNTIME enabled.
|
||||
The CONFIG_USB_SUSPEND option has been eliminated.)
|
||||
|
||||
|
||||
What is Remote Wakeup?
|
||||
----------------------
|
||||
|
@ -206,10 +210,8 @@ initialized to 5. (The idle-delay values for already existing devices
|
|||
will not be affected.)
|
||||
|
||||
Setting the initial default idle-delay to -1 will prevent any
|
||||
autosuspend of any USB device. This is a simple alternative to
|
||||
disabling CONFIG_USB_SUSPEND and rebuilding the kernel, and it has the
|
||||
added benefit of allowing you to enable autosuspend for selected
|
||||
devices.
|
||||
autosuspend of any USB device. This has the benefit of allowing you
|
||||
then to enable autosuspend for selected devices.
|
||||
|
||||
|
||||
Warnings
|
||||
|
|
|
@ -549,6 +549,8 @@ config ARCH_IXP4XX
|
|||
select GENERIC_CLOCKEVENTS
|
||||
select MIGHT_HAVE_PCI
|
||||
select NEED_MACH_IO_H
|
||||
select USB_EHCI_BIG_ENDIAN_MMIO
|
||||
select USB_EHCI_BIG_ENDIAN_DESC
|
||||
help
|
||||
Support for Intel's IXP4XX (XScale) family of processors.
|
||||
|
||||
|
|
|
@ -188,6 +188,7 @@ CONFIG_USB_EHCI_HCD=y
|
|||
CONFIG_USB_EHCI_MXC=y
|
||||
CONFIG_USB_CHIPIDEA=y
|
||||
CONFIG_USB_CHIPIDEA_HOST=y
|
||||
CONFIG_USB_PHY=y
|
||||
CONFIG_USB_MXS_PHY=y
|
||||
CONFIG_USB_STORAGE=y
|
||||
CONFIG_MMC=y
|
||||
|
|
|
@ -134,6 +134,7 @@ CONFIG_SND_DEBUG_VERBOSE=y
|
|||
# CONFIG_SND_SPI is not set
|
||||
CONFIG_SND_SOC=y
|
||||
CONFIG_USB=y
|
||||
CONFIG_USB_PHY=y
|
||||
CONFIG_USB_OHCI_HCD=y
|
||||
CONFIG_USB_STORAGE=y
|
||||
CONFIG_USB_GADGET=y
|
||||
|
|
|
@ -120,6 +120,7 @@ CONFIG_USB_EHCI_HCD=y
|
|||
CONFIG_USB_CHIPIDEA=y
|
||||
CONFIG_USB_CHIPIDEA_HOST=y
|
||||
CONFIG_USB_STORAGE=y
|
||||
CONFIG_USB_PHY=y
|
||||
CONFIG_USB_MXS_PHY=y
|
||||
CONFIG_MMC=y
|
||||
CONFIG_MMC_MXS=y
|
||||
|
|
|
@ -195,6 +195,7 @@ CONFIG_SND_SOC=y
|
|||
CONFIG_SND_OMAP_SOC=y
|
||||
# CONFIG_USB_HID is not set
|
||||
CONFIG_USB=y
|
||||
CONFIG_USB_PHY=y
|
||||
CONFIG_USB_DEBUG=y
|
||||
CONFIG_USB_DEVICEFS=y
|
||||
# CONFIG_USB_DEVICE_CLASS is not set
|
||||
|
|
|
@ -204,9 +204,9 @@ static int exynos4210_usb_phy1_exit(struct platform_device *pdev)
|
|||
|
||||
int s5p_usb_phy_init(struct platform_device *pdev, int type)
|
||||
{
|
||||
if (type == S5P_USB_PHY_DEVICE)
|
||||
if (type == USB_PHY_TYPE_DEVICE)
|
||||
return exynos4210_usb_phy0_init(pdev);
|
||||
else if (type == S5P_USB_PHY_HOST)
|
||||
else if (type == USB_PHY_TYPE_HOST)
|
||||
return exynos4210_usb_phy1_init(pdev);
|
||||
|
||||
return -EINVAL;
|
||||
|
@ -214,9 +214,9 @@ int s5p_usb_phy_init(struct platform_device *pdev, int type)
|
|||
|
||||
int s5p_usb_phy_exit(struct platform_device *pdev, int type)
|
||||
{
|
||||
if (type == S5P_USB_PHY_DEVICE)
|
||||
if (type == USB_PHY_TYPE_DEVICE)
|
||||
return exynos4210_usb_phy0_exit(pdev);
|
||||
else if (type == S5P_USB_PHY_HOST)
|
||||
else if (type == USB_PHY_TYPE_HOST)
|
||||
return exynos4210_usb_phy1_exit(pdev);
|
||||
|
||||
return -EINVAL;
|
||||
|
|
|
@ -223,13 +223,7 @@ static struct pxa27x_keypad_platform_data aspenite_keypad_info __initdata = {
|
|||
};
|
||||
|
||||
#if defined(CONFIG_USB_EHCI_MV)
|
||||
static char *pxa168_sph_clock_name[] = {
|
||||
[0] = "PXA168-USBCLK",
|
||||
};
|
||||
|
||||
static struct mv_usb_platform_data pxa168_sph_pdata = {
|
||||
.clknum = 1,
|
||||
.clkname = pxa168_sph_clock_name,
|
||||
.mode = MV_USB_MODE_HOST,
|
||||
.phy_init = pxa_usb_phy_init,
|
||||
.phy_deinit = pxa_usb_phy_deinit,
|
||||
|
|
|
@ -162,13 +162,7 @@ static struct i2c_board_info ttc_dkb_i2c_info[] = {
|
|||
#ifdef CONFIG_USB_SUPPORT
|
||||
#if defined(CONFIG_USB_MV_UDC) || defined(CONFIG_USB_EHCI_MV_U2O)
|
||||
|
||||
static char *pxa910_usb_clock_name[] = {
|
||||
[0] = "U2OCLK",
|
||||
};
|
||||
|
||||
static struct mv_usb_platform_data ttc_usb_pdata = {
|
||||
.clknum = 1,
|
||||
.clkname = pxa910_usb_clock_name,
|
||||
.vbus = NULL,
|
||||
.mode = MV_USB_MODE_OTG,
|
||||
.otg_force_a_bus_req = 1,
|
||||
|
|
|
@ -76,7 +76,7 @@ static int s3c_usb_otgphy_exit(struct platform_device *pdev)
|
|||
|
||||
int s5p_usb_phy_init(struct platform_device *pdev, int type)
|
||||
{
|
||||
if (type == S5P_USB_PHY_DEVICE)
|
||||
if (type == USB_PHY_TYPE_DEVICE)
|
||||
return s3c_usb_otgphy_init(pdev);
|
||||
|
||||
return -EINVAL;
|
||||
|
@ -84,7 +84,7 @@ int s5p_usb_phy_init(struct platform_device *pdev, int type)
|
|||
|
||||
int s5p_usb_phy_exit(struct platform_device *pdev, int type)
|
||||
{
|
||||
if (type == S5P_USB_PHY_DEVICE)
|
||||
if (type == USB_PHY_TYPE_DEVICE)
|
||||
return s3c_usb_otgphy_exit(pdev);
|
||||
|
||||
return -EINVAL;
|
||||
|
|
|
@ -80,7 +80,7 @@ static int s5pv210_usb_otgphy_exit(struct platform_device *pdev)
|
|||
|
||||
int s5p_usb_phy_init(struct platform_device *pdev, int type)
|
||||
{
|
||||
if (type == S5P_USB_PHY_DEVICE)
|
||||
if (type == USB_PHY_TYPE_DEVICE)
|
||||
return s5pv210_usb_otgphy_init(pdev);
|
||||
|
||||
return -EINVAL;
|
||||
|
@ -88,7 +88,7 @@ int s5p_usb_phy_init(struct platform_device *pdev, int type)
|
|||
|
||||
int s5p_usb_phy_exit(struct platform_device *pdev, int type)
|
||||
{
|
||||
if (type == S5P_USB_PHY_DEVICE)
|
||||
if (type == USB_PHY_TYPE_DEVICE)
|
||||
return s5pv210_usb_otgphy_exit(pdev);
|
||||
|
||||
return -EINVAL;
|
||||
|
|
|
@ -169,7 +169,7 @@ static int usbhsf_get_id(struct platform_device *pdev)
|
|||
return USBHS_GADGET;
|
||||
}
|
||||
|
||||
static void usbhsf_power_ctrl(struct platform_device *pdev,
|
||||
static int usbhsf_power_ctrl(struct platform_device *pdev,
|
||||
void __iomem *base, int enable)
|
||||
{
|
||||
struct usbhsf_private *priv = usbhsf_get_priv(pdev);
|
||||
|
@ -223,6 +223,8 @@ static void usbhsf_power_ctrl(struct platform_device *pdev,
|
|||
clk_disable(priv->pci); /* usb work around */
|
||||
clk_disable(priv->usb24); /* usb work around */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbhsf_get_vbus(struct platform_device *pdev)
|
||||
|
@ -239,7 +241,7 @@ static irqreturn_t usbhsf_interrupt(int irq, void *data)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void usbhsf_hardware_exit(struct platform_device *pdev)
|
||||
static int usbhsf_hardware_exit(struct platform_device *pdev)
|
||||
{
|
||||
struct usbhsf_private *priv = usbhsf_get_priv(pdev);
|
||||
|
||||
|
@ -264,6 +266,8 @@ static void usbhsf_hardware_exit(struct platform_device *pdev)
|
|||
priv->usbh_base = NULL;
|
||||
|
||||
free_irq(IRQ7, pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbhsf_hardware_init(struct platform_device *pdev)
|
||||
|
|
|
@ -155,12 +155,14 @@ static int usbhs_get_vbus(struct platform_device *pdev)
|
|||
return !((1 << 7) & __raw_readw(priv->cr2));
|
||||
}
|
||||
|
||||
static void usbhs_phy_reset(struct platform_device *pdev)
|
||||
static int usbhs_phy_reset(struct platform_device *pdev)
|
||||
{
|
||||
struct usbhs_private *priv = usbhs_get_priv(pdev);
|
||||
|
||||
/* init phy */
|
||||
__raw_writew(0x8a0a, priv->cr2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbhs_get_id(struct platform_device *pdev)
|
||||
|
@ -202,7 +204,7 @@ static int usbhs_hardware_init(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void usbhs_hardware_exit(struct platform_device *pdev)
|
||||
static int usbhs_hardware_exit(struct platform_device *pdev)
|
||||
{
|
||||
struct usbhs_private *priv = usbhs_get_priv(pdev);
|
||||
|
||||
|
@ -210,6 +212,8 @@ static void usbhs_hardware_exit(struct platform_device *pdev)
|
|||
__raw_writew(USB_PHY_MODE | USB_PHY_INT_CLR, priv->phy);
|
||||
|
||||
free_irq(IRQ15, pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 usbhs_pipe_cfg[] = {
|
||||
|
|
|
@ -596,12 +596,14 @@ static int usbhs_get_vbus(struct platform_device *pdev)
|
|||
return usbhs_is_connected(usbhs_get_priv(pdev));
|
||||
}
|
||||
|
||||
static void usbhs_phy_reset(struct platform_device *pdev)
|
||||
static int usbhs_phy_reset(struct platform_device *pdev)
|
||||
{
|
||||
struct usbhs_private *priv = usbhs_get_priv(pdev);
|
||||
|
||||
/* init phy */
|
||||
__raw_writew(0x8a0a, priv->usbcrcaddr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbhs0_get_id(struct platform_device *pdev)
|
||||
|
@ -628,11 +630,13 @@ static int usbhs0_hardware_init(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void usbhs0_hardware_exit(struct platform_device *pdev)
|
||||
static int usbhs0_hardware_exit(struct platform_device *pdev)
|
||||
{
|
||||
struct usbhs_private *priv = usbhs_get_priv(pdev);
|
||||
|
||||
cancel_delayed_work_sync(&priv->work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct usbhs_private usbhs0_private = {
|
||||
|
@ -735,7 +739,7 @@ static int usbhs1_hardware_init(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void usbhs1_hardware_exit(struct platform_device *pdev)
|
||||
static int usbhs1_hardware_exit(struct platform_device *pdev)
|
||||
{
|
||||
struct usbhs_private *priv = usbhs_get_priv(pdev);
|
||||
|
||||
|
@ -743,6 +747,8 @@ static void usbhs1_hardware_exit(struct platform_device *pdev)
|
|||
__raw_writew(USB_PHY_MODE | USB_PHY_INT_CLR, priv->usbphyaddr);
|
||||
|
||||
free_irq(IRQ8, pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbhs1_get_id(struct platform_device *pdev)
|
||||
|
|
|
@ -18,8 +18,8 @@ config ARCH_TEGRA_2x_SOC
|
|||
select PL310_ERRATA_727915 if CACHE_L2X0
|
||||
select PL310_ERRATA_769419 if CACHE_L2X0
|
||||
select USB_ARCH_HAS_EHCI if USB_SUPPORT
|
||||
select USB_ULPI if USB
|
||||
select USB_ULPI_VIEWPORT if USB_SUPPORT
|
||||
select USB_ULPI if USB_PHY
|
||||
select USB_ULPI_VIEWPORT if USB_PHY
|
||||
help
|
||||
Support for NVIDIA Tegra AP20 and T20 processors, based on the
|
||||
ARM CortexA9MP CPU and the ARM PL310 L2 cache controller
|
||||
|
@ -37,8 +37,8 @@ config ARCH_TEGRA_3x_SOC
|
|||
select PINCTRL_TEGRA30
|
||||
select PL310_ERRATA_769419 if CACHE_L2X0
|
||||
select USB_ARCH_HAS_EHCI if USB_SUPPORT
|
||||
select USB_ULPI if USB
|
||||
select USB_ULPI_VIEWPORT if USB_SUPPORT
|
||||
select USB_ULPI if USB_PHY
|
||||
select USB_ULPI_VIEWPORT if USB_PHY
|
||||
help
|
||||
Support for NVIDIA Tegra T30 processor family, based on the
|
||||
ARM CortexA9MP CPU and the ARM PL310 L2 cache controller
|
||||
|
|
|
@ -11,10 +11,7 @@
|
|||
#ifndef __PLAT_SAMSUNG_USB_PHY_H
|
||||
#define __PLAT_SAMSUNG_USB_PHY_H __FILE__
|
||||
|
||||
enum s5p_usb_phy_type {
|
||||
S5P_USB_PHY_DEVICE,
|
||||
S5P_USB_PHY_HOST,
|
||||
};
|
||||
#include <linux/usb/samsung_usb_phy.h>
|
||||
|
||||
extern int s5p_usb_phy_init(struct platform_device *pdev, int type);
|
||||
extern int s5p_usb_phy_exit(struct platform_device *pdev, int type);
|
||||
|
|
|
@ -404,6 +404,8 @@ config PMC_MSP
|
|||
select IRQ_CPU
|
||||
select SERIAL_8250
|
||||
select SERIAL_8250_CONSOLE
|
||||
select USB_EHCI_BIG_ENDIAN_MMIO
|
||||
select USB_EHCI_BIG_ENDIAN_DESC
|
||||
help
|
||||
This adds support for the PMC-Sierra family of Multi-Service
|
||||
Processor System-On-A-Chips. These parts include a number
|
||||
|
@ -1433,6 +1435,7 @@ config CPU_CAVIUM_OCTEON
|
|||
select CPU_SUPPORTS_HUGEPAGES
|
||||
select LIBFDT
|
||||
select USE_OF
|
||||
select USB_EHCI_BIG_ENDIAN_MMIO
|
||||
help
|
||||
The Cavium Octeon processor is a highly integrated chip containing
|
||||
many ethernet hardware widgets for networking tasks. The processor
|
||||
|
|
|
@ -274,6 +274,8 @@ config 440EPX
|
|||
select IBM_EMAC_EMAC4
|
||||
select IBM_EMAC_RGMII
|
||||
select IBM_EMAC_ZMII
|
||||
select USB_EHCI_BIG_ENDIAN_MMIO
|
||||
select USB_EHCI_BIG_ENDIAN_DESC
|
||||
|
||||
config 440GRX
|
||||
bool
|
||||
|
|
|
@ -7,6 +7,8 @@ config PPC_MPC512x
|
|||
select PPC_PCI_CHOICE
|
||||
select FSL_PCI if PCI
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
select USB_EHCI_BIG_ENDIAN_MMIO
|
||||
select USB_EHCI_BIG_ENDIAN_DESC
|
||||
|
||||
config MPC5121_ADS
|
||||
bool "Freescale MPC5121E ADS"
|
||||
|
|
|
@ -254,11 +254,13 @@ static int usbhs_get_id(struct platform_device *pdev)
|
|||
return gpio_get_value(GPIO_PTB3);
|
||||
}
|
||||
|
||||
static void usbhs_phy_reset(struct platform_device *pdev)
|
||||
static int usbhs_phy_reset(struct platform_device *pdev)
|
||||
{
|
||||
/* enable vbus if HOST */
|
||||
if (!gpio_get_value(GPIO_PTB3))
|
||||
gpio_set_value(GPIO_PTB5, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct renesas_usbhs_platform_info usbhs_info = {
|
||||
|
|
|
@ -407,6 +407,8 @@ config SERIAL_CONSOLE
|
|||
config SPARC_LEON
|
||||
bool "Sparc Leon processor family"
|
||||
depends on SPARC32
|
||||
select USB_EHCI_BIG_ENDIAN_MMIO
|
||||
select USB_EHCI_BIG_ENDIAN_DESC
|
||||
---help---
|
||||
If you say Y here if you are running on a SPARC-LEON processor.
|
||||
The LEON processor is a synthesizable VHDL model of the
|
||||
|
|
|
@ -79,7 +79,7 @@ obj-$(CONFIG_ATA_OVER_ETH) += block/aoe/
|
|||
obj-$(CONFIG_PARIDE) += block/paride/
|
||||
obj-$(CONFIG_TC) += tc/
|
||||
obj-$(CONFIG_UWB) += uwb/
|
||||
obj-$(CONFIG_USB_OTG_UTILS) += usb/
|
||||
obj-$(CONFIG_USB_PHY) += usb/
|
||||
obj-$(CONFIG_USB) += usb/
|
||||
obj-$(CONFIG_PCI) += usb/
|
||||
obj-$(CONFIG_USB_GADGET) += usb/
|
||||
|
|
|
@ -1493,7 +1493,7 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
|
|||
{
|
||||
struct hid_device *hid = usb_get_intfdata(intf);
|
||||
struct usbhid_device *usbhid = hid->driver_data;
|
||||
int status;
|
||||
int status = 0;
|
||||
bool driver_suspended = false;
|
||||
|
||||
if (PMSG_IS_AUTO(message)) {
|
||||
|
@ -1520,19 +1520,15 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
|
|||
}
|
||||
|
||||
} else {
|
||||
if (hid->driver && hid->driver->suspend) {
|
||||
/* TODO: resume() might need to handle suspend failure */
|
||||
if (hid->driver && hid->driver->suspend)
|
||||
status = hid->driver->suspend(hid, message);
|
||||
if (status < 0)
|
||||
return status;
|
||||
}
|
||||
driver_suspended = true;
|
||||
spin_lock_irq(&usbhid->lock);
|
||||
set_bit(HID_SUSPENDED, &usbhid->iofl);
|
||||
spin_unlock_irq(&usbhid->lock);
|
||||
if (usbhid_wait_io(hid) < 0) {
|
||||
if (usbhid_wait_io(hid) < 0)
|
||||
status = -EIO;
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
hid_cancel_delayed_stuff(usbhid);
|
||||
|
@ -1544,7 +1540,7 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
|
|||
goto failed;
|
||||
}
|
||||
dev_dbg(&intf->dev, "suspend\n");
|
||||
return 0;
|
||||
return status;
|
||||
|
||||
failed:
|
||||
hid_resume_common(hid, driver_suspended);
|
||||
|
|
|
@ -323,6 +323,11 @@ static int cdc_mbim_suspend(struct usb_interface *intf, pm_message_t message)
|
|||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Both usbnet_suspend() and subdriver->suspend() MUST return 0
|
||||
* in system sleep context, otherwise, the resume callback has
|
||||
* to recover device from previous suspend failure.
|
||||
*/
|
||||
ret = usbnet_suspend(intf, message);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
|
|
@ -374,6 +374,11 @@ static int qmi_wwan_suspend(struct usb_interface *intf, pm_message_t message)
|
|||
struct qmi_wwan_state *info = (void *)&dev->data;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Both usbnet_suspend() and subdriver->suspend() MUST return 0
|
||||
* in system sleep context, otherwise, the resume callback has
|
||||
* to recover device from previous suspend failure.
|
||||
*/
|
||||
ret = usbnet_suspend(intf, message);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
|
|
@ -2015,7 +2015,11 @@ static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message)
|
|||
ret = smsc75xx_enter_suspend0(dev);
|
||||
|
||||
done:
|
||||
if (ret)
|
||||
/*
|
||||
* TODO: resume() might need to handle the suspend failure
|
||||
* in system sleep
|
||||
*/
|
||||
if (ret && PMSG_IS_AUTO(message))
|
||||
usbnet_resume(intf);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -1660,7 +1660,11 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message)
|
|||
ret = smsc95xx_enter_suspend0(dev);
|
||||
|
||||
done:
|
||||
if (ret)
|
||||
/*
|
||||
* TODO: resume() might need to handle the suspend failure
|
||||
* in system sleep
|
||||
*/
|
||||
if (ret && PMSG_IS_AUTO(message))
|
||||
usbnet_resume(intf);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -254,7 +254,7 @@ config BATTERY_RX51
|
|||
|
||||
config CHARGER_ISP1704
|
||||
tristate "ISP1704 USB Charger Detection"
|
||||
depends on USB_OTG_UTILS
|
||||
depends on USB_PHY
|
||||
help
|
||||
Say Y to enable support for USB Charger Detection with
|
||||
ISP1707/ISP1704 USB transceivers.
|
||||
|
|
|
@ -35,7 +35,7 @@ static struct timer_list supply_timer;
|
|||
static struct timer_list polling_timer;
|
||||
static int polling;
|
||||
|
||||
#ifdef CONFIG_USB_OTG_UTILS
|
||||
#if IS_ENABLED(CONFIG_USB_PHY)
|
||||
static struct usb_phy *transceiver;
|
||||
static struct notifier_block otg_nb;
|
||||
#endif
|
||||
|
@ -218,7 +218,7 @@ static void polling_timer_func(unsigned long unused)
|
|||
jiffies + msecs_to_jiffies(pdata->polling_interval));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_OTG_UTILS
|
||||
#if IS_ENABLED(CONFIG_USB_PHY)
|
||||
static int otg_is_usb_online(void)
|
||||
{
|
||||
return (transceiver->last_event == USB_EVENT_VBUS ||
|
||||
|
@ -315,7 +315,7 @@ static int pda_power_probe(struct platform_device *pdev)
|
|||
pda_psy_usb.num_supplicants = pdata->num_supplicants;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_OTG_UTILS
|
||||
#if IS_ENABLED(CONFIG_USB_PHY)
|
||||
transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
|
||||
if (!IS_ERR_OR_NULL(transceiver)) {
|
||||
if (!pdata->is_usb_online)
|
||||
|
@ -367,7 +367,7 @@ static int pda_power_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_OTG_UTILS
|
||||
#if IS_ENABLED(CONFIG_USB_PHY)
|
||||
if (!IS_ERR_OR_NULL(transceiver) && pdata->use_otg_notifier) {
|
||||
otg_nb.notifier_call = otg_handle_notification;
|
||||
ret = usb_register_notifier(transceiver, &otg_nb);
|
||||
|
@ -391,7 +391,7 @@ static int pda_power_probe(struct platform_device *pdev)
|
|||
|
||||
return 0;
|
||||
|
||||
#ifdef CONFIG_USB_OTG_UTILS
|
||||
#if IS_ENABLED(CONFIG_USB_PHY)
|
||||
otg_reg_notifier_failed:
|
||||
if (pdata->is_usb_online && usb_irq)
|
||||
free_irq(usb_irq->start, &pda_psy_usb);
|
||||
|
@ -402,7 +402,7 @@ usb_irq_failed:
|
|||
usb_supply_failed:
|
||||
if (pdata->is_ac_online && ac_irq)
|
||||
free_irq(ac_irq->start, &pda_psy_ac);
|
||||
#ifdef CONFIG_USB_OTG_UTILS
|
||||
#if IS_ENABLED(CONFIG_USB_PHY)
|
||||
if (!IS_ERR_OR_NULL(transceiver))
|
||||
usb_put_phy(transceiver);
|
||||
#endif
|
||||
|
@ -437,7 +437,7 @@ static int pda_power_remove(struct platform_device *pdev)
|
|||
power_supply_unregister(&pda_psy_usb);
|
||||
if (pdata->is_ac_online)
|
||||
power_supply_unregister(&pda_psy_ac);
|
||||
#ifdef CONFIG_USB_OTG_UTILS
|
||||
#if IS_ENABLED(CONFIG_USB_PHY)
|
||||
if (!IS_ERR_OR_NULL(transceiver))
|
||||
usb_put_phy(transceiver);
|
||||
#endif
|
||||
|
|
|
@ -122,9 +122,9 @@ config USB
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called usbcore.
|
||||
|
||||
source "drivers/usb/core/Kconfig"
|
||||
if USB
|
||||
|
||||
source "drivers/usb/dwc3/Kconfig"
|
||||
source "drivers/usb/core/Kconfig"
|
||||
|
||||
source "drivers/usb/mon/Kconfig"
|
||||
|
||||
|
@ -134,8 +134,6 @@ source "drivers/usb/host/Kconfig"
|
|||
|
||||
source "drivers/usb/musb/Kconfig"
|
||||
|
||||
source "drivers/usb/chipidea/Kconfig"
|
||||
|
||||
source "drivers/usb/renesas_usbhs/Kconfig"
|
||||
|
||||
source "drivers/usb/class/Kconfig"
|
||||
|
@ -144,12 +142,19 @@ source "drivers/usb/storage/Kconfig"
|
|||
|
||||
source "drivers/usb/image/Kconfig"
|
||||
|
||||
endif
|
||||
|
||||
source "drivers/usb/dwc3/Kconfig"
|
||||
|
||||
source "drivers/usb/chipidea/Kconfig"
|
||||
|
||||
comment "USB port drivers"
|
||||
depends on USB
|
||||
|
||||
if USB
|
||||
|
||||
config USB_USS720
|
||||
tristate "USS720 parport driver"
|
||||
depends on USB && PARPORT
|
||||
depends on PARPORT
|
||||
select PARPORT_NOT_PC
|
||||
---help---
|
||||
This driver is for USB parallel port adapters that use the Lucent
|
||||
|
@ -180,12 +185,12 @@ source "drivers/usb/serial/Kconfig"
|
|||
|
||||
source "drivers/usb/misc/Kconfig"
|
||||
|
||||
source "drivers/usb/phy/Kconfig"
|
||||
|
||||
source "drivers/usb/atm/Kconfig"
|
||||
|
||||
endif # USB
|
||||
|
||||
source "drivers/usb/phy/Kconfig"
|
||||
|
||||
source "drivers/usb/gadget/Kconfig"
|
||||
|
||||
source "drivers/usb/otg/Kconfig"
|
||||
|
||||
endif # USB_SUPPORT
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
obj-$(CONFIG_USB) += core/
|
||||
|
||||
obj-$(CONFIG_USB_OTG_UTILS) += otg/
|
||||
|
||||
obj-$(CONFIG_USB_DWC3) += dwc3/
|
||||
|
||||
obj-$(CONFIG_USB_MON) += mon/
|
||||
|
@ -46,7 +44,7 @@ obj-$(CONFIG_USB_MICROTEK) += image/
|
|||
obj-$(CONFIG_USB_SERIAL) += serial/
|
||||
|
||||
obj-$(CONFIG_USB) += misc/
|
||||
obj-$(CONFIG_USB_OTG_UTILS) += phy/
|
||||
obj-$(CONFIG_USB_PHY) += phy/
|
||||
obj-$(CONFIG_EARLY_PRINTK_DBGP) += early/
|
||||
|
||||
obj-$(CONFIG_USB_ATM) += atm/
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
menuconfig USB_ATM
|
||||
tristate "USB DSL modem support"
|
||||
depends on USB && ATM
|
||||
depends on ATM
|
||||
select CRC32
|
||||
default n
|
||||
help
|
||||
|
|
|
@ -672,9 +672,6 @@ static int usbatm_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
|
|||
struct usbatm_control *ctrl = UDSL_SKB(skb);
|
||||
int err;
|
||||
|
||||
vdbg(&instance->usb_intf->dev, "%s called (skb 0x%p, len %u)", __func__,
|
||||
skb, skb->len);
|
||||
|
||||
/* racy disconnection check - fine */
|
||||
if (!instance || instance->disconnected) {
|
||||
#ifdef DEBUG
|
||||
|
@ -684,6 +681,9 @@ static int usbatm_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
vdbg(&instance->usb_intf->dev, "%s called (skb 0x%p, len %u)", __func__,
|
||||
skb, skb->len);
|
||||
|
||||
if (vcc->qos.aal != ATM_AAL5) {
|
||||
atm_rldbg(instance, "%s: unsupported ATM type %d!\n", __func__, vcc->qos.aal);
|
||||
err = -EINVAL;
|
||||
|
|
|
@ -17,5 +17,5 @@ ifneq ($(CONFIG_PCI),)
|
|||
endif
|
||||
|
||||
ifneq ($(CONFIG_OF_DEVICE),)
|
||||
obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_imx.o usbmisc_imx6q.o
|
||||
obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_imx.o usbmisc_imx.o
|
||||
endif
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
/******************************************************************************
|
||||
* DEFINE
|
||||
*****************************************************************************/
|
||||
#define TD_PAGE_COUNT 5
|
||||
#define CI13XXX_PAGE_SIZE 4096ul /* page size for TD's */
|
||||
#define ENDPT_MAX 32
|
||||
|
||||
|
@ -129,6 +130,7 @@ struct hw_bank {
|
|||
* @vbus_active: is VBUS active
|
||||
* @transceiver: pointer to USB PHY, if any
|
||||
* @hcd: pointer to usb_hcd for ehci host driver
|
||||
* @debugfs: root dentry for this controller in debugfs
|
||||
*/
|
||||
struct ci13xxx {
|
||||
struct device *dev;
|
||||
|
@ -139,7 +141,6 @@ struct ci13xxx {
|
|||
enum ci_role role;
|
||||
bool is_otg;
|
||||
struct work_struct work;
|
||||
struct work_struct vbus_work;
|
||||
struct workqueue_struct *wq;
|
||||
|
||||
struct dma_pool *qh_pool;
|
||||
|
@ -165,6 +166,7 @@ struct ci13xxx {
|
|||
bool global_phy;
|
||||
struct usb_phy *transceiver;
|
||||
struct usb_hcd *hcd;
|
||||
struct dentry *debugfs;
|
||||
};
|
||||
|
||||
static inline struct ci_role_driver *ci_role(struct ci13xxx *ci)
|
||||
|
@ -233,19 +235,6 @@ enum ci13xxx_regs {
|
|||
OP_LAST = OP_ENDPTCTRL + ENDPT_MAX / 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* ffs_nr: find first (least significant) bit set
|
||||
* @x: the word to search
|
||||
*
|
||||
* This function returns bit number (instead of position)
|
||||
*/
|
||||
static inline int ffs_nr(u32 x)
|
||||
{
|
||||
int n = ffs(x);
|
||||
|
||||
return n ? n-1 : 32;
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_read: reads from a hw register
|
||||
* @reg: register index
|
||||
|
@ -304,7 +293,7 @@ static inline u32 hw_test_and_write(struct ci13xxx *ci, enum ci13xxx_regs reg,
|
|||
u32 val = hw_read(ci, reg, ~0);
|
||||
|
||||
hw_write(ci, reg, mask, data);
|
||||
return (val & mask) >> ffs_nr(mask);
|
||||
return (val & mask) >> __ffs(mask);
|
||||
}
|
||||
|
||||
int hw_device_reset(struct ci13xxx *ci, u32 mode);
|
||||
|
|
|
@ -79,6 +79,9 @@ int usbmisc_get_init_data(struct device *dev, struct usbmisc_usb_device *usbdev)
|
|||
if (of_find_property(np, "disable-over-current", NULL))
|
||||
usbdev->disable_oc = 1;
|
||||
|
||||
if (of_find_property(np, "external-vbus-divider", NULL))
|
||||
usbdev->evdo = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbmisc_get_init_data);
|
||||
|
@ -202,6 +205,15 @@ static int ci13xxx_imx_probe(struct platform_device *pdev)
|
|||
goto err;
|
||||
}
|
||||
|
||||
if (usbmisc_ops && usbmisc_ops->post) {
|
||||
ret = usbmisc_ops->post(&pdev->dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"usbmisc post failed, ret=%d\n", ret);
|
||||
goto put_np;
|
||||
}
|
||||
}
|
||||
|
||||
data->ci_pdev = plat_ci;
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
struct usbmisc_ops {
|
||||
/* It's called once when probe a usb device */
|
||||
int (*init)(struct device *dev);
|
||||
/* It's called once after adding a usb device */
|
||||
int (*post)(struct device *dev);
|
||||
};
|
||||
|
||||
struct usbmisc_usb_device {
|
||||
|
@ -20,6 +22,7 @@ struct usbmisc_usb_device {
|
|||
int index;
|
||||
|
||||
unsigned int disable_oc:1; /* over current detect disabled */
|
||||
unsigned int evdo:1; /* set external vbus divider option */
|
||||
};
|
||||
|
||||
int usbmisc_set_ops(const struct usbmisc_ops *ops);
|
||||
|
|
|
@ -23,17 +23,17 @@
|
|||
/******************************************************************************
|
||||
* PCI block
|
||||
*****************************************************************************/
|
||||
struct ci13xxx_platform_data pci_platdata = {
|
||||
static struct ci13xxx_platform_data pci_platdata = {
|
||||
.name = UDC_DRIVER_NAME,
|
||||
.capoffset = DEF_CAPOFFSET,
|
||||
};
|
||||
|
||||
struct ci13xxx_platform_data langwell_pci_platdata = {
|
||||
static struct ci13xxx_platform_data langwell_pci_platdata = {
|
||||
.name = UDC_DRIVER_NAME,
|
||||
.capoffset = 0,
|
||||
};
|
||||
|
||||
struct ci13xxx_platform_data penwell_pci_platdata = {
|
||||
static struct ci13xxx_platform_data penwell_pci_platdata = {
|
||||
.name = UDC_DRIVER_NAME,
|
||||
.capoffset = 0,
|
||||
.power_budget = 200,
|
||||
|
|
|
@ -51,15 +51,12 @@
|
|||
*/
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
@ -158,7 +155,7 @@ int hw_port_test_set(struct ci13xxx *ci, u8 mode)
|
|||
if (mode > TEST_MODE_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
hw_write(ci, OP_PORTSC, PORTSC_PTC, mode << ffs_nr(PORTSC_PTC));
|
||||
hw_write(ci, OP_PORTSC, PORTSC_PTC, mode << __ffs(PORTSC_PTC));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -169,7 +166,7 @@ int hw_port_test_set(struct ci13xxx *ci, u8 mode)
|
|||
*/
|
||||
u8 hw_port_test_get(struct ci13xxx *ci)
|
||||
{
|
||||
return hw_read(ci, OP_PORTSC, PORTSC_PTC) >> ffs_nr(PORTSC_PTC);
|
||||
return hw_read(ci, OP_PORTSC, PORTSC_PTC) >> __ffs(PORTSC_PTC);
|
||||
}
|
||||
|
||||
static int hw_device_init(struct ci13xxx *ci, void __iomem *base)
|
||||
|
@ -181,11 +178,11 @@ static int hw_device_init(struct ci13xxx *ci, void __iomem *base)
|
|||
|
||||
ci->hw_bank.cap = ci->hw_bank.abs;
|
||||
ci->hw_bank.cap += ci->platdata->capoffset;
|
||||
ci->hw_bank.op = ci->hw_bank.cap + ioread8(ci->hw_bank.cap);
|
||||
ci->hw_bank.op = ci->hw_bank.cap + (ioread32(ci->hw_bank.cap) & 0xff);
|
||||
|
||||
hw_alloc_regmap(ci, false);
|
||||
reg = hw_read(ci, CAP_HCCPARAMS, HCCPARAMS_LEN) >>
|
||||
ffs_nr(HCCPARAMS_LEN);
|
||||
__ffs(HCCPARAMS_LEN);
|
||||
ci->hw_bank.lpm = reg;
|
||||
hw_alloc_regmap(ci, !!reg);
|
||||
ci->hw_bank.size = ci->hw_bank.op - ci->hw_bank.abs;
|
||||
|
@ -193,7 +190,7 @@ static int hw_device_init(struct ci13xxx *ci, void __iomem *base)
|
|||
ci->hw_bank.size /= sizeof(u32);
|
||||
|
||||
reg = hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DEN) >>
|
||||
ffs_nr(DCCPARAMS_DEN);
|
||||
__ffs(DCCPARAMS_DEN);
|
||||
ci->hw_ep_max = reg * 2; /* cache hw ENDPT_MAX */
|
||||
|
||||
if (ci->hw_ep_max > ENDPT_MAX)
|
||||
|
@ -283,38 +280,6 @@ static void ci_role_work(struct work_struct *work)
|
|||
}
|
||||
}
|
||||
|
||||
static ssize_t show_role(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct ci13xxx *ci = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%s\n", ci_role(ci)->name);
|
||||
}
|
||||
|
||||
static ssize_t store_role(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ci13xxx *ci = dev_get_drvdata(dev);
|
||||
enum ci_role role;
|
||||
int ret;
|
||||
|
||||
for (role = CI_ROLE_HOST; role < CI_ROLE_END; role++)
|
||||
if (ci->roles[role] && !strcmp(buf, ci->roles[role]->name))
|
||||
break;
|
||||
|
||||
if (role == CI_ROLE_END || role == ci->role)
|
||||
return -EINVAL;
|
||||
|
||||
ci_role_stop(ci);
|
||||
ret = ci_role_start(ci, role);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(role, S_IRUSR | S_IWUSR, show_role, store_role);
|
||||
|
||||
static irqreturn_t ci_irq(int irq, void *data)
|
||||
{
|
||||
struct ci13xxx *ci = data;
|
||||
|
@ -410,11 +375,9 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
base = devm_request_and_ioremap(dev, res);
|
||||
if (!base) {
|
||||
dev_err(dev, "can't request and ioremap resource\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
ci = devm_kzalloc(dev, sizeof(*ci), GFP_KERNEL);
|
||||
if (!ci) {
|
||||
|
@ -489,17 +452,14 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto stop;
|
||||
|
||||
ret = device_create_file(dev, &dev_attr_role);
|
||||
if (ret)
|
||||
goto rm_attr;
|
||||
|
||||
if (ci->is_otg)
|
||||
hw_write(ci, OP_OTGSC, OTGSC_IDIE, OTGSC_IDIE);
|
||||
|
||||
return ret;
|
||||
ret = dbg_create_files(ci);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
rm_attr:
|
||||
device_remove_file(dev, &dev_attr_role);
|
||||
free_irq(ci->irq, ci);
|
||||
stop:
|
||||
ci_role_stop(ci);
|
||||
rm_wq:
|
||||
|
@ -513,9 +473,9 @@ static int ci_hdrc_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct ci13xxx *ci = platform_get_drvdata(pdev);
|
||||
|
||||
dbg_remove_files(ci);
|
||||
flush_workqueue(ci->wq);
|
||||
destroy_workqueue(ci->wq);
|
||||
device_remove_file(ci->dev, &dev_attr_role);
|
||||
free_irq(ci->irq, ci);
|
||||
ci_role_stop(ci);
|
||||
|
||||
|
|
|
@ -1,590 +1,126 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/chipidea.h>
|
||||
|
||||
#include "ci.h"
|
||||
#include "udc.h"
|
||||
#include "bits.h"
|
||||
#include "debug.h"
|
||||
|
||||
/* Interrupt statistics */
|
||||
#define ISR_MASK 0x1F
|
||||
static struct isr_statistics {
|
||||
u32 test;
|
||||
u32 ui;
|
||||
u32 uei;
|
||||
u32 pci;
|
||||
u32 uri;
|
||||
u32 sli;
|
||||
u32 none;
|
||||
struct {
|
||||
u32 cnt;
|
||||
u32 buf[ISR_MASK+1];
|
||||
u32 idx;
|
||||
} hndl;
|
||||
} isr_statistics;
|
||||
|
||||
void dbg_interrupt(u32 intmask)
|
||||
{
|
||||
if (!intmask) {
|
||||
isr_statistics.none++;
|
||||
return;
|
||||
}
|
||||
|
||||
isr_statistics.hndl.buf[isr_statistics.hndl.idx++] = intmask;
|
||||
isr_statistics.hndl.idx &= ISR_MASK;
|
||||
isr_statistics.hndl.cnt++;
|
||||
|
||||
if (USBi_URI & intmask)
|
||||
isr_statistics.uri++;
|
||||
if (USBi_PCI & intmask)
|
||||
isr_statistics.pci++;
|
||||
if (USBi_UEI & intmask)
|
||||
isr_statistics.uei++;
|
||||
if (USBi_UI & intmask)
|
||||
isr_statistics.ui++;
|
||||
if (USBi_SLI & intmask)
|
||||
isr_statistics.sli++;
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_register_read: reads all device registers (execute without interruption)
|
||||
* @buf: destination buffer
|
||||
* @size: buffer size
|
||||
*
|
||||
* This function returns number of registers read
|
||||
* ci_device_show: prints information about device capabilities and status
|
||||
*/
|
||||
static size_t hw_register_read(struct ci13xxx *ci, u32 *buf, size_t size)
|
||||
static int ci_device_show(struct seq_file *s, void *data)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
if (size > ci->hw_bank.size)
|
||||
size = ci->hw_bank.size;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
buf[i] = hw_read(ci, i * sizeof(u32), ~0);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_register_write: writes to register
|
||||
* @addr: register address
|
||||
* @data: register value
|
||||
*
|
||||
* This function returns an error code
|
||||
*/
|
||||
static int hw_register_write(struct ci13xxx *ci, u16 addr, u32 data)
|
||||
{
|
||||
/* align */
|
||||
addr /= sizeof(u32);
|
||||
|
||||
if (addr >= ci->hw_bank.size)
|
||||
return -EINVAL;
|
||||
|
||||
/* align */
|
||||
addr *= sizeof(u32);
|
||||
|
||||
hw_write(ci, addr, ~0, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_intr_clear: disables interrupt & clears interrupt status (execute without
|
||||
* interruption)
|
||||
* @n: interrupt bit
|
||||
*
|
||||
* This function returns an error code
|
||||
*/
|
||||
static int hw_intr_clear(struct ci13xxx *ci, int n)
|
||||
{
|
||||
if (n >= REG_BITS)
|
||||
return -EINVAL;
|
||||
|
||||
hw_write(ci, OP_USBINTR, BIT(n), 0);
|
||||
hw_write(ci, OP_USBSTS, BIT(n), BIT(n));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_intr_force: enables interrupt & forces interrupt status (execute without
|
||||
* interruption)
|
||||
* @n: interrupt bit
|
||||
*
|
||||
* This function returns an error code
|
||||
*/
|
||||
static int hw_intr_force(struct ci13xxx *ci, int n)
|
||||
{
|
||||
if (n >= REG_BITS)
|
||||
return -EINVAL;
|
||||
|
||||
hw_write(ci, CAP_TESTMODE, TESTMODE_FORCE, TESTMODE_FORCE);
|
||||
hw_write(ci, OP_USBINTR, BIT(n), BIT(n));
|
||||
hw_write(ci, OP_USBSTS, BIT(n), BIT(n));
|
||||
hw_write(ci, CAP_TESTMODE, TESTMODE_FORCE, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* show_device: prints information about device capabilities and status
|
||||
*
|
||||
* Check "device.h" for details
|
||||
*/
|
||||
static ssize_t show_device(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
|
||||
struct ci13xxx *ci = s->private;
|
||||
struct usb_gadget *gadget = &ci->gadget;
|
||||
int n = 0;
|
||||
|
||||
if (attr == NULL || buf == NULL) {
|
||||
dev_err(ci->dev, "[%s] EINVAL\n", __func__);
|
||||
seq_printf(s, "speed = %d\n", gadget->speed);
|
||||
seq_printf(s, "max_speed = %d\n", gadget->max_speed);
|
||||
seq_printf(s, "is_otg = %d\n", gadget->is_otg);
|
||||
seq_printf(s, "is_a_peripheral = %d\n", gadget->is_a_peripheral);
|
||||
seq_printf(s, "b_hnp_enable = %d\n", gadget->b_hnp_enable);
|
||||
seq_printf(s, "a_hnp_support = %d\n", gadget->a_hnp_support);
|
||||
seq_printf(s, "a_alt_hnp_support = %d\n", gadget->a_alt_hnp_support);
|
||||
seq_printf(s, "name = %s\n",
|
||||
(gadget->name ? gadget->name : ""));
|
||||
|
||||
if (!ci->driver)
|
||||
return 0;
|
||||
}
|
||||
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "speed = %d\n",
|
||||
gadget->speed);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "max_speed = %d\n",
|
||||
gadget->max_speed);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "is_otg = %d\n",
|
||||
gadget->is_otg);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "is_a_peripheral = %d\n",
|
||||
gadget->is_a_peripheral);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "b_hnp_enable = %d\n",
|
||||
gadget->b_hnp_enable);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "a_hnp_support = %d\n",
|
||||
gadget->a_hnp_support);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "a_alt_hnp_support = %d\n",
|
||||
gadget->a_alt_hnp_support);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "name = %s\n",
|
||||
(gadget->name ? gadget->name : ""));
|
||||
seq_printf(s, "gadget function = %s\n",
|
||||
(ci->driver->function ? ci->driver->function : ""));
|
||||
seq_printf(s, "gadget max speed = %d\n", ci->driver->max_speed);
|
||||
|
||||
return n;
|
||||
return 0;
|
||||
}
|
||||
static DEVICE_ATTR(device, S_IRUSR, show_device, NULL);
|
||||
|
||||
/**
|
||||
* show_driver: prints information about attached gadget (if any)
|
||||
*
|
||||
* Check "device.h" for details
|
||||
*/
|
||||
static ssize_t show_driver(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
static int ci_device_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
|
||||
struct usb_gadget_driver *driver = ci->driver;
|
||||
int n = 0;
|
||||
|
||||
if (attr == NULL || buf == NULL) {
|
||||
dev_err(dev, "[%s] EINVAL\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (driver == NULL)
|
||||
return scnprintf(buf, PAGE_SIZE,
|
||||
"There is no gadget attached!\n");
|
||||
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "function = %s\n",
|
||||
(driver->function ? driver->function : ""));
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "max speed = %d\n",
|
||||
driver->max_speed);
|
||||
|
||||
return n;
|
||||
return single_open(file, ci_device_show, inode->i_private);
|
||||
}
|
||||
static DEVICE_ATTR(driver, S_IRUSR, show_driver, NULL);
|
||||
|
||||
/* Maximum event message length */
|
||||
#define DBG_DATA_MSG 64UL
|
||||
|
||||
/* Maximum event messages */
|
||||
#define DBG_DATA_MAX 128UL
|
||||
|
||||
/* Event buffer descriptor */
|
||||
static struct {
|
||||
char (buf[DBG_DATA_MAX])[DBG_DATA_MSG]; /* buffer */
|
||||
unsigned idx; /* index */
|
||||
unsigned tty; /* print to console? */
|
||||
rwlock_t lck; /* lock */
|
||||
} dbg_data = {
|
||||
.idx = 0,
|
||||
.tty = 0,
|
||||
.lck = __RW_LOCK_UNLOCKED(dbg_data.lck)
|
||||
static const struct file_operations ci_device_fops = {
|
||||
.open = ci_device_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
/**
|
||||
* dbg_dec: decrements debug event index
|
||||
* @idx: buffer index
|
||||
* ci_port_test_show: reads port test mode
|
||||
*/
|
||||
static void dbg_dec(unsigned *idx)
|
||||
static int ci_port_test_show(struct seq_file *s, void *data)
|
||||
{
|
||||
*idx = (*idx - 1) & (DBG_DATA_MAX-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* dbg_inc: increments debug event index
|
||||
* @idx: buffer index
|
||||
*/
|
||||
static void dbg_inc(unsigned *idx)
|
||||
{
|
||||
*idx = (*idx + 1) & (DBG_DATA_MAX-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* dbg_print: prints the common part of the event
|
||||
* @addr: endpoint address
|
||||
* @name: event name
|
||||
* @status: status
|
||||
* @extra: extra information
|
||||
*/
|
||||
static void dbg_print(u8 addr, const char *name, int status, const char *extra)
|
||||
{
|
||||
struct timeval tval;
|
||||
unsigned int stamp;
|
||||
unsigned long flags;
|
||||
|
||||
write_lock_irqsave(&dbg_data.lck, flags);
|
||||
|
||||
do_gettimeofday(&tval);
|
||||
stamp = tval.tv_sec & 0xFFFF; /* 2^32 = 4294967296. Limit to 4096s */
|
||||
stamp = stamp * 1000000 + tval.tv_usec;
|
||||
|
||||
scnprintf(dbg_data.buf[dbg_data.idx], DBG_DATA_MSG,
|
||||
"%04X\t? %02X %-7.7s %4i ?\t%s\n",
|
||||
stamp, addr, name, status, extra);
|
||||
|
||||
dbg_inc(&dbg_data.idx);
|
||||
|
||||
write_unlock_irqrestore(&dbg_data.lck, flags);
|
||||
|
||||
if (dbg_data.tty != 0)
|
||||
pr_notice("%04X\t? %02X %-7.7s %4i ?\t%s\n",
|
||||
stamp, addr, name, status, extra);
|
||||
}
|
||||
|
||||
/**
|
||||
* dbg_done: prints a DONE event
|
||||
* @addr: endpoint address
|
||||
* @td: transfer descriptor
|
||||
* @status: status
|
||||
*/
|
||||
void dbg_done(u8 addr, const u32 token, int status)
|
||||
{
|
||||
char msg[DBG_DATA_MSG];
|
||||
|
||||
scnprintf(msg, sizeof(msg), "%d %02X",
|
||||
(int)(token & TD_TOTAL_BYTES) >> ffs_nr(TD_TOTAL_BYTES),
|
||||
(int)(token & TD_STATUS) >> ffs_nr(TD_STATUS));
|
||||
dbg_print(addr, "DONE", status, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* dbg_event: prints a generic event
|
||||
* @addr: endpoint address
|
||||
* @name: event name
|
||||
* @status: status
|
||||
*/
|
||||
void dbg_event(u8 addr, const char *name, int status)
|
||||
{
|
||||
if (name != NULL)
|
||||
dbg_print(addr, name, status, "");
|
||||
}
|
||||
|
||||
/*
|
||||
* dbg_queue: prints a QUEUE event
|
||||
* @addr: endpoint address
|
||||
* @req: USB request
|
||||
* @status: status
|
||||
*/
|
||||
void dbg_queue(u8 addr, const struct usb_request *req, int status)
|
||||
{
|
||||
char msg[DBG_DATA_MSG];
|
||||
|
||||
if (req != NULL) {
|
||||
scnprintf(msg, sizeof(msg),
|
||||
"%d %d", !req->no_interrupt, req->length);
|
||||
dbg_print(addr, "QUEUE", status, msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dbg_setup: prints a SETUP event
|
||||
* @addr: endpoint address
|
||||
* @req: setup request
|
||||
*/
|
||||
void dbg_setup(u8 addr, const struct usb_ctrlrequest *req)
|
||||
{
|
||||
char msg[DBG_DATA_MSG];
|
||||
|
||||
if (req != NULL) {
|
||||
scnprintf(msg, sizeof(msg),
|
||||
"%02X %02X %04X %04X %d", req->bRequestType,
|
||||
req->bRequest, le16_to_cpu(req->wValue),
|
||||
le16_to_cpu(req->wIndex), le16_to_cpu(req->wLength));
|
||||
dbg_print(addr, "SETUP", 0, msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* show_events: displays the event buffer
|
||||
*
|
||||
* Check "device.h" for details
|
||||
*/
|
||||
static ssize_t show_events(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned i, j, n = 0;
|
||||
|
||||
if (attr == NULL || buf == NULL) {
|
||||
dev_err(dev->parent, "[%s] EINVAL\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
read_lock_irqsave(&dbg_data.lck, flags);
|
||||
|
||||
i = dbg_data.idx;
|
||||
for (dbg_dec(&i); i != dbg_data.idx; dbg_dec(&i)) {
|
||||
n += strlen(dbg_data.buf[i]);
|
||||
if (n >= PAGE_SIZE) {
|
||||
n -= strlen(dbg_data.buf[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (j = 0, dbg_inc(&i); j < n; dbg_inc(&i))
|
||||
j += scnprintf(buf + j, PAGE_SIZE - j,
|
||||
"%s", dbg_data.buf[i]);
|
||||
|
||||
read_unlock_irqrestore(&dbg_data.lck, flags);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* store_events: configure if events are going to be also printed to console
|
||||
*
|
||||
* Check "device.h" for details
|
||||
*/
|
||||
static ssize_t store_events(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
unsigned tty;
|
||||
|
||||
if (attr == NULL || buf == NULL) {
|
||||
dev_err(dev, "[%s] EINVAL\n", __func__);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (sscanf(buf, "%u", &tty) != 1 || tty > 1) {
|
||||
dev_err(dev, "<1|0>: enable|disable console log\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
dbg_data.tty = tty;
|
||||
dev_info(dev, "tty = %u", dbg_data.tty);
|
||||
|
||||
done:
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(events, S_IRUSR | S_IWUSR, show_events, store_events);
|
||||
|
||||
/**
|
||||
* show_inters: interrupt status, enable status and historic
|
||||
*
|
||||
* Check "device.h" for details
|
||||
*/
|
||||
static ssize_t show_inters(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
|
||||
unsigned long flags;
|
||||
u32 intr;
|
||||
unsigned i, j, n = 0;
|
||||
|
||||
if (attr == NULL || buf == NULL) {
|
||||
dev_err(ci->dev, "[%s] EINVAL\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ci->lock, flags);
|
||||
|
||||
/*n += scnprintf(buf + n, PAGE_SIZE - n,
|
||||
"status = %08x\n", hw_read_intr_status(ci));
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n,
|
||||
"enable = %08x\n", hw_read_intr_enable(ci));*/
|
||||
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "*test = %d\n",
|
||||
isr_statistics.test);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "? ui = %d\n",
|
||||
isr_statistics.ui);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "? uei = %d\n",
|
||||
isr_statistics.uei);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "? pci = %d\n",
|
||||
isr_statistics.pci);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "? uri = %d\n",
|
||||
isr_statistics.uri);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "? sli = %d\n",
|
||||
isr_statistics.sli);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "*none = %d\n",
|
||||
isr_statistics.none);
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "*hndl = %d\n",
|
||||
isr_statistics.hndl.cnt);
|
||||
|
||||
for (i = isr_statistics.hndl.idx, j = 0; j <= ISR_MASK; j++, i++) {
|
||||
i &= ISR_MASK;
|
||||
intr = isr_statistics.hndl.buf[i];
|
||||
|
||||
if (USBi_UI & intr)
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "ui ");
|
||||
intr &= ~USBi_UI;
|
||||
if (USBi_UEI & intr)
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "uei ");
|
||||
intr &= ~USBi_UEI;
|
||||
if (USBi_PCI & intr)
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "pci ");
|
||||
intr &= ~USBi_PCI;
|
||||
if (USBi_URI & intr)
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "uri ");
|
||||
intr &= ~USBi_URI;
|
||||
if (USBi_SLI & intr)
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "sli ");
|
||||
intr &= ~USBi_SLI;
|
||||
if (intr)
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "??? ");
|
||||
if (isr_statistics.hndl.buf[i])
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n, "\n");
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&ci->lock, flags);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* store_inters: enable & force or disable an individual interrutps
|
||||
* (to be used for test purposes only)
|
||||
*
|
||||
* Check "device.h" for details
|
||||
*/
|
||||
static ssize_t store_inters(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
|
||||
unsigned long flags;
|
||||
unsigned en, bit;
|
||||
|
||||
if (attr == NULL || buf == NULL) {
|
||||
dev_err(ci->dev, "EINVAL\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (sscanf(buf, "%u %u", &en, &bit) != 2 || en > 1) {
|
||||
dev_err(ci->dev, "<1|0> <bit>: enable|disable interrupt\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ci->lock, flags);
|
||||
if (en) {
|
||||
if (hw_intr_force(ci, bit))
|
||||
dev_err(dev, "invalid bit number\n");
|
||||
else
|
||||
isr_statistics.test++;
|
||||
} else {
|
||||
if (hw_intr_clear(ci, bit))
|
||||
dev_err(dev, "invalid bit number\n");
|
||||
}
|
||||
spin_unlock_irqrestore(&ci->lock, flags);
|
||||
|
||||
done:
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(inters, S_IRUSR | S_IWUSR, show_inters, store_inters);
|
||||
|
||||
/**
|
||||
* show_port_test: reads port test mode
|
||||
*
|
||||
* Check "device.h" for details
|
||||
*/
|
||||
static ssize_t show_port_test(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
|
||||
struct ci13xxx *ci = s->private;
|
||||
unsigned long flags;
|
||||
unsigned mode;
|
||||
|
||||
if (attr == NULL || buf == NULL) {
|
||||
dev_err(ci->dev, "EINVAL\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ci->lock, flags);
|
||||
mode = hw_port_test_get(ci);
|
||||
spin_unlock_irqrestore(&ci->lock, flags);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "mode = %u\n", mode);
|
||||
seq_printf(s, "mode = %u\n", mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* store_port_test: writes port test mode
|
||||
*
|
||||
* Check "device.h" for details
|
||||
* ci_port_test_write: writes port test mode
|
||||
*/
|
||||
static ssize_t store_port_test(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
static ssize_t ci_port_test_write(struct file *file, const char __user *ubuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
|
||||
struct seq_file *s = file->private_data;
|
||||
struct ci13xxx *ci = s->private;
|
||||
unsigned long flags;
|
||||
unsigned mode;
|
||||
char buf[32];
|
||||
int ret;
|
||||
|
||||
if (attr == NULL || buf == NULL) {
|
||||
dev_err(ci->dev, "[%s] EINVAL\n", __func__);
|
||||
goto done;
|
||||
}
|
||||
if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
|
||||
return -EFAULT;
|
||||
|
||||
if (sscanf(buf, "%u", &mode) != 1) {
|
||||
dev_err(ci->dev, "<mode>: set port test mode");
|
||||
goto done;
|
||||
}
|
||||
if (sscanf(buf, "%u", &mode) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&ci->lock, flags);
|
||||
if (hw_port_test_set(ci, mode))
|
||||
dev_err(ci->dev, "invalid mode\n");
|
||||
ret = hw_port_test_set(ci, mode);
|
||||
spin_unlock_irqrestore(&ci->lock, flags);
|
||||
|
||||
done:
|
||||
return count;
|
||||
return ret ? ret : count;
|
||||
}
|
||||
static DEVICE_ATTR(port_test, S_IRUSR | S_IWUSR,
|
||||
show_port_test, store_port_test);
|
||||
|
||||
static int ci_port_test_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, ci_port_test_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations ci_port_test_fops = {
|
||||
.open = ci_port_test_open,
|
||||
.write = ci_port_test_write,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
/**
|
||||
* show_qheads: DMA contents of all queue heads
|
||||
*
|
||||
* Check "device.h" for details
|
||||
* ci_qheads_show: DMA contents of all queue heads
|
||||
*/
|
||||
static ssize_t show_qheads(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
static int ci_qheads_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
|
||||
struct ci13xxx *ci = s->private;
|
||||
unsigned long flags;
|
||||
unsigned i, j, n = 0;
|
||||
unsigned i, j;
|
||||
|
||||
if (attr == NULL || buf == NULL) {
|
||||
dev_err(ci->dev, "[%s] EINVAL\n", __func__);
|
||||
if (ci->role != CI_ROLE_GADGET) {
|
||||
seq_printf(s, "not in gadget mode\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -593,209 +129,173 @@ static ssize_t show_qheads(struct device *dev, struct device_attribute *attr,
|
|||
struct ci13xxx_ep *mEpRx = &ci->ci13xxx_ep[i];
|
||||
struct ci13xxx_ep *mEpTx =
|
||||
&ci->ci13xxx_ep[i + ci->hw_ep_max/2];
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n,
|
||||
"EP=%02i: RX=%08X TX=%08X\n",
|
||||
i, (u32)mEpRx->qh.dma, (u32)mEpTx->qh.dma);
|
||||
for (j = 0; j < (sizeof(struct ci13xxx_qh)/sizeof(u32)); j++) {
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n,
|
||||
" %04X: %08X %08X\n", j,
|
||||
*((u32 *)mEpRx->qh.ptr + j),
|
||||
*((u32 *)mEpTx->qh.ptr + j));
|
||||
}
|
||||
seq_printf(s, "EP=%02i: RX=%08X TX=%08X\n",
|
||||
i, (u32)mEpRx->qh.dma, (u32)mEpTx->qh.dma);
|
||||
for (j = 0; j < (sizeof(struct ci13xxx_qh)/sizeof(u32)); j++)
|
||||
seq_printf(s, " %04X: %08X %08X\n", j,
|
||||
*((u32 *)mEpRx->qh.ptr + j),
|
||||
*((u32 *)mEpTx->qh.ptr + j));
|
||||
}
|
||||
spin_unlock_irqrestore(&ci->lock, flags);
|
||||
|
||||
return n;
|
||||
}
|
||||
static DEVICE_ATTR(qheads, S_IRUSR, show_qheads, NULL);
|
||||
|
||||
/**
|
||||
* show_registers: dumps all registers
|
||||
*
|
||||
* Check "device.h" for details
|
||||
*/
|
||||
#define DUMP_ENTRIES 512
|
||||
static ssize_t show_registers(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
|
||||
unsigned long flags;
|
||||
u32 *dump;
|
||||
unsigned i, k, n = 0;
|
||||
|
||||
if (attr == NULL || buf == NULL) {
|
||||
dev_err(ci->dev, "[%s] EINVAL\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dump = kmalloc(sizeof(u32) * DUMP_ENTRIES, GFP_KERNEL);
|
||||
if (!dump) {
|
||||
dev_err(ci->dev, "%s: out of memory\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ci->lock, flags);
|
||||
k = hw_register_read(ci, dump, DUMP_ENTRIES);
|
||||
spin_unlock_irqrestore(&ci->lock, flags);
|
||||
|
||||
for (i = 0; i < k; i++) {
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n,
|
||||
"reg[0x%04X] = 0x%08X\n",
|
||||
i * (unsigned)sizeof(u32), dump[i]);
|
||||
}
|
||||
kfree(dump);
|
||||
|
||||
return n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* store_registers: writes value to register address
|
||||
*
|
||||
* Check "device.h" for details
|
||||
*/
|
||||
static ssize_t store_registers(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
static int ci_qheads_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
|
||||
unsigned long addr, data, flags;
|
||||
|
||||
if (attr == NULL || buf == NULL) {
|
||||
dev_err(ci->dev, "[%s] EINVAL\n", __func__);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (sscanf(buf, "%li %li", &addr, &data) != 2) {
|
||||
dev_err(ci->dev,
|
||||
"<addr> <data>: write data to register address\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ci->lock, flags);
|
||||
if (hw_register_write(ci, addr, data))
|
||||
dev_err(ci->dev, "invalid address range\n");
|
||||
spin_unlock_irqrestore(&ci->lock, flags);
|
||||
|
||||
done:
|
||||
return count;
|
||||
return single_open(file, ci_qheads_show, inode->i_private);
|
||||
}
|
||||
static DEVICE_ATTR(registers, S_IRUSR | S_IWUSR,
|
||||
show_registers, store_registers);
|
||||
|
||||
static const struct file_operations ci_qheads_fops = {
|
||||
.open = ci_qheads_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
/**
|
||||
* show_requests: DMA contents of all requests currently queued (all endpts)
|
||||
*
|
||||
* Check "device.h" for details
|
||||
* ci_requests_show: DMA contents of all requests currently queued (all endpts)
|
||||
*/
|
||||
static ssize_t show_requests(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
static int ci_requests_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
|
||||
struct ci13xxx *ci = s->private;
|
||||
unsigned long flags;
|
||||
struct list_head *ptr = NULL;
|
||||
struct ci13xxx_req *req = NULL;
|
||||
unsigned i, j, n = 0, qSize = sizeof(struct ci13xxx_td)/sizeof(u32);
|
||||
unsigned i, j, qsize = sizeof(struct ci13xxx_td)/sizeof(u32);
|
||||
|
||||
if (attr == NULL || buf == NULL) {
|
||||
dev_err(ci->dev, "[%s] EINVAL\n", __func__);
|
||||
if (ci->role != CI_ROLE_GADGET) {
|
||||
seq_printf(s, "not in gadget mode\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ci->lock, flags);
|
||||
for (i = 0; i < ci->hw_ep_max; i++)
|
||||
list_for_each(ptr, &ci->ci13xxx_ep[i].qh.queue)
|
||||
{
|
||||
list_for_each(ptr, &ci->ci13xxx_ep[i].qh.queue) {
|
||||
req = list_entry(ptr, struct ci13xxx_req, queue);
|
||||
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n,
|
||||
"EP=%02i: TD=%08X %s\n",
|
||||
i % ci->hw_ep_max/2, (u32)req->dma,
|
||||
((i < ci->hw_ep_max/2) ? "RX" : "TX"));
|
||||
seq_printf(s, "EP=%02i: TD=%08X %s\n",
|
||||
i % (ci->hw_ep_max / 2), (u32)req->dma,
|
||||
((i < ci->hw_ep_max/2) ? "RX" : "TX"));
|
||||
|
||||
for (j = 0; j < qSize; j++)
|
||||
n += scnprintf(buf + n, PAGE_SIZE - n,
|
||||
" %04X: %08X\n", j,
|
||||
*((u32 *)req->ptr + j));
|
||||
for (j = 0; j < qsize; j++)
|
||||
seq_printf(s, " %04X: %08X\n", j,
|
||||
*((u32 *)req->ptr + j));
|
||||
}
|
||||
spin_unlock_irqrestore(&ci->lock, flags);
|
||||
|
||||
return n;
|
||||
return 0;
|
||||
}
|
||||
static DEVICE_ATTR(requests, S_IRUSR, show_requests, NULL);
|
||||
|
||||
static int ci_requests_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, ci_requests_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations ci_requests_fops = {
|
||||
.open = ci_requests_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int ci_role_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct ci13xxx *ci = s->private;
|
||||
|
||||
seq_printf(s, "%s\n", ci_role(ci)->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t ci_role_write(struct file *file, const char __user *ubuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct seq_file *s = file->private_data;
|
||||
struct ci13xxx *ci = s->private;
|
||||
enum ci_role role;
|
||||
char buf[8];
|
||||
int ret;
|
||||
|
||||
if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
|
||||
return -EFAULT;
|
||||
|
||||
for (role = CI_ROLE_HOST; role < CI_ROLE_END; role++)
|
||||
if (ci->roles[role] &&
|
||||
!strncmp(buf, ci->roles[role]->name,
|
||||
strlen(ci->roles[role]->name)))
|
||||
break;
|
||||
|
||||
if (role == CI_ROLE_END || role == ci->role)
|
||||
return -EINVAL;
|
||||
|
||||
ci_role_stop(ci);
|
||||
ret = ci_role_start(ci, role);
|
||||
|
||||
return ret ? ret : count;
|
||||
}
|
||||
|
||||
static int ci_role_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, ci_role_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations ci_role_fops = {
|
||||
.open = ci_role_open,
|
||||
.write = ci_role_write,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
/**
|
||||
* dbg_create_files: initializes the attribute interface
|
||||
* @dev: device
|
||||
* @ci: device
|
||||
*
|
||||
* This function returns an error code
|
||||
*/
|
||||
int dbg_create_files(struct device *dev)
|
||||
int dbg_create_files(struct ci13xxx *ci)
|
||||
{
|
||||
int retval = 0;
|
||||
struct dentry *dent;
|
||||
|
||||
if (dev == NULL)
|
||||
return -EINVAL;
|
||||
retval = device_create_file(dev, &dev_attr_device);
|
||||
if (retval)
|
||||
goto done;
|
||||
retval = device_create_file(dev, &dev_attr_driver);
|
||||
if (retval)
|
||||
goto rm_device;
|
||||
retval = device_create_file(dev, &dev_attr_events);
|
||||
if (retval)
|
||||
goto rm_driver;
|
||||
retval = device_create_file(dev, &dev_attr_inters);
|
||||
if (retval)
|
||||
goto rm_events;
|
||||
retval = device_create_file(dev, &dev_attr_port_test);
|
||||
if (retval)
|
||||
goto rm_inters;
|
||||
retval = device_create_file(dev, &dev_attr_qheads);
|
||||
if (retval)
|
||||
goto rm_port_test;
|
||||
retval = device_create_file(dev, &dev_attr_registers);
|
||||
if (retval)
|
||||
goto rm_qheads;
|
||||
retval = device_create_file(dev, &dev_attr_requests);
|
||||
if (retval)
|
||||
goto rm_registers;
|
||||
return 0;
|
||||
ci->debugfs = debugfs_create_dir(dev_name(ci->dev), NULL);
|
||||
if (!ci->debugfs)
|
||||
return -ENOMEM;
|
||||
|
||||
rm_registers:
|
||||
device_remove_file(dev, &dev_attr_registers);
|
||||
rm_qheads:
|
||||
device_remove_file(dev, &dev_attr_qheads);
|
||||
rm_port_test:
|
||||
device_remove_file(dev, &dev_attr_port_test);
|
||||
rm_inters:
|
||||
device_remove_file(dev, &dev_attr_inters);
|
||||
rm_events:
|
||||
device_remove_file(dev, &dev_attr_events);
|
||||
rm_driver:
|
||||
device_remove_file(dev, &dev_attr_driver);
|
||||
rm_device:
|
||||
device_remove_file(dev, &dev_attr_device);
|
||||
done:
|
||||
return retval;
|
||||
dent = debugfs_create_file("device", S_IRUGO, ci->debugfs, ci,
|
||||
&ci_device_fops);
|
||||
if (!dent)
|
||||
goto err;
|
||||
|
||||
dent = debugfs_create_file("port_test", S_IRUGO | S_IWUSR, ci->debugfs,
|
||||
ci, &ci_port_test_fops);
|
||||
if (!dent)
|
||||
goto err;
|
||||
|
||||
dent = debugfs_create_file("qheads", S_IRUGO, ci->debugfs, ci,
|
||||
&ci_qheads_fops);
|
||||
if (!dent)
|
||||
goto err;
|
||||
|
||||
dent = debugfs_create_file("requests", S_IRUGO, ci->debugfs, ci,
|
||||
&ci_requests_fops);
|
||||
if (!dent)
|
||||
goto err;
|
||||
|
||||
dent = debugfs_create_file("role", S_IRUGO | S_IWUSR, ci->debugfs, ci,
|
||||
&ci_role_fops);
|
||||
if (dent)
|
||||
return 0;
|
||||
err:
|
||||
debugfs_remove_recursive(ci->debugfs);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/**
|
||||
* dbg_remove_files: destroys the attribute interface
|
||||
* @dev: device
|
||||
*
|
||||
* This function returns an error code
|
||||
* @ci: device
|
||||
*/
|
||||
int dbg_remove_files(struct device *dev)
|
||||
void dbg_remove_files(struct ci13xxx *ci)
|
||||
{
|
||||
if (dev == NULL)
|
||||
return -EINVAL;
|
||||
device_remove_file(dev, &dev_attr_requests);
|
||||
device_remove_file(dev, &dev_attr_registers);
|
||||
device_remove_file(dev, &dev_attr_qheads);
|
||||
device_remove_file(dev, &dev_attr_port_test);
|
||||
device_remove_file(dev, &dev_attr_inters);
|
||||
device_remove_file(dev, &dev_attr_events);
|
||||
device_remove_file(dev, &dev_attr_driver);
|
||||
device_remove_file(dev, &dev_attr_device);
|
||||
return 0;
|
||||
debugfs_remove_recursive(ci->debugfs);
|
||||
}
|
||||
|
|
|
@ -14,42 +14,16 @@
|
|||
#define __DRIVERS_USB_CHIPIDEA_DEBUG_H
|
||||
|
||||
#ifdef CONFIG_USB_CHIPIDEA_DEBUG
|
||||
void dbg_interrupt(u32 intmask);
|
||||
void dbg_done(u8 addr, const u32 token, int status);
|
||||
void dbg_event(u8 addr, const char *name, int status);
|
||||
void dbg_queue(u8 addr, const struct usb_request *req, int status);
|
||||
void dbg_setup(u8 addr, const struct usb_ctrlrequest *req);
|
||||
int dbg_create_files(struct device *dev);
|
||||
int dbg_remove_files(struct device *dev);
|
||||
int dbg_create_files(struct ci13xxx *ci);
|
||||
void dbg_remove_files(struct ci13xxx *ci);
|
||||
#else
|
||||
static inline void dbg_interrupt(u32 intmask)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void dbg_done(u8 addr, const u32 token, int status)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void dbg_event(u8 addr, const char *name, int status)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void dbg_queue(u8 addr, const struct usb_request *req, int status)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void dbg_setup(u8 addr, const struct usb_ctrlrequest *req)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int dbg_create_files(struct device *dev)
|
||||
static inline int dbg_create_files(struct ci13xxx *ci)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int dbg_remove_files(struct device *dev)
|
||||
static inline void dbg_remove_files(struct ci13xxx *ci)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -13,14 +13,8 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
@ -146,7 +140,7 @@ static int hw_ep_enable(struct ci13xxx *ci, int num, int dir, int type)
|
|||
|
||||
if (dir) {
|
||||
mask = ENDPTCTRL_TXT; /* type */
|
||||
data = type << ffs_nr(mask);
|
||||
data = type << __ffs(mask);
|
||||
|
||||
mask |= ENDPTCTRL_TXS; /* unstall */
|
||||
mask |= ENDPTCTRL_TXR; /* reset data toggle */
|
||||
|
@ -155,7 +149,7 @@ static int hw_ep_enable(struct ci13xxx *ci, int num, int dir, int type)
|
|||
data |= ENDPTCTRL_TXE;
|
||||
} else {
|
||||
mask = ENDPTCTRL_RXT; /* type */
|
||||
data = type << ffs_nr(mask);
|
||||
data = type << __ffs(mask);
|
||||
|
||||
mask |= ENDPTCTRL_RXS; /* unstall */
|
||||
mask |= ENDPTCTRL_RXR; /* reset data toggle */
|
||||
|
@ -305,18 +299,6 @@ static u32 hw_test_and_clear_intr_active(struct ci13xxx *ci)
|
|||
return reg;
|
||||
}
|
||||
|
||||
static void hw_enable_vbus_intr(struct ci13xxx *ci)
|
||||
{
|
||||
hw_write(ci, OP_OTGSC, OTGSC_AVVIS, OTGSC_AVVIS);
|
||||
hw_write(ci, OP_OTGSC, OTGSC_AVVIE, OTGSC_AVVIE);
|
||||
queue_work(ci->wq, &ci->vbus_work);
|
||||
}
|
||||
|
||||
static void hw_disable_vbus_intr(struct ci13xxx *ci)
|
||||
{
|
||||
hw_write(ci, OP_OTGSC, OTGSC_AVVIE, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_test_and_clear_setup_guard: test & clear setup guard (execute without
|
||||
* interruption)
|
||||
|
@ -349,7 +331,7 @@ static int hw_test_and_set_setup_guard(struct ci13xxx *ci)
|
|||
static void hw_usb_set_address(struct ci13xxx *ci, u8 value)
|
||||
{
|
||||
hw_write(ci, OP_DEVICEADDR, DEVICEADDR_USBADR,
|
||||
value << ffs_nr(DEVICEADDR_USBADR));
|
||||
value << __ffs(DEVICEADDR_USBADR));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -383,16 +365,6 @@ static int hw_usb_reset(struct ci13xxx *ci)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void vbus_work(struct work_struct *work)
|
||||
{
|
||||
struct ci13xxx *ci = container_of(work, struct ci13xxx, vbus_work);
|
||||
|
||||
if (hw_read(ci, OP_OTGSC, OTGSC_AVV))
|
||||
usb_gadget_vbus_connect(&ci->gadget);
|
||||
else
|
||||
usb_gadget_vbus_disconnect(&ci->gadget);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* UTIL block
|
||||
*****************************************************************************/
|
||||
|
@ -432,10 +404,10 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
|
|||
return -ENOMEM;
|
||||
|
||||
memset(mReq->zptr, 0, sizeof(*mReq->zptr));
|
||||
mReq->zptr->next = TD_TERMINATE;
|
||||
mReq->zptr->token = TD_STATUS_ACTIVE;
|
||||
mReq->zptr->next = cpu_to_le32(TD_TERMINATE);
|
||||
mReq->zptr->token = cpu_to_le32(TD_STATUS_ACTIVE);
|
||||
if (!mReq->req.no_interrupt)
|
||||
mReq->zptr->token |= TD_IOC;
|
||||
mReq->zptr->token |= cpu_to_le32(TD_IOC);
|
||||
}
|
||||
ret = usb_gadget_map_request(&ci->gadget, &mReq->req, mEp->dir);
|
||||
if (ret)
|
||||
|
@ -446,32 +418,37 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
|
|||
* TODO - handle requests which spawns into several TDs
|
||||
*/
|
||||
memset(mReq->ptr, 0, sizeof(*mReq->ptr));
|
||||
mReq->ptr->token = length << ffs_nr(TD_TOTAL_BYTES);
|
||||
mReq->ptr->token &= TD_TOTAL_BYTES;
|
||||
mReq->ptr->token |= TD_STATUS_ACTIVE;
|
||||
mReq->ptr->token = cpu_to_le32(length << __ffs(TD_TOTAL_BYTES));
|
||||
mReq->ptr->token &= cpu_to_le32(TD_TOTAL_BYTES);
|
||||
mReq->ptr->token |= cpu_to_le32(TD_STATUS_ACTIVE);
|
||||
if (mReq->zptr) {
|
||||
mReq->ptr->next = mReq->zdma;
|
||||
mReq->ptr->next = cpu_to_le32(mReq->zdma);
|
||||
} else {
|
||||
mReq->ptr->next = TD_TERMINATE;
|
||||
mReq->ptr->next = cpu_to_le32(TD_TERMINATE);
|
||||
if (!mReq->req.no_interrupt)
|
||||
mReq->ptr->token |= TD_IOC;
|
||||
mReq->ptr->token |= cpu_to_le32(TD_IOC);
|
||||
}
|
||||
mReq->ptr->page[0] = mReq->req.dma;
|
||||
for (i = 1; i < 5; i++)
|
||||
mReq->ptr->page[i] =
|
||||
(mReq->req.dma + i * CI13XXX_PAGE_SIZE) & ~TD_RESERVED_MASK;
|
||||
mReq->ptr->page[0] = cpu_to_le32(mReq->req.dma);
|
||||
for (i = 1; i < TD_PAGE_COUNT; i++) {
|
||||
u32 page = mReq->req.dma + i * CI13XXX_PAGE_SIZE;
|
||||
page &= ~TD_RESERVED_MASK;
|
||||
mReq->ptr->page[i] = cpu_to_le32(page);
|
||||
}
|
||||
|
||||
wmb();
|
||||
|
||||
if (!list_empty(&mEp->qh.queue)) {
|
||||
struct ci13xxx_req *mReqPrev;
|
||||
int n = hw_ep_bit(mEp->num, mEp->dir);
|
||||
int tmp_stat;
|
||||
u32 next = mReq->dma & TD_ADDR_MASK;
|
||||
|
||||
mReqPrev = list_entry(mEp->qh.queue.prev,
|
||||
struct ci13xxx_req, queue);
|
||||
if (mReqPrev->zptr)
|
||||
mReqPrev->zptr->next = mReq->dma & TD_ADDR_MASK;
|
||||
mReqPrev->zptr->next = cpu_to_le32(next);
|
||||
else
|
||||
mReqPrev->ptr->next = mReq->dma & TD_ADDR_MASK;
|
||||
mReqPrev->ptr->next = cpu_to_le32(next);
|
||||
wmb();
|
||||
if (hw_read(ci, OP_ENDPTPRIME, BIT(n)))
|
||||
goto done;
|
||||
|
@ -485,9 +462,9 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
|
|||
}
|
||||
|
||||
/* QH configuration */
|
||||
mEp->qh.ptr->td.next = mReq->dma; /* TERMINATE = 0 */
|
||||
mEp->qh.ptr->td.token &= ~TD_STATUS; /* clear status */
|
||||
mEp->qh.ptr->cap |= QH_ZLT;
|
||||
mEp->qh.ptr->td.next = cpu_to_le32(mReq->dma); /* TERMINATE = 0 */
|
||||
mEp->qh.ptr->td.token &=
|
||||
cpu_to_le32(~(TD_STATUS_HALTED|TD_STATUS_ACTIVE));
|
||||
|
||||
wmb(); /* synchronize before ep prime */
|
||||
|
||||
|
@ -506,14 +483,16 @@ done:
|
|||
*/
|
||||
static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
|
||||
{
|
||||
u32 tmptoken = le32_to_cpu(mReq->ptr->token);
|
||||
|
||||
if (mReq->req.status != -EALREADY)
|
||||
return -EINVAL;
|
||||
|
||||
if ((TD_STATUS_ACTIVE & mReq->ptr->token) != 0)
|
||||
if ((TD_STATUS_ACTIVE & tmptoken) != 0)
|
||||
return -EBUSY;
|
||||
|
||||
if (mReq->zptr) {
|
||||
if ((TD_STATUS_ACTIVE & mReq->zptr->token) != 0)
|
||||
if ((cpu_to_le32(TD_STATUS_ACTIVE) & mReq->zptr->token) != 0)
|
||||
return -EBUSY;
|
||||
dma_pool_free(mEp->td_pool, mReq->zptr, mReq->zdma);
|
||||
mReq->zptr = NULL;
|
||||
|
@ -523,7 +502,7 @@ static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
|
|||
|
||||
usb_gadget_unmap_request(&mEp->ci->gadget, &mReq->req, mEp->dir);
|
||||
|
||||
mReq->req.status = mReq->ptr->token & TD_STATUS;
|
||||
mReq->req.status = tmptoken & TD_STATUS;
|
||||
if ((TD_STATUS_HALTED & mReq->req.status) != 0)
|
||||
mReq->req.status = -1;
|
||||
else if ((TD_STATUS_DT_ERR & mReq->req.status) != 0)
|
||||
|
@ -531,8 +510,8 @@ static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq)
|
|||
else if ((TD_STATUS_TR_ERR & mReq->req.status) != 0)
|
||||
mReq->req.status = -1;
|
||||
|
||||
mReq->req.actual = mReq->ptr->token & TD_TOTAL_BYTES;
|
||||
mReq->req.actual >>= ffs_nr(TD_TOTAL_BYTES);
|
||||
mReq->req.actual = tmptoken & TD_TOTAL_BYTES;
|
||||
mReq->req.actual >>= __ffs(TD_TOTAL_BYTES);
|
||||
mReq->req.actual = mReq->req.length - mReq->req.actual;
|
||||
mReq->req.actual = mReq->req.status ? 0 : mReq->req.actual;
|
||||
|
||||
|
@ -561,6 +540,12 @@ __acquires(mEp->lock)
|
|||
struct ci13xxx_req *mReq = \
|
||||
list_entry(mEp->qh.queue.next,
|
||||
struct ci13xxx_req, queue);
|
||||
|
||||
if (mReq->zptr) {
|
||||
dma_pool_free(mEp->td_pool, mReq->zptr, mReq->zdma);
|
||||
mReq->zptr = NULL;
|
||||
}
|
||||
|
||||
list_del_init(&mReq->queue);
|
||||
mReq->req.status = -ESHUTDOWN;
|
||||
|
||||
|
@ -629,8 +614,6 @@ __acquires(ci->lock)
|
|||
{
|
||||
int retval;
|
||||
|
||||
dbg_event(0xFF, "BUS RST", 0);
|
||||
|
||||
spin_unlock(&ci->lock);
|
||||
retval = _gadget_stop_activity(&ci->gadget);
|
||||
if (retval)
|
||||
|
@ -667,6 +650,59 @@ static void isr_get_status_complete(struct usb_ep *ep, struct usb_request *req)
|
|||
usb_ep_free_request(ep, req);
|
||||
}
|
||||
|
||||
/**
|
||||
* _ep_queue: queues (submits) an I/O request to an endpoint
|
||||
*
|
||||
* Caller must hold lock
|
||||
*/
|
||||
static int _ep_queue(struct usb_ep *ep, struct usb_request *req,
|
||||
gfp_t __maybe_unused gfp_flags)
|
||||
{
|
||||
struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep);
|
||||
struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req);
|
||||
struct ci13xxx *ci = mEp->ci;
|
||||
int retval = 0;
|
||||
|
||||
if (ep == NULL || req == NULL || mEp->ep.desc == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (mEp->type == USB_ENDPOINT_XFER_CONTROL) {
|
||||
if (req->length)
|
||||
mEp = (ci->ep0_dir == RX) ?
|
||||
ci->ep0out : ci->ep0in;
|
||||
if (!list_empty(&mEp->qh.queue)) {
|
||||
_ep_nuke(mEp);
|
||||
retval = -EOVERFLOW;
|
||||
dev_warn(mEp->ci->dev, "endpoint ctrl %X nuked\n",
|
||||
_usb_addr(mEp));
|
||||
}
|
||||
}
|
||||
|
||||
/* first nuke then test link, e.g. previous status has not sent */
|
||||
if (!list_empty(&mReq->queue)) {
|
||||
dev_err(mEp->ci->dev, "request already in queue\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (req->length > (TD_PAGE_COUNT - 1) * CI13XXX_PAGE_SIZE) {
|
||||
dev_err(mEp->ci->dev, "request bigger than one td\n");
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
/* push request */
|
||||
mReq->req.status = -EINPROGRESS;
|
||||
mReq->req.actual = 0;
|
||||
|
||||
retval = _hardware_enqueue(mEp, mReq);
|
||||
|
||||
if (retval == -EALREADY)
|
||||
retval = 0;
|
||||
if (!retval)
|
||||
list_add_tail(&mReq->queue, &mEp->qh.queue);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* isr_get_status_response: get_status request response
|
||||
* @ci: ci struct
|
||||
|
@ -714,9 +750,7 @@ __acquires(mEp->lock)
|
|||
}
|
||||
/* else do nothing; reserved for future use */
|
||||
|
||||
spin_unlock(mEp->lock);
|
||||
retval = usb_ep_queue(&mEp->ep, req, gfp_flags);
|
||||
spin_lock(mEp->lock);
|
||||
retval = _ep_queue(&mEp->ep, req, gfp_flags);
|
||||
if (retval)
|
||||
goto err_free_buf;
|
||||
|
||||
|
@ -763,8 +797,6 @@ isr_setup_status_complete(struct usb_ep *ep, struct usb_request *req)
|
|||
* This function returns an error code
|
||||
*/
|
||||
static int isr_setup_status_phase(struct ci13xxx *ci)
|
||||
__releases(mEp->lock)
|
||||
__acquires(mEp->lock)
|
||||
{
|
||||
int retval;
|
||||
struct ci13xxx_ep *mEp;
|
||||
|
@ -773,9 +805,7 @@ __acquires(mEp->lock)
|
|||
ci->status->context = ci;
|
||||
ci->status->complete = isr_setup_status_complete;
|
||||
|
||||
spin_unlock(mEp->lock);
|
||||
retval = usb_ep_queue(&mEp->ep, ci->status, GFP_ATOMIC);
|
||||
spin_lock(mEp->lock);
|
||||
retval = _ep_queue(&mEp->ep, ci->status, GFP_ATOMIC);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
@ -801,7 +831,6 @@ __acquires(mEp->lock)
|
|||
if (retval < 0)
|
||||
break;
|
||||
list_del_init(&mReq->queue);
|
||||
dbg_done(_usb_addr(mEp), mReq->ptr->token, retval);
|
||||
if (mReq->req.complete != NULL) {
|
||||
spin_unlock(mEp->lock);
|
||||
if ((mEp->type == USB_ENDPOINT_XFER_CONTROL) &&
|
||||
|
@ -814,8 +843,6 @@ __acquires(mEp->lock)
|
|||
|
||||
if (retval == -EBUSY)
|
||||
retval = 0;
|
||||
if (retval < 0)
|
||||
dbg_event(_usb_addr(mEp), "DONE", retval);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
@ -847,8 +874,6 @@ __acquires(ci->lock)
|
|||
if (err > 0) /* needs status phase */
|
||||
err = isr_setup_status_phase(ci);
|
||||
if (err < 0) {
|
||||
dbg_event(_usb_addr(mEp),
|
||||
"ERROR", err);
|
||||
spin_unlock(&ci->lock);
|
||||
if (usb_ep_set_halt(&mEp->ep))
|
||||
dev_err(ci->dev,
|
||||
|
@ -884,8 +909,6 @@ __acquires(ci->lock)
|
|||
|
||||
ci->ep0_dir = (type & USB_DIR_IN) ? TX : RX;
|
||||
|
||||
dbg_setup(_usb_addr(mEp), &req);
|
||||
|
||||
switch (req.bRequest) {
|
||||
case USB_REQ_CLEAR_FEATURE:
|
||||
if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
|
||||
|
@ -997,8 +1020,6 @@ delegate:
|
|||
}
|
||||
|
||||
if (err < 0) {
|
||||
dbg_event(_usb_addr(mEp), "ERROR", err);
|
||||
|
||||
spin_unlock(&ci->lock);
|
||||
if (usb_ep_set_halt(&mEp->ep))
|
||||
dev_err(ci->dev, "error: ep_set_halt\n");
|
||||
|
@ -1021,6 +1042,7 @@ static int ep_enable(struct usb_ep *ep,
|
|||
struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep);
|
||||
int retval = 0;
|
||||
unsigned long flags;
|
||||
u32 cap = 0;
|
||||
|
||||
if (ep == NULL || desc == NULL)
|
||||
return -EINVAL;
|
||||
|
@ -1040,20 +1062,15 @@ static int ep_enable(struct usb_ep *ep,
|
|||
|
||||
mEp->ep.maxpacket = usb_endpoint_maxp(desc);
|
||||
|
||||
dbg_event(_usb_addr(mEp), "ENABLE", 0);
|
||||
|
||||
mEp->qh.ptr->cap = 0;
|
||||
|
||||
if (mEp->type == USB_ENDPOINT_XFER_CONTROL)
|
||||
mEp->qh.ptr->cap |= QH_IOS;
|
||||
else if (mEp->type == USB_ENDPOINT_XFER_ISOC)
|
||||
mEp->qh.ptr->cap &= ~QH_MULT;
|
||||
else
|
||||
mEp->qh.ptr->cap &= ~QH_ZLT;
|
||||
cap |= QH_IOS;
|
||||
if (mEp->num)
|
||||
cap |= QH_ZLT;
|
||||
cap |= (mEp->ep.maxpacket << __ffs(QH_MAX_PKT)) & QH_MAX_PKT;
|
||||
|
||||
mEp->qh.ptr->cap |=
|
||||
(mEp->ep.maxpacket << ffs_nr(QH_MAX_PKT)) & QH_MAX_PKT;
|
||||
mEp->qh.ptr->td.next |= TD_TERMINATE; /* needed? */
|
||||
mEp->qh.ptr->cap = cpu_to_le32(cap);
|
||||
|
||||
mEp->qh.ptr->td.next |= cpu_to_le32(TD_TERMINATE); /* needed? */
|
||||
|
||||
/*
|
||||
* Enable endpoints in the HW other than ep0 as ep0
|
||||
|
@ -1088,8 +1105,6 @@ static int ep_disable(struct usb_ep *ep)
|
|||
|
||||
direction = mEp->dir;
|
||||
do {
|
||||
dbg_event(_usb_addr(mEp), "DISABLE", 0);
|
||||
|
||||
retval |= _ep_nuke(mEp);
|
||||
retval |= hw_ep_disable(mEp->ci, mEp->num, mEp->dir);
|
||||
|
||||
|
@ -1129,8 +1144,6 @@ static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
|
|||
}
|
||||
}
|
||||
|
||||
dbg_event(_usb_addr(mEp), "ALLOC", mReq == NULL);
|
||||
|
||||
return (mReq == NULL) ? NULL : &mReq->req;
|
||||
}
|
||||
|
||||
|
@ -1158,8 +1171,6 @@ static void ep_free_request(struct usb_ep *ep, struct usb_request *req)
|
|||
dma_pool_free(mEp->td_pool, mReq->ptr, mReq->dma);
|
||||
kfree(mReq);
|
||||
|
||||
dbg_event(_usb_addr(mEp), "FREE", 0);
|
||||
|
||||
spin_unlock_irqrestore(mEp->lock, flags);
|
||||
}
|
||||
|
||||
|
@ -1172,8 +1183,6 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req,
|
|||
gfp_t __maybe_unused gfp_flags)
|
||||
{
|
||||
struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep);
|
||||
struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req);
|
||||
struct ci13xxx *ci = mEp->ci;
|
||||
int retval = 0;
|
||||
unsigned long flags;
|
||||
|
||||
|
@ -1181,48 +1190,7 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req,
|
|||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(mEp->lock, flags);
|
||||
|
||||
if (mEp->type == USB_ENDPOINT_XFER_CONTROL) {
|
||||
if (req->length)
|
||||
mEp = (ci->ep0_dir == RX) ?
|
||||
ci->ep0out : ci->ep0in;
|
||||
if (!list_empty(&mEp->qh.queue)) {
|
||||
_ep_nuke(mEp);
|
||||
retval = -EOVERFLOW;
|
||||
dev_warn(mEp->ci->dev, "endpoint ctrl %X nuked\n",
|
||||
_usb_addr(mEp));
|
||||
}
|
||||
}
|
||||
|
||||
/* first nuke then test link, e.g. previous status has not sent */
|
||||
if (!list_empty(&mReq->queue)) {
|
||||
retval = -EBUSY;
|
||||
dev_err(mEp->ci->dev, "request already in queue\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (req->length > 4 * CI13XXX_PAGE_SIZE) {
|
||||
req->length = 4 * CI13XXX_PAGE_SIZE;
|
||||
retval = -EMSGSIZE;
|
||||
dev_warn(mEp->ci->dev, "request length truncated\n");
|
||||
}
|
||||
|
||||
dbg_queue(_usb_addr(mEp), req, retval);
|
||||
|
||||
/* push request */
|
||||
mReq->req.status = -EINPROGRESS;
|
||||
mReq->req.actual = 0;
|
||||
|
||||
retval = _hardware_enqueue(mEp, mReq);
|
||||
|
||||
if (retval == -EALREADY) {
|
||||
dbg_event(_usb_addr(mEp), "QUEUE", retval);
|
||||
retval = 0;
|
||||
}
|
||||
if (!retval)
|
||||
list_add_tail(&mReq->queue, &mEp->qh.queue);
|
||||
|
||||
done:
|
||||
retval = _ep_queue(ep, req, gfp_flags);
|
||||
spin_unlock_irqrestore(mEp->lock, flags);
|
||||
return retval;
|
||||
}
|
||||
|
@ -1245,8 +1213,6 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req)
|
|||
|
||||
spin_lock_irqsave(mEp->lock, flags);
|
||||
|
||||
dbg_event(_usb_addr(mEp), "DEQUEUE", 0);
|
||||
|
||||
hw_ep_flush(mEp->ci, mEp->num, mEp->dir);
|
||||
|
||||
/* pop request */
|
||||
|
@ -1293,7 +1259,6 @@ static int ep_set_halt(struct usb_ep *ep, int value)
|
|||
|
||||
direction = mEp->dir;
|
||||
do {
|
||||
dbg_event(_usb_addr(mEp), "HALT", value);
|
||||
retval |= hw_ep_set_halt(mEp->ci, mEp->num, mEp->dir, value);
|
||||
|
||||
if (!value)
|
||||
|
@ -1322,10 +1287,7 @@ static int ep_set_wedge(struct usb_ep *ep)
|
|||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(mEp->lock, flags);
|
||||
|
||||
dbg_event(_usb_addr(mEp), "WEDGE", 0);
|
||||
mEp->wedge = 1;
|
||||
|
||||
spin_unlock_irqrestore(mEp->lock, flags);
|
||||
|
||||
return usb_ep_set_halt(ep);
|
||||
|
@ -1348,7 +1310,6 @@ static void ep_fifo_flush(struct usb_ep *ep)
|
|||
|
||||
spin_lock_irqsave(mEp->lock, flags);
|
||||
|
||||
dbg_event(_usb_addr(mEp), "FFLUSH", 0);
|
||||
hw_ep_flush(mEp->ci, mEp->num, mEp->dir);
|
||||
|
||||
spin_unlock_irqrestore(mEp->lock, flags);
|
||||
|
@ -1392,7 +1353,6 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
|
|||
if (is_active) {
|
||||
pm_runtime_get_sync(&_gadget->dev);
|
||||
hw_device_reset(ci, USBMODE_CM_DC);
|
||||
hw_enable_vbus_intr(ci);
|
||||
hw_device_state(ci, ci->ep0out->qh.dma);
|
||||
} else {
|
||||
hw_device_state(ci, 0);
|
||||
|
@ -1567,10 +1527,8 @@ static int ci13xxx_start(struct usb_gadget *gadget,
|
|||
pm_runtime_get_sync(&ci->gadget.dev);
|
||||
if (ci->platdata->flags & CI13XXX_PULLUP_ON_VBUS) {
|
||||
if (ci->vbus_active) {
|
||||
if (ci->platdata->flags & CI13XXX_REGS_SHARED) {
|
||||
if (ci->platdata->flags & CI13XXX_REGS_SHARED)
|
||||
hw_device_reset(ci, USBMODE_CM_DC);
|
||||
hw_enable_vbus_intr(ci);
|
||||
}
|
||||
} else {
|
||||
pm_runtime_put_sync(&ci->gadget.dev);
|
||||
goto done;
|
||||
|
@ -1642,7 +1600,6 @@ static irqreturn_t udc_irq(struct ci13xxx *ci)
|
|||
}
|
||||
}
|
||||
intr = hw_test_and_clear_intr_active(ci);
|
||||
dbg_interrupt(intr);
|
||||
|
||||
if (intr) {
|
||||
/* order defines priority - do NOT change it */
|
||||
|
@ -1676,28 +1633,11 @@ static irqreturn_t udc_irq(struct ci13xxx *ci)
|
|||
} else {
|
||||
retval = IRQ_NONE;
|
||||
}
|
||||
|
||||
intr = hw_read(ci, OP_OTGSC, ~0);
|
||||
hw_write(ci, OP_OTGSC, ~0, intr);
|
||||
|
||||
if (intr & (OTGSC_AVVIE & OTGSC_AVVIS))
|
||||
queue_work(ci->wq, &ci->vbus_work);
|
||||
|
||||
spin_unlock(&ci->lock);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* udc_release: driver release function
|
||||
* @dev: device
|
||||
*
|
||||
* Currently does nothing
|
||||
*/
|
||||
static void udc_release(struct device *dev)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* udc_start: initialize gadget role
|
||||
* @ci: chipidea controller
|
||||
|
@ -1717,12 +1657,6 @@ static int udc_start(struct ci13xxx *ci)
|
|||
|
||||
INIT_LIST_HEAD(&ci->gadget.ep_list);
|
||||
|
||||
dev_set_name(&ci->gadget.dev, "gadget");
|
||||
ci->gadget.dev.dma_mask = dev->dma_mask;
|
||||
ci->gadget.dev.coherent_dma_mask = dev->coherent_dma_mask;
|
||||
ci->gadget.dev.parent = dev;
|
||||
ci->gadget.dev.release = udc_release;
|
||||
|
||||
/* alloc resources */
|
||||
ci->qh_pool = dma_pool_create("ci13xxx_qh", dev,
|
||||
sizeof(struct ci13xxx_qh),
|
||||
|
@ -1758,24 +1692,13 @@ static int udc_start(struct ci13xxx *ci)
|
|||
retval = hw_device_reset(ci, USBMODE_CM_DC);
|
||||
if (retval)
|
||||
goto put_transceiver;
|
||||
hw_enable_vbus_intr(ci);
|
||||
}
|
||||
|
||||
retval = device_register(&ci->gadget.dev);
|
||||
if (retval) {
|
||||
put_device(&ci->gadget.dev);
|
||||
goto put_transceiver;
|
||||
}
|
||||
|
||||
retval = dbg_create_files(ci->dev);
|
||||
if (retval)
|
||||
goto unreg_device;
|
||||
|
||||
if (!IS_ERR_OR_NULL(ci->transceiver)) {
|
||||
retval = otg_set_peripheral(ci->transceiver->otg,
|
||||
&ci->gadget);
|
||||
if (retval)
|
||||
goto remove_dbg;
|
||||
goto put_transceiver;
|
||||
}
|
||||
|
||||
retval = usb_add_gadget_udc(dev, &ci->gadget);
|
||||
|
@ -1795,10 +1718,6 @@ remove_trans:
|
|||
}
|
||||
|
||||
dev_err(dev, "error = %i\n", retval);
|
||||
remove_dbg:
|
||||
dbg_remove_files(ci->dev);
|
||||
unreg_device:
|
||||
device_unregister(&ci->gadget.dev);
|
||||
put_transceiver:
|
||||
if (!IS_ERR_OR_NULL(ci->transceiver) && ci->global_phy)
|
||||
usb_put_phy(ci->transceiver);
|
||||
|
@ -1821,9 +1740,6 @@ static void udc_stop(struct ci13xxx *ci)
|
|||
if (ci == NULL)
|
||||
return;
|
||||
|
||||
hw_disable_vbus_intr(ci);
|
||||
cancel_work_sync(&ci->vbus_work);
|
||||
|
||||
usb_del_gadget_udc(&ci->gadget);
|
||||
|
||||
destroy_eps(ci);
|
||||
|
@ -1836,8 +1752,6 @@ static void udc_stop(struct ci13xxx *ci)
|
|||
if (ci->global_phy)
|
||||
usb_put_phy(ci->transceiver);
|
||||
}
|
||||
dbg_remove_files(ci->dev);
|
||||
device_unregister(&ci->gadget.dev);
|
||||
/* my kobject is dynamic, I swear! */
|
||||
memset(&ci->gadget, 0, sizeof(ci->gadget));
|
||||
}
|
||||
|
@ -1864,7 +1778,6 @@ int ci_hdrc_gadget_init(struct ci13xxx *ci)
|
|||
rdrv->irq = udc_irq;
|
||||
rdrv->name = "gadget";
|
||||
ci->roles[CI_ROLE_GADGET] = rdrv;
|
||||
INIT_WORK(&ci->vbus_work, vbus_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ struct ci13xxx_td {
|
|||
#define TD_CURR_OFFSET (0x0FFFUL << 0)
|
||||
#define TD_FRAME_NUM (0x07FFUL << 0)
|
||||
#define TD_RESERVED_MASK (0x0FFFUL << 0)
|
||||
} __attribute__ ((packed));
|
||||
} __attribute__ ((packed, aligned(4)));
|
||||
|
||||
/* DMA layout of queue heads */
|
||||
struct ci13xxx_qh {
|
||||
|
@ -57,7 +57,7 @@ struct ci13xxx_qh {
|
|||
/* 9 */
|
||||
u32 RESERVED;
|
||||
struct usb_ctrlrequest setup;
|
||||
} __attribute__ ((packed));
|
||||
} __attribute__ ((packed, aligned(4)));
|
||||
|
||||
/**
|
||||
* struct ci13xxx_req - usb request representation
|
||||
|
|
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
* Copyright 2012 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "ci13xxx_imx.h"
|
||||
|
||||
#define USB_DEV_MAX 4
|
||||
|
||||
#define MX25_USB_PHY_CTRL_OFFSET 0x08
|
||||
#define MX25_BM_EXTERNAL_VBUS_DIVIDER BIT(23)
|
||||
|
||||
#define MX53_USB_OTG_PHY_CTRL_0_OFFSET 0x08
|
||||
#define MX53_USB_UH2_CTRL_OFFSET 0x14
|
||||
#define MX53_USB_UH3_CTRL_OFFSET 0x18
|
||||
#define MX53_BM_OVER_CUR_DIS_H1 BIT(5)
|
||||
#define MX53_BM_OVER_CUR_DIS_OTG BIT(8)
|
||||
#define MX53_BM_OVER_CUR_DIS_UHx BIT(30)
|
||||
|
||||
#define MX6_BM_OVER_CUR_DIS BIT(7)
|
||||
|
||||
struct imx_usbmisc {
|
||||
void __iomem *base;
|
||||
spinlock_t lock;
|
||||
struct clk *clk;
|
||||
struct usbmisc_usb_device usbdev[USB_DEV_MAX];
|
||||
const struct usbmisc_ops *ops;
|
||||
};
|
||||
|
||||
static struct imx_usbmisc *usbmisc;
|
||||
|
||||
static struct usbmisc_usb_device *get_usbdev(struct device *dev)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < USB_DEV_MAX; i++) {
|
||||
if (usbmisc->usbdev[i].dev == dev)
|
||||
return &usbmisc->usbdev[i];
|
||||
else if (!usbmisc->usbdev[i].dev)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= USB_DEV_MAX)
|
||||
return ERR_PTR(-EBUSY);
|
||||
|
||||
ret = usbmisc_get_init_data(dev, &usbmisc->usbdev[i]);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return &usbmisc->usbdev[i];
|
||||
}
|
||||
|
||||
static int usbmisc_imx25_post(struct device *dev)
|
||||
{
|
||||
struct usbmisc_usb_device *usbdev;
|
||||
void __iomem *reg;
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
usbdev = get_usbdev(dev);
|
||||
if (IS_ERR(usbdev))
|
||||
return PTR_ERR(usbdev);
|
||||
|
||||
reg = usbmisc->base + MX25_USB_PHY_CTRL_OFFSET;
|
||||
|
||||
if (usbdev->evdo) {
|
||||
spin_lock_irqsave(&usbmisc->lock, flags);
|
||||
val = readl(reg);
|
||||
writel(val | MX25_BM_EXTERNAL_VBUS_DIVIDER, reg);
|
||||
spin_unlock_irqrestore(&usbmisc->lock, flags);
|
||||
usleep_range(5000, 10000); /* needed to stabilize voltage */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbmisc_imx53_init(struct device *dev)
|
||||
{
|
||||
struct usbmisc_usb_device *usbdev;
|
||||
void __iomem *reg = NULL;
|
||||
unsigned long flags;
|
||||
u32 val = 0;
|
||||
|
||||
usbdev = get_usbdev(dev);
|
||||
if (IS_ERR(usbdev))
|
||||
return PTR_ERR(usbdev);
|
||||
|
||||
if (usbdev->disable_oc) {
|
||||
spin_lock_irqsave(&usbmisc->lock, flags);
|
||||
switch (usbdev->index) {
|
||||
case 0:
|
||||
reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET;
|
||||
val = readl(reg) | MX53_BM_OVER_CUR_DIS_OTG;
|
||||
break;
|
||||
case 1:
|
||||
reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET;
|
||||
val = readl(reg) | MX53_BM_OVER_CUR_DIS_H1;
|
||||
break;
|
||||
case 2:
|
||||
reg = usbmisc->base + MX53_USB_UH2_CTRL_OFFSET;
|
||||
val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx;
|
||||
break;
|
||||
case 3:
|
||||
reg = usbmisc->base + MX53_USB_UH3_CTRL_OFFSET;
|
||||
val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx;
|
||||
break;
|
||||
}
|
||||
if (reg && val)
|
||||
writel(val, reg);
|
||||
spin_unlock_irqrestore(&usbmisc->lock, flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbmisc_imx6q_init(struct device *dev)
|
||||
{
|
||||
|
||||
struct usbmisc_usb_device *usbdev;
|
||||
unsigned long flags;
|
||||
u32 reg;
|
||||
|
||||
usbdev = get_usbdev(dev);
|
||||
if (IS_ERR(usbdev))
|
||||
return PTR_ERR(usbdev);
|
||||
|
||||
if (usbdev->disable_oc) {
|
||||
spin_lock_irqsave(&usbmisc->lock, flags);
|
||||
reg = readl(usbmisc->base + usbdev->index * 4);
|
||||
writel(reg | MX6_BM_OVER_CUR_DIS,
|
||||
usbmisc->base + usbdev->index * 4);
|
||||
spin_unlock_irqrestore(&usbmisc->lock, flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct usbmisc_ops imx25_usbmisc_ops = {
|
||||
.post = usbmisc_imx25_post,
|
||||
};
|
||||
|
||||
static const struct usbmisc_ops imx53_usbmisc_ops = {
|
||||
.init = usbmisc_imx53_init,
|
||||
};
|
||||
|
||||
static const struct usbmisc_ops imx6q_usbmisc_ops = {
|
||||
.init = usbmisc_imx6q_init,
|
||||
};
|
||||
|
||||
static const struct of_device_id usbmisc_imx_dt_ids[] = {
|
||||
{
|
||||
.compatible = "fsl,imx25-usbmisc",
|
||||
.data = &imx25_usbmisc_ops,
|
||||
},
|
||||
{
|
||||
.compatible = "fsl,imx53-usbmisc",
|
||||
.data = &imx53_usbmisc_ops,
|
||||
},
|
||||
{
|
||||
.compatible = "fsl,imx6q-usbmisc",
|
||||
.data = &imx6q_usbmisc_ops,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static int usbmisc_imx_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct imx_usbmisc *data;
|
||||
int ret;
|
||||
struct of_device_id *tmp_dev;
|
||||
|
||||
if (usbmisc)
|
||||
return -EBUSY;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&data->lock);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
data->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(data->base))
|
||||
return PTR_ERR(data->base);
|
||||
|
||||
data->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(data->clk)) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to get clock, err=%ld\n", PTR_ERR(data->clk));
|
||||
return PTR_ERR(data->clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(data->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"clk_prepare_enable failed, err=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
tmp_dev = (struct of_device_id *)
|
||||
of_match_device(usbmisc_imx_dt_ids, &pdev->dev);
|
||||
data->ops = (const struct usbmisc_ops *)tmp_dev->data;
|
||||
usbmisc = data;
|
||||
ret = usbmisc_set_ops(data->ops);
|
||||
if (ret) {
|
||||
usbmisc = NULL;
|
||||
clk_disable_unprepare(data->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbmisc_imx_remove(struct platform_device *pdev)
|
||||
{
|
||||
usbmisc_unset_ops(usbmisc->ops);
|
||||
clk_disable_unprepare(usbmisc->clk);
|
||||
usbmisc = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver usbmisc_imx_driver = {
|
||||
.probe = usbmisc_imx_probe,
|
||||
.remove = usbmisc_imx_remove,
|
||||
.driver = {
|
||||
.name = "usbmisc_imx",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = usbmisc_imx_dt_ids,
|
||||
},
|
||||
};
|
||||
|
||||
int usbmisc_imx_drv_init(void)
|
||||
{
|
||||
return platform_driver_register(&usbmisc_imx_driver);
|
||||
}
|
||||
subsys_initcall(usbmisc_imx_drv_init);
|
||||
|
||||
void usbmisc_imx_drv_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&usbmisc_imx_driver);
|
||||
}
|
||||
module_exit(usbmisc_imx_drv_exit);
|
||||
|
||||
MODULE_ALIAS("platform:usbmisc-imx");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("driver for imx usb non-core registers");
|
||||
MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");
|
|
@ -1,162 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "ci13xxx_imx.h"
|
||||
|
||||
#define USB_DEV_MAX 4
|
||||
|
||||
#define BM_OVER_CUR_DIS BIT(7)
|
||||
|
||||
struct imx6q_usbmisc {
|
||||
void __iomem *base;
|
||||
spinlock_t lock;
|
||||
struct clk *clk;
|
||||
struct usbmisc_usb_device usbdev[USB_DEV_MAX];
|
||||
};
|
||||
|
||||
static struct imx6q_usbmisc *usbmisc;
|
||||
|
||||
static struct usbmisc_usb_device *get_usbdev(struct device *dev)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < USB_DEV_MAX; i++) {
|
||||
if (usbmisc->usbdev[i].dev == dev)
|
||||
return &usbmisc->usbdev[i];
|
||||
else if (!usbmisc->usbdev[i].dev)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= USB_DEV_MAX)
|
||||
return ERR_PTR(-EBUSY);
|
||||
|
||||
ret = usbmisc_get_init_data(dev, &usbmisc->usbdev[i]);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return &usbmisc->usbdev[i];
|
||||
}
|
||||
|
||||
static int usbmisc_imx6q_init(struct device *dev)
|
||||
{
|
||||
|
||||
struct usbmisc_usb_device *usbdev;
|
||||
unsigned long flags;
|
||||
u32 reg;
|
||||
|
||||
usbdev = get_usbdev(dev);
|
||||
if (IS_ERR(usbdev))
|
||||
return PTR_ERR(usbdev);
|
||||
|
||||
if (usbdev->disable_oc) {
|
||||
spin_lock_irqsave(&usbmisc->lock, flags);
|
||||
reg = readl(usbmisc->base + usbdev->index * 4);
|
||||
writel(reg | BM_OVER_CUR_DIS,
|
||||
usbmisc->base + usbdev->index * 4);
|
||||
spin_unlock_irqrestore(&usbmisc->lock, flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct usbmisc_ops imx6q_usbmisc_ops = {
|
||||
.init = usbmisc_imx6q_init,
|
||||
};
|
||||
|
||||
static const struct of_device_id usbmisc_imx6q_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx6q-usbmisc"},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static int usbmisc_imx6q_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct imx6q_usbmisc *data;
|
||||
int ret;
|
||||
|
||||
if (usbmisc)
|
||||
return -EBUSY;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&data->lock);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
data->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(data->base))
|
||||
return PTR_ERR(data->base);
|
||||
|
||||
data->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(data->clk)) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to get clock, err=%ld\n", PTR_ERR(data->clk));
|
||||
return PTR_ERR(data->clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(data->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"clk_prepare_enable failed, err=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = usbmisc_set_ops(&imx6q_usbmisc_ops);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(data->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
usbmisc = data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbmisc_imx6q_remove(struct platform_device *pdev)
|
||||
{
|
||||
usbmisc_unset_ops(&imx6q_usbmisc_ops);
|
||||
clk_disable_unprepare(usbmisc->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver usbmisc_imx6q_driver = {
|
||||
.probe = usbmisc_imx6q_probe,
|
||||
.remove = usbmisc_imx6q_remove,
|
||||
.driver = {
|
||||
.name = "usbmisc_imx6q",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = usbmisc_imx6q_dt_ids,
|
||||
},
|
||||
};
|
||||
|
||||
int __init usbmisc_imx6q_drv_init(void)
|
||||
{
|
||||
return platform_driver_register(&usbmisc_imx6q_driver);
|
||||
}
|
||||
subsys_initcall(usbmisc_imx6q_drv_init);
|
||||
|
||||
void __exit usbmisc_imx6q_drv_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&usbmisc_imx6q_driver);
|
||||
}
|
||||
module_exit(usbmisc_imx6q_drv_exit);
|
||||
|
||||
MODULE_ALIAS("platform:usbmisc-imx6q");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("driver for imx6q usb non-core registers");
|
||||
MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");
|
|
@ -2,11 +2,10 @@
|
|||
# USB Class driver configuration
|
||||
#
|
||||
comment "USB Device Class drivers"
|
||||
depends on USB
|
||||
|
||||
config USB_ACM
|
||||
tristate "USB Modem (CDC ACM) support"
|
||||
depends on USB && TTY
|
||||
depends on TTY
|
||||
---help---
|
||||
This driver supports USB modems and ISDN adapters which support the
|
||||
Communication Device Class Abstract Control Model interface.
|
||||
|
@ -21,7 +20,6 @@ config USB_ACM
|
|||
|
||||
config USB_PRINTER
|
||||
tristate "USB Printer support"
|
||||
depends on USB
|
||||
help
|
||||
Say Y here if you want to connect a USB printer to your computer's
|
||||
USB port.
|
||||
|
@ -31,7 +29,6 @@ config USB_PRINTER
|
|||
|
||||
config USB_WDM
|
||||
tristate "USB Wireless Device Management support"
|
||||
depends on USB
|
||||
---help---
|
||||
This driver supports the WMC Device Management functionality
|
||||
of cell phones compliant to the CDC WMC specification. You can use
|
||||
|
@ -42,7 +39,6 @@ config USB_WDM
|
|||
|
||||
config USB_TMC
|
||||
tristate "USB Test and Measurement Class support"
|
||||
depends on USB
|
||||
help
|
||||
Say Y here if you want to connect a USB device that follows
|
||||
the USB.org specification for USB Test and Measurement devices
|
||||
|
|
|
@ -828,14 +828,6 @@ static int acm_tty_ioctl(struct tty_struct *tty,
|
|||
return rv;
|
||||
}
|
||||
|
||||
static const __u32 acm_tty_speed[] = {
|
||||
0, 50, 75, 110, 134, 150, 200, 300, 600,
|
||||
1200, 1800, 2400, 4800, 9600, 19200, 38400,
|
||||
57600, 115200, 230400, 460800, 500000, 576000,
|
||||
921600, 1000000, 1152000, 1500000, 2000000,
|
||||
2500000, 3000000, 3500000, 4000000
|
||||
};
|
||||
|
||||
static void acm_tty_set_termios(struct tty_struct *tty,
|
||||
struct ktermios *termios_old)
|
||||
{
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
|
@ -644,6 +645,22 @@ static int wdm_release(struct inode *inode, struct file *file)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static long wdm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct wdm_device *desc = file->private_data;
|
||||
int rv = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case IOCTL_WDM_MAX_COMMAND:
|
||||
if (copy_to_user((void __user *)arg, &desc->wMaxCommand, sizeof(desc->wMaxCommand)))
|
||||
rv = -EFAULT;
|
||||
break;
|
||||
default:
|
||||
rv = -ENOTTY;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
static const struct file_operations wdm_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = wdm_read,
|
||||
|
@ -652,6 +669,8 @@ static const struct file_operations wdm_fops = {
|
|||
.flush = wdm_flush,
|
||||
.release = wdm_release,
|
||||
.poll = wdm_poll,
|
||||
.unlocked_ioctl = wdm_ioctl,
|
||||
.compat_ioctl = wdm_ioctl,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
|
|
|
@ -718,50 +718,32 @@ exit:
|
|||
|
||||
static int usbtmc_ioctl_clear_out_halt(struct usbtmc_device_data *data)
|
||||
{
|
||||
u8 *buffer;
|
||||
int rv;
|
||||
|
||||
buffer = kmalloc(2, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
rv = usb_clear_halt(data->usb_dev,
|
||||
usb_sndbulkpipe(data->usb_dev, data->bulk_out));
|
||||
|
||||
if (rv < 0) {
|
||||
dev_err(&data->usb_dev->dev, "usb_control_msg returned %d\n",
|
||||
rv);
|
||||
goto exit;
|
||||
return rv;
|
||||
}
|
||||
rv = 0;
|
||||
|
||||
exit:
|
||||
kfree(buffer);
|
||||
return rv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbtmc_ioctl_clear_in_halt(struct usbtmc_device_data *data)
|
||||
{
|
||||
u8 *buffer;
|
||||
int rv;
|
||||
|
||||
buffer = kmalloc(2, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
rv = usb_clear_halt(data->usb_dev,
|
||||
usb_rcvbulkpipe(data->usb_dev, data->bulk_in));
|
||||
|
||||
if (rv < 0) {
|
||||
dev_err(&data->usb_dev->dev, "usb_control_msg returned %d\n",
|
||||
rv);
|
||||
goto exit;
|
||||
return rv;
|
||||
}
|
||||
rv = 0;
|
||||
|
||||
exit:
|
||||
kfree(buffer);
|
||||
return rv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_capabilities(struct usbtmc_device_data *data)
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#
|
||||
config USB_DEBUG
|
||||
bool "USB verbose debug messages"
|
||||
depends on USB
|
||||
help
|
||||
Say Y here if you want the USB core & hub drivers to produce a bunch
|
||||
of debug messages to the system log. Select this if you are having a
|
||||
|
@ -11,7 +10,6 @@ config USB_DEBUG
|
|||
|
||||
config USB_ANNOUNCE_NEW_DEVICES
|
||||
bool "USB announce new devices"
|
||||
depends on USB
|
||||
default N
|
||||
help
|
||||
Say Y here if you want the USB core to always announce the
|
||||
|
@ -25,11 +23,24 @@ config USB_ANNOUNCE_NEW_DEVICES
|
|||
log, or have any doubts about this, say N here.
|
||||
|
||||
comment "Miscellaneous USB options"
|
||||
depends on USB
|
||||
|
||||
config USB_DEFAULT_PERSIST
|
||||
bool "Enable USB persist by default"
|
||||
default y
|
||||
help
|
||||
Say N here if you don't want USB power session persistance
|
||||
enabled by default. If you say N it will make suspended USB
|
||||
devices that lose power get reenumerated as if they had been
|
||||
unplugged, causing any mounted filesystems to be lost. The
|
||||
persist feature can still be enabled for individual devices
|
||||
through the power/persist sysfs node. See
|
||||
Documentation/usb/persist.txt for more info.
|
||||
|
||||
If you have any questions about this, say Y here, only say N
|
||||
if you know exactly what you are doing.
|
||||
|
||||
config USB_DYNAMIC_MINORS
|
||||
bool "Dynamic USB minor allocation"
|
||||
depends on USB
|
||||
help
|
||||
If you say Y here, the USB subsystem will use dynamic minor
|
||||
allocation for any device that uses the USB major number.
|
||||
|
@ -38,25 +49,8 @@ config USB_DYNAMIC_MINORS
|
|||
|
||||
If you are unsure about this, say N here.
|
||||
|
||||
config USB_SUSPEND
|
||||
bool "USB runtime power management (autosuspend) and wakeup"
|
||||
depends on USB && PM_RUNTIME
|
||||
help
|
||||
If you say Y here, you can use driver calls or the sysfs
|
||||
"power/control" file to enable or disable autosuspend for
|
||||
individual USB peripherals (see
|
||||
Documentation/usb/power-management.txt for more details).
|
||||
|
||||
Also, USB "remote wakeup" signaling is supported, whereby some
|
||||
USB devices (like keyboards and network adapters) can wake up
|
||||
their parent hub. That wakeup cascades up the USB tree, and
|
||||
could wake the system from states like suspend-to-RAM.
|
||||
|
||||
If you are unsure about this, say N here.
|
||||
|
||||
config USB_OTG
|
||||
bool "OTG support"
|
||||
depends on USB
|
||||
depends on USB_SUSPEND
|
||||
default n
|
||||
help
|
||||
|
|
|
@ -739,6 +739,8 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype,
|
|||
index &= 0xff;
|
||||
switch (requesttype & USB_RECIP_MASK) {
|
||||
case USB_RECIP_ENDPOINT:
|
||||
if ((index & ~USB_DIR_IN) == 0)
|
||||
return 0;
|
||||
ret = findintfep(ps->dev, index);
|
||||
if (ret >= 0)
|
||||
ret = checkintf(ps, ret);
|
||||
|
|
|
@ -1196,9 +1196,14 @@ done:
|
|||
*
|
||||
* This is the central routine for suspending USB devices. It calls the
|
||||
* suspend methods for all the interface drivers in @udev and then calls
|
||||
* the suspend method for @udev itself. If an error occurs at any stage,
|
||||
* all the interfaces which were suspended are resumed so that they remain
|
||||
* in the same state as the device.
|
||||
* the suspend method for @udev itself. When the routine is called in
|
||||
* autosuspend, if an error occurs at any stage, all the interfaces
|
||||
* which were suspended are resumed so that they remain in the same
|
||||
* state as the device, but when called from system sleep, all error
|
||||
* from suspend methods of interfaces and the non-root-hub device itself
|
||||
* are simply ignored, so all suspended interfaces are only resumed
|
||||
* to the device's state when @udev is root-hub and its suspend method
|
||||
* returns failure.
|
||||
*
|
||||
* Autosuspend requests originating from a child device or an interface
|
||||
* driver may be made without the protection of @udev's device lock, but
|
||||
|
@ -1248,10 +1253,12 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
|
|||
|
||||
/* If the suspend failed, resume interfaces that did get suspended */
|
||||
if (status != 0) {
|
||||
msg.event ^= (PM_EVENT_SUSPEND | PM_EVENT_RESUME);
|
||||
while (++i < n) {
|
||||
intf = udev->actconfig->interface[i];
|
||||
usb_resume_interface(udev, intf, msg, 0);
|
||||
if (udev->actconfig) {
|
||||
msg.event ^= (PM_EVENT_SUSPEND | PM_EVENT_RESUME);
|
||||
while (++i < n) {
|
||||
intf = udev->actconfig->interface[i];
|
||||
usb_resume_interface(udev, intf, msg, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* If the suspend succeeded then prevent any more URB submissions
|
||||
|
@ -1407,7 +1414,7 @@ int usb_resume(struct device *dev, pm_message_t msg)
|
|||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
||||
/**
|
||||
* usb_enable_autosuspend - allow a USB device to be autosuspended
|
||||
|
@ -1775,7 +1782,7 @@ int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable)
|
|||
return ret;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_USB_SUSPEND */
|
||||
#endif /* CONFIG_PM_RUNTIME */
|
||||
|
||||
struct bus_type usb_bus_type = {
|
||||
.name = "usb",
|
||||
|
|
|
@ -169,7 +169,7 @@ static int generic_probe(struct usb_device *udev)
|
|||
c = usb_choose_configuration(udev);
|
||||
if (c >= 0) {
|
||||
err = usb_set_configuration(udev, c);
|
||||
if (err) {
|
||||
if (err && err != -ENODEV) {
|
||||
dev_err(&udev->dev, "can't set config #%d, error %d\n",
|
||||
c, err);
|
||||
/* This need not be fatal. The user can try to
|
||||
|
|
|
@ -37,119 +37,123 @@
|
|||
|
||||
/* PCI-based HCs are common, but plenty of non-PCI HCs are used too */
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
/* Coordinate handoffs between EHCI and companion controllers
|
||||
* during system resume
|
||||
/*
|
||||
* Coordinate handoffs between EHCI and companion controllers
|
||||
* during EHCI probing and system resume.
|
||||
*/
|
||||
|
||||
static DEFINE_MUTEX(companions_mutex);
|
||||
static DECLARE_RWSEM(companions_rwsem);
|
||||
|
||||
#define CL_UHCI PCI_CLASS_SERIAL_USB_UHCI
|
||||
#define CL_OHCI PCI_CLASS_SERIAL_USB_OHCI
|
||||
#define CL_EHCI PCI_CLASS_SERIAL_USB_EHCI
|
||||
|
||||
enum companion_action {
|
||||
SET_HS_COMPANION, CLEAR_HS_COMPANION, WAIT_FOR_COMPANIONS
|
||||
};
|
||||
static inline int is_ohci_or_uhci(struct pci_dev *pdev)
|
||||
{
|
||||
return pdev->class == CL_OHCI || pdev->class == CL_UHCI;
|
||||
}
|
||||
|
||||
static void companion_common(struct pci_dev *pdev, struct usb_hcd *hcd,
|
||||
enum companion_action action)
|
||||
typedef void (*companion_fn)(struct pci_dev *pdev, struct usb_hcd *hcd,
|
||||
struct pci_dev *companion, struct usb_hcd *companion_hcd);
|
||||
|
||||
/* Iterate over PCI devices in the same slot as pdev and call fn for each */
|
||||
static void for_each_companion(struct pci_dev *pdev, struct usb_hcd *hcd,
|
||||
companion_fn fn)
|
||||
{
|
||||
struct pci_dev *companion;
|
||||
struct usb_hcd *companion_hcd;
|
||||
unsigned int slot = PCI_SLOT(pdev->devfn);
|
||||
|
||||
/* Iterate through other PCI functions in the same slot.
|
||||
* If pdev is OHCI or UHCI then we are looking for EHCI, and
|
||||
* vice versa.
|
||||
/*
|
||||
* Iterate through other PCI functions in the same slot.
|
||||
* If the function's drvdata isn't set then it isn't bound to
|
||||
* a USB host controller driver, so skip it.
|
||||
*/
|
||||
companion = NULL;
|
||||
for_each_pci_dev(companion) {
|
||||
if (companion->bus != pdev->bus ||
|
||||
PCI_SLOT(companion->devfn) != slot)
|
||||
continue;
|
||||
|
||||
companion_hcd = pci_get_drvdata(companion);
|
||||
if (!companion_hcd)
|
||||
continue;
|
||||
|
||||
/* For SET_HS_COMPANION, store a pointer to the EHCI bus in
|
||||
* the OHCI/UHCI companion bus structure.
|
||||
* For CLEAR_HS_COMPANION, clear the pointer to the EHCI bus
|
||||
* in the OHCI/UHCI companion bus structure.
|
||||
* For WAIT_FOR_COMPANIONS, wait until the OHCI/UHCI
|
||||
* companion controllers have fully resumed.
|
||||
*/
|
||||
|
||||
if ((pdev->class == CL_OHCI || pdev->class == CL_UHCI) &&
|
||||
companion->class == CL_EHCI) {
|
||||
/* action must be SET_HS_COMPANION */
|
||||
dev_dbg(&companion->dev, "HS companion for %s\n",
|
||||
dev_name(&pdev->dev));
|
||||
hcd->self.hs_companion = &companion_hcd->self;
|
||||
|
||||
} else if (pdev->class == CL_EHCI &&
|
||||
(companion->class == CL_OHCI ||
|
||||
companion->class == CL_UHCI)) {
|
||||
switch (action) {
|
||||
case SET_HS_COMPANION:
|
||||
dev_dbg(&pdev->dev, "HS companion for %s\n",
|
||||
dev_name(&companion->dev));
|
||||
companion_hcd->self.hs_companion = &hcd->self;
|
||||
break;
|
||||
case CLEAR_HS_COMPANION:
|
||||
companion_hcd->self.hs_companion = NULL;
|
||||
break;
|
||||
case WAIT_FOR_COMPANIONS:
|
||||
device_pm_wait_for_dev(&pdev->dev,
|
||||
&companion->dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fn(pdev, hcd, companion, companion_hcd);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_hs_companion(struct pci_dev *pdev, struct usb_hcd *hcd)
|
||||
/*
|
||||
* We're about to add an EHCI controller, which will unceremoniously grab
|
||||
* all the port connections away from its companions. To prevent annoying
|
||||
* error messages, lock the companion's root hub and gracefully unconfigure
|
||||
* it beforehand. Leave it locked until the EHCI controller is all set.
|
||||
*/
|
||||
static void ehci_pre_add(struct pci_dev *pdev, struct usb_hcd *hcd,
|
||||
struct pci_dev *companion, struct usb_hcd *companion_hcd)
|
||||
{
|
||||
mutex_lock(&companions_mutex);
|
||||
dev_set_drvdata(&pdev->dev, hcd);
|
||||
companion_common(pdev, hcd, SET_HS_COMPANION);
|
||||
mutex_unlock(&companions_mutex);
|
||||
struct usb_device *udev;
|
||||
|
||||
if (is_ohci_or_uhci(companion)) {
|
||||
udev = companion_hcd->self.root_hub;
|
||||
usb_lock_device(udev);
|
||||
usb_set_configuration(udev, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void clear_hs_companion(struct pci_dev *pdev, struct usb_hcd *hcd)
|
||||
/*
|
||||
* Adding the EHCI controller has either succeeded or failed. Set the
|
||||
* companion pointer accordingly, and in either case, reconfigure and
|
||||
* unlock the root hub.
|
||||
*/
|
||||
static void ehci_post_add(struct pci_dev *pdev, struct usb_hcd *hcd,
|
||||
struct pci_dev *companion, struct usb_hcd *companion_hcd)
|
||||
{
|
||||
mutex_lock(&companions_mutex);
|
||||
dev_set_drvdata(&pdev->dev, NULL);
|
||||
struct usb_device *udev;
|
||||
|
||||
/* If pdev is OHCI or UHCI, just clear its hs_companion pointer */
|
||||
if (pdev->class == CL_OHCI || pdev->class == CL_UHCI)
|
||||
hcd->self.hs_companion = NULL;
|
||||
|
||||
/* Otherwise search for companion buses and clear their pointers */
|
||||
else
|
||||
companion_common(pdev, hcd, CLEAR_HS_COMPANION);
|
||||
mutex_unlock(&companions_mutex);
|
||||
if (is_ohci_or_uhci(companion)) {
|
||||
if (dev_get_drvdata(&pdev->dev)) { /* Succeeded */
|
||||
dev_dbg(&pdev->dev, "HS companion for %s\n",
|
||||
dev_name(&companion->dev));
|
||||
companion_hcd->self.hs_companion = &hcd->self;
|
||||
}
|
||||
udev = companion_hcd->self.root_hub;
|
||||
usb_set_configuration(udev, 1);
|
||||
usb_unlock_device(udev);
|
||||
}
|
||||
}
|
||||
|
||||
static void wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd)
|
||||
/*
|
||||
* We just added a non-EHCI controller. Find the EHCI controller to
|
||||
* which it is a companion, and store a pointer to the bus structure.
|
||||
*/
|
||||
static void non_ehci_add(struct pci_dev *pdev, struct usb_hcd *hcd,
|
||||
struct pci_dev *companion, struct usb_hcd *companion_hcd)
|
||||
{
|
||||
/* Only EHCI controllers need to wait.
|
||||
* No locking is needed because a controller cannot be resumed
|
||||
* while one of its companions is getting unbound.
|
||||
*/
|
||||
if (pdev->class == CL_EHCI)
|
||||
companion_common(pdev, hcd, WAIT_FOR_COMPANIONS);
|
||||
if (is_ohci_or_uhci(pdev) && companion->class == CL_EHCI) {
|
||||
dev_dbg(&pdev->dev, "FS/LS companion for %s\n",
|
||||
dev_name(&companion->dev));
|
||||
hcd->self.hs_companion = &companion_hcd->self;
|
||||
}
|
||||
}
|
||||
|
||||
#else /* !CONFIG_PM_SLEEP */
|
||||
/* We are removing an EHCI controller. Clear the companions' pointers. */
|
||||
static void ehci_remove(struct pci_dev *pdev, struct usb_hcd *hcd,
|
||||
struct pci_dev *companion, struct usb_hcd *companion_hcd)
|
||||
{
|
||||
if (is_ohci_or_uhci(companion))
|
||||
companion_hcd->self.hs_companion = NULL;
|
||||
}
|
||||
|
||||
static inline void set_hs_companion(struct pci_dev *d, struct usb_hcd *h) {}
|
||||
static inline void clear_hs_companion(struct pci_dev *d, struct usb_hcd *h) {}
|
||||
static inline void wait_for_companions(struct pci_dev *d, struct usb_hcd *h) {}
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
#endif /* !CONFIG_PM_SLEEP */
|
||||
/* An EHCI controller must wait for its companions before resuming. */
|
||||
static void ehci_wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd,
|
||||
struct pci_dev *companion, struct usb_hcd *companion_hcd)
|
||||
{
|
||||
if (is_ohci_or_uhci(companion))
|
||||
device_pm_wait_for_dev(&pdev->dev, &companion->dev);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
@ -217,7 +221,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
driver->description)) {
|
||||
dev_dbg(&dev->dev, "controller already in use\n");
|
||||
retval = -EBUSY;
|
||||
goto clear_companion;
|
||||
goto put_hcd;
|
||||
}
|
||||
hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
|
||||
if (hcd->regs == NULL) {
|
||||
|
@ -244,16 +248,35 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
if (region == PCI_ROM_RESOURCE) {
|
||||
dev_dbg(&dev->dev, "no i/o regions available\n");
|
||||
retval = -EBUSY;
|
||||
goto clear_companion;
|
||||
goto put_hcd;
|
||||
}
|
||||
}
|
||||
|
||||
pci_set_master(dev);
|
||||
|
||||
retval = usb_add_hcd(hcd, hcd_irq, IRQF_SHARED);
|
||||
/* Note: dev_set_drvdata must be called while holding the rwsem */
|
||||
if (dev->class == CL_EHCI) {
|
||||
down_write(&companions_rwsem);
|
||||
dev_set_drvdata(&dev->dev, hcd);
|
||||
for_each_companion(dev, hcd, ehci_pre_add);
|
||||
retval = usb_add_hcd(hcd, hcd_irq, IRQF_SHARED);
|
||||
if (retval != 0)
|
||||
dev_set_drvdata(&dev->dev, NULL);
|
||||
for_each_companion(dev, hcd, ehci_post_add);
|
||||
up_write(&companions_rwsem);
|
||||
} else {
|
||||
down_read(&companions_rwsem);
|
||||
dev_set_drvdata(&dev->dev, hcd);
|
||||
retval = usb_add_hcd(hcd, hcd_irq, IRQF_SHARED);
|
||||
if (retval != 0)
|
||||
dev_set_drvdata(&dev->dev, NULL);
|
||||
else
|
||||
for_each_companion(dev, hcd, non_ehci_add);
|
||||
up_read(&companions_rwsem);
|
||||
}
|
||||
|
||||
if (retval != 0)
|
||||
goto unmap_registers;
|
||||
set_hs_companion(dev, hcd);
|
||||
|
||||
if (pci_dev_run_wake(dev))
|
||||
pm_runtime_put_noidle(&dev->dev);
|
||||
|
@ -266,8 +289,7 @@ release_mem_region:
|
|||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
} else
|
||||
release_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
clear_companion:
|
||||
clear_hs_companion(dev, hcd);
|
||||
put_hcd:
|
||||
usb_put_hcd(hcd);
|
||||
disable_pci:
|
||||
pci_disable_device(dev);
|
||||
|
@ -310,14 +332,29 @@ void usb_hcd_pci_remove(struct pci_dev *dev)
|
|||
usb_hcd_irq(0, hcd);
|
||||
local_irq_enable();
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
/* Note: dev_set_drvdata must be called while holding the rwsem */
|
||||
if (dev->class == CL_EHCI) {
|
||||
down_write(&companions_rwsem);
|
||||
for_each_companion(dev, hcd, ehci_remove);
|
||||
usb_remove_hcd(hcd);
|
||||
dev_set_drvdata(&dev->dev, NULL);
|
||||
up_write(&companions_rwsem);
|
||||
} else {
|
||||
/* Not EHCI; just clear the companion pointer */
|
||||
down_read(&companions_rwsem);
|
||||
hcd->self.hs_companion = NULL;
|
||||
usb_remove_hcd(hcd);
|
||||
dev_set_drvdata(&dev->dev, NULL);
|
||||
up_read(&companions_rwsem);
|
||||
}
|
||||
|
||||
if (hcd->driver->flags & HCD_MEMORY) {
|
||||
iounmap(hcd->regs);
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
} else {
|
||||
release_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
}
|
||||
clear_hs_companion(dev, hcd);
|
||||
|
||||
usb_put_hcd(hcd);
|
||||
pci_disable_device(dev);
|
||||
}
|
||||
|
@ -463,8 +500,15 @@ static int resume_common(struct device *dev, int event)
|
|||
pci_set_master(pci_dev);
|
||||
|
||||
if (hcd->driver->pci_resume && !HCD_DEAD(hcd)) {
|
||||
if (event != PM_EVENT_AUTO_RESUME)
|
||||
wait_for_companions(pci_dev, hcd);
|
||||
|
||||
/*
|
||||
* Only EHCI controllers have to wait for their companions.
|
||||
* No locking is needed because PCI controller drivers do not
|
||||
* get unbound during system resume.
|
||||
*/
|
||||
if (pci_dev->class == CL_EHCI && event != PM_EVENT_AUTO_RESUME)
|
||||
for_each_companion(pci_dev, hcd,
|
||||
ehci_wait_for_companions);
|
||||
|
||||
retval = hcd->driver->pci_resume(hcd,
|
||||
event == PM_EVENT_RESTORE);
|
||||
|
|
|
@ -2125,7 +2125,7 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
|
|||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
||||
/* Workqueue routine for root-hub remote wakeup */
|
||||
static void hcd_resume_work(struct work_struct *work)
|
||||
|
@ -2160,7 +2160,7 @@ void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub);
|
||||
|
||||
#endif /* CONFIG_USB_SUSPEND */
|
||||
#endif /* CONFIG_PM_RUNTIME */
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
@ -2336,7 +2336,7 @@ struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver,
|
|||
init_timer(&hcd->rh_timer);
|
||||
hcd->rh_timer.function = rh_timer_func;
|
||||
hcd->rh_timer.data = (unsigned long) hcd;
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
|
||||
#endif
|
||||
|
||||
|
@ -2590,7 +2590,7 @@ error_create_attr_group:
|
|||
hcd->rh_registered = 0;
|
||||
spin_unlock_irq(&hcd_root_hub_lock);
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
cancel_work_sync(&hcd->wakeup_work);
|
||||
#endif
|
||||
mutex_lock(&usb_bus_list_lock);
|
||||
|
@ -2645,7 +2645,7 @@ void usb_remove_hcd(struct usb_hcd *hcd)
|
|||
hcd->rh_registered = 0;
|
||||
spin_unlock_irq (&hcd_root_hub_lock);
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
cancel_work_sync(&hcd->wakeup_work);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -555,8 +555,9 @@ static int hub_port_status(struct usb_hub *hub, int port1,
|
|||
mutex_lock(&hub->status_mutex);
|
||||
ret = get_port_status(hub->hdev, port1, &hub->status->port);
|
||||
if (ret < 4) {
|
||||
dev_err(hub->intfdev,
|
||||
"%s failed (err = %d)\n", __func__, ret);
|
||||
if (ret != -ENODEV)
|
||||
dev_err(hub->intfdev,
|
||||
"%s failed (err = %d)\n", __func__, ret);
|
||||
if (ret >= 0)
|
||||
ret = -EIO;
|
||||
} else {
|
||||
|
@ -699,7 +700,7 @@ static void hub_tt_work(struct work_struct *work)
|
|||
/* drop lock so HCD can concurrently report other TT errors */
|
||||
spin_unlock_irqrestore (&hub->tt.lock, flags);
|
||||
status = hub_clear_tt_buffer (hdev, clear->devinfo, clear->tt);
|
||||
if (status)
|
||||
if (status && status != -ENODEV)
|
||||
dev_err (&hdev->dev,
|
||||
"clear tt %d (%04x) error %d\n",
|
||||
clear->tt, clear->devinfo, status);
|
||||
|
@ -837,10 +838,11 @@ static int hub_hub_status(struct usb_hub *hub,
|
|||
|
||||
mutex_lock(&hub->status_mutex);
|
||||
ret = get_hub_status(hub->hdev, &hub->status->hub);
|
||||
if (ret < 0)
|
||||
dev_err (hub->intfdev,
|
||||
"%s failed (err = %d)\n", __func__, ret);
|
||||
else {
|
||||
if (ret < 0) {
|
||||
if (ret != -ENODEV)
|
||||
dev_err(hub->intfdev,
|
||||
"%s failed (err = %d)\n", __func__, ret);
|
||||
} else {
|
||||
*status = le16_to_cpu(hub->status->hub.wHubStatus);
|
||||
*change = le16_to_cpu(hub->status->hub.wHubChange);
|
||||
ret = 0;
|
||||
|
@ -877,11 +879,8 @@ static int hub_usb3_port_disable(struct usb_hub *hub, int port1)
|
|||
return -EINVAL;
|
||||
|
||||
ret = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_SS_DISABLED);
|
||||
if (ret) {
|
||||
dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n",
|
||||
port1, ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Wait for the link to enter the disabled state. */
|
||||
for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {
|
||||
|
@ -918,7 +917,7 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
|
|||
ret = usb_clear_port_feature(hdev, port1,
|
||||
USB_PORT_FEAT_ENABLE);
|
||||
}
|
||||
if (ret)
|
||||
if (ret && ret != -ENODEV)
|
||||
dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n",
|
||||
port1, ret);
|
||||
return ret;
|
||||
|
@ -1317,6 +1316,10 @@ static int hub_configure(struct usb_hub *hub,
|
|||
message = "hub has too many ports!";
|
||||
ret = -ENODEV;
|
||||
goto fail;
|
||||
} else if (hub->descriptor->bNbrPorts == 0) {
|
||||
message = "hub doesn't have any ports!";
|
||||
ret = -ENODEV;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hdev->maxchild = hub->descriptor->bNbrPorts;
|
||||
|
@ -2192,8 +2195,9 @@ static int usb_enumerate_device(struct usb_device *udev)
|
|||
if (udev->config == NULL) {
|
||||
err = usb_get_configuration(udev);
|
||||
if (err < 0) {
|
||||
dev_err(&udev->dev, "can't read configurations, error %d\n",
|
||||
err);
|
||||
if (err != -ENODEV)
|
||||
dev_err(&udev->dev, "can't read configurations, error %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
@ -2640,14 +2644,16 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
|
|||
status = set_port_feature(hub->hdev, port1, (warm ?
|
||||
USB_PORT_FEAT_BH_PORT_RESET :
|
||||
USB_PORT_FEAT_RESET));
|
||||
if (status) {
|
||||
if (status == -ENODEV) {
|
||||
; /* The hub is gone */
|
||||
} else if (status) {
|
||||
dev_err(hub->intfdev,
|
||||
"cannot %sreset port %d (err = %d)\n",
|
||||
warm ? "warm " : "", port1, status);
|
||||
} else {
|
||||
status = hub_port_wait_reset(hub, port1, udev, delay,
|
||||
warm);
|
||||
if (status && status != -ENOTCONN)
|
||||
if (status && status != -ENOTCONN && status != -ENODEV)
|
||||
dev_dbg(hub->intfdev,
|
||||
"port_wait_reset: err = %d\n",
|
||||
status);
|
||||
|
@ -2821,7 +2827,7 @@ void usb_enable_ltm(struct usb_device *udev)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(usb_enable_ltm);
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
#ifdef CONFIG_PM
|
||||
/*
|
||||
* usb_disable_function_remotewakeup - disable usb3.0
|
||||
* device's function remote wakeup
|
||||
|
@ -2880,9 +2886,11 @@ static int usb_disable_function_remotewakeup(struct usb_device *udev)
|
|||
* Linux (2.6) currently has NO mechanisms to initiate that: no khubd
|
||||
* timer, no SRP, no requests through sysfs.
|
||||
*
|
||||
* If CONFIG_USB_SUSPEND isn't enabled, devices only really suspend when
|
||||
* the root hub for their bus goes into global suspend ... so we don't
|
||||
* (falsely) update the device power state to say it suspended.
|
||||
* If Runtime PM isn't enabled or used, non-SuperSpeed devices really get
|
||||
* suspended only when their bus goes into global suspend (i.e., the root
|
||||
* hub is suspended). Nevertheless, we change @udev->state to
|
||||
* USB_STATE_SUSPENDED as this is the device's "logical" state. The actual
|
||||
* upstream port setting is stored in @udev->port_is_suspended.
|
||||
*
|
||||
* Returns 0 on success, else negative errno.
|
||||
*/
|
||||
|
@ -2893,6 +2901,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
|
|||
enum pm_qos_flags_status pm_qos_stat;
|
||||
int port1 = udev->portnum;
|
||||
int status;
|
||||
bool really_suspend = true;
|
||||
|
||||
/* enable remote wakeup when appropriate; this lets the device
|
||||
* wake up the upstream hub (including maybe the root hub).
|
||||
|
@ -2949,9 +2958,19 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
|
|||
/* see 7.1.7.6 */
|
||||
if (hub_is_superspeed(hub->hdev))
|
||||
status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U3);
|
||||
else
|
||||
else if (PMSG_IS_AUTO(msg))
|
||||
status = set_port_feature(hub->hdev, port1,
|
||||
USB_PORT_FEAT_SUSPEND);
|
||||
/*
|
||||
* For system suspend, we do not need to enable the suspend feature
|
||||
* on individual USB-2 ports. The devices will automatically go
|
||||
* into suspend a few ms after the root hub stops sending packets.
|
||||
* The USB 2.0 spec calls this "global suspend".
|
||||
*/
|
||||
else {
|
||||
really_suspend = false;
|
||||
status = 0;
|
||||
}
|
||||
if (status) {
|
||||
dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n",
|
||||
port1, status);
|
||||
|
@ -2987,8 +3006,10 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
|
|||
(PMSG_IS_AUTO(msg) ? "auto-" : ""),
|
||||
udev->do_remote_wakeup);
|
||||
usb_set_device_state(udev, USB_STATE_SUSPENDED);
|
||||
udev->port_is_suspended = 1;
|
||||
msleep(10);
|
||||
if (really_suspend) {
|
||||
udev->port_is_suspended = 1;
|
||||
msleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3226,6 +3247,10 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
|
|||
return status;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
||||
/* caller has locked udev */
|
||||
int usb_remote_wakeup(struct usb_device *udev)
|
||||
{
|
||||
|
@ -3242,38 +3267,6 @@ int usb_remote_wakeup(struct usb_device *udev)
|
|||
return status;
|
||||
}
|
||||
|
||||
#else /* CONFIG_USB_SUSPEND */
|
||||
|
||||
/* When CONFIG_USB_SUSPEND isn't set, we never suspend or resume any ports. */
|
||||
|
||||
int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* However we may need to do a reset-resume */
|
||||
|
||||
int usb_port_resume(struct usb_device *udev, pm_message_t msg)
|
||||
{
|
||||
struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
|
||||
int port1 = udev->portnum;
|
||||
int status;
|
||||
u16 portchange, portstatus;
|
||||
|
||||
status = hub_port_status(hub, port1, &portstatus, &portchange);
|
||||
status = check_port_resume_type(udev,
|
||||
hub, port1, status, portchange, portstatus);
|
||||
|
||||
if (status) {
|
||||
dev_dbg(&udev->dev, "can't resume, status %d\n", status);
|
||||
hub_port_logical_disconnect(hub, port1);
|
||||
} else if (udev->reset_resume) {
|
||||
dev_dbg(&udev->dev, "reset-resume\n");
|
||||
status = usb_reset_and_verify_device(udev);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int check_ports_changed(struct usb_hub *hub)
|
||||
|
@ -4090,9 +4083,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
goto fail;
|
||||
}
|
||||
if (r) {
|
||||
dev_err(&udev->dev,
|
||||
"device descriptor read/64, error %d\n",
|
||||
r);
|
||||
if (r != -ENODEV)
|
||||
dev_err(&udev->dev, "device descriptor read/64, error %d\n",
|
||||
r);
|
||||
retval = -EMSGSIZE;
|
||||
continue;
|
||||
}
|
||||
|
@ -4112,9 +4105,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
msleep(200);
|
||||
}
|
||||
if (retval < 0) {
|
||||
dev_err(&udev->dev,
|
||||
"device not accepting address %d, error %d\n",
|
||||
devnum, retval);
|
||||
if (retval != -ENODEV)
|
||||
dev_err(&udev->dev, "device not accepting address %d, error %d\n",
|
||||
devnum, retval);
|
||||
goto fail;
|
||||
}
|
||||
if (udev->speed == USB_SPEED_SUPER) {
|
||||
|
@ -4136,7 +4129,8 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
|
||||
retval = usb_get_device_descriptor(udev, 8);
|
||||
if (retval < 8) {
|
||||
dev_err(&udev->dev,
|
||||
if (retval != -ENODEV)
|
||||
dev_err(&udev->dev,
|
||||
"device descriptor read/8, error %d\n",
|
||||
retval);
|
||||
if (retval >= 0)
|
||||
|
@ -4190,8 +4184,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
|
||||
retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
|
||||
if (retval < (signed)sizeof(udev->descriptor)) {
|
||||
dev_err(&udev->dev, "device descriptor read/all, error %d\n",
|
||||
retval);
|
||||
if (retval != -ENODEV)
|
||||
dev_err(&udev->dev, "device descriptor read/all, error %d\n",
|
||||
retval);
|
||||
if (retval >= 0)
|
||||
retval = -ENOMSG;
|
||||
goto fail;
|
||||
|
@ -4333,7 +4328,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
|
|||
if (portstatus & USB_PORT_STAT_ENABLE) {
|
||||
status = 0; /* Nothing to do */
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
} else if (udev->state == USB_STATE_SUSPENDED &&
|
||||
udev->persist_enabled) {
|
||||
/* For a suspended device, treat this as a
|
||||
|
@ -4373,7 +4368,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
|
|||
USB_PORT_STAT_C_ENABLE)) {
|
||||
status = hub_port_debounce_be_stable(hub, port1);
|
||||
if (status < 0) {
|
||||
if (printk_ratelimit())
|
||||
if (status != -ENODEV && printk_ratelimit())
|
||||
dev_err(hub_dev, "connect-debounce failed, "
|
||||
"port %d disabled\n", port1);
|
||||
portstatus &= ~USB_PORT_STAT_CONNECTION;
|
||||
|
@ -4402,6 +4397,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
|
|||
else
|
||||
unit_load = 100;
|
||||
|
||||
status = 0;
|
||||
for (i = 0; i < SET_CONFIG_TRIES; i++) {
|
||||
|
||||
/* reallocate for each attempt, since references
|
||||
|
@ -4526,9 +4522,11 @@ loop:
|
|||
}
|
||||
if (hub->hdev->parent ||
|
||||
!hcd->driver->port_handed_over ||
|
||||
!(hcd->driver->port_handed_over)(hcd, port1))
|
||||
dev_err(hub_dev, "unable to enumerate USB device on port %d\n",
|
||||
port1);
|
||||
!(hcd->driver->port_handed_over)(hcd, port1)) {
|
||||
if (status != -ENOTCONN && status != -ENODEV)
|
||||
dev_err(hub_dev, "unable to enumerate USB device on port %d\n",
|
||||
port1);
|
||||
}
|
||||
|
||||
done:
|
||||
hub_port_disable(hub, port1, 1);
|
||||
|
|
|
@ -70,7 +70,7 @@ static void usb_port_device_release(struct device *dev)
|
|||
kfree(port_dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
static int usb_port_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct usb_port *port_dev = to_usb_port(dev);
|
||||
|
@ -138,7 +138,7 @@ static int usb_port_runtime_suspend(struct device *dev)
|
|||
#endif
|
||||
|
||||
static const struct dev_pm_ops usb_port_pm_ops = {
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
.runtime_suspend = usb_port_runtime_suspend,
|
||||
.runtime_resume = usb_port_runtime_resume,
|
||||
.runtime_idle = pm_generic_runtime_idle,
|
||||
|
|
|
@ -201,20 +201,14 @@ void usb_detect_quirks(struct usb_device *udev)
|
|||
dev_dbg(&udev->dev, "USB quirks for this device: %x\n",
|
||||
udev->quirks);
|
||||
|
||||
/* For the present, all devices default to USB-PERSIST enabled */
|
||||
#if 0 /* was: #ifdef CONFIG_PM */
|
||||
#ifdef CONFIG_USB_DEFAULT_PERSIST
|
||||
if (!(udev->quirks & USB_QUIRK_RESET))
|
||||
udev->persist_enabled = 1;
|
||||
#else
|
||||
/* Hubs are automatically enabled for USB-PERSIST */
|
||||
if (udev->descriptor.bDeviceClass == USB_CLASS_HUB)
|
||||
udev->persist_enabled = 1;
|
||||
|
||||
#else
|
||||
/* In the absence of PM, we can safely enable USB-PERSIST
|
||||
* for all devices. It will affect things like hub resets
|
||||
* and EMF-related port disables.
|
||||
*/
|
||||
if (!(udev->quirks & USB_QUIRK_RESET))
|
||||
udev->persist_enabled = 1;
|
||||
#endif /* CONFIG_PM */
|
||||
#endif /* CONFIG_USB_DEFAULT_PERSIST */
|
||||
}
|
||||
|
||||
void usb_detect_interface_quirks(struct usb_device *udev)
|
||||
|
|
|
@ -338,7 +338,7 @@ static void remove_persist_attributes(struct device *dev)
|
|||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
||||
static ssize_t
|
||||
show_connected_duration(struct device *dev, struct device_attribute *attr,
|
||||
|
@ -544,7 +544,7 @@ static void remove_power_attributes(struct device *dev)
|
|||
#define add_power_attributes(dev) 0
|
||||
#define remove_power_attributes(dev) do {} while (0)
|
||||
|
||||
#endif /* CONFIG_USB_SUSPEND */
|
||||
#endif /* CONFIG_PM_RUNTIME */
|
||||
|
||||
|
||||
/* Descriptor fields */
|
||||
|
|
|
@ -683,10 +683,13 @@ EXPORT_SYMBOL_GPL(usb_kill_urb);
|
|||
void usb_poison_urb(struct urb *urb)
|
||||
{
|
||||
might_sleep();
|
||||
if (!(urb && urb->dev && urb->ep))
|
||||
if (!urb)
|
||||
return;
|
||||
atomic_inc(&urb->reject);
|
||||
|
||||
if (!urb->dev || !urb->ep)
|
||||
return;
|
||||
|
||||
usb_hcd_unlink_urb(urb, -ENOENT);
|
||||
wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ const char *usbcore_name = "usbcore";
|
|||
|
||||
static bool nousb; /* Disable USB when built into kernel image */
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
static int usb_autosuspend_delay = 2; /* Default delay value,
|
||||
* in seconds */
|
||||
module_param_named(autosuspend, usb_autosuspend_delay, int, 0644);
|
||||
|
@ -307,7 +307,7 @@ static const struct dev_pm_ops usb_device_pm_ops = {
|
|||
.thaw = usb_dev_thaw,
|
||||
.poweroff = usb_dev_poweroff,
|
||||
.restore = usb_dev_restore,
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
.runtime_suspend = usb_runtime_suspend,
|
||||
.runtime_resume = usb_runtime_resume,
|
||||
.runtime_idle = usb_runtime_idle,
|
||||
|
|
|
@ -93,7 +93,7 @@ static inline int usb_port_resume(struct usb_device *udev, pm_message_t msg)
|
|||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
||||
extern void usb_autosuspend_device(struct usb_device *udev);
|
||||
extern int usb_autoresume_device(struct usb_device *udev);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
config USB_DWC3
|
||||
tristate "DesignWare USB3 DRD Core Support"
|
||||
depends on (USB || USB_GADGET) && GENERIC_HARDIRQS
|
||||
select USB_OTG_UTILS
|
||||
select USB_XHCI_PLATFORM if USB_SUPPORT && USB_XHCI_HCD
|
||||
help
|
||||
Say Y or M here if your system has a Dual Role SuperSpeed
|
||||
|
|
|
@ -140,7 +140,8 @@ static void dwc3_free_one_event_buffer(struct dwc3 *dwc,
|
|||
* Returns a pointer to the allocated event buffer structure on success
|
||||
* otherwise ERR_PTR(errno).
|
||||
*/
|
||||
static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc, unsigned length)
|
||||
static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc,
|
||||
unsigned length)
|
||||
{
|
||||
struct dwc3_event_buffer *evt;
|
||||
|
||||
|
@ -259,6 +260,17 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
|
|||
}
|
||||
}
|
||||
|
||||
static void dwc3_core_num_eps(struct dwc3 *dwc)
|
||||
{
|
||||
struct dwc3_hwparams *parms = &dwc->hwparams;
|
||||
|
||||
dwc->num_in_eps = DWC3_NUM_IN_EPS(parms);
|
||||
dwc->num_out_eps = DWC3_NUM_EPS(parms) - dwc->num_in_eps;
|
||||
|
||||
dev_vdbg(dwc->dev, "found %d IN and %d OUT endpoints\n",
|
||||
dwc->num_in_eps, dwc->num_out_eps);
|
||||
}
|
||||
|
||||
static void dwc3_cache_hwparams(struct dwc3 *dwc)
|
||||
{
|
||||
struct dwc3_hwparams *parms = &dwc->hwparams;
|
||||
|
@ -335,13 +347,9 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
|||
if (dwc->revision < DWC3_REVISION_190A)
|
||||
reg |= DWC3_GCTL_U2RSTECN;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
|
||||
dwc3_core_num_eps(dwc);
|
||||
|
||||
ret = dwc3_event_buffers_setup(dwc);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to setup event buffers\n");
|
||||
goto err0;
|
||||
}
|
||||
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -351,8 +359,6 @@ err0:
|
|||
|
||||
static void dwc3_core_exit(struct dwc3 *dwc)
|
||||
{
|
||||
dwc3_event_buffers_cleanup(dwc);
|
||||
|
||||
usb_phy_shutdown(dwc->usb2_phy);
|
||||
usb_phy_shutdown(dwc->usb3_phy);
|
||||
}
|
||||
|
@ -428,12 +434,32 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
dwc->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3);
|
||||
}
|
||||
|
||||
if (IS_ERR_OR_NULL(dwc->usb2_phy)) {
|
||||
if (IS_ERR(dwc->usb2_phy)) {
|
||||
ret = PTR_ERR(dwc->usb2_phy);
|
||||
|
||||
/*
|
||||
* if -ENXIO is returned, it means PHY layer wasn't
|
||||
* enabled, so it makes no sense to return -EPROBE_DEFER
|
||||
* in that case, since no PHY driver will ever probe.
|
||||
*/
|
||||
if (ret == -ENXIO)
|
||||
return ret;
|
||||
|
||||
dev_err(dev, "no usb2 phy configured\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
if (IS_ERR_OR_NULL(dwc->usb3_phy)) {
|
||||
if (IS_ERR(dwc->usb3_phy)) {
|
||||
ret = PTR_ERR(dwc->usb2_phy);
|
||||
|
||||
/*
|
||||
* if -ENXIO is returned, it means PHY layer wasn't
|
||||
* enabled, so it makes no sense to return -EPROBE_DEFER
|
||||
* in that case, since no PHY driver will ever probe.
|
||||
*/
|
||||
if (ret == -ENXIO)
|
||||
return ret;
|
||||
|
||||
dev_err(dev, "no usb3 phy configured\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
@ -448,6 +474,10 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
dwc->regs_size = resource_size(res);
|
||||
dwc->dev = dev;
|
||||
|
||||
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 (!strncmp("super", maximum_speed, 5))
|
||||
dwc->maximum_speed = DWC3_DCFG_SUPERSPEED;
|
||||
else if (!strncmp("high", maximum_speed, 4))
|
||||
|
@ -480,7 +510,18 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
goto err0;
|
||||
}
|
||||
|
||||
mode = DWC3_MODE(dwc->hwparams.hwparams0);
|
||||
ret = dwc3_event_buffers_setup(dwc);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to setup event buffers\n");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
|
||||
mode = DWC3_MODE_HOST;
|
||||
else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
|
||||
mode = DWC3_MODE_DEVICE;
|
||||
else
|
||||
mode = DWC3_MODE_DRD;
|
||||
|
||||
switch (mode) {
|
||||
case DWC3_MODE_DEVICE:
|
||||
|
@ -488,7 +529,7 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
ret = dwc3_gadget_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize gadget\n");
|
||||
goto err1;
|
||||
goto err2;
|
||||
}
|
||||
break;
|
||||
case DWC3_MODE_HOST:
|
||||
|
@ -496,7 +537,7 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
ret = dwc3_host_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize host\n");
|
||||
goto err1;
|
||||
goto err2;
|
||||
}
|
||||
break;
|
||||
case DWC3_MODE_DRD:
|
||||
|
@ -504,32 +545,32 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
ret = dwc3_host_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize host\n");
|
||||
goto err1;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
ret = dwc3_gadget_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize gadget\n");
|
||||
goto err1;
|
||||
goto err2;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Unsupported mode of operation %d\n", mode);
|
||||
goto err1;
|
||||
goto err2;
|
||||
}
|
||||
dwc->mode = mode;
|
||||
|
||||
ret = dwc3_debugfs_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize debugfs\n");
|
||||
goto err2;
|
||||
goto err3;
|
||||
}
|
||||
|
||||
pm_runtime_allow(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
err3:
|
||||
switch (mode) {
|
||||
case DWC3_MODE_DEVICE:
|
||||
dwc3_gadget_exit(dwc);
|
||||
|
@ -546,6 +587,9 @@ err2:
|
|||
break;
|
||||
}
|
||||
|
||||
err2:
|
||||
dwc3_event_buffers_cleanup(dwc);
|
||||
|
||||
err1:
|
||||
dwc3_core_exit(dwc);
|
||||
|
||||
|
@ -583,12 +627,130 @@ static int dwc3_remove(struct platform_device *pdev)
|
|||
break;
|
||||
}
|
||||
|
||||
dwc3_event_buffers_cleanup(dwc);
|
||||
dwc3_free_event_buffers(dwc);
|
||||
dwc3_core_exit(dwc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#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->mode) {
|
||||
case DWC3_MODE_DEVICE:
|
||||
case DWC3_MODE_DRD:
|
||||
dwc3_gadget_prepare(dwc);
|
||||
/* FALLTHROUGH */
|
||||
case DWC3_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);
|
||||
|
||||
switch (dwc->mode) {
|
||||
case DWC3_MODE_DEVICE:
|
||||
case DWC3_MODE_DRD:
|
||||
dwc3_gadget_complete(dwc);
|
||||
/* FALLTHROUGH */
|
||||
case DWC3_MODE_HOST:
|
||||
default:
|
||||
dwc3_event_buffers_setup(dwc);
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
}
|
||||
|
||||
static int dwc3_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
switch (dwc->mode) {
|
||||
case DWC3_MODE_DEVICE:
|
||||
case DWC3_MODE_DRD:
|
||||
dwc3_gadget_suspend(dwc);
|
||||
/* FALLTHROUGH */
|
||||
case DWC3_MODE_HOST:
|
||||
default:
|
||||
/* do nothing */
|
||||
break;
|
||||
}
|
||||
|
||||
dwc->gctl = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
usb_phy_shutdown(dwc->usb3_phy);
|
||||
usb_phy_shutdown(dwc->usb2_phy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
|
||||
usb_phy_init(dwc->usb3_phy);
|
||||
usb_phy_init(dwc->usb2_phy);
|
||||
msleep(100);
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GCTL, dwc->gctl);
|
||||
|
||||
switch (dwc->mode) {
|
||||
case DWC3_MODE_DEVICE:
|
||||
case DWC3_MODE_DRD:
|
||||
dwc3_gadget_resume(dwc);
|
||||
/* FALLTHROUGH */
|
||||
case DWC3_MODE_HOST:
|
||||
default:
|
||||
/* do nothing */
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
};
|
||||
|
||||
#define DWC3_PM_OPS &(dwc3_dev_pm_ops)
|
||||
#else
|
||||
#define DWC3_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id of_dwc3_match[] = {
|
||||
{
|
||||
|
@ -605,6 +767,7 @@ static struct platform_driver dwc3_driver = {
|
|||
.driver = {
|
||||
.name = "dwc3",
|
||||
.of_match_table = of_match_ptr(of_dwc3_match),
|
||||
.pm = DWC3_PM_OPS,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -154,8 +154,9 @@
|
|||
/* OTG Registers */
|
||||
#define DWC3_OCFG 0xcc00
|
||||
#define DWC3_OCTL 0xcc04
|
||||
#define DWC3_OEVTEN 0xcc08
|
||||
#define DWC3_OSTS 0xcc0C
|
||||
#define DWC3_OEVT 0xcc08
|
||||
#define DWC3_OEVTEN 0xcc0C
|
||||
#define DWC3_OSTS 0xcc10
|
||||
|
||||
/* Bit fields */
|
||||
|
||||
|
@ -369,6 +370,9 @@ struct dwc3_trb;
|
|||
* @list: a list of event buffers
|
||||
* @buf: _THE_ buffer
|
||||
* @length: size of this buffer
|
||||
* @lpos: event offset
|
||||
* @count: cache of last read event count register
|
||||
* @flags: flags related to this event buffer
|
||||
* @dma: dma_addr_t
|
||||
* @dwc: pointer to DWC controller
|
||||
*/
|
||||
|
@ -376,6 +380,10 @@ struct dwc3_event_buffer {
|
|||
void *buf;
|
||||
unsigned length;
|
||||
unsigned int lpos;
|
||||
unsigned int count;
|
||||
unsigned int flags;
|
||||
|
||||
#define DWC3_EVENT_PENDING BIT(0)
|
||||
|
||||
dma_addr_t dma;
|
||||
|
||||
|
@ -487,12 +495,6 @@ enum dwc3_link_state {
|
|||
DWC3_LINK_STATE_MASK = 0x0f,
|
||||
};
|
||||
|
||||
enum dwc3_device_state {
|
||||
DWC3_DEFAULT_STATE,
|
||||
DWC3_ADDRESS_STATE,
|
||||
DWC3_CONFIGURED_STATE,
|
||||
};
|
||||
|
||||
/* TRB Length, PCM and Status */
|
||||
#define DWC3_TRB_SIZE_MASK (0x00ffffff)
|
||||
#define DWC3_TRB_SIZE_LENGTH(n) ((n) & DWC3_TRB_SIZE_MASK)
|
||||
|
@ -574,6 +576,14 @@ struct dwc3_hwparams {
|
|||
/* HWPARAMS1 */
|
||||
#define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15)
|
||||
|
||||
/* HWPARAMS3 */
|
||||
#define DWC3_NUM_IN_EPS_MASK (0x1f << 18)
|
||||
#define DWC3_NUM_EPS_MASK (0x3f << 12)
|
||||
#define DWC3_NUM_EPS(p) (((p)->hwparams3 & \
|
||||
(DWC3_NUM_EPS_MASK)) >> 12)
|
||||
#define DWC3_NUM_IN_EPS(p) (((p)->hwparams3 & \
|
||||
(DWC3_NUM_IN_EPS_MASK)) >> 18)
|
||||
|
||||
/* HWPARAMS7 */
|
||||
#define DWC3_RAM1_DEPTH(n) ((n) & 0xffff)
|
||||
|
||||
|
@ -618,7 +628,6 @@ struct dwc3_scratchpad_array {
|
|||
* @gadget_driver: pointer to the gadget driver
|
||||
* @regs: base address for our registers
|
||||
* @regs_size: address space size
|
||||
* @irq: IRQ number
|
||||
* @num_event_buffers: calculated number of event buffers
|
||||
* @u1u2: only used on revisions <1.83a for workaround
|
||||
* @maximum_speed: maximum speed requested (mainly for testing purposes)
|
||||
|
@ -626,6 +635,8 @@ struct dwc3_scratchpad_array {
|
|||
* @mode: mode of operation
|
||||
* @usb2_phy: pointer to USB2 PHY
|
||||
* @usb3_phy: pointer to USB3 PHY
|
||||
* @dcfg: saved contents of DCFG register
|
||||
* @gctl: saved contents of GCTL register
|
||||
* @is_selfpowered: true when we are selfpowered
|
||||
* @three_stage_setup: set if we perform a three phase setup
|
||||
* @ep0_bounced: true when we used bounce buffer
|
||||
|
@ -639,6 +650,8 @@ struct dwc3_scratchpad_array {
|
|||
* @u2pel: parameter from Set SEL request.
|
||||
* @u1sel: parameter from Set SEL request.
|
||||
* @u1pel: parameter from Set SEL request.
|
||||
* @num_out_eps: number of out endpoints
|
||||
* @num_in_eps: number of in endpoints
|
||||
* @ep0_next_event: hold the next expected event
|
||||
* @ep0state: state of endpoint zero
|
||||
* @link_state: link state
|
||||
|
@ -656,8 +669,10 @@ struct dwc3 {
|
|||
dma_addr_t ep0_trb_addr;
|
||||
dma_addr_t ep0_bounce_addr;
|
||||
struct dwc3_request ep0_usb_req;
|
||||
|
||||
/* device lock */
|
||||
spinlock_t lock;
|
||||
|
||||
struct device *dev;
|
||||
|
||||
struct platform_device *xhci;
|
||||
|
@ -675,6 +690,10 @@ struct dwc3 {
|
|||
void __iomem *regs;
|
||||
size_t regs_size;
|
||||
|
||||
/* used for suspend/resume */
|
||||
u32 dcfg;
|
||||
u32 gctl;
|
||||
|
||||
u32 num_event_buffers;
|
||||
u32 u1u2;
|
||||
u32 maximum_speed;
|
||||
|
@ -694,6 +713,9 @@ struct dwc3 {
|
|||
#define DWC3_REVISION_202A 0x5533202a
|
||||
#define DWC3_REVISION_210A 0x5533210a
|
||||
#define DWC3_REVISION_220A 0x5533220a
|
||||
#define DWC3_REVISION_230A 0x5533230a
|
||||
#define DWC3_REVISION_240A 0x5533240a
|
||||
#define DWC3_REVISION_250A 0x5533250a
|
||||
|
||||
unsigned is_selfpowered:1;
|
||||
unsigned three_stage_setup:1;
|
||||
|
@ -704,11 +726,11 @@ struct dwc3 {
|
|||
unsigned delayed_status:1;
|
||||
unsigned needs_fifo_resize:1;
|
||||
unsigned resize_fifos:1;
|
||||
unsigned pullups_connected:1;
|
||||
|
||||
enum dwc3_ep0_next ep0_next_event;
|
||||
enum dwc3_ep0_state ep0state;
|
||||
enum dwc3_link_state link_state;
|
||||
enum dwc3_device_state dev_state;
|
||||
|
||||
u16 isoch_delay;
|
||||
u16 u2sel;
|
||||
|
@ -718,6 +740,9 @@ struct dwc3 {
|
|||
|
||||
u8 speed;
|
||||
|
||||
u8 num_out_eps;
|
||||
u8 num_in_eps;
|
||||
|
||||
void *mem;
|
||||
|
||||
struct dwc3_hwparams hwparams;
|
||||
|
@ -884,4 +909,31 @@ static inline void dwc3_gadget_exit(struct dwc3 *dwc)
|
|||
{ }
|
||||
#endif
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
static inline int dwc3_gadget_resume(struct dwc3 *dwc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */
|
||||
|
||||
#endif /* __DRIVERS_USB_DWC3_CORE_H */
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
.offset = DWC3_ ##nm - DWC3_GLOBALS_REGS_START, \
|
||||
}
|
||||
|
||||
static struct debugfs_reg32 dwc3_regs[] = {
|
||||
static const struct debugfs_reg32 dwc3_regs[] = {
|
||||
dump_register(GSBUSCFG0),
|
||||
dump_register(GSBUSCFG1),
|
||||
dump_register(GTXTHRCFG),
|
||||
|
@ -372,6 +372,7 @@ static struct debugfs_reg32 dwc3_regs[] = {
|
|||
|
||||
dump_register(OCFG),
|
||||
dump_register(OCTL),
|
||||
dump_register(OEVT),
|
||||
dump_register(OEVTEN),
|
||||
dump_register(OSTS),
|
||||
};
|
||||
|
@ -577,8 +578,14 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused)
|
|||
case DWC3_LINK_STATE_LPBK:
|
||||
seq_printf(s, "Loopback\n");
|
||||
break;
|
||||
case DWC3_LINK_STATE_RESET:
|
||||
seq_printf(s, "Reset\n");
|
||||
break;
|
||||
case DWC3_LINK_STATE_RESUME:
|
||||
seq_printf(s, "Resume\n");
|
||||
break;
|
||||
default:
|
||||
seq_printf(s, "UNKNOWN %d\n", reg);
|
||||
seq_printf(s, "UNKNOWN %d\n", state);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -661,28 +668,31 @@ int dwc3_debugfs_init(struct dwc3 *dwc)
|
|||
goto err1;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC3_GADGET)
|
||||
file = debugfs_create_file("mode", S_IRUGO | S_IWUSR, root,
|
||||
dwc, &dwc3_mode_fops);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)) {
|
||||
file = debugfs_create_file("mode", S_IRUGO | S_IWUSR, root,
|
||||
dwc, &dwc3_mode_fops);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
}
|
||||
|
||||
file = debugfs_create_file("testmode", S_IRUGO | S_IWUSR, root,
|
||||
dwc, &dwc3_testmode_fops);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) ||
|
||||
IS_ENABLED(CONFIG_USB_DWC3_GADGET)) {
|
||||
file = debugfs_create_file("testmode", S_IRUGO | S_IWUSR, root,
|
||||
dwc, &dwc3_testmode_fops);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
file = debugfs_create_file("link_state", S_IRUGO | S_IWUSR, root,
|
||||
dwc, &dwc3_link_state_fops);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
file = debugfs_create_file("link_state", S_IRUGO | S_IWUSR, root,
|
||||
dwc, &dwc3_link_state_fops);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -22,9 +22,9 @@
|
|||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/nop-usb-xceiv.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
struct dwc3_exynos {
|
||||
struct platform_device *dwc3;
|
||||
struct platform_device *usb2_phy;
|
||||
struct platform_device *usb3_phy;
|
||||
struct device *dev;
|
||||
|
@ -86,21 +86,30 @@ err1:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int dwc3_exynos_remove_child(struct device *dev, void *unused)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
||||
platform_device_unregister(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u64 dwc3_exynos_dma_mask = DMA_BIT_MASK(32);
|
||||
|
||||
static int dwc3_exynos_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct platform_device *dwc3;
|
||||
struct dwc3_exynos *exynos;
|
||||
struct clk *clk;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
|
||||
int ret = -ENOMEM;
|
||||
|
||||
exynos = devm_kzalloc(dev, sizeof(*exynos), GFP_KERNEL);
|
||||
if (!exynos) {
|
||||
dev_err(dev, "not enough memory\n");
|
||||
return -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -108,21 +117,15 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
|
|||
* Since shared usb code relies on it, set it here for now.
|
||||
* Once we move to full device tree support this will vanish off.
|
||||
*/
|
||||
if (!pdev->dev.dma_mask)
|
||||
pdev->dev.dma_mask = &dwc3_exynos_dma_mask;
|
||||
if (!dev->dma_mask)
|
||||
dev->dma_mask = &dwc3_exynos_dma_mask;
|
||||
|
||||
platform_set_drvdata(pdev, exynos);
|
||||
|
||||
ret = dwc3_exynos_register_phys(exynos);
|
||||
if (ret) {
|
||||
dev_err(dev, "couldn't register PHYs\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO);
|
||||
if (!dwc3) {
|
||||
dev_err(dev, "couldn't allocate dwc3 device\n");
|
||||
return -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
clk = devm_clk_get(dev, "usbdrd30");
|
||||
|
@ -132,37 +135,28 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
|
|||
goto err1;
|
||||
}
|
||||
|
||||
dma_set_coherent_mask(&dwc3->dev, dev->coherent_dma_mask);
|
||||
|
||||
dwc3->dev.parent = dev;
|
||||
dwc3->dev.dma_mask = dev->dma_mask;
|
||||
dwc3->dev.dma_parms = dev->dma_parms;
|
||||
exynos->dwc3 = dwc3;
|
||||
exynos->dev = dev;
|
||||
exynos->clk = clk;
|
||||
|
||||
clk_enable(exynos->clk);
|
||||
clk_prepare_enable(exynos->clk);
|
||||
|
||||
ret = platform_device_add_resources(dwc3, pdev->resource,
|
||||
pdev->num_resources);
|
||||
if (ret) {
|
||||
dev_err(dev, "couldn't add resources to dwc3 device\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
ret = platform_device_add(dwc3);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register dwc3 device\n");
|
||||
if (node) {
|
||||
ret = of_platform_populate(node, NULL, NULL, dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to add dwc3 core\n");
|
||||
goto err2;
|
||||
}
|
||||
} else {
|
||||
dev_err(dev, "no device node, failed to add dwc3 core\n");
|
||||
ret = -ENODEV;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
clk_disable(clk);
|
||||
clk_disable_unprepare(clk);
|
||||
err1:
|
||||
platform_device_put(dwc3);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -170,11 +164,11 @@ static int dwc3_exynos_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct dwc3_exynos *exynos = platform_get_drvdata(pdev);
|
||||
|
||||
platform_device_unregister(exynos->dwc3);
|
||||
platform_device_unregister(exynos->usb2_phy);
|
||||
platform_device_unregister(exynos->usb3_phy);
|
||||
device_for_each_child(&pdev->dev, NULL, dwc3_exynos_remove_child);
|
||||
|
||||
clk_disable(exynos->clk);
|
||||
clk_disable_unprepare(exynos->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -187,12 +181,46 @@ static const struct of_device_id exynos_dwc3_match[] = {
|
|||
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->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_exynos_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3_exynos *exynos = dev_get_drvdata(dev);
|
||||
|
||||
clk_enable(exynos->clk);
|
||||
|
||||
/* runtime set active to reflect active state. */
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dwc3_exynos_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dwc3_exynos_suspend, dwc3_exynos_resume)
|
||||
};
|
||||
|
||||
#define DEV_PM_OPS (&dwc3_exynos_dev_pm_ops)
|
||||
#else
|
||||
#define DEV_PM_OPS NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static struct platform_driver dwc3_exynos_driver = {
|
||||
.probe = dwc3_exynos_probe,
|
||||
.remove = dwc3_exynos_remove,
|
||||
.driver = {
|
||||
.name = "exynos-dwc3",
|
||||
.of_match_table = of_match_ptr(exynos_dwc3_match),
|
||||
.pm = DEV_PM_OPS,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -52,7 +52,6 @@
|
|||
#include <linux/of_platform.h>
|
||||
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/nop-usb-xceiv.h>
|
||||
|
||||
/*
|
||||
* All these registers belong to OMAP's Wrapper around the
|
||||
|
@ -117,20 +116,17 @@ struct dwc3_omap {
|
|||
/* device lock */
|
||||
spinlock_t lock;
|
||||
|
||||
struct platform_device *usb2_phy;
|
||||
struct platform_device *usb3_phy;
|
||||
struct device *dev;
|
||||
|
||||
int irq;
|
||||
void __iomem *base;
|
||||
|
||||
void *context;
|
||||
u32 resource_size;
|
||||
u32 utmi_otg_status;
|
||||
|
||||
u32 dma_status:1;
|
||||
};
|
||||
|
||||
struct dwc3_omap *_omap;
|
||||
static struct dwc3_omap *_omap;
|
||||
|
||||
static inline u32 dwc3_omap_readl(void __iomem *base, u32 offset)
|
||||
{
|
||||
|
@ -142,11 +138,14 @@ static inline void dwc3_omap_writel(void __iomem *base, u32 offset, u32 value)
|
|||
writel(value, base + offset);
|
||||
}
|
||||
|
||||
void dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status)
|
||||
int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status)
|
||||
{
|
||||
u32 val;
|
||||
struct dwc3_omap *omap = _omap;
|
||||
|
||||
if (!omap)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
switch (status) {
|
||||
case OMAP_DWC3_ID_GROUND:
|
||||
dev_dbg(omap->dev, "ID GND\n");
|
||||
|
@ -189,64 +188,10 @@ void dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status)
|
|||
dev_dbg(omap->dev, "ID float\n");
|
||||
}
|
||||
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc3_omap_mailbox);
|
||||
|
||||
static int dwc3_omap_register_phys(struct dwc3_omap *omap)
|
||||
{
|
||||
struct nop_usb_xceiv_platform_data pdata;
|
||||
struct platform_device *pdev;
|
||||
int ret;
|
||||
|
||||
memset(&pdata, 0x00, sizeof(pdata));
|
||||
|
||||
pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO);
|
||||
if (!pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
omap->usb2_phy = pdev;
|
||||
pdata.type = USB_PHY_TYPE_USB2;
|
||||
|
||||
ret = platform_device_add_data(omap->usb2_phy, &pdata, sizeof(pdata));
|
||||
if (ret)
|
||||
goto err1;
|
||||
|
||||
pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO);
|
||||
if (!pdev) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
omap->usb3_phy = pdev;
|
||||
pdata.type = USB_PHY_TYPE_USB3;
|
||||
|
||||
ret = platform_device_add_data(omap->usb3_phy, &pdata, sizeof(pdata));
|
||||
if (ret)
|
||||
goto err2;
|
||||
|
||||
ret = platform_device_add(omap->usb2_phy);
|
||||
if (ret)
|
||||
goto err2;
|
||||
|
||||
ret = platform_device_add(omap->usb3_phy);
|
||||
if (ret)
|
||||
goto err3;
|
||||
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
platform_device_del(omap->usb2_phy);
|
||||
|
||||
err2:
|
||||
platform_device_put(omap->usb3_phy);
|
||||
|
||||
err1:
|
||||
platform_device_put(omap->usb2_phy);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
|
||||
{
|
||||
struct dwc3_omap *omap = _omap;
|
||||
|
@ -307,121 +252,10 @@ static int dwc3_omap_remove_core(struct device *dev, void *c)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_omap_probe(struct platform_device *pdev)
|
||||
static void dwc3_omap_enable_irqs(struct dwc3_omap *omap)
|
||||
{
|
||||
struct dwc3_omap_data *pdata = pdev->dev.platform_data;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
|
||||
struct dwc3_omap *omap;
|
||||
struct resource *res;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
int size;
|
||||
int ret = -ENOMEM;
|
||||
int irq;
|
||||
|
||||
const u32 *utmi_mode;
|
||||
u32 reg;
|
||||
|
||||
void __iomem *base;
|
||||
void *context;
|
||||
|
||||
omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL);
|
||||
if (!omap) {
|
||||
dev_err(dev, "not enough memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, omap);
|
||||
|
||||
irq = platform_get_irq(pdev, 1);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "missing IRQ resource\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (!res) {
|
||||
dev_err(dev, "missing memory base resource\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
base = devm_ioremap_nocache(dev, res->start, resource_size(res));
|
||||
if (!base) {
|
||||
dev_err(dev, "ioremap failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = dwc3_omap_register_phys(omap);
|
||||
if (ret) {
|
||||
dev_err(dev, "couldn't register PHYs\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
context = devm_kzalloc(dev, resource_size(res), GFP_KERNEL);
|
||||
if (!context) {
|
||||
dev_err(dev, "couldn't allocate dwc3 context memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spin_lock_init(&omap->lock);
|
||||
|
||||
omap->resource_size = resource_size(res);
|
||||
omap->context = context;
|
||||
omap->dev = dev;
|
||||
omap->irq = irq;
|
||||
omap->base = base;
|
||||
|
||||
/*
|
||||
* REVISIT if we ever have two instances of the wrapper, we will be
|
||||
* in big trouble
|
||||
*/
|
||||
_omap = omap;
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "get_sync failed with err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
reg = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
|
||||
|
||||
utmi_mode = of_get_property(node, "utmi-mode", &size);
|
||||
if (utmi_mode && size == sizeof(*utmi_mode)) {
|
||||
reg |= *utmi_mode;
|
||||
} else {
|
||||
if (!pdata) {
|
||||
dev_dbg(dev, "missing platform data\n");
|
||||
} else {
|
||||
switch (pdata->utmi_mode) {
|
||||
case DWC3_OMAP_UTMI_MODE_SW:
|
||||
reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
|
||||
break;
|
||||
case DWC3_OMAP_UTMI_MODE_HW:
|
||||
reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(dev, "UNKNOWN utmi mode %d\n",
|
||||
pdata->utmi_mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, reg);
|
||||
|
||||
/* check the DMA Status */
|
||||
reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
|
||||
omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE);
|
||||
|
||||
ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, 0,
|
||||
"dwc3-omap", omap);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request IRQ #%d --> %d\n",
|
||||
omap->irq, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* enable all IRQs */
|
||||
reg = USBOTGSS_IRQO_COREIRQ_ST;
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, reg);
|
||||
|
@ -437,14 +271,120 @@ static int dwc3_omap_probe(struct platform_device *pdev)
|
|||
USBOTGSS_IRQ1_IDPULLUP_FALL);
|
||||
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg);
|
||||
}
|
||||
|
||||
if (node) {
|
||||
ret = of_platform_populate(node, NULL, NULL, dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to add create dwc3 core\n");
|
||||
return ret;
|
||||
}
|
||||
static void dwc3_omap_disable_irqs(struct dwc3_omap *omap)
|
||||
{
|
||||
/* disable all IRQs */
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, 0x00);
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, 0x00);
|
||||
}
|
||||
|
||||
static u64 dwc3_omap_dma_mask = DMA_BIT_MASK(32);
|
||||
|
||||
static int dwc3_omap_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
|
||||
struct dwc3_omap *omap;
|
||||
struct resource *res;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
int ret = -ENOMEM;
|
||||
int irq;
|
||||
|
||||
int utmi_mode = 0;
|
||||
|
||||
u32 reg;
|
||||
|
||||
void __iomem *base;
|
||||
|
||||
if (!node) {
|
||||
dev_err(dev, "device node not found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL);
|
||||
if (!omap) {
|
||||
dev_err(dev, "not enough memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, omap);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "missing IRQ resource\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "missing memory base resource\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
base = devm_ioremap_nocache(dev, res->start, resource_size(res));
|
||||
if (!base) {
|
||||
dev_err(dev, "ioremap failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spin_lock_init(&omap->lock);
|
||||
|
||||
omap->dev = dev;
|
||||
omap->irq = irq;
|
||||
omap->base = base;
|
||||
dev->dma_mask = &dwc3_omap_dma_mask;
|
||||
|
||||
/*
|
||||
* REVISIT if we ever have two instances of the wrapper, we will be
|
||||
* in big trouble
|
||||
*/
|
||||
_omap = omap;
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "get_sync failed with err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
reg = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
|
||||
|
||||
of_property_read_u32(node, "utmi-mode", &utmi_mode);
|
||||
|
||||
switch (utmi_mode) {
|
||||
case DWC3_OMAP_UTMI_MODE_SW:
|
||||
reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
|
||||
break;
|
||||
case DWC3_OMAP_UTMI_MODE_HW:
|
||||
reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(dev, "UNKNOWN utmi mode %d\n", utmi_mode);
|
||||
}
|
||||
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, reg);
|
||||
|
||||
/* check the DMA Status */
|
||||
reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
|
||||
omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE);
|
||||
|
||||
ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, 0,
|
||||
"dwc3-omap", omap);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request IRQ #%d --> %d\n",
|
||||
omap->irq, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dwc3_omap_enable_irqs(omap);
|
||||
|
||||
ret = of_platform_populate(node, NULL, NULL, dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to create dwc3 core\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -454,8 +394,7 @@ static int dwc3_omap_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct dwc3_omap *omap = platform_get_drvdata(pdev);
|
||||
|
||||
platform_device_unregister(omap->usb2_phy);
|
||||
platform_device_unregister(omap->usb3_phy);
|
||||
dwc3_omap_disable_irqs(omap);
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
device_for_each_child(&pdev->dev, NULL, dwc3_omap_remove_core);
|
||||
|
@ -465,18 +404,72 @@ static int dwc3_omap_remove(struct platform_device *pdev)
|
|||
|
||||
static const struct of_device_id of_dwc3_match[] = {
|
||||
{
|
||||
"ti,dwc3",
|
||||
.compatible = "ti,dwc3"
|
||||
},
|
||||
{ },
|
||||
};
|
||||
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_readl(omap->base,
|
||||
USBOTGSS_UTMI_OTG_STATUS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_omap_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3_omap *omap = dev_get_drvdata(dev);
|
||||
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS,
|
||||
omap->utmi_otg_status);
|
||||
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
};
|
||||
|
||||
#define DEV_PM_OPS (&dwc3_omap_dev_pm_ops)
|
||||
#else
|
||||
#define DEV_PM_OPS NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static struct platform_driver dwc3_omap_driver = {
|
||||
.probe = dwc3_omap_probe,
|
||||
.remove = dwc3_omap_remove,
|
||||
.driver = {
|
||||
.name = "omap-dwc3",
|
||||
.of_match_table = of_dwc3_match,
|
||||
.pm = DEV_PM_OPS,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -212,11 +212,49 @@ static DEFINE_PCI_DEVICE_TABLE(dwc3_pci_id_table) = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int dwc3_pci_suspend(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pci = to_pci_dev(dev);
|
||||
|
||||
pci_disable_device(pci);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_pci_resume(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pci = to_pci_dev(dev);
|
||||
int ret;
|
||||
|
||||
ret = pci_enable_device(pci);
|
||||
if (ret) {
|
||||
dev_err(dev, "can't re-enable device --> %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pci_set_master(pci);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dwc3_pci_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dwc3_pci_suspend, dwc3_pci_resume)
|
||||
};
|
||||
|
||||
#define DEV_PM_OPS (&dwc3_pci_dev_pm_ops)
|
||||
#else
|
||||
#define DEV_PM_OPS NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static struct pci_driver dwc3_pci_driver = {
|
||||
.name = "dwc3-pci",
|
||||
.id_table = dwc3_pci_id_table,
|
||||
.probe = dwc3_pci_probe,
|
||||
.remove = dwc3_pci_remove,
|
||||
.driver = {
|
||||
.pm = DEV_PM_OPS,
|
||||
},
|
||||
};
|
||||
|
||||
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
|
||||
|
|
|
@ -394,10 +394,13 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
|
|||
u32 wIndex;
|
||||
u32 reg;
|
||||
int ret;
|
||||
enum usb_device_state state;
|
||||
|
||||
wValue = le16_to_cpu(ctrl->wValue);
|
||||
wIndex = le16_to_cpu(ctrl->wIndex);
|
||||
recip = ctrl->bRequestType & USB_RECIP_MASK;
|
||||
state = dwc->gadget.state;
|
||||
|
||||
switch (recip) {
|
||||
case USB_RECIP_DEVICE:
|
||||
|
||||
|
@ -409,7 +412,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
|
|||
* default control pipe
|
||||
*/
|
||||
case USB_DEVICE_U1_ENABLE:
|
||||
if (dwc->dev_state != DWC3_CONFIGURED_STATE)
|
||||
if (state != USB_STATE_CONFIGURED)
|
||||
return -EINVAL;
|
||||
if (dwc->speed != DWC3_DSTS_SUPERSPEED)
|
||||
return -EINVAL;
|
||||
|
@ -423,7 +426,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
|
|||
break;
|
||||
|
||||
case USB_DEVICE_U2_ENABLE:
|
||||
if (dwc->dev_state != DWC3_CONFIGURED_STATE)
|
||||
if (state != USB_STATE_CONFIGURED)
|
||||
return -EINVAL;
|
||||
if (dwc->speed != DWC3_DSTS_SUPERSPEED)
|
||||
return -EINVAL;
|
||||
|
@ -493,6 +496,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
|
|||
|
||||
static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
enum usb_device_state state = dwc->gadget.state;
|
||||
u32 addr;
|
||||
u32 reg;
|
||||
|
||||
|
@ -502,7 +506,7 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (dwc->dev_state == DWC3_CONFIGURED_STATE) {
|
||||
if (state == USB_STATE_CONFIGURED) {
|
||||
dev_dbg(dwc->dev, "trying to set address when configured\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -513,9 +517,9 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
|||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
|
||||
if (addr)
|
||||
dwc->dev_state = DWC3_ADDRESS_STATE;
|
||||
usb_gadget_set_state(&dwc->gadget, USB_STATE_ADDRESS);
|
||||
else
|
||||
dwc->dev_state = DWC3_DEFAULT_STATE;
|
||||
usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -532,6 +536,7 @@ static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
|||
|
||||
static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
enum usb_device_state state = dwc->gadget.state;
|
||||
u32 cfg;
|
||||
int ret;
|
||||
u32 reg;
|
||||
|
@ -539,16 +544,18 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
|||
dwc->start_config_issued = false;
|
||||
cfg = le16_to_cpu(ctrl->wValue);
|
||||
|
||||
switch (dwc->dev_state) {
|
||||
case DWC3_DEFAULT_STATE:
|
||||
switch (state) {
|
||||
case USB_STATE_DEFAULT:
|
||||
return -EINVAL;
|
||||
break;
|
||||
|
||||
case DWC3_ADDRESS_STATE:
|
||||
case USB_STATE_ADDRESS:
|
||||
ret = dwc3_ep0_delegate_req(dwc, ctrl);
|
||||
/* if the cfg matches and the cfg is non zero */
|
||||
if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) {
|
||||
dwc->dev_state = DWC3_CONFIGURED_STATE;
|
||||
usb_gadget_set_state(&dwc->gadget,
|
||||
USB_STATE_CONFIGURED);
|
||||
|
||||
/*
|
||||
* Enable transition to U1/U2 state when
|
||||
* nothing is pending from application.
|
||||
|
@ -562,10 +569,11 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
|||
}
|
||||
break;
|
||||
|
||||
case DWC3_CONFIGURED_STATE:
|
||||
case USB_STATE_CONFIGURED:
|
||||
ret = dwc3_ep0_delegate_req(dwc, ctrl);
|
||||
if (!cfg)
|
||||
dwc->dev_state = DWC3_ADDRESS_STATE;
|
||||
usb_gadget_set_state(&dwc->gadget,
|
||||
USB_STATE_ADDRESS);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
|
@ -620,10 +628,11 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req)
|
|||
static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct dwc3_ep *dep;
|
||||
enum usb_device_state state = dwc->gadget.state;
|
||||
u16 wLength;
|
||||
u16 wValue;
|
||||
|
||||
if (dwc->dev_state == DWC3_DEFAULT_STATE)
|
||||
if (state == USB_STATE_DEFAULT)
|
||||
return -EINVAL;
|
||||
|
||||
wValue = le16_to_cpu(ctrl->wValue);
|
||||
|
|
|
@ -1425,8 +1425,10 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
|
|||
if (dwc->revision >= DWC3_REVISION_194A)
|
||||
reg &= ~DWC3_DCTL_KEEP_CONNECT;
|
||||
reg |= DWC3_DCTL_RUN_STOP;
|
||||
dwc->pullups_connected = true;
|
||||
} else {
|
||||
reg &= ~DWC3_DCTL_RUN_STOP;
|
||||
dwc->pullups_connected = false;
|
||||
}
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
|
@ -1469,6 +1471,33 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
/* Enable all but Start and End of Frame IRQs */
|
||||
reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN |
|
||||
DWC3_DEVTEN_EVNTOVERFLOWEN |
|
||||
DWC3_DEVTEN_CMDCMPLTEN |
|
||||
DWC3_DEVTEN_ERRTICERREN |
|
||||
DWC3_DEVTEN_WKUPEVTEN |
|
||||
DWC3_DEVTEN_ULSTCNGEN |
|
||||
DWC3_DEVTEN_CONNECTDONEEN |
|
||||
DWC3_DEVTEN_USBRSTEN |
|
||||
DWC3_DEVTEN_DISCONNEVTEN);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
|
||||
}
|
||||
|
||||
static void dwc3_gadget_disable_irq(struct dwc3 *dwc)
|
||||
{
|
||||
/* mask all interrupts */
|
||||
dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00);
|
||||
}
|
||||
|
||||
static irqreturn_t dwc3_interrupt(int irq, void *_dwc);
|
||||
static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc);
|
||||
|
||||
static int dwc3_gadget_start(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
|
@ -1476,6 +1505,7 @@ static int dwc3_gadget_start(struct usb_gadget *g,
|
|||
struct dwc3_ep *dep;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
int irq;
|
||||
u32 reg;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
@ -1489,7 +1519,6 @@ static int dwc3_gadget_start(struct usb_gadget *g,
|
|||
}
|
||||
|
||||
dwc->gadget_driver = driver;
|
||||
dwc->gadget.dev.driver = &driver->driver;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
||||
reg &= ~(DWC3_DCFG_SPEED_MASK);
|
||||
|
@ -1536,6 +1565,17 @@ static int dwc3_gadget_start(struct usb_gadget *g,
|
|||
dwc->ep0state = EP0_SETUP_PHASE;
|
||||
dwc3_ep0_out_start(dwc);
|
||||
|
||||
irq = platform_get_irq(to_platform_device(dwc->dev), 0);
|
||||
ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
|
||||
IRQF_SHARED | IRQF_ONESHOT, "dwc3", dwc);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
|
||||
irq, ret);
|
||||
goto err1;
|
||||
}
|
||||
|
||||
dwc3_gadget_enable_irq(dwc);
|
||||
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return 0;
|
||||
|
@ -1554,14 +1594,18 @@ static int dwc3_gadget_stop(struct usb_gadget *g,
|
|||
{
|
||||
struct dwc3 *dwc = gadget_to_dwc(g);
|
||||
unsigned long flags;
|
||||
int irq;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
dwc3_gadget_disable_irq(dwc);
|
||||
irq = platform_get_irq(to_platform_device(dwc->dev), 0);
|
||||
free_irq(irq, dwc);
|
||||
|
||||
__dwc3_gadget_ep_disable(dwc->eps[0]);
|
||||
__dwc3_gadget_ep_disable(dwc->eps[1]);
|
||||
|
||||
dwc->gadget_driver = NULL;
|
||||
dwc->gadget.dev.driver = NULL;
|
||||
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
|
@ -1579,14 +1623,15 @@ static const struct usb_gadget_ops dwc3_gadget_ops = {
|
|||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static int dwc3_gadget_init_endpoints(struct dwc3 *dwc)
|
||||
static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
|
||||
u8 num, u32 direction)
|
||||
{
|
||||
struct dwc3_ep *dep;
|
||||
u8 epnum;
|
||||
u8 i;
|
||||
|
||||
INIT_LIST_HEAD(&dwc->gadget.ep_list);
|
||||
for (i = 0; i < num; i++) {
|
||||
u8 epnum = (i << 1) | (!!direction);
|
||||
|
||||
for (epnum = 0; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
|
||||
dep = kzalloc(sizeof(*dep), GFP_KERNEL);
|
||||
if (!dep) {
|
||||
dev_err(dwc->dev, "can't allocate endpoint %d\n",
|
||||
|
@ -1600,6 +1645,7 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc)
|
|||
|
||||
snprintf(dep->name, sizeof(dep->name), "ep%d%s", epnum >> 1,
|
||||
(epnum & 1) ? "in" : "out");
|
||||
|
||||
dep->endpoint.name = dep->name;
|
||||
dep->direction = (epnum & 1);
|
||||
|
||||
|
@ -1630,6 +1676,27 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_gadget_init_endpoints(struct dwc3 *dwc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
INIT_LIST_HEAD(&dwc->gadget.ep_list);
|
||||
|
||||
ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_out_eps, 0);
|
||||
if (ret < 0) {
|
||||
dev_vdbg(dwc->dev, "failed to allocate OUT endpoints\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_in_eps, 1);
|
||||
if (ret < 0) {
|
||||
dev_vdbg(dwc->dev, "failed to allocate IN endpoints\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
|
||||
{
|
||||
struct dwc3_ep *dep;
|
||||
|
@ -1637,6 +1704,9 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
|
|||
|
||||
for (epnum = 0; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
|
||||
dep = dwc->eps[epnum];
|
||||
if (!dep)
|
||||
continue;
|
||||
|
||||
dwc3_free_trb_pool(dep);
|
||||
|
||||
if (epnum != 0 && epnum != 1)
|
||||
|
@ -1646,12 +1716,8 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
|
|||
}
|
||||
}
|
||||
|
||||
static void dwc3_gadget_release(struct device *dev)
|
||||
{
|
||||
dev_dbg(dev, "%s\n", __func__);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
||||
struct dwc3_request *req, struct dwc3_trb *trb,
|
||||
const struct dwc3_event_depevt *event, int status)
|
||||
|
@ -1975,6 +2041,9 @@ static void dwc3_stop_active_transfers(struct dwc3 *dwc)
|
|||
struct dwc3_ep *dep;
|
||||
|
||||
dep = dwc->eps[epnum];
|
||||
if (!dep)
|
||||
continue;
|
||||
|
||||
if (!(dep->flags & DWC3_EP_ENABLED))
|
||||
continue;
|
||||
|
||||
|
@ -1992,6 +2061,8 @@ static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
|
|||
int ret;
|
||||
|
||||
dep = dwc->eps[epnum];
|
||||
if (!dep)
|
||||
continue;
|
||||
|
||||
if (!(dep->flags & DWC3_EP_STALL))
|
||||
continue;
|
||||
|
@ -2091,7 +2162,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
|
|||
}
|
||||
|
||||
/* after reset -> Default State */
|
||||
dwc->dev_state = DWC3_DEFAULT_STATE;
|
||||
usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT);
|
||||
|
||||
/* Recent versions support automatic phy suspend and don't need this */
|
||||
if (dwc->revision < DWC3_REVISION_194A) {
|
||||
|
@ -2277,6 +2348,34 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
|
|||
unsigned int evtinfo)
|
||||
{
|
||||
enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK;
|
||||
unsigned int pwropt;
|
||||
|
||||
/*
|
||||
* WORKAROUND: DWC3 < 2.50a have an issue when configured without
|
||||
* Hibernation mode enabled which would show up when device detects
|
||||
* host-initiated U3 exit.
|
||||
*
|
||||
* In that case, device will generate a Link State Change Interrupt
|
||||
* from U3 to RESUME which is only necessary if Hibernation is
|
||||
* configured in.
|
||||
*
|
||||
* There are no functional changes due to such spurious event and we
|
||||
* just need to ignore it.
|
||||
*
|
||||
* Refers to:
|
||||
*
|
||||
* STAR#9000570034 RTL: SS Resume event generated in non-Hibernation
|
||||
* operational mode
|
||||
*/
|
||||
pwropt = DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1);
|
||||
if ((dwc->revision < DWC3_REVISION_250A) &&
|
||||
(pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) {
|
||||
if ((dwc->link_state == DWC3_LINK_STATE_U3) &&
|
||||
(next == DWC3_LINK_STATE_RESUME)) {
|
||||
dev_vdbg(dwc->dev, "ignoring transition U3 -> Resume\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* WORKAROUND: DWC3 Revisions <1.83a have an issue which, depending
|
||||
|
@ -2387,40 +2486,73 @@ static void dwc3_process_event_entry(struct dwc3 *dwc,
|
|||
}
|
||||
}
|
||||
|
||||
static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc)
|
||||
{
|
||||
struct dwc3 *dwc = _dwc;
|
||||
unsigned long flags;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
for (i = 0; i < dwc->num_event_buffers; i++) {
|
||||
struct dwc3_event_buffer *evt;
|
||||
int left;
|
||||
|
||||
evt = dwc->ev_buffs[i];
|
||||
left = evt->count;
|
||||
|
||||
if (!(evt->flags & DWC3_EVENT_PENDING))
|
||||
continue;
|
||||
|
||||
while (left > 0) {
|
||||
union dwc3_event event;
|
||||
|
||||
event.raw = *(u32 *) (evt->buf + evt->lpos);
|
||||
|
||||
dwc3_process_event_entry(dwc, &event);
|
||||
|
||||
/*
|
||||
* FIXME we wrap around correctly to the next entry as
|
||||
* almost all entries are 4 bytes in size. There is one
|
||||
* entry which has 12 bytes which is a regular entry
|
||||
* followed by 8 bytes data. ATM I don't know how
|
||||
* things are organized if we get next to the a
|
||||
* boundary so I worry about that once we try to handle
|
||||
* that.
|
||||
*/
|
||||
evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE;
|
||||
left -= 4;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(i), 4);
|
||||
}
|
||||
|
||||
evt->count = 0;
|
||||
evt->flags &= ~DWC3_EVENT_PENDING;
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf)
|
||||
{
|
||||
struct dwc3_event_buffer *evt;
|
||||
int left;
|
||||
u32 count;
|
||||
|
||||
evt = dwc->ev_buffs[buf];
|
||||
|
||||
count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(buf));
|
||||
count &= DWC3_GEVNTCOUNT_MASK;
|
||||
if (!count)
|
||||
return IRQ_NONE;
|
||||
|
||||
evt = dwc->ev_buffs[buf];
|
||||
left = count;
|
||||
evt->count = count;
|
||||
evt->flags |= DWC3_EVENT_PENDING;
|
||||
|
||||
while (left > 0) {
|
||||
union dwc3_event event;
|
||||
|
||||
event.raw = *(u32 *) (evt->buf + evt->lpos);
|
||||
|
||||
dwc3_process_event_entry(dwc, &event);
|
||||
/*
|
||||
* XXX we wrap around correctly to the next entry as almost all
|
||||
* entries are 4 bytes in size. There is one entry which has 12
|
||||
* bytes which is a regular entry followed by 8 bytes data. ATM
|
||||
* I don't know how things are organized if were get next to the
|
||||
* a boundary so I worry about that once we try to handle that.
|
||||
*/
|
||||
evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE;
|
||||
left -= 4;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), 4);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static irqreturn_t dwc3_interrupt(int irq, void *_dwc)
|
||||
|
@ -2435,7 +2567,7 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc)
|
|||
irqreturn_t status;
|
||||
|
||||
status = dwc3_process_event_buf(dwc, i);
|
||||
if (status == IRQ_HANDLED)
|
||||
if (status == IRQ_WAKE_THREAD)
|
||||
ret = status;
|
||||
}
|
||||
|
||||
|
@ -2454,7 +2586,6 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
|||
{
|
||||
u32 reg;
|
||||
int ret;
|
||||
int irq;
|
||||
|
||||
dwc->ctrl_req = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
|
||||
&dwc->ctrl_req_addr, GFP_KERNEL);
|
||||
|
@ -2488,19 +2619,10 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
|||
goto err3;
|
||||
}
|
||||
|
||||
dev_set_name(&dwc->gadget.dev, "gadget");
|
||||
|
||||
dwc->gadget.ops = &dwc3_gadget_ops;
|
||||
dwc->gadget.max_speed = USB_SPEED_SUPER;
|
||||
dwc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
dwc->gadget.dev.parent = dwc->dev;
|
||||
dwc->gadget.sg_supported = true;
|
||||
|
||||
dma_set_coherent_mask(&dwc->gadget.dev, dwc->dev->coherent_dma_mask);
|
||||
|
||||
dwc->gadget.dev.dma_parms = dwc->dev->dma_parms;
|
||||
dwc->gadget.dev.dma_mask = dwc->dev->dma_mask;
|
||||
dwc->gadget.dev.release = dwc3_gadget_release;
|
||||
dwc->gadget.name = "dwc3-gadget";
|
||||
|
||||
/*
|
||||
|
@ -2512,60 +2634,24 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
|||
if (ret)
|
||||
goto err4;
|
||||
|
||||
irq = platform_get_irq(to_platform_device(dwc->dev), 0);
|
||||
|
||||
ret = request_irq(irq, dwc3_interrupt, IRQF_SHARED,
|
||||
"dwc3", dwc);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
|
||||
irq, ret);
|
||||
goto err5;
|
||||
}
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
||||
reg |= DWC3_DCFG_LPM_CAP;
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
|
||||
/* Enable all but Start and End of Frame IRQs */
|
||||
reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN |
|
||||
DWC3_DEVTEN_EVNTOVERFLOWEN |
|
||||
DWC3_DEVTEN_CMDCMPLTEN |
|
||||
DWC3_DEVTEN_ERRTICERREN |
|
||||
DWC3_DEVTEN_WKUPEVTEN |
|
||||
DWC3_DEVTEN_ULSTCNGEN |
|
||||
DWC3_DEVTEN_CONNECTDONEEN |
|
||||
DWC3_DEVTEN_USBRSTEN |
|
||||
DWC3_DEVTEN_DISCONNEVTEN);
|
||||
dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
|
||||
|
||||
/* automatic phy suspend only on recent versions */
|
||||
/* Enable USB2 LPM and automatic phy suspend only on recent versions */
|
||||
if (dwc->revision >= DWC3_REVISION_194A) {
|
||||
dwc3_gadget_usb2_phy_suspend(dwc, false);
|
||||
dwc3_gadget_usb3_phy_suspend(dwc, false);
|
||||
}
|
||||
|
||||
ret = device_register(&dwc->gadget.dev);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to register gadget device\n");
|
||||
put_device(&dwc->gadget.dev);
|
||||
goto err6;
|
||||
}
|
||||
|
||||
ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to register udc\n");
|
||||
goto err7;
|
||||
goto err5;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err7:
|
||||
device_unregister(&dwc->gadget.dev);
|
||||
|
||||
err6:
|
||||
dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00);
|
||||
free_irq(irq, dwc);
|
||||
|
||||
err5:
|
||||
dwc3_gadget_free_endpoints(dwc);
|
||||
|
||||
|
@ -2588,15 +2674,11 @@ err0:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
void dwc3_gadget_exit(struct dwc3 *dwc)
|
||||
{
|
||||
int irq;
|
||||
|
||||
usb_del_gadget_udc(&dwc->gadget);
|
||||
irq = platform_get_irq(to_platform_device(dwc->dev), 0);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00);
|
||||
free_irq(irq, dwc);
|
||||
|
||||
dwc3_gadget_free_endpoints(dwc);
|
||||
|
||||
|
@ -2610,6 +2692,63 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
|
|||
|
||||
dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
|
||||
dwc->ctrl_req, dwc->ctrl_req_addr);
|
||||
|
||||
device_unregister(&dwc->gadget.dev);
|
||||
}
|
||||
|
||||
int dwc3_gadget_prepare(struct dwc3 *dwc)
|
||||
{
|
||||
if (dwc->pullups_connected)
|
||||
dwc3_gadget_disable_irq(dwc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dwc3_gadget_complete(struct dwc3 *dwc)
|
||||
{
|
||||
if (dwc->pullups_connected) {
|
||||
dwc3_gadget_enable_irq(dwc);
|
||||
dwc3_gadget_run_stop(dwc, true);
|
||||
}
|
||||
}
|
||||
|
||||
int dwc3_gadget_suspend(struct dwc3 *dwc)
|
||||
{
|
||||
__dwc3_gadget_ep_disable(dwc->eps[0]);
|
||||
__dwc3_gadget_ep_disable(dwc->eps[1]);
|
||||
|
||||
dwc->dcfg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dwc3_gadget_resume(struct dwc3 *dwc)
|
||||
{
|
||||
struct dwc3_ep *dep;
|
||||
int ret;
|
||||
|
||||
/* Start with SuperSpeed Default */
|
||||
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
|
||||
|
||||
dep = dwc->eps[0];
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
|
||||
if (ret)
|
||||
goto err0;
|
||||
|
||||
dep = dwc->eps[1];
|
||||
ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
|
||||
if (ret)
|
||||
goto err1;
|
||||
|
||||
/* begin to receive SETUP packets */
|
||||
dwc->ep0state = EP0_SETUP_PHASE;
|
||||
dwc3_ep0_out_start(dwc);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, dwc->dcfg);
|
||||
|
||||
return 0;
|
||||
|
||||
err1:
|
||||
__dwc3_gadget_ep_disable(dwc->eps[0]);
|
||||
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -144,6 +144,7 @@ config USB_AT91
|
|||
config USB_LPC32XX
|
||||
tristate "LPC32XX USB Peripheral Controller"
|
||||
depends on ARCH_LPC32XX
|
||||
depends on USB_PHY
|
||||
select USB_ISP1301
|
||||
select USB_OTG_UTILS
|
||||
help
|
||||
|
@ -195,8 +196,8 @@ config USB_FUSB300
|
|||
config USB_OMAP
|
||||
tristate "OMAP USB Device Controller"
|
||||
depends on ARCH_OMAP1
|
||||
depends on USB_PHY
|
||||
select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_H4_OTG
|
||||
select USB_OTG_UTILS if ARCH_OMAP
|
||||
help
|
||||
Many Texas Instruments OMAP processors have flexible full
|
||||
speed USB device controllers, with support for up to 30
|
||||
|
@ -211,7 +212,6 @@ config USB_OMAP
|
|||
config USB_PXA25X
|
||||
tristate "PXA 25x or IXP 4xx"
|
||||
depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX
|
||||
select USB_OTG_UTILS
|
||||
help
|
||||
Intel's PXA 25x series XScale ARM-5TE processors include
|
||||
an integrated full speed USB 1.1 device controller. The
|
||||
|
@ -259,8 +259,6 @@ config USB_RENESAS_USBHS_UDC
|
|||
|
||||
config USB_PXA27X
|
||||
tristate "PXA 27x"
|
||||
depends on ARCH_PXA && (PXA27x || PXA3xx)
|
||||
select USB_OTG_UTILS
|
||||
help
|
||||
Intel's PXA 27x series XScale ARM v5TE processors include
|
||||
an integrated full speed USB 1.1 device controller.
|
||||
|
@ -329,9 +327,6 @@ config USB_MV_UDC
|
|||
|
||||
config USB_MV_U3D
|
||||
tristate "MARVELL PXA2128 USB 3.0 controller"
|
||||
depends on CPU_MMP3
|
||||
select USB_GADGET_DUALSPEED
|
||||
select USB_GADGET_SUPERSPEED
|
||||
help
|
||||
MARVELL PXA2128 Processor series include a super speed USB3.0 device
|
||||
controller, which support super speed USB peripheral.
|
||||
|
@ -501,6 +496,7 @@ endmenu
|
|||
# composite based drivers
|
||||
config USB_LIBCOMPOSITE
|
||||
tristate
|
||||
select CONFIGFS_FS
|
||||
depends on USB_GADGET
|
||||
|
||||
config USB_F_ACM
|
||||
|
@ -512,6 +508,12 @@ config USB_F_SS_LB
|
|||
config USB_U_SERIAL
|
||||
tristate
|
||||
|
||||
config USB_F_SERIAL
|
||||
tristate
|
||||
|
||||
config USB_F_OBEX
|
||||
tristate
|
||||
|
||||
choice
|
||||
tristate "USB Gadget Drivers"
|
||||
default USB_ETH
|
||||
|
@ -766,6 +768,8 @@ config USB_G_SERIAL
|
|||
depends on TTY
|
||||
select USB_U_SERIAL
|
||||
select USB_F_ACM
|
||||
select USB_F_SERIAL
|
||||
select USB_F_OBEX
|
||||
select USB_LIBCOMPOSITE
|
||||
help
|
||||
The Serial Gadget talks to the Linux-USB generic serial driver.
|
||||
|
@ -839,6 +843,7 @@ config USB_G_NOKIA
|
|||
depends on PHONET
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_U_SERIAL
|
||||
select USB_F_ACM
|
||||
help
|
||||
The Nokia composite gadget provides support for acm, obex
|
||||
and phonet in only one composite gadget driver.
|
||||
|
@ -957,6 +962,7 @@ config USB_G_WEBCAM
|
|||
tristate "USB Webcam Gadget"
|
||||
depends on VIDEO_DEV
|
||||
select USB_LIBCOMPOSITE
|
||||
select VIDEOBUF2_VMALLOC
|
||||
help
|
||||
The Webcam Gadget acts as a composite USB Audio and Video Class
|
||||
device. It provides a userspace API to process UVC control requests
|
||||
|
|
|
@ -6,7 +6,7 @@ ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG
|
|||
obj-$(CONFIG_USB_GADGET) += udc-core.o
|
||||
obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o
|
||||
libcomposite-y := usbstring.o config.o epautoconf.o
|
||||
libcomposite-y += composite.o functions.o
|
||||
libcomposite-y += composite.o functions.o configfs.o
|
||||
obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
|
||||
obj-$(CONFIG_USB_NET2272) += net2272.o
|
||||
obj-$(CONFIG_USB_NET2280) += net2280.o
|
||||
|
@ -36,10 +36,15 @@ obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o
|
|||
obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o
|
||||
|
||||
# USB Functions
|
||||
obj-$(CONFIG_USB_F_ACM) += f_acm.o
|
||||
f_ss_lb-y := f_loopback.o f_sourcesink.o
|
||||
obj-$(CONFIG_USB_F_SS_LB) += f_ss_lb.o
|
||||
usb_f_acm-y := f_acm.o
|
||||
obj-$(CONFIG_USB_F_ACM) += usb_f_acm.o
|
||||
usb_f_ss_lb-y := f_loopback.o f_sourcesink.o
|
||||
obj-$(CONFIG_USB_F_SS_LB) += usb_f_ss_lb.o
|
||||
obj-$(CONFIG_USB_U_SERIAL) += u_serial.o
|
||||
usb_f_serial-y := f_serial.o
|
||||
obj-$(CONFIG_USB_F_SERIAL) += usb_f_serial.o
|
||||
usb_f_obex-y := f_obex.o
|
||||
obj-$(CONFIG_USB_F_OBEX) += usb_f_obex.o
|
||||
|
||||
#
|
||||
# USB gadget drivers
|
||||
|
|
|
@ -109,7 +109,6 @@ FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
|
|||
static struct fsg_common fsg_common;
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
static unsigned char tty_line;
|
||||
static struct usb_function *f_acm;
|
||||
static struct usb_function_instance *f_acm_inst;
|
||||
/*
|
||||
|
@ -117,7 +116,6 @@ static struct usb_function_instance *f_acm_inst;
|
|||
*/
|
||||
static int __init acm_ms_do_config(struct usb_configuration *c)
|
||||
{
|
||||
struct f_serial_opts *opts;
|
||||
int status;
|
||||
|
||||
if (gadget_is_otg(c->cdev->gadget)) {
|
||||
|
@ -129,9 +127,6 @@ static int __init acm_ms_do_config(struct usb_configuration *c)
|
|||
if (IS_ERR(f_acm_inst))
|
||||
return PTR_ERR(f_acm_inst);
|
||||
|
||||
opts = container_of(f_acm_inst, struct f_serial_opts, func_inst);
|
||||
opts->port_num = tty_line;
|
||||
|
||||
f_acm = usb_get_function(f_acm_inst);
|
||||
if (IS_ERR(f_acm)) {
|
||||
status = PTR_ERR(f_acm);
|
||||
|
@ -171,16 +166,11 @@ static int __init acm_ms_bind(struct usb_composite_dev *cdev)
|
|||
int status;
|
||||
void *retp;
|
||||
|
||||
/* set up serial link layer */
|
||||
status = gserial_alloc_line(&tty_line);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
/* set up mass storage function */
|
||||
retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data);
|
||||
if (IS_ERR(retp)) {
|
||||
status = PTR_ERR(retp);
|
||||
goto fail0;
|
||||
return PTR_ERR(retp);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -207,8 +197,6 @@ static int __init acm_ms_bind(struct usb_composite_dev *cdev)
|
|||
/* error recovery */
|
||||
fail1:
|
||||
fsg_common_put(&fsg_common);
|
||||
fail0:
|
||||
gserial_free_line(tty_line);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -216,7 +204,6 @@ static int __exit acm_ms_unbind(struct usb_composite_dev *cdev)
|
|||
{
|
||||
usb_put_function(f_acm);
|
||||
usb_put_function_instance(f_acm_inst);
|
||||
gserial_free_line(tty_line);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1922,7 +1922,6 @@ static int amd5536_udc_start(struct usb_gadget *g,
|
|||
|
||||
driver->driver.bus = NULL;
|
||||
dev->driver = driver;
|
||||
dev->gadget.dev.driver = &driver->driver;
|
||||
|
||||
/* Some gadget drivers use both ep0 directions.
|
||||
* NOTE: to gadget driver, ep0 is just one endpoint...
|
||||
|
@ -1973,7 +1972,6 @@ static int amd5536_udc_stop(struct usb_gadget *g,
|
|||
shutdown(dev, driver);
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
dev->gadget.dev.driver = NULL;
|
||||
dev->driver = NULL;
|
||||
|
||||
/* set SD */
|
||||
|
@ -3080,7 +3078,6 @@ static void udc_pci_remove(struct pci_dev *pdev)
|
|||
if (dev->active)
|
||||
pci_disable_device(pdev);
|
||||
|
||||
device_unregister(&dev->gadget.dev);
|
||||
pci_set_drvdata(pdev, NULL);
|
||||
|
||||
udc_remove(dev);
|
||||
|
@ -3245,8 +3242,6 @@ static int udc_pci_probe(
|
|||
dev->phys_addr = resource;
|
||||
dev->irq = pdev->irq;
|
||||
dev->pdev = pdev;
|
||||
dev->gadget.dev.parent = &pdev->dev;
|
||||
dev->gadget.dev.dma_mask = pdev->dev.dma_mask;
|
||||
|
||||
/* general probing */
|
||||
if (udc_probe(dev) == 0)
|
||||
|
@ -3273,7 +3268,6 @@ static int udc_probe(struct udc *dev)
|
|||
dev->gadget.ops = &udc_ops;
|
||||
|
||||
dev_set_name(&dev->gadget.dev, "gadget");
|
||||
dev->gadget.dev.release = gadget_release;
|
||||
dev->gadget.name = name;
|
||||
dev->gadget.max_speed = USB_SPEED_HIGH;
|
||||
|
||||
|
@ -3297,17 +3291,11 @@ static int udc_probe(struct udc *dev)
|
|||
"driver version: %s(for Geode5536 B1)\n", tmp);
|
||||
udc = dev;
|
||||
|
||||
retval = usb_add_gadget_udc(&udc->pdev->dev, &dev->gadget);
|
||||
retval = usb_add_gadget_udc_release(&udc->pdev->dev, &dev->gadget,
|
||||
gadget_release);
|
||||
if (retval)
|
||||
goto finished;
|
||||
|
||||
retval = device_register(&dev->gadget.dev);
|
||||
if (retval) {
|
||||
usb_del_gadget_udc(&dev->gadget);
|
||||
put_device(&dev->gadget.dev);
|
||||
goto finished;
|
||||
}
|
||||
|
||||
/* timer init */
|
||||
init_timer(&udc_timer);
|
||||
udc_timer.function = udc_timer_function;
|
||||
|
|
|
@ -472,7 +472,6 @@ struct udc_request {
|
|||
|
||||
/* flags */
|
||||
unsigned dma_going : 1,
|
||||
dma_mapping : 1,
|
||||
dma_done : 1;
|
||||
/* phys. address */
|
||||
dma_addr_t td_phys;
|
||||
|
|
|
@ -1631,7 +1631,6 @@ static int at91_start(struct usb_gadget *gadget,
|
|||
|
||||
udc = container_of(gadget, struct at91_udc, gadget);
|
||||
udc->driver = driver;
|
||||
udc->gadget.dev.driver = &driver->driver;
|
||||
udc->gadget.dev.of_node = udc->pdev->dev.of_node;
|
||||
udc->enabled = 1;
|
||||
udc->selfpowered = 1;
|
||||
|
@ -1652,7 +1651,6 @@ static int at91_stop(struct usb_gadget *gadget,
|
|||
at91_udp_write(udc, AT91_UDP_IDR, ~0);
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
udc->gadget.dev.driver = NULL;
|
||||
udc->driver = NULL;
|
||||
|
||||
DBG("unbound from %s\n", driver->driver.name);
|
||||
|
@ -1780,13 +1778,7 @@ static int at91udc_probe(struct platform_device *pdev)
|
|||
DBG("clocks missing\n");
|
||||
retval = -ENODEV;
|
||||
/* NOTE: we "know" here that refcounts on these are NOPs */
|
||||
goto fail0b;
|
||||
}
|
||||
|
||||
retval = device_register(&udc->gadget.dev);
|
||||
if (retval < 0) {
|
||||
put_device(&udc->gadget.dev);
|
||||
goto fail0b;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
/* don't do anything until we have both gadget driver and VBUS */
|
||||
|
@ -1857,8 +1849,6 @@ fail3:
|
|||
fail2:
|
||||
free_irq(udc->udp_irq, udc);
|
||||
fail1:
|
||||
device_unregister(&udc->gadget.dev);
|
||||
fail0b:
|
||||
iounmap(udc->udp_baseaddr);
|
||||
fail0a:
|
||||
if (cpu_is_at91rm9200())
|
||||
|
@ -1892,8 +1882,6 @@ static int __exit at91udc_remove(struct platform_device *pdev)
|
|||
gpio_free(udc->board.vbus_pin);
|
||||
}
|
||||
free_irq(udc->udp_irq, udc);
|
||||
device_unregister(&udc->gadget.dev);
|
||||
|
||||
iounmap(udc->udp_baseaddr);
|
||||
|
||||
if (cpu_is_at91rm9200())
|
||||
|
|
|
@ -489,13 +489,8 @@ request_complete(struct usba_ep *ep, struct usba_request *req, int status)
|
|||
if (req->req.status == -EINPROGRESS)
|
||||
req->req.status = status;
|
||||
|
||||
if (req->mapped) {
|
||||
dma_unmap_single(
|
||||
&udc->pdev->dev, req->req.dma, req->req.length,
|
||||
ep->is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
req->req.dma = DMA_ADDR_INVALID;
|
||||
req->mapped = 0;
|
||||
}
|
||||
if (req->using_dma)
|
||||
usb_gadget_unmap_request(&udc->gadget, &req->req, ep->is_in);
|
||||
|
||||
DBG(DBG_GADGET | DBG_REQ,
|
||||
"%s: req %p complete: status %d, actual %u\n",
|
||||
|
@ -684,7 +679,6 @@ usba_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
|
|||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(&req->queue);
|
||||
req->req.dma = DMA_ADDR_INVALID;
|
||||
|
||||
return &req->req;
|
||||
}
|
||||
|
@ -717,20 +711,11 @@ static int queue_dma(struct usba_udc *udc, struct usba_ep *ep,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = usb_gadget_map_request(&udc->gadget, &req->req, ep->is_in);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
req->using_dma = 1;
|
||||
|
||||
if (req->req.dma == DMA_ADDR_INVALID) {
|
||||
req->req.dma = dma_map_single(
|
||||
&udc->pdev->dev, req->req.buf, req->req.length,
|
||||
ep->is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
req->mapped = 1;
|
||||
} else {
|
||||
dma_sync_single_for_device(
|
||||
&udc->pdev->dev, req->req.dma, req->req.length,
|
||||
ep->is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
req->mapped = 0;
|
||||
}
|
||||
|
||||
req->ctrl = USBA_BF(DMA_BUF_LEN, req->req.length)
|
||||
| USBA_DMA_CH_EN | USBA_DMA_END_BUF_IE
|
||||
| USBA_DMA_END_TR_EN | USBA_DMA_END_TR_IE;
|
||||
|
@ -1799,7 +1784,6 @@ static int atmel_usba_start(struct usb_gadget *gadget,
|
|||
|
||||
udc->devstatus = 1 << USB_DEVICE_SELF_POWERED;
|
||||
udc->driver = driver;
|
||||
udc->gadget.dev.driver = &driver->driver;
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
clk_enable(udc->pclk);
|
||||
|
@ -1841,7 +1825,6 @@ static int atmel_usba_stop(struct usb_gadget *gadget,
|
|||
toggle_bias(0);
|
||||
usba_writel(udc, CTRL, USBA_DISABLE_MASK);
|
||||
|
||||
udc->gadget.dev.driver = NULL;
|
||||
udc->driver = NULL;
|
||||
|
||||
clk_disable(udc->hclk);
|
||||
|
@ -1900,10 +1883,6 @@ static int __init usba_udc_probe(struct platform_device *pdev)
|
|||
dev_info(&pdev->dev, "FIFO at 0x%08lx mapped at %p\n",
|
||||
(unsigned long)fifo->start, udc->fifo);
|
||||
|
||||
device_initialize(&udc->gadget.dev);
|
||||
udc->gadget.dev.parent = &pdev->dev;
|
||||
udc->gadget.dev.dma_mask = pdev->dev.dma_mask;
|
||||
|
||||
platform_set_drvdata(pdev, udc);
|
||||
|
||||
/* Make sure we start from a clean slate */
|
||||
|
@ -1962,12 +1941,6 @@ static int __init usba_udc_probe(struct platform_device *pdev)
|
|||
}
|
||||
udc->irq = irq;
|
||||
|
||||
ret = device_add(&udc->gadget.dev);
|
||||
if (ret) {
|
||||
dev_dbg(&pdev->dev, "Could not add gadget: %d\n", ret);
|
||||
goto err_device_add;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(pdata->vbus_pin)) {
|
||||
if (!gpio_request(pdata->vbus_pin, "atmel_usba_udc")) {
|
||||
udc->vbus_pin = pdata->vbus_pin;
|
||||
|
@ -2007,9 +1980,6 @@ err_add_udc:
|
|||
gpio_free(udc->vbus_pin);
|
||||
}
|
||||
|
||||
device_unregister(&udc->gadget.dev);
|
||||
|
||||
err_device_add:
|
||||
free_irq(irq, udc);
|
||||
err_request_irq:
|
||||
kfree(usba_ep);
|
||||
|
@ -2053,8 +2023,6 @@ static int __exit usba_udc_remove(struct platform_device *pdev)
|
|||
clk_put(udc->hclk);
|
||||
clk_put(udc->pclk);
|
||||
|
||||
device_unregister(&udc->gadget.dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -216,12 +216,6 @@
|
|||
#define EP0_EPT_SIZE USBA_EPT_SIZE_64
|
||||
#define EP0_NR_BANKS 1
|
||||
|
||||
/*
|
||||
* REVISIT: Try to eliminate this value. Can we rely on req->mapped to
|
||||
* provide this information?
|
||||
*/
|
||||
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
|
||||
|
||||
#define FIFO_IOMEM_ID 0
|
||||
#define CTRL_IOMEM_ID 1
|
||||
|
||||
|
|
|
@ -1819,7 +1819,6 @@ static int bcm63xx_udc_start(struct usb_gadget *gadget,
|
|||
|
||||
udc->driver = driver;
|
||||
driver->driver.bus = NULL;
|
||||
udc->gadget.dev.driver = &driver->driver;
|
||||
udc->gadget.dev.of_node = udc->dev->of_node;
|
||||
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
@ -1841,7 +1840,6 @@ static int bcm63xx_udc_stop(struct usb_gadget *gadget,
|
|||
spin_lock_irqsave(&udc->lock, flags);
|
||||
|
||||
udc->driver = NULL;
|
||||
udc->gadget.dev.driver = NULL;
|
||||
|
||||
/*
|
||||
* If we switch the PHY too abruptly after dropping D+, the host
|
||||
|
@ -2305,17 +2303,6 @@ static void bcm63xx_udc_cleanup_debugfs(struct bcm63xx_udc *udc)
|
|||
* Driver init/exit
|
||||
***********************************************************************/
|
||||
|
||||
/**
|
||||
* bcm63xx_udc_gadget_release - Called from device_release().
|
||||
* @dev: Unused.
|
||||
*
|
||||
* We get a warning if this function doesn't exist, but it's empty because
|
||||
* we don't have to free any of the memory allocated with the devm_* APIs.
|
||||
*/
|
||||
static void bcm63xx_udc_gadget_release(struct device *dev)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* bcm63xx_udc_probe - Initialize a new instance of the UDC.
|
||||
* @pdev: Platform device struct from the bcm63xx BSP code.
|
||||
|
@ -2368,13 +2355,9 @@ static int bcm63xx_udc_probe(struct platform_device *pdev)
|
|||
|
||||
spin_lock_init(&udc->lock);
|
||||
INIT_WORK(&udc->ep0_wq, bcm63xx_ep0_process);
|
||||
dev_set_name(&udc->gadget.dev, "gadget");
|
||||
|
||||
udc->gadget.ops = &bcm63xx_udc_ops;
|
||||
udc->gadget.name = dev_name(dev);
|
||||
udc->gadget.dev.parent = dev;
|
||||
udc->gadget.dev.release = bcm63xx_udc_gadget_release;
|
||||
udc->gadget.dev.dma_mask = dev->dma_mask;
|
||||
|
||||
if (!pd->use_fullspeed && !use_fullspeed)
|
||||
udc->gadget.max_speed = USB_SPEED_HIGH;
|
||||
|
@ -2414,17 +2397,12 @@ static int bcm63xx_udc_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
rc = device_register(&udc->gadget.dev);
|
||||
if (rc)
|
||||
goto out_uninit;
|
||||
|
||||
bcm63xx_udc_init_debugfs(udc);
|
||||
rc = usb_add_gadget_udc(dev, &udc->gadget);
|
||||
if (!rc)
|
||||
return 0;
|
||||
|
||||
bcm63xx_udc_cleanup_debugfs(udc);
|
||||
device_unregister(&udc->gadget.dev);
|
||||
out_uninit:
|
||||
bcm63xx_uninit_udc_hw(udc);
|
||||
return rc;
|
||||
|
@ -2440,7 +2418,6 @@ static int bcm63xx_udc_remove(struct platform_device *pdev)
|
|||
|
||||
bcm63xx_udc_cleanup_debugfs(udc);
|
||||
usb_del_gadget_udc(&udc->gadget);
|
||||
device_unregister(&udc->gadget.dev);
|
||||
BUG_ON(udc->driver);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
|
|
@ -103,18 +103,16 @@ static struct usb_gadget_strings *dev_strings[] = {
|
|||
};
|
||||
|
||||
static u8 hostaddr[ETH_ALEN];
|
||||
|
||||
static struct eth_dev *the_dev;
|
||||
/*-------------------------------------------------------------------------*/
|
||||
static struct usb_function *f_acm;
|
||||
static struct usb_function_instance *fi_serial;
|
||||
|
||||
static unsigned char tty_line;
|
||||
/*
|
||||
* We _always_ have both CDC ECM and CDC ACM functions.
|
||||
*/
|
||||
static int __init cdc_do_config(struct usb_configuration *c)
|
||||
{
|
||||
struct f_serial_opts *opts;
|
||||
int status;
|
||||
|
||||
if (gadget_is_otg(c->cdev->gadget)) {
|
||||
|
@ -122,7 +120,7 @@ static int __init cdc_do_config(struct usb_configuration *c)
|
|||
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
}
|
||||
|
||||
status = ecm_bind_config(c, hostaddr);
|
||||
status = ecm_bind_config(c, hostaddr, the_dev);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
|
@ -130,12 +128,11 @@ static int __init cdc_do_config(struct usb_configuration *c)
|
|||
if (IS_ERR(fi_serial))
|
||||
return PTR_ERR(fi_serial);
|
||||
|
||||
opts = container_of(fi_serial, struct f_serial_opts, func_inst);
|
||||
opts->port_num = tty_line;
|
||||
|
||||
f_acm = usb_get_function(fi_serial);
|
||||
if (IS_ERR(f_acm))
|
||||
if (IS_ERR(f_acm)) {
|
||||
status = PTR_ERR(f_acm);
|
||||
goto err_func_acm;
|
||||
}
|
||||
|
||||
status = usb_add_function(c, f_acm);
|
||||
if (status)
|
||||
|
@ -169,14 +166,9 @@ static int __init cdc_bind(struct usb_composite_dev *cdev)
|
|||
}
|
||||
|
||||
/* set up network link layer */
|
||||
status = gether_setup(cdev->gadget, hostaddr);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
/* set up serial link layer */
|
||||
status = gserial_alloc_line(&tty_line);
|
||||
if (status < 0)
|
||||
goto fail0;
|
||||
the_dev = gether_setup(cdev->gadget, hostaddr);
|
||||
if (IS_ERR(the_dev))
|
||||
return PTR_ERR(the_dev);
|
||||
|
||||
/* Allocate string descriptor numbers ... note that string
|
||||
* contents can be overridden by the composite_dev glue.
|
||||
|
@ -200,9 +192,7 @@ static int __init cdc_bind(struct usb_composite_dev *cdev)
|
|||
return 0;
|
||||
|
||||
fail1:
|
||||
gserial_free_line(tty_line);
|
||||
fail0:
|
||||
gether_cleanup();
|
||||
gether_cleanup(the_dev);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -210,8 +200,7 @@ static int __exit cdc_unbind(struct usb_composite_dev *cdev)
|
|||
{
|
||||
usb_put_function(f_acm);
|
||||
usb_put_function_instance(fi_serial);
|
||||
gserial_free_line(tty_line);
|
||||
gether_cleanup();
|
||||
gether_cleanup(the_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1637,6 +1637,7 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev)
|
|||
kfree(cdev->req->buf);
|
||||
usb_ep_free_request(cdev->gadget->ep0, cdev->req);
|
||||
}
|
||||
cdev->next_string_id = 0;
|
||||
device_remove_file(&cdev->gadget->dev, &dev_attr_suspended);
|
||||
}
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -912,7 +912,6 @@ static int dummy_udc_start(struct usb_gadget *g,
|
|||
dum->devstatus = 0;
|
||||
|
||||
dum->driver = driver;
|
||||
dum->gadget.dev.driver = &driver->driver;
|
||||
dev_dbg(udc_dev(dum), "binding gadget driver '%s'\n",
|
||||
driver->driver.name);
|
||||
return 0;
|
||||
|
@ -927,7 +926,6 @@ static int dummy_udc_stop(struct usb_gadget *g,
|
|||
dev_dbg(udc_dev(dum), "unregister gadget driver '%s'\n",
|
||||
driver->driver.name);
|
||||
|
||||
dum->gadget.dev.driver = NULL;
|
||||
dum->driver = NULL;
|
||||
|
||||
return 0;
|
||||
|
@ -937,11 +935,6 @@ static int dummy_udc_stop(struct usb_gadget *g,
|
|||
|
||||
/* The gadget structure is stored inside the hcd structure and will be
|
||||
* released along with it. */
|
||||
static void dummy_gadget_release(struct device *dev)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static void init_dummy_udc_hw(struct dummy *dum)
|
||||
{
|
||||
int i;
|
||||
|
@ -984,15 +977,7 @@ static int dummy_udc_probe(struct platform_device *pdev)
|
|||
dum->gadget.ops = &dummy_ops;
|
||||
dum->gadget.max_speed = USB_SPEED_SUPER;
|
||||
|
||||
dev_set_name(&dum->gadget.dev, "gadget");
|
||||
dum->gadget.dev.parent = &pdev->dev;
|
||||
dum->gadget.dev.release = dummy_gadget_release;
|
||||
rc = device_register(&dum->gadget.dev);
|
||||
if (rc < 0) {
|
||||
put_device(&dum->gadget.dev);
|
||||
return rc;
|
||||
}
|
||||
|
||||
init_dummy_udc_hw(dum);
|
||||
|
||||
rc = usb_add_gadget_udc(&pdev->dev, &dum->gadget);
|
||||
|
@ -1008,7 +993,6 @@ static int dummy_udc_probe(struct platform_device *pdev)
|
|||
err_dev:
|
||||
usb_del_gadget_udc(&dum->gadget);
|
||||
err_udc:
|
||||
device_unregister(&dum->gadget.dev);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -1019,7 +1003,6 @@ static int dummy_udc_remove(struct platform_device *pdev)
|
|||
usb_del_gadget_udc(&dum->gadget);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
device_remove_file(&dum->gadget.dev, &dev_attr_function);
|
||||
device_unregister(&dum->gadget.dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1923,7 +1906,7 @@ done:
|
|||
}
|
||||
|
||||
/* usb 3.0 root hub device descriptor */
|
||||
struct {
|
||||
static struct {
|
||||
struct usb_bos_descriptor bos;
|
||||
struct usb_ss_cap_descriptor ss_cap;
|
||||
} __packed usb3_bos_desc = {
|
||||
|
|
|
@ -207,7 +207,7 @@ static struct usb_gadget_strings *dev_strings[] = {
|
|||
};
|
||||
|
||||
static u8 hostaddr[ETH_ALEN];
|
||||
|
||||
static struct eth_dev *the_dev;
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
|
@ -224,7 +224,7 @@ static int __init rndis_do_config(struct usb_configuration *c)
|
|||
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
}
|
||||
|
||||
return rndis_bind_config(c, hostaddr);
|
||||
return rndis_bind_config(c, hostaddr, the_dev);
|
||||
}
|
||||
|
||||
static struct usb_configuration rndis_config_driver = {
|
||||
|
@ -257,11 +257,11 @@ static int __init eth_do_config(struct usb_configuration *c)
|
|||
}
|
||||
|
||||
if (use_eem)
|
||||
return eem_bind_config(c);
|
||||
return eem_bind_config(c, the_dev);
|
||||
else if (can_support_ecm(c->cdev->gadget))
|
||||
return ecm_bind_config(c, hostaddr);
|
||||
return ecm_bind_config(c, hostaddr, the_dev);
|
||||
else
|
||||
return geth_bind_config(c, hostaddr);
|
||||
return geth_bind_config(c, hostaddr, the_dev);
|
||||
}
|
||||
|
||||
static struct usb_configuration eth_config_driver = {
|
||||
|
@ -279,9 +279,9 @@ static int __init eth_bind(struct usb_composite_dev *cdev)
|
|||
int status;
|
||||
|
||||
/* set up network link layer */
|
||||
status = gether_setup(cdev->gadget, hostaddr);
|
||||
if (status < 0)
|
||||
return status;
|
||||
the_dev = gether_setup(cdev->gadget, hostaddr);
|
||||
if (IS_ERR(the_dev))
|
||||
return PTR_ERR(the_dev);
|
||||
|
||||
/* set up main config label and device descriptor */
|
||||
if (use_eem) {
|
||||
|
@ -338,13 +338,13 @@ static int __init eth_bind(struct usb_composite_dev *cdev)
|
|||
return 0;
|
||||
|
||||
fail:
|
||||
gether_cleanup();
|
||||
gether_cleanup(the_dev);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int __exit eth_unbind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
gether_cleanup();
|
||||
gether_cleanup(the_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -715,72 +715,6 @@ fail:
|
|||
return status;
|
||||
}
|
||||
|
||||
static struct f_acm *acm_alloc_basic_func(void)
|
||||
{
|
||||
struct f_acm *acm;
|
||||
|
||||
acm = kzalloc(sizeof(*acm), GFP_KERNEL);
|
||||
if (!acm)
|
||||
return NULL;
|
||||
|
||||
spin_lock_init(&acm->lock);
|
||||
|
||||
acm->port.connect = acm_connect;
|
||||
acm->port.disconnect = acm_disconnect;
|
||||
acm->port.send_break = acm_send_break;
|
||||
|
||||
acm->port.func.name = "acm";
|
||||
/* descriptors are per-instance copies */
|
||||
acm->port.func.bind = acm_bind;
|
||||
acm->port.func.set_alt = acm_set_alt;
|
||||
acm->port.func.setup = acm_setup;
|
||||
acm->port.func.disable = acm_disable;
|
||||
|
||||
return acm;
|
||||
}
|
||||
|
||||
#ifdef USB_FACM_INCLUDED
|
||||
static void
|
||||
acm_old_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct f_acm *acm = func_to_acm(f);
|
||||
|
||||
usb_free_all_descriptors(f);
|
||||
if (acm->notify_req)
|
||||
gs_free_req(acm->notify, acm->notify_req);
|
||||
kfree(acm);
|
||||
}
|
||||
|
||||
/**
|
||||
* acm_bind_config - add a CDC ACM function to a configuration
|
||||
* @c: the configuration to support the CDC ACM instance
|
||||
* @port_num: /dev/ttyGS* port this interface will use
|
||||
* Context: single threaded during gadget setup
|
||||
*
|
||||
* Returns zero on success, else negative errno.
|
||||
*
|
||||
*/
|
||||
int acm_bind_config(struct usb_configuration *c, u8 port_num)
|
||||
{
|
||||
struct f_acm *acm;
|
||||
int status;
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
acm = acm_alloc_basic_func();
|
||||
if (!acm)
|
||||
return -ENOMEM;
|
||||
|
||||
acm->port_num = port_num;
|
||||
acm->port.func.unbind = acm_old_unbind;
|
||||
|
||||
status = usb_add_function(c, &acm->port.func);
|
||||
if (status)
|
||||
kfree(acm);
|
||||
return status;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void acm_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct f_acm *acm = func_to_acm(f);
|
||||
|
@ -803,10 +737,24 @@ static struct usb_function *acm_alloc_func(struct usb_function_instance *fi)
|
|||
struct f_serial_opts *opts;
|
||||
struct f_acm *acm;
|
||||
|
||||
acm = acm_alloc_basic_func();
|
||||
acm = kzalloc(sizeof(*acm), GFP_KERNEL);
|
||||
if (!acm)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
spin_lock_init(&acm->lock);
|
||||
|
||||
acm->port.connect = acm_connect;
|
||||
acm->port.disconnect = acm_disconnect;
|
||||
acm->port.send_break = acm_send_break;
|
||||
|
||||
acm->port.func.name = "acm";
|
||||
acm->port.func.strings = acm_strings;
|
||||
/* descriptors are per-instance copies */
|
||||
acm->port.func.bind = acm_bind;
|
||||
acm->port.func.set_alt = acm_set_alt;
|
||||
acm->port.func.setup = acm_setup;
|
||||
acm->port.func.disable = acm_disable;
|
||||
|
||||
opts = container_of(fi, struct f_serial_opts, func_inst);
|
||||
acm->port_num = opts->port_num;
|
||||
acm->port.func.unbind = acm_unbind;
|
||||
|
@ -815,24 +763,85 @@ static struct usb_function *acm_alloc_func(struct usb_function_instance *fi)
|
|||
return &acm->port.func;
|
||||
}
|
||||
|
||||
static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item)
|
||||
{
|
||||
return container_of(to_config_group(item), struct f_serial_opts,
|
||||
func_inst.group);
|
||||
}
|
||||
|
||||
CONFIGFS_ATTR_STRUCT(f_serial_opts);
|
||||
static ssize_t f_acm_attr_show(struct config_item *item,
|
||||
struct configfs_attribute *attr,
|
||||
char *page)
|
||||
{
|
||||
struct f_serial_opts *opts = to_f_serial_opts(item);
|
||||
struct f_serial_opts_attribute *f_serial_opts_attr =
|
||||
container_of(attr, struct f_serial_opts_attribute, attr);
|
||||
ssize_t ret = 0;
|
||||
|
||||
if (f_serial_opts_attr->show)
|
||||
ret = f_serial_opts_attr->show(opts, page);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void acm_attr_release(struct config_item *item)
|
||||
{
|
||||
struct f_serial_opts *opts = to_f_serial_opts(item);
|
||||
|
||||
usb_put_function_instance(&opts->func_inst);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations acm_item_ops = {
|
||||
.release = acm_attr_release,
|
||||
.show_attribute = f_acm_attr_show,
|
||||
};
|
||||
|
||||
static ssize_t f_acm_port_num_show(struct f_serial_opts *opts, char *page)
|
||||
{
|
||||
return sprintf(page, "%u\n", opts->port_num);
|
||||
}
|
||||
|
||||
static struct f_serial_opts_attribute f_acm_port_num =
|
||||
__CONFIGFS_ATTR_RO(port_num, f_acm_port_num_show);
|
||||
|
||||
|
||||
static struct configfs_attribute *acm_attrs[] = {
|
||||
&f_acm_port_num.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct config_item_type acm_func_type = {
|
||||
.ct_item_ops = &acm_item_ops,
|
||||
.ct_attrs = acm_attrs,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static void acm_free_instance(struct usb_function_instance *fi)
|
||||
{
|
||||
struct f_serial_opts *opts;
|
||||
|
||||
opts = container_of(fi, struct f_serial_opts, func_inst);
|
||||
gserial_free_line(opts->port_num);
|
||||
kfree(opts);
|
||||
}
|
||||
|
||||
static struct usb_function_instance *acm_alloc_instance(void)
|
||||
{
|
||||
struct f_serial_opts *opts;
|
||||
int ret;
|
||||
|
||||
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
|
||||
if (!opts)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
opts->func_inst.free_func_inst = acm_free_instance;
|
||||
ret = gserial_alloc_line(&opts->port_num);
|
||||
if (ret) {
|
||||
kfree(opts);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
config_group_init_type_name(&opts->func_inst.group, "",
|
||||
&acm_func_type);
|
||||
return &opts->func_inst;
|
||||
}
|
||||
DECLARE_USB_FUNCTION_INIT(acm, acm_alloc_instance, acm_alloc_func);
|
||||
MODULE_LICENSE("GPL");
|
||||
#endif
|
||||
|
|
|
@ -824,7 +824,8 @@ ecm_unbind(struct usb_configuration *c, struct usb_function *f)
|
|||
* for calling @gether_cleanup() before module unload.
|
||||
*/
|
||||
int
|
||||
ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
|
||||
ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
|
||||
struct eth_dev *dev)
|
||||
{
|
||||
struct f_ecm *ecm;
|
||||
int status;
|
||||
|
@ -852,6 +853,7 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
|
|||
snprintf(ecm->ethaddr, sizeof ecm->ethaddr, "%pm", ethaddr);
|
||||
ecm_string_defs[1].s = ecm->ethaddr;
|
||||
|
||||
ecm->port.ioport = dev;
|
||||
ecm->port.cdc_filter = DEFAULT_FILTER;
|
||||
|
||||
ecm->port.func.name = "cdc_ethernet";
|
||||
|
|
|
@ -528,7 +528,7 @@ error:
|
|||
* Caller must have called @gether_setup(). Caller is also responsible
|
||||
* for calling @gether_cleanup() before module unload.
|
||||
*/
|
||||
int __init eem_bind_config(struct usb_configuration *c)
|
||||
int __init eem_bind_config(struct usb_configuration *c, struct eth_dev *dev)
|
||||
{
|
||||
struct f_eem *eem;
|
||||
int status;
|
||||
|
@ -549,6 +549,7 @@ int __init eem_bind_config(struct usb_configuration *c)
|
|||
if (!eem)
|
||||
return -ENOMEM;
|
||||
|
||||
eem->port.ioport = dev;
|
||||
eem->port.cdc_filter = DEFAULT_FILTER;
|
||||
|
||||
eem->port.func.name = "cdc_eem";
|
||||
|
|
|
@ -1287,7 +1287,8 @@ ncm_unbind(struct usb_configuration *c, struct usb_function *f)
|
|||
* Caller must have called @gether_setup(). Caller is also responsible
|
||||
* for calling @gether_cleanup() before module unload.
|
||||
*/
|
||||
int __init ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
|
||||
int __init ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
|
||||
struct eth_dev *dev)
|
||||
{
|
||||
struct f_ncm *ncm;
|
||||
int status;
|
||||
|
@ -1321,6 +1322,7 @@ int __init ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
|
|||
|
||||
spin_lock_init(&ncm->lock);
|
||||
ncm_reset_values(ncm);
|
||||
ncm->port.ioport = dev;
|
||||
ncm->port.is_fixed = true;
|
||||
|
||||
ncm->port.func.name = "cdc_network";
|
||||
|
|
|
@ -72,7 +72,7 @@ static struct usb_gadget_strings *obex_strings[] = {
|
|||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static struct usb_interface_descriptor obex_control_intf __initdata = {
|
||||
static struct usb_interface_descriptor obex_control_intf = {
|
||||
.bLength = sizeof(obex_control_intf),
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bInterfaceNumber = 0,
|
||||
|
@ -83,7 +83,7 @@ static struct usb_interface_descriptor obex_control_intf __initdata = {
|
|||
.bInterfaceSubClass = USB_CDC_SUBCLASS_OBEX,
|
||||
};
|
||||
|
||||
static struct usb_interface_descriptor obex_data_nop_intf __initdata = {
|
||||
static struct usb_interface_descriptor obex_data_nop_intf = {
|
||||
.bLength = sizeof(obex_data_nop_intf),
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bInterfaceNumber = 1,
|
||||
|
@ -93,7 +93,7 @@ static struct usb_interface_descriptor obex_data_nop_intf __initdata = {
|
|||
.bInterfaceClass = USB_CLASS_CDC_DATA,
|
||||
};
|
||||
|
||||
static struct usb_interface_descriptor obex_data_intf __initdata = {
|
||||
static struct usb_interface_descriptor obex_data_intf = {
|
||||
.bLength = sizeof(obex_data_intf),
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bInterfaceNumber = 2,
|
||||
|
@ -103,14 +103,14 @@ static struct usb_interface_descriptor obex_data_intf __initdata = {
|
|||
.bInterfaceClass = USB_CLASS_CDC_DATA,
|
||||
};
|
||||
|
||||
static struct usb_cdc_header_desc obex_cdc_header_desc __initdata = {
|
||||
static struct usb_cdc_header_desc obex_cdc_header_desc = {
|
||||
.bLength = sizeof(obex_cdc_header_desc),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
|
||||
.bcdCDC = cpu_to_le16(0x0120),
|
||||
};
|
||||
|
||||
static struct usb_cdc_union_desc obex_cdc_union_desc __initdata = {
|
||||
static struct usb_cdc_union_desc obex_cdc_union_desc = {
|
||||
.bLength = sizeof(obex_cdc_union_desc),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_UNION_TYPE,
|
||||
|
@ -118,7 +118,7 @@ static struct usb_cdc_union_desc obex_cdc_union_desc __initdata = {
|
|||
.bSlaveInterface0 = 2,
|
||||
};
|
||||
|
||||
static struct usb_cdc_obex_desc obex_desc __initdata = {
|
||||
static struct usb_cdc_obex_desc obex_desc = {
|
||||
.bLength = sizeof(obex_desc),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_OBEX_TYPE,
|
||||
|
@ -127,7 +127,7 @@ static struct usb_cdc_obex_desc obex_desc __initdata = {
|
|||
|
||||
/* High-Speed Support */
|
||||
|
||||
static struct usb_endpoint_descriptor obex_hs_ep_out_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor obex_hs_ep_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
|
@ -136,7 +136,7 @@ static struct usb_endpoint_descriptor obex_hs_ep_out_desc __initdata = {
|
|||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor obex_hs_ep_in_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor obex_hs_ep_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
|
@ -145,7 +145,7 @@ static struct usb_endpoint_descriptor obex_hs_ep_in_desc __initdata = {
|
|||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *hs_function[] __initdata = {
|
||||
static struct usb_descriptor_header *hs_function[] = {
|
||||
(struct usb_descriptor_header *) &obex_control_intf,
|
||||
(struct usb_descriptor_header *) &obex_cdc_header_desc,
|
||||
(struct usb_descriptor_header *) &obex_desc,
|
||||
|
@ -160,7 +160,7 @@ static struct usb_descriptor_header *hs_function[] __initdata = {
|
|||
|
||||
/* Full-Speed Support */
|
||||
|
||||
static struct usb_endpoint_descriptor obex_fs_ep_in_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor obex_fs_ep_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
|
@ -168,7 +168,7 @@ static struct usb_endpoint_descriptor obex_fs_ep_in_desc __initdata = {
|
|||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor obex_fs_ep_out_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor obex_fs_ep_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
|
@ -176,7 +176,7 @@ static struct usb_endpoint_descriptor obex_fs_ep_out_desc __initdata = {
|
|||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *fs_function[] __initdata = {
|
||||
static struct usb_descriptor_header *fs_function[] = {
|
||||
(struct usb_descriptor_header *) &obex_control_intf,
|
||||
(struct usb_descriptor_header *) &obex_cdc_header_desc,
|
||||
(struct usb_descriptor_header *) &obex_desc,
|
||||
|
@ -290,14 +290,43 @@ static void obex_disconnect(struct gserial *g)
|
|||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init
|
||||
obex_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
/* Some controllers can't support CDC OBEX ... */
|
||||
static inline bool can_support_obex(struct usb_configuration *c)
|
||||
{
|
||||
/* Since the first interface is a NOP, we can ignore the
|
||||
* issue of multi-interface support on most controllers.
|
||||
*
|
||||
* Altsettings are mandatory, however...
|
||||
*/
|
||||
if (!gadget_supports_altsettings(c->cdev->gadget))
|
||||
return false;
|
||||
|
||||
/* everything else is *probably* fine ... */
|
||||
return true;
|
||||
}
|
||||
|
||||
static int obex_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
struct f_obex *obex = func_to_obex(f);
|
||||
int status;
|
||||
struct usb_ep *ep;
|
||||
|
||||
if (!can_support_obex(c))
|
||||
return -EINVAL;
|
||||
|
||||
if (obex_string_defs[OBEX_CTRL_IDX].id == 0) {
|
||||
status = usb_string_ids_tab(c->cdev, obex_string_defs);
|
||||
if (status < 0)
|
||||
return status;
|
||||
obex_control_intf.iInterface =
|
||||
obex_string_defs[OBEX_CTRL_IDX].id;
|
||||
|
||||
status = obex_string_defs[OBEX_DATA_IDX].id;
|
||||
obex_data_nop_intf.iInterface = status;
|
||||
obex_data_intf.iInterface = status;
|
||||
}
|
||||
|
||||
/* allocate instance-specific interface IDs, and patch descriptors */
|
||||
|
||||
status = usb_interface_id(c, f);
|
||||
|
@ -319,6 +348,7 @@ obex_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
|
||||
/* allocate instance-specific endpoints */
|
||||
|
||||
status = -ENODEV;
|
||||
ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_in_desc);
|
||||
if (!ep)
|
||||
goto fail;
|
||||
|
@ -376,29 +406,16 @@ fail:
|
|||
return status;
|
||||
}
|
||||
|
||||
#ifdef USBF_OBEX_INCLUDED
|
||||
|
||||
static void
|
||||
obex_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
obex_old_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
obex_string_defs[OBEX_CTRL_IDX].id = 0;
|
||||
usb_free_all_descriptors(f);
|
||||
kfree(func_to_obex(f));
|
||||
}
|
||||
|
||||
/* Some controllers can't support CDC OBEX ... */
|
||||
static inline bool can_support_obex(struct usb_configuration *c)
|
||||
{
|
||||
/* Since the first interface is a NOP, we can ignore the
|
||||
* issue of multi-interface support on most controllers.
|
||||
*
|
||||
* Altsettings are mandatory, however...
|
||||
*/
|
||||
if (!gadget_supports_altsettings(c->cdev->gadget))
|
||||
return false;
|
||||
|
||||
/* everything else is *probably* fine ... */
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* obex_bind_config - add a CDC OBEX function to a configuration
|
||||
* @c: the configuration to support the CDC OBEX instance
|
||||
|
@ -412,21 +429,6 @@ int __init obex_bind_config(struct usb_configuration *c, u8 port_num)
|
|||
struct f_obex *obex;
|
||||
int status;
|
||||
|
||||
if (!can_support_obex(c))
|
||||
return -EINVAL;
|
||||
|
||||
if (obex_string_defs[OBEX_CTRL_IDX].id == 0) {
|
||||
status = usb_string_ids_tab(c->cdev, obex_string_defs);
|
||||
if (status < 0)
|
||||
return status;
|
||||
obex_control_intf.iInterface =
|
||||
obex_string_defs[OBEX_CTRL_IDX].id;
|
||||
|
||||
status = obex_string_defs[OBEX_DATA_IDX].id;
|
||||
obex_data_nop_intf.iInterface = status;
|
||||
obex_data_intf.iInterface = status;
|
||||
}
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
obex = kzalloc(sizeof *obex, GFP_KERNEL);
|
||||
if (!obex)
|
||||
|
@ -441,7 +443,7 @@ int __init obex_bind_config(struct usb_configuration *c, u8 port_num)
|
|||
obex->port.func.strings = obex_strings;
|
||||
/* descriptors are per-instance copies */
|
||||
obex->port.func.bind = obex_bind;
|
||||
obex->port.func.unbind = obex_unbind;
|
||||
obex->port.func.unbind = obex_old_unbind;
|
||||
obex->port.func.set_alt = obex_set_alt;
|
||||
obex->port.func.get_alt = obex_get_alt;
|
||||
obex->port.func.disable = obex_disable;
|
||||
|
@ -453,5 +455,138 @@ int __init obex_bind_config(struct usb_configuration *c, u8 port_num)
|
|||
return status;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item)
|
||||
{
|
||||
return container_of(to_config_group(item), struct f_serial_opts,
|
||||
func_inst.group);
|
||||
}
|
||||
|
||||
CONFIGFS_ATTR_STRUCT(f_serial_opts);
|
||||
static ssize_t f_obex_attr_show(struct config_item *item,
|
||||
struct configfs_attribute *attr,
|
||||
char *page)
|
||||
{
|
||||
struct f_serial_opts *opts = to_f_serial_opts(item);
|
||||
struct f_serial_opts_attribute *f_serial_opts_attr =
|
||||
container_of(attr, struct f_serial_opts_attribute, attr);
|
||||
ssize_t ret = 0;
|
||||
|
||||
if (f_serial_opts_attr->show)
|
||||
ret = f_serial_opts_attr->show(opts, page);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void obex_attr_release(struct config_item *item)
|
||||
{
|
||||
struct f_serial_opts *opts = to_f_serial_opts(item);
|
||||
|
||||
usb_put_function_instance(&opts->func_inst);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations obex_item_ops = {
|
||||
.release = obex_attr_release,
|
||||
.show_attribute = f_obex_attr_show,
|
||||
};
|
||||
|
||||
static ssize_t f_obex_port_num_show(struct f_serial_opts *opts, char *page)
|
||||
{
|
||||
return sprintf(page, "%u\n", opts->port_num);
|
||||
}
|
||||
|
||||
static struct f_serial_opts_attribute f_obex_port_num =
|
||||
__CONFIGFS_ATTR_RO(port_num, f_obex_port_num_show);
|
||||
|
||||
static struct configfs_attribute *acm_attrs[] = {
|
||||
&f_obex_port_num.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct config_item_type obex_func_type = {
|
||||
.ct_item_ops = &obex_item_ops,
|
||||
.ct_attrs = acm_attrs,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static void obex_free_inst(struct usb_function_instance *f)
|
||||
{
|
||||
struct f_serial_opts *opts;
|
||||
|
||||
opts = container_of(f, struct f_serial_opts, func_inst);
|
||||
gserial_free_line(opts->port_num);
|
||||
kfree(opts);
|
||||
}
|
||||
|
||||
static struct usb_function_instance *obex_alloc_inst(void)
|
||||
{
|
||||
struct f_serial_opts *opts;
|
||||
int ret;
|
||||
|
||||
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
|
||||
if (!opts)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
opts->func_inst.free_func_inst = obex_free_inst;
|
||||
ret = gserial_alloc_line(&opts->port_num);
|
||||
if (ret) {
|
||||
kfree(opts);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
config_group_init_type_name(&opts->func_inst.group, "",
|
||||
&obex_func_type);
|
||||
|
||||
return &opts->func_inst;
|
||||
}
|
||||
|
||||
static void obex_free(struct usb_function *f)
|
||||
{
|
||||
struct f_obex *obex;
|
||||
|
||||
obex = func_to_obex(f);
|
||||
kfree(obex);
|
||||
}
|
||||
|
||||
static void obex_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
obex_string_defs[OBEX_CTRL_IDX].id = 0;
|
||||
usb_free_all_descriptors(f);
|
||||
}
|
||||
|
||||
struct usb_function *obex_alloc(struct usb_function_instance *fi)
|
||||
{
|
||||
struct f_obex *obex;
|
||||
struct f_serial_opts *opts;
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
obex = kzalloc(sizeof(*obex), GFP_KERNEL);
|
||||
if (!obex)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
opts = container_of(fi, struct f_serial_opts, func_inst);
|
||||
|
||||
obex->port_num = opts->port_num;
|
||||
|
||||
obex->port.connect = obex_connect;
|
||||
obex->port.disconnect = obex_disconnect;
|
||||
|
||||
obex->port.func.name = "obex";
|
||||
obex->port.func.strings = obex_strings;
|
||||
/* descriptors are per-instance copies */
|
||||
obex->port.func.bind = obex_bind;
|
||||
obex->port.func.unbind = obex_unbind;
|
||||
obex->port.func.set_alt = obex_set_alt;
|
||||
obex->port.func.get_alt = obex_get_alt;
|
||||
obex->port.func.disable = obex_disable;
|
||||
obex->port.func.free_func = obex_free;
|
||||
|
||||
return &obex->port.func;
|
||||
}
|
||||
|
||||
DECLARE_USB_FUNCTION_INIT(obex, obex_alloc_inst, obex_alloc);
|
||||
|
||||
#endif
|
||||
|
||||
MODULE_AUTHOR("Felipe Balbi");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -813,7 +813,7 @@ static inline bool can_support_rndis(struct usb_configuration *c)
|
|||
|
||||
int
|
||||
rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
|
||||
u32 vendorID, const char *manufacturer)
|
||||
u32 vendorID, const char *manufacturer, struct eth_dev *dev)
|
||||
{
|
||||
struct f_rndis *rndis;
|
||||
int status;
|
||||
|
@ -846,6 +846,7 @@ rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
|
|||
rndis->vendorID = vendorID;
|
||||
rndis->manufacturer = manufacturer;
|
||||
|
||||
rndis->port.ioport = dev;
|
||||
/* RNDIS activates when the host changes this filter */
|
||||
rndis->port.cdc_filter = 0;
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include "u_serial.h"
|
||||
|
@ -42,7 +43,7 @@ static inline struct f_gser *func_to_gser(struct usb_function *f)
|
|||
|
||||
/* interface descriptor: */
|
||||
|
||||
static struct usb_interface_descriptor gser_interface_desc __initdata = {
|
||||
static struct usb_interface_descriptor gser_interface_desc = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
/* .bInterfaceNumber = DYNAMIC */
|
||||
|
@ -55,21 +56,21 @@ static struct usb_interface_descriptor gser_interface_desc __initdata = {
|
|||
|
||||
/* full speed support: */
|
||||
|
||||
static struct usb_endpoint_descriptor gser_fs_in_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor gser_fs_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor gser_fs_out_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor gser_fs_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_OUT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *gser_fs_function[] __initdata = {
|
||||
static struct usb_descriptor_header *gser_fs_function[] = {
|
||||
(struct usb_descriptor_header *) &gser_interface_desc,
|
||||
(struct usb_descriptor_header *) &gser_fs_in_desc,
|
||||
(struct usb_descriptor_header *) &gser_fs_out_desc,
|
||||
|
@ -78,47 +79,47 @@ static struct usb_descriptor_header *gser_fs_function[] __initdata = {
|
|||
|
||||
/* high speed support: */
|
||||
|
||||
static struct usb_endpoint_descriptor gser_hs_in_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor gser_hs_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor gser_hs_out_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor gser_hs_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *gser_hs_function[] __initdata = {
|
||||
static struct usb_descriptor_header *gser_hs_function[] = {
|
||||
(struct usb_descriptor_header *) &gser_interface_desc,
|
||||
(struct usb_descriptor_header *) &gser_hs_in_desc,
|
||||
(struct usb_descriptor_header *) &gser_hs_out_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor gser_ss_in_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor gser_ss_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(1024),
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor gser_ss_out_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor gser_ss_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 gser_ss_bulk_comp_desc __initdata = {
|
||||
static struct usb_ss_ep_comp_descriptor gser_ss_bulk_comp_desc = {
|
||||
.bLength = sizeof gser_ss_bulk_comp_desc,
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *gser_ss_function[] __initdata = {
|
||||
static struct usb_descriptor_header *gser_ss_function[] = {
|
||||
(struct usb_descriptor_header *) &gser_interface_desc,
|
||||
(struct usb_descriptor_header *) &gser_ss_in_desc,
|
||||
(struct usb_descriptor_header *) &gser_ss_bulk_comp_desc,
|
||||
|
@ -183,14 +184,25 @@ static void gser_disable(struct usb_function *f)
|
|||
|
||||
/* serial function driver setup/binding */
|
||||
|
||||
static int __init
|
||||
gser_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
static int gser_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
struct f_gser *gser = func_to_gser(f);
|
||||
int status;
|
||||
struct usb_ep *ep;
|
||||
|
||||
/* REVISIT might want instance-specific strings to help
|
||||
* distinguish instances ...
|
||||
*/
|
||||
|
||||
/* maybe allocate device-global string ID */
|
||||
if (gser_string_defs[0].id == 0) {
|
||||
status = usb_string_id(c->cdev);
|
||||
if (status < 0)
|
||||
return status;
|
||||
gser_string_defs[0].id = status;
|
||||
}
|
||||
|
||||
/* allocate instance-specific interface IDs */
|
||||
status = usb_interface_id(c, f);
|
||||
if (status < 0)
|
||||
|
@ -246,44 +258,115 @@ fail:
|
|||
return status;
|
||||
}
|
||||
|
||||
static void
|
||||
gser_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item)
|
||||
{
|
||||
usb_free_all_descriptors(f);
|
||||
kfree(func_to_gser(f));
|
||||
return container_of(to_config_group(item), struct f_serial_opts,
|
||||
func_inst.group);
|
||||
}
|
||||
|
||||
/**
|
||||
* gser_bind_config - add a generic serial function to a configuration
|
||||
* @c: the configuration to support the serial instance
|
||||
* @port_num: /dev/ttyGS* port this interface will use
|
||||
* Context: single threaded during gadget setup
|
||||
*
|
||||
* Returns zero on success, else negative errno.
|
||||
*/
|
||||
int __init gser_bind_config(struct usb_configuration *c, u8 port_num)
|
||||
CONFIGFS_ATTR_STRUCT(f_serial_opts);
|
||||
static ssize_t f_serial_attr_show(struct config_item *item,
|
||||
struct configfs_attribute *attr,
|
||||
char *page)
|
||||
{
|
||||
struct f_serial_opts *opts = to_f_serial_opts(item);
|
||||
struct f_serial_opts_attribute *f_serial_opts_attr =
|
||||
container_of(attr, struct f_serial_opts_attribute, attr);
|
||||
ssize_t ret = 0;
|
||||
|
||||
if (f_serial_opts_attr->show)
|
||||
ret = f_serial_opts_attr->show(opts, page);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void serial_attr_release(struct config_item *item)
|
||||
{
|
||||
struct f_serial_opts *opts = to_f_serial_opts(item);
|
||||
|
||||
usb_put_function_instance(&opts->func_inst);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations serial_item_ops = {
|
||||
.release = serial_attr_release,
|
||||
.show_attribute = f_serial_attr_show,
|
||||
};
|
||||
|
||||
static ssize_t f_serial_port_num_show(struct f_serial_opts *opts, char *page)
|
||||
{
|
||||
return sprintf(page, "%u\n", opts->port_num);
|
||||
}
|
||||
|
||||
static struct f_serial_opts_attribute f_serial_port_num =
|
||||
__CONFIGFS_ATTR_RO(port_num, f_serial_port_num_show);
|
||||
|
||||
static struct configfs_attribute *acm_attrs[] = {
|
||||
&f_serial_port_num.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct config_item_type serial_func_type = {
|
||||
.ct_item_ops = &serial_item_ops,
|
||||
.ct_attrs = acm_attrs,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static void gser_free_inst(struct usb_function_instance *f)
|
||||
{
|
||||
struct f_serial_opts *opts;
|
||||
|
||||
opts = container_of(f, struct f_serial_opts, func_inst);
|
||||
gserial_free_line(opts->port_num);
|
||||
kfree(opts);
|
||||
}
|
||||
|
||||
static struct usb_function_instance *gser_alloc_inst(void)
|
||||
{
|
||||
struct f_serial_opts *opts;
|
||||
int ret;
|
||||
|
||||
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
|
||||
if (!opts)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
opts->func_inst.free_func_inst = gser_free_inst;
|
||||
ret = gserial_alloc_line(&opts->port_num);
|
||||
if (ret) {
|
||||
kfree(opts);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
config_group_init_type_name(&opts->func_inst.group, "",
|
||||
&serial_func_type);
|
||||
|
||||
return &opts->func_inst;
|
||||
}
|
||||
|
||||
static void gser_free(struct usb_function *f)
|
||||
{
|
||||
struct f_gser *serial;
|
||||
|
||||
serial = func_to_gser(f);
|
||||
kfree(serial);
|
||||
}
|
||||
|
||||
static void gser_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
usb_free_all_descriptors(f);
|
||||
}
|
||||
|
||||
struct usb_function *gser_alloc(struct usb_function_instance *fi)
|
||||
{
|
||||
struct f_gser *gser;
|
||||
int status;
|
||||
|
||||
/* REVISIT might want instance-specific strings to help
|
||||
* distinguish instances ...
|
||||
*/
|
||||
|
||||
/* maybe allocate device-global string ID */
|
||||
if (gser_string_defs[0].id == 0) {
|
||||
status = usb_string_id(c->cdev);
|
||||
if (status < 0)
|
||||
return status;
|
||||
gser_string_defs[0].id = status;
|
||||
}
|
||||
struct f_serial_opts *opts;
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
gser = kzalloc(sizeof *gser, GFP_KERNEL);
|
||||
gser = kzalloc(sizeof(*gser), GFP_KERNEL);
|
||||
if (!gser)
|
||||
return -ENOMEM;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
gser->port_num = port_num;
|
||||
opts = container_of(fi, struct f_serial_opts, func_inst);
|
||||
|
||||
gser->port_num = opts->port_num;
|
||||
|
||||
gser->port.func.name = "gser";
|
||||
gser->port.func.strings = gser_strings;
|
||||
|
@ -291,9 +374,12 @@ int __init gser_bind_config(struct usb_configuration *c, u8 port_num)
|
|||
gser->port.func.unbind = gser_unbind;
|
||||
gser->port.func.set_alt = gser_set_alt;
|
||||
gser->port.func.disable = gser_disable;
|
||||
gser->port.func.free_func = gser_free;
|
||||
|
||||
status = usb_add_function(c, &gser->port.func);
|
||||
if (status)
|
||||
kfree(gser);
|
||||
return status;
|
||||
return &gser->port.func;
|
||||
}
|
||||
|
||||
DECLARE_USB_FUNCTION_INIT(gser, gser_alloc_inst, gser_alloc);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Al Borchers");
|
||||
MODULE_AUTHOR("David Brownell");
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче