Merge tag 'phy-for-5.6_v2' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy into usb-next

Kishon writes:

phy: for 5.6

*) Add support in PHY core to create link between PHY consumer and PHY
   provider
*) Add DisplayPort PHY configuration set to be used for negotiating the
   configurations to be used between DisplayPort controller and
   DisplayPort PHY
*) Add PHY wrapper driver (configure inputs to Cadence Sierra PHY) for
   TI's J721E SoC and adapt Cadence Sierra PHY driver to be used for
   J721E SoC (Supports USB and PCIe)
*) Add PHY driver for eMMC PHY in Intel LGM SoC
*) Add PHY support for 7216 and 7211 Broadcom SoCs which uses the new
   Synopsys USB Controller
*) Add support for 16nm SATA PHY present in Broadcom 7216 SoC
*) Fix lost packet issue, fix MDIO from getting inaccessible, fix
   occasional transaction failures, fix USB driver from crashing in
   Broadcom USB PHY driver
*) Fix missing PCS SW reset in UFS PHY of Qualcomm SM8150
*) Use "struct phy_configure_opts_mipi_dphy" to pass parameters from
   display controller to rockchip-inno-dsidphy
*) Other cleanups including compile testing for some of the PHY drivers,
   fixing Kconfig indentation, duplicate writes in drivers etc.,

Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>

* tag 'phy-for-5.6_v2' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy: (54 commits)
  dt-bindings: phy: Add PHY_TYPE_DP definition
  phy: ti: j721e-wiz: Fix return value check in wiz_probe()
  dt-bindings: usb: Convert Allwinner A80 USB PHY controller to a schema
  phy: intel-lgm-emmc: Fix warning by adding missing MODULE_LICENSE
  phy: ti: j721e-wiz: Manage typec-gpio-dir
  dt-bindings: phy: ti,phy-j721e-wiz: Add Type-C dir GPIO
  phy: cadence: Sierra: add phy_reset hook
  phy: cadence: Sierra: remove redundant initialization of pointer regmap
  phy: Add DisplayPort configuration options
  phy: Enable compile testing for some of drivers
  phy: mediatek: Fix Kconfig indentation
  phy: intel-lgm-emmc: Add support for eMMC PHY
  dt-bindings: phy: intel-emmc-phy: Add YAML schema for LGM eMMC PHY
  phy: ti: j721e-wiz: Add support for WIZ module present in TI J721E SoC
  dt-bindings: phy: Document WIZ (SERDES wrapper) bindings
  phy: cadence: Sierra: Use correct dev pointer in cdns_sierra_phy_remove()
  phy: cadence: Sierra: Set cmn_refclk_dig_div/cmn_refclk1_dig_div frequency to 25MHz
  phy: cadence: Sierra: Change MAX_LANES of Sierra to 16
  phy: cadence: Sierra: Check for PLL lock during PHY power on
  phy: cadence: Sierra: Get reset control "array" for each link
  ...
This commit is contained in:
Greg Kroah-Hartman
2020-01-17 07:52:26 +01:00
38 changed files with 3616 additions and 632 deletions

View File

@@ -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 = <&reg_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 = <&reg_usb3_vbus>;
#phy-cells = <0>;
};

View File

@@ -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";
};

View File

@@ -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"

View File

@@ -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>;
};
};
...

View File

@@ -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

View File

@@ -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>;

View File

@@ -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>;
};

View File

@@ -0,0 +1,221 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
%YAML 1.2
---
$id: "http://devicetree.org/schemas/phy/ti,phy-j721e-wiz.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: TI J721E WIZ (SERDES Wrapper)
maintainers:
- Kishon Vijay Abraham I <kishon@ti.com>
properties:
compatible:
enum:
- ti,j721e-wiz-16g
- ti,j721e-wiz-10g
power-domains:
maxItems: 1
clocks:
maxItems: 3
description: clock-specifier to represent input to the WIZ
clock-names:
items:
- const: fck
- const: core_ref_clk
- const: ext_ref_clk
num-lanes:
minimum: 1
maximum: 4
"#address-cells":
const: 1
"#size-cells":
const: 1
"#reset-cells":
const: 1
ranges: true
assigned-clocks:
maxItems: 2
assigned-clock-parents:
maxItems: 2
typec-dir-gpios:
maxItems: 1
description:
GPIO to signal Type-C cable orientation for lane swap.
If GPIO is active, lane 0 and lane 1 of SERDES will be swapped to
achieve the funtionality of an external type-C plug flip mux.
typec-dir-debounce-ms:
minimum: 100
maximum: 1000
default: 100
description:
Number of milliseconds to wait before sampling typec-dir-gpio.
If not specified, the default debounce of 100ms will be used.
Type-C spec states minimum CC pin debounce of 100 ms and maximum
of 200 ms. However, some solutions might need more than 200 ms.
patternProperties:
"^pll[0|1]-refclk$":
type: object
description: |
WIZ node should have subnodes for each of the PLLs present in
the SERDES.
properties:
clocks:
maxItems: 2
description: Phandle to clock nodes representing the two inputs to PLL.
"#clock-cells":
const: 0
assigned-clocks:
maxItems: 1
assigned-clock-parents:
maxItems: 1
required:
- clocks
- "#clock-cells"
- assigned-clocks
- assigned-clock-parents
"^cmn-refclk1?-dig-div$":
type: object
description:
WIZ node should have subnodes for each of the PMA common refclock
provided by the SERDES.
properties:
clocks:
maxItems: 1
description: Phandle to the clock node representing the input to the
divider clock.
"#clock-cells":
const: 0
required:
- clocks
- "#clock-cells"
"^refclk-dig$":
type: object
description: |
WIZ node should have subnode for refclk_dig to select the reference
clock source for the reference clock used in the PHY and PMA digital
logic.
properties:
clocks:
maxItems: 4
description: Phandle to four clock nodes representing the inputs to
refclk_dig
"#clock-cells":
const: 0
assigned-clocks:
maxItems: 1
assigned-clock-parents:
maxItems: 1
required:
- clocks
- "#clock-cells"
- assigned-clocks
- assigned-clock-parents
"^serdes@[0-9a-f]+$":
type: object
description: |
WIZ node should have '1' subnode for the SERDES. It could be either
Sierra SERDES or Torrent SERDES. Sierra SERDES should follow the
bindings specified in
Documentation/devicetree/bindings/phy/phy-cadence-sierra.txt
Torrent SERDES should follow the bindings specified in
Documentation/devicetree/bindings/phy/phy-cadence-dp.txt
required:
- compatible
- power-domains
- clocks
- clock-names
- num-lanes
- "#address-cells"
- "#size-cells"
- "#reset-cells"
- ranges
examples:
- |
#include <dt-bindings/soc/ti,sci_pm_domain.h>
wiz@5000000 {
compatible = "ti,j721e-wiz-16g";
#address-cells = <1>;
#size-cells = <1>;
power-domains = <&k3_pds 292 TI_SCI_PD_EXCLUSIVE>;
clocks = <&k3_clks 292 5>, <&k3_clks 292 11>, <&dummy_cmn_refclk>;
clock-names = "fck", "core_ref_clk", "ext_ref_clk";
assigned-clocks = <&k3_clks 292 11>, <&k3_clks 292 0>;
assigned-clock-parents = <&k3_clks 292 15>, <&k3_clks 292 4>;
num-lanes = <2>;
#reset-cells = <1>;
ranges = <0x5000000 0x5000000 0x10000>;
pll0-refclk {
clocks = <&k3_clks 293 13>, <&dummy_cmn_refclk>;
#clock-cells = <0>;
assigned-clocks = <&wiz1_pll0_refclk>;
assigned-clock-parents = <&k3_clks 293 13>;
};
pll1-refclk {
clocks = <&k3_clks 293 0>, <&dummy_cmn_refclk1>;
#clock-cells = <0>;
assigned-clocks = <&wiz1_pll1_refclk>;
assigned-clock-parents = <&k3_clks 293 0>;
};
cmn-refclk-dig-div {
clocks = <&wiz1_refclk_dig>;
#clock-cells = <0>;
};
cmn-refclk1-dig-div {
clocks = <&wiz1_pll1_refclk>;
#clock-cells = <0>;
};
refclk-dig {
clocks = <&k3_clks 292 11>, <&k3_clks 292 0>, <&dummy_cmn_refclk>, <&dummy_cmn_refclk1>;
#clock-cells = <0>;
assigned-clocks = <&wiz0_refclk_dig>;
assigned-clock-parents = <&k3_clks 292 11>;
};
serdes@5000000 {
compatible = "cdns,ti,sierra-phy-t0";
reg-names = "serdes";
reg = <0x5000000 0x10000>;
#address-cells = <1>;
#size-cells = <0>;
resets = <&serdes_wiz0 0>;
reset-names = "sierra_reset";
clocks = <&wiz0_cmn_refclk_dig_div>, <&wiz0_cmn_refclk1_dig_div>;
clock-names = "cmn_refclk_dig_div", "cmn_refclk1_dig_div";
};
};

View File

@@ -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

View File

@@ -18,6 +18,7 @@ obj-y += broadcom/ \
cadence/ \
freescale/ \
hisilicon/ \
intel/ \
lantiq/ \
marvell/ \
motorola/ \

View File

@@ -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

View File

@@ -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",

View File

@@ -0,0 +1,414 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2018, Broadcom */
/*
* This module contains USB PHY initialization for power up and S3 resume
* for newer Synopsys based USB hardware first used on the bcm7216.
*/
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/soc/brcmstb/brcmstb.h>
#include "phy-brcm-usb-init.h"
#define PHY_LOCK_TIMEOUT_MS 200
/* Register definitions for syscon piarbctl registers */
#define PIARBCTL_CAM 0x00
#define PIARBCTL_SPLITTER 0x04
#define PIARBCTL_MISC 0x08
#define PIARBCTL_MISC_SECURE_MASK 0x80000000
#define PIARBCTL_MISC_USB_SELECT_MASK 0x40000000
#define PIARBCTL_MISC_USB_4G_SDRAM_MASK 0x20000000
#define PIARBCTL_MISC_USB_PRIORITY_MASK 0x000f0000
#define PIARBCTL_MISC_USB_MEM_PAGE_MASK 0x0000f000
#define PIARBCTL_MISC_CAM1_MEM_PAGE_MASK 0x00000f00
#define PIARBCTL_MISC_CAM0_MEM_PAGE_MASK 0x000000f0
#define PIARBCTL_MISC_SATA_PRIORITY_MASK 0x0000000f
#define PIARBCTL_MISC_USB_ONLY_MASK \
(PIARBCTL_MISC_USB_SELECT_MASK | \
PIARBCTL_MISC_USB_4G_SDRAM_MASK | \
PIARBCTL_MISC_USB_PRIORITY_MASK | \
PIARBCTL_MISC_USB_MEM_PAGE_MASK)
/* Register definitions for the USB CTRL block */
#define USB_CTRL_SETUP 0x00
#define USB_CTRL_SETUP_STRAP_IPP_SEL_MASK 0x02000000
#define USB_CTRL_SETUP_SCB2_EN_MASK 0x00008000
#define USB_CTRL_SETUP_tca_drv_sel_MASK 0x01000000
#define USB_CTRL_SETUP_SCB1_EN_MASK 0x00004000
#define USB_CTRL_SETUP_SOFT_SHUTDOWN_MASK 0x00000200
#define USB_CTRL_SETUP_IPP_MASK 0x00000020
#define USB_CTRL_SETUP_IOC_MASK 0x00000010
#define USB_CTRL_USB_PM 0x04
#define USB_CTRL_USB_PM_USB_PWRDN_MASK 0x80000000
#define USB_CTRL_USB_PM_SOFT_RESET_MASK 0x40000000
#define USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK 0x00800000
#define USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK 0x00400000
#define USB_CTRL_USB_PM_STATUS 0x08
#define USB_CTRL_USB_DEVICE_CTL1 0x10
#define USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK 0x00000003
#define USB_CTRL_TEST_PORT_CTL 0x30
#define USB_CTRL_TEST_PORT_CTL_TPOUT_SEL_MASK 0x000000ff
#define USB_CTRL_TEST_PORT_CTL_TPOUT_SEL_PME_GEN_MASK 0x0000002e
#define USB_CTRL_TP_DIAG1 0x34
#define USB_CTLR_TP_DIAG1_wake_MASK 0x00000002
#define USB_CTRL_CTLR_CSHCR 0x50
#define USB_CTRL_CTLR_CSHCR_ctl_pme_en_MASK 0x00040000
/* Register definitions for the USB_PHY block in 7211b0 */
#define USB_PHY_PLL_CTL 0x00
#define USB_PHY_PLL_CTL_PLL_RESETB_MASK 0x40000000
#define USB_PHY_PLL_LDO_CTL 0x08
#define USB_PHY_PLL_LDO_CTL_AFE_CORERDY_MASK 0x00000004
#define USB_PHY_PLL_LDO_CTL_AFE_LDO_PWRDWNB_MASK 0x00000002
#define USB_PHY_PLL_LDO_CTL_AFE_BG_PWRDWNB_MASK 0x00000001
#define USB_PHY_UTMI_CTL_1 0x04
#define USB_PHY_UTMI_CTL_1_POWER_UP_FSM_EN_MASK 0x00000800
#define USB_PHY_UTMI_CTL_1_PHY_MODE_MASK 0x0000000c
#define USB_PHY_UTMI_CTL_1_PHY_MODE_SHIFT 2
#define USB_PHY_IDDQ 0x1c
#define USB_PHY_IDDQ_phy_iddq_MASK 0x00000001
#define USB_PHY_STATUS 0x20
#define USB_PHY_STATUS_pll_lock_MASK 0x00000001
/* Register definitions for the MDIO registers in the DWC2 block of
* the 7211b0.
* NOTE: The PHY's MDIO registers are only accessible through the
* legacy DesignWare USB controller even though it's not being used.
*/
#define USB_GMDIOCSR 0
#define USB_GMDIOGEN 4
/* Register definitions for the BDC EC block in 7211b0 */
#define BDC_EC_AXIRDA 0x0c
#define BDC_EC_AXIRDA_RTS_MASK 0xf0000000
#define BDC_EC_AXIRDA_RTS_SHIFT 28
static void usb_mdio_write_7211b0(struct brcm_usb_init_params *params,
uint8_t addr, uint16_t data)
{
void __iomem *usb_mdio = params->regs[BRCM_REGS_USB_MDIO];
addr &= 0x1f; /* 5-bit address */
brcm_usb_writel(0xffffffff, usb_mdio + USB_GMDIOGEN);
while (brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & (1<<31))
;
brcm_usb_writel(0x59020000 | (addr << 18) | data,
usb_mdio + USB_GMDIOGEN);
while (brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & (1<<31))
;
brcm_usb_writel(0x00000000, usb_mdio + USB_GMDIOGEN);
while (brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & (1<<31))
;
}
static uint16_t __maybe_unused usb_mdio_read_7211b0(
struct brcm_usb_init_params *params, uint8_t addr)
{
void __iomem *usb_mdio = params->regs[BRCM_REGS_USB_MDIO];
addr &= 0x1f; /* 5-bit address */
brcm_usb_writel(0xffffffff, usb_mdio + USB_GMDIOGEN);
while (brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & (1<<31))
;
brcm_usb_writel(0x69020000 | (addr << 18), usb_mdio + USB_GMDIOGEN);
while (brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & (1<<31))
;
brcm_usb_writel(0x00000000, usb_mdio + USB_GMDIOGEN);
while (brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & (1<<31))
;
return brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & 0xffff;
}
static void usb2_eye_fix_7211b0(struct brcm_usb_init_params *params)
{
/* select bank */
usb_mdio_write_7211b0(params, 0x1f, 0x80a0);
/* Set the eye */
usb_mdio_write_7211b0(params, 0x0a, 0xc6a0);
}
static void xhci_soft_reset(struct brcm_usb_init_params *params,
int on_off)
{
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
/* Assert reset */
if (on_off)
USB_CTRL_UNSET(ctrl, USB_PM, XHC_SOFT_RESETB);
/* De-assert reset */
else
USB_CTRL_SET(ctrl, USB_PM, XHC_SOFT_RESETB);
}
static void usb_init_ipp(struct brcm_usb_init_params *params)
{
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
u32 reg;
u32 orig_reg;
pr_debug("%s\n", __func__);
orig_reg = reg = brcm_usb_readl(USB_CTRL_REG(ctrl, SETUP));
if (params->ipp != 2)
/* override ipp strap pin (if it exits) */
reg &= ~(USB_CTRL_MASK(SETUP, STRAP_IPP_SEL));
/* Override the default OC and PP polarity */
reg &= ~(USB_CTRL_MASK(SETUP, IPP) | USB_CTRL_MASK(SETUP, IOC));
if (params->ioc)
reg |= USB_CTRL_MASK(SETUP, IOC);
if (params->ipp == 1)
reg |= USB_CTRL_MASK(SETUP, IPP);
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, SETUP));
/*
* If we're changing IPP, make sure power is off long enough
* to turn off any connected devices.
*/
if ((reg ^ orig_reg) & USB_CTRL_MASK(SETUP, IPP))
msleep(50);
}
static void syscon_piarbctl_init(struct regmap *rmap)
{
/* Switch from legacy USB OTG controller to new STB USB controller */
regmap_update_bits(rmap, PIARBCTL_MISC, PIARBCTL_MISC_USB_ONLY_MASK,
PIARBCTL_MISC_USB_SELECT_MASK |
PIARBCTL_MISC_USB_4G_SDRAM_MASK);
}
static void usb_init_common(struct brcm_usb_init_params *params)
{
u32 reg;
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
pr_debug("%s\n", __func__);
USB_CTRL_UNSET(ctrl, USB_PM, USB_PWRDN);
/* 1 millisecond - for USB clocks to settle down */
usleep_range(1000, 2000);
if (USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE)) {
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
reg &= ~USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE);
reg |= params->mode;
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
}
switch (params->mode) {
case USB_CTLR_MODE_HOST:
USB_CTRL_UNSET(ctrl, USB_PM, BDC_SOFT_RESETB);
break;
default:
USB_CTRL_UNSET(ctrl, USB_PM, BDC_SOFT_RESETB);
USB_CTRL_SET(ctrl, USB_PM, BDC_SOFT_RESETB);
break;
}
}
static void usb_wake_enable_7211b0(struct brcm_usb_init_params *params,
bool enable)
{
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
if (enable)
USB_CTRL_SET(ctrl, CTLR_CSHCR, ctl_pme_en);
else
USB_CTRL_UNSET(ctrl, CTLR_CSHCR, ctl_pme_en);
}
static void usb_init_common_7211b0(struct brcm_usb_init_params *params)
{
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
void __iomem *usb_phy = params->regs[BRCM_REGS_USB_PHY];
void __iomem *bdc_ec = params->regs[BRCM_REGS_BDC_EC];
int timeout_ms = PHY_LOCK_TIMEOUT_MS;
u32 reg;
if (params->syscon_piarbctl)
syscon_piarbctl_init(params->syscon_piarbctl);
USB_CTRL_UNSET(ctrl, USB_PM, USB_PWRDN);
usb_wake_enable_7211b0(params, false);
if (!params->wake_enabled) {
/* undo possible suspend settings */
brcm_usb_writel(0, usb_phy + USB_PHY_IDDQ);
reg = brcm_usb_readl(usb_phy + USB_PHY_PLL_CTL);
reg |= USB_PHY_PLL_CTL_PLL_RESETB_MASK;
brcm_usb_writel(reg, usb_phy + USB_PHY_PLL_CTL);
/* temporarily enable FSM so PHY comes up properly */
reg = brcm_usb_readl(usb_phy + USB_PHY_UTMI_CTL_1);
reg |= USB_PHY_UTMI_CTL_1_POWER_UP_FSM_EN_MASK;
brcm_usb_writel(reg, usb_phy + USB_PHY_UTMI_CTL_1);
}
/* Init the PHY */
reg = USB_PHY_PLL_LDO_CTL_AFE_CORERDY_MASK |
USB_PHY_PLL_LDO_CTL_AFE_LDO_PWRDWNB_MASK |
USB_PHY_PLL_LDO_CTL_AFE_BG_PWRDWNB_MASK;
brcm_usb_writel(reg, usb_phy + USB_PHY_PLL_LDO_CTL);
/* wait for lock */
while (timeout_ms-- > 0) {
reg = brcm_usb_readl(usb_phy + USB_PHY_STATUS);
if (reg & USB_PHY_STATUS_pll_lock_MASK)
break;
usleep_range(1000, 2000);
}
/* Set the PHY_MODE */
reg = brcm_usb_readl(usb_phy + USB_PHY_UTMI_CTL_1);
reg &= ~USB_PHY_UTMI_CTL_1_PHY_MODE_MASK;
reg |= params->mode << USB_PHY_UTMI_CTL_1_PHY_MODE_SHIFT;
brcm_usb_writel(reg, usb_phy + USB_PHY_UTMI_CTL_1);
/* Fix the incorrect default */
reg = brcm_usb_readl(ctrl + USB_CTRL_SETUP);
reg &= ~USB_CTRL_SETUP_tca_drv_sel_MASK;
brcm_usb_writel(reg, ctrl + USB_CTRL_SETUP);
usb_init_common(params);
/*
* The BDC controller will get occasional failures with
* the default "Read Transaction Size" of 6 (1024 bytes).
* Set it to 4 (256 bytes).
*/
if ((params->mode != USB_CTLR_MODE_HOST) && bdc_ec) {
reg = brcm_usb_readl(bdc_ec + BDC_EC_AXIRDA);
reg &= ~BDC_EC_AXIRDA_RTS_MASK;
reg |= (0x4 << BDC_EC_AXIRDA_RTS_SHIFT);
brcm_usb_writel(reg, bdc_ec + BDC_EC_AXIRDA);
}
/*
* Disable FSM, otherwise the PHY will auto suspend when no
* device is connected and will be reset on resume.
*/
reg = brcm_usb_readl(usb_phy + USB_PHY_UTMI_CTL_1);
reg &= ~USB_PHY_UTMI_CTL_1_POWER_UP_FSM_EN_MASK;
brcm_usb_writel(reg, usb_phy + USB_PHY_UTMI_CTL_1);
usb2_eye_fix_7211b0(params);
}
static void usb_init_xhci(struct brcm_usb_init_params *params)
{
pr_debug("%s\n", __func__);
xhci_soft_reset(params, 0);
}
static void usb_uninit_common(struct brcm_usb_init_params *params)
{
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
pr_debug("%s\n", __func__);
USB_CTRL_SET(ctrl, USB_PM, USB_PWRDN);
}
static void usb_uninit_common_7211b0(struct brcm_usb_init_params *params)
{
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
void __iomem *usb_phy = params->regs[BRCM_REGS_USB_PHY];
u32 reg;
pr_debug("%s\n", __func__);
if (params->wake_enabled) {
USB_CTRL_SET(ctrl, TEST_PORT_CTL, TPOUT_SEL_PME_GEN);
usb_wake_enable_7211b0(params, true);
} else {
USB_CTRL_SET(ctrl, USB_PM, USB_PWRDN);
brcm_usb_writel(0, usb_phy + USB_PHY_PLL_LDO_CTL);
reg = brcm_usb_readl(usb_phy + USB_PHY_PLL_CTL);
reg &= ~USB_PHY_PLL_CTL_PLL_RESETB_MASK;
brcm_usb_writel(reg, usb_phy + USB_PHY_PLL_CTL);
brcm_usb_writel(USB_PHY_IDDQ_phy_iddq_MASK,
usb_phy + USB_PHY_IDDQ);
}
}
static void usb_uninit_xhci(struct brcm_usb_init_params *params)
{
pr_debug("%s\n", __func__);
if (!params->wake_enabled)
xhci_soft_reset(params, 1);
}
static int usb_get_dual_select(struct brcm_usb_init_params *params)
{
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
u32 reg = 0;
pr_debug("%s\n", __func__);
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
reg &= USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE);
return reg;
}
static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode)
{
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
u32 reg;
pr_debug("%s\n", __func__);
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
reg &= ~USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE);
reg |= mode;
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
}
static const struct brcm_usb_init_ops bcm7216_ops = {
.init_ipp = usb_init_ipp,
.init_common = usb_init_common,
.init_xhci = usb_init_xhci,
.uninit_common = usb_uninit_common,
.uninit_xhci = usb_uninit_xhci,
.get_dual_select = usb_get_dual_select,
.set_dual_select = usb_set_dual_select,
};
static const struct brcm_usb_init_ops bcm7211b0_ops = {
.init_ipp = usb_init_ipp,
.init_common = usb_init_common_7211b0,
.init_xhci = usb_init_xhci,
.uninit_common = usb_uninit_common_7211b0,
.uninit_xhci = usb_uninit_xhci,
.get_dual_select = usb_get_dual_select,
.set_dual_select = usb_set_dual_select,
};
void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params)
{
pr_debug("%s\n", __func__);
params->family_name = "7216";
params->ops = &bcm7216_ops;
}
void brcm_usb_dvr_init_7211b0(struct brcm_usb_init_params *params)
{
pr_debug("%s\n", __func__);
params->family_name = "7211";
params->ops = &bcm7211b0_ops;
params->suspend_with_clocks = true;
}

