Commit 9f76e198 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge tag 'phy-for-5.10' of...

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

Vinod writes:

phy for 5.9

 - Core:
   - New PHY attribute for max_link_rate

 - New phy drivers:
   - Rockchip dphy driver moved from staging
   - Socionext UniPhier AHCI PHY driver
   - Intel LGM SoC USB phy
   - Intel Keem Bay eMMC PHY driver

 - Updates:
   - Support for imx8mp usb phy
   - Support for DP Phy and USB3+DP combo phy in QMP driver
   - Support for Qualcomm sc7180 DP phy
   - Support for cadence torrent PCIe and USB single linke and multilink
     configurations along with USB, SGMII/QSGMII configurations

* tag 'phy-for-5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux-phy: (72 commits)
  phy: qcom-qmp: initialize the pointer to NULL
  phy: qcom-qmp: Add support for sc7180 DP phy
  phy: qcom-qmp: Add support for DP in USB3+DP combo phy
  phy: qcom-qmp: Use devm_platform_ioremap_resource() to simplify
  phy: qcom-qmp: Get dp_com I/O resource by index
  phy: qcom-qmp: Move 'serdes' and 'cfg' into 'struct qcom_phy'
  phy: qcom-qmp: Remove 'initialized' in favor of 'init_count'
  phy: qcom-qmp: Move phy mode into struct qmp_phy
  dt-bindings: phy: qcom,qmp-usb3-dp: Add DP phy information
  dt-bindings: phy: ti,phy-j721e-wiz: fix bindings for torrent phy
  dt-bindings: phy: cdns,torrent-phy: add reset-names
  phy: rockchip-dphy-rx0: Include linux/delay.h
  phy: fix USB_LGM_PHY warning & build errors
  phy: cadence-torrent: Add USB + SGMII/QSGMII multilink configuration
  phy: cadence-torrent: Add PCIe + USB multilink configuration
  phy: cadence-torrent: Add single link USB register sequences
  phy: cadence-torrent: Add single link SGMII/QSGMII register sequences
  phy: cadence-torrent: Configure PHY_PLL_CFG as part of link_cmn_vals
  phy: cadence-torrent: Add PHY link configuration sequences for single link
  phy: cadence-torrent: Add clk changes for multilink configuration
  ...
