1*7a099760SInochi Amaoto // SPDX-License-Identifier: GPL-2.0-only
2*7a099760SInochi Amaoto /*
3*7a099760SInochi Amaoto * phy-k3-combo.c - SpacemiT K3 PCIe/USB3 Combo PHY Driver
4*7a099760SInochi Amaoto *
5*7a099760SInochi Amaoto * Copyright (c) 2025 SpacemiT Technology Co. Ltd
6*7a099760SInochi Amaoto */
7*7a099760SInochi Amaoto
8*7a099760SInochi Amaoto #include <linux/bitfield.h>
9*7a099760SInochi Amaoto #include <linux/io.h>
10*7a099760SInochi Amaoto #include <linux/module.h>
11*7a099760SInochi Amaoto #include <linux/mfd/syscon.h>
12*7a099760SInochi Amaoto #include <linux/iopoll.h>
13*7a099760SInochi Amaoto #include <linux/of.h>
14*7a099760SInochi Amaoto #include <linux/platform_device.h>
15*7a099760SInochi Amaoto #include <linux/regmap.h>
16*7a099760SInochi Amaoto #include <linux/phy/phy.h>
17*7a099760SInochi Amaoto
18*7a099760SInochi Amaoto #include <dt-bindings/phy/phy.h>
19*7a099760SInochi Amaoto
20*7a099760SInochi Amaoto #include "phy-k3-common.h"
21*7a099760SInochi Amaoto
22*7a099760SInochi Amaoto /*
23*7a099760SInochi Amaoto * The PCIE/USB Subsystem on SpacemiT K3 have 3 single lane PIPE3 PHYs
24*7a099760SInochi Amaoto * (PHY2/3/4) shared by PCIE PortC/D and USB3 PortB/C/D.
25*7a099760SInochi Amaoto *
26*7a099760SInochi Amaoto * PMUA_PCIE_SUBSYS_MGMT[4:0]
27*7a099760SInochi Amaoto *
28*7a099760SInochi Amaoto * bit4 = 0 : PCIe A X8 mode, all 8 lanes dedicated to PCIe Port A
29*7a099760SInochi Amaoto * 1 : PHY lanes shared between PCIe or USB according to [3:0]
30*7a099760SInochi Amaoto *
31*7a099760SInochi Amaoto * All PHY matrix combinations according to [4:0]:
32*7a099760SInochi Amaoto *
33*7a099760SInochi Amaoto * 0x0X : PCIe-A X8
34*7a099760SInochi Amaoto * 0x10 : PCIe-C x2 (PHY2+PHY3) + PCIe-D x1 (PHY4)
35*7a099760SInochi Amaoto * 0x11 : PCIe-C x2 (PHY2+PHY3) + USB-D (PHY4)
36*7a099760SInochi Amaoto * 0x12 : PCIe-C x1 (PHY2) + USB-C (PHY3)
37*7a099760SInochi Amaoto * 0x13 : PCIe-C x1 (PHY2) + USB-C (PHY3) + USB-D (PHY4)
38*7a099760SInochi Amaoto * 0x14 : PCIe-C x1 (PHY3) + USB-B (PHY2)
39*7a099760SInochi Amaoto * 0x15 : PCIe-C x1 (PHY3) + USB-B (PHY2) + USB-D (PHY4)
40*7a099760SInochi Amaoto * 0x16 : USB-B (PHY2) + USB-C (PHY3) + PCIe D x1 (PHY4)
41*7a099760SInochi Amaoto * 0x17 : USB-B (PHY2) + USB-C (PHY3) + USB-D (PHY4)
42*7a099760SInochi Amaoto *
43*7a099760SInochi Amaoto * So any USB Port B/C/D operation requires PCIe A X8 mode to be disabled.
44*7a099760SInochi Amaoto */
45*7a099760SInochi Amaoto #define PMUA_PCIE_SUBSYS_MGMT 0x1d8
46*7a099760SInochi Amaoto #define PU_MATRIX_CONF_MASK GENMASK(4, 0)
47*7a099760SInochi Amaoto
48*7a099760SInochi Amaoto #define COMBPHY_MAX_SUBPHYS 6
49*7a099760SInochi Amaoto
50*7a099760SInochi Amaoto struct k3_combo_phy {
51*7a099760SInochi Amaoto struct device *dev;
52*7a099760SInochi Amaoto struct k3_lane_group groups[COMBPHY_MAX_SUBPHYS];
53*7a099760SInochi Amaoto void __iomem *base;
54*7a099760SInochi Amaoto struct regmap *apb_spare;
55*7a099760SInochi Amaoto };
56*7a099760SInochi Amaoto
57*7a099760SInochi Amaoto static const struct k3_phy_lane_group_data k3_combphy_lane_group0 = {
58*7a099760SInochi Amaoto .lanes = 2,
59*7a099760SInochi Amaoto .config = 0xff,
60*7a099760SInochi Amaoto .mask = 0x00,
61*7a099760SInochi Amaoto .offsets = {
62*7a099760SInochi Amaoto 0x0, 0x400
63*7a099760SInochi Amaoto },
64*7a099760SInochi Amaoto };
65*7a099760SInochi Amaoto
66*7a099760SInochi Amaoto static const struct k3_phy_lane_group_data k3_combphy_lane_group1 = {
67*7a099760SInochi Amaoto .lanes = 2,
68*7a099760SInochi Amaoto .config = 0xff,
69*7a099760SInochi Amaoto .mask = 0x00,
70*7a099760SInochi Amaoto .offsets = {
71*7a099760SInochi Amaoto 0x100000, 0x100400
72*7a099760SInochi Amaoto },
73*7a099760SInochi Amaoto };
74*7a099760SInochi Amaoto
75*7a099760SInochi Amaoto static const struct k3_phy_lane_group_data k3_combphy_lane_group2 = {
76*7a099760SInochi Amaoto .lanes = 1,
77*7a099760SInochi Amaoto .config = 0x14,
78*7a099760SInochi Amaoto .mask = 0x14,
79*7a099760SInochi Amaoto .offsets = {
80*7a099760SInochi Amaoto 0x200000
81*7a099760SInochi Amaoto },
82*7a099760SInochi Amaoto };
83*7a099760SInochi Amaoto
84*7a099760SInochi Amaoto static const struct k3_phy_lane_group_data k3_combphy_lane_group3 = {
85*7a099760SInochi Amaoto .lanes = 1,
86*7a099760SInochi Amaoto .config = 0x12,
87*7a099760SInochi Amaoto .mask = 0x12,
88*7a099760SInochi Amaoto .offsets = {
89*7a099760SInochi Amaoto 0x300000
90*7a099760SInochi Amaoto },
91*7a099760SInochi Amaoto };
92*7a099760SInochi Amaoto
93*7a099760SInochi Amaoto static const struct k3_phy_lane_group_data k3_combphy_lane_group4 = {
94*7a099760SInochi Amaoto .lanes = 1,
95*7a099760SInochi Amaoto .config = 0x11,
96*7a099760SInochi Amaoto .mask = 0x11,
97*7a099760SInochi Amaoto .offsets = {
98*7a099760SInochi Amaoto 0x400000
99*7a099760SInochi Amaoto },
100*7a099760SInochi Amaoto };
101*7a099760SInochi Amaoto
102*7a099760SInochi Amaoto static const struct k3_phy_lane_group_data k3_combphy_lane_group5 = {
103*7a099760SInochi Amaoto .lanes = 1,
104*7a099760SInochi Amaoto .config = 0xff,
105*7a099760SInochi Amaoto .mask = 0x00,
106*7a099760SInochi Amaoto .offsets = {
107*7a099760SInochi Amaoto 0x500000
108*7a099760SInochi Amaoto },
109*7a099760SInochi Amaoto };
110*7a099760SInochi Amaoto
111*7a099760SInochi Amaoto static const struct k3_phy_lane_group_data *k3_combphy_lane_datas[] = {
112*7a099760SInochi Amaoto &k3_combphy_lane_group0,
113*7a099760SInochi Amaoto &k3_combphy_lane_group1,
114*7a099760SInochi Amaoto &k3_combphy_lane_group2,
115*7a099760SInochi Amaoto &k3_combphy_lane_group3,
116*7a099760SInochi Amaoto &k3_combphy_lane_group4,
117*7a099760SInochi Amaoto &k3_combphy_lane_group5,
118*7a099760SInochi Amaoto };
119*7a099760SInochi Amaoto
k3_combo_phy_init_lanes(struct k3_combo_phy * phy,unsigned int config)120*7a099760SInochi Amaoto static int k3_combo_phy_init_lanes(struct k3_combo_phy *phy, unsigned int config)
121*7a099760SInochi Amaoto {
122*7a099760SInochi Amaoto int i;
123*7a099760SInochi Amaoto
124*7a099760SInochi Amaoto for (i = 0; i < ARRAY_SIZE(k3_combphy_lane_datas); i++) {
125*7a099760SInochi Amaoto const struct k3_phy_lane_group_data *data = k3_combphy_lane_datas[i];
126*7a099760SInochi Amaoto struct k3_lane_group *lg = &phy->groups[i];
127*7a099760SInochi Amaoto const struct phy_ops *ops;
128*7a099760SInochi Amaoto bool is_usb;
129*7a099760SInochi Amaoto
130*7a099760SInochi Amaoto is_usb = (data->mask & config) == data->config;
131*7a099760SInochi Amaoto if (is_usb)
132*7a099760SInochi Amaoto ops = &k3_usb3_phy_ops;
133*7a099760SInochi Amaoto else
134*7a099760SInochi Amaoto ops = &k3_pcie_phy_ops;
135*7a099760SInochi Amaoto
136*7a099760SInochi Amaoto dev_dbg(phy->dev, "phy %d is %s\n", i, is_usb ? "usb" : "pcie");
137*7a099760SInochi Amaoto
138*7a099760SInochi Amaoto lg->phy = devm_phy_create(phy->dev, NULL, ops);
139*7a099760SInochi Amaoto if (IS_ERR(lg->phy))
140*7a099760SInochi Amaoto return PTR_ERR(lg->phy);
141*7a099760SInochi Amaoto
142*7a099760SInochi Amaoto lg->is_pcie = !is_usb;
143*7a099760SInochi Amaoto lg->data = data;
144*7a099760SInochi Amaoto lg->base = phy->base;
145*7a099760SInochi Amaoto phy_set_drvdata(lg->phy, lg);
146*7a099760SInochi Amaoto }
147*7a099760SInochi Amaoto
148*7a099760SInochi Amaoto return 0;
149*7a099760SInochi Amaoto }
150*7a099760SInochi Amaoto
k3_combo_phy_update_config(struct regmap * apmu,unsigned int config)151*7a099760SInochi Amaoto static int k3_combo_phy_update_config(struct regmap *apmu, unsigned int config)
152*7a099760SInochi Amaoto {
153*7a099760SInochi Amaoto if (config & ~PU_MATRIX_CONF_MASK)
154*7a099760SInochi Amaoto return -EINVAL;
155*7a099760SInochi Amaoto
156*7a099760SInochi Amaoto return regmap_update_bits(apmu, PMUA_PCIE_SUBSYS_MGMT, PU_MATRIX_CONF_MASK, config);
157*7a099760SInochi Amaoto }
158*7a099760SInochi Amaoto
k3_combo_phy_xlate(struct device * dev,const struct of_phandle_args * args)159*7a099760SInochi Amaoto static struct phy *k3_combo_phy_xlate(struct device *dev, const struct of_phandle_args *args)
160*7a099760SInochi Amaoto {
161*7a099760SInochi Amaoto struct k3_combo_phy *phy = dev_get_drvdata(dev);
162*7a099760SInochi Amaoto struct k3_lane_group *lg;
163*7a099760SInochi Amaoto
164*7a099760SInochi Amaoto if (args->args_count != 2) {
165*7a099760SInochi Amaoto dev_err(dev, "Invalid number of arguments\n");
166*7a099760SInochi Amaoto return ERR_PTR(-EINVAL);
167*7a099760SInochi Amaoto }
168*7a099760SInochi Amaoto
169*7a099760SInochi Amaoto if (args->args[0] >= ARRAY_SIZE(k3_combphy_lane_datas)) {
170*7a099760SInochi Amaoto dev_err(dev, "Invalid PHY id\n");
171*7a099760SInochi Amaoto return ERR_PTR(-EINVAL);
172*7a099760SInochi Amaoto }
173*7a099760SInochi Amaoto
174*7a099760SInochi Amaoto lg = &phy->groups[args->args[0]];
175*7a099760SInochi Amaoto
176*7a099760SInochi Amaoto if ((lg->is_pcie && args->args[1] != PHY_TYPE_PCIE) ||
177*7a099760SInochi Amaoto (!lg->is_pcie && args->args[1] != PHY_TYPE_USB3)) {
178*7a099760SInochi Amaoto dev_err(dev, "Invalid PHY mode\n");
179*7a099760SInochi Amaoto return ERR_PTR(-EINVAL);
180*7a099760SInochi Amaoto }
181*7a099760SInochi Amaoto
182*7a099760SInochi Amaoto return lg->phy;
183*7a099760SInochi Amaoto }
184*7a099760SInochi Amaoto
k3_combo_phy_probe(struct platform_device * pdev)185*7a099760SInochi Amaoto static int k3_combo_phy_probe(struct platform_device *pdev)
186*7a099760SInochi Amaoto {
187*7a099760SInochi Amaoto struct device *dev = &pdev->dev;
188*7a099760SInochi Amaoto struct device_node *node = dev->of_node;
189*7a099760SInochi Amaoto struct phy_provider *provider;
190*7a099760SInochi Amaoto struct k3_combo_phy *phy;
191*7a099760SInochi Amaoto struct regmap *apmu;
192*7a099760SInochi Amaoto u32 config = 0;
193*7a099760SInochi Amaoto int ret;
194*7a099760SInochi Amaoto
195*7a099760SInochi Amaoto phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
196*7a099760SInochi Amaoto if (!phy)
197*7a099760SInochi Amaoto return -ENOMEM;
198*7a099760SInochi Amaoto
199*7a099760SInochi Amaoto phy->base = devm_platform_ioremap_resource(pdev, 0);
200*7a099760SInochi Amaoto if (IS_ERR(phy->base))
201*7a099760SInochi Amaoto return PTR_ERR(phy->base);
202*7a099760SInochi Amaoto
203*7a099760SInochi Amaoto phy->apb_spare = syscon_regmap_lookup_by_phandle(node, "spacemit,apb-spare");
204*7a099760SInochi Amaoto if (IS_ERR(phy->apb_spare))
205*7a099760SInochi Amaoto return dev_err_probe(dev, PTR_ERR(phy->apb_spare),
206*7a099760SInochi Amaoto "Failed to find APB SPARE syscon");
207*7a099760SInochi Amaoto
208*7a099760SInochi Amaoto apmu = syscon_regmap_lookup_by_phandle_args(node, "spacemit,apmu", 1, &config);
209*7a099760SInochi Amaoto if (IS_ERR(apmu))
210*7a099760SInochi Amaoto return dev_err_probe(dev, PTR_ERR(apmu),
211*7a099760SInochi Amaoto "Failed to find APMU syscon");
212*7a099760SInochi Amaoto
213*7a099760SInochi Amaoto ret = k3_combo_phy_update_config(apmu, config);
214*7a099760SInochi Amaoto if (ret < 0)
215*7a099760SInochi Amaoto return dev_err_probe(dev, ret, "Failed to set lane configuration");
216*7a099760SInochi Amaoto
217*7a099760SInochi Amaoto phy->dev = dev;
218*7a099760SInochi Amaoto platform_set_drvdata(pdev, phy);
219*7a099760SInochi Amaoto
220*7a099760SInochi Amaoto ret = k3_phy_calibrate(phy->apb_spare);
221*7a099760SInochi Amaoto if (ret < 0)
222*7a099760SInochi Amaoto return dev_err_probe(dev, ret, "Failed to calibrate phy");
223*7a099760SInochi Amaoto
224*7a099760SInochi Amaoto ret = k3_combo_phy_init_lanes(phy, config);
225*7a099760SInochi Amaoto if (ret < 0)
226*7a099760SInochi Amaoto return dev_err_probe(dev, ret, "Failed to init lanes");
227*7a099760SInochi Amaoto
228*7a099760SInochi Amaoto provider = devm_of_phy_provider_register(dev, k3_combo_phy_xlate);
229*7a099760SInochi Amaoto if (IS_ERR(provider))
230*7a099760SInochi Amaoto return dev_err_probe(dev, PTR_ERR(provider),
231*7a099760SInochi Amaoto "Failed to register provider\n");
232*7a099760SInochi Amaoto
233*7a099760SInochi Amaoto return 0;
234*7a099760SInochi Amaoto }
235*7a099760SInochi Amaoto
236*7a099760SInochi Amaoto static const struct of_device_id k3_combo_phy_of_match[] = {
237*7a099760SInochi Amaoto { .compatible = "spacemit,k3-combo-phy" },
238*7a099760SInochi Amaoto { },
239*7a099760SInochi Amaoto };
240*7a099760SInochi Amaoto MODULE_DEVICE_TABLE(of, k3_combo_phy_of_match);
241*7a099760SInochi Amaoto
242*7a099760SInochi Amaoto static struct platform_driver k3_combo_phy_driver = {
243*7a099760SInochi Amaoto .probe = k3_combo_phy_probe,
244*7a099760SInochi Amaoto .driver = {
245*7a099760SInochi Amaoto .name = "spacemit,k3-combo-phy",
246*7a099760SInochi Amaoto .of_match_table = k3_combo_phy_of_match,
247*7a099760SInochi Amaoto },
248*7a099760SInochi Amaoto };
249*7a099760SInochi Amaoto module_platform_driver(k3_combo_phy_driver);
250*7a099760SInochi Amaoto
251*7a099760SInochi Amaoto MODULE_DESCRIPTION("SpacemiT K3 USB3/PCIe combo PHY driver");
252*7a099760SInochi Amaoto MODULE_LICENSE("GPL");
253