View File

@@ -42,6 +42,7 @@
#define USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK 0x80000000 /* option */
#define USB_CTRL_EBRIDGE 0x0c
#define USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK 0x00020000 /* option */
#define USB_CTRL_EBRIDGE_EBR_SCB_SIZE_MASK 0x00000f80 /* option */
#define USB_CTRL_OBRIDGE 0x10
#define USB_CTRL_OBRIDGE_LS_KEEP_ALIVE_MASK 0x08000000
#define USB_CTRL_MDIO 0x14
@@ -57,6 +58,8 @@
#define USB_CTRL_USB_PM_SOFT_RESET_MASK 0x40000000 /* option */
#define USB_CTRL_USB_PM_USB20_HC_RESETB_MASK 0x30000000 /* option */
#define USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK 0x00300000 /* option */
#define USB_CTRL_USB_PM_RMTWKUP_EN_MASK 0x00000001
#define USB_CTRL_USB_PM_STATUS 0x38
#define USB_CTRL_USB30_CTL1 0x60
#define USB_CTRL_USB30_CTL1_PHY3_PLL_SEQ_START_MASK 0x00000010
#define USB_CTRL_USB30_CTL1_PHY3_RESETB_MASK 0x00010000
@@ -126,10 +129,6 @@ enum {
USB_CTRL_SELECTOR_COUNT,
};
#define USB_CTRL_REG(base, reg) ((void __iomem *)base + USB_CTRL_##reg)
#define USB_XHCI_EC_REG(base, reg) ((void __iomem *)base + USB_XHCI_EC_##reg)
#define USB_CTRL_MASK(reg, field) \
USB_CTRL_##reg##_##field##_MASK
#define USB_CTRL_MASK_FAMILY(params, reg, field) \
(params->usb_reg_bits_map[USB_CTRL_##reg##_##field##_SELECTOR])
@@ -140,13 +139,6 @@ enum {
usb_ctrl_unset_family(params, USB_CTRL_##reg, \
USB_CTRL_##reg##_##field##_SELECTOR)
#define USB_CTRL_SET(base, reg, field) \
usb_ctrl_set(USB_CTRL_REG(base, reg), \
USB_CTRL_##reg##_##field##_MASK)
#define USB_CTRL_UNSET(base, reg, field) \
usb_ctrl_unset(USB_CTRL_REG(base, reg), \
USB_CTRL_##reg##_##field##_MASK)
#define MDIO_USB2 0
#define MDIO_USB3 BIT(31)
@@ -176,6 +168,7 @@ static const struct id_to_type id_to_type_table[] = {
{ 0x33900000, BRCM_FAMILY_3390A0 },
{ 0x72500010, BRCM_FAMILY_7250B0 },
{ 0x72600000, BRCM_FAMILY_7260A0 },
{ 0x72550000, BRCM_FAMILY_7260A0 },
{ 0x72680000, BRCM_FAMILY_7271A0 },
{ 0x72710000, BRCM_FAMILY_7271A0 },
{ 0x73640000, BRCM_FAMILY_7364A0 },
@@ -401,26 +394,14 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = {
},
};
static inline u32 brcmusb_readl(void __iomem *addr)
{
return readl(addr);
}
static inline void brcmusb_writel(u32 val, void __iomem *addr)
{
writel(val, addr);
}
static inline
void usb_ctrl_unset_family(struct brcm_usb_init_params *params,
u32 reg_offset, u32 field)
{
u32 mask;
void __iomem *reg;
mask = params->usb_reg_bits_map[field];
reg = params->ctrl_regs + reg_offset;
brcmusb_writel(brcmusb_readl(reg) & ~mask, reg);
brcm_usb_ctrl_unset(params->regs[BRCM_REGS_CTRL] + reg_offset, mask);
};
static inline
@@ -428,45 +409,27 @@ void usb_ctrl_set_family(struct brcm_usb_init_params *params,
u32 reg_offset, u32 field)
{
u32 mask;
void __iomem *reg;
mask = params->usb_reg_bits_map[field];
reg = params->ctrl_regs + reg_offset;
brcmusb_writel(brcmusb_readl(reg) | mask, reg);
brcm_usb_ctrl_set(params->regs[BRCM_REGS_CTRL] + reg_offset, mask);
};
static inline void usb_ctrl_set(void __iomem *reg, u32 field)
{
u32 value;
value = brcmusb_readl(reg);
brcmusb_writel(value | field, reg);
}
static inline void usb_ctrl_unset(void __iomem *reg, u32 field)
{
u32 value;
value = brcmusb_readl(reg);
brcmusb_writel(value & ~field, reg);
}
static u32 brcmusb_usb_mdio_read(void __iomem *ctrl_base, u32 reg, int mode)
{
u32 data;
data = (reg << 16) | mode;
brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
brcm_usb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
data |= (1 << 24);
brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
brcm_usb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
data &= ~(1 << 24);
/* wait for the 60MHz parallel to serial shifter */
usleep_range(10, 20);
brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
brcm_usb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
/* wait for the 60MHz parallel to serial shifter */
usleep_range(10, 20);
return brcmusb_readl(USB_CTRL_REG(ctrl_base, MDIO2)) & 0xffff;
return brcm_usb_readl(USB_CTRL_REG(ctrl_base, MDIO2)) & 0xffff;
}
static void brcmusb_usb_mdio_write(void __iomem *ctrl_base, u32 reg,
@@ -475,14 +438,14 @@ static void brcmusb_usb_mdio_write(void __iomem *ctrl_base, u32 reg,
u32 data;
data = (reg << 16) | val | mode;
brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
brcm_usb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
data |= (1 << 25);
brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
brcm_usb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
data &= ~(1 << 25);
/* wait for the 60MHz parallel to serial shifter */
usleep_range(10, 20);
brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
brcm_usb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
/* wait for the 60MHz parallel to serial shifter */
usleep_range(10, 20);
}
@@ -581,7 +544,7 @@ static void brcmusb_usb3_pll_54mhz(struct brcm_usb_init_params *params)
{
u32 ofs;
int ii;
void __iomem *ctrl_base = params->ctrl_regs;
void __iomem *ctrl_base = params->regs[BRCM_REGS_CTRL];
/*
* On newer B53 based SoC's, the reference clock for the
@@ -662,7 +625,7 @@ static void brcmusb_usb3_ssc_enable(void __iomem *ctrl_base)
static void brcmusb_usb3_phy_workarounds(struct brcm_usb_init_params *params)
{
void __iomem *ctrl_base = params->ctrl_regs;
void __iomem *ctrl_base = params->regs[BRCM_REGS_CTRL];
brcmusb_usb3_pll_fix(ctrl_base);
brcmusb_usb3_pll_54mhz(params);
@@ -704,21 +667,21 @@ static void brcmusb_memc_fix(struct brcm_usb_init_params *params)
static void brcmusb_usb3_otp_fix(struct brcm_usb_init_params *params)
{
void __iomem *xhci_ec_base = params->xhci_ec_regs;
void __iomem *xhci_ec_base = params->regs[BRCM_REGS_XHCI_EC];
u32 val;
if (params->family_id != 0x74371000 || !xhci_ec_base)
return;
brcmusb_writel(0xa20c, USB_XHCI_EC_REG(xhci_ec_base, IRAADR));
val = brcmusb_readl(USB_XHCI_EC_REG(xhci_ec_base, IRADAT));
brcm_usb_writel(0xa20c, USB_XHCI_EC_REG(xhci_ec_base, IRAADR));
val = brcm_usb_readl(USB_XHCI_EC_REG(xhci_ec_base, IRADAT));
/* set cfg_pick_ss_lock */
val |= (1 << 27);
brcmusb_writel(val, USB_XHCI_EC_REG(xhci_ec_base, IRADAT));
brcm_usb_writel(val, USB_XHCI_EC_REG(xhci_ec_base, IRADAT));
/* Reset USB 3.0 PHY for workaround to take effect */
USB_CTRL_UNSET(params->ctrl_regs, USB30_CTL1, PHY3_RESETB);
USB_CTRL_SET(params->ctrl_regs, USB30_CTL1, PHY3_RESETB);
USB_CTRL_UNSET(params->regs[BRCM_REGS_CTRL], USB30_CTL1, PHY3_RESETB);
USB_CTRL_SET(params->regs[BRCM_REGS_CTRL], USB30_CTL1, PHY3_RESETB);
}
static void brcmusb_xhci_soft_reset(struct brcm_usb_init_params *params,
@@ -747,7 +710,7 @@ static void brcmusb_xhci_soft_reset(struct brcm_usb_init_params *params,
* - default chip/rev.
* NOTE: The minor rev is always ignored.
*/
static enum brcm_family_type brcmusb_get_family_type(
static enum brcm_family_type get_family_type(
struct brcm_usb_init_params *params)
{
int last_type = -1;
@@ -775,9 +738,9 @@ static enum brcm_family_type brcmusb_get_family_type(
return last_type;
}
void brcm_usb_init_ipp(struct brcm_usb_init_params *params)
static void usb_init_ipp(struct brcm_usb_init_params *params)
{
void __iomem *ctrl = params->ctrl_regs;
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
u32 reg;
u32 orig_reg;
@@ -791,7 +754,7 @@ void brcm_usb_init_ipp(struct brcm_usb_init_params *params)
USB_CTRL_SET_FAMILY(params, USB30_CTL1, USB3_IPP);
}
reg = brcmusb_readl(USB_CTRL_REG(ctrl, SETUP));
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, SETUP));
orig_reg = reg;
if (USB_CTRL_MASK_FAMILY(params, SETUP, STRAP_CC_DRD_MODE_ENABLE_SEL))
/* Never use the strap, it's going away. */
@@ -799,8 +762,8 @@ void brcm_usb_init_ipp(struct brcm_usb_init_params *params)
SETUP,
STRAP_CC_DRD_MODE_ENABLE_SEL));
if (USB_CTRL_MASK_FAMILY(params, SETUP, STRAP_IPP_SEL))
/* override ipp strap pin (if it exits) */
if (params->ipp != 2)
/* override ipp strap pin (if it exits) */
reg &= ~(USB_CTRL_MASK_FAMILY(params, SETUP,
STRAP_IPP_SEL));
@@ -808,50 +771,38 @@ void brcm_usb_init_ipp(struct brcm_usb_init_params *params)
reg &= ~(USB_CTRL_MASK(SETUP, IPP) | USB_CTRL_MASK(SETUP, IOC));
if (params->ioc)
reg |= USB_CTRL_MASK(SETUP, IOC);
if (params->ipp == 1 && ((reg & USB_CTRL_MASK(SETUP, IPP)) == 0))
if (params->ipp == 1)
reg |= USB_CTRL_MASK(SETUP, IPP);
brcmusb_writel(reg, USB_CTRL_REG(ctrl, SETUP));
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, SETUP));
/*
* If we're changing IPP, make sure power is off long enough
* to turn off any connected devices.
*/
if (reg != orig_reg)
if ((reg ^ orig_reg) & USB_CTRL_MASK(SETUP, IPP))
msleep(50);
}
int brcm_usb_init_get_dual_select(struct brcm_usb_init_params *params)
static void usb_wake_enable(struct brcm_usb_init_params *params,
bool enable)
{
void __iomem *ctrl = params->ctrl_regs;
u32 reg = 0;
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) {
reg = brcmusb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
reg &= USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1,
PORT_MODE);
}
return reg;
if (enable)
USB_CTRL_SET(ctrl, USB_PM, RMTWKUP_EN);
else
USB_CTRL_UNSET(ctrl, USB_PM, RMTWKUP_EN);
}
void brcm_usb_init_set_dual_select(struct brcm_usb_init_params *params,
int mode)
{
void __iomem *ctrl = params->ctrl_regs;
u32 reg;
if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) {
reg = brcmusb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
reg &= ~USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1,
PORT_MODE);
reg |= mode;
brcmusb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
}
}
void brcm_usb_init_common(struct brcm_usb_init_params *params)
static void usb_init_common(struct brcm_usb_init_params *params)
{
u32 reg;
void __iomem *ctrl = params->ctrl_regs;
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
/* Clear any pending wake conditions */
usb_wake_enable(params, false);
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_PM_STATUS));
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_PM_STATUS));
/* Take USB out of power down */
if (USB_CTRL_MASK_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN)) {
@@ -877,7 +828,7 @@ void brcm_usb_init_common(struct brcm_usb_init_params *params)
/* Block auto PLL suspend by USB2 PHY (Sasi) */
USB_CTRL_SET(ctrl, PLL_CTL, PLL_SUSPEND_EN);
reg = brcmusb_readl(USB_CTRL_REG(ctrl, SETUP));
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, SETUP));
if (params->selected_family == BRCM_FAMILY_7364A0)
/* Suppress overcurrent indication from USB30 ports for A0 */
reg |= USB_CTRL_MASK_FAMILY(params, SETUP, OC3_DISABLE);
@@ -893,16 +844,16 @@ void brcm_usb_init_common(struct brcm_usb_init_params *params)
reg |= USB_CTRL_MASK_FAMILY(params, SETUP, SCB1_EN);
if (USB_CTRL_MASK_FAMILY(params, SETUP, SCB2_EN))
reg |= USB_CTRL_MASK_FAMILY(params, SETUP, SCB2_EN);
brcmusb_writel(reg, USB_CTRL_REG(ctrl, SETUP));
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, SETUP));
brcmusb_memc_fix(params);
if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) {
reg = brcmusb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
reg &= ~USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1,
PORT_MODE);
reg |= params->mode;
brcmusb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
}
if (USB_CTRL_MASK_FAMILY(params, USB_PM, BDC_SOFT_RESETB)) {
switch (params->mode) {
@@ -924,10 +875,10 @@ void brcm_usb_init_common(struct brcm_usb_init_params *params)
}
}
void brcm_usb_init_eohci(struct brcm_usb_init_params *params)
static void usb_init_eohci(struct brcm_usb_init_params *params)
{
u32 reg;
void __iomem *ctrl = params->ctrl_regs;
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
if (USB_CTRL_MASK_FAMILY(params, USB_PM, USB20_HC_RESETB))
USB_CTRL_SET_FAMILY(params, USB_PM, USB20_HC_RESETB);
@@ -940,19 +891,30 @@ void brcm_usb_init_eohci(struct brcm_usb_init_params *params)
USB_CTRL_SET(ctrl, EBRIDGE, ESTOP_SCB_REQ);
/* Setup the endian bits */
reg = brcmusb_readl(USB_CTRL_REG(ctrl, SETUP));
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, SETUP));
reg &= ~USB_CTRL_SETUP_ENDIAN_BITS;
reg |= USB_CTRL_MASK_FAMILY(params, SETUP, ENDIAN);
brcmusb_writel(reg, USB_CTRL_REG(ctrl, SETUP));
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, SETUP));
if (params->selected_family == BRCM_FAMILY_7271A0)
/* Enable LS keep alive fix for certain keyboards */
USB_CTRL_SET(ctrl, OBRIDGE, LS_KEEP_ALIVE);
if (params->family_id == 0x72550000) {
/*
* Make the burst size 512 bytes to fix a hardware bug
* on the 7255a0. See HW7255-24.
*/
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, EBRIDGE));
reg &= ~USB_CTRL_MASK(EBRIDGE, EBR_SCB_SIZE);
reg |= 0x800;
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, EBRIDGE));
}
}
void brcm_usb_init_xhci(struct brcm_usb_init_params *params)
static void usb_init_xhci(struct brcm_usb_init_params *params)
{
void __iomem *ctrl = params->ctrl_regs;
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
USB_CTRL_UNSET(ctrl, USB30_PCTL, PHY3_IDDQ_OVERRIDE);
/* 1 millisecond - for USB clocks to settle down */
@@ -978,34 +940,80 @@ void brcm_usb_init_xhci(struct brcm_usb_init_params *params)
brcmusb_usb3_otp_fix(params);
}
void brcm_usb_uninit_common(struct brcm_usb_init_params *params)
static void usb_uninit_common(struct brcm_usb_init_params *params)
{
if (USB_CTRL_MASK_FAMILY(params, USB_PM, USB_PWRDN))
USB_CTRL_SET_FAMILY(params, USB_PM, USB_PWRDN);
if (USB_CTRL_MASK_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN))
USB_CTRL_SET_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN);
if (params->wake_enabled)
usb_wake_enable(params, true);
}
void brcm_usb_uninit_eohci(struct brcm_usb_init_params *params)
static void usb_uninit_eohci(struct brcm_usb_init_params *params)
{
if (USB_CTRL_MASK_FAMILY(params, USB_PM, USB20_HC_RESETB))
USB_CTRL_UNSET_FAMILY(params, USB_PM, USB20_HC_RESETB);
}
void brcm_usb_uninit_xhci(struct brcm_usb_init_params *params)
static void usb_uninit_xhci(struct brcm_usb_init_params *params)
{
brcmusb_xhci_soft_reset(params, 1);
USB_CTRL_SET(params->ctrl_regs, USB30_PCTL, PHY3_IDDQ_OVERRIDE);
USB_CTRL_SET(params->regs[BRCM_REGS_CTRL], USB30_PCTL,
PHY3_IDDQ_OVERRIDE);
}
void brcm_usb_set_family_map(struct brcm_usb_init_params *params)
static int usb_get_dual_select(struct brcm_usb_init_params *params)
{
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
u32 reg = 0;
pr_debug("%s\n", __func__);
if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) {
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
reg &= USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1,
PORT_MODE);
}
return reg;
}
static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode)
{
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
u32 reg;
pr_debug("%s\n", __func__);
if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) {
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
reg &= ~USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1,
PORT_MODE);
reg |= mode;
brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
}
}
static const struct brcm_usb_init_ops bcm7445_ops = {
.init_ipp = usb_init_ipp,
.init_common = usb_init_common,
.init_eohci = usb_init_eohci,
.init_xhci = usb_init_xhci,
.uninit_common = usb_uninit_common,
.uninit_eohci = usb_uninit_eohci,
.uninit_xhci = usb_uninit_xhci,
.get_dual_select = usb_get_dual_select,
.set_dual_select = usb_set_dual_select,
};
void brcm_usb_dvr_init_7445(struct brcm_usb_init_params *params)
{
int fam;
fam = brcmusb_get_family_type(params);
pr_debug("%s\n", __func__);
fam = get_family_type(params);
params->selected_family = fam;
params->usb_reg_bits_map =
&usb_reg_bits_map_table[fam][0];
params->family_name = family_names[fam];
params->ops = &bcm7445_ops;
}

