1
0
Fork 0

USB/PHY patches for 4.20-rc1

Here is the big USB/PHY driver patches for 4.20-rc1
 
 Lots of USB changes in here, primarily in these areas:
   - typec updates and new drivers
   - new PHY drivers
   - dwc2 driver updates and additions (this old core keeps getting added
     to new devices.)
   - usbtmc major update based on the industry group coming together and
     working to add new features and performance to the driver.
   - USB gadget additions for new features
   - USB gadget configfs updates
   - chipidea driver updates
   - other USB gadget updates
   - USB serial driver updates
   - renesas driver updates
   - xhci driver updates
   - other tiny USB driver updates
 
 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-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCW9LlHw8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ymnvwCffYmMWyMG9zSOw1oSzFPl7TVN1hYAoMyJqzLg
 umyLwWxC9ZWWkrpc3iD8
 =ux+Y
 -----END PGP SIGNATURE-----

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

Pull USB/PHY updates from Greg KH:
 "Here is the big USB/PHY driver patches for 4.20-rc1

  Lots of USB changes in here, primarily in these areas:

   - typec updates and new drivers

   - new PHY drivers

   - dwc2 driver updates and additions (this old core keeps getting
     added to new devices.)

   - usbtmc major update based on the industry group coming together and
     working to add new features and performance to the driver.

   - USB gadget additions for new features

   - USB gadget configfs updates

   - chipidea driver updates

   - other USB gadget updates

   - USB serial driver updates

   - renesas driver updates

   - xhci driver updates

   - other tiny USB driver updates

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

* tag 'usb-4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (229 commits)
  usb: phy: ab8500: silence some uninitialized variable warnings
  usb: xhci: tegra: Add genpd support
  usb: xhci: tegra: Power-off power-domains on removal
  usbip:vudc: BUG kmalloc-2048 (Not tainted): Poison overwritten
  usbip: tools: fix atoi() on non-null terminated string
  USB: misc: appledisplay: fix backlight update_status return code
  phy: phy-pxa-usb: add a new driver
  usb: host: add DT bindings for faraday fotg2
  usb: host: ohci-at91: fix request of irq for optional gpio
  usb/early: remove set but not used variable 'remain_length'
  usb: typec: Fix copy/paste on typec_set_vconn_role() kerneldoc
  usb: typec: tcpm: Report back negotiated PPS voltage and current
  USB: core: remove set but not used variable 'udev'
  usb: core: fix memory leak on port_dev_path allocation
  USB: net2280: Remove ->disconnect() callback from net2280_pullup()
  usb: dwc2: disable power_down on rockchip devices
  usb: gadget: udc: renesas_usb3: add support for r8a77990
  dt-bindings: usb: renesas_usb3: add bindings for r8a77990
  usb: gadget: udc: renesas_usb3: Add r8a774a1 support
  USB: serial: cypress_m8: remove set but not used variable 'iflag'
  ...
hifive-unleashed-5.1
Linus Torvalds 2018-10-26 08:14:13 -07:00
commit 9703fc8caf
181 changed files with 8807 additions and 2209 deletions

View File

@ -25,38 +25,3 @@ Description:
4.2.2.
The files are read only.
What: /sys/bus/usb/drivers/usbtmc/*/TermChar
Date: August 2008
Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Description:
This file is the TermChar value to be sent to the USB TMC
device as described by the document, "Universal Serial Bus Test
and Measurement Class Specification
(USBTMC) Revision 1.0" as published by the USB-IF.
Note that the TermCharEnabled file determines if this value is
sent to the device or not.
What: /sys/bus/usb/drivers/usbtmc/*/TermCharEnabled
Date: August 2008
Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Description:
This file determines if the TermChar is to be sent to the
device on every transaction or not. For more details about
this, please see the document, "Universal Serial Bus Test and
Measurement Class Specification (USBTMC) Revision 1.0" as
published by the USB-IF.
What: /sys/bus/usb/drivers/usbtmc/*/auto_abort
Date: August 2008
Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Description:
This file determines if the transaction of the USB TMC
device is to be automatically aborted if there is any error.
For more details about this, please see the document,
"Universal Serial Bus Test and Measurement Class Specification
(USBTMC) Revision 1.0" as published by the USB-IF.

View File

@ -12,6 +12,10 @@ Date: Dec 2014
KernelVersion: 4.0
Description: Control descriptors
All attributes read only:
bInterfaceNumber - USB interface number for this
streaming interface
What: /config/usb-gadget/gadget/functions/uvc.name/control/class
Date: Dec 2014
KernelVersion: 4.0
@ -109,6 +113,10 @@ Date: Dec 2014
KernelVersion: 4.0
Description: Streaming descriptors
All attributes read only:
bInterfaceNumber - USB interface number for this
streaming interface
What: /config/usb-gadget/gadget/functions/uvc.name/streaming/class
Date: Dec 2014
KernelVersion: 4.0
@ -160,6 +168,10 @@ Description: Specific MJPEG format descriptors
All attributes read only,
except bmaControls and bDefaultFrameIndex:
bFormatIndex - unique id for this format descriptor;
only defined after parent header is
linked into the streaming class;
read-only
bmaControls - this format's data for bmaControls in
the streaming header
bmInterfaceFlags - specifies interlace information,
@ -177,6 +189,10 @@ Date: Dec 2014
KernelVersion: 4.0
Description: Specific MJPEG frame descriptors
bFrameIndex - unique id for this framedescriptor;
only defined after parent format is
linked into the streaming header;
read-only
dwFrameInterval - indicates how frame interval can be
programmed; a number of values
separated by newline can be specified
@ -204,6 +220,10 @@ Date: Dec 2014
KernelVersion: 4.0
Description: Specific uncompressed format descriptors
bFormatIndex - unique id for this format descriptor;
only defined after parent header is
linked into the streaming class;
read-only
bmaControls - this format's data for bmaControls in
the streaming header
bmInterfaceFlags - specifies interlace information,
@ -224,6 +244,10 @@ Date: Dec 2014
KernelVersion: 4.0
Description: Specific uncompressed frame descriptors
bFrameIndex - unique id for this framedescriptor;
only defined after parent format is
linked into the streaming header;
read-only
dwFrameInterval - indicates how frame interval can be
programmed; a number of values
separated by newline can be specified

View File

@ -189,6 +189,16 @@ Description:
The file will read "hotplug", "wired" and "not used" if the
information is available, and "unknown" otherwise.
What: /sys/bus/usb/devices/.../(hub interface)/portX/location
Date: October 2018
Contact: Bjørn Mork <bjorn@mork.no>
Description:
Some platforms provide usb port physical location through
firmware. This is used by the kernel to pair up logical ports
mapping to the same physical connector. The attribute exposes the
raw location value as a hex integer.
What: /sys/bus/usb/devices/.../(hub interface)/portX/quirks
Date: May 2018
Contact: Nicolas Boichat <drinkcat@chromium.org>
@ -219,7 +229,14 @@ Description:
ports and report them to the kernel. This attribute is to expose
the number of over-current situation occurred on a specific port
to user space. This file will contain an unsigned 32 bit value
which wraps to 0 after its maximum is reached.
which wraps to 0 after its maximum is reached. This file supports
poll() for monitoring changes to this value in user space.
Any time this value changes the corresponding hub device will send a
udev event with the following attributes:
OVER_CURRENT_PORT=/sys/bus/usb/devices/.../(hub interface)/portX
OVER_CURRENT_COUNT=[current value of this sysfs attribute]
What: /sys/bus/usb/devices/.../(hub interface)/portX/usb3_lpm_permit
Date: November 2015

View File

@ -4623,7 +4623,8 @@
usbcore.old_scheme_first=
[USB] Start with the old device initialization
scheme (default 0 = off).
scheme, applies only to low and full-speed devices
(default 0 = off).
usbcore.usbfs_memory_mb=
[USB] Memory limit (in MB) for buffers allocated by

View File

@ -29,15 +29,15 @@ Required properties for usb-c-connector with power delivery support:
in "Universal Serial Bus Power Delivery Specification" chapter 6.4.1.2
Source_Capabilities Message, the order of each entry(PDO) should follow
the PD spec chapter 6.4.1. Required for power source and power dual role.
User can specify the source PDO array via PDO_FIXED/BATT/VAR() defined in
dt-bindings/usb/pd.h.
User can specify the source PDO array via PDO_FIXED/BATT/VAR/PPS_APDO()
defined in dt-bindings/usb/pd.h.
- sink-pdos: An array of u32 with each entry providing supported power
sink data object(PDO), the detailed bit definitions of PDO can be found
in "Universal Serial Bus Power Delivery Specification" chapter 6.4.1.3
Sink Capabilities Message, the order of each entry(PDO) should follow
the PD spec chapter 6.4.1. Required for power sink and power dual role.
User can specify the sink PDO array via PDO_FIXED/BATT/VAR() defined in
dt-bindings/usb/pd.h.
User can specify the sink PDO array via PDO_FIXED/BATT/VAR/PPS_APDO() defined
in dt-bindings/usb/pd.h.
- op-sink-microwatt: Sink required operating power in microwatt, if source
can't offer the power, Capability Mismatch is set. Required for power
sink and power dual role.

View File

@ -8,6 +8,7 @@ Required properties:
"brcm,iproc-nsp-sata-phy"
"brcm,phy-sata3"
"brcm,iproc-sr-sata-phy"
"brcm,bcm63138-sata-phy"
- address-cells: should be 1
- size-cells: should be 0
- reg: register ranges for the PHY PCB interface

View File

@ -0,0 +1,30 @@
Cadence MHDP DisplayPort SD0801 PHY binding
===========================================
This binding describes the Cadence SD0801 PHY hardware included with
the Cadence MHDP DisplayPort controller.
-------------------------------------------------------------------------------
Required properties (controller (parent) node):
- compatible : Should be "cdns,dp-phy"
- reg : Defines the following sets of registers in the parent
mhdp device:
- Offset of the DPTX PHY configuration registers
- Offset of the SD0801 PHY configuration registers
- #phy-cells : from the generic PHY bindings, must be 0.
Optional properties:
- num_lanes : Number of DisplayPort lanes to use (1, 2 or 4)
- max_bit_rate : Maximum DisplayPort link bit rate to use, in Mbps (2160,
2430, 2700, 3240, 4320, 5400 or 8100)
-------------------------------------------------------------------------------
Example:
dp_phy: phy@f0fb030a00 {
compatible = "cdns,dp-phy";
reg = <0xf0 0xfb030a00 0x0 0x00000040>,
<0xf0 0xfb500000 0x0 0x00100000>;
num_lanes = <4>;
max_bit_rate = <8100>;
#phy-cells = <0>;
};

View File

@ -0,0 +1,43 @@
ROCKCHIP HDMI PHY WITH INNO IP BLOCK
Required properties:
- compatible : should be one of the listed compatibles:
* "rockchip,rk3228-hdmi-phy",
* "rockchip,rk3328-hdmi-phy";
- reg : Address and length of the hdmi phy control register set
- clocks : phandle + clock specifier for the phy clocks
- clock-names : string, clock name, must contain "sysclk" for system
control and register configuration, "refoclk" for crystal-
oscillator reference PLL clock input and "refpclk" for pclk-
based refeference PLL clock input.
- #clock-cells: should be 0.
- clock-output-names : shall be the name for the output clock.
- interrupts : phandle + interrupt specified for the hdmiphy interrupt
- #phy-cells : must be 0. See ./phy-bindings.txt for details.
Optional properties for rk3328-hdmi-phy:
- nvmem-cells = phandle + nvmem specifier for the cpu-version efuse
- nvmem-cell-names : "cpu-version" to read the chip version, required
for adjustment to some frequency settings
Example:
hdmi_phy: hdmi-phy@12030000 {
compatible = "rockchip,rk3228-hdmi-phy";
reg = <0x12030000 0x10000>;
#phy-cells = <0>;
clocks = <&cru PCLK_HDMI_PHY>, <&xin24m>, <&cru DCLK_HDMIPHY>;
clock-names = "sysclk", "refoclk", "refpclk";
#clock-cells = <0>;
clock-output-names = "hdmi_phy";
status = "disabled";
};
Then the PHY can be used in other nodes such as:
hdmi: hdmi@200a0000 {
compatible = "rockchip,rk3228-dw-hdmi";
...
phys = <&hdmi_phy>;
phy-names = "hdmi";
...
};

View File

@ -10,16 +10,20 @@ Required properties:
"qcom,msm8996-qmp-pcie-phy" for 14nm PCIe phy on msm8996,
"qcom,msm8996-qmp-usb3-phy" for 14nm USB3 phy on msm8996,
"qcom,sdm845-qmp-usb3-phy" for USB3 QMP V3 phy on sdm845,
"qcom,sdm845-qmp-usb3-uni-phy" for USB3 QMP V3 UNI phy on sdm845.
"qcom,sdm845-qmp-usb3-uni-phy" for USB3 QMP V3 UNI phy on sdm845,
"qcom,sdm845-qmp-ufs-phy" for UFS QMP phy on sdm845.
- reg:
- For "qcom,sdm845-qmp-usb3-phy":
- index 0: address and length of register set for PHY's common serdes
block.
- named register "dp_com" (using reg-names): address and length of the
DP_COM control block.
- For all others:
- offset and length of register set for PHY's common serdes block.
- reg:
- index 0: address and length of register set for PHY's common
serdes block.
- index 1: address and length of the DP_COM control block (for
"qcom,sdm845-qmp-usb3-phy" only).
- reg-names:
- For "qcom,sdm845-qmp-usb3-phy":
- Should be: "reg-base", "dp_com"
- For all others:
- The reg-names property shouldn't be defined.
- #clock-cells: must be 1
- Phy pll outputs a bunch of clocks for Tx, Rx and Pipe
@ -35,6 +39,7 @@ Required properties:
"aux" for phy aux clock,
"ref" for 19.2 MHz ref clk,
"com_aux" for phy common block aux clock,
"ref_aux" for phy reference aux clock,
For "qcom,msm8996-qmp-pcie-phy" must contain:
"aux", "cfg_ahb", "ref".
For "qcom,msm8996-qmp-usb3-phy" must contain:

View File

@ -1,10 +1,12 @@
* Renesas R-Car generation 3 USB 2.0 PHY
This file provides information on what the device node for the R-Car generation
3 USB 2.0 PHY contains.
3 and RZ/G2 USB 2.0 PHY contain.
Required properties:
- compatible: "renesas,usb2-phy-r8a7795" if the device is a part of an R8A7795
- compatible: "renesas,usb2-phy-r8a774a1" if the device is a part of an R8A774A1
SoC.
"renesas,usb2-phy-r8a7795" if the device is a part of an R8A7795
SoC.
"renesas,usb2-phy-r8a7796" if the device is a part of an R8A7796
SoC.
@ -14,7 +16,8 @@ Required properties:
R8A77990 SoC.
"renesas,usb2-phy-r8a77995" if the device is a part of an
R8A77995 SoC.
"renesas,rcar-gen3-usb2-phy" for a generic R-Car Gen3 compatible device.
"renesas,rcar-gen3-usb2-phy" for a generic R-Car Gen3 or RZ/G2
compatible device.
When compatible with the generic version, nodes must list the
SoC-specific version corresponding to the platform first
@ -31,6 +34,8 @@ channel as USB OTG:
- interrupts: interrupt specifier for the PHY.
- vbus-supply: Phandle to a regulator that provides power to the VBUS. This
regulator will be managed during the PHY power on/off sequence.
- renesas,no-otg-pins: boolean, specify when a board does not provide proper
otg pins.
Example (R-Car H3):

View File

@ -1,20 +1,22 @@
* Renesas R-Car generation 3 USB 3.0 PHY
This file provides information on what the device node for the R-Car generation
3 USB 3.0 PHY contains.
3 and RZ/G2 USB 3.0 PHY contain.
If you want to enable spread spectrum clock (ssc), you should use USB_EXTAL
instead of USB3_CLK. However, if you don't want to these features, you don't
need this driver.
Required properties:
- compatible: "renesas,r8a7795-usb3-phy" if the device is a part of an R8A7795
- compatible: "renesas,r8a774a1-usb3-phy" if the device is a part of an R8A774A1
SoC.
"renesas,r8a7795-usb3-phy" if the device is a part of an R8A7795
SoC.
"renesas,r8a7796-usb3-phy" if the device is a part of an R8A7796
SoC.
"renesas,r8a77965-usb3-phy" if the device is a part of an
R8A77965 SoC.
"renesas,rcar-gen3-usb3-phy" for a generic R-Car Gen3 compatible
device.
"renesas,rcar-gen3-usb3-phy" for a generic R-Car Gen3 or RZ/G2
compatible device.
When compatible with the generic version, nodes must list the
SoC-specific version corresponding to the platform first

View File

@ -0,0 +1,31 @@
Socionext UniPhier PCIe PHY bindings
This describes the devicetree bindings for PHY interface built into
PCIe controller implemented on Socionext UniPhier SoCs.
Required properties:
- compatible: Should contain one of the following:
"socionext,uniphier-ld20-pcie-phy" - for LD20 PHY
"socionext,uniphier-pxs3-pcie-phy" - for PXs3 PHY
- reg: Specifies offset and length of the register set for the device.
- #phy-cells: Must be zero.
- clocks: A phandle to the clock gate for PCIe glue layer including
this phy.
- resets: A phandle to the reset line for PCIe glue layer including
this phy.
Optional properties:
- socionext,syscon: A phandle to system control to set configurations
for phy.
Refer to phy/phy-bindings.txt for the generic PHY binding properties.
Example:
pcie_phy: phy@66038000 {
compatible = "socionext,uniphier-ld20-pcie-phy";
reg = <0x66038000 0x4000>;
#phy-cells = <0>;
clocks = <&sys_clk 24>;
resets = <&sys_rst 24>;
socionext,syscon = <&soc_glue>;
};

View File

@ -0,0 +1,45 @@
Socionext UniPhier USB2 PHY
This describes the devicetree bindings for PHY interface built into
USB2 controller implemented on Socionext UniPhier SoCs.
Pro4 SoC has both USB2 and USB3 host controllers, however, this USB3
controller doesn't include its own High-Speed PHY. This needs to specify
USB2 PHY instead of USB3 HS-PHY.
Required properties:
- compatible: Should contain one of the following:
"socionext,uniphier-pro4-usb2-phy" - for Pro4 SoC
"socionext,uniphier-ld11-usb2-phy" - for LD11 SoC
Sub-nodes:
Each PHY should be represented as a sub-node.
Sub-nodes required properties:
- #phy-cells: Should be 0.
- reg: The number of the PHY.
Sub-nodes optional properties:
- vbus-supply: A phandle to the regulator for USB VBUS.
Refer to phy/phy-bindings.txt for the generic PHY binding properties.
Example:
soc-glue@5f800000 {
...
usb-phy {
compatible = "socionext,uniphier-ld11-usb2-phy";
usb_phy0: phy@0 {
reg = <0>;
#phy-cells = <0>;
};
...
};
};
usb@5a800100 {
compatible = "socionext,uniphier-ehci", "generic-ehci";
...
phy-names = "usb";
phys = <&usb_phy0>;
};

View File

@ -0,0 +1,69 @@
Socionext UniPhier USB3 High-Speed (HS) PHY
This describes the devicetree bindings for PHY interfaces built into
USB3 controller implemented on Socionext UniPhier SoCs.
Although the controller includes High-Speed PHY and Super-Speed PHY,
this describes about High-Speed PHY.
Required properties:
- compatible: Should contain one of the following:
"socionext,uniphier-pro4-usb3-hsphy" - for Pro4 SoC
"socionext,uniphier-pxs2-usb3-hsphy" - for PXs2 SoC
"socionext,uniphier-ld20-usb3-hsphy" - for LD20 SoC
"socionext,uniphier-pxs3-usb3-hsphy" - for PXs3 SoC
- reg: Specifies offset and length of the register set for the device.
- #phy-cells: Should be 0.
- clocks: A list of phandles to the clock gate for USB3 glue layer.
According to the clock-names, appropriate clocks are required.
- clock-names: Should contain the following:
"gio", "link" - for Pro4 SoC
"phy", "phy-ext", "link" - for PXs3 SoC, "phy-ext" is optional.
"phy", "link" - for others
- resets: A list of phandles to the reset control for USB3 glue layer.
According to the reset-names, appropriate resets are required.
- reset-names: Should contain the following:
"gio", "link" - for Pro4 SoC
"phy", "link" - for others
Optional properties:
- vbus-supply: A phandle to the regulator for USB VBUS.
- nvmem-cells: Phandles to nvmem cell that contains the trimming data.
Available only for HS-PHY implemented on LD20 and PXs3, and
if unspecified, default value is used.
- nvmem-cell-names: Should be the following names, which correspond to
each nvmem-cells.
All of the 3 parameters associated with the following names are
required for each port, if any one is omitted, the trimming data
of the port will not be set at all.
"rterm", "sel_t", "hs_i" - Each cell name for phy parameters
Refer to phy/phy-bindings.txt for the generic PHY binding properties.
Example:
usb-glue@65b00000 {
compatible = "socionext,uniphier-ld20-dwc3-glue",
"simple-mfd";
#address-cells = <1>;
#size-cells = <1>;
ranges = <0 0x65b00000 0x400>;
usb_vbus0: regulator {
...
};
usb_hsphy0: hs-phy@200 {
compatible = "socionext,uniphier-ld20-usb3-hsphy";
reg = <0x200 0x10>;
#phy-cells = <0>;
clock-names = "link", "phy";
clocks = <&sys_clk 14>, <&sys_clk 16>;
reset-names = "link", "phy";
resets = <&sys_rst 14>, <&sys_rst 16>;
vbus-supply = <&usb_vbus0>;
nvmem-cell-names = "rterm", "sel_t", "hs_i";
nvmem-cells = <&usb_rterm0>, <&usb_sel_t0>,
<&usb_hs_i0>;
};
...
};

View File

@ -0,0 +1,57 @@
Socionext UniPhier USB3 Super-Speed (SS) PHY
This describes the devicetree bindings for PHY interfaces built into
USB3 controller implemented on Socionext UniPhier SoCs.
Although the controller includes High-Speed PHY and Super-Speed PHY,
this describes about Super-Speed PHY.
Required properties:
- compatible: Should contain one of the following:
"socionext,uniphier-pro4-usb3-ssphy" - for Pro4 SoC
"socionext,uniphier-pxs2-usb3-ssphy" - for PXs2 SoC
"socionext,uniphier-ld20-usb3-ssphy" - for LD20 SoC
"socionext,uniphier-pxs3-usb3-ssphy" - for PXs3 SoC
- reg: Specifies offset and length of the register set for the device.
- #phy-cells: Should be 0.
- clocks: A list of phandles to the clock gate for USB3 glue layer.
According to the clock-names, appropriate clocks are required.
- clock-names:
"gio", "link" - for Pro4 SoC
"phy", "phy-ext", "link" - for PXs3 SoC, "phy-ext" is optional.
"phy", "link" - for others
- resets: A list of phandles to the reset control for USB3 glue layer.
According to the reset-names, appropriate resets are required.
- reset-names:
"gio", "link" - for Pro4 SoC
"phy", "link" - for others
Optional properties:
- vbus-supply: A phandle to the regulator for USB VBUS.
Refer to phy/phy-bindings.txt for the generic PHY binding properties.
Example:
usb-glue@65b00000 {
compatible = "socionext,uniphier-ld20-dwc3-glue",
"simple-mfd";
#address-cells = <1>;
#size-cells = <1>;
ranges = <0 0x65b00000 0x400>;
usb_vbus0: regulator {
...
};
usb_ssphy0: ss-phy@300 {
compatible = "socionext,uniphier-ld20-usb3-ssphy";
reg = <0x300 0x10>;
#phy-cells = <0>;
clock-names = "link", "phy";
clocks = <&sys_clk 14>, <&sys_clk 16>;
reset-names = "link", "phy";
resets = <&sys_rst 14>, <&sys_rst 16>;
vbus-supply = <&usb_vbus0>;
};
...
};

View File

@ -80,6 +80,8 @@ Optional properties:
controller. It's expected that a mux state of 0 indicates device mode and a
mux state of 1 indicates host mode.
- mux-control-names: Shall be "usb_switch" if mux-controls is specified.
- pinctrl-names: Names for optional pin modes in "default", "host", "device"
- pinctrl-n: alternate pin modes
i.mx specific properties
- fsl,usbmisc: phandler of non-core register device, with one

View File

@ -19,6 +19,7 @@ Exception for clocks:
"cavium,octeon-7130-usb-uctl"
"qcom,dwc3"
"samsung,exynos5250-dwusb3"
"samsung,exynos5433-dwusb3"
"samsung,exynos7-dwusb3"
"sprd,sc9860-dwc3"
"st,stih407-dwc3"

View File

@ -0,0 +1,23 @@
* Marvell PXA/MMP EHCI controller.
Required properties:
- compatible: must be "marvell,pxau2o-ehci"
- reg: physical base addresses of the controller and length of memory mapped region
- interrupts: one EHCI controller interrupt should be described here
- clocks: phandle list of usb clocks
- clock-names: should be "USBCLK"
- phys: phandle for the PHY device
- phy-names: should be "usb"
Example:
ehci0: usb-ehci@d4208000 {
compatible = "marvell,pxau2o-ehci";
reg = <0xd4208000 0x200>;
interrupts = <44>;
clocks = <&soc_clocks MMP2_CLK_USB>;
clock-names = "USBCLK";
phys = <&usb_otg_phy>;
phy-names = "usb";
};

View File

@ -83,6 +83,8 @@ Required properties:
- compatible: should be one of the following -
"samsung,exynos5250-dwusb3": for USB 3.0 DWC3 controller on
Exynos5250/5420.
"samsung,exynos5433-dwusb3": for USB 3.0 DWC3 controller on
Exynos5433.
"samsung,exynos7-dwusb3": for USB 3.0 DWC3 controller on Exynos7.
- #address-cells, #size-cells : should be '1' if the device has sub-nodes
with 'reg' property.

View File

@ -0,0 +1,35 @@
Faraday FOTG Host controller
This OTG-capable USB host controller is found in Cortina Systems
Gemini and other SoC products.
Required properties:
- compatible: should be one of:
"faraday,fotg210"
"cortina,gemini-usb", "faraday,fotg210"
- reg: should contain one register range i.e. start and length
- interrupts: description of the interrupt line
Optional properties:
- clocks: should contain the IP block clock
- clock-names: should be "PCLK" for the IP block clock
Required properties for "cortina,gemini-usb" compatible:
- syscon: a phandle to the system controller to access PHY registers
Optional properties for "cortina,gemini-usb" compatible:
- cortina,gemini-mini-b: boolean property that indicates that a Mini-B
OTG connector is in use
- wakeup-source: see power/wakeup-source.txt
Example for Gemini:
usb@68000000 {
compatible = "cortina,gemini-usb", "faraday,fotg210";
reg = <0x68000000 0x1000>;
interrupts = <10 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cc 12>;
clock-names = "PCLK";
syscon = <&syscon>;
wakeup-source;
};

View File

@ -5,10 +5,19 @@ Required properties :
- reg : I2C slave address
- interrupts : Interrupt specifier
Optional properties :
- fcs,operating-sink-microwatt :
Minimum amount of power accepted from a sink
when negotiating
Required sub-node:
- connector : The "usb-c-connector" attached to the FUSB302 IC. The bindings
of the connector node are specified in:
Documentation/devicetree/bindings/connector/usb-connector.txt
Deprecated properties :
- fcs,max-sink-microvolt : Maximum sink voltage accepted by port controller
- fcs,max-sink-microamp : Maximum sink current accepted by port controller
- fcs,max-sink-microwatt : Maximum sink power accepted by port controller
- fcs,operating-sink-microwatt : Minimum amount of power accepted from a sink
when negotiating
Example:
@ -17,7 +26,16 @@ fusb302: typec-portc@54 {
reg = <0x54>;
interrupt-parent = <&nmi_intc>;
interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
fcs,max-sink-microvolt = <12000000>;
fcs,max-sink-microamp = <3000000>;
fcs,max-sink-microwatt = <36000000>;
usb_con: connector {
compatible = "usb-c-connector";
label = "USB-C";
power-role = "dual";
try-power-role = "sink";
source-pdos = <PDO_FIXED(5000, 3000, PDO_FIXED_USB_COMM)>;
sink-pdos = <PDO_FIXED(5000, 3000, PDO_FIXED_USB_COMM)
PDO_VAR(3000, 12000, 3000)
PDO_PPS_APDO(3000, 11000, 3000)>;
op-sink-microwatt = <10000000>;
};
};

View File

@ -2,11 +2,13 @@ Renesas Electronics USB3.0 Peripheral driver
Required properties:
- compatible: Must contain one of the following:
- "renesas,r8a774a1-usb3-peri"
- "renesas,r8a7795-usb3-peri"
- "renesas,r8a7796-usb3-peri"
- "renesas,r8a77965-usb3-peri"
- "renesas,rcar-gen3-usb3-peri" for a generic R-Car Gen3 compatible
device
- "renesas,r8a77990-usb3-peri"
- "renesas,rcar-gen3-usb3-peri" for a generic R-Car Gen3 or RZ/G2
compatible device
When compatible with the generic version, nodes must list the
SoC-specific version corresponding to the platform first

View File

@ -4,7 +4,9 @@ Required properties:
- compatible: Must contain one or more of the following:
- "renesas,usbhs-r8a7743" for r8a7743 (RZ/G1M) compatible device
- "renesas,usbhs-r8a7744" for r8a7744 (RZ/G1N) compatible device
- "renesas,usbhs-r8a7745" for r8a7745 (RZ/G1E) compatible device
- "renesas,usbhs-r8a774a1" for r8a774a1 (RZ/G2M) compatible device
- "renesas,usbhs-r8a7790" for r8a7790 (R-Car H2) compatible device
- "renesas,usbhs-r8a7791" for r8a7791 (R-Car M2-W) compatible device
- "renesas,usbhs-r8a7792" for r8a7792 (R-Car V2H) compatible device
@ -13,10 +15,11 @@ Required properties:
- "renesas,usbhs-r8a7795" for r8a7795 (R-Car H3) compatible device
- "renesas,usbhs-r8a7796" for r8a7796 (R-Car M3-W) compatible device
- "renesas,usbhs-r8a77965" for r8a77965 (R-Car M3-N) compatible device
- "renesas,usbhs-r8a77990" for r8a77990 (R-Car E3) compatible device
- "renesas,usbhs-r8a77995" for r8a77995 (R-Car D3) compatible device
- "renesas,usbhs-r7s72100" for r7s72100 (RZ/A1) compatible device
- "renesas,rcar-gen2-usbhs" for R-Car Gen2 or RZ/G1 compatible devices
- "renesas,rcar-gen3-usbhs" for R-Car Gen3 compatible device
- "renesas,rcar-gen3-usbhs" for R-Car Gen3 or RZ/G2 compatible devices
- "renesas,rza1-usbhs" for RZ/A1 compatible device
When compatible with the generic version, nodes must list the
@ -25,7 +28,11 @@ Required properties:
- reg: Base address and length of the register for the USBHS
- interrupts: Interrupt specifier for the USBHS
- clocks: A list of phandle + clock specifier pairs
- clocks: A list of phandle + clock specifier pairs.
- In case of "renesas,rcar-gen3-usbhs", two clocks are required.
First clock should be peripheral and second one should be host.
- In case of except above, one clock is required. First clock
should be peripheral.
Optional properties:
- renesas,buswait: Integer to use BUSWAIT register

View File

@ -15,7 +15,11 @@ Optional properties:
- needs-reset-on-resume : boolean, set this to force EHCI reset after resume
- has-transaction-translator : boolean, set this if EHCI have a Transaction
Translator built into the root hub.
- clocks : a list of phandle + clock specifier pairs
- clocks : a list of phandle + clock specifier pairs. In case of Renesas
R-Car Gen3 SoCs:
- if a host only channel: first clock should be host.
- if a USB DRD channel: first clock should be host and second one
should be peripheral.
- phys : see usb-hcd.txt in the current directory
- resets : phandle + reset specifier pair

View File

@ -12,7 +12,11 @@ Optional properties:
- no-big-frame-no : boolean, set if frame_no lives in bits [15:0] of HCCA
- remote-wakeup-connected: remote wakeup is wired on the platform
- num-ports : u32, to override the detected port count
- clocks : a list of phandle + clock specifier pairs
- clocks : a list of phandle + clock specifier pairs. In case of Renesas
R-Car Gen3 SoCs:
- if a host only channel: first clock should be host.
- if a USB DRD channel: first clock should be host and second one
should be peripheral.
- phys : see usb-hcd.txt in the current directory
- resets : a list of phandle + reset specifier pairs

View File

@ -8,6 +8,8 @@ Required properties:
- "marvell,armada-375-xhci" for Armada 375 SoCs
- "marvell,armada-380-xhci" for Armada 38x SoCs
- "renesas,xhci-r8a7743" for r8a7743 SoC
- "renesas,xhci-r8a7744" for r8a7744 SoC
- "renesas,xhci-r8a774a1" for r8a774a1 SoC
- "renesas,xhci-r8a7790" for r8a7790 SoC
- "renesas,xhci-r8a7791" for r8a7791 SoC
- "renesas,xhci-r8a7793" for r8a7793 SoC
@ -17,7 +19,8 @@ Required properties:
- "renesas,xhci-r8a77990" for r8a77990 SoC
- "renesas,rcar-gen2-xhci" for a generic R-Car Gen2 or RZ/G1 compatible
device
- "renesas,rcar-gen3-xhci" for a generic R-Car Gen3 compatible device
- "renesas,rcar-gen3-xhci" for a generic R-Car Gen3 or RZ/G2 compatible
device
- "xhci-platform" (deprecated)
When compatible with the generic version, nodes must list the

View File

@ -201,7 +201,7 @@ Code Seq#(hex) Include File Comments
'X' 01 linux/pktcdvd.h conflict!
'Y' all linux/cyclades.h
'Z' 14-15 drivers/message/fusion/mptctl.h
'[' 00-07 linux/usb/tmc.h USB Test and Measurement Devices
'[' 00-3F linux/usb/tmc.h USB Test and Measurement Devices
<mailto:gregkh@linuxfoundation.org>
'a' all linux/atm*.h, linux/sonet.h ATM on linux
<http://lrcwww.epfl.ch/>

View File

@ -15443,6 +15443,12 @@ F: Documentation/driver-api/usb/typec_bus.rst
F: drivers/usb/typec/altmodes/
F: include/linux/usb/typec_altmode.h
USB TYPEC PORT CONTROLLER DRIVERS
M: Guenter Roeck <linux@roeck-us.net>
L: linux-usb@vger.kernel.org
S: Maintained
F: drivers/usb/typec/tcpm/
USB UHCI DRIVER
M: Alan Stern <stern@rowland.harvard.edu>
L: linux-usb@vger.kernel.org

View File

@ -277,21 +277,12 @@ struct platform_device pxa168_device_u2o = {
#if IS_ENABLED(CONFIG_USB_EHCI_MV_U2O)
struct resource pxa168_u2oehci_resources[] = {
/* regbase */
[0] = {
.start = PXA168_U2O_REGBASE + U2x_CAPREGS_OFFSET,
.start = PXA168_U2O_REGBASE,
.end = PXA168_U2O_REGBASE + USB_REG_RANGE,
.flags = IORESOURCE_MEM,
.name = "capregs",
},
/* phybase */
[1] = {
.start = PXA168_U2O_PHYBASE,
.end = PXA168_U2O_PHYBASE + USB_PHY_RANGE,
.flags = IORESOURCE_MEM,
.name = "phyregs",
},
[2] = {
.start = IRQ_PXA168_USB1,
.end = IRQ_PXA168_USB1,
.flags = IORESOURCE_IRQ,

View File

@ -116,6 +116,7 @@ static void em28xx_audio_isocirq(struct urb *urb)
stride = runtime->frame_bits >> 3;
for (i = 0; i < urb->number_of_packets; i++) {
unsigned long flags;
int length =
urb->iso_frame_desc[i].actual_length / stride;
cp = (unsigned char *)urb->transfer_buffer +
@ -137,7 +138,7 @@ static void em28xx_audio_isocirq(struct urb *urb)
length * stride);
}
snd_pcm_stream_lock(substream);
snd_pcm_stream_lock_irqsave(substream, flags);
dev->adev.hwptr_done_capture += length;
if (dev->adev.hwptr_done_capture >=
@ -153,7 +154,7 @@ static void em28xx_audio_isocirq(struct urb *urb)
period_elapsed = 1;
}
snd_pcm_stream_unlock(substream);
snd_pcm_stream_unlock_irqrestore(substream, flags);
}
if (period_elapsed)
snd_pcm_period_elapsed(substream);

