From patchwork Thu Jan 27 04:52:43 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mingkai Hu X-Patchwork-Id: 80589 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 03841B7126 for ; Thu, 27 Jan 2011 16:21:41 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id C85D4280D8; Thu, 27 Jan 2011 06:21:03 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id qKvswHS1-4qM; Thu, 27 Jan 2011 06:21:03 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id DC4D0280B2; Thu, 27 Jan 2011 06:20:34 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 113E3280CD for ; Thu, 27 Jan 2011 06:20:21 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id r1iZm7ZvpqE3 for ; Thu, 27 Jan 2011 06:20:15 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from VA3EHSOBE006.bigfish.com (va3ehsobe006.messaging.microsoft.com [216.32.180.16]) by theia.denx.de (Postfix) with ESMTPS id 5E00828087 for ; Thu, 27 Jan 2011 06:20:05 +0100 (CET) Received: from mail88-va3-R.bigfish.com (10.7.14.247) by VA3EHSOBE006.bigfish.com (10.7.40.26) with Microsoft SMTP Server id 14.1.225.8; Thu, 27 Jan 2011 05:20:04 +0000 Received: from mail88-va3 (localhost.localdomain [127.0.0.1]) by mail88-va3-R.bigfish.com (Postfix) with ESMTP id 45A8D1AF014B; Thu, 27 Jan 2011 05:20:04 +0000 (UTC) X-SpamScore: 16 X-BigFish: VS16(zzbb2cKc8kzz1202hzz8275bh8275dhz2dh2a8h668h69ih) X-Spam-TCS-SCL: 8:0 X-Forefront-Antispam-Report: KIP:(null); UIP:(null); IPVD:NLI; H:az33egw02.freescale.net; RD:az33egw02.freescale.net; EFVD:NLI Received: from mail88-va3 (localhost.localdomain [127.0.0.1]) by mail88-va3 (MessageSwitch) id 1296105600189336_19564; Thu, 27 Jan 2011 05:20:00 +0000 (UTC) Received: from VA3EHSMHS019.bigfish.com (unknown [10.7.14.250]) by mail88-va3.bigfish.com (Postfix) with ESMTP id 19AFB1AE0051; Thu, 27 Jan 2011 05:20:00 +0000 (UTC) Received: from az33egw02.freescale.net (192.88.158.103) by VA3EHSMHS019.bigfish.com (10.7.99.29) with Microsoft SMTP Server (TLS) id 14.1.225.8; Thu, 27 Jan 2011 05:19:57 +0000 Received: from az33smr01.freescale.net (az33smr01.freescale.net [10.64.34.199]) by az33egw02.freescale.net (8.14.3/8.14.3) with ESMTP id p0R5Jujm029181; Wed, 26 Jan 2011 22:19:56 -0700 (MST) Received: from localhost (rock.ap.freescale.net [10.193.20.106]) by az33smr01.freescale.net (8.13.1/8.13.0) with ESMTP id p0R5Jq9V006650; Wed, 26 Jan 2011 23:19:52 -0600 (CST) From: Mingkai Hu To: Date: Thu, 27 Jan 2011 12:52:43 +0800 Message-ID: <1296103972-2696-6-git-send-email-Mingkai.hu@freescale.com> X-Mailer: git-send-email 1.6.4 In-Reply-To: <1296103972-2696-5-git-send-email-Mingkai.hu@freescale.com> References: <1296103972-2696-1-git-send-email-Mingkai.hu@freescale.com> <1296103972-2696-2-git-send-email-Mingkai.hu@freescale.com> <1296103972-2696-3-git-send-email-Mingkai.hu@freescale.com> <1296103972-2696-4-git-send-email-Mingkai.hu@freescale.com> <1296103972-2696-5-git-send-email-Mingkai.hu@freescale.com> MIME-Version: 1.0 X-OriginatorOrg: freescale.com Cc: kim.phillips@freescale.com, daveliu@freescale.com, galak@kernel.crashing.org, Emilian.Medve@freescale.com, ruxandra.radulescu@freescale.com, afleming@freescale.com, dai.haruki@freescale.com, timur@freescale.com Subject: [U-Boot] [PATCH 05/14] powerpc/qoirq: Add support for FMan ethernet in Independent mode X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.9 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de From: Dave Liu 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: Kumar Gala Signed-off-by: Mingkai Hu --- Makefile | 1 + arch/powerpc/cpu/mpc85xx/cpu_init.c | 5 + arch/powerpc/cpu/mpc8xxx/cpu.c | 4 + drivers/net/Makefile | 1 + drivers/net/fm/Makefile | 45 ++ drivers/net/fm/fm.c | 519 +++++++++++++++ drivers/net/fm/fm.h | 312 +++++++++ drivers/net/fm/fm_eth.c | 1234 +++++++++++++++++++++++++++++++++++ include/fm_eth.h | 348 ++++++++++ 9 files changed, 2469 insertions(+), 0 deletions(-) create mode 100644 drivers/net/fm/Makefile create mode 100644 drivers/net/fm/fm.c create mode 100644 drivers/net/fm/fm.h create mode 100644 drivers/net/fm/fm_eth.c create mode 100644 include/fm_eth.h diff --git a/Makefile b/Makefile index 5f93646..a0532e0 100644 --- a/Makefile +++ b/Makefile @@ -225,6 +225,7 @@ LIBS += arch/powerpc/cpu/mpc8xxx/lib8xxx.o endif ifeq ($(CPU),mpc85xx) LIBS += drivers/qe/libqe.o +LIBS += drivers/net/fm/libfm.a LIBS += arch/powerpc/cpu/mpc8xxx/ddr/libddr.o LIBS += arch/powerpc/cpu/mpc8xxx/lib8xxx.o endif diff --git a/arch/powerpc/cpu/mpc85xx/cpu_init.c b/arch/powerpc/cpu/mpc85xx/cpu_init.c index 8ece970..0ceec19 100644 --- a/arch/powerpc/cpu/mpc85xx/cpu_init.c +++ b/arch/powerpc/cpu/mpc85xx/cpu_init.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -419,6 +420,10 @@ int cpu_init_r(void) isync(); #endif +#ifdef CONFIG_FMAN_ENET + fman_enet_init(); +#endif + return 0; } diff --git a/arch/powerpc/cpu/mpc8xxx/cpu.c b/arch/powerpc/cpu/mpc8xxx/cpu.c index 4335fb4..97e4fa4 100644 --- a/arch/powerpc/cpu/mpc8xxx/cpu.c +++ b/arch/powerpc/cpu/mpc8xxx/cpu.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -160,5 +161,8 @@ int cpu_eth_init(bd_t *bis) tsec_standard_init(bis); #endif +#ifdef CONFIG_FMAN_ENET + fm_standard_init(bis); +#endif return 0; } diff --git a/drivers/net/Makefile b/drivers/net/Makefile index fd9d0b4..3810665 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 +COBJS-$(CONFIG_FMAN_ENET) += fsl_phy.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..0dc99ca --- /dev/null +++ b/drivers/net/fm/Makefile @@ -0,0 +1,45 @@ +# +# Copyright 2009-2010 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.a + +COBJS-$(CONFIG_FMAN_ENET) += dtsec.o fm.o fm_eth.o tgec.o tgec_phy.o + +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) +CFLAGS := $(CFLAGS) -D__GCC__ + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/drivers/net/fm/fm.c b/drivers/net/fm/fm.c new file mode 100644 index 0000000..ecc4ffc --- /dev/null +++ b/drivers/net/fm/fm.c @@ -0,0 +1,519 @@ +/* + * Copyright 2009-2010 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 */ + +struct fm_global *fman[MAX_NUM_FM]; + +u32 fm_get_base_addr(int fm, enum fm_block block, int port) +{ + u32 addr = 0; + + if (fm == 0) + addr = CONFIG_SYS_FSL_FM1_ADDR; + else if (fm == 1) + addr = CONFIG_SYS_FSL_FM2_ADDR; + + switch (block) { + case fm_muram_e: /* muram */ + addr += 0; + break; + case fm_bmi_e: + addr += 0x80000 + port * 0x1000; + break; + case fm_qmi_e: + addr += 0x80400 + port * 0x1000; + break; + case fm_parser_e: + addr += 0x80800 + port * 0x1000; + break; + case fm_policer_e: + addr += 0xc0000; + break; + case fm_keygen_e: + addr += 0xc1000; + break; + case fm_dma_e: + addr += 0xc2000; + break; + case fm_fpm_e: + addr += 0xc3000; + break; + case fm_imem_e: + addr += 0xc4000; + break; + case fm_soft_paser_e: + addr += 0xc7000; + break; + case fm_mac_e: + addr += port < MAX_NUM_1G_MAC ? (0xe0000 + port * 0x2000) + : 0xf0000; + break; + case fm_mdio_e: + addr += port < MAX_NUM_1G_MAC ? (0xe1120 + port * 0x2000) + : 0xf1000; + break; + case fm_1588_tmr_e: + addr += 0xfe000; + break; + } + return addr; +} + +int fm_get_port_id(enum fm_port_type type, int num) +{ + int port_id, base; + + switch (type) { + case fm_port_type_oh_e: + base = OH_PORT_ID_BASE; + break; + case fm_port_type_rx_e: + base = RX_PORT_1G_BASE; + break; + case fm_port_type_rx_10g_e: + base = RX_PORT_10G_BASE; + break; + case fm_port_type_tx_e: + base = TX_PORT_1G_BASE; + break; + case fm_port_type_tx_10g_e: + base = TX_PORT_10G_BASE; + break; + default: + base = 0; + break; + } + + port_id = base + num; + return port_id; +} + +/** 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, u32 *ucode, unsigned int size) +{ + struct fm_iram *iram; + unsigned int i; + unsigned int timeout = 1000000; + + iram = (struct fm_iram *)fm_get_base_addr(fm, fm_imem_e, 0); + /* enable address auto increase */ + out_be32(&iram->iadd, IRAM_IADD_AIE); + /* write microcode to IRAM */ + for (i = 0; i < size / 4; i++) + out_be32(&iram->idata, ucode[i]); + + /* verify if the writing is over */ + out_be32(&iram->iadd, 0); + while ((in_be32(&iram->idata) != ucode[0]) && --timeout); + + /* enable microcode from IRAM */ + out_be32(&iram->iready, IRAM_READY); +} + +static void fm_init_axi_dma(int fm) +{ + struct fm_dma *axi_dma; + u32 val; + + axi_dma = (struct fm_dma *)fm_get_base_addr(fm, fm_dma_e, 0); + /* clear DMA status */ + val = in_be32(&axi_dma->fmdmsr); + out_be32(&axi_dma->fmdmsr, val | FMDMSR_CLEAR_ALL); + /* set DMA mode */ + val = in_be32(&axi_dma->fmdmmr); + out_be32(&axi_dma->fmdmmr, val | FMDMMR_INIT); + /* set thresholds - high */ + out_be32(&axi_dma->fmdmtr, FMDMTR_DEFAULT); + /* set hysteresis - low */ + out_be32(&axi_dma->fmdmhy, FMDMHY_DEFAULT); + /* set emergency threshold */ + out_be32(&axi_dma->fmdmsetr, FMDMSETR_DEFAULT); +} + +u32 fm_muram_alloc(struct fm_muram *mem, u32 size, u32 align) +{ + u32 ret; + u32 align_mask, off; + u32 save; + + align_mask = align - 1; + save = mem->alloc; + + if ((off = (save & align_mask)) != 0) + mem->alloc += (align - off); + if ((off = size & align_mask) != 0) + size += (align - off); + if ((mem->alloc + size) >= mem->top) { + mem->alloc = save; + printf("%s: run out of ram.\n", __func__); + } + + ret = mem->alloc; + mem->alloc += size; + memset((void *)ret, 0, size); + + return ret; +} + +static void fm_init_muram(int fm, struct fm_muram *mem) +{ + u32 base; + + base = fm_get_base_addr(fm, fm_muram_e, 0); + mem->fm = fm; + mem->base = base; + mem->res_size = FM_MURAM_RES_SIZE; + mem->size = FM_MURAM_SIZE; + mem->alloc = base + FM_MURAM_RES_SIZE; + mem->top = base + FM_MURAM_SIZE; +} + +static void fm_reset(int) __attribute__((__unused__)); +static void fm_reset(int fm) +{ + struct fm_fpm *fpm; + fpm = (struct fm_fpm *)fm_get_base_addr(fm, fm_fpm_e, 0); + + /* reset entire FMAN and MACs */ + out_be32(&fpm->fmrstc, FMRSTC_RESET_FMAN); + udelay(10); +} + +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(int fm) +{ + struct fm_fpm *fpm; + int i, port_id; + u32 val; + + fpm = (struct fm_fpm *)fm_get_base_addr(fm, fm_fpm_e, 0); + + val = in_be32(&fpm->fmnee); + out_be32(&fpm->fmnee, val | 0x0000000f); + + /* 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 = fm_get_port_id(fm_port_type_oh_e, 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 = fm_get_port_id(fm_port_type_rx_e, 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 = fm_get_port_id(fm_port_type_tx_e, i); + val = fm_assign_risc(port_id); + out_be32(&fpm->fpmprc, val); + } + /* Rx 10G port */ + port_id = fm_get_port_id(fm_port_type_rx_10g_e, 0); + val = fm_assign_risc(port_id); + out_be32(&fpm->fpmprc, val); + /* Tx 10G port */ + port_id = fm_get_port_id(fm_port_type_tx_10g_e, 0); + val = fm_assign_risc(port_id); + out_be32(&fpm->fpmprc, val); + + /* disable the dispatch limit in IM case */ + out_be32(&fpm->fpmflc, FMFPFLC_DEFAULT); + /* set the dispatch thresholds */ + out_be32(&fpm->fpmdis1, FMFPDIST1_DEFAULT); + out_be32(&fpm->fpmdis2, FMFPDIST2_DEFAULT); + /* clear events */ + out_be32(&fpm->fmnee, FMFPEE_CLEAR_EVENT); + + /* clear risc events */ + for (i = 0; i < 4; i++) + out_be32(&fpm->fpmcev[i], 0xffffffff); + + /* clear error */ + out_be32(&fpm->fpmrcr, 0x0000c000); +} + +static void fm_init_bmi(int fm, u32 offset, u32 pool_size) +{ + struct fm_bmi_common *bmi; + int blk, i, port_id; + u32 val; + + bmi = (struct fm_bmi_common *)fm_get_base_addr(fm, fm_bmi_e, 0); + + /* Need 128KB total free buffer pool size */ + val = offset / 256; + blk = 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); + + /* max outstanding tasks/dma transfer = 96/24 */ + out_be32(&bmi->fmbm_cfg2, FMBM_CFG2_INIT); + + /* 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 = fm_get_port_id(fm_port_type_oh_e, i); + /* max tasks=1, max dma=1, no extra */ + out_be32(&bmi->fmbm_pp[port_id - 1], 0); + /* port FIFO size - 256 bytes, no extra */ + out_be32(&bmi->fmbm_pfs[port_id - 1], 0); + } + /* Rx 1G port */ + for (i = 0; i < MAX_NUM_RX_PORT_1G; i++) { + port_id = fm_get_port_id(fm_port_type_rx_e, i); + /* max tasks=4, max dma=1, no extra */ + out_be32(&bmi->fmbm_pp[port_id - 1], 0x03000000); + /* FIFO size - 4KB, no extra */ + out_be32(&bmi->fmbm_pfs[port_id - 1], 0x0000000f); + } + /* Tx 1G port FIFO size - 4KB, no extra */ + for (i = 0; i < MAX_NUM_TX_PORT_1G; i++) { + port_id = fm_get_port_id(fm_port_type_tx_e, i); + /* max tasks=4, max dma=1, no extra */ + out_be32(&bmi->fmbm_pp[port_id - 1], 0x03000000); + /* FIFO size - 4KB, no extra */ + out_be32(&bmi->fmbm_pfs[port_id - 1], 0x0000000f); + } + /* Rx 10G port */ + port_id = fm_get_port_id(fm_port_type_rx_10g_e, 0); + /* max tasks=12, max dma=3, no extra */ + out_be32(&bmi->fmbm_pp[port_id - 1], 0x0b000200); + /* FIFO size - 4KB, no extra */ + out_be32(&bmi->fmbm_pfs[port_id - 1], 0x0000000f); + + /* Tx 10G port */ + port_id = fm_get_port_id(fm_port_type_tx_10g_e, 0); + /* max tasks=12, max dma=3, no extra */ + out_be32(&bmi->fmbm_pp[port_id - 1], 0x0b000200); + /* FIFO size - 4KB, no extra */ + out_be32(&bmi->fmbm_pfs[port_id - 1], 0x0000000f); + + /* initialize internal buffers data base (linked list) */ + out_be32(&bmi->fmbm_init, FMBM_INIT_START); +} + +static void fm_init_qmi(int fm) +{ + struct fm_qmi_common *qmi; + + qmi = (struct fm_qmi_common *)fm_get_base_addr(fm, fm_qmi_e, 0); + + /* disable enqueue and dequeue of QMI */ + out_be32(&qmi->fmqm_gc, FMQM_GC_INIT); + + /* 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); +} + +struct fm_global *fm_get_global(int index) +{ + return fman[index]; +} + +/* + * 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(struct fm_global *fm, + 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: Invalid address for firmware\n"); + 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: Data at %p is not a firmware\n", firmware); + return -EPERM; + } + + /* Check the version */ + if (hdr->version != 1) { + printf("Fman: Unsupported firmware version %u\n", hdr->version); + return -EPERM; + } + + /* Validate some of the fields */ + if ((firmware->count != 1)) { + printf("Fman: Invalid data in firmware header\n"); + 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: Invalid length in firmware header\n"); + 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 Firmware CRC is invalid\n"); + 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) { + printf("Fman: Uploading microcode version %u.%u.%u.\n", + ucode->major, ucode->minor, ucode->revision); + fm->ucode = (void *) CONFIG_SYS_FMAN_FW_ADDR + + ucode->code_offset; + fm->ucode_size = sizeof(u32) * ucode->count; + fm->ucode_ver = (ucode->major << 16) | + (ucode->major << 8) | ucode->revision; + fm_upload_ucode(fm->fm, fm->ucode, fm->ucode_size); + } + } + + return 0; +} + +/* Init common part of FM, index is fm num# like fm as above */ +int fm_init_common(int index) +{ + struct fm_global *fm; + struct fm_muram *muram; + u32 free_pool_offset; + int rc; + + fm = (struct fm_global *)malloc(sizeof(struct fm_global)); + if (!fm) { + printf("%s: no memory\n", __func__); + return -ENOMEM; + } + /* set it to global */ + fman[index] = fm; + /* zero the fm_global */ + memset(fm, 0, sizeof(struct fm_global)); + + /* init muram */ + muram = &fm->muram; + fm_init_muram(index, muram); + + /* save these information */ + fm->fm = index; + + /* Upload the Fman microcode if it's present */ +#ifdef CONFIG_SYS_FMAN_FW_ADDR + rc = fman_upload_firmware(fm, (void *) CONFIG_SYS_FMAN_FW_ADDR); + if (rc) + return rc; +#endif + + /* alloc free buffer pool in MURAM */ + fm->free_pool_base = fm_muram_alloc(muram, FM_FREE_POOL_SIZE, + FM_FREE_POOL_ALIGN); + if (!fm->free_pool_base) { + printf("%s: no muram for free buffer pool\n", __func__); + return -ENOMEM; + } + free_pool_offset = fm->free_pool_base - muram->base; + fm->free_pool_size = FM_FREE_POOL_SIZE; + + /* init qmi */ + fm_init_qmi(index); + /* init fpm */ + fm_init_fpm(index); + /* int axi dma */ + fm_init_axi_dma(index); + /* init bmi common */ + fm_init_bmi(index, free_pool_offset, fm->free_pool_size); + + return 0; +} + diff --git a/drivers/net/fm/fm.h b/drivers/net/fm/fm.h new file mode 100644 index 0000000..5d68a9c --- /dev/null +++ b/drivers/net/fm/fm.h @@ -0,0 +1,312 @@ +/* + * Copyright 2009-2010 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 + */ + +#ifndef __FM_H__ +#define __FM_H__ + +#include +#include +#include +#include + +#define MAX_NUM_FM 2 +#define MAX_NUM_1G_MAC 5 /* max number of 1G MAC per FM */ + +/* 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 4 +#define RX_PORT_10G_BASE 0x10 +#define TX_PORT_1G_BASE 0x28 +#define MAX_NUM_TX_PORT_1G 4 +#define TX_PORT_10G_BASE 0x30 + +enum fm_port_type { + fm_port_type_oh_e, + fm_port_type_rx_e, + fm_port_type_rx_10g_e, + fm_port_type_tx_e, + fm_port_type_tx_10g_e +}; + +/* NIA - next invoked action + */ +#define NIA_ENG_RISC 0x00000000 +#define NIA_ENG_MASK 0x007c0000 + +/* action code + */ +#define NIA_RISC_AC_CC 0x00000006 +#define NIA_RISC_AC_IM_TX 0x00000008 /* independent mode Tx */ +#define NIA_RISC_AC_IM_RX 0x0000000a /* independent mode Rx */ +#define NIA_RISC_AC_HC 0x0000000c + +enum fm_block { + fm_muram_e, + fm_bmi_e, + fm_qmi_e, + fm_parser_e, + fm_policer_e, + fm_keygen_e, + fm_dma_e, + fm_fpm_e, + fm_imem_e, + fm_soft_paser_e, + fm_mac_e, /* 1gmac(1-4), 10gmac */ + fm_mdio_e, /* 5 mdio, the 5th match to 10gmac */ + fm_1588_tmr_e +}; + +/* FM MURAM + */ +struct fm_muram { + u32 base; + u32 top; + u32 size; + u32 res_size; + u32 fm; + u32 alloc; +}; +#define FM_MURAM_SIZE 0x28000 +#define FM_MURAM_RES_SIZE 0x01000 + +/* FM IRAM registers + */ +struct fm_iram { + u32 iadd; /* instruction address register */ + u32 idata; /* instruction data register */ + u32 itcfg; /* timing config register */ + u32 iready; /* ready register */ + u32 res[0x1FFFC]; +} __attribute__ ((packed)); + +#define IRAM_IADD_AIE 0x80000000 /* address auto increase enable */ +#define IRAM_READY 0x80000000 /* ready to use */ + + +/* FMDMSR - Fman DMA status register */ +#define FMDMSR_CMDQNE 0x10000000 /* command queue not empty */ +#define FMDMSR_BER 0x08000000 /* bus error event occurred on bus */ +#define FMDMSR_RDB_ECC 0x04000000 /* read buffer ECC error */ +#define FMDMSR_WRB_SECC 0x02000000 /* write buffer ECC error on system side */ +#define FMDMSR_WRB_FECC 0x01000000 /* write buffer ECC error on Fman side */ +#define FMDMSR_DPEXT_SECC 0x00800000 /* dual port external ECC error on system side */ +#define FMDMSR_DPEXT_FECC 0x00400000 /* dual port external ECC error on Fman side */ +#define FMDMSR_DPDAT_SECC 0x00200000 /* dual port data ECC error on system side */ +#define FMDMSR_DPDAT_FECC 0x00100000 /* dual port data ECC error on Fman side */ +#define FMDMSR_SPDAT_FECC 0x00080000 /* single port data ECC error on Fman side */ + +#define FMDMSR_CLEAR_ALL (FMDMSR_BER | FMDMSR_RDB_ECC \ + | FMDMSR_WRB_SECC | FMDMSR_WRB_FECC \ + | FMDMSR_DPEXT_SECC | FMDMSR_DPEXT_FECC \ + | FMDMSR_DPDAT_SECC | FMDMSR_DPDAT_FECC \ + | FMDMSR_SPDAT_FECC) + +/* FMDMMR - FMan DMA mode register + */ +#define FMDMMR_CACHE_OVRD_MASK 0xc0000000 /* override cache field one the command bus */ +#define FMDMMR_CACHE_NO_CACHING 0x00000000 /* 00 - no override, no caching */ +#define FMDMMR_CACHE_NO_STASH 0x40000000 /* 01 - data should not be stashed in cache */ +#define FMDMMR_CACHE_MAY_STASH 0x80000000 /* 10 - data may be stashed in cache */ +#define FMDMMR_CACHE_STASH 0xc0000000 /* 11 - data should be stashed in cache */ +#define FMDMMR_AID_OVRD 0x20000000 /* AID override, AID='0000' */ +#define FMDMMR_SBER 0x10000000 /* stop the DMA transaction if a bus error */ +#define FMDMMR_AXI_DBG_MASK 0x0f000000 /* number of beates to be written to external mem */ +#define FMDMMR_AXI_DBG_SHIFT 24 +#define FMDMMR_ERRD_EN 0x00800000 /* enable read port emergency */ +#define FMDMMR_ERWR_EN 0x00400000 /* enable write port emergency */ +#define FMDMMR_BER_EN 0x00200000 /* enable external bus error event */ +#define FMDMMR_EB_EN 0x00100000 /* enable emergency towards external bus */ +#define FMDMMR_ERRD_EME 0x00080000 /* set manual emergency on read port */ +#define FMDMMR_ERWR_EME 0x00040000 /* set manual emergency on write port */ +#define FMDMMR_EB_EME_MASK 0x00030000 /* priority on external bus */ +#define FMDMMR_EB_EME_NORMAL 0x00000000 /* 00 - normal */ +#define FMDMMR_EB_EME_EBS 0x00010000 /* 01 - extended bus service */ +#define FMDMMR_EB_EME_SOS 0x00020000 /* 10 - SOS priority */ +#define FMDMMR_EB_EME_EBSSOS 0x00030000 /* 11 - EBS + SOS priority */ +#define FMDMMR_PROT0 0x00001000 /* bus protection - privilege */ +#define FMDMMR_PROT2 0x00000400 /* bus protection - instruction */ +#define FMDMMR_BMI_EMR 0x00000040 /* SOS emergency is set by BMI */ +#define FMDMMR_ECC_MASK 0x00000020 /* enable ECC error events */ +#define FMDMMR_AID_TNUM 0x00000010 /* choose the 4 LSB bits of TNUM output on AID */ + +#define FMDMMR_INIT (FMDMMR_SBER) + +/* FMDMTR - FMan DMA threshold register + */ +#define FMDMTR_DEFAULT 0x18600060 /* high- cmd=24, read/write internal buf=96 */ + +/* FMDMHY - FMan DMA hysteresis register + */ +#define FMDMHY_DEFAULT 0x10400040 /* low- cmd=16, read/write internal buf=64 */ + +/* FMDMSETR - FMan DMA SOS emergency threshold register + */ +#define FMDMSETR_DEFAULT 0x00000000 + +#define FMFPPRC_PORTID_MASK 0x3f000000 +#define FMFPPRC_PORTID_SHIFT 24 +#define FMFPPRC_ORA_SHIFT 16 +#define FMFPPRC_RISC1 0x00000001 +#define FMFPPRC_RISC2 0x00000002 +#define FMFPPRC_RISC_ALL (FMFPPRC_RISC1 | FMFPPRC_RSIC2) + +#define FMFPFLC_DEFAULT 0x00000000 /* no dispatch limitation */ +#define FMFPDIST1_DEFAULT 0x10101010 +#define FMFPDIST2_DEFAULT 0x10101010 + +#define FMRSTC_RESET_FMAN 0x80000000 /* reset entire FMAN and MACs */ + +/* FMFP_EE - FPM event and enable register + */ +#define FMFPEE_DECC 0x80000000 /* double ECC erorr on FPM ram access */ +#define FMFPEE_STL 0x40000000 /* stall of task ... */ +#define FMFPEE_SECC 0x20000000 /* single ECC error */ +#define FMFPEE_RFM 0x00010000 /* release FMan */ +#define FMFPEE_DECC_EN 0x00008000 /* double ECC interrupt enable */ +#define FMFPEE_STL_EN 0x00004000 /* stall of task interrupt enable */ +#define FMFPEE_SECC_EN 0x00002000 /* single ECC error interrupt enable */ +#define FMFPEE_EHM 0x00000008 /* external halt enable */ +#define FMFPEE_UEC 0x00000004 /* FMan is not halted */ +#define FMFPEE_CER 0x00000002 /* only errornous task stalled */ +#define FMFPEE_DER 0x00000001 /* DMA error is just reported */ + +#define FMFPEE_CLEAR_EVENT (FMFPEE_DECC | FMFPEE_STL | FMFPEE_SECC | FMFPEE_EHM | FMFPEE_UEC | FMFPEE_CER | FMFPEE_DER | FMFPEE_RFM) + +/* FMBM_INIT - BMI initialization register + */ +#define FMBM_INIT_START 0x80000000 /* start to init the internal buf linked list */ + +/* FMBM_CFG1 - BMI configuration 1 + */ +#define FMBM_CFG1_FBPS_MASK 0x03ff0000 /* Free buffer pool size */ +#define FMBM_CFG1_FBPS_SHIFT 16 +#define FMBM_CFG1_FBPO_MASK 0x000003ff /* Free buffer pool offset */ +#define FMBM_CFG1_FBPO_INIT 0x00000010 + +/* FMBM_CFG2 - BMI configuration 2 + */ +#define FMBM_CFG2_TNTSKS_MASK 0x007f0000 /* Total number of task */ +#define FMBM_CFG2_TNTSKS_SHIFT 16 +#define FMBM_CFG2_TDMA_MASK 0x0000003f /* Total DMA */ + +#define FMBM_CFG2_INIT 0x00600018 /* max outstanding tasks/dma transfer = 96/24 */ + +/* FMBM_IEVR - interrupt event + */ +#define FMBM_IEVR_PEC 0x80000000 /* pipeline table ECC error detected */ +#define FMBM_IEVR_LEC 0x40000000 /* linked list RAM ECC error */ +#define FMBM_IEVR_SEC 0x20000000 /* statistics count RAM ECC error */ +#define FMBM_IEVR_CLEAR_ALL (FMBM_IEVR_PEC | FMBM_IEVR_LEC | FMBM_IEVR_SEC) + +/* FMBM_IER - interrupt enable + */ +#define FMBM_IER_PECE 0x80000000 /* PEC interrupt enable */ +#define FMBM_IER_LECE 0x40000000 /* LEC interrupt enable */ +#define FMBM_IER_SECE 0x20000000 /* SEC interrupt enable */ + +#define FMBM_IER_DISABLE_ALL 0x00000000 + +/* FMQM_GC - global configuration + */ +#define FMQM_GC_ENQ_EN 0x80000000 /* enqueue enable */ +#define FMQM_GC_DEQ_EN 0x40000000 /* dequeue enable */ +#define FMQM_GC_STEN 0x10000000 /* enable global statistic counters */ +#define FMQM_GC_ENQ_THR_MASK 0x00003f00 /* max number of enqueue Tnum */ +#define FMQM_GC_ENQ_THR_SHIFT 8 +#define FMQM_GC_DEQ_THR_MASK 0x0000003f /* max number of dequeue Tnum */ + +#define FMQM_GC_INIT 0x00003030 /* enqueue/dequeue disable */ + +/* FMQM_EIE - error interrupt event register + */ +#define FMQM_EIE_DEE 0x80000000 /* double-bit ECC detected */ +#define FMQM_EIE_DFUPE 0x40000000 /* dequeue from unkown PortID error */ +#define FMQM_EIE_CLEAR_ALL (FMQM_EIE_DEE | FMQM_EIE_DFUPE) + +/* FMQM_EIEN - error interrupt enable register + */ +#define FMQM_EIEN_DEEN 0x80000000 /* double-bit ECC interrupt enable */ +#define FMQM_EIEN_DFUPEN 0x40000000 /* dequeue from unkown PortID int enable */ +#define FMQM_EIEN_DISABLE_ALL 0x00000000 + +/* FMQM_IE - interrupt event register + */ +#define FMQM_IE_SEE 0x80000000 /* single-bit ECC error detected */ +#define FMQM_IE_CLEAR_ALL FMQM_IE_SEE + +/* FMQM_IEN - interrupt enable register + */ +#define FMQM_IEN_SEE 0x80000000 /* single-bit ECC error interrupt enable */ +#define FMQM_IEN_DISABLE_ALL 0x00000000 + +struct fm_global { + int fm; /* 0-FM1, 1-FM2 */ + u32 ucode_ver; /* microcode version */ + u32 *ucode; /* microcode */ + u32 ucode_size; + struct fm_muram muram; /* muram info structure */ + u32 free_pool_base; /* Free buffer pool base - FIFO */ + u32 free_pool_size; +}; + +#define FM_FREE_POOL_SIZE 0x20000 /* 128K bytes */ +#define FM_FREE_POOL_ALIGN 256 + +u32 fm_get_base_addr(int fm, enum fm_block block, int port); +int fm_get_port_id(enum fm_port_type type, int num); +u32 fm_muram_alloc(struct fm_muram *mem, u32 size, u32 align); +int fm_init_common(int index); +struct fm_global *fm_get_global(int index); + +/* Fman ethernet private struct */ +typedef struct fm_eth { + enum fm_port port; + int fm_index; /* Fman index */ + u32 num; /* 0-3: dTSEC0-3 and 4: TGEC */ + int rx_port; /* Rx port for the ethernet */ + int tx_port; /* Tx port for the ethernet */ + enum fm_eth_type type; /* 1G or 10G ethernet */ + enum fsl_phy_enet_if enet_if; + struct fm_global *fm; /* Fman global information */ + struct fsl_enet_mac *mac; /* MAC controller */ + struct phy_info *phyinfo; + struct mii_info *mii_info; + void *phyregs; + int phyaddr; + struct eth_device *dev; + 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 */ +} __attribute__ ((packed)) fm_eth_t; + +#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/fm_eth.c b/drivers/net/fm/fm_eth.c new file mode 100644 index 0000000..17b6807 --- /dev/null +++ b/drivers/net/fm/fm_eth.c @@ -0,0 +1,1234 @@ +/* + * Copyright 2009-2010 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 "fm.h" +#include "miiphy.h" +#include "dtsec.h" +#include "tgec.h" +#include "../fsl_phy.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define MAXCONTROLLERS (10) +static struct eth_device *devlist[MAXCONTROLLERS]; +static int num_controllers; + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) \ + && !defined(BITBANGMII) +static int cur_mdio45_dev; +extern void tgec_write_phy_reg(struct tgec_mdio_controller *regs, int port_addr, + int dev_addr, int regnum, int value); +extern int tgec_read_phy_reg(struct tgec_mdio_controller *regs, int port_addr, + int dev_addr, int regnum); +extern void mux_mdio_for_fm(enum fm_port port, int fm, int num); +#endif + +struct fm_eth_info fm1_dtsec_info[CONFIG_SYS_NUM_FM1_DTSEC] = { +#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 +}; + +struct fm_eth_info fm1_10gec_info[CONFIG_SYS_NUM_FM1_10GEC] = { + FM_TGEC_INFO_INITIALIZER(1, 1), +}; + +#if (CONFIG_SYS_NUM_FMAN == 2) +struct fm_eth_info fm2_dtsec_info[CONFIG_SYS_NUM_FM2_DTSEC] = { + FM_DTSEC_INFO_INITIALIZER(2, 1), + FM_DTSEC_INFO_INITIALIZER(2, 2), + FM_DTSEC_INFO_INITIALIZER(2, 3), + FM_DTSEC_INFO_INITIALIZER(2, 4), +}; + +struct fm_eth_info fm2_10gec_info[CONFIG_SYS_NUM_FM2_10GEC] = { + FM_TGEC_INFO_INITIALIZER(2, 1), +}; +#endif + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) \ + && !defined(BITBANGMII) +/* + * Find a device index from the devlist by name + * + * Returns: + * The index where the device is located, -1 on error + */ +static int miiphy_find_dev_by_name(const char *devname) +{ + int i; + + for (i = 0; i < num_controllers; i++) { + if (strncmp(devname, devlist[i]->name, strlen(devname)) == 0) + break; + } + + /* If device cannot be found, returns -1 */ + if (i == num_controllers) { + printf("%s: device %s not found in devlist\n", + __func__, devname); + i = -1; + } + + return i; +} + +static int dtsec_miiphy_read(const char *devname, unsigned char addr, + unsigned char reg, unsigned short *value) +{ + int devindex = 0; + struct fm_eth *fm; + + if (devname == NULL || value == NULL) { + printf("%s: NULL pointer given\n", __func__); + } else { + devindex = miiphy_find_dev_by_name(devname); + if (devindex >= 0) { + tsec_mii_t *phyregs; + + fm = devlist[devindex]->priv; + phyregs = fm->phyregs; + mux_mdio_for_fm(fm->port, fm->fm_index, fm->num); + *value = tsec_local_mdio_read(phyregs, addr, 0, reg); + } + } + + return 0; +} + +static int dtsec_miiphy_write(const char *devname, unsigned char addr, + unsigned char reg, unsigned short value) +{ + int devindex = 0; + struct fm_eth *fm; + + if (devname == NULL) { + printf("%s: NULL pointer given\n", __func__); + } else { + devindex = miiphy_find_dev_by_name(devname); + if (devindex >= 0) { + tsec_mii_t *phyregs; + + fm = devlist[devindex]->priv; + phyregs = fm->phyregs; + mux_mdio_for_fm(fm->port, fm->fm_index, fm->num); + tsec_local_mdio_write(phyregs, addr, 0, reg, value); + } + } + + return 0; +} + +int do_mdio45(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int port; + int dev; + int reg; + struct tgec_mdio_controller *regs; + struct fm_eth *fm_eth; + int result; + + if (argc < 3) { + cmd_usage(cmdtp); + return 1; + } + + if (argc == 3) { + int dev; + + if (strcmp(argv[1], "device")) { + cmd_usage(cmdtp); + return 1; + } + dev = miiphy_find_dev_by_name(argv[2]); + + if (dev >= 0) + cur_mdio45_dev = dev; + + return 0; + } + + port = (int)simple_strtoul(argv[2], NULL, 10); + dev = (int)simple_strtoul(argv[3], NULL, 10); + reg = (int)simple_strtoul(argv[4], NULL, 10); + + fm_eth = devlist[cur_mdio45_dev]->priv; + regs = fm_eth->phyregs; + + mux_mdio_for_fm(fm_eth->port, fm_eth->fm_index, fm_eth->num); + + if (strcmp(argv[1], "read") == 0) { + result = tgec_read_phy_reg(regs, port, dev, reg); + printf("Read %d %d %d: %x\n", port, dev, reg, result); + } else if (strcmp(argv[1], "write") == 0 && argc > 5) + tgec_write_phy_reg(regs, port, dev, reg, + (int)simple_strtoul(argv[5], NULL, 16)); + + return 0; +} +U_BOOT_CMD( + mdio45, 6, 1, do_mdio45, + "MDIO Clause 45 utility commands", + "mdio45 device - Set controller to \n" + "mdio45 read - read [0-65535] of device [1-31] on PHY [0-31]\n" + "mdio45 write - write reg [0-65535] of device [1-31] on PHY [0-31]\n" +); + +#define TBIANA_SETTINGS ( \ + TBIANA_ASYMMETRIC_PAUSE \ + | TBIANA_SYMMETRIC_PAUSE \ + | TBIANA_FULL_DUPLEX \ + ) + +#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; + tsec_mii_t *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, regs->tbipa, 0, TBI_TBICON, + TBICON_CLK_SELECT); + tsec_local_mdio_write(phyregs, regs->tbipa, 0, TBI_ANA, + TBIANA_SETTINGS); + tsec_local_mdio_write(phyregs, regs->tbipa, 0, TBI_CR, TBICR_SETTINGS); +} + +static struct phy_info *dtsec_init_phy(struct eth_device *dev, + struct mii_info *mii_info) +{ + struct fm_eth *fm_eth; + struct dtsec *regs; + tsec_mii_t *miiregs; + struct phy_info *curphy; + int timeout = 1000000; + + mii_info->advertising = (ADVERTISED_10baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | + ADVERTISED_100baseT_Full | + ADVERTISED_1000baseT_Full); + + fm_eth = dev->priv; + regs = (struct dtsec *)fm_eth->mac->base; + miiregs = (tsec_mii_t *)fm_eth->mac->phyregs; + + /* Assign a Physical address to the TBI */ + out_be32(®s->tbipa, CONFIG_SYS_TBIPA_VALUE); + + out_be32(&miiregs->miimcfg, MIIMCFG_RESET_MGMT); + out_be32(&miiregs->miimcfg, 0); + out_be32(&miiregs->miimcfg, MIIMCFG_INIT_VALUE); + asm("sync"); + while (in_be32(&miiregs->miimind)&MIIMIND_BUSY && timeout--); + + if (fm_eth->enet_if == SGMII) + dtsec_configure_serdes(fm_eth); + + /* Get the cmd structure corresponding to the attached PHY */ + curphy = tsec_get_phy_info(mii_info); + if (curphy == NULL) { + printf("%s: No PHY found\n", dev->name); + return NULL; + } + + return curphy; +} + +static struct phy_info *tgec_init_phy(struct eth_device *dev, + struct mii_info *mii_info) +{ + struct phy_info *curphy; + struct fm_eth *fm; + char phyopt[20]; + + fm = dev->priv; + + mii_info->advertising = (ADVERTISED_100baseT_Full | + ADVERTISED_1000baseT_Full | + ADVERTISED_10000baseT_Full); + + /* Get the cmd structure corresponding to the attached PHY */ + curphy = tgec_get_phy_info(mii_info); + if (curphy == NULL) + printf("%s: No PHY found\n", dev->name); + + cur_mdio45_dev = miiphy_find_dev_by_name(dev->name); + + sprintf(phyopt, "fsl_fm%d_xaui_phy", fm->fm_index + 1); + + if (hwconfig_arg_cmp(phyopt, "xfi")) + mii_info->port = PORT_FIBRE; + + return curphy; +} +#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_enable(int fm, int port) +{ + struct fm_bmi_rx_port *rx_port; + u32 val; + + rx_port = (struct fm_bmi_rx_port *)fm_get_base_addr(fm, + fm_bmi_e, port); + val = in_be32(&rx_port->fmbm_rcfg); + val |= FMBM_RCFG_EN; + out_be32(&rx_port->fmbm_rcfg, val); +} + +static void bmi_rx_port_disable(int fm, int port) +{ + struct fm_bmi_rx_port *rx_port; + u32 val; + int timeout = 1000000; + + rx_port = (struct fm_bmi_rx_port *)fm_get_base_addr(fm, + fm_bmi_e, port); + val = in_be32(&rx_port->fmbm_rcfg); + val &= ~FMBM_RCFG_EN; + out_be32(&rx_port->fmbm_rcfg, val); + + /* 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(int fm, int port) +{ + struct fm_bmi_rx_port *rx_port; + rx_port = (struct fm_bmi_rx_port *)fm_get_base_addr(fm, + fm_bmi_e, port); + + /* set BMI to independent mode, Rx port disable */ + out_be32(&rx_port->fmbm_rcfg, FMBM_RCFG_IM); + /* set Rx DMA attributes - no swap, no stash/no optimization */ + out_be32(&rx_port->fmbm_rda, FMBM_RDA_INIT); + /* set Rx FIFO parameters */ + out_be32(&rx_port->fmbm_rfp, FMBM_RFP_DEFAULT); + /* set Rx frame end parameters */ + out_be32(&rx_port->fmbm_rfed, FMBM_RFED_DEFAULT); + /* set Rx IC parameters */ + out_be32(&rx_port->fmbm_ricp, FMBM_RICP_DEFAULT); + /* 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 */ + out_be32(&rx_port->fmbm_rfca, FMBM_RFCA_DEFAULT); + /* 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_enable(int fm, int port) +{ + struct fm_bmi_tx_port *tx_port; + u32 val; + + tx_port = (struct fm_bmi_tx_port *)fm_get_base_addr(fm, + fm_bmi_e, port); + val = in_be32(&tx_port->fmbm_tcfg); + val |= FMBM_TCFG_EN; + out_be32(&tx_port->fmbm_tcfg, val); +} + +static void bmi_tx_port_disable(int fm, int port) +{ + struct fm_bmi_tx_port *tx_port; + u32 val; + int timeout = 1000000; + + tx_port = (struct fm_bmi_tx_port *)fm_get_base_addr(fm, + fm_bmi_e, port); + val = in_be32(&tx_port->fmbm_tcfg); + val &= ~FMBM_TCFG_EN; + out_be32(&tx_port->fmbm_tcfg, val); + + /* 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(int fm, int port) +{ + struct fm_bmi_tx_port *tx_port; + + tx_port = (struct fm_bmi_tx_port *)fm_get_base_addr(fm, + fm_bmi_e, port); + + /* set BMI to independent mode, Tx port disable */ + out_be32(&tx_port->fmbm_tcfg, FMBM_TCFG_IM); + /* set Tx DMA attributes - no stash/no swap */ + out_be32(&tx_port->fmbm_tda, FMBM_TDA_INIT); + /* set Tx FIFO parameters - pipeline depth=1, low comfort 20*256B */ + out_be32(&tx_port->fmbm_tfp, FMBM_TFP_INIT); + /* set Tx frame margins parameters */ + out_be32(&tx_port->fmbm_tfed, FMBM_TFED_DEFAULT); + /* set Tx IC parameters */ + out_be32(&tx_port->fmbm_ticp, FMBM_TICP_DEFAULT); + /* 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 */ + out_be32(&tx_port->fmbm_tfca, FMBM_TFCA_DEFAULT); + /* 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 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; + __asm__ __volatile__ ("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; + __asm__ __volatile__ ("sync"); +} + +static int fm_eth_rx_port_parameter_init(struct fm_eth *fm_eth) +{ + struct fm_global *fm; + struct fm_muram *muram; + 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; + int i; + + fm = fm_eth->fm; + muram = &fm->muram; + + /* alloc global parameter ram at MURAM */ + pram = (struct fm_port_global_pram *)fm_muram_alloc(muram, + FM_PRAM_SIZE, FM_PRAM_ALIGN); + fm_eth->rx_pram = pram; + + /* parameter page offset to MURAM */ + pram_page_offset = (u32)pram - muram->base; + + /* 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 */ + bmi_rx_port = (struct fm_bmi_rx_port *)fm_get_base_addr(fm->fm, + fm_bmi_e, fm_eth->rx_port); + 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_global *fm; + struct fm_muram *muram; + 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; + int i; + + fm = fm_eth->fm; + muram = &fm->muram; + + /* alloc global parameter ram at MURAM */ + pram = (struct fm_port_global_pram *)fm_muram_alloc(muram, + FM_PRAM_SIZE, FM_PRAM_ALIGN); + fm_eth->tx_pram = pram; + + /* parameter page offset to MURAM */ + pram_page_offset = (u32)pram - muram->base; + + /* 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 */ + bmi_tx_port = (struct fm_bmi_tx_port *)fm_get_base_addr(fm->fm, + fm_bmi_e, fm_eth->tx_port); + 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 */ + if (mac->init_mac) + mac->init_mac(mac); + + /* For some reason we need to set SPEED_100 */ + if ((fm_eth->enet_if == 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->fm_index, fm_eth->rx_port); + /* init bmi tx port, IM mode and disable */ + bmi_tx_port_init(fm_eth->fm_index, fm_eth->tx_port); + + return 1; +} + +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; + } + if (mac->set_mac_addr) + mac->set_mac_addr(mac, dev->enetaddr); + + /* enable bmi Rx port */ + bmi_rx_port_enable(fm_eth->fm_index, fm_eth->rx_port); + /* enable MAC rx/tx port */ + if (mac->enable_mac) + mac->enable_mac(mac); + /* enable bmi Tx port */ + bmi_tx_port_enable(fm_eth->fm_index, fm_eth->tx_port); + /* re-enable transmission of frame */ + fmc_tx_port_graceful_stop_disable(fm_eth); + +#ifdef CONFIG_MII + mux_mdio_for_fm(fm_eth->port, fm_eth->fm_index, fm_eth->num); + + if (fm_eth->phyinfo && fm_eth->phyinfo->startup) + fm_eth->phyinfo->startup(fm_eth->mii_info); +#else + fm_eth->mii_info->speed = SPEED_1000; + fm_eth->mii_info->link = 1; + fm_eth->mii_info->duplex = DUPLEX_FULL; +#endif + + /* set the MAC-PHY mode */ + if (mac->set_if_mode) + mac->set_if_mode(mac, fm_eth->enet_if, fm_eth->mii_info->speed); + + return fm_eth->mii_info->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->fm_index, fm_eth->tx_port); + /* disable MAC rx/tx port */ + if (mac->disable_mac) + mac->disable_mac(mac); + /* disable bmi Rx port */ + bmi_rx_port_disable(fm_eth->fm_index, fm_eth->rx_port); + +#ifdef CONFIG_MII + mux_mdio_for_fm(fm_eth->port, fm_eth->fm_index, fm_eth->num); + + if (fm_eth->phyinfo && fm_eth->phyinfo->shutdown) + fm_eth->phyinfo->shutdown(fm_eth->mii_info); +#endif +} + +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; + volatile 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(1000); + 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; + __asm__ __volatile__ ("sync"); + txbd->status = TxBD_READY | TxBD_LAST; + __asm__ __volatile__ ("sync"); + + /* update TxQD, let RISC to send the packet */ + offset_in = muram_readw(&pram->txqd.offset_in); + offset_in += SIZEOFBD; + if (offset_in >= muram_readw(&pram->txqd.bd_ring_size)) + offset_in = 0; + muram_writew(&pram->txqd.offset_in, offset_in); + __asm__ __volatile__ ("sync"); + + /* wait for buffer to be transmitted */ + for (i = 0; txbd->status & TxBD_READY; i++) { + udelay(1000); + 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; + volatile 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; + __asm__ __volatile__ ("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 += SIZEOFBD; + if (offset_out >= muram_readw(&pram->rxqd.bd_ring_size)) + offset_out = 0; + muram_writew(&pram->rxqd.offset_out, offset_out); + __asm__ __volatile__ ("sync"); + } + fm_eth->cur_rxbd = (void *)rxbd; + + return 1; +} + +static int fm_eth_init_mac(struct fm_eth *fm_eth) +{ + struct fsl_enet_mac *mac; + int fm, num; + void *base, *phyregs = NULL; + + mac = fm_eth->mac; + fm = fm_eth->fm_index; + num = fm_eth->num; + + /* Get the mac registers base address */ + base = (void *)fm_get_base_addr(fm, fm_mac_e, num); + phyregs = (void *)fm_get_base_addr(fm, fm_mdio_e, num); + + /* 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, phyregs, 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 mii_info *mii_info; +#ifdef CONFIG_MII + struct phy_info *curphy; +#endif + + mii_info = malloc(sizeof(*mii_info)); + if (!mii_info) { + printf("%s: Could not allocate mii_info", dev->name); + return -1; + } + memset(mii_info, 0, sizeof(*mii_info)); + + mii_info->duplex = DUPLEX_FULL; + mii_info->link = 0; + + mii_info->autoneg = 1; + mii_info->mii_id = fm_eth->phyaddr; + + mii_info->priv = fm_eth; + mii_info->dev = dev; + fm_eth->mii_info = mii_info; + + mii_info->phyregs = fm_eth->phyregs; + +#ifdef CONFIG_MII + mux_mdio_for_fm(fm_eth->port, fm_eth->fm_index, fm_eth->num); + + if (fm_eth->type == fm_eth_1g_e) + curphy = dtsec_init_phy(dev, mii_info); + else + curphy = tgec_init_phy(dev, mii_info); + + fm_eth->phyinfo = curphy; + + if (!curphy) + return -1; + + if (curphy->config) + curphy->config(fm_eth->mii_info); +#endif + + return 0; +} + +static int fm_eth_initialize(struct fm_global *fm, struct fm_eth_info *info) +{ + struct eth_device *dev; + struct fm_eth *fm_eth; + int rx_port, tx_port; + int i, 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 the fm global to Fman ethernet struct */ + fm_eth->fm = fm; + /* save the fm num# to Fman ethernet struct */ + fm_eth->fm_index = fm->fm; + /* save num to Fman ethenet (0-3: dTSEC, 4: TGEC) */ + num = info->num; + fm_eth->num = num; + fm_eth->port = info->port; + fm_eth->type = info->type; + + /* Rx port and Tx port for the Fman ethernet */ + if (fm_eth->type == fm_eth_1g_e) { + rx_port = fm_get_port_id(fm_port_type_rx_e, num); + tx_port = fm_get_port_id(fm_port_type_tx_e, num); + } else { + rx_port = fm_get_port_id(fm_port_type_rx_10g_e, 0); + tx_port = fm_get_port_id(fm_port_type_tx_10g_e, 0); + } + fm_eth->rx_port = rx_port; + fm_eth->tx_port = tx_port; + + /* init global mac structure */ + if (!fm_eth_init_mac(fm_eth)) + 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", fm->fm + 1, num + 1); + else + sprintf(dev->name, "FM%d@TGEC%d", fm->fm + 1, 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; + + /* clear the ethernet address */ + for (i = 0; i < 6; i++) + dev->enetaddr[i] = 0; + eth_register(dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) \ + && !defined(BITBANGMII) + miiphy_register(dev->name, dtsec_miiphy_read, dtsec_miiphy_write); +#endif + + fm_eth->phyaddr = info->phy_addr; + fm_eth->phyregs = info->phy_regs; + 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; + + return 1; +} + +int fm_standard_init(bd_t *bis) +{ + int i; + struct fm_global *fm; + + if (fm_init_common(0)) + return 0; + + fm = fm_get_global(0); + + for (i = 0; i < CONFIG_SYS_NUM_FM1_DTSEC; i++) { + if (fm1_dtsec_info[i].enabled) { + if (!fm_eth_initialize(fm, &fm1_dtsec_info[i])) + return 0; + } + } + for (i = 0; i < CONFIG_SYS_NUM_FM1_10GEC; i++) { + if (fm1_10gec_info[i].enabled) { + if (!fm_eth_initialize(fm, &fm1_10gec_info[i])) + return 0; + } + } + +#if (CONFIG_SYS_NUM_FMAN == 2) + if (fm_init_common(1)) + return 0; + + fm = fm_get_global(1); + + for (i = 0; i < CONFIG_SYS_NUM_FM2_DTSEC; i++) { + if (fm2_dtsec_info[i].enabled) { + if (!fm_eth_initialize(fm, &fm2_dtsec_info[i])) + return 0; + } + } + for (i = 0; i < CONFIG_SYS_NUM_FM2_10GEC; i++) { + if (fm2_10gec_info[i].enabled) { + if (!fm_eth_initialize(fm, &fm2_10gec_info[i])) + return 0; + } + } +#endif + + return 1; +} + +static int is_device_enabled(u32 devdisr_mask) +{ + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + u32 devdisr2 = in_be32(&gur->devdisr2); + + return !(devdisr_mask & devdisr2); +} + +/* Init data structures based on HW cfg */ +void fman_enet_init(void) +{ + int i; + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + u32 rcwsr11 = in_be32(&gur->rcwsr[11]); + + for (i = 0; i < CONFIG_SYS_NUM_FM1_DTSEC; i++) { + if (is_device_enabled(fm1_dtsec_info[i].devdisr_mask)) { + fm1_dtsec_info[i].enabled = 1; + fm1_dtsec_info[i].enet_if = SGMII; + } else { + fm1_dtsec_info[i].enabled = 0; + } + } + for (i = 0; i < CONFIG_SYS_NUM_FM1_10GEC; i++) { + if (is_device_enabled(fm1_10gec_info[i].devdisr_mask)) { + fm1_10gec_info[i].enabled = 1; + fm1_10gec_info[i].enet_if = XAUI; + } else { + fm1_10gec_info[i].enabled = 0; + } + if (!is_serdes_configured(XAUI_FM1)) + fm1_10gec_info[i].enabled = 0; + } + + if (!is_serdes_configured(SGMII_FM1_DTSEC1)) + fm1_dtsec_info[0].enabled = 0; + + if ((rcwsr11 & FSL_CORENET_RCWSR11_EC1) == + FSL_CORENET_RCWSR11_EC1_FM1_DTSEC1) { + fm1_dtsec_info[0].enabled = 1; + fm1_dtsec_info[0].enet_if = RGMII; + } + + if ((rcwsr11 & FSL_CORENET_RCWSR11_EC2) == + FSL_CORENET_RCWSR11_EC2_FM1_DTSEC2) { + fm1_dtsec_info[1].enabled = 1; + fm1_dtsec_info[1].enet_if = RGMII; + } + +#if (CONFIG_SYS_NUM_FMAN == 2) + for (i = 0; i < CONFIG_SYS_NUM_FM2_DTSEC; i++) { + if (is_device_enabled(fm2_dtsec_info[i].devdisr_mask)) { + fm2_dtsec_info[i].enabled = 1; + fm2_dtsec_info[i].enet_if = SGMII; + } else { + fm2_dtsec_info[i].enabled = 0; + } + } + for (i = 0; i < CONFIG_SYS_NUM_FM2_10GEC; i++) { + if (is_device_enabled(fm2_10gec_info[i].devdisr_mask)) { + fm2_10gec_info[i].enabled = 1; + fm2_10gec_info[i].enet_if = XAUI; + } else { + fm2_10gec_info[i].enabled = 0; + } + if (!is_serdes_configured(XAUI_FM2)) + fm2_10gec_info[i].enabled = 0; + } + + if ((rcwsr11 & FSL_CORENET_RCWSR11_EC2) == + FSL_CORENET_RCWSR11_EC2_FM2_DTSEC1) { + fm2_dtsec_info[0].enabled = 1; + fm2_dtsec_info[0].enet_if = RGMII; + } +#endif + + return ; +} + +void fm_info_set_phy_address(enum fm_port port, int address) +{ + switch (port) { + case FM1_DTSEC1: + case FM1_DTSEC2: + case FM1_DTSEC3: + case FM1_DTSEC4: + case FM1_DTSEC5: + fm1_dtsec_info[port - FM1_DTSEC1].phy_addr = address; + break; + case FM1_10GEC1: + fm1_10gec_info[port - FM1_10GEC1].phy_addr = address; + break; +#if (CONFIG_SYS_NUM_FMAN == 2) + case FM2_DTSEC1: + case FM2_DTSEC2: + case FM2_DTSEC3: + case FM2_DTSEC4: + fm2_dtsec_info[port - FM2_DTSEC1].phy_addr = address; + break; + case FM2_10GEC1: + fm2_10gec_info[port - FM2_10GEC1].phy_addr = address; + break; +#endif + default: + break; + }; +} + +enum fsl_phy_enet_if fm_info_get_enet_if(enum fm_port port) +{ + switch (port) { + case FM1_DTSEC1: + case FM1_DTSEC2: + case FM1_DTSEC3: + case FM1_DTSEC4: + case FM1_DTSEC5: + if (fm1_dtsec_info[port - FM1_DTSEC1].enabled) + return fm1_dtsec_info[port - FM1_DTSEC1].enet_if; + break; + case FM1_10GEC1: + if (fm1_10gec_info[port - FM1_10GEC1].enabled) + return fm1_10gec_info[port - FM1_10GEC1].enet_if; + break; +#if (CONFIG_SYS_NUM_FMAN == 2) + case FM2_DTSEC1: + case FM2_DTSEC2: + case FM2_DTSEC3: + case FM2_DTSEC4: + if (fm2_dtsec_info[port - FM2_DTSEC1].enabled) + return fm2_dtsec_info[port - FM2_DTSEC1].enet_if; + break; + case FM2_10GEC1: + if (fm2_10gec_info[port - FM2_10GEC1].enabled) + return fm2_10gec_info[port - FM2_10GEC1].enet_if; + break; +#endif + default: + break; + }; + + return FSL_ETH_IF_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_fman_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 < CONFIG_SYS_NUM_FM1_DTSEC; i++) + ft_fman_fixup_port(blob, &fm1_dtsec_info[i], "fsl,fman-1g-mac"); + for (i = 0; i < CONFIG_SYS_NUM_FM1_10GEC; i++) + ft_fman_fixup_port(blob, &fm1_10gec_info[i], "fsl,fman-10g-mac"); + +#if (CONFIG_SYS_NUM_FMAN == 2) + for (i = 0; i < CONFIG_SYS_NUM_FM2_DTSEC; i++) + ft_fman_fixup_port(blob, &fm2_dtsec_info[i], "fsl,fman-1g-mac"); + for (i = 0; i < CONFIG_SYS_NUM_FM2_10GEC; i++) + ft_fman_fixup_port(blob, &fm2_10gec_info[i], "fsl,fman-10g-mac"); +#endif +} diff --git a/include/fm_eth.h b/include/fm_eth.h new file mode 100644 index 0000000..199bcd9 --- /dev/null +++ b/include/fm_eth.h @@ -0,0 +1,348 @@ +/* + * Copyright 2009-2010 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 + */ + +#ifndef __FM_ETH_H__ +#define __FM_ETH_H__ + +#include +#include +#include + +enum fm_port { + FM1_DTSEC1, + FM1_DTSEC2, + FM1_DTSEC3, + FM1_DTSEC4, + FM1_DTSEC5, + FM1_10GEC1, + FM2_DTSEC1, + FM2_DTSEC2, + FM2_DTSEC3, + FM2_DTSEC4, + FM2_10GEC1, + NUM_FM_PORTS, +}; + +struct fm_bmi_rx_port { + u32 fmbm_rcfg; /* Rx configuration */ + u32 fmbm_rst; /* Rx status */ + u32 fmbm_rda; /* Rx DMA attributes */ + u32 fmbm_rfp; /* Rx FIFO parameters */ + u32 fmbm_rfed; /* Rx frame end data */ + u32 fmbm_ricp; /* Rx internal context parameters */ + u32 fmbm_rim; /* Rx internal margins */ + u32 fmbm_rebm; /* Rx external buffer margins */ + u32 fmbm_rfne; /* Rx frame next engine */ + u32 fmbm_rfca; /* Rx frame command attributes */ + u32 fmbm_rfpne; /* Rx frame parser next engine */ + u32 fmbm_rpso; /* Rx parse start offset */ + u32 fmbm_rpp; /* Rx policer profile */ + u32 fmbm_rccb; /* Rx coarse classification base */ + u32 res1[0x2]; + u32 fmbm_rprai[0x8]; /* Rx parse results array Initialization */ + u32 fmbm_rfqid; /* Rx frame queue ID */ + u32 fmbm_refqid; /* Rx error frame queue ID */ + u32 fmbm_rfsdm; /* Rx frame status discard mask */ + u32 fmbm_rfsem; /* Rx frame status error mask */ + u32 fmbm_rfene; /* Rx frame enqueue next engine */ + u32 res2[0x23]; + u32 fmbm_ebmpi[0x8]; /* buffer manager pool information */ + u32 fmbm_acnt[0x8]; /* allocate counter */ + u32 res3[0x8]; + u32 fmbm_cgm[0x8]; /* congestion group map */ + u32 fmbm_mpd; /* BMan pool depletion */ + u32 res4[0x1F]; + u32 fmbm_rstc; /* Rx statistics counters */ + u32 fmbm_rfrc; /* Rx frame counters */ + u32 fmbm_rfbc; /* Rx bad frames counter */ + u32 fmbm_rlfc; /* Rx large frames counter */ + u32 fmbm_rffc; /* Rx filter frames counter */ + u32 fmbm_rfdc; /* Rx frame discard counter */ + u32 fmbm_rfldec; /* Rx frames list DMA error counter */ + u32 fmbm_rodc; /* Rx out of buffers discard counter */ + u32 fmbm_rbdc; /* Rx buffers deallocate counter */ + u32 res5[0x17]; + u32 fmbm_rpc; /* Rx performance counters */ + u32 fmbm_rpcp; /* Rx performance count parameters */ + u32 fmbm_rccn; /* Rx cycle counter */ + u32 fmbm_rtuc; /* Rx tasks utilization counter */ + u32 fmbm_rrquc; /* Rx receive queue utilization counter */ + u32 fmbm_rduc; /* Rx DMA utilization counter */ + u32 fmbm_rfuc; /* Rx FIFO utilization counter */ + u32 fmbm_rpac; /* Rx pause activation counter */ + u32 res6[0x18]; + u32 fmbm_rdbg; /* Rx debug configuration */ +} __attribute__ ((packed)); + +/* FMBM_RCFG - Rx configuration + */ +#define FMBM_RCFG_EN 0x80000000 /* port is enabled to receive data */ +#define FMBM_RCFG_FDOVR 0x02000000 /* frame discard override */ +#define FMBM_RCFG_IM 0x01000000 /* independent mode */ + +/* FMBM_RST - Rx status + */ +#define FMBM_RST_BSY 0x80000000 /* Rx port is busy */ + +/* FMBM_RDA - Rx DMA attributes + */ +#define FMBM_RDA_SWAP_MASK 0xc0000000 +#define FMBM_RDA_SWAP_SHIFT 30 +#define FMBM_RDA_NO_SWAP 0x00000000 +#define FMBM_RDA_SWAP_LE 0x40000000 /* swapped in powerpc little endian mode */ +#define FMBM_RDA_SWAP_BE 0x80000000 /* swapped in big endian mode */ +#define FMBM_RDA_ICC_MASK 0x30000000 +#define FMBM_RDA_ICC_NOSTASH 0x00000000 /* IC write cacheable, no allocate */ +#define FMBM_RDA_ICC_STASH 0x10000000 /* IC write cacheable and allocate */ +#define FMBM_RDA_FHC_MASK 0x0c000000 +#define FMBM_RDA_FHC_NOSTASH 0x00000000 /* frame header write cacheable, no allocate */ +#define FMBM_RDA_FHC_STASH 0x04000000 /* frame header write cacheable and allocate */ +#define FMBM_RDA_SGC_MASK 0x03000000 +#define FMBM_RDA_SGC_NOSTASH 0x00000000 /* scatter gather write cacheable, no alloc */ +#define FMBM_RDA_SGC_STASH 0x01000000 /* scatter gather write cacheable and alloc */ +#define FMBM_RDA_WOPT_MASK 0x00300000 +#define FMBM_RDA_NO_OPT 0x00000000 +#define FMBM_RDA_WRITE_OPT 0x00100000 /* allow to write more bytes than the actual */ + +#define FMBM_RDA_MASK_ALL (FMBM_RDA_SWAP_MASK | FMBM_RDA_ICC_MASK \ + | FMBM_RDA_FHC_MASK | FMBM_RDA_SGC_MASK \ + | FMBM_RDA_WOPT_MASK) + +#define FMBM_RDA_INIT 0x00000000 + +/* FMBM_RFP - Rx FIFO parameters + */ +#define FMBM_RFP_DEFAULT 0x03ff03ff + +/* FMBM_RFED - Rx Frame end data + */ +#define FMBM_RFED_DEFAULT 0x00000000 + +/* FMBM_RICP - Rx internal context + */ +#define FMBM_RICP_DEFAULT 0x00000002 + +/* FMBM_RFCA - Rx frame command attributes + */ +#define FMBM_RFCA_ORDER 0x80000000 +#define FMBM_RFCA_SYNC 0x02000000 +#define FMBM_RFCA_DEFAULT 0x003c0000 + +/* FMBM_RSTC - Rx statistics + */ +#define FMBM_RSTC_EN 0x80000000 /* statistics counters enable */ + +struct fm_bmi_tx_port { + u32 fmbm_tcfg; /* Tx configuration */ + u32 fmbm_tst; /* Tx status */ + u32 fmbm_tda; /* Tx DMA attributes */ + u32 fmbm_tfp; /* Tx FIFO parameters */ + u32 fmbm_tfed; /* Tx frame end data */ + u32 fmbm_ticp; /* Tx internal context parameters */ + u32 fmbm_tfne; /* Tx frame next engine */ + u32 fmbm_tfca; /* Tx frame command attributes */ + u32 fmbm_tcfqid;/* Tx confirmation frame queue ID */ + u32 fmbm_tfeqid;/* Tx error frame queue ID */ + u32 fmbm_tfene; /* Tx frame enqueue next engine */ + u32 fmbm_trlmts;/* Tx rate limiter scale */ + u32 fmbm_trlmt; /* Tx rate limiter */ + u32 res0[0x73]; + u32 fmbm_tstc; /* Tx statistics counters */ + u32 fmbm_tfrc; /* Tx frame counter */ + u32 fmbm_tfdc; /* Tx frames discard counter */ + u32 fmbm_tfledc;/* Tx frame length error discard counter */ + u32 fmbm_tfufdc;/* Tx frame unsupported format discard counter */ + u32 fmbm_tbdc; /* Tx buffers deallocate counter */ + u32 res1[0x1a]; + u32 fmbm_tpc; /* Tx performance counters */ + u32 fmbm_tpcp; /* Tx performance count parameters */ + u32 fmbm_tccn; /* Tx cycle counter */ + u32 fmbm_ttuc; /* Tx tasks utilization counter */ + u32 fmbm_ttcquc;/* Tx transmit confirm queue utilization counter */ + u32 fmbm_tduc; /* Tx DMA utilization counter */ + u32 fmbm_tfuc; /* Tx FIFO utilization counter */ + u32 res2[0x19]; + u32 fmbm_tdcfg; /* Tx debug configuration */ +} __attribute__ ((packed)); + +/* FMBM_TCFG - Tx configuration + */ +#define FMBM_TCFG_EN 0x80000000 /* port is enabled to transmit data */ +#define FMBM_TCFG_IM 0x01000000 /* independent mode enable */ + +/* FMBM_TST - Tx status + */ +#define FMBM_TST_BSY 0x80000000 /* Tx port is busy */ + +/* FMBM_TDA - Tx DMA attributes + */ +#define FMBM_TDA_INIT 0x00000000 /* No swap, no stashing */ + +/* FMBM_TFP - Tx FIFO parameters + */ +#define FMBM_TFP_INIT 0x00000013 /* pipeline=1, low comfort=5KB */ + +/* FMBM_TFED - Tx frame end data + */ +#define FMBM_TFED_DEFAULT 0x00000000 + +/* FMBM_TICP - Tx internal context parameters + */ +#define FMBM_TICP_DEFAULT 0x00000000 + +/* FMBM_TFCA - Tx frame command attributes + */ +#define FMBM_TFCA_DEFAULT 0x00040000 + +/* FMBM_TSTC - Tx statistics counters + */ +#define FMBM_TSTC_EN 0x80000000 + +/* Rx/Tx buffer descriptor + */ +struct fm_port_bd { + u16 status; + u16 len; + u32 res0; + u16 res1; + u16 buf_ptr_hi; + u32 buf_ptr_lo; +} __attribute__ ((packed)); + +#define SIZEOFBD sizeof(struct fm_port_bd) + +/* 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]; +} __attribute__ ((packed)); + +#define QD_RXF_INT_MASK 0x0010 /* 1=set interrupt for receive frame */ +#define QD_BSY_INT_MASK 0x0008 /* 1=set interrupt for receive busy event */ +#define QD_FPMEVT_SEL_MASK 0x0003 + +/* IM global parameter RAM + */ +struct fm_port_global_pram { + u32 mode; /* independent mode register */ + u32 rxqd_ptr; /* Rx queue descriptor pointer, RxQD size=32 bytes + 8 bytes aligned, independent mode page address + 0x20 */ + u32 txqd_ptr; /* Tx queue descriptor pointer, TxQD size=32 bytes + 8 bytes aligned, independent mode page address + 0x40 */ + u16 mrblr; /* max Rx buffer length, set its value to the power of 2 */ + 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]; +} __attribute__ ((packed)); + +#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 + +/**************************** + * Fman ethernet + ****************************/ +enum fm_eth_type { + fm_eth_1g_e, + fm_eth_10g_e, +}; + +/* Fman MAC controller + */ +#define CONFIG_SYS_FM1_DTSEC1_MDIO_ADDR (CONFIG_SYS_FSL_FM1_ADDR + 0xe1120) +#define CONFIG_SYS_FM1_TGEC_MDIO_ADDR (CONFIG_SYS_FSL_FM1_ADDR + 0xf1000) + +/* Fman ethernet info struct */ +#define FM_ETH_INFO_INITIALIZER(idx, pregs) \ + .fm = idx, \ + .phy_regs = (void *)pregs, \ + .enet_if = FSL_ETH_IF_NONE, \ + +#define FM_DTSEC_INFO_INITIALIZER(idx, n) \ +{ \ + FM_ETH_INFO_INITIALIZER(idx, CONFIG_SYS_FM1_DTSEC1_MDIO_ADDR) \ + .num = n - 1, \ + .type = fm_eth_1g_e, \ + .port = FM##idx##_DTSEC##n, \ + .devdisr_mask = FSL_CORENET_DEVDISR2_DTSEC##idx##_##n, \ + .compat_offset = CONFIG_SYS_FSL_FM##idx##_OFFSET + \ + offsetof(struct ccsr_fman, mac[n-1]), \ +} + +#define FM_TGEC_INFO_INITIALIZER(idx, n) \ +{ \ + FM_ETH_INFO_INITIALIZER(idx, CONFIG_SYS_FM1_TGEC_MDIO_ADDR) \ + .num = n + 3, \ + .type = fm_eth_10g_e, \ + .port = FM##idx##_10GEC##n, \ + .devdisr_mask = FSL_CORENET_DEVDISR2_10GEC##idx, \ + .compat_offset = CONFIG_SYS_FSL_FM##idx##_OFFSET + \ + offsetof(struct ccsr_fman, fm_10gec), \ +} + +struct fm_eth_info { + int enabled; + u32 devdisr_mask; + int fm; + int num; + enum fm_port port; + enum fm_eth_type type; + u8 phy_addr; + void *phy_regs; + enum fsl_phy_enet_if enet_if; + u32 compat_offset; +}; + +int fm_standard_init(bd_t *bis); +int fm_initialize(int index, struct fm_eth_info *fm_info, int num); +void fman_enet_init(void); +void fdt_fixup_fman_ethernet(void *fdt); +enum fsl_phy_enet_if fm_info_get_enet_if(enum fm_port port); +void fm_info_set_phy_address(enum fm_port port, int address); + +#endif