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