View File

@@ -6,16 +6,50 @@
#ifndef _USB_BRCM_COMMON_INIT_H
#define _USB_BRCM_COMMON_INIT_H
#include <linux/regmap.h>
#define USB_CTLR_MODE_HOST 0
#define USB_CTLR_MODE_DEVICE 1
#define USB_CTLR_MODE_DRD 2
#define USB_CTLR_MODE_TYPEC_PD 3
enum brcmusb_reg_sel {
BRCM_REGS_CTRL = 0,
BRCM_REGS_XHCI_EC,
BRCM_REGS_XHCI_GBL,
BRCM_REGS_USB_PHY,
BRCM_REGS_USB_MDIO,
BRCM_REGS_BDC_EC,
BRCM_REGS_MAX
};
#define USB_CTRL_REG(base, reg) ((void __iomem *)base + USB_CTRL_##reg)
#define USB_XHCI_EC_REG(base, reg) ((void __iomem *)base + USB_XHCI_EC_##reg)
#define USB_CTRL_MASK(reg, field) \
USB_CTRL_##reg##_##field##_MASK
#define USB_CTRL_SET(base, reg, field) \
brcm_usb_ctrl_set(USB_CTRL_REG(base, reg), \
USB_CTRL_##reg##_##field##_MASK)
#define USB_CTRL_UNSET(base, reg, field) \
brcm_usb_ctrl_unset(USB_CTRL_REG(base, reg), \
USB_CTRL_##reg##_##field##_MASK)
struct brcm_usb_init_params;
struct brcm_usb_init_ops {
void (*init_ipp)(struct brcm_usb_init_params *params);
void (*init_common)(struct brcm_usb_init_params *params);
void (*init_eohci)(struct brcm_usb_init_params *params);
void (*init_xhci)(struct brcm_usb_init_params *params);
void (*uninit_common)(struct brcm_usb_init_params *params);
void (*uninit_eohci)(struct brcm_usb_init_params *params);
void (*uninit_xhci)(struct brcm_usb_init_params *params);
int (*get_dual_select)(struct brcm_usb_init_params *params);
void (*set_dual_select)(struct brcm_usb_init_params *params, int mode);
};
struct brcm_usb_init_params {
void __iomem *ctrl_regs;
void __iomem *xhci_ec_regs;
void __iomem *regs[BRCM_REGS_MAX];
int ioc;
int ipp;
int mode;
@@ -24,19 +58,105 @@ struct brcm_usb_init_params {
int selected_family;
const char *family_name;
const u32 *usb_reg_bits_map;
const struct brcm_usb_init_ops *ops;
struct regmap *syscon_piarbctl;
bool wake_enabled;
bool suspend_with_clocks;
};
void brcm_usb_set_family_map(struct brcm_usb_init_params *params);
int brcm_usb_init_get_dual_select(struct brcm_usb_init_params *params);
void brcm_usb_init_set_dual_select(struct brcm_usb_init_params *params,
int mode);
void brcm_usb_dvr_init_7445(struct brcm_usb_init_params *params);
void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params);
void brcm_usb_dvr_init_7211b0(struct brcm_usb_init_params *params);
void brcm_usb_init_ipp(struct brcm_usb_init_params *ini);
void brcm_usb_init_common(struct brcm_usb_init_params *ini);
void brcm_usb_init_eohci(struct brcm_usb_init_params *ini);
void brcm_usb_init_xhci(struct brcm_usb_init_params *ini);
void brcm_usb_uninit_common(struct brcm_usb_init_params *ini);
void brcm_usb_uninit_eohci(struct brcm_usb_init_params *ini);
void brcm_usb_uninit_xhci(struct brcm_usb_init_params *ini);
static inline u32 brcm_usb_readl(void __iomem *addr)
{
/*
* MIPS endianness is configured by boot strap, which also reverses all
* bus endianness (i.e., big-endian CPU + big endian bus ==> native
* endian I/O).
*
* Other architectures (e.g., ARM) either do not support big endian, or
* else leave I/O in little endian mode.
*/
if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN))
return __raw_readl(addr);
else
return readl_relaxed(addr);
}
static inline void brcm_usb_writel(u32 val, void __iomem *addr)
{
/* See brcmnand_readl() comments */
if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN))
__raw_writel(val, addr);
else
writel_relaxed(val, addr);
}
static inline void brcm_usb_ctrl_unset(void __iomem *reg, u32 mask)
{
brcm_usb_writel(brcm_usb_readl(reg) & ~(mask), reg);
};
static inline void brcm_usb_ctrl_set(void __iomem *reg, u32 mask)
{
brcm_usb_writel(brcm_usb_readl(reg) | (mask), reg);
};
static inline void brcm_usb_init_ipp(struct brcm_usb_init_params *ini)
{
if (ini->ops->init_ipp)
ini->ops->init_ipp(ini);
}
static inline void brcm_usb_init_common(struct brcm_usb_init_params *ini)
{
if (ini->ops->init_common)
ini->ops->init_common(ini);
}
static inline void brcm_usb_init_eohci(struct brcm_usb_init_params *ini)
{
if (ini->ops->init_eohci)
ini->ops->init_eohci(ini);
}
static inline void brcm_usb_init_xhci(struct brcm_usb_init_params *ini)
{
if (ini->ops->init_xhci)
ini->ops->init_xhci(ini);
}
static inline void brcm_usb_uninit_common(struct brcm_usb_init_params *ini)
{
if (ini->ops->uninit_common)
ini->ops->uninit_common(ini);
}
static inline void brcm_usb_uninit_eohci(struct brcm_usb_init_params *ini)
{
if (ini->ops->uninit_eohci)
ini->ops->uninit_eohci(ini);
}
static inline void brcm_usb_uninit_xhci(struct brcm_usb_init_params *ini)
{
if (ini->ops->uninit_xhci)
ini->ops->uninit_xhci(ini);
}
static inline int brcm_usb_get_dual_select(struct brcm_usb_init_params *ini)
{
if (ini->ops->get_dual_select)
return ini->ops->get_dual_select(ini);
return 0;
}
static inline void brcm_usb_set_dual_select(struct brcm_usb_init_params *ini,
int mode)
{
if (ini->ops->set_dual_select)
ini->ops->set_dual_select(ini, mode);
}
#endif /* _USB_BRCM_COMMON_INIT_H */

View File

