/************************************************************************** * Etherboot - BOOTP/TFTP Bootstrap Program * P2001 NIC driver for Etherboot **************************************************************************/ /* * Copyright (C) 2005 Tobias Lorenz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ /* to get some global routines like printf */ #include "etherboot.h" /* to get the interface to the body of the program */ #include "nic.h" /* to get the ISA support functions, if this is an ISA NIC */ #include "isa.h" #include "hardware.h" #include "mii.h" #include "timer.h" /* NIC specific static variables go here */ static unsigned char MAC_HW_ADDR[6]={MAC_HW_ADDR_DRV}; /* DMA descriptors and buffers */ #define NUM_RX_DESC 4 /* Number of Rx descriptor registers. */ #define DMA_BUF_SIZE 2048 /* Buffer size */ static DMA_DSC txd __attribute__ ((__section__(".dma.desc"))); static DMA_DSC rxd[NUM_RX_DESC] __attribute__ ((__section__(".dma.desc"))); static char rxb[NUM_RX_DESC * DMA_BUF_SIZE] __attribute__ ((__section__(".dma.buffer"))); static char txb[ DMA_BUF_SIZE] __attribute__ ((__section__(".dma.buffer"))); static unsigned int cur_rx; /* Device selectors */ static unsigned int cur_channel; // DMA channel : 0..3 static unsigned int cur_phy; // PHY Address : 0..31 static P2001_ETH_regs_ptr EU; // Ethernet Unit : 0x0018_000 with _=0..3 /* mdio handling */ static int p2001_eth_mdio_read (int phy_id, int location); static void p2001_eth_mdio_write(int phy_id, int location, int val); /* net_device functions */ static int p2001_eth_poll (struct nic *nic, int retrieve); static void p2001_eth_transmit (struct nic *nic, const char *d, unsigned int t, unsigned int s, const char *p); static void p2001_eth_irq (struct nic *nic, irq_action_t action); static void p2001_eth_init (); static void p2001_eth_disable (struct dev *dev); static int p2001_eth_check_link(unsigned int phy); static int link; static void p2001_eth_phyreset (); static int p2001_eth_probe (struct dev *dev, unsigned short *probe_addrs __unused); /* Supported MII list */ static struct mii_chip_info { const char * name; unsigned int physid; // (MII_PHYSID2 << 16) | MII_PHYSID1 } mii_chip_table[] = { { "Intel LXT971A", 0x78e20013 }, { "Altima AC104-QF", 0x55410022 }, {NULL,0}, }; /************************************************************************** * PHY MANAGEMENT UNIT - Read/write **************************************************************************/ /** * mdio_read - read MII PHY register * @dev: the net device to read * @regadr: the phy register id to read * * Read MII registers through MDIO and MDC * using MDIO management frame structure and protocol(defined by ISO/IEC). */ static int p2001_eth_mdio_read(int phy_id, int location) { int result, boguscnt = 1000; do { /* Warten bis Hardware inaktiv (MIU = "0") */ while (P2001_MU->MU_CNTL & 0x8000) barrier(); /* Schreiben MU_CNTL */ P2001_MU->MU_CNTL = location + (phy_id<<5) + (2<<10); /* Warten bis Hardware aktiv (MIU = "1") */ while ((P2001_MU->MU_CNTL & 0x8000) == 0) barrier(); //asm("nop \r\n nop"); /* Warten bis Hardware inaktiv (MIU = "0") */ while (P2001_MU->MU_CNTL & 0x8000) barrier(); /* Fehler, wenn MDIO Read Error (MRE = "1") */ } while ((P2001_MU->MU_CNTL & 0x4000) && (--boguscnt > 0)); /* Lesen MU_DATA */ result = P2001_MU->MU_DATA; if (boguscnt == 0) return 0; if ((result & 0xffff) == 0xffff) return 0; return result & 0xffff; } /** * mdio_write - write MII PHY register * @dev: the net device to write * @regadr: the phy register id to write * @value: the register value to write with * * Write MII registers with @value through MDIO and MDC * using MDIO management frame structure and protocol(defined by ISO/IEC) */ static void p2001_eth_mdio_write(int phy_id, int location, int val) { /* Warten bis Hardware inaktiv (MIU = "0") */ while (P2001_MU->MU_CNTL & 0x8000) barrier(); /* Schreiben MU_DATA */ P2001_MU->MU_DATA = val; /* Schreiben MU_CNTL */ P2001_MU->MU_CNTL = location + (phy_id<<5) + (1<<10); /* Warten bis Hardware aktiv (MIU = "1") */ while ((P2001_MU->MU_CNTL & 0x8000) == 0) barrier(); //asm("nop \r\n nop"); /* Warten bis Hardware inaktiv (MIU = "0") */ while (P2001_MU->MU_CNTL & 0x8000) barrier(); } /************************************************************************** * POLL - Wait for a frame **************************************************************************/ /* Function: p2001_eth_poll * * Description: checks for a received packet and returns it if found. * * Arguments: struct nic *nic: NIC data structure * * Returns: 1 if a packet was received. * 0 if no pacet was received. * * Side effects: * Returns (copies) the packet to the array nic->packet. * Returns the length of the packet in nic->packetlen. */ static int p2001_eth_poll(struct nic *nic, int retrieve) { /* return true if there's an ethernet packet ready to read */ /* nic->packet should contain data on return */ /* nic->packetlen should contain length of data */ int retstat = 0; if (rxd[cur_rx].stat & (1<<31)) // OWN return retstat; if (!retrieve) return 1; nic->packetlen = rxd[cur_rx].cntl & 0xffff; if (rxd[cur_rx].stat & ((1<<26)|(1<<25)|(1<<24)|(1<<23)|(1<<22))) { /* corrupted packet received */ printf("p2001_eth_poll: Corrupted packet received, stat = %X\n", rxd[cur_rx].stat); retstat = 0; } else { /* give packet to higher routine */ memcpy(nic->packet, (rxb + cur_rx*DMA_BUF_SIZE), nic->packetlen); retstat = 1; } #ifdef DEBUG_NIC printf("p2001_eth_poll: packet from %! to %! received\n", (rxb+cur_rx*DMA_BUF_SIZE)+ETH_ALEN, (rxb+cur_rx*DMA_BUF_SIZE)); #endif /* disable receiver */ // FIXME: is that ok? it can produce grave errors. EU->RMAC_DMA_EN = 0; /* clear run bit */ /* return the descriptor and buffer to receive ring */ rxd[cur_rx].stat = (1<<31) | (1<<30) | (1<<29); // DSC0 OWN|START|END rxd[cur_rx].cntl = (1<<23); // DSC1 RECEIVE rxd[cur_rx].cntl |= cur_channel << 16; // DSC1 CHANNEL rxd[cur_rx].cntl |= DMA_BUF_SIZE; // DSC1 LEN if (++cur_rx == NUM_RX_DESC) cur_rx = 0; /* enable receiver */ if (!(EU->RMAC_DMA_EN & 0x01)) EU->RMAC_DMA_EN = 0x01; /* set run bit */ #ifdef DEBUG_NIC printf("RMAC_MIB0..5: %d:%d:%d:%d:%d:%d\n", EU->RMAC_MIB0, EU->RMAC_MIB1, EU->RMAC_MIB2, EU->RMAC_MIB3, EU->RMAC_MIB4, EU->RMAC_MIB5); #endif return retstat; /* initially as this is called to flush the input */ } /************************************************************************** * TRANSMIT - Transmit a frame **************************************************************************/ /* Function: p2001_eth_transmit * * Description: transmits a packet and waits for completion or timeout. * * Arguments: char d[6]: destination ethernet address. * unsigned short t: ethernet protocol type. * unsigned short s: size of the data-part of the packet. * char *p: the data for the packet. * * Returns: void. */ static void p2001_eth_transmit( struct nic *nic __unused, const char *d, /* Destination */ unsigned int t, /* Type */ unsigned int s, /* size */ const char *p) /* Packet */ { unsigned int nstype; #ifdef DEBUG_NIC unsigned int status; #endif /* assemble packet */ memcpy(txb, d, ETH_ALEN); // destination memcpy(txb+ETH_ALEN, nic->node_addr, ETH_ALEN); // source nstype = htons(t); memcpy(txb+2*ETH_ALEN, (char*)&nstype, 2); // type memcpy(txb+ETH_HLEN, p, s); // packet s += ETH_HLEN; /* pad to minimum packet size */ // while (s<ETH_ZLEN) // txb[s++] = '\0'; // TMAC_CNTL.ATP does the same #ifdef DEBUG_NIC printf("p2001_eth_transmit: packet from %! to %! sent (size: %d)\n", txb+ETH_ALEN, txb, s); #endif /* configure descriptor */ txd.stat = (1<<31) | (1<<30) | (1<<29); // DSC0 OWN|START|END txd.cntl = cur_channel << 16; // DSC1 CHANNEL txd.cntl |= s; // DSC1 LEN /* restart the transmitter */ EU->TMAC_DMA_EN = 0x01; /* set run bit */ while(EU->TMAC_DMA_EN & 0x01); /* wait */ #ifdef DEBUG_NIC /* check status */ status = EU->TMAC_DMA_STAT; if (status & ~(0x40)) // not END printf("p2001_eth_transmit: dma status=0x%hx\n", status); printf("TMAC_MIB6..7: %d:%d\n", EU->TMAC_MIB6, EU->TMAC_MIB7); #endif } /************************************************************************** * IRQ - Enable, Disable or Force Interrupts **************************************************************************/ /* Function: p2001_eth_irq * * Description: Enable, Disable, or Force, interrupts * * Arguments: struct nic *nic: NIC data structure * irq_action_t action: Requested action * * Returns: void. */ static void p2001_eth_irq(struct nic *nic __unused, irq_action_t action __unused) { switch ( action ) { case DISABLE : break; case ENABLE : break; case FORCE : break; } } /************************************************************************** * INIT - Initialize device **************************************************************************/ /* Function: p2001_init * * Description: resets the ethernet controller chip and various * data structures required for sending and receiving packets. * * returns: void. */ static void p2001_eth_init() { static int i; /* activate MII 3 */ if (cur_channel == 3) P2001_GPIO->PIN_MUX |= (1<<8); // MII_3_en = 1 #ifdef RMII /* RMII init sequence */ if (link & LPA_100) { EU->CONF_RMII = (1<<2) | (1<<1); // softres | 100Mbit EU->CONF_RMII = (1<<2) | (1<<1) | (1<<0); // softres | 100Mbit | RMII EU->CONF_RMII = (1<<1) | (1<<0); // 100 Mbit | RMII } else { EU->CONF_RMII = (1<<2); // softres EU->CONF_RMII = (1<<2) | (1<<0); // softres | RMII EU->CONF_RMII = (1<<0); // RMII } #endif /* disable transceiver */ // EU->TMAC_DMA_EN = 0; /* clear run bit */ // EU->RMAC_DMA_EN = 0; /* clear run bit */ /* set rx filter (physical mac addresses) */ EU->RMAC_PHYU = (MAC_HW_ADDR[0]<< 8) + (MAC_HW_ADDR[1]<< 0); EU->RMAC_PHYL = (MAC_HW_ADDR[2]<<24) + (MAC_HW_ADDR[3]<<16) + (MAC_HW_ADDR[4]<<8 ) + (MAC_HW_ADDR[5]<<0 ); /* initialize the tx descriptor ring */ // txd.stat = (1<<31) | (1<<30) | (1<<29); // DSC0 OWN|START|END // txd.cntl = cur_channel << 16; // DSC1 CHANNEL // txd.cntl |= DMA_BUF_SIZE; // DSC1 LEN txd.buf = (char *)&txb; // DSC2 BUFFER txd.next = &txd; // DSC3 NEXTDSC @self EU->TMAC_DMA_DESC = &txd; /* initialize the rx descriptor ring */ cur_rx = 0; for (i = 0; i < NUM_RX_DESC; i++) { rxd[i].stat = (1<<31) | (1<<30) | (1<<29); // DSC0 OWN|START|END rxd[i].cntl = (1<<23); // DSC1 RECEIVE rxd[i].cntl |= cur_channel << 16; // DSC1 CHANNEL rxd[i].cntl |= DMA_BUF_SIZE; // DSC1 LEN rxd[i].buf = &rxb[i*DMA_BUF_SIZE]; // DSC2 BUFFER (EU-RX data) rxd[i].next = &rxd[i+1]; // DSC3 NEXTDSC @next } rxd[NUM_RX_DESC-1].next = &rxd[0]; // DSC3 NEXTDSC @first EU->RMAC_DMA_DESC = &rxd[0]; /* set transmitter mode */ if (link & LPA_DUPLEX) EU->TMAC_CNTL = (1<<4) | /* COI: Collision ignore */ (1<<3) | /* CSI: Carrier Sense ignore */ (1<<2); /* ATP: Automatic Transmit Padding */ else EU->TMAC_CNTL = (1<<2); /* ATP: Automatic Transmit Padding */ /* set receive mode */ EU->RMAC_CNTL = (1<<3) | /* BROAD: Broadcast packets */ (1<<1); /* PHY : Packets to out MAC address */ /* enable receiver */ EU->RMAC_DMA_EN = 1; /* set run bit */ } /************************************************************************** * DISABLE - Turn off ethernet interface **************************************************************************/ static void p2001_eth_disable(struct dev *dev __unused) { /* put the card in its initial state */ /* This function serves 3 purposes. * This disables DMA and interrupts so we don't receive * unexpected packets or interrupts from the card after * etherboot has finished. * This frees resources so etherboot may use * this driver on another interface * This allows etherboot to reinitialize the interface * if something is something goes wrong. */ /* disable transmitter */ EU->TMAC_DMA_EN = 0; /* clear run bit */ /* disable receiver */ EU->RMAC_DMA_EN = 0; /* clear run bit */ } /************************************************************************** * LINK - Check for valid link **************************************************************************/ static int p2001_eth_check_link(unsigned int phy) { static int status; static unsigned int i, physid; /* print some information about out PHY */ physid = (p2001_eth_mdio_read(phy, MII_PHYSID2) << 16) | p2001_eth_mdio_read(phy, MII_PHYSID1); printf("PHY %d, ID 0x%x ", phy, physid); for (i = 0; mii_chip_table[i].physid; i++) if (mii_chip_table[i].physid == physid) { printf("(%s).\n", mii_chip_table[i].name); break; } if (!mii_chip_table[i].physid) printf("(unknown).\n"); /* Use 0x3300 for restarting NWay */ printf("Starting auto-negotiation... "); p2001_eth_mdio_write(phy, MII_BMCR, 0x3300); /* Bit 1.5 is set once the Auto-Negotiation process is completed. */ i = 0; do { mdelay(500); status = p2001_eth_mdio_read(phy, MII_BMSR); if (!status || (i++ > 6)) // 6*500ms = 3s timeout goto failed; } while (!(status & BMSR_ANEGCOMPLETE)); /* Bits 1.2 is set once the link is established. */ if ((status = p2001_eth_mdio_read(phy, MII_BMSR)) & BMSR_LSTATUS) { link = p2001_eth_mdio_read(phy, MII_ADVERTISE) & p2001_eth_mdio_read(phy, MII_LPA); printf(" Valid link, operating at: %sMb-%s\n", (link & LPA_100) ? "100" : "10", (link & LPA_DUPLEX) ? "FD" : "HD"); return 1; } failed: if (!status) printf("Failed\n"); else printf("No valid link\n"); return 0; } /************************************************************************** * PHYRESET - hardware reset all MII PHYs **************************************************************************/ /** * p2001_eth_phyreset - hardware reset all MII PHYs */ static void p2001_eth_phyreset() { /* GPIO24/25: TX_ER2/TX_ER0 */ /* GPIO26/27: PHY_RESET/TX_ER1 */ P2001_GPIO->PIN_MUX |= 0x0018; // 31-16: 0000 1111 0000 0000 P2001_GPIO->GPIO2_En |= 0x0400; P2001_GPIO->GPIO2_Out |= 0x04000000; P2001_GPIO->GPIO2_Out &= ~0x0400; mdelay(500); P2001_GPIO->GPIO2_Out |= 0x0400; #ifdef RMII /* RMII_clk_sel = 0xxb no RMII (default) */ /* RMII_clk_sel = 100b COL_0 */ /* RMII_clk_sel = 101b COL_1 */ /* RMII_clk_sel = 110b COL_2 */ /* RMII_clk_sel = 111b COL_3 */ P2001_GPIO->PIN_MUX |= (4 << 13); #endif } /************************************************************************** * PROBE - Look for an adapter, this routine's visible to the outside **************************************************************************/ static int p2001_eth_probe(struct dev *dev, unsigned short *probe_addrs __unused) { struct nic *nic = (struct nic *)dev; /* if probe_addrs is 0, then routine can use a hardwired default */ /* reset phys and configure mdio clk */ printf("Resetting PHYs...\n"); p2001_eth_phyreset(); /* set management unit clock divisor */ // max. MDIO CLK = 2.048 MHz (EU.doc) P2001_MU->MU_DIV = (SYSCLK/4096000)-1; // 2.048 MHz //asm("nop \n nop"); /* find the correct PHY/DMA/MAC combination */ printf("Searching for P2001 NICs...\n"); cur_phy = -1; for (cur_channel=0; cur_channel<4; cur_channel++) { EU = P2001_EU(cur_channel); /* find next phy */ while (++cur_phy < 16) { //printf("phy detect %d\n", cur_phy); if (p2001_eth_mdio_read(cur_phy, MII_BMSR) != 0) break; } if (cur_phy == 16) { printf("no more MII PHYs found\n"); break; } /* first a non destructive test for initial value RMAC_TLEN=1518 */ if (EU->RMAC_TLEN == 1518) { printf("Checking EU%d...\n", cur_channel); if (p2001_eth_check_link(cur_phy)) { /* initialize device */ p2001_eth_init(nic); /* set node address */ printf("Setting MAC address to %!\n", MAC_HW_ADDR); memcpy(nic->node_addr, MAC_HW_ADDR, 6); /* point to NIC specific routines */ dev->disable = p2001_eth_disable; nic->poll = p2001_eth_poll; nic->transmit = p2001_eth_transmit; nic->irq = p2001_eth_irq; /* Report the ISA pnp id of the board */ dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR); return 1; } } } /* else */ return 0; } ISA_ROM("p2001_eth", "P2001 Ethernet Driver") static struct isa_driver p2001_eth_driver __isa_driver = { .type = NIC_DRIVER, .name = "P2001 Ethernet Driver", .probe = p2001_eth_probe, .ioaddrs = 0, };