Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 | // SPDX-License-Identifier: GPL-2.0 /* * T-HEAD DWMAC platform driver * * Copyright (C) 2021 Alibaba Group Holding Limited. * Copyright (C) 2023 Jisheng Zhang <jszhang@kernel.org> * Copyright (C) 2025 Yao Zi <ziyao@disroot.org> * */ #include <asm/io.h> #include <clk.h> #include <dm.h> #include <linux/bitfield.h> #include <phy.h> #include "designware.h" #define GMAC_CLK_EN 0x00 #define GMAC_TX_CLK_EN BIT(1) #define GMAC_TX_CLK_N_EN BIT(2) #define GMAC_TX_CLK_OUT_EN BIT(3) #define GMAC_RX_CLK_EN BIT(4) #define GMAC_RX_CLK_N_EN BIT(5) #define GMAC_EPHY_REF_CLK_EN BIT(6) #define GMAC_RXCLK_DELAY_CTRL 0x04 #define GMAC_RXCLK_BYPASS BIT(15) #define GMAC_RXCLK_INVERT BIT(14) #define GMAC_RXCLK_DELAY GENMASK(4, 0) #define GMAC_TXCLK_DELAY_CTRL 0x08 #define GMAC_TXCLK_BYPASS BIT(15) #define GMAC_TXCLK_INVERT BIT(14) #define GMAC_TXCLK_DELAY GENMASK(4, 0) #define GMAC_PLLCLK_DIV 0x0c #define GMAC_PLLCLK_DIV_EN BIT(31) #define GMAC_PLLCLK_DIV_NUM GENMASK(7, 0) #define GMAC_GTXCLK_SEL 0x18 #define GMAC_GTXCLK_SEL_PLL BIT(0) #define GMAC_INTF_CTRL 0x1c #define PHY_INTF_MASK BIT(0) #define PHY_INTF_RGMII FIELD_PREP(PHY_INTF_MASK, 1) #define PHY_INTF_MII_GMII FIELD_PREP(PHY_INTF_MASK, 0) #define GMAC_TXCLK_OEN 0x20 #define TXCLK_DIR_MASK BIT(0) #define TXCLK_DIR_OUTPUT FIELD_PREP(TXCLK_DIR_MASK, 0) #define TXCLK_DIR_INPUT FIELD_PREP(TXCLK_DIR_MASK, 1) #define GMAC_RGMII_CLK_RATE 125000000 struct dwmac_thead_plat { struct dw_eth_pdata dw_eth_pdata; void __iomem *apb_base; }; static int dwmac_thead_set_phy_if(struct dwmac_thead_plat *plat) { u32 phyif; switch (plat->dw_eth_pdata.eth_pdata.phy_interface) { case PHY_INTERFACE_MODE_MII: phyif = PHY_INTF_MII_GMII; break; case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII_TXID: case PHY_INTERFACE_MODE_RGMII_RXID: phyif = PHY_INTF_RGMII; break; default: return -EINVAL; } writel(phyif, plat->apb_base + GMAC_INTF_CTRL); return 0; } static int dwmac_thead_set_txclk_dir(struct dwmac_thead_plat *plat) { u32 txclk_dir; switch (plat->dw_eth_pdata.eth_pdata.phy_interface) { case PHY_INTERFACE_MODE_MII: txclk_dir = TXCLK_DIR_INPUT; break; case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII_TXID: case PHY_INTERFACE_MODE_RGMII_RXID: txclk_dir = TXCLK_DIR_OUTPUT; break; default: return -EINVAL; } writel(txclk_dir, plat->apb_base + GMAC_TXCLK_OEN); return 0; } static unsigned long dwmac_thead_rgmii_tx_rate(int speed) { switch (speed) { case 10: return 2500000; case 100: return 25000000; case 1000: return 125000000; } return -EINVAL; } static int dwmac_thead_set_clk_tx_rate(struct dwmac_thead_plat *plat, struct dw_eth_dev *edev, unsigned long tx_rate) { unsigned long rate; u32 div, reg; rate = clk_get_rate(&edev->clocks[0]); writel(0, plat->apb_base + GMAC_PLLCLK_DIV); div = rate / tx_rate; if (rate != tx_rate * div) { pr_err("invalid gmac rate %lu\n", rate); return -EINVAL; } reg = FIELD_PREP(GMAC_PLLCLK_DIV_EN, 1) | FIELD_PREP(GMAC_PLLCLK_DIV_NUM, div); writel(reg, plat->apb_base + GMAC_PLLCLK_DIV); return 0; } static int dwmac_thead_enable_clk(struct dwmac_thead_plat *plat) { u32 reg; switch (plat->dw_eth_pdata.eth_pdata.phy_interface) { case PHY_INTERFACE_MODE_MII: reg = GMAC_RX_CLK_EN | GMAC_TX_CLK_EN; break; case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_TXID: /* use pll */ writel(GMAC_GTXCLK_SEL_PLL, plat->apb_base + GMAC_GTXCLK_SEL); reg = GMAC_TX_CLK_EN | GMAC_TX_CLK_N_EN | GMAC_TX_CLK_OUT_EN | GMAC_RX_CLK_EN | GMAC_RX_CLK_N_EN; break; default: return -EINVAL; } writel(reg, plat->apb_base + GMAC_CLK_EN); return 0; } static int dwmac_thead_eth_start(struct udevice *dev) { struct dwmac_thead_plat *plat = dev_get_plat(dev); struct dw_eth_dev *edev = dev_get_priv(dev); phy_interface_t interface; bool is_rgmii; long tx_rate; int ret; interface = plat->dw_eth_pdata.eth_pdata.phy_interface; is_rgmii = (interface == PHY_INTERFACE_MODE_RGMII) | (interface == PHY_INTERFACE_MODE_RGMII_ID) | (interface == PHY_INTERFACE_MODE_RGMII_RXID) | (interface == PHY_INTERFACE_MODE_RGMII_TXID); /* * When operating in RGMII mode, the TX clock is generated by an * internal divider and fed to the MAC. Configure and enable it before * initializing the MAC. */ if (is_rgmii) { ret = dwmac_thead_set_clk_tx_rate(plat, edev, GMAC_RGMII_CLK_RATE); if (ret) return ret; } ret = designware_eth_init(edev, plat->dw_eth_pdata.eth_pdata.enetaddr); if (ret) return ret; if (is_rgmii) { tx_rate = dwmac_thead_rgmii_tx_rate(edev->phydev->speed); if (tx_rate < 0) return tx_rate; ret = dwmac_thead_set_clk_tx_rate(plat, edev, tx_rate); if (ret) return ret; } ret = designware_eth_enable(edev); if (ret) return ret; return 0; } static int dwmac_thead_probe(struct udevice *dev) { struct dwmac_thead_plat *plat = dev_get_plat(dev); unsigned int reg; int ret; ret = designware_eth_probe(dev); if (ret) return ret; ret = dwmac_thead_set_phy_if(plat); if (ret) { pr_err("failed to set phy interface: %d\n", ret); return ret; } ret = dwmac_thead_set_txclk_dir(plat); if (ret) { pr_err("failed to set TX clock direction: %d\n", ret); return ret; } reg = readl(plat->apb_base + GMAC_RXCLK_DELAY_CTRL); reg &= ~(GMAC_RXCLK_DELAY); reg |= FIELD_PREP(GMAC_RXCLK_DELAY, 0); writel(reg, plat->apb_base + GMAC_RXCLK_DELAY_CTRL); reg = readl(plat->apb_base + GMAC_TXCLK_DELAY_CTRL); reg &= ~(GMAC_TXCLK_DELAY); reg |= FIELD_PREP(GMAC_TXCLK_DELAY, 0); writel(reg, plat->apb_base + GMAC_TXCLK_DELAY_CTRL); ret = dwmac_thead_enable_clk(plat); if (ret) pr_err("failed to enable clock: %d\n", ret); return ret; } static int dwmac_thead_of_to_plat(struct udevice *dev) { struct dwmac_thead_plat *pdata = dev_get_plat(dev); pdata->apb_base = dev_read_addr_index_ptr(dev, 1); if (!pdata->apb_base) { pr_err("failed to get apb registers\n"); return -ENOENT; } return designware_eth_of_to_plat(dev); } static const struct eth_ops dwmac_thead_eth_ops = { .start = dwmac_thead_eth_start, .send = designware_eth_send, .recv = designware_eth_recv, .free_pkt = designware_eth_free_pkt, .stop = designware_eth_stop, .write_hwaddr = designware_eth_write_hwaddr, }; static const struct udevice_id dwmac_thead_match[] = { { .compatible = "thead,th1520-gmac" }, { /* sentinel */ } }; U_BOOT_DRIVER(dwmac_thead) = { .name = "dwmac_thead", .id = UCLASS_ETH, .of_match = dwmac_thead_match, .of_to_plat = dwmac_thead_of_to_plat, .probe = dwmac_thead_probe, .ops = &dwmac_thead_eth_ops, .priv_auto = sizeof(struct dw_eth_dev), .plat_auto = sizeof(struct dwmac_thead_plat), .flags = DM_FLAG_ALLOC_PRIV_DMA, }; |