@@ -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 = {

View File

@@ -22,48 +22,134 @@
#include <dt-bindings/phy/phy.h>
/* PHY register offsets */
#define SIERRA_PHY_PLL_CFG (0xc00e << 2)
#define SIERRA_DET_STANDEC_A (0x4000 << 2)
#define SIERRA_DET_STANDEC_B (0x4001 << 2)
#define SIERRA_DET_STANDEC_C (0x4002 << 2)
#define SIERRA_DET_STANDEC_D (0x4003 << 2)
#define SIERRA_DET_STANDEC_E (0x4004 << 2)
#define SIERRA_PSM_LANECAL (0x4008 << 2)
#define SIERRA_PSM_DIAG (0x4015 << 2)
#define SIERRA_PSC_TX_A0 (0x4028 << 2)
#define SIERRA_PSC_TX_A1 (0x4029 << 2)
#define SIERRA_PSC_TX_A2 (0x402A << 2)
#define SIERRA_PSC_TX_A3 (0x402B << 2)
#define SIERRA_PSC_RX_A0 (0x4030 << 2)
#define SIERRA_PSC_RX_A1 (0x4031 << 2)
#define SIERRA_PSC_RX_A2 (0x4032 << 2)
#define SIERRA_PSC_RX_A3 (0x4033 << 2)
#define SIERRA_PLLCTRL_SUBRATE (0x403A << 2)
#define SIERRA_PLLCTRL_GEN_D (0x403E << 2)
#define SIERRA_DRVCTRL_ATTEN (0x406A << 2)
#define SIERRA_CLKPATHCTRL_TMR (0x4081 << 2)
#define SIERRA_RX_CREQ_FLTR_A_MODE1 (0x4087 << 2)
#define SIERRA_RX_CREQ_FLTR_A_MODE0 (0x4088 << 2)
#define SIERRA_CREQ_CCLKDET_MODE01 (0x408E << 2)
#define SIERRA_RX_CTLE_MAINTENANCE (0x4091 << 2)
#define SIERRA_CREQ_FSMCLK_SEL (0x4092 << 2)
#define SIERRA_CTLELUT_CTRL (0x4098 << 2)
#define SIERRA_DFE_ECMP_RATESEL (0x40C0 << 2)
#define SIERRA_DFE_SMP_RATESEL (0x40C1 << 2)
#define SIERRA_DEQ_VGATUNE_CTRL (0x40E1 << 2)
#define SIERRA_TMRVAL_MODE3 (0x416E << 2)
#define SIERRA_TMRVAL_MODE2 (0x416F << 2)
#define SIERRA_TMRVAL_MODE1 (0x4170 << 2)
#define SIERRA_TMRVAL_MODE0 (0x4171 << 2)
#define SIERRA_PICNT_MODE1 (0x4174 << 2)
#define SIERRA_CPI_OUTBUF_RATESEL (0x417C << 2)
#define SIERRA_LFPSFILT_NS (0x418A << 2)
#define SIERRA_LFPSFILT_RD (0x418B << 2)
#define SIERRA_LFPSFILT_MP (0x418C << 2)
#define SIERRA_SDFILT_H2L_A (0x4191 << 2)
#define SIERRA_COMMON_CDB_OFFSET 0x0
#define SIERRA_MACRO_ID_REG 0x0
#define SIERRA_CMN_PLLLC_MODE_PREG 0x48
#define SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG 0x49
#define SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG 0x4A
#define SIERRA_CMN_PLLLC_LOCK_CNTSTART_PREG 0x4B
#define SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG 0x4F
#define SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG 0x50
#define SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG 0x62
#define SIERRA_MACRO_ID 0x00007364
#define SIERRA_MAX_LANES 4
#define SIERRA_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \
((0x4000 << (block_offset)) + \
(((ln) << 9) << (reg_offset)))
#define SIERRA_DET_STANDEC_A_PREG 0x000
#define SIERRA_DET_STANDEC_B_PREG 0x001
#define SIERRA_DET_STANDEC_C_PREG 0x002
#define SIERRA_DET_STANDEC_D_PREG 0x003
#define SIERRA_DET_STANDEC_E_PREG 0x004
#define SIERRA_PSM_LANECAL_DLY_A1_RESETS_PREG 0x008
#define SIERRA_PSM_A0IN_TMR_PREG 0x009
#define SIERRA_PSM_DIAG_PREG 0x015
#define SIERRA_PSC_TX_A0_PREG 0x028
#define SIERRA_PSC_TX_A1_PREG 0x029
#define SIERRA_PSC_TX_A2_PREG 0x02A
#define SIERRA_PSC_TX_A3_PREG 0x02B
#define SIERRA_PSC_RX_A0_PREG 0x030
#define SIERRA_PSC_RX_A1_PREG 0x031
#define SIERRA_PSC_RX_A2_PREG 0x032
#define SIERRA_PSC_RX_A3_PREG 0x033
#define SIERRA_PLLCTRL_SUBRATE_PREG 0x03A
#define SIERRA_PLLCTRL_GEN_D_PREG 0x03E
#define SIERRA_PLLCTRL_CPGAIN_MODE_PREG 0x03F
#define SIERRA_PLLCTRL_STATUS_PREG 0x044
#define SIERRA_CLKPATH_BIASTRIM_PREG 0x04B
#define SIERRA_DFE_BIASTRIM_PREG 0x04C
#define SIERRA_DRVCTRL_ATTEN_PREG 0x06A
#define SIERRA_CLKPATHCTRL_TMR_PREG 0x081
#define SIERRA_RX_CREQ_FLTR_A_MODE3_PREG 0x085
#define SIERRA_RX_CREQ_FLTR_A_MODE2_PREG 0x086
#define SIERRA_RX_CREQ_FLTR_A_MODE1_PREG 0x087
#define SIERRA_RX_CREQ_FLTR_A_MODE0_PREG 0x088
#define SIERRA_CREQ_CCLKDET_MODE01_PREG 0x08E
#define SIERRA_RX_CTLE_MAINTENANCE_PREG 0x091
#define SIERRA_CREQ_FSMCLK_SEL_PREG 0x092
#define SIERRA_CREQ_EQ_CTRL_PREG 0x093
#define SIERRA_CREQ_SPARE_PREG 0x096
#define SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG 0x097
#define SIERRA_CTLELUT_CTRL_PREG 0x098
#define SIERRA_DFE_ECMP_RATESEL_PREG 0x0C0
#define SIERRA_DFE_SMP_RATESEL_PREG 0x0C1
#define SIERRA_DEQ_PHALIGN_CTRL 0x0C4
#define SIERRA_DEQ_CONCUR_CTRL1_PREG 0x0C8
#define SIERRA_DEQ_CONCUR_CTRL2_PREG 0x0C9
#define SIERRA_DEQ_EPIPWR_CTRL2_PREG 0x0CD
#define SIERRA_DEQ_FAST_MAINT_CYCLES_PREG 0x0CE
#define SIERRA_DEQ_ERRCMP_CTRL_PREG 0x0D0
#define SIERRA_DEQ_OFFSET_CTRL_PREG 0x0D8
#define SIERRA_DEQ_GAIN_CTRL_PREG 0x0E0
#define SIERRA_DEQ_VGATUNE_CTRL_PREG 0x0E1
#define SIERRA_DEQ_GLUT0 0x0E8
#define SIERRA_DEQ_GLUT1 0x0E9
#define SIERRA_DEQ_GLUT2 0x0EA
#define SIERRA_DEQ_GLUT3 0x0EB
#define SIERRA_DEQ_GLUT4 0x0EC
#define SIERRA_DEQ_GLUT5 0x0ED
#define SIERRA_DEQ_GLUT6 0x0EE
#define SIERRA_DEQ_GLUT7 0x0EF
#define SIERRA_DEQ_GLUT8 0x0F0
#define SIERRA_DEQ_GLUT9 0x0F1
#define SIERRA_DEQ_GLUT10 0x0F2
#define SIERRA_DEQ_GLUT11 0x0F3
#define SIERRA_DEQ_GLUT12 0x0F4
#define SIERRA_DEQ_GLUT13 0x0F5
#define SIERRA_DEQ_GLUT14 0x0F6
#define SIERRA_DEQ_GLUT15 0x0F7
#define SIERRA_DEQ_GLUT16 0x0F8
#define SIERRA_DEQ_ALUT0 0x108
#define SIERRA_DEQ_ALUT1 0x109
#define SIERRA_DEQ_ALUT2 0x10A
#define SIERRA_DEQ_ALUT3 0x10B
#define SIERRA_DEQ_ALUT4 0x10C
#define SIERRA_DEQ_ALUT5 0x10D
#define SIERRA_DEQ_ALUT6 0x10E
#define SIERRA_DEQ_ALUT7 0x10F
#define SIERRA_DEQ_ALUT8 0x110
#define SIERRA_DEQ_ALUT9 0x111
#define SIERRA_DEQ_ALUT10 0x112
#define SIERRA_DEQ_ALUT11 0x113
#define SIERRA_DEQ_ALUT12 0x114
#define SIERRA_DEQ_ALUT13 0x115
#define SIERRA_DEQ_DFETAP_CTRL_PREG 0x128
#define SIERRA_DFE_EN_1010_IGNORE_PREG 0x134
#define SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG 0x150
#define SIERRA_DEQ_TAU_CTRL2_PREG 0x151
#define SIERRA_DEQ_PICTRL_PREG 0x161
#define SIERRA_CPICAL_TMRVAL_MODE1_PREG 0x170
#define SIERRA_CPICAL_TMRVAL_MODE0_PREG 0x171
#define SIERRA_CPICAL_PICNT_MODE1_PREG 0x174
#define SIERRA_CPI_OUTBUF_RATESEL_PREG 0x17C
#define SIERRA_CPICAL_RES_STARTCODE_MODE23_PREG 0x183
#define SIERRA_LFPSDET_SUPPORT_PREG 0x188
#define SIERRA_LFPSFILT_NS_PREG 0x18A
#define SIERRA_LFPSFILT_RD_PREG 0x18B
#define SIERRA_LFPSFILT_MP_PREG 0x18C
#define SIERRA_SIGDET_SUPPORT_PREG 0x190
#define SIERRA_SDFILT_H2L_A_PREG 0x191
#define SIERRA_SDFILT_L2H_PREG 0x193
#define SIERRA_RXBUFFER_CTLECTRL_PREG 0x19E
#define SIERRA_RXBUFFER_RCDFECTRL_PREG 0x19F
#define SIERRA_RXBUFFER_DFECTRL_PREG 0x1A0
#define SIERRA_DEQ_TAU_CTRL1_FAST_MAINT_PREG 0x14F
#define SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG 0x150
#define SIERRA_PHY_CONFIG_CTRL_OFFSET(block_offset) \
(0xc000 << (block_offset))
#define SIERRA_PHY_PLL_CFG 0xe
#define SIERRA_MACRO_ID 0x00007364
#define SIERRA_MAX_LANES 16
#define PLL_LOCK_TIME 100000
static const struct reg_field macro_id_type =
REG_FIELD(SIERRA_MACRO_ID_REG, 0, 15);
static const struct reg_field phy_pll_cfg_1 =
REG_FIELD(SIERRA_PHY_PLL_CFG, 1, 1);
static const struct reg_field pllctrl_lock =
REG_FIELD(SIERRA_PLLCTRL_STATUS_PREG, 0, 0);
struct cdns_sierra_inst {
struct phy *phy;
@@ -80,53 +166,172 @@ struct cdns_reg_pairs {
struct cdns_sierra_data {
u32 id_value;
u32 pcie_regs;
u32 usb_regs;
struct cdns_reg_pairs *pcie_vals;
struct cdns_reg_pairs *usb_vals;
u8 block_offset_shift;
u8 reg_offset_shift;
u32 pcie_cmn_regs;
u32 pcie_ln_regs;
u32 usb_cmn_regs;
u32 usb_ln_regs;
struct cdns_reg_pairs *pcie_cmn_vals;
struct cdns_reg_pairs *pcie_ln_vals;
struct cdns_reg_pairs *usb_cmn_vals;
struct cdns_reg_pairs *usb_ln_vals;
};
struct cdns_regmap_cdb_context {
struct device *dev;
void __iomem *base;
u8 reg_offset_shift;
};
struct cdns_sierra_phy {
struct device *dev;
void __iomem *base;
struct regmap *regmap;
struct cdns_sierra_data *init_data;
struct cdns_sierra_inst phys[SIERRA_MAX_LANES];
struct reset_control *phy_rst;
struct reset_control *apb_rst;
struct regmap *regmap_lane_cdb[SIERRA_MAX_LANES];
struct regmap *regmap_phy_config_ctrl;
struct regmap *regmap_common_cdb;
struct regmap_field *macro_id_type;
struct regmap_field *phy_pll_cfg_1;
struct regmap_field *pllctrl_lock[SIERRA_MAX_LANES];
struct clk *clk;
struct clk *cmn_refclk_dig_div;
struct clk *cmn_refclk1_dig_div;
int nsubnodes;
u32 num_lanes;
bool autoconf;
};
static void cdns_sierra_phy_init(struct phy *gphy)
static int cdns_regmap_write(void *context, unsigned int reg, unsigned int val)
{
struct cdns_regmap_cdb_context *ctx = context;
u32 offset = reg << ctx->reg_offset_shift;
writew(val, ctx->base + offset);
return 0;
}
static int cdns_regmap_read(void *context, unsigned int reg, unsigned int *val)
{
struct cdns_regmap_cdb_context *ctx = context;
u32 offset = reg << ctx->reg_offset_shift;
*val = readw(ctx->base + offset);
return 0;
}
#define SIERRA_LANE_CDB_REGMAP_CONF(n) \
{ \
.name = "sierra_lane" n "_cdb", \
.reg_stride = 1, \
.fast_io = true, \
.reg_write = cdns_regmap_write, \
.reg_read = cdns_regmap_read, \
}
static struct regmap_config cdns_sierra_lane_cdb_config[] = {
SIERRA_LANE_CDB_REGMAP_CONF("0"),
SIERRA_LANE_CDB_REGMAP_CONF("1"),
SIERRA_LANE_CDB_REGMAP_CONF("2"),
SIERRA_LANE_CDB_REGMAP_CONF("3"),
SIERRA_LANE_CDB_REGMAP_CONF("4"),
SIERRA_LANE_CDB_REGMAP_CONF("5"),
SIERRA_LANE_CDB_REGMAP_CONF("6"),
SIERRA_LANE_CDB_REGMAP_CONF("7"),
SIERRA_LANE_CDB_REGMAP_CONF("8"),
SIERRA_LANE_CDB_REGMAP_CONF("9"),
SIERRA_LANE_CDB_REGMAP_CONF("10"),
SIERRA_LANE_CDB_REGMAP_CONF("11"),
SIERRA_LANE_CDB_REGMAP_CONF("12"),
SIERRA_LANE_CDB_REGMAP_CONF("13"),
SIERRA_LANE_CDB_REGMAP_CONF("14"),
SIERRA_LANE_CDB_REGMAP_CONF("15"),
};
static struct regmap_config cdns_sierra_common_cdb_config = {
.name = "sierra_common_cdb",
.reg_stride = 1,
.fast_io = true,
.reg_write = cdns_regmap_write,
.reg_read = cdns_regmap_read,
};
static struct regmap_config cdns_sierra_phy_config_ctrl_config = {
.name = "sierra_phy_config_ctrl",
.reg_stride = 1,
.fast_io = true,
.reg_write = cdns_regmap_write,
.reg_read = cdns_regmap_read,
};
static int cdns_sierra_phy_init(struct phy *gphy)
{
struct cdns_sierra_inst *ins = phy_get_drvdata(gphy);
struct cdns_sierra_phy *phy = dev_get_drvdata(gphy->dev.parent);
struct regmap *regmap;
int i, j;
struct cdns_reg_pairs *vals;
u32 num_regs;
struct cdns_reg_pairs *cmn_vals, *ln_vals;
u32 num_cmn_regs, num_ln_regs;
/* Initialise the PHY registers, unless auto configured */
if (phy->autoconf)
return 0;
clk_set_rate(phy->cmn_refclk_dig_div, 25000000);
clk_set_rate(phy->cmn_refclk1_dig_div, 25000000);
if (ins->phy_type == PHY_TYPE_PCIE) {
num_regs = phy->init_data->pcie_regs;
vals = phy->init_data->pcie_vals;
num_cmn_regs = phy->init_data->pcie_cmn_regs;
num_ln_regs = phy->init_data->pcie_ln_regs;
cmn_vals = phy->init_data->pcie_cmn_vals;
ln_vals = phy->init_data->pcie_ln_vals;
} else if (ins->phy_type == PHY_TYPE_USB3) {
num_regs = phy->init_data->usb_regs;
vals = phy->init_data->usb_vals;
num_cmn_regs = phy->init_data->usb_cmn_regs;
num_ln_regs = phy->init_data->usb_ln_regs;
cmn_vals = phy->init_data->usb_cmn_vals;
ln_vals = phy->init_data->usb_ln_vals;
} else {
return;
return -EINVAL;
}
for (i = 0; i < ins->num_lanes; i++)
for (j = 0; j < num_regs ; j++)
writel(vals[j].val, phy->base +
vals[j].off + (i + ins->mlane) * 0x800);
regmap = phy->regmap_common_cdb;
for (j = 0; j < num_cmn_regs ; j++)
regmap_write(regmap, cmn_vals[j].off, cmn_vals[j].val);
for (i = 0; i < ins->num_lanes; i++) {
for (j = 0; j < num_ln_regs ; j++) {
regmap = phy->regmap_lane_cdb[i + ins->mlane];
regmap_write(regmap, ln_vals[j].off, ln_vals[j].val);
}
}
return 0;
}
static int cdns_sierra_phy_on(struct phy *gphy)
{
struct cdns_sierra_phy *sp = dev_get_drvdata(gphy->dev.parent);
struct cdns_sierra_inst *ins = phy_get_drvdata(gphy);
struct device *dev = sp->dev;
u32 val;
int ret;
/* Take the PHY lane group out of reset */
return reset_control_deassert(ins->lnk_rst);
ret = reset_control_deassert(ins->lnk_rst);
if (ret) {
dev_err(dev, "Failed to take the PHY lane out of reset\n");
return ret;
}
ret = regmap_field_read_poll_timeout(sp->pllctrl_lock[ins->mlane],
val, val, 1000, PLL_LOCK_TIME);
if (ret < 0)
dev_err(dev, "PLL lock of lane failed\n");
return ret;
}
static int cdns_sierra_phy_off(struct phy *gphy)
@@ -136,9 +341,20 @@ static int cdns_sierra_phy_off(struct phy *gphy)
return reset_control_assert(ins->lnk_rst);
}
static int cdns_sierra_phy_reset(struct phy *gphy)
{
struct cdns_sierra_phy *sp = dev_get_drvdata(gphy->dev.parent);
reset_control_assert(sp->phy_rst);
reset_control_deassert(sp->phy_rst);
return 0;
};
static const struct phy_ops ops = {
.init = cdns_sierra_phy_init,
.power_on = cdns_sierra_phy_on,
.power_off = cdns_sierra_phy_off,
.reset = cdns_sierra_phy_reset,
.owner = THIS_MODULE,
};
@@ -159,41 +375,152 @@ static int cdns_sierra_get_optional(struct cdns_sierra_inst *inst,
static const struct of_device_id cdns_sierra_id_table[];
static struct regmap *cdns_regmap_init(struct device *dev, void __iomem *base,
u32 block_offset, u8 reg_offset_shift,
const struct regmap_config *config)
{
struct cdns_regmap_cdb_context *ctx;
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return ERR_PTR(-ENOMEM);
ctx->dev = dev;
ctx->base = base + block_offset;
ctx->reg_offset_shift = reg_offset_shift;
return devm_regmap_init(dev, NULL, ctx, config);
}
static int cdns_regfield_init(struct cdns_sierra_phy *sp)
{
struct device *dev = sp->dev;
struct regmap_field *field;
struct regmap *regmap;
int i;
regmap = sp->regmap_common_cdb;
field = devm_regmap_field_alloc(dev, regmap, macro_id_type);
if (IS_ERR(field)) {
dev_err(dev, "MACRO_ID_TYPE reg field init failed\n");
return PTR_ERR(field);
}
sp->macro_id_type = field;
regmap = sp->regmap_phy_config_ctrl;
field = devm_regmap_field_alloc(dev, regmap, phy_pll_cfg_1);
if (IS_ERR(field)) {
dev_err(dev, "PHY_PLL_CFG_1 reg field init failed\n");
return PTR_ERR(field);
}
sp->phy_pll_cfg_1 = field;
for (i = 0; i < SIERRA_MAX_LANES; i++) {
regmap = sp->regmap_lane_cdb[i];
field = devm_regmap_field_alloc(dev, regmap, pllctrl_lock);
if (IS_ERR(field)) {
dev_err(dev, "P%d_ENABLE reg field init failed\n", i);
return PTR_ERR(field);
}
sp->pllctrl_lock[i] = field;
}
return 0;
}
static int cdns_regmap_init_blocks(struct cdns_sierra_phy *sp,
void __iomem *base, u8 block_offset_shift,
u8 reg_offset_shift)
{
struct device *dev = sp->dev;
struct regmap *regmap;
u32 block_offset;
int i;
for (i = 0; i < SIERRA_MAX_LANES; i++) {
block_offset = SIERRA_LANE_CDB_OFFSET(i, block_offset_shift,
reg_offset_shift);
regmap = cdns_regmap_init(dev, base, block_offset,
reg_offset_shift,
&cdns_sierra_lane_cdb_config[i]);
if (IS_ERR(regmap)) {
dev_err(dev, "Failed to init lane CDB regmap\n");
return PTR_ERR(regmap);
}
sp->regmap_lane_cdb[i] = regmap;
}
regmap = cdns_regmap_init(dev, base, SIERRA_COMMON_CDB_OFFSET,
reg_offset_shift,
&cdns_sierra_common_cdb_config);
if (IS_ERR(regmap)) {
dev_err(dev, "Failed to init common CDB regmap\n");
return PTR_ERR(regmap);
}
sp->regmap_common_cdb = regmap;
block_offset = SIERRA_PHY_CONFIG_CTRL_OFFSET(block_offset_shift);
regmap = cdns_regmap_init(dev, base, block_offset, reg_offset_shift,
&cdns_sierra_phy_config_ctrl_config);
if (IS_ERR(regmap)) {
dev_err(dev, "Failed to init PHY config and control regmap\n");
return PTR_ERR(regmap);
}
sp->regmap_phy_config_ctrl = regmap;
return 0;
}
static int cdns_sierra_phy_probe(struct platform_device *pdev)
{
struct cdns_sierra_phy *sp;
struct phy_provider *phy_provider;
struct device *dev = &pdev->dev;
const struct of_device_id *match;
struct cdns_sierra_data *data;
unsigned int id_value;
struct resource *res;
int i, ret, node = 0;
void __iomem *base;
struct clk *clk;
struct device_node *dn = dev->of_node, *child;
if (of_get_child_count(dn) == 0)
return -ENODEV;
/* Get init data for this PHY */
match = of_match_device(cdns_sierra_id_table, dev);
if (!match)
return -EINVAL;
data = (struct cdns_sierra_data *)match->data;
sp = devm_kzalloc(dev, sizeof(*sp), GFP_KERNEL);
if (!sp)
return -ENOMEM;
dev_set_drvdata(dev, sp);
sp->dev = dev;
sp->init_data = data;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
sp->base = devm_ioremap_resource(dev, res);
if (IS_ERR(sp->base)) {
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base)) {
dev_err(dev, "missing \"reg\"\n");
return PTR_ERR(sp->base);
return PTR_ERR(base);
}
/* Get init data for this PHY */
match = of_match_device(cdns_sierra_id_table, dev);
if (!match)
return -EINVAL;
sp->init_data = (struct cdns_sierra_data *)match->data;
ret = cdns_regmap_init_blocks(sp, base, data->block_offset_shift,
data->reg_offset_shift);
if (ret)
return ret;
ret = cdns_regfield_init(sp);
if (ret)
return ret;
platform_set_drvdata(pdev, sp);
sp->clk = devm_clk_get(dev, "phy_clk");
sp->clk = devm_clk_get_optional(dev, "phy_clk");
if (IS_ERR(sp->clk)) {
dev_err(dev, "failed to get clock phy_clk\n");
return PTR_ERR(sp->clk);
@@ -205,12 +532,28 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
return PTR_ERR(sp->phy_rst);
}
sp->apb_rst = devm_reset_control_get(dev, "sierra_apb");
sp->apb_rst = devm_reset_control_get_optional(dev, "sierra_apb");
if (IS_ERR(sp->apb_rst)) {
dev_err(dev, "failed to get apb reset\n");
return PTR_ERR(sp->apb_rst);
}
clk = devm_clk_get_optional(dev, "cmn_refclk_dig_div");
if (IS_ERR(clk)) {
dev_err(dev, "cmn_refclk_dig_div clock not found\n");
ret = PTR_ERR(clk);
return ret;
}
sp->cmn_refclk_dig_div = clk;
clk = devm_clk_get_optional(dev, "cmn_refclk1_dig_div");
if (IS_ERR(clk)) {
dev_err(dev, "cmn_refclk1_dig_div clock not found\n");
ret = PTR_ERR(clk);
return ret;
}
sp->cmn_refclk1_dig_div = clk;
ret = clk_prepare_enable(sp->clk);
if (ret)
return ret;
@@ -219,7 +562,8 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
reset_control_deassert(sp->apb_rst);
/* Check that PHY is present */
if (sp->init_data->id_value != readl(sp->base)) {
regmap_field_read(sp->macro_id_type, &id_value);
if (sp->init_data->id_value != id_value) {
ret = -EINVAL;
goto clk_disable;
}
@@ -230,7 +574,7 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
struct phy *gphy;
sp->phys[node].lnk_rst =
of_reset_control_get_exclusive_by_index(child, 0);
of_reset_control_array_get_exclusive(child);
if (IS_ERR(sp->phys[node].lnk_rst)) {
dev_err(dev, "failed to get reset %s\n",
@@ -248,6 +592,8 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
}
}
sp->num_lanes += sp->phys[node].num_lanes;
gphy = devm_phy_create(dev, child, &ops);
if (IS_ERR(gphy)) {
@@ -257,17 +603,18 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
sp->phys[node].phy = gphy;
phy_set_drvdata(gphy, &sp->phys[node]);
/* Initialise the PHY registers, unless auto configured */
if (!sp->autoconf)
cdns_sierra_phy_init(gphy);
node++;
}
sp->nsubnodes = node;
if (sp->num_lanes > SIERRA_MAX_LANES) {
dev_err(dev, "Invalid lane configuration\n");
goto put_child2;
}
/* If more than one subnode, configure the PHY as multilink */
if (!sp->autoconf && sp->nsubnodes > 1)
writel(2, sp->base + SIERRA_PHY_PLL_CFG);
regmap_field_write(sp->phy_pll_cfg_1, 0x1);
pm_runtime_enable(dev);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
@@ -288,7 +635,7 @@ clk_disable:
static int cdns_sierra_phy_remove(struct platform_device *pdev)
{
struct cdns_sierra_phy *phy = dev_get_drvdata(pdev->dev.parent);
struct cdns_sierra_phy *phy = platform_get_drvdata(pdev);
int i;
reset_control_assert(phy->phy_rst);
@@ -306,68 +653,158 @@ static int cdns_sierra_phy_remove(struct platform_device *pdev)
return 0;
}
static struct cdns_reg_pairs cdns_usb_regs[] = {
/*
* Write USB configuration parameters to the PHY.
* These values are specific to this specific hardware
* configuration.
*/
{0xFE0A, SIERRA_DET_STANDEC_A},
{0x000F, SIERRA_DET_STANDEC_B},
{0x55A5, SIERRA_DET_STANDEC_C},
{0x69AD, SIERRA_DET_STANDEC_D},
{0x0241, SIERRA_DET_STANDEC_E},
{0x0110, SIERRA_PSM_LANECAL},
{0xCF00, SIERRA_PSM_DIAG},
{0x001F, SIERRA_PSC_TX_A0},
{0x0007, SIERRA_PSC_TX_A1},
{0x0003, SIERRA_PSC_TX_A2},
{0x0003, SIERRA_PSC_TX_A3},
{0x0FFF, SIERRA_PSC_RX_A0},
{0x0003, SIERRA_PSC_RX_A1},
{0x0003, SIERRA_PSC_RX_A2},
{0x0001, SIERRA_PSC_RX_A3},
{0x0001, SIERRA_PLLCTRL_SUBRATE},
{0x0406, SIERRA_PLLCTRL_GEN_D},
{0x0000, SIERRA_DRVCTRL_ATTEN},
{0x823E, SIERRA_CLKPATHCTRL_TMR},
{0x078F, SIERRA_RX_CREQ_FLTR_A_MODE1},
{0x078F, SIERRA_RX_CREQ_FLTR_A_MODE0},
{0x7B3C, SIERRA_CREQ_CCLKDET_MODE01},
{0x023C, SIERRA_RX_CTLE_MAINTENANCE},
{0x3232, SIERRA_CREQ_FSMCLK_SEL},
{0x8452, SIERRA_CTLELUT_CTRL},
{0x4121, SIERRA_DFE_ECMP_RATESEL},
{0x4121, SIERRA_DFE_SMP_RATESEL},
{0x9999, SIERRA_DEQ_VGATUNE_CTRL},
{0x0330, SIERRA_TMRVAL_MODE0},
{0x01FF, SIERRA_PICNT_MODE1},
{0x0009, SIERRA_CPI_OUTBUF_RATESEL},
{0x000F, SIERRA_LFPSFILT_NS},
{0x0009, SIERRA_LFPSFILT_RD},
{0x0001, SIERRA_LFPSFILT_MP},
{0x8013, SIERRA_SDFILT_H2L_A},
{0x0400, SIERRA_TMRVAL_MODE1},
/* refclk100MHz_32b_PCIe_cmn_pll_ext_ssc */
static struct cdns_reg_pairs cdns_pcie_cmn_regs_ext_ssc[] = {
{0x2106, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG},
{0x2106, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG},
{0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG},
{0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG},
{0x1B1B, SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG}
};
static struct cdns_reg_pairs cdns_pcie_regs[] = {
/*
* Write PCIe configuration parameters to the PHY.
* These values are specific to this specific hardware
* configuration.
*/
{0x891f, SIERRA_DET_STANDEC_D},
{0x0053, SIERRA_DET_STANDEC_E},
{0x0400, SIERRA_TMRVAL_MODE2},
{0x0200, SIERRA_TMRVAL_MODE3},
/* refclk100MHz_32b_PCIe_ln_ext_ssc */
static struct cdns_reg_pairs cdns_pcie_ln_regs_ext_ssc[] = {
{0x813E, SIERRA_CLKPATHCTRL_TMR_PREG},
{0x8047, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG},
{0x808F, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG},
{0x808F, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG},
{0x808F, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG},
{0x033C, SIERRA_RX_CTLE_MAINTENANCE_PREG},
{0x44CC, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG}
};
/* refclk100MHz_20b_USB_cmn_pll_ext_ssc */
static struct cdns_reg_pairs cdns_usb_cmn_regs_ext_ssc[] = {
{0x2085, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG},
{0x2085, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG},
{0x0000, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG},
{0x0000, SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG}
};
/* refclk100MHz_20b_USB_ln_ext_ssc */
static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = {
{0xFE0A, SIERRA_DET_STANDEC_A_PREG},
{0x000F, SIERRA_DET_STANDEC_B_PREG},
{0x00A5, SIERRA_DET_STANDEC_C_PREG},
{0x69ad, SIERRA_DET_STANDEC_D_PREG},
{0x0241, SIERRA_DET_STANDEC_E_PREG},
{0x0010, SIERRA_PSM_LANECAL_DLY_A1_RESETS_PREG},
{0x0014, SIERRA_PSM_A0IN_TMR_PREG},
{0xCF00, SIERRA_PSM_DIAG_PREG},
{0x001F, SIERRA_PSC_TX_A0_PREG},
{0x0007, SIERRA_PSC_TX_A1_PREG},
{0x0003, SIERRA_PSC_TX_A2_PREG},
{0x0003, SIERRA_PSC_TX_A3_PREG},
{0x0FFF, SIERRA_PSC_RX_A0_PREG},
{0x0619, SIERRA_PSC_RX_A1_PREG},
{0x0003, SIERRA_PSC_RX_A2_PREG},
{0x0001, SIERRA_PSC_RX_A3_PREG},
{0x0001, SIERRA_PLLCTRL_SUBRATE_PREG},
{0x0406, SIERRA_PLLCTRL_GEN_D_PREG},
{0x5233, SIERRA_PLLCTRL_CPGAIN_MODE_PREG},
{0x00CA, SIERRA_CLKPATH_BIASTRIM_PREG},
{0x2512, SIERRA_DFE_BIASTRIM_PREG},
{0x0000, SIERRA_DRVCTRL_ATTEN_PREG},
{0x873E, SIERRA_CLKPATHCTRL_TMR_PREG},
{0x03CF, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG},
{0x01CE, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG},
{0x7B3C, SIERRA_CREQ_CCLKDET_MODE01_PREG},
{0x033F, SIERRA_RX_CTLE_MAINTENANCE_PREG},
{0x3232, SIERRA_CREQ_FSMCLK_SEL_PREG},
{0x0000, SIERRA_CREQ_EQ_CTRL_PREG},
{0x8000, SIERRA_CREQ_SPARE_PREG},
{0xCC44, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG},
{0x8453, SIERRA_CTLELUT_CTRL_PREG},
{0x4110, SIERRA_DFE_ECMP_RATESEL_PREG},
{0x4110, SIERRA_DFE_SMP_RATESEL_PREG},
{0x0002, SIERRA_DEQ_PHALIGN_CTRL},
{0x3200, SIERRA_DEQ_CONCUR_CTRL1_PREG},
{0x5064, SIERRA_DEQ_CONCUR_CTRL2_PREG},
{0x0030, SIERRA_DEQ_EPIPWR_CTRL2_PREG},
{0x0048, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG},
{0x5A5A, SIERRA_DEQ_ERRCMP_CTRL_PREG},
{0x02F5, SIERRA_DEQ_OFFSET_CTRL_PREG},
{0x02F5, SIERRA_DEQ_GAIN_CTRL_PREG},
{0x9A8A, SIERRA_DEQ_VGATUNE_CTRL_PREG},
{0x0014, SIERRA_DEQ_GLUT0},
{0x0014, SIERRA_DEQ_GLUT1},
{0x0014, SIERRA_DEQ_GLUT2},
{0x0014, SIERRA_DEQ_GLUT3},
{0x0014, SIERRA_DEQ_GLUT4},
{0x0014, SIERRA_DEQ_GLUT5},
{0x0014, SIERRA_DEQ_GLUT6},
{0x0014, SIERRA_DEQ_GLUT7},
{0x0014, SIERRA_DEQ_GLUT8},
{0x0014, SIERRA_DEQ_GLUT9},
{0x0014, SIERRA_DEQ_GLUT10},
{0x0014, SIERRA_DEQ_GLUT11},
{0x0014, SIERRA_DEQ_GLUT12},
{0x0014, SIERRA_DEQ_GLUT13},
{0x0014, SIERRA_DEQ_GLUT14},
{0x0014, SIERRA_DEQ_GLUT15},
{0x0014, SIERRA_DEQ_GLUT16},
{0x0BAE, SIERRA_DEQ_ALUT0},
{0x0AEB, SIERRA_DEQ_ALUT1},
{0x0A28, SIERRA_DEQ_ALUT2},
{0x0965, SIERRA_DEQ_ALUT3},
{0x08A2, SIERRA_DEQ_ALUT4},
{0x07DF, SIERRA_DEQ_ALUT5},
{0x071C, SIERRA_DEQ_ALUT6},
{0x0659, SIERRA_DEQ_ALUT7},
{0x0596, SIERRA_DEQ_ALUT8},
{0x0514, SIERRA_DEQ_ALUT9},
{0x0492, SIERRA_DEQ_ALUT10},
{0x0410, SIERRA_DEQ_ALUT11},
{0x038E, SIERRA_DEQ_ALUT12},
{0x030C, SIERRA_DEQ_ALUT13},
{0x03F4, SIERRA_DEQ_DFETAP_CTRL_PREG},
{0x0001, SIERRA_DFE_EN_1010_IGNORE_PREG},
{0x3C01, SIERRA_DEQ_TAU_CTRL1_FAST_MAINT_PREG},
{0x3C40, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG},
{0x1C08, SIERRA_DEQ_TAU_CTRL2_PREG},
{0x0033, SIERRA_DEQ_PICTRL_PREG},
{0x0400, SIERRA_CPICAL_TMRVAL_MODE1_PREG},
{0x0330, SIERRA_CPICAL_TMRVAL_MODE0_PREG},
{0x01FF, SIERRA_CPICAL_PICNT_MODE1_PREG},
{0x0009, SIERRA_CPI_OUTBUF_RATESEL_PREG},
{0x3232, SIERRA_CPICAL_RES_STARTCODE_MODE23_PREG},
{0x0005, SIERRA_LFPSDET_SUPPORT_PREG},
{0x000F, SIERRA_LFPSFILT_NS_PREG},
{0x0009, SIERRA_LFPSFILT_RD_PREG},
{0x0001, SIERRA_LFPSFILT_MP_PREG},
{0x8013, SIERRA_SDFILT_H2L_A_PREG},
{0x8009, SIERRA_SDFILT_L2H_PREG},
{0x0024, SIERRA_RXBUFFER_CTLECTRL_PREG},
{0x0020, SIERRA_RXBUFFER_RCDFECTRL_PREG},
{0x4243, SIERRA_RXBUFFER_DFECTRL_PREG}
};
static const struct cdns_sierra_data cdns_map_sierra = {
SIERRA_MACRO_ID,
ARRAY_SIZE(cdns_pcie_regs),
ARRAY_SIZE(cdns_usb_regs),
cdns_pcie_regs,
cdns_usb_regs
0x2,
0x2,
ARRAY_SIZE(cdns_pcie_cmn_regs_ext_ssc),
ARRAY_SIZE(cdns_pcie_ln_regs_ext_ssc),
ARRAY_SIZE(cdns_usb_cmn_regs_ext_ssc),
ARRAY_SIZE(cdns_usb_ln_regs_ext_ssc),
cdns_pcie_cmn_regs_ext_ssc,
cdns_pcie_ln_regs_ext_ssc,
cdns_usb_cmn_regs_ext_ssc,
cdns_usb_ln_regs_ext_ssc,
};
static const struct cdns_sierra_data cdns_ti_map_sierra = {
SIERRA_MACRO_ID,
0x0,
0x1,
ARRAY_SIZE(cdns_pcie_cmn_regs_ext_ssc),
ARRAY_SIZE(cdns_pcie_ln_regs_ext_ssc),
ARRAY_SIZE(cdns_usb_cmn_regs_ext_ssc),
ARRAY_SIZE(cdns_usb_ln_regs_ext_ssc),
cdns_pcie_cmn_regs_ext_ssc,
cdns_pcie_ln_regs_ext_ssc,
cdns_usb_cmn_regs_ext_ssc,
cdns_usb_ln_regs_ext_ssc,
};
static const struct of_device_id cdns_sierra_id_table[] = {
@@ -375,6 +812,10 @@ static const struct of_device_id cdns_sierra_id_table[] = {
.compatible = "cdns,sierra-phy-t0",
.data = &cdns_map_sierra,
},
{
.compatible = "ti,sierra-phy-t0",
.data = &cdns_ti_map_sierra,
},
{}
};
MODULE_DEVICE_TABLE(of, cdns_sierra_id_table);

View File

@@ -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"

View File

@@ -0,0 +1,9 @@
# SPDX-License-Identifier: GPL-2.0
#
# Phy drivers for Intel Lightning Mountain(LGM) platform
#
config PHY_INTEL_EMMC
tristate "Intel EMMC PHY driver"
select GENERIC_PHY
help
Enable this to support the Intel EMMC PHY

View File

@@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_PHY_INTEL_EMMC) += phy-intel-emmc.o

View File

@@ -0,0 +1,284 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Intel eMMC PHY driver
* Copyright (C) 2019 Intel, Corp.
*/
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
/* eMMC phy register definitions */
#define EMMC_PHYCTRL0_REG 0xa8
#define DR_TY_MASK GENMASK(30, 28)
#define DR_TY_SHIFT(x) (((x) << 28) & DR_TY_MASK)
#define OTAPDLYENA BIT(14)
#define OTAPDLYSEL_MASK GENMASK(13, 10)
#define OTAPDLYSEL_SHIFT(x) (((x) << 10) & OTAPDLYSEL_MASK)
#define EMMC_PHYCTRL1_REG 0xac
#define PDB_MASK BIT(0)
#define PDB_SHIFT(x) (((x) << 0) & PDB_MASK)
#define ENDLL_MASK BIT(7)
#define ENDLL_SHIFT(x) (((x) << 7) & ENDLL_MASK)
#define EMMC_PHYCTRL2_REG 0xb0
#define FRQSEL_25M 0
#define FRQSEL_50M 1
#define FRQSEL_100M 2
#define FRQSEL_150M 3
#define FRQSEL_MASK GENMASK(24, 22)
#define FRQSEL_SHIFT(x) (((x) << 22) & FRQSEL_MASK)
#define EMMC_PHYSTAT_REG 0xbc
#define CALDONE_MASK BIT(9)
#define DLLRDY_MASK BIT(8)
#define IS_CALDONE(x) ((x) & CALDONE_MASK)
#define IS_DLLRDY(x) ((x) & DLLRDY_MASK)
struct intel_emmc_phy {
struct regmap *syscfg;
struct clk *emmcclk;
};
static int intel_emmc_phy_power(struct phy *phy, bool on_off)
{
struct intel_emmc_phy *priv = phy_get_drvdata(phy);
unsigned int caldone;
unsigned int dllrdy;
unsigned int freqsel;
unsigned long rate;
int ret, quot;
/*
* Keep phyctrl_pdb and phyctrl_endll low to allow
* initialization of CALIO state M/C DFFs
*/
ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL1_REG, PDB_MASK,
PDB_SHIFT(0));
if (ret) {
dev_err(&phy->dev, "CALIO power down bar failed: %d\n", ret);
return ret;
}
/* Already finish power_off above */
if (!on_off)
return 0;
rate = clk_get_rate(priv->emmcclk);
quot = DIV_ROUND_CLOSEST(rate, 50000000);
if (quot > FRQSEL_150M)
dev_warn(&phy->dev, "Unsupported rate: %lu\n", rate);
freqsel = clamp_t(int, quot, FRQSEL_25M, FRQSEL_150M);
/*
* According to the user manual, calpad calibration
* cycle takes more than 2us without the minimal recommended
* value, so we may need a little margin here
*/
udelay(5);
ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL1_REG, PDB_MASK,
PDB_SHIFT(1));
if (ret) {
dev_err(&phy->dev, "CALIO power down bar failed: %d\n", ret);
return ret;
}
/*
* According to the user manual, it asks driver to wait 5us for
* calpad busy trimming. However it is documented that this value is
* PVT(A.K.A process,voltage and temperature) relevant, so some
* failure cases are found which indicates we should be more tolerant
* to calpad busy trimming.
*/
ret = regmap_read_poll_timeout(priv->syscfg, EMMC_PHYSTAT_REG,
caldone, IS_CALDONE(caldone),
0, 50);
if (ret) {
dev_err(&phy->dev, "caldone failed, ret=%d\n", ret);
return ret;
}
/* Set the frequency of the DLL operation */
ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL2_REG, FRQSEL_MASK,
FRQSEL_SHIFT(freqsel));
if (ret) {
dev_err(&phy->dev, "set the frequency of dll failed:%d\n", ret);
return ret;
}
/* Turn on the DLL */
ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL1_REG, ENDLL_MASK,
ENDLL_SHIFT(1));
if (ret) {
dev_err(&phy->dev, "turn on the dll failed: %d\n", ret);
return ret;
}
/*
* After enabling analog DLL circuits docs say that we need 10.2 us if
* our source clock is at 50 MHz and that lock time scales linearly
* with clock speed. If we are powering on the PHY and the card clock
* is super slow (like 100 kHZ) this could take as long as 5.1 ms as
* per the math: 10.2 us * (50000000 Hz / 100000 Hz) => 5.1 ms
* Hopefully we won't be running at 100 kHz, but we should still make
* sure we wait long enough.
*
* NOTE: There appear to be corner cases where the DLL seems to take
* extra long to lock for reasons that aren't understood. In some
* extreme cases we've seen it take up to over 10ms (!). We'll be
* generous and give it 50ms.
*/
ret = regmap_read_poll_timeout(priv->syscfg,
EMMC_PHYSTAT_REG,
dllrdy, IS_DLLRDY(dllrdy),
0, 50 * USEC_PER_MSEC);
if (ret) {
dev_err(&phy->dev, "dllrdy failed. ret=%d\n", ret);
return ret;
}
return 0;
}
static int intel_emmc_phy_init(struct phy *phy)
{
struct intel_emmc_phy *priv = phy_get_drvdata(phy);
/*
* We purposely get the clock here and not in probe to avoid the
* circular dependency problem. We expect:
* - PHY driver to probe
* - SDHCI driver to start probe
* - SDHCI driver to register it's clock
* - SDHCI driver to get the PHY
* - SDHCI driver to init the PHY
*
* The clock is optional, so upon any error just return it like
* any other error to user.
*
*/
priv->emmcclk = clk_get_optional(&phy->dev, "emmcclk");
if (IS_ERR(priv->emmcclk)) {
dev_err(&phy->dev, "ERROR: getting emmcclk\n");
return PTR_ERR(priv->emmcclk);
}
return 0;
}
static int intel_emmc_phy_exit(struct phy *phy)
{
struct intel_emmc_phy *priv = phy_get_drvdata(phy);
clk_put(priv->emmcclk);
return 0;
}
static int intel_emmc_phy_power_on(struct phy *phy)
{
struct intel_emmc_phy *priv = phy_get_drvdata(phy);
int ret;
/* Drive impedance: 50 Ohm */
ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL0_REG, DR_TY_MASK,
DR_TY_SHIFT(6));
if (ret) {
dev_err(&phy->dev, "ERROR set drive-impednce-50ohm: %d\n", ret);
return ret;
}
/* Output tap delay: disable */
ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL0_REG, OTAPDLYENA,
0);
if (ret) {
dev_err(&phy->dev, "ERROR Set output tap delay : %d\n", ret);
return ret;
}
/* Output tap delay */
ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL0_REG,
OTAPDLYSEL_MASK, OTAPDLYSEL_SHIFT(4));
if (ret) {
dev_err(&phy->dev, "ERROR: output tap dly select: %d\n", ret);
return ret;
}
/* Power up eMMC phy analog blocks */
return intel_emmc_phy_power(phy, true);
}
static int intel_emmc_phy_power_off(struct phy *phy)
{
/* Power down eMMC phy analog blocks */
return intel_emmc_phy_power(phy, false);
}
static const struct phy_ops ops = {
.init = intel_emmc_phy_init,
.exit = intel_emmc_phy_exit,
.power_on = intel_emmc_phy_power_on,
.power_off = intel_emmc_phy_power_off,
.owner = THIS_MODULE,
};
static int intel_emmc_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct intel_emmc_phy *priv;
struct phy *generic_phy;
struct phy_provider *phy_provider;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
/* Get eMMC phy (accessed via chiptop) regmap */
priv->syscfg = syscon_regmap_lookup_by_phandle(np, "intel,syscon");
if (IS_ERR(priv->syscfg)) {
dev_err(dev, "failed to find syscon\n");
return PTR_ERR(priv->syscfg);
}
generic_phy = devm_phy_create(dev, np, &ops);
if (IS_ERR(generic_phy)) {
dev_err(dev, "failed to create PHY\n");
return PTR_ERR(generic_phy);
}
phy_set_drvdata(generic_phy, priv);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct of_device_id intel_emmc_phy_dt_ids[] = {
{ .compatible = "intel,lgm-emmc-phy" },
{}
};
MODULE_DEVICE_TABLE(of, intel_emmc_phy_dt_ids);
static struct platform_driver intel_emmc_driver = {
.probe = intel_emmc_phy_probe,
.driver = {
.name = "intel-emmc-phy",
.of_match_table = intel_emmc_phy_dt_ids,
},
};
module_platform_driver(intel_emmc_driver);
MODULE_AUTHOR("Peter Harliman Liem <peter.harliman.liem@intel.com>");
MODULE_DESCRIPTION("Intel eMMC PHY driver");
MODULE_LICENSE("GPL v2");

