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

phy: amlogic: Add Amlogic AXG PCIE PHY Driver

This adds support for the PCI PHY found in the Amlogic AXG SoC Family.
This will allow to mutualize code in pci-meson.c between AXG and G12A
SoC.

This PHY also uses and chains an analog PHY, which on AXG platform
is needed to have reliable PCIe communication.

Signed-off-by: Remi Pommarel <repk@triplefau.lt>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>

authored by

Remi Pommarel and committed by
Lorenzo Pieralisi
e2463559 af3f5722

+204
+11
drivers/phy/amlogic/Kconfig
··· 60 60 in Meson G12A SoCs. 61 61 If unsure, say N. 62 62 63 + config PHY_MESON_AXG_PCIE 64 + tristate "Meson AXG PCIE PHY driver" 65 + default ARCH_MESON 66 + depends on OF && (ARCH_MESON || COMPILE_TEST) 67 + select GENERIC_PHY 68 + select REGMAP_MMIO 69 + help 70 + Enable this to support the Meson MIPI + PCIE PHY found 71 + in Meson AXG SoCs. 72 + If unsure, say N. 73 + 63 74 config PHY_MESON_AXG_MIPI_PCIE_ANALOG 64 75 tristate "Meson AXG MIPI + PCIE analog PHY driver" 65 76 default ARCH_MESON
+1
drivers/phy/amlogic/Makefile
··· 4 4 obj-$(CONFIG_PHY_MESON_G12A_USB2) += phy-meson-g12a-usb2.o 5 5 obj-$(CONFIG_PHY_MESON_GXL_USB3) += phy-meson-gxl-usb3.o 6 6 obj-$(CONFIG_PHY_MESON_G12A_USB3_PCIE) += phy-meson-g12a-usb3-pcie.o 7 + obj-$(CONFIG_PHY_MESON_AXG_PCIE) += phy-meson-axg-pcie.o 7 8 obj-$(CONFIG_PHY_MESON_AXG_MIPI_PCIE_ANALOG) += phy-meson-axg-mipi-pcie-analog.o
+192
drivers/phy/amlogic/phy-meson-axg-pcie.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Amlogic AXG PCIE PHY driver 4 + * 5 + * Copyright (C) 2020 Remi Pommarel <repk@triplefau.lt> 6 + */ 7 + #include <linux/module.h> 8 + #include <linux/phy/phy.h> 9 + #include <linux/regmap.h> 10 + #include <linux/reset.h> 11 + #include <linux/platform_device.h> 12 + #include <linux/bitfield.h> 13 + #include <dt-bindings/phy/phy.h> 14 + 15 + #define MESON_PCIE_REG0 0x00 16 + #define MESON_PCIE_COMMON_CLK BIT(4) 17 + #define MESON_PCIE_PORT_SEL GENMASK(3, 2) 18 + #define MESON_PCIE_CLK BIT(1) 19 + #define MESON_PCIE_POWERDOWN BIT(0) 20 + 21 + #define MESON_PCIE_TWO_X1 FIELD_PREP(MESON_PCIE_PORT_SEL, 0x3) 22 + #define MESON_PCIE_COMMON_REF_CLK FIELD_PREP(MESON_PCIE_COMMON_CLK, 0x1) 23 + #define MESON_PCIE_PHY_INIT (MESON_PCIE_TWO_X1 | \ 24 + MESON_PCIE_COMMON_REF_CLK) 25 + #define MESON_PCIE_RESET_DELAY 500 26 + 27 + struct phy_axg_pcie_priv { 28 + struct phy *phy; 29 + struct phy *analog; 30 + struct regmap *regmap; 31 + struct reset_control *reset; 32 + }; 33 + 34 + static const struct regmap_config phy_axg_pcie_regmap_conf = { 35 + .reg_bits = 8, 36 + .val_bits = 32, 37 + .reg_stride = 4, 38 + .max_register = MESON_PCIE_REG0, 39 + }; 40 + 41 + static int phy_axg_pcie_power_on(struct phy *phy) 42 + { 43 + struct phy_axg_pcie_priv *priv = phy_get_drvdata(phy); 44 + int ret; 45 + 46 + ret = phy_power_on(priv->analog); 47 + if (ret != 0) 48 + return ret; 49 + 50 + regmap_update_bits(priv->regmap, MESON_PCIE_REG0, 51 + MESON_PCIE_POWERDOWN, 0); 52 + return 0; 53 + } 54 + 55 + static int phy_axg_pcie_power_off(struct phy *phy) 56 + { 57 + struct phy_axg_pcie_priv *priv = phy_get_drvdata(phy); 58 + int ret; 59 + 60 + ret = phy_power_off(priv->analog); 61 + if (ret != 0) 62 + return ret; 63 + 64 + regmap_update_bits(priv->regmap, MESON_PCIE_REG0, 65 + MESON_PCIE_POWERDOWN, 1); 66 + return 0; 67 + } 68 + 69 + static int phy_axg_pcie_init(struct phy *phy) 70 + { 71 + struct phy_axg_pcie_priv *priv = phy_get_drvdata(phy); 72 + int ret; 73 + 74 + ret = phy_init(priv->analog); 75 + if (ret != 0) 76 + return ret; 77 + 78 + regmap_write(priv->regmap, MESON_PCIE_REG0, MESON_PCIE_PHY_INIT); 79 + return reset_control_reset(priv->reset); 80 + } 81 + 82 + static int phy_axg_pcie_exit(struct phy *phy) 83 + { 84 + struct phy_axg_pcie_priv *priv = phy_get_drvdata(phy); 85 + int ret; 86 + 87 + ret = phy_exit(priv->analog); 88 + if (ret != 0) 89 + return ret; 90 + 91 + return reset_control_reset(priv->reset); 92 + } 93 + 94 + static int phy_axg_pcie_reset(struct phy *phy) 95 + { 96 + struct phy_axg_pcie_priv *priv = phy_get_drvdata(phy); 97 + int ret = 0; 98 + 99 + ret = phy_reset(priv->analog); 100 + if (ret != 0) 101 + goto out; 102 + 103 + ret = reset_control_assert(priv->reset); 104 + if (ret != 0) 105 + goto out; 106 + udelay(MESON_PCIE_RESET_DELAY); 107 + 108 + ret = reset_control_deassert(priv->reset); 109 + if (ret != 0) 110 + goto out; 111 + udelay(MESON_PCIE_RESET_DELAY); 112 + 113 + out: 114 + return ret; 115 + } 116 + 117 + static const struct phy_ops phy_axg_pcie_ops = { 118 + .init = phy_axg_pcie_init, 119 + .exit = phy_axg_pcie_exit, 120 + .power_on = phy_axg_pcie_power_on, 121 + .power_off = phy_axg_pcie_power_off, 122 + .reset = phy_axg_pcie_reset, 123 + .owner = THIS_MODULE, 124 + }; 125 + 126 + static int phy_axg_pcie_probe(struct platform_device *pdev) 127 + { 128 + struct phy_provider *pphy; 129 + struct device *dev = &pdev->dev; 130 + struct phy_axg_pcie_priv *priv; 131 + struct device_node *np = dev->of_node; 132 + struct resource *res; 133 + void __iomem *base; 134 + int ret; 135 + 136 + priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL); 137 + if (!priv) 138 + return -ENOMEM; 139 + 140 + priv->phy = devm_phy_create(dev, np, &phy_axg_pcie_ops); 141 + if (IS_ERR(priv->phy)) { 142 + ret = PTR_ERR(priv->phy); 143 + if (ret != -EPROBE_DEFER) 144 + dev_err(dev, "failed to create PHY\n"); 145 + return ret; 146 + } 147 + 148 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 149 + base = devm_ioremap_resource(dev, res); 150 + if (IS_ERR(base)) 151 + return PTR_ERR(base); 152 + 153 + priv->regmap = devm_regmap_init_mmio(dev, base, 154 + &phy_axg_pcie_regmap_conf); 155 + if (IS_ERR(priv->regmap)) 156 + return PTR_ERR(priv->regmap); 157 + 158 + priv->reset = devm_reset_control_array_get(dev, false, false); 159 + if (IS_ERR(priv->reset)) 160 + return PTR_ERR(priv->reset); 161 + 162 + priv->analog = devm_phy_get(dev, "analog"); 163 + if (IS_ERR(priv->analog)) 164 + return PTR_ERR(priv->analog); 165 + 166 + phy_set_drvdata(priv->phy, priv); 167 + dev_set_drvdata(dev, priv); 168 + pphy = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 169 + 170 + return PTR_ERR_OR_ZERO(pphy); 171 + } 172 + 173 + static const struct of_device_id phy_axg_pcie_of_match[] = { 174 + { 175 + .compatible = "amlogic,axg-pcie-phy", 176 + }, 177 + { }, 178 + }; 179 + MODULE_DEVICE_TABLE(of, phy_axg_pcie_of_match); 180 + 181 + static struct platform_driver phy_axg_pcie_driver = { 182 + .probe = phy_axg_pcie_probe, 183 + .driver = { 184 + .name = "phy-axg-pcie", 185 + .of_match_table = phy_axg_pcie_of_match, 186 + }, 187 + }; 188 + module_platform_driver(phy_axg_pcie_driver); 189 + 190 + MODULE_AUTHOR("Remi Pommarel <repk@triplefau.lt>"); 191 + MODULE_DESCRIPTION("Amlogic AXG PCIE PHY driver"); 192 + MODULE_LICENSE("GPL v2");