diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/i2c/fsl_i2c.c | 34 | ||||
-rw-r--r-- | drivers/mtd/nand/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/nand/s3c64xx.c | 319 | ||||
-rw-r--r-- | drivers/net/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/mpc512x_fec.c | 794 | ||||
-rw-r--r-- | drivers/net/mpc512x_fec.h | 227 | ||||
-rw-r--r-- | drivers/net/mpc5xxx_fec.c | 1067 | ||||
-rw-r--r-- | drivers/net/mpc5xxx_fec.h | 282 | ||||
-rw-r--r-- | drivers/serial/Makefile | 1 | ||||
-rw-r--r-- | drivers/serial/s3c64xx.c | 172 | ||||
-rw-r--r-- | drivers/serial/serial_sh.c | 49 | ||||
-rw-r--r-- | drivers/usb/usb_ohci.c | 1 |
12 files changed, 2935 insertions, 14 deletions
diff --git a/drivers/i2c/fsl_i2c.c b/drivers/i2c/fsl_i2c.c index b7b3a75bc95..264553dfa84 100644 --- a/drivers/i2c/fsl_i2c.c +++ b/drivers/i2c/fsl_i2c.c @@ -76,12 +76,20 @@ static const struct fsl_i2c *i2c_dev[2] = { * For this table, the values are based on a value of 1 for the DFSR * register. See the application note AN2919 "Determining the I2C Frequency * Divider Ratio for SCL" + * + * ColdFire I2C frequency dividers for FDR values are different from + * PowerPC. The protocol to use the I2C module is still the same. + * A different table is defined and are based on MCF5xxx user manual. + * */ static const struct { unsigned short divider; +#ifdef __PPC__ u8 dfsr; +#endif u8 fdr; } fsl_i2c_speed_map[] = { +#ifdef __PPC__ {160, 1, 32}, {192, 1, 33}, {224, 1, 34}, {256, 1, 35}, {288, 1, 0}, {320, 1, 1}, {352, 6, 1}, {384, 1, 2}, {416, 6, 2}, {448, 1, 38}, {480, 1, 3}, {512, 1, 39}, {544, 11, 3}, {576, 1, 4}, @@ -98,6 +106,25 @@ static const struct { {20480, 1, 25}, {24576, 1, 26}, {28672, 1, 62}, {30720, 1, 27}, {32768, 1, 63}, {36864, 1, 28}, {40960, 1, 29}, {49152, 1, 30}, {61440, 1, 31}, {-1, 1, 31} +#elif defined(__M68K__) + {20, 32}, {22, 33}, {24, 34}, {26, 35}, + {28, 0}, {28, 36}, {30, 1}, {32, 37}, + {34, 2}, {36, 38}, {40, 3}, {40, 39}, + {44, 4}, {48, 5}, {48, 40}, {56, 6}, + {56, 41}, {64, 42}, {68, 7}, {72, 43}, + {80, 8}, {80, 44}, {88, 9}, {96, 41}, + {104, 10}, {112, 42}, {128, 11}, {128, 43}, + {144, 12}, {160, 13}, {160, 48}, {192, 14}, + {192, 49}, {224, 50}, {240, 15}, {256, 51}, + {288, 16}, {320, 17}, {320, 52}, {384, 18}, + {384, 53}, {448, 54}, {480, 19}, {512, 55}, + {576, 20}, {640, 21}, {640, 56}, {768, 22}, + {768, 57}, {960, 23}, {896, 58}, {1024, 59}, + {1152, 24}, {1280, 25}, {1280, 60}, {1536, 26}, + {1536, 61}, {1792, 62}, {1920, 27}, {2048, 63}, + {2304, 28}, {2560, 29}, {3072, 30}, {3840, 31}, + {-1, 31} +#endif }; /** @@ -126,12 +153,17 @@ static unsigned int set_i2c_bus_speed(const struct fsl_i2c *dev, for (i = 0; i < ARRAY_SIZE(fsl_i2c_speed_map); i++) if (fsl_i2c_speed_map[i].divider >= divider) { - u8 fdr, dfsr; + u8 fdr; +#ifdef __PPC__ + u8 dfsr; dfsr = fsl_i2c_speed_map[i].dfsr; +#endif fdr = fsl_i2c_speed_map[i].fdr; speed = i2c_clk / fsl_i2c_speed_map[i].divider; writeb(fdr, &dev->fdr); /* set bus speed */ +#ifdef __PPC__ writeb(dfsr, &dev->dfsrr); /* set default filter */ +#endif break; } diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 19233105a20..b0abe6e5234 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -37,6 +37,7 @@ endif COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o COBJS-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o +COBJS-$(CONFIG_NAND_S3C64XX) += s3c64xx.o endif COBJS := $(COBJS-y) diff --git a/drivers/mtd/nand/s3c64xx.c b/drivers/mtd/nand/s3c64xx.c new file mode 100644 index 00000000000..159fe76aa5f --- /dev/null +++ b/drivers/mtd/nand/s3c64xx.c @@ -0,0 +1,319 @@ +/* + * (C) Copyright 2006 DENX Software Engineering + * + * Implementation for U-Boot 1.1.6 by Samsung + * + * (C) Copyright 2008 + * Guennadi Liakhovetki, DENX Software Engineering, <lg@denx.de> + * + * 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 <nand.h> +#include <s3c6400.h> + +#include <asm/io.h> +#include <asm/errno.h> + +#define MAX_CHIPS 2 +static int nand_cs[MAX_CHIPS] = {0, 1}; + +#ifdef CONFIG_NAND_SPL +#define printf(arg...) do {} while (0) +#endif + +/* Nand flash definition values by jsgood */ +#ifdef S3C_NAND_DEBUG +/* + * Function to print out oob buffer for debugging + * Written by jsgood + */ +static void print_oob(const char *header, struct mtd_info *mtd) +{ + int i; + struct nand_chip *chip = mtd->priv; + + printf("%s:\t", header); + + for (i = 0; i < 64; i++) + printf("%02x ", chip->oob_poi[i]); + + printf("\n"); +} +#endif /* S3C_NAND_DEBUG */ + +#ifdef CONFIG_NAND_SPL +static u_char nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + return readb(this->IO_ADDR_R); +} + +static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i = 0; i < len; i++) + writeb(buf[i], this->IO_ADDR_W); +} + +static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i = 0; i < len; i++) + buf[i] = readb(this->IO_ADDR_R); +} +#endif + +static void s3c_nand_select_chip(struct mtd_info *mtd, int chip) +{ + int ctrl = readl(NFCONT); + + switch (chip) { + case -1: + ctrl |= 6; + break; + case 0: + ctrl &= ~2; + break; + case 1: + ctrl &= ~4; + break; + default: + return; + } + + writel(ctrl, NFCONT); +} + +/* + * Hardware specific access to control-lines function + * Written by jsgood + */ +static void s3c_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *this = mtd->priv; + + if (ctrl & NAND_CTRL_CHANGE) { + if (ctrl & NAND_CLE) + this->IO_ADDR_W = (void __iomem *)NFCMMD; + else if (ctrl & NAND_ALE) + this->IO_ADDR_W = (void __iomem *)NFADDR; + else + this->IO_ADDR_W = (void __iomem *)NFDATA; + if (ctrl & NAND_NCE) + s3c_nand_select_chip(mtd, *(int *)this->priv); + else + s3c_nand_select_chip(mtd, -1); + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); +} + +/* + * Function for checking device ready pin + * Written by jsgood + */ +static int s3c_nand_device_ready(struct mtd_info *mtdinfo) +{ + return !!(readl(NFSTAT) & NFSTAT_RnB); +} + +#ifdef CFG_S3C_NAND_HWECC +/* + * This function is called before encoding ecc codes to ready ecc engine. + * Written by jsgood + */ +static void s3c_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + u_long nfcont, nfconf; + + /* + * The original driver used 4-bit ECC for "new" MLC chips, i.e., for + * those with non-zero ID[3][3:2], which anyway only holds for ST + * (Numonyx) chips + */ + nfconf = readl(NFCONF) & ~NFCONF_ECC_4BIT; + + writel(nfconf, NFCONF); + + /* Initialize & unlock */ + nfcont = readl(NFCONT); + nfcont |= NFCONT_INITECC; + nfcont &= ~NFCONT_MECCLOCK; + + if (mode == NAND_ECC_WRITE) + nfcont |= NFCONT_ECC_ENC; + else if (mode == NAND_ECC_READ) + nfcont &= ~NFCONT_ECC_ENC; + + writel(nfcont, NFCONT); +} + +/* + * This function is called immediately after encoding ecc codes. + * This function returns encoded ecc codes. + * Written by jsgood + */ +static int s3c_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + u_long nfcont, nfmecc0; + + /* Lock */ + nfcont = readl(NFCONT); + nfcont |= NFCONT_MECCLOCK; + writel(nfcont, NFCONT); + + nfmecc0 = readl(NFMECC0); + + ecc_code[0] = nfmecc0 & 0xff; + ecc_code[1] = (nfmecc0 >> 8) & 0xff; + ecc_code[2] = (nfmecc0 >> 16) & 0xff; + ecc_code[3] = (nfmecc0 >> 24) & 0xff; + + return 0; +} + +/* + * This function determines whether read data is good or not. + * If SLC, must write ecc codes to controller before reading status bit. + * If MLC, status bit is already set, so only reading is needed. + * If status bit is good, return 0. + * If correctable errors occured, do that. + * If uncorrectable errors occured, return -1. + * Written by jsgood + */ +static int s3c_nand_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + int ret = -1; + u_long nfestat0, nfmeccdata0, nfmeccdata1, err_byte_addr; + u_char err_type, repaired; + + /* SLC: Write ecc to compare */ + nfmeccdata0 = (calc_ecc[1] << 16) | calc_ecc[0]; + nfmeccdata1 = (calc_ecc[3] << 16) | calc_ecc[2]; + writel(nfmeccdata0, NFMECCDATA0); + writel(nfmeccdata1, NFMECCDATA1); + + /* Read ecc status */ + nfestat0 = readl(NFESTAT0); + err_type = nfestat0 & 0x3; + + switch (err_type) { + case 0: /* No error */ + ret = 0; + break; + + case 1: + /* + * 1 bit error (Correctable) + * (nfestat0 >> 7) & 0x7ff :error byte number + * (nfestat0 >> 4) & 0x7 :error bit number + */ + err_byte_addr = (nfestat0 >> 7) & 0x7ff; + repaired = dat[err_byte_addr] ^ (1 << ((nfestat0 >> 4) & 0x7)); + + printf("S3C NAND: 1 bit error detected at byte %ld. " + "Correcting from 0x%02x to 0x%02x...OK\n", + err_byte_addr, dat[err_byte_addr], repaired); + + dat[err_byte_addr] = repaired; + + ret = 1; + break; + + case 2: /* Multiple error */ + case 3: /* ECC area error */ + printf("S3C NAND: ECC uncorrectable error detected. " + "Not correctable.\n"); + ret = -1; + break; + } + + return ret; +} +#endif /* CFG_S3C_NAND_HWECC */ + +/* + * Board-specific NAND initialization. The following members of the + * argument are board-specific (per include/linux/mtd/nand.h): + * - IO_ADDR_R?: address to read the 8 I/O lines of the flash device + * - IO_ADDR_W?: address to write the 8 I/O lines of the flash device + * - hwcontrol: hardwarespecific function for accesing control-lines + * - dev_ready: hardwarespecific function for accesing device ready/busy line + * - enable_hwecc?: function to enable (reset) hardware ecc generator. Must + * only be provided if a hardware ECC is available + * - eccmode: mode of ecc, see defines + * - chip_delay: chip dependent delay for transfering data from array to + * read regs (tR) + * - options: various chip options. They can partly be set to inform + * nand_scan about special functionality. See the defines for further + * explanation + * Members with a "?" were not set in the merged testing-NAND branch, + * so they are not set here either. + */ +int board_nand_init(struct nand_chip *nand) +{ + static int chip_n; + + if (chip_n >= MAX_CHIPS) + return -ENODEV; + + NFCONT_REG = (NFCONT_REG & ~NFCONT_WP) | NFCONT_ENABLE | 0x6; + + nand->IO_ADDR_R = (void __iomem *)NFDATA; + nand->IO_ADDR_W = (void __iomem *)NFDATA; + nand->cmd_ctrl = s3c_nand_hwcontrol; + nand->dev_ready = s3c_nand_device_ready; + nand->select_chip = s3c_nand_select_chip; + nand->options = 0; +#ifdef CONFIG_NAND_SPL + nand->read_byte = nand_read_byte; + nand->write_buf = nand_write_buf; + nand->read_buf = nand_read_buf; +#endif + +#ifdef CFG_S3C_NAND_HWECC + nand->ecc.hwctl = s3c_nand_enable_hwecc; + nand->ecc.calculate = s3c_nand_calculate_ecc; + nand->ecc.correct = s3c_nand_correct_data; + + /* + * If you get more than 1 NAND-chip with different page-sizes on the + * board one day, it will get more complicated... + */ + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.size = CFG_NAND_ECCSIZE; + nand->ecc.bytes = CFG_NAND_ECCBYTES; +#else + nand->ecc.mode = NAND_ECC_SOFT; +#endif /* ! CFG_S3C_NAND_HWECC */ + + nand->priv = nand_cs + chip_n++; + + return 0; +} diff --git a/drivers/net/Makefile b/drivers/net/Makefile index bcf31cbe278..a084000a938 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -41,6 +41,8 @@ COBJS-$(CONFIG_DRIVER_KS8695ETH) += ks8695eth.o COBJS-$(CONFIG_DRIVER_LAN91C96) += lan91c96.o COBJS-$(CONFIG_MACB) += macb.o COBJS-$(CONFIG_MCFFEC) += mcffec.o +COBJS-$(CONFIG_MPC5xxx_FEC) += mpc5xxx_fec.o +COBJS-$(CONFIG_MPC512x_FEC) += mpc512x_fec.o COBJS-$(CONFIG_NATSEMI) += natsemi.o ifeq ($(CONFIG_DRIVER_NE2000),y) COBJS-y += ne2000.o diff --git a/drivers/net/mpc512x_fec.c b/drivers/net/mpc512x_fec.c new file mode 100644 index 00000000000..7caeeda6533 --- /dev/null +++ b/drivers/net/mpc512x_fec.c @@ -0,0 +1,794 @@ +/* + * (C) Copyright 2003-2007 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Derived from the MPC8xx FEC driver. + * Adapted for MPC512x by Grzegorz Bernacki <gjb@semihalf.com> + */ + +#include <common.h> +#include <mpc512x.h> +#include <malloc.h> +#include <net.h> +#include <miiphy.h> +#include "mpc512x_fec.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define DEBUG 0 + +#if defined(CONFIG_CMD_NET) && defined(CONFIG_NET_MULTI) && \ + defined(CONFIG_MPC512x_FEC) + +#if !(defined(CONFIG_MII) || defined(CONFIG_CMD_MII)) +#error "CONFIG_MII has to be defined!" +#endif + +#if (DEBUG & 0x40) +static uint32 local_crc32(char *string, unsigned int crc_value, int len); +#endif + +int fec512x_miiphy_read(char *devname, uint8 phyAddr, uint8 regAddr, uint16 * retVal); +int fec512x_miiphy_write(char *devname, uint8 phyAddr, uint8 regAddr, uint16 data); +int mpc512x_fec_init_phy(struct eth_device *dev, bd_t * bis); + +static uchar rx_buff[FEC_BUFFER_SIZE]; +static int rx_buff_idx = 0; + +/********************************************************************/ +#if (DEBUG & 0x2) +static void mpc512x_fec_phydump (char *devname) +{ + uint16 phyStatus, i; + uint8 phyAddr = CONFIG_PHY_ADDR; + uint8 reg_mask[] = { + /* regs to print: 0...8, 21,27,31 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, + }; + + for (i = 0; i < 32; i++) { + if (reg_mask[i]) { + miiphy_read (devname, phyAddr, i, &phyStatus); + printf ("Mii reg %d: 0x%04x\n", i, phyStatus); + } + } +} +#endif + +/********************************************************************/ +static int mpc512x_fec_bd_init (mpc512x_fec_priv *fec) +{ + int ix; + + /* + * Receive BDs init + */ + for (ix = 0; ix < FEC_RBD_NUM; ix++) { + fec->bdBase->rbd[ix].dataPointer = (uint32)&fec->bdBase->recv_frames[ix]; + fec->bdBase->rbd[ix].status = FEC_RBD_EMPTY; + fec->bdBase->rbd[ix].dataLength = 0; + } + + /* + * have the last RBD to close the ring + */ + fec->bdBase->rbd[ix - 1].status |= FEC_RBD_WRAP; + fec->rbdIndex = 0; + + /* + * Trasmit BDs init + */ + for (ix = 0; ix < FEC_TBD_NUM; ix++) { + fec->bdBase->tbd[ix].status = 0; + } + + /* + * Have the last TBD to close the ring + */ + fec->bdBase->tbd[ix - 1].status |= FEC_TBD_WRAP; + + /* + * Initialize some indices + */ + fec->tbdIndex = 0; + fec->usedTbdIndex = 0; + fec->cleanTbdNum = FEC_TBD_NUM; + + return 0; +} + +/********************************************************************/ +static void mpc512x_fec_rbd_clean (mpc512x_fec_priv *fec, volatile FEC_RBD * pRbd) +{ + /* + * Reset buffer descriptor as empty + */ + if ((fec->rbdIndex) == (FEC_RBD_NUM - 1)) + pRbd->status = (FEC_RBD_WRAP | FEC_RBD_EMPTY); + else + pRbd->status = FEC_RBD_EMPTY; + + pRbd->dataLength = 0; + + /* + * Increment BD count + */ + fec->rbdIndex = (fec->rbdIndex + 1) % FEC_RBD_NUM; + + /* + * Now, we have an empty RxBD, notify FEC + */ + fec->eth->r_des_active = 0x01000000; /* Descriptor polling active */ +} + +/********************************************************************/ +static void mpc512x_fec_tbd_scrub (mpc512x_fec_priv *fec) +{ + volatile FEC_TBD *pUsedTbd; + +#if (DEBUG & 0x1) + printf ("tbd_scrub: fec->cleanTbdNum = %d, fec->usedTbdIndex = %d\n", + fec->cleanTbdNum, fec->usedTbdIndex); +#endif + + /* + * process all the consumed TBDs + */ + while (fec->cleanTbdNum < FEC_TBD_NUM) { + pUsedTbd = &fec->bdBase->tbd[fec->usedTbdIndex]; + if (pUsedTbd->status & FEC_TBD_READY) { +#if (DEBUG & 0x20) + printf ("Cannot clean TBD %d, in use\n", fec->usedTbdIndex); +#endif + return; + } + + /* + * clean this buffer descriptor + */ + if (fec->usedTbdIndex == (FEC_TBD_NUM - 1)) + pUsedTbd->status = FEC_TBD_WRAP; + else + pUsedTbd->status = 0; + + /* + * update some indeces for a correct handling of the TBD ring + */ + fec->cleanTbdNum++; + fec->usedTbdIndex = (fec->usedTbdIndex + 1) % FEC_TBD_NUM; + } +} + +/********************************************************************/ +static void mpc512x_fec_set_hwaddr (mpc512x_fec_priv *fec, char *mac) +{ + uint8 currByte; /* byte for which to compute the CRC */ + int byte; /* loop - counter */ + int bit; /* loop - counter */ + uint32 crc = 0xffffffff; /* initial value */ + + /* + * The algorithm used is the following: + * we loop on each of the six bytes of the provided address, + * and we compute the CRC by left-shifting the previous + * value by one position, so that each bit in the current + * byte of the address may contribute the calculation. If + * the latter and the MSB in the CRC are different, then + * the CRC value so computed is also ex-ored with the + * "polynomium generator". The current byte of the address + * is also shifted right by one bit at each iteration. + * This is because the CRC generatore in hardware is implemented + * as a shift-register with as many ex-ores as the radixes + * in the polynomium. This suggests that we represent the + * polynomiumm itself as a 32-bit constant. + */ + for (byte = 0; byte < 6; byte++) { + currByte = mac[byte]; + for (bit = 0; bit < 8; bit++) { + if ((currByte & 0x01) ^ (crc & 0x01)) { + crc >>= 1; + crc = crc ^ 0xedb88320; + } else { + crc >>= 1; + } + currByte >>= 1; + } + } + + crc = crc >> 26; + + /* + * Set individual hash table register + */ + if (crc >= 32) { + fec->eth->iaddr1 = (1 << (crc - 32)); + fec->eth->iaddr2 = 0; + } else { + fec->eth->iaddr1 = 0; + fec->eth->iaddr2 = (1 << crc); + } + + /* + * Set physical address + */ + fec->eth->paddr1 = (mac[0] << 24) + (mac[1] << 16) + (mac[2] << 8) + mac[3]; + fec->eth->paddr2 = (mac[4] << 24) + (mac[5] << 16) + 0x8808; +} + +/********************************************************************/ +static int mpc512x_fec_init (struct eth_device *dev, bd_t * bis) +{ + mpc512x_fec_priv *fec = (mpc512x_fec_priv *)dev->priv; + +#if (DEBUG & 0x1) + printf ("mpc512x_fec_init... Begin\n"); +#endif + + /* Set interrupt mask register */ + fec->eth->imask = 0x00000000; + + /* Clear FEC-Lite interrupt event register(IEVENT) */ + fec->eth->ievent = 0xffffffff; + + /* Set transmit fifo watermark register(X_WMRK), default = 64 */ + fec->eth->x_wmrk = 0x0; + + /* Set Opcode/Pause Duration Register */ + fec->eth->op_pause = 0x00010020; + + /* Frame length=1522; MII mode */ + fec->eth->r_cntrl = (FEC_MAX_FRAME_LEN << 16) | 0x24; + + /* Half-duplex, heartbeat disabled */ + fec->eth->x_cntrl = 0x00000000; + + /* Enable MIB counters */ + fec->eth->mib_control = 0x0; + + /* Setup recv fifo start and buff size */ + fec->eth->r_fstart = 0x500; + fec->eth->r_buff_size = FEC_BUFFER_SIZE; + + /* Setup BD base addresses */ + fec->eth->r_des_start = (uint32)fec->bdBase->rbd; + fec->eth->x_des_start = (uint32)fec->bdBase->tbd; + + /* DMA Control */ + fec->eth->dma_control = 0xc0000000; + + /* Enable FEC */ + fec->eth->ecntrl |= 0x00000006; + + /* Initilize addresses and status words of BDs */ + mpc512x_fec_bd_init (fec); + + /* Descriptor polling active */ + fec->eth->r_des_active = 0x01000000; + +#if (DEBUG & 0x1) + printf("mpc512x_fec_init... Done \n"); +#endif + return 1; +} + +/********************************************************************/ +int mpc512x_fec_init_phy (struct eth_device *dev, bd_t * bis) +{ + mpc512x_fec_priv *fec = (mpc512x_fec_priv *)dev->priv; + const uint8 phyAddr = CONFIG_PHY_ADDR; /* Only one PHY */ + int timeout = 1; + uint16 phyStatus; + +#if (DEBUG & 0x1) + printf ("mpc512x_fec_init_phy... Begin\n"); +#endif + + /* + * Clear FEC-Lite interrupt event register(IEVENT) + */ + fec->eth->ievent = 0xffffffff; + + /* + * Set interrupt mask register + */ + fec->eth->imask = 0x00000000; + + if (fec->xcv_type != SEVENWIRE) { + /* + * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock + * and do not drop the Preamble. + */ + fec->eth->mii_speed = (((gd->ips_clk / 1000000) / 5) + 1) << 1; + + /* + * Reset PHY, then delay 300ns + */ + miiphy_write (dev->name, phyAddr, 0x0, 0x8000); + udelay (1000); + + if (fec->xcv_type == MII10) { + /* + * Force 10Base-T, FDX operation + */ +#if (DEBUG & 0x2) + printf ("Forcing 10 Mbps ethernet link... "); +#endif + miiphy_read (dev->name, phyAddr, 0x1, &phyStatus); + + miiphy_write (dev->name, phyAddr, 0x0, 0x0180); + + timeout = 20; + do { /* wait for link status to go down */ + udelay (10000); + if ((timeout--) == 0) { +#if (DEBUG & 0x2) + printf ("hmmm, should not have waited..."); +#endif + break; + } + miiphy_read (dev->name, phyAddr, 0x1, &phyStatus); +#if (DEBUG & 0x2) + printf ("="); +#endif + } while ((phyStatus & 0x0004)); /* !link up */ + + timeout = 1000; + do { /* wait for link status to come back up */ + udelay (10000); + if ((timeout--) == 0) { + printf ("failed. Link is down.\n"); + break; + } + miiphy_read (dev->name, phyAddr, 0x1, &phyStatus); +#if (DEBUG & 0x2) + printf ("+"); +#endif + } while (!(phyStatus & 0x0004)); /* !link up */ + +#if (DEBUG & 0x2) + printf ("done.\n"); +#endif + } else { /* MII100 */ + /* + * Set the auto-negotiation advertisement register bits + */ + miiphy_write (dev->name, phyAddr, 0x4, 0x01e1); + + /* + * Set MDIO bit 0.12 = 1(&& bit 0.9=1?) to enable auto-negotiation + */ + miiphy_write (dev->name, phyAddr, 0x0, 0x1200); + + /* + * Wait for AN completion + */ + timeout = 50000; + do { + udelay (1000); + + if ((timeout--) == 0) { +#if (DEBUG & 0x2) + printf ("PHY auto neg 0 failed...\n"); +#endif + return -1; + } + + if (miiphy_read (dev->name, phyAddr, 0x1, &phyStatus) != 0) { +#if (DEBUG & 0x2) + printf ("PHY auto neg 1 failed 0x%04x...\n", phyStatus); +#endif + return -1; + } + } while (!(phyStatus & 0x0004)); + +#if (DEBUG & 0x2) + printf ("PHY auto neg complete! \n"); +#endif + } + } + +#if (DEBUG & 0x2) + if (fec->xcv_type != SEVENWIRE) + mpc512x_fec_phydump (dev->name); +#endif + +#if (DEBUG & 0x1) + printf ("mpc512x_fec_init_phy... Done \n"); +#endif + return 1; +} + +/********************************************************************/ +static void mpc512x_fec_halt (struct eth_device *dev) +{ + mpc512x_fec_priv *fec = (mpc512x_fec_priv *)dev->priv; + int counter = 0xffff; + +#if (DEBUG & 0x2) + if (fec->xcv_type != SEVENWIRE) + mpc512x_fec_phydump (dev->name); +#endif + + /* + * mask FEC chip interrupts + */ + fec->eth->imask = 0; + + /* + * issue graceful stop command to the FEC transmitter if necessary + */ + fec->eth->x_cntrl |= 0x00000001; + + /* + * wait for graceful stop to register + */ + while ((counter--) && (!(fec->eth->ievent & 0x10000000))) ; + + /* + * Disable the Ethernet Controller + */ + fec->eth->ecntrl &= 0xfffffffd; + + /* + * Issue a reset command to the FEC chip + */ + fec->eth->ecntrl |= 0x1; + + /* + * wait at least 16 clock cycles + */ + udelay (10); +#if (DEBUG & 0x3) + printf ("Ethernet task stopped\n"); +#endif +} + +/********************************************************************/ + +static int mpc512x_fec_send (struct eth_device *dev, volatile void *eth_data, + int data_length) +{ + /* + * This routine transmits one frame. This routine only accepts + * 6-byte Ethernet addresses. + */ + mpc512x_fec_priv *fec = (mpc512x_fec_priv *)dev->priv; + volatile FEC_TBD *pTbd; + +#if (DEBUG & 0x20) + printf("tbd status: 0x%04x\n", fec->tbdBase[fec->tbdIndex].status); +#endif + + /* + * Clear Tx BD ring at first + */ + mpc512x_fec_tbd_scrub (fec); + + /* + * Check for valid length of data. + */ + if ((data_length > 1500) || (data_length <= 0)) { + return -1; + } + + /* + * Check the number of vacant TxBDs. + */ + if (fec->cleanTbdNum < 1) { +#if (DEBUG & 0x20) + printf ("No available TxBDs ...\n"); +#endif + return -1; + } + + /* + * Get the first TxBD to send the mac header + */ + pTbd = &fec->bdBase->tbd[fec->tbdIndex]; + pTbd->dataLength = data_length; + pTbd->dataPointer = (uint32)eth_data; + pTbd->status |= FEC_TBD_LAST | FEC_TBD_TC | FEC_TBD_READY; + fec->tbdIndex = (fec->tbdIndex + 1) % FEC_TBD_NUM; + + /* Activate transmit Buffer Descriptor polling */ + fec->eth->x_des_active = 0x01000000; /* Descriptor polling active */ + +#if (DEBUG & 0x8) + printf ( "+" ); +#endif + + fec->cleanTbdNum -= 1; + + /* + * wait until frame is sent . + */ + while (pTbd->status & FEC_TBD_READY) { + udelay (10); +#if (DEBUG & 0x8) + printf ("TDB status = %04x\n", pTbd->status); +#endif + } + + return 0; +} + + +/********************************************************************/ +static int mpc512x_fec_recv (struct eth_device *dev) +{ + /* + * This command pulls one frame from the card + */ + mpc512x_fec_priv *fec = (mpc512x_fec_priv *)dev->priv; + volatile FEC_RBD *pRbd = &fec->bdBase->rbd[fec->rbdIndex]; + unsigned long ievent; + int frame_length = 0; + +#if (DEBUG & 0x1) + printf ("mpc512x_fec_recv %d Start...\n", fec->rbdIndex); +#endif +#if (DEBUG & 0x8) + printf( "-" ); +#endif + + /* + * Check if any critical events have happened + */ + ievent = fec->eth->ievent; + fec->eth->ievent = ievent; + if (ievent & 0x20060000) { + /* BABT, Rx/Tx FIFO errors */ + mpc512x_fec_halt (dev); + mpc512x_fec_init (dev, NULL); + return 0; + } + if (ievent & 0x80000000) { + /* Heartbeat error */ + fec->eth->x_cntrl |= 0x00000001; + } + if (ievent & 0x10000000) { + /* Graceful stop complete */ + if (fec->eth->x_cntrl & 0x00000001) { + mpc512x_fec_halt (dev); + fec->eth->x_cntrl &= ~0x00000001; + mpc512x_fec_init (dev, NULL); + } + } + + if (!(pRbd->status & FEC_RBD_EMPTY)) { + if (!(pRbd->status & FEC_RBD_ERR) && + ((pRbd->dataLength - 4) > 14)) { + + /* + * Get buffer size + */ + if (pRbd->status & FEC_RBD_LAST) + frame_length = pRbd->dataLength - 4; + else + frame_length = pRbd->dataLength; +#if (DEBUG & 0x20) + { + int i; + printf ("recv data length 0x%08x data hdr: ", + pRbd->dataLength); + for (i = 0; i < 14; i++) + printf ("%x ", *((uint8*)pRbd->dataPointer + i)); + printf("\n"); + } +#endif + /* + * Fill the buffer and pass it to upper layers + */ + memcpy (&rx_buff[rx_buff_idx], (void*)pRbd->dataPointer, + frame_length - rx_buff_idx); + rx_buff_idx = frame_length; + + if (pRbd->status & FEC_RBD_LAST) { + NetReceive ((uchar*)rx_buff, frame_length); + rx_buff_idx = 0; + } + } + + /* + * Reset buffer descriptor as empty + */ + mpc512x_fec_rbd_clean (fec, pRbd); + } + + /* Try to fill Buffer Descriptors */ + fec->eth->r_des_active = 0x01000000; /* Descriptor polling active */ + return frame_length; +} + +/********************************************************************/ +int mpc512x_fec_initialize (bd_t * bis) +{ + mpc512x_fec_priv *fec; + struct eth_device *dev; + int i; + char *tmp, *end, env_enetaddr[6]; + void * bd; + + fec = (mpc512x_fec_priv *) malloc (sizeof(*fec)); + dev = (struct eth_device *) malloc (sizeof(*dev)); + memset (dev, 0, sizeof *dev); + + fec->eth = (ethernet_regs *) MPC512X_FEC; + +# ifndef CONFIG_FEC_10MBIT + fec->xcv_type = MII100; +# else + fec->xcv_type = MII10; +# endif + dev->priv = (void *)fec; + dev->iobase = MPC512X_FEC; + dev->init = mpc512x_fec_init; + dev->halt = mpc512x_fec_halt; + dev->send = mpc512x_fec_send; + dev->recv = mpc512x_fec_recv; + + sprintf (dev->name, "FEC ETHERNET"); + eth_register (dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register (dev->name, + fec512x_miiphy_read, fec512x_miiphy_write); +#endif + + /* Clean up space FEC's MIB and FIFO RAM ...*/ + memset ((void *) MPC512X_FEC + 0x200, 0x00, 0x400); + + /* + * Malloc space for BDs (must be quad word-aligned) + * this pointer is lost, so cannot be freed + */ + bd = malloc (sizeof(mpc512x_buff_descs) + 0x1f); + fec->bdBase = (mpc512x_buff_descs*)((uint32)bd & 0xfffffff0); + memset ((void *) bd, 0x00, sizeof(mpc512x_buff_descs) + 0x1f); + + /* + * Set interrupt mask register + */ + fec->eth->imask = 0x00000000; + + /* + * Clear FEC-Lite interrupt event register(IEVENT) + */ + fec->eth->ievent = 0xffffffff; + + /* + * Try to set the mac address now. The fec mac address is + * a garbage after reset. When not using fec for booting + * the Linux fec driver will try to work with this garbage. + */ + tmp = getenv ("ethaddr"); + if (tmp) { + for (i=0; i<6; i++) { + env_enetaddr[i] = tmp ? simple_strtoul (tmp, &end, 16) : 0; + if (tmp) + tmp = (*end) ? end+1 : end; + } + mpc512x_fec_set_hwaddr (fec, env_enetaddr); + fec->eth->gaddr1 = 0x00000000; + fec->eth->gaddr2 = 0x00000000; + } + + mpc512x_fec_init_phy (dev, bis); + + return 1; +} + +/* MII-interface related functions */ +/********************************************************************/ +int fec512x_miiphy_read (char *devname, uint8 phyAddr, uint8 regAddr, uint16 * retVal) +{ + ethernet_regs *eth = (ethernet_regs *) MPC512X_FEC; + uint32 reg; /* convenient holder for the PHY register */ + uint32 phy; /* convenient holder for the PHY */ + int timeout = 0xffff; + + /* + * reading from any PHY's register is done by properly + * programming the FEC's MII data register. + */ + reg = regAddr << FEC_MII_DATA_RA_SHIFT; + phy = phyAddr << FEC_MII_DATA_PA_SHIFT; + + eth->mii_data = (FEC_MII_DATA_ST | FEC_MII_DATA_OP_RD | FEC_MII_DATA_TA | phy | reg); + + /* + * wait for the related interrupt + */ + while ((timeout--) && (!(eth->ievent & 0x00800000))) ; + + if (timeout == 0) { +#if (DEBUG & 0x2) + printf ("Read MDIO failed...\n"); +#endif + return -1; + } + + /* + * clear mii interrupt bit + */ + eth->ievent = 0x00800000; + + /* + * it's now safe to read the PHY's register + */ + *retVal = (uint16) eth->mii_data; + + return 0; +} + +/********************************************************************/ +int fec512x_miiphy_write (char *devname, uint8 phyAddr, uint8 regAddr, uint16 data) +{ + ethernet_regs *eth = (ethernet_regs *) MPC512X_FEC; + uint32 reg; /* convenient holder for the PHY register */ + uint32 phy; /* convenient holder for the PHY */ + int timeout = 0xffff; + + reg = regAddr << FEC_MII_DATA_RA_SHIFT; + phy = phyAddr << FEC_MII_DATA_PA_SHIFT; + + eth->mii_data = (FEC_MII_DATA_ST | FEC_MII_DATA_OP_WR | + FEC_MII_DATA_TA | phy | reg | data); + + /* + * wait for the MII interrupt + */ + while ((timeout--) && (!(eth->ievent & 0x00800000))) ; + + if (timeout == 0) { +#if (DEBUG & 0x2) + printf ("Write MDIO failed...\n"); +#endif + return -1; + } + + /* + * clear MII interrupt bit + */ + eth->ievent = 0x00800000; + + return 0; +} + +#if (DEBUG & 0x40) +static uint32 local_crc32 (char *string, unsigned int crc_value, int len) +{ + int i; + char c; + unsigned int crc, count; + + /* + * crc32 algorithm + */ + /* + * crc = 0xffffffff; * The initialized value should be 0xffffffff + */ + crc = crc_value; + + for (i = len; --i >= 0;) { + c = *string++; + for (count = 0; count < 8; count++) { + if ((c & 0x01) ^ (crc & 0x01)) { + crc >>= 1; + crc = crc ^ 0xedb88320; + } else { + crc >>= 1; + } + c >>= 1; + } + } + + /* + * In big endian system, do byte swaping for crc value + */ + /**/ return crc; +} +#endif /* DEBUG */ + +#endif /* CONFIG_MPC512x_FEC */ diff --git a/drivers/net/mpc512x_fec.h b/drivers/net/mpc512x_fec.h new file mode 100644 index 00000000000..9c385028708 --- /dev/null +++ b/drivers/net/mpc512x_fec.h @@ -0,0 +1,227 @@ +/* + * (C) Copyright 2003 - 2007 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Derived from the MPC8xx driver's header file. + */ + +#ifndef __MPC512X_FEC_H +#define __MPC512X_FEC_H + +#include <common.h> +#include <mpc512x.h> + +typedef unsigned long uint32; +typedef unsigned short uint16; +typedef unsigned char uint8; + +typedef struct ethernet_register_set { + +/* [10:2]addr = 00 */ + +/* Control and status Registers (offset 000-1FF) */ + + volatile uint32 fec_id; /* MBAR_ETH + 0x000 */ + volatile uint32 ievent; /* MBAR_ETH + 0x004 */ + volatile uint32 imask; /* MBAR_ETH + 0x008 */ + + volatile uint32 RES0[1]; /* MBAR_ETH + 0x00C */ + volatile uint32 r_des_active; /* MBAR_ETH + 0x010 */ + volatile uint32 x_des_active; /* MBAR_ETH + 0x014 */ + + volatile uint32 RES1[3]; /* MBAR_ETH + 0x018-020 */ + volatile uint32 ecntrl; /* MBAR_ETH + 0x024 */ + + volatile uint32 RES2[6]; /* MBAR_ETH + 0x028-03C */ + volatile uint32 mii_data; /* MBAR_ETH + 0x040 */ + volatile uint32 mii_speed; /* MBAR_ETH + 0x044 */ + + volatile uint32 RES3[7]; /* MBAR_ETH + 0x048-060 */ + volatile uint32 mib_control; /* MBAR_ETH + 0x064 */ + + volatile uint32 RES4[7]; /* MBAR_ETH + 0x068-80 */ + volatile uint32 r_cntrl; /* MBAR_ETH + 0x084 */ + volatile uint32 r_hash; /* MBAR_ETH + 0x088 */ + + volatile uint32 RES5[14]; /* MBAR_ETH + 0x08c-0C0 */ + volatile uint32 x_cntrl; /* MBAR_ETH + 0x0C4 */ + + volatile uint32 RES6[7]; /* MBAR_ETH + 0x0C8-0E0 */ + volatile uint32 paddr1; /* MBAR_ETH + 0x0E4 */ + volatile uint32 paddr2; /* MBAR_ETH + 0x0E8 */ + volatile uint32 op_pause; /* MBAR_ETH + 0x0EC */ + + volatile uint32 RES7[10]; /* MBAR_ETH + 0x0F0-114 */ + volatile uint32 iaddr1; /* MBAR_ETH + 0x118 */ + volatile uint32 iaddr2; /* MBAR_ETH + 0x11C */ + volatile uint32 gaddr1; /* MBAR_ETH + 0x120 */ + volatile uint32 gaddr2; /* MBAR_ETH + 0x124 */ + + volatile uint32 RES8[6]; /* MBAR_ETH + 0x128-13C */ + volatile uint32 fifo_id; /* MBAR_ETH + 0x140 */ + volatile uint32 x_wmrk; /* MBAR_ETH + 0x144 */ + volatile uint32 RES9[1]; /* MBAR_ETH + 0x148 */ + volatile uint32 r_bound; /* MBAR_ETH + 0x14C */ + volatile uint32 r_fstart; /* MBAR_ETH + 0x150 */ + + volatile uint32 RES10[11]; /* MBAR_ETH + 0x154-17C */ + volatile uint32 r_des_start; /* MBAR_ETH + 0x180 */ + volatile uint32 x_des_start; /* MBAR_ETH + 0x184 */ + volatile uint32 r_buff_size; /* MBAR_ETH + 0x188 */ + volatile uint32 RES11[26]; /* MBAR_ETH + 0x18C-1F0 */ + volatile uint32 dma_control; /* MBAR_ETH + 0x1F4 */ + volatile uint32 RES12[2]; /* MBAR_ETH + 0x1F8-1FC */ + +/* MIB COUNTERS (Offset 200-2FF) */ + + volatile uint32 rmon_t_drop; /* MBAR_ETH + 0x200 */ + volatile uint32 rmon_t_packets; /* MBAR_ETH + 0x204 */ + volatile uint32 rmon_t_bc_pkt; /* MBAR_ETH + 0x208 */ + volatile uint32 rmon_t_mc_pkt; /* MBAR_ETH + 0x20C */ + volatile uint32 rmon_t_crc_align; /* MBAR_ETH + 0x210 */ + volatile uint32 rmon_t_undersize; /* MBAR_ETH + 0x214 */ + volatile uint32 rmon_t_oversize; /* MBAR_ETH + 0x218 */ + volatile uint32 rmon_t_frag; /* MBAR_ETH + 0x21C */ + volatile uint32 rmon_t_jab; /* MBAR_ETH + 0x220 */ + volatile uint32 rmon_t_col; /* MBAR_ETH + 0x224 */ + volatile uint32 rmon_t_p64; /* MBAR_ETH + 0x228 */ + volatile uint32 rmon_t_p65to127; /* MBAR_ETH + 0x22C */ + volatile uint32 rmon_t_p128to255; /* MBAR_ETH + 0x230 */ + volatile uint32 rmon_t_p256to511; /* MBAR_ETH + 0x234 */ + volatile uint32 rmon_t_p512to1023; /* MBAR_ETH + 0x238 */ + volatile uint32 rmon_t_p1024to2047; /* MBAR_ETH + 0x23C */ + volatile uint32 rmon_t_p_gte2048; /* MBAR_ETH + 0x240 */ + volatile uint32 rmon_t_octets; /* MBAR_ETH + 0x244 */ + volatile uint32 ieee_t_drop; /* MBAR_ETH + 0x248 */ + volatile uint32 ieee_t_frame_ok; /* MBAR_ETH + 0x24C */ + volatile uint32 ieee_t_1col; /* MBAR_ETH + 0x250 */ + volatile uint32 ieee_t_mcol; /* MBAR_ETH + 0x254 */ + volatile uint32 ieee_t_def; /* MBAR_ETH + 0x258 */ + volatile uint32 ieee_t_lcol; /* MBAR_ETH + 0x25C */ + volatile uint32 ieee_t_excol; /* MBAR_ETH + 0x260 */ + volatile uint32 ieee_t_macerr; /* MBAR_ETH + 0x264 */ + volatile uint32 ieee_t_cserr; /* MBAR_ETH + 0x268 */ + volatile uint32 ieee_t_sqe; /* MBAR_ETH + 0x26C */ + volatile uint32 t_fdxfc; /* MBAR_ETH + 0x270 */ + volatile uint32 ieee_t_octets_ok; /* MBAR_ETH + 0x274 */ + + volatile uint32 RES13[2]; /* MBAR_ETH + 0x278-27C */ + volatile uint32 rmon_r_drop; /* MBAR_ETH + 0x280 */ + volatile uint32 rmon_r_packets; /* MBAR_ETH + 0x284 */ + volatile uint32 rmon_r_bc_pkt; /* MBAR_ETH + 0x288 */ + volatile uint32 rmon_r_mc_pkt; /* MBAR_ETH + 0x28C */ + volatile uint32 rmon_r_crc_align; /* MBAR_ETH + 0x290 */ + volatile uint32 rmon_r_undersize; /* MBAR_ETH + 0x294 */ + volatile uint32 rmon_r_oversize; /* MBAR_ETH + 0x298 */ + volatile uint32 rmon_r_frag; /* MBAR_ETH + 0x29C */ + volatile uint32 rmon_r_jab; /* MBAR_ETH + 0x2A0 */ + + volatile uint32 rmon_r_resvd_0; /* MBAR_ETH + 0x2A4 */ + + volatile uint32 rmon_r_p64; /* MBAR_ETH + 0x2A8 */ + volatile uint32 rmon_r_p65to127; /* MBAR_ETH + 0x2AC */ + volatile uint32 rmon_r_p128to255; /* MBAR_ETH + 0x2B0 */ + volatile uint32 rmon_r_p256to511; /* MBAR_ETH + 0x2B4 */ + volatile uint32 rmon_r_p512to1023; /* MBAR_ETH + 0x2B8 */ + volatile uint32 rmon_r_p1024to2047; /* MBAR_ETH + 0x2BC */ + volatile uint32 rmon_r_p_gte2048; /* MBAR_ETH + 0x2C0 */ + volatile uint32 rmon_r_octets; /* MBAR_ETH + 0x2C4 */ + volatile uint32 ieee_r_drop; /* MBAR_ETH + 0x2C8 */ + volatile uint32 ieee_r_frame_ok; /* MBAR_ETH + 0x2CC */ + volatile uint32 ieee_r_crc; /* MBAR_ETH + 0x2D0 */ + volatile uint32 ieee_r_align; /* MBAR_ETH + 0x2D4 */ + volatile uint32 r_macerr; /* MBAR_ETH + 0x2D8 */ + volatile uint32 r_fdxfc; /* MBAR_ETH + 0x2DC */ + volatile uint32 ieee_r_octets_ok; /* MBAR_ETH + 0x2E0 */ + + volatile uint32 RES14[6]; /* MBAR_ETH + 0x2E4-2FC */ + + volatile uint32 RES15[64]; /* MBAR_ETH + 0x300-3FF */ +} ethernet_regs; + +/* Receive & Transmit Buffer Descriptor definitions */ +typedef struct BufferDescriptor { + uint16 status; + uint16 dataLength; + uint32 dataPointer; +} FEC_RBD; + +typedef struct { + uint16 status; + uint16 dataLength; + uint32 dataPointer; +} FEC_TBD; + +/* private structure */ +typedef enum { + SEVENWIRE, /* 7-wire */ + MII10, /* MII 10Mbps */ + MII100 /* MII 100Mbps */ +} xceiver_type; + +/* BD Numer definitions */ +#define FEC_TBD_NUM 48 /* The user can adjust this value */ +#define FEC_RBD_NUM 32 /* The user can adjust this value */ + +/* packet size limit */ +#define FEC_MAX_FRAME_LEN 1522 /* recommended default value */ + +/* Buffer size must be evenly divisible by 16 */ +#define FEC_BUFFER_SIZE ((FEC_MAX_FRAME_LEN + 0x10) & (~0xf)) + +typedef struct { + uint8 frame[FEC_BUFFER_SIZE]; +} mpc512x_frame; + +typedef struct { + FEC_RBD rbd[FEC_RBD_NUM]; /* RBD ring */ + FEC_TBD tbd[FEC_TBD_NUM]; /* TBD ring */ + mpc512x_frame recv_frames[FEC_RBD_NUM]; /* receive buff */ +} mpc512x_buff_descs; + +typedef struct { + ethernet_regs *eth; + xceiver_type xcv_type; /* transceiver type */ + mpc512x_buff_descs *bdBase; /* BD rings and recv buffer */ + uint16 rbdIndex; /* next receive BD to read */ + uint16 tbdIndex; /* next transmit BD to send */ + uint16 usedTbdIndex; /* next transmit BD to clean */ + uint16 cleanTbdNum; /* the number of available transmit BDs */ +} mpc512x_fec_priv; + +/* RBD bits definitions */ +#define FEC_RBD_EMPTY 0x8000 /* Buffer is empty */ +#define FEC_RBD_WRAP 0x2000 /* Last BD in ring */ +#define FEC_RBD_LAST 0x0800 /* Buffer is last in frame(useless) */ +#define FEC_RBD_MISS 0x0100 /* Miss bit for prom mode */ +#define FEC_RBD_BC 0x0080 /* The received frame is broadcast frame */ +#define FEC_RBD_MC 0x0040 /* The received frame is multicast frame */ +#define FEC_RBD_LG 0x0020 /* Frame length violation */ +#define FEC_RBD_NO 0x0010 /* Nonoctet align frame */ +#define FEC_RBD_SH 0x0008 /* Short frame */ +#define FEC_RBD_CR 0x0004 /* CRC error */ +#define FEC_RBD_OV 0x0002 /* Receive FIFO overrun */ +#define FEC_RBD_TR 0x0001 /* Frame is truncated */ +#define FEC_RBD_ERR (FEC_RBD_LG | FEC_RBD_NO | FEC_RBD_CR | \ + FEC_RBD_OV | FEC_RBD_TR) + +/* TBD bits definitions */ +#define FEC_TBD_READY 0x8000 /* Buffer is ready */ +#define FEC_TBD_WRAP 0x2000 /* Last BD in ring */ +#define FEC_TBD_LAST 0x0800 /* Buffer is last in frame */ +#define FEC_TBD_TC 0x0400 /* Transmit the CRC */ +#define FEC_TBD_ABC 0x0200 /* Append bad CRC */ + +/* MII-related definitios */ +#define FEC_MII_DATA_ST 0x40000000 /* Start of frame delimiter */ +#define FEC_MII_DATA_OP_RD 0x20000000 /* Perform a read operation */ +#define FEC_MII_DATA_OP_WR 0x10000000 /* Perform a write operation */ +#define FEC_MII_DATA_PA_MSK 0x0f800000 /* PHY Address field mask */ +#define FEC_MII_DATA_RA_MSK 0x007c0000 /* PHY Register field mask */ +#define FEC_MII_DATA_TA 0x00020000 /* Turnaround */ +#define FEC_MII_DATA_DATAMSK 0x0000ffff /* PHY data field */ + +#define FEC_MII_DATA_RA_SHIFT 18 /* MII Register address bits */ +#define FEC_MII_DATA_PA_SHIFT 23 /* MII PHY address bits */ + +#endif /* __MPC512X_FEC_H */ diff --git a/drivers/net/mpc5xxx_fec.c b/drivers/net/mpc5xxx_fec.c new file mode 100644 index 00000000000..3d3eb8bae83 --- /dev/null +++ b/drivers/net/mpc5xxx_fec.c @@ -0,0 +1,1067 @@ +/* + * (C) Copyright 2003-2005 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This file is based on mpc4200fec.c, + * (C) Copyright Motorola, Inc., 2000 + */ + +#include <common.h> +#include <mpc5xxx.h> +#include <mpc5xxx_sdma.h> +#include <malloc.h> +#include <net.h> +#include <miiphy.h> +#include "mpc5xxx_fec.h" + +DECLARE_GLOBAL_DATA_PTR; + +/* #define DEBUG 0x28 */ + +#if defined(CONFIG_CMD_NET) && defined(CONFIG_NET_MULTI) && \ + defined(CONFIG_MPC5xxx_FEC) + +#if !(defined(CONFIG_MII) || defined(CONFIG_CMD_MII)) +#error "CONFIG_MII has to be defined!" +#endif + +#if (DEBUG & 0x60) +static void tfifo_print(char *devname, mpc5xxx_fec_priv *fec); +static void rfifo_print(char *devname, mpc5xxx_fec_priv *fec); +#endif /* DEBUG */ + +#if (DEBUG & 0x40) +static uint32 local_crc32(char *string, unsigned int crc_value, int len); +#endif + +typedef struct { + uint8 data[1500]; /* actual data */ + int length; /* actual length */ + int used; /* buffer in use or not */ + uint8 head[16]; /* MAC header(6 + 6 + 2) + 2(aligned) */ +} NBUF; + +int fec5xxx_miiphy_read(char *devname, uint8 phyAddr, uint8 regAddr, uint16 * retVal); +int fec5xxx_miiphy_write(char *devname, uint8 phyAddr, uint8 regAddr, uint16 data); + +/********************************************************************/ +#if (DEBUG & 0x2) +static void mpc5xxx_fec_phydump (char *devname) +{ + uint16 phyStatus, i; + uint8 phyAddr = CONFIG_PHY_ADDR; + uint8 reg_mask[] = { +#if CONFIG_PHY_TYPE == 0x79c874 /* AMD Am79C874 */ + /* regs to print: 0...7, 16...19, 21, 23, 24 */ + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, +#else + /* regs to print: 0...8, 16...20 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +#endif + }; + + for (i = 0; i < 32; i++) { + if (reg_mask[i]) { + miiphy_read(devname, phyAddr, i, &phyStatus); + printf("Mii reg %d: 0x%04x\n", i, phyStatus); + } + } +} +#endif + +/********************************************************************/ +static int mpc5xxx_fec_rbd_init(mpc5xxx_fec_priv *fec) +{ + int ix; + char *data; + static int once = 0; + + for (ix = 0; ix < FEC_RBD_NUM; ix++) { + if (!once) { + data = (char *)malloc(FEC_MAX_PKT_SIZE); + if (data == NULL) { + printf ("RBD INIT FAILED\n"); + return -1; + } + fec->rbdBase[ix].dataPointer = (uint32)data; + } + fec->rbdBase[ix].status = FEC_RBD_EMPTY; + fec->rbdBase[ix].dataLength = 0; + } + once ++; + + /* + * have the last RBD to close the ring + */ + fec->rbdBase[ix - 1].status |= FEC_RBD_WRAP; + fec->rbdIndex = 0; + + return 0; +} + +/********************************************************************/ +static void mpc5xxx_fec_tbd_init(mpc5xxx_fec_priv *fec) +{ + int ix; + + for (ix = 0; ix < FEC_TBD_NUM; ix++) { + fec->tbdBase[ix].status = 0; + } + + /* + * Have the last TBD to close the ring + */ + fec->tbdBase[ix - 1].status |= FEC_TBD_WRAP; + + /* + * Initialize some indices + */ + fec->tbdIndex = 0; + fec->usedTbdIndex = 0; + fec->cleanTbdNum = FEC_TBD_NUM; +} + +/********************************************************************/ +static void mpc5xxx_fec_rbd_clean(mpc5xxx_fec_priv *fec, volatile FEC_RBD * pRbd) +{ + /* + * Reset buffer descriptor as empty + */ + if ((fec->rbdIndex) == (FEC_RBD_NUM - 1)) + pRbd->status = (FEC_RBD_WRAP | FEC_RBD_EMPTY); + else + pRbd->status = FEC_RBD_EMPTY; + + pRbd->dataLength = 0; + + /* + * Now, we have an empty RxBD, restart the SmartDMA receive task + */ + SDMA_TASK_ENABLE(FEC_RECV_TASK_NO); + + /* + * Increment BD count + */ + fec->rbdIndex = (fec->rbdIndex + 1) % FEC_RBD_NUM; +} + +/********************************************************************/ +static void mpc5xxx_fec_tbd_scrub(mpc5xxx_fec_priv *fec) +{ + volatile FEC_TBD *pUsedTbd; + +#if (DEBUG & 0x1) + printf ("tbd_scrub: fec->cleanTbdNum = %d, fec->usedTbdIndex = %d\n", + fec->cleanTbdNum, fec->usedTbdIndex); +#endif + + /* + * process all the consumed TBDs + */ + while (fec->cleanTbdNum < FEC_TBD_NUM) { + pUsedTbd = &fec->tbdBase[fec->usedTbdIndex]; + if (pUsedTbd->status & FEC_TBD_READY) { +#if (DEBUG & 0x20) + printf("Cannot clean TBD %d, in use\n", fec->cleanTbdNum); +#endif + return; + } + + /* + * clean this buffer descriptor + */ + if (fec->usedTbdIndex == (FEC_TBD_NUM - 1)) + pUsedTbd->status = FEC_TBD_WRAP; + else + pUsedTbd->status = 0; + + /* + * update some indeces for a correct handling of the TBD ring + */ + fec->cleanTbdNum++; + fec->usedTbdIndex = (fec->usedTbdIndex + 1) % FEC_TBD_NUM; + } +} + +/********************************************************************/ +static void mpc5xxx_fec_set_hwaddr(mpc5xxx_fec_priv *fec, char *mac) +{ + uint8 currByte; /* byte for which to compute the CRC */ + int byte; /* loop - counter */ + int bit; /* loop - counter */ + uint32 crc = 0xffffffff; /* initial value */ + + /* + * The algorithm used is the following: + * we loop on each of the six bytes of the provided address, + * and we compute the CRC by left-shifting the previous + * value by one position, so that each bit in the current + * byte of the address may contribute the calculation. If + * the latter and the MSB in the CRC are different, then + * the CRC value so computed is also ex-ored with the + * "polynomium generator". The current byte of the address + * is also shifted right by one bit at each iteration. + * This is because the CRC generatore in hardware is implemented + * as a shift-register with as many ex-ores as the radixes + * in the polynomium. This suggests that we represent the + * polynomiumm itself as a 32-bit constant. + */ + for (byte = 0; byte < 6; byte++) { + currByte = mac[byte]; + for (bit = 0; bit < 8; bit++) { + if ((currByte & 0x01) ^ (crc & 0x01)) { + crc >>= 1; + crc = crc ^ 0xedb88320; + } else { + crc >>= 1; + } + currByte >>= 1; + } + } + + crc = crc >> 26; + + /* + * Set individual hash table register + */ + if (crc >= 32) { + fec->eth->iaddr1 = (1 << (crc - 32)); + fec->eth->iaddr2 = 0; + } else { + fec->eth->iaddr1 = 0; + fec->eth->iaddr2 = (1 << crc); + } + + /* + * Set physical address + */ + fec->eth->paddr1 = (mac[0] << 24) + (mac[1] << 16) + (mac[2] << 8) + mac[3]; + fec->eth->paddr2 = (mac[4] << 24) + (mac[5] << 16) + 0x8808; +} + +/********************************************************************/ +static int mpc5xxx_fec_init(struct eth_device *dev, bd_t * bis) +{ + mpc5xxx_fec_priv *fec = (mpc5xxx_fec_priv *)dev->priv; + struct mpc5xxx_sdma *sdma = (struct mpc5xxx_sdma *)MPC5XXX_SDMA; + +#if (DEBUG & 0x1) + printf ("mpc5xxx_fec_init... Begin\n"); +#endif + + /* + * Initialize RxBD/TxBD rings + */ + mpc5xxx_fec_rbd_init(fec); + mpc5xxx_fec_tbd_init(fec); + + /* + * Clear FEC-Lite interrupt event register(IEVENT) + */ + fec->eth->ievent = 0xffffffff; + + /* + * Set interrupt mask register + */ + fec->eth->imask = 0x00000000; + + /* + * Set FEC-Lite receive control register(R_CNTRL): + */ + if (fec->xcv_type == SEVENWIRE) { + /* + * Frame length=1518; 7-wire mode + */ + fec->eth->r_cntrl = 0x05ee0020; /*0x05ee0000;FIXME */ + } else { + /* + * Frame length=1518; MII mode; + */ + fec->eth->r_cntrl = 0x05ee0024; /*0x05ee0004;FIXME */ + } + + fec->eth->x_cntrl = 0x00000000; /* half-duplex, heartbeat disabled */ + if (fec->xcv_type != SEVENWIRE) { + /* + * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock + * and do not drop the Preamble. + */ + fec->eth->mii_speed = (((gd->ipb_clk >> 20) / 5) << 1); /* No MII for 7-wire mode */ + } + + /* + * Set Opcode/Pause Duration Register + */ + fec->eth->op_pause = 0x00010020; /*FIXME 0xffff0020; */ + + /* + * Set Rx FIFO alarm and granularity value + */ + fec->eth->rfifo_cntrl = 0x0c000000 + | (fec->eth->rfifo_cntrl & ~0x0f000000); + fec->eth->rfifo_alarm = 0x0000030c; +#if (DEBUG & 0x22) + if (fec->eth->rfifo_status & 0x00700000 ) { + printf("mpc5xxx_fec_init() RFIFO error\n"); + } +#endif + + /* + * Set Tx FIFO granularity value + */ + fec->eth->tfifo_cntrl = 0x0c000000 + | (fec->eth->tfifo_cntrl & ~0x0f000000); +#if (DEBUG & 0x2) + printf("tfifo_status: 0x%08x\n", fec->eth->tfifo_status); + printf("tfifo_alarm: 0x%08x\n", fec->eth->tfifo_alarm); +#endif + + /* + * Set transmit fifo watermark register(X_WMRK), default = 64 + */ + fec->eth->tfifo_alarm = 0x00000080; + fec->eth->x_wmrk = 0x2; + + /* + * Set individual address filter for unicast address + * and set physical address registers. + */ + mpc5xxx_fec_set_hwaddr(fec, (char *)dev->enetaddr); + + /* + * Set multicast address filter + */ + fec->eth->gaddr1 = 0x00000000; + fec->eth->gaddr2 = 0x00000000; + + /* + * Turn ON cheater FSM: ???? + */ + fec->eth->xmit_fsm = 0x03000000; + +#if defined(CONFIG_MPC5200) + /* + * Turn off COMM bus prefetch in the MGT5200 BestComm. It doesn't + * work w/ the current receive task. + */ + sdma->PtdCntrl |= 0x00000001; +#endif + + /* + * Set priority of different initiators + */ + sdma->IPR0 = 7; /* always */ + sdma->IPR3 = 6; /* Eth RX */ + sdma->IPR4 = 5; /* Eth Tx */ + + /* + * Clear SmartDMA task interrupt pending bits + */ + SDMA_CLEAR_IEVENT(FEC_RECV_TASK_NO); + + /* + * Initialize SmartDMA parameters stored in SRAM + */ + *(volatile int *)FEC_TBD_BASE = (int)fec->tbdBase; + *(volatile int *)FEC_RBD_BASE = (int)fec->rbdBase; + *(volatile int *)FEC_TBD_NEXT = (int)fec->tbdBase; + *(volatile int *)FEC_RBD_NEXT = (int)fec->rbdBase; + + /* + * Enable FEC-Lite controller + */ + fec->eth->ecntrl |= 0x00000006; + +#if (DEBUG & 0x2) + if (fec->xcv_type != SEVENWIRE) + mpc5xxx_fec_phydump (dev->name); +#endif + + /* + * Enable SmartDMA receive task + */ + SDMA_TASK_ENABLE(FEC_RECV_TASK_NO); + +#if (DEBUG & 0x1) + printf("mpc5xxx_fec_init... Done \n"); +#endif + + return 1; +} + +/********************************************************************/ +static int mpc5xxx_fec_init_phy(struct eth_device *dev, bd_t * bis) +{ + mpc5xxx_fec_priv *fec = (mpc5xxx_fec_priv *)dev->priv; + const uint8 phyAddr = CONFIG_PHY_ADDR; /* Only one PHY */ + +#if (DEBUG & 0x1) + printf ("mpc5xxx_fec_init_phy... Begin\n"); +#endif + + /* + * Initialize GPIO pins + */ + if (fec->xcv_type == SEVENWIRE) { + /* 10MBit with 7-wire operation */ +#if defined(CONFIG_TOTAL5200) + /* 7-wire and USB2 on Ethernet */ + *(vu_long *)MPC5XXX_GPS_PORT_CONFIG |= 0x00030000; +#else /* !CONFIG_TOTAL5200 */ + /* 7-wire only */ + *(vu_long *)MPC5XXX_GPS_PORT_CONFIG |= 0x00020000; +#endif /* CONFIG_TOTAL5200 */ + } else { + /* 100MBit with MD operation */ + *(vu_long *)MPC5XXX_GPS_PORT_CONFIG |= 0x00050000; + } + + /* + * Clear FEC-Lite interrupt event register(IEVENT) + */ + fec->eth->ievent = 0xffffffff; + + /* + * Set interrupt mask register + */ + fec->eth->imask = 0x00000000; + +/* + * In original Promess-provided code PHY initialization is disabled with the + * following comment: "Phy initialization is DISABLED for now. There was a + * problem with running 100 Mbps on PRO board". Thus we temporarily disable + * PHY initialization for the Motion-PRO board, until a proper fix is found. + */ + + if (fec->xcv_type != SEVENWIRE) { + /* + * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock + * and do not drop the Preamble. + */ + fec->eth->mii_speed = (((gd->ipb_clk >> 20) / 5) << 1); /* No MII for 7-wire mode */ + } + + if (fec->xcv_type != SEVENWIRE) { + /* + * Initialize PHY(LXT971A): + * + * Generally, on power up, the LXT971A reads its configuration + * pins to check for forced operation, If not cofigured for + * forced operation, it uses auto-negotiation/parallel detection + * to automatically determine line operating conditions. + * If the PHY device on the other side of the link supports + * auto-negotiation, the LXT971A auto-negotiates with it + * using Fast Link Pulse(FLP) Bursts. If the PHY partner does not + * support auto-negotiation, the LXT971A automatically detects + * the presence of either link pulses(10Mbps PHY) or Idle + * symbols(100Mbps) and sets its operating conditions accordingly. + * + * When auto-negotiation is controlled by software, the following + * steps are recommended. + * + * Note: + * The physical address is dependent on hardware configuration. + * + */ + int timeout = 1; + uint16 phyStatus; + + /* + * Reset PHY, then delay 300ns + */ + miiphy_write(dev->name, phyAddr, 0x0, 0x8000); + udelay(1000); + +#if defined(CONFIG_UC101) || defined(CONFIG_MUCMC52) + /* Set the LED configuration Register for the UC101 + and MUCMC52 Board */ + miiphy_write(dev->name, phyAddr, 0x14, 0x4122); +#endif + if (fec->xcv_type == MII10) { + /* + * Force 10Base-T, FDX operation + */ +#if (DEBUG & 0x2) + printf("Forcing 10 Mbps ethernet link... "); +#endif + miiphy_read(dev->name, phyAddr, 0x1, &phyStatus); + /* + miiphy_write(dev->name, fec, phyAddr, 0x0, 0x0100); + */ + miiphy_write(dev->name, phyAddr, 0x0, 0x0180); + + timeout = 20; + do { /* wait for link status to go down */ + udelay(10000); + if ((timeout--) == 0) { +#if (DEBUG & 0x2) + printf("hmmm, should not have waited..."); +#endif + break; + } + miiphy_read(dev->name, phyAddr, 0x1, &phyStatus); +#if (DEBUG & 0x2) + printf("="); +#endif + } while ((phyStatus & 0x0004)); /* !link up */ + + timeout = 1000; + do { /* wait for link status to come back up */ + udelay(10000); + if ((timeout--) == 0) { + printf("failed. Link is down.\n"); + break; + } + miiphy_read(dev->name, phyAddr, 0x1, &phyStatus); +#if (DEBUG & 0x2) + printf("+"); +#endif + } while (!(phyStatus & 0x0004)); /* !link up */ + +#if (DEBUG & 0x2) + printf ("done.\n"); +#endif + } else { /* MII100 */ + /* + * Set the auto-negotiation advertisement register bits + */ + miiphy_write(dev->name, phyAddr, 0x4, 0x01e1); + + /* + * Set MDIO bit 0.12 = 1(&& bit 0.9=1?) to enable auto-negotiation + */ + miiphy_write(dev->name, phyAddr, 0x0, 0x1200); + + /* + * Wait for AN completion + */ + timeout = 5000; + do { + udelay(1000); + + if ((timeout--) == 0) { +#if (DEBUG & 0x2) + printf("PHY auto neg 0 failed...\n"); +#endif + return -1; + } + + if (miiphy_read(dev->name, phyAddr, 0x1, &phyStatus) != 0) { +#if (DEBUG & 0x2) + printf("PHY auto neg 1 failed 0x%04x...\n", phyStatus); +#endif + return -1; + } + } while (!(phyStatus & 0x0004)); + +#if (DEBUG & 0x2) + printf("PHY auto neg complete! \n"); +#endif + } + + } + +#if (DEBUG & 0x2) + if (fec->xcv_type != SEVENWIRE) + mpc5xxx_fec_phydump (dev->name); +#endif + + +#if (DEBUG & 0x1) + printf("mpc5xxx_fec_init_phy... Done \n"); +#endif + + return 1; +} + +/********************************************************************/ +static void mpc5xxx_fec_halt(struct eth_device *dev) +{ +#if defined(CONFIG_MPC5200) + struct mpc5xxx_sdma *sdma = (struct mpc5xxx_sdma *)MPC5XXX_SDMA; +#endif + mpc5xxx_fec_priv *fec = (mpc5xxx_fec_priv *)dev->priv; + int counter = 0xffff; + +#if (DEBUG & 0x2) + if (fec->xcv_type != SEVENWIRE) + mpc5xxx_fec_phydump (dev->name); +#endif + + /* + * mask FEC chip interrupts + */ + fec->eth->imask = 0; + + /* + * issue graceful stop command to the FEC transmitter if necessary + */ + fec->eth->x_cntrl |= 0x00000001; + + /* + * wait for graceful stop to register + */ + while ((counter--) && (!(fec->eth->ievent & 0x10000000))) ; + + /* + * Disable SmartDMA tasks + */ + SDMA_TASK_DISABLE (FEC_XMIT_TASK_NO); + SDMA_TASK_DISABLE (FEC_RECV_TASK_NO); + +#if defined(CONFIG_MPC5200) + /* + * Turn on COMM bus prefetch in the MGT5200 BestComm after we're + * done. It doesn't work w/ the current receive task. + */ + sdma->PtdCntrl &= ~0x00000001; +#endif + + /* + * Disable the Ethernet Controller + */ + fec->eth->ecntrl &= 0xfffffffd; + + /* + * Clear FIFO status registers + */ + fec->eth->rfifo_status &= 0x00700000; + fec->eth->tfifo_status &= 0x00700000; + + fec->eth->reset_cntrl = 0x01000000; + + /* + * Issue a reset command to the FEC chip + */ + fec->eth->ecntrl |= 0x1; + + /* + * wait at least 16 clock cycles + */ + udelay(10); + +#if (DEBUG & 0x3) + printf("Ethernet task stopped\n"); +#endif +} + +#if (DEBUG & 0x60) +/********************************************************************/ + +static void tfifo_print(char *devname, mpc5xxx_fec_priv *fec) +{ + uint16 phyAddr = CONFIG_PHY_ADDR; + uint16 phyStatus; + + if ((fec->eth->tfifo_lrf_ptr != fec->eth->tfifo_lwf_ptr) + || (fec->eth->tfifo_rdptr != fec->eth->tfifo_wrptr)) { + + miiphy_read(devname, phyAddr, 0x1, &phyStatus); + printf("\nphyStatus: 0x%04x\n", phyStatus); + printf("ecntrl: 0x%08x\n", fec->eth->ecntrl); + printf("ievent: 0x%08x\n", fec->eth->ievent); + printf("x_status: 0x%08x\n", fec->eth->x_status); + printf("tfifo: status 0x%08x\n", fec->eth->tfifo_status); + + printf(" control 0x%08x\n", fec->eth->tfifo_cntrl); + printf(" lrfp 0x%08x\n", fec->eth->tfifo_lrf_ptr); + printf(" lwfp 0x%08x\n", fec->eth->tfifo_lwf_ptr); + printf(" alarm 0x%08x\n", fec->eth->tfifo_alarm); + printf(" readptr 0x%08x\n", fec->eth->tfifo_rdptr); + printf(" writptr 0x%08x\n", fec->eth->tfifo_wrptr); + } +} + +static void rfifo_print(char *devname, mpc5xxx_fec_priv *fec) +{ + uint16 phyAddr = CONFIG_PHY_ADDR; + uint16 phyStatus; + + if ((fec->eth->rfifo_lrf_ptr != fec->eth->rfifo_lwf_ptr) + || (fec->eth->rfifo_rdptr != fec->eth->rfifo_wrptr)) { + + miiphy_read(devname, phyAddr, 0x1, &phyStatus); + printf("\nphyStatus: 0x%04x\n", phyStatus); + printf("ecntrl: 0x%08x\n", fec->eth->ecntrl); + printf("ievent: 0x%08x\n", fec->eth->ievent); + printf("x_status: 0x%08x\n", fec->eth->x_status); + printf("rfifo: status 0x%08x\n", fec->eth->rfifo_status); + + printf(" control 0x%08x\n", fec->eth->rfifo_cntrl); + printf(" lrfp 0x%08x\n", fec->eth->rfifo_lrf_ptr); + printf(" lwfp 0x%08x\n", fec->eth->rfifo_lwf_ptr); + printf(" alarm 0x%08x\n", fec->eth->rfifo_alarm); + printf(" readptr 0x%08x\n", fec->eth->rfifo_rdptr); + printf(" writptr 0x%08x\n", fec->eth->rfifo_wrptr); + } +} +#endif /* DEBUG */ + +/********************************************************************/ + +static int mpc5xxx_fec_send(struct eth_device *dev, volatile void *eth_data, + int data_length) +{ + /* + * This routine transmits one frame. This routine only accepts + * 6-byte Ethernet addresses. + */ + mpc5xxx_fec_priv *fec = (mpc5xxx_fec_priv *)dev->priv; + volatile FEC_TBD *pTbd; + +#if (DEBUG & 0x20) + printf("tbd status: 0x%04x\n", fec->tbdBase[0].status); + tfifo_print(dev->name, fec); +#endif + + /* + * Clear Tx BD ring at first + */ + mpc5xxx_fec_tbd_scrub(fec); + + /* + * Check for valid length of data. + */ + if ((data_length > 1500) || (data_length <= 0)) { + return -1; + } + + /* + * Check the number of vacant TxBDs. + */ + if (fec->cleanTbdNum < 1) { +#if (DEBUG & 0x20) + printf("No available TxBDs ...\n"); +#endif + return -1; + } + + /* + * Get the first TxBD to send the mac header + */ + pTbd = &fec->tbdBase[fec->tbdIndex]; + pTbd->dataLength = data_length; + pTbd->dataPointer = (uint32)eth_data; + pTbd->status |= FEC_TBD_LAST | FEC_TBD_TC | FEC_TBD_READY; + fec->tbdIndex = (fec->tbdIndex + 1) % FEC_TBD_NUM; + +#if (DEBUG & 0x100) + printf("SDMA_TASK_ENABLE, fec->tbdIndex = %d \n", fec->tbdIndex); +#endif + + /* + * Kick the MII i/f + */ + if (fec->xcv_type != SEVENWIRE) { + uint16 phyStatus; + miiphy_read(dev->name, 0, 0x1, &phyStatus); + } + + /* + * Enable SmartDMA transmit task + */ + +#if (DEBUG & 0x20) + tfifo_print(dev->name, fec); +#endif + SDMA_TASK_ENABLE (FEC_XMIT_TASK_NO); +#if (DEBUG & 0x20) + tfifo_print(dev->name, fec); +#endif +#if (DEBUG & 0x8) + printf( "+" ); +#endif + + fec->cleanTbdNum -= 1; + +#if (DEBUG & 0x129) && (DEBUG & 0x80000000) + printf ("smartDMA ethernet Tx task enabled\n"); +#endif + /* + * wait until frame is sent . + */ + while (pTbd->status & FEC_TBD_READY) { + udelay(10); +#if (DEBUG & 0x8) + printf ("TDB status = %04x\n", pTbd->status); +#endif + } + + return 0; +} + + +/********************************************************************/ +static int mpc5xxx_fec_recv(struct eth_device *dev) +{ + /* + * This command pulls one frame from the card + */ + mpc5xxx_fec_priv *fec = (mpc5xxx_fec_priv *)dev->priv; + volatile FEC_RBD *pRbd = &fec->rbdBase[fec->rbdIndex]; + unsigned long ievent; + int frame_length, len = 0; + NBUF *frame; + uchar buff[FEC_MAX_PKT_SIZE]; + +#if (DEBUG & 0x1) + printf ("mpc5xxx_fec_recv %d Start...\n", fec->rbdIndex); +#endif +#if (DEBUG & 0x8) + printf( "-" ); +#endif + + /* + * Check if any critical events have happened + */ + ievent = fec->eth->ievent; + fec->eth->ievent = ievent; + if (ievent & 0x20060000) { + /* BABT, Rx/Tx FIFO errors */ + mpc5xxx_fec_halt(dev); + mpc5xxx_fec_init(dev, NULL); + return 0; + } + if (ievent & 0x80000000) { + /* Heartbeat error */ + fec->eth->x_cntrl |= 0x00000001; + } + if (ievent & 0x10000000) { + /* Graceful stop complete */ + if (fec->eth->x_cntrl & 0x00000001) { + mpc5xxx_fec_halt(dev); + fec->eth->x_cntrl &= ~0x00000001; + mpc5xxx_fec_init(dev, NULL); + } + } + + if (!(pRbd->status & FEC_RBD_EMPTY)) { + if ((pRbd->status & FEC_RBD_LAST) && !(pRbd->status & FEC_RBD_ERR) && + ((pRbd->dataLength - 4) > 14)) { + + /* + * Get buffer address and size + */ + frame = (NBUF *)pRbd->dataPointer; + frame_length = pRbd->dataLength - 4; + +#if (DEBUG & 0x20) + { + int i; + printf("recv data hdr:"); + for (i = 0; i < 14; i++) + printf("%x ", *(frame->head + i)); + printf("\n"); + } +#endif + /* + * Fill the buffer and pass it to upper layers + */ + memcpy(buff, frame->head, 14); + memcpy(buff + 14, frame->data, frame_length); + NetReceive(buff, frame_length); + len = frame_length; + } + /* + * Reset buffer descriptor as empty + */ + mpc5xxx_fec_rbd_clean(fec, pRbd); + } + SDMA_CLEAR_IEVENT (FEC_RECV_TASK_NO); + return len; +} + + +/********************************************************************/ +int mpc5xxx_fec_initialize(bd_t * bis) +{ + mpc5xxx_fec_priv *fec; + struct eth_device *dev; + char *tmp, *end; + char env_enetaddr[6]; + int i; + + fec = (mpc5xxx_fec_priv *)malloc(sizeof(*fec)); + dev = (struct eth_device *)malloc(sizeof(*dev)); + memset(dev, 0, sizeof *dev); + + fec->eth = (ethernet_regs *)MPC5XXX_FEC; + fec->tbdBase = (FEC_TBD *)FEC_BD_BASE; + fec->rbdBase = (FEC_RBD *)(FEC_BD_BASE + FEC_TBD_NUM * sizeof(FEC_TBD)); +#if defined(CONFIG_CANMB) || \ + defined(CONFIG_CM5200) || \ + defined(CONFIG_HMI1001) || \ + defined(CONFIG_ICECUBE) || \ + defined(CONFIG_INKA4X0) || \ + defined(CONFIG_JUPITER) || \ + defined(CONFIG_MCC200) || \ + defined(CONFIG_MOTIONPRO) || \ + defined(CONFIG_MUCMC52) || \ + defined(CONFIG_O2DNT) || \ + defined(CONFIG_PM520) || \ + defined(CONFIG_TOP5200) || \ + defined(CONFIG_TQM5200) || \ + defined(CONFIG_UC101) || \ + defined(CONFIG_V38B) || \ + defined(CONFIG_MUNICES) +# ifndef CONFIG_FEC_10MBIT + fec->xcv_type = MII100; +# else + fec->xcv_type = MII10; +# endif +#elif defined(CONFIG_TOTAL5200) + fec->xcv_type = SEVENWIRE; +#else +#error fec->xcv_type not initialized. +#endif + + dev->priv = (void *)fec; + dev->iobase = MPC5XXX_FEC; + dev->init = mpc5xxx_fec_init; + dev->halt = mpc5xxx_fec_halt; + dev->send = mpc5xxx_fec_send; + dev->recv = mpc5xxx_fec_recv; + + sprintf(dev->name, "FEC ETHERNET"); + eth_register(dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register (dev->name, + fec5xxx_miiphy_read, fec5xxx_miiphy_write); +#endif + + /* + * Try to set the mac address now. The fec mac address is + * a garbage after reset. When not using fec for booting + * the Linux fec driver will try to work with this garbage. + */ + tmp = getenv("ethaddr"); + if (tmp) { + for (i=0; i<6; i++) { + env_enetaddr[i] = tmp ? simple_strtoul(tmp, &end, 16) : 0; + if (tmp) + tmp = (*end) ? end+1 : end; + } + mpc5xxx_fec_set_hwaddr(fec, env_enetaddr); + } + + mpc5xxx_fec_init_phy(dev, bis); + + return 1; +} + +/* MII-interface related functions */ +/********************************************************************/ +int fec5xxx_miiphy_read(char *devname, uint8 phyAddr, uint8 regAddr, uint16 * retVal) +{ + ethernet_regs *eth = (ethernet_regs *)MPC5XXX_FEC; + uint32 reg; /* convenient holder for the PHY register */ + uint32 phy; /* convenient holder for the PHY */ + int timeout = 0xffff; + + /* + * reading from any PHY's register is done by properly + * programming the FEC's MII data register. + */ + reg = regAddr << FEC_MII_DATA_RA_SHIFT; + phy = phyAddr << FEC_MII_DATA_PA_SHIFT; + + eth->mii_data = (FEC_MII_DATA_ST | FEC_MII_DATA_OP_RD | FEC_MII_DATA_TA | phy | reg); + + /* + * wait for the related interrupt + */ + while ((timeout--) && (!(eth->ievent & 0x00800000))) ; + + if (timeout == 0) { +#if (DEBUG & 0x2) + printf ("Read MDIO failed...\n"); +#endif + return -1; + } + + /* + * clear mii interrupt bit + */ + eth->ievent = 0x00800000; + + /* + * it's now safe to read the PHY's register + */ + *retVal = (uint16) eth->mii_data; + + return 0; +} + +/********************************************************************/ +int fec5xxx_miiphy_write(char *devname, uint8 phyAddr, uint8 regAddr, uint16 data) +{ + ethernet_regs *eth = (ethernet_regs *)MPC5XXX_FEC; + uint32 reg; /* convenient holder for the PHY register */ + uint32 phy; /* convenient holder for the PHY */ + int timeout = 0xffff; + + reg = regAddr << FEC_MII_DATA_RA_SHIFT; + phy = phyAddr << FEC_MII_DATA_PA_SHIFT; + + eth->mii_data = (FEC_MII_DATA_ST | FEC_MII_DATA_OP_WR | + FEC_MII_DATA_TA | phy | reg | data); + + /* + * wait for the MII interrupt + */ + while ((timeout--) && (!(eth->ievent & 0x00800000))) ; + + if (timeout == 0) { +#if (DEBUG & 0x2) + printf ("Write MDIO failed...\n"); +#endif + return -1; + } + + /* + * clear MII interrupt bit + */ + eth->ievent = 0x00800000; + + return 0; +} + +#if (DEBUG & 0x40) +static uint32 local_crc32(char *string, unsigned int crc_value, int len) +{ + int i; + char c; + unsigned int crc, count; + + /* + * crc32 algorithm + */ + /* + * crc = 0xffffffff; * The initialized value should be 0xffffffff + */ + crc = crc_value; + + for (i = len; --i >= 0;) { + c = *string++; + for (count = 0; count < 8; count++) { + if ((c & 0x01) ^ (crc & 0x01)) { + crc >>= 1; + crc = crc ^ 0xedb88320; + } else { + crc >>= 1; + } + c >>= 1; + } + } + + /* + * In big endian system, do byte swaping for crc value + */ + /**/ return crc; +} +#endif /* DEBUG */ + +#endif /* CONFIG_MPC5xxx_FEC */ diff --git a/drivers/net/mpc5xxx_fec.h b/drivers/net/mpc5xxx_fec.h new file mode 100644 index 00000000000..16c3e8e9186 --- /dev/null +++ b/drivers/net/mpc5xxx_fec.h @@ -0,0 +1,282 @@ +/* + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This file is based on mpc4200fec.h + * (C) Copyright Motorola, Inc., 2000 + * + * odin ethernet header file + */ + +#ifndef __MPC5XXX_FEC_H +#define __MPC5XXX_FEC_H + +typedef unsigned long uint32; +typedef unsigned short uint16; +typedef unsigned char uint8; + +typedef struct ethernet_register_set { + +/* [10:2]addr = 00 */ + +/* Control and status Registers (offset 000-1FF) */ + + volatile uint32 fec_id; /* MBAR_ETH + 0x000 */ + volatile uint32 ievent; /* MBAR_ETH + 0x004 */ + volatile uint32 imask; /* MBAR_ETH + 0x008 */ + + volatile uint32 RES0[1]; /* MBAR_ETH + 0x00C */ + volatile uint32 r_des_active; /* MBAR_ETH + 0x010 */ + volatile uint32 x_des_active; /* MBAR_ETH + 0x014 */ + volatile uint32 r_des_active_cl; /* MBAR_ETH + 0x018 */ + volatile uint32 x_des_active_cl; /* MBAR_ETH + 0x01C */ + volatile uint32 ivent_set; /* MBAR_ETH + 0x020 */ + volatile uint32 ecntrl; /* MBAR_ETH + 0x024 */ + + volatile uint32 RES1[6]; /* MBAR_ETH + 0x028-03C */ + volatile uint32 mii_data; /* MBAR_ETH + 0x040 */ + volatile uint32 mii_speed; /* MBAR_ETH + 0x044 */ + volatile uint32 mii_status; /* MBAR_ETH + 0x048 */ + + volatile uint32 RES2[5]; /* MBAR_ETH + 0x04C-05C */ + volatile uint32 mib_data; /* MBAR_ETH + 0x060 */ + volatile uint32 mib_control; /* MBAR_ETH + 0x064 */ + + volatile uint32 RES3[6]; /* MBAR_ETH + 0x068-7C */ + volatile uint32 r_activate; /* MBAR_ETH + 0x080 */ + volatile uint32 r_cntrl; /* MBAR_ETH + 0x084 */ + volatile uint32 r_hash; /* MBAR_ETH + 0x088 */ + volatile uint32 r_data; /* MBAR_ETH + 0x08C */ + volatile uint32 ar_done; /* MBAR_ETH + 0x090 */ + volatile uint32 r_test; /* MBAR_ETH + 0x094 */ + volatile uint32 r_mib; /* MBAR_ETH + 0x098 */ + volatile uint32 r_da_low; /* MBAR_ETH + 0x09C */ + volatile uint32 r_da_high; /* MBAR_ETH + 0x0A0 */ + + volatile uint32 RES4[7]; /* MBAR_ETH + 0x0A4-0BC */ + volatile uint32 x_activate; /* MBAR_ETH + 0x0C0 */ + volatile uint32 x_cntrl; /* MBAR_ETH + 0x0C4 */ + volatile uint32 backoff; /* MBAR_ETH + 0x0C8 */ + volatile uint32 x_data; /* MBAR_ETH + 0x0CC */ + volatile uint32 x_status; /* MBAR_ETH + 0x0D0 */ + volatile uint32 x_mib; /* MBAR_ETH + 0x0D4 */ + volatile uint32 x_test; /* MBAR_ETH + 0x0D8 */ + volatile uint32 fdxfc_da1; /* MBAR_ETH + 0x0DC */ + volatile uint32 fdxfc_da2; /* MBAR_ETH + 0x0E0 */ + volatile uint32 paddr1; /* MBAR_ETH + 0x0E4 */ + volatile uint32 paddr2; /* MBAR_ETH + 0x0E8 */ + volatile uint32 op_pause; /* MBAR_ETH + 0x0EC */ + + volatile uint32 RES5[4]; /* MBAR_ETH + 0x0F0-0FC */ + volatile uint32 instr_reg; /* MBAR_ETH + 0x100 */ + volatile uint32 context_reg; /* MBAR_ETH + 0x104 */ + volatile uint32 test_cntrl; /* MBAR_ETH + 0x108 */ + volatile uint32 acc_reg; /* MBAR_ETH + 0x10C */ + volatile uint32 ones; /* MBAR_ETH + 0x110 */ + volatile uint32 zeros; /* MBAR_ETH + 0x114 */ + volatile uint32 iaddr1; /* MBAR_ETH + 0x118 */ + volatile uint32 iaddr2; /* MBAR_ETH + 0x11C */ + volatile uint32 gaddr1; /* MBAR_ETH + 0x120 */ + volatile uint32 gaddr2; /* MBAR_ETH + 0x124 */ + volatile uint32 random; /* MBAR_ETH + 0x128 */ + volatile uint32 rand1; /* MBAR_ETH + 0x12C */ + volatile uint32 tmp; /* MBAR_ETH + 0x130 */ + + volatile uint32 RES6[3]; /* MBAR_ETH + 0x134-13C */ + volatile uint32 fifo_id; /* MBAR_ETH + 0x140 */ + volatile uint32 x_wmrk; /* MBAR_ETH + 0x144 */ + volatile uint32 fcntrl; /* MBAR_ETH + 0x148 */ + volatile uint32 r_bound; /* MBAR_ETH + 0x14C */ + volatile uint32 r_fstart; /* MBAR_ETH + 0x150 */ + volatile uint32 r_count; /* MBAR_ETH + 0x154 */ + volatile uint32 r_lag; /* MBAR_ETH + 0x158 */ + volatile uint32 r_read; /* MBAR_ETH + 0x15C */ + volatile uint32 r_write; /* MBAR_ETH + 0x160 */ + volatile uint32 x_count; /* MBAR_ETH + 0x164 */ + volatile uint32 x_lag; /* MBAR_ETH + 0x168 */ + volatile uint32 x_retry; /* MBAR_ETH + 0x16C */ + volatile uint32 x_write; /* MBAR_ETH + 0x170 */ + volatile uint32 x_read; /* MBAR_ETH + 0x174 */ + + volatile uint32 RES7[2]; /* MBAR_ETH + 0x178-17C */ + volatile uint32 fm_cntrl; /* MBAR_ETH + 0x180 */ + volatile uint32 rfifo_data; /* MBAR_ETH + 0x184 */ + volatile uint32 rfifo_status; /* MBAR_ETH + 0x188 */ + volatile uint32 rfifo_cntrl; /* MBAR_ETH + 0x18C */ + volatile uint32 rfifo_lrf_ptr; /* MBAR_ETH + 0x190 */ + volatile uint32 rfifo_lwf_ptr; /* MBAR_ETH + 0x194 */ + volatile uint32 rfifo_alarm; /* MBAR_ETH + 0x198 */ + volatile uint32 rfifo_rdptr; /* MBAR_ETH + 0x19C */ + volatile uint32 rfifo_wrptr; /* MBAR_ETH + 0x1A0 */ + volatile uint32 tfifo_data; /* MBAR_ETH + 0x1A4 */ + volatile uint32 tfifo_status; /* MBAR_ETH + 0x1A8 */ + volatile uint32 tfifo_cntrl; /* MBAR_ETH + 0x1AC */ + volatile uint32 tfifo_lrf_ptr; /* MBAR_ETH + 0x1B0 */ + volatile uint32 tfifo_lwf_ptr; /* MBAR_ETH + 0x1B4 */ + volatile uint32 tfifo_alarm; /* MBAR_ETH + 0x1B8 */ + volatile uint32 tfifo_rdptr; /* MBAR_ETH + 0x1BC */ + volatile uint32 tfifo_wrptr; /* MBAR_ETH + 0x1C0 */ + + volatile uint32 reset_cntrl; /* MBAR_ETH + 0x1C4 */ + volatile uint32 xmit_fsm; /* MBAR_ETH + 0x1C8 */ + + volatile uint32 RES8[3]; /* MBAR_ETH + 0x1CC-1D4 */ + volatile uint32 rdes_data0; /* MBAR_ETH + 0x1D8 */ + volatile uint32 rdes_data1; /* MBAR_ETH + 0x1DC */ + volatile uint32 r_length; /* MBAR_ETH + 0x1E0 */ + volatile uint32 x_length; /* MBAR_ETH + 0x1E4 */ + volatile uint32 x_addr; /* MBAR_ETH + 0x1E8 */ + volatile uint32 cdes_data; /* MBAR_ETH + 0x1EC */ + volatile uint32 status; /* MBAR_ETH + 0x1F0 */ + volatile uint32 dma_control; /* MBAR_ETH + 0x1F4 */ + volatile uint32 des_cmnd; /* MBAR_ETH + 0x1F8 */ + volatile uint32 data; /* MBAR_ETH + 0x1FC */ + +/* MIB COUNTERS (Offset 200-2FF) */ + + volatile uint32 rmon_t_drop; /* MBAR_ETH + 0x200 */ + volatile uint32 rmon_t_packets; /* MBAR_ETH + 0x204 */ + volatile uint32 rmon_t_bc_pkt; /* MBAR_ETH + 0x208 */ + volatile uint32 rmon_t_mc_pkt; /* MBAR_ETH + 0x20C */ + volatile uint32 rmon_t_crc_align; /* MBAR_ETH + 0x210 */ + volatile uint32 rmon_t_undersize; /* MBAR_ETH + 0x214 */ + volatile uint32 rmon_t_oversize; /* MBAR_ETH + 0x218 */ + volatile uint32 rmon_t_frag; /* MBAR_ETH + 0x21C */ + volatile uint32 rmon_t_jab; /* MBAR_ETH + 0x220 */ + volatile uint32 rmon_t_col; /* MBAR_ETH + 0x224 */ + volatile uint32 rmon_t_p64; /* MBAR_ETH + 0x228 */ + volatile uint32 rmon_t_p65to127; /* MBAR_ETH + 0x22C */ + volatile uint32 rmon_t_p128to255; /* MBAR_ETH + 0x230 */ + volatile uint32 rmon_t_p256to511; /* MBAR_ETH + 0x234 */ + volatile uint32 rmon_t_p512to1023; /* MBAR_ETH + 0x238 */ + volatile uint32 rmon_t_p1024to2047; /* MBAR_ETH + 0x23C */ + volatile uint32 rmon_t_p_gte2048; /* MBAR_ETH + 0x240 */ + volatile uint32 rmon_t_octets; /* MBAR_ETH + 0x244 */ + volatile uint32 ieee_t_drop; /* MBAR_ETH + 0x248 */ + volatile uint32 ieee_t_frame_ok; /* MBAR_ETH + 0x24C */ + volatile uint32 ieee_t_1col; /* MBAR_ETH + 0x250 */ + volatile uint32 ieee_t_mcol; /* MBAR_ETH + 0x254 */ + volatile uint32 ieee_t_def; /* MBAR_ETH + 0x258 */ + volatile uint32 ieee_t_lcol; /* MBAR_ETH + 0x25C */ + volatile uint32 ieee_t_excol; /* MBAR_ETH + 0x260 */ + volatile uint32 ieee_t_macerr; /* MBAR_ETH + 0x264 */ + volatile uint32 ieee_t_cserr; /* MBAR_ETH + 0x268 */ + volatile uint32 ieee_t_sqe; /* MBAR_ETH + 0x26C */ + volatile uint32 t_fdxfc; /* MBAR_ETH + 0x270 */ + volatile uint32 ieee_t_octets_ok; /* MBAR_ETH + 0x274 */ + + volatile uint32 RES9[2]; /* MBAR_ETH + 0x278-27C */ + volatile uint32 rmon_r_drop; /* MBAR_ETH + 0x280 */ + volatile uint32 rmon_r_packets; /* MBAR_ETH + 0x284 */ + volatile uint32 rmon_r_bc_pkt; /* MBAR_ETH + 0x288 */ + volatile uint32 rmon_r_mc_pkt; /* MBAR_ETH + 0x28C */ + volatile uint32 rmon_r_crc_align; /* MBAR_ETH + 0x290 */ + volatile uint32 rmon_r_undersize; /* MBAR_ETH + 0x294 */ + volatile uint32 rmon_r_oversize; /* MBAR_ETH + 0x298 */ + volatile uint32 rmon_r_frag; /* MBAR_ETH + 0x29C */ + volatile uint32 rmon_r_jab; /* MBAR_ETH + 0x2A0 */ + + volatile uint32 rmon_r_resvd_0; /* MBAR_ETH + 0x2A4 */ + + volatile uint32 rmon_r_p64; /* MBAR_ETH + 0x2A8 */ + volatile uint32 rmon_r_p65to127; /* MBAR_ETH + 0x2AC */ + volatile uint32 rmon_r_p128to255; /* MBAR_ETH + 0x2B0 */ + volatile uint32 rmon_r_p256to511; /* MBAR_ETH + 0x2B4 */ + volatile uint32 rmon_r_p512to1023; /* MBAR_ETH + 0x2B8 */ + volatile uint32 rmon_r_p1024to2047; /* MBAR_ETH + 0x2BC */ + volatile uint32 rmon_r_p_gte2048; /* MBAR_ETH + 0x2C0 */ + volatile uint32 rmon_r_octets; /* MBAR_ETH + 0x2C4 */ + volatile uint32 ieee_r_drop; /* MBAR_ETH + 0x2C8 */ + volatile uint32 ieee_r_frame_ok; /* MBAR_ETH + 0x2CC */ + volatile uint32 ieee_r_crc; /* MBAR_ETH + 0x2D0 */ + volatile uint32 ieee_r_align; /* MBAR_ETH + 0x2D4 */ + volatile uint32 r_macerr; /* MBAR_ETH + 0x2D8 */ + volatile uint32 r_fdxfc; /* MBAR_ETH + 0x2DC */ + volatile uint32 ieee_r_octets_ok; /* MBAR_ETH + 0x2E0 */ + + volatile uint32 RES10[6]; /* MBAR_ETH + 0x2E4-2FC */ + + volatile uint32 RES11[64]; /* MBAR_ETH + 0x300-3FF */ +} ethernet_regs; + +/* Receive & Transmit Buffer Descriptor definitions */ +typedef struct BufferDescriptor { + uint16 status; + uint16 dataLength; + uint32 dataPointer; +} FEC_RBD; +typedef struct { + uint16 status; + uint16 dataLength; + uint32 dataPointer; +} FEC_TBD; + +/* private structure */ +typedef enum { + SEVENWIRE, /* 7-wire */ + MII10, /* MII 10Mbps */ + MII100 /* MII 100Mbps */ +} xceiver_type; + +typedef struct { + ethernet_regs *eth; + xceiver_type xcv_type; /* transceiver type */ + FEC_RBD *rbdBase; /* RBD ring */ + FEC_TBD *tbdBase; /* TBD ring */ + uint16 rbdIndex; /* next receive BD to read */ + uint16 tbdIndex; /* next transmit BD to send */ + uint16 usedTbdIndex; /* next transmit BD to clean */ + uint16 cleanTbdNum; /* the number of available transmit BDs */ +} mpc5xxx_fec_priv; + +/* Ethernet parameter area */ +#define FEC_TBD_BASE (FEC_PARAM_BASE + 0x00) +#define FEC_TBD_NEXT (FEC_PARAM_BASE + 0x04) +#define FEC_RBD_BASE (FEC_PARAM_BASE + 0x08) +#define FEC_RBD_NEXT (FEC_PARAM_BASE + 0x0c) + +/* BD Numer definitions */ +#define FEC_TBD_NUM 48 /* The user can adjust this value */ +#define FEC_RBD_NUM 32 /* The user can adjust this value */ + +/* packet size limit */ +#define FEC_MAX_PKT_SIZE 1536 + +/* RBD bits definitions */ +#define FEC_RBD_EMPTY 0x8000 /* Buffer is empty */ +#define FEC_RBD_WRAP 0x2000 /* Last BD in ring */ +#define FEC_RBD_INT 0x1000 /* Interrupt */ +#define FEC_RBD_LAST 0x0800 /* Buffer is last in frame(useless) */ +#define FEC_RBD_MISS 0x0100 /* Miss bit for prom mode */ +#define FEC_RBD_BC 0x0080 /* The received frame is broadcast frame */ +#define FEC_RBD_MC 0x0040 /* The received frame is multicast frame */ +#define FEC_RBD_LG 0x0020 /* Frame length violation */ +#define FEC_RBD_NO 0x0010 /* Nonoctet align frame */ +#define FEC_RBD_SH 0x0008 /* Short frame */ +#define FEC_RBD_CR 0x0004 /* CRC error */ +#define FEC_RBD_OV 0x0002 /* Receive FIFO overrun */ +#define FEC_RBD_TR 0x0001 /* Frame is truncated */ +#define FEC_RBD_ERR (FEC_RBD_LG | FEC_RBD_NO | FEC_RBD_CR | \ + FEC_RBD_OV | FEC_RBD_TR) + +/* TBD bits definitions */ +#define FEC_TBD_READY 0x8000 /* Buffer is ready */ +#define FEC_TBD_WRAP 0x2000 /* Last BD in ring */ +#define FEC_TBD_INT 0x1000 /* Interrupt */ +#define FEC_TBD_LAST 0x0800 /* Buffer is last in frame */ +#define FEC_TBD_TC 0x0400 /* Transmit the CRC */ +#define FEC_TBD_ABC 0x0200 /* Append bad CRC */ + +/* MII-related definitios */ +#define FEC_MII_DATA_ST 0x40000000 /* Start of frame delimiter */ +#define FEC_MII_DATA_OP_RD 0x20000000 /* Perform a read operation */ +#define FEC_MII_DATA_OP_WR 0x10000000 /* Perform a write operation */ +#define FEC_MII_DATA_PA_MSK 0x0f800000 /* PHY Address field mask */ +#define FEC_MII_DATA_RA_MSK 0x007c0000 /* PHY Register field mask */ +#define FEC_MII_DATA_TA 0x00020000 /* Turnaround */ +#define FEC_MII_DATA_DATAMSK 0x0000ffff /* PHY data field */ + +#define FEC_MII_DATA_RA_SHIFT 18 /* MII Register address bits */ +#define FEC_MII_DATA_PA_SHIFT 23 /* MII PHY address bits */ + +#endif /* __MPC5XXX_FEC_H */ diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 2384735a2ce..f30014d32ef 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -30,6 +30,7 @@ COBJS-$(CONFIG_MCFUART) += mcfuart.o COBJS-$(CONFIG_NS9750_UART) += ns9750_serial.o COBJS-y += ns16550.o COBJS-$(CONFIG_DRIVER_S3C4510_UART) += s3c4510b_uart.o +COBJS-$(CONFIG_S3C64XX) += s3c64xx.o COBJS-y += serial.o COBJS-$(CONFIG_MAX3100_SERIAL) += serial_max3100.o COBJS-y += serial_pl010.o diff --git a/drivers/serial/s3c64xx.c b/drivers/serial/s3c64xx.c new file mode 100644 index 00000000000..9d8fcb9b498 --- /dev/null +++ b/drivers/serial/s3c64xx.c @@ -0,0 +1,172 @@ +/* + * (C) Copyright 2002 + * Gary Jennejohn, DENX Software Engineering, <gj@denx.de> + * + * (C) Copyright 2008 + * Guennadi Liakhovetki, DENX Software Engineering, <lg@denx.de> + * + * 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 <s3c6400.h> + +#ifdef CONFIG_SERIAL1 +#define UART_NR S3C64XX_UART0 + +#elif defined(CONFIG_SERIAL2) +#define UART_NR S3C64XX_UART1 + +#elif defined(CONFIG_SERIAL3) +#define UART_NR S3C64XX_UART2 + +#else +#error "Bad: you didn't configure serial ..." +#endif + +#define barrier() asm volatile("" ::: "memory") + +/* + * The coefficient, used to calculate the baudrate on S3C6400 UARTs is + * calculated as + * C = UBRDIV * 16 + number_of_set_bits_in_UDIVSLOT + * however, section 31.6.11 of the datasheet doesn't recomment using 1 for 1, + * 3 for 2, ... (2^n - 1) for n, instead, they suggest using these constants: + */ +static const int udivslot[] = { + 0, + 0x0080, + 0x0808, + 0x0888, + 0x2222, + 0x4924, + 0x4a52, + 0x54aa, + 0x5555, + 0xd555, + 0xd5d5, + 0xddd5, + 0xdddd, + 0xdfdd, + 0xdfdf, + 0xffdf, +}; + +void serial_setbrg(void) +{ + DECLARE_GLOBAL_DATA_PTR; + s3c64xx_uart *const uart = s3c64xx_get_base_uart(UART_NR); + u32 pclk = get_PCLK(); + u32 baudrate = gd->baudrate; + int i; + + i = (pclk / baudrate) % 16; + + uart->UBRDIV = pclk / baudrate / 16 - 1; + uart->UDIVSLOT = udivslot[i]; + + for (i = 0; i < 100; i++) + barrier(); +} + +/* + * Initialise the serial port with the given baudrate. The settings + * are always 8 data bits, no parity, 1 stop bit, no start bits. + */ +int serial_init(void) +{ + s3c64xx_uart *const uart = s3c64xx_get_base_uart(UART_NR); + + /* reset and enable FIFOs, set triggers to the maximum */ + uart->UFCON = 0xff; + uart->UMCON = 0; + /* 8N1 */ + uart->ULCON = 3; + /* No interrupts, no DMA, pure polling */ + uart->UCON = 5; + + serial_setbrg(); + + return 0; +} + +/* + * Read a single byte from the serial port. Returns 1 on success, 0 + * otherwise. When the function is succesfull, the character read is + * written into its argument c. + */ +int serial_getc(void) +{ + s3c64xx_uart *const uart = s3c64xx_get_base_uart(UART_NR); + + /* wait for character to arrive */ + while (!(uart->UTRSTAT & 0x1)); + + return uart->URXH & 0xff; +} + +#ifdef CONFIG_MODEM_SUPPORT +static int be_quiet; +void disable_putc(void) +{ + be_quiet = 1; +} + +void enable_putc(void) +{ + be_quiet = 0; +} +#endif + + +/* + * Output a single byte to the serial port. + */ +void serial_putc(const char c) +{ + s3c64xx_uart *const uart = s3c64xx_get_base_uart(UART_NR); + +#ifdef CONFIG_MODEM_SUPPORT + if (be_quiet) + return; +#endif + + /* wait for room in the tx FIFO */ + while (!(uart->UTRSTAT & 0x2)); + + uart->UTXH = c; + + /* If \n, also do \r */ + if (c == '\n') + serial_putc('\r'); +} + +/* + * Test whether a character is in the RX buffer + */ +int serial_tstc(void) +{ + s3c64xx_uart *const uart = s3c64xx_get_base_uart(UART_NR); + + return uart->UTRSTAT & 0x1; +} + +void serial_puts(const char *s) +{ + while (*s) + serial_putc(*s++); +} diff --git a/drivers/serial/serial_sh.c b/drivers/serial/serial_sh.c index 2b9eeed47e0..61c2b82c0ac 100644 --- a/drivers/serial/serial_sh.c +++ b/drivers/serial/serial_sh.c @@ -20,14 +20,20 @@ #include <common.h> #include <asm/processor.h> -#if defined (CONFIG_CONS_SCIF0) -#define SCIF_BASE SCIF0_BASE -#elif defined (CONFIG_CONS_SCIF1) -#define SCIF_BASE SCIF1_BASE -#elif defined (CONFIG_CONS_SCIF2) -#define SCIF_BASE SCIF2_BASE +#if defined(CONFIG_CONS_SCIF0) +# define SCIF_BASE SCIF0_BASE +#elif defined(CONFIG_CONS_SCIF1) +# define SCIF_BASE SCIF1_BASE +#elif defined(CONFIG_CONS_SCIF2) +# define SCIF_BASE SCIF2_BASE +#elif defined(CONFIG_CONS_SCIF3) +# define SCIF_BASE SCIF3_BASE +#elif defined(CONFIG_CONS_SCIF4) +# define SCIF_BASE SCIF4_BASE +#elif defined(CONFIG_CONS_SCIF5) +# define SCIF_BASE SCIF5_BASE #else -#error "Default SCIF doesn't set....." +# error "Default SCIF doesn't set....." #endif /* Base register */ @@ -36,7 +42,8 @@ #define SCSCR (vu_short *)(SCIF_BASE + 0x8) #define SCFCR (vu_short *)(SCIF_BASE + 0x18) #define SCFDR (vu_short *)(SCIF_BASE + 0x1C) -#ifdef CONFIG_CPU_SH7720 /* SH7720 specific */ +#if defined(CONFIG_CPU_SH7720) || \ + (defined(CONFIG_CPU_SH7723) && defined(CONFIG_SCIF_A)) # define SCFSR (vu_short *)(SCIF_BASE + 0x14) /* SCSSR */ # define SCFTDR (vu_char *)(SCIF_BASE + 0x20) # define SCFRDR (vu_char *)(SCIF_BASE + 0x24) @@ -55,7 +62,7 @@ # define LSR_ORER 1 # define FIFOLEVEL_MASK 0xFF #elif defined(CONFIG_CPU_SH7763) -# if defined (CONFIG_CONS_SCIF2) +# if defined(CONFIG_CONS_SCIF2) # define SCSPTR (vu_short *)(SCIF_BASE + 0x20) # define SCLSR (vu_short *)(SCIF_BASE + 0x24) # define LSR_ORER 1 @@ -68,9 +75,20 @@ # define LSR_ORER 1 # define FIFOLEVEL_MASK 0xFF # endif +#elif defined(CONFIG_CPU_SH7723) +# if defined(CONIFG_SCIF_A) +# define SCLSR SCFSR +# define LSR_ORER 0x0200 +# define FIFOLEVEL_MASK 0x3F +#else +# define SCLSR (vu_short *)(SCIF_BASE + 0x24) +# define LSR_ORER 1 +# define FIFOLEVEL_MASK 0x1F +#endif #elif defined(CONFIG_CPU_SH7750) || \ defined(CONFIG_CPU_SH7751) || \ - defined(CONFIG_CPU_SH7722) + defined(CONFIG_CPU_SH7722) || \ + defined(CONFIG_CPU_SH7203) # define SCSPTR (vu_short *)(SCIF_BASE + 0x20) # define SCLSR (vu_short *)(SCIF_BASE + 0x24) # define LSR_ORER 1 @@ -89,6 +107,9 @@ /* SCBRR register value setting */ #if defined(CONFIG_CPU_SH7720) # define SCBRR_VALUE(bps, clk) (((clk*2)+16*bps)/(32*bps)-1) +#elif defined(CONFIG_CPU_SH7723) && defined(CONFIG_SCIF_A) +/* SH7723 SCIFA use bus clock. So clock *2 */ +# define SCBRR_VALUE(bps, clk) (((clk*2*2)+16*bps)/(32*bps)-1) #else /* Generic SuperH */ # define SCBRR_VALUE(bps, clk) ((clk+16*bps)/(32*bps)-1) #endif @@ -167,7 +188,7 @@ void serial_puts(const char *s) int serial_tstc(void) { - return serial_rx_fifo_level()? 1 : 0; + return serial_rx_fifo_level() ? 1 : 0; } #define FSR_ERR_CLEAR 0x0063 @@ -191,14 +212,16 @@ int serial_getc_check(void) handle_error(); if (*SCLSR & LSR_ORER) handle_error(); - return (status & (FSR_DR | FSR_RDF)); + return status & (FSR_DR | FSR_RDF); } int serial_getc(void) { unsigned short status; char ch; - while (!serial_getc_check()) ; + + while (!serial_getc_check()) + ; ch = *SCFRDR; status = *SCFSR; diff --git a/drivers/usb/usb_ohci.c b/drivers/usb/usb_ohci.c index fd60edbb3d4..0bfa4d782a5 100644 --- a/drivers/usb/usb_ohci.c +++ b/drivers/usb/usb_ohci.c @@ -69,6 +69,7 @@ #if defined(CONFIG_ARM920T) || \ defined(CONFIG_S3C2400) || \ defined(CONFIG_S3C2410) || \ + defined(CONFIG_S3C6400) || \ defined(CONFIG_440EP) || \ defined(CONFIG_PCI_OHCI) || \ defined(CONFIG_MPC5200) || \ |