1// Copyright 2016 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <inttypes.h> 6#include <stdint.h> 7#include <zircon/listnode.h> 8 9#include <stdio.h> 10#include <stdlib.h> 11#include <string.h> 12#include <threads.h> 13 14#if _KERNEL 15//TODO: proper includes/defines kernel driver 16#else 17// includes and defines for userspace driver 18#include <zircon/types.h> 19#include <zircon/syscalls.h> 20#include <ddk/driver.h> 21typedef int status_t; 22#define nanosleep(x) zx_nanosleep(zx_deadline_after(x)); 23#define usleep(x) nanosleep((x) * 1000) 24#define REG32(addr) ((volatile uint32_t *)(uintptr_t)(addr)) 25#define writel(v, a) (*REG32(eth->iobase + (a)) = (v)) 26#define readl(a) (*REG32(eth->iobase + (a))) 27#endif 28 29#include "ie.h" 30 31void eth_dump_regs(ethdev_t* eth) { 32 printf("STAT %08x CTRL %08x EXT %08x IMS %08x\n", 33 readl(IE_STATUS), readl(IE_CTRL), readl(IE_CTRL_EXT), readl(IE_IMS)); 34 printf("RCTL %08x RDLN %08x RDH %08x RDT %08x\n", 35 readl(IE_RCTL), readl(IE_RDLEN), readl(IE_RDH), readl(IE_RDT)); 36 printf("RXDC %08x RDTR %08x RBH %08x RBL %08x\n", 37 readl(IE_RXDCTL), readl(IE_RDTR), readl(IE_RDBAH), readl(IE_RDBAL)); 38 printf("TCTL %08x TDLN %08x TDH %08x TDT %08x\n", 39 readl(IE_TCTL), readl(IE_TDLEN), readl(IE_TDH), readl(IE_TDT)); 40 printf("TXDC %08x TIDV %08x TBH %08x TBL %08x\n", 41 readl(IE_TXDCTL), readl(IE_TIDV), readl(IE_TDBAH), readl(IE_TDBAL)); 42} 43 44unsigned eth_handle_irq(ethdev_t* eth) { 45 // clears irqs on read 46 return readl(IE_ICR); 47} 48 49bool eth_status_online(ethdev_t* eth) { 50 return readl(IE_STATUS) & IE_STATUS_LU; 51} 52 53status_t eth_rx(ethdev_t* eth, void** data, size_t* len) { 54 uint32_t n = eth->rx_rd_ptr; 55 uint64_t info = eth->rxd[n].info; 56 57 if (!(info & IE_RXD_DONE)) { 58 return ZX_ERR_SHOULD_WAIT; 59 } 60 61 // copy out packet 62 zx_status_t r = IE_RXD_LEN(info); 63 64 *data = eth->rxb + ETH_RXBUF_SIZE * n; 65 *len = r; 66 67 return ZX_OK; 68} 69 70void eth_rx_ack(ethdev_t* eth) { 71 uint32_t n = eth->rx_rd_ptr; 72 73 // make buffer available to hw 74 eth->rxd[n].info = 0; 75 writel(n, IE_RDT); 76 n = (n + 1) & (ETH_RXBUF_COUNT - 1); 77 eth->rx_rd_ptr = n; 78} 79 80void eth_enable_rx(ethdev_t* eth) { 81 uint32_t rctl = readl(IE_RCTL); 82 writel(rctl | IE_RCTL_EN, IE_RCTL); 83} 84 85void eth_disable_rx(ethdev_t* eth) { 86 uint32_t rctl = readl(IE_RCTL); 87 writel(rctl & ~IE_RCTL_EN, IE_RCTL); 88} 89 90static void reap_tx_buffers(ethdev_t* eth) { 91 uint32_t n = eth->tx_rd_ptr; 92 for (;;) { 93 uint64_t info = eth->txd[n].info; 94 if (!(info & IE_TXD_DONE)) { 95 break; 96 } 97 framebuf_t* frame = list_remove_head_type(ð->busy_frames, framebuf_t, node); 98 if (frame == NULL) { 99 panic(); 100 } 101 // TODO: verify that this is the matching buffer to txd[n] addr? 102 list_add_tail(ð->free_frames, &frame->node); 103 eth->txd[n].info = 0; 104 n = (n + 1) & (ETH_TXBUF_COUNT - 1); 105 } 106 eth->tx_rd_ptr = n; 107} 108 109status_t eth_tx(ethdev_t* eth, const void* data, size_t len) { 110 if (len > ETH_TXBUF_DSIZE) { 111 printf("intel-eth: unsupported packet length %zu\n", len); 112 return ZX_ERR_INVALID_ARGS; 113 } 114 115 zx_status_t status = ZX_OK; 116 117 mtx_lock(ð->send_lock); 118 119 reap_tx_buffers(eth); 120 121 // obtain buffer, copy into it, setup descriptor 122 framebuf_t *frame = list_remove_head_type(ð->free_frames, framebuf_t, node); 123 if (frame == NULL) { 124 status = ZX_ERR_NO_RESOURCES; 125 goto out; 126 } 127 128 uint32_t n = eth->tx_wr_ptr; 129 memcpy(frame->data, data, len); 130 // Pad out short packets. 131 if (len < 60) { 132 memset(frame->data + len, 0, 60 - len); 133 len = 60; 134 } 135 eth->txd[n].addr = frame->phys; 136 eth->txd[n].info = IE_TXD_LEN(len) | IE_TXD_EOP | IE_TXD_IFCS | IE_TXD_RS; 137 list_add_tail(ð->busy_frames, &frame->node); 138 139 // inform hw of buffer availability 140 n = (n + 1) & (ETH_TXBUF_COUNT - 1); 141 eth->tx_wr_ptr = n; 142 writel(n, IE_TDT); 143 144out: 145 mtx_unlock(ð->send_lock); 146 return status; 147} 148 149// Returns the number of Tx packets in the hw queue 150size_t eth_tx_queued(ethdev_t* eth) { 151 reap_tx_buffers(eth); 152 return ((eth->tx_wr_ptr + ETH_TXBUF_COUNT) - eth->tx_rd_ptr) & (ETH_TXBUF_COUNT - 1); 153} 154 155void eth_enable_tx(ethdev_t* eth) { 156 uint32_t tctl = readl(IE_TCTL); 157 writel(tctl | IE_TCTL_EN, IE_TCTL); 158} 159 160void eth_disable_tx(ethdev_t* eth) { 161 uint32_t tctl = readl(IE_TCTL); 162 writel(tctl & ~IE_TCTL_EN, IE_TCTL); 163} 164 165void eth_start_promisc(ethdev_t* eth) { 166 uint32_t rctl = readl(IE_RCTL); 167 writel(rctl | IE_RCTL_UPE, IE_RCTL); 168} 169 170void eth_stop_promisc(ethdev_t* eth) { 171 uint32_t rctl = readl(IE_RCTL); 172 writel(rctl & ~IE_RCTL_UPE, IE_RCTL); 173} 174 175static zx_status_t wait_for_mdic(ethdev_t* eth, uint32_t* reg_value) { 176 uint32_t mdic; 177 uint32_t iterations = 0; 178 do { 179 nanosleep(50); 180 mdic = readl(IE_MDIC); 181 if (mdic & IE_MDIC_R) { 182 goto success; 183 } 184 iterations++; 185 } while (!(mdic & IE_MDIC_R) && (iterations < 100)); 186 printf("intel-eth: timed out waiting for MDIC to be ready\n"); 187 return ZX_ERR_TIMED_OUT; 188 189success: 190 if (reg_value) { 191 *reg_value = mdic; 192 } 193 return ZX_OK; 194} 195 196static zx_status_t phy_read(ethdev_t* eth, uint8_t phyadd, uint8_t regadd, uint16_t* result) { 197 uint32_t mdic = IE_MDIC_PUT_PHYADD(phyadd) | 198 IE_MDIC_PUT_REGADD(regadd) | 199 IE_MDIC_OP_READ; 200 writel(mdic, IE_MDIC); 201 zx_status_t status = wait_for_mdic(eth, &mdic); 202 if (status == ZX_OK) { 203 *result = IE_MDIC_GET_DATA(mdic); 204 } 205 return status; 206} 207 208static zx_status_t phy_write(ethdev_t* eth, uint8_t phyadd, uint8_t regadd, uint16_t value) { 209 uint32_t mdic = IE_MDIC_PUT_DATA(value) | 210 IE_MDIC_PUT_PHYADD(phyadd) | 211 IE_MDIC_PUT_REGADD(regadd) | 212 IE_MDIC_OP_WRITE; 213 writel(mdic, IE_MDIC); 214 return wait_for_mdic(eth, NULL); 215} 216 217static zx_status_t get_phy_addr(ethdev_t* eth, uint8_t* phy_addr) { 218 if (eth->phy_addr != 0) { 219 *phy_addr = eth->phy_addr; 220 } 221 for (uint8_t addr = 1; addr <= IE_MAX_PHY_ADDR; addr++) { 222 uint16_t pid; 223 zx_status_t status = phy_read(eth, addr, IE_PHY_PID, &pid); 224 // TODO: Identify the PHY more precisely 225 if (status == ZX_OK && pid != 0) { 226 *phy_addr = pid; 227 return ZX_OK; 228 } 229 } 230 printf("intel-eth: unable to identify valid PHY address\n"); 231 return ZX_ERR_NOT_FOUND; 232} 233 234zx_status_t eth_enable_phy(ethdev_t* eth) { 235 uint8_t phy_addr; 236 zx_status_t status = get_phy_addr(eth, &phy_addr); 237 if (status != ZX_OK) { 238 return status; 239 } 240 241 uint16_t phy_ctrl; 242 status = phy_read(eth, phy_addr, IE_PHY_PCTRL, &phy_ctrl); 243 if (status != ZX_OK) { 244 return status; 245 } 246 247 if (phy_ctrl & IE_PHY_PCTRL_POWER_DOWN) { 248 return phy_write(eth, phy_addr, IE_PHY_PCTRL, phy_ctrl & ~IE_PHY_PCTRL_POWER_DOWN); 249 } 250 return ZX_OK; 251} 252 253zx_status_t eth_disable_phy(ethdev_t* eth) { 254 uint8_t phy_addr; 255 zx_status_t status = get_phy_addr(eth, &phy_addr); 256 if (status != ZX_OK) { 257 return status; 258 } 259 260 uint16_t phy_ctrl; 261 status = phy_read(eth, phy_addr, IE_PHY_PCTRL, &phy_ctrl); 262 if (status != ZX_OK) { 263 return status; 264 } 265 return phy_write(eth, phy_addr, IE_PHY_PCTRL, phy_ctrl | IE_PHY_PCTRL_POWER_DOWN); 266} 267 268status_t eth_reset_hw(ethdev_t* eth) { 269 // TODO: don't rely on bootloader having initialized the 270 // controller in order to obtain the mac address 271 uint32_t n; 272 n = readl(IE_RAL(0)); 273 memcpy(eth->mac + 0, &n, 4); 274 n = readl(IE_RAH(0)); 275 memcpy(eth->mac + 4, &n, 2); 276 printf("eth: mac: %02x:%02x:%02x:%02x:%02x:%02x\n", 277 eth->mac[0],eth->mac[1],eth->mac[2], 278 eth->mac[3],eth->mac[4],eth->mac[5]); 279 280 // disable all interrupts 281 if (eth->pci_did == IE_DID_I211_AT) { 282 writel(0, IE_IAM); 283 } 284 writel(0xffffffff, IE_IMC); 285 286 // disable tx/rx 287 writel(0, IE_RCTL); 288 writel(IE_TCTL_PSP, IE_TCTL); 289 290 // global reset 291 uint32_t reg = readl(IE_CTRL); 292 writel(reg | IE_CTRL_RST, IE_CTRL); 293 294 if (eth->pci_did == IE_DID_I211_AT) { 295 usleep(20); 296 reg = readl(IE_STATUS); 297 if (!(reg & IE_STATUS_PF_RST_DONE)) { 298 printf("eth: reset failed (1)\n"); 299 return ZX_ERR_BAD_STATE; 300 } 301 reg = readl(IE_EEC); 302 if (!(reg & IE_EEC_AUTO_RD)) { 303 printf("eth: reset failed (2)\n"); 304 return ZX_ERR_BAD_STATE; 305 } 306 } else { 307 usleep(5); 308 309 if (readl(IE_CTRL) & IE_CTRL_RST) { 310 printf("eth: reset failed\n"); 311 return ZX_ERR_BAD_STATE; 312 } 313 } 314 315 // disable all interrupts 316 if (eth->pci_did == IE_DID_I211_AT) { 317 writel(0, IE_IAM); 318 } 319 writel(0xffffffff, IE_IMC); 320 321 // clear any pending interrupts 322 readl(IE_ICR); 323 324 return ZX_OK; 325} 326 327void eth_init_hw(ethdev_t* eth) { 328 //TODO: tune RXDCTL and TXDCTL settings 329 //TODO: TCTL COLD should be based on link state 330 //TODO: use address filtering for multicast 331 332 // set link up (Must be set to enable communications between MAC and PHY.) 333 uint32_t reg = readl(IE_CTRL); 334 writel(reg | IE_CTRL_SLU, IE_CTRL); 335 336 usleep(15); 337 338 // setup rx ring 339 eth->rx_rd_ptr = 0; 340 writel(eth->rxd_phys, IE_RDBAL); 341 writel(eth->rxd_phys >> 32, IE_RDBAH); 342 writel(ETH_RXBUF_COUNT * 16, IE_RDLEN); 343 344 reg = IE_RXDCTL_PTHRESH(12) | IE_RXDCTL_HTHRESH(10) | IE_RXDCTL_WTHRESH(1); 345 if (eth->pci_did == IE_DID_I211_AT) { 346 reg |= IE_RXDCTL_ENABLE; 347 } else { 348 reg |= IE_RXDCTL_GRAN; 349 } 350 writel(reg, IE_RXDCTL); 351 352 // wait for enable to complete 353 if (eth->pci_did == IE_DID_I211_AT) { 354 while (!(readl(IE_RXDCTL) & IE_RXDCTL_ENABLE)) { 355 } 356 } 357 358 writel(ETH_RXBUF_COUNT - 1, IE_RDT); 359 writel(IE_RCTL_BSIZE2048 | IE_RCTL_DPF | IE_RCTL_SECRC | 360 IE_RCTL_BAM | IE_RCTL_MPE | IE_RCTL_EN, 361 IE_RCTL); 362 363 // setup tx ring 364 eth->tx_wr_ptr = 0; 365 eth->tx_rd_ptr = 0; 366 writel(eth->txd_phys, IE_TDBAL); 367 writel(eth->txd_phys >> 32, IE_TDBAH); 368 writel(ETH_TXBUF_COUNT * 16, IE_TDLEN); 369 370 reg = IE_TXDCTL_WTHRESH(1); 371 if (eth->pci_did == IE_DID_I211_AT) { 372 reg |= IE_TXDCTL_ENABLE; 373 } else { 374 reg |= IE_TXDCTL_GRAN; 375 } 376 writel(reg, IE_TXDCTL); 377 378 // wait for enable to complete 379 if (eth->pci_did == IE_DID_I211_AT) { 380 while (!(readl(IE_TXDCTL) & IE_TXDCTL_ENABLE)) { 381 } 382 } 383 384 if (eth->pci_did == IE_DID_I211_AT) { 385 reg = IE_TCTL_CT(15) | IE_TCTL_BST(64) | IE_TCTL_PSP | IE_TCTL_EN; 386 } else { 387 reg = readl(IE_TCTL) & IE_TCTL_RESERVED; 388 reg |= IE_TCTL_CT(15) | IE_TCTL_COLD_FD | IE_TCTL_EN; 389 } 390 writel(reg, IE_TCTL); 391 392 // enable interrupts 393 if (eth->pci_did == IE_DID_I211_AT) { 394 // Receiver Descriptor Write Back & Link Status Change interrupts 395 writel(IE_INT_RXDW | IE_INT_LSC, IE_IMS); 396 } else { 397 // enable rx & link status change irqs 398 writel(IE_INT_RXT0 | IE_INT_LSC, IE_IMS); 399 } 400} 401 402void eth_setup_buffers(ethdev_t* eth, void* iomem, zx_paddr_t iophys) { 403 printf("eth: iomem @%p (phys %" PRIxPTR ")\n", iomem, iophys); 404 405 list_initialize(ð->free_frames); 406 list_initialize(ð->busy_frames); 407 408 eth->rxd = iomem; 409 eth->rxd_phys = iophys; 410 iomem += ETH_DRING_SIZE; 411 iophys += ETH_DRING_SIZE; 412 memset(eth->rxd, 0, ETH_DRING_SIZE); 413 414 eth->txd = iomem; 415 eth->txd_phys = iophys; 416 iomem += ETH_DRING_SIZE; 417 iophys += ETH_DRING_SIZE; 418 memset(eth->txd, 0, ETH_DRING_SIZE); 419 420 eth->rxb = iomem; 421 eth->rxb_phys = iophys; 422 iomem += ETH_RXBUF_SIZE * ETH_RXBUF_COUNT; 423 iophys += ETH_RXBUF_SIZE * ETH_RXBUF_COUNT; 424 425 for (int n = 0; n < ETH_RXBUF_COUNT; n++) { 426 eth->rxd[n].addr = eth->rxb_phys + ETH_RXBUF_SIZE * n; 427 } 428 for (int n = 0; n < ETH_TXBUF_COUNT - 1; n++) { 429 framebuf_t *txb = iomem; 430 txb->phys = iophys + ETH_TXBUF_HSIZE; 431 txb->size = ETH_TXBUF_SIZE - ETH_TXBUF_HSIZE; 432 txb->data = iomem + ETH_TXBUF_HSIZE; 433 list_add_tail(ð->free_frames, &txb->node); 434 435 iomem += ETH_TXBUF_SIZE; 436 iophys += ETH_TXBUF_SIZE; 437 } 438} 439