USB patches for 4.12-rc1

Here is the big USB patchset for 4.12-rc1.
 
 Lots of good stuff here, after many many many attempts, the kernel
 finally has a working typeC interface, many thanks to the Heikki and
 Guenter and others who have taken the time to get this merged.  It
 wasn't an easy path for them at all.
 
 There's also a staging driver that uses this new api, which is why it's
 coming in through this tree.
 
 Along with that, there's the usual huge number of changes for gadget
 drivers, xhci, and other stuff.  Johan also finally refactored pretty
 much every driver that was looking at USB endpoints to do it in a common
 way, which will help prevent any "badly-formed" devices from causing
 problems in drivers.  That too wasn't a simple task.
 
 All of these have been in linux-next for a while with no reported
 issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCWQvEIQ8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+yny4gCePCXxnrQdMWE+IMXf1H1hMubLkVkAn0ZWgQkq
 BspgO7ZmGb+9Fpf6YvNz
 =nwAu
 -----END PGP SIGNATURE-----

Merge tag 'usb-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb

Pull USB updates from Greg KH:
 "Here is the big USB patchset for 4.12-rc1.

  Lots of good stuff here, after many many many attempts, the kernel
  finally has a working typeC interface, many thanks to Heikki and
  Guenter and others who have taken the time to get this merged. It
  wasn't an easy path for them at all.

  There's also a staging driver that uses this new api, which is why
  it's coming in through this tree.

  Along with that, there's the usual huge number of changes for gadget
  drivers, xhci, and other stuff. Johan also finally refactored pretty
  much every driver that was looking at USB endpoints to do it in a
  common way, which will help prevent any "badly-formed" devices from
  causing problems in drivers. That too wasn't a simple task.

  All of these have been in linux-next for a while with no reported
  issues"

* tag 'usb-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (263 commits)
  staging: typec: Fairchild FUSB302 Type-c chip driver
  staging: typec: Type-C Port Controller Interface driver (tcpci)
  staging: typec: USB Type-C Port Manager (tcpm)
  usb: host: xhci: remove #ifdef around PM functions
  usb: musb: don't mark of_dev_auxdata as initdata
  usb: misc: legousbtower: Fix buffers on stack
  USB: Revert "cdc-wdm: fix "out-of-sync" due to missing notifications"
  usb: Make sure usb/phy/of gets built-in
  USB: storage: e-mail update in drivers/usb/storage/unusual_devs.h
  usb: host: xhci: print correct command ring address
  usb: host: xhci: delete sp_dma_buffers for scratchpad
  usb: host: xhci: using correct specification chapter reference for DCBAAP
  xhci: switch to pci_alloc_irq_vectors
  usb: host: xhci-plat: set resume_quirk() for R-Car controllers
  usb: host: xhci-plat: add resume_quirk()
  usb: host: xhci-plat: enable clk in resume timing
  usb: host: plat: Enable xHCI plat runtime PM
  USB: serial: ftdi_sio: add device ID for Microsemi/Arrow SF2PLUS Dev Kit
  USB: serial: constify static arrays
  usb: fix some references for /proc/bus/usb
  ...
This commit is contained in:
Linus Torvalds 2017-05-04 18:03:51 -07:00
commit 8f28472a73
230 changed files with 15574 additions and 3813 deletions

View File

@ -0,0 +1,276 @@
USB Type-C port devices (eg. /sys/class/typec/port0/)
What: /sys/class/typec/<port>/data_role
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
The supported USB data roles. This attribute can be used for
requesting data role swapping on the port. Swapping is supported
as synchronous operation, so write(2) to the attribute will not
return until the operation has finished. The attribute is
notified about role changes so that poll(2) on the attribute
wakes up. Change on the role will also generate uevent
KOBJ_CHANGE on the port. The current role is show in brackets,
for example "[host] device" when DRP port is in host mode.
Valid values: host, device
What: /sys/class/typec/<port>/power_role
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
The supported power roles. This attribute can be used to request
power role swap on the port when the port supports USB Power
Delivery. Swapping is supported as synchronous operation, so
write(2) to the attribute will not return until the operation
has finished. The attribute is notified about role changes so
that poll(2) on the attribute wakes up. Change on the role will
also generate uevent KOBJ_CHANGE. The current role is show in
brackets, for example "[source] sink" when in source mode.
Valid values: source, sink
What: /sys/class/typec/<port>/vconn_source
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
Shows is the port VCONN Source. This attribute can be used to
request VCONN swap to change the VCONN Source during connection
when both the port and the partner support USB Power Delivery.
Swapping is supported as synchronous operation, so write(2) to
the attribute will not return until the operation has finished.
The attribute is notified about VCONN source changes so that
poll(2) on the attribute wakes up. Change on VCONN source also
generates uevent KOBJ_CHANGE.
Valid values:
- "no" when the port is not the VCONN Source
- "yes" when the port is the VCONN Source
What: /sys/class/typec/<port>/power_operation_mode
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
Shows the current power operational mode the port is in. The
power operation mode means current level for VBUS. In case USB
Power Delivery communication is used for negotiating the levels,
power operation mode should show "usb_power_delivery".
Valid values:
- default
- 1.5A
- 3.0A
- usb_power_delivery
What: /sys/class/typec/<port>/preferred_role
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
The user space can notify the driver about the preferred role.
It should be handled as enabling of Try.SRC or Try.SNK, as
defined in USB Type-C specification, in the port drivers. By
default the preferred role should come from the platform.
Valid values: source, sink, none (to remove preference)
What: /sys/class/typec/<port>/supported_accessory_modes
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
Space separated list of accessory modes, defined in the USB
Type-C specification, the port supports.
What: /sys/class/typec/<port>/usb_power_delivery_revision
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
Revision number of the supported USB Power Delivery
specification, or 0 when USB Power Delivery is not supported.
What: /sys/class/typec/<port>/usb_typec_revision
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
Revision number of the supported USB Type-C specification.
USB Type-C partner devices (eg. /sys/class/typec/port0-partner/)
What: /sys/class/typec/<port>-partner/accessory_mode
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
Shows the Accessory Mode name when the partner is an Accessory.
The Accessory Modes are defined in USB Type-C Specification.
What: /sys/class/typec/<port>-partner/supports_usb_power_delivery
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
Shows if the partner supports USB Power Delivery communication:
Valid values: yes, no
What: /sys/class/typec/<port>-partner>/identity/
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
This directory appears only if the port device driver is capable
of showing the result of Discover Identity USB power delivery
command. That will not always be possible even when USB power
delivery is supported, for example when USB power delivery
communication for the port is mostly handled in firmware. If the
directory exists, it will have an attribute file for every VDO
in Discover Identity command result.
What: /sys/class/typec/<port>-partner/identity/id_header
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
ID Header VDO part of Discover Identity command result. The
value will show 0 until Discover Identity command result becomes
available. The value can be polled.
What: /sys/class/typec/<port>-partner/identity/cert_stat
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
Cert Stat VDO part of Discover Identity command result. The
value will show 0 until Discover Identity command result becomes
available. The value can be polled.
What: /sys/class/typec/<port>-partner/identity/product
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
Product VDO part of Discover Identity command result. The value
will show 0 until Discover Identity command result becomes
available. The value can be polled.
USB Type-C cable devices (eg. /sys/class/typec/port0-cable/)
Note: Electronically Marked Cables will have a device also for one cable plug
(eg. /sys/class/typec/port0-plug0). If the cable is active and has also SOP
Double Prime controller (USB Power Deliver specification ch. 2.4) it will have
second device also for the other plug. Both plugs may have alternate modes as
described in USB Type-C and USB Power Delivery specifications.
What: /sys/class/typec/<port>-cable/type
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
Shows if the cable is active.
Valid values: active, passive
What: /sys/class/typec/<port>-cable/plug_type
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
Shows type of the plug on the cable:
- type-a - Standard A
- type-b - Standard B
- type-c
- captive
What: /sys/class/typec/<port>-cable/identity/
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
This directory appears only if the port device driver is capable
of showing the result of Discover Identity USB power delivery
command. That will not always be possible even when USB power
delivery is supported. If the directory exists, it will have an
attribute for every VDO returned by Discover Identity command.
What: /sys/class/typec/<port>-cable/identity/id_header
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
ID Header VDO part of Discover Identity command result. The
value will show 0 until Discover Identity command result becomes
available. The value can be polled.
What: /sys/class/typec/<port>-cable/identity/cert_stat
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
Cert Stat VDO part of Discover Identity command result. The
value will show 0 until Discover Identity command result becomes
available. The value can be polled.
What: /sys/class/typec/<port>-cable/identity/product
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
Product VDO part of Discover Identity command result. The value
will show 0 until Discover Identity command result becomes
available. The value can be polled.
Alternate Mode devices.
The alternate modes will have Standard or Vendor ID (SVID) assigned by USB-IF.
The ports, partners and cable plugs can have alternate modes. A supported SVID
will consist of a set of modes. Every SVID a port/partner/plug supports will
have a device created for it, and every supported mode for a supported SVID will
have its own directory under that device. Below <dev> refers to the device for
the alternate mode.
What: /sys/class/typec/<port|partner|cable>/<dev>/svid
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
The SVID (Standard or Vendor ID) assigned by USB-IF for this
alternate mode.
What: /sys/class/typec/<port|partner|cable>/<dev>/mode<index>/
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
Every supported mode will have its own directory. The name of
a mode will be "mode<index>" (for example mode1), where <index>
is the actual index to the mode VDO returned by Discover Modes
USB power delivery command.
What: /sys/class/typec/<port|partner|cable>/<dev>/mode<index>/description
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
Shows description of the mode. The description is optional for
the drivers, just like with the Billboard Devices.
What: /sys/class/typec/<port|partner|cable>/<dev>/mode<index>/vdo
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
Shows the VDO in hexadecimal returned by Discover Modes command
for this mode.
What: /sys/class/typec/<port|partner|cable>/<dev>/mode<index>/active
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
Shows if the mode is active or not. The attribute can be used
for entering/exiting the mode with partners and cable plugs, and
with the port alternate modes it can be used for disabling
support for specific alternate modes. Entering/exiting modes is
supported as synchronous operation so write(2) to the attribute
does not return until the enter/exit mode operation has
finished. The attribute is notified when the mode is
entered/exited so poll(2) on the attribute wakes up.
Entering/exiting a mode will also generate uevent KOBJ_CHANGE.
Valid values: yes, no
What: /sys/class/typec/<port>/<dev>/mode<index>/supported_roles
Date: April 2017
Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Description:
Space separated list of the supported roles.
This attribute is available for the devices describing the
alternate modes a port supports, and it will not be exposed with
the devices presenting the alternate modes the partners or cable
plugs support.
Valid values: source, sink

View File

@ -0,0 +1,9 @@
What: /sys/bus/platform/devices/ci_hdrc.0/role
Date: Mar 2017
Contact: Peter Chen <peter.chen@nxp.com>
Description:
It returns string "gadget" or "host" when read it, it indicates
current controller role.
It will do role switch when write "gadget" or "host" to it.
Only controller at dual-role configuration supports writing.

View File

@ -0,0 +1,15 @@
What: /sys/devices/platform/<renesas_usb3's name>/role
Date: March 2017
KernelVersion: 4.13
Contact: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Description:
This file can be read and write.
The file can show/change the drd mode of usb.
Write the following string to change the mode:
"host" - switching mode from peripheral to host.
"peripheral" - switching mode from host to peripheral.
Read the file, then it shows the following strings:
"host" - The mode is host now.
"peripheral" - The mode is peripheral now.

View File

@ -6,12 +6,11 @@ This binding describes a usb3.0 phy for mt65xx platforms of Medaitek SoC.
Required properties (controller (parent) node):
- compatible : should be one of
"mediatek,mt2701-u3phy"
"mediatek,mt2712-u3phy"
"mediatek,mt8173-u3phy"
- reg : offset and length of register for phy, exclude port's
register.
- clocks : a list of phandle + clock-specifier pairs, one for each
entry in clock-names
- clock-names : must contain
- clocks : (deprecated, use port's clocks instead) a list of phandle +
clock-specifier pairs, one for each entry in clock-names
- clock-names : (deprecated, use port's one instead) must contain
"u3phya_ref": for reference clock of usb3.0 analog phy.
Required nodes : a sub-node is required for each port the controller
@ -19,8 +18,19 @@ Required nodes : a sub-node is required for each port the controller
'reg' property is used inside these nodes to describe
the controller's topology.
Optional properties (controller (parent) node):
- reg : offset and length of register shared by multiple ports,
exclude port's private register. It is needed on mt2701
and mt8173, but not on mt2712.
Required properties (port (child) node):
- reg : address and length of the register set for the port.
- clocks : a list of phandle + clock-specifier pairs, one for each
entry in clock-names
- clock-names : must contain
"ref": 48M reference clock for HighSpeed analog phy; and 26M
reference clock for SuperSpeed analog phy, sometimes is
24M, 25M or 27M, depended on platform.
- #phy-cells : should be 1 (See second example)
cell after port phandle is phy type from:
- PHY_TYPE_USB2
@ -31,21 +41,31 @@ Example:
u3phy: usb-phy@11290000 {
compatible = "mediatek,mt8173-u3phy";
reg = <0 0x11290000 0 0x800>;
clocks = <&apmixedsys CLK_APMIXED_REF2USB_TX>;
clock-names = "u3phya_ref";
#address-cells = <2>;
#size-cells = <2>;
ranges;
status = "okay";
phy_port0: port@11290800 {
reg = <0 0x11290800 0 0x800>;
u2port0: usb-phy@11290800 {
reg = <0 0x11290800 0 0x100>;
clocks = <&apmixedsys CLK_APMIXED_REF2USB_TX>;
clock-names = "ref";
#phy-cells = <1>;
status = "okay";
};
phy_port1: port@11291000 {
reg = <0 0x11291000 0 0x800>;
u3port0: usb-phy@11290900 {
reg = <0 0x11290800 0 0x700>;
clocks = <&clk26m>;
clock-names = "ref";
#phy-cells = <1>;
status = "okay";
};
u2port1: usb-phy@11291000 {
reg = <0 0x11291000 0 0x100>;
clocks = <&apmixedsys CLK_APMIXED_REF2USB_TX>;
clock-names = "ref";
#phy-cells = <1>;
status = "okay";
};
@ -64,7 +84,54 @@ Example:
usb30: usb@11270000 {
...
phys = <&phy_port0 PHY_TYPE_USB3>;
phy-names = "usb3-0";
phys = <&u2port0 PHY_TYPE_USB2>, <&u3port0 PHY_TYPE_USB3>;
phy-names = "usb2-0", "usb3-0";
...
};
Layout differences of banks between mt8173/mt2701 and mt2712
-------------------------------------------------------------
mt8173 and mt2701:
port offset bank
shared 0x0000 SPLLC
0x0100 FMREG
u2 port0 0x0800 U2PHY_COM
u3 port0 0x0900 U3PHYD
0x0a00 U3PHYD_BANK2
0x0b00 U3PHYA
0x0c00 U3PHYA_DA
u2 port1 0x1000 U2PHY_COM
u3 port1 0x1100 U3PHYD
0x1200 U3PHYD_BANK2
0x1300 U3PHYA
0x1400 U3PHYA_DA
u2 port2 0x1800 U2PHY_COM
...
mt2712:
port offset bank
u2 port0 0x0000 MISC
0x0100 FMREG
0x0300 U2PHY_COM
u3 port0 0x0700 SPLLC
0x0800 CHIP
0x0900 U3PHYD
0x0a00 U3PHYD_BANK2
0x0b00 U3PHYA
0x0c00 U3PHYA_DA
u2 port1 0x1000 MISC
0x1100 FMREG
0x1300 U2PHY_COM
u3 port1 0x1700 SPLLC
0x1800 CHIP
0x1900 U3PHYD
0x1a00 U3PHYD_BANK2
0x1b00 U3PHYA
0x1c00 U3PHYA_DA
u2 port2 0x2000 MISC
...
SPLLC shared by u3 ports and FMREG shared by u2 ports on
mt8173/mt2701 are put back into each port; a new bank MISC for
u2 ports and CHIP for u3 ports are added on mt2712.

View File

@ -2,6 +2,7 @@ ROCKCHIP USB2.0 PHY WITH INNO IP BLOCK
Required properties (phy (parent) node):
- compatible : should be one of the listed compatibles:
* "rockchip,rk3328-usb2phy"
* "rockchip,rk3366-usb2phy"
* "rockchip,rk3399-usb2phy"
- reg : the address offset of grf for usb-phy configuration.
@ -11,6 +12,11 @@ Required properties (phy (parent) node):
Optional properties:
- clocks : phandle + phy specifier pair, for the input clock of phy.
- clock-names : input clock name of phy, must be "phyclk".
- assigned-clocks : phandle of usb 480m clock.
- assigned-clock-parents : parent of usb 480m clock, select between
usb-phy output 480m and xin24m.
Refer to clk/clock-bindings.txt for generic clock
consumer properties.
Required nodes : a sub-node is required for each port the phy provides.
The sub-node name is used to identify host or otg port,

View File

@ -0,0 +1,106 @@
Qualcomm QMP PHY controller
===========================
QMP phy controller supports physical layer functionality for a number of
controllers on Qualcomm chipsets, such as, PCIe, UFS, and USB.
Required properties:
- compatible: compatible list, contains:
"qcom,msm8996-qmp-pcie-phy" for 14nm PCIe phy on msm8996,
"qcom,msm8996-qmp-usb3-phy" for 14nm USB3 phy on msm8996.
- reg: offset and length of register set for PHY's common serdes block.
- #clock-cells: must be 1
- Phy pll outputs a bunch of clocks for Tx, Rx and Pipe
interface (for pipe based PHYs). These clock are then gate-controlled
by gcc.
- #address-cells: must be 1
- #size-cells: must be 1
- ranges: must be present
- clocks: a list of phandles and clock-specifier pairs,
one for each entry in clock-names.
- clock-names: "cfg_ahb" for phy config clock,
"aux" for phy aux clock,
"ref" for 19.2 MHz ref clk,
For "qcom,msm8996-qmp-pcie-phy" must contain:
"aux", "cfg_ahb", "ref".
For "qcom,msm8996-qmp-usb3-phy" must contain:
"aux", "cfg_ahb", "ref".
- resets: a list of phandles and reset controller specifier pairs,
one for each entry in reset-names.
- reset-names: "phy" for reset of phy block,
"common" for phy common block reset,
"cfg" for phy's ahb cfg block reset (Optional).
For "qcom,msm8996-qmp-pcie-phy" must contain:
"phy", "common", "cfg".
For "qcom,msm8996-qmp-usb3-phy" must contain
"phy", "common".
- vdda-phy-supply: Phandle to a regulator supply to PHY core block.
- vdda-pll-supply: Phandle to 1.8V regulator supply to PHY refclk pll block.
Optional properties:
- vddp-ref-clk-supply: Phandle to a regulator supply to any specific refclk
pll block.
Required nodes:
- Each device node of QMP phy is required to have as many child nodes as
the number of lanes the PHY has.
Required properties for child node:
- reg: list of offset and length pairs of register sets for PHY blocks -
tx, rx and pcs.
- #phy-cells: must be 0
- clocks: a list of phandles and clock-specifier pairs,
one for each entry in clock-names.
- clock-names: Must contain following for pcie and usb qmp phys:
"pipe<lane-number>" for pipe clock specific to each lane.
- resets: a list of phandles and reset controller specifier pairs,
one for each entry in reset-names.
- reset-names: Must contain following for pcie qmp phys:
"lane<lane-number>" for reset specific to each lane.
Example:
phy@34000 {
compatible = "qcom,msm8996-qmp-pcie-phy";
reg = <0x34000 0x488>;
#clock-cells = <1>;
#address-cells = <1>;
#size-cells = <1>;
ranges;
clocks = <&gcc GCC_PCIE_PHY_AUX_CLK>,
<&gcc GCC_PCIE_PHY_CFG_AHB_CLK>,
<&gcc GCC_PCIE_CLKREF_CLK>;
clock-names = "aux", "cfg_ahb", "ref";
vdda-phy-supply = <&pm8994_l28>;
vdda-pll-supply = <&pm8994_l12>;
resets = <&gcc GCC_PCIE_PHY_BCR>,
<&gcc GCC_PCIE_PHY_COM_BCR>,
<&gcc GCC_PCIE_PHY_COM_NOCSR_BCR>;
reset-names = "phy", "common", "cfg";
pciephy_0: lane@35000 {
reg = <0x35000 0x130>,
<0x35200 0x200>,
<0x35400 0x1dc>;
#phy-cells = <0>;
clocks = <&gcc GCC_PCIE_0_PIPE_CLK>;
clock-names = "pipe0";
resets = <&gcc GCC_PCIE_0_PHY_BCR>;
reset-names = "lane0";
};
pciephy_1: lane@36000 {
...
...
};

View File

@ -0,0 +1,43 @@
Qualcomm QUSB2 phy controller
=============================
QUSB2 controller supports LS/FS/HS usb connectivity on Qualcomm chipsets.
Required properties:
- compatible: compatible list, contains "qcom,msm8996-qusb2-phy".
- reg: offset and length of the PHY register set.
- #phy-cells: must be 0.
- clocks: a list of phandles and clock-specifier pairs,
one for each entry in clock-names.
- clock-names: must be "cfg_ahb" for phy config clock,
"ref" for 19.2 MHz ref clk,
"iface" for phy interface clock (Optional).
- vdda-pll-supply: Phandle to 1.8V regulator supply to PHY refclk pll block.
- vdda-phy-dpdm-supply: Phandle to 3.1V regulator supply to Dp/Dm port signals.
- resets: Phandle to reset to phy block.
Optional properties:
- nvmem-cells: Phandle to nvmem cell that contains 'HS Tx trim'
tuning parameter value for qusb2 phy.
- qcom,tcsr-syscon: Phandle to TCSR syscon register region.
Example:
hsusb_phy: phy@7411000 {
compatible = "qcom,msm8996-qusb2-phy";
reg = <0x7411000 0x180>;
#phy-cells = <0>;
clocks = <&gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
<&gcc GCC_RX1_USB2_CLKREF_CLK>,
clock-names = "cfg_ahb", "ref";
vdda-pll-supply = <&pm8994_l12>;
vdda-phy-dpdm-supply = <&pm8994_l24>;
resets = <&gcc GCC_QUSB2PHY_PRIM_BCR>;
nvmem-cells = <&qusb2p_hstx_trim>;
};

View File

@ -30,6 +30,7 @@ Optional Properties:
- reset-names: Only allow the following entries:
- phy-reset
- resets: Must contain an entry for each entry in reset-names.
- vbus-supply: power-supply phandle for vbus power source
Example:

View File

@ -15,6 +15,7 @@ Required properties:
- reg : a list of offset + length pairs
- reg-names :
* "phy_ctrl"
* "pmu0" for H3, V3s and A64
* "pmu1"
* "pmu2" for sun4i, sun6i or sun7i
- #phy-cells : from the generic phy bindings, must be 1

View File

@ -8,6 +8,8 @@ From RK3368 SoCs, the GRF is divided into two sections,
- SGRF, used for general secure system,
- PMUGRF, used for always on system
On RK3328 SoCs, the GRF adds a section for USB2PHYGRF,
Required Properties:
- compatible: GRF should be one of the following:
@ -23,6 +25,8 @@ Required Properties:
- "rockchip,rk3399-pmugrf", "syscon": for rk3399
- compatible: SGRF should be one of the following
- "rockchip,rk3288-sgrf", "syscon": for rk3288
- compatible: USB2PHYGRF should be one of the followings
- "rockchip,rk3328-usb2phy-grf", "syscon": for rk3328
- reg: physical base address of the controller and length of memory mapped
region.

View File

@ -1,7 +1,9 @@
* EHCI controller, Orion Marvell variants
Required properties:
- compatible: must be "marvell,orion-ehci"
- compatible: must be one of the following
"marvell,orion-ehci"
"marvell,armada-3700-ehci"
- reg: physical base address of the controller and length of memory mapped
region.
- interrupts: The EHCI interrupt

View File

@ -22,6 +22,7 @@ Optional properties:
property is used if any real OTG features(HNP/SRP/ADP)
is enabled, if ADP is required, otg-rev should be
0x0200 or above.
- companion: phandle of a companion
- hnp-disable: tells OTG controllers we want to disable OTG HNP, normally HNP
is the basic function of real OTG except you want it
to be a srp-capable only B device.

View File

@ -145,8 +145,8 @@ dev_hint
A camera is specified by its type (the number from the camera model,
like PCA645, PCVC750VC, etc) and optionally the serial number (visible
in /proc/bus/usb/devices). A hint consists of a string with the following
format::
in /sys/kernel/debug/usb/devices). A hint consists of a string with the
following format::
[type[.serialnumber]:]node

184
Documentation/usb/typec.rst Normal file
View File

@ -0,0 +1,184 @@
USB Type-C connector class
==========================
Introduction
------------
The typec class is meant for describing the USB Type-C ports in a system to the
user space in unified fashion. The class is designed to provide nothing else
except the user space interface implementation in hope that it can be utilized
on as many platforms as possible.
The platforms are expected to register every USB Type-C port they have with the
class. In a normal case the registration will be done by a USB Type-C or PD PHY
driver, but it may be a driver for firmware interface such as UCSI, driver for
USB PD controller or even driver for Thunderbolt3 controller. This document
considers the component registering the USB Type-C ports with the class as "port
driver".
On top of showing the capabilities, the class also offer user space control over
the roles and alternate modes of ports, partners and cable plugs when the port
driver is capable of supporting those features.
The class provides an API for the port drivers described in this document. The
attributes are described in Documentation/ABI/testing/sysfs-class-typec.
User space interface
--------------------
Every port will be presented as its own device under /sys/class/typec/. The
first port will be named "port0", the second "port1" and so on.
When connected, the partner will be presented also as its own device under
/sys/class/typec/. The parent of the partner device will always be the port it
is attached to. The partner attached to port "port0" will be named
"port0-partner". Full path to the device would be
/sys/class/typec/port0/port0-partner/.
The cable and the two plugs on it may also be optionally presented as their own
devices under /sys/class/typec/. The cable attached to the port "port0" port
will be named port0-cable and the plug on the SOP Prime end (see USB Power
Delivery Specification ch. 2.4) will be named "port0-plug0" and on the SOP
Double Prime end "port0-plug1". The parent of a cable will always be the port,
and the parent of the cable plugs will always be the cable.
If the port, partner or cable plug supports Alternate Modes, every supported
Alternate Mode SVID will have their own device describing them. Note that the
Alternate Mode devices will not be attached to the typec class. The parent of an
alternate mode will be the device that supports it, so for example an alternate
mode of port0-partner will be presented under /sys/class/typec/port0-partner/.
Every mode that is supported will have its own group under the Alternate Mode
device named "mode<index>", for example /sys/class/typec/port0/<alternate
mode>/mode1/. The requests for entering/exiting a mode can be done with "active"
attribute file in that group.
Driver API
----------
Registering the ports
~~~~~~~~~~~~~~~~~~~~~
The port drivers will describe every Type-C port they control with struct
typec_capability data structure, and register them with the following API:
.. kernel-doc:: drivers/usb/typec/typec.c
:functions: typec_register_port typec_unregister_port
When registering the ports, the prefer_role member in struct typec_capability
deserves special notice. If the port that is being registered does not have
initial role preference, which means the port does not execute Try.SNK or
Try.SRC by default, the member must have value TYPEC_NO_PREFERRED_ROLE.
Otherwise if the port executes Try.SNK by default, the member must have value
TYPEC_DEVICE, and with Try.SRC the value must be TYPEC_HOST.
Registering Partners
~~~~~~~~~~~~~~~~~~~~
After successful connection of a partner, the port driver needs to register the
partner with the class. Details about the partner need to be described in struct
typec_partner_desc. The class copies the details of the partner during
registration. The class offers the following API for registering/unregistering
partners.
.. kernel-doc:: drivers/usb/typec/typec.c
:functions: typec_register_partner typec_unregister_partner
The class will provide a handle to struct typec_partner if the registration was
successful, or NULL.
If the partner is USB Power Delivery capable, and the port driver is able to
show the result of Discover Identity command, the partner descriptor structure
should include handle to struct usb_pd_identity instance. The class will then
create a sysfs directory for the identity under the partner device. The result
of Discover Identity command can then be reported with the following API:
.. kernel-doc:: drivers/usb/typec/typec.c
:functions: typec_partner_set_identity
Registering Cables
~~~~~~~~~~~~~~~~~~
After successful connection of a cable that supports USB Power Delivery
Structured VDM "Discover Identity", the port driver needs to register the cable
and one or two plugs, depending if there is CC Double Prime controller present
in the cable or not. So a cable capable of SOP Prime communication, but not SOP
Double Prime communication, should only have one plug registered. For more
information about SOP communication, please read chapter about it from the
latest USB Power Delivery specification.
The plugs are represented as their own devices. The cable is registered first,
followed by registration of the cable plugs. The cable will be the parent device
for the plugs. Details about the cable need to be described in struct
typec_cable_desc and about a plug in struct typec_plug_desc. The class copies
the details during registration. The class offers the following API for
registering/unregistering cables and their plugs:
.. kernel-doc:: drivers/usb/typec/typec.c
:functions: typec_register_cable typec_unregister_cable typec_register_plug
typec_unregister_plug
The class will provide a handle to struct typec_cable and struct typec_plug if
the registration is successful, or NULL if it isn't.
If the cable is USB Power Delivery capable, and the port driver is able to show
the result of Discover Identity command, the cable descriptor structure should
include handle to struct usb_pd_identity instance. The class will then create a
sysfs directory for the identity under the cable device. The result of Discover
Identity command can then be reported with the following API:
.. kernel-doc:: drivers/usb/typec/typec.c
:functions: typec_cable_set_identity
Notifications
~~~~~~~~~~~~~
When the partner has executed a role change, or when the default roles change
during connection of a partner or cable, the port driver must use the following
APIs to report it to the class:
.. kernel-doc:: drivers/usb/typec/typec.c
:functions: typec_set_data_role typec_set_pwr_role typec_set_vconn_role
typec_set_pwr_opmode
Alternate Modes
~~~~~~~~~~~~~~~
USB Type-C ports, partners and cable plugs may support Alternate Modes. Each
Alternate Mode will have identifier called SVID, which is either a Standard ID
given by USB-IF or vendor ID, and each supported SVID can have 1 - 6 modes. The
class provides struct typec_mode_desc for describing individual mode of a SVID,
and struct typec_altmode_desc which is a container for all the supported modes.
Ports that support Alternate Modes need to register each SVID they support with
the following API:
.. kernel-doc:: drivers/usb/typec/typec.c
:functions: typec_port_register_altmode
If a partner or cable plug provides a list of SVIDs as response to USB Power
Delivery Structured VDM Discover SVIDs message, each SVID needs to be
registered.
API for the partners:
.. kernel-doc:: drivers/usb/typec/typec.c
:functions: typec_partner_register_altmode
API for the Cable Plugs:
.. kernel-doc:: drivers/usb/typec/typec.c
:functions: typec_plug_register_altmode
So ports, partners and cable plugs will register the alternate modes with their
own functions, but the registration will always return a handle to struct
typec_altmode on success, or NULL. The unregistration will happen with the same
function:
.. kernel-doc:: drivers/usb/typec/typec.c
:functions: typec_unregister_altmode
If a partner or cable plug enters or exits a mode, the port driver needs to
notify the class with the following API:
.. kernel-doc:: drivers/usb/typec/typec.c
:functions: typec_altmode_update_active

View File

@ -13248,6 +13248,15 @@ F: drivers/usb/
F: include/linux/usb.h
F: include/linux/usb/
USB TYPEC SUBSYSTEM
M: Heikki Krogerus <heikki.krogerus@linux.intel.com>
L: linux-usb@vger.kernel.org
S: Maintained
F: Documentation/ABI/testing/sysfs-class-typec
F: Documentation/usb/typec.rst
F: drivers/usb/typec/
F: include/linux/usb/typec.h
USB UHCI DRIVER
M: Alan Stern <stern@rowland.harvard.edu>
L: linux-usb@vger.kernel.org

View File

@ -116,6 +116,12 @@
status = "okay";
};
/* CON27 */
&usb2 {
status = "okay";
};
&mdio {
status = "okay";
phy0: ethernet-phy@0 {

View File

@ -200,6 +200,13 @@
status = "disabled";
};
usb2: usb@5e000 {
compatible = "marvell,armada-3700-ehci";
reg = <0x5e000 0x2000>;
interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
status = "disabled";
};
xor@60900 {
compatible = "marvell,armada-3700-xor";
reg = <0x60900 0x100

View File

@ -104,6 +104,7 @@ obj-$(CONFIG_USB_PHY) += usb/
obj-$(CONFIG_USB) += usb/
obj-$(CONFIG_PCI) += usb/
obj-$(CONFIG_USB_GADGET) += usb/
obj-$(CONFIG_OF) += usb/
obj-$(CONFIG_SERIO) += input/serio/
obj-$(CONFIG_GAMEPORT) += input/gameport/
obj-$(CONFIG_INPUT) += input/

View File

@ -439,6 +439,25 @@ config PHY_STIH407_USB
Enable this support to enable the picoPHY device used by USB2
and USB3 controllers on STMicroelectronics STiH407 SoC families.
config PHY_QCOM_QMP
tristate "Qualcomm QMP PHY Driver"
depends on OF && COMMON_CLK && (ARCH_QCOM || COMPILE_TEST)
select GENERIC_PHY
help
Enable this to support the QMP PHY transceiver that is used
with controllers such as PCIe, UFS, and USB on Qualcomm chips.
config PHY_QCOM_QUSB2
tristate "Qualcomm QUSB2 PHY Driver"
depends on OF && (ARCH_QCOM || COMPILE_TEST)
depends on NVMEM || !NVMEM
select GENERIC_PHY
help
Enable this to support the HighSpeed QUSB2 PHY transceiver for USB
controllers on Qualcomm chips. This driver supports the high-speed
PHY which is usually paired with either the ChipIdea or Synopsys DWC3
USB IPs on MSM SOCs.
config PHY_QCOM_UFS
tristate "Qualcomm UFS PHY driver"
depends on OF && ARCH_QCOM

View File

@ -50,6 +50,8 @@ obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY) += phy-spear1310-miphy.o
obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) += phy-spear1340-miphy.o
obj-$(CONFIG_PHY_XGENE) += phy-xgene.o
obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o
obj-$(CONFIG_PHY_QCOM_QMP) += phy-qcom-qmp.o
obj-$(CONFIG_PHY_QCOM_QUSB2) += phy-qcom-qusb2.o
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o

View File

@ -2,6 +2,7 @@
* Broadcom Northstar USB 3.0 PHY Driver
*
* Copyright (C) 2016 Rafał Miłecki <rafal@milecki.pl>
* Copyright (C) 2016 Broadcom
*
* All magic values used for initialization (and related comments) were obtained
* from Broadcom's SDK:
@ -23,6 +24,23 @@
#define BCM_NS_USB3_MII_MNG_TIMEOUT_US 1000 /* usecs */
#define BCM_NS_USB3_PHY_BASE_ADDR_REG 0x1f
#define BCM_NS_USB3_PHY_PLL30_BLOCK 0x8000
#define BCM_NS_USB3_PHY_TX_PMD_BLOCK 0x8040
#define BCM_NS_USB3_PHY_PIPE_BLOCK 0x8060
/* Registers of PLL30 block */
#define BCM_NS_USB3_PLL_CONTROL 0x01
#define BCM_NS_USB3_PLLA_CONTROL0 0x0a
#define BCM_NS_USB3_PLLA_CONTROL1 0x0b
/* Registers of TX PMD block */
#define BCM_NS_USB3_TX_PMD_CONTROL1 0x01
/* Registers of PIPE block */
#define BCM_NS_USB3_LFPS_CMP 0x02
#define BCM_NS_USB3_LFPS_DEGLITCH 0x03
enum bcm_ns_family {
BCM_NS_UNKNOWN,
BCM_NS_AX,
@ -76,8 +94,10 @@ static inline int bcm_ns_usb3_mii_mng_wait_idle(struct bcm_ns_usb3 *usb3)
usecs_to_jiffies(BCM_NS_USB3_MII_MNG_TIMEOUT_US));
}
static int bcm_ns_usb3_mii_mng_write32(struct bcm_ns_usb3 *usb3, u32 value)
static int bcm_ns_usb3_mdio_phy_write(struct bcm_ns_usb3 *usb3, u16 reg,
u16 value)
{
u32 tmp = 0;
int err;
err = bcm_ns_usb3_mii_mng_wait_idle(usb3);
@ -86,7 +106,11 @@ static int bcm_ns_usb3_mii_mng_write32(struct bcm_ns_usb3 *usb3, u32 value)
return err;
}
writel(value, usb3->ccb_mii + BCMA_CCB_MII_MNG_CMD_DATA);
/* TODO: Use a proper MDIO bus layer */
tmp |= 0x58020000; /* Magic value for MDIO PHY write */
tmp |= reg << 18;
tmp |= value;
writel(tmp, usb3->ccb_mii + BCMA_CCB_MII_MNG_CMD_DATA);
return 0;
}
@ -102,21 +126,22 @@ static int bcm_ns_usb3_phy_init_ns_bx(struct bcm_ns_usb3 *usb3)
udelay(2);
/* USB3 PLL Block */
err = bcm_ns_usb3_mii_mng_write32(usb3, 0x587e8000);
err = bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG,
BCM_NS_USB3_PHY_PLL30_BLOCK);
if (err < 0)
return err;
/* Assert Ana_Pllseq start */
bcm_ns_usb3_mii_mng_write32(usb3, 0x58061000);
bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLL_CONTROL, 0x1000);
/* Assert CML Divider ratio to 26 */
bcm_ns_usb3_mii_mng_write32(usb3, 0x582a6400);
bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL0, 0x6400);
/* Asserting PLL Reset */
bcm_ns_usb3_mii_mng_write32(usb3, 0x582ec000);
bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL1, 0xc000);
/* Deaaserting PLL Reset */
bcm_ns_usb3_mii_mng_write32(usb3, 0x582e8000);
bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL1, 0x8000);
/* Waiting MII Mgt interface idle */
bcm_ns_usb3_mii_mng_wait_idle(usb3);
@ -125,22 +150,24 @@ static int bcm_ns_usb3_phy_init_ns_bx(struct bcm_ns_usb3 *usb3)
writel(0, usb3->dmp + BCMA_RESET_CTL);
/* PLL frequency monitor enable */
bcm_ns_usb3_mii_mng_write32(usb3, 0x58069000);
bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLL_CONTROL, 0x9000);
/* PIPE Block */
bcm_ns_usb3_mii_mng_write32(usb3, 0x587e8060);
bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG,
BCM_NS_USB3_PHY_PIPE_BLOCK);
/* CMPMAX & CMPMINTH setting */
bcm_ns_usb3_mii_mng_write32(usb3, 0x580af30d);
bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_LFPS_CMP, 0xf30d);
/* DEGLITCH MIN & MAX setting */
bcm_ns_usb3_mii_mng_write32(usb3, 0x580e6302);
bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_LFPS_DEGLITCH, 0x6302);
/* TXPMD block */
bcm_ns_usb3_mii_mng_write32(usb3, 0x587e8040);
bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG,
BCM_NS_USB3_PHY_TX_PMD_BLOCK);
/* Enabling SSC */
bcm_ns_usb3_mii_mng_write32(usb3, 0x58061003);
bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_TX_PMD_CONTROL1, 0x1003);
/* Waiting MII Mgt interface idle */
bcm_ns_usb3_mii_mng_wait_idle(usb3);
@ -159,22 +186,24 @@ static int bcm_ns_usb3_phy_init_ns_ax(struct bcm_ns_usb3 *usb3)
udelay(2);
/* PLL30 block */
err = bcm_ns_usb3_mii_mng_write32(usb3, 0x587e8000);
err = bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG,
BCM_NS_USB3_PHY_PLL30_BLOCK);
if (err < 0)
return err;
bcm_ns_usb3_mii_mng_write32(usb3, 0x582a6400);
bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL0, 0x6400);
bcm_ns_usb3_mii_mng_write32(usb3, 0x587e80e0);
bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, 0x80e0);
bcm_ns_usb3_mii_mng_write32(usb3, 0x580a009c);
bcm_ns_usb3_mdio_phy_write(usb3, 0x02, 0x009c);
/* Enable SSC */
bcm_ns_usb3_mii_mng_write32(usb3, 0x587e8040);
bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG,
BCM_NS_USB3_PHY_TX_PMD_BLOCK);
bcm_ns_usb3_mii_mng_write32(usb3, 0x580a21d3);
bcm_ns_usb3_mdio_phy_write(usb3, 0x02, 0x21d3);
bcm_ns_usb3_mii_mng_write32(usb3, 0x58061003);
bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_TX_PMD_CONTROL1, 0x1003);
/* Waiting MII Mgt interface idle */
bcm_ns_usb3_mii_mng_wait_idle(usb3);

View File

@ -14,12 +14,12 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/exynos5-pmu.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/soc/samsung/exynos-regs-pmu.h>
struct exynos_dp_video_phy_drvdata {
u32 phy_ctrl_offset;
@ -36,7 +36,7 @@ static int exynos_dp_video_phy_power_on(struct phy *phy)
/* Disable power isolation on DP-PHY */
return regmap_update_bits(state->regs, state->drvdata->phy_ctrl_offset,
EXYNOS5_PHY_ENABLE, EXYNOS5_PHY_ENABLE);
EXYNOS4_PHY_ENABLE, EXYNOS4_PHY_ENABLE);
}
static int exynos_dp_video_phy_power_off(struct phy *phy)
@ -45,7 +45,7 @@ static int exynos_dp_video_phy_power_off(struct phy *phy)
/* Enable power isolation on DP-PHY */
return regmap_update_bits(state->regs, state->drvdata->phy_ctrl_offset,
EXYNOS5_PHY_ENABLE, 0);
EXYNOS4_PHY_ENABLE, 0);
}
static const struct phy_ops exynos_dp_video_phy_ops = {

View File

@ -12,8 +12,6 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon/exynos4-pmu.h>
#include <linux/mfd/syscon/exynos5-pmu.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
@ -21,6 +19,7 @@
#include <linux/phy/phy.h>
#include <linux/regmap.h>
#include <linux/spinlock.h>
#include <linux/soc/samsung/exynos-regs-pmu.h>
#include <linux/mfd/syscon.h>
enum exynos_mipi_phy_id {
@ -64,7 +63,7 @@ static const struct mipi_phy_device_desc s5pv210_mipi_phy = {
{
/* EXYNOS_MIPI_PHY_ID_CSIS0 */
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0,
.enable_val = EXYNOS4_MIPI_PHY_ENABLE,
.enable_val = EXYNOS4_PHY_ENABLE,
.enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0),
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
.resetn_val = EXYNOS4_MIPI_PHY_SRESETN,
@ -73,7 +72,7 @@ static const struct mipi_phy_device_desc s5pv210_mipi_phy = {
}, {
/* EXYNOS_MIPI_PHY_ID_DSIM0 */
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0,
.enable_val = EXYNOS4_MIPI_PHY_ENABLE,
.enable_val = EXYNOS4_PHY_ENABLE,
.enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0),
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
.resetn_val = EXYNOS4_MIPI_PHY_MRESETN,
@ -82,7 +81,7 @@ static const struct mipi_phy_device_desc s5pv210_mipi_phy = {
}, {
/* EXYNOS_MIPI_PHY_ID_CSIS1 */
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM1,
.enable_val = EXYNOS4_MIPI_PHY_ENABLE,
.enable_val = EXYNOS4_PHY_ENABLE,
.enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1),
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
.resetn_val = EXYNOS4_MIPI_PHY_SRESETN,
@ -91,7 +90,7 @@ static const struct mipi_phy_device_desc s5pv210_mipi_phy = {
}, {
/* EXYNOS_MIPI_PHY_ID_DSIM1 */
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS1,
.enable_val = EXYNOS4_MIPI_PHY_ENABLE,
.enable_val = EXYNOS4_PHY_ENABLE,
.enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1),
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
.resetn_val = EXYNOS4_MIPI_PHY_MRESETN,
@ -109,47 +108,47 @@ static const struct mipi_phy_device_desc exynos5420_mipi_phy = {
{
/* EXYNOS_MIPI_PHY_ID_CSIS0 */
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0,
.enable_val = EXYNOS5_PHY_ENABLE,
.enable_reg = EXYNOS5420_MIPI_PHY0_CONTROL,
.enable_val = EXYNOS4_PHY_ENABLE,
.enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(0),
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
.resetn_val = EXYNOS5_MIPI_PHY_S_RESETN,
.resetn_reg = EXYNOS5420_MIPI_PHY0_CONTROL,
.resetn_val = EXYNOS4_MIPI_PHY_SRESETN,
.resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(0),
.resetn_map = EXYNOS_MIPI_REGMAP_PMU,
}, {
/* EXYNOS_MIPI_PHY_ID_DSIM0 */
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0,
.enable_val = EXYNOS5_PHY_ENABLE,
.enable_reg = EXYNOS5420_MIPI_PHY0_CONTROL,
.enable_val = EXYNOS4_PHY_ENABLE,
.enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(0),
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
.resetn_val = EXYNOS5_MIPI_PHY_M_RESETN,
.resetn_reg = EXYNOS5420_MIPI_PHY0_CONTROL,
.resetn_val = EXYNOS4_MIPI_PHY_MRESETN,
.resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(0),
.resetn_map = EXYNOS_MIPI_REGMAP_PMU,
}, {
/* EXYNOS_MIPI_PHY_ID_CSIS1 */
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM1,
.enable_val = EXYNOS5_PHY_ENABLE,
.enable_reg = EXYNOS5420_MIPI_PHY1_CONTROL,
.enable_val = EXYNOS4_PHY_ENABLE,
.enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(1),
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
.resetn_val = EXYNOS5_MIPI_PHY_S_RESETN,
.resetn_reg = EXYNOS5420_MIPI_PHY1_CONTROL,
.resetn_val = EXYNOS4_MIPI_PHY_SRESETN,
.resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(1),
.resetn_map = EXYNOS_MIPI_REGMAP_PMU,
}, {
/* EXYNOS_MIPI_PHY_ID_DSIM1 */
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS1,
.enable_val = EXYNOS5_PHY_ENABLE,
.enable_reg = EXYNOS5420_MIPI_PHY1_CONTROL,
.enable_val = EXYNOS4_PHY_ENABLE,
.enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(1),
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
.resetn_val = EXYNOS5_MIPI_PHY_M_RESETN,
.resetn_reg = EXYNOS5420_MIPI_PHY1_CONTROL,
.resetn_val = EXYNOS4_MIPI_PHY_MRESETN,
.resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(1),
.resetn_map = EXYNOS_MIPI_REGMAP_PMU,
}, {
/* EXYNOS_MIPI_PHY_ID_CSIS2 */
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE,
.enable_val = EXYNOS5_PHY_ENABLE,
.enable_reg = EXYNOS5420_MIPI_PHY2_CONTROL,
.enable_val = EXYNOS4_PHY_ENABLE,
.enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(2),
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
.resetn_val = EXYNOS5_MIPI_PHY_S_RESETN,
.resetn_reg = EXYNOS5420_MIPI_PHY2_CONTROL,
.resetn_val = EXYNOS4_MIPI_PHY_SRESETN,
.resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(2),
.resetn_map = EXYNOS_MIPI_REGMAP_PMU,
},
},
@ -172,8 +171,8 @@ static const struct mipi_phy_device_desc exynos5433_mipi_phy = {
{
/* EXYNOS_MIPI_PHY_ID_CSIS0 */
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0,
.enable_val = EXYNOS5_PHY_ENABLE,
.enable_reg = EXYNOS5433_MIPI_PHY0_CONTROL,
.enable_val = EXYNOS4_PHY_ENABLE,
.enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0),
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
.resetn_val = BIT(0),
.resetn_reg = EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON,
@ -181,8 +180,8 @@ static const struct mipi_phy_device_desc exynos5433_mipi_phy = {
}, {
/* EXYNOS_MIPI_PHY_ID_DSIM0 */
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0,
.enable_val = EXYNOS5_PHY_ENABLE,
.enable_reg = EXYNOS5433_MIPI_PHY0_CONTROL,
.enable_val = EXYNOS4_PHY_ENABLE,
.enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0),
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
.resetn_val = BIT(0),
.resetn_reg = EXYNOS5433_SYSREG_DISP_MIPI_PHY,
@ -190,8 +189,8 @@ static const struct mipi_phy_device_desc exynos5433_mipi_phy = {
}, {
/* EXYNOS_MIPI_PHY_ID_CSIS1 */
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE,
.enable_val = EXYNOS5_PHY_ENABLE,
.enable_reg = EXYNOS5433_MIPI_PHY1_CONTROL,
.enable_val = EXYNOS4_PHY_ENABLE,
.enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1),
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
.resetn_val = BIT(1),
.resetn_reg = EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON,
@ -199,8 +198,8 @@ static const struct mipi_phy_device_desc exynos5433_mipi_phy = {
}, {
/* EXYNOS_MIPI_PHY_ID_DSIM1 */
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE,
.enable_val = EXYNOS5_PHY_ENABLE,
.enable_reg = EXYNOS5433_MIPI_PHY1_CONTROL,
.enable_val = EXYNOS4_PHY_ENABLE,
.enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1),
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
.resetn_val = BIT(1),
.resetn_reg = EXYNOS5433_SYSREG_DISP_MIPI_PHY,
@ -208,8 +207,8 @@ static const struct mipi_phy_device_desc exynos5433_mipi_phy = {
}, {
/* EXYNOS_MIPI_PHY_ID_CSIS2 */
.coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE,
.enable_val = EXYNOS5_PHY_ENABLE,
.enable_reg = EXYNOS5433_MIPI_PHY2_CONTROL,
.enable_val = EXYNOS4_PHY_ENABLE,
.enable_reg = EXYNOS4_MIPI_PHY_CONTROL(2),
.enable_map = EXYNOS_MIPI_REGMAP_PMU,
.resetn_val = BIT(0),
.resetn_reg = EXYNOS5433_SYSREG_CAM1_MIPI_DPHY_CON,

View File

@ -14,8 +14,8 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/init.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
@ -228,7 +228,6 @@ static const struct of_device_id exynos_pcie_phy_match[] = {
},
{},
};
MODULE_DEVICE_TABLE(of, exynos_pcie_phy_match);
static int exynos_pcie_phy_probe(struct platform_device *pdev)
{
@ -278,8 +277,5 @@ static struct platform_driver exynos_pcie_phy_driver = {
.name = "exynos_pcie_phy",
}
};
module_platform_driver(exynos_pcie_phy_driver);
MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC PCIe PHY driver");
MODULE_AUTHOR("Jaehoon Chung <jh80.chung@samsung.com>");
MODULE_LICENSE("GPL v2");
builtin_platform_driver(exynos_pcie_phy_driver);

View File

@ -22,9 +22,9 @@
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/exynos5-pmu.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/soc/samsung/exynos-regs-pmu.h>
/* Exynos USB PHY registers */
#define EXYNOS5_FSEL_9MHZ6 0x0
@ -235,10 +235,10 @@ static void exynos5_usbdrd_phy_isol(struct phy_usb_instance *inst,
if (!inst->reg_pmu)
return;
val = on ? 0 : EXYNOS5_PHY_ENABLE;
val = on ? 0 : EXYNOS4_PHY_ENABLE;
regmap_update_bits(inst->reg_pmu, inst->pmu_offset,
EXYNOS5_PHY_ENABLE, val);
EXYNOS4_PHY_ENABLE, val);
}
/*

View File

@ -81,9 +81,9 @@
#define REG_ADP_BC_ACA_PIN_GND BIT(25)
#define REG_ADP_BC_ACA_PIN_FLOAT BIT(26)
#define REG_DBG_UART 0x14
#define REG_DBG_UART 0x10
#define REG_TEST 0x18
#define REG_TEST 0x14
#define REG_TEST_DATA_IN_MASK GENMASK(3, 0)
#define REG_TEST_EN_MASK GENMASK(7, 4)
#define REG_TEST_ADDR_MASK GENMASK(11, 8)
@ -93,7 +93,7 @@
#define REG_TEST_DATA_OUT_MASK GENMASK(19, 16)
#define REG_TEST_DISABLE_ID_PULLUP BIT(20)
#define REG_TUNE 0x1c
#define REG_TUNE 0x18
#define REG_TUNE_TX_RES_TUNE_MASK GENMASK(1, 0)
#define REG_TUNE_TX_HSXV_TUNE_MASK GENMASK(3, 2)
#define REG_TUNE_TX_VREF_TUNE_MASK GENMASK(7, 4)

View File

@ -23,47 +23,55 @@
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
/*
* for sifslv2 register, but exclude port's;
* relative to USB3_SIF2_BASE base address
*/
#define SSUSB_SIFSLV_SPLLC 0x0000
#define SSUSB_SIFSLV_U2FREQ 0x0100
/* version V1 sub-banks offset base address */
/* banks shared by multiple phys */
#define SSUSB_SIFSLV_V1_SPLLC 0x000 /* shared by u3 phys */
#define SSUSB_SIFSLV_V1_U2FREQ 0x100 /* shared by u2 phys */
/* u2 phy bank */
#define SSUSB_SIFSLV_V1_U2PHY_COM 0x000
/* u3 phy banks */
#define SSUSB_SIFSLV_V1_U3PHYD 0x000
#define SSUSB_SIFSLV_V1_U3PHYA 0x200
/* offsets of sub-segment in each port registers */
#define SSUSB_SIFSLV_U2PHY_COM_BASE 0x0000
#define SSUSB_SIFSLV_U3PHYD_BASE 0x0100
#define SSUSB_USB30_PHYA_SIV_B_BASE 0x0300
#define SSUSB_SIFSLV_U3PHYA_DA_BASE 0x0400
/* version V2 sub-banks offset base address */
/* u2 phy banks */
#define SSUSB_SIFSLV_V2_MISC 0x000
#define SSUSB_SIFSLV_V2_U2FREQ 0x100
#define SSUSB_SIFSLV_V2_U2PHY_COM 0x300
/* u3 phy banks */
#define SSUSB_SIFSLV_V2_SPLLC 0x000
#define SSUSB_SIFSLV_V2_CHIP 0x100
#define SSUSB_SIFSLV_V2_U3PHYD 0x200
#define SSUSB_SIFSLV_V2_U3PHYA 0x400
#define U3P_USBPHYACR0 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0000)
#define U3P_USBPHYACR0 0x000
#define PA0_RG_U2PLL_FORCE_ON BIT(15)
#define PA0_RG_USB20_INTR_EN BIT(5)
#define U3P_USBPHYACR2 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0008)
#define U3P_USBPHYACR2 0x008
#define PA2_RG_SIF_U2PLL_FORCE_EN BIT(18)
#define U3P_USBPHYACR5 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0014)
#define U3P_USBPHYACR5 0x014
#define PA5_RG_U2_HSTX_SRCAL_EN BIT(15)
#define PA5_RG_U2_HSTX_SRCTRL GENMASK(14, 12)
#define PA5_RG_U2_HSTX_SRCTRL_VAL(x) ((0x7 & (x)) << 12)
#define PA5_RG_U2_HS_100U_U3_EN BIT(11)
#define U3P_USBPHYACR6 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0018)
#define PA6_RG_U2_ISO_EN BIT(31)
#define U3P_USBPHYACR6 0x018
#define PA6_RG_U2_BC11_SW_EN BIT(23)
#define PA6_RG_U2_OTG_VBUSCMP_EN BIT(20)
#define PA6_RG_U2_SQTH GENMASK(3, 0)
#define PA6_RG_U2_SQTH_VAL(x) (0xf & (x))
#define U3P_U2PHYACR4 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0020)
#define U3P_U2PHYACR4 0x020
#define P2C_RG_USB20_GPIO_CTL BIT(9)
#define P2C_USB20_GPIO_MODE BIT(8)
#define P2C_U2_GPIO_CTR_MSK (P2C_RG_USB20_GPIO_CTL | P2C_USB20_GPIO_MODE)
#define U3D_U2PHYDCR0 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0060)
#define U3D_U2PHYDCR0 0x060
#define P2C_RG_SIF_U2PLL_FORCE_ON BIT(24)
#define U3P_U2PHYDTM0 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0068)
#define U3P_U2PHYDTM0 0x068
#define P2C_FORCE_UART_EN BIT(26)
#define P2C_FORCE_DATAIN BIT(23)
#define P2C_FORCE_DM_PULLDOWN BIT(21)
@ -85,47 +93,56 @@
P2C_FORCE_TERMSEL | P2C_RG_DMPULLDOWN | \
P2C_RG_DPPULLDOWN | P2C_RG_TERMSEL)
#define U3P_U2PHYDTM1 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x006C)
#define U3P_U2PHYDTM1 0x06C
#define P2C_RG_UART_EN BIT(16)
#define P2C_RG_VBUSVALID BIT(5)
#define P2C_RG_SESSEND BIT(4)
#define P2C_RG_AVALID BIT(2)
#define U3P_U3_PHYA_REG0 (SSUSB_USB30_PHYA_SIV_B_BASE + 0x0000)
#define P3A_RG_U3_VUSB10_ON BIT(5)
#define U3P_U3_PHYA_REG6 (SSUSB_USB30_PHYA_SIV_B_BASE + 0x0018)
#define U3P_U3_PHYA_REG6 0x018
#define P3A_RG_TX_EIDLE_CM GENMASK(31, 28)
#define P3A_RG_TX_EIDLE_CM_VAL(x) ((0xf & (x)) << 28)
#define U3P_U3_PHYA_REG9 (SSUSB_USB30_PHYA_SIV_B_BASE + 0x0024)
#define U3P_U3_PHYA_REG9 0x024
#define P3A_RG_RX_DAC_MUX GENMASK(5, 1)
#define P3A_RG_RX_DAC_MUX_VAL(x) ((0x1f & (x)) << 1)
#define U3P_U3PHYA_DA_REG0 (SSUSB_SIFSLV_U3PHYA_DA_BASE + 0x0000)
#define U3P_U3_PHYA_DA_REG0 0x100
#define P3A_RG_XTAL_EXT_EN_U3 GENMASK(11, 10)
#define P3A_RG_XTAL_EXT_EN_U3_VAL(x) ((0x3 & (x)) << 10)
#define U3P_PHYD_CDR1 (SSUSB_SIFSLV_U3PHYD_BASE + 0x005c)
#define U3P_U3_PHYD_LFPS1 0x00c
#define P3D_RG_FWAKE_TH GENMASK(21, 16)
#define P3D_RG_FWAKE_TH_VAL(x) ((0x3f & (x)) << 16)
#define U3P_U3_PHYD_CDR1 0x05c
#define P3D_RG_CDR_BIR_LTD1 GENMASK(28, 24)
#define P3D_RG_CDR_BIR_LTD1_VAL(x) ((0x1f & (x)) << 24)
#define P3D_RG_CDR_BIR_LTD0 GENMASK(12, 8)
#define P3D_RG_CDR_BIR_LTD0_VAL(x) ((0x1f & (x)) << 8)
#define U3P_XTALCTL3 (SSUSB_SIFSLV_SPLLC + 0x0018)
#define U3P_U3_PHYD_RXDET1 0x128
#define P3D_RG_RXDET_STB2_SET GENMASK(17, 9)
#define P3D_RG_RXDET_STB2_SET_VAL(x) ((0x1ff & (x)) << 9)
#define U3P_U3_PHYD_RXDET2 0x12c
#define P3D_RG_RXDET_STB2_SET_P3 GENMASK(8, 0)
#define P3D_RG_RXDET_STB2_SET_P3_VAL(x) (0x1ff & (x))
#define U3P_SPLLC_XTALCTL3 0x018
#define XC3_RG_U3_XTAL_RX_PWD BIT(9)
#define XC3_RG_U3_FRC_XTAL_RX_PWD BIT(8)
#define U3P_U2FREQ_FMCR0 (SSUSB_SIFSLV_U2FREQ + 0x00)
#define U3P_U2FREQ_FMCR0 0x00
#define P2F_RG_MONCLK_SEL GENMASK(27, 26)
#define P2F_RG_MONCLK_SEL_VAL(x) ((0x3 & (x)) << 26)
#define P2F_RG_FREQDET_EN BIT(24)
#define P2F_RG_CYCLECNT GENMASK(23, 0)
#define P2F_RG_CYCLECNT_VAL(x) ((P2F_RG_CYCLECNT) & (x))
#define U3P_U2FREQ_VALUE (SSUSB_SIFSLV_U2FREQ + 0x0c)
#define U3P_U2FREQ_VALUE 0x0c
#define U3P_U2FREQ_FMMONR1 (SSUSB_SIFSLV_U2FREQ + 0x10)
#define U3P_U2FREQ_FMMONR1 0x10
#define P2F_USB_FM_VALID BIT(0)
#define P2F_RG_FRCK_EN BIT(8)
@ -134,21 +151,46 @@
#define U3P_SR_COEF_DIVISOR 1000
#define U3P_FM_DET_CYCLE_CNT 1024
enum mt_phy_version {
MT_PHY_V1 = 1,
MT_PHY_V2,
};
struct mt65xx_phy_pdata {
/* avoid RX sensitivity level degradation only for mt8173 */
bool avoid_rx_sen_degradation;
enum mt_phy_version version;
};
struct u2phy_banks {
void __iomem *misc;
void __iomem *fmreg;
void __iomem *com;
};
struct u3phy_banks {
void __iomem *spllc;
void __iomem *chip;
void __iomem *phyd; /* include u3phyd_bank2 */
void __iomem *phya; /* include u3phya_da */
};
struct mt65xx_phy_instance {
struct phy *phy;
void __iomem *port_base;
union {
struct u2phy_banks u2_banks;
struct u3phy_banks u3_banks;
};
struct clk *ref_clk; /* reference clock of anolog phy */
u32 index;
u8 type;
};
struct mt65xx_u3phy {
struct device *dev;
void __iomem *sif_base; /* include sif2, but exclude port's */
void __iomem *sif_base; /* only shared sif */
/* deprecated, use @ref_clk instead in phy instance */
struct clk *u3phya_ref; /* reference clock of usb3 anolog phy */
const struct mt65xx_phy_pdata *pdata;
struct mt65xx_phy_instance **phys;
@ -158,49 +200,53 @@ struct mt65xx_u3phy {
static void hs_slew_rate_calibrate(struct mt65xx_u3phy *u3phy,
struct mt65xx_phy_instance *instance)
{
void __iomem *sif_base = u3phy->sif_base;
struct u2phy_banks *u2_banks = &instance->u2_banks;
void __iomem *fmreg = u2_banks->fmreg;
void __iomem *com = u2_banks->com;
int calibration_val;
int fm_out;
u32 tmp;
/* enable USB ring oscillator */
tmp = readl(instance->port_base + U3P_USBPHYACR5);
tmp = readl(com + U3P_USBPHYACR5);
tmp |= PA5_RG_U2_HSTX_SRCAL_EN;
writel(tmp, instance->port_base + U3P_USBPHYACR5);
writel(tmp, com + U3P_USBPHYACR5);
udelay(1);
/*enable free run clock */
tmp = readl(sif_base + U3P_U2FREQ_FMMONR1);
tmp = readl(fmreg + U3P_U2FREQ_FMMONR1);
tmp |= P2F_RG_FRCK_EN;
writel(tmp, sif_base + U3P_U2FREQ_FMMONR1);
writel(tmp, fmreg + U3P_U2FREQ_FMMONR1);
/* set cycle count as 1024, and select u2 channel */
tmp = readl(sif_base + U3P_U2FREQ_FMCR0);
tmp = readl(fmreg + U3P_U2FREQ_FMCR0);
tmp &= ~(P2F_RG_CYCLECNT | P2F_RG_MONCLK_SEL);
tmp |= P2F_RG_CYCLECNT_VAL(U3P_FM_DET_CYCLE_CNT);
tmp |= P2F_RG_MONCLK_SEL_VAL(instance->index);
writel(tmp, sif_base + U3P_U2FREQ_FMCR0);
if (u3phy->pdata->version == MT_PHY_V1)
tmp |= P2F_RG_MONCLK_SEL_VAL(instance->index >> 1);
writel(tmp, fmreg + U3P_U2FREQ_FMCR0);
/* enable frequency meter */
tmp = readl(sif_base + U3P_U2FREQ_FMCR0);
tmp = readl(fmreg + U3P_U2FREQ_FMCR0);
tmp |= P2F_RG_FREQDET_EN;
writel(tmp, sif_base + U3P_U2FREQ_FMCR0);
writel(tmp, fmreg + U3P_U2FREQ_FMCR0);
/* ignore return value */
readl_poll_timeout(sif_base + U3P_U2FREQ_FMMONR1, tmp,
(tmp & P2F_USB_FM_VALID), 10, 200);
readl_poll_timeout(fmreg + U3P_U2FREQ_FMMONR1, tmp,
(tmp & P2F_USB_FM_VALID), 10, 200);
fm_out = readl(sif_base + U3P_U2FREQ_VALUE);
fm_out = readl(fmreg + U3P_U2FREQ_VALUE);
/* disable frequency meter */
tmp = readl(sif_base + U3P_U2FREQ_FMCR0);
tmp = readl(fmreg + U3P_U2FREQ_FMCR0);
tmp &= ~P2F_RG_FREQDET_EN;
writel(tmp, sif_base + U3P_U2FREQ_FMCR0);
writel(tmp, fmreg + U3P_U2FREQ_FMCR0);
/*disable free run clock */
tmp = readl(sif_base + U3P_U2FREQ_FMMONR1);
tmp = readl(fmreg + U3P_U2FREQ_FMMONR1);
tmp &= ~P2F_RG_FRCK_EN;
writel(tmp, sif_base + U3P_U2FREQ_FMMONR1);
writel(tmp, fmreg + U3P_U2FREQ_FMMONR1);
if (fm_out) {
/* ( 1024 / FM_OUT ) x reference clock frequency x 0.028 */
@ -215,85 +261,125 @@ static void hs_slew_rate_calibrate(struct mt65xx_u3phy *u3phy,
instance->index, fm_out, calibration_val);
/* set HS slew rate */
tmp = readl(instance->port_base + U3P_USBPHYACR5);
tmp = readl(com + U3P_USBPHYACR5);
tmp &= ~PA5_RG_U2_HSTX_SRCTRL;
tmp |= PA5_RG_U2_HSTX_SRCTRL_VAL(calibration_val);
writel(tmp, instance->port_base + U3P_USBPHYACR5);
writel(tmp, com + U3P_USBPHYACR5);
/* disable USB ring oscillator */
tmp = readl(instance->port_base + U3P_USBPHYACR5);
tmp = readl(com + U3P_USBPHYACR5);
tmp &= ~PA5_RG_U2_HSTX_SRCAL_EN;
writel(tmp, instance->port_base + U3P_USBPHYACR5);
writel(tmp, com + U3P_USBPHYACR5);
}
static void u3_phy_instance_init(struct mt65xx_u3phy *u3phy,
struct mt65xx_phy_instance *instance)
{
struct u3phy_banks *u3_banks = &instance->u3_banks;
u32 tmp;
/* gating PCIe Analog XTAL clock */
tmp = readl(u3_banks->spllc + U3P_SPLLC_XTALCTL3);
tmp |= XC3_RG_U3_XTAL_RX_PWD | XC3_RG_U3_FRC_XTAL_RX_PWD;
writel(tmp, u3_banks->spllc + U3P_SPLLC_XTALCTL3);
/* gating XSQ */
tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG0);
tmp &= ~P3A_RG_XTAL_EXT_EN_U3;
tmp |= P3A_RG_XTAL_EXT_EN_U3_VAL(2);
writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG0);
tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG9);
tmp &= ~P3A_RG_RX_DAC_MUX;
tmp |= P3A_RG_RX_DAC_MUX_VAL(4);
writel(tmp, u3_banks->phya + U3P_U3_PHYA_REG9);
tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG6);
tmp &= ~P3A_RG_TX_EIDLE_CM;
tmp |= P3A_RG_TX_EIDLE_CM_VAL(0xe);
writel(tmp, u3_banks->phya + U3P_U3_PHYA_REG6);
tmp = readl(u3_banks->phyd + U3P_U3_PHYD_CDR1);
tmp &= ~(P3D_RG_CDR_BIR_LTD0 | P3D_RG_CDR_BIR_LTD1);
tmp |= P3D_RG_CDR_BIR_LTD0_VAL(0xc) | P3D_RG_CDR_BIR_LTD1_VAL(0x3);
writel(tmp, u3_banks->phyd + U3P_U3_PHYD_CDR1);
tmp = readl(u3_banks->phyd + U3P_U3_PHYD_LFPS1);
tmp &= ~P3D_RG_FWAKE_TH;
tmp |= P3D_RG_FWAKE_TH_VAL(0x34);
writel(tmp, u3_banks->phyd + U3P_U3_PHYD_LFPS1);
tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RXDET1);
tmp &= ~P3D_RG_RXDET_STB2_SET;
tmp |= P3D_RG_RXDET_STB2_SET_VAL(0x10);
writel(tmp, u3_banks->phyd + U3P_U3_PHYD_RXDET1);
tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RXDET2);
tmp &= ~P3D_RG_RXDET_STB2_SET_P3;
tmp |= P3D_RG_RXDET_STB2_SET_P3_VAL(0x10);
writel(tmp, u3_banks->phyd + U3P_U3_PHYD_RXDET2);
dev_dbg(u3phy->dev, "%s(%d)\n", __func__, instance->index);
}
static void phy_instance_init(struct mt65xx_u3phy *u3phy,
struct mt65xx_phy_instance *instance)
{
void __iomem *port_base = instance->port_base;
struct u2phy_banks *u2_banks = &instance->u2_banks;
void __iomem *com = u2_banks->com;
u32 index = instance->index;
u32 tmp;
/* switch to USB function. (system register, force ip into usb mode) */
tmp = readl(port_base + U3P_U2PHYDTM0);
tmp = readl(com + U3P_U2PHYDTM0);
tmp &= ~P2C_FORCE_UART_EN;
tmp |= P2C_RG_XCVRSEL_VAL(1) | P2C_RG_DATAIN_VAL(0);
writel(tmp, port_base + U3P_U2PHYDTM0);
writel(tmp, com + U3P_U2PHYDTM0);
tmp = readl(port_base + U3P_U2PHYDTM1);
tmp = readl(com + U3P_U2PHYDTM1);
tmp &= ~P2C_RG_UART_EN;
writel(tmp, port_base + U3P_U2PHYDTM1);
writel(tmp, com + U3P_U2PHYDTM1);
tmp = readl(com + U3P_USBPHYACR0);
tmp |= PA0_RG_USB20_INTR_EN;
writel(tmp, com + U3P_USBPHYACR0);
/* disable switch 100uA current to SSUSB */
tmp = readl(com + U3P_USBPHYACR5);
tmp &= ~PA5_RG_U2_HS_100U_U3_EN;
writel(tmp, com + U3P_USBPHYACR5);
if (!index) {
tmp = readl(port_base + U3P_U2PHYACR4);
tmp = readl(com + U3P_U2PHYACR4);
tmp &= ~P2C_U2_GPIO_CTR_MSK;
writel(tmp, port_base + U3P_U2PHYACR4);
writel(tmp, com + U3P_U2PHYACR4);
}
if (u3phy->pdata->avoid_rx_sen_degradation) {
if (!index) {
tmp = readl(port_base + U3P_USBPHYACR2);
tmp = readl(com + U3P_USBPHYACR2);
tmp |= PA2_RG_SIF_U2PLL_FORCE_EN;
writel(tmp, port_base + U3P_USBPHYACR2);
writel(tmp, com + U3P_USBPHYACR2);
tmp = readl(port_base + U3D_U2PHYDCR0);
tmp = readl(com + U3D_U2PHYDCR0);
tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON;
writel(tmp, port_base + U3D_U2PHYDCR0);
writel(tmp, com + U3D_U2PHYDCR0);
} else {
tmp = readl(port_base + U3D_U2PHYDCR0);
tmp = readl(com + U3D_U2PHYDCR0);
tmp |= P2C_RG_SIF_U2PLL_FORCE_ON;
writel(tmp, port_base + U3D_U2PHYDCR0);
writel(tmp, com + U3D_U2PHYDCR0);
tmp = readl(port_base + U3P_U2PHYDTM0);
tmp = readl(com + U3P_U2PHYDTM0);
tmp |= P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM;
writel(tmp, port_base + U3P_U2PHYDTM0);
writel(tmp, com + U3P_U2PHYDTM0);
}
}
tmp = readl(port_base + U3P_USBPHYACR6);
tmp = readl(com + U3P_USBPHYACR6);
tmp &= ~PA6_RG_U2_BC11_SW_EN; /* DP/DM BC1.1 path Disable */
tmp &= ~PA6_RG_U2_SQTH;
tmp |= PA6_RG_U2_SQTH_VAL(2);
writel(tmp, port_base + U3P_USBPHYACR6);
tmp = readl(port_base + U3P_U3PHYA_DA_REG0);
tmp &= ~P3A_RG_XTAL_EXT_EN_U3;
tmp |= P3A_RG_XTAL_EXT_EN_U3_VAL(2);
writel(tmp, port_base + U3P_U3PHYA_DA_REG0);
tmp = readl(port_base + U3P_U3_PHYA_REG9);
tmp &= ~P3A_RG_RX_DAC_MUX;
tmp |= P3A_RG_RX_DAC_MUX_VAL(4);
writel(tmp, port_base + U3P_U3_PHYA_REG9);
tmp = readl(port_base + U3P_U3_PHYA_REG6);
tmp &= ~P3A_RG_TX_EIDLE_CM;
tmp |= P3A_RG_TX_EIDLE_CM_VAL(0xe);
writel(tmp, port_base + U3P_U3_PHYA_REG6);
tmp = readl(port_base + U3P_PHYD_CDR1);
tmp &= ~(P3D_RG_CDR_BIR_LTD0 | P3D_RG_CDR_BIR_LTD1);
tmp |= P3D_RG_CDR_BIR_LTD0_VAL(0xc) | P3D_RG_CDR_BIR_LTD1_VAL(0x3);
writel(tmp, port_base + U3P_PHYD_CDR1);
writel(tmp, com + U3P_USBPHYACR6);
dev_dbg(u3phy->dev, "%s(%d)\n", __func__, index);
}
@ -301,58 +387,35 @@ static void phy_instance_init(struct mt65xx_u3phy *u3phy,
static void phy_instance_power_on(struct mt65xx_u3phy *u3phy,
struct mt65xx_phy_instance *instance)
{
void __iomem *port_base = instance->port_base;
struct u2phy_banks *u2_banks = &instance->u2_banks;
void __iomem *com = u2_banks->com;
u32 index = instance->index;
u32 tmp;
if (!index) {
/* Set RG_SSUSB_VUSB10_ON as 1 after VUSB10 ready */
tmp = readl(port_base + U3P_U3_PHYA_REG0);
tmp |= P3A_RG_U3_VUSB10_ON;
writel(tmp, port_base + U3P_U3_PHYA_REG0);
}
/* (force_suspendm=0) (let suspendm=1, enable usb 480MHz pll) */
tmp = readl(port_base + U3P_U2PHYDTM0);
tmp = readl(com + U3P_U2PHYDTM0);
tmp &= ~(P2C_FORCE_SUSPENDM | P2C_RG_XCVRSEL);
tmp &= ~(P2C_RG_DATAIN | P2C_DTM0_PART_MASK);
writel(tmp, port_base + U3P_U2PHYDTM0);
writel(tmp, com + U3P_U2PHYDTM0);
/* OTG Enable */
tmp = readl(port_base + U3P_USBPHYACR6);
tmp = readl(com + U3P_USBPHYACR6);
tmp |= PA6_RG_U2_OTG_VBUSCMP_EN;
writel(tmp, port_base + U3P_USBPHYACR6);
writel(tmp, com + U3P_USBPHYACR6);
if (!index) {
tmp = readl(u3phy->sif_base + U3P_XTALCTL3);
tmp |= XC3_RG_U3_XTAL_RX_PWD | XC3_RG_U3_FRC_XTAL_RX_PWD;
writel(tmp, u3phy->sif_base + U3P_XTALCTL3);
/* switch 100uA current to SSUSB */
tmp = readl(port_base + U3P_USBPHYACR5);
tmp |= PA5_RG_U2_HS_100U_U3_EN;
writel(tmp, port_base + U3P_USBPHYACR5);
}
tmp = readl(port_base + U3P_U2PHYDTM1);
tmp = readl(com + U3P_U2PHYDTM1);
tmp |= P2C_RG_VBUSVALID | P2C_RG_AVALID;
tmp &= ~P2C_RG_SESSEND;
writel(tmp, port_base + U3P_U2PHYDTM1);
/* USB 2.0 slew rate calibration */
tmp = readl(port_base + U3P_USBPHYACR5);
tmp &= ~PA5_RG_U2_HSTX_SRCTRL;
tmp |= PA5_RG_U2_HSTX_SRCTRL_VAL(4);
writel(tmp, port_base + U3P_USBPHYACR5);
writel(tmp, com + U3P_U2PHYDTM1);
if (u3phy->pdata->avoid_rx_sen_degradation && index) {
tmp = readl(port_base + U3D_U2PHYDCR0);
tmp = readl(com + U3D_U2PHYDCR0);
tmp |= P2C_RG_SIF_U2PLL_FORCE_ON;
writel(tmp, port_base + U3D_U2PHYDCR0);
writel(tmp, com + U3D_U2PHYDCR0);
tmp = readl(port_base + U3P_U2PHYDTM0);
tmp = readl(com + U3P_U2PHYDTM0);
tmp |= P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM;
writel(tmp, port_base + U3P_U2PHYDTM0);
writel(tmp, com + U3P_U2PHYDTM0);
}
dev_dbg(u3phy->dev, "%s(%d)\n", __func__, index);
}
@ -360,48 +423,36 @@ static void phy_instance_power_on(struct mt65xx_u3phy *u3phy,
static void phy_instance_power_off(struct mt65xx_u3phy *u3phy,
struct mt65xx_phy_instance *instance)
{
void __iomem *port_base = instance->port_base;
struct u2phy_banks *u2_banks = &instance->u2_banks;
void __iomem *com = u2_banks->com;
u32 index = instance->index;
u32 tmp;
tmp = readl(port_base + U3P_U2PHYDTM0);
tmp = readl(com + U3P_U2PHYDTM0);
tmp &= ~(P2C_RG_XCVRSEL | P2C_RG_DATAIN);
tmp |= P2C_FORCE_SUSPENDM;
writel(tmp, port_base + U3P_U2PHYDTM0);
writel(tmp, com + U3P_U2PHYDTM0);
/* OTG Disable */
tmp = readl(port_base + U3P_USBPHYACR6);
tmp = readl(com + U3P_USBPHYACR6);
tmp &= ~PA6_RG_U2_OTG_VBUSCMP_EN;
writel(tmp, port_base + U3P_USBPHYACR6);
if (!index) {
/* switch 100uA current back to USB2.0 */
tmp = readl(port_base + U3P_USBPHYACR5);
tmp &= ~PA5_RG_U2_HS_100U_U3_EN;
writel(tmp, port_base + U3P_USBPHYACR5);
}
writel(tmp, com + U3P_USBPHYACR6);
/* let suspendm=0, set utmi into analog power down */
tmp = readl(port_base + U3P_U2PHYDTM0);
tmp = readl(com + U3P_U2PHYDTM0);
tmp &= ~P2C_RG_SUSPENDM;
writel(tmp, port_base + U3P_U2PHYDTM0);
writel(tmp, com + U3P_U2PHYDTM0);
udelay(1);
tmp = readl(port_base + U3P_U2PHYDTM1);
tmp = readl(com + U3P_U2PHYDTM1);
tmp &= ~(P2C_RG_VBUSVALID | P2C_RG_AVALID);
tmp |= P2C_RG_SESSEND;
writel(tmp, port_base + U3P_U2PHYDTM1);
if (!index) {
tmp = readl(port_base + U3P_U3_PHYA_REG0);
tmp &= ~P3A_RG_U3_VUSB10_ON;
writel(tmp, port_base + U3P_U3_PHYA_REG0);
}
writel(tmp, com + U3P_U2PHYDTM1);
if (u3phy->pdata->avoid_rx_sen_degradation && index) {
tmp = readl(port_base + U3D_U2PHYDCR0);
tmp = readl(com + U3D_U2PHYDCR0);
tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON;
writel(tmp, port_base + U3D_U2PHYDCR0);
writel(tmp, com + U3D_U2PHYDCR0);
}
dev_dbg(u3phy->dev, "%s(%d)\n", __func__, index);
@ -410,18 +461,55 @@ static void phy_instance_power_off(struct mt65xx_u3phy *u3phy,
static void phy_instance_exit(struct mt65xx_u3phy *u3phy,
struct mt65xx_phy_instance *instance)
{
void __iomem *port_base = instance->port_base;
struct u2phy_banks *u2_banks = &instance->u2_banks;
void __iomem *com = u2_banks->com;
u32 index = instance->index;
u32 tmp;
if (u3phy->pdata->avoid_rx_sen_degradation && index) {
tmp = readl(port_base + U3D_U2PHYDCR0);
tmp = readl(com + U3D_U2PHYDCR0);
tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON;
writel(tmp, port_base + U3D_U2PHYDCR0);
writel(tmp, com + U3D_U2PHYDCR0);
tmp = readl(port_base + U3P_U2PHYDTM0);
tmp = readl(com + U3P_U2PHYDTM0);
tmp &= ~P2C_FORCE_SUSPENDM;
writel(tmp, port_base + U3P_U2PHYDTM0);
writel(tmp, com + U3P_U2PHYDTM0);
}
}
static void phy_v1_banks_init(struct mt65xx_u3phy *u3phy,
struct mt65xx_phy_instance *instance)
{
struct u2phy_banks *u2_banks = &instance->u2_banks;
struct u3phy_banks *u3_banks = &instance->u3_banks;
if (instance->type == PHY_TYPE_USB2) {
u2_banks->misc = NULL;
u2_banks->fmreg = u3phy->sif_base + SSUSB_SIFSLV_V1_U2FREQ;
u2_banks->com = instance->port_base + SSUSB_SIFSLV_V1_U2PHY_COM;
} else if (instance->type == PHY_TYPE_USB3) {
u3_banks->spllc = u3phy->sif_base + SSUSB_SIFSLV_V1_SPLLC;
u3_banks->chip = NULL;
u3_banks->phyd = instance->port_base + SSUSB_SIFSLV_V1_U3PHYD;
u3_banks->phya = instance->port_base + SSUSB_SIFSLV_V1_U3PHYA;
}
}
static void phy_v2_banks_init(struct mt65xx_u3phy *u3phy,
struct mt65xx_phy_instance *instance)
{
struct u2phy_banks *u2_banks = &instance->u2_banks;
struct u3phy_banks *u3_banks = &instance->u3_banks;
if (instance->type == PHY_TYPE_USB2) {
u2_banks->misc = instance->port_base + SSUSB_SIFSLV_V2_MISC;
u2_banks->fmreg = instance->port_base + SSUSB_SIFSLV_V2_U2FREQ;
u2_banks->com = instance->port_base + SSUSB_SIFSLV_V2_U2PHY_COM;
} else if (instance->type == PHY_TYPE_USB3) {
u3_banks->spllc = instance->port_base + SSUSB_SIFSLV_V2_SPLLC;
u3_banks->chip = instance->port_base + SSUSB_SIFSLV_V2_CHIP;
u3_banks->phyd = instance->port_base + SSUSB_SIFSLV_V2_U3PHYD;
u3_banks->phya = instance->port_base + SSUSB_SIFSLV_V2_U3PHYA;
}
}
@ -437,7 +525,17 @@ static int mt65xx_phy_init(struct phy *phy)
return ret;
}
phy_instance_init(u3phy, instance);
ret = clk_prepare_enable(instance->ref_clk);
if (ret) {
dev_err(u3phy->dev, "failed to enable ref_clk\n");
return ret;
}
if (instance->type == PHY_TYPE_USB2)
phy_instance_init(u3phy, instance);
else
u3_phy_instance_init(u3phy, instance);
return 0;
}
@ -446,8 +544,10 @@ static int mt65xx_phy_power_on(struct phy *phy)
struct mt65xx_phy_instance *instance = phy_get_drvdata(phy);
struct mt65xx_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
phy_instance_power_on(u3phy, instance);
hs_slew_rate_calibrate(u3phy, instance);
if (instance->type == PHY_TYPE_USB2) {
phy_instance_power_on(u3phy, instance);
hs_slew_rate_calibrate(u3phy, instance);
}
return 0;
}
@ -456,7 +556,9 @@ static int mt65xx_phy_power_off(struct phy *phy)
struct mt65xx_phy_instance *instance = phy_get_drvdata(phy);
struct mt65xx_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
phy_instance_power_off(u3phy, instance);
if (instance->type == PHY_TYPE_USB2)
phy_instance_power_off(u3phy, instance);
return 0;
}
@ -465,7 +567,10 @@ static int mt65xx_phy_exit(struct phy *phy)
struct mt65xx_phy_instance *instance = phy_get_drvdata(phy);
struct mt65xx_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
phy_instance_exit(u3phy, instance);
if (instance->type == PHY_TYPE_USB2)
phy_instance_exit(u3phy, instance);
clk_disable_unprepare(instance->ref_clk);
clk_disable_unprepare(u3phy->u3phya_ref);
return 0;
}
@ -478,7 +583,6 @@ static struct phy *mt65xx_phy_xlate(struct device *dev,
struct device_node *phy_np = args->np;
int index;
if (args->args_count != 1) {
dev_err(dev, "invalid number of cells in 'phy' property\n");
return ERR_PTR(-EINVAL);
@ -496,13 +600,21 @@ static struct phy *mt65xx_phy_xlate(struct device *dev,
}
instance->type = args->args[0];
if (!(instance->type == PHY_TYPE_USB2 ||
instance->type == PHY_TYPE_USB3)) {
dev_err(dev, "unsupported device type: %d\n", instance->type);
return ERR_PTR(-EINVAL);
}
if (u3phy->pdata->version == MT_PHY_V1) {
phy_v1_banks_init(u3phy, instance);
} else if (u3phy->pdata->version == MT_PHY_V2) {
phy_v2_banks_init(u3phy, instance);
} else {
dev_err(dev, "phy version is not supported\n");
return ERR_PTR(-EINVAL);
}
return instance->phy;
}
@ -516,14 +628,22 @@ static const struct phy_ops mt65xx_u3phy_ops = {
static const struct mt65xx_phy_pdata mt2701_pdata = {
.avoid_rx_sen_degradation = false,
.version = MT_PHY_V1,
};
static const struct mt65xx_phy_pdata mt2712_pdata = {
.avoid_rx_sen_degradation = false,
.version = MT_PHY_V2,
};
static const struct mt65xx_phy_pdata mt8173_pdata = {
.avoid_rx_sen_degradation = true,
.version = MT_PHY_V1,
};
static const struct of_device_id mt65xx_u3phy_id_table[] = {
{ .compatible = "mediatek,mt2701-u3phy", .data = &mt2701_pdata },
{ .compatible = "mediatek,mt2712-u3phy", .data = &mt2712_pdata },
{ .compatible = "mediatek,mt8173-u3phy", .data = &mt8173_pdata },
{ },
};
@ -559,17 +679,23 @@ static int mt65xx_u3phy_probe(struct platform_device *pdev)
u3phy->dev = dev;
platform_set_drvdata(pdev, u3phy);
sif_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
u3phy->sif_base = devm_ioremap_resource(dev, sif_res);
if (IS_ERR(u3phy->sif_base)) {
dev_err(dev, "failed to remap sif regs\n");
return PTR_ERR(u3phy->sif_base);
if (u3phy->pdata->version == MT_PHY_V1) {
/* get banks shared by multiple phys */
sif_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
u3phy->sif_base = devm_ioremap_resource(dev, sif_res);
if (IS_ERR(u3phy->sif_base)) {
dev_err(dev, "failed to remap sif regs\n");
return PTR_ERR(u3phy->sif_base);
}
}
/* it's deprecated, make it optional for backward compatibility */
u3phy->u3phya_ref = devm_clk_get(dev, "u3phya_ref");
if (IS_ERR(u3phy->u3phya_ref)) {
dev_err(dev, "error to get u3phya_ref\n");
return PTR_ERR(u3phy->u3phya_ref);
if (PTR_ERR(u3phy->u3phya_ref) == -EPROBE_DEFER)
return -EPROBE_DEFER;
u3phy->u3phya_ref = NULL;
}
port = 0;
@ -610,6 +736,17 @@ static int mt65xx_u3phy_probe(struct platform_device *pdev)
instance->index = port;
phy_set_drvdata(phy, instance);
port++;
/* if deprecated clock is provided, ignore instance's one */
if (u3phy->u3phya_ref)
continue;
instance->ref_clk = devm_clk_get(&phy->dev, "ref");
if (IS_ERR(instance->ref_clk)) {
dev_err(dev, "failed to get ref_clk(id-%d)\n", port);
retval = PTR_ERR(instance->ref_clk);
goto put_child;
}
}
provider = devm_of_phy_provider_register(dev, mt65xx_phy_xlate);

1153
drivers/phy/phy-qcom-qmp.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,493 @@
/*
* Copyright (c) 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/slab.h>
#define QUSB2PHY_PLL_TEST 0x04
#define CLK_REF_SEL BIT(7)
#define QUSB2PHY_PLL_TUNE 0x08
#define QUSB2PHY_PLL_USER_CTL1 0x0c
#define QUSB2PHY_PLL_USER_CTL2 0x10
#define QUSB2PHY_PLL_AUTOPGM_CTL1 0x1c
#define QUSB2PHY_PLL_PWR_CTRL 0x18
#define QUSB2PHY_PLL_STATUS 0x38
#define PLL_LOCKED BIT(5)
#define QUSB2PHY_PORT_TUNE1 0x80
#define QUSB2PHY_PORT_TUNE2 0x84
#define QUSB2PHY_PORT_TUNE3 0x88
#define QUSB2PHY_PORT_TUNE4 0x8c
#define QUSB2PHY_PORT_TUNE5 0x90
#define QUSB2PHY_PORT_TEST2 0x9c
#define QUSB2PHY_PORT_POWERDOWN 0xb4
#define CLAMP_N_EN BIT(5)
#define FREEZIO_N BIT(1)
#define POWER_DOWN BIT(0)
#define QUSB2PHY_REFCLK_ENABLE BIT(0)
#define PHY_CLK_SCHEME_SEL BIT(0)
struct qusb2_phy_init_tbl {
unsigned int offset;
unsigned int val;
};
#define QUSB2_PHY_INIT_CFG(o, v) \
{ \
.offset = o, \
.val = v, \
}
static const struct qusb2_phy_init_tbl msm8996_init_tbl[] = {
QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE1, 0xf8),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE2, 0xb3),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE3, 0x83),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE4, 0xc0),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TEST2, 0x14),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9f),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00),
};
struct qusb2_phy_cfg {
const struct qusb2_phy_init_tbl *tbl;
/* number of entries in the table */
unsigned int tbl_num;
/* offset to PHY_CLK_SCHEME register in TCSR map */
unsigned int clk_scheme_offset;
};
static const struct qusb2_phy_cfg msm8996_phy_cfg = {
.tbl = msm8996_init_tbl,
.tbl_num = ARRAY_SIZE(msm8996_init_tbl),
};
static const char * const qusb2_phy_vreg_names[] = {
"vdda-pll", "vdda-phy-dpdm",
};
#define QUSB2_NUM_VREGS ARRAY_SIZE(qusb2_phy_vreg_names)
/**
* struct qusb2_phy - structure holding qusb2 phy attributes
*
* @phy: generic phy
* @base: iomapped memory space for qubs2 phy
*
* @cfg_ahb_clk: AHB2PHY interface clock
* @ref_clk: phy reference clock
* @iface_clk: phy interface clock
* @phy_reset: phy reset control
* @vregs: regulator supplies bulk data
*
* @tcsr: TCSR syscon register map
* @cell: nvmem cell containing phy tuning value
*
* @cfg: phy config data
* @has_se_clk_scheme: indicate if PHY has single-ended ref clock scheme
*/
struct qusb2_phy {
struct phy *phy;
void __iomem *base;
struct clk *cfg_ahb_clk;
struct clk *ref_clk;
struct clk *iface_clk;
struct reset_control *phy_reset;
struct regulator_bulk_data vregs[QUSB2_NUM_VREGS];
struct regmap *tcsr;
struct nvmem_cell *cell;
const struct qusb2_phy_cfg *cfg;
bool has_se_clk_scheme;
};
static inline void qusb2_setbits(void __iomem *base, u32 offset, u32 val)
{
u32 reg;
reg = readl(base + offset);
reg |= val;
writel(reg, base + offset);
/* Ensure above write is completed */
readl(base + offset);
}
static inline void qusb2_clrbits(void __iomem *base, u32 offset, u32 val)
{
u32 reg;
reg = readl(base + offset);
reg &= ~val;
writel(reg, base + offset);
/* Ensure above write is completed */
readl(base + offset);
}
static inline
void qcom_qusb2_phy_configure(void __iomem *base,
const struct qusb2_phy_init_tbl tbl[], int num)
{
int i;
for (i = 0; i < num; i++)
writel(tbl[i].val, base + tbl[i].offset);
}
/*
* Fetches HS Tx tuning value from nvmem and sets the
* QUSB2PHY_PORT_TUNE2 register.
* For error case, skip setting the value and use the default value.
*/
static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy)
{
struct device *dev = &qphy->phy->dev;
u8 *val;
/*
* Read efuse register having TUNE2 parameter's high nibble.
* If efuse register shows value as 0x0, or if we fail to find
* a valid efuse register settings, then use default value
* as 0xB for high nibble that we have already set while
* configuring phy.
*/
val = nvmem_cell_read(qphy->cell, NULL);
if (IS_ERR(val) || !val[0]) {
dev_dbg(dev, "failed to read a valid hs-tx trim value\n");
return;
}
/* Fused TUNE2 value is the higher nibble only */
qusb2_setbits(qphy->base, QUSB2PHY_PORT_TUNE2, val[0] << 0x4);
}
static int qusb2_phy_poweron(struct phy *phy)
{
struct qusb2_phy *qphy = phy_get_drvdata(phy);
int num = ARRAY_SIZE(qphy->vregs);
int ret;
dev_vdbg(&phy->dev, "%s(): Powering-on QUSB2 phy\n", __func__);
/* turn on regulator supplies */
ret = regulator_bulk_enable(num, qphy->vregs);
if (ret)
return ret;
ret = clk_prepare_enable(qphy->iface_clk);
if (ret) {
dev_err(&phy->dev, "failed to enable iface_clk, %d\n", ret);
regulator_bulk_disable(num, qphy->vregs);
return ret;
}
return 0;
}
static int qusb2_phy_poweroff(struct phy *phy)
{
struct qusb2_phy *qphy = phy_get_drvdata(phy);
clk_disable_unprepare(qphy->iface_clk);
regulator_bulk_disable(ARRAY_SIZE(qphy->vregs), qphy->vregs);
return 0;
}
static int qusb2_phy_init(struct phy *phy)
{
struct qusb2_phy *qphy = phy_get_drvdata(phy);
unsigned int val;
unsigned int clk_scheme;
int ret;
dev_vdbg(&phy->dev, "%s(): Initializing QUSB2 phy\n", __func__);
/* enable ahb interface clock to program phy */
ret = clk_prepare_enable(qphy->cfg_ahb_clk);
if (ret) {
dev_err(&phy->dev, "failed to enable cfg ahb clock, %d\n", ret);
return ret;
}
/* Perform phy reset */
ret = reset_control_assert(qphy->phy_reset);
if (ret) {
dev_err(&phy->dev, "failed to assert phy_reset, %d\n", ret);
goto disable_ahb_clk;
}
/* 100 us delay to keep PHY in reset mode */
usleep_range(100, 150);
ret = reset_control_deassert(qphy->phy_reset);
if (ret) {
dev_err(&phy->dev, "failed to de-assert phy_reset, %d\n", ret);
goto disable_ahb_clk;
}
/* Disable the PHY */
qusb2_setbits(qphy->base, QUSB2PHY_PORT_POWERDOWN,
CLAMP_N_EN | FREEZIO_N | POWER_DOWN);
/* save reset value to override reference clock scheme later */
val = readl(qphy->base + QUSB2PHY_PLL_TEST);
qcom_qusb2_phy_configure(qphy->base, qphy->cfg->tbl,
qphy->cfg->tbl_num);
/* Set efuse value for tuning the PHY */
qusb2_phy_set_tune2_param(qphy);
/* Enable the PHY */
qusb2_clrbits(qphy->base, QUSB2PHY_PORT_POWERDOWN, POWER_DOWN);
/* Required to get phy pll lock successfully */
usleep_range(150, 160);
/* Default is single-ended clock on msm8996 */
qphy->has_se_clk_scheme = true;
/*
* read TCSR_PHY_CLK_SCHEME register to check if single-ended
* clock scheme is selected. If yes, then disable differential
* ref_clk and use single-ended clock, otherwise use differential
* ref_clk only.
*/
if (qphy->tcsr) {
ret = regmap_read(qphy->tcsr, qphy->cfg->clk_scheme_offset,
&clk_scheme);
if (ret) {
dev_err(&phy->dev, "failed to read clk scheme reg\n");
goto assert_phy_reset;
}
/* is it a differential clock scheme ? */
if (!(clk_scheme & PHY_CLK_SCHEME_SEL)) {
dev_vdbg(&phy->dev, "%s(): select differential clk\n",
__func__);
qphy->has_se_clk_scheme = false;
} else {
dev_vdbg(&phy->dev, "%s(): select single-ended clk\n",
__func__);
}
}
if (!qphy->has_se_clk_scheme) {
val &= ~CLK_REF_SEL;
ret = clk_prepare_enable(qphy->ref_clk);
if (ret) {
dev_err(&phy->dev, "failed to enable ref clk, %d\n",
ret);
goto assert_phy_reset;
}
} else {
val |= CLK_REF_SEL;
}
writel(val, qphy->base + QUSB2PHY_PLL_TEST);
/* ensure above write is through */
readl(qphy->base + QUSB2PHY_PLL_TEST);
/* Required to get phy pll lock successfully */
usleep_range(100, 110);
val = readb(qphy->base + QUSB2PHY_PLL_STATUS);
if (!(val & PLL_LOCKED)) {
dev_err(&phy->dev,
"QUSB2PHY pll lock failed: status reg = %x\n", val);
ret = -EBUSY;
goto disable_ref_clk;
}
return 0;
disable_ref_clk:
if (!qphy->has_se_clk_scheme)
clk_disable_unprepare(qphy->ref_clk);
assert_phy_reset:
reset_control_assert(qphy->phy_reset);
disable_ahb_clk:
clk_disable_unprepare(qphy->cfg_ahb_clk);
return ret;
}
static int qusb2_phy_exit(struct phy *phy)
{
struct qusb2_phy *qphy = phy_get_drvdata(phy);
/* Disable the PHY */
qusb2_setbits(qphy->base, QUSB2PHY_PORT_POWERDOWN,
CLAMP_N_EN | FREEZIO_N | POWER_DOWN);
if (!qphy->has_se_clk_scheme)
clk_disable_unprepare(qphy->ref_clk);
reset_control_assert(qphy->phy_reset);
clk_disable_unprepare(qphy->cfg_ahb_clk);
return 0;
}
static const struct phy_ops qusb2_phy_gen_ops = {
.init = qusb2_phy_init,
.exit = qusb2_phy_exit,
.power_on = qusb2_phy_poweron,
.power_off = qusb2_phy_poweroff,
.owner = THIS_MODULE,
};
static const struct of_device_id qusb2_phy_of_match_table[] = {
{
.compatible = "qcom,msm8996-qusb2-phy",
.data = &msm8996_phy_cfg,
},
{ },
};
MODULE_DEVICE_TABLE(of, qusb2_phy_of_match_table);
static int qusb2_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct qusb2_phy *qphy;
struct phy_provider *phy_provider;
struct phy *generic_phy;
struct resource *res;
int ret, i;
int num;
qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
if (!qphy)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
qphy->base = devm_ioremap_resource(dev, res);
if (IS_ERR(qphy->base))
return PTR_ERR(qphy->base);
qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb");
if (IS_ERR(qphy->cfg_ahb_clk)) {
ret = PTR_ERR(qphy->cfg_ahb_clk);
if (ret != -EPROBE_DEFER)
dev_err(dev, "failed to get cfg ahb clk, %d\n", ret);
return ret;
}
qphy->ref_clk = devm_clk_get(dev, "ref");
if (IS_ERR(qphy->ref_clk)) {
ret = PTR_ERR(qphy->ref_clk);
if (ret != -EPROBE_DEFER)
dev_err(dev, "failed to get ref clk, %d\n", ret);
return ret;
}
qphy->iface_clk = devm_clk_get(dev, "iface");
if (IS_ERR(qphy->iface_clk)) {
ret = PTR_ERR(qphy->iface_clk);
if (ret == -EPROBE_DEFER)
return ret;
qphy->iface_clk = NULL;
dev_dbg(dev, "failed to get iface clk, %d\n", ret);
}
qphy->phy_reset = devm_reset_control_get_by_index(&pdev->dev, 0);
if (IS_ERR(qphy->phy_reset)) {
dev_err(dev, "failed to get phy core reset\n");
return PTR_ERR(qphy->phy_reset);
}
num = ARRAY_SIZE(qphy->vregs);
for (i = 0; i < num; i++)
qphy->vregs[i].supply = qusb2_phy_vreg_names[i];
ret = devm_regulator_bulk_get(dev, num, qphy->vregs);
if (ret) {
dev_err(dev, "failed to get regulator supplies\n");
return ret;
}
/* Get the specific init parameters of QMP phy */
qphy->cfg = of_device_get_match_data(dev);
qphy->tcsr = syscon_regmap_lookup_by_phandle(dev->of_node,
"qcom,tcsr-syscon");
if (IS_ERR(qphy->tcsr)) {
dev_dbg(dev, "failed to lookup TCSR regmap\n");
qphy->tcsr = NULL;
}
qphy->cell = devm_nvmem_cell_get(dev, NULL);
if (IS_ERR(qphy->cell)) {
if (PTR_ERR(qphy->cell) == -EPROBE_DEFER)
return -EPROBE_DEFER;
qphy->cell = NULL;
dev_dbg(dev, "failed to lookup tune2 hstx trim value\n");
}
generic_phy = devm_phy_create(dev, NULL, &qusb2_phy_gen_ops);
if (IS_ERR(generic_phy)) {
ret = PTR_ERR(generic_phy);
dev_err(dev, "failed to create phy, %d\n", ret);
return ret;
}
qphy->phy = generic_phy;
dev_set_drvdata(dev, qphy);
phy_set_drvdata(generic_phy, qphy);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (!IS_ERR(phy_provider))
dev_info(dev, "Registered Qcom-QUSB2 phy\n");
return PTR_ERR_OR_ZERO(phy_provider);
}
static struct platform_driver qusb2_phy_driver = {
.probe = qusb2_phy_probe,
.driver = {
.name = "qcom-qusb2-phy",
.of_match_table = qusb2_phy_of_match_table,
},
};
module_platform_driver(qusb2_phy_driver);
MODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>");
MODULE_DESCRIPTION("Qualcomm QUSB2 PHY driver");
MODULE_LICENSE("GPL v2");

View File

@ -20,6 +20,7 @@
#include <linux/of_address.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/workqueue.h>
@ -395,7 +396,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
struct rcar_gen3_chan *channel;
struct phy_provider *provider;
struct resource *res;
int irq;
int irq, ret = 0;
if (!dev->of_node) {
dev_err(dev, "This driver needs device tree\n");
@ -434,17 +435,24 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
}
}
/* devm_phy_create() will call pm_runtime_enable(dev); */
/*
* devm_phy_create() will call pm_runtime_enable(&phy->dev);
* And then, phy-core will manage runtime pm for this device.
*/
pm_runtime_enable(dev);
channel->phy = devm_phy_create(dev, NULL, &rcar_gen3_phy_usb2_ops);
if (IS_ERR(channel->phy)) {
dev_err(dev, "Failed to create USB2 PHY\n");
return PTR_ERR(channel->phy);
ret = PTR_ERR(channel->phy);
goto error;
}
channel->vbus = devm_regulator_get_optional(dev, "vbus");
if (IS_ERR(channel->vbus)) {
if (PTR_ERR(channel->vbus) == -EPROBE_DEFER)
return PTR_ERR(channel->vbus);
if (PTR_ERR(channel->vbus) == -EPROBE_DEFER) {
ret = PTR_ERR(channel->vbus);
goto error;
}
channel->vbus = NULL;
}
@ -454,15 +462,22 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (IS_ERR(provider)) {
dev_err(dev, "Failed to register PHY provider\n");
ret = PTR_ERR(provider);
goto error;
} else if (channel->has_otg) {
int ret;
ret = device_create_file(dev, &dev_attr_role);
if (ret < 0)
return ret;
goto error;
}
return PTR_ERR_OR_ZERO(provider);
return 0;
error:
pm_runtime_disable(dev);
return ret;
}
static int rcar_gen3_phy_usb2_remove(struct platform_device *pdev)
@ -472,6 +487,8 @@ static int rcar_gen3_phy_usb2_remove(struct platform_device *pdev)
if (channel->has_otg)
device_remove_file(&pdev->dev, &dev_attr_role);
pm_runtime_disable(&pdev->dev);
return 0;
};

View File

@ -554,8 +554,7 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
case USB_CHG_STATE_DETECTED:
switch (rphy->chg_type) {
case POWER_SUPPLY_TYPE_USB:
dev_dbg(&rport->phy->dev,
"sdp cable is connecetd\n");
dev_dbg(&rport->phy->dev, "sdp cable is connected\n");
rockchip_usb2phy_power_on(rport->phy);
rport->state = OTG_STATE_B_PERIPHERAL;
notify_charger = true;
@ -563,16 +562,14 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
cable = EXTCON_CHG_USB_SDP;
break;
case POWER_SUPPLY_TYPE_USB_DCP:
dev_dbg(&rport->phy->dev,
"dcp cable is connecetd\n");
dev_dbg(&rport->phy->dev, "dcp cable is connected\n");
rockchip_usb2phy_power_off(rport->phy);
notify_charger = true;
sch_work = true;
cable = EXTCON_CHG_USB_DCP;
break;
case POWER_SUPPLY_TYPE_USB_CDP:
dev_dbg(&rport->phy->dev,
"cdp cable is connecetd\n");
dev_dbg(&rport->phy->dev, "cdp cable is connected\n");
rockchip_usb2phy_power_on(rport->phy);
rport->state = OTG_STATE_B_PERIPHERAL;
notify_charger = true;
@ -1141,6 +1138,49 @@ disable_clks:
return ret;
}
static const struct rockchip_usb2phy_cfg rk3328_phy_cfgs[] = {
{
.reg = 0x100,
.num_ports = 2,
.clkout_ctl = { 0x108, 4, 4, 1, 0 },
.port_cfgs = {
[USB2PHY_PORT_OTG] = {
.phy_sus = { 0x0100, 15, 0, 0, 0x1d1 },
.bvalid_det_en = { 0x0110, 2, 2, 0, 1 },
.bvalid_det_st = { 0x0114, 2, 2, 0, 1 },
.bvalid_det_clr = { 0x0118, 2, 2, 0, 1 },
.ls_det_en = { 0x0110, 0, 0, 0, 1 },
.ls_det_st = { 0x0114, 0, 0, 0, 1 },
.ls_det_clr = { 0x0118, 0, 0, 0, 1 },
.utmi_avalid = { 0x0120, 10, 10, 0, 1 },
.utmi_bvalid = { 0x0120, 9, 9, 0, 1 },
.utmi_ls = { 0x0120, 5, 4, 0, 1 },
},
[USB2PHY_PORT_HOST] = {
.phy_sus = { 0x104, 15, 0, 0, 0x1d1 },
.ls_det_en = { 0x110, 1, 1, 0, 1 },
.ls_det_st = { 0x114, 1, 1, 0, 1 },
.ls_det_clr = { 0x118, 1, 1, 0, 1 },
.utmi_ls = { 0x120, 17, 16, 0, 1 },
.utmi_hstdet = { 0x120, 19, 19, 0, 1 }
}
},
.chg_det = {
.opmode = { 0x0100, 3, 0, 5, 1 },
.cp_det = { 0x0120, 24, 24, 0, 1 },
.dcp_det = { 0x0120, 23, 23, 0, 1 },
.dp_det = { 0x0120, 25, 25, 0, 1 },
.idm_sink_en = { 0x0108, 8, 8, 0, 1 },
.idp_sink_en = { 0x0108, 7, 7, 0, 1 },
.idp_src_en = { 0x0108, 9, 9, 0, 1 },
.rdm_pdwn_en = { 0x0108, 10, 10, 0, 1 },
.vdm_src_en = { 0x0108, 12, 12, 0, 1 },
.vdp_src_en = { 0x0108, 11, 11, 0, 1 },
},
},
{ /* sentinel */ }
};
static const struct rockchip_usb2phy_cfg rk3366_phy_cfgs[] = {
{
.reg = 0x700,
@ -1223,6 +1263,7 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = {
};
static const struct of_device_id rockchip_usb2phy_dt_match[] = {
{ .compatible = "rockchip,rk3328-usb2phy", .data = &rk3328_phy_cfgs },
{ .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs },
{ .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs },
{}

View File

@ -66,6 +66,7 @@ struct rockchip_usb_phy {
struct phy *phy;
bool uart_enabled;
struct reset_control *reset;
struct regulator *vbus;
};
static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy,
@ -88,6 +89,9 @@ static void rockchip_usb_phy480m_disable(struct clk_hw *hw)
struct rockchip_usb_phy,
clk480m_hw);
if (phy->vbus)
regulator_disable(phy->vbus);
/* Power down usb phy analog blocks by set siddq 1 */
rockchip_usb_phy_power(phy, 1);
}
@ -143,6 +147,14 @@ static int rockchip_usb_phy_power_on(struct phy *_phy)
if (phy->uart_enabled)
return -EBUSY;
if (phy->vbus) {
int ret;
ret = regulator_enable(phy->vbus);
if (ret)
return ret;
}
return clk_prepare_enable(phy->clk480m);
}
@ -268,6 +280,13 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
}
phy_set_drvdata(rk_phy->phy, rk_phy);
rk_phy->vbus = devm_regulator_get_optional(&rk_phy->phy->dev, "vbus");
if (IS_ERR(rk_phy->vbus)) {
if (PTR_ERR(rk_phy->vbus) == -EPROBE_DEFER)
return PTR_ERR(rk_phy->vbus);
rk_phy->vbus = NULL;
}
/*
* When acting as uart-pipe, just keep clock on otherwise
* only power up usb phy when it use, so disable it when init

View File

@ -49,12 +49,14 @@
#define REG_PHYBIST 0x08
#define REG_PHYTUNE 0x0c
#define REG_PHYCTL_A33 0x10
#define REG_PHY_UNK_H3 0x20
#define REG_PHY_OTGCTL 0x20
#define REG_PMU_UNK1 0x10
#define PHYCTL_DATA BIT(7)
#define OTGCTL_ROUTE_MUSB BIT(0)
#define SUNXI_AHB_ICHR8_EN BIT(10)
#define SUNXI_AHB_INCR4_BURST_EN BIT(9)
#define SUNXI_AHB_INCRX_ALIGN_EN BIT(8)
@ -110,6 +112,7 @@ struct sun4i_usb_phy_cfg {
u8 phyctl_offset;
bool dedicated_clocks;
bool enable_pmu_unk1;
bool phy0_dual_route;
};
struct sun4i_usb_phy_data {
@ -188,10 +191,8 @@ static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
spin_lock_irqsave(&phy_data->reg_lock, flags);
if (phy_data->cfg->type == sun8i_a33_phy ||
phy_data->cfg->type == sun50i_a64_phy ||
phy_data->cfg->type == sun8i_v3s_phy) {
/* A33 or A64 needs us to set phyctl to 0 explicitly */
if (phy_data->cfg->phyctl_offset == REG_PHYCTL_A33) {
/* SoCs newer than A33 need us to set phyctl to 0 explicitly */
writel(0, phyctl);
}
@ -271,23 +272,16 @@ static int sun4i_usb_phy_init(struct phy *_phy)
writel(val & ~2, phy->pmu + REG_PMU_UNK1);
}
if (data->cfg->type == sun8i_h3_phy) {
if (phy->index == 0) {
val = readl(data->base + REG_PHY_UNK_H3);
writel(val & ~1, data->base + REG_PHY_UNK_H3);
}
} else {
/* Enable USB 45 Ohm resistor calibration */
if (phy->index == 0)
sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1);
/* 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);
/* Adjust PHY's magnitude and rate */
sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5);
/* Disconnect threshold adjustment */
sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL,
data->cfg->disc_thresh, 2);
}
/* Disconnect threshold adjustment */
sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL,
data->cfg->disc_thresh, 2);
sun4i_usb_phy_passby(phy, 1);
@ -486,6 +480,21 @@ static const struct phy_ops sun4i_usb_phy_ops = {
.owner = THIS_MODULE,
};
static void sun4i_usb_phy0_reroute(struct sun4i_usb_phy_data *data, int id_det)
{
u32 regval;
regval = readl(data->base + REG_PHY_OTGCTL);
if (id_det == 0) {
/* Host mode. Route phy0 to EHCI/OHCI */
regval &= ~OTGCTL_ROUTE_MUSB;
} else {
/* Peripheral mode. Route phy0 to MUSB */
regval |= OTGCTL_ROUTE_MUSB;
}
writel(regval, data->base + REG_PHY_OTGCTL);
}
static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
{
struct sun4i_usb_phy_data *data =
@ -546,6 +555,10 @@ static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
sun4i_usb_phy0_set_vbus_detect(phy0, 1);
mutex_unlock(&phy0->mutex);
}
/* Re-route PHY0 if necessary */
if (data->cfg->phy0_dual_route)
sun4i_usb_phy0_reroute(data, id_det);
}
if (vbus_notify)
@ -700,7 +713,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
return PTR_ERR(phy->reset);
}
if (i) { /* No pmu for usbc0 */
if (i || data->cfg->phy0_dual_route) { /* No pmu for musb */
snprintf(name, sizeof(name), "pmu%d", i);
res = platform_get_resource_byname(pdev,
IORESOURCE_MEM, name);
@ -823,8 +836,10 @@ static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = {
.num_phys = 4,
.type = sun8i_h3_phy,
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A33,
.dedicated_clocks = true,
.enable_pmu_unk1 = true,
.phy0_dual_route = true,
};
static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = {
@ -843,6 +858,7 @@ static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = {
.phyctl_offset = REG_PHYCTL_A33,
.dedicated_clocks = true,
.enable_pmu_unk1 = true,
.phy0_dual_route = true,
};
static const struct of_device_id sun4i_usb_phy_of_match[] = {

View File

@ -104,4 +104,6 @@ source "drivers/staging/vc04_services/Kconfig"
source "drivers/staging/bcm2835-audio/Kconfig"
source "drivers/staging/typec/Kconfig"
endif # STAGING

View File

@ -1,6 +1,7 @@
# Makefile for staging directory
obj-y += media/
obj-y += typec/
obj-$(CONFIG_PRISM2_USB) += wlan-ng/
obj-$(CONFIG_COMEDI) += comedi/
obj-$(CONFIG_FB_OLPC_DCON) += olpc_dcon/
@ -41,4 +42,3 @@ obj-$(CONFIG_KS7010) += ks7010/
obj-$(CONFIG_GREYBUS) += greybus/
obj-$(CONFIG_BCM2835_VCHIQ) += vc04_services/
obj-$(CONFIG_SND_BCM2835) += bcm2835-audio/

View File

@ -0,0 +1,24 @@
menu "USB Power Delivery and Type-C drivers"
config TYPEC_TCPM
tristate "USB Type-C Port Controller Manager"
depends on USB
select TYPEC
help
The Type-C Port Controller Manager provides a USB PD and USB Type-C
state machine for use with Type-C Port Controllers.
if TYPEC_TCPM
config TYPEC_TCPCI
tristate "Type-C Port Controller Interface driver"
depends on I2C
select REGMAP_I2C
help
Type-C Port Controller driver for TCPCI-compliant controller.
source "drivers/staging/typec/fusb302/Kconfig"
endif
endmenu

View File

@ -0,0 +1,3 @@
obj-$(CONFIG_TYPEC_TCPM) += tcpm.o
obj-$(CONFIG_TYPEC_TCPCI) += tcpci.o
obj-y += fusb302/

View File

@ -0,0 +1,15 @@
tcpm:
- Add documentation (at the very least for the API to low level drivers)
- Split PD code into separate file
- Check if it makes sense to use tracepoints instead of debugfs for debug logs
- Implement Alternate Mode handling
- Address "#if 0" code if not addressed with the above
- Validate all comments marked with "XXX"; either address or remove comments
- Add support for USB PD 3.0. While not mandatory, at least fast role swap
as well as authentication support would be very desirable.
tcpci:
- Test with real hardware
Please send patches to Guenter Roeck <linux@roeck-us.net> and copy
Heikki Krogerus <heikki.krogerus@linux.intel.com>.

View File

@ -0,0 +1,7 @@
config TYPEC_FUSB302
tristate "Fairchild FUSB302 Type-C chip driver"
depends on I2C
help
The Fairchild FUSB302 Type-C chip driver that works with
Type-C Port Controller Manager to provide USB PD and USB
Type-C functionalities.

View File

@ -0,0 +1 @@
obj-$(CONFIG_TYPEC_FUSB302) += fusb302.o

View File

@ -0,0 +1,6 @@
fusb302:
- Find a better logging scheme, at least not having the same debugging/logging
code replicated here and in tcpm
- Find a non-hacky way to coordinate between PM and I2C access
- Documentation? The FUSB302 datasheet provides information on the chip to help
understand the code. But it may still be helpful to have a documentation.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,186 @@
/*
* Copyright 2016-2017 Google, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Fairchild FUSB302 Type-C Chip Driver
*/
#ifndef FUSB302_REG_H
#define FUSB302_REG_H
#define FUSB_REG_DEVICE_ID 0x01
#define FUSB_REG_SWITCHES0 0x02
#define FUSB_REG_SWITCHES0_CC2_PU_EN BIT(7)
#define FUSB_REG_SWITCHES0_CC1_PU_EN BIT(6)
#define FUSB_REG_SWITCHES0_VCONN_CC2 BIT(5)
#define FUSB_REG_SWITCHES0_VCONN_CC1 BIT(4)
#define FUSB_REG_SWITCHES0_MEAS_CC2 BIT(3)
#define FUSB_REG_SWITCHES0_MEAS_CC1 BIT(2)
#define FUSB_REG_SWITCHES0_CC2_PD_EN BIT(1)
#define FUSB_REG_SWITCHES0_CC1_PD_EN BIT(0)
#define FUSB_REG_SWITCHES1 0x03
#define FUSB_REG_SWITCHES1_POWERROLE BIT(7)
#define FUSB_REG_SWITCHES1_SPECREV1 BIT(6)
#define FUSB_REG_SWITCHES1_SPECREV0 BIT(5)
#define FUSB_REG_SWITCHES1_DATAROLE BIT(4)
#define FUSB_REG_SWITCHES1_AUTO_GCRC BIT(2)
#define FUSB_REG_SWITCHES1_TXCC2_EN BIT(1)
#define FUSB_REG_SWITCHES1_TXCC1_EN BIT(0)
#define FUSB_REG_MEASURE 0x04
#define FUSB_REG_MEASURE_MDAC5 BIT(7)
#define FUSB_REG_MEASURE_MDAC4 BIT(6)
#define FUSB_REG_MEASURE_MDAC3 BIT(5)
#define FUSB_REG_MEASURE_MDAC2 BIT(4)
#define FUSB_REG_MEASURE_MDAC1 BIT(3)
#define FUSB_REG_MEASURE_MDAC0 BIT(2)
#define FUSB_REG_MEASURE_VBUS BIT(1)
#define FUSB_REG_MEASURE_XXXX5 BIT(0)
#define FUSB_REG_CONTROL0 0x06
#define FUSB_REG_CONTROL0_TX_FLUSH BIT(6)
#define FUSB_REG_CONTROL0_INT_MASK BIT(5)
#define FUSB_REG_CONTROL0_HOST_CUR_MASK (0xC)
#define FUSB_REG_CONTROL0_HOST_CUR_HIGH (0xC)
#define FUSB_REG_CONTROL0_HOST_CUR_MED (0x8)
#define FUSB_REG_CONTROL0_HOST_CUR_DEF (0x4)
#define FUSB_REG_CONTROL0_TX_START BIT(0)
#define FUSB_REG_CONTROL1 0x07
#define FUSB_REG_CONTROL1_ENSOP2DB BIT(6)
#define FUSB_REG_CONTROL1_ENSOP1DB BIT(5)
#define FUSB_REG_CONTROL1_BIST_MODE2 BIT(4)
#define FUSB_REG_CONTROL1_RX_FLUSH BIT(2)
#define FUSB_REG_CONTROL1_ENSOP2 BIT(1)
#define FUSB_REG_CONTROL1_ENSOP1 BIT(0)
#define FUSB_REG_CONTROL2 0x08
#define FUSB_REG_CONTROL2_MODE BIT(1)
#define FUSB_REG_CONTROL2_MODE_MASK (0x6)
#define FUSB_REG_CONTROL2_MODE_DFP (0x6)
#define FUSB_REG_CONTROL2_MODE_UFP (0x4)
#define FUSB_REG_CONTROL2_MODE_DRP (0x2)
#define FUSB_REG_CONTROL2_MODE_NONE (0x0)
#define FUSB_REG_CONTROL2_TOGGLE BIT(0)
#define FUSB_REG_CONTROL3 0x09
#define FUSB_REG_CONTROL3_SEND_HARDRESET BIT(6)
#define FUSB_REG_CONTROL3_BIST_TMODE BIT(5) /* 302B Only */
#define FUSB_REG_CONTROL3_AUTO_HARDRESET BIT(4)
#define FUSB_REG_CONTROL3_AUTO_SOFTRESET BIT(3)
#define FUSB_REG_CONTROL3_N_RETRIES BIT(1)
#define FUSB_REG_CONTROL3_N_RETRIES_MASK (0x6)
#define FUSB_REG_CONTROL3_N_RETRIES_3 (0x6)
#define FUSB_REG_CONTROL3_N_RETRIES_2 (0x4)
#define FUSB_REG_CONTROL3_N_RETRIES_1 (0x2)
#define FUSB_REG_CONTROL3_AUTO_RETRY BIT(0)
#define FUSB_REG_MASK 0x0A
#define FUSB_REG_MASK_VBUSOK BIT(7)
#define FUSB_REG_MASK_ACTIVITY BIT(6)
#define FUSB_REG_MASK_COMP_CHNG BIT(5)
#define FUSB_REG_MASK_CRC_CHK BIT(4)
#define FUSB_REG_MASK_ALERT BIT(3)
#define FUSB_REG_MASK_WAKE BIT(2)
#define FUSB_REG_MASK_COLLISION BIT(1)
#define FUSB_REG_MASK_BC_LVL BIT(0)
#define FUSB_REG_POWER 0x0B
#define FUSB_REG_POWER_PWR BIT(0)
#define FUSB_REG_POWER_PWR_LOW 0x1
#define FUSB_REG_POWER_PWR_MEDIUM 0x3
#define FUSB_REG_POWER_PWR_HIGH 0x7
#define FUSB_REG_POWER_PWR_ALL 0xF
#define FUSB_REG_RESET 0x0C
#define FUSB_REG_RESET_PD_RESET BIT(1)
#define FUSB_REG_RESET_SW_RESET BIT(0)
#define FUSB_REG_MASKA 0x0E
#define FUSB_REG_MASKA_OCP_TEMP BIT(7)
#define FUSB_REG_MASKA_TOGDONE BIT(6)
#define FUSB_REG_MASKA_SOFTFAIL BIT(5)
#define FUSB_REG_MASKA_RETRYFAIL BIT(4)
#define FUSB_REG_MASKA_HARDSENT BIT(3)
#define FUSB_REG_MASKA_TX_SUCCESS BIT(2)
#define FUSB_REG_MASKA_SOFTRESET BIT(1)
#define FUSB_REG_MASKA_HARDRESET BIT(0)
#define FUSB_REG_MASKB 0x0F
#define FUSB_REG_MASKB_GCRCSENT BIT(0)
#define FUSB_REG_STATUS0A 0x3C
#define FUSB_REG_STATUS0A_SOFTFAIL BIT(5)
#define FUSB_REG_STATUS0A_RETRYFAIL BIT(4)
#define FUSB_REG_STATUS0A_POWER BIT(2)
#define FUSB_REG_STATUS0A_RX_SOFT_RESET BIT(1)
#define FUSB_REG_STATUS0A_RX_HARD_RESET BIT(0)
#define FUSB_REG_STATUS1A 0x3D
#define FUSB_REG_STATUS1A_TOGSS BIT(3)
#define FUSB_REG_STATUS1A_TOGSS_RUNNING 0x0
#define FUSB_REG_STATUS1A_TOGSS_SRC1 0x1
#define FUSB_REG_STATUS1A_TOGSS_SRC2 0x2
#define FUSB_REG_STATUS1A_TOGSS_SNK1 0x5
#define FUSB_REG_STATUS1A_TOGSS_SNK2 0x6
#define FUSB_REG_STATUS1A_TOGSS_AA 0x7
#define FUSB_REG_STATUS1A_TOGSS_POS (3)
#define FUSB_REG_STATUS1A_TOGSS_MASK (0x7)
#define FUSB_REG_STATUS1A_RXSOP2DB BIT(2)
#define FUSB_REG_STATUS1A_RXSOP1DB BIT(1)
#define FUSB_REG_STATUS1A_RXSOP BIT(0)
#define FUSB_REG_INTERRUPTA 0x3E
#define FUSB_REG_INTERRUPTA_OCP_TEMP BIT(7)
#define FUSB_REG_INTERRUPTA_TOGDONE BIT(6)
#define FUSB_REG_INTERRUPTA_SOFTFAIL BIT(5)
#define FUSB_REG_INTERRUPTA_RETRYFAIL BIT(4)
#define FUSB_REG_INTERRUPTA_HARDSENT BIT(3)
#define FUSB_REG_INTERRUPTA_TX_SUCCESS BIT(2)
#define FUSB_REG_INTERRUPTA_SOFTRESET BIT(1)
#define FUSB_REG_INTERRUPTA_HARDRESET BIT(0)
#define FUSB_REG_INTERRUPTB 0x3F
#define FUSB_REG_INTERRUPTB_GCRCSENT BIT(0)
#define FUSB_REG_STATUS0 0x40
#define FUSB_REG_STATUS0_VBUSOK BIT(7)
#define FUSB_REG_STATUS0_ACTIVITY BIT(6)
#define FUSB_REG_STATUS0_COMP BIT(5)
#define FUSB_REG_STATUS0_CRC_CHK BIT(4)
#define FUSB_REG_STATUS0_ALERT BIT(3)
#define FUSB_REG_STATUS0_WAKE BIT(2)
#define FUSB_REG_STATUS0_BC_LVL_MASK 0x03
#define FUSB_REG_STATUS0_BC_LVL_0_200 0x0
#define FUSB_REG_STATUS0_BC_LVL_200_600 0x1
#define FUSB_REG_STATUS0_BC_LVL_600_1230 0x2
#define FUSB_REG_STATUS0_BC_LVL_1230_MAX 0x3
#define FUSB_REG_STATUS0_BC_LVL1 BIT(1)
#define FUSB_REG_STATUS0_BC_LVL0 BIT(0)
#define FUSB_REG_STATUS1 0x41
#define FUSB_REG_STATUS1_RXSOP2 BIT(7)
#define FUSB_REG_STATUS1_RXSOP1 BIT(6)
#define FUSB_REG_STATUS1_RX_EMPTY BIT(5)
#define FUSB_REG_STATUS1_RX_FULL BIT(4)
#define FUSB_REG_STATUS1_TX_EMPTY BIT(3)
#define FUSB_REG_STATUS1_TX_FULL BIT(2)
#define FUSB_REG_INTERRUPT 0x42
#define FUSB_REG_INTERRUPT_VBUSOK BIT(7)
#define FUSB_REG_INTERRUPT_ACTIVITY BIT(6)
#define FUSB_REG_INTERRUPT_COMP_CHNG BIT(5)
#define FUSB_REG_INTERRUPT_CRC_CHK BIT(4)
#define FUSB_REG_INTERRUPT_ALERT BIT(3)
#define FUSB_REG_INTERRUPT_WAKE BIT(2)
#define FUSB_REG_INTERRUPT_COLLISION BIT(1)
#define FUSB_REG_INTERRUPT_BC_LVL BIT(0)
#define FUSB_REG_FIFOS 0x43
/* Tokens defined for the FUSB302 TX FIFO */
enum fusb302_txfifo_tokens {
FUSB302_TKN_TXON = 0xA1,
FUSB302_TKN_SYNC1 = 0x12,
FUSB302_TKN_SYNC2 = 0x13,
FUSB302_TKN_SYNC3 = 0x1B,
FUSB302_TKN_RST1 = 0x15,
FUSB302_TKN_RST2 = 0x16,
FUSB302_TKN_PACKSYM = 0x80,
FUSB302_TKN_JAMCRC = 0xFF,
FUSB302_TKN_EOP = 0x14,
FUSB302_TKN_TXOFF = 0xFE,
};
#endif

281
drivers/staging/typec/pd.h Normal file
View File

@ -0,0 +1,281 @@
/*
* Copyright 2015-2017 Google, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __LINUX_USB_PD_H
#define __LINUX_USB_PD_H
#include <linux/types.h>
#include <linux/usb/typec.h>
/* USB PD Messages */
enum pd_ctrl_msg_type {
/* 0 Reserved */
PD_CTRL_GOOD_CRC = 1,
PD_CTRL_GOTO_MIN = 2,
PD_CTRL_ACCEPT = 3,
PD_CTRL_REJECT = 4,
PD_CTRL_PING = 5,
PD_CTRL_PS_RDY = 6,
PD_CTRL_GET_SOURCE_CAP = 7,
PD_CTRL_GET_SINK_CAP = 8,
PD_CTRL_DR_SWAP = 9,
PD_CTRL_PR_SWAP = 10,
PD_CTRL_VCONN_SWAP = 11,
PD_CTRL_WAIT = 12,
PD_CTRL_SOFT_RESET = 13,
/* 14-15 Reserved */
};
enum pd_data_msg_type {
/* 0 Reserved */
PD_DATA_SOURCE_CAP = 1,
PD_DATA_REQUEST = 2,
PD_DATA_BIST = 3,
PD_DATA_SINK_CAP = 4,
/* 5-14 Reserved */
PD_DATA_VENDOR_DEF = 15,
};
#define PD_REV10 0x0
#define PD_REV20 0x1
#define PD_HEADER_CNT_SHIFT 12
#define PD_HEADER_CNT_MASK 0x7
#define PD_HEADER_ID_SHIFT 9
#define PD_HEADER_ID_MASK 0x7
#define PD_HEADER_PWR_ROLE BIT(8)
#define PD_HEADER_REV_SHIFT 6
#define PD_HEADER_REV_MASK 0x3
#define PD_HEADER_DATA_ROLE BIT(5)
#define PD_HEADER_TYPE_SHIFT 0
#define PD_HEADER_TYPE_MASK 0xf
#define PD_HEADER(type, pwr, data, id, cnt) \
((((type) & PD_HEADER_TYPE_MASK) << PD_HEADER_TYPE_SHIFT) | \
((pwr) == TYPEC_SOURCE ? PD_HEADER_PWR_ROLE : 0) | \
((data) == TYPEC_HOST ? PD_HEADER_DATA_ROLE : 0) | \
(PD_REV20 << PD_HEADER_REV_SHIFT) | \
(((id) & PD_HEADER_ID_MASK) << PD_HEADER_ID_SHIFT) | \
(((cnt) & PD_HEADER_CNT_MASK) << PD_HEADER_CNT_SHIFT))
#define PD_HEADER_LE(type, pwr, data, id, cnt) \
cpu_to_le16(PD_HEADER((type), (pwr), (data), (id), (cnt)))
static inline unsigned int pd_header_cnt(u16 header)
{
return (header >> PD_HEADER_CNT_SHIFT) & PD_HEADER_CNT_MASK;
}
static inline unsigned int pd_header_cnt_le(__le16 header)
{
return pd_header_cnt(le16_to_cpu(header));
}
static inline unsigned int pd_header_type(u16 header)
{
return (header >> PD_HEADER_TYPE_SHIFT) & PD_HEADER_TYPE_MASK;
}
static inline unsigned int pd_header_type_le(__le16 header)
{
return pd_header_type(le16_to_cpu(header));
}
#define PD_MAX_PAYLOAD 7
struct pd_message {
__le16 header;
__le32 payload[PD_MAX_PAYLOAD];
} __packed;
/* PDO: Power Data Object */
#define PDO_MAX_OBJECTS 7
enum pd_pdo_type {
PDO_TYPE_FIXED = 0,
PDO_TYPE_BATT = 1,
PDO_TYPE_VAR = 2,
};
#define PDO_TYPE_SHIFT 30
#define PDO_TYPE_MASK 0x3
#define PDO_TYPE(t) ((t) << PDO_TYPE_SHIFT)
#define PDO_VOLT_MASK 0x3ff
#define PDO_CURR_MASK 0x3ff
#define PDO_PWR_MASK 0x3ff
#define PDO_FIXED_DUAL_ROLE BIT(29) /* Power role swap supported */
#define PDO_FIXED_SUSPEND BIT(28) /* USB Suspend supported (Source) */
#define PDO_FIXED_HIGHER_CAP BIT(28) /* Requires more than vSafe5V (Sink) */
#define PDO_FIXED_EXTPOWER BIT(27) /* Externally powered */
#define PDO_FIXED_USB_COMM BIT(26) /* USB communications capable */
#define PDO_FIXED_DATA_SWAP BIT(25) /* Data role swap supported */
#define PDO_FIXED_VOLT_SHIFT 10 /* 50mV units */
#define PDO_FIXED_CURR_SHIFT 0 /* 10mA units */
#define PDO_FIXED_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_FIXED_VOLT_SHIFT)
#define PDO_FIXED_CURR(ma) ((((ma) / 10) & PDO_CURR_MASK) << PDO_FIXED_CURR_SHIFT)
#define PDO_FIXED(mv, ma, flags) \
(PDO_TYPE(PDO_TYPE_FIXED) | (flags) | \
PDO_FIXED_VOLT(mv) | PDO_FIXED_CURR(ma))
#define PDO_BATT_MAX_VOLT_SHIFT 20 /* 50mV units */
#define PDO_BATT_MIN_VOLT_SHIFT 10 /* 50mV units */
#define PDO_BATT_MAX_PWR_SHIFT 0 /* 250mW units */
#define PDO_BATT_MIN_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_BATT_MIN_VOLT_SHIFT)
#define PDO_BATT_MAX_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_BATT_MAX_VOLT_SHIFT)
#define PDO_BATT_MAX_POWER(mw) ((((mw) / 250) & PDO_PWR_MASK) << PDO_BATT_MAX_PWR_SHIFT)
#define PDO_BATT(min_mv, max_mv, max_mw) \
(PDO_TYPE(PDO_TYPE_BATT) | PDO_BATT_MIN_VOLT(min_mv) | \
PDO_BATT_MAX_VOLT(max_mv) | PDO_BATT_MAX_POWER(max_mw))
#define PDO_VAR_MAX_VOLT_SHIFT 20 /* 50mV units */
#define PDO_VAR_MIN_VOLT_SHIFT 10 /* 50mV units */
#define PDO_VAR_MAX_CURR_SHIFT 0 /* 10mA units */
#define PDO_VAR_MIN_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_VAR_MIN_VOLT_SHIFT)
#define PDO_VAR_MAX_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_VAR_MAX_VOLT_SHIFT)
#define PDO_VAR_MAX_CURR(ma) ((((ma) / 10) & PDO_CURR_MASK) << PDO_VAR_MAX_CURR_SHIFT)
#define PDO_VAR(min_mv, max_mv, max_ma) \
(PDO_TYPE(PDO_TYPE_VAR) | PDO_VAR_MIN_VOLT(min_mv) | \
PDO_VAR_MAX_VOLT(max_mv) | PDO_VAR_MAX_CURR(max_ma))
static inline enum pd_pdo_type pdo_type(u32 pdo)
{
return (pdo >> PDO_TYPE_SHIFT) & PDO_TYPE_MASK;
}
static inline unsigned int pdo_fixed_voltage(u32 pdo)
{
return ((pdo >> PDO_FIXED_VOLT_SHIFT) & PDO_VOLT_MASK) * 50;
}
static inline unsigned int pdo_min_voltage(u32 pdo)
{
return ((pdo >> PDO_VAR_MIN_VOLT_SHIFT) & PDO_VOLT_MASK) * 50;
}
static inline unsigned int pdo_max_voltage(u32 pdo)
{
return ((pdo >> PDO_VAR_MAX_VOLT_SHIFT) & PDO_VOLT_MASK) * 50;
}
static inline unsigned int pdo_max_current(u32 pdo)
{
return ((pdo >> PDO_VAR_MAX_CURR_SHIFT) & PDO_CURR_MASK) * 10;
}
static inline unsigned int pdo_max_power(u32 pdo)
{
return ((pdo >> PDO_BATT_MAX_PWR_SHIFT) & PDO_PWR_MASK) * 250;
}
/* RDO: Request Data Object */
#define RDO_OBJ_POS_SHIFT 28
#define RDO_OBJ_POS_MASK 0x7
#define RDO_GIVE_BACK BIT(27) /* Supports reduced operating current */
#define RDO_CAP_MISMATCH BIT(26) /* Not satisfied by source caps */
#define RDO_USB_COMM BIT(25) /* USB communications capable */
#define RDO_NO_SUSPEND BIT(24) /* USB Suspend not supported */
#define RDO_PWR_MASK 0x3ff
#define RDO_CURR_MASK 0x3ff
#define RDO_FIXED_OP_CURR_SHIFT 10
#define RDO_FIXED_MAX_CURR_SHIFT 0
#define RDO_OBJ(idx) (((idx) & RDO_OBJ_POS_MASK) << RDO_OBJ_POS_SHIFT)
#define PDO_FIXED_OP_CURR(ma) ((((ma) / 10) & RDO_CURR_MASK) << RDO_FIXED_OP_CURR_SHIFT)
#define PDO_FIXED_MAX_CURR(ma) ((((ma) / 10) & RDO_CURR_MASK) << RDO_FIXED_MAX_CURR_SHIFT)
#define RDO_FIXED(idx, op_ma, max_ma, flags) \
(RDO_OBJ(idx) | (flags) | \
PDO_FIXED_OP_CURR(op_ma) | PDO_FIXED_MAX_CURR(max_ma))
#define RDO_BATT_OP_PWR_SHIFT 10 /* 250mW units */
#define RDO_BATT_MAX_PWR_SHIFT 0 /* 250mW units */
#define RDO_BATT_OP_PWR(mw) ((((mw) / 250) & RDO_PWR_MASK) << RDO_BATT_OP_PWR_SHIFT)
#define RDO_BATT_MAX_PWR(mw) ((((mw) / 250) & RDO_PWR_MASK) << RDO_BATT_MAX_PWR_SHIFT)
#define RDO_BATT(idx, op_mw, max_mw, flags) \
(RDO_OBJ(idx) | (flags) | \
RDO_BATT_OP_PWR(op_mw) | RDO_BATT_MAX_PWR(max_mw))
static inline unsigned int rdo_index(u32 rdo)
{
return (rdo >> RDO_OBJ_POS_SHIFT) & RDO_OBJ_POS_MASK;
}
static inline unsigned int rdo_op_current(u32 rdo)
{
return ((rdo >> RDO_FIXED_OP_CURR_SHIFT) & RDO_CURR_MASK) * 10;
}
static inline unsigned int rdo_max_current(u32 rdo)
{
return ((rdo >> RDO_FIXED_MAX_CURR_SHIFT) &
RDO_CURR_MASK) * 10;
}
static inline unsigned int rdo_op_power(u32 rdo)
{
return ((rdo >> RDO_BATT_OP_PWR_SHIFT) & RDO_PWR_MASK) * 250;
}
static inline unsigned int rdo_max_power(u32 rdo)
{
return ((rdo >> RDO_BATT_MAX_PWR_SHIFT) & RDO_PWR_MASK) * 250;
}
/* USB PD timers and counters */
#define PD_T_NO_RESPONSE 5000 /* 4.5 - 5.5 seconds */
#define PD_T_DB_DETECT 10000 /* 10 - 15 seconds */
#define PD_T_SEND_SOURCE_CAP 150 /* 100 - 200 ms */
#define PD_T_SENDER_RESPONSE 60 /* 24 - 30 ms, relaxed */
#define PD_T_SOURCE_ACTIVITY 45
#define PD_T_SINK_ACTIVITY 135
#define PD_T_SINK_WAIT_CAP 240
#define PD_T_PS_TRANSITION 500
#define PD_T_SRC_TRANSITION 35
#define PD_T_DRP_SNK 40
#define PD_T_DRP_SRC 30
#define PD_T_PS_SOURCE_OFF 920
#define PD_T_PS_SOURCE_ON 480
#define PD_T_PS_HARD_RESET 30
#define PD_T_SRC_RECOVER 760
#define PD_T_SRC_RECOVER_MAX 1000
#define PD_T_SRC_TURN_ON 275
#define PD_T_SAFE_0V 650
#define PD_T_VCONN_SOURCE_ON 100
#define PD_T_SINK_REQUEST 100 /* 100 ms minimum */
#define PD_T_ERROR_RECOVERY 100 /* minimum 25 is insufficient */
#define PD_T_DRP_TRY 100 /* 75 - 150 ms */
#define PD_T_DRP_TRYWAIT 600 /* 400 - 800 ms */
#define PD_T_CC_DEBOUNCE 200 /* 100 - 200 ms */
#define PD_T_PD_DEBOUNCE 20 /* 10 - 20 ms */
#define PD_N_CAPS_COUNT (PD_T_NO_RESPONSE / PD_T_SEND_SOURCE_CAP)
#define PD_N_HARD_RESET_COUNT 2
#endif /* __LINUX_USB_PD_H */

View File

@ -0,0 +1,31 @@
/*
* Copyright 2015-2017 Google, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __LINUX_USB_PD_BDO_H
#define __LINUX_USB_PD_BDO_H
/* BDO : BIST Data Object */
#define BDO_MODE_RECV (0 << 28)
#define BDO_MODE_TRANSMIT (1 << 28)
#define BDO_MODE_COUNTERS (2 << 28)
#define BDO_MODE_CARRIER0 (3 << 28)
#define BDO_MODE_CARRIER1 (4 << 28)
#define BDO_MODE_CARRIER2 (5 << 28)
#define BDO_MODE_CARRIER3 (6 << 28)
#define BDO_MODE_EYE (7 << 28)
#define BDO_MODE_TESTDATA (8 << 28)
#define BDO_MODE_MASK(mode) ((mode) & 0xf0000000)
#endif

View File

@ -0,0 +1,249 @@
/*
* Copyright 2015-2017 Google, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __LINUX_USB_PD_VDO_H
#define __LINUX_USB_PD_VDO_H
#include "pd.h"
/*
* VDO : Vendor Defined Message Object
* VDM object is minimum of VDM header + 6 additional data objects.
*/
/*
* VDM header
* ----------
* <31:16> :: SVID
* <15> :: VDM type ( 1b == structured, 0b == unstructured )
* <14:13> :: Structured VDM version (can only be 00 == 1.0 currently)
* <12:11> :: reserved
* <10:8> :: object position (1-7 valid ... used for enter/exit mode only)
* <7:6> :: command type (SVDM only?)
* <5> :: reserved (SVDM), command type (UVDM)
* <4:0> :: command
*/
#define VDO_MAX_SIZE 7
#define VDO(vid, type, custom) \
(((vid) << 16) | \
((type) << 15) | \
((custom) & 0x7FFF))
#define VDO_SVDM_TYPE (1 << 15)
#define VDO_SVDM_VERS(x) ((x) << 13)
#define VDO_OPOS(x) ((x) << 8)
#define VDO_CMDT(x) ((x) << 6)
#define VDO_OPOS_MASK VDO_OPOS(0x7)
#define VDO_CMDT_MASK VDO_CMDT(0x3)
#define CMDT_INIT 0
#define CMDT_RSP_ACK 1
#define CMDT_RSP_NAK 2
#define CMDT_RSP_BUSY 3
/* reserved for SVDM ... for Google UVDM */
#define VDO_SRC_INITIATOR (0 << 5)
#define VDO_SRC_RESPONDER (1 << 5)
#define CMD_DISCOVER_IDENT 1
#define CMD_DISCOVER_SVID 2
#define CMD_DISCOVER_MODES 3
#define CMD_ENTER_MODE 4
#define CMD_EXIT_MODE 5
#define CMD_ATTENTION 6
#define VDO_CMD_VENDOR(x) (((10 + (x)) & 0x1f))
/* ChromeOS specific commands */
#define VDO_CMD_VERSION VDO_CMD_VENDOR(0)
#define VDO_CMD_SEND_INFO VDO_CMD_VENDOR(1)
#define VDO_CMD_READ_INFO VDO_CMD_VENDOR(2)
#define VDO_CMD_REBOOT VDO_CMD_VENDOR(5)
#define VDO_CMD_FLASH_ERASE VDO_CMD_VENDOR(6)
#define VDO_CMD_FLASH_WRITE VDO_CMD_VENDOR(7)
#define VDO_CMD_ERASE_SIG VDO_CMD_VENDOR(8)
#define VDO_CMD_PING_ENABLE VDO_CMD_VENDOR(10)
#define VDO_CMD_CURRENT VDO_CMD_VENDOR(11)
#define VDO_CMD_FLIP VDO_CMD_VENDOR(12)
#define VDO_CMD_GET_LOG VDO_CMD_VENDOR(13)
#define VDO_CMD_CCD_EN VDO_CMD_VENDOR(14)
#define PD_VDO_VID(vdo) ((vdo) >> 16)
#define PD_VDO_SVDM(vdo) (((vdo) >> 15) & 1)
#define PD_VDO_OPOS(vdo) (((vdo) >> 8) & 0x7)
#define PD_VDO_CMD(vdo) ((vdo) & 0x1f)
#define PD_VDO_CMDT(vdo) (((vdo) >> 6) & 0x3)
/*
* SVDM Identity request -> response
*
* Request is simply properly formatted SVDM header
*
* Response is 4 data objects:
* [0] :: SVDM header
* [1] :: Identitiy header
* [2] :: Cert Stat VDO
* [3] :: (Product | Cable) VDO
* [4] :: AMA VDO
*
*/
#define VDO_INDEX_HDR 0
#define VDO_INDEX_IDH 1
#define VDO_INDEX_CSTAT 2
#define VDO_INDEX_CABLE 3
#define VDO_INDEX_PRODUCT 3
#define VDO_INDEX_AMA 4
/*
* SVDM Identity Header
* --------------------
* <31> :: data capable as a USB host
* <30> :: data capable as a USB device
* <29:27> :: product type
* <26> :: modal operation supported (1b == yes)
* <25:16> :: Reserved, Shall be set to zero
* <15:0> :: USB-IF assigned VID for this cable vendor
*/
#define IDH_PTYPE_UNDEF 0
#define IDH_PTYPE_HUB 1
#define IDH_PTYPE_PERIPH 2
#define IDH_PTYPE_PCABLE 3
#define IDH_PTYPE_ACABLE 4
#define IDH_PTYPE_AMA 5
#define VDO_IDH(usbh, usbd, ptype, is_modal, vid) \
((usbh) << 31 | (usbd) << 30 | ((ptype) & 0x7) << 27 \
| (is_modal) << 26 | ((vid) & 0xffff))
#define PD_IDH_PTYPE(vdo) (((vdo) >> 27) & 0x7)
#define PD_IDH_VID(vdo) ((vdo) & 0xffff)
#define PD_IDH_MODAL_SUPP(vdo) ((vdo) & (1 << 26))
/*
* Cert Stat VDO
* -------------
* <31:0> : USB-IF assigned XID for this cable
*/
#define PD_CSTAT_XID(vdo) (vdo)
/*
* Product VDO
* -----------
* <31:16> : USB Product ID
* <15:0> : USB bcdDevice
*/
#define VDO_PRODUCT(pid, bcd) (((pid) & 0xffff) << 16 | ((bcd) & 0xffff))
#define PD_PRODUCT_PID(vdo) (((vdo) >> 16) & 0xffff)
/*
* Cable VDO
* ---------
* <31:28> :: Cable HW version
* <27:24> :: Cable FW version
* <23:20> :: Reserved, Shall be set to zero
* <19:18> :: type-C to Type-A/B/C (00b == A, 01 == B, 10 == C)
* <17> :: Type-C to Plug/Receptacle (0b == plug, 1b == receptacle)
* <16:13> :: cable latency (0001 == <10ns(~1m length))
* <12:11> :: cable termination type (11b == both ends active VCONN req)
* <10> :: SSTX1 Directionality support (0b == fixed, 1b == cfgable)
* <9> :: SSTX2 Directionality support
* <8> :: SSRX1 Directionality support
* <7> :: SSRX2 Directionality support
* <6:5> :: Vbus current handling capability
* <4> :: Vbus through cable (0b == no, 1b == yes)
* <3> :: SOP" controller present? (0b == no, 1b == yes)
* <2:0> :: USB SS Signaling support
*/
#define CABLE_ATYPE 0
#define CABLE_BTYPE 1
#define CABLE_CTYPE 2
#define CABLE_PLUG 0
#define CABLE_RECEPTACLE 1
#define CABLE_CURR_1A5 0
#define CABLE_CURR_3A 1
#define CABLE_CURR_5A 2
#define CABLE_USBSS_U2_ONLY 0
#define CABLE_USBSS_U31_GEN1 1
#define CABLE_USBSS_U31_GEN2 2
#define VDO_CABLE(hw, fw, cbl, gdr, lat, term, tx1d, tx2d, rx1d, rx2d, cur,\
vps, sopp, usbss) \
(((hw) & 0x7) << 28 | ((fw) & 0x7) << 24 | ((cbl) & 0x3) << 18 \
| (gdr) << 17 | ((lat) & 0x7) << 13 | ((term) & 0x3) << 11 \
| (tx1d) << 10 | (tx2d) << 9 | (rx1d) << 8 | (rx2d) << 7 \
| ((cur) & 0x3) << 5 | (vps) << 4 | (sopp) << 3 \
| ((usbss) & 0x7))
/*
* AMA VDO
* ---------
* <31:28> :: Cable HW version
* <27:24> :: Cable FW version
* <23:12> :: Reserved, Shall be set to zero
* <11> :: SSTX1 Directionality support (0b == fixed, 1b == cfgable)
* <10> :: SSTX2 Directionality support
* <9> :: SSRX1 Directionality support
* <8> :: SSRX2 Directionality support
* <7:5> :: Vconn power
* <4> :: Vconn power required
* <3> :: Vbus power required
* <2:0> :: USB SS Signaling support
*/
#define VDO_AMA(hw, fw, tx1d, tx2d, rx1d, rx2d, vcpwr, vcr, vbr, usbss) \
(((hw) & 0x7) << 28 | ((fw) & 0x7) << 24 \
| (tx1d) << 11 | (tx2d) << 10 | (rx1d) << 9 | (rx2d) << 8 \
| ((vcpwr) & 0x7) << 5 | (vcr) << 4 | (vbr) << 3 \
| ((usbss) & 0x7))
#define PD_VDO_AMA_VCONN_REQ(vdo) (((vdo) >> 4) & 1)
#define PD_VDO_AMA_VBUS_REQ(vdo) (((vdo) >> 3) & 1)
#define AMA_VCONN_PWR_1W 0
#define AMA_VCONN_PWR_1W5 1
#define AMA_VCONN_PWR_2W 2
#define AMA_VCONN_PWR_3W 3
#define AMA_VCONN_PWR_4W 4
#define AMA_VCONN_PWR_5W 5
#define AMA_VCONN_PWR_6W 6
#define AMA_USBSS_U2_ONLY 0
#define AMA_USBSS_U31_GEN1 1
#define AMA_USBSS_U31_GEN2 2
#define AMA_USBSS_BBONLY 3
/*
* SVDM Discover SVIDs request -> response
*
* Request is properly formatted VDM Header with discover SVIDs command.
* Response is a set of SVIDs of all all supported SVIDs with all zero's to
* mark the end of SVIDs. If more than 12 SVIDs are supported command SHOULD be
* repeated.
*/
#define VDO_SVID(svid0, svid1) (((svid0) & 0xffff) << 16 | ((svid1) & 0xffff))
#define PD_VDO_SVID_SVID0(vdo) ((vdo) >> 16)
#define PD_VDO_SVID_SVID1(vdo) ((vdo) & 0xffff)
/* USB-IF SIDs */
#define USB_SID_PD 0xff00 /* power delivery */
#define USB_SID_DISPLAYPORT 0xff01
#define USB_SID_MHL 0xff02 /* Mobile High-Definition Link */
/* VDM command timeouts (in ms) */
#define PD_T_VDM_UNSTRUCTURED 500
#define PD_T_VDM_BUSY 100
#define PD_T_VDM_WAIT_MODE_E 100
#define PD_T_VDM_SNDR_RSP 30
#define PD_T_VDM_E_MODE 25
#define PD_T_VDM_RCVR_RSP 15
#endif /* __LINUX_USB_PD_VDO_H */

View File

@ -0,0 +1,526 @@
/*
* Copyright 2015-2017 Google, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* USB Type-C Port Controller Interface.
*/
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/regmap.h>
#include <linux/usb/typec.h>
#include "pd.h"
#include "tcpci.h"
#include "tcpm.h"
#define PD_RETRY_COUNT 3
struct tcpci {
struct device *dev;
struct i2c_client *client;
struct tcpm_port *port;
struct regmap *regmap;
bool controls_vbus;
struct tcpc_dev tcpc;
};
static inline struct tcpci *tcpc_to_tcpci(struct tcpc_dev *tcpc)
{
return container_of(tcpc, struct tcpci, tcpc);
}
static int tcpci_read16(struct tcpci *tcpci, unsigned int reg,
unsigned int *val)
{
return regmap_raw_read(tcpci->regmap, reg, val, sizeof(u16));
}
static int tcpci_write16(struct tcpci *tcpci, unsigned int reg, u16 val)
{
return regmap_raw_write(tcpci->regmap, reg, &val, sizeof(u16));
}
static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc)
{
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
unsigned int reg;
int ret;
switch (cc) {
case TYPEC_CC_RA:
reg = (TCPC_ROLE_CTRL_CC_RA << TCPC_ROLE_CTRL_CC1_SHIFT) |
(TCPC_ROLE_CTRL_CC_RA << TCPC_ROLE_CTRL_CC2_SHIFT);
break;
case TYPEC_CC_RD:
reg = (TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT) |
(TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT);
break;
case TYPEC_CC_RP_DEF:
reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
(TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) |
(TCPC_ROLE_CTRL_RP_VAL_DEF <<
TCPC_ROLE_CTRL_RP_VAL_SHIFT);
break;
case TYPEC_CC_RP_1_5:
reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
(TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) |
(TCPC_ROLE_CTRL_RP_VAL_1_5 <<
TCPC_ROLE_CTRL_RP_VAL_SHIFT);
break;
case TYPEC_CC_RP_3_0:
reg = (TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT) |
(TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT) |
(TCPC_ROLE_CTRL_RP_VAL_3_0 <<
TCPC_ROLE_CTRL_RP_VAL_SHIFT);
break;
case TYPEC_CC_OPEN:
default:
reg = (TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT) |
(TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC2_SHIFT);
break;
}
ret = regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
if (ret < 0)
return ret;
return 0;
}
static int tcpci_start_drp_toggling(struct tcpc_dev *tcpc,
enum typec_cc_status cc)
{
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
unsigned int reg = TCPC_ROLE_CTRL_DRP;
switch (cc) {
default:
case TYPEC_CC_RP_DEF:
reg |= (TCPC_ROLE_CTRL_RP_VAL_DEF <<
TCPC_ROLE_CTRL_RP_VAL_SHIFT);
break;
case TYPEC_CC_RP_1_5:
reg |= (TCPC_ROLE_CTRL_RP_VAL_1_5 <<
TCPC_ROLE_CTRL_RP_VAL_SHIFT);
break;
case TYPEC_CC_RP_3_0:
reg |= (TCPC_ROLE_CTRL_RP_VAL_3_0 <<
TCPC_ROLE_CTRL_RP_VAL_SHIFT);
break;
}
return regmap_write(tcpci->regmap, TCPC_ROLE_CTRL, reg);
}
static enum typec_cc_status tcpci_to_typec_cc(unsigned int cc, bool sink)
{
switch (cc) {
case 0x1:
return sink ? TYPEC_CC_RP_DEF : TYPEC_CC_RA;
case 0x2:
return sink ? TYPEC_CC_RP_1_5 : TYPEC_CC_RD;
case 0x3:
if (sink)
return TYPEC_CC_RP_3_0;
case 0x0:
default:
return TYPEC_CC_OPEN;
}
}
static int tcpci_get_cc(struct tcpc_dev *tcpc,
enum typec_cc_status *cc1, enum typec_cc_status *cc2)
{
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
unsigned int reg;
int ret;
ret = regmap_read(tcpci->regmap, TCPC_CC_STATUS, &reg);
if (ret < 0)
return ret;
*cc1 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC1_SHIFT) &
TCPC_CC_STATUS_CC1_MASK,
reg & TCPC_CC_STATUS_TERM);
*cc2 = tcpci_to_typec_cc((reg >> TCPC_CC_STATUS_CC2_SHIFT) &
TCPC_CC_STATUS_CC2_MASK,
reg & TCPC_CC_STATUS_TERM);
return 0;
}
static int tcpci_set_polarity(struct tcpc_dev *tcpc,
enum typec_cc_polarity polarity)
{
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
int ret;
ret = regmap_write(tcpci->regmap, TCPC_TCPC_CTRL,
(polarity == TYPEC_POLARITY_CC2) ?
TCPC_TCPC_CTRL_ORIENTATION : 0);
if (ret < 0)
return ret;
return 0;
}
static int tcpci_set_vconn(struct tcpc_dev *tcpc, bool enable)
{
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
int ret;
ret = regmap_write(tcpci->regmap, TCPC_POWER_CTRL,
enable ? TCPC_POWER_CTRL_VCONN_ENABLE : 0);
if (ret < 0)
return ret;
return 0;
}
static int tcpci_set_roles(struct tcpc_dev *tcpc, bool attached,
enum typec_role role, enum typec_data_role data)
{
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
unsigned int reg;
int ret;
reg = PD_REV20 << TCPC_MSG_HDR_INFO_REV_SHIFT;
if (role == TYPEC_SOURCE)
reg |= TCPC_MSG_HDR_INFO_PWR_ROLE;
if (data == TYPEC_HOST)
reg |= TCPC_MSG_HDR_INFO_DATA_ROLE;
ret = regmap_write(tcpci->regmap, TCPC_MSG_HDR_INFO, reg);
if (ret < 0)
return ret;
return 0;
}
static int tcpci_set_pd_rx(struct tcpc_dev *tcpc, bool enable)
{
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
unsigned int reg = 0;
int ret;
if (enable)
reg = TCPC_RX_DETECT_SOP | TCPC_RX_DETECT_HARD_RESET;
ret = regmap_write(tcpci->regmap, TCPC_RX_DETECT, reg);
if (ret < 0)
return ret;
return 0;
}
static int tcpci_get_vbus(struct tcpc_dev *tcpc)
{
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
unsigned int reg;
int ret;
ret = regmap_read(tcpci->regmap, TCPC_POWER_STATUS, &reg);
if (ret < 0)
return ret;
return !!(reg & TCPC_POWER_STATUS_VBUS_PRES);
}
static int tcpci_set_vbus(struct tcpc_dev *tcpc, bool source, bool sink)
{
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
int ret;
/* Disable both source and sink first before enabling anything */
if (!source) {
ret = regmap_write(tcpci->regmap, TCPC_COMMAND,
TCPC_CMD_DISABLE_SRC_VBUS);
if (ret < 0)
return ret;
}
if (!sink) {
ret = regmap_write(tcpci->regmap, TCPC_COMMAND,
TCPC_CMD_DISABLE_SINK_VBUS);
if (ret < 0)
return ret;
}
if (source) {
ret = regmap_write(tcpci->regmap, TCPC_COMMAND,
TCPC_CMD_SRC_VBUS_DEFAULT);
if (ret < 0)
return ret;
}
if (sink) {
ret = regmap_write(tcpci->regmap, TCPC_COMMAND,
TCPC_CMD_SINK_VBUS);
if (ret < 0)
return ret;
}
return 0;
}
static int tcpci_pd_transmit(struct tcpc_dev *tcpc,
enum tcpm_transmit_type type,
const struct pd_message *msg)
{
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
unsigned int reg, cnt, header;
int ret;
cnt = msg ? pd_header_cnt(msg->header) * 4 : 0;
ret = regmap_write(tcpci->regmap, TCPC_TX_BYTE_CNT, cnt + 2);
if (ret < 0)
return ret;
header = msg ? msg->header : 0;
ret = tcpci_write16(tcpci, TCPC_TX_HDR, header);
if (ret < 0)
return ret;
if (cnt > 0) {
ret = regmap_raw_write(tcpci->regmap, TCPC_TX_DATA,
&msg->payload, cnt);
if (ret < 0)
return ret;
}
reg = (PD_RETRY_COUNT << TCPC_TRANSMIT_RETRY_SHIFT) |
(type << TCPC_TRANSMIT_TYPE_SHIFT);
ret = regmap_write(tcpci->regmap, TCPC_TRANSMIT, reg);
if (ret < 0)
return ret;
return 0;
}
static int tcpci_init(struct tcpc_dev *tcpc)
{
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
unsigned long timeout = jiffies + msecs_to_jiffies(2000); /* XXX */
unsigned int reg;
int ret;
while (time_before_eq(jiffies, timeout)) {
ret = regmap_read(tcpci->regmap, TCPC_POWER_STATUS, &reg);
if (ret < 0)
return ret;
if (!(reg & TCPC_POWER_STATUS_UNINIT))
break;
usleep_range(10000, 20000);
}
if (time_after(jiffies, timeout))
return -ETIMEDOUT;
/* Clear all events */
ret = tcpci_write16(tcpci, TCPC_ALERT, 0xffff);
if (ret < 0)
return ret;
if (tcpci->controls_vbus)
reg = TCPC_POWER_STATUS_VBUS_PRES;
else
reg = 0;
ret = regmap_write(tcpci->regmap, TCPC_POWER_STATUS_MASK, reg);
if (ret < 0)
return ret;
reg = TCPC_ALERT_TX_SUCCESS | TCPC_ALERT_TX_FAILED |
TCPC_ALERT_TX_DISCARDED | TCPC_ALERT_RX_STATUS |
TCPC_ALERT_RX_HARD_RST | TCPC_ALERT_CC_STATUS;
if (tcpci->controls_vbus)
reg |= TCPC_ALERT_POWER_STATUS;
return tcpci_write16(tcpci, TCPC_ALERT_MASK, reg);
}
static irqreturn_t tcpci_irq(int irq, void *dev_id)
{
struct tcpci *tcpci = dev_id;
unsigned int status, reg;
tcpci_read16(tcpci, TCPC_ALERT, &status);
/*
* Clear alert status for everything except RX_STATUS, which shouldn't
* be cleared until we have successfully retrieved message.
*/
if (status & ~TCPC_ALERT_RX_STATUS)
tcpci_write16(tcpci, TCPC_ALERT,
status & ~TCPC_ALERT_RX_STATUS);
if (status & TCPC_ALERT_CC_STATUS)
tcpm_cc_change(tcpci->port);
if (status & TCPC_ALERT_POWER_STATUS) {
regmap_read(tcpci->regmap, TCPC_POWER_STATUS_MASK, &reg);
/*
* If power status mask has been reset, then the TCPC
* has reset.
*/
if (reg == 0xff)
tcpm_tcpc_reset(tcpci->port);
else
tcpm_vbus_change(tcpci->port);
}
if (status & TCPC_ALERT_RX_STATUS) {
struct pd_message msg;
unsigned int cnt;
regmap_read(tcpci->regmap, TCPC_RX_BYTE_CNT, &cnt);
tcpci_read16(tcpci, TCPC_RX_HDR, &reg);
msg.header = reg;
if (WARN_ON(cnt > sizeof(msg.payload)))
cnt = sizeof(msg.payload);
if (cnt > 0)
regmap_raw_read(tcpci->regmap, TCPC_RX_DATA,
&msg.payload, cnt);
/* Read complete, clear RX status alert bit */
tcpci_write16(tcpci, TCPC_ALERT, TCPC_ALERT_RX_STATUS);
tcpm_pd_receive(tcpci->port, &msg);
}
if (status & TCPC_ALERT_RX_HARD_RST)
tcpm_pd_hard_reset(tcpci->port);
if (status & TCPC_ALERT_TX_SUCCESS)
tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_SUCCESS);
else if (status & TCPC_ALERT_TX_DISCARDED)
tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_DISCARDED);
else if (status & TCPC_ALERT_TX_FAILED)
tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_FAILED);
return IRQ_HANDLED;
}
static const struct regmap_config tcpci_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x7F, /* 0x80 .. 0xFF are vendor defined */
};
const struct tcpc_config tcpci_tcpc_config = {
.type = TYPEC_PORT_DFP,
.default_role = TYPEC_SINK,
};
static int tcpci_parse_config(struct tcpci *tcpci)
{
tcpci->controls_vbus = true; /* XXX */
/* TODO: Populate struct tcpc_config from ACPI/device-tree */
tcpci->tcpc.config = &tcpci_tcpc_config;
return 0;
}
static int tcpci_probe(struct i2c_client *client,
const struct i2c_device_id *i2c_id)
{
struct tcpci *tcpci;
int err;
tcpci = devm_kzalloc(&client->dev, sizeof(*tcpci), GFP_KERNEL);
if (!tcpci)
return -ENOMEM;
tcpci->client = client;
tcpci->dev = &client->dev;
i2c_set_clientdata(client, tcpci);
tcpci->regmap = devm_regmap_init_i2c(client, &tcpci_regmap_config);
if (IS_ERR(tcpci->regmap))
return PTR_ERR(tcpci->regmap);
tcpci->tcpc.init = tcpci_init;
tcpci->tcpc.get_vbus = tcpci_get_vbus;
tcpci->tcpc.set_vbus = tcpci_set_vbus;
tcpci->tcpc.set_cc = tcpci_set_cc;
tcpci->tcpc.get_cc = tcpci_get_cc;
tcpci->tcpc.set_polarity = tcpci_set_polarity;
tcpci->tcpc.set_vconn = tcpci_set_vconn;
tcpci->tcpc.start_drp_toggling = tcpci_start_drp_toggling;
tcpci->tcpc.set_pd_rx = tcpci_set_pd_rx;
tcpci->tcpc.set_roles = tcpci_set_roles;
tcpci->tcpc.pd_transmit = tcpci_pd_transmit;
err = tcpci_parse_config(tcpci);
if (err < 0)
return err;
/* Disable chip interrupts */
tcpci_write16(tcpci, TCPC_ALERT_MASK, 0);
err = devm_request_threaded_irq(tcpci->dev, client->irq, NULL,
tcpci_irq,
IRQF_ONESHOT | IRQF_TRIGGER_LOW,
dev_name(tcpci->dev), tcpci);
if (err < 0)
return err;
tcpci->port = tcpm_register_port(tcpci->dev, &tcpci->tcpc);
return PTR_ERR_OR_ZERO(tcpci->port);
}
static int tcpci_remove(struct i2c_client *client)
{
struct tcpci *tcpci = i2c_get_clientdata(client);
tcpm_unregister_port(tcpci->port);
return 0;
}
static const struct i2c_device_id tcpci_id[] = {
{ "tcpci", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, tcpci_id);
#ifdef CONFIG_OF
static const struct of_device_id tcpci_of_match[] = {
{ .compatible = "usb,tcpci", },
{},
};
MODULE_DEVICE_TABLE(of, tcpci_of_match);
#endif
static struct i2c_driver tcpci_i2c_driver = {
.driver = {
.name = "tcpci",
.of_match_table = of_match_ptr(tcpci_of_match),
},
.probe = tcpci_probe,
.remove = tcpci_remove,
.id_table = tcpci_id,
};
module_i2c_driver(tcpci_i2c_driver);
MODULE_DESCRIPTION("USB Type-C Port Controller Interface driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,133 @@
/*
* Copyright 2015-2017 Google, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* USB Type-C Port Controller Interface.
*/
#ifndef __LINUX_USB_TCPCI_H
#define __LINUX_USB_TCPCI_H
#define TCPC_VENDOR_ID 0x0
#define TCPC_PRODUCT_ID 0x2
#define TCPC_BCD_DEV 0x4
#define TCPC_TC_REV 0x6
#define TCPC_PD_REV 0x8
#define TCPC_PD_INT_REV 0xa
#define TCPC_ALERT 0x10
#define TCPC_ALERT_VBUS_DISCNCT BIT(11)
#define TCPC_ALERT_RX_BUF_OVF BIT(10)
#define TCPC_ALERT_FAULT BIT(9)
#define TCPC_ALERT_V_ALARM_LO BIT(8)
#define TCPC_ALERT_V_ALARM_HI BIT(7)
#define TCPC_ALERT_TX_SUCCESS BIT(6)
#define TCPC_ALERT_TX_DISCARDED BIT(5)
#define TCPC_ALERT_TX_FAILED BIT(4)
#define TCPC_ALERT_RX_HARD_RST BIT(3)
#define TCPC_ALERT_RX_STATUS BIT(2)
#define TCPC_ALERT_POWER_STATUS BIT(1)
#define TCPC_ALERT_CC_STATUS BIT(0)
#define TCPC_ALERT_MASK 0x12
#define TCPC_POWER_STATUS_MASK 0x14
#define TCPC_FAULT_STATUS_MASK 0x15
#define TCPC_CONFIG_STD_OUTPUT 0x18
#define TCPC_TCPC_CTRL 0x19
#define TCPC_TCPC_CTRL_ORIENTATION BIT(0)
#define TCPC_ROLE_CTRL 0x1a
#define TCPC_ROLE_CTRL_DRP BIT(6)
#define TCPC_ROLE_CTRL_RP_VAL_SHIFT 4
#define TCPC_ROLE_CTRL_RP_VAL_MASK 0x3
#define TCPC_ROLE_CTRL_RP_VAL_DEF 0x0
#define TCPC_ROLE_CTRL_RP_VAL_1_5 0x1
#define TCPC_ROLE_CTRL_RP_VAL_3_0 0x2
#define TCPC_ROLE_CTRL_CC2_SHIFT 2
#define TCPC_ROLE_CTRL_CC2_MASK 0x3
#define TCPC_ROLE_CTRL_CC1_SHIFT 0
#define TCPC_ROLE_CTRL_CC1_MASK 0x3
#define TCPC_ROLE_CTRL_CC_RA 0x0
#define TCPC_ROLE_CTRL_CC_RP 0x1
#define TCPC_ROLE_CTRL_CC_RD 0x2
#define TCPC_ROLE_CTRL_CC_OPEN 0x3
#define TCPC_FAULT_CTRL 0x1b
#define TCPC_POWER_CTRL 0x1c
#define TCPC_POWER_CTRL_VCONN_ENABLE BIT(0)
#define TCPC_CC_STATUS 0x1d
#define TCPC_CC_STATUS_TERM BIT(4)
#define TCPC_CC_STATUS_CC2_SHIFT 2
#define TCPC_CC_STATUS_CC2_MASK 0x3
#define TCPC_CC_STATUS_CC1_SHIFT 0
#define TCPC_CC_STATUS_CC1_MASK 0x3
#define TCPC_POWER_STATUS 0x1e
#define TCPC_POWER_STATUS_UNINIT BIT(6)
#define TCPC_POWER_STATUS_VBUS_DET BIT(3)
#define TCPC_POWER_STATUS_VBUS_PRES BIT(2)
#define TCPC_FAULT_STATUS 0x1f
#define TCPC_COMMAND 0x23
#define TCPC_CMD_WAKE_I2C 0x11
#define TCPC_CMD_DISABLE_VBUS_DETECT 0x22
#define TCPC_CMD_ENABLE_VBUS_DETECT 0x33
#define TCPC_CMD_DISABLE_SINK_VBUS 0x44
#define TCPC_CMD_SINK_VBUS 0x55
#define TCPC_CMD_DISABLE_SRC_VBUS 0x66
#define TCPC_CMD_SRC_VBUS_DEFAULT 0x77
#define TCPC_CMD_SRC_VBUS_HIGH 0x88
#define TCPC_CMD_LOOK4CONNECTION 0x99
#define TCPC_CMD_RXONEMORE 0xAA
#define TCPC_CMD_I2C_IDLE 0xFF
#define TCPC_DEV_CAP_1 0x24
#define TCPC_DEV_CAP_2 0x26
#define TCPC_STD_INPUT_CAP 0x28
#define TCPC_STD_OUTPUT_CAP 0x29
#define TCPC_MSG_HDR_INFO 0x2e
#define TCPC_MSG_HDR_INFO_DATA_ROLE BIT(3)
#define TCPC_MSG_HDR_INFO_PWR_ROLE BIT(0)
#define TCPC_MSG_HDR_INFO_REV_SHIFT 1
#define TCPC_MSG_HDR_INFO_REV_MASK 0x3
#define TCPC_RX_DETECT 0x2f
#define TCPC_RX_DETECT_HARD_RESET BIT(5)
#define TCPC_RX_DETECT_SOP BIT(0)
#define TCPC_RX_BYTE_CNT 0x30
#define TCPC_RX_BUF_FRAME_TYPE 0x31
#define TCPC_RX_HDR 0x32
#define TCPC_RX_DATA 0x34 /* through 0x4f */
#define TCPC_TRANSMIT 0x50
#define TCPC_TRANSMIT_RETRY_SHIFT 4
#define TCPC_TRANSMIT_RETRY_MASK 0x3
#define TCPC_TRANSMIT_TYPE_SHIFT 0
#define TCPC_TRANSMIT_TYPE_MASK 0x7
#define TCPC_TX_BYTE_CNT 0x51
#define TCPC_TX_HDR 0x52
#define TCPC_TX_DATA 0x54 /* through 0x6f */
#define TCPC_VBUS_VOLTAGE 0x70
#define TCPC_VBUS_SINK_DISCONNECT_THRESH 0x72
#define TCPC_VBUS_STOP_DISCHARGE_THRESH 0x74
#define TCPC_VBUS_VOLTAGE_ALARM_HI_CFG 0x76
#define TCPC_VBUS_VOLTAGE_ALARM_LO_CFG 0x78
#endif /* __LINUX_USB_TCPCI_H */

3465
drivers/staging/typec/tcpm.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,150 @@
/*
* Copyright 2015-2017 Google, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __LINUX_USB_TCPM_H
#define __LINUX_USB_TCPM_H
#include <linux/bitops.h>
#include <linux/usb/typec.h>
#include "pd.h"
enum typec_cc_status {
TYPEC_CC_OPEN,
TYPEC_CC_RA,
TYPEC_CC_RD,
TYPEC_CC_RP_DEF,
TYPEC_CC_RP_1_5,
TYPEC_CC_RP_3_0,
};
enum typec_cc_polarity {
TYPEC_POLARITY_CC1,
TYPEC_POLARITY_CC2,
};
/* Time to wait for TCPC to complete transmit */
#define PD_T_TCPC_TX_TIMEOUT 100
enum tcpm_transmit_status {
TCPC_TX_SUCCESS = 0,
TCPC_TX_DISCARDED = 1,
TCPC_TX_FAILED = 2,
};
enum tcpm_transmit_type {
TCPC_TX_SOP = 0,
TCPC_TX_SOP_PRIME = 1,
TCPC_TX_SOP_PRIME_PRIME = 2,
TCPC_TX_SOP_DEBUG_PRIME = 3,
TCPC_TX_SOP_DEBUG_PRIME_PRIME = 4,
TCPC_TX_HARD_RESET = 5,
TCPC_TX_CABLE_RESET = 6,
TCPC_TX_BIST_MODE_2 = 7
};
struct tcpc_config {
const u32 *src_pdo;
unsigned int nr_src_pdo;
const u32 *snk_pdo;
unsigned int nr_snk_pdo;
unsigned int max_snk_mv;
unsigned int max_snk_ma;
unsigned int max_snk_mw;
unsigned int operating_snk_mw;
enum typec_port_type type;
enum typec_role default_role;
bool try_role_hw; /* try.{src,snk} implemented in hardware */
struct typec_altmode_desc *alt_modes;
};
enum tcpc_usb_switch {
TCPC_USB_SWITCH_CONNECT,
TCPC_USB_SWITCH_DISCONNECT,
TCPC_USB_SWITCH_RESTORE, /* TODO FIXME */
};
/* Mux state attributes */
#define TCPC_MUX_USB_ENABLED BIT(0) /* USB enabled */
#define TCPC_MUX_DP_ENABLED BIT(1) /* DP enabled */
#define TCPC_MUX_POLARITY_INVERTED BIT(2) /* Polarity inverted */
/* Mux modes, decoded to attributes */
enum tcpc_mux_mode {
TYPEC_MUX_NONE = 0, /* Open switch */
TYPEC_MUX_USB = TCPC_MUX_USB_ENABLED, /* USB only */
TYPEC_MUX_DP = TCPC_MUX_DP_ENABLED, /* DP only */
TYPEC_MUX_DOCK = TCPC_MUX_USB_ENABLED | /* Both USB and DP */
TCPC_MUX_DP_ENABLED,
};
struct tcpc_mux_dev {
int (*set)(struct tcpc_mux_dev *dev, enum tcpc_mux_mode mux_mode,
enum tcpc_usb_switch usb_config,
enum typec_cc_polarity polarity);
bool dfp_only;
void *priv_data;
};
struct tcpc_dev {
const struct tcpc_config *config;
int (*init)(struct tcpc_dev *dev);
int (*get_vbus)(struct tcpc_dev *dev);
int (*set_cc)(struct tcpc_dev *dev, enum typec_cc_status cc);
int (*get_cc)(struct tcpc_dev *dev, enum typec_cc_status *cc1,
enum typec_cc_status *cc2);
int (*set_polarity)(struct tcpc_dev *dev,
enum typec_cc_polarity polarity);
int (*set_vconn)(struct tcpc_dev *dev, bool on);
int (*set_vbus)(struct tcpc_dev *dev, bool on, bool charge);
int (*set_current_limit)(struct tcpc_dev *dev, u32 max_ma, u32 mv);
int (*set_pd_rx)(struct tcpc_dev *dev, bool on);
int (*set_roles)(struct tcpc_dev *dev, bool attached,
enum typec_role role, enum typec_data_role data);
int (*start_drp_toggling)(struct tcpc_dev *dev,
enum typec_cc_status cc);
int (*try_role)(struct tcpc_dev *dev, int role);
int (*pd_transmit)(struct tcpc_dev *dev, enum tcpm_transmit_type type,
const struct pd_message *msg);
struct tcpc_mux_dev *mux;
};
struct tcpm_port;
struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc);
void tcpm_unregister_port(struct tcpm_port *port);
void tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo,
unsigned int nr_pdo);
void tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo,
unsigned int nr_pdo,
unsigned int max_snk_mv,
unsigned int max_snk_ma,
unsigned int max_snk_mw,
unsigned int operating_snk_mw);
void tcpm_vbus_change(struct tcpm_port *port);
void tcpm_cc_change(struct tcpm_port *port);
void tcpm_pd_receive(struct tcpm_port *port,
const struct pd_message *msg);
void tcpm_pd_transmit_complete(struct tcpm_port *port,
enum tcpm_transmit_status status);
void tcpm_pd_hard_reset(struct tcpm_port *port);
void tcpm_tcpc_reset(struct tcpm_port *port);
#endif /* __LINUX_USB_TCPM_H */

View File

@ -35,7 +35,6 @@ config USB_COMMON
config USB_ARCH_HAS_HCD
def_bool y
# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
config USB
tristate "Support for Host-side USB"
depends on USB_ARCH_HAS_HCD
@ -73,6 +72,17 @@ config USB
To compile this driver as a module, choose M here: the
module will be called usbcore.
config USB_PCI
bool "PCI based USB host interface"
depends on PCI
default y
---help---
A lot of embeded system SOC (e.g. freescale T2080) have both
PCI and USB modules. But USB module is controlled by registers
directly, it have no relationship with PCI module.
When say N here it will not build PCI related code in USB driver.
if USB
source "drivers/usb/core/Kconfig"
@ -152,6 +162,8 @@ source "drivers/usb/phy/Kconfig"
source "drivers/usb/gadget/Kconfig"
source "drivers/usb/typec/Kconfig"
config USB_LED_TRIG
bool "USB LED Triggers"
depends on LEDS_CLASS && LEDS_TRIGGERS

View File

@ -14,7 +14,7 @@ obj-$(CONFIG_USB_ISP1760) += isp1760/
obj-$(CONFIG_USB_MON) += mon/
obj-$(CONFIG_USB_MTU3) += mtu3/
obj-$(CONFIG_PCI) += host/
obj-$(CONFIG_USB_PCI) += host/
obj-$(CONFIG_USB_EHCI_HCD) += host/
obj-$(CONFIG_USB_ISP116X_HCD) += host/
obj-$(CONFIG_USB_OHCI_HCD) += host/
@ -62,3 +62,5 @@ obj-$(CONFIG_USB_GADGET) += gadget/
obj-$(CONFIG_USB_COMMON) += common/
obj-$(CONFIG_USBIP_CORE) += usbip/
obj-$(CONFIG_TYPEC) += typec/

View File

@ -474,7 +474,7 @@ static ssize_t cxacru_sysfs_store_adsl_config(struct device *dev,
ret = sscanf(buf + pos, "%x=%x%n", &index, &value, &tmp);
if (ret < 2)
return -EINVAL;
if (index < 0 || index > 0x7f)
if (index > 0x7f)
return -EINVAL;
if (tmp < 0 || tmp > len - pos)
return -EINVAL;

View File

@ -20,7 +20,7 @@ config USB_CHIPIDEA_OF
config USB_CHIPIDEA_PCI
tristate
depends on PCI
depends on USB_PCI
depends on NOP_USB_XCEIV
default USB_CHIPIDEA

View File

@ -177,6 +177,7 @@ struct hw_bank {
* @td_pool: allocation pool for transfer descriptors
* @gadget: device side representation for peripheral controller
* @driver: gadget driver
* @resume_state: save the state of gadget suspend from
* @hw_ep_max: total number of endpoints supported by hardware
* @ci_hw_ep: array of endpoints
* @ep0_dir: ep0 direction
@ -227,6 +228,7 @@ struct ci_hdrc {
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
enum usb_device_state resume_state;
unsigned hw_ep_max;
struct ci_hw_ep ci_hw_ep[ENDPT_MAX];
u32 ep0_dir;

View File

@ -783,9 +783,6 @@ struct platform_device *ci_hdrc_add_device(struct device *dev,
}
pdev->dev.parent = dev;
pdev->dev.dma_mask = dev->dma_mask;
pdev->dev.dma_parms = dev->dma_parms;
dma_set_coherent_mask(&pdev->dev, dev->coherent_dma_mask);
ret = platform_device_add_resources(pdev, res, nres);
if (ret)
@ -841,6 +838,56 @@ static void ci_get_otg_capable(struct ci_hdrc *ci)
}
}
static ssize_t ci_role_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct ci_hdrc *ci = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", ci_role(ci)->name);
}
static ssize_t ci_role_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t n)
{
struct ci_hdrc *ci = dev_get_drvdata(dev);
enum ci_role role;
int ret;
if (!(ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET])) {
dev_warn(dev, "Current configuration is not dual-role, quit\n");
return -EPERM;
}
for (role = CI_ROLE_HOST; role < CI_ROLE_END; role++)
if (!strncmp(buf, ci->roles[role]->name,
strlen(ci->roles[role]->name)))
break;
if (role == CI_ROLE_END || role == ci->role)
return -EINVAL;
pm_runtime_get_sync(dev);
disable_irq(ci->irq);
ci_role_stop(ci);
ret = ci_role_start(ci, role);
if (!ret && ci->role == CI_ROLE_GADGET)
ci_handle_vbus_change(ci);
enable_irq(ci->irq);
pm_runtime_put_sync(dev);
return (ret == 0) ? n : ret;
}
static DEVICE_ATTR(role, 0644, ci_role_show, ci_role_store);
static struct attribute *ci_attrs[] = {
&dev_attr_role.attr,
NULL,
};
static struct attribute_group ci_attr_group = {
.attrs = ci_attrs,
};
static int ci_hdrc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@ -1007,11 +1054,18 @@ static int ci_hdrc_probe(struct platform_device *pdev)
ci_hdrc_otg_fsm_start(ci);
device_set_wakeup_capable(&pdev->dev, true);
ret = dbg_create_files(ci);
if (!ret)
return 0;
if (ret)
goto stop;
ret = sysfs_create_group(&dev->kobj, &ci_attr_group);
if (ret)
goto remove_debug;
return 0;
remove_debug:
dbg_remove_files(ci);
stop:
ci_role_destroy(ci);
deinit_phy:
@ -1033,6 +1087,7 @@ static int ci_hdrc_remove(struct platform_device *pdev)
}
dbg_remove_files(ci);
sysfs_remove_group(&ci->dev->kobj, &ci_attr_group);
ci_role_destroy(ci);
ci_hdrc_enter_lpm(ci, true);
ci_usb_phy_exit(ci);

View File

@ -123,7 +123,8 @@ static int host_start(struct ci_hdrc *ci)
if (usb_disabled())
return -ENODEV;
hcd = usb_create_hcd(&ci_ehci_hc_driver, ci->dev, dev_name(ci->dev));
hcd = __usb_create_hcd(&ci_ehci_hc_driver, ci->dev->parent,
ci->dev, dev_name(ci->dev), NULL);
if (!hcd)
return -ENOMEM;

View File

@ -423,7 +423,8 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
hwreq->req.status = -EALREADY;
ret = usb_gadget_map_request(&ci->gadget, &hwreq->req, hwep->dir);
ret = usb_gadget_map_request_by_dev(ci->dev->parent,
&hwreq->req, hwep->dir);
if (ret)
return ret;
@ -603,7 +604,8 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
list_del_init(&node->td);
}
usb_gadget_unmap_request(&hwep->ci->gadget, &hwreq->req, hwep->dir);
usb_gadget_unmap_request_by_dev(hwep->ci->dev->parent,
&hwreq->req, hwep->dir);
hwreq->req.actual += actual;
@ -1845,27 +1847,32 @@ static irqreturn_t udc_irq(struct ci_hdrc *ci)
if (USBi_PCI & intr) {
ci->gadget.speed = hw_port_is_high_speed(ci) ?
USB_SPEED_HIGH : USB_SPEED_FULL;
if (ci->suspended && ci->driver->resume) {
spin_unlock(&ci->lock);
ci->driver->resume(&ci->gadget);
spin_lock(&ci->lock);
if (ci->suspended) {
if (ci->driver->resume) {
spin_unlock(&ci->lock);
ci->driver->resume(&ci->gadget);
spin_lock(&ci->lock);
}
ci->suspended = 0;
usb_gadget_set_state(&ci->gadget,
ci->resume_state);
}
}
if (USBi_UI & intr)
isr_tr_complete_handler(ci);
if (USBi_SLI & intr) {
if ((USBi_SLI & intr) && !(ci->suspended)) {
ci->suspended = 1;
ci->resume_state = ci->gadget.state;
if (ci->gadget.speed != USB_SPEED_UNKNOWN &&
ci->driver->suspend) {
ci->suspended = 1;
spin_unlock(&ci->lock);
ci->driver->suspend(&ci->gadget);
usb_gadget_set_state(&ci->gadget,
USB_STATE_SUSPENDED);
spin_lock(&ci->lock);
}
usb_gadget_set_state(&ci->gadget,
USB_STATE_SUSPENDED);
}
retval = IRQ_HANDLED;
} else {
@ -1899,13 +1906,13 @@ static int udc_start(struct ci_hdrc *ci)
INIT_LIST_HEAD(&ci->gadget.ep_list);
/* alloc resources */
ci->qh_pool = dma_pool_create("ci_hw_qh", dev,
ci->qh_pool = dma_pool_create("ci_hw_qh", dev->parent,
sizeof(struct ci_hw_qh),
64, CI_HDRC_PAGE_SIZE);
if (ci->qh_pool == NULL)
return -ENOMEM;
ci->td_pool = dma_pool_create("ci_hw_td", dev,
ci->td_pool = dma_pool_create("ci_hw_td", dev->parent,
sizeof(struct ci_hw_td),
64, CI_HDRC_PAGE_SIZE);
if (ci->td_pool == NULL) {
@ -1973,6 +1980,8 @@ static void udc_id_switch_for_host(struct ci_hdrc *ci)
*/
if (ci->is_otg)
hw_write_otgsc(ci, OTGSC_BSVIE | OTGSC_BSVIS, OTGSC_BSVIS);
ci->vbus_active = 0;
}
/**

View File

@ -12,7 +12,7 @@ config USB_ACM
Please read <file:Documentation/usb/acm.txt> for details.
If your modem only reports "Cls=ff(vend.)" in the descriptors in
/proc/bus/usb/devices, then your modem will not work with this
/sys/kernel/debug/usb/devices, then your modem will not work with this
driver.
To compile this driver as a module, choose M here: the

View File

@ -36,6 +36,7 @@
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/log2.h>
#include <linux/tty.h>
#include <linux/serial.h>
#include <linux/tty_driver.h>
@ -283,39 +284,13 @@ static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
* Interrupt handlers for various ACM device responses
*/
/* control interface reports status changes with "interrupt" transfers */
static void acm_ctrl_irq(struct urb *urb)
static void acm_process_notification(struct acm *acm, unsigned char *buf)
{
struct acm *acm = urb->context;
struct usb_cdc_notification *dr = urb->transfer_buffer;
unsigned char *data;
int newctrl;
int difference;
int retval;
int status = urb->status;
struct usb_cdc_notification *dr = (struct usb_cdc_notification *)buf;
unsigned char *data = buf + sizeof(struct usb_cdc_notification);
switch (status) {
case 0:
/* success */
break;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
dev_dbg(&acm->control->dev,
"%s - urb shutting down with status: %d\n",
__func__, status);
return;
default:
dev_dbg(&acm->control->dev,
"%s - nonzero urb status received: %d\n",
__func__, status);
goto exit;
}
usb_mark_last_busy(acm->dev);
data = (unsigned char *)(dr + 1);
switch (dr->bNotificationType) {
case USB_CDC_NOTIFY_NETWORK_CONNECTION:
dev_dbg(&acm->control->dev,
@ -323,7 +298,15 @@ static void acm_ctrl_irq(struct urb *urb)
break;
case USB_CDC_NOTIFY_SERIAL_STATE:
if (le16_to_cpu(dr->wLength) != 2) {
dev_dbg(&acm->control->dev,
"%s - malformed serial state\n", __func__);
break;
}
newctrl = get_unaligned_le16(data);
dev_dbg(&acm->control->dev,
"%s - serial state: 0x%x\n", __func__, newctrl);
if (!acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
dev_dbg(&acm->control->dev,
@ -359,13 +342,86 @@ static void acm_ctrl_irq(struct urb *urb)
default:
dev_dbg(&acm->control->dev,
"%s - unknown notification %d received: index %d "
"len %d data0 %d data1 %d\n",
"%s - unknown notification %d received: index %d len %d\n",
__func__,
dr->bNotificationType, dr->wIndex,
dr->wLength, data[0], data[1]);
break;
dr->bNotificationType, dr->wIndex, dr->wLength);
}
}
/* control interface reports status changes with "interrupt" transfers */
static void acm_ctrl_irq(struct urb *urb)
{
struct acm *acm = urb->context;
struct usb_cdc_notification *dr = urb->transfer_buffer;
unsigned int current_size = urb->actual_length;
unsigned int expected_size, copy_size, alloc_size;
int retval;
int status = urb->status;
switch (status) {
case 0:
/* success */
break;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
acm->nb_index = 0;
dev_dbg(&acm->control->dev,
"%s - urb shutting down with status: %d\n",
__func__, status);
return;
default:
dev_dbg(&acm->control->dev,
"%s - nonzero urb status received: %d\n",
__func__, status);
goto exit;
}
usb_mark_last_busy(acm->dev);
if (acm->nb_index)
dr = (struct usb_cdc_notification *)acm->notification_buffer;
/* size = notification-header + (optional) data */
expected_size = sizeof(struct usb_cdc_notification) +
le16_to_cpu(dr->wLength);
if (current_size < expected_size) {
/* notification is transmitted fragmented, reassemble */
if (acm->nb_size < expected_size) {
if (acm->nb_size) {
kfree(acm->notification_buffer);
acm->nb_size = 0;
}
alloc_size = roundup_pow_of_two(expected_size);
/*
* kmalloc ensures a valid notification_buffer after a
* use of kfree in case the previous allocation was too
* small. Final freeing is done on disconnect.
*/
acm->notification_buffer =
kmalloc(alloc_size, GFP_ATOMIC);
if (!acm->notification_buffer)
goto exit;
acm->nb_size = alloc_size;
}
copy_size = min(current_size,
expected_size - acm->nb_index);
memcpy(&acm->notification_buffer[acm->nb_index],
urb->transfer_buffer, copy_size);
acm->nb_index += copy_size;
current_size = acm->nb_index;
}
if (current_size >= expected_size) {
/* notification complete */
acm_process_notification(acm, (unsigned char *)dr);
acm->nb_index = 0;
}
exit:
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval && retval != -EPERM)
@ -1174,6 +1230,7 @@ static int acm_probe(struct usb_interface *intf,
int combined_interfaces = 0;
struct device *tty_dev;
int rv = -ENOMEM;
int res;
/* normal quirks */
quirks = (unsigned long)id->driver_info;
@ -1274,23 +1331,12 @@ static int acm_probe(struct usb_interface *intf,
return -EINVAL;
}
look_for_collapsed_interface:
for (i = 0; i < 3; i++) {
struct usb_endpoint_descriptor *ep;
ep = &data_interface->cur_altsetting->endpoint[i].desc;
res = usb_find_common_endpoints(data_interface->cur_altsetting,
&epread, &epwrite, &epctrl, NULL);
if (res)
return res;
if (usb_endpoint_is_int_in(ep))
epctrl = ep;
else if (usb_endpoint_is_bulk_out(ep))
epwrite = ep;
else if (usb_endpoint_is_bulk_in(ep))
epread = ep;
else
return -EINVAL;
}
if (!epctrl || !epread || !epwrite)
return -ENODEV;
else
goto made_compressed_probe;
goto made_compressed_probe;
}
skip_normal_probe:
@ -1488,6 +1534,9 @@ skip_countries:
epctrl->bInterval ? epctrl->bInterval : 16);
acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
acm->ctrlurb->transfer_dma = acm->ctrl_dma;
acm->notification_buffer = NULL;
acm->nb_index = 0;
acm->nb_size = 0;
dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
@ -1580,6 +1629,8 @@ static void acm_disconnect(struct usb_interface *intf)
usb_free_coherent(acm->dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
acm_read_buffers_free(acm);
kfree(acm->notification_buffer);
if (!acm->combined_interfaces)
usb_driver_release_interface(&acm_driver, intf == acm->control ?
acm->data : acm->control);

View File

@ -98,7 +98,9 @@ struct acm {
struct acm_wb *putbuffer; /* for acm_tty_put_char() */
int rx_buflimit;
spinlock_t read_lock;
int write_used; /* number of non-empty write buffers */
u8 *notification_buffer; /* to reassemble fragmented notifications */
unsigned int nb_index;
unsigned int nb_size;
int transmitting;
spinlock_t write_lock;
struct mutex mutex;

View File

@ -58,7 +58,6 @@ MODULE_DEVICE_TABLE (usb, wdm_ids);
#define WDM_SUSPENDING 8
#define WDM_RESETTING 9
#define WDM_OVERFLOW 10
#define WDM_DRAIN_ON_OPEN 11
#define WDM_MAX 16
@ -182,7 +181,7 @@ static void wdm_in_callback(struct urb *urb)
"nonzero urb status received: -ESHUTDOWN\n");
goto skip_error;
case -EPIPE:
dev_dbg(&desc->intf->dev,
dev_err(&desc->intf->dev,
"nonzero urb status received: -EPIPE\n");
break;
default:
@ -210,25 +209,6 @@ static void wdm_in_callback(struct urb *urb)
desc->reslength = length;
}
}
/*
* Handling devices with the WDM_DRAIN_ON_OPEN flag set:
* If desc->resp_count is unset, then the urb was submitted
* without a prior notification. If the device returned any
* data, then this implies that it had messages queued without
* notifying us. Continue reading until that queue is flushed.
*/
if (!desc->resp_count) {
if (!length) {
/* do not propagate the expected -EPIPE */
desc->rerr = 0;
goto unlock;
}
dev_dbg(&desc->intf->dev, "got %d bytes without notification\n", length);
set_bit(WDM_RESPONDING, &desc->flags);
usb_submit_urb(desc->response, GFP_ATOMIC);
}
skip_error:
set_bit(WDM_READ, &desc->flags);
wake_up(&desc->wait);
@ -243,7 +223,6 @@ skip_error:
service_outstanding_interrupt(desc);
}
unlock:
spin_unlock(&desc->iuspin);
}
@ -686,17 +665,6 @@ static int wdm_open(struct inode *inode, struct file *file)
dev_err(&desc->intf->dev,
"Error submitting int urb - %d\n", rv);
rv = usb_translate_errors(rv);
} else if (test_bit(WDM_DRAIN_ON_OPEN, &desc->flags)) {
/*
* Some devices keep pending messages queued
* without resending notifications. We must
* flush the message queue before we can
* assume a one-to-one relationship between
* notifications and messages in the queue
*/
dev_dbg(&desc->intf->dev, "draining queued data\n");
set_bit(WDM_RESPONDING, &desc->flags);
rv = usb_submit_urb(desc->response, GFP_KERNEL);
}
} else {
rv = 0;
@ -803,8 +771,7 @@ static void wdm_rxwork(struct work_struct *work)
/* --- hotplug --- */
static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor *ep,
u16 bufsize, int (*manage_power)(struct usb_interface *, int),
bool drain_on_open)
u16 bufsize, int (*manage_power)(struct usb_interface *, int))
{
int rv = -ENOMEM;
struct wdm_device *desc;
@ -891,68 +858,6 @@ static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor
desc->manage_power = manage_power;
/*
* "drain_on_open" enables a hack to work around a firmware
* issue observed on network functions, in particular MBIM
* functions.
*
* Quoting section 7 of the CDC-WMC r1.1 specification:
*
* "The firmware shall interpret GetEncapsulatedResponse as a
* request to read response bytes. The firmware shall send
* the next wLength bytes from the response. The firmware
* shall allow the host to retrieve data using any number of
* GetEncapsulatedResponse requests. The firmware shall
* return a zero- length reply if there are no data bytes
* available.
*
* The firmware shall send ResponseAvailable notifications
* periodically, using any appropriate algorithm, to inform
* the host that there is data available in the reply
* buffer. The firmware is allowed to send ResponseAvailable
* notifications even if there is no data available, but
* this will obviously reduce overall performance."
*
* These requirements, although they make equally sense, are
* often not implemented by network functions. Some firmwares
* will queue data indefinitely, without ever resending a
* notification. The result is that the driver and firmware
* loses "syncronization" if the driver ever fails to respond
* to a single notification, something which easily can happen
* on release(). When this happens, the driver will appear to
* never receive notifications for the most current data. Each
* notification will only cause a single read, which returns
* the oldest data in the firmware's queue.
*
* The "drain_on_open" hack resolves the situation by draining
* data from the firmware until none is returned, without a
* prior notification.
*
* This will inevitably race with the firmware, risking that
* we read data from the device before handling the associated
* notification. To make things worse, some of the devices
* needing the hack do not implement the "return zero if no
* data is available" requirement either. Instead they return
* an error on the subsequent read in this case. This means
* that "winning" the race can cause an unexpected EIO to
* userspace.
*
* "winning" the race is more likely on resume() than on
* open(), and the unexpected error is more harmful in the
* middle of an open session. The hack is therefore only
* applied on open(), and not on resume() where it logically
* would be equally necessary. So we define open() as the only
* driver <-> device "syncronization point". Should we happen
* to lose a notification after open(), then syncronization
* will be lost until release()
*
* The hack should not be enabled for CDC WDM devices
* conforming to the CDC-WMC r1.1 specification. This is
* ensured by setting drain_on_open to false in wdm_probe().
*/
if (drain_on_open)
set_bit(WDM_DRAIN_ON_OPEN, &desc->flags);
spin_lock(&wdm_device_list_lock);
list_add(&desc->device_list, &wdm_device_list);
spin_unlock(&wdm_device_list_lock);
@ -1006,7 +911,7 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
goto err;
ep = &iface->endpoint[0].desc;
rv = wdm_create(intf, ep, maxcom, &wdm_manage_power, false);
rv = wdm_create(intf, ep, maxcom, &wdm_manage_power);
err:
return rv;
@ -1038,7 +943,7 @@ struct usb_driver *usb_cdc_wdm_register(struct usb_interface *intf,
{
int rv = -EINVAL;
rv = wdm_create(intf, ep, bufsize, manage_power, true);
rv = wdm_create(intf, ep, bufsize, manage_power);
if (rv < 0)
goto err;

View File

@ -294,7 +294,7 @@ static int usblp_ctrl_msg(struct usblp *usblp, int request, int type, int dir, i
/*
* See the description for usblp_select_alts() below for the usage
* explanation. Look into your /proc/bus/usb/devices and dmesg in
* explanation. Look into your /sys/kernel/debug/usb/devices and dmesg in
* case of any trouble.
*/
static int proto_bias = -1;
@ -1239,8 +1239,9 @@ static int usblp_select_alts(struct usblp *usblp)
{
struct usb_interface *if_alt;
struct usb_host_interface *ifd;
struct usb_endpoint_descriptor *epd, *epwrite, *epread;
int p, i, e;
struct usb_endpoint_descriptor *epwrite, *epread;
int p, i;
int res;
if_alt = usblp->intf;
@ -1260,31 +1261,21 @@ static int usblp_select_alts(struct usblp *usblp)
ifd->desc.bInterfaceProtocol > USBLP_LAST_PROTOCOL)
continue;
/* Look for bulk OUT and IN endpoints. */
epwrite = epread = NULL;
for (e = 0; e < ifd->desc.bNumEndpoints; e++) {
epd = &ifd->endpoint[e].desc;
if (usb_endpoint_is_bulk_out(epd))
if (!epwrite)
epwrite = epd;
if (usb_endpoint_is_bulk_in(epd))
if (!epread)
epread = epd;
/* Look for the expected bulk endpoints. */
if (ifd->desc.bInterfaceProtocol > 1) {
res = usb_find_common_endpoints(ifd,
&epread, &epwrite, NULL, NULL);
} else {
epread = NULL;
res = usb_find_bulk_out_endpoint(ifd, &epwrite);
}
/* Ignore buggy hardware without the right endpoints. */
if (!epwrite || (ifd->desc.bInterfaceProtocol > 1 && !epread))
if (res)
continue;
/*
* Turn off reads for USB_CLASS_PRINTER/1/1 (unidirectional)
* interfaces and buggy bidirectional printers.
*/
if (ifd->desc.bInterfaceProtocol == 1) {
epread = NULL;
} else if (usblp->quirks & USBLP_QUIRK_BIDIR) {
/* Turn off reads for buggy bidirectional printers. */
if (usblp->quirks & USBLP_QUIRK_BIDIR) {
printk(KERN_INFO "usblp%d: Disabling reads from "
"problematic bidirectional printer\n",
usblp->minor);

View File

@ -1375,7 +1375,7 @@ static int usbtmc_probe(struct usb_interface *intf,
{
struct usbtmc_device_data *data;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
struct usb_endpoint_descriptor *bulk_in, *bulk_out, *int_in;
int n;
int retcode;
@ -1421,49 +1421,29 @@ static int usbtmc_probe(struct usb_interface *intf,
iface_desc = data->intf->cur_altsetting;
data->ifnum = iface_desc->desc.bInterfaceNumber;
/* Find bulk in endpoint */
for (n = 0; n < iface_desc->desc.bNumEndpoints; n++) {
endpoint = &iface_desc->endpoint[n].desc;
if (usb_endpoint_is_bulk_in(endpoint)) {
data->bulk_in = endpoint->bEndpointAddress;
dev_dbg(&intf->dev, "Found bulk in endpoint at %u\n",
data->bulk_in);
break;
}
}
/* Find bulk out endpoint */
for (n = 0; n < iface_desc->desc.bNumEndpoints; n++) {
endpoint = &iface_desc->endpoint[n].desc;
if (usb_endpoint_is_bulk_out(endpoint)) {
data->bulk_out = endpoint->bEndpointAddress;
dev_dbg(&intf->dev, "Found Bulk out endpoint at %u\n",
data->bulk_out);
break;
}
}
if (!data->bulk_out || !data->bulk_in) {
/* Find bulk endpoints */
retcode = usb_find_common_endpoints(iface_desc,
&bulk_in, &bulk_out, NULL, NULL);
if (retcode) {
dev_err(&intf->dev, "bulk endpoints not found\n");
retcode = -ENODEV;
goto err_put;
}
/* Find int endpoint */
for (n = 0; n < iface_desc->desc.bNumEndpoints; n++) {
endpoint = &iface_desc->endpoint[n].desc;
data->bulk_in = bulk_in->bEndpointAddress;
dev_dbg(&intf->dev, "Found bulk in endpoint at %u\n", data->bulk_in);
if (usb_endpoint_is_int_in(endpoint)) {
data->iin_ep_present = 1;
data->iin_ep = endpoint->bEndpointAddress;
data->iin_wMaxPacketSize = usb_endpoint_maxp(endpoint);
data->iin_interval = endpoint->bInterval;
dev_dbg(&intf->dev, "Found Int in endpoint at %u\n",
data->bulk_out = bulk_out->bEndpointAddress;
dev_dbg(&intf->dev, "Found Bulk out endpoint at %u\n", data->bulk_out);
/* Find int endpoint */
retcode = usb_find_int_in_endpoint(iface_desc, &int_in);
if (!retcode) {
data->iin_ep_present = 1;
data->iin_ep = int_in->bEndpointAddress;
data->iin_wMaxPacketSize = usb_endpoint_maxp(int_in);
data->iin_interval = int_in->bInterval;
dev_dbg(&intf->dev, "Found Int in endpoint at %u\n",
data->iin_ep);
break;
}
}
retcode = get_capabilities(data);

View File

@ -31,6 +31,13 @@
#include <linux/usb/otg.h>
#include <linux/usb/otg-fsm.h>
#ifdef VERBOSE
#define VDBG(fmt, args...) pr_debug("[%s] " fmt, \
__func__, ## args)
#else
#define VDBG(stuff...) do {} while (0)
#endif
/* Change USB protocol when there is a protocol change */
static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
{

View File

@ -8,7 +8,7 @@ usbcore-y += devio.o notify.o generic.o quirks.o devices.o
usbcore-y += port.o
usbcore-$(CONFIG_OF) += of.o
usbcore-$(CONFIG_PCI) += hcd-pci.o
usbcore-$(CONFIG_USB_PCI) += hcd-pci.o
usbcore-$(CONFIG_ACPI) += usb-acpi.o
obj-$(CONFIG_USB) += usbcore.o

View File

@ -66,7 +66,7 @@ int hcd_buffer_create(struct usb_hcd *hcd)
int i, size;
if (!IS_ENABLED(CONFIG_HAS_DMA) ||
(!hcd->self.controller->dma_mask &&
(!is_device_dma_capable(hcd->self.sysdev) &&
!(hcd->driver->flags & HCD_LOCAL_MEM)))
return 0;
@ -75,7 +75,7 @@ int hcd_buffer_create(struct usb_hcd *hcd)
if (!size)
continue;
snprintf(name, sizeof(name), "buffer-%d", size);
hcd->pool[i] = dma_pool_create(name, hcd->self.controller,
hcd->pool[i] = dma_pool_create(name, hcd->self.sysdev,
size, size, 0);
if (!hcd->pool[i]) {
hcd_buffer_destroy(hcd);
@ -130,7 +130,7 @@ void *hcd_buffer_alloc(
/* some USB hosts just use PIO */
if (!IS_ENABLED(CONFIG_HAS_DMA) ||
(!bus->controller->dma_mask &&
(!is_device_dma_capable(bus->sysdev) &&
!(hcd->driver->flags & HCD_LOCAL_MEM))) {
*dma = ~(dma_addr_t) 0;
return kmalloc(size, mem_flags);
@ -140,7 +140,7 @@ void *hcd_buffer_alloc(
if (size <= pool_max[i])
return dma_pool_alloc(hcd->pool[i], mem_flags, dma);
}
return dma_alloc_coherent(hcd->self.controller, size, dma, mem_flags);
return dma_alloc_coherent(hcd->self.sysdev, size, dma, mem_flags);
}
void hcd_buffer_free(
@ -157,7 +157,7 @@ void hcd_buffer_free(
return;
if (!IS_ENABLED(CONFIG_HAS_DMA) ||
(!bus->controller->dma_mask &&
(!is_device_dma_capable(bus->sysdev) &&
!(hcd->driver->flags & HCD_LOCAL_MEM))) {
kfree(addr);
return;
@ -169,5 +169,5 @@ void hcd_buffer_free(
return;
}
}
dma_free_coherent(hcd->self.controller, size, addr, dma);
dma_free_coherent(hcd->self.sysdev, size, addr, dma);
}

View File

@ -24,7 +24,7 @@
* <mountpoint>/devices contains USB topology, device, config, class,
* interface, & endpoint data.
*
* I considered using /proc/bus/usb/devices/device# for each device
* I considered using /dev/bus/usb/device# for each device
* as it is attached or detached, but I didn't like this for some
* reason -- maybe it's just too deep of a directory structure.
* I also don't like looking in multiple places to gather and view
@ -40,7 +40,7 @@
* Converted the whole proc stuff to real
* read methods. Now not the whole device list needs to fit
* into one page, only the device list for one bus.
* Added a poll method to /proc/bus/usb/devices, to wake
* Added a poll method to /sys/kernel/debug/usb/devices, to wake
* up an eventual usbd
* 2000-01-04: Thomas Sailer <sailer@ife.ee.ethz.ch>
* Turned into its own filesystem

View File

@ -1331,6 +1331,24 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
*/
if (udev->parent && !PMSG_IS_AUTO(msg))
status = 0;
/*
* If the device is inaccessible, don't try to resume
* suspended interfaces and just return the error.
*/
if (status && status != -EBUSY) {
int err;
u16 devstat;
err = usb_get_status(udev, USB_RECIP_DEVICE, 0,
&devstat);
if (err) {
dev_err(&udev->dev,
"Failed to suspend device, error %d\n",
status);
goto done;
}
}
}
/* If the suspend failed, resume interfaces that did get suspended */
@ -1763,6 +1781,9 @@ static int autosuspend_check(struct usb_device *udev)
int w, i;
struct usb_interface *intf;
if (udev->state == USB_STATE_NOTATTACHED)
return -ENODEV;
/* Fail if autosuspend is disabled, or any interfaces are in use, or
* any interface drivers require remote wakeup but it isn't available.
*/

View File

@ -29,6 +29,7 @@
#define MAX_USB_MINORS 256
static const struct file_operations *usb_minors[MAX_USB_MINORS];
static DECLARE_RWSEM(minor_rwsem);
static DEFINE_MUTEX(init_usb_class_mutex);
static int usb_open(struct inode *inode, struct file *file)
{
@ -111,8 +112,9 @@ static void release_usb_class(struct kref *kref)
static void destroy_usb_class(void)
{
if (usb_class)
kref_put(&usb_class->kref, release_usb_class);
mutex_lock(&init_usb_class_mutex);
kref_put(&usb_class->kref, release_usb_class);
mutex_unlock(&init_usb_class_mutex);
}
int usb_major_init(void)
@ -173,7 +175,10 @@ int usb_register_dev(struct usb_interface *intf,
if (intf->minor >= 0)
return -EADDRINUSE;
mutex_lock(&init_usb_class_mutex);
retval = init_usb_class();
mutex_unlock(&init_usb_class_mutex);
if (retval)
return retval;

View File

@ -1076,6 +1076,7 @@ static void usb_deregister_bus (struct usb_bus *bus)
static int register_root_hub(struct usb_hcd *hcd)
{
struct device *parent_dev = hcd->self.controller;
struct device *sysdev = hcd->self.sysdev;
struct usb_device *usb_dev = hcd->self.root_hub;
const int devnum = 1;
int retval;
@ -1122,7 +1123,7 @@ static int register_root_hub(struct usb_hcd *hcd)
/* Did the HC die before the root hub was registered? */
if (HCD_DEAD(hcd))
usb_hc_died (hcd); /* This time clean up */
usb_dev->dev.of_node = parent_dev->of_node;
usb_dev->dev.of_node = sysdev->of_node;
}
mutex_unlock(&usb_bus_idr_lock);
@ -1435,7 +1436,7 @@ void usb_hcd_unmap_urb_setup_for_dma(struct usb_hcd *hcd, struct urb *urb)
{
if (IS_ENABLED(CONFIG_HAS_DMA) &&
(urb->transfer_flags & URB_SETUP_MAP_SINGLE))
dma_unmap_single(hcd->self.controller,
dma_unmap_single(hcd->self.sysdev,
urb->setup_dma,
sizeof(struct usb_ctrlrequest),
DMA_TO_DEVICE);
@ -1468,19 +1469,19 @@ void usb_hcd_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
if (IS_ENABLED(CONFIG_HAS_DMA) &&
(urb->transfer_flags & URB_DMA_MAP_SG))
dma_unmap_sg(hcd->self.controller,
dma_unmap_sg(hcd->self.sysdev,
urb->sg,
urb->num_sgs,
dir);
else if (IS_ENABLED(CONFIG_HAS_DMA) &&
(urb->transfer_flags & URB_DMA_MAP_PAGE))
dma_unmap_page(hcd->self.controller,
dma_unmap_page(hcd->self.sysdev,
urb->transfer_dma,
urb->transfer_buffer_length,
dir);
else if (IS_ENABLED(CONFIG_HAS_DMA) &&
(urb->transfer_flags & URB_DMA_MAP_SINGLE))
dma_unmap_single(hcd->self.controller,
dma_unmap_single(hcd->self.sysdev,
urb->transfer_dma,
urb->transfer_buffer_length,
dir);
@ -1523,11 +1524,11 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
return ret;
if (IS_ENABLED(CONFIG_HAS_DMA) && hcd->self.uses_dma) {
urb->setup_dma = dma_map_single(
hcd->self.controller,
hcd->self.sysdev,
urb->setup_packet,
sizeof(struct usb_ctrlrequest),
DMA_TO_DEVICE);
if (dma_mapping_error(hcd->self.controller,
if (dma_mapping_error(hcd->self.sysdev,
urb->setup_dma))
return -EAGAIN;
urb->transfer_flags |= URB_SETUP_MAP_SINGLE;
@ -1558,7 +1559,7 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
}
n = dma_map_sg(
hcd->self.controller,
hcd->self.sysdev,
urb->sg,
urb->num_sgs,
dir);
@ -1573,12 +1574,12 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
} else if (urb->sg) {
struct scatterlist *sg = urb->sg;
urb->transfer_dma = dma_map_page(
hcd->self.controller,
hcd->self.sysdev,
sg_page(sg),
sg->offset,
urb->transfer_buffer_length,
dir);
if (dma_mapping_error(hcd->self.controller,
if (dma_mapping_error(hcd->self.sysdev,
urb->transfer_dma))
ret = -EAGAIN;
else
@ -1588,11 +1589,11 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
ret = -EAGAIN;
} else {
urb->transfer_dma = dma_map_single(
hcd->self.controller,
hcd->self.sysdev,
urb->transfer_buffer,
urb->transfer_buffer_length,
dir);
if (dma_mapping_error(hcd->self.controller,
if (dma_mapping_error(hcd->self.sysdev,
urb->transfer_dma))
ret = -EAGAIN;
else
@ -2498,24 +2499,8 @@ static void init_giveback_urb_bh(struct giveback_urb_bh *bh)
tasklet_init(&bh->bh, usb_giveback_urb_bh, (unsigned long)bh);
}
/**
* usb_create_shared_hcd - create and initialize an HCD structure
* @driver: HC driver that will use this hcd
* @dev: device for this HC, stored in hcd->self.controller
* @bus_name: value to store in hcd->self.bus_name
* @primary_hcd: a pointer to the usb_hcd structure that is sharing the
* PCI device. Only allocate certain resources for the primary HCD
* Context: !in_interrupt()
*
* Allocate a struct usb_hcd, with extra space at the end for the
* HC driver's private data. Initialize the generic members of the
* hcd structure.
*
* Return: On success, a pointer to the created and initialized HCD structure.
* On failure (e.g. if memory is unavailable), %NULL.
*/
struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver,
struct device *dev, const char *bus_name,
struct usb_hcd *__usb_create_hcd(const struct hc_driver *driver,
struct device *sysdev, struct device *dev, const char *bus_name,
struct usb_hcd *primary_hcd)
{
struct usb_hcd *hcd;
@ -2556,8 +2541,9 @@ struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver,
usb_bus_init(&hcd->self);
hcd->self.controller = dev;
hcd->self.sysdev = sysdev;
hcd->self.bus_name = bus_name;
hcd->self.uses_dma = (dev->dma_mask != NULL);
hcd->self.uses_dma = (sysdev->dma_mask != NULL);
init_timer(&hcd->rh_timer);
hcd->rh_timer.function = rh_timer_func;
@ -2572,6 +2558,30 @@ struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver,
"USB Host Controller";
return hcd;
}
EXPORT_SYMBOL_GPL(__usb_create_hcd);
/**
* usb_create_shared_hcd - create and initialize an HCD structure
* @driver: HC driver that will use this hcd
* @dev: device for this HC, stored in hcd->self.controller
* @bus_name: value to store in hcd->self.bus_name
* @primary_hcd: a pointer to the usb_hcd structure that is sharing the
* PCI device. Only allocate certain resources for the primary HCD
* Context: !in_interrupt()
*
* Allocate a struct usb_hcd, with extra space at the end for the
* HC driver's private data. Initialize the generic members of the
* hcd structure.
*
* Return: On success, a pointer to the created and initialized HCD structure.
* On failure (e.g. if memory is unavailable), %NULL.
*/
struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver,
struct device *dev, const char *bus_name,
struct usb_hcd *primary_hcd)
{
return __usb_create_hcd(driver, dev, dev, bus_name, primary_hcd);
}
EXPORT_SYMBOL_GPL(usb_create_shared_hcd);
/**
@ -2591,7 +2601,7 @@ EXPORT_SYMBOL_GPL(usb_create_shared_hcd);
struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,
struct device *dev, const char *bus_name)
{
return usb_create_shared_hcd(driver, dev, bus_name, NULL);
return __usb_create_hcd(driver, dev, dev, bus_name, NULL);
}
EXPORT_SYMBOL_GPL(usb_create_hcd);
@ -2718,7 +2728,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
struct usb_device *rhdev;
if (IS_ENABLED(CONFIG_USB_PHY) && !hcd->usb_phy) {
struct usb_phy *phy = usb_get_phy_dev(hcd->self.controller, 0);
struct usb_phy *phy = usb_get_phy_dev(hcd->self.sysdev, 0);
if (IS_ERR(phy)) {
retval = PTR_ERR(phy);
@ -2736,7 +2746,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
}
if (IS_ENABLED(CONFIG_GENERIC_PHY) && !hcd->phy) {
struct phy *phy = phy_get(hcd->self.controller, "usb");
struct phy *phy = phy_get(hcd->self.sysdev, "usb");
if (IS_ERR(phy)) {
retval = PTR_ERR(phy);
@ -2784,7 +2794,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
*/
retval = hcd_buffer_create(hcd);
if (retval != 0) {
dev_dbg(hcd->self.controller, "pool alloc failed\n");
dev_dbg(hcd->self.sysdev, "pool alloc failed\n");
goto err_create_buf;
}
@ -2794,7 +2804,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
rhdev = usb_alloc_dev(NULL, &hcd->self, 0);
if (rhdev == NULL) {
dev_err(hcd->self.controller, "unable to allocate root hub\n");
dev_err(hcd->self.sysdev, "unable to allocate root hub\n");
retval = -ENOMEM;
goto err_allocate_root_hub;
}

View File

@ -1066,6 +1066,9 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
portstatus = portchange = 0;
status = hub_port_status(hub, port1, &portstatus, &portchange);
if (status)
goto abort;
if (udev || (portstatus & USB_PORT_STAT_CONNECTION))
dev_dbg(&port_dev->dev, "status %04x change %04x\n",
portstatus, portchange);
@ -1198,7 +1201,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
/* Scan all ports that need attention */
kick_hub_wq(hub);
abort:
if (type == HUB_INIT2 || type == HUB_INIT3) {
/* Allow autosuspend if it was suppressed */
disconnected:
@ -2084,6 +2087,12 @@ void usb_disconnect(struct usb_device **pdev)
dev_info(&udev->dev, "USB disconnect, device number %d\n",
udev->devnum);
/*
* Ensure that the pm runtime code knows that the USB device
* is in the process of being disconnected.
*/
pm_runtime_barrier(&udev->dev);
usb_lock_device(udev);
hub_disconnect_children(udev);

View File

@ -18,6 +18,7 @@
*/
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/usb/of.h>
/**
@ -46,3 +47,25 @@ struct device_node *usb_of_get_child_node(struct device_node *parent,
}
EXPORT_SYMBOL_GPL(usb_of_get_child_node);
/**
* usb_of_get_companion_dev - Find the companion device
* @dev: the device pointer to find a companion
*
* Find the companion device from platform bus.
*
* Return: On success, a pointer to the companion device, %NULL on failure.
*/
struct device *usb_of_get_companion_dev(struct device *dev)
{
struct device_node *node;
struct platform_device *pdev = NULL;
node = of_parse_phandle(dev->of_node, "companion", 0);
if (node)
pdev = of_find_device_by_node(node);
of_node_put(node);
return pdev ? &pdev->dev : NULL;
}
EXPORT_SYMBOL_GPL(usb_of_get_companion_dev);

View File

@ -74,6 +74,140 @@ MODULE_PARM_DESC(autosuspend, "default autosuspend delay");
#define usb_autosuspend_delay 0
#endif
static bool match_endpoint(struct usb_endpoint_descriptor *epd,
struct usb_endpoint_descriptor **bulk_in,
struct usb_endpoint_descriptor **bulk_out,
struct usb_endpoint_descriptor **int_in,
struct usb_endpoint_descriptor **int_out)
{
switch (usb_endpoint_type(epd)) {
case USB_ENDPOINT_XFER_BULK:
if (usb_endpoint_dir_in(epd)) {
if (bulk_in && !*bulk_in) {
*bulk_in = epd;
break;
}
} else {
if (bulk_out && !*bulk_out) {
*bulk_out = epd;
break;
}
}
return false;
case USB_ENDPOINT_XFER_INT:
if (usb_endpoint_dir_in(epd)) {
if (int_in && !*int_in) {
*int_in = epd;
break;
}
} else {
if (int_out && !*int_out) {
*int_out = epd;
break;
}
}
return false;
default:
return false;
}
return (!bulk_in || *bulk_in) && (!bulk_out || *bulk_out) &&
(!int_in || *int_in) && (!int_out || *int_out);
}
/**
* usb_find_common_endpoints() -- look up common endpoint descriptors
* @alt: alternate setting to search
* @bulk_in: pointer to descriptor pointer, or NULL
* @bulk_out: pointer to descriptor pointer, or NULL
* @int_in: pointer to descriptor pointer, or NULL
* @int_out: pointer to descriptor pointer, or NULL
*
* Search the alternate setting's endpoint descriptors for the first bulk-in,
* bulk-out, interrupt-in and interrupt-out endpoints and return them in the
* provided pointers (unless they are NULL).
*
* If a requested endpoint is not found, the corresponding pointer is set to
* NULL.
*
* Return: Zero if all requested descriptors were found, or -ENXIO otherwise.
*/
int usb_find_common_endpoints(struct usb_host_interface *alt,
struct usb_endpoint_descriptor **bulk_in,
struct usb_endpoint_descriptor **bulk_out,
struct usb_endpoint_descriptor **int_in,
struct usb_endpoint_descriptor **int_out)
{
struct usb_endpoint_descriptor *epd;
int i;
if (bulk_in)
*bulk_in = NULL;
if (bulk_out)
*bulk_out = NULL;
if (int_in)
*int_in = NULL;
if (int_out)
*int_out = NULL;
for (i = 0; i < alt->desc.bNumEndpoints; ++i) {
epd = &alt->endpoint[i].desc;
if (match_endpoint(epd, bulk_in, bulk_out, int_in, int_out))
return 0;
}
return -ENXIO;
}
EXPORT_SYMBOL_GPL(usb_find_common_endpoints);
/**
* usb_find_common_endpoints_reverse() -- look up common endpoint descriptors
* @alt: alternate setting to search
* @bulk_in: pointer to descriptor pointer, or NULL
* @bulk_out: pointer to descriptor pointer, or NULL
* @int_in: pointer to descriptor pointer, or NULL
* @int_out: pointer to descriptor pointer, or NULL
*
* Search the alternate setting's endpoint descriptors for the last bulk-in,
* bulk-out, interrupt-in and interrupt-out endpoints and return them in the
* provided pointers (unless they are NULL).
*
* If a requested endpoint is not found, the corresponding pointer is set to
* NULL.
*
* Return: Zero if all requested descriptors were found, or -ENXIO otherwise.
*/
int usb_find_common_endpoints_reverse(struct usb_host_interface *alt,
struct usb_endpoint_descriptor **bulk_in,
struct usb_endpoint_descriptor **bulk_out,
struct usb_endpoint_descriptor **int_in,
struct usb_endpoint_descriptor **int_out)
{
struct usb_endpoint_descriptor *epd;
int i;
if (bulk_in)
*bulk_in = NULL;
if (bulk_out)
*bulk_out = NULL;
if (int_in)
*int_in = NULL;
if (int_out)
*int_out = NULL;
for (i = alt->desc.bNumEndpoints - 1; i >= 0; --i) {
epd = &alt->endpoint[i].desc;
if (match_endpoint(epd, bulk_in, bulk_out, int_in, int_out))
return 0;
}
return -ENXIO;
}
EXPORT_SYMBOL_GPL(usb_find_common_endpoints_reverse);
/**
* usb_find_alt_setting() - Given a configuration, find the alternate setting
@ -453,9 +587,9 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
* Note: calling dma_set_mask() on a USB device would set the
* mask for the entire HCD, so don't do that.
*/
dev->dev.dma_mask = bus->controller->dma_mask;
dev->dev.dma_pfn_offset = bus->controller->dma_pfn_offset;
set_dev_node(&dev->dev, dev_to_node(bus->controller));
dev->dev.dma_mask = bus->sysdev->dma_mask;
dev->dev.dma_pfn_offset = bus->sysdev->dma_pfn_offset;
set_dev_node(&dev->dev, dev_to_node(bus->sysdev));
dev->state = USB_STATE_ATTACHED;
dev->lpm_disable_count = 1;
atomic_set(&dev->urbnum, 0);
@ -803,7 +937,7 @@ struct urb *usb_buffer_map(struct urb *urb)
if (!urb
|| !urb->dev
|| !(bus = urb->dev->bus)
|| !(controller = bus->controller))
|| !(controller = bus->sysdev))
return NULL;
if (controller->dma_mask) {
@ -841,7 +975,7 @@ void usb_buffer_dmasync(struct urb *urb)
|| !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
|| !urb->dev
|| !(bus = urb->dev->bus)
|| !(controller = bus->controller))
|| !(controller = bus->sysdev))
return;
if (controller->dma_mask) {
@ -875,7 +1009,7 @@ void usb_buffer_unmap(struct urb *urb)
|| !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
|| !urb->dev
|| !(bus = urb->dev->bus)
|| !(controller = bus->controller))
|| !(controller = bus->sysdev))
return;
if (controller->dma_mask) {
@ -925,7 +1059,7 @@ int usb_buffer_map_sg(const struct usb_device *dev, int is_in,
if (!dev
|| !(bus = dev->bus)
|| !(controller = bus->controller)
|| !(controller = bus->sysdev)
|| !controller->dma_mask)
return -EINVAL;
@ -961,7 +1095,7 @@ void usb_buffer_dmasync_sg(const struct usb_device *dev, int is_in,
if (!dev
|| !(bus = dev->bus)
|| !(controller = bus->controller)
|| !(controller = bus->sysdev)
|| !controller->dma_mask)
return;
@ -989,7 +1123,7 @@ void usb_buffer_unmap_sg(const struct usb_device *dev, int is_in,
if (!dev
|| !(bus = dev->bus)
|| !(controller = bus->controller)
|| !(controller = bus->sysdev)
|| !controller->dma_mask)
return;

View File

@ -54,7 +54,7 @@ endchoice
config USB_DWC2_PCI
tristate "DWC2 PCI"
depends on PCI
depends on USB_PCI
depends on USB_GADGET || !USB_GADGET
default n
select NOP_USB_XCEIV

View File

@ -423,6 +423,10 @@ enum dwc2_ep0_state {
* needed.
* 0 - No (default)
* 1 - Yes
* @activate_stm_fs_transceiver: Activate internal transceiver using GGPIO
* register.
* 0 - Deactivate the transceiver (default)
* 1 - Activate the transceiver
* @g_dma: Enables gadget dma usage (default: autodetect).
* @g_dma_desc: Enables gadget descriptor DMA (default: autodetect).
* @g_rx_fifo_size: The periodic rx fifo size for the device, in
@ -477,6 +481,7 @@ struct dwc2_core_params {
bool uframe_sched;
bool external_id_pin_ctl;
bool hibernation;
bool activate_stm_fs_transceiver;
u16 max_packet_count;
u32 max_transfer_size;
u32 ahbcfg;

View File

@ -121,7 +121,7 @@ static void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg)
static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
{
u32 usbcfg, i2cctl;
u32 usbcfg, ggpio, i2cctl;
int retval = 0;
/*
@ -145,6 +145,19 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
return retval;
}
}
if (hsotg->params.activate_stm_fs_transceiver) {
ggpio = dwc2_readl(hsotg->regs + GGPIO);
if (!(ggpio & GGPIO_STM32_OTG_GCCFG_PWRDWN)) {
dev_dbg(hsotg->dev, "Activating transceiver\n");
/*
* STM32F4x9 uses the GGPIO register as general
* core configuration register.
*/
ggpio |= GGPIO_STM32_OTG_GCCFG_PWRDWN;
dwc2_writel(ggpio, hsotg->regs + GGPIO);
}
}
}
/*
@ -3264,6 +3277,7 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
dwc2_core_init(hsotg, false);
dwc2_enable_global_interrupts(hsotg);
spin_lock_irqsave(&hsotg->lock, flags);
dwc2_hsotg_disconnect(hsotg);
dwc2_hsotg_core_init_disconnected(hsotg, false);
spin_unlock_irqrestore(&hsotg->lock, flags);
dwc2_hsotg_core_connect(hsotg);

View File

@ -225,6 +225,8 @@
#define GPVNDCTL HSOTG_REG(0x0034)
#define GGPIO HSOTG_REG(0x0038)
#define GGPIO_STM32_OTG_GCCFG_PWRDWN BIT(16)
#define GUID HSOTG_REG(0x003c)
#define GSNPSID HSOTG_REG(0x0040)
#define GHWCFG1 HSOTG_REG(0x0044)

View File

@ -120,6 +120,22 @@ static void dwc2_set_amcc_params(struct dwc2_hsotg *hsotg)
p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT;
}
static void dwc2_set_stm32f4x9_fsotg_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE;
p->speed = DWC2_SPEED_PARAM_FULL;
p->host_rx_fifo_size = 128;
p->host_nperio_tx_fifo_size = 96;
p->host_perio_tx_fifo_size = 96;
p->max_packet_count = 256;
p->phy_type = DWC2_PHY_TYPE_PARAM_FS;
p->i2c_enable = false;
p->uframe_sched = false;
p->activate_stm_fs_transceiver = true;
}
const struct of_device_id dwc2_of_match_table[] = {
{ .compatible = "brcm,bcm2835-usb", .data = dwc2_set_bcm_params },
{ .compatible = "hisilicon,hi6220-usb", .data = dwc2_set_his_params },
@ -133,6 +149,9 @@ const struct of_device_id dwc2_of_match_table[] = {
{ .compatible = "amlogic,meson-gxbb-usb",
.data = dwc2_set_amlogic_params },
{ .compatible = "amcc,dwc-otg", .data = dwc2_set_amcc_params },
{ .compatible = "st,stm32f4x9-fsotg",
.data = dwc2_set_stm32f4x9_fsotg_params },
{ .compatible = "st,stm32f4x9-hsotg" },
{},
};
MODULE_DEVICE_TABLE(of, dwc2_of_match_table);

View File

@ -214,20 +214,11 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg)
hsotg->reset = devm_reset_control_get_optional(hsotg->dev, "dwc2");
if (IS_ERR(hsotg->reset)) {
ret = PTR_ERR(hsotg->reset);
switch (ret) {
case -ENOENT:
case -ENOTSUPP:
hsotg->reset = NULL;
break;
default:
dev_err(hsotg->dev, "error getting reset control %d\n",
ret);
return ret;
}
dev_err(hsotg->dev, "error getting reset control %d\n", ret);
return ret;
}
if (hsotg->reset)
reset_control_deassert(hsotg->reset);
reset_control_deassert(hsotg->reset);
/* Set default UTMI width */
hsotg->phyif = GUSBCFG_PHYIF16;
@ -326,8 +317,7 @@ static int dwc2_driver_remove(struct platform_device *dev)
if (hsotg->ll_hw_enabled)
dwc2_lowlevel_hw_disable(hsotg);
if (hsotg->reset)
reset_control_assert(hsotg->reset);
reset_control_assert(hsotg->reset);
return 0;
}

View File

@ -41,6 +41,7 @@ config USB_DWC3_GADGET
config USB_DWC3_DUAL_ROLE
bool "Dual Role mode"
depends on ((USB=y || USB=USB_DWC3) && (USB_GADGET=y || USB_GADGET=USB_DWC3))
depends on (EXTCON=y || EXTCON=USB_DWC3)
help
This is the default mode of working of DWC3 controller where
both host and gadget features are enabled.
@ -70,7 +71,7 @@ config USB_DWC3_EXYNOS
config USB_DWC3_PCI
tristate "PCIe-based Platforms"
depends on PCI && ACPI
depends on USB_PCI && ACPI
default USB_DWC3
help
If you're using the DesignWare Core IP with a PCIe, please say

View File

@ -17,6 +17,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
dwc3-y += gadget.o ep0.o
endif
ifneq ($(CONFIG_USB_DWC3_DUAL_ROLE),)
dwc3-y += drd.o
endif
ifneq ($(CONFIG_USB_DWC3_ULPI),)
dwc3-y += ulpi.o
endif

View File

@ -100,7 +100,10 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
return 0;
}
void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
static void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
static int dwc3_event_buffers_setup(struct dwc3 *dwc);
static void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
{
u32 reg;
@ -110,6 +113,69 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
}
static void __dwc3_set_mode(struct work_struct *work)
{
struct dwc3 *dwc = work_to_dwc(work);
unsigned long flags;
int ret;
if (!dwc->desired_dr_role)
return;
if (dwc->desired_dr_role == dwc->current_dr_role)
return;
if (dwc->dr_mode != USB_DR_MODE_OTG)
return;
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_HOST:
dwc3_host_exit(dwc);
break;
case DWC3_GCTL_PRTCAP_DEVICE:
dwc3_gadget_exit(dwc);
dwc3_event_buffers_cleanup(dwc);
break;
default:
break;
}
spin_lock_irqsave(&dwc->lock, flags);
dwc3_set_prtcap(dwc, dwc->desired_dr_role);
dwc->current_dr_role = dwc->desired_dr_role;
spin_unlock_irqrestore(&dwc->lock, flags);
switch (dwc->desired_dr_role) {
case DWC3_GCTL_PRTCAP_HOST:
ret = dwc3_host_init(dwc);
if (ret)
dev_err(dwc->dev, "failed to initialize host\n");
break;
case DWC3_GCTL_PRTCAP_DEVICE:
dwc3_event_buffers_setup(dwc);
ret = dwc3_gadget_init(dwc);
if (ret)
dev_err(dwc->dev, "failed to initialize peripheral\n");
break;
default:
break;
}
}
void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
{
unsigned long flags;
spin_lock_irqsave(&dwc->lock, flags);
dwc->desired_dr_role = mode;
spin_unlock_irqrestore(&dwc->lock, flags);
queue_work(system_power_efficient_wq, &dwc->drd_work);
}
u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type)
{
struct dwc3 *dwc = dep->dwc;
@ -397,8 +463,7 @@ static void dwc3_core_num_eps(struct dwc3 *dwc)
{
struct dwc3_hwparams *parms = &dwc->hwparams;
dwc->num_in_eps = DWC3_NUM_IN_EPS(parms);
dwc->num_out_eps = DWC3_NUM_EPS(parms) - dwc->num_in_eps;
dwc->num_eps = DWC3_NUM_EPS(parms);
}
static void dwc3_cache_hwparams(struct dwc3 *dwc)
@ -431,6 +496,12 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
/*
* Make sure UX_EXIT_PX is cleared as that causes issues with some
* PHYs. Also, this bit is not supposed to be used in normal operation.
*/
reg &= ~DWC3_GUSB3PIPECTL_UX_EXIT_PX;
/*
* Above 1.94a, it is recommended to set DWC3_GUSB3PIPECTL_SUSPHY
* to '0' during coreConsultant configuration. So default value
@ -714,21 +785,6 @@ static int dwc3_core_init(struct dwc3 *dwc)
goto err4;
}
switch (dwc->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
break;
case USB_DR_MODE_HOST:
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
break;
case USB_DR_MODE_OTG:
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
break;
default:
dev_warn(dwc->dev, "Unsupported mode %d\n", dwc->dr_mode);
break;
}
/*
* ENDXFER polling is available on version 3.10a and later of
* the DWC_usb3 controller. It is NOT available in the
@ -846,6 +902,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
switch (dwc->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
ret = dwc3_gadget_init(dwc);
if (ret) {
if (ret != -EPROBE_DEFER)
@ -854,6 +911,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
}
break;
case USB_DR_MODE_HOST:
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST);
ret = dwc3_host_init(dwc);
if (ret) {
if (ret != -EPROBE_DEFER)
@ -862,17 +920,11 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
}
break;
case USB_DR_MODE_OTG:
ret = dwc3_host_init(dwc);
INIT_WORK(&dwc->drd_work, __dwc3_set_mode);
ret = dwc3_drd_init(dwc);
if (ret) {
if (ret != -EPROBE_DEFER)
dev_err(dev, "failed to initialize host\n");
return ret;
}
ret = dwc3_gadget_init(dwc);
if (ret) {
if (ret != -EPROBE_DEFER)
dev_err(dev, "failed to initialize gadget\n");
dev_err(dev, "failed to initialize dual-role\n");
return ret;
}
break;
@ -894,8 +946,7 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
dwc3_host_exit(dwc);
break;
case USB_DR_MODE_OTG:
dwc3_host_exit(dwc);
dwc3_gadget_exit(dwc);
dwc3_drd_exit(dwc);
break;
default:
/* do nothing */

View File

@ -23,10 +23,12 @@
#include <linux/spinlock.h>
#include <linux/ioport.h>
#include <linux/list.h>
#include <linux/bitops.h>
#include <linux/dma-mapping.h>
#include <linux/mm.h>
#include <linux/debugfs.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@ -39,9 +41,8 @@
/* Global constants */
#define DWC3_PULL_UP_TIMEOUT 500 /* ms */
#define DWC3_ZLP_BUF_SIZE 1024 /* size of a superspeed bulk */
#define DWC3_BOUNCE_SIZE 1024 /* size of a superspeed bulk */
#define DWC3_EP0_BOUNCE_SIZE 512
#define DWC3_EP0_SETUP_SIZE 512
#define DWC3_ENDPOINTS_NUM 32
#define DWC3_XHCI_RESOURCES_NUM 2
@ -66,7 +67,7 @@
#define DWC3_DEVICE_EVENT_OVERFLOW 11
#define DWC3_GEVNTCOUNT_MASK 0xfffc
#define DWC3_GEVNTCOUNT_EHB (1 << 31)
#define DWC3_GEVNTCOUNT_EHB BIT(31)
#define DWC3_GSNPSID_MASK 0xffff0000
#define DWC3_GSNPSREV_MASK 0xffff
@ -116,20 +117,20 @@
#define DWC3_VER_NUMBER 0xc1a0
#define DWC3_VER_TYPE 0xc1a4
#define DWC3_GUSB2PHYCFG(n) (0xc200 + (n * 0x04))
#define DWC3_GUSB2I2CCTL(n) (0xc240 + (n * 0x04))
#define DWC3_GUSB2PHYCFG(n) (0xc200 + ((n) * 0x04))
#define DWC3_GUSB2I2CCTL(n) (0xc240 + ((n) * 0x04))
#define DWC3_GUSB2PHYACC(n) (0xc280 + (n * 0x04))
#define DWC3_GUSB2PHYACC(n) (0xc280 + ((n) * 0x04))
#define DWC3_GUSB3PIPECTL(n) (0xc2c0 + (n * 0x04))
#define DWC3_GUSB3PIPECTL(n) (0xc2c0 + ((n) * 0x04))
#define DWC3_GTXFIFOSIZ(n) (0xc300 + (n * 0x04))
#define DWC3_GRXFIFOSIZ(n) (0xc380 + (n * 0x04))
#define DWC3_GTXFIFOSIZ(n) (0xc300 + ((n) * 0x04))
#define DWC3_GRXFIFOSIZ(n) (0xc380 + ((n) * 0x04))
#define DWC3_GEVNTADRLO(n) (0xc400 + (n * 0x10))
#define DWC3_GEVNTADRHI(n) (0xc404 + (n * 0x10))
#define DWC3_GEVNTSIZ(n) (0xc408 + (n * 0x10))
#define DWC3_GEVNTCOUNT(n) (0xc40c + (n * 0x10))
#define DWC3_GEVNTADRLO(n) (0xc400 + ((n) * 0x10))
#define DWC3_GEVNTADRHI(n) (0xc404 + ((n) * 0x10))
#define DWC3_GEVNTSIZ(n) (0xc408 + ((n) * 0x10))
#define DWC3_GEVNTCOUNT(n) (0xc40c + ((n) * 0x10))
#define DWC3_GHWPARAMS8 0xc600
#define DWC3_GFLADJ 0xc630
@ -143,13 +144,13 @@
#define DWC3_DGCMD 0xc714
#define DWC3_DALEPENA 0xc720
#define DWC3_DEP_BASE(n) (0xc800 + (n * 0x10))
#define DWC3_DEP_BASE(n) (0xc800 + ((n) * 0x10))
#define DWC3_DEPCMDPAR2 0x00
#define DWC3_DEPCMDPAR1 0x04
#define DWC3_DEPCMDPAR0 0x08
#define DWC3_DEPCMD 0x0c
#define DWC3_DEV_IMOD(n) (0xca00 + (n * 0x4))
#define DWC3_DEV_IMOD(n) (0xca00 + ((n) * 0x4))
/* OTG Registers */
#define DWC3_OCFG 0xcc00
@ -176,11 +177,11 @@
/* Global RX Threshold Configuration Register */
#define DWC3_GRXTHRCFG_MAXRXBURSTSIZE(n) (((n) & 0x1f) << 19)
#define DWC3_GRXTHRCFG_RXPKTCNT(n) (((n) & 0xf) << 24)
#define DWC3_GRXTHRCFG_PKTCNTSEL (1 << 29)
#define DWC3_GRXTHRCFG_PKTCNTSEL BIT(29)
/* Global Configuration Register */
#define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19)
#define DWC3_GCTL_U2RSTECN (1 << 16)
#define DWC3_GCTL_U2RSTECN BIT(16)
#define DWC3_GCTL_RAMCLKSEL(x) (((x) & DWC3_GCTL_CLK_MASK) << 6)
#define DWC3_GCTL_CLK_BUS (0)
#define DWC3_GCTL_CLK_PIPE (1)
@ -193,24 +194,24 @@
#define DWC3_GCTL_PRTCAP_DEVICE 2
#define DWC3_GCTL_PRTCAP_OTG 3
#define DWC3_GCTL_CORESOFTRESET (1 << 11)
#define DWC3_GCTL_SOFITPSYNC (1 << 10)
#define DWC3_GCTL_CORESOFTRESET BIT(11)
#define DWC3_GCTL_SOFITPSYNC BIT(10)
#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)
#define DWC3_GCTL_DISSCRAMBLE BIT(3)
#define DWC3_GCTL_U2EXIT_LFPS BIT(2)
#define DWC3_GCTL_GBLHIBERNATIONEN BIT(1)
#define DWC3_GCTL_DSBLCLKGTNG BIT(0)
/* Global User Control 1 Register */
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW (1 << 24)
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
/* Global USB2 PHY Configuration Register */
#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
#define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS (1 << 30)
#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6)
#define DWC3_GUSB2PHYCFG_ULPI_UTMI (1 << 4)
#define DWC3_GUSB2PHYCFG_ENBLSLPM (1 << 8)
#define DWC3_GUSB2PHYCFG_PHYSOFTRST BIT(31)
#define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS BIT(30)
#define DWC3_GUSB2PHYCFG_SUSPHY BIT(6)
#define DWC3_GUSB2PHYCFG_ULPI_UTMI BIT(4)
#define DWC3_GUSB2PHYCFG_ENBLSLPM BIT(8)
#define DWC3_GUSB2PHYCFG_PHYIF(n) (n << 3)
#define DWC3_GUSB2PHYCFG_PHYIF_MASK DWC3_GUSB2PHYCFG_PHYIF(1)
#define DWC3_GUSB2PHYCFG_USBTRDTIM(n) (n << 10)
@ -221,25 +222,26 @@
#define UTMI_PHYIF_8_BIT 0
/* Global USB2 PHY Vendor Control Register */
#define DWC3_GUSB2PHYACC_NEWREGREQ (1 << 25)
#define DWC3_GUSB2PHYACC_BUSY (1 << 23)
#define DWC3_GUSB2PHYACC_WRITE (1 << 22)
#define DWC3_GUSB2PHYACC_NEWREGREQ BIT(25)
#define DWC3_GUSB2PHYACC_BUSY BIT(23)
#define DWC3_GUSB2PHYACC_WRITE BIT(22)
#define DWC3_GUSB2PHYACC_ADDR(n) (n << 16)
#define DWC3_GUSB2PHYACC_EXTEND_ADDR(n) (n << 8)
#define DWC3_GUSB2PHYACC_DATA(n) (n & 0xff)
/* Global USB3 PIPE Control Register */
#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
#define DWC3_GUSB3PIPECTL_U2SSINP3OK (1 << 29)
#define DWC3_GUSB3PIPECTL_DISRXDETINP3 (1 << 28)
#define DWC3_GUSB3PIPECTL_REQP1P2P3 (1 << 24)
#define DWC3_GUSB3PIPECTL_PHYSOFTRST BIT(31)
#define DWC3_GUSB3PIPECTL_U2SSINP3OK BIT(29)
#define DWC3_GUSB3PIPECTL_DISRXDETINP3 BIT(28)
#define DWC3_GUSB3PIPECTL_UX_EXIT_PX BIT(27)
#define DWC3_GUSB3PIPECTL_REQP1P2P3 BIT(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_DEPOCHANGE BIT(18)
#define DWC3_GUSB3PIPECTL_SUSPHY BIT(17)
#define DWC3_GUSB3PIPECTL_LFPSFILT BIT(9)
#define DWC3_GUSB3PIPECTL_RX_DETOPOLL BIT(8)
#define DWC3_GUSB3PIPECTL_TX_DEEPH_MASK DWC3_GUSB3PIPECTL_TX_DEEPH(3)
#define DWC3_GUSB3PIPECTL_TX_DEEPH(n) ((n) << 1)
@ -248,7 +250,7 @@
#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000)
/* Global Event Size Registers */
#define DWC3_GEVNTSIZ_INTMASK (1 << 31)
#define DWC3_GEVNTSIZ_INTMASK BIT(31)
#define DWC3_GEVNTSIZ_SIZE(n) ((n) & 0xffff)
/* Global HWPARAMS0 Register */
@ -289,18 +291,18 @@
#define DWC3_MAX_HIBER_SCRATCHBUFS 15
/* Global HWPARAMS6 Register */
#define DWC3_GHWPARAMS6_EN_FPGA (1 << 7)
#define DWC3_GHWPARAMS6_EN_FPGA BIT(7)
/* Global HWPARAMS7 Register */
#define DWC3_GHWPARAMS7_RAM1_DEPTH(n) ((n) & 0xffff)
#define DWC3_GHWPARAMS7_RAM2_DEPTH(n) (((n) >> 16) & 0xffff)
/* Global Frame Length Adjustment Register */
#define DWC3_GFLADJ_30MHZ_SDBND_SEL (1 << 7)
#define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7)
#define DWC3_GFLADJ_30MHZ_MASK 0x3f
/* Global User Control Register 2 */
#define DWC3_GUCTL2_RST_ACTBITLATER (1 << 14)
#define DWC3_GUCTL2_RST_ACTBITLATER BIT(14)
/* Device Configuration Register */
#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
@ -310,23 +312,23 @@
#define DWC3_DCFG_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */
#define DWC3_DCFG_SUPERSPEED (4 << 0)
#define DWC3_DCFG_HIGHSPEED (0 << 0)
#define DWC3_DCFG_FULLSPEED (1 << 0)
#define DWC3_DCFG_FULLSPEED BIT(0)
#define DWC3_DCFG_LOWSPEED (2 << 0)
#define DWC3_DCFG_NUMP_SHIFT 17
#define DWC3_DCFG_NUMP(n) (((n) >> DWC3_DCFG_NUMP_SHIFT) & 0x1f)
#define DWC3_DCFG_NUMP_MASK (0x1f << DWC3_DCFG_NUMP_SHIFT)
#define DWC3_DCFG_LPM_CAP (1 << 22)
#define DWC3_DCFG_LPM_CAP BIT(22)
/* Device Control Register */
#define DWC3_DCTL_RUN_STOP (1 << 31)
#define DWC3_DCTL_CSFTRST (1 << 30)
#define DWC3_DCTL_LSFTRST (1 << 29)
#define DWC3_DCTL_RUN_STOP BIT(31)
#define DWC3_DCTL_CSFTRST BIT(30)
#define DWC3_DCTL_LSFTRST BIT(29)
#define DWC3_DCTL_HIRD_THRES_MASK (0x1f << 24)
#define DWC3_DCTL_HIRD_THRES(n) ((n) << 24)
#define DWC3_DCTL_APPL1RES (1 << 23)
#define DWC3_DCTL_APPL1RES BIT(23)
/* These apply for core versions 1.87a and earlier */
#define DWC3_DCTL_TRGTULST_MASK (0x0f << 17)
@ -341,15 +343,15 @@
#define DWC3_DCTL_LPM_ERRATA_MASK DWC3_DCTL_LPM_ERRATA(0xf)
#define DWC3_DCTL_LPM_ERRATA(n) ((n) << 20)
#define DWC3_DCTL_KEEP_CONNECT (1 << 19)
#define DWC3_DCTL_L1_HIBER_EN (1 << 18)
#define DWC3_DCTL_CRS (1 << 17)
#define DWC3_DCTL_CSS (1 << 16)
#define DWC3_DCTL_KEEP_CONNECT BIT(19)
#define DWC3_DCTL_L1_HIBER_EN BIT(18)
#define DWC3_DCTL_CRS BIT(17)
#define DWC3_DCTL_CSS BIT(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_INITU2ENA BIT(12)
#define DWC3_DCTL_ACCEPTU2ENA BIT(11)
#define DWC3_DCTL_INITU1ENA BIT(10)
#define DWC3_DCTL_ACCEPTU1ENA BIT(9)
#define DWC3_DCTL_TSTCTRL_MASK (0xf << 1)
#define DWC3_DCTL_ULSTCHNGREQ_MASK (0x0f << 5)
@ -364,36 +366,36 @@
#define DWC3_DCTL_ULSTCHNG_LOOPBACK (DWC3_DCTL_ULSTCHNGREQ(11))
/* Device Event Enable Register */
#define DWC3_DEVTEN_VNDRDEVTSTRCVEDEN (1 << 12)
#define DWC3_DEVTEN_EVNTOVERFLOWEN (1 << 11)
#define DWC3_DEVTEN_CMDCMPLTEN (1 << 10)
#define DWC3_DEVTEN_ERRTICERREN (1 << 9)
#define DWC3_DEVTEN_SOFEN (1 << 7)
#define DWC3_DEVTEN_EOPFEN (1 << 6)
#define DWC3_DEVTEN_HIBERNATIONREQEVTEN (1 << 5)
#define DWC3_DEVTEN_WKUPEVTEN (1 << 4)
#define DWC3_DEVTEN_ULSTCNGEN (1 << 3)
#define DWC3_DEVTEN_CONNECTDONEEN (1 << 2)
#define DWC3_DEVTEN_USBRSTEN (1 << 1)
#define DWC3_DEVTEN_DISCONNEVTEN (1 << 0)
#define DWC3_DEVTEN_VNDRDEVTSTRCVEDEN BIT(12)
#define DWC3_DEVTEN_EVNTOVERFLOWEN BIT(11)
#define DWC3_DEVTEN_CMDCMPLTEN BIT(10)
#define DWC3_DEVTEN_ERRTICERREN BIT(9)
#define DWC3_DEVTEN_SOFEN BIT(7)
#define DWC3_DEVTEN_EOPFEN BIT(6)
#define DWC3_DEVTEN_HIBERNATIONREQEVTEN BIT(5)
#define DWC3_DEVTEN_WKUPEVTEN BIT(4)
#define DWC3_DEVTEN_ULSTCNGEN BIT(3)
#define DWC3_DEVTEN_CONNECTDONEEN BIT(2)
#define DWC3_DEVTEN_USBRSTEN BIT(1)
#define DWC3_DEVTEN_DISCONNEVTEN BIT(0)
/* Device Status Register */
#define DWC3_DSTS_DCNRD (1 << 29)
#define DWC3_DSTS_DCNRD BIT(29)
/* This applies for core versions 1.87a and earlier */
#define DWC3_DSTS_PWRUPREQ (1 << 24)
#define DWC3_DSTS_PWRUPREQ BIT(24)
/* These apply for core versions 1.94a and later */
#define DWC3_DSTS_RSS (1 << 25)
#define DWC3_DSTS_SSS (1 << 24)
#define DWC3_DSTS_RSS BIT(25)
#define DWC3_DSTS_SSS BIT(24)
#define DWC3_DSTS_COREIDLE (1 << 23)
#define DWC3_DSTS_DEVCTRLHLT (1 << 22)
#define DWC3_DSTS_COREIDLE BIT(23)
#define DWC3_DSTS_DEVCTRLHLT BIT(22)
#define DWC3_DSTS_USBLNKST_MASK (0x0f << 18)
#define DWC3_DSTS_USBLNKST(n) (((n) & DWC3_DSTS_USBLNKST_MASK) >> 18)
#define DWC3_DSTS_RXFIFOEMPTY (1 << 17)
#define DWC3_DSTS_RXFIFOEMPTY BIT(17)
#define DWC3_DSTS_SOFFN_MASK (0x3fff << 3)
#define DWC3_DSTS_SOFFN(n) (((n) & DWC3_DSTS_SOFFN_MASK) >> 3)
@ -403,7 +405,7 @@
#define DWC3_DSTS_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */
#define DWC3_DSTS_SUPERSPEED (4 << 0)
#define DWC3_DSTS_HIGHSPEED (0 << 0)
#define DWC3_DSTS_FULLSPEED (1 << 0)
#define DWC3_DSTS_FULLSPEED BIT(0)
#define DWC3_DSTS_LOWSPEED (2 << 0)
/* Device Generic Command Register */
@ -421,26 +423,26 @@
#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10
#define DWC3_DGCMD_STATUS(n) (((n) >> 12) & 0x0F)
#define DWC3_DGCMD_CMDACT (1 << 10)
#define DWC3_DGCMD_CMDIOC (1 << 8)
#define DWC3_DGCMD_CMDACT BIT(10)
#define DWC3_DGCMD_CMDIOC BIT(8)
/* Device Generic Command Parameter Register */
#define DWC3_DGCMDPAR_FORCE_LINKPM_ACCEPT (1 << 0)
#define DWC3_DGCMDPAR_FORCE_LINKPM_ACCEPT BIT(0)
#define DWC3_DGCMDPAR_FIFO_NUM(n) ((n) << 0)
#define DWC3_DGCMDPAR_RX_FIFO (0 << 5)
#define DWC3_DGCMDPAR_TX_FIFO (1 << 5)
#define DWC3_DGCMDPAR_TX_FIFO BIT(5)
#define DWC3_DGCMDPAR_LOOPBACK_DIS (0 << 0)
#define DWC3_DGCMDPAR_LOOPBACK_ENA (1 << 0)
#define DWC3_DGCMDPAR_LOOPBACK_ENA BIT(0)
/* Device Endpoint Command Register */
#define DWC3_DEPCMD_PARAM_SHIFT 16
#define DWC3_DEPCMD_PARAM(x) ((x) << DWC3_DEPCMD_PARAM_SHIFT)
#define DWC3_DEPCMD_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f)
#define DWC3_DEPCMD_STATUS(x) (((x) >> 12) & 0x0F)
#define DWC3_DEPCMD_HIPRI_FORCERM (1 << 11)
#define DWC3_DEPCMD_CLEARPENDIN (1 << 11)
#define DWC3_DEPCMD_CMDACT (1 << 10)
#define DWC3_DEPCMD_CMDIOC (1 << 8)
#define DWC3_DEPCMD_HIPRI_FORCERM BIT(11)
#define DWC3_DEPCMD_CLEARPENDIN BIT(11)
#define DWC3_DEPCMD_CMDACT BIT(10)
#define DWC3_DEPCMD_CMDIOC BIT(8)
#define DWC3_DEPCMD_DEPSTARTCFG (0x09 << 0)
#define DWC3_DEPCMD_ENDTRANSFER (0x08 << 0)
@ -458,7 +460,7 @@
#define DWC3_DEPCMD_CMD(x) ((x) & 0xf)
/* The EP number goes 0..31 so ep0 is always out and ep1 is always in */
#define DWC3_DALEPENA_EP(n) (1 << n)
#define DWC3_DALEPENA_EP(n) BIT(n)
#define DWC3_DEPCMD_TYPE_CONTROL 0
#define DWC3_DEPCMD_TYPE_ISOC 1
@ -500,8 +502,8 @@ struct dwc3_event_buffer {
struct dwc3 *dwc;
};
#define DWC3_EP_FLAG_STALLED (1 << 0)
#define DWC3_EP_FLAG_WEDGED (1 << 1)
#define DWC3_EP_FLAG_STALLED BIT(0)
#define DWC3_EP_FLAG_WEDGED BIT(1)
#define DWC3_EP_DIRECTION_TX true
#define DWC3_EP_DIRECTION_RX false
@ -550,17 +552,17 @@ struct dwc3_ep {
u32 saved_state;
unsigned flags;
#define DWC3_EP_ENABLED (1 << 0)
#define DWC3_EP_STALL (1 << 1)
#define DWC3_EP_WEDGE (1 << 2)
#define DWC3_EP_BUSY (1 << 4)
#define DWC3_EP_PENDING_REQUEST (1 << 5)
#define DWC3_EP_MISSED_ISOC (1 << 6)
#define DWC3_EP_END_TRANSFER_PENDING (1 << 7)
#define DWC3_EP_TRANSFER_STARTED (1 << 8)
#define DWC3_EP_ENABLED BIT(0)
#define DWC3_EP_STALL BIT(1)
#define DWC3_EP_WEDGE BIT(2)
#define DWC3_EP_BUSY BIT(4)
#define DWC3_EP_PENDING_REQUEST BIT(5)
#define DWC3_EP_MISSED_ISOC BIT(6)
#define DWC3_EP_END_TRANSFER_PENDING BIT(7)
#define DWC3_EP_TRANSFER_STARTED BIT(8)
/* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN (1 << 31)
#define DWC3_EP0_DIR_IN BIT(31)
/*
* IMPORTANT: we *know* we have 256 TRBs in our @trb_pool, so we will
@ -638,13 +640,13 @@ enum dwc3_link_state {
#define DWC3_TRB_STS_XFER_IN_PROG 4
/* TRB Control */
#define DWC3_TRB_CTRL_HWO (1 << 0)
#define DWC3_TRB_CTRL_LST (1 << 1)
#define DWC3_TRB_CTRL_CHN (1 << 2)
#define DWC3_TRB_CTRL_CSP (1 << 3)
#define DWC3_TRB_CTRL_HWO BIT(0)
#define DWC3_TRB_CTRL_LST BIT(1)
#define DWC3_TRB_CTRL_CHN BIT(2)
#define DWC3_TRB_CTRL_CSP BIT(3)
#define DWC3_TRB_CTRL_TRBCTL(n) (((n) & 0x3f) << 4)
#define DWC3_TRB_CTRL_ISP_IMI (1 << 10)
#define DWC3_TRB_CTRL_IOC (1 << 11)
#define DWC3_TRB_CTRL_ISP_IMI BIT(10)
#define DWC3_TRB_CTRL_IOC BIT(11)
#define DWC3_TRB_CTRL_SID_SOFN(n) (((n) & 0xffff) << 14)
#define DWC3_TRBCTL_TYPE(n) ((n) & (0x3f << 4))
@ -746,6 +748,7 @@ struct dwc3_request {
unsigned direction:1;
unsigned mapped:1;
unsigned started:1;
unsigned zero:1;
};
/*
@ -758,15 +761,11 @@ struct dwc3_scratchpad_array {
/**
* struct dwc3 - representation of our controller
* @ctrl_req: usb control request which is used for ep0
* @drd_work - workqueue used for role swapping
* @ep0_trb: trb which is used for the ctrl_req
* @ep0_bounce: bounce buffer for ep0
* @zlp_buf: used when request->zero is set
* @setup_buf: used while precessing STD USB requests
* @ctrl_req_addr: dma address of ctrl_req
* @ep0_trb: dma address of ep0_trb
* @ep0_usb_req: dummy req used while handling STD USB requests
* @ep0_bounce_addr: dma address of ep0_bounce
* @scratch_addr: dma address of scratchbuf
* @ep0_in_setup: one control transfer is completed and enter setup phase
* @lock: for synchronizing
@ -784,6 +783,10 @@ struct dwc3_scratchpad_array {
* @maximum_speed: maximum speed requested (mainly for testing purposes)
* @revision: revision register contents
* @dr_mode: requested mode of operation
* @current_dr_role: current role of operation when in dual-role mode
* @desired_dr_role: desired role of operation when in dual-role mode
* @edev: extcon handle
* @edev_nb: extcon notifier
* @hsphy_mode: UTMI phy mode, one of following:
* - USBPHY_INTERFACE_MODE_UTMI
* - USBPHY_INTERFACE_MODE_UTMIW
@ -799,8 +802,7 @@ struct dwc3_scratchpad_array {
* @u2pel: parameter from Set SEL request.
* @u1sel: parameter from Set SEL request.
* @u1pel: parameter from Set SEL request.
* @num_out_eps: number of out endpoints
* @num_in_eps: number of in endpoints
* @num_eps: number of endpoints
* @ep0_next_event: hold the next expected event
* @ep0state: state of endpoint zero
* @link_state: link state
@ -858,17 +860,13 @@ struct dwc3_scratchpad_array {
* increments or 0 to disable.
*/
struct dwc3 {
struct usb_ctrlrequest *ctrl_req;
struct work_struct drd_work;
struct dwc3_trb *ep0_trb;
void *bounce;
void *ep0_bounce;
void *zlp_buf;
void *scratchbuf;
u8 *setup_buf;
dma_addr_t ctrl_req_addr;
dma_addr_t ep0_trb_addr;
dma_addr_t bounce_addr;
dma_addr_t ep0_bounce_addr;
dma_addr_t scratch_addr;
struct dwc3_request ep0_usb_req;
struct completion ep0_in_setup;
@ -900,6 +898,10 @@ struct dwc3 {
size_t regs_size;
enum usb_dr_mode dr_mode;
u32 current_dr_role;
u32 desired_dr_role;
struct extcon_dev *edev;
struct notifier_block edev_nb;
enum usb_phy_interface hsphy_mode;
u32 fladj;
@ -960,8 +962,7 @@ struct dwc3 {
u8 speed;
u8 num_out_eps;
u8 num_in_eps;
u8 num_eps;
struct dwc3_hwparams hwparams;
struct dentry *root;
@ -1010,7 +1011,7 @@ struct dwc3 {
u16 imod_interval;
};
/* -------------------------------------------------------------------------- */
#define work_to_dwc(w) (container_of((w), struct dwc3, drd_work))
/* -------------------------------------------------------------------------- */
@ -1054,13 +1055,13 @@ struct dwc3_event_depevt {
u32 status:4;
/* Within XferNotReady */
#define DEPEVT_STATUS_TRANSFER_ACTIVE (1 << 3)
#define DEPEVT_STATUS_TRANSFER_ACTIVE BIT(3)
/* Within XferComplete */
#define DEPEVT_STATUS_BUSERR (1 << 0)
#define DEPEVT_STATUS_SHORT (1 << 1)
#define DEPEVT_STATUS_IOC (1 << 2)
#define DEPEVT_STATUS_LST (1 << 3)
#define DEPEVT_STATUS_BUSERR BIT(0)
#define DEPEVT_STATUS_SHORT BIT(1)
#define DEPEVT_STATUS_IOC BIT(2)
#define DEPEVT_STATUS_LST BIT(3)
/* Stream event only */
#define DEPEVT_STREAMEVT_FOUND 1
@ -1221,6 +1222,16 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
{ return 0; }
#endif
#if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_drd_init(struct dwc3 *dwc);
void dwc3_drd_exit(struct dwc3 *dwc);
#else
static inline int dwc3_drd_init(struct dwc3 *dwc)
{ return 0; }
static inline void dwc3_drd_exit(struct dwc3 *dwc)
{ }
#endif
/* power management interface */
#if !IS_ENABLED(CONFIG_USB_DWC3_HOST)
int dwc3_gadget_suspend(struct dwc3 *dwc);

View File

@ -124,6 +124,34 @@ dwc3_gadget_link_string(enum dwc3_link_state link_state)
}
}
/**
* dwc3_trb_type_string - returns TRB type as a string
* @type: the type of the TRB
*/
static inline const char *dwc3_trb_type_string(unsigned int type)
{
switch (type) {
case DWC3_TRBCTL_NORMAL:
return "normal";
case DWC3_TRBCTL_CONTROL_SETUP:
return "setup";
case DWC3_TRBCTL_CONTROL_STATUS2:
return "status2";
case DWC3_TRBCTL_CONTROL_STATUS3:
return "status3";
case DWC3_TRBCTL_CONTROL_DATA:
return "data";
case DWC3_TRBCTL_ISOCHRONOUS_FIRST:
return "isoc-first";
case DWC3_TRBCTL_ISOCHRONOUS:
return "isoc";
case DWC3_TRBCTL_LINK_TRB:
return "link";
default:
return "UNKNOWN";
}
}
static inline const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
{
switch (state) {

View File

@ -300,7 +300,7 @@ static int dwc3_mode_show(struct seq_file *s, void *unused)
seq_printf(s, "device\n");
break;
case DWC3_GCTL_PRTCAP_OTG:
seq_printf(s, "OTG\n");
seq_printf(s, "otg\n");
break;
default:
seq_printf(s, "UNKNOWN %08x\n", DWC3_GCTL_PRTCAP(reg));
@ -319,7 +319,6 @@ static ssize_t dwc3_mode_write(struct file *file,
{
struct seq_file *s = file->private_data;
struct dwc3 *dwc = s->private;
unsigned long flags;
u32 mode = 0;
char buf[32];
@ -327,19 +326,16 @@ static ssize_t dwc3_mode_write(struct file *file,
return -EFAULT;
if (!strncmp(buf, "host", 4))
mode |= DWC3_GCTL_PRTCAP_HOST;
mode = DWC3_GCTL_PRTCAP_HOST;
if (!strncmp(buf, "device", 6))
mode |= DWC3_GCTL_PRTCAP_DEVICE;
mode = DWC3_GCTL_PRTCAP_DEVICE;
if (!strncmp(buf, "otg", 3))
mode |= DWC3_GCTL_PRTCAP_OTG;
mode = DWC3_GCTL_PRTCAP_OTG;
dwc3_set_mode(dwc, mode);
if (mode) {
spin_lock_irqsave(&dwc->lock, flags);
dwc3_set_mode(dwc, mode);
spin_unlock_irqrestore(&dwc->lock, flags);
}
return count;
}
@ -446,52 +442,7 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused)
state = DWC3_DSTS_USBLNKST(reg);
spin_unlock_irqrestore(&dwc->lock, flags);
switch (state) {
case DWC3_LINK_STATE_U0:
seq_printf(s, "U0\n");
break;
case DWC3_LINK_STATE_U1:
seq_printf(s, "U1\n");
break;
case DWC3_LINK_STATE_U2:
seq_printf(s, "U2\n");
break;
case DWC3_LINK_STATE_U3:
seq_printf(s, "U3\n");
break;
case DWC3_LINK_STATE_SS_DIS:
seq_printf(s, "SS.Disabled\n");
break;
case DWC3_LINK_STATE_RX_DET:
seq_printf(s, "Rx.Detect\n");
break;
case DWC3_LINK_STATE_SS_INACT:
seq_printf(s, "SS.Inactive\n");
break;
case DWC3_LINK_STATE_POLL:
seq_printf(s, "Poll\n");
break;
case DWC3_LINK_STATE_RECOV:
seq_printf(s, "Recovery\n");
break;
case DWC3_LINK_STATE_HRESET:
seq_printf(s, "HRESET\n");
break;
case DWC3_LINK_STATE_CMPLY:
seq_printf(s, "Compliance\n");
break;
case DWC3_LINK_STATE_LPBK:
seq_printf(s, "Loopback\n");
break;
case DWC3_LINK_STATE_RESET:
seq_printf(s, "Reset\n");
break;
case DWC3_LINK_STATE_RESUME:
seq_printf(s, "Resume\n");
break;
default:
seq_printf(s, "UNKNOWN %d\n", state);
}
seq_printf(s, "%s\n", dwc3_gadget_link_string(state));
return 0;
}
@ -689,30 +640,6 @@ out:
return 0;
}
static inline const char *dwc3_trb_type_string(struct dwc3_trb *trb)
{
switch (DWC3_TRBCTL_TYPE(trb->ctrl)) {
case DWC3_TRBCTL_NORMAL:
return "normal";
case DWC3_TRBCTL_CONTROL_SETUP:
return "control-setup";
case DWC3_TRBCTL_CONTROL_STATUS2:
return "control-status2";
case DWC3_TRBCTL_CONTROL_STATUS3:
return "control-status3";
case DWC3_TRBCTL_CONTROL_DATA:
return "control-data";
case DWC3_TRBCTL_ISOCHRONOUS_FIRST:
return "isoc-first";
case DWC3_TRBCTL_ISOCHRONOUS:
return "isoc";
case DWC3_TRBCTL_LINK_TRB:
return "link";
default:
return "UNKNOWN";
}
}
static int dwc3_ep_trb_ring_show(struct seq_file *s, void *unused)
{
struct dwc3_ep *dep = s->private;
@ -733,10 +660,11 @@ static int dwc3_ep_trb_ring_show(struct seq_file *s, void *unused)
for (i = 0; i < DWC3_TRB_NUM; i++) {
struct dwc3_trb *trb = &dep->trb_pool[i];
unsigned int type = DWC3_TRBCTL_TYPE(trb->ctrl);
seq_printf(s, "%08x%08x,%d,%s,%d,%d,%d,%d,%d,%d\n",
trb->bph, trb->bpl, trb->size,
dwc3_trb_type_string(trb),
dwc3_trb_type_string(type),
!!(trb->ctrl & DWC3_TRB_CTRL_IOC),
!!(trb->ctrl & DWC3_TRB_CTRL_ISP_IMI),
!!(trb->ctrl & DWC3_TRB_CTRL_CSP),
@ -822,19 +750,8 @@ static void dwc3_debugfs_create_endpoint_dirs(struct dwc3 *dwc,
{
int i;
for (i = 0; i < dwc->num_in_eps; i++) {
u8 epnum = (i << 1) | 1;
struct dwc3_ep *dep = dwc->eps[epnum];
if (!dep)
continue;
dwc3_debugfs_create_endpoint_dir(dep, parent);
}
for (i = 0; i < dwc->num_out_eps; i++) {
u8 epnum = (i << 1);
struct dwc3_ep *dep = dwc->eps[epnum];
for (i = 0; i < dwc->num_eps; i++) {
struct dwc3_ep *dep = dwc->eps[i];
if (!dep)
continue;

85
drivers/usb/dwc3/drd.c Normal file
View File

@ -0,0 +1,85 @@
/**
* drd.c - DesignWare USB3 DRD Controller Dual-role support
*
* Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com
*
* Authors: Roger Quadros <rogerq@ti.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 of
* the License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/extcon.h>
#include "debug.h"
#include "core.h"
#include "gadget.h"
static void dwc3_drd_update(struct dwc3 *dwc)
{
int id;
id = extcon_get_state(dwc->edev, EXTCON_USB_HOST);
if (id < 0)
id = 0;
dwc3_set_mode(dwc, id ?
DWC3_GCTL_PRTCAP_HOST :
DWC3_GCTL_PRTCAP_DEVICE);
}
static int dwc3_drd_notifier(struct notifier_block *nb,
unsigned long event, void *ptr)
{
struct dwc3 *dwc = container_of(nb, struct dwc3, edev_nb);
dwc3_set_mode(dwc, event ?
DWC3_GCTL_PRTCAP_HOST :
DWC3_GCTL_PRTCAP_DEVICE);
return NOTIFY_DONE;
}
int dwc3_drd_init(struct dwc3 *dwc)
{
int ret;
if (dwc->dev->of_node) {
if (of_property_read_bool(dwc->dev->of_node, "extcon"))
dwc->edev = extcon_get_edev_by_phandle(dwc->dev, 0);
if (IS_ERR(dwc->edev))
return PTR_ERR(dwc->edev);
dwc->edev_nb.notifier_call = dwc3_drd_notifier;
ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST,
&dwc->edev_nb);
if (ret < 0) {
dev_err(dwc->dev, "couldn't register cable notifier\n");
return ret;
}
}
dwc3_drd_update(dwc);
return 0;
}
void dwc3_drd_exit(struct dwc3 *dwc)
{
extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST,
&dwc->edev_nb);
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
flush_work(&dwc->drd_work);
dwc3_gadget_exit(dwc);
}

View File

@ -147,53 +147,53 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
exynos->vdd33 = devm_regulator_get(dev, "vdd33");
if (IS_ERR(exynos->vdd33)) {
ret = PTR_ERR(exynos->vdd33);
goto err2;
goto vdd33_err;
}
ret = regulator_enable(exynos->vdd33);
if (ret) {
dev_err(dev, "Failed to enable VDD33 supply\n");
goto err2;
goto vdd33_err;
}
exynos->vdd10 = devm_regulator_get(dev, "vdd10");
if (IS_ERR(exynos->vdd10)) {
ret = PTR_ERR(exynos->vdd10);
goto err3;
goto vdd10_err;
}
ret = regulator_enable(exynos->vdd10);
if (ret) {
dev_err(dev, "Failed to enable VDD10 supply\n");
goto err3;
goto vdd10_err;
}
ret = dwc3_exynos_register_phys(exynos);
if (ret) {
dev_err(dev, "couldn't register PHYs\n");
goto err4;
goto phys_err;
}
if (node) {
ret = of_platform_populate(node, NULL, NULL, dev);
if (ret) {
dev_err(dev, "failed to add dwc3 core\n");
goto err5;
goto populate_err;
}
} else {
dev_err(dev, "no device node, failed to add dwc3 core\n");
ret = -ENODEV;
goto err5;
goto populate_err;
}
return 0;
err5:
populate_err:
platform_device_unregister(exynos->usb2_phy);
platform_device_unregister(exynos->usb3_phy);
err4:
phys_err:
regulator_disable(exynos->vdd10);
err3:
vdd10_err:
regulator_disable(exynos->vdd33);
err2:
vdd33_err:
clk_disable_unprepare(exynos->axius_clk);
axius_clk_err:
clk_disable_unprepare(exynos->susp_clk);

View File

@ -79,40 +79,40 @@
#define USBOTGSS_DEBUG_OFFSET 0x0600
/* SYSCONFIG REGISTER */
#define USBOTGSS_SYSCONFIG_DMADISABLE (1 << 16)
#define USBOTGSS_SYSCONFIG_DMADISABLE BIT(16)
/* IRQ_EOI REGISTER */
#define USBOTGSS_IRQ_EOI_LINE_NUMBER (1 << 0)
#define USBOTGSS_IRQ_EOI_LINE_NUMBER BIT(0)
/* IRQS0 BITS */
#define USBOTGSS_IRQO_COREIRQ_ST (1 << 0)
#define USBOTGSS_IRQO_COREIRQ_ST BIT(0)
/* IRQMISC BITS */
#define USBOTGSS_IRQMISC_DMADISABLECLR (1 << 17)
#define USBOTGSS_IRQMISC_OEVT (1 << 16)
#define USBOTGSS_IRQMISC_DRVVBUS_RISE (1 << 13)
#define USBOTGSS_IRQMISC_CHRGVBUS_RISE (1 << 12)
#define USBOTGSS_IRQMISC_DISCHRGVBUS_RISE (1 << 11)
#define USBOTGSS_IRQMISC_IDPULLUP_RISE (1 << 8)
#define USBOTGSS_IRQMISC_DRVVBUS_FALL (1 << 5)
#define USBOTGSS_IRQMISC_CHRGVBUS_FALL (1 << 4)
#define USBOTGSS_IRQMISC_DISCHRGVBUS_FALL (1 << 3)
#define USBOTGSS_IRQMISC_IDPULLUP_FALL (1 << 0)
#define USBOTGSS_IRQMISC_DMADISABLECLR BIT(17)
#define USBOTGSS_IRQMISC_OEVT BIT(16)
#define USBOTGSS_IRQMISC_DRVVBUS_RISE BIT(13)
#define USBOTGSS_IRQMISC_CHRGVBUS_RISE BIT(12)
#define USBOTGSS_IRQMISC_DISCHRGVBUS_RISE BIT(11)
#define USBOTGSS_IRQMISC_IDPULLUP_RISE BIT(8)
#define USBOTGSS_IRQMISC_DRVVBUS_FALL BIT(5)
#define USBOTGSS_IRQMISC_CHRGVBUS_FALL BIT(4)
#define USBOTGSS_IRQMISC_DISCHRGVBUS_FALL BIT(3)
#define USBOTGSS_IRQMISC_IDPULLUP_FALL BIT(0)
/* UTMI_OTG_STATUS REGISTER */
#define USBOTGSS_UTMI_OTG_STATUS_DRVVBUS (1 << 5)
#define USBOTGSS_UTMI_OTG_STATUS_CHRGVBUS (1 << 4)
#define USBOTGSS_UTMI_OTG_STATUS_DISCHRGVBUS (1 << 3)
#define USBOTGSS_UTMI_OTG_STATUS_IDPULLUP (1 << 0)
#define USBOTGSS_UTMI_OTG_STATUS_DRVVBUS BIT(5)
#define USBOTGSS_UTMI_OTG_STATUS_CHRGVBUS BIT(4)
#define USBOTGSS_UTMI_OTG_STATUS_DISCHRGVBUS BIT(3)
#define USBOTGSS_UTMI_OTG_STATUS_IDPULLUP BIT(0)
/* UTMI_OTG_CTRL REGISTER */
#define USBOTGSS_UTMI_OTG_CTRL_SW_MODE (1 << 31)
#define USBOTGSS_UTMI_OTG_CTRL_POWERPRESENT (1 << 9)
#define USBOTGSS_UTMI_OTG_CTRL_TXBITSTUFFENABLE (1 << 8)
#define USBOTGSS_UTMI_OTG_CTRL_IDDIG (1 << 4)
#define USBOTGSS_UTMI_OTG_CTRL_SESSEND (1 << 3)
#define USBOTGSS_UTMI_OTG_CTRL_SESSVALID (1 << 2)
#define USBOTGSS_UTMI_OTG_CTRL_VBUSVALID (1 << 1)
#define USBOTGSS_UTMI_OTG_CTRL_SW_MODE BIT(31)
#define USBOTGSS_UTMI_OTG_CTRL_POWERPRESENT BIT(9)
#define USBOTGSS_UTMI_OTG_CTRL_TXBITSTUFFENABLE BIT(8)
#define USBOTGSS_UTMI_OTG_CTRL_IDDIG BIT(4)
#define USBOTGSS_UTMI_OTG_CTRL_SESSEND BIT(3)
#define USBOTGSS_UTMI_OTG_CTRL_SESSVALID BIT(2)
#define USBOTGSS_UTMI_OTG_CTRL_VBUSVALID BIT(1)
struct dwc3_omap {
struct device *dev;

View File

@ -39,14 +39,13 @@ static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep);
static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
struct dwc3_ep *dep, struct dwc3_request *req);
static void dwc3_ep0_prepare_one_trb(struct dwc3 *dwc, u8 epnum,
static void dwc3_ep0_prepare_one_trb(struct dwc3_ep *dep,
dma_addr_t buf_dma, u32 len, u32 type, bool chain)
{
struct dwc3_trb *trb;
struct dwc3_ep *dep;
dep = dwc->eps[epnum];
struct dwc3 *dwc;
dwc = dep->dwc;
trb = &dwc->ep0_trb[dep->trb_enqueue];
if (chain)
@ -69,16 +68,17 @@ static void dwc3_ep0_prepare_one_trb(struct dwc3 *dwc, u8 epnum,
trace_dwc3_prepare_trb(dep, trb);
}
static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum)
static int dwc3_ep0_start_trans(struct dwc3_ep *dep)
{
struct dwc3_gadget_ep_cmd_params params;
struct dwc3_ep *dep;
struct dwc3 *dwc;
int ret;
dep = dwc->eps[epnum];
if (dep->flags & DWC3_EP_BUSY)
return 0;
dwc = dep->dwc;
memset(&params, 0, sizeof(params));
params.param0 = upper_32_bits(dwc->ep0_trb_addr);
params.param1 = lower_32_bits(dwc->ep0_trb_addr);
@ -279,13 +279,15 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
void dwc3_ep0_out_start(struct dwc3 *dwc)
{
struct dwc3_ep *dep;
int ret;
complete(&dwc->ep0_in_setup);
dwc3_ep0_prepare_one_trb(dwc, 0, dwc->ctrl_req_addr, 8,
dep = dwc->eps[0];
dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 8,
DWC3_TRBCTL_CONTROL_SETUP, false);
ret = dwc3_ep0_start_trans(dwc, 0);
ret = dwc3_ep0_start_trans(dep);
WARN_ON(ret < 0);
}
@ -794,7 +796,7 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
struct usb_ctrlrequest *ctrl = dwc->ctrl_req;
struct usb_ctrlrequest *ctrl = (void *) dwc->ep0_trb;
int ret = -EINVAL;
u32 len;
@ -834,7 +836,6 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
struct usb_request *ur;
struct dwc3_trb *trb;
struct dwc3_ep *ep0;
unsigned transfer_size = 0;
unsigned maxp;
unsigned remaining_ur_length;
void *buf;
@ -847,9 +848,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
ep0 = dwc->eps[0];
dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS;
trb = dwc->ep0_trb;
trace_dwc3_complete_trb(ep0, trb);
r = next_request(&ep0->pending_list);
@ -870,58 +869,23 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
remaining_ur_length = ur->length;
length = trb->size & DWC3_TRB_SIZE_MASK;
maxp = ep0->endpoint.maxpacket;
if (dwc->ep0_bounced) {
/*
* Handle the first TRB before handling the bounce buffer if
* the request length is greater than the bounce buffer size
*/
if (ur->length > DWC3_EP0_BOUNCE_SIZE) {
transfer_size = ALIGN(ur->length - maxp, maxp);
transferred = transfer_size - length;
buf = (u8 *)buf + transferred;
ur->actual += transferred;
remaining_ur_length -= transferred;
trb++;
length = trb->size & DWC3_TRB_SIZE_MASK;
ep0->trb_enqueue = 0;
}
transfer_size = roundup((ur->length - transfer_size),
maxp);
transferred = min_t(u32, remaining_ur_length,
transfer_size - length);
memcpy(buf, dwc->ep0_bounce, transferred);
} else {
transferred = ur->length - length;
}
transferred = ur->length - length;
ur->actual += transferred;
if ((epnum & 1) && ur->actual < ur->length) {
/* for some reason we did not get everything out */
dwc3_ep0_stall_and_restart(dwc);
} else {
dwc3_gadget_giveback(ep0, r, 0);
if (IS_ALIGNED(ur->length, ep0->endpoint.maxpacket) &&
ur->length && ur->zero) {
int ret;
dwc->ep0_next_event = DWC3_EP0_COMPLETE;
dwc3_ep0_prepare_one_trb(dwc, epnum, dwc->ctrl_req_addr,
0, DWC3_TRBCTL_CONTROL_DATA, false);
ret = dwc3_ep0_start_trans(dwc, epnum);
WARN_ON(ret < 0);
}
if ((IS_ALIGNED(ur->length, ep0->endpoint.maxpacket) &&
ur->length && ur->zero) || dwc->ep0_bounced) {
trb++;
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
trace_dwc3_complete_trb(ep0, trb);
ep0->trb_enqueue = 0;
dwc->ep0_bounced = false;
}
if ((epnum & 1) && ur->actual < ur->length)
dwc3_ep0_stall_and_restart(dwc);
else
dwc3_gadget_giveback(ep0, r, 0);
}
static void dwc3_ep0_complete_status(struct dwc3 *dwc,
@ -997,14 +961,13 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
req->direction = !!dep->number;
if (req->request.length == 0) {
dwc3_ep0_prepare_one_trb(dwc, dep->number,
dwc->ctrl_req_addr, 0,
dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 0,
DWC3_TRBCTL_CONTROL_DATA, false);
ret = dwc3_ep0_start_trans(dwc, dep->number);
ret = dwc3_ep0_start_trans(dep);
} else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket)
&& (dep->number == 0)) {
u32 transfer_size = 0;
u32 maxpacket;
u32 rem;
ret = usb_gadget_map_request_by_dev(dwc->sysdev,
&req->request, dep->number);
@ -1012,36 +975,55 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
return;
maxpacket = dep->endpoint.maxpacket;
if (req->request.length > DWC3_EP0_BOUNCE_SIZE) {
transfer_size = ALIGN(req->request.length - maxpacket,
maxpacket);
dwc3_ep0_prepare_one_trb(dwc, dep->number,
req->request.dma,
transfer_size,
DWC3_TRBCTL_CONTROL_DATA,
true);
}
transfer_size = roundup((req->request.length - transfer_size),
maxpacket);
rem = req->request.length % maxpacket;
dwc->ep0_bounced = true;
dwc3_ep0_prepare_one_trb(dwc, dep->number,
dwc->ep0_bounce_addr, transfer_size,
DWC3_TRBCTL_CONTROL_DATA, false);
ret = dwc3_ep0_start_trans(dwc, dep->number);
/* prepare normal TRB */
dwc3_ep0_prepare_one_trb(dep, req->request.dma,
req->request.length,
DWC3_TRBCTL_CONTROL_DATA,
true);
/* Now prepare one extra TRB to align transfer size */
dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr,
maxpacket - rem,
DWC3_TRBCTL_CONTROL_DATA,
false);
ret = dwc3_ep0_start_trans(dep);
} else if (IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) &&
req->request.length && req->request.zero) {
u32 maxpacket;
u32 rem;
ret = usb_gadget_map_request_by_dev(dwc->sysdev,
&req->request, dep->number);
if (ret)
return;
maxpacket = dep->endpoint.maxpacket;
rem = req->request.length % maxpacket;
/* prepare normal TRB */
dwc3_ep0_prepare_one_trb(dep, req->request.dma,
req->request.length,
DWC3_TRBCTL_CONTROL_DATA,
true);
/* Now prepare one extra TRB to align transfer size */
dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr,
0, DWC3_TRBCTL_CONTROL_DATA,
false);
ret = dwc3_ep0_start_trans(dep);
} else {
ret = usb_gadget_map_request_by_dev(dwc->sysdev,
&req->request, dep->number);
if (ret)
return;
dwc3_ep0_prepare_one_trb(dwc, dep->number, req->request.dma,
dwc3_ep0_prepare_one_trb(dep, req->request.dma,
req->request.length, DWC3_TRBCTL_CONTROL_DATA,
false);
ret = dwc3_ep0_start_trans(dwc, dep->number);
ret = dwc3_ep0_start_trans(dep);
}
WARN_ON(ret < 0);
@ -1055,9 +1037,8 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
type = dwc->three_stage_setup ? DWC3_TRBCTL_CONTROL_STATUS3
: DWC3_TRBCTL_CONTROL_STATUS2;
dwc3_ep0_prepare_one_trb(dwc, dep->number,
dwc->ctrl_req_addr, 0, type, false);
return dwc3_ep0_start_trans(dwc, dep->number);
dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 0, type, false);
return dwc3_ep0_start_trans(dep);
}
static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)

View File

@ -171,7 +171,6 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
int status)
{
struct dwc3 *dwc = dep->dwc;
unsigned int unmap_after_complete = false;
req->started = false;
list_del(&req->list);
@ -181,19 +180,8 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
if (req->request.status == -EINPROGRESS)
req->request.status = status;
/*
* NOTICE we don't want to unmap before calling ->complete() if we're
* dealing with a bounced ep0 request. If we unmap it here, we would end
* up overwritting the contents of req->buf and this could confuse the
* gadget driver.
*/
if (dwc->ep0_bounced && dep->number <= 1) {
dwc->ep0_bounced = false;
unmap_after_complete = true;
} else {
usb_gadget_unmap_request_by_dev(dwc->sysdev,
&req->request, req->direction);
}
usb_gadget_unmap_request_by_dev(dwc->sysdev,
&req->request, req->direction);
trace_dwc3_gadget_giveback(req);
@ -201,10 +189,6 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
usb_gadget_giveback_request(&dep->endpoint, &req->request);
spin_lock(&dwc->lock);
if (unmap_after_complete)
usb_gadget_unmap_request_by_dev(dwc->sysdev,
&req->request, req->direction);
if (dep->number > 1)
pm_runtime_put(dwc->dev);
}
@ -1060,6 +1044,22 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
false, 0, req->request.stream_id,
req->request.short_not_ok,
req->request.no_interrupt);
} else if (req->request.zero && req->request.length &&
(IS_ALIGNED(req->request.length,dep->endpoint.maxpacket))) {
struct dwc3 *dwc = dep->dwc;
struct dwc3_trb *trb;
req->zero = true;
/* prepare normal TRB */
dwc3_prepare_one_trb(dep, req, true, 0);
/* Now prepare one extra TRB to handle ZLP */
trb = &dep->trb_pool[dep->trb_enqueue];
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
false, 0, req->request.stream_id,
req->request.short_not_ok,
req->request.no_interrupt);
} else {
dwc3_prepare_one_trb(dep, req, false, 0);
}
@ -1184,8 +1184,11 @@ static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
return;
}
/* 4 micro frames in the future */
uf = cur_uf + dep->interval * 4;
/*
* Schedule the first trb for one interval in the future or at
* least 4 microframes.
*/
uf = cur_uf + max_t(u32, 4, dep->interval);
__dwc3_gadget_kick_transfer(dep, uf);
}
@ -1272,31 +1275,6 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
return ret;
}
static void __dwc3_gadget_ep_zlp_complete(struct usb_ep *ep,
struct usb_request *request)
{
dwc3_gadget_ep_free_request(ep, request);
}
static int __dwc3_gadget_ep_queue_zlp(struct dwc3 *dwc, struct dwc3_ep *dep)
{
struct dwc3_request *req;
struct usb_request *request;
struct usb_ep *ep = &dep->endpoint;
request = dwc3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
if (!request)
return -ENOMEM;
request->length = 0;
request->buf = dwc->zlp_buf;
request->complete = __dwc3_gadget_ep_zlp_complete;
req = to_dwc3_request(request);
return __dwc3_gadget_ep_queue(dep, req);
}
static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
gfp_t gfp_flags)
{
@ -1310,17 +1288,6 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
spin_lock_irqsave(&dwc->lock, flags);
ret = __dwc3_gadget_ep_queue(dep, req);
/*
* Okay, here's the thing, if gadget driver has requested for a ZLP by
* setting request->zero, instead of doing magic, we will just queue an
* extra usb_request ourselves so that it gets handled the same way as
* any other request.
*/
if (ret == 0 && request->zero && request->length &&
(request->length % ep->maxpacket == 0))
ret = __dwc3_gadget_ep_queue_zlp(dwc, dep);
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@ -1400,7 +1367,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
dwc3_ep_inc_deq(dep);
}
if (r->unaligned) {
if (r->unaligned || r->zero) {
trb = r->trb + r->num_pending_sgs + 1;
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
dwc3_ep_inc_deq(dep);
@ -1411,7 +1378,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
dwc3_ep_inc_deq(dep);
if (r->unaligned) {
if (r->unaligned || r->zero) {
trb = r->trb + 1;
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
dwc3_ep_inc_deq(dep);
@ -2006,14 +1973,15 @@ static const struct usb_gadget_ops dwc3_gadget_ops = {
/* -------------------------------------------------------------------------- */
static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
u8 num, u32 direction)
static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 num)
{
struct dwc3_ep *dep;
u8 i;
u8 epnum;
for (i = 0; i < num; i++) {
u8 epnum = (i << 1) | (direction ? 1 : 0);
INIT_LIST_HEAD(&dwc->gadget.ep_list);
for (epnum = 0; epnum < num; epnum++) {
bool direction = epnum & 1;
dep = kzalloc(sizeof(*dep), GFP_KERNEL);
if (!dep)
@ -2021,12 +1989,12 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
dep->dwc = dwc;
dep->number = epnum;
dep->direction = !!direction;
dep->direction = direction;
dep->regs = dwc->regs + DWC3_DEP_BASE(epnum);
dwc->eps[epnum] = dep;
snprintf(dep->name, sizeof(dep->name), "ep%d%s", epnum >> 1,
(epnum & 1) ? "in" : "out");
direction ? "in" : "out");
dep->endpoint.name = dep->name;
@ -2053,7 +2021,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
/* MDWIDTH is represented in bits, we need it in bytes */
mdwidth /= 8;
size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(i));
size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(epnum >> 1));
size = DWC3_GTXFIFOSIZ_TXFDEF(size);
/* FIFO Depth is in MDWDITH bytes. Multiply */
@ -2103,7 +2071,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
dep->endpoint.caps.type_int = true;
}
dep->endpoint.caps.dir_in = !!direction;
dep->endpoint.caps.dir_in = direction;
dep->endpoint.caps.dir_out = !direction;
INIT_LIST_HEAD(&dep->pending_list);
@ -2113,27 +2081,6 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
return 0;
}
static int dwc3_gadget_init_endpoints(struct dwc3 *dwc)
{
int ret;
INIT_LIST_HEAD(&dwc->gadget.ep_list);
ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_out_eps, 0);
if (ret < 0) {
dev_err(dwc->dev, "failed to initialize OUT endpoints\n");
return ret;
}
ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_in_eps, 1);
if (ret < 0) {
dev_err(dwc->dev, "failed to initialize IN endpoints\n");
return ret;
}
return 0;
}
static void dwc3_gadget_free_endpoints(struct dwc3 *dwc)
{
struct dwc3_ep *dep;
@ -2197,7 +2144,7 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
* with one TRB pending in the ring. We need to manually clear HWO bit
* from that TRB.
*/
if (req->unaligned && (trb->ctrl & DWC3_TRB_CTRL_HWO)) {
if ((req->zero || req->unaligned) && (trb->ctrl & DWC3_TRB_CTRL_HWO)) {
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
return 1;
}
@ -2291,11 +2238,12 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
event, status, chain);
}
if (req->unaligned) {
if (req->unaligned || req->zero) {
trb = &dep->trb_pool[dep->trb_dequeue];
ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
event, status, false);
req->unaligned = false;
req->zero = false;
}
req->request.actual = length - req->remaining;
@ -3161,49 +3109,26 @@ int dwc3_gadget_init(struct dwc3 *dwc)
dwc->irq_gadget = irq;
dwc->ctrl_req = dma_alloc_coherent(dwc->sysdev, sizeof(*dwc->ctrl_req),
&dwc->ctrl_req_addr, GFP_KERNEL);
if (!dwc->ctrl_req) {
dev_err(dwc->dev, "failed to allocate ctrl request\n");
ret = -ENOMEM;
goto err0;
}
dwc->ep0_trb = dma_alloc_coherent(dwc->sysdev,
sizeof(*dwc->ep0_trb) * 2,
&dwc->ep0_trb_addr, GFP_KERNEL);
if (!dwc->ep0_trb) {
dev_err(dwc->dev, "failed to allocate ep0 trb\n");
ret = -ENOMEM;
goto err1;
goto err0;
}
dwc->setup_buf = kzalloc(DWC3_EP0_BOUNCE_SIZE, GFP_KERNEL);
dwc->setup_buf = kzalloc(DWC3_EP0_SETUP_SIZE, GFP_KERNEL);
if (!dwc->setup_buf) {
ret = -ENOMEM;
goto err2;
}
dwc->ep0_bounce = dma_alloc_coherent(dwc->sysdev,
DWC3_EP0_BOUNCE_SIZE, &dwc->ep0_bounce_addr,
GFP_KERNEL);
if (!dwc->ep0_bounce) {
dev_err(dwc->dev, "failed to allocate ep0 bounce buffer\n");
ret = -ENOMEM;
goto err3;
}
dwc->zlp_buf = kzalloc(DWC3_ZLP_BUF_SIZE, GFP_KERNEL);
if (!dwc->zlp_buf) {
ret = -ENOMEM;
goto err4;
goto err1;
}
dwc->bounce = dma_alloc_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE,
&dwc->bounce_addr, GFP_KERNEL);
if (!dwc->bounce) {
ret = -ENOMEM;
goto err5;
goto err2;
}
init_completion(&dwc->ep0_in_setup);
@ -3241,39 +3166,31 @@ int dwc3_gadget_init(struct dwc3 *dwc)
* sure we're starting from a well known location.
*/
ret = dwc3_gadget_init_endpoints(dwc);
ret = dwc3_gadget_init_endpoints(dwc, dwc->num_eps);
if (ret)
goto err6;
goto err3;
ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
if (ret) {
dev_err(dwc->dev, "failed to register udc\n");
goto err6;
goto err4;
}
return 0;
err6:
dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
dwc->bounce_addr);
err5:
kfree(dwc->zlp_buf);
err4:
dwc3_gadget_free_endpoints(dwc);
dma_free_coherent(dwc->sysdev, DWC3_EP0_BOUNCE_SIZE,
dwc->ep0_bounce, dwc->ep0_bounce_addr);
err3:
kfree(dwc->setup_buf);
dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
dwc->bounce_addr);
err2:
dma_free_coherent(dwc->sysdev, sizeof(*dwc->ep0_trb) * 2,
dwc->ep0_trb, dwc->ep0_trb_addr);
kfree(dwc->setup_buf);
err1:
dma_free_coherent(dwc->sysdev, sizeof(*dwc->ctrl_req),
dwc->ctrl_req, dwc->ctrl_req_addr);
dma_free_coherent(dwc->sysdev, sizeof(*dwc->ep0_trb) * 2,
dwc->ep0_trb, dwc->ep0_trb_addr);
err0:
return ret;
@ -3284,22 +3201,12 @@ err0:
void dwc3_gadget_exit(struct dwc3 *dwc)
{
usb_del_gadget_udc(&dwc->gadget);
dwc3_gadget_free_endpoints(dwc);
dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
dwc->bounce_addr);
dma_free_coherent(dwc->sysdev, DWC3_EP0_BOUNCE_SIZE,
dwc->ep0_bounce, dwc->ep0_bounce_addr);
dwc->bounce_addr);
kfree(dwc->setup_buf);
kfree(dwc->zlp_buf);
dma_free_coherent(dwc->sysdev, sizeof(*dwc->ep0_trb) * 2,
dwc->ep0_trb, dwc->ep0_trb_addr);
dma_free_coherent(dwc->sysdev, sizeof(*dwc->ctrl_req),
dwc->ctrl_req, dwc->ctrl_req_addr);
dwc->ep0_trb, dwc->ep0_trb_addr);
}
int dwc3_gadget_suspend(struct dwc3 *dwc)

View File

@ -29,16 +29,16 @@ struct dwc3;
/* DEPCFG parameter 1 */
#define DWC3_DEPCFG_INT_NUM(n) (((n) & 0x1f) << 0)
#define DWC3_DEPCFG_XFER_COMPLETE_EN (1 << 8)
#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN (1 << 9)
#define DWC3_DEPCFG_XFER_NOT_READY_EN (1 << 10)
#define DWC3_DEPCFG_FIFO_ERROR_EN (1 << 11)
#define DWC3_DEPCFG_STREAM_EVENT_EN (1 << 13)
#define DWC3_DEPCFG_XFER_COMPLETE_EN BIT(8)
#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN BIT(9)
#define DWC3_DEPCFG_XFER_NOT_READY_EN BIT(10)
#define DWC3_DEPCFG_FIFO_ERROR_EN BIT(11)
#define DWC3_DEPCFG_STREAM_EVENT_EN BIT(12)
#define DWC3_DEPCFG_BINTERVAL_M1(n) (((n) & 0xff) << 16)
#define DWC3_DEPCFG_STREAM_CAPABLE (1 << 24)
#define DWC3_DEPCFG_STREAM_CAPABLE BIT(24)
#define DWC3_DEPCFG_EP_NUMBER(n) (((n) & 0x1f) << 25)
#define DWC3_DEPCFG_BULK_BASED (1 << 30)
#define DWC3_DEPCFG_FIFO_BASED (1 << 31)
#define DWC3_DEPCFG_BULK_BASED BIT(30)
#define DWC3_DEPCFG_FIFO_BASED BIT(31)
/* DEPCFG parameter 0 */
#define DWC3_DEPCFG_EP_TYPE(n) (((n) & 0x3) << 1)
@ -47,10 +47,10 @@ struct dwc3;
#define DWC3_DEPCFG_BURST_SIZE(n) (((n) & 0xf) << 22)
#define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26)
/* This applies for core versions earlier than 1.94a */
#define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31)
#define DWC3_DEPCFG_IGN_SEQ_NUM BIT(31)
/* These apply for core versions 1.94a and later */
#define DWC3_DEPCFG_ACTION_INIT (0 << 30)
#define DWC3_DEPCFG_ACTION_RESTORE (1 << 30)
#define DWC3_DEPCFG_ACTION_RESTORE BIT(30)
#define DWC3_DEPCFG_ACTION_MODIFY (2 << 30)
/* DEPXFERCFG parameter 0 */

View File

@ -27,31 +27,6 @@
#include "core.h"
#include "debug.h"
DECLARE_EVENT_CLASS(dwc3_log_msg,
TP_PROTO(struct va_format *vaf),
TP_ARGS(vaf),
TP_STRUCT__entry(__dynamic_array(char, msg, DWC3_MSG_MAX)),
TP_fast_assign(
vsnprintf(__get_str(msg), DWC3_MSG_MAX, vaf->fmt, *vaf->va);
),
TP_printk("%s", __get_str(msg))
);
DEFINE_EVENT(dwc3_log_msg, dwc3_gadget,
TP_PROTO(struct va_format *vaf),
TP_ARGS(vaf)
);
DEFINE_EVENT(dwc3_log_msg, dwc3_core,
TP_PROTO(struct va_format *vaf),
TP_ARGS(vaf)
);
DEFINE_EVENT(dwc3_log_msg, dwc3_ep0,
TP_PROTO(struct va_format *vaf),
TP_ARGS(vaf)
);
DECLARE_EVENT_CLASS(dwc3_log_io,
TP_PROTO(void *base, u32 offset, u32 value),
TP_ARGS(base, offset, value),
@ -198,7 +173,7 @@ DECLARE_EVENT_CLASS(dwc3_log_generic_cmd,
__entry->param = param;
__entry->status = status;
),
TP_printk("cmd '%s' [%d] param %08x --> status: %s",
TP_printk("cmd '%s' [%x] param %08x --> status: %s",
dwc3_gadget_generic_cmd_string(__entry->cmd),
__entry->cmd, __entry->param,
dwc3_gadget_generic_cmd_status_string(__entry->status)
@ -298,36 +273,7 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
__entry->ctrl & DWC3_TRB_CTRL_CSP ? 'S' : 's',
__entry->ctrl & DWC3_TRB_CTRL_ISP_IMI ? 'S' : 's',
__entry->ctrl & DWC3_TRB_CTRL_IOC ? 'C' : 'c',
({char *s;
switch (__entry->ctrl & 0x3f0) {
case DWC3_TRBCTL_NORMAL:
s = "normal";
break;
case DWC3_TRBCTL_CONTROL_SETUP:
s = "setup";
break;
case DWC3_TRBCTL_CONTROL_STATUS2:
s = "status2";
break;
case DWC3_TRBCTL_CONTROL_STATUS3:
s = "status3";
break;
case DWC3_TRBCTL_CONTROL_DATA:
s = "data";
break;
case DWC3_TRBCTL_ISOCHRONOUS_FIRST:
s = "isoc-first";
break;
case DWC3_TRBCTL_ISOCHRONOUS:
s = "isoc";
break;
case DWC3_TRBCTL_LINK_TRB:
s = "link";
break;
default:
s = "UNKNOWN";
break;
} s; })
dwc3_trb_type_string(DWC3_TRBCTL_TYPE(__entry->ctrl))
)
);

View File

@ -212,7 +212,7 @@ config USB_F_TCM
# this first set of drivers all depend on bulk-capable hardware.
config USB_CONFIGFS
tristate "USB functions configurable through configfs"
tristate "USB Gadget functions configurable through configfs"
select USB_LIBCOMPOSITE
help
A Linux USB "gadget" can be set up through configfs.
@ -458,8 +458,9 @@ config USB_CONFIGFS_F_TCM
UAS utilizes the USB 3.0 feature called streams support.
choice
tristate "USB Gadget Drivers"
tristate "USB Gadget precomposed configurations"
default USB_ETH
optional
help
A Linux "Gadget Driver" talks to the USB Peripheral Controller
driver through the abstract "gadget" API. Some other operating
@ -476,6 +477,12 @@ choice
not be able work with that controller, or might need to implement
a less common variant of a device class protocol.
The available choices each represent a single precomposed USB
gadget configuration. In the device model, each option contains
both the device instantiation as a child for a USB gadget
controller, and the relevant drivers for each function declared
by the device.
source "drivers/usb/gadget/legacy/Kconfig"
endchoice

View File

@ -246,7 +246,6 @@ EXPORT_SYMBOL_GPL(ffs_lock);
static struct ffs_dev *_ffs_find_dev(const char *name);
static struct ffs_dev *_ffs_alloc_dev(void);
static int _ffs_name_dev(struct ffs_dev *dev, const char *name);
static void _ffs_free_dev(struct ffs_dev *dev);
static void *ffs_acquire_dev(const char *dev_name);
static void ffs_release_dev(struct ffs_data *ffs_data);
@ -1571,14 +1570,14 @@ static void ffs_data_get(struct ffs_data *ffs)
{
ENTER();
atomic_inc(&ffs->ref);
refcount_inc(&ffs->ref);
}
static void ffs_data_opened(struct ffs_data *ffs)
{
ENTER();
atomic_inc(&ffs->ref);
refcount_inc(&ffs->ref);
if (atomic_add_return(1, &ffs->opened) == 1 &&
ffs->state == FFS_DEACTIVATED) {
ffs->state = FFS_CLOSING;
@ -1590,7 +1589,7 @@ static void ffs_data_put(struct ffs_data *ffs)
{
ENTER();
if (unlikely(atomic_dec_and_test(&ffs->ref))) {
if (unlikely(refcount_dec_and_test(&ffs->ref))) {
pr_info("%s(): freeing\n", __func__);
ffs_data_clear(ffs);
BUG_ON(waitqueue_active(&ffs->ev.waitq) ||
@ -1635,7 +1634,7 @@ static struct ffs_data *ffs_data_new(void)
ENTER();
atomic_set(&ffs->ref, 1);
refcount_set(&ffs->ref, 1);
atomic_set(&ffs->opened, 0);
ffs->state = FFS_READ_DESCRIPTORS;
mutex_init(&ffs->mutex);
@ -3302,9 +3301,10 @@ static struct ffs_dev *_ffs_do_find_dev(const char *name)
{
struct ffs_dev *dev;
if (!name)
return NULL;
list_for_each_entry(dev, &ffs_devices, entry) {
if (!dev->name || !name)
continue;
if (strcmp(dev->name, name) == 0)
return dev;
}
@ -3380,42 +3380,11 @@ static void ffs_free_inst(struct usb_function_instance *f)
kfree(opts);
}
#define MAX_INST_NAME_LEN 40
static int ffs_set_inst_name(struct usb_function_instance *fi, const char *name)
{
struct f_fs_opts *opts;
char *ptr;
const char *tmp;
int name_len, ret;
name_len = strlen(name) + 1;
if (name_len > MAX_INST_NAME_LEN)
if (strlen(name) >= FIELD_SIZEOF(struct ffs_dev, name))
return -ENAMETOOLONG;
ptr = kstrndup(name, name_len, GFP_KERNEL);
if (!ptr)
return -ENOMEM;
opts = to_f_fs_opts(fi);
tmp = NULL;
ffs_dev_lock();
tmp = opts->dev->name_allocated ? opts->dev->name : NULL;
ret = _ffs_name_dev(opts->dev, ptr);
if (ret) {
kfree(ptr);
ffs_dev_unlock();
return ret;
}
opts->dev->name_allocated = true;
ffs_dev_unlock();
kfree(tmp);
return 0;
return ffs_name_dev(to_f_fs_opts(fi)->dev, name);
}
static struct usb_function_instance *ffs_alloc_inst(void)
@ -3545,32 +3514,19 @@ static struct ffs_dev *_ffs_alloc_dev(void)
return dev;
}
/*
* ffs_lock must be taken by the caller of this function
* The caller is responsible for "name" being available whenever f_fs needs it
*/
static int _ffs_name_dev(struct ffs_dev *dev, const char *name)
{
struct ffs_dev *existing;
existing = _ffs_do_find_dev(name);
if (existing)
return -EBUSY;
dev->name = name;
return 0;
}
/*
* The caller is responsible for "name" being available whenever f_fs needs it
*/
int ffs_name_dev(struct ffs_dev *dev, const char *name)
{
int ret;
struct ffs_dev *existing;
int ret = 0;
ffs_dev_lock();
ret = _ffs_name_dev(dev, name);
existing = _ffs_do_find_dev(name);
if (!existing)
strlcpy(dev->name, name, ARRAY_SIZE(dev->name));
else if (existing != dev)
ret = -EBUSY;
ffs_dev_unlock();
return ret;
@ -3600,8 +3556,6 @@ EXPORT_SYMBOL_GPL(ffs_single_dev);
static void _ffs_free_dev(struct ffs_dev *dev)
{
list_del(&dev->entry);
if (dev->name_allocated)
kfree(dev->name);
/* Clear the private_data pointer to stop incorrect dev access */
if (dev->ffs_data)

View File

@ -178,6 +178,7 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req);
static int
rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
{
struct usb_gadget *g = dev->gadget;
struct sk_buff *skb;
int retval = -ENOMEM;
size_t size = 0;
@ -209,8 +210,11 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
*/
size += sizeof(struct ethhdr) + dev->net->mtu + RX_EXTRA;
size += dev->port_usb->header_len;
size += out->maxpacket - 1;
size -= size % out->maxpacket;
if (g->quirk_ep_out_aligned_size) {
size += out->maxpacket - 1;
size -= size % out->maxpacket;
}
if (dev->port_usb->is_fixed)
size = max_t(size_t, size, dev->port_usb->fixed_out_len);
@ -401,13 +405,12 @@ done:
static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
{
struct usb_request *req;
struct usb_request *tmp;
unsigned long flags;
/* fill unused rxq slots with some skb */
spin_lock_irqsave(&dev->req_lock, flags);
while (!list_empty(&dev->rx_reqs)) {
req = container_of(dev->rx_reqs.next,
struct usb_request, list);
list_for_each_entry_safe(req, tmp, &dev->rx_reqs, list) {
list_del_init(&req->list);
spin_unlock_irqrestore(&dev->req_lock, flags);
@ -527,7 +530,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
return NETDEV_TX_BUSY;
}
req = container_of(dev->tx_reqs.next, struct usb_request, list);
req = list_first_entry(&dev->tx_reqs, struct usb_request, list);
list_del(&req->list);
/* temporarily stop TX queue when the freelist empties */
@ -1122,6 +1125,7 @@ void gether_disconnect(struct gether *link)
{
struct eth_dev *dev = link->ioport;
struct usb_request *req;
struct usb_request *tmp;
WARN_ON(!dev);
if (!dev)
@ -1138,9 +1142,7 @@ void gether_disconnect(struct gether *link)
*/
usb_ep_disable(link->in_ep);
spin_lock(&dev->req_lock);
while (!list_empty(&dev->tx_reqs)) {
req = container_of(dev->tx_reqs.next,
struct usb_request, list);
list_for_each_entry_safe(req, tmp, &dev->tx_reqs, list) {
list_del(&req->list);
spin_unlock(&dev->req_lock);
@ -1152,9 +1154,7 @@ void gether_disconnect(struct gether *link)
usb_ep_disable(link->out_ep);
spin_lock(&dev->req_lock);
while (!list_empty(&dev->rx_reqs)) {
req = container_of(dev->rx_reqs.next,
struct usb_request, list);
list_for_each_entry_safe(req, tmp, &dev->rx_reqs, list) {
list_del(&req->list);
spin_unlock(&dev->req_lock);

View File

@ -20,6 +20,7 @@
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
#include <linux/refcount.h>
#ifdef VERBOSE_DEBUG
#ifndef pr_vdebug
@ -39,15 +40,16 @@
struct f_fs_opts;
struct ffs_dev {
const char *name;
bool name_allocated;
bool mounted;
bool desc_ready;
bool single;
struct ffs_data *ffs_data;
struct f_fs_opts *opts;
struct list_head entry;
char name[41];
bool mounted;
bool desc_ready;
bool single;
int (*ffs_ready_callback)(struct ffs_data *ffs);
void (*ffs_closed_callback)(struct ffs_data *ffs);
void *(*ffs_acquire_dev_callback)(struct ffs_dev *dev);
@ -177,7 +179,7 @@ struct ffs_data {
struct completion ep0req_completion; /* P: mutex */
/* reference counter */
atomic_t ref;
refcount_t ref;
/* how many files are opened (EP0 and others) */
atomic_t opened;

View File

@ -2125,7 +2125,7 @@ static struct configfs_item_operations uvc_item_ops = {
.release = uvc_attr_release,
};
#define UVCG_OPTS_ATTR(cname, conv, str2u, uxx, vnoc, limit) \
#define UVCG_OPTS_ATTR(cname, aname, conv, str2u, uxx, vnoc, limit) \
static ssize_t f_uvc_opts_##cname##_show( \
struct config_item *item, char *page) \
{ \
@ -2168,16 +2168,16 @@ end: \
return ret; \
} \
\
UVC_ATTR(f_uvc_opts_, cname, aname)
UVC_ATTR(f_uvc_opts_, cname, cname)
#define identity_conv(x) (x)
UVCG_OPTS_ATTR(streaming_interval, identity_conv, kstrtou8, u8, identity_conv,
16);
UVCG_OPTS_ATTR(streaming_maxpacket, le16_to_cpu, kstrtou16, u16, le16_to_cpu,
3072);
UVCG_OPTS_ATTR(streaming_maxburst, identity_conv, kstrtou8, u8, identity_conv,
15);
UVCG_OPTS_ATTR(streaming_interval, streaming_interval, identity_conv,
kstrtou8, u8, identity_conv, 16);
UVCG_OPTS_ATTR(streaming_maxpacket, streaming_maxpacket, le16_to_cpu,
kstrtou16, u16, le16_to_cpu, 3072);
UVCG_OPTS_ATTR(streaming_maxburst, streaming_maxburst, identity_conv,
kstrtou8, u8, identity_conv, 15);
#undef identity_conv

View File

@ -27,6 +27,7 @@
#include <linux/mmu_context.h>
#include <linux/aio.h>
#include <linux/uio.h>
#include <linux/refcount.h>
#include <linux/device.h>
#include <linux/moduleparam.h>
@ -114,7 +115,7 @@ enum ep0_state {
struct dev_data {
spinlock_t lock;
atomic_t count;
refcount_t count;
enum ep0_state state; /* P: lock */
struct usb_gadgetfs_event event [N_EVENT];
unsigned ev_next;
@ -150,12 +151,12 @@ struct dev_data {
static inline void get_dev (struct dev_data *data)
{
atomic_inc (&data->count);
refcount_inc (&data->count);
}
static void put_dev (struct dev_data *data)
{
if (likely (!atomic_dec_and_test (&data->count)))
if (likely (!refcount_dec_and_test (&data->count)))
return;
/* needs no more cleanup */
BUG_ON (waitqueue_active (&data->wait));
@ -170,7 +171,7 @@ static struct dev_data *dev_new (void)
if (!dev)
return NULL;
dev->state = STATE_DEV_DISABLED;
atomic_set (&dev->count, 1);
refcount_set (&dev->count, 1);
spin_lock_init (&dev->lock);
INIT_LIST_HEAD (&dev->epfiles);
init_waitqueue_head (&dev->wait);
@ -190,7 +191,7 @@ enum ep_state {
struct ep_data {
struct mutex lock;
enum ep_state state;
atomic_t count;
refcount_t count;
struct dev_data *dev;
/* must hold dev->lock before accessing ep or req */
struct usb_ep *ep;
@ -205,12 +206,12 @@ struct ep_data {
static inline void get_ep (struct ep_data *data)
{
atomic_inc (&data->count);
refcount_inc (&data->count);
}
static void put_ep (struct ep_data *data)
{
if (likely (!atomic_dec_and_test (&data->count)))
if (likely (!refcount_dec_and_test (&data->count)))
return;
put_dev (data->dev);
/* needs no more cleanup */
@ -1561,7 +1562,7 @@ static int activate_ep_files (struct dev_data *dev)
init_waitqueue_head (&data->wait);
strncpy (data->name, ep->name, sizeof (data->name) - 1);
atomic_set (&data->count, 1);
refcount_set (&data->count, 1);
data->dev = dev;
get_dev (dev);

View File

@ -62,8 +62,9 @@ config USB_ATMEL_USBA
The fifo_mode parameter is used to select endpoint allocation mode.
fifo_mode = 0 is used to let the driver autoconfigure the endpoints.
In this case 2 banks are allocated for isochronous endpoints and
only one bank is allocated for the rest of the endpoints.
In this case, for ep1 2 banks are allocated if it works in isochronous
mode and only 1 bank otherwise. For the rest of the endpoints
only 1 bank is allocated.
fifo_mode = 1 is a generic maximum fifo size (1024 bytes) configuration
allowing the usage of ep1 - ep6
@ -191,6 +192,7 @@ config USB_RENESAS_USBHS_UDC
config USB_RENESAS_USB3
tristate 'Renesas USB3.0 Peripheral controller'
depends on ARCH_RENESAS || COMPILE_TEST
depends on EXTCON
help
Renesas USB3.0 Peripheral controller is a USB peripheral controller
that supports super, high, and full speed USB 3.0 data transfers.
@ -253,6 +255,20 @@ config USB_MV_U3D
MARVELL PXA2128 Processor series include a super speed USB3.0 device
controller, which support super speed USB peripheral.
config USB_SNP_CORE
depends on USB_AMD5536UDC
tristate
help
This enables core driver support for Synopsys USB 2.0 Device
controller.
This will be enabled when PCI or Platform driver for this UDC is
selected. Currently, this will be enabled by USB_SNP_UDC_PLAT or
USB_AMD5536UDC options.
This IP is different to the High Speed OTG IP that can be enabled
by selecting USB_DWC2 or USB_DWC3 options.
#
# Controllers available in both integrated and discrete versions
#
@ -277,7 +293,8 @@ source "drivers/usb/gadget/udc/bdc/Kconfig"
config USB_AMD5536UDC
tristate "AMD5536 UDC"
depends on PCI
depends on USB_PCI
select USB_SNP_CORE
help
The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge.
It is a USB Highspeed DMA capable USB device controller. Beside ep0
@ -285,6 +302,9 @@ config USB_AMD5536UDC
The UDC port supports OTG operation, and may be used as a host port
if it's not being used to implement peripheral or OTG roles.
This UDC is based on Synopsys USB device controller IP and selects
CONFIG_USB_SNP_CORE option to build the core driver.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "amd5536udc" and force all
gadget drivers to also be dynamically linked.
@ -327,7 +347,7 @@ config USB_NET2272_DMA
config USB_NET2280
tristate "NetChip NET228x / PLX USB3x8x"
depends on PCI
depends on USB_PCI
help
NetChip 2280 / 2282 is a PCI based USB peripheral controller which
supports both full and high speed USB 2.0 data transfers.
@ -352,7 +372,7 @@ config USB_NET2280
config USB_GOKU
tristate "Toshiba TC86C001 'Goku-S'"
depends on PCI
depends on USB_PCI
help
The Toshiba TC86C001 is a PCI device which includes controllers
for full speed USB devices, IDE, I2C, SIO, plus a USB host (OHCI).
@ -366,7 +386,7 @@ config USB_GOKU
config USB_EG20T
tristate "Intel QUARK X1000/EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC"
depends on PCI
depends on USB_PCI
help
This is a USB device driver for EG20T PCH.
EG20T PCH is the platform controller hub that is used in Intel's

Some files were not shown because too many files have changed in this diff Show More