View File

@ -777,6 +777,7 @@ EXPORT_SYMBOL_GPL(em28xx_set_mode);
static void em28xx_irq_callback(struct urb *urb)
{
struct em28xx *dev = urb->context;
unsigned long flags;
int i;
switch (urb->status) {
@ -793,9 +794,9 @@ static void em28xx_irq_callback(struct urb *urb)
}
/* Copy data from URB */
spin_lock(&dev->slock);
spin_lock_irqsave(&dev->slock, flags);
dev->usb_ctl.urb_data_copy(dev, urb);
spin_unlock(&dev->slock);
spin_unlock_irqrestore(&dev->slock, flags);
/* Reset urb buffers */
for (i = 0; i < urb->number_of_packets; i++) {

View File

@ -419,6 +419,7 @@ static void tm6000_irq_callback(struct urb *urb)
{
struct tm6000_dmaqueue *dma_q = urb->context;
struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
unsigned long flags;
int i;
switch (urb->status) {
@ -436,9 +437,9 @@ static void tm6000_irq_callback(struct urb *urb)
break;
}
spin_lock(&dev->slock);
spin_lock_irqsave(&dev->slock, flags);
tm6000_isoc_copy(urb);
spin_unlock(&dev->slock);
spin_unlock_irqrestore(&dev->slock, flags);
/* Reset urb buffers */
for (i = 0; i < urb->number_of_packets; i++) {

View File

@ -43,6 +43,7 @@ config PHY_XGENE
source "drivers/phy/allwinner/Kconfig"
source "drivers/phy/amlogic/Kconfig"
source "drivers/phy/broadcom/Kconfig"
source "drivers/phy/cadence/Kconfig"
source "drivers/phy/hisilicon/Kconfig"
source "drivers/phy/lantiq/Kconfig"
source "drivers/phy/marvell/Kconfig"
@ -54,6 +55,7 @@ source "drivers/phy/ralink/Kconfig"
source "drivers/phy/renesas/Kconfig"
source "drivers/phy/rockchip/Kconfig"
source "drivers/phy/samsung/Kconfig"
source "drivers/phy/socionext/Kconfig"
source "drivers/phy/st/Kconfig"
source "drivers/phy/tegra/Kconfig"
source "drivers/phy/ti/Kconfig"

View File

@ -15,6 +15,7 @@ obj-$(CONFIG_ARCH_RENESAS) += renesas/
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-y += broadcom/ \
cadence/ \
hisilicon/ \
marvell/ \
motorola/ \
@ -22,5 +23,6 @@ obj-y += broadcom/ \
qualcomm/ \
ralink/ \
samsung/ \
socionext/ \
st/ \
ti/

View File

@ -60,7 +60,8 @@ config PHY_NS2_USB_DRD
config PHY_BRCM_SATA
tristate "Broadcom SATA PHY driver"
depends on ARCH_BRCMSTB || ARCH_BCM_IPROC || BMIPS_GENERIC || COMPILE_TEST
depends on ARCH_BRCMSTB || ARCH_BCM_IPROC || BMIPS_GENERIC || \
ARCH_BCM_63XX || COMPILE_TEST
depends on OF
select GENERIC_PHY
default ARCH_BCM_IPROC

View File

@ -153,8 +153,8 @@ static int cygnus_pcie_phy_probe(struct platform_device *pdev)
struct cygnus_pcie_phy *p;
if (of_property_read_u32(child, "reg", &id)) {
dev_err(dev, "missing reg property for %s\n",
child->name);
dev_err(dev, "missing reg property for %pOFn\n",
child);
ret = -EINVAL;
goto put_child;
}

View File

@ -47,6 +47,7 @@ enum brcm_sata_phy_version {
BRCM_SATA_PHY_IPROC_NS2,
BRCM_SATA_PHY_IPROC_NSP,
BRCM_SATA_PHY_IPROC_SR,
BRCM_SATA_PHY_DSL_28NM,
};
enum brcm_sata_phy_rxaeq_mode {
@ -96,7 +97,10 @@ enum sata_phy_regs {
PLLCONTROL_0_FREQ_DET_RESTART = BIT(13),
PLLCONTROL_0_FREQ_MONITOR = BIT(12),
PLLCONTROL_0_SEQ_START = BIT(15),
PLL_CAP_CHARGE_TIME = 0x83,
PLL_VCO_CAL_THRESH = 0x84,
PLL_CAP_CONTROL = 0x85,
PLL_FREQ_DET_TIME = 0x86,
PLL_ACTRL2 = 0x8b,
PLL_ACTRL2_SELDIV_MASK = 0x1f,
PLL_ACTRL2_SELDIV_SHIFT = 9,
@ -106,6 +110,9 @@ enum sata_phy_regs {
PLL1_ACTRL2 = 0x82,
PLL1_ACTRL3 = 0x83,
PLL1_ACTRL4 = 0x84,
PLL1_ACTRL5 = 0x85,
PLL1_ACTRL6 = 0x86,
PLL1_ACTRL7 = 0x87,
TX_REG_BANK = 0x070,
TX_ACTRL0 = 0x80,
@ -119,6 +126,8 @@ enum sata_phy_regs {
AEQ_FRC_EQ_FORCE = BIT(0),
AEQ_FRC_EQ_FORCE_VAL = BIT(1),
AEQRX_REG_BANK_1 = 0xe0,
AEQRX_SLCAL0_CTRL0 = 0x82,
AEQRX_SLCAL1_CTRL0 = 0x86,
OOB_REG_BANK = 0x150,
OOB1_REG_BANK = 0x160,
@ -168,6 +177,7 @@ static inline void __iomem *brcm_sata_pcb_base(struct brcm_sata_port *port)
switch (priv->version) {
case BRCM_SATA_PHY_STB_28NM:
case BRCM_SATA_PHY_IPROC_NS2:
case BRCM_SATA_PHY_DSL_28NM:
size = SATA_PCB_REG_28NM_SPACE_SIZE;
break;
case BRCM_SATA_PHY_STB_40NM:
@ -482,6 +492,61 @@ static int brcm_sr_sata_init(struct brcm_sata_port *port)
return 0;
}
static int brcm_dsl_sata_init(struct brcm_sata_port *port)
{
void __iomem *base = brcm_sata_pcb_base(port);
struct device *dev = port->phy_priv->dev;
unsigned int try;
u32 tmp;
brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL7, 0, 0x873);
brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL6, 0, 0xc000);
brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_REG_BANK_0_PLLCONTROL_0,
0, 0x3089);
usleep_range(1000, 2000);
brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_REG_BANK_0_PLLCONTROL_0,
0, 0x3088);
usleep_range(1000, 2000);
brcm_sata_phy_wr(base, AEQRX_REG_BANK_1, AEQRX_SLCAL0_CTRL0,
0, 0x3000);
brcm_sata_phy_wr(base, AEQRX_REG_BANK_1, AEQRX_SLCAL1_CTRL0,
0, 0x3000);
usleep_range(1000, 2000);
brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_CAP_CHARGE_TIME, 0, 0x32);
brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_VCO_CAL_THRESH, 0, 0xa);
brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_FREQ_DET_TIME, 0, 0x64);
usleep_range(1000, 2000);
/* Acquire PLL lock */
try = 50;
while (try) {
tmp = brcm_sata_phy_rd(base, BLOCK0_REG_BANK,
BLOCK0_XGXSSTATUS);
if (tmp & BLOCK0_XGXSSTATUS_PLL_LOCK)
break;
msleep(20);
try--;
};
if (!try) {
/* PLL did not lock; give up */
dev_err(dev, "port%d PLL did not lock\n", port->portnum);
return -ETIMEDOUT;
}
dev_dbg(dev, "port%d initialized\n", port->portnum);
return 0;
}
static int brcm_sata_phy_init(struct phy *phy)
{
int rc;
@ -501,6 +566,9 @@ static int brcm_sata_phy_init(struct phy *phy)
case BRCM_SATA_PHY_IPROC_SR:
rc = brcm_sr_sata_init(port);
break;
case BRCM_SATA_PHY_DSL_28NM:
rc = brcm_dsl_sata_init(port);
break;
default:
rc = -ENODEV;
}
@ -552,6 +620,8 @@ static const struct of_device_id brcm_sata_phy_of_match[] = {
.data = (void *)BRCM_SATA_PHY_IPROC_NSP },
{ .compatible = "brcm,iproc-sr-sata-phy",
.data = (void *)BRCM_SATA_PHY_IPROC_SR },
{ .compatible = "brcm,bcm63138-sata-phy",
.data = (void *)BRCM_SATA_PHY_DSL_28NM },
{},
};
MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match);
@ -600,8 +670,8 @@ static int brcm_sata_phy_probe(struct platform_device *pdev)
struct brcm_sata_port *port;
if (of_property_read_u32(child, "reg", &id)) {
dev_err(dev, "missing reg property in node %s\n",
child->name);
dev_err(dev, "missing reg property in node %pOFn\n",
child);
ret = -EINVAL;
goto put_child;
}

View File

@ -372,10 +372,8 @@ static int brcm_usb_phy_probe(struct platform_device *pdev)
clk_disable(priv->usb_30_clk);
phy_provider = devm_of_phy_provider_register(dev, brcm_usb_phy_xlate);
if (IS_ERR(phy_provider))
return PTR_ERR(phy_provider);
return 0;
return PTR_ERR_OR_ZERO(phy_provider);
}
#ifdef CONFIG_PM_SLEEP

View File

@ -0,0 +1,10 @@
#
# Phy driver for Cadence MHDP DisplayPort controller
#
config PHY_CADENCE_DP
tristate "Cadence MHDP DisplayPort PHY driver"
depends on OF
depends on HAS_IOMEM
select GENERIC_PHY
help
Support for Cadence MHDP DisplayPort PHY.

View File

@ -0,0 +1 @@
obj-$(CONFIG_PHY_CADENCE_DP) += phy-cadence-dp.o

View File

