mirror of
https://github.com/armbian/build.git
synced 2025-08-10 21:26:59 +02:00
1325 lines
41 KiB
Diff
1325 lines
41 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Jonas Karlman <jonas@kwiboo.se>
|
|
Date: Sat, 10 Oct 2020 15:32:18 +0000
|
|
Subject: [PATCH] phy/rockchip: inno-hdmi: use correct vco_div_5 macro on
|
|
rk3328
|
|
|
|
inno_hdmi_phy_rk3328_clk_set_rate() is using the RK3228 macro
|
|
when configuring vco_div_5 on RK3328.
|
|
|
|
Fix this by using correct vco_div_5 macro for RK3328.
|
|
|
|
Fixes: 53706a116863 ("phy: add Rockchip Innosilicon hdmi phy")
|
|
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
|
|
---
|
|
drivers/phy/rockchip/phy-rockchip-inno-hdmi.c | 4 ++--
|
|
1 file changed, 2 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
|
|
index 9ca20c947283..b0ac1d3ee390 100644
|
|
--- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
|
|
+++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
|
|
@@ -790,8 +790,8 @@ static int inno_hdmi_phy_rk3328_clk_set_rate(struct clk_hw *hw,
|
|
RK3328_PRE_PLL_POWER_DOWN);
|
|
|
|
/* Configure pre-pll */
|
|
- inno_update_bits(inno, 0xa0, RK3228_PCLK_VCO_DIV_5_MASK,
|
|
- RK3228_PCLK_VCO_DIV_5(cfg->vco_div_5_en));
|
|
+ inno_update_bits(inno, 0xa0, RK3328_PCLK_VCO_DIV_5_MASK,
|
|
+ RK3328_PCLK_VCO_DIV_5(cfg->vco_div_5_en));
|
|
inno_write(inno, 0xa1, RK3328_PRE_PLL_PRE_DIV(cfg->prediv));
|
|
|
|
val = RK3328_SPREAD_SPECTRUM_MOD_DISABLE;
|
|
|
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Zheng Yang <zhengyang@rock-chips.com>
|
|
Date: Sat, 10 Oct 2020 15:32:18 +0000
|
|
Subject: [PATCH] phy/rockchip: inno-hdmi: round fractal pixclock in rk3328
|
|
recalc_rate
|
|
|
|
inno_hdmi_phy_rk3328_clk_recalc_rate() is returning a rate not found
|
|
in the pre pll config table when the fractal divider is used.
|
|
This can prevent proper power_on because a tmdsclock for the new rate
|
|
is not found in the pre pll config table.
|
|
|
|
Fix this by saving and returning a rounded pixel rate that exist
|
|
in the pre pll config table.
|
|
|
|
Fixes: 53706a116863 ("phy: add Rockchip Innosilicon hdmi phy")
|
|
Signed-off-by: Zheng Yang <zhengyang@rock-chips.com>
|
|
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
|
|
---
|
|
drivers/phy/rockchip/phy-rockchip-inno-hdmi.c | 8 +++++---
|
|
1 file changed, 5 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
|
|
index b0ac1d3ee390..093d2334e8cd 100644
|
|
--- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
|
|
+++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
|
|
@@ -745,10 +745,12 @@ unsigned long inno_hdmi_phy_rk3328_clk_recalc_rate(struct clk_hw *hw,
|
|
do_div(vco, (nd * (no_a == 1 ? no_b : no_a) * no_d * 2));
|
|
}
|
|
|
|
- inno->pixclock = vco;
|
|
- dev_dbg(inno->dev, "%s rate %lu\n", __func__, inno->pixclock);
|
|
+ inno->pixclock = DIV_ROUND_CLOSEST((unsigned long)vco, 1000) * 1000;
|
|
|
|
- return vco;
|
|
+ dev_dbg(inno->dev, "%s rate %lu vco %llu\n",
|
|
+ __func__, inno->pixclock, vco);
|
|
+
|
|
+ return inno->pixclock;
|
|
}
|
|
|
|
static long inno_hdmi_phy_rk3328_clk_round_rate(struct clk_hw *hw,
|
|
|
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Jonas Karlman <jonas@kwiboo.se>
|
|
Date: Sat, 10 Oct 2020 15:32:19 +0000
|
|
Subject: [PATCH] phy/rockchip: inno-hdmi: remove unused no_c from rk3328
|
|
recalc_rate
|
|
|
|
no_c is not used in any calculation, lets remove it.
|
|
|
|
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
|
|
---
|
|
drivers/phy/rockchip/phy-rockchip-inno-hdmi.c | 5 +----
|
|
1 file changed, 1 insertion(+), 4 deletions(-)
|
|
|
|
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
|
|
index 093d2334e8cd..06db69c8373e 100644
|
|
--- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
|
|
+++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
|
|
@@ -714,7 +714,7 @@ unsigned long inno_hdmi_phy_rk3328_clk_recalc_rate(struct clk_hw *hw,
|
|
{
|
|
struct inno_hdmi_phy *inno = to_inno_hdmi_phy(hw);
|
|
unsigned long frac;
|
|
- u8 nd, no_a, no_b, no_c, no_d;
|
|
+ u8 nd, no_a, no_b, no_d;
|
|
u64 vco;
|
|
u16 nf;
|
|
|
|
@@ -737,9 +737,6 @@ unsigned long inno_hdmi_phy_rk3328_clk_recalc_rate(struct clk_hw *hw,
|
|
no_b = inno_read(inno, 0xa5) & RK3328_PRE_PLL_PCLK_DIV_B_MASK;
|
|
no_b >>= RK3328_PRE_PLL_PCLK_DIV_B_SHIFT;
|
|
no_b += 2;
|
|
- no_c = inno_read(inno, 0xa6) & RK3328_PRE_PLL_PCLK_DIV_C_MASK;
|
|
- no_c >>= RK3328_PRE_PLL_PCLK_DIV_C_SHIFT;
|
|
- no_c = 1 << no_c;
|
|
no_d = inno_read(inno, 0xa6) & RK3328_PRE_PLL_PCLK_DIV_D_MASK;
|
|
|
|
do_div(vco, (nd * (no_a == 1 ? no_b : no_a) * no_d * 2));
|
|
|
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Jonas Karlman <jonas@kwiboo.se>
|
|
Date: Sat, 10 Oct 2020 15:32:19 +0000
|
|
Subject: [PATCH] phy/rockchip: inno-hdmi: do not power on rk3328 post pll on
|
|
reg write
|
|
|
|
inno_write is used to configure 0xaa reg, that also hold the
|
|
POST_PLL_POWER_DOWN bit.
|
|
When POST_PLL_REFCLK_SEL_TMDS is configured the power down bit is not
|
|
taken into consideration.
|
|
|
|
Fix this by keeping the power down bit until configuration is complete.
|
|
Also reorder the reg write order for consistency.
|
|
|
|
Fixes: 53706a116863 ("phy: add Rockchip Innosilicon hdmi phy")
|
|
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
|
|
---
|
|
drivers/phy/rockchip/phy-rockchip-inno-hdmi.c | 6 ++++--
|
|
1 file changed, 4 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
|
|
index 06db69c8373e..3a59a6da0440 100644
|
|
--- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
|
|
+++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
|
|
@@ -1020,9 +1020,10 @@ inno_hdmi_phy_rk3328_power_on(struct inno_hdmi_phy *inno,
|
|
|
|
inno_write(inno, 0xac, RK3328_POST_PLL_FB_DIV_7_0(cfg->fbdiv));
|
|
if (cfg->postdiv == 1) {
|
|
- inno_write(inno, 0xaa, RK3328_POST_PLL_REFCLK_SEL_TMDS);
|
|
inno_write(inno, 0xab, RK3328_POST_PLL_FB_DIV_8(cfg->fbdiv) |
|
|
RK3328_POST_PLL_PRE_DIV(cfg->prediv));
|
|
+ inno_write(inno, 0xaa, RK3328_POST_PLL_REFCLK_SEL_TMDS |
|
|
+ RK3328_POST_PLL_POWER_DOWN);
|
|
} else {
|
|
v = (cfg->postdiv / 2) - 1;
|
|
v &= RK3328_POST_PLL_POST_DIV_MASK;
|
|
@@ -1030,7 +1031,8 @@ inno_hdmi_phy_rk3328_power_on(struct inno_hdmi_phy *inno,
|
|
inno_write(inno, 0xab, RK3328_POST_PLL_FB_DIV_8(cfg->fbdiv) |
|
|
RK3328_POST_PLL_PRE_DIV(cfg->prediv));
|
|
inno_write(inno, 0xaa, RK3328_POST_PLL_POST_DIV_ENABLE |
|
|
- RK3328_POST_PLL_REFCLK_SEL_TMDS);
|
|
+ RK3328_POST_PLL_REFCLK_SEL_TMDS |
|
|
+ RK3328_POST_PLL_POWER_DOWN);
|
|
}
|
|
|
|
for (v = 0; v < 14; v++)
|
|
|
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Huicong Xu <xhc@rock-chips.com>
|
|
Date: Sat, 10 Oct 2020 15:32:20 +0000
|
|
Subject: [PATCH] phy/rockchip: inno-hdmi: force set_rate on power_on
|
|
|
|
Regular 8-bit and Deep Color video formats mainly differ in TMDS rate and
|
|
not in pixel clock rate.
|
|
When the hdmiphy clock is configured with the same pixel clock rate using
|
|
clk_set_rate() the clock framework do not signal the hdmi phy driver
|
|
to set_rate when switching between 8-bit and Deep Color.
|
|
This result in pre/post pll not being re-configured when switching between
|
|
regular 8-bit and Deep Color video formats.
|
|
|
|
Fix this by calling set_rate in power_on to force pre pll re-configuration.
|
|
|
|
Signed-off-by: Huicong Xu <xhc@rock-chips.com>
|
|
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
|
|
---
|
|
drivers/phy/rockchip/phy-rockchip-inno-hdmi.c | 13 +++++++++++++
|
|
1 file changed, 13 insertions(+)
|
|
|
|
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
|
|
index 3a59a6da0440..3719309ad0d0 100644
|
|
--- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
|
|
+++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
|
|
@@ -245,6 +245,7 @@ struct inno_hdmi_phy {
|
|
struct clk_hw hw;
|
|
struct clk *phyclk;
|
|
unsigned long pixclock;
|
|
+ unsigned long tmdsclock;
|
|
};
|
|
|
|
struct pre_pll_config {
|
|
@@ -485,6 +486,8 @@ static int inno_hdmi_phy_power_on(struct phy *phy)
|
|
|
|
dev_dbg(inno->dev, "Inno HDMI PHY Power On\n");
|
|
|
|
+ inno->plat_data->clk_ops->set_rate(&inno->hw, inno->pixclock, 24000000);
|
|
+
|
|
ret = clk_prepare_enable(inno->phyclk);
|
|
if (ret)
|
|
return ret;
|
|
@@ -509,6 +512,8 @@ static int inno_hdmi_phy_power_off(struct phy *phy)
|
|
|
|
clk_disable_unprepare(inno->phyclk);
|
|
|
|
+ inno->tmdsclock = 0;
|
|
+
|
|
dev_dbg(inno->dev, "Inno HDMI PHY Power Off\n");
|
|
|
|
return 0;
|
|
@@ -628,6 +633,9 @@ static int inno_hdmi_phy_rk3228_clk_set_rate(struct clk_hw *hw,
|
|
dev_dbg(inno->dev, "%s rate %lu tmdsclk %lu\n",
|
|
__func__, rate, tmdsclock);
|
|
|
|
+ if (inno->pixclock == rate && inno->tmdsclock == tmdsclock)
|
|
+ return 0;
|
|
+
|
|
cfg = inno_hdmi_phy_get_pre_pll_cfg(inno, rate);
|
|
if (IS_ERR(cfg))
|
|
return PTR_ERR(cfg);
|
|
@@ -670,6 +678,7 @@ static int inno_hdmi_phy_rk3228_clk_set_rate(struct clk_hw *hw,
|
|
}
|
|
|
|
inno->pixclock = rate;
|
|
+ inno->tmdsclock = tmdsclock;
|
|
|
|
return 0;
|
|
}
|
|
@@ -781,6 +790,9 @@ static int inno_hdmi_phy_rk3328_clk_set_rate(struct clk_hw *hw,
|
|
dev_dbg(inno->dev, "%s rate %lu tmdsclk %lu\n",
|
|
__func__, rate, tmdsclock);
|
|
|
|
+ if (inno->pixclock == rate && inno->tmdsclock == tmdsclock)
|
|
+ return 0;
|
|
+
|
|
cfg = inno_hdmi_phy_get_pre_pll_cfg(inno, rate);
|
|
if (IS_ERR(cfg))
|
|
return PTR_ERR(cfg);
|
|
@@ -820,6 +832,7 @@ static int inno_hdmi_phy_rk3328_clk_set_rate(struct clk_hw *hw,
|
|
}
|
|
|
|
inno->pixclock = rate;
|
|
+ inno->tmdsclock = tmdsclock;
|
|
|
|
return 0;
|
|
}
|
|
|
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Peter Geis <pgwipeout@gmail.com>
|
|
Date: Mon, 16 Nov 2020 15:17:33 +0000
|
|
Subject: [PATCH] phy: rockchip: add rockchip usb3 innosilicon phy driver
|
|
|
|
The innosilicon based usb3 phy used in rockchip devices such as the rk3328 is bugged, requiring special handling.
|
|
The following erata have been observed:
|
|
- usb3 device disconnect events are not detected by the controller
|
|
- usb2 hubs with no devices attached do not trigger disconnect events when removed
|
|
- interrupts are not reliable
|
|
|
|
To work around these issue we implement polling of the usb2 and usb3 status.
|
|
On usb3 disconnection we reset the usb3 phy which triggers the disconnect event.
|
|
On usb2 disconnection we have to force reset the whole controller.
|
|
This requires a handoff to a special dwc3 device driver.
|
|
|
|
This has been tested on the rk3328-roc-cc board with the following devices:
|
|
- usb2 only device
|
|
- usb3 only device
|
|
- usb2 only hub without devices
|
|
- usb3 hub without devices
|
|
- usb2 hub with devices
|
|
- usb3 hub with devices
|
|
|
|
Signed-off-by: Peter Geis <pgwipeout@gmail.com>
|
|
---
|
|
drivers/phy/rockchip/Kconfig | 9 +
|
|
drivers/phy/rockchip/Makefile | 1 +
|
|
drivers/phy/rockchip/phy-rockchip-inno-usb3.c | 425 ++++++++++++++++++
|
|
3 files changed, 435 insertions(+)
|
|
create mode 100644 drivers/phy/rockchip/phy-rockchip-inno-usb3.c
|
|
|
|
diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig
|
|
index c2f22f90736c..ce16e0877354 100644
|
|
--- a/drivers/phy/rockchip/Kconfig
|
|
+++ b/drivers/phy/rockchip/Kconfig
|
|
@@ -47,6 +47,15 @@ config PHY_ROCKCHIP_INNO_USB2
|
|
help
|
|
Support for Rockchip USB2.0 PHY with Innosilicon IP block.
|
|
|
|
+config PHY_ROCKCHIP_INNO_USB3
|
|
+ tristate "Rockchip INNO USB3PHY Driver"
|
|
+ depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF
|
|
+ depends on COMMON_CLK
|
|
+ depends on USB_SUPPORT
|
|
+ select USB_COMMON
|
|
+ help
|
|
+ Support for Rockchip USB3.0 PHY with Innosilicon IP block.
|
|
+
|
|
config PHY_ROCKCHIP_INNO_DSIDPHY
|
|
tristate "Rockchip Innosilicon MIPI/LVDS/TTL PHY driver"
|
|
depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF
|
|
diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile
|
|
index c3cfc7f0af5c..738e3574a722 100644
|
|
--- a/drivers/phy/rockchip/Makefile
|
|
+++ b/drivers/phy/rockchip/Makefile
|
|
@@ -5,6 +5,7 @@ obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
|
|
obj-$(CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY) += phy-rockchip-inno-dsidphy.o
|
|
obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o
|
|
obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o
|
|
+obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB3) += phy-rockchip-inno-usb3.o
|
|
obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o
|
|
obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o
|
|
obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
|
|
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb3.c b/drivers/phy/rockchip/phy-rockchip-inno-usb3.c
|
|
new file mode 100644
|
|
index 000000000000..6e4aa2f0ba46
|
|
--- /dev/null
|
|
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb3.c
|
|
@@ -0,0 +1,425 @@
|
|
+// SPDX-License-Identifier: GPL-2.0+
|
|
+
|
|
+#include <linux/clk.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/err.h>
|
|
+#include <linux/extcon-provider.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/mutex.h>
|
|
+#include <linux/notifier.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/of_address.h>
|
|
+#include <linux/of_device.h>
|
|
+#include <linux/of_platform.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/pm_runtime.h>
|
|
+#include <linux/regmap.h>
|
|
+#include <linux/reset.h>
|
|
+#include <linux/workqueue.h>
|
|
+#include <linux/mfd/syscon.h>
|
|
+#include <linux/usb/phy.h>
|
|
+
|
|
+#define USB3_STATUS_REG 0x284
|
|
+#define USB2_STATUS_REG 0x30
|
|
+#define USB3_CONN_BIT BIT(0)
|
|
+#define USB2_CONN_BIT BIT(7)
|
|
+#define USB2_STATE_SHIFT 6
|
|
+#define REG_WRITE_MASK GENMASK(31, 16)
|
|
+
|
|
+struct rockchip_usb3phy_port{
|
|
+ struct device *dev;
|
|
+ struct regmap *regmap;
|
|
+ struct usb_phy phy;
|
|
+ struct rockchip_usb3phy *parent;
|
|
+ unsigned char type;
|
|
+};
|
|
+
|
|
+enum usb3phy_mode {
|
|
+ PHY_IDLE = 0,
|
|
+ PHY_USB3,
|
|
+ PHY_USB2,
|
|
+ PHY_COMBO
|
|
+};
|
|
+
|
|
+struct rockchip_usb3phy {
|
|
+ struct device *dev;
|
|
+ struct regmap *regmap;
|
|
+ struct clk *clk_pipe;
|
|
+ struct clk *clk_otg;
|
|
+ struct reset_control *u3por_rst;
|
|
+ struct reset_control *u2por_rst;
|
|
+ struct reset_control *pipe_rst;
|
|
+ struct reset_control *utmi_rst;
|
|
+ struct reset_control *pipe_apb_rst;
|
|
+ struct reset_control *utmi_apb_rst;
|
|
+ struct rockchip_usb3phy_port port_pipe;
|
|
+ struct rockchip_usb3phy_port port_utmi;
|
|
+ struct work_struct usb_phy_work;
|
|
+ struct notifier_block nb;
|
|
+ enum usb3phy_mode mode;
|
|
+ struct mutex lock;
|
|
+};
|
|
+
|
|
+static int rockchip_usb3phy_reset(struct rockchip_usb3phy *usb3phy, bool reset, enum usb3phy_mode mode)
|
|
+{
|
|
+ if (reset == true) {
|
|
+ if ((mode == PHY_USB2) | (mode == PHY_COMBO)){
|
|
+ clk_disable_unprepare(usb3phy->clk_otg);
|
|
+ reset_control_assert(usb3phy->utmi_rst);
|
|
+ reset_control_assert(usb3phy->u2por_rst);
|
|
+ }
|
|
+ if ((mode == PHY_USB3) | (mode == PHY_COMBO)){
|
|
+ clk_disable_unprepare(usb3phy->clk_pipe);
|
|
+ reset_control_assert(usb3phy->pipe_rst);
|
|
+ reset_control_assert(usb3phy->u3por_rst);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (reset == false) {
|
|
+ if ((mode == PHY_USB2) | (mode == PHY_COMBO)){
|
|
+ reset_control_deassert(usb3phy->u2por_rst);
|
|
+ udelay(1000);
|
|
+ clk_prepare_enable(usb3phy->clk_otg);
|
|
+ udelay(500);
|
|
+ reset_control_deassert(usb3phy->utmi_rst);
|
|
+ }
|
|
+ if ((mode == PHY_USB3) | (mode == PHY_COMBO)){
|
|
+ reset_control_deassert(usb3phy->u3por_rst);
|
|
+ udelay(500);
|
|
+ clk_prepare_enable(usb3phy->clk_pipe);
|
|
+ udelay(1000);
|
|
+ reset_control_deassert(usb3phy->pipe_rst);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void rockchip_usb3phy_work(struct work_struct *work)
|
|
+{
|
|
+ struct rockchip_usb3phy *usb3phy = container_of(work, struct rockchip_usb3phy, usb_phy_work);
|
|
+ struct rockchip_usb3phy_port *port_pipe = &usb3phy->port_pipe;
|
|
+ struct rockchip_usb3phy_port *port_utmi = &usb3phy->port_utmi;
|
|
+ int usb2, usb3, tmp, state;
|
|
+
|
|
+ mutex_lock(&usb3phy->lock);
|
|
+
|
|
+ regmap_read(port_pipe->regmap, USB3_STATUS_REG, &tmp);
|
|
+ usb3 = tmp & USB3_CONN_BIT;
|
|
+ regmap_read(usb3phy->regmap, USB2_STATUS_REG, &tmp);
|
|
+ usb2 = ((tmp & USB2_CONN_BIT) ^ USB2_CONN_BIT ) >> USB2_STATE_SHIFT;
|
|
+ state = (usb3 | usb2);
|
|
+ dev_dbg(usb3phy->dev, "mode %i, state %i\n", usb3phy->mode, state);
|
|
+
|
|
+ if (usb3phy->mode == state)
|
|
+ /* not our device */
|
|
+ goto out;
|
|
+
|
|
+ if (usb2) {
|
|
+ usb3phy->mode = PHY_USB2;
|
|
+ dev_dbg(usb3phy->dev, "usb3phy utmi polling started\n");
|
|
+ regmap_read_poll_timeout(usb3phy->regmap, USB2_STATUS_REG, tmp, (tmp & USB2_CONN_BIT), 2000, 0);
|
|
+ state = ((tmp & USB2_CONN_BIT) ^ USB2_CONN_BIT ) >> USB2_STATE_SHIFT;
|
|
+ dev_dbg(usb3phy->dev, "usb3phy utmi polling completed\n");
|
|
+
|
|
+ atomic_notifier_call_chain(&port_utmi->phy.notifier, 0, NULL);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (usb3) {
|
|
+ dev_dbg(usb3phy->dev, "usb3phy pipe polling started\n");
|
|
+ regmap_read_poll_timeout(port_pipe->regmap, USB3_STATUS_REG, tmp, !(tmp & USB3_CONN_BIT), 2000, 0);
|
|
+ dev_dbg(usb3phy->dev, "usb3phy pipe polling completed\n");
|
|
+
|
|
+ rockchip_usb3phy_reset(usb3phy, true, PHY_USB3);
|
|
+ udelay(500);
|
|
+ rockchip_usb3phy_reset(usb3phy, false, PHY_USB3);
|
|
+ udelay(500);
|
|
+
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+out:
|
|
+ usb3phy->mode = PHY_IDLE;
|
|
+ mutex_unlock(&usb3phy->lock);
|
|
+ return;
|
|
+}
|
|
+
|
|
+static int rockchip_usb3phy_parse_dt(struct rockchip_usb3phy *usb3phy, struct device *dev)
|
|
+{
|
|
+ usb3phy->clk_pipe = devm_clk_get(dev, "usb3phy-pipe");
|
|
+ if (IS_ERR(usb3phy->clk_pipe)) {
|
|
+ dev_err(dev, "could not get usb3phy pipe clock\n");
|
|
+ return PTR_ERR(usb3phy->clk_pipe);
|
|
+ }
|
|
+
|
|
+ usb3phy->clk_otg = devm_clk_get(dev, "usb3phy-otg");
|
|
+ if (IS_ERR(usb3phy->clk_otg)) {
|
|
+ dev_err(dev, "could not get usb3phy otg clock\n");
|
|
+ return PTR_ERR(usb3phy->clk_otg);
|
|
+ }
|
|
+
|
|
+ usb3phy->u2por_rst = devm_reset_control_get(dev, "usb3phy-u2-por");
|
|
+ if (IS_ERR(usb3phy->u2por_rst)) {
|
|
+ dev_err(dev, "no usb3phy-u2-por reset control found\n");
|
|
+ return PTR_ERR(usb3phy->u2por_rst);
|
|
+ }
|
|
+
|
|
+ usb3phy->u3por_rst = devm_reset_control_get(dev, "usb3phy-u3-por");
|
|
+ if (IS_ERR(usb3phy->u3por_rst)) {
|
|
+ dev_err(dev, "no usb3phy-u3-por reset control found\n");
|
|
+ return PTR_ERR(usb3phy->u3por_rst);
|
|
+ }
|
|
+
|
|
+ usb3phy->pipe_rst = devm_reset_control_get(dev, "usb3phy-pipe-mac");
|
|
+ if (IS_ERR(usb3phy->pipe_rst)) {
|
|
+ dev_err(dev, "no usb3phy_pipe_mac reset control found\n");
|
|
+ return PTR_ERR(usb3phy->pipe_rst);
|
|
+ }
|
|
+
|
|
+ usb3phy->utmi_rst = devm_reset_control_get(dev, "usb3phy-utmi-mac");
|
|
+ if (IS_ERR(usb3phy->utmi_rst)) {
|
|
+ dev_err(dev, "no usb3phy-utmi-mac reset control found\n");
|
|
+ return PTR_ERR(usb3phy->utmi_rst);
|
|
+ }
|
|
+
|
|
+ usb3phy->pipe_apb_rst = devm_reset_control_get(dev, "usb3phy-pipe-apb");
|
|
+ if (IS_ERR(usb3phy->pipe_apb_rst)) {
|
|
+ dev_err(dev, "no usb3phy-pipe-apb reset control found\n");
|
|
+ return PTR_ERR(usb3phy->pipe_apb_rst);
|
|
+ }
|
|
+
|
|
+ usb3phy->utmi_apb_rst = devm_reset_control_get(dev, "usb3phy-utmi-apb");
|
|
+ if (IS_ERR(usb3phy->utmi_apb_rst)) {
|
|
+ dev_err(dev, "no usb3phy-utmi-apb reset control found\n");
|
|
+ return PTR_ERR(usb3phy->utmi_apb_rst);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int rockchip_usb3phy_notify(struct notifier_block *nb, unsigned long action, void *data)
|
|
+{
|
|
+ struct rockchip_usb3phy *usb3phy = container_of(nb, struct rockchip_usb3phy, nb);
|
|
+ switch (action) {
|
|
+ case USB_DEVICE_ADD:
|
|
+ dev_dbg(usb3phy->dev, "notified of device add\n");
|
|
+ if (!(mutex_is_locked(&usb3phy->lock)))
|
|
+ schedule_work(&usb3phy->usb_phy_work);
|
|
+ return NOTIFY_OK;
|
|
+ }
|
|
+ return NOTIFY_DONE;
|
|
+}
|
|
+
|
|
+static int rockchip_usb3phy_init(struct usb_phy *phy)
|
|
+{
|
|
+ struct rockchip_usb3phy_port *usb3phy_port = container_of(phy, struct rockchip_usb3phy_port, phy);
|
|
+ struct rockchip_usb3phy *usb3phy = usb3phy_port->parent;
|
|
+
|
|
+ dev_warn(usb3phy->dev, "usb3phy_init %s\n", phy->label);
|
|
+ if (phy->type == USB_PHY_TYPE_USB3){
|
|
+ rockchip_usb3phy_reset(usb3phy, false, PHY_USB3);
|
|
+ udelay(100); /* let it stabilize */
|
|
+ usb3phy->nb.notifier_call = rockchip_usb3phy_notify;
|
|
+ usb_register_notify(&usb3phy->nb);
|
|
+ }
|
|
+ if (phy->type == USB_PHY_TYPE_USB2){
|
|
+ rockchip_usb3phy_reset(usb3phy, false, PHY_USB2);
|
|
+ udelay(100); /* let it stabilize */
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void rockchip_usb3phy_shutdown(struct usb_phy *phy)
|
|
+{
|
|
+ struct rockchip_usb3phy_port *usb3phy_port = container_of(phy, struct rockchip_usb3phy_port, phy);
|
|
+ struct rockchip_usb3phy *usb3phy = usb3phy_port->parent;
|
|
+
|
|
+ dev_dbg(usb3phy->dev, "usb3phy_shutdown\n");
|
|
+ if (phy->type == USB_PHY_TYPE_USB3){
|
|
+ rockchip_usb3phy_reset(usb3phy, false, PHY_USB3);
|
|
+ usb_unregister_notify(&usb3phy->nb);
|
|
+ }
|
|
+ if (phy->type == USB_PHY_TYPE_USB2){
|
|
+ rockchip_usb3phy_reset(usb3phy, false, PHY_USB2);
|
|
+ }
|
|
+}
|
|
+
|
|
+static const struct regmap_config rockchip_usb3phy_port_regmap_config = {
|
|
+ .reg_bits = 32,
|
|
+ .val_bits = 32,
|
|
+ .reg_stride = 4,
|
|
+ .max_register = 0x1000,
|
|
+};
|
|
+
|
|
+static const struct regmap_config rockchip_usb3phy_regmap_config = {
|
|
+ .reg_bits = 32,
|
|
+ .val_bits = 32,
|
|
+ .reg_stride = 4,
|
|
+ .max_register = 0x1000,
|
|
+ .write_flag_mask = REG_WRITE_MASK,
|
|
+};
|
|
+
|
|
+static int rockchip_usb3phy_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct device *dev = &pdev->dev;
|
|
+ struct device_node *np = dev->of_node;
|
|
+ struct rockchip_usb3phy *usb3phy;
|
|
+ struct rockchip_usb3phy_port *usb3phy_port;
|
|
+ struct regmap_config regmap_config = rockchip_usb3phy_regmap_config;
|
|
+ struct regmap_config regmap_port_config = rockchip_usb3phy_port_regmap_config;
|
|
+ const struct of_device_id *match;
|
|
+ void __iomem *base;
|
|
+ int ret;
|
|
+
|
|
+ match = of_match_device(dev->driver->of_match_table, dev);
|
|
+ if (!match) {
|
|
+ dev_err(dev, "phy node not assigned\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (of_node_name_eq(np, "usb3-phy")) {
|
|
+ dev_dbg(dev, "Probe usb3phy main block\n");
|
|
+
|
|
+ usb3phy = devm_kzalloc(dev, sizeof(*usb3phy), GFP_KERNEL);
|
|
+ if (!usb3phy)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ ret = rockchip_usb3phy_parse_dt(usb3phy, dev);
|
|
+ if (ret) {
|
|
+ dev_err(dev, "parse dt failed %i\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ base = devm_of_iomap(dev, np, 0, NULL);
|
|
+ if (IS_ERR(base)) {
|
|
+ dev_err(dev, "failed port ioremap\n");
|
|
+ return PTR_ERR(base);
|
|
+ }
|
|
+
|
|
+ regmap_config.name = np->name;
|
|
+
|
|
+ usb3phy->regmap = devm_regmap_init_mmio(dev, base, ®map_config);
|
|
+ if (IS_ERR(usb3phy->regmap)) {
|
|
+ dev_err(dev, "regmap init failed\n");
|
|
+ return PTR_ERR(usb3phy->regmap);
|
|
+ }
|
|
+
|
|
+ usb3phy->dev = dev;
|
|
+ platform_set_drvdata(pdev, usb3phy);
|
|
+
|
|
+ /* place block in reset */
|
|
+ reset_control_assert(usb3phy->pipe_rst);
|
|
+ reset_control_assert(usb3phy->utmi_rst);
|
|
+ reset_control_assert(usb3phy->u3por_rst);
|
|
+ reset_control_assert(usb3phy->u2por_rst);
|
|
+ reset_control_assert(usb3phy->pipe_apb_rst);
|
|
+ reset_control_assert(usb3phy->utmi_apb_rst);
|
|
+
|
|
+ udelay(20);
|
|
+
|
|
+ /* take apb interface out of reset */
|
|
+ reset_control_deassert(usb3phy->utmi_apb_rst);
|
|
+ reset_control_deassert(usb3phy->pipe_apb_rst);
|
|
+
|
|
+ usb3phy->mode = PHY_IDLE;
|
|
+ INIT_WORK(&usb3phy->usb_phy_work, rockchip_usb3phy_work);
|
|
+ dev_dbg(dev, "Completed usb3phy core probe \n");
|
|
+
|
|
+ return devm_of_platform_populate(&pdev->dev);
|
|
+ }
|
|
+
|
|
+ /* probe the actual ports */
|
|
+ usb3phy = platform_get_drvdata(of_find_device_by_node(np->parent));
|
|
+
|
|
+ if (of_node_name_eq(np, "utmi")) {
|
|
+ usb3phy_port = &usb3phy->port_utmi;
|
|
+ usb3phy_port->phy.label = "usb2-phy";
|
|
+ usb3phy_port->phy.type = USB_PHY_TYPE_USB2;
|
|
+ }
|
|
+ else if (of_node_name_eq(np, "pipe")) {
|
|
+ usb3phy_port = &usb3phy->port_pipe;
|
|
+ usb3phy_port->phy.label = "usb3-phy";
|
|
+ usb3phy_port->phy.type = USB_PHY_TYPE_USB3;
|
|
+ }
|
|
+ else {
|
|
+ dev_err(dev, "unknown child node port type %s\n", np->name);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ usb3phy_port->dev = dev;
|
|
+
|
|
+ base = devm_of_iomap(dev, np, 0, NULL);
|
|
+ if (IS_ERR(base)) {
|
|
+ dev_err(dev, "failed port ioremap\n");
|
|
+ return PTR_ERR(base);
|
|
+ }
|
|
+
|
|
+ regmap_port_config.name = np->name;
|
|
+
|
|
+ usb3phy_port->regmap = devm_regmap_init_mmio(dev, base, ®map_port_config);
|
|
+ if (IS_ERR(usb3phy_port->regmap)) {
|
|
+ dev_err(dev, "regmap init failed\n");
|
|
+ return PTR_ERR(usb3phy_port->regmap);
|
|
+ }
|
|
+
|
|
+ usb3phy_port->phy.dev = dev;
|
|
+ usb3phy_port->phy.init = rockchip_usb3phy_init;
|
|
+ usb3phy_port->phy.shutdown = rockchip_usb3phy_shutdown;
|
|
+ usb3phy_port->parent = usb3phy;
|
|
+
|
|
+ ret = usb_add_phy_dev(&usb3phy_port->phy);
|
|
+ if (ret) {
|
|
+ dev_err(dev, "add usb phy failed %i\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ mutex_init(&usb3phy->lock);
|
|
+
|
|
+ dev_info(dev, "Completed usb3phy %s port init\n", usb3phy_port->phy.label);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static int rockchip_usb3phy_remove(struct platform_device *pdev)
|
|
+{
|
|
+ struct rockchip_usb3phy *usb3phy = platform_get_drvdata(pdev);
|
|
+ struct rockchip_usb3phy_port *port_pipe = &usb3phy->port_pipe;
|
|
+ struct rockchip_usb3phy_port *port_utmi = &usb3phy->port_utmi;
|
|
+
|
|
+ if (&port_pipe->phy.head)
|
|
+ usb_remove_phy(&port_pipe->phy);
|
|
+ if (&port_utmi->phy.head)
|
|
+ usb_remove_phy(&port_utmi->phy);
|
|
+
|
|
+ reset_control_assert(usb3phy->pipe_apb_rst);
|
|
+ reset_control_assert(usb3phy->utmi_apb_rst);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct of_device_id rockchip_usb3phy_dt_ids[] = {
|
|
+ { .compatible = "rockchip,rk3328-usb3phy", },
|
|
+ { .compatible = "rockchip,rk3328-usb3phy-utmi", },
|
|
+ { .compatible = "rockchip,rk3328-usb3phy-pipe", },
|
|
+ { /* sentinel */ }
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(of, rockchip_usb3phy_dt_ids);
|
|
+
|
|
+static struct platform_driver rockchip_usb3phy_driver = {
|
|
+ .probe = rockchip_usb3phy_probe,
|
|
+ .remove = rockchip_usb3phy_remove,
|
|
+ .driver = {
|
|
+ .name = "rockchip-usb3-phy",
|
|
+ .of_match_table = rockchip_usb3phy_dt_ids,
|
|
+ },
|
|
+};
|
|
+
|
|
+module_platform_driver(rockchip_usb3phy_driver);
|
|
+
|
|
+MODULE_AUTHOR("Peter Geis <pgwipeout@gmail.com>");
|
|
+MODULE_DESCRIPTION("Rockchip USB 3 PHY driver");
|
|
+MODULE_LICENSE("GPL v2");
|
|
|
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Peter Geis <pgwipeout@gmail.com>
|
|
Date: Mon, 16 Nov 2020 15:17:34 +0000
|
|
Subject: [PATCH] usb: dwc3: add rockchip innosilicon usb3 glue layer
|
|
|
|
This adds the handler glue for the rockchip usb3 innosilicon phy driver.
|
|
This driver attaches to the phy driver through the notification system.
|
|
When a usb2 disconnect event occurs this driver tears down the hcd and rebuilds it manually.
|
|
This is to work around the usb2 controller becoming wedged and not detecting any usb2 devices after a usb2 hub is removed.
|
|
|
|
It is based off work originally done by rockchip.
|
|
|
|
Signed-off-by: Peter Geis <pgwipeout@gmail.com>
|
|
---
|
|
drivers/usb/dwc3/Kconfig | 10 +
|
|
drivers/usb/dwc3/Makefile | 1 +
|
|
drivers/usb/dwc3/dwc3-rockchip-inno.c | 271 ++++++++++++++++++++++++++
|
|
3 files changed, 282 insertions(+)
|
|
create mode 100644 drivers/usb/dwc3/dwc3-rockchip-inno.c
|
|
|
|
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
|
|
index 7a2304565a73..2e33a45f55ff 100644
|
|
--- a/drivers/usb/dwc3/Kconfig
|
|
+++ b/drivers/usb/dwc3/Kconfig
|
|
@@ -139,4 +139,14 @@ config USB_DWC3_QCOM
|
|
for peripheral mode support.
|
|
Say 'Y' or 'M' if you have one such device.
|
|
|
|
+config USB_DWC3_ROCKCHIP_INNO
|
|
+ tristate "Rockchip Platforms with INNO PHY"
|
|
+ depends on OF && COMMON_CLK && ARCH_ROCKCHIP
|
|
+ depends on USB=y || USB=USB_DWC3
|
|
+ default USB_DWC3
|
|
+ help
|
|
+ Support of USB2/3 functionality in Rockchip platforms
|
|
+ with INNO USB 3.0 PHY IP inside.
|
|
+ say 'Y' or 'M' if you have one such device.
|
|
+
|
|
endif
|
|
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
|
|
index ae86da0dc5bd..f5eb7de10128 100644
|
|
--- a/drivers/usb/dwc3/Makefile
|
|
+++ b/drivers/usb/dwc3/Makefile
|
|
@@ -51,3 +51,4 @@ obj-$(CONFIG_USB_DWC3_MESON_G12A) += dwc3-meson-g12a.o
|
|
obj-$(CONFIG_USB_DWC3_OF_SIMPLE) += dwc3-of-simple.o
|
|
obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o
|
|
obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o
|
|
+obj-$(CONFIG_USB_DWC3_ROCKCHIP_INNO) += dwc3-rockchip-inno.o
|
|
diff --git a/drivers/usb/dwc3/dwc3-rockchip-inno.c b/drivers/usb/dwc3/dwc3-rockchip-inno.c
|
|
new file mode 100644
|
|
index 000000000000..7007ddbcbdae
|
|
--- /dev/null
|
|
+++ b/drivers/usb/dwc3/dwc3-rockchip-inno.c
|
|
@@ -0,0 +1,271 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+/*
|
|
+ * dwc3-rockchip-inno.c - DWC3 glue layer for Rockchip devices with Innosilicon based PHY
|
|
+ *
|
|
+ * Based on dwc3-of-simple.c
|
|
+ */
|
|
+
|
|
+#include <linux/module.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/dma-mapping.h>
|
|
+#include <linux/clk.h>
|
|
+#include <linux/notifier.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/of_platform.h>
|
|
+#include <linux/pm_runtime.h>
|
|
+#include <linux/reset.h>
|
|
+
|
|
+#include <linux/workqueue.h>
|
|
+#include <linux/usb.h>
|
|
+#include <linux/usb/hcd.h>
|
|
+#include <linux/usb/phy.h>
|
|
+
|
|
+#include "core.h"
|
|
+#include "../host/xhci.h"
|
|
+
|
|
+
|
|
+struct dwc3_rk_inno {
|
|
+ struct device *dev;
|
|
+ struct clk_bulk_data *clks;
|
|
+ struct dwc3 *dwc;
|
|
+ struct usb_phy *phy;
|
|
+ struct notifier_block reset_nb;
|
|
+ struct work_struct reset_work;
|
|
+ struct mutex lock;
|
|
+ int num_clocks;
|
|
+ struct reset_control *resets;
|
|
+};
|
|
+
|
|
+static int dwc3_rk_inno_host_reset_notifier(struct notifier_block *nb, unsigned long event, void *data)
|
|
+{
|
|
+ struct dwc3_rk_inno *rk_inno = container_of(nb, struct dwc3_rk_inno, reset_nb);
|
|
+
|
|
+ schedule_work(&rk_inno->reset_work);
|
|
+
|
|
+ return NOTIFY_DONE;
|
|
+}
|
|
+
|
|
+static void dwc3_rk_inno_host_reset_work(struct work_struct *work)
|
|
+{
|
|
+ struct dwc3_rk_inno *rk_inno = container_of(work, struct dwc3_rk_inno, reset_work);
|
|
+ struct usb_hcd *hcd = dev_get_drvdata(&rk_inno->dwc->xhci->dev);
|
|
+ struct usb_hcd *shared_hcd = hcd->shared_hcd;
|
|
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
|
+ unsigned int count = 0;
|
|
+
|
|
+ mutex_lock(&rk_inno->lock);
|
|
+
|
|
+ if (hcd->state != HC_STATE_HALT) {
|
|
+ usb_remove_hcd(shared_hcd);
|
|
+ usb_remove_hcd(hcd);
|
|
+ }
|
|
+
|
|
+ if (rk_inno->phy)
|
|
+ usb_phy_shutdown(rk_inno->phy);
|
|
+
|
|
+ while (hcd->state != HC_STATE_HALT) {
|
|
+ if (++count > 1000) {
|
|
+ dev_err(rk_inno->dev, "wait for HCD remove 1s timeout!\n");
|
|
+ break;
|
|
+ }
|
|
+ usleep_range(1000, 1100);
|
|
+ }
|
|
+
|
|
+ if (hcd->state == HC_STATE_HALT) {
|
|
+ xhci->shared_hcd = shared_hcd;
|
|
+ usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
|
|
+ usb_add_hcd(shared_hcd, hcd->irq, IRQF_SHARED);
|
|
+ }
|
|
+
|
|
+ if (rk_inno->phy)
|
|
+ usb_phy_init(rk_inno->phy);
|
|
+
|
|
+ mutex_unlock(&rk_inno->lock);
|
|
+ dev_dbg(rk_inno->dev, "host reset complete\n");
|
|
+}
|
|
+
|
|
+static int dwc3_rk_inno_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct dwc3_rk_inno *rk_inno;
|
|
+ struct device *dev = &pdev->dev;
|
|
+ struct device_node *np = dev->of_node, *child, *node;
|
|
+ struct platform_device *child_pdev;
|
|
+
|
|
+ int ret;
|
|
+
|
|
+ rk_inno = devm_kzalloc(dev, sizeof(*rk_inno), GFP_KERNEL);
|
|
+ if (!rk_inno)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ platform_set_drvdata(pdev, rk_inno);
|
|
+ rk_inno->dev = dev;
|
|
+
|
|
+ rk_inno->resets = of_reset_control_array_get(np, false, true,
|
|
+ true);
|
|
+ if (IS_ERR(rk_inno->resets)) {
|
|
+ ret = PTR_ERR(rk_inno->resets);
|
|
+ dev_err(dev, "failed to get device resets, err=%d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = reset_control_deassert(rk_inno->resets);
|
|
+ if (ret)
|
|
+ goto err_resetc_put;
|
|
+
|
|
+ ret = clk_bulk_get_all(rk_inno->dev, &rk_inno->clks);
|
|
+ if (ret < 0)
|
|
+ goto err_resetc_assert;
|
|
+
|
|
+ rk_inno->num_clocks = ret;
|
|
+ ret = clk_bulk_prepare_enable(rk_inno->num_clocks, rk_inno->clks);
|
|
+ if (ret)
|
|
+ goto err_resetc_assert;
|
|
+
|
|
+ ret = of_platform_populate(np, NULL, NULL, dev);
|
|
+ if (ret)
|
|
+ goto err_clk_put;
|
|
+
|
|
+ child = of_get_child_by_name(np, "dwc3");
|
|
+ if (!child) {
|
|
+ dev_err(dev, "failed to find dwc3 core node\n");
|
|
+ ret = -ENODEV;
|
|
+ goto err_plat_depopulate;
|
|
+ }
|
|
+
|
|
+ child_pdev = of_find_device_by_node(child);
|
|
+ if (!child_pdev) {
|
|
+ dev_err(dev, "failed to get dwc3 core device\n");
|
|
+ ret = -ENODEV;
|
|
+ goto err_plat_depopulate;
|
|
+ }
|
|
+
|
|
+ rk_inno->dwc = platform_get_drvdata(child_pdev);
|
|
+ if (!rk_inno->dwc || !rk_inno->dwc->xhci) {
|
|
+ ret = -EPROBE_DEFER;
|
|
+ goto err_plat_depopulate;
|
|
+ }
|
|
+
|
|
+ node = of_parse_phandle(child, "usb-phy", 0);
|
|
+ INIT_WORK(&rk_inno->reset_work, dwc3_rk_inno_host_reset_work);
|
|
+ rk_inno->reset_nb.notifier_call = dwc3_rk_inno_host_reset_notifier;
|
|
+ rk_inno->phy = devm_usb_get_phy_by_node(dev, node, &rk_inno->reset_nb);
|
|
+ of_node_put(node);
|
|
+ mutex_init(&rk_inno->lock);
|
|
+
|
|
+ pm_runtime_set_active(dev);
|
|
+ pm_runtime_enable(dev);
|
|
+ pm_runtime_get_sync(dev);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_plat_depopulate:
|
|
+ of_platform_depopulate(dev);
|
|
+
|
|
+err_clk_put:
|
|
+ clk_bulk_disable_unprepare(rk_inno->num_clocks, rk_inno->clks);
|
|
+ clk_bulk_put_all(rk_inno->num_clocks, rk_inno->clks);
|
|
+
|
|
+err_resetc_assert:
|
|
+ reset_control_assert(rk_inno->resets);
|
|
+
|
|
+err_resetc_put:
|
|
+ reset_control_put(rk_inno->resets);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void __dwc3_rk_inno_teardown(struct dwc3_rk_inno *rk_inno)
|
|
+{
|
|
+ of_platform_depopulate(rk_inno->dev);
|
|
+
|
|
+ clk_bulk_disable_unprepare(rk_inno->num_clocks, rk_inno->clks);
|
|
+ clk_bulk_put_all(rk_inno->num_clocks, rk_inno->clks);
|
|
+ rk_inno->num_clocks = 0;
|
|
+
|
|
+ reset_control_assert(rk_inno->resets);
|
|
+
|
|
+ reset_control_put(rk_inno->resets);
|
|
+
|
|
+ pm_runtime_disable(rk_inno->dev);
|
|
+ pm_runtime_put_noidle(rk_inno->dev);
|
|
+ pm_runtime_set_suspended(rk_inno->dev);
|
|
+}
|
|
+
|
|
+static int dwc3_rk_inno_remove(struct platform_device *pdev)
|
|
+{
|
|
+ struct dwc3_rk_inno *rk_inno = platform_get_drvdata(pdev);
|
|
+
|
|
+ __dwc3_rk_inno_teardown(rk_inno);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void dwc3_rk_inno_shutdown(struct platform_device *pdev)
|
|
+{
|
|
+ struct dwc3_rk_inno *rk_inno = platform_get_drvdata(pdev);
|
|
+
|
|
+ __dwc3_rk_inno_teardown(rk_inno);
|
|
+}
|
|
+
|
|
+static int __maybe_unused dwc3_rk_inno_runtime_suspend(struct device *dev)
|
|
+{
|
|
+ struct dwc3_rk_inno *rk_inno = dev_get_drvdata(dev);
|
|
+
|
|
+ clk_bulk_disable(rk_inno->num_clocks, rk_inno->clks);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int __maybe_unused dwc3_rk_inno_runtime_resume(struct device *dev)
|
|
+{
|
|
+ struct dwc3_rk_inno *rk_inno = dev_get_drvdata(dev);
|
|
+
|
|
+ return clk_bulk_enable(rk_inno->num_clocks, rk_inno->clks);
|
|
+}
|
|
+
|
|
+static int __maybe_unused dwc3_rk_inno_suspend(struct device *dev)
|
|
+{
|
|
+ struct dwc3_rk_inno *rk_inno = dev_get_drvdata(dev);
|
|
+
|
|
+ reset_control_assert(rk_inno->resets);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int __maybe_unused dwc3_rk_inno_resume(struct device *dev)
|
|
+{
|
|
+ struct dwc3_rk_inno *rk_inno = dev_get_drvdata(dev);
|
|
+
|
|
+ reset_control_deassert(rk_inno->resets);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct dev_pm_ops dwc3_rk_inno_dev_pm_ops = {
|
|
+ SET_SYSTEM_SLEEP_PM_OPS(dwc3_rk_inno_suspend, dwc3_rk_inno_resume)
|
|
+ SET_RUNTIME_PM_OPS(dwc3_rk_inno_runtime_suspend,
|
|
+ dwc3_rk_inno_runtime_resume, NULL)
|
|
+};
|
|
+
|
|
+static const struct of_device_id of_dwc3_rk_inno_match[] = {
|
|
+ { .compatible = "rockchip,rk3328-dwc3" },
|
|
+ { /* Sentinel */ }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(of, of_dwc3_rk_inno_match);
|
|
+
|
|
+static struct platform_driver dwc3_rk_inno_driver = {
|
|
+ .probe = dwc3_rk_inno_probe,
|
|
+ .remove = dwc3_rk_inno_remove,
|
|
+ .shutdown = dwc3_rk_inno_shutdown,
|
|
+ .driver = {
|
|
+ .name = "dwc3-rk-inno",
|
|
+ .of_match_table = of_dwc3_rk_inno_match,
|
|
+ .pm = &dwc3_rk_inno_dev_pm_ops,
|
|
+ },
|
|
+};
|
|
+
|
|
+module_platform_driver(dwc3_rk_inno_driver);
|
|
+MODULE_LICENSE("GPL v2");
|
|
+MODULE_DESCRIPTION("DesignWare USB3 Rockchip Innosilicon Glue Layer");
|
|
+MODULE_AUTHOR("Peter Geis <pgwipeout@gmail.com>");
|
|
|
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Peter Geis <pgwipeout@gmail.com>
|
|
Date: Mon, 16 Nov 2020 15:17:35 +0000
|
|
Subject: [PATCH] arm64: dts: rockchip: add rk3328 usb3 and usb3phy nodes
|
|
|
|
Add the usb3 controller and usb3 phy nodes to the rk3328.
|
|
|
|
Signed-off-by: Peter Geis <pgwipeout@gmail.com>
|
|
---
|
|
arch/arm64/boot/dts/rockchip/rk3328.dtsi | 65 ++++++++++++++++++++++++
|
|
1 file changed, 65 insertions(+)
|
|
|
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3328.dtsi b/arch/arm64/boot/dts/rockchip/rk3328.dtsi
|
|
index 17709faf651b..d327fd300116 100644
|
|
--- a/arch/arm64/boot/dts/rockchip/rk3328.dtsi
|
|
+++ b/arch/arm64/boot/dts/rockchip/rk3328.dtsi
|
|
@@ -856,6 +856,40 @@ u2phy_host: host-port {
|
|
};
|
|
};
|
|
|
|
+ usb3phy: usb3-phy@ff470000 {
|
|
+ compatible = "rockchip,rk3328-usb3phy";
|
|
+ reg = <0x0 0xff460000 0x0 0x10000>;
|
|
+ clocks = <&cru PCLK_USB3PHY_OTG>, <&cru PCLK_USB3PHY_PIPE>;
|
|
+ clock-names = "usb3phy-otg", "usb3phy-pipe";
|
|
+ resets = <&cru SRST_USB3PHY_U2>,
|
|
+ <&cru SRST_USB3PHY_U3>,
|
|
+ <&cru SRST_USB3PHY_PIPE>,
|
|
+ <&cru SRST_USB3OTG_UTMI>,
|
|
+ <&cru SRST_USB3PHY_OTG_P>,
|
|
+ <&cru SRST_USB3PHY_PIPE_P>;
|
|
+ reset-names = "usb3phy-u2-por", "usb3phy-u3-por",
|
|
+ "usb3phy-pipe-mac", "usb3phy-utmi-mac",
|
|
+ "usb3phy-utmi-apb", "usb3phy-pipe-apb";
|
|
+ #address-cells = <2>;
|
|
+ #size-cells = <2>;
|
|
+ ranges;
|
|
+ status = "disabled";
|
|
+
|
|
+ usb3phy_utmi: utmi@ff470000 {
|
|
+ compatible = "rockchip,rk3328-usb3phy-utmi";
|
|
+ reg = <0x0 0xff470000 0x0 0x8000>;
|
|
+ #phy-cells = <0>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+
|
|
+ usb3phy_pipe: pipe@ff478000 {
|
|
+ compatible = "rockchip,rk3328-usb3phy-pipe";
|
|
+ reg = <0x0 0xff478000 0x0 0x8000>;
|
|
+ #phy-cells = <0>;
|
|
+ status = "disabled";
|
|
+ };
|
|
+ };
|
|
+
|
|
sdmmc: mmc@ff500000 {
|
|
compatible = "rockchip,rk3328-dw-mshc", "rockchip,rk3288-dw-mshc";
|
|
reg = <0x0 0xff500000 0x0 0x4000>;
|
|
@@ -987,6 +1021,37 @@ usb_host0_ohci: usb@ff5d0000 {
|
|
status = "disabled";
|
|
};
|
|
|
|
+ usbdrd3: usb@ff600000 {
|
|
+ compatible = "rockchip,rk3328-dwc3";
|
|
+ clocks = <&cru SCLK_USB3OTG_REF>, <&cru ACLK_USB3OTG>,
|
|
+ <&cru SCLK_USB3OTG_SUSPEND>;
|
|
+ clock-names = "ref", "bus_early", "suspend";
|
|
+ #address-cells = <2>;
|
|
+ #size-cells = <2>;
|
|
+ ranges;
|
|
+ status = "disabled";
|
|
+
|
|
+ usbdrd_dwc3: dwc3@ff600000 {
|
|
+ compatible = "snps,dwc3";
|
|
+ reg = <0x0 0xff600000 0x0 0x100000>;
|
|
+ interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
|
|
+ clocks = <&cru SCLK_USB3OTG_REF>, <&cru ACLK_USB3OTG>,
|
|
+ <&cru SCLK_USB3OTG_SUSPEND>;
|
|
+ clock-names = "ref", "bus_early", "suspend";
|
|
+ dr_mode = "host";
|
|
+ usb-phy = <&usb3phy_utmi>, <&usb3phy_pipe>;
|
|
+ phy_type = "utmi_wide";
|
|
+ snps,dis_enblslpm_quirk;
|
|
+ snps,dis-u2-freeclk-exists-quirk;
|
|
+ snps,dis_u2_susphy_quirk;
|
|
+ snps,dis_u3_susphy_quirk;
|
|
+ snps,dis-del-phy-power-chg-quirk;
|
|
+ snps,dis-tx-ipgap-linecheck-quirk;
|
|
+ snps,xhci-trb-ent-quirk;
|
|
+ status = "disabled";
|
|
+ };
|
|
+ };
|
|
+
|
|
gic: interrupt-controller@ff811000 {
|
|
compatible = "arm,gic-400";
|
|
#interrupt-cells = <3>;
|
|
|
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Peter Geis <pgwipeout@gmail.com>
|
|
Date: Mon, 16 Nov 2020 15:17:36 +0000
|
|
Subject: [PATCH] arm64: dts: rockchip: enable usb3 on rk3328-roc-cc board
|
|
|
|
Enable the usb3 controller and usb3 phy nodes on the rk3328-roc-cc board file.
|
|
|
|
Signed-off-by: Peter Geis <pgwipeout@gmail.com>
|
|
---
|
|
.../arm64/boot/dts/rockchip/rk3328-roc-cc.dts | 21 +++++++++++++++++++
|
|
1 file changed, 21 insertions(+)
|
|
|
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3328-roc-cc.dts b/arch/arm64/boot/dts/rockchip/rk3328-roc-cc.dts
|
|
index 19959bfba451..3ac876c08d61 100644
|
|
--- a/arch/arm64/boot/dts/rockchip/rk3328-roc-cc.dts
|
|
+++ b/arch/arm64/boot/dts/rockchip/rk3328-roc-cc.dts
|
|
@@ -366,6 +366,27 @@ &usb_host0_ohci {
|
|
status = "okay";
|
|
};
|
|
|
|
+&usbdrd3 {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&usbdrd_dwc3 {
|
|
+ dr_mode = "host";
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&usb3phy {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&usb3phy_utmi {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
+&usb3phy_pipe {
|
|
+ status = "okay";
|
|
+};
|
|
+
|
|
&vop {
|
|
status = "okay";
|
|
};
|
|
|
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Jonas Karlman <jonas@kwiboo.se>
|
|
Date: Sun, 17 Feb 2019 22:14:38 +0000
|
|
Subject: [PATCH] mmc: core: set initial signal voltage on power off
|
|
|
|
Some boards have SD card connectors where the power rail cannot be switched
|
|
off by the driver. If the card has not been power cycled, it may still be
|
|
using 1.8V signaling after a warm re-boot. Bootroms expecting 3.3V signaling
|
|
will fail to boot from a UHS card that continue to use 1.8V signaling.
|
|
|
|
Set initial signal voltage in mmc_power_off() to allow re-boot to function.
|
|
|
|
This fixes re-boot with UHS cards on Asus Tinker Board (Rockchip RK3288),
|
|
same issue have been seen on some Rockchip RK3399 boards.
|
|
|
|
I am sending this as a RFC because I have no insights into SD/MMC subsystem,
|
|
this change fix a re-boot issue on my boards and does not break emmc/sdio.
|
|
Is this an acceptable workaround? Any advice is appreciated.
|
|
|
|
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
|
|
---
|
|
drivers/mmc/core/core.c | 8 ++++++++
|
|
1 file changed, 8 insertions(+)
|
|
|
|
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
|
|
index eaf4810fe656..6f8ea06b187b 100644
|
|
--- a/drivers/mmc/core/core.c
|
|
+++ b/drivers/mmc/core/core.c
|
|
@@ -1349,6 +1349,14 @@ void mmc_power_off(struct mmc_host *host)
|
|
if (host->ios.power_mode == MMC_POWER_OFF)
|
|
return;
|
|
|
|
+ mmc_set_initial_signal_voltage(host);
|
|
+
|
|
+ /*
|
|
+ * This delay should be sufficient to allow the power supply
|
|
+ * to reach the minimum voltage.
|
|
+ */
|
|
+ mmc_delay(host->ios.power_delay_ms);
|
|
+
|
|
mmc_pwrseq_power_off(host);
|
|
|
|
host->ios.clock = 0;
|
|
|
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Rudi Heitbaum <rudi@heitbaum.com>
|
|
Date: Fri, 28 May 2021 10:19:50 +0000
|
|
Subject: [PATCH] regulator: fan53555: add tcs4526
|
|
|
|
For rk3399pro boards the tcs4526 regulator supports the vdd_gpu
|
|
regulator. The tcs4526 regulator has a chip id of <0>.
|
|
Add the compatibile tcs,tcs4526
|
|
|
|
without this patch, the dmesg output is:
|
|
fan53555-regulator 0-0010: Chip ID 0 not supported!
|
|
fan53555-regulator 0-0010: Failed to setup device!
|
|
fan53555-regulator: probe of 0-0010 failed with error -22
|
|
with this patch, the dmesg output is:
|
|
vdd_gpu: supplied by vcc5v0_sys
|
|
|
|
The regulators are described as:
|
|
- Dedicated power management IC TCS4525
|
|
- Lithium battery protection chip TCS4526
|
|
|
|
This has been tested with a Radxa Rock Pi N10.
|
|
|
|
Signed-off-by: Rudi Heitbaum <rudi@heitbaum.com>
|
|
---
|
|
drivers/regulator/fan53555.c | 11 +++++++++++
|
|
1 file changed, 11 insertions(+)
|
|
|
|
diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c
|
|
index f3f49cf3731b..bc8242e1dd0f 100644
|
|
--- a/drivers/regulator/fan53555.c
|
|
+++ b/drivers/regulator/fan53555.c
|
|
@@ -92,6 +92,10 @@ enum {
|
|
TCS4525_CHIP_ID_12 = 12,
|
|
};
|
|
|
|
+enum {
|
|
+ TCS4526_CHIP_ID_00 = 0,
|
|
+};
|
|
+
|
|
/* IC mask revision */
|
|
enum {
|
|
FAN53555_CHIP_REV_00 = 0x3,
|
|
@@ -372,6 +376,7 @@ static int fan53526_voltages_setup_tcs(struct fan53555_device_info *di)
|
|
{
|
|
switch (di->chip_id) {
|
|
case TCS4525_CHIP_ID_12:
|
|
+ case TCS4526_CHIP_ID_00:
|
|
di->slew_reg = TCS4525_TIME;
|
|
di->slew_mask = TCS_SLEW_MASK;
|
|
di->slew_shift = TCS_SLEW_SHIFT;
|
|
@@ -562,6 +567,9 @@ static const struct of_device_id __maybe_unused fan53555_dt_ids[] = {
|
|
}, {
|
|
.compatible = "tcs,tcs4525",
|
|
.data = (void *)FAN53526_VENDOR_TCS
|
|
+ }, {
|
|
+ .compatible = "tcs,tcs4526",
|
|
+ .data = (void *)FAN53526_VENDOR_TCS
|
|
},
|
|
{ }
|
|
};
|
|
@@ -670,6 +678,9 @@ static const struct i2c_device_id fan53555_id[] = {
|
|
}, {
|
|
.name = "tcs4525",
|
|
.driver_data = FAN53526_VENDOR_TCS
|
|
+ }, {
|
|
+ .name = "tcs4526",
|
|
+ .driver_data = FAN53526_VENDOR_TCS
|
|
},
|
|
{ },
|
|
};
|