mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
USB patches for 3.19-rc1
Here's the big set of USB and PHY patches for 3.19-rc1. The normal churn in the USB gadget area is in here, as well as xhci and other individual USB driver updates. The PHY tree is also in here, as there were dependancies on the USB tree. All of these have been in linux-next. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iEYEABECAAYFAlSOEHcACgkQMUfUDdst+ykziQCgsm1D/af2nac6CTF2pov8VMIY ywgAnRi8LtZ2WassrwTNxY86Avaqryis =UVp8 -----END PGP SIGNATURE----- Merge tag 'usb-3.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB updates from Greg KH: "Here's the big set of USB and PHY patches for 3.19-rc1. The normal churn in the USB gadget area is in here, as well as xhci and other individual USB driver updates. The PHY tree is also in here, as there were dependancies on the USB tree. All of these have been in linux-next" * tag 'usb-3.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (351 commits) arm: omap3: twl: remove usb phy init data usbip: fix error handling in stub_probe() usb: gadget: udc: missing curly braces USB: mos7720: delete some unneeded code wusb: replace memset by memzero_explicit usbip: remove unneeded structure usb: xhci: fix comment for PORT_DEV_REMOVE xhci: don't use the same variable for stopped and halted rings current TD xhci: clear extra bits from slot context when setting max exit latency xhci: cleanup finish_td function USB: adutux: NULL dereferences on disconnect usb: chipidea: fix platform_no_drv_owner.cocci warnings usb: chipidea: Fixed a few typos in comments Documentation: bindings: add doc for the USB2 ChipIdea USB driver usb: chipidea: add a usb2 driver for ci13xxx usb: chipidea: fix phy handling usb: chipidea: remove duplicate dev_set_drvdata for host_start usb: chipidea: parameter 'mode' isn't needed for hw_device_reset usb: chipidea: add controller reset API usb: chipidea: remove flag CI_HDRC_REQUIRE_TRANSCEIVER ...
This commit is contained in:
commit
e7cf773d43
93
Documentation/ABI/stable/sysfs-class-udc
Normal file
93
Documentation/ABI/stable/sysfs-class-udc
Normal file
@ -0,0 +1,93 @@
|
||||
What: /sys/class/udc/<udc>/a_alt_hnp_support
|
||||
Date: June 2011
|
||||
KernelVersion: 3.1
|
||||
Contact: Felipe Balbi <balbi@kernel.org>
|
||||
Description:
|
||||
Indicates if an OTG A-Host supports HNP at an alternate port.
|
||||
Users:
|
||||
|
||||
What: /sys/class/udc/<udc>/a_hnp_support
|
||||
Date: June 2011
|
||||
KernelVersion: 3.1
|
||||
Contact: Felipe Balbi <balbi@kernel.org>
|
||||
Description:
|
||||
Indicates if an OTG A-Host supports HNP at this port.
|
||||
Users:
|
||||
|
||||
What: /sys/class/udc/<udc>/b_hnp_enable
|
||||
Date: June 2011
|
||||
KernelVersion: 3.1
|
||||
Contact: Felipe Balbi <balbi@kernel.org>
|
||||
Description:
|
||||
Indicates if an OTG A-Host enabled HNP support.
|
||||
Users:
|
||||
|
||||
What: /sys/class/udc/<udc>/current_speed
|
||||
Date: June 2011
|
||||
KernelVersion: 3.1
|
||||
Contact: Felipe Balbi <balbi@kernel.org>
|
||||
Description:
|
||||
Indicates the current negotiated speed at this port.
|
||||
Users:
|
||||
|
||||
What: /sys/class/udc/<udc>/is_a_peripheral
|
||||
Date: June 2011
|
||||
KernelVersion: 3.1
|
||||
Contact: Felipe Balbi <balbi@kernel.org>
|
||||
Description:
|
||||
Indicates that this port is the default Host on an OTG session
|
||||
but HNP was used to switch roles.
|
||||
Users:
|
||||
|
||||
What: /sys/class/udc/<udc>/is_otg
|
||||
Date: June 2011
|
||||
KernelVersion: 3.1
|
||||
Contact: Felipe Balbi <balbi@kernel.org>
|
||||
Description:
|
||||
Indicates that this port support OTG.
|
||||
Users:
|
||||
|
||||
What: /sys/class/udc/<udc>/maximum_speed
|
||||
Date: June 2011
|
||||
KernelVersion: 3.1
|
||||
Contact: Felipe Balbi <balbi@kernel.org>
|
||||
Description:
|
||||
Indicates the maximum USB speed supported by this port.
|
||||
Users:
|
||||
|
||||
What: /sys/class/udc/<udc>/maximum_speed
|
||||
Date: June 2011
|
||||
KernelVersion: 3.1
|
||||
Contact: Felipe Balbi <balbi@kernel.org>
|
||||
Description:
|
||||
Indicates the maximum USB speed supported by this port.
|
||||
Users:
|
||||
|
||||
What: /sys/class/udc/<udc>/soft_connect
|
||||
Date: June 2011
|
||||
KernelVersion: 3.1
|
||||
Contact: Felipe Balbi <balbi@kernel.org>
|
||||
Description:
|
||||
Allows users to disconnect data pullup resistors thus causing a
|
||||
logical disconnection from the USB Host.
|
||||
Users:
|
||||
|
||||
What: /sys/class/udc/<udc>/srp
|
||||
Date: June 2011
|
||||
KernelVersion: 3.1
|
||||
Contact: Felipe Balbi <balbi@kernel.org>
|
||||
Description:
|
||||
Allows users to manually start Session Request Protocol.
|
||||
Users:
|
||||
|
||||
What: /sys/class/udc/<udc>/state
|
||||
Date: June 2011
|
||||
KernelVersion: 3.1
|
||||
Contact: Felipe Balbi <balbi@kernel.org>
|
||||
Description:
|
||||
Indicates current state of the USB Device Controller. Valid
|
||||
states are: 'not-attached', 'attached', 'powered',
|
||||
'reconnecting', 'unauthenticated', 'default', 'addressed',
|
||||
'configured', and 'suspended'; however not all USB Device
|
||||
Controllers support reporting all states.
|
||||
Users:
|
11
Documentation/ABI/testing/configfs-usb-gadget-hid
Normal file
11
Documentation/ABI/testing/configfs-usb-gadget-hid
Normal file
@ -0,0 +1,11 @@
|
||||
What: /config/usb-gadget/gadget/functions/hid.name
|
||||
Date: Nov 2014
|
||||
KernelVersion: 3.19
|
||||
Description:
|
||||
The attributes:
|
||||
|
||||
protocol - HID protocol to use
|
||||
report_desc - blob corresponding to HID report descriptors
|
||||
except the data passed through /dev/hidg<N>
|
||||
report_length - HID report length
|
||||
subclass - HID device subclass to use
|
12
Documentation/ABI/testing/configfs-usb-gadget-midi
Normal file
12
Documentation/ABI/testing/configfs-usb-gadget-midi
Normal file
@ -0,0 +1,12 @@
|
||||
What: /config/usb-gadget/gadget/functions/midi.name
|
||||
Date: Nov 2014
|
||||
KernelVersion: 3.19
|
||||
Description:
|
||||
The attributes:
|
||||
|
||||
index - index value for the USB MIDI adapter
|
||||
id - ID string for the USB MIDI adapter
|
||||
buflen - MIDI buffer length
|
||||
qlen - USB read request queue length
|
||||
in_ports - number of MIDI input ports
|
||||
out_ports - number of MIDI output ports
|
@ -6,11 +6,17 @@ Required Properties:
|
||||
- interrupts : Interrupt controller is using
|
||||
- nr-ports : Number of SATA ports in use.
|
||||
|
||||
Optional Properties:
|
||||
- phys : List of phandles to sata phys
|
||||
- phy-names : Should be "0", "1", etc, one number per phandle
|
||||
|
||||
Example:
|
||||
|
||||
sata@80000 {
|
||||
compatible = "marvell,orion-sata";
|
||||
reg = <0x80000 0x5000>;
|
||||
interrupts = <21>;
|
||||
phys = <&sata_phy0>, <&sata_phy1>;
|
||||
phy-names = "0", "1";
|
||||
nr-ports = <2>;
|
||||
}
|
||||
|
@ -2,7 +2,9 @@ Berlin SATA PHY
|
||||
---------------
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "marvell,berlin2q-sata-phy"
|
||||
- compatible: should be one of
|
||||
"marvell,berlin2-sata-phy"
|
||||
"marvell,berlin2q-sata-phy"
|
||||
- address-cells: should be 1
|
||||
- size-cells: should be 0
|
||||
- phy-cells: from the generic PHY bindings, must be 1
|
||||
|
16
Documentation/devicetree/bindings/phy/berlin-usb-phy.txt
Normal file
16
Documentation/devicetree/bindings/phy/berlin-usb-phy.txt
Normal file
@ -0,0 +1,16 @@
|
||||
* Marvell Berlin USB PHY
|
||||
|
||||
Required properties:
|
||||
- compatible: "marvell,berlin2-usb-phy" or "marvell,berlin2cd-usb-phy"
|
||||
- reg: base address and length of the registers
|
||||
- #phys-cells: should be 0
|
||||
- resets: reference to the reset controller
|
||||
|
||||
Example:
|
||||
|
||||
usb-phy@f774000 {
|
||||
compatible = "marvell,berlin2-usb-phy";
|
||||
reg = <0xf774000 0x128>;
|
||||
#phy-cells = <0>;
|
||||
resets = <&chip 0x104 14>;
|
||||
};
|
128
Documentation/devicetree/bindings/phy/phy-miphy28lp.txt
Normal file
128
Documentation/devicetree/bindings/phy/phy-miphy28lp.txt
Normal file
@ -0,0 +1,128 @@
|
||||
STMicroelectronics STi MIPHY28LP PHY binding
|
||||
============================================
|
||||
|
||||
This binding describes a miphy device that is used to control PHY hardware
|
||||
for SATA, PCIe or USB3.
|
||||
|
||||
Required properties (controller (parent) node):
|
||||
- compatible : Should be "st,miphy28lp-phy".
|
||||
- st,syscfg : Should be a phandle of the system configuration register group
|
||||
which contain the SATA, PCIe or USB3 mode setting bits.
|
||||
|
||||
Required nodes : A sub-node is required for each channel the controller
|
||||
provides. Address range information including the usual
|
||||
'reg' and 'reg-names' properties are used inside these
|
||||
nodes to describe the controller's topology. These nodes
|
||||
are translated by the driver's .xlate() function.
|
||||
|
||||
Required properties (port (child) node):
|
||||
- #phy-cells : Should be 1 (See second example)
|
||||
Cell after port phandle is device type from:
|
||||
- PHY_TYPE_SATA
|
||||
- PHY_TYPE_PCI
|
||||
- PHY_TYPE_USB3
|
||||
- reg : Address and length of the register set for the device.
|
||||
- reg-names : The names of the register addresses corresponding to the registers
|
||||
filled in "reg". It can also contain the offset of the system configuration
|
||||
registers used as glue-logic to setup the device for SATA/PCIe or USB3
|
||||
devices.
|
||||
- resets : phandle to the parent reset controller.
|
||||
- reset-names : Associated name must be "miphy-sw-rst".
|
||||
|
||||
Optional properties (port (child) node):
|
||||
- st,osc-rdy : to check the MIPHY0_OSC_RDY status in the glue-logic. This
|
||||
is not available in all the MiPHY. For example, for STiH407, only the
|
||||
MiPHY0 has this bit.
|
||||
- st,osc-force-ext : to select the external oscillator. This can change from
|
||||
different MiPHY inside the same SoC.
|
||||
- st,sata_gen : to select which SATA_SPDMODE has to be set in the SATA system config
|
||||
register.
|
||||
- st,px_rx_pol_inv : to invert polarity of RXn/RXp (respectively negative line and positive
|
||||
line).
|
||||
- st,scc-on : enable ssc to reduce effects of EMI (only for sata or PCIe).
|
||||
- st,tx-impedance-comp : to compensate tx impedance avoiding out of range values.
|
||||
|
||||
example:
|
||||
|
||||
miphy28lp_phy: miphy28lp@9b22000 {
|
||||
compatible = "st,miphy28lp-phy";
|
||||
st,syscfg = <&syscfg_core>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
|
||||
phy_port0: port@9b22000 {
|
||||
reg = <0x9b22000 0xff>,
|
||||
<0x9b09000 0xff>,
|
||||
<0x9b04000 0xff>,
|
||||
<0x114 0x4>, /* sysctrl MiPHY cntrl */
|
||||
<0x818 0x4>, /* sysctrl MiPHY status*/
|
||||
<0xe0 0x4>, /* sysctrl PCIe */
|
||||
<0xec 0x4>; /* sysctrl SATA */
|
||||
reg-names = "sata-up",
|
||||
"pcie-up",
|
||||
"pipew",
|
||||
"miphy-ctrl-glue",
|
||||
"miphy-status-glue",
|
||||
"pcie-glue",
|
||||
"sata-glue";
|
||||
#phy-cells = <1>;
|
||||
st,osc-rdy;
|
||||
reset-names = "miphy-sw-rst";
|
||||
resets = <&softreset STIH407_MIPHY0_SOFTRESET>;
|
||||
};
|
||||
|
||||
phy_port1: port@9b2a000 {
|
||||
reg = <0x9b2a000 0xff>,
|
||||
<0x9b19000 0xff>,
|
||||
<0x9b14000 0xff>,
|
||||
<0x118 0x4>,
|
||||
<0x81c 0x4>,
|
||||
<0xe4 0x4>,
|
||||
<0xf0 0x4>;
|
||||
reg-names = "sata-up",
|
||||
"pcie-up",
|
||||
"pipew",
|
||||
"miphy-ctrl-glue",
|
||||
"miphy-status-glue",
|
||||
"pcie-glue",
|
||||
"sata-glue";
|
||||
#phy-cells = <1>;
|
||||
st,osc-force-ext;
|
||||
reset-names = "miphy-sw-rst";
|
||||
resets = <&softreset STIH407_MIPHY1_SOFTRESET>;
|
||||
};
|
||||
|
||||
phy_port2: port@8f95000 {
|
||||
reg = <0x8f95000 0xff>,
|
||||
<0x8f90000 0xff>,
|
||||
<0x11c 0x4>,
|
||||
<0x820 0x4>;
|
||||
reg-names = "pipew",
|
||||
"usb3-up",
|
||||
"miphy-ctrl-glue",
|
||||
"miphy-status-glue";
|
||||
#phy-cells = <1>;
|
||||
reset-names = "miphy-sw-rst";
|
||||
resets = <&softreset STIH407_MIPHY2_SOFTRESET>;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Specifying phy control of devices
|
||||
=================================
|
||||
|
||||
Device nodes should specify the configuration required in their "phys"
|
||||
property, containing a phandle to the miphy device node and an index
|
||||
specifying which configuration to use, as described in phy-bindings.txt.
|
||||
|
||||
example:
|
||||
sata0: sata@9b20000 {
|
||||
...
|
||||
phys = <&phy_port0 PHY_TYPE_SATA>;
|
||||
...
|
||||
};
|
||||
|
||||
Macro definitions for the supported miphy configuration can be found in:
|
||||
|
||||
include/dt-bindings/phy/phy-miphy28lp.h
|
43
Documentation/devicetree/bindings/phy/phy-mvebu.txt
Normal file
43
Documentation/devicetree/bindings/phy/phy-mvebu.txt
Normal file
@ -0,0 +1,43 @@
|
||||
* Marvell MVEBU SATA PHY
|
||||
|
||||
Power control for the SATA phy found on Marvell MVEBU SoCs.
|
||||
|
||||
This document extends the binding described in phy-bindings.txt
|
||||
|
||||
Required properties :
|
||||
|
||||
- reg : Offset and length of the register set for the SATA device
|
||||
- compatible : Should be "marvell,mvebu-sata-phy"
|
||||
- clocks : phandle of clock and specifier that supplies the device
|
||||
- clock-names : Should be "sata"
|
||||
|
||||
Example:
|
||||
sata-phy@84000 {
|
||||
compatible = "marvell,mvebu-sata-phy";
|
||||
reg = <0x84000 0x0334>;
|
||||
clocks = <&gate_clk 15>;
|
||||
clock-names = "sata";
|
||||
#phy-cells = <0>;
|
||||
status = "ok";
|
||||
};
|
||||
|
||||
Armada 375 USB cluster
|
||||
----------------------
|
||||
|
||||
Armada 375 comes with an USB2 host and device controller and an USB3
|
||||
controller. The USB cluster control register allows to manage common
|
||||
features of both USB controllers.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: "marvell,armada-375-usb-cluster"
|
||||
- reg: Should contain usb cluster register location and length.
|
||||
- #phy-cells : from the generic phy bindings, must be 1. Possible
|
||||
values are 1 (USB2), 2 (USB3).
|
||||
|
||||
Example:
|
||||
usbcluster: usb-cluster@18400 {
|
||||
compatible = "marvell,armada-375-usb-cluster";
|
||||
reg = <0x18400 0x4>;
|
||||
#phy-cells = <1>
|
||||
};
|
@ -128,6 +128,7 @@ Required properties:
|
||||
- compatible : Should be set to one of the following supported values:
|
||||
- "samsung,exynos5250-usbdrd-phy" - for exynos5250 SoC,
|
||||
- "samsung,exynos5420-usbdrd-phy" - for exynos5420 SoC.
|
||||
- "samsung,exynos7-usbdrd-phy" - for exynos7 SoC.
|
||||
- reg : Register offset and length of USB DRD PHY register set;
|
||||
- clocks: Clock IDs array as required by the controller
|
||||
- clock-names: names of clocks correseponding to IDs in the clock property;
|
||||
@ -138,6 +139,11 @@ Required properties:
|
||||
PHY operations, associated by phy name. It is used to
|
||||
determine bit values for clock settings register.
|
||||
For Exynos5420 this is given as 'sclk_usbphy30' in CMU.
|
||||
- optional clocks: Exynos7 SoC has now following additional
|
||||
gate clocks available:
|
||||
- phy_pipe: for PIPE3 phy
|
||||
- phy_utmi: for UTMI+ phy
|
||||
- itp: for ITP generation
|
||||
- samsung,pmu-syscon: phandle for PMU system controller interface, used to
|
||||
control pmu registers for power isolation.
|
||||
- #phy-cells : from the generic PHY bindings, must be 1;
|
||||
|
24
Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt
Normal file
24
Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt
Normal file
@ -0,0 +1,24 @@
|
||||
* USB2 ChipIdea USB controller for ci13xxx
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "chipidea,usb2"
|
||||
- reg: base address and length of the registers
|
||||
- interrupts: interrupt for the USB controller
|
||||
|
||||
Optional properties:
|
||||
- clocks: reference to the USB clock
|
||||
- phys: reference to the USB PHY
|
||||
- phy-names: should be "usb-phy"
|
||||
- vbus-supply: reference to the VBUS regulator
|
||||
|
||||
Example:
|
||||
|
||||
usb@f7ed0000 {
|
||||
compatible = "chipidea,usb2";
|
||||
reg = <0xf7ed0000 0x10000>;
|
||||
interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&chip CLKID_USB0>;
|
||||
phys = <&usb_phy0>;
|
||||
phy-names = "usb-phy";
|
||||
vbus-supply = <®_usb0_vbus>;
|
||||
};
|
@ -14,6 +14,29 @@ Optional properties:
|
||||
- phys: from the *Generic PHY* bindings
|
||||
- phy-names: from the *Generic PHY* bindings
|
||||
- tx-fifo-resize: determines if the FIFO *has* to be reallocated.
|
||||
- snps,disable_scramble_quirk: true when SW should disable data scrambling.
|
||||
Only really useful for FPGA builds.
|
||||
- snps,has-lpm-erratum: true when DWC3 was configured with LPM Erratum enabled
|
||||
- snps,lpm-nyet-threshold: LPM NYET threshold
|
||||
- snps,u2exit_lfps_quirk: set if we want to enable u2exit lfps quirk
|
||||
- snps,u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk
|
||||
- snps,req_p1p2p3_quirk: when set, the core will always request for
|
||||
P1/P2/P3 transition sequence.
|
||||
- snps,del_p1p2p3_quirk: when set core will delay P1/P2/P3 until a certain
|
||||
amount of 8B10B errors occur.
|
||||
- snps,del_phy_power_chg_quirk: when set core will delay PHY power change
|
||||
from P0 to P1/P2/P3.
|
||||
- snps,lfps_filter_quirk: when set core will filter LFPS reception.
|
||||
- snps,rx_detect_poll_quirk: when set core will disable a 400us delay to start
|
||||
Polling LFPS after RX.Detect.
|
||||
- snps,tx_de_emphasis_quirk: when set core will set Tx de-emphasis value.
|
||||
- snps,tx_de_emphasis: the value driven to the PHY is controlled by the
|
||||
LTSSM during USB3 Compliance mode.
|
||||
- snps,dis_u3_susphy_quirk: when set core will disable USB3 suspend phy.
|
||||
- snps,dis_u2_susphy_quirk: when set core will disable USB2 suspend phy.
|
||||
- snps,is-utmi-l1-suspend: true when DWC3 asserts output signal
|
||||
utmi_l1_suspend_n, false when asserts utmi_sleep_n
|
||||
- snps,hird-threshold: HIRD threshold
|
||||
|
||||
This is usually a subnode to DWC3 glue to which it is connected.
|
||||
|
||||
|
@ -82,8 +82,10 @@ Example:
|
||||
|
||||
DWC3
|
||||
Required properties:
|
||||
- compatible: should be "samsung,exynos5250-dwusb3" for USB 3.0 DWC3
|
||||
controller.
|
||||
- compatible: should be one of the following -
|
||||
"samsung,exynos5250-dwusb3": for USB 3.0 DWC3 controller on
|
||||
Exynos5250/5420.
|
||||
"samsung,exynos7-dwusb3": for USB 3.0 DWC3 controller on Exynos7.
|
||||
- #address-cells, #size-cells : should be '1' if the device has sub-nodes
|
||||
with 'reg' property.
|
||||
- ranges: allows valid 1:1 translation between child's address space and
|
||||
|
@ -29,3 +29,25 @@ Example:
|
||||
marvell,port-mode = <2>; /* PMM_GLOBAL_MODE */
|
||||
};
|
||||
|
||||
UDC
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "marvell,pxa270-udc" for USB controllers
|
||||
used in device mode.
|
||||
- reg: usb device MMIO address space
|
||||
- interrupts: single interrupt generated by the UDC IP
|
||||
- clocks: input clock of the UDC IP (see clock-bindings.txt)
|
||||
|
||||
Optional properties:
|
||||
- gpios:
|
||||
- gpio activated to control the USB D+ pullup (see gpio.txt)
|
||||
|
||||
Example:
|
||||
|
||||
pxa27x_udc: udc@40600000 {
|
||||
compatible = "marvell,pxa270-udc";
|
||||
reg = <0x40600000 0x10000>;
|
||||
interrupts = <11>;
|
||||
clocks = <&pxa2xx_clks 11>;
|
||||
gpios = <&gpio 22 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
|
@ -9,6 +9,8 @@ Optional properties:
|
||||
- big-endian-regs : boolean, set this for hcds with big-endian registers
|
||||
- big-endian-desc : boolean, set this for hcds with big-endian descriptors
|
||||
- big-endian : boolean, for hcds with big-endian-regs + big-endian-desc
|
||||
- no-big-frame-no : boolean, set if frame_no lives in bits [15:0] of HCCA
|
||||
- num-ports : u32, to override the detected port count
|
||||
- clocks : a list of phandle + clock specifier pairs
|
||||
- phys : phandle + phy specifier pair
|
||||
- phy-names : "usb"
|
||||
|
@ -54,18 +54,14 @@ The PHY driver should create the PHY in order for other peripheral controllers
|
||||
to make use of it. The PHY framework provides 2 APIs to create the PHY.
|
||||
|
||||
struct phy *phy_create(struct device *dev, struct device_node *node,
|
||||
const struct phy_ops *ops,
|
||||
struct phy_init_data *init_data);
|
||||
const struct phy_ops *ops);
|
||||
struct phy *devm_phy_create(struct device *dev, struct device_node *node,
|
||||
const struct phy_ops *ops,
|
||||
struct phy_init_data *init_data);
|
||||
const struct phy_ops *ops);
|
||||
|
||||
The PHY drivers can use one of the above 2 APIs to create the PHY by passing
|
||||
the device pointer, phy ops and init_data.
|
||||
the device pointer and phy ops.
|
||||
phy_ops is a set of function pointers for performing PHY operations such as
|
||||
init, exit, power_on and power_off. *init_data* is mandatory to get a reference
|
||||
to the PHY in the case of non-dt boot. See section *Board File Initialization*
|
||||
on how init_data should be used.
|
||||
init, exit, power_on and power_off.
|
||||
|
||||
Inorder to dereference the private data (in phy_ops), the phy provider driver
|
||||
can use phy_set_drvdata() after creating the PHY and use phy_get_drvdata() in
|
||||
@ -137,42 +133,18 @@ There are exported APIs like phy_pm_runtime_get, phy_pm_runtime_get_sync,
|
||||
phy_pm_runtime_put, phy_pm_runtime_put_sync, phy_pm_runtime_allow and
|
||||
phy_pm_runtime_forbid for performing PM operations.
|
||||
|
||||
8. Board File Initialization
|
||||
8. PHY Mappings
|
||||
|
||||
Certain board file initialization is necessary in order to get a reference
|
||||
to the PHY in the case of non-dt boot.
|
||||
Say we have a single device that implements 3 PHYs that of USB, SATA and PCIe,
|
||||
then in the board file the following initialization should be done.
|
||||
In order to get reference to a PHY without help from DeviceTree, the framework
|
||||
offers lookups which can be compared to clkdev that allow clk structures to be
|
||||
bound to devices. A lookup can be made be made during runtime when a handle to
|
||||
the struct phy already exists.
|
||||
|
||||
struct phy_consumer consumers[] = {
|
||||
PHY_CONSUMER("dwc3.0", "usb"),
|
||||
PHY_CONSUMER("pcie.0", "pcie"),
|
||||
PHY_CONSUMER("sata.0", "sata"),
|
||||
};
|
||||
PHY_CONSUMER takes 2 parameters, first is the device name of the controller
|
||||
(PHY consumer) and second is the port name.
|
||||
The framework offers the following API for registering and unregistering the
|
||||
lookups.
|
||||
|
||||
struct phy_init_data init_data = {
|
||||
.consumers = consumers,
|
||||
.num_consumers = ARRAY_SIZE(consumers),
|
||||
};
|
||||
|
||||
static const struct platform_device pipe3_phy_dev = {
|
||||
.name = "pipe3-phy",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.platform_data = {
|
||||
.init_data = &init_data,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
then, while doing phy_create, the PHY driver should pass this init_data
|
||||
phy_create(dev, ops, pdata->init_data);
|
||||
|
||||
and the controller driver (phy consumer) should pass the port name along with
|
||||
the device to get a reference to the PHY
|
||||
phy_get(dev, "pcie");
|
||||
int phy_create_lookup(struct phy *phy, const char *con_id, const char *dev_id);
|
||||
void phy_remove_lookup(struct phy *phy, const char *con_id, const char *dev_id);
|
||||
|
||||
9. DeviceTree Binding
|
||||
|
||||
|
@ -74,6 +74,13 @@ static struct platform_device my_hid = {
|
||||
You can add as many HID functions as you want, only limited by
|
||||
the amount of interrupt endpoints your gadget driver supports.
|
||||
|
||||
Configuration with configfs
|
||||
|
||||
Instead of adding fake platform devices and drivers in order to pass
|
||||
some data to the kernel, if HID is a part of a gadget composed with
|
||||
configfs the hidg_func_descriptor.report_desc is passed to the kernel
|
||||
by writing the appropriate stream of bytes to a configfs attribute.
|
||||
|
||||
Send and receive HID reports
|
||||
|
||||
HID reports can be sent/received using read/write on the
|
||||
|
@ -145,7 +145,7 @@ Keyspan PDA Serial Adapter
|
||||
Single port DB-9 serial adapter, pushed as a PDA adapter for iMacs (mostly
|
||||
sold in Macintosh catalogs, comes in a translucent white/green dongle).
|
||||
Fairly simple device. Firmware is homebrew.
|
||||
This driver also works for the Xircom/Entrgra single port serial adapter.
|
||||
This driver also works for the Xircom/Entrega single port serial adapter.
|
||||
|
||||
Current status:
|
||||
Things that work:
|
||||
|
@ -843,6 +843,8 @@
|
||||
maximum-speed = "high-speed";
|
||||
dr_mode = "otg";
|
||||
status = "disabled";
|
||||
snps,dis_u3_susphy_quirk;
|
||||
snps,dis_u2_susphy_quirk;
|
||||
};
|
||||
};
|
||||
|
||||
@ -865,6 +867,8 @@
|
||||
maximum-speed = "high-speed";
|
||||
dr_mode = "otg";
|
||||
status = "disabled";
|
||||
snps,dis_u3_susphy_quirk;
|
||||
snps,dis_u2_susphy_quirk;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -91,18 +91,8 @@ void __init omap_pmic_late_init(void)
|
||||
}
|
||||
|
||||
#if defined(CONFIG_ARCH_OMAP3)
|
||||
struct phy_consumer consumers[] = {
|
||||
PHY_CONSUMER("musb-hdrc.0", "usb"),
|
||||
};
|
||||
|
||||
struct phy_init_data init_data = {
|
||||
.consumers = consumers,
|
||||
.num_consumers = ARRAY_SIZE(consumers),
|
||||
};
|
||||
|
||||
static struct twl4030_usb_data omap3_usb_pdata = {
|
||||
.usb_mode = T2_USB_MODE_ULPI,
|
||||
.init_data = &init_data,
|
||||
.usb_mode = T2_USB_MODE_ULPI,
|
||||
};
|
||||
|
||||
static int omap3_batt_table[] = {
|
||||
|
@ -7,22 +7,27 @@
|
||||
* Copyright (C) 2008 Wind River Systems
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_fdt.h>
|
||||
#include <linux/libfdt.h>
|
||||
#include <linux/usb/ehci_pdriver.h>
|
||||
#include <linux/usb/ohci_pdriver.h>
|
||||
|
||||
#include <asm/octeon/octeon.h>
|
||||
#include <asm/octeon/cvmx-rnm-defs.h>
|
||||
#include <asm/octeon/cvmx-helper.h>
|
||||
#include <asm/octeon/cvmx-helper-board.h>
|
||||
#include <asm/octeon/cvmx-uctlx-defs.h>
|
||||
|
||||
/* Octeon Random Number Generator. */
|
||||
static int __init octeon_rng_device_init(void)
|
||||
@ -68,6 +73,229 @@ device_initcall(octeon_rng_device_init);
|
||||
|
||||
#ifdef CONFIG_USB
|
||||
|
||||
static DEFINE_MUTEX(octeon2_usb_clocks_mutex);
|
||||
|
||||
static int octeon2_usb_clock_start_cnt;
|
||||
|
||||
static void octeon2_usb_clocks_start(void)
|
||||
{
|
||||
u64 div;
|
||||
union cvmx_uctlx_if_ena if_ena;
|
||||
union cvmx_uctlx_clk_rst_ctl clk_rst_ctl;
|
||||
union cvmx_uctlx_uphy_ctl_status uphy_ctl_status;
|
||||
union cvmx_uctlx_uphy_portx_ctl_status port_ctl_status;
|
||||
int i;
|
||||
unsigned long io_clk_64_to_ns;
|
||||
|
||||
|
||||
mutex_lock(&octeon2_usb_clocks_mutex);
|
||||
|
||||
octeon2_usb_clock_start_cnt++;
|
||||
if (octeon2_usb_clock_start_cnt != 1)
|
||||
goto exit;
|
||||
|
||||
io_clk_64_to_ns = 64000000000ull / octeon_get_io_clock_rate();
|
||||
|
||||
/*
|
||||
* Step 1: Wait for voltages stable. That surely happened
|
||||
* before starting the kernel.
|
||||
*
|
||||
* Step 2: Enable SCLK of UCTL by writing UCTL0_IF_ENA[EN] = 1
|
||||
*/
|
||||
if_ena.u64 = 0;
|
||||
if_ena.s.en = 1;
|
||||
cvmx_write_csr(CVMX_UCTLX_IF_ENA(0), if_ena.u64);
|
||||
|
||||
/* Step 3: Configure the reference clock, PHY, and HCLK */
|
||||
clk_rst_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_CLK_RST_CTL(0));
|
||||
|
||||
/*
|
||||
* If the UCTL looks like it has already been started, skip
|
||||
* the initialization, otherwise bus errors are obtained.
|
||||
*/
|
||||
if (clk_rst_ctl.s.hrst)
|
||||
goto end_clock;
|
||||
/* 3a */
|
||||
clk_rst_ctl.s.p_por = 1;
|
||||
clk_rst_ctl.s.hrst = 0;
|
||||
clk_rst_ctl.s.p_prst = 0;
|
||||
clk_rst_ctl.s.h_clkdiv_rst = 0;
|
||||
clk_rst_ctl.s.o_clkdiv_rst = 0;
|
||||
clk_rst_ctl.s.h_clkdiv_en = 0;
|
||||
clk_rst_ctl.s.o_clkdiv_en = 0;
|
||||
cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64);
|
||||
|
||||
/* 3b */
|
||||
/* 12MHz crystal. */
|
||||
clk_rst_ctl.s.p_refclk_sel = 0;
|
||||
clk_rst_ctl.s.p_refclk_div = 0;
|
||||
cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64);
|
||||
|
||||
/* 3c */
|
||||
div = octeon_get_io_clock_rate() / 130000000ull;
|
||||
|
||||
switch (div) {
|
||||
case 0:
|
||||
div = 1;
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
break;
|
||||
case 5:
|
||||
div = 4;
|
||||
break;
|
||||
case 6:
|
||||
case 7:
|
||||
div = 6;
|
||||
break;
|
||||
case 8:
|
||||
case 9:
|
||||
case 10:
|
||||
case 11:
|
||||
div = 8;
|
||||
break;
|
||||
default:
|
||||
div = 12;
|
||||
break;
|
||||
}
|
||||
clk_rst_ctl.s.h_div = div;
|
||||
cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64);
|
||||
/* Read it back, */
|
||||
clk_rst_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_CLK_RST_CTL(0));
|
||||
clk_rst_ctl.s.h_clkdiv_en = 1;
|
||||
cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64);
|
||||
/* 3d */
|
||||
clk_rst_ctl.s.h_clkdiv_rst = 1;
|
||||
cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64);
|
||||
|
||||
/* 3e: delay 64 io clocks */
|
||||
ndelay(io_clk_64_to_ns);
|
||||
|
||||
/*
|
||||
* Step 4: Program the power-on reset field in the UCTL
|
||||
* clock-reset-control register.
|
||||
*/
|
||||
clk_rst_ctl.s.p_por = 0;
|
||||
cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64);
|
||||
|
||||
/* Step 5: Wait 1 ms for the PHY clock to start. */
|
||||
mdelay(1);
|
||||
|
||||
/*
|
||||
* Step 6: Program the reset input from automatic test
|
||||
* equipment field in the UPHY CSR
|
||||
*/
|
||||
uphy_ctl_status.u64 = cvmx_read_csr(CVMX_UCTLX_UPHY_CTL_STATUS(0));
|
||||
uphy_ctl_status.s.ate_reset = 1;
|
||||
cvmx_write_csr(CVMX_UCTLX_UPHY_CTL_STATUS(0), uphy_ctl_status.u64);
|
||||
|
||||
/* Step 7: Wait for at least 10ns. */
|
||||
ndelay(10);
|
||||
|
||||
/* Step 8: Clear the ATE_RESET field in the UPHY CSR. */
|
||||
uphy_ctl_status.s.ate_reset = 0;
|
||||
cvmx_write_csr(CVMX_UCTLX_UPHY_CTL_STATUS(0), uphy_ctl_status.u64);
|
||||
|
||||
/*
|
||||
* Step 9: Wait for at least 20ns for UPHY to output PHY clock
|
||||
* signals and OHCI_CLK48
|
||||
*/
|
||||
ndelay(20);
|
||||
|
||||
/* Step 10: Configure the OHCI_CLK48 and OHCI_CLK12 clocks. */
|
||||
/* 10a */
|
||||
clk_rst_ctl.s.o_clkdiv_rst = 1;
|
||||
cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64);
|
||||
|
||||
/* 10b */
|
||||
clk_rst_ctl.s.o_clkdiv_en = 1;
|
||||
cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64);
|
||||
|
||||
/* 10c */
|
||||
ndelay(io_clk_64_to_ns);
|
||||
|
||||
/*
|
||||
* Step 11: Program the PHY reset field:
|
||||
* UCTL0_CLK_RST_CTL[P_PRST] = 1
|
||||
*/
|
||||
clk_rst_ctl.s.p_prst = 1;
|
||||
cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64);
|
||||
|
||||
/* Step 12: Wait 1 uS. */
|
||||
udelay(1);
|
||||
|
||||
/* Step 13: Program the HRESET_N field: UCTL0_CLK_RST_CTL[HRST] = 1 */
|
||||
clk_rst_ctl.s.hrst = 1;
|
||||
cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64);
|
||||
|
||||
end_clock:
|
||||
/* Now we can set some other registers. */
|
||||
|
||||
for (i = 0; i <= 1; i++) {
|
||||
port_ctl_status.u64 =
|
||||
cvmx_read_csr(CVMX_UCTLX_UPHY_PORTX_CTL_STATUS(i, 0));
|
||||
/* Set txvreftune to 15 to obtain compliant 'eye' diagram. */
|
||||
port_ctl_status.s.txvreftune = 15;
|
||||
port_ctl_status.s.txrisetune = 1;
|
||||
port_ctl_status.s.txpreemphasistune = 1;
|
||||
cvmx_write_csr(CVMX_UCTLX_UPHY_PORTX_CTL_STATUS(i, 0),
|
||||
port_ctl_status.u64);
|
||||
}
|
||||
|
||||
/* Set uSOF cycle period to 60,000 bits. */
|
||||
cvmx_write_csr(CVMX_UCTLX_EHCI_FLA(0), 0x20ull);
|
||||
exit:
|
||||
mutex_unlock(&octeon2_usb_clocks_mutex);
|
||||
}
|
||||
|
||||
static void octeon2_usb_clocks_stop(void)
|
||||
{
|
||||
mutex_lock(&octeon2_usb_clocks_mutex);
|
||||
octeon2_usb_clock_start_cnt--;
|
||||
mutex_unlock(&octeon2_usb_clocks_mutex);
|
||||
}
|
||||
|
||||
static int octeon_ehci_power_on(struct platform_device *pdev)
|
||||
{
|
||||
octeon2_usb_clocks_start();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void octeon_ehci_power_off(struct platform_device *pdev)
|
||||
{
|
||||
octeon2_usb_clocks_stop();
|
||||
}
|
||||
|
||||
static struct usb_ehci_pdata octeon_ehci_pdata = {
|
||||
/* Octeon EHCI matches CPU endianness. */
|
||||
#ifdef __BIG_ENDIAN
|
||||
.big_endian_mmio = 1,
|
||||
#endif
|
||||
.power_on = octeon_ehci_power_on,
|
||||
.power_off = octeon_ehci_power_off,
|
||||
};
|
||||
|
||||
static void __init octeon_ehci_hw_start(void)
|
||||
{
|
||||
union cvmx_uctlx_ehci_ctl ehci_ctl;
|
||||
|
||||
octeon2_usb_clocks_start();
|
||||
|
||||
ehci_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_EHCI_CTL(0));
|
||||
/* Use 64-bit addressing. */
|
||||
ehci_ctl.s.ehci_64b_addr_en = 1;
|
||||
ehci_ctl.s.l2c_addr_msb = 0;
|
||||
ehci_ctl.s.l2c_buff_emod = 1; /* Byte swapped. */
|
||||
ehci_ctl.s.l2c_desc_emod = 1; /* Byte swapped. */
|
||||
cvmx_write_csr(CVMX_UCTLX_EHCI_CTL(0), ehci_ctl.u64);
|
||||
|
||||
octeon2_usb_clocks_stop();
|
||||
}
|
||||
|
||||
static u64 octeon_ehci_dma_mask = DMA_BIT_MASK(64);
|
||||
|
||||
static int __init octeon_ehci_device_init(void)
|
||||
{
|
||||
struct platform_device *pd;
|
||||
@ -88,7 +316,7 @@ static int __init octeon_ehci_device_init(void)
|
||||
if (octeon_is_simulation() || usb_disabled())
|
||||
return 0; /* No USB in the simulator. */
|
||||
|
||||
pd = platform_device_alloc("octeon-ehci", 0);
|
||||
pd = platform_device_alloc("ehci-platform", 0);
|
||||
if (!pd) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
@ -105,6 +333,10 @@ static int __init octeon_ehci_device_init(void)
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
pd->dev.dma_mask = &octeon_ehci_dma_mask;
|
||||
pd->dev.platform_data = &octeon_ehci_pdata;
|
||||
octeon_ehci_hw_start();
|
||||
|
||||
ret = platform_device_add(pd);
|
||||
if (ret)
|
||||
goto fail;
|
||||
@ -117,6 +349,41 @@ out:
|
||||
}
|
||||
device_initcall(octeon_ehci_device_init);
|
||||
|
||||
static int octeon_ohci_power_on(struct platform_device *pdev)
|
||||
{
|
||||
octeon2_usb_clocks_start();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void octeon_ohci_power_off(struct platform_device *pdev)
|
||||
{
|
||||
octeon2_usb_clocks_stop();
|
||||
}
|
||||
|
||||
static struct usb_ohci_pdata octeon_ohci_pdata = {
|
||||
/* Octeon OHCI matches CPU endianness. */
|
||||
#ifdef __BIG_ENDIAN
|
||||
.big_endian_mmio = 1,
|
||||
#endif
|
||||
.power_on = octeon_ohci_power_on,
|
||||
.power_off = octeon_ohci_power_off,
|
||||
};
|
||||
|
||||
static void __init octeon_ohci_hw_start(void)
|
||||
{
|
||||
union cvmx_uctlx_ohci_ctl ohci_ctl;
|
||||
|
||||
octeon2_usb_clocks_start();
|
||||
|
||||
ohci_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_OHCI_CTL(0));
|
||||
ohci_ctl.s.l2c_addr_msb = 0;
|
||||
ohci_ctl.s.l2c_buff_emod = 1; /* Byte swapped. */
|
||||
ohci_ctl.s.l2c_desc_emod = 1; /* Byte swapped. */
|
||||
cvmx_write_csr(CVMX_UCTLX_OHCI_CTL(0), ohci_ctl.u64);
|
||||
|
||||
octeon2_usb_clocks_stop();
|
||||
}
|
||||
|
||||
static int __init octeon_ohci_device_init(void)
|
||||
{
|
||||
struct platform_device *pd;
|
||||
@ -137,7 +404,7 @@ static int __init octeon_ohci_device_init(void)
|
||||
if (octeon_is_simulation() || usb_disabled())
|
||||
return 0; /* No USB in the simulator. */
|
||||
|
||||
pd = platform_device_alloc("octeon-ohci", 0);
|
||||
pd = platform_device_alloc("ohci-platform", 0);
|
||||
if (!pd) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
@ -154,6 +421,9 @@ static int __init octeon_ohci_device_init(void)
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
pd->dev.platform_data = &octeon_ohci_pdata;
|
||||
octeon_ohci_hw_start();
|
||||
|
||||
ret = platform_device_add(pd);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
@ -120,6 +120,9 @@ CONFIG_SPI_OCTEON=y
|
||||
# CONFIG_HWMON is not set
|
||||
CONFIG_WATCHDOG=y
|
||||
# CONFIG_USB_SUPPORT is not set
|
||||
CONFIG_USB_EHCI_BIG_ENDIAN_MMIO=y
|
||||
CONFIG_USB_OHCI_BIG_ENDIAN_MMIO=y
|
||||
CONFIG_USB_OHCI_LITTLE_ENDIAN=y
|
||||
CONFIG_RTC_CLASS=y
|
||||
CONFIG_RTC_DRV_DS1307=y
|
||||
CONFIG_STAGING=y
|
||||
|
@ -378,6 +378,26 @@ static void quirk_ati_exploding_mce(struct pci_dev *dev)
|
||||
}
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RS100, quirk_ati_exploding_mce);
|
||||
|
||||
/*
|
||||
* In the AMD NL platform, this device ([1022:7912]) has a class code of
|
||||
* PCI_CLASS_SERIAL_USB_XHCI (0x0c0330), which means the xhci driver will
|
||||
* claim it.
|
||||
* But the dwc3 driver is a more specific driver for this device, and we'd
|
||||
* prefer to use it instead of xhci. To prevent xhci from claiming the
|
||||
* device, change the class code to 0x0c03fe, which the PCI r3.0 spec
|
||||
* defines as "USB device (not host controller)". The dwc3 driver can then
|
||||
* claim it based on its Vendor and Device ID.
|
||||
*/
|
||||
static void quirk_amd_nl_class(struct pci_dev *pdev)
|
||||
{
|
||||
/*
|
||||
* Use 'USB Device' (0x0c03fe) instead of PCI header provided
|
||||
*/
|
||||
pdev->class = 0x0c03fe;
|
||||
}
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_NL_USB,
|
||||
quirk_amd_nl_class);
|
||||
|
||||
/*
|
||||
* Let's make the southbridge information explicit instead
|
||||
* of having to worry about people probing the ACPI areas,
|
||||
|
@ -15,6 +15,13 @@ config GENERIC_PHY
|
||||
phy users can obtain reference to the PHY. All the users of this
|
||||
framework should select this config.
|
||||
|
||||
config PHY_BERLIN_USB
|
||||
tristate "Marvell Berlin USB PHY Driver"
|
||||
depends on ARCH_BERLIN && RESET_CONTROLLER && HAS_IOMEM && OF
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Enable this to support the USB PHY on Marvell Berlin SoCs.
|
||||
|
||||
config PHY_BERLIN_SATA
|
||||
tristate "Marvell Berlin SATA PHY driver"
|
||||
depends on ARCH_BERLIN && HAS_IOMEM && OF
|
||||
@ -22,6 +29,12 @@ config PHY_BERLIN_SATA
|
||||
help
|
||||
Enable this to support the SATA PHY on Marvell Berlin SoCs.
|
||||
|
||||
config ARMADA375_USBCLUSTER_PHY
|
||||
def_bool y
|
||||
depends on MACH_ARMADA_375 || COMPILE_TEST
|
||||
depends on OF
|
||||
select GENERIC_PHY
|
||||
|
||||
config PHY_EXYNOS_MIPI_VIDEO
|
||||
tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver"
|
||||
depends on HAS_IOMEM
|
||||
@ -38,6 +51,14 @@ config PHY_MVEBU_SATA
|
||||
depends on OF
|
||||
select GENERIC_PHY
|
||||
|
||||
config PHY_MIPHY28LP
|
||||
tristate "STMicroelectronics MIPHY28LP PHY driver for STiH407"
|
||||
depends on ARCH_STI
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Enable this to support the miphy transceiver (for SATA/PCIE/USB3)
|
||||
that is part of STMicroelectronics STiH407 SoC.
|
||||
|
||||
config PHY_MIPHY365X
|
||||
tristate "STMicroelectronics MIPHY365X PHY driver for STiH41x series"
|
||||
depends on ARCH_STI
|
||||
@ -193,7 +214,7 @@ config PHY_EXYNOS5250_USB2
|
||||
|
||||
config PHY_EXYNOS5_USBDRD
|
||||
tristate "Exynos5 SoC series USB DRD PHY driver"
|
||||
depends on ARCH_EXYNOS5 && OF
|
||||
depends on ARCH_EXYNOS && OF
|
||||
depends on HAS_IOMEM
|
||||
depends on USB_DWC3_EXYNOS
|
||||
select GENERIC_PHY
|
||||
|
@ -3,11 +3,14 @@
|
||||
#
|
||||
|
||||
obj-$(CONFIG_GENERIC_PHY) += phy-core.o
|
||||
obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o
|
||||
obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o
|
||||
obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o
|
||||
obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o
|
||||
obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o
|
||||
obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
|
||||
obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
|
||||
obj-$(CONFIG_PHY_MIPHY28LP) += phy-miphy28lp.o
|
||||
obj-$(CONFIG_PHY_MIPHY365X) += phy-miphy365x.o
|
||||
obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o
|
||||
obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o
|
||||
|
158
drivers/phy/phy-armada375-usb2.c
Normal file
158
drivers/phy/phy-armada375-usb2.c
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* USB cluster support for Armada 375 platform.
|
||||
*
|
||||
* Copyright (C) 2014 Marvell
|
||||
*
|
||||
* Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2 or later. This program is licensed "as is"
|
||||
* without any warranty of any kind, whether express or implied.
|
||||
*
|
||||
* Armada 375 comes with an USB2 host and device controller and an
|
||||
* USB3 controller. The USB cluster control register allows to manage
|
||||
* common features of both USB controllers.
|
||||
*/
|
||||
|
||||
#include <dt-bindings/phy/phy.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define USB2_PHY_CONFIG_DISABLE BIT(0)
|
||||
|
||||
struct armada375_cluster_phy {
|
||||
struct phy *phy;
|
||||
void __iomem *reg;
|
||||
bool use_usb3;
|
||||
int phy_provided;
|
||||
};
|
||||
|
||||
static int armada375_usb_phy_init(struct phy *phy)
|
||||
{
|
||||
struct armada375_cluster_phy *cluster_phy;
|
||||
u32 reg;
|
||||
|
||||
cluster_phy = dev_get_drvdata(phy->dev.parent);
|
||||
if (!cluster_phy)
|
||||
return -ENODEV;
|
||||
|
||||
reg = readl(cluster_phy->reg);
|
||||
if (cluster_phy->use_usb3)
|
||||
reg |= USB2_PHY_CONFIG_DISABLE;
|
||||
else
|
||||
reg &= ~USB2_PHY_CONFIG_DISABLE;
|
||||
writel(reg, cluster_phy->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_ops armada375_usb_phy_ops = {
|
||||
.init = armada375_usb_phy_init,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
/*
|
||||
* Only one controller can use this PHY. We shouldn't have the case
|
||||
* when two controllers want to use this PHY. But if this case occurs
|
||||
* then we provide a phy to the first one and return an error for the
|
||||
* next one. This error has also to be an error returned by
|
||||
* devm_phy_optional_get() so different from ENODEV for USB2. In the
|
||||
* USB3 case it still optional and we use ENODEV.
|
||||
*/
|
||||
static struct phy *armada375_usb_phy_xlate(struct device *dev,
|
||||
struct of_phandle_args *args)
|
||||
{
|
||||
struct armada375_cluster_phy *cluster_phy = dev_get_drvdata(dev);
|
||||
|
||||
if (!cluster_phy)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
/*
|
||||
* Either the phy had never been requested and then the first
|
||||
* usb claiming it can get it, or it had already been
|
||||
* requested in this case, we only allow to use it with the
|
||||
* same configuration.
|
||||
*/
|
||||
if (WARN_ON((cluster_phy->phy_provided != PHY_NONE) &&
|
||||
(cluster_phy->phy_provided != args->args[0]))) {
|
||||
dev_err(dev, "This PHY has already been provided!\n");
|
||||
dev_err(dev, "Check your device tree, only one controller can use it\n.");
|
||||
if (args->args[0] == PHY_TYPE_USB2)
|
||||
return ERR_PTR(-EBUSY);
|
||||
else
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
if (args->args[0] == PHY_TYPE_USB2)
|
||||
cluster_phy->use_usb3 = false;
|
||||
else if (args->args[0] == PHY_TYPE_USB3)
|
||||
cluster_phy->use_usb3 = true;
|
||||
else {
|
||||
dev_err(dev, "Invalid PHY mode\n");
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
/* Store which phy mode is used for next test */
|
||||
cluster_phy->phy_provided = args->args[0];
|
||||
|
||||
return cluster_phy->phy;
|
||||
}
|
||||
|
||||
static int armada375_usb_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct phy *phy;
|
||||
struct phy_provider *phy_provider;
|
||||
void __iomem *usb_cluster_base;
|
||||
struct resource *res;
|
||||
struct armada375_cluster_phy *cluster_phy;
|
||||
|
||||
cluster_phy = devm_kzalloc(dev, sizeof(*cluster_phy), GFP_KERNEL);
|
||||
if (!cluster_phy)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
usb_cluster_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (!usb_cluster_base)
|
||||
return -ENOMEM;
|
||||
|
||||
phy = devm_phy_create(dev, NULL, &armada375_usb_phy_ops);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(dev, "failed to create PHY\n");
|
||||
return PTR_ERR(phy);
|
||||
}
|
||||
|
||||
cluster_phy->phy = phy;
|
||||
cluster_phy->reg = usb_cluster_base;
|
||||
|
||||
dev_set_drvdata(dev, cluster_phy);
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(&pdev->dev,
|
||||
armada375_usb_phy_xlate);
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id of_usb_cluster_table[] = {
|
||||
{ .compatible = "marvell,armada-375-usb-cluster", },
|
||||
{ /* end of list */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_usb_cluster_table);
|
||||
|
||||
static struct platform_driver armada375_usb_phy_driver = {
|
||||
.probe = armada375_usb_phy_probe,
|
||||
.driver = {
|
||||
.of_match_table = of_usb_cluster_table,
|
||||
.name = "armada-375-usb-cluster",
|
||||
.owner = THIS_MODULE,
|
||||
}
|
||||
};
|
||||
module_platform_driver(armada375_usb_phy_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Armada 375 USB cluster driver");
|
||||
MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>");
|
||||
MODULE_LICENSE("GPL");
|
@ -117,7 +117,7 @@ static int bcm_kona_usb2_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, phy);
|
||||
|
||||
gphy = devm_phy_create(dev, NULL, &ops, NULL);
|
||||
gphy = devm_phy_create(dev, NULL, &ops);
|
||||
if (IS_ERR(gphy))
|
||||
return PTR_ERR(gphy);
|
||||
|
||||
|
@ -30,7 +30,8 @@
|
||||
#define MBUS_WRITE_REQUEST_SIZE_128 (BIT(2) << 16)
|
||||
#define MBUS_READ_REQUEST_SIZE_128 (BIT(2) << 19)
|
||||
|
||||
#define PHY_BASE 0x200
|
||||
#define BG2_PHY_BASE 0x080
|
||||
#define BG2Q_PHY_BASE 0x200
|
||||
|
||||
/* register 0x01 */
|
||||
#define REF_FREF_SEL_25 BIT(0)
|
||||
@ -61,15 +62,16 @@ struct phy_berlin_priv {
|
||||
struct clk *clk;
|
||||
struct phy_berlin_desc **phys;
|
||||
unsigned nphys;
|
||||
u32 phy_base;
|
||||
};
|
||||
|
||||
static inline void phy_berlin_sata_reg_setbits(void __iomem *ctrl_reg, u32 reg,
|
||||
u32 mask, u32 val)
|
||||
static inline void phy_berlin_sata_reg_setbits(void __iomem *ctrl_reg,
|
||||
u32 phy_base, u32 reg, u32 mask, u32 val)
|
||||
{
|
||||
u32 regval;
|
||||
|
||||
/* select register */
|
||||
writel(PHY_BASE + reg, ctrl_reg + PORT_VSR_ADDR);
|
||||
writel(phy_base + reg, ctrl_reg + PORT_VSR_ADDR);
|
||||
|
||||
/* set bits */
|
||||
regval = readl(ctrl_reg + PORT_VSR_DATA);
|
||||
@ -103,17 +105,20 @@ static int phy_berlin_sata_power_on(struct phy *phy)
|
||||
writel(regval, priv->base + HOST_VSA_DATA);
|
||||
|
||||
/* set PHY mode and ref freq to 25 MHz */
|
||||
phy_berlin_sata_reg_setbits(ctrl_reg, 0x1, 0xff,
|
||||
REF_FREF_SEL_25 | PHY_MODE_SATA);
|
||||
phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x01,
|
||||
0x00ff, REF_FREF_SEL_25 | PHY_MODE_SATA);
|
||||
|
||||
/* set PHY up to 6 Gbps */
|
||||
phy_berlin_sata_reg_setbits(ctrl_reg, 0x25, 0xc00, PHY_GEN_MAX_6_0);
|
||||
phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x25,
|
||||
0x0c00, PHY_GEN_MAX_6_0);
|
||||
|
||||
/* set 40 bits width */
|
||||
phy_berlin_sata_reg_setbits(ctrl_reg, 0x23, 0xc00, DATA_BIT_WIDTH_40);
|
||||
phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x23,
|
||||
0x0c00, DATA_BIT_WIDTH_40);
|
||||
|
||||
/* use max pll rate */
|
||||
phy_berlin_sata_reg_setbits(ctrl_reg, 0x2, 0x0, USE_MAX_PLL_RATE);
|
||||
phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x02,
|
||||
0x0000, USE_MAX_PLL_RATE);
|
||||
|
||||
/* set Gen3 controller speed */
|
||||
regval = readl(ctrl_reg + PORT_SCR_CTL);
|
||||
@ -218,6 +223,11 @@ static int phy_berlin_sata_probe(struct platform_device *pdev)
|
||||
if (!priv->phys)
|
||||
return -ENOMEM;
|
||||
|
||||
if (of_device_is_compatible(dev->of_node, "marvell,berlin2-sata-phy"))
|
||||
priv->phy_base = BG2_PHY_BASE;
|
||||
else
|
||||
priv->phy_base = BG2Q_PHY_BASE;
|
||||
|
||||
dev_set_drvdata(dev, priv);
|
||||
spin_lock_init(&priv->lock);
|
||||
|
||||
@ -239,7 +249,7 @@ static int phy_berlin_sata_probe(struct platform_device *pdev)
|
||||
if (!phy_desc)
|
||||
return -ENOMEM;
|
||||
|
||||
phy = devm_phy_create(dev, NULL, &phy_berlin_sata_ops, NULL);
|
||||
phy = devm_phy_create(dev, NULL, &phy_berlin_sata_ops);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(dev, "failed to create PHY %d\n", phy_id);
|
||||
return PTR_ERR(phy);
|
||||
@ -258,13 +268,11 @@ static int phy_berlin_sata_probe(struct platform_device *pdev)
|
||||
|
||||
phy_provider =
|
||||
devm_of_phy_provider_register(dev, phy_berlin_sata_phy_xlate);
|
||||
if (IS_ERR(phy_provider))
|
||||
return PTR_ERR(phy_provider);
|
||||
|
||||
return 0;
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id phy_berlin_sata_of_match[] = {
|
||||
{ .compatible = "marvell,berlin2-sata-phy" },
|
||||
{ .compatible = "marvell,berlin2q-sata-phy" },
|
||||
{ },
|
||||
};
|
||||
|
223
drivers/phy/phy-berlin-usb.c
Normal file
223
drivers/phy/phy-berlin-usb.c
Normal file
@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Marvell Technology Group Ltd.
|
||||
*
|
||||
* Antoine Tenart <antoine.tenart@free-electrons.com>
|
||||
* Jisheng Zhang <jszhang@marvell.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#define USB_PHY_PLL 0x04
|
||||
#define USB_PHY_PLL_CONTROL 0x08
|
||||
#define USB_PHY_TX_CTRL0 0x10
|
||||
#define USB_PHY_TX_CTRL1 0x14
|
||||
#define USB_PHY_TX_CTRL2 0x18
|
||||
#define USB_PHY_RX_CTRL 0x20
|
||||
#define USB_PHY_ANALOG 0x34
|
||||
|
||||
/* USB_PHY_PLL */
|
||||
#define CLK_REF_DIV(x) ((x) << 4)
|
||||
#define FEEDBACK_CLK_DIV(x) ((x) << 8)
|
||||
|
||||
/* USB_PHY_PLL_CONTROL */
|
||||
#define CLK_STABLE BIT(0)
|
||||
#define PLL_CTRL_PIN BIT(1)
|
||||
#define PLL_CTRL_REG BIT(2)
|
||||
#define PLL_ON BIT(3)
|
||||
#define PHASE_OFF_TOL_125 (0x0 << 5)
|
||||
#define PHASE_OFF_TOL_250 BIT(5)
|
||||
#define KVC0_CALIB (0x0 << 9)
|
||||
#define KVC0_REG_CTRL BIT(9)
|
||||
#define KVC0_HIGH (0x0 << 10)
|
||||
#define KVC0_LOW (0x3 << 10)
|
||||
#define CLK_BLK_EN BIT(13)
|
||||
|
||||
/* USB_PHY_TX_CTRL0 */
|
||||
#define EXT_HS_RCAL_EN BIT(3)
|
||||
#define EXT_FS_RCAL_EN BIT(4)
|
||||
#define IMPCAL_VTH_DIV(x) ((x) << 5)
|
||||
#define EXT_RS_RCAL_DIV(x) ((x) << 8)
|
||||
#define EXT_FS_RCAL_DIV(x) ((x) << 12)
|
||||
|
||||
/* USB_PHY_TX_CTRL1 */
|
||||
#define TX_VDD15_14 (0x0 << 4)
|
||||
#define TX_VDD15_15 BIT(4)
|
||||
#define TX_VDD15_16 (0x2 << 4)
|
||||
#define TX_VDD15_17 (0x3 << 4)
|
||||
#define TX_VDD12_VDD (0x0 << 6)
|
||||
#define TX_VDD12_11 BIT(6)
|
||||
#define TX_VDD12_12 (0x2 << 6)
|
||||
#define TX_VDD12_13 (0x3 << 6)
|
||||
#define LOW_VDD_EN BIT(8)
|
||||
#define TX_OUT_AMP(x) ((x) << 9)
|
||||
|
||||
/* USB_PHY_TX_CTRL2 */
|
||||
#define TX_CHAN_CTRL_REG(x) ((x) << 0)
|
||||
#define DRV_SLEWRATE(x) ((x) << 4)
|
||||
#define IMP_CAL_FS_HS_DLY_0 (0x0 << 6)
|
||||
#define IMP_CAL_FS_HS_DLY_1 BIT(6)
|
||||
#define IMP_CAL_FS_HS_DLY_2 (0x2 << 6)
|
||||
#define IMP_CAL_FS_HS_DLY_3 (0x3 << 6)
|
||||
#define FS_DRV_EN_MASK(x) ((x) << 8)
|
||||
#define HS_DRV_EN_MASK(x) ((x) << 12)
|
||||
|
||||
/* USB_PHY_RX_CTRL */
|
||||
#define PHASE_FREEZE_DLY_2_CL (0x0 << 0)
|
||||
#define PHASE_FREEZE_DLY_4_CL BIT(0)
|
||||
#define ACK_LENGTH_8_CL (0x0 << 2)
|
||||
#define ACK_LENGTH_12_CL BIT(2)
|
||||
#define ACK_LENGTH_16_CL (0x2 << 2)
|
||||
#define ACK_LENGTH_20_CL (0x3 << 2)
|
||||
#define SQ_LENGTH_3 (0x0 << 4)
|
||||
#define SQ_LENGTH_6 BIT(4)
|
||||
#define SQ_LENGTH_9 (0x2 << 4)
|
||||
#define SQ_LENGTH_12 (0x3 << 4)
|
||||
#define DISCON_THRESHOLD_260 (0x0 << 6)
|
||||
#define DISCON_THRESHOLD_270 BIT(6)
|
||||
#define DISCON_THRESHOLD_280 (0x2 << 6)
|
||||
#define DISCON_THRESHOLD_290 (0x3 << 6)
|
||||
#define SQ_THRESHOLD(x) ((x) << 8)
|
||||
#define LPF_COEF(x) ((x) << 12)
|
||||
#define INTPL_CUR_10 (0x0 << 14)
|
||||
#define INTPL_CUR_20 BIT(14)
|
||||
#define INTPL_CUR_30 (0x2 << 14)
|
||||
#define INTPL_CUR_40 (0x3 << 14)
|
||||
|
||||
/* USB_PHY_ANALOG */
|
||||
#define ANA_PWR_UP BIT(1)
|
||||
#define ANA_PWR_DOWN BIT(2)
|
||||
#define V2I_VCO_RATIO(x) ((x) << 7)
|
||||
#define R_ROTATE_90 (0x0 << 10)
|
||||
#define R_ROTATE_0 BIT(10)
|
||||
#define MODE_TEST_EN BIT(11)
|
||||
#define ANA_TEST_DC_CTRL(x) ((x) << 12)
|
||||
|
||||
#define to_phy_berlin_usb_priv(p) \
|
||||
container_of((p), struct phy_berlin_usb_priv, phy)
|
||||
|
||||
static const u32 phy_berlin_pll_dividers[] = {
|
||||
/* Berlin 2 */
|
||||
CLK_REF_DIV(0xc) | FEEDBACK_CLK_DIV(0x54),
|
||||
/* Berlin 2CD */
|
||||
CLK_REF_DIV(0x6) | FEEDBACK_CLK_DIV(0x55),
|
||||
};
|
||||
|
||||
struct phy_berlin_usb_priv {
|
||||
void __iomem *base;
|
||||
struct phy *phy;
|
||||
struct reset_control *rst_ctrl;
|
||||
u32 pll_divider;
|
||||
};
|
||||
|
||||
static int phy_berlin_usb_power_on(struct phy *phy)
|
||||
{
|
||||
struct phy_berlin_usb_priv *priv = dev_get_drvdata(phy->dev.parent);
|
||||
|
||||
reset_control_reset(priv->rst_ctrl);
|
||||
|
||||
writel(priv->pll_divider,
|
||||
priv->base + USB_PHY_PLL);
|
||||
writel(CLK_STABLE | PLL_CTRL_REG | PHASE_OFF_TOL_250 | KVC0_REG_CTRL |
|
||||
CLK_BLK_EN, priv->base + USB_PHY_PLL_CONTROL);
|
||||
writel(V2I_VCO_RATIO(0x5) | R_ROTATE_0 | ANA_TEST_DC_CTRL(0x5),
|
||||
priv->base + USB_PHY_ANALOG);
|
||||
writel(PHASE_FREEZE_DLY_4_CL | ACK_LENGTH_16_CL | SQ_LENGTH_12 |
|
||||
DISCON_THRESHOLD_260 | SQ_THRESHOLD(0xa) | LPF_COEF(0x2) |
|
||||
INTPL_CUR_30, priv->base + USB_PHY_RX_CTRL);
|
||||
|
||||
writel(TX_VDD12_13 | TX_OUT_AMP(0x3), priv->base + USB_PHY_TX_CTRL1);
|
||||
writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4),
|
||||
priv->base + USB_PHY_TX_CTRL0);
|
||||
|
||||
writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4) |
|
||||
EXT_FS_RCAL_DIV(0x2), priv->base + USB_PHY_TX_CTRL0);
|
||||
|
||||
writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4),
|
||||
priv->base + USB_PHY_TX_CTRL0);
|
||||
writel(TX_CHAN_CTRL_REG(0xf) | DRV_SLEWRATE(0x3) | IMP_CAL_FS_HS_DLY_3 |
|
||||
FS_DRV_EN_MASK(0xd), priv->base + USB_PHY_TX_CTRL2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_ops phy_berlin_usb_ops = {
|
||||
.power_on = phy_berlin_usb_power_on,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct of_device_id phy_berlin_sata_of_match[] = {
|
||||
{
|
||||
.compatible = "marvell,berlin2-usb-phy",
|
||||
.data = &phy_berlin_pll_dividers[0],
|
||||
},
|
||||
{
|
||||
.compatible = "marvell,berlin2cd-usb-phy",
|
||||
.data = &phy_berlin_pll_dividers[1],
|
||||
},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, phy_berlin_sata_of_match);
|
||||
|
||||
static int phy_berlin_usb_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *match =
|
||||
of_match_device(phy_berlin_sata_of_match, &pdev->dev);
|
||||
struct phy_berlin_usb_priv *priv;
|
||||
struct resource *res;
|
||||
struct phy_provider *phy_provider;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
priv->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(priv->base))
|
||||
return PTR_ERR(priv->base);
|
||||
|
||||
priv->rst_ctrl = devm_reset_control_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(priv->rst_ctrl))
|
||||
return PTR_ERR(priv->rst_ctrl);
|
||||
|
||||
priv->pll_divider = *((u32 *)match->data);
|
||||
|
||||
priv->phy = devm_phy_create(&pdev->dev, NULL, &phy_berlin_usb_ops);
|
||||
if (IS_ERR(priv->phy)) {
|
||||
dev_err(&pdev->dev, "failed to create PHY\n");
|
||||
return PTR_ERR(priv->phy);
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
phy_provider =
|
||||
devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
|
||||
if (IS_ERR(phy_provider))
|
||||
return PTR_ERR(phy_provider);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver phy_berlin_usb_driver = {
|
||||
.probe = phy_berlin_usb_probe,
|
||||
.driver = {
|
||||
.name = "phy-berlin-usb",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = phy_berlin_sata_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(phy_berlin_usb_driver);
|
||||
|
||||
MODULE_AUTHOR("Antoine Tenart <antoine.tenart@free-electrons.com>");
|
||||
MODULE_DESCRIPTION("Marvell Berlin PHY driver for USB");
|
||||
MODULE_LICENSE("GPL");
|
@ -26,6 +26,7 @@
|
||||
static struct class *phy_class;
|
||||
static DEFINE_MUTEX(phy_provider_mutex);
|
||||
static LIST_HEAD(phy_provider_list);
|
||||
static LIST_HEAD(phys);
|
||||
static DEFINE_IDA(phy_ida);
|
||||
|
||||
static void devm_phy_release(struct device *dev, void *res)
|
||||
@ -54,34 +55,79 @@ static int devm_phy_match(struct device *dev, void *res, void *match_data)
|
||||
return res == match_data;
|
||||
}
|
||||
|
||||
static struct phy *phy_lookup(struct device *device, const char *port)
|
||||
/**
|
||||
* phy_create_lookup() - allocate and register PHY/device association
|
||||
* @phy: the phy of the association
|
||||
* @con_id: connection ID string on device
|
||||
* @dev_id: the device of the association
|
||||
*
|
||||
* Creates and registers phy_lookup entry.
|
||||
*/
|
||||
int phy_create_lookup(struct phy *phy, const char *con_id, const char *dev_id)
|
||||
{
|
||||
unsigned int count;
|
||||
struct phy *phy;
|
||||
struct device *dev;
|
||||
struct phy_consumer *consumers;
|
||||
struct class_dev_iter iter;
|
||||
struct phy_lookup *pl;
|
||||
|
||||
class_dev_iter_init(&iter, phy_class, NULL, NULL);
|
||||
while ((dev = class_dev_iter_next(&iter))) {
|
||||
phy = to_phy(dev);
|
||||
if (!phy || !dev_id || !con_id)
|
||||
return -EINVAL;
|
||||
|
||||
if (!phy->init_data)
|
||||
continue;
|
||||
count = phy->init_data->num_consumers;
|
||||
consumers = phy->init_data->consumers;
|
||||
while (count--) {
|
||||
if (!strcmp(consumers->dev_name, dev_name(device)) &&
|
||||
!strcmp(consumers->port, port)) {
|
||||
class_dev_iter_exit(&iter);
|
||||
return phy;
|
||||
}
|
||||
consumers++;
|
||||
pl = kzalloc(sizeof(*pl), GFP_KERNEL);
|
||||
if (!pl)
|
||||
return -ENOMEM;
|
||||
|
||||
pl->dev_id = dev_id;
|
||||
pl->con_id = con_id;
|
||||
pl->phy = phy;
|
||||
|
||||
mutex_lock(&phy_provider_mutex);
|
||||
list_add_tail(&pl->node, &phys);
|
||||
mutex_unlock(&phy_provider_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phy_create_lookup);
|
||||
|
||||
/**
|
||||
* phy_remove_lookup() - find and remove PHY/device association
|
||||
* @phy: the phy of the association
|
||||
* @con_id: connection ID string on device
|
||||
* @dev_id: the device of the association
|
||||
*
|
||||
* Finds and unregisters phy_lookup entry that was created with
|
||||
* phy_create_lookup().
|
||||
*/
|
||||
void phy_remove_lookup(struct phy *phy, const char *con_id, const char *dev_id)
|
||||
{
|
||||
struct phy_lookup *pl;
|
||||
|
||||
if (!phy || !dev_id || !con_id)
|
||||
return;
|
||||
|
||||
mutex_lock(&phy_provider_mutex);
|
||||
list_for_each_entry(pl, &phys, node)
|
||||
if (pl->phy == phy && !strcmp(pl->dev_id, dev_id) &&
|
||||
!strcmp(pl->con_id, con_id)) {
|
||||
list_del(&pl->node);
|
||||
kfree(pl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&phy_provider_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phy_remove_lookup);
|
||||
|
||||
class_dev_iter_exit(&iter);
|
||||
return ERR_PTR(-ENODEV);
|
||||
static struct phy *phy_find(struct device *dev, const char *con_id)
|
||||
{
|
||||
const char *dev_id = dev_name(dev);
|
||||
struct phy_lookup *p, *pl = NULL;
|
||||
|
||||
mutex_lock(&phy_provider_mutex);
|
||||
list_for_each_entry(p, &phys, node)
|
||||
if (!strcmp(p->dev_id, dev_id) && !strcmp(p->con_id, con_id)) {
|
||||
pl = p;
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&phy_provider_mutex);
|
||||
|
||||
return pl ? pl->phy : ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
static struct phy_provider *of_phy_provider_lookup(struct device_node *node)
|
||||
@ -414,21 +460,13 @@ struct phy *of_phy_simple_xlate(struct device *dev, struct of_phandle_args
|
||||
{
|
||||
struct phy *phy;
|
||||
struct class_dev_iter iter;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct device_node *child;
|
||||
|
||||
class_dev_iter_init(&iter, phy_class, NULL, NULL);
|
||||
while ((dev = class_dev_iter_next(&iter))) {
|
||||
phy = to_phy(dev);
|
||||
if (node != phy->dev.of_node) {
|
||||
for_each_child_of_node(node, child) {
|
||||
if (child == phy->dev.of_node)
|
||||
goto phy_found;
|
||||
}
|
||||
if (args->np != phy->dev.of_node)
|
||||
continue;
|
||||
}
|
||||
|
||||
phy_found:
|
||||
class_dev_iter_exit(&iter);
|
||||
return phy;
|
||||
}
|
||||
@ -463,7 +501,7 @@ struct phy *phy_get(struct device *dev, const char *string)
|
||||
string);
|
||||
phy = _of_phy_get(dev->of_node, index);
|
||||
} else {
|
||||
phy = phy_lookup(dev, string);
|
||||
phy = phy_find(dev, string);
|
||||
}
|
||||
if (IS_ERR(phy))
|
||||
return phy;
|
||||
@ -588,13 +626,11 @@ EXPORT_SYMBOL_GPL(devm_of_phy_get);
|
||||
* @dev: device that is creating the new phy
|
||||
* @node: device node of the phy
|
||||
* @ops: function pointers for performing phy operations
|
||||
* @init_data: contains the list of PHY consumers or NULL
|
||||
*
|
||||
* Called to create a phy using phy framework.
|
||||
*/
|
||||
struct phy *phy_create(struct device *dev, struct device_node *node,
|
||||
const struct phy_ops *ops,
|
||||
struct phy_init_data *init_data)
|
||||
const struct phy_ops *ops)
|
||||
{
|
||||
int ret;
|
||||
int id;
|
||||
@ -632,7 +668,6 @@ struct phy *phy_create(struct device *dev, struct device_node *node,
|
||||
phy->dev.of_node = node ?: dev->of_node;
|
||||
phy->id = id;
|
||||
phy->ops = ops;
|
||||
phy->init_data = init_data;
|
||||
|
||||
ret = dev_set_name(&phy->dev, "phy-%s.%d", dev_name(dev), id);
|
||||
if (ret)
|
||||
@ -667,7 +702,6 @@ EXPORT_SYMBOL_GPL(phy_create);
|
||||
* @dev: device that is creating the new phy
|
||||
* @node: device node of the phy
|
||||
* @ops: function pointers for performing phy operations
|
||||
* @init_data: contains the list of PHY consumers or NULL
|
||||
*
|
||||
* Creates a new PHY device adding it to the PHY class.
|
||||
* While at that, it also associates the device with the phy using devres.
|
||||
@ -675,8 +709,7 @@ EXPORT_SYMBOL_GPL(phy_create);
|
||||
* then, devres data is freed.
|
||||
*/
|
||||
struct phy *devm_phy_create(struct device *dev, struct device_node *node,
|
||||
const struct phy_ops *ops,
|
||||
struct phy_init_data *init_data)
|
||||
const struct phy_ops *ops)
|
||||
{
|
||||
struct phy **ptr, *phy;
|
||||
|
||||
@ -684,7 +717,7 @@ struct phy *devm_phy_create(struct device *dev, struct device_node *node,
|
||||
if (!ptr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
phy = phy_create(dev, node, ops, init_data);
|
||||
phy = phy_create(dev, node, ops);
|
||||
if (!IS_ERR(phy)) {
|
||||
*ptr = phy;
|
||||
devres_add(dev, ptr);
|
||||
|
@ -112,7 +112,7 @@ static int exynos_dp_video_phy_probe(struct platform_device *pdev)
|
||||
match = of_match_node(exynos_dp_video_phy_of_match, dev->of_node);
|
||||
state->drvdata = match->data;
|
||||
|
||||
phy = devm_phy_create(dev, NULL, &exynos_dp_video_phy_ops, NULL);
|
||||
phy = devm_phy_create(dev, NULL, &exynos_dp_video_phy_ops);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(dev, "failed to create Display Port PHY\n");
|
||||
return PTR_ERR(phy);
|
||||
|
@ -137,7 +137,7 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
|
||||
|
||||
for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) {
|
||||
struct phy *phy = devm_phy_create(dev, NULL,
|
||||
&exynos_mipi_video_phy_ops, NULL);
|
||||
&exynos_mipi_video_phy_ops);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(dev, "failed to create PHY %d\n", i);
|
||||
return PTR_ERR(phy);
|
||||
|
@ -141,6 +141,7 @@ struct exynos5_usbdrd_phy_drvdata {
|
||||
const struct exynos5_usbdrd_phy_config *phy_cfg;
|
||||
u32 pmu_offset_usbdrd0_phy;
|
||||
u32 pmu_offset_usbdrd1_phy;
|
||||
bool has_common_clk_gate;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -148,6 +149,9 @@ struct exynos5_usbdrd_phy_drvdata {
|
||||
* @dev: pointer to device instance of this platform device
|
||||
* @reg_phy: usb phy controller register memory base
|
||||
* @clk: phy clock for register access
|
||||
* @pipeclk: clock for pipe3 phy
|
||||
* @utmiclk: clock for utmi+ phy
|
||||
* @itpclk: clock for ITP generation
|
||||
* @drv_data: pointer to SoC level driver data structure
|
||||
* @phys[]: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY
|
||||
* instances each with its 'phy' and 'phy_cfg'.
|
||||
@ -155,12 +159,16 @@ struct exynos5_usbdrd_phy_drvdata {
|
||||
* reference clocks' for SS and HS operations
|
||||
* @ref_clk: reference clock to PHY block from which PHY's
|
||||
* operational clocks are derived
|
||||
* @ref_rate: rate of above reference clock
|
||||
* vbus: VBUS regulator for phy
|
||||
* vbus_boost: Boost regulator for VBUS present on few Exynos boards
|
||||
*/
|
||||
struct exynos5_usbdrd_phy {
|
||||
struct device *dev;
|
||||
void __iomem *reg_phy;
|
||||
struct clk *clk;
|
||||
struct clk *pipeclk;
|
||||
struct clk *utmiclk;
|
||||
struct clk *itpclk;
|
||||
const struct exynos5_usbdrd_phy_drvdata *drv_data;
|
||||
struct phy_usb_instance {
|
||||
struct phy *phy;
|
||||
@ -172,6 +180,7 @@ struct exynos5_usbdrd_phy {
|
||||
u32 extrefclk;
|
||||
struct clk *ref_clk;
|
||||
struct regulator *vbus;
|
||||
struct regulator *vbus_boost;
|
||||
};
|
||||
|
||||
static inline
|
||||
@ -447,13 +456,27 @@ static int exynos5_usbdrd_phy_power_on(struct phy *phy)
|
||||
dev_dbg(phy_drd->dev, "Request to power_on usbdrd_phy phy\n");
|
||||
|
||||
clk_prepare_enable(phy_drd->ref_clk);
|
||||
if (!phy_drd->drv_data->has_common_clk_gate) {
|
||||
clk_prepare_enable(phy_drd->pipeclk);
|
||||
clk_prepare_enable(phy_drd->utmiclk);
|
||||
clk_prepare_enable(phy_drd->itpclk);
|
||||
}
|
||||
|
||||
/* Enable VBUS supply */
|
||||
if (phy_drd->vbus_boost) {
|
||||
ret = regulator_enable(phy_drd->vbus_boost);
|
||||
if (ret) {
|
||||
dev_err(phy_drd->dev,
|
||||
"Failed to enable VBUS boost supply\n");
|
||||
goto fail_vbus;
|
||||
}
|
||||
}
|
||||
|
||||
if (phy_drd->vbus) {
|
||||
ret = regulator_enable(phy_drd->vbus);
|
||||
if (ret) {
|
||||
dev_err(phy_drd->dev, "Failed to enable VBUS supply\n");
|
||||
goto fail_vbus;
|
||||
goto fail_vbus_boost;
|
||||
}
|
||||
}
|
||||
|
||||
@ -462,8 +485,17 @@ static int exynos5_usbdrd_phy_power_on(struct phy *phy)
|
||||
|
||||
return 0;
|
||||
|
||||
fail_vbus_boost:
|
||||
if (phy_drd->vbus_boost)
|
||||
regulator_disable(phy_drd->vbus_boost);
|
||||
|
||||
fail_vbus:
|
||||
clk_disable_unprepare(phy_drd->ref_clk);
|
||||
if (!phy_drd->drv_data->has_common_clk_gate) {
|
||||
clk_disable_unprepare(phy_drd->itpclk);
|
||||
clk_disable_unprepare(phy_drd->utmiclk);
|
||||
clk_disable_unprepare(phy_drd->pipeclk);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -481,8 +513,15 @@ static int exynos5_usbdrd_phy_power_off(struct phy *phy)
|
||||
/* Disable VBUS supply */
|
||||
if (phy_drd->vbus)
|
||||
regulator_disable(phy_drd->vbus);
|
||||
if (phy_drd->vbus_boost)
|
||||
regulator_disable(phy_drd->vbus_boost);
|
||||
|
||||
clk_disable_unprepare(phy_drd->ref_clk);
|
||||
if (!phy_drd->drv_data->has_common_clk_gate) {
|
||||
clk_disable_unprepare(phy_drd->itpclk);
|
||||
clk_disable_unprepare(phy_drd->pipeclk);
|
||||
clk_disable_unprepare(phy_drd->utmiclk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -506,6 +545,57 @@ static struct phy_ops exynos5_usbdrd_phy_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int exynos5_usbdrd_phy_clk_handle(struct exynos5_usbdrd_phy *phy_drd)
|
||||
{
|
||||
unsigned long ref_rate;
|
||||
int ret;
|
||||
|
||||
phy_drd->clk = devm_clk_get(phy_drd->dev, "phy");
|
||||
if (IS_ERR(phy_drd->clk)) {
|
||||
dev_err(phy_drd->dev, "Failed to get phy clock\n");
|
||||
return PTR_ERR(phy_drd->clk);
|
||||
}
|
||||
|
||||
phy_drd->ref_clk = devm_clk_get(phy_drd->dev, "ref");
|
||||
if (IS_ERR(phy_drd->ref_clk)) {
|
||||
dev_err(phy_drd->dev, "Failed to get phy reference clock\n");
|
||||
return PTR_ERR(phy_drd->ref_clk);
|
||||
}
|
||||
ref_rate = clk_get_rate(phy_drd->ref_clk);
|
||||
|
||||
ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk);
|
||||
if (ret) {
|
||||
dev_err(phy_drd->dev, "Clock rate (%ld) not supported\n",
|
||||
ref_rate);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!phy_drd->drv_data->has_common_clk_gate) {
|
||||
phy_drd->pipeclk = devm_clk_get(phy_drd->dev, "phy_pipe");
|
||||
if (IS_ERR(phy_drd->pipeclk)) {
|
||||
dev_info(phy_drd->dev,
|
||||
"PIPE3 phy operational clock not specified\n");
|
||||
phy_drd->pipeclk = NULL;
|
||||
}
|
||||
|
||||
phy_drd->utmiclk = devm_clk_get(phy_drd->dev, "phy_utmi");
|
||||
if (IS_ERR(phy_drd->utmiclk)) {
|
||||
dev_info(phy_drd->dev,
|
||||
"UTMI phy operational clock not specified\n");
|
||||
phy_drd->utmiclk = NULL;
|
||||
}
|
||||
|
||||
phy_drd->itpclk = devm_clk_get(phy_drd->dev, "itp");
|
||||
if (IS_ERR(phy_drd->itpclk)) {
|
||||
dev_info(phy_drd->dev,
|
||||
"ITP clock from main OSC not specified\n");
|
||||
phy_drd->itpclk = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = {
|
||||
{
|
||||
.id = EXYNOS5_DRDPHY_UTMI,
|
||||
@ -525,11 +615,19 @@ static const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = {
|
||||
.phy_cfg = phy_cfg_exynos5,
|
||||
.pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
|
||||
.pmu_offset_usbdrd1_phy = EXYNOS5420_USBDRD1_PHY_CONTROL,
|
||||
.has_common_clk_gate = true,
|
||||
};
|
||||
|
||||
static const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = {
|
||||
.phy_cfg = phy_cfg_exynos5,
|
||||
.pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
|
||||
.has_common_clk_gate = true,
|
||||
};
|
||||
|
||||
static const struct exynos5_usbdrd_phy_drvdata exynos7_usbdrd_phy = {
|
||||
.phy_cfg = phy_cfg_exynos5,
|
||||
.pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
|
||||
.has_common_clk_gate = false,
|
||||
};
|
||||
|
||||
static const struct of_device_id exynos5_usbdrd_phy_of_match[] = {
|
||||
@ -539,6 +637,9 @@ static const struct of_device_id exynos5_usbdrd_phy_of_match[] = {
|
||||
}, {
|
||||
.compatible = "samsung,exynos5420-usbdrd-phy",
|
||||
.data = &exynos5420_usbdrd_phy
|
||||
}, {
|
||||
.compatible = "samsung,exynos7-usbdrd-phy",
|
||||
.data = &exynos7_usbdrd_phy
|
||||
},
|
||||
{ },
|
||||
};
|
||||
@ -555,7 +656,6 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
|
||||
const struct exynos5_usbdrd_phy_drvdata *drv_data;
|
||||
struct regmap *reg_pmu;
|
||||
u32 pmu_offset;
|
||||
unsigned long ref_rate;
|
||||
int i, ret;
|
||||
int channel;
|
||||
|
||||
@ -576,23 +676,9 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
|
||||
drv_data = match->data;
|
||||
phy_drd->drv_data = drv_data;
|
||||
|
||||
phy_drd->clk = devm_clk_get(dev, "phy");
|
||||
if (IS_ERR(phy_drd->clk)) {
|
||||
dev_err(dev, "Failed to get clock of phy controller\n");
|
||||
return PTR_ERR(phy_drd->clk);
|
||||
}
|
||||
|
||||
phy_drd->ref_clk = devm_clk_get(dev, "ref");
|
||||
if (IS_ERR(phy_drd->ref_clk)) {
|
||||
dev_err(dev, "Failed to get reference clock of usbdrd phy\n");
|
||||
return PTR_ERR(phy_drd->ref_clk);
|
||||
}
|
||||
ref_rate = clk_get_rate(phy_drd->ref_clk);
|
||||
|
||||
ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk);
|
||||
ret = exynos5_usbdrd_phy_clk_handle(phy_drd);
|
||||
if (ret) {
|
||||
dev_err(phy_drd->dev, "Clock rate (%ld) not supported\n",
|
||||
ref_rate);
|
||||
dev_err(dev, "Failed to initialize clocks\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -622,7 +708,7 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Get Vbus regulator */
|
||||
/* Get Vbus regulators */
|
||||
phy_drd->vbus = devm_regulator_get(dev, "vbus");
|
||||
if (IS_ERR(phy_drd->vbus)) {
|
||||
ret = PTR_ERR(phy_drd->vbus);
|
||||
@ -633,12 +719,21 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
|
||||
phy_drd->vbus = NULL;
|
||||
}
|
||||
|
||||
phy_drd->vbus_boost = devm_regulator_get(dev, "vbus-boost");
|
||||
if (IS_ERR(phy_drd->vbus_boost)) {
|
||||
ret = PTR_ERR(phy_drd->vbus_boost);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
|
||||
dev_warn(dev, "Failed to get VBUS boost supply regulator\n");
|
||||
phy_drd->vbus_boost = NULL;
|
||||
}
|
||||
|
||||
dev_vdbg(dev, "Creating usbdrd_phy phy\n");
|
||||
|
||||
for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) {
|
||||
struct phy *phy = devm_phy_create(dev, NULL,
|
||||
&exynos5_usbdrd_phy_ops,
|
||||
NULL);
|
||||
&exynos5_usbdrd_phy_ops);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(dev, "Failed to create usbdrd_phy phy\n");
|
||||
return PTR_ERR(phy);
|
||||
|
@ -210,7 +210,7 @@ static int exynos_sata_phy_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
sata_phy->phy = devm_phy_create(dev, NULL, &exynos_sata_phy_ops, NULL);
|
||||
sata_phy->phy = devm_phy_create(dev, NULL, &exynos_sata_phy_ops);
|
||||
if (IS_ERR(sata_phy->phy)) {
|
||||
clk_disable_unprepare(sata_phy->phyclk);
|
||||
dev_err(dev, "failed to create PHY\n");
|
||||
|
@ -156,7 +156,7 @@ static int hix5hd2_sata_phy_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(priv->peri_ctrl))
|
||||
priv->peri_ctrl = NULL;
|
||||
|
||||
phy = devm_phy_create(dev, NULL, &hix5hd2_sata_phy_ops, NULL);
|
||||
phy = devm_phy_create(dev, NULL, &hix5hd2_sata_phy_ops);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(dev, "failed to create PHY\n");
|
||||
return PTR_ERR(phy);
|
||||
@ -164,10 +164,7 @@ static int hix5hd2_sata_phy_probe(struct platform_device *pdev)
|
||||
|
||||
phy_set_drvdata(phy, priv);
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
if (IS_ERR(phy_provider))
|
||||
return PTR_ERR(phy_provider);
|
||||
|
||||
return 0;
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id hix5hd2_sata_phy_of_match[] = {
|
||||
|
1283
drivers/phy/phy-miphy28lp.c
Normal file
1283
drivers/phy/phy-miphy28lp.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -593,7 +593,7 @@ static int miphy365x_probe(struct platform_device *pdev)
|
||||
|
||||
miphy_dev->phys[port] = miphy_phy;
|
||||
|
||||
phy = devm_phy_create(&pdev->dev, child, &miphy365x_ops, NULL);
|
||||
phy = devm_phy_create(&pdev->dev, child, &miphy365x_ops);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(&pdev->dev, "failed to create PHY\n");
|
||||
return PTR_ERR(phy);
|
||||
@ -610,10 +610,7 @@ static int miphy365x_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
provider = devm_of_phy_provider_register(&pdev->dev, miphy365x_xlate);
|
||||
if (IS_ERR(provider))
|
||||
return PTR_ERR(provider);
|
||||
|
||||
return 0;
|
||||
return PTR_ERR_OR_ZERO(provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id miphy365x_of_match[] = {
|
||||
|
@ -101,7 +101,7 @@ static int phy_mvebu_sata_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(priv->clk))
|
||||
return PTR_ERR(priv->clk);
|
||||
|
||||
phy = devm_phy_create(&pdev->dev, NULL, &phy_mvebu_sata_ops, NULL);
|
||||
phy = devm_phy_create(&pdev->dev, NULL, &phy_mvebu_sata_ops);
|
||||
if (IS_ERR(phy))
|
||||
return PTR_ERR(phy);
|
||||
|
||||
|
@ -60,7 +60,7 @@ EXPORT_SYMBOL_GPL(omap_usb2_set_comparator);
|
||||
|
||||
static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled)
|
||||
{
|
||||
struct omap_usb *phy = phy_to_omapusb(otg->phy);
|
||||
struct omap_usb *phy = phy_to_omapusb(otg->usb_phy);
|
||||
|
||||
if (!phy->comparator)
|
||||
return -ENODEV;
|
||||
@ -70,7 +70,7 @@ static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled)
|
||||
|
||||
static int omap_usb_start_srp(struct usb_otg *otg)
|
||||
{
|
||||
struct omap_usb *phy = phy_to_omapusb(otg->phy);
|
||||
struct omap_usb *phy = phy_to_omapusb(otg->usb_phy);
|
||||
|
||||
if (!phy->comparator)
|
||||
return -ENODEV;
|
||||
@ -80,11 +80,9 @@ static int omap_usb_start_srp(struct usb_otg *otg)
|
||||
|
||||
static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
|
||||
{
|
||||
struct usb_phy *phy = otg->phy;
|
||||
|
||||
otg->host = host;
|
||||
if (!host)
|
||||
phy->state = OTG_STATE_UNDEFINED;
|
||||
otg->state = OTG_STATE_UNDEFINED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -92,11 +90,9 @@ static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
|
||||
static int omap_usb_set_peripheral(struct usb_otg *otg,
|
||||
struct usb_gadget *gadget)
|
||||
{
|
||||
struct usb_phy *phy = otg->phy;
|
||||
|
||||
otg->gadget = gadget;
|
||||
if (!gadget)
|
||||
phy->state = OTG_STATE_UNDEFINED;
|
||||
otg->state = OTG_STATE_UNDEFINED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -255,12 +251,12 @@ static int omap_usb2_probe(struct platform_device *pdev)
|
||||
otg->set_vbus = omap_usb_set_vbus;
|
||||
if (phy_data->flags & OMAP_USB2_HAS_START_SRP)
|
||||
otg->start_srp = omap_usb_start_srp;
|
||||
otg->phy = &phy->phy;
|
||||
otg->usb_phy = &phy->phy;
|
||||
|
||||
platform_set_drvdata(pdev, phy);
|
||||
pm_runtime_enable(phy->dev);
|
||||
|
||||
generic_phy = devm_phy_create(phy->dev, NULL, &ops, NULL);
|
||||
generic_phy = devm_phy_create(phy->dev, NULL, &ops);
|
||||
if (IS_ERR(generic_phy)) {
|
||||
pm_runtime_disable(phy->dev);
|
||||
return PTR_ERR(generic_phy);
|
||||
|
@ -228,8 +228,7 @@ static int qcom_apq8064_sata_phy_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(phy->mmio))
|
||||
return PTR_ERR(phy->mmio);
|
||||
|
||||
generic_phy = devm_phy_create(dev, NULL, &qcom_apq8064_sata_phy_ops,
|
||||
NULL);
|
||||
generic_phy = devm_phy_create(dev, NULL, &qcom_apq8064_sata_phy_ops);
|
||||
if (IS_ERR(generic_phy)) {
|
||||
dev_err(dev, "%s: failed to create phy\n", __func__);
|
||||
return PTR_ERR(generic_phy);
|
||||
|
@ -150,8 +150,7 @@ static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(phy->mmio))
|
||||
return PTR_ERR(phy->mmio);
|
||||
|
||||
generic_phy = devm_phy_create(dev, NULL, &qcom_ipq806x_sata_phy_ops,
|
||||
NULL);
|
||||
generic_phy = devm_phy_create(dev, NULL, &qcom_ipq806x_sata_phy_ops);
|
||||
if (IS_ERR(generic_phy)) {
|
||||
dev_err(dev, "%s: failed to create phy\n", __func__);
|
||||
return PTR_ERR(generic_phy);
|
||||
|
@ -304,7 +304,7 @@ static int rcar_gen2_phy_probe(struct platform_device *pdev)
|
||||
phy->select_value = select_value[channel_num][n];
|
||||
|
||||
phy->phy = devm_phy_create(dev, NULL,
|
||||
&rcar_gen2_phy_ops, NULL);
|
||||
&rcar_gen2_phy_ops);
|
||||
if (IS_ERR(phy->phy)) {
|
||||
dev_err(dev, "Failed to create PHY\n");
|
||||
return PTR_ERR(phy->phy);
|
||||
|
@ -202,8 +202,7 @@ static int samsung_usb2_phy_probe(struct platform_device *pdev)
|
||||
struct samsung_usb2_phy_instance *p = &drv->instances[i];
|
||||
|
||||
dev_dbg(dev, "Creating phy \"%s\"\n", label);
|
||||
p->phy = devm_phy_create(dev, NULL, &samsung_usb2_phy_ops,
|
||||
NULL);
|
||||
p->phy = devm_phy_create(dev, NULL, &samsung_usb2_phy_ops);
|
||||
if (IS_ERR(p->phy)) {
|
||||
dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n",
|
||||
label);
|
||||
|
@ -227,7 +227,7 @@ static int spear1310_miphy_probe(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->phy = devm_phy_create(dev, NULL, &spear1310_miphy_ops, NULL);
|
||||
priv->phy = devm_phy_create(dev, NULL, &spear1310_miphy_ops);
|
||||
if (IS_ERR(priv->phy)) {
|
||||
dev_err(dev, "failed to create SATA PCIe PHY\n");
|
||||
return PTR_ERR(priv->phy);
|
||||
|
@ -259,7 +259,7 @@ static int spear1340_miphy_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(priv->misc);
|
||||
}
|
||||
|
||||
priv->phy = devm_phy_create(dev, NULL, &spear1340_miphy_ops, NULL);
|
||||
priv->phy = devm_phy_create(dev, NULL, &spear1340_miphy_ops);
|
||||
if (IS_ERR(priv->phy)) {
|
||||
dev_err(dev, "failed to create SATA PCIe PHY\n");
|
||||
return PTR_ERR(priv->phy);
|
||||
|
@ -137,7 +137,7 @@ static int stih407_usb2_picophy_probe(struct platform_device *pdev)
|
||||
}
|
||||
phy_dev->param = res->start;
|
||||
|
||||
phy = devm_phy_create(dev, NULL, &stih407_usb2_picophy_data, NULL);
|
||||
phy = devm_phy_create(dev, NULL, &stih407_usb2_picophy_data);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(dev, "failed to create Display Port PHY\n");
|
||||
return PTR_ERR(phy);
|
||||
|
@ -148,7 +148,7 @@ static int stih41x_usb_phy_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(phy_dev->clk);
|
||||
}
|
||||
|
||||
phy = devm_phy_create(dev, NULL, &stih41x_usb_phy_ops, NULL);
|
||||
phy = devm_phy_create(dev, NULL, &stih41x_usb_phy_ops);
|
||||
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(dev, "failed to create phy\n");
|
||||
@ -160,10 +160,7 @@ static int stih41x_usb_phy_probe(struct platform_device *pdev)
|
||||
phy_set_drvdata(phy, phy_dev);
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
if (IS_ERR(phy_provider))
|
||||
return PTR_ERR(phy_provider);
|
||||
|
||||
return 0;
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id stih41x_usb_phy_of_match[] = {
|
||||
|
@ -157,6 +157,10 @@ static int sun4i_usb_phy_init(struct phy *_phy)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Enable USB 45 Ohm resistor calibration */
|
||||
if (phy->index == 0)
|
||||
sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1);
|
||||
|
||||
/* Adjust PHY's magnitude and rate */
|
||||
sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5);
|
||||
|
||||
@ -213,7 +217,7 @@ static struct phy *sun4i_usb_phy_xlate(struct device *dev,
|
||||
{
|
||||
struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
|
||||
|
||||
if (WARN_ON(args->args[0] == 0 || args->args[0] >= data->num_phys))
|
||||
if (args->args[0] >= data->num_phys)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
return data->phys[args->args[0]].phy;
|
||||
@ -255,8 +259,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(data->base))
|
||||
return PTR_ERR(data->base);
|
||||
|
||||
/* Skip 0, 0 is the phy for otg which is not yet supported. */
|
||||
for (i = 1; i < data->num_phys; i++) {
|
||||
for (i = 0; i < data->num_phys; i++) {
|
||||
struct sun4i_usb_phy *phy = data->phys + i;
|
||||
char name[16];
|
||||
|
||||
@ -295,7 +298,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(phy->pmu);
|
||||
}
|
||||
|
||||
phy->phy = devm_phy_create(dev, NULL, &sun4i_usb_phy_ops, NULL);
|
||||
phy->phy = devm_phy_create(dev, NULL, &sun4i_usb_phy_ops);
|
||||
if (IS_ERR(phy->phy)) {
|
||||
dev_err(dev, "failed to create PHY %d\n", i);
|
||||
return PTR_ERR(phy->phy);
|
||||
|
@ -399,7 +399,7 @@ static int ti_pipe3_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, phy);
|
||||
pm_runtime_enable(phy->dev);
|
||||
|
||||
generic_phy = devm_phy_create(phy->dev, NULL, &ops, NULL);
|
||||
generic_phy = devm_phy_create(phy->dev, NULL, &ops);
|
||||
if (IS_ERR(generic_phy))
|
||||
return PTR_ERR(generic_phy);
|
||||
|
||||
|
@ -606,7 +606,7 @@ static int twl4030_set_peripheral(struct usb_otg *otg,
|
||||
|
||||
otg->gadget = gadget;
|
||||
if (!gadget)
|
||||
otg->phy->state = OTG_STATE_UNDEFINED;
|
||||
otg->state = OTG_STATE_UNDEFINED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -618,7 +618,7 @@ static int twl4030_set_host(struct usb_otg *otg, struct usb_bus *host)
|
||||
|
||||
otg->host = host;
|
||||
if (!host)
|
||||
otg->phy->state = OTG_STATE_UNDEFINED;
|
||||
otg->state = OTG_STATE_UNDEFINED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -644,7 +644,6 @@ static int twl4030_usb_probe(struct platform_device *pdev)
|
||||
struct usb_otg *otg;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct phy_provider *phy_provider;
|
||||
struct phy_init_data *init_data = NULL;
|
||||
|
||||
twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL);
|
||||
if (!twl)
|
||||
@ -655,7 +654,6 @@ static int twl4030_usb_probe(struct platform_device *pdev)
|
||||
(enum twl4030_usb_mode *)&twl->usb_mode);
|
||||
else if (pdata) {
|
||||
twl->usb_mode = pdata->usb_mode;
|
||||
init_data = pdata->init_data;
|
||||
} else {
|
||||
dev_err(&pdev->dev, "twl4030 initialized without pdata\n");
|
||||
return -EINVAL;
|
||||
@ -676,11 +674,11 @@ static int twl4030_usb_probe(struct platform_device *pdev)
|
||||
twl->phy.otg = otg;
|
||||
twl->phy.type = USB_PHY_TYPE_USB2;
|
||||
|
||||
otg->phy = &twl->phy;
|
||||
otg->usb_phy = &twl->phy;
|
||||
otg->set_host = twl4030_set_host;
|
||||
otg->set_peripheral = twl4030_set_peripheral;
|
||||
|
||||
phy = devm_phy_create(twl->dev, NULL, &ops, init_data);
|
||||
phy = devm_phy_create(twl->dev, NULL, &ops);
|
||||
if (IS_ERR(phy)) {
|
||||
dev_dbg(&pdev->dev, "Failed to create PHY\n");
|
||||
return PTR_ERR(phy);
|
||||
@ -733,6 +731,11 @@ static int twl4030_usb_probe(struct platform_device *pdev)
|
||||
return status;
|
||||
}
|
||||
|
||||
if (pdata)
|
||||
err = phy_create_lookup(phy, "usb", "musb-hdrc.0");
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
pm_runtime_mark_last_busy(&pdev->dev);
|
||||
pm_runtime_put_autosuspend(twl->dev);
|
||||
|
||||
|
@ -1707,7 +1707,7 @@ static int xgene_phy_probe(struct platform_device *pdev)
|
||||
ctx->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, ctx);
|
||||
|
||||
ctx->phy = devm_phy_create(ctx->dev, NULL, &xgene_phy_ops, NULL);
|
||||
ctx->phy = devm_phy_create(ctx->dev, NULL, &xgene_phy_ops);
|
||||
if (IS_ERR(ctx->phy)) {
|
||||
dev_dbg(&pdev->dev, "Failed to create PHY\n");
|
||||
rc = PTR_ERR(ctx->phy);
|
||||
|
@ -915,7 +915,7 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev)
|
||||
goto reset;
|
||||
}
|
||||
|
||||
phy = devm_phy_create(&pdev->dev, NULL, &pcie_phy_ops, NULL);
|
||||
phy = devm_phy_create(&pdev->dev, NULL, &pcie_phy_ops);
|
||||
if (IS_ERR(phy)) {
|
||||
err = PTR_ERR(phy);
|
||||
goto unregister;
|
||||
@ -924,7 +924,7 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev)
|
||||
padctl->phys[TEGRA_XUSB_PADCTL_PCIE] = phy;
|
||||
phy_set_drvdata(phy, padctl);
|
||||
|
||||
phy = devm_phy_create(&pdev->dev, NULL, &sata_phy_ops, NULL);
|
||||
phy = devm_phy_create(&pdev->dev, NULL, &sata_phy_ops);
|
||||
if (IS_ERR(phy)) {
|
||||
err = PTR_ERR(phy);
|
||||
goto unregister;
|
||||
|
@ -10,6 +10,7 @@ ci_hdrc-$(CONFIG_USB_OTG_FSM) += otg_fsm.o
|
||||
|
||||
# Glue/Bridge layers go here
|
||||
|
||||
obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_usb2.o
|
||||
obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_msm.o
|
||||
obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_zevio.o
|
||||
|
||||
|
@ -161,7 +161,8 @@ struct hw_bank {
|
||||
* @test_mode: the selected test mode
|
||||
* @platdata: platform specific information supplied by parent device
|
||||
* @vbus_active: is VBUS active
|
||||
* @transceiver: pointer to USB PHY, if any
|
||||
* @phy: pointer to PHY, if any
|
||||
* @usb_phy: pointer to USB PHY, if any and if using the USB PHY framework
|
||||
* @hcd: pointer to usb_hcd for ehci host driver
|
||||
* @debugfs: root dentry for this controller in debugfs
|
||||
* @id_event: indicates there is an id event, and handled at ci_otg_work
|
||||
@ -177,6 +178,7 @@ struct ci_hdrc {
|
||||
struct ci_role_driver *roles[CI_ROLE_END];
|
||||
enum ci_role role;
|
||||
bool is_otg;
|
||||
struct usb_otg otg;
|
||||
struct otg_fsm fsm;
|
||||
struct ci_otg_fsm_timer_list *fsm_timer;
|
||||
struct work_struct work;
|
||||
@ -201,7 +203,9 @@ struct ci_hdrc {
|
||||
|
||||
struct ci_hdrc_platform_data *platdata;
|
||||
int vbus_active;
|
||||
struct usb_phy *transceiver;
|
||||
struct phy *phy;
|
||||
/* old usb_phy interface */
|
||||
struct usb_phy *usb_phy;
|
||||
struct usb_hcd *hcd;
|
||||
struct dentry *debugfs;
|
||||
bool id_event;
|
||||
@ -348,7 +352,7 @@ u32 hw_read_intr_enable(struct ci_hdrc *ci);
|
||||
|
||||
u32 hw_read_intr_status(struct ci_hdrc *ci);
|
||||
|
||||
int hw_device_reset(struct ci_hdrc *ci, u32 mode);
|
||||
int hw_device_reset(struct ci_hdrc *ci);
|
||||
|
||||
int hw_port_test_set(struct ci_hdrc *ci, u8 mode);
|
||||
|
||||
|
@ -106,8 +106,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
|
||||
struct ci_hdrc_platform_data pdata = {
|
||||
.name = dev_name(&pdev->dev),
|
||||
.capoffset = DEF_CAPOFFSET,
|
||||
.flags = CI_HDRC_REQUIRE_TRANSCEIVER |
|
||||
CI_HDRC_DISABLE_STREAMING,
|
||||
.flags = CI_HDRC_DISABLE_STREAMING,
|
||||
};
|
||||
int ret;
|
||||
const struct of_device_id *of_id =
|
||||
@ -115,10 +114,8 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
|
||||
const struct ci_hdrc_imx_platform_flag *imx_platform_flag = of_id->data;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
dev_err(&pdev->dev, "Failed to allocate ci_hdrc-imx data!\n");
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
data->usbmisc_data = usbmisc_get_init_data(&pdev->dev);
|
||||
if (IS_ERR(data->usbmisc_data))
|
||||
@ -147,7 +144,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
pdata.phy = data->phy;
|
||||
pdata.usb_phy = data->phy;
|
||||
|
||||
if (imx_platform_flag->flags & CI_HDRC_IMX_IMX28_WRITE_FIX)
|
||||
pdata.flags |= CI_HDRC_IMX28_WRITE_FIX;
|
||||
@ -210,6 +207,41 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int imx_controller_suspend(struct device *dev)
|
||||
{
|
||||
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
|
||||
|
||||
dev_dbg(dev, "at %s\n", __func__);
|
||||
|
||||
clk_disable_unprepare(data->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_controller_resume(struct device *dev)
|
||||
{
|
||||
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
|
||||
|
||||
dev_dbg(dev, "at %s\n", __func__);
|
||||
|
||||
return clk_prepare_enable(data->clk);
|
||||
}
|
||||
|
||||
static int ci_hdrc_imx_suspend(struct device *dev)
|
||||
{
|
||||
return imx_controller_suspend(dev);
|
||||
}
|
||||
|
||||
static int ci_hdrc_imx_resume(struct device *dev)
|
||||
{
|
||||
return imx_controller_resume(dev);
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume)
|
||||
};
|
||||
static struct platform_driver ci_hdrc_imx_driver = {
|
||||
.probe = ci_hdrc_imx_probe,
|
||||
.remove = ci_hdrc_imx_remove,
|
||||
@ -217,6 +249,7 @@ static struct platform_driver ci_hdrc_imx_driver = {
|
||||
.name = "imx_usb",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = ci_hdrc_imx_dt_ids,
|
||||
.pm = &ci_hdrc_imx_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -26,15 +26,15 @@ static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
|
||||
dev_dbg(dev, "CI_HDRC_CONTROLLER_RESET_EVENT received\n");
|
||||
writel(0, USB_AHBBURST);
|
||||
writel(0, USB_AHBMODE);
|
||||
usb_phy_init(ci->transceiver);
|
||||
usb_phy_init(ci->usb_phy);
|
||||
break;
|
||||
case CI_HDRC_CONTROLLER_STOPPED_EVENT:
|
||||
dev_dbg(dev, "CI_HDRC_CONTROLLER_STOPPED_EVENT received\n");
|
||||
/*
|
||||
* Put the transceiver in non-driving mode. Otherwise host
|
||||
* Put the phy in non-driving mode. Otherwise host
|
||||
* may not detect soft-disconnection.
|
||||
*/
|
||||
usb_phy_notify_disconnect(ci->transceiver, USB_SPEED_UNKNOWN);
|
||||
usb_phy_notify_disconnect(ci->usb_phy, USB_SPEED_UNKNOWN);
|
||||
break;
|
||||
default:
|
||||
dev_dbg(dev, "unknown ci_hdrc event\n");
|
||||
@ -46,7 +46,6 @@ static struct ci_hdrc_platform_data ci_hdrc_msm_platdata = {
|
||||
.name = "ci_hdrc_msm",
|
||||
.capoffset = DEF_CAPOFFSET,
|
||||
.flags = CI_HDRC_REGS_SHARED |
|
||||
CI_HDRC_REQUIRE_TRANSCEIVER |
|
||||
CI_HDRC_DISABLE_STREAMING,
|
||||
|
||||
.notify_event = ci_hdrc_msm_notify_event,
|
||||
@ -68,7 +67,7 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(phy))
|
||||
return PTR_ERR(phy);
|
||||
|
||||
ci_hdrc_msm_platdata.phy = phy;
|
||||
ci_hdrc_msm_platdata.usb_phy = phy;
|
||||
|
||||
plat_ci = ci_hdrc_add_device(&pdev->dev,
|
||||
pdev->resource, pdev->num_resources,
|
||||
|
115
drivers/usb/chipidea/ci_hdrc_usb2.c
Normal file
115
drivers/usb/chipidea/ci_hdrc_usb2.c
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Marvell Technology Group Ltd.
|
||||
*
|
||||
* Antoine Tenart <antoine.tenart@free-electrons.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/usb/chipidea.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/usb/ulpi.h>
|
||||
|
||||
#include "ci.h"
|
||||
|
||||
struct ci_hdrc_usb2_priv {
|
||||
struct platform_device *ci_pdev;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static struct ci_hdrc_platform_data ci_default_pdata = {
|
||||
.capoffset = DEF_CAPOFFSET,
|
||||
.flags = CI_HDRC_DISABLE_STREAMING,
|
||||
};
|
||||
|
||||
static int ci_hdrc_usb2_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct ci_hdrc_usb2_priv *priv;
|
||||
struct ci_hdrc_platform_data *ci_pdata = dev_get_platdata(dev);
|
||||
int ret;
|
||||
|
||||
if (!ci_pdata)
|
||||
ci_pdata = &ci_default_pdata;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->clk = devm_clk_get(dev, NULL);
|
||||
if (!IS_ERR(priv->clk)) {
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable the clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
|
||||
if (ret)
|
||||
goto clk_err;
|
||||
|
||||
ci_pdata->name = dev_name(dev);
|
||||
|
||||
priv->ci_pdev = ci_hdrc_add_device(dev, pdev->resource,
|
||||
pdev->num_resources, ci_pdata);
|
||||
if (IS_ERR(priv->ci_pdev)) {
|
||||
ret = PTR_ERR(priv->ci_pdev);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev,
|
||||
"failed to register ci_hdrc platform device: %d\n",
|
||||
ret);
|
||||
goto clk_err;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
pm_runtime_no_callbacks(dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
clk_err:
|
||||
if (!IS_ERR(priv->clk))
|
||||
clk_disable_unprepare(priv->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ci_hdrc_usb2_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ci_hdrc_usb2_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
ci_hdrc_remove_device(priv->ci_pdev);
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ci_hdrc_usb2_of_match[] = {
|
||||
{ .compatible = "chipidea,usb2" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ci_hdrc_usb2_of_match);
|
||||
|
||||
static struct platform_driver ci_hdrc_usb2_driver = {
|
||||
.probe = ci_hdrc_usb2_probe,
|
||||
.remove = ci_hdrc_usb2_remove,
|
||||
.driver = {
|
||||
.name = "chipidea-usb2",
|
||||
.of_match_table = of_match_ptr(ci_hdrc_usb2_of_match),
|
||||
},
|
||||
};
|
||||
module_platform_driver(ci_hdrc_usb2_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ChipIdea HDRC USB2 binding for ci13xxx");
|
||||
MODULE_AUTHOR("Antoine Tenart <antoine.tenart@free-electrons.com>");
|
||||
MODULE_LICENSE("GPL");
|
@ -47,6 +47,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/idr.h>
|
||||
@ -189,24 +190,29 @@ u8 hw_port_test_get(struct ci_hdrc *ci)
|
||||
return hw_read(ci, OP_PORTSC, PORTSC_PTC) >> __ffs(PORTSC_PTC);
|
||||
}
|
||||
|
||||
static void hw_wait_phy_stable(void)
|
||||
{
|
||||
/*
|
||||
* The phy needs some delay to output the stable status from low
|
||||
* power mode. And for OTGSC, the status inputs are debounced
|
||||
* using a 1 ms time constant, so, delay 2ms for controller to get
|
||||
* the stable status, like vbus and id when the phy leaves low power.
|
||||
*/
|
||||
usleep_range(2000, 2500);
|
||||
}
|
||||
|
||||
/* The PHY enters/leaves low power mode */
|
||||
static void ci_hdrc_enter_lpm(struct ci_hdrc *ci, bool enable)
|
||||
{
|
||||
enum ci_hw_regs reg = ci->hw_bank.lpm ? OP_DEVLC : OP_PORTSC;
|
||||
bool lpm = !!(hw_read(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm)));
|
||||
|
||||
if (enable && !lpm) {
|
||||
if (enable && !lpm)
|
||||
hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm),
|
||||
PORTSC_PHCD(ci->hw_bank.lpm));
|
||||
} else if (!enable && lpm) {
|
||||
else if (!enable && lpm)
|
||||
hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm),
|
||||
0);
|
||||
/*
|
||||
* the PHY needs some time (less
|
||||
* than 1ms) to leave low power mode.
|
||||
*/
|
||||
usleep_range(1000, 1100);
|
||||
}
|
||||
}
|
||||
|
||||
static int hw_device_init(struct ci_hdrc *ci, void __iomem *base)
|
||||
@ -298,6 +304,49 @@ static void hw_phymode_configure(struct ci_hdrc *ci)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* _ci_usb_phy_init: initialize phy taking in account both phy and usb_phy
|
||||
* interfaces
|
||||
* @ci: the controller
|
||||
*
|
||||
* This function returns an error code if the phy failed to init
|
||||
*/
|
||||
static int _ci_usb_phy_init(struct ci_hdrc *ci)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (ci->phy) {
|
||||
ret = phy_init(ci->phy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = phy_power_on(ci->phy);
|
||||
if (ret) {
|
||||
phy_exit(ci->phy);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = usb_phy_init(ci->usb_phy);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* _ci_usb_phy_exit: deinitialize phy taking in account both phy and usb_phy
|
||||
* interfaces
|
||||
* @ci: the controller
|
||||
*/
|
||||
static void ci_usb_phy_exit(struct ci_hdrc *ci)
|
||||
{
|
||||
if (ci->phy) {
|
||||
phy_power_off(ci->phy);
|
||||
phy_exit(ci->phy);
|
||||
} else {
|
||||
usb_phy_shutdown(ci->usb_phy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ci_usb_phy_init: initialize phy according to different phy type
|
||||
* @ci: the controller
|
||||
@ -312,40 +361,68 @@ static int ci_usb_phy_init(struct ci_hdrc *ci)
|
||||
case USBPHY_INTERFACE_MODE_UTMI:
|
||||
case USBPHY_INTERFACE_MODE_UTMIW:
|
||||
case USBPHY_INTERFACE_MODE_HSIC:
|
||||
ret = usb_phy_init(ci->transceiver);
|
||||
if (ret)
|
||||
ret = _ci_usb_phy_init(ci);
|
||||
if (!ret)
|
||||
hw_wait_phy_stable();
|
||||
else
|
||||
return ret;
|
||||
hw_phymode_configure(ci);
|
||||
break;
|
||||
case USBPHY_INTERFACE_MODE_ULPI:
|
||||
case USBPHY_INTERFACE_MODE_SERIAL:
|
||||
hw_phymode_configure(ci);
|
||||
ret = usb_phy_init(ci->transceiver);
|
||||
ret = _ci_usb_phy_init(ci);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
default:
|
||||
ret = usb_phy_init(ci->transceiver);
|
||||
ret = _ci_usb_phy_init(ci);
|
||||
if (!ret)
|
||||
hw_wait_phy_stable();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_device_reset: resets chip (execute without interruption)
|
||||
* hw_controller_reset: do controller reset
|
||||
* @ci: the controller
|
||||
*
|
||||
* This function returns an error code
|
||||
*/
|
||||
int hw_device_reset(struct ci_hdrc *ci, u32 mode)
|
||||
static int hw_controller_reset(struct ci_hdrc *ci)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
hw_write(ci, OP_USBCMD, USBCMD_RST, USBCMD_RST);
|
||||
while (hw_read(ci, OP_USBCMD, USBCMD_RST)) {
|
||||
udelay(10);
|
||||
if (count++ > 1000)
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hw_device_reset: resets chip (execute without interruption)
|
||||
* @ci: the controller
|
||||
*
|
||||
* This function returns an error code
|
||||
*/
|
||||
int hw_device_reset(struct ci_hdrc *ci)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* should flush & stop before reset */
|
||||
hw_write(ci, OP_ENDPTFLUSH, ~0, ~0);
|
||||
hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
|
||||
|
||||
hw_write(ci, OP_USBCMD, USBCMD_RST, USBCMD_RST);
|
||||
while (hw_read(ci, OP_USBCMD, USBCMD_RST))
|
||||
udelay(10); /* not RTOS friendly */
|
||||
ret = hw_controller_reset(ci);
|
||||
if (ret) {
|
||||
dev_err(ci->dev, "error resetting controller, ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ci->platdata->notify_event)
|
||||
ci->platdata->notify_event(ci,
|
||||
@ -363,12 +440,12 @@ int hw_device_reset(struct ci_hdrc *ci, u32 mode)
|
||||
|
||||
/* USBMODE should be configured step by step */
|
||||
hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE);
|
||||
hw_write(ci, OP_USBMODE, USBMODE_CM, mode);
|
||||
hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_DC);
|
||||
/* HW >= 2.3 */
|
||||
hw_write(ci, OP_USBMODE, USBMODE_SLOM, USBMODE_SLOM);
|
||||
|
||||
if (hw_read(ci, OP_USBMODE, USBMODE_CM) != mode) {
|
||||
pr_err("cannot enter in %s mode", ci_role(ci)->name);
|
||||
if (hw_read(ci, OP_USBMODE, USBMODE_CM) != USBMODE_CM_DC) {
|
||||
pr_err("cannot enter in %s device mode", ci_role(ci)->name);
|
||||
pr_err("lpm = %i", ci->hw_bank.lpm);
|
||||
return -ENODEV;
|
||||
}
|
||||
@ -472,7 +549,7 @@ static int ci_get_platdata(struct device *dev,
|
||||
if (PTR_ERR(platdata->reg_vbus) == -EPROBE_DEFER) {
|
||||
return -EPROBE_DEFER;
|
||||
} else if (PTR_ERR(platdata->reg_vbus) == -ENODEV) {
|
||||
/* no vbus regualator is needed */
|
||||
/* no vbus regulator is needed */
|
||||
platdata->reg_vbus = NULL;
|
||||
} else if (IS_ERR(platdata->reg_vbus)) {
|
||||
dev_err(dev, "Getting regulator error: %ld\n",
|
||||
@ -589,11 +666,10 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(base);
|
||||
|
||||
ci = devm_kzalloc(dev, sizeof(*ci), GFP_KERNEL);
|
||||
if (!ci) {
|
||||
dev_err(dev, "can't allocate device\n");
|
||||
if (!ci)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, ci);
|
||||
ci->dev = dev;
|
||||
ci->platdata = dev_get_platdata(dev);
|
||||
ci->imx28_write_fix = !!(ci->platdata->flags &
|
||||
@ -605,36 +681,32 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (ci->platdata->phy)
|
||||
ci->transceiver = ci->platdata->phy;
|
||||
else
|
||||
ci->transceiver = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
|
||||
if (ci->platdata->phy) {
|
||||
ci->phy = ci->platdata->phy;
|
||||
} else if (ci->platdata->usb_phy) {
|
||||
ci->usb_phy = ci->platdata->usb_phy;
|
||||
} else {
|
||||
ci->phy = devm_phy_get(dev->parent, "usb-phy");
|
||||
ci->usb_phy = devm_usb_get_phy(dev->parent, USB_PHY_TYPE_USB2);
|
||||
|
||||
if (IS_ERR(ci->transceiver)) {
|
||||
ret = PTR_ERR(ci->transceiver);
|
||||
/*
|
||||
* if -ENXIO is returned, it means PHY layer wasn't
|
||||
* enabled, so it makes no sense to return -EPROBE_DEFER
|
||||
* in that case, since no PHY driver will ever probe.
|
||||
*/
|
||||
if (ret == -ENXIO)
|
||||
return ret;
|
||||
/* if both generic PHY and USB PHY layers aren't enabled */
|
||||
if (PTR_ERR(ci->phy) == -ENOSYS &&
|
||||
PTR_ERR(ci->usb_phy) == -ENXIO)
|
||||
return -ENXIO;
|
||||
|
||||
dev_err(dev, "no usb2 phy configured\n");
|
||||
return -EPROBE_DEFER;
|
||||
if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy))
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
if (IS_ERR(ci->phy))
|
||||
ci->phy = NULL;
|
||||
else if (IS_ERR(ci->usb_phy))
|
||||
ci->usb_phy = NULL;
|
||||
}
|
||||
|
||||
ret = ci_usb_phy_init(ci);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to init phy: %d\n", ret);
|
||||
return ret;
|
||||
} else {
|
||||
/*
|
||||
* The delay to sync PHY's status, the maximum delay is
|
||||
* 2ms since the otgsc uses 1ms timer to debounce the
|
||||
* PHY's input
|
||||
*/
|
||||
usleep_range(2000, 2500);
|
||||
}
|
||||
|
||||
ci->hw_bank.phys = res->start;
|
||||
@ -711,9 +783,8 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, ci);
|
||||
ret = request_irq(ci->irq, ci_irq, IRQF_SHARED, ci->platdata->name,
|
||||
ci);
|
||||
ret = devm_request_irq(dev, ci->irq, ci_irq, IRQF_SHARED,
|
||||
ci->platdata->name, ci);
|
||||
if (ret)
|
||||
goto stop;
|
||||
|
||||
@ -724,11 +795,10 @@ static int ci_hdrc_probe(struct platform_device *pdev)
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
free_irq(ci->irq, ci);
|
||||
stop:
|
||||
ci_role_destroy(ci);
|
||||
deinit_phy:
|
||||
usb_phy_shutdown(ci->transceiver);
|
||||
ci_usb_phy_exit(ci);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -738,19 +808,66 @@ static int ci_hdrc_remove(struct platform_device *pdev)
|
||||
struct ci_hdrc *ci = platform_get_drvdata(pdev);
|
||||
|
||||
dbg_remove_files(ci);
|
||||
free_irq(ci->irq, ci);
|
||||
ci_role_destroy(ci);
|
||||
ci_hdrc_enter_lpm(ci, true);
|
||||
usb_phy_shutdown(ci->transceiver);
|
||||
ci_usb_phy_exit(ci);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static void ci_controller_suspend(struct ci_hdrc *ci)
|
||||
{
|
||||
ci_hdrc_enter_lpm(ci, true);
|
||||
|
||||
if (ci->usb_phy)
|
||||
usb_phy_set_suspend(ci->usb_phy, 1);
|
||||
}
|
||||
|
||||
static int ci_controller_resume(struct device *dev)
|
||||
{
|
||||
struct ci_hdrc *ci = dev_get_drvdata(dev);
|
||||
|
||||
dev_dbg(dev, "at %s\n", __func__);
|
||||
|
||||
ci_hdrc_enter_lpm(ci, false);
|
||||
|
||||
if (ci->usb_phy) {
|
||||
usb_phy_set_suspend(ci->usb_phy, 0);
|
||||
usb_phy_set_wakeup(ci->usb_phy, false);
|
||||
hw_wait_phy_stable();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ci_suspend(struct device *dev)
|
||||
{
|
||||
struct ci_hdrc *ci = dev_get_drvdata(dev);
|
||||
|
||||
if (ci->wq)
|
||||
flush_workqueue(ci->wq);
|
||||
|
||||
ci_controller_suspend(ci);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ci_resume(struct device *dev)
|
||||
{
|
||||
return ci_controller_resume(dev);
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static const struct dev_pm_ops ci_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(ci_suspend, ci_resume)
|
||||
};
|
||||
static struct platform_driver ci_hdrc_driver = {
|
||||
.probe = ci_hdrc_probe,
|
||||
.remove = ci_hdrc_remove,
|
||||
.driver = {
|
||||
.name = "ci_hdrc",
|
||||
.pm = &ci_pm_ops,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
@ -220,7 +220,7 @@ static int ci_otg_show(struct seq_file *s, void *unused)
|
||||
|
||||
/* ------ State ----- */
|
||||
seq_printf(s, "OTG state: %s\n\n",
|
||||
usb_otg_state_string(ci->transceiver->state));
|
||||
usb_otg_state_string(ci->otg.state));
|
||||
|
||||
/* ------ State Machine Variables ----- */
|
||||
seq_printf(s, "a_bus_drop: %d\n", fsm->a_bus_drop);
|
||||
|
@ -34,6 +34,44 @@
|
||||
|
||||
static struct hc_driver __read_mostly ci_ehci_hc_driver;
|
||||
|
||||
struct ehci_ci_priv {
|
||||
struct regulator *reg_vbus;
|
||||
};
|
||||
|
||||
static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
struct ehci_ci_priv *priv = (struct ehci_ci_priv *)ehci->priv;
|
||||
struct device *dev = hcd->self.controller;
|
||||
struct ci_hdrc *ci = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
int port = HCS_N_PORTS(ehci->hcs_params);
|
||||
|
||||
if (priv->reg_vbus && !ci_otg_is_fsm_mode(ci)) {
|
||||
if (port > 1) {
|
||||
dev_warn(dev,
|
||||
"Not support multi-port regulator control\n");
|
||||
return 0;
|
||||
}
|
||||
if (enable)
|
||||
ret = regulator_enable(priv->reg_vbus);
|
||||
else
|
||||
ret = regulator_disable(priv->reg_vbus);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"Failed to %s vbus regulator, ret=%d\n",
|
||||
enable ? "enable" : "disable", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
static const struct ehci_driver_overrides ehci_ci_overrides = {
|
||||
.extra_priv_size = sizeof(struct ehci_ci_priv),
|
||||
.port_power = ehci_ci_portpower,
|
||||
};
|
||||
|
||||
static irqreturn_t host_irq(struct ci_hdrc *ci)
|
||||
{
|
||||
return usb_hcd_irq(ci->irq, ci->hcd);
|
||||
@ -43,6 +81,7 @@ static int host_start(struct ci_hdrc *ci)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
struct ehci_hcd *ehci;
|
||||
struct ehci_ci_priv *priv;
|
||||
int ret;
|
||||
|
||||
if (usb_disabled())
|
||||
@ -52,15 +91,17 @@ static int host_start(struct ci_hdrc *ci)
|
||||
if (!hcd)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(ci->dev, ci);
|
||||
hcd->rsrc_start = ci->hw_bank.phys;
|
||||
hcd->rsrc_len = ci->hw_bank.size;
|
||||
hcd->regs = ci->hw_bank.abs;
|
||||
hcd->has_tt = 1;
|
||||
|
||||
hcd->power_budget = ci->platdata->power_budget;
|
||||
hcd->usb_phy = ci->transceiver;
|
||||
hcd->tpl_support = ci->platdata->tpl_support;
|
||||
if (ci->phy)
|
||||
hcd->phy = ci->phy;
|
||||
else
|
||||
hcd->usb_phy = ci->usb_phy;
|
||||
|
||||
ehci = hcd_to_ehci(hcd);
|
||||
ehci->caps = ci->hw_bank.cap;
|
||||
@ -68,28 +109,21 @@ static int host_start(struct ci_hdrc *ci)
|
||||
ehci->has_tdi_phy_lpm = ci->hw_bank.lpm;
|
||||
ehci->imx28_write_fix = ci->imx28_write_fix;
|
||||
|
||||
/*
|
||||
* vbus is always on if host is not in OTG FSM mode,
|
||||
* otherwise should be controlled by OTG FSM
|
||||
*/
|
||||
if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci)) {
|
||||
ret = regulator_enable(ci->platdata->reg_vbus);
|
||||
if (ret) {
|
||||
dev_err(ci->dev,
|
||||
"Failed to enable vbus regulator, ret=%d\n",
|
||||
ret);
|
||||
goto put_hcd;
|
||||
}
|
||||
}
|
||||
priv = (struct ehci_ci_priv *)ehci->priv;
|
||||
priv->reg_vbus = NULL;
|
||||
|
||||
if (ci->platdata->reg_vbus)
|
||||
priv->reg_vbus = ci->platdata->reg_vbus;
|
||||
|
||||
ret = usb_add_hcd(hcd, 0, 0);
|
||||
if (ret) {
|
||||
goto disable_reg;
|
||||
goto put_hcd;
|
||||
} else {
|
||||
struct usb_otg *otg = ci->transceiver->otg;
|
||||
struct usb_otg *otg = &ci->otg;
|
||||
|
||||
ci->hcd = hcd;
|
||||
if (otg) {
|
||||
|
||||
if (ci_otg_is_fsm_mode(ci)) {
|
||||
otg->host = &hcd->self;
|
||||
hcd->self.otg_port = 1;
|
||||
}
|
||||
@ -100,10 +134,6 @@ static int host_start(struct ci_hdrc *ci)
|
||||
|
||||
return ret;
|
||||
|
||||
disable_reg:
|
||||
if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci))
|
||||
regulator_disable(ci->platdata->reg_vbus);
|
||||
|
||||
put_hcd:
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
@ -117,8 +147,6 @@ static void host_stop(struct ci_hdrc *ci)
|
||||
if (hcd) {
|
||||
usb_remove_hcd(hcd);
|
||||
usb_put_hcd(hcd);
|
||||
if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci))
|
||||
regulator_disable(ci->platdata->reg_vbus);
|
||||
}
|
||||
}
|
||||
|
||||
@ -146,7 +174,7 @@ int ci_hdrc_host_init(struct ci_hdrc *ci)
|
||||
rdrv->name = "host";
|
||||
ci->roles[CI_ROLE_HOST] = rdrv;
|
||||
|
||||
ehci_init_driver(&ci_ehci_hc_driver, NULL);
|
||||
ehci_init_driver(&ci_ehci_hc_driver, &ehci_ci_overrides);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -303,7 +303,7 @@ static void a_wait_vfall_tmout_func(void *ptr, unsigned long indicator)
|
||||
set_tmout(ci, indicator);
|
||||
/* Disable port power */
|
||||
hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP, 0);
|
||||
/* Clear exsiting DP irq */
|
||||
/* Clear existing DP irq */
|
||||
hw_write_otgsc(ci, OTGSC_DPIS, OTGSC_DPIS);
|
||||
/* Enable data pulse irq */
|
||||
hw_write_otgsc(ci, OTGSC_DPIE, OTGSC_DPIE);
|
||||
@ -328,7 +328,7 @@ static void b_ssend_srp_tmout_func(void *ptr, unsigned long indicator)
|
||||
set_tmout(ci, indicator);
|
||||
|
||||
/* only vbus fall below B_sess_vld in b_idle state */
|
||||
if (ci->transceiver->state == OTG_STATE_B_IDLE)
|
||||
if (ci->fsm.otg->state == OTG_STATE_B_IDLE)
|
||||
ci_otg_queue_work(ci);
|
||||
}
|
||||
|
||||
@ -543,7 +543,7 @@ static int ci_otg_start_host(struct otg_fsm *fsm, int on)
|
||||
ci_role_start(ci, CI_ROLE_HOST);
|
||||
} else {
|
||||
ci_role_stop(ci);
|
||||
hw_device_reset(ci, USBMODE_CM_DC);
|
||||
hw_device_reset(ci);
|
||||
ci_role_start(ci, CI_ROLE_GADGET);
|
||||
}
|
||||
mutex_lock(&fsm->lock);
|
||||
@ -582,11 +582,11 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
|
||||
* when there is no gadget class driver
|
||||
*/
|
||||
if (ci->fsm.id && !(ci->driver) &&
|
||||
ci->transceiver->state < OTG_STATE_A_IDLE)
|
||||
ci->fsm.otg->state < OTG_STATE_A_IDLE)
|
||||
return 0;
|
||||
|
||||
if (otg_statemachine(&ci->fsm)) {
|
||||
if (ci->transceiver->state == OTG_STATE_A_IDLE) {
|
||||
if (ci->fsm.otg->state == OTG_STATE_A_IDLE) {
|
||||
/*
|
||||
* Further state change for cases:
|
||||
* a_idle to b_idle; or
|
||||
@ -600,7 +600,7 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
|
||||
ci_otg_queue_work(ci);
|
||||
if (ci->id_event)
|
||||
ci->id_event = false;
|
||||
} else if (ci->transceiver->state == OTG_STATE_B_IDLE) {
|
||||
} else if (ci->fsm.otg->state == OTG_STATE_B_IDLE) {
|
||||
if (ci->fsm.b_sess_vld) {
|
||||
ci->fsm.power_up = 0;
|
||||
/*
|
||||
@ -627,7 +627,7 @@ static void ci_otg_fsm_event(struct ci_hdrc *ci)
|
||||
otg_bsess_vld = hw_read_otgsc(ci, OTGSC_BSV);
|
||||
port_conn = hw_read(ci, OP_PORTSC, PORTSC_CCS);
|
||||
|
||||
switch (ci->transceiver->state) {
|
||||
switch (ci->fsm.otg->state) {
|
||||
case OTG_STATE_A_WAIT_BCON:
|
||||
if (port_conn) {
|
||||
fsm->b_conn = 1;
|
||||
@ -663,7 +663,7 @@ static void ci_otg_fsm_event(struct ci_hdrc *ci)
|
||||
fsm->b_bus_suspend = 1;
|
||||
/*
|
||||
* Init a timer to know how long this suspend
|
||||
* will contine, if time out, indicates B no longer
|
||||
* will continue, if time out, indicates B no longer
|
||||
* wants to be host role
|
||||
*/
|
||||
ci_otg_add_timer(ci, A_BIDL_ADIS);
|
||||
@ -778,34 +778,25 @@ void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci)
|
||||
int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci)
|
||||
{
|
||||
int retval = 0;
|
||||
struct usb_otg *otg;
|
||||
|
||||
otg = devm_kzalloc(ci->dev,
|
||||
sizeof(struct usb_otg), GFP_KERNEL);
|
||||
if (!otg) {
|
||||
dev_err(ci->dev,
|
||||
"Failed to allocate usb_otg structure for ci hdrc otg!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (ci->phy)
|
||||
ci->otg.phy = ci->phy;
|
||||
else
|
||||
ci->otg.usb_phy = ci->usb_phy;
|
||||
|
||||
otg->phy = ci->transceiver;
|
||||
otg->gadget = &ci->gadget;
|
||||
ci->fsm.otg = otg;
|
||||
ci->transceiver->otg = ci->fsm.otg;
|
||||
ci->otg.gadget = &ci->gadget;
|
||||
ci->fsm.otg = &ci->otg;
|
||||
ci->fsm.power_up = 1;
|
||||
ci->fsm.id = hw_read_otgsc(ci, OTGSC_ID) ? 1 : 0;
|
||||
ci->transceiver->state = OTG_STATE_UNDEFINED;
|
||||
ci->fsm.otg->state = OTG_STATE_UNDEFINED;
|
||||
ci->fsm.ops = &ci_otg_ops;
|
||||
|
||||
mutex_init(&ci->fsm.lock);
|
||||
|
||||
ci->fsm_timer = devm_kzalloc(ci->dev,
|
||||
sizeof(struct ci_otg_fsm_timer_list), GFP_KERNEL);
|
||||
if (!ci->fsm_timer) {
|
||||
dev_err(ci->dev,
|
||||
"Failed to allocate timer structure for ci hdrc otg!\n");
|
||||
if (!ci->fsm_timer)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&ci->fsm_timer->active_timers);
|
||||
retval = ci_otg_init_timers(ci);
|
||||
|
@ -692,10 +692,8 @@ __acquires(ci->lock)
|
||||
int retval;
|
||||
|
||||
spin_unlock(&ci->lock);
|
||||
if (ci->gadget.speed != USB_SPEED_UNKNOWN) {
|
||||
if (ci->driver)
|
||||
ci->driver->disconnect(&ci->gadget);
|
||||
}
|
||||
if (ci->gadget.speed != USB_SPEED_UNKNOWN)
|
||||
usb_gadget_udc_reset(&ci->gadget, ci->driver);
|
||||
|
||||
retval = _gadget_stop_activity(&ci->gadget);
|
||||
if (retval)
|
||||
@ -709,8 +707,6 @@ __acquires(ci->lock)
|
||||
if (ci->status == NULL)
|
||||
retval = -ENOMEM;
|
||||
|
||||
usb_gadget_set_state(&ci->gadget, USB_STATE_DEFAULT);
|
||||
|
||||
done:
|
||||
spin_lock(&ci->lock);
|
||||
|
||||
@ -1475,7 +1471,7 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
|
||||
if (gadget_ready) {
|
||||
if (is_active) {
|
||||
pm_runtime_get_sync(&_gadget->dev);
|
||||
hw_device_reset(ci, USBMODE_CM_DC);
|
||||
hw_device_reset(ci);
|
||||
hw_device_state(ci, ci->ep0out->qh.dma);
|
||||
usb_gadget_set_state(_gadget, USB_STATE_POWERED);
|
||||
} else {
|
||||
@ -1519,8 +1515,8 @@ static int ci_udc_vbus_draw(struct usb_gadget *_gadget, unsigned ma)
|
||||
{
|
||||
struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget);
|
||||
|
||||
if (ci->transceiver)
|
||||
return usb_phy_set_power(ci->transceiver, ma);
|
||||
if (ci->usb_phy)
|
||||
return usb_phy_set_power(ci->usb_phy, ma);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
@ -1544,8 +1540,7 @@ static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on)
|
||||
|
||||
static int ci_udc_start(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int ci_udc_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver);
|
||||
static int ci_udc_stop(struct usb_gadget *gadget);
|
||||
/**
|
||||
* Device operations part of the API to the USB controller hardware,
|
||||
* which don't involve endpoints (or i/o)
|
||||
@ -1665,7 +1660,7 @@ static int ci_udc_start(struct usb_gadget *gadget,
|
||||
pm_runtime_get_sync(&ci->gadget.dev);
|
||||
if (ci->vbus_active) {
|
||||
spin_lock_irqsave(&ci->lock, flags);
|
||||
hw_device_reset(ci, USBMODE_CM_DC);
|
||||
hw_device_reset(ci);
|
||||
} else {
|
||||
pm_runtime_put_sync(&ci->gadget.dev);
|
||||
return retval;
|
||||
@ -1682,8 +1677,7 @@ static int ci_udc_start(struct usb_gadget *gadget,
|
||||
/**
|
||||
* ci_udc_stop: unregister a gadget driver
|
||||
*/
|
||||
static int ci_udc_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int ci_udc_stop(struct usb_gadget *gadget)
|
||||
{
|
||||
struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget);
|
||||
unsigned long flags;
|
||||
|
@ -117,10 +117,9 @@ static int usbmisc_imx25_post(struct imx_usbmisc_data *data)
|
||||
if (data->index > 2)
|
||||
return -EINVAL;
|
||||
|
||||
reg = usbmisc->base + MX25_USB_PHY_CTRL_OFFSET;
|
||||
|
||||
if (data->evdo) {
|
||||
spin_lock_irqsave(&usbmisc->lock, flags);
|
||||
reg = usbmisc->base + MX25_USB_PHY_CTRL_OFFSET;
|
||||
val = readl(reg);
|
||||
writel(val | MX25_BM_EXTERNAL_VBUS_DIVIDER, reg);
|
||||
spin_unlock_irqrestore(&usbmisc->lock, flags);
|
||||
@ -172,8 +171,7 @@ static int usbmisc_imx53_init(struct imx_usbmisc_data *data)
|
||||
return -EINVAL;
|
||||
|
||||
/* Select a 24 MHz reference clock for the PHY */
|
||||
reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_1_OFFSET;
|
||||
val = readl(reg);
|
||||
val = readl(usbmisc->base + MX53_USB_OTG_PHY_CTRL_1_OFFSET);
|
||||
val &= ~MX53_USB_PHYCTRL1_PLLDIV_MASK;
|
||||
val |= MX53_USB_PLL_DIV_24_MHZ;
|
||||
writel(val, usbmisc->base + MX53_USB_OTG_PHY_CTRL_1_OFFSET);
|
||||
|
@ -1157,8 +1157,6 @@ static int acm_probe(struct usb_interface *intf,
|
||||
case USB_CDC_CALL_MANAGEMENT_TYPE:
|
||||
call_management_function = buffer[3];
|
||||
call_interface_num = buffer[4];
|
||||
if ((quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3)
|
||||
dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
|
||||
break;
|
||||
default:
|
||||
/* there are LOTS more CDC descriptors that
|
||||
@ -1197,10 +1195,11 @@ next_desc:
|
||||
} else {
|
||||
control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
|
||||
data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
|
||||
if (!control_interface || !data_interface) {
|
||||
dev_dbg(&intf->dev, "no interfaces\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
if (!control_interface || !data_interface) {
|
||||
dev_dbg(&intf->dev, "no interfaces\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (data_interface_num != call_interface_num)
|
||||
@ -1475,6 +1474,7 @@ alloc_fail8:
|
||||
&dev_attr_wCountryCodes);
|
||||
device_remove_file(&acm->control->dev,
|
||||
&dev_attr_iCountryCodeRelDate);
|
||||
kfree(acm->country_codes);
|
||||
}
|
||||
device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
|
||||
alloc_fail7:
|
||||
@ -1813,11 +1813,6 @@ static const struct usb_device_id acm_ids[] = {
|
||||
|
||||
/* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */
|
||||
|
||||
/* Support Lego NXT using pbLua firmware */
|
||||
{ USB_DEVICE(0x0694, 0xff00),
|
||||
.driver_info = NOT_A_MODEM,
|
||||
},
|
||||
|
||||
/* Support for Droids MuIn LCD */
|
||||
{ USB_DEVICE(0x04d8, 0x000b),
|
||||
.driver_info = NO_DATA_INTERFACE,
|
||||
|
@ -130,7 +130,6 @@ struct acm {
|
||||
#define NO_UNION_NORMAL BIT(0)
|
||||
#define SINGLE_RX_URB BIT(1)
|
||||
#define NO_CAP_LINE BIT(2)
|
||||
#define NOT_A_MODEM BIT(3)
|
||||
#define NO_DATA_INTERFACE BIT(4)
|
||||
#define IGNORE_DEVICE BIT(5)
|
||||
#define QUIRK_CONTROL_LINE_STATE BIT(6)
|
||||
|
@ -1104,10 +1104,8 @@ static int usbtmc_probe(struct usb_interface *intf,
|
||||
dev_dbg(&intf->dev, "%s called\n", __func__);
|
||||
|
||||
data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
dev_err(&intf->dev, "Unable to allocate kernel memory\n");
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
data->intf = intf;
|
||||
data->id = id;
|
||||
|
@ -124,10 +124,10 @@ static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
|
||||
static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
|
||||
{
|
||||
state_changed = 1;
|
||||
if (fsm->otg->phy->state == new_state)
|
||||
if (fsm->otg->state == new_state)
|
||||
return 0;
|
||||
VDBG("Set state: %s\n", usb_otg_state_string(new_state));
|
||||
otg_leave_state(fsm, fsm->otg->phy->state);
|
||||
otg_leave_state(fsm, fsm->otg->state);
|
||||
switch (new_state) {
|
||||
case OTG_STATE_B_IDLE:
|
||||
otg_drv_vbus(fsm, 0);
|
||||
@ -236,7 +236,7 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
|
||||
break;
|
||||
}
|
||||
|
||||
fsm->otg->phy->state = new_state;
|
||||
fsm->otg->state = new_state;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -247,7 +247,7 @@ int otg_statemachine(struct otg_fsm *fsm)
|
||||
|
||||
mutex_lock(&fsm->lock);
|
||||
|
||||
state = fsm->otg->phy->state;
|
||||
state = fsm->otg->state;
|
||||
state_changed = 0;
|
||||
/* State machine state change judgement */
|
||||
|
||||
|
@ -2646,7 +2646,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_GENERIC_PHY)) {
|
||||
if (IS_ENABLED(CONFIG_GENERIC_PHY) && !hcd->phy) {
|
||||
struct phy *phy = phy_get(hcd->self.controller, "usb");
|
||||
|
||||
if (IS_ERR(phy)) {
|
||||
@ -2666,6 +2666,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
|
||||
goto err_phy;
|
||||
}
|
||||
hcd->phy = phy;
|
||||
hcd->remove_phy = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2812,7 +2813,7 @@ err_allocate_root_hub:
|
||||
err_register_bus:
|
||||
hcd_buffer_destroy(hcd);
|
||||
err_create_buf:
|
||||
if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->phy) {
|
||||
if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->remove_phy && hcd->phy) {
|
||||
phy_power_off(hcd->phy);
|
||||
phy_exit(hcd->phy);
|
||||
phy_put(hcd->phy);
|
||||
@ -2896,7 +2897,7 @@ void usb_remove_hcd(struct usb_hcd *hcd)
|
||||
usb_deregister_bus(&hcd->self);
|
||||
hcd_buffer_destroy(hcd);
|
||||
|
||||
if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->phy) {
|
||||
if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->remove_phy && hcd->phy) {
|
||||
phy_power_off(hcd->phy);
|
||||
phy_exit(hcd->phy);
|
||||
phy_put(hcd->phy);
|
||||
|
@ -2543,11 +2543,14 @@ int usb_authorize_device(struct usb_device *usb_dev)
|
||||
"can't autoresume for authorization: %d\n", result);
|
||||
goto error_autoresume;
|
||||
}
|
||||
result = usb_get_device_descriptor(usb_dev, sizeof(usb_dev->descriptor));
|
||||
if (result < 0) {
|
||||
dev_err(&usb_dev->dev, "can't re-read device descriptor for "
|
||||
"authorization: %d\n", result);
|
||||
goto error_device_descriptor;
|
||||
|
||||
if (usb_dev->wusb) {
|
||||
result = usb_get_device_descriptor(usb_dev, sizeof(usb_dev->descriptor));
|
||||
if (result < 0) {
|
||||
dev_err(&usb_dev->dev, "can't re-read device descriptor for "
|
||||
"authorization: %d\n", result);
|
||||
goto error_device_descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
usb_dev->authorized = 1;
|
||||
@ -3907,14 +3910,9 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
|
||||
static int usb_disable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
|
||||
enum usb3_link_state state)
|
||||
{
|
||||
int feature;
|
||||
|
||||
switch (state) {
|
||||
case USB3_LPM_U1:
|
||||
feature = USB_PORT_FEAT_U1_TIMEOUT;
|
||||
break;
|
||||
case USB3_LPM_U2:
|
||||
feature = USB_PORT_FEAT_U2_TIMEOUT;
|
||||
break;
|
||||
default:
|
||||
dev_warn(&udev->dev, "%s: Can't disable non-U1 or U2 state.\n",
|
||||
|
@ -1,6 +1,6 @@
|
||||
config USB_DWC2
|
||||
bool "DesignWare USB2 DRD Core Support"
|
||||
depends on USB
|
||||
tristate "DesignWare USB2 DRD Core Support"
|
||||
depends on USB || USB_GADGET
|
||||
help
|
||||
Say Y here if your system has a Dual Role Hi-Speed USB
|
||||
controller based on the DesignWare HSOTG IP Core.
|
||||
@ -10,49 +10,61 @@ config USB_DWC2
|
||||
bus interface module (if you have a PCI bus system) will be
|
||||
called dwc2_pci.ko, and the platform interface module (for
|
||||
controllers directly connected to the CPU) will be called
|
||||
dwc2_platform.ko. For gadget mode, there will be a single
|
||||
module called dwc2_gadget.ko.
|
||||
|
||||
NOTE: The s3c-hsotg driver is now renamed to dwc2_gadget. The
|
||||
host and gadget drivers are still currently separate drivers.
|
||||
There are plans to merge the dwc2_gadget driver with the dwc2
|
||||
host driver in the near future to create a dual-role driver.
|
||||
dwc2_platform.ko. For all modes(host, gadget and dual-role), there
|
||||
will be an additional module named dwc2.ko.
|
||||
|
||||
if USB_DWC2
|
||||
|
||||
choice
|
||||
bool "DWC2 Mode Selection"
|
||||
default USB_DWC2_DUAL_ROLE if (USB && USB_GADGET)
|
||||
default USB_DWC2_HOST if (USB && !USB_GADGET)
|
||||
default USB_DWC2_PERIPHERAL if (!USB && USB_GADGET)
|
||||
|
||||
config USB_DWC2_HOST
|
||||
tristate "Host only mode"
|
||||
bool "Host only mode"
|
||||
depends on USB
|
||||
help
|
||||
The Designware USB2.0 high-speed host controller
|
||||
integrated into many SoCs.
|
||||
integrated into many SoCs. Select this option if you want the
|
||||
driver to operate in Host-only mode.
|
||||
|
||||
comment "Gadget/Dual-role mode requires USB Gadget support to be enabled"
|
||||
|
||||
config USB_DWC2_PERIPHERAL
|
||||
bool "Gadget only mode"
|
||||
depends on USB_GADGET=y || USB_GADGET=USB_DWC2
|
||||
help
|
||||
The Designware USB2.0 high-speed gadget controller
|
||||
integrated into many SoCs. Select this option if you want the
|
||||
driver to operate in Peripheral-only mode. This option requires
|
||||
USB_GADGET to be enabled.
|
||||
|
||||
config USB_DWC2_DUAL_ROLE
|
||||
bool "Dual Role mode"
|
||||
depends on (USB=y || USB=USB_DWC2) && (USB_GADGET=y || USB_GADGET=USB_DWC2)
|
||||
help
|
||||
Select this option if you want the driver to work in a dual-role
|
||||
mode. In this mode both host and gadget features are enabled, and
|
||||
the role will be determined by the cable that gets plugged-in. This
|
||||
option requires USB_GADGET to be enabled.
|
||||
endchoice
|
||||
|
||||
config USB_DWC2_PLATFORM
|
||||
bool "DWC2 Platform"
|
||||
depends on USB_DWC2_HOST
|
||||
default USB_DWC2_HOST
|
||||
help
|
||||
The Designware USB2.0 platform interface module for
|
||||
controllers directly connected to the CPU. This is only
|
||||
used for host mode.
|
||||
tristate "DWC2 Platform"
|
||||
default USB_DWC2_HOST || USB_DWC2_PERIPHERAL
|
||||
help
|
||||
The Designware USB2.0 platform interface module for
|
||||
controllers directly connected to the CPU.
|
||||
|
||||
config USB_DWC2_PCI
|
||||
bool "DWC2 PCI"
|
||||
tristate "DWC2 PCI"
|
||||
depends on USB_DWC2_HOST && PCI
|
||||
default USB_DWC2_HOST
|
||||
help
|
||||
The Designware USB2.0 PCI interface module for controllers
|
||||
connected to a PCI bus. This is only used for host mode.
|
||||
|
||||
comment "Gadget mode requires USB Gadget support to be enabled"
|
||||
|
||||
config USB_DWC2_PERIPHERAL
|
||||
tristate "Gadget only mode"
|
||||
depends on USB_GADGET
|
||||
help
|
||||
The Designware USB2.0 high-speed gadget controller
|
||||
integrated into many SoCs.
|
||||
|
||||
config USB_DWC2_DEBUG
|
||||
bool "Enable Debugging Messages"
|
||||
help
|
||||
|
@ -1,28 +1,28 @@
|
||||
ccflags-$(CONFIG_USB_DWC2_DEBUG) += -DDEBUG
|
||||
ccflags-$(CONFIG_USB_DWC2_VERBOSE) += -DVERBOSE_DEBUG
|
||||
|
||||
obj-$(CONFIG_USB_DWC2_HOST) += dwc2.o
|
||||
obj-$(CONFIG_USB_DWC2) += dwc2.o
|
||||
dwc2-y := core.o core_intr.o
|
||||
dwc2-y += hcd.o hcd_intr.o
|
||||
dwc2-y += hcd_queue.o hcd_ddma.o
|
||||
|
||||
ifneq ($(filter y,$(CONFIG_USB_DWC2_HOST) $(CONFIG_USB_DWC2_DUAL_ROLE)),)
|
||||
dwc2-y += hcd.o hcd_intr.o
|
||||
dwc2-y += hcd_queue.o hcd_ddma.o
|
||||
endif
|
||||
|
||||
ifneq ($(filter y,$(CONFIG_USB_DWC2_PERIPHERAL) $(CONFIG_USB_DWC2_DUAL_ROLE)),)
|
||||
dwc2-y += gadget.o
|
||||
endif
|
||||
|
||||
# NOTE: The previous s3c-hsotg peripheral mode only driver has been moved to
|
||||
# this location and renamed gadget.c. When building for dynamically linked
|
||||
# modules, dwc2_gadget.ko will get built for peripheral mode. For host mode,
|
||||
# the core module will be dwc2.ko, the PCI bus interface module will called
|
||||
# dwc2_pci.ko and the platform interface module will be called dwc2_platform.ko.
|
||||
# At present the host and gadget driver will be separate drivers, but there
|
||||
# are plans in the near future to create a dual-role driver.
|
||||
# modules, dwc2.ko will get built for host mode, peripheral mode, and dual-role
|
||||
# mode. The PCI bus interface module will called dwc2_pci.ko and the platform
|
||||
# interface module will be called dwc2_platform.ko.
|
||||
|
||||
ifneq ($(CONFIG_USB_DWC2_PCI),)
|
||||
obj-$(CONFIG_USB_DWC2_HOST) += dwc2_pci.o
|
||||
obj-$(CONFIG_USB_DWC2) += dwc2_pci.o
|
||||
dwc2_pci-y := pci.o
|
||||
endif
|
||||
|
||||
ifneq ($(CONFIG_USB_DWC2_PLATFORM),)
|
||||
obj-$(CONFIG_USB_DWC2_HOST) += dwc2_platform.o
|
||||
dwc2_platform-y := platform.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_USB_DWC2_PERIPHERAL) += dwc2_gadget.o
|
||||
dwc2_gadget-y := gadget.o
|
||||
obj-$(CONFIG_USB_DWC2_PLATFORM) += dwc2_platform.o
|
||||
dwc2_platform-y := platform.o
|
||||
|
@ -458,16 +458,6 @@ int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy, int irq)
|
||||
/* Clear the SRP success bit for FS-I2c */
|
||||
hsotg->srp_success = 0;
|
||||
|
||||
if (irq >= 0) {
|
||||
dev_dbg(hsotg->dev, "registering common handler for irq%d\n",
|
||||
irq);
|
||||
retval = devm_request_irq(hsotg->dev, irq,
|
||||
dwc2_handle_common_intr, IRQF_SHARED,
|
||||
dev_name(hsotg->dev), hsotg);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Enable common interrupts */
|
||||
dwc2_enable_common_interrupts(hsotg);
|
||||
|
||||
|
@ -84,7 +84,7 @@ static const char * const s3c_hsotg_supply_names[] = {
|
||||
*/
|
||||
#define EP0_MPS_LIMIT 64
|
||||
|
||||
struct s3c_hsotg;
|
||||
struct dwc2_hsotg;
|
||||
struct s3c_hsotg_req;
|
||||
|
||||
/**
|
||||
@ -130,7 +130,7 @@ struct s3c_hsotg_req;
|
||||
struct s3c_hsotg_ep {
|
||||
struct usb_ep ep;
|
||||
struct list_head queue;
|
||||
struct s3c_hsotg *parent;
|
||||
struct dwc2_hsotg *parent;
|
||||
struct s3c_hsotg_req *req;
|
||||
struct dentry *debugfs;
|
||||
|
||||
@ -154,67 +154,6 @@ struct s3c_hsotg_ep {
|
||||
char name[10];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct s3c_hsotg - driver state.
|
||||
* @dev: The parent device supplied to the probe function
|
||||
* @driver: USB gadget driver
|
||||
* @phy: The otg phy transceiver structure for phy control.
|
||||
* @uphy: The otg phy transceiver structure for old USB phy control.
|
||||
* @plat: The platform specific configuration data. This can be removed once
|
||||
* all SoCs support usb transceiver.
|
||||
* @regs: The memory area mapped for accessing registers.
|
||||
* @irq: The IRQ number we are using
|
||||
* @supplies: Definition of USB power supplies
|
||||
* @phyif: PHY interface width
|
||||
* @dedicated_fifos: Set if the hardware has dedicated IN-EP fifos.
|
||||
* @num_of_eps: Number of available EPs (excluding EP0)
|
||||
* @debug_root: root directrory for debugfs.
|
||||
* @debug_file: main status file for debugfs.
|
||||
* @debug_fifo: FIFO status file for debugfs.
|
||||
* @ep0_reply: Request used for ep0 reply.
|
||||
* @ep0_buff: Buffer for EP0 reply data, if needed.
|
||||
* @ctrl_buff: Buffer for EP0 control requests.
|
||||
* @ctrl_req: Request for EP0 control packets.
|
||||
* @setup: NAK management for EP0 SETUP
|
||||
* @last_rst: Time of last reset
|
||||
* @eps: The endpoints being supplied to the gadget framework
|
||||
*/
|
||||
struct s3c_hsotg {
|
||||
struct device *dev;
|
||||
struct usb_gadget_driver *driver;
|
||||
struct phy *phy;
|
||||
struct usb_phy *uphy;
|
||||
struct s3c_hsotg_plat *plat;
|
||||
|
||||
spinlock_t lock;
|
||||
|
||||
void __iomem *regs;
|
||||
int irq;
|
||||
struct clk *clk;
|
||||
|
||||
struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsotg_supply_names)];
|
||||
|
||||
u32 phyif;
|
||||
int fifo_mem;
|
||||
unsigned int dedicated_fifos:1;
|
||||
unsigned char num_of_eps;
|
||||
u32 fifo_map;
|
||||
|
||||
struct dentry *debug_root;
|
||||
struct dentry *debug_file;
|
||||
struct dentry *debug_fifo;
|
||||
|
||||
struct usb_request *ep0_reply;
|
||||
struct usb_request *ctrl_req;
|
||||
u8 ep0_buff[8];
|
||||
u8 ctrl_buff[8];
|
||||
|
||||
struct usb_gadget gadget;
|
||||
unsigned int setup;
|
||||
unsigned long last_rst;
|
||||
struct s3c_hsotg_ep *eps;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct s3c_hsotg_req - data transfer request
|
||||
* @req: The USB gadget request
|
||||
@ -229,6 +168,7 @@ struct s3c_hsotg_req {
|
||||
unsigned char mapped;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
|
||||
#define call_gadget(_hs, _entry) \
|
||||
do { \
|
||||
if ((_hs)->gadget.speed != USB_SPEED_UNKNOWN && \
|
||||
@ -238,6 +178,9 @@ do { \
|
||||
spin_lock(&_hs->lock); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define call_gadget(_hs, _entry) do {} while (0)
|
||||
#endif
|
||||
|
||||
struct dwc2_hsotg;
|
||||
struct dwc2_host_chan;
|
||||
@ -495,11 +438,13 @@ struct dwc2_hw_params {
|
||||
* struct dwc2_hsotg - Holds the state of the driver, including the non-periodic
|
||||
* and periodic schedules
|
||||
*
|
||||
* These are common for both host and peripheral modes:
|
||||
*
|
||||
* @dev: The struct device pointer
|
||||
* @regs: Pointer to controller regs
|
||||
* @core_params: Parameters that define how the core should be configured
|
||||
* @hw_params: Parameters that were autodetected from the
|
||||
* hardware registers
|
||||
* @core_params: Parameters that define how the core should be configured
|
||||
* @op_state: The operational State, during transitions (a_host=>
|
||||
* a_peripheral and b_device=>b_host) this may not match
|
||||
* the core, but allows the software to determine
|
||||
@ -508,6 +453,8 @@ struct dwc2_hw_params {
|
||||
* - USB_DR_MODE_PERIPHERAL
|
||||
* - USB_DR_MODE_HOST
|
||||
* - USB_DR_MODE_OTG
|
||||
* @lock: Spinlock that protects all the driver data structures
|
||||
* @priv: Stores a pointer to the struct usb_hcd
|
||||
* @queuing_high_bandwidth: True if multiple packets of a high-bandwidth
|
||||
* transfer are in process of being queued
|
||||
* @srp_success: Stores status of SRP request in the case of a FS PHY
|
||||
@ -517,6 +464,9 @@ struct dwc2_hw_params {
|
||||
* interrupt
|
||||
* @wkp_timer: Timer object for handling Wakeup Detected interrupt
|
||||
* @lx_state: Lx state of connected device
|
||||
*
|
||||
* These are for host mode:
|
||||
*
|
||||
* @flags: Flags for handling root port state changes
|
||||
* @non_periodic_sched_inactive: Inactive QHs in the non-periodic schedule.
|
||||
* Transfers associated with these QHs are not currently
|
||||
@ -585,11 +535,31 @@ struct dwc2_hw_params {
|
||||
* @status_buf_dma: DMA address for status_buf
|
||||
* @start_work: Delayed work for handling host A-cable connection
|
||||
* @reset_work: Delayed work for handling a port reset
|
||||
* @lock: Spinlock that protects all the driver data structures
|
||||
* @priv: Stores a pointer to the struct usb_hcd
|
||||
* @otg_port: OTG port number
|
||||
* @frame_list: Frame list
|
||||
* @frame_list_dma: Frame list DMA address
|
||||
*
|
||||
* These are for peripheral mode:
|
||||
*
|
||||
* @driver: USB gadget driver
|
||||
* @phy: The otg phy transceiver structure for phy control.
|
||||
* @uphy: The otg phy transceiver structure for old USB phy control.
|
||||
* @plat: The platform specific configuration data. This can be removed once
|
||||
* all SoCs support usb transceiver.
|
||||
* @supplies: Definition of USB power supplies
|
||||
* @phyif: PHY interface width
|
||||
* @dedicated_fifos: Set if the hardware has dedicated IN-EP fifos.
|
||||
* @num_of_eps: Number of available EPs (excluding EP0)
|
||||
* @debug_root: Root directrory for debugfs.
|
||||
* @debug_file: Main status file for debugfs.
|
||||
* @debug_fifo: FIFO status file for debugfs.
|
||||
* @ep0_reply: Request used for ep0 reply.
|
||||
* @ep0_buff: Buffer for EP0 reply data, if needed.
|
||||
* @ctrl_buff: Buffer for EP0 control requests.
|
||||
* @ctrl_req: Request for EP0 control packets.
|
||||
* @setup: NAK management for EP0 SETUP
|
||||
* @last_rst: Time of last reset
|
||||
* @eps: The endpoints being supplied to the gadget framework
|
||||
*/
|
||||
struct dwc2_hsotg {
|
||||
struct device *dev;
|
||||
@ -601,6 +571,16 @@ struct dwc2_hsotg {
|
||||
enum usb_otg_state op_state;
|
||||
enum usb_dr_mode dr_mode;
|
||||
|
||||
struct phy *phy;
|
||||
struct usb_phy *uphy;
|
||||
struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsotg_supply_names)];
|
||||
|
||||
spinlock_t lock;
|
||||
struct mutex init_mutex;
|
||||
void *priv;
|
||||
int irq;
|
||||
struct clk *clk;
|
||||
|
||||
unsigned int queuing_high_bandwidth:1;
|
||||
unsigned int srp_success:1;
|
||||
|
||||
@ -609,6 +589,18 @@ struct dwc2_hsotg {
|
||||
struct timer_list wkp_timer;
|
||||
enum dwc2_lx_state lx_state;
|
||||
|
||||
struct dentry *debug_root;
|
||||
struct dentry *debug_file;
|
||||
struct dentry *debug_fifo;
|
||||
|
||||
/* DWC OTG HW Release versions */
|
||||
#define DWC2_CORE_REV_2_71a 0x4f54271a
|
||||
#define DWC2_CORE_REV_2_90a 0x4f54290a
|
||||
#define DWC2_CORE_REV_2_92a 0x4f54292a
|
||||
#define DWC2_CORE_REV_2_94a 0x4f54294a
|
||||
#define DWC2_CORE_REV_3_00a 0x4f54300a
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
|
||||
union dwc2_hcd_internal_flags {
|
||||
u32 d32;
|
||||
struct {
|
||||
@ -655,19 +647,10 @@ struct dwc2_hsotg {
|
||||
|
||||
struct delayed_work start_work;
|
||||
struct delayed_work reset_work;
|
||||
spinlock_t lock;
|
||||
void *priv;
|
||||
u8 otg_port;
|
||||
u32 *frame_list;
|
||||
dma_addr_t frame_list_dma;
|
||||
|
||||
/* DWC OTG HW Release versions */
|
||||
#define DWC2_CORE_REV_2_71a 0x4f54271a
|
||||
#define DWC2_CORE_REV_2_90a 0x4f54290a
|
||||
#define DWC2_CORE_REV_2_92a 0x4f54292a
|
||||
#define DWC2_CORE_REV_2_94a 0x4f54294a
|
||||
#define DWC2_CORE_REV_3_00a 0x4f54300a
|
||||
|
||||
#ifdef DEBUG
|
||||
u32 frrem_samples;
|
||||
u64 frrem_accum;
|
||||
@ -686,6 +669,31 @@ struct dwc2_hsotg {
|
||||
u32 hfnum_other_samples_b;
|
||||
u64 hfnum_other_frrem_accum_b;
|
||||
#endif
|
||||
#endif /* CONFIG_USB_DWC2_HOST || CONFIG_USB_DWC2_DUAL_ROLE */
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
|
||||
/* Gadget structures */
|
||||
struct usb_gadget_driver *driver;
|
||||
struct s3c_hsotg_plat *plat;
|
||||
|
||||
u32 phyif;
|
||||
int fifo_mem;
|
||||
unsigned int dedicated_fifos:1;
|
||||
unsigned char num_of_eps;
|
||||
u32 fifo_map;
|
||||
|
||||
struct usb_request *ep0_reply;
|
||||
struct usb_request *ctrl_req;
|
||||
u8 ep0_buff[8];
|
||||
u8 ctrl_buff[8];
|
||||
|
||||
struct usb_gadget gadget;
|
||||
unsigned int enabled:1;
|
||||
unsigned int connected:1;
|
||||
unsigned int setup:1;
|
||||
unsigned long last_rst;
|
||||
struct s3c_hsotg_ep *eps;
|
||||
#endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */
|
||||
};
|
||||
|
||||
/* Reasons for halting a host channel */
|
||||
@ -955,4 +963,43 @@ extern void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg);
|
||||
*/
|
||||
extern u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg);
|
||||
|
||||
/* Gadget defines */
|
||||
#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
|
||||
extern int s3c_hsotg_remove(struct dwc2_hsotg *hsotg);
|
||||
extern int s3c_hsotg_suspend(struct dwc2_hsotg *dwc2);
|
||||
extern int s3c_hsotg_resume(struct dwc2_hsotg *dwc2);
|
||||
extern int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq);
|
||||
extern void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2);
|
||||
extern void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg);
|
||||
extern void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2);
|
||||
#else
|
||||
static inline int s3c_hsotg_remove(struct dwc2_hsotg *dwc2)
|
||||
{ return 0; }
|
||||
static inline int s3c_hsotg_suspend(struct dwc2_hsotg *dwc2)
|
||||
{ return 0; }
|
||||
static inline int s3c_hsotg_resume(struct dwc2_hsotg *dwc2)
|
||||
{ return 0; }
|
||||
static inline int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
|
||||
{ return 0; }
|
||||
static inline void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2) {}
|
||||
static inline void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg) {}
|
||||
static inline void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2) {}
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
|
||||
extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg);
|
||||
extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg);
|
||||
extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
|
||||
#else
|
||||
static inline void dwc2_set_all_params(struct dwc2_core_params *params, int value) {}
|
||||
static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg) {}
|
||||
static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {}
|
||||
static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {}
|
||||
static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq,
|
||||
const struct dwc2_core_params *params)
|
||||
{ return 0; }
|
||||
#endif
|
||||
|
||||
#endif /* __DWC2_CORE_H__ */
|
||||
|
@ -128,6 +128,9 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
|
||||
dwc2_op_state_str(hsotg));
|
||||
gotgctl = readl(hsotg->regs + GOTGCTL);
|
||||
|
||||
if (dwc2_is_device_mode(hsotg))
|
||||
s3c_hsotg_disconnect(hsotg);
|
||||
|
||||
if (hsotg->op_state == OTG_STATE_B_HOST) {
|
||||
hsotg->op_state = OTG_STATE_B_PERIPHERAL;
|
||||
} else {
|
||||
@ -287,9 +290,11 @@ static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg)
|
||||
* Release lock before scheduling workq as it holds spinlock during
|
||||
* scheduling.
|
||||
*/
|
||||
spin_unlock(&hsotg->lock);
|
||||
queue_work(hsotg->wq_otg, &hsotg->wf_otg);
|
||||
spin_lock(&hsotg->lock);
|
||||
if (hsotg->wq_otg) {
|
||||
spin_unlock(&hsotg->lock);
|
||||
queue_work(hsotg->wq_otg, &hsotg->wf_otg);
|
||||
spin_lock(&hsotg->lock);
|
||||
}
|
||||
|
||||
/* Clear interrupt */
|
||||
writel(GINTSTS_CONIDSTSCHNG, hsotg->regs + GINTSTS);
|
||||
@ -312,6 +317,12 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
|
||||
|
||||
/* Clear interrupt */
|
||||
writel(GINTSTS_SESSREQINT, hsotg->regs + GINTSTS);
|
||||
|
||||
/*
|
||||
* Report disconnect if there is any previous session established
|
||||
*/
|
||||
if (dwc2_is_device_mode(hsotg))
|
||||
s3c_hsotg_disconnect(hsotg);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
@ -36,6 +37,7 @@
|
||||
#include <linux/platform_data/s3c-hsotg.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "hw.h"
|
||||
|
||||
/* conversion functions */
|
||||
static inline struct s3c_hsotg_req *our_req(struct usb_request *req)
|
||||
@ -48,9 +50,9 @@ static inline struct s3c_hsotg_ep *our_ep(struct usb_ep *ep)
|
||||
return container_of(ep, struct s3c_hsotg_ep, ep);
|
||||
}
|
||||
|
||||
static inline struct s3c_hsotg *to_hsotg(struct usb_gadget *gadget)
|
||||
static inline struct dwc2_hsotg *to_hsotg(struct usb_gadget *gadget)
|
||||
{
|
||||
return container_of(gadget, struct s3c_hsotg, gadget);
|
||||
return container_of(gadget, struct dwc2_hsotg, gadget);
|
||||
}
|
||||
|
||||
static inline void __orr32(void __iomem *ptr, u32 val)
|
||||
@ -64,7 +66,7 @@ static inline void __bic32(void __iomem *ptr, u32 val)
|
||||
}
|
||||
|
||||
/* forward decleration of functions */
|
||||
static void s3c_hsotg_dump(struct s3c_hsotg *hsotg);
|
||||
static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg);
|
||||
|
||||
/**
|
||||
* using_dma - return the DMA status of the driver.
|
||||
@ -85,7 +87,7 @@ static void s3c_hsotg_dump(struct s3c_hsotg *hsotg);
|
||||
*
|
||||
* Until this issue is sorted out, we always return 'false'.
|
||||
*/
|
||||
static inline bool using_dma(struct s3c_hsotg *hsotg)
|
||||
static inline bool using_dma(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
return false; /* support is not complete */
|
||||
}
|
||||
@ -95,7 +97,7 @@ static inline bool using_dma(struct s3c_hsotg *hsotg)
|
||||
* @hsotg: The device state
|
||||
* @ints: A bitmask of the interrupts to enable
|
||||
*/
|
||||
static void s3c_hsotg_en_gsint(struct s3c_hsotg *hsotg, u32 ints)
|
||||
static void s3c_hsotg_en_gsint(struct dwc2_hsotg *hsotg, u32 ints)
|
||||
{
|
||||
u32 gsintmsk = readl(hsotg->regs + GINTMSK);
|
||||
u32 new_gsintmsk;
|
||||
@ -113,7 +115,7 @@ static void s3c_hsotg_en_gsint(struct s3c_hsotg *hsotg, u32 ints)
|
||||
* @hsotg: The device state
|
||||
* @ints: A bitmask of the interrupts to enable
|
||||
*/
|
||||
static void s3c_hsotg_disable_gsint(struct s3c_hsotg *hsotg, u32 ints)
|
||||
static void s3c_hsotg_disable_gsint(struct dwc2_hsotg *hsotg, u32 ints)
|
||||
{
|
||||
u32 gsintmsk = readl(hsotg->regs + GINTMSK);
|
||||
u32 new_gsintmsk;
|
||||
@ -134,7 +136,7 @@ static void s3c_hsotg_disable_gsint(struct s3c_hsotg *hsotg, u32 ints)
|
||||
* Set or clear the mask for an individual endpoint's interrupt
|
||||
* request.
|
||||
*/
|
||||
static void s3c_hsotg_ctrl_epint(struct s3c_hsotg *hsotg,
|
||||
static void s3c_hsotg_ctrl_epint(struct dwc2_hsotg *hsotg,
|
||||
unsigned int ep, unsigned int dir_in,
|
||||
unsigned int en)
|
||||
{
|
||||
@ -159,7 +161,7 @@ static void s3c_hsotg_ctrl_epint(struct s3c_hsotg *hsotg,
|
||||
* s3c_hsotg_init_fifo - initialise non-periodic FIFOs
|
||||
* @hsotg: The device instance.
|
||||
*/
|
||||
static void s3c_hsotg_init_fifo(struct s3c_hsotg *hsotg)
|
||||
static void s3c_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
unsigned int ep;
|
||||
unsigned int addr;
|
||||
@ -283,7 +285,7 @@ static inline int is_ep_periodic(struct s3c_hsotg_ep *hs_ep)
|
||||
* This is the reverse of s3c_hsotg_map_dma(), called for the completion
|
||||
* of a request to ensure the buffer is ready for access by the caller.
|
||||
*/
|
||||
static void s3c_hsotg_unmap_dma(struct s3c_hsotg *hsotg,
|
||||
static void s3c_hsotg_unmap_dma(struct dwc2_hsotg *hsotg,
|
||||
struct s3c_hsotg_ep *hs_ep,
|
||||
struct s3c_hsotg_req *hs_req)
|
||||
{
|
||||
@ -312,7 +314,7 @@ static void s3c_hsotg_unmap_dma(struct s3c_hsotg *hsotg,
|
||||
*
|
||||
* This routine is only needed for PIO
|
||||
*/
|
||||
static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
|
||||
static int s3c_hsotg_write_fifo(struct dwc2_hsotg *hsotg,
|
||||
struct s3c_hsotg_ep *hs_ep,
|
||||
struct s3c_hsotg_req *hs_req)
|
||||
{
|
||||
@ -517,7 +519,7 @@ static unsigned get_ep_limit(struct s3c_hsotg_ep *hs_ep)
|
||||
* Start the given request running by setting the endpoint registers
|
||||
* appropriately, and writing any data to the FIFOs.
|
||||
*/
|
||||
static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg,
|
||||
static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg,
|
||||
struct s3c_hsotg_ep *hs_ep,
|
||||
struct s3c_hsotg_req *hs_req,
|
||||
bool continuing)
|
||||
@ -707,7 +709,7 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg,
|
||||
* DMA memory, then we map the memory and mark our request to allow us to
|
||||
* cleanup on completion.
|
||||
*/
|
||||
static int s3c_hsotg_map_dma(struct s3c_hsotg *hsotg,
|
||||
static int s3c_hsotg_map_dma(struct dwc2_hsotg *hsotg,
|
||||
struct s3c_hsotg_ep *hs_ep,
|
||||
struct usb_request *req)
|
||||
{
|
||||
@ -736,7 +738,7 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
|
||||
{
|
||||
struct s3c_hsotg_req *hs_req = our_req(req);
|
||||
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
|
||||
struct s3c_hsotg *hs = hs_ep->parent;
|
||||
struct dwc2_hsotg *hs = hs_ep->parent;
|
||||
bool first;
|
||||
|
||||
dev_dbg(hs->dev, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n",
|
||||
@ -768,7 +770,7 @@ static int s3c_hsotg_ep_queue_lock(struct usb_ep *ep, struct usb_request *req,
|
||||
gfp_t gfp_flags)
|
||||
{
|
||||
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
|
||||
struct s3c_hsotg *hs = hs_ep->parent;
|
||||
struct dwc2_hsotg *hs = hs_ep->parent;
|
||||
unsigned long flags = 0;
|
||||
int ret = 0;
|
||||
|
||||
@ -799,7 +801,7 @@ static void s3c_hsotg_complete_oursetup(struct usb_ep *ep,
|
||||
struct usb_request *req)
|
||||
{
|
||||
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
|
||||
struct s3c_hsotg *hsotg = hs_ep->parent;
|
||||
struct dwc2_hsotg *hsotg = hs_ep->parent;
|
||||
|
||||
dev_dbg(hsotg->dev, "%s: ep %p, req %p\n", __func__, ep, req);
|
||||
|
||||
@ -814,7 +816,7 @@ static void s3c_hsotg_complete_oursetup(struct usb_ep *ep,
|
||||
* Convert the given wIndex into a pointer to an driver endpoint
|
||||
* structure, or return NULL if it is not a valid endpoint.
|
||||
*/
|
||||
static struct s3c_hsotg_ep *ep_from_windex(struct s3c_hsotg *hsotg,
|
||||
static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg,
|
||||
u32 windex)
|
||||
{
|
||||
struct s3c_hsotg_ep *ep = &hsotg->eps[windex & 0x7F];
|
||||
@ -843,7 +845,7 @@ static struct s3c_hsotg_ep *ep_from_windex(struct s3c_hsotg *hsotg,
|
||||
* Create a request and queue it on the given endpoint. This is useful as
|
||||
* an internal method of sending replies to certain control requests, etc.
|
||||
*/
|
||||
static int s3c_hsotg_send_reply(struct s3c_hsotg *hsotg,
|
||||
static int s3c_hsotg_send_reply(struct dwc2_hsotg *hsotg,
|
||||
struct s3c_hsotg_ep *ep,
|
||||
void *buff,
|
||||
int length)
|
||||
@ -884,7 +886,7 @@ static int s3c_hsotg_send_reply(struct s3c_hsotg *hsotg,
|
||||
* @hsotg: The device state
|
||||
* @ctrl: USB control request
|
||||
*/
|
||||
static int s3c_hsotg_process_req_status(struct s3c_hsotg *hsotg,
|
||||
static int s3c_hsotg_process_req_status(struct dwc2_hsotg *hsotg,
|
||||
struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
|
||||
@ -955,7 +957,7 @@ static struct s3c_hsotg_req *get_ep_head(struct s3c_hsotg_ep *hs_ep)
|
||||
* @hsotg: The device state
|
||||
* @ctrl: USB control request
|
||||
*/
|
||||
static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
|
||||
static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
|
||||
struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
|
||||
@ -1028,8 +1030,7 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg);
|
||||
static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg);
|
||||
static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg);
|
||||
|
||||
/**
|
||||
* s3c_hsotg_stall_ep0 - stall ep0
|
||||
@ -1037,7 +1038,7 @@ static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg);
|
||||
*
|
||||
* Set stall for ep0 as response for setup request.
|
||||
*/
|
||||
static void s3c_hsotg_stall_ep0(struct s3c_hsotg *hsotg)
|
||||
static void s3c_hsotg_stall_ep0(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
|
||||
u32 reg;
|
||||
@ -1076,7 +1077,7 @@ static void s3c_hsotg_stall_ep0(struct s3c_hsotg *hsotg)
|
||||
* needs to work out what to do next (and whether to pass it on to the
|
||||
* gadget driver).
|
||||
*/
|
||||
static void s3c_hsotg_process_control(struct s3c_hsotg *hsotg,
|
||||
static void s3c_hsotg_process_control(struct dwc2_hsotg *hsotg,
|
||||
struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
|
||||
@ -1107,7 +1108,6 @@ static void s3c_hsotg_process_control(struct s3c_hsotg *hsotg,
|
||||
if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
|
||||
switch (ctrl->bRequest) {
|
||||
case USB_REQ_SET_ADDRESS:
|
||||
s3c_hsotg_disconnect(hsotg);
|
||||
dcfg = readl(hsotg->regs + DCFG);
|
||||
dcfg &= ~DCFG_DEVADDR_MASK;
|
||||
dcfg |= (le16_to_cpu(ctrl->wValue) <<
|
||||
@ -1161,7 +1161,7 @@ static void s3c_hsotg_complete_setup(struct usb_ep *ep,
|
||||
struct usb_request *req)
|
||||
{
|
||||
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
|
||||
struct s3c_hsotg *hsotg = hs_ep->parent;
|
||||
struct dwc2_hsotg *hsotg = hs_ep->parent;
|
||||
|
||||
if (req->status < 0) {
|
||||
dev_dbg(hsotg->dev, "%s: failed %d\n", __func__, req->status);
|
||||
@ -1183,7 +1183,7 @@ static void s3c_hsotg_complete_setup(struct usb_ep *ep,
|
||||
* Enqueue a request on EP0 if necessary to received any SETUP packets
|
||||
* received from the host.
|
||||
*/
|
||||
static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg)
|
||||
static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct usb_request *req = hsotg->ctrl_req;
|
||||
struct s3c_hsotg_req *hs_req = our_req(req);
|
||||
@ -1226,7 +1226,7 @@ static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg)
|
||||
*
|
||||
* Note, expects the ep to already be locked as appropriate.
|
||||
*/
|
||||
static void s3c_hsotg_complete_request(struct s3c_hsotg *hsotg,
|
||||
static void s3c_hsotg_complete_request(struct dwc2_hsotg *hsotg,
|
||||
struct s3c_hsotg_ep *hs_ep,
|
||||
struct s3c_hsotg_req *hs_req,
|
||||
int result)
|
||||
@ -1291,7 +1291,7 @@ static void s3c_hsotg_complete_request(struct s3c_hsotg *hsotg,
|
||||
* endpoint, so sort out whether we need to read the data into a request
|
||||
* that has been made for that endpoint.
|
||||
*/
|
||||
static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size)
|
||||
static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size)
|
||||
{
|
||||
struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep_idx];
|
||||
struct s3c_hsotg_req *hs_req = hs_ep->req;
|
||||
@ -1356,7 +1356,7 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size)
|
||||
* currently believed that we do not need to wait for any space in
|
||||
* the TxFIFO.
|
||||
*/
|
||||
static void s3c_hsotg_send_zlp(struct s3c_hsotg *hsotg,
|
||||
static void s3c_hsotg_send_zlp(struct dwc2_hsotg *hsotg,
|
||||
struct s3c_hsotg_req *req)
|
||||
{
|
||||
u32 ctrl;
|
||||
@ -1398,7 +1398,7 @@ static void s3c_hsotg_send_zlp(struct s3c_hsotg *hsotg,
|
||||
* transfer for an OUT endpoint has been completed, either by a short
|
||||
* packet or by the finish of a transfer.
|
||||
*/
|
||||
static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg,
|
||||
static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg,
|
||||
int epnum, bool was_setup)
|
||||
{
|
||||
u32 epsize = readl(hsotg->regs + DOEPTSIZ(epnum));
|
||||
@ -1471,7 +1471,7 @@ static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg,
|
||||
*
|
||||
* Return the current frame number
|
||||
*/
|
||||
static u32 s3c_hsotg_read_frameno(struct s3c_hsotg *hsotg)
|
||||
static u32 s3c_hsotg_read_frameno(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 dsts;
|
||||
|
||||
@ -1498,7 +1498,7 @@ static u32 s3c_hsotg_read_frameno(struct s3c_hsotg *hsotg)
|
||||
* as the actual data should be sent to the memory directly and we turn
|
||||
* on the completion interrupts to get notifications of transfer completion.
|
||||
*/
|
||||
static void s3c_hsotg_handle_rx(struct s3c_hsotg *hsotg)
|
||||
static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 grxstsr = readl(hsotg->regs + GRXSTSP);
|
||||
u32 epnum, status, size;
|
||||
@ -1590,7 +1590,7 @@ static u32 s3c_hsotg_ep0_mps(unsigned int mps)
|
||||
* Configure the maximum packet size for the given endpoint, updating
|
||||
* the hardware control registers to reflect this.
|
||||
*/
|
||||
static void s3c_hsotg_set_ep_maxpacket(struct s3c_hsotg *hsotg,
|
||||
static void s3c_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg,
|
||||
unsigned int ep, unsigned int mps)
|
||||
{
|
||||
struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep];
|
||||
@ -1645,7 +1645,7 @@ bad_mps:
|
||||
* @hsotg: The driver state
|
||||
* @idx: The index for the endpoint (0..15)
|
||||
*/
|
||||
static void s3c_hsotg_txfifo_flush(struct s3c_hsotg *hsotg, unsigned int idx)
|
||||
static void s3c_hsotg_txfifo_flush(struct dwc2_hsotg *hsotg, unsigned int idx)
|
||||
{
|
||||
int timeout;
|
||||
int val;
|
||||
@ -1681,7 +1681,7 @@ static void s3c_hsotg_txfifo_flush(struct s3c_hsotg *hsotg, unsigned int idx)
|
||||
* Check to see if there is a request that has data to send, and if so
|
||||
* make an attempt to write data into the FIFO.
|
||||
*/
|
||||
static int s3c_hsotg_trytx(struct s3c_hsotg *hsotg,
|
||||
static int s3c_hsotg_trytx(struct dwc2_hsotg *hsotg,
|
||||
struct s3c_hsotg_ep *hs_ep)
|
||||
{
|
||||
struct s3c_hsotg_req *hs_req = hs_ep->req;
|
||||
@ -1714,7 +1714,7 @@ static int s3c_hsotg_trytx(struct s3c_hsotg *hsotg,
|
||||
* An IN transfer has been completed, update the transfer's state and then
|
||||
* call the relevant completion routines.
|
||||
*/
|
||||
static void s3c_hsotg_complete_in(struct s3c_hsotg *hsotg,
|
||||
static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg,
|
||||
struct s3c_hsotg_ep *hs_ep)
|
||||
{
|
||||
struct s3c_hsotg_req *hs_req = hs_ep->req;
|
||||
@ -1791,7 +1791,7 @@ static void s3c_hsotg_complete_in(struct s3c_hsotg *hsotg,
|
||||
*
|
||||
* Process and clear any interrupt pending for an individual endpoint
|
||||
*/
|
||||
static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx,
|
||||
static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
|
||||
int dir_in)
|
||||
{
|
||||
struct s3c_hsotg_ep *hs_ep = &hsotg->eps[idx];
|
||||
@ -1916,7 +1916,7 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx,
|
||||
* Handle updating the device settings after the enumeration phase has
|
||||
* been completed.
|
||||
*/
|
||||
static void s3c_hsotg_irq_enumdone(struct s3c_hsotg *hsotg)
|
||||
static void s3c_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 dsts = readl(hsotg->regs + DSTS);
|
||||
int ep0_mps = 0, ep_mps = 8;
|
||||
@ -1993,7 +1993,7 @@ static void s3c_hsotg_irq_enumdone(struct s3c_hsotg *hsotg)
|
||||
* Go through the requests on the given endpoint and mark them
|
||||
* completed with the given result code.
|
||||
*/
|
||||
static void kill_all_requests(struct s3c_hsotg *hsotg,
|
||||
static void kill_all_requests(struct dwc2_hsotg *hsotg,
|
||||
struct s3c_hsotg_ep *ep,
|
||||
int result, bool force)
|
||||
{
|
||||
@ -2027,22 +2027,27 @@ static void kill_all_requests(struct s3c_hsotg *hsotg,
|
||||
* transactions and signal the gadget driver that this
|
||||
* has happened.
|
||||
*/
|
||||
static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg)
|
||||
void s3c_hsotg_disconnect(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
unsigned ep;
|
||||
|
||||
if (!hsotg->connected)
|
||||
return;
|
||||
|
||||
hsotg->connected = 0;
|
||||
for (ep = 0; ep < hsotg->num_of_eps; ep++)
|
||||
kill_all_requests(hsotg, &hsotg->eps[ep], -ESHUTDOWN, true);
|
||||
|
||||
call_gadget(hsotg, disconnect);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(s3c_hsotg_disconnect);
|
||||
|
||||
/**
|
||||
* s3c_hsotg_irq_fifoempty - TX FIFO empty interrupt handler
|
||||
* @hsotg: The device state:
|
||||
* @periodic: True if this is a periodic FIFO interrupt
|
||||
*/
|
||||
static void s3c_hsotg_irq_fifoempty(struct s3c_hsotg *hsotg, bool periodic)
|
||||
static void s3c_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic)
|
||||
{
|
||||
struct s3c_hsotg_ep *ep;
|
||||
int epno, ret;
|
||||
@ -2076,7 +2081,7 @@ static void s3c_hsotg_irq_fifoempty(struct s3c_hsotg *hsotg, bool periodic)
|
||||
*
|
||||
* Issue a soft reset to the core, and await the core finishing it.
|
||||
*/
|
||||
static int s3c_hsotg_corereset(struct s3c_hsotg *hsotg)
|
||||
static int s3c_hsotg_corereset(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
int timeout;
|
||||
u32 grstctl;
|
||||
@ -2124,7 +2129,7 @@ static int s3c_hsotg_corereset(struct s3c_hsotg *hsotg)
|
||||
*
|
||||
* Issue a soft reset to the core, and await the core finishing it.
|
||||
*/
|
||||
static void s3c_hsotg_core_init(struct s3c_hsotg *hsotg)
|
||||
void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
s3c_hsotg_corereset(hsotg);
|
||||
|
||||
@ -2241,12 +2246,23 @@ static void s3c_hsotg_core_init(struct s3c_hsotg *hsotg)
|
||||
readl(hsotg->regs + DOEPCTL0));
|
||||
|
||||
/* clear global NAKs */
|
||||
writel(DCTL_CGOUTNAK | DCTL_CGNPINNAK,
|
||||
writel(DCTL_CGOUTNAK | DCTL_CGNPINNAK | DCTL_SFTDISCON,
|
||||
hsotg->regs + DCTL);
|
||||
|
||||
/* must be at-least 3ms to allow bus to see disconnect */
|
||||
mdelay(3);
|
||||
|
||||
hsotg->last_rst = jiffies;
|
||||
}
|
||||
|
||||
static void s3c_hsotg_core_disconnect(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
/* set the soft-disconnect bit */
|
||||
__orr32(hsotg->regs + DCTL, DCTL_SFTDISCON);
|
||||
}
|
||||
|
||||
void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
/* remove the soft-disconnect and let's go */
|
||||
__bic32(hsotg->regs + DCTL, DCTL_SFTDISCON);
|
||||
}
|
||||
@ -2258,7 +2274,7 @@ static void s3c_hsotg_core_init(struct s3c_hsotg *hsotg)
|
||||
*/
|
||||
static irqreturn_t s3c_hsotg_irq(int irq, void *pw)
|
||||
{
|
||||
struct s3c_hsotg *hsotg = pw;
|
||||
struct dwc2_hsotg *hsotg = pw;
|
||||
int retry_count = 8;
|
||||
u32 gintsts;
|
||||
u32 gintmsk;
|
||||
@ -2273,31 +2289,11 @@ irq_retry:
|
||||
|
||||
gintsts &= gintmsk;
|
||||
|
||||
if (gintsts & GINTSTS_OTGINT) {
|
||||
u32 otgint = readl(hsotg->regs + GOTGINT);
|
||||
|
||||
dev_info(hsotg->dev, "OTGInt: %08x\n", otgint);
|
||||
|
||||
writel(otgint, hsotg->regs + GOTGINT);
|
||||
}
|
||||
|
||||
if (gintsts & GINTSTS_SESSREQINT) {
|
||||
dev_dbg(hsotg->dev, "%s: SessReqInt\n", __func__);
|
||||
writel(GINTSTS_SESSREQINT, hsotg->regs + GINTSTS);
|
||||
}
|
||||
|
||||
if (gintsts & GINTSTS_ENUMDONE) {
|
||||
writel(GINTSTS_ENUMDONE, hsotg->regs + GINTSTS);
|
||||
|
||||
s3c_hsotg_irq_enumdone(hsotg);
|
||||
}
|
||||
|
||||
if (gintsts & GINTSTS_CONIDSTSCHNG) {
|
||||
dev_dbg(hsotg->dev, "ConIDStsChg (DSTS=0x%08x, GOTCTL=%08x)\n",
|
||||
readl(hsotg->regs + DSTS),
|
||||
readl(hsotg->regs + GOTGCTL));
|
||||
|
||||
writel(GINTSTS_CONIDSTSCHNG, hsotg->regs + GINTSTS);
|
||||
hsotg->connected = 1;
|
||||
}
|
||||
|
||||
if (gintsts & (GINTSTS_OEPINT | GINTSTS_IEPINT)) {
|
||||
@ -2340,8 +2336,8 @@ irq_retry:
|
||||
kill_all_requests(hsotg, &hsotg->eps[0],
|
||||
-ECONNRESET, true);
|
||||
|
||||
s3c_hsotg_core_init(hsotg);
|
||||
hsotg->last_rst = jiffies;
|
||||
s3c_hsotg_core_init_disconnected(hsotg);
|
||||
s3c_hsotg_core_connect(hsotg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2380,25 +2376,6 @@ irq_retry:
|
||||
s3c_hsotg_handle_rx(hsotg);
|
||||
}
|
||||
|
||||
if (gintsts & GINTSTS_MODEMIS) {
|
||||
dev_warn(hsotg->dev, "warning, mode mismatch triggered\n");
|
||||
writel(GINTSTS_MODEMIS, hsotg->regs + GINTSTS);
|
||||
}
|
||||
|
||||
if (gintsts & GINTSTS_USBSUSP) {
|
||||
dev_info(hsotg->dev, "GINTSTS_USBSusp\n");
|
||||
writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS);
|
||||
|
||||
call_gadget(hsotg, suspend);
|
||||
}
|
||||
|
||||
if (gintsts & GINTSTS_WKUPINT) {
|
||||
dev_info(hsotg->dev, "GINTSTS_WkUpIn\n");
|
||||
writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS);
|
||||
|
||||
call_gadget(hsotg, resume);
|
||||
}
|
||||
|
||||
if (gintsts & GINTSTS_ERLYSUSP) {
|
||||
dev_dbg(hsotg->dev, "GINTSTS_ErlySusp\n");
|
||||
writel(GINTSTS_ERLYSUSP, hsotg->regs + GINTSTS);
|
||||
@ -2450,7 +2427,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
|
||||
const struct usb_endpoint_descriptor *desc)
|
||||
{
|
||||
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
|
||||
struct s3c_hsotg *hsotg = hs_ep->parent;
|
||||
struct dwc2_hsotg *hsotg = hs_ep->parent;
|
||||
unsigned long flags;
|
||||
int index = hs_ep->index;
|
||||
u32 epctrl_reg;
|
||||
@ -2593,7 +2570,7 @@ error:
|
||||
static int s3c_hsotg_ep_disable(struct usb_ep *ep)
|
||||
{
|
||||
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
|
||||
struct s3c_hsotg *hsotg = hs_ep->parent;
|
||||
struct dwc2_hsotg *hsotg = hs_ep->parent;
|
||||
int dir_in = hs_ep->dir_in;
|
||||
int index = hs_ep->index;
|
||||
unsigned long flags;
|
||||
@ -2658,7 +2635,7 @@ static int s3c_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct s3c_hsotg_req *hs_req = our_req(req);
|
||||
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
|
||||
struct s3c_hsotg *hs = hs_ep->parent;
|
||||
struct dwc2_hsotg *hs = hs_ep->parent;
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(hs->dev, "ep_dequeue(%p,%p)\n", ep, req);
|
||||
@ -2684,7 +2661,7 @@ static int s3c_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
|
||||
static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
|
||||
{
|
||||
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
|
||||
struct s3c_hsotg *hs = hs_ep->parent;
|
||||
struct dwc2_hsotg *hs = hs_ep->parent;
|
||||
int index = hs_ep->index;
|
||||
u32 epreg;
|
||||
u32 epctl;
|
||||
@ -2748,7 +2725,7 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
|
||||
static int s3c_hsotg_ep_sethalt_lock(struct usb_ep *ep, int value)
|
||||
{
|
||||
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
|
||||
struct s3c_hsotg *hs = hs_ep->parent;
|
||||
struct dwc2_hsotg *hs = hs_ep->parent;
|
||||
unsigned long flags = 0;
|
||||
int ret = 0;
|
||||
|
||||
@ -2777,7 +2754,7 @@ static struct usb_ep_ops s3c_hsotg_ep_ops = {
|
||||
* A wrapper for platform code responsible for controlling
|
||||
* low-level USB code
|
||||
*/
|
||||
static void s3c_hsotg_phy_enable(struct s3c_hsotg *hsotg)
|
||||
static void s3c_hsotg_phy_enable(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(hsotg->dev);
|
||||
|
||||
@ -2800,7 +2777,7 @@ static void s3c_hsotg_phy_enable(struct s3c_hsotg *hsotg)
|
||||
* A wrapper for platform code responsible for controlling
|
||||
* low-level USB code
|
||||
*/
|
||||
static void s3c_hsotg_phy_disable(struct s3c_hsotg *hsotg)
|
||||
static void s3c_hsotg_phy_disable(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(hsotg->dev);
|
||||
|
||||
@ -2818,7 +2795,7 @@ static void s3c_hsotg_phy_disable(struct s3c_hsotg *hsotg)
|
||||
* s3c_hsotg_init - initalize the usb core
|
||||
* @hsotg: The driver state
|
||||
*/
|
||||
static void s3c_hsotg_init(struct s3c_hsotg *hsotg)
|
||||
static void s3c_hsotg_init(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
/* unmask subset of endpoint interrupts */
|
||||
|
||||
@ -2868,7 +2845,8 @@ static void s3c_hsotg_init(struct s3c_hsotg *hsotg)
|
||||
static int s3c_hsotg_udc_start(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
{
|
||||
struct s3c_hsotg *hsotg = to_hsotg(gadget);
|
||||
struct dwc2_hsotg *hsotg = to_hsotg(gadget);
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
if (!hsotg) {
|
||||
@ -2889,6 +2867,7 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&hsotg->init_mutex);
|
||||
WARN_ON(hsotg->driver);
|
||||
|
||||
driver->driver.bus = NULL;
|
||||
@ -2905,11 +2884,22 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget,
|
||||
goto err;
|
||||
}
|
||||
|
||||
hsotg->last_rst = jiffies;
|
||||
s3c_hsotg_phy_enable(hsotg);
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
s3c_hsotg_init(hsotg);
|
||||
s3c_hsotg_core_init_disconnected(hsotg);
|
||||
hsotg->enabled = 0;
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
dev_info(hsotg->dev, "bound driver %s\n", driver->driver.name);
|
||||
|
||||
mutex_unlock(&hsotg->init_mutex);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
mutex_unlock(&hsotg->init_mutex);
|
||||
hsotg->driver = NULL;
|
||||
return ret;
|
||||
}
|
||||
@ -2921,16 +2911,17 @@ err:
|
||||
*
|
||||
* Stop udc hw block and stay tunned for future transmissions
|
||||
*/
|
||||
static int s3c_hsotg_udc_stop(struct usb_gadget *gadget,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int s3c_hsotg_udc_stop(struct usb_gadget *gadget)
|
||||
{
|
||||
struct s3c_hsotg *hsotg = to_hsotg(gadget);
|
||||
struct dwc2_hsotg *hsotg = to_hsotg(gadget);
|
||||
unsigned long flags = 0;
|
||||
int ep;
|
||||
|
||||
if (!hsotg)
|
||||
return -ENODEV;
|
||||
|
||||
mutex_lock(&hsotg->init_mutex);
|
||||
|
||||
/* all endpoints should be shutdown */
|
||||
for (ep = 1; ep < hsotg->num_of_eps; ep++)
|
||||
s3c_hsotg_ep_disable(&hsotg->eps[ep].ep);
|
||||
@ -2939,13 +2930,18 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget,
|
||||
|
||||
hsotg->driver = NULL;
|
||||
hsotg->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
hsotg->enabled = 0;
|
||||
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
s3c_hsotg_phy_disable(hsotg);
|
||||
|
||||
regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
|
||||
|
||||
clk_disable(hsotg->clk);
|
||||
|
||||
mutex_unlock(&hsotg->init_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2969,23 +2965,26 @@ static int s3c_hsotg_gadget_getframe(struct usb_gadget *gadget)
|
||||
*/
|
||||
static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on)
|
||||
{
|
||||
struct s3c_hsotg *hsotg = to_hsotg(gadget);
|
||||
struct dwc2_hsotg *hsotg = to_hsotg(gadget);
|
||||
unsigned long flags = 0;
|
||||
|
||||
dev_dbg(hsotg->dev, "%s: is_on: %d\n", __func__, is_on);
|
||||
|
||||
mutex_lock(&hsotg->init_mutex);
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
if (is_on) {
|
||||
s3c_hsotg_phy_enable(hsotg);
|
||||
clk_enable(hsotg->clk);
|
||||
s3c_hsotg_core_init(hsotg);
|
||||
hsotg->enabled = 1;
|
||||
s3c_hsotg_core_connect(hsotg);
|
||||
} else {
|
||||
s3c_hsotg_core_disconnect(hsotg);
|
||||
hsotg->enabled = 0;
|
||||
clk_disable(hsotg->clk);
|
||||
s3c_hsotg_phy_disable(hsotg);
|
||||
}
|
||||
|
||||
hsotg->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
mutex_unlock(&hsotg->init_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -3007,7 +3006,7 @@ static const struct usb_gadget_ops s3c_hsotg_gadget_ops = {
|
||||
* creation) to give to the gadget driver. Setup the endpoint name, any
|
||||
* direction information and other state that may be required.
|
||||
*/
|
||||
static void s3c_hsotg_initep(struct s3c_hsotg *hsotg,
|
||||
static void s3c_hsotg_initep(struct dwc2_hsotg *hsotg,
|
||||
struct s3c_hsotg_ep *hs_ep,
|
||||
int epnum)
|
||||
{
|
||||
@ -3056,7 +3055,7 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg,
|
||||
*
|
||||
* Read the USB core HW configuration registers
|
||||
*/
|
||||
static void s3c_hsotg_hw_cfg(struct s3c_hsotg *hsotg)
|
||||
static void s3c_hsotg_hw_cfg(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 cfg2, cfg3, cfg4;
|
||||
/* check hardware configuration */
|
||||
@ -3080,7 +3079,7 @@ static void s3c_hsotg_hw_cfg(struct s3c_hsotg *hsotg)
|
||||
* s3c_hsotg_dump - dump state of the udc
|
||||
* @param: The device state
|
||||
*/
|
||||
static void s3c_hsotg_dump(struct s3c_hsotg *hsotg)
|
||||
static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
struct device *dev = hsotg->dev;
|
||||
@ -3139,7 +3138,7 @@ static void s3c_hsotg_dump(struct s3c_hsotg *hsotg)
|
||||
*/
|
||||
static int state_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct s3c_hsotg *hsotg = seq->private;
|
||||
struct dwc2_hsotg *hsotg = seq->private;
|
||||
void __iomem *regs = hsotg->regs;
|
||||
int idx;
|
||||
|
||||
@ -3209,7 +3208,7 @@ static const struct file_operations state_fops = {
|
||||
*/
|
||||
static int fifo_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct s3c_hsotg *hsotg = seq->private;
|
||||
struct dwc2_hsotg *hsotg = seq->private;
|
||||
void __iomem *regs = hsotg->regs;
|
||||
u32 val;
|
||||
int idx;
|
||||
@ -3265,7 +3264,7 @@ static const char *decode_direction(int is_in)
|
||||
static int ep_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct s3c_hsotg_ep *ep = seq->private;
|
||||
struct s3c_hsotg *hsotg = ep->parent;
|
||||
struct dwc2_hsotg *hsotg = ep->parent;
|
||||
struct s3c_hsotg_req *req;
|
||||
void __iomem *regs = hsotg->regs;
|
||||
int index = ep->index;
|
||||
@ -3342,7 +3341,7 @@ static const struct file_operations ep_fops = {
|
||||
* with the same name as the device itself, in case we end up
|
||||
* with multiple blocks in future systems.
|
||||
*/
|
||||
static void s3c_hsotg_create_debug(struct s3c_hsotg *hsotg)
|
||||
static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dentry *root;
|
||||
unsigned epidx;
|
||||
@ -3388,7 +3387,7 @@ static void s3c_hsotg_create_debug(struct s3c_hsotg *hsotg)
|
||||
*
|
||||
* Cleanup (remove) the debugfs files for use on module exit.
|
||||
*/
|
||||
static void s3c_hsotg_delete_debug(struct s3c_hsotg *hsotg)
|
||||
static void s3c_hsotg_delete_debug(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
unsigned epidx;
|
||||
|
||||
@ -3403,27 +3402,21 @@ static void s3c_hsotg_delete_debug(struct s3c_hsotg *hsotg)
|
||||
}
|
||||
|
||||
/**
|
||||
* s3c_hsotg_probe - probe function for hsotg driver
|
||||
* @pdev: The platform information for the driver
|
||||
* dwc2_gadget_init - init function for gadget
|
||||
* @dwc2: The data structure for the DWC2 driver.
|
||||
* @irq: The IRQ number for the controller.
|
||||
*/
|
||||
|
||||
static int s3c_hsotg_probe(struct platform_device *pdev)
|
||||
int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
|
||||
{
|
||||
struct s3c_hsotg_plat *plat = dev_get_platdata(&pdev->dev);
|
||||
struct device *dev = hsotg->dev;
|
||||
struct s3c_hsotg_plat *plat = dev->platform_data;
|
||||
struct phy *phy;
|
||||
struct usb_phy *uphy;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct s3c_hsotg_ep *eps;
|
||||
struct s3c_hsotg *hsotg;
|
||||
struct resource *res;
|
||||
int epnum;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
hsotg = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsotg), GFP_KERNEL);
|
||||
if (!hsotg)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Set default UTMI width */
|
||||
hsotg->phyif = GUSBCFG_PHYIF16;
|
||||
|
||||
@ -3431,14 +3424,14 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
|
||||
* Attempt to find a generic PHY, then look for an old style
|
||||
* USB PHY, finally fall back to pdata
|
||||
*/
|
||||
phy = devm_phy_get(&pdev->dev, "usb2-phy");
|
||||
phy = devm_phy_get(dev, "usb2-phy");
|
||||
if (IS_ERR(phy)) {
|
||||
uphy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
|
||||
if (IS_ERR(uphy)) {
|
||||
/* Fallback for pdata */
|
||||
plat = dev_get_platdata(&pdev->dev);
|
||||
plat = dev_get_platdata(dev);
|
||||
if (!plat) {
|
||||
dev_err(&pdev->dev,
|
||||
dev_err(dev,
|
||||
"no platform data or transceiver defined\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
@ -3455,43 +3448,24 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
|
||||
hsotg->phyif = GUSBCFG_PHYIF8;
|
||||
}
|
||||
|
||||
hsotg->dev = dev;
|
||||
|
||||
hsotg->clk = devm_clk_get(&pdev->dev, "otg");
|
||||
hsotg->clk = devm_clk_get(dev, "otg");
|
||||
if (IS_ERR(hsotg->clk)) {
|
||||
dev_err(dev, "cannot get otg clock\n");
|
||||
return PTR_ERR(hsotg->clk);
|
||||
hsotg->clk = NULL;
|
||||
dev_dbg(dev, "cannot get otg clock\n");
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, hsotg);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
hsotg->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(hsotg->regs)) {
|
||||
ret = PTR_ERR(hsotg->regs);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "cannot find IRQ\n");
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
spin_lock_init(&hsotg->lock);
|
||||
|
||||
hsotg->irq = ret;
|
||||
|
||||
dev_info(dev, "regs %p, irq %d\n", hsotg->regs, hsotg->irq);
|
||||
|
||||
hsotg->gadget.max_speed = USB_SPEED_HIGH;
|
||||
hsotg->gadget.ops = &s3c_hsotg_gadget_ops;
|
||||
hsotg->gadget.name = dev_name(dev);
|
||||
|
||||
/* reset the system */
|
||||
|
||||
clk_prepare_enable(hsotg->clk);
|
||||
ret = clk_prepare_enable(hsotg->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable otg clk\n");
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
|
||||
/* regulators */
|
||||
|
||||
@ -3509,7 +3483,7 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
|
||||
hsotg->supplies);
|
||||
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "failed to enable supplies: %d\n", ret);
|
||||
dev_err(dev, "failed to enable supplies: %d\n", ret);
|
||||
goto err_supplies;
|
||||
}
|
||||
|
||||
@ -3520,14 +3494,14 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
|
||||
s3c_hsotg_hw_cfg(hsotg);
|
||||
s3c_hsotg_init(hsotg);
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, hsotg->irq, s3c_hsotg_irq, 0,
|
||||
dev_name(dev), hsotg);
|
||||
ret = devm_request_irq(hsotg->dev, irq, s3c_hsotg_irq, IRQF_SHARED,
|
||||
dev_name(hsotg->dev), hsotg);
|
||||
if (ret < 0) {
|
||||
s3c_hsotg_phy_disable(hsotg);
|
||||
clk_disable_unprepare(hsotg->clk);
|
||||
regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
|
||||
hsotg->supplies);
|
||||
dev_err(dev, "cannot claim IRQ\n");
|
||||
dev_err(dev, "cannot claim IRQ for gadget\n");
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
@ -3573,11 +3547,11 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
|
||||
ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
|
||||
hsotg->supplies);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "failed to disable supplies: %d\n", ret);
|
||||
dev_err(dev, "failed to disable supplies: %d\n", ret);
|
||||
goto err_ep_mem;
|
||||
}
|
||||
|
||||
ret = usb_add_gadget_udc(&pdev->dev, &hsotg->gadget);
|
||||
ret = usb_add_gadget_udc(dev, &hsotg->gadget);
|
||||
if (ret)
|
||||
goto err_ep_mem;
|
||||
|
||||
@ -3596,47 +3570,44 @@ err_clk:
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc2_gadget_init);
|
||||
|
||||
/**
|
||||
* s3c_hsotg_remove - remove function for hsotg driver
|
||||
* @pdev: The platform information for the driver
|
||||
*/
|
||||
static int s3c_hsotg_remove(struct platform_device *pdev)
|
||||
int s3c_hsotg_remove(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct s3c_hsotg *hsotg = platform_get_drvdata(pdev);
|
||||
|
||||
usb_del_gadget_udc(&hsotg->gadget);
|
||||
|
||||
s3c_hsotg_delete_debug(hsotg);
|
||||
|
||||
if (hsotg->driver) {
|
||||
/* should have been done already by driver model core */
|
||||
usb_gadget_unregister_driver(hsotg->driver);
|
||||
}
|
||||
|
||||
clk_disable_unprepare(hsotg->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(s3c_hsotg_remove);
|
||||
|
||||
static int s3c_hsotg_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct s3c_hsotg *hsotg = platform_get_drvdata(pdev);
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
if (hsotg->driver)
|
||||
dev_info(hsotg->dev, "suspending usb gadget %s\n",
|
||||
hsotg->driver->driver.name);
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
s3c_hsotg_disconnect(hsotg);
|
||||
s3c_hsotg_phy_disable(hsotg);
|
||||
hsotg->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
mutex_lock(&hsotg->init_mutex);
|
||||
|
||||
if (hsotg->driver) {
|
||||
int ep;
|
||||
|
||||
dev_info(hsotg->dev, "suspending usb gadget %s\n",
|
||||
hsotg->driver->driver.name);
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
if (hsotg->enabled)
|
||||
s3c_hsotg_core_disconnect(hsotg);
|
||||
s3c_hsotg_disconnect(hsotg);
|
||||
hsotg->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
s3c_hsotg_phy_disable(hsotg);
|
||||
|
||||
for (ep = 0; ep < hsotg->num_of_eps; ep++)
|
||||
s3c_hsotg_ep_disable(&hsotg->eps[ep].ep);
|
||||
|
||||
@ -3645,57 +3616,37 @@ static int s3c_hsotg_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
clk_disable(hsotg->clk);
|
||||
}
|
||||
|
||||
mutex_unlock(&hsotg->init_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(s3c_hsotg_suspend);
|
||||
|
||||
static int s3c_hsotg_resume(struct platform_device *pdev)
|
||||
int s3c_hsotg_resume(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct s3c_hsotg *hsotg = platform_get_drvdata(pdev);
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&hsotg->init_mutex);
|
||||
|
||||
if (hsotg->driver) {
|
||||
dev_info(hsotg->dev, "resuming usb gadget %s\n",
|
||||
hsotg->driver->driver.name);
|
||||
|
||||
clk_enable(hsotg->clk);
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies),
|
||||
hsotg->supplies);
|
||||
}
|
||||
hsotg->supplies);
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
hsotg->last_rst = jiffies;
|
||||
s3c_hsotg_phy_enable(hsotg);
|
||||
s3c_hsotg_core_init(hsotg);
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
s3c_hsotg_phy_enable(hsotg);
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
s3c_hsotg_core_init_disconnected(hsotg);
|
||||
if (hsotg->enabled)
|
||||
s3c_hsotg_core_connect(hsotg);
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
}
|
||||
mutex_unlock(&hsotg->init_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id s3c_hsotg_of_ids[] = {
|
||||
{ .compatible = "samsung,s3c6400-hsotg", },
|
||||
{ .compatible = "snps,dwc2", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, s3c_hsotg_of_ids);
|
||||
#endif
|
||||
|
||||
static struct platform_driver s3c_hsotg_driver = {
|
||||
.driver = {
|
||||
.name = "s3c-hsotg",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(s3c_hsotg_of_ids),
|
||||
},
|
||||
.probe = s3c_hsotg_probe,
|
||||
.remove = s3c_hsotg_remove,
|
||||
.suspend = s3c_hsotg_suspend,
|
||||
.resume = s3c_hsotg_resume,
|
||||
};
|
||||
|
||||
module_platform_driver(s3c_hsotg_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Samsung S3C USB High-speed/OtG device");
|
||||
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:s3c-hsotg");
|
||||
EXPORT_SYMBOL_GPL(s3c_hsotg_resume);
|
||||
|
@ -1371,6 +1371,8 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
|
||||
hsotg->op_state = OTG_STATE_B_PERIPHERAL;
|
||||
dwc2_core_init(hsotg, false, -1);
|
||||
dwc2_enable_global_interrupts(hsotg);
|
||||
s3c_hsotg_core_init_disconnected(hsotg);
|
||||
s3c_hsotg_core_connect(hsotg);
|
||||
} else {
|
||||
/* A-Device connector (Host Mode) */
|
||||
dev_dbg(hsotg->dev, "connId A\n");
|
||||
@ -1471,6 +1473,30 @@ static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex)
|
||||
}
|
||||
}
|
||||
|
||||
static void dwc2_port_resume(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 hprt0;
|
||||
|
||||
/* After clear the Stop PHY clock bit, we should wait for a moment
|
||||
* for PLL work stable with clock output.
|
||||
*/
|
||||
writel(0, hsotg->regs + PCGCTL);
|
||||
usleep_range(2000, 4000);
|
||||
|
||||
hprt0 = dwc2_read_hprt0(hsotg);
|
||||
hprt0 |= HPRT0_RES;
|
||||
writel(hprt0, hsotg->regs + HPRT0);
|
||||
hprt0 &= ~HPRT0_SUSP;
|
||||
/* according to USB2.0 Spec 7.1.7.7, the host must send the resume
|
||||
* signal for at least 20ms
|
||||
*/
|
||||
usleep_range(20000, 25000);
|
||||
|
||||
hprt0 &= ~HPRT0_RES;
|
||||
writel(hprt0, hsotg->regs + HPRT0);
|
||||
hsotg->lx_state = DWC2_L0;
|
||||
}
|
||||
|
||||
/* Handles hub class-specific requests */
|
||||
static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
|
||||
u16 wvalue, u16 windex, char *buf, u16 wlength)
|
||||
@ -1516,17 +1542,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
|
||||
case USB_PORT_FEAT_SUSPEND:
|
||||
dev_dbg(hsotg->dev,
|
||||
"ClearPortFeature USB_PORT_FEAT_SUSPEND\n");
|
||||
writel(0, hsotg->regs + PCGCTL);
|
||||
usleep_range(20000, 40000);
|
||||
|
||||
hprt0 = dwc2_read_hprt0(hsotg);
|
||||
hprt0 |= HPRT0_RES;
|
||||
writel(hprt0, hsotg->regs + HPRT0);
|
||||
hprt0 &= ~HPRT0_SUSP;
|
||||
usleep_range(100000, 150000);
|
||||
|
||||
hprt0 &= ~HPRT0_RES;
|
||||
writel(hprt0, hsotg->regs + HPRT0);
|
||||
dwc2_port_resume(hsotg);
|
||||
break;
|
||||
|
||||
case USB_PORT_FEAT_POWER:
|
||||
@ -2299,6 +2315,55 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
|
||||
usleep_range(1000, 3000);
|
||||
}
|
||||
|
||||
static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
|
||||
u32 hprt0;
|
||||
|
||||
if (!((hsotg->op_state == OTG_STATE_B_HOST) ||
|
||||
(hsotg->op_state == OTG_STATE_A_HOST)))
|
||||
return 0;
|
||||
|
||||
/* TODO: We get into suspend from 'on' state, maybe we need to do
|
||||
* something if we get here from DWC2_L1(LPM sleep) state one day.
|
||||
*/
|
||||
if (hsotg->lx_state != DWC2_L0)
|
||||
return 0;
|
||||
|
||||
hprt0 = dwc2_read_hprt0(hsotg);
|
||||
if (hprt0 & HPRT0_CONNSTS) {
|
||||
dwc2_port_suspend(hsotg, 1);
|
||||
} else {
|
||||
u32 pcgctl = readl(hsotg->regs + PCGCTL);
|
||||
|
||||
pcgctl |= PCGCTL_STOPPCLK;
|
||||
writel(pcgctl, hsotg->regs + PCGCTL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _dwc2_hcd_resume(struct usb_hcd *hcd)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
|
||||
u32 hprt0;
|
||||
|
||||
if (!((hsotg->op_state == OTG_STATE_B_HOST) ||
|
||||
(hsotg->op_state == OTG_STATE_A_HOST)))
|
||||
return 0;
|
||||
|
||||
if (hsotg->lx_state != DWC2_L2)
|
||||
return 0;
|
||||
|
||||
hprt0 = dwc2_read_hprt0(hsotg);
|
||||
if ((hprt0 & HPRT0_CONNSTS) && (hprt0 & HPRT0_SUSP))
|
||||
dwc2_port_resume(hsotg);
|
||||
else
|
||||
writel(0, hsotg->regs + PCGCTL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns the current frame number */
|
||||
static int _dwc2_hcd_get_frame_number(struct usb_hcd *hcd)
|
||||
{
|
||||
@ -2669,6 +2734,9 @@ static struct hc_driver dwc2_hc_driver = {
|
||||
.hub_status_data = _dwc2_hcd_hub_status_data,
|
||||
.hub_control = _dwc2_hcd_hub_control,
|
||||
.clear_tt_buffer_complete = _dwc2_hcd_clear_tt_buffer_complete,
|
||||
|
||||
.bus_suspend = _dwc2_hcd_suspend,
|
||||
.bus_resume = _dwc2_hcd_resume,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -2778,6 +2846,9 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq,
|
||||
int i, num_channels;
|
||||
int retval;
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
dev_dbg(hsotg->dev, "DWC OTG HCD INIT\n");
|
||||
|
||||
/* Detect config values from hardware */
|
||||
@ -2839,7 +2910,6 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq,
|
||||
|
||||
hcd->has_tt = 1;
|
||||
|
||||
spin_lock_init(&hsotg->lock);
|
||||
((struct wrapper_priv_data *) &hcd->hcd_priv)->hsotg = hsotg;
|
||||
hsotg->priv = hcd;
|
||||
|
||||
|
@ -668,9 +668,6 @@ extern irqreturn_t dwc2_handle_hcd_intr(struct dwc2_hsotg *hsotg);
|
||||
*/
|
||||
extern void dwc2_hcd_stop(struct dwc2_hsotg *hsotg);
|
||||
|
||||
extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
|
||||
extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg);
|
||||
|
||||
/**
|
||||
* dwc2_hcd_is_b_host() - Returns 1 if core currently is acting as B host,
|
||||
* and 0 otherwise
|
||||
@ -679,13 +676,6 @@ extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg);
|
||||
*/
|
||||
extern int dwc2_hcd_is_b_host(struct dwc2_hsotg *hsotg);
|
||||
|
||||
/**
|
||||
* dwc2_hcd_get_frame_number() - Returns current frame number
|
||||
*
|
||||
* @hsotg: The DWC2 HCD
|
||||
*/
|
||||
extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg);
|
||||
|
||||
/**
|
||||
* dwc2_hcd_dump_state() - Dumps hsotg state
|
||||
*
|
||||
|
@ -141,6 +141,13 @@ static int dwc2_driver_probe(struct pci_dev *dev,
|
||||
|
||||
pci_set_master(dev);
|
||||
|
||||
retval = devm_request_irq(hsotg->dev, dev->irq,
|
||||
dwc2_handle_common_intr, IRQF_SHARED,
|
||||
dev_name(hsotg->dev), hsotg);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
spin_lock_init(&hsotg->lock);
|
||||
retval = dwc2_hcd_init(hsotg, dev->irq, &dwc2_module_params);
|
||||
if (retval) {
|
||||
pci_disable_device(dev);
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <linux/usb/of.h>
|
||||
@ -121,6 +122,7 @@ static int dwc2_driver_remove(struct platform_device *dev)
|
||||
struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
|
||||
|
||||
dwc2_hcd_remove(hsotg);
|
||||
s3c_hsotg_remove(hsotg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -129,6 +131,7 @@ static const struct of_device_id dwc2_of_match_table[] = {
|
||||
{ .compatible = "brcm,bcm2835-usb", .data = ¶ms_bcm2835 },
|
||||
{ .compatible = "rockchip,rk3066-usb", .data = ¶ms_rk3066 },
|
||||
{ .compatible = "snps,dwc2", .data = NULL },
|
||||
{ .compatible = "samsung,s3c6400-hsotg", .data = NULL},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dwc2_of_match_table);
|
||||
@ -155,9 +158,6 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
||||
int retval;
|
||||
int irq;
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
match = of_match_device(dwc2_of_match_table, &dev->dev);
|
||||
if (match && match->data) {
|
||||
params = match->data;
|
||||
@ -194,6 +194,14 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
||||
return irq;
|
||||
}
|
||||
|
||||
dev_dbg(hsotg->dev, "registering common handler for irq%d\n",
|
||||
irq);
|
||||
retval = devm_request_irq(hsotg->dev, irq,
|
||||
dwc2_handle_common_intr, IRQF_SHARED,
|
||||
dev_name(hsotg->dev), hsotg);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
||||
hsotg->regs = devm_ioremap_resource(&dev->dev, res);
|
||||
if (IS_ERR(hsotg->regs))
|
||||
@ -204,6 +212,11 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
||||
|
||||
hsotg->dr_mode = of_usb_get_dr_mode(dev->dev.of_node);
|
||||
|
||||
spin_lock_init(&hsotg->lock);
|
||||
mutex_init(&hsotg->init_mutex);
|
||||
retval = dwc2_gadget_init(hsotg, irq);
|
||||
if (retval)
|
||||
return retval;
|
||||
retval = dwc2_hcd_init(hsotg, irq, params);
|
||||
if (retval)
|
||||
return retval;
|
||||
@ -213,10 +226,35 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc2_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (dwc2_is_device_mode(dwc2))
|
||||
ret = s3c_hsotg_suspend(dwc2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused dwc2_resume(struct device *dev)
|
||||
{
|
||||
struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (dwc2_is_device_mode(dwc2))
|
||||
ret = s3c_hsotg_resume(dwc2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dwc2_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dwc2_suspend, dwc2_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver dwc2_platform_driver = {
|
||||
.driver = {
|
||||
.name = dwc2_driver_name,
|
||||
.of_match_table = dwc2_of_match_table,
|
||||
.pm = &dwc2_dev_pm_ops,
|
||||
},
|
||||
.probe = dwc2_driver_probe,
|
||||
.remove = dwc2_driver_remove,
|
||||
|
@ -55,7 +55,7 @@ config USB_DWC3_OMAP
|
||||
|
||||
config USB_DWC3_EXYNOS
|
||||
tristate "Samsung Exynos Platform"
|
||||
depends on ARCH_EXYNOS || COMPILE_TEST
|
||||
depends on ARCH_EXYNOS && OF || COMPILE_TEST
|
||||
default USB_DWC3
|
||||
help
|
||||
Recent Exynos5 SoCs ship with one DesignWare Core USB3 IP inside,
|
||||
|
@ -19,6 +19,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
@ -32,6 +33,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
@ -362,6 +364,72 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
|
||||
parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core
|
||||
* @dwc: Pointer to our controller context structure
|
||||
*/
|
||||
static void dwc3_phy_setup(struct dwc3 *dwc)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
|
||||
|
||||
/*
|
||||
* Above 1.94a, it is recommended to set DWC3_GUSB3PIPECTL_SUSPHY
|
||||
* to '0' during coreConsultant configuration. So default value
|
||||
* will be '0' when the core is reset. Application needs to set it
|
||||
* to '1' after the core initialization is completed.
|
||||
*/
|
||||
if (dwc->revision > DWC3_REVISION_194A)
|
||||
reg |= DWC3_GUSB3PIPECTL_SUSPHY;
|
||||
|
||||
if (dwc->u2ss_inp3_quirk)
|
||||
reg |= DWC3_GUSB3PIPECTL_U2SSINP3OK;
|
||||
|
||||
if (dwc->req_p1p2p3_quirk)
|
||||
reg |= DWC3_GUSB3PIPECTL_REQP1P2P3;
|
||||
|
||||
if (dwc->del_p1p2p3_quirk)
|
||||
reg |= DWC3_GUSB3PIPECTL_DEP1P2P3_EN;
|
||||
|
||||
if (dwc->del_phy_power_chg_quirk)
|
||||
reg |= DWC3_GUSB3PIPECTL_DEPOCHANGE;
|
||||
|
||||
if (dwc->lfps_filter_quirk)
|
||||
reg |= DWC3_GUSB3PIPECTL_LFPSFILT;
|
||||
|
||||
if (dwc->rx_detect_poll_quirk)
|
||||
reg |= DWC3_GUSB3PIPECTL_RX_DETOPOLL;
|
||||
|
||||
if (dwc->tx_de_emphasis_quirk)
|
||||
reg |= DWC3_GUSB3PIPECTL_TX_DEEPH(dwc->tx_de_emphasis);
|
||||
|
||||
if (dwc->dis_u3_susphy_quirk)
|
||||
reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
|
||||
|
||||
mdelay(100);
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
|
||||
|
||||
/*
|
||||
* Above 1.94a, it is recommended to set DWC3_GUSB2PHYCFG_SUSPHY to
|
||||
* '0' during coreConsultant configuration. So default value will
|
||||
* be '0' when the core is reset. Application needs to set it to
|
||||
* '1' after the core initialization is completed.
|
||||
*/
|
||||
if (dwc->revision > DWC3_REVISION_194A)
|
||||
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
|
||||
|
||||
if (dwc->dis_u2_susphy_quirk)
|
||||
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
||||
|
||||
mdelay(100);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_core_init - Low-level initialization of DWC3 Core
|
||||
* @dwc: Pointer to our controller context structure
|
||||
@ -384,6 +452,12 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
||||
}
|
||||
dwc->revision = reg;
|
||||
|
||||
/*
|
||||
* Write Linux Version Code to our GUID register so it's easy to figure
|
||||
* out which kernel version a bug was found.
|
||||
*/
|
||||
dwc3_writel(dwc->regs, DWC3_GUID, LINUX_VERSION_CODE);
|
||||
|
||||
/* Handle USB2.0-only core configuration */
|
||||
if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
|
||||
DWC3_GHWPARAMS3_SSPHY_IFC_DIS) {
|
||||
@ -414,7 +488,6 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
|
||||
reg &= ~DWC3_GCTL_DISSCRAMBLE;
|
||||
|
||||
switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) {
|
||||
case DWC3_GHWPARAMS1_EN_PWROPT_CLK:
|
||||
@ -441,11 +514,34 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
||||
case DWC3_GHWPARAMS1_EN_PWROPT_HIB:
|
||||
/* enable hibernation here */
|
||||
dwc->nr_scratch = DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(hwparams4);
|
||||
|
||||
/*
|
||||
* REVISIT Enabling this bit so that host-mode hibernation
|
||||
* will work. Device-mode hibernation is not yet implemented.
|
||||
*/
|
||||
reg |= DWC3_GCTL_GBLHIBERNATIONEN;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(dwc->dev, "No power optimization available\n");
|
||||
}
|
||||
|
||||
/* check if current dwc3 is on simulation board */
|
||||
if (dwc->hwparams.hwparams6 & DWC3_GHWPARAMS6_EN_FPGA) {
|
||||
dev_dbg(dwc->dev, "it is on FPGA board\n");
|
||||
dwc->is_fpga = true;
|
||||
}
|
||||
|
||||
WARN_ONCE(dwc->disable_scramble_quirk && !dwc->is_fpga,
|
||||
"disable_scramble cannot be used on non-FPGA builds\n");
|
||||
|
||||
if (dwc->disable_scramble_quirk && dwc->is_fpga)
|
||||
reg |= DWC3_GCTL_DISSCRAMBLE;
|
||||
else
|
||||
reg &= ~DWC3_GCTL_DISSCRAMBLE;
|
||||
|
||||
if (dwc->u2exit_lfps_quirk)
|
||||
reg |= DWC3_GCTL_U2EXIT_LFPS;
|
||||
|
||||
/*
|
||||
* WORKAROUND: DWC3 revisions <1.90a have a bug
|
||||
* where the device can fail to connect at SuperSpeed
|
||||
@ -459,6 +555,8 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
|
||||
|
||||
dwc3_phy_setup(dwc);
|
||||
|
||||
ret = dwc3_alloc_scratch_buffers(dwc);
|
||||
if (ret)
|
||||
goto err1;
|
||||
@ -630,6 +728,9 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
struct device_node *node = dev->of_node;
|
||||
struct resource *res;
|
||||
struct dwc3 *dwc;
|
||||
u8 lpm_nyet_threshold;
|
||||
u8 tx_de_emphasis;
|
||||
u8 hird_threshold;
|
||||
|
||||
int ret;
|
||||
|
||||
@ -685,22 +786,96 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
*/
|
||||
res->start -= DWC3_GLOBALS_REGS_START;
|
||||
|
||||
/* default to highest possible threshold */
|
||||
lpm_nyet_threshold = 0xff;
|
||||
|
||||
/* default to -3.5dB de-emphasis */
|
||||
tx_de_emphasis = 1;
|
||||
|
||||
/*
|
||||
* default to assert utmi_sleep_n and use maximum allowed HIRD
|
||||
* threshold value of 0b1100
|
||||
*/
|
||||
hird_threshold = 12;
|
||||
|
||||
if (node) {
|
||||
dwc->maximum_speed = of_usb_get_maximum_speed(node);
|
||||
dwc->has_lpm_erratum = of_property_read_bool(node,
|
||||
"snps,has-lpm-erratum");
|
||||
of_property_read_u8(node, "snps,lpm-nyet-threshold",
|
||||
&lpm_nyet_threshold);
|
||||
dwc->is_utmi_l1_suspend = of_property_read_bool(node,
|
||||
"snps,is-utmi-l1-suspend");
|
||||
of_property_read_u8(node, "snps,hird-threshold",
|
||||
&hird_threshold);
|
||||
|
||||
dwc->needs_fifo_resize = of_property_read_bool(node, "tx-fifo-resize");
|
||||
dwc->needs_fifo_resize = of_property_read_bool(node,
|
||||
"tx-fifo-resize");
|
||||
dwc->dr_mode = of_usb_get_dr_mode(node);
|
||||
|
||||
dwc->disable_scramble_quirk = of_property_read_bool(node,
|
||||
"snps,disable_scramble_quirk");
|
||||
dwc->u2exit_lfps_quirk = of_property_read_bool(node,
|
||||
"snps,u2exit_lfps_quirk");
|
||||
dwc->u2ss_inp3_quirk = of_property_read_bool(node,
|
||||
"snps,u2ss_inp3_quirk");
|
||||
dwc->req_p1p2p3_quirk = of_property_read_bool(node,
|
||||
"snps,req_p1p2p3_quirk");
|
||||
dwc->del_p1p2p3_quirk = of_property_read_bool(node,
|
||||
"snps,del_p1p2p3_quirk");
|
||||
dwc->del_phy_power_chg_quirk = of_property_read_bool(node,
|
||||
"snps,del_phy_power_chg_quirk");
|
||||
dwc->lfps_filter_quirk = of_property_read_bool(node,
|
||||
"snps,lfps_filter_quirk");
|
||||
dwc->rx_detect_poll_quirk = of_property_read_bool(node,
|
||||
"snps,rx_detect_poll_quirk");
|
||||
dwc->dis_u3_susphy_quirk = of_property_read_bool(node,
|
||||
"snps,dis_u3_susphy_quirk");
|
||||
dwc->dis_u2_susphy_quirk = of_property_read_bool(node,
|
||||
"snps,dis_u2_susphy_quirk");
|
||||
|
||||
dwc->tx_de_emphasis_quirk = of_property_read_bool(node,
|
||||
"snps,tx_de_emphasis_quirk");
|
||||
of_property_read_u8(node, "snps,tx_de_emphasis",
|
||||
&tx_de_emphasis);
|
||||
} else if (pdata) {
|
||||
dwc->maximum_speed = pdata->maximum_speed;
|
||||
dwc->has_lpm_erratum = pdata->has_lpm_erratum;
|
||||
if (pdata->lpm_nyet_threshold)
|
||||
lpm_nyet_threshold = pdata->lpm_nyet_threshold;
|
||||
dwc->is_utmi_l1_suspend = pdata->is_utmi_l1_suspend;
|
||||
if (pdata->hird_threshold)
|
||||
hird_threshold = pdata->hird_threshold;
|
||||
|
||||
dwc->needs_fifo_resize = pdata->tx_fifo_resize;
|
||||
dwc->dr_mode = pdata->dr_mode;
|
||||
|
||||
dwc->disable_scramble_quirk = pdata->disable_scramble_quirk;
|
||||
dwc->u2exit_lfps_quirk = pdata->u2exit_lfps_quirk;
|
||||
dwc->u2ss_inp3_quirk = pdata->u2ss_inp3_quirk;
|
||||
dwc->req_p1p2p3_quirk = pdata->req_p1p2p3_quirk;
|
||||
dwc->del_p1p2p3_quirk = pdata->del_p1p2p3_quirk;
|
||||
dwc->del_phy_power_chg_quirk = pdata->del_phy_power_chg_quirk;
|
||||
dwc->lfps_filter_quirk = pdata->lfps_filter_quirk;
|
||||
dwc->rx_detect_poll_quirk = pdata->rx_detect_poll_quirk;
|
||||
dwc->dis_u3_susphy_quirk = pdata->dis_u3_susphy_quirk;
|
||||
dwc->dis_u2_susphy_quirk = pdata->dis_u2_susphy_quirk;
|
||||
|
||||
dwc->tx_de_emphasis_quirk = pdata->tx_de_emphasis_quirk;
|
||||
if (pdata->tx_de_emphasis)
|
||||
tx_de_emphasis = pdata->tx_de_emphasis;
|
||||
}
|
||||
|
||||
/* default to superspeed if no maximum_speed passed */
|
||||
if (dwc->maximum_speed == USB_SPEED_UNKNOWN)
|
||||
dwc->maximum_speed = USB_SPEED_SUPER;
|
||||
|
||||
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
|
||||
dwc->tx_de_emphasis = tx_de_emphasis;
|
||||
|
||||
dwc->hird_threshold = hird_threshold
|
||||
| (dwc->is_utmi_l1_suspend << 4);
|
||||
|
||||
ret = dwc3_core_get_phy(dwc);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -708,9 +883,11 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
spin_lock_init(&dwc->lock);
|
||||
platform_set_drvdata(pdev, dwc);
|
||||
|
||||
dev->dma_mask = dev->parent->dma_mask;
|
||||
dev->dma_parms = dev->parent->dma_parms;
|
||||
dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask);
|
||||
if (!dev->dma_mask) {
|
||||
dev->dma_mask = dev->parent->dma_mask;
|
||||
dev->dma_parms = dev->parent->dma_parms;
|
||||
dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask);
|
||||
}
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_get_sync(dev);
|
||||
@ -815,50 +992,6 @@ static int dwc3_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int dwc3_prepare(struct device *dev)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
switch (dwc->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
case USB_DR_MODE_OTG:
|
||||
dwc3_gadget_prepare(dwc);
|
||||
/* FALLTHROUGH */
|
||||
case USB_DR_MODE_HOST:
|
||||
default:
|
||||
dwc3_event_buffers_cleanup(dwc);
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dwc3_complete(struct device *dev)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
dwc3_event_buffers_setup(dwc);
|
||||
switch (dwc->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
case USB_DR_MODE_OTG:
|
||||
dwc3_gadget_complete(dwc);
|
||||
/* FALLTHROUGH */
|
||||
case USB_DR_MODE_HOST:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
}
|
||||
|
||||
static int dwc3_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
@ -873,7 +1006,7 @@ static int dwc3_suspend(struct device *dev)
|
||||
/* FALLTHROUGH */
|
||||
case USB_DR_MODE_HOST:
|
||||
default:
|
||||
/* do nothing */
|
||||
dwc3_event_buffers_cleanup(dwc);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -906,6 +1039,7 @@ static int dwc3_resume(struct device *dev)
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
dwc3_event_buffers_setup(dwc);
|
||||
dwc3_writel(dwc->regs, DWC3_GCTL, dwc->gctl);
|
||||
|
||||
switch (dwc->dr_mode) {
|
||||
@ -934,9 +1068,6 @@ err_usb2phy_init:
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dwc3_dev_pm_ops = {
|
||||
.prepare = dwc3_prepare,
|
||||
.complete = dwc3_complete,
|
||||
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume)
|
||||
};
|
||||
|
||||
@ -958,12 +1089,24 @@ static const struct of_device_id of_dwc3_match[] = {
|
||||
MODULE_DEVICE_TABLE(of, of_dwc3_match);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
#define ACPI_ID_INTEL_BSW "808622B7"
|
||||
|
||||
static const struct acpi_device_id dwc3_acpi_match[] = {
|
||||
{ ACPI_ID_INTEL_BSW, 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, dwc3_acpi_match);
|
||||
#endif
|
||||
|
||||
static struct platform_driver dwc3_driver = {
|
||||
.probe = dwc3_probe,
|
||||
.remove = dwc3_remove,
|
||||
.driver = {
|
||||
.name = "dwc3",
|
||||
.of_match_table = of_match_ptr(of_dwc3_match),
|
||||
.acpi_match_table = ACPI_PTR(dwc3_acpi_match),
|
||||
.pm = DWC3_PM_OPS,
|
||||
},
|
||||
};
|
||||
|
@ -166,6 +166,7 @@
|
||||
#define DWC3_GCTL_SCALEDOWN(n) ((n) << 4)
|
||||
#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3)
|
||||
#define DWC3_GCTL_DISSCRAMBLE (1 << 3)
|
||||
#define DWC3_GCTL_U2EXIT_LFPS (1 << 2)
|
||||
#define DWC3_GCTL_GBLHIBERNATIONEN (1 << 1)
|
||||
#define DWC3_GCTL_DSBLCLKGTNG (1 << 0)
|
||||
|
||||
@ -175,7 +176,17 @@
|
||||
|
||||
/* Global USB3 PIPE Control Register */
|
||||
#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
|
||||
#define DWC3_GUSB3PIPECTL_U2SSINP3OK (1 << 29)
|
||||
#define DWC3_GUSB3PIPECTL_REQP1P2P3 (1 << 24)
|
||||
#define DWC3_GUSB3PIPECTL_DEP1P2P3(n) ((n) << 19)
|
||||
#define DWC3_GUSB3PIPECTL_DEP1P2P3_MASK DWC3_GUSB3PIPECTL_DEP1P2P3(7)
|
||||
#define DWC3_GUSB3PIPECTL_DEP1P2P3_EN DWC3_GUSB3PIPECTL_DEP1P2P3(1)
|
||||
#define DWC3_GUSB3PIPECTL_DEPOCHANGE (1 << 18)
|
||||
#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17)
|
||||
#define DWC3_GUSB3PIPECTL_LFPSFILT (1 << 9)
|
||||
#define DWC3_GUSB3PIPECTL_RX_DETOPOLL (1 << 8)
|
||||
#define DWC3_GUSB3PIPECTL_TX_DEEPH_MASK DWC3_GUSB3PIPECTL_TX_DEEPH(3)
|
||||
#define DWC3_GUSB3PIPECTL_TX_DEEPH(n) ((n) << 1)
|
||||
|
||||
/* Global TX Fifo Size Register */
|
||||
#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
|
||||
@ -210,6 +221,9 @@
|
||||
#define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13)
|
||||
#define DWC3_MAX_HIBER_SCRATCHBUFS 15
|
||||
|
||||
/* Global HWPARAMS6 Register */
|
||||
#define DWC3_GHWPARAMS6_EN_FPGA (1 << 7)
|
||||
|
||||
/* Device Configuration Register */
|
||||
#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
|
||||
#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f)
|
||||
@ -243,16 +257,19 @@
|
||||
#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6))
|
||||
|
||||
/* These apply for core versions 1.94a and later */
|
||||
#define DWC3_DCTL_KEEP_CONNECT (1 << 19)
|
||||
#define DWC3_DCTL_L1_HIBER_EN (1 << 18)
|
||||
#define DWC3_DCTL_CRS (1 << 17)
|
||||
#define DWC3_DCTL_CSS (1 << 16)
|
||||
#define DWC3_DCTL_LPM_ERRATA_MASK DWC3_DCTL_LPM_ERRATA(0xf)
|
||||
#define DWC3_DCTL_LPM_ERRATA(n) ((n) << 20)
|
||||
|
||||
#define DWC3_DCTL_INITU2ENA (1 << 12)
|
||||
#define DWC3_DCTL_ACCEPTU2ENA (1 << 11)
|
||||
#define DWC3_DCTL_INITU1ENA (1 << 10)
|
||||
#define DWC3_DCTL_ACCEPTU1ENA (1 << 9)
|
||||
#define DWC3_DCTL_TSTCTRL_MASK (0xf << 1)
|
||||
#define DWC3_DCTL_KEEP_CONNECT (1 << 19)
|
||||
#define DWC3_DCTL_L1_HIBER_EN (1 << 18)
|
||||
#define DWC3_DCTL_CRS (1 << 17)
|
||||
#define DWC3_DCTL_CSS (1 << 16)
|
||||
|
||||
#define DWC3_DCTL_INITU2ENA (1 << 12)
|
||||
#define DWC3_DCTL_ACCEPTU2ENA (1 << 11)
|
||||
#define DWC3_DCTL_INITU1ENA (1 << 10)
|
||||
#define DWC3_DCTL_ACCEPTU1ENA (1 << 9)
|
||||
#define DWC3_DCTL_TSTCTRL_MASK (0xf << 1)
|
||||
|
||||
#define DWC3_DCTL_ULSTCHNGREQ_MASK (0x0f << 5)
|
||||
#define DWC3_DCTL_ULSTCHNGREQ(n) (((n) << 5) & DWC3_DCTL_ULSTCHNGREQ_MASK)
|
||||
@ -657,17 +674,41 @@ struct dwc3_scratchpad_array {
|
||||
* @regset: debugfs pointer to regdump file
|
||||
* @test_mode: true when we're entering a USB test mode
|
||||
* @test_mode_nr: test feature selector
|
||||
* @lpm_nyet_threshold: LPM NYET response threshold
|
||||
* @hird_threshold: HIRD threshold
|
||||
* @delayed_status: true when gadget driver asks for delayed status
|
||||
* @ep0_bounced: true when we used bounce buffer
|
||||
* @ep0_expect_in: true when we expect a DATA IN transfer
|
||||
* @has_hibernation: true when dwc3 was configured with Hibernation
|
||||
* @has_lpm_erratum: true when core was configured with LPM Erratum. Note that
|
||||
* there's now way for software to detect this in runtime.
|
||||
* @is_utmi_l1_suspend: the core asserts output signal
|
||||
* 0 - utmi_sleep_n
|
||||
* 1 - utmi_l1_suspend_n
|
||||
* @is_selfpowered: true when we are selfpowered
|
||||
* @is_fpga: true when we are using the FPGA board
|
||||
* @needs_fifo_resize: not all users might want fifo resizing, flag it
|
||||
* @pullups_connected: true when Run/Stop bit is set
|
||||
* @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes.
|
||||
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
|
||||
* @start_config_issued: true when StartConfig command has been issued
|
||||
* @three_stage_setup: set if we perform a three phase setup
|
||||
* @disable_scramble_quirk: set if we enable the disable scramble quirk
|
||||
* @u2exit_lfps_quirk: set if we enable u2exit lfps quirk
|
||||
* @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk
|
||||
* @req_p1p2p3_quirk: set if we enable request p1p2p3 quirk
|
||||
* @del_p1p2p3_quirk: set if we enable delay p1p2p3 quirk
|
||||
* @del_phy_power_chg_quirk: set if we enable delay phy power change quirk
|
||||
* @lfps_filter_quirk: set if we enable LFPS filter quirk
|
||||
* @rx_detect_poll_quirk: set if we enable rx_detect to polling lfps quirk
|
||||
* @dis_u3_susphy_quirk: set if we disable usb3 suspend phy
|
||||
* @dis_u2_susphy_quirk: set if we disable usb2 suspend phy
|
||||
* @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
|
||||
* @tx_de_emphasis: Tx de-emphasis value
|
||||
* 0 - -6dB de-emphasis
|
||||
* 1 - -3.5dB de-emphasis
|
||||
* 2 - No de-emphasis
|
||||
* 3 - Reserved
|
||||
*/
|
||||
struct dwc3 {
|
||||
struct usb_ctrlrequest *ctrl_req;
|
||||
@ -759,18 +800,37 @@ struct dwc3 {
|
||||
|
||||
u8 test_mode;
|
||||
u8 test_mode_nr;
|
||||
u8 lpm_nyet_threshold;
|
||||
u8 hird_threshold;
|
||||
|
||||
unsigned delayed_status:1;
|
||||
unsigned ep0_bounced:1;
|
||||
unsigned ep0_expect_in:1;
|
||||
unsigned has_hibernation:1;
|
||||
unsigned has_lpm_erratum:1;
|
||||
unsigned is_utmi_l1_suspend:1;
|
||||
unsigned is_selfpowered:1;
|
||||
unsigned is_fpga:1;
|
||||
unsigned needs_fifo_resize:1;
|
||||
unsigned pullups_connected:1;
|
||||
unsigned resize_fifos:1;
|
||||
unsigned setup_packet_pending:1;
|
||||
unsigned start_config_issued:1;
|
||||
unsigned three_stage_setup:1;
|
||||
|
||||
unsigned disable_scramble_quirk:1;
|
||||
unsigned u2exit_lfps_quirk:1;
|
||||
unsigned u2ss_inp3_quirk:1;
|
||||
unsigned req_p1p2p3_quirk:1;
|
||||
unsigned del_p1p2p3_quirk:1;
|
||||
unsigned del_phy_power_chg_quirk:1;
|
||||
unsigned lfps_filter_quirk:1;
|
||||
unsigned rx_detect_poll_quirk:1;
|
||||
unsigned dis_u3_susphy_quirk:1;
|
||||
unsigned dis_u2_susphy_quirk:1;
|
||||
|
||||
unsigned tx_de_emphasis_quirk:1;
|
||||
unsigned tx_de_emphasis:2;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
@ -964,20 +1024,9 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
|
||||
|
||||
/* power management interface */
|
||||
#if !IS_ENABLED(CONFIG_USB_DWC3_HOST)
|
||||
int dwc3_gadget_prepare(struct dwc3 *dwc);
|
||||
void dwc3_gadget_complete(struct dwc3 *dwc);
|
||||
int dwc3_gadget_suspend(struct dwc3 *dwc);
|
||||
int dwc3_gadget_resume(struct dwc3 *dwc);
|
||||
#else
|
||||
static inline int dwc3_gadget_prepare(struct dwc3 *dwc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void dwc3_gadget_complete(struct dwc3 *dwc)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int dwc3_gadget_suspend(struct dwc3 *dwc)
|
||||
{
|
||||
return 0;
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/dwc3-exynos.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/usb/otg.h>
|
||||
@ -35,6 +34,9 @@ struct dwc3_exynos {
|
||||
struct device *dev;
|
||||
|
||||
struct clk *clk;
|
||||
struct clk *susp_clk;
|
||||
struct clk *axius_clk;
|
||||
|
||||
struct regulator *vdd33;
|
||||
struct regulator *vdd10;
|
||||
};
|
||||
@ -106,7 +108,6 @@ static int dwc3_exynos_remove_child(struct device *dev, void *unused)
|
||||
static int dwc3_exynos_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3_exynos *exynos;
|
||||
struct clk *clk;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
|
||||
@ -133,17 +134,33 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
clk = devm_clk_get(dev, "usbdrd30");
|
||||
if (IS_ERR(clk)) {
|
||||
exynos->dev = dev;
|
||||
|
||||
exynos->clk = devm_clk_get(dev, "usbdrd30");
|
||||
if (IS_ERR(exynos->clk)) {
|
||||
dev_err(dev, "couldn't get clock\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
exynos->dev = dev;
|
||||
exynos->clk = clk;
|
||||
|
||||
clk_prepare_enable(exynos->clk);
|
||||
|
||||
exynos->susp_clk = devm_clk_get(dev, "usbdrd30_susp_clk");
|
||||
if (IS_ERR(exynos->susp_clk)) {
|
||||
dev_dbg(dev, "no suspend clk specified\n");
|
||||
exynos->susp_clk = NULL;
|
||||
}
|
||||
clk_prepare_enable(exynos->susp_clk);
|
||||
|
||||
if (of_device_is_compatible(node, "samsung,exynos7-dwusb3")) {
|
||||
exynos->axius_clk = devm_clk_get(dev, "usbdrd30_axius_clk");
|
||||
if (IS_ERR(exynos->axius_clk)) {
|
||||
dev_err(dev, "no AXI UpScaler clk specified\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
clk_prepare_enable(exynos->axius_clk);
|
||||
} else {
|
||||
exynos->axius_clk = NULL;
|
||||
}
|
||||
|
||||
exynos->vdd33 = devm_regulator_get(dev, "vdd33");
|
||||
if (IS_ERR(exynos->vdd33)) {
|
||||
ret = PTR_ERR(exynos->vdd33);
|
||||
@ -185,7 +202,9 @@ err4:
|
||||
err3:
|
||||
regulator_disable(exynos->vdd33);
|
||||
err2:
|
||||
clk_disable_unprepare(clk);
|
||||
clk_disable_unprepare(exynos->axius_clk);
|
||||
clk_disable_unprepare(exynos->susp_clk);
|
||||
clk_disable_unprepare(exynos->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -197,6 +216,8 @@ static int dwc3_exynos_remove(struct platform_device *pdev)
|
||||
platform_device_unregister(exynos->usb2_phy);
|
||||
platform_device_unregister(exynos->usb3_phy);
|
||||
|
||||
clk_disable_unprepare(exynos->axius_clk);
|
||||
clk_disable_unprepare(exynos->susp_clk);
|
||||
clk_disable_unprepare(exynos->clk);
|
||||
|
||||
regulator_disable(exynos->vdd33);
|
||||
@ -205,19 +226,19 @@ static int dwc3_exynos_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id exynos_dwc3_match[] = {
|
||||
{ .compatible = "samsung,exynos5250-dwusb3" },
|
||||
{ .compatible = "samsung,exynos7-dwusb3" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, exynos_dwc3_match);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int dwc3_exynos_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3_exynos *exynos = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable(exynos->axius_clk);
|
||||
clk_disable(exynos->clk);
|
||||
|
||||
regulator_disable(exynos->vdd33);
|
||||
@ -243,6 +264,7 @@ static int dwc3_exynos_resume(struct device *dev)
|
||||
}
|
||||
|
||||
clk_enable(exynos->clk);
|
||||
clk_enable(exynos->axius_clk);
|
||||
|
||||
/* runtime set active to reflect active state. */
|
||||
pm_runtime_disable(dev);
|
||||
@ -266,7 +288,7 @@ static struct platform_driver dwc3_exynos_driver = {
|
||||
.remove = dwc3_exynos_remove,
|
||||
.driver = {
|
||||
.name = "exynos-dwc3",
|
||||
.of_match_table = of_match_ptr(exynos_dwc3_match),
|
||||
.of_match_table = exynos_dwc3_match,
|
||||
.pm = DEV_PM_OPS,
|
||||
},
|
||||
};
|
||||
|
@ -104,11 +104,6 @@ static int kdwc3_probe(struct platform_device *pdev)
|
||||
kdwc->dev = dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "missing usbss resource\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
kdwc->usbss = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(kdwc->usbss))
|
||||
return PTR_ERR(kdwc->usbss);
|
||||
@ -128,6 +123,7 @@ static int kdwc3_probe(struct platform_device *pdev)
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "missing irq\n");
|
||||
error = irq;
|
||||
goto err_irq;
|
||||
}
|
||||
|
||||
|
@ -593,27 +593,12 @@ static const struct of_device_id of_dwc3_match[] = {
|
||||
MODULE_DEVICE_TABLE(of, of_dwc3_match);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int dwc3_omap_prepare(struct device *dev)
|
||||
{
|
||||
struct dwc3_omap *omap = dev_get_drvdata(dev);
|
||||
|
||||
dwc3_omap_disable_irqs(omap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dwc3_omap_complete(struct device *dev)
|
||||
{
|
||||
struct dwc3_omap *omap = dev_get_drvdata(dev);
|
||||
|
||||
dwc3_omap_enable_irqs(omap);
|
||||
}
|
||||
|
||||
static int dwc3_omap_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3_omap *omap = dev_get_drvdata(dev);
|
||||
|
||||
omap->utmi_otg_status = dwc3_omap_read_utmi_status(omap);
|
||||
dwc3_omap_disable_irqs(omap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -623,6 +608,7 @@ static int dwc3_omap_resume(struct device *dev)
|
||||
struct dwc3_omap *omap = dev_get_drvdata(dev);
|
||||
|
||||
dwc3_omap_write_utmi_status(omap, omap->utmi_otg_status);
|
||||
dwc3_omap_enable_irqs(omap);
|
||||
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
@ -632,8 +618,6 @@ static int dwc3_omap_resume(struct device *dev)
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dwc3_omap_dev_pm_ops = {
|
||||
.prepare = dwc3_omap_prepare,
|
||||
.complete = dwc3_omap_complete,
|
||||
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dwc3_omap_suspend, dwc3_omap_resume)
|
||||
};
|
||||
|
@ -25,6 +25,8 @@
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/usb_phy_generic.h>
|
||||
|
||||
#include "platform_data.h"
|
||||
|
||||
/* FIXME define these in <linux/pci_ids.h> */
|
||||
#define PCI_VENDOR_ID_SYNOPSYS 0x16c3
|
||||
#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd
|
||||
@ -102,6 +104,9 @@ static int dwc3_pci_probe(struct pci_dev *pci,
|
||||
struct dwc3_pci *glue;
|
||||
int ret;
|
||||
struct device *dev = &pci->dev;
|
||||
struct dwc3_platform_data dwc3_pdata;
|
||||
|
||||
memset(&dwc3_pdata, 0x00, sizeof(dwc3_pdata));
|
||||
|
||||
glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL);
|
||||
if (!glue)
|
||||
@ -140,6 +145,31 @@ static int dwc3_pci_probe(struct pci_dev *pci,
|
||||
res[1].name = "dwc_usb3";
|
||||
res[1].flags = IORESOURCE_IRQ;
|
||||
|
||||
if (pci->vendor == PCI_VENDOR_ID_AMD &&
|
||||
pci->device == PCI_DEVICE_ID_AMD_NL_USB) {
|
||||
dwc3_pdata.has_lpm_erratum = true;
|
||||
dwc3_pdata.lpm_nyet_threshold = 0xf;
|
||||
|
||||
dwc3_pdata.u2exit_lfps_quirk = true;
|
||||
dwc3_pdata.u2ss_inp3_quirk = true;
|
||||
dwc3_pdata.req_p1p2p3_quirk = true;
|
||||
dwc3_pdata.del_p1p2p3_quirk = true;
|
||||
dwc3_pdata.del_phy_power_chg_quirk = true;
|
||||
dwc3_pdata.lfps_filter_quirk = true;
|
||||
dwc3_pdata.rx_detect_poll_quirk = true;
|
||||
|
||||
dwc3_pdata.tx_de_emphasis_quirk = true;
|
||||
dwc3_pdata.tx_de_emphasis = 1;
|
||||
|
||||
/*
|
||||
* FIXME these quirks should be removed when AMD NL
|
||||
* taps out
|
||||
*/
|
||||
dwc3_pdata.disable_scramble_quirk = true;
|
||||
dwc3_pdata.dis_u3_susphy_quirk = true;
|
||||
dwc3_pdata.dis_u2_susphy_quirk = true;
|
||||
}
|
||||
|
||||
ret = platform_device_add_resources(dwc3, res, ARRAY_SIZE(res));
|
||||
if (ret) {
|
||||
dev_err(dev, "couldn't add resources to dwc3 device\n");
|
||||
@ -148,6 +178,10 @@ static int dwc3_pci_probe(struct pci_dev *pci,
|
||||
|
||||
pci_set_drvdata(pci, glue);
|
||||
|
||||
ret = platform_device_add_data(dwc3, &dwc3_pdata, sizeof(dwc3_pdata));
|
||||
if (ret)
|
||||
goto err3;
|
||||
|
||||
dma_set_coherent_mask(&dwc3->dev, dev->coherent_dma_mask);
|
||||
|
||||
dwc3->dev.dma_mask = dev->dma_mask;
|
||||
@ -185,6 +219,7 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BSW), },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT), },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_MRFLD), },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_NL_USB), },
|
||||
{ } /* Terminating Entry */
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table);
|
||||
|
@ -243,7 +243,7 @@ static int st_dwc3_probe(struct platform_device *pdev)
|
||||
dwc3_data->rstc_rst = devm_reset_control_get(dev, "softreset");
|
||||
if (IS_ERR(dwc3_data->rstc_rst)) {
|
||||
dev_err(&pdev->dev, "could not get reset controller\n");
|
||||
ret = PTR_ERR(dwc3_data->rstc_pwrdn);
|
||||
ret = PTR_ERR(dwc3_data->rstc_rst);
|
||||
goto undo_powerdown;
|
||||
}
|
||||
|
||||
|
@ -86,6 +86,8 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
|
||||
params.param0 = upper_32_bits(dwc->ep0_trb_addr);
|
||||
params.param1 = lower_32_bits(dwc->ep0_trb_addr);
|
||||
|
||||
trace_dwc3_prepare_trb(dep, trb);
|
||||
|
||||
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
|
||||
DWC3_DEPCMD_STARTTRANSFER, ¶ms);
|
||||
if (ret < 0) {
|
||||
@ -441,7 +443,6 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
|
||||
|
||||
case USB_DEVICE_LTM_ENABLE:
|
||||
return -EINVAL;
|
||||
break;
|
||||
|
||||
case USB_DEVICE_TEST_MODE:
|
||||
if ((wIndex & 0xff) != 0)
|
||||
@ -550,7 +551,6 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
||||
switch (state) {
|
||||
case USB_STATE_DEFAULT:
|
||||
return -EINVAL;
|
||||
break;
|
||||
|
||||
case USB_STATE_ADDRESS:
|
||||
ret = dwc3_ep0_delegate_req(dwc, ctrl);
|
||||
@ -700,35 +700,35 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
||||
|
||||
switch (ctrl->bRequest) {
|
||||
case USB_REQ_GET_STATUS:
|
||||
dwc3_trace(trace_dwc3_ep0, "USB_REQ_GET_STATUS\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "USB_REQ_GET_STATUS");
|
||||
ret = dwc3_ep0_handle_status(dwc, ctrl);
|
||||
break;
|
||||
case USB_REQ_CLEAR_FEATURE:
|
||||
dwc3_trace(trace_dwc3_ep0, "USB_REQ_CLEAR_FEATURE\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "USB_REQ_CLEAR_FEATURE");
|
||||
ret = dwc3_ep0_handle_feature(dwc, ctrl, 0);
|
||||
break;
|
||||
case USB_REQ_SET_FEATURE:
|
||||
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_FEATURE\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_FEATURE");
|
||||
ret = dwc3_ep0_handle_feature(dwc, ctrl, 1);
|
||||
break;
|
||||
case USB_REQ_SET_ADDRESS:
|
||||
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ADDRESS\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ADDRESS");
|
||||
ret = dwc3_ep0_set_address(dwc, ctrl);
|
||||
break;
|
||||
case USB_REQ_SET_CONFIGURATION:
|
||||
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_CONFIGURATION\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_CONFIGURATION");
|
||||
ret = dwc3_ep0_set_config(dwc, ctrl);
|
||||
break;
|
||||
case USB_REQ_SET_SEL:
|
||||
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_SEL\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_SEL");
|
||||
ret = dwc3_ep0_set_sel(dwc, ctrl);
|
||||
break;
|
||||
case USB_REQ_SET_ISOCH_DELAY:
|
||||
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ISOCH_DELAY\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ISOCH_DELAY");
|
||||
ret = dwc3_ep0_set_isoch_delay(dwc, ctrl);
|
||||
break;
|
||||
default:
|
||||
dwc3_trace(trace_dwc3_ep0, "Forwarding to gadget driver\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "Forwarding to gadget driver");
|
||||
ret = dwc3_ep0_delegate_req(dwc, ctrl);
|
||||
break;
|
||||
}
|
||||
@ -791,6 +791,8 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
|
||||
|
||||
trb = dwc->ep0_trb;
|
||||
|
||||
trace_dwc3_complete_trb(ep0, trb);
|
||||
|
||||
r = next_request(&ep0->request_list);
|
||||
if (!r)
|
||||
return;
|
||||
@ -855,6 +857,8 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
|
||||
dep = dwc->eps[0];
|
||||
trb = dwc->ep0_trb;
|
||||
|
||||
trace_dwc3_complete_trb(dep, trb);
|
||||
|
||||
if (!list_empty(&dep->request_list)) {
|
||||
r = next_request(&dep->request_list);
|
||||
|
||||
@ -875,7 +879,7 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
|
||||
|
||||
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
|
||||
if (status == DWC3_TRBSTS_SETUP_PENDING)
|
||||
dwc3_trace(trace_dwc3_ep0, "Setup Pending received\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "Setup Pending received");
|
||||
|
||||
dwc->ep0state = EP0_SETUP_PHASE;
|
||||
dwc3_ep0_out_start(dwc);
|
||||
|
@ -1140,8 +1140,14 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
|
||||
if (!dep->endpoint.desc) {
|
||||
dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n",
|
||||
request, ep->name);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
return -ESHUTDOWN;
|
||||
ret = -ESHUTDOWN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (WARN(req->dep != dep, "request %p belongs to '%s'\n",
|
||||
request, req->dep->name)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev_vdbg(dwc->dev, "queing request %p to %s length %d\n",
|
||||
@ -1149,6 +1155,8 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
|
||||
trace_dwc3_ep_queue(req);
|
||||
|
||||
ret = __dwc3_gadget_ep_queue(dep, req);
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return ret;
|
||||
@ -1622,8 +1630,7 @@ err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dwc3_gadget_stop(struct usb_gadget *g,
|
||||
struct usb_gadget_driver *driver)
|
||||
static int dwc3_gadget_stop(struct usb_gadget *g)
|
||||
{
|
||||
struct dwc3 *dwc = gadget_to_dwc(g);
|
||||
unsigned long flags;
|
||||
@ -2034,6 +2041,17 @@ static void dwc3_resume_gadget(struct dwc3 *dwc)
|
||||
if (dwc->gadget_driver && dwc->gadget_driver->resume) {
|
||||
spin_unlock(&dwc->lock);
|
||||
dwc->gadget_driver->resume(&dwc->gadget);
|
||||
}
|
||||
}
|
||||
|
||||
static void dwc3_reset_gadget(struct dwc3 *dwc)
|
||||
{
|
||||
if (!dwc->gadget_driver)
|
||||
return;
|
||||
|
||||
if (dwc->gadget.speed != USB_SPEED_UNKNOWN) {
|
||||
spin_unlock(&dwc->lock);
|
||||
usb_gadget_udc_reset(&dwc->gadget, dwc->gadget_driver);
|
||||
spin_lock(&dwc->lock);
|
||||
}
|
||||
}
|
||||
@ -2140,6 +2158,7 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
|
||||
|
||||
dwc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
dwc->setup_packet_pending = false;
|
||||
usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED);
|
||||
}
|
||||
|
||||
static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
|
||||
@ -2177,11 +2196,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
|
||||
dwc3_gadget_disconnect_interrupt(dwc);
|
||||
}
|
||||
|
||||
/* after reset -> Default State */
|
||||
usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT);
|
||||
|
||||
if (dwc->gadget.speed != USB_SPEED_UNKNOWN)
|
||||
dwc3_disconnect_gadget(dwc);
|
||||
dwc3_reset_gadget(dwc);
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
|
||||
@ -2287,11 +2302,20 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
|
||||
|
||||
reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold);
|
||||
|
||||
/*
|
||||
* TODO: This should be configurable. For now using
|
||||
* maximum allowed HIRD threshold value of 0b1100
|
||||
* When dwc3 revisions >= 2.40a, LPM Erratum is enabled and
|
||||
* DCFG.LPMCap is set, core responses with an ACK and the
|
||||
* BESL value in the LPM token is less than or equal to LPM
|
||||
* NYET threshold.
|
||||
*/
|
||||
reg |= DWC3_DCTL_HIRD_THRES(12);
|
||||
WARN_ONCE(dwc->revision < DWC3_REVISION_240A
|
||||
&& dwc->has_lpm_erratum,
|
||||
"LPM Erratum not available on dwc3 revisisions < 2.40a\n");
|
||||
|
||||
if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A)
|
||||
reg |= DWC3_DCTL_LPM_ERRATA(dwc->lpm_nyet_threshold);
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
} else {
|
||||
@ -2744,26 +2768,13 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
|
||||
dwc->ctrl_req, dwc->ctrl_req_addr);
|
||||
}
|
||||
|
||||
int dwc3_gadget_prepare(struct dwc3 *dwc)
|
||||
int dwc3_gadget_suspend(struct dwc3 *dwc)
|
||||
{
|
||||
if (dwc->pullups_connected) {
|
||||
dwc3_gadget_disable_irq(dwc);
|
||||
dwc3_gadget_run_stop(dwc, true, true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dwc3_gadget_complete(struct dwc3 *dwc)
|
||||
{
|
||||
if (dwc->pullups_connected) {
|
||||
dwc3_gadget_enable_irq(dwc);
|
||||
dwc3_gadget_run_stop(dwc, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
int dwc3_gadget_suspend(struct dwc3 *dwc)
|
||||
{
|
||||
__dwc3_gadget_ep_disable(dwc->eps[0]);
|
||||
__dwc3_gadget_ep_disable(dwc->eps[1]);
|
||||
|
||||
@ -2798,6 +2809,11 @@ int dwc3_gadget_resume(struct dwc3 *dwc)
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, dwc->dcfg);
|
||||
|
||||
if (dwc->pullups_connected) {
|
||||
dwc3_gadget_enable_irq(dwc);
|
||||
dwc3_gadget_run_stop(dwc, true, false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err1:
|
||||
|
@ -29,8 +29,7 @@ int dwc3_host_init(struct dwc3 *dwc)
|
||||
xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
|
||||
if (!xhci) {
|
||||
dev_err(dwc->dev, "couldn't allocate xHCI device\n");
|
||||
ret = -ENOMEM;
|
||||
goto err0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dma_set_coherent_mask(&xhci->dev, dwc->dev->coherent_dma_mask);
|
||||
@ -60,22 +59,33 @@ int dwc3_host_init(struct dwc3 *dwc)
|
||||
goto err1;
|
||||
}
|
||||
|
||||
phy_create_lookup(dwc->usb2_generic_phy, "usb2-phy",
|
||||
dev_name(&xhci->dev));
|
||||
phy_create_lookup(dwc->usb3_generic_phy, "usb3-phy",
|
||||
dev_name(&xhci->dev));
|
||||
|
||||
ret = platform_device_add(xhci);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to register xHCI device\n");
|
||||
goto err1;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy",
|
||||
dev_name(&xhci->dev));
|
||||
phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy",
|
||||
dev_name(&xhci->dev));
|
||||
err1:
|
||||
platform_device_put(xhci);
|
||||
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dwc3_host_exit(struct dwc3 *dwc)
|
||||
{
|
||||
phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy",
|
||||
dev_name(&dwc->xhci->dev));
|
||||
phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy",
|
||||
dev_name(&dwc->xhci->dev));
|
||||
platform_device_unregister(dwc->xhci);
|
||||
}
|
||||
|
@ -24,4 +24,24 @@ struct dwc3_platform_data {
|
||||
enum usb_device_speed maximum_speed;
|
||||
enum usb_dr_mode dr_mode;
|
||||
bool tx_fifo_resize;
|
||||
|
||||
unsigned is_utmi_l1_suspend:1;
|
||||
u8 hird_threshold;
|
||||
|
||||
u8 lpm_nyet_threshold;
|
||||
|
||||
unsigned disable_scramble_quirk:1;
|
||||
unsigned has_lpm_erratum:1;
|
||||
unsigned u2exit_lfps_quirk:1;
|
||||
unsigned u2ss_inp3_quirk:1;
|
||||
unsigned req_p1p2p3_quirk:1;
|
||||
unsigned del_p1p2p3_quirk:1;
|
||||
unsigned del_phy_power_chg_quirk:1;
|
||||
unsigned lfps_filter_quirk:1;
|
||||
unsigned rx_detect_poll_quirk:1;
|
||||
unsigned dis_u3_susphy_quirk:1;
|
||||
unsigned dis_u2_susphy_quirk:1;
|
||||
|
||||
unsigned tx_de_emphasis_quirk:1;
|
||||
unsigned tx_de_emphasis:2;
|
||||
};
|
||||
|
@ -61,7 +61,7 @@ DECLARE_EVENT_CLASS(dwc3_log_event,
|
||||
TP_fast_assign(
|
||||
__entry->event = event;
|
||||
),
|
||||
TP_printk("event %08x\n", __entry->event)
|
||||
TP_printk("event %08x", __entry->event)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_event, dwc3_event,
|
||||
@ -157,7 +157,7 @@ DECLARE_EVENT_CLASS(dwc3_log_generic_cmd,
|
||||
__entry->cmd = cmd;
|
||||
__entry->param = param;
|
||||
),
|
||||
TP_printk("cmd '%s' [%d] param %08x\n",
|
||||
TP_printk("cmd '%s' [%d] param %08x",
|
||||
dwc3_gadget_generic_cmd_string(__entry->cmd),
|
||||
__entry->cmd, __entry->param
|
||||
)
|
||||
@ -175,17 +175,21 @@ DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd,
|
||||
TP_STRUCT__entry(
|
||||
__dynamic_array(char, name, DWC3_MSG_MAX)
|
||||
__field(unsigned int, cmd)
|
||||
__field(struct dwc3_gadget_ep_cmd_params *, params)
|
||||
__field(u32, param0)
|
||||
__field(u32, param1)
|
||||
__field(u32, param2)
|
||||
),
|
||||
TP_fast_assign(
|
||||
snprintf(__get_str(name), DWC3_MSG_MAX, "%s", dep->name);
|
||||
__entry->cmd = cmd;
|
||||
__entry->params = params;
|
||||
__entry->param0 = params->param0;
|
||||
__entry->param1 = params->param1;
|
||||
__entry->param2 = params->param2;
|
||||
),
|
||||
TP_printk("%s: cmd '%s' [%d] params %08x %08x %08x\n",
|
||||
TP_printk("%s: cmd '%s' [%d] params %08x %08x %08x",
|
||||
__get_str(name), dwc3_gadget_ep_cmd_string(__entry->cmd),
|
||||
__entry->cmd, __entry->params->param0,
|
||||
__entry->params->param1, __entry->params->param2
|
||||
__entry->cmd, __entry->param0,
|
||||
__entry->param1, __entry->param2
|
||||
)
|
||||
);
|
||||
|
||||
@ -214,7 +218,7 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
|
||||
__entry->size = trb->size;
|
||||
__entry->ctrl = trb->ctrl;
|
||||
),
|
||||
TP_printk("%s: trb %p bph %08x bpl %08x size %08x ctrl %08x\n",
|
||||
TP_printk("%s: trb %p bph %08x bpl %08x size %08x ctrl %08x",
|
||||
__get_str(name), __entry->trb, __entry->bph, __entry->bpl,
|
||||
__entry->size, __entry->ctrl
|
||||
)
|
||||
|
@ -190,6 +190,12 @@ config USB_F_UAC2
|
||||
config USB_F_UVC
|
||||
tristate
|
||||
|
||||
config USB_F_MIDI
|
||||
tristate
|
||||
|
||||
config USB_F_HID
|
||||
tristate
|
||||
|
||||
choice
|
||||
tristate "USB Gadget Drivers"
|
||||
default USB_ETH
|
||||
@ -362,6 +368,61 @@ config USB_CONFIGFS_F_FS
|
||||
implemented in kernel space (for instance Ethernet, serial or
|
||||
mass storage) and other are implemented in user space.
|
||||
|
||||
config USB_CONFIGFS_F_UAC1
|
||||
boolean "Audio Class 1.0"
|
||||
depends on USB_CONFIGFS
|
||||
depends on SND
|
||||
select USB_LIBCOMPOSITE
|
||||
select SND_PCM
|
||||
select USB_F_UAC1
|
||||
help
|
||||
This Audio function implements 1 AudioControl interface,
|
||||
1 AudioStreaming Interface each for USB-OUT and USB-IN.
|
||||
This driver requires a real Audio codec to be present
|
||||
on the device.
|
||||
|
||||
config USB_CONFIGFS_F_UAC2
|
||||
boolean "Audio Class 2.0"
|
||||
depends on USB_CONFIGFS
|
||||
depends on SND
|
||||
select USB_LIBCOMPOSITE
|
||||
select SND_PCM
|
||||
select USB_F_UAC2
|
||||
help
|
||||
This Audio function is compatible with USB Audio Class
|
||||
specification 2.0. It implements 1 AudioControl interface,
|
||||
1 AudioStreaming Interface each for USB-OUT and USB-IN.
|
||||
This driver doesn't expect any real Audio codec to be present
|
||||
on the device - the audio streams are simply sinked to and
|
||||
sourced from a virtual ALSA sound card created. The user-space
|
||||
application may choose to do whatever it wants with the data
|
||||
received from the USB Host and choose to provide whatever it
|
||||
wants as audio data to the USB Host.
|
||||
|
||||
config USB_CONFIGFS_F_MIDI
|
||||
boolean "MIDI function"
|
||||
depends on USB_CONFIGFS
|
||||
depends on SND
|
||||
select USB_LIBCOMPOSITE
|
||||
select SND_RAWMIDI
|
||||
select USB_F_MIDI
|
||||
help
|
||||
The MIDI Function acts as a USB Audio device, with one MIDI
|
||||
input and one MIDI output. These MIDI jacks appear as
|
||||
a sound "card" in the ALSA sound system. Other MIDI
|
||||
connections can then be made on the gadget system, using
|
||||
ALSA's aconnect utility etc.
|
||||
|
||||
config USB_CONFIGFS_F_HID
|
||||
boolean "HID function"
|
||||
depends on USB_CONFIGFS
|
||||
select USB_F_HID
|
||||
help
|
||||
The HID function driver provides generic emulation of USB
|
||||
Human Interface Devices (HID).
|
||||
|
||||
For more information, see Documentation/usb/gadget_hid.txt.
|
||||
|
||||
source "drivers/usb/gadget/legacy/Kconfig"
|
||||
|
||||
endchoice
|
||||
|
@ -1246,10 +1246,49 @@ EXPORT_SYMBOL_GPL(usb_string_ids_n);
|
||||
|
||||
static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct usb_composite_dev *cdev;
|
||||
|
||||
if (req->status || req->actual != req->length)
|
||||
DBG((struct usb_composite_dev *) ep->driver_data,
|
||||
"setup complete --> %d, %d/%d\n",
|
||||
req->status, req->actual, req->length);
|
||||
|
||||
/*
|
||||
* REVIST The same ep0 requests are shared with function drivers
|
||||
* so they don't have to maintain the same ->complete() stubs.
|
||||
*
|
||||
* Because of that, we need to check for the validity of ->context
|
||||
* here, even though we know we've set it to something useful.
|
||||
*/
|
||||
if (!req->context)
|
||||
return;
|
||||
|
||||
cdev = req->context;
|
||||
|
||||
if (cdev->req == req)
|
||||
cdev->setup_pending = false;
|
||||
else if (cdev->os_desc_req == req)
|
||||
cdev->os_desc_pending = false;
|
||||
else
|
||||
WARN(1, "unknown request %p\n", req);
|
||||
}
|
||||
|
||||
static int composite_ep0_queue(struct usb_composite_dev *cdev,
|
||||
struct usb_request *req, gfp_t gfp_flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = usb_ep_queue(cdev->gadget->ep0, req, gfp_flags);
|
||||
if (ret == 0) {
|
||||
if (cdev->req == req)
|
||||
cdev->setup_pending = true;
|
||||
else if (cdev->os_desc_req == req)
|
||||
cdev->os_desc_pending = true;
|
||||
else
|
||||
WARN(1, "unknown request %p\n", req);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int count_ext_compat(struct usb_configuration *c)
|
||||
@ -1428,6 +1467,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
||||
* when we delegate to it.
|
||||
*/
|
||||
req->zero = 0;
|
||||
req->context = cdev;
|
||||
req->complete = composite_setup_complete;
|
||||
req->length = 0;
|
||||
gadget->ep0->driver_data = cdev;
|
||||
@ -1624,6 +1664,7 @@ unknown:
|
||||
int count = 0;
|
||||
|
||||
req = cdev->os_desc_req;
|
||||
req->context = cdev;
|
||||
req->complete = composite_setup_complete;
|
||||
buf = req->buf;
|
||||
os_desc_cfg = cdev->os_desc_config;
|
||||
@ -1686,8 +1727,9 @@ unknown:
|
||||
break;
|
||||
}
|
||||
req->length = value;
|
||||
req->context = cdev;
|
||||
req->zero = value < w_length;
|
||||
value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
|
||||
value = composite_ep0_queue(cdev, req, GFP_ATOMIC);
|
||||
if (value < 0) {
|
||||
DBG(cdev, "ep_queue --> %d\n", value);
|
||||
req->status = 0;
|
||||
@ -1757,8 +1799,9 @@ unknown:
|
||||
/* respond with data transfer before status phase? */
|
||||
if (value >= 0 && value != USB_GADGET_DELAYED_STATUS) {
|
||||
req->length = value;
|
||||
req->context = cdev;
|
||||
req->zero = value < w_length;
|
||||
value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
|
||||
value = composite_ep0_queue(cdev, req, GFP_ATOMIC);
|
||||
if (value < 0) {
|
||||
DBG(cdev, "ep_queue --> %d\n", value);
|
||||
req->status = 0;
|
||||
@ -1893,6 +1936,7 @@ int composite_dev_prepare(struct usb_composite_driver *composite,
|
||||
goto fail_dev;
|
||||
|
||||
cdev->req->complete = composite_setup_complete;
|
||||
cdev->req->context = cdev;
|
||||
gadget->ep0->driver_data = cdev;
|
||||
|
||||
cdev->driver = composite;
|
||||
@ -1937,6 +1981,7 @@ int composite_os_desc_req_prepare(struct usb_composite_dev *cdev,
|
||||
kfree(cdev->os_desc_req);
|
||||
goto end;
|
||||
}
|
||||
cdev->os_desc_req->context = cdev;
|
||||
cdev->os_desc_req->complete = composite_setup_complete;
|
||||
end:
|
||||
return ret;
|
||||
@ -1951,10 +1996,16 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev)
|
||||
kfree(uc);
|
||||
}
|
||||
if (cdev->os_desc_req) {
|
||||
if (cdev->os_desc_pending)
|
||||
usb_ep_dequeue(cdev->gadget->ep0, cdev->os_desc_req);
|
||||
|
||||
kfree(cdev->os_desc_req->buf);
|
||||
usb_ep_free_request(cdev->gadget->ep0, cdev->os_desc_req);
|
||||
}
|
||||
if (cdev->req) {
|
||||
if (cdev->setup_pending)
|
||||
usb_ep_dequeue(cdev->gadget->ep0, cdev->req);
|
||||
|
||||
kfree(cdev->req->buf);
|
||||
usb_ep_free_request(cdev->gadget->ep0, cdev->req);
|
||||
}
|
||||
@ -2013,8 +2064,7 @@ fail:
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static void
|
||||
composite_suspend(struct usb_gadget *gadget)
|
||||
void composite_suspend(struct usb_gadget *gadget)
|
||||
{
|
||||
struct usb_composite_dev *cdev = get_gadget_data(gadget);
|
||||
struct usb_function *f;
|
||||
@ -2037,8 +2087,7 @@ composite_suspend(struct usb_gadget *gadget)
|
||||
usb_gadget_vbus_draw(gadget, 2);
|
||||
}
|
||||
|
||||
static void
|
||||
composite_resume(struct usb_gadget *gadget)
|
||||
void composite_resume(struct usb_gadget *gadget)
|
||||
{
|
||||
struct usb_composite_dev *cdev = get_gadget_data(gadget);
|
||||
struct usb_function *f;
|
||||
@ -2158,7 +2207,8 @@ void usb_composite_setup_continue(struct usb_composite_dev *cdev)
|
||||
} else if (--cdev->delayed_status == 0) {
|
||||
DBG(cdev, "%s: Completing delayed status\n", __func__);
|
||||
req->length = 0;
|
||||
value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
|
||||
req->context = cdev;
|
||||
value = composite_ep0_queue(cdev, req, GFP_ATOMIC);
|
||||
if (value < 0) {
|
||||
DBG(cdev, "ep_queue --> %d\n", value);
|
||||
req->status = 0;
|
||||
|
@ -271,7 +271,7 @@ static ssize_t gadget_dev_desc_UDC_store(struct gadget_info *gi,
|
||||
ret = -EBUSY;
|
||||
goto err;
|
||||
}
|
||||
ret = udc_attach_driver(name, &gi->composite.gadget_driver);
|
||||
ret = usb_udc_attach_driver(name, &gi->composite.gadget_driver);
|
||||
if (ret)
|
||||
goto err;
|
||||
gi->udc_name = name;
|
||||
@ -1453,6 +1453,9 @@ static const struct usb_gadget_driver configfs_driver_template = {
|
||||
.reset = composite_disconnect,
|
||||
.disconnect = composite_disconnect,
|
||||
|
||||
.suspend = composite_suspend,
|
||||
.resume = composite_resume,
|
||||
|
||||
.max_speed = USB_SPEED_SUPER,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
|
@ -38,3 +38,7 @@ usb_f_uac2-y := f_uac2.o
|
||||
obj-$(CONFIG_USB_F_UAC2) += usb_f_uac2.o
|
||||
usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o
|
||||
obj-$(CONFIG_USB_F_UVC) += usb_f_uvc.o
|
||||
usb_f_midi-y := f_midi.o
|
||||
obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o
|
||||
usb_f_hid-y := f_hid.o
|
||||
obj-$(CONFIG_USB_F_HID) += usb_f_hid.o
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/poll.h>
|
||||
@ -21,9 +22,14 @@
|
||||
#include <linux/usb/g_hid.h>
|
||||
|
||||
#include "u_f.h"
|
||||
#include "u_hid.h"
|
||||
|
||||
#define HIDG_MINORS 4
|
||||
|
||||
static int major, minors;
|
||||
static struct class *hidg_class;
|
||||
static DEFINE_IDA(hidg_ida);
|
||||
static DEFINE_MUTEX(hidg_ida_lock); /* protects access to hidg_ida */
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/* HID gadget struct */
|
||||
@ -160,6 +166,26 @@ static struct usb_descriptor_header *hidg_fs_descriptors[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/* Strings */
|
||||
|
||||
#define CT_FUNC_HID_IDX 0
|
||||
|
||||
static struct usb_string ct_func_string_defs[] = {
|
||||
[CT_FUNC_HID_IDX].s = "HID Interface",
|
||||
{}, /* end of list */
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings ct_func_string_table = {
|
||||
.language = 0x0409, /* en-US */
|
||||
.strings = ct_func_string_defs,
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings *ct_func_strings[] = {
|
||||
&ct_func_string_table,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/* Char Device */
|
||||
|
||||
@ -552,13 +578,22 @@ const struct file_operations f_hidg_fops = {
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_ep *ep;
|
||||
struct f_hidg *hidg = func_to_hidg(f);
|
||||
struct usb_string *us;
|
||||
struct device *device;
|
||||
int status;
|
||||
dev_t dev;
|
||||
|
||||
/* maybe allocate device-global string IDs, and patch descriptors */
|
||||
us = usb_gstrings_attach(c->cdev, ct_func_strings,
|
||||
ARRAY_SIZE(ct_func_string_defs));
|
||||
if (IS_ERR(us))
|
||||
return PTR_ERR(us);
|
||||
hidg_interface_desc.iInterface = us[CT_FUNC_HID_IDX].id;
|
||||
|
||||
/* allocate instance-specific interface IDs, and patch descriptors */
|
||||
status = usb_interface_id(c, f);
|
||||
if (status < 0)
|
||||
@ -623,10 +658,16 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
if (status)
|
||||
goto fail_free_descs;
|
||||
|
||||
device_create(hidg_class, NULL, dev, NULL, "%s%d", "hidg", hidg->minor);
|
||||
device = device_create(hidg_class, NULL, dev, NULL,
|
||||
"%s%d", "hidg", hidg->minor);
|
||||
if (IS_ERR(device)) {
|
||||
status = PTR_ERR(device);
|
||||
goto del;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
del:
|
||||
cdev_del(&hidg->cdev);
|
||||
fail_free_descs:
|
||||
usb_free_all_descriptors(f);
|
||||
fail:
|
||||
@ -640,6 +681,223 @@ fail:
|
||||
return status;
|
||||
}
|
||||
|
||||
static inline int hidg_get_minor(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ida_simple_get(&hidg_ida, 0, 0, GFP_KERNEL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline struct f_hid_opts *to_f_hid_opts(struct config_item *item)
|
||||
{
|
||||
return container_of(to_config_group(item), struct f_hid_opts,
|
||||
func_inst.group);
|
||||
}
|
||||
|
||||
CONFIGFS_ATTR_STRUCT(f_hid_opts);
|
||||
CONFIGFS_ATTR_OPS(f_hid_opts);
|
||||
|
||||
static void hid_attr_release(struct config_item *item)
|
||||
{
|
||||
struct f_hid_opts *opts = to_f_hid_opts(item);
|
||||
|
||||
usb_put_function_instance(&opts->func_inst);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations hidg_item_ops = {
|
||||
.release = hid_attr_release,
|
||||
.show_attribute = f_hid_opts_attr_show,
|
||||
.store_attribute = f_hid_opts_attr_store,
|
||||
};
|
||||
|
||||
#define F_HID_OPT(name, prec, limit) \
|
||||
static ssize_t f_hid_opts_##name##_show(struct f_hid_opts *opts, char *page)\
|
||||
{ \
|
||||
int result; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
result = sprintf(page, "%d\n", opts->name); \
|
||||
mutex_unlock(&opts->lock); \
|
||||
\
|
||||
return result; \
|
||||
} \
|
||||
\
|
||||
static ssize_t f_hid_opts_##name##_store(struct f_hid_opts *opts, \
|
||||
const char *page, size_t len) \
|
||||
{ \
|
||||
int ret; \
|
||||
u##prec num; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
if (opts->refcnt) { \
|
||||
ret = -EBUSY; \
|
||||
goto end; \
|
||||
} \
|
||||
\
|
||||
ret = kstrtou##prec(page, 0, &num); \
|
||||
if (ret) \
|
||||
goto end; \
|
||||
\
|
||||
if (num > limit) { \
|
||||
ret = -EINVAL; \
|
||||
goto end; \
|
||||
} \
|
||||
opts->name = num; \
|
||||
ret = len; \
|
||||
\
|
||||
end: \
|
||||
mutex_unlock(&opts->lock); \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
static struct f_hid_opts_attribute f_hid_opts_##name = \
|
||||
__CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, f_hid_opts_##name##_show,\
|
||||
f_hid_opts_##name##_store)
|
||||
|
||||
F_HID_OPT(subclass, 8, 255);
|
||||
F_HID_OPT(protocol, 8, 255);
|
||||
F_HID_OPT(report_length, 16, 65536);
|
||||
|
||||
static ssize_t f_hid_opts_report_desc_show(struct f_hid_opts *opts, char *page)
|
||||
{
|
||||
int result;
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
result = opts->report_desc_length;
|
||||
memcpy(page, opts->report_desc, opts->report_desc_length);
|
||||
mutex_unlock(&opts->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static ssize_t f_hid_opts_report_desc_store(struct f_hid_opts *opts,
|
||||
const char *page, size_t len)
|
||||
{
|
||||
int ret = -EBUSY;
|
||||
char *d;
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
|
||||
if (opts->refcnt)
|
||||
goto end;
|
||||
if (len > PAGE_SIZE) {
|
||||
ret = -ENOSPC;
|
||||
goto end;
|
||||
}
|
||||
d = kmemdup(page, len, GFP_KERNEL);
|
||||
if (!d) {
|
||||
ret = -ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
kfree(opts->report_desc);
|
||||
opts->report_desc = d;
|
||||
opts->report_desc_length = len;
|
||||
opts->report_desc_alloc = true;
|
||||
ret = len;
|
||||
end:
|
||||
mutex_unlock(&opts->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct f_hid_opts_attribute f_hid_opts_report_desc =
|
||||
__CONFIGFS_ATTR(report_desc, S_IRUGO | S_IWUSR,
|
||||
f_hid_opts_report_desc_show,
|
||||
f_hid_opts_report_desc_store);
|
||||
|
||||
static struct configfs_attribute *hid_attrs[] = {
|
||||
&f_hid_opts_subclass.attr,
|
||||
&f_hid_opts_protocol.attr,
|
||||
&f_hid_opts_report_length.attr,
|
||||
&f_hid_opts_report_desc.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct config_item_type hid_func_type = {
|
||||
.ct_item_ops = &hidg_item_ops,
|
||||
.ct_attrs = hid_attrs,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static inline void hidg_put_minor(int minor)
|
||||
{
|
||||
ida_simple_remove(&hidg_ida, minor);
|
||||
}
|
||||
|
||||
static void hidg_free_inst(struct usb_function_instance *f)
|
||||
{
|
||||
struct f_hid_opts *opts;
|
||||
|
||||
opts = container_of(f, struct f_hid_opts, func_inst);
|
||||
|
||||
mutex_lock(&hidg_ida_lock);
|
||||
|
||||
hidg_put_minor(opts->minor);
|
||||
if (idr_is_empty(&hidg_ida.idr))
|
||||
ghid_cleanup();
|
||||
|
||||
mutex_unlock(&hidg_ida_lock);
|
||||
|
||||
if (opts->report_desc_alloc)
|
||||
kfree(opts->report_desc);
|
||||
|
||||
kfree(opts);
|
||||
}
|
||||
|
||||
static struct usb_function_instance *hidg_alloc_inst(void)
|
||||
{
|
||||
struct f_hid_opts *opts;
|
||||
struct usb_function_instance *ret;
|
||||
int status = 0;
|
||||
|
||||
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
|
||||
if (!opts)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
mutex_init(&opts->lock);
|
||||
opts->func_inst.free_func_inst = hidg_free_inst;
|
||||
ret = &opts->func_inst;
|
||||
|
||||
mutex_lock(&hidg_ida_lock);
|
||||
|
||||
if (idr_is_empty(&hidg_ida.idr)) {
|
||||
status = ghid_setup(NULL, HIDG_MINORS);
|
||||
if (status) {
|
||||
ret = ERR_PTR(status);
|
||||
kfree(opts);
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
|
||||
opts->minor = hidg_get_minor();
|
||||
if (opts->minor < 0) {
|
||||
ret = ERR_PTR(opts->minor);
|
||||
kfree(opts);
|
||||
if (idr_is_empty(&hidg_ida.idr))
|
||||
ghid_cleanup();
|
||||
goto unlock;
|
||||
}
|
||||
config_group_init_type_name(&opts->func_inst.group, "", &hid_func_type);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&hidg_ida_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hidg_free(struct usb_function *f)
|
||||
{
|
||||
struct f_hidg *hidg;
|
||||
struct f_hid_opts *opts;
|
||||
|
||||
hidg = func_to_hidg(f);
|
||||
opts = container_of(f->fi, struct f_hid_opts, func_inst);
|
||||
kfree(hidg->report_desc);
|
||||
kfree(hidg);
|
||||
mutex_lock(&opts->lock);
|
||||
--opts->refcnt;
|
||||
mutex_unlock(&opts->lock);
|
||||
}
|
||||
|
||||
static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct f_hidg *hidg = func_to_hidg(f);
|
||||
@ -654,102 +912,82 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
usb_ep_free_request(hidg->in_ep, hidg->req);
|
||||
|
||||
usb_free_all_descriptors(f);
|
||||
|
||||
kfree(hidg->report_desc);
|
||||
kfree(hidg);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/* Strings */
|
||||
|
||||
#define CT_FUNC_HID_IDX 0
|
||||
|
||||
static struct usb_string ct_func_string_defs[] = {
|
||||
[CT_FUNC_HID_IDX].s = "HID Interface",
|
||||
{}, /* end of list */
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings ct_func_string_table = {
|
||||
.language = 0x0409, /* en-US */
|
||||
.strings = ct_func_string_defs,
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings *ct_func_strings[] = {
|
||||
&ct_func_string_table,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/* usb_configuration */
|
||||
|
||||
int __init hidg_bind_config(struct usb_configuration *c,
|
||||
struct hidg_func_descriptor *fdesc, int index)
|
||||
static struct usb_function *hidg_alloc(struct usb_function_instance *fi)
|
||||
{
|
||||
struct f_hidg *hidg;
|
||||
int status;
|
||||
|
||||
if (index >= minors)
|
||||
return -ENOENT;
|
||||
|
||||
/* maybe allocate device-global string IDs, and patch descriptors */
|
||||
if (ct_func_string_defs[CT_FUNC_HID_IDX].id == 0) {
|
||||
status = usb_string_id(c->cdev);
|
||||
if (status < 0)
|
||||
return status;
|
||||
ct_func_string_defs[CT_FUNC_HID_IDX].id = status;
|
||||
hidg_interface_desc.iInterface = status;
|
||||
}
|
||||
struct f_hid_opts *opts;
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
hidg = kzalloc(sizeof *hidg, GFP_KERNEL);
|
||||
hidg = kzalloc(sizeof(*hidg), GFP_KERNEL);
|
||||
if (!hidg)
|
||||
return -ENOMEM;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
hidg->minor = index;
|
||||
hidg->bInterfaceSubClass = fdesc->subclass;
|
||||
hidg->bInterfaceProtocol = fdesc->protocol;
|
||||
hidg->report_length = fdesc->report_length;
|
||||
hidg->report_desc_length = fdesc->report_desc_length;
|
||||
hidg->report_desc = kmemdup(fdesc->report_desc,
|
||||
fdesc->report_desc_length,
|
||||
GFP_KERNEL);
|
||||
if (!hidg->report_desc) {
|
||||
kfree(hidg);
|
||||
return -ENOMEM;
|
||||
opts = container_of(fi, struct f_hid_opts, func_inst);
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
++opts->refcnt;
|
||||
|
||||
hidg->minor = opts->minor;
|
||||
hidg->bInterfaceSubClass = opts->subclass;
|
||||
hidg->bInterfaceProtocol = opts->protocol;
|
||||
hidg->report_length = opts->report_length;
|
||||
hidg->report_desc_length = opts->report_desc_length;
|
||||
if (opts->report_desc) {
|
||||
hidg->report_desc = kmemdup(opts->report_desc,
|
||||
opts->report_desc_length,
|
||||
GFP_KERNEL);
|
||||
if (!hidg->report_desc) {
|
||||
kfree(hidg);
|
||||
mutex_unlock(&opts->lock);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&opts->lock);
|
||||
|
||||
hidg->func.name = "hid";
|
||||
hidg->func.strings = ct_func_strings;
|
||||
hidg->func.bind = hidg_bind;
|
||||
hidg->func.unbind = hidg_unbind;
|
||||
hidg->func.set_alt = hidg_set_alt;
|
||||
hidg->func.disable = hidg_disable;
|
||||
hidg->func.setup = hidg_setup;
|
||||
hidg->func.free_func = hidg_free;
|
||||
|
||||
/* this could me made configurable at some point */
|
||||
hidg->qlen = 4;
|
||||
|
||||
status = usb_add_function(c, &hidg->func);
|
||||
if (status)
|
||||
kfree(hidg);
|
||||
|
||||
return status;
|
||||
return &hidg->func;
|
||||
}
|
||||
|
||||
int __init ghid_setup(struct usb_gadget *g, int count)
|
||||
DECLARE_USB_FUNCTION_INIT(hid, hidg_alloc_inst, hidg_alloc);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Fabien Chouteau");
|
||||
|
||||
int ghid_setup(struct usb_gadget *g, int count)
|
||||
{
|
||||
int status;
|
||||
dev_t dev;
|
||||
|
||||
hidg_class = class_create(THIS_MODULE, "hidg");
|
||||
|
||||
status = alloc_chrdev_region(&dev, 0, count, "hidg");
|
||||
if (!status) {
|
||||
major = MAJOR(dev);
|
||||
minors = count;
|
||||
if (IS_ERR(hidg_class)) {
|
||||
status = PTR_ERR(hidg_class);
|
||||
hidg_class = NULL;
|
||||
return status;
|
||||
}
|
||||
|
||||
return status;
|
||||
status = alloc_chrdev_region(&dev, 0, count, "hidg");
|
||||
if (status) {
|
||||
class_destroy(hidg_class);
|
||||
hidg_class = NULL;
|
||||
return status;
|
||||
}
|
||||
|
||||
major = MAJOR(dev);
|
||||
minors = count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ghid_cleanup(void)
|
||||
|
@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
@ -33,6 +34,7 @@
|
||||
#include <linux/usb/midi.h>
|
||||
|
||||
#include "u_f.h"
|
||||
#include "u_midi.h"
|
||||
|
||||
MODULE_AUTHOR("Ben Williamson");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
@ -99,7 +101,7 @@ DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
|
||||
DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16);
|
||||
|
||||
/* B.3.1 Standard AC Interface Descriptor */
|
||||
static struct usb_interface_descriptor ac_interface_desc __initdata = {
|
||||
static struct usb_interface_descriptor ac_interface_desc = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
/* .bInterfaceNumber = DYNAMIC */
|
||||
@ -110,7 +112,7 @@ static struct usb_interface_descriptor ac_interface_desc __initdata = {
|
||||
};
|
||||
|
||||
/* B.3.2 Class-Specific AC Interface Descriptor */
|
||||
static struct uac1_ac_header_descriptor_1 ac_header_desc __initdata = {
|
||||
static struct uac1_ac_header_descriptor_1 ac_header_desc = {
|
||||
.bLength = UAC_DT_AC_HEADER_SIZE(1),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubtype = USB_MS_HEADER,
|
||||
@ -121,7 +123,7 @@ static struct uac1_ac_header_descriptor_1 ac_header_desc __initdata = {
|
||||
};
|
||||
|
||||
/* B.4.1 Standard MS Interface Descriptor */
|
||||
static struct usb_interface_descriptor ms_interface_desc __initdata = {
|
||||
static struct usb_interface_descriptor ms_interface_desc = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
/* .bInterfaceNumber = DYNAMIC */
|
||||
@ -132,7 +134,7 @@ static struct usb_interface_descriptor ms_interface_desc __initdata = {
|
||||
};
|
||||
|
||||
/* B.4.2 Class-Specific MS Interface Descriptor */
|
||||
static struct usb_ms_header_descriptor ms_header_desc __initdata = {
|
||||
static struct usb_ms_header_descriptor ms_header_desc = {
|
||||
.bLength = USB_DT_MS_HEADER_SIZE,
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubtype = USB_MS_HEADER,
|
||||
@ -387,29 +389,6 @@ static void f_midi_disable(struct usb_function *f)
|
||||
usb_ep_disable(midi->out_ep);
|
||||
}
|
||||
|
||||
static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
struct f_midi *midi = func_to_midi(f);
|
||||
struct snd_card *card;
|
||||
|
||||
DBG(cdev, "unbind\n");
|
||||
|
||||
/* just to be sure */
|
||||
f_midi_disable(f);
|
||||
|
||||
card = midi->card;
|
||||
midi->card = NULL;
|
||||
if (card)
|
||||
snd_card_free(card);
|
||||
|
||||
kfree(midi->id);
|
||||
midi->id = NULL;
|
||||
|
||||
usb_free_all_descriptors(f);
|
||||
kfree(midi);
|
||||
}
|
||||
|
||||
static int f_midi_snd_free(struct snd_device *device)
|
||||
{
|
||||
return 0;
|
||||
@ -654,6 +633,14 @@ static struct snd_rawmidi_ops gmidi_out_ops = {
|
||||
.trigger = f_midi_out_trigger
|
||||
};
|
||||
|
||||
static inline void f_midi_unregister_card(struct f_midi *midi)
|
||||
{
|
||||
if (midi->card) {
|
||||
snd_card_free(midi->card);
|
||||
midi->card = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* register as a sound "card" */
|
||||
static int f_midi_register_card(struct f_midi *midi)
|
||||
{
|
||||
@ -715,17 +702,13 @@ static int f_midi_register_card(struct f_midi *midi)
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (midi->card) {
|
||||
snd_card_free(midi->card);
|
||||
midi->card = NULL;
|
||||
}
|
||||
f_midi_unregister_card(midi);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* MIDI function driver setup/binding */
|
||||
|
||||
static int __init
|
||||
f_midi_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_descriptor_header **midi_function;
|
||||
struct usb_midi_in_jack_descriptor jack_in_ext_desc[MAX_PORTS];
|
||||
@ -734,15 +717,23 @@ f_midi_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
struct usb_midi_out_jack_descriptor_1 jack_out_emb_desc[MAX_PORTS];
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
struct f_midi *midi = func_to_midi(f);
|
||||
struct usb_string *us;
|
||||
int status, n, jack = 1, i = 0;
|
||||
|
||||
midi->gadget = cdev->gadget;
|
||||
tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi);
|
||||
status = f_midi_register_card(midi);
|
||||
if (status < 0)
|
||||
goto fail_register;
|
||||
|
||||
/* maybe allocate device-global string ID */
|
||||
if (midi_string_defs[0].id == 0) {
|
||||
status = usb_string_id(c->cdev);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
midi_string_defs[0].id = status;
|
||||
us = usb_gstrings_attach(c->cdev, midi_strings,
|
||||
ARRAY_SIZE(midi_string_defs));
|
||||
if (IS_ERR(us)) {
|
||||
status = PTR_ERR(us);
|
||||
goto fail;
|
||||
}
|
||||
ac_interface_desc.iInterface = us[STRING_FUNC_IDX].id;
|
||||
|
||||
/* We have two interfaces, AudioControl and MIDIStreaming */
|
||||
status = usb_interface_id(c, f);
|
||||
@ -892,6 +883,8 @@ fail_f_midi:
|
||||
kfree(midi_function);
|
||||
usb_free_descriptors(f->hs_descriptors);
|
||||
fail:
|
||||
f_midi_unregister_card(midi);
|
||||
fail_register:
|
||||
/* we might as well release our claims on endpoints */
|
||||
if (midi->out_ep)
|
||||
midi->out_ep->driver_data = NULL;
|
||||
@ -903,42 +896,235 @@ fail:
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* f_midi_bind_config - add USB MIDI function to a configuration
|
||||
* @c: the configuration to supcard the USB audio function
|
||||
* @index: the soundcard index to use for the ALSA device creation
|
||||
* @id: the soundcard id to use for the ALSA device creation
|
||||
* @buflen: the buffer length to use
|
||||
* @qlen the number of read requests to pre-allocate
|
||||
* Context: single threaded during gadget setup
|
||||
*
|
||||
* Returns zero on success, else negative errno.
|
||||
*/
|
||||
int __init f_midi_bind_config(struct usb_configuration *c,
|
||||
int index, char *id,
|
||||
unsigned int in_ports,
|
||||
unsigned int out_ports,
|
||||
unsigned int buflen,
|
||||
unsigned int qlen)
|
||||
static inline struct f_midi_opts *to_f_midi_opts(struct config_item *item)
|
||||
{
|
||||
struct f_midi *midi;
|
||||
int status, i;
|
||||
return container_of(to_config_group(item), struct f_midi_opts,
|
||||
func_inst.group);
|
||||
}
|
||||
|
||||
/* sanity check */
|
||||
if (in_ports > MAX_PORTS || out_ports > MAX_PORTS)
|
||||
return -EINVAL;
|
||||
CONFIGFS_ATTR_STRUCT(f_midi_opts);
|
||||
CONFIGFS_ATTR_OPS(f_midi_opts);
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
midi = kzalloc(sizeof *midi, GFP_KERNEL);
|
||||
if (!midi) {
|
||||
status = -ENOMEM;
|
||||
goto fail;
|
||||
static void midi_attr_release(struct config_item *item)
|
||||
{
|
||||
struct f_midi_opts *opts = to_f_midi_opts(item);
|
||||
|
||||
usb_put_function_instance(&opts->func_inst);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations midi_item_ops = {
|
||||
.release = midi_attr_release,
|
||||
.show_attribute = f_midi_opts_attr_show,
|
||||
.store_attribute = f_midi_opts_attr_store,
|
||||
};
|
||||
|
||||
#define F_MIDI_OPT(name, test_limit, limit) \
|
||||
static ssize_t f_midi_opts_##name##_show(struct f_midi_opts *opts, char *page) \
|
||||
{ \
|
||||
int result; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
result = sprintf(page, "%d\n", opts->name); \
|
||||
mutex_unlock(&opts->lock); \
|
||||
\
|
||||
return result; \
|
||||
} \
|
||||
\
|
||||
static ssize_t f_midi_opts_##name##_store(struct f_midi_opts *opts, \
|
||||
const char *page, size_t len) \
|
||||
{ \
|
||||
int ret; \
|
||||
u32 num; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
if (opts->refcnt) { \
|
||||
ret = -EBUSY; \
|
||||
goto end; \
|
||||
} \
|
||||
\
|
||||
ret = kstrtou32(page, 0, &num); \
|
||||
if (ret) \
|
||||
goto end; \
|
||||
\
|
||||
if (test_limit && num > limit) { \
|
||||
ret = -EINVAL; \
|
||||
goto end; \
|
||||
} \
|
||||
opts->name = num; \
|
||||
ret = len; \
|
||||
\
|
||||
end: \
|
||||
mutex_unlock(&opts->lock); \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
static struct f_midi_opts_attribute f_midi_opts_##name = \
|
||||
__CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, f_midi_opts_##name##_show, \
|
||||
f_midi_opts_##name##_store)
|
||||
|
||||
F_MIDI_OPT(index, true, SNDRV_CARDS);
|
||||
F_MIDI_OPT(buflen, false, 0);
|
||||
F_MIDI_OPT(qlen, false, 0);
|
||||
F_MIDI_OPT(in_ports, true, MAX_PORTS);
|
||||
F_MIDI_OPT(out_ports, true, MAX_PORTS);
|
||||
|
||||
static ssize_t f_midi_opts_id_show(struct f_midi_opts *opts, char *page)
|
||||
{
|
||||
int result;
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
result = strlcpy(page, opts->id, PAGE_SIZE);
|
||||
mutex_unlock(&opts->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static ssize_t f_midi_opts_id_store(struct f_midi_opts *opts,
|
||||
const char *page, size_t len)
|
||||
{
|
||||
int ret;
|
||||
char *c;
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
if (opts->refcnt) {
|
||||
ret = -EBUSY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
for (i = 0; i < in_ports; i++) {
|
||||
c = kstrndup(page, len, GFP_KERNEL);
|
||||
if (!c) {
|
||||
ret = -ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
if (opts->id_allocated)
|
||||
kfree(opts->id);
|
||||
opts->id = c;
|
||||
opts->id_allocated = true;
|
||||
ret = len;
|
||||
end:
|
||||
mutex_unlock(&opts->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct f_midi_opts_attribute f_midi_opts_id =
|
||||
__CONFIGFS_ATTR(id, S_IRUGO | S_IWUSR, f_midi_opts_id_show,
|
||||
f_midi_opts_id_store);
|
||||
|
||||
static struct configfs_attribute *midi_attrs[] = {
|
||||
&f_midi_opts_index.attr,
|
||||
&f_midi_opts_buflen.attr,
|
||||
&f_midi_opts_qlen.attr,
|
||||
&f_midi_opts_in_ports.attr,
|
||||
&f_midi_opts_out_ports.attr,
|
||||
&f_midi_opts_id.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct config_item_type midi_func_type = {
|
||||
.ct_item_ops = &midi_item_ops,
|
||||
.ct_attrs = midi_attrs,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static void f_midi_free_inst(struct usb_function_instance *f)
|
||||
{
|
||||
struct f_midi_opts *opts;
|
||||
|
||||
opts = container_of(f, struct f_midi_opts, func_inst);
|
||||
|
||||
if (opts->id_allocated)
|
||||
kfree(opts->id);
|
||||
|
||||
kfree(opts);
|
||||
}
|
||||
|
||||
static struct usb_function_instance *f_midi_alloc_inst(void)
|
||||
{
|
||||
struct f_midi_opts *opts;
|
||||
|
||||
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
|
||||
if (!opts)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mutex_init(&opts->lock);
|
||||
opts->func_inst.free_func_inst = f_midi_free_inst;
|
||||
opts->index = SNDRV_DEFAULT_IDX1;
|
||||
opts->id = SNDRV_DEFAULT_STR1;
|
||||
opts->buflen = 256;
|
||||
opts->qlen = 32;
|
||||
opts->in_ports = 1;
|
||||
opts->out_ports = 1;
|
||||
|
||||
config_group_init_type_name(&opts->func_inst.group, "",
|
||||
&midi_func_type);
|
||||
|
||||
return &opts->func_inst;
|
||||
}
|
||||
|
||||
static void f_midi_free(struct usb_function *f)
|
||||
{
|
||||
struct f_midi *midi;
|
||||
struct f_midi_opts *opts;
|
||||
int i;
|
||||
|
||||
midi = func_to_midi(f);
|
||||
opts = container_of(f->fi, struct f_midi_opts, func_inst);
|
||||
kfree(midi->id);
|
||||
mutex_lock(&opts->lock);
|
||||
for (i = opts->in_ports - 1; i >= 0; --i)
|
||||
kfree(midi->in_port[i]);
|
||||
kfree(midi);
|
||||
--opts->refcnt;
|
||||
mutex_unlock(&opts->lock);
|
||||
}
|
||||
|
||||
static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
struct f_midi *midi = func_to_midi(f);
|
||||
struct snd_card *card;
|
||||
|
||||
DBG(cdev, "unbind\n");
|
||||
|
||||
/* just to be sure */
|
||||
f_midi_disable(f);
|
||||
|
||||
card = midi->card;
|
||||
midi->card = NULL;
|
||||
if (card)
|
||||
snd_card_free(card);
|
||||
|
||||
usb_free_all_descriptors(f);
|
||||
}
|
||||
|
||||
static struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
|
||||
{
|
||||
struct f_midi *midi;
|
||||
struct f_midi_opts *opts;
|
||||
int status, i;
|
||||
|
||||
opts = container_of(fi, struct f_midi_opts, func_inst);
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
/* sanity check */
|
||||
if (opts->in_ports > MAX_PORTS || opts->out_ports > MAX_PORTS) {
|
||||
mutex_unlock(&opts->lock);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
midi = kzalloc(sizeof(*midi), GFP_KERNEL);
|
||||
if (!midi) {
|
||||
mutex_unlock(&opts->lock);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
for (i = 0; i < opts->in_ports; i++) {
|
||||
struct gmidi_in_port *port = kzalloc(sizeof(*port), GFP_KERNEL);
|
||||
|
||||
if (!port) {
|
||||
status = -ENOMEM;
|
||||
mutex_unlock(&opts->lock);
|
||||
goto setup_fail;
|
||||
}
|
||||
|
||||
@ -948,39 +1134,37 @@ int __init f_midi_bind_config(struct usb_configuration *c,
|
||||
midi->in_port[i] = port;
|
||||
}
|
||||
|
||||
midi->gadget = c->cdev->gadget;
|
||||
tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi);
|
||||
|
||||
/* set up ALSA midi devices */
|
||||
midi->in_ports = in_ports;
|
||||
midi->out_ports = out_ports;
|
||||
status = f_midi_register_card(midi);
|
||||
if (status < 0)
|
||||
goto setup_fail;
|
||||
midi->id = kstrdup(opts->id, GFP_KERNEL);
|
||||
if (opts->id && !midi->id) {
|
||||
status = -ENOMEM;
|
||||
mutex_unlock(&opts->lock);
|
||||
goto kstrdup_fail;
|
||||
}
|
||||
midi->in_ports = opts->in_ports;
|
||||
midi->out_ports = opts->out_ports;
|
||||
midi->index = opts->index;
|
||||
midi->buflen = opts->buflen;
|
||||
midi->qlen = opts->qlen;
|
||||
++opts->refcnt;
|
||||
mutex_unlock(&opts->lock);
|
||||
|
||||
midi->func.name = "gmidi function";
|
||||
midi->func.strings = midi_strings;
|
||||
midi->func.bind = f_midi_bind;
|
||||
midi->func.unbind = f_midi_unbind;
|
||||
midi->func.set_alt = f_midi_set_alt;
|
||||
midi->func.disable = f_midi_disable;
|
||||
midi->func.name = "gmidi function";
|
||||
midi->func.bind = f_midi_bind;
|
||||
midi->func.unbind = f_midi_unbind;
|
||||
midi->func.set_alt = f_midi_set_alt;
|
||||
midi->func.disable = f_midi_disable;
|
||||
midi->func.free_func = f_midi_free;
|
||||
|
||||
midi->id = kstrdup(id, GFP_KERNEL);
|
||||
midi->index = index;
|
||||
midi->buflen = buflen;
|
||||
midi->qlen = qlen;
|
||||
|
||||
status = usb_add_function(c, &midi->func);
|
||||
if (status)
|
||||
goto setup_fail;
|
||||
|
||||
return 0;
|
||||
return &midi->func;
|
||||
|
||||
kstrdup_fail:
|
||||
f_midi_unregister_card(midi);
|
||||
setup_fail:
|
||||
for (--i; i >= 0; i--)
|
||||
kfree(midi->in_port[i]);
|
||||
kfree(midi);
|
||||
fail:
|
||||
return status;
|
||||
return ERR_PTR(status);
|
||||
}
|
||||
|
||||
DECLARE_USB_FUNCTION_INIT(midi, f_midi_alloc_inst, f_midi_alloc);
|
||||
|
@ -1441,6 +1441,9 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
|
||||
status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function,
|
||||
NULL);
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* NOTE: all that is done without knowing or caring about
|
||||
* the network link ... which is unavailable to this code
|
||||
|
@ -375,8 +375,7 @@ static struct sk_buff *rndis_add_header(struct gether *port,
|
||||
struct sk_buff *skb2;
|
||||
|
||||
skb2 = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type));
|
||||
if (skb2)
|
||||
rndis_add_hdr(skb2);
|
||||
rndis_add_hdr(skb2);
|
||||
|
||||
dev_kfree_skb(skb);
|
||||
return skb2;
|
||||
|
42
drivers/usb/gadget/function/u_hid.h
Normal file
42
drivers/usb/gadget/function/u_hid.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* u_hid.h
|
||||
*
|
||||
* Utility definitions for the hid function
|
||||
*
|
||||
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef U_HID_H
|
||||
#define U_HID_H
|
||||
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
struct f_hid_opts {
|
||||
struct usb_function_instance func_inst;
|
||||
int minor;
|
||||
unsigned char subclass;
|
||||
unsigned char protocol;
|
||||
unsigned short report_length;
|
||||
unsigned short report_desc_length;
|
||||
unsigned char *report_desc;
|
||||
bool report_desc_alloc;
|
||||
|
||||
/*
|
||||
* Protect the data form concurrent access by read/write
|
||||
* and create symlink/remove symlink.
|
||||
*/
|
||||
struct mutex lock;
|
||||
int refcnt;
|
||||
};
|
||||
|
||||
int ghid_setup(struct usb_gadget *g, int count);
|
||||
void ghid_cleanup(void);
|
||||
|
||||
#endif /* U_HID_H */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user