aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorWolfgang Denk <wd@denx.de>2011-10-04 22:08:13 +0200
committerWolfgang Denk <wd@denx.de>2011-10-04 22:08:13 +0200
commit1fed668b3fb9c35932f58af00ff5539239fa4e1d (patch)
treeeaaaead8ca19924af1823caae040f504be9b6d98 /drivers
parentc52575350fd6e794717f6bee4f81dbb8038fe22e (diff)
parent6d7b061af153bc5beb633c3bd15348284716a067 (diff)
downloadu-boot-1fed668b3fb9c35932f58af00ff5539239fa4e1d.zip
u-boot-1fed668b3fb9c35932f58af00ff5539239fa4e1d.tar.gz
u-boot-1fed668b3fb9c35932f58af00ff5539239fa4e1d.tar.bz2
Merge branch 'master' of git://git.denx.de/u-boot-mpc85xx
* 'master' of git://git.denx.de/u-boot-mpc85xx: powerpc/p3060: Add SoC related support for P3060 platform powerpc/85xx: Add support for setting up RAID engine liodns on P5020 powerpc/85xx: Refactor some defines out of corenet_ds.h fm-eth: Add ability for board code to disable a port powerpc/mpc8548: Add workaround for erratum NMG_LBC103 powerpc/mpc8548: Add workaround for erratum NMG_DDR120 powerpc/mpc85xxcds: Fix PCI speed powerpc/mpc8548cds: Fix booting message powerpc/p4080: Add support for secure boot flow powerpc/85xx: Add Secure Boot support on P1010RDB for NOR, NAND & SPIFLASH powerpc/85xx: Add PBL & SECUREBOOT support on P3041/P5020DS boards powerpc/p2041rdb: remove watch dog related codes powerpc/p2041rdb: updated description of cpld command powerpc/p2041rdb: add more ddr frequencies support powerpc/p2041rdb: set sysclk according to status of physical switch SW1 powerpc/p2041rdb: update cpld reset command according to CPLD 2.0 powerpc/mpc8349emds: Migrate from spd_sdram to unified DDR driver powerpc/mpc83xx: Migrate from spd_sdram to unified DDR driver powerpc/mpc8xxx: Add DDR2 to unified DDR driver powerpc/mpc8xxx: Fix picos_to_mclk() and get_memory_clk_period_ps() powerpc/mpc8xxx: Add SPD EEPROM address for single controller 2 slots powerpc/mpc8xxx: Fix DDR code for empty first DIMM slot and enable DQS_en powerpc/85xx: Refactor P2041RDB to use common p_corenet files powerpc/85xx: refactor common P-Series CoreNet files for FSL boards powerpc/85xx: Enable CMD_REGINFO on corenet boards powerpc/85xx: p2041rdb - Remove unused 'execute' perm in TLB entries powerpc/85xx: Fix USB protocol definitions for P1020RDB powerpc/corenet_ds: Use separated speed tables for UDIMM and RDIMM powerpc/mpc8xxx: Move DDR RCW overriding to common code powerpc/mpc8xxx: Extend CWL table powerpc/85xx: Cleanup how SVR_MAJ() is defined on MPC8536 powerpc/85xx: Cleanup extern in corenet_ds board code powerpc/p2041rdb: Add ethernet support on P2041RDB board powerpc/85xx: Add networking support to P1023RDS powerpc/hydra: Add ethernet support on P5020/P3041 DS boards powerpc/85xx: Add FMan ethernet support to P4080DS powerpc/85xx: Add support for FMan ethernet in Independent mode powerpc/mpc8548cds: Cleanup mpc8548cds.c powerpc/mp: add support for discontiguous cores powerpc/85xx: corenet_ds - Remove unused 'execute' perm in TLB entries fdt: Add new fdt_create_phandle helper fdt: Rename fdt_create_phandle to fdt_set_phandle powerpc/85xx: Fix compile warnings/errors if CONFIG_SYS_DPAA_FMAN isn't set fsl_ifc: Add the workaround for erratum IFC A-003399(enabled on P1010) powerpc/P1010: Add workaround for erratum P1010-A003549 (related to IFC) fsl_ifc: Add the workaround for erratum IFC-A002769 (enable on P1010) powerpc/85xx: Expanding the window of CCSRBAR in AS=1 from 4k to 1M powerpc/85xx: Add NAND/NAND_SPL support to P1010RDB nand: Freescale Integrated Flash Controller NAND support powerpc/85xx: Add basic support for P1010RDB powerpc/85xx: Add support for new P102x/P2020 RDB style boards powerpc/85xx: relocate CCSR before creating the initial RAM area powerpc/85xx: introduce and document CONFIG_SYS_CCSRBAR macros powerpc/85xx: Enable internal USB UTMI PHY on p204x/p3041/p50x0 powerpc/85xx: Add ULPI and UTMI USB Phy support for P1010/P1014
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/fsl_ifc_nand.c850
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/fm/Makefile62
-rw-r--r--drivers/net/fm/dtsec.c181
-rw-r--r--drivers/net/fm/eth.c670
-rw-r--r--drivers/net/fm/fm.c432
-rw-r--r--drivers/net/fm/fm.h155
-rw-r--r--drivers/net/fm/init.c216
-rw-r--r--drivers/net/fm/p1023.c74
-rw-r--r--drivers/net/fm/p3060.c109
-rw-r--r--drivers/net/fm/p4080.c100
-rw-r--r--drivers/net/fm/p5020.c91
-rw-r--r--drivers/net/fm/tgec.c119
-rw-r--r--drivers/net/fm/tgec_phy.c139
-rw-r--r--drivers/usb/host/ehci-fsl.c39
16 files changed, 3236 insertions, 3 deletions
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index b6a7886..dae2442 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -43,6 +43,7 @@ COBJS-$(CONFIG_NAND_ATMEL) += atmel_nand.o
COBJS-$(CONFIG_DRIVER_NAND_BFIN) += bfin_nand.o
COBJS-$(CONFIG_NAND_DAVINCI) += davinci_nand.o
COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o
+COBJS-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_nand.o
COBJS-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o
COBJS-$(CONFIG_NAND_KB9202) += kb9202_nand.o
COBJS-$(CONFIG_NAND_KIRKWOOD) += kirkwood_nand.o
diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c
new file mode 100644
index 0000000..b3f3c3c
--- /dev/null
+++ b/drivers/mtd/nand/fsl_ifc_nand.c
@@ -0,0 +1,850 @@
+/* Integrated Flash Controller NAND Machine Driver
+ *
+ * Copyright (c) 2011 Freescale Semiconductor, Inc
+ *
+ * Authors: Dipen Dudhat <Dipen.Dudhat@freescale.com>
+ *
+ * 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 <malloc.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+
+#include <asm/io.h>
+#include <asm/errno.h>
+#include <asm/fsl_ifc.h>
+
+#define MAX_BANKS 4
+#define ERR_BYTE 0xFF /* Value returned for read bytes
+ when read failed */
+#define IFC_TIMEOUT_MSECS 10 /* Maximum number of mSecs to wait for IFC
+ NAND Machine */
+
+struct fsl_ifc_ctrl;
+
+/* mtd information per set */
+struct fsl_ifc_mtd {
+ struct mtd_info mtd;
+ struct nand_chip chip;
+ struct fsl_ifc_ctrl *ctrl;
+
+ struct device *dev;
+ int bank; /* Chip select bank number */
+ unsigned int bufnum_mask; /* bufnum = page & bufnum_mask */
+ u8 __iomem *vbase; /* Chip select base virtual address */
+};
+
+/* overview of the fsl ifc controller */
+struct fsl_ifc_ctrl {
+ struct nand_hw_control controller;
+ struct fsl_ifc_mtd *chips[MAX_BANKS];
+
+ /* device info */
+ struct fsl_ifc *regs;
+ uint8_t __iomem *addr; /* Address of assigned IFC buffer */
+ unsigned int cs_nand; /* On which chipsel NAND is connected */
+ unsigned int page; /* Last page written to / read from */
+ unsigned int read_bytes; /* Number of bytes read during command */
+ unsigned int column; /* Saved column from SEQIN */
+ unsigned int index; /* Pointer to next byte to 'read' */
+ unsigned int status; /* status read from NEESR after last op */
+ unsigned int oob; /* Non zero if operating on OOB data */
+ unsigned int eccread; /* Non zero for a full-page ECC read */
+};
+
+static struct fsl_ifc_ctrl *ifc_ctrl;
+
+/* 512-byte page with 4-bit ECC, 8-bit */
+static struct nand_ecclayout oob_512_8bit_ecc4 = {
+ .eccbytes = 8,
+ .eccpos = {8, 9, 10, 11, 12, 13, 14, 15},
+ .oobfree = { {0, 5}, {6, 2} },
+};
+
+/* 512-byte page with 4-bit ECC, 16-bit */
+static struct nand_ecclayout oob_512_16bit_ecc4 = {
+ .eccbytes = 8,
+ .eccpos = {8, 9, 10, 11, 12, 13, 14, 15},
+ .oobfree = { {2, 6}, },
+};
+
+/* 2048-byte page size with 4-bit ECC */
+static struct nand_ecclayout oob_2048_ecc4 = {
+ .eccbytes = 32,
+ .eccpos = {
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ },
+ .oobfree = { {2, 6}, {40, 24} },
+};
+
+/* 4096-byte page size with 4-bit ECC */
+static struct nand_ecclayout oob_4096_ecc4 = {
+ .eccbytes = 64,
+ .eccpos = {
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ },
+ .oobfree = { {2, 6}, {72, 56} },
+};
+
+/* 4096-byte page size with 8-bit ECC -- requires 218-byte OOB */
+static struct nand_ecclayout oob_4096_ecc8 = {
+ .eccbytes = 128,
+ .eccpos = {
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 134, 135,
+ },
+ .oobfree = { {2, 6}, {136, 82} },
+};
+
+
+/*
+ * Generic flash bbt descriptors
+ */
+static u8 bbt_pattern[] = {'B', 'b', 't', '0' };
+static u8 mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+ NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .offs = 2, /* 0 on 8-bit small page */
+ .len = 4,
+ .veroffs = 6,
+ .maxblocks = 4,
+ .pattern = bbt_pattern,
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+ NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .offs = 2, /* 0 on 8-bit small page */
+ .len = 4,
+ .veroffs = 6,
+ .maxblocks = 4,
+ .pattern = mirror_pattern,
+};
+
+/*
+ * Set up the IFC hardware block and page address fields, and the ifc nand
+ * structure addr field to point to the correct IFC buffer in memory
+ */
+static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct fsl_ifc_mtd *priv = chip->priv;
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+ struct fsl_ifc *ifc = ctrl->regs;
+ int buf_num;
+
+ ctrl->page = page_addr;
+
+ /* Program ROW0/COL0 */
+ out_be32(&ifc->ifc_nand.row0, page_addr);
+ out_be32(&ifc->ifc_nand.col0, (oob ? IFC_NAND_COL_MS : 0) | column);
+
+ buf_num = page_addr & priv->bufnum_mask;
+
+ ctrl->addr = priv->vbase + buf_num * (mtd->writesize * 2);
+ ctrl->index = column;
+
+ /* for OOB data point to the second half of the buffer */
+ if (oob)
+ ctrl->index += mtd->writesize;
+}
+
+static int is_blank(struct mtd_info *mtd, struct fsl_ifc_ctrl *ctrl,
+ unsigned int bufnum)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct fsl_ifc_mtd *priv = chip->priv;
+ u8 __iomem *addr = priv->vbase + bufnum * (mtd->writesize * 2);
+ u32 __iomem *main = (u32 *)addr;
+ u8 __iomem *oob = addr + mtd->writesize;
+ int i;
+
+ for (i = 0; i < mtd->writesize / 4; i++) {
+ if (__raw_readl(&main[i]) != 0xffffffff)
+ return 0;
+ }
+
+ for (i = 0; i < chip->ecc.layout->eccbytes; i++) {
+ int pos = chip->ecc.layout->eccpos[i];
+
+ if (__raw_readb(&oob[pos]) != 0xff)
+ return 0;
+ }
+
+ return 1;
+}
+
+/* returns nonzero if entire page is blank */
+static int check_read_ecc(struct mtd_info *mtd, struct fsl_ifc_ctrl *ctrl,
+ u32 *eccstat, unsigned int bufnum)
+{
+ u32 reg = eccstat[bufnum / 4];
+ int errors = (reg >> ((3 - bufnum % 4) * 8)) & 15;
+
+ if (errors == 15) { /* uncorrectable */
+ /* Blank pages fail hw ECC checks */
+ if (is_blank(mtd, ctrl, bufnum))
+ return 1;
+
+ /*
+ * We disable ECCER reporting in hardware due to
+ * erratum IFC-A002770 -- so report it now if we
+ * see an uncorrectable error in ECCSTAT.
+ */
+ ctrl->status |= IFC_NAND_EVTER_STAT_ECCER;
+ } else if (errors > 0) {
+ mtd->ecc_stats.corrected += errors;
+ }
+
+ return 0;
+}
+
+/*
+ * execute IFC NAND command and wait for it to complete
+ */
+static int fsl_ifc_run_command(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct fsl_ifc_mtd *priv = chip->priv;
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+ struct fsl_ifc *ifc = ctrl->regs;
+ long long end_tick;
+ u32 eccstat[4];
+ int i;
+
+ /* set the chip select for NAND Transaction */
+ out_be32(&ifc->ifc_nand.nand_csel, ifc_ctrl->cs_nand);
+
+ /* start read/write seq */
+ out_be32(&ifc->ifc_nand.nandseq_strt,
+ IFC_NAND_SEQ_STRT_FIR_STRT);
+
+ /* wait for NAND Machine complete flag or timeout */
+ end_tick = usec2ticks(IFC_TIMEOUT_MSECS * 1000) + get_ticks();
+
+ while (end_tick > get_ticks()) {
+ ctrl->status = in_be32(&ifc->ifc_nand.nand_evter_stat);
+
+ if (ctrl->status & IFC_NAND_EVTER_STAT_OPC)
+ break;
+ }
+
+ out_be32(&ifc->ifc_nand.nand_evter_stat, ctrl->status);
+
+ if (ctrl->status & IFC_NAND_EVTER_STAT_FTOER)
+ printf("%s: Flash Time Out Error\n", __func__);
+ if (ctrl->status & IFC_NAND_EVTER_STAT_WPER)
+ printf("%s: Write Protect Error\n", __func__);
+
+ if (ctrl->eccread) {
+ int bufperpage = mtd->writesize / 512;
+ int bufnum = (ctrl->page & priv->bufnum_mask) * bufperpage;
+ int bufnum_end = bufnum + bufperpage - 1;
+
+ for (i = bufnum / 4; i <= bufnum_end / 4; i++)
+ eccstat[i] = in_be32(&ifc->ifc_nand.nand_eccstat[i]);
+
+ for (i = bufnum; i <= bufnum_end; i++) {
+ if (check_read_ecc(mtd, ctrl, eccstat, i))
+ break;
+ }
+
+ ctrl->eccread = 0;
+ }
+
+ /* returns 0 on success otherwise non-zero) */
+ return ctrl->status == IFC_NAND_EVTER_STAT_OPC ? 0 : -EIO;
+}
+
+static void fsl_ifc_do_read(struct nand_chip *chip,
+ int oob,
+ struct mtd_info *mtd)
+{
+ struct fsl_ifc_mtd *priv = chip->priv;
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+ struct fsl_ifc *ifc = ctrl->regs;
+
+ /* Program FIR/IFC_NAND_FCR0 for Small/Large page */
+ if (mtd->writesize > 512) {
+ out_be32(&ifc->ifc_nand.nand_fir0,
+ (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
+ (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) |
+ (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP4_SHIFT));
+ out_be32(&ifc->ifc_nand.nand_fir1, 0x0);
+
+ out_be32(&ifc->ifc_nand.nand_fcr0,
+ (NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) |
+ (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT));
+ } else {
+ out_be32(&ifc->ifc_nand.nand_fir0,
+ (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
+ (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP3_SHIFT));
+
+ if (oob)
+ out_be32(&ifc->ifc_nand.nand_fcr0,
+ NAND_CMD_READOOB << IFC_NAND_FCR0_CMD0_SHIFT);
+ else
+ out_be32(&ifc->ifc_nand.nand_fcr0,
+ NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT);
+ }
+}
+
+/* cmdfunc send commands to the IFC NAND Machine */
+static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct fsl_ifc_mtd *priv = chip->priv;
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+ struct fsl_ifc *ifc = ctrl->regs;
+
+ /* clear the read buffer */
+ ctrl->read_bytes = 0;
+ if (command != NAND_CMD_PAGEPROG)
+ ctrl->index = 0;
+
+ switch (command) {
+ /* READ0 read the entire buffer to use hardware ECC. */
+ case NAND_CMD_READ0: {
+ out_be32(&ifc->ifc_nand.nand_fbcr, 0);
+ set_addr(mtd, 0, page_addr, 0);
+
+ ctrl->read_bytes = mtd->writesize + mtd->oobsize;
+ ctrl->index += column;
+
+ if (chip->ecc.mode == NAND_ECC_HW)
+ ctrl->eccread = 1;
+
+ fsl_ifc_do_read(chip, 0, mtd);
+ fsl_ifc_run_command(mtd);
+ return;
+ }
+
+ /* READOOB reads only the OOB because no ECC is performed. */
+ case NAND_CMD_READOOB:
+ out_be32(&ifc->ifc_nand.nand_fbcr, mtd->oobsize - column);
+ set_addr(mtd, column, page_addr, 1);
+
+ ctrl->read_bytes = mtd->writesize + mtd->oobsize;
+
+ fsl_ifc_do_read(chip, 1, mtd);
+ fsl_ifc_run_command(mtd);
+
+ return;
+
+ /* READID must read all possible bytes while CEB is active */
+ case NAND_CMD_READID:
+ out_be32(&ifc->ifc_nand.nand_fir0,
+ (IFC_FIR_OP_CMD0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) |
+ (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT));
+ out_be32(&ifc->ifc_nand.nand_fcr0,
+ NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT);
+ /* 4 bytes for manuf, device and exts */
+ out_be32(&ifc->ifc_nand.nand_fbcr, 4);
+ ctrl->read_bytes = 4;
+
+ set_addr(mtd, 0, 0, 0);
+ fsl_ifc_run_command(mtd);
+ return;
+
+ /* ERASE1 stores the block and page address */
+ case NAND_CMD_ERASE1:
+ set_addr(mtd, 0, page_addr, 0);
+ return;
+
+ /* ERASE2 uses the block and page address from ERASE1 */
+ case NAND_CMD_ERASE2:
+ out_be32(&ifc->ifc_nand.nand_fir0,
+ (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP1_SHIFT) |
+ (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP2_SHIFT));
+
+ out_be32(&ifc->ifc_nand.nand_fcr0,
+ (NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) |
+ (NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT));
+
+ out_be32(&ifc->ifc_nand.nand_fbcr, 0);
+ ctrl->read_bytes = 0;
+ fsl_ifc_run_command(mtd);
+ return;
+
+ /* SEQIN sets up the addr buffer and all registers except the length */
+ case NAND_CMD_SEQIN: {
+ u32 nand_fcr0;
+ ctrl->column = column;
+ ctrl->oob = 0;
+
+ if (mtd->writesize > 512) {
+ nand_fcr0 =
+ (NAND_CMD_SEQIN << IFC_NAND_FCR0_CMD0_SHIFT) |
+ (NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD1_SHIFT);
+
+ out_be32(&ifc->ifc_nand.nand_fir0,
+ (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
+ (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP3_SHIFT) |
+ (IFC_FIR_OP_CW1 << IFC_NAND_FIR0_OP4_SHIFT));
+ out_be32(&ifc->ifc_nand.nand_fir1, 0);
+ } else {
+ nand_fcr0 = ((NAND_CMD_PAGEPROG <<
+ IFC_NAND_FCR0_CMD1_SHIFT) |
+ (NAND_CMD_SEQIN <<
+ IFC_NAND_FCR0_CMD2_SHIFT));
+
+ out_be32(&ifc->ifc_nand.nand_fir0,
+ (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP1_SHIFT) |
+ (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP2_SHIFT) |
+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP3_SHIFT) |
+ (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP4_SHIFT));
+ out_be32(&ifc->ifc_nand.nand_fir1,
+ (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT));
+
+ if (column >= mtd->writesize) {
+ /* OOB area --> READOOB */
+ column -= mtd->writesize;
+ nand_fcr0 |= NAND_CMD_READOOB <<
+ IFC_NAND_FCR0_CMD0_SHIFT;
+ ctrl->oob = 1;
+ } else if (column < 256) {
+ /* First 256 bytes --> READ0 */
+ nand_fcr0 |= NAND_CMD_READ0 << FCR_CMD0_SHIFT;
+ } else {
+ /* Second 256 bytes --> READ1 */
+ nand_fcr0 |= NAND_CMD_READ1 << FCR_CMD0_SHIFT;
+ }
+ }
+
+ out_be32(&ifc->ifc_nand.nand_fcr0, nand_fcr0);
+ set_addr(mtd, column, page_addr, ctrl->oob);
+ return;
+ }
+
+ /* PAGEPROG reuses all of the setup from SEQIN and adds the length */
+ case NAND_CMD_PAGEPROG:
+ if (ctrl->oob)
+ out_be32(&ifc->ifc_nand.nand_fbcr, ctrl->index);
+ else
+ out_be32(&ifc->ifc_nand.nand_fbcr, 0);
+
+ fsl_ifc_run_command(mtd);
+ return;
+
+ case NAND_CMD_STATUS:
+ out_be32(&ifc->ifc_nand.nand_fir0,
+ (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP1_SHIFT));
+ out_be32(&ifc->ifc_nand.nand_fcr0,
+ NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT);
+ out_be32(&ifc->ifc_nand.nand_fbcr, 1);
+ set_addr(mtd, 0, 0, 0);
+ ctrl->read_bytes = 1;
+
+ fsl_ifc_run_command(mtd);
+
+ /* Chip sometimes reporting write protect even when it's not */
+ out_8(ctrl->addr, in_8(ctrl->addr) | NAND_STATUS_WP);
+ return;
+
+ case NAND_CMD_RESET:
+ out_be32(&ifc->ifc_nand.nand_fir0,
+ IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT);
+ out_be32(&ifc->ifc_nand.nand_fcr0,
+ NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT);
+ fsl_ifc_run_command(mtd);
+ return;
+
+ default:
+ printf("%s: error, unsupported command 0x%x.\n",
+ __func__, command);
+ }
+}
+
+/*
+ * Write buf to the IFC NAND Controller Data Buffer
+ */
+static void fsl_ifc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct fsl_ifc_mtd *priv = chip->priv;
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+ unsigned int bufsize = mtd->writesize + mtd->oobsize;
+
+ if (len <= 0) {
+ printf("%s of %d bytes", __func__, len);
+ ctrl->status = 0;
+ return;
+ }
+
+ if ((unsigned int)len > bufsize - ctrl->index) {
+ printf("%s beyond end of buffer "
+ "(%d requested, %u available)\n",
+ __func__, len, bufsize - ctrl->index);
+ len = bufsize - ctrl->index;
+ }
+
+ memcpy_toio(&ctrl->addr[ctrl->index], buf, len);
+ ctrl->index += len;
+}
+
+/*
+ * read a byte from either the IFC hardware buffer if it has any data left
+ * otherwise issue a command to read a single byte.
+ */
+static u8 fsl_ifc_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct fsl_ifc_mtd *priv = chip->priv;
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+
+ /* If there are still bytes in the IFC buffer, then use the
+ * next byte. */
+ if (ctrl->index < ctrl->read_bytes)
+ return in_8(&ctrl->addr[ctrl->index++]);
+
+ printf("%s beyond end of buffer\n", __func__);
+ return ERR_BYTE;
+}
+
+/*
+ * Read two bytes from the IFC hardware buffer
+ * read function for 16-bit buswith
+ */
+static uint8_t fsl_ifc_read_byte16(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct fsl_ifc_mtd *priv = chip->priv;
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+ uint16_t data;
+
+ /*
+ * If there are still bytes in the IFC buffer, then use the
+ * next byte.
+ */
+ if (ctrl->index < ctrl->read_bytes) {
+ data = in_be16((uint16_t *)&ctrl->
+ addr[ctrl->index]);
+ ctrl->index += 2;
+ return (uint8_t)data;
+ }
+
+ printf("%s beyond end of buffer\n", __func__);
+ return ERR_BYTE;
+}
+
+/*
+ * Read from the IFC Controller Data Buffer
+ */
+static void fsl_ifc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct fsl_ifc_mtd *priv = chip->priv;
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+ int avail;
+
+ if (len < 0)
+ return;
+
+ avail = min((unsigned int)len, ctrl->read_bytes - ctrl->index);
+ memcpy_fromio(buf, &ctrl->addr[ctrl->index], avail);
+ ctrl->index += avail;
+
+ if (len > avail)
+ printf("%s beyond end of buffer "
+ "(%d requested, %d available)\n",
+ __func__, len, avail);
+}
+
+/*
+ * Verify buffer against the IFC Controller Data Buffer
+ */
+static int fsl_ifc_verify_buf(struct mtd_info *mtd,
+ const u_char *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct fsl_ifc_mtd *priv = chip->priv;
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+ int i;
+
+ if (len < 0) {
+ printf("%s of %d bytes", __func__, len);
+ return -EINVAL;
+ }
+
+ if ((unsigned int)len > ctrl->read_bytes - ctrl->index) {
+ printf("%s beyond end of buffer "
+ "(%d requested, %u available)\n",
+ __func__, len, ctrl->read_bytes - ctrl->index);
+
+ ctrl->index = ctrl->read_bytes;
+ return -EINVAL;
+ }
+
+ for (i = 0; i < len; i++)
+ if (in_8(&ctrl->addr[ctrl->index + i]) != buf[i])
+ break;
+
+ ctrl->index += len;
+ return i == len && ctrl->status == IFC_NAND_EVTER_STAT_OPC ? 0 : -EIO;
+}
+
+/* This function is called after Program and Erase Operations to
+ * check for success or failure.
+ */
+static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip)
+{
+ struct fsl_ifc_mtd *priv = chip->priv;
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+ struct fsl_ifc *ifc = ctrl->regs;
+ u32 nand_fsr;
+
+ if (ctrl->status != IFC_NAND_EVTER_STAT_OPC)
+ return NAND_STATUS_FAIL;
+
+ /* Use READ_STATUS command, but wait for the device to be ready */
+ out_be32(&ifc->ifc_nand.nand_fir0,
+ (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR0_OP1_SHIFT));
+ out_be32(&ifc->ifc_nand.nand_fcr0, NAND_CMD_STATUS <<
+ IFC_NAND_FCR0_CMD0_SHIFT);
+ out_be32(&ifc->ifc_nand.nand_fbcr, 1);
+ set_addr(mtd, 0, 0, 0);
+ ctrl->read_bytes = 1;
+
+ fsl_ifc_run_command(mtd);
+
+ if (ctrl->status != IFC_NAND_EVTER_STAT_OPC)
+ return NAND_STATUS_FAIL;
+
+ nand_fsr = in_be32(&ifc->ifc_nand.nand_fsr);
+
+ /* Chip sometimes reporting write protect even when it's not */
+ nand_fsr = nand_fsr | NAND_STATUS_WP;
+ return nand_fsr;
+}
+
+static int fsl_ifc_read_page(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ uint8_t *buf, int page)
+{
+ struct fsl_ifc_mtd *priv = chip->priv;
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+
+ fsl_ifc_read_buf(mtd, buf, mtd->writesize);
+ fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ if (ctrl->status != IFC_NAND_EVTER_STAT_OPC)
+ mtd->ecc_stats.failed++;
+
+ return 0;
+}
+
+/* ECC will be calculated automatically, and errors will be detected in
+ * waitfunc.
+ */
+static void fsl_ifc_write_page(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ const uint8_t *buf)
+{
+ fsl_ifc_write_buf(mtd, buf, mtd->writesize);
+ fsl_ifc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
+}
+
+static void fsl_ifc_ctrl_init(void)
+{
+ ifc_ctrl = kzalloc(sizeof(*ifc_ctrl), GFP_KERNEL);
+ if (!ifc_ctrl)
+ return;
+
+ ifc_ctrl->regs = IFC_BASE_ADDR;
+
+ /* clear event registers */
+ out_be32(&ifc_ctrl->regs->ifc_nand.nand_evter_stat, ~0U);
+ out_be32(&ifc_ctrl->regs->ifc_nand.pgrdcmpl_evt_stat, ~0U);
+
+ /* Enable error and event for any detected errors */
+ out_be32(&ifc_ctrl->regs->ifc_nand.nand_evter_en,
+ IFC_NAND_EVTER_EN_OPC_EN |
+ IFC_NAND_EVTER_EN_PGRDCMPL_EN |
+ IFC_NAND_EVTER_EN_FTOER_EN |
+ IFC_NAND_EVTER_EN_WPER_EN);
+
+ out_be32(&ifc_ctrl->regs->ifc_nand.ncfgr, 0x0);
+}
+
+static void fsl_ifc_select_chip(struct mtd_info *mtd, int chip)
+{
+}
+
+int board_nand_init(struct nand_chip *nand)
+{
+ struct fsl_ifc_mtd *priv;
+ struct nand_ecclayout *layout;
+ uint32_t cspr = 0, csor = 0;
+
+ if (!ifc_ctrl) {
+ fsl_ifc_ctrl_init();
+ if (!ifc_ctrl)
+ return -1;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->ctrl = ifc_ctrl;
+ priv->vbase = nand->IO_ADDR_R;
+
+ /* Find which chip select it is connected to.
+ */
+ for (priv->bank = 0; priv->bank < MAX_BANKS; priv->bank++) {
+ phys_addr_t base_addr = virt_to_phys(nand->IO_ADDR_R);
+
+ cspr = in_be32(&ifc_ctrl->regs->cspr_cs[priv->bank].cspr);
+ csor = in_be32(&ifc_ctrl->regs->csor_cs[priv->bank].csor);
+
+ if ((cspr & CSPR_V) && (cspr & CSPR_MSEL) == CSPR_MSEL_NAND &&
+ (cspr & CSPR_BA) == CSPR_PHYS_ADDR(base_addr)) {
+ ifc_ctrl->cs_nand = priv->bank << IFC_NAND_CSEL_SHIFT;
+ break;
+ }
+ }
+
+ if (priv->bank >= MAX_BANKS) {
+ printf("%s: address did not match any "
+ "chip selects\n", __func__);
+ return -ENODEV;
+ }
+
+ ifc_ctrl->chips[priv->bank] = priv;
+
+ /* fill in nand_chip structure */
+ /* set up function call table */
+
+ nand->write_buf = fsl_ifc_write_buf;
+ nand->read_buf = fsl_ifc_read_buf;
+ nand->verify_buf = fsl_ifc_verify_buf;
+ nand->select_chip = fsl_ifc_select_chip;
+ nand->cmdfunc = fsl_ifc_cmdfunc;
+ nand->waitfunc = fsl_ifc_wait;
+
+ /* set up nand options */
+ nand->bbt_td = &bbt_main_descr;
+ nand->bbt_md = &bbt_mirror_descr;
+
+ /* set up nand options */
+ nand->options = NAND_NO_READRDY | NAND_NO_AUTOINCR |
+ NAND_USE_FLASH_BBT;
+
+ if (cspr & CSPR_PORT_SIZE_16) {
+ nand->read_byte = fsl_ifc_read_byte16;
+ nand->options |= NAND_BUSWIDTH_16;
+ } else {
+ nand->read_byte = fsl_ifc_read_byte;
+ }
+
+ nand->controller = &ifc_ctrl->controller;
+ nand->priv = priv;
+
+ nand->ecc.read_page = fsl_ifc_read_page;
+ nand->ecc.write_page = fsl_ifc_write_page;
+
+ /* Hardware generates ECC per 512 Bytes */
+ nand->ecc.size = 512;
+ nand->ecc.bytes = 8;
+
+ switch (csor & CSOR_NAND_PGS_MASK) {
+ case CSOR_NAND_PGS_512:
+ if (nand->options & NAND_BUSWIDTH_16) {
+ layout = &oob_512_16bit_ecc4;
+ } else {
+ layout = &oob_512_8bit_ecc4;
+
+ /* Avoid conflict with bad block marker */
+ bbt_main_descr.offs = 0;
+ bbt_mirror_descr.offs = 0;
+ }
+
+ priv->bufnum_mask = 15;
+ break;
+
+ case CSOR_NAND_PGS_2K:
+ layout = &oob_2048_ecc4;
+ priv->bufnum_mask = 3;
+ break;
+
+ case CSOR_NAND_PGS_4K:
+ if ((csor & CSOR_NAND_ECC_MODE_MASK) ==
+ CSOR_NAND_ECC_MODE_4) {
+ layout = &oob_4096_ecc4;
+ } else {
+ layout = &oob_4096_ecc8;
+ nand->ecc.bytes = 16;
+ }
+
+ priv->bufnum_mask = 1;
+ break;
+
+ default:
+ printf("ifc nand: bad csor %#x: bad page size\n", csor);
+ return -ENODEV;
+ }
+
+ /* Must also set CSOR_NAND_ECC_ENC_EN if DEC_EN set */
+ if (csor & CSOR_NAND_ECC_DEC_EN) {
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.layout = layout;
+ } else {
+ nand->ecc.mode = NAND_ECC_SOFT;
+ }
+
+ return 0;
+}
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 34b4322..1f94360 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -81,6 +81,7 @@ COBJS-$(CONFIG_TIGON3) += bcm570x_autoneg.o
COBJS-$(CONFIG_TIGON3) += 5701rls.o
COBJS-$(CONFIG_DRIVER_TI_EMAC) += davinci_emac.o
COBJS-$(CONFIG_TSEC_ENET) += tsec.o fsl_mdio.o
+COBJS-$(CONFIG_FMAN_ENET) += fsl_mdio.o
COBJS-$(CONFIG_TSI108_ETH) += tsi108_eth.o
COBJS-$(CONFIG_ULI526X) += uli526x.o
COBJS-$(CONFIG_VSC7385_ENET) += vsc7385.o
diff --git a/drivers/net/fm/Makefile b/drivers/net/fm/Makefile
new file mode 100644
index 0000000..072b178
--- /dev/null
+++ b/drivers/net/fm/Makefile
@@ -0,0 +1,62 @@
+#
+# Copyright 2009-2011 Freescale Semiconductor, Inc.
+#
+# 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 $(TOPDIR)/config.mk
+
+LIB := $(obj)libfm.o
+
+ifdef CONFIG_FMAN_ENET
+COBJS-y += dtsec.o
+COBJS-y += eth.o
+COBJS-y += fm.o
+COBJS-y += init.o
+COBJS-y += tgec.o
+COBJS-y += tgec_phy.o
+
+# SoC specific SERDES support
+COBJS-$(CONFIG_P1017) += p1023.o
+COBJS-$(CONFIG_P1023) += p1023.o
+# The P204x, P304x, and P5020 are the same
+COBJS-$(CONFIG_PPC_P2040) += p5020.o
+COBJS-$(CONFIG_PPC_P2041) += p5020.o
+COBJS-$(CONFIG_PPC_P3041) += p5020.o
+COBJS-$(CONFIG_PPC_P3060) += p3060.o
+COBJS-$(CONFIG_PPC_P4080) += p4080.o
+COBJS-$(CONFIG_PPC_P5020) += p5020.o
+endif
+
+COBJS := $(COBJS-y)
+SRCS := $(COBJS:.o=.c)
+OBJS := $(addprefix $(obj),$(COBJS))
+
+all: $(LIB)
+
+$(LIB): $(obj).depend $(OBJS)
+ $(call cmd_link_o_target, $(OBJS))
+
+#########################################################################
+
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
diff --git a/drivers/net/fm/dtsec.c b/drivers/net/fm/dtsec.c
new file mode 100644
index 0000000..a77ee20
--- /dev/null
+++ b/drivers/net/fm/dtsec.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2009-2011 Freescale Semiconductor, Inc.
+ *
+ * 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/types.h>
+#include <asm/io.h>
+#include <asm/fsl_enet.h>
+#include <asm/fsl_dtsec.h>
+#include <fsl_mdio.h>
+#include <phy.h>
+
+#include "fm.h"
+
+#define RCTRL_INIT (RCTRL_GRS | RCTRL_UPROM)
+#define TCTRL_INIT TCTRL_GTS
+#define MACCFG1_INIT MACCFG1_SOFT_RST
+
+#define MACCFG2_INIT (MACCFG2_PRE_LEN(0x7) | MACCFG2_LEN_CHECK | \
+ MACCFG2_PAD_CRC | MACCFG2_FULL_DUPLEX | \
+ MACCFG2_IF_MODE_NIBBLE)
+
+/* MAXFRM - maximum frame length register */
+#define MAXFRM_MASK 0x00003fff
+
+static void dtsec_init_mac(struct fsl_enet_mac *mac)
+{
+ struct dtsec *regs = mac->base;
+
+ /* soft reset */
+ out_be32(&regs->maccfg1, MACCFG1_SOFT_RST);
+ udelay(1000);
+
+ /* clear soft reset, Rx/Tx MAC disable */
+ out_be32(&regs->maccfg1, 0);
+
+ /* graceful stop rx */
+ out_be32(&regs->rctrl, RCTRL_INIT);
+ udelay(1000);
+
+ /* graceful stop tx */
+ out_be32(&regs->tctrl, TCTRL_INIT);
+ udelay(1000);
+
+ /* disable all interrupts */
+ out_be32(&regs->imask, IMASK_MASK_ALL);
+
+ /* clear all events */
+ out_be32(&regs->ievent, IEVENT_CLEAR_ALL);
+
+ /* set the max Rx length */
+ out_be32(&regs->maxfrm, mac->max_rx_len & MAXFRM_MASK);
+
+ /* set the ecntrl to reset value */
+ out_be32(&regs->ecntrl, ECNTRL_DEFAULT);
+
+ /*
+ * Rx length check, no strip CRC for Rx, pad and append CRC for Tx,
+ * full duplex
+ */
+ out_be32(&regs->maccfg2, MACCFG2_INIT);
+}
+
+static void dtsec_enable_mac(struct fsl_enet_mac *mac)
+{
+ struct dtsec *regs = mac->base;
+
+ /* enable Rx/Tx MAC */
+ setbits_be32(&regs->maccfg1, MACCFG1_RXTX_EN);
+
+ /* clear the graceful Rx stop */
+ clrbits_be32(&regs->rctrl, RCTRL_GRS);
+
+ /* clear the graceful Tx stop */
+ clrbits_be32(&regs->tctrl, TCTRL_GTS);
+}
+
+static void dtsec_disable_mac(struct fsl_enet_mac *mac)
+{
+ struct dtsec *regs = mac->base;
+
+ /* graceful Rx stop */
+ setbits_be32(&regs->rctrl, RCTRL_GRS);
+
+ /* graceful Tx stop */
+ setbits_be32(&regs->tctrl, TCTRL_GTS);
+
+ /* disable Rx/Tx MAC */
+ clrbits_be32(&regs->maccfg1, MACCFG1_RXTX_EN);
+}
+
+static void dtsec_set_mac_addr(struct fsl_enet_mac *mac, u8 *mac_addr)
+{
+ struct dtsec *regs = mac->base;
+ u32 mac_addr1, mac_addr2;
+
+ /*
+ * if a station address of 0x12345678ABCD, perform a write to
+ * MACSTNADDR1 of 0xCDAB7856, MACSTNADDR2 of 0x34120000
+ */
+ mac_addr1 = (mac_addr[5] << 24) | (mac_addr[4] << 16) | \
+ (mac_addr[3] << 8) | (mac_addr[2]);
+ out_be32(&regs->macstnaddr1, mac_addr1);
+
+ mac_addr2 = ((mac_addr[1] << 24) | (mac_addr[0] << 16)) & 0xffff0000;
+ out_be32(&regs->macstnaddr2, mac_addr2);
+}
+
+static void dtsec_set_interface_mode(struct fsl_enet_mac *mac,
+ phy_interface_t type, int speed)
+{
+ struct dtsec *regs = mac->base;
+ u32 ecntrl, maccfg2;
+
+ /* clear all bits relative with interface mode */
+ ecntrl = in_be32(&regs->ecntrl);
+ ecntrl &= ~(ECNTRL_TBIM | ECNTRL_GMIIM | ECNTRL_RPM |
+ ECNTRL_R100M | ECNTRL_SGMIIM);
+
+ maccfg2 = in_be32(&regs->maccfg2);
+ maccfg2 &= ~MACCFG2_IF_MODE_MASK;
+
+ if (speed == SPEED_1000)
+ maccfg2 |= MACCFG2_IF_MODE_BYTE;
+ else
+ maccfg2 |= MACCFG2_IF_MODE_NIBBLE;
+
+ /* set interface mode */
+ switch (type) {
+ case PHY_INTERFACE_MODE_GMII:
+ ecntrl |= ECNTRL_GMIIM;
+ break;
+ case PHY_INTERFACE_MODE_RGMII:
+ ecntrl |= (ECNTRL_GMIIM | ECNTRL_RPM);
+ if (speed == SPEED_100)
+ ecntrl |= ECNTRL_R100M;
+ break;
+ case PHY_INTERFACE_MODE_RMII:
+ if (speed == SPEED_100)
+ ecntrl |= ECNTRL_R100M;
+ break;
+ case PHY_INTERFACE_MODE_SGMII:
+ ecntrl |= (ECNTRL_SGMIIM | ECNTRL_TBIM);
+ if (speed == SPEED_100)
+ ecntrl |= ECNTRL_R100M;
+ break;
+ default:
+ break;
+ }
+
+ out_be32(&regs->ecntrl, ecntrl);
+ out_be32(&regs->maccfg2, maccfg2);
+}
+
+void init_dtsec(struct fsl_enet_mac *mac, void *base,
+ void *phyregs, int max_rx_len)
+{
+ mac->base = base;
+ mac->phyregs = NULL;
+ mac->max_rx_len = max_rx_len;
+ mac->init_mac = dtsec_init_mac;
+ mac->enable_mac = dtsec_enable_mac;
+ mac->disable_mac = dtsec_disable_mac;
+ mac->set_mac_addr = dtsec_set_mac_addr;
+ mac->set_if_mode = dtsec_set_interface_mode;
+}
diff --git a/drivers/net/fm/eth.c b/drivers/net/fm/eth.c
new file mode 100644
index 0000000..308d610
--- /dev/null
+++ b/drivers/net/fm/eth.c
@@ -0,0 +1,670 @@
+/*
+ * Copyright 2009-2011 Freescale Semiconductor, Inc.
+ * Dave Liu <daveliu@freescale.com>
+ *
+ * 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>
+#include <malloc.h>
+#include <net.h>
+#include <hwconfig.h>
+#include <fm_eth.h>
+#include <fsl_mdio.h>
+#include <miiphy.h>
+#include <phy.h>
+#include <asm/fsl_dtsec.h>
+#include <asm/fsl_tgec.h>
+
+#include "fm.h"
+
+static struct eth_device *devlist[NUM_FM_PORTS];
+static int num_controllers;
+
+#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) && !defined(BITBANGMII)
+
+#define TBIANA_SETTINGS (TBIANA_ASYMMETRIC_PAUSE | TBIANA_SYMMETRIC_PAUSE | \
+ TBIANA_FULL_DUPLEX)
+
+#define TBIANA_SGMII_ACK 0x4001
+
+#define TBICR_SETTINGS (TBICR_ANEG_ENABLE | TBICR_RESTART_ANEG | \
+ TBICR_FULL_DUPLEX | TBICR_SPEED1_SET)
+
+/* Configure the TBI for SGMII operation */
+void dtsec_configure_serdes(struct fm_eth *priv)
+{
+ struct dtsec *regs = priv->mac->base;
+ struct tsec_mii_mng *phyregs = priv->mac->phyregs;
+
+ /*
+ * Access TBI PHY registers at given TSEC register offset as
+ * opposed to the register offset used for external PHY accesses
+ */
+ tsec_local_mdio_write(phyregs, in_be32(&regs->tbipa), 0, TBI_TBICON,
+ TBICON_CLK_SELECT);
+ tsec_local_mdio_write(phyregs, in_be32(&regs->tbipa), 0, TBI_ANA,
+ TBIANA_SGMII_ACK);
+ tsec_local_mdio_write(phyregs, in_be32(&regs->tbipa), 0,
+ TBI_CR, TBICR_SETTINGS);
+}
+
+static void dtsec_init_phy(struct eth_device *dev)
+{
+ struct fm_eth *fm_eth = dev->priv;
+ struct dtsec *regs = (struct dtsec *)fm_eth->mac->base;
+
+ /* Assign a Physical address to the TBI */
+ out_be32(&regs->tbipa, CONFIG_SYS_TBIPA_VALUE);
+
+ if (fm_eth->enet_if == PHY_INTERFACE_MODE_SGMII)
+ dtsec_configure_serdes(fm_eth);
+}
+
+static int tgec_is_fibre(struct eth_device *dev)
+{
+ struct fm_eth *fm = dev->priv;
+ char phyopt[20];
+
+ sprintf(phyopt, "fsl_fm%d_xaui_phy", fm->fm_index + 1);
+
+ return hwconfig_arg_cmp(phyopt, "xfi");
+}
+#endif
+
+static u16 muram_readw(u16 *addr)
+{
+ u32 base = (u32)addr & ~0x3;
+ u32 val32 = *(u32 *)base;
+ int byte_pos;
+ u16 ret;
+
+ byte_pos = (u32)addr & 0x3;
+ if (byte_pos)
+ ret = (u16)(val32 & 0x0000ffff);
+ else
+ ret = (u16)((val32 & 0xffff0000) >> 16);
+
+ return ret;
+}
+
+static void muram_writew(u16 *addr, u16 val)
+{
+ u32 base = (u32)addr & ~0x3;
+ u32 org32 = *(u32 *)base;
+ u32 val32;
+ int byte_pos;
+
+ byte_pos = (u32)addr & 0x3;
+ if (byte_pos)
+ val32 = (org32 & 0xffff0000) | val;
+ else
+ val32 = (org32 & 0x0000ffff) | ((u32)val << 16);
+
+ *(u32 *)base = val32;
+}
+
+static void bmi_rx_port_disable(struct fm_bmi_rx_port *rx_port)
+{
+ int timeout = 1000000;
+
+ clrbits_be32(&rx_port->fmbm_rcfg, FMBM_RCFG_EN);
+
+ /* wait until the rx port is not busy */
+ while ((in_be32(&rx_port->fmbm_rst) & FMBM_RST_BSY) && timeout--)
+ ;
+}
+
+static void bmi_rx_port_init(struct fm_bmi_rx_port *rx_port)
+{
+ /* set BMI to independent mode, Rx port disable */
+ out_be32(&rx_port->fmbm_rcfg, FMBM_RCFG_IM);
+ /* clear FOF in IM case */
+ out_be32(&rx_port->fmbm_rim, 0);
+ /* Rx frame next engine -RISC */
+ out_be32(&rx_port->fmbm_rfne, NIA_ENG_RISC | NIA_RISC_AC_IM_RX);
+ /* Rx command attribute - no order, MR[3] = 1 */
+ clrbits_be32(&rx_port->fmbm_rfca, FMBM_RFCA_ORDER | FMBM_RFCA_MR_MASK);
+ setbits_be32(&rx_port->fmbm_rfca, FMBM_RFCA_MR(4));
+ /* enable Rx statistic counters */
+ out_be32(&rx_port->fmbm_rstc, FMBM_RSTC_EN);
+ /* disable Rx performance counters */
+ out_be32(&rx_port->fmbm_rpc, 0);
+}
+
+static void bmi_tx_port_disable(struct fm_bmi_tx_port *tx_port)
+{
+ int timeout = 1000000;
+
+ clrbits_be32(&tx_port->fmbm_tcfg, FMBM_TCFG_EN);
+
+ /* wait until the tx port is not busy */
+ while ((in_be32(&tx_port->fmbm_tst) & FMBM_TST_BSY) && timeout--)
+ ;
+}
+
+static void bmi_tx_port_init(struct fm_bmi_tx_port *tx_port)
+{
+ /* set BMI to independent mode, Tx port disable */
+ out_be32(&tx_port->fmbm_tcfg, FMBM_TCFG_IM);
+ /* Tx frame next engine -RISC */
+ out_be32(&tx_port->fmbm_tfne, NIA_ENG_RISC | NIA_RISC_AC_IM_TX);
+ out_be32(&tx_port->fmbm_tfene, NIA_ENG_RISC | NIA_RISC_AC_IM_TX);
+ /* Tx command attribute - no order, MR[3] = 1 */
+ clrbits_be32(&tx_port->fmbm_tfca, FMBM_TFCA_ORDER | FMBM_TFCA_MR_MASK);
+ setbits_be32(&tx_port->fmbm_tfca, FMBM_TFCA_MR(4));
+ /* enable Tx statistic counters */
+ out_be32(&tx_port->fmbm_tstc, FMBM_TSTC_EN);
+ /* disable Tx performance counters */
+ out_be32(&tx_port->fmbm_tpc, 0);
+}
+
+static int fm_eth_rx_port_parameter_init(struct fm_eth *fm_eth)
+{
+ struct fm_port_global_pram *pram;
+ u32 pram_page_offset;
+ void *rx_bd_ring_base;
+ void *rx_buf_pool;
+ struct fm_port_bd *rxbd;
+ struct fm_port_qd *rxqd;
+ struct fm_bmi_rx_port *bmi_rx_port = fm_eth->rx_port;
+ int i;
+
+ /* alloc global parameter ram at MURAM */
+ pram = (struct fm_port_global_pram *)fm_muram_alloc(fm_eth->fm_index,
+ FM_PRAM_SIZE, FM_PRAM_ALIGN);
+ fm_eth->rx_pram = pram;
+
+ /* parameter page offset to MURAM */
+ pram_page_offset = (u32)pram - fm_muram_base(fm_eth->fm_index);
+
+ /* enable global mode- snooping data buffers and BDs */
+ pram->mode = PRAM_MODE_GLOBAL;
+
+ /* init the Rx queue descriptor pionter */
+ pram->rxqd_ptr = pram_page_offset + 0x20;
+
+ /* set the max receive buffer length, power of 2 */
+ muram_writew(&pram->mrblr, MAX_RXBUF_LOG2);
+
+ /* alloc Rx buffer descriptors from main memory */
+ rx_bd_ring_base = malloc(sizeof(struct fm_port_bd)
+ * RX_BD_RING_SIZE);
+ if (!rx_bd_ring_base)
+ return 0;
+ memset(rx_bd_ring_base, 0, sizeof(struct fm_port_bd)
+ * RX_BD_RING_SIZE);
+
+ /* alloc Rx buffer from main memory */
+ rx_buf_pool = malloc(MAX_RXBUF_LEN * RX_BD_RING_SIZE);
+ if (!rx_buf_pool)
+ return 0;
+ memset(rx_buf_pool, 0, MAX_RXBUF_LEN * RX_BD_RING_SIZE);
+
+ /* save them to fm_eth */
+ fm_eth->rx_bd_ring = rx_bd_ring_base;
+ fm_eth->cur_rxbd = rx_bd_ring_base;
+ fm_eth->rx_buf = rx_buf_pool;
+
+ /* init Rx BDs ring */
+ rxbd = (struct fm_port_bd *)rx_bd_ring_base;
+ for (i = 0; i < RX_BD_RING_SIZE; i++) {
+ rxbd->status = RxBD_EMPTY;
+ rxbd->len = 0;
+ rxbd->buf_ptr_hi = 0;
+ rxbd->buf_ptr_lo = (u32)rx_buf_pool + i * MAX_RXBUF_LEN;
+ rxbd++;
+ }
+
+ /* set the Rx queue descriptor */
+ rxqd = &pram->rxqd;
+ muram_writew(&rxqd->gen, 0);
+ muram_writew(&rxqd->bd_ring_base_hi, 0);
+ rxqd->bd_ring_base_lo = (u32)rx_bd_ring_base;
+ muram_writew(&rxqd->bd_ring_size, sizeof(struct fm_port_bd)
+ * RX_BD_RING_SIZE);
+ muram_writew(&rxqd->offset_in, 0);
+ muram_writew(&rxqd->offset_out, 0);
+
+ /* set IM parameter ram pointer to Rx Frame Queue ID */
+ out_be32(&bmi_rx_port->fmbm_rfqid, pram_page_offset);
+
+ return 1;
+}
+
+static int fm_eth_tx_port_parameter_init(struct fm_eth *fm_eth)
+{
+ struct fm_port_global_pram *pram;
+ u32 pram_page_offset;
+ void *tx_bd_ring_base;
+ struct fm_port_bd *txbd;
+ struct fm_port_qd *txqd;
+ struct fm_bmi_tx_port *bmi_tx_port = fm_eth->tx_port;
+ int i;
+
+ /* alloc global parameter ram at MURAM */
+ pram = (struct fm_port_global_pram *)fm_muram_alloc(fm_eth->fm_index,
+ FM_PRAM_SIZE, FM_PRAM_ALIGN);
+ fm_eth->tx_pram = pram;
+
+ /* parameter page offset to MURAM */
+ pram_page_offset = (u32)pram - fm_muram_base(fm_eth->fm_index);
+
+ /* enable global mode- snooping data buffers and BDs */
+ pram->mode = PRAM_MODE_GLOBAL;
+
+ /* init the Tx queue descriptor pionter */
+ pram->txqd_ptr = pram_page_offset + 0x40;
+
+ /* alloc Tx buffer descriptors from main memory */
+ tx_bd_ring_base = malloc(sizeof(struct fm_port_bd)
+ * TX_BD_RING_SIZE);
+ if (!tx_bd_ring_base)
+ return 0;
+ memset(tx_bd_ring_base, 0, sizeof(struct fm_port_bd)
+ * TX_BD_RING_SIZE);
+ /* save it to fm_eth */
+ fm_eth->tx_bd_ring = tx_bd_ring_base;
+ fm_eth->cur_txbd = tx_bd_ring_base;
+
+ /* init Tx BDs ring */
+ txbd = (struct fm_port_bd *)tx_bd_ring_base;
+ for (i = 0; i < TX_BD_RING_SIZE; i++) {
+ txbd->status = TxBD_LAST;
+ txbd->len = 0;
+ txbd->buf_ptr_hi = 0;
+ txbd->buf_ptr_lo = 0;
+ }
+
+ /* set the Tx queue decriptor */
+ txqd = &pram->txqd;
+ muram_writew(&txqd->bd_ring_base_hi, 0);
+ txqd->bd_ring_base_lo = (u32)tx_bd_ring_base;
+ muram_writew(&txqd->bd_ring_size, sizeof(struct fm_port_bd)
+ * TX_BD_RING_SIZE);
+ muram_writew(&txqd->offset_in, 0);
+ muram_writew(&txqd->offset_out, 0);
+
+ /* set IM parameter ram pointer to Tx Confirmation Frame Queue ID */
+ out_be32(&bmi_tx_port->fmbm_tcfqid, pram_page_offset);
+
+ return 1;
+}
+
+static int fm_eth_init(struct fm_eth *fm_eth)
+{
+
+ if (!fm_eth_rx_port_parameter_init(fm_eth))
+ return 0;
+
+ if (!fm_eth_tx_port_parameter_init(fm_eth))
+ return 0;
+
+ return 1;
+}
+
+static int fm_eth_startup(struct fm_eth *fm_eth)
+{
+ struct fsl_enet_mac *mac;
+ mac = fm_eth->mac;
+
+ /* Rx/TxBDs, Rx/TxQDs, Rx buff and parameter ram init */
+ if (!fm_eth_init(fm_eth))
+ return 0;
+ /* setup the MAC controller */
+ mac->init_mac(mac);
+
+ /* For some reason we need to set SPEED_100 */
+ if ((fm_eth->enet_if == PHY_INTERFACE_MODE_SGMII) && mac->set_if_mode)
+ mac->set_if_mode(mac, fm_eth->enet_if, SPEED_100);
+
+ /* init bmi rx port, IM mode and disable */
+ bmi_rx_port_init(fm_eth->rx_port);
+ /* init bmi tx port, IM mode and disable */
+ bmi_tx_port_init(fm_eth->tx_port);
+
+ return 1;
+}
+
+static void fmc_tx_port_graceful_stop_enable(struct fm_eth *fm_eth)
+{
+ struct fm_port_global_pram *pram;
+
+ pram = fm_eth->tx_pram;
+ /* graceful stop transmission of frames */
+ pram->mode |= PRAM_MODE_GRACEFUL_STOP;
+ sync();
+}
+
+static void fmc_tx_port_graceful_stop_disable(struct fm_eth *fm_eth)
+{
+ struct fm_port_global_pram *pram;
+
+ pram = fm_eth->tx_pram;
+ /* re-enable transmission of frames */
+ pram->mode &= ~PRAM_MODE_GRACEFUL_STOP;
+ sync();
+}
+
+static int fm_eth_open(struct eth_device *dev, bd_t *bd)
+{
+ struct fm_eth *fm_eth;
+ struct fsl_enet_mac *mac;
+
+ fm_eth = (struct fm_eth *)dev->priv;
+ mac = fm_eth->mac;
+
+ /* setup the MAC address */
+ if (dev->enetaddr[0] & 0x01) {
+ printf("%s: MacAddress is multcast address\n", __func__);
+ return 1;
+ }
+ mac->set_mac_addr(mac, dev->enetaddr);
+
+ /* enable bmi Rx port */
+ setbits_be32(&fm_eth->rx_port->fmbm_rcfg, FMBM_RCFG_EN);
+ /* enable MAC rx/tx port */
+ mac->enable_mac(mac);
+ /* enable bmi Tx port */
+ setbits_be32(&fm_eth->tx_port->fmbm_tcfg, FMBM_TCFG_EN);
+ /* re-enable transmission of frame */
+ fmc_tx_port_graceful_stop_disable(fm_eth);
+
+#ifdef CONFIG_PHYLIB
+ phy_startup(fm_eth->phydev);
+#else
+ fm_eth->phydev->speed = SPEED_1000;
+ fm_eth->phydev->link = 1;
+ fm_eth->phydev->duplex = DUPLEX_FULL;
+#endif
+
+ /* set the MAC-PHY mode */
+ mac->set_if_mode(mac, fm_eth->enet_if, fm_eth->phydev->speed);
+
+ if (!fm_eth->phydev->link)
+ printf("%s: No link.\n", fm_eth->phydev->dev->name);
+
+ return fm_eth->phydev->link ? 0 : -1;
+}
+
+static void fm_eth_halt(struct eth_device *dev)
+{
+ struct fm_eth *fm_eth;
+ struct fsl_enet_mac *mac;
+
+ fm_eth = (struct fm_eth *)dev->priv;
+ mac = fm_eth->mac;
+
+ /* graceful stop the transmission of frames */
+ fmc_tx_port_graceful_stop_enable(fm_eth);
+ /* disable bmi Tx port */
+ bmi_tx_port_disable(fm_eth->tx_port);
+ /* disable MAC rx/tx port */
+ mac->disable_mac(mac);
+ /* disable bmi Rx port */
+ bmi_rx_port_disable(fm_eth->rx_port);
+
+ phy_shutdown(fm_eth->phydev);
+}
+
+static int fm_eth_send(struct eth_device *dev, volatile void *buf, int len)
+{
+ struct fm_eth *fm_eth;
+ struct fm_port_global_pram *pram;
+ struct fm_port_bd *txbd, *txbd_base;
+ u16 offset_in;
+ int i;
+
+ fm_eth = (struct fm_eth *)dev->priv;
+ pram = fm_eth->tx_pram;
+ txbd = fm_eth->cur_txbd;
+
+ /* find one empty TxBD */
+ for (i = 0; txbd->status & TxBD_READY; i++) {
+ udelay(100);
+ if (i > 0x1000) {
+ printf("%s: Tx buffer not ready\n", dev->name);
+ return 0;
+ }
+ }
+ /* setup TxBD */
+ txbd->buf_ptr_hi = 0;
+ txbd->buf_ptr_lo = (u32)buf;
+ txbd->len = len;
+ sync();
+ txbd->status = TxBD_READY | TxBD_LAST;
+ sync();
+
+ /* update TxQD, let RISC to send the packet */
+ offset_in = muram_readw(&pram->txqd.offset_in);
+ offset_in += sizeof(struct fm_port_bd);
+ if (offset_in >= muram_readw(&pram->txqd.bd_ring_size))
+ offset_in = 0;
+ muram_writew(&pram->txqd.offset_in, offset_in);
+ sync();
+
+ /* wait for buffer to be transmitted */
+ for (i = 0; txbd->status & TxBD_READY; i++) {
+ udelay(100);
+ if (i > 0x10000) {
+ printf("%s: Tx error\n", dev->name);
+ return 0;
+ }
+ }
+
+ /* advance the TxBD */
+ txbd++;
+ txbd_base = (struct fm_port_bd *)fm_eth->tx_bd_ring;
+ if (txbd >= (txbd_base + TX_BD_RING_SIZE))
+ txbd = txbd_base;
+ /* update current txbd */
+ fm_eth->cur_txbd = (void *)txbd;
+
+ return 1;
+}
+
+static int fm_eth_recv(struct eth_device *dev)
+{
+ struct fm_eth *fm_eth;
+ struct fm_port_global_pram *pram;
+ struct fm_port_bd *rxbd, *rxbd_base;
+ u16 status, len;
+ u8 *data;
+ u16 offset_out;
+
+ fm_eth = (struct fm_eth *)dev->priv;
+ pram = fm_eth->rx_pram;
+ rxbd = fm_eth->cur_rxbd;
+ status = rxbd->status;
+
+ while (!(status & RxBD_EMPTY)) {
+ if (!(status & RxBD_ERROR)) {
+ data = (u8 *)rxbd->buf_ptr_lo;
+ len = rxbd->len;
+ NetReceive(data, len);
+ } else {
+ printf("%s: Rx error\n", dev->name);
+ return 0;
+ }
+
+ /* clear the RxBDs */
+ rxbd->status = RxBD_EMPTY;
+ rxbd->len = 0;
+ sync();
+
+ /* advance RxBD */
+ rxbd++;
+ rxbd_base = (struct fm_port_bd *)fm_eth->rx_bd_ring;
+ if (rxbd >= (rxbd_base + RX_BD_RING_SIZE))
+ rxbd = rxbd_base;
+ /* read next status */
+ status = rxbd->status;
+
+ /* update RxQD */
+ offset_out = muram_readw(&pram->rxqd.offset_out);
+ offset_out += sizeof(struct fm_port_bd);
+ if (offset_out >= muram_readw(&pram->rxqd.bd_ring_size))
+ offset_out = 0;
+ muram_writew(&pram->rxqd.offset_out, offset_out);
+ sync();
+ }
+ fm_eth->cur_rxbd = (void *)rxbd;
+
+ return 1;
+}
+
+static int fm_eth_init_mac(struct fm_eth *fm_eth, struct ccsr_fman *reg)
+{
+ struct fsl_enet_mac *mac;
+ int num;
+ void *base, *phyregs = NULL;
+
+ num = fm_eth->num;
+
+ /* Get the mac registers base address */
+ if (fm_eth->type == FM_ETH_1G_E) {
+ base = &reg->mac_1g[num].fm_dtesc;
+ } else {
+ base = &reg->mac_10g[num].fm_10gec;
+ phyregs = &reg->mac_10g[num].fm_10gec_mdio;
+ }
+
+ /* alloc mac controller */
+ mac = malloc(sizeof(struct fsl_enet_mac));
+ if (!mac)
+ return 0;
+ memset(mac, 0, sizeof(struct fsl_enet_mac));
+
+ /* save the mac to fm_eth struct */
+ fm_eth->mac = mac;
+
+ if (fm_eth->type == FM_ETH_1G_E)
+ init_dtsec(mac, base, NULL, MAX_RXBUF_LEN);
+ else
+ init_tgec(mac, base, phyregs, MAX_RXBUF_LEN);
+
+ return 1;
+}
+
+static int init_phy(struct eth_device *dev)
+{
+ struct fm_eth *fm_eth = dev->priv;
+ struct phy_device *phydev = NULL;
+ u32 supported;
+
+#ifdef CONFIG_PHYLIB
+ if (fm_eth->type == FM_ETH_1G_E)
+ dtsec_init_phy(dev);
+
+ if (fm_eth->bus) {
+ phydev = phy_connect(fm_eth->bus, fm_eth->phyaddr, dev,
+ fm_eth->enet_if);
+ }
+
+ if (!phydev) {
+ printf("Failed to connect\n");
+ return -1;
+ }
+
+ if (fm_eth->type == FM_ETH_1G_E) {
+ supported = (SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full |
+ SUPPORTED_1000baseT_Full);
+ } else {
+ supported = SUPPORTED_10000baseT_Full;
+
+ if (tgec_is_fibre(dev))
+ phydev->port = PORT_FIBRE;
+ }
+
+ phydev->supported &= supported;
+ phydev->advertising = phydev->supported;
+
+ fm_eth->phydev = phydev;
+
+ phy_config(phydev);
+#endif
+
+ return 0;
+}
+
+int fm_eth_initialize(struct ccsr_fman *reg, struct fm_eth_info *info)
+{
+ struct eth_device *dev;
+ struct fm_eth *fm_eth;
+ int i, num = info->num;
+
+ /* alloc eth device */
+ dev = (struct eth_device *)malloc(sizeof(struct eth_device));
+ if (!dev)
+ return 0;
+ memset(dev, 0, sizeof(struct eth_device));
+
+ /* alloc the FMan ethernet private struct */
+ fm_eth = (struct fm_eth *)malloc(sizeof(struct fm_eth));
+ if (!fm_eth)
+ return 0;
+ memset(fm_eth, 0, sizeof(struct fm_eth));
+
+ /* save off some things we need from the info struct */
+ fm_eth->fm_index = info->index - 1; /* keep as 0 based for muram */
+ fm_eth->num = num;
+ fm_eth->type = info->type;
+
+ fm_eth->rx_port = (void *)&reg->port[info->rx_port_id - 1].fm_bmi;
+ fm_eth->tx_port = (void *)&reg->port[info->tx_port_id - 1].fm_bmi;
+
+ /* set the ethernet max receive length */
+ fm_eth->max_rx_len = MAX_RXBUF_LEN;
+
+ /* init global mac structure */
+ if (!fm_eth_init_mac(fm_eth, reg))
+ return 0;
+
+ /* keep same as the manual, we call FMAN1, FMAN2, DTSEC1, DTSEC2, etc */
+ if (fm_eth->type == FM_ETH_1G_E)
+ sprintf(dev->name, "FM%d@DTSEC%d", info->index, num + 1);
+ else
+ sprintf(dev->name, "FM%d@TGEC%d", info->index, num + 1);
+
+ devlist[num_controllers++] = dev;
+ dev->iobase = 0;
+ dev->priv = (void *)fm_eth;
+ dev->init = fm_eth_open;
+ dev->halt = fm_eth_halt;
+ dev->send = fm_eth_send;
+ dev->recv = fm_eth_recv;
+ fm_eth->dev = dev;
+ fm_eth->bus = info->bus;
+ fm_eth->phyaddr = info->phy_addr;
+ fm_eth->enet_if = info->enet_if;
+
+ /* startup the FM im */
+ if (!fm_eth_startup(fm_eth))
+ return 0;
+
+ if (init_phy(dev))
+ return 0;
+
+ /* clear the ethernet address */
+ for (i = 0; i < 6; i++)
+ dev->enetaddr[i] = 0;
+ eth_register(dev);
+
+ return 1;
+}
diff --git a/drivers/net/fm/fm.c b/drivers/net/fm/fm.c
new file mode 100644
index 0000000..23ef14b
--- /dev/null
+++ b/drivers/net/fm/fm.c
@@ -0,0 +1,432 @@
+/*
+ * Copyright 2009-2011 Freescale Semiconductor, Inc.
+ * Dave Liu <daveliu@freescale.com>
+ *
+ * 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 <malloc.h>
+#include <asm/io.h>
+#include <asm/errno.h>
+
+#include "fm.h"
+#include "../../qe/qe.h" /* For struct qe_firmware */
+
+#ifdef CONFIG_SYS_QE_FW_IN_NAND
+#include <nand.h>
+#elif defined(CONFIG_SYS_QE_FW_IN_SPIFLASH)
+#include <spi_flash.h>
+#elif defined(CONFIG_SYS_QE_FW_IN_MMC)
+#include <mmc.h>
+#endif
+
+struct fm_muram muram[CONFIG_SYS_NUM_FMAN];
+
+u32 fm_muram_base(int fm_idx)
+{
+ return muram[fm_idx].base;
+}
+
+u32 fm_muram_alloc(int fm_idx, u32 size, u32 align)
+{
+ u32 ret;
+ u32 align_mask, off;
+ u32 save;
+
+ align_mask = align - 1;
+ save = muram[fm_idx].alloc;
+
+ off = save & align_mask;
+ if (off != 0)
+ muram[fm_idx].alloc += (align - off);
+ off = size & align_mask;
+ if (off != 0)
+ size += (align - off);
+ if ((muram[fm_idx].alloc + size) >= muram[fm_idx].top) {
+ muram[fm_idx].alloc = save;
+ printf("%s: run out of ram.\n", __func__);
+ }
+
+ ret = muram[fm_idx].alloc;
+ muram[fm_idx].alloc += size;
+ memset((void *)ret, 0, size);
+
+ return ret;
+}
+
+static void fm_init_muram(int fm_idx, void *reg)
+{
+ u32 base = (u32)reg;
+
+ muram[fm_idx].base = base;
+ muram[fm_idx].size = CONFIG_SYS_FM_MURAM_SIZE;
+ muram[fm_idx].alloc = base + FM_MURAM_RES_SIZE;
+ muram[fm_idx].top = base + CONFIG_SYS_FM_MURAM_SIZE;
+}
+
+/*
+ * fm_upload_ucode - Fman microcode upload worker function
+ *
+ * This function does the actual uploading of an Fman microcode
+ * to an Fman.
+ */
+static void fm_upload_ucode(int fm_idx, struct fm_imem *imem,
+ u32 *ucode, unsigned int size)
+{
+ unsigned int i;
+ unsigned int timeout = 1000000;
+
+ /* enable address auto increase */
+ out_be32(&imem->iadd, IRAM_IADD_AIE);
+ /* write microcode to IRAM */
+ for (i = 0; i < size / 4; i++)
+ out_be32(&imem->idata, ucode[i]);
+
+ /* verify if the writing is over */
+ out_be32(&imem->iadd, 0);
+ while ((in_be32(&imem->idata) != ucode[0]) && --timeout)
+ ;
+ if (!timeout)
+ printf("Fman%u: microcode upload timeout\n", fm_idx + 1);
+
+ /* enable microcode from IRAM */
+ out_be32(&imem->iready, IRAM_READY);
+}
+
+/*
+ * Upload an Fman firmware
+ *
+ * This function is similar to qe_upload_firmware(), exception that it uploads
+ * a microcode to the Fman instead of the QE.
+ *
+ * Because the process for uploading a microcode to the Fman is similar for
+ * that of the QE, the QE firmware binary format is used for Fman microcode.
+ * It should be possible to unify these two functions, but for now we keep them
+ * separate.
+ */
+static int fman_upload_firmware(int fm_idx,
+ struct fm_imem *fm_imem,
+ const struct qe_firmware *firmware)
+{
+ unsigned int i;
+ u32 crc;
+ size_t calc_size = sizeof(struct qe_firmware);
+ size_t length;
+ const struct qe_header *hdr;
+
+ if (!firmware) {
+ printf("Fman%u: Invalid address for firmware\n", fm_idx + 1);
+ return -EINVAL;
+ }
+
+ hdr = &firmware->header;
+ length = be32_to_cpu(hdr->length);
+
+ /* Check the magic */
+ if ((hdr->magic[0] != 'Q') || (hdr->magic[1] != 'E') ||
+ (hdr->magic[2] != 'F')) {
+ printf("Fman%u: Data at %p is not a firmware\n", fm_idx + 1,
+ firmware);
+ return -EPERM;
+ }
+
+ /* Check the version */
+ if (hdr->version != 1) {
+ printf("Fman%u: Unsupported firmware version %u\n", fm_idx + 1,
+ hdr->version);
+ return -EPERM;
+ }
+
+ /* Validate some of the fields */
+ if ((firmware->count != 1)) {
+ printf("Fman%u: Invalid data in firmware header\n", fm_idx + 1);
+ return -EINVAL;
+ }
+
+ /* Validate the length and check if there's a CRC */
+ calc_size += (firmware->count - 1) * sizeof(struct qe_microcode);
+
+ for (i = 0; i < firmware->count; i++)
+ /*
+ * For situations where the second RISC uses the same microcode
+ * as the first, the 'code_offset' and 'count' fields will be
+ * zero, so it's okay to add those.
+ */
+ calc_size += sizeof(u32) *
+ be32_to_cpu(firmware->microcode[i].count);
+
+ /* Validate the length */
+ if (length != calc_size + sizeof(u32)) {
+ printf("Fman%u: Invalid length in firmware header\n",
+ fm_idx + 1);
+ return -EPERM;
+ }
+
+ /*
+ * Validate the CRC. We would normally call crc32_no_comp(), but that
+ * function isn't available unless you turn on JFFS support.
+ */
+ crc = be32_to_cpu(*(u32 *)((void *)firmware + calc_size));
+ if (crc != (crc32(-1, (const void *)firmware, calc_size) ^ -1)) {
+ printf("Fman%u: Firmware CRC is invalid\n", fm_idx + 1);
+ return -EIO;
+ }
+
+ /* Loop through each microcode. */
+ for (i = 0; i < firmware->count; i++) {
+ const struct qe_microcode *ucode = &firmware->microcode[i];
+
+ /* Upload a microcode if it's present */
+ if (ucode->code_offset) {
+ u32 ucode_size;
+ u32 *code;
+ printf("Fman%u: Uploading microcode version %u.%u.%u\n",
+ fm_idx + 1, ucode->major, ucode->minor,
+ ucode->revision);
+ code = (void *)firmware + ucode->code_offset;
+ ucode_size = sizeof(u32) * ucode->count;
+ fm_upload_ucode(fm_idx, fm_imem, code, ucode_size);
+ }
+ }
+
+ return 0;
+}
+
+static u32 fm_assign_risc(int port_id)
+{
+ u32 risc_sel, val;
+ risc_sel = (port_id & 0x1) ? FMFPPRC_RISC2 : FMFPPRC_RISC1;
+ val = (port_id << FMFPPRC_PORTID_SHIFT) & FMFPPRC_PORTID_MASK;
+ val |= ((risc_sel << FMFPPRC_ORA_SHIFT) | risc_sel);
+
+ return val;
+}
+
+static void fm_init_fpm(struct fm_fpm *fpm)
+{
+ int i, port_id;
+ u32 val;
+
+ setbits_be32(&fpm->fmfpee, FMFPEE_EHM | FMFPEE_UEC |
+ FMFPEE_CER | FMFPEE_DER);
+
+ /* IM mode, each even port ID to RISC#1, each odd port ID to RISC#2 */
+
+ /* offline/parser port */
+ for (i = 0; i < MAX_NUM_OH_PORT; i++) {
+ port_id = OH_PORT_ID_BASE + i;
+ val = fm_assign_risc(port_id);
+ out_be32(&fpm->fpmprc, val);
+ }
+ /* Rx 1G port */
+ for (i = 0; i < MAX_NUM_RX_PORT_1G; i++) {
+ port_id = RX_PORT_1G_BASE + i;
+ val = fm_assign_risc(port_id);
+ out_be32(&fpm->fpmprc, val);
+ }
+ /* Tx 1G port */
+ for (i = 0; i < MAX_NUM_TX_PORT_1G; i++) {
+ port_id = TX_PORT_1G_BASE + i;
+ val = fm_assign_risc(port_id);
+ out_be32(&fpm->fpmprc, val);
+ }
+ /* Rx 10G port */
+ port_id = RX_PORT_10G_BASE;
+ val = fm_assign_risc(port_id);
+ out_be32(&fpm->fpmprc, val);
+ /* Tx 10G port */
+ port_id = TX_PORT_10G_BASE;
+ val = fm_assign_risc(port_id);
+ out_be32(&fpm->fpmprc, val);
+
+ /* disable the dispatch limit in IM case */
+ out_be32(&fpm->fpmflc, FMFP_FLC_DISP_LIM_NONE);
+ /* clear events */
+ out_be32(&fpm->fmfpee, FMFPEE_CLEAR_EVENT);
+
+ /* clear risc events */
+ for (i = 0; i < 4; i++)
+ out_be32(&fpm->fpmcev[i], 0xffffffff);
+
+ /* clear error */
+ out_be32(&fpm->fpmrcr, FMFP_RCR_MDEC | FMFP_RCR_IDEC);
+}
+
+static int fm_init_bmi(int fm_idx, struct fm_bmi_common *bmi)
+{
+ int blk, i, port_id;
+ u32 val, offset, base;
+
+ /* alloc free buffer pool in MURAM */
+ base = fm_muram_alloc(fm_idx, FM_FREE_POOL_SIZE, FM_FREE_POOL_ALIGN);
+ if (!base) {
+ printf("%s: no muram for free buffer pool\n", __func__);
+ return -ENOMEM;
+ }
+ offset = base - fm_muram_base(fm_idx);
+
+ /* Need 128KB total free buffer pool size */
+ val = offset / 256;
+ blk = FM_FREE_POOL_SIZE / 256;
+ /* in IM, we must not begin from offset 0 in MURAM */
+ val |= ((blk - 1) << FMBM_CFG1_FBPS_SHIFT);
+ out_be32(&bmi->fmbm_cfg1, val);
+
+ /* disable all BMI interrupt */
+ out_be32(&bmi->fmbm_ier, FMBM_IER_DISABLE_ALL);
+
+ /* clear all events */
+ out_be32(&bmi->fmbm_ievr, FMBM_IEVR_CLEAR_ALL);
+
+ /*
+ * set port parameters - FMBM_PP_x
+ * max tasks 10G Rx/Tx=12, 1G Rx/Tx 4, others is 1
+ * max dma 10G Rx/Tx=3, others is 1
+ * set port FIFO size - FMBM_PFS_x
+ * 4KB for all Rx and Tx ports
+ */
+ /* offline/parser port */
+ for (i = 0; i < MAX_NUM_OH_PORT; i++) {
+ port_id = OH_PORT_ID_BASE + i - 1;
+ /* max tasks=1, max dma=1, no extra */
+ out_be32(&bmi->fmbm_pp[port_id], 0);
+ /* port FIFO size - 256 bytes, no extra */
+ out_be32(&bmi->fmbm_pfs[port_id], 0);
+ }
+ /* Rx 1G port */
+ for (i = 0; i < MAX_NUM_RX_PORT_1G; i++) {
+ port_id = RX_PORT_1G_BASE + i - 1;
+ /* max tasks=4, max dma=1, no extra */
+ out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(4));
+ /* FIFO size - 4KB, no extra */
+ out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf));
+ }
+ /* Tx 1G port FIFO size - 4KB, no extra */
+ for (i = 0; i < MAX_NUM_TX_PORT_1G; i++) {
+ port_id = TX_PORT_1G_BASE + i - 1;
+ /* max tasks=4, max dma=1, no extra */
+ out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(4));
+ /* FIFO size - 4KB, no extra */
+ out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf));
+ }
+ /* Rx 10G port */
+ port_id = RX_PORT_10G_BASE - 1;
+ /* max tasks=12, max dma=3, no extra */
+ out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(12) | FMBM_PP_MXD(3));
+ /* FIFO size - 4KB, no extra */
+ out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf));
+
+ /* Tx 10G port */
+ port_id = TX_PORT_10G_BASE - 1;
+ /* max tasks=12, max dma=3, no extra */
+ out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(12) | FMBM_PP_MXD(3));
+ /* FIFO size - 4KB, no extra */
+ out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf));
+
+ /* initialize internal buffers data base (linked list) */
+ out_be32(&bmi->fmbm_init, FMBM_INIT_START);
+
+ return 0;
+}
+
+static void fm_init_qmi(struct fm_qmi_common *qmi)
+{
+ /* disable enqueue and dequeue of QMI */
+ clrbits_be32(&qmi->fmqm_gc, FMQM_GC_ENQ_EN | FMQM_GC_DEQ_EN);
+
+ /* disable all error interrupts */
+ out_be32(&qmi->fmqm_eien, FMQM_EIEN_DISABLE_ALL);
+ /* clear all error events */
+ out_be32(&qmi->fmqm_eie, FMQM_EIE_CLEAR_ALL);
+
+ /* disable all interrupts */
+ out_be32(&qmi->fmqm_ien, FMQM_IEN_DISABLE_ALL);
+ /* clear all interrupts */
+ out_be32(&qmi->fmqm_ie, FMQM_IE_CLEAR_ALL);
+}
+
+/* Init common part of FM, index is fm num# like fm as above */
+int fm_init_common(int index, struct ccsr_fman *reg)
+{
+ int rc;
+ char env_addr[32];
+#if defined(CONFIG_SYS_FMAN_FW_ADDR)
+ void *addr = (void *)CONFIG_SYS_FMAN_FW_ADDR;
+#elif defined(CONFIG_SYS_QE_FW_IN_NAND)
+ size_t fw_length = CONFIG_SYS_FMAN_FW_LENGTH;
+ void *addr = malloc(CONFIG_SYS_FMAN_FW_LENGTH);
+
+ rc = nand_read(&nand_info[0], (loff_t)CONFIG_SYS_QE_FW_IN_NAND,
+ &fw_length, (u_char *)addr);
+ if (rc == -EUCLEAN) {
+ printf("NAND read of FMAN firmware at offset 0x%x failed %d\n",
+ CONFIG_SYS_QE_FW_IN_NAND, rc);
+ }
+#elif defined(CONFIG_SYS_QE_FW_IN_SPIFLASH)
+ struct spi_flash *ucode_flash;
+ void *addr = malloc(CONFIG_SYS_FMAN_FW_LENGTH);
+ int ret = 0;
+
+ ucode_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS,
+ CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE);
+ if (!ucode_flash)
+ printf("SF: probe for ucode failed\n");
+ else {
+ ret = spi_flash_read(ucode_flash, CONFIG_SYS_QE_FW_IN_SPIFLASH,
+ CONFIG_SYS_FMAN_FW_LENGTH, addr);
+ if (ret)
+ printf("SF: read for ucode failed\n");
+ spi_flash_free(ucode_flash);
+ }
+#elif defined(CONFIG_SYS_QE_FW_IN_MMC)
+ int dev = CONFIG_SYS_MMC_ENV_DEV;
+ void *addr = malloc(CONFIG_SYS_FMAN_FW_LENGTH);
+ u32 cnt = CONFIG_SYS_FMAN_FW_LENGTH / 512;
+ u32 n;
+ u32 blk = CONFIG_SYS_QE_FW_IN_MMC / 512;
+ struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
+
+ if (!mmc)
+ printf("\nMMC cannot find device for ucode\n");
+ else {
+ printf("\nMMC read: dev # %u, block # %u, count %u ...\n",
+ dev, blk, cnt);
+ mmc_init(mmc);
+ n = mmc->block_dev.block_read(dev, blk, cnt, addr);
+ /* flush cache after read */
+ flush_cache((ulong)addr, cnt * 512);
+ }
+#endif
+
+ /* Upload the Fman microcode if it's present */
+ rc = fman_upload_firmware(index, &reg->fm_imem, addr);
+ if (rc)
+ return rc;
+ sprintf(env_addr, "0x%lx", (long unsigned int)addr);
+ setenv("fman_ucode", env_addr);
+
+ fm_init_muram(index, &reg->muram);
+ fm_init_qmi(&reg->fm_qmi_common);
+ fm_init_fpm(&reg->fm_fpm);
+
+ /* clear DMA status */
+ setbits_be32(&reg->fm_dma.fmdmsr, FMDMSR_CLEAR_ALL);
+
+ /* set DMA mode */
+ setbits_be32(&reg->fm_dma.fmdmmr, FMDMMR_SBER);
+
+ return fm_init_bmi(index, &reg->fm_bmi_common);
+}
diff --git a/drivers/net/fm/fm.h b/drivers/net/fm/fm.h
new file mode 100644
index 0000000..228df33
--- /dev/null
+++ b/drivers/net/fm/fm.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2009-2011 Freescale Semiconductor, Inc.
+ *
+ * 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
+ */
+
+#ifndef __FM_H__
+#define __FM_H__
+
+#include <common.h>
+#include <fm_eth.h>
+#include <asm/fsl_enet.h>
+#include <asm/fsl_fman.h>
+
+/* Port ID */
+#define OH_PORT_ID_BASE 0x01
+#define MAX_NUM_OH_PORT 7
+#define RX_PORT_1G_BASE 0x08
+#define MAX_NUM_RX_PORT_1G CONFIG_SYS_NUM_FM1_DTSEC
+#define RX_PORT_10G_BASE 0x10
+#define TX_PORT_1G_BASE 0x28
+#define MAX_NUM_TX_PORT_1G CONFIG_SYS_NUM_FM1_DTSEC
+#define TX_PORT_10G_BASE 0x30
+
+struct fm_muram {
+ u32 base;
+ u32 top;
+ u32 size;
+ u32 alloc;
+};
+#define FM_MURAM_RES_SIZE 0x01000
+
+/* Rx/Tx buffer descriptor */
+struct fm_port_bd {
+ u16 status;
+ u16 len;
+ u32 res0;
+ u16 res1;
+ u16 buf_ptr_hi;
+ u32 buf_ptr_lo;
+};
+
+/* Common BD flags */
+#define BD_LAST 0x0800
+
+/* Rx BD status flags */
+#define RxBD_EMPTY 0x8000
+#define RxBD_LAST BD_LAST
+#define RxBD_FIRST 0x0400
+#define RxBD_PHYS_ERR 0x0008
+#define RxBD_SIZE_ERR 0x0004
+#define RxBD_ERROR (RxBD_PHYS_ERR | RxBD_SIZE_ERR)
+
+/* Tx BD status flags */
+#define TxBD_READY 0x8000
+#define TxBD_LAST BD_LAST
+
+/* Rx/Tx queue descriptor */
+struct fm_port_qd {
+ u16 gen;
+ u16 bd_ring_base_hi;
+ u32 bd_ring_base_lo;
+ u16 bd_ring_size;
+ u16 offset_in;
+ u16 offset_out;
+ u16 res0;
+ u32 res1[0x4];
+};
+
+/* IM global parameter RAM */
+struct fm_port_global_pram {
+ u32 mode; /* independent mode register */
+ u32 rxqd_ptr; /* Rx queue descriptor pointer */
+ u32 txqd_ptr; /* Tx queue descriptor pointer */
+ u16 mrblr; /* max Rx buffer length */
+ u16 rxqd_bsy_cnt; /* RxQD busy counter, should be cleared */
+ u32 res0[0x4];
+ struct fm_port_qd rxqd; /* Rx queue descriptor */
+ struct fm_port_qd txqd; /* Tx queue descriptor */
+ u32 res1[0x28];
+};
+
+#define FM_PRAM_SIZE sizeof(struct fm_port_global_pram)
+#define FM_PRAM_ALIGN 256
+#define PRAM_MODE_GLOBAL 0x20000000
+#define PRAM_MODE_GRACEFUL_STOP 0x00800000
+
+#if defined(CONFIG_P1017) || defined(CONFIG_P1023)
+#define FM_FREE_POOL_SIZE 0x2000 /* 8K bytes */
+#else
+#define FM_FREE_POOL_SIZE 0x20000 /* 128K bytes */
+#endif
+#define FM_FREE_POOL_ALIGN 256
+
+u32 fm_muram_alloc(int fm_idx, u32 size, u32 align);
+u32 fm_muram_base(int fm_idx);
+int fm_init_common(int index, struct ccsr_fman *reg);
+int fm_eth_initialize(struct ccsr_fman *reg, struct fm_eth_info *info);
+phy_interface_t fman_port_enet_if(enum fm_port port);
+void fman_disable_port(enum fm_port port);
+
+struct fsl_enet_mac {
+ void *base; /* MAC controller registers base address */
+ void *phyregs;
+ int max_rx_len;
+ void (*init_mac)(struct fsl_enet_mac *mac);
+ void (*enable_mac)(struct fsl_enet_mac *mac);
+ void (*disable_mac)(struct fsl_enet_mac *mac);
+ void (*set_mac_addr)(struct fsl_enet_mac *mac, u8 *mac_addr);
+ void (*set_if_mode)(struct fsl_enet_mac *mac, phy_interface_t type,
+ int speed);
+};
+
+/* Fman ethernet private struct */
+struct fm_eth {
+ int fm_index; /* Fman index */
+ u32 num; /* 0..n-1 for give type */
+ struct fm_bmi_tx_port *tx_port;
+ struct fm_bmi_rx_port *rx_port;
+ enum fm_eth_type type; /* 1G or 10G ethernet */
+ phy_interface_t enet_if;
+ struct fsl_enet_mac *mac; /* MAC controller */
+ struct mii_dev *bus;
+ struct phy_device *phydev;
+ int phyaddr;
+ struct eth_device *dev;
+ int max_rx_len;
+ struct fm_port_global_pram *rx_pram; /* Rx parameter table */
+ struct fm_port_global_pram *tx_pram; /* Tx parameter table */
+ void *rx_bd_ring; /* Rx BD ring base */
+ void *cur_rxbd; /* current Rx BD */
+ void *rx_buf; /* Rx buffer base */
+ void *tx_bd_ring; /* Tx BD ring base */
+ void *cur_txbd; /* current Tx BD */
+};
+
+#define RX_BD_RING_SIZE 8
+#define TX_BD_RING_SIZE 8
+#define MAX_RXBUF_LOG2 11
+#define MAX_RXBUF_LEN (1 << MAX_RXBUF_LOG2)
+
+#endif /* __FM_H__ */
diff --git a/drivers/net/fm/init.c b/drivers/net/fm/init.c
new file mode 100644
index 0000000..512d7dd
--- /dev/null
+++ b/drivers/net/fm/init.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ *
+ * 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>
+#include <asm/fsl_serdes.h>
+
+#include "fm.h"
+
+struct fm_eth_info fm_info[] = {
+#if (CONFIG_SYS_NUM_FM1_DTSEC >= 1)
+ FM_DTSEC_INFO_INITIALIZER(1, 1),
+#endif
+#if (CONFIG_SYS_NUM_FM1_DTSEC >= 2)
+ FM_DTSEC_INFO_INITIALIZER(1, 2),
+#endif
+#if (CONFIG_SYS_NUM_FM1_DTSEC >= 3)
+ FM_DTSEC_INFO_INITIALIZER(1, 3),
+#endif
+#if (CONFIG_SYS_NUM_FM1_DTSEC >= 4)
+ FM_DTSEC_INFO_INITIALIZER(1, 4),
+#endif
+#if (CONFIG_SYS_NUM_FM1_DTSEC >= 5)
+ FM_DTSEC_INFO_INITIALIZER(1, 5),
+#endif
+#if (CONFIG_SYS_NUM_FM2_DTSEC >= 1)
+ FM_DTSEC_INFO_INITIALIZER(2, 1),
+#endif
+#if (CONFIG_SYS_NUM_FM2_DTSEC >= 2)
+ FM_DTSEC_INFO_INITIALIZER(2, 2),
+#endif
+#if (CONFIG_SYS_NUM_FM2_DTSEC >= 3)
+ FM_DTSEC_INFO_INITIALIZER(2, 3),
+#endif
+#if (CONFIG_SYS_NUM_FM2_DTSEC >= 4)
+ FM_DTSEC_INFO_INITIALIZER(2, 4),
+#endif
+#if (CONFIG_SYS_NUM_FM1_10GEC >= 1)
+ FM_TGEC_INFO_INITIALIZER(1, 1),
+#endif
+#if (CONFIG_SYS_NUM_FM2_10GEC >= 1)
+ FM_TGEC_INFO_INITIALIZER(2, 1),
+#endif
+};
+
+int fm_standard_init(bd_t *bis)
+{
+ int i;
+ struct ccsr_fman *reg;
+
+ reg = (void *)CONFIG_SYS_FSL_FM1_ADDR;
+ if (fm_init_common(0, reg))
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(fm_info); i++) {
+ if ((fm_info[i].enabled) && (fm_info[i].index == 1))
+ fm_eth_initialize(reg, &fm_info[i]);
+ }
+
+#if (CONFIG_SYS_NUM_FMAN == 2)
+ reg = (void *)CONFIG_SYS_FSL_FM2_ADDR;
+ if (fm_init_common(1, reg))
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(fm_info); i++) {
+ if ((fm_info[i].enabled) && (fm_info[i].index == 2))
+ fm_eth_initialize(reg, &fm_info[i]);
+ }
+#endif
+
+ return 1;
+}
+
+/* simple linear search to map from port to array index */
+static int fm_port_to_index(enum fm_port port)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fm_info); i++) {
+ if (fm_info[i].port == port)
+ return i;
+ }
+
+ return -1;
+}
+
+/*
+ * Determine if an interface is actually active based on HW config
+ * we expect fman_port_enet_if() to report PHY_INTERFACE_MODE_NONE if
+ * the interface is not active based on HW cfg of the SoC
+ */
+void fman_enet_init(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fm_info); i++) {
+ phy_interface_t enet_if;
+
+ enet_if = fman_port_enet_if(fm_info[i].port);
+ if (enet_if != PHY_INTERFACE_MODE_NONE) {
+ fm_info[i].enabled = 1;
+ fm_info[i].enet_if = enet_if;
+ } else {
+ fm_info[i].enabled = 0;
+ }
+ }
+
+ return ;
+}
+
+void fm_disable_port(enum fm_port port)
+{
+ int i = fm_port_to_index(port);
+
+ fm_info[i].enabled = 0;
+ fman_disable_port(port);
+}
+
+void fm_info_set_mdio(enum fm_port port, struct mii_dev *bus)
+{
+ int i = fm_port_to_index(port);
+
+ if (i == -1)
+ return;
+
+ fm_info[i].bus = bus;
+}
+
+void fm_info_set_phy_address(enum fm_port port, int address)
+{
+ int i = fm_port_to_index(port);
+
+ if (i == -1)
+ return;
+
+ fm_info[i].phy_addr = address;
+}
+
+/*
+ * Returns the type of the data interface between the given MAC and its PHY.
+ * This is typically determined by the RCW.
+ */
+phy_interface_t fm_info_get_enet_if(enum fm_port port)
+{
+ int i = fm_port_to_index(port);
+
+ if (i == -1)
+ return PHY_INTERFACE_MODE_NONE;
+
+ if (fm_info[i].enabled)
+ return fm_info[i].enet_if;
+
+ return PHY_INTERFACE_MODE_NONE;
+}
+
+static void
+__def_board_ft_fman_fixup_port(void *blob, char * prop, phys_addr_t pa,
+ enum fm_port port, int offset)
+{
+ return ;
+}
+
+void board_ft_fman_fixup_port(void *blob, char * prop, phys_addr_t pa,
+ enum fm_port port, int offset)
+ __attribute__((weak, alias("__def_board_ft_fman_fixup_port")));
+
+static void ft_fixup_port(void *blob, struct fm_eth_info *info, char *prop)
+{
+ int off, ph;
+ phys_addr_t paddr = CONFIG_SYS_CCSRBAR_PHYS + info->compat_offset;
+
+ off = fdt_node_offset_by_compat_reg(blob, prop, paddr);
+
+ if (info->enabled) {
+ fdt_fixup_phy_connection(blob, off, info->enet_if);
+ board_ft_fman_fixup_port(blob, prop, paddr, info->port, off);
+ return ;
+ }
+
+ /* board code might have caused offset to change */
+ off = fdt_node_offset_by_compat_reg(blob, prop, paddr);
+
+ /* disable both the mac node and the node that has a handle to it */
+ fdt_setprop_string(blob, off, "status", "disabled");
+
+ ph = fdt_get_phandle(blob, off);
+ do_fixup_by_prop(blob, "fsl,fman-mac", &ph, sizeof(ph),
+ "status", "disabled", strlen("disabled") + 1, 1);
+}
+
+void fdt_fixup_fman_ethernet(void *blob)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fm_info); i++) {
+ if (fm_info[i].type == FM_ETH_1G_E)
+ ft_fixup_port(blob, &fm_info[i], "fsl,fman-1g-mac");
+ else
+ ft_fixup_port(blob, &fm_info[i], "fsl,fman-10g-mac");
+ }
+}
diff --git a/drivers/net/fm/p1023.c b/drivers/net/fm/p1023.c
new file mode 100644
index 0000000..b17dc40
--- /dev/null
+++ b/drivers/net/fm/p1023.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ *
+ * 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 <phy.h>
+#include <fm_eth.h>
+#include <asm/io.h>
+#include <asm/immap_85xx.h>
+#include <asm/fsl_serdes.h>
+
+u32 port_to_devdisr[] = {
+ [FM1_DTSEC1] = MPC85xx_DEVDISR_TSEC1,
+ [FM1_DTSEC2] = MPC85xx_DEVDISR_TSEC2,
+};
+
+static int is_device_disabled(enum fm_port port)
+{
+ ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
+ u32 devdisr = in_be32(&gur->devdisr);
+
+ return port_to_devdisr[port] & devdisr;
+}
+
+void fman_disable_port(enum fm_port port)
+{
+ ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
+ setbits_be32(&gur->devdisr, port_to_devdisr[port]);
+}
+
+phy_interface_t fman_port_enet_if(enum fm_port port)
+{
+ ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
+ u32 pordevsr = in_be32(&gur->pordevsr);
+
+ if (is_device_disabled(port))
+ return PHY_INTERFACE_MODE_NONE;
+
+ /* DTSEC1 can be SGMII, RGMII or RMII */
+ if (port == FM1_DTSEC1) {
+ if (is_serdes_configured(SGMII_FM1_DTSEC1))
+ return PHY_INTERFACE_MODE_SGMII;
+ if (pordevsr & MPC85xx_PORDEVSR_SGMII1_DIS) {
+ if (pordevsr & MPC85xx_PORDEVSR_TSEC1_PRTC)
+ return PHY_INTERFACE_MODE_RGMII;
+ else
+ return PHY_INTERFACE_MODE_RMII;
+ }
+ }
+
+ /* DTSEC2 only supports SGMII or RGMII */
+ if (port == FM1_DTSEC2) {
+ if (is_serdes_configured(SGMII_FM1_DTSEC2))
+ return PHY_INTERFACE_MODE_SGMII;
+ if (pordevsr & MPC85xx_PORDEVSR_SGMII2_DIS)
+ return PHY_INTERFACE_MODE_RGMII;
+ }
+
+ return PHY_INTERFACE_MODE_NONE;
+}
diff --git a/drivers/net/fm/p3060.c b/drivers/net/fm/p3060.c
new file mode 100644
index 0000000..b25bca7
--- /dev/null
+++ b/drivers/net/fm/p3060.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ *
+ * 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 <phy.h>
+#include <fm_eth.h>
+#include <asm/io.h>
+#include <asm/immap_85xx.h>
+#include <asm/fsl_serdes.h>
+
+u32 port_to_devdisr[] = {
+ [FM1_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC1_1,
+ [FM1_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC1_2,
+ [FM1_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC1_3,
+ [FM1_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC1_4,
+ [FM2_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC2_1,
+ [FM2_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC2_2,
+ [FM2_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC2_3,
+ [FM2_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC2_4,
+};
+
+static int is_device_disabled(enum fm_port port)
+{
+ ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
+ u32 devdisr2 = in_be32(&gur->devdisr2);
+
+ return port_to_devdisr[port] & devdisr2;
+}
+
+void fman_disable_port(enum fm_port port)
+{
+ ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
+ setbits_be32(&gur->devdisr2, port_to_devdisr[port]);
+}
+
+phy_interface_t fman_port_enet_if(enum fm_port port)
+{
+ ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
+ u32 rcwsr11 = in_be32(&gur->rcwsr[11]);
+ u32 rcwsr13 = in_be32(&gur->rcwsr[13]);
+
+ if (is_device_disabled(port))
+ return PHY_INTERFACE_MODE_NONE;
+
+ /* handle RGMII/MII first */
+ if ((port == FM1_DTSEC1) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC1) ==
+ FSL_CORENET_RCWSR11_EC1_FM1_DTSEC1))
+ return PHY_INTERFACE_MODE_RGMII;
+
+ if ((port == FM1_DTSEC2) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC2) ==
+ FSL_CORENET_RCWSR11_EC2_FM1_DTSEC2))
+ return PHY_INTERFACE_MODE_RGMII;
+
+ if ((port == FM2_DTSEC1) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC2) ==
+ FSL_CORENET_RCWSR11_EC2_FM2_DTSEC1))
+ return PHY_INTERFACE_MODE_RGMII;
+
+ if ((port == FM1_DTSEC4) && ((rcwsr13 & FSL_CORENET_RCWSR13_EC1_EXT) ==
+ FSL_CORENET_RCWSR13_EC1_EXT_FM1_DTSEC4_RGMII))
+ return PHY_INTERFACE_MODE_RGMII;
+
+ if ((port == FM1_DTSEC4) && ((rcwsr13 & FSL_CORENET_RCWSR13_EC1_EXT) ==
+ FSL_CORENET_RCWSR13_EC1_EXT_FM1_DTSEC4_MII))
+ return PHY_INTERFACE_MODE_MII;
+
+ if ((port == FM2_DTSEC4) && ((rcwsr13 & FSL_CORENET_RCWSR13_EC2_EXT) ==
+ FSL_CORENET_RCWSR13_EC2_EXT_FM2_DTSEC4_RGMII))
+ return PHY_INTERFACE_MODE_RGMII;
+
+ if ((port == FM2_DTSEC4) && ((rcwsr13 & FSL_CORENET_RCWSR13_EC2_EXT) ==
+ FSL_CORENET_RCWSR13_EC2_EXT_FM2_DTSEC4_MII))
+ return PHY_INTERFACE_MODE_MII;
+
+ switch (port) {
+ case FM1_DTSEC1:
+ case FM1_DTSEC2:
+ case FM1_DTSEC3:
+ case FM1_DTSEC4:
+ if (is_serdes_configured(SGMII_FM1_DTSEC1 + port - FM1_DTSEC1))
+ return PHY_INTERFACE_MODE_SGMII;
+ break;
+ case FM2_DTSEC1:
+ case FM2_DTSEC2:
+ case FM2_DTSEC3:
+ case FM2_DTSEC4:
+ if (is_serdes_configured(SGMII_FM2_DTSEC1 + port - FM2_DTSEC1))
+ return PHY_INTERFACE_MODE_SGMII;
+ break;
+ default:
+ return PHY_INTERFACE_MODE_NONE;
+ }
+
+ return PHY_INTERFACE_MODE_NONE;
+}
diff --git a/drivers/net/fm/p4080.c b/drivers/net/fm/p4080.c
new file mode 100644
index 0000000..791caab
--- /dev/null
+++ b/drivers/net/fm/p4080.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ *
+ * 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 <phy.h>
+#include <fm_eth.h>
+#include <asm/io.h>
+#include <asm/immap_85xx.h>
+#include <asm/fsl_serdes.h>
+
+u32 port_to_devdisr[] = {
+ [FM1_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC1_1,
+ [FM1_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC1_2,
+ [FM1_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC1_3,
+ [FM1_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC1_4,
+ [FM1_10GEC1] = FSL_CORENET_DEVDISR2_10GEC1,
+ [FM2_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC2_1,
+ [FM2_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC2_2,
+ [FM2_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC2_3,
+ [FM2_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC2_4,
+ [FM2_10GEC1] = FSL_CORENET_DEVDISR2_10GEC2,
+};
+
+static int is_device_disabled(enum fm_port port)
+{
+ ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
+ u32 devdisr2 = in_be32(&gur->devdisr2);
+
+ return port_to_devdisr[port] & devdisr2;
+}
+
+void fman_disable_port(enum fm_port port)
+{
+ ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
+ setbits_be32(&gur->devdisr2, port_to_devdisr[port]);
+}
+
+phy_interface_t fman_port_enet_if(enum fm_port port)
+{
+ ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
+ u32 rcwsr11 = in_be32(&gur->rcwsr[11]);
+
+ if (is_device_disabled(port))
+ return PHY_INTERFACE_MODE_NONE;
+
+ if ((port == FM1_10GEC1) && (is_serdes_configured(XAUI_FM1)))
+ return PHY_INTERFACE_MODE_XGMII;
+
+ if ((port == FM2_10GEC1) && (is_serdes_configured(XAUI_FM2)))
+ return PHY_INTERFACE_MODE_XGMII;
+
+ /* handle RGMII first */
+ if ((port == FM1_DTSEC1) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC1) ==
+ FSL_CORENET_RCWSR11_EC1_FM1_DTSEC1))
+ return PHY_INTERFACE_MODE_RGMII;
+
+ if ((port == FM1_DTSEC2) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC2) ==
+ FSL_CORENET_RCWSR11_EC2_FM1_DTSEC2))
+ return PHY_INTERFACE_MODE_RGMII;
+
+ if ((port == FM2_DTSEC1) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC2) ==
+ FSL_CORENET_RCWSR11_EC2_FM2_DTSEC1))
+ return PHY_INTERFACE_MODE_RGMII;
+
+ switch (port) {
+ case FM1_DTSEC1:
+ case FM1_DTSEC2:
+ case FM1_DTSEC3:
+ case FM1_DTSEC4:
+ if (is_serdes_configured(SGMII_FM1_DTSEC1 + port - FM1_DTSEC1))
+ return PHY_INTERFACE_MODE_SGMII;
+ break;
+ case FM2_DTSEC1:
+ case FM2_DTSEC2:
+ case FM2_DTSEC3:
+ case FM2_DTSEC4:
+ if (is_serdes_configured(SGMII_FM2_DTSEC1 + port - FM2_DTSEC1))
+ return PHY_INTERFACE_MODE_SGMII;
+ break;
+ default:
+ return PHY_INTERFACE_MODE_NONE;
+ }
+
+ return PHY_INTERFACE_MODE_NONE;
+}
diff --git a/drivers/net/fm/p5020.c b/drivers/net/fm/p5020.c
new file mode 100644
index 0000000..69c27d2
--- /dev/null
+++ b/drivers/net/fm/p5020.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ *
+ * 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 <phy.h>
+#include <fm_eth.h>
+#include <asm/io.h>
+#include <asm/immap_85xx.h>
+#include <asm/fsl_serdes.h>
+
+u32 port_to_devdisr[] = {
+ [FM1_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC1_1,
+ [FM1_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC1_2,
+ [FM1_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC1_3,
+ [FM1_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC1_4,
+ [FM1_DTSEC5] = FSL_CORENET_DEVDISR2_DTSEC1_5,
+ [FM1_10GEC1] = FSL_CORENET_DEVDISR2_10GEC1,
+};
+
+static int is_device_disabled(enum fm_port port)
+{
+ ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
+ u32 devdisr2 = in_be32(&gur->devdisr2);
+
+ return port_to_devdisr[port] & devdisr2;
+}
+
+void fman_disable_port(enum fm_port port)
+{
+ ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
+ setbits_be32(&gur->devdisr2, port_to_devdisr[port]);
+}
+
+phy_interface_t fman_port_enet_if(enum fm_port port)
+{
+ ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
+ u32 rcwsr11 = in_be32(&gur->rcwsr[11]);
+
+ if (is_device_disabled(port))
+ return PHY_INTERFACE_MODE_NONE;
+
+ if ((port == FM1_10GEC1) && (is_serdes_configured(XAUI_FM1)))
+ return PHY_INTERFACE_MODE_XGMII;
+
+ /* handle RGMII first */
+ if ((port == FM1_DTSEC4) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC1) ==
+ FSL_CORENET_RCWSR11_EC1_FM1_DTSEC4_RGMII))
+ return PHY_INTERFACE_MODE_RGMII;
+
+ if ((port == FM1_DTSEC4) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC1) ==
+ FSL_CORENET_RCWSR11_EC1_FM1_DTSEC4_MII))
+ return PHY_INTERFACE_MODE_MII;
+
+ if ((port == FM1_DTSEC5) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC2) ==
+ FSL_CORENET_RCWSR11_EC2_FM1_DTSEC5_RGMII))
+ return PHY_INTERFACE_MODE_RGMII;
+
+ if ((port == FM1_DTSEC5) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC2) ==
+ FSL_CORENET_RCWSR11_EC2_FM1_DTSEC5_MII))
+ return PHY_INTERFACE_MODE_MII;
+
+ switch (port) {
+ case FM1_DTSEC1:
+ case FM1_DTSEC2:
+ case FM1_DTSEC3:
+ case FM1_DTSEC4:
+ case FM1_DTSEC5:
+ if (is_serdes_configured(SGMII_FM1_DTSEC1 + port - FM1_DTSEC1))
+ return PHY_INTERFACE_MODE_SGMII;
+ break;
+ default:
+ return PHY_INTERFACE_MODE_NONE;
+ }
+
+ return PHY_INTERFACE_MODE_NONE;
+}
diff --git a/drivers/net/fm/tgec.c b/drivers/net/fm/tgec.c
new file mode 100644
index 0000000..6c1d471
--- /dev/null
+++ b/drivers/net/fm/tgec.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2009-2011 Freescale Semiconductor, Inc.
+ * Dave Liu <daveliu@freescale.com>
+ *
+ * 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
+ */
+
+/* MAXFRM - maximum frame length */
+#define MAXFRM_MASK 0x0000ffff
+
+#include <common.h>
+#include <phy.h>
+#include <asm/types.h>
+#include <asm/io.h>
+#include <asm/fsl_enet.h>
+#include <asm/fsl_tgec.h>
+
+#include "fm.h"
+
+#define TGEC_CMD_CFG_INIT (TGEC_CMD_CFG_NO_LEN_CHK | \
+ TGEC_CMD_CFG_RX_ER_DISC | \
+ TGEC_CMD_CFG_STAT_CLR | \
+ TGEC_CMD_CFG_PAUSE_IGNORE | \
+ TGEC_CMD_CFG_CRC_FWD)
+#define TGEC_CMD_CFG_FINAL (TGEC_CMD_CFG_NO_LEN_CHK | \
+ TGEC_CMD_CFG_RX_ER_DISC | \
+ TGEC_CMD_CFG_PAUSE_IGNORE | \
+ TGEC_CMD_CFG_CRC_FWD)
+
+static void tgec_init_mac(struct fsl_enet_mac *mac)
+{
+ struct tgec *regs = mac->base;
+
+ /* mask all interrupt */
+ out_be32(&regs->imask, IMASK_MASK_ALL);
+
+ /* clear all events */
+ out_be32(&regs->ievent, IEVENT_CLEAR_ALL);
+
+ /* set the max receive length */
+ out_be32(&regs->maxfrm, mac->max_rx_len & MAXFRM_MASK);
+
+ /*
+ * 1588 disable, insert second mac disable payload length check
+ * disable, normal operation, any rx error frame is discarded, clear
+ * counters, pause frame ignore, no promiscuous, LAN mode Rx CRC no
+ * strip, Tx CRC append, Rx disable and Tx disable
+ */
+ out_be32(&regs->command_config, TGEC_CMD_CFG_INIT);
+ udelay(1000);
+ out_be32(&regs->command_config, TGEC_CMD_CFG_FINAL);
+
+ /* multicast frame reception for the hash entry disable */
+ out_be32(&regs->hashtable_ctrl, 0);
+}
+
+static void tgec_enable_mac(struct fsl_enet_mac *mac)
+{
+ struct tgec *regs = mac->base;
+
+ setbits_be32(&regs->command_config, TGEC_CMD_CFG_RXTX_EN);
+}
+
+static void tgec_disable_mac(struct fsl_enet_mac *mac)
+{
+ struct tgec *regs = mac->base;
+
+ clrbits_be32(&regs->command_config, TGEC_CMD_CFG_RXTX_EN);
+}
+
+static void tgec_set_mac_addr(struct fsl_enet_mac *mac, u8 *mac_addr)
+{
+ struct tgec *regs = mac->base;
+ u32 mac_addr0, mac_addr1;
+
+ /*
+ * if a station address of 0x12345678ABCD, perform a write to
+ * MAC_ADDR0 of 0x78563412, MAC_ADDR1 of 0x0000CDAB
+ */
+ mac_addr0 = (mac_addr[3] << 24) | (mac_addr[2] << 16) | \
+ (mac_addr[1] << 8) | (mac_addr[0]);
+ out_be32(&regs->mac_addr_0, mac_addr0);
+
+ mac_addr1 = ((mac_addr[5] << 8) | mac_addr[4]) & 0x0000ffff;
+ out_be32(&regs->mac_addr_1, mac_addr1);
+}
+
+static void tgec_set_interface_mode(struct fsl_enet_mac *mac,
+ phy_interface_t type, int speed)
+{
+ /* nothing right now */
+ return;
+}
+
+void init_tgec(struct fsl_enet_mac *mac, void *base,
+ void *phyregs, int max_rx_len)
+{
+ mac->base = base;
+ mac->phyregs = phyregs;
+ mac->max_rx_len = max_rx_len;
+ mac->init_mac = tgec_init_mac;
+ mac->enable_mac = tgec_enable_mac;
+ mac->disable_mac = tgec_disable_mac;
+ mac->set_mac_addr = tgec_set_mac_addr;
+ mac->set_if_mode = tgec_set_interface_mode;
+}
diff --git a/drivers/net/fm/tgec_phy.c b/drivers/net/fm/tgec_phy.c
new file mode 100644
index 0000000..2d349ad
--- /dev/null
+++ b/drivers/net/fm/tgec_phy.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2009-2011 Freescale Semiconductor, Inc.
+ * Andy Fleming <afleming@freescale.com>
+ *
+ * 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
+ * Some part is taken from tsec.c
+ */
+#include <common.h>
+#include <miiphy.h>
+#include <phy.h>
+#include <asm/io.h>
+#include <asm/fsl_tgec.h>
+#include <fm_eth.h>
+
+/*
+ * Write value to the PHY for this device to the register at regnum, waiting
+ * until the write is done before it returns. All PHY configuration has to be
+ * done through the TSEC1 MIIM regs
+ */
+int tgec_mdio_write(struct mii_dev *bus, int port_addr, int dev_addr,
+ int regnum, u16 value)
+{
+ u32 mdio_ctl;
+ u32 stat_val;
+ struct tgec_mdio_controller *regs = bus->priv;
+
+ if (dev_addr == MDIO_DEVAD_NONE)
+ return 0;
+
+ /* Wait till the bus is free */
+ stat_val = MDIO_STAT_CLKDIV(100);
+ out_be32(&regs->mdio_stat, stat_val);
+ while ((in_be32(&regs->mdio_stat)) & MDIO_STAT_BSY)
+ ;
+
+ /* Set the port and dev addr */
+ mdio_ctl = MDIO_CTL_PORT_ADDR(port_addr) | MDIO_CTL_DEV_ADDR(dev_addr);
+ out_be32(&regs->mdio_ctl, mdio_ctl);
+
+ /* Set the register address */
+ out_be32(&regs->mdio_addr, regnum & 0xffff);
+
+ /* Wait till the bus is free */
+ while ((in_be32(&regs->mdio_stat)) & MDIO_STAT_BSY)
+ ;
+
+ /* Write the value to the register */
+ out_be32(&regs->mdio_data, MDIO_DATA(value));
+
+ /* Wait till the MDIO write is complete */
+ while ((in_be32(&regs->mdio_data)) & MDIO_DATA_BSY)
+ ;
+
+ return 0;
+}
+
+/*
+ * Reads from register regnum in the PHY for device dev, returning the value.
+ * Clears miimcom first. All PHY configuration has to be done through the
+ * TSEC1 MIIM regs
+ */
+int tgec_mdio_read(struct mii_dev *bus, int port_addr, int dev_addr,
+ int regnum)
+{
+ u32 mdio_ctl;
+ u32 stat_val;
+ struct tgec_mdio_controller *regs = bus->priv;
+
+ if (dev_addr == MDIO_DEVAD_NONE)
+ return 0xffff;
+
+ stat_val = MDIO_STAT_CLKDIV(100);
+ out_be32(&regs->mdio_stat, stat_val);
+ /* Wait till the bus is free */
+ while ((in_be32(&regs->mdio_stat)) & MDIO_STAT_BSY)
+ ;
+
+ /* Set the Port and Device Addrs */
+ mdio_ctl = MDIO_CTL_PORT_ADDR(port_addr) | MDIO_CTL_DEV_ADDR(dev_addr);
+ out_be32(&regs->mdio_ctl, mdio_ctl);
+
+ /* Set the register address */
+ out_be32(&regs->mdio_addr, regnum & 0xffff);
+
+ /* Wait till the bus is free */
+ while ((in_be32(&regs->mdio_stat)) & MDIO_STAT_BSY)
+ ;
+
+ /* Initiate the read */
+ mdio_ctl |= MDIO_CTL_READ;
+ out_be32(&regs->mdio_ctl, mdio_ctl);
+
+ /* Wait till the MDIO write is complete */
+ while ((in_be32(&regs->mdio_data)) & MDIO_DATA_BSY)
+ ;
+
+ /* Return all Fs if nothing was there */
+ if (in_be32(&regs->mdio_stat) & MDIO_STAT_RD_ER)
+ return 0xffff;
+
+ return in_be32(&regs->mdio_data) & 0xffff;
+}
+
+int tgec_mdio_reset(struct mii_dev *bus)
+{
+ return 0;
+}
+
+int fm_tgec_mdio_init(bd_t *bis, struct tgec_mdio_info *info)
+{
+ struct mii_dev *bus = mdio_alloc();
+
+ if (!bus) {
+ printf("Failed to allocate FM TGEC MDIO bus\n");
+ return -1;
+ }
+
+ bus->read = tgec_mdio_read;
+ bus->write = tgec_mdio_write;
+ bus->reset = tgec_mdio_reset;
+ sprintf(bus->name, info->name);
+
+ bus->priv = info->regs;
+
+ return mdio_register(bus);
+}
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
index 6e0043a..5a65d92 100644
--- a/drivers/usb/host/ehci-fsl.c
+++ b/drivers/usb/host/ehci-fsl.c
@@ -1,5 +1,5 @@
/*
- * (C) Copyright 2009 Freescale Semiconductor, Inc.
+ * (C) Copyright 2009, 2011 Freescale Semiconductor, Inc.
*
* (C) Copyright 2008, Excito Elektronik i Sk=E5ne AB
*
@@ -26,6 +26,7 @@
#include <usb.h>
#include <asm/io.h>
#include <usb/ehci-fsl.h>
+#include <hwconfig.h>
#include "ehci.h"
#include "ehci-core.h"
@@ -39,6 +40,11 @@
int ehci_hcd_init(void)
{
struct usb_ehci *ehci;
+ char usb_phy[5];
+ const char *phy_type = NULL;
+ size_t len;
+
+ usb_phy[0] = '\0';
ehci = (struct usb_ehci *)CONFIG_SYS_FSL_USB_ADDR;
hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength);
@@ -52,10 +58,37 @@ int ehci_hcd_init(void)
out_be32(&ehci->snoop2, 0x80000000 | SNOOP_SIZE_2GB);
/* Init phy */
- if (!strcmp(getenv("usb_phy_type"), "utmi"))
- out_le32(&(hcor->or_portsc[0]), PORT_PTS_UTMI);
+ if (hwconfig_sub("usb1", "phy_type"))
+ phy_type = hwconfig_subarg("usb1", "phy_type", &len);
else
+ phy_type = getenv("usb_phy_type");
+
+ if (!phy_type) {
+#ifdef CONFIG_SYS_FSL_USB_INTERNAL_UTMI_PHY
+ /* if none specified assume internal UTMI */
+ strcpy(usb_phy, "utmi");
+ phy_type = usb_phy;
+#else
+ printf("WARNING: USB phy type not defined !!\n");
+ return -1;
+#endif
+ }
+
+ if (!strcmp(phy_type, "utmi")) {
+#if defined(CONFIG_SYS_FSL_USB_INTERNAL_UTMI_PHY)
+ setbits_be32(&ehci->control, PHY_CLK_SEL_UTMI);
+ setbits_be32(&ehci->control, UTMI_PHY_EN);
+ udelay(1000); /* delay required for PHY Clk to appear */
+#endif
+ out_le32(&(hcor->or_portsc[0]), PORT_PTS_UTMI);
+ } else {
+#if defined(CONFIG_SYS_FSL_USB_INTERNAL_UTMI_PHY)
+ clrbits_be32(&ehci->control, UTMI_PHY_EN);
+ setbits_be32(&ehci->control, PHY_CLK_SEL_ULPI);
+ udelay(1000); /* delay required for PHY Clk to appear */
+#endif
out_le32(&(hcor->or_portsc[0]), PORT_PTS_ULPI);
+ }
/* Enable interface. */
setbits_be32(&ehci->control, USB_EN);