diff mbox

phy: Renesas R-Car gen3 PCIe PHY driver

Message ID 0f28c74d-f35a-5ccb-eeba-f21ae39c3857@cogentembedded.com (mailing list archive)
State Accepted
Delegated to: Geert Uytterhoeven
Headers show

Commit Message

Sergei Shtylyov April 4, 2018, 7:31 p.m. UTC
This PHY is still mostly undocumented -- the only documented registers
exist on R-Car V3H (R8A77980) SoC  where this PHY stays in a powered-down
state after a reset and thus  we must power it on for PCIe to work...

Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>

---
The patch is against the 'next' branch of Kishon's 'linux-phy.git' repo.

 Documentation/devicetree/bindings/phy/rcar-gen3-phy-pcie.txt |   32 ++
 drivers/phy/renesas/Kconfig                                  |    7 
 drivers/phy/renesas/Makefile                                 |    1 
 drivers/phy/renesas/phy-rcar-gen3-pcie.c                     |  158 +++++++++++
 4 files changed, 198 insertions(+)

Comments

Geert Uytterhoeven April 9, 2018, 9:46 a.m. UTC | #1
Hi Sergei,

Thanks for your patch!

On Wed, Apr 4, 2018 at 9:31 PM, Sergei Shtylyov
<sergei.shtylyov@cogentembedded.com> wrote:
> This PHY is still mostly undocumented -- the only documented registers
> exist on R-Car V3H (R8A77980) SoC  where this PHY stays in a powered-down
> state after a reset and thus  we must power it on for PCIe to work...

Bogus spaces slipping in again?

> Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>

> --- /dev/null
> +++ linux-phy/Documentation/devicetree/bindings/phy/rcar-gen3-phy-pcie.txt
> @@ -0,0 +1,32 @@
> +* Renesas R-Car generation 3 PCIe PHY
> +
> +This file provides information on what the device node for the R-Car
> +generation 3 PCIe PHY contains.
> +
> +Required properties:
> +- compatible: "renesas,r8a77980-pcie-phy" if the device is a part of R8A77980
> +             SoC.
> +             "renesas,rcar-gen3-pcie-phy" for a generic R-Car Gen3 compatible
> +             device.
> +
> +             When compatible with the generic version, nodes must list the
> +             SoC-specific version corresponding to the platform first
> +             followed by the generic version.
> +
> +- reg: offset and length of the register block.
> +- clocks: clock phandle and specifier pair.
> +- power-domains: power domain phandle and specifier pair.
> +- resets: reset phandle and specifier pair.
> +- #phy-cells: see phy-bindings.txt in the same directory, must be <0>.
> +
> +Example (R-Car V3H):
> +
> +       pcie-phy@e65d0000 {
> +               compatible = "renesas,r8a77980-pcie-phy",
> +                            "renesas,rcar-gen3-pcie-phy";

Is the R8A77980 PCIe PHY compatible with the generic version, given it needs
to be powered up explicitly?

The only documented register is the lane reversal register, do we need bindings
to configure it?

> --- /dev/null
> +++ linux-phy/drivers/phy/renesas/phy-rcar-gen3-pcie.c
> @@ -0,0 +1,158 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Renesas R-Car Gen2 PHY driver

Gen3 PCIe PHY

> +static int rcar_gen3_phy_pcie_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct phy_provider *provider;
> +       struct rcar_gen3_phy *phy;
> +       struct resource *res;
> +       void __iomem *base;
> +       int error;
> +
> +       if (!dev->of_node) {
> +               dev_err(dev,
> +                       "This driver must only be instantiated from the device tree\n");
> +               return -EINVAL;
> +       }

Do we need this check? Just go bang, like most other DT-only drivers do?

Gr{oetje,eeting}s,

                        Geert
Sergei Shtylyov April 9, 2018, 3:57 p.m. UTC | #2
On 04/09/2018 12:46 PM, Geert Uytterhoeven wrote:

>> This PHY is still mostly undocumented -- the only documented registers
>> exist on R-Car V3H (R8A77980) SoC  where this PHY stays in a powered-down
>> state after a reset and thus  we must power it on for PCIe to work...
> 
> Bogus spaces slipping in again?

   Yes, I should have worked in the type-setting. :-)

>> Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
> 
>> --- /dev/null
>> +++ linux-phy/Documentation/devicetree/bindings/phy/rcar-gen3-phy-pcie.txt
>> @@ -0,0 +1,32 @@
[...]
>> +Example (R-Car V3H):
>> +
>> +       pcie-phy@e65d0000 {
>> +               compatible = "renesas,r8a77980-pcie-phy",
>> +                            "renesas,rcar-gen3-pcie-phy";
> 
> Is the R8A77980 PCIe PHY compatible with the generic version, given it needs
> to be powered up explicitly?

   I assumed it's upwardly compatible. However, given the lack of information,
it might not be...

> The only documented register is the lane reversal register, do we need bindings
> to configure it?

   Don't know, I'm not PCIe expert...

>> --- /dev/null
>> +++ linux-phy/drivers/phy/renesas/phy-rcar-gen3-pcie.c
>> @@ -0,0 +1,158 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Renesas R-Car Gen2 PHY driver
> 
> Gen3 PCIe PHY

  Yeah, my overlook. Thanks for noticing...

>> +static int rcar_gen3_phy_pcie_probe(struct platform_device *pdev)
>> +{
>> +       struct device *dev = &pdev->dev;
>> +       struct phy_provider *provider;
>> +       struct rcar_gen3_phy *phy;
>> +       struct resource *res;
>> +       void __iomem *base;
>> +       int error;
>> +
>> +       if (!dev->of_node) {
>> +               dev_err(dev,
>> +                       "This driver must only be instantiated from the device tree\n");
>> +               return -EINVAL;
>> +       }
> 
> Do we need this check? Just go bang, like most other DT-only drivers do?

   You mean NULL pointer dereference?

> Gr{oetje,eeting}s,
> 
>                         Geert
> 

MBR, Sergei
Geert Uytterhoeven April 9, 2018, 4:23 p.m. UTC | #3
Hi Sergei,

On Mon, Apr 9, 2018 at 5:57 PM, Sergei Shtylyov
<sergei.shtylyov@cogentembedded.com> wrote:
> On 04/09/2018 12:46 PM, Geert Uytterhoeven wrote:
>>> +static int rcar_gen3_phy_pcie_probe(struct platform_device *pdev)
>>> +{
>>> +       struct device *dev = &pdev->dev;
>>> +       struct phy_provider *provider;
>>> +       struct rcar_gen3_phy *phy;
>>> +       struct resource *res;
>>> +       void __iomem *base;
>>> +       int error;
>>> +
>>> +       if (!dev->of_node) {
>>> +               dev_err(dev,
>>> +                       "This driver must only be instantiated from the device tree\n");
>>> +               return -EINVAL;
>>> +       }
>>
>> Do we need this check? Just go bang, like most other DT-only drivers do?
>
>    You mean NULL pointer dereference?

Yep. (S)he who instantiates a "phy_rcar_gen3_pcie" device without DT is
bound to die.

Gr{oetje,eeting}s,

                        Geert
Simon Horman April 10, 2018, 9:31 a.m. UTC | #4
On Mon, Apr 09, 2018 at 06:57:11PM +0300, Sergei Shtylyov wrote:
> On 04/09/2018 12:46 PM, Geert Uytterhoeven wrote:
> 
> >> This PHY is still mostly undocumented -- the only documented registers
> >> exist on R-Car V3H (R8A77980) SoC  where this PHY stays in a powered-down
> >> state after a reset and thus  we must power it on for PCIe to work...
> > 
> > Bogus spaces slipping in again?
> 
>    Yes, I should have worked in the type-setting. :-)
> 
> >> Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
> > 
> >> --- /dev/null
> >> +++ linux-phy/Documentation/devicetree/bindings/phy/rcar-gen3-phy-pcie.txt
> >> @@ -0,0 +1,32 @@
> [...]
> >> +Example (R-Car V3H):
> >> +
> >> +       pcie-phy@e65d0000 {
> >> +               compatible = "renesas,r8a77980-pcie-phy",
> >> +                            "renesas,rcar-gen3-pcie-phy";
> > 
> > Is the R8A77980 PCIe PHY compatible with the generic version, given it needs
> > to be powered up explicitly?
> 
>    I assumed it's upwardly compatible. However, given the lack of information,
> it might not be...

If we are not sure I'd rather assume that it is not upwardly compatible.
Because falling back to something that doesn't work does not seem at all
useful to me.

...
Rob Herring April 10, 2018, 1:59 p.m. UTC | #5
On Wed, Apr 04, 2018 at 10:31:26PM +0300, Sergei Shtylyov wrote:
> This PHY is still mostly undocumented -- the only documented registers
> exist on R-Car V3H (R8A77980) SoC  where this PHY stays in a powered-down
> state after a reset and thus  we must power it on for PCIe to work...
> 
> Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
> 
> ---
> The patch is against the 'next' branch of Kishon's 'linux-phy.git' repo.
> 
>  Documentation/devicetree/bindings/phy/rcar-gen3-phy-pcie.txt |   32 ++

Separate patch please.

>  drivers/phy/renesas/Kconfig                                  |    7 
>  drivers/phy/renesas/Makefile                                 |    1 
>  drivers/phy/renesas/phy-rcar-gen3-pcie.c                     |  158 +++++++++++
>  4 files changed, 198 insertions(+)
> 
> Index: linux-phy/Documentation/devicetree/bindings/phy/rcar-gen3-phy-pcie.txt
> ===================================================================
> --- /dev/null
> +++ linux-phy/Documentation/devicetree/bindings/phy/rcar-gen3-phy-pcie.txt
> @@ -0,0 +1,32 @@
> +* Renesas R-Car generation 3 PCIe PHY
> +
> +This file provides information on what the device node for the R-Car
> +generation 3 PCIe PHY contains.
> +
> +Required properties:
> +- compatible: "renesas,r8a77980-pcie-phy" if the device is a part of R8A77980
> +	      SoC.
> +	      "renesas,rcar-gen3-pcie-phy" for a generic R-Car Gen3 compatible
> +	      device.
> +
> +	      When compatible with the generic version, nodes must list the
> +	      SoC-specific version corresponding to the platform first
> +	      followed by the generic version.
> +
> +- reg: offset and length of the register block.
> +- clocks: clock phandle and specifier pair.
> +- power-domains: power domain phandle and specifier pair.
> +- resets: reset phandle and specifier pair.
> +- #phy-cells: see phy-bindings.txt in the same directory, must be <0>.
> +
> +Example (R-Car V3H):
> +
> +	pcie-phy@e65d0000 {
> +		compatible = "renesas,r8a77980-pcie-phy",
> +			     "renesas,rcar-gen3-pcie-phy";
> +		reg = <0 0xe65d0000 0 0x8000>;
> +		#phy-cells = <0>;
> +		clocks = <&cpg CPG_MOD 319>;
> +		power-domains = <&sysc 32>;
> +		resets = <&cpg 319>;
> +	};
diff mbox

Patch

Index: linux-phy/Documentation/devicetree/bindings/phy/rcar-gen3-phy-pcie.txt
===================================================================
--- /dev/null
+++ linux-phy/Documentation/devicetree/bindings/phy/rcar-gen3-phy-pcie.txt
@@ -0,0 +1,32 @@ 
+* Renesas R-Car generation 3 PCIe PHY
+
+This file provides information on what the device node for the R-Car
+generation 3 PCIe PHY contains.
+
+Required properties:
+- compatible: "renesas,r8a77980-pcie-phy" if the device is a part of R8A77980
+	      SoC.
+	      "renesas,rcar-gen3-pcie-phy" for a generic R-Car Gen3 compatible
+	      device.
+
+	      When compatible with the generic version, nodes must list the
+	      SoC-specific version corresponding to the platform first
+	      followed by the generic version.
+
+- reg: offset and length of the register block.
+- clocks: clock phandle and specifier pair.
+- power-domains: power domain phandle and specifier pair.
+- resets: reset phandle and specifier pair.
+- #phy-cells: see phy-bindings.txt in the same directory, must be <0>.
+
+Example (R-Car V3H):
+
+	pcie-phy@e65d0000 {
+		compatible = "renesas,r8a77980-pcie-phy",
+			     "renesas,rcar-gen3-pcie-phy";
+		reg = <0 0xe65d0000 0 0x8000>;
+		#phy-cells = <0>;
+		clocks = <&cpg CPG_MOD 319>;
+		power-domains = <&sysc 32>;
+		resets = <&cpg 319>;
+	};
Index: linux-phy/drivers/phy/renesas/Kconfig
===================================================================
--- linux-phy.orig/drivers/phy/renesas/Kconfig
+++ linux-phy/drivers/phy/renesas/Kconfig
@@ -8,6 +8,13 @@  config PHY_RCAR_GEN2
 	help
 	  Support for USB PHY found on Renesas R-Car generation 2 SoCs.
 
+config PHY_RCAR_GEN3_PCIE
+	tristate "Renesas R-Car generation 3 PCIe PHY driver"
+	depends on ARCH_RENESAS
+	select GENERIC_PHY
+	help
+	  Support for the PCIe PHY found on Renesas R-Car generation 3 SoCs.
+
 config PHY_RCAR_GEN3_USB2
 	tristate "Renesas R-Car generation 3 USB 2.0 PHY driver"
 	depends on ARCH_RENESAS
Index: linux-phy/drivers/phy/renesas/Makefile
===================================================================
--- linux-phy.orig/drivers/phy/renesas/Makefile
+++ linux-phy/drivers/phy/renesas/Makefile
@@ -1,3 +1,4 @@ 
 obj-$(CONFIG_PHY_RCAR_GEN2)		+= phy-rcar-gen2.o
+obj-$(CONFIG_PHY_RCAR_GEN3_PCIE)	+= phy-rcar-gen3-pcie.o
 obj-$(CONFIG_PHY_RCAR_GEN3_USB2)	+= phy-rcar-gen3-usb2.o
 obj-$(CONFIG_PHY_RCAR_GEN3_USB3)	+= phy-rcar-gen3-usb3.o
Index: linux-phy/drivers/phy/renesas/phy-rcar-gen3-pcie.c
===================================================================
--- /dev/null
+++ linux-phy/drivers/phy/renesas/phy-rcar-gen3-pcie.c
@@ -0,0 +1,158 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas R-Car Gen2 PHY driver
+ *
+ * Copyright (C) 2018 Cogent Embedded, Inc.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+#define PHY_CTRL		0x4000		/* R8A77980 only */
+
+/* PHY control register (PHY_CTRL) */
+#define PHY_CTRL_PHY_PWDN	0x00000004
+
+struct rcar_gen3_phy {
+	struct phy *phy;
+	spinlock_t lock;
+	void __iomem *base;
+};
+
+static void rcar_gen3_phy_pcie_modify_reg(struct phy *p, unsigned int reg,
+					  u32 clear, u32 set)
+{
+	struct rcar_gen3_phy *phy = phy_get_drvdata(p);
+	void __iomem *base = phy->base;
+	unsigned long flags;
+	u32 value;
+
+	spin_lock_irqsave(&phy->lock, flags);
+
+	value = readl(base + reg);
+	value &= ~clear;
+	value |= set;
+	writel(value, base + reg);
+
+	spin_unlock_irqrestore(&phy->lock, flags);
+}
+
+static int r8a77980_phy_pcie_power_on(struct phy *p)
+{
+	/* Power on the PCIe PHY */
+	rcar_gen3_phy_pcie_modify_reg(p, PHY_CTRL, PHY_CTRL_PHY_PWDN, 0);
+
+	return 0;
+}
+
+static int r8a77980_phy_pcie_power_off(struct phy *p)
+{
+	/* Power off the PCIe PHY */
+	rcar_gen3_phy_pcie_modify_reg(p, PHY_CTRL, 0, PHY_CTRL_PHY_PWDN);
+
+	return 0;
+}
+
+static const struct phy_ops rcar_gen3_phy_pcie_ops = {
+	.owner		= THIS_MODULE,
+};
+
+static const struct phy_ops r8a77980_phy_pcie_ops = {
+	.power_on	= r8a77980_phy_pcie_power_on,
+	.power_off	= r8a77980_phy_pcie_power_off,
+	.owner		= THIS_MODULE,
+};
+
+static const struct of_device_id rcar_gen3_phy_pcie_match_table[] = {
+	{ .compatible = "renesas,r8a77980-pcie-phy",
+	  .data = &r8a77980_phy_pcie_ops },
+	{ .compatible = "renesas,rcar-gen3-pcie-phy" },
+	{ .data = &rcar_gen3_phy_pcie_ops },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, rcar_gen3_phy_pcie_match_table);
+
+static int rcar_gen3_phy_pcie_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct phy_provider *provider;
+	struct rcar_gen3_phy *phy;
+	struct resource *res;
+	void __iomem *base;
+	int error;
+
+	if (!dev->of_node) {
+		dev_err(dev,
+			"This driver must only be instantiated from the device tree\n");
+		return -EINVAL;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+	if (!phy)
+		return -ENOMEM;
+
+	spin_lock_init(&phy->lock);
+
+	phy->base = base;
+
+	/*
+	 * devm_phy_create() will call pm_runtime_enable(&phy->dev);
+	 * And then, phy-core will manage runtime pm for this device.
+	 */
+	pm_runtime_enable(dev);
+
+	phy->phy = devm_phy_create(dev, NULL, of_device_get_match_data(dev));
+	if (IS_ERR(phy->phy)) {
+		dev_err(dev, "Failed to create PCIe PHY\n");
+		error = PTR_ERR(phy->phy);
+		goto error;
+	}
+	phy_set_drvdata(phy->phy, phy);
+
+	provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+	if (IS_ERR(provider)) {
+		dev_err(dev, "Failed to register PHY provider\n");
+		error = PTR_ERR(provider);
+		goto error;
+	}
+
+	return 0;
+
+error:
+	pm_runtime_disable(dev);
+
+	return error;
+}
+
+static int rcar_gen3_phy_pcie_remove(struct platform_device *pdev)
+{
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+};
+
+static struct platform_driver rcar_gen3_phy_driver = {
+	.driver = {
+		.name		= "phy_rcar_gen3_pcie",
+		.of_match_table	= rcar_gen3_phy_pcie_match_table,
+	},
+	.probe	= rcar_gen3_phy_pcie_probe,
+	.remove = rcar_gen3_phy_pcie_remove,
+};
+
+module_platform_driver(rcar_gen3_phy_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Renesas R-Car Gen3 PCIe PHY");
+MODULE_AUTHOR("Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>");