diff options
Diffstat (limited to 'drivers/net/fm')
-rw-r--r-- | drivers/net/fm/Makefile | 61 | ||||
-rw-r--r-- | drivers/net/fm/dtsec.c | 181 | ||||
-rw-r--r-- | drivers/net/fm/eth.c | 670 | ||||
-rw-r--r-- | drivers/net/fm/fm.c | 432 | ||||
-rw-r--r-- | drivers/net/fm/fm.h | 154 | ||||
-rw-r--r-- | drivers/net/fm/init.c | 208 | ||||
-rw-r--r-- | drivers/net/fm/p1023.c | 68 | ||||
-rw-r--r-- | drivers/net/fm/p4080.c | 94 | ||||
-rw-r--r-- | drivers/net/fm/p5020.c | 85 | ||||
-rw-r--r-- | drivers/net/fm/tgec.c | 119 | ||||
-rw-r--r-- | drivers/net/fm/tgec_phy.c | 139 |
11 files changed, 2211 insertions, 0 deletions
diff --git a/drivers/net/fm/Makefile b/drivers/net/fm/Makefile new file mode 100644 index 0000000000..9692575dc8 --- /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 0000000000..a77ee20f3b --- /dev/null +++ b/drivers/net/fm/dtsec.c @@ -0,0 +1,181 @@ +/* + * Copyright 2009-2011 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/types.h> +#include <asm/io.h> +#include <asm/fsl_enet.h> +#include <asm/fsl_dtsec.h> +#include <fsl_mdio.h> +#include <phy.h> + +#include "fm.h" + +#define RCTRL_INIT (RCTRL_GRS | RCTRL_UPROM) +#define TCTRL_INIT TCTRL_GTS +#define MACCFG1_INIT MACCFG1_SOFT_RST + +#define MACCFG2_INIT (MACCFG2_PRE_LEN(0x7) | MACCFG2_LEN_CHECK | \ + MACCFG2_PAD_CRC | MACCFG2_FULL_DUPLEX | \ + MACCFG2_IF_MODE_NIBBLE) + +/* MAXFRM - maximum frame length register */ +#define MAXFRM_MASK 0x00003fff + +static void dtsec_init_mac(struct fsl_enet_mac *mac) +{ + struct dtsec *regs = mac->base; + + /* soft reset */ + out_be32(®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 0000000000..308d610a78 --- /dev/null +++ b/drivers/net/fm/eth.c @@ -0,0 +1,670 @@ +/* + * Copyright 2009-2011 Freescale Semiconductor, Inc. + * Dave Liu <daveliu@freescale.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#include <common.h> +#include <asm/io.h> +#include <malloc.h> +#include <net.h> +#include <hwconfig.h> +#include <fm_eth.h> +#include <fsl_mdio.h> +#include <miiphy.h> +#include <phy.h> +#include <asm/fsl_dtsec.h> +#include <asm/fsl_tgec.h> + +#include "fm.h" + +static struct eth_device *devlist[NUM_FM_PORTS]; +static int num_controllers; + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) && !defined(BITBANGMII) + +#define TBIANA_SETTINGS (TBIANA_ASYMMETRIC_PAUSE | TBIANA_SYMMETRIC_PAUSE | \ + TBIANA_FULL_DUPLEX) + +#define TBIANA_SGMII_ACK 0x4001 + +#define TBICR_SETTINGS (TBICR_ANEG_ENABLE | TBICR_RESTART_ANEG | \ + TBICR_FULL_DUPLEX | TBICR_SPEED1_SET) + +/* Configure the TBI for SGMII operation */ +void dtsec_configure_serdes(struct fm_eth *priv) +{ + struct dtsec *regs = priv->mac->base; + struct tsec_mii_mng *phyregs = priv->mac->phyregs; + + /* + * Access TBI PHY registers at given TSEC register offset as + * opposed to the register offset used for external PHY accesses + */ + tsec_local_mdio_write(phyregs, in_be32(®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 0000000000..23ef14baf4 --- /dev/null +++ b/drivers/net/fm/fm.c @@ -0,0 +1,432 @@ +/* + * Copyright 2009-2011 Freescale Semiconductor, Inc. + * Dave Liu <daveliu@freescale.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#include <common.h> +#include <malloc.h> +#include <asm/io.h> +#include <asm/errno.h> + +#include "fm.h" +#include "../../qe/qe.h" /* For struct qe_firmware */ + +#ifdef CONFIG_SYS_QE_FW_IN_NAND +#include <nand.h> +#elif defined(CONFIG_SYS_QE_FW_IN_SPIFLASH) +#include <spi_flash.h> +#elif defined(CONFIG_SYS_QE_FW_IN_MMC) +#include <mmc.h> +#endif + +struct fm_muram muram[CONFIG_SYS_NUM_FMAN]; + +u32 fm_muram_base(int fm_idx) +{ + return muram[fm_idx].base; +} + +u32 fm_muram_alloc(int fm_idx, u32 size, u32 align) +{ + u32 ret; + u32 align_mask, off; + u32 save; + + align_mask = align - 1; + save = muram[fm_idx].alloc; + + off = save & align_mask; + if (off != 0) + muram[fm_idx].alloc += (align - off); + off = size & align_mask; + if (off != 0) + size += (align - off); + if ((muram[fm_idx].alloc + size) >= muram[fm_idx].top) { + muram[fm_idx].alloc = save; + printf("%s: run out of ram.\n", __func__); + } + + ret = muram[fm_idx].alloc; + muram[fm_idx].alloc += size; + memset((void *)ret, 0, size); + + return ret; +} + +static void fm_init_muram(int fm_idx, void *reg) +{ + u32 base = (u32)reg; + + muram[fm_idx].base = base; + muram[fm_idx].size = CONFIG_SYS_FM_MURAM_SIZE; + muram[fm_idx].alloc = base + FM_MURAM_RES_SIZE; + muram[fm_idx].top = base + CONFIG_SYS_FM_MURAM_SIZE; +} + +/* + * fm_upload_ucode - Fman microcode upload worker function + * + * This function does the actual uploading of an Fman microcode + * to an Fman. + */ +static void fm_upload_ucode(int fm_idx, struct fm_imem *imem, + u32 *ucode, unsigned int size) +{ + unsigned int i; + unsigned int timeout = 1000000; + + /* enable address auto increase */ + out_be32(&imem->iadd, IRAM_IADD_AIE); + /* write microcode to IRAM */ + for (i = 0; i < size / 4; i++) + out_be32(&imem->idata, ucode[i]); + + /* verify if the writing is over */ + out_be32(&imem->iadd, 0); + while ((in_be32(&imem->idata) != ucode[0]) && --timeout) + ; + if (!timeout) + printf("Fman%u: microcode upload timeout\n", fm_idx + 1); + + /* enable microcode from IRAM */ + out_be32(&imem->iready, IRAM_READY); +} + +/* + * Upload an Fman firmware + * + * This function is similar to qe_upload_firmware(), exception that it uploads + * a microcode to the Fman instead of the QE. + * + * Because the process for uploading a microcode to the Fman is similar for + * that of the QE, the QE firmware binary format is used for Fman microcode. + * It should be possible to unify these two functions, but for now we keep them + * separate. + */ +static int fman_upload_firmware(int fm_idx, + struct fm_imem *fm_imem, + const struct qe_firmware *firmware) +{ + unsigned int i; + u32 crc; + size_t calc_size = sizeof(struct qe_firmware); + size_t length; + const struct qe_header *hdr; + + if (!firmware) { + printf("Fman%u: Invalid address for firmware\n", fm_idx + 1); + return -EINVAL; + } + + hdr = &firmware->header; + length = be32_to_cpu(hdr->length); + + /* Check the magic */ + if ((hdr->magic[0] != 'Q') || (hdr->magic[1] != 'E') || + (hdr->magic[2] != 'F')) { + printf("Fman%u: Data at %p is not a firmware\n", fm_idx + 1, + firmware); + return -EPERM; + } + + /* Check the version */ + if (hdr->version != 1) { + printf("Fman%u: Unsupported firmware version %u\n", fm_idx + 1, + hdr->version); + return -EPERM; + } + + /* Validate some of the fields */ + if ((firmware->count != 1)) { + printf("Fman%u: Invalid data in firmware header\n", fm_idx + 1); + return -EINVAL; + } + + /* Validate the length and check if there's a CRC */ + calc_size += (firmware->count - 1) * sizeof(struct qe_microcode); + + for (i = 0; i < firmware->count; i++) + /* + * For situations where the second RISC uses the same microcode + * as the first, the 'code_offset' and 'count' fields will be + * zero, so it's okay to add those. + */ + calc_size += sizeof(u32) * + be32_to_cpu(firmware->microcode[i].count); + + /* Validate the length */ + if (length != calc_size + sizeof(u32)) { + printf("Fman%u: Invalid length in firmware header\n", + fm_idx + 1); + return -EPERM; + } + + /* + * Validate the CRC. We would normally call crc32_no_comp(), but that + * function isn't available unless you turn on JFFS support. + */ + crc = be32_to_cpu(*(u32 *)((void *)firmware + calc_size)); + if (crc != (crc32(-1, (const void *)firmware, calc_size) ^ -1)) { + printf("Fman%u: Firmware CRC is invalid\n", fm_idx + 1); + return -EIO; + } + + /* Loop through each microcode. */ + for (i = 0; i < firmware->count; i++) { + const struct qe_microcode *ucode = &firmware->microcode[i]; + + /* Upload a microcode if it's present */ + if (ucode->code_offset) { + u32 ucode_size; + u32 *code; + printf("Fman%u: Uploading microcode version %u.%u.%u\n", + fm_idx + 1, ucode->major, ucode->minor, + ucode->revision); + code = (void *)firmware + ucode->code_offset; + ucode_size = sizeof(u32) * ucode->count; + fm_upload_ucode(fm_idx, fm_imem, code, ucode_size); + } + } + + return 0; +} + +static u32 fm_assign_risc(int port_id) +{ + u32 risc_sel, val; + risc_sel = (port_id & 0x1) ? FMFPPRC_RISC2 : FMFPPRC_RISC1; + val = (port_id << FMFPPRC_PORTID_SHIFT) & FMFPPRC_PORTID_MASK; + val |= ((risc_sel << FMFPPRC_ORA_SHIFT) | risc_sel); + + return val; +} + +static void fm_init_fpm(struct fm_fpm *fpm) +{ + int i, port_id; + u32 val; + + setbits_be32(&fpm->fmfpee, FMFPEE_EHM | FMFPEE_UEC | + FMFPEE_CER | FMFPEE_DER); + + /* IM mode, each even port ID to RISC#1, each odd port ID to RISC#2 */ + + /* offline/parser port */ + for (i = 0; i < MAX_NUM_OH_PORT; i++) { + port_id = OH_PORT_ID_BASE + i; + val = fm_assign_risc(port_id); + out_be32(&fpm->fpmprc, val); + } + /* Rx 1G port */ + for (i = 0; i < MAX_NUM_RX_PORT_1G; i++) { + port_id = RX_PORT_1G_BASE + i; + val = fm_assign_risc(port_id); + out_be32(&fpm->fpmprc, val); + } + /* Tx 1G port */ + for (i = 0; i < MAX_NUM_TX_PORT_1G; i++) { + port_id = TX_PORT_1G_BASE + i; + val = fm_assign_risc(port_id); + out_be32(&fpm->fpmprc, val); + } + /* Rx 10G port */ + port_id = RX_PORT_10G_BASE; + val = fm_assign_risc(port_id); + out_be32(&fpm->fpmprc, val); + /* Tx 10G port */ + port_id = TX_PORT_10G_BASE; + val = fm_assign_risc(port_id); + out_be32(&fpm->fpmprc, val); + + /* disable the dispatch limit in IM case */ + out_be32(&fpm->fpmflc, FMFP_FLC_DISP_LIM_NONE); + /* clear events */ + out_be32(&fpm->fmfpee, FMFPEE_CLEAR_EVENT); + + /* clear risc events */ + for (i = 0; i < 4; i++) + out_be32(&fpm->fpmcev[i], 0xffffffff); + + /* clear error */ + out_be32(&fpm->fpmrcr, FMFP_RCR_MDEC | FMFP_RCR_IDEC); +} + +static int fm_init_bmi(int fm_idx, struct fm_bmi_common *bmi) +{ + int blk, i, port_id; + u32 val, offset, base; + + /* alloc free buffer pool in MURAM */ + base = fm_muram_alloc(fm_idx, FM_FREE_POOL_SIZE, FM_FREE_POOL_ALIGN); + if (!base) { + printf("%s: no muram for free buffer pool\n", __func__); + return -ENOMEM; + } + offset = base - fm_muram_base(fm_idx); + + /* Need 128KB total free buffer pool size */ + val = offset / 256; + blk = FM_FREE_POOL_SIZE / 256; + /* in IM, we must not begin from offset 0 in MURAM */ + val |= ((blk - 1) << FMBM_CFG1_FBPS_SHIFT); + out_be32(&bmi->fmbm_cfg1, val); + + /* disable all BMI interrupt */ + out_be32(&bmi->fmbm_ier, FMBM_IER_DISABLE_ALL); + + /* clear all events */ + out_be32(&bmi->fmbm_ievr, FMBM_IEVR_CLEAR_ALL); + + /* + * set port parameters - FMBM_PP_x + * max tasks 10G Rx/Tx=12, 1G Rx/Tx 4, others is 1 + * max dma 10G Rx/Tx=3, others is 1 + * set port FIFO size - FMBM_PFS_x + * 4KB for all Rx and Tx ports + */ + /* offline/parser port */ + for (i = 0; i < MAX_NUM_OH_PORT; i++) { + port_id = OH_PORT_ID_BASE + i - 1; + /* max tasks=1, max dma=1, no extra */ + out_be32(&bmi->fmbm_pp[port_id], 0); + /* port FIFO size - 256 bytes, no extra */ + out_be32(&bmi->fmbm_pfs[port_id], 0); + } + /* Rx 1G port */ + for (i = 0; i < MAX_NUM_RX_PORT_1G; i++) { + port_id = RX_PORT_1G_BASE + i - 1; + /* max tasks=4, max dma=1, no extra */ + out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(4)); + /* FIFO size - 4KB, no extra */ + out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf)); + } + /* Tx 1G port FIFO size - 4KB, no extra */ + for (i = 0; i < MAX_NUM_TX_PORT_1G; i++) { + port_id = TX_PORT_1G_BASE + i - 1; + /* max tasks=4, max dma=1, no extra */ + out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(4)); + /* FIFO size - 4KB, no extra */ + out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf)); + } + /* Rx 10G port */ + port_id = RX_PORT_10G_BASE - 1; + /* max tasks=12, max dma=3, no extra */ + out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(12) | FMBM_PP_MXD(3)); + /* FIFO size - 4KB, no extra */ + out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf)); + + /* Tx 10G port */ + port_id = TX_PORT_10G_BASE - 1; + /* max tasks=12, max dma=3, no extra */ + out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(12) | FMBM_PP_MXD(3)); + /* FIFO size - 4KB, no extra */ + out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf)); + + /* initialize internal buffers data base (linked list) */ + out_be32(&bmi->fmbm_init, FMBM_INIT_START); + + return 0; +} + +static void fm_init_qmi(struct fm_qmi_common *qmi) +{ + /* disable enqueue and dequeue of QMI */ + clrbits_be32(&qmi->fmqm_gc, FMQM_GC_ENQ_EN | FMQM_GC_DEQ_EN); + + /* disable all error interrupts */ + out_be32(&qmi->fmqm_eien, FMQM_EIEN_DISABLE_ALL); + /* clear all error events */ + out_be32(&qmi->fmqm_eie, FMQM_EIE_CLEAR_ALL); + + /* disable all interrupts */ + out_be32(&qmi->fmqm_ien, FMQM_IEN_DISABLE_ALL); + /* clear all interrupts */ + out_be32(&qmi->fmqm_ie, FMQM_IE_CLEAR_ALL); +} + +/* Init common part of FM, index is fm num# like fm as above */ +int fm_init_common(int index, struct ccsr_fman *reg) +{ + int rc; + char env_addr[32]; +#if defined(CONFIG_SYS_FMAN_FW_ADDR) + void *addr = (void *)CONFIG_SYS_FMAN_FW_ADDR; +#elif defined(CONFIG_SYS_QE_FW_IN_NAND) + size_t fw_length = CONFIG_SYS_FMAN_FW_LENGTH; + void *addr = malloc(CONFIG_SYS_FMAN_FW_LENGTH); + + rc = nand_read(&nand_info[0], (loff_t)CONFIG_SYS_QE_FW_IN_NAND, + &fw_length, (u_char *)addr); + if (rc == -EUCLEAN) { + printf("NAND read of FMAN firmware at offset 0x%x failed %d\n", + CONFIG_SYS_QE_FW_IN_NAND, rc); + } +#elif defined(CONFIG_SYS_QE_FW_IN_SPIFLASH) + struct spi_flash *ucode_flash; + void *addr = malloc(CONFIG_SYS_FMAN_FW_LENGTH); + int ret = 0; + + ucode_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS, + CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE); + if (!ucode_flash) + printf("SF: probe for ucode failed\n"); + else { + ret = spi_flash_read(ucode_flash, CONFIG_SYS_QE_FW_IN_SPIFLASH, + CONFIG_SYS_FMAN_FW_LENGTH, addr); + if (ret) + printf("SF: read for ucode failed\n"); + spi_flash_free(ucode_flash); + } +#elif defined(CONFIG_SYS_QE_FW_IN_MMC) + int dev = CONFIG_SYS_MMC_ENV_DEV; + void *addr = malloc(CONFIG_SYS_FMAN_FW_LENGTH); + u32 cnt = CONFIG_SYS_FMAN_FW_LENGTH / 512; + u32 n; + u32 blk = CONFIG_SYS_QE_FW_IN_MMC / 512; + struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV); + + if (!mmc) + printf("\nMMC cannot find device for ucode\n"); + else { + printf("\nMMC read: dev # %u, block # %u, count %u ...\n", + dev, blk, cnt); + mmc_init(mmc); + n = mmc->block_dev.block_read(dev, blk, cnt, addr); + /* flush cache after read */ + flush_cache((ulong)addr, cnt * 512); + } +#endif + + /* Upload the Fman microcode if it's present */ + rc = fman_upload_firmware(index, ®->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 0000000000..be6714f27a --- /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 <common.h> +#include <fm_eth.h> +#include <asm/fsl_enet.h> +#include <asm/fsl_fman.h> + +/* Port ID */ +#define OH_PORT_ID_BASE 0x01 +#define MAX_NUM_OH_PORT 7 +#define RX_PORT_1G_BASE 0x08 +#define MAX_NUM_RX_PORT_1G CONFIG_SYS_NUM_FM1_DTSEC +#define RX_PORT_10G_BASE 0x10 +#define TX_PORT_1G_BASE 0x28 +#define MAX_NUM_TX_PORT_1G CONFIG_SYS_NUM_FM1_DTSEC +#define TX_PORT_10G_BASE 0x30 + +struct fm_muram { + u32 base; + u32 top; + u32 size; + u32 alloc; +}; +#define FM_MURAM_RES_SIZE 0x01000 + +/* Rx/Tx buffer descriptor */ +struct fm_port_bd { + u16 status; + u16 len; + u32 res0; + u16 res1; + u16 buf_ptr_hi; + u32 buf_ptr_lo; +}; + +/* Common BD flags */ +#define BD_LAST 0x0800 + +/* Rx BD status flags */ +#define RxBD_EMPTY 0x8000 +#define RxBD_LAST BD_LAST +#define RxBD_FIRST 0x0400 +#define RxBD_PHYS_ERR 0x0008 +#define RxBD_SIZE_ERR 0x0004 +#define RxBD_ERROR (RxBD_PHYS_ERR | RxBD_SIZE_ERR) + +/* Tx BD status flags */ +#define TxBD_READY 0x8000 +#define TxBD_LAST BD_LAST + +/* Rx/Tx queue descriptor */ +struct fm_port_qd { + u16 gen; + u16 bd_ring_base_hi; + u32 bd_ring_base_lo; + u16 bd_ring_size; + u16 offset_in; + u16 offset_out; + u16 res0; + u32 res1[0x4]; +}; + +/* IM global parameter RAM */ +struct fm_port_global_pram { + u32 mode; /* independent mode register */ + u32 rxqd_ptr; /* Rx queue descriptor pointer */ + u32 txqd_ptr; /* Tx queue descriptor pointer */ + u16 mrblr; /* max Rx buffer length */ + u16 rxqd_bsy_cnt; /* RxQD busy counter, should be cleared */ + u32 res0[0x4]; + struct fm_port_qd rxqd; /* Rx queue descriptor */ + struct fm_port_qd txqd; /* Tx queue descriptor */ + u32 res1[0x28]; +}; + +#define FM_PRAM_SIZE sizeof(struct fm_port_global_pram) +#define FM_PRAM_ALIGN 256 +#define PRAM_MODE_GLOBAL 0x20000000 +#define PRAM_MODE_GRACEFUL_STOP 0x00800000 + +#if defined(CONFIG_P1017) || defined(CONFIG_P1023) +#define FM_FREE_POOL_SIZE 0x2000 /* 8K bytes */ +#else +#define FM_FREE_POOL_SIZE 0x20000 /* 128K bytes */ +#endif +#define FM_FREE_POOL_ALIGN 256 + +u32 fm_muram_alloc(int fm_idx, u32 size, u32 align); +u32 fm_muram_base(int fm_idx); +int fm_init_common(int index, struct ccsr_fman *reg); +int fm_eth_initialize(struct ccsr_fman *reg, struct fm_eth_info *info); +phy_interface_t fman_port_enet_if(enum fm_port port); + +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 0000000000..5f05ab14af --- /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 <common.h> +#include <asm/io.h> +#include <asm/fsl_serdes.h> + +#include "fm.h" + +struct fm_eth_info fm_info[] = { +#if (CONFIG_SYS_NUM_FM1_DTSEC >= 1) + FM_DTSEC_INFO_INITIALIZER(1, 1), +#endif +#if (CONFIG_SYS_NUM_FM1_DTSEC >= 2) + FM_DTSEC_INFO_INITIALIZER(1, 2), +#endif +#if (CONFIG_SYS_NUM_FM1_DTSEC >= 3) + FM_DTSEC_INFO_INITIALIZER(1, 3), +#endif +#if (CONFIG_SYS_NUM_FM1_DTSEC >= 4) + FM_DTSEC_INFO_INITIALIZER(1, 4), +#endif +#if (CONFIG_SYS_NUM_FM1_DTSEC >= 5) + FM_DTSEC_INFO_INITIALIZER(1, 5), +#endif +#if (CONFIG_SYS_NUM_FM2_DTSEC >= 1) + FM_DTSEC_INFO_INITIALIZER(2, 1), +#endif +#if (CONFIG_SYS_NUM_FM2_DTSEC >= 2) + FM_DTSEC_INFO_INITIALIZER(2, 2), +#endif +#if (CONFIG_SYS_NUM_FM2_DTSEC >= 3) + FM_DTSEC_INFO_INITIALIZER(2, 3), +#endif +#if (CONFIG_SYS_NUM_FM2_DTSEC >= 4) + FM_DTSEC_INFO_INITIALIZER(2, 4), +#endif +#if (CONFIG_SYS_NUM_FM1_10GEC >= 1) + FM_TGEC_INFO_INITIALIZER(1, 1), +#endif +#if (CONFIG_SYS_NUM_FM2_10GEC >= 1) + FM_TGEC_INFO_INITIALIZER(2, 1), +#endif +}; + +int fm_standard_init(bd_t *bis) +{ + int i; + struct ccsr_fman *reg; + + reg = (void *)CONFIG_SYS_FSL_FM1_ADDR; + if (fm_init_common(0, reg)) + return 0; + + for (i = 0; i < ARRAY_SIZE(fm_info); i++) { + if ((fm_info[i].enabled) && (fm_info[i].index == 1)) + fm_eth_initialize(reg, &fm_info[i]); + } + +#if (CONFIG_SYS_NUM_FMAN == 2) + reg = (void *)CONFIG_SYS_FSL_FM2_ADDR; + if (fm_init_common(1, reg)) + return 0; + + for (i = 0; i < ARRAY_SIZE(fm_info); i++) { + if ((fm_info[i].enabled) && (fm_info[i].index == 2)) + fm_eth_initialize(reg, &fm_info[i]); + } +#endif + + return 1; +} + +/* simple linear search to map from port to array index */ +static int fm_port_to_index(enum fm_port port) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fm_info); i++) { + if (fm_info[i].port == port) + return i; + } + + return -1; +} + +/* + * Determine if an interface is actually active based on HW config + * we expect fman_port_enet_if() to report PHY_INTERFACE_MODE_NONE if + * the interface is not active based on HW cfg of the SoC + */ +void fman_enet_init(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fm_info); i++) { + phy_interface_t enet_if; + + enet_if = fman_port_enet_if(fm_info[i].port); + if (enet_if != PHY_INTERFACE_MODE_NONE) { + fm_info[i].enabled = 1; + fm_info[i].enet_if = enet_if; + } else { + fm_info[i].enabled = 0; + } + } + + return ; +} + +void fm_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 0000000000..c196e79b68 --- /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 <common.h> +#include <phy.h> +#include <fm_eth.h> +#include <asm/io.h> +#include <asm/immap_85xx.h> +#include <asm/fsl_serdes.h> + +u32 port_to_devdisr[] = { + [FM1_DTSEC1] = MPC85xx_DEVDISR_TSEC1, + [FM1_DTSEC2] = MPC85xx_DEVDISR_TSEC2, +}; + +static int is_device_disabled(enum fm_port port) +{ + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + u32 devdisr = in_be32(&gur->devdisr); + + return port_to_devdisr[port] & devdisr; +} + +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 0000000000..6761a2f43b --- /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 <common.h> +#include <phy.h> +#include <fm_eth.h> +#include <asm/io.h> +#include <asm/immap_85xx.h> +#include <asm/fsl_serdes.h> + +u32 port_to_devdisr[] = { + [FM1_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC1_1, + [FM1_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC1_2, + [FM1_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC1_3, + [FM1_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC1_4, + [FM1_10GEC1] = FSL_CORENET_DEVDISR2_10GEC1, + [FM2_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC2_1, + [FM2_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC2_2, + [FM2_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC2_3, + [FM2_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC2_4, + [FM2_10GEC1] = FSL_CORENET_DEVDISR2_10GEC2, +}; + +static int is_device_disabled(enum fm_port port) +{ + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + u32 devdisr2 = in_be32(&gur->devdisr2); + + return port_to_devdisr[port] & devdisr2; +} + +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 0000000000..59638eb2aa --- /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 <common.h> +#include <phy.h> +#include <fm_eth.h> +#include <asm/io.h> +#include <asm/immap_85xx.h> +#include <asm/fsl_serdes.h> + +u32 port_to_devdisr[] = { + [FM1_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC1_1, + [FM1_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC1_2, + [FM1_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC1_3, + [FM1_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC1_4, + [FM1_DTSEC5] = FSL_CORENET_DEVDISR2_DTSEC1_5, + [FM1_10GEC1] = FSL_CORENET_DEVDISR2_10GEC1, +}; + +static int is_device_disabled(enum fm_port port) +{ + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + u32 devdisr2 = in_be32(&gur->devdisr2); + + return port_to_devdisr[port] & devdisr2; +} + +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 0000000000..6c1d471199 --- /dev/null +++ b/drivers/net/fm/tgec.c @@ -0,0 +1,119 @@ +/* + * Copyright 2009-2011 Freescale Semiconductor, Inc. + * Dave Liu <daveliu@freescale.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* MAXFRM - maximum frame length */ +#define MAXFRM_MASK 0x0000ffff + +#include <common.h> +#include <phy.h> +#include <asm/types.h> +#include <asm/io.h> +#include <asm/fsl_enet.h> +#include <asm/fsl_tgec.h> + +#include "fm.h" + +#define TGEC_CMD_CFG_INIT (TGEC_CMD_CFG_NO_LEN_CHK | \ + TGEC_CMD_CFG_RX_ER_DISC | \ + TGEC_CMD_CFG_STAT_CLR | \ + TGEC_CMD_CFG_PAUSE_IGNORE | \ + TGEC_CMD_CFG_CRC_FWD) +#define TGEC_CMD_CFG_FINAL (TGEC_CMD_CFG_NO_LEN_CHK | \ + TGEC_CMD_CFG_RX_ER_DISC | \ + TGEC_CMD_CFG_PAUSE_IGNORE | \ + TGEC_CMD_CFG_CRC_FWD) + +static void tgec_init_mac(struct fsl_enet_mac *mac) +{ + struct tgec *regs = mac->base; + + /* mask all interrupt */ + out_be32(®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 0000000000..2d349ad037 --- /dev/null +++ b/drivers/net/fm/tgec_phy.c @@ -0,0 +1,139 @@ +/* + * Copyright 2009-2011 Freescale Semiconductor, Inc. + * Andy Fleming <afleming@freescale.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * Some part is taken from tsec.c + */ +#include <common.h> +#include <miiphy.h> +#include <phy.h> +#include <asm/io.h> +#include <asm/fsl_tgec.h> +#include <fm_eth.h> + +/* + * Write value to the PHY for this device to the register at regnum, waiting + * until the write is done before it returns. All PHY configuration has to be + * done through the TSEC1 MIIM regs + */ +int tgec_mdio_write(struct mii_dev *bus, int port_addr, int dev_addr, + int regnum, u16 value) +{ + u32 mdio_ctl; + u32 stat_val; + struct tgec_mdio_controller *regs = bus->priv; + + if (dev_addr == MDIO_DEVAD_NONE) + return 0; + + /* Wait till the bus is free */ + stat_val = MDIO_STAT_CLKDIV(100); + out_be32(®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); +} |