diff options
| author | Tom Rini <trini@konsulko.com> | 2018-08-17 07:25:04 -0400 | 
|---|---|---|
| committer | Tom Rini <trini@konsulko.com> | 2018-08-17 07:25:04 -0400 | 
| commit | 8297e648b82197b7b543b0cc702983fcf98fe82c (patch) | |
| tree | 1321a58229aeae0342ad39962648680bad73807a /drivers/phy/phy-rcar-gen2.c | |
| parent | 719afeb0b3c60af82f701f122978b935aa6a5217 (diff) | |
| parent | 4f10989280bb91f0981ffe2ffabe936bb2a92364 (diff) | |
Merge branch 'master' of git://git.denx.de/u-boot-sh
Diffstat (limited to 'drivers/phy/phy-rcar-gen2.c')
| -rw-r--r-- | drivers/phy/phy-rcar-gen2.c | 190 | 
1 files changed, 190 insertions, 0 deletions
| diff --git a/drivers/phy/phy-rcar-gen2.c b/drivers/phy/phy-rcar-gen2.c new file mode 100644 index 00000000000..ee70b81d882 --- /dev/null +++ b/drivers/phy/phy-rcar-gen2.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas RCar Gen2 USB PHY driver + * + * Copyright (C) 2018 Marek Vasut <marek.vasut@gmail.com> + */ + +#include <common.h> +#include <clk.h> +#include <div64.h> +#include <dm.h> +#include <fdtdec.h> +#include <generic-phy.h> +#include <reset.h> +#include <syscon.h> +#include <usb.h> +#include <asm/io.h> +#include <linux/bitops.h> +#include <power/regulator.h> + +#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_alloc_size = sizeof(struct rcar_gen2_phy), +}; | 
