14bd3bb7bSSamin Guo // SPDX-License-Identifier: GPL-2.0+ 24bd3bb7bSSamin Guo /* 34bd3bb7bSSamin Guo * StarFive DWMAC platform driver 44bd3bb7bSSamin Guo * 54bd3bb7bSSamin Guo * Copyright (C) 2021 Emil Renner Berthing <kernel@esmil.dk> 64bd3bb7bSSamin Guo * Copyright (C) 2022 StarFive Technology Co., Ltd. 74bd3bb7bSSamin Guo * 84bd3bb7bSSamin Guo */ 94bd3bb7bSSamin Guo 104bd3bb7bSSamin Guo #include <linux/mfd/syscon.h> 114bd3bb7bSSamin Guo #include <linux/of_device.h> 124bd3bb7bSSamin Guo #include <linux/regmap.h> 134bd3bb7bSSamin Guo 144bd3bb7bSSamin Guo #include "stmmac_platform.h" 154bd3bb7bSSamin Guo 16b4a5afa5SSamin Guo #define STARFIVE_DWMAC_PHY_INFT_RGMII 0x1 17b4a5afa5SSamin Guo #define STARFIVE_DWMAC_PHY_INFT_RMII 0x4 18b4a5afa5SSamin Guo #define STARFIVE_DWMAC_PHY_INFT_FIELD 0x7U 19b4a5afa5SSamin Guo 204bd3bb7bSSamin Guo struct starfive_dwmac { 214bd3bb7bSSamin Guo struct device *dev; 224bd3bb7bSSamin Guo struct clk *clk_tx; 234bd3bb7bSSamin Guo }; 244bd3bb7bSSamin Guo 254bd3bb7bSSamin Guo static void starfive_dwmac_fix_mac_speed(void *priv, unsigned int speed) 264bd3bb7bSSamin Guo { 274bd3bb7bSSamin Guo struct starfive_dwmac *dwmac = priv; 284bd3bb7bSSamin Guo unsigned long rate; 294bd3bb7bSSamin Guo int err; 304bd3bb7bSSamin Guo 314bd3bb7bSSamin Guo rate = clk_get_rate(dwmac->clk_tx); 324bd3bb7bSSamin Guo 334bd3bb7bSSamin Guo switch (speed) { 344bd3bb7bSSamin Guo case SPEED_1000: 354bd3bb7bSSamin Guo rate = 125000000; 364bd3bb7bSSamin Guo break; 374bd3bb7bSSamin Guo case SPEED_100: 384bd3bb7bSSamin Guo rate = 25000000; 394bd3bb7bSSamin Guo break; 404bd3bb7bSSamin Guo case SPEED_10: 414bd3bb7bSSamin Guo rate = 2500000; 424bd3bb7bSSamin Guo break; 434bd3bb7bSSamin Guo default: 444bd3bb7bSSamin Guo dev_err(dwmac->dev, "invalid speed %u\n", speed); 454bd3bb7bSSamin Guo break; 464bd3bb7bSSamin Guo } 474bd3bb7bSSamin Guo 484bd3bb7bSSamin Guo err = clk_set_rate(dwmac->clk_tx, rate); 494bd3bb7bSSamin Guo if (err) 504bd3bb7bSSamin Guo dev_err(dwmac->dev, "failed to set tx rate %lu\n", rate); 514bd3bb7bSSamin Guo } 524bd3bb7bSSamin Guo 53b4a5afa5SSamin Guo static int starfive_dwmac_set_mode(struct plat_stmmacenet_data *plat_dat) 54b4a5afa5SSamin Guo { 55b4a5afa5SSamin Guo struct starfive_dwmac *dwmac = plat_dat->bsp_priv; 56b4a5afa5SSamin Guo struct regmap *regmap; 57b4a5afa5SSamin Guo unsigned int args[2]; 58b4a5afa5SSamin Guo unsigned int mode; 59b4a5afa5SSamin Guo int err; 60b4a5afa5SSamin Guo 61b4a5afa5SSamin Guo switch (plat_dat->interface) { 62b4a5afa5SSamin Guo case PHY_INTERFACE_MODE_RMII: 63b4a5afa5SSamin Guo mode = STARFIVE_DWMAC_PHY_INFT_RMII; 64b4a5afa5SSamin Guo break; 65b4a5afa5SSamin Guo 66b4a5afa5SSamin Guo case PHY_INTERFACE_MODE_RGMII: 67b4a5afa5SSamin Guo case PHY_INTERFACE_MODE_RGMII_ID: 68b4a5afa5SSamin Guo mode = STARFIVE_DWMAC_PHY_INFT_RGMII; 69b4a5afa5SSamin Guo break; 70b4a5afa5SSamin Guo 71b4a5afa5SSamin Guo default: 72b4a5afa5SSamin Guo dev_err(dwmac->dev, "unsupported interface %d\n", 73b4a5afa5SSamin Guo plat_dat->interface); 74b4a5afa5SSamin Guo return -EINVAL; 75b4a5afa5SSamin Guo } 76b4a5afa5SSamin Guo 77b4a5afa5SSamin Guo regmap = syscon_regmap_lookup_by_phandle_args(dwmac->dev->of_node, 78b4a5afa5SSamin Guo "starfive,syscon", 79b4a5afa5SSamin Guo 2, args); 80b4a5afa5SSamin Guo if (IS_ERR(regmap)) 81b4a5afa5SSamin Guo return dev_err_probe(dwmac->dev, PTR_ERR(regmap), "getting the regmap failed\n"); 82b4a5afa5SSamin Guo 83b4a5afa5SSamin Guo /* args[0]:offset args[1]: shift */ 84b4a5afa5SSamin Guo err = regmap_update_bits(regmap, args[0], 85b4a5afa5SSamin Guo STARFIVE_DWMAC_PHY_INFT_FIELD << args[1], 86b4a5afa5SSamin Guo mode << args[1]); 87b4a5afa5SSamin Guo if (err) 88b4a5afa5SSamin Guo return dev_err_probe(dwmac->dev, err, "error setting phy mode\n"); 89b4a5afa5SSamin Guo 90b4a5afa5SSamin Guo return 0; 91b4a5afa5SSamin Guo } 92b4a5afa5SSamin Guo 934bd3bb7bSSamin Guo static int starfive_dwmac_probe(struct platform_device *pdev) 944bd3bb7bSSamin Guo { 954bd3bb7bSSamin Guo struct plat_stmmacenet_data *plat_dat; 964bd3bb7bSSamin Guo struct stmmac_resources stmmac_res; 974bd3bb7bSSamin Guo struct starfive_dwmac *dwmac; 984bd3bb7bSSamin Guo struct clk *clk_gtx; 994bd3bb7bSSamin Guo int err; 1004bd3bb7bSSamin Guo 1014bd3bb7bSSamin Guo err = stmmac_get_platform_resources(pdev, &stmmac_res); 1024bd3bb7bSSamin Guo if (err) 1034bd3bb7bSSamin Guo return dev_err_probe(&pdev->dev, err, 1044bd3bb7bSSamin Guo "failed to get resources\n"); 1054bd3bb7bSSamin Guo 1064bd3bb7bSSamin Guo plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac); 1074bd3bb7bSSamin Guo if (IS_ERR(plat_dat)) 1084bd3bb7bSSamin Guo return dev_err_probe(&pdev->dev, PTR_ERR(plat_dat), 1094bd3bb7bSSamin Guo "dt configuration failed\n"); 1104bd3bb7bSSamin Guo 1114bd3bb7bSSamin Guo dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL); 1124bd3bb7bSSamin Guo if (!dwmac) 1134bd3bb7bSSamin Guo return -ENOMEM; 1144bd3bb7bSSamin Guo 1154bd3bb7bSSamin Guo dwmac->clk_tx = devm_clk_get_enabled(&pdev->dev, "tx"); 1164bd3bb7bSSamin Guo if (IS_ERR(dwmac->clk_tx)) 1174bd3bb7bSSamin Guo return dev_err_probe(&pdev->dev, PTR_ERR(dwmac->clk_tx), 1184bd3bb7bSSamin Guo "error getting tx clock\n"); 1194bd3bb7bSSamin Guo 1204bd3bb7bSSamin Guo clk_gtx = devm_clk_get_enabled(&pdev->dev, "gtx"); 1214bd3bb7bSSamin Guo if (IS_ERR(clk_gtx)) 1224bd3bb7bSSamin Guo return dev_err_probe(&pdev->dev, PTR_ERR(clk_gtx), 1234bd3bb7bSSamin Guo "error getting gtx clock\n"); 1244bd3bb7bSSamin Guo 1254bd3bb7bSSamin Guo /* Generally, the rgmii_tx clock is provided by the internal clock, 1264bd3bb7bSSamin Guo * which needs to match the corresponding clock frequency according 1274bd3bb7bSSamin Guo * to different speeds. If the rgmii_tx clock is provided by the 1284bd3bb7bSSamin Guo * external rgmii_rxin, there is no need to configure the clock 1294bd3bb7bSSamin Guo * internally, because rgmii_rxin will be adaptively adjusted. 1304bd3bb7bSSamin Guo */ 1314bd3bb7bSSamin Guo if (!device_property_read_bool(&pdev->dev, "starfive,tx-use-rgmii-clk")) 1324bd3bb7bSSamin Guo plat_dat->fix_mac_speed = starfive_dwmac_fix_mac_speed; 1334bd3bb7bSSamin Guo 1344bd3bb7bSSamin Guo dwmac->dev = &pdev->dev; 1354bd3bb7bSSamin Guo plat_dat->bsp_priv = dwmac; 1364bd3bb7bSSamin Guo plat_dat->dma_cfg->dche = true; 1374bd3bb7bSSamin Guo 138b4a5afa5SSamin Guo err = starfive_dwmac_set_mode(plat_dat); 139b4a5afa5SSamin Guo if (err) 140b4a5afa5SSamin Guo return err; 141b4a5afa5SSamin Guo 1424bd3bb7bSSamin Guo err = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); 1434bd3bb7bSSamin Guo if (err) { 1444bd3bb7bSSamin Guo stmmac_remove_config_dt(pdev, plat_dat); 1454bd3bb7bSSamin Guo return err; 1464bd3bb7bSSamin Guo } 1474bd3bb7bSSamin Guo 1484bd3bb7bSSamin Guo return 0; 1494bd3bb7bSSamin Guo } 1504bd3bb7bSSamin Guo 1514bd3bb7bSSamin Guo static const struct of_device_id starfive_dwmac_match[] = { 1524bd3bb7bSSamin Guo { .compatible = "starfive,jh7110-dwmac" }, 1534bd3bb7bSSamin Guo { /* sentinel */ } 1544bd3bb7bSSamin Guo }; 1554bd3bb7bSSamin Guo MODULE_DEVICE_TABLE(of, starfive_dwmac_match); 1564bd3bb7bSSamin Guo 1574bd3bb7bSSamin Guo static struct platform_driver starfive_dwmac_driver = { 1584bd3bb7bSSamin Guo .probe = starfive_dwmac_probe, 159*3246627fSUwe Kleine-König .remove_new = stmmac_pltfr_remove, 1604bd3bb7bSSamin Guo .driver = { 1614bd3bb7bSSamin Guo .name = "starfive-dwmac", 1624bd3bb7bSSamin Guo .pm = &stmmac_pltfr_pm_ops, 1634bd3bb7bSSamin Guo .of_match_table = starfive_dwmac_match, 1644bd3bb7bSSamin Guo }, 1654bd3bb7bSSamin Guo }; 1664bd3bb7bSSamin Guo module_platform_driver(starfive_dwmac_driver); 1674bd3bb7bSSamin Guo 1684bd3bb7bSSamin Guo MODULE_LICENSE("GPL"); 1694bd3bb7bSSamin Guo MODULE_DESCRIPTION("StarFive DWMAC platform driver"); 1704bd3bb7bSSamin Guo MODULE_AUTHOR("Emil Renner Berthing <kernel@esmil.dk>"); 1714bd3bb7bSSamin Guo MODULE_AUTHOR("Samin Guo <samin.guo@starfivetech.com>"); 172