1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * T-HEAD DWMAC platform driver 4 * 5 * Copyright (C) 2021 Alibaba Group Holding Limited. 6 * Copyright (C) 2023 Jisheng Zhang <jszhang@kernel.org> 7 * 8 */ 9 10 #include <linux/bitfield.h> 11 #include <linux/module.h> 12 #include <linux/of.h> 13 #include <linux/of_device.h> 14 #include <linux/of_net.h> 15 #include <linux/platform_device.h> 16 17 #include "stmmac_platform.h" 18 19 #define GMAC_CLK_EN 0x00 20 #define GMAC_TX_CLK_EN BIT(1) 21 #define GMAC_TX_CLK_N_EN BIT(2) 22 #define GMAC_TX_CLK_OUT_EN BIT(3) 23 #define GMAC_RX_CLK_EN BIT(4) 24 #define GMAC_RX_CLK_N_EN BIT(5) 25 #define GMAC_EPHY_REF_CLK_EN BIT(6) 26 #define GMAC_RXCLK_DELAY_CTRL 0x04 27 #define GMAC_RXCLK_BYPASS BIT(15) 28 #define GMAC_RXCLK_INVERT BIT(14) 29 #define GMAC_RXCLK_DELAY GENMASK(4, 0) 30 #define GMAC_TXCLK_DELAY_CTRL 0x08 31 #define GMAC_TXCLK_BYPASS BIT(15) 32 #define GMAC_TXCLK_INVERT BIT(14) 33 #define GMAC_TXCLK_DELAY GENMASK(4, 0) 34 #define GMAC_PLLCLK_DIV 0x0c 35 #define GMAC_PLLCLK_DIV_EN BIT(31) 36 #define GMAC_PLLCLK_DIV_NUM GENMASK(7, 0) 37 #define GMAC_GTXCLK_SEL 0x18 38 #define GMAC_GTXCLK_SEL_PLL BIT(0) 39 #define GMAC_INTF_CTRL 0x1c 40 #define PHY_INTF_MASK BIT(0) 41 #define PHY_INTF_RGMII FIELD_PREP(PHY_INTF_MASK, 1) 42 #define PHY_INTF_MII_GMII FIELD_PREP(PHY_INTF_MASK, 0) 43 #define GMAC_TXCLK_OEN 0x20 44 #define TXCLK_DIR_MASK BIT(0) 45 #define TXCLK_DIR_OUTPUT FIELD_PREP(TXCLK_DIR_MASK, 0) 46 #define TXCLK_DIR_INPUT FIELD_PREP(TXCLK_DIR_MASK, 1) 47 48 struct thead_dwmac { 49 struct plat_stmmacenet_data *plat; 50 void __iomem *apb_base; 51 struct device *dev; 52 }; 53 54 static int thead_dwmac_set_phy_if(struct plat_stmmacenet_data *plat) 55 { 56 struct thead_dwmac *dwmac = plat->bsp_priv; 57 u32 phyif; 58 59 switch (plat->mac_interface) { 60 case PHY_INTERFACE_MODE_MII: 61 phyif = PHY_INTF_MII_GMII; 62 break; 63 case PHY_INTERFACE_MODE_RGMII: 64 case PHY_INTERFACE_MODE_RGMII_ID: 65 case PHY_INTERFACE_MODE_RGMII_TXID: 66 case PHY_INTERFACE_MODE_RGMII_RXID: 67 phyif = PHY_INTF_RGMII; 68 break; 69 default: 70 dev_err(dwmac->dev, "unsupported phy interface %d\n", 71 plat->mac_interface); 72 return -EINVAL; 73 } 74 75 writel(phyif, dwmac->apb_base + GMAC_INTF_CTRL); 76 return 0; 77 } 78 79 static int thead_dwmac_set_txclk_dir(struct plat_stmmacenet_data *plat) 80 { 81 struct thead_dwmac *dwmac = plat->bsp_priv; 82 u32 txclk_dir; 83 84 switch (plat->mac_interface) { 85 case PHY_INTERFACE_MODE_MII: 86 txclk_dir = TXCLK_DIR_INPUT; 87 break; 88 case PHY_INTERFACE_MODE_RGMII: 89 case PHY_INTERFACE_MODE_RGMII_ID: 90 case PHY_INTERFACE_MODE_RGMII_TXID: 91 case PHY_INTERFACE_MODE_RGMII_RXID: 92 txclk_dir = TXCLK_DIR_OUTPUT; 93 break; 94 default: 95 dev_err(dwmac->dev, "unsupported phy interface %d\n", 96 plat->mac_interface); 97 return -EINVAL; 98 } 99 100 writel(txclk_dir, dwmac->apb_base + GMAC_TXCLK_OEN); 101 return 0; 102 } 103 104 static int thead_set_clk_tx_rate(void *bsp_priv, struct clk *clk_tx_i, 105 phy_interface_t interface, int speed) 106 { 107 struct thead_dwmac *dwmac = bsp_priv; 108 struct plat_stmmacenet_data *plat; 109 unsigned long rate; 110 long tx_rate; 111 u32 div, reg; 112 113 plat = dwmac->plat; 114 115 switch (plat->mac_interface) { 116 /* For MII, rxc/txc is provided by phy */ 117 case PHY_INTERFACE_MODE_MII: 118 return 0; 119 120 case PHY_INTERFACE_MODE_RGMII: 121 case PHY_INTERFACE_MODE_RGMII_ID: 122 case PHY_INTERFACE_MODE_RGMII_RXID: 123 case PHY_INTERFACE_MODE_RGMII_TXID: 124 rate = clk_get_rate(plat->stmmac_clk); 125 126 writel(0, dwmac->apb_base + GMAC_PLLCLK_DIV); 127 128 tx_rate = rgmii_clock(speed); 129 if (tx_rate < 0) { 130 dev_err(dwmac->dev, "invalid speed %d\n", speed); 131 return tx_rate; 132 } 133 134 div = rate / tx_rate; 135 if (rate != tx_rate * div) { 136 dev_err(dwmac->dev, "invalid gmac rate %lu\n", rate); 137 return -EINVAL; 138 } 139 140 reg = FIELD_PREP(GMAC_PLLCLK_DIV_EN, 1) | 141 FIELD_PREP(GMAC_PLLCLK_DIV_NUM, div); 142 writel(reg, dwmac->apb_base + GMAC_PLLCLK_DIV); 143 return 0; 144 145 default: 146 dev_err(dwmac->dev, "unsupported phy interface %d\n", 147 plat->mac_interface); 148 return -EINVAL; 149 } 150 } 151 152 static int thead_dwmac_enable_clk(struct plat_stmmacenet_data *plat) 153 { 154 struct thead_dwmac *dwmac = plat->bsp_priv; 155 u32 reg; 156 157 switch (plat->mac_interface) { 158 case PHY_INTERFACE_MODE_MII: 159 reg = GMAC_RX_CLK_EN | GMAC_TX_CLK_EN; 160 break; 161 162 case PHY_INTERFACE_MODE_RGMII: 163 case PHY_INTERFACE_MODE_RGMII_ID: 164 case PHY_INTERFACE_MODE_RGMII_RXID: 165 case PHY_INTERFACE_MODE_RGMII_TXID: 166 /* use pll */ 167 writel(GMAC_GTXCLK_SEL_PLL, dwmac->apb_base + GMAC_GTXCLK_SEL); 168 reg = GMAC_TX_CLK_EN | GMAC_TX_CLK_N_EN | GMAC_TX_CLK_OUT_EN | 169 GMAC_RX_CLK_EN | GMAC_RX_CLK_N_EN; 170 break; 171 172 default: 173 dev_err(dwmac->dev, "unsupported phy interface %d\n", 174 plat->mac_interface); 175 return -EINVAL; 176 } 177 178 writel(reg, dwmac->apb_base + GMAC_CLK_EN); 179 return 0; 180 } 181 182 static int thead_dwmac_init(struct platform_device *pdev, void *priv) 183 { 184 struct thead_dwmac *dwmac = priv; 185 unsigned int reg; 186 int ret; 187 188 ret = thead_dwmac_set_phy_if(dwmac->plat); 189 if (ret) 190 return ret; 191 192 ret = thead_dwmac_set_txclk_dir(dwmac->plat); 193 if (ret) 194 return ret; 195 196 reg = readl(dwmac->apb_base + GMAC_RXCLK_DELAY_CTRL); 197 reg &= ~(GMAC_RXCLK_DELAY); 198 reg |= FIELD_PREP(GMAC_RXCLK_DELAY, 0); 199 writel(reg, dwmac->apb_base + GMAC_RXCLK_DELAY_CTRL); 200 201 reg = readl(dwmac->apb_base + GMAC_TXCLK_DELAY_CTRL); 202 reg &= ~(GMAC_TXCLK_DELAY); 203 reg |= FIELD_PREP(GMAC_TXCLK_DELAY, 0); 204 writel(reg, dwmac->apb_base + GMAC_TXCLK_DELAY_CTRL); 205 206 return thead_dwmac_enable_clk(dwmac->plat); 207 } 208 209 static int thead_dwmac_probe(struct platform_device *pdev) 210 { 211 struct stmmac_resources stmmac_res; 212 struct plat_stmmacenet_data *plat; 213 struct thead_dwmac *dwmac; 214 void __iomem *apb; 215 int ret; 216 217 ret = stmmac_get_platform_resources(pdev, &stmmac_res); 218 if (ret) 219 return dev_err_probe(&pdev->dev, ret, 220 "failed to get resources\n"); 221 222 plat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac); 223 if (IS_ERR(plat)) 224 return dev_err_probe(&pdev->dev, PTR_ERR(plat), 225 "dt configuration failed\n"); 226 227 dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL); 228 if (!dwmac) 229 return -ENOMEM; 230 231 apb = devm_platform_ioremap_resource(pdev, 1); 232 if (IS_ERR(apb)) 233 return dev_err_probe(&pdev->dev, PTR_ERR(apb), 234 "failed to remap gmac apb registers\n"); 235 236 dwmac->dev = &pdev->dev; 237 dwmac->plat = plat; 238 dwmac->apb_base = apb; 239 plat->bsp_priv = dwmac; 240 plat->set_clk_tx_rate = thead_set_clk_tx_rate; 241 plat->init = thead_dwmac_init; 242 243 return devm_stmmac_pltfr_probe(pdev, plat, &stmmac_res); 244 } 245 246 static const struct of_device_id thead_dwmac_match[] = { 247 { .compatible = "thead,th1520-gmac" }, 248 { /* sentinel */ } 249 }; 250 MODULE_DEVICE_TABLE(of, thead_dwmac_match); 251 252 static struct platform_driver thead_dwmac_driver = { 253 .probe = thead_dwmac_probe, 254 .driver = { 255 .name = "thead-dwmac", 256 .pm = &stmmac_pltfr_pm_ops, 257 .of_match_table = thead_dwmac_match, 258 }, 259 }; 260 module_platform_driver(thead_dwmac_driver); 261 262 MODULE_AUTHOR("Jisheng Zhang <jszhang@kernel.org>"); 263 MODULE_AUTHOR("Drew Fustini <drew@pdp7.com>"); 264 MODULE_DESCRIPTION("T-HEAD DWMAC platform driver"); 265 MODULE_LICENSE("GPL"); 266