phy: phy-rockchip-inno-usb2: add rk3588 support
authorSebastian Reichel <sebastian.reichel@collabora.com>
Mon, 22 May 2023 17:03:19 +0000 (19:03 +0200)
committerVinod Koul <vkoul@kernel.org>
Wed, 12 Jul 2023 16:57:40 +0000 (22:27 +0530)
Add basic support for the USB2 PHY found in the Rockchip RK3588.

Co-developed-by: William Wu <william.wu@rock-chips.com>
Signed-off-by: William Wu <william.wu@rock-chips.com>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Link: https://lore.kernel.org/r/20230522170324.61349-3-sebastian.reichel@collabora.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
drivers/phy/rockchip/phy-rockchip-inno-usb2.c

index a0bc10a..2c4683c 100644 (file)
@@ -116,6 +116,12 @@ 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.
+ * @disfall_en: host disconnect fall edge detection enable.
+ * @disfall_st: host disconnect fall edge detection state.
+ * @disfall_clr: host disconnect fall edge detection clear.
+ * @disrise_en: host disconnect rise edge detection enable.
+ * @disrise_st: host disconnect rise edge detection state.
+ * @disrise_clr: host disconnect rise edge detection clear.
  * @id_det_en: id detection enable register.
  * @id_det_st: id detection state register.
  * @id_det_clr: id detection clear register.
@@ -133,6 +139,12 @@ 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      disfall_en;
+       struct usb2phy_reg      disfall_st;
+       struct usb2phy_reg      disfall_clr;
+       struct usb2phy_reg      disrise_en;
+       struct usb2phy_reg      disrise_st;
+       struct usb2phy_reg      disrise_clr;
        struct usb2phy_reg      id_det_en;
        struct usb2phy_reg      id_det_st;
        struct usb2phy_reg      id_det_clr;