parents 37d2a363 60f5a24c
* Freescale i.MX8MQ USB3 PHY binding
Required properties:
- compatible: Should be "fsl,imx8mq-usb-phy"
- compatible: Should be "fsl,imx8mq-usb-phy" or "fsl,imx8mp-usb-phy"
- #phys-cells: must be 0 (see phy-bindings.txt in this directory)
- reg: The base address and length of the registers
- clocks: phandles to the clocks for each clock listed in clock-names
......
......@@ -23,7 +23,9 @@ description: |+
properties:
compatible:
const: intel,lgm-emmc-phy
oneOf:
- const: intel,lgm-emmc-phy
- const: intel,keembay-emmc-phy
"#phy-cells":
const: 0
......@@ -34,6 +36,10 @@ properties:
clocks:
maxItems: 1
clock-names:
items:
- const: emmcclk
required:
- "#phy-cells"
- compatible
......@@ -57,4 +63,13 @@ examples:
#phy-cells = <0>;
};
};
- |
phy@20290000 {
compatible = "intel,keembay-emmc-phy";
reg = <0x20290000 0x54>;
clocks = <&emmc>;
clock-names = "emmcclk";
#phy-cells = <0>;
};
...
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/phy/intel,lgm-usb-phy.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Intel LGM USB PHY Device Tree Bindings
maintainers:
- Vadivel Murugan Ramuthevar <vadivel.muruganx.ramuthevar@linux.intel.com>
properties:
compatible:
const: intel,lgm-usb-phy
reg:
maxItems: 1
clocks:
maxItems: 1
resets:
items:
- description: USB PHY and Host controller reset
- description: APB BUS reset
- description: General Hardware reset
reset-names:
items:
- const: phy
- const: apb
- const: phy31
"#phy-cells":
const: 0
required:
- compatible
- clocks
- reg
- resets
- reset-names
- "#phy-cells"
additionalProperties: false
examples:
- |
usb-phy@e7e00000 {
compatible = "intel,lgm-usb-phy";
reg = <0xe7e00000 0x10000>;
clocks = <&cgu0 153>;
resets = <&rcu 0x70 0x24>,
<&rcu 0x70 0x26>,
<&rcu 0x70 0x28>;
reset-names = "phy", "apb", "phy31";
#phy-cells = <0>;
};
......@@ -4,11 +4,13 @@
$id: "http://devicetree.org/schemas/phy/phy-cadence-torrent.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: Cadence Torrent SD0801 PHY binding for DisplayPort
title: Cadence Torrent SD0801 PHY binding
description:
This binding describes the Cadence SD0801 PHY (also known as Torrent PHY)
hardware included with the Cadence MHDP DisplayPort controller.
hardware included with the Cadence MHDP DisplayPort controller. Torrent
PHY also supports multilink multiprotocol combinations including protocols
such as PCIe, USB, SGMII, QSGMII etc.
maintainers:
- Swapnil Jakhade <sjakhade@cadence.com>
......@@ -49,13 +51,21 @@ properties:
- const: dptx_phy
resets:
maxItems: 1
description:
Torrent PHY reset.
See Documentation/devicetree/bindings/reset/reset.txt
minItems: 1
maxItems: 2
items:
- description: Torrent PHY reset.
- description: Torrent APB reset. This is optional.
reset-names:
minItems: 1
maxItems: 2
items:
- const: torrent_reset
- const: torrent_apb
patternProperties:
'^phy@[0-7]+$':
'^phy@[0-3]$':
type: object
description:
Each group of PHY lanes with a single master lane should be represented as a sub-node.
......@@ -63,6 +73,8 @@ patternProperties:
reg:
description:
The master lane number. This is the lowest numbered lane in the lane group.
minimum: 0
maximum: 3
resets:
minItems: 1
......@@ -78,15 +90,25 @@ patternProperties:
Specifies the type of PHY for which the group of PHY lanes is used.
Refer include/dt-bindings/phy/phy.h. Constants from the header should be used.
$ref: /schemas/types.yaml#/definitions/uint32
enum: [1, 2, 3, 4, 5, 6]
minimum: 1
maximum: 9
cdns,num-lanes:
description:
Number of DisplayPort lanes.
Number of lanes.
$ref: /schemas/types.yaml#/definitions/uint32
enum: [1, 2, 4]
enum: [1, 2, 3, 4]
default: 4
cdns,ssc-mode:
description:
Specifies the Spread Spectrum Clocking mode used. It can be NO_SSC,
EXTERNAL_SSC or INTERNAL_SSC.
Refer include/dt-bindings/phy/phy-cadence-torrent.h for the constants to be used.
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2]
default: 0
cdns,max-bit-rate:
description:
Maximum DisplayPort link bit rate to use, in Mbps
......@@ -99,6 +121,7 @@ patternProperties:
- resets
- "#phy-cells"
- cdns,phy-type
- cdns,num-lanes
additionalProperties: false
......@@ -111,6 +134,7 @@ required:
- reg
- reg-names
- resets
- reset-names
additionalProperties: false
......@@ -128,18 +152,56 @@ examples:
<0xf0 0xfb030a00 0x0 0x00000040>;
reg-names = "torrent_phy", "dptx_phy";
resets = <&phyrst 0>;
reset-names = "torrent_reset";
clocks = <&ref_clk>;
clock-names = "refclk";
#address-cells = <1>;
#size-cells = <0>;
phy@0 {
reg = <0>;
resets = <&phyrst 1>, <&phyrst 2>,
<&phyrst 3>, <&phyrst 4>;
#phy-cells = <0>;
cdns,phy-type = <PHY_TYPE_DP>;
cdns,num-lanes = <4>;
cdns,max-bit-rate = <8100>;
reg = <0>;
resets = <&phyrst 1>, <&phyrst 2>,
<&phyrst 3>, <&phyrst 4>;
#phy-cells = <0>;
cdns,phy-type = <PHY_TYPE_DP>;
cdns,num-lanes = <4>;
cdns,max-bit-rate = <8100>;
};
};
};
- |
#include <dt-bindings/phy/phy.h>
#include <dt-bindings/phy/phy-cadence-torrent.h>
bus {
#address-cells = <2>;
#size-cells = <2>;
torrent-phy@f0fb500000 {
compatible = "cdns,torrent-phy";
reg = <0xf0 0xfb500000 0x0 0x00100000>;
reg-names = "torrent_phy";
resets = <&phyrst 0>, <&phyrst 1>;
reset-names = "torrent_reset", "torrent_apb";
clocks = <&ref_clk>;
clock-names = "refclk";
#address-cells = <1>;
#size-cells = <0>;
phy@0 {
reg = <0>;
resets = <&phyrst 2>, <&phyrst 3>;
#phy-cells = <0>;
cdns,phy-type = <PHY_TYPE_PCIE>;
cdns,num-lanes = <2>;
cdns,ssc-mode = <TORRENT_SERDES_NO_SSC>;
};
phy@2 {
reg = <2>;
resets = <&phyrst 4>;
#phy-cells = <0>;
cdns,phy-type = <PHY_TYPE_SGMII>;
cdns,num-lanes = <1>;
cdns,ssc-mode = <TORRENT_SERDES_NO_SSC>;
};
};
};
......
......@@ -13,17 +13,21 @@ maintainers:
properties:
compatible:
enum:
- qcom,sc7180-qmp-usb3-dp-phy
- qcom,sc7180-qmp-usb3-phy
- qcom,sdm845-qmp-usb3-dp-phy
- qcom,sdm845-qmp-usb3-phy
reg:
items:
- description: Address and length of PHY's common serdes block.
- description: Address and length of PHY's USB serdes block.
- description: Address and length of the DP_COM control block.
- description: Address and length of PHY's DP serdes block.
reg-names:
items:
- const: reg-base
- const: usb
- const: dp_com
- const: dp
"#clock-cells":
enum: [ 1, 2 ]
......@@ -74,16 +78,74 @@ properties:
#Required nodes:
patternProperties:
"^phy@[0-9a-f]+$":
"^usb3-phy@[0-9a-f]+$":
type: object
description:
Each device node of QMP phy is required to have as many child nodes as
the number of lanes the PHY has.
The USB3 PHY.
properties:
reg:
items:
- description: Address and length of TX.
- description: Address and length of RX.
- description: Address and length of PCS.
- description: Address and length of TX2.
- description: Address and length of RX2.
- description: Address and length of pcs_misc.
clocks:
items:
- description: pipe clock
clock-names:
items:
- const: pipe0
clock-output-names:
items:
- const: usb3_phy_pipe_clk_src
'#clock-cells':
const: 0
'#phy-cells':
const: 0
required:
- reg
- clocks
- clock-names
- '#clock-cells'
- '#phy-cells'
"^dp-phy@[0-9a-f]+$":
type: object
description:
The DP PHY.
properties:
reg:
items:
- description: Address and length of TX.
- description: Address and length of RX.
- description: Address and length of PCS.
- description: Address and length of TX2.
- description: Address and length of RX2.
'#clock-cells':
const: 1
'#phy-cells':
const: 0
required:
- reg
- '#clock-cells'
- '#phy-cells'
required:
- compatible
- reg
- reg-names
- "#clock-cells"
- "#address-cells"
- "#size-cells"
......@@ -101,14 +163,15 @@ examples:
- |
#include <dt-bindings/clock/qcom,gcc-sdm845.h>
usb_1_qmpphy: phy-wrapper@88e9000 {
compatible = "qcom,sdm845-qmp-usb3-phy";
compatible = "qcom,sdm845-qmp-usb3-dp-phy";
reg = <0x088e9000 0x18c>,
<0x088e8000 0x10>;
reg-names = "reg-base", "dp_com";
<0x088e8000 0x10>,
<0x088ea000 0x40>;
reg-names = "usb", "dp_com", "dp";
#clock-cells = <1>;
#address-cells = <1>;
#size-cells = <1>;
ranges = <0x0 0x088e9000 0x1000>;
ranges = <0x0 0x088e9000 0x2000>;
clocks = <&gcc GCC_USB3_PRIM_PHY_AUX_CLK>,
<&gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
......@@ -123,7 +186,7 @@ examples:
vdda-phy-supply = <&vdda_usb2_ss_1p2>;
vdda-pll-supply = <&vdda_usb2_ss_core>;
phy@200 {
usb3-phy@200 {
reg = <0x200 0x128>,
<0x400 0x200>,
<0xc00 0x218>,
......@@ -136,4 +199,14 @@ examples:
clock-names = "pipe0";
clock-output-names = "usb3_phy_pipe_clk_src";
};
dp-phy@88ea200 {
reg = <0xa200 0x200>,
<0xa400 0x200>,
<0xaa00 0x200>,
<0xa600 0x200>,
<0xa800 0x200>;
#clock-cells = <1>;
#phy-cells = <0>;
};
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/phy/socionext,uniphier-ahci-phy.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Socionext UniPhier AHCI PHY
description: |
This describes the deivcetree bindings for PHY interfaces built into
AHCI controller implemented on Socionext UniPhier SoCs.
maintainers:
- Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
properties:
compatible:
enum:
- socionext,uniphier-pxs2-ahci-phy
- socionext,uniphier-pxs3-ahci-phy
reg:
description: PHY register region (offset and length)
"#phy-cells":
const: 0
clocks:
maxItems: 2
clock-names:
oneOf:
- items: # for PXs2
- const: link
- items: # for others
- const: link
- const: phy
resets:
maxItems: 2
reset-names:
items:
- const: link
- const: phy
required:
- compatible
- reg
- "#phy-cells"
- clocks
- clock-names
- resets
- reset-names
additionalProperties: false
examples:
- |
ahci-glue@65700000 {
compatible = "socionext,uniphier-pxs3-ahci-glue",
"simple-mfd";
#address-cells = <1>;
#size-cells = <1>;
ranges = <0 0x65700000 0x100>;
ahci_phy: phy@10 {
compatible = "socionext,uniphier-pxs3-ahci-phy";
reg = <0x10 0x10>;
#phy-cells = <0>;
clock-names = "link", "phy";
clocks = <&sys_clk 28>, <&sys_clk 30>;
reset-names = "link", "phy";
resets = <&sys_rst 28>, <&sys_rst 30>;
};
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/phy/ti,omap-usb2.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: OMAP USB2 PHY
maintainers:
- Kishon Vijay Abraham I <kishon@ti.com>
- Roger Quadros <rogerq@ti.com>
properties:
compatible:
oneOf:
- items:
- enum:
- ti,dra7x-usb2
- ti,dra7x-usb2-phy2
- ti,am654-usb2
- enum:
- ti,omap-usb2
- items:
- const: ti,am437x-usb2
- items:
- const: ti,omap-usb2
reg:
maxItems: 1
"#phy-cells":
const: 0
clocks:
minItems: 1
items:
- description: wakeup clock
- description: reference clock
clock-names:
minItems: 1
items:
- const: wkupclk
- const: refclk
syscon-phy-power:
$ref: /schemas/types.yaml#definitions/phandle-array
description:
phandle/offset pair. Phandle to the system control module and
register offset to power on/off the PHY.
ctrl-module:
$ref: /schemas/types.yaml#definitions/phandle
description:
(deprecated) phandle of the control module used by PHY driver
to power on the PHY. Use syscon-phy-power instead.
required:
- compatible
- reg
- "#phy-cells"
- clocks
- clock-names
examples:
- |
usb0_phy: phy@4100000 {
compatible = "ti,am654-usb2", "ti,omap-usb2";
reg = <0x4100000 0x54>;
syscon-phy-power = <&scm_conf 0x4000>;
clocks = <&k3_clks 151 0>, <&k3_clks 151 1>;
clock-names = "wkupclk", "refclk";
#phy-cells = <0>;
};
......@@ -45,9 +45,15 @@ properties:
ranges: true
assigned-clocks:
minItems: 1
maxItems: 2
assigned-clock-parents:
minItems: 1
maxItems: 2
assigned-clock-rates:
minItems: 1
maxItems: 2
typec-dir-gpios:
......@@ -119,9 +125,10 @@ patternProperties:
logic.
properties:
clocks:
minItems: 2
maxItems: 4
description: Phandle to four clock nodes representing the inputs to
refclk_dig
description: Phandle to two (Torrent) or four (Sierra) clock nodes representing
the inputs to refclk_dig
"#clock-cells":
const: 0
......@@ -203,7 +210,7 @@ examples:
};
refclk-dig {
clocks = <&k3_clks 292 11>, <&k3_clks 292 0>,
clocks = <&k3_clks 292 11>, <&k3_clks 292 0>,
<&dummy_cmn_refclk>, <&dummy_cmn_refclk1>;
#clock-cells = <0>;
assigned-clocks = <&wiz0_refclk_dig>;
......
......@@ -27,43 +27,6 @@ omap_control_usb: omap-control-usb@4a002300 {
reg-names = "otghs_control";
};
OMAP USB2 PHY
Required properties:
- compatible: Should be "ti,omap-usb2"
Should be "ti,dra7x-usb2" for the 1st instance of USB2 PHY on
DRA7x
Should be "ti,dra7x-usb2-phy2" for the 2nd instance of USB2 PHY
in DRA7x
Should be "ti,am654-usb2" for the USB2 PHYs on AM654.
- reg : Address and length of the register set for the device.
- #phy-cells: determine the number of cells that should be given in the
phandle while referencing this phy.
- clocks: a list of phandles and clock-specifier pairs, one for each entry in
clock-names.
- clock-names: should include:
* "wkupclk" - wakeup clock.
* "refclk" - reference clock (optional).
Deprecated properties:
- ctrl-module : phandle of the control module used by PHY driver to power on
the PHY.
Recommended properies:
- syscon-phy-power : phandle/offset pair. Phandle to the system control
module and the register offset to power on/off the PHY.
This is usually a subnode of ocp2scp to which it is connected.
usb2phy@4a0ad080 {
compatible = "ti,omap-usb2";
reg = <0x4a0ad080 0x58>;
ctrl-module = <&omap_control_usb>;
#phy-cells = <0>;
clocks = <&usb_phy_cm_clk32k>, <&usb_otg_ss_refclk960m>;
clock-names = "wkupclk", "refclk";
};
TI PIPE3 PHY
Required properties:
......
......@@ -49,6 +49,17 @@ config PHY_XGENE
help
This option enables support for APM X-Gene SoC multi-purpose PHY.
config USB_LGM_PHY
tristate "INTEL Lightning Mountain USB PHY Driver"
depends on USB_SUPPORT
select USB_PHY
select REGULATOR
select REGULATOR_FIXED_VOLTAGE
help
Enable this to support Intel DWC3 PHY USB phy. This driver provides
interface to interact with USB GEN-II and USB 3.x PHY that is part
of the Intel network SOC.
source "drivers/phy/allwinner/Kconfig"
source "drivers/phy/amlogic/Kconfig"
source "drivers/phy/broadcom/Kconfig"
......
......@@ -8,6 +8,7 @@ obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY) += phy-core-mipi-dphy.o
obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o
obj-$(CONFIG_PHY_XGENE) += phy-xgene.o
obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o
obj-$(CONFIG_USB_LGM_PHY) += phy-lgm-usb.o
obj-y += allwinner/ \
amlogic/ \
broadcom/ \
......
......@@ -13,6 +13,7 @@
#include <linux/bcma/bcma.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/iopoll.h>
#include <linux/mdio.h>
#include <linux/module.h>
#include <linux/of_address.h>
......@@ -258,29 +259,24 @@ static struct mdio_driver bcm_ns_usb3_mdio_driver = {
**************************************************/
static int bcm_ns_usb3_wait_reg(struct bcm_ns_usb3 *usb3, void __iomem *addr,
u32 mask, u32 value, unsigned long timeout)
u32 mask, u32 value, int usec)
{
unsigned long deadline = jiffies + timeout;
u32 val;
int ret;
do {
val = readl(addr);
if ((val & mask) == value)
return 0;
cpu_relax();
udelay(10);
} while (!time_after_eq(jiffies, deadline));
ret = readl_poll_timeout_atomic(addr, val, ((val & mask) == value),
10, usec);
if (ret)
dev_err(usb3->dev, "Timeout waiting for register %p\n", addr);
dev_err(usb3->dev, "Timeout waiting for register %p\n", addr);
return -EBUSY;
return ret;
}
static inline int bcm_ns_usb3_mii_mng_wait_idle(struct bcm_ns_usb3 *usb3)
{
return bcm_ns_usb3_wait_reg(usb3, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL,
0x0100, 0x0000,
usecs_to_jiffies(BCM_NS_USB3_MII_MNG_TIMEOUT_US));
BCM_NS_USB3_MII_MNG_TIMEOUT_US);
}
static int bcm_ns_usb3_platform_phy_write(struct bcm_ns_usb3 *usb3, u16 reg,
......
......@@ -18,6 +18,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/irq.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
......@@ -87,17 +88,11 @@ static const unsigned int usb_extcon_cable[] = {
static inline int pll_lock_stat(u32 usb_reg, int reg_mask,
struct ns2_phy_driver *driver)
{
int retry = PLL_LOCK_RETRY;
u32 val;
do {
udelay(1);
val = readl(driver->icfgdrd_regs + usb_reg);
if (val & reg_mask)
return 0;
} while (--retry > 0);
return -EBUSY;
return readl_poll_timeout_atomic(driver->icfgdrd_regs + usb_reg,
val, (val & reg_mask), 1,
PLL_LOCK_RETRY);
}
static int ns2_drd_phy_init(struct phy *phy)
......
......@@ -5,6 +5,7 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
......@@ -109,19 +110,15 @@ static inline void bcm_usb_reg32_setbits(void __iomem *addr, uint32_t set)
static int bcm_usb_pll_lock_check(void __iomem *addr, u32 bit)
{
int retry;
u32 rd_data;
u32 data;
int ret;
retry = PLL_LOCK_RETRY_COUNT;
do {
rd_data = readl(addr);
if (rd_data & bit)
return 0;
udelay(1);
} while (--retry > 0);
ret = readl_poll_timeout_atomic(addr, data, (data & bit), 1,
PLL_LOCK_RETRY_COUNT);
if (ret)
pr_err("%s: FAIL\n", __func__);
pr_err("%s: FAIL\n", __func__);
return -ETIMEDOUT;
return ret;
}
static int bcm_usb_ss_phy_init(struct bcm_usb_phy_cfg *phy_cfg)
......
......@@ -97,7 +97,7 @@ struct cdns_reg_pairs {
struct cdns_salvo_data {
u8 reg_offset_shift;
struct cdns_reg_pairs *init_sequence_val;
const struct cdns_reg_pairs *init_sequence_val;
u8 init_sequence_length;
};
......@@ -126,7 +126,7 @@ static void cdns_salvo_write(struct cdns_salvo_phy *salvo_phy,
* Below bringup sequence pair are from Cadence PHY's User Guide
* and NXP platform tuning results.
*/
static struct cdns_reg_pairs cdns_nxp_sequence_pair[] = {
static const struct cdns_reg_pairs cdns_nxp_sequence_pair[] = {
{0x0830, PHY_PMA_CMN_CTRL1},
{0x0010, TB_ADDR_CMN_DIAG_HSCLK_SEL},
{0x00f0, TB_ADDR_CMN_PLL0_VCOCAL_INIT_TMR},
......@@ -217,7 +217,7 @@ static int cdns_salvo_phy_init(struct phy *phy)
return ret;
for (i = 0; i < data->init_sequence_length; i++) {
struct cdns_reg_pairs *reg_pair = data->init_sequence_val + i;
const struct cdns_reg_pairs *reg_pair = data->init_sequence_val + i;
cdns_salvo_write(salvo_phy, reg_pair->off, reg_pair->val);
}
......@@ -251,7 +251,7 @@ static int cdns_salvo_phy_power_off(struct phy *phy)
return 0;
}
static struct phy_ops cdns_salvo_phy_ops = {
static const struct phy_ops cdns_salvo_phy_ops = {
.init = cdns_salvo_phy_init,
.power_on = cdns_salvo_phy_power_on,
.power_off = cdns_salvo_phy_power_off,
......
......@@ -172,10 +172,10 @@ struct cdns_sierra_data {
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;
const struct cdns_reg_pairs *pcie_cmn_vals;
const struct cdns_reg_pairs *pcie_ln_vals;
const struct cdns_reg_pairs *usb_cmn_vals;
const struct cdns_reg_pairs *usb_ln_vals;
};
struct cdns_regmap_cdb_context {
......@@ -233,7 +233,7 @@ static int cdns_regmap_read(void *context, unsigned int reg, unsigned int *val)
.reg_read = cdns_regmap_read, \
}
static struct regmap_config cdns_sierra_lane_cdb_config[] = {
static const 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"),
......@@ -252,7 +252,7 @@ static struct regmap_config cdns_sierra_lane_cdb_config[] = {
SIERRA_LANE_CDB_REGMAP_CONF("15"),
};
static struct regmap_config cdns_sierra_common_cdb_config = {
static const struct regmap_config cdns_sierra_common_cdb_config = {
.name = "sierra_common_cdb",
.reg_stride = 1,
.fast_io = true,
......@@ -260,7 +260,7 @@ static struct regmap_config cdns_sierra_common_cdb_config = {
.reg_read = cdns_regmap_read,
};
static struct regmap_config cdns_sierra_phy_config_ctrl_config = {
static const struct regmap_config cdns_sierra_phy_config_ctrl_config = {
.name = "sierra_phy_config_ctrl",
.reg_stride = 1,
.fast_io = true,
......@@ -274,7 +274,7 @@ static int cdns_sierra_phy_init(struct phy *gphy)
struct cdns_sierra_phy *phy = dev_get_drvdata(gphy->dev.parent);
struct regmap *regmap;
int i, j;
struct cdns_reg_pairs *cmn_vals, *ln_vals;
const struct cdns_reg_pairs *cmn_vals, *ln_vals;
u32 num_cmn_regs, num_ln_regs;
/* Initialise the PHY registers, unless auto configured */
......@@ -654,7 +654,7 @@ static int cdns_sierra_phy_remove(struct platform_device *pdev)
}
/* refclk100MHz_32b_PCIe_cmn_pll_ext_ssc */
static struct cdns_reg_pairs cdns_pcie_cmn_regs_ext_ssc[] = {
static const 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},
......@@ -663,7 +663,7 @@ static struct cdns_reg_pairs cdns_pcie_cmn_regs_ext_ssc[] = {
};
/* refclk100MHz_32b_PCIe_ln_ext_ssc */
static struct cdns_reg_pairs cdns_pcie_ln_regs_ext_ssc[] = {
static const 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},
......@@ -674,7 +674,7 @@ static struct cdns_reg_pairs cdns_pcie_ln_regs_ext_ssc[] = {
};
/* refclk100MHz_20b_USB_cmn_pll_ext_ssc */
static struct cdns_reg_pairs cdns_usb_cmn_regs_ext_ssc[] = {
static const 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},
......@@ -682,7 +682,7 @@ static struct cdns_reg_pairs cdns_usb_cmn_regs_ext_ssc[] = {
};
/* refclk100MHz_20b_USB_ln_ext_ssc */
static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = {
static const struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = {
{0xFE0A, SIERRA_DET_STANDEC_A_PREG},
{0x000F, SIERRA_DET_STANDEC_B_PREG},
{0x55A5, SIERRA_DET_STANDEC_C_PREG},
......
......@@ -25,11 +25,14 @@
#define REF_CLK_19_2MHz 19200000
#define REF_CLK_25MHz 25000000
#define DEFAULT_NUM_LANES 4
#define MAX_NUM_LANES 4
#define DEFAULT_MAX_BIT_RATE 8100 /* in Mbps */
#define NUM_SSC_MODE 3
#define NUM_PHY_TYPE 6
#define POLL_TIMEOUT_US 5000
#define PLL_LOCK_TIMEOUT 100000
#define TORRENT_COMMON_CDB_OFFSET 0x0
......@@ -79,6 +82,8 @@
#define CMN_PLLSM0_PLLLOCK_TMR 0x002CU
#define CMN_PLLSM1_PLLPRE_TMR 0x0032U
#define CMN_PLLSM1_PLLLOCK_TMR 0x0034U
#define CMN_CDIAG_CDB_PWRI_OVRD 0x0041U
#define CMN_CDIAG_XCVRC_PWRI_OVRD 0x0047U
#define CMN_BGCAL_INIT_TMR 0x0064U
#define CMN_BGCAL_ITER_TMR 0x0065U
#define CMN_IBCAL_INIT_TMR 0x0074U
......@@ -99,6 +104,14 @@
#define CMN_PLL0_LOCK_REFCNT_START 0x009CU
#define CMN_PLL0_LOCK_PLLCNT_START 0x009EU
#define CMN_PLL0_LOCK_PLLCNT_THR 0x009FU
#define CMN_PLL0_INTDIV_M1 0x00A0U
#define CMN_PLL0_FRACDIVH_M1 0x00A2U
#define CMN_PLL0_HIGH_THR_M1 0x00A3U
#define CMN_PLL0_DSM_DIAG_M1 0x00A4U
#define CMN_PLL0_SS_CTRL1_M1 0x00A8U
#define CMN_PLL0_SS_CTRL2_M1 0x00A9U
#define CMN_PLL0_SS_CTRL3_M1 0x00AAU
#define CMN_PLL0_SS_CTRL4_M1 0x00ABU
#define CMN_PLL1_VCOCAL_TCTRL 0x00C2U
#define CMN_PLL1_VCOCAL_INIT_TMR 0x00C4U
#define CMN_PLL1_VCOCAL_ITER_TMR 0x00C5U
......@@ -116,8 +129,10 @@
#define CMN_PLL1_LOCK_REFCNT_START 0x00DCU
#define CMN_PLL1_LOCK_PLLCNT_START 0x00DEU
#define CMN_PLL1_LOCK_PLLCNT_THR 0x00DFU
#define CMN_TXPUCAL_TUNE 0x0103U
#define CMN_TXPUCAL_INIT_TMR 0x0104U
#define CMN_TXPUCAL_ITER_TMR 0x0105U
#define CMN_TXPDCAL_TUNE 0x010BU
#define CMN_TXPDCAL_INIT_TMR 0x010CU
#define CMN_TXPDCAL_ITER_TMR 0x010DU
#define CMN_RXCAL_INIT_TMR 0x0114U
......@@ -131,24 +146,31 @@
#define CMN_PDIAG_PLL0_CP_PADJ_M0 0x01A4U
#define CMN_PDIAG_PLL0_CP_IADJ_M0 0x01A5U
#define CMN_PDIAG_PLL0_FILT_PADJ_M0 0x01A6U
#define CMN_PDIAG_PLL0_CTRL_M1 0x01B0U
#define CMN_PDIAG_PLL0_CLK_SEL_M1 0x01B1U
#define CMN_PDIAG_PLL0_CP_PADJ_M1 0x01B4U
#define CMN_PDIAG_PLL0_CP_IADJ_M1 0x01B5U
#define CMN_PDIAG_PLL0_FILT_PADJ_M1 0x01B6U
#define CMN_PDIAG_PLL1_CTRL_M0 0x01C0U
#define CMN_PDIAG_PLL1_CLK_SEL_M0 0x01C1U
#define CMN_PDIAG_PLL1_CP_PADJ_M0 0x01C4U
#define CMN_PDIAG_PLL1_CP_IADJ_M0 0x01C5U
#define CMN_PDIAG_PLL1_FILT_PADJ_M0 0x01C6U
#define CMN_DIAG_BIAS_OVRD1 0x01E1U
/* PMA TX Lane registers */
#define TX_TXCC_CTRL 0x0040U
#define TX_TXCC_CPOST_MULT_00 0x004CU
#define TX_TXCC_CPOST_MULT_01 0x004DU
#define TX_TXCC_MGNFS_MULT_000 0x0050U
#define DRV_DIAG_TX_DRV 0x00C6U
#define XCVR_DIAG_PLLDRC_CTRL 0x00E5U
#define XCVR_DIAG_HSCLK_SEL 0x00E6U
#define XCVR_DIAG_HSCLK_DIV 0x00E7U
#define XCVR_DIAG_BIDI_CTRL 0x00EAU
#define XCVR_DIAG_PSC_OVRD 0x00EBU
#define TX_PSC_A0 0x0100U
#define TX_PSC_A1 0x0101U
#define TX_PSC_A2 0x0102U
#define TX_PSC_A3 0x0103U
#define TX_RCVDET_ST_TMR 0x0123U
......@@ -157,23 +179,49 @@
/* PMA RX Lane registers */
#define RX_PSC_A0 0x0000U
#define RX_PSC_A1 0x0001U
#define RX_PSC_A2 0x0002U
#define RX_PSC_A3 0x0003U
#define RX_PSC_CAL 0x0006U
#define RX_CDRLF_CNFG 0x0080U
#define RX_CDRLF_CNFG3 0x0082U
#define RX_SIGDET_HL_FILT_TMR 0x0090U
#define RX_REE_GCSM1_CTRL 0x0108U
#define RX_REE_GCSM1_EQENM_PH1 0x0109U
#define RX_REE_GCSM1_EQENM_PH2 0x010AU
#define RX_REE_GCSM2_CTRL 0x0110U
#define RX_REE_PERGCSM_CTRL 0x0118U
#define RX_REE_ATTEN_THR 0x0149U
#define RX_REE_TAP1_CLIP 0x0171U
#define RX_REE_TAP2TON_CLIP 0x0172U
#define RX_REE_SMGM_CTRL1 0x0177U
#define RX_REE_SMGM_CTRL2 0x0178U
#define RX_DIAG_DFE_CTRL 0x01E0U
#define RX_DIAG_DFE_AMP_TUNE_2 0x01E2U
#define RX_DIAG_DFE_AMP_TUNE_3 0x01E3U
#define RX_DIAG_NQST_CTRL 0x01E5U
#define RX_DIAG_SIGDET_TUNE 0x01E8U
#define RX_DIAG_PI_RATE 0x01F4U
#define RX_DIAG_PI_CAP 0x01F5U
#define RX_DIAG_ACYA 0x01FFU
/* PHY PCS common registers */
#define PHY_PLL_CFG 0x000EU
#define PHY_PIPE_USB3_GEN2_PRE_CFG0 0x0020U
#define PHY_PIPE_USB3_GEN2_POST_CFG0 0x0022U
#define PHY_PIPE_USB3_GEN2_POST_CFG1 0x0023U
/* PHY PMA common registers */
#define PHY_PMA_CMN_CTRL1 0x0000U
#define PHY_PMA_CMN_CTRL2 0x0001U
#define PHY_PMA_PLL_RAW_CTRL 0x0003U
static const struct reg_field phy_pll_cfg =
REG_FIELD(PHY_PLL_CFG, 0, 1);
static const struct reg_field phy_pma_cmn_ctrl_1 =
REG_FIELD(PHY_PMA_CMN_CTRL1, 0, 0);
static const struct reg_field phy_pma_cmn_ctrl_2 =
REG_FIELD(PHY_PMA_CMN_CTRL2, 0, 7);
......@@ -183,14 +231,28 @@ static const struct reg_field phy_pma_pll_raw_ctrl =
static const struct reg_field phy_reset_ctrl =
REG_FIELD(PHY_RESET, 8, 8);
static const struct of_device_id cdns_torrent_phy_of_match[];
enum cdns_torrent_phy_type {
TYPE_NONE,
TYPE_DP,
TYPE_PCIE,
TYPE_SGMII,
TYPE_QSGMII,
TYPE_USB,
};
enum cdns_torrent_ssc_mode {
NO_SSC,
EXTERNAL_SSC,
INTERNAL_SSC
};
struct cdns_torrent_inst {
struct phy *phy;
u32 mlane;
u32 phy_type;
enum cdns_torrent_phy_type phy_type;
u32 num_lanes;
struct reset_control *lnk_rst;
enum cdns_torrent_ssc_mode ssc_mode;
};
struct cdns_torrent_phy {
......@@ -198,11 +260,13 @@ struct cdns_torrent_phy {
void __iomem *sd_base; /* SD0801 registers base */
u32 max_bit_rate; /* Maximum link bit rate to use (in Mbps) */
struct reset_control *phy_rst;
struct reset_control *apb_rst;
struct device *dev;
struct clk *clk;
unsigned long ref_clk_rate;
struct cdns_torrent_inst phys[MAX_NUM_LANES];
int nsubnodes;
const struct cdns_torrent_data *init_data;
struct regmap *regmap;
struct regmap *regmap_common_cdb;
struct regmap *regmap_phy_pcs_common_cdb;
......@@ -211,6 +275,7 @@ struct cdns_torrent_phy {
struct regmap *regmap_rx_lane_cdb[MAX_NUM_LANES];
struct regmap *regmap_dptx_phy_reg;
struct regmap_field *phy_pll_cfg;
struct regmap_field *phy_pma_cmn_ctrl_1;
struct regmap_field *phy_pma_cmn_ctrl_2;
struct regmap_field *phy_pma_pll_raw_ctrl;
struct regmap_field *phy_reset_ctrl;
......@@ -223,8 +288,8 @@ enum phy_powerstate {
POWERSTATE_A3 = 3,
};
static int cdns_torrent_phy_init(struct phy *phy);
static int cdns_torrent_dp_init(struct phy *phy);
static int cdns_torrent_dp_exit(struct phy *phy);
static int cdns_torrent_dp_run(struct cdns_torrent_phy *cdns_phy,
u32 num_lanes);
static
......@@ -254,17 +319,38 @@ static int cdns_torrent_phy_on(struct phy *phy);
static int cdns_torrent_phy_off(struct phy *phy);
static const struct phy_ops cdns_torrent_phy_ops = {
.init = cdns_torrent_dp_init,
.exit = cdns_torrent_dp_exit,
.init = cdns_torrent_phy_init,
.configure = cdns_torrent_dp_configure,
.power_on = cdns_torrent_phy_on,
.power_off = cdns_torrent_phy_off,
.owner = THIS_MODULE,
};
struct cdns_reg_pairs {
u32 val;
u32 off;
};
struct cdns_torrent_vals {
struct cdns_reg_pairs *reg_pairs;
u32 num_regs;
};
struct cdns_torrent_data {
u8 block_offset_shift;
u8 reg_offset_shift;
u8 block_offset_shift;
u8 reg_offset_shift;
struct cdns_torrent_vals *link_cmn_vals[NUM_PHY_TYPE][NUM_PHY_TYPE]
[NUM_SSC_MODE];
struct cdns_torrent_vals *xcvr_diag_vals[NUM_PHY_TYPE][NUM_PHY_TYPE]
[NUM_SSC_MODE];
struct cdns_torrent_vals *pcs_cmn_vals[NUM_PHY_TYPE][NUM_PHY_TYPE]
[NUM_SSC_MODE];
struct cdns_torrent_vals *cmn_vals[NUM_PHY_TYPE][NUM_PHY_TYPE]
[NUM_SSC_MODE];
struct cdns_torrent_vals *tx_ln_vals[NUM_PHY_TYPE][NUM_PHY_TYPE]
[NUM_SSC_MODE];
struct cdns_torrent_vals *rx_ln_vals[NUM_PHY_TYPE][NUM_PHY_TYPE]
[NUM_SSC_MODE];
};
struct cdns_regmap_cdb_context {
......@@ -331,21 +417,21 @@ static int cdns_regmap_dptx_read(void *context, unsigned int reg,
.reg_read = cdns_regmap_read, \
}
static struct regmap_config cdns_torrent_tx_lane_cdb_config[] = {
static const struct regmap_config cdns_torrent_tx_lane_cdb_config[] = {
TORRENT_TX_LANE_CDB_REGMAP_CONF("0"),
TORRENT_TX_LANE_CDB_REGMAP_CONF("1"),
TORRENT_TX_LANE_CDB_REGMAP_CONF("2"),
TORRENT_TX_LANE_CDB_REGMAP_CONF("3"),
};
static struct regmap_config cdns_torrent_rx_lane_cdb_config[] = {
static const struct regmap_config cdns_torrent_rx_lane_cdb_config[] = {
TORRENT_RX_LANE_CDB_REGMAP_CONF("0"),
TORRENT_RX_LANE_CDB_REGMAP_CONF("1"),
TORRENT_RX_LANE_CDB_REGMAP_CONF("2"),
TORRENT_RX_LANE_CDB_REGMAP_CONF("3"),
};
static struct regmap_config cdns_torrent_common_cdb_config = {
static const struct regmap_config cdns_torrent_common_cdb_config = {
.name = "torrent_common_cdb",
.reg_stride = 1,
.fast_io = true,
......@@ -353,7 +439,7 @@ static struct regmap_config cdns_torrent_common_cdb_config = {
.reg_read = cdns_regmap_read,
};
static struct regmap_config cdns_torrent_phy_pcs_cmn_cdb_config = {
static const struct regmap_config cdns_torrent_phy_pcs_cmn_cdb_config = {
.name = "torrent_phy_pcs_cmn_cdb",
.reg_stride = 1,
.fast_io = true,
......@@ -361,7 +447,7 @@ static struct regmap_config cdns_torrent_phy_pcs_cmn_cdb_config = {
.reg_read = cdns_regmap_read,
};
static struct regmap_config cdns_torrent_phy_pma_cmn_cdb_config = {
static const struct regmap_config cdns_torrent_phy_pma_cmn_cdb_config = {
.name = "torrent_phy_pma_cmn_cdb",
.reg_stride = 1,
.fast_io = true,
......@@ -369,7 +455,7 @@ static struct regmap_config cdns_torrent_phy_pma_cmn_cdb_config = {
.reg_read = cdns_regmap_read,
};
static struct regmap_config cdns_torrent_dptx_phy_config = {
static const struct regmap_config cdns_torrent_dptx_phy_config = {
.name = "torrent_dptx_phy",
.reg_stride = 1,
.fast_io = true,
......@@ -848,19 +934,6 @@ static int cdns_torrent_dp_init(struct phy *phy)
struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent);
struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg;
ret = clk_prepare_enable(cdns_phy->clk);
if (ret) {
dev_err(cdns_phy->dev, "Failed to prepare ref clock\n");
return ret;
}
cdns_phy->ref_clk_rate = clk_get_rate(cdns_phy->clk);
if (!(cdns_phy->ref_clk_rate)) {
dev_err(cdns_phy->dev, "Failed to get ref clock rate\n");
clk_disable_unprepare(cdns_phy->clk);
return -EINVAL;
}
switch (cdns_phy->ref_clk_rate) {
case REF_CLK_19_2MHz:
case REF_CLK_25MHz:
......@@ -920,14 +993,6 @@ static int cdns_torrent_dp_init(struct phy *phy)
return ret;
}
static int cdns_torrent_dp_exit(struct phy *phy)
{
struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent);
clk_disable_unprepare(cdns_phy->clk);
return 0;
}
static
int cdns_torrent_dp_wait_pma_cmn_ready(struct cdns_torrent_phy *cdns_phy)
{
......@@ -1543,15 +1608,34 @@ static int cdns_torrent_phy_on(struct phy *phy)
{
struct cdns_torrent_inst *inst = phy_get_drvdata(phy);
struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent);
u32 read_val;
int ret;
/* Take the PHY out of reset */
ret = reset_control_deassert(cdns_phy->phy_rst);
if (ret)
if (cdns_phy->nsubnodes == 1) {
/* Take the PHY lane group out of reset */
reset_control_deassert(inst->lnk_rst);
/* Take the PHY out of reset */
ret = reset_control_deassert(cdns_phy->phy_rst);
if (ret)
return ret;
}
/*
* Wait for cmn_ready assertion
* PHY_PMA_CMN_CTRL1[0] == 1
*/
ret = regmap_field_read_poll_timeout(cdns_phy->phy_pma_cmn_ctrl_1,
read_val, read_val, 1000,
PLL_LOCK_TIMEOUT);
if (ret) {
dev_err(cdns_phy->dev, "Timeout waiting for CMN ready\n");
return ret;
}
mdelay(10);
/* Take the PHY lane group out of reset */
return reset_control_deassert(inst->lnk_rst);
return 0;
}
static int cdns_torrent_phy_off(struct phy *phy)
......@@ -1560,6 +1644,9 @@ static int cdns_torrent_phy_off(struct phy *phy)
struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent);
int ret;
if (cdns_phy->nsubnodes != 1)
return 0;
ret = reset_control_assert(cdns_phy->phy_rst);
if (ret)
return ret;
......@@ -1585,7 +1672,24 @@ static struct regmap *cdns_regmap_init(struct device *dev, void __iomem *base,
return devm_regmap_init(dev, NULL, ctx, config);
}
static int cdns_regfield_init(struct cdns_torrent_phy *cdns_phy)
static int cdns_torrent_dp_regfield_init(struct cdns_torrent_phy *cdns_phy)
{
struct device *dev = cdns_phy->dev;
struct regmap_field *field;
struct regmap *regmap;
regmap = cdns_phy->regmap_dptx_phy_reg;
field = devm_regmap_field_alloc(dev, regmap, phy_reset_ctrl);
if (IS_ERR(field)) {
dev_err(dev, "PHY_RESET reg field init failed\n");
return PTR_ERR(field);
}
cdns_phy->phy_reset_ctrl = field;
return 0;
}
static int cdns_torrent_regfield_init(struct cdns_torrent_phy *cdns_phy)
{
struct device *dev = cdns_phy->dev;
struct regmap_field *field;
......@@ -1599,6 +1703,14 @@ static int cdns_regfield_init(struct cdns_torrent_phy *cdns_phy)
}
cdns_phy->phy_pll_cfg = field;
regmap = cdns_phy->regmap_phy_pma_common_cdb;
field = devm_regmap_field_alloc(dev, regmap, phy_pma_cmn_ctrl_1);
if (IS_ERR(field)) {
dev_err(dev, "PHY_PMA_CMN_CTRL1 reg field init failed\n");
return PTR_ERR(field);
}
cdns_phy->phy_pma_cmn_ctrl_1 = field;
regmap = cdns_phy->regmap_phy_pma_common_cdb;
field = devm_regmap_field_alloc(dev, regmap, phy_pma_cmn_ctrl_2);
if (IS_ERR(field)) {
......@@ -1615,28 +1727,44 @@ static int cdns_regfield_init(struct cdns_torrent_phy *cdns_phy)
}
cdns_phy->phy_pma_pll_raw_ctrl = field;
regmap = cdns_phy->regmap_dptx_phy_reg;
field = devm_regmap_field_alloc(dev, regmap, phy_reset_ctrl);
if (IS_ERR(field)) {
dev_err(dev, "PHY_RESET reg field init failed\n");
return PTR_ERR(field);
return 0;
}
static int cdns_torrent_dp_regmap_init(struct cdns_torrent_phy *cdns_phy)
{
void __iomem *base = cdns_phy->base;
struct device *dev = cdns_phy->dev;
struct regmap *regmap;
u8 reg_offset_shift;
u32 block_offset;
reg_offset_shift = cdns_phy->init_data->reg_offset_shift;
block_offset = TORRENT_DPTX_PHY_OFFSET;
regmap = cdns_regmap_init(dev, base, block_offset,
reg_offset_shift,
&cdns_torrent_dptx_phy_config);
if (IS_ERR(regmap)) {
dev_err(dev, "Failed to init DPTX PHY regmap\n");
return PTR_ERR(regmap);
}
cdns_phy->phy_reset_ctrl = field;
cdns_phy->regmap_dptx_phy_reg = regmap;
return 0;
}
static int cdns_regmap_init_torrent_dp(struct cdns_torrent_phy *cdns_phy,
void __iomem *sd_base,
void __iomem *base,
u8 block_offset_shift,
u8 reg_offset_shift)
static int cdns_torrent_regmap_init(struct cdns_torrent_phy *cdns_phy)
{
void __iomem *sd_base = cdns_phy->sd_base;
u8 block_offset_shift, reg_offset_shift;
struct device *dev = cdns_phy->dev;
struct regmap *regmap;
u32 block_offset;
int i;
block_offset_shift = cdns_phy->init_data->block_offset_shift;
reg_offset_shift = cdns_phy->init_data->reg_offset_shift;
for (i = 0; i < MAX_NUM_LANES; i++) {
block_offset = TORRENT_TX_LANE_CDB_OFFSET(i, block_offset_shift,
reg_offset_shift);
......@@ -1691,43 +1819,282 @@ static int cdns_regmap_init_torrent_dp(struct cdns_torrent_phy *cdns_phy,
}
cdns_phy->regmap_phy_pma_common_cdb = regmap;
block_offset = TORRENT_DPTX_PHY_OFFSET;
regmap = cdns_regmap_init(dev, base, block_offset,
reg_offset_shift,
&cdns_torrent_dptx_phy_config);
if (IS_ERR(regmap)) {
dev_err(dev, "Failed to init DPTX PHY regmap\n");
return PTR_ERR(regmap);
return 0;
}
static int cdns_torrent_phy_init(struct phy *phy)
{
struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent);
const struct cdns_torrent_data *init_data = cdns_phy->init_data;
struct cdns_torrent_vals *cmn_vals, *tx_ln_vals, *rx_ln_vals;
struct cdns_torrent_vals *link_cmn_vals, *xcvr_diag_vals;
struct cdns_torrent_inst *inst = phy_get_drvdata(phy);
enum cdns_torrent_phy_type phy_type = inst->phy_type;
enum cdns_torrent_ssc_mode ssc = inst->ssc_mode;
struct cdns_torrent_vals *pcs_cmn_vals;
struct cdns_reg_pairs *reg_pairs;
struct regmap *regmap;
u32 num_regs;
int i, j;
if (cdns_phy->nsubnodes > 1)
return 0;
if (phy_type == TYPE_DP)
return cdns_torrent_dp_init(phy);
/**
* Spread spectrum generation is not required or supported
* for SGMII/QSGMII
*/
if (phy_type == TYPE_SGMII || phy_type == TYPE_QSGMII)
ssc = NO_SSC;
/* PHY configuration specific registers for single link */
link_cmn_vals = init_data->link_cmn_vals[phy_type][TYPE_NONE][ssc];
if (link_cmn_vals) {
reg_pairs = link_cmn_vals->reg_pairs;
num_regs = link_cmn_vals->num_regs;
regmap = cdns_phy->regmap_common_cdb;
/**
* First array value in link_cmn_vals must be of
* PHY_PLL_CFG register
*/
regmap_field_write(cdns_phy->phy_pll_cfg, reg_pairs[0].val);
for (i = 1; i < num_regs; i++)
regmap_write(regmap, reg_pairs[i].off,
reg_pairs[i].val);
}
xcvr_diag_vals = init_data->xcvr_diag_vals[phy_type][TYPE_NONE][ssc];
if (xcvr_diag_vals) {
reg_pairs = xcvr_diag_vals->reg_pairs;
num_regs = xcvr_diag_vals->num_regs;
for (i = 0; i < inst->num_lanes; i++) {
regmap = cdns_phy->regmap_tx_lane_cdb[i + inst->mlane];
for (j = 0; j < num_regs; j++)
regmap_write(regmap, reg_pairs[j].off,
reg_pairs[j].val);
}
}
/* PHY PCS common registers configurations */
pcs_cmn_vals = init_data->pcs_cmn_vals[phy_type][TYPE_NONE][ssc];
if (pcs_cmn_vals) {
reg_pairs = pcs_cmn_vals->reg_pairs;
num_regs = pcs_cmn_vals->num_regs;
regmap = cdns_phy->regmap_phy_pcs_common_cdb;
for (i = 0; i < num_regs; i++)
regmap_write(regmap, reg_pairs[i].off,
reg_pairs[i].val);
}
/* PMA common registers configurations */
cmn_vals = init_data->cmn_vals[phy_type][TYPE_NONE][ssc];
if (cmn_vals) {
reg_pairs = cmn_vals->reg_pairs;
num_regs = cmn_vals->num_regs;
regmap = cdns_phy->regmap_common_cdb;
for (i = 0; i < num_regs; i++)
regmap_write(regmap, reg_pairs[i].off,
reg_pairs[i].val);
}
/* PMA TX lane registers configurations */
tx_ln_vals = init_data->tx_ln_vals[phy_type][TYPE_NONE][ssc];
if (tx_ln_vals) {
reg_pairs = tx_ln_vals->reg_pairs;
num_regs = tx_ln_vals->num_regs;
for (i = 0; i < inst->num_lanes; i++) {
regmap = cdns_phy->regmap_tx_lane_cdb[i + inst->mlane];
for (j = 0; j < num_regs; j++)
regmap_write(regmap, reg_pairs[j].off,
reg_pairs[j].val);
}
}
/* PMA RX lane registers configurations */
rx_ln_vals = init_data->rx_ln_vals[phy_type][TYPE_NONE][ssc];
if (rx_ln_vals) {
reg_pairs = rx_ln_vals->reg_pairs;
num_regs = rx_ln_vals->num_regs;
for (i = 0; i < inst->num_lanes; i++) {
regmap = cdns_phy->regmap_rx_lane_cdb[i + inst->mlane];
for (j = 0; j < num_regs; j++)
regmap_write(regmap, reg_pairs[j].off,
reg_pairs[j].val);
}
}
cdns_phy->regmap_dptx_phy_reg = regmap;
return 0;
}
static
int cdns_torrent_phy_configure_multilink(struct cdns_torrent_phy *cdns_phy)
{
const struct cdns_torrent_data *init_data = cdns_phy->init_data;
struct cdns_torrent_vals *cmn_vals, *tx_ln_vals, *rx_ln_vals;
struct cdns_torrent_vals *link_cmn_vals, *xcvr_diag_vals;
enum cdns_torrent_phy_type phy_t1, phy_t2, tmp_phy_type;
struct cdns_torrent_vals *pcs_cmn_vals;
int i, j, node, mlane, num_lanes, ret;
struct cdns_reg_pairs *reg_pairs;
enum cdns_torrent_ssc_mode ssc;
struct regmap *regmap;
u32 num_regs;
/* Maximum 2 links (subnodes) are supported */
if (cdns_phy->nsubnodes != 2)
return -EINVAL;
phy_t1 = cdns_phy->phys[0].phy_type;
phy_t2 = cdns_phy->phys[1].phy_type;
/**
* First configure the PHY for first link with phy_t1. Get the array
* values as [phy_t1][phy_t2][ssc].
*/
for (node = 0; node < cdns_phy->nsubnodes; node++) {
if (node == 1) {
/**
* If first link with phy_t1 is configured, then
* configure the PHY for second link with phy_t2.
* Get the array values as [phy_t2][phy_t1][ssc].
*/
tmp_phy_type = phy_t1;
phy_t1 = phy_t2;
phy_t2 = tmp_phy_type;
}
mlane = cdns_phy->phys[node].mlane;
ssc = cdns_phy->phys[node].ssc_mode;
num_lanes = cdns_phy->phys[node].num_lanes;
/**
* PHY configuration specific registers:
* link_cmn_vals depend on combination of PHY types being
* configured and are common for both PHY types, so array
* values should be same for [phy_t1][phy_t2][ssc] and
* [phy_t2][phy_t1][ssc].
* xcvr_diag_vals also depend on combination of PHY types
* being configured, but these can be different for particular
* PHY type and are per lane.
*/
link_cmn_vals = init_data->link_cmn_vals[phy_t1][phy_t2][ssc];
if (link_cmn_vals) {
reg_pairs = link_cmn_vals->reg_pairs;
num_regs = link_cmn_vals->num_regs;
regmap = cdns_phy->regmap_common_cdb;
/**
* First array value in link_cmn_vals must be of
* PHY_PLL_CFG register
*/
regmap_field_write(cdns_phy->phy_pll_cfg,
reg_pairs[0].val);
for (i = 1; i < num_regs; i++)
regmap_write(regmap, reg_pairs[i].off,
reg_pairs[i].val);
}
xcvr_diag_vals = init_data->xcvr_diag_vals[phy_t1][phy_t2][ssc];
if (xcvr_diag_vals) {
reg_pairs = xcvr_diag_vals->reg_pairs;
num_regs = xcvr_diag_vals->num_regs;
for (i = 0; i < num_lanes; i++) {
regmap = cdns_phy->regmap_tx_lane_cdb[i + mlane];
for (j = 0; j < num_regs; j++)
regmap_write(regmap, reg_pairs[j].off,
reg_pairs[j].val);
}
}
/* PHY PCS common registers configurations */
pcs_cmn_vals = init_data->pcs_cmn_vals[phy_t1][phy_t2][ssc];
if (pcs_cmn_vals) {
reg_pairs = pcs_cmn_vals->reg_pairs;
num_regs = pcs_cmn_vals->num_regs;
regmap = cdns_phy->regmap_phy_pcs_common_cdb;
for (i = 0; i < num_regs; i++)
regmap_write(regmap, reg_pairs[i].off,
reg_pairs[i].val);
}
/* PMA common registers configurations */
cmn_vals = init_data->cmn_vals[phy_t1][phy_t2][ssc];
if (cmn_vals) {
reg_pairs = cmn_vals->reg_pairs;
num_regs = cmn_vals->num_regs;
regmap = cdns_phy->regmap_common_cdb;
for (i = 0; i < num_regs; i++)
regmap_write(regmap, reg_pairs[i].off,
reg_pairs[i].val);
}
/* PMA TX lane registers configurations */
tx_ln_vals = init_data->tx_ln_vals[phy_t1][phy_t2][ssc];
if (tx_ln_vals) {
reg_pairs = tx_ln_vals->reg_pairs;
num_regs = tx_ln_vals->num_regs;
for (i = 0; i < num_lanes; i++) {
regmap = cdns_phy->regmap_tx_lane_cdb[i + mlane];
for (j = 0; j < num_regs; j++)
regmap_write(regmap, reg_pairs[j].off,
reg_pairs[j].val);
}
}
/* PMA RX lane registers configurations */
rx_ln_vals = init_data->rx_ln_vals[phy_t1][phy_t2][ssc];
if (rx_ln_vals) {
reg_pairs = rx_ln_vals->reg_pairs;
num_regs = rx_ln_vals->num_regs;
for (i = 0; i < num_lanes; i++) {
regmap = cdns_phy->regmap_rx_lane_cdb[i + mlane];
for (j = 0; j < num_regs; j++)
regmap_write(regmap, reg_pairs[j].off,
reg_pairs[j].val);
}
}
reset_control_deassert(cdns_phy->phys[node].lnk_rst);
}
/* Take the PHY out of reset */
ret = reset_control_deassert(cdns_phy->phy_rst);
if (ret)
return ret;
return 0;
}
static int cdns_torrent_phy_probe(struct platform_device *pdev)
{
struct resource *regs;
struct cdns_torrent_phy *cdns_phy;
struct device *dev = &pdev->dev;
struct phy_provider *phy_provider;
const struct of_device_id *match;
struct cdns_torrent_data *data;
const struct cdns_torrent_data *data;
struct device_node *child;
int ret, subnodes, node = 0, i;
u32 total_num_lanes = 0;
u8 init_dp_regmap = 0;
u32 phy_type;
/* Get init data for this PHY */
match = of_match_device(cdns_torrent_phy_of_match, dev);
if (!match)
data = of_device_get_match_data(dev);
if (!data)
return -EINVAL;
data = (struct cdns_torrent_data *)match->data;
cdns_phy = devm_kzalloc(dev, sizeof(*cdns_phy), GFP_KERNEL);
if (!cdns_phy)
return -ENOMEM;
dev_set_drvdata(dev, cdns_phy);
cdns_phy->dev = dev;
cdns_phy->init_data = data;
cdns_phy->phy_rst = devm_reset_control_get_exclusive_by_index(dev, 0);
if (IS_ERR(cdns_phy->phy_rst)) {
......@@ -1736,14 +2103,20 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev)
return PTR_ERR(cdns_phy->phy_rst);
}
cdns_phy->apb_rst = devm_reset_control_get_optional(dev, "torrent_apb");
if (IS_ERR(cdns_phy->apb_rst)) {
dev_err(dev, "%s: failed to get apb reset\n",
dev->of_node->full_name);
return PTR_ERR(cdns_phy->apb_rst);
}
cdns_phy->clk = devm_clk_get(dev, "refclk");
if (IS_ERR(cdns_phy->clk)) {
dev_err(dev, "phy ref clock not found\n");
return PTR_ERR(cdns_phy->clk);
}
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
cdns_phy->sd_base = devm_ioremap_resource(&pdev->dev, regs);
cdns_phy->sd_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(cdns_phy->sd_base))
return PTR_ERR(cdns_phy->sd_base);
......@@ -1751,14 +2124,39 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev)
if (subnodes == 0) {
dev_err(dev, "No available link subnodes found\n");
return -EINVAL;
} else if (subnodes != 1) {
dev_err(dev, "Driver supports only one link subnode.\n");
}
ret = cdns_torrent_regmap_init(cdns_phy);
if (ret)
return ret;
ret = cdns_torrent_regfield_init(cdns_phy);
if (ret)
return ret;
ret = clk_prepare_enable(cdns_phy->clk);
if (ret) {
dev_err(cdns_phy->dev, "Failed to prepare ref clock\n");
return ret;
}
cdns_phy->ref_clk_rate = clk_get_rate(cdns_phy->clk);
if (!(cdns_phy->ref_clk_rate)) {
dev_err(cdns_phy->dev, "Failed to get ref clock rate\n");
clk_disable_unprepare(cdns_phy->clk);
return -EINVAL;
}
/* Enable APB */
reset_control_deassert(cdns_phy->apb_rst);
for_each_available_child_of_node(dev->of_node, child) {
struct phy *gphy;
/* PHY subnode name must be 'phy'. */
if (!(of_node_name_eq(child, "phy")))
continue;
cdns_phy->phys[node].lnk_rst =
of_reset_control_array_get_exclusive(child);
if (IS_ERR(cdns_phy->phys[node].lnk_rst)) {
......@@ -1776,27 +2174,57 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev)
goto put_child;
}
if (cdns_phy->phys[node].mlane != 0) {
dev_err(dev,
"%s: Driver supports only lane-0 as master lane.\n",
if (of_property_read_u32(child, "cdns,phy-type", &phy_type)) {
dev_err(dev, "%s: No \"cdns,phy-type\"-property.\n",
child->full_name);
ret = -EINVAL;
goto put_child;
}
if (of_property_read_u32(child, "cdns,phy-type",
&cdns_phy->phys[node].phy_type)) {
dev_err(dev, "%s: No \"cdns,phy-type\"-property.\n",
switch (phy_type) {
case PHY_TYPE_PCIE:
cdns_phy->phys[node].phy_type = TYPE_PCIE;
break;
case PHY_TYPE_DP:
cdns_phy->phys[node].phy_type = TYPE_DP;
break;
case PHY_TYPE_SGMII:
cdns_phy->phys[node].phy_type = TYPE_SGMII;
break;
case PHY_TYPE_QSGMII:
cdns_phy->phys[node].phy_type = TYPE_QSGMII;
break;
case PHY_TYPE_USB3:
cdns_phy->phys[node].phy_type = TYPE_USB;
break;
default:
dev_err(dev, "Unsupported protocol\n");
ret = -EINVAL;
goto put_child;
}
if (of_property_read_u32(child, "cdns,num-lanes",
&cdns_phy->phys[node].num_lanes)) {
dev_err(dev, "%s: No \"cdns,num-lanes\"-property.\n",
child->full_name);
ret = -EINVAL;
goto put_child;
}
cdns_phy->phys[node].num_lanes = DEFAULT_NUM_LANES;
of_property_read_u32(child, "cdns,num-lanes",
&cdns_phy->phys[node].num_lanes);
total_num_lanes += cdns_phy->phys[node].num_lanes;
/* Get SSC mode */
cdns_phy->phys[node].ssc_mode = NO_SSC;
of_property_read_u32(child, "cdns,ssc-mode",
&cdns_phy->phys[node].ssc_mode);
gphy = devm_phy_create(dev, child, &cdns_torrent_phy_ops);
if (IS_ERR(gphy)) {
ret = PTR_ERR(gphy);
goto put_child;
}
if (cdns_phy->phys[node].phy_type == PHY_TYPE_DP) {
if (cdns_phy->phys[node].phy_type == TYPE_DP) {
switch (cdns_phy->phys[node].num_lanes) {
case 1:
case 2:
......@@ -1833,30 +2261,34 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev)
}
/* DPTX registers */
regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
cdns_phy->base = devm_ioremap_resource(&pdev->dev,
regs);
cdns_phy->base = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(cdns_phy->base)) {
ret = PTR_ERR(cdns_phy->base);
goto put_child;
}
gphy = devm_phy_create(dev, child,
&cdns_torrent_phy_ops);
if (IS_ERR(gphy)) {
ret = PTR_ERR(gphy);
goto put_child;
if (!init_dp_regmap) {
ret = cdns_torrent_dp_regmap_init(cdns_phy);
if (ret)
goto put_child;
ret = cdns_torrent_dp_regfield_init(cdns_phy);
if (ret)
goto put_child;
init_dp_regmap++;
}
dev_info(dev, "%d lanes, max bit rate %d.%03d Gbps\n",
cdns_phy->phys[node].num_lanes,
cdns_phy->max_bit_rate / 1000,
cdns_phy->max_bit_rate % 1000);
} else {
dev_err(dev, "Driver supports only PHY_TYPE_DP\n");
ret = -ENOTSUPP;
goto put_child;
gphy->attrs.bus_width = cdns_phy->phys[node].num_lanes;
gphy->attrs.max_link_rate = cdns_phy->max_bit_rate;
gphy->attrs.mode = PHY_MODE_DP;
}
cdns_phy->phys[node].phy = gphy;
phy_set_drvdata(gphy, &cdns_phy->phys[node]);
......@@ -1864,16 +2296,16 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev)
}
cdns_phy->nsubnodes = node;
ret = cdns_regmap_init_torrent_dp(cdns_phy, cdns_phy->sd_base,
cdns_phy->base,
data->block_offset_shift,
data->reg_offset_shift);
if (ret)
if (total_num_lanes > MAX_NUM_LANES) {
dev_err(dev, "Invalid lane configuration\n");
goto put_lnk_rst;
}
ret = cdns_regfield_init(cdns_phy);
if (ret)
goto put_lnk_rst;
if (cdns_phy->nsubnodes > 1) {
ret = cdns_torrent_phy_configure_multilink(cdns_phy);
if (ret)
goto put_lnk_rst;
}
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (IS_ERR(phy_provider)) {
......@@ -1889,6 +2321,8 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev)
for (i = 0; i < node; i++)
reset_control_put(cdns_phy->phys[i].lnk_rst);
of_node_put(child);
reset_control_assert(cdns_phy->apb_rst);
clk_disable_unprepare(cdns_phy->clk);
return ret;
}
......@@ -1898,22 +2332,1505 @@ static int cdns_torrent_phy_remove(struct platform_device *pdev)
int i;
reset_control_assert(cdns_phy->phy_rst);
reset_control_assert(cdns_phy->apb_rst);
for (i = 0; i < cdns_phy->nsubnodes; i++) {
reset_control_assert(cdns_phy->phys[i].lnk_rst);
reset_control_put(cdns_phy->phys[i].lnk_rst);
}
clk_disable_unprepare(cdns_phy->clk);
return 0;
}
/* USB and SGMII/QSGMII link configuration */
static struct cdns_reg_pairs usb_sgmii_link_cmn_regs[] = {
{0x0002, PHY_PLL_CFG},
{0x8600, CMN_PDIAG_PLL0_CLK_SEL_M0},
{0x0601, CMN_PDIAG_PLL1_CLK_SEL_M0}
};
static struct cdns_reg_pairs usb_sgmii_xcvr_diag_ln_regs[] = {
{0x0000, XCVR_DIAG_HSCLK_SEL},
{0x0001, XCVR_DIAG_HSCLK_DIV},
{0x0041, XCVR_DIAG_PLLDRC_CTRL}
};
static struct cdns_reg_pairs sgmii_usb_xcvr_diag_ln_regs[] = {
{0x0011, XCVR_DIAG_HSCLK_SEL},
{0x0003, XCVR_DIAG_HSCLK_DIV},
{0x009B, XCVR_DIAG_PLLDRC_CTRL}
};
static struct cdns_torrent_vals usb_sgmii_link_cmn_vals = {
.reg_pairs = usb_sgmii_link_cmn_regs,
.num_regs = ARRAY_SIZE(usb_sgmii_link_cmn_regs),
};
static struct cdns_torrent_vals usb_sgmii_xcvr_diag_ln_vals = {
.reg_pairs = usb_sgmii_xcvr_diag_ln_regs,
.num_regs = ARRAY_SIZE(usb_sgmii_xcvr_diag_ln_regs),
};
static struct cdns_torrent_vals sgmii_usb_xcvr_diag_ln_vals = {
.reg_pairs = sgmii_usb_xcvr_diag_ln_regs,
.num_regs = ARRAY_SIZE(sgmii_usb_xcvr_diag_ln_regs),
};
/* PCIe and USB Unique SSC link configuration */
static struct cdns_reg_pairs pcie_usb_link_cmn_regs[] = {
{0x0003, PHY_PLL_CFG},
{0x0601, CMN_PDIAG_PLL0_CLK_SEL_M0},
{0x0400, CMN_PDIAG_PLL0_CLK_SEL_M1},
{0x8600, CMN_PDIAG_PLL1_CLK_SEL_M0}
};
static struct cdns_reg_pairs pcie_usb_xcvr_diag_ln_regs[] = {
{0x0000, XCVR_DIAG_HSCLK_SEL},
{0x0001, XCVR_DIAG_HSCLK_DIV},
{0x0012, XCVR_DIAG_PLLDRC_CTRL}
};
static struct cdns_reg_pairs usb_pcie_xcvr_diag_ln_regs[] = {
{0x0011, XCVR_DIAG_HSCLK_SEL},
{0x0001, XCVR_DIAG_HSCLK_DIV},
{0x00C9, XCVR_DIAG_PLLDRC_CTRL}
};
static struct cdns_torrent_vals pcie_usb_link_cmn_vals = {
.reg_pairs = pcie_usb_link_cmn_regs,
.num_regs = ARRAY_SIZE(pcie_usb_link_cmn_regs),
};
static struct cdns_torrent_vals pcie_usb_xcvr_diag_ln_vals = {
.reg_pairs = pcie_usb_xcvr_diag_ln_regs,
.num_regs = ARRAY_SIZE(pcie_usb_xcvr_diag_ln_regs),
};
static struct cdns_torrent_vals usb_pcie_xcvr_diag_ln_vals = {
.reg_pairs = usb_pcie_xcvr_diag_ln_regs,
.num_regs = ARRAY_SIZE(usb_pcie_xcvr_diag_ln_regs),
};
/* USB 100 MHz Ref clk, internal SSC */
static struct cdns_reg_pairs usb_100_int_ssc_cmn_regs[] = {
{0x0004, CMN_PLL0_DSM_DIAG_M0},
{0x0004, CMN_PLL0_DSM_DIAG_M1},
{0x0004, CMN_PLL1_DSM_DIAG_M0},
{0x0509, CMN_PDIAG_PLL0_CP_PADJ_M0},
{0x0509, CMN_PDIAG_PLL0_CP_PADJ_M1},
{0x0509, CMN_PDIAG_PLL1_CP_PADJ_M0},
{0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M0},
{0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M1},
{0x0F00, CMN_PDIAG_PLL1_CP_IADJ_M0},
{0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M0},
{0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M1},
{0x0F08, CMN_PDIAG_PLL1_FILT_PADJ_M0},
{0x0064, CMN_PLL0_INTDIV_M0},
{0x0050, CMN_PLL0_INTDIV_M1},
{0x0064, CMN_PLL1_INTDIV_M0},
{0x0002, CMN_PLL0_FRACDIVH_M0},
{0x0002, CMN_PLL0_FRACDIVH_M1},
{0x0002, CMN_PLL1_FRACDIVH_M0},
{0x0044, CMN_PLL0_HIGH_THR_M0},
{0x0036, CMN_PLL0_HIGH_THR_M1},
{0x0044, CMN_PLL1_HIGH_THR_M0},
{0x0002, CMN_PDIAG_PLL0_CTRL_M0},
{0x0002, CMN_PDIAG_PLL0_CTRL_M1},
{0x0002, CMN_PDIAG_PLL1_CTRL_M0},
{0x0001, CMN_PLL0_SS_CTRL1_M0},
{0x0001, CMN_PLL0_SS_CTRL1_M1},
{0x0001, CMN_PLL1_SS_CTRL1_M0},
{0x011B, CMN_PLL0_SS_CTRL2_M0},
{0x011B, CMN_PLL0_SS_CTRL2_M1},
{0x011B, CMN_PLL1_SS_CTRL2_M0},
{0x006E, CMN_PLL0_SS_CTRL3_M0},
{0x0058, CMN_PLL0_SS_CTRL3_M1},
{0x006E, CMN_PLL1_SS_CTRL3_M0},
{0x000E, CMN_PLL0_SS_CTRL4_M0},
{0x0012, CMN_PLL0_SS_CTRL4_M1},
{0x000E, CMN_PLL1_SS_CTRL4_M0},
{0x0C5E, CMN_PLL0_VCOCAL_REFTIM_START},
{0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START},
{0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START},
{0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START},
{0x0003, CMN_PLL0_VCOCAL_TCTRL},
{0x0003, CMN_PLL1_VCOCAL_TCTRL},
{0x00C7, CMN_PLL0_LOCK_REFCNT_START},
{0x00C7, CMN_PLL1_LOCK_REFCNT_START},
{0x00C7, CMN_PLL0_LOCK_PLLCNT_START},
{0x00C7, CMN_PLL1_LOCK_PLLCNT_START},
{0x0005, CMN_PLL0_LOCK_PLLCNT_THR},
{0x0005, CMN_PLL1_LOCK_PLLCNT_THR},
{0x8200, CMN_CDIAG_CDB_PWRI_OVRD},
{0x8200, CMN_CDIAG_XCVRC_PWRI_OVRD}
};
static struct cdns_torrent_vals usb_100_int_ssc_cmn_vals = {
.reg_pairs = usb_100_int_ssc_cmn_regs,
.num_regs = ARRAY_SIZE(usb_100_int_ssc_cmn_regs),
};
/* Single USB link configuration */
static struct cdns_reg_pairs sl_usb_link_cmn_regs[] = {
{0x0000, PHY_PLL_CFG},
{0x8600, CMN_PDIAG_PLL0_CLK_SEL_M0}
};
static struct cdns_reg_pairs sl_usb_xcvr_diag_ln_regs[] = {
{0x0000, XCVR_DIAG_HSCLK_SEL},
{0x0001, XCVR_DIAG_HSCLK_DIV},
{0x0041, XCVR_DIAG_PLLDRC_CTRL}
};
static struct cdns_torrent_vals sl_usb_link_cmn_vals = {
.reg_pairs = sl_usb_link_cmn_regs,
.num_regs = ARRAY_SIZE(sl_usb_link_cmn_regs),
};
static struct cdns_torrent_vals sl_usb_xcvr_diag_ln_vals = {
.reg_pairs = sl_usb_xcvr_diag_ln_regs,
.num_regs = ARRAY_SIZE(sl_usb_xcvr_diag_ln_regs),
};
/* USB PHY PCS common configuration */
static struct cdns_reg_pairs usb_phy_pcs_cmn_regs[] = {
{0x0A0A, PHY_PIPE_USB3_GEN2_PRE_CFG0},
{0x1000, PHY_PIPE_USB3_GEN2_POST_CFG0},
{0x0010, PHY_PIPE_USB3_GEN2_POST_CFG1}
};
static struct cdns_torrent_vals usb_phy_pcs_cmn_vals = {
.reg_pairs = usb_phy_pcs_cmn_regs,
.num_regs = ARRAY_SIZE(usb_phy_pcs_cmn_regs),
};
/* USB 100 MHz Ref clk, no SSC */
static struct cdns_reg_pairs usb_100_no_ssc_cmn_regs[] = {
{0x0003, CMN_PLL0_VCOCAL_TCTRL},
{0x0003, CMN_PLL1_VCOCAL_TCTRL},
{0x8200, CMN_CDIAG_CDB_PWRI_OVRD},
{0x8200, CMN_CDIAG_XCVRC_PWRI_OVRD}
};
static struct cdns_reg_pairs usb_100_no_ssc_tx_ln_regs[] = {
{0x02FF, TX_PSC_A0},
{0x06AF, TX_PSC_A1},
{0x06AE, TX_PSC_A2},
{0x06AE, TX_PSC_A3},
{0x2A82, TX_TXCC_CTRL},
{0x0014, TX_TXCC_CPOST_MULT_01},
{0x0003, XCVR_DIAG_PSC_OVRD}
};
static struct cdns_reg_pairs usb_100_no_ssc_rx_ln_regs[] = {
{0x0D1D, RX_PSC_A0},
{0x0D1D, RX_PSC_A1},
{0x0D00, RX_PSC_A2},
{0x0500, RX_PSC_A3},
{0x0013, RX_SIGDET_HL_FILT_TMR},
{0x0000, RX_REE_GCSM1_CTRL},
{0x0C02, RX_REE_ATTEN_THR},
{0x0330, RX_REE_SMGM_CTRL1},
{0x0300, RX_REE_SMGM_CTRL2},
{0x0019, RX_REE_TAP1_CLIP},
{0x0019, RX_REE_TAP2TON_CLIP},
{0x1004, RX_DIAG_SIGDET_TUNE},
{0x00F9, RX_DIAG_NQST_CTRL},
{0x0C01, RX_DIAG_DFE_AMP_TUNE_2},
{0x0002, RX_DIAG_DFE_AMP_TUNE_3},
{0x0000, RX_DIAG_PI_CAP},
{0x0031, RX_DIAG_PI_RATE},
{0x0001, RX_DIAG_ACYA},
{0x018C, RX_CDRLF_CNFG},
{0x0003, RX_CDRLF_CNFG3}
};
static struct cdns_torrent_vals usb_100_no_ssc_cmn_vals = {
.reg_pairs = usb_100_no_ssc_cmn_regs,
.num_regs = ARRAY_SIZE(usb_100_no_ssc_cmn_regs),
};
static struct cdns_torrent_vals usb_100_no_ssc_tx_ln_vals = {
.reg_pairs = usb_100_no_ssc_tx_ln_regs,
.num_regs = ARRAY_SIZE(usb_100_no_ssc_tx_ln_regs),
};
static struct cdns_torrent_vals usb_100_no_ssc_rx_ln_vals = {
.reg_pairs = usb_100_no_ssc_rx_ln_regs,
.num_regs = ARRAY_SIZE(usb_100_no_ssc_rx_ln_regs),
};
/* Single link USB, 100 MHz Ref clk, internal SSC */
static struct cdns_reg_pairs sl_usb_100_int_ssc_cmn_regs[] = {
{0x0004, CMN_PLL0_DSM_DIAG_M0},
{0x0004, CMN_PLL1_DSM_DIAG_M0},
{0x0509, CMN_PDIAG_PLL0_CP_PADJ_M0},
{0x0509, CMN_PDIAG_PLL1_CP_PADJ_M0},
{0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M0},
{0x0F00, CMN_PDIAG_PLL1_CP_IADJ_M0},
{0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M0},
{0x0F08, CMN_PDIAG_PLL1_FILT_PADJ_M0},
{0x0064, CMN_PLL0_INTDIV_M0},
{0x0064, CMN_PLL1_INTDIV_M0},
{0x0002, CMN_PLL0_FRACDIVH_M0},
{0x0002, CMN_PLL1_FRACDIVH_M0},
{0x0044, CMN_PLL0_HIGH_THR_M0},
{0x0044, CMN_PLL1_HIGH_THR_M0},
{0x0002, CMN_PDIAG_PLL0_CTRL_M0},
{0x0002, CMN_PDIAG_PLL1_CTRL_M0},
{0x0001, CMN_PLL0_SS_CTRL1_M0},
{0x0001, CMN_PLL1_SS_CTRL1_M0},
{0x011B, CMN_PLL0_SS_CTRL2_M0},
{0x011B, CMN_PLL1_SS_CTRL2_M0},
{0x006E, CMN_PLL0_SS_CTRL3_M0},
{0x006E, CMN_PLL1_SS_CTRL3_M0},
{0x000E, CMN_PLL0_SS_CTRL4_M0},
{0x000E, CMN_PLL1_SS_CTRL4_M0},
{0x0C5E, CMN_PLL0_VCOCAL_REFTIM_START},
{0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START},
{0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START},
{0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START},
{0x0003, CMN_PLL0_VCOCAL_TCTRL},
{0x0003, CMN_PLL1_VCOCAL_TCTRL},
{0x00C7, CMN_PLL0_LOCK_REFCNT_START},
{0x00C7, CMN_PLL1_LOCK_REFCNT_START},
{0x00C7, CMN_PLL0_LOCK_PLLCNT_START},
{0x00C7, CMN_PLL1_LOCK_PLLCNT_START},
{0x0005, CMN_PLL0_LOCK_PLLCNT_THR},
{0x0005, CMN_PLL1_LOCK_PLLCNT_THR},
{0x8200, CMN_CDIAG_CDB_PWRI_OVRD},
{0x8200, CMN_CDIAG_XCVRC_PWRI_OVRD}
};
static struct cdns_torrent_vals sl_usb_100_int_ssc_cmn_vals = {
.reg_pairs = sl_usb_100_int_ssc_cmn_regs,
.num_regs = ARRAY_SIZE(sl_usb_100_int_ssc_cmn_regs),
};
/* PCIe and SGMII/QSGMII Unique SSC link configuration */
static struct cdns_reg_pairs pcie_sgmii_link_cmn_regs[] = {
{0x0003, PHY_PLL_CFG},
{0x0601, CMN_PDIAG_PLL0_CLK_SEL_M0},
{0x0400, CMN_PDIAG_PLL0_CLK_SEL_M1},
{0x0601, CMN_PDIAG_PLL1_CLK_SEL_M0}
};
static struct cdns_reg_pairs pcie_sgmii_xcvr_diag_ln_regs[] = {
{0x0000, XCVR_DIAG_HSCLK_SEL},
{0x0001, XCVR_DIAG_HSCLK_DIV},
{0x0012, XCVR_DIAG_PLLDRC_CTRL}
};
static struct cdns_reg_pairs sgmii_pcie_xcvr_diag_ln_regs[] = {
{0x0011, XCVR_DIAG_HSCLK_SEL},
{0x0003, XCVR_DIAG_HSCLK_DIV},
{0x009B, XCVR_DIAG_PLLDRC_CTRL}
};
static struct cdns_torrent_vals pcie_sgmii_link_cmn_vals = {
.reg_pairs = pcie_sgmii_link_cmn_regs,
.num_regs = ARRAY_SIZE(pcie_sgmii_link_cmn_regs),
};
static struct cdns_torrent_vals pcie_sgmii_xcvr_diag_ln_vals = {
.reg_pairs = pcie_sgmii_xcvr_diag_ln_regs,
.num_regs = ARRAY_SIZE(pcie_sgmii_xcvr_diag_ln_regs),
};
static struct cdns_torrent_vals sgmii_pcie_xcvr_diag_ln_vals = {
.reg_pairs = sgmii_pcie_xcvr_diag_ln_regs,
.num_regs = ARRAY_SIZE(sgmii_pcie_xcvr_diag_ln_regs),
};
/* SGMII 100 MHz Ref clk, no SSC */
static struct cdns_reg_pairs sgmii_100_no_ssc_cmn_regs[] = {
{0x0003, CMN_PLL0_VCOCAL_TCTRL},
{0x0003, CMN_PLL1_VCOCAL_TCTRL},
{0x3700, CMN_DIAG_BIAS_OVRD1},
{0x0008, CMN_TXPUCAL_TUNE},
{0x0008, CMN_TXPDCAL_TUNE}
};
static struct cdns_reg_pairs sgmii_100_no_ssc_tx_ln_regs[] = {
{0x00F3, TX_PSC_A0},
{0x04A2, TX_PSC_A2},
{0x04A2, TX_PSC_A3},
{0x0000, TX_TXCC_CPOST_MULT_00},
{0x00B3, DRV_DIAG_TX_DRV}
};
static struct cdns_reg_pairs sgmii_100_no_ssc_rx_ln_regs[] = {
{0x091D, RX_PSC_A0},
{0x0900, RX_PSC_A2},
{0x0100, RX_PSC_A3},
{0x03C7, RX_REE_GCSM1_EQENM_PH1},
{0x01C7, RX_REE_GCSM1_EQENM_PH2},
{0x0000, RX_DIAG_DFE_CTRL},
{0x0019, RX_REE_TAP1_CLIP},
{0x0019, RX_REE_TAP2TON_CLIP},
{0x0098, RX_DIAG_NQST_CTRL},
{0x0C01, RX_DIAG_DFE_AMP_TUNE_2},
{0x0000, RX_DIAG_DFE_AMP_TUNE_3},
{0x0000, RX_DIAG_PI_CAP},
{0x0010, RX_DIAG_PI_RATE},
{0x0001, RX_DIAG_ACYA},
{0x018C, RX_CDRLF_CNFG},
};
static struct cdns_torrent_vals sgmii_100_no_ssc_cmn_vals = {
.reg_pairs = sgmii_100_no_ssc_cmn_regs,
.num_regs = ARRAY_SIZE(sgmii_100_no_ssc_cmn_regs),
};
static struct cdns_torrent_vals sgmii_100_no_ssc_tx_ln_vals = {
.reg_pairs = sgmii_100_no_ssc_tx_ln_regs,
.num_regs = ARRAY_SIZE(sgmii_100_no_ssc_tx_ln_regs),
};
static struct cdns_torrent_vals sgmii_100_no_ssc_rx_ln_vals = {
.reg_pairs = sgmii_100_no_ssc_rx_ln_regs,
.num_regs = ARRAY_SIZE(sgmii_100_no_ssc_rx_ln_regs),
};
/* SGMII 100 MHz Ref clk, internal SSC */
static struct cdns_reg_pairs sgmii_100_int_ssc_cmn_regs[] = {
{0x0004, CMN_PLL0_DSM_DIAG_M0},
{0x0004, CMN_PLL0_DSM_DIAG_M1},
{0x0004, CMN_PLL1_DSM_DIAG_M0},
{0x0509, CMN_PDIAG_PLL0_CP_PADJ_M0},
{0x0509, CMN_PDIAG_PLL0_CP_PADJ_M1},
{0x0509, CMN_PDIAG_PLL1_CP_PADJ_M0},
{0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M0},
{0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M1},
{0x0F00, CMN_PDIAG_PLL1_CP_IADJ_M0},
{0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M0},
{0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M1},
{0x0F08, CMN_PDIAG_PLL1_FILT_PADJ_M0},
{0x0064, CMN_PLL0_INTDIV_M0},
{0x0050, CMN_PLL0_INTDIV_M1},
{0x0064, CMN_PLL1_INTDIV_M0},
{0x0002, CMN_PLL0_FRACDIVH_M0},
{0x0002, CMN_PLL0_FRACDIVH_M1},
{0x0002, CMN_PLL1_FRACDIVH_M0},
{0x0044, CMN_PLL0_HIGH_THR_M0},
{0x0036, CMN_PLL0_HIGH_THR_M1},
{0x0044, CMN_PLL1_HIGH_THR_M0},
{0x0002, CMN_PDIAG_PLL0_CTRL_M0},
{0x0002, CMN_PDIAG_PLL0_CTRL_M1},
{0x0002, CMN_PDIAG_PLL1_CTRL_M0},
{0x0001, CMN_PLL0_SS_CTRL1_M0},
{0x0001, CMN_PLL0_SS_CTRL1_M1},
{0x0001, CMN_PLL1_SS_CTRL1_M0},
{0x011B, CMN_PLL0_SS_CTRL2_M0},
{0x011B, CMN_PLL0_SS_CTRL2_M1},
{0x011B, CMN_PLL1_SS_CTRL2_M0},
{0x006E, CMN_PLL0_SS_CTRL3_M0},
{0x0058, CMN_PLL0_SS_CTRL3_M1},
{0x006E, CMN_PLL1_SS_CTRL3_M0},
{0x000E, CMN_PLL0_SS_CTRL4_M0},
{0x0012, CMN_PLL0_SS_CTRL4_M1},
{0x000E, CMN_PLL1_SS_CTRL4_M0},
{0x0C5E, CMN_PLL0_VCOCAL_REFTIM_START},
{0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START},
{0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START},
{0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START},
{0x0003, CMN_PLL0_VCOCAL_TCTRL},
{0x0003, CMN_PLL1_VCOCAL_TCTRL},
{0x00C7, CMN_PLL0_LOCK_REFCNT_START},
{0x00C7, CMN_PLL1_LOCK_REFCNT_START},
{0x00C7, CMN_PLL0_LOCK_PLLCNT_START},
{0x00C7, CMN_PLL1_LOCK_PLLCNT_START},
{0x0005, CMN_PLL0_LOCK_PLLCNT_THR},
{0x0005, CMN_PLL1_LOCK_PLLCNT_THR},
{0x3700, CMN_DIAG_BIAS_OVRD1},
{0x0008, CMN_TXPUCAL_TUNE},
{0x0008, CMN_TXPDCAL_TUNE}
};
static struct cdns_torrent_vals sgmii_100_int_ssc_cmn_vals = {
.reg_pairs = sgmii_100_int_ssc_cmn_regs,
.num_regs = ARRAY_SIZE(sgmii_100_int_ssc_cmn_regs),
};
/* QSGMII 100 MHz Ref clk, no SSC */
static struct cdns_reg_pairs qsgmii_100_no_ssc_cmn_regs[] = {
{0x0003, CMN_PLL0_VCOCAL_TCTRL},
{0x0003, CMN_PLL1_VCOCAL_TCTRL}
};
static struct cdns_reg_pairs qsgmii_100_no_ssc_tx_ln_regs[] = {
{0x00F3, TX_PSC_A0},
{0x04A2, TX_PSC_A2},
{0x04A2, TX_PSC_A3},
{0x0000, TX_TXCC_CPOST_MULT_00},
{0x0003, DRV_DIAG_TX_DRV}
};
static struct cdns_reg_pairs qsgmii_100_no_ssc_rx_ln_regs[] = {
{0x091D, RX_PSC_A0},
{0x0900, RX_PSC_A2},
{0x0100, RX_PSC_A3},
{0x03C7, RX_REE_GCSM1_EQENM_PH1},
{0x01C7, RX_REE_GCSM1_EQENM_PH2},
{0x0000, RX_DIAG_DFE_CTRL},
{0x0019, RX_REE_TAP1_CLIP},
{0x0019, RX_REE_TAP2TON_CLIP},
{0x0098, RX_DIAG_NQST_CTRL},
{0x0C01, RX_DIAG_DFE_AMP_TUNE_2},
{0x0000, RX_DIAG_DFE_AMP_TUNE_3},
{0x0000, RX_DIAG_PI_CAP},
{0x0010, RX_DIAG_PI_RATE},
{0x0001, RX_DIAG_ACYA},
{0x018C, RX_CDRLF_CNFG},
};
static struct cdns_torrent_vals qsgmii_100_no_ssc_cmn_vals = {
.reg_pairs = qsgmii_100_no_ssc_cmn_regs,
.num_regs = ARRAY_SIZE(qsgmii_100_no_ssc_cmn_regs),
};
static struct cdns_torrent_vals qsgmii_100_no_ssc_tx_ln_vals = {
.reg_pairs = qsgmii_100_no_ssc_tx_ln_regs,
.num_regs = ARRAY_SIZE(qsgmii_100_no_ssc_tx_ln_regs),
};
static struct cdns_torrent_vals qsgmii_100_no_ssc_rx_ln_vals = {
.reg_pairs = qsgmii_100_no_ssc_rx_ln_regs,
.num_regs = ARRAY_SIZE(qsgmii_100_no_ssc_rx_ln_regs),
};
/* QSGMII 100 MHz Ref clk, internal SSC */
static struct cdns_reg_pairs qsgmii_100_int_ssc_cmn_regs[] = {
{0x0004, CMN_PLL0_DSM_DIAG_M0},
{0x0004, CMN_PLL0_DSM_DIAG_M1},
{0x0004, CMN_PLL1_DSM_DIAG_M0},
{0x0509, CMN_PDIAG_PLL0_CP_PADJ_M0},
{0x0509, CMN_PDIAG_PLL0_CP_PADJ_M1},
{0x0509, CMN_PDIAG_PLL1_CP_PADJ_M0},
{0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M0},
{0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M1},
{0x0F00, CMN_PDIAG_PLL1_CP_IADJ_M0},
{0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M0},
{0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M1},
{0x0F08, CMN_PDIAG_PLL1_FILT_PADJ_M0},
{0x0064, CMN_PLL0_INTDIV_M0},
{0x0050, CMN_PLL0_INTDIV_M1},
{0x0064, CMN_PLL1_INTDIV_M0},
{0x0002, CMN_PLL0_FRACDIVH_M0},
{0x0002, CMN_PLL0_FRACDIVH_M1},
{0x0002, CMN_PLL1_FRACDIVH_M0},
{0x0044, CMN_PLL0_HIGH_THR_M0},
{0x0036, CMN_PLL0_HIGH_THR_M1},
{0x0044, CMN_PLL1_HIGH_THR_M0},
{0x0002, CMN_PDIAG_PLL0_CTRL_M0},
{0x0002, CMN_PDIAG_PLL0_CTRL_M1},
{0x0002, CMN_PDIAG_PLL1_CTRL_M0},
{0x0001, CMN_PLL0_SS_CTRL1_M0},
{0x0001, CMN_PLL0_SS_CTRL1_M1},
{0x0001, CMN_PLL1_SS_CTRL1_M0},
{0x011B, CMN_PLL0_SS_CTRL2_M0},
{0x011B, CMN_PLL0_SS_CTRL2_M1},
{0x011B, CMN_PLL1_SS_CTRL2_M0},
{0x006E, CMN_PLL0_SS_CTRL3_M0},
{0x0058, CMN_PLL0_SS_CTRL3_M1},
{0x006E, CMN_PLL1_SS_CTRL3_M0},
{0x000E, CMN_PLL0_SS_CTRL4_M0},
{0x0012, CMN_PLL0_SS_CTRL4_M1},
{0x000E, CMN_PLL1_SS_CTRL4_M0},
{0x0C5E, CMN_PLL0_VCOCAL_REFTIM_START},
{0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START},
{0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START},
{0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START},
{0x0003, CMN_PLL0_VCOCAL_TCTRL},
{0x0003, CMN_PLL1_VCOCAL_TCTRL},
{0x00C7, CMN_PLL0_LOCK_REFCNT_START},
{0x00C7, CMN_PLL1_LOCK_REFCNT_START},
{0x00C7, CMN_PLL0_LOCK_PLLCNT_START},
{0x00C7, CMN_PLL1_LOCK_PLLCNT_START},
{0x0005, CMN_PLL0_LOCK_PLLCNT_THR},
{0x0005, CMN_PLL1_LOCK_PLLCNT_THR}
};
static struct cdns_torrent_vals qsgmii_100_int_ssc_cmn_vals = {
.reg_pairs = qsgmii_100_int_ssc_cmn_regs,
.num_regs = ARRAY_SIZE(qsgmii_100_int_ssc_cmn_regs),
};
/* Single SGMII/QSGMII link configuration */
static struct cdns_reg_pairs sl_sgmii_link_cmn_regs[] = {
{0x0000, PHY_PLL_CFG},
{0x0601, CMN_PDIAG_PLL0_CLK_SEL_M0}
};
static struct cdns_reg_pairs sl_sgmii_xcvr_diag_ln_regs[] = {
{0x0000, XCVR_DIAG_HSCLK_SEL},
{0x0003, XCVR_DIAG_HSCLK_DIV},
{0x0013, XCVR_DIAG_PLLDRC_CTRL}
};
static struct cdns_torrent_vals sl_sgmii_link_cmn_vals = {
.reg_pairs = sl_sgmii_link_cmn_regs,
.num_regs = ARRAY_SIZE(sl_sgmii_link_cmn_regs),
};
static struct cdns_torrent_vals sl_sgmii_xcvr_diag_ln_vals = {
.reg_pairs = sl_sgmii_xcvr_diag_ln_regs,
.num_regs = ARRAY_SIZE(sl_sgmii_xcvr_diag_ln_regs),
};
/* Multi link PCIe, 100 MHz Ref clk, internal SSC */
static struct cdns_reg_pairs pcie_100_int_ssc_cmn_regs[] = {
{0x0004, CMN_PLL0_DSM_DIAG_M0},
{0x0004, CMN_PLL0_DSM_DIAG_M1},
{0x0004, CMN_PLL1_DSM_DIAG_M0},
{0x0509, CMN_PDIAG_PLL0_CP_PADJ_M0},
{0x0509, CMN_PDIAG_PLL0_CP_PADJ_M1},
{0x0509, CMN_PDIAG_PLL1_CP_PADJ_M0},
{0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M0},
{0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M1},
{0x0F00, CMN_PDIAG_PLL1_CP_IADJ_M0},
{0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M0},
{0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M1},
{0x0F08, CMN_PDIAG_PLL1_FILT_PADJ_M0},
{0x0064, CMN_PLL0_INTDIV_M0},
{0x0050, CMN_PLL0_INTDIV_M1},
{0x0064, CMN_PLL1_INTDIV_M0},
{0x0002, CMN_PLL0_FRACDIVH_M0},
{0x0002, CMN_PLL0_FRACDIVH_M1},
{0x0002, CMN_PLL1_FRACDIVH_M0},
{0x0044, CMN_PLL0_HIGH_THR_M0},
{0x0036, CMN_PLL0_HIGH_THR_M1},
{0x0044, CMN_PLL1_HIGH_THR_M0},
{0x0002, CMN_PDIAG_PLL0_CTRL_M0},
{0x0002, CMN_PDIAG_PLL0_CTRL_M1},
{0x0002, CMN_PDIAG_PLL1_CTRL_M0},
{0x0001, CMN_PLL0_SS_CTRL1_M0},
{0x0001, CMN_PLL0_SS_CTRL1_M1},
{0x0001, CMN_PLL1_SS_CTRL1_M0},
{0x011B, CMN_PLL0_SS_CTRL2_M0},
{0x011B, CMN_PLL0_SS_CTRL2_M1},
{0x011B, CMN_PLL1_SS_CTRL2_M0},
{0x006E, CMN_PLL0_SS_CTRL3_M0},
{0x0058, CMN_PLL0_SS_CTRL3_M1},
{0x006E, CMN_PLL1_SS_CTRL3_M0},
{0x000E, CMN_PLL0_SS_CTRL4_M0},
{0x0012, CMN_PLL0_SS_CTRL4_M1},
{0x000E, CMN_PLL1_SS_CTRL4_M0},
{0x0C5E, CMN_PLL0_VCOCAL_REFTIM_START},
{0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START},
{0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START},
{0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START},
{0x0003, CMN_PLL0_VCOCAL_TCTRL},
{0x0003, CMN_PLL1_VCOCAL_TCTRL},
{0x00C7, CMN_PLL0_LOCK_REFCNT_START},
{0x00C7, CMN_PLL1_LOCK_REFCNT_START},
{0x00C7, CMN_PLL0_LOCK_PLLCNT_START},
{0x00C7, CMN_PLL1_LOCK_PLLCNT_START},
{0x0005, CMN_PLL0_LOCK_PLLCNT_THR},
{0x0005, CMN_PLL1_LOCK_PLLCNT_THR}
};
static struct cdns_torrent_vals pcie_100_int_ssc_cmn_vals = {
.reg_pairs = pcie_100_int_ssc_cmn_regs,
.num_regs = ARRAY_SIZE(pcie_100_int_ssc_cmn_regs),
};
/* Single link PCIe, 100 MHz Ref clk, internal SSC */
static struct cdns_reg_pairs sl_pcie_100_int_ssc_cmn_regs[] = {
{0x0004, CMN_PLL0_DSM_DIAG_M0},
{0x0004, CMN_PLL0_DSM_DIAG_M1},
{0x0004, CMN_PLL1_DSM_DIAG_M0},
{0x0509, CMN_PDIAG_PLL0_CP_PADJ_M0},
{0x0509, CMN_PDIAG_PLL0_CP_PADJ_M1},
{0x0509, CMN_PDIAG_PLL1_CP_PADJ_M0},
{0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M0},
{0x0F00, CMN_PDIAG_PLL0_CP_IADJ_M1},
{0x0F00, CMN_PDIAG_PLL1_CP_IADJ_M0},
{0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M0},
{0x0F08, CMN_PDIAG_PLL0_FILT_PADJ_M1},
{0x0F08, CMN_PDIAG_PLL1_FILT_PADJ_M0},
{0x0064, CMN_PLL0_INTDIV_M0},
{0x0050, CMN_PLL0_INTDIV_M1},
{0x0050, CMN_PLL1_INTDIV_M0},
{0x0002, CMN_PLL0_FRACDIVH_M0},
{0x0002, CMN_PLL0_FRACDIVH_M1},
{0x0002, CMN_PLL1_FRACDIVH_M0},
{0x0044, CMN_PLL0_HIGH_THR_M0},
{0x0036, CMN_PLL0_HIGH_THR_M1},
{0x0036, CMN_PLL1_HIGH_THR_M0},
{0x0002, CMN_PDIAG_PLL0_CTRL_M0},
{0x0002, CMN_PDIAG_PLL0_CTRL_M1},
{0x0002, CMN_PDIAG_PLL1_CTRL_M0},
{0x0001, CMN_PLL0_SS_CTRL1_M0},
{0x0001, CMN_PLL0_SS_CTRL1_M1},
{0x0001, CMN_PLL1_SS_CTRL1_M0},
{0x011B, CMN_PLL0_SS_CTRL2_M0},
{0x011B, CMN_PLL0_SS_CTRL2_M1},
{0x011B, CMN_PLL1_SS_CTRL2_M0},
{0x006E, CMN_PLL0_SS_CTRL3_M0},
{0x0058, CMN_PLL0_SS_CTRL3_M1},
{0x0058, CMN_PLL1_SS_CTRL3_M0},
{0x000E, CMN_PLL0_SS_CTRL4_M0},
{0x0012, CMN_PLL0_SS_CTRL4_M1},
{0x0012, CMN_PLL1_SS_CTRL4_M0},
{0x0C5E, CMN_PLL0_VCOCAL_REFTIM_START},
{0x0C5E, CMN_PLL1_VCOCAL_REFTIM_START},
{0x0C56, CMN_PLL0_VCOCAL_PLLCNT_START},
{0x0C56, CMN_PLL1_VCOCAL_PLLCNT_START},
{0x0003, CMN_PLL0_VCOCAL_TCTRL},
{0x0003, CMN_PLL1_VCOCAL_TCTRL},
{0x00C7, CMN_PLL0_LOCK_REFCNT_START},
{0x00C7, CMN_PLL1_LOCK_REFCNT_START},
{0x00C7, CMN_PLL0_LOCK_PLLCNT_START},
{0x00C7, CMN_PLL1_LOCK_PLLCNT_START},
{0x0005, CMN_PLL0_LOCK_PLLCNT_THR},
{0x0005, CMN_PLL1_LOCK_PLLCNT_THR}
};
static struct cdns_torrent_vals sl_pcie_100_int_ssc_cmn_vals = {
.reg_pairs = sl_pcie_100_int_ssc_cmn_regs,
.num_regs = ARRAY_SIZE(sl_pcie_100_int_ssc_cmn_regs),
};
/* PCIe, 100 MHz Ref clk, no SSC & external SSC */
static struct cdns_reg_pairs pcie_100_ext_no_ssc_cmn_regs[] = {
{0x0003, CMN_PLL0_VCOCAL_TCTRL},
{0x0003, CMN_PLL1_VCOCAL_TCTRL}
};
static struct cdns_reg_pairs pcie_100_ext_no_ssc_rx_ln_regs[] = {
{0x0019, RX_REE_TAP1_CLIP},
{0x0019, RX_REE_TAP2TON_CLIP},
{0x0001, RX_DIAG_ACYA}
};
static struct cdns_torrent_vals pcie_100_no_ssc_cmn_vals = {
.reg_pairs = pcie_100_ext_no_ssc_cmn_regs,
.num_regs = ARRAY_SIZE(pcie_100_ext_no_ssc_cmn_regs),
};
static struct cdns_torrent_vals pcie_100_no_ssc_rx_ln_vals = {
.reg_pairs = pcie_100_ext_no_ssc_rx_ln_regs,
.num_regs = ARRAY_SIZE(pcie_100_ext_no_ssc_rx_ln_regs),
};
static const struct cdns_torrent_data cdns_map_torrent = {
.block_offset_shift = 0x2,
.reg_offset_shift = 0x2,
.link_cmn_vals = {
[TYPE_PCIE] = {
[TYPE_NONE] = {
[NO_SSC] = NULL,
[EXTERNAL_SSC] = NULL,
[INTERNAL_SSC] = NULL,
},
[TYPE_SGMII] = {
[NO_SSC] = &pcie_sgmii_link_cmn_vals,
[EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
[INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
},
[TYPE_QSGMII] = {
[NO_SSC] = &pcie_sgmii_link_cmn_vals,
[EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
[INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
},
[TYPE_USB] = {
[NO_SSC] = &pcie_usb_link_cmn_vals,
[EXTERNAL_SSC] = &pcie_usb_link_cmn_vals,
[INTERNAL_SSC] = &pcie_usb_link_cmn_vals,
},
},
[TYPE_SGMII] = {
[TYPE_NONE] = {
[NO_SSC] = &sl_sgmii_link_cmn_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &pcie_sgmii_link_cmn_vals,
[EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
[INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
},
[TYPE_USB] = {
[NO_SSC] = &usb_sgmii_link_cmn_vals,
[EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
[INTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
},
},
[TYPE_QSGMII] = {
[TYPE_NONE] = {
[NO_SSC] = &sl_sgmii_link_cmn_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &pcie_sgmii_link_cmn_vals,
[EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
[INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
},
[TYPE_USB] = {
[NO_SSC] = &usb_sgmii_link_cmn_vals,
[EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
[INTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
},
},
[TYPE_USB] = {
[TYPE_NONE] = {
[NO_SSC] = &sl_usb_link_cmn_vals,
[EXTERNAL_SSC] = &sl_usb_link_cmn_vals,
[INTERNAL_SSC] = &sl_usb_link_cmn_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &pcie_usb_link_cmn_vals,
[EXTERNAL_SSC] = &pcie_usb_link_cmn_vals,
[INTERNAL_SSC] = &pcie_usb_link_cmn_vals,
},
[TYPE_SGMII] = {
[NO_SSC] = &usb_sgmii_link_cmn_vals,
[EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
[INTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
},
[TYPE_QSGMII] = {
[NO_SSC] = &usb_sgmii_link_cmn_vals,
[EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
[INTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
},
},
},
.xcvr_diag_vals = {
[TYPE_PCIE] = {
[TYPE_NONE] = {
[NO_SSC] = NULL,
[EXTERNAL_SSC] = NULL,
[INTERNAL_SSC] = NULL,
},
[TYPE_SGMII] = {
[NO_SSC] = &pcie_sgmii_xcvr_diag_ln_vals,
[EXTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals,
[INTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals,
},
[TYPE_QSGMII] = {
[NO_SSC] = &pcie_sgmii_xcvr_diag_ln_vals,
[EXTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals,
[INTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals,
},
[TYPE_USB] = {
[NO_SSC] = &pcie_usb_xcvr_diag_ln_vals,
[EXTERNAL_SSC] = &pcie_usb_xcvr_diag_ln_vals,
[INTERNAL_SSC] = &pcie_usb_xcvr_diag_ln_vals,
},
},
[TYPE_SGMII] = {
[TYPE_NONE] = {
[NO_SSC] = &sl_sgmii_xcvr_diag_ln_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &sgmii_pcie_xcvr_diag_ln_vals,
[EXTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals,
[INTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals,
},
[TYPE_USB] = {
[NO_SSC] = &sgmii_usb_xcvr_diag_ln_vals,
[EXTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals,
[INTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals,
},
},
[TYPE_QSGMII] = {
[TYPE_NONE] = {
[NO_SSC] = &sl_sgmii_xcvr_diag_ln_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &sgmii_pcie_xcvr_diag_ln_vals,
[EXTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals,
[INTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals,
},
[TYPE_USB] = {
[NO_SSC] = &sgmii_usb_xcvr_diag_ln_vals,
[EXTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals,
[INTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals,
},
},
[TYPE_USB] = {
[TYPE_NONE] = {
[NO_SSC] = &sl_usb_xcvr_diag_ln_vals,
[EXTERNAL_SSC] = &sl_usb_xcvr_diag_ln_vals,
[INTERNAL_SSC] = &sl_usb_xcvr_diag_ln_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &usb_pcie_xcvr_diag_ln_vals,
[EXTERNAL_SSC] = &usb_pcie_xcvr_diag_ln_vals,
[INTERNAL_SSC] = &usb_pcie_xcvr_diag_ln_vals,
},
[TYPE_SGMII] = {
[NO_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
[EXTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
[INTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
},
[TYPE_QSGMII] = {
[NO_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
[EXTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
[INTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
},
},
},
.pcs_cmn_vals = {
[TYPE_USB] = {
[TYPE_NONE] = {
[NO_SSC] = &usb_phy_pcs_cmn_vals,
[EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
[INTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &usb_phy_pcs_cmn_vals,
[EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
[INTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
},
[TYPE_SGMII] = {
[NO_SSC] = &usb_phy_pcs_cmn_vals,
[EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
[INTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
},
[TYPE_QSGMII] = {
[NO_SSC] = &usb_phy_pcs_cmn_vals,
[EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
[INTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
},
},
},
.cmn_vals = {
[TYPE_PCIE] = {
[TYPE_NONE] = {
[NO_SSC] = &pcie_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &sl_pcie_100_int_ssc_cmn_vals,
},
[TYPE_SGMII] = {
[NO_SSC] = &pcie_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals,
},
[TYPE_QSGMII] = {
[NO_SSC] = &pcie_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals,
},
[TYPE_USB] = {
[NO_SSC] = &pcie_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals,
},
},
[TYPE_SGMII] = {
[TYPE_NONE] = {
[NO_SSC] = &sgmii_100_no_ssc_cmn_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &sgmii_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &sgmii_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &sgmii_100_int_ssc_cmn_vals,
},
[TYPE_USB] = {
[NO_SSC] = &sgmii_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &sgmii_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &sgmii_100_no_ssc_cmn_vals,
},
},
[TYPE_QSGMII] = {
[TYPE_NONE] = {
[NO_SSC] = &qsgmii_100_no_ssc_cmn_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &qsgmii_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &qsgmii_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &qsgmii_100_int_ssc_cmn_vals,
},
[TYPE_USB] = {
[NO_SSC] = &qsgmii_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &qsgmii_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &qsgmii_100_no_ssc_cmn_vals,
},
},
[TYPE_USB] = {
[TYPE_NONE] = {
[NO_SSC] = &usb_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &usb_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &usb_100_int_ssc_cmn_vals,
},
[TYPE_SGMII] = {
[NO_SSC] = &usb_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals,
},
[TYPE_QSGMII] = {
[NO_SSC] = &usb_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals,
},
},
},
.tx_ln_vals = {
[TYPE_PCIE] = {
[TYPE_NONE] = {
[NO_SSC] = NULL,
[EXTERNAL_SSC] = NULL,
[INTERNAL_SSC] = NULL,
},
[TYPE_SGMII] = {
[NO_SSC] = NULL,
[EXTERNAL_SSC] = NULL,
[INTERNAL_SSC] = NULL,
},
[TYPE_QSGMII] = {
[NO_SSC] = NULL,
[EXTERNAL_SSC] = NULL,
[INTERNAL_SSC] = NULL,
},
[TYPE_USB] = {
[NO_SSC] = NULL,
[EXTERNAL_SSC] = NULL,
[INTERNAL_SSC] = NULL,
},
},
[TYPE_SGMII] = {
[TYPE_NONE] = {
[NO_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
[EXTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
[INTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
},
[TYPE_USB] = {
[NO_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
[EXTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
[INTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
},
},
[TYPE_QSGMII] = {
[TYPE_NONE] = {
[NO_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
[EXTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
[INTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
},
[TYPE_USB] = {
[NO_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
[EXTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
[INTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
},
},
[TYPE_USB] = {
[TYPE_NONE] = {
[NO_SSC] = &usb_100_no_ssc_tx_ln_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
[INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &usb_100_no_ssc_tx_ln_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
[INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
},
[TYPE_SGMII] = {
[NO_SSC] = &usb_100_no_ssc_tx_ln_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
[INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
},
[TYPE_QSGMII] = {
[NO_SSC] = &usb_100_no_ssc_tx_ln_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
[INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
},
},
},
.rx_ln_vals = {
[TYPE_PCIE] = {
[TYPE_NONE] = {
[NO_SSC] = &pcie_100_no_ssc_rx_ln_vals,
[EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
},
[TYPE_SGMII] = {
[NO_SSC] = &pcie_100_no_ssc_rx_ln_vals,
[EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
},
[TYPE_QSGMII] = {
[NO_SSC] = &pcie_100_no_ssc_rx_ln_vals,
[EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
},
[TYPE_USB] = {
[NO_SSC] = &pcie_100_no_ssc_rx_ln_vals,
[EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
},
},
[TYPE_SGMII] = {
[TYPE_NONE] = {
[NO_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
[EXTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
},
[TYPE_USB] = {
[NO_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
[EXTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
},
},
[TYPE_QSGMII] = {
[TYPE_NONE] = {
[NO_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
[EXTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
},
[TYPE_USB] = {
[NO_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
[EXTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
},
},
[TYPE_USB] = {
[TYPE_NONE] = {
[NO_SSC] = &usb_100_no_ssc_rx_ln_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &usb_100_no_ssc_rx_ln_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
},
[TYPE_SGMII] = {
[NO_SSC] = &usb_100_no_ssc_rx_ln_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
},
[TYPE_QSGMII] = {
[NO_SSC] = &usb_100_no_ssc_rx_ln_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
},
},
},
};
static const struct cdns_torrent_data ti_j721e_map_torrent = {
.block_offset_shift = 0x0,
.reg_offset_shift = 0x1,
.link_cmn_vals = {
[TYPE_PCIE] = {
[TYPE_NONE] = {
[NO_SSC] = NULL,
[EXTERNAL_SSC] = NULL,
[INTERNAL_SSC] = NULL,
},
[TYPE_SGMII] = {
[NO_SSC] = &pcie_sgmii_link_cmn_vals,
[EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
[INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
},
[TYPE_QSGMII] = {
[NO_SSC] = &pcie_sgmii_link_cmn_vals,
[EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
[INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
},
[TYPE_USB] = {
[NO_SSC] = &pcie_usb_link_cmn_vals,
[EXTERNAL_SSC] = &pcie_usb_link_cmn_vals,
[INTERNAL_SSC] = &pcie_usb_link_cmn_vals,
},
},
[TYPE_SGMII] = {
[TYPE_NONE] = {
[NO_SSC] = &sl_sgmii_link_cmn_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &pcie_sgmii_link_cmn_vals,
[EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
[INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
},
[TYPE_USB] = {
[NO_SSC] = &usb_sgmii_link_cmn_vals,
[EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
[INTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
},
},
[TYPE_QSGMII] = {
[TYPE_NONE] = {
[NO_SSC] = &sl_sgmii_link_cmn_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &pcie_sgmii_link_cmn_vals,
[EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
[INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals,
},
[TYPE_USB] = {
[NO_SSC] = &usb_sgmii_link_cmn_vals,
[EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
[INTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
},
},
[TYPE_USB] = {
[TYPE_NONE] = {
[NO_SSC] = &sl_usb_link_cmn_vals,
[EXTERNAL_SSC] = &sl_usb_link_cmn_vals,
[INTERNAL_SSC] = &sl_usb_link_cmn_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &pcie_usb_link_cmn_vals,
[EXTERNAL_SSC] = &pcie_usb_link_cmn_vals,
[INTERNAL_SSC] = &pcie_usb_link_cmn_vals,
},
[TYPE_SGMII] = {
[NO_SSC] = &usb_sgmii_link_cmn_vals,
[EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
[INTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
},
[TYPE_QSGMII] = {
[NO_SSC] = &usb_sgmii_link_cmn_vals,
[EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
[INTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
},
},
},
.xcvr_diag_vals = {
[TYPE_PCIE] = {
[TYPE_NONE] = {
[NO_SSC] = NULL,
[EXTERNAL_SSC] = NULL,
[INTERNAL_SSC] = NULL,
},
[TYPE_SGMII] = {
[NO_SSC] = &pcie_sgmii_xcvr_diag_ln_vals,
[EXTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals,
[INTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals,
},
[TYPE_QSGMII] = {
[NO_SSC] = &pcie_sgmii_xcvr_diag_ln_vals,
[EXTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals,
[INTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals,
},
[TYPE_USB] = {
[NO_SSC] = &pcie_usb_xcvr_diag_ln_vals,
[EXTERNAL_SSC] = &pcie_usb_xcvr_diag_ln_vals,
[INTERNAL_SSC] = &pcie_usb_xcvr_diag_ln_vals,
},
},
[TYPE_SGMII] = {
[TYPE_NONE] = {
[NO_SSC] = &sl_sgmii_xcvr_diag_ln_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &sgmii_pcie_xcvr_diag_ln_vals,
[EXTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals,
[INTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals,
},
[TYPE_USB] = {
[NO_SSC] = &sgmii_usb_xcvr_diag_ln_vals,
[EXTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals,
[INTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals,
},
},
[TYPE_QSGMII] = {
[TYPE_NONE] = {
[NO_SSC] = &sl_sgmii_xcvr_diag_ln_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &sgmii_pcie_xcvr_diag_ln_vals,
[EXTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals,
[INTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals,
},
[TYPE_USB] = {
[NO_SSC] = &sgmii_usb_xcvr_diag_ln_vals,
[EXTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals,
[INTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals,
},
},
[TYPE_USB] = {
[TYPE_NONE] = {
[NO_SSC] = &sl_usb_xcvr_diag_ln_vals,
[EXTERNAL_SSC] = &sl_usb_xcvr_diag_ln_vals,
[INTERNAL_SSC] = &sl_usb_xcvr_diag_ln_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &usb_pcie_xcvr_diag_ln_vals,
[EXTERNAL_SSC] = &usb_pcie_xcvr_diag_ln_vals,
[INTERNAL_SSC] = &usb_pcie_xcvr_diag_ln_vals,
},
[TYPE_SGMII] = {
[NO_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
[EXTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
[INTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
},
[TYPE_QSGMII] = {
[NO_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
[EXTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
[INTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
},
},
},
.pcs_cmn_vals = {
[TYPE_USB] = {
[TYPE_NONE] = {
[NO_SSC] = &usb_phy_pcs_cmn_vals,
[EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
[INTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &usb_phy_pcs_cmn_vals,
[EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
[INTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
},
[TYPE_SGMII] = {
[NO_SSC] = &usb_phy_pcs_cmn_vals,
[EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
[INTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
},
[TYPE_QSGMII] = {
[NO_SSC] = &usb_phy_pcs_cmn_vals,
[EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
[INTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
},
},
},
.cmn_vals = {
[TYPE_PCIE] = {
[TYPE_NONE] = {
[NO_SSC] = &pcie_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &sl_pcie_100_int_ssc_cmn_vals,
},
[TYPE_SGMII] = {
[NO_SSC] = &pcie_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals,
},
[TYPE_QSGMII] = {
[NO_SSC] = &pcie_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals,
},
[TYPE_USB] = {
[NO_SSC] = &pcie_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals,
},
},
[TYPE_SGMII] = {
[TYPE_NONE] = {
[NO_SSC] = &sgmii_100_no_ssc_cmn_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &sgmii_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &sgmii_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &sgmii_100_int_ssc_cmn_vals,
},
[TYPE_USB] = {
[NO_SSC] = &sgmii_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &sgmii_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &sgmii_100_no_ssc_cmn_vals,
},
},
[TYPE_QSGMII] = {
[TYPE_NONE] = {
[NO_SSC] = &qsgmii_100_no_ssc_cmn_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &qsgmii_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &qsgmii_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &qsgmii_100_int_ssc_cmn_vals,
},
[TYPE_USB] = {
[NO_SSC] = &qsgmii_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &qsgmii_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &qsgmii_100_no_ssc_cmn_vals,
},
},
[TYPE_USB] = {
[TYPE_NONE] = {
[NO_SSC] = &usb_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &usb_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &usb_100_int_ssc_cmn_vals,
},
[TYPE_SGMII] = {
[NO_SSC] = &usb_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals,
},
[TYPE_QSGMII] = {
[NO_SSC] = &usb_100_no_ssc_cmn_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals,
},
},
},
.tx_ln_vals = {
[TYPE_PCIE] = {
[TYPE_NONE] = {
[NO_SSC] = NULL,
[EXTERNAL_SSC] = NULL,
[INTERNAL_SSC] = NULL,
},
[TYPE_SGMII] = {
[NO_SSC] = NULL,
[EXTERNAL_SSC] = NULL,
[INTERNAL_SSC] = NULL,
},
[TYPE_QSGMII] = {
[NO_SSC] = NULL,
[EXTERNAL_SSC] = NULL,
[INTERNAL_SSC] = NULL,
},
[TYPE_USB] = {
[NO_SSC] = NULL,
[EXTERNAL_SSC] = NULL,
[INTERNAL_SSC] = NULL,
},
},
[TYPE_SGMII] = {
[TYPE_NONE] = {
[NO_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
[EXTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
[INTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
},
[TYPE_USB] = {
[NO_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
[EXTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
[INTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals,
},
},
[TYPE_QSGMII] = {
[TYPE_NONE] = {
[NO_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
[EXTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
[INTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
},
[TYPE_USB] = {
[NO_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
[EXTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
[INTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals,
},
},
[TYPE_USB] = {
[TYPE_NONE] = {
[NO_SSC] = &usb_100_no_ssc_tx_ln_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
[INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &usb_100_no_ssc_tx_ln_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
[INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
},
[TYPE_SGMII] = {
[NO_SSC] = &usb_100_no_ssc_tx_ln_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
[INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
},
[TYPE_QSGMII] = {
[NO_SSC] = &usb_100_no_ssc_tx_ln_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
[INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
},
},
},
.rx_ln_vals = {
[TYPE_PCIE] = {
[TYPE_NONE] = {
[NO_SSC] = &pcie_100_no_ssc_rx_ln_vals,
[EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
},
[TYPE_SGMII] = {
[NO_SSC] = &pcie_100_no_ssc_rx_ln_vals,
[EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
},
[TYPE_QSGMII] = {
[NO_SSC] = &pcie_100_no_ssc_rx_ln_vals,
[EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
},
[TYPE_USB] = {
[NO_SSC] = &pcie_100_no_ssc_rx_ln_vals,
[EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
},
},
[TYPE_SGMII] = {
[TYPE_NONE] = {
[NO_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
[EXTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
},
[TYPE_USB] = {
[NO_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
[EXTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals,
},
},
[TYPE_QSGMII] = {
[TYPE_NONE] = {
[NO_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
[EXTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
},
[TYPE_USB] = {
[NO_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
[EXTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals,
},
},
[TYPE_USB] = {
[TYPE_NONE] = {
[NO_SSC] = &usb_100_no_ssc_rx_ln_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
},
[TYPE_PCIE] = {
[NO_SSC] = &usb_100_no_ssc_rx_ln_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
},
[TYPE_SGMII] = {
[NO_SSC] = &usb_100_no_ssc_rx_ln_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
},
[TYPE_QSGMII] = {
[NO_SSC] = &usb_100_no_ssc_rx_ln_vals,
[EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
},
},
},
};
static const struct of_device_id cdns_torrent_phy_of_match[] = {
......
// SPDX-License-Identifier: GPL-2.0+
/* Copyright (c) 2017 NXP. */
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#define PHY_CTRL0 0x0
#define PHY_CTRL0_REF_SSP_EN BIT(2)
#define PHY_CTRL0_FSEL_MASK GENMASK(10, 5)
#define PHY_CTRL0_FSEL_24M 0x2a
#define PHY_CTRL1 0x4
#define PHY_CTRL1_RESET BIT(0)
......@@ -20,6 +25,11 @@
#define PHY_CTRL2 0x8
#define PHY_CTRL2_TXENABLEN0 BIT(8)
#define PHY_CTRL2_OTG_DISABLE BIT(9)
#define PHY_CTRL6 0x18
#define PHY_CTRL6_ALT_CLK_EN BIT(1)
#define PHY_CTRL6_ALT_CLK_SEL BIT(0)
struct imx8mq_usb_phy {
struct phy *phy;
......@@ -54,6 +64,44 @@ static int imx8mq_usb_phy_init(struct phy *phy)
return 0;
}
static int imx8mp_usb_phy_init(struct phy *phy)
{
struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);
u32 value;
/* USB3.0 PHY signal fsel for 24M ref */
value = readl(imx_phy->base + PHY_CTRL0);
value &= ~PHY_CTRL0_FSEL_MASK;
value |= FIELD_PREP(PHY_CTRL0_FSEL_MASK, PHY_CTRL0_FSEL_24M);
writel(value, imx_phy->base + PHY_CTRL0);
/* Disable alt_clk_en and use internal MPLL clocks */
value = readl(imx_phy->base + PHY_CTRL6);
value &= ~(PHY_CTRL6_ALT_CLK_SEL | PHY_CTRL6_ALT_CLK_EN);
writel(value, imx_phy->base + PHY_CTRL6);
value = readl(imx_phy->base + PHY_CTRL1);
value &= ~(PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0);
value |= PHY_CTRL1_RESET | PHY_CTRL1_ATERESET;
writel(value, imx_phy->base + PHY_CTRL1);
value = readl(imx_phy->base + PHY_CTRL0);
value |= PHY_CTRL0_REF_SSP_EN;
writel(value, imx_phy->base + PHY_CTRL0);
value = readl(imx_phy->base + PHY_CTRL2);
value |= PHY_CTRL2_TXENABLEN0 | PHY_CTRL2_OTG_DISABLE;
writel(value, imx_phy->base + PHY_CTRL2);
udelay(10);
value = readl(imx_phy->base + PHY_CTRL1);
value &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET);
writel(value, imx_phy->base + PHY_CTRL1);
return 0;
}
static int imx8mq_phy_power_on(struct phy *phy)
{
struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);
......@@ -76,19 +124,36 @@ static int imx8mq_phy_power_off(struct phy *phy)
return 0;
}
static struct phy_ops imx8mq_usb_phy_ops = {
static const struct phy_ops imx8mq_usb_phy_ops = {
.init = imx8mq_usb_phy_init,
.power_on = imx8mq_phy_power_on,
.power_off = imx8mq_phy_power_off,
.owner = THIS_MODULE,
};
static struct phy_ops imx8mp_usb_phy_ops = {
.init = imx8mp_usb_phy_init,
.power_on = imx8mq_phy_power_on,
.power_off = imx8mq_phy_power_off,
.owner = THIS_MODULE,
};
static const struct of_device_id imx8mq_usb_phy_of_match[] = {
{.compatible = "fsl,imx8mq-usb-phy",
.data = &imx8mq_usb_phy_ops,},
{.compatible = "fsl,imx8mp-usb-phy",
.data = &imx8mp_usb_phy_ops,},
{ }
};
MODULE_DEVICE_TABLE(of, imx8mq_usb_phy_of_match);
static int imx8mq_usb_phy_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;
struct device *dev = &pdev->dev;
struct imx8mq_usb_phy *imx_phy;
struct resource *res;
const struct phy_ops *phy_ops;
imx_phy = devm_kzalloc(dev, sizeof(*imx_phy), GFP_KERNEL);
if (!imx_phy)
......@@ -105,7 +170,11 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev)
if (IS_ERR(imx_phy->base))
return PTR_ERR(imx_phy->base);
imx_phy->phy = devm_phy_create(dev, NULL, &imx8mq_usb_phy_ops);
phy_ops = of_device_get_match_data(dev);
if (!phy_ops)
return -EINVAL;
imx_phy->phy = devm_phy_create(dev, NULL, phy_ops);
if (IS_ERR(imx_phy->phy))
return PTR_ERR(imx_phy->phy);
......@@ -120,12 +189,6 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev)
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct of_device_id imx8mq_usb_phy_of_match[] = {
{.compatible = "fsl,imx8mq-usb-phy",},
{ },
};
MODULE_DEVICE_TABLE(of, imx8mq_usb_phy_of_match);
static struct platform_driver imx8mq_usb_phy_driver = {
.probe = imx8mq_usb_phy_probe,
.driver = {
......
......@@ -161,7 +161,7 @@ static int hi3660_phy_exit(struct phy *phy)
return ret;
}
static struct phy_ops hi3660_phy_ops = {
static const struct phy_ops hi3660_phy_ops = {
.init = hi3660_phy_init,
.exit = hi3660_phy_exit,
.owner = THIS_MODULE,
......
# SPDX-License-Identifier: GPL-2.0
#
# Phy drivers for Intel Lightning Mountain(LGM) platform
# Phy drivers for Intel platforms
#
config PHY_INTEL_COMBO
bool "Intel ComboPHY driver"
config PHY_INTEL_KEEMBAY_EMMC
tristate "Intel Keem Bay EMMC PHY driver"
depends on (OF && ARM64) || COMPILE_TEST
depends on HAS_IOMEM
select GENERIC_PHY
select REGMAP_MMIO
help
Choose this option if you have an Intel Keem Bay SoC.
To compile this driver as a module, choose M here: the module
will be called phy-keembay-emmc.ko.
config PHY_INTEL_LGM_COMBO
bool "Intel Lightning Mountain ComboPHY driver"
depends on X86 || COMPILE_TEST
depends on OF && HAS_IOMEM
select MFD_SYSCON
......@@ -16,8 +28,8 @@ config PHY_INTEL_COMBO
chipsets which provides PHYs for various controllers, EMAC,
SATA and PCIe.
config PHY_INTEL_EMMC
tristate "Intel EMMC PHY driver"
config PHY_INTEL_LGM_EMMC
tristate "Intel Lightning Mountain EMMC PHY driver"
depends on X86 || COMPILE_TEST
select GENERIC_PHY
help
......
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_PHY_INTEL_COMBO) += phy-intel-combo.o
obj-$(CONFIG_PHY_INTEL_EMMC) += phy-intel-emmc.o
obj-$(CONFIG_PHY_INTEL_KEEMBAY_EMMC) += phy-intel-keembay-emmc.o
obj-$(CONFIG_PHY_INTEL_LGM_COMBO) += phy-intel-lgm-combo.o
obj-$(CONFIG_PHY_INTEL_LGM_EMMC) += phy-intel-lgm-emmc.o
// SPDX-License-Identifier: GPL-2.0-only
/*
* Intel Keem Bay eMMC PHY driver
* Copyright (C) 2020 Intel Corporation
*/
#include <linux/bitfield.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/SD/SDIO core/phy configuration registers */
#define PHY_CFG_0 0x24
#define SEL_DLY_TXCLK_MASK BIT(29)
#define OTAP_DLY_ENA_MASK BIT(27)
#define OTAP_DLY_SEL_MASK GENMASK(26, 23)
#define DLL_EN_MASK BIT(10)
#define PWR_DOWN_MASK BIT(0)
#define PHY_CFG_2 0x2c
#define SEL_FREQ_MASK GENMASK(12, 10)
#define PHY_STAT 0x40
#define CAL_DONE_MASK BIT(6)
#define IS_CALDONE(x) ((x) & CAL_DONE_MASK)
#define DLL_RDY_MASK BIT(5)
#define IS_DLLRDY(x) ((x) & DLL_RDY_MASK)
/* From ACS_eMMC51_16nFFC_RO1100_Userguide_v1p0.pdf p17 */
#define FREQSEL_200M_170M 0x0
#define FREQSEL_170M_140M 0x1
#define FREQSEL_140M_110M 0x2
#define FREQSEL_110M_80M 0x3
#define FREQSEL_80M_50M 0x4
struct keembay_emmc_phy {
struct regmap *syscfg;
struct clk *emmcclk;
};
static const struct regmap_config keembay_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
};
static int keembay_emmc_phy_power(struct phy *phy, bool on_off)
{
struct keembay_emmc_phy *priv = phy_get_drvdata(phy);
unsigned int caldone;
unsigned int dllrdy;
unsigned int freqsel;
unsigned int mhz;
int ret;
/*
* Keep phyctrl_pdb and phyctrl_endll low to allow
* initialization of CALIO state M/C DFFs
*/
ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, PWR_DOWN_MASK,
FIELD_PREP(PWR_DOWN_MASK, 0));
if (ret) {
dev_err(&phy->dev, "CALIO power down bar failed: %d\n", ret);
return ret;
}
ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, DLL_EN_MASK,
FIELD_PREP(DLL_EN_MASK, 0));
if (ret) {
dev_err(&phy->dev, "turn off the dll failed: %d\n", ret);
return ret;
}
/* Already finish power off above */
if (!on_off)
return 0;
mhz = DIV_ROUND_CLOSEST(clk_get_rate(priv->emmcclk), 1000000);
if (mhz <= 200 && mhz >= 170)
freqsel = FREQSEL_200M_170M;
else if (mhz <= 170 && mhz >= 140)
freqsel = FREQSEL_170M_140M;
else if (mhz <= 140 && mhz >= 110)
freqsel = FREQSEL_140M_110M;
else if (mhz <= 110 && mhz >= 80)
freqsel = FREQSEL_110M_80M;
else if (mhz <= 80 && mhz >= 50)
freqsel = FREQSEL_80M_50M;
else
freqsel = 0x0;
if (mhz < 50 || mhz > 200)
dev_warn(&phy->dev, "Unsupported rate: %d MHz\n", mhz);
/*
* 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, PHY_CFG_0, PWR_DOWN_MASK,
FIELD_PREP(PWR_DOWN_MASK, 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, PHY_STAT,
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, PHY_CFG_2, SEL_FREQ_MASK,
FIELD_PREP(SEL_FREQ_MASK, 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, PHY_CFG_0, DLL_EN_MASK,
FIELD_PREP(DLL_EN_MASK, 1));
if (ret) {
dev_err(&phy->dev, "turn on the dll failed: %d\n", ret);
return ret;
}
/*
* We turned on the DLL even though the rate was 0 because we the
* clock might be turned on later. ...but we can't wait for the DLL
* to lock when the rate is 0 because it will never lock with no
* input clock.
*
* Technically we should be checking the lock later when the clock
* is turned on, but for now we won't.
*/
if (mhz == 0)
return 0;
/*
* 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 100kHz) 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, PHY_STAT,
dllrdy, IS_DLLRDY(dllrdy),
0, 50 * USEC_PER_MSEC);
if (ret)
dev_err(&phy->dev, "dllrdy failed, ret=%d\n", ret);
return ret;
}
static int keembay_emmc_phy_init(struct phy *phy)
{
struct keembay_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");
return PTR_ERR_OR_ZERO(priv->emmcclk);
}
static int keembay_emmc_phy_exit(struct phy *phy)
{
struct keembay_emmc_phy *priv = phy_get_drvdata(phy);
clk_put(priv->emmcclk);
return 0;
};
static int keembay_emmc_phy_power_on(struct phy *phy)
{
struct keembay_emmc_phy *priv = phy_get_drvdata(phy);
int ret;
/* Delay chain based txclk: enable */
ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, SEL_DLY_TXCLK_MASK,
FIELD_PREP(SEL_DLY_TXCLK_MASK, 1));
if (ret) {
dev_err(&phy->dev, "ERROR: delay chain txclk set: %d\n", ret);
return ret;
}
/* Output tap delay: enable */
ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, OTAP_DLY_ENA_MASK,
FIELD_PREP(OTAP_DLY_ENA_MASK, 1));
if (ret) {
dev_err(&phy->dev, "ERROR: output tap delay set: %d\n", ret);
return ret;
}
/* Output tap delay */
ret = regmap_update_bits(priv->syscfg, PHY_CFG_0, OTAP_DLY_SEL_MASK,
FIELD_PREP(OTAP_DLY_SEL_MASK, 2));
if (ret) {
dev_err(&phy->dev, "ERROR: output tap delay select: %d\n", ret);
return ret;
}
/* Power up eMMC phy analog blocks */
return keembay_emmc_phy_power(phy, true);
}
static int keembay_emmc_phy_power_off(struct phy *phy)
{
/* Power down eMMC phy analog blocks */
return keembay_emmc_phy_power(phy, false);
}
static const struct phy_ops ops = {
.init = keembay_emmc_phy_init,
.exit = keembay_emmc_phy_exit,
.power_on = keembay_emmc_phy_power_on,
.power_off = keembay_emmc_phy_power_off,
.owner = THIS_MODULE,
};
static int keembay_emmc_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct keembay_emmc_phy *priv;
struct phy *generic_phy;
struct phy_provider *phy_provider;
void __iomem *base;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
priv->syscfg = devm_regmap_init_mmio(dev, base, &keembay_regmap_config);
if (IS_ERR(priv->syscfg))
return PTR_ERR(priv->syscfg);
generic_phy = devm_phy_create(dev, np, &ops);
if (IS_ERR(generic_phy))
return dev_err_probe(dev, PTR_ERR(generic_phy),
"failed to create PHY\n");
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 keembay_emmc_phy_dt_ids[] = {
{ .compatible = "intel,keembay-emmc-phy" },
{}
};
MODULE_DEVICE_TABLE(of, keembay_emmc_phy_dt_ids);
static struct platform_driver keembay_emmc_phy_driver = {
.probe = keembay_emmc_phy_probe,
.driver = {
.name = "keembay-emmc-phy",
.of_match_table = keembay_emmc_phy_dt_ids,
},
};
module_platform_driver(keembay_emmc_phy_driver);
MODULE_AUTHOR("Wan Ahmad Zainie <wan.ahmad.zainie.wan.mohamad@intel.com>");
MODULE_DESCRIPTION("Intel Keem Bay eMMC PHY driver");
MODULE_LICENSE("GPL v2");
......@@ -141,7 +141,7 @@ static int ltq_rcu_usb2_phy_power_off(struct phy *phy)
return 0;
}
static struct phy_ops ltq_rcu_usb2_phy_ops = {
static const struct phy_ops ltq_rcu_usb2_phy_ops = {
.init = ltq_rcu_usb2_phy_init,
.power_on = ltq_rcu_usb2_phy_power_on,
.power_off = ltq_rcu_usb2_phy_power_off,
......
......@@ -349,7 +349,7 @@ static int ltq_vrx200_pcie_phy_power_off(struct phy *phy)
return 0;
}
static struct phy_ops ltq_vrx200_pcie_phy_ops = {
static const struct phy_ops ltq_vrx200_pcie_phy_ops = {
.init = ltq_vrx200_pcie_phy_init,
.exit = ltq_vrx200_pcie_phy_exit,
.power_on = ltq_vrx200_pcie_phy_power_on,
......
......@@ -12,6 +12,7 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/module.h>
......@@ -44,15 +45,12 @@ struct mv_hsic_phy {
struct clk *clk;
};
static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout)
static int wait_for_reg(void __iomem *reg, u32 mask, u32 ms)
{
timeout += jiffies;
while (time_is_after_eq_jiffies(timeout)) {
if ((readl(reg) & mask) == mask)
return true;
msleep(1);
}
return false;
u32 val;
return readl_poll_timeout(reg, val, ((val & mask) == mask),
1000, 1000 * ms);
}
static int mv_hsic_phy_init(struct phy *phy)
......@@ -60,6 +58,7 @@ static int mv_hsic_phy_init(struct phy *phy)
struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy);
struct platform_device *pdev = mv_phy->pdev;
void __iomem *base = mv_phy->base;
int ret;
clk_prepare_enable(mv_phy->clk);
......@@ -75,14 +74,14 @@ static int mv_hsic_phy_init(struct phy *phy)
base + PHY_28NM_HSIC_PLL_CTRL2);
/* Make sure PHY PLL is locked */
if (!wait_for_reg(base + PHY_28NM_HSIC_PLL_CTRL2,
PHY_28NM_HSIC_H2S_PLL_LOCK, HZ / 10)) {
ret = wait_for_reg(base + PHY_28NM_HSIC_PLL_CTRL2,
PHY_28NM_HSIC_H2S_PLL_LOCK, 100);
if (ret) {
dev_err(&pdev->dev, "HSIC PHY PLL not locked after 100mS.");
clk_disable_unprepare(mv_phy->clk);
return -ETIMEDOUT;
}
return 0;
return ret;
}
static int mv_hsic_phy_power_on(struct phy *phy)
......@@ -91,6 +90,7 @@ static int mv_hsic_phy_power_on(struct phy *phy)
struct platform_device *pdev = mv_phy->pdev;
void __iomem *base = mv_phy->base;
u32 reg;
int ret;
reg = readl(base + PHY_28NM_HSIC_CTRL);
/* Avoid SE0 state when resume for some device will take it as reset */
......@@ -108,20 +108,20 @@ static int mv_hsic_phy_power_on(struct phy *phy)
*/
/* Make sure PHY Calibration is ready */
if (!wait_for_reg(base + PHY_28NM_HSIC_IMPCAL_CAL,
PHY_28NM_HSIC_H2S_IMPCAL_DONE, HZ / 10)) {
ret = wait_for_reg(base + PHY_28NM_HSIC_IMPCAL_CAL,
PHY_28NM_HSIC_H2S_IMPCAL_DONE, 100);
if (ret) {
dev_warn(&pdev->dev, "HSIC PHY READY not set after 100mS.");
return -ETIMEDOUT;
return ret;
}
/* Waiting for HSIC connect int*/
if (!wait_for_reg(base + PHY_28NM_HSIC_INT,
PHY_28NM_HSIC_CONNECT_INT, HZ / 5)) {
ret = wait_for_reg(base + PHY_28NM_HSIC_INT,
PHY_28NM_HSIC_CONNECT_INT, 200);
if (ret)
dev_warn(&pdev->dev, "HSIC wait for connect interrupt timeout.");
return -ETIMEDOUT;
}
return 0;
return ret;
}
static int mv_hsic_phy_power_off(struct phy *phy)
......
......@@ -13,6 +13,7 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/module.h>
......@@ -138,15 +139,12 @@ struct mv_usb2_phy {
struct clk *clk;
};
static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout)
static int wait_for_reg(void __iomem *reg, u32 mask, u32 ms)
{
timeout += jiffies;
while (time_is_after_eq_jiffies(timeout)) {
if ((readl(reg) & mask) == mask)
return true;
msleep(1);
}
return false;
u32 val;
return readl_poll_timeout(reg, val, ((val & mask) == mask),
1000, 1000 * ms);
}
static int mv_usb2_phy_28nm_init(struct phy *phy)
......@@ -208,24 +206,23 @@ static int mv_usb2_phy_28nm_init(struct phy *phy)
*/
/* Make sure PHY Calibration is ready */
if (!wait_for_reg(base + PHY_28NM_CAL_REG,
PHY_28NM_PLL_PLLCAL_DONE | PHY_28NM_PLL_IMPCAL_DONE,
HZ / 10)) {
ret = wait_for_reg(base + PHY_28NM_CAL_REG,
PHY_28NM_PLL_PLLCAL_DONE | PHY_28NM_PLL_IMPCAL_DONE,
100);
if (ret) {
dev_warn(&pdev->dev, "USB PHY PLL calibrate not done after 100mS.");
ret = -ETIMEDOUT;
goto err_clk;
}
if (!wait_for_reg(base + PHY_28NM_RX_REG1,
PHY_28NM_RX_SQCAL_DONE, HZ / 10)) {
ret = wait_for_reg(base + PHY_28NM_RX_REG1,
PHY_28NM_RX_SQCAL_DONE, 100);
if (ret) {
dev_warn(&pdev->dev, "USB PHY RX SQ calibrate not done after 100mS.");
ret = -ETIMEDOUT;
goto err_clk;
}
/* Make sure PHY PLL is ready */
if (!wait_for_reg(base + PHY_28NM_PLL_REG0,
PHY_28NM_PLL_READY, HZ / 10)) {
ret = wait_for_reg(base + PHY_28NM_PLL_REG0, PHY_28NM_PLL_READY, 100);
if (ret) {
dev_warn(&pdev->dev, "PLL_READY not set after 100mS.");
ret = -ETIMEDOUT;
goto err_clk;
}
......
// SPDX-License-Identifier: GPL-2.0
/*
* Intel LGM USB PHY driver
*
* Copyright (C) 2020 Intel Corporation.
*/
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/usb/phy.h>
#include <linux/workqueue.h>
#define CTRL1_OFFSET 0x14
#define SRAM_EXT_LD_DONE BIT(25)
#define SRAM_INIT_DONE BIT(26)
#define TCPC_OFFSET 0x1014
#define TCPC_MUX_CTL GENMASK(1, 0)
#define MUX_NC 0
#define MUX_USB 1
#define MUX_DP 2
#define MUX_USBDP 3
#define TCPC_FLIPPED BIT(2)
#define TCPC_LOW_POWER_EN BIT(3)
#define TCPC_VALID BIT(4)
#define TCPC_CONN \
(TCPC_VALID | FIELD_PREP(TCPC_MUX_CTL, MUX_USB))
#define TCPC_DISCONN \
(TCPC_VALID | FIELD_PREP(TCPC_MUX_CTL, MUX_NC) | TCPC_LOW_POWER_EN)
static const char *const PHY_RESETS[] = { "phy31", "phy", };
static const char *const CTL_RESETS[] = { "apb", "ctrl", };
struct tca_apb {
struct reset_control *resets[ARRAY_SIZE(PHY_RESETS)];
struct regulator *vbus;
struct work_struct wk;
struct usb_phy phy;
bool regulator_enabled;
bool phy_initialized;
bool connected;
};
static int get_flipped(struct tca_apb *ta, bool *flipped)
{
union extcon_property_value property;
int ret;
ret = extcon_get_property(ta->phy.edev, EXTCON_USB_HOST,
EXTCON_PROP_USB_TYPEC_POLARITY, &property);
if (ret) {
dev_err(ta->phy.dev, "no polarity property from extcon\n");
return ret;
}
*flipped = property.intval;
return 0;
}
static int phy_init(struct usb_phy *phy)
{
struct tca_apb *ta = container_of(phy, struct tca_apb, phy);
void __iomem *ctrl1 = phy->io_priv + CTRL1_OFFSET;
int val, ret, i;
if (ta->phy_initialized)
return 0;
for (i = 0; i < ARRAY_SIZE(PHY_RESETS); i++)
reset_control_deassert(ta->resets[i]);
ret = readl_poll_timeout(ctrl1, val, val & SRAM_INIT_DONE, 10, 10 * 1000);
if (ret) {
dev_err(ta->phy.dev, "SRAM init failed, 0x%x\n", val);
return ret;
}
writel(readl(ctrl1) | SRAM_EXT_LD_DONE, ctrl1);
ta->phy_initialized = true;
if (!ta->phy.edev) {
writel(TCPC_CONN, ta->phy.io_priv + TCPC_OFFSET);
return phy->set_vbus(phy, true);
}
schedule_work(&ta->wk);
return ret;
}
static void phy_shutdown(struct usb_phy *phy)
{
struct tca_apb *ta = container_of(phy, struct tca_apb, phy);
int i;
if (!ta->phy_initialized)
return;
ta->phy_initialized = false;
flush_work(&ta->wk);
ta->phy.set_vbus(&ta->phy, false);
ta->connected = false;
writel(TCPC_DISCONN, ta->phy.io_priv + TCPC_OFFSET);
for (i = 0; i < ARRAY_SIZE(PHY_RESETS); i++)
reset_control_assert(ta->resets[i]);
}
static int phy_set_vbus(struct usb_phy *phy, int on)
{
struct tca_apb *ta = container_of(phy, struct tca_apb, phy);
int ret;
if (!!on == ta->regulator_enabled)
return 0;
if (on)
ret = regulator_enable(ta->vbus);
else
ret = regulator_disable(ta->vbus);
if (!ret)
ta->regulator_enabled = on;
dev_dbg(ta->phy.dev, "set vbus: %d\n", on);
return ret;
}
static void tca_work(struct work_struct *work)
{
struct tca_apb *ta = container_of(work, struct tca_apb, wk);
bool connected;
bool flipped = false;
u32 val;
int ret;
ret = get_flipped(ta, &flipped);
if (ret)
return;
connected = extcon_get_state(ta->phy.edev, EXTCON_USB_HOST);
if (connected == ta->connected)
return;
ta->connected = connected;
if (connected) {
val = TCPC_CONN;
if (flipped)
val |= TCPC_FLIPPED;
dev_dbg(ta->phy.dev, "connected%s\n", flipped ? " flipped" : "");
} else {
val = TCPC_DISCONN;
dev_dbg(ta->phy.dev, "disconnected\n");
}
writel(val, ta->phy.io_priv + TCPC_OFFSET);
ret = ta->phy.set_vbus(&ta->phy, connected);
if (ret)
dev_err(ta->phy.dev, "failed to set VBUS\n");
}
static int id_notifier(struct notifier_block *nb, unsigned long event, void *ptr)
{
struct tca_apb *ta = container_of(nb, struct tca_apb, phy.id_nb);
if (ta->phy_initialized)
schedule_work(&ta->wk);
return NOTIFY_DONE;
}
static int vbus_notifier(struct notifier_block *nb, unsigned long evnt, void *ptr)
{
return NOTIFY_DONE;
}
static int phy_probe(struct platform_device *pdev)
{
struct reset_control *resets[ARRAY_SIZE(CTL_RESETS)];
struct device *dev = &pdev->dev;
struct usb_phy *phy;
struct tca_apb *ta;
int i;
ta = devm_kzalloc(dev, sizeof(*ta), GFP_KERNEL);
if (!ta)
return -ENOMEM;
platform_set_drvdata(pdev, ta);
INIT_WORK(&ta->wk, tca_work);
phy = &ta->phy;
phy->dev = dev;
phy->label = dev_name(dev);
phy->type = USB_PHY_TYPE_USB3;
phy->init = phy_init;
phy->shutdown = phy_shutdown;
phy->set_vbus = phy_set_vbus;
phy->id_nb.notifier_call = id_notifier;
phy->vbus_nb.notifier_call = vbus_notifier;
phy->io_priv = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(phy->io_priv))
return PTR_ERR(phy->io_priv);
ta->vbus = devm_regulator_get(dev, "vbus");
if (IS_ERR(ta->vbus))
return PTR_ERR(ta->vbus);
for (i = 0; i < ARRAY_SIZE(CTL_RESETS); i++) {
resets[i] = devm_reset_control_get_exclusive(dev, CTL_RESETS[i]);
if (IS_ERR(resets[i])) {
dev_err(dev, "%s reset not found\n", CTL_RESETS[i]);
return PTR_ERR(resets[i]);
}
}
for (i = 0; i < ARRAY_SIZE(PHY_RESETS); i++) {
ta->resets[i] = devm_reset_control_get_exclusive(dev, PHY_RESETS[i]);
if (IS_ERR(ta->resets[i])) {
dev_err(dev, "%s reset not found\n", PHY_RESETS[i]);
return PTR_ERR(ta->resets[i]);
}
}
for (i = 0; i < ARRAY_SIZE(CTL_RESETS); i++)
reset_control_assert(resets[i]);
for (i = 0; i < ARRAY_SIZE(PHY_RESETS); i++)
reset_control_assert(ta->resets[i]);
/*
* Out-of-band reset of the controller after PHY reset will cause
* controller malfunctioning, so we should use in-band controller
* reset only and leave the controller de-asserted here.
*/
for (i = 0; i < ARRAY_SIZE(CTL_RESETS); i++)
reset_control_deassert(resets[i]);
/* Need to wait at least 20us after de-assert the controller */
usleep_range(20, 100);
return usb_add_phy_dev(phy);
}
static int phy_remove(struct platform_device *pdev)
{
struct tca_apb *ta = platform_get_drvdata(pdev);
usb_remove_phy(&ta->phy);
return 0;
}
static const struct of_device_id intel_usb_phy_dt_ids[] = {
{ .compatible = "intel,lgm-usb-phy" },
{ }
};
MODULE_DEVICE_TABLE(of, intel_usb_phy_dt_ids);
static struct platform_driver lgm_phy_driver = {
.driver = {
.name = "lgm-usb-phy",
.of_match_table = intel_usb_phy_dt_ids,
},
.probe = phy_probe,
.remove = phy_remove,
};
module_platform_driver(lgm_phy_driver);
MODULE_DESCRIPTION("Intel LGM USB PHY driver");
MODULE_AUTHOR("Li Yin <yin1.li@intel.com>");
MODULE_AUTHOR("Vadivel Murugan R <vadivel.muruganx.ramuthevar@linux.intel.com>");
MODULE_LICENSE("GPL v2");
......@@ -4,6 +4,7 @@
*/
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
......@@ -72,18 +73,12 @@ struct qcom_apq8064_sata_phy {
};
/* Helper function to do poll and timeout */
static int read_poll_timeout(void __iomem *addr, u32 mask)
static int poll_timeout(void __iomem *addr, u32 mask)
{
unsigned long timeout = jiffies + msecs_to_jiffies(TIMEOUT_MS);
u32 val;
do {
if (readl_relaxed(addr) & mask)
return 0;
usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50);
} while (!time_after(jiffies, timeout));
return (readl_relaxed(addr) & mask) ? 0 : -ETIMEDOUT;
return readl_relaxed_poll_timeout(addr, val, (val & mask),
DELAY_INTERVAL_US, TIMEOUT_MS * 1000);
}
static int qcom_apq8064_sata_phy_init(struct phy *generic_phy)
......@@ -137,21 +132,21 @@ static int qcom_apq8064_sata_phy_init(struct phy *generic_phy)
writel_relaxed(0x05, base + UNIPHY_PLL_LKDET_CFG2);
/* PLL Lock wait */
ret = read_poll_timeout(base + UNIPHY_PLL_STATUS, UNIPHY_PLL_LOCK);
ret = poll_timeout(base + UNIPHY_PLL_STATUS, UNIPHY_PLL_LOCK);
if (ret) {
dev_err(phy->dev, "poll timeout UNIPHY_PLL_STATUS\n");
return ret;
}
/* TX Calibration */
ret = read_poll_timeout(base + SATA_PHY_TX_IMCAL_STAT, SATA_PHY_TX_CAL);
ret = poll_timeout(base + SATA_PHY_TX_IMCAL_STAT, SATA_PHY_TX_CAL);
if (ret) {
dev_err(phy->dev, "poll timeout SATA_PHY_TX_IMCAL_STAT\n");
return ret;
}
/* RX Calibration */
ret = read_poll_timeout(base + SATA_PHY_RX_IMCAL_STAT, SATA_PHY_RX_CAL);
ret = poll_timeout(base + SATA_PHY_RX_IMCAL_STAT, SATA_PHY_RX_CAL);
if (ret) {
dev_err(phy->dev, "poll timeout SATA_PHY_RX_IMCAL_STAT\n");
return ret;
......
......@@ -48,7 +48,7 @@ static int ipq4019_ss_phy_power_on(struct phy *_phy)
return 0;
}
static struct phy_ops ipq4019_usb_ss_phy_ops = {
static const struct phy_ops ipq4019_usb_ss_phy_ops = {
.power_on = ipq4019_ss_phy_power_on,
.power_off = ipq4019_ss_phy_power_off,
};
......@@ -80,7 +80,7 @@ static int ipq4019_hs_phy_power_on(struct phy *_phy)
return 0;
}
static struct phy_ops ipq4019_usb_hs_phy_ops = {
static const struct phy_ops ipq4019_usb_hs_phy_ops = {
.power_on = ipq4019_hs_phy_power_on,
.power_off = ipq4019_hs_phy_power_off,
};
......
......@@ -946,6 +946,88 @@ static const struct qmp_phy_init_tbl qmp_v3_usb3_tx_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x06),
};
static const struct qmp_phy_init_tbl qmp_v3_dp_serdes_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x37),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_ENABLE1, 0x0e),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x02),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x0a),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_CTRL, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x3f),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x1f),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06),
};
static const struct qmp_phy_init_tbl qmp_v3_dp_serdes_tbl_rbr[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x0c),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x69),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0x80),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x07),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x6f),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x08),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x00),
};
static const struct qmp_phy_init_tbl qmp_v3_dp_serdes_tbl_hbr[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x04),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x69),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0x80),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x07),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x0f),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x0e),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x00),
};
static const struct qmp_phy_init_tbl qmp_v3_dp_serdes_tbl_hbr2[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x8c),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x0a),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x1f),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x1c),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x00),
};
static const struct qmp_phy_init_tbl qmp_v3_dp_serdes_tbl_hbr3[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x03),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x69),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0x80),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x07),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x2f),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x2a),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x08),
};
static const struct qmp_phy_init_tbl qmp_v3_dp_tx_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_TX_TRANSCEIVER_BIAS_EN, 0x1a),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_VMODE_CTRL1, 0x40),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_PRE_STALL_LDO_BOOST_EN, 0x30),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_INTERFACE_SELECT, 0x3d),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_CLKBUF_ENABLE, 0x0f),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_RESET_TSYNC_EN, 0x03),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_TRAN_DRVR_EMP_EN, 0x03),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_PARRATE_REC_DETECT_IDLE_EN, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_TX_INTERFACE_MODE, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_TX_BAND, 0x4),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_TX_POL_INV, 0x0a),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_TX_DRV_LVL, 0x38),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_TX_EMP_POST1_LVL, 0x20),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x07),
};
static const struct qmp_phy_init_tbl qmp_v3_usb3_rx_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f),
......@@ -1761,6 +1843,16 @@ struct qmp_phy_cfg {
const struct qmp_phy_init_tbl *pcs_misc_tbl;
int pcs_misc_tbl_num;
/* Init sequence for DP PHY block link rates */
const struct qmp_phy_init_tbl *serdes_tbl_rbr;
int serdes_tbl_rbr_num;
const struct qmp_phy_init_tbl *serdes_tbl_hbr;
int serdes_tbl_hbr_num;
const struct qmp_phy_init_tbl *serdes_tbl_hbr2;
int serdes_tbl_hbr2_num;
const struct qmp_phy_init_tbl *serdes_tbl_hbr3;
int serdes_tbl_hbr3_num;
/* clock ids to be requested */
const char * const *clk_list;
int num_clks;
......@@ -1797,10 +1889,17 @@ struct qmp_phy_cfg {
bool no_pcs_sw_reset;
};
struct qmp_phy_combo_cfg {
const struct qmp_phy_cfg *usb_cfg;
const struct qmp_phy_cfg *dp_cfg;
};
/**
* struct qmp_phy - per-lane phy descriptor
*
* @phy: generic phy
* @cfg: phy specific configuration
* @serdes: iomapped memory space for phy's serdes (i.e. PLL)
* @tx: iomapped memory space for lane's tx
* @rx: iomapped memory space for lane's rx
* @pcs: iomapped memory space for lane's pcs
......@@ -1811,9 +1910,12 @@ struct qmp_phy_cfg {
* @index: lane index
* @qmp: QMP phy to which this lane belongs
* @lane_rst: lane's reset controller
* @mode: current PHY mode
*/
struct qmp_phy {
struct phy *phy;
const struct qmp_phy_cfg *cfg;
void __iomem *serdes;
void __iomem *tx;
void __iomem *rx;
void __iomem *pcs;
......@@ -1824,43 +1926,45 @@ struct qmp_phy {
unsigned int index;
struct qcom_qmp *qmp;
struct reset_control *lane_rst;
enum phy_mode mode;
unsigned int dp_aux_cfg;
struct phy_configure_opts_dp dp_opts;
struct qmp_phy_dp_clks *dp_clks;
};
struct qmp_phy_dp_clks {
struct qmp_phy *qphy;
struct clk_hw dp_link_hw;
struct clk_hw dp_pixel_hw;
};
/**
* struct qcom_qmp - structure holding QMP phy block attributes
*
* @dev: device
* @serdes: iomapped memory space for phy's serdes
* @dp_com: iomapped memory space for phy's dp_com control block
*
* @clks: array of clocks required by phy
* @resets: array of resets required by phy
* @vregs: regulator supplies bulk data
*
* @cfg: phy specific configuration
* @phys: array of per-lane phy descriptors
* @phy_mutex: mutex lock for PHY common block initialization
* @init_count: phy common block initialization count
* @phy_initialized: indicate if PHY has been initialized
* @mode: current PHY mode
* @ufs_reset: optional UFS PHY reset handle
*/
struct qcom_qmp {
struct device *dev;
void __iomem *serdes;
void __iomem *dp_com;
struct clk_bulk_data *clks;
struct reset_control **resets;
struct regulator_bulk_data *vregs;
const struct qmp_phy_cfg *cfg;
struct qmp_phy **phys;
struct mutex phy_mutex;
int init_count;
bool phy_initialized;
enum phy_mode mode;
struct reset_control *ufs_reset;
};
......@@ -2203,6 +2307,41 @@ static const struct qmp_phy_cfg sc7180_usb3phy_cfg = {
.is_dual_lane_phy = true,
};
static const struct qmp_phy_cfg sc7180_dpphy_cfg = {
.type = PHY_TYPE_DP,
.nlanes = 1,
.serdes_tbl = qmp_v3_dp_serdes_tbl,
.serdes_tbl_num = ARRAY_SIZE(qmp_v3_dp_serdes_tbl),
.tx_tbl = qmp_v3_dp_tx_tbl,
.tx_tbl_num = ARRAY_SIZE(qmp_v3_dp_tx_tbl),
.serdes_tbl_rbr = qmp_v3_dp_serdes_tbl_rbr,
.serdes_tbl_rbr_num = ARRAY_SIZE(qmp_v3_dp_serdes_tbl_rbr),
.serdes_tbl_hbr = qmp_v3_dp_serdes_tbl_hbr,
.serdes_tbl_hbr_num = ARRAY_SIZE(qmp_v3_dp_serdes_tbl_hbr),
.serdes_tbl_hbr2 = qmp_v3_dp_serdes_tbl_hbr2,
.serdes_tbl_hbr2_num = ARRAY_SIZE(qmp_v3_dp_serdes_tbl_hbr2),
.serdes_tbl_hbr3 = qmp_v3_dp_serdes_tbl_hbr3,
.serdes_tbl_hbr3_num = ARRAY_SIZE(qmp_v3_dp_serdes_tbl_hbr3),
.clk_list = qmp_v3_phy_clk_l,
.num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l),
.reset_list = sc7180_usb3phy_reset_l,
.num_resets = ARRAY_SIZE(sc7180_usb3phy_reset_l),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
.regs = qmp_v3_usb3phy_regs_layout,
.has_phy_dp_com_ctrl = true,
.is_dual_lane_phy = true,
};
static const struct qmp_phy_combo_cfg sc7180_usb3dpphy_cfg = {
.usb_cfg = &sc7180_usb3phy_cfg,
.dp_cfg = &sc7180_dpphy_cfg,
};
static const struct qmp_phy_cfg qmp_v3_usb3_uniphy_cfg = {
.type = PHY_TYPE_USB3,
.nlanes = 1,
......@@ -2479,11 +2618,300 @@ static void qcom_qmp_phy_configure(void __iomem *base,
qcom_qmp_phy_configure_lane(base, regs, tbl, num, 0xff);
}
static int qcom_qmp_phy_serdes_init(struct qmp_phy *qphy)
{
struct qcom_qmp *qmp = qphy->qmp;
const struct qmp_phy_cfg *cfg = qphy->cfg;
void __iomem *serdes = qphy->serdes;
const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts;
const struct qmp_phy_init_tbl *serdes_tbl = cfg->serdes_tbl;
int serdes_tbl_num = cfg->serdes_tbl_num;
int ret;
qcom_qmp_phy_configure(serdes, cfg->regs, serdes_tbl, serdes_tbl_num);
if (cfg->type == PHY_TYPE_DP) {
switch (dp_opts->link_rate) {
case 1620:
qcom_qmp_phy_configure(serdes, cfg->regs,
cfg->serdes_tbl_rbr,
cfg->serdes_tbl_rbr_num);
break;
case 2700:
qcom_qmp_phy_configure(serdes, cfg->regs,
cfg->serdes_tbl_hbr,
cfg->serdes_tbl_hbr_num);
break;
case 5400:
qcom_qmp_phy_configure(serdes, cfg->regs,
cfg->serdes_tbl_hbr2,
cfg->serdes_tbl_hbr2_num);
break;
case 8100:
qcom_qmp_phy_configure(serdes, cfg->regs,
cfg->serdes_tbl_hbr3,
cfg->serdes_tbl_hbr3_num);
break;
default:
/* Other link rates aren't supported */
return -EINVAL;
}
}
if (cfg->has_phy_com_ctrl) {
void __iomem *status;
unsigned int mask, val;
qphy_clrbits(serdes, cfg->regs[QPHY_COM_SW_RESET], SW_RESET);
qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL],
SERDES_START | PCS_START);
status = serdes + cfg->regs[QPHY_COM_PCS_READY_STATUS];
mask = cfg->mask_com_pcs_ready;
ret = readl_poll_timeout(status, val, (val & mask), 10,
PHY_INIT_COMPLETE_TIMEOUT);
if (ret) {
dev_err(qmp->dev,
"phy common block init timed-out\n");
return ret;
}
}
return 0;
}
static void qcom_qmp_phy_dp_aux_init(struct qmp_phy *qphy)
{
writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN |
DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN,
qphy->pcs + QSERDES_V3_DP_PHY_PD_CTL);
/* Turn on BIAS current for PHY/PLL */
writel(QSERDES_V3_COM_BIAS_EN | QSERDES_V3_COM_BIAS_EN_MUX |
QSERDES_V3_COM_CLKBUF_L_EN | QSERDES_V3_COM_EN_SYSCLK_TX_SEL,
qphy->serdes + QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN);
writel(DP_PHY_PD_CTL_PSR_PWRDN, qphy->pcs + QSERDES_V3_DP_PHY_PD_CTL);
writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN |
DP_PHY_PD_CTL_LANE_0_1_PWRDN |
DP_PHY_PD_CTL_LANE_2_3_PWRDN | DP_PHY_PD_CTL_PLL_PWRDN |
DP_PHY_PD_CTL_DP_CLAMP_EN,
qphy->pcs + QSERDES_V3_DP_PHY_PD_CTL);
writel(QSERDES_V3_COM_BIAS_EN |
QSERDES_V3_COM_BIAS_EN_MUX | QSERDES_V3_COM_CLKBUF_R_EN |
QSERDES_V3_COM_CLKBUF_L_EN | QSERDES_V3_COM_EN_SYSCLK_TX_SEL |
QSERDES_V3_COM_CLKBUF_RX_DRIVE_L,
qphy->serdes + QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN);
writel(0x00, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG0);
writel(0x13, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG1);
writel(0x24, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG2);
writel(0x00, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG3);
writel(0x0a, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG4);
writel(0x26, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG5);
writel(0x0a, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG6);
writel(0x03, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG7);
writel(0xbb, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG8);
writel(0x03, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG9);
qphy->dp_aux_cfg = 0;
writel(PHY_AUX_STOP_ERR_MASK | PHY_AUX_DEC_ERR_MASK |
PHY_AUX_SYNC_ERR_MASK | PHY_AUX_ALIGN_ERR_MASK |
PHY_AUX_REQ_ERR_MASK,
qphy->pcs + QSERDES_V3_DP_PHY_AUX_INTERRUPT_MASK);
}
static const u8 qmp_dp_v3_pre_emphasis_hbr_rbr[4][4] = {
{ 0x00, 0x0c, 0x14, 0x19 },
{ 0x00, 0x0b, 0x12, 0xff },
{ 0x00, 0x0b, 0xff, 0xff },
{ 0x04, 0xff, 0xff, 0xff }
};
static const u8 qmp_dp_v3_voltage_swing_hbr_rbr[4][4] = {
{ 0x08, 0x0f, 0x16, 0x1f },
{ 0x11, 0x1e, 0x1f, 0xff },
{ 0x19, 0x1f, 0xff, 0xff },
{ 0x1f, 0xff, 0xff, 0xff }
};
static void qcom_qmp_phy_configure_dp_tx(struct qmp_phy *qphy)
{
const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts;
unsigned int v_level = 0, p_level = 0;
u32 bias_en, drvr_en;
u8 voltage_swing_cfg, pre_emphasis_cfg;
int i;
for (i = 0; i < dp_opts->lanes; i++) {
v_level = max(v_level, dp_opts->voltage[i]);
p_level = max(p_level, dp_opts->pre[i]);
}
if (dp_opts->lanes == 1) {
bias_en = 0x3e;
drvr_en = 0x13;
} else {
bias_en = 0x3f;
drvr_en = 0x10;
}
voltage_swing_cfg = qmp_dp_v3_voltage_swing_hbr_rbr[v_level][p_level];
pre_emphasis_cfg = qmp_dp_v3_pre_emphasis_hbr_rbr[v_level][p_level];
/* TODO: Move check to config check */
if (voltage_swing_cfg == 0xFF && pre_emphasis_cfg == 0xFF)
return;
/* Enable MUX to use Cursor values from these registers */
voltage_swing_cfg |= DP_PHY_TXn_TX_DRV_LVL_MUX_EN;
pre_emphasis_cfg |= DP_PHY_TXn_TX_EMP_POST1_LVL_MUX_EN;
writel(voltage_swing_cfg, qphy->tx + QSERDES_V3_TX_TX_DRV_LVL);
writel(pre_emphasis_cfg, qphy->tx + QSERDES_V3_TX_TX_EMP_POST1_LVL);
writel(voltage_swing_cfg, qphy->tx2 + QSERDES_V3_TX_TX_DRV_LVL);
writel(pre_emphasis_cfg, qphy->tx2 + QSERDES_V3_TX_TX_EMP_POST1_LVL);
writel(drvr_en, qphy->tx + QSERDES_V3_TX_HIGHZ_DRVR_EN);
writel(bias_en, qphy->tx + QSERDES_V3_TX_TRANSCEIVER_BIAS_EN);
writel(drvr_en, qphy->tx2 + QSERDES_V3_TX_HIGHZ_DRVR_EN);
writel(bias_en, qphy->tx2 + QSERDES_V3_TX_TRANSCEIVER_BIAS_EN);
}
static int qcom_qmp_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
{
const struct phy_configure_opts_dp *dp_opts = &opts->dp;
struct qmp_phy *qphy = phy_get_drvdata(phy);
memcpy(&qphy->dp_opts, dp_opts, sizeof(*dp_opts));
if (qphy->dp_opts.set_voltages) {
qcom_qmp_phy_configure_dp_tx(qphy);
qphy->dp_opts.set_voltages = 0;
}
return 0;
}
static int qcom_qmp_phy_configure_dp_phy(struct qmp_phy *qphy)
{
const struct qmp_phy_dp_clks *dp_clks = qphy->dp_clks;
const struct phy_configure_opts_dp *dp_opts = &qphy->dp_opts;
u32 val, phy_vco_div, status;
unsigned long pixel_freq;
val = DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN |
DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN;
/*
* TODO: Assume orientation is CC1 for now and two lanes, need to
* use type-c connector to understand orientation and lanes.
*
* Otherwise val changes to be like below if this code understood
* the orientation of the type-c cable.
*
* if (lane_cnt == 4 || orientation == ORIENTATION_CC2)
* val |= DP_PHY_PD_CTL_LANE_0_1_PWRDN;
* if (lane_cnt == 4 || orientation == ORIENTATION_CC1)
* val |= DP_PHY_PD_CTL_LANE_2_3_PWRDN;
* if (orientation == ORIENTATION_CC2)
* writel(0x4c, qphy->pcs + QSERDES_V3_DP_PHY_MODE);
*/
val |= DP_PHY_PD_CTL_LANE_2_3_PWRDN;
writel(val, qphy->pcs + QSERDES_V3_DP_PHY_PD_CTL);
writel(0x5c, qphy->pcs + QSERDES_V3_DP_PHY_MODE);
writel(0x05, qphy->pcs + QSERDES_V3_DP_PHY_TX0_TX1_LANE_CTL);
writel(0x05, qphy->pcs + QSERDES_V3_DP_PHY_TX2_TX3_LANE_CTL);
switch (dp_opts->link_rate) {
case 1620:
phy_vco_div = 0x1;
pixel_freq = 1620000000UL / 2;
break;
case 2700:
phy_vco_div = 0x1;
pixel_freq = 2700000000UL / 2;
break;
case 5400:
phy_vco_div = 0x2;
pixel_freq = 5400000000UL / 4;
break;
case 8100:
phy_vco_div = 0x0;
pixel_freq = 8100000000UL / 6;
break;
default:
/* Other link rates aren't supported */
return -EINVAL;
}
writel(phy_vco_div, qphy->pcs + QSERDES_V3_DP_PHY_VCO_DIV);
clk_set_rate(dp_clks->dp_link_hw.clk, dp_opts->link_rate * 100000);
clk_set_rate(dp_clks->dp_pixel_hw.clk, pixel_freq);
writel(0x04, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG2);
writel(0x01, qphy->pcs + QSERDES_V3_DP_PHY_CFG);
writel(0x05, qphy->pcs + QSERDES_V3_DP_PHY_CFG);
writel(0x01, qphy->pcs + QSERDES_V3_DP_PHY_CFG);
writel(0x09, qphy->pcs + QSERDES_V3_DP_PHY_CFG);
writel(0x20, qphy->serdes + QSERDES_V3_COM_RESETSM_CNTRL);
if (readl_poll_timeout(qphy->serdes + QSERDES_V3_COM_C_READY_STATUS,
status,
((status & BIT(0)) > 0),
500,
10000))
return -ETIMEDOUT;
writel(0x19, qphy->pcs + QSERDES_V3_DP_PHY_CFG);
if (readl_poll_timeout(qphy->pcs + QSERDES_V3_DP_PHY_STATUS,
status,
((status & BIT(1)) > 0),
500,
10000))
return -ETIMEDOUT;
writel(0x18, qphy->pcs + QSERDES_V3_DP_PHY_CFG);
udelay(2000);
writel(0x19, qphy->pcs + QSERDES_V3_DP_PHY_CFG);
return readl_poll_timeout(qphy->pcs + QSERDES_V3_DP_PHY_STATUS,
status,
((status & BIT(1)) > 0),
500,
10000);
}
/*
* We need to calibrate the aux setting here as many times
* as the caller tries
*/
static int qcom_qmp_dp_phy_calibrate(struct phy *phy)
{
struct qmp_phy *qphy = phy_get_drvdata(phy);
const u8 cfg1_settings[] = { 0x13, 0x23, 0x1d };
u8 val;
qphy->dp_aux_cfg++;
qphy->dp_aux_cfg %= ARRAY_SIZE(cfg1_settings);
val = cfg1_settings[qphy->dp_aux_cfg];
writel(val, qphy->pcs + QSERDES_V3_DP_PHY_AUX_CFG1);
return 0;
}
static int qcom_qmp_phy_com_init(struct qmp_phy *qphy)
{
struct qcom_qmp *qmp = qphy->qmp;
const struct qmp_phy_cfg *cfg = qmp->cfg;
void __iomem *serdes = qmp->serdes;
const struct qmp_phy_cfg *cfg = qphy->cfg;
void __iomem *serdes = qphy->serdes;
void __iomem *pcs = qphy->pcs;
void __iomem *dp_com = qmp->dp_com;
int ret, i;
......@@ -2514,7 +2942,7 @@ static int qcom_qmp_phy_com_init(struct qmp_phy *qphy)
ret = reset_control_deassert(qmp->resets[i]);
if (ret) {
dev_err(qmp->dev, "%s reset deassert failed\n",
qmp->cfg->reset_list[i]);
qphy->cfg->reset_list[i]);
goto err_rst;
}
}
......@@ -2533,6 +2961,9 @@ static int qcom_qmp_phy_com_init(struct qmp_phy *qphy)
SW_DPPHY_RESET_MUX | SW_DPPHY_RESET |
SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
/* Default type-c orientation, i.e CC1 */
qphy_setbits(dp_com, QPHY_V3_DP_COM_TYPEC_CTRL, 0x02);
qphy_setbits(dp_com, QPHY_V3_DP_COM_PHY_MODE_CTRL,
USB3_MODE | DP_MODE);
......@@ -2540,6 +2971,9 @@ static int qcom_qmp_phy_com_init(struct qmp_phy *qphy)
qphy_clrbits(dp_com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
SW_DPPHY_RESET_MUX | SW_DPPHY_RESET |
SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
qphy_clrbits(dp_com, QPHY_V3_DP_COM_SWI_CTRL, 0x03);
qphy_clrbits(dp_com, QPHY_V3_DP_COM_SW_RESET, SW_RESET);
}
if (cfg->has_phy_com_ctrl) {
......@@ -2555,36 +2989,10 @@ static int qcom_qmp_phy_com_init(struct qmp_phy *qphy)
cfg->pwrdn_ctrl);
}
/* Serdes configuration */
qcom_qmp_phy_configure(serdes, cfg->regs, cfg->serdes_tbl,
cfg->serdes_tbl_num);
if (cfg->has_phy_com_ctrl) {
void __iomem *status;
unsigned int mask, val;
qphy_clrbits(serdes, cfg->regs[QPHY_COM_SW_RESET], SW_RESET);
qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL],
SERDES_START | PCS_START);
status = serdes + cfg->regs[QPHY_COM_PCS_READY_STATUS];
mask = cfg->mask_com_pcs_ready;
ret = readl_poll_timeout(status, val, (val & mask), 10,
PHY_INIT_COMPLETE_TIMEOUT);
if (ret) {
dev_err(qmp->dev,
"phy common block init timed-out\n");
goto err_com_init;
}
}
mutex_unlock(&qmp->phy_mutex);
return 0;
err_com_init:
clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
err_rst:
while (++i < cfg->num_resets)
reset_control_assert(qmp->resets[i]);
......@@ -2596,10 +3004,11 @@ static int qcom_qmp_phy_com_init(struct qmp_phy *qphy)
return ret;
}
static int qcom_qmp_phy_com_exit(struct qcom_qmp *qmp)
static int qcom_qmp_phy_com_exit(struct qmp_phy *qphy)
{
const struct qmp_phy_cfg *cfg = qmp->cfg;
void __iomem *serdes = qmp->serdes;
struct qcom_qmp *qmp = qphy->qmp;
const struct qmp_phy_cfg *cfg = qphy->cfg;
void __iomem *serdes = qphy->serdes;
int i = cfg->num_resets;
mutex_lock(&qmp->phy_mutex);
......@@ -2630,20 +3039,12 @@ static int qcom_qmp_phy_com_exit(struct qcom_qmp *qmp)
return 0;
}
static int qcom_qmp_phy_enable(struct phy *phy)
static int qcom_qmp_phy_init(struct phy *phy)
{
struct qmp_phy *qphy = phy_get_drvdata(phy);
struct qcom_qmp *qmp = qphy->qmp;
const struct qmp_phy_cfg *cfg = qmp->cfg;
void __iomem *tx = qphy->tx;
void __iomem *rx = qphy->rx;
void __iomem *pcs = qphy->pcs;
void __iomem *pcs_misc = qphy->pcs_misc;
void __iomem *dp_com = qmp->dp_com;
void __iomem *status;
unsigned int mask, val, ready;
const struct qmp_phy_cfg *cfg = qphy->cfg;
int ret;
dev_vdbg(qmp->dev, "Initializing QMP phy\n");
if (cfg->no_pcs_sw_reset) {
......@@ -2670,13 +3071,34 @@ static int qcom_qmp_phy_enable(struct phy *phy)
ret = reset_control_assert(qmp->ufs_reset);
if (ret)
goto err_lane_rst;
return ret;
}
ret = qcom_qmp_phy_com_init(qphy);
if (ret)
return ret;
if (cfg->type == PHY_TYPE_DP)
qcom_qmp_phy_dp_aux_init(qphy);
return 0;
}
static int qcom_qmp_phy_power_on(struct phy *phy)
{
struct qmp_phy *qphy = phy_get_drvdata(phy);
struct qcom_qmp *qmp = qphy->qmp;
const struct qmp_phy_cfg *cfg = qphy->cfg;
void __iomem *tx = qphy->tx;
void __iomem *rx = qphy->rx;
void __iomem *pcs = qphy->pcs;
void __iomem *pcs_misc = qphy->pcs_misc;
void __iomem *status;
unsigned int mask, val, ready;
int ret;
qcom_qmp_phy_serdes_init(qphy);
if (cfg->has_lane_rst) {
ret = reset_control_deassert(qphy->lane_rst);
if (ret) {
......@@ -2700,13 +3122,23 @@ static int qcom_qmp_phy_enable(struct phy *phy)
qcom_qmp_phy_configure_lane(qphy->tx2, cfg->regs,
cfg->tx_tbl, cfg->tx_tbl_num, 2);
/* Configure special DP tx tunings */
if (cfg->type == PHY_TYPE_DP)
qcom_qmp_phy_configure_dp_tx(qphy);
qcom_qmp_phy_configure_lane(rx, cfg->regs,
cfg->rx_tbl, cfg->rx_tbl_num, 1);
if (cfg->is_dual_lane_phy)
qcom_qmp_phy_configure_lane(qphy->rx2, cfg->regs,
cfg->rx_tbl, cfg->rx_tbl_num, 2);
qcom_qmp_phy_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num);
/* Configure link rate, swing, etc. */
if (cfg->type == PHY_TYPE_DP)
qcom_qmp_phy_configure_dp_phy(qphy);
else
qcom_qmp_phy_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num);
ret = reset_control_deassert(qmp->ufs_reset);
if (ret)
goto err_lane_rst;
......@@ -2724,102 +3156,129 @@ static int qcom_qmp_phy_enable(struct phy *phy)
if (cfg->has_pwrdn_delay)
usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max);
/* Pull PHY out of reset state */
if (!cfg->no_pcs_sw_reset)
qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
if (cfg->has_phy_dp_com_ctrl)
qphy_clrbits(dp_com, QPHY_V3_DP_COM_SW_RESET, SW_RESET);
/* start SerDes and Phy-Coding-Sublayer */
qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
if (cfg->type == PHY_TYPE_UFS) {
status = pcs + cfg->regs[QPHY_PCS_READY_STATUS];
mask = PCS_READY;
ready = PCS_READY;
} else {
status = pcs + cfg->regs[QPHY_PCS_STATUS];
mask = PHYSTATUS;
ready = 0;
}
if (cfg->type != PHY_TYPE_DP) {
/* Pull PHY out of reset state */
if (!cfg->no_pcs_sw_reset)
qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
/* start SerDes and Phy-Coding-Sublayer */
qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
if (cfg->type == PHY_TYPE_UFS) {
status = pcs + cfg->regs[QPHY_PCS_READY_STATUS];
mask = PCS_READY;
ready = PCS_READY;
} else {
status = pcs + cfg->regs[QPHY_PCS_STATUS];
mask = PHYSTATUS;
ready = 0;
}
ret = readl_poll_timeout(status, val, (val & mask) == ready, 10,
PHY_INIT_COMPLETE_TIMEOUT);
if (ret) {
dev_err(qmp->dev, "phy initialization timed-out\n");
goto err_pcs_ready;
ret = readl_poll_timeout(status, val, (val & mask) == ready, 10,
PHY_INIT_COMPLETE_TIMEOUT);
if (ret) {
dev_err(qmp->dev, "phy initialization timed-out\n");
goto err_pcs_ready;
}
}
qmp->phy_initialized = true;
return 0;
err_pcs_ready:
reset_control_assert(qmp->ufs_reset);
clk_disable_unprepare(qphy->pipe_clk);
err_clk_enable:
if (cfg->has_lane_rst)
reset_control_assert(qphy->lane_rst);
err_lane_rst:
qcom_qmp_phy_com_exit(qmp);
return ret;
}
static int qcom_qmp_phy_disable(struct phy *phy)
static int qcom_qmp_phy_power_off(struct phy *phy)
{
struct qmp_phy *qphy = phy_get_drvdata(phy);
struct qcom_qmp *qmp = qphy->qmp;
const struct qmp_phy_cfg *cfg = qmp->cfg;
const struct qmp_phy_cfg *cfg = qphy->cfg;
clk_disable_unprepare(qphy->pipe_clk);
/* PHY reset */
if (!cfg->no_pcs_sw_reset)
qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
if (cfg->type == PHY_TYPE_DP) {
/* Assert DP PHY power down */
writel(DP_PHY_PD_CTL_PSR_PWRDN, qphy->pcs + QSERDES_V3_DP_PHY_PD_CTL);
} else {
/* PHY reset */
if (!cfg->no_pcs_sw_reset)
qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
/* stop SerDes and Phy-Coding-Sublayer */
qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
/* stop SerDes and Phy-Coding-Sublayer */
qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
/* Put PHY into POWER DOWN state: active low */
if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL]) {
qphy_clrbits(qphy->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
cfg->pwrdn_ctrl);
} else {
qphy_clrbits(qphy->pcs, QPHY_POWER_DOWN_CONTROL,
cfg->pwrdn_ctrl);
/* Put PHY into POWER DOWN state: active low */
if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL]) {
qphy_clrbits(qphy->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
cfg->pwrdn_ctrl);
} else {
qphy_clrbits(qphy->pcs, QPHY_POWER_DOWN_CONTROL,
cfg->pwrdn_ctrl);
}
}
return 0;
}
static int qcom_qmp_phy_exit(struct phy *phy)
{
struct qmp_phy *qphy = phy_get_drvdata(phy);
const struct qmp_phy_cfg *cfg = qphy->cfg;
if (cfg->has_lane_rst)
reset_control_assert(qphy->lane_rst);
qcom_qmp_phy_com_exit(qmp);
qmp->phy_initialized = false;
qcom_qmp_phy_com_exit(qphy);
return 0;
}
static int qcom_qmp_phy_enable(struct phy *phy)
{
int ret;
ret = qcom_qmp_phy_init(phy);
if (ret)
return ret;
ret = qcom_qmp_phy_power_on(phy);
if (ret)
qcom_qmp_phy_exit(phy);
return ret;
}
static int qcom_qmp_phy_disable(struct phy *phy)
{
int ret;
ret = qcom_qmp_phy_power_off(phy);
if (ret)
return ret;
return qcom_qmp_phy_exit(phy);
}
static int qcom_qmp_phy_set_mode(struct phy *phy,
enum phy_mode mode, int submode)
{
struct qmp_phy *qphy = phy_get_drvdata(phy);
struct qcom_qmp *qmp = qphy->qmp;
qmp->mode = mode;
qphy->mode = mode;
return 0;
}
static void qcom_qmp_phy_enable_autonomous_mode(struct qmp_phy *qphy)
{
struct qcom_qmp *qmp = qphy->qmp;
const struct qmp_phy_cfg *cfg = qmp->cfg;
const struct qmp_phy_cfg *cfg = qphy->cfg;
void __iomem *pcs = qphy->pcs;
void __iomem *pcs_misc = qphy->pcs_misc;
u32 intr_mask;
if (qmp->mode == PHY_MODE_USB_HOST_SS ||
qmp->mode == PHY_MODE_USB_DEVICE_SS)
if (qphy->mode == PHY_MODE_USB_HOST_SS ||
qphy->mode == PHY_MODE_USB_DEVICE_SS)
intr_mask = ARCVR_DTCT_EN | ALFPS_DTCT_EN;
else
intr_mask = ARCVR_DTCT_EN | ARCVR_DTCT_EVENT_SEL;
......@@ -2842,8 +3301,7 @@ static void qcom_qmp_phy_enable_autonomous_mode(struct qmp_phy *qphy)
static void qcom_qmp_phy_disable_autonomous_mode(struct qmp_phy *qphy)
{
struct qcom_qmp *qmp = qphy->qmp;
const struct qmp_phy_cfg *cfg = qmp->cfg;
const struct qmp_phy_cfg *cfg = qphy->cfg;
void __iomem *pcs = qphy->pcs;
void __iomem *pcs_misc = qphy->pcs_misc;
......@@ -2863,15 +3321,15 @@ static int __maybe_unused qcom_qmp_phy_runtime_suspend(struct device *dev)
{
struct qcom_qmp *qmp = dev_get_drvdata(dev);
struct qmp_phy *qphy = qmp->phys[0];
const struct qmp_phy_cfg *cfg = qmp->cfg;
const struct qmp_phy_cfg *cfg = qphy->cfg;
dev_vdbg(dev, "Suspending QMP phy, mode:%d\n", qmp->mode);
dev_vdbg(dev, "Suspending QMP phy, mode:%d\n", qphy->mode);
/* Supported only for USB3 PHY */
/* Supported only for USB3 PHY and luckily USB3 is the first phy */
if (cfg->type != PHY_TYPE_USB3)
return 0;
if (!qmp->phy_initialized) {
if (!qmp->init_count) {
dev_vdbg(dev, "PHY not initialized, bailing out\n");
return 0;
}
......@@ -2888,16 +3346,16 @@ static int __maybe_unused qcom_qmp_phy_runtime_resume(struct device *dev)
{
struct qcom_qmp *qmp = dev_get_drvdata(dev);
struct qmp_phy *qphy = qmp->phys[0];
const struct qmp_phy_cfg *cfg = qmp->cfg;
const struct qmp_phy_cfg *cfg = qphy->cfg;
int ret = 0;
dev_vdbg(dev, "Resuming QMP phy, mode:%d\n", qmp->mode);
dev_vdbg(dev, "Resuming QMP phy, mode:%d\n", qphy->mode);
/* Supported only for USB3 PHY */
/* Supported only for USB3 PHY and luckily USB3 is the first phy */
if (cfg->type != PHY_TYPE_USB3)
return 0;
if (!qmp->phy_initialized) {
if (!qmp->init_count) {
dev_vdbg(dev, "PHY not initialized, bailing out\n");
return 0;
}
......@@ -2920,10 +3378,10 @@ static int __maybe_unused qcom_qmp_phy_runtime_resume(struct device *dev)
return 0;
}
static int qcom_qmp_phy_vreg_init(struct device *dev)
static int qcom_qmp_phy_vreg_init(struct device *dev, const struct qmp_phy_cfg *cfg)
{
struct qcom_qmp *qmp = dev_get_drvdata(dev);
int num = qmp->cfg->num_vregs;
int num = cfg->num_vregs;
int i;
qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL);
......@@ -2931,24 +3389,24 @@ static int qcom_qmp_phy_vreg_init(struct device *dev)
return -ENOMEM;
for (i = 0; i < num; i++)
qmp->vregs[i].supply = qmp->cfg->vreg_list[i];
qmp->vregs[i].supply = cfg->vreg_list[i];
return devm_regulator_bulk_get(dev, num, qmp->vregs);
}
static int qcom_qmp_phy_reset_init(struct device *dev)
static int qcom_qmp_phy_reset_init(struct device *dev, const struct qmp_phy_cfg *cfg)
{
struct qcom_qmp *qmp = dev_get_drvdata(dev);
int i;
qmp->resets = devm_kcalloc(dev, qmp->cfg->num_resets,
qmp->resets = devm_kcalloc(dev, cfg->num_resets,
sizeof(*qmp->resets), GFP_KERNEL);
if (!qmp->resets)
return -ENOMEM;
for (i = 0; i < qmp->cfg->num_resets; i++) {
for (i = 0; i < cfg->num_resets; i++) {
struct reset_control *rst;
const char *name = qmp->cfg->reset_list[i];
const char *name = cfg->reset_list[i];
rst = devm_reset_control_get(dev, name);
if (IS_ERR(rst)) {
......@@ -2961,10 +3419,10 @@ static int qcom_qmp_phy_reset_init(struct device *dev)
return 0;
}
static int qcom_qmp_phy_clk_init(struct device *dev)
static int qcom_qmp_phy_clk_init(struct device *dev, const struct qmp_phy_cfg *cfg)
{
struct qcom_qmp *qmp = dev_get_drvdata(dev);
int num = qmp->cfg->num_clks;
int num = cfg->num_clks;
int i;
qmp->clks = devm_kcalloc(dev, num, sizeof(*qmp->clks), GFP_KERNEL);
......@@ -2972,12 +3430,12 @@ static int qcom_qmp_phy_clk_init(struct device *dev)
return -ENOMEM;
for (i = 0; i < num; i++)
qmp->clks[i].id = qmp->cfg->clk_list[i];
qmp->clks[i].id = cfg->clk_list[i];
return devm_clk_bulk_get(dev, num, qmp->clks);
}
static void phy_pipe_clk_release_provider(void *res)
static void phy_clk_release_provider(void *res)
{
of_clk_del_provider(res);
}
......@@ -3006,12 +3464,6 @@ static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np)
struct clk_init_data init = { };
int ret;
if ((qmp->cfg->type != PHY_TYPE_USB3) &&
(qmp->cfg->type != PHY_TYPE_PCIE)) {
/* not all phys register pipe clocks, so return success */
return 0;
}
ret = of_property_read_string(np, "clock-output-names", &init.name);
if (ret) {
dev_err(qmp->dev, "%pOFn: No clock-output-names\n", np);
......@@ -3040,9 +3492,202 @@ static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np)
* Roll a devm action because the clock provider is the child node, but
* the child node is not actually a device.
*/
ret = devm_add_action(qmp->dev, phy_pipe_clk_release_provider, np);
ret = devm_add_action(qmp->dev, phy_clk_release_provider, np);
if (ret)
phy_clk_release_provider(np);
return ret;
}
/*
* Display Port PLL driver block diagram for branch clocks
*
* +------------------------------+
* | DP_VCO_CLK |
* | |
* | +-------------------+ |
* | | (DP PLL/VCO) | |
* | +---------+---------+ |
* | v |
* | +----------+-----------+ |
* | | hsclk_divsel_clk_src | |
* | +----------+-----------+ |
* +------------------------------+
* |
* +---------<---------v------------>----------+
* | |
* +--------v----------------+ |
* | dp_phy_pll_link_clk | |
* | link_clk | |
* +--------+----------------+ |
* | |
* | |
* v v
* Input to DISPCC block |
* for link clk, crypto clk |
* and interface clock |
* |
* |
* +--------<------------+-----------------+---<---+
* | | |
* +----v---------+ +--------v-----+ +--------v------+
* | vco_divided | | vco_divided | | vco_divided |
* | _clk_src | | _clk_src | | _clk_src |
* | | | | | |
* |divsel_six | | divsel_two | | divsel_four |
* +-------+------+ +-----+--------+ +--------+------+
* | | |
* v---->----------v-------------<------v
* |
* +----------+-----------------+
* | dp_phy_pll_vco_div_clk |
* +---------+------------------+
* |
* v
* Input to DISPCC block
* for DP pixel clock
*
*/
static int qcom_qmp_dp_pixel_clk_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
switch (req->rate) {
case 1620000000UL / 2:
case 2700000000UL / 2:
/* 5.4 and 8.1 GHz are same link rate as 2.7GHz, i.e. div 4 and div 6 */
return 0;
default:
return -EINVAL;
}
}
static unsigned long
qcom_qmp_dp_pixel_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
const struct qmp_phy_dp_clks *dp_clks;
const struct qmp_phy *qphy;
const struct phy_configure_opts_dp *dp_opts;
dp_clks = container_of(hw, struct qmp_phy_dp_clks, dp_pixel_hw);
qphy = dp_clks->qphy;
dp_opts = &qphy->dp_opts;
switch (dp_opts->link_rate) {
case 1620:
return 1620000000UL / 2;
case 2700:
return 2700000000UL / 2;
case 5400:
return 5400000000UL / 4;
case 8100:
return 8100000000UL / 6;
default:
return 0;
}
}
static const struct clk_ops qcom_qmp_dp_pixel_clk_ops = {
.determine_rate = qcom_qmp_dp_pixel_clk_determine_rate,
.recalc_rate = qcom_qmp_dp_pixel_clk_recalc_rate,
};
static int qcom_qmp_dp_link_clk_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
switch (req->rate) {
case 162000000:
case 270000000:
case 540000000:
case 810000000:
return 0;
default:
return -EINVAL;
}
}
static unsigned long
qcom_qmp_dp_link_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
const struct qmp_phy_dp_clks *dp_clks;
const struct qmp_phy *qphy;
const struct phy_configure_opts_dp *dp_opts;
dp_clks = container_of(hw, struct qmp_phy_dp_clks, dp_link_hw);
qphy = dp_clks->qphy;
dp_opts = &qphy->dp_opts;
switch (dp_opts->link_rate) {
case 1620:
case 2700:
case 5400:
case 8100:
return dp_opts->link_rate * 100000;
default:
return 0;
}
}
static const struct clk_ops qcom_qmp_dp_link_clk_ops = {
.determine_rate = qcom_qmp_dp_link_clk_determine_rate,
.recalc_rate = qcom_qmp_dp_link_clk_recalc_rate,
};
static struct clk_hw *
qcom_qmp_dp_clks_hw_get(struct of_phandle_args *clkspec, void *data)
{
struct qmp_phy_dp_clks *dp_clks = data;
unsigned int idx = clkspec->args[0];
if (idx >= 2) {
pr_err("%s: invalid index %u\n", __func__, idx);
return ERR_PTR(-EINVAL);
}
if (idx == 0)
return &dp_clks->dp_link_hw;
return &dp_clks->dp_pixel_hw;
}
static int phy_dp_clks_register(struct qcom_qmp *qmp, struct qmp_phy *qphy,
struct device_node *np)
{
struct clk_init_data init = { };
struct qmp_phy_dp_clks *dp_clks;
int ret;
dp_clks = devm_kzalloc(qmp->dev, sizeof(*dp_clks), GFP_KERNEL);
if (!dp_clks)
return -ENOMEM;
dp_clks->qphy = qphy;
qphy->dp_clks = dp_clks;
init.ops = &qcom_qmp_dp_link_clk_ops;
init.name = "qmp_dp_phy_pll_link_clk";
dp_clks->dp_link_hw.init = &init;
ret = devm_clk_hw_register(qmp->dev, &dp_clks->dp_link_hw);
if (ret)
return ret;
init.ops = &qcom_qmp_dp_pixel_clk_ops;
init.name = "qmp_dp_phy_pll_vco_div_clk";
dp_clks->dp_pixel_hw.init = &init;
ret = devm_clk_hw_register(qmp->dev, &dp_clks->dp_pixel_hw);
if (ret)
return ret;
ret = of_clk_add_hw_provider(np, qcom_qmp_dp_clks_hw_get, dp_clks);
if (ret)
return ret;
/*
* Roll a devm action because the clock provider is the child node, but
* the child node is not actually a device.
*/
ret = devm_add_action(qmp->dev, phy_clk_release_provider, np);
if (ret)
phy_pipe_clk_release_provider(np);
phy_clk_release_provider(np);
return ret;
}
......@@ -3054,6 +3699,17 @@ static const struct phy_ops qcom_qmp_phy_gen_ops = {
.owner = THIS_MODULE,
};
static const struct phy_ops qcom_qmp_phy_dp_ops = {
.init = qcom_qmp_phy_init,
.configure = qcom_qmp_dp_phy_configure,
.power_on = qcom_qmp_phy_power_on,
.calibrate = qcom_qmp_dp_phy_calibrate,
.power_off = qcom_qmp_phy_power_off,
.exit = qcom_qmp_phy_exit,
.set_mode = qcom_qmp_phy_set_mode,
.owner = THIS_MODULE,
};
static const struct phy_ops qcom_qmp_pcie_ufs_ops = {
.power_on = qcom_qmp_phy_enable,
.power_off = qcom_qmp_phy_disable,
......@@ -3062,12 +3718,13 @@ static const struct phy_ops qcom_qmp_pcie_ufs_ops = {
};
static
int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id)
int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id,
void __iomem *serdes, const struct qmp_phy_cfg *cfg)
{
struct qcom_qmp *qmp = dev_get_drvdata(dev);
struct phy *generic_phy;
struct qmp_phy *qphy;
const struct phy_ops *ops = &qcom_qmp_phy_gen_ops;
const struct phy_ops *ops;
char prop_name[MAX_PROP_NAME];
int ret;
......@@ -3075,6 +3732,8 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id)
if (!qphy)
return -ENOMEM;
qphy->cfg = cfg;
qphy->serdes = serdes;
/*
* Get memory resources for each phy lane:
* Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2.
......@@ -3099,7 +3758,7 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id)
* back to old legacy behavior of assuming they can be reached at an
* offset from the first lane.
*/
if (qmp->cfg->is_dual_lane_phy) {
if (cfg->is_dual_lane_phy) {
qphy->tx2 = of_iomap(np, 3);
qphy->rx2 = of_iomap(np, 4);
if (!qphy->tx2 || !qphy->rx2) {
......@@ -3132,8 +3791,8 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id)
snprintf(prop_name, sizeof(prop_name), "pipe%d", id);
qphy->pipe_clk = of_clk_get_by_name(np, prop_name);
if (IS_ERR(qphy->pipe_clk)) {
if (qmp->cfg->type == PHY_TYPE_PCIE ||
qmp->cfg->type == PHY_TYPE_USB3) {
if (cfg->type == PHY_TYPE_PCIE ||
cfg->type == PHY_TYPE_USB3) {
ret = PTR_ERR(qphy->pipe_clk);
if (ret != -EPROBE_DEFER)
dev_err(dev,
......@@ -3145,7 +3804,7 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id)
}
/* Get lane reset, if any */
if (qmp->cfg->has_lane_rst) {
if (cfg->has_lane_rst) {
snprintf(prop_name, sizeof(prop_name), "lane%d", id);
qphy->lane_rst = of_reset_control_get(np, prop_name);
if (IS_ERR(qphy->lane_rst)) {
......@@ -3154,8 +3813,12 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id)
}
}
if (qmp->cfg->type == PHY_TYPE_UFS || qmp->cfg->type == PHY_TYPE_PCIE)
if (cfg->type == PHY_TYPE_UFS || cfg->type == PHY_TYPE_PCIE)
ops = &qcom_qmp_pcie_ufs_ops;
else if (cfg->type == PHY_TYPE_DP)
ops = &qcom_qmp_phy_dp_ops;
else
ops = &qcom_qmp_phy_gen_ops;
generic_phy = devm_phy_create(dev, np, ops);
if (IS_ERR(generic_phy)) {
......@@ -3198,6 +3861,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
}, {
.compatible = "qcom,sc7180-qmp-usb3-phy",
.data = &sc7180_usb3phy_cfg,
}, {
.compatible = "qcom,sc7180-qmp-usb3-dp-phy",
/* It's a combo phy */
}, {
.compatible = "qcom,sdm845-qhp-pcie-phy",
.data = &sdm845_qhp_pciephy_cfg,
......@@ -3239,6 +3905,14 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
};
MODULE_DEVICE_TABLE(of, qcom_qmp_phy_of_match_table);
static const struct of_device_id qcom_qmp_combo_phy_of_match_table[] = {
{
.compatible = "qcom,sc7180-qmp-usb3-dp-phy",
.data = &sc7180_usb3dpphy_cfg,
},
{ }
};
static const struct dev_pm_ops qcom_qmp_phy_pm_ops = {
SET_RUNTIME_PM_OPS(qcom_qmp_phy_runtime_suspend,
qcom_qmp_phy_runtime_resume, NULL)
......@@ -3248,11 +3922,16 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
{
struct qcom_qmp *qmp;
struct device *dev = &pdev->dev;
struct resource *res;
struct device_node *child;
struct phy_provider *phy_provider;
void __iomem *base;
int num, id;
void __iomem *serdes;
void __iomem *usb_serdes;
void __iomem *dp_serdes;
const struct qmp_phy_combo_cfg *combo_cfg = NULL;
const struct qmp_phy_cfg *cfg = NULL;
const struct qmp_phy_cfg *usb_cfg = NULL;
const struct qmp_phy_cfg *dp_cfg = NULL;
int num, id, expected_phys;
int ret;
qmp = devm_kzalloc(dev, sizeof(*qmp), GFP_KERNEL);
......@@ -3263,40 +3942,57 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
dev_set_drvdata(dev, qmp);
/* Get the specific init parameters of QMP phy */
qmp->cfg = of_device_get_match_data(dev);
if (!qmp->cfg)
return -EINVAL;
cfg = of_device_get_match_data(dev);
if (!cfg) {
const struct of_device_id *match;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
match = of_match_device(qcom_qmp_combo_phy_of_match_table, dev);
if (!match)
return -EINVAL;
combo_cfg = match->data;
if (!combo_cfg)
return -EINVAL;
usb_cfg = combo_cfg->usb_cfg;
cfg = usb_cfg; /* Setup clks and regulators */
}
/* per PHY serdes; usually located at base address */
qmp->serdes = base;
usb_serdes = serdes = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(serdes))
return PTR_ERR(serdes);
/* per PHY dp_com; if PHY has dp_com control block */
if (qmp->cfg->has_phy_dp_com_ctrl) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"dp_com");
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
qmp->dp_com = base;
if (combo_cfg || cfg->has_phy_dp_com_ctrl) {
qmp->dp_com = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(qmp->dp_com))
return PTR_ERR(qmp->dp_com);
}
if (combo_cfg) {
/* Only two serdes for combo PHY */
dp_serdes = devm_platform_ioremap_resource(pdev, 2);
if (IS_ERR(dp_serdes))
return PTR_ERR(dp_serdes);
dp_cfg = combo_cfg->dp_cfg;
expected_phys = 2;
} else {
expected_phys = cfg->nlanes;
}
mutex_init(&qmp->phy_mutex);
ret = qcom_qmp_phy_clk_init(dev);
ret = qcom_qmp_phy_clk_init(dev, cfg);
if (ret)
return ret;
ret = qcom_qmp_phy_reset_init(dev);
ret = qcom_qmp_phy_reset_init(dev, cfg);
if (ret)
return ret;
ret = qcom_qmp_phy_vreg_init(dev);
ret = qcom_qmp_phy_vreg_init(dev, cfg);
if (ret) {
if (ret != -EPROBE_DEFER)
dev_err(dev, "failed to get regulator supplies: %d\n",
......@@ -3306,14 +4002,13 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
num = of_get_available_child_count(dev->of_node);
/* do we have a rogue child node ? */
if (num > qmp->cfg->nlanes)
if (num > expected_phys)
return -EINVAL;
qmp->phys = devm_kcalloc(dev, num, sizeof(*qmp->phys), GFP_KERNEL);
if (!qmp->phys)
return -ENOMEM;
id = 0;
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
/*
......@@ -3322,9 +4017,18 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
*/
pm_runtime_forbid(dev);
id = 0;
for_each_available_child_of_node(dev->of_node, child) {
if (of_node_name_eq(child, "dp-phy")) {
cfg = dp_cfg;
serdes = dp_serdes;
} else if (of_node_name_eq(child, "usb3-phy")) {
cfg = usb_cfg;
serdes = usb_serdes;
}
/* Create per-lane phy */
ret = qcom_qmp_phy_create(dev, child, id);
ret = qcom_qmp_phy_create(dev, child, id, serdes, cfg);
if (ret) {
dev_err(dev, "failed to create lane%d phy, %d\n",
id, ret);
......@@ -3335,11 +4039,20 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
* Register the pipe clock provided by phy.
* See function description to see details of this pipe clock.
*/
ret = phy_pipe_clk_register(qmp, child);
if (ret) {
dev_err(qmp->dev,
"failed to register pipe clock source\n");
goto err_node_put;
if (cfg->type == PHY_TYPE_USB3 || cfg->type == PHY_TYPE_PCIE) {
ret = phy_pipe_clk_register(qmp, child);
if (ret) {
dev_err(qmp->dev,
"failed to register pipe clock source\n");
goto err_node_put;
}
} else if (cfg->type == PHY_TYPE_DP) {
ret = phy_dp_clks_register(qmp, qmp->phys[id], child);
if (ret) {
dev_err(qmp->dev,
"failed to register DP clock source\n");
goto err_node_put;
}
}
id++;
}
......
......@@ -137,6 +137,9 @@
#define QPHY_V3_DP_COM_RESET_OVRD_CTRL 0x1c
/* Only for QMP V3 PHY - QSERDES COM registers */
#define QSERDES_V3_COM_ATB_SEL1 0x000
#define QSERDES_V3_COM_ATB_SEL2 0x004
#define QSERDES_V3_COM_FREQ_UPDATE 0x008
#define QSERDES_V3_COM_BG_TIMER 0x00c
#define QSERDES_V3_COM_SSC_EN_CENTER 0x010
#define QSERDES_V3_COM_SSC_ADJ_PER1 0x014
......@@ -146,6 +149,13 @@
#define QSERDES_V3_COM_SSC_STEP_SIZE1 0x024
#define QSERDES_V3_COM_SSC_STEP_SIZE2 0x028
#define QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN 0x034
# define QSERDES_V3_COM_BIAS_EN 0x0001
# define QSERDES_V3_COM_BIAS_EN_MUX 0x0002
# define QSERDES_V3_COM_CLKBUF_R_EN 0x0004
# define QSERDES_V3_COM_CLKBUF_L_EN 0x0008
# define QSERDES_V3_COM_EN_SYSCLK_TX_SEL 0x0010
# define QSERDES_V3_COM_CLKBUF_RX_DRIVE_L 0x0020
# define QSERDES_V3_COM_CLKBUF_RX_DRIVE_R 0x0040
#define QSERDES_V3_COM_CLK_ENABLE1 0x038
#define QSERDES_V3_COM_SYS_CLK_CTRL 0x03c
#define QSERDES_V3_COM_SYSCLK_BUF_ENABLE 0x040
......@@ -207,12 +217,36 @@
#define QSERDES_V3_COM_CMN_MODE 0x184
/* Only for QMP V3 PHY - TX registers */
#define QSERDES_V3_TX_BIST_MODE_LANENO 0x000
#define QSERDES_V3_TX_CLKBUF_ENABLE 0x008
#define QSERDES_V3_TX_TX_EMP_POST1_LVL 0x00c
# define DP_PHY_TXn_TX_EMP_POST1_LVL_MASK 0x001f
# define DP_PHY_TXn_TX_EMP_POST1_LVL_MUX_EN 0x0020
#define QSERDES_V3_TX_TX_DRV_LVL 0x01c
# define DP_PHY_TXn_TX_DRV_LVL_MASK 0x001f
# define DP_PHY_TXn_TX_DRV_LVL_MUX_EN 0x0020
#define QSERDES_V3_TX_RESET_TSYNC_EN 0x024
#define QSERDES_V3_TX_PRE_STALL_LDO_BOOST_EN 0x028
#define QSERDES_V3_TX_TX_BAND 0x02c
#define QSERDES_V3_TX_SLEW_CNTL 0x030
#define QSERDES_V3_TX_INTERFACE_SELECT 0x034
#define QSERDES_V3_TX_RES_CODE_LANE_TX 0x03c
#define QSERDES_V3_TX_RES_CODE_LANE_RX 0x040
#define QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX 0x044
#define QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX 0x048
#define QSERDES_V3_TX_DEBUG_BUS_SEL 0x058
#define QSERDES_V3_TX_TRANSCEIVER_BIAS_EN 0x05c
#define QSERDES_V3_TX_HIGHZ_DRVR_EN 0x060
#define QSERDES_V3_TX_TX_POL_INV 0x064
#define QSERDES_V3_TX_PARRATE_REC_DETECT_IDLE_EN 0x068
#define QSERDES_V3_TX_LANE_MODE_1 0x08c
#define QSERDES_V3_TX_RCV_DETECT_LVL_2 0x0a4
#define QSERDES_V3_TX_TRAN_DRVR_EMP_EN 0x0c0
#define QSERDES_V3_TX_TX_INTERFACE_MODE 0x0c4
#define QSERDES_V3_TX_VMODE_CTRL1 0x0f0
/* Only for QMP V3 PHY - RX registers */
#define QSERDES_V3_RX_UCDR_FO_GAIN 0x008
......@@ -315,6 +349,52 @@
#define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG4 0x5c
#define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG5 0x60
/* Only for QMP V3 PHY - DP PHY registers */
#define QSERDES_V3_DP_PHY_REVISION_ID0 0x000
#define QSERDES_V3_DP_PHY_REVISION_ID1 0x004
#define QSERDES_V3_DP_PHY_REVISION_ID2 0x008
#define QSERDES_V3_DP_PHY_REVISION_ID3 0x00c
#define QSERDES_V3_DP_PHY_CFG 0x010
#define QSERDES_V3_DP_PHY_PD_CTL 0x018
# define DP_PHY_PD_CTL_PWRDN 0x001
# define DP_PHY_PD_CTL_PSR_PWRDN 0x002
# define DP_PHY_PD_CTL_AUX_PWRDN 0x004
# define DP_PHY_PD_CTL_LANE_0_1_PWRDN 0x008
# define DP_PHY_PD_CTL_LANE_2_3_PWRDN 0x010
# define DP_PHY_PD_CTL_PLL_PWRDN 0x020
# define DP_PHY_PD_CTL_DP_CLAMP_EN 0x040
#define QSERDES_V3_DP_PHY_MODE 0x01c
#define QSERDES_V3_DP_PHY_AUX_CFG0 0x020
#define QSERDES_V3_DP_PHY_AUX_CFG1 0x024
#define QSERDES_V3_DP_PHY_AUX_CFG2 0x028
#define QSERDES_V3_DP_PHY_AUX_CFG3 0x02c
#define QSERDES_V3_DP_PHY_AUX_CFG4 0x030
#define QSERDES_V3_DP_PHY_AUX_CFG5 0x034
#define QSERDES_V3_DP_PHY_AUX_CFG6 0x038
#define QSERDES_V3_DP_PHY_AUX_CFG7 0x03c
#define QSERDES_V3_DP_PHY_AUX_CFG8 0x040
#define QSERDES_V3_DP_PHY_AUX_CFG9 0x044
#define QSERDES_V3_DP_PHY_AUX_INTERRUPT_MASK 0x048
# define PHY_AUX_STOP_ERR_MASK 0x01
# define PHY_AUX_DEC_ERR_MASK 0x02
# define PHY_AUX_SYNC_ERR_MASK 0x04
# define PHY_AUX_ALIGN_ERR_MASK 0x08
# define PHY_AUX_REQ_ERR_MASK 0x10
#define QSERDES_V3_DP_PHY_AUX_INTERRUPT_CLEAR 0x04c
#define QSERDES_V3_DP_PHY_AUX_BIST_CFG 0x050
#define QSERDES_V3_DP_PHY_VCO_DIV 0x064
#define QSERDES_V3_DP_PHY_TX0_TX1_LANE_CTL 0x06c
#define QSERDES_V3_DP_PHY_TX2_TX3_LANE_CTL 0x088
#define QSERDES_V3_DP_PHY_SPARE0 0x0ac
#define DP_PHY_SPARE0_MASK 0x0f
#define DP_PHY_SPARE0_ORIENTATION_INFO_SHIFT 0x04(0x0004)
#define QSERDES_V3_DP_PHY_STATUS 0x0c0
/* Only for QMP V4 PHY - QSERDES COM registers */
#define QSERDES_V4_COM_SSC_EN_CENTER 0x010
#define QSERDES_V4_COM_SSC_PER1 0x01c
......
......@@ -142,7 +142,7 @@ static int ralink_usb_phy_power_off(struct phy *_phy)
return 0;
}
static struct phy_ops ralink_usb_phy_ops = {
static const struct phy_ops ralink_usb_phy_ops = {
.power_on = ralink_usb_phy_power_on,
.power_off = ralink_usb_phy_power_off,
.owner = THIS_MODULE,
......
......@@ -9,6 +9,18 @@ config PHY_ROCKCHIP_DP
help
Enable this to support the Rockchip Display Port PHY.
config PHY_ROCKCHIP_DPHY_RX0
tristate "Rockchip MIPI Synopsys DPHY RX0 driver"
depends on ARCH_ROCKCHIP || COMPILE_TEST
select GENERIC_PHY_MIPI_DPHY
select GENERIC_PHY
help
Enable this to support the Rockchip MIPI Synopsys DPHY RX0
associated to the Rockchip ISP module present in RK3399 SoCs.
To compile this driver as a module, choose M here: the module
will be called phy-rockchip-dphy-rx0.
config PHY_ROCKCHIP_EMMC
tristate "Rockchip EMMC PHY Driver"
depends on ARCH_ROCKCHIP && OF
......
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_PHY_ROCKCHIP_DP) += phy-rockchip-dp.o
obj-$(CONFIG_PHY_ROCKCHIP_DPHY_RX0) += phy-rockchip-dphy-rx0.o
obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
obj-$(CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY) += phy-rockchip-inno-dsidphy.o
obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o
......
......@@ -16,6 +16,7 @@
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
......
......@@ -16,6 +16,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/iopoll.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
......@@ -556,41 +557,25 @@ static int exynos5_usbdrd_phy_power_off(struct phy *phy)
static int crport_handshake(struct exynos5_usbdrd_phy *phy_drd,
u32 val, u32 cmd)
{
u32 usec = 100;
unsigned int result;
int err;
writel(val | cmd, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0);
do {
result = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1);
if (result & PHYREG1_CR_ACK)
break;
udelay(1);
} while (usec-- > 0);
if (!usec) {
dev_err(phy_drd->dev,
"CRPORT handshake timeout1 (0x%08x)\n", val);
return -ETIME;
err = readl_poll_timeout(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1,
result, (result & PHYREG1_CR_ACK), 1, 100);
if (err == -ETIMEDOUT) {
dev_err(phy_drd->dev, "CRPORT handshake timeout1 (0x%08x)\n", val);
return err;
}
usec = 100;
writel(val, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0);
do {
result = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1);
if (!(result & PHYREG1_CR_ACK))
break;
udelay(1);
} while (usec-- > 0);
if (!usec) {
dev_err(phy_drd->dev,
"CRPORT handshake timeout2 (0x%08x)\n", val);
return -ETIME;
err = readl_poll_timeout(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1,
result, !(result & PHYREG1_CR_ACK), 1, 100);
if (err == -ETIMEDOUT) {
dev_err(phy_drd->dev, "CRPORT handshake timeout2 (0x%08x)\n", val);
return err;
}
return 0;
......
......@@ -268,7 +268,7 @@ static int samsung_ufs_phy_exit(struct phy *phy)
return 0;
}
static struct phy_ops samsung_ufs_phy_ops = {
static const struct phy_ops samsung_ufs_phy_ops = {
.init = samsung_ufs_phy_init,
.exit = samsung_ufs_phy_exit,
.power_on = samsung_ufs_phy_power_on,
......
......@@ -34,3 +34,13 @@ config PHY_UNIPHIER_PCIE
help
Enable this to support PHY implemented in PCIe controller
on UniPhier SoCs. This driver supports LD20 and PXs3 SoCs.
config PHY_UNIPHIER_AHCI
tristate "UniPhier AHCI PHY driver"
depends on ARCH_UNIPHIER || COMPILE_TEST
depends on OF && HAS_IOMEM
default SATA_AHCI_PLATFORM
select GENERIC_PHY
help
Enable this to support PHY implemented in AHCI controller
on UniPhier SoCs. This driver supports PXs2 and PXs3 SoCs.
......@@ -6,3 +6,4 @@
obj-$(CONFIG_PHY_UNIPHIER_USB2) += phy-uniphier-usb2.o
obj-$(CONFIG_PHY_UNIPHIER_USB3) += phy-uniphier-usb3hs.o phy-uniphier-usb3ss.o
obj-$(CONFIG_PHY_UNIPHIER_PCIE) += phy-uniphier-pcie.o
obj-$(CONFIG_PHY_UNIPHIER_AHCI) += phy-uniphier-ahci.o
// SPDX-License-Identifier: GPL-2.0
/*
* phy-uniphier-ahci.c - PHY driver for UniPhier AHCI controller
* Copyright 2016-2020, Socionext Inc.
* Author: Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
struct uniphier_ahciphy_priv {
struct device *dev;
void __iomem *base;
struct clk *clk, *clk_parent;
struct reset_control *rst, *rst_parent;
const struct uniphier_ahciphy_soc_data *data;
};
struct uniphier_ahciphy_soc_data {
int (*init)(struct uniphier_ahciphy_priv *priv);
int (*power_on)(struct uniphier_ahciphy_priv *priv);
int (*power_off)(struct uniphier_ahciphy_priv *priv);
bool is_ready_high;
bool is_phy_clk;
};
/* for PXs2/PXs3 */
#define CKCTRL 0x0
#define CKCTRL_P0_READY BIT(15)
#define CKCTRL_P0_RESET BIT(10)
#define CKCTRL_REF_SSP_EN BIT(9)
#define TXCTRL0 0x4
#define TXCTRL0_AMP_G3_MASK GENMASK(22, 16)
#define TXCTRL0_AMP_G2_MASK GENMASK(14, 8)
#define TXCTRL0_AMP_G1_MASK GENMASK(6, 0)
#define TXCTRL1 0x8
#define TXCTRL1_DEEMPH_G3_MASK GENMASK(21, 16)
#define TXCTRL1_DEEMPH_G2_MASK GENMASK(13, 8)
#define TXCTRL1_DEEMPH_G1_MASK GENMASK(5, 0)
#define RXCTRL 0xc
#define RXCTRL_LOS_LVL_MASK GENMASK(20, 16)
#define RXCTRL_LOS_BIAS_MASK GENMASK(10, 8)
#define RXCTRL_RX_EQ_MASK GENMASK(2, 0)
static void uniphier_ahciphy_pxs2_enable(struct uniphier_ahciphy_priv *priv,
bool enable)
{
u32 val;
val = readl(priv->base + CKCTRL);
if (enable) {
val |= CKCTRL_REF_SSP_EN;
writel(val, priv->base + CKCTRL);
val &= ~CKCTRL_P0_RESET;
writel(val, priv->base + CKCTRL);
} else {
val |= CKCTRL_P0_RESET;
writel(val, priv->base + CKCTRL);
val &= ~CKCTRL_REF_SSP_EN;
writel(val, priv->base + CKCTRL);
}
}
static int uniphier_ahciphy_pxs2_power_on(struct uniphier_ahciphy_priv *priv)
{
int ret;
u32 val;
uniphier_ahciphy_pxs2_enable(priv, true);
/* wait until PLL is ready */
if (priv->data->is_ready_high)
ret = readl_poll_timeout(priv->base + CKCTRL, val,
(val & CKCTRL_P0_READY), 200, 400);
else
ret = readl_poll_timeout(priv->base + CKCTRL, val,
!(val & CKCTRL_P0_READY), 200, 400);
if (ret) {
dev_err(priv->dev, "Failed to check whether PHY PLL is ready\n");
uniphier_ahciphy_pxs2_enable(priv, false);
}
return ret;
}
static int uniphier_ahciphy_pxs2_power_off(struct uniphier_ahciphy_priv *priv)
{
uniphier_ahciphy_pxs2_enable(priv, false);
return 0;
}
static int uniphier_ahciphy_pxs3_init(struct uniphier_ahciphy_priv *priv)
{
int i;
u32 val;
/* setup port parameter */
val = readl(priv->base + TXCTRL0);
val &= ~TXCTRL0_AMP_G3_MASK;
val |= FIELD_PREP(TXCTRL0_AMP_G3_MASK, 0x73);
val &= ~TXCTRL0_AMP_G2_MASK;
val |= FIELD_PREP(TXCTRL0_AMP_G2_MASK, 0x46);
val &= ~TXCTRL0_AMP_G1_MASK;
val |= FIELD_PREP(TXCTRL0_AMP_G1_MASK, 0x42);
writel(val, priv->base + TXCTRL0);
val = readl(priv->base + TXCTRL1);
val &= ~TXCTRL1_DEEMPH_G3_MASK;
val |= FIELD_PREP(TXCTRL1_DEEMPH_G3_MASK, 0x23);
val &= ~TXCTRL1_DEEMPH_G2_MASK;
val |= FIELD_PREP(TXCTRL1_DEEMPH_G2_MASK, 0x05);
val &= ~TXCTRL1_DEEMPH_G1_MASK;
val |= FIELD_PREP(TXCTRL1_DEEMPH_G1_MASK, 0x05);
val = readl(priv->base + RXCTRL);
val &= ~RXCTRL_LOS_LVL_MASK;
val |= FIELD_PREP(RXCTRL_LOS_LVL_MASK, 0x9);
val &= ~RXCTRL_LOS_BIAS_MASK;
val |= FIELD_PREP(RXCTRL_LOS_BIAS_MASK, 0x2);
val &= ~RXCTRL_RX_EQ_MASK;
val |= FIELD_PREP(RXCTRL_RX_EQ_MASK, 0x1);
/* dummy read 25 times to make a wait time for the phy to stabilize */
for (i = 0; i < 25; i++)
readl(priv->base + CKCTRL);
return 0;
}
static int uniphier_ahciphy_init(struct phy *phy)
{
struct uniphier_ahciphy_priv *priv = phy_get_drvdata(phy);
int ret;
ret = clk_prepare_enable(priv->clk_parent);
if (ret)
return ret;
ret = reset_control_deassert(priv->rst_parent);
if (ret)
goto out_clk_disable;
if (priv->data->init) {
ret = priv->data->init(priv);
if (ret)
goto out_rst_assert;
}
return 0;
out_rst_assert:
reset_control_assert(priv->rst_parent);
out_clk_disable:
clk_disable_unprepare(priv->clk_parent);
return ret;
}
static int uniphier_ahciphy_exit(struct phy *phy)
{
struct uniphier_ahciphy_priv *priv = phy_get_drvdata(phy);
reset_control_assert(priv->rst_parent);
clk_disable_unprepare(priv->clk_parent);
return 0;
}
static int uniphier_ahciphy_power_on(struct phy *phy)
{
struct uniphier_ahciphy_priv *priv = phy_get_drvdata(phy);
int ret = 0;
ret = clk_prepare_enable(priv->clk);
if (ret)
return ret;
ret = reset_control_deassert(priv->rst);
if (ret)
goto out_clk_disable;
if (priv->data->power_on) {
ret = priv->data->power_on(priv);
if (ret)
goto out_reset_assert;
}
return 0;
out_reset_assert:
reset_control_assert(priv->rst);
out_clk_disable:
clk_disable_unprepare(priv->clk);
return ret;
}
static int uniphier_ahciphy_power_off(struct phy *phy)
{
struct uniphier_ahciphy_priv *priv = phy_get_drvdata(phy);
int ret = 0;
if (priv->data->power_off)
ret = priv->data->power_off(priv);
reset_control_assert(priv->rst);
clk_disable_unprepare(priv->clk);
return ret;
}
static const struct phy_ops uniphier_ahciphy_ops = {
.init = uniphier_ahciphy_init,
.exit = uniphier_ahciphy_exit,
.power_on = uniphier_ahciphy_power_on,
.power_off = uniphier_ahciphy_power_off,
.owner = THIS_MODULE,
};
static int uniphier_ahciphy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct uniphier_ahciphy_priv *priv;
struct phy *phy;
struct phy_provider *phy_provider;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = dev;
priv->data = of_device_get_match_data(dev);
if (WARN_ON(!priv->data))
return -EINVAL;
priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
priv->clk_parent = devm_clk_get(dev, "link");
if (IS_ERR(priv->clk_parent))
return PTR_ERR(priv->clk_parent);
if (priv->data->is_phy_clk) {
priv->clk = devm_clk_get(dev, "phy");
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
}
priv->rst_parent = devm_reset_control_get_shared(dev, "link");
if (IS_ERR(priv->rst_parent))
return PTR_ERR(priv->rst_parent);
priv->rst = devm_reset_control_get_shared(dev, "phy");
if (IS_ERR(priv->rst))
return PTR_ERR(priv->rst);
phy = devm_phy_create(dev, dev->of_node, &uniphier_ahciphy_ops);
if (IS_ERR(phy)) {
dev_err(dev, "failed to create phy\n");
return PTR_ERR(phy);
}
phy_set_drvdata(phy, priv);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (IS_ERR(phy_provider))
return PTR_ERR(phy_provider);
return 0;
}
static const struct uniphier_ahciphy_soc_data uniphier_pxs2_data = {
.power_on = uniphier_ahciphy_pxs2_power_on,
.power_off = uniphier_ahciphy_pxs2_power_off,
.is_ready_high = false,
.is_phy_clk = false,
};
static const struct uniphier_ahciphy_soc_data uniphier_pxs3_data = {
.init = uniphier_ahciphy_pxs3_init,
.power_on = uniphier_ahciphy_pxs2_power_on,
.power_off = uniphier_ahciphy_pxs2_power_off,
.is_ready_high = true,
.is_phy_clk = true,
};
static const struct of_device_id uniphier_ahciphy_match[] = {
{
.compatible = "socionext,uniphier-pxs2-ahci-phy",
.data = &uniphier_pxs2_data,
},
{
.compatible = "socionext,uniphier-pxs3-ahci-phy",
.data = &uniphier_pxs3_data,
},
{ /* Sentinel */ },
};
MODULE_DEVICE_TABLE(of, uniphier_ahciphy_match);
static struct platform_driver uniphier_ahciphy_driver = {
.probe = uniphier_ahciphy_probe,
.driver = {
.name = "uniphier-ahci-phy",
.of_match_table = uniphier_ahciphy_match,
},
};
module_platform_driver(uniphier_ahciphy_driver);
MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");
MODULE_DESCRIPTION("UniPhier PHY driver for AHCI controller");
MODULE_LICENSE("GPL v2");
......@@ -19,15 +19,38 @@
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#define CMU_R004 0x4
#define CMU_R060 0x60
#define CMU_R07C 0x7c
#define CMU_R088 0x88
#define CMU_R0D0 0xd0
#define CMU_R0E8 0xe8
#define LANE_R048 0x248
#define LANE_R058 0x258
#define LANE_R06c 0x26c
#define LANE_R070 0x270
#define LANE_R070 0x270
#define LANE_R19C 0x39c
#define COMLANE_R004 0xa04
#define COMLANE_R138 0xb38
#define VERSION 0x70
#define VERSION_VAL 0x70
#define COMLANE_R190 0xb90
#define COMLANE_R194 0xb94
#define COMRXEQ_R004 0x1404
#define COMRXEQ_R008 0x1408
#define COMRXEQ_R00C 0x140c
#define COMRXEQ_R014 0x1414
#define COMRXEQ_R018 0x1418
#define COMRXEQ_R01C 0x141c
#define COMRXEQ_R04C 0x144c
#define COMRXEQ_R088 0x1488
#define COMRXEQ_R094 0x1494
#define COMRXEQ_R098 0x1498
#define SERDES_CTRL 0x1fd0
#define WIZ_LANEXCTL_STS 0x1fe0
......@@ -80,27 +103,136 @@ static const struct regmap_config serdes_am654_regmap_config = {
.max_register = 0x1ffc,
};
static const struct reg_field cmu_master_cdn_o = REG_FIELD(CMU_R07C, 24, 24);
static const struct reg_field config_version = REG_FIELD(COMLANE_R138, 16, 23);
static const struct reg_field l1_master_cdn_o = REG_FIELD(COMLANE_R190, 9, 9);
static const struct reg_field cmu_ok_i_0 = REG_FIELD(COMLANE_R194, 19, 19);
static const struct reg_field por_en = REG_FIELD(SERDES_CTRL, 29, 29);
static const struct reg_field tx0_enable = REG_FIELD(WIZ_LANEXCTL_STS, 29, 31);
static const struct reg_field rx0_enable = REG_FIELD(WIZ_LANEXCTL_STS, 13, 15);
static const struct reg_field pll_enable = REG_FIELD(WIZ_PLL_CTRL, 29, 31);
static const struct reg_field pll_ok = REG_FIELD(WIZ_PLL_CTRL, 28, 28);
enum serdes_am654_fields {
/* CMU PLL Control */
CMU_PLL_CTRL,
LANE_PLL_CTRL_RXEQ_RXIDLE,
/* CMU VCO bias current and VREG setting */
AHB_PMA_CM_VCO_VBIAS_VREG,
AHB_PMA_CM_VCO_BIAS_VREG,
AHB_PMA_CM_SR,
AHB_SSC_GEN_Z_O_20_13,
/* AHB PMA Lane Configuration */
AHB_PMA_LN_AGC_THSEL_VREGH,
/* AGC and Signal detect threshold for Gen3 */
AHB_PMA_LN_GEN3_AGC_SD_THSEL,
AHB_PMA_LN_RX_SELR_GEN3,
AHB_PMA_LN_TX_DRV,
/* CMU Master Reset */
CMU_MASTER_CDN,
/* P2S ring buffer initial startup pointer difference */
P2S_RBUF_PTR_DIFF,
CONFIG_VERSION,
/* Lane 1 Master Reset */
L1_MASTER_CDN,
/* CMU OK Status */
CMU_OK_I_0,
/* Mid-speed initial calibration control */
COMRXEQ_MS_INIT_CTRL_7_0,
/* High-speed initial calibration control */
COMRXEQ_HS_INIT_CAL_7_0,
/* Mid-speed recalibration control */
COMRXEQ_MS_RECAL_CTRL_7_0,
/* High-speed recalibration control */
COMRXEQ_HS_RECAL_CTRL_7_0,
/* ATT configuration */
COMRXEQ_CSR_ATT_CONFIG,
/* Edge based boost adaptation window length */
COMRXEQ_CSR_EBSTADAPT_WIN_LEN,
/* COMRXEQ control 3 & 4 */
COMRXEQ_CTRL_3_4,
/* COMRXEQ control 14, 15 and 16*/
COMRXEQ_CTRL_14_15_16,
/* Threshold for errors in pattern data */
COMRXEQ_CSR_DLEV_ERR_THRESH,
/* COMRXEQ control 25 */
COMRXEQ_CTRL_25,
/* Mid-speed rate change calibration control */
CSR_RXEQ_RATE_CHANGE_CAL_RUN_RATE2_O,
/* High-speed rate change calibration control */
COMRXEQ_HS_RCHANGE_CTRL_7_0,
/* Serdes reset */
POR_EN,
/* Tx Enable Value */
TX0_ENABLE,
/* Rx Enable Value */
RX0_ENABLE,
/* PLL Enable Value */
PLL_ENABLE,
/* PLL ready for use */
PLL_OK,
/* sentinel */
MAX_FIELDS
};
static const struct reg_field serdes_am654_reg_fields[] = {
[CMU_PLL_CTRL] = REG_FIELD(CMU_R004, 8, 15),
[AHB_PMA_CM_VCO_VBIAS_VREG] = REG_FIELD(CMU_R060, 8, 15),
[CMU_MASTER_CDN] = REG_FIELD(CMU_R07C, 24, 31),
[AHB_PMA_CM_VCO_BIAS_VREG] = REG_FIELD(CMU_R088, 24, 31),
[AHB_PMA_CM_SR] = REG_FIELD(CMU_R0D0, 24, 31),
[AHB_SSC_GEN_Z_O_20_13] = REG_FIELD(CMU_R0E8, 8, 15),
[LANE_PLL_CTRL_RXEQ_RXIDLE] = REG_FIELD(LANE_R048, 8, 15),
[AHB_PMA_LN_AGC_THSEL_VREGH] = REG_FIELD(LANE_R058, 16, 23),
[AHB_PMA_LN_GEN3_AGC_SD_THSEL] = REG_FIELD(LANE_R06c, 0, 7),
[AHB_PMA_LN_RX_SELR_GEN3] = REG_FIELD(LANE_R070, 16, 23),
[AHB_PMA_LN_TX_DRV] = REG_FIELD(LANE_R19C, 16, 23),
[P2S_RBUF_PTR_DIFF] = REG_FIELD(COMLANE_R004, 0, 7),
[CONFIG_VERSION] = REG_FIELD(COMLANE_R138, 16, 23),
[L1_MASTER_CDN] = REG_FIELD(COMLANE_R190, 8, 15),
[CMU_OK_I_0] = REG_FIELD(COMLANE_R194, 19, 19),
[COMRXEQ_MS_INIT_CTRL_7_0] = REG_FIELD(COMRXEQ_R004, 24, 31),
[COMRXEQ_HS_INIT_CAL_7_0] = REG_FIELD(COMRXEQ_R008, 0, 7),
[COMRXEQ_MS_RECAL_CTRL_7_0] = REG_FIELD(COMRXEQ_R00C, 8, 15),
[COMRXEQ_HS_RECAL_CTRL_7_0] = REG_FIELD(COMRXEQ_R00C, 16, 23),
[COMRXEQ_CSR_ATT_CONFIG] = REG_FIELD(COMRXEQ_R014, 16, 23),
[COMRXEQ_CSR_EBSTADAPT_WIN_LEN] = REG_FIELD(COMRXEQ_R018, 16, 23),
[COMRXEQ_CTRL_3_4] = REG_FIELD(COMRXEQ_R01C, 8, 15),
[COMRXEQ_CTRL_14_15_16] = REG_FIELD(COMRXEQ_R04C, 0, 7),
[COMRXEQ_CSR_DLEV_ERR_THRESH] = REG_FIELD(COMRXEQ_R088, 16, 23),
[COMRXEQ_CTRL_25] = REG_FIELD(COMRXEQ_R094, 24, 31),
[CSR_RXEQ_RATE_CHANGE_CAL_RUN_RATE2_O] = REG_FIELD(COMRXEQ_R098, 8, 15),
[COMRXEQ_HS_RCHANGE_CTRL_7_0] = REG_FIELD(COMRXEQ_R098, 16, 23),
[POR_EN] = REG_FIELD(SERDES_CTRL, 29, 29),
[TX0_ENABLE] = REG_FIELD(WIZ_LANEXCTL_STS, 29, 31),
[RX0_ENABLE] = REG_FIELD(WIZ_LANEXCTL_STS, 13, 15),
[PLL_ENABLE] = REG_FIELD(WIZ_PLL_CTRL, 29, 31),
[PLL_OK] = REG_FIELD(WIZ_PLL_CTRL, 28, 28),
};
struct serdes_am654 {
struct regmap *regmap;
struct regmap_field *cmu_master_cdn_o;
struct regmap_field *config_version;
struct regmap_field *l1_master_cdn_o;
struct regmap_field *cmu_ok_i_0;
struct regmap_field *por_en;
struct regmap_field *tx0_enable;
struct regmap_field *rx0_enable;
struct regmap_field *pll_enable;
struct regmap_field *pll_ok;
struct regmap_field *fields[MAX_FIELDS];
struct device *dev;
struct mux_control *control;
......@@ -116,12 +248,12 @@ static int serdes_am654_enable_pll(struct serdes_am654 *phy)
int ret;
u32 val;
ret = regmap_field_write(phy->pll_enable, PLL_ENABLE_STATE);
ret = regmap_field_write(phy->fields[PLL_ENABLE], PLL_ENABLE_STATE);
if (ret)
return ret;
return regmap_field_read_poll_timeout(phy->pll_ok, val, val, 1000,
PLL_LOCK_TIME);
return regmap_field_read_poll_timeout(phy->fields[PLL_OK], val, val,
1000, PLL_LOCK_TIME);
}
static void serdes_am654_disable_pll(struct serdes_am654 *phy)
......@@ -129,41 +261,39 @@ static void serdes_am654_disable_pll(struct serdes_am654 *phy)
struct device *dev = phy->dev;
int ret;
ret = regmap_field_write(phy->pll_enable, PLL_DISABLE_STATE);
ret = regmap_field_write(phy->fields[PLL_ENABLE], PLL_DISABLE_STATE);
if (ret)
dev_err(dev, "Failed to disable PLL\n");
}
static int serdes_am654_enable_txrx(struct serdes_am654 *phy)
{
int ret;
int ret = 0;
/* Enable TX */
ret = regmap_field_write(phy->tx0_enable, TX0_ENABLE_STATE);
if (ret)
return ret;
ret |= regmap_field_write(phy->fields[TX0_ENABLE], TX0_ENABLE_STATE);
/* Enable RX */
ret = regmap_field_write(phy->rx0_enable, RX0_ENABLE_STATE);
ret |= regmap_field_write(phy->fields[RX0_ENABLE], RX0_ENABLE_STATE);
if (ret)
return ret;
return -EIO;
return 0;
}
static int serdes_am654_disable_txrx(struct serdes_am654 *phy)
{
int ret;
int ret = 0;
/* Disable TX */
ret = regmap_field_write(phy->tx0_enable, TX0_DISABLE_STATE);
if (ret)
return ret;
ret |= regmap_field_write(phy->fields[TX0_ENABLE], TX0_DISABLE_STATE);
/* Disable RX */
ret = regmap_field_write(phy->rx0_enable, RX0_DISABLE_STATE);
ret |= regmap_field_write(phy->fields[RX0_ENABLE], RX0_DISABLE_STATE);
if (ret)
return ret;
return -EIO;
return 0;
}
......@@ -187,8 +317,8 @@ static int serdes_am654_power_on(struct phy *x)
return ret;
}
return regmap_field_read_poll_timeout(phy->cmu_ok_i_0, val, val,
SLEEP_TIME, PLL_LOCK_TIME);
return regmap_field_read_poll_timeout(phy->fields[CMU_OK_I_0], val,
val, SLEEP_TIME, PLL_LOCK_TIME);
}
static int serdes_am654_power_off(struct phy *x)
......@@ -286,19 +416,37 @@ static int serdes_am654_usb3_init(struct serdes_am654 *phy)
static int serdes_am654_pcie_init(struct serdes_am654 *phy)
{
int ret;
ret = regmap_field_write(phy->config_version, VERSION);
if (ret)
return ret;
int ret = 0;
ret = regmap_field_write(phy->cmu_master_cdn_o, 0x1);
if (ret)
return ret;
ret |= regmap_field_write(phy->fields[CMU_PLL_CTRL], 0x2);
ret |= regmap_field_write(phy->fields[AHB_PMA_CM_VCO_VBIAS_VREG], 0x98);
ret |= regmap_field_write(phy->fields[AHB_PMA_CM_VCO_BIAS_VREG], 0x98);
ret |= regmap_field_write(phy->fields[AHB_PMA_CM_SR], 0x45);
ret |= regmap_field_write(phy->fields[AHB_SSC_GEN_Z_O_20_13], 0xe);
ret |= regmap_field_write(phy->fields[LANE_PLL_CTRL_RXEQ_RXIDLE], 0x5);
ret |= regmap_field_write(phy->fields[AHB_PMA_LN_AGC_THSEL_VREGH], 0x83);
ret |= regmap_field_write(phy->fields[AHB_PMA_LN_GEN3_AGC_SD_THSEL], 0x83);
ret |= regmap_field_write(phy->fields[AHB_PMA_LN_RX_SELR_GEN3], 0x81);
ret |= regmap_field_write(phy->fields[AHB_PMA_LN_TX_DRV], 0x3b);
ret |= regmap_field_write(phy->fields[P2S_RBUF_PTR_DIFF], 0x3);
ret |= regmap_field_write(phy->fields[CONFIG_VERSION], VERSION_VAL);
ret |= regmap_field_write(phy->fields[COMRXEQ_MS_INIT_CTRL_7_0], 0xf);
ret |= regmap_field_write(phy->fields[COMRXEQ_HS_INIT_CAL_7_0], 0x4f);
ret |= regmap_field_write(phy->fields[COMRXEQ_MS_RECAL_CTRL_7_0], 0xf);
ret |= regmap_field_write(phy->fields[COMRXEQ_HS_RECAL_CTRL_7_0], 0x4f);
ret |= regmap_field_write(phy->fields[COMRXEQ_CSR_ATT_CONFIG], 0x7);
ret |= regmap_field_write(phy->fields[COMRXEQ_CSR_EBSTADAPT_WIN_LEN], 0x7f);
ret |= regmap_field_write(phy->fields[COMRXEQ_CTRL_3_4], 0xf);
ret |= regmap_field_write(phy->fields[COMRXEQ_CTRL_14_15_16], 0x9a);
ret |= regmap_field_write(phy->fields[COMRXEQ_CSR_DLEV_ERR_THRESH], 0x32);
ret |= regmap_field_write(phy->fields[COMRXEQ_CTRL_25], 0x80);
ret |= regmap_field_write(phy->fields[CSR_RXEQ_RATE_CHANGE_CAL_RUN_RATE2_O], 0xf);
ret |= regmap_field_write(phy->fields[COMRXEQ_HS_RCHANGE_CTRL_7_0], 0x4f);
ret |= regmap_field_write(phy->fields[CMU_MASTER_CDN], 0x1);
ret |= regmap_field_write(phy->fields[L1_MASTER_CDN], 0x2);
ret = regmap_field_write(phy->l1_master_cdn_o, 0x1);
if (ret)
return ret;
return -EIO;
return 0;
}
......@@ -320,20 +468,19 @@ static int serdes_am654_init(struct phy *x)
static int serdes_am654_reset(struct phy *x)
{
struct serdes_am654 *phy = phy_get_drvdata(x);
int ret;
int ret = 0;
serdes_am654_disable_pll(phy);
serdes_am654_disable_txrx(phy);
ret = regmap_field_write(phy->por_en, 0x1);
if (ret)
return ret;
ret |= regmap_field_write(phy->fields[POR_EN], 0x1);
mdelay(1);
ret = regmap_field_write(phy->por_en, 0x0);
ret |= regmap_field_write(phy->fields[POR_EN], 0x0);
if (ret)
return ret;
return -EIO;
return 0;
}
......@@ -587,66 +734,16 @@ static int serdes_am654_regfield_init(struct serdes_am654 *am654_phy)
{
struct regmap *regmap = am654_phy->regmap;
struct device *dev = am654_phy->dev;
int i;
am654_phy->cmu_master_cdn_o = devm_regmap_field_alloc(dev, regmap,
cmu_master_cdn_o);
if (IS_ERR(am654_phy->cmu_master_cdn_o)) {
dev_err(dev, "CMU_MASTER_CDN_O reg field init failed\n");
return PTR_ERR(am654_phy->cmu_master_cdn_o);
}
am654_phy->config_version = devm_regmap_field_alloc(dev, regmap,
config_version);
if (IS_ERR(am654_phy->config_version)) {
dev_err(dev, "CONFIG_VERSION reg field init failed\n");
return PTR_ERR(am654_phy->config_version);
}
am654_phy->l1_master_cdn_o = devm_regmap_field_alloc(dev, regmap,
l1_master_cdn_o);
if (IS_ERR(am654_phy->l1_master_cdn_o)) {
dev_err(dev, "L1_MASTER_CDN_O reg field init failed\n");
return PTR_ERR(am654_phy->l1_master_cdn_o);
}
am654_phy->cmu_ok_i_0 = devm_regmap_field_alloc(dev, regmap,
cmu_ok_i_0);
if (IS_ERR(am654_phy->cmu_ok_i_0)) {
dev_err(dev, "CMU_OK_I_0 reg field init failed\n");
return PTR_ERR(am654_phy->cmu_ok_i_0);
}
am654_phy->por_en = devm_regmap_field_alloc(dev, regmap, por_en);
if (IS_ERR(am654_phy->por_en)) {
dev_err(dev, "POR_EN reg field init failed\n");
return PTR_ERR(am654_phy->por_en);
}
am654_phy->tx0_enable = devm_regmap_field_alloc(dev, regmap,
tx0_enable);
if (IS_ERR(am654_phy->tx0_enable)) {
dev_err(dev, "TX0_ENABLE reg field init failed\n");
return PTR_ERR(am654_phy->tx0_enable);
}
am654_phy->rx0_enable = devm_regmap_field_alloc(dev, regmap,
rx0_enable);
if (IS_ERR(am654_phy->rx0_enable)) {
dev_err(dev, "RX0_ENABLE reg field init failed\n");
return PTR_ERR(am654_phy->rx0_enable);
}
am654_phy->pll_enable = devm_regmap_field_alloc(dev, regmap,
pll_enable);
if (IS_ERR(am654_phy->pll_enable)) {
dev_err(dev, "PLL_ENABLE reg field init failed\n");
return PTR_ERR(am654_phy->pll_enable);
}
am654_phy->pll_ok = devm_regmap_field_alloc(dev, regmap, pll_ok);
if (IS_ERR(am654_phy->pll_ok)) {
dev_err(dev, "PLL_OK reg field init failed\n");
return PTR_ERR(am654_phy->pll_ok);
for (i = 0; i < MAX_FIELDS; i++) {
am654_phy->fields[i] = devm_regmap_field_alloc(dev,
regmap,
serdes_am654_reg_fields[i]);
if (IS_ERR(am654_phy->fields[i])) {
dev_err(dev, "Unable to allocate regmap field %d\n", i);
return PTR_ERR(am654_phy->fields[i]);
}
}
return 0;
......
......@@ -11,6 +11,7 @@
#include <linux/module.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_net.h>
#include <linux/phy.h>
#include <linux/phy/phy.h>
......@@ -22,7 +23,7 @@
#define AM33XX_GMII_SEL_MODE_RGMII 2
enum {
PHY_GMII_SEL_PORT_MODE,
PHY_GMII_SEL_PORT_MODE = 0,
PHY_GMII_SEL_RGMII_ID_MODE,
PHY_GMII_SEL_RMII_IO_CLK_EN,
PHY_GMII_SEL_LAST,
......@@ -41,6 +42,7 @@ struct phy_gmii_sel_soc_data {
u32 num_ports;
u32 features;
const struct reg_field (*regfields)[PHY_GMII_SEL_LAST];
bool use_of_data;
};
struct phy_gmii_sel_priv {
......@@ -49,6 +51,8 @@ struct phy_gmii_sel_priv {
struct regmap *regmap;
struct phy_provider *phy_provider;
struct phy_gmii_sel_phy_priv *if_phys;
u32 num_ports;
u32 reg_offset;
};
static int phy_gmii_sel_mode(struct phy *phy, enum phy_mode mode, int submode)
......@@ -147,13 +151,9 @@ static const
struct reg_field phy_gmii_sel_fields_dra7[][PHY_GMII_SEL_LAST] = {
{
[PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x554, 0, 1),
[PHY_GMII_SEL_RGMII_ID_MODE] = REG_FIELD((~0), 0, 0),
[PHY_GMII_SEL_RMII_IO_CLK_EN] = REG_FIELD((~0), 0, 0),
},
{
[PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x554, 4, 5),
[PHY_GMII_SEL_RGMII_ID_MODE] = REG_FIELD((~0), 0, 0),
[PHY_GMII_SEL_RMII_IO_CLK_EN] = REG_FIELD((~0), 0, 0),
},
};
......@@ -172,16 +172,19 @@ struct phy_gmii_sel_soc_data phy_gmii_sel_soc_dm814 = {
static const
struct reg_field phy_gmii_sel_fields_am654[][PHY_GMII_SEL_LAST] = {
{
[PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x4040, 0, 1),
[PHY_GMII_SEL_RGMII_ID_MODE] = REG_FIELD((~0), 0, 0),
[PHY_GMII_SEL_RMII_IO_CLK_EN] = REG_FIELD((~0), 0, 0),
},
{ [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x0, 0, 2), },
{ [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x4, 0, 2), },
{ [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x8, 0, 2), },
{ [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0xC, 0, 2), },
{ [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x10, 0, 2), },
{ [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x14, 0, 2), },
{ [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x18, 0, 2), },
{ [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x1C, 0, 2), },
};
static const
struct phy_gmii_sel_soc_data phy_gmii_sel_soc_am654 = {
.num_ports = 1,
.use_of_data = true,
.regfields = phy_gmii_sel_fields_am654,
};
......@@ -228,7 +231,7 @@ static struct phy *phy_gmii_sel_of_xlate(struct device *dev,
if (priv->soc_data->features & BIT(PHY_GMII_SEL_RMII_IO_CLK_EN) &&
args->args_count < 2)
return ERR_PTR(-EINVAL);
if (phy_id > priv->soc_data->num_ports)
if (phy_id > priv->num_ports)
return ERR_PTR(-EINVAL);
if (phy_id != priv->if_phys[phy_id - 1].id)
return ERR_PTR(-EINVAL);
......@@ -242,68 +245,97 @@ static struct phy *phy_gmii_sel_of_xlate(struct device *dev,
return priv->if_phys[phy_id].if_phy;
}
static int phy_gmii_sel_init_ports(struct phy_gmii_sel_priv *priv)
static int phy_gmii_init_phy(struct phy_gmii_sel_priv *priv, int port,
struct phy_gmii_sel_phy_priv *if_phy)
{
const struct phy_gmii_sel_soc_data *soc_data = priv->soc_data;
struct device *dev = priv->dev;
const struct reg_field *fields;
struct regmap_field *regfield;
struct reg_field field;
int ret;
if_phy->id = port;
if_phy->priv = priv;
fields = soc_data->regfields[port - 1];
field = *fields++;
field.reg += priv->reg_offset;
dev_dbg(dev, "%s field %x %d %d\n", __func__,
field.reg, field.msb, field.lsb);
regfield = devm_regmap_field_alloc(dev, priv->regmap, field);
if (IS_ERR(regfield))
return PTR_ERR(regfield);
if_phy->fields[PHY_GMII_SEL_PORT_MODE] = regfield;
field = *fields++;
field.reg += priv->reg_offset;
if (soc_data->features & BIT(PHY_GMII_SEL_RGMII_ID_MODE)) {
regfield = devm_regmap_field_alloc(dev,
priv->regmap,
field);
if (IS_ERR(regfield))
return PTR_ERR(regfield);
if_phy->fields[PHY_GMII_SEL_RGMII_ID_MODE] = regfield;
dev_dbg(dev, "%s field %x %d %d\n", __func__,
field.reg, field.msb, field.lsb);
}
field = *fields;
field.reg += priv->reg_offset;
if (soc_data->features & BIT(PHY_GMII_SEL_RMII_IO_CLK_EN)) {
regfield = devm_regmap_field_alloc(dev,
priv->regmap,
field);
if (IS_ERR(regfield))
return PTR_ERR(regfield);
if_phy->fields[PHY_GMII_SEL_RMII_IO_CLK_EN] = regfield;
dev_dbg(dev, "%s field %x %d %d\n", __func__,
field.reg, field.msb, field.lsb);
}
if_phy->if_phy = devm_phy_create(dev,
priv->dev->of_node,
&phy_gmii_sel_ops);
if (IS_ERR(if_phy->if_phy)) {
ret = PTR_ERR(if_phy->if_phy);
dev_err(dev, "Failed to create phy%d %d\n", port, ret);
return ret;
}
phy_set_drvdata(if_phy->if_phy, if_phy);
return 0;
}
static int phy_gmii_sel_init_ports(struct phy_gmii_sel_priv *priv)
{
const struct phy_gmii_sel_soc_data *soc_data = priv->soc_data;
struct phy_gmii_sel_phy_priv *if_phys;
int i, num_ports, ret;
struct device *dev = priv->dev;
int i, ret;
num_ports = priv->soc_data->num_ports;
if (soc_data->use_of_data) {
const __be32 *offset;
u64 size;
if_phys = devm_kcalloc(priv->dev, num_ports,
offset = of_get_address(dev->of_node, 0, &size, NULL);
priv->num_ports = size / sizeof(u32);
if (!priv->num_ports)
return -EINVAL;
priv->reg_offset = __be32_to_cpu(*offset);
}
if_phys = devm_kcalloc(dev, priv->num_ports,
sizeof(*if_phys), GFP_KERNEL);
if (!if_phys)
return -ENOMEM;
dev_dbg(dev, "%s %d\n", __func__, num_ports);
for (i = 0; i < num_ports; i++) {
const struct reg_field *field;
struct regmap_field *regfield;
dev_dbg(dev, "%s %d\n", __func__, priv->num_ports);
if_phys[i].id = i + 1;
if_phys[i].priv = priv;
field = &soc_data->regfields[i][PHY_GMII_SEL_PORT_MODE];
dev_dbg(dev, "%s field %x %d %d\n", __func__,
field->reg, field->msb, field->lsb);
regfield = devm_regmap_field_alloc(dev, priv->regmap, *field);
if (IS_ERR(regfield))
return PTR_ERR(regfield);
if_phys[i].fields[PHY_GMII_SEL_PORT_MODE] = regfield;
field = &soc_data->regfields[i][PHY_GMII_SEL_RGMII_ID_MODE];
if (field->reg != (~0)) {
regfield = devm_regmap_field_alloc(dev,
priv->regmap,
*field);
if (IS_ERR(regfield))
return PTR_ERR(regfield);
if_phys[i].fields[PHY_GMII_SEL_RGMII_ID_MODE] =
regfield;
}
field = &soc_data->regfields[i][PHY_GMII_SEL_RMII_IO_CLK_EN];
if (field->reg != (~0)) {
regfield = devm_regmap_field_alloc(dev,
priv->regmap,
*field);
if (IS_ERR(regfield))
return PTR_ERR(regfield);
if_phys[i].fields[PHY_GMII_SEL_RMII_IO_CLK_EN] =
regfield;
}
if_phys[i].if_phy = devm_phy_create(dev,
priv->dev->of_node,
&phy_gmii_sel_ops);
if (IS_ERR(if_phys[i].if_phy)) {
ret = PTR_ERR(if_phys[i].if_phy);
dev_err(dev, "Failed to create phy%d %d\n", i, ret);
for (i = 0; i < priv->num_ports; i++) {
ret = phy_gmii_init_phy(priv, i + 1, &if_phys[i]);
if (ret)
return ret;
}
phy_set_drvdata(if_phys[i].if_phy, &if_phys[i]);
}
priv->if_phys = if_phys;
......@@ -328,6 +360,7 @@ static int phy_gmii_sel_probe(struct platform_device *pdev)
priv->dev = &pdev->dev;
priv->soc_data = of_id->data;
priv->num_ports = priv->soc_data->num_ports;
priv->regmap = syscon_node_to_regmap(node->parent);
if (IS_ERR(priv->regmap)) {
......
......@@ -20,7 +20,6 @@
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/reset-controller.h>
#include <dt-bindings/phy/phy.h>
#define WIZ_SERDES_CTRL 0x404
#define WIZ_SERDES_TOP_CTRL 0x408
......
......@@ -6,23 +6,23 @@
* Author: Kishon Vijay Abraham I <kishon@ti.com>
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/phy/omap_usb.h>
#include <linux/usb/phy_companion.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/pm_runtime.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/phy/omap_control_phy.h>
#include <linux/phy/omap_usb.h>
#include <linux/phy/phy.h>
#include <linux/mfd/syscon.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
#include <linux/sys_soc.h>
#include <linux/usb/phy_companion.h>
#define USB2PHY_ANA_CONFIG1 0x4c
#define USB2PHY_DISCON_BYP_LATCH BIT(31)
......@@ -89,7 +89,7 @@ static inline void omap_usb_writel(void __iomem *addr, unsigned int offset,
}
/**
* omap_usb2_set_comparator - links the comparator present in the sytem with
* omap_usb2_set_comparator - links the comparator present in the system with
* this phy
* @comparator - the companion phy(comparator) for this phy
*
......@@ -142,7 +142,7 @@ static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
}
static int omap_usb_set_peripheral(struct usb_otg *otg,
struct usb_gadget *gadget)
struct usb_gadget *gadget)
{
otg->gadget = gadget;
if (!gadget)
......@@ -409,7 +409,7 @@ static int omap_usb2_probe(struct platform_device *pdev)
return PTR_ERR(phy->phy_base);
phy->syscon_phy_power = syscon_regmap_lookup_by_phandle(node,
"syscon-phy-power");
"syscon-phy-power");
if (IS_ERR(phy->syscon_phy_power)) {
dev_dbg(&pdev->dev,
"can't get syscon-phy-power, using control device\n");
......@@ -438,7 +438,6 @@ static int omap_usb2_probe(struct platform_device *pdev)
}
}
phy->wkupclk = devm_clk_get(phy->dev, "wkupclk");
if (IS_ERR(phy->wkupclk)) {
if (PTR_ERR(phy->wkupclk) == -EPROBE_DEFER)
......@@ -452,10 +451,10 @@ static int omap_usb2_probe(struct platform_device *pdev)
if (PTR_ERR(phy->wkupclk) != -EPROBE_DEFER)
dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n");
return PTR_ERR(phy->wkupclk);
} else {
dev_warn(&pdev->dev,
"found usb_phy_cm_clk32k, please fix DTS\n");
}
dev_warn(&pdev->dev,
"found usb_phy_cm_clk32k, please fix DTS\n");
}
phy->optclk = devm_clk_get(phy->dev, "refclk");
......@@ -504,7 +503,6 @@ static int omap_usb2_probe(struct platform_device *pdev)
return PTR_ERR(phy_provider);
}
usb_add_phy_dev(&phy->phy);
return 0;
......
......@@ -42,8 +42,6 @@ source "drivers/staging/media/tegra-video/Kconfig"
source "drivers/staging/media/ipu3/Kconfig"
source "drivers/staging/media/phy-rockchip-dphy-rx0/Kconfig"
source "drivers/staging/media/rkisp1/Kconfig"
if MEDIA_ANALOG_TV_SUPPORT
......
......@@ -10,6 +10,5 @@ obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/
obj-$(CONFIG_TEGRA_VDE) += tegra-vde/
obj-$(CONFIG_VIDEO_HANTRO) += hantro/
obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/
obj-$(CONFIG_PHY_ROCKCHIP_DPHY_RX0) += phy-rockchip-dphy-rx0/
obj-$(CONFIG_VIDEO_ROCKCHIP_ISP1) += rkisp1/
obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
# SPDX-License-Identifier: GPL-2.0-only
config PHY_ROCKCHIP_DPHY_RX0
tristate "Rockchip MIPI Synopsys DPHY RX0 driver"
depends on ARCH_ROCKCHIP || COMPILE_TEST
select GENERIC_PHY_MIPI_DPHY
select GENERIC_PHY
help
Enable this to support the Rockchip MIPI Synopsys DPHY RX0
associated to the Rockchip ISP module present in RK3399 SoCs.
To compile this driver as a module, choose M here: the module
will be called phy-rockchip-dphy-rx0.
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_PHY_ROCKCHIP_DPHY_RX0) += phy-rockchip-dphy-rx0.o
The main reason for keeping this in staging is because the only driver
that uses this is rkisp1, which is also in staging. It should be moved together
with rkisp1.
Please CC patches to Linux Media <linux-media@vger.kernel.org> and
Helen Koike <helen.koike@collabora.com>.
/* SPDX-License-Identifier: GPL-2.0 */
/*
* This header provides constants for Cadence Torrent SERDES.
*/
#ifndef _DT_BINDINGS_TORRENT_SERDES_H
#define _DT_BINDINGS_TORRENT_SERDES_H
#define TORRENT_SERDES_NO_SSC 0
#define TORRENT_SERDES_EXTERNAL_SSC 1
#define TORRENT_SERDES_INTERNAL_SSC 2
#endif /* _DT_BINDINGS_TORRENT_SERDES_H */
......@@ -19,5 +19,6 @@
#define PHY_TYPE_DP 6
#define PHY_TYPE_XPCS 7
#define PHY_TYPE_SGMII 8
#define PHY_TYPE_QSGMII 9
#endif /* _DT_BINDINGS_PHY */
......@@ -115,10 +115,12 @@ struct phy_ops {
/**
* struct phy_attrs - represents phy attributes
* @bus_width: Data path width implemented by PHY
* @max_link_rate: Maximum link rate supported by PHY (in Mbps)
* @mode: PHY mode
*/
struct phy_attrs {
u32 bus_width;
u32 max_link_rate;
enum phy_mode mode;
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment