From 1b719e66548a50ac763eebf9513bf1e58e8fb6ff Mon Sep 17 00:00:00 2001 From: Ramneek Mehresh Date: Wed, 23 Mar 2011 15:20:43 +0530 Subject: powerpc/85xx: Add ULPI and UTMI USB Phy support for P1010/P1014 Add UTMI and ULPI PHY support for USB controller on qoriq series of processors with internal UTMI PHY implemented, for example P1010/P1014 - Use both getenv() and hwconfig to get USB phy type till getenv() is depricated - Introduce CONFIG_SYS_FSL_USB_INTERNAL_UTMI_PHY to specify if soc has internal UTMI phy Signed-off-by: Ramneek Mehresh Acked-by: Remy Bohmer Signed-off-by: Kumar Gala --- drivers/usb/host/ehci-fsl.c | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) (limited to 'drivers') 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 #include #include +#include #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); -- cgit v1.1 From 52f90dad60d2252ec34c208cae1100bc75201ec7 Mon Sep 17 00:00:00 2001 From: Dipen Dudhat Date: Tue, 22 Mar 2011 09:27:39 +0530 Subject: nand: Freescale Integrated Flash Controller NAND support Add NAND support (including spl) on IFC, such as is found on the p1010. Note that using hardware ECC on IFC with small-page NAND (which is what comes on the p1010rdb reference board) means there will be insufficient OOB space for JFFS2, since IFC does not support 1-bit ECC. UBI should work, as it does not use OOB for anything but ECC. When hardware ECC is not enabled in CSOR, software ECC is now used. Signed-off-by: Dipen Dudhat [scottwood@freescale.com: ECC rework and misc fixes] Signed-off-by: Scott Wood --- drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/fsl_ifc_nand.c | 850 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 851 insertions(+) create mode 100644 drivers/mtd/nand/fsl_ifc_nand.c (limited to 'drivers') diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 8b598f6..3353dcd 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -37,6 +37,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 + * + * 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 +#include + +#include +#include +#include + +#include +#include +#include + +#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; +} -- cgit v1.1 From c916d7c914665347f057bf1506c02c5ac57e3184 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Wed, 13 Apr 2011 08:37:44 -0500 Subject: powerpc/85xx: Add support for FMan ethernet in Independent mode The Frame Manager (FMan) on QorIQ SoCs with DPAA (datapath acceleration architecture) is the ethernet contoller block. Normally it is utilized via Queue Manager (Qman) and Buffer Manager (Bman). However for boot usage the FMan supports a mode similar to QE or CPM ethernet collers called Independent mode. Additionally the FMan block supports multiple 1g and 10g interfaces as a single entity in the system rather than each controller being managed uniquely. This means we have to initialize all of Fman regardless of the number of interfaces we utilize. Different SoCs support different combinations of the number of FMan as well as the number of 1g & 10g interfaces support per Fman. We add support for the following SoCs: * P1023 - 1 Fman, 2x1g * P4080 - 2 Fman, each Fman has 4x1g and 1x10g * P204x/P3041/P5020 - 1 Fman, 5x1g, 1x10g Signed-off-by: Dave Liu Signed-off-by: Andy Fleming Signed-off-by: Timur Tabi Signed-off-by: Roy Zang Signed-off-by: Dai Haruki Signed-off-by: Kim Phillips Signed-off-by: Ioana Radulescu Signed-off-by: Lei Xu Signed-off-by: Mingkai Hu Signed-off-by: Scott Wood Signed-off-by: Shaohui Xie Signed-off-by: Kumar Gala --- drivers/net/Makefile | 1 + drivers/net/fm/Makefile | 61 +++++ drivers/net/fm/dtsec.c | 181 +++++++++++++ drivers/net/fm/eth.c | 670 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/net/fm/fm.c | 432 ++++++++++++++++++++++++++++++ drivers/net/fm/fm.h | 154 +++++++++++ drivers/net/fm/init.c | 208 ++++++++++++++ drivers/net/fm/p1023.c | 68 +++++ drivers/net/fm/p4080.c | 94 +++++++ drivers/net/fm/p5020.c | 85 ++++++ drivers/net/fm/tgec.c | 119 ++++++++ drivers/net/fm/tgec_phy.c | 139 ++++++++++ 12 files changed, 2212 insertions(+) create mode 100644 drivers/net/fm/Makefile create mode 100644 drivers/net/fm/dtsec.c create mode 100644 drivers/net/fm/eth.c create mode 100644 drivers/net/fm/fm.c create mode 100644 drivers/net/fm/fm.h create mode 100644 drivers/net/fm/init.c create mode 100644 drivers/net/fm/p1023.c create mode 100644 drivers/net/fm/p4080.c create mode 100644 drivers/net/fm/p5020.c create mode 100644 drivers/net/fm/tgec.c create mode 100644 drivers/net/fm/tgec_phy.c (limited to 'drivers') diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 819b197..39f036e 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -80,6 +80,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..9692575 --- /dev/null +++ b/drivers/net/fm/Makefile @@ -0,0 +1,61 @@ +# +# 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_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 +#include +#include +#include +#include +#include +#include + +#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(®s->maccfg1, MACCFG1_SOFT_RST); + udelay(1000); + + /* clear soft reset, Rx/Tx MAC disable */ + out_be32(®s->maccfg1, 0); + + /* graceful stop rx */ + out_be32(®s->rctrl, RCTRL_INIT); + udelay(1000); + + /* graceful stop tx */ + out_be32(®s->tctrl, TCTRL_INIT); + udelay(1000); + + /* disable all interrupts */ + out_be32(®s->imask, IMASK_MASK_ALL); + + /* clear all events */ + out_be32(®s->ievent, IEVENT_CLEAR_ALL); + + /* set the max Rx length */ + out_be32(®s->maxfrm, mac->max_rx_len & MAXFRM_MASK); + + /* set the ecntrl to reset value */ + out_be32(®s->ecntrl, ECNTRL_DEFAULT); + + /* + * Rx length check, no strip CRC for Rx, pad and append CRC for Tx, + * full duplex + */ + out_be32(®s->maccfg2, MACCFG2_INIT); +} + +static void dtsec_enable_mac(struct fsl_enet_mac *mac) +{ + struct dtsec *regs = mac->base; + + /* enable Rx/Tx MAC */ + setbits_be32(®s->maccfg1, MACCFG1_RXTX_EN); + + /* clear the graceful Rx stop */ + clrbits_be32(®s->rctrl, RCTRL_GRS); + + /* clear the graceful Tx stop */ + clrbits_be32(®s->tctrl, TCTRL_GTS); +} + +static void dtsec_disable_mac(struct fsl_enet_mac *mac) +{ + struct dtsec *regs = mac->base; + + /* graceful Rx stop */ + setbits_be32(®s->rctrl, RCTRL_GRS); + + /* graceful Tx stop */ + setbits_be32(®s->tctrl, TCTRL_GTS); + + /* disable Rx/Tx MAC */ + clrbits_be32(®s->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(®s->macstnaddr1, mac_addr1); + + mac_addr2 = ((mac_addr[1] << 24) | (mac_addr[0] << 16)) & 0xffff0000; + out_be32(®s->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(®s->ecntrl); + ecntrl &= ~(ECNTRL_TBIM | ECNTRL_GMIIM | ECNTRL_RPM | + ECNTRL_R100M | ECNTRL_SGMIIM); + + maccfg2 = in_be32(®s->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(®s->ecntrl, ecntrl); + out_be32(®s->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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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(®s->tbipa), 0, TBI_TBICON, + TBICON_CLK_SELECT); + tsec_local_mdio_write(phyregs, in_be32(®s->tbipa), 0, TBI_ANA, + TBIANA_SGMII_ACK); + tsec_local_mdio_write(phyregs, in_be32(®s->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(®s->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 = ®->mac_1g[num].fm_dtesc; + } else { + base = ®->mac_10g[num].fm_10gec; + phyregs = ®->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 *)®->port[info->rx_port_id - 1].fm_bmi; + fm_eth->tx_port = (void *)®->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 + * + * 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 +#include +#include +#include + +#include "fm.h" +#include "../../qe/qe.h" /* For struct qe_firmware */ + +#ifdef CONFIG_SYS_QE_FW_IN_NAND +#include +#elif defined(CONFIG_SYS_QE_FW_IN_SPIFLASH) +#include +#elif defined(CONFIG_SYS_QE_FW_IN_MMC) +#include +#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, ®->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, ®->muram); + fm_init_qmi(®->fm_qmi_common); + fm_init_fpm(®->fm_fpm); + + /* clear DMA status */ + setbits_be32(®->fm_dma.fmdmsr, FMDMSR_CLEAR_ALL); + + /* set DMA mode */ + setbits_be32(®->fm_dma.fmdmmr, FMDMMR_SBER); + + return fm_init_bmi(index, ®->fm_bmi_common); +} diff --git a/drivers/net/fm/fm.h b/drivers/net/fm/fm.h new file mode 100644 index 0000000..be6714f --- /dev/null +++ b/drivers/net/fm/fm.h @@ -0,0 +1,154 @@ +/* + * 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 +#include +#include +#include + +/* 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); + +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..5f05ab1 --- /dev/null +++ b/drivers/net/fm/init.c @@ -0,0 +1,208 @@ +/* + * 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 +#include +#include + +#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_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..c196e79 --- /dev/null +++ b/drivers/net/fm/p1023.c @@ -0,0 +1,68 @@ +/* + * 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 +#include +#include +#include +#include +#include + +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; +} + +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/p4080.c b/drivers/net/fm/p4080.c new file mode 100644 index 0000000..6761a2f --- /dev/null +++ b/drivers/net/fm/p4080.c @@ -0,0 +1,94 @@ +/* + * 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 +#include +#include +#include +#include +#include + +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; +} + +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..59638eb --- /dev/null +++ b/drivers/net/fm/p5020.c @@ -0,0 +1,85 @@ +/* + * 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 +#include +#include +#include +#include +#include + +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; +} + +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 + * + * 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 +#include +#include +#include +#include +#include + +#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(®s->imask, IMASK_MASK_ALL); + + /* clear all events */ + out_be32(®s->ievent, IEVENT_CLEAR_ALL); + + /* set the max receive length */ + out_be32(®s->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(®s->command_config, TGEC_CMD_CFG_INIT); + udelay(1000); + out_be32(®s->command_config, TGEC_CMD_CFG_FINAL); + + /* multicast frame reception for the hash entry disable */ + out_be32(®s->hashtable_ctrl, 0); +} + +static void tgec_enable_mac(struct fsl_enet_mac *mac) +{ + struct tgec *regs = mac->base; + + setbits_be32(®s->command_config, TGEC_CMD_CFG_RXTX_EN); +} + +static void tgec_disable_mac(struct fsl_enet_mac *mac) +{ + struct tgec *regs = mac->base; + + clrbits_be32(®s->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(®s->mac_addr_0, mac_addr0); + + mac_addr1 = ((mac_addr[5] << 8) | mac_addr[4]) & 0x0000ffff; + out_be32(®s->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 + * + * 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 +#include +#include +#include +#include +#include + +/* + * 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(®s->mdio_stat, stat_val); + while ((in_be32(®s->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(®s->mdio_ctl, mdio_ctl); + + /* Set the register address */ + out_be32(®s->mdio_addr, regnum & 0xffff); + + /* Wait till the bus is free */ + while ((in_be32(®s->mdio_stat)) & MDIO_STAT_BSY) + ; + + /* Write the value to the register */ + out_be32(®s->mdio_data, MDIO_DATA(value)); + + /* Wait till the MDIO write is complete */ + while ((in_be32(®s->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(®s->mdio_stat, stat_val); + /* Wait till the bus is free */ + while ((in_be32(®s->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(®s->mdio_ctl, mdio_ctl); + + /* Set the register address */ + out_be32(®s->mdio_addr, regnum & 0xffff); + + /* Wait till the bus is free */ + while ((in_be32(®s->mdio_stat)) & MDIO_STAT_BSY) + ; + + /* Initiate the read */ + mdio_ctl |= MDIO_CTL_READ; + out_be32(®s->mdio_ctl, mdio_ctl); + + /* Wait till the MDIO write is complete */ + while ((in_be32(®s->mdio_data)) & MDIO_DATA_BSY) + ; + + /* Return all Fs if nothing was there */ + if (in_be32(®s->mdio_stat) & MDIO_STAT_RD_ER) + return 0xffff; + + return in_be32(®s->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); +} -- cgit v1.1 From 69a852425883a4abd8dc726da34e3149a08ee95d Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Wed, 14 Sep 2011 12:01:35 -0500 Subject: fm-eth: Add ability for board code to disable a port The SoC configuration may have more ports enabled than a given board actually can utilize. Add a routinue that allows the board code to disable a port that it knows isn't being used. fm_disable_port() needs to be called before cpu_eth_init(). Signed-off-by: Kumar Gala --- drivers/net/fm/fm.h | 1 + drivers/net/fm/init.c | 8 ++++++++ drivers/net/fm/p1023.c | 6 ++++++ drivers/net/fm/p4080.c | 6 ++++++ drivers/net/fm/p5020.c | 6 ++++++ 5 files changed, 27 insertions(+) (limited to 'drivers') diff --git a/drivers/net/fm/fm.h b/drivers/net/fm/fm.h index be6714f..228df33 100644 --- a/drivers/net/fm/fm.h +++ b/drivers/net/fm/fm.h @@ -110,6 +110,7 @@ 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 */ diff --git a/drivers/net/fm/init.c b/drivers/net/fm/init.c index 5f05ab1..512d7dd 100644 --- a/drivers/net/fm/init.c +++ b/drivers/net/fm/init.c @@ -123,6 +123,14 @@ void fman_enet_init(void) 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); diff --git a/drivers/net/fm/p1023.c b/drivers/net/fm/p1023.c index c196e79..b17dc40 100644 --- a/drivers/net/fm/p1023.c +++ b/drivers/net/fm/p1023.c @@ -36,6 +36,12 @@ static int is_device_disabled(enum fm_port port) 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); diff --git a/drivers/net/fm/p4080.c b/drivers/net/fm/p4080.c index 6761a2f..791caab 100644 --- a/drivers/net/fm/p4080.c +++ b/drivers/net/fm/p4080.c @@ -44,6 +44,12 @@ static int is_device_disabled(enum fm_port port) 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); diff --git a/drivers/net/fm/p5020.c b/drivers/net/fm/p5020.c index 59638eb..69c27d2 100644 --- a/drivers/net/fm/p5020.c +++ b/drivers/net/fm/p5020.c @@ -40,6 +40,12 @@ static int is_device_disabled(enum fm_port port) 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); -- cgit v1.1 From 6d7b061af153bc5beb633c3bd15348284716a067 Mon Sep 17 00:00:00 2001 From: Shengzhou Liu Date: Wed, 31 Aug 2011 17:48:18 +0800 Subject: powerpc/p3060: Add SoC related support for P3060 platform Add P3060 SoC specific information:cores setup, LIODN setup, etc The P3060 SoC combines six e500mc Power Architecture processor cores with high-performance datapath acceleration architecture(DPAA), CoreNet fabric infrastructure, as well as network and peripheral interfaces. Signed-off-by: Shengzhou Liu Signed-off-by: Kumar Gala --- drivers/net/fm/Makefile | 1 + drivers/net/fm/p3060.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 drivers/net/fm/p3060.c (limited to 'drivers') diff --git a/drivers/net/fm/Makefile b/drivers/net/fm/Makefile index 9692575..072b178 100644 --- a/drivers/net/fm/Makefile +++ b/drivers/net/fm/Makefile @@ -39,6 +39,7 @@ COBJS-$(CONFIG_P1023) += p1023.o 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 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 +#include +#include +#include +#include +#include + +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; +} -- cgit v1.1