Commit 3a7db8e9 authored by Sebastian Reichel's avatar Sebastian Reichel Committed by Vinod Koul

phy: phy-rockchip-inno-usb2: add rk3588 support

Add basic support for the USB2 PHY found in the Rockchip RK3588.
Co-developed-by: default avatarWilliam Wu <william.wu@rock-chips.com>
Signed-off-by: default avatarWilliam Wu <william.wu@rock-chips.com>
Signed-off-by: default avatarSebastian Reichel <sebastian.reichel@collabora.com>
Link: https://lore.kernel.org/r/20230522170324.61349-3-sebastian.reichel@collabora.comSigned-off-by: default avatarVinod Koul <vkoul@kernel.org>
parent cb240921
......@@ -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;
ul_mask = GENMASK(rport->port_cfg->utmi_ls.bitend,
rport->port_cfg->utmi_ls.bitstart);
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);
ul_mask = GENMASK(rport->port_cfg->utmi_ls.bitend,
rport->port_cfg->utmi_ls.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 },
{}
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment