Skip to content

Commit 74badb8

Browse files
Nitheesh Sekarvinodkoul
authored andcommitted
phy: qcom: Introduce PCIe UNIPHY 28LP driver
Add Qualcomm PCIe UNIPHY 28LP driver support present in Qualcomm IPQ5332 SoC and the phy init sequence. Reviewed-by: Dmitry Baryshkov <[email protected]> Signed-off-by: Nitheesh Sekar <[email protected]> Signed-off-by: Varadarajan Narayanan <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Vinod Koul <[email protected]>
1 parent a2e9348 commit 74badb8

File tree

3 files changed

+299
-0
lines changed

3 files changed

+299
-0
lines changed

drivers/phy/qualcomm/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,18 @@ config PHY_QCOM_M31_USB
154154
management. This driver is required even for peripheral only or
155155
host only mode configurations.
156156

157+
config PHY_QCOM_UNIPHY_PCIE_28LP
158+
bool "PCIE UNIPHY 28LP PHY driver"
159+
depends on ARCH_QCOM
160+
depends on HAS_IOMEM
161+
depends on OF
162+
select GENERIC_PHY
163+
help
164+
Enable this to support the PCIe UNIPHY 28LP phy transceiver that
165+
is used with PCIe controllers on Qualcomm IPQ5332 chips. It
166+
handles PHY initialization, clock management required after
167+
resetting the hardware and power management.
168+
157169
config PHY_QCOM_USB_HS
158170
tristate "Qualcomm USB HS PHY module"
159171
depends on USB_ULPI_BUS

