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