xref: /linux/drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c (revision 55a42f78ffd386e01a5404419f8c5ded7db70a21)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * dwmac-renesas-gbeth.c - DWMAC Specific Glue layer for Renesas GBETH
4  *
5  * The Rx and Tx clocks are supplied as follows for the GBETH IP.
6  *
7  *                         Rx / Tx
8  *   -------+------------- on / off -------
9  *          |
10  *          |            Rx-180 / Tx-180
11  *          +---- not ---- on / off -------
12  *
13  * Copyright (C) 2025 Renesas Electronics Corporation
14  */
15 
16 #include <linux/clk.h>
17 #include <linux/device.h>
18 #include <linux/module.h>
19 #include <linux/of.h>
20 #include <linux/pcs-rzn1-miic.h>
21 #include <linux/platform_device.h>
22 #include <linux/reset.h>
23 #include <linux/types.h>
24 
25 #include "stmmac_platform.h"
26 
27 /**
28  * struct renesas_gbeth_of_data - OF data for Renesas GBETH
29  *
30  * @clks: Array of clock names
31  * @num_clks: Number of clocks
32  * @stmmac_flags: Flags for the stmmac driver
33  * @handle_reset: Flag to indicate if reset control is
34  *                handled by the glue driver or core driver.
35  * @set_clk_tx_rate: Flag to indicate if Tx clock is fixed or
36  *                   set_clk_tx_rate is needed.
37  * @has_pcs: Flag to indicate if the MAC has a PCS
38  */
39 struct renesas_gbeth_of_data {
40 	const char * const *clks;
41 	u8 num_clks;
42 	u32 stmmac_flags;
43 	bool handle_reset;
44 	bool set_clk_tx_rate;
45 	bool has_pcs;
46 };
47 
48 struct renesas_gbeth {
49 	const struct renesas_gbeth_of_data *of_data;
50 	struct plat_stmmacenet_data *plat_dat;
51 	struct reset_control *rstc;
52 	struct device *dev;
53 };
54 
55 static const char *const renesas_gbeth_clks[] = {
56 	"tx", "tx-180", "rx", "rx-180",
57 };
58 
59 static const char *const renesas_gmac_clks[] = {
60 	"tx",
61 };
62 
63 static int renesas_gmac_pcs_init(struct stmmac_priv *priv)
64 {
65 	struct device_node *np = priv->device->of_node;
66 	struct device_node *pcs_node;
67 	struct phylink_pcs *pcs;
68 
69 	pcs_node = of_parse_phandle(np, "pcs-handle", 0);
70 	if (pcs_node) {
71 		pcs = miic_create(priv->device, pcs_node);
72 		of_node_put(pcs_node);
73 		if (IS_ERR(pcs))
74 			return PTR_ERR(pcs);
75 
76 		priv->hw->phylink_pcs = pcs;
77 	}
78 
79 	return 0;
80 }
81 
82 static void renesas_gmac_pcs_exit(struct stmmac_priv *priv)
83 {
84 	if (priv->hw->phylink_pcs)
85 		miic_destroy(priv->hw->phylink_pcs);
86 }
87 
88 static struct phylink_pcs *renesas_gmac_select_pcs(struct stmmac_priv *priv,
89 						   phy_interface_t interface)
90 {
91 	return priv->hw->phylink_pcs;
92 }
93 
94 static int renesas_gbeth_init(struct platform_device *pdev, void *priv)
95 {
96 	struct plat_stmmacenet_data *plat_dat;
97 	struct renesas_gbeth *gbeth = priv;
98 	int ret;
99 
100 	plat_dat = gbeth->plat_dat;
101 
102 	ret = reset_control_deassert(gbeth->rstc);
103 	if (ret) {
104 		dev_err(gbeth->dev, "Reset deassert failed\n");
105 		return ret;
106 	}
107 
108 	ret = clk_bulk_prepare_enable(plat_dat->num_clks,
109 				      plat_dat->clks);
110 	if (ret)
111 		reset_control_assert(gbeth->rstc);
112 
113 	return ret;
114 }
115 
116 static void renesas_gbeth_exit(struct platform_device *pdev, void *priv)
117 {
118 	struct plat_stmmacenet_data *plat_dat;
119 	struct renesas_gbeth *gbeth = priv;
120 	int ret;
121 
122 	plat_dat = gbeth->plat_dat;
123 
124 	clk_bulk_disable_unprepare(plat_dat->num_clks, plat_dat->clks);
125 
126 	ret = reset_control_assert(gbeth->rstc);
127 	if (ret)
128 		dev_err(gbeth->dev, "Reset assert failed\n");
129 }
130 
131 static int renesas_gbeth_probe(struct platform_device *pdev)
132 {
133 	const struct renesas_gbeth_of_data *of_data;
134 	struct plat_stmmacenet_data *plat_dat;
135 	struct stmmac_resources stmmac_res;
136 	struct device *dev = &pdev->dev;
137 	struct renesas_gbeth *gbeth;
138 	unsigned int i;
139 	int err;
140 
141 	err = stmmac_get_platform_resources(pdev, &stmmac_res);
142 	if (err)
143 		return dev_err_probe(dev, err,
144 				     "failed to get resources\n");
145 
146 	plat_dat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac);
147 	if (IS_ERR(plat_dat))
148 		return dev_err_probe(dev, PTR_ERR(plat_dat),
149 				     "dt configuration failed\n");
150 
151 	gbeth = devm_kzalloc(dev, sizeof(*gbeth), GFP_KERNEL);
152 	if (!gbeth)
153 		return -ENOMEM;
154 
155 	of_data = of_device_get_match_data(&pdev->dev);
156 	gbeth->of_data = of_data;
157 
158 	plat_dat->num_clks = of_data->num_clks;
159 	plat_dat->clks = devm_kcalloc(dev, plat_dat->num_clks,
160 				      sizeof(*plat_dat->clks), GFP_KERNEL);
161 	if (!plat_dat->clks)
162 		return -ENOMEM;
163 
164 	for (i = 0; i < plat_dat->num_clks; i++)
165 		plat_dat->clks[i].id = of_data->clks[i];
166 
167 	err = devm_clk_bulk_get(dev, plat_dat->num_clks, plat_dat->clks);
168 	if (err < 0)
169 		return err;
170 
171 	plat_dat->clk_tx_i = stmmac_pltfr_find_clk(plat_dat, "tx");
172 	if (!plat_dat->clk_tx_i)
173 		return dev_err_probe(dev, -EINVAL,
174 				     "error finding tx clock\n");
175 
176 	if (of_data->handle_reset) {
177 		gbeth->rstc = devm_reset_control_get_exclusive(dev, NULL);
178 		if (IS_ERR(gbeth->rstc))
179 			return PTR_ERR(gbeth->rstc);
180 	}
181 
182 	gbeth->dev = dev;
183 	gbeth->plat_dat = plat_dat;
184 	plat_dat->bsp_priv = gbeth;
185 	if (of_data->set_clk_tx_rate)
186 		plat_dat->set_clk_tx_rate = stmmac_set_clk_tx_rate;
187 	plat_dat->init = renesas_gbeth_init;
188 	plat_dat->exit = renesas_gbeth_exit;
189 	plat_dat->flags |= STMMAC_FLAG_EN_TX_LPI_CLK_PHY_CAP |
190 			   gbeth->of_data->stmmac_flags;
191 	if (of_data->has_pcs) {
192 		plat_dat->pcs_init = renesas_gmac_pcs_init;
193 		plat_dat->pcs_exit = renesas_gmac_pcs_exit;
194 		plat_dat->select_pcs = renesas_gmac_select_pcs;
195 	}
196 
197 	return devm_stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res);
198 }
199 
200 static const struct renesas_gbeth_of_data renesas_gbeth_of_data = {
201 	.clks = renesas_gbeth_clks,
202 	.num_clks = ARRAY_SIZE(renesas_gbeth_clks),
203 	.handle_reset = true,
204 	.set_clk_tx_rate = true,
205 	.stmmac_flags = STMMAC_FLAG_HWTSTAMP_CORRECT_LATENCY |
206 			STMMAC_FLAG_SPH_DISABLE,
207 };
208 
209 static const struct renesas_gbeth_of_data renesas_gmac_of_data = {
210 	.clks = renesas_gmac_clks,
211 	.num_clks = ARRAY_SIZE(renesas_gmac_clks),
212 	.stmmac_flags = STMMAC_FLAG_HWTSTAMP_CORRECT_LATENCY,
213 	.has_pcs = true,
214 };
215 
216 static const struct of_device_id renesas_gbeth_match[] = {
217 	{ .compatible = "renesas,r9a09g077-gbeth", .data = &renesas_gmac_of_data },
218 	{ .compatible = "renesas,rzv2h-gbeth", .data = &renesas_gbeth_of_data },
219 	{ /* Sentinel */ }
220 };
221 MODULE_DEVICE_TABLE(of, renesas_gbeth_match);
222 
223 static struct platform_driver renesas_gbeth_driver = {
224 	.probe  = renesas_gbeth_probe,
225 	.driver = {
226 		.name		= "renesas-gbeth",
227 		.pm		= &stmmac_pltfr_pm_ops,
228 		.of_match_table	= renesas_gbeth_match,
229 	},
230 };
231 module_platform_driver(renesas_gbeth_driver);
232 
233 MODULE_AUTHOR("Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>");
234 MODULE_DESCRIPTION("Renesas GBETH DWMAC Specific Glue layer");
235 MODULE_LICENSE("GPL");
236