1 // SPDX-License-Identifier: GPL-2.0 2 /* Toshiba Visconti Ethernet Support 3 * 4 * (C) Copyright 2020 TOSHIBA CORPORATION 5 * (C) Copyright 2020 Toshiba Electronic Devices & Storage Corporation 6 */ 7 8 #include <linux/module.h> 9 #include <linux/of.h> 10 #include <linux/platform_device.h> 11 #include <linux/of_net.h> 12 #include <linux/stmmac.h> 13 14 #include "stmmac_platform.h" 15 #include "dwmac4.h" 16 17 #define REG_ETHER_CONTROL 0x52D4 18 #define ETHER_ETH_CONTROL_RESET BIT(17) 19 20 #define REG_ETHER_CLOCK_SEL 0x52D0 21 #define ETHER_CLK_SEL_TX_CLK_EN BIT(0) 22 #define ETHER_CLK_SEL_RX_CLK_EN BIT(1) 23 #define ETHER_CLK_SEL_RMII_CLK_EN BIT(2) 24 #define ETHER_CLK_SEL_RMII_CLK_RST BIT(3) 25 #define ETHER_CLK_SEL_DIV_SEL_2 BIT(4) 26 #define ETHER_CLK_SEL_DIV_SEL_20 0 27 #define ETHER_CLK_SEL_FREQ_SEL_125M (BIT(9) | BIT(8)) 28 #define ETHER_CLK_SEL_FREQ_SEL_50M BIT(9) 29 #define ETHER_CLK_SEL_FREQ_SEL_25M BIT(8) 30 #define ETHER_CLK_SEL_FREQ_SEL_2P5M 0 31 #define ETHER_CLK_SEL_TX_CLK_EXT_SEL_IN 0 32 #define ETHER_CLK_SEL_TX_CLK_EXT_SEL_TXC BIT(10) 33 #define ETHER_CLK_SEL_TX_CLK_EXT_SEL_DIV BIT(11) 34 #define ETHER_CLK_SEL_RX_CLK_EXT_SEL_IN 0 35 #define ETHER_CLK_SEL_RX_CLK_EXT_SEL_RXC BIT(12) 36 #define ETHER_CLK_SEL_RX_CLK_EXT_SEL_DIV BIT(13) 37 #define ETHER_CLK_SEL_TX_CLK_O_TX_I 0 38 #define ETHER_CLK_SEL_TX_CLK_O_RMII_I BIT(14) 39 #define ETHER_CLK_SEL_TX_O_E_N_IN BIT(15) 40 #define ETHER_CLK_SEL_RMII_CLK_SEL_IN 0 41 #define ETHER_CLK_SEL_RMII_CLK_SEL_RX_C BIT(16) 42 43 #define ETHER_CLK_SEL_RX_TX_CLK_EN (ETHER_CLK_SEL_RX_CLK_EN | ETHER_CLK_SEL_TX_CLK_EN) 44 45 struct visconti_eth { 46 void __iomem *reg; 47 struct clk *phy_ref_clk; 48 struct device *dev; 49 }; 50 51 static int visconti_eth_set_clk_tx_rate(void *bsp_priv, struct clk *clk_tx_i, 52 phy_interface_t interface, int speed) 53 { 54 struct visconti_eth *dwmac = bsp_priv; 55 unsigned long clk_sel, val; 56 57 if (phy_interface_mode_is_rgmii(interface)) { 58 switch (speed) { 59 case SPEED_1000: 60 clk_sel = ETHER_CLK_SEL_FREQ_SEL_125M; 61 break; 62 63 case SPEED_100: 64 clk_sel = ETHER_CLK_SEL_FREQ_SEL_25M; 65 break; 66 67 case SPEED_10: 68 clk_sel = ETHER_CLK_SEL_FREQ_SEL_2P5M; 69 break; 70 71 default: 72 return -EINVAL; 73 } 74 75 /* Stop internal clock */ 76 val = readl(dwmac->reg + REG_ETHER_CLOCK_SEL); 77 val &= ~(ETHER_CLK_SEL_RMII_CLK_EN | 78 ETHER_CLK_SEL_RX_TX_CLK_EN); 79 val |= ETHER_CLK_SEL_TX_O_E_N_IN; 80 writel(val, dwmac->reg + REG_ETHER_CLOCK_SEL); 81 82 /* Set Clock-Mux, Start clock, Set TX_O direction */ 83 val = clk_sel | ETHER_CLK_SEL_RX_CLK_EXT_SEL_RXC; 84 writel(val, dwmac->reg + REG_ETHER_CLOCK_SEL); 85 86 val |= ETHER_CLK_SEL_RX_TX_CLK_EN; 87 writel(val, dwmac->reg + REG_ETHER_CLOCK_SEL); 88 89 val &= ~ETHER_CLK_SEL_TX_O_E_N_IN; 90 writel(val, dwmac->reg + REG_ETHER_CLOCK_SEL); 91 } else if (interface == PHY_INTERFACE_MODE_RMII) { 92 switch (speed) { 93 case SPEED_100: 94 clk_sel = ETHER_CLK_SEL_DIV_SEL_2; 95 break; 96 97 case SPEED_10: 98 clk_sel = ETHER_CLK_SEL_DIV_SEL_20; 99 break; 100 101 default: 102 return -EINVAL; 103 } 104 105 /* Stop internal clock */ 106 val = readl(dwmac->reg + REG_ETHER_CLOCK_SEL); 107 val &= ~(ETHER_CLK_SEL_RMII_CLK_EN | 108 ETHER_CLK_SEL_RX_TX_CLK_EN); 109 val |= ETHER_CLK_SEL_TX_O_E_N_IN; 110 writel(val, dwmac->reg + REG_ETHER_CLOCK_SEL); 111 112 /* Set Clock-Mux, Start clock, Set TX_O direction */ 113 val = clk_sel | ETHER_CLK_SEL_RX_CLK_EXT_SEL_DIV | 114 ETHER_CLK_SEL_TX_CLK_EXT_SEL_DIV | 115 ETHER_CLK_SEL_TX_O_E_N_IN | 116 ETHER_CLK_SEL_RMII_CLK_SEL_RX_C; 117 writel(val, dwmac->reg + REG_ETHER_CLOCK_SEL); 118 119 val |= ETHER_CLK_SEL_RMII_CLK_RST; 120 writel(val, dwmac->reg + REG_ETHER_CLOCK_SEL); 121 122 val |= ETHER_CLK_SEL_RMII_CLK_EN | ETHER_CLK_SEL_RX_TX_CLK_EN; 123 writel(val, dwmac->reg + REG_ETHER_CLOCK_SEL); 124 } else { 125 /* Stop internal clock */ 126 val = readl(dwmac->reg + REG_ETHER_CLOCK_SEL); 127 val &= ~(ETHER_CLK_SEL_RMII_CLK_EN | 128 ETHER_CLK_SEL_RX_TX_CLK_EN); 129 val |= ETHER_CLK_SEL_TX_O_E_N_IN; 130 writel(val, dwmac->reg + REG_ETHER_CLOCK_SEL); 131 132 /* Set Clock-Mux, Start clock, Set TX_O direction */ 133 val = ETHER_CLK_SEL_RX_CLK_EXT_SEL_RXC | 134 ETHER_CLK_SEL_TX_CLK_EXT_SEL_TXC | 135 ETHER_CLK_SEL_TX_O_E_N_IN; 136 writel(val, dwmac->reg + REG_ETHER_CLOCK_SEL); 137 138 val |= ETHER_CLK_SEL_RX_TX_CLK_EN; 139 writel(val, dwmac->reg + REG_ETHER_CLOCK_SEL); 140 } 141 142 return 0; 143 } 144 145 static int visconti_eth_init_hw(struct platform_device *pdev, struct plat_stmmacenet_data *plat_dat) 146 { 147 struct visconti_eth *dwmac = plat_dat->bsp_priv; 148 unsigned int clk_sel_val; 149 int phy_intf_sel; 150 151 phy_intf_sel = stmmac_get_phy_intf_sel(plat_dat->phy_interface); 152 if (phy_intf_sel != PHY_INTF_SEL_GMII_MII && 153 phy_intf_sel != PHY_INTF_SEL_RGMII && 154 phy_intf_sel != PHY_INTF_SEL_RMII) { 155 dev_err(&pdev->dev, "Unsupported phy-mode (%d)\n", plat_dat->phy_interface); 156 return -EOPNOTSUPP; 157 } 158 159 writel(phy_intf_sel, dwmac->reg + REG_ETHER_CONTROL); 160 161 /* Enable TX/RX clock */ 162 clk_sel_val = ETHER_CLK_SEL_FREQ_SEL_125M; 163 writel(clk_sel_val, dwmac->reg + REG_ETHER_CLOCK_SEL); 164 165 writel((clk_sel_val | ETHER_CLK_SEL_RMII_CLK_EN | ETHER_CLK_SEL_RX_TX_CLK_EN), 166 dwmac->reg + REG_ETHER_CLOCK_SEL); 167 168 /* release internal-reset */ 169 phy_intf_sel |= ETHER_ETH_CONTROL_RESET; 170 writel(phy_intf_sel, dwmac->reg + REG_ETHER_CONTROL); 171 172 return 0; 173 } 174 175 static int visconti_eth_clock_probe(struct platform_device *pdev, 176 struct plat_stmmacenet_data *plat_dat) 177 { 178 struct visconti_eth *dwmac = plat_dat->bsp_priv; 179 int err; 180 181 dwmac->phy_ref_clk = devm_clk_get(&pdev->dev, "phy_ref_clk"); 182 if (IS_ERR(dwmac->phy_ref_clk)) 183 return dev_err_probe(&pdev->dev, PTR_ERR(dwmac->phy_ref_clk), 184 "phy_ref_clk clock not found.\n"); 185 186 err = clk_prepare_enable(dwmac->phy_ref_clk); 187 if (err < 0) { 188 dev_err(&pdev->dev, "failed to enable phy_ref clock: %d\n", err); 189 return err; 190 } 191 192 return 0; 193 } 194 195 static void visconti_eth_clock_remove(struct platform_device *pdev) 196 { 197 struct visconti_eth *dwmac = get_stmmac_bsp_priv(&pdev->dev); 198 struct net_device *ndev = platform_get_drvdata(pdev); 199 struct stmmac_priv *priv = netdev_priv(ndev); 200 201 clk_disable_unprepare(dwmac->phy_ref_clk); 202 clk_disable_unprepare(priv->plat->stmmac_clk); 203 } 204 205 static int visconti_eth_dwmac_probe(struct platform_device *pdev) 206 { 207 struct plat_stmmacenet_data *plat_dat; 208 struct stmmac_resources stmmac_res; 209 struct visconti_eth *dwmac; 210 int ret; 211 212 ret = stmmac_get_platform_resources(pdev, &stmmac_res); 213 if (ret) 214 return ret; 215 216 plat_dat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac); 217 if (IS_ERR(plat_dat)) 218 return PTR_ERR(plat_dat); 219 220 dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL); 221 if (!dwmac) 222 return -ENOMEM; 223 224 dwmac->reg = stmmac_res.addr; 225 dwmac->dev = &pdev->dev; 226 plat_dat->bsp_priv = dwmac; 227 plat_dat->set_clk_tx_rate = visconti_eth_set_clk_tx_rate; 228 229 ret = visconti_eth_clock_probe(pdev, plat_dat); 230 if (ret) 231 return ret; 232 233 visconti_eth_init_hw(pdev, plat_dat); 234 235 plat_dat->dma_cfg->aal = 1; 236 237 ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); 238 if (ret) 239 goto remove; 240 241 return ret; 242 243 remove: 244 visconti_eth_clock_remove(pdev); 245 246 return ret; 247 } 248 249 static void visconti_eth_dwmac_remove(struct platform_device *pdev) 250 { 251 stmmac_pltfr_remove(pdev); 252 visconti_eth_clock_remove(pdev); 253 } 254 255 static const struct of_device_id visconti_eth_dwmac_match[] = { 256 { .compatible = "toshiba,visconti-dwmac" }, 257 { } 258 }; 259 MODULE_DEVICE_TABLE(of, visconti_eth_dwmac_match); 260 261 static struct platform_driver visconti_eth_dwmac_driver = { 262 .probe = visconti_eth_dwmac_probe, 263 .remove = visconti_eth_dwmac_remove, 264 .driver = { 265 .name = "visconti-eth-dwmac", 266 .of_match_table = visconti_eth_dwmac_match, 267 }, 268 }; 269 module_platform_driver(visconti_eth_dwmac_driver); 270 271 MODULE_AUTHOR("Toshiba"); 272 MODULE_DESCRIPTION("Toshiba Visconti Ethernet DWMAC glue driver"); 273 MODULE_AUTHOR("Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp"); 274 MODULE_LICENSE("GPL v2"); 275