From e3e12f52b214121840b44c91de5b3e5af5d3eb84 Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Mon, 06 Nov 2023 03:04:41 +0000
Subject: [PATCH] rk3568 rt init

---
 kernel/drivers/phy/rockchip/phy-rockchip-inno-usb2.c |  254 ++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 220 insertions(+), 34 deletions(-)

diff --git a/kernel/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/kernel/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
index 207e314..9d6dbbf 100644
--- a/kernel/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
+++ b/kernel/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
@@ -59,6 +59,7 @@
 	PHY_STATE_DISCONNECT	= 1,
 	PHY_STATE_CONNECT	= 2,
 	PHY_STATE_FS_LS_ONLINE	= 4,
+	PHY_STATE_SE1		= 6,
 };
 
 /**
@@ -158,6 +159,7 @@
  * @utmi_ls: utmi linestate state register.
  * @utmi_hstdet: utmi host disconnect register.
  * @vbus_det_en: vbus detect function power down register.
+ * @port_ls_filter_con: set linestate filter time for otg port or host port.
  */
 struct rockchip_usb2phy_port_cfg {
 	struct usb2phy_reg	phy_sus;
@@ -188,6 +190,7 @@
 	struct usb2phy_reg	utmi_ls;
 	struct usb2phy_reg	utmi_hstdet;
 	struct usb2phy_reg	vbus_det_en;
+	struct usb2phy_reg	port_ls_filter_con;
 };
 
 /**
@@ -196,7 +199,9 @@
  * @num_ports: specify how many ports that the phy has.
  * @phy_tuning: phy default parameters tunning.
  * @vbus_detect: vbus voltage level detection function.
- * @clkout_ctl: keep on/turn off output clk of phy.
+ * @clkout_ctl: keep on/turn off output clk of phy via commonon bit.
+ * @clkout_ctl_phy: keep on/turn off output clk of phy via phy inner
+ *		    debug register.
  * @ls_filter_con: set linestate filter time.
  * @chg_det: charger detection registers.
  */
@@ -204,8 +209,11 @@
 	unsigned int	reg;
 	unsigned int	num_ports;
 	int (*phy_tuning)(struct rockchip_usb2phy *);
-	int (*vbus_detect)(struct rockchip_usb2phy *rphy, bool en);
+	int (*vbus_detect)(struct rockchip_usb2phy *rphy,
+			   const struct usb2phy_reg *vbus_det_en,
+			   bool en);
 	struct usb2phy_reg	clkout_ctl;
+	struct usb2phy_reg	clkout_ctl_phy;
 	struct usb2phy_reg	ls_filter_con;
 	const struct rockchip_usb2phy_port_cfg	port_cfgs[USB2PHY_NUM_PORTS];
 	const struct rockchip_chg_det_reg	chg_det;
@@ -253,6 +261,7 @@
 	bool		vbus_always_on;
 	bool		vbus_enabled;
 	bool		bypass_uart_en;
+	bool		dis_u2_susphy;
 	int		bvalid_irq;
 	int		ls_irq;
 	int             id_irq;
@@ -275,9 +284,10 @@
  * @grf: General Register Files regmap.
  * @usbgrf: USB General Register Files regmap.
  * *phy_base: the base address of USB PHY.
- * @clk: clock struct of phy input clk.
+ * @clks: array of phy input clocks.
  * @clk480m: clock struct of phy output clk.
  * @clk_hw: clock struct of phy output clk management.
+ * @num_clks: number of phy input clocks.
  * @chg_state: states involved in USB charger detection.
  * @chg_type: USB charger types.
  * @dcd_retries: The retry count used to track Data contact
@@ -297,9 +307,10 @@
 	struct regmap	*grf;
 	struct regmap	*usbgrf;
 	void __iomem	*phy_base;
-	struct clk	*clk;
+	struct clk_bulk_data	*clks;
 	struct clk	*clk480m;
 	struct clk_hw	clk480m_hw;
+	int		num_clks;
 	enum usb_chg_state	chg_state;
 	enum power_supply_type	chg_type;
 	u8			dcd_retries;
@@ -344,6 +355,29 @@
 	return tmp == reg->enable;
 }
 
