1df482650SStephan Linz /* 2df482650SStephan Linz * Xilinx xps_ll_temac ethernet driver for u-boot 3df482650SStephan Linz * 4df482650SStephan Linz * supports SDMA or FIFO access and MDIO bus communication 5df482650SStephan Linz * 6df482650SStephan Linz * Copyright (C) 2011 - 2012 Stephan Linz <linz@li-pro.net> 7df482650SStephan Linz * Copyright (C) 2008 - 2011 Michal Simek <monstr@monstr.eu> 8df482650SStephan Linz * Copyright (C) 2008 - 2011 PetaLogix 9df482650SStephan Linz * 10df482650SStephan Linz * Based on Yoshio Kashiwagi kashiwagi@co-nss.co.jp driver 11df482650SStephan Linz * Copyright (C) 2008 Nissin Systems Co.,Ltd. 12df482650SStephan Linz * March 2008 created 13df482650SStephan Linz * 14df482650SStephan Linz * This program is free software; you can redistribute it and/or modify it 15df482650SStephan Linz * under the terms of the GNU General Public License as published by the 16df482650SStephan Linz * Free Software Foundation; either version 2 of the License, or (at your 17df482650SStephan Linz * option) any later version. 18df482650SStephan Linz * 19df482650SStephan Linz * [0]: http://www.xilinx.com/support/documentation 20df482650SStephan Linz * 21df482650SStephan Linz * [S]: [0]/ip_documentation/xps_ll_temac.pdf 22df482650SStephan Linz * [A]: [0]/application_notes/xapp1041.pdf 23df482650SStephan Linz */ 24df482650SStephan Linz 25df482650SStephan Linz #include <config.h> 26df482650SStephan Linz #include <common.h> 27df482650SStephan Linz #include <net.h> 28df482650SStephan Linz #include <netdev.h> 29df482650SStephan Linz #include <malloc.h> 30df482650SStephan Linz #include <asm/io.h> 31df482650SStephan Linz #include <miiphy.h> 32df482650SStephan Linz 33df482650SStephan Linz #include "xilinx_ll_temac.h" 34df482650SStephan Linz #include "xilinx_ll_temac_fifo.h" 35df482650SStephan Linz #include "xilinx_ll_temac_sdma.h" 36df482650SStephan Linz #include "xilinx_ll_temac_mdio.h" 37df482650SStephan Linz 38df482650SStephan Linz #if !defined(CONFIG_MII) 39df482650SStephan Linz # error "LL_TEMAC requires MII -- missing CONFIG_MII" 40df482650SStephan Linz #endif 41df482650SStephan Linz 42df482650SStephan Linz #if !defined(CONFIG_PHYLIB) 43df482650SStephan Linz # error "LL_TEMAC requires PHYLIB -- missing CONFIG_PHYLIB" 44df482650SStephan Linz #endif 45df482650SStephan Linz 46df482650SStephan Linz struct ll_temac_info { 47df482650SStephan Linz int flags; 48df482650SStephan Linz unsigned long base_addr; 49df482650SStephan Linz unsigned long ctrl_addr; 50df482650SStephan Linz char *devname; 51df482650SStephan Linz unsigned int phyaddr; 52df482650SStephan Linz char *mdio_busname; 53df482650SStephan Linz }; 54df482650SStephan Linz 55df482650SStephan Linz /* Ethernet interface ready status */ 56df482650SStephan Linz int ll_temac_check_status(struct temac_reg *regs, u32 mask) 57df482650SStephan Linz { 58df482650SStephan Linz unsigned timeout = 50; /* 1usec * 50 = 50usec */ 59df482650SStephan Linz 60df482650SStephan Linz /* 61df482650SStephan Linz * Quote from LL TEMAC documentation: The bits in the RDY 62df482650SStephan Linz * register are asserted when there is no access in progress. 63df482650SStephan Linz * When an access is in progress, a bit corresponding to the 64df482650SStephan Linz * type of access is automatically de-asserted. The bit is 65df482650SStephan Linz * automatically re-asserted when the access is complete. 66df482650SStephan Linz */ 67df482650SStephan Linz while (timeout && (!(in_be32(®s->rdy) & mask))) { 68df482650SStephan Linz timeout--; 69df482650SStephan Linz udelay(1); 70df482650SStephan Linz } 71df482650SStephan Linz 72df482650SStephan Linz if (!timeout) { 73df482650SStephan Linz printf("%s: Timeout on 0x%08x @%p\n", __func__, 74df482650SStephan Linz mask, ®s->rdy); 75df482650SStephan Linz return 1; 76df482650SStephan Linz } 77df482650SStephan Linz 78df482650SStephan Linz return 0; 79df482650SStephan Linz } 80df482650SStephan Linz 81df482650SStephan Linz /* 82df482650SStephan Linz * Indirect write to ll_temac. 83df482650SStephan Linz * 84df482650SStephan Linz * http://www.xilinx.com/support/documentation/ip_documentation/xps_ll_temac.pdf 85df482650SStephan Linz * page 23, second paragraph, The use of CTL0 register or CTL1 register 86df482650SStephan Linz */ 87df482650SStephan Linz int ll_temac_indirect_set(struct temac_reg *regs, u16 regn, u32 reg_data) 88df482650SStephan Linz { 89df482650SStephan Linz out_be32(®s->lsw, (reg_data & MLSW_MASK)); 90df482650SStephan Linz out_be32(®s->ctl, CTL_WEN | (regn & CTL_ADDR_MASK)); 91df482650SStephan Linz 92df482650SStephan Linz if (ll_temac_check_status(regs, RSE_CFG_WR)) 93df482650SStephan Linz return 0; 94df482650SStephan Linz 95df482650SStephan Linz return 1; 96df482650SStephan Linz } 97df482650SStephan Linz 98df482650SStephan Linz /* 99df482650SStephan Linz * Indirect read from ll_temac. 100df482650SStephan Linz * 101df482650SStephan Linz * http://www.xilinx.com/support/documentation/ip_documentation/xps_ll_temac.pdf 102df482650SStephan Linz * page 23, second paragraph, The use of CTL0 register or CTL1 register 103df482650SStephan Linz */ 104df482650SStephan Linz int ll_temac_indirect_get(struct temac_reg *regs, u16 regn, u32* reg_data) 105df482650SStephan Linz { 106df482650SStephan Linz out_be32(®s->ctl, (regn & CTL_ADDR_MASK)); 107df482650SStephan Linz 108df482650SStephan Linz if (ll_temac_check_status(regs, RSE_CFG_RR)) 109df482650SStephan Linz return 0; 110df482650SStephan Linz 111df482650SStephan Linz *reg_data = in_be32(®s->lsw) & MLSW_MASK; 112df482650SStephan Linz return 1; 113df482650SStephan Linz } 114df482650SStephan Linz 115df482650SStephan Linz /* setting sub-controller and ll_temac to proper setting */ 116df482650SStephan Linz static int ll_temac_setup_ctrl(struct eth_device *dev) 117df482650SStephan Linz { 118df482650SStephan Linz struct ll_temac *ll_temac = dev->priv; 119df482650SStephan Linz struct temac_reg *regs = (struct temac_reg *)dev->iobase; 120df482650SStephan Linz 121df482650SStephan Linz if (ll_temac->ctrlreset && ll_temac->ctrlreset(dev)) 122df482650SStephan Linz return 0; 123df482650SStephan Linz 124df482650SStephan Linz if (ll_temac->ctrlinit && ll_temac->ctrlinit(dev)) 125df482650SStephan Linz return 0; 126df482650SStephan Linz 127df482650SStephan Linz /* Promiscuous mode disable */ 128df482650SStephan Linz if (!ll_temac_indirect_set(regs, TEMAC_AFM, 0)) 129df482650SStephan Linz return 0; 130df482650SStephan Linz 131df482650SStephan Linz /* Enable Receiver - RX bit */ 132df482650SStephan Linz if (!ll_temac_indirect_set(regs, TEMAC_RCW1, RCW1_RX)) 133df482650SStephan Linz return 0; 134df482650SStephan Linz 135df482650SStephan Linz /* Enable Transmitter - TX bit */ 136df482650SStephan Linz if (!ll_temac_indirect_set(regs, TEMAC_TC, TC_TX)) 137df482650SStephan Linz return 0; 138df482650SStephan Linz 139df482650SStephan Linz return 1; 140df482650SStephan Linz } 141df482650SStephan Linz 142df482650SStephan Linz /* 143df482650SStephan Linz * Configure ll_temac based on negotiated speed and duplex 144df482650SStephan Linz * reported by PHY handling code 145df482650SStephan Linz */ 146df482650SStephan Linz static int ll_temac_adjust_link(struct eth_device *dev) 147df482650SStephan Linz { 148df482650SStephan Linz unsigned int speed, emmc_reg; 149df482650SStephan Linz struct temac_reg *regs = (struct temac_reg *)dev->iobase; 150df482650SStephan Linz struct ll_temac *ll_temac = dev->priv; 151df482650SStephan Linz struct phy_device *phydev = ll_temac->phydev; 152df482650SStephan Linz 153df482650SStephan Linz if (!phydev->link) { 154df482650SStephan Linz printf("%s: No link.\n", phydev->dev->name); 155df482650SStephan Linz return 0; 156df482650SStephan Linz } 157df482650SStephan Linz 158df482650SStephan Linz switch (phydev->speed) { 159df482650SStephan Linz case 1000: 160df482650SStephan Linz speed = EMMC_LSPD_1000; 161df482650SStephan Linz break; 162df482650SStephan Linz case 100: 163df482650SStephan Linz speed = EMMC_LSPD_100; 164df482650SStephan Linz break; 165df482650SStephan Linz case 10: 166df482650SStephan Linz speed = EMMC_LSPD_10; 167df482650SStephan Linz break; 168df482650SStephan Linz default: 169df482650SStephan Linz return 0; 170df482650SStephan Linz } 171df482650SStephan Linz 172df482650SStephan Linz if (!ll_temac_indirect_get(regs, TEMAC_EMMC, &emmc_reg)) 173df482650SStephan Linz return 0; 174df482650SStephan Linz 175df482650SStephan Linz emmc_reg &= ~EMMC_LSPD_MASK; 176df482650SStephan Linz emmc_reg |= speed; 177df482650SStephan Linz 178df482650SStephan Linz if (!ll_temac_indirect_set(regs, TEMAC_EMMC, emmc_reg)) 179df482650SStephan Linz return 0; 180df482650SStephan Linz 181df482650SStephan Linz printf("%s: PHY is %s with %dbase%s, %s%s\n", 182df482650SStephan Linz dev->name, phydev->drv->name, 183df482650SStephan Linz phydev->speed, (phydev->port == PORT_TP) ? "T" : "X", 184df482650SStephan Linz (phydev->duplex) ? "FDX" : "HDX", 185df482650SStephan Linz (phydev->port == PORT_OTHER) ? ", unkown mode" : ""); 186df482650SStephan Linz 187df482650SStephan Linz return 1; 188df482650SStephan Linz } 189df482650SStephan Linz 190df482650SStephan Linz /* setup mac addr */ 191df482650SStephan Linz static int ll_temac_setup_mac_addr(struct eth_device *dev) 192df482650SStephan Linz { 193df482650SStephan Linz struct temac_reg *regs = (struct temac_reg *)dev->iobase; 194df482650SStephan Linz u32 val; 195df482650SStephan Linz 196df482650SStephan Linz /* set up unicast MAC address filter */ 197df482650SStephan Linz val = ((dev->enetaddr[3] << 24) | (dev->enetaddr[2] << 16) | 198df482650SStephan Linz (dev->enetaddr[1] << 8) | (dev->enetaddr[0])); 199df482650SStephan Linz val &= UAW0_UADDR_MASK; 200df482650SStephan Linz 201df482650SStephan Linz if (!ll_temac_indirect_set(regs, TEMAC_UAW0, val)) 202df482650SStephan Linz return 1; 203df482650SStephan Linz 204df482650SStephan Linz val = ((dev->enetaddr[5] << 8) | dev->enetaddr[4]); 205df482650SStephan Linz val &= UAW1_UADDR_MASK; 206df482650SStephan Linz 207df482650SStephan Linz if (!ll_temac_indirect_set(regs, TEMAC_UAW1, val)) 208df482650SStephan Linz return 1; 209df482650SStephan Linz 210df482650SStephan Linz return 0; 211df482650SStephan Linz } 212df482650SStephan Linz 213df482650SStephan Linz /* halt device */ 214df482650SStephan Linz static void ll_temac_halt(struct eth_device *dev) 215df482650SStephan Linz { 216df482650SStephan Linz struct ll_temac *ll_temac = dev->priv; 217df482650SStephan Linz struct temac_reg *regs = (struct temac_reg *)dev->iobase; 218df482650SStephan Linz 219df482650SStephan Linz /* Disable Receiver */ 220df482650SStephan Linz ll_temac_indirect_set(regs, TEMAC_RCW0, 0); 221df482650SStephan Linz 222df482650SStephan Linz /* Disable Transmitter */ 223df482650SStephan Linz ll_temac_indirect_set(regs, TEMAC_TC, 0); 224df482650SStephan Linz 225df482650SStephan Linz if (ll_temac->ctrlhalt) 226df482650SStephan Linz ll_temac->ctrlhalt(dev); 227df482650SStephan Linz 228df482650SStephan Linz /* Shut down the PHY, as needed */ 229df482650SStephan Linz phy_shutdown(ll_temac->phydev); 230df482650SStephan Linz } 231df482650SStephan Linz 232df482650SStephan Linz static int ll_temac_init(struct eth_device *dev, bd_t *bis) 233df482650SStephan Linz { 234df482650SStephan Linz struct ll_temac *ll_temac = dev->priv; 235*11af8d65STimur Tabi int ret; 236df482650SStephan Linz 237df482650SStephan Linz printf("%s: Xilinx XPS LocalLink Tri-Mode Ether MAC #%d at 0x%08X.\n", 238df482650SStephan Linz dev->name, dev->index, dev->iobase); 239df482650SStephan Linz 240df482650SStephan Linz if (!ll_temac_setup_ctrl(dev)) 241df482650SStephan Linz return -1; 242df482650SStephan Linz 243df482650SStephan Linz /* Start up the PHY */ 244*11af8d65STimur Tabi ret = phy_startup(ll_temac->phydev); 245*11af8d65STimur Tabi if (ret) { 246*11af8d65STimur Tabi printf("%s: Could not initialize PHY %s\n", 247*11af8d65STimur Tabi dev->name, ll_temac->phydev->dev->name); 248*11af8d65STimur Tabi return ret; 249*11af8d65STimur Tabi } 250df482650SStephan Linz 251df482650SStephan Linz if (!ll_temac_adjust_link(dev)) { 252df482650SStephan Linz ll_temac_halt(dev); 253df482650SStephan Linz return -1; 254df482650SStephan Linz } 255df482650SStephan Linz 256df482650SStephan Linz /* If there's no link, fail */ 257df482650SStephan Linz return ll_temac->phydev->link ? 0 : -1; 258df482650SStephan Linz } 259df482650SStephan Linz 260df482650SStephan Linz /* 261df482650SStephan Linz * Discover which PHY is attached to the device, and configure it 262df482650SStephan Linz * properly. If the PHY is not recognized, then return 0 263df482650SStephan Linz * (failure). Otherwise, return 1 264df482650SStephan Linz */ 265df482650SStephan Linz static int ll_temac_phy_init(struct eth_device *dev) 266df482650SStephan Linz { 267df482650SStephan Linz struct ll_temac *ll_temac = dev->priv; 268df482650SStephan Linz struct phy_device *phydev; 269df482650SStephan Linz unsigned int supported = PHY_GBIT_FEATURES; 270df482650SStephan Linz 271df482650SStephan Linz /* interface - look at driver/net/tsec.c */ 272df482650SStephan Linz phydev = phy_connect(ll_temac->bus, ll_temac->phyaddr, 273df482650SStephan Linz dev, PHY_INTERFACE_MODE_NONE); 274df482650SStephan Linz 275df482650SStephan Linz phydev->supported &= supported; 276df482650SStephan Linz phydev->advertising = phydev->supported; 277df482650SStephan Linz 278df482650SStephan Linz ll_temac->phydev = phydev; 279df482650SStephan Linz 280df482650SStephan Linz phy_config(phydev); 281df482650SStephan Linz 282df482650SStephan Linz return 1; 283df482650SStephan Linz } 284df482650SStephan Linz 285df482650SStephan Linz /* 286df482650SStephan Linz * Initialize a single ll_temac devices 287df482650SStephan Linz * 288df482650SStephan Linz * Returns the result of ll_temac phy interface that were initialized 289df482650SStephan Linz */ 290df482650SStephan Linz int xilinx_ll_temac_initialize(bd_t *bis, struct ll_temac_info *devinf) 291df482650SStephan Linz { 292df482650SStephan Linz struct eth_device *dev; 293df482650SStephan Linz struct ll_temac *ll_temac; 294df482650SStephan Linz 295df482650SStephan Linz dev = calloc(1, sizeof(*dev)); 296df482650SStephan Linz if (dev == NULL) 297df482650SStephan Linz return 0; 298df482650SStephan Linz 299df482650SStephan Linz ll_temac = calloc(1, sizeof(struct ll_temac)); 300df482650SStephan Linz if (ll_temac == NULL) { 301df482650SStephan Linz free(dev); 302df482650SStephan Linz return 0; 303df482650SStephan Linz } 304df482650SStephan Linz 305df482650SStephan Linz /* use given name or generate its own unique name */ 306df482650SStephan Linz if (devinf->devname) { 30734921d04SStephan Linz strncpy(dev->name, devinf->devname, sizeof(dev->name)); 308df482650SStephan Linz } else { 30934921d04SStephan Linz snprintf(dev->name, sizeof(dev->name), "lltemac.%lx", devinf->base_addr); 310df482650SStephan Linz devinf->devname = dev->name; 311df482650SStephan Linz } 312df482650SStephan Linz 313df482650SStephan Linz dev->iobase = devinf->base_addr; 314df482650SStephan Linz 315df482650SStephan Linz dev->priv = ll_temac; 316df482650SStephan Linz dev->init = ll_temac_init; 317df482650SStephan Linz dev->halt = ll_temac_halt; 318df482650SStephan Linz dev->write_hwaddr = ll_temac_setup_mac_addr; 319df482650SStephan Linz 320df482650SStephan Linz ll_temac->ctrladdr = devinf->ctrl_addr; 321df482650SStephan Linz if (devinf->flags & XILINX_LL_TEMAC_M_SDMA_PLB) { 322df482650SStephan Linz #if defined(CONFIG_XILINX_440) || defined(CONFIG_XILINX_405) 323df482650SStephan Linz if (devinf->flags & XILINX_LL_TEMAC_M_SDMA_DCR) { 324df482650SStephan Linz ll_temac_collect_xldcr_sdma_reg_addr(dev); 325df482650SStephan Linz ll_temac->in32 = ll_temac_xldcr_in32; 326df482650SStephan Linz ll_temac->out32 = ll_temac_xldcr_out32; 327df482650SStephan Linz } else 328df482650SStephan Linz #endif 329df482650SStephan Linz { 330df482650SStephan Linz ll_temac_collect_xlplb_sdma_reg_addr(dev); 331df482650SStephan Linz ll_temac->in32 = ll_temac_xlplb_in32; 332df482650SStephan Linz ll_temac->out32 = ll_temac_xlplb_out32; 333df482650SStephan Linz } 334df482650SStephan Linz ll_temac->ctrlinit = ll_temac_init_sdma; 335df482650SStephan Linz ll_temac->ctrlhalt = ll_temac_halt_sdma; 336df482650SStephan Linz ll_temac->ctrlreset = ll_temac_reset_sdma; 337df482650SStephan Linz dev->recv = ll_temac_recv_sdma; 338df482650SStephan Linz dev->send = ll_temac_send_sdma; 339df482650SStephan Linz } else { 340df482650SStephan Linz ll_temac->in32 = NULL; 341df482650SStephan Linz ll_temac->out32 = NULL; 342df482650SStephan Linz ll_temac->ctrlinit = NULL; 343df482650SStephan Linz ll_temac->ctrlhalt = NULL; 344df482650SStephan Linz ll_temac->ctrlreset = ll_temac_reset_fifo; 345df482650SStephan Linz dev->recv = ll_temac_recv_fifo; 346df482650SStephan Linz dev->send = ll_temac_send_fifo; 347df482650SStephan Linz } 348df482650SStephan Linz 349df482650SStephan Linz /* Link to specified MDIO bus */ 350df482650SStephan Linz strncpy(ll_temac->mdio_busname, devinf->mdio_busname, MDIO_NAME_LEN); 351df482650SStephan Linz ll_temac->bus = miiphy_get_dev_by_name(ll_temac->mdio_busname); 352df482650SStephan Linz 353df482650SStephan Linz /* Looking for a valid PHY address if it is not yet set */ 354df482650SStephan Linz if (devinf->phyaddr == -1) 355df482650SStephan Linz ll_temac->phyaddr = ll_temac_phy_addr(ll_temac->bus); 356df482650SStephan Linz else 357df482650SStephan Linz ll_temac->phyaddr = devinf->phyaddr; 358df482650SStephan Linz 359df482650SStephan Linz eth_register(dev); 360df482650SStephan Linz 361df482650SStephan Linz /* Try to initialize PHY here, and return */ 362df482650SStephan Linz return ll_temac_phy_init(dev); 363df482650SStephan Linz } 364df482650SStephan Linz 365df482650SStephan Linz /* 366df482650SStephan Linz * Initialize a single ll_temac device with its mdio bus behind ll_temac 367df482650SStephan Linz * 368df482650SStephan Linz * Returns 1 if the ll_temac device and the mdio bus were initialized 369df482650SStephan Linz * otherwise returns 0 370df482650SStephan Linz */ 371df482650SStephan Linz int xilinx_ll_temac_eth_init(bd_t *bis, unsigned long base_addr, int flags, 372df482650SStephan Linz unsigned long ctrl_addr) 373df482650SStephan Linz { 374df482650SStephan Linz struct ll_temac_info devinf; 375df482650SStephan Linz struct ll_temac_mdio_info mdioinf; 376df482650SStephan Linz int ret; 377df482650SStephan Linz 378df482650SStephan Linz /* prepare the internal driver informations */ 379df482650SStephan Linz devinf.flags = flags; 380df482650SStephan Linz devinf.base_addr = base_addr; 381df482650SStephan Linz devinf.ctrl_addr = ctrl_addr; 382df482650SStephan Linz devinf.devname = NULL; 383df482650SStephan Linz devinf.phyaddr = -1; 384df482650SStephan Linz 385df482650SStephan Linz mdioinf.name = devinf.mdio_busname = NULL; 386df482650SStephan Linz mdioinf.regs = (struct temac_reg *)devinf.base_addr; 387df482650SStephan Linz 388df482650SStephan Linz ret = xilinx_ll_temac_mdio_initialize(bis, &mdioinf); 389df482650SStephan Linz if (ret >= 0) { 390df482650SStephan Linz 391df482650SStephan Linz /* 392df482650SStephan Linz * If there was no MDIO bus name then take over the 393df482650SStephan Linz * new automaticaly generated by the MDIO init code. 394df482650SStephan Linz */ 395df482650SStephan Linz if (mdioinf.name != devinf.mdio_busname) 396df482650SStephan Linz devinf.mdio_busname = mdioinf.name; 397df482650SStephan Linz 398df482650SStephan Linz ret = xilinx_ll_temac_initialize(bis, &devinf); 399df482650SStephan Linz if (ret > 0) 400df482650SStephan Linz return 1; 401df482650SStephan Linz 402df482650SStephan Linz } 403df482650SStephan Linz 404df482650SStephan Linz return 0; 405df482650SStephan Linz } 406