Merge tag 'phy-for-5.6_v2' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy into usb-next
Kishon writes: phy: for 5.6 *) Add support in PHY core to create link between PHY consumer and PHY provider *) Add DisplayPort PHY configuration set to be used for negotiating the configurations to be used between DisplayPort controller and DisplayPort PHY *) Add PHY wrapper driver (configure inputs to Cadence Sierra PHY) for TI's J721E SoC and adapt Cadence Sierra PHY driver to be used for J721E SoC (Supports USB and PCIe) *) Add PHY driver for eMMC PHY in Intel LGM SoC *) Add PHY support for 7216 and 7211 Broadcom SoCs which uses the new Synopsys USB Controller *) Add support for 16nm SATA PHY present in Broadcom 7216 SoC *) Fix lost packet issue, fix MDIO from getting inaccessible, fix occasional transaction failures, fix USB driver from crashing in Broadcom USB PHY driver *) Fix missing PCS SW reset in UFS PHY of Qualcomm SM8150 *) Use "struct phy_configure_opts_mipi_dphy" to pass parameters from display controller to rockchip-inno-dsidphy *) Other cleanups including compile testing for some of the PHY drivers, fixing Kconfig indentation, duplicate writes in drivers etc., Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com> * tag 'phy-for-5.6_v2' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy: (54 commits) dt-bindings: phy: Add PHY_TYPE_DP definition phy: ti: j721e-wiz: Fix return value check in wiz_probe() dt-bindings: usb: Convert Allwinner A80 USB PHY controller to a schema phy: intel-lgm-emmc: Fix warning by adding missing MODULE_LICENSE phy: ti: j721e-wiz: Manage typec-gpio-dir dt-bindings: phy: ti,phy-j721e-wiz: Add Type-C dir GPIO phy: cadence: Sierra: add phy_reset hook phy: cadence: Sierra: remove redundant initialization of pointer regmap phy: Add DisplayPort configuration options phy: Enable compile testing for some of drivers phy: mediatek: Fix Kconfig indentation phy: intel-lgm-emmc: Add support for eMMC PHY dt-bindings: phy: intel-emmc-phy: Add YAML schema for LGM eMMC PHY phy: ti: j721e-wiz: Add support for WIZ module present in TI J721E SoC dt-bindings: phy: Document WIZ (SERDES wrapper) bindings phy: cadence: Sierra: Use correct dev pointer in cdns_sierra_phy_remove() phy: cadence: Sierra: Set cmn_refclk_dig_div/cmn_refclk1_dig_div frequency to 25MHz phy: cadence: Sierra: Change MAX_LANES of Sierra to 16 phy: cadence: Sierra: Check for PLL lock during PHY power on phy: cadence: Sierra: Get reset control "array" for each link ...
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/phy/allwinner,sun9i-a80-usb-phy.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Allwinner A80 USB PHY Device Tree Bindings
|
||||
|
||||
maintainers:
|
||||
- Chen-Yu Tsai <wens@csie.org>
|
||||
- Maxime Ripard <mripard@kernel.org>
|
||||
|
||||
properties:
|
||||
"#phy-cells":
|
||||
const: 0
|
||||
|
||||
compatible:
|
||||
const: allwinner,sun9i-a80-usb-phy
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
anyOf:
|
||||
- description: Main PHY Clock
|
||||
|
||||
- items:
|
||||
- description: Main PHY clock
|
||||
- description: HSIC 12MHz clock
|
||||
- description: HSIC 480MHz clock
|
||||
|
||||
clock-names:
|
||||
oneOf:
|
||||
- const: phy
|
||||
|
||||
- items:
|
||||
- const: phy
|
||||
- const: hsic_12M
|
||||
- const: hsic_480M
|
||||
|
||||
resets:
|
||||
anyOf:
|
||||
- description: Normal USB PHY reset
|
||||
|
||||
- items:
|
||||
- description: Normal USB PHY reset
|
||||
- description: HSIC Reset
|
||||
|
||||
reset-names:
|
||||
oneOf:
|
||||
- const: phy
|
||||
|
||||
- items:
|
||||
- const: phy
|
||||
- const: hsic
|
||||
|
||||
phy_type:
|
||||
const: hsic
|
||||
description:
|
||||
When absent, the PHY type will be assumed to be normal USB.
|
||||
|
||||
phy-supply:
|
||||
description:
|
||||
Regulator that powers VBUS
|
||||
|
||||
required:
|
||||
- "#phy-cells"
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- resets
|
||||
- reset-names
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
if:
|
||||
properties:
|
||||
phy_type:
|
||||
const: hsic
|
||||
|
||||
required:
|
||||
- phy_type
|
||||
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 3
|
||||
|
||||
clock-names:
|
||||
maxItems: 3
|
||||
|
||||
resets:
|
||||
maxItems: 2
|
||||
|
||||
reset-names:
|
||||
maxItems: 2
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/sun9i-a80-usb.h>
|
||||
#include <dt-bindings/reset/sun9i-a80-usb.h>
|
||||
|
||||
usbphy1: phy@a00800 {
|
||||
compatible = "allwinner,sun9i-a80-usb-phy";
|
||||
reg = <0x00a00800 0x4>;
|
||||
clocks = <&usb_clocks CLK_USB0_PHY>;
|
||||
clock-names = "phy";
|
||||
resets = <&usb_clocks RST_USB0_PHY>;
|
||||
reset-names = "phy";
|
||||
phy-supply = <®_usb1_vbus>;
|
||||
#phy-cells = <0>;
|
||||
};
|
||||
|
||||
- |
|
||||
#include <dt-bindings/clock/sun9i-a80-usb.h>
|
||||
#include <dt-bindings/reset/sun9i-a80-usb.h>
|
||||
|
||||
usbphy3: phy@a02800 {
|
||||
compatible = "allwinner,sun9i-a80-usb-phy";
|
||||
reg = <0x00a02800 0x4>;
|
||||
clocks = <&usb_clocks CLK_USB2_PHY>,
|
||||
<&usb_clocks CLK_USB_HSIC>,
|
||||
<&usb_clocks CLK_USB2_HSIC>;
|
||||
clock-names = "phy",
|
||||
"hsic_12M",
|
||||
"hsic_480M";
|
||||
resets = <&usb_clocks RST_USB2_PHY>,
|
||||
<&usb_clocks RST_USB2_HSIC>;
|
||||
reset-names = "phy",
|
||||
"hsic";
|
||||
phy_type = "hsic";
|
||||
phy-supply = <®_usb3_vbus>;
|
||||
#phy-cells = <0>;
|
||||
};
|
@@ -1,30 +1,49 @@
|
||||
Broadcom STB USB PHY
|
||||
|
||||
Required properties:
|
||||
- compatible: brcm,brcmstb-usb-phy
|
||||
- reg: two offset and length pairs.
|
||||
The first pair specifies a manditory set of memory mapped
|
||||
registers used for general control of the PHY.
|
||||
The second pair specifies optional registers used by some of
|
||||
the SoCs that support USB 3.x
|
||||
- #phy-cells: Shall be 1 as it expects one argument for setting
|
||||
the type of the PHY. Possible values are:
|
||||
- PHY_TYPE_USB2 for USB1.1/2.0 PHY
|
||||
- PHY_TYPE_USB3 for USB3.x PHY
|
||||
- compatible: should be one of
|
||||
"brcm,brcmstb-usb-phy"
|
||||
"brcm,bcm7216-usb-phy"
|
||||
"brcm,bcm7211-usb-phy"
|
||||
|
||||
- reg and reg-names properties requirements are specific to the
|
||||
compatible string.
|
||||
"brcm,brcmstb-usb-phy":
|
||||
- reg: 1 or 2 offset and length pairs. One for the base CTRL registers
|
||||
and an optional pair for systems with USB 3.x support
|
||||
- reg-names: not specified
|
||||
"brcm,bcm7216-usb-phy":
|
||||
- reg: 3 offset and length pairs for CTRL, XHCI_EC and XHCI_GBL
|
||||
registers
|
||||
- reg-names: "ctrl", "xhci_ec", "xhci_gbl"
|
||||
"brcm,bcm7211-usb-phy":
|
||||
- reg: 5 offset and length pairs for CTRL, XHCI_EC, XHCI_GBL,
|
||||
USB_PHY and USB_MDIO registers and an optional pair
|
||||
for the BDC registers
|
||||
- reg-names: "ctrl", "xhci_ec", "xhci_gbl", "usb_phy", "usb_mdio", "bdc_ec"
|
||||
|
||||
- #phy-cells: Shall be 1 as it expects one argument for setting
|
||||
the type of the PHY. Possible values are:
|
||||
- PHY_TYPE_USB2 for USB1.1/2.0 PHY
|
||||
- PHY_TYPE_USB3 for USB3.x PHY
|
||||
|
||||
Optional Properties:
|
||||
- clocks : clock phandles.
|
||||
- clock-names: String, clock name.
|
||||
- interrupts: wakeup interrupt
|
||||
- interrupt-names: "wakeup"
|
||||
- brcm,ipp: Boolean, Invert Port Power.
|
||||
Possible values are: 0 (Don't invert), 1 (Invert)
|
||||
- brcm,ioc: Boolean, Invert Over Current detection.
|
||||
Possible values are: 0 (Don't invert), 1 (Invert)
|
||||
NOTE: one or both of the following two properties must be set
|
||||
- brcm,has-xhci: Boolean indicating the phy has an XHCI phy.
|
||||
- brcm,has-eohci: Boolean indicating the phy has an EHCI/OHCI phy.
|
||||
- dr_mode: String, PHY Device mode.
|
||||
Possible values are: "host", "peripheral ", "drd" or "typec-pd"
|
||||
If this property is not defined, the phy will default to "host" mode.
|
||||
- brcm,syscon-piarbctl: phandle to syscon for handling config registers
|
||||
NOTE: one or both of the following two properties must be set
|
||||
- brcm,has-xhci: Boolean indicating the phy has an XHCI phy.
|
||||
- brcm,has-eohci: Boolean indicating the phy has an EHCI/OHCI phy.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
@@ -41,3 +60,27 @@ usbphy_0: usb-phy@f0470200 {
|
||||
clocks = <&usb20>, <&usb30>;
|
||||
clock-names = "sw_usb", "sw_usb3";
|
||||
};
|
||||
|
||||
usb-phy@29f0200 {
|
||||
reg = <0x29f0200 0x200>,
|
||||
<0x29c0880 0x30>,
|
||||
<0x29cc100 0x534>,
|
||||
<0x2808000 0x24>,
|
||||
<0x2980080 0x8>;
|
||||
reg-names = "ctrl",
|
||||
"xhci_ec",
|
||||
"xhci_gbl",
|
||||
"usb_phy",
|
||||
"usb_mdio";
|
||||
brcm,ioc = <0x0>;
|
||||
brcm,ipp = <0x0>;
|
||||
compatible = "brcm,bcm7211-usb-phy";
|
||||
interrupts = <0x30>;
|
||||
interrupt-parent = <&vpu_intr1_nosec_intc>;
|
||||
interrupt-names = "wake";
|
||||
#phy-cells = <0x1>;
|
||||
brcm,has-xhci;
|
||||
syscon-piarbctl = <&syscon_piarbctl>;
|
||||
clocks = <&scmi_clk 256>;
|
||||
clock-names = "sw_usb";
|
||||
};
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
Required properties:
|
||||
- compatible: should be one or more of
|
||||
"brcm,bcm7216-sata-phy"
|
||||
"brcm,bcm7425-sata-phy"
|
||||
"brcm,bcm7445-sata-phy"
|
||||
"brcm,iproc-ns2-sata-phy"
|
||||
|
@@ -0,0 +1,56 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/phy/intel,lgm-emmc-phy.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Intel Lightning Mountain(LGM) eMMC PHY Device Tree Bindings
|
||||
|
||||
maintainers:
|
||||
- Ramuthevar Vadivel Murugan <vadivel.muruganx.ramuthevar@linux.intel.com>
|
||||
|
||||
description: |+
|
||||
Bindings for eMMC PHY on Intel's Lightning Mountain SoC, syscon
|
||||
node is used to reference the base address of eMMC phy registers.
|
||||
|
||||
The eMMC PHY node should be the child of a syscon node with the
|
||||
required property:
|
||||
|
||||
- compatible: Should be one of the following:
|
||||
"intel,lgm-syscon", "syscon"
|
||||
- reg:
|
||||
maxItems: 1
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: intel,lgm-emmc-phy
|
||||
|
||||
"#phy-cells":
|
||||
const: 0
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- "#phy-cells"
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
|
||||
examples:
|
||||
- |
|
||||
sysconf: chiptop@e0200000 {
|
||||
compatible = "intel,lgm-syscon", "syscon";
|
||||
reg = <0xe0200000 0x100>;
|
||||
|
||||
emmc-phy: emmc-phy@a8 {
|
||||
compatible = "intel,lgm-emmc-phy";
|
||||
reg = <0x00a8 0x10>;
|
||||
clocks = <&emmc>;
|
||||
#phy-cells = <0>;
|
||||
};
|
||||
};
|
||||
...
|
@@ -2,21 +2,24 @@ Cadence Sierra PHY
|
||||
-----------------------
|
||||
|
||||
Required properties:
|
||||
- compatible: cdns,sierra-phy-t0
|
||||
- clocks: Must contain an entry in clock-names.
|
||||
See ../clocks/clock-bindings.txt for details.
|
||||
- clock-names: Must be "phy_clk"
|
||||
- compatible: Must be "cdns,sierra-phy-t0" for Sierra in Cadence platform
|
||||
Must be "ti,sierra-phy-t0" for Sierra in TI's J721E SoC.
|
||||
- resets: Must contain an entry for each in reset-names.
|
||||
See ../reset/reset.txt for details.
|
||||
- reset-names: Must include "sierra_reset" and "sierra_apb".
|
||||
"sierra_reset" must control the reset line to the PHY.
|
||||
"sierra_apb" must control the reset line to the APB PHY
|
||||
interface.
|
||||
interface ("sierra_apb" is optional).
|
||||
- reg: register range for the PHY.
|
||||
- #address-cells: Must be 1
|
||||
- #size-cells: Must be 0
|
||||
|
||||
Optional properties:
|
||||
- clocks: Must contain an entry in clock-names.
|
||||
See ../clocks/clock-bindings.txt for details.
|
||||
- clock-names: Must contain "cmn_refclk_dig_div" and
|
||||
"cmn_refclk1_dig_div" for configuring the frequency of
|
||||
the clock to the lanes. "phy_clk" is deprecated.
|
||||
- cdns,autoconf: A boolean property whose presence indicates that the
|
||||
PHY registers will be configured by hardware. If not
|
||||
present, all sub-node optional properties must be
|
||||
|
@@ -13,9 +13,6 @@ properties:
|
||||
"#phy-cells":
|
||||
const: 0
|
||||
|
||||
"#clock-cells":
|
||||
const: 0
|
||||
|
||||
compatible:
|
||||
enum:
|
||||
- rockchip,px30-dsi-dphy
|
||||
@@ -49,7 +46,6 @@ properties:
|
||||
|
||||
required:
|
||||
- "#phy-cells"
|
||||
- "#clock-cells"
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
@@ -66,7 +62,6 @@ examples:
|
||||
reg = <0x0 0xff2e0000 0x0 0x10000>;
|
||||
clocks = <&pmucru 13>, <&cru 12>;
|
||||
clock-names = "ref", "pclk";
|
||||
#clock-cells = <0>;
|
||||
resets = <&cru 12>;
|
||||
reset-names = "apb";
|
||||
#phy-cells = <0>;
|
||||
|
@@ -1,37 +0,0 @@
|
||||
Allwinner sun9i USB PHY
|
||||
-----------------------
|
||||
|
||||
Required properties:
|
||||
- compatible : should be one of
|
||||
* allwinner,sun9i-a80-usb-phy
|
||||
- reg : a list of offset + length pairs
|
||||
- #phy-cells : from the generic phy bindings, must be 0
|
||||
- phy_type : "hsic" for HSIC usage;
|
||||
other values or absence of this property indicates normal USB
|
||||
- clocks : phandle + clock specifier for the phy clocks
|
||||
- clock-names : depending on the "phy_type" property,
|
||||
* "phy" for normal USB
|
||||
* "hsic_480M", "hsic_12M" for HSIC
|
||||
- resets : a list of phandle + reset specifier pairs
|
||||
- reset-names : depending on the "phy_type" property,
|
||||
* "phy" for normal USB
|
||||
* "hsic" for HSIC
|
||||
|
||||
Optional Properties:
|
||||
- phy-supply : from the generic phy bindings, a phandle to a regulator that
|
||||
provides power to VBUS.
|
||||
|
||||
It is recommended to list all clocks and resets available.
|
||||
The driver will only use those matching the phy_type.
|
||||
|
||||
Example:
|
||||
usbphy1: phy@a01800 {
|
||||
compatible = "allwinner,sun9i-a80-usb-phy";
|
||||
reg = <0x00a01800 0x4>;
|
||||
clocks = <&usb_phy_clk 2>, <&usb_phy_clk 10>,
|
||||
<&usb_phy_clk 3>;
|
||||
clock-names = "hsic_480M", "hsic_12M", "phy";
|
||||
resets = <&usb_phy_clk 18>, <&usb_phy_clk 19>;
|
||||
reset-names = "hsic", "phy";
|
||||
#phy-cells = <0>;
|
||||
};
|
221
Documentation/devicetree/bindings/phy/ti,phy-j721e-wiz.yaml
Normal file
221
Documentation/devicetree/bindings/phy/ti,phy-j721e-wiz.yaml
Normal file
@@ -0,0 +1,221 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
# Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/phy/ti,phy-j721e-wiz.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: TI J721E WIZ (SERDES Wrapper)
|
||||
|
||||
maintainers:
|
||||
- Kishon Vijay Abraham I <kishon@ti.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ti,j721e-wiz-16g
|
||||
- ti,j721e-wiz-10g
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 3
|
||||
description: clock-specifier to represent input to the WIZ
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: fck
|
||||
- const: core_ref_clk
|
||||
- const: ext_ref_clk
|
||||
|
||||
num-lanes:
|
||||
minimum: 1
|
||||
maximum: 4
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 1
|
||||
|
||||
"#reset-cells":
|
||||
const: 1
|
||||
|
||||
ranges: true
|
||||
|
||||
assigned-clocks:
|
||||
maxItems: 2
|
||||
|
||||
assigned-clock-parents:
|
||||
maxItems: 2
|
||||
|
||||
typec-dir-gpios:
|
||||
maxItems: 1
|
||||
description:
|
||||
GPIO to signal Type-C cable orientation for lane swap.
|
||||
If GPIO is active, lane 0 and lane 1 of SERDES will be swapped to
|
||||
achieve the funtionality of an external type-C plug flip mux.
|
||||
|
||||
typec-dir-debounce-ms:
|
||||
minimum: 100
|
||||
maximum: 1000
|
||||
default: 100
|
||||
description:
|
||||
Number of milliseconds to wait before sampling typec-dir-gpio.
|
||||
If not specified, the default debounce of 100ms will be used.
|
||||
Type-C spec states minimum CC pin debounce of 100 ms and maximum
|
||||
of 200 ms. However, some solutions might need more than 200 ms.
|
||||
|
||||
patternProperties:
|
||||
"^pll[0|1]-refclk$":
|
||||
type: object
|
||||
description: |
|
||||
WIZ node should have subnodes for each of the PLLs present in
|
||||
the SERDES.
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 2
|
||||
description: Phandle to clock nodes representing the two inputs to PLL.
|
||||
|
||||
"#clock-cells":
|
||||
const: 0
|
||||
|
||||
assigned-clocks:
|
||||
maxItems: 1
|
||||
|
||||
assigned-clock-parents:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- clocks
|
||||
- "#clock-cells"
|
||||
- assigned-clocks
|
||||
- assigned-clock-parents
|
||||
|
||||
"^cmn-refclk1?-dig-div$":
|
||||
type: object
|
||||
description:
|
||||
WIZ node should have subnodes for each of the PMA common refclock
|
||||
provided by the SERDES.
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
description: Phandle to the clock node representing the input to the
|
||||
divider clock.
|
||||
|
||||
"#clock-cells":
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- clocks
|
||||
- "#clock-cells"
|
||||
|
||||
"^refclk-dig$":
|
||||
type: object
|
||||
description: |
|
||||
WIZ node should have subnode for refclk_dig to select the reference
|
||||
clock source for the reference clock used in the PHY and PMA digital
|
||||
logic.
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 4
|
||||
description: Phandle to four clock nodes representing the inputs to
|
||||
refclk_dig
|
||||
|
||||
"#clock-cells":
|
||||
const: 0
|
||||
|
||||
assigned-clocks:
|
||||
maxItems: 1
|
||||
|
||||
assigned-clock-parents:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- clocks
|
||||
- "#clock-cells"
|
||||
- assigned-clocks
|
||||
- assigned-clock-parents
|
||||
|
||||
"^serdes@[0-9a-f]+$":
|
||||
type: object
|
||||
description: |
|
||||
WIZ node should have '1' subnode for the SERDES. It could be either
|
||||
Sierra SERDES or Torrent SERDES. Sierra SERDES should follow the
|
||||
bindings specified in
|
||||
Documentation/devicetree/bindings/phy/phy-cadence-sierra.txt
|
||||
Torrent SERDES should follow the bindings specified in
|
||||
Documentation/devicetree/bindings/phy/phy-cadence-dp.txt
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- power-domains
|
||||
- clocks
|
||||
- clock-names
|
||||
- num-lanes
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
- "#reset-cells"
|
||||
- ranges
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/soc/ti,sci_pm_domain.h>
|
||||
|
||||
wiz@5000000 {
|
||||
compatible = "ti,j721e-wiz-16g";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
power-domains = <&k3_pds 292 TI_SCI_PD_EXCLUSIVE>;
|
||||
clocks = <&k3_clks 292 5>, <&k3_clks 292 11>, <&dummy_cmn_refclk>;
|
||||
clock-names = "fck", "core_ref_clk", "ext_ref_clk";
|
||||
assigned-clocks = <&k3_clks 292 11>, <&k3_clks 292 0>;
|
||||
assigned-clock-parents = <&k3_clks 292 15>, <&k3_clks 292 4>;
|
||||
num-lanes = <2>;
|
||||
#reset-cells = <1>;
|
||||
ranges = <0x5000000 0x5000000 0x10000>;
|
||||
|
||||
pll0-refclk {
|
||||
clocks = <&k3_clks 293 13>, <&dummy_cmn_refclk>;
|
||||
#clock-cells = <0>;
|
||||
assigned-clocks = <&wiz1_pll0_refclk>;
|
||||
assigned-clock-parents = <&k3_clks 293 13>;
|
||||
};
|
||||
|
||||
pll1-refclk {
|
||||
clocks = <&k3_clks 293 0>, <&dummy_cmn_refclk1>;
|
||||
#clock-cells = <0>;
|
||||
assigned-clocks = <&wiz1_pll1_refclk>;
|
||||
assigned-clock-parents = <&k3_clks 293 0>;
|
||||
};
|
||||
|
||||
cmn-refclk-dig-div {
|
||||
clocks = <&wiz1_refclk_dig>;
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
cmn-refclk1-dig-div {
|
||||
clocks = <&wiz1_pll1_refclk>;
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
refclk-dig {
|
||||
clocks = <&k3_clks 292 11>, <&k3_clks 292 0>, <&dummy_cmn_refclk>, <&dummy_cmn_refclk1>;
|
||||
#clock-cells = <0>;
|
||||
assigned-clocks = <&wiz0_refclk_dig>;
|
||||
assigned-clock-parents = <&k3_clks 292 11>;
|
||||
};
|
||||
|
||||
serdes@5000000 {
|
||||
compatible = "cdns,ti,sierra-phy-t0";
|
||||
reg-names = "serdes";
|
||||
reg = <0x5000000 0x10000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
resets = <&serdes_wiz0 0>;
|
||||
reset-names = "sierra_reset";
|
||||
clocks = <&wiz0_cmn_refclk_dig_div>, <&wiz0_cmn_refclk1_dig_div>;
|
||||
clock-names = "cmn_refclk_dig_div", "cmn_refclk1_dig_div";
|
||||
};
|
||||
};
|
@@ -69,5 +69,6 @@ source "drivers/phy/socionext/Kconfig"
|
||||
source "drivers/phy/st/Kconfig"
|
||||
source "drivers/phy/tegra/Kconfig"
|
||||
source "drivers/phy/ti/Kconfig"
|
||||
source "drivers/phy/intel/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
@@ -18,6 +18,7 @@ obj-y += broadcom/ \
|
||||
cadence/ \
|
||||
freescale/ \
|
||||
hisilicon/ \
|
||||
intel/ \
|
||||
lantiq/ \
|
||||
marvell/ \
|
||||
motorola/ \
|
||||
|
@@ -8,7 +8,7 @@ obj-$(CONFIG_PHY_NS2_USB_DRD) += phy-bcm-ns2-usbdrd.o
|
||||
obj-$(CONFIG_PHY_BRCM_SATA) += phy-brcm-sata.o
|
||||
obj-$(CONFIG_PHY_BRCM_USB) += phy-brcm-usb-dvr.o
|
||||
|
||||
phy-brcm-usb-dvr-objs := phy-brcm-usb.o phy-brcm-usb-init.o
|
||||
phy-brcm-usb-dvr-objs := phy-brcm-usb.o phy-brcm-usb-init.o phy-brcm-usb-init-synopsys.o
|
||||
|
||||
obj-$(CONFIG_PHY_BCM_SR_PCIE) += phy-bcm-sr-pcie.o
|
||||
obj-$(CONFIG_PHY_BCM_SR_USB) += phy-bcm-sr-usb.o
|
||||
|
@@ -33,6 +33,7 @@
|
||||
#define SATA_PHY_CTRL_REG_28NM_SPACE_SIZE 0x8
|
||||
|
||||
enum brcm_sata_phy_version {
|
||||
BRCM_SATA_PHY_STB_16NM,
|
||||
BRCM_SATA_PHY_STB_28NM,
|
||||
BRCM_SATA_PHY_STB_40NM,
|
||||
BRCM_SATA_PHY_IPROC_NS2,
|
||||
@@ -104,10 +105,13 @@ enum sata_phy_regs {
|
||||
PLL1_ACTRL5 = 0x85,
|
||||
PLL1_ACTRL6 = 0x86,
|
||||
PLL1_ACTRL7 = 0x87,
|
||||
PLL1_ACTRL8 = 0x88,
|
||||
|
||||
TX_REG_BANK = 0x070,
|
||||
TX_ACTRL0 = 0x80,
|
||||
TX_ACTRL0_TXPOL_FLIP = BIT(6),
|
||||
TX_ACTRL5 = 0x85,
|
||||
TX_ACTRL5_SSC_EN = BIT(11),
|
||||
|
||||
AEQRX_REG_BANK_0 = 0xd0,
|
||||
AEQ_CONTROL1 = 0x81,
|
||||
@@ -116,6 +120,7 @@ enum sata_phy_regs {
|
||||
AEQ_FRC_EQ = 0x83,
|
||||
AEQ_FRC_EQ_FORCE = BIT(0),
|
||||
AEQ_FRC_EQ_FORCE_VAL = BIT(1),
|
||||
AEQ_RFZ_FRC_VAL = BIT(8),
|
||||
AEQRX_REG_BANK_1 = 0xe0,
|
||||
AEQRX_SLCAL0_CTRL0 = 0x82,
|
||||
AEQRX_SLCAL1_CTRL0 = 0x86,
|
||||
@@ -152,7 +157,28 @@ enum sata_phy_regs {
|
||||
TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK = 0x3ff,
|
||||
|
||||
RXPMD_REG_BANK = 0x1c0,
|
||||
RXPMD_RX_CDR_CONTROL1 = 0x81,
|
||||
RXPMD_RX_PPM_VAL_MASK = 0x1ff,
|
||||
RXPMD_RXPMD_EN_FRC = BIT(12),
|
||||
RXPMD_RXPMD_EN_FRC_VAL = BIT(13),
|
||||
RXPMD_RX_CDR_CDR_PROP_BW = 0x82,
|
||||
RXPMD_G_CDR_PROP_BW_MASK = 0x7,
|
||||
RXPMD_G1_CDR_PROP_BW_SHIFT = 0,
|
||||
RXPMD_G2_CDR_PROP_BW_SHIFT = 3,
|
||||
RXPMD_G3_CDR_PROB_BW_SHIFT = 6,
|
||||
RXPMD_RX_CDR_CDR_ACQ_INTEG_BW = 0x83,
|
||||
RXPMD_G_CDR_ACQ_INT_BW_MASK = 0x7,
|
||||
RXPMD_G1_CDR_ACQ_INT_BW_SHIFT = 0,
|
||||
RXPMD_G2_CDR_ACQ_INT_BW_SHIFT = 3,
|
||||
RXPMD_G3_CDR_ACQ_INT_BW_SHIFT = 6,
|
||||
RXPMD_RX_CDR_CDR_LOCK_INTEG_BW = 0x84,
|
||||
RXPMD_G_CDR_LOCK_INT_BW_MASK = 0x7,
|
||||
RXPMD_G1_CDR_LOCK_INT_BW_SHIFT = 0,
|
||||
RXPMD_G2_CDR_LOCK_INT_BW_SHIFT = 3,
|
||||
RXPMD_G3_CDR_LOCK_INT_BW_SHIFT = 6,
|
||||
RXPMD_RX_FREQ_MON_CONTROL1 = 0x87,
|
||||
RXPMD_MON_CORRECT_EN = BIT(8),
|
||||
RXPMD_MON_MARGIN_VAL_MASK = 0xff,
|
||||
};
|
||||
|
||||
enum sata_phy_ctrl_regs {
|
||||
@@ -166,6 +192,7 @@ static inline void __iomem *brcm_sata_pcb_base(struct brcm_sata_port *port)
|
||||
u32 size = 0;
|
||||
|
||||
switch (priv->version) {
|
||||
case BRCM_SATA_PHY_STB_16NM:
|
||||
case BRCM_SATA_PHY_STB_28NM:
|
||||
case BRCM_SATA_PHY_IPROC_NS2:
|
||||
case BRCM_SATA_PHY_DSL_28NM:
|
||||
@@ -287,6 +314,94 @@ static int brcm_stb_sata_init(struct brcm_sata_port *port)
|
||||
return brcm_stb_sata_rxaeq_init(port);
|
||||
}
|
||||
|
||||
static int brcm_stb_sata_16nm_ssc_init(struct brcm_sata_port *port)
|
||||
{
|
||||
void __iomem *base = brcm_sata_pcb_base(port);
|
||||
u32 tmp, value;
|
||||
|
||||
/* Reduce CP tail current to 1/16th of its default value */
|
||||
brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL6, 0, 0x141);
|
||||
|
||||
/* Turn off CP tail current boost */
|
||||
brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL8, 0, 0xc006);
|
||||
|
||||
/* Set a specific AEQ equalizer value */
|
||||
tmp = AEQ_FRC_EQ_FORCE_VAL | AEQ_FRC_EQ_FORCE;
|
||||
brcm_sata_phy_wr(base, AEQRX_REG_BANK_0, AEQ_FRC_EQ,
|
||||
~(tmp | AEQ_RFZ_FRC_VAL |
|
||||
AEQ_FRC_EQ_VAL_MASK << AEQ_FRC_EQ_VAL_SHIFT),
|
||||
tmp | 32 << AEQ_FRC_EQ_VAL_SHIFT);
|
||||
|
||||
/* Set RX PPM val center frequency */
|
||||
if (port->ssc_en)
|
||||
value = 0x52;
|
||||
else
|
||||
value = 0;
|
||||
brcm_sata_phy_wr(base, RXPMD_REG_BANK, RXPMD_RX_CDR_CONTROL1,
|
||||
~RXPMD_RX_PPM_VAL_MASK, value);
|
||||
|
||||
/* Set proportional loop bandwith Gen1/2/3 */
|
||||
tmp = RXPMD_G_CDR_PROP_BW_MASK << RXPMD_G1_CDR_PROP_BW_SHIFT |
|
||||
RXPMD_G_CDR_PROP_BW_MASK << RXPMD_G2_CDR_PROP_BW_SHIFT |
|
||||
RXPMD_G_CDR_PROP_BW_MASK << RXPMD_G3_CDR_PROB_BW_SHIFT;
|
||||
if (port->ssc_en)
|
||||
value = 2 << RXPMD_G1_CDR_PROP_BW_SHIFT |
|
||||
2 << RXPMD_G2_CDR_PROP_BW_SHIFT |
|
||||
2 << RXPMD_G3_CDR_PROB_BW_SHIFT;
|
||||
else
|
||||
value = 1 << RXPMD_G1_CDR_PROP_BW_SHIFT |
|
||||
1 << RXPMD_G2_CDR_PROP_BW_SHIFT |
|
||||
1 << RXPMD_G3_CDR_PROB_BW_SHIFT;
|
||||
brcm_sata_phy_wr(base, RXPMD_REG_BANK, RXPMD_RX_CDR_CDR_PROP_BW, ~tmp,
|
||||
value);
|
||||
|
||||
/* Set CDR integral loop acquisition bandwidth for Gen1/2/3 */
|
||||
tmp = RXPMD_G_CDR_ACQ_INT_BW_MASK << RXPMD_G1_CDR_ACQ_INT_BW_SHIFT |
|
||||
RXPMD_G_CDR_ACQ_INT_BW_MASK << RXPMD_G2_CDR_ACQ_INT_BW_SHIFT |
|
||||
RXPMD_G_CDR_ACQ_INT_BW_MASK << RXPMD_G3_CDR_ACQ_INT_BW_SHIFT;
|
||||
if (port->ssc_en)
|
||||
value = 1 << RXPMD_G1_CDR_ACQ_INT_BW_SHIFT |
|
||||
1 << RXPMD_G2_CDR_ACQ_INT_BW_SHIFT |
|
||||
1 << RXPMD_G3_CDR_ACQ_INT_BW_SHIFT;
|
||||
else
|
||||
value = 0;
|
||||
brcm_sata_phy_wr(base, RXPMD_REG_BANK, RXPMD_RX_CDR_CDR_ACQ_INTEG_BW,
|
||||
~tmp, value);
|
||||
|
||||
/* Set CDR integral loop locking bandwidth to 1 for Gen 1/2/3 */
|
||||
tmp = RXPMD_G_CDR_LOCK_INT_BW_MASK << RXPMD_G1_CDR_LOCK_INT_BW_SHIFT |
|
||||
RXPMD_G_CDR_LOCK_INT_BW_MASK << RXPMD_G2_CDR_LOCK_INT_BW_SHIFT |
|
||||
RXPMD_G_CDR_LOCK_INT_BW_MASK << RXPMD_G3_CDR_LOCK_INT_BW_SHIFT;
|
||||
if (port->ssc_en)
|
||||
value = 1 << RXPMD_G1_CDR_LOCK_INT_BW_SHIFT |
|
||||
1 << RXPMD_G2_CDR_LOCK_INT_BW_SHIFT |
|
||||
1 << RXPMD_G3_CDR_LOCK_INT_BW_SHIFT;
|
||||
else
|
||||
value = 0;
|
||||
brcm_sata_phy_wr(base, RXPMD_REG_BANK, RXPMD_RX_CDR_CDR_LOCK_INTEG_BW,
|
||||
~tmp, value);
|
||||
|
||||
/* Set no guard band and clamp CDR */
|
||||
tmp = RXPMD_MON_CORRECT_EN | RXPMD_MON_MARGIN_VAL_MASK;
|
||||
if (port->ssc_en)
|
||||
value = 0x51;
|
||||
else
|
||||
value = 0;
|
||||
brcm_sata_phy_wr(base, RXPMD_REG_BANK, RXPMD_RX_FREQ_MON_CONTROL1,
|
||||
~tmp, RXPMD_MON_CORRECT_EN | value);
|
||||
|
||||
/* Turn on/off SSC */
|
||||
brcm_sata_phy_wr(base, TX_REG_BANK, TX_ACTRL5, ~TX_ACTRL5_SSC_EN,
|
||||
port->ssc_en ? TX_ACTRL5_SSC_EN : 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int brcm_stb_sata_16nm_init(struct brcm_sata_port *port)
|
||||
{
|
||||
return brcm_stb_sata_16nm_ssc_init(port);
|
||||
}
|
||||
|
||||
/* NS2 SATA PLL1 defaults were characterized by H/W group */
|
||||
#define NS2_PLL1_ACTRL2_MAGIC 0x1df8
|
||||
#define NS2_PLL1_ACTRL3_MAGIC 0x2b00
|
||||
@@ -544,6 +659,9 @@ static int brcm_sata_phy_init(struct phy *phy)
|
||||
struct brcm_sata_port *port = phy_get_drvdata(phy);
|
||||
|
||||
switch (port->phy_priv->version) {
|
||||
case BRCM_SATA_PHY_STB_16NM:
|
||||
rc = brcm_stb_sata_16nm_init(port);
|
||||
break;
|
||||
case BRCM_SATA_PHY_STB_28NM:
|
||||
case BRCM_SATA_PHY_STB_40NM:
|
||||
rc = brcm_stb_sata_init(port);
|
||||
@@ -601,6 +719,8 @@ static const struct phy_ops phy_ops = {
|
||||
};
|
||||
|
||||
static const struct of_device_id brcm_sata_phy_of_match[] = {
|
||||
{ .compatible = "brcm,bcm7216-sata-phy",
|
||||
.data = (void *)BRCM_SATA_PHY_STB_16NM },
|
||||
{ .compatible = "brcm,bcm7445-sata-phy",
|
||||
.data = (void *)BRCM_SATA_PHY_STB_28NM },
|
||||
{ .compatible = "brcm,bcm7425-sata-phy",
|
||||
|
414
drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c
Normal file
414
drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c
Normal file
@@ -0,0 +1,414 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2018, Broadcom */
|
||||
|
||||
/*
|
||||
* This module contains USB PHY initialization for power up and S3 resume
|
||||
* for newer Synopsys based USB hardware first used on the bcm7216.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <linux/soc/brcmstb/brcmstb.h>
|
||||
#include "phy-brcm-usb-init.h"
|
||||
|
||||
#define PHY_LOCK_TIMEOUT_MS 200
|
||||
|
||||
/* Register definitions for syscon piarbctl registers */
|
||||
#define PIARBCTL_CAM 0x00
|
||||
#define PIARBCTL_SPLITTER 0x04
|
||||
#define PIARBCTL_MISC 0x08
|
||||
#define PIARBCTL_MISC_SECURE_MASK 0x80000000
|
||||
#define PIARBCTL_MISC_USB_SELECT_MASK 0x40000000
|
||||
#define PIARBCTL_MISC_USB_4G_SDRAM_MASK 0x20000000
|
||||
#define PIARBCTL_MISC_USB_PRIORITY_MASK 0x000f0000
|
||||
#define PIARBCTL_MISC_USB_MEM_PAGE_MASK 0x0000f000
|
||||
#define PIARBCTL_MISC_CAM1_MEM_PAGE_MASK 0x00000f00
|
||||
#define PIARBCTL_MISC_CAM0_MEM_PAGE_MASK 0x000000f0
|
||||
#define PIARBCTL_MISC_SATA_PRIORITY_MASK 0x0000000f
|
||||
|
||||
#define PIARBCTL_MISC_USB_ONLY_MASK \
|
||||
(PIARBCTL_MISC_USB_SELECT_MASK | \
|
||||
PIARBCTL_MISC_USB_4G_SDRAM_MASK | \
|
||||
PIARBCTL_MISC_USB_PRIORITY_MASK | \
|
||||
PIARBCTL_MISC_USB_MEM_PAGE_MASK)
|
||||
|
||||
/* Register definitions for the USB CTRL block */
|
||||
#define USB_CTRL_SETUP 0x00
|
||||
#define USB_CTRL_SETUP_STRAP_IPP_SEL_MASK 0x02000000
|
||||
#define USB_CTRL_SETUP_SCB2_EN_MASK 0x00008000
|
||||
#define USB_CTRL_SETUP_tca_drv_sel_MASK 0x01000000
|
||||
#define USB_CTRL_SETUP_SCB1_EN_MASK 0x00004000
|
||||
#define USB_CTRL_SETUP_SOFT_SHUTDOWN_MASK 0x00000200
|
||||
#define USB_CTRL_SETUP_IPP_MASK 0x00000020
|
||||
#define USB_CTRL_SETUP_IOC_MASK 0x00000010
|
||||
#define USB_CTRL_USB_PM 0x04
|
||||
#define USB_CTRL_USB_PM_USB_PWRDN_MASK 0x80000000
|
||||
#define USB_CTRL_USB_PM_SOFT_RESET_MASK 0x40000000
|
||||
#define USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK 0x00800000
|
||||
#define USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK 0x00400000
|
||||
#define USB_CTRL_USB_PM_STATUS 0x08
|
||||
#define USB_CTRL_USB_DEVICE_CTL1 0x10
|
||||
#define USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK 0x00000003
|
||||
#define USB_CTRL_TEST_PORT_CTL 0x30
|
||||
#define USB_CTRL_TEST_PORT_CTL_TPOUT_SEL_MASK 0x000000ff
|
||||
#define USB_CTRL_TEST_PORT_CTL_TPOUT_SEL_PME_GEN_MASK 0x0000002e
|
||||
#define USB_CTRL_TP_DIAG1 0x34
|
||||
#define USB_CTLR_TP_DIAG1_wake_MASK 0x00000002
|
||||
#define USB_CTRL_CTLR_CSHCR 0x50
|
||||
#define USB_CTRL_CTLR_CSHCR_ctl_pme_en_MASK 0x00040000
|
||||
|
||||
/* Register definitions for the USB_PHY block in 7211b0 */
|
||||
#define USB_PHY_PLL_CTL 0x00
|
||||
#define USB_PHY_PLL_CTL_PLL_RESETB_MASK 0x40000000
|
||||
#define USB_PHY_PLL_LDO_CTL 0x08
|
||||
#define USB_PHY_PLL_LDO_CTL_AFE_CORERDY_MASK 0x00000004
|
||||
#define USB_PHY_PLL_LDO_CTL_AFE_LDO_PWRDWNB_MASK 0x00000002
|
||||
#define USB_PHY_PLL_LDO_CTL_AFE_BG_PWRDWNB_MASK 0x00000001
|
||||
#define USB_PHY_UTMI_CTL_1 0x04
|
||||
#define USB_PHY_UTMI_CTL_1_POWER_UP_FSM_EN_MASK 0x00000800
|
||||
#define USB_PHY_UTMI_CTL_1_PHY_MODE_MASK 0x0000000c
|
||||
#define USB_PHY_UTMI_CTL_1_PHY_MODE_SHIFT 2
|
||||
#define USB_PHY_IDDQ 0x1c
|
||||
#define USB_PHY_IDDQ_phy_iddq_MASK 0x00000001
|
||||
#define USB_PHY_STATUS 0x20
|
||||
#define USB_PHY_STATUS_pll_lock_MASK 0x00000001
|
||||
|
||||
/* Register definitions for the MDIO registers in the DWC2 block of
|
||||
* the 7211b0.
|
||||
* NOTE: The PHY's MDIO registers are only accessible through the
|
||||
* legacy DesignWare USB controller even though it's not being used.
|
||||
*/
|
||||
#define USB_GMDIOCSR 0
|
||||
#define USB_GMDIOGEN 4
|
||||
|
||||
/* Register definitions for the BDC EC block in 7211b0 */
|
||||
#define BDC_EC_AXIRDA 0x0c
|
||||
#define BDC_EC_AXIRDA_RTS_MASK 0xf0000000
|
||||
#define BDC_EC_AXIRDA_RTS_SHIFT 28
|
||||
|
||||
|
||||
static void usb_mdio_write_7211b0(struct brcm_usb_init_params *params,
|
||||
uint8_t addr, uint16_t data)
|
||||
{
|
||||
void __iomem *usb_mdio = params->regs[BRCM_REGS_USB_MDIO];
|
||||
|
||||
addr &= 0x1f; /* 5-bit address */
|
||||
brcm_usb_writel(0xffffffff, usb_mdio + USB_GMDIOGEN);
|
||||
while (brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & (1<<31))
|
||||
;
|
||||
brcm_usb_writel(0x59020000 | (addr << 18) | data,
|
||||
usb_mdio + USB_GMDIOGEN);
|
||||
while (brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & (1<<31))
|
||||
;
|
||||
brcm_usb_writel(0x00000000, usb_mdio + USB_GMDIOGEN);
|
||||
while (brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & (1<<31))
|
||||
;
|
||||
}
|
||||
|
||||
static uint16_t __maybe_unused usb_mdio_read_7211b0(
|
||||
struct brcm_usb_init_params *params, uint8_t addr)
|
||||
{
|
||||
void __iomem *usb_mdio = params->regs[BRCM_REGS_USB_MDIO];
|
||||
|
||||
addr &= 0x1f; /* 5-bit address */
|
||||
brcm_usb_writel(0xffffffff, usb_mdio + USB_GMDIOGEN);
|
||||
while (brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & (1<<31))
|
||||
;
|
||||
brcm_usb_writel(0x69020000 | (addr << 18), usb_mdio + USB_GMDIOGEN);
|
||||
while (brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & (1<<31))
|
||||
;
|
||||
brcm_usb_writel(0x00000000, usb_mdio + USB_GMDIOGEN);
|
||||
while (brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & (1<<31))
|
||||
;
|
||||
return brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & 0xffff;
|
||||
}
|
||||
|
||||
static void usb2_eye_fix_7211b0(struct brcm_usb_init_params *params)
|
||||
{
|
||||
/* select bank */
|
||||
usb_mdio_write_7211b0(params, 0x1f, 0x80a0);
|
||||
|
||||
/* Set the eye */
|
||||
usb_mdio_write_7211b0(params, 0x0a, 0xc6a0);
|
||||
}
|
||||
|
||||
static void xhci_soft_reset(struct brcm_usb_init_params *params,
|
||||
int on_off)
|
||||
{
|
||||
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
|
||||
|
||||
/* Assert reset */
|
||||
if (on_off)
|
||||
USB_CTRL_UNSET(ctrl, USB_PM, XHC_SOFT_RESETB);
|
||||
/* De-assert reset */
|
||||
else
|
||||
USB_CTRL_SET(ctrl, USB_PM, XHC_SOFT_RESETB);
|
||||
}
|
||||
|
||||
static void usb_init_ipp(struct brcm_usb_init_params *params)
|
||||
{
|
||||
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
|
||||
u32 reg;
|
||||
u32 orig_reg;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
orig_reg = reg = brcm_usb_readl(USB_CTRL_REG(ctrl, SETUP));
|
||||
if (params->ipp != 2)
|
||||
/* override ipp strap pin (if it exits) */
|
||||
reg &= ~(USB_CTRL_MASK(SETUP, STRAP_IPP_SEL));
|
||||
|
||||
/* Override the default OC and PP polarity */
|
||||
reg &= ~(USB_CTRL_MASK(SETUP, IPP) | USB_CTRL_MASK(SETUP, IOC));
|
||||
if (params->ioc)
|
||||
reg |= USB_CTRL_MASK(SETUP, IOC);
|
||||
if (params->ipp == 1)
|
||||
reg |= USB_CTRL_MASK(SETUP, IPP);
|
||||
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, SETUP));
|
||||
|
||||
/*
|
||||
* If we're changing IPP, make sure power is off long enough
|
||||
* to turn off any connected devices.
|
||||
*/
|
||||
if ((reg ^ orig_reg) & USB_CTRL_MASK(SETUP, IPP))
|
||||
msleep(50);
|
||||
}
|
||||
|
||||
static void syscon_piarbctl_init(struct regmap *rmap)
|
||||
{
|
||||
/* Switch from legacy USB OTG controller to new STB USB controller */
|
||||
regmap_update_bits(rmap, PIARBCTL_MISC, PIARBCTL_MISC_USB_ONLY_MASK,
|
||||
PIARBCTL_MISC_USB_SELECT_MASK |
|
||||
PIARBCTL_MISC_USB_4G_SDRAM_MASK);
|
||||
}
|
||||
|
||||
static void usb_init_common(struct brcm_usb_init_params *params)
|
||||
{
|
||||
u32 reg;
|
||||
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
USB_CTRL_UNSET(ctrl, USB_PM, USB_PWRDN);
|
||||
/* 1 millisecond - for USB clocks to settle down */
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
if (USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE)) {
|
||||
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
|
||||
reg &= ~USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE);
|
||||
reg |= params->mode;
|
||||
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
|
||||
}
|
||||
switch (params->mode) {
|
||||
case USB_CTLR_MODE_HOST:
|
||||
USB_CTRL_UNSET(ctrl, USB_PM, BDC_SOFT_RESETB);
|
||||
break;
|
||||
default:
|
||||
USB_CTRL_UNSET(ctrl, USB_PM, BDC_SOFT_RESETB);
|
||||
USB_CTRL_SET(ctrl, USB_PM, BDC_SOFT_RESETB);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_wake_enable_7211b0(struct brcm_usb_init_params *params,
|
||||
bool enable)
|
||||
{
|
||||
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
|
||||
|
||||
if (enable)
|
||||
USB_CTRL_SET(ctrl, CTLR_CSHCR, ctl_pme_en);
|
||||
else
|
||||
USB_CTRL_UNSET(ctrl, CTLR_CSHCR, ctl_pme_en);
|
||||
}
|
||||
|
||||
static void usb_init_common_7211b0(struct brcm_usb_init_params *params)
|
||||
{
|
||||
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
|
||||
void __iomem *usb_phy = params->regs[BRCM_REGS_USB_PHY];
|
||||
void __iomem *bdc_ec = params->regs[BRCM_REGS_BDC_EC];
|
||||
int timeout_ms = PHY_LOCK_TIMEOUT_MS;
|
||||
u32 reg;
|
||||
|
||||
if (params->syscon_piarbctl)
|
||||
syscon_piarbctl_init(params->syscon_piarbctl);
|
||||
|
||||
USB_CTRL_UNSET(ctrl, USB_PM, USB_PWRDN);
|
||||
|
||||
usb_wake_enable_7211b0(params, false);
|
||||
if (!params->wake_enabled) {
|
||||
|
||||
/* undo possible suspend settings */
|
||||
brcm_usb_writel(0, usb_phy + USB_PHY_IDDQ);
|
||||
reg = brcm_usb_readl(usb_phy + USB_PHY_PLL_CTL);
|
||||
reg |= USB_PHY_PLL_CTL_PLL_RESETB_MASK;
|
||||
brcm_usb_writel(reg, usb_phy + USB_PHY_PLL_CTL);
|
||||
|
||||
/* temporarily enable FSM so PHY comes up properly */
|
||||
reg = brcm_usb_readl(usb_phy + USB_PHY_UTMI_CTL_1);
|
||||
reg |= USB_PHY_UTMI_CTL_1_POWER_UP_FSM_EN_MASK;
|
||||
brcm_usb_writel(reg, usb_phy + USB_PHY_UTMI_CTL_1);
|
||||
}
|
||||
|
||||
/* Init the PHY */
|
||||
reg = USB_PHY_PLL_LDO_CTL_AFE_CORERDY_MASK |
|
||||
USB_PHY_PLL_LDO_CTL_AFE_LDO_PWRDWNB_MASK |
|
||||
USB_PHY_PLL_LDO_CTL_AFE_BG_PWRDWNB_MASK;
|
||||
brcm_usb_writel(reg, usb_phy + USB_PHY_PLL_LDO_CTL);
|
||||
|
||||
/* wait for lock */
|
||||
while (timeout_ms-- > 0) {
|
||||
reg = brcm_usb_readl(usb_phy + USB_PHY_STATUS);
|
||||
if (reg & USB_PHY_STATUS_pll_lock_MASK)
|
||||
break;
|
||||
usleep_range(1000, 2000);
|
||||
}
|
||||
|
||||
/* Set the PHY_MODE */
|
||||
reg = brcm_usb_readl(usb_phy + USB_PHY_UTMI_CTL_1);
|
||||
reg &= ~USB_PHY_UTMI_CTL_1_PHY_MODE_MASK;
|
||||
reg |= params->mode << USB_PHY_UTMI_CTL_1_PHY_MODE_SHIFT;
|
||||
brcm_usb_writel(reg, usb_phy + USB_PHY_UTMI_CTL_1);
|
||||
|
||||
/* Fix the incorrect default */
|
||||
reg = brcm_usb_readl(ctrl + USB_CTRL_SETUP);
|
||||
reg &= ~USB_CTRL_SETUP_tca_drv_sel_MASK;
|
||||
brcm_usb_writel(reg, ctrl + USB_CTRL_SETUP);
|
||||
|
||||
usb_init_common(params);
|
||||
|
||||
/*
|
||||
* The BDC controller will get occasional failures with
|
||||
* the default "Read Transaction Size" of 6 (1024 bytes).
|
||||
* Set it to 4 (256 bytes).
|
||||
*/
|
||||
if ((params->mode != USB_CTLR_MODE_HOST) && bdc_ec) {
|
||||
reg = brcm_usb_readl(bdc_ec + BDC_EC_AXIRDA);
|
||||
reg &= ~BDC_EC_AXIRDA_RTS_MASK;
|
||||
reg |= (0x4 << BDC_EC_AXIRDA_RTS_SHIFT);
|
||||
brcm_usb_writel(reg, bdc_ec + BDC_EC_AXIRDA);
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable FSM, otherwise the PHY will auto suspend when no
|
||||
* device is connected and will be reset on resume.
|
||||
*/
|
||||
reg = brcm_usb_readl(usb_phy + USB_PHY_UTMI_CTL_1);
|
||||
reg &= ~USB_PHY_UTMI_CTL_1_POWER_UP_FSM_EN_MASK;
|
||||
brcm_usb_writel(reg, usb_phy + USB_PHY_UTMI_CTL_1);
|
||||
|
||||
usb2_eye_fix_7211b0(params);
|
||||
}
|
||||
|
||||
static void usb_init_xhci(struct brcm_usb_init_params *params)
|
||||
{
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
xhci_soft_reset(params, 0);
|
||||
}
|
||||
|
||||
static void usb_uninit_common(struct brcm_usb_init_params *params)
|
||||
{
|
||||
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
USB_CTRL_SET(ctrl, USB_PM, USB_PWRDN);
|
||||
|
||||
}
|
||||
|
||||
static void usb_uninit_common_7211b0(struct brcm_usb_init_params *params)
|
||||
{
|
||||
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
|
||||
void __iomem *usb_phy = params->regs[BRCM_REGS_USB_PHY];
|
||||
u32 reg;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
if (params->wake_enabled) {
|
||||
USB_CTRL_SET(ctrl, TEST_PORT_CTL, TPOUT_SEL_PME_GEN);
|
||||
usb_wake_enable_7211b0(params, true);
|
||||
} else {
|
||||
USB_CTRL_SET(ctrl, USB_PM, USB_PWRDN);
|
||||
brcm_usb_writel(0, usb_phy + USB_PHY_PLL_LDO_CTL);
|
||||
reg = brcm_usb_readl(usb_phy + USB_PHY_PLL_CTL);
|
||||
reg &= ~USB_PHY_PLL_CTL_PLL_RESETB_MASK;
|
||||
brcm_usb_writel(reg, usb_phy + USB_PHY_PLL_CTL);
|
||||
brcm_usb_writel(USB_PHY_IDDQ_phy_iddq_MASK,
|
||||
usb_phy + USB_PHY_IDDQ);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void usb_uninit_xhci(struct brcm_usb_init_params *params)
|
||||
{
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
if (!params->wake_enabled)
|
||||
xhci_soft_reset(params, 1);
|
||||
}
|
||||
|
||||
static int usb_get_dual_select(struct brcm_usb_init_params *params)
|
||||
{
|
||||
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
|
||||
u32 reg = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
|
||||
reg &= USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE);
|
||||
return reg;
|
||||
}
|
||||
|
||||
static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode)
|
||||
{
|
||||
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
|
||||
u32 reg;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
|
||||
reg &= ~USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE);
|
||||
reg |= mode;
|
||||
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
|
||||
}
|
||||
|
||||
static const struct brcm_usb_init_ops bcm7216_ops = {
|
||||
.init_ipp = usb_init_ipp,
|
||||
.init_common = usb_init_common,
|
||||
.init_xhci = usb_init_xhci,
|
||||
.uninit_common = usb_uninit_common,
|
||||
.uninit_xhci = usb_uninit_xhci,
|
||||
.get_dual_select = usb_get_dual_select,
|
||||
.set_dual_select = usb_set_dual_select,
|
||||
};
|
||||
|
||||
static const struct brcm_usb_init_ops bcm7211b0_ops = {
|
||||
.init_ipp = usb_init_ipp,
|
||||
.init_common = usb_init_common_7211b0,
|
||||
.init_xhci = usb_init_xhci,
|
||||
.uninit_common = usb_uninit_common_7211b0,
|
||||
.uninit_xhci = usb_uninit_xhci,
|
||||
.get_dual_select = usb_get_dual_select,
|
||||
.set_dual_select = usb_set_dual_select,
|
||||
};
|
||||
|
||||
void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params)
|
||||
{
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
params->family_name = "7216";
|
||||
params->ops = &bcm7216_ops;
|
||||
}
|
||||
|
||||
void brcm_usb_dvr_init_7211b0(struct brcm_usb_init_params *params)
|
||||
{
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
params->family_name = "7211";
|
||||
params->ops = &bcm7211b0_ops;
|
||||
params->suspend_with_clocks = true;
|
||||
}
|
@@ -42,6 +42,7 @@
|
||||
#define USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK 0x80000000 /* option */
|
||||
#define USB_CTRL_EBRIDGE 0x0c
|
||||
#define USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK 0x00020000 /* option */
|
||||
#define USB_CTRL_EBRIDGE_EBR_SCB_SIZE_MASK 0x00000f80 /* option */
|
||||
#define USB_CTRL_OBRIDGE 0x10
|
||||
#define USB_CTRL_OBRIDGE_LS_KEEP_ALIVE_MASK 0x08000000
|
||||
#define USB_CTRL_MDIO 0x14
|
||||
@@ -57,6 +58,8 @@
|
||||
#define USB_CTRL_USB_PM_SOFT_RESET_MASK 0x40000000 /* option */
|
||||
#define USB_CTRL_USB_PM_USB20_HC_RESETB_MASK 0x30000000 /* option */
|
||||
#define USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK 0x00300000 /* option */
|
||||
#define USB_CTRL_USB_PM_RMTWKUP_EN_MASK 0x00000001
|
||||
#define USB_CTRL_USB_PM_STATUS 0x38
|
||||
#define USB_CTRL_USB30_CTL1 0x60
|
||||
#define USB_CTRL_USB30_CTL1_PHY3_PLL_SEQ_START_MASK 0x00000010
|
||||
#define USB_CTRL_USB30_CTL1_PHY3_RESETB_MASK 0x00010000
|
||||
@@ -126,10 +129,6 @@ enum {
|
||||
USB_CTRL_SELECTOR_COUNT,
|
||||
};
|
||||
|
||||
#define USB_CTRL_REG(base, reg) ((void __iomem *)base + USB_CTRL_##reg)
|
||||
#define USB_XHCI_EC_REG(base, reg) ((void __iomem *)base + USB_XHCI_EC_##reg)
|
||||
#define USB_CTRL_MASK(reg, field) \
|
||||
USB_CTRL_##reg##_##field##_MASK
|
||||
#define USB_CTRL_MASK_FAMILY(params, reg, field) \
|
||||
(params->usb_reg_bits_map[USB_CTRL_##reg##_##field##_SELECTOR])
|
||||
|
||||
@@ -140,13 +139,6 @@ enum {
|
||||
usb_ctrl_unset_family(params, USB_CTRL_##reg, \
|
||||
USB_CTRL_##reg##_##field##_SELECTOR)
|
||||
|
||||
#define USB_CTRL_SET(base, reg, field) \
|
||||
usb_ctrl_set(USB_CTRL_REG(base, reg), \
|
||||
USB_CTRL_##reg##_##field##_MASK)
|
||||
#define USB_CTRL_UNSET(base, reg, field) \
|
||||
usb_ctrl_unset(USB_CTRL_REG(base, reg), \
|
||||
USB_CTRL_##reg##_##field##_MASK)
|
||||
|
||||
#define MDIO_USB2 0
|
||||
#define MDIO_USB3 BIT(31)
|
||||
|
||||
@@ -176,6 +168,7 @@ static const struct id_to_type id_to_type_table[] = {
|
||||
{ 0x33900000, BRCM_FAMILY_3390A0 },
|
||||
{ 0x72500010, BRCM_FAMILY_7250B0 },
|
||||
{ 0x72600000, BRCM_FAMILY_7260A0 },
|
||||
{ 0x72550000, BRCM_FAMILY_7260A0 },
|
||||
{ 0x72680000, BRCM_FAMILY_7271A0 },
|
||||
{ 0x72710000, BRCM_FAMILY_7271A0 },
|
||||
{ 0x73640000, BRCM_FAMILY_7364A0 },
|
||||
@@ -401,26 +394,14 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = {
|
||||
},
|
||||
};
|
||||
|
||||
static inline u32 brcmusb_readl(void __iomem *addr)
|
||||
{
|
||||
return readl(addr);
|
||||
}
|
||||
|
||||
static inline void brcmusb_writel(u32 val, void __iomem *addr)
|
||||
{
|
||||
writel(val, addr);
|
||||
}
|
||||
|
||||
static inline
|
||||
void usb_ctrl_unset_family(struct brcm_usb_init_params *params,
|
||||
u32 reg_offset, u32 field)
|
||||
{
|
||||
u32 mask;
|
||||
void __iomem *reg;
|
||||
|
||||
mask = params->usb_reg_bits_map[field];
|
||||
reg = params->ctrl_regs + reg_offset;
|
||||
brcmusb_writel(brcmusb_readl(reg) & ~mask, reg);
|
||||
brcm_usb_ctrl_unset(params->regs[BRCM_REGS_CTRL] + reg_offset, mask);
|
||||
};
|
||||
|
||||
static inline
|
||||
@@ -428,45 +409,27 @@ void usb_ctrl_set_family(struct brcm_usb_init_params *params,
|
||||
u32 reg_offset, u32 field)
|
||||
{
|
||||
u32 mask;
|
||||
void __iomem *reg;
|
||||
|
||||
mask = params->usb_reg_bits_map[field];
|
||||
reg = params->ctrl_regs + reg_offset;
|
||||
brcmusb_writel(brcmusb_readl(reg) | mask, reg);
|
||||
brcm_usb_ctrl_set(params->regs[BRCM_REGS_CTRL] + reg_offset, mask);
|
||||
};
|
||||
|
||||
static inline void usb_ctrl_set(void __iomem *reg, u32 field)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
value = brcmusb_readl(reg);
|
||||
brcmusb_writel(value | field, reg);
|
||||
}
|
||||
|
||||
static inline void usb_ctrl_unset(void __iomem *reg, u32 field)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
value = brcmusb_readl(reg);
|
||||
brcmusb_writel(value & ~field, reg);
|
||||
}
|
||||
|
||||
static u32 brcmusb_usb_mdio_read(void __iomem *ctrl_base, u32 reg, int mode)
|
||||
{
|
||||
u32 data;
|
||||
|
||||
data = (reg << 16) | mode;
|
||||
brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
|
||||
brcm_usb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
|
||||
data |= (1 << 24);
|
||||
brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
|
||||
brcm_usb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
|
||||
data &= ~(1 << 24);
|
||||
/* wait for the 60MHz parallel to serial shifter */
|
||||
usleep_range(10, 20);
|
||||
brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
|
||||
brcm_usb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
|
||||
/* wait for the 60MHz parallel to serial shifter */
|
||||
usleep_range(10, 20);
|
||||
|
||||
return brcmusb_readl(USB_CTRL_REG(ctrl_base, MDIO2)) & 0xffff;
|
||||
return brcm_usb_readl(USB_CTRL_REG(ctrl_base, MDIO2)) & 0xffff;
|
||||
}
|
||||
|
||||
static void brcmusb_usb_mdio_write(void __iomem *ctrl_base, u32 reg,
|
||||
@@ -475,14 +438,14 @@ static void brcmusb_usb_mdio_write(void __iomem *ctrl_base, u32 reg,
|
||||
u32 data;
|
||||
|
||||
data = (reg << 16) | val | mode;
|
||||
brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
|
||||
brcm_usb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
|
||||
data |= (1 << 25);
|
||||
brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
|
||||
brcm_usb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
|
||||
data &= ~(1 << 25);
|
||||
|
||||
/* wait for the 60MHz parallel to serial shifter */
|
||||
usleep_range(10, 20);
|
||||
brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
|
||||
brcm_usb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
|
||||
/* wait for the 60MHz parallel to serial shifter */
|
||||
usleep_range(10, 20);
|
||||
}
|
||||
@@ -581,7 +544,7 @@ static void brcmusb_usb3_pll_54mhz(struct brcm_usb_init_params *params)
|
||||
{
|
||||
u32 ofs;
|
||||
int ii;
|
||||
void __iomem *ctrl_base = params->ctrl_regs;
|
||||
void __iomem *ctrl_base = params->regs[BRCM_REGS_CTRL];
|
||||
|
||||
/*
|
||||
* On newer B53 based SoC's, the reference clock for the
|
||||
@@ -662,7 +625,7 @@ static void brcmusb_usb3_ssc_enable(void __iomem *ctrl_base)
|
||||
|
||||
static void brcmusb_usb3_phy_workarounds(struct brcm_usb_init_params *params)
|
||||
{
|
||||
void __iomem *ctrl_base = params->ctrl_regs;
|
||||
void __iomem *ctrl_base = params->regs[BRCM_REGS_CTRL];
|
||||
|
||||
brcmusb_usb3_pll_fix(ctrl_base);
|
||||
brcmusb_usb3_pll_54mhz(params);
|
||||
@@ -704,21 +667,21 @@ static void brcmusb_memc_fix(struct brcm_usb_init_params *params)
|
||||
|
||||
static void brcmusb_usb3_otp_fix(struct brcm_usb_init_params *params)
|
||||
{
|
||||
void __iomem *xhci_ec_base = params->xhci_ec_regs;
|
||||
void __iomem *xhci_ec_base = params->regs[BRCM_REGS_XHCI_EC];
|
||||
u32 val;
|
||||
|
||||
if (params->family_id != 0x74371000 || !xhci_ec_base)
|
||||
return;
|
||||
brcmusb_writel(0xa20c, USB_XHCI_EC_REG(xhci_ec_base, IRAADR));
|
||||
val = brcmusb_readl(USB_XHCI_EC_REG(xhci_ec_base, IRADAT));
|
||||
brcm_usb_writel(0xa20c, USB_XHCI_EC_REG(xhci_ec_base, IRAADR));
|
||||
val = brcm_usb_readl(USB_XHCI_EC_REG(xhci_ec_base, IRADAT));
|
||||
|
||||
/* set cfg_pick_ss_lock */
|
||||
val |= (1 << 27);
|
||||
brcmusb_writel(val, USB_XHCI_EC_REG(xhci_ec_base, IRADAT));
|
||||
brcm_usb_writel(val, USB_XHCI_EC_REG(xhci_ec_base, IRADAT));
|
||||
|
||||
/* Reset USB 3.0 PHY for workaround to take effect */
|
||||
USB_CTRL_UNSET(params->ctrl_regs, USB30_CTL1, PHY3_RESETB);
|
||||
USB_CTRL_SET(params->ctrl_regs, USB30_CTL1, PHY3_RESETB);
|
||||
USB_CTRL_UNSET(params->regs[BRCM_REGS_CTRL], USB30_CTL1, PHY3_RESETB);
|
||||
USB_CTRL_SET(params->regs[BRCM_REGS_CTRL], USB30_CTL1, PHY3_RESETB);
|
||||
}
|
||||
|
||||
static void brcmusb_xhci_soft_reset(struct brcm_usb_init_params *params,
|
||||
@@ -747,7 +710,7 @@ static void brcmusb_xhci_soft_reset(struct brcm_usb_init_params *params,
|
||||
* - default chip/rev.
|
||||
* NOTE: The minor rev is always ignored.
|
||||
*/
|
||||
static enum brcm_family_type brcmusb_get_family_type(
|
||||
static enum brcm_family_type get_family_type(
|
||||
struct brcm_usb_init_params *params)
|
||||
{
|
||||
int last_type = -1;
|
||||
@@ -775,9 +738,9 @@ static enum brcm_family_type brcmusb_get_family_type(
|
||||
return last_type;
|
||||
}
|
||||
|
||||
void brcm_usb_init_ipp(struct brcm_usb_init_params *params)
|
||||
static void usb_init_ipp(struct brcm_usb_init_params *params)
|
||||
{
|
||||
void __iomem *ctrl = params->ctrl_regs;
|
||||
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
|
||||
u32 reg;
|
||||
u32 orig_reg;
|
||||
|
||||
@@ -791,7 +754,7 @@ void brcm_usb_init_ipp(struct brcm_usb_init_params *params)
|
||||
USB_CTRL_SET_FAMILY(params, USB30_CTL1, USB3_IPP);
|
||||
}
|
||||
|
||||
reg = brcmusb_readl(USB_CTRL_REG(ctrl, SETUP));
|
||||
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, SETUP));
|
||||
orig_reg = reg;
|
||||
if (USB_CTRL_MASK_FAMILY(params, SETUP, STRAP_CC_DRD_MODE_ENABLE_SEL))
|
||||
/* Never use the strap, it's going away. */
|
||||
@@ -799,8 +762,8 @@ void brcm_usb_init_ipp(struct brcm_usb_init_params *params)
|
||||
SETUP,
|
||||
STRAP_CC_DRD_MODE_ENABLE_SEL));
|
||||
if (USB_CTRL_MASK_FAMILY(params, SETUP, STRAP_IPP_SEL))
|
||||
/* override ipp strap pin (if it exits) */
|
||||
if (params->ipp != 2)
|
||||
/* override ipp strap pin (if it exits) */
|
||||
reg &= ~(USB_CTRL_MASK_FAMILY(params, SETUP,
|
||||
STRAP_IPP_SEL));
|
||||
|
||||
@@ -808,50 +771,38 @@ void brcm_usb_init_ipp(struct brcm_usb_init_params *params)
|
||||
reg &= ~(USB_CTRL_MASK(SETUP, IPP) | USB_CTRL_MASK(SETUP, IOC));
|
||||
if (params->ioc)
|
||||
reg |= USB_CTRL_MASK(SETUP, IOC);
|
||||
if (params->ipp == 1 && ((reg & USB_CTRL_MASK(SETUP, IPP)) == 0))
|
||||
if (params->ipp == 1)
|
||||
reg |= USB_CTRL_MASK(SETUP, IPP);
|
||||
brcmusb_writel(reg, USB_CTRL_REG(ctrl, SETUP));
|
||||
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, SETUP));
|
||||
|
||||
/*
|
||||
* If we're changing IPP, make sure power is off long enough
|
||||
* to turn off any connected devices.
|
||||
*/
|
||||
if (reg != orig_reg)
|
||||
if ((reg ^ orig_reg) & USB_CTRL_MASK(SETUP, IPP))
|
||||
msleep(50);
|
||||
}
|
||||
|
||||
int brcm_usb_init_get_dual_select(struct brcm_usb_init_params *params)
|
||||
static void usb_wake_enable(struct brcm_usb_init_params *params,
|
||||
bool enable)
|
||||
{
|
||||
void __iomem *ctrl = params->ctrl_regs;
|
||||
u32 reg = 0;
|
||||
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
|
||||
|
||||
if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) {
|
||||
reg = brcmusb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
|
||||
reg &= USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1,
|
||||
PORT_MODE);
|
||||
}
|
||||
return reg;
|
||||
if (enable)
|
||||
USB_CTRL_SET(ctrl, USB_PM, RMTWKUP_EN);
|
||||
else
|
||||
USB_CTRL_UNSET(ctrl, USB_PM, RMTWKUP_EN);
|
||||
}
|
||||
|
||||
void brcm_usb_init_set_dual_select(struct brcm_usb_init_params *params,
|
||||
int mode)
|
||||
{
|
||||
void __iomem *ctrl = params->ctrl_regs;
|
||||
u32 reg;
|
||||
|
||||
if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) {
|
||||
reg = brcmusb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
|
||||
reg &= ~USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1,
|
||||
PORT_MODE);
|
||||
reg |= mode;
|
||||
brcmusb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
|
||||
}
|
||||
}
|
||||
|
||||
void brcm_usb_init_common(struct brcm_usb_init_params *params)
|
||||
static void usb_init_common(struct brcm_usb_init_params *params)
|
||||
{
|
||||
u32 reg;
|
||||
void __iomem *ctrl = params->ctrl_regs;
|
||||
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
|
||||
|
||||
/* Clear any pending wake conditions */
|
||||
usb_wake_enable(params, false);
|
||||
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_PM_STATUS));
|
||||
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_PM_STATUS));
|
||||
|
||||
/* Take USB out of power down */
|
||||
if (USB_CTRL_MASK_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN)) {
|
||||
@@ -877,7 +828,7 @@ void brcm_usb_init_common(struct brcm_usb_init_params *params)
|
||||
/* Block auto PLL suspend by USB2 PHY (Sasi) */
|
||||
USB_CTRL_SET(ctrl, PLL_CTL, PLL_SUSPEND_EN);
|
||||
|
||||
reg = brcmusb_readl(USB_CTRL_REG(ctrl, SETUP));
|
||||
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, SETUP));
|
||||
if (params->selected_family == BRCM_FAMILY_7364A0)
|
||||
/* Suppress overcurrent indication from USB30 ports for A0 */
|
||||
reg |= USB_CTRL_MASK_FAMILY(params, SETUP, OC3_DISABLE);
|
||||
@@ -893,16 +844,16 @@ void brcm_usb_init_common(struct brcm_usb_init_params *params)
|
||||
reg |= USB_CTRL_MASK_FAMILY(params, SETUP, SCB1_EN);
|
||||
if (USB_CTRL_MASK_FAMILY(params, SETUP, SCB2_EN))
|
||||
reg |= USB_CTRL_MASK_FAMILY(params, SETUP, SCB2_EN);
|
||||
brcmusb_writel(reg, USB_CTRL_REG(ctrl, SETUP));
|
||||
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, SETUP));
|
||||
|
||||
brcmusb_memc_fix(params);
|
||||
|
||||
if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) {
|
||||
reg = brcmusb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
|
||||
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
|
||||
reg &= ~USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1,
|
||||
PORT_MODE);
|
||||
reg |= params->mode;
|
||||
brcmusb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
|
||||
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
|
||||
}
|
||||
if (USB_CTRL_MASK_FAMILY(params, USB_PM, BDC_SOFT_RESETB)) {
|
||||
switch (params->mode) {
|
||||
@@ -924,10 +875,10 @@ void brcm_usb_init_common(struct brcm_usb_init_params *params)
|
||||
}
|
||||
}
|
||||
|
||||
void brcm_usb_init_eohci(struct brcm_usb_init_params *params)
|
||||
static void usb_init_eohci(struct brcm_usb_init_params *params)
|
||||
{
|
||||
u32 reg;
|
||||
void __iomem *ctrl = params->ctrl_regs;
|
||||
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
|
||||
|
||||
if (USB_CTRL_MASK_FAMILY(params, USB_PM, USB20_HC_RESETB))
|
||||
USB_CTRL_SET_FAMILY(params, USB_PM, USB20_HC_RESETB);
|
||||
@@ -940,19 +891,30 @@ void brcm_usb_init_eohci(struct brcm_usb_init_params *params)
|
||||
USB_CTRL_SET(ctrl, EBRIDGE, ESTOP_SCB_REQ);
|
||||
|
||||
/* Setup the endian bits */
|
||||
reg = brcmusb_readl(USB_CTRL_REG(ctrl, SETUP));
|
||||
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, SETUP));
|
||||
reg &= ~USB_CTRL_SETUP_ENDIAN_BITS;
|
||||
reg |= USB_CTRL_MASK_FAMILY(params, SETUP, ENDIAN);
|
||||
brcmusb_writel(reg, USB_CTRL_REG(ctrl, SETUP));
|
||||
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, SETUP));
|
||||
|
||||
if (params->selected_family == BRCM_FAMILY_7271A0)
|
||||
/* Enable LS keep alive fix for certain keyboards */
|
||||
USB_CTRL_SET(ctrl, OBRIDGE, LS_KEEP_ALIVE);
|
||||
|
||||
if (params->family_id == 0x72550000) {
|
||||
/*
|
||||
* Make the burst size 512 bytes to fix a hardware bug
|
||||
* on the 7255a0. See HW7255-24.
|
||||
*/
|
||||
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, EBRIDGE));
|
||||
reg &= ~USB_CTRL_MASK(EBRIDGE, EBR_SCB_SIZE);
|
||||
reg |= 0x800;
|
||||
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, EBRIDGE));
|
||||
}
|
||||
}
|
||||
|
||||
void brcm_usb_init_xhci(struct brcm_usb_init_params *params)
|
||||
static void usb_init_xhci(struct brcm_usb_init_params *params)
|
||||
{
|
||||
void __iomem *ctrl = params->ctrl_regs;
|
||||
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
|
||||
|
||||
USB_CTRL_UNSET(ctrl, USB30_PCTL, PHY3_IDDQ_OVERRIDE);
|
||||
/* 1 millisecond - for USB clocks to settle down */
|
||||
@@ -978,34 +940,80 @@ void brcm_usb_init_xhci(struct brcm_usb_init_params *params)
|
||||
brcmusb_usb3_otp_fix(params);
|
||||
}
|
||||
|
||||
void brcm_usb_uninit_common(struct brcm_usb_init_params *params)
|
||||
static void usb_uninit_common(struct brcm_usb_init_params *params)
|
||||
{
|
||||
if (USB_CTRL_MASK_FAMILY(params, USB_PM, USB_PWRDN))
|
||||
USB_CTRL_SET_FAMILY(params, USB_PM, USB_PWRDN);
|
||||
|
||||
if (USB_CTRL_MASK_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN))
|
||||
USB_CTRL_SET_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN);
|
||||
if (params->wake_enabled)
|
||||
usb_wake_enable(params, true);
|
||||
}
|
||||
|
||||
void brcm_usb_uninit_eohci(struct brcm_usb_init_params *params)
|
||||
static void usb_uninit_eohci(struct brcm_usb_init_params *params)
|
||||
{
|
||||
if (USB_CTRL_MASK_FAMILY(params, USB_PM, USB20_HC_RESETB))
|
||||
USB_CTRL_UNSET_FAMILY(params, USB_PM, USB20_HC_RESETB);
|
||||
}
|
||||
|
||||
void brcm_usb_uninit_xhci(struct brcm_usb_init_params *params)
|
||||
static void usb_uninit_xhci(struct brcm_usb_init_params *params)
|
||||
{
|
||||
brcmusb_xhci_soft_reset(params, 1);
|
||||
USB_CTRL_SET(params->ctrl_regs, USB30_PCTL, PHY3_IDDQ_OVERRIDE);
|
||||
USB_CTRL_SET(params->regs[BRCM_REGS_CTRL], USB30_PCTL,
|
||||
PHY3_IDDQ_OVERRIDE);
|
||||
}
|
||||
|
||||
void brcm_usb_set_family_map(struct brcm_usb_init_params *params)
|
||||
static int usb_get_dual_select(struct brcm_usb_init_params *params)
|
||||
{
|
||||
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
|
||||
u32 reg = 0;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) {
|
||||
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
|
||||
reg &= USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1,
|
||||
PORT_MODE);
|
||||
}
|
||||
return reg;
|
||||
}
|
||||
|
||||
static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode)
|
||||
{
|
||||
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
|
||||
u32 reg;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) {
|
||||
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
|
||||
reg &= ~USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1,
|
||||
PORT_MODE);
|
||||
reg |= mode;
|
||||
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
|
||||
}
|
||||
}
|
||||
|
||||
static const struct brcm_usb_init_ops bcm7445_ops = {
|
||||
.init_ipp = usb_init_ipp,
|
||||
.init_common = usb_init_common,
|
||||
.init_eohci = usb_init_eohci,
|
||||
.init_xhci = usb_init_xhci,
|
||||
.uninit_common = usb_uninit_common,
|
||||
.uninit_eohci = usb_uninit_eohci,
|
||||
.uninit_xhci = usb_uninit_xhci,
|
||||
.get_dual_select = usb_get_dual_select,
|
||||
.set_dual_select = usb_set_dual_select,
|
||||
};
|
||||
|
||||
void brcm_usb_dvr_init_7445(struct brcm_usb_init_params *params)
|
||||
{
|
||||
int fam;
|
||||
|
||||
fam = brcmusb_get_family_type(params);
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
fam = get_family_type(params);
|
||||
params->selected_family = fam;
|
||||
params->usb_reg_bits_map =
|
||||
&usb_reg_bits_map_table[fam][0];
|
||||
params->family_name = family_names[fam];
|
||||
params->ops = &bcm7445_ops;
|
||||
}
|
||||
|
@@ -6,16 +6,50 @@
|
||||
#ifndef _USB_BRCM_COMMON_INIT_H
|
||||
#define _USB_BRCM_COMMON_INIT_H
|
||||
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define USB_CTLR_MODE_HOST 0
|
||||
#define USB_CTLR_MODE_DEVICE 1
|
||||
#define USB_CTLR_MODE_DRD 2
|
||||
#define USB_CTLR_MODE_TYPEC_PD 3
|
||||
|
||||
enum brcmusb_reg_sel {
|
||||
BRCM_REGS_CTRL = 0,
|
||||
BRCM_REGS_XHCI_EC,
|
||||
BRCM_REGS_XHCI_GBL,
|
||||
BRCM_REGS_USB_PHY,
|
||||
BRCM_REGS_USB_MDIO,
|
||||
BRCM_REGS_BDC_EC,
|
||||
BRCM_REGS_MAX
|
||||
};
|
||||
|
||||
#define USB_CTRL_REG(base, reg) ((void __iomem *)base + USB_CTRL_##reg)
|
||||
#define USB_XHCI_EC_REG(base, reg) ((void __iomem *)base + USB_XHCI_EC_##reg)
|
||||
#define USB_CTRL_MASK(reg, field) \
|
||||
USB_CTRL_##reg##_##field##_MASK
|
||||
#define USB_CTRL_SET(base, reg, field) \
|
||||
brcm_usb_ctrl_set(USB_CTRL_REG(base, reg), \
|
||||
USB_CTRL_##reg##_##field##_MASK)
|
||||
#define USB_CTRL_UNSET(base, reg, field) \
|
||||
brcm_usb_ctrl_unset(USB_CTRL_REG(base, reg), \
|
||||
USB_CTRL_##reg##_##field##_MASK)
|
||||
|
||||
struct brcm_usb_init_params;
|
||||
|
||||
struct brcm_usb_init_ops {
|
||||
void (*init_ipp)(struct brcm_usb_init_params *params);
|
||||
void (*init_common)(struct brcm_usb_init_params *params);
|
||||
void (*init_eohci)(struct brcm_usb_init_params *params);
|
||||
void (*init_xhci)(struct brcm_usb_init_params *params);
|
||||
void (*uninit_common)(struct brcm_usb_init_params *params);
|
||||
void (*uninit_eohci)(struct brcm_usb_init_params *params);
|
||||
void (*uninit_xhci)(struct brcm_usb_init_params *params);
|
||||
int (*get_dual_select)(struct brcm_usb_init_params *params);
|
||||
void (*set_dual_select)(struct brcm_usb_init_params *params, int mode);
|
||||
};
|
||||
|
||||
struct brcm_usb_init_params {
|
||||
void __iomem *ctrl_regs;
|
||||
void __iomem *xhci_ec_regs;
|
||||
void __iomem *regs[BRCM_REGS_MAX];
|
||||
int ioc;
|
||||
int ipp;
|
||||
int mode;
|
||||
@@ -24,19 +58,105 @@ struct brcm_usb_init_params {
|
||||
int selected_family;
|
||||
const char *family_name;
|
||||
const u32 *usb_reg_bits_map;
|
||||
const struct brcm_usb_init_ops *ops;
|
||||
struct regmap *syscon_piarbctl;
|
||||
bool wake_enabled;
|
||||
bool suspend_with_clocks;
|
||||
};
|
||||
|
||||
void brcm_usb_set_family_map(struct brcm_usb_init_params *params);
|
||||
int brcm_usb_init_get_dual_select(struct brcm_usb_init_params *params);
|
||||
void brcm_usb_init_set_dual_select(struct brcm_usb_init_params *params,
|
||||
int mode);
|
||||
void brcm_usb_dvr_init_7445(struct brcm_usb_init_params *params);
|
||||
void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params);
|
||||
void brcm_usb_dvr_init_7211b0(struct brcm_usb_init_params *params);
|
||||
|
||||
void brcm_usb_init_ipp(struct brcm_usb_init_params *ini);
|
||||
void brcm_usb_init_common(struct brcm_usb_init_params *ini);
|
||||
void brcm_usb_init_eohci(struct brcm_usb_init_params *ini);
|
||||
void brcm_usb_init_xhci(struct brcm_usb_init_params *ini);
|
||||
void brcm_usb_uninit_common(struct brcm_usb_init_params *ini);
|
||||
void brcm_usb_uninit_eohci(struct brcm_usb_init_params *ini);
|
||||
void brcm_usb_uninit_xhci(struct brcm_usb_init_params *ini);
|
||||
static inline u32 brcm_usb_readl(void __iomem *addr)
|
||||
{
|
||||
/*
|
||||
* MIPS endianness is configured by boot strap, which also reverses all
|
||||
* bus endianness (i.e., big-endian CPU + big endian bus ==> native
|
||||
* endian I/O).
|
||||
*
|
||||
* Other architectures (e.g., ARM) either do not support big endian, or
|
||||
* else leave I/O in little endian mode.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN))
|
||||
return __raw_readl(addr);
|
||||
else
|
||||
return readl_relaxed(addr);
|
||||
}
|
||||
|
||||
static inline void brcm_usb_writel(u32 val, void __iomem *addr)
|
||||
{
|
||||
/* See brcmnand_readl() comments */
|
||||
if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN))
|
||||
__raw_writel(val, addr);
|
||||
else
|
||||
writel_relaxed(val, addr);
|
||||
}
|
||||
|
||||
static inline void brcm_usb_ctrl_unset(void __iomem *reg, u32 mask)
|
||||
{
|
||||
brcm_usb_writel(brcm_usb_readl(reg) & ~(mask), reg);
|
||||
};
|
||||
|
||||
static inline void brcm_usb_ctrl_set(void __iomem *reg, u32 mask)
|
||||
{
|
||||
brcm_usb_writel(brcm_usb_readl(reg) | (mask), reg);
|
||||
};
|
||||
|
||||
static inline void brcm_usb_init_ipp(struct brcm_usb_init_params *ini)
|
||||
{
|
||||
if (ini->ops->init_ipp)
|
||||
ini->ops->init_ipp(ini);
|
||||
}
|
||||
|
||||
static inline void brcm_usb_init_common(struct brcm_usb_init_params *ini)
|
||||
{
|
||||
if (ini->ops->init_common)
|
||||
ini->ops->init_common(ini);
|
||||
}
|
||||
|
||||
static inline void brcm_usb_init_eohci(struct brcm_usb_init_params *ini)
|
||||
{
|
||||
if (ini->ops->init_eohci)
|
||||
ini->ops->init_eohci(ini);
|
||||
}
|
||||
|
||||
static inline void brcm_usb_init_xhci(struct brcm_usb_init_params *ini)
|
||||
{
|
||||
if (ini->ops->init_xhci)
|
||||
ini->ops->init_xhci(ini);
|
||||
}
|
||||
|
||||
static inline void brcm_usb_uninit_common(struct brcm_usb_init_params *ini)
|
||||
{
|
||||
if (ini->ops->uninit_common)
|
||||
ini->ops->uninit_common(ini);
|
||||
}
|
||||
|
||||
static inline void brcm_usb_uninit_eohci(struct brcm_usb_init_params *ini)
|
||||
{
|
||||
if (ini->ops->uninit_eohci)
|
||||
ini->ops->uninit_eohci(ini);
|
||||
}
|
||||
|
||||
static inline void brcm_usb_uninit_xhci(struct brcm_usb_init_params *ini)
|
||||
{
|
||||
if (ini->ops->uninit_xhci)
|
||||
ini->ops->uninit_xhci(ini);
|
||||
}
|
||||
|
||||
static inline int brcm_usb_get_dual_select(struct brcm_usb_init_params *ini)
|
||||
{
|
||||
if (ini->ops->get_dual_select)
|
||||
return ini->ops->get_dual_select(ini);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void brcm_usb_set_dual_select(struct brcm_usb_init_params *ini,
|
||||
int mode)
|
||||
{
|
||||
if (ini->ops->set_dual_select)
|
||||
ini->ops->set_dual_select(ini, mode);
|
||||
}
|
||||
|
||||
#endif /* _USB_BRCM_COMMON_INIT_H */
|
||||
|
@@ -16,6 +16,7 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/soc/brcmstb/brcmstb.h>
|
||||
#include <dt-bindings/phy/phy.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
|
||||
#include "phy-brcm-usb-init.h"
|
||||
|
||||
@@ -32,6 +33,12 @@ struct value_to_name_map {
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct match_chip_info {
|
||||
void *init_func;
|
||||
u8 required_regs[BRCM_REGS_MAX + 1];
|
||||
u8 optional_reg;
|
||||
};
|
||||
|
||||
static struct value_to_name_map brcm_dr_mode_to_name[] = {
|
||||
{ USB_CTLR_MODE_HOST, "host" },
|
||||
{ USB_CTLR_MODE_DEVICE, "peripheral" },
|
||||
@@ -57,11 +64,26 @@ struct brcm_usb_phy_data {
|
||||
bool has_xhci;
|
||||
struct clk *usb_20_clk;
|
||||
struct clk *usb_30_clk;
|
||||
struct clk *suspend_clk;
|
||||
struct mutex mutex; /* serialize phy init */
|
||||
int init_count;
|
||||
int wake_irq;
|
||||
struct brcm_usb_phy phys[BRCM_USB_PHY_ID_MAX];
|
||||
};
|
||||
|
||||
static s8 *node_reg_names[BRCM_REGS_MAX] = {
|
||||
"crtl", "xhci_ec", "xhci_gbl", "usb_phy", "usb_mdio", "bdc_ec"
|
||||
};
|
||||
|
||||
static irqreturn_t brcm_usb_phy_wake_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct phy *gphy = dev_id;
|
||||
|
||||
pm_wakeup_event(&gphy->dev, 0);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int brcm_usb_phy_init(struct phy *gphy)
|
||||
{
|
||||
struct brcm_usb_phy *phy = phy_get_drvdata(gphy);
|
||||
@@ -74,8 +96,9 @@ static int brcm_usb_phy_init(struct phy *gphy)
|
||||
*/
|
||||
mutex_lock(&priv->mutex);
|
||||
if (priv->init_count++ == 0) {
|
||||
clk_enable(priv->usb_20_clk);
|
||||
clk_enable(priv->usb_30_clk);
|
||||
clk_prepare_enable(priv->usb_20_clk);
|
||||
clk_prepare_enable(priv->usb_30_clk);
|
||||
clk_prepare_enable(priv->suspend_clk);
|
||||
brcm_usb_init_common(&priv->ini);
|
||||
}
|
||||
mutex_unlock(&priv->mutex);
|
||||
@@ -106,8 +129,9 @@ static int brcm_usb_phy_exit(struct phy *gphy)
|
||||
mutex_lock(&priv->mutex);
|
||||
if (--priv->init_count == 0) {
|
||||
brcm_usb_uninit_common(&priv->ini);
|
||||
clk_disable(priv->usb_20_clk);
|
||||
clk_disable(priv->usb_30_clk);
|
||||
clk_disable_unprepare(priv->usb_20_clk);
|
||||
clk_disable_unprepare(priv->usb_30_clk);
|
||||
clk_disable_unprepare(priv->suspend_clk);
|
||||
}
|
||||
mutex_unlock(&priv->mutex);
|
||||
phy->inited = false;
|
||||
@@ -194,7 +218,7 @@ static ssize_t dual_select_store(struct device *dev,
|
||||
res = name_to_value(&brcm_dual_mode_to_name[0],
|
||||
ARRAY_SIZE(brcm_dual_mode_to_name), buf, &value);
|
||||
if (!res) {
|
||||
brcm_usb_init_set_dual_select(&priv->ini, value);
|
||||
brcm_usb_set_dual_select(&priv->ini, value);
|
||||
res = len;
|
||||
}
|
||||
mutex_unlock(&sysfs_lock);
|
||||
@@ -209,7 +233,7 @@ static ssize_t dual_select_show(struct device *dev,
|
||||
int value;
|
||||
|
||||
mutex_lock(&sysfs_lock);
|
||||
value = brcm_usb_init_get_dual_select(&priv->ini);
|
||||
value = brcm_usb_get_dual_select(&priv->ini);
|
||||
mutex_unlock(&sysfs_lock);
|
||||
return sprintf(buf, "%s\n",
|
||||
value_to_name(&brcm_dual_mode_to_name[0],
|
||||
@@ -228,15 +252,106 @@ static const struct attribute_group brcm_usb_phy_group = {
|
||||
.attrs = brcm_usb_phy_attrs,
|
||||
};
|
||||
|
||||
static int brcm_usb_phy_dvr_init(struct device *dev,
|
||||
static struct match_chip_info chip_info_7216 = {
|
||||
.init_func = &brcm_usb_dvr_init_7216,
|
||||
.required_regs = {
|
||||
BRCM_REGS_CTRL,
|
||||
BRCM_REGS_XHCI_EC,
|
||||
BRCM_REGS_XHCI_GBL,
|
||||
-1,
|
||||
},
|
||||
};
|
||||
|
||||
static struct match_chip_info chip_info_7211b0 = {
|
||||
.init_func = &brcm_usb_dvr_init_7211b0,
|
||||
.required_regs = {
|
||||
BRCM_REGS_CTRL,
|
||||
BRCM_REGS_XHCI_EC,
|
||||
BRCM_REGS_XHCI_GBL,
|
||||
BRCM_REGS_USB_PHY,
|
||||
BRCM_REGS_USB_MDIO,
|
||||
-1,
|
||||
},
|
||||
.optional_reg = BRCM_REGS_BDC_EC,
|
||||
};
|
||||
|
||||
static struct match_chip_info chip_info_7445 = {
|
||||
.init_func = &brcm_usb_dvr_init_7445,
|
||||
.required_regs = {
|
||||
BRCM_REGS_CTRL,
|
||||
BRCM_REGS_XHCI_EC,
|
||||
-1,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct of_device_id brcm_usb_dt_ids[] = {
|
||||
{
|
||||
.compatible = "brcm,bcm7216-usb-phy",
|
||||
.data = &chip_info_7216,
|
||||
},
|
||||
{
|
||||
.compatible = "brcm,bcm7211-usb-phy",
|
||||
.data = &chip_info_7211b0,
|
||||
},
|
||||
{
|
||||
.compatible = "brcm,brcmstb-usb-phy",
|
||||
.data = &chip_info_7445,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static int brcm_usb_get_regs(struct platform_device *pdev,
|
||||
enum brcmusb_reg_sel regs,
|
||||
struct brcm_usb_init_params *ini,
|
||||
bool optional)
|
||||
{
|
||||
struct resource *res;
|
||||
|
||||
/* Older DT nodes have ctrl and optional xhci_ec by index only */
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
node_reg_names[regs]);
|
||||
if (res == NULL) {
|
||||
if (regs == BRCM_REGS_CTRL) {
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
} else if (regs == BRCM_REGS_XHCI_EC) {
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
/* XHCI_EC registers are optional */
|
||||
if (res == NULL)
|
||||
return 0;
|
||||
}
|
||||
if (res == NULL) {
|
||||
if (optional) {
|
||||
dev_dbg(&pdev->dev,
|
||||
"Optional reg %s not found\n",
|
||||
node_reg_names[regs]);
|
||||
return 0;
|
||||
}
|
||||
dev_err(&pdev->dev, "can't get %s base addr\n",
|
||||
node_reg_names[regs]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
ini->regs[regs] = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(ini->regs[regs])) {
|
||||
dev_err(&pdev->dev, "can't map %s register space\n",
|
||||
node_reg_names[regs]);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int brcm_usb_phy_dvr_init(struct platform_device *pdev,
|
||||
struct brcm_usb_phy_data *priv,
|
||||
struct device_node *dn)
|
||||
{
|
||||
struct phy *gphy;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct phy *gphy = NULL;
|
||||
int err;
|
||||
|
||||
priv->usb_20_clk = of_clk_get_by_name(dn, "sw_usb");
|
||||
if (IS_ERR(priv->usb_20_clk)) {
|
||||
if (PTR_ERR(priv->usb_20_clk) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
dev_info(dev, "Clock not found in Device Tree\n");
|
||||
priv->usb_20_clk = NULL;
|
||||
}
|
||||
@@ -267,6 +382,8 @@ static int brcm_usb_phy_dvr_init(struct device *dev,
|
||||
|
||||
priv->usb_30_clk = of_clk_get_by_name(dn, "sw_usb3");
|
||||
if (IS_ERR(priv->usb_30_clk)) {
|
||||
if (PTR_ERR(priv->usb_30_clk) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
dev_info(dev,
|
||||
"USB3.0 clock not found in Device Tree\n");
|
||||
priv->usb_30_clk = NULL;
|
||||
@@ -275,18 +392,46 @@ static int brcm_usb_phy_dvr_init(struct device *dev,
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
priv->suspend_clk = clk_get(dev, "usb0_freerun");
|
||||
if (IS_ERR(priv->suspend_clk)) {
|
||||
if (PTR_ERR(priv->suspend_clk) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
dev_err(dev, "Suspend Clock not found in Device Tree\n");
|
||||
priv->suspend_clk = NULL;
|
||||
}
|
||||
|
||||
priv->wake_irq = platform_get_irq_byname(pdev, "wake");
|
||||
if (priv->wake_irq < 0)
|
||||
priv->wake_irq = platform_get_irq_byname(pdev, "wakeup");
|
||||
if (priv->wake_irq >= 0) {
|
||||
err = devm_request_irq(dev, priv->wake_irq,
|
||||
brcm_usb_phy_wake_isr, 0,
|
||||
dev_name(dev), gphy);
|
||||
if (err < 0)
|
||||
return err;
|
||||
device_set_wakeup_capable(dev, 1);
|
||||
} else {
|
||||
dev_info(dev,
|
||||
"Wake interrupt missing, system wake not supported\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int brcm_usb_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct brcm_usb_phy_data *priv;
|
||||
struct phy_provider *phy_provider;
|
||||
struct device_node *dn = pdev->dev.of_node;
|
||||
int err;
|
||||
const char *mode;
|
||||
const struct of_device_id *match;
|
||||
void (*dvr_init)(struct brcm_usb_init_params *params);
|
||||
const struct match_chip_info *info;
|
||||
struct regmap *rmap;
|
||||
int x;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
@@ -295,30 +440,14 @@ static int brcm_usb_phy_probe(struct platform_device *pdev)
|
||||
|
||||
priv->ini.family_id = brcmstb_get_family_id();
|
||||
priv->ini.product_id = brcmstb_get_product_id();
|
||||
brcm_usb_set_family_map(&priv->ini);
|
||||
|
||||
match = of_match_node(brcm_usb_dt_ids, dev->of_node);
|
||||
info = match->data;
|
||||
dvr_init = info->init_func;
|
||||
(*dvr_init)(&priv->ini);
|
||||
|
||||
dev_dbg(dev, "Best mapping table is for %s\n",
|
||||
priv->ini.family_name);
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "can't get USB_CTRL base address\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
priv->ini.ctrl_regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(priv->ini.ctrl_regs)) {
|
||||
dev_err(dev, "can't map CTRL register space\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* The XHCI EC registers are optional */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (res) {
|
||||
priv->ini.xhci_ec_regs =
|
||||
devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(priv->ini.xhci_ec_regs)) {
|
||||
dev_err(dev, "can't map XHCI EC register space\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
of_property_read_u32(dn, "brcm,ipp", &priv->ini.ipp);
|
||||
of_property_read_u32(dn, "brcm,ioc", &priv->ini.ioc);
|
||||
@@ -335,7 +464,23 @@ static int brcm_usb_phy_probe(struct platform_device *pdev)
|
||||
if (of_property_read_bool(dn, "brcm,has-eohci"))
|
||||
priv->has_eohci = true;
|
||||
|
||||
err = brcm_usb_phy_dvr_init(dev, priv, dn);
|
||||
for (x = 0; x < BRCM_REGS_MAX; x++) {
|
||||
if (info->required_regs[x] >= BRCM_REGS_MAX)
|
||||
break;
|
||||
|
||||
err = brcm_usb_get_regs(pdev, info->required_regs[x],
|
||||
&priv->ini, false);
|
||||
if (err)
|
||||
return -EINVAL;
|
||||
}
|
||||
if (info->optional_reg) {
|
||||
err = brcm_usb_get_regs(pdev, info->optional_reg,
|
||||
&priv->ini, true);
|
||||
if (err)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = brcm_usb_phy_dvr_init(pdev, priv, dn);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -354,14 +499,23 @@ static int brcm_usb_phy_probe(struct platform_device *pdev)
|
||||
if (err)
|
||||
dev_warn(dev, "Error creating sysfs attributes\n");
|
||||
|
||||
/* Get piarbctl syscon if it exists */
|
||||
rmap = syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||
"syscon-piarbctl");
|
||||
if (IS_ERR(rmap))
|
||||
rmap = syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||
"brcm,syscon-piarbctl");
|
||||
if (!IS_ERR(rmap))
|
||||
priv->ini.syscon_piarbctl = rmap;
|
||||
|
||||
/* start with everything off */
|
||||
if (priv->has_xhci)
|
||||
brcm_usb_uninit_xhci(&priv->ini);
|
||||
if (priv->has_eohci)
|
||||
brcm_usb_uninit_eohci(&priv->ini);
|
||||
brcm_usb_uninit_common(&priv->ini);
|
||||
clk_disable(priv->usb_20_clk);
|
||||
clk_disable(priv->usb_30_clk);
|
||||
clk_disable_unprepare(priv->usb_20_clk);
|
||||
clk_disable_unprepare(priv->usb_30_clk);
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(dev, brcm_usb_phy_xlate);
|
||||
|
||||
@@ -381,8 +535,28 @@ static int brcm_usb_phy_suspend(struct device *dev)
|
||||
struct brcm_usb_phy_data *priv = dev_get_drvdata(dev);
|
||||
|
||||
if (priv->init_count) {
|
||||
clk_disable(priv->usb_20_clk);
|
||||
clk_disable(priv->usb_30_clk);
|
||||
priv->ini.wake_enabled = device_may_wakeup(dev);
|
||||
if (priv->phys[BRCM_USB_PHY_3_0].inited)
|
||||
brcm_usb_uninit_xhci(&priv->ini);
|
||||
if (priv->phys[BRCM_USB_PHY_2_0].inited)
|
||||
brcm_usb_uninit_eohci(&priv->ini);
|
||||
brcm_usb_uninit_common(&priv->ini);
|
||||
|
||||
/*
|
||||
* Handle the clocks unless needed for wake. This has
|
||||
* to work for both older XHCI->3.0-clks, EOHCI->2.0-clks
|
||||
* and newer XHCI->2.0-clks/3.0-clks.
|
||||
*/
|
||||
|
||||
if (!priv->ini.suspend_with_clocks) {
|
||||
if (priv->phys[BRCM_USB_PHY_3_0].inited)
|
||||
clk_disable_unprepare(priv->usb_30_clk);
|
||||
if (priv->phys[BRCM_USB_PHY_2_0].inited ||
|
||||
!priv->has_eohci)
|
||||
clk_disable_unprepare(priv->usb_20_clk);
|
||||
}
|
||||
if (priv->wake_irq >= 0)
|
||||
enable_irq_wake(priv->wake_irq);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -391,8 +565,8 @@ static int brcm_usb_phy_resume(struct device *dev)
|
||||
{
|
||||
struct brcm_usb_phy_data *priv = dev_get_drvdata(dev);
|
||||
|
||||
clk_enable(priv->usb_20_clk);
|
||||
clk_enable(priv->usb_30_clk);
|
||||
clk_prepare_enable(priv->usb_20_clk);
|
||||
clk_prepare_enable(priv->usb_30_clk);
|
||||
brcm_usb_init_ipp(&priv->ini);
|
||||
|
||||
/*
|
||||
@@ -400,18 +574,22 @@ static int brcm_usb_phy_resume(struct device *dev)
|
||||
* Uninitialize anything that wasn't previously initialized.
|
||||
*/
|
||||
if (priv->init_count) {
|
||||
if (priv->wake_irq >= 0)
|
||||
disable_irq_wake(priv->wake_irq);
|
||||
brcm_usb_init_common(&priv->ini);
|
||||
if (priv->phys[BRCM_USB_PHY_2_0].inited) {
|
||||
brcm_usb_init_eohci(&priv->ini);
|
||||
} else if (priv->has_eohci) {
|
||||
brcm_usb_uninit_eohci(&priv->ini);
|
||||
clk_disable(priv->usb_20_clk);
|
||||
clk_disable_unprepare(priv->usb_20_clk);
|
||||
}
|
||||
if (priv->phys[BRCM_USB_PHY_3_0].inited) {
|
||||
brcm_usb_init_xhci(&priv->ini);
|
||||
} else if (priv->has_xhci) {
|
||||
brcm_usb_uninit_xhci(&priv->ini);
|
||||
clk_disable(priv->usb_30_clk);
|
||||
clk_disable_unprepare(priv->usb_30_clk);
|
||||
if (!priv->has_eohci)
|
||||
clk_disable_unprepare(priv->usb_20_clk);
|
||||
}
|
||||
} else {
|
||||
if (priv->has_xhci)
|
||||
@@ -419,10 +597,10 @@ static int brcm_usb_phy_resume(struct device *dev)
|
||||
if (priv->has_eohci)
|
||||
brcm_usb_uninit_eohci(&priv->ini);
|
||||
brcm_usb_uninit_common(&priv->ini);
|
||||
clk_disable(priv->usb_20_clk);
|
||||
clk_disable(priv->usb_30_clk);
|
||||
clk_disable_unprepare(priv->usb_20_clk);
|
||||
clk_disable_unprepare(priv->usb_30_clk);
|
||||
}
|
||||
|
||||
priv->ini.wake_enabled = false;
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
@@ -431,11 +609,6 @@ static const struct dev_pm_ops brcm_usb_phy_pm_ops = {
|
||||
SET_LATE_SYSTEM_SLEEP_PM_OPS(brcm_usb_phy_suspend, brcm_usb_phy_resume)
|
||||
};
|
||||
|
||||
static const struct of_device_id brcm_usb_dt_ids[] = {
|
||||
{ .compatible = "brcm,brcmstb-usb-phy" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, brcm_usb_dt_ids);
|
||||
|
||||
static struct platform_driver brcm_usb_driver = {
|
||||
|
@@ -22,48 +22,134 @@
|
||||
#include <dt-bindings/phy/phy.h>
|
||||
|
||||
/* PHY register offsets */
|
||||
#define SIERRA_PHY_PLL_CFG (0xc00e << 2)
|
||||
#define SIERRA_DET_STANDEC_A (0x4000 << 2)
|
||||
#define SIERRA_DET_STANDEC_B (0x4001 << 2)
|
||||
#define SIERRA_DET_STANDEC_C (0x4002 << 2)
|
||||
#define SIERRA_DET_STANDEC_D (0x4003 << 2)
|
||||
#define SIERRA_DET_STANDEC_E (0x4004 << 2)
|
||||
#define SIERRA_PSM_LANECAL (0x4008 << 2)
|
||||
#define SIERRA_PSM_DIAG (0x4015 << 2)
|
||||
#define SIERRA_PSC_TX_A0 (0x4028 << 2)
|
||||
#define SIERRA_PSC_TX_A1 (0x4029 << 2)
|
||||
#define SIERRA_PSC_TX_A2 (0x402A << 2)
|
||||
#define SIERRA_PSC_TX_A3 (0x402B << 2)
|
||||
#define SIERRA_PSC_RX_A0 (0x4030 << 2)
|
||||
#define SIERRA_PSC_RX_A1 (0x4031 << 2)
|
||||
#define SIERRA_PSC_RX_A2 (0x4032 << 2)
|
||||
#define SIERRA_PSC_RX_A3 (0x4033 << 2)
|
||||
#define SIERRA_PLLCTRL_SUBRATE (0x403A << 2)
|
||||
#define SIERRA_PLLCTRL_GEN_D (0x403E << 2)
|
||||
#define SIERRA_DRVCTRL_ATTEN (0x406A << 2)
|
||||
#define SIERRA_CLKPATHCTRL_TMR (0x4081 << 2)
|
||||
#define SIERRA_RX_CREQ_FLTR_A_MODE1 (0x4087 << 2)
|
||||
#define SIERRA_RX_CREQ_FLTR_A_MODE0 (0x4088 << 2)
|
||||
#define SIERRA_CREQ_CCLKDET_MODE01 (0x408E << 2)
|
||||
#define SIERRA_RX_CTLE_MAINTENANCE (0x4091 << 2)
|
||||
#define SIERRA_CREQ_FSMCLK_SEL (0x4092 << 2)
|
||||
#define SIERRA_CTLELUT_CTRL (0x4098 << 2)
|
||||
#define SIERRA_DFE_ECMP_RATESEL (0x40C0 << 2)
|
||||
#define SIERRA_DFE_SMP_RATESEL (0x40C1 << 2)
|
||||
#define SIERRA_DEQ_VGATUNE_CTRL (0x40E1 << 2)
|
||||
#define SIERRA_TMRVAL_MODE3 (0x416E << 2)
|
||||
#define SIERRA_TMRVAL_MODE2 (0x416F << 2)
|
||||
#define SIERRA_TMRVAL_MODE1 (0x4170 << 2)
|
||||
#define SIERRA_TMRVAL_MODE0 (0x4171 << 2)
|
||||
#define SIERRA_PICNT_MODE1 (0x4174 << 2)
|
||||
#define SIERRA_CPI_OUTBUF_RATESEL (0x417C << 2)
|
||||
#define SIERRA_LFPSFILT_NS (0x418A << 2)
|
||||
#define SIERRA_LFPSFILT_RD (0x418B << 2)
|
||||
#define SIERRA_LFPSFILT_MP (0x418C << 2)
|
||||
#define SIERRA_SDFILT_H2L_A (0x4191 << 2)
|
||||
#define SIERRA_COMMON_CDB_OFFSET 0x0
|
||||
#define SIERRA_MACRO_ID_REG 0x0
|
||||
#define SIERRA_CMN_PLLLC_MODE_PREG 0x48
|
||||
#define SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG 0x49
|
||||
#define SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG 0x4A
|
||||
#define SIERRA_CMN_PLLLC_LOCK_CNTSTART_PREG 0x4B
|
||||
#define SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG 0x4F
|
||||
#define SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG 0x50
|
||||
#define SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG 0x62
|
||||
|
||||
#define SIERRA_MACRO_ID 0x00007364
|
||||
#define SIERRA_MAX_LANES 4
|
||||
#define SIERRA_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \
|
||||
((0x4000 << (block_offset)) + \
|
||||
(((ln) << 9) << (reg_offset)))
|
||||
|
||||
#define SIERRA_DET_STANDEC_A_PREG 0x000
|
||||
#define SIERRA_DET_STANDEC_B_PREG 0x001
|
||||
#define SIERRA_DET_STANDEC_C_PREG 0x002
|
||||
#define SIERRA_DET_STANDEC_D_PREG 0x003
|
||||
#define SIERRA_DET_STANDEC_E_PREG 0x004
|
||||
#define SIERRA_PSM_LANECAL_DLY_A1_RESETS_PREG 0x008
|
||||
#define SIERRA_PSM_A0IN_TMR_PREG 0x009
|
||||
#define SIERRA_PSM_DIAG_PREG 0x015
|
||||
#define SIERRA_PSC_TX_A0_PREG 0x028
|
||||
#define SIERRA_PSC_TX_A1_PREG 0x029
|
||||
#define SIERRA_PSC_TX_A2_PREG 0x02A
|
||||
#define SIERRA_PSC_TX_A3_PREG 0x02B
|
||||
#define SIERRA_PSC_RX_A0_PREG 0x030
|
||||
#define SIERRA_PSC_RX_A1_PREG 0x031
|
||||
#define SIERRA_PSC_RX_A2_PREG 0x032
|
||||
#define SIERRA_PSC_RX_A3_PREG 0x033
|
||||
#define SIERRA_PLLCTRL_SUBRATE_PREG 0x03A
|
||||
#define SIERRA_PLLCTRL_GEN_D_PREG 0x03E
|
||||
#define SIERRA_PLLCTRL_CPGAIN_MODE_PREG 0x03F
|
||||
#define SIERRA_PLLCTRL_STATUS_PREG 0x044
|
||||
#define SIERRA_CLKPATH_BIASTRIM_PREG 0x04B
|
||||
#define SIERRA_DFE_BIASTRIM_PREG 0x04C
|
||||
#define SIERRA_DRVCTRL_ATTEN_PREG 0x06A
|
||||
#define SIERRA_CLKPATHCTRL_TMR_PREG 0x081
|
||||
#define SIERRA_RX_CREQ_FLTR_A_MODE3_PREG 0x085
|
||||
#define SIERRA_RX_CREQ_FLTR_A_MODE2_PREG 0x086
|
||||
#define SIERRA_RX_CREQ_FLTR_A_MODE1_PREG 0x087
|
||||
#define SIERRA_RX_CREQ_FLTR_A_MODE0_PREG 0x088
|
||||
#define SIERRA_CREQ_CCLKDET_MODE01_PREG 0x08E
|
||||
#define SIERRA_RX_CTLE_MAINTENANCE_PREG 0x091
|
||||
#define SIERRA_CREQ_FSMCLK_SEL_PREG 0x092
|
||||
#define SIERRA_CREQ_EQ_CTRL_PREG 0x093
|
||||
#define SIERRA_CREQ_SPARE_PREG 0x096
|
||||
#define SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG 0x097
|
||||
#define SIERRA_CTLELUT_CTRL_PREG 0x098
|
||||
#define SIERRA_DFE_ECMP_RATESEL_PREG 0x0C0
|
||||
#define SIERRA_DFE_SMP_RATESEL_PREG 0x0C1
|
||||
#define SIERRA_DEQ_PHALIGN_CTRL 0x0C4
|
||||
#define SIERRA_DEQ_CONCUR_CTRL1_PREG 0x0C8
|
||||
#define SIERRA_DEQ_CONCUR_CTRL2_PREG 0x0C9
|
||||
#define SIERRA_DEQ_EPIPWR_CTRL2_PREG 0x0CD
|
||||
#define SIERRA_DEQ_FAST_MAINT_CYCLES_PREG 0x0CE
|
||||
#define SIERRA_DEQ_ERRCMP_CTRL_PREG 0x0D0
|
||||
#define SIERRA_DEQ_OFFSET_CTRL_PREG 0x0D8
|
||||
#define SIERRA_DEQ_GAIN_CTRL_PREG 0x0E0
|
||||
#define SIERRA_DEQ_VGATUNE_CTRL_PREG 0x0E1
|
||||
#define SIERRA_DEQ_GLUT0 0x0E8
|
||||
#define SIERRA_DEQ_GLUT1 0x0E9
|
||||
#define SIERRA_DEQ_GLUT2 0x0EA
|
||||
#define SIERRA_DEQ_GLUT3 0x0EB
|
||||
#define SIERRA_DEQ_GLUT4 0x0EC
|
||||
#define SIERRA_DEQ_GLUT5 0x0ED
|
||||
#define SIERRA_DEQ_GLUT6 0x0EE
|
||||
#define SIERRA_DEQ_GLUT7 0x0EF
|
||||
#define SIERRA_DEQ_GLUT8 0x0F0
|
||||
#define SIERRA_DEQ_GLUT9 0x0F1
|
||||
#define SIERRA_DEQ_GLUT10 0x0F2
|
||||
#define SIERRA_DEQ_GLUT11 0x0F3
|
||||
#define SIERRA_DEQ_GLUT12 0x0F4
|
||||
#define SIERRA_DEQ_GLUT13 0x0F5
|
||||
#define SIERRA_DEQ_GLUT14 0x0F6
|
||||
#define SIERRA_DEQ_GLUT15 0x0F7
|
||||
#define SIERRA_DEQ_GLUT16 0x0F8
|
||||
#define SIERRA_DEQ_ALUT0 0x108
|
||||
#define SIERRA_DEQ_ALUT1 0x109
|
||||
#define SIERRA_DEQ_ALUT2 0x10A
|
||||
#define SIERRA_DEQ_ALUT3 0x10B
|
||||
#define SIERRA_DEQ_ALUT4 0x10C
|
||||
#define SIERRA_DEQ_ALUT5 0x10D
|
||||
#define SIERRA_DEQ_ALUT6 0x10E
|
||||
#define SIERRA_DEQ_ALUT7 0x10F
|
||||
#define SIERRA_DEQ_ALUT8 0x110
|
||||
#define SIERRA_DEQ_ALUT9 0x111
|
||||
#define SIERRA_DEQ_ALUT10 0x112
|
||||
#define SIERRA_DEQ_ALUT11 0x113
|
||||
#define SIERRA_DEQ_ALUT12 0x114
|
||||
#define SIERRA_DEQ_ALUT13 0x115
|
||||
#define SIERRA_DEQ_DFETAP_CTRL_PREG 0x128
|
||||
#define SIERRA_DFE_EN_1010_IGNORE_PREG 0x134
|
||||
#define SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG 0x150
|
||||
#define SIERRA_DEQ_TAU_CTRL2_PREG 0x151
|
||||
#define SIERRA_DEQ_PICTRL_PREG 0x161
|
||||
#define SIERRA_CPICAL_TMRVAL_MODE1_PREG 0x170
|
||||
#define SIERRA_CPICAL_TMRVAL_MODE0_PREG 0x171
|
||||
#define SIERRA_CPICAL_PICNT_MODE1_PREG 0x174
|
||||
#define SIERRA_CPI_OUTBUF_RATESEL_PREG 0x17C
|
||||
#define SIERRA_CPICAL_RES_STARTCODE_MODE23_PREG 0x183
|
||||
#define SIERRA_LFPSDET_SUPPORT_PREG 0x188
|
||||
#define SIERRA_LFPSFILT_NS_PREG 0x18A
|
||||
#define SIERRA_LFPSFILT_RD_PREG 0x18B
|
||||
#define SIERRA_LFPSFILT_MP_PREG 0x18C
|
||||
#define SIERRA_SIGDET_SUPPORT_PREG 0x190
|
||||
#define SIERRA_SDFILT_H2L_A_PREG 0x191
|
||||
#define SIERRA_SDFILT_L2H_PREG 0x193
|
||||
#define SIERRA_RXBUFFER_CTLECTRL_PREG 0x19E
|
||||
#define SIERRA_RXBUFFER_RCDFECTRL_PREG 0x19F
|
||||
#define SIERRA_RXBUFFER_DFECTRL_PREG 0x1A0
|
||||
#define SIERRA_DEQ_TAU_CTRL1_FAST_MAINT_PREG 0x14F
|
||||
#define SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG 0x150
|
||||
|
||||
#define SIERRA_PHY_CONFIG_CTRL_OFFSET(block_offset) \
|
||||
(0xc000 << (block_offset))
|
||||
#define SIERRA_PHY_PLL_CFG 0xe
|
||||
|
||||
#define SIERRA_MACRO_ID 0x00007364
|
||||
#define SIERRA_MAX_LANES 16
|
||||
#define PLL_LOCK_TIME 100000
|
||||
|
||||
static const struct reg_field macro_id_type =
|
||||
REG_FIELD(SIERRA_MACRO_ID_REG, 0, 15);
|
||||
static const struct reg_field phy_pll_cfg_1 =
|
||||
REG_FIELD(SIERRA_PHY_PLL_CFG, 1, 1);
|
||||
static const struct reg_field pllctrl_lock =
|
||||
REG_FIELD(SIERRA_PLLCTRL_STATUS_PREG, 0, 0);
|
||||
|
||||
struct cdns_sierra_inst {
|
||||
struct phy *phy;
|
||||
@@ -80,53 +166,172 @@ struct cdns_reg_pairs {
|
||||
|
||||
struct cdns_sierra_data {
|
||||
u32 id_value;
|
||||
u32 pcie_regs;
|
||||
u32 usb_regs;
|
||||
struct cdns_reg_pairs *pcie_vals;
|
||||
struct cdns_reg_pairs *usb_vals;
|
||||
u8 block_offset_shift;
|
||||
u8 reg_offset_shift;
|
||||
u32 pcie_cmn_regs;
|
||||
u32 pcie_ln_regs;
|
||||
u32 usb_cmn_regs;
|
||||
u32 usb_ln_regs;
|
||||
struct cdns_reg_pairs *pcie_cmn_vals;
|
||||
struct cdns_reg_pairs *pcie_ln_vals;
|
||||
struct cdns_reg_pairs *usb_cmn_vals;
|
||||
struct cdns_reg_pairs *usb_ln_vals;
|
||||
};
|
||||
|
||||
struct cdns_regmap_cdb_context {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
u8 reg_offset_shift;
|
||||
};
|
||||
|
||||
struct cdns_sierra_phy {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
struct regmap *regmap;
|
||||
struct cdns_sierra_data *init_data;
|
||||
struct cdns_sierra_inst phys[SIERRA_MAX_LANES];
|
||||
struct reset_control *phy_rst;
|
||||
struct reset_control *apb_rst;
|
||||
struct regmap *regmap_lane_cdb[SIERRA_MAX_LANES];
|
||||
struct regmap *regmap_phy_config_ctrl;
|
||||
struct regmap *regmap_common_cdb;
|
||||
struct regmap_field *macro_id_type;
|
||||
struct regmap_field *phy_pll_cfg_1;
|
||||
struct regmap_field *pllctrl_lock[SIERRA_MAX_LANES];
|
||||
struct clk *clk;
|
||||
struct clk *cmn_refclk_dig_div;
|
||||
struct clk *cmn_refclk1_dig_div;
|
||||
int nsubnodes;
|
||||
u32 num_lanes;
|
||||
bool autoconf;
|
||||
};
|
||||
|
||||
static void cdns_sierra_phy_init(struct phy *gphy)
|
||||
static int cdns_regmap_write(void *context, unsigned int reg, unsigned int val)
|
||||
{
|
||||
struct cdns_regmap_cdb_context *ctx = context;
|
||||
u32 offset = reg << ctx->reg_offset_shift;
|
||||
|
||||
writew(val, ctx->base + offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdns_regmap_read(void *context, unsigned int reg, unsigned int *val)
|
||||
{
|
||||
struct cdns_regmap_cdb_context *ctx = context;
|
||||
u32 offset = reg << ctx->reg_offset_shift;
|
||||
|
||||
*val = readw(ctx->base + offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SIERRA_LANE_CDB_REGMAP_CONF(n) \
|
||||
{ \
|
||||
.name = "sierra_lane" n "_cdb", \
|
||||
.reg_stride = 1, \
|
||||
.fast_io = true, \
|
||||
.reg_write = cdns_regmap_write, \
|
||||
.reg_read = cdns_regmap_read, \
|
||||
}
|
||||
|
||||
static struct regmap_config cdns_sierra_lane_cdb_config[] = {
|
||||
SIERRA_LANE_CDB_REGMAP_CONF("0"),
|
||||
SIERRA_LANE_CDB_REGMAP_CONF("1"),
|
||||
SIERRA_LANE_CDB_REGMAP_CONF("2"),
|
||||
SIERRA_LANE_CDB_REGMAP_CONF("3"),
|
||||
SIERRA_LANE_CDB_REGMAP_CONF("4"),
|
||||
SIERRA_LANE_CDB_REGMAP_CONF("5"),
|
||||
SIERRA_LANE_CDB_REGMAP_CONF("6"),
|
||||
SIERRA_LANE_CDB_REGMAP_CONF("7"),
|
||||
SIERRA_LANE_CDB_REGMAP_CONF("8"),
|
||||
SIERRA_LANE_CDB_REGMAP_CONF("9"),
|
||||
SIERRA_LANE_CDB_REGMAP_CONF("10"),
|
||||
SIERRA_LANE_CDB_REGMAP_CONF("11"),
|
||||
SIERRA_LANE_CDB_REGMAP_CONF("12"),
|
||||
SIERRA_LANE_CDB_REGMAP_CONF("13"),
|
||||
SIERRA_LANE_CDB_REGMAP_CONF("14"),
|
||||
SIERRA_LANE_CDB_REGMAP_CONF("15"),
|
||||
};
|
||||
|
||||
static struct regmap_config cdns_sierra_common_cdb_config = {
|
||||
.name = "sierra_common_cdb",
|
||||
.reg_stride = 1,
|
||||
.fast_io = true,
|
||||
.reg_write = cdns_regmap_write,
|
||||
.reg_read = cdns_regmap_read,
|
||||
};
|
||||
|
||||
static struct regmap_config cdns_sierra_phy_config_ctrl_config = {
|
||||
.name = "sierra_phy_config_ctrl",
|
||||
.reg_stride = 1,
|
||||
.fast_io = true,
|
||||
.reg_write = cdns_regmap_write,
|
||||
.reg_read = cdns_regmap_read,
|
||||
};
|
||||
|
||||
static int cdns_sierra_phy_init(struct phy *gphy)
|
||||
{
|
||||
struct cdns_sierra_inst *ins = phy_get_drvdata(gphy);
|
||||
struct cdns_sierra_phy *phy = dev_get_drvdata(gphy->dev.parent);
|
||||
struct regmap *regmap;
|
||||
int i, j;
|
||||
struct cdns_reg_pairs *vals;
|
||||
u32 num_regs;
|
||||
struct cdns_reg_pairs *cmn_vals, *ln_vals;
|
||||
u32 num_cmn_regs, num_ln_regs;
|
||||
|
||||
/* Initialise the PHY registers, unless auto configured */
|
||||
if (phy->autoconf)
|
||||
return 0;
|
||||
|
||||
clk_set_rate(phy->cmn_refclk_dig_div, 25000000);
|
||||
clk_set_rate(phy->cmn_refclk1_dig_div, 25000000);
|
||||
if (ins->phy_type == PHY_TYPE_PCIE) {
|
||||
num_regs = phy->init_data->pcie_regs;
|
||||
vals = phy->init_data->pcie_vals;
|
||||
num_cmn_regs = phy->init_data->pcie_cmn_regs;
|
||||
num_ln_regs = phy->init_data->pcie_ln_regs;
|
||||
cmn_vals = phy->init_data->pcie_cmn_vals;
|
||||
ln_vals = phy->init_data->pcie_ln_vals;
|
||||
} else if (ins->phy_type == PHY_TYPE_USB3) {
|
||||
num_regs = phy->init_data->usb_regs;
|
||||
vals = phy->init_data->usb_vals;
|
||||
num_cmn_regs = phy->init_data->usb_cmn_regs;
|
||||
num_ln_regs = phy->init_data->usb_ln_regs;
|
||||
cmn_vals = phy->init_data->usb_cmn_vals;
|
||||
ln_vals = phy->init_data->usb_ln_vals;
|
||||
} else {
|
||||
return;
|
||||
return -EINVAL;
|
||||
}
|
||||
for (i = 0; i < ins->num_lanes; i++)
|
||||
for (j = 0; j < num_regs ; j++)
|
||||
writel(vals[j].val, phy->base +
|
||||
vals[j].off + (i + ins->mlane) * 0x800);
|
||||
|
||||
regmap = phy->regmap_common_cdb;
|
||||
for (j = 0; j < num_cmn_regs ; j++)
|
||||
regmap_write(regmap, cmn_vals[j].off, cmn_vals[j].val);
|
||||
|
||||
for (i = 0; i < ins->num_lanes; i++) {
|
||||
for (j = 0; j < num_ln_regs ; j++) {
|
||||
regmap = phy->regmap_lane_cdb[i + ins->mlane];
|
||||
regmap_write(regmap, ln_vals[j].off, ln_vals[j].val);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdns_sierra_phy_on(struct phy *gphy)
|
||||
{
|
||||
struct cdns_sierra_phy *sp = dev_get_drvdata(gphy->dev.parent);
|
||||
struct cdns_sierra_inst *ins = phy_get_drvdata(gphy);
|
||||
struct device *dev = sp->dev;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
/* Take the PHY lane group out of reset */
|
||||
return reset_control_deassert(ins->lnk_rst);
|
||||
ret = reset_control_deassert(ins->lnk_rst);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to take the PHY lane out of reset\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_field_read_poll_timeout(sp->pllctrl_lock[ins->mlane],
|
||||
val, val, 1000, PLL_LOCK_TIME);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "PLL lock of lane failed\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cdns_sierra_phy_off(struct phy *gphy)
|
||||
@@ -136,9 +341,20 @@ static int cdns_sierra_phy_off(struct phy *gphy)
|
||||
return reset_control_assert(ins->lnk_rst);
|
||||
}
|
||||
|
||||
static int cdns_sierra_phy_reset(struct phy *gphy)
|
||||
{
|
||||
struct cdns_sierra_phy *sp = dev_get_drvdata(gphy->dev.parent);
|
||||
|
||||
reset_control_assert(sp->phy_rst);
|
||||
reset_control_deassert(sp->phy_rst);
|
||||
return 0;
|
||||
};
|
||||
|
||||
static const struct phy_ops ops = {
|
||||
.init = cdns_sierra_phy_init,
|
||||
.power_on = cdns_sierra_phy_on,
|
||||
.power_off = cdns_sierra_phy_off,
|
||||
.reset = cdns_sierra_phy_reset,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
@@ -159,41 +375,152 @@ static int cdns_sierra_get_optional(struct cdns_sierra_inst *inst,
|
||||
|
||||
static const struct of_device_id cdns_sierra_id_table[];
|
||||
|
||||
static struct regmap *cdns_regmap_init(struct device *dev, void __iomem *base,
|
||||
u32 block_offset, u8 reg_offset_shift,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
struct cdns_regmap_cdb_context *ctx;
|
||||
|
||||
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ctx->dev = dev;
|
||||
ctx->base = base + block_offset;
|
||||
ctx->reg_offset_shift = reg_offset_shift;
|
||||
|
||||
return devm_regmap_init(dev, NULL, ctx, config);
|
||||
}
|
||||
|
||||
static int cdns_regfield_init(struct cdns_sierra_phy *sp)
|
||||
{
|
||||
struct device *dev = sp->dev;
|
||||
struct regmap_field *field;
|
||||
struct regmap *regmap;
|
||||
int i;
|
||||
|
||||
regmap = sp->regmap_common_cdb;
|
||||
field = devm_regmap_field_alloc(dev, regmap, macro_id_type);
|
||||
if (IS_ERR(field)) {
|
||||
dev_err(dev, "MACRO_ID_TYPE reg field init failed\n");
|
||||
return PTR_ERR(field);
|
||||
}
|
||||
sp->macro_id_type = field;
|
||||
|
||||
regmap = sp->regmap_phy_config_ctrl;
|
||||
field = devm_regmap_field_alloc(dev, regmap, phy_pll_cfg_1);
|
||||
if (IS_ERR(field)) {
|
||||
dev_err(dev, "PHY_PLL_CFG_1 reg field init failed\n");
|
||||
return PTR_ERR(field);
|
||||
}
|
||||
sp->phy_pll_cfg_1 = field;
|
||||
|
||||
for (i = 0; i < SIERRA_MAX_LANES; i++) {
|
||||
regmap = sp->regmap_lane_cdb[i];
|
||||
field = devm_regmap_field_alloc(dev, regmap, pllctrl_lock);
|
||||
if (IS_ERR(field)) {
|
||||
dev_err(dev, "P%d_ENABLE reg field init failed\n", i);
|
||||
return PTR_ERR(field);
|
||||
}
|
||||
sp->pllctrl_lock[i] = field;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdns_regmap_init_blocks(struct cdns_sierra_phy *sp,
|
||||
void __iomem *base, u8 block_offset_shift,
|
||||
u8 reg_offset_shift)
|
||||
{
|
||||
struct device *dev = sp->dev;
|
||||
struct regmap *regmap;
|
||||
u32 block_offset;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < SIERRA_MAX_LANES; i++) {
|
||||
block_offset = SIERRA_LANE_CDB_OFFSET(i, block_offset_shift,
|
||||
reg_offset_shift);
|
||||
regmap = cdns_regmap_init(dev, base, block_offset,
|
||||
reg_offset_shift,
|
||||
&cdns_sierra_lane_cdb_config[i]);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(dev, "Failed to init lane CDB regmap\n");
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
sp->regmap_lane_cdb[i] = regmap;
|
||||
}
|
||||
|
||||
regmap = cdns_regmap_init(dev, base, SIERRA_COMMON_CDB_OFFSET,
|
||||
reg_offset_shift,
|
||||
&cdns_sierra_common_cdb_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(dev, "Failed to init common CDB regmap\n");
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
sp->regmap_common_cdb = regmap;
|
||||
|
||||
block_offset = SIERRA_PHY_CONFIG_CTRL_OFFSET(block_offset_shift);
|
||||
regmap = cdns_regmap_init(dev, base, block_offset, reg_offset_shift,
|
||||
&cdns_sierra_phy_config_ctrl_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(dev, "Failed to init PHY config and control regmap\n");
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
sp->regmap_phy_config_ctrl = regmap;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdns_sierra_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct cdns_sierra_phy *sp;
|
||||
struct phy_provider *phy_provider;
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct of_device_id *match;
|
||||
struct cdns_sierra_data *data;
|
||||
unsigned int id_value;
|
||||
struct resource *res;
|
||||
int i, ret, node = 0;
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
struct device_node *dn = dev->of_node, *child;
|
||||
|
||||
if (of_get_child_count(dn) == 0)
|
||||
return -ENODEV;
|
||||
|
||||
/* Get init data for this PHY */
|
||||
match = of_match_device(cdns_sierra_id_table, dev);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
|
||||
data = (struct cdns_sierra_data *)match->data;
|
||||
|
||||
sp = devm_kzalloc(dev, sizeof(*sp), GFP_KERNEL);
|
||||
if (!sp)
|
||||
return -ENOMEM;
|
||||
dev_set_drvdata(dev, sp);
|
||||
sp->dev = dev;
|
||||
sp->init_data = data;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
sp->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(sp->base)) {
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base)) {
|
||||
dev_err(dev, "missing \"reg\"\n");
|
||||
return PTR_ERR(sp->base);
|
||||
return PTR_ERR(base);
|
||||
}
|
||||
|
||||
/* Get init data for this PHY */
|
||||
match = of_match_device(cdns_sierra_id_table, dev);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
sp->init_data = (struct cdns_sierra_data *)match->data;
|
||||
ret = cdns_regmap_init_blocks(sp, base, data->block_offset_shift,
|
||||
data->reg_offset_shift);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cdns_regfield_init(sp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, sp);
|
||||
|
||||
sp->clk = devm_clk_get(dev, "phy_clk");
|
||||
sp->clk = devm_clk_get_optional(dev, "phy_clk");
|
||||
if (IS_ERR(sp->clk)) {
|
||||
dev_err(dev, "failed to get clock phy_clk\n");
|
||||
return PTR_ERR(sp->clk);
|
||||
@@ -205,12 +532,28 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(sp->phy_rst);
|
||||
}
|
||||
|
||||
sp->apb_rst = devm_reset_control_get(dev, "sierra_apb");
|
||||
sp->apb_rst = devm_reset_control_get_optional(dev, "sierra_apb");
|
||||
if (IS_ERR(sp->apb_rst)) {
|
||||
dev_err(dev, "failed to get apb reset\n");
|
||||
return PTR_ERR(sp->apb_rst);
|
||||
}
|
||||
|
||||
clk = devm_clk_get_optional(dev, "cmn_refclk_dig_div");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(dev, "cmn_refclk_dig_div clock not found\n");
|
||||
ret = PTR_ERR(clk);
|
||||
return ret;
|
||||
}
|
||||
sp->cmn_refclk_dig_div = clk;
|
||||
|
||||
clk = devm_clk_get_optional(dev, "cmn_refclk1_dig_div");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(dev, "cmn_refclk1_dig_div clock not found\n");
|
||||
ret = PTR_ERR(clk);
|
||||
return ret;
|
||||
}
|
||||
sp->cmn_refclk1_dig_div = clk;
|
||||
|
||||
ret = clk_prepare_enable(sp->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -219,7 +562,8 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
|
||||
reset_control_deassert(sp->apb_rst);
|
||||
|
||||
/* Check that PHY is present */
|
||||
if (sp->init_data->id_value != readl(sp->base)) {
|
||||
regmap_field_read(sp->macro_id_type, &id_value);
|
||||
if (sp->init_data->id_value != id_value) {
|
||||
ret = -EINVAL;
|
||||
goto clk_disable;
|
||||
}
|
||||
@@ -230,7 +574,7 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
|
||||
struct phy *gphy;
|
||||
|
||||
sp->phys[node].lnk_rst =
|
||||
of_reset_control_get_exclusive_by_index(child, 0);
|
||||
of_reset_control_array_get_exclusive(child);
|
||||
|
||||
if (IS_ERR(sp->phys[node].lnk_rst)) {
|
||||
dev_err(dev, "failed to get reset %s\n",
|
||||
@@ -248,6 +592,8 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
sp->num_lanes += sp->phys[node].num_lanes;
|
||||
|
||||
gphy = devm_phy_create(dev, child, &ops);
|
||||
|
||||
if (IS_ERR(gphy)) {
|
||||
@@ -257,17 +603,18 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
|
||||
sp->phys[node].phy = gphy;
|
||||
phy_set_drvdata(gphy, &sp->phys[node]);
|
||||
|
||||
/* Initialise the PHY registers, unless auto configured */
|
||||
if (!sp->autoconf)
|
||||
cdns_sierra_phy_init(gphy);
|
||||
|
||||
node++;
|
||||
}
|
||||
sp->nsubnodes = node;
|
||||
|
||||
if (sp->num_lanes > SIERRA_MAX_LANES) {
|
||||
dev_err(dev, "Invalid lane configuration\n");
|
||||
goto put_child2;
|
||||
}
|
||||
|
||||
/* If more than one subnode, configure the PHY as multilink */
|
||||
if (!sp->autoconf && sp->nsubnodes > 1)
|
||||
writel(2, sp->base + SIERRA_PHY_PLL_CFG);
|
||||
regmap_field_write(sp->phy_pll_cfg_1, 0x1);
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
@@ -288,7 +635,7 @@ clk_disable:
|
||||
|
||||
static int cdns_sierra_phy_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct cdns_sierra_phy *phy = dev_get_drvdata(pdev->dev.parent);
|
||||
struct cdns_sierra_phy *phy = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
reset_control_assert(phy->phy_rst);
|
||||
@@ -306,68 +653,158 @@ static int cdns_sierra_phy_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct cdns_reg_pairs cdns_usb_regs[] = {
|
||||
/*
|
||||
* Write USB configuration parameters to the PHY.
|
||||
* These values are specific to this specific hardware
|
||||
* configuration.
|
||||
*/
|
||||
{0xFE0A, SIERRA_DET_STANDEC_A},
|
||||
{0x000F, SIERRA_DET_STANDEC_B},
|
||||
{0x55A5, SIERRA_DET_STANDEC_C},
|
||||
{0x69AD, SIERRA_DET_STANDEC_D},
|
||||
{0x0241, SIERRA_DET_STANDEC_E},
|
||||
{0x0110, SIERRA_PSM_LANECAL},
|
||||
{0xCF00, SIERRA_PSM_DIAG},
|
||||
{0x001F, SIERRA_PSC_TX_A0},
|
||||
{0x0007, SIERRA_PSC_TX_A1},
|
||||
{0x0003, SIERRA_PSC_TX_A2},
|
||||
{0x0003, SIERRA_PSC_TX_A3},
|
||||
{0x0FFF, SIERRA_PSC_RX_A0},
|
||||
{0x0003, SIERRA_PSC_RX_A1},
|
||||
{0x0003, SIERRA_PSC_RX_A2},
|
||||
{0x0001, SIERRA_PSC_RX_A3},
|
||||
{0x0001, SIERRA_PLLCTRL_SUBRATE},
|
||||
{0x0406, SIERRA_PLLCTRL_GEN_D},
|
||||
{0x0000, SIERRA_DRVCTRL_ATTEN},
|
||||
{0x823E, SIERRA_CLKPATHCTRL_TMR},
|
||||
{0x078F, SIERRA_RX_CREQ_FLTR_A_MODE1},
|
||||
{0x078F, SIERRA_RX_CREQ_FLTR_A_MODE0},
|
||||
{0x7B3C, SIERRA_CREQ_CCLKDET_MODE01},
|
||||
{0x023C, SIERRA_RX_CTLE_MAINTENANCE},
|
||||
{0x3232, SIERRA_CREQ_FSMCLK_SEL},
|
||||
{0x8452, SIERRA_CTLELUT_CTRL},
|
||||
{0x4121, SIERRA_DFE_ECMP_RATESEL},
|
||||
{0x4121, SIERRA_DFE_SMP_RATESEL},
|
||||
{0x9999, SIERRA_DEQ_VGATUNE_CTRL},
|
||||
{0x0330, SIERRA_TMRVAL_MODE0},
|
||||
{0x01FF, SIERRA_PICNT_MODE1},
|
||||
{0x0009, SIERRA_CPI_OUTBUF_RATESEL},
|
||||
{0x000F, SIERRA_LFPSFILT_NS},
|
||||
{0x0009, SIERRA_LFPSFILT_RD},
|
||||
{0x0001, SIERRA_LFPSFILT_MP},
|
||||
{0x8013, SIERRA_SDFILT_H2L_A},
|
||||
{0x0400, SIERRA_TMRVAL_MODE1},
|
||||
/* refclk100MHz_32b_PCIe_cmn_pll_ext_ssc */
|
||||
static struct cdns_reg_pairs cdns_pcie_cmn_regs_ext_ssc[] = {
|
||||
{0x2106, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG},
|
||||
{0x2106, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG},
|
||||
{0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG},
|
||||
{0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG},
|
||||
{0x1B1B, SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG}
|
||||
};
|
||||
|
||||
static struct cdns_reg_pairs cdns_pcie_regs[] = {
|
||||
/*
|
||||
* Write PCIe configuration parameters to the PHY.
|
||||
* These values are specific to this specific hardware
|
||||
* configuration.
|
||||
*/
|
||||
{0x891f, SIERRA_DET_STANDEC_D},
|
||||
{0x0053, SIERRA_DET_STANDEC_E},
|
||||
{0x0400, SIERRA_TMRVAL_MODE2},
|
||||
{0x0200, SIERRA_TMRVAL_MODE3},
|
||||
/* refclk100MHz_32b_PCIe_ln_ext_ssc */
|
||||
static struct cdns_reg_pairs cdns_pcie_ln_regs_ext_ssc[] = {
|
||||
{0x813E, SIERRA_CLKPATHCTRL_TMR_PREG},
|
||||
{0x8047, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG},
|
||||
{0x808F, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG},
|
||||
{0x808F, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG},
|
||||
{0x808F, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG},
|
||||
{0x033C, SIERRA_RX_CTLE_MAINTENANCE_PREG},
|
||||
{0x44CC, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG}
|
||||
};
|
||||
|
||||
/* refclk100MHz_20b_USB_cmn_pll_ext_ssc */
|
||||
static struct cdns_reg_pairs cdns_usb_cmn_regs_ext_ssc[] = {
|
||||
{0x2085, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG},
|
||||
{0x2085, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG},
|
||||
{0x0000, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG},
|
||||
{0x0000, SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG}
|
||||
};
|
||||
|
||||
/* refclk100MHz_20b_USB_ln_ext_ssc */
|
||||
static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = {
|
||||
{0xFE0A, SIERRA_DET_STANDEC_A_PREG},
|
||||
{0x000F, SIERRA_DET_STANDEC_B_PREG},
|
||||
{0x00A5, SIERRA_DET_STANDEC_C_PREG},
|
||||
{0x69ad, SIERRA_DET_STANDEC_D_PREG},
|
||||
{0x0241, SIERRA_DET_STANDEC_E_PREG},
|
||||
{0x0010, SIERRA_PSM_LANECAL_DLY_A1_RESETS_PREG},
|
||||
{0x0014, SIERRA_PSM_A0IN_TMR_PREG},
|
||||
{0xCF00, SIERRA_PSM_DIAG_PREG},
|
||||
{0x001F, SIERRA_PSC_TX_A0_PREG},
|
||||
{0x0007, SIERRA_PSC_TX_A1_PREG},
|
||||
{0x0003, SIERRA_PSC_TX_A2_PREG},
|
||||
{0x0003, SIERRA_PSC_TX_A3_PREG},
|
||||
{0x0FFF, SIERRA_PSC_RX_A0_PREG},
|
||||
{0x0619, SIERRA_PSC_RX_A1_PREG},
|
||||
{0x0003, SIERRA_PSC_RX_A2_PREG},
|
||||
{0x0001, SIERRA_PSC_RX_A3_PREG},
|
||||
{0x0001, SIERRA_PLLCTRL_SUBRATE_PREG},
|
||||
{0x0406, SIERRA_PLLCTRL_GEN_D_PREG},
|
||||
{0x5233, SIERRA_PLLCTRL_CPGAIN_MODE_PREG},
|
||||
{0x00CA, SIERRA_CLKPATH_BIASTRIM_PREG},
|
||||
{0x2512, SIERRA_DFE_BIASTRIM_PREG},
|
||||
{0x0000, SIERRA_DRVCTRL_ATTEN_PREG},
|
||||
{0x873E, SIERRA_CLKPATHCTRL_TMR_PREG},
|
||||
{0x03CF, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG},
|
||||
{0x01CE, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG},
|
||||
{0x7B3C, SIERRA_CREQ_CCLKDET_MODE01_PREG},
|
||||
{0x033F, SIERRA_RX_CTLE_MAINTENANCE_PREG},
|
||||
{0x3232, SIERRA_CREQ_FSMCLK_SEL_PREG},
|
||||
{0x0000, SIERRA_CREQ_EQ_CTRL_PREG},
|
||||
{0x8000, SIERRA_CREQ_SPARE_PREG},
|
||||
{0xCC44, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG},
|
||||
{0x8453, SIERRA_CTLELUT_CTRL_PREG},
|
||||
{0x4110, SIERRA_DFE_ECMP_RATESEL_PREG},
|
||||
{0x4110, SIERRA_DFE_SMP_RATESEL_PREG},
|
||||
{0x0002, SIERRA_DEQ_PHALIGN_CTRL},
|
||||
{0x3200, SIERRA_DEQ_CONCUR_CTRL1_PREG},
|
||||
{0x5064, SIERRA_DEQ_CONCUR_CTRL2_PREG},
|
||||
{0x0030, SIERRA_DEQ_EPIPWR_CTRL2_PREG},
|
||||
{0x0048, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG},
|
||||
{0x5A5A, SIERRA_DEQ_ERRCMP_CTRL_PREG},
|
||||
{0x02F5, SIERRA_DEQ_OFFSET_CTRL_PREG},
|
||||
{0x02F5, SIERRA_DEQ_GAIN_CTRL_PREG},
|
||||
{0x9A8A, SIERRA_DEQ_VGATUNE_CTRL_PREG},
|
||||
{0x0014, SIERRA_DEQ_GLUT0},
|
||||
{0x0014, SIERRA_DEQ_GLUT1},
|
||||
{0x0014, SIERRA_DEQ_GLUT2},
|
||||
{0x0014, SIERRA_DEQ_GLUT3},
|
||||
{0x0014, SIERRA_DEQ_GLUT4},
|
||||
{0x0014, SIERRA_DEQ_GLUT5},
|
||||
{0x0014, SIERRA_DEQ_GLUT6},
|
||||
{0x0014, SIERRA_DEQ_GLUT7},
|
||||
{0x0014, SIERRA_DEQ_GLUT8},
|
||||
{0x0014, SIERRA_DEQ_GLUT9},
|
||||
{0x0014, SIERRA_DEQ_GLUT10},
|
||||
{0x0014, SIERRA_DEQ_GLUT11},
|
||||
{0x0014, SIERRA_DEQ_GLUT12},
|
||||
{0x0014, SIERRA_DEQ_GLUT13},
|
||||
{0x0014, SIERRA_DEQ_GLUT14},
|
||||
{0x0014, SIERRA_DEQ_GLUT15},
|
||||
{0x0014, SIERRA_DEQ_GLUT16},
|
||||
{0x0BAE, SIERRA_DEQ_ALUT0},
|
||||
{0x0AEB, SIERRA_DEQ_ALUT1},
|
||||
{0x0A28, SIERRA_DEQ_ALUT2},
|
||||
{0x0965, SIERRA_DEQ_ALUT3},
|
||||
{0x08A2, SIERRA_DEQ_ALUT4},
|
||||
{0x07DF, SIERRA_DEQ_ALUT5},
|
||||
{0x071C, SIERRA_DEQ_ALUT6},
|
||||
{0x0659, SIERRA_DEQ_ALUT7},
|
||||
{0x0596, SIERRA_DEQ_ALUT8},
|
||||
{0x0514, SIERRA_DEQ_ALUT9},
|
||||
{0x0492, SIERRA_DEQ_ALUT10},
|
||||
{0x0410, SIERRA_DEQ_ALUT11},
|
||||
{0x038E, SIERRA_DEQ_ALUT12},
|
||||
{0x030C, SIERRA_DEQ_ALUT13},
|
||||
{0x03F4, SIERRA_DEQ_DFETAP_CTRL_PREG},
|
||||
{0x0001, SIERRA_DFE_EN_1010_IGNORE_PREG},
|
||||
{0x3C01, SIERRA_DEQ_TAU_CTRL1_FAST_MAINT_PREG},
|
||||
{0x3C40, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG},
|
||||
{0x1C08, SIERRA_DEQ_TAU_CTRL2_PREG},
|
||||
{0x0033, SIERRA_DEQ_PICTRL_PREG},
|
||||
{0x0400, SIERRA_CPICAL_TMRVAL_MODE1_PREG},
|
||||
{0x0330, SIERRA_CPICAL_TMRVAL_MODE0_PREG},
|
||||
{0x01FF, SIERRA_CPICAL_PICNT_MODE1_PREG},
|
||||
{0x0009, SIERRA_CPI_OUTBUF_RATESEL_PREG},
|
||||
{0x3232, SIERRA_CPICAL_RES_STARTCODE_MODE23_PREG},
|
||||
{0x0005, SIERRA_LFPSDET_SUPPORT_PREG},
|
||||
{0x000F, SIERRA_LFPSFILT_NS_PREG},
|
||||
{0x0009, SIERRA_LFPSFILT_RD_PREG},
|
||||
{0x0001, SIERRA_LFPSFILT_MP_PREG},
|
||||
{0x8013, SIERRA_SDFILT_H2L_A_PREG},
|
||||
{0x8009, SIERRA_SDFILT_L2H_PREG},
|
||||
{0x0024, SIERRA_RXBUFFER_CTLECTRL_PREG},
|
||||
{0x0020, SIERRA_RXBUFFER_RCDFECTRL_PREG},
|
||||
{0x4243, SIERRA_RXBUFFER_DFECTRL_PREG}
|
||||
};
|
||||
|
||||
static const struct cdns_sierra_data cdns_map_sierra = {
|
||||
SIERRA_MACRO_ID,
|
||||
ARRAY_SIZE(cdns_pcie_regs),
|
||||
ARRAY_SIZE(cdns_usb_regs),
|
||||
cdns_pcie_regs,
|
||||
cdns_usb_regs
|
||||
0x2,
|
||||
0x2,
|
||||
ARRAY_SIZE(cdns_pcie_cmn_regs_ext_ssc),
|
||||
ARRAY_SIZE(cdns_pcie_ln_regs_ext_ssc),
|
||||
ARRAY_SIZE(cdns_usb_cmn_regs_ext_ssc),
|
||||
ARRAY_SIZE(cdns_usb_ln_regs_ext_ssc),
|
||||
cdns_pcie_cmn_regs_ext_ssc,
|
||||
cdns_pcie_ln_regs_ext_ssc,
|
||||
cdns_usb_cmn_regs_ext_ssc,
|
||||
cdns_usb_ln_regs_ext_ssc,
|
||||
};
|
||||
|
||||
static const struct cdns_sierra_data cdns_ti_map_sierra = {
|
||||
SIERRA_MACRO_ID,
|
||||
0x0,
|
||||
0x1,
|
||||
ARRAY_SIZE(cdns_pcie_cmn_regs_ext_ssc),
|
||||
ARRAY_SIZE(cdns_pcie_ln_regs_ext_ssc),
|
||||
ARRAY_SIZE(cdns_usb_cmn_regs_ext_ssc),
|
||||
ARRAY_SIZE(cdns_usb_ln_regs_ext_ssc),
|
||||
cdns_pcie_cmn_regs_ext_ssc,
|
||||
cdns_pcie_ln_regs_ext_ssc,
|
||||
cdns_usb_cmn_regs_ext_ssc,
|
||||
cdns_usb_ln_regs_ext_ssc,
|
||||
};
|
||||
|
||||
static const struct of_device_id cdns_sierra_id_table[] = {
|
||||
@@ -375,6 +812,10 @@ static const struct of_device_id cdns_sierra_id_table[] = {
|
||||
.compatible = "cdns,sierra-phy-t0",
|
||||
.data = &cdns_map_sierra,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,sierra-phy-t0",
|
||||
.data = &cdns_ti_map_sierra,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cdns_sierra_id_table);
|
||||
|
@@ -33,14 +33,14 @@ config PHY_HISTB_COMBPHY
|
||||
If unsure, say N.
|
||||
|
||||
config PHY_HISI_INNO_USB2
|
||||
tristate "HiSilicon INNO USB2 PHY support"
|
||||
depends on (ARCH_HISI && ARM64) || COMPILE_TEST
|
||||
select GENERIC_PHY
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Support for INNO USB2 PHY on HiSilicon SoCs. This Phy supports
|
||||
USB 1.5Mb/s, USB 12Mb/s, USB 480Mb/s speeds. It supports one
|
||||
USB host port to accept one USB device.
|
||||
tristate "HiSilicon INNO USB2 PHY support"
|
||||
depends on (ARCH_HISI && ARM64) || COMPILE_TEST
|
||||
select GENERIC_PHY
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Support for INNO USB2 PHY on HiSilicon SoCs. This Phy supports
|
||||
USB 1.5Mb/s, USB 12Mb/s, USB 480Mb/s speeds. It supports one
|
||||
USB host port to accept one USB device.
|
||||
|
||||
config PHY_HIX5HD2_SATA
|
||||
tristate "HIX5HD2 SATA PHY Driver"
|
||||
|
9
drivers/phy/intel/Kconfig
Normal file
9
drivers/phy/intel/Kconfig
Normal file
@@ -0,0 +1,9 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Phy drivers for Intel Lightning Mountain(LGM) platform
|
||||
#
|
||||
config PHY_INTEL_EMMC
|
||||
tristate "Intel EMMC PHY driver"
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Enable this to support the Intel EMMC PHY
|
2
drivers/phy/intel/Makefile
Normal file
2
drivers/phy/intel/Makefile
Normal file
@@ -0,0 +1,2 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_PHY_INTEL_EMMC) += phy-intel-emmc.o
|
284
drivers/phy/intel/phy-intel-emmc.c
Normal file
284
drivers/phy/intel/phy-intel-emmc.c
Normal file
@@ -0,0 +1,284 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Intel eMMC PHY driver
|
||||
* Copyright (C) 2019 Intel, Corp.
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* eMMC phy register definitions */
|
||||
#define EMMC_PHYCTRL0_REG 0xa8
|
||||
#define DR_TY_MASK GENMASK(30, 28)
|
||||
#define DR_TY_SHIFT(x) (((x) << 28) & DR_TY_MASK)
|
||||
#define OTAPDLYENA BIT(14)
|
||||
#define OTAPDLYSEL_MASK GENMASK(13, 10)
|
||||
#define OTAPDLYSEL_SHIFT(x) (((x) << 10) & OTAPDLYSEL_MASK)
|
||||
|
||||
#define EMMC_PHYCTRL1_REG 0xac
|
||||
#define PDB_MASK BIT(0)
|
||||
#define PDB_SHIFT(x) (((x) << 0) & PDB_MASK)
|
||||
#define ENDLL_MASK BIT(7)
|
||||
#define ENDLL_SHIFT(x) (((x) << 7) & ENDLL_MASK)
|
||||
|
||||
#define EMMC_PHYCTRL2_REG 0xb0
|
||||
#define FRQSEL_25M 0
|
||||
#define FRQSEL_50M 1
|
||||
#define FRQSEL_100M 2
|
||||
#define FRQSEL_150M 3
|
||||
#define FRQSEL_MASK GENMASK(24, 22)
|
||||
#define FRQSEL_SHIFT(x) (((x) << 22) & FRQSEL_MASK)
|
||||
|
||||
#define EMMC_PHYSTAT_REG 0xbc
|
||||
#define CALDONE_MASK BIT(9)
|
||||
#define DLLRDY_MASK BIT(8)
|
||||
#define IS_CALDONE(x) ((x) & CALDONE_MASK)
|
||||
#define IS_DLLRDY(x) ((x) & DLLRDY_MASK)
|
||||
|
||||
struct intel_emmc_phy {
|
||||
struct regmap *syscfg;
|
||||
struct clk *emmcclk;
|
||||
};
|
||||
|
||||
static int intel_emmc_phy_power(struct phy *phy, bool on_off)
|
||||
{
|
||||
struct intel_emmc_phy *priv = phy_get_drvdata(phy);
|
||||
unsigned int caldone;
|
||||
unsigned int dllrdy;
|
||||
unsigned int freqsel;
|
||||
unsigned long rate;
|
||||
int ret, quot;
|
||||
|
||||
/*
|
||||
* Keep phyctrl_pdb and phyctrl_endll low to allow
|
||||
* initialization of CALIO state M/C DFFs
|
||||
*/
|
||||
ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL1_REG, PDB_MASK,
|
||||
PDB_SHIFT(0));
|
||||
if (ret) {
|
||||
dev_err(&phy->dev, "CALIO power down bar failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Already finish power_off above */
|
||||
if (!on_off)
|
||||
return 0;
|
||||
|
||||
rate = clk_get_rate(priv->emmcclk);
|
||||
quot = DIV_ROUND_CLOSEST(rate, 50000000);
|
||||
if (quot > FRQSEL_150M)
|
||||
dev_warn(&phy->dev, "Unsupported rate: %lu\n", rate);
|
||||
freqsel = clamp_t(int, quot, FRQSEL_25M, FRQSEL_150M);
|
||||
|
||||
/*
|
||||
* According to the user manual, calpad calibration
|
||||
* cycle takes more than 2us without the minimal recommended
|
||||
* value, so we may need a little margin here
|
||||
*/
|
||||
udelay(5);
|
||||
|
||||
ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL1_REG, PDB_MASK,
|
||||
PDB_SHIFT(1));
|
||||
if (ret) {
|
||||
dev_err(&phy->dev, "CALIO power down bar failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* According to the user manual, it asks driver to wait 5us for
|
||||
* calpad busy trimming. However it is documented that this value is
|
||||
* PVT(A.K.A process,voltage and temperature) relevant, so some
|
||||
* failure cases are found which indicates we should be more tolerant
|
||||
* to calpad busy trimming.
|
||||
*/
|
||||
ret = regmap_read_poll_timeout(priv->syscfg, EMMC_PHYSTAT_REG,
|
||||
caldone, IS_CALDONE(caldone),
|
||||
0, 50);
|
||||
if (ret) {
|
||||
dev_err(&phy->dev, "caldone failed, ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set the frequency of the DLL operation */
|
||||
ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL2_REG, FRQSEL_MASK,
|
||||
FRQSEL_SHIFT(freqsel));
|
||||
if (ret) {
|
||||
dev_err(&phy->dev, "set the frequency of dll failed:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Turn on the DLL */
|
||||
ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL1_REG, ENDLL_MASK,
|
||||
ENDLL_SHIFT(1));
|
||||
if (ret) {
|
||||
dev_err(&phy->dev, "turn on the dll failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* After enabling analog DLL circuits docs say that we need 10.2 us if
|
||||
* our source clock is at 50 MHz and that lock time scales linearly
|
||||
* with clock speed. If we are powering on the PHY and the card clock
|
||||
* is super slow (like 100 kHZ) this could take as long as 5.1 ms as
|
||||
* per the math: 10.2 us * (50000000 Hz / 100000 Hz) => 5.1 ms
|
||||
* Hopefully we won't be running at 100 kHz, but we should still make
|
||||
* sure we wait long enough.
|
||||
*
|
||||
* NOTE: There appear to be corner cases where the DLL seems to take
|
||||
* extra long to lock for reasons that aren't understood. In some
|
||||
* extreme cases we've seen it take up to over 10ms (!). We'll be
|
||||
* generous and give it 50ms.
|
||||
*/
|
||||
ret = regmap_read_poll_timeout(priv->syscfg,
|
||||
EMMC_PHYSTAT_REG,
|
||||
dllrdy, IS_DLLRDY(dllrdy),
|
||||
0, 50 * USEC_PER_MSEC);
|
||||
if (ret) {
|
||||
dev_err(&phy->dev, "dllrdy failed. ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_emmc_phy_init(struct phy *phy)
|
||||
{
|
||||
struct intel_emmc_phy *priv = phy_get_drvdata(phy);
|
||||
|
||||
/*
|
||||
* We purposely get the clock here and not in probe to avoid the
|
||||
* circular dependency problem. We expect:
|
||||
* - PHY driver to probe
|
||||
* - SDHCI driver to start probe
|
||||
* - SDHCI driver to register it's clock
|
||||
* - SDHCI driver to get the PHY
|
||||
* - SDHCI driver to init the PHY
|
||||
*
|
||||
* The clock is optional, so upon any error just return it like
|
||||
* any other error to user.
|
||||
*
|
||||
*/
|
||||
priv->emmcclk = clk_get_optional(&phy->dev, "emmcclk");
|
||||
if (IS_ERR(priv->emmcclk)) {
|
||||
dev_err(&phy->dev, "ERROR: getting emmcclk\n");
|
||||
return PTR_ERR(priv->emmcclk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_emmc_phy_exit(struct phy *phy)
|
||||
{
|
||||
struct intel_emmc_phy *priv = phy_get_drvdata(phy);
|
||||
|
||||
clk_put(priv->emmcclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_emmc_phy_power_on(struct phy *phy)
|
||||
{
|
||||
struct intel_emmc_phy *priv = phy_get_drvdata(phy);
|
||||
int ret;
|
||||
|
||||
/* Drive impedance: 50 Ohm */
|
||||
ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL0_REG, DR_TY_MASK,
|
||||
DR_TY_SHIFT(6));
|
||||
if (ret) {
|
||||
dev_err(&phy->dev, "ERROR set drive-impednce-50ohm: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Output tap delay: disable */
|
||||
ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL0_REG, OTAPDLYENA,
|
||||
0);
|
||||
if (ret) {
|
||||
dev_err(&phy->dev, "ERROR Set output tap delay : %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Output tap delay */
|
||||
ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL0_REG,
|
||||
OTAPDLYSEL_MASK, OTAPDLYSEL_SHIFT(4));
|
||||
if (ret) {
|
||||
dev_err(&phy->dev, "ERROR: output tap dly select: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Power up eMMC phy analog blocks */
|
||||
return intel_emmc_phy_power(phy, true);
|
||||
}
|
||||
|
||||
static int intel_emmc_phy_power_off(struct phy *phy)
|
||||
{
|
||||
/* Power down eMMC phy analog blocks */
|
||||
return intel_emmc_phy_power(phy, false);
|
||||
}
|
||||
|
||||
static const struct phy_ops ops = {
|
||||
.init = intel_emmc_phy_init,
|
||||
.exit = intel_emmc_phy_exit,
|
||||
.power_on = intel_emmc_phy_power_on,
|
||||
.power_off = intel_emmc_phy_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int intel_emmc_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct intel_emmc_phy *priv;
|
||||
struct phy *generic_phy;
|
||||
struct phy_provider *phy_provider;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Get eMMC phy (accessed via chiptop) regmap */
|
||||
priv->syscfg = syscon_regmap_lookup_by_phandle(np, "intel,syscon");
|
||||
if (IS_ERR(priv->syscfg)) {
|
||||
dev_err(dev, "failed to find syscon\n");
|
||||
return PTR_ERR(priv->syscfg);
|
||||
}
|
||||
|
||||
generic_phy = devm_phy_create(dev, np, &ops);
|
||||
if (IS_ERR(generic_phy)) {
|
||||
dev_err(dev, "failed to create PHY\n");
|
||||
return PTR_ERR(generic_phy);
|
||||
}
|
||||
|
||||
phy_set_drvdata(generic_phy, priv);
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id intel_emmc_phy_dt_ids[] = {
|
||||
{ .compatible = "intel,lgm-emmc-phy" },
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, intel_emmc_phy_dt_ids);
|
||||
|
||||
static struct platform_driver intel_emmc_driver = {
|
||||
.probe = intel_emmc_phy_probe,
|
||||
.driver = {
|
||||
.name = "intel-emmc-phy",
|
||||
.of_match_table = intel_emmc_phy_dt_ids,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(intel_emmc_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Harliman Liem <peter.harliman.liem@intel.com>");
|
||||
MODULE_DESCRIPTION("Intel eMMC PHY driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@@ -386,7 +386,7 @@ static struct phy *ltq_vrx200_pcie_phy_xlate(struct device *dev,
|
||||
default:
|
||||
dev_err(dev, "invalid PHY mode %u\n", mode);
|
||||
return ERR_PTR(-EINVAL);
|
||||
};
|
||||
}
|
||||
|
||||
return priv->phy;
|
||||
}
|
||||
|
@@ -3,14 +3,13 @@
|
||||
# Phy drivers for Mediatek devices
|
||||
#
|
||||
config PHY_MTK_TPHY
|
||||
tristate "MediaTek T-PHY Driver"
|
||||
depends on ARCH_MEDIATEK && OF
|
||||
tristate "MediaTek T-PHY Driver"
|
||||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||
depends on OF
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Say 'Y' here to add support for MediaTek T-PHY driver,
|
||||
it supports multiple usb2.0, usb3.0 ports, PCIe and
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Say 'Y' here to add support for MediaTek T-PHY driver,
|
||||
it supports multiple usb2.0, usb3.0 ports, PCIe and
|
||||
SATA, and meanwhile supports two version T-PHY which have
|
||||
different banks layout, the T-PHY with shared banks between
|
||||
multi-ports is first version, otherwise is second veriosn,
|
||||
@@ -28,11 +27,11 @@ config PHY_MTK_UFS
|
||||
specified M-PHYs.
|
||||
|
||||
config PHY_MTK_XSPHY
|
||||
tristate "MediaTek XS-PHY Driver"
|
||||
tristate "MediaTek XS-PHY Driver"
|
||||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||
depends on OF
|
||||
select GENERIC_PHY
|
||||
help
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Enable this to support the SuperSpeedPlus XS-PHY transceiver for
|
||||
USB3.1 GEN2 controllers on MediaTek chips. The driver supports
|
||||
multiple USB2.0, USB3.1 GEN2 ports.
|
||||
|
@@ -29,7 +29,7 @@ static void devm_phy_release(struct device *dev, void *res)
|
||||
{
|
||||
struct phy *phy = *(struct phy **)res;
|
||||
|
||||
phy_put(phy);
|
||||
phy_put(dev, phy);
|
||||
}
|
||||
|
||||
static void devm_phy_provider_release(struct device *dev, void *res)
|
||||
@@ -566,12 +566,12 @@ struct phy *of_phy_get(struct device_node *np, const char *con_id)
|
||||
EXPORT_SYMBOL_GPL(of_phy_get);
|
||||
|
||||
/**
|
||||
* phy_put() - release the PHY
|
||||
* @phy: the phy returned by phy_get()
|
||||
* of_phy_put() - release the PHY
|
||||
* @phy: the phy returned by of_phy_get()
|
||||
*
|
||||
* Releases a refcount the caller received from phy_get().
|
||||
* Releases a refcount the caller received from of_phy_get().
|
||||
*/
|
||||
void phy_put(struct phy *phy)
|
||||
void of_phy_put(struct phy *phy)
|
||||
{
|
||||
if (!phy || IS_ERR(phy))
|
||||
return;
|
||||
@@ -584,6 +584,20 @@ void phy_put(struct phy *phy)
|
||||
module_put(phy->ops->owner);
|
||||
put_device(&phy->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_phy_put);
|
||||
|
||||
/**
|
||||
* phy_put() - release the PHY
|
||||
* @dev: device that wants to release this phy
|
||||
* @phy: the phy returned by phy_get()
|
||||
*
|
||||
* Releases a refcount the caller received from phy_get().
|
||||
*/
|
||||
void phy_put(struct device *dev, struct phy *phy)
|
||||
{
|
||||
device_link_remove(dev, &phy->dev);
|
||||
of_phy_put(phy);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phy_put);
|
||||
|
||||
/**
|
||||
@@ -651,6 +665,7 @@ struct phy *phy_get(struct device *dev, const char *string)
|
||||
{
|
||||
int index = 0;
|
||||
struct phy *phy;
|
||||
struct device_link *link;
|
||||
|
||||
if (string == NULL) {
|
||||
dev_WARN(dev, "missing string\n");
|
||||
@@ -672,6 +687,13 @@ struct phy *phy_get(struct device *dev, const char *string)
|
||||
|
||||
get_device(&phy->dev);
|
||||
|
||||
link = device_link_add(dev, &phy->dev, DL_FLAG_STATELESS);
|
||||
if (!link) {
|
||||
dev_err(dev, "failed to create device link to %s\n",
|
||||
dev_name(phy->dev.parent));
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
return phy;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phy_get);
|
||||
@@ -765,6 +787,7 @@ struct phy *devm_of_phy_get(struct device *dev, struct device_node *np,
|
||||
const char *con_id)
|
||||
{
|
||||
struct phy **ptr, *phy;
|
||||
struct device_link *link;
|
||||
|
||||
ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL);
|
||||
if (!ptr)
|
||||
@@ -776,6 +799,14 @@ struct phy *devm_of_phy_get(struct device *dev, struct device_node *np,
|
||||
devres_add(dev, ptr);
|
||||
} else {
|
||||
devres_free(ptr);
|
||||
return phy;
|
||||
}
|
||||
|
||||
link = device_link_add(dev, &phy->dev, DL_FLAG_STATELESS);
|
||||
if (!link) {
|
||||
dev_err(dev, "failed to create device link to %s\n",
|
||||
dev_name(phy->dev.parent));
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
return phy;
|
||||
@@ -798,6 +829,7 @@ struct phy *devm_of_phy_get_by_index(struct device *dev, struct device_node *np,
|
||||
int index)
|
||||
{
|
||||
struct phy **ptr, *phy;
|
||||
struct device_link *link;
|
||||
|
||||
ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL);
|
||||
if (!ptr)
|
||||
@@ -819,6 +851,13 @@ struct phy *devm_of_phy_get_by_index(struct device *dev, struct device_node *np,
|
||||
*ptr = phy;
|
||||
devres_add(dev, ptr);
|
||||
|
||||
link = device_link_add(dev, &phy->dev, DL_FLAG_STATELESS);
|
||||
if (!link) {
|
||||
dev_err(dev, "failed to create device link to %s\n",
|
||||
dev_name(phy->dev.parent));
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
return phy;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_of_phy_get_by_index);
|
||||
|
@@ -80,7 +80,7 @@ static int read_poll_timeout(void __iomem *addr, u32 mask)
|
||||
if (readl_relaxed(addr) & mask)
|
||||
return 0;
|
||||
|
||||
usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50);
|
||||
usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50);
|
||||
} while (!time_after(jiffies, timeout));
|
||||
|
||||
return (readl_relaxed(addr) & mask) ? 0 : -ETIMEDOUT;
|
||||
|
@@ -166,8 +166,9 @@ static const unsigned int sdm845_ufsphy_regs_layout[] = {
|
||||
};
|
||||
|
||||
static const unsigned int sm8150_ufsphy_regs_layout[] = {
|
||||
[QPHY_START_CTRL] = 0x00,
|
||||
[QPHY_PCS_READY_STATUS] = 0x180,
|
||||
[QPHY_START_CTRL] = QPHY_V4_PHY_START,
|
||||
[QPHY_PCS_READY_STATUS] = QPHY_V4_PCS_READY_STATUS,
|
||||
[QPHY_SW_RESET] = QPHY_V4_SW_RESET,
|
||||
};
|
||||
|
||||
static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = {
|
||||
@@ -885,7 +886,6 @@ static const struct qmp_phy_init_tbl msm8998_usb3_pcs_tbl[] = {
|
||||
};
|
||||
|
||||
static const struct qmp_phy_init_tbl sm8150_ufsphy_serdes_tbl[] = {
|
||||
QMP_PHY_INIT_CFG(QPHY_POWER_DOWN_CONTROL, 0x01),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0xd9),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x11),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_HS_SWITCH_SEL, 0x00),
|
||||
@@ -1390,7 +1390,6 @@ static const struct qmp_phy_cfg sm8150_ufsphy_cfg = {
|
||||
.pwrdn_ctrl = SW_PWRDN,
|
||||
|
||||
.is_dual_lane_phy = true,
|
||||
.no_pcs_sw_reset = true,
|
||||
};
|
||||
|
||||
static void qcom_qmp_phy_configure(void __iomem *base,
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2017, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
@@ -39,6 +39,7 @@ config PHY_ROCKCHIP_INNO_DSIDPHY
|
||||
tristate "Rockchip Innosilicon MIPI/LVDS/TTL PHY driver"
|
||||
depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF
|
||||
select GENERIC_PHY
|
||||
select GENERIC_PHY_MIPI_DPHY
|
||||
help
|
||||
Enable this to support the Rockchip MIPI/LVDS/TTL PHY with
|
||||
Innosilicon IP block.
|
||||
|
@@ -16,6 +16,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/phy/phy-mipi-dphy.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
|
||||
@@ -167,31 +168,6 @@
|
||||
#define DSI_PHY_STATUS 0xb0
|
||||
#define PHY_LOCK BIT(0)
|
||||
|
||||
struct mipi_dphy_timing {
|
||||
unsigned int clkmiss;
|
||||
unsigned int clkpost;
|
||||
unsigned int clkpre;
|
||||
unsigned int clkprepare;
|
||||
unsigned int clksettle;
|
||||
unsigned int clktermen;
|
||||
unsigned int clktrail;
|
||||
unsigned int clkzero;
|
||||
unsigned int dtermen;
|
||||
unsigned int eot;
|
||||
unsigned int hsexit;
|
||||
unsigned int hsprepare;
|
||||
unsigned int hszero;
|
||||
unsigned int hssettle;
|
||||
unsigned int hsskip;
|
||||
unsigned int hstrail;
|
||||
unsigned int init;
|
||||
unsigned int lpx;
|
||||
unsigned int taget;
|
||||
unsigned int tago;
|
||||
unsigned int tasure;
|
||||
unsigned int wakeup;
|
||||
};
|
||||
|
||||
struct inno_dsidphy {
|
||||
struct device *dev;
|
||||
struct clk *ref_clk;
|
||||
@@ -201,7 +177,9 @@ struct inno_dsidphy {
|
||||
void __iomem *host_base;
|
||||
struct reset_control *rst;
|
||||
enum phy_mode mode;
|
||||
struct phy_configure_opts_mipi_dphy dphy_cfg;
|
||||
|
||||
struct clk *pll_clk;
|
||||
struct {
|
||||
struct clk_hw hw;
|
||||
u8 prediv;
|
||||
@@ -238,37 +216,79 @@ static void phy_update_bits(struct inno_dsidphy *inno,
|
||||
writel(tmp, inno->phy_base + reg);
|
||||
}
|
||||
|
||||
static void mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing,
|
||||
unsigned long period)
|
||||
static unsigned long inno_dsidphy_pll_calc_rate(struct inno_dsidphy *inno,
|
||||
unsigned long rate)
|
||||
{
|
||||
/* Global Operation Timing Parameters */
|
||||
timing->clkmiss = 0;
|
||||
timing->clkpost = 70000 + 52 * period;
|
||||
timing->clkpre = 8 * period;
|
||||
timing->clkprepare = 65000;
|
||||
timing->clksettle = 95000;
|
||||
timing->clktermen = 0;
|
||||
timing->clktrail = 80000;
|
||||
timing->clkzero = 260000;
|
||||
timing->dtermen = 0;
|
||||
timing->eot = 0;
|
||||
timing->hsexit = 120000;
|
||||
timing->hsprepare = 65000 + 4 * period;
|
||||
timing->hszero = 145000 + 6 * period;
|
||||
timing->hssettle = 85000 + 6 * period;
|
||||
timing->hsskip = 40000;
|
||||
timing->hstrail = max(8 * period, 60000 + 4 * period);
|
||||
timing->init = 100000000;
|
||||
timing->lpx = 60000;
|
||||
timing->taget = 5 * timing->lpx;
|
||||
timing->tago = 4 * timing->lpx;
|
||||
timing->tasure = 2 * timing->lpx;
|
||||
timing->wakeup = 1000000000;
|
||||
unsigned long prate = clk_get_rate(inno->ref_clk);
|
||||
unsigned long best_freq = 0;
|
||||
unsigned long fref, fout;
|
||||
u8 min_prediv, max_prediv;
|
||||
u8 _prediv, best_prediv = 1;
|
||||
u16 _fbdiv, best_fbdiv = 1;
|
||||
u32 min_delta = UINT_MAX;
|
||||
|
||||
/*
|
||||
* The PLL output frequency can be calculated using a simple formula:
|
||||
* PLL_Output_Frequency = (FREF / PREDIV * FBDIV) / 2
|
||||
* PLL_Output_Frequency: it is equal to DDR-Clock-Frequency * 2
|
||||
*/
|
||||
fref = prate / 2;
|
||||
if (rate > 1000000000UL)
|
||||
fout = 1000000000UL;
|
||||
else
|
||||
fout = rate;
|
||||
|
||||
/* 5Mhz < Fref / prediv < 40MHz */
|
||||
min_prediv = DIV_ROUND_UP(fref, 40000000);
|
||||
max_prediv = fref / 5000000;
|
||||
|
||||
for (_prediv = min_prediv; _prediv <= max_prediv; _prediv++) {
|
||||
u64 tmp;
|
||||
u32 delta;
|
||||
|
||||
tmp = (u64)fout * _prediv;
|
||||
do_div(tmp, fref);
|
||||
_fbdiv = tmp;
|
||||
|
||||
/*
|
||||
* The possible settings of feedback divider are
|
||||
* 12, 13, 14, 16, ~ 511
|
||||
*/
|
||||
if (_fbdiv == 15)
|
||||
continue;
|
||||
|
||||
if (_fbdiv < 12 || _fbdiv > 511)
|
||||
continue;
|
||||
|
||||
tmp = (u64)_fbdiv * fref;
|
||||
do_div(tmp, _prediv);
|
||||
|
||||
delta = abs(fout - tmp);
|
||||
if (!delta) {
|
||||
best_prediv = _prediv;
|
||||
best_fbdiv = _fbdiv;
|
||||
best_freq = tmp;
|
||||
break;
|
||||
} else if (delta < min_delta) {
|
||||
best_prediv = _prediv;
|
||||
best_fbdiv = _fbdiv;
|
||||
best_freq = tmp;
|
||||
min_delta = delta;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_freq) {
|
||||
inno->pll.prediv = best_prediv;
|
||||
inno->pll.fbdiv = best_fbdiv;
|
||||
inno->pll.rate = best_freq;
|
||||
}
|
||||
|
||||
return best_freq;
|
||||
}
|
||||
|
||||
static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
|
||||
{
|
||||
struct mipi_dphy_timing gotp;
|
||||
struct phy_configure_opts_mipi_dphy *cfg = &inno->dphy_cfg;
|
||||
const struct {
|
||||
unsigned long rate;
|
||||
u8 hs_prepare;
|
||||
@@ -288,12 +308,14 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
|
||||
{ 800000000, 0x21, 0x1f, 0x09, 0x29},
|
||||
{1000000000, 0x09, 0x20, 0x09, 0x27},
|
||||
};
|
||||
u32 t_txbyteclkhs, t_txclkesc, ui;
|
||||
u32 t_txbyteclkhs, t_txclkesc;
|
||||
u32 txbyteclkhs, txclkesc, esc_clk_div;
|
||||
u32 hs_exit, clk_post, clk_pre, wakeup, lpx, ta_go, ta_sure, ta_wait;
|
||||
u32 hs_prepare, hs_trail, hs_zero, clk_lane_hs_zero, data_lane_hs_zero;
|
||||
unsigned int i;
|
||||
|
||||
inno_dsidphy_pll_calc_rate(inno, cfg->hs_clk_rate);
|
||||
|
||||
/* Select MIPI mode */
|
||||
phy_update_bits(inno, REGISTER_PART_LVDS, 0x03,
|
||||
MODE_ENABLE_MASK, MIPI_MODE_ENABLE);
|
||||
@@ -328,32 +350,27 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
|
||||
txclkesc = txbyteclkhs / esc_clk_div;
|
||||
t_txclkesc = div_u64(PSEC_PER_SEC, txclkesc);
|
||||
|
||||
ui = div_u64(PSEC_PER_SEC, inno->pll.rate);
|
||||
|
||||
memset(&gotp, 0, sizeof(gotp));
|
||||
mipi_dphy_timing_get_default(&gotp, ui);
|
||||
|
||||
/*
|
||||
* The value of counter for HS Ths-exit
|
||||
* Ths-exit = Tpin_txbyteclkhs * value
|
||||
*/
|
||||
hs_exit = DIV_ROUND_UP(gotp.hsexit, t_txbyteclkhs);
|
||||
hs_exit = DIV_ROUND_UP(cfg->hs_exit, t_txbyteclkhs);
|
||||
/*
|
||||
* The value of counter for HS Tclk-post
|
||||
* Tclk-post = Tpin_txbyteclkhs * value
|
||||
*/
|
||||
clk_post = DIV_ROUND_UP(gotp.clkpost, t_txbyteclkhs);
|
||||
clk_post = DIV_ROUND_UP(cfg->clk_post, t_txbyteclkhs);
|
||||
/*
|
||||
* The value of counter for HS Tclk-pre
|
||||
* Tclk-pre = Tpin_txbyteclkhs * value
|
||||
*/
|
||||
clk_pre = DIV_ROUND_UP(gotp.clkpre, t_txbyteclkhs);
|
||||
clk_pre = DIV_ROUND_UP(cfg->clk_pre, t_txbyteclkhs);
|
||||
|
||||
/*
|
||||
* The value of counter for HS Tlpx Time
|
||||
* Tlpx = Tpin_txbyteclkhs * (2 + value)
|
||||
*/
|
||||
lpx = DIV_ROUND_UP(gotp.lpx, t_txbyteclkhs);
|
||||
lpx = DIV_ROUND_UP(cfg->lpx, t_txbyteclkhs);
|
||||
if (lpx >= 2)
|
||||
lpx -= 2;
|
||||
|
||||
@@ -362,19 +379,19 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
|
||||
* Tta-go for turnaround
|
||||
* Tta-go = Ttxclkesc * value
|
||||
*/
|
||||
ta_go = DIV_ROUND_UP(gotp.tago, t_txclkesc);
|
||||
ta_go = DIV_ROUND_UP(cfg->ta_go, t_txclkesc);
|
||||
/*
|
||||
* The value of counter for HS Tta-sure
|
||||
* Tta-sure for turnaround
|
||||
* Tta-sure = Ttxclkesc * value
|
||||
*/
|
||||
ta_sure = DIV_ROUND_UP(gotp.tasure, t_txclkesc);
|
||||
ta_sure = DIV_ROUND_UP(cfg->ta_sure, t_txclkesc);
|
||||
/*
|
||||
* The value of counter for HS Tta-wait
|
||||
* Tta-wait for turnaround
|
||||
* Tta-wait = Ttxclkesc * value
|
||||
*/
|
||||
ta_wait = DIV_ROUND_UP(gotp.taget, t_txclkesc);
|
||||
ta_wait = DIV_ROUND_UP(cfg->ta_get, t_txclkesc);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(timings); i++)
|
||||
if (inno->pll.rate <= timings[i].rate)
|
||||
@@ -479,6 +496,7 @@ static int inno_dsidphy_power_on(struct phy *phy)
|
||||
struct inno_dsidphy *inno = phy_get_drvdata(phy);
|
||||
|
||||
clk_prepare_enable(inno->pclk_phy);
|
||||
clk_prepare_enable(inno->ref_clk);
|
||||
pm_runtime_get_sync(inno->dev);
|
||||
|
||||
/* Bandgap power on */
|
||||
@@ -524,6 +542,7 @@ static int inno_dsidphy_power_off(struct phy *phy)
|
||||
LVDS_PLL_POWER_OFF | LVDS_BANDGAP_POWER_DOWN);
|
||||
|
||||
pm_runtime_put(inno->dev);
|
||||
clk_disable_unprepare(inno->ref_clk);
|
||||
clk_disable_unprepare(inno->pclk_phy);
|
||||
|
||||
return 0;
|
||||
@@ -546,168 +565,32 @@ static int inno_dsidphy_set_mode(struct phy *phy, enum phy_mode mode,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int inno_dsidphy_configure(struct phy *phy,
|
||||
union phy_configure_opts *opts)
|
||||
{
|
||||
struct inno_dsidphy *inno = phy_get_drvdata(phy);
|
||||
int ret;
|
||||
|
||||
if (inno->mode != PHY_MODE_MIPI_DPHY)
|
||||
return -EINVAL;
|
||||
|
||||
ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
memcpy(&inno->dphy_cfg, &opts->mipi_dphy, sizeof(inno->dphy_cfg));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct phy_ops inno_dsidphy_ops = {
|
||||
.configure = inno_dsidphy_configure,
|
||||
.set_mode = inno_dsidphy_set_mode,
|
||||
.power_on = inno_dsidphy_power_on,
|
||||
.power_off = inno_dsidphy_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static unsigned long inno_dsidphy_pll_round_rate(struct inno_dsidphy *inno,
|
||||
unsigned long prate,
|
||||
unsigned long rate,
|
||||
u8 *prediv, u16 *fbdiv)
|
||||
{
|
||||
unsigned long best_freq = 0;
|
||||
unsigned long fref, fout;
|
||||
u8 min_prediv, max_prediv;
|
||||
u8 _prediv, best_prediv = 1;
|
||||
u16 _fbdiv, best_fbdiv = 1;
|
||||
u32 min_delta = UINT_MAX;
|
||||
|
||||
/*
|
||||
* The PLL output frequency can be calculated using a simple formula:
|
||||
* PLL_Output_Frequency = (FREF / PREDIV * FBDIV) / 2
|
||||
* PLL_Output_Frequency: it is equal to DDR-Clock-Frequency * 2
|
||||
*/
|
||||
fref = prate / 2;
|
||||
if (rate > 1000000000UL)
|
||||
fout = 1000000000UL;
|
||||
else
|
||||
fout = rate;
|
||||
|
||||
/* 5Mhz < Fref / prediv < 40MHz */
|
||||
min_prediv = DIV_ROUND_UP(fref, 40000000);
|
||||
max_prediv = fref / 5000000;
|
||||
|
||||
for (_prediv = min_prediv; _prediv <= max_prediv; _prediv++) {
|
||||
u64 tmp;
|
||||
u32 delta;
|
||||
|
||||
tmp = (u64)fout * _prediv;
|
||||
do_div(tmp, fref);
|
||||
_fbdiv = tmp;
|
||||
|
||||
/*
|
||||
* The possible settings of feedback divider are
|
||||
* 12, 13, 14, 16, ~ 511
|
||||
*/
|
||||
if (_fbdiv == 15)
|
||||
continue;
|
||||
|
||||
if (_fbdiv < 12 || _fbdiv > 511)
|
||||
continue;
|
||||
|
||||
tmp = (u64)_fbdiv * fref;
|
||||
do_div(tmp, _prediv);
|
||||
|
||||
delta = abs(fout - tmp);
|
||||
if (!delta) {
|
||||
best_prediv = _prediv;
|
||||
best_fbdiv = _fbdiv;
|
||||
best_freq = tmp;
|
||||
break;
|
||||
} else if (delta < min_delta) {
|
||||
best_prediv = _prediv;
|
||||
best_fbdiv = _fbdiv;
|
||||
best_freq = tmp;
|
||||
min_delta = delta;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_freq) {
|
||||
*prediv = best_prediv;
|
||||
*fbdiv = best_fbdiv;
|
||||
}
|
||||
|
||||
return best_freq;
|
||||
}
|
||||
|
||||
static long inno_dsidphy_pll_clk_round_rate(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
struct inno_dsidphy *inno = hw_to_inno(hw);
|
||||
unsigned long fout;
|
||||
u16 fbdiv = 1;
|
||||
u8 prediv = 1;
|
||||
|
||||
fout = inno_dsidphy_pll_round_rate(inno, *prate, rate,
|
||||
&prediv, &fbdiv);
|
||||
|
||||
return fout;
|
||||
}
|
||||
|
||||
static int inno_dsidphy_pll_clk_set_rate(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct inno_dsidphy *inno = hw_to_inno(hw);
|
||||
unsigned long fout;
|
||||
u16 fbdiv = 1;
|
||||
u8 prediv = 1;
|
||||
|
||||
fout = inno_dsidphy_pll_round_rate(inno, parent_rate, rate,
|
||||
&prediv, &fbdiv);
|
||||
|
||||
dev_dbg(inno->dev, "fin=%lu, fout=%lu, prediv=%u, fbdiv=%u\n",
|
||||
parent_rate, fout, prediv, fbdiv);
|
||||
|
||||
inno->pll.prediv = prediv;
|
||||
inno->pll.fbdiv = fbdiv;
|
||||
inno->pll.rate = fout;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
inno_dsidphy_pll_clk_recalc_rate(struct clk_hw *hw, unsigned long prate)
|
||||
{
|
||||
struct inno_dsidphy *inno = hw_to_inno(hw);
|
||||
|
||||
/* PLL_Output_Frequency = (FREF / PREDIV * FBDIV) / 2 */
|
||||
return (prate / inno->pll.prediv * inno->pll.fbdiv) / 2;
|
||||
}
|
||||
|
||||
static const struct clk_ops inno_dsidphy_pll_clk_ops = {
|
||||
.round_rate = inno_dsidphy_pll_clk_round_rate,
|
||||
.set_rate = inno_dsidphy_pll_clk_set_rate,
|
||||
.recalc_rate = inno_dsidphy_pll_clk_recalc_rate,
|
||||
};
|
||||
|
||||
static int inno_dsidphy_pll_register(struct inno_dsidphy *inno)
|
||||
{
|
||||
struct device *dev = inno->dev;
|
||||
struct clk *clk;
|
||||
const char *parent_name;
|
||||
struct clk_init_data init;
|
||||
int ret;
|
||||
|
||||
parent_name = __clk_get_name(inno->ref_clk);
|
||||
|
||||
init.name = "mipi_dphy_pll";
|
||||
ret = of_property_read_string(dev->of_node, "clock-output-names",
|
||||
&init.name);
|
||||
if (ret < 0)
|
||||
dev_dbg(dev, "phy should set clock-output-names property\n");
|
||||
|
||||
init.ops = &inno_dsidphy_pll_clk_ops;
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
init.flags = 0;
|
||||
|
||||
inno->pll.hw.init = &init;
|
||||
clk = devm_clk_register(dev, &inno->pll.hw);
|
||||
if (IS_ERR(clk)) {
|
||||
ret = PTR_ERR(clk);
|
||||
dev_err(dev, "failed to register PLL: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
|
||||
&inno->pll.hw);
|
||||
}
|
||||
|
||||
static int inno_dsidphy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@@ -764,10 +647,6 @@ static int inno_dsidphy_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = inno_dsidphy_pll_register(inno);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
return 0;
|
||||
|
@@ -33,6 +33,21 @@ config PHY_AM654_SERDES
|
||||
This option enables support for TI AM654 SerDes PHY used for
|
||||
PCIe.
|
||||
|
||||
config PHY_J721E_WIZ
|
||||
tristate "TI J721E WIZ (SERDES Wrapper) support"
|
||||
depends on OF && ARCH_K3 || COMPILE_TEST
|
||||
depends on COMMON_CLK
|
||||
select GENERIC_PHY
|
||||
select MULTIPLEXER
|
||||
select REGMAP_MMIO
|
||||
select MUX_MMIO
|
||||
help
|
||||
This option enables support for WIZ module present in TI's J721E
|
||||
SoC. WIZ is a serdes wrapper used to configure some of the input
|
||||
signals to the SERDES (Sierra/Torrent). This driver configures
|
||||
three clock selects (pll0, pll1, dig) and resets for each of the
|
||||
lanes.
|
||||
|
||||
config OMAP_CONTROL_PHY
|
||||
tristate "OMAP CONTROL PHY Driver"
|
||||
depends on ARCH_OMAP2PLUS || COMPILE_TEST
|
||||
|
@@ -8,3 +8,4 @@ obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o
|
||||
obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
|
||||
obj-$(CONFIG_PHY_AM654_SERDES) += phy-am654-serdes.o
|
||||
obj-$(CONFIG_PHY_TI_GMII_SEL) += phy-gmii-sel.o
|
||||
obj-$(CONFIG_PHY_J721E_WIZ) += phy-j721e-wiz.o
|
||||
|
959
drivers/phy/ti/phy-j721e-wiz.c
Normal file
959
drivers/phy/ti/phy-j721e-wiz.c
Normal file
@@ -0,0 +1,959 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/**
|
||||
* Wrapper driver for SERDES used in J721E
|
||||
*
|
||||
* Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Author: Kishon Vijay Abraham I <kishon@ti.com>
|
||||
*/
|
||||
|
||||
#include <dt-bindings/phy/phy.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mux/consumer.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset-controller.h>
|
||||
|
||||
#define WIZ_SERDES_CTRL 0x404
|
||||
#define WIZ_SERDES_TOP_CTRL 0x408
|
||||
#define WIZ_SERDES_RST 0x40c
|
||||
#define WIZ_SERDES_TYPEC 0x410
|
||||
#define WIZ_LANECTL(n) (0x480 + (0x40 * (n)))
|
||||
|
||||
#define WIZ_MAX_LANES 4
|
||||
#define WIZ_MUX_NUM_CLOCKS 3
|
||||
#define WIZ_DIV_NUM_CLOCKS_16G 2
|
||||
#define WIZ_DIV_NUM_CLOCKS_10G 1
|
||||
|
||||
#define WIZ_SERDES_TYPEC_LN10_SWAP BIT(30)
|
||||
|
||||
enum wiz_lane_standard_mode {
|
||||
LANE_MODE_GEN1,
|
||||
LANE_MODE_GEN2,
|
||||
LANE_MODE_GEN3,
|
||||
LANE_MODE_GEN4,
|
||||
};
|
||||
|
||||
enum wiz_refclk_mux_sel {
|
||||
PLL0_REFCLK,
|
||||
PLL1_REFCLK,
|
||||
REFCLK_DIG,
|
||||
};
|
||||
|
||||
enum wiz_refclk_div_sel {
|
||||
CMN_REFCLK_DIG_DIV,
|
||||
CMN_REFCLK1_DIG_DIV,
|
||||
};
|
||||
|
||||
static const struct reg_field por_en = REG_FIELD(WIZ_SERDES_CTRL, 31, 31);
|
||||
static const struct reg_field phy_reset_n = REG_FIELD(WIZ_SERDES_RST, 31, 31);
|
||||
static const struct reg_field pll1_refclk_mux_sel =
|
||||
REG_FIELD(WIZ_SERDES_RST, 29, 29);
|
||||
static const struct reg_field pll0_refclk_mux_sel =
|
||||
REG_FIELD(WIZ_SERDES_RST, 28, 28);
|
||||
static const struct reg_field refclk_dig_sel_16g =
|
||||
REG_FIELD(WIZ_SERDES_RST, 24, 25);
|
||||
static const struct reg_field refclk_dig_sel_10g =
|
||||
REG_FIELD(WIZ_SERDES_RST, 24, 24);
|
||||
static const struct reg_field pma_cmn_refclk_int_mode =
|
||||
REG_FIELD(WIZ_SERDES_TOP_CTRL, 28, 29);
|
||||
static const struct reg_field pma_cmn_refclk_mode =
|
||||
REG_FIELD(WIZ_SERDES_TOP_CTRL, 30, 31);
|
||||
static const struct reg_field pma_cmn_refclk_dig_div =
|
||||
REG_FIELD(WIZ_SERDES_TOP_CTRL, 26, 27);
|
||||
static const struct reg_field pma_cmn_refclk1_dig_div =
|
||||
REG_FIELD(WIZ_SERDES_TOP_CTRL, 24, 25);
|
||||
|
||||
static const struct reg_field p_enable[WIZ_MAX_LANES] = {
|
||||
REG_FIELD(WIZ_LANECTL(0), 30, 31),
|
||||
REG_FIELD(WIZ_LANECTL(1), 30, 31),
|
||||
REG_FIELD(WIZ_LANECTL(2), 30, 31),
|
||||
REG_FIELD(WIZ_LANECTL(3), 30, 31),
|
||||
};
|
||||
|
||||
static const struct reg_field p_align[WIZ_MAX_LANES] = {
|
||||
REG_FIELD(WIZ_LANECTL(0), 29, 29),
|
||||
REG_FIELD(WIZ_LANECTL(1), 29, 29),
|
||||
REG_FIELD(WIZ_LANECTL(2), 29, 29),
|
||||
REG_FIELD(WIZ_LANECTL(3), 29, 29),
|
||||
};
|
||||
|
||||
static const struct reg_field p_raw_auto_start[WIZ_MAX_LANES] = {
|
||||
REG_FIELD(WIZ_LANECTL(0), 28, 28),
|
||||
REG_FIELD(WIZ_LANECTL(1), 28, 28),
|
||||
REG_FIELD(WIZ_LANECTL(2), 28, 28),
|
||||
REG_FIELD(WIZ_LANECTL(3), 28, 28),
|
||||
};
|
||||
|
||||
static const struct reg_field p_standard_mode[WIZ_MAX_LANES] = {
|
||||
REG_FIELD(WIZ_LANECTL(0), 24, 25),
|
||||
REG_FIELD(WIZ_LANECTL(1), 24, 25),
|
||||
REG_FIELD(WIZ_LANECTL(2), 24, 25),
|
||||
REG_FIELD(WIZ_LANECTL(3), 24, 25),
|
||||
};
|
||||
|
||||
static const struct reg_field typec_ln10_swap =
|
||||
REG_FIELD(WIZ_SERDES_TYPEC, 30, 30);
|
||||
|
||||
struct wiz_clk_mux {
|
||||
struct clk_hw hw;
|
||||
struct regmap_field *field;
|
||||
u32 *table;
|
||||
struct clk_init_data clk_data;
|
||||
};
|
||||
|
||||
#define to_wiz_clk_mux(_hw) container_of(_hw, struct wiz_clk_mux, hw)
|
||||
|
||||
struct wiz_clk_divider {
|
||||
struct clk_hw hw;
|
||||
struct regmap_field *field;
|
||||
struct clk_div_table *table;
|
||||
struct clk_init_data clk_data;
|
||||
};
|
||||
|
||||
#define to_wiz_clk_div(_hw) container_of(_hw, struct wiz_clk_divider, hw)
|
||||
|
||||
struct wiz_clk_mux_sel {
|
||||
struct regmap_field *field;
|
||||
u32 table[4];
|
||||
const char *node_name;
|
||||
};
|
||||
|
||||
struct wiz_clk_div_sel {
|
||||
struct regmap_field *field;
|
||||
struct clk_div_table *table;
|
||||
const char *node_name;
|
||||
};
|
||||
|
||||
static struct wiz_clk_mux_sel clk_mux_sel_16g[] = {
|
||||
{
|
||||
/*
|
||||
* Mux value to be configured for each of the input clocks
|
||||
* in the order populated in device tree
|
||||
*/
|
||||
.table = { 1, 0 },
|
||||
.node_name = "pll0-refclk",
|
||||
},
|
||||
{
|
||||
.table = { 1, 0 },
|
||||
.node_name = "pll1-refclk",
|
||||
},
|
||||
{
|
||||
.table = { 1, 3, 0, 2 },
|
||||
.node_name = "refclk-dig",
|
||||
},
|
||||
};
|
||||
|
||||
static struct wiz_clk_mux_sel clk_mux_sel_10g[] = {
|
||||
{
|
||||
/*
|
||||
* Mux value to be configured for each of the input clocks
|
||||
* in the order populated in device tree
|
||||
*/
|
||||
.table = { 1, 0 },
|
||||
.node_name = "pll0-refclk",
|
||||
},
|
||||
{
|
||||
.table = { 1, 0 },
|
||||
.node_name = "pll1-refclk",
|
||||
},
|
||||
{
|
||||
.table = { 1, 0 },
|
||||
.node_name = "refclk-dig",
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_div_table clk_div_table[] = {
|
||||
{ .val = 0, .div = 1, },
|
||||
{ .val = 1, .div = 2, },
|
||||
{ .val = 2, .div = 4, },
|
||||
{ .val = 3, .div = 8, },
|
||||
};
|
||||
|
||||
static struct wiz_clk_div_sel clk_div_sel[] = {
|
||||
{
|
||||
.table = clk_div_table,
|
||||
.node_name = "cmn-refclk-dig-div",
|
||||
},
|
||||
{
|
||||
.table = clk_div_table,
|
||||
.node_name = "cmn-refclk1-dig-div",
|
||||
},
|
||||
};
|
||||
|
||||
enum wiz_type {
|
||||
J721E_WIZ_16G,
|
||||
J721E_WIZ_10G,
|
||||
};
|
||||
|
||||
#define WIZ_TYPEC_DIR_DEBOUNCE_MIN 100 /* ms */
|
||||
#define WIZ_TYPEC_DIR_DEBOUNCE_MAX 1000
|
||||
|
||||
struct wiz {
|
||||
struct regmap *regmap;
|
||||
enum wiz_type type;
|
||||
struct wiz_clk_mux_sel *clk_mux_sel;
|
||||
struct wiz_clk_div_sel *clk_div_sel;
|
||||
unsigned int clk_div_sel_num;
|
||||
struct regmap_field *por_en;
|
||||
struct regmap_field *phy_reset_n;
|
||||
struct regmap_field *p_enable[WIZ_MAX_LANES];
|
||||
struct regmap_field *p_align[WIZ_MAX_LANES];
|
||||
struct regmap_field *p_raw_auto_start[WIZ_MAX_LANES];
|
||||
struct regmap_field *p_standard_mode[WIZ_MAX_LANES];
|
||||
struct regmap_field *pma_cmn_refclk_int_mode;
|
||||
struct regmap_field *pma_cmn_refclk_mode;
|
||||
struct regmap_field *pma_cmn_refclk_dig_div;
|
||||
struct regmap_field *pma_cmn_refclk1_dig_div;
|
||||
struct regmap_field *typec_ln10_swap;
|
||||
|
||||
struct device *dev;
|
||||
u32 num_lanes;
|
||||
struct platform_device *serdes_pdev;
|
||||
struct reset_controller_dev wiz_phy_reset_dev;
|
||||
struct gpio_desc *gpio_typec_dir;
|
||||
int typec_dir_delay;
|
||||
};
|
||||
|
||||
static int wiz_reset(struct wiz *wiz)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_field_write(wiz->por_en, 0x1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mdelay(1);
|
||||
|
||||
ret = regmap_field_write(wiz->por_en, 0x0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wiz_mode_select(struct wiz *wiz)
|
||||
{
|
||||
u32 num_lanes = wiz->num_lanes;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_lanes; i++) {
|
||||
ret = regmap_field_write(wiz->p_standard_mode[i],
|
||||
LANE_MODE_GEN4);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wiz_init_raw_interface(struct wiz *wiz, bool enable)
|
||||
{
|
||||
u32 num_lanes = wiz->num_lanes;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < num_lanes; i++) {
|
||||
ret = regmap_field_write(wiz->p_align[i], enable);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_field_write(wiz->p_raw_auto_start[i], enable);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wiz_init(struct wiz *wiz)
|
||||
{
|
||||
struct device *dev = wiz->dev;
|
||||
int ret;
|
||||
|
||||
ret = wiz_reset(wiz);
|
||||
if (ret) {
|
||||
dev_err(dev, "WIZ reset failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = wiz_mode_select(wiz);
|
||||
if (ret) {
|
||||
dev_err(dev, "WIZ mode select failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = wiz_init_raw_interface(wiz, true);
|
||||
if (ret) {
|
||||
dev_err(dev, "WIZ interface initialization failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wiz_regfield_init(struct wiz *wiz)
|
||||
{
|
||||
struct wiz_clk_mux_sel *clk_mux_sel;
|
||||
struct wiz_clk_div_sel *clk_div_sel;
|
||||
struct regmap *regmap = wiz->regmap;
|
||||
int num_lanes = wiz->num_lanes;
|
||||
struct device *dev = wiz->dev;
|
||||
int i;
|
||||
|
||||
wiz->por_en = devm_regmap_field_alloc(dev, regmap, por_en);
|
||||
if (IS_ERR(wiz->por_en)) {
|
||||
dev_err(dev, "POR_EN reg field init failed\n");
|
||||
return PTR_ERR(wiz->por_en);
|
||||
}
|
||||
|
||||
wiz->phy_reset_n = devm_regmap_field_alloc(dev, regmap,
|
||||
phy_reset_n);
|
||||
if (IS_ERR(wiz->phy_reset_n)) {
|
||||
dev_err(dev, "PHY_RESET_N reg field init failed\n");
|
||||
return PTR_ERR(wiz->phy_reset_n);
|
||||
}
|
||||
|
||||
wiz->pma_cmn_refclk_int_mode =
|
||||
devm_regmap_field_alloc(dev, regmap, pma_cmn_refclk_int_mode);
|
||||
if (IS_ERR(wiz->pma_cmn_refclk_int_mode)) {
|
||||
dev_err(dev, "PMA_CMN_REFCLK_INT_MODE reg field init failed\n");
|
||||
return PTR_ERR(wiz->pma_cmn_refclk_int_mode);
|
||||
}
|
||||
|
||||
wiz->pma_cmn_refclk_mode =
|
||||
devm_regmap_field_alloc(dev, regmap, pma_cmn_refclk_mode);
|
||||
if (IS_ERR(wiz->pma_cmn_refclk_mode)) {
|
||||
dev_err(dev, "PMA_CMN_REFCLK_MODE reg field init failed\n");
|
||||
return PTR_ERR(wiz->pma_cmn_refclk_mode);
|
||||
}
|
||||
|
||||
clk_div_sel = &wiz->clk_div_sel[CMN_REFCLK_DIG_DIV];
|
||||
clk_div_sel->field = devm_regmap_field_alloc(dev, regmap,
|
||||
pma_cmn_refclk_dig_div);
|
||||
if (IS_ERR(clk_div_sel->field)) {
|
||||
dev_err(dev, "PMA_CMN_REFCLK_DIG_DIV reg field init failed\n");
|
||||
return PTR_ERR(clk_div_sel->field);
|
||||
}
|
||||
|
||||
if (wiz->type == J721E_WIZ_16G) {
|
||||
clk_div_sel = &wiz->clk_div_sel[CMN_REFCLK1_DIG_DIV];
|
||||
clk_div_sel->field =
|
||||
devm_regmap_field_alloc(dev, regmap,
|
||||
pma_cmn_refclk1_dig_div);
|
||||
if (IS_ERR(clk_div_sel->field)) {
|
||||
dev_err(dev, "PMA_CMN_REFCLK1_DIG_DIV reg field init failed\n");
|
||||
return PTR_ERR(clk_div_sel->field);
|
||||
}
|
||||
}
|
||||
|
||||
clk_mux_sel = &wiz->clk_mux_sel[PLL0_REFCLK];
|
||||
clk_mux_sel->field = devm_regmap_field_alloc(dev, regmap,
|
||||
pll0_refclk_mux_sel);
|
||||
if (IS_ERR(clk_mux_sel->field)) {
|
||||
dev_err(dev, "PLL0_REFCLK_SEL reg field init failed\n");
|
||||
return PTR_ERR(clk_mux_sel->field);
|
||||
}
|
||||
|
||||
clk_mux_sel = &wiz->clk_mux_sel[PLL1_REFCLK];
|
||||
clk_mux_sel->field = devm_regmap_field_alloc(dev, regmap,
|
||||
pll1_refclk_mux_sel);
|
||||
if (IS_ERR(clk_mux_sel->field)) {
|
||||
dev_err(dev, "PLL1_REFCLK_SEL reg field init failed\n");
|
||||
return PTR_ERR(clk_mux_sel->field);
|
||||
}
|
||||
|
||||
clk_mux_sel = &wiz->clk_mux_sel[REFCLK_DIG];
|
||||
if (wiz->type == J721E_WIZ_10G)
|
||||
clk_mux_sel->field =
|
||||
devm_regmap_field_alloc(dev, regmap,
|
||||
refclk_dig_sel_10g);
|
||||
else
|
||||
clk_mux_sel->field =
|
||||
devm_regmap_field_alloc(dev, regmap,
|
||||
refclk_dig_sel_16g);
|
||||
|
||||
if (IS_ERR(clk_mux_sel->field)) {
|
||||
dev_err(dev, "REFCLK_DIG_SEL reg field init failed\n");
|
||||
return PTR_ERR(clk_mux_sel->field);
|
||||
}
|
||||
|
||||
for (i = 0; i < num_lanes; i++) {
|
||||
wiz->p_enable[i] = devm_regmap_field_alloc(dev, regmap,
|
||||
p_enable[i]);
|
||||
if (IS_ERR(wiz->p_enable[i])) {
|
||||
dev_err(dev, "P%d_ENABLE reg field init failed\n", i);
|
||||
return PTR_ERR(wiz->p_enable[i]);
|
||||
}
|
||||
|
||||
wiz->p_align[i] = devm_regmap_field_alloc(dev, regmap,
|
||||
p_align[i]);
|
||||
if (IS_ERR(wiz->p_align[i])) {
|
||||
dev_err(dev, "P%d_ALIGN reg field init failed\n", i);
|
||||
return PTR_ERR(wiz->p_align[i]);
|
||||
}
|
||||
|
||||
wiz->p_raw_auto_start[i] =
|
||||
devm_regmap_field_alloc(dev, regmap, p_raw_auto_start[i]);
|
||||
if (IS_ERR(wiz->p_raw_auto_start[i])) {
|
||||
dev_err(dev, "P%d_RAW_AUTO_START reg field init fail\n",
|
||||
i);
|
||||
return PTR_ERR(wiz->p_raw_auto_start[i]);
|
||||
}
|
||||
|
||||
wiz->p_standard_mode[i] =
|
||||
devm_regmap_field_alloc(dev, regmap, p_standard_mode[i]);
|
||||
if (IS_ERR(wiz->p_standard_mode[i])) {
|
||||
dev_err(dev, "P%d_STANDARD_MODE reg field init fail\n",
|
||||
i);
|
||||
return PTR_ERR(wiz->p_standard_mode[i]);
|
||||
}
|
||||
}
|
||||
|
||||
wiz->typec_ln10_swap = devm_regmap_field_alloc(dev, regmap,
|
||||
typec_ln10_swap);
|
||||
if (IS_ERR(wiz->typec_ln10_swap)) {
|
||||
dev_err(dev, "LN10_SWAP reg field init failed\n");
|
||||
return PTR_ERR(wiz->typec_ln10_swap);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 wiz_clk_mux_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct wiz_clk_mux *mux = to_wiz_clk_mux(hw);
|
||||
struct regmap_field *field = mux->field;
|
||||
unsigned int val;
|
||||
|
||||
regmap_field_read(field, &val);
|
||||
return clk_mux_val_to_index(hw, mux->table, 0, val);
|
||||
}
|
||||
|
||||
static int wiz_clk_mux_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct wiz_clk_mux *mux = to_wiz_clk_mux(hw);
|
||||
struct regmap_field *field = mux->field;
|
||||
int val;
|
||||
|
||||
val = mux->table[index];
|
||||
return regmap_field_write(field, val);
|
||||
}
|
||||
|
||||
static const struct clk_ops wiz_clk_mux_ops = {
|
||||
.set_parent = wiz_clk_mux_set_parent,
|
||||
.get_parent = wiz_clk_mux_get_parent,
|
||||
};
|
||||
|
||||
static int wiz_mux_clk_register(struct wiz *wiz, struct device_node *node,
|
||||
struct regmap_field *field, u32 *table)
|
||||
{
|
||||
struct device *dev = wiz->dev;
|
||||
struct clk_init_data *init;
|
||||
const char **parent_names;
|
||||
unsigned int num_parents;
|
||||
struct wiz_clk_mux *mux;
|
||||
char clk_name[100];
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
|
||||
if (!mux)
|
||||
return -ENOMEM;
|
||||
|
||||
num_parents = of_clk_get_parent_count(node);
|
||||
if (num_parents < 2) {
|
||||
dev_err(dev, "SERDES clock must have parents\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
parent_names = devm_kzalloc(dev, (sizeof(char *) * num_parents),
|
||||
GFP_KERNEL);
|
||||
if (!parent_names)
|
||||
return -ENOMEM;
|
||||
|
||||
of_clk_parent_fill(node, parent_names, num_parents);
|
||||
|
||||
snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev),
|
||||
node->name);
|
||||
|
||||
init = &mux->clk_data;
|
||||
|
||||
init->ops = &wiz_clk_mux_ops;
|
||||
init->flags = CLK_SET_RATE_NO_REPARENT;
|
||||
init->parent_names = parent_names;
|
||||
init->num_parents = num_parents;
|
||||
init->name = clk_name;
|
||||
|
||||
mux->field = field;
|
||||
mux->table = table;
|
||||
mux->hw.init = init;
|
||||
|
||||
clk = devm_clk_register(dev, &mux->hw);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
if (ret)
|
||||
dev_err(dev, "Failed to add clock provider: %s\n", clk_name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned long wiz_clk_div_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct wiz_clk_divider *div = to_wiz_clk_div(hw);
|
||||
struct regmap_field *field = div->field;
|
||||
int val;
|
||||
|
||||
regmap_field_read(field, &val);
|
||||
|
||||
return divider_recalc_rate(hw, parent_rate, val, div->table, 0x0, 2);
|
||||
}
|
||||
|
||||
static long wiz_clk_div_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
struct wiz_clk_divider *div = to_wiz_clk_div(hw);
|
||||
|
||||
return divider_round_rate(hw, rate, prate, div->table, 2, 0x0);
|
||||
}
|
||||
|
||||
static int wiz_clk_div_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct wiz_clk_divider *div = to_wiz_clk_div(hw);
|
||||
struct regmap_field *field = div->field;
|
||||
int val;
|
||||
|
||||
val = divider_get_val(rate, parent_rate, div->table, 2, 0x0);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
return regmap_field_write(field, val);
|
||||
}
|
||||
|
||||
static const struct clk_ops wiz_clk_div_ops = {
|
||||
.recalc_rate = wiz_clk_div_recalc_rate,
|
||||
.round_rate = wiz_clk_div_round_rate,
|
||||
.set_rate = wiz_clk_div_set_rate,
|
||||
};
|
||||
|
||||
static int wiz_div_clk_register(struct wiz *wiz, struct device_node *node,
|
||||
struct regmap_field *field,
|
||||
struct clk_div_table *table)
|
||||
{
|
||||
struct device *dev = wiz->dev;
|
||||
struct wiz_clk_divider *div;
|
||||
struct clk_init_data *init;
|
||||
const char **parent_names;
|
||||
char clk_name[100];
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
|
||||
if (!div)
|
||||
return -ENOMEM;
|
||||
|
||||
snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev),
|
||||
node->name);
|
||||
|
||||
parent_names = devm_kzalloc(dev, sizeof(char *), GFP_KERNEL);
|
||||
if (!parent_names)
|
||||
return -ENOMEM;
|
||||
|
||||
of_clk_parent_fill(node, parent_names, 1);
|
||||
|
||||
init = &div->clk_data;
|
||||
|
||||
init->ops = &wiz_clk_div_ops;
|
||||
init->flags = 0;
|
||||
init->parent_names = parent_names;
|
||||
init->num_parents = 1;
|
||||
init->name = clk_name;
|
||||
|
||||
div->field = field;
|
||||
div->table = table;
|
||||
div->hw.init = init;
|
||||
|
||||
clk = devm_clk_register(dev, &div->hw);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
if (ret)
|
||||
dev_err(dev, "Failed to add clock provider: %s\n", clk_name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void wiz_clock_cleanup(struct wiz *wiz, struct device_node *node)
|
||||
{
|
||||
struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel;
|
||||
struct device_node *clk_node;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < WIZ_MUX_NUM_CLOCKS; i++) {
|
||||
clk_node = of_get_child_by_name(node, clk_mux_sel[i].node_name);
|
||||
of_clk_del_provider(clk_node);
|
||||
of_node_put(clk_node);
|
||||
}
|
||||
}
|
||||
|
||||
static int wiz_clock_init(struct wiz *wiz, struct device_node *node)
|
||||
{
|
||||
struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel;
|
||||
struct device *dev = wiz->dev;
|
||||
struct device_node *clk_node;
|
||||
const char *node_name;
|
||||
unsigned long rate;
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
clk = devm_clk_get(dev, "core_ref_clk");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(dev, "core_ref_clk clock not found\n");
|
||||
ret = PTR_ERR(clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
rate = clk_get_rate(clk);
|
||||
if (rate >= 100000000)
|
||||
regmap_field_write(wiz->pma_cmn_refclk_int_mode, 0x1);
|
||||
else
|
||||
regmap_field_write(wiz->pma_cmn_refclk_int_mode, 0x3);
|
||||
|
||||
clk = devm_clk_get(dev, "ext_ref_clk");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(dev, "ext_ref_clk clock not found\n");
|
||||
ret = PTR_ERR(clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
rate = clk_get_rate(clk);
|
||||
if (rate >= 100000000)
|
||||
regmap_field_write(wiz->pma_cmn_refclk_mode, 0x0);
|
||||
else
|
||||
regmap_field_write(wiz->pma_cmn_refclk_mode, 0x2);
|
||||
|
||||
for (i = 0; i < WIZ_MUX_NUM_CLOCKS; i++) {
|
||||
node_name = clk_mux_sel[i].node_name;
|
||||
clk_node = of_get_child_by_name(node, node_name);
|
||||
if (!clk_node) {
|
||||
dev_err(dev, "Unable to get %s node\n", node_name);
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = wiz_mux_clk_register(wiz, clk_node, clk_mux_sel[i].field,
|
||||
clk_mux_sel[i].table);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register %s clock\n",
|
||||
node_name);
|
||||
of_node_put(clk_node);
|
||||
goto err;
|
||||
}
|
||||
|
||||
of_node_put(clk_node);
|
||||
}
|
||||
|
||||
for (i = 0; i < wiz->clk_div_sel_num; i++) {
|
||||
node_name = clk_div_sel[i].node_name;
|
||||
clk_node = of_get_child_by_name(node, node_name);
|
||||
if (!clk_node) {
|
||||
dev_err(dev, "Unable to get %s node\n", node_name);
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = wiz_div_clk_register(wiz, clk_node, clk_div_sel[i].field,
|
||||
clk_div_sel[i].table);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register %s clock\n",
|
||||
node_name);
|
||||
of_node_put(clk_node);
|
||||
goto err;
|
||||
}
|
||||
|
||||
of_node_put(clk_node);
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
wiz_clock_cleanup(wiz, node);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wiz_phy_reset_assert(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
struct device *dev = rcdev->dev;
|
||||
struct wiz *wiz = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (id == 0) {
|
||||
ret = regmap_field_write(wiz->phy_reset_n, false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_field_write(wiz->p_enable[id - 1], false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wiz_phy_reset_deassert(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
struct device *dev = rcdev->dev;
|
||||
struct wiz *wiz = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
/* if typec-dir gpio was specified, set LN10 SWAP bit based on that */
|
||||
if (id == 0 && wiz->gpio_typec_dir) {
|
||||
if (wiz->typec_dir_delay)
|
||||
msleep_interruptible(wiz->typec_dir_delay);
|
||||
|
||||
if (gpiod_get_value_cansleep(wiz->gpio_typec_dir))
|
||||
regmap_field_write(wiz->typec_ln10_swap, 1);
|
||||
else
|
||||
regmap_field_write(wiz->typec_ln10_swap, 0);
|
||||
}
|
||||
|
||||
if (id == 0) {
|
||||
ret = regmap_field_write(wiz->phy_reset_n, true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_field_write(wiz->p_enable[id - 1], true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct reset_control_ops wiz_phy_reset_ops = {
|
||||
.assert = wiz_phy_reset_assert,
|
||||
.deassert = wiz_phy_reset_deassert,
|
||||
};
|
||||
|
||||
static struct regmap_config wiz_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.fast_io = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id wiz_id_table[] = {
|
||||
{
|
||||
.compatible = "ti,j721e-wiz-16g", .data = (void *)J721E_WIZ_16G
|
||||
},
|
||||
{
|
||||
.compatible = "ti,j721e-wiz-10g", .data = (void *)J721E_WIZ_10G
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, wiz_id_table);
|
||||
|
||||
static int wiz_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct reset_controller_dev *phy_reset_dev;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct platform_device *serdes_pdev;
|
||||
struct device_node *child_node;
|
||||
struct regmap *regmap;
|
||||
struct resource res;
|
||||
void __iomem *base;
|
||||
struct wiz *wiz;
|
||||
u32 num_lanes;
|
||||
int ret;
|
||||
|
||||
wiz = devm_kzalloc(dev, sizeof(*wiz), GFP_KERNEL);
|
||||
if (!wiz)
|
||||
return -ENOMEM;
|
||||
|
||||
wiz->type = (enum wiz_type)of_device_get_match_data(dev);
|
||||
|
||||
child_node = of_get_child_by_name(node, "serdes");
|
||||
if (!child_node) {
|
||||
dev_err(dev, "Failed to get SERDES child DT node\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = of_address_to_resource(child_node, 0, &res);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to get memory resource\n");
|
||||
goto err_addr_to_resource;
|
||||
}
|
||||
|
||||
base = devm_ioremap(dev, res.start, resource_size(&res));
|
||||
if (!base)
|
||||
goto err_addr_to_resource;
|
||||
|
||||
regmap = devm_regmap_init_mmio(dev, base, &wiz_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(dev, "Failed to initialize regmap\n");
|
||||
ret = PTR_ERR(regmap);
|
||||
goto err_addr_to_resource;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(node, "num-lanes", &num_lanes);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to read num-lanes property\n");
|
||||
goto err_addr_to_resource;
|
||||
}
|
||||
|
||||
if (num_lanes > WIZ_MAX_LANES) {
|
||||
dev_err(dev, "Cannot support %d lanes\n", num_lanes);
|
||||
goto err_addr_to_resource;
|
||||
}
|
||||
|
||||
wiz->gpio_typec_dir = devm_gpiod_get_optional(dev, "typec-dir",
|
||||
GPIOD_IN);
|
||||
if (IS_ERR(wiz->gpio_typec_dir)) {
|
||||
ret = PTR_ERR(wiz->gpio_typec_dir);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to request typec-dir gpio: %d\n",
|
||||
ret);
|
||||
goto err_addr_to_resource;
|
||||
}
|
||||
|
||||
if (wiz->gpio_typec_dir) {
|
||||
ret = of_property_read_u32(node, "typec-dir-debounce-ms",
|
||||
&wiz->typec_dir_delay);
|
||||
if (ret && ret != -EINVAL) {
|
||||
dev_err(dev, "Invalid typec-dir-debounce property\n");
|
||||
goto err_addr_to_resource;
|
||||
}
|
||||
|
||||
/* use min. debounce from Type-C spec if not provided in DT */
|
||||
if (ret == -EINVAL)
|
||||
wiz->typec_dir_delay = WIZ_TYPEC_DIR_DEBOUNCE_MIN;
|
||||
|
||||
if (wiz->typec_dir_delay < WIZ_TYPEC_DIR_DEBOUNCE_MIN ||
|
||||
wiz->typec_dir_delay > WIZ_TYPEC_DIR_DEBOUNCE_MAX) {
|
||||
dev_err(dev, "Invalid typec-dir-debounce property\n");
|
||||
goto err_addr_to_resource;
|
||||
}
|
||||
}
|
||||
|
||||
wiz->dev = dev;
|
||||
wiz->regmap = regmap;
|
||||
wiz->num_lanes = num_lanes;
|
||||
if (wiz->type == J721E_WIZ_10G)
|
||||
wiz->clk_mux_sel = clk_mux_sel_10g;
|
||||
else
|
||||
wiz->clk_mux_sel = clk_mux_sel_16g;
|
||||
|
||||
wiz->clk_div_sel = clk_div_sel;
|
||||
|
||||
if (wiz->type == J721E_WIZ_10G)
|
||||
wiz->clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_10G;
|
||||
else
|
||||
wiz->clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_16G;
|
||||
|
||||
platform_set_drvdata(pdev, wiz);
|
||||
|
||||
ret = wiz_regfield_init(wiz);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to initialize regfields\n");
|
||||
goto err_addr_to_resource;
|
||||
}
|
||||
|
||||
phy_reset_dev = &wiz->wiz_phy_reset_dev;
|
||||
phy_reset_dev->dev = dev;
|
||||
phy_reset_dev->ops = &wiz_phy_reset_ops,
|
||||
phy_reset_dev->owner = THIS_MODULE,
|
||||
phy_reset_dev->of_node = node;
|
||||
/* Reset for each of the lane and one for the entire SERDES */
|
||||
phy_reset_dev->nr_resets = num_lanes + 1;
|
||||
|
||||
ret = devm_reset_controller_register(dev, phy_reset_dev);
|
||||
if (ret < 0) {
|
||||
dev_warn(dev, "Failed to register reset controller\n");
|
||||
goto err_addr_to_resource;
|
||||
}
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "pm_runtime_get_sync failed\n");
|
||||
goto err_get_sync;
|
||||
}
|
||||
|
||||
ret = wiz_clock_init(wiz, node);
|
||||
if (ret < 0) {
|
||||
dev_warn(dev, "Failed to initialize clocks\n");
|
||||
goto err_get_sync;
|
||||
}
|
||||
|
||||
serdes_pdev = of_platform_device_create(child_node, NULL, dev);
|
||||
if (!serdes_pdev) {
|
||||
dev_WARN(dev, "Unable to create SERDES platform device\n");
|
||||
goto err_pdev_create;
|
||||
}
|
||||
wiz->serdes_pdev = serdes_pdev;
|
||||
|
||||
ret = wiz_init(wiz);
|
||||
if (ret) {
|
||||
dev_err(dev, "WIZ initialization failed\n");
|
||||
goto err_wiz_init;
|
||||
}
|
||||
|
||||
of_node_put(child_node);
|
||||
return 0;
|
||||
|
||||
err_wiz_init:
|
||||
of_platform_device_destroy(&serdes_pdev->dev, NULL);
|
||||
|
||||
err_pdev_create:
|
||||
wiz_clock_cleanup(wiz, node);
|
||||
|
||||
err_get_sync:
|
||||
pm_runtime_put(dev);
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
err_addr_to_resource:
|
||||
of_node_put(child_node);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wiz_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct platform_device *serdes_pdev;
|
||||
struct wiz *wiz;
|
||||
|
||||
wiz = dev_get_drvdata(dev);
|
||||
serdes_pdev = wiz->serdes_pdev;
|
||||
|
||||
of_platform_device_destroy(&serdes_pdev->dev, NULL);
|
||||
wiz_clock_cleanup(wiz, node);
|
||||
pm_runtime_put(dev);
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver wiz_driver = {
|
||||
.probe = wiz_probe,
|
||||
.remove = wiz_remove,
|
||||
.driver = {
|
||||
.name = "wiz",
|
||||
.of_match_table = wiz_id_table,
|
||||
},
|
||||
};
|
||||
module_platform_driver(wiz_driver);
|
||||
|
||||
MODULE_AUTHOR("Texas Instruments Inc.");
|
||||
MODULE_DESCRIPTION("TI J721E WIZ driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@@ -850,6 +850,12 @@ static int ti_pipe3_probe(struct platform_device *pdev)
|
||||
|
||||
static int ti_pipe3_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ti_pipe3 *phy = platform_get_drvdata(pdev);
|
||||
|
||||
if (phy->mode == PIPE3_MODE_SATA) {
|
||||
clk_disable_unprepare(phy->refclk);
|
||||
phy->sata_refclk_enabled = false;
|
||||
}
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
@@ -900,18 +906,8 @@ static void ti_pipe3_disable_clocks(struct ti_pipe3 *phy)
|
||||
{
|
||||
if (!IS_ERR(phy->wkupclk))
|
||||
clk_disable_unprepare(phy->wkupclk);
|
||||
if (!IS_ERR(phy->refclk)) {
|
||||
if (!IS_ERR(phy->refclk))
|
||||
clk_disable_unprepare(phy->refclk);
|
||||
/*
|
||||
* SATA refclk needs an additional disable as we left it
|
||||
* on in probe to avoid Errata i783
|
||||
*/
|
||||
if (phy->sata_refclk_enabled) {
|
||||
clk_disable_unprepare(phy->refclk);
|
||||
phy->sata_refclk_enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!IS_ERR(phy->div_clk))
|
||||
clk_disable_unprepare(phy->div_clk);
|
||||
}
|
||||
|
@@ -32,7 +32,7 @@ static int usbhs_rcar2_hardware_exit(struct platform_device *pdev)
|
||||
struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
|
||||
|
||||
if (priv->phy) {
|
||||
phy_put(priv->phy);
|
||||
phy_put(&pdev->dev, priv->phy);
|
||||
priv->phy = NULL;
|
||||
}
|
||||
|
||||
|
@@ -29,7 +29,7 @@ static int usbhs_rza2_hardware_exit(struct platform_device *pdev)
|
||||
{
|
||||
struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
|
||||
|
||||
phy_put(priv->phy);
|
||||
phy_put(&pdev->dev, priv->phy);
|
||||
priv->phy = NULL;
|
||||
|
||||
return 0;
|
||||
|
@@ -16,5 +16,6 @@
|
||||
#define PHY_TYPE_USB2 3
|
||||
#define PHY_TYPE_USB3 4
|
||||
#define PHY_TYPE_UFS 5
|
||||
#define PHY_TYPE_DP 6
|
||||
|
||||
#endif /* _DT_BINDINGS_PHY */
|
||||
|
95
include/linux/phy/phy-dp.h
Normal file
95
include/linux/phy/phy-dp.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2019 Cadence Design Systems Inc.
|
||||
*/
|
||||
|
||||
#ifndef __PHY_DP_H_
|
||||
#define __PHY_DP_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/**
|
||||
* struct phy_configure_opts_dp - DisplayPort PHY configuration set
|
||||
*
|
||||
* This structure is used to represent the configuration state of a
|
||||
* DisplayPort phy.
|
||||
*/
|
||||
struct phy_configure_opts_dp {
|
||||
/**
|
||||
* @link_rate:
|
||||
*
|
||||
* Link Rate, in Mb/s, of the main link.
|
||||
*
|
||||
* Allowed values: 1620, 2160, 2430, 2700, 3240, 4320, 5400, 8100 Mb/s
|
||||
*/
|
||||
unsigned int link_rate;
|
||||
|
||||
/**
|
||||
* @lanes:
|
||||
*
|
||||
* Number of active, consecutive, data lanes, starting from
|
||||
* lane 0, used for the transmissions on main link.
|
||||
*
|
||||
* Allowed values: 1, 2, 4
|
||||
*/
|
||||
unsigned int lanes;
|
||||
|
||||
/**
|
||||
* @voltage:
|
||||
*
|
||||
* Voltage swing levels, as specified by DisplayPort specification,
|
||||
* to be used by particular lanes. One value per lane.
|
||||
* voltage[0] is for lane 0, voltage[1] is for lane 1, etc.
|
||||
*
|
||||
* Maximum value: 3
|
||||
*/
|
||||
unsigned int voltage[4];
|
||||
|
||||
/**
|
||||
* @pre:
|
||||
*
|
||||
* Pre-emphasis levels, as specified by DisplayPort specification, to be
|
||||
* used by particular lanes. One value per lane.
|
||||
*
|
||||
* Maximum value: 3
|
||||
*/
|
||||
unsigned int pre[4];
|
||||
|
||||
/**
|
||||
* @ssc:
|
||||
*
|
||||
* Flag indicating, whether or not to enable spread-spectrum clocking.
|
||||
*
|
||||
*/
|
||||
u8 ssc : 1;
|
||||
|
||||
/**
|
||||
* @set_rate:
|
||||
*
|
||||
* Flag indicating, whether or not reconfigure link rate and SSC to
|
||||
* requested values.
|
||||
*
|
||||
*/
|
||||
u8 set_rate : 1;
|
||||
|
||||
/**
|
||||
* @set_lanes:
|
||||
*
|
||||
* Flag indicating, whether or not reconfigure lane count to
|
||||
* requested value.
|
||||
*
|
||||
*/
|
||||
u8 set_lanes : 1;
|
||||
|
||||
/**
|
||||
* @set_voltages:
|
||||
*
|
||||
* Flag indicating, whether or not reconfigure voltage swing
|
||||
* and pre-emphasis to requested values. Only lanes specified
|
||||
* by "lanes" parameter will be affected.
|
||||
*
|
||||
*/
|
||||
u8 set_voltages : 1;
|
||||
};
|
||||
|
||||
#endif /* __PHY_DP_H_ */
|
@@ -16,6 +16,7 @@
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <linux/phy/phy-dp.h>
|
||||
#include <linux/phy/phy-mipi-dphy.h>
|
||||
|
||||
struct phy;
|
||||
@@ -40,6 +41,7 @@ enum phy_mode {
|
||||
PHY_MODE_MIPI_DPHY,
|
||||
PHY_MODE_SATA,
|
||||
PHY_MODE_LVDS,
|
||||
PHY_MODE_DP
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -47,9 +49,12 @@ enum phy_mode {
|
||||
*
|
||||
* @mipi_dphy: Configuration set applicable for phys supporting
|
||||
* the MIPI_DPHY phy mode.
|
||||
* @dp: Configuration set applicable for phys supporting
|
||||
* the DisplayPort protocol.
|
||||
*/
|
||||
union phy_configure_opts {
|
||||
struct phy_configure_opts_mipi_dphy mipi_dphy;
|
||||
struct phy_configure_opts_dp dp;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -234,7 +239,8 @@ struct phy *devm_of_phy_get(struct device *dev, struct device_node *np,
|
||||
const char *con_id);
|
||||
struct phy *devm_of_phy_get_by_index(struct device *dev, struct device_node *np,
|
||||
int index);
|
||||
void phy_put(struct phy *phy);
|
||||
void of_phy_put(struct phy *phy);
|
||||
void phy_put(struct device *dev, struct phy *phy);
|
||||
void devm_phy_put(struct device *dev, struct phy *phy);
|
||||
struct phy *of_phy_get(struct device_node *np, const char *con_id);
|
||||
struct phy *of_phy_simple_xlate(struct device *dev,
|
||||
@@ -419,7 +425,11 @@ static inline struct phy *devm_of_phy_get_by_index(struct device *dev,
|
||||
return ERR_PTR(-ENOSYS);
|
||||
}
|
||||
|
||||
static inline void phy_put(struct phy *phy)
|
||||
static inline void of_phy_put(struct phy *phy)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void phy_put(struct device *dev, struct phy *phy)
|
||||
{
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user