+static inline void phy_property_enable(void __iomem *base,
+				    const struct usb2phy_reg *reg, bool en)
+{
+	unsigned int val, tmp;
+
+	val = readl(base + reg->offset);
+	tmp = en ? reg->enable : reg->disable;
+	val &= ~GENMASK(reg->bitend, reg->bitstart);
+	val |= tmp << reg->bitstart;
+	writel(val, base + reg->offset);
+}
+
+static inline bool phy_property_enabled(void __iomem *base,
+				    const struct usb2phy_reg *reg)
+{
+	unsigned int orig, tmp;
+	unsigned int mask = GENMASK(reg->bitend, reg->bitstart);
+
+	orig = readl(base + reg->offset);
+	tmp = (orig & mask) >> reg->bitstart;
+	return tmp == reg->enable;
+}
+
 static int rockchip_usb2phy_clk480m_prepare(struct clk_hw *hw)
 {
 	struct rockchip_usb2phy *rphy =
@@ -352,7 +386,14 @@
 	int ret;
 
 	/* turn on 480m clk output if it is off */
-	if (!property_enabled(base, &rphy->phy_cfg->clkout_ctl)) {
+	if (rphy->phy_cfg->clkout_ctl_phy.enable) {
+		if (!phy_property_enabled(rphy->phy_base, &rphy->phy_cfg->clkout_ctl_phy)) {
+			phy_property_enable(rphy->phy_base, &rphy->phy_cfg->clkout_ctl_phy, true);
+
+			/* waiting for the clk become stable */
+			usleep_range(1200, 1300);
+		}
+	} else if (!property_enabled(base, &rphy->phy_cfg->clkout_ctl)) {
 		ret = property_enable(base, &rphy->phy_cfg->clkout_ctl, true);
 		if (ret)
 			return ret;
@@ -371,7 +412,10 @@
 	struct regmap *base = get_reg_base(rphy);
 
 	/* turn off 480m clk output */
-	property_enable(base, &rphy->phy_cfg->clkout_ctl, false);
+	if (rphy->phy_cfg->clkout_ctl_phy.enable)
+		phy_property_enable(rphy->phy_base, &rphy->phy_cfg->clkout_ctl_phy, false);
+	else
+		property_enable(base, &rphy->phy_cfg->clkout_ctl, false);
 }
 
 static int rockchip_usb2phy_clk480m_prepared(struct clk_hw *hw)
@@ -380,7 +424,10 @@
 		container_of(hw, struct rockchip_usb2phy, clk480m_hw);
 	struct regmap *base = get_reg_base(rphy);
 
-	return property_enabled(base, &rphy->phy_cfg->clkout_ctl);
+	if (rphy->phy_cfg->clkout_ctl_phy.enable)
+		return phy_property_enabled(rphy->phy_base, &rphy->phy_cfg->clkout_ctl_phy);
+	else
+		return property_enabled(base, &rphy->phy_cfg->clkout_ctl);
 }
 
 static unsigned long
@@ -410,6 +457,7 @@
 {
 	struct device_node *node = rphy->dev->of_node;
 	struct clk_init_data init = {};
+	struct clk *refclk = of_clk_get_by_name(node, "phyclk");
 	const char *clk_name;
 	int ret;
 
@@ -420,8 +468,8 @@
 	/* 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);
+	if (!IS_ERR(refclk)) {
+		clk_name = __clk_get_name(refclk);
 		init.parent_names = &clk_name;
 		init.num_parents = 1;
 	} else {
@@ -855,7 +903,8 @@
 	}
 
 	if (rphy->phy_cfg->vbus_detect)
-		rphy->phy_cfg->vbus_detect(rphy, vbus_det_en);
+		rphy->phy_cfg->vbus_detect(rphy, &rport->port_cfg->vbus_det_en,
+					   vbus_det_en);
 	else
 		ret = property_enable(rphy->grf, &rport->port_cfg->vbus_det_en,
 				      vbus_det_en);
@@ -1029,7 +1078,8 @@
 		rport->state = OTG_STATE_B_IDLE;
 		if (!rport->vbus_attached) {
 			mutex_unlock(&rport->mutex);
-			rockchip_usb2phy_power_off(rport->phy);
+			if (!rport->dis_u2_susphy)
+				rockchip_usb2phy_power_off(rport->phy);
 			mutex_lock(&rport->mutex);
 		}
 		/* fall through */
@@ -1090,7 +1140,8 @@
 			rphy->chg_state = USB_CHG_STATE_UNDEFINED;
 			rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
 			mutex_unlock(&rport->mutex);
-			rockchip_usb2phy_power_off(rport->phy);
+			if (!rport->dis_u2_susphy)
+				rockchip_usb2phy_power_off(rport->phy);
 			mutex_lock(&rport->mutex);
 		}
 		break;
@@ -1434,6 +1485,15 @@
 			dev_dbg(&rport->phy->dev, "FS/LS online\n");
 		}
 		break;