@@ -168,6 +180,7 @@ struct rockchip_usb2phy_cfg {
  * @port_id: flag for otg port or host port.
  * @suspended: phy suspended flag.
  * @vbus_attached: otg device vbus status.
+ * @host_disconnect: usb host disconnect 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.
@@ -187,6 +200,7 @@ struct rockchip_usb2phy_port {
        unsigned int    port_id;
        bool            suspended;
        bool            vbus_attached;
+       bool            host_disconnect;
        int             bvalid_irq;
        int             id_irq;
        int             ls_irq;
@@ -405,6 +419,27 @@ static int rockchip_usb2phy_extcon_register(struct rockchip_usb2phy *rphy)
        return 0;
 }
 
+static int rockchip_usb2phy_enable_host_disc_irq(struct rockchip_usb2phy *rphy,
+                                                struct rockchip_usb2phy_port *rport,
+                                                bool en)
+{
+       int ret;
+
+       ret = property_enable(rphy->grf, &rport->port_cfg->disfall_clr, true);
+       if (ret)
+               return ret;
+
+       ret = property_enable(rphy->grf, &rport->port_cfg->disfall_en, en);
+       if (ret)
+               return ret;
+
+       ret = property_enable(rphy->grf, &rport->port_cfg->disrise_clr, true);
+       if (ret)
+               return ret;
+
+       return property_enable(rphy->grf, &rport->port_cfg->disrise_en, en);
+}
+
 static int rockchip_usb2phy_init(struct phy *phy)
 {
        struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
@@ -449,6 +484,15 @@ static int rockchip_usb2phy_init(struct phy *phy)
                        dev_dbg(&rport->phy->dev, "mode %d\n", rport->mode);
                }
        } else if (rport->port_id == USB2PHY_PORT_HOST) {
+               if (rport->port_cfg->disfall_en.offset) {
+                       rport->host_disconnect = true;
+                       ret = rockchip_usb2phy_enable_host_disc_irq(rphy, rport, true);
+                       if (ret) {
+                               dev_err(rphy->dev, "failed to enable disconnect irq\n");
+                               goto out;
+                       }
+               }
+
                /* clear linestate and enable linestate detect irq */
                ret = property_enable(rphy->grf,
                                      &rport->port_cfg->ls_det_clr, true);
@@ -810,9 +854,7 @@ static void rockchip_usb2phy_sm_work(struct work_struct *work)
        struct rockchip_usb2phy_port *rport =
                container_of(work, struct rockchip_usb2phy_port, sm_work.work);
        struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
-       unsigned int sh = rport->port_cfg->utmi_hstdet.bitend -
-                         rport->port_cfg->utmi_hstdet.bitstart + 1;
-       unsigned int ul, uhd, state;
+       unsigned int sh, ul, uhd, state;
        unsigned int ul_mask, uhd_mask;
        int ret;
 
@@ -822,18 +864,26 @@ static void rockchip_usb2phy_sm_work(struct work_struct *work)
        if (ret < 0)
                goto next_schedule;
 
-       ret = regmap_read(rphy->grf, rport->port_cfg->utmi_hstdet.offset, &uhd);
-       if (ret < 0)
-               goto next_schedule;
-
-       uhd_mask = GENMASK(rport->port_cfg->utmi_hstdet.bitend,
-                          rport->port_cfg->utmi_hstdet.bitstart);
        ul_mask = GENMASK(rport->port_cfg->utmi_ls.bitend,
                          rport->port_cfg->utmi_ls.bitstart);
 
-       /* stitch on utmi_ls and utmi_hstdet as phy state */
-       state = ((uhd & uhd_mask) >> rport->port_cfg->utmi_hstdet.bitstart) |
-               (((ul & ul_mask) >> rport->port_cfg->utmi_ls.bitstart) << sh);
+       if (rport->port_cfg->utmi_hstdet.offset) {
+               ret = regmap_read(rphy->grf, rport->port_cfg->utmi_hstdet.offset, &uhd);
+               if (ret < 0)
+                       goto next_schedule;
+
+               uhd_mask = GENMASK(rport->port_cfg->utmi_hstdet.bitend,
+                                  rport->port_cfg->utmi_hstdet.bitstart);
+
+               sh = rport->port_cfg->utmi_hstdet.bitend -
+                    rport->port_cfg->utmi_hstdet.bitstart + 1;
+               /* stitch on utmi_ls and utmi_hstdet as phy state */
+               state = ((uhd & uhd_mask) >> rport->port_cfg->utmi_hstdet.bitstart) |
+                       (((ul & ul_mask) >> rport->port_cfg->utmi_ls.bitstart) << sh);
+       } else {
+               state = ((ul & ul_mask) >> rport->port_cfg->utmi_ls.bitstart) << 1 |
+                       rport->host_disconnect;
+       }
 
        switch (state) {
        case PHY_STATE_HS_ONLINE:
@@ -966,6 +1016,31 @@ static irqreturn_t rockchip_usb2phy_otg_mux_irq(int irq, void *data)
        return ret;
 }
 
+static irqreturn_t rockchip_usb2phy_host_disc_irq(int irq, void *data)
+{
+       struct rockchip_usb2phy_port *rport = data;
+       struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
+
+       if (!property_enabled(rphy->grf, &rport->port_cfg->disfall_st) &&
+           !property_enabled(rphy->grf, &rport->port_cfg->disrise_st))
+               return IRQ_NONE;
+
+       mutex_lock(&rport->mutex);
+
+       /* clear disconnect fall or rise detect irq pending status */
+       if (property_enabled(rphy->grf, &rport->port_cfg->disfall_st)) {
+               property_enable(rphy->grf, &rport->port_cfg->disfall_clr, true);
+               rport->host_disconnect = false;
+       } else if (property_enabled(rphy->grf, &rport->port_cfg->disrise_st)) {
+               property_enable(rphy->grf, &rport->port_cfg->disrise_clr, true);
+               rport->host_disconnect = true;
+       }
+
+       mutex_unlock(&rport->mutex);
+
+       return IRQ_HANDLED;
+}
+
 static irqreturn_t rockchip_usb2phy_irq(int irq, void *data)
 {
        struct rockchip_usb2phy *rphy = data;
@@ -978,6 +1053,10 @@ static irqreturn_t rockchip_usb2phy_irq(int irq, void *data)
                if (!rport->phy)
                        continue;
 
+               if (rport->port_id == USB2PHY_PORT_HOST &&
+                   rport->port_cfg->disfall_en.offset)
+                       ret |= rockchip_usb2phy_host_disc_irq(irq, rport);
+
                switch (rport->port_id) {
                case USB2PHY_PORT_OTG:
                        if (rport->mode != USB_DR_MODE_HOST &&
@@ -1233,7 +1312,7 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
        }
 
        /* support address_cells=2 */
-       if (reg == 0) {
+       if (of_property_count_u32_elems(np, "reg") > 2 && reg == 0) {
                if (of_property_read_u32_index(np, "reg", 1, &reg)) {
                        dev_err(dev, "the reg property is not assigned in %pOFn node\n",
                                np);
@@ -1254,14 +1333,14 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
 
        /* find out a proper config which can be matched with dt. */
        index = 0;
-       while (phy_cfgs[index].reg) {
+       do {
                if (phy_cfgs[index].reg == reg) {
                        rphy->phy_cfg = &phy_cfgs[index];
                        break;
                }
 
                ++index;
-       }
+       } while (phy_cfgs[index].reg);
 
        if (!rphy->phy_cfg) {
                dev_err(dev, "no phy-config can be matched with %pOFn node\n",
@@ -1664,6 +1743,122 @@ static const struct rockchip_usb2phy_cfg rk3568_phy_cfgs[] = {
        { /* sentinel */ }
 };
 
+static const struct rockchip_usb2phy_cfg rk3588_phy_cfgs[] = {
+       {
+               .reg = 0x0000,
+               .num_ports      = 1,
+               .clkout_ctl     = { 0x0000, 0, 0, 1, 0 },
+               .port_cfgs      = {
+                       [USB2PHY_PORT_OTG] = {
+                               .phy_sus        = { 0x000c, 11, 11, 0, 1 },
+                               .bvalid_det_en  = { 0x0080, 1, 1, 0, 1 },
+                               .bvalid_det_st  = { 0x0084, 1, 1, 0, 1 },
+                               .bvalid_det_clr = { 0x0088, 1, 1, 0, 1 },
+                               .ls_det_en      = { 0x0080, 0, 0, 0, 1 },
+                               .ls_det_st      = { 0x0084, 0, 0, 0, 1 },
+                               .ls_det_clr     = { 0x0088, 0, 0, 0, 1 },
+                               .disfall_en     = { 0x0080, 6, 6, 0, 1 },
+                               .disfall_st     = { 0x0084, 6, 6, 0, 1 },
+                               .disfall_clr    = { 0x0088, 6, 6, 0, 1 },
+                               .disrise_en     = { 0x0080, 5, 5, 0, 1 },
+                               .disrise_st     = { 0x0084, 5, 5, 0, 1 },
+                               .disrise_clr    = { 0x0088, 5, 5, 0, 1 },
+                               .utmi_avalid    = { 0x00c0, 7, 7, 0, 1 },
+                               .utmi_bvalid    = { 0x00c0, 6, 6, 0, 1 },
+                               .utmi_ls        = { 0x00c0, 10, 9, 0, 1 },
+                       }
+               },
+               .chg_det = {
+                       .cp_det         = { 0x00c0, 0, 0, 0, 1 },
+                       .dcp_det        = { 0x00c0, 0, 0, 0, 1 },
+                       .dp_det         = { 0x00c0, 1, 1, 1, 0 },
+                       .idm_sink_en    = { 0x0008, 5, 5, 1, 0 },
+                       .idp_sink_en    = { 0x0008, 5, 5, 0, 1 },
+                       .idp_src_en     = { 0x0008, 14, 14, 0, 1 },
+                       .rdm_pdwn_en    = { 0x0008, 14, 14, 0, 1 },
+                       .vdm_src_en     = { 0x0008, 7, 6, 0, 3 },
+                       .vdp_src_en     = { 0x0008, 7, 6, 0, 3 },
+               },
+       },
+       {
+               .reg = 0x4000,
+               .num_ports      = 1,
+               .clkout_ctl     = { 0x0000, 0, 0, 1, 0 },
+               .port_cfgs      = {
+                       [USB2PHY_PORT_OTG] = {
+                               .phy_sus        = { 0x000c, 11, 11, 0, 1 },
+                               .bvalid_det_en  = { 0x0080, 1, 1, 0, 1 },
+                               .bvalid_det_st  = { 0x0084, 1, 1, 0, 1 },
+                               .bvalid_det_clr = { 0x0088, 1, 1, 0, 1 },
+                               .ls_det_en      = { 0x0080, 0, 0, 0, 1 },
+                               .ls_det_st      = { 0x0084, 0, 0, 0, 1 },
+                               .ls_det_clr     = { 0x0088, 0, 0, 0, 1 },
+                               .disfall_en     = { 0x0080, 6, 6, 0, 1 },
+                               .disfall_st     = { 0x0084, 6, 6, 0, 1 },
+                               .disfall_clr    = { 0x0088, 6, 6, 0, 1 },
+                               .disrise_en     = { 0x0080, 5, 5, 0, 1 },
+                               .disrise_st     = { 0x0084, 5, 5, 0, 1 },
+                               .disrise_clr    = { 0x0088, 5, 5, 0, 1 },
+                               .utmi_avalid    = { 0x00c0, 7, 7, 0, 1 },
+                               .utmi_bvalid    = { 0x00c0, 6, 6, 0, 1 },
+                               .utmi_ls        = { 0x00c0, 10, 9, 0, 1 },
+                       }
+               },
+               .chg_det = {
+                       .cp_det         = { 0x00c0, 0, 0, 0, 1 },
+                       .dcp_det        = { 0x00c0, 0, 0, 0, 1 },
+                       .dp_det         = { 0x00c0, 1, 1, 1, 0 },
+                       .idm_sink_en    = { 0x0008, 5, 5, 1, 0 },
+                       .idp_sink_en    = { 0x0008, 5, 5, 0, 1 },
+                       .idp_src_en     = { 0x0008, 14, 14, 0, 1 },
+                       .rdm_pdwn_en    = { 0x0008, 14, 14, 0, 1 },
+                       .vdm_src_en     = { 0x0008, 7, 6, 0, 3 },
+                       .vdp_src_en     = { 0x0008, 7, 6, 0, 3 },
+               },
+       },
+       {
+               .reg = 0x8000,
+               .num_ports      = 1,
+               .clkout_ctl     = { 0x0000, 0, 0, 1, 0 },
+               .port_cfgs      = {
+                       [USB2PHY_PORT_HOST] = {
+                               .phy_sus        = { 0x0008, 2, 2, 0, 1 },
+                               .ls_det_en      = { 0x0080, 0, 0, 0, 1 },
+                               .ls_det_st      = { 0x0084, 0, 0, 0, 1 },
+                               .ls_det_clr     = { 0x0088, 0, 0, 0, 1 },
+                               .disfall_en     = { 0x0080, 6, 6, 0, 1 },
+                               .disfall_st     = { 0x0084, 6, 6, 0, 1 },
+                               .disfall_clr    = { 0x0088, 6, 6, 0, 1 },
+                               .disrise_en     = { 0x0080, 5, 5, 0, 1 },
+                               .disrise_st     = { 0x0084, 5, 5, 0, 1 },
+                               .disrise_clr    = { 0x0088, 5, 5, 0, 1 },
+                               .utmi_ls        = { 0x00c0, 10, 9, 0, 1 },
+                       }
+               },
+       },
+       {
+               .reg = 0xc000,
+               .num_ports      = 1,
+               .clkout_ctl     = { 0x0000, 0, 0, 1, 0 },
+               .port_cfgs      = {
+                       [USB2PHY_PORT_HOST] = {
+                               .phy_sus        = { 0x0008, 2, 2, 0, 1 },
+                               .ls_det_en      = { 0x0080, 0, 0, 0, 1 },
+                               .ls_det_st      = { 0x0084, 0, 0, 0, 1 },
+                               .ls_det_clr     = { 0x0088, 0, 0, 0, 1 },
+                               .disfall_en     = { 0x0080, 6, 6, 0, 1 },
+                               .disfall_st     = { 0x0084, 6, 6, 0, 1 },
+                               .disfall_clr    = { 0x0088, 6, 6, 0, 1 },
+                               .disrise_en     = { 0x0080, 5, 5, 0, 1 },
+                               .disrise_st     = { 0x0084, 5, 5, 0, 1 },
+                               .disrise_clr    = { 0x0088, 5, 5, 0, 1 },
+                               .utmi_ls        = { 0x00c0, 10, 9, 0, 1 },
+                       }
+               },
+       },
+       { /* sentinel */ }
+};
+
 static const struct rockchip_usb2phy_cfg rv1108_phy_cfgs[] = {
        {
                .reg = 0x100,
@@ -1714,6 +1909,7 @@ static const struct of_device_id rockchip_usb2phy_dt_match[] = {
        { .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs },
        { .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs },
        { .compatible = "rockchip,rk3568-usb2phy", .data = &rk3568_phy_cfgs },
+       { .compatible = "rockchip,rk3588-usb2phy", .data = &rk3588_phy_cfgs },
        { .compatible = "rockchip,rv1108-usb2phy", .data = &rv1108_phy_cfgs },
        {}
 };