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