355 lines
12 KiB
Diff
355 lines
12 KiB
Diff
From 33a76663fe1d1bf177214a88fdbf85d94f49d40c Mon Sep 17 00:00:00 2001
|
|
From: Sebastian Reichel <sebastian.reichel@collabora.com>
|
|
Date: Thu, 12 Jan 2023 19:15:52 +0100
|
|
Subject: [PATCH 415/469] phy: phy-rockchip-inno-usb2: add rk3588 support
|
|
|
|
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>
|
|
---
|
|
drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 226 ++++++++++++++++--
|
|
1 file changed, 211 insertions(+), 15 deletions(-)
|
|
|
|
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
|
|
index a0bc10aa7961..2c4683c67a8e 100644
|
|
--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
|
|
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
|
|
@@ -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, ®)) {
|
|
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 },
|
|
{}
|
|
};
|
|
--
|
|
2.34.1
|
|
|