+	case PHY_STATE_SE1:
+		if (rport->suspended) {
+			dev_dbg(&rport->phy->dev, "linestate is SE1, power on phy\n");
+			mutex_unlock(&rport->mutex);
+			rockchip_usb2phy_power_on(rport->phy);
+			mutex_lock(&rport->mutex);
+			rport->suspended = false;
+		}
+		break;
 	case PHY_STATE_DISCONNECT:
 		if (!rport->suspended) {
 			dev_dbg(&rport->phy->dev, "Disconnected\n");
@@ -1764,6 +1824,11 @@
 	return NOTIFY_DONE;
 }
 
+static void rockchip_otg_wake_lock_destroy(void *data)
+{
+	wake_lock_destroy((struct wake_lock *)(data));
+}
+
 static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy,
 					  struct rockchip_usb2phy_port *rport,
 					  struct device_node *child_np)
@@ -1789,6 +1854,8 @@
 		of_property_read_bool(child_np, "rockchip,vbus-always-on");
 	rport->utmi_avalid =
 		of_property_read_bool(child_np, "rockchip,utmi-avalid");
+	rport->dis_u2_susphy =
+		of_property_read_bool(child_np, "rockchip,dis-u2-susphy");
 
 	/* enter lower power state when suspend */
 	rport->low_power_en =
@@ -1833,6 +1900,11 @@
 		property_enable(base, &rport->port_cfg->bvalid_set, false);
 
 	wake_lock_init(&rport->wakelock, WAKE_LOCK_SUSPEND, "rockchip_otg");
+	ret = devm_add_action_or_reset(rphy->dev, rockchip_otg_wake_lock_destroy,
+				       &rport->wakelock);
+	if (ret)
+		return ret;
+
 	INIT_DELAYED_WORK(&rport->bypass_uart_work,
 			  rockchip_usb_bypass_uart_work);
 	INIT_DELAYED_WORK(&rport->chg_work, rockchip_chg_detect_work);
@@ -1845,7 +1917,7 @@
 					EXTCON_USB_HOST, &rport->event_nb);
 		if (ret) {
 			dev_err(rphy->dev, "register USB HOST notifier failed\n");
-			goto err;
+			return ret;
 		}
 	}
 
@@ -1861,10 +1933,6 @@
 	rport->suspended = true;
 
 	return 0;
-
-err:
-	wake_lock_destroy(&rport->wakelock);
-	return ret;
 }
 
 static int rockchip_usb2phy_probe(struct platform_device *pdev)
@@ -1963,13 +2031,19 @@
 	pm_runtime_enable(dev);
 	pm_runtime_get_sync(dev);
 
-	rphy->clk = of_clk_get_by_name(np, "phyclk");
-	if (!IS_ERR(rphy->clk)) {
-		clk_prepare_enable(rphy->clk);
-	} else {
-		dev_info(&pdev->dev, "no phyclk specified\n");
-		rphy->clk = NULL;
-	}
+	ret = devm_clk_bulk_get_all(dev, &rphy->clks);
+	if (ret == -EPROBE_DEFER)
+		return ret;
+
+	/* Clocks are optional */
+	if (ret < 0)
+		rphy->num_clks = 0;
+	else
+		rphy->num_clks = ret;
+
+	ret = clk_bulk_prepare_enable(rphy->num_clks, rphy->clks);
+	if (ret)
+		return ret;
 
 	if (rphy->phy_cfg->phy_tuning) {
 		ret = rphy->phy_cfg->phy_tuning(rphy);
@@ -2061,10 +2135,8 @@
 disable_clks:
 	pm_runtime_put_sync(dev);
 	pm_runtime_disable(dev);
-	if (rphy->clk) {
-		clk_disable_unprepare(rphy->clk);
-		clk_put(rphy->clk);
-	}
+	clk_bulk_disable_unprepare(rphy->num_clks, rphy->clks);
+
 	return ret;
 }
 
@@ -2305,6 +2377,43 @@
 	return ret;
 }
 