drivers/phy/qualcomm/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ obj-$(CONFIG_PHY_QCOM_QMP_USB_LEGACY) += phy-qcom-qmp-usb-legacy.o
1717
obj-$(CONFIG_PHY_QCOM_QUSB2) += phy-qcom-qusb2.o
1818
obj-$(CONFIG_PHY_QCOM_SNPS_EUSB2) += phy-qcom-snps-eusb2.o
1919
obj-$(CONFIG_PHY_QCOM_EUSB2_REPEATER) += phy-qcom-eusb2-repeater.o
20+
obj-$(CONFIG_PHY_QCOM_UNIPHY_PCIE_28LP) += phy-qcom-uniphy-pcie-28lp.o
2021
obj-$(CONFIG_PHY_QCOM_USB_HS) += phy-qcom-usb-hs.o
2122
obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o
2223
obj-$(CONFIG_PHY_QCOM_USB_HS_28NM) += phy-qcom-usb-hs-28nm.o
Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
// SPDX-License-Identifier: GPL-2.0+
2+
/*
3+
* Copyright (c) 2025, The Linux Foundation. All rights reserved.
4+
*/
5+
6+
#include <linux/clk.h>
7+
#include <linux/clk-provider.h>
8+
#include <linux/delay.h>
9+
#include <linux/err.h>
10+
#include <linux/io.h>
11+
#include <linux/mfd/syscon.h>
12+
#include <linux/module.h>
13+
#include <linux/of_device.h>
14+
#include <linux/of.h>
15+
#include <linux/phy/phy.h>
16+
#include <linux/platform_device.h>
17+
#include <linux/regmap.h>
18+
#include <linux/reset.h>
19+
#include <linux/units.h>
20+
21+
#define RST_ASSERT_DELAY_MIN_US 100
22+
#define RST_ASSERT_DELAY_MAX_US 150
23+
#define PIPE_CLK_DELAY_MIN_US 5000
24+
#define PIPE_CLK_DELAY_MAX_US 5100
25+
#define CLK_EN_DELAY_MIN_US 30
26+
#define CLK_EN_DELAY_MAX_US 50
27+
#define CDR_CTRL_REG_1 0x80
28+
#define CDR_CTRL_REG_2 0x84
29+
#define CDR_CTRL_REG_3 0x88
30+
#define CDR_CTRL_REG_4 0x8c
31+
#define CDR_CTRL_REG_5 0x90
32+
#define CDR_CTRL_REG_6 0x94
33+
#define CDR_CTRL_REG_7 0x98
34+
#define SSCG_CTRL_REG_1 0x9c
35+
#define SSCG_CTRL_REG_2 0xa0
36+
#define SSCG_CTRL_REG_3 0xa4
37+
#define SSCG_CTRL_REG_4 0xa8
38+
#define SSCG_CTRL_REG_5 0xac
39+
#define SSCG_CTRL_REG_6 0xb0
40+
#define PCS_INTERNAL_CONTROL_2 0x2d8
41+
42+
#define PHY_CFG_PLLCFG 0x220
43+
#define PHY_CFG_EIOS_DTCT_REG 0x3e4
44+
#define PHY_CFG_GEN3_ALIGN_HOLDOFF_TIME 0x3e8
45+
46+
enum qcom_uniphy_pcie_type {
47+
PHY_TYPE_PCIE = 1,
48+
PHY_TYPE_PCIE_GEN2,
49+
PHY_TYPE_PCIE_GEN3,
50+
};
51+
52+
struct qcom_uniphy_pcie_regs {
53+
u32 offset;
54+
u32 val;
55+
};
56+
57+
struct qcom_uniphy_pcie_data {
58+
int lane_offset; /* offset between the lane register bases */
59+
u32 phy_type;
60+
const struct qcom_uniphy_pcie_regs *init_seq;
61+
u32 init_seq_num;
62+
u32 pipe_clk_rate;
63+
};
64+
65+
struct qcom_uniphy_pcie {
66+
struct phy phy;
67+
struct device *dev;
68+
const struct qcom_uniphy_pcie_data *data;
69+
struct clk_bulk_data *clks;
70+
int num_clks;
71+
struct reset_control *resets;
72+
void __iomem *base;
73+
int lanes;
74+
};
75+
76+
#define phy_to_dw_phy(x) container_of((x), struct qca_uni_pcie_phy, phy)
77+
78+
static const struct qcom_uniphy_pcie_regs ipq5332_regs[] = {
79+
{
80+
.offset = PHY_CFG_PLLCFG,
81+
.val = 0x30,
82+
}, {
83+
.offset = PHY_CFG_EIOS_DTCT_REG,
84+
.val = 0x53ef,
85+
}, {
86+
.offset = PHY_CFG_GEN3_ALIGN_HOLDOFF_TIME,
87+
.val = 0xcf,
88+
},
89+
};
90+
91+
static const struct qcom_uniphy_pcie_data ipq5332_data = {
92+
.lane_offset = 0x800,
93+
.phy_type = PHY_TYPE_PCIE_GEN3,
94+
.init_seq = ipq5332_regs,
95+
.init_seq_num = ARRAY_SIZE(ipq5332_regs),
96+
.pipe_clk_rate = 250 * MEGA,
97+
};
98+
99+
static void qcom_uniphy_pcie_init(struct qcom_uniphy_pcie *phy)
100+
{
101+
const struct qcom_uniphy_pcie_data *data = phy->data;
102+
const struct qcom_uniphy_pcie_regs *init_seq;
103+
void __iomem *base = phy->base;
104+
int lane, i;
105+
106+
for (lane = 0; lane < phy->lanes; lane++) {
107+
init_seq = data->init_seq;
108+
109+
for (i = 0; i < data->init_seq_num; i++)
110+
writel(init_seq[i].val, base + init_seq[i].offset);
111+
112+
base += data->lane_offset;
113+
}
114+
}
115+
116+
static int qcom_uniphy_pcie_power_off(struct phy *x)
117+
{
118+
struct qcom_uniphy_pcie *phy = phy_get_drvdata(x);
119+
120+
clk_bulk_disable_unprepare(phy->num_clks, phy->clks);
121+
122+
return reset_control_assert(phy->resets);
123+
}
124+
125+
static int qcom_uniphy_pcie_power_on(struct phy *x)
126+
{
127+
struct qcom_uniphy_pcie *phy = phy_get_drvdata(x);
128+
int ret;
129+
130+
ret = reset_control_assert(phy->resets);
131+
if (ret) {
132+
dev_err(phy->dev, "reset assert failed (%d)\n", ret);
133+
return ret;
134+
}
135+
136+
usleep_range(RST_ASSERT_DELAY_MIN_US, RST_ASSERT_DELAY_MAX_US);
137+
138+
ret = reset_control_deassert(phy->resets);
139+
if (ret) {
140+
dev_err(phy->dev, "reset deassert failed (%d)\n", ret);
141+
return ret;
142+
}
143+
144+
usleep_range(PIPE_CLK_DELAY_MIN_US, PIPE_CLK_DELAY_MAX_US);
145+
146+
ret = clk_bulk_prepare_enable(phy->num_clks, phy->clks);
147+
if (ret) {
148+
dev_err(phy->dev, "clk prepare and enable failed %d\n", ret);
149+
return ret;
150+
}
151+
152+
usleep_range(CLK_EN_DELAY_MIN_US, CLK_EN_DELAY_MAX_US);
153+
154+
qcom_uniphy_pcie_init(phy);
155+
156+
return 0;
157+
}
158+
159+
static inline int qcom_uniphy_pcie_get_resources(struct platform_device *pdev,
160+
struct qcom_uniphy_pcie *phy)
161+
{
162+
struct resource *res;
163+
164+
phy->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
165+
if (IS_ERR(phy->base))
166+
return PTR_ERR(phy->base);
167+
168+
phy->num_clks = devm_clk_bulk_get_all(phy->dev, &phy->clks);
169+
if (phy->num_clks < 0)
170+
return phy->num_clks;
171+
172+
phy->resets = devm_reset_control_array_get_exclusive(phy->dev);
173+
if (IS_ERR(phy->resets))
174+
return PTR_ERR(phy->resets);
175+
176+
return 0;
177+
}
178+
179+
/*
180+
* Register a fixed rate pipe clock.
181+
*
182+
* The <s>_pipe_clksrc generated by PHY goes to the GCC that gate
183+
* controls it. The <s>_pipe_clk coming out of the GCC is requested
184+
* by the PHY driver for its operations.
185+
* We register the <s>_pipe_clksrc here. The gcc driver takes care
186+
* of assigning this <s>_pipe_clksrc as parent to <s>_pipe_clk.
187+
* Below picture shows this relationship.
188+
*
189+
* +---------------+
190+
* | PHY block |<<---------------------------------------+
191+
* | | |
192+
* | +-------+ | +-----+ |
193+
* I/P---^-->| PLL |---^--->pipe_clksrc--->| GCC |--->pipe_clk---+
194+
* clk | +-------+ | +-----+
195+
* +---------------+
196+
*/
197+
static inline int phy_pipe_clk_register(struct qcom_uniphy_pcie *phy, int id)
198+
{
199+
const struct qcom_uniphy_pcie_data *data = phy->data;
200+
struct clk_hw *hw;
201+
char name[64];
202+
203+
snprintf(name, sizeof(name), "phy%d_pipe_clk_src", id);
204+
hw = devm_clk_hw_register_fixed_rate(phy->dev, name, NULL, 0,
205+
data->pipe_clk_rate);
206+
if (IS_ERR(hw))
207+
return dev_err_probe(phy->dev, PTR_ERR(hw),
208+
"Unable to register %s\n", name);
209+
210+
return devm_of_clk_add_hw_provider(phy->dev, of_clk_hw_simple_get, hw);
211+
}
212+
213+
static const struct of_device_id qcom_uniphy_pcie_id_table[] = {
214+
{
215+
.compatible = "qcom,ipq5332-uniphy-pcie-phy",
216+
.data = &ipq5332_data,
217+
}, {
218+
/* Sentinel */
219+
},
220+
};
221+
MODULE_DEVICE_TABLE(of, qcom_uniphy_pcie_id_table);
222+
223+
static const struct phy_ops pcie_ops = {
224+
.power_on = qcom_uniphy_pcie_power_on,
225+
.power_off = qcom_uniphy_pcie_power_off,
226+
.owner = THIS_MODULE,
227+
};
228+
229+
static int qcom_uniphy_pcie_probe(struct platform_device *pdev)
230+
{
231+
struct phy_provider *phy_provider;
232+
struct device *dev = &pdev->dev;
233+
struct qcom_uniphy_pcie *phy;
234+
struct phy *generic_phy;
235+
int ret;
236+
237+
phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
238+
if (!phy)
239+
return -ENOMEM;
240+
241+
platform_set_drvdata(pdev, phy);
242+
phy->dev = &pdev->dev;
243+
244+
phy->data = of_device_get_match_data(dev);
245+
if (!phy->data)
246+
return -EINVAL;
247+
248+
ret = of_property_read_u32(dev_of_node(dev), "num-lanes", &phy->lanes);
249+
if (ret)
250+
return dev_err_probe(dev, ret, "Couldn't read num-lanes\n");
251+
252+
ret = qcom_uniphy_pcie_get_resources(pdev, phy);
253+
if (ret < 0)
254+
return dev_err_probe(&pdev->dev, ret,
255+
"failed to get resources: %d\n", ret);
256+
257+
generic_phy = devm_phy_create(phy->dev, NULL, &pcie_ops);
258+
if (IS_ERR(generic_phy))
259+
return PTR_ERR(generic_phy);
260+
261+
phy_set_drvdata(generic_phy, phy);
262+
263+
ret = phy_pipe_clk_register(phy, generic_phy->id);
264+
if (ret)
265+
dev_err(&pdev->dev, "failed to register phy pipe clk\n");
266+
267+
phy_provider = devm_of_phy_provider_register(phy->dev,
268+
of_phy_simple_xlate);
269+
if (IS_ERR(phy_provider))
270+
return PTR_ERR(phy_provider);
271+
272+
return 0;
273+
}
274+
275+
static struct platform_driver qcom_uniphy_pcie_driver = {
276+
.probe = qcom_uniphy_pcie_probe,
277+
.driver = {
278+
.name = "qcom-uniphy-pcie",
279+
.of_match_table = qcom_uniphy_pcie_id_table,
280+
},
281+
};
282+
283+
module_platform_driver(qcom_uniphy_pcie_driver);
284+
285+
MODULE_DESCRIPTION("PCIE QCOM UNIPHY driver");
286+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)