@ -0,0 +1,541 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Cadence MHDP DisplayPort SD0801 PHY driver.
*
* Copyright 2018 Cadence Design Systems, Inc.
*
*/
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#define DEFAULT_NUM_LANES 2
#define MAX_NUM_LANES 4
#define DEFAULT_MAX_BIT_RATE 8100 /* in Mbps */
#define POLL_TIMEOUT_US 2000
#define LANE_MASK 0x7
/*
* register offsets from DPTX PHY register block base (i.e MHDP
* register base + 0x30a00)
*/
#define PHY_AUX_CONFIG 0x00
#define PHY_AUX_CTRL 0x04
#define PHY_RESET 0x20
#define PHY_PMA_XCVR_PLLCLK_EN 0x24
#define PHY_PMA_XCVR_PLLCLK_EN_ACK 0x28
#define PHY_PMA_XCVR_POWER_STATE_REQ 0x2c
#define PHY_POWER_STATE_LN_0 0x0000
#define PHY_POWER_STATE_LN_1 0x0008
#define PHY_POWER_STATE_LN_2 0x0010
#define PHY_POWER_STATE_LN_3 0x0018
#define PHY_PMA_XCVR_POWER_STATE_ACK 0x30
#define PHY_PMA_CMN_READY 0x34
#define PHY_PMA_XCVR_TX_VMARGIN 0x38
#define PHY_PMA_XCVR_TX_DEEMPH 0x3c
/*
* register offsets from SD0801 PHY register block base (i.e MHDP
* register base + 0x500000)
*/
#define CMN_SSM_BANDGAP_TMR 0x00084
#define CMN_SSM_BIAS_TMR 0x00088
#define CMN_PLLSM0_PLLPRE_TMR 0x000a8
#define CMN_PLLSM0_PLLLOCK_TMR 0x000b0
#define CMN_PLLSM1_PLLPRE_TMR 0x000c8
#define CMN_PLLSM1_PLLLOCK_TMR 0x000d0
#define CMN_BGCAL_INIT_TMR 0x00190
#define CMN_BGCAL_ITER_TMR 0x00194
#define CMN_IBCAL_INIT_TMR 0x001d0
#define CMN_PLL0_VCOCAL_INIT_TMR 0x00210
#define CMN_PLL0_VCOCAL_ITER_TMR 0x00214
#define CMN_PLL0_VCOCAL_REFTIM_START 0x00218
#define CMN_PLL0_VCOCAL_PLLCNT_START 0x00220
#define CMN_PLL0_INTDIV_M0 0x00240
#define CMN_PLL0_FRACDIVL_M0 0x00244
#define CMN_PLL0_FRACDIVH_M0 0x00248
#define CMN_PLL0_HIGH_THR_M0 0x0024c
#define CMN_PLL0_DSM_DIAG_M0 0x00250
#define CMN_PLL0_LOCK_PLLCNT_START 0x00278
#define CMN_PLL1_VCOCAL_INIT_TMR 0x00310
#define CMN_PLL1_VCOCAL_ITER_TMR 0x00314
#define CMN_PLL1_DSM_DIAG_M0 0x00350
#define CMN_TXPUCAL_INIT_TMR 0x00410
#define CMN_TXPUCAL_ITER_TMR 0x00414
#define CMN_TXPDCAL_INIT_TMR 0x00430
#define CMN_TXPDCAL_ITER_TMR 0x00434
#define CMN_RXCAL_INIT_TMR 0x00450
#define CMN_RXCAL_ITER_TMR 0x00454
#define CMN_SD_CAL_INIT_TMR 0x00490
#define CMN_SD_CAL_ITER_TMR 0x00494
#define CMN_SD_CAL_REFTIM_START 0x00498
#define CMN_SD_CAL_PLLCNT_START 0x004a0
#define CMN_PDIAG_PLL0_CTRL_M0 0x00680
#define CMN_PDIAG_PLL0_CLK_SEL_M0 0x00684
#define CMN_PDIAG_PLL0_CP_PADJ_M0 0x00690
#define CMN_PDIAG_PLL0_CP_IADJ_M0 0x00694
#define CMN_PDIAG_PLL0_FILT_PADJ_M0 0x00698
#define CMN_PDIAG_PLL0_CP_PADJ_M1 0x006d0
#define CMN_PDIAG_PLL0_CP_IADJ_M1 0x006d4
#define CMN_PDIAG_PLL1_CLK_SEL_M0 0x00704
#define XCVR_DIAG_PLLDRC_CTRL 0x10394
#define XCVR_DIAG_HSCLK_SEL 0x10398
#define XCVR_DIAG_HSCLK_DIV 0x1039c
#define TX_PSC_A0 0x10400
#define TX_PSC_A1 0x10404
#define TX_PSC_A2 0x10408
#define TX_PSC_A3 0x1040c
#define RX_PSC_A0 0x20000
#define RX_PSC_A1 0x20004
#define RX_PSC_A2 0x20008
#define RX_PSC_A3 0x2000c
#define PHY_PLL_CFG 0x30038
struct cdns_dp_phy {
void __iomem *base; /* DPTX registers base */
void __iomem *sd_base; /* SD0801 registers base */
u32 num_lanes; /* Number of lanes to use */
u32 max_bit_rate; /* Maximum link bit rate to use (in Mbps) */
struct device *dev;
};
static int cdns_dp_phy_init(struct phy *phy);
static void cdns_dp_phy_run(struct cdns_dp_phy *cdns_phy);
static void cdns_dp_phy_wait_pma_cmn_ready(struct cdns_dp_phy *cdns_phy);
static void cdns_dp_phy_pma_cfg(struct cdns_dp_phy *cdns_phy);
static void cdns_dp_phy_pma_cmn_cfg_25mhz(struct cdns_dp_phy *cdns_phy);
static void cdns_dp_phy_pma_lane_cfg(struct cdns_dp_phy *cdns_phy,
unsigned int lane);
static void cdns_dp_phy_pma_cmn_vco_cfg_25mhz(struct cdns_dp_phy *cdns_phy);
static void cdns_dp_phy_pma_cmn_rate(struct cdns_dp_phy *cdns_phy);
static void cdns_dp_phy_write_field(struct cdns_dp_phy *cdns_phy,
unsigned int offset,
unsigned char start_bit,
unsigned char num_bits,
unsigned int val);
static const struct phy_ops cdns_dp_phy_ops = {
.init = cdns_dp_phy_init,
.owner = THIS_MODULE,
};
static int cdns_dp_phy_init(struct phy *phy)
{
unsigned char lane_bits;
struct cdns_dp_phy *cdns_phy = phy_get_drvdata(phy);
writel(0x0003, cdns_phy->base + PHY_AUX_CTRL); /* enable AUX */
/* PHY PMA registers configuration function */
cdns_dp_phy_pma_cfg(cdns_phy);
/*
* Set lines power state to A0
* Set lines pll clk enable to 0
*/
cdns_dp_phy_write_field(cdns_phy, PHY_PMA_XCVR_POWER_STATE_REQ,
PHY_POWER_STATE_LN_0, 6, 0x0000);
if (cdns_phy->num_lanes >= 2) {
cdns_dp_phy_write_field(cdns_phy,
PHY_PMA_XCVR_POWER_STATE_REQ,
PHY_POWER_STATE_LN_1, 6, 0x0000);
if (cdns_phy->num_lanes == 4) {
cdns_dp_phy_write_field(cdns_phy,
PHY_PMA_XCVR_POWER_STATE_REQ,
PHY_POWER_STATE_LN_2, 6, 0);
cdns_dp_phy_write_field(cdns_phy,
PHY_PMA_XCVR_POWER_STATE_REQ,
PHY_POWER_STATE_LN_3, 6, 0);
}
}
cdns_dp_phy_write_field(cdns_phy, PHY_PMA_XCVR_PLLCLK_EN,
0, 1, 0x0000);
if (cdns_phy->num_lanes >= 2) {
cdns_dp_phy_write_field(cdns_phy, PHY_PMA_XCVR_PLLCLK_EN,
1, 1, 0x0000);
if (cdns_phy->num_lanes == 4) {
cdns_dp_phy_write_field(cdns_phy,
PHY_PMA_XCVR_PLLCLK_EN,
2, 1, 0x0000);
cdns_dp_phy_write_field(cdns_phy,
PHY_PMA_XCVR_PLLCLK_EN,
3, 1, 0x0000);
}
}
/*
* release phy_l0*_reset_n and pma_tx_elec_idle_ln_* based on
* used lanes
*/
lane_bits = (1 << cdns_phy->num_lanes) - 1;
writel(((0xF & ~lane_bits) << 4) | (0xF & lane_bits),
cdns_phy->base + PHY_RESET);
/* release pma_xcvr_pllclk_en_ln_*, only for the master lane */
writel(0x0001, cdns_phy->base + PHY_PMA_XCVR_PLLCLK_EN);
/* PHY PMA registers configuration functions */
cdns_dp_phy_pma_cmn_vco_cfg_25mhz(cdns_phy);
cdns_dp_phy_pma_cmn_rate(cdns_phy);
/* take out of reset */
cdns_dp_phy_write_field(cdns_phy, PHY_RESET, 8, 1, 1);
cdns_dp_phy_wait_pma_cmn_ready(cdns_phy);
cdns_dp_phy_run(cdns_phy);
return 0;
}
static void cdns_dp_phy_wait_pma_cmn_ready(struct cdns_dp_phy *cdns_phy)
{
unsigned int reg;
int ret;
ret = readl_poll_timeout(cdns_phy->base + PHY_PMA_CMN_READY, reg,
reg & 1, 0, 500);
if (ret == -ETIMEDOUT)
dev_err(cdns_phy->dev,
"timeout waiting for PMA common ready\n");
}
static void cdns_dp_phy_pma_cfg(struct cdns_dp_phy *cdns_phy)
{
unsigned int i;
/* PMA common configuration */
cdns_dp_phy_pma_cmn_cfg_25mhz(cdns_phy);
/* PMA lane configuration to deal with multi-link operation */
for (i = 0; i < cdns_phy->num_lanes; i++)
cdns_dp_phy_pma_lane_cfg(cdns_phy, i);
}
static void cdns_dp_phy_pma_cmn_cfg_25mhz(struct cdns_dp_phy *cdns_phy)
{
/* refclock registers - assumes 25 MHz refclock */
writel(0x0019, cdns_phy->sd_base + CMN_SSM_BIAS_TMR);
writel(0x0032, cdns_phy->sd_base + CMN_PLLSM0_PLLPRE_TMR);
writel(0x00D1, cdns_phy->sd_base + CMN_PLLSM0_PLLLOCK_TMR);
writel(0x0032, cdns_phy->sd_base + CMN_PLLSM1_PLLPRE_TMR);
writel(0x00D1, cdns_phy->sd_base + CMN_PLLSM1_PLLLOCK_TMR);
writel(0x007D, cdns_phy->sd_base + CMN_BGCAL_INIT_TMR);
writel(0x007D, cdns_phy->sd_base + CMN_BGCAL_ITER_TMR);
writel(0x0019, cdns_phy->sd_base + CMN_IBCAL_INIT_TMR);
writel(0x001E, cdns_phy->sd_base + CMN_TXPUCAL_INIT_TMR);
writel(0x0006, cdns_phy->sd_base + CMN_TXPUCAL_ITER_TMR);
writel(0x001E, cdns_phy->sd_base + CMN_TXPDCAL_INIT_TMR);
writel(0x0006, cdns_phy->sd_base + CMN_TXPDCAL_ITER_TMR);
writel(0x02EE, cdns_phy->sd_base + CMN_RXCAL_INIT_TMR);
writel(0x0006, cdns_phy->sd_base + CMN_RXCAL_ITER_TMR);
writel(0x0002, cdns_phy->sd_base + CMN_SD_CAL_INIT_TMR);
writel(0x0002, cdns_phy->sd_base + CMN_SD_CAL_ITER_TMR);
writel(0x000E, cdns_phy->sd_base + CMN_SD_CAL_REFTIM_START);
writel(0x012B, cdns_phy->sd_base + CMN_SD_CAL_PLLCNT_START);
/* PLL registers */
writel(0x0409, cdns_phy->sd_base + CMN_PDIAG_PLL0_CP_PADJ_M0);
writel(0x1001, cdns_phy->sd_base + CMN_PDIAG_PLL0_CP_IADJ_M0);
writel(0x0F08, cdns_phy->sd_base + CMN_PDIAG_PLL0_FILT_PADJ_M0);
writel(0x0004, cdns_phy->sd_base + CMN_PLL0_DSM_DIAG_M0);
writel(0x00FA, cdns_phy->sd_base + CMN_PLL0_VCOCAL_INIT_TMR);
writel(0x0004, cdns_phy->sd_base + CMN_PLL0_VCOCAL_ITER_TMR);
writel(0x00FA, cdns_phy->sd_base + CMN_PLL1_VCOCAL_INIT_TMR);
writel(0x0004, cdns_phy->sd_base + CMN_PLL1_VCOCAL_ITER_TMR);
writel(0x0318, cdns_phy->sd_base + CMN_PLL0_VCOCAL_REFTIM_START);
}
static void cdns_dp_phy_pma_cmn_vco_cfg_25mhz(struct cdns_dp_phy *cdns_phy)
{
/* Assumes 25 MHz refclock */
switch (cdns_phy->max_bit_rate) {
/* Setting VCO for 10.8GHz */
case 2700:
case 5400:
writel(0x01B0, cdns_phy->sd_base + CMN_PLL0_INTDIV_M0);
writel(0x0000, cdns_phy->sd_base + CMN_PLL0_FRACDIVL_M0);
writel(0x0002, cdns_phy->sd_base + CMN_PLL0_FRACDIVH_M0);
writel(0x0120, cdns_phy->sd_base + CMN_PLL0_HIGH_THR_M0);
break;
/* Setting VCO for 9.72GHz */
case 2430:
case 3240:
writel(0x0184, cdns_phy->sd_base + CMN_PLL0_INTDIV_M0);
writel(0xCCCD, cdns_phy->sd_base + CMN_PLL0_FRACDIVL_M0);
writel(0x0002, cdns_phy->sd_base + CMN_PLL0_FRACDIVH_M0);
writel(0x0104, cdns_phy->sd_base + CMN_PLL0_HIGH_THR_M0);
break;
/* Setting VCO for 8.64GHz */
case 2160:
case 4320:
writel(0x0159, cdns_phy->sd_base + CMN_PLL0_INTDIV_M0);
writel(0x999A, cdns_phy->sd_base + CMN_PLL0_FRACDIVL_M0);
writel(0x0002, cdns_phy->sd_base + CMN_PLL0_FRACDIVH_M0);
writel(0x00E7, cdns_phy->sd_base + CMN_PLL0_HIGH_THR_M0);
break;
/* Setting VCO for 8.1GHz */
case 8100:
writel(0x0144, cdns_phy->sd_base + CMN_PLL0_INTDIV_M0);
writel(0x0000, cdns_phy->sd_base + CMN_PLL0_FRACDIVL_M0);
writel(0x0002, cdns_phy->sd_base + CMN_PLL0_FRACDIVH_M0);
writel(0x00D8, cdns_phy->sd_base + CMN_PLL0_HIGH_THR_M0);
break;
}
writel(0x0002, cdns_phy->sd_base + CMN_PDIAG_PLL0_CTRL_M0);
writel(0x0318, cdns_phy->sd_base + CMN_PLL0_VCOCAL_PLLCNT_START);
}
static void cdns_dp_phy_pma_cmn_rate(struct cdns_dp_phy *cdns_phy)
{
unsigned int clk_sel_val = 0;
unsigned int hsclk_div_val = 0;
unsigned int i;
/* 16'h0000 for single DP link configuration */
writel(0x0000, cdns_phy->sd_base + PHY_PLL_CFG);
switch (cdns_phy->max_bit_rate) {
case 1620:
clk_sel_val = 0x0f01;
hsclk_div_val = 2;
break;
case 2160:
case 2430:
case 2700:
clk_sel_val = 0x0701;
hsclk_div_val = 1;
break;
case 3240:
clk_sel_val = 0x0b00;
hsclk_div_val = 2;
break;
case 4320:
case 5400:
clk_sel_val = 0x0301;
hsclk_div_val = 0;
break;
case 8100:
clk_sel_val = 0x0200;
hsclk_div_val = 0;
break;
}
writel(clk_sel_val, cdns_phy->sd_base + CMN_PDIAG_PLL0_CLK_SEL_M0);
/* PMA lane configuration to deal with multi-link operation */
for (i = 0; i < cdns_phy->num_lanes; i++) {
writel(hsclk_div_val,
cdns_phy->sd_base + (XCVR_DIAG_HSCLK_DIV | (i<<11)));
}
}
static void cdns_dp_phy_pma_lane_cfg(struct cdns_dp_phy *cdns_phy,
unsigned int lane)
{
unsigned int lane_bits = (lane & LANE_MASK) << 11;
/* Writing Tx/Rx Power State Controllers registers */
writel(0x00FB, cdns_phy->sd_base + (TX_PSC_A0 | lane_bits));
writel(0x04AA, cdns_phy->sd_base + (TX_PSC_A2 | lane_bits));
writel(0x04AA, cdns_phy->sd_base + (TX_PSC_A3 | lane_bits));
writel(0x0000, cdns_phy->sd_base + (RX_PSC_A0 | lane_bits));
writel(0x0000, cdns_phy->sd_base + (RX_PSC_A2 | lane_bits));
writel(0x0000, cdns_phy->sd_base + (RX_PSC_A3 | lane_bits));
writel(0x0001, cdns_phy->sd_base + (XCVR_DIAG_PLLDRC_CTRL | lane_bits));
writel(0x0000, cdns_phy->sd_base + (XCVR_DIAG_HSCLK_SEL | lane_bits));
}
static void cdns_dp_phy_run(struct cdns_dp_phy *cdns_phy)
{
unsigned int read_val;
u32 write_val1 = 0;
u32 write_val2 = 0;
u32 mask = 0;
int ret;
/*
* waiting for ACK of pma_xcvr_pllclk_en_ln_*, only for the
* master lane
*/
ret = readl_poll_timeout(cdns_phy->base + PHY_PMA_XCVR_PLLCLK_EN_ACK,
read_val, read_val & 1, 0, POLL_TIMEOUT_US);
if (ret == -ETIMEDOUT)
dev_err(cdns_phy->dev,
"timeout waiting for link PLL clock enable ack\n");
ndelay(100);
switch (cdns_phy->num_lanes) {
case 1: /* lane 0 */
write_val1 = 0x00000004;
write_val2 = 0x00000001;
mask = 0x0000003f;
break;
case 2: /* lane 0-1 */
write_val1 = 0x00000404;
write_val2 = 0x00000101;
mask = 0x00003f3f;
break;
case 4: /* lane 0-3 */
write_val1 = 0x04040404;
write_val2 = 0x01010101;
mask = 0x3f3f3f3f;
break;
}
writel(write_val1, cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_REQ);
ret = readl_poll_timeout(cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_ACK,
read_val, (read_val & mask) == write_val1, 0,
POLL_TIMEOUT_US);
if (ret == -ETIMEDOUT)
dev_err(cdns_phy->dev,
"timeout waiting for link power state ack\n");
writel(0, cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_REQ);
ndelay(100);
writel(write_val2, cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_REQ);
ret = readl_poll_timeout(cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_ACK,
read_val, (read_val & mask) == write_val2, 0,
POLL_TIMEOUT_US);
if (ret == -ETIMEDOUT)
dev_err(cdns_phy->dev,
"timeout waiting for link power state ack\n");
writel(0, cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_REQ);
ndelay(100);
}
static void cdns_dp_phy_write_field(struct cdns_dp_phy *cdns_phy,
unsigned int offset,
unsigned char start_bit,
unsigned char num_bits,
unsigned int val)
{
unsigned int read_val;
read_val = readl(cdns_phy->base + offset);
writel(((val << start_bit) | (read_val & ~(((1 << num_bits) - 1) <<
start_bit))), cdns_phy->base + offset);
}
static int cdns_dp_phy_probe(struct platform_device *pdev)
{
struct resource *regs;
struct cdns_dp_phy *cdns_phy;
struct device *dev = &pdev->dev;
struct phy_provider *phy_provider;
struct phy *phy;
int err;
cdns_phy = devm_kzalloc(dev, sizeof(*cdns_phy), GFP_KERNEL);
if (!cdns_phy)
return -ENOMEM;
cdns_phy->dev = &pdev->dev;
phy = devm_phy_create(dev, NULL, &cdns_dp_phy_ops);
if (IS_ERR(phy)) {
dev_err(dev, "failed to create DisplayPort PHY\n");
return PTR_ERR(phy);
}
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
cdns_phy->base = devm_ioremap_resource(&pdev->dev, regs);
if (IS_ERR(cdns_phy->base))
return PTR_ERR(cdns_phy->base);
regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
cdns_phy->sd_base = devm_ioremap_resource(&pdev->dev, regs);
if (IS_ERR(cdns_phy->sd_base))
return PTR_ERR(cdns_phy->sd_base);
err = device_property_read_u32(dev, "num_lanes",
&(cdns_phy->num_lanes));
if (err)
cdns_phy->num_lanes = DEFAULT_NUM_LANES;
switch (cdns_phy->num_lanes) {
case 1:
case 2:
case 4:
/* valid number of lanes */
break;
default:
dev_err(dev, "unsupported number of lanes: %d\n",
cdns_phy->num_lanes);
return -EINVAL;
}
err = device_property_read_u32(dev, "max_bit_rate",
&(cdns_phy->max_bit_rate));
if (err)
cdns_phy->max_bit_rate = DEFAULT_MAX_BIT_RATE;
switch (cdns_phy->max_bit_rate) {
case 2160:
case 2430:
case 2700:
case 3240:
case 4320:
case 5400:
case 8100:
/* valid bit rate */
break;
default:
dev_err(dev, "unsupported max bit rate: %dMbps\n",
cdns_phy->max_bit_rate);
return -EINVAL;
}
phy_set_drvdata(phy, cdns_phy);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
dev_info(dev, "%d lanes, max bit rate %d.%03d Gbps\n",
cdns_phy->num_lanes,
cdns_phy->max_bit_rate / 1000,
cdns_phy->max_bit_rate % 1000);
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct of_device_id cdns_dp_phy_of_match[] = {
{
.compatible = "cdns,dp-phy"
},
{}
};
MODULE_DEVICE_TABLE(of, cdns_dp_phy_of_match);
static struct platform_driver cdns_dp_phy_driver = {
.probe = cdns_dp_phy_probe,
.driver = {
.name = "cdns-dp-phy",
.of_match_table = cdns_dp_phy_of_match,
}
};
module_platform_driver(cdns_dp_phy_driver);
MODULE_AUTHOR("Cadence Design Systems, Inc.");
MODULE_DESCRIPTION("Cadence MHDP PHY driver");
MODULE_LICENSE("GPL v2");

View File

@ -156,7 +156,6 @@ static int ltq_rcu_usb2_of_parse(struct ltq_rcu_usb2_priv *priv,
{
struct device *dev = priv->dev;
const __be32 *offset;
int ret;
priv->reg_bits = of_device_get_match_data(dev);
@ -196,10 +195,8 @@ static int ltq_rcu_usb2_of_parse(struct ltq_rcu_usb2_priv *priv,
}
priv->phy_reset = devm_reset_control_get_optional(dev, "phy");
if (IS_ERR(priv->phy_reset))
return PTR_ERR(priv->phy_reset);
return 0;
return PTR_ERR_OR_ZERO(priv->phy_reset);
}
static int ltq_rcu_usb2_phy_probe(struct platform_device *pdev)

View File

@ -59,3 +59,14 @@ config PHY_PXA_28NM_USB2
The PHY driver will be used by Marvell udc/ehci/otg driver.
To compile this driver as a module, choose M here.
config PHY_PXA_USB
tristate "Marvell PXA USB PHY Driver"
depends on ARCH_PXA || ARCH_MMP
select GENERIC_PHY
help
Enable this to support Marvell PXA USB PHY driver for Marvell
SoC. This driver will do the PHY initialization and shutdown.
The PHY driver will be used by Marvell udc/ehci/otg driver.
To compile this driver as a module, choose M here.

View File

@ -6,3 +6,4 @@ obj-$(CONFIG_PHY_MVEBU_CP110_COMPHY) += phy-mvebu-cp110-comphy.o
obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
obj-$(CONFIG_PHY_PXA_28NM_HSIC) += phy-pxa-28nm-hsic.o
obj-$(CONFIG_PHY_PXA_28NM_USB2) += phy-pxa-28nm-usb2.o
obj-$(CONFIG_PHY_PXA_USB) += phy-pxa-usb.o

View File

@ -231,14 +231,14 @@ static int phy_berlin_sata_probe(struct platform_device *pdev)
struct phy_berlin_desc *phy_desc;
if (of_property_read_u32(child, "reg", &phy_id)) {
dev_err(dev, "missing reg property in node %s\n",
child->name);
dev_err(dev, "missing reg property in node %pOFn\n",
child);
ret = -EINVAL;
goto put_child;
}
if (phy_id >= ARRAY_SIZE(phy_berlin_power_down_bits)) {
dev_err(dev, "invalid reg in node %s\n", child->name);
dev_err(dev, "invalid reg in node %pOFn\n", child);
ret = -EINVAL;
goto put_child;
}

View File

@ -0,0 +1,345 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2011 Marvell International Ltd. All rights reserved.
* Copyright (C) 2018 Lubomir Rintel <lkundrak@v3.sk>
*/
#include <dt-bindings/phy/phy.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
/* phy regs */
#define UTMI_REVISION 0x0
#define UTMI_CTRL 0x4
#define UTMI_PLL 0x8
#define UTMI_TX 0xc
#define UTMI_RX 0x10
#define UTMI_IVREF 0x14
#define UTMI_T0 0x18
#define UTMI_T1 0x1c
#define UTMI_T2 0x20
#define UTMI_T3 0x24
#define UTMI_T4 0x28
#define UTMI_T5 0x2c
#define UTMI_RESERVE 0x30
#define UTMI_USB_INT 0x34
#define UTMI_DBG_CTL 0x38
#define UTMI_OTG_ADDON 0x3c
/* For UTMICTRL Register */
#define UTMI_CTRL_USB_CLK_EN (1 << 31)
/* pxa168 */
#define UTMI_CTRL_SUSPEND_SET1 (1 << 30)
#define UTMI_CTRL_SUSPEND_SET2 (1 << 29)
#define UTMI_CTRL_RXBUF_PDWN (1 << 24)
#define UTMI_CTRL_TXBUF_PDWN (1 << 11)
#define UTMI_CTRL_INPKT_DELAY_SHIFT 30
#define UTMI_CTRL_INPKT_DELAY_SOF_SHIFT 28
#define UTMI_CTRL_PU_REF_SHIFT 20
#define UTMI_CTRL_ARC_PULLDN_SHIFT 12
#define UTMI_CTRL_PLL_PWR_UP_SHIFT 1
#define UTMI_CTRL_PWR_UP_SHIFT 0
/* For UTMI_PLL Register */
#define UTMI_PLL_PLLCALI12_SHIFT 29
#define UTMI_PLL_PLLCALI12_MASK (0x3 << 29)
#define UTMI_PLL_PLLVDD18_SHIFT 27
#define UTMI_PLL_PLLVDD18_MASK (0x3 << 27)
#define UTMI_PLL_PLLVDD12_SHIFT 25
#define UTMI_PLL_PLLVDD12_MASK (0x3 << 25)
#define UTMI_PLL_CLK_BLK_EN_SHIFT 24
#define CLK_BLK_EN (0x1 << 24)
#define PLL_READY (0x1 << 23)
#define KVCO_EXT (0x1 << 22)
#define VCOCAL_START (0x1 << 21)
#define UTMI_PLL_KVCO_SHIFT 15
#define UTMI_PLL_KVCO_MASK (0x7 << 15)
#define UTMI_PLL_ICP_SHIFT 12
#define UTMI_PLL_ICP_MASK (0x7 << 12)
#define UTMI_PLL_FBDIV_SHIFT 4
#define UTMI_PLL_FBDIV_MASK (0xFF << 4)
#define UTMI_PLL_REFDIV_SHIFT 0
#define UTMI_PLL_REFDIV_MASK (0xF << 0)
/* For UTMI_TX Register */
#define UTMI_TX_REG_EXT_FS_RCAL_SHIFT 27
#define UTMI_TX_REG_EXT_FS_RCAL_MASK (0xf << 27)
#define UTMI_TX_REG_EXT_FS_RCAL_EN_SHIFT 26
#define UTMI_TX_REG_EXT_FS_RCAL_EN_MASK (0x1 << 26)
#define UTMI_TX_TXVDD12_SHIFT 22
#define UTMI_TX_TXVDD12_MASK (0x3 << 22)
#define UTMI_TX_CK60_PHSEL_SHIFT 17
#define UTMI_TX_CK60_PHSEL_MASK (0xf << 17)
#define UTMI_TX_IMPCAL_VTH_SHIFT 14
#define UTMI_TX_IMPCAL_VTH_MASK (0x7 << 14)
#define REG_RCAL_START (0x1 << 12)
#define UTMI_TX_LOW_VDD_EN_SHIFT 11
#define UTMI_TX_AMP_SHIFT 0
#define UTMI_TX_AMP_MASK (0x7 << 0)
/* For UTMI_RX Register */
#define UTMI_REG_SQ_LENGTH_SHIFT 15
#define UTMI_REG_SQ_LENGTH_MASK (0x3 << 15)
#define UTMI_RX_SQ_THRESH_SHIFT 4
#define UTMI_RX_SQ_THRESH_MASK (0xf << 4)
#define UTMI_OTG_ADDON_OTG_ON (1 << 0)
enum pxa_usb_phy_version {
PXA_USB_PHY_MMP2,
PXA_USB_PHY_PXA910,
PXA_USB_PHY_PXA168,
};
struct pxa_usb_phy {
struct phy *phy;
void __iomem *base;
enum pxa_usb_phy_version version;
};
/*****************************************************************************
* The registers read/write routines
*****************************************************************************/
static unsigned int u2o_get(void __iomem *base, unsigned int offset)
{
return readl_relaxed(base + offset);
}
static void u2o_set(void __iomem *base, unsigned int offset,
unsigned int value)
{
u32 reg;
reg = readl_relaxed(base + offset);
reg |= value;
writel_relaxed(reg, base + offset);
readl_relaxed(base + offset);
}
static void u2o_clear(void __iomem *base, unsigned int offset,
unsigned int value)
{
u32 reg;
reg = readl_relaxed(base + offset);
reg &= ~value;
writel_relaxed(reg, base + offset);
readl_relaxed(base + offset);
}
static void u2o_write(void __iomem *base, unsigned int offset,
unsigned int value)
{
writel_relaxed(value, base + offset);
readl_relaxed(base + offset);
}
static int pxa_usb_phy_init(struct phy *phy)
{
struct pxa_usb_phy *pxa_usb_phy = phy_get_drvdata(phy);
void __iomem *base = pxa_usb_phy->base;
int loops;
dev_info(&phy->dev, "initializing Marvell PXA USB PHY");
/* Initialize the USB PHY power */
if (pxa_usb_phy->version == PXA_USB_PHY_PXA910) {
u2o_set(base, UTMI_CTRL, (1<<UTMI_CTRL_INPKT_DELAY_SOF_SHIFT)
| (1<<UTMI_CTRL_PU_REF_SHIFT));
}
u2o_set(base, UTMI_CTRL, 1<<UTMI_CTRL_PLL_PWR_UP_SHIFT);
u2o_set(base, UTMI_CTRL, 1<<UTMI_CTRL_PWR_UP_SHIFT);
/* UTMI_PLL settings */
u2o_clear(base, UTMI_PLL, UTMI_PLL_PLLVDD18_MASK
| UTMI_PLL_PLLVDD12_MASK | UTMI_PLL_PLLCALI12_MASK
| UTMI_PLL_FBDIV_MASK | UTMI_PLL_REFDIV_MASK
| UTMI_PLL_ICP_MASK | UTMI_PLL_KVCO_MASK);
u2o_set(base, UTMI_PLL, 0xee<<UTMI_PLL_FBDIV_SHIFT
| 0xb<<UTMI_PLL_REFDIV_SHIFT | 3<<UTMI_PLL_PLLVDD18_SHIFT
| 3<<UTMI_PLL_PLLVDD12_SHIFT | 3<<UTMI_PLL_PLLCALI12_SHIFT
| 1<<UTMI_PLL_ICP_SHIFT | 3<<UTMI_PLL_KVCO_SHIFT);
/* UTMI_TX */
u2o_clear(base, UTMI_TX, UTMI_TX_REG_EXT_FS_RCAL_EN_MASK
| UTMI_TX_TXVDD12_MASK | UTMI_TX_CK60_PHSEL_MASK
| UTMI_TX_IMPCAL_VTH_MASK | UTMI_TX_REG_EXT_FS_RCAL_MASK
| UTMI_TX_AMP_MASK);
u2o_set(base, UTMI_TX, 3<<UTMI_TX_TXVDD12_SHIFT
| 4<<UTMI_TX_CK60_PHSEL_SHIFT | 4<<UTMI_TX_IMPCAL_VTH_SHIFT
| 8<<UTMI_TX_REG_EXT_FS_RCAL_SHIFT | 3<<UTMI_TX_AMP_SHIFT);
/* UTMI_RX */
u2o_clear(base, UTMI_RX, UTMI_RX_SQ_THRESH_MASK
| UTMI_REG_SQ_LENGTH_MASK);
u2o_set(base, UTMI_RX, 7<<UTMI_RX_SQ_THRESH_SHIFT
| 2<<UTMI_REG_SQ_LENGTH_SHIFT);
/* UTMI_IVREF */
if (pxa_usb_phy->version == PXA_USB_PHY_PXA168) {
/*
* fixing Microsoft Altair board interface with NEC hub issue -
* Set UTMI_IVREF from 0x4a3 to 0x4bf
*/
u2o_write(base, UTMI_IVREF, 0x4bf);
}
/* toggle VCOCAL_START bit of UTMI_PLL */
udelay(200);
u2o_set(base, UTMI_PLL, VCOCAL_START);
udelay(40);
u2o_clear(base, UTMI_PLL, VCOCAL_START);
/* toggle REG_RCAL_START bit of UTMI_TX */
udelay(400);
u2o_set(base, UTMI_TX, REG_RCAL_START);
udelay(40);
u2o_clear(base, UTMI_TX, REG_RCAL_START);
udelay(400);
/* Make sure PHY PLL is ready */
loops = 0;
while ((u2o_get(base, UTMI_PLL) & PLL_READY) == 0) {
mdelay(1);
loops++;
if (loops > 100) {
dev_warn(&phy->dev, "calibrate timeout, UTMI_PLL %x\n",
u2o_get(base, UTMI_PLL));
break;
}
}
if (pxa_usb_phy->version == PXA_USB_PHY_PXA168) {
u2o_set(base, UTMI_RESERVE, 1 << 5);
/* Turn on UTMI PHY OTG extension */
u2o_write(base, UTMI_OTG_ADDON, 1);
}
return 0;
}
static int pxa_usb_phy_exit(struct phy *phy)
{
struct pxa_usb_phy *pxa_usb_phy = phy_get_drvdata(phy);
void __iomem *base = pxa_usb_phy->base;
dev_info(&phy->dev, "deinitializing Marvell PXA USB PHY");
if (pxa_usb_phy->version == PXA_USB_PHY_PXA168)
u2o_clear(base, UTMI_OTG_ADDON, UTMI_OTG_ADDON_OTG_ON);
u2o_clear(base, UTMI_CTRL, UTMI_CTRL_RXBUF_PDWN);
u2o_clear(base, UTMI_CTRL, UTMI_CTRL_TXBUF_PDWN);
u2o_clear(base, UTMI_CTRL, UTMI_CTRL_USB_CLK_EN);
u2o_clear(base, UTMI_CTRL, 1<<UTMI_CTRL_PWR_UP_SHIFT);
u2o_clear(base, UTMI_CTRL, 1<<UTMI_CTRL_PLL_PWR_UP_SHIFT);
return 0;
}
static const struct phy_ops pxa_usb_phy_ops = {
.init = pxa_usb_phy_init,
.exit = pxa_usb_phy_exit,
.owner = THIS_MODULE,
};
static const struct of_device_id pxa_usb_phy_of_match[] = {
{
.compatible = "marvell,mmp2-usb-phy",
.data = (void *)PXA_USB_PHY_MMP2,
}, {
.compatible = "marvell,pxa910-usb-phy",
.data = (void *)PXA_USB_PHY_PXA910,
}, {
.compatible = "marvell,pxa168-usb-phy",
.data = (void *)PXA_USB_PHY_PXA168,
},
{ },
};
MODULE_DEVICE_TABLE(of, pxa_usb_phy_of_match);
static int pxa_usb_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *resource;
struct pxa_usb_phy *pxa_usb_phy;
struct phy_provider *provider;
const struct of_device_id *of_id;
pxa_usb_phy = devm_kzalloc(dev, sizeof(struct pxa_usb_phy), GFP_KERNEL);
if (!pxa_usb_phy)
return -ENOMEM;
of_id = of_match_node(pxa_usb_phy_of_match, dev->of_node);
if (of_id)
pxa_usb_phy->version = (enum pxa_usb_phy_version)of_id->data;
else
pxa_usb_phy->version = PXA_USB_PHY_MMP2;
resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pxa_usb_phy->base = devm_ioremap_resource(dev, resource);
if (IS_ERR(pxa_usb_phy->base)) {
dev_err(dev, "failed to remap PHY regs\n");
return PTR_ERR(pxa_usb_phy->base);
}
pxa_usb_phy->phy = devm_phy_create(dev, NULL, &pxa_usb_phy_ops);
if (IS_ERR(pxa_usb_phy->phy)) {
dev_err(dev, "failed to create PHY\n");
return PTR_ERR(pxa_usb_phy->phy);
}
phy_set_drvdata(pxa_usb_phy->phy, pxa_usb_phy);
provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (IS_ERR(provider)) {
dev_err(dev, "failed to register PHY provider\n");
return PTR_ERR(provider);
}
if (!dev->of_node) {
phy_create_lookup(pxa_usb_phy->phy, "usb", "mv-udc");
phy_create_lookup(pxa_usb_phy->phy, "usb", "pxa-u2oehci");
phy_create_lookup(pxa_usb_phy->phy, "usb", "mv-otg");
}
dev_info(dev, "Marvell PXA USB PHY");
return 0;
}
static struct platform_driver pxa_usb_phy_driver = {
.probe = pxa_usb_phy_probe,
.driver = {
.name = "pxa-usb-phy",
.of_match_table = pxa_usb_phy_of_match,
},
};
module_platform_driver(pxa_usb_phy_driver);
MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
MODULE_DESCRIPTION("Marvell PXA USB PHY Driver");
MODULE_LICENSE("GPL v2");

View File

@ -50,6 +50,23 @@ config PHY_QCOM_UFS
help
Support for UFS PHY on QCOM chipsets.
if PHY_QCOM_UFS
config PHY_QCOM_UFS_14NM
tristate
default PHY_QCOM_UFS
help
Support for 14nm UFS QMP phy present on QCOM chipsets.
config PHY_QCOM_UFS_20NM
tristate
default PHY_QCOM_UFS
depends on BROKEN
help
Support for 20nm UFS QMP phy present on QCOM chipsets.
endif
config PHY_QCOM_USB_HS
tristate "Qualcomm USB HS PHY module"
depends on USB_ULPI_BUS

View File

@ -5,7 +5,7 @@ obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.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-14nm.o
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o
obj-$(CONFIG_PHY_QCOM_UFS_14NM) += phy-qcom-ufs-qmp-14nm.o
obj-$(CONFIG_PHY_QCOM_UFS_20NM) += phy-qcom-ufs-qmp-20nm.o
obj-$(CONFIG_PHY_QCOM_USB_HS) += phy-qcom-usb-hs.o
obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o

View File

@ -156,6 +156,11 @@ static const unsigned int qmp_v3_usb3phy_regs_layout[] = {
[QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x170,
};
static const unsigned int sdm845_ufsphy_regs_layout[] = {
[QPHY_START_CTRL] = 0x00,
[QPHY_PCS_READY_STATUS] = 0x160,
};
static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1c),
QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10),
@ -601,6 +606,83 @@ static const struct qmp_phy_init_tbl qmp_v3_usb3_uniphy_pcs_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V3_PCS_REFGEN_REQ_CONFIG2, 0x60),
};
static const struct qmp_phy_init_tbl sdm845_ufsphy_serdes_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x04),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x0a),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0xd5),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL, 0x20),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x01),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_CTRL, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x04),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x05),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_INITVAL1, 0xff),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_INITVAL2, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xda),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0xff),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x0c),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE1, 0x98),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE1, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE1, 0x16),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE1, 0x36),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE1, 0x3f),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE1, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE1, 0xc1),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE1, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE1, 0x32),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE1, 0x0f),
/* Rate B */
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x44),
};
static const struct qmp_phy_init_tbl sdm845_ufsphy_tx_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x04),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x07),
};
static const struct qmp_phy_init_tbl sdm845_ufsphy_rx_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_LVL, 0x24),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x0f),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x1e),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_INTERFACE_MODE, 0x40),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_TERM_BW, 0x5b),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1b),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SVS_SO_GAIN_HALF, 0x04),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SVS_SO_GAIN_QUARTER, 0x04),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SVS_SO_GAIN, 0x04),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x81),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x80),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x59),
};
static const struct qmp_phy_init_tbl sdm845_ufsphy_pcs_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_CTRL2, 0x6e),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TX_LARGE_AMP_DRV_LVL, 0x0a),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TX_SMALL_AMP_DRV_LVL, 0x02),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SYM_RESYNC_CTRL, 0x03),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TX_MID_TERM_CTRL1, 0x43),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_CTRL1, 0x0f),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_MIN_HIBERN8_TIME, 0x9a),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_MULTI_LANE_CTRL1, 0x02),
};
/* struct qmp_phy_cfg - per-PHY initialization config */
struct qmp_phy_cfg {
@ -649,9 +731,14 @@ struct qmp_phy_cfg {
/* true, if PHY has a separate DP_COM control block */
bool has_phy_dp_com_ctrl;
/* true, if PHY has secondary tx/rx lanes to be configured */
bool is_dual_lane_phy;
/* Register offset of secondary tx/rx lanes for USB DP combo PHY */
unsigned int tx_b_lane_offset;
unsigned int rx_b_lane_offset;
/* true, if PCS block has no separate SW_RESET register */
bool no_pcs_sw_reset;
};
/**
@ -748,6 +835,10 @@ static const char * const qmp_v3_phy_clk_l[] = {
"aux", "cfg_ahb", "ref", "com_aux",
};
static const char * const sdm845_ufs_phy_clk_l[] = {
"ref", "ref_aux",
};
/* list of resets */
static const char * const msm8996_pciephy_reset_l[] = {
"phy", "common", "cfg",
@ -758,7 +849,7 @@ static const char * const msm8996_usb3phy_reset_l[] = {
};
/* list of regulators */
static const char * const msm8996_phy_vreg_l[] = {
static const char * const qmp_phy_vreg_l[] = {
"vdda-phy", "vdda-pll",
};
@ -778,8 +869,8 @@ static const struct qmp_phy_cfg msm8996_pciephy_cfg = {
.num_clks = ARRAY_SIZE(msm8996_phy_clk_l),
.reset_list = msm8996_pciephy_reset_l,
.num_resets = ARRAY_SIZE(msm8996_pciephy_reset_l),
.vreg_list = msm8996_phy_vreg_l,
.num_vregs = ARRAY_SIZE(msm8996_phy_vreg_l),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
.regs = pciephy_regs_layout,
.start_ctrl = PCS_START | PLL_READY_GATE_EN,
@ -809,8 +900,8 @@ static const struct qmp_phy_cfg msm8996_usb3phy_cfg = {
.num_clks = ARRAY_SIZE(msm8996_phy_clk_l),
.reset_list = msm8996_usb3phy_reset_l,
.num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
.vreg_list = msm8996_phy_vreg_l,
.num_vregs = ARRAY_SIZE(msm8996_phy_vreg_l),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
.regs = usb3phy_regs_layout,
.start_ctrl = SERDES_START | PCS_START,
@ -870,8 +961,8 @@ static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = {
.num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l),
.reset_list = msm8996_usb3phy_reset_l,
.num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
.vreg_list = msm8996_phy_vreg_l,
.num_vregs = ARRAY_SIZE(msm8996_phy_vreg_l),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
.regs = qmp_v3_usb3phy_regs_layout,
.start_ctrl = SERDES_START | PCS_START,
@ -883,6 +974,7 @@ static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = {
.pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
.has_phy_dp_com_ctrl = true,
.is_dual_lane_phy = true,
.tx_b_lane_offset = 0x400,
.rx_b_lane_offset = 0x400,
};
@ -903,8 +995,8 @@ static const struct qmp_phy_cfg qmp_v3_usb3_uniphy_cfg = {
.num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l),
.reset_list = msm8996_usb3phy_reset_l,
.num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
.vreg_list = msm8996_phy_vreg_l,
.num_vregs = ARRAY_SIZE(msm8996_phy_vreg_l),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
.regs = qmp_v3_usb3phy_regs_layout,
.start_ctrl = SERDES_START | PCS_START,
@ -916,6 +1008,35 @@ static const struct qmp_phy_cfg qmp_v3_usb3_uniphy_cfg = {
.pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
};
static const struct qmp_phy_cfg sdm845_ufsphy_cfg = {
.type = PHY_TYPE_UFS,
.nlanes = 2,
.serdes_tbl = sdm845_ufsphy_serdes_tbl,
.serdes_tbl_num = ARRAY_SIZE(sdm845_ufsphy_serdes_tbl),
.tx_tbl = sdm845_ufsphy_tx_tbl,
.tx_tbl_num = ARRAY_SIZE(sdm845_ufsphy_tx_tbl),
.rx_tbl = sdm845_ufsphy_rx_tbl,
.rx_tbl_num = ARRAY_SIZE(sdm845_ufsphy_rx_tbl),
.pcs_tbl = sdm845_ufsphy_pcs_tbl,
.pcs_tbl_num = ARRAY_SIZE(sdm845_ufsphy_pcs_tbl),
.clk_list = sdm845_ufs_phy_clk_l,
.num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
.regs = sdm845_ufsphy_regs_layout,
.start_ctrl = SERDES_START,
.pwrdn_ctrl = SW_PWRDN,
.mask_pcs_ready = PCS_READY,
.is_dual_lane_phy = true,
.tx_b_lane_offset = 0x400,
.rx_b_lane_offset = 0x400,
.no_pcs_sw_reset = true,
};
static void qcom_qmp_phy_configure(void __iomem *base,
const unsigned int *regs,
const struct qmp_phy_init_tbl tbl[],
@ -935,10 +1056,12 @@ static void qcom_qmp_phy_configure(void __iomem *base,
}
}
static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp)
static int qcom_qmp_phy_com_init(struct qmp_phy *qphy)
{
struct qcom_qmp *qmp = qphy->qmp;
const struct qmp_phy_cfg *cfg = qmp->cfg;
void __iomem *serdes = qmp->serdes;
void __iomem *pcs = qphy->pcs;
void __iomem *dp_com = qmp->dp_com;
int ret, i;
@ -979,10 +1102,6 @@ static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp)
goto err_rst;
}
if (cfg->has_phy_com_ctrl)
qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL],
SW_PWRDN);
if (cfg->has_phy_dp_com_ctrl) {
qphy_setbits(dp_com, QPHY_V3_DP_COM_POWER_DOWN_CTRL,
SW_PWRDN);
@ -1000,6 +1119,12 @@ static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp)
SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
}
if (cfg->has_phy_com_ctrl)
qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL],
SW_PWRDN);
else
qphy_setbits(pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl);
/* Serdes configuration */
qcom_qmp_phy_configure(serdes, cfg->regs, cfg->serdes_tbl,
cfg->serdes_tbl_num);
@ -1090,7 +1215,7 @@ static int qcom_qmp_phy_init(struct phy *phy)
dev_vdbg(qmp->dev, "Initializing QMP phy\n");
ret = qcom_qmp_phy_com_init(qmp);
ret = qcom_qmp_phy_com_init(qphy);
if (ret)
return ret;
@ -1112,22 +1237,31 @@ static int qcom_qmp_phy_init(struct phy *phy)
/* Tx, Rx, and PCS configurations */
qcom_qmp_phy_configure(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num);
/* Configuration for other LANE for USB-DP combo PHY */
if (cfg->has_phy_dp_com_ctrl)
if (cfg->is_dual_lane_phy)
qcom_qmp_phy_configure(tx + cfg->tx_b_lane_offset, cfg->regs,
cfg->tx_tbl, cfg->tx_tbl_num);
qcom_qmp_phy_configure(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num);
if (cfg->has_phy_dp_com_ctrl)
if (cfg->is_dual_lane_phy)
qcom_qmp_phy_configure(rx + cfg->rx_b_lane_offset, cfg->regs,
cfg->rx_tbl, cfg->rx_tbl_num);
qcom_qmp_phy_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num);
/*
* UFS PHY requires the deassert of software reset before serdes start.
* For UFS PHYs that do not have software reset control bits, defer
* starting serdes until the power on callback.
*/
if ((cfg->type == PHY_TYPE_UFS) && cfg->no_pcs_sw_reset)
goto out;
/*
* Pull out PHY from POWER DOWN state.
* This is active low enable signal to power-down PHY.
*/
qphy_setbits(pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl);
if(cfg->type == PHY_TYPE_PCIE)
qphy_setbits(pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl);
if (cfg->has_pwrdn_delay)
usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max);
@ -1151,6 +1285,7 @@ static int qcom_qmp_phy_init(struct phy *phy)
}
qmp->phy_initialized = true;
out:
return ret;
err_pcs_ready:
@ -1173,7 +1308,8 @@ static int qcom_qmp_phy_exit(struct phy *phy)
clk_disable_unprepare(qphy->pipe_clk);
/* PHY reset */
qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
if (!cfg->no_pcs_sw_reset)
qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
/* stop SerDes and Phy-Coding-Sublayer */
qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
@ -1191,6 +1327,44 @@ static int qcom_qmp_phy_exit(struct phy *phy)
return 0;
}
static int qcom_qmp_phy_poweron(struct phy *phy)
{
struct qmp_phy *qphy = phy_get_drvdata(phy);
struct qcom_qmp *qmp = qphy->qmp;
const struct qmp_phy_cfg *cfg = qmp->cfg;
void __iomem *pcs = qphy->pcs;
void __iomem *status;
unsigned int mask, val;
int ret = 0;
if (cfg->type != PHY_TYPE_UFS)
return 0;
/*
* For UFS PHY that has not software reset control, serdes start
* should only happen when UFS driver explicitly calls phy_power_on
* after it deasserts software reset.
*/
if (cfg->no_pcs_sw_reset && !qmp->phy_initialized &&
(qmp->init_count != 0)) {
/* start SerDes and Phy-Coding-Sublayer */
qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
status = pcs + cfg->regs[QPHY_PCS_READY_STATUS];
mask = cfg->mask_pcs_ready;
ret = readl_poll_timeout(status, val, !(val & mask), 1,
PHY_INIT_COMPLETE_TIMEOUT);
if (ret) {
dev_err(qmp->dev, "phy initialization timed-out\n");
return ret;
}
qmp->phy_initialized = true;
}
return ret;
}
static int qcom_qmp_phy_set_mode(struct phy *phy, enum phy_mode mode)
{
struct qmp_phy *qphy = phy_get_drvdata(phy);
@ -1400,7 +1574,7 @@ static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np)
ret = of_property_read_string(np, "clock-output-names", &init.name);
if (ret) {
dev_err(qmp->dev, "%s: No clock-output-names\n", np->name);
dev_err(qmp->dev, "%pOFn: No clock-output-names\n", np);
return ret;
}
@ -1420,6 +1594,7 @@ static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np)
static const struct phy_ops qcom_qmp_phy_gen_ops = {
.init = qcom_qmp_phy_init,
.exit = qcom_qmp_phy_exit,
.power_on = qcom_qmp_phy_poweron,
.set_mode = qcom_qmp_phy_set_mode,
.owner = THIS_MODULE,
};
@ -1522,6 +1697,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
}, {
.compatible = "qcom,sdm845-qmp-usb3-uni-phy",
.data = &qmp_v3_usb3_uniphy_cfg,
}, {
.compatible = "qcom,sdm845-qmp-ufs-phy",
.data = &sdm845_ufsphy_cfg,
},
{ },
};
@ -1586,7 +1764,9 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
ret = qcom_qmp_phy_vreg_init(dev);
if (ret) {
dev_err(dev, "failed to get regulator supplies\n");
if (ret != -EPROBE_DEFER)
dev_err(dev, "failed to get regulator supplies: %d\n",
ret);
return ret;
}