+static int rk3528_usb2phy_tuning(struct rockchip_usb2phy *rphy)
+{
+	u32 reg;
+	int ret = 0;
+
+	/* Turn off otg port differential receiver in suspend mode */
+	reg = readl(rphy->phy_base + 0x30);
+	writel(reg & ~BIT(2), rphy->phy_base + 0x30);
+
+	/* Turn off host port differential receiver in suspend mode */
+	reg = readl(rphy->phy_base + 0x0430);
+	writel(reg & ~BIT(2), rphy->phy_base + 0x0430);
+
+	/* Set otg port HS eye height to 400mv(default is 450mv) */
+	reg = readl(rphy->phy_base + 0x30);
+	reg &= ~GENMASK(6, 4);
+	reg |= (0x00 << 4);
+	writel(reg, rphy->phy_base + 0x30);
+
+	/* Set host port HS eye height to 400mv(default is 450mv) */
+	reg = readl(rphy->phy_base + 0x430);
+	reg &= ~GENMASK(6, 4);
+	reg |= (0x00 << 4);
+	writel(reg, rphy->phy_base + 0x430);
+
+	/* Choose the Tx fs/ls data as linestate from TX driver for otg port */
+	reg = readl(rphy->phy_base + 0x94);
+	reg &= ~GENMASK(6, 3);
+	reg |= (0x03 << 3);
+	writel(reg, rphy->phy_base + 0x94);
+
+	/* Enable otg and host ports phy irq to pmu wakeup source */
+	ret |= regmap_write(rphy->grf, 0x80004, 0x00030003);
+
+	return ret;
+}
+
 static int rk3568_usb2phy_tuning(struct rockchip_usb2phy *rphy)
 {
 	u32 reg;
@@ -2352,18 +2461,20 @@
 	return ret;
 }
 
