From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from correo.ziv.es (eu31-237.clientes.euskaltel.es [212.55.31.237]) by ozlabs.org (Postfix) with ESMTP id 87AC7679FA for ; Mon, 13 Feb 2006 19:08:07 +1100 (EST) Subject: Re: Stability of MPC5200 FEC From: "Asier Llano Palacios" To: "Sylvain Munaut" In-Reply-To: <43EE029A.5040504@246tNt.com> References: <1139592010.14894.22.camel@localhost.localdomain> <43EE029A.5040504@246tNt.com> Content-Type: multipart/mixed; boundary="=-brzpes5YUe/mdFzWSclR" Date: Mon, 13 Feb 2006 09:08:01 +0100 Message-ID: <1139818081.14894.45.camel@localhost.localdomain> MIME-Version: 1.0 Cc: John Rigby , linuxppc-embedded@ozlabs.org List-Id: Linux on Embedded PowerPC Developers Mail List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , This is a multi-part message in MIME format. --=-brzpes5YUe/mdFzWSclR Content-Type: text/plain; charset="ISO-8859-15" Content-Transfer-Encoding: quoted-printable El s=E1b, 11-02-2006 a las 16:28 +0100, Sylvain Munaut escribi=F3: > Asier Llano Palacios wrote: > Did you ever observe that behavior on a lite5200 ? > What is 'long uptime' and do you have sustained transfer during that > period ? (To try reproduice the problem). Weeks uptime. We don't work with the lite5200 for so long time, but our board is based on a lite5200, and the FEC configuration is exactly equal. > Also, what are the 'symptoms' ? (anything in dmesg ?) FEC doesn't work at all, everything else works. If using a console I execute 'ifconfig eth0 down && ifconfig eth0 up ' the fec starts working again. I haven't reproduced it directly, but a partner and a customer. I'll try to reproduce this behaviour myself and bring back more information. > I haven't touched the FEC code much except to remove the > aligment code (newer bestcomm microcode doesn't require alignement of > the skb).=20 >=20 > You can send me the driver/net/fec_mpc52xx and = arch/ppc/syslib/bestcomm > you're using, I'll tell you if there was major changes. I've sent to you the patches that I extracted from your kernel source tree some time ago. I have all the patches until the 1.2161 one. But I've only sent to you the ones related to the FEC and the bestcomm. --=20 Asier Llano Palacios ZIV I+D Smart Energy Networks Dpto. Ingenier=EDa de Desarrollo. ZIV, Aplicaciones y Tecnolog=EDa. Parque Tecnol=F3gico, 210 48170 ZAMUDIO (Bizkaia) E-mail: a.llano@ziv.es Tlfno: 944037400=20 =20 ----------------------------------------- PLEASE NOTE = ------------------------------------------- This message, along with any attachments, may be confidential or legally = privileged.=20 It is intended only for the named person(s), who is/are the only = authorized recipients. If this message has reached you in error, kindly destroy it without = review and notify the sender immediately. Thank you for your help. =B5SysCom uses virus scanning software but excludes any liability for = viruses contained in any attachment. =20 ------------------------------------ ROGAMOS LEA ESTE TEXTO = ------------------------------- Este mensaje y sus anexos pueden contener informaci=F3n confidencial y/o = con derecho legal.=20 Est=E1 dirigido =FAnicamente a la/s persona/s o entidad/es rese=F1adas = como =FAnico destinatario autorizado. Si este mensaje le hubiera llegado por error, por favor elim=EDnelo sin = revisarlo ni reenviarlo y notif=EDquelo inmediatamente al remitente. = Gracias por su colaboraci=F3n. =20 =B5SysCom utiliza software antivirus, pero no se hace responsable de los = virus contenidos en los ficheros anexos. --=-brzpes5YUe/mdFzWSclR Content-Disposition: attachment; filename="1.2156-bestcomm-api-mpc52xx.patch" Content-Type: text/x-patch; charset="ISO-8859-15"; name="1.2156-bestcomm-api-mpc52xx.patch" Content-Transfer-Encoding: 7bit diff -Nru a/arch/ppc/Kconfig b/arch/ppc/Kconfig --- a/arch/ppc/Kconfig 2004-11-19 12:41:34 +01:00 +++ b/arch/ppc/Kconfig 2004-11-20 23:21:00 +01:00 @@ -623,6 +623,10 @@ config PPC_MPC52xx bool +config PPC_BESTCOMM + bool + depends on PPC_MPC52xx + config 8260 bool "CPM2 Support" if WILLOW depends on 6xx diff -Nru a/arch/ppc/syslib/Makefile b/arch/ppc/syslib/Makefile --- a/arch/ppc/syslib/Makefile 2004-11-19 12:41:34 +01:00 +++ b/arch/ppc/syslib/Makefile 2004-11-20 23:21:00 +01:00 @@ -88,3 +88,4 @@ ifeq ($(CONFIG_PPC_MPC52xx),y) obj-$(CONFIG_PCI) += mpc52xx_pci.o endif +obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/ diff -Nru a/arch/ppc/syslib/bestcomm/Makefile b/arch/ppc/syslib/bestcomm/Makefile --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/arch/ppc/syslib/bestcomm/Makefile 2004-11-20 23:21:00 +01:00 @@ -0,0 +1,2 @@ +obj-$(CONFIG_PPC_BESTCOMM) += bestcomm.o +obj-$(CONFIG_FEC_MPC52xx) += sdma_fec_rx_task.o sdma_fec_tx_task.o fec.o diff -Nru a/arch/ppc/syslib/bestcomm/bestcomm.c b/arch/ppc/syslib/bestcomm/bestcomm.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/arch/ppc/syslib/bestcomm/bestcomm.c 2004-11-20 23:21:01 +01:00 @@ -0,0 +1,248 @@ +/* + * arch/ppc/syslib/bestcomm/bestcomm.c + * + * Driver for MPC52xx processor BestComm peripheral controller + * + * Author: Dale Farnsworth + * + * 2003-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "bestcomm.h" + +static spinlock_t sdma_lock = SPIN_LOCK_UNLOCKED; + +/* + * Use a very simple SRAM allocator. + * There is no mechanism for freeing space. + * In an attempt to minimize internal fragmentation, the SRAM is + * divided into two areas. + * + * Area 1 is at the beginning of SRAM + * and is used for allocations requiring alignments of 16 bytes or less. + * Successive allocations return higher addresses. + * + * Area 2 is at the end of SRAM and is used for the remaining allocations. + * Successive allocations return lower addresses. + * + * I've considered adding routines to support the freeing of SRAM allocations, + * but the SRAM is so small (16K) that fragmentation can quickly cause the + * SRAM to be unusable. If you can come up with a slick way to free SRAM + * memory without the fragmentation problem, please do so. + */ + +struct sdma_tdt *sdma_tdt; + +static u8 *area1_end; +static u8 *area2_begin; + +static void sdma_init(void); + +void *sdma_sram_alloc(int size, int alignment) +{ + u8 *a; + + spin_lock(&sdma_lock); + if (!area1_end) + sdma_init(); + + /* alignment must be a power of 2 */ + BUG_ON(alignment & (alignment - 1)); + + if (alignment < 16) { + a = (u8 *)(((u32)area1_end + (alignment-1)) & ~(alignment-1)); + if (a + size <= area2_begin) + area1_end = a + size; + else + a = 0; /* out of memory */ + } else { + a = (u8 *)(((u32)area2_begin - size) & ~(alignment - 1)); + if (a >= area1_end) + area2_begin = a; + else + a = 0; /* out of memory */ + } + spin_unlock(&sdma_lock); + return (void *)a; +} + +/* this will need to be updated if Freescale changes their task code FDT */ +static u32 fdt_ops[] = { + 0xa0045670, /* FDT[48] */ + 0x80045670, /* FDT[49] */ + 0x21800000, /* FDT[50] */ + 0x21e00000, /* FDT[51] */ + 0x21500000, /* FDT[52] */ + 0x21400000, /* FDT[53] */ + 0x21500000, /* FDT[54] */ + 0x20400000, /* FDT[55] */ + 0x20500000, /* FDT[56] */ + 0x20800000, /* FDT[57] */ + 0x20a00000, /* FDT[58] */ + 0xc0170000, /* FDT[59] */ + 0xc0145670, /* FDT[60] */ + 0xc0345670, /* FDT[61] */ + 0xa0076540, /* FDT[62] */ + 0xa0000760, /* FDT[63] */ +}; + +static void sdma_init(void) +{ + struct mpc52xx_sdma *sdma = (struct mpc52xx_sdma *)MPC52xx_SDMA; + int task; + u32 *context; + u32 *var; + u32 *fdt; + struct sdma_tdt *tdt; + + area1_end = (u8 *)MPC52xx_SRAM; + area2_begin = (u8 *)(MPC52xx_SRAM + MPC52xx_SRAM_SIZE); + + memset((void *)MPC52xx_SRAM, 0, MPC52xx_SRAM_SIZE); + + /* allocate space for task descriptors, contexts, and var tables */ + sdma_tdt = sdma_sram_alloc(sizeof(*sdma_tdt) * SDMA_MAX_TASKS, 4); + context = sdma_sram_alloc(SDMA_CONTEXT_SIZE * SDMA_MAX_TASKS, + SDMA_CONTEXT_ALIGN); + var = sdma_sram_alloc(SDMA_VAR_SIZE * SDMA_MAX_TASKS, SDMA_VAR_ALIGN); + fdt = sdma_sram_alloc(SDMA_FDT_SIZE, SDMA_FDT_ALIGN); + memcpy(&fdt[48], fdt_ops, sizeof(fdt_ops)); + + out_be32(&sdma->taskBar, (u32)sdma_tdt); + + tdt = sdma_tdt; + for (task=0; task < SDMA_MAX_TASKS; task++) { + out_be16(&sdma->tcr[task], 0); + out_8(&sdma->ipr[task], 0); + + tdt->context = context; + tdt->var = var; + tdt->fdt = fdt; + var += SDMA_MAX_VAR + SDMA_MAX_INC; + context += SDMA_MAX_CONTEXT; + tdt++; + } + + out_8(&sdma->ipr[SDMA_INITIATOR_ALWAYS], SDMA_IPR_ALWAYS); + + /* Disable COMM Bus Prefetch, apparently it's not reliable yet */ + out_be16(&sdma->PtdCntrl, in_be16(&sdma->PtdCntrl) | 1); +} + +static int new_task_number(void) +{ + struct sdma_tdt *tdt; + int i; + + spin_lock(&sdma_lock); + + if (!sdma_tdt) + sdma_init(); + tdt = sdma_tdt; + + for (i=0; istart == 0) + break; + if (i == SDMA_MAX_TASKS) + i = -1; + + spin_unlock(&sdma_lock); + + return i; +} + +int sdma_load_task(u32 *task_image) +{ + struct sdma_task_header *head = (struct sdma_task_header *)task_image; + struct sdma_tdt *tdt; + int tasknum; + u32 *desc; + u32 *var; + u32 *inc; + + BUG_ON(head->magic != SDMA_TASK_MAGIC); + + tasknum = new_task_number(); + if (tasknum < 0) + return -ENOMEM; + + desc = (u32 *)(head + 1); + var = desc + head->desc_size; + inc = var + head->var_size; + + tdt = &sdma_tdt[tasknum]; + tdt->start = sdma_sram_alloc(head->desc_size * 4, 4); + if (!tdt->start) + return -ENOMEM; + tdt->stop = tdt->start + head->desc_size - 1; + + memcpy(tdt->start, desc, head->desc_size * sizeof(u32)); + memcpy(tdt->var + head->first_var, var, head->var_size * sizeof(u32)); + memcpy(tdt->var + SDMA_MAX_VAR, inc, head->inc_size * sizeof(u32)); + return tasknum; +} + +void sdma_set_initiator(int task, int initiator) +{ + int i; + int num_descs; + u32 *desc; + int next_drd_has_initiator; + + sdma_set_tcr_initiator(task, initiator); + + desc = sdma_task_desc(task); + next_drd_has_initiator = 1; + num_descs = sdma_task_num_descs(task); + + for (i=0; icookie = cookie; + } + + s->num_bd = queue_size; + return s; +} + +void sdma_free(struct sdma *s) +{ + if (s->cookie) + kfree(s->cookie); + kfree(s); +} diff -Nru a/arch/ppc/syslib/bestcomm/bestcomm.h b/arch/ppc/syslib/bestcomm/bestcomm.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/arch/ppc/syslib/bestcomm/bestcomm.h 2004-11-20 23:21:02 +01:00 @@ -0,0 +1,441 @@ +/* + * arch/ppc/syslib/bestcomm/bestcomm.h + * + * Driver for MPC52xx processor BestComm peripheral controller + * + * Author: Dale Farnsworth + * + * 2003-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifndef __BESTCOMM_BESTCOMM_H__ +#define __BESTCOMM_BESTCOMM_H__ + + +#undef SDMA_DEBUG + +/* Buffer Descriptor definitions */ +struct sdma_bd { + u32 status; + void *data; +}; + +struct sdma_bd2 { + u32 status; + void *data1; + void *data2; +}; + +#define SDMA_LEN_BITS 26 +#define SDMA_LEN_MASK ((1 << SDMA_LEN_BITS) - 1) + +#define SDMA_BD_READY 0x40000000UL + +#define SDMA_FEC_TX_BD_TFD 0x08000000UL /* transmit frame done */ +#define SDMA_FEC_TX_BD_INT 0x04000000UL /* Interrupt */ +#define SDMA_FEC_TX_BD_TFD_INIT (SDMA_BD_READY | SDMA_FEC_TX_BD_TFD | \ + SDMA_FEC_TX_BD_INT) + +struct sdma { + union { + struct sdma_bd *bd; + struct sdma_bd2 *bd2; + }; + void **cookie; + u16 index; + u16 outdex; + u16 num_bd; + s16 tasknum; + u32 flags; +}; + +#define SDMA_FLAGS_NONE 0x0000 +#define SDMA_FLAGS_ENABLE_TASK 0x0001 +#define SDMA_FLAGS_BD2 0x0002 + +/* Task Descriptor Table Entry */ +struct sdma_tdt { + u32 *start; + u32 *stop; + u32 *var; + u32 *fdt; + u32 status; + u32 mvtp; + u32 *context; + u32 litbase; +}; + +extern struct sdma_tdt *sdma_tdt; + +#define SDMA_MAX_TASKS 16 +#define SDMA_MAX_VAR 24 +#define SDMA_MAX_INC 8 +#define SDMA_MAX_FDT 64 +#define SDMA_MAX_CONTEXT 20 +#define SDMA_CONTEXT_SIZE SDMA_MAX_CONTEXT * sizeof(u32) +#define SDMA_CONTEXT_ALIGN 0x100 +#define SDMA_VAR_SIZE SDMA_MAX_VAR * sizeof(u32) +#define SDMA_VAR_ALIGN 0x80 +#define SDMA_FDT_SIZE SDMA_MAX_FDT * sizeof(u32) +#define SDMA_FDT_ALIGN 0x100 +#define SDMA_BD_ALIGN 0x10 + +#define TASK_ENABLE 0x8000 + +static inline void sdma_enable_task(int task) +{ + struct mpc52xx_sdma *sdma = (struct mpc52xx_sdma *)MPC52xx_SDMA; + + out_be16(&sdma->tcr[task], in_be16(&sdma->tcr[task]) | TASK_ENABLE); +} + +static inline void sdma_disable_task(int task) +{ + struct mpc52xx_sdma *sdma = (struct mpc52xx_sdma *)MPC52xx_SDMA; + + out_be16(&sdma->tcr[task], in_be16(&sdma->tcr[task]) & ~TASK_ENABLE); +} + +static inline int sdma_irq(struct sdma *s) +{ + return MPC52xx_SDMA_IRQ_BASE + s->tasknum; +} + +static inline void sdma_enable(struct sdma *s) +{ + sdma_enable_task(s->tasknum); +} + +static inline void sdma_disable(struct sdma *s) +{ + sdma_disable_task(s->tasknum); +} + +static inline int sdma_queue_empty(struct sdma *s) +{ + return s->index == s->outdex; +} + +static inline void sdma_clear_irq(struct sdma *s) +{ + struct mpc52xx_sdma *sdma = (struct mpc52xx_sdma *)MPC52xx_SDMA; + + out_be32(&sdma->IntPend, 1 << s->tasknum); +} + +static inline int sdma_next_index(struct sdma *s) +{ + return ((s->index + 1) == s->num_bd) ? 0 : s->index + 1; +} + +static inline int sdma_next_outdex(struct sdma *s) +{ + return ((s->outdex + 1) == s->num_bd) ? 0 : s->outdex + 1; +} + +static inline int sdma_queue_full(struct sdma *s) +{ + return s->outdex == sdma_next_index(s); +} + +static inline int sdma_buffer_done(struct sdma *s) +{ +#ifdef SDMA_DEBUG + BUG_ON(s->flags & SDMA_FLAGS_BD2); +#endif + if (sdma_queue_empty(s)) + return 0; + + return (s->bd[s->outdex].status & SDMA_BD_READY) == 0; +} + +static inline int sdma_buffer2_done(struct sdma *s) +{ +#ifdef SDMA_DEBUG + BUG_ON(!(s->flags & SDMA_FLAGS_BD2)); +#endif + if (sdma_queue_empty(s)) + return 0; + return (s->bd2[s->outdex].status & SDMA_BD_READY) == 0; +} + +static inline u32 *sdma_task_desc(int task) +{ + return sdma_tdt[task].start; +} + +static inline int sdma_task_num_descs(int task) +{ + return sdma_tdt[task].stop - sdma_tdt[task].start + 1; +} + +static inline u32 *sdma_task_var(int task) +{ + return sdma_tdt[task].var; +} + +static inline u32 *sdma_task_inc(int task) +{ + return sdma_task_var(task) + SDMA_MAX_VAR; +} + +static inline void sdma_set_tcr_initiator(int task, int initiator) { + struct mpc52xx_sdma *sdma = (struct mpc52xx_sdma *)MPC52xx_SDMA; + + u16 *tcr = &sdma->tcr[task]; + + out_be16(tcr, (in_be16(tcr) & ~0x1f00) | (initiator << 8)); +} + +#define SDMA_DRD_INITIATOR_SHIFT 21 + +static inline int sdma_desc_initiator(u32 desc) +{ + return (desc >> SDMA_DRD_INITIATOR_SHIFT) & 0x1f; +} + +static inline void sdma_set_desc_initiator(u32 *desc, int initiator) +{ + *desc = (*desc & ~(0x1f << SDMA_DRD_INITIATOR_SHIFT)) | + ((initiator << SDMA_DRD_INITIATOR_SHIFT) & 0x1f); +} + +static inline void sdma_submit_buffer(struct sdma *s, void *cookie, void *data, + int length) +{ +#ifdef SDMA_DEBUG + BUG_ON(s->flags & SDMA_FLAGS_BD2); +#endif + s->cookie[s->index] = cookie; + s->bd[s->index].data = data; + s->bd[s->index].status = SDMA_BD_READY | length; + s->index = sdma_next_index(s); + if (s->flags & SDMA_FLAGS_ENABLE_TASK) + sdma_enable_task(s->tasknum); +} + +/* + * Special submit_buffer function to submit last buffer of a frame to + * the FEC tx task. tfd means "transmit frame done". + */ +static inline void sdma_fec_tfd_submit_buffer(struct sdma *s, void *cookie, + void *data, int length) +{ +#ifdef SDMA_DEBUG + BUG_ON(s->flags & SDMA_FLAGS_BD2); +#endif + s->cookie[s->index] = cookie; + s->bd[s->index].data = data; + s->bd[s->index].status = SDMA_FEC_TX_BD_TFD_INIT | length; + s->index = sdma_next_index(s); + sdma_enable_task(s->tasknum); +} + +static inline void *sdma_retrieve_buffer(struct sdma *s, int *length) +{ + void *cookie = s->cookie[s->outdex]; + +#ifdef SDMA_DEBUG + BUG_ON(s->flags & SDMA_FLAGS_BD2); +#endif + if (length) + *length = s->bd[s->outdex].status & SDMA_LEN_MASK; + s->outdex = sdma_next_outdex(s); + return cookie; +} + +static inline void sdma_submit_buffer2(struct sdma *s, void *cookie, + void *data1, void *data2, int length) +{ +#ifdef SDMA_DEBUG + BUG_ON(!(s->flags & SDMA_FLAGS_BD2)); +#endif + s->cookie[s->index] = cookie; + s->bd2[s->index].data1 = data1; + s->bd2[s->index].data2 = data2; + s->bd2[s->index].status = SDMA_BD_READY | length; + s->index = sdma_next_index(s); + if (s->flags & SDMA_FLAGS_ENABLE_TASK) + sdma_enable_task(s->tasknum); +} + +static inline void *sdma_retrieve_buffer2(struct sdma *s, int *length) +{ + void *cookie = s->cookie[s->outdex]; + +#ifdef SDMA_DEBUG + BUG_ON(!(s->flags & SDMA_FLAGS_BD2)); +#endif + if (length) + *length = s->bd2[s->outdex].status & SDMA_LEN_MASK; + s->outdex = sdma_next_outdex(s); + return cookie; +} + +#define SDMA_TASK_MAGIC 0x4243544B /* 'BCTK' */ + +/* the size fields are given in number of 32-bit words */ +struct sdma_task_header { + u32 magic; + u8 desc_size; + u8 var_size; + u8 inc_size; + u8 first_var; + u8 reserved[8]; +}; + +#define SDMA_DESC_NOP 0x000001f8 +#define SDMA_LCD_MASK 0x80000000 +#define SDMA_DRD_EXTENDED 0x40000000 + +#define sdma_drd_is_extended(desc) ((desc) & SDMA_DRD_EXTENDED) + +static inline int sdma_desc_is_drd(u32 desc) { + return !(desc & SDMA_LCD_MASK) && desc != SDMA_DESC_NOP; +}; + +#define SDMA_PRAGMA_BIT_RSV 7 /* reserved pragma bit */ +#define SDMA_PRAGMA_BIT_PRECISE_INC 6 /* increment 0=when possible, */ + /* 1=iter end */ +#define SDMA_PRAGMA_BIT_RST_ERROR_NO 5 /* don't reset errors on */ + /* task enable */ +#define SDMA_PRAGMA_BIT_PACK 4 /* pack data enable */ +#define SDMA_PRAGMA_BIT_INTEGER 3 /* data alignment */ + /* 0=frac(msb), 1=int(lsb) */ +#define SDMA_PRAGMA_BIT_SPECREAD 2 /* XLB speculative read */ +#define SDMA_PRAGMA_BIT_CW 1 /* write line buffer enable */ +#define SDMA_PRAGMA_BIT_RL 0 /* read line buffer enable */ + +#define SDMA_STD_PRAGMA ((0 << SDMA_PRAGMA_BIT_RSV) | \ + (0 << SDMA_PRAGMA_BIT_PRECISE_INC) | \ + (0 << SDMA_PRAGMA_BIT_RST_ERROR_NO) | \ + (0 << SDMA_PRAGMA_BIT_PACK) | \ + (0 << SDMA_PRAGMA_BIT_INTEGER) | \ + (1 << SDMA_PRAGMA_BIT_SPECREAD) | \ + (1 << SDMA_PRAGMA_BIT_CW) | \ + (1 << SDMA_PRAGMA_BIT_RL)) + +#define SDMA_PCI_PRAGMA ((0 << SDMA_PRAGMA_BIT_RSV) | \ + (0 << SDMA_PRAGMA_BIT_PRECISE_INC) | \ + (0 << SDMA_PRAGMA_BIT_RST_ERROR_NO) | \ + (0 << SDMA_PRAGMA_BIT_PACK) | \ + (1 << SDMA_PRAGMA_BIT_INTEGER) | \ + (1 << SDMA_PRAGMA_BIT_SPECREAD) | \ + (1 << SDMA_PRAGMA_BIT_CW) | \ + (1 << SDMA_PRAGMA_BIT_RL)) + +#define SDMA_ATA_PRAGMA SDMA_STD_PRAGMA +#define SDMA_CRC16_DP_0_PRAGMA SDMA_STD_PRAGMA +#define SDMA_CRC16_DP_1_PRAGMA SDMA_STD_PRAGMA +#define SDMA_FEC_RX_BD_PRAGMA SDMA_STD_PRAGMA +#define SDMA_FEC_TX_BD_PRAGMA SDMA_STD_PRAGMA +#define SDMA_GEN_DP_0_PRAGMA SDMA_STD_PRAGMA +#define SDMA_GEN_DP_1_PRAGMA SDMA_STD_PRAGMA +#define SDMA_GEN_DP_2_PRAGMA SDMA_STD_PRAGMA +#define SDMA_GEN_DP_3_PRAGMA SDMA_STD_PRAGMA +#define SDMA_GEN_DP_BD_0_PRAGMA SDMA_STD_PRAGMA +#define SDMA_GEN_DP_BD_1_PRAGMA SDMA_STD_PRAGMA +#define SDMA_GEN_RX_BD_PRAGMA SDMA_STD_PRAGMA +#define SDMA_GEN_TX_BD_PRAGMA SDMA_STD_PRAGMA +#define SDMA_GEN_LPC_PRAGMA SDMA_STD_PRAGMA +#define SDMA_PCI_RX_PRAGMA SDMA_PCI_PRAGMA +#define SDMA_PCI_TX_PRAGMA SDMA_PCI_PRAGMA + +static inline void sdma_set_task_pragma(int task, int pragma) +{ + u32 *fdt = (u32 *)&sdma_tdt[task].fdt; + + *fdt = (*fdt & ~0xff) | pragma; +} + +static inline void sdma_set_task_auto_start(int task, int next_task) +{ + struct mpc52xx_sdma *sdma = (struct mpc52xx_sdma *)MPC52xx_SDMA; + u16 *tcr = &sdma->tcr[task]; + + out_be16(tcr, (in_be16(tcr) & ~0xff) | 0x00c0 | next_task); +} + +#define SDMA_INITIATOR_ALWAYS 0 +#define SDMA_INITIATOR_SCTMR_0 1 +#define SDMA_INITIATOR_SCTMR_1 2 +#define SDMA_INITIATOR_FEC_RX 3 +#define SDMA_INITIATOR_FEC_TX 4 +#define SDMA_INITIATOR_ATA_RX 5 +#define SDMA_INITIATOR_ATA_TX 6 +#define SDMA_INITIATOR_SCPCI_RX 7 +#define SDMA_INITIATOR_SCPCI_TX 8 +#define SDMA_INITIATOR_PSC3_RX 9 +#define SDMA_INITIATOR_PSC3_TX 10 +#define SDMA_INITIATOR_PSC2_RX 11 +#define SDMA_INITIATOR_PSC2_TX 12 +#define SDMA_INITIATOR_PSC1_RX 13 +#define SDMA_INITIATOR_PSC1_TX 14 +#define SDMA_INITIATOR_SCTMR_2 15 +#define SDMA_INITIATOR_SCLPC 16 +#define SDMA_INITIATOR_PSC5_RX 17 +#define SDMA_INITIATOR_PSC5_TX 18 +#define SDMA_INITIATOR_PSC4_RX 19 +#define SDMA_INITIATOR_PSC4_TX 20 +#define SDMA_INITIATOR_I2C2_RX 21 +#define SDMA_INITIATOR_I2C2_TX 22 +#define SDMA_INITIATOR_I2C1_RX 23 +#define SDMA_INITIATOR_I2C1_TX 24 +#define SDMA_INITIATOR_PSC6_RX 25 +#define SDMA_INITIATOR_PSC6_TX 26 +#define SDMA_INITIATOR_IRDA_RX 25 +#define SDMA_INITIATOR_IRDA_TX 26 +#define SDMA_INITIATOR_SCTMR_3 27 +#define SDMA_INITIATOR_SCTMR_4 28 +#define SDMA_INITIATOR_SCTMR_5 29 +#define SDMA_INITIATOR_SCTMR_6 30 +#define SDMA_INITIATOR_SCTMR_7 31 + +#define SDMA_IPR_ALWAYS 7 +#define SDMA_IPR_SCTMR_0 2 +#define SDMA_IPR_SCTMR_1 2 +#define SDMA_IPR_FEC_RX 6 +#define SDMA_IPR_FEC_TX 5 +#define SDMA_IPR_ATA_RX 4 +#define SDMA_IPR_ATA_TX 3 +#define SDMA_IPR_SCPCI_RX 2 +#define SDMA_IPR_SCPCI_TX 2 +#define SDMA_IPR_PSC3_RX 2 +#define SDMA_IPR_PSC3_TX 2 +#define SDMA_IPR_PSC2_RX 2 +#define SDMA_IPR_PSC2_TX 2 +#define SDMA_IPR_PSC1_RX 2 +#define SDMA_IPR_PSC1_TX 2 +#define SDMA_IPR_SCTMR_2 2 +#define SDMA_IPR_SCLPC 2 +#define SDMA_IPR_PSC5_RX 2 +#define SDMA_IPR_PSC5_TX 2 +#define SDMA_IPR_PSC4_RX 2 +#define SDMA_IPR_PSC4_TX 2 +#define SDMA_IPR_I2C2_RX 2 +#define SDMA_IPR_I2C2_TX 2 +#define SDMA_IPR_I2C1_RX 2 +#define SDMA_IPR_I2C1_TX 2 +#define SDMA_IPR_PSC6_RX 2 +#define SDMA_IPR_PSC6_TX 2 +#define SDMA_IPR_IRDA_RX 2 +#define SDMA_IPR_IRDA_TX 2 +#define SDMA_IPR_SCTMR_3 2 +#define SDMA_IPR_SCTMR_4 2 +#define SDMA_IPR_SCTMR_5 2 +#define SDMA_IPR_SCTMR_6 2 +#define SDMA_IPR_SCTMR_7 2 + +extern struct sdma *sdma_alloc(int request_queue_size); +extern void sdma_free(struct sdma *sdma_struct); +extern int sdma_load_task(u32 *task_image); +extern void *sdma_sram_alloc(int size, int alignment); +extern void sdma_init_bd(struct sdma *s); +extern void sdma_init_bd2(struct sdma *s); + +#define FIELD_OFFSET(s,f) ((unsigned long)(&(((struct s*)0)->f))) + +#endif /* __BESTCOMM_BESTCOMM_H__ */ diff -Nru a/arch/ppc/syslib/bestcomm/fec.c b/arch/ppc/syslib/bestcomm/fec.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/arch/ppc/syslib/bestcomm/fec.c 2004-11-20 23:21:03 +01:00 @@ -0,0 +1,161 @@ +/* + * arch/ppc/syslib/bestcomm/fec.c + * + * Driver for MPC52xx processor BestComm FEC controller + * + * Author: Dale Farnsworth + * + * 2003-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include + +#include + +#include "bestcomm.h" +#include "fec.h" + +/* + * Initialize FEC receive task. + * Returns task number of FEC receive task. + * Returns -1 on failure + */ +int sdma_fec_rx_init(struct sdma *s, phys_addr_t fifo, int maxbufsize) +{ + struct mpc52xx_sdma *sdma = (struct mpc52xx_sdma *)MPC52xx_SDMA; + struct sdma_fec_rx_var *var; + struct sdma_fec_rx_inc *inc; + static int tasknum = -1; + static struct sdma_bd *bd = 0; + + if (tasknum < 0) { + tasknum = sdma_load_task(sdma_fec_rx_task); + if (tasknum < 0) + return tasknum; + } + + if (!bd) + bd = (struct sdma_bd *)sdma_sram_alloc(sizeof(*bd) * s->num_bd, + SDMA_BD_ALIGN); + if (!bd) + return -ENOMEM; + + sdma_disable_task(tasknum); + + s->tasknum = tasknum; + s->bd = bd; + s->flags = SDMA_FLAGS_NONE; + s->index = 0; + s->outdex = 0; + memset(bd, 0, sizeof(*bd) * s->num_bd); + + var = (struct sdma_fec_rx_var *)sdma_task_var(tasknum); + var->enable = MPC52xx_SDMA + + FIELD_OFFSET(mpc52xx_sdma,tcr[tasknum]); + var->fifo = fifo; + var->bd_base = bd; + var->bd_last = &bd[s->num_bd - 1]; + var->bd_start = bd; + var->buffer_size = maxbufsize; + + /* These are constants, they should have been in the image file */ + inc = (struct sdma_fec_rx_inc *)sdma_task_inc(tasknum); + inc->incr_bytes = -(s16)sizeof(u32); + inc->incr_dst = sizeof(u32); + + sdma_set_task_pragma(tasknum, SDMA_FEC_RX_BD_PRAGMA); + sdma_set_task_auto_start(tasknum, tasknum); + + /* clear pending interrupt bits */ + out_be32(&sdma->IntPend, 1<ipr[SDMA_INITIATOR_FEC_RX], SDMA_IPR_FEC_RX); + + return tasknum; +} + +/* + * Return 2nd to last DRD + * This is an ugly hack, but at least it's only done once at initialization + */ +static u32 *self_modified_drd(int tasknum) +{ + u32 *desc; + int num_descs; + int drd_count; + int i; + + num_descs = sdma_task_num_descs(tasknum); + desc = sdma_task_desc(tasknum) + num_descs - 1; + drd_count = 0; + for (i=0; inum_bd, + SDMA_BD_ALIGN); + if (!bd) + return -ENOMEM; + + sdma_disable_task(tasknum); + + s->tasknum = tasknum; + s->bd = bd; + s->flags = SDMA_FLAGS_ENABLE_TASK; + s->index = 0; + s->outdex = 0; + memset(bd, 0, sizeof(*bd) * s->num_bd); + + var = (struct sdma_fec_tx_var *)sdma_task_var(tasknum); + var->DRD = self_modified_drd(tasknum); + var->fifo = fifo; + var->enable = MPC52xx_SDMA + + FIELD_OFFSET(mpc52xx_sdma,tcr[tasknum]); + var->bd_base = bd; + var->bd_last = &bd[s->num_bd - 1]; + var->bd_start = bd; + + /* These are constants, they should have been in the image file */ + inc = (struct sdma_fec_tx_inc *)sdma_task_inc(tasknum); + inc->incr_bytes = -(s16)sizeof(u32); + inc->incr_src = sizeof(u32); + inc->incr_src_ma = sizeof(u8); + + sdma_set_task_pragma(tasknum, SDMA_FEC_TX_BD_PRAGMA); + sdma_set_task_auto_start(tasknum, tasknum); + + /* clear pending interrupt bits */ + out_be32(&sdma->IntPend, 1<ipr[SDMA_INITIATOR_FEC_TX], SDMA_IPR_FEC_RX); + + return tasknum; +} diff -Nru a/arch/ppc/syslib/bestcomm/fec.h b/arch/ppc/syslib/bestcomm/fec.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/arch/ppc/syslib/bestcomm/fec.h 2004-11-20 23:21:04 +01:00 @@ -0,0 +1,64 @@ +/* + * arch/ppc/syslib/bestcomm/fec.h + * + * Driver for MPC52xx processor BestComm FEC controller + * + * Author: Dale Farnsworth + * + * 2003-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifndef __BESTCOMM_FEC_H__ +#define __BESTCOMM_FEC_H__ + + +/* rx task vars that need to be set before enabling the task */ +struct sdma_fec_rx_var { + u32 enable; /* (u16*) address of task's control register */ + u32 fifo; /* (u32*) address of fec's fifo */ + u32 bd_base; /* (struct sdma_bd*) beginning of ring buffer */ + u32 bd_last; /* (struct sdma_bd*) end of ring buffer */ + u32 bd_start; /* (struct sdma_bd*) current bd */ + u32 buffer_size; /* size of receive buffer */ +}; + +/* rx task incs that need to be set before enabling the task */ +struct sdma_fec_rx_inc { + u16 pad0; + s16 incr_bytes; + u16 pad1; + s16 incr_dst; +}; + +/* tx task vars that need to be set before enabling the task */ +struct sdma_fec_tx_var { + u32 DRD; /* (u32*) address of self-modified DRD */ + u32 fifo; /* (u32*) address of fec's fifo */ + u32 enable; /* (u16*) address of task's control register */ + u32 bd_base; /* (struct sdma_bd*) beginning of ring buffer */ + u32 bd_last; /* (struct sdma_bd*) end of ring buffer */ + u32 bd_start; /* (struct sdma_bd*) current bd */ + u32 buffer_size; /* set by uCode for each packet */ +}; + +/* tx task incs that need to be set before enabling the task */ +struct sdma_fec_tx_inc { + u16 pad0; + s16 incr_bytes; + u16 pad1; + s16 incr_src; + u16 pad2; + s16 incr_src_ma; +}; + +extern int sdma_fec_rx_init(struct sdma *s, phys_addr_t fifo, int maxbufsize); +extern int sdma_fec_tx_init(struct sdma *s, phys_addr_t fifo); + +extern u32 sdma_fec_rx_task[]; +extern u32 sdma_fec_tx_task[]; + + +#endif /* __BESTCOMM_FEC_H__ */ diff -Nru a/arch/ppc/syslib/bestcomm/sdma_fec_rx_task.c b/arch/ppc/syslib/bestcomm/sdma_fec_rx_task.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/arch/ppc/syslib/bestcomm/sdma_fec_rx_task.c 2004-11-20 23:21:05 +01:00 @@ -0,0 +1,52 @@ +/* + * sdma_fec_rx_task.c + * + * Automatically created by split_dma_image based on dma_image.hex + * on Wed Sep 8 00:37:22 2004 GMT + */ + +#include + +/* + * The header consists of the following fields: + * uint32_t magic; + * uint8_t desc_size; + * uint8_t var_size; + * uint8_t inc_size; + * uint8_t first_var; + * uint8_t reserved[8]; + * + * The size fields contain the number of 32-bit words. +*/ + +uint32_t sdma_fec_rx_task[] = { + /* header */ + 0x4243544b, + 0x0c020409, + 0x00000000, + 0x00000000, + + /* Task descriptors */ + 0x808220da, /* LCD: idx0 = var1, idx1 = var4; idx1 <= var3; idx0 += inc3, idx1 += inc2 */ + 0x10601010, /* DRD1A: var4 = var2; FN=0 MORE init=3 WS=0 RS=0 */ + 0xb880025b, /* LCD: idx2 = *idx1, idx3 = var0; idx2 < var9; idx2 += inc3, idx3 += inc3 */ + 0x10001308, /* DRD1A: var4 = idx1; FN=0 MORE init=0 WS=0 RS=0 */ + 0x60140002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=2 RS=2 */ + 0x0cccfcca, /* DRD2B1: *idx3 = EU3(); EU3(*idx3,var10) */ + 0xd9190240, /* LCDEXT: idx2 = idx2; idx2 > var9; idx2 += inc0 */ + 0xb8c5e009, /* LCD: idx3 = *(idx1 + var00000015); ; idx3 += inc1 */ + 0x047ecf80, /* DRD1A: *idx3 = *idx0; FN=0 INT init=3 WS=3 RS=3 */ + 0x98190024, /* LCD: idx2 = idx0; idx2 once var0; idx2 += inc4 */ + 0x0060c790, /* DRD1A: *idx1 = *idx2; FN=0 init=3 WS=0 RS=0 */ + 0x000001f8, /* NOP */ + + /* VAR[9]-VAR[10] */ + 0x40000000, + 0x7fff7fff, + + /* INC[0]-INC[3] */ + 0x40000000, + 0xe0000000, + 0xa0000008, + 0x20000000, +}; diff -Nru a/arch/ppc/syslib/bestcomm/sdma_fec_tx_task.c b/arch/ppc/syslib/bestcomm/sdma_fec_tx_task.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/arch/ppc/syslib/bestcomm/sdma_fec_tx_task.c 2004-11-20 23:21:06 +01:00 @@ -0,0 +1,74 @@ +/* + * sdma_fec_tx_task.c + * + * Automatically created by split_dma_image based on dma_image.hex + * on Wed Sep 8 00:37:22 2004 GMT + */ + +#include + +/* + * The header consists of the following fields: + * uint32_t magic; + * uint8_t desc_size; + * uint8_t var_size; + * uint8_t inc_size; + * uint8_t first_var; + * uint8_t reserved[8]; + * + * The size fields contain the number of 32-bit words. +*/ + +uint32_t sdma_fec_tx_task[] = { + /* header */ + 0x4243544b, + 0x1b06070c, + 0x00000000, + 0x00000000, + + /* Task descriptors */ + 0x8018001b, /* LCD: idx0 = var0; idx0 <= var0; idx0 += inc3 */ + 0x60000005, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=5 EXT init=0 WS=0 RS=0 */ + 0x01ccfc0c, /* DRD2B1: var7 = EU3(); EU3(*idx0,var12) */ + 0x8082a123, /* LCD: idx0 = var1, idx1 = var5; idx1 <= var4; idx0 += inc4, idx1 += inc3 */ + 0x10801418, /* DRD1A: var5 = var3; FN=0 MORE init=4 WS=0 RS=0 */ + 0xf8810364, /* LCDEXT: idx2 = *idx1, idx3 = var2; idx2 < var13; idx2 += inc4, idx3 += inc4 */ + 0x801a6024, /* LCD: idx4 = var0; ; idx4 += inc4 */ + 0x10001708, /* DRD1A: var5 = idx1; FN=0 MORE init=0 WS=0 RS=0 */ + 0x60140002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=2 RS=2 */ + 0x0cccfcce, /* DRD2B1: *idx3 = EU3(); EU3(*idx3,var14) */ + 0x991a002c, /* LCD: idx2 = idx2, idx3 = idx4; idx2 once var0; idx2 += inc5, idx3 += inc4 */ + 0x60000002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=0 RS=0 */ + 0x088cfc4f, /* DRD2B1: idx2 = EU3(); EU3(*idx1,var15) */ + 0x9919802c, /* LCD: idx2 = idx2, idx3 = idx3; idx2 once var0; idx2 += inc5, idx3 += inc4 */ + 0x70000002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT MORE init=0 WS=0 RS=0 */ + 0x024cfc4c, /* DRD2B1: var9 = EU3(); EU3(*idx1,var12) */ + 0x60000003, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=3 EXT init=0 WS=0 RS=0 */ + 0x0cccf247, /* DRD2B1: *idx3 = EU3(); EU3(var9,var7) */ + 0xd9190440, /* LCDEXT: idx2 = idx2; idx2 > var17; idx2 += inc0 */ + 0xb8c86009, /* LCD: idx3 = *(idx1 + var0000001a); ; idx3 += inc1 */ + 0x009ec398, /* DRD1A: *idx0 = *idx3; FN=0 init=4 WS=3 RS=3 */ + 0x99198372, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var13; idx2 += inc6, idx3 += inc2 */ + 0x088ac398, /* DRD1A: *idx0 = *idx3; FN=0 TFD init=4 WS=1 RS=1 */ + 0x9919002d, /* LCD: idx2 = idx2; idx2 once var0; idx2 += inc5 */ + 0x60000005, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=5 EXT init=0 WS=0 RS=0 */ + 0x0c4cf88d, /* DRD2B1: *idx1 = EU3(); EU3(idx2,var13) */ + 0x000001f8, /* NOP */ + + /* VAR[12]-VAR[17] */ + 0x0c000000, + 0x40000000, + 0x7fff7fff, + 0x43ffffff, + 0x00000000, + 0x40000004, + + /* INC[0]-INC[6] */ + 0x40000000, + 0xe0000000, + 0xe0000000, + 0xa0000008, + 0x20000000, + 0x00000000, + 0x4000ffff, +}; --=-brzpes5YUe/mdFzWSclR Content-Disposition: attachment; filename="1.2157-fec-support-mpc52xx.patch" Content-Type: text/x-patch; charset="ISO-8859-15"; name="1.2157-fec-support-mpc52xx.patch" Content-Transfer-Encoding: 7bit diff -Nru a/arch/ppc/platforms/lite5200.c b/arch/ppc/platforms/lite5200.c --- a/arch/ppc/platforms/lite5200.c 2004-11-19 17:20:08 +01:00 +++ b/arch/ppc/platforms/lite5200.c 2004-11-20 23:26:31 +01:00 @@ -132,6 +132,10 @@ port_config &= ~0x00007000; /* Differential mode - USB1 only */ port_config |= 0x00001000; + /* Ethernet port */ + port_config &= ~0x000f0000; /* 100 Mbits with MDIO lines */ + port_config |= 0x00050000; + /* Commit port config */ out_be32(&gpio->port_config, port_config); diff -Nru a/arch/ppc/platforms/mpc5200.c b/arch/ppc/platforms/mpc5200.c --- a/arch/ppc/platforms/mpc5200.c 2004-11-19 17:20:08 +01:00 +++ b/arch/ppc/platforms/mpc5200.c 2004-11-20 23:26:31 +01:00 @@ -54,6 +54,13 @@ .irq = MPC52xx_USB_IRQ, .pm = OCP_CPM_NA, }, + { + .vendor = OCP_VENDOR_FREESCALE, + .function = OCP_FUNC_FEC_MPC52xx, + .paddr = MPC52xx_FEC, + .irq = MPC52xx_FEC_IRQ, + .pm = OCP_CPM_NA, + }, { /* Terminating entry */ .vendor = OCP_VENDOR_INVALID } diff -Nru a/drivers/net/Kconfig b/drivers/net/Kconfig --- a/drivers/net/Kconfig 2004-11-05 11:37:26 +01:00 +++ b/drivers/net/Kconfig 2004-11-20 23:26:31 +01:00 @@ -1817,6 +1817,7 @@ controller on the Renesas H8/300 processor. source "drivers/net/fec_8xx/Kconfig" +source "drivers/net/fec_mpc52xx/Kconfig" endmenu diff -Nru a/drivers/net/Makefile b/drivers/net/Makefile --- a/drivers/net/Makefile 2004-11-11 09:34:32 +01:00 +++ b/drivers/net/Makefile 2004-11-20 23:26:31 +01:00 @@ -182,6 +182,7 @@ obj-$(CONFIG_S2IO) += s2io.o obj-$(CONFIG_SMC91X) += smc91x.o obj-$(CONFIG_FEC_8XX) += fec_8xx/ +obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx/ obj-$(CONFIG_ARM) += arm/ obj-$(CONFIG_NET_FC) += fc/ diff -Nru a/drivers/net/fec_mpc52xx/Kconfig b/drivers/net/fec_mpc52xx/Kconfig --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/fec_mpc52xx/Kconfig 2004-11-20 23:26:31 +01:00 @@ -0,0 +1,23 @@ +menu "MPC5200 Networking Options" + depends PPC_MPC52xx && NET_ETHERNET + +config FEC_MPC52xx + bool "FEC Ethernet" + depends on NET_ETHERNET + select PPC_BESTCOMM + select CRC32 + ---help--- + This option enables support for the MPC5200's on-chip + Fast Ethernet Controller + +config USE_MDIO + bool " Use external Ethernet MII PHY" + select MII + depends FEC_MPC52xx + ---help--- + The MPC5200's FEC can connect to the Ethernet either with + an external MII PHY chip or 10 Mbps 7-wire interface + (Motorola? industry standard). + If your board uses an external PHY, say y, else n. + +endmenu diff -Nru a/drivers/net/fec_mpc52xx/Makefile b/drivers/net/fec_mpc52xx/Makefile --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/fec_mpc52xx/Makefile 2004-11-20 23:26:32 +01:00 @@ -0,0 +1,2 @@ +obj-$(CONFIG_FEC_MPC52xx) += fec.o +obj-$(CONFIG_USE_MDIO) += fec_phy.o diff -Nru a/drivers/net/fec_mpc52xx/fec.c b/drivers/net/fec_mpc52xx/fec.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/fec_mpc52xx/fec.c 2004-11-20 23:26:33 +01:00 @@ -0,0 +1,791 @@ +/* + * drivers/net/fec_mpc52xx/fec.c + * + * Driver for the MPC5200 Fast Ethernet Controller + * + * Author: Dale Farnsworth + * + * 2003-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "fec_phy.h" +#include "fec.h" + +static irqreturn_t fec_interrupt(int, void *, struct pt_regs *); +static irqreturn_t fec_rx_interrupt(int, void *, struct pt_regs *); +static irqreturn_t fec_tx_interrupt(int, void *, struct pt_regs *); +static struct net_device_stats *fec_get_stats(struct net_device *); +static void fec_set_multicast_list(struct net_device *dev); +static void fec_reinit(struct net_device *dev); + +static u8 mpc52xx_fec_mac_addr[6]; +static u8 null_mac[6]; + +static void fec_tx_timeout(struct net_device *dev) +{ + struct fec_priv *priv = (struct fec_priv *)dev->priv; + + priv->stats.tx_errors++; + + if (!priv->tx_full) + netif_wake_queue(dev); +} + +static void fec_set_paddr(struct net_device *dev, u8 *mac) +{ + struct fec_priv *priv = (struct fec_priv *)dev->priv; + struct mpc52xx_fec *fec = priv->fec; + + out_be32(&fec->paddr1, *(u32*)(&mac[0])); + out_be32(&fec->paddr2, (*(u16*)(&mac[4]) << 16) | 0x8808); +} + +static void fec_get_paddr(struct net_device *dev, u8 *mac) +{ + struct fec_priv *priv = (struct fec_priv *)dev->priv; + struct mpc52xx_fec *fec = priv->fec; + + *(u32*)(&mac[0]) = in_be32(&fec->paddr1); + *(u16*)(&mac[4]) = in_be32(&fec->paddr2) >> 16; +} + +static int fec_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *sock = (struct sockaddr *)addr; + + memcpy(dev->dev_addr, sock->sa_data, dev->addr_len); + + fec_set_paddr(dev, sock->sa_data); + return 0; +} + +/* This function is called to start or restart the FEC during a link + * change. This happens on fifo errors or when switching between half + * and full duplex. + */ +static void fec_restart(struct net_device *dev, int duplex) +{ + struct fec_priv *priv = (struct fec_priv *)dev->priv; + struct mpc52xx_fec *fec = priv->fec; + u32 rcntrl; + u32 tcntrl; + int i; + + out_be32(&fec->rfifo_status, in_be32(&fec->rfifo_status) & 0x700000); + out_be32(&fec->tfifo_status, in_be32(&fec->tfifo_status) & 0x700000); + out_be32(&fec->reset_cntrl, 0x1000000); + + /* Whack a reset. We should wait for this. */ + out_be32(&fec->ecntrl, FEC_ECNTRL_RESET); + for (i = 0; i < FEC_RESET_DELAY; ++i) { + if ((in_be32(&fec->ecntrl) & FEC_ECNTRL_RESET) == 0) + break; + udelay(1); + } + if (i == FEC_RESET_DELAY) + printk (KERN_ERR "FEC Reset timeout!\n"); + + /* Set station address. */ + fec_set_paddr(dev, dev->dev_addr); + + fec_set_multicast_list(dev); + + rcntrl = FEC_RX_BUFFER_SIZE << 16; /* max frame length */ + rcntrl |= FEC_RCNTRL_FCE; + rcntrl |= MII_RCNTL_MODE; + if (duplex) + tcntrl = FEC_TCNTRL_FDEN; /* FD enable */ + else { + rcntrl |= FEC_RCNTRL_DRT; + tcntrl = 0; + } + out_be32(&fec->r_cntrl, rcntrl); + out_be32(&fec->x_cntrl, tcntrl); + + set_phy_speed(fec, priv->phy_speed); + + priv->full_duplex = duplex; + + /* Clear any outstanding interrupt. */ + out_be32(&fec->ievent, 0xffffffff); /* clear intr events */ + + /* Enable interrupts we wish to service. + */ + out_be32(&fec->imask, FEC_IMASK_ENABLE); + + /* And last, enable the transmit and receive processing. + */ + out_be32(&fec->ecntrl, FEC_ECNTRL_ETHER_EN); + out_be32(&fec->r_des_active, 0x01000000); + + /* The tx ring is no longer full. */ + if (priv->tx_full) + { + priv->tx_full = 0; + netif_wake_queue(dev); + } +} + +static void fec_free_rx_buffers(struct sdma *s) +{ + struct sk_buff *skb; + + while (!sdma_queue_empty(s)) { + skb = sdma_retrieve_buffer(s, NULL); + kfree_skb(skb); + } +} + +static int fec_open(struct net_device *dev) +{ + struct fec_priv *priv = (struct fec_priv *)dev->priv; + struct sk_buff *skb; + void *data; + + sdma_fec_rx_init(priv->rx_sdma, priv->rx_fifo, FEC_RX_BUFFER_SIZE); + sdma_fec_tx_init(priv->tx_sdma, priv->tx_fifo); + + while (!sdma_queue_full(priv->rx_sdma)) { + skb = dev_alloc_skb(FEC_RX_BUFFER_SIZE); + if (skb == 0) + goto eagain; + skb->dev = dev; + skb_put(skb, FEC_RX_BUFFER_SIZE); + + /* zero out the initial receive buffers to aid debugging */ + memset(skb->data, 0, FEC_RX_BUFFER_SIZE); + data = (void *)virt_to_phys(skb->data); + sdma_submit_buffer(priv->rx_sdma, skb, data, skb->len); + } + + fec_set_paddr(dev, dev->dev_addr); + + if (fec_mii_wait(dev) != 0) + return -ENODEV; + + sdma_enable(priv->rx_sdma); + sdma_enable(priv->tx_sdma); + + netif_start_queue(dev); + + return 0; + +eagain: + printk(KERN_ERR "fec_open: failed\n"); + + fec_free_rx_buffers(priv->rx_sdma); + + return -EAGAIN; +} + +/* The BestComm hardware requires data to be 32-bit aligned. + * We also pad to minimum ethernet packet length, ETH_ZLEN. + */ +static inline struct sk_buff *fec_skb_align_and_pad(struct sk_buff *skb) +{ + void *data = skb->data; + int len = skb->len; + int pad = (int)data & 0x3; + struct sk_buff *nskb; + int nlen; + + if (pad == 0) + return skb; + + if (!skb_cloned(skb)) { + skb_push(skb, pad); + memmove(skb->data, data, len); + skb_trim(skb, len); + skb = skb_padto(skb, ETH_ZLEN); + return skb; + } + + /* ensure skb_padto doesn't have to reallocate */ + nlen = (len >= ETH_ZLEN) ? len : ETH_ZLEN; + + nskb = alloc_skb(nlen, GFP_ATOMIC); + if (nskb) { + skb_put(nskb, len); + memcpy(nskb->data, data, len); + nskb = skb_padto(nskb, ETH_ZLEN); + } + kfree_skb(skb); + return nskb; +} + +/* This will only be invoked if your driver is _not_ in XOFF state. + * What this means is that you need not check it, and that this + * invariant will hold if you make sure that the netif_*_queue() + * calls are done at the proper times. + */ +static int fec_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct fec_priv *priv = (struct fec_priv *)dev->priv; + void *data; + + if (sdma_queue_full(priv->tx_sdma)) + panic("MPC52xx transmit queue overrun\n"); + + skb = fec_skb_align_and_pad(skb); + if (!skb) { + priv->stats.tx_dropped++; + return 0; + } + + spin_lock_irq(&priv->lock); + dev->trans_start = jiffies; + + data = (void *)virt_to_phys(skb->data); + sdma_fec_tfd_submit_buffer(priv->tx_sdma, skb, data, skb->len); + + if (sdma_queue_full(priv->tx_sdma)) { + priv->tx_full = 1; + netif_stop_queue(dev); + } + spin_unlock_irq(&priv->lock); + + return 0; +} + +/* This handles BestComm transmit task interrupts + */ +static irqreturn_t fec_tx_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = dev_id; + struct fec_priv *priv = (struct fec_priv *)dev->priv; + struct sk_buff *skb; + + for (;;) { + sdma_clear_irq(priv->tx_sdma); + spin_lock(&priv->lock); + if (!sdma_buffer_done(priv->tx_sdma)) { + spin_unlock(&priv->lock); + break; + } + skb = sdma_retrieve_buffer(priv->tx_sdma, NULL); + + if (priv->tx_full) { + priv->tx_full = 0; + netif_wake_queue(dev); + } + spin_unlock(&priv->lock); + dev_kfree_skb_irq(skb); + } + + return IRQ_HANDLED; +} + +static irqreturn_t fec_rx_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = dev_id; + struct fec_priv *priv = (struct fec_priv *)dev->priv; + struct sk_buff *skb; + struct sk_buff *rskb; + void *data; + int length; + + for (;;) { + sdma_clear_irq(priv->rx_sdma); + + if (!sdma_buffer_done(priv->rx_sdma)) + break; + + rskb = sdma_retrieve_buffer(priv->rx_sdma, &length); + /* length included sizeof CRC32 */ + skb_trim(rskb, length - sizeof(u32)); + + /* allocate replacement skb */ + skb = dev_alloc_skb(FEC_RX_BUFFER_SIZE); + if (skb) { + rskb->protocol = eth_type_trans(rskb, dev); + netif_rx(rskb); + dev->last_rx = jiffies; + } else { + printk(KERN_NOTICE + "%s: Memory squeeze, dropping packet.\n", + dev->name); + priv->stats.rx_dropped++; + + skb_trim(rskb, 0); + skb = rskb; + } + + skb->dev = dev; + skb_put(skb, FEC_RX_BUFFER_SIZE); + data = (void *)virt_to_phys(skb->data); + sdma_submit_buffer(priv->rx_sdma, skb, data, skb->len); + } + + return IRQ_HANDLED; +} + +static irqreturn_t fec_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct fec_priv *priv = (struct fec_priv *)dev->priv; + struct mpc52xx_fec *fec = priv->fec; + int ievent; + + ievent = in_be32(&fec->ievent); + out_be32(&fec->ievent, ievent); /* clear pending events */ + + if (ievent & (FEC_IEVENT_RFIFO_ERROR | FEC_IEVENT_XFIFO_ERROR)) { + if (ievent & FEC_IEVENT_RFIFO_ERROR) + printk(KERN_WARNING "FEC_IEVENT_RFIFO_ERROR\n"); + if (ievent & FEC_IEVENT_XFIFO_ERROR) + printk(KERN_WARNING "FEC_IEVENT_XFIFO_ERROR\n"); + fec_reinit(dev); + } + else if (ievent & FEC_IEVENT_MII) + fec_mii(dev); + return IRQ_HANDLED; +} + +static int fec_close(struct net_device *dev) +{ + struct fec_priv *priv = (struct fec_priv *)dev->priv; + unsigned long timeout; + + priv->open_time = 0; + priv->sequence_done = 0; + + netif_stop_queue(dev); + + sdma_disable(priv->rx_sdma); /* disable receive task */ + + /* Wait for queues to drain */ + timeout = jiffies + 2*HZ; + while (time_before(jiffies, timeout) && + (!sdma_queue_empty(priv->tx_sdma) || + !sdma_queue_empty(priv->rx_sdma))) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/10); + } + if (time_after_eq(jiffies, timeout)) + printk(KERN_ERR "fec_close: queues didn't drain\n"); + + sdma_disable(priv->tx_sdma); + + fec_free_rx_buffers(priv->rx_sdma); + + fec_get_stats(dev); + + return 0; +} + +/* + * Get the current statistics. + * This may be called with the card open or closed. + */ +static struct net_device_stats *fec_get_stats(struct net_device *dev) +{ + struct fec_priv *priv = (struct fec_priv *)dev->priv; + struct net_device_stats *stats = &priv->stats; + struct mpc52xx_fec *fec = priv->fec; + + stats->rx_bytes = in_be32(&fec->rmon_r_octets); + stats->rx_packets = in_be32(&fec->rmon_r_packets); + stats->rx_errors = stats->rx_packets - in_be32(&fec->ieee_r_frame_ok); + stats->tx_bytes = in_be32(&fec->rmon_t_octets); + stats->tx_packets = in_be32(&fec->rmon_t_packets); + stats->tx_errors = stats->tx_packets - ( + in_be32(&fec->ieee_t_frame_ok) + + in_be32(&fec->rmon_t_col) + + in_be32(&fec->ieee_t_1col) + + in_be32(&fec->ieee_t_mcol) + + in_be32(&fec->ieee_t_def)); + stats->multicast = in_be32(&fec->rmon_r_mc_pkt); + stats->collisions = in_be32(&fec->rmon_t_col); + + /* detailed rx_errors: */ + stats->rx_length_errors = in_be32(&fec->rmon_r_undersize) + + in_be32(&fec->rmon_r_oversize) + + in_be32(&fec->rmon_r_frag) + + in_be32(&fec->rmon_r_jab); + stats->rx_over_errors = in_be32(&fec->r_macerr); + stats->rx_crc_errors = in_be32(&fec->ieee_r_crc); + stats->rx_frame_errors = in_be32(&fec->ieee_r_align); + stats->rx_fifo_errors = in_be32(&fec->rmon_r_drop); + stats->rx_missed_errors = in_be32(&fec->rmon_r_drop); + + /* detailed tx_errors: */ + stats->tx_aborted_errors = 0; + stats->tx_carrier_errors = in_be32(&fec->ieee_t_cserr); + stats->tx_fifo_errors = in_be32(&fec->rmon_t_drop); + stats->tx_heartbeat_errors = in_be32(&fec->ieee_t_sqe); + stats->tx_window_errors = in_be32(&fec->ieee_t_lcol); + + return stats; +} + +static void fec_update_stat(struct net_device *dev) +{ + struct fec_priv *priv = (struct fec_priv *)dev->priv; + struct net_device_stats *stats = &priv->stats; + struct mpc52xx_fec *fec = priv->fec; + + out_be32(&fec->mib_control, FEC_MIB_DISABLE); + memset_io(&fec->rmon_t_drop, 0, + (u32)&fec->reserved10 - (u32)&fec->rmon_t_drop); + out_be32(&fec->mib_control, 0); + memset(stats, 0, sizeof *stats); + fec_get_stats(dev); +} + +/* + * Set or clear the multicast filter for this adaptor. + */ +static void fec_set_multicast_list(struct net_device *dev) +{ + struct fec_priv *priv = (struct fec_priv *)dev->priv; + struct mpc52xx_fec *fec = priv->fec; + u32 rx_control; + + rx_control = in_be32(&fec->r_cntrl); + + if (dev->flags & IFF_PROMISC) { + rx_control |= FEC_RCNTRL_PROM; + out_be32(&fec->r_cntrl, rx_control); + } else { + rx_control &= ~FEC_RCNTRL_PROM; + out_be32(&fec->r_cntrl, rx_control); + + if (dev->flags & IFF_ALLMULTI) { + out_be32(&fec->gaddr1, 0xffffffff); + out_be32(&fec->gaddr2, 0xffffffff); + } else { + u32 crc; + int i; + struct dev_mc_list *dmi; + u32 gaddr1 = 0x00000000; + u32 gaddr2 = 0x00000000; + + dmi = dev->mc_list; + for (i=0; imc_count; i++) { + crc = ether_crc_le(6, dmi->dmi_addr) >> 26; + if (crc >= 32) + gaddr1 |= 1 << (crc-32); + else + gaddr2 |= 1 << crc; + dmi = dmi->next; + } + out_be32(&fec->gaddr1, gaddr1); + out_be32(&fec->gaddr2, gaddr2); + } + } +} + +static void __init fec_str2mac(char *str, unsigned char *mac) +{ + int i; + u64 val64; + + val64 = simple_strtoull(str, NULL, 16); + + for (i = 0; i < 6; i++) + mac[5-i] = val64 >> (i*8); +} + +int __init mpc52xx_fec_mac_setup(char *mac_address) +{ + fec_str2mac(mac_address, mpc52xx_fec_mac_addr); + return 0; +} + +__setup("mpc52xx_mac=", mpc52xx_fec_mac_setup); + +static void fec_hw_init(struct net_device *dev) +{ + struct fec_priv *priv = (struct fec_priv *)dev->priv; + struct mpc52xx_fec *fec = priv->fec; + bd_t *bd = (bd_t *) &__res; + + out_be32(&fec->op_pause, 0x00010020); + out_be32(&fec->rfifo_cntrl, 0x0f000000); + out_be32(&fec->rfifo_alarm, 0x0000030c); + out_be32(&fec->tfifo_cntrl, 0x0f000000); + out_be32(&fec->tfifo_alarm, 0x00000100); + out_be32(&fec->x_wmrk, 0x3); /* xmit fifo watermark = 256 */ + out_be32(&fec->xmit_fsm, 0x03000000); /* enable crc generation */ + out_be32(&fec->iaddr1, 0x00000000); /* No individual filter */ + out_be32(&fec->iaddr2, 0x00000000); /* No individual filter */ + + priv->phy_speed = ((bd->bi_ipbfreq >> 20) / 5) << 1; + + fec_restart(dev, 0); /* always use half duplex mode only */ + /* + * Read MIB counters in order to reset them, + * then zero all the stats fields in memory + */ + fec_update_stat(dev); +} + + +static void fec_reinit(struct net_device *dev) +{ + struct fec_priv *priv = (struct fec_priv *)dev->priv; + struct mpc52xx_fec *fec = priv->fec; + static void fec_update_stat(struct net_device *); + + netif_stop_queue(dev); + out_be32(&fec->imask, 0x0); + + /* Disable the rx and tx tasks. */ + sdma_disable(priv->rx_sdma); + sdma_disable(priv->tx_sdma); + + /* Stop FEC */ + out_be32(&fec->ecntrl, in_be32(&fec->ecntrl) & ~0x2); + + /* Restart the DMA tasks */ + sdma_fec_rx_init(priv->rx_sdma, priv->rx_fifo, FEC_RX_BUFFER_SIZE); + sdma_fec_tx_init(priv->tx_sdma, priv->tx_fifo); + fec_hw_init(dev); + + if (priv->sequence_done) { /* redo the fec_open() */ + fec_free_rx_buffers(priv->rx_sdma); + fec_open(dev); + } + return; +} + + +/* ======================================================================== */ +/* OCP Driver */ +/* ======================================================================== */ + +static int __devinit +mpc52xx_fec_probe(struct ocp_device *ocp) +{ + int ret; + struct net_device *dev; + struct fec_priv *priv = NULL; + + /* Reserve FEC control zone */ + if (!request_mem_region(ocp->def->paddr, sizeof(struct mpc52xx_fec), + "mpc52xx_fec")) + return -EBUSY; + + /* Get the ether dev & it's private zone */ + dev = alloc_etherdev(sizeof(struct fec_priv)); + if (!dev) { + ret = -ENOMEM; + goto probe_error; + } + + priv = (struct fec_priv *)dev->priv; + + /* Init ether dev with what we have */ + dev->open = fec_open; + dev->stop = fec_close; + dev->hard_start_xmit = fec_hard_start_xmit; + dev->do_ioctl = fec_ioctl; + dev->get_stats = fec_get_stats; + dev->set_mac_address = fec_set_mac_address; + dev->set_multicast_list = fec_set_multicast_list; + dev->tx_timeout = fec_tx_timeout; + dev->watchdog_timeo = FEC_WATCHDOG_TIMEOUT; + dev->flags &= ~IFF_RUNNING; + dev->base_addr = ocp->def->paddr; + + priv->rx_fifo = dev->base_addr + FIELD_OFFSET(mpc52xx_fec,rfifo_data); + priv->tx_fifo = dev->base_addr + FIELD_OFFSET(mpc52xx_fec,tfifo_data); + priv->t_irq = priv->r_irq = dev->irq = -1; /* IRQ are free for now */ + + spin_lock_init(&priv->lock); + + /* ioremap the zones */ + priv->fec = (struct mpc52xx_fec *) + ioremap(ocp->def->paddr, sizeof(struct mpc52xx_fec)); + + if (!priv->fec) { + ret = -ENOMEM; + goto probe_error; + } + + /* SDMA init */ + priv->rx_sdma = sdma_alloc(FEC_RX_NUM_BD); + priv->tx_sdma = sdma_alloc(FEC_TX_NUM_BD); + + if (!priv->rx_sdma || !priv->tx_sdma) { + ret = -ENOMEM; + goto probe_error; + } + + ret = sdma_fec_rx_init(priv->rx_sdma, priv->rx_fifo,FEC_RX_BUFFER_SIZE); + if (ret < 0) + goto probe_error; + + ret = sdma_fec_tx_init(priv->tx_sdma, priv->tx_fifo); + if (ret < 0) + goto probe_error; + + /* Get the IRQ we need one by one */ + /* Control */ + dev->irq = ocp->def->irq; + if (request_irq(dev->irq, &fec_interrupt, SA_INTERRUPT, + "mpc52xx_fec_ctrl", dev)) { + printk(KERN_ERR "mpc52xx_fec: ctrl interrupt request failed\n"); + ret = -EBUSY; + dev->irq = -1; /* Don't try to free it */ + goto probe_error; + } + + /* RX */ + priv->r_irq = sdma_irq(priv->rx_sdma); + if (request_irq(priv->r_irq, &fec_rx_interrupt, SA_INTERRUPT, + "mpc52xx_fec_rx", dev)) { + printk(KERN_ERR "mpc52xx_fec: rx interrupt request failed\n"); + ret = -EBUSY; + priv->r_irq = -1; /* Don't try to free it */ + goto probe_error; + } + + /* TX */ + priv->t_irq = sdma_irq(priv->tx_sdma); + if (request_irq(priv->t_irq, &fec_tx_interrupt, SA_INTERRUPT, + "mpc52xx_fec_tx", dev)) { + printk(KERN_ERR "mpc52xx_fec: tx interrupt request failed\n"); + ret = -EBUSY; + priv->t_irq = -1; /* Don't try to free it */ + goto probe_error; + } + + /* MAC address init */ + if (memcmp(mpc52xx_fec_mac_addr, null_mac, 6) != 0) + memcpy(dev->dev_addr, mpc52xx_fec_mac_addr, 6); + else + fec_get_paddr(dev, dev->dev_addr); + + /* Hardware init */ + fec_hw_init(dev); + + /* Register the new network device */ + ret = register_netdev(dev); + if(ret < 0) + goto probe_error; + + /* MII init : After register ???? */ + fec_mii_init(dev); + + /* We're done ! */ + ocp_set_drvdata(ocp, dev); + + return 0; + + + /* Error handling - free everything that might be allocated */ +probe_error: + + if (dev) { + if (priv->rx_sdma) sdma_free(priv->rx_sdma); + if (priv->tx_sdma) sdma_free(priv->tx_sdma); + + if (dev->irq >= 0) free_irq(dev->irq, dev); + if (priv->r_irq >= 0) free_irq(priv->r_irq, dev); + if (priv->t_irq >= 0) free_irq(priv->t_irq, dev); + + if (priv->fec) iounmap(priv->fec); + + free_netdev(dev); + } + + release_mem_region(ocp->def->paddr, sizeof(struct mpc52xx_fec)); + + return ret; +} + +static void +mpc52xx_fec_remove(struct ocp_device *ocp) +{ + struct net_device *dev; + struct fec_priv *priv; + + dev = (struct net_device *) ocp_get_drvdata(ocp); + if (!dev) + return; + priv = (struct fec_priv *) dev->priv; + + unregister_netdev(dev); + + free_irq(dev->irq, dev); + free_irq(priv->r_irq, dev); + free_irq(priv->t_irq, dev); + + iounmap(priv->fec); + + release_mem_region(dev->base_addr, sizeof(struct mpc52xx_fec)); + + free_netdev(dev); + + ocp_set_drvdata(ocp, NULL); +} + +static struct ocp_device_id mpc52xx_fec_ids[] __devinitdata = { + { .vendor = OCP_VENDOR_FREESCALE, .function = OCP_FUNC_FEC_MPC52xx }, + { .vendor = OCP_VENDOR_INVALID /* Terminating entry */ } +}; + +MODULE_DEVICE_TABLE(ocp, mpc52xx_fec_ids); + +static struct ocp_driver mpc52xx_fec_ocp_driver = { + .name = "mpc52xx_fec", + .id_table = mpc52xx_fec_ids, + .probe = mpc52xx_fec_probe, + .remove = mpc52xx_fec_remove, +#ifdef CONFIG_PM +/* .suspend = mpc52xx_fec_suspend, TODO */ +/* .resume = mpc52xx_fec_resume, TODO */ +#endif +}; + + +/* ======================================================================== */ +/* Module */ +/* ======================================================================== */ + +static int __init +mpc52xx_fec_init(void) +{ + return ocp_register_driver(&mpc52xx_fec_ocp_driver); +} + +static void __exit +mpc52xx_fec_exit(void) +{ + ocp_unregister_driver(&mpc52xx_fec_ocp_driver); +} + + +module_init(mpc52xx_fec_init); +module_exit(mpc52xx_fec_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Dale Farnsworth"); +MODULE_DESCRIPTION("Ethernet driver for the Freescale MPC52xx FEC"); diff -Nru a/drivers/net/fec_mpc52xx/fec.h b/drivers/net/fec_mpc52xx/fec.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/fec_mpc52xx/fec.h 2004-11-20 23:26:34 +01:00 @@ -0,0 +1,308 @@ +/* + * drivers/net/fec_mpc52xx/fec.h + * + * Driver for the MPC5200 Fast Ethernet Controller + * + * Author: Dale Farnsworth + * + * 2003-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifndef __DRIVERS_NET_MPC52XX_FEC_H__ +#define __DRIVERS_NET_MPC52XX_FEC_H__ + +/* Tunable constant */ +/* FEC_RX_BUFFER_SIZE includes 4 bytes for CRC32 */ +#define FEC_RX_BUFFER_SIZE 1522 /* max receive packet size */ +#define FEC_RX_NUM_BD 64 +#define FEC_TX_NUM_BD 64 + +#define FEC_RESET_DELAY 50 /* uS */ + +#define FEC_WATCHDOG_TIMEOUT ((400*HZ)/1000) + +struct fec_priv { + int full_duplex; + int tx_full; + int r_irq; + int t_irq; + u32 last_transmit_time; + struct mpc52xx_fec *fec; + struct sdma *rx_sdma; + struct sdma *tx_sdma; + spinlock_t lock; + unsigned long open_time; + struct net_device_stats stats; +#ifdef CONFIG_USE_MDIO + uint phy_id; + uint phy_id_done; + uint phy_status; + uint phy_speed; + phy_info_t *phy; + struct tasklet_struct phy_task; + uint sequence_done; + uint phy_addr; + struct timer_list phy_timer_list; + u16 old_status; + phys_addr_t rx_fifo; + phys_addr_t tx_fifo; +#endif /* CONFIG_USE_MDIO */ +}; + + +/* ======================================================================== */ +/* Hardware register sets & bits */ +/* ======================================================================== */ + +struct mpc52xx_fec { + u32 fec_id; /* FEC + 0x000 */ + u32 ievent; /* FEC + 0x004 */ + u32 imask; /* FEC + 0x008 */ + + u32 reserved0[1]; /* FEC + 0x00C */ + u32 r_des_active; /* FEC + 0x010 */ + u32 x_des_active; /* FEC + 0x014 */ + u32 r_des_active_cl; /* FEC + 0x018 */ + u32 x_des_active_cl; /* FEC + 0x01C */ + u32 ivent_set; /* FEC + 0x020 */ + u32 ecntrl; /* FEC + 0x024 */ + + u32 reserved1[6]; /* FEC + 0x028-03C */ + u32 mii_data; /* FEC + 0x040 */ + u32 mii_speed; /* FEC + 0x044 */ + u32 mii_status; /* FEC + 0x048 */ + + u32 reserved2[5]; /* FEC + 0x04C-05C */ + u32 mib_data; /* FEC + 0x060 */ + u32 mib_control; /* FEC + 0x064 */ + + u32 reserved3[6]; /* FEC + 0x068-7C */ + u32 r_activate; /* FEC + 0x080 */ + u32 r_cntrl; /* FEC + 0x084 */ + u32 r_hash; /* FEC + 0x088 */ + u32 r_data; /* FEC + 0x08C */ + u32 ar_done; /* FEC + 0x090 */ + u32 r_test; /* FEC + 0x094 */ + u32 r_mib; /* FEC + 0x098 */ + u32 r_da_low; /* FEC + 0x09C */ + u32 r_da_high; /* FEC + 0x0A0 */ + + u32 reserved4[7]; /* FEC + 0x0A4-0BC */ + u32 x_activate; /* FEC + 0x0C0 */ + u32 x_cntrl; /* FEC + 0x0C4 */ + u32 backoff; /* FEC + 0x0C8 */ + u32 x_data; /* FEC + 0x0CC */ + u32 x_status; /* FEC + 0x0D0 */ + u32 x_mib; /* FEC + 0x0D4 */ + u32 x_test; /* FEC + 0x0D8 */ + u32 fdxfc_da1; /* FEC + 0x0DC */ + u32 fdxfc_da2; /* FEC + 0x0E0 */ + u32 paddr1; /* FEC + 0x0E4 */ + u32 paddr2; /* FEC + 0x0E8 */ + u32 op_pause; /* FEC + 0x0EC */ + + u32 reserved5[4]; /* FEC + 0x0F0-0FC */ + u32 instr_reg; /* FEC + 0x100 */ + u32 context_reg; /* FEC + 0x104 */ + u32 test_cntrl; /* FEC + 0x108 */ + u32 acc_reg; /* FEC + 0x10C */ + u32 ones; /* FEC + 0x110 */ + u32 zeros; /* FEC + 0x114 */ + u32 iaddr1; /* FEC + 0x118 */ + u32 iaddr2; /* FEC + 0x11C */ + u32 gaddr1; /* FEC + 0x120 */ + u32 gaddr2; /* FEC + 0x124 */ + u32 random; /* FEC + 0x128 */ + u32 rand1; /* FEC + 0x12C */ + u32 tmp; /* FEC + 0x130 */ + + u32 reserved6[3]; /* FEC + 0x134-13C */ + u32 fifo_id; /* FEC + 0x140 */ + u32 x_wmrk; /* FEC + 0x144 */ + u32 fcntrl; /* FEC + 0x148 */ + u32 r_bound; /* FEC + 0x14C */ + u32 r_fstart; /* FEC + 0x150 */ + u32 r_count; /* FEC + 0x154 */ + u32 r_lag; /* FEC + 0x158 */ + u32 r_read; /* FEC + 0x15C */ + u32 r_write; /* FEC + 0x160 */ + u32 x_count; /* FEC + 0x164 */ + u32 x_lag; /* FEC + 0x168 */ + u32 x_retry; /* FEC + 0x16C */ + u32 x_write; /* FEC + 0x170 */ + u32 x_read; /* FEC + 0x174 */ + + u32 reserved7[2]; /* FEC + 0x178-17C */ + u32 fm_cntrl; /* FEC + 0x180 */ + u32 rfifo_data; /* FEC + 0x184 */ + u32 rfifo_status; /* FEC + 0x188 */ + u32 rfifo_cntrl; /* FEC + 0x18C */ + u32 rfifo_lrf_ptr; /* FEC + 0x190 */ + u32 rfifo_lwf_ptr; /* FEC + 0x194 */ + u32 rfifo_alarm; /* FEC + 0x198 */ + u32 rfifo_rdptr; /* FEC + 0x19C */ + u32 rfifo_wrptr; /* FEC + 0x1A0 */ + u32 tfifo_data; /* FEC + 0x1A4 */ + u32 tfifo_status; /* FEC + 0x1A8 */ + u32 tfifo_cntrl; /* FEC + 0x1AC */ + u32 tfifo_lrf_ptr; /* FEC + 0x1B0 */ + u32 tfifo_lwf_ptr; /* FEC + 0x1B4 */ + u32 tfifo_alarm; /* FEC + 0x1B8 */ + u32 tfifo_rdptr; /* FEC + 0x1BC */ + u32 tfifo_wrptr; /* FEC + 0x1C0 */ + + u32 reset_cntrl; /* FEC + 0x1C4 */ + u32 xmit_fsm; /* FEC + 0x1C8 */ + + u32 reserved8[3]; /* FEC + 0x1CC-1D4 */ + u32 rdes_data0; /* FEC + 0x1D8 */ + u32 rdes_data1; /* FEC + 0x1DC */ + u32 r_length; /* FEC + 0x1E0 */ + u32 x_length; /* FEC + 0x1E4 */ + u32 x_addr; /* FEC + 0x1E8 */ + u32 cdes_data; /* FEC + 0x1EC */ + u32 status; /* FEC + 0x1F0 */ + u32 dma_control; /* FEC + 0x1F4 */ + u32 des_cmnd; /* FEC + 0x1F8 */ + u32 data; /* FEC + 0x1FC */ + + u32 rmon_t_drop; /* FEC + 0x200 */ + u32 rmon_t_packets; /* FEC + 0x204 */ + u32 rmon_t_bc_pkt; /* FEC + 0x208 */ + u32 rmon_t_mc_pkt; /* FEC + 0x20C */ + u32 rmon_t_crc_align; /* FEC + 0x210 */ + u32 rmon_t_undersize; /* FEC + 0x214 */ + u32 rmon_t_oversize; /* FEC + 0x218 */ + u32 rmon_t_frag; /* FEC + 0x21C */ + u32 rmon_t_jab; /* FEC + 0x220 */ + u32 rmon_t_col; /* FEC + 0x224 */ + u32 rmon_t_p64; /* FEC + 0x228 */ + u32 rmon_t_p65to127; /* FEC + 0x22C */ + u32 rmon_t_p128to255; /* FEC + 0x230 */ + u32 rmon_t_p256to511; /* FEC + 0x234 */ + u32 rmon_t_p512to1023; /* FEC + 0x238 */ + u32 rmon_t_p1024to2047; /* FEC + 0x23C */ + u32 rmon_t_p_gte2048; /* FEC + 0x240 */ + u32 rmon_t_octets; /* FEC + 0x244 */ + u32 ieee_t_drop; /* FEC + 0x248 */ + u32 ieee_t_frame_ok; /* FEC + 0x24C */ + u32 ieee_t_1col; /* FEC + 0x250 */ + u32 ieee_t_mcol; /* FEC + 0x254 */ + u32 ieee_t_def; /* FEC + 0x258 */ + u32 ieee_t_lcol; /* FEC + 0x25C */ + u32 ieee_t_excol; /* FEC + 0x260 */ + u32 ieee_t_macerr; /* FEC + 0x264 */ + u32 ieee_t_cserr; /* FEC + 0x268 */ + u32 ieee_t_sqe; /* FEC + 0x26C */ + u32 t_fdxfc; /* FEC + 0x270 */ + u32 ieee_t_octets_ok; /* FEC + 0x274 */ + + u32 reserved9[2]; /* FEC + 0x278-27C */ + u32 rmon_r_drop; /* FEC + 0x280 */ + u32 rmon_r_packets; /* FEC + 0x284 */ + u32 rmon_r_bc_pkt; /* FEC + 0x288 */ + u32 rmon_r_mc_pkt; /* FEC + 0x28C */ + u32 rmon_r_crc_align; /* FEC + 0x290 */ + u32 rmon_r_undersize; /* FEC + 0x294 */ + u32 rmon_r_oversize; /* FEC + 0x298 */ + u32 rmon_r_frag; /* FEC + 0x29C */ + u32 rmon_r_jab; /* FEC + 0x2A0 */ + + u32 rmon_r_resvd_0; /* FEC + 0x2A4 */ + + u32 rmon_r_p64; /* FEC + 0x2A8 */ + u32 rmon_r_p65to127; /* FEC + 0x2AC */ + u32 rmon_r_p128to255; /* FEC + 0x2B0 */ + u32 rmon_r_p256to511; /* FEC + 0x2B4 */ + u32 rmon_r_p512to1023; /* FEC + 0x2B8 */ + u32 rmon_r_p1024to2047; /* FEC + 0x2BC */ + u32 rmon_r_p_gte2048; /* FEC + 0x2C0 */ + u32 rmon_r_octets; /* FEC + 0x2C4 */ + u32 ieee_r_drop; /* FEC + 0x2C8 */ + u32 ieee_r_frame_ok; /* FEC + 0x2CC */ + u32 ieee_r_crc; /* FEC + 0x2D0 */ + u32 ieee_r_align; /* FEC + 0x2D4 */ + u32 r_macerr; /* FEC + 0x2D8 */ + u32 r_fdxfc; /* FEC + 0x2DC */ + u32 ieee_r_octets_ok; /* FEC + 0x2E0 */ + + u32 reserved10[6]; /* FEC + 0x2E4-2FC */ + + u32 reserved11[64]; /* FEC + 0x300-3FF */ +}; + +#define FEC_MIB_DISABLE 0x80000000 + +#define FEC_IEVENT_HBERR 0x80000000 +#define FEC_IEVENT_BABR 0x40000000 +#define FEC_IEVENT_BABT 0x20000000 +#define FEC_IEVENT_GRA 0x10000000 +#define FEC_IEVENT_TFINT 0x08000000 +#define FEC_IEVENT_MII 0x00800000 +#define FEC_IEVENT_LATE_COL 0x00200000 +#define FEC_IEVENT_COL_RETRY_LIM 0x00100000 +#define FEC_IEVENT_XFIFO_UN 0x00080000 +#define FEC_IEVENT_XFIFO_ERROR 0x00040000 +#define FEC_IEVENT_RFIFO_ERROR 0x00020000 + +#define FEC_IMASK_HBERR 0x80000000 +#define FEC_IMASK_BABR 0x40000000 +#define FEC_IMASK_BABT 0x20000000 +#define FEC_IMASK_GRA 0x10000000 +#define FEC_IMASK_MII 0x00800000 +#define FEC_IMASK_LATE_COL 0x00200000 +#define FEC_IMASK_COL_RETRY_LIM 0x00100000 +#define FEC_IMASK_XFIFO_UN 0x00080000 +#define FEC_IMASK_XFIFO_ERROR 0x00040000 +#define FEC_IMASK_RFIFO_ERROR 0x00020000 + +#define FEC_RCNTRL_MAX_FL_SHIFT 16 +#define FEC_RCNTRL_LOOP 0x01 +#define FEC_RCNTRL_DRT 0x02 +#define FEC_RCNTRL_MII_MODE 0x04 +#define FEC_RCNTRL_PROM 0x08 +#define FEC_RCNTRL_BC_REJ 0x10 +#define FEC_RCNTRL_FCE 0x20 + +#define FEC_TCNTRL_GTS 0x00000001 +#define FEC_TCNTRL_HBC 0x00000002 +#define FEC_TCNTRL_FDEN 0x00000004 +#define FEC_TCNTRL_TFC_PAUSE 0x00000008 +#define FEC_TCNTRL_RFC_PAUSE 0x00000010 + +#define FEC_ECNTRL_RESET 0x00000001 +#define FEC_ECNTRL_ETHER_EN 0x00000002 + +struct mibCounters { + unsigned int byteReceived; + unsigned int byteSent; + unsigned int framesReceived; + unsigned int framesSent; + unsigned int totalByteReceived; + unsigned int totalFramesReceived; + unsigned int broadcastFramesReceived; + unsigned int multicastFramesReceived; + unsigned int cRCError; + unsigned int oversizeFrames; + unsigned int fragments; + unsigned int jabber; + unsigned int collision; + unsigned int lateCollision; + unsigned int frames64; + unsigned int frames65_127; + unsigned int frames128_255; + unsigned int frames256_511; + unsigned int frames512_1023; + unsigned int frames1024_MaxSize; + unsigned int macRxError; + unsigned int droppedFrames; + unsigned int outMulticastFrames; + unsigned int outBroadcastFrames; + unsigned int undersizeFrames; +}; + + +#endif /* __DRIVERS_NET_MPC52XX_FEC_H__ */ diff -Nru a/drivers/net/fec_mpc52xx/fec_phy.c b/drivers/net/fec_mpc52xx/fec_phy.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/fec_mpc52xx/fec_phy.c 2004-11-20 23:26:34 +01:00 @@ -0,0 +1,526 @@ +/* + * arch/ppc/52xx_io/fec_phy.c + * + * Driver for the MPC5200 Fast Ethernet Controller + * Based heavily on the MII support for the MPC8xx by Dan Malek + * + * Author: Dale Farnsworth + * + * 2003-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fec_phy.h" +#include "fec.h" + +static int mpc52xx_netdev_ethtool_ioctl(struct net_device *dev, void *useraddr); + +/* MII processing. We keep this as simple as possible. Requests are + * placed on the list (if there is room). When the request is finished + * by the MII, an optional function may be called. + */ +typedef struct mii_list { + uint mii_regval; + void (*mii_func)(uint val, struct net_device *dev, uint data); + struct mii_list *mii_next; + uint mii_data; +} mii_list_t; + +#define NMII 20 +mii_list_t mii_cmds[NMII]; +mii_list_t *mii_free; +mii_list_t *mii_head; +mii_list_t *mii_tail; + +typedef struct mdio_read_data { + __u16 regval; + struct task_struct *sleeping_task; +} mdio_read_data_t; + +static int mii_queue(struct net_device *dev, int request, + void (*func)(uint, struct net_device *, uint), uint data); + +/* Make MII read/write commands for the FEC. + * */ +#define mk_mii_read(REG) (0x60020000 | ((REG & 0x1f) << 18)) +#define mk_mii_write(REG, VAL) (0x50020000 | ((REG & 0x1f) << 18) | \ + (VAL & 0xffff)) +#define mk_mii_end 0 + +/* Register definitions for the PHY. +*/ + +#define MII_REG_CR 0 /* Control Register */ +#define MII_REG_SR 1 /* Status Register */ +#define MII_REG_PHYIR1 2 /* PHY Identification Register 1 */ +#define MII_REG_PHYIR2 3 /* PHY Identification Register 2 */ +#define MII_REG_ANAR 4 /* A-N Advertisement Register */ +#define MII_REG_ANLPAR 5 /* A-N Link Partner Ability Register */ +#define MII_REG_ANER 6 /* A-N Expansion Register */ +#define MII_REG_ANNPTR 7 /* A-N Next Page Transmit Register */ +#define MII_REG_ANLPRNPR 8 /* A-N Link Partner Received Next Page Reg. */ + +/* values for phy_status */ + +#define PHY_CONF_ANE 0x0001 /* 1 auto-negotiation enabled */ +#define PHY_CONF_LOOP 0x0002 /* 1 loopback mode enabled */ +#define PHY_CONF_SPMASK 0x00f0 /* mask for speed */ +#define PHY_CONF_10HDX 0x0010 /* 10 Mbit half duplex supported */ +#define PHY_CONF_10FDX 0x0020 /* 10 Mbit full duplex supported */ +#define PHY_CONF_100HDX 0x0040 /* 100 Mbit half duplex supported */ +#define PHY_CONF_100FDX 0x0080 /* 100 Mbit full duplex supported */ + +#define PHY_STAT_LINK 0x0100 /* 1 up - 0 down */ +#define PHY_STAT_FAULT 0x0200 /* 1 remote fault */ +#define PHY_STAT_ANC 0x0400 /* 1 auto-negotiation complete */ +#define PHY_STAT_SPMASK 0xf000 /* mask for speed */ +#define PHY_STAT_10HDX 0x1000 /* 10 Mbit half duplex selected */ +#define PHY_STAT_10FDX 0x2000 /* 10 Mbit full duplex selected */ +#define PHY_STAT_100HDX 0x4000 /* 100 Mbit half duplex selected */ +#define PHY_STAT_100FDX 0x8000 /* 100 Mbit full duplex selected */ + +void fec_mii(struct net_device *dev) +{ + struct fec_priv *priv = (struct fec_priv *)dev->priv; + struct mpc52xx_fec *fec = priv->fec; + mii_list_t *mip; + uint mii_reg; + + mii_reg = in_be32(&fec->mii_data); + + if ((mip = mii_head) == NULL) { + printk(KERN_ERR "MII and no head!\n"); + return; + } + + if (mip->mii_func != NULL) + (*(mip->mii_func))(mii_reg, dev, mip->mii_data); + + mii_head = mip->mii_next; + mip->mii_next = mii_free; + mii_free = mip; + + if ((mip = mii_head) != NULL) + out_be32(&fec->mii_data, mip->mii_regval); +} + +static int mii_queue(struct net_device *dev, int regval, + void (*func)(uint, struct net_device *, uint), + uint data) +{ + struct fec_priv *priv = (struct fec_priv *)dev->priv; + struct mpc52xx_fec *fec = priv->fec; + mii_list_t *mip; + int retval; + + /* Add PHY address to register command. + */ + regval |= priv->phy_addr << 23; + + retval = 0; + + if ((mip = mii_free) != NULL) { + mii_free = mip->mii_next; + mip->mii_regval = regval; + mip->mii_func = func; + mip->mii_next = NULL; + mip->mii_data = data; + if (mii_head) { + mii_tail->mii_next = mip; + mii_tail = mip; + } else { + mii_head = mii_tail = mip; + out_be32(&fec->mii_data, regval); + } + } else + retval = 1; + + return retval; +} + +static void mii_do_cmd(struct net_device *dev, const phy_cmd_t *c) +{ + int k; + + if (!c) + return; + + for (k = 0; (c+k)->mii_data != mk_mii_end; k++) + mii_queue(dev, (c+k)->mii_data, (c+k)->funct, 0); +} + +static void mii_parse_sr(uint mii_reg, struct net_device *dev, uint data) +{ + struct fec_priv *priv = (struct fec_priv *)dev->priv; + uint s = priv->phy_status; + + s &= ~(PHY_STAT_LINK | PHY_STAT_FAULT | PHY_STAT_ANC); + + if (mii_reg & 0x0004) + s |= PHY_STAT_LINK; + if (mii_reg & 0x0010) + s |= PHY_STAT_FAULT; + if (mii_reg & 0x0020) + s |= PHY_STAT_ANC; + + priv->phy_status = s; +} + +static void mii_parse_cr(uint mii_reg, struct net_device *dev, uint data) +{ + struct fec_priv *priv = (struct fec_priv *)dev->priv; + uint s = priv->phy_status; + + s &= ~(PHY_CONF_ANE | PHY_CONF_LOOP); + + if (mii_reg & 0x1000) + s |= PHY_CONF_ANE; + if (mii_reg & 0x4000) + s |= PHY_CONF_LOOP; + + priv->phy_status = s; +} + +static void mii_parse_anar(uint mii_reg, struct net_device *dev, uint data) +{ + struct fec_priv *priv = (struct fec_priv *)dev->priv; + uint s = priv->phy_status; + + s &= ~(PHY_CONF_SPMASK); + + if (mii_reg & 0x0020) + s |= PHY_CONF_10HDX; + if (mii_reg & 0x0040) + s |= PHY_CONF_10FDX; + if (mii_reg & 0x0080) + s |= PHY_CONF_100HDX; + if (mii_reg & 0x0100) + s |= PHY_CONF_100FDX; + + priv->phy_status = s; +} + +/* ------------------------------------------------------------------------- */ +/* Generic PHY support. Should work for all PHYs, but does not support link + * change interrupts. + */ +static phy_info_t phy_info_generic = { + 0x00000000, /* 0-->match any PHY */ + "GENERIC", + + (const phy_cmd_t []) { /* config */ + /* advertise only half-duplex capabilities */ + { mk_mii_write(MII_ADVERTISE, MII_ADVERTISE_HALF), + mii_parse_anar }, + + /* enable auto-negotiation */ + { mk_mii_write(MII_BMCR, BMCR_ANENABLE), mii_parse_cr }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* startup */ + /* restart auto-negotiation */ + { mk_mii_write(MII_BMCR, (BMCR_ANENABLE | BMCR_ANRESTART)), + NULL }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* ack_int */ + /* We don't actually use the ack_int table with a generic + * PHY, but putting a reference to mii_parse_sr here keeps + * us from getting a compiler warning about unused static + * functions in the case where we only compile in generic + * PHY support. + */ + { mk_mii_read(MII_BMSR), mii_parse_sr }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* shutdown */ + { mk_mii_end, } + }, +}; +/* -------------------------------------------------------------------- */ + +/* register definitions for the 971 */ + +#define MII_LXT971_PCR 16 /* Port Control Register */ +#define MII_LXT971_SR2 17 /* Status Register 2 */ +#define MII_LXT971_IER 18 /* Interrupt Enable Register */ +#define MII_LXT971_ISR 19 /* Interrupt Status Register */ +#define MII_LXT971_LCR 20 /* LED Control Register */ +#define MII_LXT971_TCR 30 /* Transmit Control Register */ + +static void mii_parse_lxt971_sr2(uint mii_reg, struct net_device *dev, uint data) +{ + struct fec_priv *priv = (struct fec_priv *)dev->priv; + uint s = priv->phy_status; + + s &= ~(PHY_STAT_SPMASK); + + if (mii_reg & 0x4000) { + if (mii_reg & 0x0200) + s |= PHY_STAT_100FDX; + else + s |= PHY_STAT_100HDX; + } else { + if (mii_reg & 0x0200) + s |= PHY_STAT_10FDX; + else + s |= PHY_STAT_10HDX; + } + if (mii_reg & 0x0008) + s |= PHY_STAT_FAULT; + + priv->phy_status = s; +} + +static phy_info_t phy_info_lxt971 = { + 0x0001378e, + "LXT971", + + (const phy_cmd_t []) { /* config */ + { mk_mii_write(MII_REG_ANAR, 0x0A1), NULL }, /* 10/100, HD */ + { mk_mii_read(MII_REG_CR), mii_parse_cr }, + { mk_mii_read(MII_REG_ANAR), mii_parse_anar }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* startup - enable interrupts */ + { mk_mii_write(MII_LXT971_IER, 0x00f2), NULL }, + { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */ + + /* Somehow does the 971 tell me that the link is down + * the first read after power-up. + * read here to get a valid value in ack_int */ + + { mk_mii_read(MII_REG_SR), mii_parse_sr }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* ack_int */ + /* find out the current status */ + + { mk_mii_read(MII_REG_SR), mii_parse_sr }, + { mk_mii_read(MII_LXT971_SR2), mii_parse_lxt971_sr2 }, + + /* we only need to read ISR to acknowledge */ + + { mk_mii_read(MII_LXT971_ISR), NULL }, + { mk_mii_end, } + }, + (const phy_cmd_t []) { /* shutdown - disable interrupts */ + { mk_mii_write(MII_LXT971_IER, 0x0000), NULL }, + { mk_mii_end, } + }, +}; + +static phy_info_t *phy_info[] = { + &phy_info_lxt971, + /* Generic PHY support. This must be the last PHY in the table. + * It will be used to support any PHY that doesn't match a previous + * entry in the table. + */ + &phy_info_generic, + NULL +}; + +static void mii_display_config(struct net_device *dev) +{ + struct fec_priv *priv = (struct fec_priv *)dev->priv; + uint s = priv->phy_status; + + printk(KERN_INFO "%s: config: auto-negotiation ", dev->name); + + if (s & PHY_CONF_ANE) + printk("on"); + else + printk("off"); + + if (s & PHY_CONF_100FDX) + printk(", 100FDX"); + if (s & PHY_CONF_100HDX) + printk(", 100HDX"); + if (s & PHY_CONF_10FDX) + printk(", 10FDX"); + if (s & PHY_CONF_10HDX) + printk(", 10HDX"); + if (!(s & PHY_CONF_SPMASK)) + printk(", No speed/duplex selected?"); + + if (s & PHY_CONF_LOOP) + printk(", loopback enabled"); + + printk(".\n"); + + priv->sequence_done = 1; +} + +static void mii_queue_config(uint mii_reg, struct net_device *dev, uint data) +{ + struct fec_priv *priv = (struct fec_priv *)dev->priv; + + priv->phy_task.func = (void *)mii_display_config; + priv->phy_task.data = (unsigned long)dev; + tasklet_schedule(&priv->phy_task); +} + + +phy_cmd_t phy_cmd_config[] = { { mk_mii_read(MII_REG_CR), mii_queue_config }, + { mk_mii_end, } }; + + +/* Read remainder of PHY ID. +*/ +static void mii_discover_phy3(uint mii_reg, struct net_device *dev, uint data) +{ + struct fec_priv *priv = (struct fec_priv *)dev->priv; + int i; + + priv->phy_id |= (mii_reg & 0xffff); + + for (i = 0; phy_info[i]; i++) { + if (phy_info[i]->id == (priv->phy_id >> 4) || !phy_info[i]->id) + break; + if (phy_info[i]->id == 0) /* check generic entry */ + break; + } + + if (!phy_info[i]) + panic("%s: PHY id 0x%08x is not supported!\n", + dev->name, priv->phy_id); + + priv->phy = phy_info[i]; + priv->phy_id_done = 1; + + printk(KERN_INFO "%s: Phy @ 0x%x, type %s (0x%08x)\n", + dev->name, priv->phy_addr, priv->phy->name, priv->phy_id); +} + +/* Scan all of the MII PHY addresses looking for someone to respond + * with a valid ID. This usually happens quickly. + */ +static void mii_discover_phy(uint mii_reg, struct net_device *dev, uint data) +{ + struct fec_priv *priv = (struct fec_priv *)dev->priv; + uint phytype; + + if ((phytype = (mii_reg & 0xffff)) != 0xffff) { + /* Got first part of ID, now get remainder. + */ + priv->phy_id = phytype << 16; + mii_queue(dev, mk_mii_read(MII_REG_PHYIR2), mii_discover_phy3, + 0); + } else { + priv->phy_addr++; + if (priv->phy_addr < 32) + mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), + mii_discover_phy, 0); + else + printk(KERN_ERR "fec: No PHY device found.\n"); + } +} + +static int mpc52xx_netdev_ethtool_ioctl(struct net_device *dev, void *useraddr) +{ + __u32 ethcmd; + + if (copy_from_user(ðcmd, useraddr, sizeof ethcmd)) + return -EFAULT; + + switch (ethcmd) { + + /* Get driver info */ + case ETHTOOL_GDRVINFO:{ + struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; + strncpy(info.driver, "MPC5200 FEC", + sizeof info.driver - 1); + if (copy_to_user(useraddr, &info, sizeof info)) + return -EFAULT; + return 0; + } + /* get message-level */ + case ETHTOOL_GMSGLVL:{ + struct ethtool_value edata = { ETHTOOL_GMSGLVL }; + edata.data = 0; /* XXX */ + if (copy_to_user(useraddr, &edata, sizeof edata)) + return -EFAULT; + return 0; + } + /* set message-level */ + case ETHTOOL_SMSGLVL:{ + struct ethtool_value edata; + if (copy_from_user(&edata, useraddr, sizeof edata)) + return -EFAULT; + return 0; + } + } + return -EOPNOTSUPP; +} + +int fec_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + int retval; + + switch (cmd) { + case SIOCETHTOOL: + retval = mpc52xx_netdev_ethtool_ioctl( + dev, (void *) rq->ifr_data); + break; + + default: + retval = -EOPNOTSUPP; + break; + } + return retval; +} + +void fec_mii_init(struct net_device *dev) +{ + struct fec_priv *priv = (struct fec_priv *)dev->priv; + int i; + + for (i=0; iphy_id_done = 0; + priv->phy_addr = 0; + mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy, 0); + + priv->old_status = 0; +} + +int fec_mii_wait(struct net_device *dev) +{ + struct fec_priv *priv = (struct fec_priv *)dev->priv; + + if (!priv->sequence_done) { + if (!priv->phy) { + printk("KERN_ERR fec_open: PHY not configured\n"); + return -ENODEV; /* No PHY we understand */ + } + + mii_do_cmd(dev, priv->phy->config); + mii_do_cmd(dev, phy_cmd_config); /* display configuration */ + while(!priv->sequence_done) + schedule(); + + mii_do_cmd(dev, priv->phy->startup); + } + return 0; +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Dale Farnsworth"); +MODULE_DESCRIPTION("PHY driver for Motorola MPC52xx FEC"); diff -Nru a/drivers/net/fec_mpc52xx/fec_phy.h b/drivers/net/fec_mpc52xx/fec_phy.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/fec_mpc52xx/fec_phy.h 2004-11-20 23:26:35 +01:00 @@ -0,0 +1,73 @@ +/* + * arch/ppc/52xx_io/fec_phy.h + * + * Driver for the MPC5200 Fast Ethernet Controller + * Based heavily on the MII support for the MPC8xx by Dan Malek + * + * Author: Dale Farnsworth + * + * 2003-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifdef CONFIG_USE_MDIO +#define MII_ADVERTISE_HALF (ADVERTISE_100HALF | ADVERTISE_10HALF | \ + ADVERTISE_CSMA) + +#define MII_ADVERTISE_ALL (ADVERTISE_100FULL | ADVERTISE_10FULL | \ + MII_ADVERTISE_HALF) +#ifdef PHY_INTERRUPT +#define MII_ADVERTISE_DEFAULT MII_ADVERTISE_ALL +#else +#define MII_ADVERTISE_DEFAULT MII_ADVERTISE_HALF +#endif + +#define MII_RCNTL_MODE FEC_RCNTRL_MII_MODE +#define set_phy_speed(fec, s) out_be32(&fec->mii_speed, s) +#define FEC_IMASK_ENABLE 0xf0fe0000 + +typedef struct { + uint mii_data; + void (*funct)(uint mii_reg, struct net_device *dev, uint data); +} phy_cmd_t; + +typedef struct { + uint id; + char *name; + + const phy_cmd_t *config; + const phy_cmd_t *startup; + const phy_cmd_t *ack_int; + const phy_cmd_t *shutdown; +} phy_info_t; + +#else +#define MII_RCNTL_MODE 0 +#define set_phy_speed(fec, s) +#define FEC_IMASK_ENABLE 0xf07e0000 +#define fec_mii_wait(dev) 0 +#define fec_mii(dev) printk(KERN_WARNING "unexpected FEC_IEVENT_MII\n") +#define fec_mii_init(dev) +#endif /* CONFIG_USE_MDIO */ + +/* MII-related definitions */ +#define FEC_MII_DATA_ST 0x40000000 /* Start frame */ +#define FEC_MII_DATA_OP_RD 0x20000000 /* Perform read */ +#define FEC_MII_DATA_OP_WR 0x10000000 /* Perform write */ +#define FEC_MII_DATA_PA_MSK 0x0f800000 /* PHY Address mask */ +#define FEC_MII_DATA_RA_MSK 0x007c0000 /* PHY Register mask */ +#define FEC_MII_DATA_TA 0x00020000 /* Turnaround */ +#define FEC_MII_DATA_DATAMSK 0x00000fff /* PHY data mask */ + +#define FEC_MII_DATA_RA_SHIFT 0x12 /* MII reg addr bits */ +#define FEC_MII_DATA_PA_SHIFT 0x17 /* MII PHY addr bits */ + +#define FEC_MII_SPEED (5 * 2) + +extern void fec_mii_init(struct net_device *dev); +extern int fec_mii_wait(struct net_device *dev); +extern void fec_mii(struct net_device *dev); + +extern int fec_ioctl(struct net_device *, struct ifreq *rq, int cmd); diff -Nru a/include/asm-ppc/ocp_ids.h b/include/asm-ppc/ocp_ids.h --- a/include/asm-ppc/ocp_ids.h 2004-11-11 09:25:52 +01:00 +++ b/include/asm-ppc/ocp_ids.h 2004-11-20 23:26:31 +01:00 @@ -66,6 +66,7 @@ /* Network 0x0200 - 0x02FF */ #define OCP_FUNC_EMAC 0x0200 #define OCP_FUNC_GFAR 0x0201 /* TSEC & FEC */ +#define OCP_FUNC_FEC_MPC52xx 0x0202 /* Bridge devices 0xE00 - 0xEFF */ #define OCP_FUNC_OPB 0x0E00 --=-brzpes5YUe/mdFzWSclR Content-Disposition: attachment; filename="1.2159-fec-fix-frame-reception.patch" Content-Type: text/x-patch; charset="ISO-8859-15"; name="1.2159-fec-fix-frame-reception.patch" Content-Transfer-Encoding: 7bit --- 1.1/drivers/net/fec_mpc52xx/fec.c 2004-11-20 23:26:33 +01:00 +++ 1.2/drivers/net/fec_mpc52xx/fec.c 2005-03-03 16:06:51 +01:00 @@ -175,13 +175,11 @@ skb = dev_alloc_skb(FEC_RX_BUFFER_SIZE); if (skb == 0) goto eagain; - skb->dev = dev; - skb_put(skb, FEC_RX_BUFFER_SIZE); /* zero out the initial receive buffers to aid debugging */ memset(skb->data, 0, FEC_RX_BUFFER_SIZE); data = (void *)virt_to_phys(skb->data); - sdma_submit_buffer(priv->rx_sdma, skb, data, skb->len); + sdma_submit_buffer(priv->rx_sdma, skb, data, FEC_RX_BUFFER_SIZE); } fec_set_paddr(dev, dev->dev_addr); @@ -307,8 +305,7 @@ struct fec_priv *priv = (struct fec_priv *)dev->priv; struct sk_buff *skb; struct sk_buff *rskb; - void *data; - int length; + int status; for (;;) { sdma_clear_irq(priv->rx_sdma); @@ -316,30 +313,44 @@ if (!sdma_buffer_done(priv->rx_sdma)) break; - rskb = sdma_retrieve_buffer(priv->rx_sdma, &length); - /* length included sizeof CRC32 */ - skb_trim(rskb, length - sizeof(u32)); + rskb = sdma_retrieve_buffer(priv->rx_sdma, &status); + + /* Test for errors in received frame */ + if (status & 0x370000) { + /* Drop packet and reuse the buffer */ + sdma_submit_buffer( + priv->rx_sdma, rskb, + (void *)virt_to_phys(rskb->data), + FEC_RX_BUFFER_SIZE ); + + priv->stats.rx_dropped++; + + continue; + } /* allocate replacement skb */ skb = dev_alloc_skb(FEC_RX_BUFFER_SIZE); if (skb) { + /* Process the received skb */ + int length = (status & ((1<<11) - 1)) - sizeof(u32); + skb_put(rskb, length); /* length included CRC32 */ + + rskb->dev = dev; rskb->protocol = eth_type_trans(rskb, dev); netif_rx(rskb); dev->last_rx = jiffies; } else { + /* Can't get a new one : reuse the same & drop pkt */ printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name); priv->stats.rx_dropped++; - skb_trim(rskb, 0); skb = rskb; } - skb->dev = dev; - skb_put(skb, FEC_RX_BUFFER_SIZE); - data = (void *)virt_to_phys(skb->data); - sdma_submit_buffer(priv->rx_sdma, skb, data, skb->len); + sdma_submit_buffer( priv->rx_sdma, skb, + (void *)virt_to_phys(skb->data), FEC_RX_BUFFER_SIZE ); } return IRQ_HANDLED; --=-brzpes5YUe/mdFzWSclR Content-Disposition: attachment; filename="1.2160-fix-typo-bestcomm-fec.patch" Content-Type: text/x-patch; charset="ISO-8859-15"; name="1.2160-fix-typo-bestcomm-fec.patch" Content-Transfer-Encoding: 7bit --- a/arch/ppc/syslib/bestcomm/fec.c 2004-11-20 23:21:03 +01:00 +++ b/arch/ppc/syslib/bestcomm/fec.c 2005-03-22 11:21:01 +01:00 @@ -155,7 +155,7 @@ /* clear pending interrupt bits */ out_be32(&sdma->IntPend, 1<ipr[SDMA_INITIATOR_FEC_TX], SDMA_IPR_FEC_RX); + out_8(&sdma->ipr[SDMA_INITIATOR_FEC_TX], SDMA_IPR_FEC_TX); return tasknum; } --=-brzpes5YUe/mdFzWSclR Content-Disposition: attachment; filename="1.2161-update-fec-microcode.patch" Content-Type: text/x-patch; charset="ISO-8859-15"; name="1.2161-update-fec-microcode.patch" Content-Transfer-Encoding: 7bit diff -Nru a/arch/ppc/syslib/bestcomm/fec.c b/arch/ppc/syslib/bestcomm/fec.c --- a/arch/ppc/syslib/bestcomm/fec.c 2005-03-22 11:21:01 +01:00 +++ b/arch/ppc/syslib/bestcomm/fec.c 2005-03-22 12:59:04 +01:00 @@ -68,6 +68,7 @@ inc = (struct sdma_fec_rx_inc *)sdma_task_inc(tasknum); inc->incr_bytes = -(s16)sizeof(u32); inc->incr_dst = sizeof(u32); + inc->incr_dst_ma = sizeof(u8); sdma_set_task_pragma(tasknum, SDMA_FEC_RX_BD_PRAGMA); sdma_set_task_auto_start(tasknum, tasknum); diff -Nru a/arch/ppc/syslib/bestcomm/fec.h b/arch/ppc/syslib/bestcomm/fec.h --- a/arch/ppc/syslib/bestcomm/fec.h 2004-11-20 23:21:04 +01:00 +++ b/arch/ppc/syslib/bestcomm/fec.h 2005-03-22 12:59:04 +01:00 @@ -31,6 +31,8 @@ s16 incr_bytes; u16 pad1; s16 incr_dst; + u16 pad2; + s16 incr_dst_ma; }; /* tx task vars that need to be set before enabling the task */ diff -Nru a/arch/ppc/syslib/bestcomm/sdma_fec_rx_task.c b/arch/ppc/syslib/bestcomm/sdma_fec_rx_task.c --- a/arch/ppc/syslib/bestcomm/sdma_fec_rx_task.c 2004-11-20 23:21:05 +01:00 +++ b/arch/ppc/syslib/bestcomm/sdma_fec_rx_task.c 2005-03-22 12:59:04 +01:00 @@ -1,8 +1,8 @@ /* * sdma_fec_rx_task.c * - * Automatically created by split_dma_image based on dma_image.hex - * on Wed Sep 8 00:37:22 2004 GMT + * Automatically created based on BestCommAPI-2.2/code_dma/image_rtos1/dma_image.hex + * on Tue Mar 22 11:19:38 2005 GMT */ #include @@ -22,31 +22,50 @@ uint32_t sdma_fec_rx_task[] = { /* header */ 0x4243544b, - 0x0c020409, + 0x18060709, 0x00000000, 0x00000000, /* Task descriptors */ - 0x808220da, /* LCD: idx0 = var1, idx1 = var4; idx1 <= var3; idx0 += inc3, idx1 += inc2 */ + 0x808220e3, /* LCD: idx0 = var1, idx1 = var4; idx1 <= var3; idx0 += inc4, idx1 += inc3 */ 0x10601010, /* DRD1A: var4 = var2; FN=0 MORE init=3 WS=0 RS=0 */ - 0xb880025b, /* LCD: idx2 = *idx1, idx3 = var0; idx2 < var9; idx2 += inc3, idx3 += inc3 */ + 0xb8800264, /* LCD: idx2 = *idx1, idx3 = var0; idx2 < var9; idx2 += inc4, idx3 += inc4 */ 0x10001308, /* DRD1A: var4 = idx1; FN=0 MORE init=0 WS=0 RS=0 */ 0x60140002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=2 RS=2 */ 0x0cccfcca, /* DRD2B1: *idx3 = EU3(); EU3(*idx3,var10) */ - 0xd9190240, /* LCDEXT: idx2 = idx2; idx2 > var9; idx2 += inc0 */ - 0xb8c5e009, /* LCD: idx3 = *(idx1 + var00000015); ; idx3 += inc1 */ - 0x047ecf80, /* DRD1A: *idx3 = *idx0; FN=0 INT init=3 WS=3 RS=3 */ - 0x98190024, /* LCD: idx2 = idx0; idx2 once var0; idx2 += inc4 */ + 0x80004000, /* LCDEXT: idx2 = 0x00000000; ; */ + 0xb8c58029, /* LCD: idx3 = *(idx1 + var00000015); idx3 once var0; idx3 += inc5 */ + 0x60000002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=0 RS=0 */ + 0x088cf8cc, /* DRD2B1: idx2 = EU3(); EU3(idx3,var12) */ + 0x991982f2, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var11; idx2 += inc6, idx3 += inc2 */ + 0x006acf80, /* DRD1A: *idx3 = *idx0; FN=0 init=3 WS=1 RS=1 */ + 0x80004000, /* LCDEXT: idx2 = 0x00000000; ; */ + 0x9999802d, /* LCD: idx3 = idx3; idx3 once var0; idx3 += inc5 */ + 0x70000002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT MORE init=0 WS=0 RS=0 */ + 0x034cfc4e, /* DRD2B1: var13 = EU3(); EU3(*idx1,var14) */ + 0x00008868, /* DRD1A: idx2 = var13; FN=0 init=0 WS=0 RS=0 */ + 0x99198341, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var13; idx2 += inc0, idx3 += inc1 */ + 0x007ecf80, /* DRD1A: *idx3 = *idx0; FN=0 init=3 WS=3 RS=3 */ + 0x99198272, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var9; idx2 += inc6, idx3 += inc2 */ + 0x046acf80, /* DRD1A: *idx3 = *idx0; FN=0 INT init=3 WS=1 RS=1 */ + 0x9819002d, /* LCD: idx2 = idx0; idx2 once var0; idx2 += inc5 */ 0x0060c790, /* DRD1A: *idx1 = *idx2; FN=0 init=3 WS=0 RS=0 */ 0x000001f8, /* NOP */ - /* VAR[9]-VAR[10] */ + /* VAR[9]-VAR[14] */ 0x40000000, 0x7fff7fff, + 0x00000000, + 0x00000003, + 0x40000008, + 0x43ffffff, - /* INC[0]-INC[3] */ + /* INC[0]-INC[6] */ 0x40000000, 0xe0000000, + 0xe0000000, 0xa0000008, 0x20000000, + 0x00000000, + 0x4000ffff, }; diff -Nru a/arch/ppc/syslib/bestcomm/sdma_fec_tx_task.c b/arch/ppc/syslib/bestcomm/sdma_fec_tx_task.c --- a/arch/ppc/syslib/bestcomm/sdma_fec_tx_task.c 2004-11-20 23:21:06 +01:00 +++ b/arch/ppc/syslib/bestcomm/sdma_fec_tx_task.c 2005-03-22 12:59:04 +01:00 @@ -1,8 +1,8 @@ /* * sdma_fec_tx_task.c * - * Automatically created by split_dma_image based on dma_image.hex - * on Wed Sep 8 00:37:22 2004 GMT + * Automatically created based on BestCommAPI-2.2/code_dma/image_rtos1/dma_image.hex + * on Tue Mar 22 11:19:29 2005 GMT */ #include @@ -22,46 +22,56 @@ uint32_t sdma_fec_tx_task[] = { /* header */ 0x4243544b, - 0x1b06070c, + 0x2407070d, 0x00000000, 0x00000000, /* Task descriptors */ 0x8018001b, /* LCD: idx0 = var0; idx0 <= var0; idx0 += inc3 */ 0x60000005, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=5 EXT init=0 WS=0 RS=0 */ - 0x01ccfc0c, /* DRD2B1: var7 = EU3(); EU3(*idx0,var12) */ + 0x01ccfc0d, /* DRD2B1: var7 = EU3(); EU3(*idx0,var13) */ 0x8082a123, /* LCD: idx0 = var1, idx1 = var5; idx1 <= var4; idx0 += inc4, idx1 += inc3 */ 0x10801418, /* DRD1A: var5 = var3; FN=0 MORE init=4 WS=0 RS=0 */ - 0xf8810364, /* LCDEXT: idx2 = *idx1, idx3 = var2; idx2 < var13; idx2 += inc4, idx3 += inc4 */ + 0xf88103a4, /* LCDEXT: idx2 = *idx1, idx3 = var2; idx2 < var14; idx2 += inc4, idx3 += inc4 */ 0x801a6024, /* LCD: idx4 = var0; ; idx4 += inc4 */ 0x10001708, /* DRD1A: var5 = idx1; FN=0 MORE init=0 WS=0 RS=0 */ 0x60140002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=2 RS=2 */ - 0x0cccfcce, /* DRD2B1: *idx3 = EU3(); EU3(*idx3,var14) */ + 0x0cccfccf, /* DRD2B1: *idx3 = EU3(); EU3(*idx3,var15) */ 0x991a002c, /* LCD: idx2 = idx2, idx3 = idx4; idx2 once var0; idx2 += inc5, idx3 += inc4 */ - 0x60000002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT init=0 WS=0 RS=0 */ - 0x088cfc4f, /* DRD2B1: idx2 = EU3(); EU3(*idx1,var15) */ - 0x9919802c, /* LCD: idx2 = idx2, idx3 = idx3; idx2 once var0; idx2 += inc5, idx3 += inc4 */ 0x70000002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT MORE init=0 WS=0 RS=0 */ - 0x024cfc4c, /* DRD2B1: var9 = EU3(); EU3(*idx1,var12) */ + 0x024cfc4d, /* DRD2B1: var9 = EU3(); EU3(*idx1,var13) */ 0x60000003, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=3 EXT init=0 WS=0 RS=0 */ 0x0cccf247, /* DRD2B1: *idx3 = EU3(); EU3(var9,var7) */ - 0xd9190440, /* LCDEXT: idx2 = idx2; idx2 > var17; idx2 += inc0 */ - 0xb8c86009, /* LCD: idx3 = *(idx1 + var0000001a); ; idx3 += inc1 */ + 0x80004000, /* LCDEXT: idx2 = 0x00000000; ; */ + 0xb8c80029, /* LCD: idx3 = *(idx1 + var0000001a); idx3 once var0; idx3 += inc5 */ + 0x70000002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT MORE init=0 WS=0 RS=0 */ + 0x088cf8d1, /* DRD2B1: idx2 = EU3(); EU3(idx3,var17) */ + 0x00002f10, /* DRD1A: var11 = idx2; FN=0 init=0 WS=0 RS=0 */ + 0x99198432, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var16; idx2 += inc6, idx3 += inc2 */ + 0x008ac398, /* DRD1A: *idx0 = *idx3; FN=0 init=4 WS=1 RS=1 */ + 0x80004000, /* LCDEXT: idx2 = 0x00000000; ; */ + 0x9999802d, /* LCD: idx3 = idx3; idx3 once var0; idx3 += inc5 */ + 0x70000002, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=2 EXT MORE init=0 WS=0 RS=0 */ + 0x048cfc53, /* DRD2B1: var18 = EU3(); EU3(*idx1,var19) */ + 0x60000008, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=8 EXT init=0 WS=0 RS=0 */ + 0x088cf48b, /* DRD2B1: idx2 = EU3(); EU3(var18,var11) */ + 0x99198481, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var18; idx2 += inc0, idx3 += inc1 */ 0x009ec398, /* DRD1A: *idx0 = *idx3; FN=0 init=4 WS=3 RS=3 */ - 0x99198372, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var13; idx2 += inc6, idx3 += inc2 */ + 0x991983b2, /* LCD: idx2 = idx2, idx3 = idx3; idx2 > var14; idx2 += inc6, idx3 += inc2 */ 0x088ac398, /* DRD1A: *idx0 = *idx3; FN=0 TFD init=4 WS=1 RS=1 */ 0x9919002d, /* LCD: idx2 = idx2; idx2 once var0; idx2 += inc5 */ 0x60000005, /* DRD2A: EU0=0 EU1=0 EU2=0 EU3=5 EXT init=0 WS=0 RS=0 */ - 0x0c4cf88d, /* DRD2B1: *idx1 = EU3(); EU3(idx2,var13) */ + 0x0c4cf88e, /* DRD2B1: *idx1 = EU3(); EU3(idx2,var14) */ 0x000001f8, /* NOP */ - /* VAR[12]-VAR[17] */ + /* VAR[13]-VAR[19] */ 0x0c000000, 0x40000000, 0x7fff7fff, - 0x43ffffff, 0x00000000, + 0x00000003, 0x40000004, + 0x43ffffff, /* INC[0]-INC[6] */ 0x40000000, diff -Nru a/drivers/net/fec_mpc52xx/fec.c b/drivers/net/fec_mpc52xx/fec.c --- a/drivers/net/fec_mpc52xx/fec.c 2005-03-03 16:06:51 +01:00 +++ b/drivers/net/fec_mpc52xx/fec.c 2005-03-22 12:59:04 +01:00 @@ -202,41 +202,6 @@ return -EAGAIN; } -/* The BestComm hardware requires data to be 32-bit aligned. - * We also pad to minimum ethernet packet length, ETH_ZLEN. - */ -static inline struct sk_buff *fec_skb_align_and_pad(struct sk_buff *skb) -{ - void *data = skb->data; - int len = skb->len; - int pad = (int)data & 0x3; - struct sk_buff *nskb; - int nlen; - - if (pad == 0) - return skb; - - if (!skb_cloned(skb)) { - skb_push(skb, pad); - memmove(skb->data, data, len); - skb_trim(skb, len); - skb = skb_padto(skb, ETH_ZLEN); - return skb; - } - - /* ensure skb_padto doesn't have to reallocate */ - nlen = (len >= ETH_ZLEN) ? len : ETH_ZLEN; - - nskb = alloc_skb(nlen, GFP_ATOMIC); - if (nskb) { - skb_put(nskb, len); - memcpy(nskb->data, data, len); - nskb = skb_padto(nskb, ETH_ZLEN); - } - kfree_skb(skb); - return nskb; -} - /* This will only be invoked if your driver is _not_ in XOFF state. * What this means is that you need not check it, and that this * invariant will hold if you make sure that the netif_*_queue() @@ -250,12 +215,6 @@ if (sdma_queue_full(priv->tx_sdma)) panic("MPC52xx transmit queue overrun\n"); - skb = fec_skb_align_and_pad(skb); - if (!skb) { - priv->stats.tx_dropped++; - return 0; - } - spin_lock_irq(&priv->lock); dev->trans_start = jiffies; @@ -639,7 +598,7 @@ ret = -ENOMEM; goto probe_error; } - + /* SDMA init */ priv->rx_sdma = sdma_alloc(FEC_RX_NUM_BD); priv->tx_sdma = sdma_alloc(FEC_TX_NUM_BD); --=-brzpes5YUe/mdFzWSclR--