View File

@ -184,6 +184,8 @@
#define QSERDES_V3_COM_VCO_TUNE2_MODE0 0x0f8
#define QSERDES_V3_COM_VCO_TUNE1_MODE1 0x0fc
#define QSERDES_V3_COM_VCO_TUNE2_MODE1 0x100
#define QSERDES_V3_COM_VCO_TUNE_INITVAL1 0x104
#define QSERDES_V3_COM_VCO_TUNE_INITVAL2 0x108
#define QSERDES_V3_COM_VCO_TUNE_TIMER1 0x11c
#define QSERDES_V3_COM_VCO_TUNE_TIMER2 0x120
#define QSERDES_V3_COM_CLK_SELECT 0x138
@ -211,8 +213,13 @@
/* Only for QMP V3 PHY - RX registers */
#define QSERDES_V3_RX_UCDR_SO_GAIN_HALF 0x00c
#define QSERDES_V3_RX_UCDR_SO_GAIN 0x014
#define QSERDES_V3_RX_UCDR_SVS_SO_GAIN_HALF 0x024
#define QSERDES_V3_RX_UCDR_SVS_SO_GAIN_QUARTER 0x028
#define QSERDES_V3_RX_UCDR_SVS_SO_GAIN 0x02c
#define QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN 0x030
#define QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE 0x034
#define QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW 0x03c
#define QSERDES_V3_RX_UCDR_PI_CONTROLS 0x044
#define QSERDES_V3_RX_RX_TERM_BW 0x07c
#define QSERDES_V3_RX_VGA_CAL_CNTRL1 0x0bc
#define QSERDES_V3_RX_VGA_CAL_CNTRL2 0x0c0
@ -239,6 +246,8 @@
#define QPHY_V3_PCS_TXMGN_V3 0x018
#define QPHY_V3_PCS_TXMGN_V4 0x01c
#define QPHY_V3_PCS_TXMGN_LS 0x020
#define QPHY_V3_PCS_TX_LARGE_AMP_DRV_LVL 0x02c
#define QPHY_V3_PCS_TX_SMALL_AMP_DRV_LVL 0x034
#define QPHY_V3_PCS_TXDEEMPH_M6DB_V0 0x024
#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0 0x028
#define QPHY_V3_PCS_TXDEEMPH_M6DB_V1 0x02c
@ -275,6 +284,12 @@
#define QPHY_V3_PCS_FLL_CNT_VAL_L 0x0cc
#define QPHY_V3_PCS_FLL_CNT_VAL_H_TOL 0x0d0
#define QPHY_V3_PCS_FLL_MAN_CODE 0x0d4
#define QPHY_V3_PCS_RX_SYM_RESYNC_CTRL 0x134
#define QPHY_V3_PCS_RX_MIN_HIBERN8_TIME 0x138
#define QPHY_V3_PCS_RX_SIGDET_CTRL1 0x13c
#define QPHY_V3_PCS_RX_SIGDET_CTRL2 0x140
#define QPHY_V3_PCS_TX_MID_TERM_CTRL1 0x1bc
#define QPHY_V3_PCS_MULTI_LANE_CTRL1 0x1c4
#define QPHY_V3_PCS_RX_SIGDET_LVL 0x1d8
#define QPHY_V3_PCS_REFGEN_REQ_CONFIG1 0x20c
#define QPHY_V3_PCS_REFGEN_REQ_CONFIG2 0x210