-static int rk3568_vbus_detect_control(struct rockchip_usb2phy *rphy, bool en)
+static int rockchip_usb2phy_vbus_det_control(struct rockchip_usb2phy *rphy,
+					     const struct usb2phy_reg *vbus_det_en,
+					     bool en)
 {
 	u32 reg;
 
 	if (en) {
-		reg = readl(rphy->phy_base + 0x3c);
+		reg = readl(rphy->phy_base + vbus_det_en->offset);
 		/* Enable vbus voltage level detection function */
-		writel(reg & ~BIT(7), rphy->phy_base + 0x3c);
+		writel(reg & ~BIT(7), rphy->phy_base + vbus_det_en->offset);
 	} else {
-		reg = readl(rphy->phy_base + 0x3c);
+		reg = readl(rphy->phy_base + vbus_det_en->offset);
 		/* Disable vbus voltage level detection function */
-		writel(reg | BIT(7), rphy->phy_base + 0x3c);
+		writel(reg | BIT(7), rphy->phy_base + vbus_det_en->offset);
 	}
 
 	return 0;
@@ -2397,6 +2508,14 @@
 		rport = &rphy->ports[index];
 		if (!rport->phy)
 			continue;
+
+		if (rport->port_cfg->port_ls_filter_con.enable) {
+			ret = regmap_write(rphy->grf,
+					   rport->port_cfg->port_ls_filter_con.offset,
+					   rport->port_cfg->port_ls_filter_con.enable);
+			if (ret)
+				dev_err(rphy->dev, "failed to set port ls filter %d\n", ret);
+		}
 
 		if (rport->port_id == USB2PHY_PORT_OTG &&
 		    (rport->id_irq > 0 || rphy->irq > 0)) {
@@ -2466,6 +2585,14 @@
 		rport = &rphy->ports[index];
 		if (!rport->phy)
 			continue;
+
+		if (rport->port_cfg->port_ls_filter_con.disable) {
+			ret = regmap_write(rphy->grf,
+					   rport->port_cfg->port_ls_filter_con.offset,
+					   rport->port_cfg->port_ls_filter_con.disable);
+			if (ret)
+				dev_err(rphy->dev, "failed to set port ls filter %d\n", ret);
+		}
 
 		if (rport->port_id == USB2PHY_PORT_OTG &&
 		    (rport->id_irq > 0 || rphy->irq > 0)) {
@@ -2988,12 +3115,69 @@
 	{ /* sentinel */ }
 };
 
+static const struct rockchip_usb2phy_cfg rk3528_phy_cfgs[] = {
+	{
+		.reg = 0xffdf0000,
+		.num_ports	= 2,
+		.phy_tuning	= rk3528_usb2phy_tuning,
+		.vbus_detect	= rockchip_usb2phy_vbus_det_control,
+		.clkout_ctl_phy	= { 0x041c, 7, 2, 0, 0x27 },
+		.port_cfgs	= {
+			[USB2PHY_PORT_OTG] = {
+				.phy_sus	= { 0x6004c, 8, 0, 0, 0x1d1 },
+				.bvalid_det_en	= { 0x60074, 2, 2, 0, 1 },
+				.bvalid_det_st	= { 0x60078, 2, 2, 0, 1 },
+				.bvalid_det_clr = { 0x6007c, 2, 2, 0, 1 },
+				.bvalid_set	= { 0x6004c, 15, 14, 0, 3 },
+				.iddig_output	= { 0x6004c, 10, 10, 0, 1 },
+				.iddig_en	= { 0x6004c, 9, 9, 0, 1 },
+				.idfall_det_en	= { 0x60074, 5, 5, 0, 1 },
+				.idfall_det_st	= { 0x60078, 5, 5, 0, 1 },
+				.idfall_det_clr = { 0x6007c, 5, 5, 0, 1 },
+				.idrise_det_en	= { 0x60074, 4, 4, 0, 1 },
+				.idrise_det_st	= { 0x60078, 4, 4, 0, 1 },
+				.idrise_det_clr = { 0x6007c, 4, 4, 0, 1 },
+				.ls_det_en	= { 0x60074, 0, 0, 0, 1 },
+				.ls_det_st	= { 0x60078, 0, 0, 0, 1 },
+				.ls_det_clr	= { 0x6007c, 0, 0, 0, 1 },
+				.utmi_avalid	= { 0x6006c, 1, 1, 0, 1 },
+				.utmi_bvalid	= { 0x6006c, 0, 0, 0, 1 },
+				.utmi_iddig	= { 0x6006c, 6, 6, 0, 1 },
+				.utmi_ls	= { 0x6006c, 5, 4, 0, 1 },
+				.vbus_det_en	= { 0x003c, 7, 7, 0, 1 },
+				.port_ls_filter_con = { 0x60080, 19, 0, 0x30100, 0x20 },
+			},
+			[USB2PHY_PORT_HOST] = {
+				.phy_sus	= { 0x6005c, 8, 0, 0x1d2, 0x1d1 },
+				.ls_det_en	= { 0x60090, 0, 0, 0, 1 },
+				.ls_det_st	= { 0x60094, 0, 0, 0, 1 },
+				.ls_det_clr	= { 0x60098, 0, 0, 0, 1 },
+				.utmi_ls	= { 0x6006c, 13, 12, 0, 1 },
+				.utmi_hstdet	= { 0x6006c, 15, 15, 0, 1 },
+				.port_ls_filter_con = { 0x6009c, 19, 0, 0x30100, 0x20 },
+			}
+		},
+		.chg_det = {
+			.chg_mode	= { 0x6004c, 8, 0, 0, 0x1d7 },
+			.cp_det		= { 0x6006c, 19, 19, 0, 1 },
+			.dcp_det	= { 0x6006c, 18, 18, 0, 1 },
+			.dp_det		= { 0x6006c, 20, 20, 0, 1 },
+			.idm_sink_en	= { 0x60058, 1, 1, 0, 1 },
+			.idp_sink_en	= { 0x60058, 0, 0, 0, 1 },
+			.idp_src_en	= { 0x60058, 2, 2, 0, 1 },
+			.rdm_pdwn_en	= { 0x60058, 3, 3, 0, 1 },
+			.vdm_src_en	= { 0x60058, 5, 5, 0, 1 },
+			.vdp_src_en	= { 0x60058, 4, 4, 0, 1 },
+		},
+	}
+};
+
 static const struct rockchip_usb2phy_cfg rk3568_phy_cfgs[] = {
 	{
 		.reg = 0xfe8a0000,
 		.num_ports	= 2,
 		.phy_tuning	= rk3568_usb2phy_tuning,
-		.vbus_detect	= rk3568_vbus_detect_control,
+		.vbus_detect	= rockchip_usb2phy_vbus_det_control,
 		.clkout_ctl	= { 0x0008, 4, 4, 1, 0 },
 		.ls_filter_con	= { 0x0040, 19, 0, 0x30100, 0x00020 },
 		.port_cfgs	= {
@@ -3020,6 +3204,7 @@
 				.utmi_bvalid	= { 0x00c0, 9, 9, 0, 1 },
 				.utmi_iddig	= { 0x00c0, 6, 6, 0, 1 },
 				.utmi_ls	= { 0x00c0, 5, 4, 0, 1 },
+				.vbus_det_en	= { 0x003c, 7, 7, 0, 1 },
 			},
 			[USB2PHY_PORT_HOST] = {
 				/* Select suspend control from controller */
@@ -3123,6 +3308,7 @@
 	{ .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs },
 	{ .compatible = "rockchip,rk3368-usb2phy", .data = &rk3368_phy_cfgs },
 	{ .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs },
+	{ .compatible = "rockchip,rk3528-usb2phy", .data = &rk3528_phy_cfgs },
 	{ .compatible = "rockchip,rk3568-usb2phy", .data = &rk3568_phy_cfgs },
 	{ .compatible = "rockchip,rv1108-usb2phy", .data = &rv1108_phy_cfgs },
 	{}

--
Gitblit v1.6.2