1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * StarFive DWMAC platform driver 4 * 5 * Copyright (C) 2021 Emil Renner Berthing <kernel@esmil.dk> 6 * Copyright (C) 2022 StarFive Technology Co., Ltd. 7 * 8 */ 9 10 #include <linux/mod_devicetable.h> 11 #include <linux/platform_device.h> 12 #include <linux/property.h> 13 #include <linux/mfd/syscon.h> 14 #include <linux/regmap.h> 15 16 #include "stmmac_platform.h" 17 18 #define STARFIVE_DWMAC_PHY_INFT_FIELD 0x7U 19 20 #define JH7100_SYSMAIN_REGISTER49_DLYCHAIN 0xc8 21 22 struct starfive_dwmac_data { 23 unsigned int gtxclk_dlychain; 24 }; 25 26 struct starfive_dwmac { 27 struct device *dev; 28 const struct starfive_dwmac_data *data; 29 }; 30 31 static int starfive_dwmac_set_mode(struct plat_stmmacenet_data *plat_dat) 32 { 33 struct starfive_dwmac *dwmac = plat_dat->bsp_priv; 34 struct regmap *regmap; 35 unsigned int args[2]; 36 int phy_intf_sel; 37 int err; 38 39 phy_intf_sel = stmmac_get_phy_intf_sel(plat_dat->phy_interface); 40 if (phy_intf_sel != PHY_INTF_SEL_RGMII && 41 phy_intf_sel != PHY_INTF_SEL_RMII) { 42 dev_err(dwmac->dev, "unsupported interface %s\n", 43 phy_modes(plat_dat->phy_interface)); 44 return phy_intf_sel < 0 ? phy_intf_sel : -EINVAL; 45 } 46 47 regmap = syscon_regmap_lookup_by_phandle_args(dwmac->dev->of_node, 48 "starfive,syscon", 49 2, args); 50 if (IS_ERR(regmap)) 51 return dev_err_probe(dwmac->dev, PTR_ERR(regmap), "getting the regmap failed\n"); 52 53 /* args[0]:offset args[1]: shift */ 54 err = regmap_update_bits(regmap, args[0], 55 STARFIVE_DWMAC_PHY_INFT_FIELD << args[1], 56 phy_intf_sel << args[1]); 57 if (err) 58 return dev_err_probe(dwmac->dev, err, "error setting phy mode\n"); 59 60 if (dwmac->data) { 61 err = regmap_write(regmap, JH7100_SYSMAIN_REGISTER49_DLYCHAIN, 62 dwmac->data->gtxclk_dlychain); 63 if (err) 64 return dev_err_probe(dwmac->dev, err, 65 "error selecting gtxclk delay chain\n"); 66 } 67 68 return 0; 69 } 70 71 static int starfive_dwmac_probe(struct platform_device *pdev) 72 { 73 struct plat_stmmacenet_data *plat_dat; 74 struct stmmac_resources stmmac_res; 75 struct starfive_dwmac *dwmac; 76 struct clk *clk_gtx; 77 int err; 78 79 err = stmmac_get_platform_resources(pdev, &stmmac_res); 80 if (err) 81 return dev_err_probe(&pdev->dev, err, 82 "failed to get resources\n"); 83 84 plat_dat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac); 85 if (IS_ERR(plat_dat)) 86 return dev_err_probe(&pdev->dev, PTR_ERR(plat_dat), 87 "dt configuration failed\n"); 88 89 dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL); 90 if (!dwmac) 91 return -ENOMEM; 92 93 dwmac->data = device_get_match_data(&pdev->dev); 94 95 plat_dat->clk_tx_i = devm_clk_get_enabled(&pdev->dev, "tx"); 96 if (IS_ERR(plat_dat->clk_tx_i)) 97 return dev_err_probe(&pdev->dev, PTR_ERR(plat_dat->clk_tx_i), 98 "error getting tx clock\n"); 99 100 clk_gtx = devm_clk_get_enabled(&pdev->dev, "gtx"); 101 if (IS_ERR(clk_gtx)) 102 return dev_err_probe(&pdev->dev, PTR_ERR(clk_gtx), 103 "error getting gtx clock\n"); 104 105 /* Generally, the rgmii_tx clock is provided by the internal clock, 106 * which needs to match the corresponding clock frequency according 107 * to different speeds. If the rgmii_tx clock is provided by the 108 * external rgmii_rxin, there is no need to configure the clock 109 * internally, because rgmii_rxin will be adaptively adjusted. 110 */ 111 if (!device_property_read_bool(&pdev->dev, "starfive,tx-use-rgmii-clk")) 112 plat_dat->set_clk_tx_rate = stmmac_set_clk_tx_rate; 113 114 dwmac->dev = &pdev->dev; 115 plat_dat->flags |= STMMAC_FLAG_EN_TX_LPI_CLK_PHY_CAP; 116 plat_dat->bsp_priv = dwmac; 117 plat_dat->dma_cfg->dche = true; 118 119 err = starfive_dwmac_set_mode(plat_dat); 120 if (err) 121 return err; 122 123 return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); 124 } 125 126 static const struct starfive_dwmac_data jh7100_data = { 127 .gtxclk_dlychain = 4, 128 }; 129 130 static const struct of_device_id starfive_dwmac_match[] = { 131 { .compatible = "starfive,jh7100-dwmac", .data = &jh7100_data }, 132 { .compatible = "starfive,jh7110-dwmac" }, 133 { /* sentinel */ } 134 }; 135 MODULE_DEVICE_TABLE(of, starfive_dwmac_match); 136 137 static struct platform_driver starfive_dwmac_driver = { 138 .probe = starfive_dwmac_probe, 139 .remove = stmmac_pltfr_remove, 140 .driver = { 141 .name = "starfive-dwmac", 142 .pm = &stmmac_pltfr_pm_ops, 143 .of_match_table = starfive_dwmac_match, 144 }, 145 }; 146 module_platform_driver(starfive_dwmac_driver); 147 148 MODULE_LICENSE("GPL"); 149 MODULE_DESCRIPTION("StarFive DWMAC platform driver"); 150 MODULE_AUTHOR("Emil Renner Berthing <kernel@esmil.dk>"); 151 MODULE_AUTHOR("Samin Guo <samin.guo@starfivetech.com>"); 152