FILE
dwmac-dwc-qos-eth.c
File preview with syntax highlighting.
linux-6.19.14/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Synopsys DWC Ethernet Quality-of-Service v4.10a linux driver 4 * 5 * Copyright (C) 2016 Joao Pinto <jpinto@synopsys.com> 6 */ 7 8#include <linux/clk.h> 9#include <linux/clk-provider.h> 10#include <linux/device.h> 11#include <linux/gpio/consumer.h> 12#include <linux/ethtool.h> 13#include <linux/io.h> 14#include <linux/iopoll.h> 15#include <linux/ioport.h> 16#include <linux/module.h> 17#include <linux/of.h> 18#include <linux/of_net.h> 19#include <linux/mfd/syscon.h> 20#include <linux/platform_device.h> 21#include <linux/reset.h> 22#include <linux/stmmac.h> 23 24#include "stmmac_platform.h" 25#include "dwmac4.h" 26 27struct tegra_eqos { 28 struct device *dev; 29 void __iomem *regs; 30 31 struct reset_control *rst; 32 33 struct gpio_desc *reset; 34}; 35 36static int dwc_eth_dwmac_config_dt(struct platform_device *pdev, 37 struct plat_stmmacenet_data *plat_dat) 38{ 39 struct device *dev = &pdev->dev; 40 u32 burst_map = 0; 41 42 if (!plat_dat->axi) { 43 plat_dat->axi = devm_kzalloc(&pdev->dev, 44 sizeof(struct stmmac_axi), 45 GFP_KERNEL); 46 47 if (!plat_dat->axi) 48 return -ENOMEM; 49 } 50 51 plat_dat->axi->axi_lpi_en = device_property_read_bool(dev, 52 "snps,en-lpi"); 53 if (device_property_read_u32(dev, "snps,write-requests", 54 &plat_dat->axi->axi_wr_osr_lmt)) { 55 /** 56 * Since the register has a reset value of 1, if property 57 * is missing, default to 1. 58 */ 59 plat_dat->axi->axi_wr_osr_lmt = 1; 60 } else { 61 /** 62 * If property exists, to keep the behavior from dwc_eth_qos, 63 * subtract one after parsing. 64 */ 65 plat_dat->axi->axi_wr_osr_lmt--; 66 } 67 68 if (device_property_read_u32(dev, "snps,read-requests", 69 &plat_dat->axi->axi_rd_osr_lmt)) { 70 /** 71 * Since the register has a reset value of 1, if property 72 * is missing, default to 1. 73 */ 74 plat_dat->axi->axi_rd_osr_lmt = 1; 75 } else { 76 /** 77 * If property exists, to keep the behavior from dwc_eth_qos, 78 * subtract one after parsing. 79 */ 80 plat_dat->axi->axi_rd_osr_lmt--; 81 } 82 device_property_read_u32(dev, "snps,burst-map", &burst_map); 83 84 plat_dat->axi->axi_blen_regval = FIELD_PREP(DMA_AXI_BLEN_MASK, 85 burst_map); 86 87 /* dwc-qos needs GMAC4, AAL, TSO and PMT */ 88 plat_dat->core_type = DWMAC_CORE_GMAC4; 89 plat_dat->dma_cfg->aal = 1; 90 plat_dat->flags |= STMMAC_FLAG_TSO_EN; 91 plat_dat->pmt = 1; 92 93 return 0; 94} 95 96static int dwc_qos_probe(struct platform_device *pdev, 97 struct plat_stmmacenet_data *plat_dat, 98 struct stmmac_resources *stmmac_res) 99{ 100 plat_dat->pclk = stmmac_pltfr_find_clk(plat_dat, "phy_ref_clk"); 101 102 return 0; 103} 104 105#define SDMEMCOMPPADCTRL 0x8800 106#define SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD BIT(31) 107 108#define AUTO_CAL_CONFIG 0x8804 109#define AUTO_CAL_CONFIG_START BIT(31) 110#define AUTO_CAL_CONFIG_ENABLE BIT(29) 111 112#define AUTO_CAL_STATUS 0x880c 113#define AUTO_CAL_STATUS_ACTIVE BIT(31) 114 115static void tegra_eqos_fix_speed(void *bsp_priv, int speed, unsigned int mode) 116{ 117 struct tegra_eqos *eqos = bsp_priv; 118 bool needs_calibration = false; 119 struct stmmac_priv *priv; 120 u32 value; 121 int err; 122 123 switch (speed) { 124 case SPEED_1000: 125 case SPEED_100: 126 needs_calibration = true; 127 fallthrough; 128 129 case SPEED_10: 130 break; 131 132 default: 133 dev_err(eqos->dev, "invalid speed %d\n", speed); 134 break; 135 } 136 137 if (needs_calibration) { 138 priv = netdev_priv(dev_get_drvdata(eqos->dev)); 139 140 /* Calibration should be done with the MDIO bus idle */ 141 stmmac_mdio_lock(priv); 142 143 /* calibrate */ 144 value = readl(eqos->regs + SDMEMCOMPPADCTRL); 145 value |= SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD; 146 writel(value, eqos->regs + SDMEMCOMPPADCTRL); 147 148 udelay(1); 149 150 value = readl(eqos->regs + AUTO_CAL_CONFIG); 151 value |= AUTO_CAL_CONFIG_START | AUTO_CAL_CONFIG_ENABLE; 152 writel(value, eqos->regs + AUTO_CAL_CONFIG); 153 154 err = readl_poll_timeout_atomic(eqos->regs + AUTO_CAL_STATUS, 155 value, 156 value & AUTO_CAL_STATUS_ACTIVE, 157 1, 10); 158 if (err < 0) { 159 dev_err(eqos->dev, "calibration did not start\n"); 160 goto failed; 161 } 162 163 err = readl_poll_timeout_atomic(eqos->regs + AUTO_CAL_STATUS, 164 value, 165 (value & AUTO_CAL_STATUS_ACTIVE) == 0, 166 20, 200); 167 if (err < 0) { 168 dev_err(eqos->dev, "calibration didn't finish\n"); 169 goto failed; 170 } 171 172 failed: 173 value = readl(eqos->regs + SDMEMCOMPPADCTRL); 174 value &= ~SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD; 175 writel(value, eqos->regs + SDMEMCOMPPADCTRL); 176 177 stmmac_mdio_unlock(priv); 178 } else { 179 value = readl(eqos->regs + AUTO_CAL_CONFIG); 180 value &= ~AUTO_CAL_CONFIG_ENABLE; 181 writel(value, eqos->regs + AUTO_CAL_CONFIG); 182 } 183} 184 185static int tegra_eqos_probe(struct platform_device *pdev, 186 struct plat_stmmacenet_data *plat_dat, 187 struct stmmac_resources *res) 188{ 189 struct device *dev = &pdev->dev; 190 struct tegra_eqos *eqos; 191 int err; 192 193 eqos = devm_kzalloc(&pdev->dev, sizeof(*eqos), GFP_KERNEL); 194 if (!eqos) 195 return -ENOMEM; 196 197 eqos->dev = &pdev->dev; 198 eqos->regs = res->addr; 199 200 if (!is_of_node(dev->fwnode)) 201 goto bypass_clk_reset_gpio; 202 203 plat_dat->clk_tx_i = stmmac_pltfr_find_clk(plat_dat, "tx"); 204 205 eqos->reset = devm_gpiod_get(&pdev->dev, "phy-reset", GPIOD_OUT_HIGH); 206 if (IS_ERR(eqos->reset)) { 207 err = PTR_ERR(eqos->reset); 208 return err; 209 } 210 211 usleep_range(2000, 4000); 212 gpiod_set_value(eqos->reset, 0); 213 214 /* MDIO bus was already reset just above */ 215 plat_dat->mdio_bus_data->needs_reset = false; 216 217 eqos->rst = devm_reset_control_get(&pdev->dev, "eqos"); 218 if (IS_ERR(eqos->rst)) { 219 err = PTR_ERR(eqos->rst); 220 goto reset_phy; 221 } 222 223 err = reset_control_assert(eqos->rst); 224 if (err < 0) 225 goto reset_phy; 226 227 usleep_range(2000, 4000); 228 229 err = reset_control_deassert(eqos->rst); 230 if (err < 0) 231 goto reset_phy; 232 233 usleep_range(2000, 4000); 234 235bypass_clk_reset_gpio: 236 plat_dat->fix_mac_speed = tegra_eqos_fix_speed; 237 plat_dat->set_clk_tx_rate = stmmac_set_clk_tx_rate; 238 plat_dat->bsp_priv = eqos; 239 plat_dat->flags |= STMMAC_FLAG_SPH_DISABLE | 240 STMMAC_FLAG_EN_TX_LPI_CLK_PHY_CAP | 241 STMMAC_FLAG_USE_PHY_WOL; 242 243 return 0; 244 245reset_phy: 246 gpiod_set_value(eqos->reset, 1); 247 248 return err; 249} 250 251static void tegra_eqos_remove(struct platform_device *pdev) 252{ 253 struct tegra_eqos *eqos = get_stmmac_bsp_priv(&pdev->dev); 254 255 reset_control_assert(eqos->rst); 256 gpiod_set_value(eqos->reset, 1); 257} 258 259struct dwc_eth_dwmac_data { 260 int (*probe)(struct platform_device *pdev, 261 struct plat_stmmacenet_data *plat_dat, 262 struct stmmac_resources *res); 263 void (*remove)(struct platform_device *pdev); 264 const char *stmmac_clk_name; 265}; 266 267static const struct dwc_eth_dwmac_data dwc_qos_data = { 268 .probe = dwc_qos_probe, 269 .stmmac_clk_name = "apb_pclk", 270}; 271 272static const struct dwc_eth_dwmac_data tegra_eqos_data = { 273 .probe = tegra_eqos_probe, 274 .remove = tegra_eqos_remove, 275 .stmmac_clk_name = "slave_bus", 276}; 277 278static const struct dwc_eth_dwmac_data fsd_eqos_data = { 279 .stmmac_clk_name = "slave_bus", 280}; 281 282static int dwc_eth_dwmac_probe(struct platform_device *pdev) 283{ 284 const struct dwc_eth_dwmac_data *data; 285 struct plat_stmmacenet_data *plat_dat; 286 struct stmmac_resources stmmac_res; 287 int ret; 288 289 data = device_get_match_data(&pdev->dev); 290 291 memset(&stmmac_res, 0, sizeof(struct stmmac_resources)); 292 293 /** 294 * Since stmmac_platform supports name IRQ only, basic platform 295 * resource initialization is done in the glue logic. 296 */ 297 stmmac_res.irq = platform_get_irq(pdev, 0); 298 if (stmmac_res.irq < 0) 299 return stmmac_res.irq; 300 stmmac_res.wol_irq = stmmac_res.irq; 301 302 stmmac_res.addr = devm_platform_ioremap_resource(pdev, 0); 303 if (IS_ERR(stmmac_res.addr)) 304 return PTR_ERR(stmmac_res.addr); 305 306 plat_dat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac); 307 if (IS_ERR(plat_dat)) 308 return PTR_ERR(plat_dat); 309 310 ret = devm_clk_bulk_get_all_enabled(&pdev->dev, &plat_dat->clks); 311 if (ret < 0) 312 return dev_err_probe(&pdev->dev, ret, "Failed to retrieve and enable all required clocks\n"); 313 plat_dat->num_clks = ret; 314 315 plat_dat->stmmac_clk = stmmac_pltfr_find_clk(plat_dat, 316 data->stmmac_clk_name); 317 318 if (data->probe) 319 ret = data->probe(pdev, plat_dat, &stmmac_res); 320 if (ret < 0) { 321 dev_err_probe(&pdev->dev, ret, "failed to probe subdriver\n"); 322 return ret; 323 } 324 325 ret = dwc_eth_dwmac_config_dt(pdev, plat_dat); 326 if (ret) 327 goto remove; 328 329 ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); 330 if (ret) 331 goto remove; 332 333 return ret; 334 335remove: 336 if (data->remove) 337 data->remove(pdev); 338 339 return ret; 340} 341 342static void dwc_eth_dwmac_remove(struct platform_device *pdev) 343{ 344 const struct dwc_eth_dwmac_data *data = device_get_match_data(&pdev->dev); 345 346 stmmac_dvr_remove(&pdev->dev); 347 348 if (data->remove) 349 data->remove(pdev); 350} 351 352static const struct of_device_id dwc_eth_dwmac_match[] = { 353 { .compatible = "snps,dwc-qos-ethernet-4.10", .data = &dwc_qos_data }, 354 { .compatible = "nvidia,tegra186-eqos", .data = &tegra_eqos_data }, 355 { .compatible = "tesla,fsd-ethqos", .data = &fsd_eqos_data }, 356 { } 357}; 358MODULE_DEVICE_TABLE(of, dwc_eth_dwmac_match); 359 360static struct platform_driver dwc_eth_dwmac_driver = { 361 .probe = dwc_eth_dwmac_probe, 362 .remove = dwc_eth_dwmac_remove, 363 .driver = { 364 .name = "dwc-eth-dwmac", 365 .pm = &stmmac_pltfr_pm_ops, 366 .of_match_table = dwc_eth_dwmac_match, 367 }, 368}; 369module_platform_driver(dwc_eth_dwmac_driver); 370 371MODULE_AUTHOR("Joao Pinto <jpinto@synopsys.com>"); 372MODULE_DESCRIPTION("Synopsys DWC Ethernet Quality-of-Service v4.10a driver"); 373MODULE_LICENSE("GPL v2");
~/
Read-only file preview.