View File

@ -800,7 +800,9 @@ static int qusb2_phy_probe(struct platform_device *pdev)
ret = devm_regulator_bulk_get(dev, num, qphy->vregs);
if (ret) {
dev_err(dev, "failed to get regulator supplies\n");
if (ret != -EPROBE_DEFER)
dev_err(dev, "failed to get regulator supplies: %d\n",
ret);
return ret;
}

View File

@ -17,9 +17,9 @@
#include <linux/module.h>
#include <linux/clk.h>
#include <linux/phy/phy.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/phy/phy-qcom-ufs.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/delay.h>

View File

@ -431,56 +431,6 @@ static void ufs_qcom_phy_disable_ref_clk(struct ufs_qcom_phy *phy)
}
}
#define UFS_REF_CLK_EN (1 << 5)
static void ufs_qcom_phy_dev_ref_clk_ctrl(struct phy *generic_phy, bool enable)
{
struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy);
if (phy->dev_ref_clk_ctrl_mmio &&
(enable ^ phy->is_dev_ref_clk_enabled)) {
u32 temp = readl_relaxed(phy->dev_ref_clk_ctrl_mmio);
if (enable)
temp |= UFS_REF_CLK_EN;
else
temp &= ~UFS_REF_CLK_EN;
/*
* If we are here to disable this clock immediately after
* entering into hibern8, we need to make sure that device
* ref_clk is active atleast 1us after the hibern8 enter.
*/
if (!enable)
udelay(1);
writel_relaxed(temp, phy->dev_ref_clk_ctrl_mmio);
/* ensure that ref_clk is enabled/disabled before we return */
wmb();
/*
* If we call hibern8 exit after this, we need to make sure that
* device ref_clk is stable for atleast 1us before the hibern8
* exit command.
*/
if (enable)
udelay(1);
phy->is_dev_ref_clk_enabled = enable;
}
}
void ufs_qcom_phy_enable_dev_ref_clk(struct phy *generic_phy)
{
ufs_qcom_phy_dev_ref_clk_ctrl(generic_phy, true);
}
EXPORT_SYMBOL_GPL(ufs_qcom_phy_enable_dev_ref_clk);
void ufs_qcom_phy_disable_dev_ref_clk(struct phy *generic_phy)
{
ufs_qcom_phy_dev_ref_clk_ctrl(generic_phy, false);
}
EXPORT_SYMBOL_GPL(ufs_qcom_phy_disable_dev_ref_clk);
/* Turn ON M-PHY RMMI interface clocks */
static int ufs_qcom_phy_enable_iface_clk(struct ufs_qcom_phy *phy)
{

View File

@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
#
# Phy drivers for Renesas platforms
#

View File

@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o
obj-$(CONFIG_PHY_RCAR_GEN3_PCIE) += phy-rcar-gen3-pcie.o
obj-$(CONFIG_PHY_RCAR_GEN3_USB2) += phy-rcar-gen3-usb2.o

View File

@ -1,12 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas R-Car Gen2 PHY driver
*
* Copyright (C) 2014 Renesas Solutions Corp.
* Copyright (C) 2014 Cogent Embedded, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clk.h>

View File

@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas R-Car Gen3 for USB2.0 PHY driver
*
@ -6,10 +7,6 @@
* This is based on the phy-rcar-gen2 driver:
* Copyright (C) 2014 Renesas Solutions Corp.
* Copyright (C) 2014 Cogent Embedded, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/extcon-provider.h>
@ -81,18 +78,29 @@
#define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */
#define USB2_ADPCTRL_DRVVBUS BIT(4)
#define RCAR_GEN3_PHY_HAS_DEDICATED_PINS 1
struct rcar_gen3_chan {
void __iomem *base;
struct extcon_dev *extcon;
struct phy *phy;
struct regulator *vbus;
struct work_struct work;
enum usb_dr_mode dr_mode;
bool extcon_host;
bool has_otg_pins;
bool is_otg_channel;
bool uses_otg_pins;
};
/*
* Combination about is_otg_channel and uses_otg_pins:
*
* Parameters || Behaviors
* is_otg_channel | uses_otg_pins || irqs | role sysfs
* ---------------------+---------------++--------------+------------
* true | true || enabled | enabled
* true | false || disabled | enabled
* false | any || disabled | disabled
*/
static void rcar_gen3_phy_usb2_work(struct work_struct *work)
{
struct rcar_gen3_chan *ch = container_of(work, struct rcar_gen3_chan,
@ -147,6 +155,18 @@ static void rcar_gen3_enable_vbus_ctrl(struct rcar_gen3_chan *ch, int vbus)
writel(val, usb2_base + USB2_ADPCTRL);
}
static void rcar_gen3_control_otg_irq(struct rcar_gen3_chan *ch, int enable)
{
void __iomem *usb2_base = ch->base;
u32 val = readl(usb2_base + USB2_OBINTEN);
if (ch->uses_otg_pins && enable)
val |= USB2_OBINT_BITS;
else
val &= ~USB2_OBINT_BITS;
writel(val, usb2_base + USB2_OBINTEN);
}
static void rcar_gen3_init_for_host(struct rcar_gen3_chan *ch)
{
rcar_gen3_set_linectrl(ch, 1, 1);
@ -192,20 +212,19 @@ static void rcar_gen3_init_for_a_peri(struct rcar_gen3_chan *ch)
static void rcar_gen3_init_from_a_peri_to_a_host(struct rcar_gen3_chan *ch)
{
void __iomem *usb2_base = ch->base;
u32 val;
rcar_gen3_control_otg_irq(ch, 0);
val = readl(usb2_base + USB2_OBINTEN);
writel(val & ~USB2_OBINT_BITS, usb2_base + USB2_OBINTEN);
rcar_gen3_enable_vbus_ctrl(ch, 0);
rcar_gen3_enable_vbus_ctrl(ch, 1);
rcar_gen3_init_for_host(ch);
writel(val | USB2_OBINT_BITS, usb2_base + USB2_OBINTEN);
rcar_gen3_control_otg_irq(ch, 1);
}
static bool rcar_gen3_check_id(struct rcar_gen3_chan *ch)
{
if (!ch->uses_otg_pins)
return (ch->dr_mode == USB_DR_MODE_HOST) ? false : true;
return !!(readl(ch->base + USB2_ADPCTRL) & USB2_ADPCTRL_IDDIG);
}
@ -237,7 +256,7 @@ static ssize_t role_store(struct device *dev, struct device_attribute *attr,
bool is_b_device;
enum phy_mode cur_mode, new_mode;
if (!ch->has_otg_pins || !ch->phy->init_count)
if (!ch->is_otg_channel || !ch->phy->init_count)
return -EIO;
if (!strncmp(buf, "host", strlen("host")))
@ -275,7 +294,7 @@ static ssize_t role_show(struct device *dev, struct device_attribute *attr,
{
struct rcar_gen3_chan *ch = dev_get_drvdata(dev);
if (!ch->has_otg_pins || !ch->phy->init_count)
if (!ch->is_otg_channel || !ch->phy->init_count)
return -EIO;
return sprintf(buf, "%s\n", rcar_gen3_is_host(ch) ? "host" :
@ -291,8 +310,7 @@ static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch)
val = readl(usb2_base + USB2_VBCTRL);
writel(val | USB2_VBCTRL_DRVVBUSSEL, usb2_base + USB2_VBCTRL);
writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA);
val = readl(usb2_base + USB2_OBINTEN);
writel(val | USB2_OBINT_BITS, usb2_base + USB2_OBINTEN);
rcar_gen3_control_otg_irq(ch, 1);
val = readl(usb2_base + USB2_ADPCTRL);
writel(val | USB2_ADPCTRL_IDPULLUP, usb2_base + USB2_ADPCTRL);
val = readl(usb2_base + USB2_LINECTRL1);
@ -314,7 +332,7 @@ static int rcar_gen3_phy_usb2_init(struct phy *p)
writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET);
/* Initialize otg part */
if (channel->has_otg_pins)
if (channel->is_otg_channel)
rcar_gen3_init_otg(channel);
return 0;
@ -388,21 +406,10 @@ static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch)
}
static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = {
{
.compatible = "renesas,usb2-phy-r8a7795",
.data = (void *)RCAR_GEN3_PHY_HAS_DEDICATED_PINS,
},
{
.compatible = "renesas,usb2-phy-r8a7796",
.data = (void *)RCAR_GEN3_PHY_HAS_DEDICATED_PINS,
},
{
.compatible = "renesas,usb2-phy-r8a77965",
.data = (void *)RCAR_GEN3_PHY_HAS_DEDICATED_PINS,
},
{
.compatible = "renesas,rcar-gen3-usb2-phy",
},
{ .compatible = "renesas,usb2-phy-r8a7795" },
{ .compatible = "renesas,usb2-phy-r8a7796" },
{ .compatible = "renesas,usb2-phy-r8a77965" },
{ .compatible = "renesas,rcar-gen3-usb2-phy" },
{ }
};
MODULE_DEVICE_TABLE(of, rcar_gen3_phy_usb2_match_table);
@ -445,10 +452,13 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
dev_err(dev, "No irq handler (%d)\n", irq);
}
if (of_usb_get_dr_mode_by_phy(dev->of_node, 0) == USB_DR_MODE_OTG) {
channel->dr_mode = of_usb_get_dr_mode_by_phy(dev->of_node, 0);
if (channel->dr_mode != USB_DR_MODE_UNKNOWN) {
int ret;
channel->has_otg_pins = (uintptr_t)of_device_get_match_data(dev);
channel->is_otg_channel = true;
channel->uses_otg_pins = !of_property_read_bool(dev->of_node,
"renesas,no-otg-pins");
channel->extcon = devm_extcon_dev_allocate(dev,
rcar_gen3_phy_cable);
if (IS_ERR(channel->extcon))
@ -490,7 +500,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
dev_err(dev, "Failed to register PHY provider\n");
ret = PTR_ERR(provider);
goto error;
} else if (channel->has_otg_pins) {
} else if (channel->is_otg_channel) {
int ret;
ret = device_create_file(dev, &dev_attr_role);
@ -510,7 +520,7 @@ static int rcar_gen3_phy_usb2_remove(struct platform_device *pdev)
{
struct rcar_gen3_chan *channel = platform_get_drvdata(pdev);
if (channel->has_otg_pins)
if (channel->is_otg_channel)
device_remove_file(&pdev->dev, &dev_attr_role);
pm_runtime_disable(&pdev->dev);

View File

@ -1,11 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas R-Car Gen3 for USB3.0 PHY driver
*
* Copyright (C) 2017 Renesas Electronics Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clk.h>

View File

@ -15,6 +15,14 @@ config PHY_ROCKCHIP_EMMC
help
Enable this to support the Rockchip EMMC PHY.
config PHY_ROCKCHIP_INNO_HDMI
tristate "Rockchip INNO HDMI PHY Driver"
depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF
depends on COMMON_CLK
select GENERIC_PHY
help
Enable this to support the Rockchip Innosilicon HDMI PHY.
config PHY_ROCKCHIP_INNO_USB2
tristate "Rockchip INNO USB2PHY Driver"
depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF

View File

@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_PHY_ROCKCHIP_DP) += phy-rockchip-dp.o
obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o
obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o
obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o
obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o

View File

@ -337,8 +337,8 @@ static int rockchip_emmc_phy_probe(struct platform_device *pdev)
return -ENOMEM;
if (of_property_read_u32(dev->of_node, "reg", &reg_offset)) {
dev_err(dev, "missing reg property in node %s\n",
dev->of_node->name);
dev_err(dev, "missing reg property in node %pOFn\n",
dev->of_node);
return -EINVAL;
}

File diff suppressed because it is too large Load Diff

View File

@ -1116,8 +1116,8 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
}
if (of_property_read_u32(np, "reg", &reg)) {
dev_err(dev, "the reg property is not assigned in %s node\n",
np->name);
dev_err(dev, "the reg property is not assigned in %pOFn node\n",
np);
return -EINVAL;
}
@ -1143,8 +1143,8 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
}
if (!rphy->phy_cfg) {
dev_err(dev, "no phy-config can be matched with %s node\n",
np->name);
dev_err(dev, "no phy-config can be matched with %pOFn node\n",
np);
return -EINVAL;
}

View File

@ -1145,8 +1145,8 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
}
if (!tcphy->port_cfgs) {
dev_err(dev, "no phy-config can be matched with %s node\n",
np->name);
dev_err(dev, "no phy-config can be matched with %pOFn node\n",
np);
return -EINVAL;
}
@ -1186,8 +1186,8 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
continue;
if (IS_ERR(phy)) {
dev_err(dev, "failed to create phy: %s\n",
child_np->name);
dev_err(dev, "failed to create phy: %pOFn\n",
child_np);
pm_runtime_disable(dev);
return PTR_ERR(phy);
}

View File

