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