armbian_build/patch/kernel/station-current/1-linux-0004-rockchip-from-list.patch
2021-07-07 16:15:34 +02:00

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, &regmap_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, &regmap_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
},
{ },
};