xref: /linux/drivers/net/pcs/pcs-xpcs-rk.c (revision e7cccfde61367452e9a43257a57eef0a95e7a6bf)
1b9e7cb89SCoia Prant // SPDX-License-Identifier: GPL-2.0
2b9e7cb89SCoia Prant /*
3b9e7cb89SCoia Prant  * Rockchip XPCS platform device driver
4b9e7cb89SCoia Prant  *
5b9e7cb89SCoia Prant  * Based on the Synopsys DesignWare XPCS platform driver.
6b9e7cb89SCoia Prant  * Copyright (C) 2024 Serge Semin
7b9e7cb89SCoia Prant  *
8b9e7cb89SCoia Prant  * Adapted for Rockchip SoCs, with reference to the Rockchip OEM driver.
9b9e7cb89SCoia Prant  * Copyright (C) 2026 Coia Prant
10b9e7cb89SCoia Prant  */
11b9e7cb89SCoia Prant 
12b9e7cb89SCoia Prant #include <linux/atomic.h>
13b9e7cb89SCoia Prant #include <linux/bitfield.h>
14b9e7cb89SCoia Prant #include <linux/clk.h>
15b9e7cb89SCoia Prant #include <linux/device.h>
16b9e7cb89SCoia Prant #include <linux/io.h>
17b9e7cb89SCoia Prant #include <linux/iopoll.h>
18b9e7cb89SCoia Prant #include <linux/math.h>
19b9e7cb89SCoia Prant #include <linux/mdio.h>
20b9e7cb89SCoia Prant #include <linux/module.h>
21b9e7cb89SCoia Prant #include <linux/of.h>
22b9e7cb89SCoia Prant #include <linux/of_platform.h>
23b9e7cb89SCoia Prant #include <linux/pcs/pcs-xpcs-rk.h>
24b9e7cb89SCoia Prant #include <linux/phy.h>
25b9e7cb89SCoia Prant #include <linux/phy/phy.h>
26b9e7cb89SCoia Prant #include <linux/platform_device.h>
27b9e7cb89SCoia Prant #include <linux/pm_domain.h>
28b9e7cb89SCoia Prant #include <linux/pm_runtime.h>
29b9e7cb89SCoia Prant #include <linux/property.h>
30b9e7cb89SCoia Prant #include <linux/sizes.h>
31b9e7cb89SCoia Prant #include <linux/time.h>
32b9e7cb89SCoia Prant 
33b9e7cb89SCoia Prant #include "pcs-xpcs.h"
34b9e7cb89SCoia Prant 
35b9e7cb89SCoia Prant struct dw_xpcs_rk {
36b9e7cb89SCoia Prant 	struct platform_device *pdev;
37b9e7cb89SCoia Prant 	struct mii_bus *bus;
38b9e7cb89SCoia Prant 	void __iomem *reg_base;
39b9e7cb89SCoia Prant 	struct phy *serdes_phy;
40b9e7cb89SCoia Prant 	struct clk *csr_clk;
41b9e7cb89SCoia Prant 	struct clk *eee_clk;
42b9e7cb89SCoia Prant 	u8 eee_mult_fact;
43b9e7cb89SCoia Prant };
44b9e7cb89SCoia Prant 
45b9e7cb89SCoia Prant static ptrdiff_t xpcs_rk_addr_format(int dev, int reg)
46b9e7cb89SCoia Prant {
47b9e7cb89SCoia Prant 	return FIELD_PREP(0x70000, dev) | FIELD_PREP(0xffff, reg);
48b9e7cb89SCoia Prant }
49b9e7cb89SCoia Prant 
50b9e7cb89SCoia Prant static int xpcs_rk_read_reg(struct dw_xpcs_rk *pxpcs, int dev, int reg)
51b9e7cb89SCoia Prant {
52b9e7cb89SCoia Prant 	ptrdiff_t csr;
53b9e7cb89SCoia Prant 	int ret;
54b9e7cb89SCoia Prant 
55b9e7cb89SCoia Prant 	csr = xpcs_rk_addr_format(dev, reg);
56b9e7cb89SCoia Prant 
57b9e7cb89SCoia Prant 	ret = pm_runtime_resume_and_get(&pxpcs->pdev->dev);
58b9e7cb89SCoia Prant 	if (ret)
59b9e7cb89SCoia Prant 		return ret;
60b9e7cb89SCoia Prant 
61b9e7cb89SCoia Prant 	ret = readl(pxpcs->reg_base + (csr << 2)) & 0xffff;
62b9e7cb89SCoia Prant 
63b9e7cb89SCoia Prant 	pm_runtime_put(&pxpcs->pdev->dev);
64b9e7cb89SCoia Prant 	return ret;
65b9e7cb89SCoia Prant }
66b9e7cb89SCoia Prant 
67b9e7cb89SCoia Prant static int xpcs_rk_write_reg(struct dw_xpcs_rk *pxpcs, int dev, int reg, u16 val)
68b9e7cb89SCoia Prant {
69b9e7cb89SCoia Prant 	ptrdiff_t csr;
70b9e7cb89SCoia Prant 	int ret;
71b9e7cb89SCoia Prant 
72b9e7cb89SCoia Prant 	csr = xpcs_rk_addr_format(dev, reg);
73b9e7cb89SCoia Prant 
74b9e7cb89SCoia Prant 	ret = pm_runtime_resume_and_get(&pxpcs->pdev->dev);
75b9e7cb89SCoia Prant 	if (ret)
76b9e7cb89SCoia Prant 		return ret;
77b9e7cb89SCoia Prant 
78b9e7cb89SCoia Prant 	writel(val, pxpcs->reg_base + (csr << 2));
79b9e7cb89SCoia Prant 
80b9e7cb89SCoia Prant 	pm_runtime_put(&pxpcs->pdev->dev);
81b9e7cb89SCoia Prant 	return 0;
82b9e7cb89SCoia Prant }
83b9e7cb89SCoia Prant 
84b9e7cb89SCoia Prant #define ROCKCHIP_MMD_MII1	2
85b9e7cb89SCoia Prant #define ROCKCHIP_MMD_MII2	3
86b9e7cb89SCoia Prant #define ROCKCHIP_MMD_MII3	4
87b9e7cb89SCoia Prant #define ROCKCHIP_MMD_PMAPMD	6
88b9e7cb89SCoia Prant #define ROCKCHIP_MMD_MII	7
89b9e7cb89SCoia Prant 
90b9e7cb89SCoia Prant static bool xpcs_rk_mdio_addr_validate(int addr)
91b9e7cb89SCoia Prant {
92b9e7cb89SCoia Prant 	return !(addr < 0 || addr > 3);
93b9e7cb89SCoia Prant }
94b9e7cb89SCoia Prant 
95b9e7cb89SCoia Prant static int xpcs_rk_mdio_read_remapping(int addr, int dev)
96b9e7cb89SCoia Prant {
97b9e7cb89SCoia Prant 	switch (dev) {
98b9e7cb89SCoia Prant 	case MDIO_MMD_PMAPMD:
99b9e7cb89SCoia Prant 		return ROCKCHIP_MMD_PMAPMD;
100b9e7cb89SCoia Prant 	case MDIO_MMD_VEND2:
101b9e7cb89SCoia Prant 		break;
102b9e7cb89SCoia Prant 	default:
103b9e7cb89SCoia Prant 		return -ENXIO;
104b9e7cb89SCoia Prant 	}
105b9e7cb89SCoia Prant 
106b9e7cb89SCoia Prant 	/*
107b9e7cb89SCoia Prant 	 * Reads are redirected by hardware to the port's read-only mirror;
108b9e7cb89SCoia Prant 	 * only writes have to be targeted at MII (see the write path).
109b9e7cb89SCoia Prant 	 */
110b9e7cb89SCoia Prant 	switch (addr) {
111b9e7cb89SCoia Prant 	case 0:
112b9e7cb89SCoia Prant 		return ROCKCHIP_MMD_MII;
113b9e7cb89SCoia Prant 	case 1:
114b9e7cb89SCoia Prant 		return ROCKCHIP_MMD_MII1;
115b9e7cb89SCoia Prant 	case 2:
116b9e7cb89SCoia Prant 		return ROCKCHIP_MMD_MII2;
117b9e7cb89SCoia Prant 	case 3:
118b9e7cb89SCoia Prant 		return ROCKCHIP_MMD_MII3;
119b9e7cb89SCoia Prant 	default:
120b9e7cb89SCoia Prant 		return -ENODEV;
121b9e7cb89SCoia Prant 	}
122b9e7cb89SCoia Prant }
123b9e7cb89SCoia Prant 
124b9e7cb89SCoia Prant static int xpcs_rk_mdio_write_remapping(int addr, int dev, int reg)
125b9e7cb89SCoia Prant {
126b9e7cb89SCoia Prant 	switch (dev) {
127b9e7cb89SCoia Prant 	case MDIO_MMD_PMAPMD:
128b9e7cb89SCoia Prant 		return ROCKCHIP_MMD_PMAPMD;
129b9e7cb89SCoia Prant 	case MDIO_MMD_VEND2:
130b9e7cb89SCoia Prant 		break;
131b9e7cb89SCoia Prant 	default:
132b9e7cb89SCoia Prant 		return -ENXIO;
133b9e7cb89SCoia Prant 	}
134b9e7cb89SCoia Prant 
135b9e7cb89SCoia Prant 	/*
136b9e7cb89SCoia Prant 	 * These registers physically live only in MII (the management port).
137b9e7cb89SCoia Prant 	 * Ports 1-3 expose read-only mirrors of these bits, so writes must
138b9e7cb89SCoia Prant 	 * always target MII; the read path remaps per address and the
139b9e7cb89SCoia Prant 	 * hardware redirects to the port's mirror.
140b9e7cb89SCoia Prant 	 */
141b9e7cb89SCoia Prant 	switch (reg) {
142b9e7cb89SCoia Prant 	case DW_VR_MII_AN_CTRL:
143b9e7cb89SCoia Prant 	case DW_VR_MII_AN_INTR_STS:
144b9e7cb89SCoia Prant 	case DW_VR_MII_EEE_MCTRL0:
145b9e7cb89SCoia Prant 	case DW_VR_MII_EEE_MCTRL1:
146b9e7cb89SCoia Prant 	case DW_VR_MII_DIG_CTRL2:
147b9e7cb89SCoia Prant 		return ROCKCHIP_MMD_MII;
148b9e7cb89SCoia Prant 	default:
149b9e7cb89SCoia Prant 		break;
150b9e7cb89SCoia Prant 	}
151b9e7cb89SCoia Prant 
152b9e7cb89SCoia Prant 	switch (addr) {
153b9e7cb89SCoia Prant 	case 0:
154b9e7cb89SCoia Prant 		return ROCKCHIP_MMD_MII;
155b9e7cb89SCoia Prant 	case 1:
156b9e7cb89SCoia Prant 		return ROCKCHIP_MMD_MII1;
157b9e7cb89SCoia Prant 	case 2:
158b9e7cb89SCoia Prant 		return ROCKCHIP_MMD_MII2;
159b9e7cb89SCoia Prant 	case 3:
160b9e7cb89SCoia Prant 		return ROCKCHIP_MMD_MII3;
161b9e7cb89SCoia Prant 	default:
162b9e7cb89SCoia Prant 		return -ENODEV;
163b9e7cb89SCoia Prant 	}
164b9e7cb89SCoia Prant }
165b9e7cb89SCoia Prant 
166b9e7cb89SCoia Prant static int xpcs_rk_read_c22(struct mii_bus *bus, int addr, int reg)
167b9e7cb89SCoia Prant {
168b9e7cb89SCoia Prant 	struct dw_xpcs_rk *pxpcs = bus->priv;
169b9e7cb89SCoia Prant 	int dev;
170b9e7cb89SCoia Prant 
171b9e7cb89SCoia Prant 	if (!xpcs_rk_mdio_addr_validate(addr))
172b9e7cb89SCoia Prant 		return -ENODEV;
173b9e7cb89SCoia Prant 
174b9e7cb89SCoia Prant 	dev = xpcs_rk_mdio_read_remapping(addr, MDIO_MMD_VEND2);
175b9e7cb89SCoia Prant 	if (dev < 0)
176b9e7cb89SCoia Prant 		return 0xffff;
177b9e7cb89SCoia Prant 
178b9e7cb89SCoia Prant 	return xpcs_rk_read_reg(pxpcs, dev, reg);
179b9e7cb89SCoia Prant }
180b9e7cb89SCoia Prant 
181b9e7cb89SCoia Prant static int xpcs_rk_write_c22(struct mii_bus *bus, int addr, int reg, u16 val)
182b9e7cb89SCoia Prant {
183b9e7cb89SCoia Prant 	struct dw_xpcs_rk *pxpcs = bus->priv;
184b9e7cb89SCoia Prant 	int dev;
185b9e7cb89SCoia Prant 
186b9e7cb89SCoia Prant 	if (!xpcs_rk_mdio_addr_validate(addr))
187b9e7cb89SCoia Prant 		return -ENODEV;
188b9e7cb89SCoia Prant 
189b9e7cb89SCoia Prant 	dev = xpcs_rk_mdio_write_remapping(addr, MDIO_MMD_VEND2, reg);
190b9e7cb89SCoia Prant 	if (dev < 0)
191b9e7cb89SCoia Prant 		return 0;
192b9e7cb89SCoia Prant 
193b9e7cb89SCoia Prant 	return xpcs_rk_write_reg(pxpcs, dev, reg, val);
194b9e7cb89SCoia Prant }
195b9e7cb89SCoia Prant 
196b9e7cb89SCoia Prant static int xpcs_rk_read_c45(struct mii_bus *bus, int addr, int dev, int reg)
197b9e7cb89SCoia Prant {
198b9e7cb89SCoia Prant 	struct dw_xpcs_rk *pxpcs = bus->priv;
199b9e7cb89SCoia Prant 
200b9e7cb89SCoia Prant 	if (!xpcs_rk_mdio_addr_validate(addr))
201b9e7cb89SCoia Prant 		return -ENODEV;
202b9e7cb89SCoia Prant 
203b9e7cb89SCoia Prant 	dev = xpcs_rk_mdio_read_remapping(addr, dev);
204b9e7cb89SCoia Prant 	if (dev < 0)
205b9e7cb89SCoia Prant 		return 0xffff;
206b9e7cb89SCoia Prant 
207b9e7cb89SCoia Prant 	return xpcs_rk_read_reg(pxpcs, dev, reg);
208b9e7cb89SCoia Prant }
209b9e7cb89SCoia Prant 
210b9e7cb89SCoia Prant static int xpcs_rk_write_c45(struct mii_bus *bus, int addr, int dev, int reg, u16 val)
211b9e7cb89SCoia Prant {
212b9e7cb89SCoia Prant 	struct dw_xpcs_rk *pxpcs = bus->priv;
213b9e7cb89SCoia Prant 
214b9e7cb89SCoia Prant 	if (!xpcs_rk_mdio_addr_validate(addr))
215b9e7cb89SCoia Prant 		return -ENODEV;
216b9e7cb89SCoia Prant 
217b9e7cb89SCoia Prant 	dev = xpcs_rk_mdio_write_remapping(addr, dev, reg);
218b9e7cb89SCoia Prant 	if (dev < 0)
219b9e7cb89SCoia Prant 		return 0;
220b9e7cb89SCoia Prant 
221b9e7cb89SCoia Prant 	return xpcs_rk_write_reg(pxpcs, dev, reg, val);
222b9e7cb89SCoia Prant }
223b9e7cb89SCoia Prant 
224b9e7cb89SCoia Prant static struct dw_xpcs_rk *xpcs_rk_create_data(struct platform_device *pdev)
225b9e7cb89SCoia Prant {
226b9e7cb89SCoia Prant 	struct dw_xpcs_rk *pxpcs;
227b9e7cb89SCoia Prant 
228b9e7cb89SCoia Prant 	pxpcs = devm_kzalloc(&pdev->dev, sizeof(*pxpcs), GFP_KERNEL);
229b9e7cb89SCoia Prant 	if (!pxpcs)
230b9e7cb89SCoia Prant 		return ERR_PTR(-ENOMEM);
231b9e7cb89SCoia Prant 
232b9e7cb89SCoia Prant 	pxpcs->pdev = pdev;
233b9e7cb89SCoia Prant 
234b9e7cb89SCoia Prant 	dev_set_drvdata(&pdev->dev, pxpcs);
235b9e7cb89SCoia Prant 
236b9e7cb89SCoia Prant 	return pxpcs;
237b9e7cb89SCoia Prant }
238b9e7cb89SCoia Prant 
239b9e7cb89SCoia Prant static int xpcs_rk_serdes_phy_init(struct dw_xpcs_rk *pxpcs)
240b9e7cb89SCoia Prant {
241b9e7cb89SCoia Prant 	struct device *dev = &pxpcs->pdev->dev;
242b9e7cb89SCoia Prant 
243b9e7cb89SCoia Prant 	pxpcs->serdes_phy = devm_phy_get(dev, NULL);
244b9e7cb89SCoia Prant 	if (IS_ERR(pxpcs->serdes_phy))
245b9e7cb89SCoia Prant 		return dev_err_probe(dev, PTR_ERR(pxpcs->serdes_phy),
246b9e7cb89SCoia Prant 					"Failed to get SerDes PHY\n");
247b9e7cb89SCoia Prant 
248b9e7cb89SCoia Prant 	return 0;
249b9e7cb89SCoia Prant }
250b9e7cb89SCoia Prant 
251b9e7cb89SCoia Prant static void xpcs_rk_serdes_phy_poweroff(void *data)
252b9e7cb89SCoia Prant {
253b9e7cb89SCoia Prant 	struct dw_xpcs_rk *pxpcs = data;
254b9e7cb89SCoia Prant 	struct device *dev = &pxpcs->pdev->dev;
255b9e7cb89SCoia Prant 
256b9e7cb89SCoia Prant 	phy_power_off(pxpcs->serdes_phy);
257b9e7cb89SCoia Prant 	phy_exit(pxpcs->serdes_phy);
258b9e7cb89SCoia Prant 
259b9e7cb89SCoia Prant 	dev_pm_genpd_rpm_always_on(dev, false);
260b9e7cb89SCoia Prant }
261b9e7cb89SCoia Prant 
262b9e7cb89SCoia Prant static int xpcs_rk_serdes_phy_poweron(struct dw_xpcs_rk *pxpcs)
263b9e7cb89SCoia Prant {
264b9e7cb89SCoia Prant 	struct device *dev = &pxpcs->pdev->dev;
265b9e7cb89SCoia Prant 	int ret;
266b9e7cb89SCoia Prant 
267b9e7cb89SCoia Prant 	/*
268b9e7cb89SCoia Prant 	 * The power domain is required and must be enabled, which allows us to
269b9e7cb89SCoia Prant 	 * dynamically turn the CSR clock on/off using PM while keeping the PCS
270b9e7cb89SCoia Prant 	 * powered on.
271b9e7cb89SCoia Prant 	 */
272b9e7cb89SCoia Prant 	ret = dev_pm_genpd_rpm_always_on(dev, true);
273b9e7cb89SCoia Prant 	if (ret) {
274b9e7cb89SCoia Prant 		dev_err(dev, "Failed to power on power-domains\n");
275b9e7cb89SCoia Prant 		return ret;
276b9e7cb89SCoia Prant 	}
277b9e7cb89SCoia Prant 
278b9e7cb89SCoia Prant 	ret = phy_init(pxpcs->serdes_phy);
279b9e7cb89SCoia Prant 	if (ret) {
280b9e7cb89SCoia Prant 		dev_err(dev, "Failed to init SerDes PHY\n");
281b9e7cb89SCoia Prant 		goto pm_domain;
282b9e7cb89SCoia Prant 	}
283b9e7cb89SCoia Prant 
284b9e7cb89SCoia Prant 	ret = phy_power_on(pxpcs->serdes_phy);
285b9e7cb89SCoia Prant 	if (ret) {
286b9e7cb89SCoia Prant 		dev_err(dev, "Failed to power on SerDes PHY\n");
287b9e7cb89SCoia Prant 		goto serdes_phy;
288b9e7cb89SCoia Prant 	}
289b9e7cb89SCoia Prant 
290b9e7cb89SCoia Prant 	ret = devm_add_action_or_reset(dev, xpcs_rk_serdes_phy_poweroff, pxpcs);
291b9e7cb89SCoia Prant 	if (ret) {
292b9e7cb89SCoia Prant 		dev_err(dev, "Failed to register devm for SerDes PHY: %d\n", ret);
293b9e7cb89SCoia Prant 		return ret;
294b9e7cb89SCoia Prant 	}
295b9e7cb89SCoia Prant 
296b9e7cb89SCoia Prant 	return 0;
297b9e7cb89SCoia Prant 
298b9e7cb89SCoia Prant serdes_phy:
299b9e7cb89SCoia Prant 	phy_exit(pxpcs->serdes_phy);
300b9e7cb89SCoia Prant pm_domain:
301b9e7cb89SCoia Prant 	dev_pm_genpd_rpm_always_on(dev, false);
302b9e7cb89SCoia Prant 	return ret;
303b9e7cb89SCoia Prant }
304b9e7cb89SCoia Prant 
305b9e7cb89SCoia Prant static int xpcs_rk_init_res(struct dw_xpcs_rk *pxpcs)
306b9e7cb89SCoia Prant {
307b9e7cb89SCoia Prant 	struct platform_device *pdev = pxpcs->pdev;
308b9e7cb89SCoia Prant 	struct device *dev = &pdev->dev;
309b9e7cb89SCoia Prant 	struct resource *res;
310b9e7cb89SCoia Prant 
311b9e7cb89SCoia Prant 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
312b9e7cb89SCoia Prant 	if (!res) {
313b9e7cb89SCoia Prant 		dev_err(dev, "No reg-space found\n");
314b9e7cb89SCoia Prant 		return -EINVAL;
315b9e7cb89SCoia Prant 	}
316b9e7cb89SCoia Prant 
317b9e7cb89SCoia Prant 	if (resource_size(res) < SZ_2M) {
318b9e7cb89SCoia Prant 		dev_err(dev, "Invalid reg-space size\n");
319b9e7cb89SCoia Prant 		return -EINVAL;
320b9e7cb89SCoia Prant 	}
321b9e7cb89SCoia Prant 
322b9e7cb89SCoia Prant 	pxpcs->reg_base = devm_ioremap_resource(dev, res);
323b9e7cb89SCoia Prant 	if (IS_ERR(pxpcs->reg_base)) {
324b9e7cb89SCoia Prant 		dev_err(dev, "Failed to map reg-space\n");
325b9e7cb89SCoia Prant 		return PTR_ERR(pxpcs->reg_base);
326b9e7cb89SCoia Prant 	}
327b9e7cb89SCoia Prant 
328b9e7cb89SCoia Prant 	return 0;
329b9e7cb89SCoia Prant }
330b9e7cb89SCoia Prant 
331b9e7cb89SCoia Prant static void xpcs_rk_exit_clk(void *data)
332b9e7cb89SCoia Prant {
333b9e7cb89SCoia Prant 	struct dw_xpcs_rk *pxpcs = data;
334b9e7cb89SCoia Prant 	struct device *dev = &pxpcs->pdev->dev;
335b9e7cb89SCoia Prant 
336b9e7cb89SCoia Prant 	pm_runtime_force_suspend(dev);
337b9e7cb89SCoia Prant 	clk_disable_unprepare(pxpcs->eee_clk);
338b9e7cb89SCoia Prant }
339b9e7cb89SCoia Prant 
340b9e7cb89SCoia Prant static int xpcs_rk_init_clk(struct dw_xpcs_rk *pxpcs)
341b9e7cb89SCoia Prant {
342b9e7cb89SCoia Prant 	struct device *dev = &pxpcs->pdev->dev;
343b9e7cb89SCoia Prant 	unsigned long rate;
344b9e7cb89SCoia Prant 	u64 mult;
345b9e7cb89SCoia Prant 	int ret;
346b9e7cb89SCoia Prant 
347b9e7cb89SCoia Prant 	pxpcs->csr_clk = devm_clk_get(dev, "csr");
348b9e7cb89SCoia Prant 	if (IS_ERR(pxpcs->csr_clk))
349b9e7cb89SCoia Prant 		return dev_err_probe(dev, PTR_ERR(pxpcs->csr_clk),
350b9e7cb89SCoia Prant 					 "Failed to get CSR clock\n");
351b9e7cb89SCoia Prant 
352b9e7cb89SCoia Prant 	pxpcs->eee_clk = devm_clk_get(dev, "eee");
353b9e7cb89SCoia Prant 	if (IS_ERR(pxpcs->eee_clk))
354b9e7cb89SCoia Prant 		return dev_err_probe(dev, PTR_ERR(pxpcs->eee_clk),
355b9e7cb89SCoia Prant 					 "Failed to get EEE clock\n");
356b9e7cb89SCoia Prant 
357b9e7cb89SCoia Prant 	ret = clk_prepare_enable(pxpcs->eee_clk);
358b9e7cb89SCoia Prant 	if (ret) {
359b9e7cb89SCoia Prant 		dev_err(dev, "Failed to enable EEE clock\n");
360b9e7cb89SCoia Prant 		return ret;
361b9e7cb89SCoia Prant 	}
362b9e7cb89SCoia Prant 
363b9e7cb89SCoia Prant 	pm_runtime_set_suspended(dev);
364b9e7cb89SCoia Prant 	pm_runtime_enable(dev);
365b9e7cb89SCoia Prant 
366b9e7cb89SCoia Prant 	ret = devm_add_action_or_reset(dev, xpcs_rk_exit_clk, pxpcs);
367b9e7cb89SCoia Prant 	if (ret) {
368*e7cccfdeSCoia Prant 		dev_err(dev, "Failed to register devm for xpcs_rk_exit_clk: %d\n", ret);
369b9e7cb89SCoia Prant 		return ret;
370b9e7cb89SCoia Prant 	}
371b9e7cb89SCoia Prant 
372b9e7cb89SCoia Prant 	/*
373b9e7cb89SCoia Prant 	 * Compute the multiplier for the EEE clock so that
374b9e7cb89SCoia Prant 	 * clk_eee_period * (mult_fact + 1) falls within 80..120 ns.
375b9e7cb89SCoia Prant 	 *
376b9e7cb89SCoia Prant 	 * On RK3568, clk_xpcs_eee is muxed between gpll200 (200 MHz, 5 ns)
377b9e7cb89SCoia Prant 	 * and cpll125 (125 MHz, 8 ns), selected by CRU_CLKSEL_CON29 bit 13.
378b9e7cb89SCoia Prant 	 * The reset value is 0 (200 MHz), but derive the value at runtime to
379b9e7cb89SCoia Prant 	 * stay correct if the mux is changed by a board.
380b9e7cb89SCoia Prant 	 *
381b9e7cb89SCoia Prant 	 * Use a 64-bit intermediate: on 32-bit builds, 100 * 200000000
382b9e7cb89SCoia Prant 	 * does not fit in unsigned long. Clamp to the 4-bit
383b9e7cb89SCoia Prant 	 * DW_VR_MII_EEE_MULT_FACT_100NS field. The mux only provides
384b9e7cb89SCoia Prant 	 * 125 MHz or 200 MHz, so the rate cannot drop below the 5 MHz
385b9e7cb89SCoia Prant 	 * threshold where DIV_ROUND_CLOSEST_ULL() would return 0 and the
386b9e7cb89SCoia Prant 	 * subtraction below would underflow.
387b9e7cb89SCoia Prant 	 */
388b9e7cb89SCoia Prant 	rate = clk_get_rate(pxpcs->eee_clk);
389b9e7cb89SCoia Prant 	if (!rate)
390b9e7cb89SCoia Prant 		return dev_err_probe(dev, -EINVAL, "Invalid EEE clock rate\n");
391b9e7cb89SCoia Prant 
392b9e7cb89SCoia Prant 	mult = DIV_ROUND_CLOSEST_ULL(100ULL * rate, NSEC_PER_SEC) - 1;
393b9e7cb89SCoia Prant 	pxpcs->eee_mult_fact = min_t(u64, mult, 15);
394b9e7cb89SCoia Prant 	return 0;
395b9e7cb89SCoia Prant }
396b9e7cb89SCoia Prant 
397b9e7cb89SCoia Prant static int xpcs_rk_init_bus(struct dw_xpcs_rk *pxpcs)
398b9e7cb89SCoia Prant {
399b9e7cb89SCoia Prant 	struct device *dev = &pxpcs->pdev->dev;
400b9e7cb89SCoia Prant 	static atomic_t id = ATOMIC_INIT(-1);
401b9e7cb89SCoia Prant 	struct mii_bus *bus;
402b9e7cb89SCoia Prant 	int ret;
403b9e7cb89SCoia Prant 
404b9e7cb89SCoia Prant 	bus = devm_mdiobus_alloc_size(dev, 0);
405b9e7cb89SCoia Prant 	if (!bus)
406b9e7cb89SCoia Prant 		return -ENOMEM;
407b9e7cb89SCoia Prant 
408b9e7cb89SCoia Prant 	bus->name = "Rockchip DW XPCS MCI/APB3";
409b9e7cb89SCoia Prant 	bus->read = xpcs_rk_read_c22;
410b9e7cb89SCoia Prant 	bus->write = xpcs_rk_write_c22;
411b9e7cb89SCoia Prant 	bus->read_c45 = xpcs_rk_read_c45;
412b9e7cb89SCoia Prant 	bus->write_c45 = xpcs_rk_write_c45;
413b9e7cb89SCoia Prant 	bus->phy_mask = ~0;
414b9e7cb89SCoia Prant 	bus->parent = dev;
415b9e7cb89SCoia Prant 	bus->priv = pxpcs;
416b9e7cb89SCoia Prant 
417b9e7cb89SCoia Prant 	snprintf(bus->id, MII_BUS_ID_SIZE,
418b9e7cb89SCoia Prant 		 "rockchip_dwxpcs-%x", atomic_inc_return(&id));
419b9e7cb89SCoia Prant 
420b9e7cb89SCoia Prant 	/*
421b9e7cb89SCoia Prant 	 * MDIO-bus here serves as just a back-end engine abstracting out
422b9e7cb89SCoia Prant 	 * the MDIO and MCI/APB3 IO interfaces utilized for the Rockchip DWXPCS CSRs
423b9e7cb89SCoia Prant 	 * access.
424b9e7cb89SCoia Prant 	 */
425b9e7cb89SCoia Prant 	ret = devm_mdiobus_register(dev, bus);
426b9e7cb89SCoia Prant 	if (ret) {
427b9e7cb89SCoia Prant 		dev_err(dev, "Failed to create MDIO bus\n");
428b9e7cb89SCoia Prant 		return ret;
429b9e7cb89SCoia Prant 	}
430b9e7cb89SCoia Prant 
431b9e7cb89SCoia Prant 	pxpcs->bus = bus;
432b9e7cb89SCoia Prant 	return 0;
433b9e7cb89SCoia Prant }
434b9e7cb89SCoia Prant 
435b9e7cb89SCoia Prant static int xpcs_rk_probe(struct platform_device *pdev)
436b9e7cb89SCoia Prant {
437b9e7cb89SCoia Prant 	struct dw_xpcs_rk *pxpcs;
438b9e7cb89SCoia Prant 	int ret;
439b9e7cb89SCoia Prant 
440b9e7cb89SCoia Prant 	pxpcs = xpcs_rk_create_data(pdev);
441b9e7cb89SCoia Prant 	if (IS_ERR(pxpcs))
442b9e7cb89SCoia Prant 		return PTR_ERR(pxpcs);
443b9e7cb89SCoia Prant 
444b9e7cb89SCoia Prant 	/*
445b9e7cb89SCoia Prant 	 * The XPCS lives in the PD_PIPE power domain. The domain must be
446b9e7cb89SCoia Prant 	 * powered on before any register access, otherwise the SoC will
447b9e7cb89SCoia Prant 	 * trigger a synchronous external abort (SError).
448b9e7cb89SCoia Prant 	 *
449b9e7cb89SCoia Prant 	 * Accessing the XPCS registers also requires a TX clock from the
450b9e7cb89SCoia Prant 	 * SerDes, which is needed for the soft reset.
451b9e7cb89SCoia Prant 	 */
452b9e7cb89SCoia Prant 	ret = xpcs_rk_serdes_phy_init(pxpcs);
453b9e7cb89SCoia Prant 	if (ret)
454b9e7cb89SCoia Prant 		return ret;
455b9e7cb89SCoia Prant 
456b9e7cb89SCoia Prant 	ret = xpcs_rk_serdes_phy_poweron(pxpcs);
457b9e7cb89SCoia Prant 	if (ret)
458b9e7cb89SCoia Prant 		return ret;
459b9e7cb89SCoia Prant 
460b9e7cb89SCoia Prant 	ret = xpcs_rk_init_res(pxpcs);
461b9e7cb89SCoia Prant 	if (ret)
462b9e7cb89SCoia Prant 		return ret;
463b9e7cb89SCoia Prant 
464b9e7cb89SCoia Prant 	ret = xpcs_rk_init_clk(pxpcs);
465b9e7cb89SCoia Prant 	if (ret)
466b9e7cb89SCoia Prant 		return ret;
467b9e7cb89SCoia Prant 
468b9e7cb89SCoia Prant 	ret = xpcs_rk_init_bus(pxpcs);
469b9e7cb89SCoia Prant 	if (ret)
470b9e7cb89SCoia Prant 		return ret;
471b9e7cb89SCoia Prant 
472b9e7cb89SCoia Prant 	return 0;
473b9e7cb89SCoia Prant }
474b9e7cb89SCoia Prant 
475b9e7cb89SCoia Prant static const struct of_device_id xpcs_rk_of_ids[] = {
476b9e7cb89SCoia Prant 	{ .compatible = "rockchip,rk3568-xpcs" },
477b9e7cb89SCoia Prant 	{ /* sentinel */ },
478b9e7cb89SCoia Prant };
479b9e7cb89SCoia Prant MODULE_DEVICE_TABLE(of, xpcs_rk_of_ids);
480b9e7cb89SCoia Prant 
481b9e7cb89SCoia Prant struct dw_xpcs *xpcs_rk_create(struct device *dev, struct device_node *pcs, int port)
482b9e7cb89SCoia Prant {
483b9e7cb89SCoia Prant 	struct platform_device *pdev;
484b9e7cb89SCoia Prant 	struct dw_xpcs_rk *pxpcs;
485b9e7cb89SCoia Prant 	struct dw_xpcs *xpcs;
486b9e7cb89SCoia Prant 
487b9e7cb89SCoia Prant 	if (!of_device_is_available(pcs))
488b9e7cb89SCoia Prant 		return ERR_PTR(-ENODEV);
489b9e7cb89SCoia Prant 
490b9e7cb89SCoia Prant 	if (!of_match_node(xpcs_rk_of_ids, pcs))
491b9e7cb89SCoia Prant 		return ERR_PTR(-EINVAL);
492b9e7cb89SCoia Prant 
493b9e7cb89SCoia Prant 	if (!xpcs_rk_mdio_addr_validate(port))
494b9e7cb89SCoia Prant 		return ERR_PTR(-EINVAL);
495b9e7cb89SCoia Prant 
496b9e7cb89SCoia Prant 	pdev = of_find_device_by_node(pcs);
497b9e7cb89SCoia Prant 	if (!pdev)
498b9e7cb89SCoia Prant 		return ERR_PTR(-EPROBE_DEFER);
499b9e7cb89SCoia Prant 
500b9e7cb89SCoia Prant 	/*
501b9e7cb89SCoia Prant 	 * Establish the device link before reading the supplier's drvdata.
502b9e7cb89SCoia Prant 	 * device_link_add() does not fail on a supplier that is unbinding;
503b9e7cb89SCoia Prant 	 * it creates the link in DL_STATE_SUPPLIER_UNBIND.
504b9e7cb89SCoia Prant 	 *
505b9e7cb89SCoia Prant 	 * The check below only confirms that the supplier is bound at this
506b9e7cb89SCoia Prant 	 * point. It does not fully protect against a concurrent unbind
507b9e7cb89SCoia Prant 	 * between here and xpcs_create_mdiodev(); closing that window would
508b9e7cb89SCoia Prant 	 * require reading a managed link's status or device_lock(), neither
509b9e7cb89SCoia Prant 	 * of which is acceptable on this path. The window is small and
510b9e7cb89SCoia Prant 	 * requires an explicit unbind from userspace. Discussed on
511b9e7cb89SCoia Prant 	 * driver-core.
512b9e7cb89SCoia Prant 	 *
513b9e7cb89SCoia Prant 	 * The link is released automatically when the consumer device is
514b9e7cb89SCoia Prant 	 * destroyed (DL_FLAG_AUTOREMOVE_CONSUMER), so no explicit
515b9e7cb89SCoia Prant 	 * device_link_remove() is needed on the failure paths.
516b9e7cb89SCoia Prant 	 */
517b9e7cb89SCoia Prant 	if (!device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_CONSUMER)) {
518b9e7cb89SCoia Prant 		put_device(&pdev->dev);
519b9e7cb89SCoia Prant 		return ERR_PTR(-EPROBE_DEFER);
520b9e7cb89SCoia Prant 	}
521b9e7cb89SCoia Prant 
522b9e7cb89SCoia Prant 	if (READ_ONCE(pdev->dev.links.status) != DL_DEV_DRIVER_BOUND) {
523b9e7cb89SCoia Prant 		put_device(&pdev->dev);
524b9e7cb89SCoia Prant 		return ERR_PTR(-EPROBE_DEFER);
525b9e7cb89SCoia Prant 	}
526b9e7cb89SCoia Prant 
527b9e7cb89SCoia Prant 	pxpcs = platform_get_drvdata(pdev);
528b9e7cb89SCoia Prant 	if (!pxpcs || !pxpcs->bus) {
529b9e7cb89SCoia Prant 		put_device(&pdev->dev);
530b9e7cb89SCoia Prant 		return ERR_PTR(-EPROBE_DEFER);
531b9e7cb89SCoia Prant 	}
532b9e7cb89SCoia Prant 
533b9e7cb89SCoia Prant 	xpcs = xpcs_create_mdiodev(pxpcs->bus, port);
534b9e7cb89SCoia Prant 	if (IS_ERR(xpcs)) {
535b9e7cb89SCoia Prant 		put_device(&pdev->dev);
536b9e7cb89SCoia Prant 		return xpcs;
537b9e7cb89SCoia Prant 	}
538b9e7cb89SCoia Prant 
539b9e7cb89SCoia Prant 	xpcs_config_eee_mult_fact(xpcs, pxpcs->eee_mult_fact);
540b9e7cb89SCoia Prant 	put_device(&pdev->dev);
541b9e7cb89SCoia Prant 	return xpcs;
542b9e7cb89SCoia Prant }
543b9e7cb89SCoia Prant EXPORT_SYMBOL_GPL(xpcs_rk_create);
544b9e7cb89SCoia Prant 
545b9e7cb89SCoia Prant static int xpcs_rk_pm_runtime_suspend(struct device *dev)
546b9e7cb89SCoia Prant {
547b9e7cb89SCoia Prant 	struct dw_xpcs_rk *pxpcs = dev_get_drvdata(dev);
548b9e7cb89SCoia Prant 
549b9e7cb89SCoia Prant 	clk_disable_unprepare(pxpcs->csr_clk);
550b9e7cb89SCoia Prant 
551b9e7cb89SCoia Prant 	return 0;
552b9e7cb89SCoia Prant }
553b9e7cb89SCoia Prant 
554b9e7cb89SCoia Prant static int xpcs_rk_pm_runtime_resume(struct device *dev)
555b9e7cb89SCoia Prant {
556b9e7cb89SCoia Prant 	struct dw_xpcs_rk *pxpcs = dev_get_drvdata(dev);
557b9e7cb89SCoia Prant 
558b9e7cb89SCoia Prant 	return clk_prepare_enable(pxpcs->csr_clk);
559b9e7cb89SCoia Prant }
560b9e7cb89SCoia Prant 
561b9e7cb89SCoia Prant static int xpcs_rk_system_suspend(struct device *dev)
562b9e7cb89SCoia Prant {
563b9e7cb89SCoia Prant 	/*
564b9e7cb89SCoia Prant 	 * Keep the PD_PIPE power domain on during system suspend.
565b9e7cb89SCoia Prant 	 *
566b9e7cb89SCoia Prant 	 * PD_PIPE is shared with SATA/PCIe and would be powered down by
567b9e7cb89SCoia Prant 	 * genpd once all its consumers are suspended, killing the SerDes
568b9e7cb89SCoia Prant 	 * and breaking MAC WoL.  Mark the XPCS as part of the wakeup path
569b9e7cb89SCoia Prant 	 * so genpd keeps the domain on.  Unconditional because the XPCS
570b9e7cb89SCoia Prant 	 * core has no callback to convey the MAC WoL state.
571b9e7cb89SCoia Prant 	 */
572b9e7cb89SCoia Prant 	device_set_wakeup_path(dev);
573b9e7cb89SCoia Prant 	return 0;
574b9e7cb89SCoia Prant }
575b9e7cb89SCoia Prant 
576b9e7cb89SCoia Prant static int xpcs_rk_system_resume(struct device *dev)
577b9e7cb89SCoia Prant {
578b9e7cb89SCoia Prant 	return 0;
579b9e7cb89SCoia Prant }
580b9e7cb89SCoia Prant 
581b9e7cb89SCoia Prant static _DEFINE_DEV_PM_OPS(xpcs_rk_pm_ops,
582b9e7cb89SCoia Prant 			  xpcs_rk_system_suspend, xpcs_rk_system_resume,
583b9e7cb89SCoia Prant 			  xpcs_rk_pm_runtime_suspend, xpcs_rk_pm_runtime_resume,
584b9e7cb89SCoia Prant 			  NULL);
585b9e7cb89SCoia Prant 
586b9e7cb89SCoia Prant static struct platform_driver xpcs_rk_driver = {
587b9e7cb89SCoia Prant 	.probe = xpcs_rk_probe,
588b9e7cb89SCoia Prant 	.driver = {
589b9e7cb89SCoia Prant 		.name = "rk_xpcs-dwxpcs",
590b9e7cb89SCoia Prant 		.pm = pm_ptr(&xpcs_rk_pm_ops),
591b9e7cb89SCoia Prant 		.of_match_table = xpcs_rk_of_ids,
592b9e7cb89SCoia Prant 	},
593b9e7cb89SCoia Prant };
594b9e7cb89SCoia Prant module_platform_driver(xpcs_rk_driver);
595b9e7cb89SCoia Prant 
596b9e7cb89SCoia Prant MODULE_DESCRIPTION("Rockchip XPCS platform device driver");
597b9e7cb89SCoia Prant MODULE_AUTHOR("Coia Prant <coiaprant@gmail.com>");
598b9e7cb89SCoia Prant MODULE_LICENSE("GPL");
599