diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mmc/Makefile | 1 | ||||
-rw-r--r-- | drivers/mmc/imx_ssp_mmc.c | 371 | ||||
-rw-r--r-- | drivers/net/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/mxc_enet.c | 742 |
4 files changed, 1115 insertions, 0 deletions
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 2902ba5..39a8913 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -34,6 +34,7 @@ COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o COBJS-$(CONFIG_PXA_MMC) += pxa_mmc.o COBJS-$(CONFIG_FSL_MMC) += fsl_esdhc.o COBJS-$(CONFIG_IMX_MMC) += imx_esdhc.o +COBJS-$(CONFIG_IMX_SSP_MMC) += imx_ssp_mmc.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/mmc/imx_ssp_mmc.c b/drivers/mmc/imx_ssp_mmc.c new file mode 100644 index 0000000..61a1d85 --- /dev/null +++ b/drivers/mmc/imx_ssp_mmc.c @@ -0,0 +1,371 @@ +/* + * (C) Copyright 2008-2010 Freescale Semiconductor, Inc. + * Terry Lv + * + * Copyright 2007, Freescale Semiconductor, Inc + * Andy Fleming + * + * Based vaguely on the pxa mmc code: + * (C) Copyright 2003 + * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#include <common.h> +#include <malloc.h> +#include <mmc.h> +#include <asm/arch/mx28.h> +#include <asm/arch/regs-ssp.h> +#include <asm/arch/regs-clkctrl.h> + +#undef IMX_SSP_MMC_DEBUG + +extern void ssp_mmc_board_init(void); +extern u32 ssp_mmc_is_wp(void); + +static inline void mdelay(unsigned long msec) +{ + unsigned long i; + for (i = 0; i < msec; i++) + udelay(1000); +} + +static inline void sdelay(unsigned long sec) +{ + unsigned long i; + for (i = 0; i < sec; i++) + mdelay(1000); +} + +/* + * Sends a command out on the bus. Takes the mmc pointer, + * a command pointer, and an optional data pointer. + */ +static int +ssp_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) +{ + int i; + +#ifdef IMX_SSP_MMC_DEBUG + printf("MMC: CMD%d\n", cmd->cmdidx); +#endif + + /* Check bus busy */ + i = 0; + while (REG_RD(REGS_SSP0_BASE, HW_SSP_STATUS) & (BM_SSP_STATUS_BUSY | + BM_SSP_STATUS_DATA_BUSY | BM_SSP_STATUS_CMD_BUSY)) { + mdelay(1); + i++; + if (i == 1000) { + printf("MMC: Bus busy timeout!\n"); + return TIMEOUT; + } + } + + /* See if card is present */ + if (REG_RD(REGS_SSP0_BASE, HW_SSP_STATUS) & BM_SSP_STATUS_CARD_DETECT) { + printf("MMC: No card detected!\n"); + return NO_CARD_ERR; + } + + /* Clear all control bits except bus width */ + REG_CLR(REGS_SSP0_BASE, HW_SSP_CTRL0, 0xff3fffff); + + /* Set up command */ + if (!(cmd->resp_type & MMC_RSP_CRC)) + REG_SET(REGS_SSP0_BASE, HW_SSP_CTRL0, BM_SSP_CTRL0_IGNORE_CRC); + if (cmd->resp_type & MMC_RSP_PRESENT) /* Need to get response */ + REG_SET(REGS_SSP0_BASE, HW_SSP_CTRL0, BM_SSP_CTRL0_GET_RESP); + if (cmd->resp_type & MMC_RSP_136) /* It's a 136 bits response */ + REG_SET(REGS_SSP0_BASE, HW_SSP_CTRL0, BM_SSP_CTRL0_LONG_RESP); + + /* Command index */ + REG_WR(REGS_SSP0_BASE, HW_SSP_CMD0, + (REG_RD(REGS_SSP0_BASE, HW_SSP_CMD0) & ~BM_SSP_CMD0_CMD) | + (cmd->cmdidx << BP_SSP_CMD0_CMD)); + /* Command argument */ + REG_WR(REGS_SSP0_BASE, HW_SSP_CMD1, cmd->cmdarg); + + /* Set up data */ + if (data) { + /* READ or WRITE */ + if (data->flags & MMC_DATA_READ) { + REG_SET(REGS_SSP0_BASE, HW_SSP_CTRL0, + BM_SSP_CTRL0_READ); + } else if (ssp_mmc_is_wp()) { + printf("MMC: Can not write a locked card!\n"); + return UNUSABLE_ERR; + } + REG_SET(REGS_SSP0_BASE, HW_SSP_CTRL0, BM_SSP_CTRL0_DATA_XFER); + REG_WR(REGS_SSP0_BASE, HW_SSP_BLOCK_SIZE, + ((data->blocks - 1) << + BP_SSP_BLOCK_SIZE_BLOCK_COUNT) | + ((ffs(data->blocksize) - 1) << + BP_SSP_BLOCK_SIZE_BLOCK_SIZE)); + REG_WR(REGS_SSP0_BASE, HW_SSP_XFER_SIZE, + data->blocksize * data->blocks); + } + + /* Kick off the command */ + REG_SET(REGS_SSP0_BASE, HW_SSP_CTRL0, BM_SSP_CTRL0_WAIT_FOR_IRQ); + REG_SET(REGS_SSP0_BASE, HW_SSP_CTRL0, BM_SSP_CTRL0_ENABLE); + REG_SET(REGS_SSP0_BASE, HW_SSP_CTRL0, BM_SSP_CTRL0_RUN); + + /* Wait for the command to complete */ + i = 0; + do { + mdelay(10); + if (i++ == 100) { + printf("MMC: Command %d busy\n", cmd->cmdidx); + break; + } + } while (REG_RD(REGS_SSP0_BASE, HW_SSP_STATUS) & + BM_SSP_STATUS_CMD_BUSY); + + /* Check command timeout */ + if (REG_RD(REGS_SSP0_BASE, HW_SSP_STATUS) & + BM_SSP_STATUS_RESP_TIMEOUT) { +#ifdef IMX_SSP_MMC_DEBUG + printf("MMC: Command %d timeout\n", cmd->cmdidx); +#endif + return TIMEOUT; + } + + /* Check command errors */ + if (REG_RD(REGS_SSP0_BASE, HW_SSP_STATUS) & + (BM_SSP_STATUS_RESP_CRC_ERR | BM_SSP_STATUS_RESP_ERR)) { + printf("MMC: Command %d error (status 0x%08x)!\n", + cmd->cmdidx, REG_RD(REGS_SSP0_BASE, HW_SSP_STATUS)); + return COMM_ERR; + } + + /* Copy response to response buffer */ + if (cmd->resp_type & MMC_RSP_136) { + cmd->response[3] = REG_RD(REGS_SSP0_BASE, HW_SSP_SDRESP0); + cmd->response[2] = REG_RD(REGS_SSP0_BASE, HW_SSP_SDRESP1); + cmd->response[1] = REG_RD(REGS_SSP0_BASE, HW_SSP_SDRESP2); + cmd->response[0] = REG_RD(REGS_SSP0_BASE, HW_SSP_SDRESP3); + } else + cmd->response[0] = REG_RD(REGS_SSP0_BASE, HW_SSP_SDRESP0); + + /* Return if no data to process */ + if (!data) + return 0; + + /* Process the data */ + u32 xfer_cnt = data->blocksize * data->blocks; + u32 *tmp_ptr; + + if (data->flags & MMC_DATA_READ) { + tmp_ptr = (u32 *)data->dest; + while (xfer_cnt > 0) { + if ((REG_RD(REGS_SSP0_BASE, HW_SSP_STATUS) & + BM_SSP_STATUS_FIFO_EMPTY) == 0) { + *tmp_ptr++ = REG_RD(REGS_SSP0_BASE, HW_SSP_DATA); + xfer_cnt -= 4; + } + } + } else { + tmp_ptr = (u32 *)data->src; + while (xfer_cnt > 0) { + if ((REG_RD(REGS_SSP0_BASE, HW_SSP_STATUS) & + BM_SSP_STATUS_FIFO_FULL) == 0) { + REG_WR(REGS_SSP0_BASE, HW_SSP_DATA, *tmp_ptr++); + xfer_cnt -= 4; + } + } + } + + /* Check data errors */ + if (REG_RD(REGS_SSP0_BASE, HW_SSP_STATUS) & + (BM_SSP_STATUS_TIMEOUT | BM_SSP_STATUS_DATA_CRC_ERR | + BM_SSP_STATUS_FIFO_OVRFLW | BM_SSP_STATUS_FIFO_UNDRFLW)) { + printf("MMC: Data error with command %d (status 0x%08x)!\n", + cmd->cmdidx, REG_RD(REGS_SSP0_BASE, HW_SSP_STATUS)); + return COMM_ERR; + } + + return 0; +} + +static void set_bit_clock(u32 clock) +{ + const u32 sspclk = 480000 * 18 / 29 / 1; /* 297931 KHz */ + u32 divide, rate, tgtclk; + + /* + * SSP bit rate = SSPCLK / (CLOCK_DIVIDE * (1 + CLOCK_RATE)), + * CLOCK_DIVIDE has to be an even value from 2 to 254, and + * CLOCK_RATE could be any integer from 0 to 255. + */ + clock /= 1000; /* KHz */ + for (divide = 2; divide < 254; divide += 2) { + rate = sspclk / clock / divide; + if (rate <= 256) + break; + } + + tgtclk = sspclk / divide / rate; + while (tgtclk > clock) { + rate++; + tgtclk = sspclk / divide / rate; + } + if (rate > 256) + rate = 256; + + /* Always set timeout the maximum */ + REG_WR(REGS_SSP0_BASE, HW_SSP_TIMING, BM_SSP_TIMING_TIMEOUT | + divide << BP_SSP_TIMING_CLOCK_DIVIDE | + (rate - 1) << BP_SSP_TIMING_CLOCK_RATE); + +#ifdef IMX_SSP_MMC_DEBUG + printf("MMC: Set clock rate to %d KHz (requested %d KHz)\n", + tgtclk, clock); +#endif +} + +static void ssp_mmc_set_ios(struct mmc *mmc) +{ + u32 regval; + + /* Set the clock speed */ + if (mmc->clock) + set_bit_clock(mmc->clock); + + /* Set the bus width */ + regval = REG_RD(REGS_SSP0_BASE, HW_SSP_CTRL0); + regval &= ~BM_SSP_CTRL0_BUS_WIDTH; + switch (mmc->bus_width) { + case 1: + regval |= (BV_SSP_CTRL0_BUS_WIDTH__ONE_BIT << + BP_SSP_CTRL0_BUS_WIDTH); + break; + case 4: + regval |= (BV_SSP_CTRL0_BUS_WIDTH__FOUR_BIT << + BP_SSP_CTRL0_BUS_WIDTH); + break; + case 8: + regval |= (BV_SSP_CTRL0_BUS_WIDTH__EIGHT_BIT << + BP_SSP_CTRL0_BUS_WIDTH); + } + REG_WR(REGS_SSP0_BASE, HW_SSP_CTRL0, regval); + +#ifdef IMX_SSP_MMC_DEBUG + printf("MMC: Set %d bits bus width\n", mmc->bus_width); +#endif +} + +static int ssp_mmc_init(struct mmc *mmc) +{ + u32 regval; + + /* Board level init */ + ssp_mmc_board_init(); + + /* + * Set up SSPCLK + */ + /* Set REF_IO0 at 480 MHz */ + regval = REG_RD(REGS_CLKCTRL_BASE, HW_CLKCTRL_FRAC0); + regval &= ~BM_CLKCTRL_FRAC0_IO0FRAC; + REG_WR(REGS_CLKCTRL_BASE, HW_CLKCTRL_FRAC0, + regval | (29 << BP_CLKCTRL_FRAC0_IO0FRAC)); + /* Enable REF_IO0 */ + REG_CLR(REGS_CLKCTRL_BASE, HW_CLKCTRL_FRAC0, + BM_CLKCTRL_FRAC0_CLKGATEIO0); + /* Source SSPCLK from REF_IO0 */ + REG_CLR(REGS_CLKCTRL_BASE, HW_CLKCTRL_CLKSEQ, + BM_CLKCTRL_CLKSEQ_BYPASS_SSP0); + /* Turn on SSPCLK */ + REG_WR(REGS_CLKCTRL_BASE, HW_CLKCTRL_SSP0, + REG_RD(REGS_CLKCTRL_BASE, HW_CLKCTRL_SSP0) & + ~BM_CLKCTRL_SSP0_CLKGATE); + /* Set SSPCLK divide 1 */ + regval = REG_RD(REGS_CLKCTRL_BASE, HW_CLKCTRL_SSP0); + regval &= ~(BM_CLKCTRL_SSP0_DIV_FRAC_EN | BM_CLKCTRL_SSP0_DIV); + REG_WR(REGS_CLKCTRL_BASE, HW_CLKCTRL_SSP0, + regval | (1 << BP_CLKCTRL_SSP0_DIV)); + /* Wait for new divide ready */ + do { + udelay(10); + } while (REG_RD(REGS_CLKCTRL_BASE, HW_CLKCTRL_SSP0) & + BM_CLKCTRL_SSP0_BUSY); + + /* Prepare for software reset */ + REG_CLR(REGS_SSP0_BASE, HW_SSP_CTRL0, BM_SSP_CTRL0_SFTRST); + REG_CLR(REGS_SSP0_BASE, HW_SSP_CTRL0, BM_SSP_CTRL0_CLKGATE); + /* Assert reset */ + REG_SET(REGS_SSP0_BASE, HW_SSP_CTRL0, BM_SSP_CTRL0_SFTRST); + /* Wait for confirmation */ + while (!(REG_RD(REGS_SSP0_BASE, HW_SSP_CTRL0) & BM_SSP_CTRL0_CLKGATE)) + ; + /* Done */ + REG_CLR(REGS_SSP0_BASE, HW_SSP_CTRL0, BM_SSP_CTRL0_SFTRST); + REG_CLR(REGS_SSP0_BASE, HW_SSP_CTRL0, BM_SSP_CTRL0_CLKGATE); + + /* 8 bits word length in MMC mode */ + regval = REG_RD(REGS_SSP0_BASE, HW_SSP_CTRL1); + regval &= ~(BM_SSP_CTRL1_SSP_MODE | BM_SSP_CTRL1_WORD_LENGTH); + REG_WR(REGS_SSP0_BASE, HW_SSP_CTRL1, regval | + (BV_SSP_CTRL1_SSP_MODE__SD_MMC << BP_SSP_CTRL1_SSP_MODE) | + (BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS << + BP_SSP_CTRL1_WORD_LENGTH)); + + /* Set initial bit clock 400 KHz */ + set_bit_clock(400000); + + /* Send initial 74 clock cycles (185 us @ 400 KHz)*/ + REG_SET(REGS_SSP0_BASE, HW_SSP_CMD0, BM_SSP_CMD0_CONT_CLKING_EN); + udelay(200); + REG_CLR(REGS_SSP0_BASE, HW_SSP_CMD0, BM_SSP_CMD0_CONT_CLKING_EN); + + return 0; +} + +int imx_ssp_mmc_initialize(bd_t *bis) +{ + struct mmc *mmc; + + mmc = malloc(sizeof(struct mmc)); + + sprintf(mmc->name, "IMX_SSP_MMC"); + mmc->send_cmd = ssp_mmc_send_cmd; + mmc->set_ios = ssp_mmc_set_ios; + mmc->init = ssp_mmc_init; + + mmc->voltages = MMC_VDD_32_33 | MMC_VDD_31_32 | MMC_VDD_30_31 | + MMC_VDD_29_30 | MMC_VDD_28_29 | MMC_VDD_27_28; + + mmc->host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT | + MMC_MODE_HS_52MHz | MMC_MODE_HS; + + /* + * SSPCLK = 480 * 18 / 29 / 1 = 297.731 MHz + * SSP bit rate = SSPCLK / (CLOCK_DIVIDE * (1 + CLOCK_RATE)), + * CLOCK_DIVIDE has to be an even value from 2 to 254, and + * CLOCK_RATE could be any integer from 0 to 255. + */ + mmc->f_min = 400000; + mmc->f_max = 148000000; /* 297.731 MHz / 2 */ + + mmc_register(mmc); + + return 0; +} + diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 6ba00be..9318015 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -50,6 +50,7 @@ COBJS-$(CONFIG_MCFFEC) += mcffec.o mcfmii.o COBJS-$(CONFIG_MPC5xxx_FEC) += mpc5xxx_fec.o COBJS-$(CONFIG_MPC512x_FEC) += mpc512x_fec.o COBJS-$(CONFIG_MXC_FEC) += mxc_fec.o +COBJS-$(CONFIG_MXC_ENET) += mxc_enet.o COBJS-$(CONFIG_NATSEMI) += natsemi.o COBJS-$(CONFIG_DRIVER_NE2000) += ne2000.o ne2000_base.o COBJS-$(CONFIG_DRIVER_AX88796L) += ax88796.o ne2000_base.o diff --git a/drivers/net/mxc_enet.c b/drivers/net/mxc_enet.c new file mode 100644 index 0000000..edfbc1a --- /dev/null +++ b/drivers/net/mxc_enet.c @@ -0,0 +1,742 @@ +/* + * (C) Copyright 2000-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2008-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 <common.h> +#include <malloc.h> +#include <net.h> +#include <miiphy.h> +#include <asm/arch/mx28.h> +#include <asm/arch/regs-enet.h> +#include <asm/arch/regs-clkctrl.h> + +/* + * Debug message switch + */ +#undef MXC_ENET_DEBUG + +/* + * Buffer descriptor control/status used by Ethernet receive. + */ +#define BD_ENET_RX_EMPTY ((ushort)0x8000) +#define BD_ENET_RX_RO1 ((ushort)0x4000) +#define BD_ENET_RX_WRAP ((ushort)0x2000) +#define BD_ENET_RX_INTR ((ushort)0x1000) +#define BD_ENET_RX_RO2 BD_ENET_RX_INTR +#define BD_ENET_RX_LAST ((ushort)0x0800) +#define BD_ENET_RX_FIRST ((ushort)0x0400) +#define BD_ENET_RX_MISS ((ushort)0x0100) +#define BD_ENET_RX_BC ((ushort)0x0080) +#define BD_ENET_RX_MC ((ushort)0x0040) +#define BD_ENET_RX_LG ((ushort)0x0020) +#define BD_ENET_RX_NO ((ushort)0x0010) +#define BD_ENET_RX_SH ((ushort)0x0008) +#define BD_ENET_RX_CR ((ushort)0x0004) +#define BD_ENET_RX_OV ((ushort)0x0002) +#define BD_ENET_RX_CL ((ushort)0x0001) +#define BD_ENET_RX_TR BD_ENET_RX_CL +#define BD_ENET_RX_STATS ((ushort)0x013f) /* All status bits */ + +/* + * Buffer descriptor control/status used by Ethernet transmit. + */ +#define BD_ENET_TX_READY ((ushort)0x8000) +#define BD_ENET_TX_PAD ((ushort)0x4000) +#define BD_ENET_TX_TO1 BD_ENET_TX_PAD +#define BD_ENET_TX_WRAP ((ushort)0x2000) +#define BD_ENET_TX_INTR ((ushort)0x1000) +#define BD_ENET_TX_TO2 BD_ENET_TX_INTR_ +#define BD_ENET_TX_LAST ((ushort)0x0800) +#define BD_ENET_TX_TC ((ushort)0x0400) +#define BD_ENET_TX_DEF ((ushort)0x0200) +#define BD_ENET_TX_ABC BD_ENET_TX_DEF +#define BD_ENET_TX_HB ((ushort)0x0100) +#define BD_ENET_TX_LC ((ushort)0x0080) +#define BD_ENET_TX_RL ((ushort)0x0040) +#define BD_ENET_TX_RCMASK ((ushort)0x003c) +#define BD_ENET_TX_UN ((ushort)0x0002) +#define BD_ENET_TX_CSL ((ushort)0x0001) +#define BD_ENET_TX_STATS ((ushort)0x03ff) /* All status bits */ + +/* + * Buffer descriptors + */ +typedef struct cpm_buf_desc { + ushort cbd_datlen; /* Data length in buffer */ + ushort cbd_sc; /* Status and Control */ + uint cbd_bufaddr; /* Buffer address in host memory */ +} cbd_t; + +/* ENET private information */ +struct enet_info_s { + int index; + u32 iobase; + int phy_addr; + int dup_spd; + char *phy_name; + int phyname_init; + cbd_t *rxbd; /* Rx BD */ + cbd_t *txbd; /* Tx BD */ + uint rxIdx; + uint txIdx; + char *rxbuf; + char *txbuf; + int initialized; + struct enet_info_s *next; +}; + +/* Register read/write struct */ +typedef struct enet { + u32 resv0; /* 0x0000 */ + u32 eir; /* 0x0004 */ + u32 eimr; /* 0x0008 */ + u32 resv1; /* 0x000c */ + u32 rdar; /* 0x0010 */ + u32 tdar; /* 0x0014 */ + u32 resv2[3]; /* 0x0018 */ + u32 ecr; /* 0x0024 */ + u32 resv3[6]; /* 0x0028 */ + u32 mmfr; /* 0x0040 */ + u32 mscr; /* 0x0044 */ + u32 resv4[7]; /* 0x0048 */ + u32 mibc; /* 0x0064 */ + u32 resv5[7]; /* 0x0068 */ + u32 rcr; /* 0x0084 */ + u32 resv6[15]; /* 0x0088 */ + u32 tcr; /* 0x00c4 */ + u32 resv7[7]; /* 0x00c8 */ + u32 palr; /* 0x00e4 */ + u32 paur; /* 0x00e8 */ + u32 opd; /* 0x00ec */ + u32 resv8[10]; /* 0x00f0 */ + u32 iaur; /* 0x0118 */ + u32 ialr; /* 0x011c */ + u32 gaur; /* 0x0120 */ + u32 galr; /* 0x0124 */ + u32 resv9[7]; /* 0x0128 */ + u32 tfwr; /* 0x0144 */ + u32 resv10; /* 0x0148 */ + u32 frbr; /* 0x014c */ + u32 frsr; /* 0x0150 */ + u32 resv11[11]; /* 0x0154 */ + u32 erdsr; /* 0x0180 */ + u32 etdsr; /* 0x0184 */ + u32 emrbr; /* 0x0188 */ + /* Unused registers ... */ +} enet_t; + +/* + * Ethernet Transmit and Receive Buffers + */ +#define DBUF_LENGTH 1520 +#define RX_BUF_CNT (PKTBUFSRX) +#define TX_BUF_CNT (RX_BUF_CNT) +#define PKT_MINBUF_SIZE 64 +#define PKT_MAXBLR_SIZE 1520 +#define LAST_RX_BUF (RX_BUF_CNT - 1) +#define BD_ENET_RX_W_E (BD_ENET_RX_WRAP | BD_ENET_RX_EMPTY) +#define BD_ENET_TX_RDY_LST (BD_ENET_TX_READY | BD_ENET_TX_LAST) + +/* + * MII definitions + */ +#define ENET_MII_ST 0x40000000 +#define ENET_MII_OP_OFF 28 +#define ENET_MII_OP_MASK 0x03 +#define ENET_MII_OP_RD 0x02 +#define ENET_MII_OP_WR 0x01 +#define ENET_MII_PA_OFF 23 +#define ENET_MII_PA_MASK 0xFF +#define ENET_MII_RA_OFF 18 +#define ENET_MII_RA_MASK 0xFF +#define ENET_MII_TA 0x00020000 +#define ENET_MII_DATA_OFF 0 +#define ENET_MII_DATA_MASK 0x0000FFFF + +#define ENET_MII_FRAME \ + (ENET_MII_ST | ENET_MII_TA) + +#define ENET_MII_OP(x) \ + (((x) & ENET_MII_OP_MASK) << ENET_MII_OP_OFF) + +#define ENET_MII_PA(pa) \ + (((pa) & ENET_MII_PA_MASK) << ENET_MII_PA_OFF) + +#define ENET_MII_RA(ra) \ + (((ra) & ENET_MII_RA_MASK) << ENET_MII_RA_OFF) + +#define ENET_MII_SET_DATA(v) \ + (((v) & ENET_MII_DATA_MASK) << ENET_MII_DATA_OFF) + +#define ENET_MII_GET_DATA(v) \ + (((v) >> ENET_MII_DATA_OFF) & ENET_MII_DATA_MASK) + +#define ENET_MII_READ(pa, ra) \ + ((ENET_MII_FRAME | ENET_MII_OP(ENET_MII_OP_RD)) | \ + ENET_MII_PA(pa) | ENET_MII_RA(ra)) + +#define ENET_MII_WRITE(pa, ra, v) \ + (ENET_MII_FRAME | ENET_MII_OP(ENET_MII_OP_WR) | \ + ENET_MII_PA(pa) | ENET_MII_RA(ra) | ENET_MII_SET_DATA(v)) + +#define ENET_MII_TIMEOUT 50000 +#define ENET_MII_TICK 2 +#define ENET_MII_PHYADDR 0x0 + +/* + * Misc definitions + */ +#ifndef CONFIG_SYS_CACHELINE_SIZE +#define CONFIG_SYS_CACHELINE_SIZE 32 +#endif + +#define ENET_RESET_DELAY 100 +#define ENET_MAX_TIMEOUT 50000 +#define ENET_TIMEOUT_TICKET 2 + +#define __swap_32(x) ((((unsigned long)x) << 24) | \ + ((0x0000FF00UL & ((unsigned long)x)) << 8) | \ + ((0x00FF0000UL & ((unsigned long)x)) >> 8) | \ + (((unsigned long)x) >> 24)) + +/* + * Functions + */ +extern void enet_board_init(void); + +#if defined(CONFIG_CMD_NET) && defined(CONFIG_NET_MULTI) +static struct enet_info_s enet_info[] = { + { + 0, /* index */ + REGS_ENET_BASE, /* io base */ +#ifdef CONFIG_DISCOVER_PHY + -1, /* discover phy_addr */ +#else + ENET_MII_PHYADDR, /* phy_addr for MAC0 */ +#endif + 0, /* duplex and speed */ + 0, /* phy name */ + 0, /* phyname init */ + 0, /* RX BD */ + 0, /* TX BD */ + 0, /* rx Index */ + 0, /* tx Index */ + 0, /* tx buffer */ + 0 /* initialized flag */ + } +}; + +static inline int __enet_mii_read(volatile enet_t *enetp, unsigned char addr, + unsigned char reg, unsigned short *value) +{ + int waiting = ENET_MII_TIMEOUT; + if (enetp->eir & BM_ENET_MAC0_EIR_MII) + enetp->eir |= BM_ENET_MAC0_EIR_MII; + + enetp->mmfr = ENET_MII_READ(addr, reg); + while (1) { + if (enetp->eir & BM_ENET_MAC0_EIR_MII) { + enetp->eir |= BM_ENET_MAC0_EIR_MII; + break; + } + if ((waiting--) <= 0) + return -1; + udelay(ENET_MII_TICK); + } + *value = ENET_MII_GET_DATA(enetp->mmfr); + return 0; +} + +static inline int __enet_mii_write(volatile enet_t *enetp, unsigned char addr, + unsigned char reg, unsigned short value) +{ + int waiting = ENET_MII_TIMEOUT; + if (enetp->eir & BM_ENET_MAC0_EIR_MII) + enetp->eir |= BM_ENET_MAC0_EIR_MII; + + enetp->mmfr = ENET_MII_WRITE(addr, reg, value); + while (1) { + if (enetp->eir & BM_ENET_MAC0_EIR_MII) { + enetp->eir |= BM_ENET_MAC0_EIR_MII; + break; + } + if ((waiting--) <= 0) + return -1; + udelay(ENET_MII_TICK); + } + return 0; +} + +static int mxc_enet_mii_read(char *devname, unsigned char addr, + unsigned char reg, unsigned short *value) +{ + struct eth_device *dev = eth_get_dev_by_name(devname); + struct enet_info_s *info; + volatile enet_t *enetp; + + if (!dev) + return -1; + info = dev->priv; + enetp = (enet_t *) (info->iobase); + return __enet_mii_read(enetp, addr, reg, value); +} + +static int mxc_enet_mii_write(char *devname, unsigned char addr, + unsigned char reg, unsigned short value) +{ + struct eth_device *dev = eth_get_dev_by_name(devname); + struct enet_info_s *info; + volatile enet_t *enetp; + if (!dev) + return -1; + info = dev->priv; + enetp = (enet_t *) (info->iobase); + return __enet_mii_write(enetp, addr, reg, value); +} + +static void mxc_enet_mii_init(volatile enet_t *enetp) +{ + /* Set RMII mode */ + enetp->rcr |= BM_ENET_MAC0_RCR_RMII_MODE; + + /* The phy requires MDC clock below 2.5 MHz */ + enetp->mscr |= (enetp->mscr & ~BM_ENET_MAC0_MSCR_MII_SPEED) | + (40 << BP_ENET_MAC0_MSCR_MII_SPEED); +} + +#ifdef CONFIG_DISCOVER_PHY +static inline int __enet_mii_info(volatile enet_t *enetp, unsigned char addr) +{ + unsigned int id = 0; + unsigned short val; + + if (__enet_mii_read(enetp, addr, PHY_PHYIDR2, &val) != 0) + return -1; + id = val; + if (id == 0xffff) + return -1; + + if (__enet_mii_read(enetp, addr, PHY_PHYIDR1, &val) != 0) + return -1; + + if (val == 0xffff) + return -1; + + id |= val << 16; + +#ifdef MXC_ENMXC_ENET_DEBUG + printf("PHY indentify @ 0x%x = 0x%08x\n", addr, id); +#endif + return 0; +} + +static int mxc_enet_mii_discover_phy(struct eth_device *dev) +{ + unsigned short addr, val; + struct enet_info_s *info = dev->priv; + volatile enet_t *enetp = (enet_t *) (info->iobase); + + /* Dummy read with delay to get phy start working */ + do { + __enet_mii_read(enetp, ENET_MII_PHYADDR, PHY_PHYIDR1, &val); + udelay(10000); +#ifdef MXC_ENET_DEBUG + printf("Dummy read on phy\n"); +#endif + } while (val == 0 || val == 0xffff); + + /* Read phy ID */ + for (addr = 0; addr < 0x20; addr++) { + if (!__enet_mii_info(enetp, addr)) + return addr; + } + + return -1; +} +#endif + +static void set_duplex_speed(volatile enet_t *enetp, unsigned char addr, + int dup_spd) +{ + unsigned short val; + int ret; + + ret = __enet_mii_read(enetp, addr, PHY_BMCR, &val); + switch (dup_spd >> 16) { + case HALF: + val &= (~PHY_BMCR_DPLX); + break; + case FULL: + val |= PHY_BMCR_DPLX; + break; + default: + val |= PHY_BMCR_AUTON | PHY_BMCR_RST_NEG; + } + ret |= __enet_mii_write(enetp, addr, PHY_BMCR, val); + + if (!ret && (val & PHY_BMCR_AUTON)) { + ret = 0; + while (ret++ < ENET_MII_TIMEOUT) { + if (__enet_mii_read(enetp, addr, PHY_BMSR, &val)) + break; + if (!(val & PHY_BMSR_AUTN_ABLE)) + break; + if (val & PHY_BMSR_AUTN_COMP) + break; + } + } + + if (__enet_mii_read(enetp, addr, PHY_BMSR, &val)) { + dup_spd = _100BASET | (FULL << 16); + } else { + if (val & (PHY_BMSR_100TXF | PHY_BMSR_100TXH | PHY_BMSR_100T4)) + dup_spd = _100BASET; + else + dup_spd = _10BASET; + if (val & (PHY_BMSR_100TXF | PHY_BMSR_10TF)) + dup_spd |= (FULL << 16); + else + dup_spd |= (HALF << 16); + } + + if ((dup_spd >> 16) == FULL) { + enetp->tcr |= BM_ENET_MAC0_TCR_FEDN; +#ifdef MXC_ENET_DEBUG + printf("full duplex, "); +#endif + } else { + enetp->rcr |= BM_ENET_MAC0_RCR_DRT; + enetp->tcr &= ~BM_ENET_MAC0_TCR_FEDN; +#ifdef MXC_ENET_DEBUG + printf("half duplex, "); +#endif + } +#ifdef MXC_ENET_DEBUG + if ((dup_spd & 0xffff) == _100BASET) + printf("100 Mbps\n"); + else + printf("10 Mbps\n"); +#endif +} + +static void copy_packet(char *pdst, char *psrc, int length) +{ + long *pldst = (long *)pdst; + long *plsrc = (long *)psrc; + + length /= sizeof(long); + while (length--) { + *pldst = __swap_32(*plsrc); + pldst++; + plsrc++; + } +} + +static int enet_send(struct eth_device *dev, volatile void *packet, int length) +{ + struct enet_info_s *info = dev->priv; + volatile enet_t *enetp = (enet_t *) (info->iobase); + int i; + u16 phyStatus; + + __enet_mii_read(enetp, info->phy_addr, PHY_BMSR, &phyStatus); + + if (!(phyStatus & PHY_BMSR_LS)) { + printf("ENET: Link is down %x\n", phyStatus); + return -1; + } + + /* Wait for ready */ + i = 0; + while ((info->txbd[info->txIdx].cbd_sc & BD_ENET_TX_READY) && + (i < ENET_MAX_TIMEOUT)) { + udelay(ENET_TIMEOUT_TICKET); + i++; + } + if (i >= ENET_MAX_TIMEOUT) + printf("TX buffer is NOT ready\n"); + + /* Manipulate the packet buffer */ + copy_packet((char *)info->txbd[info->txIdx].cbd_bufaddr, + (char *)packet, length + (4 - length % 4)); + + /* Set up transmit Buffer Descriptor */ + info->txbd[info->txIdx].cbd_datlen = length; + info->txbd[info->txIdx].cbd_sc = + (info->txbd[info->txIdx].cbd_sc & BD_ENET_TX_WRAP) | + BD_ENET_TX_TC | BD_ENET_TX_RDY_LST; + + /* Activate transmit Buffer Descriptor polling */ + enetp->tdar = 0x01000000; + + /* Move Buffer Descriptor to the next */ + info->txIdx = (info->txIdx + 1) % TX_BUF_CNT; + + return length; +} + +static int enet_recv(struct eth_device *dev) +{ + struct enet_info_s *info = dev->priv; + volatile enet_t *enetp = (enet_t *) (info->iobase); + int length; + + for (;;) { + if (info->rxbd[info->rxIdx].cbd_sc & BD_ENET_RX_EMPTY) { + length = -1; + break; /* nothing received - leave for() loop */ + } + + length = info->rxbd[info->rxIdx].cbd_datlen; + + if (info->rxbd[info->rxIdx].cbd_sc & 0x003f) { +#ifdef MXC_ENET_DEBUG + printf("%s[%d] err: %x\n", + __func__, __LINE__, + info->rxbd[info->rxIdx].cbd_sc); +#endif + } else { + length -= 4; + + /* Manipulate the packet buffer */ + copy_packet((char *)NetRxPackets[info->rxIdx], + (char *)info->rxbd[info->rxIdx].cbd_bufaddr, + length + (4 - length % 4)); + + /* Pass the packet up to the protocol layers. */ + NetReceive(NetRxPackets[info->rxIdx], length); + } + + /* Give the buffer back to the ENET */ + info->rxbd[info->rxIdx].cbd_datlen = 0; + + /* Wrap around buffer index when necessary */ + if (info->rxIdx == LAST_RX_BUF) { + info->rxbd[RX_BUF_CNT - 1].cbd_sc = BD_ENET_RX_W_E; + info->rxIdx = 0; + } else { + info->rxbd[info->rxIdx].cbd_sc = BD_ENET_RX_EMPTY; + info->rxIdx++; + } + + /* Try to fill Buffer Descriptors */ + enetp->rdar = 0x01000000; + } + + return length; +} + +static void enet_reset(struct eth_device *dev) +{ + int i; + struct enet_info_s *info = dev->priv; + volatile enet_t *enetp = (enet_t *) (info->iobase); + + enetp->ecr |= BM_ENET_MAC0_ECR_RESET; + for (i = 0; (enetp->ecr & BM_ENET_MAC0_ECR_RESET) && (i < ENET_RESET_DELAY); ++i) + udelay(1); + + if (i == ENET_RESET_DELAY) + printf("ENET reset timeout\n"); +} + +static void enet_halt(struct eth_device *dev) +{ + struct enet_info_s *info = dev->priv; + + /* Reset ENET controller to stop transmit and receive */ + enet_reset(dev); + + /* Clear buffers */ + info->rxIdx = info->txIdx = 0; + memset(info->rxbd, 0, RX_BUF_CNT * sizeof(cbd_t)); + memset(info->txbd, 0, TX_BUF_CNT * sizeof(cbd_t)); + memset(info->rxbuf, 0, RX_BUF_CNT * DBUF_LENGTH); + memset(info->txbuf, 0, TX_BUF_CNT * DBUF_LENGTH); +} + +static int enet_init(struct eth_device *dev, bd_t *bd) +{ + struct enet_info_s *info = dev->priv; + volatile enet_t *enetp = (enet_t *) (info->iobase); + int i; + u8 *ea = NULL; + + /* Turn on ENET clocks */ + REG_WR(REGS_CLKCTRL_BASE, HW_CLKCTRL_ENET, + REG_RD(REGS_CLKCTRL_BASE, HW_CLKCTRL_ENET) & + ~(BM_CLKCTRL_ENET_SLEEP | BM_CLKCTRL_ENET_DISABLE)); + + /* Set up ENET PLL for 50 MHz */ + REG_SET(REGS_CLKCTRL_BASE, HW_CLKCTRL_PLL2CTRL0, + BM_CLKCTRL_PLL2CTRL0_POWER); /* Power on ENET PLL */ + udelay(10); /* Wait 10 us */ + REG_CLR(REGS_CLKCTRL_BASE, HW_CLKCTRL_PLL2CTRL0, + BM_CLKCTRL_PLL2CTRL0_CLKGATE); /* Gate on ENET PLL */ + REG_WR(REGS_CLKCTRL_BASE, HW_CLKCTRL_ENET, + REG_RD(REGS_CLKCTRL_BASE, HW_CLKCTRL_ENET) | + BM_CLKCTRL_ENET_CLK_OUT_EN); /* Enable pad output */ + + /* Board level init */ + enet_board_init(); + + /* Reset ENET controller */ + enet_reset(dev); + +#if defined(CONFIG_CMD_MII) || defined(CONFIG_MII) || \ + defined(CONFIG_DISCOVER_PHY) + mxc_enet_mii_init(enetp); +#ifdef CONFIG_DISCOVER_PHY + if (info->phy_addr < 0 || info->phy_addr > 0x1f) + info->phy_addr = mxc_enet_mii_discover_phy(dev); +#endif + set_duplex_speed(enetp, (unsigned char)info->phy_addr, info->dup_spd); +#else +#ifndef CONFIG_DISCOVER_PHY + set_duplex_speed(enetp, (unsigned char)info->phy_addr, + (ENETDUPLEX << 16) | ENETSPEED); +#endif +#endif + /* We use strictly polling mode only */ + enetp->eimr = 0; + + /* Clear any pending interrupt */ + enetp->eir = 0xffffffff; + + /* Disable loopback mode */ + enetp->rcr &= ~BM_ENET_MAC0_RCR_LOOP; + + /* Enable RX flow control */ + enetp->rcr |= BM_ENET_MAC0_RCR_FCE; + + /* Set station address and enable it for TX */ + ea = dev->enetaddr; + enetp->palr = (ea[0] << 24) | (ea[1] << 16) | (ea[2] << 8) | (ea[3]); + enetp->paur = (ea[4] << 24) | (ea[5] << 16); + enetp->tcr |= BM_ENET_MAC0_TCR_TX_ADDR_INS; + + /* Clear unicast address hash table */ + enetp->iaur = 0; + enetp->ialr = 0; + + /* Clear multicast address hash table */ + enetp->gaur = 0; + enetp->galr = 0; + + /* Set maximum receive buffer size. */ + enetp->emrbr = PKT_MAXBLR_SIZE; + + /* Setup Buffers and Buffer Desriptors */ + info->rxIdx = 0; + info->txIdx = 0; + + /* + * Setup Receiver Buffer Descriptors + * Settings: + * Empty, Wrap + */ + for (i = 0; i < RX_BUF_CNT; i++) { + info->rxbd[i].cbd_sc = BD_ENET_RX_EMPTY; + info->rxbd[i].cbd_datlen = 0; /* Reset */ + info->rxbd[i].cbd_bufaddr = + (uint) (&info->rxbuf[0] + i * DBUF_LENGTH); + } + info->rxbd[RX_BUF_CNT - 1].cbd_sc |= BD_ENET_RX_WRAP; + + /* + * Setup Transmitter Buffer Descriptors + * Settings: + * Last, Tx CRC + */ + for (i = 0; i < TX_BUF_CNT; i++) { + info->txbd[i].cbd_sc = BD_ENET_TX_LAST | BD_ENET_TX_TC; + info->txbd[i].cbd_datlen = 0; /* Reset */ + info->txbd[i].cbd_bufaddr = + (uint) (&info->txbuf[0] + i * DBUF_LENGTH); + } + info->txbd[TX_BUF_CNT - 1].cbd_sc |= BD_ENET_TX_WRAP; + + /* Set receive and transmit descriptor base */ + enetp->erdsr = (unsigned int)(&info->rxbd[0]); + enetp->etdsr = (unsigned int)(&info->txbd[0]); + + /* Now enable the transmit and receive processing */ + enetp->ecr |= BM_ENET_MAC0_ECR_ETHER_EN; + + /* And last, try to fill Rx Buffer Descriptors */ + enetp->rdar = 0x01000000; + + return 0; +} + +int mxc_enet_initialize(bd_t *bis) +{ + struct eth_device *dev; + int i; + + for (i = 0; i < sizeof(enet_info) / sizeof(enet_info[0]); i++) { + dev = (struct eth_device *) memalign(CONFIG_SYS_CACHELINE_SIZE, + sizeof(*dev)); + if (dev == NULL) + hang(); + + memset(dev, 0, sizeof(*dev)); + + /* Regiester device */ + sprintf(dev->name, "ENET%d", enet_info[i].index); + dev->priv = &enet_info[i]; + dev->init = enet_init; + dev->halt = enet_halt; + dev->send = enet_send; + dev->recv = enet_recv; + eth_register(dev); +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register(dev->name, mxc_enet_mii_read, mxc_enet_mii_write); +#endif + /* Setup Receive and Transmit buffer descriptor */ + enet_info[i].rxbd = + (cbd_t *) memalign(CONFIG_SYS_CACHELINE_SIZE, + RX_BUF_CNT * sizeof(cbd_t)); + enet_info[i].rxbuf = + (char *) memalign(CONFIG_SYS_CACHELINE_SIZE, + RX_BUF_CNT * DBUF_LENGTH); + enet_info[i].txbd = + (cbd_t *) memalign(CONFIG_SYS_CACHELINE_SIZE, + TX_BUF_CNT * sizeof(cbd_t)); + enet_info[i].txbuf = + (char *) memalign(CONFIG_SYS_CACHELINE_SIZE, + TX_BUF_CNT * DBUF_LENGTH); + enet_info[i].phy_name = + (char *)memalign(CONFIG_SYS_CACHELINE_SIZE, 32); +#ifdef MXC_ENET_DEBUG + printf("%s: rxbd %x txbd %x ->%x\n", dev->name, + (int)enet_info[i].rxbd, (int)enet_info[i].txbd, + (int)enet_info[i].txbuf); +#endif + } + + return 1; +} + +#endif /* defined(CONFIG_CMD_NET) && defined(CONFIG_NET_MULTI) */ |