// SPDX-License-Identifier: GPL-2.0 /* * Renesas RCar Gen2 USB PHY driver * * Copyright (C) 2018 Marek Vasut */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USBHS_LPSTS 0x02 #define USBHS_UGCTRL 0x80 #define USBHS_UGCTRL2 0x84 #define USBHS_UGSTS 0x88 /* From technical update */ /* Low Power Status register (LPSTS) */ #define USBHS_LPSTS_SUSPM 0x4000 /* USB General control register (UGCTRL) */ #define USBHS_UGCTRL_CONNECT BIT(2) #define USBHS_UGCTRL_PLLRESET BIT(0) /* USB General control register 2 (UGCTRL2) */ #define USBHS_UGCTRL2_USB2SEL 0x80000000 #define USBHS_UGCTRL2_USB2SEL_PCI 0x00000000 #define USBHS_UGCTRL2_USB2SEL_USB30 0x80000000 #define USBHS_UGCTRL2_USB0SEL 0x00000030 #define USBHS_UGCTRL2_USB0SEL_PCI 0x00000010 #define USBHS_UGCTRL2_USB0SEL_HS_USB 0x00000030 /* USB General status register (UGSTS) */ #define USBHS_UGSTS_LOCK 0x00000100 /* From technical update */ #define PHYS_PER_CHANNEL 2 struct rcar_gen2_phy { fdt_addr_t regs; struct clk clk; }; static int rcar_gen2_phy_phy_init(struct phy *phy) { struct rcar_gen2_phy *priv = dev_get_priv(phy->dev); u16 chan = phy->id & 0xffff; u16 mode = (phy->id >> 16) & 0xffff; u32 clrmask, setmask; if (chan == 0) { clrmask = USBHS_UGCTRL2_USB0SEL; setmask = mode ? USBHS_UGCTRL2_USB0SEL_HS_USB : USBHS_UGCTRL2_USB0SEL_PCI; } else { clrmask = USBHS_UGCTRL2_USB2SEL; setmask = mode ? USBHS_UGCTRL2_USB2SEL_USB30 : USBHS_UGCTRL2_USB2SEL_PCI; } clrsetbits_le32(priv->regs + USBHS_UGCTRL2, clrmask, setmask); return 0; } static int rcar_gen2_phy_phy_power_on(struct phy *phy) { struct rcar_gen2_phy *priv = dev_get_priv(phy->dev); int i; u32 value; /* Power on USBHS PHY */ clrbits_le32(priv->regs + USBHS_UGCTRL, USBHS_UGCTRL_PLLRESET); setbits_le16(priv->regs + USBHS_LPSTS, USBHS_LPSTS_SUSPM); for (i = 0; i < 20; i++) { value = readl(priv->regs + USBHS_UGSTS); if ((value & USBHS_UGSTS_LOCK) == USBHS_UGSTS_LOCK) { setbits_le32(priv->regs + USBHS_UGCTRL, USBHS_UGCTRL_CONNECT); return 0; } udelay(1); } return -ETIMEDOUT; } static int rcar_gen2_phy_phy_power_off(struct phy *phy) { struct rcar_gen2_phy *priv = dev_get_priv(phy->dev); /* Power off USBHS PHY */ clrbits_le32(priv->regs + USBHS_UGCTRL, USBHS_UGCTRL_CONNECT); clrbits_le16(priv->regs + USBHS_LPSTS, USBHS_LPSTS_SUSPM); setbits_le32(priv->regs + USBHS_UGCTRL, USBHS_UGCTRL_PLLRESET); return 0; } static int rcar_gen2_phy_of_xlate(struct phy *phy, struct ofnode_phandle_args *args) { if (args->args_count != 2) { dev_err(phy->dev, "Invalid DT PHY argument count: %d\n", args->args_count); return -EINVAL; } if (args->args[0] != 0 && args->args[0] != 2) { dev_err(phy->dev, "Invalid DT PHY channel: %d\n", args->args[0]); return -EINVAL; } if (args->args[1] != 0 && args->args[1] != 1) { dev_err(phy->dev, "Invalid DT PHY mode: %d\n", args->args[1]); return -EINVAL; } if (args->args_count) phy->id = args->args[0] | (args->args[1] << 16); else phy->id = 0; return 0; } static const struct phy_ops rcar_gen2_phy_phy_ops = { .init = rcar_gen2_phy_phy_init, .power_on = rcar_gen2_phy_phy_power_on, .power_off = rcar_gen2_phy_phy_power_off, .of_xlate = rcar_gen2_phy_of_xlate, }; static int rcar_gen2_phy_probe(struct udevice *dev) { struct rcar_gen2_phy *priv = dev_get_priv(dev); int ret; priv->regs = dev_read_addr(dev); if (priv->regs == FDT_ADDR_T_NONE) return -EINVAL; /* Enable clock */ ret = clk_get_by_index(dev, 0, &priv->clk); if (ret) return ret; ret = clk_enable(&priv->clk); if (ret) return ret; return 0; } static int rcar_gen2_phy_remove(struct udevice *dev) { struct rcar_gen2_phy *priv = dev_get_priv(dev); clk_disable(&priv->clk); clk_free(&priv->clk); return 0; } static const struct udevice_id rcar_gen2_phy_of_match[] = { { .compatible = "renesas,rcar-gen2-usb-phy", }, { }, }; U_BOOT_DRIVER(rcar_gen2_phy) = { .name = "rcar-gen2-phy", .id = UCLASS_PHY, .of_match = rcar_gen2_phy_of_match, .ops = &rcar_gen2_phy_phy_ops, .probe = rcar_gen2_phy_probe, .remove = rcar_gen2_phy_remove, .priv_auto = sizeof(struct rcar_gen2_phy), };