View File

@@ -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;
}

View File

@@ -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.

View File

@@ -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);

View File

@@ -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;

View File

@@ -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,

View File

@@ -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.
*/

View File

@@ -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.

View File

@@ -16,6 +16,7 @@
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/phy/phy.h>
#include <linux/phy/phy-mipi-dphy.h>
#include <linux/pm_runtime.h>
#include <linux/mfd/syscon.h>
@@ -167,31 +168,6 @@
#define DSI_PHY_STATUS 0xb0
#define PHY_LOCK BIT(0)
struct mipi_dphy_timing {
unsigned int clkmiss;
unsigned int clkpost;
unsigned int clkpre;
unsigned int clkprepare;
unsigned int clksettle;
unsigned int clktermen;
unsigned int clktrail;
unsigned int clkzero;
unsigned int dtermen;
unsigned int eot;
unsigned int hsexit;
unsigned int hsprepare;
unsigned int hszero;
unsigned int hssettle;
unsigned int hsskip;
unsigned int hstrail;
unsigned int init;
unsigned int lpx;
unsigned int taget;
unsigned int tago;
unsigned int tasure;
unsigned int wakeup;
};
struct inno_dsidphy {
struct device *dev;
struct clk *ref_clk;
@@ -201,7 +177,9 @@ struct inno_dsidphy {
void __iomem *host_base;
struct reset_control *rst;
enum phy_mode mode;
struct phy_configure_opts_mipi_dphy dphy_cfg;
struct clk *pll_clk;
struct {
struct clk_hw hw;
u8 prediv;
@@ -238,37 +216,79 @@ static void phy_update_bits(struct inno_dsidphy *inno,
writel(tmp, inno->phy_base + reg);
}
static void mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing,
unsigned long period)
static unsigned long inno_dsidphy_pll_calc_rate(struct inno_dsidphy *inno,
unsigned long rate)
{
/* Global Operation Timing Parameters */
timing->clkmiss = 0;
timing->clkpost = 70000 + 52 * period;
timing->clkpre = 8 * period;
timing->clkprepare = 65000;
timing->clksettle = 95000;
timing->clktermen = 0;
timing->clktrail = 80000;
timing->clkzero = 260000;
timing->dtermen = 0;
timing->eot = 0;
timing->hsexit = 120000;
timing->hsprepare = 65000 + 4 * period;
timing->hszero = 145000 + 6 * period;
timing->hssettle = 85000 + 6 * period;
timing->hsskip = 40000;
timing->hstrail = max(8 * period, 60000 + 4 * period);
timing->init = 100000000;
timing->lpx = 60000;
timing->taget = 5 * timing->lpx;
timing->tago = 4 * timing->lpx;
timing->tasure = 2 * timing->lpx;
timing->wakeup = 1000000000;
unsigned long prate = clk_get_rate(inno->ref_clk);
unsigned long best_freq = 0;
unsigned long fref, fout;
u8 min_prediv, max_prediv;
u8 _prediv, best_prediv = 1;
u16 _fbdiv, best_fbdiv = 1;
u32 min_delta = UINT_MAX;
/*
* The PLL output frequency can be calculated using a simple formula:
* PLL_Output_Frequency = (FREF / PREDIV * FBDIV) / 2
* PLL_Output_Frequency: it is equal to DDR-Clock-Frequency * 2
*/
fref = prate / 2;
if (rate > 1000000000UL)
fout = 1000000000UL;
else
fout = rate;
/* 5Mhz < Fref / prediv < 40MHz */
min_prediv = DIV_ROUND_UP(fref, 40000000);
max_prediv = fref / 5000000;
for (_prediv = min_prediv; _prediv <= max_prediv; _prediv++) {
u64 tmp;
u32 delta;
tmp = (u64)fout * _prediv;
do_div(tmp, fref);
_fbdiv = tmp;
/*
* The possible settings of feedback divider are
* 12, 13, 14, 16, ~ 511
*/
if (_fbdiv == 15)
continue;
if (_fbdiv < 12 || _fbdiv > 511)
continue;
tmp = (u64)_fbdiv * fref;
do_div(tmp, _prediv);
delta = abs(fout - tmp);
if (!delta) {
best_prediv = _prediv;
best_fbdiv = _fbdiv;
best_freq = tmp;
break;
} else if (delta < min_delta) {
best_prediv = _prediv;
best_fbdiv = _fbdiv;
best_freq = tmp;
min_delta = delta;
}
}
if (best_freq) {
inno->pll.prediv = best_prediv;
inno->pll.fbdiv = best_fbdiv;
inno->pll.rate = best_freq;
}
return best_freq;
}
static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
{
struct mipi_dphy_timing gotp;
struct phy_configure_opts_mipi_dphy *cfg = &inno->dphy_cfg;
const struct {
unsigned long rate;
u8 hs_prepare;
@@ -288,12 +308,14 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
{ 800000000, 0x21, 0x1f, 0x09, 0x29},
{1000000000, 0x09, 0x20, 0x09, 0x27},
};
u32 t_txbyteclkhs, t_txclkesc, ui;
u32 t_txbyteclkhs, t_txclkesc;
u32 txbyteclkhs, txclkesc, esc_clk_div;
u32 hs_exit, clk_post, clk_pre, wakeup, lpx, ta_go, ta_sure, ta_wait;
u32 hs_prepare, hs_trail, hs_zero, clk_lane_hs_zero, data_lane_hs_zero;
unsigned int i;
inno_dsidphy_pll_calc_rate(inno, cfg->hs_clk_rate);
/* Select MIPI mode */
phy_update_bits(inno, REGISTER_PART_LVDS, 0x03,
MODE_ENABLE_MASK, MIPI_MODE_ENABLE);
@@ -328,32 +350,27 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
txclkesc = txbyteclkhs / esc_clk_div;
t_txclkesc = div_u64(PSEC_PER_SEC, txclkesc);
ui = div_u64(PSEC_PER_SEC, inno->pll.rate);
memset(&gotp, 0, sizeof(gotp));
mipi_dphy_timing_get_default(&gotp, ui);
/*
* The value of counter for HS Ths-exit
* Ths-exit = Tpin_txbyteclkhs * value
*/
hs_exit = DIV_ROUND_UP(gotp.hsexit, t_txbyteclkhs);
hs_exit = DIV_ROUND_UP(cfg->hs_exit, t_txbyteclkhs);
/*
* The value of counter for HS Tclk-post
* Tclk-post = Tpin_txbyteclkhs * value
*/
clk_post = DIV_ROUND_UP(gotp.clkpost, t_txbyteclkhs);
clk_post = DIV_ROUND_UP(cfg->clk_post, t_txbyteclkhs);
/*
* The value of counter for HS Tclk-pre
* Tclk-pre = Tpin_txbyteclkhs * value
*/
clk_pre = DIV_ROUND_UP(gotp.clkpre, t_txbyteclkhs);
clk_pre = DIV_ROUND_UP(cfg->clk_pre, t_txbyteclkhs);
/*
* The value of counter for HS Tlpx Time
* Tlpx = Tpin_txbyteclkhs * (2 + value)
*/
lpx = DIV_ROUND_UP(gotp.lpx, t_txbyteclkhs);
lpx = DIV_ROUND_UP(cfg->lpx, t_txbyteclkhs);
if (lpx >= 2)
lpx -= 2;
@@ -362,19 +379,19 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
* Tta-go for turnaround
* Tta-go = Ttxclkesc * value
*/
ta_go = DIV_ROUND_UP(gotp.tago, t_txclkesc);
ta_go = DIV_ROUND_UP(cfg->ta_go, t_txclkesc);
/*
* The value of counter for HS Tta-sure
* Tta-sure for turnaround
* Tta-sure = Ttxclkesc * value
*/
ta_sure = DIV_ROUND_UP(gotp.tasure, t_txclkesc);
ta_sure = DIV_ROUND_UP(cfg->ta_sure, t_txclkesc);
/*
* The value of counter for HS Tta-wait
* Tta-wait for turnaround
* Tta-wait = Ttxclkesc * value
*/
ta_wait = DIV_ROUND_UP(gotp.taget, t_txclkesc);
ta_wait = DIV_ROUND_UP(cfg->ta_get, t_txclkesc);
for (i = 0; i < ARRAY_SIZE(timings); i++)
if (inno->pll.rate <= timings[i].rate)
@@ -479,6 +496,7 @@ static int inno_dsidphy_power_on(struct phy *phy)
struct inno_dsidphy *inno = phy_get_drvdata(phy);
clk_prepare_enable(inno->pclk_phy);
clk_prepare_enable(inno->ref_clk);
pm_runtime_get_sync(inno->dev);
/* Bandgap power on */
@@ -524,6 +542,7 @@ static int inno_dsidphy_power_off(struct phy *phy)
LVDS_PLL_POWER_OFF | LVDS_BANDGAP_POWER_DOWN);
pm_runtime_put(inno->dev);
clk_disable_unprepare(inno->ref_clk);
clk_disable_unprepare(inno->pclk_phy);
return 0;
@@ -546,168 +565,32 @@ static int inno_dsidphy_set_mode(struct phy *phy, enum phy_mode mode,
return 0;
}
static int inno_dsidphy_configure(struct phy *phy,
union phy_configure_opts *opts)
{
struct inno_dsidphy *inno = phy_get_drvdata(phy);
int ret;
if (inno->mode != PHY_MODE_MIPI_DPHY)
return -EINVAL;
ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy);
if (ret)
return ret;
memcpy(&inno->dphy_cfg, &opts->mipi_dphy, sizeof(inno->dphy_cfg));
return 0;
}
static const struct phy_ops inno_dsidphy_ops = {
.configure = inno_dsidphy_configure,
.set_mode = inno_dsidphy_set_mode,
.power_on = inno_dsidphy_power_on,
.power_off = inno_dsidphy_power_off,
.owner = THIS_MODULE,
};
static unsigned long inno_dsidphy_pll_round_rate(struct inno_dsidphy *inno,
unsigned long prate,
unsigned long rate,
u8 *prediv, u16 *fbdiv)
{
unsigned long best_freq = 0;
unsigned long fref, fout;
u8 min_prediv, max_prediv;
u8 _prediv, best_prediv = 1;
u16 _fbdiv, best_fbdiv = 1;
u32 min_delta = UINT_MAX;
/*
* The PLL output frequency can be calculated using a simple formula:
* PLL_Output_Frequency = (FREF / PREDIV * FBDIV) / 2
* PLL_Output_Frequency: it is equal to DDR-Clock-Frequency * 2
*/
fref = prate / 2;
if (rate > 1000000000UL)
fout = 1000000000UL;
else
fout = rate;
/* 5Mhz < Fref / prediv < 40MHz */
min_prediv = DIV_ROUND_UP(fref, 40000000);
max_prediv = fref / 5000000;
for (_prediv = min_prediv; _prediv <= max_prediv; _prediv++) {
u64 tmp;
u32 delta;
tmp = (u64)fout * _prediv;
do_div(tmp, fref);
_fbdiv = tmp;
/*
* The possible settings of feedback divider are
* 12, 13, 14, 16, ~ 511
*/
if (_fbdiv == 15)
continue;
if (_fbdiv < 12 || _fbdiv > 511)
continue;
tmp = (u64)_fbdiv * fref;
do_div(tmp, _prediv);
delta = abs(fout - tmp);
if (!delta) {
best_prediv = _prediv;
best_fbdiv = _fbdiv;
best_freq = tmp;
break;
} else if (delta < min_delta) {
best_prediv = _prediv;
best_fbdiv = _fbdiv;
best_freq = tmp;
min_delta = delta;
}
}
if (best_freq) {
*prediv = best_prediv;
*fbdiv = best_fbdiv;
}
return best_freq;
}
static long inno_dsidphy_pll_clk_round_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long *prate)
{
struct inno_dsidphy *inno = hw_to_inno(hw);
unsigned long fout;
u16 fbdiv = 1;
u8 prediv = 1;
fout = inno_dsidphy_pll_round_rate(inno, *prate, rate,
&prediv, &fbdiv);
return fout;
}
static int inno_dsidphy_pll_clk_set_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long parent_rate)
{
struct inno_dsidphy *inno = hw_to_inno(hw);
unsigned long fout;
u16 fbdiv = 1;
u8 prediv = 1;
fout = inno_dsidphy_pll_round_rate(inno, parent_rate, rate,
&prediv, &fbdiv);
dev_dbg(inno->dev, "fin=%lu, fout=%lu, prediv=%u, fbdiv=%u\n",
parent_rate, fout, prediv, fbdiv);
inno->pll.prediv = prediv;
inno->pll.fbdiv = fbdiv;
inno->pll.rate = fout;
return 0;
}
static unsigned long
inno_dsidphy_pll_clk_recalc_rate(struct clk_hw *hw, unsigned long prate)
{
struct inno_dsidphy *inno = hw_to_inno(hw);
/* PLL_Output_Frequency = (FREF / PREDIV * FBDIV) / 2 */
return (prate / inno->pll.prediv * inno->pll.fbdiv) / 2;
}
static const struct clk_ops inno_dsidphy_pll_clk_ops = {
.round_rate = inno_dsidphy_pll_clk_round_rate,
.set_rate = inno_dsidphy_pll_clk_set_rate,
.recalc_rate = inno_dsidphy_pll_clk_recalc_rate,
};
static int inno_dsidphy_pll_register(struct inno_dsidphy *inno)
{
struct device *dev = inno->dev;
struct clk *clk;
const char *parent_name;
struct clk_init_data init;
int ret;
parent_name = __clk_get_name(inno->ref_clk);
init.name = "mipi_dphy_pll";
ret = of_property_read_string(dev->of_node, "clock-output-names",
&init.name);
if (ret < 0)
dev_dbg(dev, "phy should set clock-output-names property\n");
init.ops = &inno_dsidphy_pll_clk_ops;
init.parent_names = &parent_name;
init.num_parents = 1;
init.flags = 0;
inno->pll.hw.init = &init;
clk = devm_clk_register(dev, &inno->pll.hw);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
dev_err(dev, "failed to register PLL: %d\n", ret);
return ret;
}
return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
&inno->pll.hw);
}
static int inno_dsidphy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -764,10 +647,6 @@ static int inno_dsidphy_probe(struct platform_device *pdev)
return ret;
}
ret = inno_dsidphy_pll_register(inno);
if (ret)
return ret;
pm_runtime_enable(dev);
return 0;

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,959 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Wrapper driver for SERDES used in J721E
*
* Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
* Author: Kishon Vijay Abraham I <kishon@ti.com>
*/
#include <dt-bindings/phy/phy.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/mux/consumer.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/reset-controller.h>
#define WIZ_SERDES_CTRL 0x404
#define WIZ_SERDES_TOP_CTRL 0x408
#define WIZ_SERDES_RST 0x40c
#define WIZ_SERDES_TYPEC 0x410
#define WIZ_LANECTL(n) (0x480 + (0x40 * (n)))
#define WIZ_MAX_LANES 4
#define WIZ_MUX_NUM_CLOCKS 3
#define WIZ_DIV_NUM_CLOCKS_16G 2
#define WIZ_DIV_NUM_CLOCKS_10G 1
#define WIZ_SERDES_TYPEC_LN10_SWAP BIT(30)
enum wiz_lane_standard_mode {
LANE_MODE_GEN1,
LANE_MODE_GEN2,
LANE_MODE_GEN3,
LANE_MODE_GEN4,
};
enum wiz_refclk_mux_sel {
PLL0_REFCLK,
PLL1_REFCLK,
REFCLK_DIG,
};
enum wiz_refclk_div_sel {
CMN_REFCLK_DIG_DIV,
CMN_REFCLK1_DIG_DIV,
};
static const struct reg_field por_en = REG_FIELD(WIZ_SERDES_CTRL, 31, 31);
static const struct reg_field phy_reset_n = REG_FIELD(WIZ_SERDES_RST, 31, 31);
static const struct reg_field pll1_refclk_mux_sel =
REG_FIELD(WIZ_SERDES_RST, 29, 29);
static const struct reg_field pll0_refclk_mux_sel =
REG_FIELD(WIZ_SERDES_RST, 28, 28);
static const struct reg_field refclk_dig_sel_16g =
REG_FIELD(WIZ_SERDES_RST, 24, 25);
static const struct reg_field refclk_dig_sel_10g =
REG_FIELD(WIZ_SERDES_RST, 24, 24);
static const struct reg_field pma_cmn_refclk_int_mode =
REG_FIELD(WIZ_SERDES_TOP_CTRL, 28, 29);
static const struct reg_field pma_cmn_refclk_mode =
REG_FIELD(WIZ_SERDES_TOP_CTRL, 30, 31);
static const struct reg_field pma_cmn_refclk_dig_div =
REG_FIELD(WIZ_SERDES_TOP_CTRL, 26, 27);
static const struct reg_field pma_cmn_refclk1_dig_div =
REG_FIELD(WIZ_SERDES_TOP_CTRL, 24, 25);
static const struct reg_field p_enable[WIZ_MAX_LANES] = {
REG_FIELD(WIZ_LANECTL(0), 30, 31),
REG_FIELD(WIZ_LANECTL(1), 30, 31),
REG_FIELD(WIZ_LANECTL(2), 30, 31),
REG_FIELD(WIZ_LANECTL(3), 30, 31),
};
static const struct reg_field p_align[WIZ_MAX_LANES] = {
REG_FIELD(WIZ_LANECTL(0), 29, 29),
REG_FIELD(WIZ_LANECTL(1), 29, 29),
REG_FIELD(WIZ_LANECTL(2), 29, 29),
REG_FIELD(WIZ_LANECTL(3), 29, 29),
};
static const struct reg_field p_raw_auto_start[WIZ_MAX_LANES] = {
REG_FIELD(WIZ_LANECTL(0), 28, 28),
REG_FIELD(WIZ_LANECTL(1), 28, 28),
REG_FIELD(WIZ_LANECTL(2), 28, 28),
REG_FIELD(WIZ_LANECTL(3), 28, 28),
};
static const struct reg_field p_standard_mode[WIZ_MAX_LANES] = {
REG_FIELD(WIZ_LANECTL(0), 24, 25),
REG_FIELD(WIZ_LANECTL(1), 24, 25),
REG_FIELD(WIZ_LANECTL(2), 24, 25),
REG_FIELD(WIZ_LANECTL(3), 24, 25),
};
static const struct reg_field typec_ln10_swap =
REG_FIELD(WIZ_SERDES_TYPEC, 30, 30);
struct wiz_clk_mux {
struct clk_hw hw;
struct regmap_field *field;
u32 *table;
struct clk_init_data clk_data;
};
#define to_wiz_clk_mux(_hw) container_of(_hw, struct wiz_clk_mux, hw)
struct wiz_clk_divider {
struct clk_hw hw;
struct regmap_field *field;
struct clk_div_table *table;
struct clk_init_data clk_data;
};
#define to_wiz_clk_div(_hw) container_of(_hw, struct wiz_clk_divider, hw)
struct wiz_clk_mux_sel {
struct regmap_field *field;
u32 table[4];
const char *node_name;
};
struct wiz_clk_div_sel {
struct regmap_field *field;
struct clk_div_table *table;
const char *node_name;
};
static struct wiz_clk_mux_sel clk_mux_sel_16g[] = {
{
/*
* Mux value to be configured for each of the input clocks
* in the order populated in device tree
*/
.table = { 1, 0 },
.node_name = "pll0-refclk",
},
{
.table = { 1, 0 },
.node_name = "pll1-refclk",
},
{
.table = { 1, 3, 0, 2 },
.node_name = "refclk-dig",
},
};
static struct wiz_clk_mux_sel clk_mux_sel_10g[] = {
{
/*
* Mux value to be configured for each of the input clocks
* in the order populated in device tree
*/
.table = { 1, 0 },
.node_name = "pll0-refclk",
},
{
.table = { 1, 0 },
.node_name = "pll1-refclk",
},
{
.table = { 1, 0 },
.node_name = "refclk-dig",
},
};
static struct clk_div_table clk_div_table[] = {
{ .val = 0, .div = 1, },
{ .val = 1, .div = 2, },
{ .val = 2, .div = 4, },
{ .val = 3, .div = 8, },
};
static struct wiz_clk_div_sel clk_div_sel[] = {
{
.table = clk_div_table,
.node_name = "cmn-refclk-dig-div",
},
{
.table = clk_div_table,
.node_name = "cmn-refclk1-dig-div",
},
};
enum wiz_type {
J721E_WIZ_16G,
J721E_WIZ_10G,
};
#define WIZ_TYPEC_DIR_DEBOUNCE_MIN 100 /* ms */
#define WIZ_TYPEC_DIR_DEBOUNCE_MAX 1000
struct wiz {
struct regmap *regmap;
enum wiz_type type;
struct wiz_clk_mux_sel *clk_mux_sel;
struct wiz_clk_div_sel *clk_div_sel;
unsigned int clk_div_sel_num;
struct regmap_field *por_en;
struct regmap_field *phy_reset_n;
struct regmap_field *p_enable[WIZ_MAX_LANES];
struct regmap_field *p_align[WIZ_MAX_LANES];
struct regmap_field *p_raw_auto_start[WIZ_MAX_LANES];
struct regmap_field *p_standard_mode[WIZ_MAX_LANES];
struct regmap_field *pma_cmn_refclk_int_mode;
struct regmap_field *pma_cmn_refclk_mode;
struct regmap_field *pma_cmn_refclk_dig_div;
struct regmap_field *pma_cmn_refclk1_dig_div;
struct regmap_field *typec_ln10_swap;
struct device *dev;
u32 num_lanes;
struct platform_device *serdes_pdev;
struct reset_controller_dev wiz_phy_reset_dev;
struct gpio_desc *gpio_typec_dir;
int typec_dir_delay;
};
static int wiz_reset(struct wiz *wiz)
{
int ret;
ret = regmap_field_write(wiz->por_en, 0x1);
if (ret)
return ret;
mdelay(1);
ret = regmap_field_write(wiz->por_en, 0x0);
if (ret)
return ret;
return 0;
}
static int wiz_mode_select(struct wiz *wiz)
{
u32 num_lanes = wiz->num_lanes;
int ret;
int i;
for (i = 0; i < num_lanes; i++) {
ret = regmap_field_write(wiz->p_standard_mode[i],
LANE_MODE_GEN4);
if (ret)
return ret;
}
return 0;
}
static int wiz_init_raw_interface(struct wiz *wiz, bool enable)
{
u32 num_lanes = wiz->num_lanes;
int i;
int ret;
for (i = 0; i < num_lanes; i++) {
ret = regmap_field_write(wiz->p_align[i], enable);
if (ret)
return ret;
ret = regmap_field_write(wiz->p_raw_auto_start[i], enable);
if (ret)
return ret;
}
return 0;
}
static int wiz_init(struct wiz *wiz)
{
struct device *dev = wiz->dev;
int ret;
ret = wiz_reset(wiz);
if (ret) {
dev_err(dev, "WIZ reset failed\n");
return ret;
}
ret = wiz_mode_select(wiz);
if (ret) {
dev_err(dev, "WIZ mode select failed\n");
return ret;
}
ret = wiz_init_raw_interface(wiz, true);
if (ret) {
dev_err(dev, "WIZ interface initialization failed\n");
return ret;
}
return 0;
}
static int wiz_regfield_init(struct wiz *wiz)
{
struct wiz_clk_mux_sel *clk_mux_sel;
struct wiz_clk_div_sel *clk_div_sel;
struct regmap *regmap = wiz->regmap;
int num_lanes = wiz->num_lanes;
struct device *dev = wiz->dev;
int i;
wiz->por_en = devm_regmap_field_alloc(dev, regmap, por_en);
if (IS_ERR(wiz->por_en)) {
dev_err(dev, "POR_EN reg field init failed\n");
return PTR_ERR(wiz->por_en);
}
wiz->phy_reset_n = devm_regmap_field_alloc(dev, regmap,
phy_reset_n);
if (IS_ERR(wiz->phy_reset_n)) {
dev_err(dev, "PHY_RESET_N reg field init failed\n");
return PTR_ERR(wiz->phy_reset_n);
}
wiz->pma_cmn_refclk_int_mode =
devm_regmap_field_alloc(dev, regmap, pma_cmn_refclk_int_mode);
if (IS_ERR(wiz->pma_cmn_refclk_int_mode)) {
dev_err(dev, "PMA_CMN_REFCLK_INT_MODE reg field init failed\n");
return PTR_ERR(wiz->pma_cmn_refclk_int_mode);
}
wiz->pma_cmn_refclk_mode =
devm_regmap_field_alloc(dev, regmap, pma_cmn_refclk_mode);
if (IS_ERR(wiz->pma_cmn_refclk_mode)) {
dev_err(dev, "PMA_CMN_REFCLK_MODE reg field init failed\n");
return PTR_ERR(wiz->pma_cmn_refclk_mode);
}
clk_div_sel = &wiz->clk_div_sel[CMN_REFCLK_DIG_DIV];
clk_div_sel->field = devm_regmap_field_alloc(dev, regmap,
pma_cmn_refclk_dig_div);
if (IS_ERR(clk_div_sel->field)) {
dev_err(dev, "PMA_CMN_REFCLK_DIG_DIV reg field init failed\n");
return PTR_ERR(clk_div_sel->field);
}
if (wiz->type == J721E_WIZ_16G) {
clk_div_sel = &wiz->clk_div_sel[CMN_REFCLK1_DIG_DIV];
clk_div_sel->field =
devm_regmap_field_alloc(dev, regmap,
pma_cmn_refclk1_dig_div);
if (IS_ERR(clk_div_sel->field)) {
dev_err(dev, "PMA_CMN_REFCLK1_DIG_DIV reg field init failed\n");
return PTR_ERR(clk_div_sel->field);
}
}
clk_mux_sel = &wiz->clk_mux_sel[PLL0_REFCLK];
clk_mux_sel->field = devm_regmap_field_alloc(dev, regmap,
pll0_refclk_mux_sel);
if (IS_ERR(clk_mux_sel->field)) {
dev_err(dev, "PLL0_REFCLK_SEL reg field init failed\n");
return PTR_ERR(clk_mux_sel->field);
}
clk_mux_sel = &wiz->clk_mux_sel[PLL1_REFCLK];
clk_mux_sel->field = devm_regmap_field_alloc(dev, regmap,
pll1_refclk_mux_sel);
if (IS_ERR(clk_mux_sel->field)) {
dev_err(dev, "PLL1_REFCLK_SEL reg field init failed\n");
return PTR_ERR(clk_mux_sel->field);
}
clk_mux_sel = &wiz->clk_mux_sel[REFCLK_DIG];
if (wiz->type == J721E_WIZ_10G)
clk_mux_sel->field =
devm_regmap_field_alloc(dev, regmap,
refclk_dig_sel_10g);
else
clk_mux_sel->field =
devm_regmap_field_alloc(dev, regmap,
refclk_dig_sel_16g);
if (IS_ERR(clk_mux_sel->field)) {
dev_err(dev, "REFCLK_DIG_SEL reg field init failed\n");
return PTR_ERR(clk_mux_sel->field);
}
for (i = 0; i < num_lanes; i++) {
wiz->p_enable[i] = devm_regmap_field_alloc(dev, regmap,
p_enable[i]);
if (IS_ERR(wiz->p_enable[i])) {
dev_err(dev, "P%d_ENABLE reg field init failed\n", i);
return PTR_ERR(wiz->p_enable[i]);
}
wiz->p_align[i] = devm_regmap_field_alloc(dev, regmap,
p_align[i]);
if (IS_ERR(wiz->p_align[i])) {
dev_err(dev, "P%d_ALIGN reg field init failed\n", i);
return PTR_ERR(wiz->p_align[i]);
}
wiz->p_raw_auto_start[i] =
devm_regmap_field_alloc(dev, regmap, p_raw_auto_start[i]);
if (IS_ERR(wiz->p_raw_auto_start[i])) {
dev_err(dev, "P%d_RAW_AUTO_START reg field init fail\n",
i);
return PTR_ERR(wiz->p_raw_auto_start[i]);
}
wiz->p_standard_mode[i] =
devm_regmap_field_alloc(dev, regmap, p_standard_mode[i]);
if (IS_ERR(wiz->p_standard_mode[i])) {
dev_err(dev, "P%d_STANDARD_MODE reg field init fail\n",
i);
return PTR_ERR(wiz->p_standard_mode[i]);
}
}
wiz->typec_ln10_swap = devm_regmap_field_alloc(dev, regmap,
typec_ln10_swap);
if (IS_ERR(wiz->typec_ln10_swap)) {
dev_err(dev, "LN10_SWAP reg field init failed\n");
return PTR_ERR(wiz->typec_ln10_swap);
}
return 0;
}
static u8 wiz_clk_mux_get_parent(struct clk_hw *hw)
{
struct wiz_clk_mux *mux = to_wiz_clk_mux(hw);
struct regmap_field *field = mux->field;
unsigned int val;
regmap_field_read(field, &val);
return clk_mux_val_to_index(hw, mux->table, 0, val);
}
static int wiz_clk_mux_set_parent(struct clk_hw *hw, u8 index)
{
struct wiz_clk_mux *mux = to_wiz_clk_mux(hw);
struct regmap_field *field = mux->field;
int val;
val = mux->table[index];
return regmap_field_write(field, val);
}
static const struct clk_ops wiz_clk_mux_ops = {
.set_parent = wiz_clk_mux_set_parent,
.get_parent = wiz_clk_mux_get_parent,
};
static int wiz_mux_clk_register(struct wiz *wiz, struct device_node *node,
struct regmap_field *field, u32 *table)
{
struct device *dev = wiz->dev;
struct clk_init_data *init;
const char **parent_names;
unsigned int num_parents;
struct wiz_clk_mux *mux;
char clk_name[100];
struct clk *clk;
int ret;
mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
if (!mux)
return -ENOMEM;
num_parents = of_clk_get_parent_count(node);
if (num_parents < 2) {
dev_err(dev, "SERDES clock must have parents\n");
return -EINVAL;
}
parent_names = devm_kzalloc(dev, (sizeof(char *) * num_parents),
GFP_KERNEL);
if (!parent_names)
return -ENOMEM;
of_clk_parent_fill(node, parent_names, num_parents);
snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev),
node->name);
init = &mux->clk_data;
init->ops = &wiz_clk_mux_ops;
init->flags = CLK_SET_RATE_NO_REPARENT;
init->parent_names = parent_names;
init->num_parents = num_parents;
init->name = clk_name;
mux->field = field;
mux->table = table;
mux->hw.init = init;
clk = devm_clk_register(dev, &mux->hw);
if (IS_ERR(clk))
return PTR_ERR(clk);
ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
if (ret)
dev_err(dev, "Failed to add clock provider: %s\n", clk_name);
return ret;
}
static unsigned long wiz_clk_div_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct wiz_clk_divider *div = to_wiz_clk_div(hw);
struct regmap_field *field = div->field;
int val;
regmap_field_read(field, &val);
return divider_recalc_rate(hw, parent_rate, val, div->table, 0x0, 2);
}
static long wiz_clk_div_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
struct wiz_clk_divider *div = to_wiz_clk_div(hw);
return divider_round_rate(hw, rate, prate, div->table, 2, 0x0);
}
static int wiz_clk_div_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct wiz_clk_divider *div = to_wiz_clk_div(hw);
struct regmap_field *field = div->field;
int val;
val = divider_get_val(rate, parent_rate, div->table, 2, 0x0);
if (val < 0)
return val;
return regmap_field_write(field, val);
}
static const struct clk_ops wiz_clk_div_ops = {
.recalc_rate = wiz_clk_div_recalc_rate,
.round_rate = wiz_clk_div_round_rate,
.set_rate = wiz_clk_div_set_rate,
};
static int wiz_div_clk_register(struct wiz *wiz, struct device_node *node,
struct regmap_field *field,
struct clk_div_table *table)
{
struct device *dev = wiz->dev;
struct wiz_clk_divider *div;
struct clk_init_data *init;
const char **parent_names;
char clk_name[100];
struct clk *clk;
int ret;
div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
if (!div)
return -ENOMEM;
snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev),
node->name);
parent_names = devm_kzalloc(dev, sizeof(char *), GFP_KERNEL);
if (!parent_names)
return -ENOMEM;
of_clk_parent_fill(node, parent_names, 1);
init = &div->clk_data;
init->ops = &wiz_clk_div_ops;
init->flags = 0;
init->parent_names = parent_names;
init->num_parents = 1;
init->name = clk_name;
div->field = field;
div->table = table;
div->hw.init = init;
clk = devm_clk_register(dev, &div->hw);
if (IS_ERR(clk))
return PTR_ERR(clk);
ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
if (ret)
dev_err(dev, "Failed to add clock provider: %s\n", clk_name);
return ret;
}
static void wiz_clock_cleanup(struct wiz *wiz, struct device_node *node)
{
struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel;
struct device_node *clk_node;
int i;
for (i = 0; i < WIZ_MUX_NUM_CLOCKS; i++) {
clk_node = of_get_child_by_name(node, clk_mux_sel[i].node_name);
of_clk_del_provider(clk_node);
of_node_put(clk_node);
}
}
static int wiz_clock_init(struct wiz *wiz, struct device_node *node)
{
struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel;
struct device *dev = wiz->dev;
struct device_node *clk_node;
const char *node_name;
unsigned long rate;
struct clk *clk;
int ret;
int i;
clk = devm_clk_get(dev, "core_ref_clk");
if (IS_ERR(clk)) {
dev_err(dev, "core_ref_clk clock not found\n");
ret = PTR_ERR(clk);
return ret;
}
rate = clk_get_rate(clk);
if (rate >= 100000000)
regmap_field_write(wiz->pma_cmn_refclk_int_mode, 0x1);
else
regmap_field_write(wiz->pma_cmn_refclk_int_mode, 0x3);
clk = devm_clk_get(dev, "ext_ref_clk");
if (IS_ERR(clk)) {
dev_err(dev, "ext_ref_clk clock not found\n");
ret = PTR_ERR(clk);
return ret;
}
rate = clk_get_rate(clk);
if (rate >= 100000000)
regmap_field_write(wiz->pma_cmn_refclk_mode, 0x0);
else
regmap_field_write(wiz->pma_cmn_refclk_mode, 0x2);
for (i = 0; i < WIZ_MUX_NUM_CLOCKS; i++) {
node_name = clk_mux_sel[i].node_name;
clk_node = of_get_child_by_name(node, node_name);
if (!clk_node) {
dev_err(dev, "Unable to get %s node\n", node_name);
ret = -EINVAL;
goto err;
}
ret = wiz_mux_clk_register(wiz, clk_node, clk_mux_sel[i].field,
clk_mux_sel[i].table);
if (ret) {
dev_err(dev, "Failed to register %s clock\n",
node_name);
of_node_put(clk_node);
goto err;
}
of_node_put(clk_node);
}
for (i = 0; i < wiz->clk_div_sel_num; i++) {
node_name = clk_div_sel[i].node_name;
clk_node = of_get_child_by_name(node, node_name);
if (!clk_node) {
dev_err(dev, "Unable to get %s node\n", node_name);
ret = -EINVAL;
goto err;
}
ret = wiz_div_clk_register(wiz, clk_node, clk_div_sel[i].field,
clk_div_sel[i].table);
if (ret) {
dev_err(dev, "Failed to register %s clock\n",
node_name);
of_node_put(clk_node);
goto err;
}
of_node_put(clk_node);
}
return 0;
err:
wiz_clock_cleanup(wiz, node);
return ret;
}
static int wiz_phy_reset_assert(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct device *dev = rcdev->dev;
struct wiz *wiz = dev_get_drvdata(dev);
int ret = 0;
if (id == 0) {
ret = regmap_field_write(wiz->phy_reset_n, false);
return ret;
}
ret = regmap_field_write(wiz->p_enable[id - 1], false);
return ret;
}
static int wiz_phy_reset_deassert(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct device *dev = rcdev->dev;
struct wiz *wiz = dev_get_drvdata(dev);
int ret;
/* if typec-dir gpio was specified, set LN10 SWAP bit based on that */
if (id == 0 && wiz->gpio_typec_dir) {
if (wiz->typec_dir_delay)
msleep_interruptible(wiz->typec_dir_delay);
if (gpiod_get_value_cansleep(wiz->gpio_typec_dir))
regmap_field_write(wiz->typec_ln10_swap, 1);
else
regmap_field_write(wiz->typec_ln10_swap, 0);
}
if (id == 0) {
ret = regmap_field_write(wiz->phy_reset_n, true);
return ret;
}
ret = regmap_field_write(wiz->p_enable[id - 1], true);
return ret;
}
static const struct reset_control_ops wiz_phy_reset_ops = {
.assert = wiz_phy_reset_assert,
.deassert = wiz_phy_reset_deassert,
};
static struct regmap_config wiz_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.fast_io = true,
};
static const struct of_device_id wiz_id_table[] = {
{
.compatible = "ti,j721e-wiz-16g", .data = (void *)J721E_WIZ_16G
},
{
.compatible = "ti,j721e-wiz-10g", .data = (void *)J721E_WIZ_10G
},
{}
};
MODULE_DEVICE_TABLE(of, wiz_id_table);
static int wiz_probe(struct platform_device *pdev)
{
struct reset_controller_dev *phy_reset_dev;
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
struct platform_device *serdes_pdev;
struct device_node *child_node;
struct regmap *regmap;
struct resource res;
void __iomem *base;
struct wiz *wiz;
u32 num_lanes;
int ret;
wiz = devm_kzalloc(dev, sizeof(*wiz), GFP_KERNEL);
if (!wiz)
return -ENOMEM;
wiz->type = (enum wiz_type)of_device_get_match_data(dev);
child_node = of_get_child_by_name(node, "serdes");
if (!child_node) {
dev_err(dev, "Failed to get SERDES child DT node\n");
return -ENODEV;
}
ret = of_address_to_resource(child_node, 0, &res);
if (ret) {
dev_err(dev, "Failed to get memory resource\n");
goto err_addr_to_resource;
}
base = devm_ioremap(dev, res.start, resource_size(&res));
if (!base)
goto err_addr_to_resource;
regmap = devm_regmap_init_mmio(dev, base, &wiz_regmap_config);
if (IS_ERR(regmap)) {
dev_err(dev, "Failed to initialize regmap\n");
ret = PTR_ERR(regmap);
goto err_addr_to_resource;
}
ret = of_property_read_u32(node, "num-lanes", &num_lanes);
if (ret) {
dev_err(dev, "Failed to read num-lanes property\n");
goto err_addr_to_resource;
}
if (num_lanes > WIZ_MAX_LANES) {
dev_err(dev, "Cannot support %d lanes\n", num_lanes);
goto err_addr_to_resource;
}
wiz->gpio_typec_dir = devm_gpiod_get_optional(dev, "typec-dir",
GPIOD_IN);
if (IS_ERR(wiz->gpio_typec_dir)) {
ret = PTR_ERR(wiz->gpio_typec_dir);
if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to request typec-dir gpio: %d\n",
ret);
goto err_addr_to_resource;
}
if (wiz->gpio_typec_dir) {
ret = of_property_read_u32(node, "typec-dir-debounce-ms",
&wiz->typec_dir_delay);
if (ret && ret != -EINVAL) {
dev_err(dev, "Invalid typec-dir-debounce property\n");
goto err_addr_to_resource;
}
/* use min. debounce from Type-C spec if not provided in DT */
if (ret == -EINVAL)
wiz->typec_dir_delay = WIZ_TYPEC_DIR_DEBOUNCE_MIN;
if (wiz->typec_dir_delay < WIZ_TYPEC_DIR_DEBOUNCE_MIN ||
wiz->typec_dir_delay > WIZ_TYPEC_DIR_DEBOUNCE_MAX) {
dev_err(dev, "Invalid typec-dir-debounce property\n");
goto err_addr_to_resource;
}
}
wiz->dev = dev;
wiz->regmap = regmap;
wiz->num_lanes = num_lanes;
if (wiz->type == J721E_WIZ_10G)
wiz->clk_mux_sel = clk_mux_sel_10g;
else
wiz->clk_mux_sel = clk_mux_sel_16g;
wiz->clk_div_sel = clk_div_sel;
if (wiz->type == J721E_WIZ_10G)
wiz->clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_10G;
else
wiz->clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_16G;
platform_set_drvdata(pdev, wiz);
ret = wiz_regfield_init(wiz);
if (ret) {
dev_err(dev, "Failed to initialize regfields\n");
goto err_addr_to_resource;
}
phy_reset_dev = &wiz->wiz_phy_reset_dev;
phy_reset_dev->dev = dev;
phy_reset_dev->ops = &wiz_phy_reset_ops,
phy_reset_dev->owner = THIS_MODULE,
phy_reset_dev->of_node = node;
/* Reset for each of the lane and one for the entire SERDES */
phy_reset_dev->nr_resets = num_lanes + 1;
ret = devm_reset_controller_register(dev, phy_reset_dev);
if (ret < 0) {
dev_warn(dev, "Failed to register reset controller\n");
goto err_addr_to_resource;
}
pm_runtime_enable(dev);
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
dev_err(dev, "pm_runtime_get_sync failed\n");
goto err_get_sync;
}
ret = wiz_clock_init(wiz, node);
if (ret < 0) {
dev_warn(dev, "Failed to initialize clocks\n");
goto err_get_sync;
}
serdes_pdev = of_platform_device_create(child_node, NULL, dev);
if (!serdes_pdev) {
dev_WARN(dev, "Unable to create SERDES platform device\n");
goto err_pdev_create;
}
wiz->serdes_pdev = serdes_pdev;
ret = wiz_init(wiz);
if (ret) {
dev_err(dev, "WIZ initialization failed\n");
goto err_wiz_init;
}
of_node_put(child_node);
return 0;
err_wiz_init:
of_platform_device_destroy(&serdes_pdev->dev, NULL);
err_pdev_create:
wiz_clock_cleanup(wiz, node);
err_get_sync:
pm_runtime_put(dev);
pm_runtime_disable(dev);
err_addr_to_resource:
of_node_put(child_node);
return ret;
}
static int wiz_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
struct platform_device *serdes_pdev;
struct wiz *wiz;
wiz = dev_get_drvdata(dev);
serdes_pdev = wiz->serdes_pdev;
of_platform_device_destroy(&serdes_pdev->dev, NULL);
wiz_clock_cleanup(wiz, node);
pm_runtime_put(dev);
pm_runtime_disable(dev);
return 0;
}
static struct platform_driver wiz_driver = {
.probe = wiz_probe,
.remove = wiz_remove,
.driver = {
.name = "wiz",
.of_match_table = wiz_id_table,
},
};
module_platform_driver(wiz_driver);
MODULE_AUTHOR("Texas Instruments Inc.");
MODULE_DESCRIPTION("TI J721E WIZ driver");
MODULE_LICENSE("GPL v2");

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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 */