@ -36,7 +36,22 @@ static int enable_usb_uart;
#define HIWORD_UPDATE(val, mask) \
((val) | (mask) << 16)
#define UOC_CON0_SIDDQ BIT(13)
#define UOC_CON0 0x00
#define UOC_CON0_SIDDQ BIT(13)
#define UOC_CON0_DISABLE BIT(4)
#define UOC_CON0_COMMON_ON_N BIT(0)
#define UOC_CON2 0x08
#define UOC_CON2_SOFT_CON_SEL BIT(2)
#define UOC_CON3 0x0c
/* bits present on rk3188 and rk3288 phys */
#define UOC_CON3_UTMI_TERMSEL_FULLSPEED BIT(5)
#define UOC_CON3_UTMI_XCVRSEELCT_FSTRANSC (1 << 3)
#define UOC_CON3_UTMI_XCVRSEELCT_MASK (3 << 3)
#define UOC_CON3_UTMI_OPMODE_NODRIVING (1 << 1)
#define UOC_CON3_UTMI_OPMODE_MASK (3 << 1)
#define UOC_CON3_UTMI_SUSPENDN BIT(0)
struct rockchip_usb_phys {
int reg;
@ -46,7 +61,8 @@ struct rockchip_usb_phys {
struct rockchip_usb_phy_base;
struct rockchip_usb_phy_pdata {
struct rockchip_usb_phys *phys;
int (*init_usb_uart)(struct regmap *grf);
int (*init_usb_uart)(struct regmap *grf,
const struct rockchip_usb_phy_pdata *pdata);
int usb_uart_phy;
};
@ -208,8 +224,8 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
rk_phy->np = child;
if (of_property_read_u32(child, "reg", &reg_offset)) {
dev_err(base->dev, "missing reg property in node %s\n",
child->name);
dev_err(base->dev, "missing reg property in node %pOFn\n",
child);
return -EINVAL;
}
@ -313,28 +329,88 @@ static const struct rockchip_usb_phy_pdata rk3066a_pdata = {
},
};
static int __init rockchip_init_usb_uart_common(struct regmap *grf,
const struct rockchip_usb_phy_pdata *pdata)
{
int regoffs = pdata->phys[pdata->usb_uart_phy].reg;
int ret;
u32 val;
/*
* COMMON_ON and DISABLE settings are described in the TRM,
* but were not present in the original code.
* Also disable the analog phy components to save power.
*/
val = HIWORD_UPDATE(UOC_CON0_COMMON_ON_N
| UOC_CON0_DISABLE
| UOC_CON0_SIDDQ,
UOC_CON0_COMMON_ON_N
| UOC_CON0_DISABLE
| UOC_CON0_SIDDQ);
ret = regmap_write(grf, regoffs + UOC_CON0, val);
if (ret)
return ret;
val = HIWORD_UPDATE(UOC_CON2_SOFT_CON_SEL,
UOC_CON2_SOFT_CON_SEL);
ret = regmap_write(grf, regoffs + UOC_CON2, val);
if (ret)
return ret;
val = HIWORD_UPDATE(UOC_CON3_UTMI_OPMODE_NODRIVING
| UOC_CON3_UTMI_XCVRSEELCT_FSTRANSC
| UOC_CON3_UTMI_TERMSEL_FULLSPEED,
UOC_CON3_UTMI_SUSPENDN
| UOC_CON3_UTMI_OPMODE_MASK
| UOC_CON3_UTMI_XCVRSEELCT_MASK
| UOC_CON3_UTMI_TERMSEL_FULLSPEED);
ret = regmap_write(grf, UOC_CON3, val);
if (ret)
return ret;
return 0;
}
#define RK3188_UOC0_CON0 0x10c
#define RK3188_UOC0_CON0_BYPASSSEL BIT(9)
#define RK3188_UOC0_CON0_BYPASSDMEN BIT(8)
/*
* Enable the bypass of uart2 data through the otg usb phy.
* See description of rk3288-variant for details.
*/
static int __init rk3188_init_usb_uart(struct regmap *grf,
const struct rockchip_usb_phy_pdata *pdata)
{
u32 val;
int ret;
ret = rockchip_init_usb_uart_common(grf, pdata);
if (ret)
return ret;
val = HIWORD_UPDATE(RK3188_UOC0_CON0_BYPASSSEL
| RK3188_UOC0_CON0_BYPASSDMEN,
RK3188_UOC0_CON0_BYPASSSEL
| RK3188_UOC0_CON0_BYPASSDMEN);
ret = regmap_write(grf, RK3188_UOC0_CON0, val);
if (ret)
return ret;
return 0;
}
static const struct rockchip_usb_phy_pdata rk3188_pdata = {
.phys = (struct rockchip_usb_phys[]){
{ .reg = 0x10c, .pll_name = "sclk_otgphy0_480m" },
{ .reg = 0x11c, .pll_name = "sclk_otgphy1_480m" },
{ /* sentinel */ }
},
.init_usb_uart = rk3188_init_usb_uart,
.usb_uart_phy = 0,
};
#define RK3288_UOC0_CON0 0x320
#define RK3288_UOC0_CON0_COMMON_ON_N BIT(0)
#define RK3288_UOC0_CON0_DISABLE BIT(4)
#define RK3288_UOC0_CON2 0x328
#define RK3288_UOC0_CON2_SOFT_CON_SEL BIT(2)
#define RK3288_UOC0_CON3 0x32c
#define RK3288_UOC0_CON3_UTMI_SUSPENDN BIT(0)
#define RK3288_UOC0_CON3_UTMI_OPMODE_NODRIVING (1 << 1)
#define RK3288_UOC0_CON3_UTMI_OPMODE_MASK (3 << 1)
#define RK3288_UOC0_CON3_UTMI_XCVRSEELCT_FSTRANSC (1 << 3)
#define RK3288_UOC0_CON3_UTMI_XCVRSEELCT_MASK (3 << 3)
#define RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED BIT(5)
#define RK3288_UOC0_CON3_BYPASSDMEN BIT(6)
#define RK3288_UOC0_CON3_BYPASSSEL BIT(7)
@ -353,40 +429,13 @@ static const struct rockchip_usb_phy_pdata rk3188_pdata = {
*
* The actual code in the vendor kernel does some things differently.
*/
static int __init rk3288_init_usb_uart(struct regmap *grf)
static int __init rk3288_init_usb_uart(struct regmap *grf,
const struct rockchip_usb_phy_pdata *pdata)
{
u32 val;
int ret;
/*
* COMMON_ON and DISABLE settings are described in the TRM,
* but were not present in the original code.
* Also disable the analog phy components to save power.
*/
val = HIWORD_UPDATE(RK3288_UOC0_CON0_COMMON_ON_N
| RK3288_UOC0_CON0_DISABLE
| UOC_CON0_SIDDQ,
RK3288_UOC0_CON0_COMMON_ON_N
| RK3288_UOC0_CON0_DISABLE
| UOC_CON0_SIDDQ);
ret = regmap_write(grf, RK3288_UOC0_CON0, val);
if (ret)
return ret;
val = HIWORD_UPDATE(RK3288_UOC0_CON2_SOFT_CON_SEL,
RK3288_UOC0_CON2_SOFT_CON_SEL);
ret = regmap_write(grf, RK3288_UOC0_CON2, val);
if (ret)
return ret;
val = HIWORD_UPDATE(RK3288_UOC0_CON3_UTMI_OPMODE_NODRIVING
| RK3288_UOC0_CON3_UTMI_XCVRSEELCT_FSTRANSC
| RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED,
RK3288_UOC0_CON3_UTMI_SUSPENDN
| RK3288_UOC0_CON3_UTMI_OPMODE_MASK
| RK3288_UOC0_CON3_UTMI_XCVRSEELCT_MASK
| RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED);
ret = regmap_write(grf, RK3288_UOC0_CON3, val);
ret = rockchip_init_usb_uart_common(grf, pdata);
if (ret)
return ret;
@ -516,7 +565,7 @@ static int __init rockchip_init_usb_uart(void)
return PTR_ERR(grf);
}
ret = data->init_usb_uart(grf);
ret = data->init_usb_uart(grf, data);
if (ret) {
pr_err("%s: could not init usb_uart, %d\n", __func__, ret);
enable_usb_uart = 0;

View File

@ -0,0 +1,34 @@
#
# PHY drivers for Socionext platforms.
#
config PHY_UNIPHIER_USB2
tristate "UniPhier USB2 PHY driver"
depends on ARCH_UNIPHIER || COMPILE_TEST
depends on OF && HAS_IOMEM
select GENERIC_PHY
select MFD_SYSCON
help
Enable this to support USB PHY implemented on USB2 controller
on UniPhier SoCs. This driver provides interface to interact
with USB 2.0 PHY that is part of the UniPhier SoC.
In case of Pro4, it is necessary to specify this USB2 PHY instead
of USB3 HS-PHY.
config PHY_UNIPHIER_USB3
tristate "UniPhier USB3 PHY driver"
depends on ARCH_UNIPHIER || COMPILE_TEST
depends on OF && HAS_IOMEM
select GENERIC_PHY
help
Enable this to support USB PHY implemented in USB3 controller
on UniPhier SoCs. This controller supports USB3.0 and lower speed.
config PHY_UNIPHIER_PCIE
tristate "Uniphier PHY driver for PCIe controller"
depends on (ARCH_UNIPHIER || COMPILE_TEST) && OF
default PCIE_UNIPHIER
select GENERIC_PHY
help
Enable this to support PHY implemented in PCIe controller
on UniPhier SoCs. This driver supports LD20 and PXs3 SoCs.

View File

@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the phy drivers.
#
obj-$(CONFIG_PHY_UNIPHIER_USB2) += phy-uniphier-usb2.o
obj-$(CONFIG_PHY_UNIPHIER_USB3) += phy-uniphier-usb3hs.o phy-uniphier-usb3ss.o
obj-$(CONFIG_PHY_UNIPHIER_PCIE) += phy-uniphier-pcie.o

View File

@ -0,0 +1,240 @@
// SPDX-License-Identifier: GPL-2.0
/*
* phy-uniphier-pcie.c - PHY driver for UniPhier PCIe controller
* Copyright 2018, Socionext Inc.
* Author: Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
*/
#include <linux/bitops.h>
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/iopoll.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/resource.h>
/* PHY */
#define PCL_PHY_TEST_I 0x2000
#define PCL_PHY_TEST_O 0x2004
#define TESTI_DAT_MASK GENMASK(13, 6)
#define TESTI_ADR_MASK GENMASK(5, 1)
#define TESTI_WR_EN BIT(0)
#define PCL_PHY_RESET 0x200c
#define PCL_PHY_RESET_N_MNMODE BIT(8) /* =1:manual */
#define PCL_PHY_RESET_N BIT(0) /* =1:deasssert */
/* SG */
#define SG_USBPCIESEL 0x590
#define SG_USBPCIESEL_PCIE BIT(0)
#define PCL_PHY_R00 0
#define RX_EQ_ADJ_EN BIT(3) /* enable for EQ adjustment */
#define PCL_PHY_R06 6
#define RX_EQ_ADJ GENMASK(5, 0) /* EQ adjustment value */
#define RX_EQ_ADJ_VAL 0
#define PCL_PHY_R26 26
#define VCO_CTRL GENMASK(7, 4) /* Tx VCO adjustment value */
#define VCO_CTRL_INIT_VAL 5
struct uniphier_pciephy_priv {
void __iomem *base;
struct device *dev;
struct clk *clk;
struct reset_control *rst;
const struct uniphier_pciephy_soc_data *data;
};
struct uniphier_pciephy_soc_data {
bool has_syscon;
};
static void uniphier_pciephy_testio_write(struct uniphier_pciephy_priv *priv,
u32 data)
{
/* need to read TESTO twice after accessing TESTI */
writel(data, priv->base + PCL_PHY_TEST_I);
readl(priv->base + PCL_PHY_TEST_O);
readl(priv->base + PCL_PHY_TEST_O);
}
static void uniphier_pciephy_set_param(struct uniphier_pciephy_priv *priv,
u32 reg, u32 mask, u32 param)
{
u32 val;
/* read previous data */
val = FIELD_PREP(TESTI_DAT_MASK, 1);
val |= FIELD_PREP(TESTI_ADR_MASK, reg);
uniphier_pciephy_testio_write(priv, val);
val = readl(priv->base + PCL_PHY_TEST_O);
/* update value */
val &= ~FIELD_PREP(TESTI_DAT_MASK, mask);
val = FIELD_PREP(TESTI_DAT_MASK, mask & param);
val |= FIELD_PREP(TESTI_ADR_MASK, reg);
uniphier_pciephy_testio_write(priv, val);
uniphier_pciephy_testio_write(priv, val | TESTI_WR_EN);
uniphier_pciephy_testio_write(priv, val);
/* read current data as dummy */
val = FIELD_PREP(TESTI_DAT_MASK, 1);
val |= FIELD_PREP(TESTI_ADR_MASK, reg);
uniphier_pciephy_testio_write(priv, val);
readl(priv->base + PCL_PHY_TEST_O);
}
static void uniphier_pciephy_assert(struct uniphier_pciephy_priv *priv)
{
u32 val;
val = readl(priv->base + PCL_PHY_RESET);
val &= ~PCL_PHY_RESET_N;
val |= PCL_PHY_RESET_N_MNMODE;
writel(val, priv->base + PCL_PHY_RESET);
}
static void uniphier_pciephy_deassert(struct uniphier_pciephy_priv *priv)
{
u32 val;
val = readl(priv->base + PCL_PHY_RESET);
val |= PCL_PHY_RESET_N_MNMODE | PCL_PHY_RESET_N;
writel(val, priv->base + PCL_PHY_RESET);
}
static int uniphier_pciephy_init(struct phy *phy)
{
struct uniphier_pciephy_priv *priv = phy_get_drvdata(phy);
int ret;
ret = clk_prepare_enable(priv->clk);
if (ret)
return ret;
ret = reset_control_deassert(priv->rst);
if (ret)
goto out_clk_disable;
uniphier_pciephy_set_param(priv, PCL_PHY_R00,
RX_EQ_ADJ_EN, RX_EQ_ADJ_EN);
uniphier_pciephy_set_param(priv, PCL_PHY_R06, RX_EQ_ADJ,
FIELD_PREP(RX_EQ_ADJ, RX_EQ_ADJ_VAL));
uniphier_pciephy_set_param(priv, PCL_PHY_R26, VCO_CTRL,
FIELD_PREP(VCO_CTRL, VCO_CTRL_INIT_VAL));
usleep_range(1, 10);
uniphier_pciephy_deassert(priv);
usleep_range(1, 10);
return 0;
out_clk_disable:
clk_disable_unprepare(priv->clk);
return ret;
}
static int uniphier_pciephy_exit(struct phy *phy)
{
struct uniphier_pciephy_priv *priv = phy_get_drvdata(phy);
uniphier_pciephy_assert(priv);
reset_control_assert(priv->rst);
clk_disable_unprepare(priv->clk);
return 0;
}
static const struct phy_ops uniphier_pciephy_ops = {
.init = uniphier_pciephy_init,
.exit = uniphier_pciephy_exit,
.owner = THIS_MODULE,
};
static int uniphier_pciephy_probe(struct platform_device *pdev)
{
struct uniphier_pciephy_priv *priv;
struct phy_provider *phy_provider;
struct device *dev = &pdev->dev;
struct regmap *regmap;
struct resource *res;
struct phy *phy;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->data = of_device_get_match_data(dev);
if (WARN_ON(!priv->data))
return -EINVAL;
priv->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->base = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
priv->rst = devm_reset_control_get_shared(dev, NULL);
if (IS_ERR(priv->rst))
return PTR_ERR(priv->rst);
phy = devm_phy_create(dev, dev->of_node, &uniphier_pciephy_ops);
if (IS_ERR(phy))
return PTR_ERR(phy);
regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
"socionext,syscon");
if (!IS_ERR(regmap) && priv->data->has_syscon)
regmap_update_bits(regmap, SG_USBPCIESEL,
SG_USBPCIESEL_PCIE, SG_USBPCIESEL_PCIE);
phy_set_drvdata(phy, priv);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct uniphier_pciephy_soc_data uniphier_ld20_data = {
.has_syscon = true,
};
static const struct uniphier_pciephy_soc_data uniphier_pxs3_data = {
.has_syscon = false,
};
static const struct of_device_id uniphier_pciephy_match[] = {
{
.compatible = "socionext,uniphier-ld20-pcie-phy",
.data = &uniphier_ld20_data,
},
{
.compatible = "socionext,uniphier-pxs3-pcie-phy",
.data = &uniphier_pxs3_data,
},
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, uniphier_pciephy_match);
static struct platform_driver uniphier_pciephy_driver = {
.probe = uniphier_pciephy_probe,
.driver = {
.name = "uniphier-pcie-phy",
.of_match_table = uniphier_pciephy_match,
},
};
module_platform_driver(uniphier_pciephy_driver);
MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");
MODULE_DESCRIPTION("UniPhier PHY driver for PCIe controller");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,244 @@
// SPDX-License-Identifier: GPL-2.0
/*
* phy-uniphier-usb2.c - PHY driver for UniPhier USB2 controller
* Copyright 2015-2018 Socionext Inc.
* Author:
* Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
*/
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#define SG_USBPHY1CTRL 0x500
#define SG_USBPHY1CTRL2 0x504
#define SG_USBPHY2CTRL 0x508
#define SG_USBPHY2CTRL2 0x50c /* LD11 */
#define SG_USBPHY12PLL 0x50c /* Pro4 */
#define SG_USBPHY3CTRL 0x510
#define SG_USBPHY3CTRL2 0x514
#define SG_USBPHY4CTRL 0x518 /* Pro4 */
#define SG_USBPHY4CTRL2 0x51c /* Pro4 */
#define SG_USBPHY34PLL 0x51c /* Pro4 */
struct uniphier_u2phy_param {
u32 offset;
u32 value;
};
struct uniphier_u2phy_soc_data {
struct uniphier_u2phy_param config0;
struct uniphier_u2phy_param config1;
};
struct uniphier_u2phy_priv {
struct regmap *regmap;
struct phy *phy;
struct regulator *vbus;
const struct uniphier_u2phy_soc_data *data;
struct uniphier_u2phy_priv *next;
};
static int uniphier_u2phy_power_on(struct phy *phy)
{
struct uniphier_u2phy_priv *priv = phy_get_drvdata(phy);
int ret = 0;
if (priv->vbus)
ret = regulator_enable(priv->vbus);
return ret;
}
static int uniphier_u2phy_power_off(struct phy *phy)
{
struct uniphier_u2phy_priv *priv = phy_get_drvdata(phy);
if (priv->vbus)
regulator_disable(priv->vbus);
return 0;
}
static int uniphier_u2phy_init(struct phy *phy)
{
struct uniphier_u2phy_priv *priv = phy_get_drvdata(phy);
if (!priv->data)
return 0;
regmap_write(priv->regmap, priv->data->config0.offset,
priv->data->config0.value);
regmap_write(priv->regmap, priv->data->config1.offset,
priv->data->config1.value);
return 0;
}
static struct phy *uniphier_u2phy_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct uniphier_u2phy_priv *priv = dev_get_drvdata(dev);
while (priv && args->np != priv->phy->dev.of_node)
priv = priv->next;
if (!priv) {
dev_err(dev, "Failed to find appropriate phy\n");
return ERR_PTR(-EINVAL);
}
return priv->phy;
}
static const struct phy_ops uniphier_u2phy_ops = {
.init = uniphier_u2phy_init,
.power_on = uniphier_u2phy_power_on,
.power_off = uniphier_u2phy_power_off,
.owner = THIS_MODULE,
};
static int uniphier_u2phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *parent, *child;
struct uniphier_u2phy_priv *priv = NULL, *next = NULL;
struct phy_provider *phy_provider;
struct regmap *regmap;
const struct uniphier_u2phy_soc_data *data;
int ret, data_idx, ndatas;
data = of_device_get_match_data(dev);
if (WARN_ON(!data))
return -EINVAL;
/* get number of data */
for (ndatas = 0; data[ndatas].config0.offset; ndatas++)
;
parent = of_get_parent(dev->of_node);
regmap = syscon_node_to_regmap(parent);
of_node_put(parent);
if (IS_ERR(regmap)) {
dev_err(dev, "Failed to get regmap\n");
return PTR_ERR(regmap);
}
for_each_child_of_node(dev->of_node, child) {
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
ret = -ENOMEM;
goto out_put_child;
}
priv->regmap = regmap;
priv->vbus = devm_regulator_get_optional(dev, "vbus");
if (IS_ERR(priv->vbus)) {
if (PTR_ERR(priv->vbus) == -EPROBE_DEFER) {
ret = PTR_ERR(priv->vbus);
goto out_put_child;
}
priv->vbus = NULL;
}
priv->phy = devm_phy_create(dev, child, &uniphier_u2phy_ops);
if (IS_ERR(priv->phy)) {
dev_err(dev, "Failed to create phy\n");
ret = PTR_ERR(priv->phy);
goto out_put_child;
}
ret = of_property_read_u32(child, "reg", &data_idx);
if (ret) {
dev_err(dev, "Failed to get reg property\n");
goto out_put_child;
}
if (data_idx < ndatas)
priv->data = &data[data_idx];
else
dev_warn(dev, "No phy configuration: %s\n",
child->full_name);
phy_set_drvdata(priv->phy, priv);
priv->next = next;
next = priv;
}
dev_set_drvdata(dev, priv);
phy_provider = devm_of_phy_provider_register(dev,
uniphier_u2phy_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
out_put_child:
of_node_put(child);
return ret;
}
static const struct uniphier_u2phy_soc_data uniphier_pro4_data[] = {
{
.config0 = { SG_USBPHY1CTRL, 0x05142400 },
.config1 = { SG_USBPHY12PLL, 0x00010010 },
},
{
.config0 = { SG_USBPHY2CTRL, 0x05142400 },
.config1 = { SG_USBPHY12PLL, 0x00010010 },
},
{
.config0 = { SG_USBPHY3CTRL, 0x05142400 },
.config1 = { SG_USBPHY34PLL, 0x00010010 },
},
{
.config0 = { SG_USBPHY4CTRL, 0x05142400 },
.config1 = { SG_USBPHY34PLL, 0x00010010 },
},
{ /* sentinel */ }
};
static const struct uniphier_u2phy_soc_data uniphier_ld11_data[] = {
{
.config0 = { SG_USBPHY1CTRL, 0x82280000 },
.config1 = { SG_USBPHY1CTRL2, 0x00000106 },
},
{
.config0 = { SG_USBPHY2CTRL, 0x82280000 },
.config1 = { SG_USBPHY2CTRL2, 0x00000106 },
},
{
.config0 = { SG_USBPHY3CTRL, 0x82280000 },
.config1 = { SG_USBPHY3CTRL2, 0x00000106 },
},
{ /* sentinel */ }
};
static const struct of_device_id uniphier_u2phy_match[] = {
{
.compatible = "socionext,uniphier-pro4-usb2-phy",
.data = &uniphier_pro4_data,
},
{
.compatible = "socionext,uniphier-ld11-usb2-phy",
.data = &uniphier_ld11_data,
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, uniphier_u2phy_match);
static struct platform_driver uniphier_u2phy_driver = {
.probe = uniphier_u2phy_probe,
.driver = {
.name = "uniphier-usb2-phy",
.of_match_table = uniphier_u2phy_match,
},
};
module_platform_driver(uniphier_u2phy_driver);
MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");
MODULE_DESCRIPTION("UniPhier PHY driver for USB2 controller");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,422 @@
// SPDX-License-Identifier: GPL-2.0
/*
* phy-uniphier-usb3hs.c - HS-PHY driver for Socionext UniPhier USB3 controller
* Copyright 2015-2018 Socionext Inc.
* Author:
* Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
* Contributors:
* Motoya Tanigawa <tanigawa.motoya@socionext.com>
* Masami Hiramatsu <masami.hiramatsu@linaro.org>
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/slab.h>
#define HSPHY_CFG0 0x0
#define HSPHY_CFG0_HS_I_MASK GENMASK(31, 28)
#define HSPHY_CFG0_HSDISC_MASK GENMASK(27, 26)
#define HSPHY_CFG0_SWING_MASK GENMASK(17, 16)
#define HSPHY_CFG0_SEL_T_MASK GENMASK(15, 12)
#define HSPHY_CFG0_RTERM_MASK GENMASK(7, 6)
#define HSPHY_CFG0_TRIMMASK (HSPHY_CFG0_HS_I_MASK \
| HSPHY_CFG0_SEL_T_MASK \
| HSPHY_CFG0_RTERM_MASK)
#define HSPHY_CFG1 0x4
#define HSPHY_CFG1_DAT_EN BIT(29)
#define HSPHY_CFG1_ADR_EN BIT(28)
#define HSPHY_CFG1_ADR_MASK GENMASK(27, 16)
#define HSPHY_CFG1_DAT_MASK GENMASK(23, 16)
#define PHY_F(regno, msb, lsb) { (regno), (msb), (lsb) }
#define LS_SLEW PHY_F(10, 6, 6) /* LS mode slew rate */
#define FS_LS_DRV PHY_F(10, 5, 5) /* FS/LS slew rate */
#define MAX_PHY_PARAMS 2
struct uniphier_u3hsphy_param {
struct {
int reg_no;
int msb;
int lsb;
} field;
u8 value;
};
struct uniphier_u3hsphy_trim_param {
unsigned int rterm;
unsigned int sel_t;
unsigned int hs_i;
};
#define trim_param_is_valid(p) ((p)->rterm || (p)->sel_t || (p)->hs_i)
struct uniphier_u3hsphy_priv {
struct device *dev;
void __iomem *base;
struct clk *clk, *clk_parent, *clk_ext;
struct reset_control *rst, *rst_parent;
struct regulator *vbus;
const struct uniphier_u3hsphy_soc_data *data;
};
struct uniphier_u3hsphy_soc_data {
int nparams;
const struct uniphier_u3hsphy_param param[MAX_PHY_PARAMS];
u32 config0;
u32 config1;
void (*trim_func)(struct uniphier_u3hsphy_priv *priv, u32 *pconfig,
struct uniphier_u3hsphy_trim_param *pt);
};
static void uniphier_u3hsphy_trim_ld20(struct uniphier_u3hsphy_priv *priv,
u32 *pconfig,
struct uniphier_u3hsphy_trim_param *pt)
{
*pconfig &= ~HSPHY_CFG0_RTERM_MASK;
*pconfig |= FIELD_PREP(HSPHY_CFG0_RTERM_MASK, pt->rterm);
*pconfig &= ~HSPHY_CFG0_SEL_T_MASK;
*pconfig |= FIELD_PREP(HSPHY_CFG0_SEL_T_MASK, pt->sel_t);
*pconfig &= ~HSPHY_CFG0_HS_I_MASK;
*pconfig |= FIELD_PREP(HSPHY_CFG0_HS_I_MASK, pt->hs_i);
}
static int uniphier_u3hsphy_get_nvparam(struct uniphier_u3hsphy_priv *priv,
const char *name, unsigned int *val)
{
struct nvmem_cell *cell;
u8 *buf;
cell = devm_nvmem_cell_get(priv->dev, name);
if (IS_ERR(cell))
return PTR_ERR(cell);
buf = nvmem_cell_read(cell, NULL);
if (IS_ERR(buf))
return PTR_ERR(buf);
*val = *buf;
kfree(buf);
return 0;
}
static int uniphier_u3hsphy_get_nvparams(struct uniphier_u3hsphy_priv *priv,
struct uniphier_u3hsphy_trim_param *pt)
{
int ret;
ret = uniphier_u3hsphy_get_nvparam(priv, "rterm", &pt->rterm);
if (ret)
return ret;
ret = uniphier_u3hsphy_get_nvparam(priv, "sel_t", &pt->sel_t);
if (ret)
return ret;
ret = uniphier_u3hsphy_get_nvparam(priv, "hs_i", &pt->hs_i);
if (ret)
return ret;
return 0;
}
static int uniphier_u3hsphy_update_config(struct uniphier_u3hsphy_priv *priv,
u32 *pconfig)
{
struct uniphier_u3hsphy_trim_param trim;
int ret, trimmed = 0;
if (priv->data->trim_func) {
ret = uniphier_u3hsphy_get_nvparams(priv, &trim);
if (ret == -EPROBE_DEFER)
return ret;
/*
* call trim_func only when trimming parameters that aren't
* all-zero can be acquired. All-zero parameters mean nothing
* has been written to nvmem.
*/
if (!ret && trim_param_is_valid(&trim)) {
priv->data->trim_func(priv, pconfig, &trim);
trimmed = 1;
} else {
dev_dbg(priv->dev, "can't get parameter from nvmem\n");
}
}
/* use default parameters without trimming values */
if (!trimmed) {
*pconfig &= ~HSPHY_CFG0_HSDISC_MASK;
*pconfig |= FIELD_PREP(HSPHY_CFG0_HSDISC_MASK, 3);
}
return 0;
}
static void uniphier_u3hsphy_set_param(struct uniphier_u3hsphy_priv *priv,
const struct uniphier_u3hsphy_param *p)
{
u32 val;
u32 field_mask = GENMASK(p->field.msb, p->field.lsb);
u8 data;
val = readl(priv->base + HSPHY_CFG1);
val &= ~HSPHY_CFG1_ADR_MASK;
val |= FIELD_PREP(HSPHY_CFG1_ADR_MASK, p->field.reg_no)
| HSPHY_CFG1_ADR_EN;
writel(val, priv->base + HSPHY_CFG1);
val = readl(priv->base + HSPHY_CFG1);
val &= ~HSPHY_CFG1_ADR_EN;
writel(val, priv->base + HSPHY_CFG1);
val = readl(priv->base + HSPHY_CFG1);
val &= ~FIELD_PREP(HSPHY_CFG1_DAT_MASK, field_mask);
data = field_mask & (p->value << p->field.lsb);
val |= FIELD_PREP(HSPHY_CFG1_DAT_MASK, data) | HSPHY_CFG1_DAT_EN;
writel(val, priv->base + HSPHY_CFG1);
val = readl(priv->base + HSPHY_CFG1);
val &= ~HSPHY_CFG1_DAT_EN;
writel(val, priv->base + HSPHY_CFG1);
}
static int uniphier_u3hsphy_power_on(struct phy *phy)
{
struct uniphier_u3hsphy_priv *priv = phy_get_drvdata(phy);
int ret;
ret = clk_prepare_enable(priv->clk_ext);
if (ret)
return ret;
ret = clk_prepare_enable(priv->clk);
if (ret)
goto out_clk_ext_disable;
ret = reset_control_deassert(priv->rst);
if (ret)
goto out_clk_disable;
if (priv->vbus) {
ret = regulator_enable(priv->vbus);
if (ret)
goto out_rst_assert;
}
return 0;
out_rst_assert:
reset_control_assert(priv->rst);
out_clk_disable:
clk_disable_unprepare(priv->clk);
out_clk_ext_disable:
clk_disable_unprepare(priv->clk_ext);
return ret;
}
static int uniphier_u3hsphy_power_off(struct phy *phy)
{
struct uniphier_u3hsphy_priv *priv = phy_get_drvdata(phy);
if (priv->vbus)
regulator_disable(priv->vbus);
reset_control_assert(priv->rst);
clk_disable_unprepare(priv->clk);
clk_disable_unprepare(priv->clk_ext);
return 0;
}
static int uniphier_u3hsphy_init(struct phy *phy)
{
struct uniphier_u3hsphy_priv *priv = phy_get_drvdata(phy);
u32 config0, config1;
int i, ret;
ret = clk_prepare_enable(priv->clk_parent);
if (ret)
return ret;
ret = reset_control_deassert(priv->rst_parent);
if (ret)
goto out_clk_disable;
if (!priv->data->config0 && !priv->data->config1)
return 0;
config0 = priv->data->config0;
config1 = priv->data->config1;
ret = uniphier_u3hsphy_update_config(priv, &config0);
if (ret)
goto out_rst_assert;
writel(config0, priv->base + HSPHY_CFG0);
writel(config1, priv->base + HSPHY_CFG1);
for (i = 0; i < priv->data->nparams; i++)
uniphier_u3hsphy_set_param(priv, &priv->data->param[i]);
return 0;
out_rst_assert:
reset_control_assert(priv->rst_parent);
out_clk_disable:
clk_disable_unprepare(priv->clk_parent);
return ret;
}
static int uniphier_u3hsphy_exit(struct phy *phy)
{
struct uniphier_u3hsphy_priv *priv = phy_get_drvdata(phy);
reset_control_assert(priv->rst_parent);
clk_disable_unprepare(priv->clk_parent);
return 0;
}
static const struct phy_ops uniphier_u3hsphy_ops = {
.init = uniphier_u3hsphy_init,
.exit = uniphier_u3hsphy_exit,
.power_on = uniphier_u3hsphy_power_on,
.power_off = uniphier_u3hsphy_power_off,
.owner = THIS_MODULE,
};
static int uniphier_u3hsphy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct uniphier_u3hsphy_priv *priv;
struct phy_provider *phy_provider;
struct resource *res;
struct phy *phy;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = dev;
priv->data = of_device_get_match_data(dev);
if (WARN_ON(!priv->data ||
priv->data->nparams > MAX_PHY_PARAMS))
return -EINVAL;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->base = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
priv->clk = devm_clk_get(dev, "phy");
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
priv->clk_parent = devm_clk_get(dev, "link");
if (IS_ERR(priv->clk_parent))
return PTR_ERR(priv->clk_parent);
priv->clk_ext = devm_clk_get(dev, "phy-ext");
if (IS_ERR(priv->clk_ext)) {
if (PTR_ERR(priv->clk_ext) == -ENOENT)
priv->clk_ext = NULL;
else
return PTR_ERR(priv->clk_ext);
}
priv->rst = devm_reset_control_get_shared(dev, "phy");
if (IS_ERR(priv->rst))
return PTR_ERR(priv->rst);
priv->rst_parent = devm_reset_control_get_shared(dev, "link");
if (IS_ERR(priv->rst_parent))
return PTR_ERR(priv->rst_parent);
priv->vbus = devm_regulator_get_optional(dev, "vbus");
if (IS_ERR(priv->vbus)) {
if (PTR_ERR(priv->vbus) == -EPROBE_DEFER)
return PTR_ERR(priv->vbus);
priv->vbus = NULL;
}
phy = devm_phy_create(dev, dev->of_node, &uniphier_u3hsphy_ops);
if (IS_ERR(phy))
return PTR_ERR(phy);
phy_set_drvdata(phy, priv);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct uniphier_u3hsphy_soc_data uniphier_pxs2_data = {
.nparams = 0,
};
static const struct uniphier_u3hsphy_soc_data uniphier_ld20_data = {
.nparams = 2,
.param = {
{ LS_SLEW, 1 },
{ FS_LS_DRV, 1 },
},
.trim_func = uniphier_u3hsphy_trim_ld20,
.config0 = 0x92316680,
.config1 = 0x00000106,
};
static const struct uniphier_u3hsphy_soc_data uniphier_pxs3_data = {
.nparams = 0,
.trim_func = uniphier_u3hsphy_trim_ld20,
.config0 = 0x92316680,
.config1 = 0x00000106,
};
static const struct of_device_id uniphier_u3hsphy_match[] = {
{
.compatible = "socionext,uniphier-pxs2-usb3-hsphy",
.data = &uniphier_pxs2_data,
},
{
.compatible = "socionext,uniphier-ld20-usb3-hsphy",
.data = &uniphier_ld20_data,
},
{
.compatible = "socionext,uniphier-pxs3-usb3-hsphy",
.data = &uniphier_pxs3_data,
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, uniphier_u3hsphy_match);
static struct platform_driver uniphier_u3hsphy_driver = {
.probe = uniphier_u3hsphy_probe,
.driver = {
.name = "uniphier-usb3-hsphy",
.of_match_table = uniphier_u3hsphy_match,
},
};
module_platform_driver(uniphier_u3hsphy_driver);
MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");
MODULE_DESCRIPTION("UniPhier HS-PHY driver for USB3 controller");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,349 @@
// SPDX-License-Identifier: GPL-2.0
/*
* phy-uniphier-usb3ss.c - SS-PHY driver for Socionext UniPhier USB3 controller
* Copyright 2015-2018 Socionext Inc.
* Author:
* Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
* Contributors:
* Motoya Tanigawa <tanigawa.motoya@socionext.com>
* Masami Hiramatsu <masami.hiramatsu@linaro.org>
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#define SSPHY_TESTI 0x0
#define SSPHY_TESTO 0x4
#define TESTI_DAT_MASK GENMASK(13, 6)
#define TESTI_ADR_MASK GENMASK(5, 1)
#define TESTI_WR_EN BIT(0)
#define PHY_F(regno, msb, lsb) { (regno), (msb), (lsb) }
#define CDR_CPD_TRIM PHY_F(7, 3, 0) /* RxPLL charge pump current */
#define CDR_CPF_TRIM PHY_F(8, 3, 0) /* RxPLL charge pump current 2 */
#define TX_PLL_TRIM PHY_F(9, 3, 0) /* TxPLL charge pump current */
#define BGAP_TRIM PHY_F(11, 3, 0) /* Bandgap voltage */
#define CDR_TRIM PHY_F(13, 6, 5) /* Clock Data Recovery setting */
#define VCO_CTRL PHY_F(26, 7, 4) /* VCO control */
#define VCOPLL_CTRL PHY_F(27, 2, 0) /* TxPLL VCO tuning */
#define VCOPLL_CM PHY_F(28, 1, 0) /* TxPLL voltage */
#define MAX_PHY_PARAMS 7
struct uniphier_u3ssphy_param {
struct {
int reg_no;
int msb;
int lsb;
} field;
u8 value;
};
struct uniphier_u3ssphy_priv {
struct device *dev;
void __iomem *base;
struct clk *clk, *clk_ext, *clk_parent, *clk_parent_gio;
struct reset_control *rst, *rst_parent, *rst_parent_gio;
struct regulator *vbus;
const struct uniphier_u3ssphy_soc_data *data;
};
struct uniphier_u3ssphy_soc_data {
bool is_legacy;
int nparams;
const struct uniphier_u3ssphy_param param[MAX_PHY_PARAMS];
};
static void uniphier_u3ssphy_testio_write(struct uniphier_u3ssphy_priv *priv,
u32 data)
{
/* need to read TESTO twice after accessing TESTI */
writel(data, priv->base + SSPHY_TESTI);
readl(priv->base + SSPHY_TESTO);
readl(priv->base + SSPHY_TESTO);
}
static void uniphier_u3ssphy_set_param(struct uniphier_u3ssphy_priv *priv,
const struct uniphier_u3ssphy_param *p)
{
u32 val;
u8 field_mask = GENMASK(p->field.msb, p->field.lsb);
u8 data;
/* read previous data */
val = FIELD_PREP(TESTI_DAT_MASK, 1);
val |= FIELD_PREP(TESTI_ADR_MASK, p->field.reg_no);
uniphier_u3ssphy_testio_write(priv, val);
val = readl(priv->base + SSPHY_TESTO);
/* update value */
val &= ~FIELD_PREP(TESTI_DAT_MASK, field_mask);
data = field_mask & (p->value << p->field.lsb);
val = FIELD_PREP(TESTI_DAT_MASK, data);
val |= FIELD_PREP(TESTI_ADR_MASK, p->field.reg_no);
uniphier_u3ssphy_testio_write(priv, val);
uniphier_u3ssphy_testio_write(priv, val | TESTI_WR_EN);
uniphier_u3ssphy_testio_write(priv, val);
/* read current data as dummy */
val = FIELD_PREP(TESTI_DAT_MASK, 1);
val |= FIELD_PREP(TESTI_ADR_MASK, p->field.reg_no);
uniphier_u3ssphy_testio_write(priv, val);
readl(priv->base + SSPHY_TESTO);
}
static int uniphier_u3ssphy_power_on(struct phy *phy)
{
struct uniphier_u3ssphy_priv *priv = phy_get_drvdata(phy);
int ret;
ret = clk_prepare_enable(priv->clk_ext);
if (ret)
return ret;
ret = clk_prepare_enable(priv->clk);
if (ret)
goto out_clk_ext_disable;
ret = reset_control_deassert(priv->rst);
if (ret)
goto out_clk_disable;
if (priv->vbus) {
ret = regulator_enable(priv->vbus);
if (ret)
goto out_rst_assert;
}
return 0;
out_rst_assert:
reset_control_assert(priv->rst);
out_clk_disable:
clk_disable_unprepare(priv->clk);
out_clk_ext_disable:
clk_disable_unprepare(priv->clk_ext);
return ret;
}
static int uniphier_u3ssphy_power_off(struct phy *phy)
{
struct uniphier_u3ssphy_priv *priv = phy_get_drvdata(phy);
if (priv->vbus)
regulator_disable(priv->vbus);
reset_control_assert(priv->rst);
clk_disable_unprepare(priv->clk);
clk_disable_unprepare(priv->clk_ext);
return 0;
}
static int uniphier_u3ssphy_init(struct phy *phy)
{
struct uniphier_u3ssphy_priv *priv = phy_get_drvdata(phy);
int i, ret;
ret = clk_prepare_enable(priv->clk_parent);
if (ret)
return ret;
ret = clk_prepare_enable(priv->clk_parent_gio);
if (ret)
goto out_clk_disable;
ret = reset_control_deassert(priv->rst_parent);
if (ret)
goto out_clk_gio_disable;
ret = reset_control_deassert(priv->rst_parent_gio);
if (ret)
goto out_rst_assert;
if (priv->data->is_legacy)
return 0;
for (i = 0; i < priv->data->nparams; i++)
uniphier_u3ssphy_set_param(priv, &priv->data->param[i]);
return 0;
out_rst_assert:
reset_control_assert(priv->rst_parent);
out_clk_gio_disable:
clk_disable_unprepare(priv->clk_parent_gio);
out_clk_disable:
clk_disable_unprepare(priv->clk_parent);
return ret;
}
static int uniphier_u3ssphy_exit(struct phy *phy)
{
struct uniphier_u3ssphy_priv *priv = phy_get_drvdata(phy);
reset_control_assert(priv->rst_parent_gio);
reset_control_assert(priv->rst_parent);
clk_disable_unprepare(priv->clk_parent_gio);
clk_disable_unprepare(priv->clk_parent);
return 0;
}
static const struct phy_ops uniphier_u3ssphy_ops = {
.init = uniphier_u3ssphy_init,
.exit = uniphier_u3ssphy_exit,
.power_on = uniphier_u3ssphy_power_on,
.power_off = uniphier_u3ssphy_power_off,
.owner = THIS_MODULE,
};
static int uniphier_u3ssphy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct uniphier_u3ssphy_priv *priv;
struct phy_provider *phy_provider;
struct resource *res;
struct phy *phy;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = dev;
priv->data = of_device_get_match_data(dev);
if (WARN_ON(!priv->data ||
priv->data->nparams > MAX_PHY_PARAMS))
return -EINVAL;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->base = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
if (!priv->data->is_legacy) {
priv->clk = devm_clk_get(dev, "phy");
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
priv->clk_ext = devm_clk_get(dev, "phy-ext");
if (IS_ERR(priv->clk_ext)) {
if (PTR_ERR(priv->clk_ext) == -ENOENT)
priv->clk_ext = NULL;
else
return PTR_ERR(priv->clk_ext);
}
priv->rst = devm_reset_control_get_shared(dev, "phy");
if (IS_ERR(priv->rst))
return PTR_ERR(priv->rst);
} else {
priv->clk_parent_gio = devm_clk_get(dev, "gio");
if (IS_ERR(priv->clk_parent_gio))
return PTR_ERR(priv->clk_parent_gio);
priv->rst_parent_gio =
devm_reset_control_get_shared(dev, "gio");
if (IS_ERR(priv->rst_parent_gio))
return PTR_ERR(priv->rst_parent_gio);
}
priv->clk_parent = devm_clk_get(dev, "link");
if (IS_ERR(priv->clk_parent))
return PTR_ERR(priv->clk_parent);
priv->rst_parent = devm_reset_control_get_shared(dev, "link");
if (IS_ERR(priv->rst_parent))
return PTR_ERR(priv->rst_parent);
priv->vbus = devm_regulator_get_optional(dev, "vbus");
if (IS_ERR(priv->vbus)) {
if (PTR_ERR(priv->vbus) == -EPROBE_DEFER)
return PTR_ERR(priv->vbus);
priv->vbus = NULL;
}
phy = devm_phy_create(dev, dev->of_node, &uniphier_u3ssphy_ops);
if (IS_ERR(phy))
return PTR_ERR(phy);
phy_set_drvdata(phy, priv);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct uniphier_u3ssphy_soc_data uniphier_pro4_data = {
.is_legacy = true,
};
static const struct uniphier_u3ssphy_soc_data uniphier_pxs2_data = {
.is_legacy = false,
.nparams = 7,
.param = {
{ CDR_CPD_TRIM, 10 },
{ CDR_CPF_TRIM, 3 },
{ TX_PLL_TRIM, 5 },
{ BGAP_TRIM, 9 },
{ CDR_TRIM, 2 },
{ VCOPLL_CTRL, 7 },
{ VCOPLL_CM, 1 },
},
};
static const struct uniphier_u3ssphy_soc_data uniphier_ld20_data = {
.is_legacy = false,
.nparams = 3,
.param = {
{ CDR_CPD_TRIM, 6 },
{ CDR_TRIM, 2 },
{ VCO_CTRL, 5 },
},
};
static const struct of_device_id uniphier_u3ssphy_match[] = {
{
.compatible = "socionext,uniphier-pro4-usb3-ssphy",
.data = &uniphier_pro4_data,
},
{
.compatible = "socionext,uniphier-pxs2-usb3-ssphy",
.data = &uniphier_pxs2_data,
},
{
.compatible = "socionext,uniphier-ld20-usb3-ssphy",
.data = &uniphier_ld20_data,
},
{
.compatible = "socionext,uniphier-pxs3-usb3-ssphy",
.data = &uniphier_ld20_data,
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, uniphier_u3ssphy_match);
static struct platform_driver uniphier_u3ssphy_driver = {
.probe = uniphier_u3ssphy_probe,
.driver = {
.name = "uniphier-usb3-ssphy",
.of_match_table = uniphier_u3ssphy_match,
},
};
module_platform_driver(uniphier_u3ssphy_driver);
MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");
MODULE_DESCRIPTION("UniPhier SS-PHY driver for USB3 controller");
MODULE_LICENSE("GPL v2");

View File

@ -115,8 +115,8 @@ int tegra_xusb_lane_parse_dt(struct tegra_xusb_lane *lane,
err = match_string(lane->soc->funcs, lane->soc->num_funcs, function);
if (err < 0) {
dev_err(dev, "invalid function \"%s\" for lane \"%s\"\n",
function, np->name);
dev_err(dev, "invalid function \"%s\" for lane \"%pOFn\"\n",
function, np);
return err;
}

View File

@ -144,6 +144,7 @@
#define PMBR1 0x0D
#define GPIO_USB_4PIN_ULPI_2430C (3 << 0)
static irqreturn_t twl4030_usb_irq(int irq, void *_twl);
/*
* If VBUS is valid or ID is ground, then we know a
* cable is present and we need to be runtime-enabled
@ -395,6 +396,33 @@ static void __twl4030_phy_power(struct twl4030_usb *twl, int on)
WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0);
}
static int __maybe_unused twl4030_usb_suspend(struct device *dev)
{
struct twl4030_usb *twl = dev_get_drvdata(dev);
/*
* we need enabled runtime on resume,
* so turn irq off here, so we do not get it early
* note: wakeup on usb plug works independently of this
*/
dev_dbg(twl->dev, "%s\n", __func__);
disable_irq(twl->irq);
return 0;
}
static int __maybe_unused twl4030_usb_resume(struct device *dev)
{
struct twl4030_usb *twl = dev_get_drvdata(dev);
dev_dbg(twl->dev, "%s\n", __func__);
enable_irq(twl->irq);
/* check whether cable status changed */
twl4030_usb_irq(0, twl);
return 0;
}
static int __maybe_unused twl4030_usb_runtime_suspend(struct device *dev)
{
struct twl4030_usb *twl = dev_get_drvdata(dev);
@ -655,6 +683,7 @@ static const struct phy_ops ops = {
static const struct dev_pm_ops twl4030_usb_pm_ops = {
SET_RUNTIME_PM_OPS(twl4030_usb_runtime_suspend,
twl4030_usb_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(twl4030_usb_suspend, twl4030_usb_resume)
};
static int twl4030_usb_probe(struct platform_device *pdev)

View File

@ -867,6 +867,8 @@ config INTEL_CHT_INT33FE
tristate "Intel Cherry Trail ACPI INT33FE Driver"
depends on X86 && ACPI && I2C && REGULATOR
depends on CHARGER_BQ24190=y || (CHARGER_BQ24190=m && m)
depends on USB_ROLES_INTEL_XHCI=y || (USB_ROLES_INTEL_XHCI=m && m)
depends on TYPEC_MUX_PI3USB30532=y || (TYPEC_MUX_PI3USB30532=m && m)
---help---
This driver add support for the INT33FE ACPI device found on
some Intel Cherry Trail devices.

View File

@ -35,7 +35,7 @@ struct cht_int33fe_data {
struct i2c_client *fusb302;
struct i2c_client *pi3usb30532;
/* Contain a list-head must be per device */
struct device_connection connections[3];
struct device_connection connections[5];
};
/*
@ -175,19 +175,20 @@ static int cht_int33fe_probe(struct platform_device *pdev)
return -EPROBE_DEFER; /* Wait for i2c-adapter to load */
}
data->connections[0].endpoint[0] = "i2c-fusb302";
data->connections[0].endpoint[0] = "port0";
data->connections[0].endpoint[1] = "i2c-pi3usb30532";
data->connections[0].id = "typec-switch";
data->connections[1].endpoint[0] = "i2c-fusb302";
data->connections[1].endpoint[0] = "port0";
data->connections[1].endpoint[1] = "i2c-pi3usb30532";
data->connections[1].id = "typec-mux";
data->connections[2].endpoint[0] = "i2c-fusb302";
data->connections[2].endpoint[1] = "intel_xhci_usb_sw-role-switch";
data->connections[2].id = "usb-role-switch";
data->connections[2].endpoint[0] = "port0";
data->connections[2].endpoint[1] = "i2c-pi3usb30532";
data->connections[2].id = "idff01m01";
data->connections[3].endpoint[0] = "i2c-fusb302";
data->connections[3].endpoint[1] = "intel_xhci_usb_sw-role-switch";
data->connections[3].id = "usb-role-switch";
device_connection_add(&data->connections[0]);
device_connection_add(&data->connections[1]);
device_connection_add(&data->connections[2]);
device_connections_add(data->connections);
memset(&board_info, 0, sizeof(board_info));
strlcpy(board_info.type, "typec_fusb302", I2C_NAME_SIZE);
@ -218,9 +219,7 @@ out_unregister_max17047:
if (data->max17047)
i2c_unregister_device(data->max17047);
device_connection_remove(&data->connections[2]);
device_connection_remove(&data->connections[1]);
device_connection_remove(&data->connections[0]);
device_connections_remove(data->connections);
return -EPROBE_DEFER; /* Wait for the i2c-adapter to load */
}
@ -234,9 +233,7 @@ static int cht_int33fe_remove(struct platform_device *pdev)
if (data->max17047)
i2c_unregister_device(data->max17047);
device_connection_remove(&data->connections[2]);
device_connection_remove(&data->connections[1]);
device_connection_remove(&data->connections[0]);
device_connections_remove(data->connections);
return 0;
}

View File

@ -16,7 +16,6 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/phy/phy.h>
#include <linux/phy/phy-qcom-ufs.h>
#include "ufshcd.h"
#include "ufshcd-pltfrm.h"
@ -191,22 +190,9 @@ out:
static int ufs_qcom_link_startup_post_change(struct ufs_hba *hba)
{
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
struct phy *phy = host->generic_phy;
u32 tx_lanes;
int err = 0;
err = ufs_qcom_get_connected_tx_lanes(hba, &tx_lanes);
if (err)
goto out;
err = ufs_qcom_phy_set_tx_lane_enable(phy, tx_lanes);
if (err)
dev_err(hba->dev, "%s: ufs_qcom_phy_set_tx_lane_enable failed\n",
__func__);
out:
return err;
return ufs_qcom_get_connected_tx_lanes(hba, &tx_lanes);
}
static int ufs_qcom_check_hibern8(struct ufs_hba *hba)
@ -934,10 +920,8 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
{
u32 val;
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
struct phy *phy = host->generic_phy;
struct ufs_qcom_dev_params ufs_qcom_cap;
int ret = 0;
int res = 0;
if (!dev_req_params) {
pr_err("%s: incoming dev_req_params is NULL\n", __func__);
@ -1004,12 +988,6 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
}
val = ~(MAX_U32 << dev_req_params->lane_tx);
res = ufs_qcom_phy_set_tx_lane_enable(phy, val);
if (res) {
dev_err(hba->dev, "%s: ufs_qcom_phy_set_tx_lane_enable() failed res = %d\n",
__func__, res);
ret = res;
}
/* cache the power mode parameters to use internally */
memcpy(&host->dev_req_params,
@ -1266,10 +1244,6 @@ static int ufs_qcom_init(struct ufs_hba *hba)
}
}
/* update phy revision information before calling phy_init() */
ufs_qcom_phy_save_controller_version(host->generic_phy,
host->hw_ver.major, host->hw_ver.minor, host->hw_ver.step);
err = ufs_qcom_init_lane_clks(host);
if (err)
goto out_variant_clear;

View File

@ -129,11 +129,6 @@ enum {
MASK_CLK_NS_REG = 0xFFFC00,
};
enum ufs_qcom_phy_init_type {
UFS_PHY_INIT_FULL,
UFS_PHY_INIT_CFG_RESTORE,
};
/* QCOM UFS debug print bit mask */
#define UFS_QCOM_DBG_PRINT_REGS_EN BIT(0)
#define UFS_QCOM_DBG_PRINT_ICE_REGS_EN BIT(1)

View File

@ -364,8 +364,7 @@ static void ci_hdrc_imx_shutdown(struct platform_device *pdev)
ci_hdrc_imx_remove(pdev);
}
#ifdef CONFIG_PM
static int imx_controller_suspend(struct device *dev)
static int __maybe_unused imx_controller_suspend(struct device *dev)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
@ -377,7 +376,7 @@ static int imx_controller_suspend(struct device *dev)
return 0;
}
static int imx_controller_resume(struct device *dev)
static int __maybe_unused imx_controller_resume(struct device *dev)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret = 0;
@ -408,8 +407,7 @@ clk_disable:
return ret;
}
#ifdef CONFIG_PM_SLEEP
static int ci_hdrc_imx_suspend(struct device *dev)
static int __maybe_unused ci_hdrc_imx_suspend(struct device *dev)
{
int ret;
@ -431,7 +429,7 @@ static int ci_hdrc_imx_suspend(struct device *dev)
return imx_controller_suspend(dev);
}
static int ci_hdrc_imx_resume(struct device *dev)
static int __maybe_unused ci_hdrc_imx_resume(struct device *dev)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret;
@ -445,9 +443,8 @@ static int ci_hdrc_imx_resume(struct device *dev)
return ret;
}
#endif /* CONFIG_PM_SLEEP */
static int ci_hdrc_imx_runtime_suspend(struct device *dev)
static int __maybe_unused ci_hdrc_imx_runtime_suspend(struct device *dev)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret;
@ -466,13 +463,11 @@ static int ci_hdrc_imx_runtime_suspend(struct device *dev)
return imx_controller_suspend(dev);
}
static int ci_hdrc_imx_runtime_resume(struct device *dev)
static int __maybe_unused ci_hdrc_imx_runtime_resume(struct device *dev)
{
return imx_controller_resume(dev);
}
#endif /* CONFIG_PM */
static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume)
SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend,
@ -492,7 +487,7 @@ static struct platform_driver ci_hdrc_imx_driver = {
module_platform_driver(ci_hdrc_imx_driver);
MODULE_ALIAS("platform:imx-usb");
MODULE_LICENSE("GPL v2");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("CI HDRC i.MX USB binding");
MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");

View File

@ -53,6 +53,7 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <linux/pinctrl/consumer.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
@ -723,6 +724,24 @@ static int ci_get_platdata(struct device *dev,
else
cable->connected = false;
}
platdata->pctl = devm_pinctrl_get(dev);
if (!IS_ERR(platdata->pctl)) {
struct pinctrl_state *p;
p = pinctrl_lookup_state(platdata->pctl, "default");
if (!IS_ERR(p))
platdata->pins_default = p;
p = pinctrl_lookup_state(platdata->pctl, "host");
if (!IS_ERR(p))
platdata->pins_host = p;
p = pinctrl_lookup_state(platdata->pctl, "device");
if (!IS_ERR(p))
platdata->pins_device = p;
}
return 0;
}

