summaryrefslogtreecommitdiffstats
path: root/drivers/phy
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/phy')
-rw-r--r--drivers/phy/Kconfig5
-rw-r--r--drivers/phy/Makefile2
-rw-r--r--drivers/phy/freescale/Kconfig9
-rw-r--r--drivers/phy/freescale/Makefile1
-rw-r--r--drivers/phy/freescale/phy-fsl-imx8mq-usb.c96
-rw-r--r--drivers/phy/phy-core.c74
-rw-r--r--drivers/phy/phy-stm32-usbphyc.c536
-rw-r--r--drivers/phy/rockchip/Kconfig20
-rw-r--r--drivers/phy/rockchip/Makefile4
-rw-r--r--drivers/phy/rockchip/phy-rockchip-inno-usb2.c993
-rw-r--r--drivers/phy/rockchip/phy-rockchip-naneng-combphy.c870
-rw-r--r--drivers/phy/rockchip/phy-rockchip-snps-pcie3.c317
-rw-r--r--drivers/phy/usb-nop-xceiv.c28
13 files changed, 2796 insertions, 159 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index b5c8e98b9e..52c7929149 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# PHY
#
@@ -23,10 +24,12 @@ config USB_NOP_XCEIV
phy programming such as ISP1x04 etc.
source "drivers/phy/freescale/Kconfig"
+source "drivers/phy/rockchip/Kconfig"
config PHY_STM32_USBPHYC
tristate "STM32 USB HS PHY Controller"
- depends on ARCH_STM32MP
+ depends on ARCH_STM32MP || COMPILE_TEST
+ depends on COMMON_CLK
help
Enable this to support the High-Speed USB transceivers that are part
of some STMicroelectronics STM32 SoCs.
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 684aaed75a..37e5f32d6a 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for the phy drivers.
#
@@ -6,3 +7,4 @@ obj-$(CONFIG_GENERIC_PHY) += phy-core.o
obj-$(CONFIG_USB_NOP_XCEIV) += usb-nop-xceiv.o
obj-y += freescale/
obj-$(CONFIG_PHY_STM32_USBPHYC) += phy-stm32-usbphyc.o
+obj-y += rockchip/
diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig
index 8e56dd7e79..04e8bcf188 100644
--- a/drivers/phy/freescale/Kconfig
+++ b/drivers/phy/freescale/Kconfig
@@ -1,4 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
config PHY_FSL_IMX8MQ_USB
- bool "Freescale i.MX8M USB3 PHY"
- default SOC_IMX8MQ
+ bool "Freescale i.MX8MQ/P USB3 PHY"
+ default ARCH_IMX8MQ || ARCH_IMX8MP
+ help
+ Enable this to add support for the USB PHY found on
+ the i.MX8M Quad and Plus.
diff --git a/drivers/phy/freescale/Makefile b/drivers/phy/freescale/Makefile
index dc2b3f1f2f..a459a44f6e 100644
--- a/drivers/phy/freescale/Makefile
+++ b/drivers/phy/freescale/Makefile
@@ -1 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_PHY_FSL_IMX8MQ_USB) += phy-fsl-imx8mq-usb.o
diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
index 6d60eacd7f..b9f9fad1fd 100644
--- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
+++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
@@ -1,21 +1,25 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: GPL-2.0-or-later
/* Copyright (c) 2017 NXP. */
#include <common.h>
+#include <driver.h>
+#include <errno.h>
#include <init.h>
#include <io.h>
-#include <of.h>
-#include <errno.h>
-#include <driver.h>
-#include <malloc.h>
-#include <usb/phy.h>
-#include <linux/phy/phy.h>
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/err.h>
+#include <linux/phy/phy.h>
+#include <malloc.h>
+#include <of_device.h>
+#include <of.h>
+#include <linux/usb/phy.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)
@@ -26,6 +30,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;
@@ -59,6 +68,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);
@@ -75,13 +122,28 @@ 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,
};
-static struct phy *imx8mq_usb_phy_xlate(struct device_d *dev,
+static const struct phy_ops imx8mp_usb_phy_ops = {
+ .init = imx8mp_usb_phy_init,
+ .power_on = imx8mq_phy_power_on,
+ .power_off = imx8mq_phy_power_off,
+};
+
+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 struct phy *imx8mq_usb_phy_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct imx8mq_usb_phy *imx_phy = dev->priv;
@@ -89,10 +151,11 @@ static struct phy *imx8mq_usb_phy_xlate(struct device_d *dev,
return imx_phy->phy;
}
-static int imx8mq_usb_phy_probe(struct device_d *dev)
+static int imx8mq_usb_phy_probe(struct device *dev)
{
struct phy_provider *phy_provider;
struct imx8mq_usb_phy *imx_phy;
+ const struct phy_ops *phy_ops;
imx_phy = xzalloc(sizeof(*imx_phy));
@@ -106,7 +169,11 @@ static int imx8mq_usb_phy_probe(struct device_d *dev)
if (IS_ERR(imx_phy->base))
return PTR_ERR(imx_phy->base);
- imx_phy->phy = phy_create(dev, NULL, &imx8mq_usb_phy_ops);
+ phy_ops = of_device_get_match_data(dev);
+ if (!phy_ops)
+ return -EINVAL;
+
+ imx_phy->phy = phy_create(dev, NULL, phy_ops);
if (IS_ERR(imx_phy->phy))
return PTR_ERR(imx_phy->phy);
@@ -117,12 +184,7 @@ static int imx8mq_usb_phy_probe(struct device_d *dev)
return PTR_ERR_OR_ZERO(phy_provider);
}
-static const struct of_device_id imx8mq_usb_phy_of_match[] = {
- {.compatible = "fsl,imx8mq-usb-phy",},
- { },
-};
-
-static struct driver_d imx8mq_usb_phy_driver = {
+static struct driver imx8mq_usb_phy_driver = {
.name = "imx8mq-usb-phy",
.probe = imx8mq_usb_phy_probe,
.of_compatible = DRV_OF_COMPAT(imx8mq_usb_phy_of_match),
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index ff6e35d160..0a2f1b0d11 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* phy-core.c -- Generic Phy framework.
*
@@ -5,17 +6,12 @@
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
*
* Author: Kishon Vijay Abraham I <kishon@ti.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#include <common.h>
#include <malloc.h>
#include <linux/phy/phy.h>
-#include <usb/phy.h>
+#include <linux/usb/phy.h>
static LIST_HEAD(phy_provider_list);
static int phy_ida;
@@ -28,7 +24,7 @@ static int phy_ida;
*
* Called to create a phy using phy framework.
*/
-struct phy *phy_create(struct device_d *dev, struct device_node *node,
+struct phy *phy_create(struct device *dev, struct device_node *node,
const struct phy_ops *ops)
{
int ret;
@@ -47,7 +43,7 @@ struct phy *phy_create(struct device_d *dev, struct device_node *node,
dev_set_name(&phy->dev, "phy");
phy->dev.id = id;
phy->dev.parent = dev;
- phy->dev.device_node = node ?: dev->device_node;
+ phy->dev.of_node = node ?: dev->of_node;
phy->id = id;
phy->ops = ops;
@@ -83,9 +79,9 @@ free_ida:
* This is used in the case of dt boot for finding the phy instance from
* phy provider.
*/
-struct phy_provider *__of_phy_provider_register(struct device_d *dev,
- struct phy * (*of_xlate)(struct device_d *dev,
- struct of_phandle_args *args))
+struct phy_provider *__of_phy_provider_register(struct device *dev,
+ struct phy * (*of_xlate)(struct device *dev,
+ struct of_phandle_args *args))
{
struct phy_provider *phy_provider;
@@ -173,8 +169,6 @@ int phy_power_on(struct phy *phy)
dev_err(&phy->dev, "phy poweron failed --> %d\n", ret);
goto out;
}
- } else {
- ret = 0; /* Override possible ret == -ENOTSUPP */
}
++phy->power_count;
@@ -209,6 +203,21 @@ int phy_power_off(struct phy *phy)
return 0;
}
+int phy_set_mode_ext(struct phy *phy, enum phy_mode mode, int submode)
+{
+ int ret;
+
+ if (!phy || !phy->ops->set_mode)
+ return 0;
+
+ ret = phy->ops->set_mode(phy, mode, submode);
+ if (!ret)
+ phy->attrs.mode = mode;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_set_mode_ext);
+
struct usb_phy *phy_to_usbphy(struct phy *phy)
{
if (!phy)
@@ -224,12 +233,17 @@ static struct phy_provider *of_phy_provider_lookup(struct device_node *node)
{
struct phy_provider *phy_provider;
struct device_node *child;
+ int ret;
+
+ ret = of_device_ensure_probed(node);
+ if (ret)
+ return ERR_PTR(ret);
list_for_each_entry(phy_provider, &phy_provider_list, list) {
- if (phy_provider->dev->device_node == node)
+ if (phy_provider->dev->of_node == node)
return phy_provider;
- for_each_child_of_node(phy_provider->dev->device_node, child)
+ for_each_child_of_node(phy_provider->dev->of_node, child)
if (child == node)
return phy_provider;
}
@@ -296,27 +310,27 @@ struct phy *of_phy_get(struct device_node *np, const char *con_id)
* -ENODEV if there is no such phy. The caller is responsible for
* calling phy_put() to release that count.
*/
-struct phy *of_phy_get_by_phandle(struct device_d *dev, const char *phandle,
+struct phy *of_phy_get_by_phandle(struct device *dev, const char *phandle,
u8 index)
{
struct device_node *np;
struct phy_provider *phy_provider;
- if (!dev->device_node) {
+ if (!dev->of_node) {
dev_dbg(dev, "device does not have a device node entry\n");
return ERR_PTR(-EINVAL);
}
- np = of_parse_phandle(dev->device_node, phandle, index);
+ np = of_parse_phandle(dev->of_node, phandle, index);
if (!np) {
- dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle,
- dev->device_node->full_name);
+ dev_dbg(dev, "failed to get %s phandle in %pOF node\n", phandle,
+ dev->of_node);
return ERR_PTR(-ENODEV);
}
phy_provider = of_phy_provider_lookup(np);
if (IS_ERR(phy_provider)) {
- return ERR_PTR(-ENODEV);
+ return ERR_CAST(phy_provider);
}
return phy_provider->of_xlate(phy_provider->dev, NULL);
@@ -332,7 +346,7 @@ struct phy *of_phy_get_by_phandle(struct device_d *dev, const char *phandle,
* -ENODEV if there is no such phy. The caller is responsible for
* calling phy_put() to release that count.
*/
-struct phy *phy_get(struct device_d *dev, const char *string)
+struct phy *phy_get(struct device *dev, const char *string)
{
int index = 0;
struct phy *phy = ERR_PTR(-ENODEV);
@@ -342,10 +356,10 @@ struct phy *phy_get(struct device_d *dev, const char *string)
return ERR_PTR(-EINVAL);
}
- if (dev->device_node) {
- index = of_property_match_string(dev->device_node, "phy-names",
- string);
- phy = _of_phy_get(dev->device_node, index);
+ if (dev->of_node) {
+ index = of_property_match_string(dev->of_node, "phy-names",
+ string);
+ phy = _of_phy_get(dev->of_node, index);
}
return phy;
@@ -361,7 +375,7 @@ struct phy *phy_get(struct device_d *dev, const char *string)
* NULL if there is no such phy. The caller is responsible for
* calling phy_put() to release that count.
*/
-struct phy *phy_optional_get(struct device_d *dev, const char *string)
+struct phy *phy_optional_get(struct device *dev, const char *string)
{
struct phy *phy = phy_get(dev, string);
@@ -378,10 +392,10 @@ struct phy *phy_optional_get(struct device_d *dev, const char *string)
*
* Gets the phy using _of_phy_get()
*/
-struct phy *phy_get_by_index(struct device_d *dev, int index)
+struct phy *phy_get_by_index(struct device *dev, int index)
{
- if (!dev->device_node)
+ if (!dev->of_node)
return ERR_PTR(-ENODEV);
- return _of_phy_get(dev->device_node, index);
+ return _of_phy_get(dev->of_node, index);
}
diff --git a/drivers/phy/phy-stm32-usbphyc.c b/drivers/phy/phy-stm32-usbphyc.c
index 093842fe14..6bac5e1e59 100644
--- a/drivers/phy/phy-stm32-usbphyc.c
+++ b/drivers/phy/phy-stm32-usbphyc.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics STM32 USB PHY Controller driver
*
@@ -10,13 +10,18 @@
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <io.h>
+#include <linux/iopoll.h>
#include <linux/phy/phy.h>
#include <linux/reset.h>
-#include <asm-generic/div64.h>
-#include <usb/phy.h>
+#include <linux/math64.h>
+#include <asm-generic/atomic.h>
+#include <linux/usb/phy.h>
+#include <linux/units.h>
#define STM32_USBPHYC_PLL 0x0
#define STM32_USBPHYC_MISC 0x8
+#define STM32_USBPHYC_MONITOR(X) (0x108 + ((X) * 0x100))
+#define STM32_USBPHYC_TUNE(X) (0x10C + ((X) * 0x100))
#define STM32_USBPHYC_VERSION 0x3F4
/* STM32_USBPHYC_PLL bit fields */
@@ -32,23 +37,96 @@
/* STM32_USBPHYC_MISC bit fields */
#define SWITHOST BIT(0)
-/* STM32_USBPHYC_VERSION bit fields */
-#define MINREV GENMASK(3, 0)
-#define MAJREV GENMASK(7, 4)
+/* STM32_USBPHYC_MONITOR bit fields */
+#define STM32_USBPHYC_MON_OUT GENMASK(3, 0)
+#define STM32_USBPHYC_MON_SEL GENMASK(8, 4)
+#define STM32_USBPHYC_MON_SEL_LOCKP 0x1F
+#define STM32_USBPHYC_MON_OUT_LOCKP BIT(3)
+
+/* STM32_USBPHYC_TUNE bit fields */
+#define INCURREN BIT(0)
+#define INCURRINT BIT(1)
+#define LFSCAPEN BIT(2)
+#define HSDRVSLEW BIT(3)
+#define HSDRVDCCUR BIT(4)
+#define HSDRVDCLEV BIT(5)
+#define HSDRVCURINCR BIT(6)
+#define FSDRVRFADJ BIT(7)
+#define HSDRVRFRED BIT(8)
+#define HSDRVCHKITRM GENMASK(12, 9)
+#define HSDRVCHKZTRM GENMASK(14, 13)
+#define OTPCOMP GENMASK(19, 15)
+#define SQLCHCTL GENMASK(21, 20)
+#define HDRXGNEQEN BIT(22)
+#define HSRXOFF GENMASK(24, 23)
+#define HSFALLPREEM BIT(25)
+#define SHTCCTCTLPROT BIT(26)
+#define STAGSEL BIT(27)
+
+enum boosting_vals {
+ BOOST_1000_UA = 1000,
+ BOOST_2000_UA = 2000,
+};
+
+enum dc_level_vals {
+ DC_NOMINAL,
+ DC_PLUS_5_TO_7_MV,
+ DC_PLUS_10_TO_14_MV,
+ DC_MINUS_5_TO_7_MV,
+ DC_MAX,
+};
+
+enum current_trim {
+ CUR_NOMINAL,
+ CUR_PLUS_1_56_PCT,
+ CUR_PLUS_3_12_PCT,
+ CUR_PLUS_4_68_PCT,
+ CUR_PLUS_6_24_PCT,
+ CUR_PLUS_7_8_PCT,
+ CUR_PLUS_9_36_PCT,
+ CUR_PLUS_10_92_PCT,
+ CUR_PLUS_12_48_PCT,
+ CUR_PLUS_14_04_PCT,
+ CUR_PLUS_15_6_PCT,
+ CUR_PLUS_17_16_PCT,
+ CUR_PLUS_19_01_PCT,
+ CUR_PLUS_20_58_PCT,
+ CUR_PLUS_22_16_PCT,
+ CUR_PLUS_23_73_PCT,
+ CUR_MAX,
+};
+
+enum impedance_trim {
+ IMP_NOMINAL,
+ IMP_MINUS_2_OHMS,
+ IMP_MINUS_4_OMHS,
+ IMP_MINUS_6_OHMS,
+ IMP_MAX,
+};
+
+enum squelch_level {
+ SQLCH_NOMINAL,
+ SQLCH_PLUS_7_MV,
+ SQLCH_MINUS_5_MV,
+ SQLCH_PLUS_14_MV,
+ SQLCH_MAX,
+};
-static const char * const supplies_names[] = {
- "vdda1v1", /* 1V1 */
- "vdda1v8", /* 1V8 */
+enum rx_offset {
+ NO_RX_OFFSET,
+ RX_OFFSET_PLUS_5_MV,
+ RX_OFFSET_PLUS_10_MV,
+ RX_OFFSET_MINUS_5_MV,
+ RX_OFFSET_MAX,
};
-#define NUM_SUPPLIES ARRAY_SIZE(supplies_names)
+/* STM32_USBPHYC_VERSION bit fields */
+#define MINREV GENMASK(3, 0)
+#define MAJREV GENMASK(7, 4)
-#define PLL_LOCK_TIME_US 100
-#define PLL_PWR_DOWN_TIME_US 5
#define PLL_FVCO_MHZ 2880
#define PLL_INFF_MIN_RATE_HZ 19200000
#define PLL_INFF_MAX_RATE_HZ 38400000
-#define HZ_PER_MHZ 1000000L
struct pll_params {
u8 ndiv;
@@ -58,28 +136,69 @@ struct pll_params {
struct stm32_usbphyc_phy {
struct phy *phy;
struct stm32_usbphyc *usbphyc;
- struct regulator_bulk_data supplies[NUM_SUPPLIES];
+ struct regulator *vbus;
u32 index;
bool active;
+ u32 tune;
};
struct stm32_usbphyc {
- struct device_d *dev;
+ struct device *dev;
void __iomem *base;
struct clk *clk;
+ struct reset_control *rst;
struct stm32_usbphyc_phy **phys;
int nphys;
+ struct regulator *vdda1v1;
+ struct regulator *vdda1v8;
+ atomic_t n_pll_cons;
+ struct clk_hw clk48_hw;
int switch_setup;
};
static inline void stm32_usbphyc_set_bits(void __iomem *reg, u32 bits)
{
- writel(readl(reg) | bits, reg);
+ writel_relaxed(readl_relaxed(reg) | bits, reg);
}
static inline void stm32_usbphyc_clr_bits(void __iomem *reg, u32 bits)
{
- writel(readl(reg) & ~bits, reg);
+ writel_relaxed(readl_relaxed(reg) & ~bits, reg);
+}
+
+static int stm32_usbphyc_regulators_enable(struct stm32_usbphyc *usbphyc)
+{
+ int ret;
+
+ ret = regulator_enable(usbphyc->vdda1v1);
+ if (ret)
+ return ret;
+
+ ret = regulator_enable(usbphyc->vdda1v8);
+ if (ret)
+ goto vdda1v1_disable;
+
+ return 0;
+
+vdda1v1_disable:
+ regulator_disable(usbphyc->vdda1v1);
+
+ return ret;
+}
+
+static int stm32_usbphyc_regulators_disable(struct stm32_usbphyc *usbphyc)
+{
+ int ret;
+
+ ret = regulator_disable(usbphyc->vdda1v8);
+ if (ret)
+ return ret;
+
+ ret = regulator_disable(usbphyc->vdda1v1);
+ if (ret)
+ return ret;
+
+ return 0;
}
static void stm32_usbphyc_get_pll_params(u32 clk_rate,
@@ -132,7 +251,7 @@ static int stm32_usbphyc_pll_init(struct stm32_usbphyc *usbphyc)
if (pll_params.frac)
usbphyc_pll |= PLLFRACCTL | frac;
- writel(usbphyc_pll, usbphyc->base + STM32_USBPHYC_PLL);
+ writel_relaxed(usbphyc_pll, usbphyc->base + STM32_USBPHYC_PLL);
dev_dbg(usbphyc->dev, "input clk freq=%dHz, ndiv=%lu, frac=%lu\n",
clk_rate, FIELD_GET(PLLNDIV, usbphyc_pll),
@@ -141,83 +260,117 @@ static int stm32_usbphyc_pll_init(struct stm32_usbphyc *usbphyc)
return 0;
}
-static bool stm32_usbphyc_has_one_phy_active(struct stm32_usbphyc *usbphyc)
+static int __stm32_usbphyc_pll_disable(struct stm32_usbphyc *usbphyc)
{
- int i;
+ void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL;
+ u32 pllen;
- for (i = 0; i < usbphyc->nphys; i++)
- if (usbphyc->phys[i]->active)
- return true;
+ stm32_usbphyc_clr_bits(pll_reg, PLLEN);
- return false;
+ /* Wait for minimum width of powerdown pulse (ENABLE = Low) */
+ if (readl_relaxed_poll_timeout(pll_reg, pllen, !(pllen & PLLEN), 50))
+ dev_err(usbphyc->dev, "PLL not reset\n");
+
+ return stm32_usbphyc_regulators_disable(usbphyc);
+}
+
+static int stm32_usbphyc_pll_disable(struct stm32_usbphyc *usbphyc)
+{
+ /* Check if a phy port is still active or clk48 in use */
+ if (atomic_dec_return(&usbphyc->n_pll_cons) != 1)
+ return 0;
+
+ return __stm32_usbphyc_pll_disable(usbphyc);
}
static int stm32_usbphyc_pll_enable(struct stm32_usbphyc *usbphyc)
{
void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL;
- bool pllen = (readl(pll_reg) & PLLEN);
+ bool pllen = readl_relaxed(pll_reg) & PLLEN;
int ret;
- /* Check if one phy port has already configured the pll */
- if (pllen && stm32_usbphyc_has_one_phy_active(usbphyc))
+ /*
+ * Check if a phy port or clk48 prepare has configured the pll
+ * and ensure the PLL is enabled
+ */
+ if (atomic_inc_return(&usbphyc->n_pll_cons) > 1 && pllen)
return 0;
if (pllen) {
- stm32_usbphyc_clr_bits(pll_reg, PLLEN);
- /* Wait for minimum width of powerdown pulse (ENABLE = Low) */
- udelay(PLL_PWR_DOWN_TIME_US);
+ /*
+ * PLL shouldn't be enabled without known consumer,
+ * disable it and reinit n_pll_cons
+ */
+ dev_warn(usbphyc->dev, "PLL enabled without known consumers\n");
+
+ ret = __stm32_usbphyc_pll_disable(usbphyc);
+ if (ret)
+ goto dec_n_pll_cons;
}
+ ret = stm32_usbphyc_regulators_enable(usbphyc);
+ if (ret)
+ goto dec_n_pll_cons;
+
ret = stm32_usbphyc_pll_init(usbphyc);
if (ret)
- return ret;
+ goto reg_disable;
stm32_usbphyc_set_bits(pll_reg, PLLEN);
- /* Wait for maximum lock time */
- udelay(PLL_LOCK_TIME_US);
-
- if (!(readl(pll_reg) & PLLEN)) {
- dev_err(usbphyc->dev, "PLLEN not set\n");
- return -EIO;
- }
-
return 0;
-}
-
-static int stm32_usbphyc_pll_disable(struct stm32_usbphyc *usbphyc)
-{
- void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL;
- /* Check if other phy port active */
- if (stm32_usbphyc_has_one_phy_active(usbphyc))
- return 0;
-
- stm32_usbphyc_clr_bits(pll_reg, PLLEN);
- /* Wait for minimum width of powerdown pulse (ENABLE = Low) */
- udelay(PLL_PWR_DOWN_TIME_US);
+reg_disable:
+ stm32_usbphyc_regulators_disable(usbphyc);
- if (readl(pll_reg) & PLLEN) {
- dev_err(usbphyc->dev, "PLL not reset\n");
- return -EIO;
- }
+dec_n_pll_cons:
+ atomic_dec(&usbphyc->n_pll_cons);
- return 0;
+ return ret;
}
static int stm32_usbphyc_phy_init(struct phy *phy)
{
struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
struct stm32_usbphyc *usbphyc = usbphyc_phy->usbphyc;
+ u32 reg_mon = STM32_USBPHYC_MONITOR(usbphyc_phy->index);
+ u32 monsel = FIELD_PREP(STM32_USBPHYC_MON_SEL,
+ STM32_USBPHYC_MON_SEL_LOCKP);
+ u32 monout;
int ret;
ret = stm32_usbphyc_pll_enable(usbphyc);
if (ret)
return ret;
+ /* Check that PLL Lock input to PHY is High */
+ writel_relaxed(monsel, usbphyc->base + reg_mon);
+ ret = readl_relaxed_poll_timeout(usbphyc->base + reg_mon, monout,
+ (monout & STM32_USBPHYC_MON_OUT_LOCKP),
+ 1000);
+ if (ret) {
+ dev_err(usbphyc->dev, "PLL Lock input to PHY is Low (val=%x)\n",
+ (u32)(monout & STM32_USBPHYC_MON_OUT));
+ goto pll_disable;
+ }
+
+ /* This mdelay seems to be necessary on some machines, since the
+ * monsel status does not seem to be accurate. On rare occasions
+ * just working with the phy after this pll check the usb
+ * peripheral (e.g. on the dwc2) run into timeout issues and
+ * leading to no functional usb. With this short mdelay this
+ * issue was not reported again.
+ */
+ mdelay(1);
+
usbphyc_phy->active = true;
return 0;
+
+pll_disable:
+ stm32_usbphyc_pll_disable(usbphyc);
+
+ return ret;
}
static int stm32_usbphyc_phy_exit(struct phy *phy)
@@ -234,14 +387,20 @@ static int stm32_usbphyc_phy_power_on(struct phy *phy)
{
struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
- return regulator_bulk_enable(NUM_SUPPLIES, usbphyc_phy->supplies);
+ if (usbphyc_phy->vbus)
+ return regulator_enable(usbphyc_phy->vbus);
+
+ return 0;
}
static int stm32_usbphyc_phy_power_off(struct phy *phy)
{
struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
- return regulator_bulk_disable(NUM_SUPPLIES, usbphyc_phy->supplies);
+ if (usbphyc_phy->vbus)
+ return regulator_disable(usbphyc_phy->vbus);
+
+ return 0;
}
static const struct phy_ops stm32_usbphyc_phy_ops = {
@@ -251,6 +410,150 @@ static const struct phy_ops stm32_usbphyc_phy_ops = {
.power_off = stm32_usbphyc_phy_power_off,
};
+static int stm32_usbphyc_clk48_prepare(struct clk_hw *hw)
+{
+ struct stm32_usbphyc *usbphyc = container_of(hw, struct stm32_usbphyc, clk48_hw);
+
+ return stm32_usbphyc_pll_enable(usbphyc);
+}
+
+static void stm32_usbphyc_clk48_unprepare(struct clk_hw *hw)
+{
+ struct stm32_usbphyc *usbphyc = container_of(hw, struct stm32_usbphyc, clk48_hw);
+
+ stm32_usbphyc_pll_disable(usbphyc);
+}
+
+static unsigned long stm32_usbphyc_clk48_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ return 48000000;
+}
+
+static const struct clk_ops usbphyc_clk48_ops = {
+ .enable = stm32_usbphyc_clk48_prepare,
+ .disable = stm32_usbphyc_clk48_unprepare,
+ .recalc_rate = stm32_usbphyc_clk48_recalc_rate,
+};
+
+static int stm32_usbphyc_clk48_register(struct stm32_usbphyc *usbphyc)
+{
+ struct device_node *node = usbphyc->dev->of_node;
+ struct clk_init_data init = { };
+ int ret = 0;
+
+ init.name = "ck_usbo_48m";
+ init.ops = &usbphyc_clk48_ops;
+
+ usbphyc->clk48_hw.init = &init;
+
+ ret = clk_hw_register(usbphyc->dev, &usbphyc->clk48_hw);
+ if (ret)
+ return ret;
+
+ return of_clk_add_hw_provider(node, of_clk_hw_simple_get, &usbphyc->clk48_hw);
+}
+
+static void stm32_usbphyc_phy_tuning(struct stm32_usbphyc *usbphyc,
+ struct device_node *np, u32 index)
+{
+ struct stm32_usbphyc_phy *usbphyc_phy = usbphyc->phys[index];
+ u32 reg = STM32_USBPHYC_TUNE(index);
+ u32 otpcomp, val;
+ int ret;
+
+ /* Backup OTP compensation code */
+ otpcomp = FIELD_GET(OTPCOMP, readl_relaxed(usbphyc->base + reg));
+
+ ret = of_property_read_u32(np, "st,current-boost-microamp", &val);
+ if (ret != -EINVAL) {
+ if (!ret && (val == BOOST_1000_UA || val == BOOST_2000_UA)) {
+ val = (val == BOOST_2000_UA) ? 1 : 0;
+ usbphyc_phy->tune |= INCURREN | FIELD_PREP(INCURRINT, val);
+ } else {
+ dev_warn(usbphyc->dev, "phy%d: invalid st,current-boost-microamp\n", index);
+ }
+ }
+
+ if (!of_property_read_bool(np, "st,no-lsfs-fb-cap"))
+ usbphyc_phy->tune |= LFSCAPEN;
+
+ if (of_property_read_bool(np, "st,decrease-hs-slew-rate"))
+ usbphyc_phy->tune |= HSDRVSLEW;
+
+ ret = of_property_read_u32(np, "st,tune-hs-dc-level", &val);
+ if (ret != -EINVAL) {
+ if (!ret && val < DC_MAX) {
+ if (val == DC_MINUS_5_TO_7_MV) {/* Decreases HS driver DC level */
+ usbphyc_phy->tune |= HSDRVDCCUR;
+ } else if (val > 0) { /* Increases HS driver DC level */
+ val = (val == DC_PLUS_10_TO_14_MV) ? 1 : 0;
+ usbphyc_phy->tune |= HSDRVCURINCR | FIELD_PREP(HSDRVDCLEV, val);
+ }
+ } else {
+ dev_warn(usbphyc->dev, "phy%d: invalid st,tune-hs-dc-level\n", index);
+ }
+ }
+
+ if (of_property_read_bool(np, "st,enable-fs-rftime-tuning"))
+ usbphyc_phy->tune |= FSDRVRFADJ;
+
+ if (of_property_read_bool(np, "st,enable-hs-rftime-reduction"))
+ usbphyc_phy->tune |= HSDRVRFRED;
+
+ ret = of_property_read_u32(np, "st,trim-hs-current", &val);
+ if (ret != -EINVAL) {
+ if (!ret && val < CUR_MAX)
+ usbphyc_phy->tune |= FIELD_PREP(HSDRVCHKITRM, val);
+ else
+ dev_warn(usbphyc->dev, "phy%d: invalid st,trim-hs-current\n", index);
+ }
+
+ ret = of_property_read_u32(np, "st,trim-hs-impedance", &val);
+ if (ret != -EINVAL) {
+ if (!ret && val < IMP_MAX)
+ usbphyc_phy->tune |= FIELD_PREP(HSDRVCHKZTRM, val);
+ else
+ dev_warn(usbphyc->dev, "phy%d: invalid st,trim-hs-impedance\n", index);
+ }
+
+ ret = of_property_read_u32(np, "st,tune-squelch-level", &val);
+ if (ret != -EINVAL) {
+ if (!ret && val < SQLCH_MAX)
+ usbphyc_phy->tune |= FIELD_PREP(SQLCHCTL, val);
+ else
+ dev_warn(usbphyc->dev, "phy%d: invalid st,tune-squelch\n", index);
+ }
+
+ if (of_property_read_bool(np, "st,enable-hs-rx-gain-eq"))
+ usbphyc_phy->tune |= HDRXGNEQEN;
+
+ ret = of_property_read_u32(np, "st,tune-hs-rx-offset", &val);
+ if (ret != -EINVAL) {
+ if (!ret && val < RX_OFFSET_MAX)
+ usbphyc_phy->tune |= FIELD_PREP(HSRXOFF, val);
+ else
+ dev_warn(usbphyc->dev, "phy%d: invalid st,tune-hs-rx-offset\n", index);
+ }
+
+ if (of_property_read_bool(np, "st,no-hs-ftime-ctrl"))
+ usbphyc_phy->tune |= HSFALLPREEM;
+
+ if (!of_property_read_bool(np, "st,no-lsfs-sc"))
+ usbphyc_phy->tune |= SHTCCTCTLPROT;
+
+ if (of_property_read_bool(np, "st,enable-hs-tx-staggering"))
+ usbphyc_phy->tune |= STAGSEL;
+
+ /* Restore OTP compensation code */
+ usbphyc_phy->tune |= FIELD_PREP(OTPCOMP, otpcomp);
+
+ /*
+ * By default, if no st,xxx tuning property is used, usbphyc_phy->tune is equal to
+ * STM32_USBPHYC_TUNE reset value (LFSCAPEN | SHTCCTCTLPROT | OTPCOMP).
+ */
+ writel_relaxed(usbphyc_phy->tune, usbphyc->base + reg);
+}
+
static void stm32_usbphyc_switch_setup(struct stm32_usbphyc *usbphyc,
u32 utmi_switch)
{
@@ -263,7 +566,7 @@ static void stm32_usbphyc_switch_setup(struct stm32_usbphyc *usbphyc,
usbphyc->switch_setup = utmi_switch;
}
-static struct phy *stm32_usbphyc_of_xlate(struct device_d *dev,
+static struct phy *stm32_usbphyc_of_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct stm32_usbphyc *usbphyc = dev->priv;
@@ -272,7 +575,7 @@ static struct phy *stm32_usbphyc_of_xlate(struct device_d *dev,
int port = 0;
for (port = 0; port < usbphyc->nphys; port++) {
- if (phynode == usbphyc->phys[port]->phy->dev.device_node) {
+ if (phynode == usbphyc->phys[port]->phy->dev.of_node) {
usbphyc_phy = usbphyc->phys[port];
break;
}
@@ -305,13 +608,13 @@ static struct phy *stm32_usbphyc_of_xlate(struct device_d *dev,
return usbphyc_phy->phy;
}
-static int stm32_usbphyc_probe(struct device_d *dev)
+static int stm32_usbphyc_probe(struct device *dev)
{
struct stm32_usbphyc *usbphyc;
- struct device_node *child, *np = dev->device_node;
+ struct device_node *child, *np = dev->of_node;
struct resource *iores;
struct phy_provider *phy_provider;
- u32 version;
+ u32 pllen, version;
int ret, port = 0;
usbphyc = xzalloc(sizeof(*usbphyc));
@@ -328,53 +631,79 @@ static int stm32_usbphyc_probe(struct device_d *dev)
if (IS_ERR(usbphyc->clk)) {
ret = PTR_ERR(usbphyc->clk);
dev_err(dev, "clk get failed: %d\n", ret);
- return ret;
+ goto release_region;
}
ret = clk_enable(usbphyc->clk);
if (ret) {
dev_err(dev, "clk enable failed: %d\n", ret);
- return ret;
+ goto release_region;
}
- device_reset_us(dev, 2);
+ ret = device_reset_us(dev, 2);
+ if (ret == -EPROBE_DEFER)
+ goto clk_disable;
+ if (ret)
+ stm32_usbphyc_clr_bits(usbphyc->base + STM32_USBPHYC_PLL, PLLEN);
+
+ /*
+ * Wait for minimum width of powerdown pulse (ENABLE = Low):
+ * we have to ensure the PLL is disabled before phys initialization.
+ */
+ if (readl_relaxed_poll_timeout(usbphyc->base + STM32_USBPHYC_PLL,
+ pllen, !(pllen & PLLEN), 50)) {
+ dev_warn(usbphyc->dev, "PLL not reset\n");
+ ret = -EPROBE_DEFER;
+ goto clk_disable;
+ }
usbphyc->switch_setup = -EINVAL;
usbphyc->nphys = of_get_child_count(np);
- usbphyc->phys = xzalloc(usbphyc->nphys * sizeof(*usbphyc->phys));
+ usbphyc->phys = calloc(usbphyc->nphys, sizeof(*usbphyc->phys));
+ if (!usbphyc->phys) {
+ ret = -ENOMEM;
+ goto clk_disable;
+ }
+
+ usbphyc->vdda1v1 = regulator_get(dev, "vdda1v1");
+ if (IS_ERR(usbphyc->vdda1v1)) {
+ ret = dev_err_probe(dev, PTR_ERR(usbphyc->vdda1v1),
+ "failed to get vdda1v1 supply\n");
+ goto clk_disable;
+ }
+
+ usbphyc->vdda1v8 = regulator_get(dev, "vdda1v8");
+ if (IS_ERR(usbphyc->vdda1v8)) {
+ ret = dev_err_probe(dev, PTR_ERR(usbphyc->vdda1v8),
+ "failed to get vdda1v8 supply\n");
+ goto clk_disable;
+ }
for_each_child_of_node(np, child) {
struct stm32_usbphyc_phy *usbphyc_phy;
+ struct device *phydev;
struct phy *phy;
u32 index;
- int i;
- phy = phy_create(dev, child, &stm32_usbphyc_phy_ops);
+ phydev = of_platform_device_create(child, dev);
+ if (!phydev)
+ continue;
+
+ of_platform_device_dummy_drv(phydev);
+
+ phy = phy_create(phydev, child, &stm32_usbphyc_phy_ops);
if (IS_ERR(phy)) {
- ret = PTR_ERR(phy);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to create phy%d: %d\n",
- port, ret);
+ ret = dev_errp_probe(dev, phy, "creating phy%d\n", port);
goto clk_disable;
}
usbphyc_phy = xzalloc(sizeof(*usbphyc_phy));
- for (i = 0; i < NUM_SUPPLIES; i++)
- usbphyc_phy->supplies[i].supply = supplies_names[i];
-
- ret = regulator_bulk_get(&phy->dev, NUM_SUPPLIES,
- usbphyc_phy->supplies);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_err(&phy->dev,
- "failed to get regulators: %d\n", ret);
- goto clk_disable;
- }
-
ret = of_property_read_u32(child, "reg", &index);
if (ret || index > usbphyc->nphys) {
dev_err(&phy->dev, "invalid reg property: %d\n", ret);
+ if (!ret)
+ ret = -EINVAL;
goto clk_disable;
}
@@ -387,6 +716,17 @@ static int stm32_usbphyc_probe(struct device_d *dev)
usbphyc->phys[port]->index = index;
usbphyc->phys[port]->active = false;
+ usbphyc->phys[port]->vbus = regulator_get(&phy->dev, "vbus");
+ if (IS_ERR(usbphyc->phys[port]->vbus)) {
+ ret = PTR_ERR(usbphyc->phys[port]->vbus);
+ if (ret == -EPROBE_DEFER)
+ goto clk_disable;
+ usbphyc->phys[port]->vbus = NULL;
+ }
+
+ /* Configure phy tuning */
+ stm32_usbphyc_phy_tuning(usbphyc, child, index);
+
port++;
}
@@ -397,21 +737,38 @@ static int stm32_usbphyc_probe(struct device_d *dev)
goto clk_disable;
}
- version = readl(usbphyc->base + STM32_USBPHYC_VERSION);
- dev_info(dev, "registered rev: %lu.%lu\n",
+ ret = stm32_usbphyc_clk48_register(usbphyc);
+ if (ret) {
+ dev_err(dev, "failed to register ck_usbo_48m clock: %d\n", ret);
+ goto clk_disable;
+ }
+
+ version = readl_relaxed(usbphyc->base + STM32_USBPHYC_VERSION);
+ dev_info(dev, "registered rev:%lu.%lu\n",
FIELD_GET(MAJREV, version), FIELD_GET(MINREV, version));
return 0;
clk_disable:
clk_disable(usbphyc->clk);
+release_region:
+ release_region(iores);
+
+ free(usbphyc->phys);
+ free(usbphyc);
return ret;
}
-static void stm32_usbphyc_remove(struct device_d *dev)
+static void stm32_usbphyc_remove(struct device *dev)
{
struct stm32_usbphyc *usbphyc = dev->priv;
+ int port;
+
+ /* Ensure PHYs are not active, to allow PLL disabling */
+ for (port = 0; port < usbphyc->nphys; port++)
+ if (usbphyc->phys[port]->active)
+ stm32_usbphyc_phy_exit(usbphyc->phys[port]->phy);
clk_disable(usbphyc->clk);
}
@@ -420,8 +777,9 @@ static const struct of_device_id stm32_usbphyc_of_match[] = {
{ .compatible = "st,stm32mp1-usbphyc", },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, stm32_usbphyc_of_match);
-static struct driver_d stm32_usbphyc_driver = {
+static struct driver stm32_usbphyc_driver = {
.name = "stm32-usbphyc",
.probe = stm32_usbphyc_probe,
.remove = stm32_usbphyc_remove,
diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig
new file mode 100644
index 0000000000..8a10190f8e
--- /dev/null
+++ b/drivers/phy/rockchip/Kconfig
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config PHY_ROCKCHIP_INNO_USB2
+ bool "Rockchip INNO USB2PHY Driver"
+ depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OFDEVICE
+ depends on COMMON_CLK
+ help
+ Support for Rockchip USB2.0 PHY with Innosilicon IP block.
+
+config PHY_ROCKCHIP_NANENG_COMBO_PHY
+ bool "Rockchip NANENG COMBO PHY Driver"
+ depends on ARCH_ROCKCHIP && OFDEVICE
+ help
+ Enable this to support the Rockchip PCIe/USB3.0/SATA/QSGMII
+ combo PHY with NaNeng IP block.
+
+config PHY_ROCKCHIP_SNPS_PCIE3
+ bool "Rockchip Snps PCIe3 PHY Driver"
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ help
+ Enable this to support the Rockchip snps PCIe3 PHY.
diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile
new file mode 100644
index 0000000000..1eaee4cbee
--- /dev/null
+++ b/drivers/phy/rockchip/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o
+obj-$(CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY) += phy-rockchip-naneng-combphy.o
+obj-$(CONFIG_PHY_ROCKCHIP_SNPS_PCIE3) += phy-rockchip-snps-pcie3.o
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
new file mode 100644
index 0000000000..34abbd85db
--- /dev/null
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
@@ -0,0 +1,993 @@
+/*
+ * Copyright 2017 Rockchip Electronics Co., Ltd
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <of.h>
+#include <errno.h>
+#include <driver.h>
+#include <malloc.h>
+#include <linux/usb/phy.h>
+#include <linux/phy/phy.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/regmap.h>
+#include <mfd/syscon.h>
+#include <regulator.h>
+
+#define U2PHY_BIT_WRITEABLE_SHIFT 16
+#define CHG_DCD_MAX_RETRIES 6
+#define CHG_PRI_MAX_RETRIES 2
+#define CHG_DCD_POLL_TIME 100 /* millisecond */
+#define CHG_PRIMARY_DET_TIME 40 /* millisecond */
+#define CHG_SECONDARY_DET_TIME 40 /* millisecond */
+
+struct rockchip_usb2phy;
+
+enum power_supply_type {
+ POWER_SUPPLY_TYPE_UNKNOWN = 0,
+ POWER_SUPPLY_TYPE_USB, /* Standard Downstream Port */
+ POWER_SUPPLY_TYPE_USB_DCP, /* Dedicated Charging Port */
+ POWER_SUPPLY_TYPE_USB_CDP, /* Charging Downstream Port */
+ POWER_SUPPLY_TYPE_USB_FLOATING, /* DCP without shorting D+/D- */
+};
+
+enum rockchip_usb2phy_port_id {
+ USB2PHY_PORT_OTG,
+ USB2PHY_PORT_HOST,
+ USB2PHY_NUM_PORTS,
+};
+
+struct usb2phy_reg {
+ u32 offset;
+ u32 bitend;
+ u32 bitstart;
+ u32 disable;
+ u32 enable;
+};
+
+/**
+ * struct rockchip_chg_det_reg: usb charger detect registers
+ * @cp_det: charging port detected successfully.
+ * @dcp_det: dedicated charging port detected successfully.
+ * @dp_det: assert data pin connect successfully.
+ * @idm_sink_en: open dm sink curren.
+ * @idp_sink_en: open dp sink current.
+ * @idp_src_en: open dm source current.
+ * @rdm_pdwn_en: open dm pull down resistor.
+ * @vdm_src_en: open dm voltage source.
+ * @vdp_src_en: open dp voltage source.
+ * @opmode: utmi operational mode.
+ */
+struct rockchip_chg_det_reg {
+ struct usb2phy_reg cp_det;
+ struct usb2phy_reg dcp_det;
+ struct usb2phy_reg dp_det;
+ struct usb2phy_reg idm_sink_en;
+ struct usb2phy_reg idp_sink_en;
+ struct usb2phy_reg idp_src_en;
+ struct usb2phy_reg rdm_pdwn_en;
+ struct usb2phy_reg vdm_src_en;
+ struct usb2phy_reg vdp_src_en;
+ struct usb2phy_reg opmode;
+};
+
+/**
+ * struct rockchip_usb2phy_port_cfg: usb-phy port configuration.
+ * @phy_sus: phy suspend register.
+ * @bvalid_det_en: vbus valid rise detection enable register.
+ * @bvalid_det_st: vbus valid rise detection status register.
+ * @bvalid_det_clr: vbus valid rise detection clear register.
+ * @ls_det_en: linestate detection enable register.
+ * @ls_det_st: linestate detection state register.
+ * @ls_det_clr: linestate detection clear register.
+ * @iddig_output: iddig output from grf.
+ * @iddig_en: utmi iddig select between grf and phy,
+ * 0: from phy; 1: from grf
+ * @idfall_det_en: id fall detection enable register.
+ * @idfall_det_st: id fall detection state register.
+ * @idfall_det_clr: id fall detection clear register.
+ * @idrise_det_en: id rise detection enable register.
+ * @idrise_det_st: id rise detection state register.
+ * @idrise_det_clr: id rise detection clear register.
+ * @utmi_avalid: utmi vbus avalid status register.
+ * @utmi_bvalid: utmi vbus bvalid status register.
+ * @utmi_iddig: otg port id pin status register.
+ * @utmi_ls: utmi linestate state register.
+ * @utmi_hstdet: utmi host disconnect register.
+ * @vbus_det_en: vbus detect function power down register.
+ */
+struct rockchip_usb2phy_port_cfg {
+ struct usb2phy_reg phy_sus;
+ struct usb2phy_reg bvalid_det_en;
+ struct usb2phy_reg bvalid_det_st;
+ struct usb2phy_reg bvalid_det_clr;
+ struct usb2phy_reg ls_det_en;
+ struct usb2phy_reg ls_det_st;
+ struct usb2phy_reg ls_det_clr;
+ struct usb2phy_reg iddig_output;
+ struct usb2phy_reg iddig_en;
+ struct usb2phy_reg idfall_det_en;
+ struct usb2phy_reg idfall_det_st;
+ struct usb2phy_reg idfall_det_clr;
+ struct usb2phy_reg idrise_det_en;
+ struct usb2phy_reg idrise_det_st;
+ struct usb2phy_reg idrise_det_clr;
+ struct usb2phy_reg utmi_avalid;
+ struct usb2phy_reg utmi_bvalid;
+ struct usb2phy_reg utmi_iddig;
+ struct usb2phy_reg utmi_ls;
+ struct usb2phy_reg utmi_hstdet;
+ struct usb2phy_reg vbus_det_en;
+};
+
+/**
+ * struct rockchip_usb2phy_cfg: usb-phy configuration.
+ * @reg: the address offset of grf for usb-phy config.
+ * @num_ports: specify how many ports that the phy has.
+ * @phy_tuning: phy default parameters tunning.
+ * @clkout_ctl: keep on/turn off output clk of phy.
+ * @chg_det: charger detection registers.
+ */
+struct rockchip_usb2phy_cfg {
+ u32 reg;
+ u32 num_ports;
+ int (*phy_tuning)(struct rockchip_usb2phy *);
+ struct usb2phy_reg clkout_ctl;
+ const struct rockchip_usb2phy_port_cfg port_cfgs[USB2PHY_NUM_PORTS];
+ const struct rockchip_chg_det_reg chg_det;
+};
+
+struct rockchip_usb2phy_phy {
+ struct phy *phy;
+ struct regulator *vbus;
+ struct rockchip_usb2phy *usb2phy;
+ const struct rockchip_usb2phy_port_cfg *port_cfg;
+};
+
+/**
+ * @dcd_retries: The retry count used to track Data contact
+ * detection process.
+ * @primary_retries: The retry count used to do usb bc detection
+ * primary stage.
+ * @grf: General Register Files register base.
+ * @usbgrf_base : USB General Register Files register base.
+ * @phy_cfg: phy register configuration, assigned by driver data.
+ */
+struct rockchip_usb2phy {
+ u8 dcd_retries;
+ u8 primary_retries;
+ struct regmap *grf_base;
+ const struct rockchip_usb2phy_cfg *phy_cfg;
+ struct rockchip_usb2phy_phy phys[2];
+ struct phy_provider *provider;
+ struct clk *clk480m;
+ struct clk_hw clk480m_hw;
+ struct device *dev;
+ struct clk *clk;
+};
+
+static inline struct regmap *get_reg_base(struct rockchip_usb2phy *rphy)
+{
+ return rphy->grf_base;
+}
+
+static inline int property_enable(struct regmap *base,
+ const struct usb2phy_reg *reg, bool en)
+{
+ u32 val, mask, tmp;
+
+ tmp = en ? reg->enable : reg->disable;
+ mask = GENMASK(reg->bitend, reg->bitstart);
+ val = (tmp << reg->bitstart) | (mask << U2PHY_BIT_WRITEABLE_SHIFT);
+
+ return regmap_write(base, reg->offset, val);
+}
+
+static inline bool property_enabled(struct regmap *base,
+ const struct usb2phy_reg *reg)
+{
+ u32 tmp, orig;
+ u32 mask = GENMASK(reg->bitend, reg->bitstart);
+
+ regmap_read(base, reg->offset, &orig);
+
+ tmp = (orig & mask) >> reg->bitstart;
+
+ return tmp == reg->enable;
+}
+
+static int rockchip_usb2phy_init(struct phy *phy)
+{
+ struct rockchip_usb2phy_phy *p = phy_get_drvdata(phy);
+ struct rockchip_usb2phy *rphy = p->usb2phy;
+ struct regmap *base = get_reg_base(rphy);
+
+ p->vbus = regulator_get(&phy->dev, "vbus");
+
+ property_enable(base, &p->port_cfg->phy_sus, false);
+
+ /* waiting for the utmi_clk to become stable */
+ udelay(2000);
+
+ return 0;
+}
+
+static int rockchip_usb2phy_exit(struct phy *phy)
+{
+ struct rockchip_usb2phy_phy *p = phy_get_drvdata(phy);
+ struct rockchip_usb2phy *rphy = p->usb2phy;
+ struct regmap *base = get_reg_base(rphy);
+
+ property_enable(base, &p->port_cfg->phy_sus, true);
+
+ return 0;
+}
+
+static int rockchip_usb2phy_power_on(struct phy *phy)
+{
+ struct rockchip_usb2phy_phy *p = phy_get_drvdata(phy);
+ int ret;
+
+ ret = regulator_enable(p->vbus);
+ if (ret) {
+ dev_err(&phy->dev, "Failed to enable VBus supply\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rockchip_usb2phy_power_off(struct phy *phy)
+{
+ struct rockchip_usb2phy_phy *p = phy_get_drvdata(phy);
+ int ret;
+
+ ret = regulator_disable(p->vbus);
+ if (ret) {
+ dev_err(&phy->dev, "Failed to disable VBus supply\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct phy *rockchip_usb2phy_of_xlate(struct device *dev,
+ struct of_phandle_args *args)
+{
+ struct rockchip_usb2phy *rphy = dev->priv;
+ struct device_node *phynode = args->np;
+ struct rockchip_usb2phy_phy *p;
+ int port;
+
+ for (port = 0; port < 2; port++) {
+ if (!rphy->phys[port].phy)
+ continue;
+
+ if (phynode == rphy->phys[port].phy->dev.of_node) {
+ p = &rphy->phys[port];
+ return p->phy;
+ }
+ }
+
+ return NULL;
+}
+
+static struct phy_ops rockchip_usb2phy_ops = {
+ .init = rockchip_usb2phy_init,
+ .exit = rockchip_usb2phy_exit,
+ .power_on = rockchip_usb2phy_power_on,
+ .power_off = rockchip_usb2phy_power_off,
+};
+
+static int rockchip_usb2phy_clk480m_prepare(struct clk_hw *hw)
+{
+ struct rockchip_usb2phy *rphy =
+ container_of(hw, struct rockchip_usb2phy, clk480m_hw);
+ struct regmap *base = get_reg_base(rphy);
+ int ret;
+
+ /* turn on 480m clk output if it is off */
+ if (!property_enabled(base, &rphy->phy_cfg->clkout_ctl)) {
+ ret = property_enable(base, &rphy->phy_cfg->clkout_ctl, true);
+ if (ret)
+ return ret;
+
+ /* waiting for the clk become stable */
+ udelay(1200);
+ }
+
+ return 0;
+}
+
+static void rockchip_usb2phy_clk480m_unprepare(struct clk_hw *hw)
+{
+ struct rockchip_usb2phy *rphy =
+ container_of(hw, struct rockchip_usb2phy, clk480m_hw);
+ struct regmap *base = get_reg_base(rphy);
+
+ /* turn off 480m clk output */
+ property_enable(base, &rphy->phy_cfg->clkout_ctl, false);
+}
+
+static int rockchip_usb2phy_clk480m_prepared(struct clk_hw *hw)
+{
+ struct rockchip_usb2phy *rphy =
+ container_of(hw, struct rockchip_usb2phy, clk480m_hw);
+ struct regmap *base = get_reg_base(rphy);
+
+ return property_enabled(base, &rphy->phy_cfg->clkout_ctl);
+}
+
+static unsigned long
+rockchip_usb2phy_clk480m_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return 480000000;
+}
+
+static const struct clk_ops rockchip_usb2phy_clkout_ops = {
+ .enable = rockchip_usb2phy_clk480m_prepare,
+ .disable = rockchip_usb2phy_clk480m_unprepare,
+ .is_enabled = rockchip_usb2phy_clk480m_prepared,
+ .recalc_rate = rockchip_usb2phy_clk480m_recalc_rate,
+};
+
+static int rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy)
+{
+ struct device_node *node = rphy->dev->of_node;
+ struct clk_init_data init = {};
+ const char *clk_name;
+ int ret;
+
+ init.flags = 0;
+ init.name = "clk_usbphy_480m";
+ init.ops = &rockchip_usb2phy_clkout_ops;
+
+ /* optional override of the clockname */
+ of_property_read_string(node, "clock-output-names", &init.name);
+
+ if (rphy->clk) {
+ clk_name = __clk_get_name(rphy->clk);
+ init.parent_names = &clk_name;
+ init.num_parents = 1;
+ } else {
+ init.parent_names = NULL;
+ init.num_parents = 0;
+ }
+
+ rphy->clk480m_hw.init = &init;
+
+ rphy->clk480m = clk_register(rphy->dev, &rphy->clk480m_hw);
+ if (IS_ERR(rphy->clk480m)) {
+ ret = PTR_ERR(rphy->clk480m);
+ goto err_ret;
+ }
+
+ ret = of_clk_add_provider(node, of_clk_src_simple_get, rphy->clk480m);
+ if (ret < 0)
+ goto err_clk_provider;
+
+ return 0;
+
+err_clk_provider:
+ clk_unregister(rphy->clk480m);
+err_ret:
+ return ret;
+}
+
+static int rockchip_usb2phy_probe(struct device *dev)
+{
+ const struct rockchip_usb2phy_cfg *phy_cfgs;
+ struct rockchip_usb2phy *rphy;
+ u32 reg, index;
+ int port = 0;
+ struct device_node *child, *np = dev->of_node;
+ struct resource *iores;
+
+ rphy = xzalloc(sizeof(*rphy));
+
+ rphy->dev = dev;
+
+ if (of_device_is_compatible(np, "rockchip,rv1108-usb2phy") ||
+ of_device_is_compatible(np, "rockchip,rk3568-usb2phy"))
+ rphy->grf_base = syscon_regmap_lookup_by_phandle(np, "rockchip,usbgrf");
+ else
+ rphy->grf_base = syscon_node_to_regmap(dev->parent->of_node);
+
+ if (IS_ERR(rphy->grf_base))
+ return PTR_ERR(rphy->grf_base);
+
+ phy_cfgs = device_get_match_data(dev);
+ if (!phy_cfgs)
+ return -EINVAL;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores)) {
+ if (of_property_read_u32(np, "reg", &reg))
+ return -EINVAL;
+ } else {
+ reg = iores->start;
+ }
+
+ /* find out a proper config which can be matched with dt. */
+ index = 0;
+ while (phy_cfgs[index].reg) {
+ if (phy_cfgs[index].reg == reg) {
+ rphy->phy_cfg = &phy_cfgs[index];
+ break;
+ }
+ ++index;
+ }
+
+ if (!rphy->phy_cfg) {
+ dev_err(dev, "no phy-config can be matched\n");
+ return -EINVAL;
+ }
+
+ for_each_child_of_node(np, child) {
+ struct rockchip_usb2phy_phy *p;
+ struct phy *phy;
+ struct device *phydev;
+
+ if (!strcmp(child->name, "host-port")) {
+ port = USB2PHY_PORT_OTG;
+ } else if (!strcmp(child->name, "otg-port")) {
+ port = USB2PHY_PORT_HOST;
+ } else {
+ dev_warn(dev, "Ignoring unknown subnode %s\n", child->name);
+ continue;
+ }
+
+ if (rphy->phys[port].phy)
+ return -EINVAL;
+
+ phydev = of_platform_device_create(child, dev);
+ if (!phydev)
+ continue;
+
+ of_platform_device_dummy_drv(phydev);
+
+ phy = phy_create(phydev, child, &rockchip_usb2phy_ops);
+ if (IS_ERR(phy))
+ return dev_errp_probe(dev, phy, "creating phy%d\n", port);
+
+ p = xzalloc(sizeof(*p));
+
+ phy_set_drvdata(phy, p);
+ p->usb2phy = rphy;
+ p->port_cfg = &phy_cfgs->port_cfgs[port];
+
+ rphy->phys[port].phy = phy;
+ }
+
+ if (rphy->phy_cfg->phy_tuning)
+ rphy->phy_cfg->phy_tuning(rphy);
+
+ dev->priv = rphy;
+
+ rphy->clk = clk_get(dev, "phyclk");
+ rockchip_usb2phy_clk480m_register(rphy);
+
+ rphy->provider = of_phy_provider_register(dev, rockchip_usb2phy_of_xlate);
+ if (IS_ERR(rphy->provider))
+ return PTR_ERR(rphy->provider);
+
+ return 0;
+}
+
+static int rk322x_usb2phy_tuning(struct rockchip_usb2phy *rphy)
+{
+ struct regmap *base = get_reg_base(rphy);
+ int ret = 0;
+
+ /* Open pre-emphasize in non-chirp state for PHY0 otg port */
+ if (rphy->phy_cfg->reg == 0x760)
+ ret = regmap_write(base, 0x76c, 0x00070004);
+
+ return ret;
+}
+
+static const struct rockchip_usb2phy_cfg rk1808_phy_cfgs[] = {
+ {
+ .reg = 0x100,
+ .num_ports = 2,
+ .clkout_ctl = { 0x108, 4, 4, 1, 0 },
+ .port_cfgs = {
+ [USB2PHY_PORT_OTG] = {
+ .phy_sus = { 0x0100, 8, 0, 0, 0x1d1 },
+ .bvalid_det_en = { 0x0110, 2, 2, 0, 1 },
+ .bvalid_det_st = { 0x0114, 2, 2, 0, 1 },
+ .bvalid_det_clr = { 0x0118, 2, 2, 0, 1 },
+ .iddig_output = { 0x0100, 10, 10, 0, 1 },
+ .iddig_en = { 0x0100, 9, 9, 0, 1 },
+ .idfall_det_en = { 0x0110, 5, 5, 0, 1 },
+ .idfall_det_st = { 0x0114, 5, 5, 0, 1 },
+ .idfall_det_clr = { 0x0118, 5, 5, 0, 1 },
+ .idrise_det_en = { 0x0110, 4, 4, 0, 1 },
+ .idrise_det_st = { 0x0114, 4, 4, 0, 1 },
+ .idrise_det_clr = { 0x0118, 4, 4, 0, 1 },
+ .ls_det_en = { 0x0110, 0, 0, 0, 1 },
+ .ls_det_st = { 0x0114, 0, 0, 0, 1 },
+ .ls_det_clr = { 0x0118, 0, 0, 0, 1 },
+ .utmi_avalid = { 0x0120, 10, 10, 0, 1 },
+ .utmi_bvalid = { 0x0120, 9, 9, 0, 1 },
+ .utmi_iddig = { 0x0120, 6, 6, 0, 1 },
+ .utmi_ls = { 0x0120, 5, 4, 0, 1 },
+ .vbus_det_en = { 0x001c, 15, 15, 1, 0 },
+ },
+ [USB2PHY_PORT_HOST] = {
+ .phy_sus = { 0x104, 8, 0, 0, 0x1d1 },
+ .ls_det_en = { 0x110, 1, 1, 0, 1 },
+ .ls_det_st = { 0x114, 1, 1, 0, 1 },
+ .ls_det_clr = { 0x118, 1, 1, 0, 1 },
+ .utmi_ls = { 0x120, 17, 16, 0, 1 },
+ .utmi_hstdet = { 0x120, 19, 19, 0, 1 }
+ }
+ },
+ .chg_det = {
+ .opmode = { 0x0100, 3, 0, 5, 1 },
+ .cp_det = { 0x0120, 24, 24, 0, 1 },
+ .dcp_det = { 0x0120, 23, 23, 0, 1 },
+ .dp_det = { 0x0120, 25, 25, 0, 1 },
+ .idm_sink_en = { 0x0108, 8, 8, 0, 1 },
+ .idp_sink_en = { 0x0108, 7, 7, 0, 1 },
+ .idp_src_en = { 0x0108, 9, 9, 0, 1 },
+ .rdm_pdwn_en = { 0x0108, 10, 10, 0, 1 },
+ .vdm_src_en = { 0x0108, 12, 12, 0, 1 },
+ .vdp_src_en = { 0x0108, 11, 11, 0, 1 },
+ },
+ },
+ { /* sentinel */ }
+};
+
+static const struct rockchip_usb2phy_cfg rk312x_phy_cfgs[] = {
+ {
+ .reg = 0x17c,
+ .num_ports = 2,
+ .clkout_ctl = { 0x0190, 15, 15, 1, 0 },
+ .port_cfgs = {
+ [USB2PHY_PORT_OTG] = {
+ .phy_sus = { 0x017c, 8, 0, 0, 0x1d1 },
+ .bvalid_det_en = { 0x017c, 14, 14, 0, 1 },
+ .bvalid_det_st = { 0x017c, 15, 15, 0, 1 },
+ .bvalid_det_clr = { 0x017c, 15, 15, 0, 1 },
+ .iddig_output = { 0x017c, 10, 10, 0, 1 },
+ .iddig_en = { 0x017c, 9, 9, 0, 1 },
+ .idfall_det_en = { 0x01a0, 2, 2, 0, 1 },
+ .idfall_det_st = { 0x01a0, 3, 3, 0, 1 },
+ .idfall_det_clr = { 0x01a0, 3, 3, 0, 1 },
+ .idrise_det_en = { 0x01a0, 0, 0, 0, 1 },
+ .idrise_det_st = { 0x01a0, 1, 1, 0, 1 },
+ .idrise_det_clr = { 0x01a0, 1, 1, 0, 1 },
+ .ls_det_en = { 0x017c, 12, 12, 0, 1 },
+ .ls_det_st = { 0x017c, 13, 13, 0, 1 },
+ .ls_det_clr = { 0x017c, 13, 13, 0, 1 },
+ .utmi_bvalid = { 0x014c, 5, 5, 0, 1 },
+ .utmi_iddig = { 0x014c, 8, 8, 0, 1 },
+ .utmi_ls = { 0x014c, 7, 6, 0, 1 },
+ },
+ [USB2PHY_PORT_HOST] = {
+ .phy_sus = { 0x0194, 8, 0, 0, 0x1d1 },
+ .ls_det_en = { 0x0194, 14, 14, 0, 1 },
+ .ls_det_st = { 0x0194, 15, 15, 0, 1 },
+ .ls_det_clr = { 0x0194, 15, 15, 0, 1 }
+ }
+ },
+ .chg_det = {
+ .opmode = { 0x017c, 3, 0, 5, 1 },
+ .cp_det = { 0x02c0, 6, 6, 0, 1 },
+ .dcp_det = { 0x02c0, 5, 5, 0, 1 },
+ .dp_det = { 0x02c0, 7, 7, 0, 1 },
+ .idm_sink_en = { 0x0184, 8, 8, 0, 1 },
+ .idp_sink_en = { 0x0184, 7, 7, 0, 1 },
+ .idp_src_en = { 0x0184, 9, 9, 0, 1 },
+ .rdm_pdwn_en = { 0x0184, 10, 10, 0, 1 },
+ .vdm_src_en = { 0x0184, 12, 12, 0, 1 },
+ .vdp_src_en = { 0x0184, 11, 11, 0, 1 },
+ },
+ },
+ { /* sentinel */ }
+};
+
+static const struct rockchip_usb2phy_cfg rk322x_phy_cfgs[] = {
+ {
+ .reg = 0x760,
+ .num_ports = 2,
+ .phy_tuning = rk322x_usb2phy_tuning,
+ .clkout_ctl = { 0x0768, 4, 4, 1, 0 },
+ .port_cfgs = {
+ [USB2PHY_PORT_OTG] = {
+ .phy_sus = { 0x0760, 8, 0, 0, 0x1d1 },
+ .bvalid_det_en = { 0x0680, 3, 3, 0, 1 },
+ .bvalid_det_st = { 0x0690, 3, 3, 0, 1 },
+ .bvalid_det_clr = { 0x06a0, 3, 3, 0, 1 },
+ .iddig_output = { 0x0760, 10, 10, 0, 1 },
+ .iddig_en = { 0x0760, 9, 9, 0, 1 },
+ .idfall_det_en = { 0x0680, 6, 6, 0, 1 },
+ .idfall_det_st = { 0x0690, 6, 6, 0, 1 },
+ .idfall_det_clr = { 0x06a0, 6, 6, 0, 1 },
+ .idrise_det_en = { 0x0680, 5, 5, 0, 1 },
+ .idrise_det_st = { 0x0690, 5, 5, 0, 1 },
+ .idrise_det_clr = { 0x06a0, 5, 5, 0, 1 },
+ .ls_det_en = { 0x0680, 2, 2, 0, 1 },
+ .ls_det_st = { 0x0690, 2, 2, 0, 1 },
+ .ls_det_clr = { 0x06a0, 2, 2, 0, 1 },
+ .utmi_bvalid = { 0x0480, 4, 4, 0, 1 },
+ .utmi_iddig = { 0x0480, 1, 1, 0, 1 },
+ .utmi_ls = { 0x0480, 3, 2, 0, 1 },
+ .vbus_det_en = { 0x0788, 15, 15, 1, 0 },
+ },
+ [USB2PHY_PORT_HOST] = {
+ .phy_sus = { 0x0764, 8, 0, 0, 0x1d1 },
+ .ls_det_en = { 0x0680, 4, 4, 0, 1 },
+ .ls_det_st = { 0x0690, 4, 4, 0, 1 },
+ .ls_det_clr = { 0x06a0, 4, 4, 0, 1 }
+ }
+ },
+ .chg_det = {
+ .opmode = { 0x0760, 3, 0, 5, 1 },
+ .cp_det = { 0x0884, 4, 4, 0, 1 },
+ .dcp_det = { 0x0884, 3, 3, 0, 1 },
+ .dp_det = { 0x0884, 5, 5, 0, 1 },
+ .idm_sink_en = { 0x0768, 8, 8, 0, 1 },
+ .idp_sink_en = { 0x0768, 7, 7, 0, 1 },
+ .idp_src_en = { 0x0768, 9, 9, 0, 1 },
+ .rdm_pdwn_en = { 0x0768, 10, 10, 0, 1 },
+ .vdm_src_en = { 0x0768, 12, 12, 0, 1 },
+ .vdp_src_en = { 0x0768, 11, 11, 0, 1 },
+ },
+ },
+ {
+ .reg = 0x800,
+ .num_ports = 2,
+ .clkout_ctl = { 0x0808, 4, 4, 1, 0 },
+ .port_cfgs = {
+ [USB2PHY_PORT_OTG] = {
+ .phy_sus = { 0x804, 8, 0, 0, 0x1d1 },
+ .ls_det_en = { 0x0684, 1, 1, 0, 1 },
+ .ls_det_st = { 0x0694, 1, 1, 0, 1 },
+ .ls_det_clr = { 0x06a4, 1, 1, 0, 1 }
+ },
+ [USB2PHY_PORT_HOST] = {
+ .phy_sus = { 0x800, 8, 0, 0, 0x1d1 },
+ .ls_det_en = { 0x0684, 0, 0, 0, 1 },
+ .ls_det_st = { 0x0694, 0, 0, 0, 1 },
+ .ls_det_clr = { 0x06a4, 0, 0, 0, 1 }
+ }
+ },
+ },
+ { /* sentinel */ }
+};
+
+static const struct rockchip_usb2phy_cfg rk3328_phy_cfgs[] = {
+ {
+ .reg = 0x100,
+ .num_ports = 2,
+ .clkout_ctl = { 0x108, 4, 4, 1, 0 },
+ .port_cfgs = {
+ [USB2PHY_PORT_OTG] = {
+ .phy_sus = { 0x0100, 8, 0, 0, 0x1d1 },
+ .bvalid_det_en = { 0x0110, 2, 2, 0, 1 },
+ .bvalid_det_st = { 0x0114, 2, 2, 0, 1 },
+ .bvalid_det_clr = { 0x0118, 2, 2, 0, 1 },
+ .iddig_output = { 0x0100, 10, 10, 0, 1 },
+ .iddig_en = { 0x0100, 9, 9, 0, 1 },
+ .idfall_det_en = { 0x0110, 5, 5, 0, 1 },
+ .idfall_det_st = { 0x0114, 5, 5, 0, 1 },
+ .idfall_det_clr = { 0x0118, 5, 5, 0, 1 },
+ .idrise_det_en = { 0x0110, 4, 4, 0, 1 },
+ .idrise_det_st = { 0x0114, 4, 4, 0, 1 },
+ .idrise_det_clr = { 0x0118, 4, 4, 0, 1 },
+ .ls_det_en = { 0x0110, 0, 0, 0, 1 },
+ .ls_det_st = { 0x0114, 0, 0, 0, 1 },
+ .ls_det_clr = { 0x0118, 0, 0, 0, 1 },
+ .utmi_avalid = { 0x0120, 10, 10, 0, 1 },
+ .utmi_bvalid = { 0x0120, 9, 9, 0, 1 },
+ .utmi_iddig = { 0x0120, 6, 6, 0, 1 },
+ .utmi_ls = { 0x0120, 5, 4, 0, 1 },
+ .vbus_det_en = { 0x001c, 15, 15, 1, 0 },
+ },
+ [USB2PHY_PORT_HOST] = {
+ .phy_sus = { 0x104, 8, 0, 0, 0x1d1 },
+ .ls_det_en = { 0x110, 1, 1, 0, 1 },
+ .ls_det_st = { 0x114, 1, 1, 0, 1 },
+ .ls_det_clr = { 0x118, 1, 1, 0, 1 },
+ .utmi_ls = { 0x120, 17, 16, 0, 1 },
+ .utmi_hstdet = { 0x120, 19, 19, 0, 1 }
+ }
+ },
+ .chg_det = {
+ .opmode = { 0x0100, 3, 0, 5, 1 },
+ .cp_det = { 0x0120, 24, 24, 0, 1 },
+ .dcp_det = { 0x0120, 23, 23, 0, 1 },
+ .dp_det = { 0x0120, 25, 25, 0, 1 },
+ .idm_sink_en = { 0x0108, 8, 8, 0, 1 },
+ .idp_sink_en = { 0x0108, 7, 7, 0, 1 },
+ .idp_src_en = { 0x0108, 9, 9, 0, 1 },
+ .rdm_pdwn_en = { 0x0108, 10, 10, 0, 1 },
+ .vdm_src_en = { 0x0108, 12, 12, 0, 1 },
+ .vdp_src_en = { 0x0108, 11, 11, 0, 1 },
+ },
+ },
+ { /* sentinel */ }
+};
+
+static const struct rockchip_usb2phy_cfg rk3368_phy_cfgs[] = {
+ {
+ .reg = 0x700,
+ .num_ports = 2,
+ .clkout_ctl = { 0x0724, 15, 15, 1, 0 },
+ .port_cfgs = {
+ [USB2PHY_PORT_OTG] = {
+ .phy_sus = { 0x0700, 8, 0, 0, 0x1d1 },
+ .bvalid_det_en = { 0x0680, 3, 3, 0, 1 },
+ .bvalid_det_st = { 0x0690, 3, 3, 0, 1 },
+ .bvalid_det_clr = { 0x06a0, 3, 3, 0, 1 },
+ .ls_det_en = { 0x0680, 2, 2, 0, 1 },
+ .ls_det_st = { 0x0690, 2, 2, 0, 1 },
+ .ls_det_clr = { 0x06a0, 2, 2, 0, 1 },
+ .utmi_bvalid = { 0x04bc, 23, 23, 0, 1 },
+ .utmi_ls = { 0x04bc, 25, 24, 0, 1 },
+ },
+ [USB2PHY_PORT_HOST] = {
+ .phy_sus = { 0x0728, 8, 0, 0, 0x1d1 },
+ .ls_det_en = { 0x0680, 4, 4, 0, 1 },
+ .ls_det_st = { 0x0690, 4, 4, 0, 1 },
+ .ls_det_clr = { 0x06a0, 4, 4, 0, 1 }
+ }
+ },
+ .chg_det = {
+ .opmode = { 0x0700, 3, 0, 5, 1 },
+ .cp_det = { 0x04b8, 30, 30, 0, 1 },
+ .dcp_det = { 0x04b8, 29, 29, 0, 1 },
+ .dp_det = { 0x04b8, 31, 31, 0, 1 },
+ .idm_sink_en = { 0x0718, 8, 8, 0, 1 },
+ .idp_sink_en = { 0x0718, 7, 7, 0, 1 },
+ .idp_src_en = { 0x0718, 9, 9, 0, 1 },
+ .rdm_pdwn_en = { 0x0718, 10, 10, 0, 1 },
+ .vdm_src_en = { 0x0718, 12, 12, 0, 1 },
+ .vdp_src_en = { 0x0718, 11, 11, 0, 1 },
+ },
+ },
+ { /* sentinel */ }
+};
+
+static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = {
+ {
+ .reg = 0xe450,
+ .num_ports = 2,
+ .clkout_ctl = { 0xe450, 4, 4, 1, 0 },
+ .port_cfgs = {
+ [USB2PHY_PORT_OTG] = {
+ .phy_sus = { 0xe454, 8, 0, 0x052, 0x1d1 },
+ .bvalid_det_en = { 0xe3c0, 3, 3, 0, 1 },
+ .bvalid_det_st = { 0xe3e0, 3, 3, 0, 1 },
+ .bvalid_det_clr = { 0xe3d0, 3, 3, 0, 1 },
+ .idfall_det_en = { 0xe3c0, 5, 5, 0, 1 },
+ .idfall_det_st = { 0xe3e0, 5, 5, 0, 1 },
+ .idfall_det_clr = { 0xe3d0, 5, 5, 0, 1 },
+ .idrise_det_en = { 0xe3c0, 4, 4, 0, 1 },
+ .idrise_det_st = { 0xe3e0, 4, 4, 0, 1 },
+ .idrise_det_clr = { 0xe3d0, 4, 4, 0, 1 },
+ .ls_det_en = { 0xe3c0, 2, 2, 0, 1 },
+ .ls_det_st = { 0xe3e0, 2, 2, 0, 1 },
+ .ls_det_clr = { 0xe3d0, 2, 2, 0, 1 },
+ .utmi_avalid = { 0xe2ac, 7, 7, 0, 1 },
+ .utmi_bvalid = { 0xe2ac, 12, 12, 0, 1 },
+ .utmi_iddig = { 0xe2ac, 8, 8, 0, 1 },
+ .utmi_ls = { 0xe2ac, 14, 13, 0, 1 },
+ .vbus_det_en = { 0x449c, 15, 15, 1, 0 },
+ },
+ [USB2PHY_PORT_HOST] = {
+ .phy_sus = { 0xe458, 1, 0, 0x2, 0x1 },
+ .ls_det_en = { 0xe3c0, 6, 6, 0, 1 },
+ .ls_det_st = { 0xe3e0, 6, 6, 0, 1 },
+ .ls_det_clr = { 0xe3d0, 6, 6, 0, 1 },
+ .utmi_ls = { 0xe2ac, 22, 21, 0, 1 },
+ .utmi_hstdet = { 0xe2ac, 23, 23, 0, 1 }
+ }
+ },
+ .chg_det = {
+ .opmode = { 0xe454, 3, 0, 5, 1 },
+ .cp_det = { 0xe2ac, 2, 2, 0, 1 },
+ .dcp_det = { 0xe2ac, 1, 1, 0, 1 },
+ .dp_det = { 0xe2ac, 0, 0, 0, 1 },
+ .idm_sink_en = { 0xe450, 8, 8, 0, 1 },
+ .idp_sink_en = { 0xe450, 7, 7, 0, 1 },
+ .idp_src_en = { 0xe450, 9, 9, 0, 1 },
+ .rdm_pdwn_en = { 0xe450, 10, 10, 0, 1 },
+ .vdm_src_en = { 0xe450, 12, 12, 0, 1 },
+ .vdp_src_en = { 0xe450, 11, 11, 0, 1 },
+ },
+ },
+ {
+ .reg = 0xe460,
+ .num_ports = 2,
+ .clkout_ctl = { 0xe460, 4, 4, 1, 0 },
+ .port_cfgs = {
+ [USB2PHY_PORT_OTG] = {
+ .phy_sus = { 0xe464, 8, 0, 0x052, 0x1d1 },
+ .bvalid_det_en = { 0xe3c0, 8, 8, 0, 1 },
+ .bvalid_det_st = { 0xe3e0, 8, 8, 0, 1 },
+ .bvalid_det_clr = { 0xe3d0, 8, 8, 0, 1 },
+ .idfall_det_en = { 0xe3c0, 10, 10, 0, 1 },
+ .idfall_det_st = { 0xe3e0, 10, 10, 0, 1 },
+ .idfall_det_clr = { 0xe3d0, 10, 10, 0, 1 },
+ .idrise_det_en = { 0xe3c0, 9, 9, 0, 1 },
+ .idrise_det_st = { 0xe3e0, 9, 9, 0, 1 },
+ .idrise_det_clr = { 0xe3d0, 9, 9, 0, 1 },
+ .ls_det_en = { 0xe3c0, 7, 7, 0, 1 },
+ .ls_det_st = { 0xe3e0, 7, 7, 0, 1 },
+ .ls_det_clr = { 0xe3d0, 7, 7, 0, 1 },
+ .utmi_avalid = { 0xe2ac, 10, 10, 0, 1 },
+ .utmi_bvalid = { 0xe2ac, 16, 16, 0, 1 },
+ .utmi_iddig = { 0xe2ac, 11, 11, 0, 1 },
+ .utmi_ls = { 0xe2ac, 18, 17, 0, 1 },
+ .vbus_det_en = { 0x451c, 15, 15, 1, 0 },
+ },
+ [USB2PHY_PORT_HOST] = {
+ .phy_sus = { 0xe468, 1, 0, 0x2, 0x1 },
+ .ls_det_en = { 0xe3c0, 11, 11, 0, 1 },
+ .ls_det_st = { 0xe3e0, 11, 11, 0, 1 },
+ .ls_det_clr = { 0xe3d0, 11, 11, 0, 1 },
+ .utmi_ls = { 0xe2ac, 26, 25, 0, 1 },
+ .utmi_hstdet = { 0xe2ac, 27, 27, 0, 1 }
+ }
+ },
+ .chg_det = {
+ .opmode = { 0xe464, 3, 0, 5, 1 },
+ .cp_det = { 0xe2ac, 5, 5, 0, 1 },
+ .dcp_det = { 0xe2ac, 4, 4, 0, 1 },
+ .dp_det = { 0xe2ac, 3, 3, 0, 1 },
+ .idm_sink_en = { 0xe460, 8, 8, 0, 1 },
+ .idp_sink_en = { 0xe460, 7, 7, 0, 1 },
+ .idp_src_en = { 0xe460, 9, 9, 0, 1 },
+ .rdm_pdwn_en = { 0xe460, 10, 10, 0, 1 },
+ .vdm_src_en = { 0xe460, 12, 12, 0, 1 },
+ .vdp_src_en = { 0xe460, 11, 11, 0, 1 },
+ },
+ },
+ { /* sentinel */ }
+};
+
+static const struct rockchip_usb2phy_cfg rv1108_phy_cfgs[] = {
+ {
+ .reg = 0x100,
+ .num_ports = 2,
+ .clkout_ctl = { 0x108, 4, 4, 1, 0 },
+ .port_cfgs = {
+ [USB2PHY_PORT_OTG] = {
+ .phy_sus = { 0x0ffa0100, 8, 0, 0, 0x1d1 },
+ .bvalid_det_en = { 0x0680, 3, 3, 0, 1 },
+ .bvalid_det_st = { 0x0690, 3, 3, 0, 1 },
+ .bvalid_det_clr = { 0x06a0, 3, 3, 0, 1 },
+ .ls_det_en = { 0x0680, 2, 2, 0, 1 },
+ .ls_det_st = { 0x0690, 2, 2, 0, 1 },
+ .ls_det_clr = { 0x06a0, 2, 2, 0, 1 },
+ .utmi_bvalid = { 0x0804, 10, 10, 0, 1 },
+ .utmi_ls = { 0x0804, 13, 12, 0, 1 },
+ },
+ [USB2PHY_PORT_HOST] = {
+ .phy_sus = { 0x0ffa0104, 8, 0, 0, 0x1d1 },
+ .ls_det_en = { 0x0680, 4, 4, 0, 1 },
+ .ls_det_st = { 0x0690, 4, 4, 0, 1 },
+ .ls_det_clr = { 0x06a0, 4, 4, 0, 1 },
+ .utmi_ls = { 0x0804, 9, 8, 0, 1 },
+ .utmi_hstdet = { 0x0804, 7, 7, 0, 1 }
+ }
+ },
+ .chg_det = {
+ .opmode = { 0x0ffa0100, 3, 0, 5, 1 },
+ .cp_det = { 0x0804, 1, 1, 0, 1 },
+ .dcp_det = { 0x0804, 0, 0, 0, 1 },
+ .dp_det = { 0x0804, 2, 2, 0, 1 },
+ .idm_sink_en = { 0x0ffa0108, 8, 8, 0, 1 },
+ .idp_sink_en = { 0x0ffa0108, 7, 7, 0, 1 },
+ .idp_src_en = { 0x0ffa0108, 9, 9, 0, 1 },
+ .rdm_pdwn_en = { 0x0ffa0108, 10, 10, 0, 1 },
+ .vdm_src_en = { 0x0ffa0108, 12, 12, 0, 1 },
+ .vdp_src_en = { 0x0ffa0108, 11, 11, 0, 1 },
+ },
+ },
+ { /* sentinel */ }
+};
+
+static const struct rockchip_usb2phy_cfg rk3568_phy_cfgs[] = {
+ {
+ .reg = 0xfe8a0000,
+ .num_ports = 2,
+ .clkout_ctl = { 0x0008, 4, 4, 1, 0 },
+ .port_cfgs = {
+ [USB2PHY_PORT_OTG] = {
+ .phy_sus = { 0x0000, 8, 0, 0x052, 0x1d1 },
+ .bvalid_det_en = { 0x0080, 2, 2, 0, 1 },
+ .bvalid_det_st = { 0x0084, 2, 2, 0, 1 },
+ .bvalid_det_clr = { 0x0088, 2, 2, 0, 1 },
+ .iddig_output = { 0x0000, 10, 10, 0, 1 },
+ .iddig_en = { 0x0000, 9, 9, 0, 1 },
+ .idfall_det_en = { 0x0080, 5, 5, 0, 1 },
+ .idfall_det_st = { 0x0084, 5, 5, 0, 1 },
+ .idfall_det_clr = { 0x0088, 5, 5, 0, 1 },
+ .idrise_det_en = { 0x0080, 4, 4, 0, 1 },
+ .idrise_det_st = { 0x0084, 4, 4, 0, 1 },
+ .idrise_det_clr = { 0x0088, 4, 4, 0, 1 },
+ .ls_det_en = { 0x0080, 0, 0, 0, 1 },
+ .ls_det_st = { 0x0084, 0, 0, 0, 1 },
+ .ls_det_clr = { 0x0088, 0, 0, 0, 1 },
+ .utmi_avalid = { 0x00c0, 10, 10, 0, 1 },
+ .utmi_bvalid = { 0x00c0, 9, 9, 0, 1 },
+ .utmi_iddig = { 0x00c0, 6, 6, 0, 1 },
+ .utmi_ls = { 0x00c0, 5, 4, 0, 1 },
+ },
+ [USB2PHY_PORT_HOST] = {
+ .phy_sus = { 0x0004, 8, 0, 0x1d2, 0x1d1 },
+ .ls_det_en = { 0x0080, 1, 1, 0, 1 },
+ .ls_det_st = { 0x0084, 1, 1, 0, 1 },
+ .ls_det_clr = { 0x0088, 1, 1, 0, 1 },
+ .utmi_ls = { 0x00c0, 17, 16, 0, 1 },
+ .utmi_hstdet = { 0x00c0, 19, 19, 0, 1 }
+ }
+ },
+ .chg_det = {
+ .opmode = { 0x0000, 3, 0, 5, 1 },
+ .cp_det = { 0x00c0, 24, 24, 0, 1 },
+ .dcp_det = { 0x00c0, 23, 23, 0, 1 },
+ .dp_det = { 0x00c0, 25, 25, 0, 1 },
+ .idm_sink_en = { 0x0008, 8, 8, 0, 1 },
+ .idp_sink_en = { 0x0008, 7, 7, 0, 1 },
+ .idp_src_en = { 0x0008, 9, 9, 0, 1 },
+ .rdm_pdwn_en = { 0x0008, 10, 10, 0, 1 },
+ .vdm_src_en = { 0x0008, 12, 12, 0, 1 },
+ .vdp_src_en = { 0x0008, 11, 11, 0, 1 },
+ },
+ },
+ {
+ .reg = 0xfe8b0000,
+ .num_ports = 2,
+ .clkout_ctl = { 0x0008, 4, 4, 1, 0 },
+ .port_cfgs = {
+ [USB2PHY_PORT_OTG] = {
+ .phy_sus = { 0x0000, 8, 0, 0x1d2, 0x1d1 },
+ .ls_det_en = { 0x0080, 0, 0, 0, 1 },
+ .ls_det_st = { 0x0084, 0, 0, 0, 1 },
+ .ls_det_clr = { 0x0088, 0, 0, 0, 1 },
+ .utmi_ls = { 0x00c0, 5, 4, 0, 1 },
+ .utmi_hstdet = { 0x00c0, 7, 7, 0, 1 }
+ },
+ [USB2PHY_PORT_HOST] = {
+ .phy_sus = { 0x0004, 8, 0, 0x1d2, 0x1d1 },
+ .ls_det_en = { 0x0080, 1, 1, 0, 1 },
+ .ls_det_st = { 0x0084, 1, 1, 0, 1 },
+ .ls_det_clr = { 0x0088, 1, 1, 0, 1 },
+ .utmi_ls = { 0x00c0, 17, 16, 0, 1 },
+ .utmi_hstdet = { 0x00c0, 19, 19, 0, 1 }
+ }
+ },
+ },
+ { /* sentinel */ }
+};
+static const struct of_device_id rockchip_usb2phy_dt_match[] = {
+ { .compatible = "rockchip,rk1808-usb2phy", .data = &rk1808_phy_cfgs },
+ { .compatible = "rockchip,rk3128-usb2phy", .data = &rk312x_phy_cfgs },
+ { .compatible = "rockchip,rk322x-usb2phy", .data = &rk322x_phy_cfgs },
+ { .compatible = "rockchip,rk3308-usb2phy", .data = &rk3328_phy_cfgs },
+ { .compatible = "rockchip,rk3328-usb2phy", .data = &rk3328_phy_cfgs },
+ { .compatible = "rockchip,rk3368-usb2phy", .data = &rk3368_phy_cfgs },
+ { .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs },
+ { .compatible = "rockchip,rk3568-usb2phy", .data = &rk3568_phy_cfgs },
+ { .compatible = "rockchip,rv1108-usb2phy", .data = &rv1108_phy_cfgs },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rockchip_usb2phy_dt_match);
+
+static struct driver rockchip_usb2phy_driver = {
+ .probe = rockchip_usb2phy_probe,
+ .name = "rockchip-usb2phy",
+ .of_compatible = rockchip_usb2phy_dt_match,
+};
+coredevice_platform_driver(rockchip_usb2phy_driver);
diff --git a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
new file mode 100644
index 0000000000..b864ecb76e
--- /dev/null
+++ b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
@@ -0,0 +1,870 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Rockchip PIPE USB3.0 PCIE SATA combphy driver
+ *
+ * Copyright (C) 2020 Rockchip Electronics Co., Ltd.
+ */
+
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <of.h>
+#include <errno.h>
+#include <driver.h>
+#include <malloc.h>
+#include <linux/usb/phy.h>
+#include <linux/phy/phy.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/reset.h>
+#include <linux/regmap.h>
+#include <mfd/syscon.h>
+#include <linux/iopoll.h>
+#include <dt-bindings/phy/phy.h>
+
+#define BIT_WRITEABLE_SHIFT 16
+
+struct rockchip_combphy_priv;
+
+struct combphy_reg {
+ u16 offset;
+ u16 bitend;
+ u16 bitstart;
+ u16 disable;
+ u16 enable;
+};
+
+struct rockchip_combphy_grfcfg {
+ struct combphy_reg pcie_mode_set;
+ struct combphy_reg usb_mode_set;
+ struct combphy_reg sgmii_mode_set;
+ struct combphy_reg qsgmii_mode_set;
+ struct combphy_reg pipe_rxterm_set;
+ struct combphy_reg pipe_txelec_set;
+ struct combphy_reg pipe_txcomp_set;
+ struct combphy_reg pipe_clk_24m;
+ struct combphy_reg pipe_clk_25m;
+ struct combphy_reg pipe_clk_100m;
+ struct combphy_reg pipe_phymode_sel;
+ struct combphy_reg pipe_rate_sel;
+ struct combphy_reg pipe_rxterm_sel;
+ struct combphy_reg pipe_txelec_sel;
+ struct combphy_reg pipe_txcomp_sel;
+ struct combphy_reg pipe_clk_ext;
+ struct combphy_reg pipe_sel_usb;
+ struct combphy_reg pipe_sel_qsgmii;
+ struct combphy_reg pipe_phy_status;
+ struct combphy_reg con0_for_pcie;
+ struct combphy_reg con1_for_pcie;
+ struct combphy_reg con2_for_pcie;
+ struct combphy_reg con3_for_pcie;
+ struct combphy_reg con0_for_sata;
+ struct combphy_reg con1_for_sata;
+ struct combphy_reg con2_for_sata;
+ struct combphy_reg con3_for_sata;
+ struct combphy_reg pipe_con0_for_sata;
+ struct combphy_reg pipe_con1_for_sata;
+ struct combphy_reg pipe_sgmii_mac_sel;
+ struct combphy_reg pipe_xpcs_phy_ready;
+ struct combphy_reg u3otg0_port_en;
+ struct combphy_reg u3otg1_port_en;
+};
+
+struct rockchip_combphy_cfg {
+ const int num_clks;
+ const struct clk_bulk_data *clks;
+ const struct rockchip_combphy_grfcfg *grfcfg;
+ bool force_det_out; /* Tx detect Rx errata */
+ int (*combphy_cfg)(struct rockchip_combphy_priv *priv);
+};
+
+struct rockchip_combphy_priv {
+ u8 mode;
+ void __iomem *mmio;
+ int num_clks;
+ struct clk_bulk_data *clks;
+ struct device *dev;
+ struct regmap *pipe_grf;
+ struct regmap *phy_grf;
+ struct phy *phy;
+ struct reset_control *phy_rst;
+ const struct rockchip_combphy_cfg *cfg;
+};
+
+static inline bool param_read(struct regmap *base,
+ const struct combphy_reg *reg, u32 val)
+{
+ int ret;
+ u32 mask, orig, tmp;
+
+ ret = regmap_read(base, reg->offset, &orig);
+ if (ret)
+ return false;
+
+ mask = GENMASK(reg->bitend, reg->bitstart);
+ tmp = (orig & mask) >> reg->bitstart;
+
+ return tmp == val;
+}
+
+static int param_write(struct regmap *base,
+ const struct combphy_reg *reg, bool en)
+{
+ u32 val, mask, tmp;
+
+ tmp = en ? reg->enable : reg->disable;
+ mask = GENMASK(reg->bitend, reg->bitstart);
+ val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT);
+
+ return regmap_write(base, reg->offset, val);
+}
+
+static u32 rockchip_combphy_is_ready(struct rockchip_combphy_priv *priv)
+{
+ const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg;
+ u32 mask, val;
+
+ mask = GENMASK(cfg->pipe_phy_status.bitend,
+ cfg->pipe_phy_status.bitstart);
+
+ regmap_read(priv->phy_grf, cfg->pipe_phy_status.offset, &val);
+ val = (val & mask) >> cfg->pipe_phy_status.bitstart;
+
+ return val;
+}
+
+static int rockchip_combphy_pcie_init(struct rockchip_combphy_priv *priv)
+{
+ int ret = 0;
+
+ if (priv->cfg->combphy_cfg) {
+ ret = priv->cfg->combphy_cfg(priv);
+ if (ret) {
+ dev_err(priv->dev, "failed to init phy for pcie\n");
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int rockchip_combphy_usb3_init(struct rockchip_combphy_priv *priv)
+{
+ int ret = 0;
+
+ if (priv->cfg->combphy_cfg) {
+ ret = priv->cfg->combphy_cfg(priv);
+ if (ret) {
+ dev_err(priv->dev, "failed to init phy for usb3\n");
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int rockchip_combphy_sata_init(struct rockchip_combphy_priv *priv)
+{
+ int ret = 0;
+
+ if (priv->cfg->combphy_cfg) {
+ ret = priv->cfg->combphy_cfg(priv);
+ if (ret) {
+ dev_err(priv->dev, "failed to init phy for sata\n");
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int rockchip_combphy_sgmii_init(struct rockchip_combphy_priv *priv)
+{
+ int ret = 0;
+
+ if (priv->cfg->combphy_cfg) {
+ ret = priv->cfg->combphy_cfg(priv);
+ if (ret) {
+ dev_err(priv->dev, "failed to init phy for sgmii\n");
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int rockchip_combphy_set_mode(struct rockchip_combphy_priv *priv)
+{
+ switch (priv->mode) {
+ case PHY_TYPE_PCIE:
+ rockchip_combphy_pcie_init(priv);
+ break;
+ case PHY_TYPE_USB3:
+ rockchip_combphy_usb3_init(priv);
+ break;
+ case PHY_TYPE_SATA:
+ rockchip_combphy_sata_init(priv);
+ break;
+ case PHY_TYPE_SGMII:
+ case PHY_TYPE_QSGMII:
+ return rockchip_combphy_sgmii_init(priv);
+ default:
+ dev_err(priv->dev, "incompatible PHY type\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rockchip_combphy_init(struct phy *phy)
+{
+ struct rockchip_combphy_priv *priv = phy_get_drvdata(phy);
+ const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg;
+ u32 val;
+ int ret;
+
+ ret = clk_bulk_enable(priv->num_clks, priv->clks);
+ if (ret) {
+ dev_err(priv->dev, "failed to enable clks\n");
+ return ret;
+ }
+
+ ret = rockchip_combphy_set_mode(priv);
+ if (ret)
+ goto err_clk;
+
+ ret = reset_control_deassert(priv->phy_rst);
+ if (ret)
+ goto err_clk;
+
+ if (priv->mode == PHY_TYPE_USB3) {
+ ret = readx_poll_timeout(rockchip_combphy_is_ready,
+ priv, val,
+ val == cfg->pipe_phy_status.enable,
+ 1000);
+ if (ret)
+ dev_warn(priv->dev, "wait phy status ready timeout\n");
+ }
+
+ return 0;
+
+err_clk:
+ clk_bulk_disable(priv->num_clks, priv->clks);
+
+ return ret;
+}
+
+static int rockchip_combphy_exit(struct phy *phy)
+{
+ struct rockchip_combphy_priv *priv = phy_get_drvdata(phy);
+
+ clk_bulk_disable(priv->num_clks, priv->clks);
+ reset_control_assert(priv->phy_rst);
+
+ return 0;
+}
+
+static const struct phy_ops rochchip_combphy_ops = {
+ .init = rockchip_combphy_init,
+ .exit = rockchip_combphy_exit,
+};
+
+static struct phy *rockchip_combphy_xlate(struct device *dev,
+ struct of_phandle_args *args)
+{
+ struct rockchip_combphy_priv *priv = dev->priv;
+
+ if (args->args_count != 1) {
+ dev_err(dev, "invalid number of arguments\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (priv->mode != PHY_NONE && priv->mode != args->args[0])
+ dev_warn(dev, "phy type select %d overwriting type %d\n",
+ args->args[0], priv->mode);
+
+ priv->mode = args->args[0];
+
+ return priv->phy;
+}
+
+static int rockchip_combphy_parse_dt(struct device *dev,
+ struct rockchip_combphy_priv *priv)
+{
+ struct device_node *np = dev->of_node;
+ const struct rockchip_combphy_cfg *phy_cfg = priv->cfg;
+ int ret, mac_id;
+
+ ret = clk_bulk_get(dev, priv->num_clks, priv->clks);
+ if (ret == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ if (ret)
+ priv->num_clks = 0;
+
+ priv->pipe_grf = syscon_regmap_lookup_by_phandle(np,
+ "rockchip,pipe-grf");
+ if (IS_ERR(priv->pipe_grf)) {
+ dev_err(dev, "failed to find peri_ctrl pipe-grf regmap\n");
+ return PTR_ERR(priv->pipe_grf);
+ }
+
+ priv->phy_grf = syscon_regmap_lookup_by_phandle(np,
+ "rockchip,pipe-phy-grf");
+ if (IS_ERR(priv->phy_grf)) {
+ dev_err(dev, "failed to find peri_ctrl pipe-phy-grf regmap\n");
+ return PTR_ERR(priv->phy_grf);
+ }
+
+ if (!of_property_read_u32(np, "rockchip,sgmii-mac-sel", &mac_id) &&
+ (mac_id > 0))
+ param_write(priv->pipe_grf, &phy_cfg->grfcfg->pipe_sgmii_mac_sel,
+ true);
+
+ priv->phy_rst = reset_control_get(dev, NULL);
+ if (IS_ERR(priv->phy_rst))
+ return dev_errp_probe(dev, priv->phy_rst, "getting phy reset\n");
+
+ return reset_control_assert(priv->phy_rst);
+}
+
+static int rockchip_combphy_probe(struct device *dev)
+{
+ struct phy_provider *phy_provider;
+ struct rockchip_combphy_priv *priv;
+ const struct rockchip_combphy_cfg *phy_cfg;
+ struct resource *res;
+ int ret;
+
+ phy_cfg = device_get_match_data(dev);
+ if (!phy_cfg) {
+ dev_err(dev, "No OF match data provided\n");
+ return -EINVAL;
+ }
+
+ priv = xzalloc(sizeof(*priv));
+ if (!priv)
+ return -ENOMEM;
+
+ res = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(res)) {
+ ret = PTR_ERR(res);
+ return ret;
+ }
+
+ priv->mmio = IOMEM(res->start);
+
+ priv->num_clks = phy_cfg->num_clks;
+
+ priv->clks = memdup(phy_cfg->clks,
+ phy_cfg->num_clks * sizeof(struct clk_bulk_data));
+ if (!priv->clks)
+ return -ENOMEM;
+
+ priv->dev = dev;
+ priv->mode = PHY_NONE;
+ priv->cfg = phy_cfg;
+
+ ret = rockchip_combphy_parse_dt(dev, priv);
+ if (ret)
+ return ret;
+
+ priv->phy = phy_create(dev, NULL, &rochchip_combphy_ops);
+ if (IS_ERR(priv->phy)) {
+ dev_err(dev, "failed to create combphy\n");
+ return PTR_ERR(priv->phy);
+ }
+
+ dev->priv = priv;
+ phy_set_drvdata(priv->phy, priv);
+
+ phy_provider = of_phy_provider_register(dev, rockchip_combphy_xlate);
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static int rk3568_combphy_cfg(struct rockchip_combphy_priv *priv)
+{
+ struct device_node *np = priv->dev->of_node;
+ const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg;
+ struct clk *refclk = NULL;
+ unsigned long rate;
+ u32 val;
+
+ /* Configure PHY reference clock frequency */
+ refclk = priv->clks[0].clk;
+ if (!refclk) {
+ dev_err(priv->dev, "No refclk found\n");
+ return -EINVAL;
+ }
+
+ switch (priv->mode) {
+ case PHY_TYPE_PCIE:
+ /* Set SSC downward spread spectrum */
+ val = readl(priv->mmio + (0x1f << 2));
+ val &= ~GENMASK(5, 4);
+ val |= 0x01 << 4;
+ writel(val, priv->mmio + 0x7c);
+
+ param_write(priv->phy_grf, &cfg->con0_for_pcie, true);
+ param_write(priv->phy_grf, &cfg->con1_for_pcie, true);
+ param_write(priv->phy_grf, &cfg->con2_for_pcie, true);
+ param_write(priv->phy_grf, &cfg->con3_for_pcie, true);
+ break;
+ case PHY_TYPE_USB3:
+ /* Set SSC downward spread spectrum */
+ val = readl(priv->mmio + (0x1f << 2));
+ val &= ~GENMASK(5, 4);
+ val |= 0x01 << 4;
+ writel(val, priv->mmio + 0x7c);
+
+ /* Enable adaptive CTLE for USB3.0 Rx */
+ val = readl(priv->mmio + (0x0e << 2));
+ val &= ~GENMASK(0, 0);
+ val |= 0x01;
+ writel(val, priv->mmio + (0x0e << 2));
+
+ param_write(priv->phy_grf, &cfg->pipe_sel_usb, true);
+ param_write(priv->phy_grf, &cfg->pipe_txcomp_sel, false);
+ param_write(priv->phy_grf, &cfg->pipe_txelec_sel, false);
+ param_write(priv->phy_grf, &cfg->usb_mode_set, true);
+ break;
+ case PHY_TYPE_SATA:
+ writel(0x41, priv->mmio + 0x38);
+ writel(0x8F, priv->mmio + 0x18);
+ param_write(priv->phy_grf, &cfg->con0_for_sata, true);
+ param_write(priv->phy_grf, &cfg->con1_for_sata, true);
+ param_write(priv->phy_grf, &cfg->con2_for_sata, true);
+ param_write(priv->phy_grf, &cfg->con3_for_sata, true);
+ param_write(priv->pipe_grf, &cfg->pipe_con0_for_sata, true);
+ break;
+ case PHY_TYPE_SGMII:
+ param_write(priv->pipe_grf, &cfg->pipe_xpcs_phy_ready, true);
+ param_write(priv->phy_grf, &cfg->pipe_phymode_sel, true);
+ param_write(priv->phy_grf, &cfg->pipe_sel_qsgmii, true);
+ param_write(priv->phy_grf, &cfg->sgmii_mode_set, true);
+ break;
+ case PHY_TYPE_QSGMII:
+ param_write(priv->pipe_grf, &cfg->pipe_xpcs_phy_ready, true);
+ param_write(priv->phy_grf, &cfg->pipe_phymode_sel, true);
+ param_write(priv->phy_grf, &cfg->pipe_rate_sel, true);
+ param_write(priv->phy_grf, &cfg->pipe_sel_qsgmii, true);
+ param_write(priv->phy_grf, &cfg->qsgmii_mode_set, true);
+ break;
+ default:
+ dev_err(priv->dev, "incompatible PHY type\n");
+ return -EINVAL;
+ }
+
+ rate = clk_get_rate(refclk);
+
+ switch (rate) {
+ case 24000000:
+ if (priv->mode == PHY_TYPE_USB3 || priv->mode == PHY_TYPE_SATA) {
+ /* Set ssc_cnt[9:0]=0101111101 & 31.5KHz */
+ val = readl(priv->mmio + (0x0e << 2));
+ val &= ~GENMASK(7, 6);
+ val |= 0x01 << 6;
+ writel(val, priv->mmio + (0x0e << 2));
+
+ val = readl(priv->mmio + (0x0f << 2));
+ val &= ~GENMASK(7, 0);
+ val |= 0x5f;
+ writel(val, priv->mmio + (0x0f << 2));
+ }
+ break;
+ case 25000000:
+ param_write(priv->phy_grf, &cfg->pipe_clk_25m, true);
+ break;
+ case 100000000:
+ param_write(priv->phy_grf, &cfg->pipe_clk_100m, true);
+ if (priv->mode == PHY_TYPE_PCIE) {
+ /* PLL KVCO tuning fine */
+ val = readl(priv->mmio + (0x20 << 2));
+ val &= ~(0x7 << 2);
+ val |= 0x2 << 2;
+ writel(val, priv->mmio + (0x20 << 2));
+
+ /* Enable controlling random jitter, aka RMJ */
+ writel(0x4, priv->mmio + (0xb << 2));
+
+ val = readl(priv->mmio + (0x5 << 2));
+ val &= ~(0x3 << 6);
+ val |= 0x1 << 6;
+ writel(val, priv->mmio + (0x5 << 2));
+
+ writel(0x32, priv->mmio + (0x11 << 2));
+ writel(0xf0, priv->mmio + (0xa << 2));
+ } else if (priv->mode == PHY_TYPE_SATA) {
+ /* downward spread spectrum +500ppm */
+ val = readl(priv->mmio + (0x1f << 2));
+ val &= ~GENMASK(7, 4);
+ val |= 0x50;
+ writel(val, priv->mmio + (0x1f << 2));
+ }
+ break;
+ default:
+ dev_err(priv->dev, "Unsupported rate: %lu\n", rate);
+ return -EINVAL;
+ }
+
+ if (of_property_read_bool(np, "rockchip,ext-refclk")) {
+ param_write(priv->phy_grf, &cfg->pipe_clk_ext, true);
+ if (priv->mode == PHY_TYPE_PCIE && rate == 100000000) {
+ val = readl(priv->mmio + (0xc << 2));
+ val |= 0x3 << 4 | 0x1 << 7;
+ writel(val, priv->mmio + (0xc << 2));
+
+ val = readl(priv->mmio + (0xd << 2));
+ val |= 0x1;
+ writel(val, priv->mmio + (0xd << 2));
+ }
+ }
+
+ if (of_property_read_bool(np, "rockchip,enable-ssc")) {
+ val = readl(priv->mmio + (0x7 << 2));
+ val |= BIT(4);
+ writel(val, priv->mmio + (0x7 << 2));
+ }
+
+ return 0;
+}
+
+static const struct rockchip_combphy_grfcfg rk3568_combphy_grfcfgs = {
+ /* pipe-phy-grf */
+ .pcie_mode_set = { 0x0000, 5, 0, 0x00, 0x11 },
+ .usb_mode_set = { 0x0000, 5, 0, 0x00, 0x04 },
+ .sgmii_mode_set = { 0x0000, 5, 0, 0x00, 0x01 },
+ .qsgmii_mode_set = { 0x0000, 5, 0, 0x00, 0x21 },
+ .pipe_rxterm_set = { 0x0000, 12, 12, 0x00, 0x01 },
+ .pipe_txelec_set = { 0x0004, 1, 1, 0x00, 0x01 },
+ .pipe_txcomp_set = { 0x0004, 4, 4, 0x00, 0x01 },
+ .pipe_clk_25m = { 0x0004, 14, 13, 0x00, 0x01 },
+ .pipe_clk_100m = { 0x0004, 14, 13, 0x00, 0x02 },
+ .pipe_phymode_sel = { 0x0008, 1, 1, 0x00, 0x01 },
+ .pipe_rate_sel = { 0x0008, 2, 2, 0x00, 0x01 },
+ .pipe_rxterm_sel = { 0x0008, 8, 8, 0x00, 0x01 },
+ .pipe_txelec_sel = { 0x0008, 12, 12, 0x00, 0x01 },
+ .pipe_txcomp_sel = { 0x0008, 15, 15, 0x00, 0x01 },
+ .pipe_clk_ext = { 0x000c, 9, 8, 0x02, 0x01 },
+ .pipe_sel_usb = { 0x000c, 14, 13, 0x00, 0x01 },
+ .pipe_sel_qsgmii = { 0x000c, 15, 13, 0x00, 0x07 },
+ .pipe_phy_status = { 0x0034, 6, 6, 0x01, 0x00 },
+ .con0_for_pcie = { 0x0000, 15, 0, 0x00, 0x1000 },
+ .con1_for_pcie = { 0x0004, 15, 0, 0x00, 0x0000 },
+ .con2_for_pcie = { 0x0008, 15, 0, 0x00, 0x0101 },
+ .con3_for_pcie = { 0x000c, 15, 0, 0x00, 0x0200 },
+ .con0_for_sata = { 0x0000, 15, 0, 0x00, 0x0119 },
+ .con1_for_sata = { 0x0004, 15, 0, 0x00, 0x0040 },
+ .con2_for_sata = { 0x0008, 15, 0, 0x00, 0x80c3 },
+ .con3_for_sata = { 0x000c, 15, 0, 0x00, 0x4407 },
+ /* pipe-grf */
+ .pipe_con0_for_sata = { 0x0000, 15, 0, 0x00, 0x2220 },
+ .pipe_sgmii_mac_sel = { 0x0040, 1, 1, 0x00, 0x01 },
+ .pipe_xpcs_phy_ready = { 0x0040, 2, 2, 0x00, 0x01 },
+ .u3otg0_port_en = { 0x0104, 15, 0, 0x0181, 0x1100 },
+ .u3otg1_port_en = { 0x0144, 15, 0, 0x0181, 0x1100 },
+};
+
+static const struct clk_bulk_data rk3568_clks[] = {
+ { .id = "ref" },
+ { .id = "apb" },
+ { .id = "pipe" },
+};
+
+static const struct rockchip_combphy_cfg rk3568_combphy_cfgs = {
+ .num_clks = ARRAY_SIZE(rk3568_clks),
+ .clks = rk3568_clks,
+ .grfcfg = &rk3568_combphy_grfcfgs,
+ .combphy_cfg = rk3568_combphy_cfg,
+};
+
+static int rk3588_combphy_cfg(struct rockchip_combphy_priv *priv)
+{
+ const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg;
+ struct clk *refclk = NULL;
+ unsigned long rate;
+ int i;
+ u32 val;
+
+ /* Configure PHY reference clock frequency */
+ for (i = 0; i < priv->num_clks; i++) {
+ if (!strncmp(priv->clks[i].id, "ref", 6)) {
+ refclk = priv->clks[i].clk;
+ break;
+ }
+ }
+
+ if (!refclk) {
+ dev_err(priv->dev, "No refclk found\n");
+ return -EINVAL;
+ }
+
+ switch (priv->mode) {
+ case PHY_TYPE_PCIE:
+ /* Set SSC downward spread spectrum */
+ val = readl(priv->mmio + (0x1f << 2));
+ val &= ~GENMASK(5, 4);
+ val |= 0x01 << 4;
+ writel(val, priv->mmio + 0x7c);
+
+ param_write(priv->phy_grf, &cfg->con0_for_pcie, true);
+ param_write(priv->phy_grf, &cfg->con1_for_pcie, true);
+ param_write(priv->phy_grf, &cfg->con2_for_pcie, true);
+ param_write(priv->phy_grf, &cfg->con3_for_pcie, true);
+ break;
+ case PHY_TYPE_USB3:
+ /* Set SSC downward spread spectrum */
+ val = readl(priv->mmio + (0x1f << 2));
+ val &= ~GENMASK(5, 4);
+ val |= 0x01 << 4;
+ writel(val, priv->mmio + 0x7c);
+
+ /* Enable adaptive CTLE for USB3.0 Rx */
+ val = readl(priv->mmio + (0x0e << 2));
+ val &= ~GENMASK(0, 0);
+ val |= 0x01;
+ writel(val, priv->mmio + (0x0e << 2));
+
+ /* Set PLL KVCO fine tuning signals */
+ val = readl(priv->mmio + (0x20 << 2));
+ val &= ~(0x7 << 2);
+ val |= 0x2 << 2;
+ writel(val, priv->mmio + (0x20 << 2));
+
+ /* Set PLL LPF R1 to su_trim[10:7]=1001 */
+ writel(0x4, priv->mmio + (0xb << 2));
+
+ /* Set PLL input clock divider 1/2 */
+ val = readl(priv->mmio + (0x5 << 2));
+ val &= ~(0x3 << 6);
+ val |= 0x1 << 6;
+ writel(val, priv->mmio + (0x5 << 2));
+
+ /* Set PLL loop divider */
+ writel(0x32, priv->mmio + (0x11 << 2));
+
+ /* Set PLL KVCO to min and set PLL charge pump current to max */
+ writel(0xf0, priv->mmio + (0xa << 2));
+
+ /* Set Rx squelch input filler bandwidth */
+ writel(0x0d, priv->mmio + (0x14 << 2));
+
+ param_write(priv->phy_grf, &cfg->pipe_txcomp_sel, false);
+ param_write(priv->phy_grf, &cfg->pipe_txelec_sel, false);
+ param_write(priv->phy_grf, &cfg->usb_mode_set, true);
+ break;
+ case PHY_TYPE_SATA:
+ /* Enable adaptive CTLE for SATA Rx */
+ val = readl(priv->mmio + (0x0e << 2));
+ val &= ~GENMASK(0, 0);
+ val |= 0x01;
+ writel(val, priv->mmio + (0x0e << 2));
+ /* Set tx_rterm = 50 ohm and rx_rterm = 43.5 ohm */
+ writel(0x8F, priv->mmio + (0x06 << 2));
+
+ param_write(priv->phy_grf, &cfg->con0_for_sata, true);
+ param_write(priv->phy_grf, &cfg->con1_for_sata, true);
+ param_write(priv->phy_grf, &cfg->con2_for_sata, true);
+ param_write(priv->phy_grf, &cfg->con3_for_sata, true);
+ param_write(priv->pipe_grf, &cfg->pipe_con0_for_sata, true);
+ param_write(priv->pipe_grf, &cfg->pipe_con1_for_sata, true);
+ break;
+ case PHY_TYPE_SGMII:
+ case PHY_TYPE_QSGMII:
+ default:
+ dev_err(priv->dev, "incompatible PHY type\n");
+ return -EINVAL;
+ }
+
+ rate = clk_get_rate(refclk);
+
+ switch (rate) {
+ case 24000000:
+ param_write(priv->phy_grf, &cfg->pipe_clk_24m, true);
+ if (priv->mode == PHY_TYPE_USB3 || priv->mode == PHY_TYPE_SATA) {
+ /* Set ssc_cnt[9:0]=0101111101 & 31.5KHz */
+ val = readl(priv->mmio + (0x0e << 2));
+ val &= ~GENMASK(7, 6);
+ val |= 0x01 << 6;
+ writel(val, priv->mmio + (0x0e << 2));
+
+ val = readl(priv->mmio + (0x0f << 2));
+ val &= ~GENMASK(7, 0);
+ val |= 0x5f;
+ writel(val, priv->mmio + (0x0f << 2));
+ } else if (priv->mode == PHY_TYPE_PCIE) {
+ /* PLL KVCO tuning fine */
+ val = readl(priv->mmio + (0x20 << 2));
+ val &= ~GENMASK(4, 2);
+ val |= 0x4 << 2;
+ writel(val, priv->mmio + (0x20 << 2));
+
+ /* Set up rx_trim */
+ val = 0x0;
+ writel(val, priv->mmio + (0x1b << 2));
+
+ /* Set up su_trim: T0_1 */
+ val = 0x90;
+ writel(val, priv->mmio + (0xa << 2));
+ val = 0x02;
+ writel(val, priv->mmio + (0xb << 2));
+ val = 0x57;
+ writel(val, priv->mmio + (0xd << 2));
+
+ val = 0x5f;
+ writel(val, priv->mmio + (0xf << 2));
+ }
+ break;
+ case 25000000:
+ param_write(priv->phy_grf, &cfg->pipe_clk_25m, true);
+ break;
+ case 100000000:
+ param_write(priv->phy_grf, &cfg->pipe_clk_100m, true);
+ if (priv->mode == PHY_TYPE_PCIE) {
+ /* gate_tx_pck_sel length select work for L1SS */
+ val = 0xc0;
+ writel(val, priv->mmio + 0x74);
+
+ /* PLL KVCO tuning fine */
+ val = readl(priv->mmio + (0x20 << 2));
+ val &= ~GENMASK(4, 2);
+ val |= 0x4 << 2;
+ writel(val, priv->mmio + (0x20 << 2));
+
+ /* Set up rx_trim: PLL LPF C1 85pf R1 1.25kohm */
+ val = 0x4c;
+ writel(val, priv->mmio + (0x1b << 2));
+
+ /* Set up su_trim: T3_P1 650mv */
+ val = 0x90;
+ writel(val, priv->mmio + (0xa << 2));
+ val = 0x43;
+ writel(val, priv->mmio + (0xb << 2));
+ val = 0x88;
+ writel(val, priv->mmio + (0xc << 2));
+ val = 0x56;
+ writel(val, priv->mmio + (0xd << 2));
+ } else if (priv->mode == PHY_TYPE_SATA) {
+ /* downward spread spectrum +500ppm */
+ val = readl(priv->mmio + (0x1f << 2));
+ val &= ~GENMASK(7, 4);
+ val |= 0x50;
+ writel(val, priv->mmio + (0x1f << 2));
+
+ /* ssc ppm adjust to 3500ppm */
+ val = readl(priv->mmio + (0x9 << 2));
+ val &= ~GENMASK(3, 0);
+ val |= 0x7;
+ writel(val, priv->mmio + (0x9 << 2));
+ }
+ break;
+ default:
+ dev_err(priv->dev, "Unsupported rate: %lu\n", rate);
+ return -EINVAL;
+ }
+
+ if (of_property_read_bool(priv->dev->of_node, "rockchip,ext-refclk")) {
+ param_write(priv->phy_grf, &cfg->pipe_clk_ext, true);
+ if (priv->mode == PHY_TYPE_PCIE && rate == 100000000) {
+ val = 0x10;
+ writel(val, priv->mmio + (0x20 << 2));
+
+ val = 0x0c;
+ writel(val, priv->mmio + (0x1b << 2));
+
+ /* Set up su_trim: T3_P1 650mv */
+ val = 0x90;
+ writel(val, priv->mmio + (0xa << 2));
+ val = 0x43;
+ writel(val, priv->mmio + (0xb << 2));
+ val = 0x88;
+ writel(val, priv->mmio + (0xc << 2));
+ val = 0x56;
+ writel(val, priv->mmio + (0xd << 2));
+ }
+ }
+
+ if (of_property_read_bool(priv->dev->of_node, "rockchip,enable-ssc")) {
+ val = readl(priv->mmio + (0x7 << 2));
+ val |= BIT(4);
+ writel(val, priv->mmio + (0x7 << 2));
+
+ if (priv->mode == PHY_TYPE_PCIE && rate == 24000000) {
+ /* Xin24M T0_1 650mV */
+ writel(0x00, priv->mmio + (0x10 << 2));
+ writel(0x32, priv->mmio + (0x11 << 2));
+ writel(0x00, priv->mmio + (0x1b << 2));
+ writel(0x90, priv->mmio + (0x0a << 2));
+ writel(0x02, priv->mmio + (0x0b << 2));
+ writel(0x08, priv->mmio + (0x0c << 2));
+ writel(0x57, priv->mmio + (0x0d << 2));
+ writel(0x40, priv->mmio + (0x0e << 2));
+ writel(0x5f, priv->mmio + (0x0f << 2));
+ writel(0x10, priv->mmio + (0x20 << 2));
+ }
+ }
+
+ return 0;
+}
+
+static const struct rockchip_combphy_grfcfg rk3588_combphy_grfcfgs = {
+ /* pipe-phy-grf */
+ .pcie_mode_set = { 0x0000, 5, 0, 0x00, 0x11 },
+ .usb_mode_set = { 0x0000, 5, 0, 0x00, 0x04 },
+ .pipe_rxterm_set = { 0x0000, 12, 12, 0x00, 0x01 },
+ .pipe_txelec_set = { 0x0004, 1, 1, 0x00, 0x01 },
+ .pipe_txcomp_set = { 0x0004, 4, 4, 0x00, 0x01 },
+ .pipe_clk_24m = { 0x0004, 14, 13, 0x00, 0x00 },
+ .pipe_clk_25m = { 0x0004, 14, 13, 0x00, 0x01 },
+ .pipe_clk_100m = { 0x0004, 14, 13, 0x00, 0x02 },
+ .pipe_rxterm_sel = { 0x0008, 8, 8, 0x00, 0x01 },
+ .pipe_txelec_sel = { 0x0008, 12, 12, 0x00, 0x01 },
+ .pipe_txcomp_sel = { 0x0008, 15, 15, 0x00, 0x01 },
+ .pipe_clk_ext = { 0x000c, 9, 8, 0x02, 0x01 },
+ .pipe_phy_status = { 0x0034, 6, 6, 0x01, 0x00 },
+ .con0_for_pcie = { 0x0000, 15, 0, 0x00, 0x1000 },
+ .con1_for_pcie = { 0x0004, 15, 0, 0x00, 0x0000 },
+ .con2_for_pcie = { 0x0008, 15, 0, 0x00, 0x0101 },
+ .con3_for_pcie = { 0x000c, 15, 0, 0x00, 0x0200 },
+ .con0_for_sata = { 0x0000, 15, 0, 0x00, 0x0129 },
+ .con1_for_sata = { 0x0004, 15, 0, 0x00, 0x0000 },
+ .con2_for_sata = { 0x0008, 15, 0, 0x00, 0x80c1 },
+ .con3_for_sata = { 0x000c, 15, 0, 0x00, 0x0407 },
+ /* pipe-grf */
+ .pipe_con0_for_sata = { 0x0000, 11, 5, 0x00, 0x22 },
+ .pipe_con1_for_sata = { 0x0004, 2, 0, 0x00, 0x2 },
+};
+
+
+static const struct clk_bulk_data rk3588_clks[] = {
+ { .id = "ref" },
+ { .id = "apb" },
+ { .id = "pipe" },
+};
+
+static const struct rockchip_combphy_cfg rk3588_combphy_cfgs = {
+ .num_clks = ARRAY_SIZE(rk3588_clks),
+ .clks = rk3588_clks,
+ .grfcfg = &rk3588_combphy_grfcfgs,
+ .combphy_cfg = rk3588_combphy_cfg,
+ .force_det_out = true,
+};
+
+static const struct of_device_id rockchip_combphy_of_match[] = {
+ {
+ .compatible = "rockchip,rk3568-naneng-combphy",
+ .data = &rk3568_combphy_cfgs,
+ }, {
+ .compatible = "rockchip,rk3588-naneng-combphy",
+ .data = &rk3588_combphy_cfgs,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, rockchip_combphy_of_match);
+
+static struct driver rockchip_combphy_driver = {
+ .probe = rockchip_combphy_probe,
+ .name = "naneng-combphy",
+ .of_compatible = rockchip_combphy_of_match,
+};
+coredevice_platform_driver(rockchip_combphy_driver);
diff --git a/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c
new file mode 100644
index 0000000000..7f39b261ca
--- /dev/null
+++ b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c
@@ -0,0 +1,317 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip PCIE3.0 phy driver
+ *
+ * Copyright (C) 2022 Rockchip Electronics Co., Ltd.
+ */
+
+#include <of.h>
+#include <common.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+//#include <linux/phy/pcie.h>
+#include <mfd/syscon.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+/* Register for RK3568 */
+#define GRF_PCIE30PHY_CON1 0x4
+#define GRF_PCIE30PHY_CON6 0x18
+#define GRF_PCIE30PHY_CON9 0x24
+#define GRF_PCIE30PHY_DA_OCM (BIT(15) | BIT(31))
+#define GRF_PCIE30PHY_STATUS0 0x80
+#define GRF_PCIE30PHY_WR_EN (0xf << 16)
+#define SRAM_INIT_DONE(reg) (reg & BIT(14))
+
+#define RK3568_BIFURCATION_LANE_0_1 BIT(0)
+
+/* Register for RK3588 */
+#define PHP_GRF_PCIESEL_CON 0x100
+#define RK3588_PCIE3PHY_GRF_CMN_CON0 0x0
+#define RK3588_PCIE3PHY_GRF_PHY0_STATUS1 0x904
+#define RK3588_PCIE3PHY_GRF_PHY1_STATUS1 0xa04
+#define RK3588_SRAM_INIT_DONE(reg) (reg & BIT(0))
+
+#define RK3588_BIFURCATION_LANE_0_1 BIT(0)
+#define RK3588_BIFURCATION_LANE_2_3 BIT(1)
+#define RK3588_LANE_AGGREGATION BIT(2)
+
+struct rockchip_p3phy_ops;
+
+struct rockchip_p3phy_priv {
+ const struct rockchip_p3phy_ops *ops;
+ void __iomem *mmio;
+ /* mode: RC, EP */
+ int mode;
+ /* pcie30_phymode: Aggregation, Bifurcation */
+ int pcie30_phymode;
+ struct regmap *phy_grf;
+ struct regmap *pipe_grf;
+ struct reset_control *p30phy;
+ struct phy *phy;
+ struct clk_bulk_data *clks;
+ int num_clks;
+ int num_lanes;
+ u32 lanes[4];
+};
+
+struct rockchip_p3phy_ops {
+ int (*phy_init)(struct rockchip_p3phy_priv *priv);
+};
+
+static int rockchip_p3phy_set_mode(struct phy *phy, enum phy_mode mode, int submode)
+{
+ return 0;
+}
+
+static int rockchip_p3phy_rk3568_init(struct rockchip_p3phy_priv *priv)
+{
+ struct phy *phy = priv->phy;
+ bool bifurcation = false;
+ int ret;
+ u32 reg;
+
+ /* Deassert PCIe PMA output clamp mode */
+ regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON9, GRF_PCIE30PHY_DA_OCM);
+
+ for (int i = 0; i < priv->num_lanes; i++) {
+ dev_info(&phy->dev, "lane number %d, val %d\n", i, priv->lanes[i]);
+ if (priv->lanes[i] > 1)
+ bifurcation = true;
+ }
+
+ /* Set bifurcation if needed, and it doesn't care RC/EP */
+ if (bifurcation) {
+ dev_info(&phy->dev, "bifurcation enabled\n");
+ regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON6,
+ GRF_PCIE30PHY_WR_EN | RK3568_BIFURCATION_LANE_0_1);
+ regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON1,
+ GRF_PCIE30PHY_DA_OCM);
+ } else {
+ dev_dbg(&phy->dev, "bifurcation disabled\n");
+ regmap_write(priv->phy_grf, GRF_PCIE30PHY_CON6,
+ GRF_PCIE30PHY_WR_EN & ~RK3568_BIFURCATION_LANE_0_1);
+ }
+
+ reset_control_deassert(priv->p30phy);
+
+ ret = regmap_read_poll_timeout(priv->phy_grf,
+ GRF_PCIE30PHY_STATUS0,
+ reg, SRAM_INIT_DONE(reg),
+ 500);
+ if (ret)
+ dev_err(&priv->phy->dev, "%s: lock failed 0x%x, check input refclk and power supply\n",
+ __func__, reg);
+ return ret;
+}
+
+static const struct rockchip_p3phy_ops rk3568_ops = {
+ .phy_init = rockchip_p3phy_rk3568_init,
+};
+
+static int rockchip_p3phy_rk3588_init(struct rockchip_p3phy_priv *priv)
+{
+ u32 reg = 0;
+ u8 mode = 0;
+ int ret;
+
+ /* Deassert PCIe PMA output clamp mode */
+ regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_CMN_CON0, BIT(8) | BIT(24));
+
+ /* Set bifurcation if needed */
+ for (int i = 0; i < priv->num_lanes; i++) {
+ if (!priv->lanes[i])
+ mode |= (BIT(i) << 3);
+
+ if (priv->lanes[i] > 1)
+ mode |= (BIT(i) >> 1);
+ }
+
+ if (!mode)
+ reg = RK3588_LANE_AGGREGATION;
+ else {
+ if (mode & (BIT(0) | BIT(1)))
+ reg |= RK3588_BIFURCATION_LANE_0_1;
+
+ if (mode & (BIT(2) | BIT(3)))
+ reg |= RK3588_BIFURCATION_LANE_2_3;
+ }
+
+ regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_CMN_CON0, (0x7<<16) | reg);
+
+ /* Set pcie1ln_sel in PHP_GRF_PCIESEL_CON */
+ if (!IS_ERR(priv->pipe_grf)) {
+ reg = (mode & (BIT(6) | BIT(7))) >> 6;
+ if (reg)
+ regmap_write(priv->pipe_grf, PHP_GRF_PCIESEL_CON,
+ (reg << 16) | reg);
+ }
+
+ reset_control_deassert(priv->p30phy);
+
+ ret = regmap_read_poll_timeout(priv->phy_grf,
+ RK3588_PCIE3PHY_GRF_PHY0_STATUS1,
+ reg, RK3588_SRAM_INIT_DONE(reg),
+ 500);
+ ret |= regmap_read_poll_timeout(priv->phy_grf,
+ RK3588_PCIE3PHY_GRF_PHY1_STATUS1,
+ reg, RK3588_SRAM_INIT_DONE(reg),
+ 500);
+ if (ret)
+ dev_err(&priv->phy->dev, "lock failed 0x%x, check input refclk and power supply\n",
+ reg);
+ return ret;
+}
+
+static const struct rockchip_p3phy_ops rk3588_ops = {
+ .phy_init = rockchip_p3phy_rk3588_init,
+};
+
+static int rochchip_p3phy_init(struct phy *phy)
+{
+ struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy);
+ int ret;
+
+ ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks);
+ if (ret) {
+ dev_err(&priv->phy->dev, "failed to enable PCIe bulk clks %d\n", ret);
+ return ret;
+ }
+
+ reset_control_assert(priv->p30phy);
+ udelay(1);
+
+ if (priv->ops->phy_init) {
+ ret = priv->ops->phy_init(priv);
+ if (ret)
+ clk_bulk_disable_unprepare(priv->num_clks, priv->clks);
+ }
+
+ return ret;
+}
+
+static int rochchip_p3phy_exit(struct phy *phy)
+{
+ struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy);
+
+ clk_bulk_disable_unprepare(priv->num_clks, priv->clks);
+ reset_control_assert(priv->p30phy);
+ return 0;
+}
+
+static const struct phy_ops rochchip_p3phy_ops = {
+ .init = rochchip_p3phy_init,
+ .exit = rochchip_p3phy_exit,
+ .set_mode = rockchip_p3phy_set_mode,
+};
+
+static struct phy *rockchip_p3phy_xlate(struct device *dev,
+ struct of_phandle_args *args)
+{
+ struct rockchip_p3phy_priv *priv = dev->priv;
+
+ return priv->phy;
+}
+
+static int rockchip_p3phy_probe(struct device *dev)
+{
+ struct phy_provider *phy_provider;
+ struct rockchip_p3phy_priv *priv;
+ struct device_node *np = dev->of_node;
+ struct resource *res;
+ int ret;
+
+ priv = xzalloc(sizeof(*priv));
+
+ res = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(res)) {
+ ret = PTR_ERR(res);
+ return ret;
+ }
+
+ priv->mmio = IOMEM(res->start);
+ if (IS_ERR(priv->mmio)) {
+ ret = PTR_ERR(priv->mmio);
+ return ret;
+ }
+
+ priv->ops = device_get_match_data(dev);
+ if (!priv->ops) {
+ dev_err(dev, "no of match data provided\n");
+ return -EINVAL;
+ }
+
+ priv->phy_grf = syscon_regmap_lookup_by_phandle(np, "rockchip,phy-grf");
+ if (IS_ERR(priv->phy_grf)) {
+ dev_err(dev, "failed to find rockchip,phy_grf regmap\n");
+ return PTR_ERR(priv->phy_grf);
+ }
+
+ if (of_device_is_compatible(np, "rockchip,rk3588-pcie3-phy")) {
+ priv->pipe_grf =
+ syscon_regmap_lookup_by_phandle(dev->of_node,
+ "rockchip,pipe-grf");
+ if (IS_ERR(priv->pipe_grf))
+ dev_info(dev, "failed to find rockchip,pipe_grf regmap\n");
+ } else {
+ priv->pipe_grf = NULL;
+ }
+
+ priv->num_lanes = of_property_read_variable_u32_array(dev->of_node, "data-lanes",
+ priv->lanes, 2,
+ ARRAY_SIZE(priv->lanes));
+
+ /* if no data-lanes assume aggregation */
+ if (priv->num_lanes == -EINVAL) {
+ dev_dbg(dev, "no data-lanes property found\n");
+ priv->num_lanes = 1;
+ priv->lanes[0] = 1;
+ } else if (priv->num_lanes < 0) {
+ dev_err(dev, "failed to read data-lanes property %d\n", priv->num_lanes);
+ return priv->num_lanes;
+ }
+
+ priv->phy = phy_create(dev, NULL, &rochchip_p3phy_ops);
+ if (IS_ERR(priv->phy)) {
+ dev_err(dev, "failed to create combphy\n");
+ return PTR_ERR(priv->phy);
+ }
+
+ priv->p30phy = reset_control_get_optional(dev, "phy");
+ if (IS_ERR(priv->p30phy)) {
+ return dev_err_probe(dev, PTR_ERR(priv->p30phy),
+ "failed to get phy reset control\n");
+ }
+ if (!priv->p30phy)
+ dev_info(dev, "no phy reset control specified\n");
+
+ priv->num_clks = clk_bulk_get_all(dev, &priv->clks);
+ if (priv->num_clks < 1)
+ return -ENODEV;
+
+ dev->priv = priv;
+ phy_set_drvdata(priv->phy, priv);
+ phy_provider = of_phy_provider_register(dev, rockchip_p3phy_xlate);
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id rockchip_p3phy_of_match[] = {
+ { .compatible = "rockchip,rk3568-pcie3-phy", .data = &rk3568_ops },
+ { .compatible = "rockchip,rk3588-pcie3-phy", .data = &rk3588_ops },
+ { },
+};
+MODULE_DEVICE_TABLE(of, rockchip_p3phy_of_match);
+
+static struct driver rockchip_p3phy_driver = {
+ .probe = rockchip_p3phy_probe,
+ .name = "rockchip-snps-pcie3-phy",
+ .of_compatible = rockchip_p3phy_of_match,
+};
+coredevice_platform_driver(rockchip_p3phy_driver);
+
+MODULE_DESCRIPTION("Rockchip Synopsys PCIe 3.0 PHY driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/usb-nop-xceiv.c b/drivers/phy/usb-nop-xceiv.c
index a9031fa7f8..9a0acf9e7f 100644
--- a/drivers/phy/usb-nop-xceiv.c
+++ b/drivers/phy/usb-nop-xceiv.c
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2016 Sascha Hauer <s.hauer@pengutronix.de>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
*/
#include <common.h>
@@ -18,7 +9,7 @@
#include <errno.h>
#include <driver.h>
#include <malloc.h>
-#include <usb/phy.h>
+#include <linux/usb/phy.h>
#include <linux/phy/phy.h>
#include <linux/clk.h>
#include <linux/err.h>
@@ -33,7 +24,7 @@ struct nop_usbphy {
int reset;
};
-static struct phy *nop_usbphy_xlate(struct device_d *dev,
+static struct phy *nop_usbphy_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct nop_usbphy *nopphy = dev->priv;
@@ -73,7 +64,7 @@ static const struct phy_ops nop_phy_ops = {
.init = nop_usbphy_init,
};
-static int nop_usbphy_probe(struct device_d *dev)
+static int nop_usbphy_probe(struct device *dev)
{
int ret;
struct nop_usbphy *nopphy;
@@ -89,7 +80,7 @@ static int nop_usbphy_probe(struct device_d *dev)
if (IS_ERR(nopphy->clk))
nopphy->clk = NULL;
- nopphy->reset = of_get_named_gpio_flags(dev->device_node,
+ nopphy->reset = of_get_named_gpio_flags(dev->of_node,
"reset-gpios", 0, &of_flags);
if (gpio_is_valid(nopphy->reset)) {
/* assert reset */
@@ -139,15 +130,12 @@ static __maybe_unused struct of_device_id nop_usbphy_dt_ids[] = {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, nop_usbphy_dt_ids);
-static struct driver_d nop_usbphy_driver = {
+static struct driver nop_usbphy_driver = {
.name = "usb-nop-xceiv",
.probe = nop_usbphy_probe,
.of_compatible = DRV_OF_COMPAT(nop_usbphy_dt_ids),
};
-static int nop_usbphy_driver_init(void)
-{
- return platform_driver_register(&nop_usbphy_driver);
-}
-fs_initcall(nop_usbphy_driver_init);
+fs_platform_driver(nop_usbphy_driver);