MD Danish Anwar | dd18b89 | 2024-04-04 12:38:02 +0530 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | /* |
| 3 | * Texas Instruments K3 AM65 PRU Ethernet Driver |
| 4 | * |
| 5 | * Copyright (C) 2018-2024 Texas Instruments Incorporated - https://www.ti.com/ |
| 6 | * |
| 7 | */ |
| 8 | |
| 9 | #include <asm/io.h> |
| 10 | #include <asm/processor.h> |
| 11 | #include <clk.h> |
| 12 | #include <dm/lists.h> |
| 13 | #include <dm/device.h> |
| 14 | #include <dma-uclass.h> |
| 15 | #include <dm/of_access.h> |
| 16 | #include <dm/pinctrl.h> |
| 17 | #include <fs_loader.h> |
| 18 | #include <miiphy.h> |
| 19 | #include <net.h> |
| 20 | #include <phy.h> |
| 21 | #include <power-domain.h> |
| 22 | #include <linux/soc/ti/ti-udma.h> |
| 23 | #include <regmap.h> |
| 24 | #include <remoteproc.h> |
| 25 | #include <syscon.h> |
| 26 | #include <soc.h> |
| 27 | #include <linux/pruss_driver.h> |
| 28 | #include <dm/device_compat.h> |
| 29 | |
| 30 | #include "icssg_prueth.h" |
| 31 | #include "icss_mii_rt.h" |
| 32 | |
| 33 | #define ICSS_SLICE0 0 |
| 34 | #define ICSS_SLICE1 1 |
| 35 | |
| 36 | #ifdef PKTSIZE_ALIGN |
| 37 | #define UDMA_RX_BUF_SIZE PKTSIZE_ALIGN |
| 38 | #else |
| 39 | #define UDMA_RX_BUF_SIZE ALIGN(PKTSIZE, ARCH_DMA_MINALIGN) |
| 40 | #endif |
| 41 | |
| 42 | #ifdef PKTBUFSRX |
| 43 | #define UDMA_RX_DESC_NUM PKTBUFSRX |
| 44 | #else |
| 45 | #define UDMA_RX_DESC_NUM 4 |
| 46 | #endif |
| 47 | |
| 48 | /* Config region lies in shared RAM */ |
| 49 | #define ICSS_CONFIG_OFFSET_SLICE0 0 |
| 50 | #define ICSS_CONFIG_OFFSET_SLICE1 0x8000 |
| 51 | |
| 52 | /* Firmware flags */ |
| 53 | #define ICSS_SET_RUN_FLAG_VLAN_ENABLE BIT(0) /* switch only */ |
| 54 | #define ICSS_SET_RUN_FLAG_FLOOD_UNICAST BIT(1) /* switch only */ |
| 55 | #define ICSS_SET_RUN_FLAG_PROMISC BIT(2) /* MAC only */ |
| 56 | #define ICSS_SET_RUN_FLAG_MULTICAST_PROMISC BIT(3) /* MAC only */ |
| 57 | |
| 58 | /* CTRLMMR_ICSSG_RGMII_CTRL register bits */ |
| 59 | #define ICSSG_CTRL_RGMII_ID_MODE BIT(24) |
| 60 | |
| 61 | /* Management packet type */ |
| 62 | #define PRUETH_PKT_TYPE_CMD 0x10 |
| 63 | |
| 64 | /* Number of PRU Cores per Slice */ |
| 65 | #define ICSSG_NUM_PRU_CORES 3 |
| 66 | |
| 67 | static int icssg_gmii_select(struct prueth_priv *priv) |
| 68 | { |
| 69 | struct phy_device *phydev = priv->phydev; |
| 70 | |
| 71 | if (phydev->interface != PHY_INTERFACE_MODE_MII && |
| 72 | phydev->interface < PHY_INTERFACE_MODE_RGMII && |
| 73 | phydev->interface > PHY_INTERFACE_MODE_RGMII_TXID) { |
| 74 | dev_err(priv->dev, "PHY mode unsupported %s\n", |
| 75 | phy_string_for_interface(phydev->interface)); |
| 76 | return -EINVAL; |
| 77 | } |
| 78 | |
| 79 | /* AM65 SR2.0 has TX Internal delay always enabled by hardware |
| 80 | * and it is not possible to disable TX Internal delay. The below |
| 81 | * switch case block describes how we handle different phy modes |
| 82 | * based on hardware restriction. |
| 83 | */ |
| 84 | switch (phydev->interface) { |
| 85 | case PHY_INTERFACE_MODE_RGMII_ID: |
| 86 | phydev->interface = PHY_INTERFACE_MODE_RGMII_RXID; |
| 87 | break; |
| 88 | case PHY_INTERFACE_MODE_RGMII_TXID: |
| 89 | phydev->interface = PHY_INTERFACE_MODE_RGMII; |
| 90 | break; |
| 91 | case PHY_INTERFACE_MODE_RGMII: |
| 92 | case PHY_INTERFACE_MODE_RGMII_RXID: |
| 93 | dev_err(priv->dev, "RGMII mode without TX delay is not supported"); |
| 94 | return -EINVAL; |
| 95 | default: |
| 96 | break; |
| 97 | } |
| 98 | |
| 99 | return 0; |
| 100 | } |
| 101 | |
| 102 | static int icssg_phy_init(struct udevice *dev) |
| 103 | { |
| 104 | struct prueth_priv *priv = dev_get_priv(dev); |
| 105 | struct phy_device *phydev; |
| 106 | u32 supported = PHY_GBIT_FEATURES; |
| 107 | int ret; |
| 108 | |
| 109 | phydev = dm_eth_phy_connect(dev); |
| 110 | if (!phydev) { |
| 111 | dev_err(dev, "phy_connect() failed\n"); |
| 112 | return -ENODEV; |
| 113 | } |
| 114 | |
| 115 | /* disable unsupported features */ |
| 116 | supported &= ~(PHY_10BT_FEATURES | |
| 117 | SUPPORTED_100baseT_Half | |
| 118 | SUPPORTED_1000baseT_Half | |
| 119 | SUPPORTED_Pause | |
| 120 | SUPPORTED_Asym_Pause); |
| 121 | |
| 122 | phydev->supported &= supported; |
| 123 | phydev->advertising = phydev->supported; |
| 124 | priv->phydev = phydev; |
| 125 | |
| 126 | ret = icssg_gmii_select(priv); |
| 127 | if (ret) |
| 128 | goto out; |
| 129 | |
| 130 | ret = phy_config(phydev); |
| 131 | if (ret < 0) |
| 132 | dev_err(dev, "phy_config() failed: %d", ret); |
| 133 | out: |
| 134 | return ret; |
| 135 | } |
| 136 | |
| 137 | static void icssg_config_set_speed(struct prueth_priv *priv, int speed) |
| 138 | { |
| 139 | struct prueth *prueth = priv->prueth; |
| 140 | u8 fw_speed; |
| 141 | |
| 142 | switch (speed) { |
| 143 | case SPEED_1000: |
| 144 | fw_speed = FW_LINK_SPEED_1G; |
| 145 | break; |
| 146 | case SPEED_100: |
| 147 | fw_speed = FW_LINK_SPEED_100M; |
| 148 | break; |
| 149 | case SPEED_10: |
| 150 | fw_speed = FW_LINK_SPEED_10M; |
| 151 | break; |
| 152 | default: |
| 153 | /* Other links speeds not supported */ |
| 154 | dev_err(priv->dev, "Unsupported link speed\n"); |
| 155 | return; |
| 156 | } |
| 157 | |
| 158 | writeb(fw_speed, prueth->dram[priv->port_id].pa + PORT_LINK_SPEED_OFFSET); |
| 159 | } |
| 160 | |
| 161 | static int icssg_update_link(struct prueth_priv *priv) |
| 162 | { |
| 163 | struct phy_device *phy = priv->phydev; |
| 164 | struct prueth *prueth = priv->prueth; |
| 165 | bool gig_en = false, full_duplex = false; |
| 166 | |
| 167 | if (phy->link) { /* link up */ |
| 168 | if (phy->speed == SPEED_1000) |
| 169 | gig_en = true; |
| 170 | if (phy->duplex == DUPLEX_FULL) |
| 171 | full_duplex = true; |
| 172 | /* Set the RGMII cfg for gig en and full duplex */ |
| 173 | icssg_update_rgmii_cfg(prueth->miig_rt, phy->speed, full_duplex, |
| 174 | priv->port_id, priv); |
| 175 | /* update the Tx IPG based on 100M/1G speed */ |
| 176 | icssg_config_ipg(priv, phy->speed, priv->port_id); |
| 177 | |
| 178 | /* Send command to firmware to update Speed setting */ |
| 179 | icssg_config_set_speed(priv, phy->speed); |
| 180 | |
| 181 | /* Enable PORT FORWARDING */ |
| 182 | emac_set_port_state(priv, ICSSG_EMAC_PORT_FORWARD); |
| 183 | |
| 184 | printf("link up on port %d, speed %d, %s duplex\n", |
| 185 | priv->port_id, phy->speed, |
| 186 | (phy->duplex == DUPLEX_FULL) ? "full" : "half"); |
| 187 | } else { |
| 188 | emac_set_port_state(priv, ICSSG_EMAC_PORT_DISABLE); |
| 189 | printf("link down on port %d\n", priv->port_id); |
| 190 | } |
| 191 | |
| 192 | return phy->link; |
| 193 | } |
| 194 | |
| 195 | struct icssg_firmwares { |
| 196 | char *pru; |
| 197 | char *rtu; |
| 198 | char *txpru; |
| 199 | }; |
| 200 | |
| 201 | static struct icssg_firmwares icssg_emac_firmwares[] = { |
| 202 | { |
| 203 | .pru = "/lib/firmware/ti-pruss/am65x-sr2-pru0-prueth-fw.elf", |
| 204 | .rtu = "/lib/firmware/ti-pruss/am65x-sr2-rtu0-prueth-fw.elf", |
| 205 | .txpru = "/lib/firmware/ti-pruss/am65x-sr2-txpru0-prueth-fw.elf", |
| 206 | }, |
| 207 | { |
| 208 | .pru = "/lib/firmware/ti-pruss/am65x-sr2-pru1-prueth-fw.elf", |
| 209 | .rtu = "/lib/firmware/ti-pruss/am65x-sr2-rtu1-prueth-fw.elf", |
| 210 | .txpru = "/lib/firmware/ti-pruss/am65x-sr2-txpru1-prueth-fw.elf", |
| 211 | } |
| 212 | }; |
| 213 | |
| 214 | static int icssg_start_pru_cores(struct udevice *dev) |
| 215 | { |
| 216 | struct prueth_priv *priv = dev_get_priv(dev); |
| 217 | struct prueth *prueth = priv->prueth; |
| 218 | struct icssg_firmwares *firmwares; |
| 219 | struct udevice *rproc_dev = NULL; |
| 220 | int ret, slice; |
| 221 | u32 phandle; |
| 222 | u8 index; |
| 223 | |
| 224 | slice = priv->port_id; |
| 225 | index = slice * ICSSG_NUM_PRU_CORES; |
| 226 | firmwares = icssg_emac_firmwares; |
| 227 | |
| 228 | ofnode_read_u32_index(dev_ofnode(prueth->dev), "ti,prus", index, &phandle); |
| 229 | ret = uclass_get_device_by_phandle_id(UCLASS_REMOTEPROC, phandle, &rproc_dev); |
| 230 | if (ret) { |
| 231 | dev_err(dev, "Unknown remote processor with phandle '0x%x' requested(%d)\n", |
| 232 | phandle, ret); |
| 233 | return ret; |
| 234 | } |
| 235 | |
| 236 | prueth->pru_core_id = dev_seq(rproc_dev); |
| 237 | ret = rproc_set_firmware(rproc_dev, firmwares[slice].pru); |
| 238 | if (ret) |
| 239 | return ret; |
| 240 | |
| 241 | ret = rproc_boot(rproc_dev); |
| 242 | if (ret) { |
| 243 | dev_err(dev, "failed to boot PRU%d: %d\n", slice, ret); |
| 244 | return -EINVAL; |
| 245 | } |
| 246 | |
| 247 | ofnode_read_u32_index(dev_ofnode(prueth->dev), "ti,prus", index + 1, &phandle); |
| 248 | ret = uclass_get_device_by_phandle_id(UCLASS_REMOTEPROC, phandle, &rproc_dev); |
| 249 | if (ret) { |
| 250 | dev_err(dev, "Unknown remote processor with phandle '0x%x' requested(%d)\n", |
| 251 | phandle, ret); |
| 252 | goto halt_pru; |
| 253 | } |
| 254 | |
| 255 | prueth->rtu_core_id = dev_seq(rproc_dev); |
| 256 | ret = rproc_set_firmware(rproc_dev, firmwares[slice].rtu); |
| 257 | if (ret) |
| 258 | goto halt_pru; |
| 259 | |
| 260 | ret = rproc_boot(rproc_dev); |
| 261 | if (ret) { |
| 262 | dev_err(dev, "failed to boot RTU%d: %d\n", slice, ret); |
| 263 | goto halt_pru; |
| 264 | } |
| 265 | |
| 266 | ofnode_read_u32_index(dev_ofnode(prueth->dev), "ti,prus", index + 2, &phandle); |
| 267 | ret = uclass_get_device_by_phandle_id(UCLASS_REMOTEPROC, phandle, &rproc_dev); |
| 268 | if (ret) { |
| 269 | dev_err(dev, "Unknown remote processor with phandle '0x%x' requested(%d)\n", |
| 270 | phandle, ret); |
| 271 | goto halt_rtu; |
| 272 | } |
| 273 | |
| 274 | prueth->txpru_core_id = dev_seq(rproc_dev); |
| 275 | ret = rproc_set_firmware(rproc_dev, firmwares[slice].txpru); |
| 276 | if (ret) |
| 277 | goto halt_rtu; |
| 278 | |
| 279 | ret = rproc_boot(rproc_dev); |
| 280 | if (ret) { |
| 281 | dev_err(dev, "failed to boot TXPRU%d: %d\n", slice, ret); |
| 282 | goto halt_rtu; |
| 283 | } |
| 284 | |
| 285 | return 0; |
| 286 | |
| 287 | halt_rtu: |
| 288 | rproc_stop(prueth->rtu_core_id); |
| 289 | |
| 290 | halt_pru: |
| 291 | rproc_stop(prueth->pru_core_id); |
| 292 | return ret; |
| 293 | } |
| 294 | |
| 295 | static int icssg_stop_pru_cores(struct udevice *dev) |
| 296 | { |
| 297 | struct prueth_priv *priv = dev_get_priv(dev); |
| 298 | struct prueth *prueth = priv->prueth; |
| 299 | |
| 300 | rproc_stop(prueth->pru_core_id); |
| 301 | rproc_stop(prueth->rtu_core_id); |
| 302 | rproc_stop(prueth->txpru_core_id); |
| 303 | |
| 304 | return 0; |
| 305 | } |
| 306 | |
| 307 | static int prueth_start(struct udevice *dev) |
| 308 | { |
| 309 | struct ti_udma_drv_chan_cfg_data *dma_rx_cfg_data; |
| 310 | struct eth_pdata *pdata = dev_get_plat(dev); |
| 311 | struct prueth_priv *priv = dev_get_priv(dev); |
| 312 | struct prueth *prueth = priv->prueth; |
| 313 | struct icssg_flow_cfg *flow_cfg; |
| 314 | u8 *hwaddr = pdata->enetaddr; |
| 315 | char chn_name[16]; |
| 316 | void *config; |
| 317 | int ret, i; |
| 318 | |
| 319 | icssg_class_set_mac_addr(prueth->miig_rt, priv->port_id, hwaddr); |
| 320 | icssg_ft1_set_mac_addr(prueth->miig_rt, priv->port_id, hwaddr); |
| 321 | icssg_class_default(prueth->miig_rt, priv->port_id, 0); |
| 322 | |
| 323 | /* Set Load time configuration */ |
| 324 | icssg_config(priv); |
| 325 | |
| 326 | ret = icssg_start_pru_cores(dev); |
| 327 | if (ret) |
| 328 | return ret; |
| 329 | |
| 330 | /* To differentiate channels for SLICE0 vs SLICE1 */ |
| 331 | snprintf(chn_name, sizeof(chn_name), "tx%d-0", priv->port_id); |
| 332 | |
| 333 | ret = dma_get_by_name(prueth->dev, chn_name, &prueth->dma_tx); |
| 334 | if (ret) |
| 335 | dev_err(dev, "TX dma get failed %d\n", ret); |
| 336 | |
| 337 | snprintf(chn_name, sizeof(chn_name), "rx%d", priv->port_id); |
| 338 | ret = dma_get_by_name(prueth->dev, chn_name, &prueth->dma_rx); |
| 339 | if (ret) |
| 340 | dev_err(dev, "RX dma get failed %d\n", ret); |
| 341 | |
| 342 | for (i = 0; i < UDMA_RX_DESC_NUM; i++) { |
| 343 | ret = dma_prepare_rcv_buf(&prueth->dma_rx, |
| 344 | net_rx_packets[i], |
| 345 | UDMA_RX_BUF_SIZE); |
| 346 | if (ret) |
| 347 | dev_err(dev, "RX dma add buf failed %d\n", ret); |
| 348 | } |
| 349 | |
| 350 | ret = dma_enable(&prueth->dma_tx); |
| 351 | if (ret) { |
| 352 | dev_err(dev, "TX dma_enable failed %d\n", ret); |
| 353 | goto tx_fail; |
| 354 | } |
| 355 | |
| 356 | ret = dma_enable(&prueth->dma_rx); |
| 357 | if (ret) { |
| 358 | dev_err(dev, "RX dma_enable failed %d\n", ret); |
| 359 | goto rx_fail; |
| 360 | } |
| 361 | |
| 362 | /* check if the rx_flow_id of dma_rx is as expected since |
| 363 | * driver hardcode that value in config struct to firmware |
| 364 | * in probe. Just add this sanity check to catch any change |
| 365 | * to rx channel assignment in the future. |
| 366 | */ |
| 367 | dma_get_cfg(&prueth->dma_rx, 0, (void **)&dma_rx_cfg_data); |
| 368 | config = (void *)(prueth->dram[priv->port_id].pa + ICSSG_CONFIG_OFFSET); |
| 369 | |
| 370 | flow_cfg = config + PSI_L_REGULAR_FLOW_ID_BASE_OFFSET; |
| 371 | writew(dma_rx_cfg_data->flow_id_base, &flow_cfg->rx_base_flow); |
| 372 | writew(0, &flow_cfg->mgm_base_flow); |
| 373 | |
| 374 | dev_info(dev, "K3 ICSSG: rflow_id_base: %u, chn_name = %s\n", |
| 375 | dma_rx_cfg_data->flow_id_base, chn_name); |
| 376 | |
MD Danish Anwar | 6b8c29e | 2024-04-04 12:38:03 +0530 | [diff] [blame] | 377 | ret = emac_fdb_flow_id_updated(priv); |
| 378 | if (ret) { |
| 379 | dev_err(dev, "Failed to update Rx Flow ID %d", ret); |
| 380 | goto phy_fail; |
| 381 | } |
| 382 | |
MD Danish Anwar | dd18b89 | 2024-04-04 12:38:02 +0530 | [diff] [blame] | 383 | ret = phy_startup(priv->phydev); |
| 384 | if (ret) { |
| 385 | dev_err(dev, "phy_startup failed\n"); |
| 386 | goto phy_fail; |
| 387 | } |
| 388 | |
| 389 | ret = icssg_update_link(priv); |
| 390 | if (!ret) { |
| 391 | ret = -ENODEV; |
| 392 | goto phy_shut; |
| 393 | } |
| 394 | |
| 395 | return 0; |
| 396 | |
| 397 | phy_shut: |
| 398 | phy_shutdown(priv->phydev); |
| 399 | phy_fail: |
| 400 | dma_disable(&prueth->dma_rx); |
| 401 | dma_free(&prueth->dma_rx); |
| 402 | rx_fail: |
| 403 | dma_disable(&prueth->dma_tx); |
| 404 | dma_free(&prueth->dma_tx); |
| 405 | |
| 406 | tx_fail: |
| 407 | icssg_class_disable(prueth->miig_rt, priv->port_id); |
| 408 | |
| 409 | return ret; |
| 410 | } |
| 411 | |
| 412 | static int prueth_send(struct udevice *dev, void *packet, int length) |
| 413 | { |
| 414 | struct prueth_priv *priv = dev_get_priv(dev); |
| 415 | struct prueth *prueth = priv->prueth; |
| 416 | int ret; |
| 417 | |
| 418 | ret = dma_send(&prueth->dma_tx, packet, length, NULL); |
| 419 | |
| 420 | return ret; |
| 421 | } |
| 422 | |
| 423 | static int prueth_recv(struct udevice *dev, int flags, uchar **packetp) |
| 424 | { |
| 425 | struct prueth_priv *priv = dev_get_priv(dev); |
| 426 | struct prueth *prueth = priv->prueth; |
| 427 | int ret; |
| 428 | |
| 429 | /* try to receive a new packet */ |
| 430 | ret = dma_receive(&prueth->dma_rx, (void **)packetp, NULL); |
| 431 | |
| 432 | return ret; |
| 433 | } |
| 434 | |
| 435 | static int prueth_free_pkt(struct udevice *dev, uchar *packet, int length) |
| 436 | { |
| 437 | struct prueth_priv *priv = dev_get_priv(dev); |
| 438 | struct prueth *prueth = priv->prueth; |
| 439 | int ret = 0; |
| 440 | |
| 441 | if (length > 0) { |
| 442 | u32 pkt = prueth->rx_next % UDMA_RX_DESC_NUM; |
| 443 | |
| 444 | dev_dbg(dev, "%s length:%d pkt:%u\n", __func__, length, pkt); |
| 445 | |
| 446 | ret = dma_prepare_rcv_buf(&prueth->dma_rx, |
| 447 | net_rx_packets[pkt], |
| 448 | UDMA_RX_BUF_SIZE); |
| 449 | prueth->rx_next++; |
| 450 | } |
| 451 | |
| 452 | return ret; |
| 453 | } |
| 454 | |
| 455 | static void prueth_stop(struct udevice *dev) |
| 456 | { |
| 457 | struct prueth_priv *priv = dev_get_priv(dev); |
| 458 | struct prueth *prueth = priv->prueth; |
| 459 | |
| 460 | phy_shutdown(priv->phydev); |
| 461 | |
| 462 | dma_disable(&prueth->dma_tx); |
| 463 | dma_disable(&prueth->dma_rx); |
| 464 | |
| 465 | icssg_stop_pru_cores(dev); |
| 466 | |
| 467 | dma_free(&prueth->dma_tx); |
| 468 | dma_free(&prueth->dma_rx); |
| 469 | } |
| 470 | |
| 471 | static const struct eth_ops prueth_ops = { |
| 472 | .start = prueth_start, |
| 473 | .send = prueth_send, |
| 474 | .recv = prueth_recv, |
| 475 | .free_pkt = prueth_free_pkt, |
| 476 | .stop = prueth_stop, |
| 477 | }; |
| 478 | |
| 479 | static int icssg_ofdata_parse_phy(struct udevice *dev) |
| 480 | { |
| 481 | struct prueth_priv *priv = dev_get_priv(dev); |
| 482 | |
| 483 | dev_read_u32(dev, "reg", &priv->port_id); |
| 484 | priv->phy_interface = dev_read_phy_mode(dev); |
| 485 | if (priv->phy_interface == PHY_INTERFACE_MODE_NA) { |
| 486 | dev_err(dev, "Invalid PHY mode '%s', port %u\n", |
| 487 | phy_string_for_interface(priv->phy_interface), |
| 488 | priv->port_id); |
| 489 | return -EINVAL; |
| 490 | } |
| 491 | |
| 492 | return 0; |
| 493 | } |
| 494 | |
| 495 | static int prueth_port_probe(struct udevice *dev) |
| 496 | { |
| 497 | struct prueth_priv *priv = dev_get_priv(dev); |
| 498 | struct prueth *prueth; |
| 499 | char portname[15]; |
| 500 | int ret; |
| 501 | |
| 502 | priv->dev = dev; |
| 503 | prueth = dev_get_priv(dev->parent); |
| 504 | priv->prueth = prueth; |
| 505 | |
| 506 | sprintf(portname, "%s-%s", dev->parent->name, dev->name); |
| 507 | |
| 508 | device_set_name(dev, portname); |
| 509 | |
| 510 | ret = icssg_ofdata_parse_phy(dev); |
| 511 | if (ret) |
| 512 | goto out; |
| 513 | |
| 514 | ret = icssg_phy_init(dev); |
| 515 | if (ret) |
| 516 | goto out; |
| 517 | |
| 518 | ret = pruss_request_mem_region(prueth->pruss, |
| 519 | priv->port_id ? PRUSS_MEM_DRAM1 : PRUSS_MEM_DRAM0, |
| 520 | &prueth->dram[priv->port_id]); |
| 521 | if (ret) { |
| 522 | dev_err(dev, "could not request DRAM%d region\n", priv->port_id); |
| 523 | return ret; |
| 524 | } |
| 525 | out: |
| 526 | return ret; |
| 527 | } |
| 528 | |
| 529 | static int prueth_probe(struct udevice *dev) |
| 530 | { |
| 531 | ofnode node, pruss_node, mdio_node, sram_node, curr_sram_node; |
| 532 | struct prueth *prueth = dev_get_priv(dev); |
| 533 | u32 phandle, err, sp, prev_end_addr; |
| 534 | struct udevice **prussdev = NULL; |
| 535 | ofnode eth_ports_node, eth_node; |
| 536 | struct udevice *port_dev; |
| 537 | int ret = 0; |
| 538 | |
| 539 | prueth->dev = dev; |
| 540 | |
| 541 | err = ofnode_read_u32(dev_ofnode(dev), "ti,prus", &phandle); |
| 542 | if (err) |
| 543 | return err; |
| 544 | |
| 545 | node = ofnode_get_by_phandle(phandle); |
| 546 | if (!ofnode_valid(node)) |
| 547 | return -EINVAL; |
| 548 | |
| 549 | pruss_node = ofnode_get_parent(node); |
| 550 | ret = device_get_global_by_ofnode(pruss_node, prussdev); |
| 551 | if (ret) |
| 552 | dev_err(dev, "error getting the pruss dev\n"); |
| 553 | prueth->pruss = *prussdev; |
| 554 | |
| 555 | ret = pruss_request_mem_region(*prussdev, PRUSS_MEM_SHRD_RAM2, |
| 556 | &prueth->shram); |
| 557 | if (ret) |
| 558 | return ret; |
| 559 | |
| 560 | ret = pruss_request_tm_region(*prussdev, &prueth->tmaddr); |
| 561 | if (ret) |
| 562 | return ret; |
| 563 | |
| 564 | prueth->miig_rt = syscon_regmap_lookup_by_phandle(dev, "ti,mii-g-rt"); |
| 565 | if (!prueth->miig_rt) { |
| 566 | dev_err(dev, "couldn't get mii-g-rt syscon regmap\n"); |
| 567 | return -ENODEV; |
| 568 | } |
| 569 | |
| 570 | prueth->mii_rt = syscon_regmap_lookup_by_phandle(dev, "ti,mii-rt"); |
| 571 | if (!prueth->mii_rt) { |
| 572 | dev_err(dev, "couldn't get mii-rt syscon regmap\n"); |
| 573 | return -ENODEV; |
| 574 | } |
| 575 | |
| 576 | ret = ofnode_read_u32(dev_ofnode(dev), "sram", &sp); |
| 577 | if (ret) { |
| 578 | dev_err(dev, "sram node fetch failed %d\n", ret); |
| 579 | return ret; |
| 580 | } |
| 581 | |
| 582 | sram_node = ofnode_get_by_phandle(sp); |
| 583 | if (!ofnode_valid(sram_node)) |
| 584 | return -EINVAL; |
| 585 | |
| 586 | prev_end_addr = ofnode_get_addr(sram_node); |
| 587 | |
| 588 | ofnode_for_each_subnode(curr_sram_node, sram_node) { |
| 589 | u32 start_addr, size, end_addr, avail; |
| 590 | const char *name; |
| 591 | |
| 592 | name = ofnode_get_name(curr_sram_node); |
| 593 | start_addr = ofnode_get_addr(curr_sram_node); |
| 594 | size = ofnode_get_size(curr_sram_node); |
| 595 | end_addr = start_addr + size; |
| 596 | avail = start_addr - prev_end_addr; |
| 597 | |
| 598 | if (avail > MSMC_RAM_SIZE) |
| 599 | break; |
| 600 | |
| 601 | prev_end_addr = end_addr; |
| 602 | } |
| 603 | |
| 604 | prueth->sram_pa = prev_end_addr; |
| 605 | if (prueth->sram_pa % SZ_64K != 0) { |
| 606 | /* This is constraint for SR2.0 firmware */ |
| 607 | dev_err(dev, "sram address needs to be 64KB aligned\n"); |
| 608 | return -EINVAL; |
| 609 | } |
| 610 | dev_dbg(dev, "sram: addr %x size %x\n", prueth->sram_pa, MSMC_RAM_SIZE); |
| 611 | |
| 612 | mdio_node = ofnode_find_subnode(pruss_node, "mdio"); |
| 613 | prueth->mdio_base = ofnode_get_addr(mdio_node); |
| 614 | ofnode_read_u32(mdio_node, "bus_freq", &prueth->mdio_freq); |
| 615 | |
| 616 | ret = clk_get_by_name_nodev(mdio_node, "fck", &prueth->mdiofck); |
| 617 | if (ret) { |
| 618 | dev_err(dev, "failed to get clock %d\n", ret); |
| 619 | return ret; |
| 620 | } |
| 621 | |
| 622 | ret = clk_enable(&prueth->mdiofck); |
| 623 | if (ret) { |
| 624 | dev_err(dev, "clk_enable failed %d\n", ret); |
| 625 | return ret; |
| 626 | } |
| 627 | |
| 628 | eth_ports_node = dev_read_subnode(dev, "ethernet-ports"); |
| 629 | if (!ofnode_valid(eth_ports_node)) |
| 630 | return -ENOENT; |
| 631 | |
| 632 | ofnode_for_each_subnode(eth_node, eth_ports_node) { |
| 633 | const char *node_name; |
| 634 | u32 port_id; |
| 635 | bool disabled; |
| 636 | |
| 637 | node_name = ofnode_get_name(eth_node); |
| 638 | disabled = !ofnode_is_enabled(eth_node); |
| 639 | ret = ofnode_read_u32(eth_node, "reg", &port_id); |
| 640 | if (ret) |
| 641 | dev_err(dev, "%s: error reading port_id (%d)\n", node_name, ret); |
| 642 | |
| 643 | if (port_id >= PRUETH_NUM_MACS) { |
| 644 | dev_err(dev, "%s: invalid port_id (%d)\n", node_name, port_id); |
| 645 | return -EINVAL; |
| 646 | } |
| 647 | |
| 648 | if (port_id < 0) |
| 649 | continue; |
| 650 | if (disabled) |
| 651 | continue; |
| 652 | |
| 653 | ret = device_bind_driver_to_node(dev, "prueth_port", |
| 654 | ofnode_get_name(eth_node), |
| 655 | eth_node, &port_dev); |
| 656 | if (ret) { |
| 657 | dev_err(dev, "Failed to bind to %s node\n", ofnode_get_name(eth_node)); |
| 658 | goto out; |
| 659 | } |
| 660 | } |
| 661 | |
| 662 | return 0; |
| 663 | out: |
| 664 | clk_disable(&prueth->mdiofck); |
| 665 | |
| 666 | return ret; |
| 667 | } |
| 668 | |
| 669 | static const struct udevice_id prueth_ids[] = { |
| 670 | { .compatible = "ti,am654-icssg-prueth" }, |
| 671 | { .compatible = "ti,am642-icssg-prueth" }, |
| 672 | { } |
| 673 | }; |
| 674 | |
| 675 | U_BOOT_DRIVER(prueth) = { |
| 676 | .name = "prueth", |
| 677 | .id = UCLASS_MISC, |
| 678 | .of_match = prueth_ids, |
| 679 | .probe = prueth_probe, |
| 680 | .priv_auto = sizeof(struct prueth), |
| 681 | }; |
| 682 | |
| 683 | U_BOOT_DRIVER(prueth_port) = { |
| 684 | .name = "prueth_port", |
| 685 | .id = UCLASS_ETH, |
| 686 | .probe = prueth_port_probe, |
| 687 | .ops = &prueth_ops, |
| 688 | .priv_auto = sizeof(struct prueth_priv), |
| 689 | .plat_auto = sizeof(struct eth_pdata), |
| 690 | .flags = DM_FLAG_ALLOC_PRIV_DMA, |
| 691 | }; |