View File

@ -13,6 +13,7 @@
#include <linux/usb/hcd.h>
#include <linux/usb/chipidea.h>
#include <linux/regulator/consumer.h>
#include <linux/pinctrl/consumer.h>
#include "../host/ehci.h"
@ -153,6 +154,10 @@ static int host_start(struct ci_hdrc *ci)
}
}
if (ci->platdata->pins_host)
pinctrl_select_state(ci->platdata->pctl,
ci->platdata->pins_host);
ret = usb_add_hcd(hcd, 0, 0);
if (ret) {
goto disable_reg;
@ -197,6 +202,10 @@ static void host_stop(struct ci_hdrc *ci)
}
ci->hcd = NULL;
ci->otg.host = NULL;
if (ci->platdata->pins_host && ci->platdata->pins_default)
pinctrl_select_state(ci->platdata->pctl,
ci->platdata->pins_default);
}

View File

@ -203,14 +203,17 @@ static void ci_otg_work(struct work_struct *work)
}
pm_runtime_get_sync(ci->dev);
if (ci->id_event) {
ci->id_event = false;
ci_handle_id_switch(ci);
} else if (ci->b_sess_valid_event) {
}
if (ci->b_sess_valid_event) {
ci->b_sess_valid_event = false;
ci_handle_vbus_change(ci);
} else
dev_err(ci->dev, "unexpected event occurs at %s\n", __func__);
}
pm_runtime_put_sync(ci->dev);
enable_irq(ci->irq);

View File

@ -17,7 +17,8 @@ void ci_handle_vbus_change(struct ci_hdrc *ci);
static inline void ci_otg_queue_work(struct ci_hdrc *ci)
{
disable_irq_nosync(ci->irq);
queue_work(ci->wq, &ci->work);
if (queue_work(ci->wq, &ci->work) == false)
enable_irq(ci->irq);
}
#endif /* __DRIVERS_USB_CHIPIDEA_OTG_H */

View File

@ -15,6 +15,7 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <linux/pinctrl/consumer.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg-fsm.h>
@ -1965,6 +1966,10 @@ void ci_hdrc_gadget_destroy(struct ci_hdrc *ci)
static int udc_id_switch_for_device(struct ci_hdrc *ci)
{
if (ci->platdata->pins_device)
pinctrl_select_state(ci->platdata->pctl,
ci->platdata->pins_device);
if (ci->is_otg)
/* Clear and enable BSV irq */
hw_write_otgsc(ci, OTGSC_BSVIS | OTGSC_BSVIE,
@ -1983,6 +1988,10 @@ static void udc_id_switch_for_host(struct ci_hdrc *ci)
hw_write_otgsc(ci, OTGSC_BSVIE | OTGSC_BSVIS, OTGSC_BSVIS);
ci->vbus_active = 0;
if (ci->platdata->pins_device && ci->platdata->pins_default)
pinctrl_select_state(ci->platdata->pctl,
ci->platdata->pins_default);
}
/**

View File

@ -343,6 +343,8 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
} else if (data->oc_polarity == 1) {
/* High active */
reg &= ~(MX6_BM_OVER_CUR_DIS | MX6_BM_OVER_CUR_POLARITY);
} else {
reg &= ~(MX6_BM_OVER_CUR_DIS);
}
writel(reg, usbmisc->base + data->index * 4);
@ -633,6 +635,6 @@ static struct platform_driver usbmisc_imx_driver = {
module_platform_driver(usbmisc_imx_driver);
MODULE_ALIAS("platform:usbmisc-imx");
MODULE_LICENSE("GPL v2");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("driver for imx usb non-core registers");
MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");

File diff suppressed because it is too large Load Diff

View File

@ -101,12 +101,8 @@ void hcd_buffer_destroy(struct usb_hcd *hcd)
return;
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
struct dma_pool *pool = hcd->pool[i];
if (pool) {
dma_pool_destroy(pool);
hcd->pool[i] = NULL;
}
dma_pool_destroy(hcd->pool[i]);
hcd->pool[i] = NULL;
}
}

View File

