From 51a9b2c03dd3fddc56c2f68740fade2e38a066d0 Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Wed, 13 Apr 2022 22:22:57 -0500
Subject: [PATCH] phy: rockchip-inno-usb2: Handle ID IRQ

This supports detecting host mode for the OTG port without an extcon.

The rv1108 properties are not updated due to lack of documentation.

Signed-off-by: Samuel Holland <samuel@sholland.org>
Tested-by: Michael Riesch <michael.riesch@wolfvision.net>
Link: https://lore.kernel.org/r/20220414032258.40984-7-samuel@sholland.org
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
 drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 85 +++++++++++++++++++
 1 file changed, 85 insertions(+)

--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
@@ -116,11 +116,15 @@ struct rockchip_chg_det_reg {
  * @bvalid_det_en: vbus valid rise detection enable register.
  * @bvalid_det_st: vbus valid rise detection status register.
  * @bvalid_det_clr: vbus valid rise detection clear register.
+ * @id_det_en: id detection enable register.
+ * @id_det_st: id detection state register.
+ * @id_det_clr: id detection clear register.
  * @ls_det_en: linestate detection enable register.
  * @ls_det_st: linestate detection state register.
  * @ls_det_clr: linestate detection clear register.
  * @utmi_avalid: utmi vbus avalid status register.
  * @utmi_bvalid: utmi vbus bvalid status register.
+ * @utmi_id: utmi id state register.
  * @utmi_ls: utmi linestate state register.
  * @utmi_hstdet: utmi host disconnect register.
  */
@@ -129,11 +133,15 @@ struct rockchip_usb2phy_port_cfg {
 	struct usb2phy_reg	bvalid_det_en;
 	struct usb2phy_reg	bvalid_det_st;
 	struct usb2phy_reg	bvalid_det_clr;
+	struct usb2phy_reg	id_det_en;
+	struct usb2phy_reg	id_det_st;
+	struct usb2phy_reg	id_det_clr;
 	struct usb2phy_reg	ls_det_en;
 	struct usb2phy_reg	ls_det_st;
 	struct usb2phy_reg	ls_det_clr;
 	struct usb2phy_reg	utmi_avalid;
 	struct usb2phy_reg	utmi_bvalid;
+	struct usb2phy_reg	utmi_id;
 	struct usb2phy_reg	utmi_ls;
 	struct usb2phy_reg	utmi_hstdet;
 };
@@ -161,6 +169,7 @@ struct rockchip_usb2phy_cfg {
  * @suspended: phy suspended flag.
  * @vbus_attached: otg device vbus status.
  * @bvalid_irq: IRQ number assigned for vbus valid rise detection.
+ * @id_irq: IRQ number assigned for ID pin detection.
  * @ls_irq: IRQ number assigned for linestate detection.
  * @otg_mux_irq: IRQ number which multiplex otg-id/otg-bvalid/linestate
  *		 irqs to one irq in otg-port.
@@ -179,6 +188,7 @@ struct rockchip_usb2phy_port {
 	bool		suspended;
 	bool		vbus_attached;
 	int		bvalid_irq;
+	int		id_irq;
 	int		ls_irq;
 	int		otg_mux_irq;
 	struct mutex	mutex;
@@ -426,6 +436,19 @@ static int rockchip_usb2phy_init(struct
 			if (ret)
 				goto out;
 
+			/* clear id status and enable id detect irq */
+			ret = property_enable(rphy->grf,
+					      &rport->port_cfg->id_det_clr,
+					      true);
+			if (ret)
+				goto out;
+
+			ret = property_enable(rphy->grf,
+					      &rport->port_cfg->id_det_en,
+					      true);
+			if (ret)
+				goto out;
+
 			schedule_delayed_work(&rport->otg_sm_work,
 					      OTG_SCHEDULE_DELAY * 3);
 		} else {
@@ -923,11 +946,30 @@ static irqreturn_t rockchip_usb2phy_bval
 	return IRQ_HANDLED;
 }
 
+static irqreturn_t rockchip_usb2phy_id_irq(int irq, void *data)
+{
+	struct rockchip_usb2phy_port *rport = data;
+	struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
+	bool id;
+
+	if (!property_enabled(rphy->grf, &rport->port_cfg->id_det_st))
+		return IRQ_NONE;
+
+	/* clear id detect irq pending status */
+	property_enable(rphy->grf, &rport->port_cfg->id_det_clr, true);
+
+	id = property_enabled(rphy->grf, &rport->port_cfg->utmi_id);
+	extcon_set_state_sync(rphy->edev, EXTCON_USB_HOST, !id);
+
+	return IRQ_HANDLED;
+}
+
 static irqreturn_t rockchip_usb2phy_otg_mux_irq(int irq, void *data)
 {
 	irqreturn_t ret = IRQ_NONE;
 
 	ret |= rockchip_usb2phy_bvalid_irq(irq, data);
+	ret |= rockchip_usb2phy_id_irq(irq, data);
 
 	return ret;
 }
@@ -1025,6 +1067,25 @@ static int rockchip_usb2phy_port_irq_ini
 					"failed to request otg-bvalid irq handle\n");
 				return ret;
 			}
+
+			rport->id_irq = of_irq_get_byname(child_np, "otg-id");
+			if (rport->id_irq < 0) {
+				dev_err(rphy->dev, "no otg-id irq provided\n");
+				ret = rport->id_irq;
+				return ret;
+			}
+
+			ret = devm_request_threaded_irq(rphy->dev, rport->id_irq,
+							NULL,
+							rockchip_usb2phy_id_irq,
+							IRQF_ONESHOT,
+							"rockchip_usb2phy_id",
+							rport);
+			if (ret) {
+				dev_err(rphy->dev,
+					"failed to request otg-id irq handle\n");
+				return ret;
+			}
 		}
 		break;
 	default:
@@ -1299,10 +1360,14 @@ static const struct rockchip_usb2phy_cfg
 				.bvalid_det_en	= { 0x0680, 3, 3, 0, 1 },
 				.bvalid_det_st	= { 0x0690, 3, 3, 0, 1 },
 				.bvalid_det_clr	= { 0x06a0, 3, 3, 0, 1 },
+				.id_det_en	= { 0x0680, 6, 5, 0, 3 },
+				.id_det_st	= { 0x0690, 6, 5, 0, 3 },
+				.id_det_clr	= { 0x06a0, 6, 5, 0, 3 },
 				.ls_det_en	= { 0x0680, 2, 2, 0, 1 },
 				.ls_det_st	= { 0x0690, 2, 2, 0, 1 },
 				.ls_det_clr	= { 0x06a0, 2, 2, 0, 1 },
 				.utmi_bvalid	= { 0x0480, 4, 4, 0, 1 },
+				.utmi_id	= { 0x0480, 1, 1, 0, 1 },
 				.utmi_ls	= { 0x0480, 3, 2, 0, 1 },
 			},
 			[USB2PHY_PORT_HOST] = {
@@ -1358,11 +1423,15 @@ static const struct rockchip_usb2phy_cfg
 				.bvalid_det_en	= { 0x3020, 3, 2, 0, 3 },
 				.bvalid_det_st	= { 0x3024, 3, 2, 0, 3 },
 				.bvalid_det_clr = { 0x3028, 3, 2, 0, 3 },
+				.id_det_en	= { 0x3020, 5, 4, 0, 3 },
+				.id_det_st	= { 0x3024, 5, 4, 0, 3 },
+				.id_det_clr	= { 0x3028, 5, 4, 0, 3 },
 				.ls_det_en	= { 0x3020, 0, 0, 0, 1 },
 				.ls_det_st	= { 0x3024, 0, 0, 0, 1 },
 				.ls_det_clr	= { 0x3028, 0, 0, 0, 1 },
 				.utmi_avalid	= { 0x0120, 10, 10, 0, 1 },
 				.utmi_bvalid	= { 0x0120, 9, 9, 0, 1 },
+				.utmi_id	= { 0x0120, 6, 6, 0, 1 },
 				.utmi_ls	= { 0x0120, 5, 4, 0, 1 },
 			},
 			[USB2PHY_PORT_HOST] = {
@@ -1401,11 +1470,15 @@ static const struct rockchip_usb2phy_cfg
 				.bvalid_det_en	= { 0x0110, 3, 2, 0, 3 },
 				.bvalid_det_st	= { 0x0114, 3, 2, 0, 3 },
 				.bvalid_det_clr = { 0x0118, 3, 2, 0, 3 },
+				.id_det_en	= { 0x0110, 5, 4, 0, 3 },
+				.id_det_st	= { 0x0114, 5, 4, 0, 3 },
+				.id_det_clr	= { 0x0118, 5, 4, 0, 3 },
 				.ls_det_en	= { 0x0110, 0, 0, 0, 1 },
 				.ls_det_st	= { 0x0114, 0, 0, 0, 1 },
 				.ls_det_clr	= { 0x0118, 0, 0, 0, 1 },
 				.utmi_avalid	= { 0x0120, 10, 10, 0, 1 },
 				.utmi_bvalid	= { 0x0120, 9, 9, 0, 1 },
+				.utmi_id	= { 0x0120, 6, 6, 0, 1 },
 				.utmi_ls	= { 0x0120, 5, 4, 0, 1 },
 			},
 			[USB2PHY_PORT_HOST] = {
@@ -1463,8 +1536,12 @@ static const struct rockchip_usb2phy_cfg
 				.bvalid_det_en	= { 0xe3c0, 3, 3, 0, 1 },
 				.bvalid_det_st	= { 0xe3e0, 3, 3, 0, 1 },
 				.bvalid_det_clr	= { 0xe3d0, 3, 3, 0, 1 },
+				.id_det_en	= { 0xe3c0, 5, 4, 0, 3 },
+				.id_det_st	= { 0xe3e0, 5, 4, 0, 3 },
+				.id_det_clr	= { 0xe3d0, 5, 4, 0, 3 },
 				.utmi_avalid	= { 0xe2ac, 7, 7, 0, 1 },
 				.utmi_bvalid	= { 0xe2ac, 12, 12, 0, 1 },
+				.utmi_id	= { 0xe2ac, 8, 8, 0, 1 },
 			},
 			[USB2PHY_PORT_HOST] = {
 				.phy_sus	= { 0xe458, 1, 0, 0x2, 0x1 },
@@ -1498,8 +1575,12 @@ static const struct rockchip_usb2phy_cfg
 				.bvalid_det_en  = { 0xe3c0, 8, 8, 0, 1 },
 				.bvalid_det_st  = { 0xe3e0, 8, 8, 0, 1 },
 				.bvalid_det_clr = { 0xe3d0, 8, 8, 0, 1 },
+				.id_det_en	= { 0xe3c0, 10, 9, 0, 3 },
+				.id_det_st	= { 0xe3e0, 10, 9, 0, 3 },
+				.id_det_clr	= { 0xe3d0, 10, 9, 0, 3 },
 				.utmi_avalid	= { 0xe2ac, 10, 10, 0, 1 },
 				.utmi_bvalid    = { 0xe2ac, 16, 16, 0, 1 },
+				.utmi_id	= { 0xe2ac, 11, 11, 0, 1 },
 			},
 			[USB2PHY_PORT_HOST] = {
 				.phy_sus	= { 0xe468, 1, 0, 0x2, 0x1 },
@@ -1525,8 +1606,12 @@ static const struct rockchip_usb2phy_cfg
 				.bvalid_det_en	= { 0x0080, 3, 2, 0, 3 },
 				.bvalid_det_st	= { 0x0084, 3, 2, 0, 3 },
 				.bvalid_det_clr = { 0x0088, 3, 2, 0, 3 },
+				.id_det_en	= { 0x0080, 5, 4, 0, 3 },
+				.id_det_st	= { 0x0084, 5, 4, 0, 3 },
+				.id_det_clr	= { 0x0088, 5, 4, 0, 3 },
 				.utmi_avalid	= { 0x00c0, 10, 10, 0, 1 },
 				.utmi_bvalid	= { 0x00c0, 9, 9, 0, 1 },
+				.utmi_id	= { 0x00c0, 6, 6, 0, 1 },
 			},
 			[USB2PHY_PORT_HOST] = {
 				/* Select suspend control from controller */