View File

@@ -0,0 +1,95 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019 Cadence Design Systems Inc.
*/
#ifndef __PHY_DP_H_
#define __PHY_DP_H_
#include <linux/types.h>
/**
* struct phy_configure_opts_dp - DisplayPort PHY configuration set
*
* This structure is used to represent the configuration state of a
* DisplayPort phy.
*/
struct phy_configure_opts_dp {
/**
* @link_rate:
*
* Link Rate, in Mb/s, of the main link.
*
* Allowed values: 1620, 2160, 2430, 2700, 3240, 4320, 5400, 8100 Mb/s
*/
unsigned int link_rate;
/**
* @lanes:
*
* Number of active, consecutive, data lanes, starting from
* lane 0, used for the transmissions on main link.
*
* Allowed values: 1, 2, 4
*/
unsigned int lanes;
/**
* @voltage:
*
* Voltage swing levels, as specified by DisplayPort specification,
* to be used by particular lanes. One value per lane.
* voltage[0] is for lane 0, voltage[1] is for lane 1, etc.
*
* Maximum value: 3
*/
unsigned int voltage[4];
/**
* @pre:
*
* Pre-emphasis levels, as specified by DisplayPort specification, to be
* used by particular lanes. One value per lane.
*
* Maximum value: 3
*/
unsigned int pre[4];
/**
* @ssc:
*
* Flag indicating, whether or not to enable spread-spectrum clocking.
*
*/
u8 ssc : 1;
/**
* @set_rate:
*
* Flag indicating, whether or not reconfigure link rate and SSC to
* requested values.
*
*/
u8 set_rate : 1;
/**
* @set_lanes:
*
* Flag indicating, whether or not reconfigure lane count to
* requested value.
*
*/
u8 set_lanes : 1;
/**
* @set_voltages:
*
* Flag indicating, whether or not reconfigure voltage swing
* and pre-emphasis to requested values. Only lanes specified
* by "lanes" parameter will be affected.
*
*/
u8 set_voltages : 1;
};
#endif /* __PHY_DP_H_ */

View File

@@ -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)
{
}