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

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.com


Signed-off-by: default avatarVinod Koul <vkoul@kernel.org>
parent cb240921
Loading
Loading
Loading
Loading
+211 −15
Original line number Diff line number Diff line
@@ -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 },
	{}
};