The USB30PHY of a RCAR-Gen3 XHCI device can select the reference clock source. The 2 available options are the differential input clock pair supplied on pins USB3S0_CLK_P / USB3S0_CLK_M (default) and the on-chip clock source supplied through USB_XTAL/USB_EXTAL. The device can be configured to use the on-chip source by adding the "renesas,use-on-chip-clk" option in the corresponding device tree node. Signed-off-by: Petre Pircalabu <Petre_Pircalabu@xxxxxxxxxx> --- .../devicetree/bindings/phy/rcar-gen3-phy-usb3.txt | 22 +++ arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi | 8 + arch/arm64/configs/defconfig | 1 + drivers/phy/Kconfig | 8 + drivers/phy/Makefile | 1 + drivers/phy/phy-rcar-gen3-usb3.c | 166 +++++++++++++++++++++ 6 files changed, 206 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb3.txt create mode 100644 drivers/phy/phy-rcar-gen3-usb3.c diff --git a/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb3.txt b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb3.txt new file mode 100644 index 0000000..aa9657c --- /dev/null +++ b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb3.txt @@ -0,0 +1,22 @@ +* Renesas R-Car generation 3 USB 3.0 PHY + +This file provides information on what the device node for the R-Car generation +3 USB 3.0 PHY contains. + +Required properties: +- compatible: "renesas,rcar-gen3-usb3-phy" for a generic R-Car Gen3 compatible device. +- reg: offset and length of the partial USB 3.0 Host PHY register block. +- #phy-cells: see phy-bindings.txt in the same directory, must be <0>. + +Optional properties: + +Example (R-Car H3): + + usb3_phy0: usb-phy@e65ee000 { + compatible = "renesas,rcar-gen3-usb3-phy"; + reg = <0 0xe65ee000 0 0x100>; + power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; + #phy-cells = <0>; + renesas,use-on-chip-clk; + status = "okay"; + }; diff --git a/arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi b/arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi index 2bf5911..33fe114 100644 --- a/arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi @@ -2499,6 +2499,14 @@ status = "disabled"; }; + usb3_phy0: usb-phy@e65ee000 { + compatible = "renesas,rcar-gen3-usb3-phy"; + reg = <0 0xe65ee000 0 0x100>; + power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; + #phy-cells = <0>; + status = "disabled"; + }; + ehci0: usb@ee080100 { compatible = "generic-ehci"; reg = <0 0xee080100 0 0x100>; diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 4e030c8..df9ca01 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -441,6 +441,7 @@ CONFIG_PWM=y CONFIG_PWM_TEGRA=m CONFIG_COMMON_RESET_HI6220=y CONFIG_PHY_RCAR_GEN3_USB2=y +CONFIG_PHY_RCAR_GEN3_USB3=y CONFIG_PHY_HI6220_USB=y CONFIG_PHY_XGENE=y CONFIG_PHY_TEGRA_XUSB=y diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index fe00f91..4c8a207 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -154,6 +154,14 @@ config PHY_RCAR_GEN3_USB2 help Support for USB 2.0 PHY found on Renesas R-Car generation 3 SoCs. +config PHY_RCAR_GEN3_USB3 + tristate "Renesas R-Car generation 3 USB 3.0 PHY driver" + depends on ARCH_RENESAS + select GENERIC_PHY + help + Enable this to add support for the USB 3.0 PHY found on + Renesas R-Car generation 3 SoCs. + config OMAP_CONTROL_PHY tristate "OMAP CONTROL PHY Driver" depends on ARCH_OMAP2PLUS || COMPILE_TEST diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index a534cf5..aeda949 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_PHY_MIPHY28LP) += phy-miphy28lp.o obj-$(CONFIG_PHY_MIPHY365X) += phy-miphy365x.o obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o obj-$(CONFIG_PHY_RCAR_GEN3_USB2) += phy-rcar-gen3-usb2.o +obj-$(CONFIG_PHY_RCAR_GEN3_USB3) += phy-rcar-gen3-usb3.o obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o diff --git a/drivers/phy/phy-rcar-gen3-usb3.c b/drivers/phy/phy-rcar-gen3-usb3.c new file mode 100644 index 0000000..87fa24d --- /dev/null +++ b/drivers/phy/phy-rcar-gen3-usb3.c @@ -0,0 +1,166 @@ +/* + * Renesas R-Car Gen3 for USB3.0 PHY driver + * + * Copyright (C) 2017 Mentor Graphics Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> + +#define USB30_CLKSET0 0x34 +#define USB30_CLKSET1 0x36 + +#define USB30_CLKSET0_FSEL_MASK 0x003F +#define USB30_CLKSET0_FSEL_USB_XTAL 0x0002 +#define USB30_CLKSET0_FSEL_USB3S0_CLK 0x0027 + +#define USB30_CLKSET1_PLL_MULTI_MASK 0x1FC0 +#define USB30_CLKSET1_PLL_MULTI_USB_XTAL (0x64 << 6) +#define USB30_CLKSET1_PLL_MULTI_USB3S0_CLK (0x00 << 6) +#define USB30_CLKSET1_REF_CLKDIV BIT(3) +#define USB30_CLKSET1_USB_SEL BIT(0) + +struct rcar_gen3_usb3_phy { + void __iomem *base; + struct phy *phy; + u8 use_on_chip_clk; +}; + +static int rcar_gen3_phy_usb3_init(struct phy *p) +{ + struct rcar_gen3_usb3_phy *phy_dev = phy_get_drvdata(p); + void __iomem *usb3_base = phy_dev->base; + + u16 clkset0, clkset1; + + clkset0 = readw(usb3_base + USB30_CLKSET0); + clkset1 = readw(usb3_base + USB30_CLKSET1); + + dev_dbg(&p->dev, "USB30_CLKSET0 initial value = 0x%04X\n", clkset0); + dev_dbg(&p->dev, "USB30_CLKSET1 initial value = 0x%04X\n", clkset1); + + clkset0 &= ~USB30_CLKSET0_FSEL_MASK; + clkset1 &= ~(USB30_CLKSET1_PLL_MULTI_MASK | USB30_CLKSET1_REF_CLKDIV | + USB30_CLKSET1_USB_SEL); + + if (phy_dev->use_on_chip_clk) { + /* Select 50MHz clock */ + dev_info(&p->dev, "USE USB_XTAL clock (50MHz)\n"); + clkset0 |= USB30_CLKSET0_FSEL_USB_XTAL; + clkset1 |= USB30_CLKSET1_PLL_MULTI_USB_XTAL | + USB30_CLKSET1_REF_CLKDIV; + clkset1 &= ~USB30_CLKSET1_USB_SEL; + } else { + /* Select 100MHz clock */ + dev_info(&p->dev, "USE USB3S0_CLK reference (100MHz)\n"); + clkset0 |= USB30_CLKSET0_FSEL_USB3S0_CLK; + clkset1 |= USB30_CLKSET1_PLL_MULTI_USB3S0_CLK | + USB30_CLKSET1_USB_SEL; + clkset1 &= ~USB30_CLKSET1_REF_CLKDIV; + } + + dev_dbg(&p->dev, "USB30_CLKSET0 new value = 0x%04X\n", clkset0); + dev_dbg(&p->dev, "USB30_CLKSET1 new value = 0x%04X\n", clkset1); + + writew(clkset0, usb3_base + USB30_CLKSET0); + writew(clkset1, usb3_base + USB30_CLKSET1); + + return 0; +} + +static int rcar_gen3_phy_usb3_exit(struct phy *p) +{ + return 0; +} + + +static struct phy_ops rcar_gen3_phy_usb3_ops = { + .init = rcar_gen3_phy_usb3_init, + .exit = rcar_gen3_phy_usb3_exit, + .owner = THIS_MODULE, +}; + +static const struct of_device_id rcar_gen3_phy_usb3_match_table[] = { + { .compatible = "renesas,rcar-gen3-usb3-phy" }, + { } +}; +MODULE_DEVICE_TABLE(of, rcar_gen3_phy_usb3_match_table); + +static int rcar_gen3_phy_usb3_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rcar_gen3_usb3_phy *phy_dev; + struct phy_provider *provider; + struct resource *res; + + if (!dev->of_node) { + dev_err(dev, "This driver needs a device tree node\n"); + return -EINVAL; + } + + phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL); + if (!phy_dev) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + phy_dev->base = devm_ioremap_resource(dev, res); + if (IS_ERR(phy_dev->base)) + return PTR_ERR(phy_dev->base); + + phy_dev->use_on_chip_clk = device_property_read_bool(dev, + "renesas,use-on-chip-clk"); + + pm_runtime_enable(dev); + phy_dev->phy = devm_phy_create(dev, NULL, &rcar_gen3_phy_usb3_ops); + if (IS_ERR(phy_dev->phy)) { + dev_err(dev, "Failed to create Rcar Gen3 USB3 PHY\n"); + return PTR_ERR(phy_dev->phy); + } + + platform_set_drvdata(pdev, phy_dev); + phy_set_drvdata(phy_dev->phy, phy_dev); + + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(provider)) { + dev_err(dev, "Failed to register PHY provider\n"); + return PTR_ERR(provider); + } + + dev_info(&pdev->dev, "Initialized RCAR Gen3 USB3 PHY module\n"); + return 0; +} + +static int rcar_gen3_phy_usb3_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + return 0; +} + +static struct platform_driver rcar_gen3_phy_usb3_driver = { + .driver = { + .name = "phy_rcar_gen3_usb3", + .of_match_table = rcar_gen3_phy_usb3_match_table, + }, + .probe = rcar_gen3_phy_usb3_probe, + .remove = rcar_gen3_phy_usb3_remove, +}; +module_platform_driver(rcar_gen3_phy_usb3_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Renesas R-Car Gen3 USB 3.0 PHY"); +MODULE_AUTHOR("Petre Pircalabu <petre_pircalabu@xxxxxxxxxx>"); -- 1.9.1