Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

phy: Renesas R-Car gen3 PCIe PHY driver

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 up for PCIe to work...

Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
Reviewed-by: Simon Horman <horms+renesas@verge.net.au>
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>

authored by

Sergei Shtylyov and committed by
Kishon Vijay Abraham I
2ce7f2f4 4fa88cd3

+159
+7
drivers/phy/renesas/Kconfig
··· 8 8 help 9 9 Support for USB PHY found on Renesas R-Car generation 2 SoCs. 10 10 11 + config PHY_RCAR_GEN3_PCIE 12 + tristate "Renesas R-Car generation 3 PCIe PHY driver" 13 + depends on ARCH_RENESAS 14 + select GENERIC_PHY 15 + help 16 + Support for the PCIe PHY found on Renesas R-Car generation 3 SoCs. 17 + 11 18 config PHY_RCAR_GEN3_USB2 12 19 tristate "Renesas R-Car generation 3 USB 2.0 PHY driver" 13 20 depends on ARCH_RENESAS
+1
drivers/phy/renesas/Makefile
··· 1 1 obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o 2 + obj-$(CONFIG_PHY_RCAR_GEN3_PCIE) += phy-rcar-gen3-pcie.o 2 3 obj-$(CONFIG_PHY_RCAR_GEN3_USB2) += phy-rcar-gen3-usb2.o 3 4 obj-$(CONFIG_PHY_RCAR_GEN3_USB3) += phy-rcar-gen3-usb3.o
+151
drivers/phy/renesas/phy-rcar-gen3-pcie.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Renesas R-Car Gen3 PCIe PHY driver 4 + * 5 + * Copyright (C) 2018 Cogent Embedded, Inc. 6 + */ 7 + 8 + #include <linux/clk.h> 9 + #include <linux/io.h> 10 + #include <linux/module.h> 11 + #include <linux/of.h> 12 + #include <linux/phy/phy.h> 13 + #include <linux/of_device.h> 14 + #include <linux/platform_device.h> 15 + #include <linux/spinlock.h> 16 + 17 + #define PHY_CTRL 0x4000 /* R8A77980 only */ 18 + 19 + /* PHY control register (PHY_CTRL) */ 20 + #define PHY_CTRL_PHY_PWDN BIT(2) 21 + 22 + struct rcar_gen3_phy { 23 + struct phy *phy; 24 + spinlock_t lock; 25 + void __iomem *base; 26 + }; 27 + 28 + static void rcar_gen3_phy_pcie_modify_reg(struct phy *p, unsigned int reg, 29 + u32 clear, u32 set) 30 + { 31 + struct rcar_gen3_phy *phy = phy_get_drvdata(p); 32 + void __iomem *base = phy->base; 33 + unsigned long flags; 34 + u32 value; 35 + 36 + spin_lock_irqsave(&phy->lock, flags); 37 + 38 + value = readl(base + reg); 39 + value &= ~clear; 40 + value |= set; 41 + writel(value, base + reg); 42 + 43 + spin_unlock_irqrestore(&phy->lock, flags); 44 + } 45 + 46 + static int r8a77980_phy_pcie_power_on(struct phy *p) 47 + { 48 + /* Power on the PCIe PHY */ 49 + rcar_gen3_phy_pcie_modify_reg(p, PHY_CTRL, PHY_CTRL_PHY_PWDN, 0); 50 + 51 + return 0; 52 + } 53 + 54 + static int r8a77980_phy_pcie_power_off(struct phy *p) 55 + { 56 + /* Power off the PCIe PHY */ 57 + rcar_gen3_phy_pcie_modify_reg(p, PHY_CTRL, 0, PHY_CTRL_PHY_PWDN); 58 + 59 + return 0; 60 + } 61 + 62 + static const struct phy_ops r8a77980_phy_pcie_ops = { 63 + .power_on = r8a77980_phy_pcie_power_on, 64 + .power_off = r8a77980_phy_pcie_power_off, 65 + .owner = THIS_MODULE, 66 + }; 67 + 68 + static const struct of_device_id rcar_gen3_phy_pcie_match_table[] = { 69 + { .compatible = "renesas,r8a77980-pcie-phy" }, 70 + { } 71 + }; 72 + MODULE_DEVICE_TABLE(of, rcar_gen3_phy_pcie_match_table); 73 + 74 + static int rcar_gen3_phy_pcie_probe(struct platform_device *pdev) 75 + { 76 + struct device *dev = &pdev->dev; 77 + struct phy_provider *provider; 78 + struct rcar_gen3_phy *phy; 79 + struct resource *res; 80 + void __iomem *base; 81 + int error; 82 + 83 + if (!dev->of_node) { 84 + dev_err(dev, 85 + "This driver must only be instantiated from the device tree\n"); 86 + return -EINVAL; 87 + } 88 + 89 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 90 + base = devm_ioremap_resource(dev, res); 91 + if (IS_ERR(base)) 92 + return PTR_ERR(base); 93 + 94 + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); 95 + if (!phy) 96 + return -ENOMEM; 97 + 98 + spin_lock_init(&phy->lock); 99 + 100 + phy->base = base; 101 + 102 + /* 103 + * devm_phy_create() will call pm_runtime_enable(&phy->dev); 104 + * And then, phy-core will manage runtime PM for this device. 105 + */ 106 + pm_runtime_enable(dev); 107 + 108 + phy->phy = devm_phy_create(dev, NULL, &r8a77980_phy_pcie_ops); 109 + if (IS_ERR(phy->phy)) { 110 + dev_err(dev, "Failed to create PCIe PHY\n"); 111 + error = PTR_ERR(phy->phy); 112 + goto error; 113 + } 114 + phy_set_drvdata(phy->phy, phy); 115 + 116 + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 117 + if (IS_ERR(provider)) { 118 + dev_err(dev, "Failed to register PHY provider\n"); 119 + error = PTR_ERR(provider); 120 + goto error; 121 + } 122 + 123 + return 0; 124 + 125 + error: 126 + pm_runtime_disable(dev); 127 + 128 + return error; 129 + } 130 + 131 + static int rcar_gen3_phy_pcie_remove(struct platform_device *pdev) 132 + { 133 + pm_runtime_disable(&pdev->dev); 134 + 135 + return 0; 136 + }; 137 + 138 + static struct platform_driver rcar_gen3_phy_driver = { 139 + .driver = { 140 + .name = "phy_rcar_gen3_pcie", 141 + .of_match_table = rcar_gen3_phy_pcie_match_table, 142 + }, 143 + .probe = rcar_gen3_phy_pcie_probe, 144 + .remove = rcar_gen3_phy_pcie_remove, 145 + }; 146 + 147 + module_platform_driver(rcar_gen3_phy_driver); 148 + 149 + MODULE_LICENSE("GPL v2"); 150 + MODULE_DESCRIPTION("Renesas R-Car Gen3 PCIe PHY"); 151 + MODULE_AUTHOR("Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>");