add at91 SoC access with c structures
Subsystems (2)
PATCH 7/8 add at91 SoC access with c structures
* add new at91_emac driver
* using c structure SoC access
* supports NET_MULTI for AT91RM9200
Signed-off-by Jens Scharsig <[email protected]>
--- drivers/net/at91_emac.c | 498 +++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 498 insertions(+), 0 deletions(-) create mode 100644 drivers/net/at91_emac.c
drivers/net/at91_emac.c new file +498 -0
@@ -0,0 +1,498 @@ +/* +* Copyright (C) 2009 BuS Elektronik GmbH & Co. KG +* Jens Scharsig (esw at bus-elektronik.de) +* +* (C) Copyright 2003 +* Author : Hamid Ikdoumi (Atmel) + +* See file CREDITS for list of people who contributed to this +* project. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, +* MA 02111-1307 USA +*/ + +#include <common.h> +#include <asm/io.h> +#ifndef CONFIG_AT91_LEGACY +#include <asm/arch/hardware.h> +#include <asm/arch/at91_emac.h> +#include <asm/arch/at91_pmc.h> +#include <asm/arch/at91_pio.h> +#else +/* remove next 5 lines, if all RM9200 boards convert to at91 arch */ +#include <asm/arch-at91/at91rm9200.h> +#include <asm/arch-at91/hardware.h> +#include <asm/arch-at91/at91_emac.h> +#include <asm/arch-at91/at91_pmc.h> +#include <asm/arch-at91/at91_pio.h> +#endif +#include <net.h> +#include <netdev.h> +#include <malloc.h> +#include <miiphy.h> +#include <linux/mii.h> + +#undef MII_DEBUG +#undef ET_DEBUG + +#if (CONFIG_SYS_RX_ETH_BUFFER >1024 ) +#error AT91 EMAC supports max 1024 RX buffers. \ + Please decrease the CONFIG_SYS_RX_ETH_BUFFER value +#endif + +/* MDIO clock must not exceed 2.5 MHz, so enable MCK divider */ +#if (AT91C_MASTER_CLOCK >80000000 ) +#define HCLK_DIV AT91_EMAC_CFG_MCLK_64 +#elif (AT91C_MASTER_CLOCK >40000000 ) +#define HCLK_DIV AT91_EMAC_CFG_MCLK_32 +#elif (AT91C_MASTER_CLOCK >20000000 ) +#define HCLK_DIV AT91_EMAC_CFG_MCLK_16 +#else +#define HCLK_DIV AT91_EMAC_CFG_MCLK_8 +#endif + +#ifdef ET_DEBUG +#define DEBUG_AT91EMAC (...) printf(__VA_ARGS__); +#else +#define DEBUG_AT91EMAC (...) +#endif + +#ifdef MII_DEBUG +#define DEBUG_AT91PHY (...) printf(__VA_ARGS__); +#else +#define DEBUG_AT91PHY (...) +#endif + +#ifndef CONFIG_DRIVER_AT91EMAC_QUIET +#define VERBOSEP (...) printf(__VA_ARGS__); +#else +#define VERBOSEP (...) +#endif + +#define RBF_ADDR 0xfffffffc +#define RBF_OWNER (1<<0) +#define RBF_WRAP (1<<1) +#define RBF_BROADCAST (1<<31) +#define RBF_MULTICAST (1<<30) +#define RBF_UNICAST (1<<29) +#define RBF_EXTERNAL (1<<28) +#define RBF_UNKOWN (1<<27) +#define RBF_SIZE 0x07ff +#define RBF_LOCAL4 (1<<26) +#define RBF_LOCAL3 (1<<25) +#define RBF_LOCAL2 (1<<24) +#define RBF_LOCAL1 (1<<23) + +#define RBF_FRAMEMAX CONFIG_SYS_RX_ETH_BUFFER +#define RBF_FRAMELEN 0x600 + +typedef struct { +unsigned long addr, size; +}rbf_t ; + +typedef struct { +rbf_t rbfdt[RBF_FRAMEMAX ]; +unsigned long rbindex; +}emac_device ; + +void at91emac_EnableMDIO (at91_emac_t *at91mac) +{ +/* Mac CTRL reg set for MDIO enable */ +writel (readl (&at91mac->ctl) |AT91_EMAC_CTL_MPE , &at91mac->ctl); +} + +void at91emac_DisableMDIO (at91_emac_t *at91mac) +{ +/* Mac CTRL reg set for MDIO disable */ +writel (readl (&at91mac->ctl) & ~AT91_EMAC_CTL_MPE , &at91mac->ctl); +} + +int at91emac_read (at91_emac_t *at91mac,unsigned char addr, +unsigned char reg,unsigned short *value) +{ +at91emac_EnableMDIO (at91mac); + +writel (AT91_EMAC_MAN_HIGH |AT91_EMAC_MAN_RW_R | +AT91_EMAC_MAN_REGA (reg) |AT91_EMAC_MAN_CODE_802_3 | +AT91_EMAC_MAN_PHYA (addr), + &at91mac->man); +udelay (10000 ); + *value =readl (&at91mac->man) &AT91_EMAC_MAN_DATA_MASK ; + +at91emac_DisableMDIO (at91mac); + + DEBUG_AT91PHY("AT91PHY read %x REG(%d)=%x\n", at91mac, reg, *value) + +return 0 ; +} + +int at91emac_write (at91_emac_t *at91mac,unsigned char addr, +unsigned char reg,unsigned short value) +{ + DEBUG_AT91PHY("AT91PHY write %x REG(%d)=%x\n", at91mac, reg, &value) + +at91emac_EnableMDIO (at91mac); + +writel (AT91_EMAC_MAN_HIGH |AT91_EMAC_MAN_RW_W | +AT91_EMAC_MAN_REGA (reg) |AT91_EMAC_MAN_CODE_802_3 | +AT91_EMAC_MAN_PHYA (addr) | (value &AT91_EMAC_MAN_DATA_MASK ), + &at91mac->man); +udelay (10000 ); + +at91emac_DisableMDIO (at91mac); +return 0 ; +} + +#if defined(CONFIG_MII ) || defined(CONFIG_CMD_MII ) + +at91_emac_t *get_emacbase_by_name (char *devname) +{ +struct eth_device *netdev; + + netdev =eth_get_dev_by_name (devname); +return (at91_emac_t *) netdev->iobase; +} + +int at91emac_mii_read (char *devname,unsigned char addr, +unsigned char reg,unsigned short *value) +{ +at91_emac_t *emac; + + emac =get_emacbase_by_name (devname); +at91emac_read (emac , addr, reg, value); +return 0 ; +} + + +int at91emac_mii_write (char *devname,unsigned char addr, +unsigned char reg,unsigned short value) +{ +at91_emac_t *emac; + + emac =get_emacbase_by_name (devname); +at91emac_write (emac, addr, reg, value); +return 0 ; +} + +#endif + +static int at91emac_phy_reset (struct eth_device *netdev) +{ +int i; +u16 status, adv; +at91_emac_t *emac; + + emac = (at91_emac_t *) netdev->iobase; + + adv =ADVERTISE_CSMA |ADVERTISE_ALL ; +at91emac_write (emac,0 ,MII_ADVERTISE , adv); +VERBOSEP ("%s: Starting autonegotiation...\n" , netdev->name); +at91emac_write (emac,0 ,MII_BMCR , (BMCR_ANENABLE |BMCR_ANRESTART )); + +for (i =0 ; i <100000 /100 ; i++) { +at91emac_read (emac,0 ,MII_BMSR , &status); +if (status &BMSR_ANEGCOMPLETE ) +break ; +udelay (100 ); + } + +if (status &BMSR_ANEGCOMPLETE ) { +VERBOSEP ("%s: Autonegotiation complete\n" , netdev->name); + }else { +printf ("%s: Autonegotiation timed out (status=0x%04x)\n" , + netdev->name, status); +return 1 ; + } +return 0 ; +} + +static int at91emac_phy_init (struct eth_device *netdev) +{ +u16 phy_id, status, adv, lpa; +int media, speed, duplex; +int i; +at91_emac_t *emac; + + emac = (at91_emac_t *) netdev->iobase; + +/* Check if the PHY is up to snuff... */ +at91emac_read (emac,0 ,MII_PHYSID1 , &phy_id); +if (phy_id ==0xffff ) { +printf ("%s: No PHY present\n" , netdev->name); +return 1 ; + } + +at91emac_read (emac,0 ,MII_BMSR , &status); + +if (!(status &BMSR_LSTATUS )) { +/* Try to re-negotiate if we don't have link already. */ +if (at91emac_phy_reset (netdev)) +return 2 ; + +for (i =0 ; i <100000 /100 ; i++) { +at91emac_read (emac,0 ,MII_BMSR , &status); +if (status &BMSR_LSTATUS ) +break ; +udelay (100 ); + } + } +if (!(status &BMSR_LSTATUS )) { +VERBOSEP ("%s: link down\n" , netdev->name); +return 3 ; + }else { +at91emac_read (emac,0 ,MII_ADVERTISE , &adv); +at91emac_read (emac,0 ,MII_LPA , &lpa); + media =mii_nway_result (lpa & adv); + speed = (media & (ADVERTISE_100FULL |ADVERTISE_100HALF ) + ?1 :0 ); + duplex = (media &ADVERTISE_FULL ) ?1 :0 ; +VERBOSEP ("%s: link up, %sMbps %s-duplex\n" , + netdev->name, + speed ?"100" :"10" , + duplex ?"full" :"half" ); + } +return 0 ; +} + +int at91emac_UpdateLinkSpeed (at91_emac_t *emac) +{ +unsigned short stat1; + +at91emac_read (emac,0 ,MII_BMSR , &stat1); + +if (!(stat1 &BMSR_LSTATUS ))/* link status up? */ +return 1 ; + +if (stat1 &BMSR_100FULL ) { +/*set Emac for 100BaseTX and Full Duplex */ +writel (readl (&emac->cfg) | +AT91_EMAC_CFG_SPD |AT91_EMAC_CFG_FD , + &emac->cfg); +return 0 ; + } + +if (stat1 &BMSR_10FULL ) { +/*set MII for 10BaseT and Full Duplex */ +writel ((readl (&emac->cfg) & + ~(AT91_EMAC_CFG_SPD |AT91_EMAC_CFG_FD ) + ) |AT91_EMAC_CFG_FD , + &emac->cfg); +return 0 ; + } + +if (stat1 &BMSR_100HALF ) { +/*set MII for 100BaseTX and Half Duplex */ +writel ((readl (&emac->cfg) & + ~(AT91_EMAC_CFG_SPD |AT91_EMAC_CFG_FD ) + ) |AT91_EMAC_CFG_SPD , + &emac->cfg); +return 0 ; + } + +if (stat1 &BMSR_10HALF ) { +/*set MII for 10BaseT and Half Duplex */ +writel ((readl (&emac->cfg) & + ~(AT91_EMAC_CFG_SPD |AT91_EMAC_CFG_FD )), + &emac->cfg); +return 0 ; + } +return 1 ; +} + +static int at91emac_init (struct eth_device *netdev,bd_t *bd) +{ +int i; +u32 value; +emac_device *dev; +at91_emac_t *emac; +at91_pio_t *pio = (at91_pio_t *)AT91_PIO_BASE ; +at91_pmc_t *pmc = (at91_pmc_t *)AT91_PMC_BASE ; + + emac = (at91_emac_t *) netdev->iobase; + dev = (emac_device *) netdev->priv; + +/* PIO Disable Register */ + value =AT91_PMX_AA_EMDIO |AT91_PMX_AA_EMDC | +AT91_PMX_AA_ERXER |AT91_PMX_AA_ERX1 | +AT91_PMX_AA_ERX0 |AT91_PMX_AA_ECRS | +AT91_PMX_AA_ETX1 |AT91_PMX_AA_ETX0 | +AT91_PMX_AA_ETXEN |AT91_PMX_AA_EREFCK ; + +writel (value, &pio->pioa.pdr); +writel (value, &pio->pioa.asr); + +#ifdef CONFIG_RMII + value =AT91_PMX_BA_ERXCK ; +#else + value =AT91_PMX_BA_ERXCK |AT91_PMX_BA_ECOL | +AT91_PMX_BA_ERXDV |AT91_PMX_BA_ERX3 | +AT91_PMX_BA_ERX2 |AT91_PMX_BA_ETXER | +AT91_PMX_BA_ETX3 |AT91_PMX_BA_ETX2 ; +#endif +writel (value, &pio->piob.pdr); +writel (value, &pio->piob.bsr); + +writel (1 <<AT91_ID_EMAC , &pmc->pcer); +writel (readl (&emac->ctl) |AT91_EMAC_CTL_CSR , &emac->ctl); + +DEBUG_AT91EMAC ("init MAC-ADDR %x%x \n" , +cpu_to_le16 (*((u16 *)(netdev->enetaddr +4 ))), +cpu_to_le32 (*((u32 *)netdev->enetaddr))); +writel (cpu_to_le32 (*((u32 *)netdev->enetaddr)), &emac->sa2l); +writel (cpu_to_le16 (*((u16 *)(netdev->enetaddr +4 ))), &emac->sa2h); +DEBUG_AT91EMAC ("init MAC-ADDR %x%x \n" , +readl (&emac->sa2h),readl (&emac->sa2l)); + +/* Init Ethernet buffers */ +for (i =0 ; i <RBF_FRAMEMAX ; i++) { + dev->rbfdt[i].addr = (unsigned long ) NetRxPackets[i]; + dev->rbfdt[i].size =0 ; + } + dev->rbfdt[RBF_FRAMEMAX -1 ].addr |=RBF_WRAP ; + dev->rbindex =0 ; +writel ((u32) &(dev->rbfdt[0 ]), &emac->rbqp); + +writel (readl (&emac->rsr) & + ~(AT91_EMAC_RSR_OVR |AT91_EMAC_RSR_REC |AT91_EMAC_RSR_BNA ), + &emac->rsr); + + value =AT91_EMAC_CFG_CAF |AT91_EMAC_CFG_NBC | +HCLK_DIV ; +#ifdef CONFIG_RMII + value |=AT91C_EMAC_RMII ; +#endif +writel (value, &emac->cfg); + +writel (readl (&emac->ctl) |AT91_EMAC_CTL_TE |AT91_EMAC_CTL_RE , + &emac->ctl); + +if (!at91emac_phy_init (netdev)) { +at91emac_UpdateLinkSpeed (emac); +return 0 ; + } +return 1 ; +} + +static void at91emac_halt (struct eth_device *netdev) +{ +at91_emac_t *emac; + + emac = (at91_emac_t *) netdev->iobase; +writel (readl (&emac->ctl) & ~(AT91_EMAC_CTL_TE |AT91_EMAC_CTL_RE ), + &emac->ctl); +DEBUG_AT91EMAC ("halt MAC\n" ); +} + +static int at91emac_send (struct eth_device *netdev,volatile void *packet, +int length) +{ +at91_emac_t *emac; + + emac = (at91_emac_t *) netdev->iobase; + +while (!(readl (&emac->tsr) &AT91_EMAC_TSR_BNQ )) + ; +writel ((u32 ) packet, &emac->tar); +writel (AT91_EMAC_TCR_LEN (length), &emac->tcr); +while (AT91_EMAC_TCR_LEN (readl (&emac->tcr))) + ; +DEBUG_AT91EMAC ("Send %d \n" , length); +writel (readl (&emac->tsr) |AT91_EMAC_TSR_COMP , &emac->tsr); +return 0 ; +} + +static int at91emac_recv (struct eth_device *netdev) +{ +emac_device *dev; +at91_emac_t *emac; +rbf_t *rbfp; +int size; + + emac = (at91_emac_t *) netdev->iobase; + dev = (emac_device *) netdev->priv; + + rbfp = &dev->rbfdt[dev->rbindex]; +while (rbfp->addr &RBF_OWNER ) { + size = rbfp->size &RBF_SIZE ; +NetReceive (NetRxPackets[dev->rbindex], size); + +DEBUG_AT91EMAC ("Recv[%d]: %d bytes @ %x \n" , + dev->rbindex, size, rbfp->addr); + + rbfp->addr &= ~RBF_OWNER ; + rbfp->size =0 ; +if (dev->rbindex < (RBF_FRAMEMAX -1 )) + dev->rbindex++; +else + dev->rbindex =0 ; + + rbfp = &(dev->rbfdt[dev->rbindex]); +if (!(rbfp->addr &RBF_OWNER )) +writel (readl (&emac->rsr) |AT91_EMAC_RSR_REC , + &emac->rsr); + } + +if (readl (&emac->isr) & AT91_EMAC_IxR_RBNA) { +/* EMAC silicon bug 41.3.1 workaround 1 */ +writel (readl (&emac->ctl) & ~AT91_EMAC_CTL_RE , &emac->ctl); +writel (readl (&emac->ctl) |AT91_EMAC_CTL_RE , &emac->ctl); + dev->rbindex =0 ; +printf ("%s: reset receiver (EMAC dead lock bug)\n" , + netdev->name); + } +return 0 ; +} + +int at91emac_register (bd_t *bis,unsigned long iobase) +{ +emac_device *emac; +emac_device *emacfix; +struct eth_device *dev; + +if (iobase ==0 ) + iobase =AT91_EMAC_BASE ; + emac =malloc (sizeof (*emac)+512 ); +if (emac ==NULL ) +return 1 ; + dev =malloc (sizeof (*dev)); +if (dev ==NULL ) { +free (emac); +return 1 ; + } +/* alignment as per Errata (64 bytes) is insufficient! */ + emacfix = (emac_device *) (((unsigned long ) emac +0x1ff ) &0xFFFFFE00 ); +memset (emacfix,0 ,sizeof (emac_device)); + +memset (dev,0 ,sizeof (*dev)); +#ifndef CONFIG_RMII +sprintf (dev->name,"AT91 EMAC" ); +#else +sprintf (dev->name,"AT91 EMAC RMII" ); +#endif + dev->iobase = iobase; + dev->priv = emacfix; + dev->init = at91emac_init; + dev->halt = at91emac_halt; + dev->send = at91emac_send; + dev->recv = at91emac_recv; + +eth_register (dev); + +#if defined(CONFIG_MII ) || defined(CONFIG_CMD_MII ) +miiphy_register (dev->name, at91emac_mii_read, at91emac_mii_write); +#endif +return 1 ; +}
Signature · 1 line
1.6.0.2