Skip to content

Commit e246355

Browse files
repkLorenzo Pieralisi
authored andcommitted
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 <[email protected]> Signed-off-by: Lorenzo Pieralisi <[email protected]> Reviewed-by: Neil Armstrong <[email protected]>
1 parent af3f572 commit e246355

File tree

3 files changed

+204
-0
lines changed

3 files changed

+204
-0
lines changed

drivers/phy/amlogic/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,17 @@ config PHY_MESON_G12A_USB3_PCIE
6060
in Meson G12A SoCs.
6161
If unsure, say N.
6262

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+
6374
config PHY_MESON_AXG_MIPI_PCIE_ANALOG
6475
tristate "Meson AXG MIPI + PCIE analog PHY driver"
6576
default ARCH_MESON

drivers/phy/amlogic/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ obj-$(CONFIG_PHY_MESON_GXL_USB2) += phy-meson-gxl-usb2.o
44
obj-$(CONFIG_PHY_MESON_G12A_USB2) += phy-meson-g12a-usb2.o
55
obj-$(CONFIG_PHY_MESON_GXL_USB3) += phy-meson-gxl-usb3.o
66
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
78
obj-$(CONFIG_PHY_MESON_AXG_MIPI_PCIE_ANALOG) += phy-meson-axg-mipi-pcie-analog.o
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Amlogic AXG PCIE PHY driver
4+
*
5+
* Copyright (C) 2020 Remi Pommarel <[email protected]>
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 <[email protected]>");
191+
MODULE_DESCRIPTION("Amlogic AXG PCIE PHY driver");
192+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)