@ -510,7 +510,6 @@ int usb_driver_claim_interface(struct usb_driver *driver,
struct usb_interface *iface, void *priv)
{
struct device *dev;
struct usb_device *udev;
int retval = 0;
if (!iface)
@ -524,8 +523,6 @@ int usb_driver_claim_interface(struct usb_driver *driver,
if (!iface->authorized)
return -ENODEV;
udev = interface_to_usbdev(iface);
dev->driver = &driver->drvwrap.driver;
usb_set_intfdata(iface, priv);
iface->needs_binding = 0;

View File

@ -21,6 +21,7 @@
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include <uapi/linux/usb/audio.h>
#include "usb.h"
static inline const char *plural(int n)
@ -42,6 +43,16 @@ static int is_activesync(struct usb_interface_descriptor *desc)
&& desc->bInterfaceProtocol == 1;
}
static bool is_audio(struct usb_interface_descriptor *desc)
{
return desc->bInterfaceClass == USB_CLASS_AUDIO;
}
static bool is_uac3_config(struct usb_interface_descriptor *desc)
{
return desc->bInterfaceProtocol == UAC_VERSION_3;
}
int usb_choose_configuration(struct usb_device *udev)
{
int i;
@ -121,6 +132,22 @@ int usb_choose_configuration(struct usb_device *udev)
#endif
}
/*
* Select first configuration as default for audio so that
* devices that don't comply with UAC3 protocol are supported.
* But, still iterate through other configurations and
* select UAC3 compliant config if present.
*/
if (i == 0 && num_configs > 1 && desc && is_audio(desc)) {
best = c;
continue;
}
if (i > 0 && desc && is_audio(desc) && is_uac3_config(desc)) {
best = c;
break;
}
/* From the remaining configs, choose the first one whose
* first interface is for a non-vendor-specific class.
* Reason: Linux is more likely to have a class driver

View File

@ -1738,7 +1738,6 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
struct usb_anchor *anchor = urb->anchor;
int status = urb->unlinked;
unsigned long flags;
urb->hcpriv = NULL;
if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) &&
@ -1755,20 +1754,7 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
/* pass ownership to the completion handler */
urb->status = status;
/*
* We disable local IRQs here avoid possible deadlock because
* drivers may call spin_lock() to hold lock which might be
* acquired in one hard interrupt handler.
*
* The local_irq_save()/local_irq_restore() around complete()
* will be removed if current USB drivers have been cleaned up
* and no one may trigger the above deadlock situation when
* running complete() in tasklet.
*/
local_irq_save(flags);
urb->complete(urb);
local_irq_restore(flags);
usb_anchor_resume_wakeups(anchor);
atomic_dec(&urb->use_count);

View File

@ -28,6 +28,7 @@
#include <linux/mutex.h>
#include <linux/random.h>
#include <linux/pm_qos.h>
#include <linux/kobject.h>
#include <linux/uaccess.h>
#include <asm/byteorder.h>
@ -2660,11 +2661,13 @@ static bool use_new_scheme(struct usb_device *udev, int retry,
{
int old_scheme_first_port =
port_dev->quirks & USB_PORT_QUIRK_OLD_SCHEME;
int quick_enumeration = (udev->speed == USB_SPEED_HIGH);
if (udev->speed >= USB_SPEED_SUPER)
return false;
return USE_NEW_SCHEME(retry, old_scheme_first_port || old_scheme_first);
return USE_NEW_SCHEME(retry, old_scheme_first_port || old_scheme_first
|| quick_enumeration);
}
/* Is a USB 3.0 port in the Inactive or Compliance Mode state?
@ -5147,6 +5150,42 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
usb_lock_port(port_dev);
}
/* Handle notifying userspace about hub over-current events */
static void port_over_current_notify(struct usb_port *port_dev)
{
static char *envp[] = { NULL, NULL, NULL };
struct device *hub_dev;
char *port_dev_path;
sysfs_notify(&port_dev->dev.kobj, NULL, "over_current_count");
hub_dev = port_dev->dev.parent;
if (!hub_dev)
return;
port_dev_path = kobject_get_path(&port_dev->dev.kobj, GFP_KERNEL);
if (!port_dev_path)
return;
envp[0] = kasprintf(GFP_KERNEL, "OVER_CURRENT_PORT=%s", port_dev_path);
if (!envp[0])
goto exit_path;
envp[1] = kasprintf(GFP_KERNEL, "OVER_CURRENT_COUNT=%u",
port_dev->over_current_count);
if (!envp[1])
goto exit;
kobject_uevent_env(&hub_dev->kobj, KOBJ_CHANGE, envp);
kfree(envp[1]);
exit:
kfree(envp[0]);
exit_path:
kfree(port_dev_path);
}
static void port_event(struct usb_hub *hub, int port1)
__must_hold(&port_dev->status_lock)
{
@ -5189,6 +5228,7 @@ static void port_event(struct usb_hub *hub, int port1)
if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
u16 status = 0, unused;
port_dev->over_current_count++;
port_over_current_notify(port_dev);
dev_dbg(&port_dev->dev, "over-current change #%u\n",
port_dev->over_current_count);

View File

@ -23,10 +23,11 @@ static int usb_phy_roothub_add_phy(struct device *dev, int index,
struct list_head *list)
{
struct usb_phy_roothub *roothub_entry;
struct phy *phy = devm_of_phy_get_by_index(dev, dev->of_node, index);
struct phy *phy;
if (IS_ERR_OR_NULL(phy)) {
if (!phy || PTR_ERR(phy) == -ENODEV)
phy = devm_of_phy_get_by_index(dev, dev->of_node, index);
if (IS_ERR(phy)) {
if (PTR_ERR(phy) == -ENODEV)
return 0;
else
return PTR_ERR(phy);

View File

@ -16,6 +16,15 @@ static int usb_port_block_power_off;
static const struct attribute_group *port_dev_group[];
static ssize_t location_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb_port *port_dev = to_usb_port(dev);
return sprintf(buf, "0x%08x\n", port_dev->location);
}
static DEVICE_ATTR_RO(location);
static ssize_t connect_type_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@ -140,6 +149,7 @@ static DEVICE_ATTR_RW(usb3_lpm_permit);
static struct attribute *port_dev_attrs[] = {
&dev_attr_connect_type.attr,
&dev_attr_location.attr,
&dev_attr_quirks.attr,
&dev_attr_over_current_count.attr,
NULL,

View File

@ -393,6 +393,20 @@ enum dwc2_ep0_state {
* 0 - No
* 1 - Yes
* @hird_threshold: Value of BESL or HIRD Threshold.
* @ref_clk_per: Indicates in terms of pico seconds the period
* of ref_clk.
* 62500 - 16MHz
* 58823 - 17MHz
* 52083 - 19.2MHz
* 50000 - 20MHz
* 41666 - 24MHz
* 33333 - 30MHz (default)
* 25000 - 40MHz
* @sof_cnt_wkup_alert: Indicates in term of number of SOF's after which
* the controller should generate an interrupt if the
* device had been in L1 state until that period.
* This is used by SW to initiate Remote WakeUp in the
* controller so as to sync to the uF number from the host.
* @activate_stm_fs_transceiver: Activate internal transceiver using GGPIO
* register.
* 0 - Deactivate the transceiver (default)
@ -416,6 +430,9 @@ enum dwc2_ep0_state {
* back to DWC2_SPEED_PARAM_HIGH while device is gone.
* 0 - No (default)
* 1 - Yes
* @service_interval: Enable service interval based scheduling.
* 0 - No
* 1 - Yes
*
* The following parameters may be specified when starting the module. These
* parameters define how the DWC_otg controller should be configured. A
@ -461,6 +478,7 @@ struct dwc2_core_params {
bool lpm_clock_gating;
bool besl;
bool hird_threshold_en;
bool service_interval;
u8 hird_threshold;
bool activate_stm_fs_transceiver;
bool ipg_isoc_en;
@ -468,6 +486,10 @@ struct dwc2_core_params {
u32 max_transfer_size;
u32 ahbcfg;
/* GREFCLK parameters */
u32 ref_clk_per;
u16 sof_cnt_wkup_alert;
/* Host parameters */
bool host_dma;
bool dma_desc_enable;
@ -605,6 +627,10 @@ struct dwc2_core_params {
* FIFO sizing is enabled 16 to 32768
* Actual maximum value is autodetected and also
* the default.
* @service_interval_mode: For enabling service interval based scheduling in the
* controller.
* 0 - Disable
* 1 - Enable
*/
struct dwc2_hw_params {
unsigned op_mode:3;
@ -635,6 +661,7 @@ struct dwc2_hw_params {
unsigned utmi_phy_data_width:2;
unsigned lpm_mode:1;
unsigned ipg_isoc_en:1;
unsigned service_interval_mode:1;
u32 snpsid;
u32 dev_ep_dirs;
u32 g_tx_fifo_size[MAX_EPS_CHANNELS];
@ -1354,6 +1381,7 @@ int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg);
int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg);
int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg);
void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg);
void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg);
#else
static inline int dwc2_hsotg_remove(struct dwc2_hsotg *dwc2)
{ return 0; }
@ -1388,6 +1416,7 @@ static inline int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg)
static inline int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg)
{ return 0; }
static inline void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg) {}
static inline void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg) {}
#endif
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)

View File

@ -701,6 +701,7 @@ static int params_show(struct seq_file *seq, void *v)
print_param(seq, p, besl);
print_param(seq, p, hird_threshold_en);
print_param(seq, p, hird_threshold);
print_param(seq, p, service_interval);
print_param(seq, p, host_dma);
print_param(seq, p, g_dma);
print_param(seq, p, g_dma_desc);

View File

@ -122,6 +122,24 @@ static inline void dwc2_gadget_incr_frame_num(struct dwc2_hsotg_ep *hs_ep)
}
}
/**
* dwc2_gadget_dec_frame_num_by_one - Decrements the targeted frame number
* by one.
* @hs_ep: The endpoint.
*
* This function used in service interval based scheduling flow to calculate
* descriptor frame number filed value. For service interval mode frame
* number in descriptor should point to last (u)frame in the interval.
*
*/
static inline void dwc2_gadget_dec_frame_num_by_one(struct dwc2_hsotg_ep *hs_ep)
{
if (hs_ep->target_frame)
hs_ep->target_frame -= 1;
else
hs_ep->target_frame = DSTS_SOFFN_LIMIT;
}
/**
* dwc2_hsotg_en_gsint - enable one or more of the general interrupt
* @hsotg: The device state
@ -227,6 +245,27 @@ int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg)
return tx_addr_max - addr;
}
/**
* dwc2_gadget_wkup_alert_handler - Handler for WKUP_ALERT interrupt
*
* @hsotg: Programming view of the DWC_otg controller
*
*/
static void dwc2_gadget_wkup_alert_handler(struct dwc2_hsotg *hsotg)
{
u32 gintsts2;
u32 gintmsk2;
gintsts2 = dwc2_readl(hsotg, GINTSTS2);
gintmsk2 = dwc2_readl(hsotg, GINTMSK2);
if (gintsts2 & GINTSTS2_WKUP_ALERT_INT) {
dev_dbg(hsotg->dev, "%s: Wkup_Alert_Int\n", __func__);
dwc2_clear_bit(hsotg, GINTSTS2, GINTSTS2_WKUP_ALERT_INT);
dwc2_set_bit(hsotg, DCFG, DCTL_RMTWKUPSIG);
}
}
/**
* dwc2_hsotg_tx_fifo_average_depth - returns average depth of device mode
* TX FIFOs
@ -2812,6 +2851,23 @@ static void dwc2_gadget_handle_nak(struct dwc2_hsotg_ep *hs_ep)
if (using_desc_dma(hsotg)) {
hs_ep->target_frame = hsotg->frame_number;
dwc2_gadget_incr_frame_num(hs_ep);
/* In service interval mode target_frame must
* be set to last (u)frame of the service interval.
*/
if (hsotg->params.service_interval) {
/* Set target_frame to the first (u)frame of
* the service interval
*/
hs_ep->target_frame &= ~hs_ep->interval + 1;
/* Set target_frame to the last (u)frame of
* the service interval
*/
dwc2_gadget_incr_frame_num(hs_ep);
dwc2_gadget_dec_frame_num_by_one(hs_ep);
}
dwc2_gadget_start_isoc_ddma(hs_ep);
return;
}
@ -3109,6 +3165,8 @@ static void kill_all_requests(struct dwc2_hsotg *hsotg,
dwc2_hsotg_txfifo_flush(hsotg, ep->fifo_index);
}
static int dwc2_hsotg_ep_disable(struct usb_ep *ep);
/**
* dwc2_hsotg_disconnect - disconnect service
* @hsotg: The device state.
@ -3127,13 +3185,12 @@ void dwc2_hsotg_disconnect(struct dwc2_hsotg *hsotg)
hsotg->connected = 0;
hsotg->test_mode = 0;
/* all endpoints should be shutdown */
for (ep = 0; ep < hsotg->num_of_eps; ep++) {
if (hsotg->eps_in[ep])
kill_all_requests(hsotg, hsotg->eps_in[ep],
-ESHUTDOWN);
dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep);
if (hsotg->eps_out[ep])
kill_all_requests(hsotg, hsotg->eps_out[ep],
-ESHUTDOWN);
dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep);
}
call_gadget(hsotg, disconnect);
@ -3191,13 +3248,23 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
u32 val;
u32 usbcfg;
u32 dcfg = 0;
int ep;
/* Kill any ep0 requests as controller will be reinitialized */
kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET);
if (!is_usb_reset)
if (!is_usb_reset) {
if (dwc2_core_reset(hsotg, true))
return;
} else {
/* all endpoints should be shutdown */
for (ep = 1; ep < hsotg->num_of_eps; ep++) {
if (hsotg->eps_in[ep])
dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep);
if (hsotg->eps_out[ep])
dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep);
}
}
/*
* we must now enable ep0 ready for host detection and then
@ -3312,6 +3379,10 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
dwc2_set_bit(hsotg, DIEPMSK, DIEPMSK_BNAININTRMSK);
}
/* Enable Service Interval mode if supported */
if (using_desc_dma(hsotg) && hsotg->params.service_interval)
dwc2_set_bit(hsotg, DCTL, DCTL_SERVICE_INTERVAL_SUPPORTED);
dwc2_writel(hsotg, 0, DAINTMSK);
dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n",
@ -3368,6 +3439,10 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
/* configure the core to support LPM */
dwc2_gadget_init_lpm(hsotg);
/* program GREFCLK register if needed */
if (using_desc_dma(hsotg) && hsotg->params.service_interval)
dwc2_gadget_program_ref_clk(hsotg);
/* must be at-least 3ms to allow bus to see disconnect */
mdelay(3);
@ -3676,6 +3751,10 @@ irq_retry:
if (gintsts & IRQ_RETRY_MASK && --retry_count > 0)
goto irq_retry;
/* Check WKUP_ALERT interrupt*/
if (hsotg->params.service_interval)
dwc2_gadget_wkup_alert_handler(hsotg);
spin_unlock(&hsotg->lock);
return IRQ_HANDLED;
@ -3993,6 +4072,7 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep)
unsigned long flags;
u32 epctrl_reg;
u32 ctrl;
int locked;
dev_dbg(hsotg->dev, "%s(ep %p)\n", __func__, ep);
@ -4008,7 +4088,9 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep)
epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index);
spin_lock_irqsave(&hsotg->lock, flags);
locked = spin_is_locked(&hsotg->lock);
if (!locked)
spin_lock_irqsave(&hsotg->lock, flags);
ctrl = dwc2_readl(hsotg, epctrl_reg);
@ -4032,7 +4114,9 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep)
hs_ep->fifo_index = 0;
hs_ep->fifo_size = 0;
spin_unlock_irqrestore(&hsotg->lock, flags);
if (!locked)
spin_unlock_irqrestore(&hsotg->lock, flags);
return 0;
}
@ -4944,6 +5028,29 @@ void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg)
val |= hsotg->params.besl ? GLPMCFG_ENBESL : 0;
dwc2_writel(hsotg, val, GLPMCFG);
dev_dbg(hsotg->dev, "GLPMCFG=0x%08x\n", dwc2_readl(hsotg, GLPMCFG));
/* Unmask WKUP_ALERT Interrupt */
if (hsotg->params.service_interval)
dwc2_set_bit(hsotg, GINTMSK2, GINTMSK2_WKUP_ALERT_INT_MSK);
}
/**
* dwc2_gadget_program_ref_clk - Program GREFCLK register in device mode
*
* @hsotg: Programming view of DWC_otg controller
*
*/
void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg)
{
u32 val = 0;
val |= GREFCLK_REF_CLK_MODE;
val |= hsotg->params.ref_clk_per << GREFCLK_REFCLKPER_SHIFT;
val |= hsotg->params.sof_cnt_wkup_alert <<
GREFCLK_SOF_CNT_WKUP_ALERT_SHIFT;
dwc2_writel(hsotg, val, GREFCLK);
dev_dbg(hsotg->dev, "GREFCLK=0x%08x\n", dwc2_readl(hsotg, GREFCLK));
}
/**

View File

@ -358,16 +358,10 @@ static void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg)
static int dwc2_vbus_supply_init(struct dwc2_hsotg *hsotg)
{
int ret;
if (hsotg->vbus_supply)
return regulator_enable(hsotg->vbus_supply);
hsotg->vbus_supply = devm_regulator_get_optional(hsotg->dev, "vbus");
if (IS_ERR(hsotg->vbus_supply)) {
ret = PTR_ERR(hsotg->vbus_supply);
hsotg->vbus_supply = NULL;
return ret == -ENODEV ? 0 : ret;
}
return regulator_enable(hsotg->vbus_supply);
return 0;
}
static int dwc2_vbus_supply_exit(struct dwc2_hsotg *hsotg)
@ -1328,14 +1322,11 @@ static void dwc2_hc_write_packet(struct dwc2_hsotg *hsotg,
u32 remaining_count;
u32 byte_count;
u32 dword_count;
u32 __iomem *data_fifo;
u32 *data_buf = (u32 *)chan->xfer_buf;
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "%s()\n", __func__);
data_fifo = (u32 __iomem *)(hsotg->regs + HCFIFO(chan->hc_num));
remaining_count = chan->xfer_len - chan->xfer_count;
if (remaining_count > chan->max_packet)
byte_count = chan->max_packet;
@ -3564,6 +3555,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
u32 port_status;
u32 speed;
u32 pcgctl;
u32 pwr;
switch (typereq) {
case ClearHubFeature:
@ -3612,8 +3604,11 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
dev_dbg(hsotg->dev,
"ClearPortFeature USB_PORT_FEAT_POWER\n");
hprt0 = dwc2_read_hprt0(hsotg);
pwr = hprt0 & HPRT0_PWR;
hprt0 &= ~HPRT0_PWR;
dwc2_writel(hsotg, hprt0, HPRT0);
if (pwr)
dwc2_vbus_supply_exit(hsotg);
break;
case USB_PORT_FEAT_INDICATOR:
@ -3823,8 +3818,11 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
dev_dbg(hsotg->dev,
"SetPortFeature - USB_PORT_FEAT_POWER\n");
hprt0 = dwc2_read_hprt0(hsotg);
pwr = hprt0 & HPRT0_PWR;
hprt0 |= HPRT0_PWR;
dwc2_writel(hsotg, hprt0, HPRT0);
if (!pwr)
dwc2_vbus_supply_init(hsotg);
break;
case USB_PORT_FEAT_RESET:
@ -3841,6 +3839,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
dwc2_writel(hsotg, 0, PCGCTL);
hprt0 = dwc2_read_hprt0(hsotg);
pwr = hprt0 & HPRT0_PWR;
/* Clear suspend bit if resetting from suspend state */
hprt0 &= ~HPRT0_SUSP;
@ -3854,6 +3853,8 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
dev_dbg(hsotg->dev,
"In host mode, hprt0=%08x\n", hprt0);
dwc2_writel(hsotg, hprt0, HPRT0);
if (!pwr)
dwc2_vbus_supply_init(hsotg);
}
/* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */
@ -4393,6 +4394,8 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd)
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
struct usb_bus *bus = hcd_to_bus(hcd);
unsigned long flags;
u32 hprt0;
int ret;
dev_dbg(hsotg->dev, "DWC OTG HCD START\n");
@ -4408,6 +4411,17 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd)
dwc2_hcd_reinit(hsotg);
hprt0 = dwc2_read_hprt0(hsotg);
/* Has vbus power been turned on in dwc2_core_host_init ? */
if (hprt0 & HPRT0_PWR) {
/* Enable external vbus supply before resuming root hub */
spin_unlock_irqrestore(&hsotg->lock, flags);
ret = dwc2_vbus_supply_init(hsotg);
if (ret)
return ret;
spin_lock_irqsave(&hsotg->lock, flags);
}
/* Initialize and connect root hub if one is not already attached */
if (bus->root_hub) {
dev_dbg(hsotg->dev, "DWC OTG HCD Has Root Hub\n");
@ -4417,7 +4431,7 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd)
spin_unlock_irqrestore(&hsotg->lock, flags);
return dwc2_vbus_supply_init(hsotg);
return 0;
}
/*
@ -4428,6 +4442,7 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
{
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
unsigned long flags;
u32 hprt0;
/* Turn off all host-specific interrupts */
dwc2_disable_host_interrupts(hsotg);
@ -4436,6 +4451,7 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
synchronize_irq(hcd->irq);
spin_lock_irqsave(&hsotg->lock, flags);
hprt0 = dwc2_read_hprt0(hsotg);
/* Ensure hcd is disconnected */
dwc2_hcd_disconnect(hsotg, true);
dwc2_hcd_stop(hsotg);
@ -4444,7 +4460,9 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
spin_unlock_irqrestore(&hsotg->lock, flags);
dwc2_vbus_supply_exit(hsotg);
/* keep balanced supply init/exit by checking HPRT0_PWR */
if (hprt0 & HPRT0_PWR)
dwc2_vbus_supply_exit(hsotg);
usleep_range(1000, 3000);
}
@ -4482,7 +4500,9 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
hprt0 |= HPRT0_SUSP;
hprt0 &= ~HPRT0_PWR;
dwc2_writel(hsotg, hprt0, HPRT0);
spin_unlock_irqrestore(&hsotg->lock, flags);
dwc2_vbus_supply_exit(hsotg);
spin_lock_irqsave(&hsotg->lock, flags);
}
/* Enter partial_power_down */

View File

@ -312,6 +312,7 @@
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT 14
#define GHWCFG4_ACG_SUPPORTED BIT(12)
#define GHWCFG4_IPG_ISOC_SUPPORTED BIT(11)
#define GHWCFG4_SERVICE_INTERVAL_SUPPORTED BIT(10)
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_8 0
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_16 1
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_8_OR_16 2
@ -404,6 +405,19 @@
#define ADPCTL_PRB_DSCHRG_MASK (0x3 << 0)
#define ADPCTL_PRB_DSCHRG_SHIFT 0
#define GREFCLK HSOTG_REG(0x0064)
#define GREFCLK_REFCLKPER_MASK (0x1ffff << 15)
#define GREFCLK_REFCLKPER_SHIFT 15
#define GREFCLK_REF_CLK_MODE BIT(14)
#define GREFCLK_SOF_CNT_WKUP_ALERT_MASK (0x3ff)
#define GREFCLK_SOF_CNT_WKUP_ALERT_SHIFT 0
#define GINTMSK2 HSOTG_REG(0x0068)
#define GINTMSK2_WKUP_ALERT_INT_MSK BIT(0)
#define GINTSTS2 HSOTG_REG(0x006c)
#define GINTSTS2_WKUP_ALERT_INT BIT(0)
#define HPTXFSIZ HSOTG_REG(0x100)
/* Use FIFOSIZE_* constants to access this register */
@ -443,6 +457,7 @@
#define DCFG_DEVSPD_FS48 3
#define DCTL HSOTG_REG(0x804)
#define DCTL_SERVICE_INTERVAL_SUPPORTED BIT(19)
#define DCTL_PWRONPRGDONE BIT(11)
#define DCTL_CGOUTNAK BIT(10)
#define DCTL_SGOUTNAK BIT(9)

View File

@ -81,6 +81,7 @@ static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg)
p->host_perio_tx_fifo_size = 256;
p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
GAHBCFG_HBSTLEN_SHIFT;
p->power_down = 0;
}
static void dwc2_set_ltq_params(struct dwc2_hsotg *hsotg)
@ -299,9 +300,12 @@ static void dwc2_set_default_params(struct dwc2_hsotg *hsotg)
p->hird_threshold_en = true;
p->hird_threshold = 4;
p->ipg_isoc_en = false;
p->service_interval = false;
p->max_packet_count = hw->max_packet_count;
p->max_transfer_size = hw->max_transfer_size;
p->ahbcfg = GAHBCFG_HBSTLEN_INCR << GAHBCFG_HBSTLEN_SHIFT;
p->ref_clk_per = 33333;
p->sof_cnt_wkup_alert = 100;
if ((hsotg->dr_mode == USB_DR_MODE_HOST) ||
(hsotg->dr_mode == USB_DR_MODE_OTG)) {
@ -592,6 +596,7 @@ static void dwc2_check_params(struct dwc2_hsotg *hsotg)
CHECK_BOOL(besl, (hsotg->hw_params.snpsid >= DWC2_CORE_REV_3_00a));
CHECK_BOOL(hird_threshold_en, hsotg->params.lpm);
CHECK_RANGE(hird_threshold, 0, hsotg->params.besl ? 12 : 7, 0);
CHECK_BOOL(service_interval, hw->service_interval_mode);
CHECK_RANGE(max_packet_count,
15, hw->max_packet_count,
hw->max_packet_count);
@ -780,6 +785,8 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT;
hw->acg_enable = !!(hwcfg4 & GHWCFG4_ACG_SUPPORTED);
hw->ipg_isoc_en = !!(hwcfg4 & GHWCFG4_IPG_ISOC_SUPPORTED);
hw->service_interval_mode = !!(hwcfg4 &
GHWCFG4_SERVICE_INTERVAL_SUPPORTED);
/* fifo sizes */
hw->rx_fifo_size = (grxfsiz & GRXFSIZ_DEPTH_MASK) >>

View File

@ -432,6 +432,14 @@ static int dwc2_driver_probe(struct platform_device *dev)
if (retval)
return retval;
hsotg->vbus_supply = devm_regulator_get_optional(hsotg->dev, "vbus");
if (IS_ERR(hsotg->vbus_supply)) {
retval = PTR_ERR(hsotg->vbus_supply);
hsotg->vbus_supply = NULL;
if (retval != -ENODEV)
return retval;
}
retval = dwc2_lowlevel_hw_enable(hsotg);
if (retval)
return retval;

View File

@ -113,7 +113,7 @@ config USB_DWC3_ST
config USB_DWC3_QCOM
tristate "Qualcomm Platform"
depends on ARCH_QCOM || COMPILE_TEST
depends on EXTCON && (ARCH_QCOM || COMPILE_TEST)
depends on OF
default USB_DWC3
help

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