uboot/drivers/net/ftmac110.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Faraday 10/100Mbps Ethernet Controller
   4 *
   5 * (C) Copyright 2013 Faraday Technology
   6 * Dante Su <dantesu@faraday-tech.com>
   7 */
   8
   9#include <common.h>
  10#include <command.h>
  11#include <malloc.h>
  12#include <net.h>
  13#include <linux/errno.h>
  14#include <asm/io.h>
  15#include <asm/dma-mapping.h>
  16
  17#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
  18#include <miiphy.h>
  19#endif
  20
  21#include "ftmac110.h"
  22
  23#define CFG_RXDES_NUM   8
  24#define CFG_TXDES_NUM   2
  25#define CFG_XBUF_SIZE   1536
  26
  27#define CFG_MDIORD_TIMEOUT  (CONFIG_SYS_HZ >> 1) /* 500 ms */
  28#define CFG_MDIOWR_TIMEOUT  (CONFIG_SYS_HZ >> 1) /* 500 ms */
  29#define CFG_LINKUP_TIMEOUT  (CONFIG_SYS_HZ << 2) /* 4 sec */
  30
  31/*
  32 * FTMAC110 DMA design issue
  33 *
  34 * Its DMA engine has a weird restriction that its Rx DMA engine
  35 * accepts only 16-bits aligned address, 32-bits aligned is not
  36 * acceptable. However this restriction does not apply to Tx DMA.
  37 *
  38 * Conclusion:
  39 * (1) Tx DMA Buffer Address:
  40 *     1 bytes aligned: Invalid
  41 *     2 bytes aligned: O.K
  42 *     4 bytes aligned: O.K (-> u-boot ZeroCopy is possible)
  43 * (2) Rx DMA Buffer Address:
  44 *     1 bytes aligned: Invalid
  45 *     2 bytes aligned: O.K
  46 *     4 bytes aligned: Invalid
  47 */
  48
  49struct ftmac110_chip {
  50        void __iomem *regs;
  51        uint32_t imr;
  52        uint32_t maccr;
  53        uint32_t lnkup;
  54        uint32_t phy_addr;
  55
  56        struct ftmac110_desc *rxd;
  57        ulong                rxd_dma;
  58        uint32_t             rxd_idx;
  59
  60        struct ftmac110_desc *txd;
  61        ulong                txd_dma;
  62        uint32_t             txd_idx;
  63};
  64
  65static int ftmac110_reset(struct eth_device *dev);
  66
  67static uint16_t mdio_read(struct eth_device *dev,
  68        uint8_t phyaddr, uint8_t phyreg)
  69{
  70        struct ftmac110_chip *chip = dev->priv;
  71        struct ftmac110_regs *regs = chip->regs;
  72        uint32_t tmp, ts;
  73        uint16_t ret = 0xffff;
  74
  75        tmp = PHYCR_READ
  76                | (phyaddr << PHYCR_ADDR_SHIFT)
  77                | (phyreg  << PHYCR_REG_SHIFT);
  78
  79        writel(tmp, &regs->phycr);
  80
  81        for (ts = get_timer(0); get_timer(ts) < CFG_MDIORD_TIMEOUT; ) {
  82                tmp = readl(&regs->phycr);
  83                if (tmp & PHYCR_READ)
  84                        continue;
  85                break;
  86        }
  87
  88        if (tmp & PHYCR_READ)
  89                printf("ftmac110: mdio read timeout\n");
  90        else
  91                ret = (uint16_t)(tmp & 0xffff);
  92
  93        return ret;
  94}
  95
  96static void mdio_write(struct eth_device *dev,
  97        uint8_t phyaddr, uint8_t phyreg, uint16_t phydata)
  98{
  99        struct ftmac110_chip *chip = dev->priv;
 100        struct ftmac110_regs *regs = chip->regs;
 101        uint32_t tmp, ts;
 102
 103        tmp = PHYCR_WRITE
 104                | (phyaddr << PHYCR_ADDR_SHIFT)
 105                | (phyreg  << PHYCR_REG_SHIFT);
 106
 107        writel(phydata, &regs->phydr);
 108        writel(tmp, &regs->phycr);
 109
 110        for (ts = get_timer(0); get_timer(ts) < CFG_MDIOWR_TIMEOUT; ) {
 111                if (readl(&regs->phycr) & PHYCR_WRITE)
 112                        continue;
 113                break;
 114        }
 115
 116        if (readl(&regs->phycr) & PHYCR_WRITE)
 117                printf("ftmac110: mdio write timeout\n");
 118}
 119
 120static uint32_t ftmac110_phyqry(struct eth_device *dev)
 121{
 122        ulong ts;
 123        uint32_t maccr;
 124        uint16_t pa, tmp, bmsr, bmcr;
 125        struct ftmac110_chip *chip = dev->priv;
 126
 127        /* Default = 100Mbps Full */
 128        maccr = MACCR_100M | MACCR_FD;
 129
 130        /* 1. find the phy device  */
 131        for (pa = 0; pa < 32; ++pa) {
 132                tmp = mdio_read(dev, pa, MII_PHYSID1);
 133                if (tmp == 0xFFFF || tmp == 0x0000)
 134                        continue;
 135                chip->phy_addr = pa;
 136                break;
 137        }
 138        if (pa >= 32) {
 139                puts("ftmac110: phy device not found!\n");
 140                goto exit;
 141        }
 142
 143        /* 2. wait until link-up & auto-negotiation complete */
 144        chip->lnkup = 0;
 145        bmcr = mdio_read(dev, chip->phy_addr, MII_BMCR);
 146        ts = get_timer(0);
 147        do {
 148                bmsr = mdio_read(dev, chip->phy_addr, MII_BMSR);
 149                chip->lnkup = (bmsr & BMSR_LSTATUS) ? 1 : 0;
 150                if (!chip->lnkup)
 151                        continue;
 152                if (!(bmcr & BMCR_ANENABLE) || (bmsr & BMSR_ANEGCOMPLETE))
 153                        break;
 154        } while (get_timer(ts) < CFG_LINKUP_TIMEOUT);
 155        if (!chip->lnkup) {
 156                puts("ftmac110: link down\n");
 157                goto exit;
 158        }
 159        if (!(bmcr & BMCR_ANENABLE))
 160                puts("ftmac110: auto negotiation disabled\n");
 161        else if (!(bmsr & BMSR_ANEGCOMPLETE))
 162                puts("ftmac110: auto negotiation timeout\n");
 163
 164        /* 3. derive MACCR */
 165        if ((bmcr & BMCR_ANENABLE) && (bmsr & BMSR_ANEGCOMPLETE)) {
 166                tmp  = mdio_read(dev, chip->phy_addr, MII_ADVERTISE);
 167                tmp &= mdio_read(dev, chip->phy_addr, MII_LPA);
 168                if (tmp & LPA_100FULL)      /* 100Mbps full-duplex */
 169                        maccr = MACCR_100M | MACCR_FD;
 170                else if (tmp & LPA_100HALF) /* 100Mbps half-duplex */
 171                        maccr = MACCR_100M;
 172                else if (tmp & LPA_10FULL)  /* 10Mbps full-duplex */
 173                        maccr = MACCR_FD;
 174                else if (tmp & LPA_10HALF)  /* 10Mbps half-duplex */
 175                        maccr = 0;
 176        } else {
 177                if (bmcr & BMCR_SPEED100)
 178                        maccr = MACCR_100M;
 179                else
 180                        maccr = 0;
 181                if (bmcr & BMCR_FULLDPLX)
 182                        maccr |= MACCR_FD;
 183        }
 184
 185exit:
 186        printf("ftmac110: %d Mbps, %s\n",
 187               (maccr & MACCR_100M) ? 100 : 10,
 188               (maccr & MACCR_FD) ? "Full" : "half");
 189        return maccr;
 190}
 191
 192static int ftmac110_reset(struct eth_device *dev)
 193{
 194        uint8_t *a;
 195        uint32_t i, maccr;
 196        struct ftmac110_chip *chip = dev->priv;
 197        struct ftmac110_regs *regs = chip->regs;
 198
 199        /* 1. MAC reset */
 200        writel(MACCR_RESET, &regs->maccr);
 201        for (i = get_timer(0); get_timer(i) < 1000; ) {
 202                if (readl(&regs->maccr) & MACCR_RESET)
 203                        continue;
 204                break;
 205        }
 206        if (readl(&regs->maccr) & MACCR_RESET) {
 207                printf("ftmac110: reset failed\n");
 208                return -ENXIO;
 209        }
 210
 211        /* 1-1. Init tx ring */
 212        for (i = 0; i < CFG_TXDES_NUM; ++i) {
 213                /* owned by SW */
 214                chip->txd[i].ctrl &= cpu_to_le64(FTMAC110_TXD_CLRMASK);
 215        }
 216        chip->txd_idx = 0;
 217
 218        /* 1-2. Init rx ring */
 219        for (i = 0; i < CFG_RXDES_NUM; ++i) {
 220                /* owned by HW */
 221                chip->rxd[i].ctrl &= cpu_to_le64(FTMAC110_RXD_CLRMASK);
 222                chip->rxd[i].ctrl |= cpu_to_le64(FTMAC110_RXD_OWNER);
 223        }
 224        chip->rxd_idx = 0;
 225
 226        /* 2. PHY status query */
 227        maccr = ftmac110_phyqry(dev);
 228
 229        /* 3. Fix up the MACCR value */
 230        chip->maccr = maccr | MACCR_CRCAPD | MACCR_RXALL | MACCR_RXRUNT
 231                | MACCR_RXEN | MACCR_TXEN | MACCR_RXDMAEN | MACCR_TXDMAEN;
 232
 233        /* 4. MAC address setup */
 234        a = dev->enetaddr;
 235        writel(a[1] | (a[0] << 8), &regs->mac[0]);
 236        writel(a[5] | (a[4] << 8) | (a[3] << 16)
 237                | (a[2] << 24), &regs->mac[1]);
 238
 239        /* 5. MAC registers setup */
 240        writel(chip->rxd_dma, &regs->rxba);
 241        writel(chip->txd_dma, &regs->txba);
 242        /* interrupt at each tx/rx */
 243        writel(ITC_DEFAULT, &regs->itc);
 244        /* no tx pool, rx poll = 1 normal cycle */
 245        writel(APTC_DEFAULT, &regs->aptc);
 246        /* rx threshold = [6/8 fifo, 2/8 fifo] */
 247        writel(DBLAC_DEFAULT, &regs->dblac);
 248        /* disable & clear all interrupt status */
 249        chip->imr = 0;
 250        writel(ISR_ALL, &regs->isr);
 251        writel(chip->imr, &regs->imr);
 252        /* enable mac */
 253        writel(chip->maccr, &regs->maccr);
 254
 255        return 0;
 256}
 257
 258static int ftmac110_probe(struct eth_device *dev, bd_t *bis)
 259{
 260        debug("ftmac110: probe\n");
 261
 262        if (ftmac110_reset(dev))
 263                return -1;
 264
 265        return 0;
 266}
 267
 268static void ftmac110_halt(struct eth_device *dev)
 269{
 270        struct ftmac110_chip *chip = dev->priv;
 271        struct ftmac110_regs *regs = chip->regs;
 272
 273        writel(0, &regs->imr);
 274        writel(0, &regs->maccr);
 275
 276        debug("ftmac110: halt\n");
 277}
 278
 279static int ftmac110_send(struct eth_device *dev, void *pkt, int len)
 280{
 281        struct ftmac110_chip *chip = dev->priv;
 282        struct ftmac110_regs *regs = chip->regs;
 283        struct ftmac110_desc *txd;
 284        uint64_t ctrl;
 285
 286        if (!chip->lnkup)
 287                return 0;
 288
 289        if (len <= 0 || len > CFG_XBUF_SIZE) {
 290                printf("ftmac110: bad tx pkt len(%d)\n", len);
 291                return 0;
 292        }
 293
 294        len = max(60, len);
 295
 296        txd = &chip->txd[chip->txd_idx];
 297        ctrl = le64_to_cpu(txd->ctrl);
 298        if (ctrl & FTMAC110_TXD_OWNER) {
 299                /* kick-off Tx DMA */
 300                writel(0xffffffff, &regs->txpd);
 301                printf("ftmac110: out of txd\n");
 302                return 0;
 303        }
 304
 305        memcpy(txd->vbuf, (void *)pkt, len);
 306        dma_map_single(txd->vbuf, len, DMA_TO_DEVICE);
 307
 308        /* clear control bits */
 309        ctrl &= FTMAC110_TXD_CLRMASK;
 310        /* set len, fts and lts */
 311        ctrl |= FTMAC110_TXD_LEN(len) | FTMAC110_TXD_FTS | FTMAC110_TXD_LTS;
 312        /* set owner bit */
 313        ctrl |= FTMAC110_TXD_OWNER;
 314        /* write back to descriptor */
 315        txd->ctrl = cpu_to_le64(ctrl);
 316
 317        /* kick-off Tx DMA */
 318        writel(0xffffffff, &regs->txpd);
 319
 320        chip->txd_idx = (chip->txd_idx + 1) % CFG_TXDES_NUM;
 321
 322        return len;
 323}
 324
 325static int ftmac110_recv(struct eth_device *dev)
 326{
 327        struct ftmac110_chip *chip = dev->priv;
 328        struct ftmac110_desc *rxd;
 329        uint32_t len, rlen = 0;
 330        uint64_t ctrl;
 331        uint8_t *buf;
 332
 333        if (!chip->lnkup)
 334                return 0;
 335
 336        do {
 337                rxd = &chip->rxd[chip->rxd_idx];
 338                ctrl = le64_to_cpu(rxd->ctrl);
 339                if (ctrl & FTMAC110_RXD_OWNER)
 340                        break;
 341
 342                len = (uint32_t)FTMAC110_RXD_LEN(ctrl);
 343                buf = rxd->vbuf;
 344
 345                if (ctrl & FTMAC110_RXD_ERRMASK) {
 346                        printf("ftmac110: rx error\n");
 347                } else {
 348                        dma_map_single(buf, len, DMA_FROM_DEVICE);
 349                        net_process_received_packet(buf, len);
 350                        rlen += len;
 351                }
 352
 353                /* owned by hardware */
 354                ctrl &= FTMAC110_RXD_CLRMASK;
 355                ctrl |= FTMAC110_RXD_OWNER;
 356                rxd->ctrl |= cpu_to_le64(ctrl);
 357
 358                chip->rxd_idx = (chip->rxd_idx + 1) % CFG_RXDES_NUM;
 359        } while (0);
 360
 361        return rlen;
 362}
 363
 364#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
 365
 366static int ftmac110_mdio_read(struct mii_dev *bus, int addr, int devad,
 367                              int reg)
 368{
 369        uint16_t value = 0;
 370        int ret = 0;
 371        struct eth_device *dev;
 372
 373        dev = eth_get_dev_by_name(bus->name);
 374        if (dev == NULL) {
 375                printf("%s: no such device\n", bus->name);
 376                ret = -1;
 377        } else {
 378                value = mdio_read(dev, addr, reg);
 379        }
 380
 381        if (ret < 0)
 382                return ret;
 383        return value;
 384}
 385
 386static int ftmac110_mdio_write(struct mii_dev *bus, int addr, int devad,
 387                               int reg, u16 value)
 388{
 389        int ret = 0;
 390        struct eth_device *dev;
 391
 392        dev = eth_get_dev_by_name(bus->name);
 393        if (dev == NULL) {
 394                printf("%s: no such device\n", bus->name);
 395                ret = -1;
 396        } else {
 397                mdio_write(dev, addr, reg, value);
 398        }
 399
 400        return ret;
 401}
 402
 403#endif    /* #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) */
 404
 405int ftmac110_initialize(bd_t *bis)
 406{
 407        int i, card_nr = 0;
 408        struct eth_device *dev;
 409        struct ftmac110_chip *chip;
 410
 411        dev = malloc(sizeof(*dev) + sizeof(*chip));
 412        if (dev == NULL) {
 413                panic("ftmac110: out of memory 1\n");
 414                return -1;
 415        }
 416        chip = (struct ftmac110_chip *)(dev + 1);
 417        memset(dev, 0, sizeof(*dev) + sizeof(*chip));
 418
 419        sprintf(dev->name, "FTMAC110#%d", card_nr);
 420
 421        dev->iobase = CONFIG_FTMAC110_BASE;
 422        chip->regs = (void __iomem *)dev->iobase;
 423        dev->priv = chip;
 424        dev->init = ftmac110_probe;
 425        dev->halt = ftmac110_halt;
 426        dev->send = ftmac110_send;
 427        dev->recv = ftmac110_recv;
 428
 429        /* allocate tx descriptors (it must be 16 bytes aligned) */
 430        chip->txd = dma_alloc_coherent(
 431                sizeof(struct ftmac110_desc) * CFG_TXDES_NUM, &chip->txd_dma);
 432        if (!chip->txd)
 433                panic("ftmac110: out of memory 3\n");
 434        memset(chip->txd, 0,
 435               sizeof(struct ftmac110_desc) * CFG_TXDES_NUM);
 436        for (i = 0; i < CFG_TXDES_NUM; ++i) {
 437                void *va = memalign(ARCH_DMA_MINALIGN, CFG_XBUF_SIZE);
 438
 439                if (!va)
 440                        panic("ftmac110: out of memory 4\n");
 441                chip->txd[i].vbuf = va;
 442                chip->txd[i].pbuf = cpu_to_le32(virt_to_phys(va));
 443                chip->txd[i].ctrl = 0;  /* owned by SW */
 444        }
 445        chip->txd[i - 1].ctrl |= cpu_to_le64(FTMAC110_TXD_END);
 446        chip->txd_idx = 0;
 447
 448        /* allocate rx descriptors (it must be 16 bytes aligned) */
 449        chip->rxd = dma_alloc_coherent(
 450                sizeof(struct ftmac110_desc) * CFG_RXDES_NUM, &chip->rxd_dma);
 451        if (!chip->rxd)
 452                panic("ftmac110: out of memory 4\n");
 453        memset((void *)chip->rxd, 0,
 454               sizeof(struct ftmac110_desc) * CFG_RXDES_NUM);
 455        for (i = 0; i < CFG_RXDES_NUM; ++i) {
 456                void *va = memalign(ARCH_DMA_MINALIGN, CFG_XBUF_SIZE + 2);
 457
 458                if (!va)
 459                        panic("ftmac110: out of memory 5\n");
 460                /* it needs to be exactly 2 bytes aligned */
 461                va = ((uint8_t *)va + 2);
 462                chip->rxd[i].vbuf = va;
 463                chip->rxd[i].pbuf = cpu_to_le32(virt_to_phys(va));
 464                chip->rxd[i].ctrl = cpu_to_le64(FTMAC110_RXD_OWNER
 465                        | FTMAC110_RXD_BUFSZ(CFG_XBUF_SIZE));
 466        }
 467        chip->rxd[i - 1].ctrl |= cpu_to_le64(FTMAC110_RXD_END);
 468        chip->rxd_idx = 0;
 469
 470        eth_register(dev);
 471
 472#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
 473        int retval;
 474        struct mii_dev *mdiodev = mdio_alloc();
 475        if (!mdiodev)
 476                return -ENOMEM;
 477        strncpy(mdiodev->name, dev->name, MDIO_NAME_LEN);
 478        mdiodev->read = ftmac110_mdio_read;
 479        mdiodev->write = ftmac110_mdio_write;
 480
 481        retval = mdio_register(mdiodev);
 482        if (retval < 0)
 483                return retval;
 484#endif
 485
 486        card_nr++;
 487
 488        return card_nr;
 489}
 490