Commit | Line | Data |
---|---|---|
d87da323 SP |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Mediatek MT7621 PCI PHY Driver | |
4 | * Author: Sergio Paracuellos <sergio.paracuellos@gmail.com> | |
5 | */ | |
6 | ||
7 | #include <dt-bindings/phy/phy.h> | |
60ece833 | 8 | #include <linux/clk.h> |
d87da323 SP |
9 | #include <linux/bitfield.h> |
10 | #include <linux/bitops.h> | |
11 | #include <linux/module.h> | |
7559e757 | 12 | #include <linux/of.h> |
d87da323 SP |
13 | #include <linux/phy/phy.h> |
14 | #include <linux/platform_device.h> | |
15 | #include <linux/regmap.h> | |
16 | #include <linux/sys_soc.h> | |
d87da323 SP |
17 | |
18 | #define RG_PE1_PIPE_REG 0x02c | |
19 | #define RG_PE1_PIPE_RST BIT(12) | |
20 | #define RG_PE1_PIPE_CMD_FRC BIT(4) | |
21 | ||
22 | #define RG_P0_TO_P1_WIDTH 0x100 | |
23 | #define RG_PE1_H_LCDDS_REG 0x49c | |
24 | #define RG_PE1_H_LCDDS_PCW GENMASK(30, 0) | |
25 | ||
26 | #define RG_PE1_FRC_H_XTAL_REG 0x400 | |
27 | #define RG_PE1_FRC_H_XTAL_TYPE BIT(8) | |
28 | #define RG_PE1_H_XTAL_TYPE GENMASK(10, 9) | |
29 | ||
30 | #define RG_PE1_FRC_PHY_REG 0x000 | |
31 | #define RG_PE1_FRC_PHY_EN BIT(4) | |
32 | #define RG_PE1_PHY_EN BIT(5) | |
33 | ||
34 | #define RG_PE1_H_PLL_REG 0x490 | |
35 | #define RG_PE1_H_PLL_BC GENMASK(23, 22) | |
36 | #define RG_PE1_H_PLL_BP GENMASK(21, 18) | |
37 | #define RG_PE1_H_PLL_IR GENMASK(15, 12) | |
38 | #define RG_PE1_H_PLL_IC GENMASK(11, 8) | |
39 | #define RG_PE1_H_PLL_PREDIV GENMASK(7, 6) | |
40 | #define RG_PE1_PLL_DIVEN GENMASK(3, 1) | |
41 | ||
42 | #define RG_PE1_H_PLL_FBKSEL_REG 0x4bc | |
43 | #define RG_PE1_H_PLL_FBKSEL GENMASK(5, 4) | |
44 | ||
45 | #define RG_PE1_H_LCDDS_SSC_PRD_REG 0x4a4 | |
46 | #define RG_PE1_H_LCDDS_SSC_PRD GENMASK(15, 0) | |
47 | ||
48 | #define RG_PE1_H_LCDDS_SSC_DELTA_REG 0x4a8 | |
49 | #define RG_PE1_H_LCDDS_SSC_DELTA GENMASK(11, 0) | |
50 | #define RG_PE1_H_LCDDS_SSC_DELTA1 GENMASK(27, 16) | |
51 | ||
52 | #define RG_PE1_LCDDS_CLK_PH_INV_REG 0x4a0 | |
53 | #define RG_PE1_LCDDS_CLK_PH_INV BIT(5) | |
54 | ||
55 | #define RG_PE1_H_PLL_BR_REG 0x4ac | |
56 | #define RG_PE1_H_PLL_BR GENMASK(18, 16) | |
57 | ||
58 | #define RG_PE1_MSTCKDIV_REG 0x414 | |
59 | #define RG_PE1_MSTCKDIV GENMASK(7, 6) | |
60 | ||
61 | #define RG_PE1_FRC_MSTCKDIV BIT(5) | |
62 | ||
d87da323 SP |
63 | #define MAX_PHYS 2 |
64 | ||
65 | /** | |
66 | * struct mt7621_pci_phy - Mt7621 Pcie PHY core | |
67 | * @dev: pointer to device | |
68 | * @regmap: kernel regmap pointer | |
69 | * @phy: pointer to the kernel PHY device | |
60ece833 | 70 | * @sys_clk: pointer to the system XTAL clock |
d87da323 SP |
71 | * @port_base: base register |
72 | * @has_dual_port: if the phy has dual ports. | |
73 | * @bypass_pipe_rst: mark if 'mt7621_bypass_pipe_rst' | |
74 | * needs to be executed. Depends on chip revision. | |
75 | */ | |
76 | struct mt7621_pci_phy { | |
77 | struct device *dev; | |
78 | struct regmap *regmap; | |
79 | struct phy *phy; | |
60ece833 | 80 | struct clk *sys_clk; |
d87da323 SP |
81 | void __iomem *port_base; |
82 | bool has_dual_port; | |
83 | bool bypass_pipe_rst; | |
84 | }; | |
85 | ||
86 | static inline void mt7621_phy_rmw(struct mt7621_pci_phy *phy, | |
87 | u32 reg, u32 clr, u32 set) | |
88 | { | |
89 | u32 val; | |
90 | ||
91 | /* | |
92 | * We cannot use 'regmap_write_bits' here because internally | |
93 | * 'set' is masked before is set to the value that will be | |
94 | * written to the register. That way results in no reliable | |
95 | * pci setup. Avoid to mask 'set' before set value to 'val' | |
96 | * completely avoid the problem. | |
97 | */ | |
98 | regmap_read(phy->regmap, reg, &val); | |
99 | val &= ~clr; | |
100 | val |= set; | |
101 | regmap_write(phy->regmap, reg, val); | |
102 | } | |
103 | ||
104 | static void mt7621_bypass_pipe_rst(struct mt7621_pci_phy *phy) | |
105 | { | |
106 | mt7621_phy_rmw(phy, RG_PE1_PIPE_REG, 0, RG_PE1_PIPE_RST); | |
107 | mt7621_phy_rmw(phy, RG_PE1_PIPE_REG, 0, RG_PE1_PIPE_CMD_FRC); | |
108 | ||
109 | if (phy->has_dual_port) { | |
110 | mt7621_phy_rmw(phy, RG_PE1_PIPE_REG + RG_P0_TO_P1_WIDTH, | |
111 | 0, RG_PE1_PIPE_RST); | |
112 | mt7621_phy_rmw(phy, RG_PE1_PIPE_REG + RG_P0_TO_P1_WIDTH, | |
113 | 0, RG_PE1_PIPE_CMD_FRC); | |
114 | } | |
115 | } | |
116 | ||
60ece833 | 117 | static int mt7621_set_phy_for_ssc(struct mt7621_pci_phy *phy) |
d87da323 SP |
118 | { |
119 | struct device *dev = phy->dev; | |
60ece833 | 120 | unsigned long clk_rate; |
d87da323 | 121 | |
60ece833 SP |
122 | clk_rate = clk_get_rate(phy->sys_clk); |
123 | if (!clk_rate) | |
124 | return -EINVAL; | |
d87da323 SP |
125 | |
126 | /* Set PCIe Port PHY to disable SSC */ | |
127 | /* Debug Xtal Type */ | |
128 | mt7621_phy_rmw(phy, RG_PE1_FRC_H_XTAL_REG, | |
129 | RG_PE1_FRC_H_XTAL_TYPE | RG_PE1_H_XTAL_TYPE, | |
130 | RG_PE1_FRC_H_XTAL_TYPE | | |
131 | FIELD_PREP(RG_PE1_H_XTAL_TYPE, 0x00)); | |
132 | ||
133 | /* disable port */ | |
134 | mt7621_phy_rmw(phy, RG_PE1_FRC_PHY_REG, RG_PE1_PHY_EN, | |
135 | RG_PE1_FRC_PHY_EN); | |
136 | ||
137 | if (phy->has_dual_port) { | |
138 | mt7621_phy_rmw(phy, RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH, | |
139 | RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN); | |
140 | } | |
141 | ||
60ece833 | 142 | if (clk_rate == 40000000) { /* 40MHz Xtal */ |
d87da323 SP |
143 | /* Set Pre-divider ratio (for host mode) */ |
144 | mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, RG_PE1_H_PLL_PREDIV, | |
145 | FIELD_PREP(RG_PE1_H_PLL_PREDIV, 0x01)); | |
146 | ||
147 | dev_dbg(dev, "Xtal is 40MHz\n"); | |
60ece833 | 148 | } else if (clk_rate == 25000000) { /* 25MHz Xal */ |
d87da323 SP |
149 | mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, RG_PE1_H_PLL_PREDIV, |
150 | FIELD_PREP(RG_PE1_H_PLL_PREDIV, 0x00)); | |
151 | ||
152 | /* Select feedback clock */ | |
153 | mt7621_phy_rmw(phy, RG_PE1_H_PLL_FBKSEL_REG, | |
154 | RG_PE1_H_PLL_FBKSEL, | |
155 | FIELD_PREP(RG_PE1_H_PLL_FBKSEL, 0x01)); | |
156 | ||
157 | /* DDS NCPO PCW (for host mode) */ | |
158 | mt7621_phy_rmw(phy, RG_PE1_H_LCDDS_SSC_PRD_REG, | |
159 | RG_PE1_H_LCDDS_SSC_PRD, | |
160 | FIELD_PREP(RG_PE1_H_LCDDS_SSC_PRD, 0x00)); | |
161 | ||
162 | /* DDS SSC dither period control */ | |
163 | mt7621_phy_rmw(phy, RG_PE1_H_LCDDS_SSC_PRD_REG, | |
164 | RG_PE1_H_LCDDS_SSC_PRD, | |
165 | FIELD_PREP(RG_PE1_H_LCDDS_SSC_PRD, 0x18d)); | |
166 | ||
167 | /* DDS SSC dither amplitude control */ | |
168 | mt7621_phy_rmw(phy, RG_PE1_H_LCDDS_SSC_DELTA_REG, | |
169 | RG_PE1_H_LCDDS_SSC_DELTA | | |
170 | RG_PE1_H_LCDDS_SSC_DELTA1, | |
171 | FIELD_PREP(RG_PE1_H_LCDDS_SSC_DELTA, 0x4a) | | |
172 | FIELD_PREP(RG_PE1_H_LCDDS_SSC_DELTA1, 0x4a)); | |
173 | ||
174 | dev_dbg(dev, "Xtal is 25MHz\n"); | |
175 | } else { /* 20MHz Xtal */ | |
176 | mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, RG_PE1_H_PLL_PREDIV, | |
177 | FIELD_PREP(RG_PE1_H_PLL_PREDIV, 0x00)); | |
178 | ||
179 | dev_dbg(dev, "Xtal is 20MHz\n"); | |
180 | } | |
181 | ||
182 | /* DDS clock inversion */ | |
183 | mt7621_phy_rmw(phy, RG_PE1_LCDDS_CLK_PH_INV_REG, | |
184 | RG_PE1_LCDDS_CLK_PH_INV, RG_PE1_LCDDS_CLK_PH_INV); | |
185 | ||
186 | /* Set PLL bits */ | |
187 | mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, | |
188 | RG_PE1_H_PLL_BC | RG_PE1_H_PLL_BP | RG_PE1_H_PLL_IR | | |
189 | RG_PE1_H_PLL_IC | RG_PE1_PLL_DIVEN, | |
190 | FIELD_PREP(RG_PE1_H_PLL_BC, 0x02) | | |
191 | FIELD_PREP(RG_PE1_H_PLL_BP, 0x06) | | |
192 | FIELD_PREP(RG_PE1_H_PLL_IR, 0x02) | | |
193 | FIELD_PREP(RG_PE1_H_PLL_IC, 0x01) | | |
194 | FIELD_PREP(RG_PE1_PLL_DIVEN, 0x02)); | |
195 | ||
196 | mt7621_phy_rmw(phy, RG_PE1_H_PLL_BR_REG, RG_PE1_H_PLL_BR, | |
197 | FIELD_PREP(RG_PE1_H_PLL_BR, 0x00)); | |
198 | ||
60ece833 | 199 | if (clk_rate == 40000000) { /* 40MHz Xtal */ |
d87da323 SP |
200 | /* set force mode enable of da_pe1_mstckdiv */ |
201 | mt7621_phy_rmw(phy, RG_PE1_MSTCKDIV_REG, | |
202 | RG_PE1_MSTCKDIV | RG_PE1_FRC_MSTCKDIV, | |
203 | FIELD_PREP(RG_PE1_MSTCKDIV, 0x01) | | |
204 | RG_PE1_FRC_MSTCKDIV); | |
205 | } | |
60ece833 SP |
206 | |
207 | return 0; | |
d87da323 SP |
208 | } |
209 | ||
210 | static int mt7621_pci_phy_init(struct phy *phy) | |
211 | { | |
212 | struct mt7621_pci_phy *mphy = phy_get_drvdata(phy); | |
213 | ||
214 | if (mphy->bypass_pipe_rst) | |
215 | mt7621_bypass_pipe_rst(mphy); | |
216 | ||
60ece833 | 217 | return mt7621_set_phy_for_ssc(mphy); |
d87da323 SP |
218 | } |
219 | ||
220 | static int mt7621_pci_phy_power_on(struct phy *phy) | |
221 | { | |
222 | struct mt7621_pci_phy *mphy = phy_get_drvdata(phy); | |
223 | ||
224 | /* Enable PHY and disable force mode */ | |
225 | mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG, | |
226 | RG_PE1_FRC_PHY_EN, RG_PE1_PHY_EN); | |
227 | ||
228 | if (mphy->has_dual_port) { | |
229 | mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH, | |
230 | RG_PE1_FRC_PHY_EN, RG_PE1_PHY_EN); | |
231 | } | |
232 | ||
233 | return 0; | |
234 | } | |
235 | ||
236 | static int mt7621_pci_phy_power_off(struct phy *phy) | |
237 | { | |
238 | struct mt7621_pci_phy *mphy = phy_get_drvdata(phy); | |
239 | ||
240 | /* Disable PHY */ | |
241 | mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG, | |
242 | RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN); | |
243 | ||
244 | if (mphy->has_dual_port) { | |
245 | mt7621_phy_rmw(mphy, RG_PE1_FRC_PHY_REG + RG_P0_TO_P1_WIDTH, | |
246 | RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN); | |
247 | } | |
248 | ||
249 | return 0; | |
250 | } | |
251 | ||
252 | static int mt7621_pci_phy_exit(struct phy *phy) | |
253 | { | |
254 | return 0; | |
255 | } | |
256 | ||
257 | static const struct phy_ops mt7621_pci_phy_ops = { | |
258 | .init = mt7621_pci_phy_init, | |
259 | .exit = mt7621_pci_phy_exit, | |
260 | .power_on = mt7621_pci_phy_power_on, | |
261 | .power_off = mt7621_pci_phy_power_off, | |
262 | .owner = THIS_MODULE, | |
263 | }; | |
264 | ||
265 | static struct phy *mt7621_pcie_phy_of_xlate(struct device *dev, | |
00ca8a15 | 266 | const struct of_phandle_args *args) |
d87da323 SP |
267 | { |
268 | struct mt7621_pci_phy *mt7621_phy = dev_get_drvdata(dev); | |
269 | ||
270 | if (WARN_ON(args->args[0] >= MAX_PHYS)) | |
271 | return ERR_PTR(-ENODEV); | |
272 | ||
273 | mt7621_phy->has_dual_port = args->args[0]; | |
274 | ||
652a6a2e SP |
275 | dev_dbg(dev, "PHY for 0x%px (dual port = %d)\n", |
276 | mt7621_phy->port_base, mt7621_phy->has_dual_port); | |
d87da323 SP |
277 | |
278 | return mt7621_phy->phy; | |
279 | } | |
280 | ||
281 | static const struct soc_device_attribute mt7621_pci_quirks_match[] = { | |
819b885c JT |
282 | { .soc_id = "mt7621", .revision = "E2" }, |
283 | { /* sentinel */ } | |
d87da323 SP |
284 | }; |
285 | ||
286 | static const struct regmap_config mt7621_pci_phy_regmap_config = { | |
287 | .reg_bits = 32, | |
288 | .val_bits = 32, | |
289 | .reg_stride = 4, | |
290 | .max_register = 0x700, | |
291 | }; | |
292 | ||
293 | static int mt7621_pci_phy_probe(struct platform_device *pdev) | |
294 | { | |
295 | struct device *dev = &pdev->dev; | |
296 | const struct soc_device_attribute *attr; | |
297 | struct phy_provider *provider; | |
298 | struct mt7621_pci_phy *phy; | |
299 | ||
300 | phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); | |
301 | if (!phy) | |
302 | return -ENOMEM; | |
303 | ||
304 | attr = soc_device_match(mt7621_pci_quirks_match); | |
305 | if (attr) | |
306 | phy->bypass_pipe_rst = true; | |
307 | ||
308 | phy->dev = dev; | |
309 | platform_set_drvdata(pdev, phy); | |
310 | ||
311 | phy->port_base = devm_platform_ioremap_resource(pdev, 0); | |
312 | if (IS_ERR(phy->port_base)) { | |
313 | dev_err(dev, "failed to remap phy regs\n"); | |
314 | return PTR_ERR(phy->port_base); | |
315 | } | |
316 | ||
317 | phy->regmap = devm_regmap_init_mmio(phy->dev, phy->port_base, | |
318 | &mt7621_pci_phy_regmap_config); | |
319 | if (IS_ERR(phy->regmap)) | |
320 | return PTR_ERR(phy->regmap); | |
321 | ||
322 | phy->phy = devm_phy_create(dev, dev->of_node, &mt7621_pci_phy_ops); | |
b976c987 | 323 | if (IS_ERR(phy->phy)) { |
d87da323 | 324 | dev_err(dev, "failed to create phy\n"); |
b976c987 | 325 | return PTR_ERR(phy->phy); |
d87da323 SP |
326 | } |
327 | ||
60ece833 SP |
328 | phy->sys_clk = devm_clk_get(dev, NULL); |
329 | if (IS_ERR(phy->sys_clk)) { | |
330 | dev_err(dev, "failed to get phy clock\n"); | |
331 | return PTR_ERR(phy->sys_clk); | |
332 | } | |
333 | ||
d87da323 SP |
334 | phy_set_drvdata(phy->phy, phy); |
335 | ||
336 | provider = devm_of_phy_provider_register(dev, mt7621_pcie_phy_of_xlate); | |
337 | ||
338 | return PTR_ERR_OR_ZERO(provider); | |
339 | } | |
340 | ||
341 | static const struct of_device_id mt7621_pci_phy_ids[] = { | |
342 | { .compatible = "mediatek,mt7621-pci-phy" }, | |
343 | {}, | |
344 | }; | |
8145dcb0 | 345 | MODULE_DEVICE_TABLE(of, mt7621_pci_phy_ids); |
d87da323 SP |
346 | |
347 | static struct platform_driver mt7621_pci_phy_driver = { | |
348 | .probe = mt7621_pci_phy_probe, | |
349 | .driver = { | |
350 | .name = "mt7621-pci-phy", | |
d6e9e8e5 | 351 | .of_match_table = mt7621_pci_phy_ids, |
d87da323 SP |
352 | }, |
353 | }; | |
354 | ||
355 | builtin_platform_driver(mt7621_pci_phy_driver); | |
356 | ||
357 | MODULE_AUTHOR("Sergio Paracuellos <sergio.paracuellos@gmail.com>"); | |
358 | MODULE_DESCRIPTION("MediaTek MT7621 PCIe PHY driver"); | |
359 | MODULE_LICENSE("GPL v2"); |