diff options
author | 2020-01-17 07:52:26 +0100 | |
---|---|---|
committer | 2020-01-17 07:52:26 +0100 | |
commit | 50f737ab96c7977317a47e60c52f4f0ce8b64e7f (patch) | |
tree | 086c96fc1e441232ec3d7f3397b67c2f93d28cc3 | |
parent | usb: musb: remove dummy driver musb_am335x.c (diff) | |
parent | dt-bindings: phy: Add PHY_TYPE_DP definition (diff) | |
download | linux-50f737ab96c7977317a47e60c52f4f0ce8b64e7f.tar.gz linux-50f737ab96c7977317a47e60c52f4f0ce8b64e7f.tar.bz2 linux-50f737ab96c7977317a47e60c52f4f0ce8b64e7f.zip |
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
...
38 files changed, 3614 insertions, 630 deletions
diff --git a/Documentation/devicetree/bindings/phy/allwinner,sun9i-a80-usb-phy.yaml b/Documentation/devicetree/bindings/phy/allwinner,sun9i-a80-usb-phy.yaml new file mode 100644 index 000000000000..ded7d6f0a119 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/allwinner,sun9i-a80-usb-phy.yaml @@ -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>; + }; diff --git a/Documentation/devicetree/bindings/phy/brcm,brcmstb-usb-phy.txt b/Documentation/devicetree/bindings/phy/brcm,brcmstb-usb-phy.txt index 24a0d06acd1d..698aacbdcfc4 100644 --- a/Documentation/devicetree/bindings/phy/brcm,brcmstb-usb-phy.txt +++ b/Documentation/devicetree/bindings/phy/brcm,brcmstb-usb-phy.txt @@ -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"; +}; diff --git a/Documentation/devicetree/bindings/phy/brcm-sata-phy.txt b/Documentation/devicetree/bindings/phy/brcm-sata-phy.txt index b640845fec67..c03ad2198410 100644 --- a/Documentation/devicetree/bindings/phy/brcm-sata-phy.txt +++ b/Documentation/devicetree/bindings/phy/brcm-sata-phy.txt @@ -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" diff --git a/Documentation/devicetree/bindings/phy/intel,lgm-emmc-phy.yaml b/Documentation/devicetree/bindings/phy/intel,lgm-emmc-phy.yaml new file mode 100644 index 000000000000..ff7959c21af0 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/intel,lgm-emmc-phy.yaml @@ -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>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/phy/phy-cadence-sierra.txt b/Documentation/devicetree/bindings/phy/phy-cadence-sierra.txt index 6e1b47bfce43..03f5939d3d19 100644 --- a/Documentation/devicetree/bindings/phy/phy-cadence-sierra.txt +++ b/Documentation/devicetree/bindings/phy/phy-cadence-sierra.txt @@ -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 diff --git a/Documentation/devicetree/bindings/phy/rockchip,px30-dsi-dphy.yaml b/Documentation/devicetree/bindings/phy/rockchip,px30-dsi-dphy.yaml index bb0da87bcd84..476c56a1dc8c 100644 --- a/Documentation/devicetree/bindings/phy/rockchip,px30-dsi-dphy.yaml +++ b/Documentation/devicetree/bindings/phy/rockchip,px30-dsi-dphy.yaml @@ -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>; diff --git a/Documentation/devicetree/bindings/phy/sun9i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun9i-usb-phy.txt deleted file mode 100644 index 64f7109aea1f..000000000000 --- a/Documentation/devicetree/bindings/phy/sun9i-usb-phy.txt +++ /dev/null @@ -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>; - }; diff --git a/Documentation/devicetree/bindings/phy/ti,phy-j721e-wiz.yaml b/Documentation/devicetree/bindings/phy/ti,phy-j721e-wiz.yaml new file mode 100644 index 000000000000..452cee1aed32 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/ti,phy-j721e-wiz.yaml @@ -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"; + }; + }; diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 0263db2ac874..b3ed94b98d9b 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -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 diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index c96a1afc95bd..310c149a9df5 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -18,6 +18,7 @@ obj-y += broadcom/ \ cadence/ \ freescale/ \ hisilicon/ \ + intel/ \ lantiq/ \ marvell/ \ motorola/ \ diff --git a/drivers/phy/broadcom/Makefile b/drivers/phy/broadcom/Makefile index f453c7d3ffff..c78de546135c 100644 --- a/drivers/phy/broadcom/Makefile +++ b/drivers/phy/broadcom/Makefile @@ -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 diff --git a/drivers/phy/broadcom/phy-brcm-sata.c b/drivers/phy/broadcom/phy-brcm-sata.c index 50ac75bbb0c9..4710cfcc3037 100644 --- a/drivers/phy/broadcom/phy-brcm-sata.c +++ b/drivers/phy/broadcom/phy-brcm-sata.c @@ -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", diff --git a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c new file mode 100644 index 000000000000..456dc4a100c2 --- /dev/null +++ b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c @@ -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; +} diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.c b/drivers/phy/broadcom/phy-brcm-usb-init.c index 91b5b09589d6..9391ab42a12b 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.c +++ b/drivers/phy/broadcom/phy-brcm-usb-init.c @@ -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) +static void usb_init_common(struct brcm_usb_init_params *params) { - void __iomem *ctrl = params->ctrl_regs; u32 reg; + 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); - reg |= mode; - brcmusb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); - } -} - -void brcm_usb_init_common(struct brcm_usb_init_params *params) -{ - u32 reg; - void __iomem *ctrl = params->ctrl_regs; + /* 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); +} + +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; } -void brcm_usb_set_family_map(struct brcm_usb_init_params *params) +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; } diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.h b/drivers/phy/broadcom/phy-brcm-usb-init.h index f4f4f6d5d258..899b9eb43fad 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.h +++ b/drivers/phy/broadcom/phy-brcm-usb-init.h @@ -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_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); + +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); }; -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_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 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 */ diff --git a/drivers/phy/broadcom/phy-brcm-usb.c b/drivers/phy/broadcom/phy-brcm-usb.c index f5c1f2983a1d..491bbd46c5b3 100644 --- a/drivers/phy/broadcom/phy-brcm-usb.c +++ b/drivers/phy/broadcom/phy-brcm-usb.c @@ -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 = { diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index de10402f2931..a5c08e5bd2bf 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -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_MACRO_ID 0x00007364 -#define SIERRA_MAX_LANES 4 +#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_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_sierra_phy { +struct cdns_regmap_cdb_context { struct device *dev; void __iomem *base; + u8 reg_offset_shift; +}; + +struct cdns_sierra_phy { + struct device *dev; + 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); diff --git a/drivers/phy/hisilicon/Kconfig b/drivers/phy/hisilicon/Kconfig index 534e393a09b3..1c73053bcc98 100644 --- a/drivers/phy/hisilicon/Kconfig +++ b/drivers/phy/hisilicon/Kconfig @@ -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" diff --git a/drivers/phy/intel/Kconfig b/drivers/phy/intel/Kconfig new file mode 100644 index 000000000000..4ea6a8897cd7 --- /dev/null +++ b/drivers/phy/intel/Kconfig @@ -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 diff --git a/drivers/phy/intel/Makefile b/drivers/phy/intel/Makefile new file mode 100644 index 000000000000..6b876a75599d --- /dev/null +++ b/drivers/phy/intel/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_PHY_INTEL_EMMC) += phy-intel-emmc.o diff --git a/drivers/phy/intel/phy-intel-emmc.c b/drivers/phy/intel/phy-intel-emmc.c new file mode 100644 index 000000000000..703aeb122541 --- /dev/null +++ b/drivers/phy/intel/phy-intel-emmc.c @@ -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"); diff --git a/drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c b/drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c index 6e457967653e..2ff9a48d833e 100644 --- a/drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c +++ b/drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c @@ -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; } diff --git a/drivers/phy/mediatek/Kconfig b/drivers/phy/mediatek/Kconfig index 81cfea156bcd..dee757c957f2 100644 --- a/drivers/phy/mediatek/Kconfig +++ b/drivers/phy/mediatek/Kconfig @@ -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. diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index b04f4fe85ac2..2eb28cc2d2dc 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -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); diff --git a/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c b/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c index 42bc5150dd92..febe0aef68d4 100644 --- a/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c +++ b/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c @@ -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; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 66f91726b8b2..7db2a94f7a99 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -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, diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index ab6ff9b45a32..90f793c2293d 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -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. */ diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig index dbd2de4d28b1..0824b9dd5683 100644 --- a/drivers/phy/rockchip/Kconfig +++ b/drivers/phy/rockchip/Kconfig @@ -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. diff --git a/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c b/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c index fc729ecd3fe9..a7c6c940a3a8 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c @@ -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 const struct phy_ops inno_dsidphy_ops = { - .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) +static int inno_dsidphy_configure(struct phy *phy, + union phy_configure_opts *opts) { - 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; + struct inno_dsidphy *inno = phy_get_drvdata(phy); + int ret; - fout = inno_dsidphy_pll_round_rate(inno, parent_rate, rate, - &prediv, &fbdiv); + if (inno->mode != PHY_MODE_MIPI_DPHY) + return -EINVAL; - dev_dbg(inno->dev, "fin=%lu, fout=%lu, prediv=%u, fbdiv=%u\n", - parent_rate, fout, prediv, fbdiv); + ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy); + if (ret) + return ret; - inno->pll.prediv = prediv; - inno->pll.fbdiv = fbdiv; - inno->pll.rate = fout; + memcpy(&inno->dphy_cfg, &opts->mipi_dphy, sizeof(inno->dphy_cfg)); 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 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 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; diff --git a/drivers/phy/ti/Kconfig b/drivers/phy/ti/Kconfig index e231c0e369c5..3a1d3887c99c 100644 --- a/drivers/phy/ti/Kconfig +++ b/drivers/phy/ti/Kconfig @@ -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 diff --git a/drivers/phy/ti/Makefile b/drivers/phy/ti/Makefile index bff901eb0ecc..dcba2571c9bd 100644 --- a/drivers/phy/ti/Makefile +++ b/drivers/phy/ti/Makefile @@ -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 diff --git a/drivers/phy/ti/phy-j721e-wiz.c b/drivers/phy/ti/phy-j721e-wiz.c new file mode 100644 index 000000000000..7b51045df783 --- /dev/null +++ b/drivers/phy/ti/phy-j721e-wiz.c @@ -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"); diff --git a/drivers/phy/ti/phy-ti-pipe3.c b/drivers/phy/ti/phy-ti-pipe3.c index edd6859afba8..a87946589eb7 100644 --- a/drivers/phy/ti/phy-ti-pipe3.c +++ b/drivers/phy/ti/phy-ti-pipe3.c @@ -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); } diff --git a/drivers/usb/renesas_usbhs/rcar2.c b/drivers/usb/renesas_usbhs/rcar2.c index 7f2f06586ea5..52756fc2ac9c 100644 --- a/drivers/usb/renesas_usbhs/rcar2.c +++ b/drivers/usb/renesas_usbhs/rcar2.c @@ -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; } diff --git a/drivers/usb/renesas_usbhs/rza2.c b/drivers/usb/renesas_usbhs/rza2.c index 021749594389..3eed3334a17f 100644 --- a/drivers/usb/renesas_usbhs/rza2.c +++ b/drivers/usb/renesas_usbhs/rza2.c @@ -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; diff --git a/include/dt-bindings/phy/phy.h b/include/dt-bindings/phy/phy.h index b6a1eaf1b339..1f3f866fae7b 100644 --- a/include/dt-bindings/phy/phy.h +++ b/include/dt-bindings/phy/phy.h @@ -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 */ diff --git a/include/linux/phy/phy-dp.h b/include/linux/phy/phy-dp.h new file mode 100644 index 000000000000..18cad23642cd --- /dev/null +++ b/include/linux/phy/phy-dp.h @@ -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_ */ diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index 56d3a100006a..bcee8eba62b3 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.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) { } |