Commit 37448f7d authored by Eugene Surovegin's avatar Eugene Surovegin Committed by Jeff Garzik

[PATCH] New PowerPC 4xx on-chip ethernet controller driver

This patch replaces current PowerPC 4xx EMAC driver with
new, re-written from the scratch version. This patch is quite big
(~234K) because there is virtualy 0% of common code between old and
new version.

New driver uses NAPI, it solves stability problems under heavy packet
load and low memory, corrects chip register access and fixes numerous
small bugs I don't even remember now.

This patch has been tested on all supported in 2.6 PPC 4xx boards.
It's been used in production for almost a year now on custom
4xx hardware. PPC32 specific parts are already upstream.

Patch was acked by the current EMAC driver maintainer (Matt Porter). I
will be maintaining this new version.
Signed-off-by: default avatarEugene Surovegin <ebs@ebshome.net>
--

 Kconfig                   |   72
 ibm_emac/Makefile         |   13
 ibm_emac/ibm_emac.h       |  418 +++--
 ibm_emac/ibm_emac_core.c  | 3414 ++++++++++++++++++++++++----------------------
 ibm_emac/ibm_emac_core.h  |  313 ++--
 ibm_emac/ibm_emac_debug.c |  377 ++---
 ibm_emac/ibm_emac_debug.h |   63
 ibm_emac/ibm_emac_mal.c   |  674 +++++----
 ibm_emac/ibm_emac_mal.h   |  336 +++-
 ibm_emac/ibm_emac_phy.c   |  335 ++--
 ibm_emac/ibm_emac_phy.h   |  105 -
 ibm_emac/ibm_emac_rgmii.c |  201 ++
 ibm_emac/ibm_emac_rgmii.h |   68
 ibm_emac/ibm_emac_tah.c   |  111 +
 ibm_emac/ibm_emac_tah.h   |   96 -
 ibm_emac/ibm_emac_zmii.c  |  255 +++
 ibm_emac/ibm_emac_zmii.h  |  114 -
 17 files changed, 4114 insertions(+), 2851 deletions(-)
Signed-off-by: default avatarJeff Garzik <jgarzik@pobox.com>
parent b71b95ef
...@@ -1163,38 +1163,74 @@ config IBMVETH ...@@ -1163,38 +1163,74 @@ config IBMVETH
be called ibmveth. be called ibmveth.
config IBM_EMAC config IBM_EMAC
bool "IBM PPC4xx EMAC driver support" tristate "PowerPC 4xx on-chip Ethernet support"
depends on 4xx depends on 4xx
select CRC32 help
---help--- This driver supports the PowerPC 4xx EMAC family of on-chip
This driver supports the IBM PPC4xx EMAC family of on-chip Ethernet controllers.
Ethernet controllers.
config IBM_EMAC_ERRMSG
bool "Verbose error messages"
depends on IBM_EMAC && BROKEN
config IBM_EMAC_RXB config IBM_EMAC_RXB
int "Number of receive buffers" int "Number of receive buffers"
depends on IBM_EMAC depends on IBM_EMAC
default "128" if IBM_EMAC4 default "128"
default "64"
config IBM_EMAC_TXB config IBM_EMAC_TXB
int "Number of transmit buffers" int "Number of transmit buffers"
depends on IBM_EMAC depends on IBM_EMAC
default "128" if IBM_EMAC4 default "64"
default "8"
config IBM_EMAC_POLL_WEIGHT
int "MAL NAPI polling weight"
depends on IBM_EMAC
default "32"
config IBM_EMAC_FGAP config IBM_EMAC_RX_COPY_THRESHOLD
int "Frame gap" int "RX skb copy threshold (bytes)"
depends on IBM_EMAC depends on IBM_EMAC
default "8" default "256"
config IBM_EMAC_SKBRES config IBM_EMAC_RX_SKB_HEADROOM
int "Skb reserve amount" int "Additional RX skb headroom (bytes)"
depends on IBM_EMAC depends on IBM_EMAC
default "0" default "0"
help
Additional receive skb headroom. Note, that driver
will always reserve at least 2 bytes to make IP header
aligned, so usualy there is no need to add any additional
headroom.
If unsure, set to 0.
config IBM_EMAC_PHY_RX_CLK_FIX
bool "PHY Rx clock workaround"
depends on IBM_EMAC && (405EP || 440GX || 440EP)
help
Enable this if EMAC attached to a PHY which doesn't generate
RX clock if there is no link, if this is the case, you will
see "TX disable timeout" or "RX disable timeout" in the system
log.
If unsure, say N.
config IBM_EMAC_DEBUG
bool "Debugging"
depends on IBM_EMAC
default n
config IBM_EMAC_ZMII
bool
depends on IBM_EMAC && (NP405H || NP405L || 44x)
default y
config IBM_EMAC_RGMII
bool
depends on IBM_EMAC && 440GX
default y
config IBM_EMAC_TAH
bool
depends on IBM_EMAC && 440GX
default y
config NET_PCI config NET_PCI
bool "EISA, VLB, PCI and on board controllers" bool "EISA, VLB, PCI and on board controllers"
......
# #
# Makefile for the IBM PPC4xx EMAC controllers # Makefile for the PowerPC 4xx on-chip ethernet driver
# #
obj-$(CONFIG_IBM_EMAC) += ibm_emac.o obj-$(CONFIG_IBM_EMAC) += ibm_emac.o
ibm_emac-objs := ibm_emac_mal.o ibm_emac_core.o ibm_emac_phy.o ibm_emac-objs := ibm_emac_mal.o ibm_emac_core.o ibm_emac_phy.o
ibm_emac-$(CONFIG_IBM_EMAC_ZMII) += ibm_emac_zmii.o
# Only need this if you want to see additional debug messages ibm_emac-$(CONFIG_IBM_EMAC_RGMII) += ibm_emac_rgmii.o
ifeq ($(CONFIG_IBM_EMAC_ERRMSG), y) ibm_emac-$(CONFIG_IBM_EMAC_TAH) += ibm_emac_tah.o
ibm_emac-objs += ibm_emac_debug.o ibm_emac-$(CONFIG_IBM_EMAC_DEBUG) += ibm_emac_debug.o
endif
/* /*
* ibm_emac.h * drivers/net/ibm_emac/ibm_emac.h
* *
* Register definitions for PowerPC 4xx on-chip ethernet contoller
* *
* Armin Kuster akuster@mvista.com * Copyright (c) 2004, 2005 Zultys Technologies.
* June, 2002 * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
* *
* Copyright 2002 MontaVista Softare Inc. * Based on original work by
* Matt Porter <mporter@kernel.crashing.org>
* Armin Kuster <akuster@mvista.com>
* Copyright 2002-2004 MontaVista Software Inc.
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the * under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your * Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. * option) any later version.
*
*/ */
#ifndef __IBM_EMAC_H_
#define __IBM_EMAC_H_
#include <linux/config.h>
#include <linux/types.h>
/* This is a simple check to prevent use of this driver on non-tested SoCs */
#if !defined(CONFIG_405GP) && !defined(CONFIG_405GPR) && !defined(CONFIG_405EP) && \
!defined(CONFIG_440GP) && !defined(CONFIG_440GX) && !defined(CONFIG_440SP) && \
!defined(CONFIG_440EP) && !defined(CONFIG_NP405H)
#error "Unknown SoC. Please, check chip user manual and make sure EMAC defines are OK"
#endif
/* EMAC registers Write Access rules */
struct emac_regs {
u32 mr0; /* special */
u32 mr1; /* Reset */
u32 tmr0; /* special */
u32 tmr1; /* special */
u32 rmr; /* Reset */
u32 isr; /* Always */
u32 iser; /* Reset */
u32 iahr; /* Reset, R, T */
u32 ialr; /* Reset, R, T */
u32 vtpid; /* Reset, R, T */
u32 vtci; /* Reset, R, T */
u32 ptr; /* Reset, T */
u32 iaht1; /* Reset, R */
u32 iaht2; /* Reset, R */
u32 iaht3; /* Reset, R */
u32 iaht4; /* Reset, R */
u32 gaht1; /* Reset, R */
u32 gaht2; /* Reset, R */
u32 gaht3; /* Reset, R */
u32 gaht4; /* Reset, R */
u32 lsah;
u32 lsal;
u32 ipgvr; /* Reset, T */
u32 stacr; /* special */
u32 trtr; /* special */
u32 rwmr; /* Reset */
u32 octx;
u32 ocrx;
u32 ipcr;
};
#if !defined(CONFIG_IBM_EMAC4)
#define EMAC_ETHTOOL_REGS_VER 0
#define EMAC_ETHTOOL_REGS_SIZE (sizeof(struct emac_regs) - sizeof(u32))
#else
#define EMAC_ETHTOOL_REGS_VER 1
#define EMAC_ETHTOOL_REGS_SIZE sizeof(struct emac_regs)
#endif
#ifndef _IBM_EMAC_H_ /* EMACx_MR0 */
#define _IBM_EMAC_H_ #define EMAC_MR0_RXI 0x80000000
/* General defines needed for the driver */ #define EMAC_MR0_TXI 0x40000000
#define EMAC_MR0_SRST 0x20000000
#define EMAC_MR0_TXE 0x10000000
#define EMAC_MR0_RXE 0x08000000
#define EMAC_MR0_WKE 0x04000000
/* Emac */ /* EMACx_MR1 */
typedef struct emac_regs { #define EMAC_MR1_FDE 0x80000000
u32 em0mr0; #define EMAC_MR1_ILE 0x40000000
u32 em0mr1; #define EMAC_MR1_VLE 0x20000000
u32 em0tmr0; #define EMAC_MR1_EIFC 0x10000000
u32 em0tmr1; #define EMAC_MR1_APP 0x08000000
u32 em0rmr; #define EMAC_MR1_IST 0x01000000
u32 em0isr;
u32 em0iser;
u32 em0iahr;
u32 em0ialr;
u32 em0vtpid;
u32 em0vtci;
u32 em0ptr;
u32 em0iaht1;
u32 em0iaht2;
u32 em0iaht3;
u32 em0iaht4;
u32 em0gaht1;
u32 em0gaht2;
u32 em0gaht3;
u32 em0gaht4;
u32 em0lsah;
u32 em0lsal;
u32 em0ipgvr;
u32 em0stacr;
u32 em0trtr;
u32 em0rwmr;
} emac_t;
/* MODE REG 0 */ #define EMAC_MR1_MF_MASK 0x00c00000
#define EMAC_M0_RXI 0x80000000 #define EMAC_MR1_MF_10 0x00000000
#define EMAC_M0_TXI 0x40000000 #define EMAC_MR1_MF_100 0x00400000
#define EMAC_M0_SRST 0x20000000 #if !defined(CONFIG_IBM_EMAC4)
#define EMAC_M0_TXE 0x10000000 #define EMAC_MR1_MF_1000 0x00000000
#define EMAC_M0_RXE 0x08000000 #define EMAC_MR1_MF_1000GPCS 0x00000000
#define EMAC_M0_WKE 0x04000000 #define EMAC_MR1_MF_IPPA(id) 0x00000000
#else
#define EMAC_MR1_MF_1000 0x00800000
#define EMAC_MR1_MF_1000GPCS 0x00c00000
#define EMAC_MR1_MF_IPPA(id) (((id) & 0x1f) << 6)
#endif
/* MODE Reg 1 */ #define EMAC_TX_FIFO_SIZE 2048
#define EMAC_M1_FDE 0x80000000
#define EMAC_M1_ILE 0x40000000
#define EMAC_M1_VLE 0x20000000
#define EMAC_M1_EIFC 0x10000000
#define EMAC_M1_APP 0x08000000
#define EMAC_M1_AEMI 0x02000000
#define EMAC_M1_IST 0x01000000
#define EMAC_M1_MF_1000GPCS 0x00c00000 /* Internal GPCS */
#define EMAC_M1_MF_1000MBPS 0x00800000 /* External GPCS */
#define EMAC_M1_MF_100MBPS 0x00400000
#define EMAC_M1_RFS_16K 0x00280000 /* 000 for 512 byte */
#define EMAC_M1_TR 0x00008000
#ifdef CONFIG_IBM_EMAC4
#define EMAC_M1_RFS_8K 0x00200000
#define EMAC_M1_RFS_4K 0x00180000
#define EMAC_M1_RFS_2K 0x00100000
#define EMAC_M1_RFS_1K 0x00080000
#define EMAC_M1_TX_FIFO_16K 0x00050000 /* 0's for 512 byte */
#define EMAC_M1_TX_FIFO_8K 0x00040000
#define EMAC_M1_TX_FIFO_4K 0x00030000
#define EMAC_M1_TX_FIFO_2K 0x00020000
#define EMAC_M1_TX_FIFO_1K 0x00010000
#define EMAC_M1_TX_TR 0x00008000
#define EMAC_M1_TX_MWSW 0x00001000 /* 0 wait for status */
#define EMAC_M1_JUMBO_ENABLE 0x00000800 /* Upt to 9Kr status */
#define EMAC_M1_OPB_CLK_66 0x00000008 /* 66Mhz */
#define EMAC_M1_OPB_CLK_83 0x00000010 /* 83Mhz */
#define EMAC_M1_OPB_CLK_100 0x00000018 /* 100Mhz */
#define EMAC_M1_OPB_CLK_100P 0x00000020 /* 100Mhz+ */
#else /* CONFIG_IBM_EMAC4 */
#define EMAC_M1_RFS_4K 0x00300000 /* ~4k for 512 byte */
#define EMAC_M1_RFS_2K 0x00200000
#define EMAC_M1_RFS_1K 0x00100000
#define EMAC_M1_TX_FIFO_2K 0x00080000 /* 0's for 512 byte */
#define EMAC_M1_TX_FIFO_1K 0x00040000
#define EMAC_M1_TR0_DEPEND 0x00010000 /* 0'x for single packet */
#define EMAC_M1_TR1_DEPEND 0x00004000
#define EMAC_M1_TR1_MULTI 0x00002000
#define EMAC_M1_JUMBO_ENABLE 0x00001000
#endif /* CONFIG_IBM_EMAC4 */
#define EMAC_M1_BASE (EMAC_M1_TX_FIFO_2K | \
EMAC_M1_APP | \
EMAC_M1_TR | EMAC_M1_VLE)
/* Transmit Mode Register 0 */ #if !defined(CONFIG_IBM_EMAC4)
#define EMAC_TMR0_GNP0 0x80000000 #define EMAC_MR1_RFS_4K 0x00300000
#define EMAC_TMR0_GNP1 0x40000000 #define EMAC_MR1_RFS_16K 0x00000000
#define EMAC_TMR0_GNPD 0x20000000 #define EMAC_RX_FIFO_SIZE(gige) 4096
#define EMAC_TMR0_FC 0x10000000 #define EMAC_MR1_TFS_2K 0x00080000
#define EMAC_MR1_TR0_MULT 0x00008000
#define EMAC_MR1_JPSM 0x00000000
#define EMAC_MR1_BASE(opb) (EMAC_MR1_TFS_2K | EMAC_MR1_TR0_MULT)
#else
#define EMAC_MR1_RFS_4K 0x00180000
#define EMAC_MR1_RFS_16K 0x00280000
#define EMAC_RX_FIFO_SIZE(gige) ((gige) ? 16384 : 4096)
#define EMAC_MR1_TFS_2K 0x00020000
#define EMAC_MR1_TR 0x00008000
#define EMAC_MR1_MWSW_001 0x00001000
#define EMAC_MR1_JPSM 0x00000800
#define EMAC_MR1_OBCI_MASK 0x00000038
#define EMAC_MR1_OBCI_50 0x00000000
#define EMAC_MR1_OBCI_66 0x00000008
#define EMAC_MR1_OBCI_83 0x00000010
#define EMAC_MR1_OBCI_100 0x00000018
#define EMAC_MR1_OBCI_100P 0x00000020
#define EMAC_MR1_OBCI(freq) ((freq) <= 50 ? EMAC_MR1_OBCI_50 : \
(freq) <= 66 ? EMAC_MR1_OBCI_66 : \
(freq) <= 83 ? EMAC_MR1_OBCI_83 : \
(freq) <= 100 ? EMAC_MR1_OBCI_100 : EMAC_MR1_OBCI_100P)
#define EMAC_MR1_BASE(opb) (EMAC_MR1_TFS_2K | EMAC_MR1_TR | \
EMAC_MR1_MWSW_001 | EMAC_MR1_OBCI(opb))
#endif
/* EMACx_TMR0 */
#define EMAC_TMR0_GNP 0x80000000
#if !defined(CONFIG_IBM_EMAC4)
#define EMAC_TMR0_DEFAULT 0x00000000
#else
#define EMAC_TMR0_TFAE_2_32 0x00000001 #define EMAC_TMR0_TFAE_2_32 0x00000001
#define EMAC_TMR0_TFAE_4_64 0x00000002 #define EMAC_TMR0_TFAE_4_64 0x00000002
#define EMAC_TMR0_TFAE_8_128 0x00000003 #define EMAC_TMR0_TFAE_8_128 0x00000003
...@@ -112,14 +144,36 @@ typedef struct emac_regs { ...@@ -112,14 +144,36 @@ typedef struct emac_regs {
#define EMAC_TMR0_TFAE_32_512 0x00000005 #define EMAC_TMR0_TFAE_32_512 0x00000005
#define EMAC_TMR0_TFAE_64_1024 0x00000006 #define EMAC_TMR0_TFAE_64_1024 0x00000006
#define EMAC_TMR0_TFAE_128_2048 0x00000007 #define EMAC_TMR0_TFAE_128_2048 0x00000007
#define EMAC_TMR0_DEFAULT EMAC_TMR0_TFAE_2_32
#endif
#define EMAC_TMR0_XMIT (EMAC_TMR0_GNP | EMAC_TMR0_DEFAULT)
/* EMACx_TMR1 */
/* IBM manuals are not very clear here.
* This is my interpretation of how things are. --ebs
*/
#if defined(CONFIG_40x)
#define EMAC_FIFO_ENTRY_SIZE 8
#define EMAC_MAL_BURST_SIZE (16 * 4)
#else
#define EMAC_FIFO_ENTRY_SIZE 16
#define EMAC_MAL_BURST_SIZE (64 * 4)
#endif
#if !defined(CONFIG_IBM_EMAC4)
#define EMAC_TMR1(l,h) (((l) << 27) | (((h) & 0xff) << 16))
#else
#define EMAC_TMR1(l,h) (((l) << 27) | (((h) & 0x3ff) << 14))
#endif
/* Receive Mode Register */ /* EMACx_RMR */
#define EMAC_RMR_SP 0x80000000 #define EMAC_RMR_SP 0x80000000
#define EMAC_RMR_SFCS 0x40000000 #define EMAC_RMR_SFCS 0x40000000
#define EMAC_RMR_ARRP 0x20000000 #define EMAC_RMR_RRP 0x20000000
#define EMAC_RMR_ARP 0x10000000 #define EMAC_RMR_RFP 0x10000000
#define EMAC_RMR_AROP 0x08000000 #define EMAC_RMR_ROP 0x08000000
#define EMAC_RMR_ARPI 0x04000000 #define EMAC_RMR_RPIR 0x04000000
#define EMAC_RMR_PPP 0x02000000 #define EMAC_RMR_PPP 0x02000000
#define EMAC_RMR_PME 0x01000000 #define EMAC_RMR_PME 0x01000000
#define EMAC_RMR_PMME 0x00800000 #define EMAC_RMR_PMME 0x00800000
...@@ -127,6 +181,9 @@ typedef struct emac_regs { ...@@ -127,6 +181,9 @@ typedef struct emac_regs {
#define EMAC_RMR_MIAE 0x00200000 #define EMAC_RMR_MIAE 0x00200000
#define EMAC_RMR_BAE 0x00100000 #define EMAC_RMR_BAE 0x00100000
#define EMAC_RMR_MAE 0x00080000 #define EMAC_RMR_MAE 0x00080000
#if !defined(CONFIG_IBM_EMAC4)
#define EMAC_RMR_BASE 0x00000000
#else
#define EMAC_RMR_RFAF_2_32 0x00000001 #define EMAC_RMR_RFAF_2_32 0x00000001
#define EMAC_RMR_RFAF_4_64 0x00000002 #define EMAC_RMR_RFAF_4_64 0x00000002
#define EMAC_RMR_RFAF_8_128 0x00000003 #define EMAC_RMR_RFAF_8_128 0x00000003
...@@ -134,9 +191,21 @@ typedef struct emac_regs { ...@@ -134,9 +191,21 @@ typedef struct emac_regs {
#define EMAC_RMR_RFAF_32_512 0x00000005 #define EMAC_RMR_RFAF_32_512 0x00000005
#define EMAC_RMR_RFAF_64_1024 0x00000006 #define EMAC_RMR_RFAF_64_1024 0x00000006
#define EMAC_RMR_RFAF_128_2048 0x00000007 #define EMAC_RMR_RFAF_128_2048 0x00000007
#define EMAC_RMR_BASE (EMAC_RMR_IAE | EMAC_RMR_BAE) #define EMAC_RMR_BASE EMAC_RMR_RFAF_128_2048
#endif
/* Interrupt Status & enable Regs */ /* EMACx_ISR & EMACx_ISER */
#if !defined(CONFIG_IBM_EMAC4)
#define EMAC_ISR_TXPE 0x00000000
#define EMAC_ISR_RXPE 0x00000000
#define EMAC_ISR_TXUE 0x00000000
#define EMAC_ISR_RXOE 0x00000000
#else
#define EMAC_ISR_TXPE 0x20000000
#define EMAC_ISR_RXPE 0x10000000
#define EMAC_ISR_TXUE 0x08000000
#define EMAC_ISR_RXOE 0x04000000
#endif
#define EMAC_ISR_OVR 0x02000000 #define EMAC_ISR_OVR 0x02000000
#define EMAC_ISR_PP 0x01000000 #define EMAC_ISR_PP 0x01000000
#define EMAC_ISR_BP 0x00800000 #define EMAC_ISR_BP 0x00800000
...@@ -147,53 +216,62 @@ typedef struct emac_regs { ...@@ -147,53 +216,62 @@ typedef struct emac_regs {
#define EMAC_ISR_PTLE 0x00040000 #define EMAC_ISR_PTLE 0x00040000
#define EMAC_ISR_ORE 0x00020000 #define EMAC_ISR_ORE 0x00020000
#define EMAC_ISR_IRE 0x00010000 #define EMAC_ISR_IRE 0x00010000
#define EMAC_ISR_DBDM 0x00000200 #define EMAC_ISR_SQE 0x00000080
#define EMAC_ISR_DB0 0x00000100 #define EMAC_ISR_TE 0x00000040
#define EMAC_ISR_SE0 0x00000080
#define EMAC_ISR_TE0 0x00000040
#define EMAC_ISR_DB1 0x00000020
#define EMAC_ISR_SE1 0x00000010
#define EMAC_ISR_TE1 0x00000008
#define EMAC_ISR_MOS 0x00000002 #define EMAC_ISR_MOS 0x00000002
#define EMAC_ISR_MOF 0x00000001 #define EMAC_ISR_MOF 0x00000001
/* STA CONTROL REG */ /* EMACx_STACR */
#define EMAC_STACR_PHYD_MASK 0xffff
#define EMAC_STACR_PHYD_SHIFT 16
#define EMAC_STACR_OC 0x00008000 #define EMAC_STACR_OC 0x00008000
#define EMAC_STACR_PHYE 0x00004000 #define EMAC_STACR_PHYE 0x00004000
#define EMAC_STACR_WRITE 0x00002000 #define EMAC_STACR_STAC_MASK 0x00003000
#define EMAC_STACR_READ 0x00001000 #define EMAC_STACR_STAC_READ 0x00001000
#define EMAC_STACR_CLK_83MHZ 0x00000800 /* 0's for 50Mhz */ #define EMAC_STACR_STAC_WRITE 0x00002000
#define EMAC_STACR_CLK_66MHZ 0x00000400 #if !defined(CONFIG_IBM_EMAC4)
#define EMAC_STACR_CLK_100MHZ 0x00000C00 #define EMAC_STACR_OPBC_MASK 0x00000C00
#define EMAC_STACR_OPBC_50 0x00000000
#define EMAC_STACR_OPBC_66 0x00000400
#define EMAC_STACR_OPBC_83 0x00000800
#define EMAC_STACR_OPBC_100 0x00000C00
#define EMAC_STACR_OPBC(freq) ((freq) <= 50 ? EMAC_STACR_OPBC_50 : \
(freq) <= 66 ? EMAC_STACR_OPBC_66 : \
(freq) <= 83 ? EMAC_STACR_OPBC_83 : EMAC_STACR_OPBC_100)
#define EMAC_STACR_BASE(opb) EMAC_STACR_OPBC(opb)
#else
#define EMAC_STACR_BASE(opb) 0x00000000
#endif
#define EMAC_STACR_PCDA_MASK 0x1f
#define EMAC_STACR_PCDA_SHIFT 5
#define EMAC_STACR_PRA_MASK 0x1f
/* EMACx_TRTR */
#if !defined(CONFIG_IBM_EMAC4)
#define EMAC_TRTR_SHIFT 27
#else
#define EMAC_TRTR_SHIFT 24
#endif
#define EMAC_TRTR(size) ((((size) >> 6) - 1) << EMAC_TRTR_SHIFT)
/* Transmit Request Threshold Register */ /* EMACx_RWMR */
#define EMAC_TRTR_1600 0x18000000 /* 0's for 64 Bytes */ #if !defined(CONFIG_IBM_EMAC4)
#define EMAC_TRTR_1024 0x0f000000 #define EMAC_RWMR(l,h) (((l) << 23) | ( ((h) & 0x1ff) << 7))
#define EMAC_TRTR_512 0x07000000 #else
#define EMAC_TRTR_256 0x03000000 #define EMAC_RWMR(l,h) (((l) << 22) | ( ((h) & 0x3ff) << 6))
#define EMAC_TRTR_192 0x10000000 #endif
#define EMAC_TRTR_128 0x01000000
/* EMAC specific TX descriptor control fields (write access) */
#define EMAC_TX_CTRL_GFCS 0x0200 #define EMAC_TX_CTRL_GFCS 0x0200
#define EMAC_TX_CTRL_GP 0x0100 #define EMAC_TX_CTRL_GP 0x0100
#define EMAC_TX_CTRL_ISA 0x0080 #define EMAC_TX_CTRL_ISA 0x0080
#define EMAC_TX_CTRL_RSA 0x0040 #define EMAC_TX_CTRL_RSA 0x0040
#define EMAC_TX_CTRL_IVT 0x0020 #define EMAC_TX_CTRL_IVT 0x0020
#define EMAC_TX_CTRL_RVT 0x0010 #define EMAC_TX_CTRL_RVT 0x0010
#define EMAC_TX_CTRL_TAH_CSUM 0x000e /* TAH only */ #define EMAC_TX_CTRL_TAH_CSUM 0x000e
#define EMAC_TX_CTRL_TAH_SEG4 0x000a /* TAH only */
#define EMAC_TX_CTRL_TAH_SEG3 0x0008 /* TAH only */
#define EMAC_TX_CTRL_TAH_SEG2 0x0006 /* TAH only */
#define EMAC_TX_CTRL_TAH_SEG1 0x0004 /* TAH only */
#define EMAC_TX_CTRL_TAH_SEG0 0x0002 /* TAH only */
#define EMAC_TX_CTRL_TAH_DIS 0x0000 /* TAH only */
#define EMAC_TX_CTRL_DFLT ( \ /* EMAC specific TX descriptor status fields (read access) */
MAL_TX_CTRL_INTR | EMAC_TX_CTRL_GFCS | EMAC_TX_CTRL_GP )
/* madmal transmit status / Control bits */
#define EMAC_TX_ST_BFCS 0x0200 #define EMAC_TX_ST_BFCS 0x0200
#define EMAC_TX_ST_BPP 0x0100
#define EMAC_TX_ST_LCS 0x0080 #define EMAC_TX_ST_LCS 0x0080
#define EMAC_TX_ST_ED 0x0040 #define EMAC_TX_ST_ED 0x0040
#define EMAC_TX_ST_EC 0x0020 #define EMAC_TX_ST_EC 0x0020
...@@ -202,8 +280,16 @@ typedef struct emac_regs { ...@@ -202,8 +280,16 @@ typedef struct emac_regs {
#define EMAC_TX_ST_SC 0x0004 #define EMAC_TX_ST_SC 0x0004
#define EMAC_TX_ST_UR 0x0002 #define EMAC_TX_ST_UR 0x0002
#define EMAC_TX_ST_SQE 0x0001 #define EMAC_TX_ST_SQE 0x0001
#if !defined(CONFIG_IBM_EMAC_TAH)
#define EMAC_IS_BAD_TX(v) ((v) & (EMAC_TX_ST_LCS | EMAC_TX_ST_ED | \
EMAC_TX_ST_EC | EMAC_TX_ST_LC | \
EMAC_TX_ST_MC | EMAC_TX_ST_UR))
#else
#define EMAC_IS_BAD_TX(v) ((v) & (EMAC_TX_ST_LCS | EMAC_TX_ST_ED | \
EMAC_TX_ST_EC | EMAC_TX_ST_LC))
#endif
/* madmal receive status / Control bits */ /* EMAC specific RX descriptor status fields (read access) */
#define EMAC_RX_ST_OE 0x0200 #define EMAC_RX_ST_OE 0x0200
#define EMAC_RX_ST_PP 0x0100 #define EMAC_RX_ST_PP 0x0100
#define EMAC_RX_ST_BP 0x0080 #define EMAC_RX_ST_BP 0x0080
...@@ -214,54 +300,10 @@ typedef struct emac_regs { ...@@ -214,54 +300,10 @@ typedef struct emac_regs {
#define EMAC_RX_ST_PTL 0x0004 #define EMAC_RX_ST_PTL 0x0004
#define EMAC_RX_ST_ORE 0x0002 #define EMAC_RX_ST_ORE 0x0002
#define EMAC_RX_ST_IRE 0x0001 #define EMAC_RX_ST_IRE 0x0001
#define EMAC_BAD_RX_PACKET 0x02ff #define EMAC_RX_TAH_BAD_CSUM 0x0003
#define EMAC_CSUM_VER_ERROR 0x0003 #define EMAC_BAD_RX_MASK (EMAC_RX_ST_OE | EMAC_RX_ST_BP | \
EMAC_RX_ST_RP | EMAC_RX_ST_SE | \
/* identify a bad rx packet dependent on emac features */ EMAC_RX_ST_AE | EMAC_RX_ST_BFCS | \
#ifdef CONFIG_IBM_EMAC4 EMAC_RX_ST_PTL | EMAC_RX_ST_ORE | \
#define EMAC_IS_BAD_RX_PACKET(desc) \ EMAC_RX_ST_IRE )
(((desc & (EMAC_BAD_RX_PACKET & ~EMAC_CSUM_VER_ERROR)) || \ #endif /* __IBM_EMAC_H_ */
((desc & EMAC_CSUM_VER_ERROR) == EMAC_RX_ST_ORE) || \
((desc & EMAC_CSUM_VER_ERROR) == EMAC_RX_ST_IRE)))
#else
#define EMAC_IS_BAD_RX_PACKET(desc) \
(desc & EMAC_BAD_RX_PACKET)
#endif
/* SoC implementation specific EMAC register defaults */
#if defined(CONFIG_440GP)
#define EMAC_RWMR_DEFAULT 0x80009000
#define EMAC_TMR0_DEFAULT 0x00000000
#define EMAC_TMR1_DEFAULT 0xf8640000
#elif defined(CONFIG_440GX)
#define EMAC_RWMR_DEFAULT 0x1000a200
#define EMAC_TMR0_DEFAULT EMAC_TMR0_TFAE_2_32
#define EMAC_TMR1_DEFAULT 0xa00f0000
#elif defined(CONFIG_440SP)
#define EMAC_RWMR_DEFAULT 0x08002000
#define EMAC_TMR0_DEFAULT EMAC_TMR0_TFAE_128_2048
#define EMAC_TMR1_DEFAULT 0xf8200000
#else
#define EMAC_RWMR_DEFAULT 0x0f002000
#define EMAC_TMR0_DEFAULT 0x00000000
#define EMAC_TMR1_DEFAULT 0x380f0000
#endif /* CONFIG_440GP */
/* Revision specific EMAC register defaults */
#ifdef CONFIG_IBM_EMAC4
#define EMAC_M1_DEFAULT (EMAC_M1_BASE | \
EMAC_M1_OPB_CLK_83 | \
EMAC_M1_TX_MWSW)
#define EMAC_RMR_DEFAULT (EMAC_RMR_BASE | \
EMAC_RMR_RFAF_128_2048)
#define EMAC_TMR0_XMIT (EMAC_TMR0_GNP0 | \
EMAC_TMR0_DEFAULT)
#define EMAC_TRTR_DEFAULT EMAC_TRTR_1024
#else /* !CONFIG_IBM_EMAC4 */
#define EMAC_M1_DEFAULT EMAC_M1_BASE
#define EMAC_RMR_DEFAULT EMAC_RMR_BASE
#define EMAC_TMR0_XMIT EMAC_TMR0_GNP0
#define EMAC_TRTR_DEFAULT EMAC_TRTR_1600
#endif /* CONFIG_IBM_EMAC4 */
#endif
This source diff could not be displayed because it is too large. You can view the blob instead.
/* /*
* ibm_emac_core.h * drivers/net/ibm_emac/ibm_emac_core.h
* *
* Ethernet driver for the built in ethernet on the IBM 405 PowerPC * Driver for PowerPC 4xx on-chip ethernet controller.
* processor.
* *
* Armin Kuster akuster@mvista.com * Copyright (c) 2004, 2005 Zultys Technologies.
* Sept, 2001 * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
* *
* Orignial driver * Based on original work by
* Johnnie Peters * Armin Kuster <akuster@mvista.com>
* jpeters@mvista.com * Johnnie Peters <jpeters@mvista.com>
* * Copyright 2000, 2001 MontaVista Softare Inc.
* Copyright 2000 MontaVista Softare Inc.
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the * under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your * Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. * option) any later version.
*
*/ */
#ifndef __IBM_EMAC_CORE_H_
#define __IBM_EMAC_CORE_H_
#ifndef _IBM_EMAC_CORE_H_ #include <linux/config.h>
#define _IBM_EMAC_CORE_H_
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/dma-mapping.h>
#include <asm/ocp.h> #include <asm/ocp.h>
#include <asm/mmu.h> /* For phys_addr_t */
#include "ibm_emac.h" #include "ibm_emac.h"
#include "ibm_emac_phy.h" #include "ibm_emac_phy.h"
#include "ibm_emac_rgmii.h"
#include "ibm_emac_zmii.h" #include "ibm_emac_zmii.h"
#include "ibm_emac_rgmii.h"
#include "ibm_emac_mal.h" #include "ibm_emac_mal.h"
#include "ibm_emac_tah.h" #include "ibm_emac_tah.h"
#ifndef CONFIG_IBM_EMAC_TXB #define NUM_TX_BUFF CONFIG_IBM_EMAC_TXB
#define NUM_TX_BUFF 64 #define NUM_RX_BUFF CONFIG_IBM_EMAC_RXB
#define NUM_RX_BUFF 64
#else
#define NUM_TX_BUFF CONFIG_IBM_EMAC_TXB
#define NUM_RX_BUFF CONFIG_IBM_EMAC_RXB
#endif
/* This does 16 byte alignment, exactly what we need. /* Simple sanity check */
* The packet length includes FCS, but we don't want to #if NUM_TX_BUFF > 256 || NUM_RX_BUFF > 256
* include that when passing upstream as it messes up #error Invalid number of buffer descriptors (greater than 256)
* bridging applications.
*/
#ifndef CONFIG_IBM_EMAC_SKBRES
#define SKB_RES 2
#else
#define SKB_RES CONFIG_IBM_EMAC_SKBRES
#endif #endif
/* Note about alignement. alloc_skb() returns a cache line // XXX
* aligned buffer. However, dev_alloc_skb() will add 16 more #define EMAC_MIN_MTU 46
* bytes and "reserve" them, so our buffer will actually end #define EMAC_MAX_MTU 9000
* on a half cache line. What we do is to use directly
* alloc_skb, allocate 16 more bytes to match the total amount /* Maximum L2 header length (VLAN tagged, no FCS) */
* allocated by dev_alloc_skb(), but we don't reserve. #define EMAC_MTU_OVERHEAD (6 * 2 + 2 + 4)
/* RX BD size for the given MTU */
static inline int emac_rx_size(int mtu)
{
if (mtu > ETH_DATA_LEN)
return MAL_MAX_RX_SIZE;
else
return mal_rx_size(ETH_DATA_LEN + EMAC_MTU_OVERHEAD);
}
#define EMAC_DMA_ALIGN(x) ALIGN((x), dma_get_cache_alignment())
#define EMAC_RX_SKB_HEADROOM \
EMAC_DMA_ALIGN(CONFIG_IBM_EMAC_RX_SKB_HEADROOM)
/* Size of RX skb for the given MTU */
static inline int emac_rx_skb_size(int mtu)
{
int size = max(mtu + EMAC_MTU_OVERHEAD, emac_rx_size(mtu));
return EMAC_DMA_ALIGN(size + 2) + EMAC_RX_SKB_HEADROOM;
}
/* RX DMA sync size */
static inline int emac_rx_sync_size(int mtu)
{
return EMAC_DMA_ALIGN(emac_rx_size(mtu) + 2);
}
/* Driver statistcs is split into two parts to make it more cache friendly:
* - normal statistics (packet count, etc)
* - error statistics
*
* When statistics is requested by ethtool, these parts are concatenated,
* normal one goes first.
*
* Please, keep these structures in sync with emac_stats_keys.
*/ */
#define MAX_NUM_BUF_DESC 255
#define DESC_BUF_SIZE 4080 /* max 4096-16 */ /* Normal TX/RX Statistics */
#define DESC_BUF_SIZE_REG (DESC_BUF_SIZE / 16) struct ibm_emac_stats {
u64 rx_packets;
/* Transmitter timeout. */ u64 rx_bytes;
#define TX_TIMEOUT (2*HZ) u64 tx_packets;
u64 tx_bytes;
/* MDIO latency delay */ u64 rx_packets_csum;
#define MDIO_DELAY 250 u64 tx_packets_csum;
};
/* Power managment shift registers */
#define IBM_CPM_EMMII 0 /* Shift value for MII */ /* Error statistics */
#define IBM_CPM_EMRX 1 /* Shift value for recv */ struct ibm_emac_error_stats {
#define IBM_CPM_EMTX 2 /* Shift value for MAC */ u64 tx_undo;
#define IBM_CPM_EMAC(x) (((x)>>IBM_CPM_EMMII) | ((x)>>IBM_CPM_EMRX) | ((x)>>IBM_CPM_EMTX))
/* Software RX Errors */
#define ENET_HEADER_SIZE 14 u64 rx_dropped_stack;
#define ENET_FCS_SIZE 4 u64 rx_dropped_oom;
#define ENET_DEF_MTU_SIZE 1500 u64 rx_dropped_error;
#define ENET_DEF_BUF_SIZE (ENET_DEF_MTU_SIZE + ENET_HEADER_SIZE + ENET_FCS_SIZE) u64 rx_dropped_resize;
#define EMAC_MIN_FRAME 64 u64 rx_dropped_mtu;
#define EMAC_MAX_FRAME 9018 u64 rx_stopped;
#define EMAC_MIN_MTU (EMAC_MIN_FRAME - ENET_HEADER_SIZE - ENET_FCS_SIZE) /* BD reported RX errors */
#define EMAC_MAX_MTU (EMAC_MAX_FRAME - ENET_HEADER_SIZE - ENET_FCS_SIZE) u64 rx_bd_errors;
u64 rx_bd_overrun;
#ifdef CONFIG_IBM_EMAC_ERRMSG u64 rx_bd_bad_packet;
void emac_serr_dump_0(struct net_device *dev); u64 rx_bd_runt_packet;
void emac_serr_dump_1(struct net_device *dev); u64 rx_bd_short_event;
void emac_err_dump(struct net_device *dev, int em0isr); u64 rx_bd_alignment_error;
void emac_phy_dump(struct net_device *); u64 rx_bd_bad_fcs;
void emac_desc_dump(struct net_device *); u64 rx_bd_packet_too_long;
void emac_mac_dump(struct net_device *); u64 rx_bd_out_of_range;
void emac_mal_dump(struct net_device *); u64 rx_bd_in_range;
#else /* EMAC IRQ reported RX errors */
#define emac_serr_dump_0(dev) do { } while (0) u64 rx_parity;
#define emac_serr_dump_1(dev) do { } while (0) u64 rx_fifo_overrun;
#define emac_err_dump(dev,x) do { } while (0) u64 rx_overrun;
#define emac_phy_dump(dev) do { } while (0) u64 rx_bad_packet;
#define emac_desc_dump(dev) do { } while (0) u64 rx_runt_packet;
#define emac_mac_dump(dev) do { } while (0) u64 rx_short_event;
#define emac_mal_dump(dev) do { } while (0) u64 rx_alignment_error;
#endif u64 rx_bad_fcs;
u64 rx_packet_too_long;
u64 rx_out_of_range;
u64 rx_in_range;
/* Software TX Errors */
u64 tx_dropped;
/* BD reported TX errors */
u64 tx_bd_errors;
u64 tx_bd_bad_fcs;
u64 tx_bd_carrier_loss;
u64 tx_bd_excessive_deferral;
u64 tx_bd_excessive_collisions;
u64 tx_bd_late_collision;
u64 tx_bd_multple_collisions;
u64 tx_bd_single_collision;
u64 tx_bd_underrun;
u64 tx_bd_sqe;
/* EMAC IRQ reported TX errors */
u64 tx_parity;
u64 tx_underrun;
u64 tx_sqe;
u64 tx_errors;
};
#define EMAC_ETHTOOL_STATS_COUNT ((sizeof(struct ibm_emac_stats) + \
sizeof(struct ibm_emac_error_stats)) \
/ sizeof(u64))
struct ocp_enet_private { struct ocp_enet_private {
struct sk_buff *tx_skb[NUM_TX_BUFF]; struct net_device *ndev; /* 0 */
struct sk_buff *rx_skb[NUM_RX_BUFF]; struct emac_regs *emacp;
struct mal_descriptor *tx_desc;
struct mal_descriptor *rx_desc; struct mal_descriptor *tx_desc;
struct mal_descriptor *rx_dirty; int tx_cnt;
struct net_device_stats stats; int tx_slot;
int tx_cnt; int ack_slot;
int rx_slot;
int dirty_rx; struct mal_descriptor *rx_desc;
int tx_slot; int rx_slot;
int ack_slot; struct sk_buff *rx_sg_skb; /* 1 */
int rx_buffer_size; int rx_skb_size;
int rx_sync_size;
struct mii_phy phy_mii;
int mii_phy_addr; struct ibm_emac_stats stats;
int want_autoneg; struct ocp_device *tah_dev;
int timer_ticks;
struct timer_list link_timer; struct ibm_ocp_mal *mal;
struct net_device *mdio_dev; struct mal_commac commac;
struct ocp_device *rgmii_dev; struct sk_buff *tx_skb[NUM_TX_BUFF];
int rgmii_input; struct sk_buff *rx_skb[NUM_RX_BUFF];
struct ocp_device *zmii_dev; struct ocp_device *zmii_dev;
int zmii_input; int zmii_input;
struct ocp_enet_private *mdio_dev;
struct ibm_ocp_mal *mal; struct ocp_device *rgmii_dev;
int mal_tx_chan, mal_rx_chan; int rgmii_input;
struct mal_commac commac;
struct ocp_def *def;
struct ocp_device *tah_dev;
struct mii_phy phy;
int opened; struct timer_list link_timer;
int going_away; int reset_failed;
int wol_irq;
emac_t *emacp; struct ibm_emac_error_stats estats;
struct ocp_device *ocpdev; struct net_device_stats nstats;
struct net_device *ndev;
spinlock_t lock; struct device* ldev;
}; };
#endif /* _IBM_EMAC_CORE_H_ */
/* Ethtool get_regs complex data.
* We want to get not just EMAC registers, but also MAL, ZMII, RGMII, TAH
* when available.
*
* Returned BLOB consists of the ibm_emac_ethtool_regs_hdr,
* MAL registers, EMAC registers and optional ZMII, RGMII, TAH registers.
* Each register component is preceded with emac_ethtool_regs_subhdr.
* Order of the optional headers follows their relative bit posititions
* in emac_ethtool_regs_hdr.components
*/
#define EMAC_ETHTOOL_REGS_ZMII 0x00000001
#define EMAC_ETHTOOL_REGS_RGMII 0x00000002
#define EMAC_ETHTOOL_REGS_TAH 0x00000004
struct emac_ethtool_regs_hdr {
u32 components;
};
struct emac_ethtool_regs_subhdr {
u32 version;
u32 index;
};
#endif /* __IBM_EMAC_CORE_H_ */
/* /*
* ibm_ocp_debug.c * drivers/net/ibm_emac/ibm_emac_debug.c
* *
* This has all the debug routines that where in *_enet.c * Driver for PowerPC 4xx on-chip ethernet controller, debug print routines.
* *
* Armin Kuster akuster@mvista.com * Copyright (c) 2004, 2005 Zultys Technologies
* April , 2002 * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
*
* Copyright 2002 MontaVista Softare Inc.
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the * under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your * Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. * option) any later version.
*
*/ */
#include <linux/config.h> #include <linux/config.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/sysrq.h>
#include <asm/io.h> #include <asm/io.h>
#include "ibm_ocp_mal.h"
#include "ibm_ocp_zmii.h"
#include "ibm_ocp_enet.h"
extern int emac_phy_read(struct net_device *dev, int mii_id, int reg); #include "ibm_emac_core.h"
static void emac_desc_dump(int idx, struct ocp_enet_private *p)
{
int i;
printk("** EMAC%d TX BDs **\n"
" tx_cnt = %d tx_slot = %d ack_slot = %d\n",
idx, p->tx_cnt, p->tx_slot, p->ack_slot);
for (i = 0; i < NUM_TX_BUFF / 2; ++i)
printk
("bd[%2d] 0x%08x %c 0x%04x %4u - bd[%2d] 0x%08x %c 0x%04x %4u\n",
i, p->tx_desc[i].data_ptr, p->tx_skb[i] ? 'V' : ' ',
p->tx_desc[i].ctrl, p->tx_desc[i].data_len,
NUM_TX_BUFF / 2 + i,
p->tx_desc[NUM_TX_BUFF / 2 + i].data_ptr,
p->tx_skb[NUM_TX_BUFF / 2 + i] ? 'V' : ' ',
p->tx_desc[NUM_TX_BUFF / 2 + i].ctrl,
p->tx_desc[NUM_TX_BUFF / 2 + i].data_len);
printk("** EMAC%d RX BDs **\n"
" rx_slot = %d rx_stopped = %d rx_skb_size = %d rx_sync_size = %d\n"
" rx_sg_skb = 0x%p\n",
idx, p->rx_slot, p->commac.rx_stopped, p->rx_skb_size,
p->rx_sync_size, p->rx_sg_skb);
for (i = 0; i < NUM_RX_BUFF / 2; ++i)
printk
("bd[%2d] 0x%08x %c 0x%04x %4u - bd[%2d] 0x%08x %c 0x%04x %4u\n",
i, p->rx_desc[i].data_ptr, p->rx_skb[i] ? 'V' : ' ',
p->rx_desc[i].ctrl, p->rx_desc[i].data_len,
NUM_RX_BUFF / 2 + i,
p->rx_desc[NUM_RX_BUFF / 2 + i].data_ptr,
p->rx_skb[NUM_RX_BUFF / 2 + i] ? 'V' : ' ',
p->rx_desc[NUM_RX_BUFF / 2 + i].ctrl,
p->rx_desc[NUM_RX_BUFF / 2 + i].data_len);
}
static void emac_mac_dump(int idx, struct ocp_enet_private *dev)
{
struct emac_regs *p = dev->emacp;
printk("** EMAC%d registers **\n"
"MR0 = 0x%08x MR1 = 0x%08x TMR0 = 0x%08x TMR1 = 0x%08x\n"
"RMR = 0x%08x ISR = 0x%08x ISER = 0x%08x\n"
"IAR = %04x%08x VTPID = 0x%04x VTCI = 0x%04x\n"
"IAHT: 0x%04x 0x%04x 0x%04x 0x%04x "
"GAHT: 0x%04x 0x%04x 0x%04x 0x%04x\n"
"LSA = %04x%08x IPGVR = 0x%04x\n"
"STACR = 0x%08x TRTR = 0x%08x RWMR = 0x%08x\n"
"OCTX = 0x%08x OCRX = 0x%08x IPCR = 0x%08x\n",
idx, in_be32(&p->mr0), in_be32(&p->mr1),
in_be32(&p->tmr0), in_be32(&p->tmr1),
in_be32(&p->rmr), in_be32(&p->isr), in_be32(&p->iser),
in_be32(&p->iahr), in_be32(&p->ialr), in_be32(&p->vtpid),
in_be32(&p->vtci),
in_be32(&p->iaht1), in_be32(&p->iaht2), in_be32(&p->iaht3),
in_be32(&p->iaht4),
in_be32(&p->gaht1), in_be32(&p->gaht2), in_be32(&p->gaht3),
in_be32(&p->gaht4),
in_be32(&p->lsah), in_be32(&p->lsal), in_be32(&p->ipgvr),
in_be32(&p->stacr), in_be32(&p->trtr), in_be32(&p->rwmr),
in_be32(&p->octx), in_be32(&p->ocrx), in_be32(&p->ipcr)
);
emac_desc_dump(idx, dev);
}
static void emac_mal_dump(struct ibm_ocp_mal *mal)
{
struct ocp_func_mal_data *maldata = mal->def->additions;
int i;
printk("** MAL%d Registers **\n"
"CFG = 0x%08x ESR = 0x%08x IER = 0x%08x\n"
"TX|CASR = 0x%08x CARR = 0x%08x EOBISR = 0x%08x DEIR = 0x%08x\n"
"RX|CASR = 0x%08x CARR = 0x%08x EOBISR = 0x%08x DEIR = 0x%08x\n",
mal->def->index,
get_mal_dcrn(mal, MAL_CFG), get_mal_dcrn(mal, MAL_ESR),
get_mal_dcrn(mal, MAL_IER),
get_mal_dcrn(mal, MAL_TXCASR), get_mal_dcrn(mal, MAL_TXCARR),
get_mal_dcrn(mal, MAL_TXEOBISR), get_mal_dcrn(mal, MAL_TXDEIR),
get_mal_dcrn(mal, MAL_RXCASR), get_mal_dcrn(mal, MAL_RXCARR),
get_mal_dcrn(mal, MAL_RXEOBISR), get_mal_dcrn(mal, MAL_RXDEIR)
);
printk("TX|");
for (i = 0; i < maldata->num_tx_chans; ++i) {
if (i && !(i % 4))
printk("\n ");
printk("CTP%d = 0x%08x ", i, get_mal_dcrn(mal, MAL_TXCTPR(i)));
}
printk("\nRX|");
for (i = 0; i < maldata->num_rx_chans; ++i) {
if (i && !(i % 4))
printk("\n ");
printk("CTP%d = 0x%08x ", i, get_mal_dcrn(mal, MAL_RXCTPR(i)));
}
printk("\n ");
for (i = 0; i < maldata->num_rx_chans; ++i) {
u32 r = get_mal_dcrn(mal, MAL_RCBS(i));
if (i && !(i % 3))
printk("\n ");
printk("RCBS%d = 0x%08x (%d) ", i, r, r * 16);
}
printk("\n");
}
static struct ocp_enet_private *__emacs[4];
static struct ibm_ocp_mal *__mals[1];
void emac_phy_dump(struct net_device *dev) void emac_dbg_register(int idx, struct ocp_enet_private *dev)
{ {
struct ocp_enet_private *fep = dev->priv; unsigned long flags;
unsigned long i;
uint data; if (idx >= sizeof(__emacs) / sizeof(__emacs[0])) {
printk(KERN_WARNING
printk(KERN_DEBUG " Prepare for Phy dump....\n"); "invalid index %d when registering EMAC for debugging\n",
for (i = 0; i < 0x1A; i++) { idx);
data = emac_phy_read(dev, fep->mii_phy_addr, i); return;
printk(KERN_DEBUG "Phy reg 0x%lx ==> %4x\n", i, data);
if (i == 0x07)
i = 0x0f;
} }
local_irq_save(flags);
__emacs[idx] = dev;
local_irq_restore(flags);
} }
void emac_desc_dump(struct net_device *dev) void mal_dbg_register(int idx, struct ibm_ocp_mal *mal)
{ {
struct ocp_enet_private *fep = dev->priv; unsigned long flags;
int curr_slot;
if (idx >= sizeof(__mals) / sizeof(__mals[0])) {
printk(KERN_DEBUG printk(KERN_WARNING
"dumping the receive descriptors: current slot is %d\n", "invalid index %d when registering MAL for debugging\n",
fep->rx_slot); idx);
for (curr_slot = 0; curr_slot < NUM_RX_BUFF; curr_slot++) { return;
printk(KERN_DEBUG
"Desc %02d: status 0x%04x, length %3d, addr 0x%x\n",
curr_slot, fep->rx_desc[curr_slot].ctrl,
fep->rx_desc[curr_slot].data_len,
(unsigned int)fep->rx_desc[curr_slot].data_ptr);
} }
local_irq_save(flags);
__mals[idx] = mal;
local_irq_restore(flags);
} }
void emac_mac_dump(struct net_device *dev) void emac_dbg_dump_all(void)
{ {
struct ocp_enet_private *fep = dev->priv; unsigned int i;
volatile emac_t *emacp = fep->emacp; unsigned long flags;
printk(KERN_DEBUG "EMAC DEBUG ********** \n"); local_irq_save(flags);
printk(KERN_DEBUG "EMAC_M0 ==> 0x%x\n", in_be32(&emacp->em0mr0));
printk(KERN_DEBUG "EMAC_M1 ==> 0x%x\n", in_be32(&emacp->em0mr1)); for (i = 0; i < sizeof(__mals) / sizeof(__mals[0]); ++i)
printk(KERN_DEBUG "EMAC_TXM0==> 0x%x\n", in_be32(&emacp->em0tmr0)); if (__mals[i])
printk(KERN_DEBUG "EMAC_TXM1==> 0x%x\n", in_be32(&emacp->em0tmr1)); emac_mal_dump(__mals[i]);
printk(KERN_DEBUG "EMAC_RXM ==> 0x%x\n", in_be32(&emacp->em0rmr));
printk(KERN_DEBUG "EMAC_ISR ==> 0x%x\n", in_be32(&emacp->em0isr)); for (i = 0; i < sizeof(__emacs) / sizeof(__emacs[0]); ++i)
printk(KERN_DEBUG "EMAC_IER ==> 0x%x\n", in_be32(&emacp->em0iser)); if (__emacs[i])
printk(KERN_DEBUG "EMAC_IAH ==> 0x%x\n", in_be32(&emacp->em0iahr)); emac_mac_dump(i, __emacs[i]);
printk(KERN_DEBUG "EMAC_IAL ==> 0x%x\n", in_be32(&emacp->em0ialr));
printk(KERN_DEBUG "EMAC_VLAN_TPID_REG ==> 0x%x\n", local_irq_restore(flags);
in_be32(&emacp->em0vtpid));
} }
void emac_mal_dump(struct net_device *dev) #if defined(CONFIG_MAGIC_SYSRQ)
static void emac_sysrq_handler(int key, struct pt_regs *pt_regs,
struct tty_struct *tty)
{ {
struct ibm_ocp_mal *mal = ((struct ocp_enet_private *)dev->priv)->mal; emac_dbg_dump_all();
printk(KERN_DEBUG " MAL DEBUG ********** \n");
printk(KERN_DEBUG " MCR ==> 0x%x\n",
(unsigned int)get_mal_dcrn(mal, DCRN_MALCR));
printk(KERN_DEBUG " ESR ==> 0x%x\n",
(unsigned int)get_mal_dcrn(mal, DCRN_MALESR));
printk(KERN_DEBUG " IER ==> 0x%x\n",
(unsigned int)get_mal_dcrn(mal, DCRN_MALIER));
#ifdef CONFIG_40x
printk(KERN_DEBUG " DBR ==> 0x%x\n",
(unsigned int)get_mal_dcrn(mal, DCRN_MALDBR));
#endif /* CONFIG_40x */
printk(KERN_DEBUG " TXCASR ==> 0x%x\n",
(unsigned int)get_mal_dcrn(mal, DCRN_MALTXCASR));
printk(KERN_DEBUG " TXCARR ==> 0x%x\n",
(unsigned int)get_mal_dcrn(mal, DCRN_MALTXCARR));
printk(KERN_DEBUG " TXEOBISR ==> 0x%x\n",
(unsigned int)get_mal_dcrn(mal, DCRN_MALTXEOBISR));
printk(KERN_DEBUG " TXDEIR ==> 0x%x\n",
(unsigned int)get_mal_dcrn(mal, DCRN_MALTXDEIR));
printk(KERN_DEBUG " RXCASR ==> 0x%x\n",
(unsigned int)get_mal_dcrn(mal, DCRN_MALRXCASR));
printk(KERN_DEBUG " RXCARR ==> 0x%x\n",
(unsigned int)get_mal_dcrn(mal, DCRN_MALRXCARR));
printk(KERN_DEBUG " RXEOBISR ==> 0x%x\n",
(unsigned int)get_mal_dcrn(mal, DCRN_MALRXEOBISR));
printk(KERN_DEBUG " RXDEIR ==> 0x%x\n",
(unsigned int)get_mal_dcrn(mal, DCRN_MALRXDEIR));
printk(KERN_DEBUG " TXCTP0R ==> 0x%x\n",
(unsigned int)get_mal_dcrn(mal, DCRN_MALTXCTP0R));
printk(KERN_DEBUG " TXCTP1R ==> 0x%x\n",
(unsigned int)get_mal_dcrn(mal, DCRN_MALTXCTP1R));
printk(KERN_DEBUG " TXCTP2R ==> 0x%x\n",
(unsigned int)get_mal_dcrn(mal, DCRN_MALTXCTP2R));
printk(KERN_DEBUG " TXCTP3R ==> 0x%x\n",
(unsigned int)get_mal_dcrn(mal, DCRN_MALTXCTP3R));
printk(KERN_DEBUG " RXCTP0R ==> 0x%x\n",
(unsigned int)get_mal_dcrn(mal, DCRN_MALRXCTP0R));
printk(KERN_DEBUG " RXCTP1R ==> 0x%x\n",
(unsigned int)get_mal_dcrn(mal, DCRN_MALRXCTP1R));
printk(KERN_DEBUG " RCBS0 ==> 0x%x\n",
(unsigned int)get_mal_dcrn(mal, DCRN_MALRCBS0));
printk(KERN_DEBUG " RCBS1 ==> 0x%x\n",
(unsigned int)get_mal_dcrn(mal, DCRN_MALRCBS1));
} }
void emac_serr_dump_0(struct net_device *dev) static struct sysrq_key_op emac_sysrq_op = {
.handler = emac_sysrq_handler,
.help_msg = "emaC",
.action_msg = "Show EMAC(s) status",
};
int __init emac_init_debug(void)
{ {
struct ibm_ocp_mal *mal = ((struct ocp_enet_private *)dev->priv)->mal; return register_sysrq_key('c', &emac_sysrq_op);
unsigned long int mal_error, plb_error, plb_addr;
mal_error = get_mal_dcrn(mal, DCRN_MALESR);
printk(KERN_DEBUG "ppc405_eth_serr: %s channel %ld \n",
(mal_error & 0x40000000) ? "Receive" :
"Transmit", (mal_error & 0x3e000000) >> 25);
printk(KERN_DEBUG " ----- latched error -----\n");
if (mal_error & MALESR_DE)
printk(KERN_DEBUG " DE: descriptor error\n");
if (mal_error & MALESR_OEN)
printk(KERN_DEBUG " ONE: OPB non-fullword error\n");
if (mal_error & MALESR_OTE)
printk(KERN_DEBUG " OTE: OPB timeout error\n");
if (mal_error & MALESR_OSE)
printk(KERN_DEBUG " OSE: OPB slave error\n");
if (mal_error & MALESR_PEIN) {
plb_error = mfdcr(DCRN_PLB0_BESR);
printk(KERN_DEBUG
" PEIN: PLB error, PLB0_BESR is 0x%x\n",
(unsigned int)plb_error);
plb_addr = mfdcr(DCRN_PLB0_BEAR);
printk(KERN_DEBUG
" PEIN: PLB error, PLB0_BEAR is 0x%x\n",
(unsigned int)plb_addr);
}
} }
void emac_serr_dump_1(struct net_device *dev) void __exit emac_fini_debug(void)
{ {
struct ibm_ocp_mal *mal = ((struct ocp_enet_private *)dev->priv)->mal; unregister_sysrq_key('c', &emac_sysrq_op);
int mal_error = get_mal_dcrn(mal, DCRN_MALESR);
printk(KERN_DEBUG " ----- cumulative errors -----\n");
if (mal_error & MALESR_DEI)
printk(KERN_DEBUG " DEI: descriptor error interrupt\n");
if (mal_error & MALESR_ONEI)
printk(KERN_DEBUG " OPB non-fullword error interrupt\n");
if (mal_error & MALESR_OTEI)
printk(KERN_DEBUG " OTEI: timeout error interrupt\n");
if (mal_error & MALESR_OSEI)
printk(KERN_DEBUG " OSEI: slave error interrupt\n");
if (mal_error & MALESR_PBEI)
printk(KERN_DEBUG " PBEI: PLB bus error interrupt\n");
} }
void emac_err_dump(struct net_device *dev, int em0isr) #else
int __init emac_init_debug(void)
{
return 0;
}
void __exit emac_fini_debug(void)
{ {
printk(KERN_DEBUG "%s: on-chip ethernet error:\n", dev->name);
if (em0isr & EMAC_ISR_OVR)
printk(KERN_DEBUG " OVR: overrun\n");
if (em0isr & EMAC_ISR_PP)
printk(KERN_DEBUG " PP: control pause packet\n");
if (em0isr & EMAC_ISR_BP)
printk(KERN_DEBUG " BP: packet error\n");
if (em0isr & EMAC_ISR_RP)
printk(KERN_DEBUG " RP: runt packet\n");
if (em0isr & EMAC_ISR_SE)
printk(KERN_DEBUG " SE: short event\n");
if (em0isr & EMAC_ISR_ALE)
printk(KERN_DEBUG " ALE: odd number of nibbles in packet\n");
if (em0isr & EMAC_ISR_BFCS)
printk(KERN_DEBUG " BFCS: bad FCS\n");
if (em0isr & EMAC_ISR_PTLE)
printk(KERN_DEBUG " PTLE: oversized packet\n");
if (em0isr & EMAC_ISR_ORE)
printk(KERN_DEBUG
" ORE: packet length field > max allowed LLC\n");
if (em0isr & EMAC_ISR_IRE)
printk(KERN_DEBUG " IRE: In Range error\n");
if (em0isr & EMAC_ISR_DBDM)
printk(KERN_DEBUG " DBDM: xmit error or SQE\n");
if (em0isr & EMAC_ISR_DB0)
printk(KERN_DEBUG " DB0: xmit error or SQE on TX channel 0\n");
if (em0isr & EMAC_ISR_SE0)
printk(KERN_DEBUG
" SE0: Signal Quality Error test failure from TX channel 0\n");
if (em0isr & EMAC_ISR_TE0)
printk(KERN_DEBUG " TE0: xmit channel 0 aborted\n");
if (em0isr & EMAC_ISR_DB1)
printk(KERN_DEBUG " DB1: xmit error or SQE on TX channel \n");
if (em0isr & EMAC_ISR_SE1)
printk(KERN_DEBUG
" SE1: Signal Quality Error test failure from TX channel 1\n");
if (em0isr & EMAC_ISR_TE1)
printk(KERN_DEBUG " TE1: xmit channel 1 aborted\n");
if (em0isr & EMAC_ISR_MOS)
printk(KERN_DEBUG " MOS\n");
if (em0isr & EMAC_ISR_MOF)
printk(KERN_DEBUG " MOF\n");
emac_mac_dump(dev);
emac_mal_dump(dev);
} }
#endif /* CONFIG_MAGIC_SYSRQ */
/*
* drivers/net/ibm_emac/ibm_ocp_debug.h
*
* Driver for PowerPC 4xx on-chip ethernet controller, debug print routines.
*
* Copyright (c) 2004, 2005 Zultys Technologies
* Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef __IBM_EMAC_DEBUG_H_
#define __IBM_EMAC_DEBUG_H_
#include <linux/config.h>
#include <linux/init.h>
#include "ibm_emac_core.h"
#include "ibm_emac_mal.h"
#if defined(CONFIG_IBM_EMAC_DEBUG)
void emac_dbg_register(int idx, struct ocp_enet_private *dev);
void mal_dbg_register(int idx, struct ibm_ocp_mal *mal);
int emac_init_debug(void) __init;
void emac_fini_debug(void) __exit;
void emac_dbg_dump_all(void);
# define DBG_LEVEL 1
#else
# define emac_dbg_register(x,y) ((void)0)
# define mal_dbg_register(x,y) ((void)0)
# define emac_init_debug() ((void)0)
# define emac_fini_debug() ((void)0)
# define emac_dbg_dump_all() ((void)0)
# define DBG_LEVEL 0
#endif
#if DBG_LEVEL > 0
# define DBG(f,x...) printk("emac" f, ##x)
# define MAL_DBG(f,x...) printk("mal" f, ##x)
# define ZMII_DBG(f,x...) printk("zmii" f, ##x)
# define RGMII_DBG(f,x...) printk("rgmii" f, ##x)
# define NL "\n"
#else
# define DBG(f,x...) ((void)0)
# define MAL_DBG(f,x...) ((void)0)
# define ZMII_DBG(f,x...) ((void)0)
# define RGMII_DBG(f,x...) ((void)0)
#endif
#if DBG_LEVEL > 1
# define DBG2(f,x...) DBG(f, ##x)
# define MAL_DBG2(f,x...) MAL_DBG(f, ##x)
# define ZMII_DBG2(f,x...) ZMII_DBG(f, ##x)
# define RGMII_DBG2(f,x...) RGMII_DBG(f, ##x)
#else
# define DBG2(f,x...) ((void)0)
# define MAL_DBG2(f,x...) ((void)0)
# define ZMII_DBG2(f,x...) ((void)0)
# define RGMII_DBG2(f,x...) ((void)0)
#endif
#endif /* __IBM_EMAC_DEBUG_H_ */
/* /*
* ibm_ocp_mal.c * drivers/net/ibm_emac/ibm_emac_mal.c
* *
* Armin Kuster akuster@mvista.com * Memory Access Layer (MAL) support
* Juen, 2002 *
* Copyright (c) 2004, 2005 Zultys Technologies.
* Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
* *
* Copyright 2002 MontaVista Softare Inc. * Based on original work by
* Benjamin Herrenschmidt <benh@kernel.crashing.org>,
* David Gibson <hermes@gibson.dropbear.id.au>,
*
* Armin Kuster <akuster@mvista.com>
* Copyright 2002 MontaVista Softare Inc.
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the * under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your * Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. * option) any later version.
*
*/ */
#include <linux/config.h> #include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/ocp.h> #include <asm/ocp.h>
#include "ibm_emac_core.h"
#include "ibm_emac_mal.h" #include "ibm_emac_mal.h"
#include "ibm_emac_debug.h"
// Locking: Should we share a lock with the client ? The client could provide int __init mal_register_commac(struct ibm_ocp_mal *mal,
// a lock pointer (optionally) in the commac structure... I don't think this is struct mal_commac *commac)
// really necessary though
/* This lock protects the commac list. On today UP implementations, it's
* really only used as IRQ protection in mal_{register,unregister}_commac()
*/
static DEFINE_RWLOCK(mal_list_lock);
int mal_register_commac(struct ibm_ocp_mal *mal, struct mal_commac *commac)
{ {
unsigned long flags; unsigned long flags;
local_irq_save(flags);
write_lock_irqsave(&mal_list_lock, flags); MAL_DBG("%d: reg(%08x, %08x)" NL, mal->def->index,
commac->tx_chan_mask, commac->rx_chan_mask);
/* Don't let multiple commacs claim the same channel */ /* Don't let multiple commacs claim the same channel(s) */
if ((mal->tx_chan_mask & commac->tx_chan_mask) || if ((mal->tx_chan_mask & commac->tx_chan_mask) ||
(mal->rx_chan_mask & commac->rx_chan_mask)) { (mal->rx_chan_mask & commac->rx_chan_mask)) {
write_unlock_irqrestore(&mal_list_lock, flags); local_irq_restore(flags);
printk(KERN_WARNING "mal%d: COMMAC channels conflict!\n",
mal->def->index);
return -EBUSY; return -EBUSY;
} }
mal->tx_chan_mask |= commac->tx_chan_mask; mal->tx_chan_mask |= commac->tx_chan_mask;
mal->rx_chan_mask |= commac->rx_chan_mask; mal->rx_chan_mask |= commac->rx_chan_mask;
list_add(&commac->list, &mal->list);
list_add(&commac->list, &mal->commac); local_irq_restore(flags);
write_unlock_irqrestore(&mal_list_lock, flags);
return 0; return 0;
} }
int mal_unregister_commac(struct ibm_ocp_mal *mal, struct mal_commac *commac) void __exit mal_unregister_commac(struct ibm_ocp_mal *mal,
struct mal_commac *commac)
{ {
unsigned long flags; unsigned long flags;
local_irq_save(flags);
write_lock_irqsave(&mal_list_lock, flags); MAL_DBG("%d: unreg(%08x, %08x)" NL, mal->def->index,
commac->tx_chan_mask, commac->rx_chan_mask);
mal->tx_chan_mask &= ~commac->tx_chan_mask; mal->tx_chan_mask &= ~commac->tx_chan_mask;
mal->rx_chan_mask &= ~commac->rx_chan_mask; mal->rx_chan_mask &= ~commac->rx_chan_mask;
list_del_init(&commac->list); list_del_init(&commac->list);
write_unlock_irqrestore(&mal_list_lock, flags); local_irq_restore(flags);
return 0;
} }
int mal_set_rcbs(struct ibm_ocp_mal *mal, int channel, unsigned long size) int mal_set_rcbs(struct ibm_ocp_mal *mal, int channel, unsigned long size)
{ {
switch (channel) { struct ocp_func_mal_data *maldata = mal->def->additions;
case 0: BUG_ON(channel < 0 || channel >= maldata->num_rx_chans ||
set_mal_dcrn(mal, DCRN_MALRCBS0, size); size > MAL_MAX_RX_SIZE);
break;
#ifdef DCRN_MALRCBS1 MAL_DBG("%d: set_rbcs(%d, %lu)" NL, mal->def->index, channel, size);
case 1:
set_mal_dcrn(mal, DCRN_MALRCBS1, size); if (size & 0xf) {
break; printk(KERN_WARNING
#endif "mal%d: incorrect RX size %lu for the channel %d\n",
#ifdef DCRN_MALRCBS2 mal->def->index, size, channel);
case 2:
set_mal_dcrn(mal, DCRN_MALRCBS2, size);
break;
#endif
#ifdef DCRN_MALRCBS3
case 3:
set_mal_dcrn(mal, DCRN_MALRCBS3, size);
break;
#endif
default:
return -EINVAL; return -EINVAL;
} }
set_mal_dcrn(mal, MAL_RCBS(channel), size >> 4);
return 0; return 0;
} }
static irqreturn_t mal_serr(int irq, void *dev_instance, struct pt_regs *regs) int mal_tx_bd_offset(struct ibm_ocp_mal *mal, int channel)
{ {
struct ibm_ocp_mal *mal = dev_instance; struct ocp_func_mal_data *maldata = mal->def->additions;
unsigned long mal_error; BUG_ON(channel < 0 || channel >= maldata->num_tx_chans);
return channel * NUM_TX_BUFF;
}
/* int mal_rx_bd_offset(struct ibm_ocp_mal *mal, int channel)
* This SERR applies to one of the devices on the MAL, here we charge {
* it against the first EMAC registered for the MAL. struct ocp_func_mal_data *maldata = mal->def->additions;
*/ BUG_ON(channel < 0 || channel >= maldata->num_rx_chans);
return maldata->num_tx_chans * NUM_TX_BUFF + channel * NUM_RX_BUFF;
}
mal_error = get_mal_dcrn(mal, DCRN_MALESR); void mal_enable_tx_channel(struct ibm_ocp_mal *mal, int channel)
{
local_bh_disable();
MAL_DBG("%d: enable_tx(%d)" NL, mal->def->index, channel);
set_mal_dcrn(mal, MAL_TXCASR,
get_mal_dcrn(mal, MAL_TXCASR) | MAL_CHAN_MASK(channel));
local_bh_enable();
}
printk(KERN_ERR "%s: System Error (MALESR=%lx)\n", void mal_disable_tx_channel(struct ibm_ocp_mal *mal, int channel)
"MAL" /* FIXME: get the name right */ , mal_error); {
set_mal_dcrn(mal, MAL_TXCARR, MAL_CHAN_MASK(channel));
MAL_DBG("%d: disable_tx(%d)" NL, mal->def->index, channel);
}
/* FIXME: decipher error */ void mal_enable_rx_channel(struct ibm_ocp_mal *mal, int channel)
/* DIXME: distribute to commacs, if possible */ {
local_bh_disable();
MAL_DBG("%d: enable_rx(%d)" NL, mal->def->index, channel);
set_mal_dcrn(mal, MAL_RXCASR,
get_mal_dcrn(mal, MAL_RXCASR) | MAL_CHAN_MASK(channel));
local_bh_enable();
}
/* Clear the error status register */ void mal_disable_rx_channel(struct ibm_ocp_mal *mal, int channel)
set_mal_dcrn(mal, DCRN_MALESR, mal_error); {
set_mal_dcrn(mal, MAL_RXCARR, MAL_CHAN_MASK(channel));
MAL_DBG("%d: disable_rx(%d)" NL, mal->def->index, channel);
}
return IRQ_HANDLED; void mal_poll_add(struct ibm_ocp_mal *mal, struct mal_commac *commac)
{
local_bh_disable();
MAL_DBG("%d: poll_add(%p)" NL, mal->def->index, commac);
list_add_tail(&commac->poll_list, &mal->poll_list);
local_bh_enable();
} }
static irqreturn_t mal_txeob(int irq, void *dev_instance, struct pt_regs *regs) void mal_poll_del(struct ibm_ocp_mal *mal, struct mal_commac *commac)
{
local_bh_disable();
MAL_DBG("%d: poll_del(%p)" NL, mal->def->index, commac);
list_del(&commac->poll_list);
local_bh_enable();
}
/* synchronized by mal_poll() */
static inline void mal_enable_eob_irq(struct ibm_ocp_mal *mal)
{
MAL_DBG2("%d: enable_irq" NL, mal->def->index);
set_mal_dcrn(mal, MAL_CFG, get_mal_dcrn(mal, MAL_CFG) | MAL_CFG_EOPIE);
}
/* synchronized by __LINK_STATE_RX_SCHED bit in ndev->state */
static inline void mal_disable_eob_irq(struct ibm_ocp_mal *mal)
{
set_mal_dcrn(mal, MAL_CFG, get_mal_dcrn(mal, MAL_CFG) & ~MAL_CFG_EOPIE);
MAL_DBG2("%d: disable_irq" NL, mal->def->index);
}
static irqreturn_t mal_serr(int irq, void *dev_instance, struct pt_regs *regs)
{ {
struct ibm_ocp_mal *mal = dev_instance; struct ibm_ocp_mal *mal = dev_instance;
struct list_head *l; u32 esr = get_mal_dcrn(mal, MAL_ESR);
unsigned long isr;
isr = get_mal_dcrn(mal, DCRN_MALTXEOBISR); /* Clear the error status register */
set_mal_dcrn(mal, DCRN_MALTXEOBISR, isr); set_mal_dcrn(mal, MAL_ESR, esr);
read_lock(&mal_list_lock); MAL_DBG("%d: SERR %08x" NL, mal->def->index, esr);
list_for_each(l, &mal->commac) {
struct mal_commac *mc = list_entry(l, struct mal_commac, list);
if (isr & mc->tx_chan_mask) { if (esr & MAL_ESR_EVB) {
mc->ops->txeob(mc->dev, isr & mc->tx_chan_mask); if (esr & MAL_ESR_DE) {
/* We ignore Descriptor error,
* TXDE or RXDE interrupt will be generated anyway.
*/
return IRQ_HANDLED;
} }
if (esr & MAL_ESR_PEIN) {
/* PLB error, it's probably buggy hardware or
* incorrect physical address in BD (i.e. bug)
*/
if (net_ratelimit())
printk(KERN_ERR
"mal%d: system error, PLB (ESR = 0x%08x)\n",
mal->def->index, esr);
return IRQ_HANDLED;
}
/* OPB error, it's probably buggy hardware or incorrect EBC setup */
if (net_ratelimit())
printk(KERN_ERR
"mal%d: system error, OPB (ESR = 0x%08x)\n",
mal->def->index, esr);
} }
read_unlock(&mal_list_lock); return IRQ_HANDLED;
}
static inline void mal_schedule_poll(struct ibm_ocp_mal *mal)
{
if (likely(netif_rx_schedule_prep(&mal->poll_dev))) {
MAL_DBG2("%d: schedule_poll" NL, mal->def->index);
mal_disable_eob_irq(mal);
__netif_rx_schedule(&mal->poll_dev);
} else
MAL_DBG2("%d: already in poll" NL, mal->def->index);
}
static irqreturn_t mal_txeob(int irq, void *dev_instance, struct pt_regs *regs)
{
struct ibm_ocp_mal *mal = dev_instance;
u32 r = get_mal_dcrn(mal, MAL_TXEOBISR);
MAL_DBG2("%d: txeob %08x" NL, mal->def->index, r);
mal_schedule_poll(mal);
set_mal_dcrn(mal, MAL_TXEOBISR, r);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static irqreturn_t mal_rxeob(int irq, void *dev_instance, struct pt_regs *regs) static irqreturn_t mal_rxeob(int irq, void *dev_instance, struct pt_regs *regs)
{ {
struct ibm_ocp_mal *mal = dev_instance; struct ibm_ocp_mal *mal = dev_instance;
struct list_head *l; u32 r = get_mal_dcrn(mal, MAL_RXEOBISR);
unsigned long isr; MAL_DBG2("%d: rxeob %08x" NL, mal->def->index, r);
mal_schedule_poll(mal);
set_mal_dcrn(mal, MAL_RXEOBISR, r);
return IRQ_HANDLED;
}
isr = get_mal_dcrn(mal, DCRN_MALRXEOBISR); static irqreturn_t mal_txde(int irq, void *dev_instance, struct pt_regs *regs)
set_mal_dcrn(mal, DCRN_MALRXEOBISR, isr); {
struct ibm_ocp_mal *mal = dev_instance;
u32 deir = get_mal_dcrn(mal, MAL_TXDEIR);
set_mal_dcrn(mal, MAL_TXDEIR, deir);
read_lock(&mal_list_lock); MAL_DBG("%d: txde %08x" NL, mal->def->index, deir);
list_for_each(l, &mal->commac) {
struct mal_commac *mc = list_entry(l, struct mal_commac, list);
if (isr & mc->rx_chan_mask) { if (net_ratelimit())
mc->ops->rxeob(mc->dev, isr & mc->rx_chan_mask); printk(KERN_ERR
} "mal%d: TX descriptor error (TXDEIR = 0x%08x)\n",
} mal->def->index, deir);
read_unlock(&mal_list_lock);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static irqreturn_t mal_txde(int irq, void *dev_instance, struct pt_regs *regs) static irqreturn_t mal_rxde(int irq, void *dev_instance, struct pt_regs *regs)
{ {
struct ibm_ocp_mal *mal = dev_instance; struct ibm_ocp_mal *mal = dev_instance;
struct list_head *l; struct list_head *l;
unsigned long deir; u32 deir = get_mal_dcrn(mal, MAL_RXDEIR);
deir = get_mal_dcrn(mal, DCRN_MALTXDEIR); MAL_DBG("%d: rxde %08x" NL, mal->def->index, deir);
/* FIXME: print which MAL correctly */ list_for_each(l, &mal->list) {
printk(KERN_WARNING "%s: Tx descriptor error (MALTXDEIR=%lx)\n",
"MAL", deir);
read_lock(&mal_list_lock);
list_for_each(l, &mal->commac) {
struct mal_commac *mc = list_entry(l, struct mal_commac, list); struct mal_commac *mc = list_entry(l, struct mal_commac, list);
if (deir & mc->rx_chan_mask) {
if (deir & mc->tx_chan_mask) { mc->rx_stopped = 1;
mc->ops->txde(mc->dev, deir & mc->tx_chan_mask); mc->ops->rxde(mc->dev);
} }
} }
read_unlock(&mal_list_lock);
mal_schedule_poll(mal);
set_mal_dcrn(mal, MAL_RXDEIR, deir);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
/* static int mal_poll(struct net_device *ndev, int *budget)
* This interrupt should be very rare at best. This occurs when
* the hardware has a problem with the receive descriptors. The manual
* states that it occurs when the hardware cannot the receive descriptor
* empty bit is not set. The recovery mechanism will be to
* traverse through the descriptors, handle any that are marked to be
* handled and reinitialize each along the way. At that point the driver
* will be restarted.
*/
static irqreturn_t mal_rxde(int irq, void *dev_instance, struct pt_regs *regs)
{ {
struct ibm_ocp_mal *mal = dev_instance; struct ibm_ocp_mal *mal = ndev->priv;
struct list_head *l; struct list_head *l;
unsigned long deir; int rx_work_limit = min(ndev->quota, *budget), received = 0, done;
deir = get_mal_dcrn(mal, DCRN_MALRXDEIR); MAL_DBG2("%d: poll(%d) %d ->" NL, mal->def->index, *budget,
rx_work_limit);
again:
/* Process TX skbs */
list_for_each(l, &mal->poll_list) {
struct mal_commac *mc =
list_entry(l, struct mal_commac, poll_list);
mc->ops->poll_tx(mc->dev);
}
/* /* Process RX skbs.
* This really is needed. This case encountered in stress testing. * We _might_ need something more smart here to enforce polling fairness.
*/ */
if (deir == 0) list_for_each(l, &mal->poll_list) {
return IRQ_HANDLED; struct mal_commac *mc =
list_entry(l, struct mal_commac, poll_list);
/* FIXME: print which MAL correctly */ int n = mc->ops->poll_rx(mc->dev, rx_work_limit);
printk(KERN_WARNING "%s: Rx descriptor error (MALRXDEIR=%lx)\n", if (n) {
"MAL", deir); received += n;
rx_work_limit -= n;
read_lock(&mal_list_lock); if (rx_work_limit <= 0) {
list_for_each(l, &mal->commac) { done = 0;
struct mal_commac *mc = list_entry(l, struct mal_commac, list); goto more_work; // XXX What if this is the last one ?
}
}
}
if (deir & mc->rx_chan_mask) { /* We need to disable IRQs to protect from RXDE IRQ here */
mc->ops->rxde(mc->dev, deir & mc->rx_chan_mask); local_irq_disable();
__netif_rx_complete(ndev);
mal_enable_eob_irq(mal);
local_irq_enable();
done = 1;
/* Check for "rotting" packet(s) */
list_for_each(l, &mal->poll_list) {
struct mal_commac *mc =
list_entry(l, struct mal_commac, poll_list);
if (unlikely(mc->ops->peek_rx(mc->dev) || mc->rx_stopped)) {
MAL_DBG2("%d: rotting packet" NL, mal->def->index);
if (netif_rx_reschedule(ndev, received))
mal_disable_eob_irq(mal);
else
MAL_DBG2("%d: already in poll list" NL,
mal->def->index);
if (rx_work_limit > 0)
goto again;
else
goto more_work;
} }
mc->ops->poll_tx(mc->dev);
} }
read_unlock(&mal_list_lock);
return IRQ_HANDLED; more_work:
ndev->quota -= received;
*budget -= received;
MAL_DBG2("%d: poll() %d <- %d" NL, mal->def->index, *budget,
done ? 0 : 1);
return done ? 0 : 1;
}
static void mal_reset(struct ibm_ocp_mal *mal)
{
int n = 10;
MAL_DBG("%d: reset" NL, mal->def->index);
set_mal_dcrn(mal, MAL_CFG, MAL_CFG_SR);
/* Wait for reset to complete (1 system clock) */
while ((get_mal_dcrn(mal, MAL_CFG) & MAL_CFG_SR) && n)
--n;
if (unlikely(!n))
printk(KERN_ERR "mal%d: reset timeout\n", mal->def->index);
}
int mal_get_regs_len(struct ibm_ocp_mal *mal)
{
return sizeof(struct emac_ethtool_regs_subhdr) +
sizeof(struct ibm_mal_regs);
}
void *mal_dump_regs(struct ibm_ocp_mal *mal, void *buf)
{
struct emac_ethtool_regs_subhdr *hdr = buf;
struct ibm_mal_regs *regs = (struct ibm_mal_regs *)(hdr + 1);
struct ocp_func_mal_data *maldata = mal->def->additions;
int i;
hdr->version = MAL_VERSION;
hdr->index = mal->def->index;
regs->tx_count = maldata->num_tx_chans;
regs->rx_count = maldata->num_rx_chans;
regs->cfg = get_mal_dcrn(mal, MAL_CFG);
regs->esr = get_mal_dcrn(mal, MAL_ESR);
regs->ier = get_mal_dcrn(mal, MAL_IER);
regs->tx_casr = get_mal_dcrn(mal, MAL_TXCASR);
regs->tx_carr = get_mal_dcrn(mal, MAL_TXCARR);
regs->tx_eobisr = get_mal_dcrn(mal, MAL_TXEOBISR);
regs->tx_deir = get_mal_dcrn(mal, MAL_TXDEIR);
regs->rx_casr = get_mal_dcrn(mal, MAL_RXCASR);
regs->rx_carr = get_mal_dcrn(mal, MAL_RXCARR);
regs->rx_eobisr = get_mal_dcrn(mal, MAL_RXEOBISR);
regs->rx_deir = get_mal_dcrn(mal, MAL_RXDEIR);
for (i = 0; i < regs->tx_count; ++i)
regs->tx_ctpr[i] = get_mal_dcrn(mal, MAL_TXCTPR(i));
for (i = 0; i < regs->rx_count; ++i) {
regs->rx_ctpr[i] = get_mal_dcrn(mal, MAL_RXCTPR(i));
regs->rcbs[i] = get_mal_dcrn(mal, MAL_RCBS(i));
}
return regs + 1;
} }
static int __init mal_probe(struct ocp_device *ocpdev) static int __init mal_probe(struct ocp_device *ocpdev)
{ {
struct ibm_ocp_mal *mal = NULL; struct ibm_ocp_mal *mal;
struct ocp_func_mal_data *maldata; struct ocp_func_mal_data *maldata;
int err = 0; int err = 0, i, bd_size;
MAL_DBG("%d: probe" NL, ocpdev->def->index);
maldata = (struct ocp_func_mal_data *)ocpdev->def->additions; maldata = ocpdev->def->additions;
if (maldata == NULL) { if (maldata == NULL) {
printk(KERN_ERR "mal%d: Missing additional datas !\n", printk(KERN_ERR "mal%d: missing additional data!\n",
ocpdev->def->index); ocpdev->def->index);
return -ENODEV; return -ENODEV;
} }
mal = kmalloc(sizeof(struct ibm_ocp_mal), GFP_KERNEL); mal = kzalloc(sizeof(struct ibm_ocp_mal), GFP_KERNEL);
if (mal == NULL) { if (!mal) {
printk(KERN_ERR printk(KERN_ERR
"mal%d: Out of memory allocating MAL structure !\n", "mal%d: out of memory allocating MAL structure!\n",
ocpdev->def->index); ocpdev->def->index);
return -ENOMEM; return -ENOMEM;
} }
memset(mal, 0, sizeof(*mal)); mal->dcrbase = maldata->dcr_base;
mal->def = ocpdev->def;
switch (ocpdev->def->index) {
case 0:
mal->dcrbase = DCRN_MAL_BASE;
break;
#ifdef DCRN_MAL1_BASE
case 1:
mal->dcrbase = DCRN_MAL1_BASE;
break;
#endif
default:
BUG();
}
/**************************/
INIT_LIST_HEAD(&mal->commac); INIT_LIST_HEAD(&mal->poll_list);
set_bit(__LINK_STATE_START, &mal->poll_dev.state);
mal->poll_dev.weight = CONFIG_IBM_EMAC_POLL_WEIGHT;
mal->poll_dev.poll = mal_poll;
mal->poll_dev.priv = mal;
atomic_set(&mal->poll_dev.refcnt, 1);
set_mal_dcrn(mal, DCRN_MALRXCARR, 0xFFFFFFFF); INIT_LIST_HEAD(&mal->list);
set_mal_dcrn(mal, DCRN_MALTXCARR, 0xFFFFFFFF);
set_mal_dcrn(mal, DCRN_MALCR, MALCR_MMSR); /* 384 */ /* Load power-on reset defaults */
/* FIXME: Add delay */ mal_reset(mal);
/* Set the MAL configuration register */ /* Set the MAL configuration register */
set_mal_dcrn(mal, DCRN_MALCR, set_mal_dcrn(mal, MAL_CFG, MAL_CFG_DEFAULT | MAL_CFG_PLBB |
MALCR_PLBB | MALCR_OPBBL | MALCR_LEA | MAL_CFG_OPBBL | MAL_CFG_LEA);
MALCR_PLBLT_DEFAULT);
mal_enable_eob_irq(mal);
/* It would be nice to allocate buffers separately for each
* channel, but we can't because the channels share the upper /* Allocate space for BD rings */
* 13 bits of address lines. Each channels buffer must also BUG_ON(maldata->num_tx_chans <= 0 || maldata->num_tx_chans > 32);
* be 4k aligned, so we allocate 4k for each channel. This is BUG_ON(maldata->num_rx_chans <= 0 || maldata->num_rx_chans > 32);
* inefficient FIXME: do better, if possible */ bd_size = sizeof(struct mal_descriptor) *
mal->tx_virt_addr = dma_alloc_coherent(&ocpdev->dev, (NUM_TX_BUFF * maldata->num_tx_chans +
MAL_DT_ALIGN * NUM_RX_BUFF * maldata->num_rx_chans);
maldata->num_tx_chans, mal->bd_virt =
&mal->tx_phys_addr, GFP_KERNEL); dma_alloc_coherent(&ocpdev->dev, bd_size, &mal->bd_dma, GFP_KERNEL);
if (mal->tx_virt_addr == NULL) {
if (!mal->bd_virt) {
printk(KERN_ERR printk(KERN_ERR
"mal%d: Out of memory allocating MAL descriptors !\n", "mal%d: out of memory allocating RX/TX descriptors!\n",
ocpdev->def->index); mal->def->index);
err = -ENOMEM; err = -ENOMEM;
goto fail; goto fail;
} }
memset(mal->bd_virt, 0, bd_size);
/* God, oh, god, I hate DCRs */ for (i = 0; i < maldata->num_tx_chans; ++i)
set_mal_dcrn(mal, DCRN_MALTXCTP0R, mal->tx_phys_addr); set_mal_dcrn(mal, MAL_TXCTPR(i), mal->bd_dma +
#ifdef DCRN_MALTXCTP1R sizeof(struct mal_descriptor) *
if (maldata->num_tx_chans > 1) mal_tx_bd_offset(mal, i));
set_mal_dcrn(mal, DCRN_MALTXCTP1R,
mal->tx_phys_addr + MAL_DT_ALIGN); for (i = 0; i < maldata->num_rx_chans; ++i)
#endif /* DCRN_MALTXCTP1R */ set_mal_dcrn(mal, MAL_RXCTPR(i), mal->bd_dma +
#ifdef DCRN_MALTXCTP2R sizeof(struct mal_descriptor) *
if (maldata->num_tx_chans > 2) mal_rx_bd_offset(mal, i));
set_mal_dcrn(mal, DCRN_MALTXCTP2R,
mal->tx_phys_addr + 2 * MAL_DT_ALIGN);
#endif /* DCRN_MALTXCTP2R */
#ifdef DCRN_MALTXCTP3R
if (maldata->num_tx_chans > 3)
set_mal_dcrn(mal, DCRN_MALTXCTP3R,
mal->tx_phys_addr + 3 * MAL_DT_ALIGN);
#endif /* DCRN_MALTXCTP3R */
#ifdef DCRN_MALTXCTP4R
if (maldata->num_tx_chans > 4)
set_mal_dcrn(mal, DCRN_MALTXCTP4R,
mal->tx_phys_addr + 4 * MAL_DT_ALIGN);
#endif /* DCRN_MALTXCTP4R */
#ifdef DCRN_MALTXCTP5R
if (maldata->num_tx_chans > 5)
set_mal_dcrn(mal, DCRN_MALTXCTP5R,
mal->tx_phys_addr + 5 * MAL_DT_ALIGN);
#endif /* DCRN_MALTXCTP5R */
#ifdef DCRN_MALTXCTP6R
if (maldata->num_tx_chans > 6)
set_mal_dcrn(mal, DCRN_MALTXCTP6R,
mal->tx_phys_addr + 6 * MAL_DT_ALIGN);
#endif /* DCRN_MALTXCTP6R */
#ifdef DCRN_MALTXCTP7R
if (maldata->num_tx_chans > 7)
set_mal_dcrn(mal, DCRN_MALTXCTP7R,
mal->tx_phys_addr + 7 * MAL_DT_ALIGN);
#endif /* DCRN_MALTXCTP7R */
mal->rx_virt_addr = dma_alloc_coherent(&ocpdev->dev,
MAL_DT_ALIGN *
maldata->num_rx_chans,
&mal->rx_phys_addr, GFP_KERNEL);
set_mal_dcrn(mal, DCRN_MALRXCTP0R, mal->rx_phys_addr);
#ifdef DCRN_MALRXCTP1R
if (maldata->num_rx_chans > 1)
set_mal_dcrn(mal, DCRN_MALRXCTP1R,
mal->rx_phys_addr + MAL_DT_ALIGN);
#endif /* DCRN_MALRXCTP1R */
#ifdef DCRN_MALRXCTP2R
if (maldata->num_rx_chans > 2)
set_mal_dcrn(mal, DCRN_MALRXCTP2R,
mal->rx_phys_addr + 2 * MAL_DT_ALIGN);
#endif /* DCRN_MALRXCTP2R */
#ifdef DCRN_MALRXCTP3R
if (maldata->num_rx_chans > 3)
set_mal_dcrn(mal, DCRN_MALRXCTP3R,
mal->rx_phys_addr + 3 * MAL_DT_ALIGN);
#endif /* DCRN_MALRXCTP3R */
err = request_irq(maldata->serr_irq, mal_serr, 0, "MAL SERR", mal); err = request_irq(maldata->serr_irq, mal_serr, 0, "MAL SERR", mal);
if (err) if (err)
goto fail; goto fail2;
err = request_irq(maldata->txde_irq, mal_txde, 0, "MAL TX DE ", mal); err = request_irq(maldata->txde_irq, mal_txde, 0, "MAL TX DE", mal);
if (err) if (err)
goto fail; goto fail3;
err = request_irq(maldata->txeob_irq, mal_txeob, 0, "MAL TX EOB", mal); err = request_irq(maldata->txeob_irq, mal_txeob, 0, "MAL TX EOB", mal);
if (err) if (err)
goto fail; goto fail4;
err = request_irq(maldata->rxde_irq, mal_rxde, 0, "MAL RX DE", mal); err = request_irq(maldata->rxde_irq, mal_rxde, 0, "MAL RX DE", mal);
if (err) if (err)
goto fail; goto fail5;
err = request_irq(maldata->rxeob_irq, mal_rxeob, 0, "MAL RX EOB", mal); err = request_irq(maldata->rxeob_irq, mal_rxeob, 0, "MAL RX EOB", mal);
if (err) if (err)
goto fail; goto fail6;
set_mal_dcrn(mal, DCRN_MALIER, /* Enable all MAL SERR interrupt sources */
MALIER_DE | MALIER_NE | MALIER_TE | set_mal_dcrn(mal, MAL_IER, MAL_IER_EVENTS);
MALIER_OPBE | MALIER_PLBE);
/* Advertise me to the rest of the world */ /* Advertise this instance to the rest of the world */
ocp_set_drvdata(ocpdev, mal); ocp_set_drvdata(ocpdev, mal);
printk(KERN_INFO "mal%d: Initialized, %d tx channels, %d rx channels\n", mal_dbg_register(mal->def->index, mal);
ocpdev->def->index, maldata->num_tx_chans,
maldata->num_rx_chans);
printk(KERN_INFO "mal%d: initialized, %d TX channels, %d RX channels\n",
mal->def->index, maldata->num_tx_chans, maldata->num_rx_chans);
return 0; return 0;
fail6:
free_irq(maldata->rxde_irq, mal);
fail5:
free_irq(maldata->txeob_irq, mal);
fail4:
free_irq(maldata->txde_irq, mal);
fail3:
free_irq(maldata->serr_irq, mal);
fail2:
dma_free_coherent(&ocpdev->dev, bd_size, mal->bd_virt, mal->bd_dma);
fail: fail:
/* FIXME: dispose requested IRQs ! */ kfree(mal);
if (err && mal)
kfree(mal);
return err; return err;
} }
static void __exit mal_remove(struct ocp_device *ocpdev) static void __exit mal_remove(struct ocp_device *ocpdev)
{ {
struct ibm_ocp_mal *mal = ocp_get_drvdata(ocpdev); struct ibm_ocp_mal *mal = ocp_get_drvdata(ocpdev);
struct ocp_func_mal_data *maldata = ocpdev->def->additions; struct ocp_func_mal_data *maldata = mal->def->additions;
MAL_DBG("%d: remove" NL, mal->def->index);
BUG_ON(!maldata); /* Syncronize with scheduled polling,
stolen from net/core/dev.c:dev_close()
*/
clear_bit(__LINK_STATE_START, &mal->poll_dev.state);
netif_poll_disable(&mal->poll_dev);
if (!list_empty(&mal->list)) {
/* This is *very* bad */
printk(KERN_EMERG
"mal%d: commac list is not empty on remove!\n",
mal->def->index);
}
ocp_set_drvdata(ocpdev, NULL); ocp_set_drvdata(ocpdev, NULL);
/* FIXME: shut down the MAL, deal with dependency with emac */
free_irq(maldata->serr_irq, mal); free_irq(maldata->serr_irq, mal);
free_irq(maldata->txde_irq, mal); free_irq(maldata->txde_irq, mal);
free_irq(maldata->txeob_irq, mal); free_irq(maldata->txeob_irq, mal);
free_irq(maldata->rxde_irq, mal); free_irq(maldata->rxde_irq, mal);
free_irq(maldata->rxeob_irq, mal); free_irq(maldata->rxeob_irq, mal);
if (mal->tx_virt_addr) mal_reset(mal);
dma_free_coherent(&ocpdev->dev,
MAL_DT_ALIGN * maldata->num_tx_chans,
mal->tx_virt_addr, mal->tx_phys_addr);
if (mal->rx_virt_addr) mal_dbg_register(mal->def->index, NULL);
dma_free_coherent(&ocpdev->dev,
MAL_DT_ALIGN * maldata->num_rx_chans, dma_free_coherent(&ocpdev->dev,
mal->rx_virt_addr, mal->rx_phys_addr); sizeof(struct mal_descriptor) *
(NUM_TX_BUFF * maldata->num_tx_chans +
NUM_RX_BUFF * maldata->num_rx_chans), mal->bd_virt,
mal->bd_dma);
kfree(mal); kfree(mal);
} }
/* Structure for a device driver */ /* Structure for a device driver */
static struct ocp_device_id mal_ids[] = { static struct ocp_device_id mal_ids[] = {
{.vendor = OCP_ANY_ID,.function = OCP_FUNC_MAL}, { .vendor = OCP_VENDOR_IBM, .function = OCP_FUNC_MAL },
{.vendor = OCP_VENDOR_INVALID} { .vendor = OCP_VENDOR_INVALID}
}; };
static struct ocp_driver mal_driver = { static struct ocp_driver mal_driver = {
...@@ -441,23 +570,14 @@ static struct ocp_driver mal_driver = { ...@@ -441,23 +570,14 @@ static struct ocp_driver mal_driver = {
.remove = mal_remove, .remove = mal_remove,
}; };
static int __init init_mals(void) int __init mal_init(void)
{ {
int rc; MAL_DBG(": init" NL);
return ocp_register_driver(&mal_driver);
rc = ocp_register_driver(&mal_driver);
if (rc < 0) {
ocp_unregister_driver(&mal_driver);
return -ENODEV;
}
return 0;
} }
static void __exit exit_mals(void) void __exit mal_exit(void)
{ {
MAL_DBG(": exit" NL);
ocp_unregister_driver(&mal_driver); ocp_unregister_driver(&mal_driver);
} }
module_init(init_mals);
module_exit(exit_mals);
#ifndef _IBM_EMAC_MAL_H /*
#define _IBM_EMAC_MAL_H * drivers/net/ibm_emac/ibm_emac_mal.h
*
* Memory Access Layer (MAL) support
*
* Copyright (c) 2004, 2005 Zultys Technologies.
* Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
*
* Based on original work by
* Armin Kuster <akuster@mvista.com>
* Copyright 2002 MontaVista Softare Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef __IBM_EMAC_MAL_H_
#define __IBM_EMAC_MAL_H_
#include <linux/config.h>
#include <linux/init.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/netdevice.h>
#define MAL_DT_ALIGN (4096) /* Alignment for each channel's descriptor table */ #include <asm/io.h>
#define MAL_CHAN_MASK(chan) (0x80000000 >> (chan)) /*
* These MAL "versions" probably aren't the real versions IBM uses for these
* MAL cores, I assigned them just to make #ifdefs in this file nicer and
* reflect the fact that 40x and 44x have slightly different MALs. --ebs
*/
#if defined(CONFIG_405GP) || defined(CONFIG_405GPR) || defined(CONFIG_405EP) || \
defined(CONFIG_440EP) || defined(CONFIG_NP405H)
#define MAL_VERSION 1
#elif defined(CONFIG_440GP) || defined(CONFIG_440GX) || defined(CONFIG_440SP)
#define MAL_VERSION 2
#else
#error "Unknown SoC, please check chip manual and choose MAL 'version'"
#endif
/* MALx DCR registers */
#define MAL_CFG 0x00
#define MAL_CFG_SR 0x80000000
#define MAL_CFG_PLBB 0x00004000
#define MAL_CFG_OPBBL 0x00000080
#define MAL_CFG_EOPIE 0x00000004
#define MAL_CFG_LEA 0x00000002
#define MAL_CFG_SD 0x00000001
#if MAL_VERSION == 1
#define MAL_CFG_PLBP_MASK 0x00c00000
#define MAL_CFG_PLBP_10 0x00800000
#define MAL_CFG_GA 0x00200000
#define MAL_CFG_OA 0x00100000
#define MAL_CFG_PLBLE 0x00080000
#define MAL_CFG_PLBT_MASK 0x00078000
#define MAL_CFG_DEFAULT (MAL_CFG_PLBP_10 | MAL_CFG_PLBT_MASK)
#elif MAL_VERSION == 2
#define MAL_CFG_RPP_MASK 0x00c00000
#define MAL_CFG_RPP_10 0x00800000
#define MAL_CFG_RMBS_MASK 0x00300000
#define MAL_CFG_WPP_MASK 0x000c0000
#define MAL_CFG_WPP_10 0x00080000
#define MAL_CFG_WMBS_MASK 0x00030000
#define MAL_CFG_PLBLE 0x00008000
#define MAL_CFG_DEFAULT (MAL_CFG_RMBS_MASK | MAL_CFG_WMBS_MASK | \
MAL_CFG_RPP_10 | MAL_CFG_WPP_10)
#else
#error "Unknown MAL version"
#endif
#define MAL_ESR 0x01
#define MAL_ESR_EVB 0x80000000
#define MAL_ESR_CIDT 0x40000000
#define MAL_ESR_CID_MASK 0x3e000000
#define MAL_ESR_CID_SHIFT 25
#define MAL_ESR_DE 0x00100000
#define MAL_ESR_OTE 0x00040000
#define MAL_ESR_OSE 0x00020000
#define MAL_ESR_PEIN 0x00010000
#define MAL_ESR_DEI 0x00000010
#define MAL_ESR_OTEI 0x00000004
#define MAL_ESR_OSEI 0x00000002
#define MAL_ESR_PBEI 0x00000001
#if MAL_VERSION == 1
#define MAL_ESR_ONE 0x00080000
#define MAL_ESR_ONEI 0x00000008
#elif MAL_VERSION == 2
#define MAL_ESR_PTE 0x00800000
#define MAL_ESR_PRE 0x00400000
#define MAL_ESR_PWE 0x00200000
#define MAL_ESR_PTEI 0x00000080
#define MAL_ESR_PREI 0x00000040
#define MAL_ESR_PWEI 0x00000020
#else
#error "Unknown MAL version"
#endif
#define MAL_IER 0x02
#define MAL_IER_DE 0x00000010
#define MAL_IER_OTE 0x00000004
#define MAL_IER_OE 0x00000002
#define MAL_IER_PE 0x00000001
#if MAL_VERSION == 1
#define MAL_IER_NWE 0x00000008
#define MAL_IER_SOC_EVENTS MAL_IER_NWE
#elif MAL_VERSION == 2
#define MAL_IER_PT 0x00000080
#define MAL_IER_PRE 0x00000040
#define MAL_IER_PWE 0x00000020
#define MAL_IER_SOC_EVENTS (MAL_IER_PT | MAL_IER_PRE | MAL_IER_PWE)
#else
#error "Unknown MAL version"
#endif
#define MAL_IER_EVENTS (MAL_IER_SOC_EVENTS | MAL_IER_OTE | \
MAL_IER_OTE | MAL_IER_OE | MAL_IER_PE)
#define MAL_TXCASR 0x04
#define MAL_TXCARR 0x05
#define MAL_TXEOBISR 0x06
#define MAL_TXDEIR 0x07
#define MAL_RXCASR 0x10
#define MAL_RXCARR 0x11
#define MAL_RXEOBISR 0x12
#define MAL_RXDEIR 0x13
#define MAL_TXCTPR(n) ((n) + 0x20)
#define MAL_RXCTPR(n) ((n) + 0x40)
#define MAL_RCBS(n) ((n) + 0x60)
/* In reality MAL can handle TX buffers up to 4095 bytes long,
* but this isn't a good round number :) --ebs
*/
#define MAL_MAX_TX_SIZE 4080
#define MAL_MAX_RX_SIZE 4080
static inline int mal_rx_size(int len)
{
len = (len + 0xf) & ~0xf;
return len > MAL_MAX_RX_SIZE ? MAL_MAX_RX_SIZE : len;
}
static inline int mal_tx_chunks(int len)
{
return (len + MAL_MAX_TX_SIZE - 1) / MAL_MAX_TX_SIZE;
}
#define MAL_CHAN_MASK(n) (0x80000000 >> (n))
/* MAL Buffer Descriptor structure */ /* MAL Buffer Descriptor structure */
struct mal_descriptor { struct mal_descriptor {
unsigned short ctrl; /* MAL / Commac status control bits */ u16 ctrl; /* MAL / Commac status control bits */
short data_len; /* Max length is 4K-1 (12 bits) */ u16 data_len; /* Max length is 4K-1 (12 bits) */
unsigned char *data_ptr; /* pointer to actual data buffer */ u32 data_ptr; /* pointer to actual data buffer */
} __attribute__ ((packed)); };
/* the following defines are for the MadMAL status and control registers. */ /* the following defines are for the MadMAL status and control registers. */
/* MADMAL transmit and receive status/control bits */ /* MADMAL transmit and receive status/control bits */
#define MAL_RX_CTRL_EMPTY 0x8000 #define MAL_RX_CTRL_EMPTY 0x8000
#define MAL_RX_CTRL_WRAP 0x4000 #define MAL_RX_CTRL_WRAP 0x4000
#define MAL_RX_CTRL_CM 0x2000 #define MAL_RX_CTRL_CM 0x2000
#define MAL_RX_CTRL_LAST 0x1000 #define MAL_RX_CTRL_LAST 0x1000
#define MAL_RX_CTRL_FIRST 0x0800 #define MAL_RX_CTRL_FIRST 0x0800
#define MAL_RX_CTRL_INTR 0x0400 #define MAL_RX_CTRL_INTR 0x0400
#define MAL_RX_CTRL_SINGLE (MAL_RX_CTRL_LAST | MAL_RX_CTRL_FIRST)
#define MAL_TX_CTRL_READY 0x8000 #define MAL_IS_SINGLE_RX(ctrl) (((ctrl) & MAL_RX_CTRL_SINGLE) == MAL_RX_CTRL_SINGLE)
#define MAL_TX_CTRL_WRAP 0x4000
#define MAL_TX_CTRL_CM 0x2000 #define MAL_TX_CTRL_READY 0x8000
#define MAL_TX_CTRL_LAST 0x1000 #define MAL_TX_CTRL_WRAP 0x4000
#define MAL_TX_CTRL_INTR 0x0400 #define MAL_TX_CTRL_CM 0x2000
#define MAL_TX_CTRL_LAST 0x1000
#define MAL_TX_CTRL_INTR 0x0400
struct mal_commac_ops { struct mal_commac_ops {
void (*txeob) (void *dev, u32 chanmask); void (*poll_tx) (void *dev);
void (*txde) (void *dev, u32 chanmask); int (*poll_rx) (void *dev, int budget);
void (*rxeob) (void *dev, u32 chanmask); int (*peek_rx) (void *dev);
void (*rxde) (void *dev, u32 chanmask); void (*rxde) (void *dev);
}; };
struct mal_commac { struct mal_commac {
struct mal_commac_ops *ops; struct mal_commac_ops *ops;
void *dev; void *dev;
u32 tx_chan_mask, rx_chan_mask; struct list_head poll_list;
struct list_head list; int rx_stopped;
u32 tx_chan_mask;
u32 rx_chan_mask;
struct list_head list;
}; };
struct ibm_ocp_mal { struct ibm_ocp_mal {
int dcrbase; int dcrbase;
struct list_head commac; struct list_head poll_list;
u32 tx_chan_mask, rx_chan_mask; struct net_device poll_dev;
dma_addr_t tx_phys_addr; struct list_head list;
struct mal_descriptor *tx_virt_addr; u32 tx_chan_mask;
u32 rx_chan_mask;
dma_addr_t rx_phys_addr; dma_addr_t bd_dma;
struct mal_descriptor *rx_virt_addr; struct mal_descriptor *bd_virt;
};
#define GET_MAL_STANZA(base,dcrn) \ struct ocp_def *def;
case base: \ };
x = mfdcr(dcrn(base)); \
break;
#define SET_MAL_STANZA(base,dcrn, val) \
case base: \
mtdcr(dcrn(base), (val)); \
break;
#define GET_MAL0_STANZA(dcrn) GET_MAL_STANZA(DCRN_MAL_BASE,dcrn)
#define SET_MAL0_STANZA(dcrn,val) SET_MAL_STANZA(DCRN_MAL_BASE,dcrn,val)
#ifdef DCRN_MAL1_BASE
#define GET_MAL1_STANZA(dcrn) GET_MAL_STANZA(DCRN_MAL1_BASE,dcrn)
#define SET_MAL1_STANZA(dcrn,val) SET_MAL_STANZA(DCRN_MAL1_BASE,dcrn,val)
#else /* ! DCRN_MAL1_BASE */
#define GET_MAL1_STANZA(dcrn)
#define SET_MAL1_STANZA(dcrn,val)
#endif
#define get_mal_dcrn(mal, dcrn) ({ \ static inline u32 get_mal_dcrn(struct ibm_ocp_mal *mal, int reg)
u32 x; \
switch ((mal)->dcrbase) { \
GET_MAL0_STANZA(dcrn) \
GET_MAL1_STANZA(dcrn) \
default: \
x = 0; \
BUG(); \
} \
x; })
#define set_mal_dcrn(mal, dcrn, val) do { \
switch ((mal)->dcrbase) { \
SET_MAL0_STANZA(dcrn,val) \
SET_MAL1_STANZA(dcrn,val) \
default: \
BUG(); \
} } while (0)
static inline void mal_enable_tx_channels(struct ibm_ocp_mal *mal, u32 chanmask)
{ {
set_mal_dcrn(mal, DCRN_MALTXCASR, return mfdcr(mal->dcrbase + reg);
get_mal_dcrn(mal, DCRN_MALTXCASR) | chanmask);
} }
static inline void mal_disable_tx_channels(struct ibm_ocp_mal *mal, static inline void set_mal_dcrn(struct ibm_ocp_mal *mal, int reg, u32 val)
u32 chanmask)
{ {
set_mal_dcrn(mal, DCRN_MALTXCARR, chanmask); mtdcr(mal->dcrbase + reg, val);
} }
static inline void mal_enable_rx_channels(struct ibm_ocp_mal *mal, u32 chanmask) /* Register MAL devices */
{ int mal_init(void) __init;
set_mal_dcrn(mal, DCRN_MALRXCASR, void mal_exit(void) __exit;
get_mal_dcrn(mal, DCRN_MALRXCASR) | chanmask);
}
static inline void mal_disable_rx_channels(struct ibm_ocp_mal *mal, int mal_register_commac(struct ibm_ocp_mal *mal,
u32 chanmask) struct mal_commac *commac) __init;
{ void mal_unregister_commac(struct ibm_ocp_mal *mal,
set_mal_dcrn(mal, DCRN_MALRXCARR, chanmask); struct mal_commac *commac) __exit;
} int mal_set_rcbs(struct ibm_ocp_mal *mal, int channel, unsigned long size);
/* Returns BD ring offset for a particular channel
(in 'struct mal_descriptor' elements)
*/
int mal_tx_bd_offset(struct ibm_ocp_mal *mal, int channel);
int mal_rx_bd_offset(struct ibm_ocp_mal *mal, int channel);
void mal_enable_tx_channel(struct ibm_ocp_mal *mal, int channel);
void mal_disable_tx_channel(struct ibm_ocp_mal *mal, int channel);
void mal_enable_rx_channel(struct ibm_ocp_mal *mal, int channel);
void mal_disable_rx_channel(struct ibm_ocp_mal *mal, int channel);
extern int mal_register_commac(struct ibm_ocp_mal *mal, /* Add/remove EMAC to/from MAL polling list */
struct mal_commac *commac); void mal_poll_add(struct ibm_ocp_mal *mal, struct mal_commac *commac);
extern int mal_unregister_commac(struct ibm_ocp_mal *mal, void mal_poll_del(struct ibm_ocp_mal *mal, struct mal_commac *commac);
struct mal_commac *commac);
/* Ethtool MAL registers */
struct ibm_mal_regs {
u32 tx_count;
u32 rx_count;
u32 cfg;
u32 esr;
u32 ier;
u32 tx_casr;
u32 tx_carr;
u32 tx_eobisr;
u32 tx_deir;
u32 rx_casr;
u32 rx_carr;
u32 rx_eobisr;
u32 rx_deir;
u32 tx_ctpr[32];
u32 rx_ctpr[32];
u32 rcbs[32];
};
extern int mal_set_rcbs(struct ibm_ocp_mal *mal, int channel, int mal_get_regs_len(struct ibm_ocp_mal *mal);
unsigned long size); void *mal_dump_regs(struct ibm_ocp_mal *mal, void *buf);
#endif /* _IBM_EMAC_MAL_H */ #endif /* __IBM_EMAC_MAL_H_ */
/* /*
* ibm_ocp_phy.c * drivers/net/ibm_emac/ibm_emac_phy.c
* *
* PHY drivers for the ibm ocp ethernet driver. Borrowed * Driver for PowerPC 4xx on-chip ethernet controller, PHY support.
* from sungem_phy.c, though I only kept the generic MII * Borrowed from sungem_phy.c, though I only kept the generic MII
* driver for now. * driver for now.
* *
* This file should be shared with other drivers or eventually * This file should be shared with other drivers or eventually
* merged as the "low level" part of miilib * merged as the "low level" part of miilib
* *
* (c) 2003, Benjamin Herrenscmidt (benh@kernel.crashing.org) * (c) 2003, Benjamin Herrenscmidt (benh@kernel.crashing.org)
* (c) 2004-2005, Eugene Surovegin <ebs@ebshome.net>
* *
*/ */
#include <linux/config.h> #include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/mii.h> #include <linux/mii.h>
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <asm/ocp.h>
#include "ibm_emac_phy.h" #include "ibm_emac_phy.h"
static int reset_one_mii_phy(struct mii_phy *phy, int phy_id) static inline int phy_read(struct mii_phy *phy, int reg)
{
return phy->mdio_read(phy->dev, phy->address, reg);
}
static inline void phy_write(struct mii_phy *phy, int reg, int val)
{ {
u16 val; phy->mdio_write(phy->dev, phy->address, reg, val);
}
int mii_reset_phy(struct mii_phy *phy)
{
int val;
int limit = 10000; int limit = 10000;
val = __phy_read(phy, phy_id, MII_BMCR); val = phy_read(phy, MII_BMCR);
val &= ~BMCR_ISOLATE; val &= ~BMCR_ISOLATE;
val |= BMCR_RESET; val |= BMCR_RESET;
__phy_write(phy, phy_id, MII_BMCR, val); phy_write(phy, MII_BMCR, val);
udelay(100); udelay(300);
while (limit--) { while (limit--) {
val = __phy_read(phy, phy_id, MII_BMCR); val = phy_read(phy, MII_BMCR);
if ((val & BMCR_RESET) == 0) if (val >= 0 && (val & BMCR_RESET) == 0)
break; break;
udelay(10); udelay(10);
} }
if ((val & BMCR_ISOLATE) && limit > 0) if ((val & BMCR_ISOLATE) && limit > 0)
__phy_write(phy, phy_id, MII_BMCR, val & ~BMCR_ISOLATE); phy_write(phy, MII_BMCR, val & ~BMCR_ISOLATE);
return (limit <= 0);
}
static int cis8201_init(struct mii_phy *phy)
{
u16 epcr;
epcr = phy_read(phy, MII_CIS8201_EPCR);
epcr &= ~EPCR_MODE_MASK;
switch (phy->mode) {
case PHY_MODE_TBI:
epcr |= EPCR_TBI_MODE;
break;
case PHY_MODE_RTBI:
epcr |= EPCR_RTBI_MODE;
break;
case PHY_MODE_GMII:
epcr |= EPCR_GMII_MODE;
break;
case PHY_MODE_RGMII:
default:
epcr |= EPCR_RGMII_MODE;
}
phy_write(phy, MII_CIS8201_EPCR, epcr); return limit <= 0;
return 0;
} }
static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise) static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise)
{ {
u16 ctl, adv; int ctl, adv;
phy->autoneg = 1; phy->autoneg = AUTONEG_ENABLE;
phy->speed = SPEED_10; phy->speed = SPEED_10;
phy->duplex = DUPLEX_HALF; phy->duplex = DUPLEX_HALF;
phy->pause = 0; phy->pause = phy->asym_pause = 0;
phy->advertising = advertise; phy->advertising = advertise;
/* Setup standard advertise */ /* Setup standard advertise */
adv = phy_read(phy, MII_ADVERTISE); adv = phy_read(phy, MII_ADVERTISE);
adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4); if (adv < 0)
return adv;
adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP |
ADVERTISE_PAUSE_ASYM);
if (advertise & ADVERTISED_10baseT_Half) if (advertise & ADVERTISED_10baseT_Half)
adv |= ADVERTISE_10HALF; adv |= ADVERTISE_10HALF;
if (advertise & ADVERTISED_10baseT_Full) if (advertise & ADVERTISED_10baseT_Full)
...@@ -99,8 +83,25 @@ static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise) ...@@ -99,8 +83,25 @@ static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise)
adv |= ADVERTISE_100HALF; adv |= ADVERTISE_100HALF;
if (advertise & ADVERTISED_100baseT_Full) if (advertise & ADVERTISED_100baseT_Full)
adv |= ADVERTISE_100FULL; adv |= ADVERTISE_100FULL;
if (advertise & ADVERTISED_Pause)
adv |= ADVERTISE_PAUSE_CAP;
if (advertise & ADVERTISED_Asym_Pause)
adv |= ADVERTISE_PAUSE_ASYM;
phy_write(phy, MII_ADVERTISE, adv); phy_write(phy, MII_ADVERTISE, adv);
if (phy->features &
(SUPPORTED_1000baseT_Full | SUPPORTED_1000baseT_Half)) {
adv = phy_read(phy, MII_CTRL1000);
if (adv < 0)
return adv;
adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);
if (advertise & ADVERTISED_1000baseT_Full)
adv |= ADVERTISE_1000FULL;
if (advertise & ADVERTISED_1000baseT_Half)
adv |= ADVERTISE_1000HALF;
phy_write(phy, MII_CTRL1000, adv);
}
/* Start/Restart aneg */ /* Start/Restart aneg */
ctl = phy_read(phy, MII_BMCR); ctl = phy_read(phy, MII_BMCR);
ctl |= (BMCR_ANENABLE | BMCR_ANRESTART); ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
...@@ -111,14 +112,16 @@ static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise) ...@@ -111,14 +112,16 @@ static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise)
static int genmii_setup_forced(struct mii_phy *phy, int speed, int fd) static int genmii_setup_forced(struct mii_phy *phy, int speed, int fd)
{ {
u16 ctl; int ctl;
phy->autoneg = 0; phy->autoneg = AUTONEG_DISABLE;
phy->speed = speed; phy->speed = speed;
phy->duplex = fd; phy->duplex = fd;
phy->pause = 0; phy->pause = phy->asym_pause = 0;
ctl = phy_read(phy, MII_BMCR); ctl = phy_read(phy, MII_BMCR);
if (ctl < 0)
return ctl;
ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_ANENABLE); ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_ANENABLE);
/* First reset the PHY */ /* First reset the PHY */
...@@ -132,6 +135,8 @@ static int genmii_setup_forced(struct mii_phy *phy, int speed, int fd) ...@@ -132,6 +135,8 @@ static int genmii_setup_forced(struct mii_phy *phy, int speed, int fd)
ctl |= BMCR_SPEED100; ctl |= BMCR_SPEED100;
break; break;
case SPEED_1000: case SPEED_1000:
ctl |= BMCR_SPEED1000;
break;
default: default:
return -EINVAL; return -EINVAL;
} }
...@@ -144,112 +149,143 @@ static int genmii_setup_forced(struct mii_phy *phy, int speed, int fd) ...@@ -144,112 +149,143 @@ static int genmii_setup_forced(struct mii_phy *phy, int speed, int fd)
static int genmii_poll_link(struct mii_phy *phy) static int genmii_poll_link(struct mii_phy *phy)
{ {
u16 status; int status;
(void)phy_read(phy, MII_BMSR); /* Clear latched value with dummy read */
phy_read(phy, MII_BMSR);
status = phy_read(phy, MII_BMSR); status = phy_read(phy, MII_BMSR);
if ((status & BMSR_LSTATUS) == 0) if (status < 0 || (status & BMSR_LSTATUS) == 0)
return 0; return 0;
if (phy->autoneg && !(status & BMSR_ANEGCOMPLETE)) if (phy->autoneg == AUTONEG_ENABLE && !(status & BMSR_ANEGCOMPLETE))
return 0; return 0;
return 1; return 1;
} }
#define MII_CIS8201_ACSR 0x1c static int genmii_read_link(struct mii_phy *phy)
#define ACSR_DUPLEX_STATUS 0x0020
#define ACSR_SPEED_1000BASET 0x0010
#define ACSR_SPEED_100BASET 0x0008
static int cis8201_read_link(struct mii_phy *phy)
{ {
u16 acsr; if (phy->autoneg == AUTONEG_ENABLE) {
int glpa = 0;
int lpa = phy_read(phy, MII_LPA) & phy_read(phy, MII_ADVERTISE);
if (lpa < 0)
return lpa;
if (phy->features &
(SUPPORTED_1000baseT_Full | SUPPORTED_1000baseT_Half)) {
int adv = phy_read(phy, MII_CTRL1000);
glpa = phy_read(phy, MII_STAT1000);
if (glpa < 0 || adv < 0)
return adv;
glpa &= adv << 2;
}
phy->speed = SPEED_10;
phy->duplex = DUPLEX_HALF;
phy->pause = phy->asym_pause = 0;
if (glpa & (LPA_1000FULL | LPA_1000HALF)) {
phy->speed = SPEED_1000;
if (glpa & LPA_1000FULL)
phy->duplex = DUPLEX_FULL;
} else if (lpa & (LPA_100FULL | LPA_100HALF)) {
phy->speed = SPEED_100;
if (lpa & LPA_100FULL)
phy->duplex = DUPLEX_FULL;
} else if (lpa & LPA_10FULL)
phy->duplex = DUPLEX_FULL;
if (phy->autoneg) { if (phy->duplex == DUPLEX_FULL) {
acsr = phy_read(phy, MII_CIS8201_ACSR); phy->pause = lpa & LPA_PAUSE_CAP ? 1 : 0;
phy->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0;
}
} else {
int bmcr = phy_read(phy, MII_BMCR);
if (bmcr < 0)
return bmcr;
if (acsr & ACSR_DUPLEX_STATUS) if (bmcr & BMCR_FULLDPLX)
phy->duplex = DUPLEX_FULL; phy->duplex = DUPLEX_FULL;
else else
phy->duplex = DUPLEX_HALF; phy->duplex = DUPLEX_HALF;
if (acsr & ACSR_SPEED_1000BASET) { if (bmcr & BMCR_SPEED1000)
phy->speed = SPEED_1000; phy->speed = SPEED_1000;
} else if (acsr & ACSR_SPEED_100BASET) else if (bmcr & BMCR_SPEED100)
phy->speed = SPEED_100; phy->speed = SPEED_100;
else else
phy->speed = SPEED_10; phy->speed = SPEED_10;
phy->pause = 0;
}
/* On non-aneg, we assume what we put in BMCR is the speed,
* though magic-aneg shouldn't prevent this case from occurring
*/
phy->pause = phy->asym_pause = 0;
}
return 0; return 0;
} }
static int genmii_read_link(struct mii_phy *phy) /* Generic implementation for most 10/100/1000 PHYs */
static struct mii_phy_ops generic_phy_ops = {
.setup_aneg = genmii_setup_aneg,
.setup_forced = genmii_setup_forced,
.poll_link = genmii_poll_link,
.read_link = genmii_read_link
};
static struct mii_phy_def genmii_phy_def = {
.phy_id = 0x00000000,
.phy_id_mask = 0x00000000,
.name = "Generic MII",
.ops = &generic_phy_ops
};
/* CIS8201 */
#define MII_CIS8201_EPCR 0x17
#define EPCR_MODE_MASK 0x3000
#define EPCR_GMII_MODE 0x0000
#define EPCR_RGMII_MODE 0x1000
#define EPCR_TBI_MODE 0x2000
#define EPCR_RTBI_MODE 0x3000
static int cis8201_init(struct mii_phy *phy)
{ {
u16 lpa; int epcr;
if (phy->autoneg) { epcr = phy_read(phy, MII_CIS8201_EPCR);
lpa = phy_read(phy, MII_LPA) & phy_read(phy, MII_ADVERTISE); if (epcr < 0)
return epcr;
phy->speed = SPEED_10; epcr &= ~EPCR_MODE_MASK;
phy->duplex = DUPLEX_HALF;
phy->pause = 0;
if (lpa & (LPA_100FULL | LPA_100HALF)) { switch (phy->mode) {
phy->speed = SPEED_100; case PHY_MODE_TBI:
if (lpa & LPA_100FULL) epcr |= EPCR_TBI_MODE;
phy->duplex = DUPLEX_FULL; break;
} else if (lpa & LPA_10FULL) case PHY_MODE_RTBI:
phy->duplex = DUPLEX_FULL; epcr |= EPCR_RTBI_MODE;
break;
case PHY_MODE_GMII:
epcr |= EPCR_GMII_MODE;
break;
case PHY_MODE_RGMII:
default:
epcr |= EPCR_RGMII_MODE;
} }
/* On non-aneg, we assume what we put in BMCR is the speed,
* though magic-aneg shouldn't prevent this case from occurring phy_write(phy, MII_CIS8201_EPCR, epcr);
*/
return 0; return 0;
} }
#define MII_BASIC_FEATURES (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | \
SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | \
SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII)
#define MII_GBIT_FEATURES (MII_BASIC_FEATURES | \
SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full)
/* CIS8201 phy ops */
static struct mii_phy_ops cis8201_phy_ops = { static struct mii_phy_ops cis8201_phy_ops = {
init:cis8201_init, .init = cis8201_init,
setup_aneg:genmii_setup_aneg, .setup_aneg = genmii_setup_aneg,
setup_forced:genmii_setup_forced, .setup_forced = genmii_setup_forced,
poll_link:genmii_poll_link, .poll_link = genmii_poll_link,
read_link:cis8201_read_link .read_link = genmii_read_link
};
/* Generic implementation for most 10/100 PHYs */
static struct mii_phy_ops generic_phy_ops = {
setup_aneg:genmii_setup_aneg,
setup_forced:genmii_setup_forced,
poll_link:genmii_poll_link,
read_link:genmii_read_link
}; };
static struct mii_phy_def cis8201_phy_def = { static struct mii_phy_def cis8201_phy_def = {
phy_id:0x000fc410, .phy_id = 0x000fc410,
phy_id_mask:0x000ffff0, .phy_id_mask = 0x000ffff0,
name:"CIS8201 Gigabit Ethernet", .name = "CIS8201 Gigabit Ethernet",
features:MII_GBIT_FEATURES, .ops = &cis8201_phy_ops
magic_aneg:0,
ops:&cis8201_phy_ops
};
static struct mii_phy_def genmii_phy_def = {
phy_id:0x00000000,
phy_id_mask:0x00000000,
name:"Generic MII",
features:MII_BASIC_FEATURES,
magic_aneg:0,
ops:&generic_phy_ops
}; };
static struct mii_phy_def *mii_phy_table[] = { static struct mii_phy_def *mii_phy_table[] = {
...@@ -258,39 +294,60 @@ static struct mii_phy_def *mii_phy_table[] = { ...@@ -258,39 +294,60 @@ static struct mii_phy_def *mii_phy_table[] = {
NULL NULL
}; };
int mii_phy_probe(struct mii_phy *phy, int mii_id) int mii_phy_probe(struct mii_phy *phy, int address)
{ {
int rc;
u32 id;
struct mii_phy_def *def; struct mii_phy_def *def;
int i; int i;
u32 id;
phy->autoneg = 0; phy->autoneg = AUTONEG_DISABLE;
phy->advertising = 0; phy->advertising = 0;
phy->mii_id = mii_id; phy->address = address;
phy->speed = 0; phy->speed = SPEED_10;
phy->duplex = 0; phy->duplex = DUPLEX_HALF;
phy->pause = 0; phy->pause = phy->asym_pause = 0;
/* Take PHY out of isloate mode and reset it. */ /* Take PHY out of isolate mode and reset it. */
rc = reset_one_mii_phy(phy, mii_id); if (mii_reset_phy(phy))
if (rc)
return -ENODEV; return -ENODEV;
/* Read ID and find matching entry */ /* Read ID and find matching entry */
id = (phy_read(phy, MII_PHYSID1) << 16 | phy_read(phy, MII_PHYSID2)) id = (phy_read(phy, MII_PHYSID1) << 16) | phy_read(phy, MII_PHYSID2);
& 0xfffffff0;
for (i = 0; (def = mii_phy_table[i]) != NULL; i++) for (i = 0; (def = mii_phy_table[i]) != NULL; i++)
if ((id & def->phy_id_mask) == def->phy_id) if ((id & def->phy_id_mask) == def->phy_id)
break; break;
/* Should never be NULL (we have a generic entry), but... */ /* Should never be NULL (we have a generic entry), but... */
if (def == NULL) if (!def)
return -ENODEV; return -ENODEV;
phy->def = def; phy->def = def;
/* Determine PHY features if needed */
phy->features = def->features;
if (!phy->features) {
u16 bmsr = phy_read(phy, MII_BMSR);
if (bmsr & BMSR_ANEGCAPABLE)
phy->features |= SUPPORTED_Autoneg;
if (bmsr & BMSR_10HALF)
phy->features |= SUPPORTED_10baseT_Half;
if (bmsr & BMSR_10FULL)
phy->features |= SUPPORTED_10baseT_Full;
if (bmsr & BMSR_100HALF)
phy->features |= SUPPORTED_100baseT_Half;
if (bmsr & BMSR_100FULL)
phy->features |= SUPPORTED_100baseT_Full;
if (bmsr & BMSR_ESTATEN) {
u16 esr = phy_read(phy, MII_ESTATUS);
if (esr & ESTATUS_1000_TFULL)
phy->features |= SUPPORTED_1000baseT_Full;
if (esr & ESTATUS_1000_THALF)
phy->features |= SUPPORTED_1000baseT_Half;
}
phy->features |= SUPPORTED_MII;
}
/* Setup default advertising */ /* Setup default advertising */
phy->advertising = def->features; phy->advertising = phy->features;
return 0; return 0;
} }
......
/* /*
* ibm_emac_phy.h * drivers/net/ibm_emac/ibm_emac_phy.h
*
* *
* Benjamin Herrenschmidt <benh@kernel.crashing.org> * Driver for PowerPC 4xx on-chip ethernet controller, PHY support
* February 2003
* *
* This program is free software; you can redistribute it and/or modify it * Benjamin Herrenschmidt <benh@kernel.crashing.org>
* under the terms of the GNU General Public License as published by the * February 2003
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * Minor additions by Eugene Surovegin <ebs@ebshome.net>, 2004
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
* *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
* *
* This file basically duplicates sungem_phy.{c,h} with different PHYs * This file basically duplicates sungem_phy.{c,h} with different PHYs
* supported. I'm looking into merging that in a single mii layer more * supported. I'm looking into merging that in a single mii layer more
* flexible than mii.c * flexible than mii.c
*/ */
#ifndef _IBM_EMAC_PHY_H_ #ifndef _IBM_OCP_PHY_H_
#define _IBM_EMAC_PHY_H_ #define _IBM_OCP_PHY_H_
/*
* PHY mode settings
* Used for multi-mode capable PHYs
*/
#define PHY_MODE_NA 0
#define PHY_MODE_MII 1
#define PHY_MODE_RMII 2
#define PHY_MODE_SMII 3
#define PHY_MODE_RGMII 4
#define PHY_MODE_TBI 5
#define PHY_MODE_GMII 6
#define PHY_MODE_RTBI 7
#define PHY_MODE_SGMII 8
/*
* PHY specific registers/values
*/
/* CIS8201 */
#define MII_CIS8201_EPCR 0x17
#define EPCR_MODE_MASK 0x3000
#define EPCR_GMII_MODE 0x0000
#define EPCR_RGMII_MODE 0x1000
#define EPCR_TBI_MODE 0x2000
#define EPCR_RTBI_MODE 0x3000
struct mii_phy; struct mii_phy;
...@@ -77,7 +37,8 @@ struct mii_phy_ops { ...@@ -77,7 +37,8 @@ struct mii_phy_ops {
struct mii_phy_def { struct mii_phy_def {
u32 phy_id; /* Concatenated ID1 << 16 | ID2 */ u32 phy_id; /* Concatenated ID1 << 16 | ID2 */
u32 phy_id_mask; /* Significant bits */ u32 phy_id_mask; /* Significant bits */
u32 features; /* Ethtool SUPPORTED_* defines */ u32 features; /* Ethtool SUPPORTED_* defines or
0 for autodetect */
int magic_aneg; /* Autoneg does all speed test for us */ int magic_aneg; /* Autoneg does all speed test for us */
const char *name; const char *name;
const struct mii_phy_ops *ops; const struct mii_phy_ops *ops;
...@@ -86,8 +47,11 @@ struct mii_phy_def { ...@@ -86,8 +47,11 @@ struct mii_phy_def {
/* An instance of a PHY, partially borrowed from mii_if_info */ /* An instance of a PHY, partially borrowed from mii_if_info */
struct mii_phy { struct mii_phy {
struct mii_phy_def *def; struct mii_phy_def *def;
int advertising; u32 advertising; /* Ethtool ADVERTISED_* defines */
int mii_id; u32 features; /* Copied from mii_phy_def.features
or determined automaticaly */
int address; /* PHY address */
int mode; /* PHY mode */
/* 1: autoneg enabled, 0: disabled */ /* 1: autoneg enabled, 0: disabled */
int autoneg; int autoneg;
...@@ -98,40 +62,19 @@ struct mii_phy { ...@@ -98,40 +62,19 @@ struct mii_phy {
int speed; int speed;
int duplex; int duplex;
int pause; int pause;
int asym_pause;
/* PHY mode - if needed */
int mode;
/* Provided by host chip */ /* Provided by host chip */
struct net_device *dev; struct net_device *dev;
int (*mdio_read) (struct net_device * dev, int mii_id, int reg); int (*mdio_read) (struct net_device * dev, int addr, int reg);
void (*mdio_write) (struct net_device * dev, int mii_id, int reg, void (*mdio_write) (struct net_device * dev, int addr, int reg,
int val); int val);
}; };
/* Pass in a struct mii_phy with dev, mdio_read and mdio_write /* Pass in a struct mii_phy with dev, mdio_read and mdio_write
* filled, the remaining fields will be filled on return * filled, the remaining fields will be filled on return
*/ */
extern int mii_phy_probe(struct mii_phy *phy, int mii_id); int mii_phy_probe(struct mii_phy *phy, int address);
int mii_reset_phy(struct mii_phy *phy);
static inline int __phy_read(struct mii_phy *phy, int id, int reg)
{
return phy->mdio_read(phy->dev, id, reg);
}
static inline void __phy_write(struct mii_phy *phy, int id, int reg, int val)
{
phy->mdio_write(phy->dev, id, reg, val);
}
static inline int phy_read(struct mii_phy *phy, int reg)
{
return phy->mdio_read(phy->dev, phy->mii_id, reg);
}
static inline void phy_write(struct mii_phy *phy, int reg, int val)
{
phy->mdio_write(phy->dev, phy->mii_id, reg, val);
}
#endif /* _IBM_EMAC_PHY_H_ */ #endif /* _IBM_OCP_PHY_H_ */
/*
* drivers/net/ibm_emac/ibm_emac_rgmii.c
*
* Driver for PowerPC 4xx on-chip ethernet controller, RGMII bridge support.
*
* Copyright (c) 2004, 2005 Zultys Technologies.
* Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
*
* Based on original work by
* Matt Porter <mporter@kernel.crashing.org>
* Copyright 2004 MontaVista Software, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/ethtool.h>
#include <asm/io.h>
#include "ibm_emac_core.h"
#include "ibm_emac_debug.h"
/* RGMIIx_FER */
#define RGMII_FER_MASK(idx) (0x7 << ((idx) * 4))
#define RGMII_FER_RTBI(idx) (0x4 << ((idx) * 4))
#define RGMII_FER_RGMII(idx) (0x5 << ((idx) * 4))
#define RGMII_FER_TBI(idx) (0x6 << ((idx) * 4))
#define RGMII_FER_GMII(idx) (0x7 << ((idx) * 4))
/* RGMIIx_SSR */
#define RGMII_SSR_MASK(idx) (0x7 << ((idx) * 8))
#define RGMII_SSR_100(idx) (0x2 << ((idx) * 8))
#define RGMII_SSR_1000(idx) (0x4 << ((idx) * 8))
/* RGMII bridge supports only GMII/TBI and RGMII/RTBI PHYs */
static inline int rgmii_valid_mode(int phy_mode)
{
return phy_mode == PHY_MODE_GMII ||
phy_mode == PHY_MODE_RGMII ||
phy_mode == PHY_MODE_TBI ||
phy_mode == PHY_MODE_RTBI;
}
static inline const char *rgmii_mode_name(int mode)
{
switch (mode) {
case PHY_MODE_RGMII:
return "RGMII";
case PHY_MODE_TBI:
return "TBI";
case PHY_MODE_GMII:
return "GMII";
case PHY_MODE_RTBI:
return "RTBI";
default:
BUG();
}
}
static inline u32 rgmii_mode_mask(int mode, int input)
{
switch (mode) {
case PHY_MODE_RGMII:
return RGMII_FER_RGMII(input);
case PHY_MODE_TBI:
return RGMII_FER_TBI(input);
case PHY_MODE_GMII:
return RGMII_FER_GMII(input);
case PHY_MODE_RTBI:
return RGMII_FER_RTBI(input);
default:
BUG();
}
}
static int __init rgmii_init(struct ocp_device *ocpdev, int input, int mode)
{
struct ibm_ocp_rgmii *dev = ocp_get_drvdata(ocpdev);
struct rgmii_regs *p;
RGMII_DBG("%d: init(%d, %d)" NL, ocpdev->def->index, input, mode);
if (!dev) {
dev = kzalloc(sizeof(struct ibm_ocp_rgmii), GFP_KERNEL);
if (!dev) {
printk(KERN_ERR
"rgmii%d: couldn't allocate device structure!\n",
ocpdev->def->index);
return -ENOMEM;
}
p = (struct rgmii_regs *)ioremap(ocpdev->def->paddr,
sizeof(struct rgmii_regs));
if (!p) {
printk(KERN_ERR
"rgmii%d: could not ioremap device registers!\n",
ocpdev->def->index);
kfree(dev);
return -ENOMEM;
}
dev->base = p;
ocp_set_drvdata(ocpdev, dev);
/* Disable all inputs by default */
out_be32(&p->fer, 0);
} else
p = dev->base;
/* Enable this input */
out_be32(&p->fer, in_be32(&p->fer) | rgmii_mode_mask(mode, input));
printk(KERN_NOTICE "rgmii%d: input %d in %s mode\n",
ocpdev->def->index, input, rgmii_mode_name(mode));
++dev->users;
return 0;
}
int __init rgmii_attach(void *emac)
{
struct ocp_enet_private *dev = emac;
struct ocp_func_emac_data *emacdata = dev->def->additions;
/* Check if we need to attach to a RGMII */
if (emacdata->rgmii_idx >= 0 && rgmii_valid_mode(emacdata->phy_mode)) {
dev->rgmii_input = emacdata->rgmii_mux;
dev->rgmii_dev =
ocp_find_device(OCP_VENDOR_IBM, OCP_FUNC_RGMII,
emacdata->rgmii_idx);
if (!dev->rgmii_dev) {
printk(KERN_ERR "emac%d: unknown rgmii%d!\n",
dev->def->index, emacdata->rgmii_idx);
return -ENODEV;
}
if (rgmii_init
(dev->rgmii_dev, dev->rgmii_input, emacdata->phy_mode)) {
printk(KERN_ERR
"emac%d: rgmii%d initialization failed!\n",
dev->def->index, emacdata->rgmii_idx);
return -ENODEV;
}
}
return 0;
}
void rgmii_set_speed(struct ocp_device *ocpdev, int input, int speed)
{
struct ibm_ocp_rgmii *dev = ocp_get_drvdata(ocpdev);
u32 ssr = in_be32(&dev->base->ssr) & ~RGMII_SSR_MASK(input);
RGMII_DBG("%d: speed(%d, %d)" NL, ocpdev->def->index, input, speed);
if (speed == SPEED_1000)
ssr |= RGMII_SSR_1000(input);
else if (speed == SPEED_100)
ssr |= RGMII_SSR_100(input);
out_be32(&dev->base->ssr, ssr);
}
void __exit __rgmii_fini(struct ocp_device *ocpdev, int input)
{
struct ibm_ocp_rgmii *dev = ocp_get_drvdata(ocpdev);
BUG_ON(!dev || dev->users == 0);
RGMII_DBG("%d: fini(%d)" NL, ocpdev->def->index, input);
/* Disable this input */
out_be32(&dev->base->fer,
in_be32(&dev->base->fer) & ~RGMII_FER_MASK(input));
if (!--dev->users) {
/* Free everything if this is the last user */
ocp_set_drvdata(ocpdev, NULL);
iounmap((void *)dev->base);
kfree(dev);
}
}
int __rgmii_get_regs_len(struct ocp_device *ocpdev)
{
return sizeof(struct emac_ethtool_regs_subhdr) +
sizeof(struct rgmii_regs);
}
void *rgmii_dump_regs(struct ocp_device *ocpdev, void *buf)
{
struct ibm_ocp_rgmii *dev = ocp_get_drvdata(ocpdev);
struct emac_ethtool_regs_subhdr *hdr = buf;
struct rgmii_regs *regs = (struct rgmii_regs *)(hdr + 1);
hdr->version = 0;
hdr->index = ocpdev->def->index;
memcpy_fromio(regs, dev->base, sizeof(struct rgmii_regs));
return regs + 1;
}
/* /*
* Defines for the IBM RGMII bridge * drivers/net/ibm_emac/ibm_emac_rgmii.c
*
* Driver for PowerPC 4xx on-chip ethernet controller, RGMII bridge support.
* *
* Based on ocp_zmii.h/ibm_emac_zmii.h * Based on ocp_zmii.h/ibm_emac_zmii.h
* Armin Kuster akuster@mvista.com * Armin Kuster akuster@mvista.com
...@@ -7,6 +9,9 @@ ...@@ -7,6 +9,9 @@
* Copyright 2004 MontaVista Software, Inc. * Copyright 2004 MontaVista Software, Inc.
* Matt Porter <mporter@kernel.crashing.org> * Matt Porter <mporter@kernel.crashing.org>
* *
* Copyright (c) 2004, 2005 Zultys Technologies.
* Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
*
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the * under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your * Free Software Foundation; either version 2 of the License, or (at your
...@@ -19,47 +24,42 @@ ...@@ -19,47 +24,42 @@
#include <linux/config.h> #include <linux/config.h>
/* RGMII bridge */ /* RGMII bridge */
typedef struct rgmii_regs { struct rgmii_regs {
u32 fer; /* Function enable register */ u32 fer; /* Function enable register */
u32 ssr; /* Speed select register */ u32 ssr; /* Speed select register */
} rgmii_t; };
#define RGMII_INPUTS 4
/* RGMII device */ /* RGMII device */
struct ibm_ocp_rgmii { struct ibm_ocp_rgmii {
struct rgmii_regs *base; struct rgmii_regs *base;
int mode[RGMII_INPUTS];
int users; /* number of EMACs using this RGMII bridge */ int users; /* number of EMACs using this RGMII bridge */
}; };
/* Fuctional Enable Reg */ #ifdef CONFIG_IBM_EMAC_RGMII
#define RGMII_FER_MASK(x) (0x00000007 << (4*x)) int rgmii_attach(void *emac) __init;
#define RGMII_RTBI 0x00000004
#define RGMII_RGMII 0x00000005
#define RGMII_TBI 0x00000006
#define RGMII_GMII 0x00000007
/* Speed Selection reg */
#define RGMII_SP2_100 0x00000002 void __rgmii_fini(struct ocp_device *ocpdev, int input) __exit;
#define RGMII_SP2_1000 0x00000004 static inline void rgmii_fini(struct ocp_device *ocpdev, int input)
#define RGMII_SP3_100 0x00000200 {
#define RGMII_SP3_1000 0x00000400 if (ocpdev)
__rgmii_fini(ocpdev, input);
}
#define RGMII_MII2_SPDMASK 0x00000007 void rgmii_set_speed(struct ocp_device *ocpdev, int input, int speed);
#define RGMII_MII3_SPDMASK 0x00000700
#define RGMII_MII2_100MB RGMII_SP2_100 & ~RGMII_SP2_1000 int __rgmii_get_regs_len(struct ocp_device *ocpdev);
#define RGMII_MII2_1000MB RGMII_SP2_1000 & ~RGMII_SP2_100 static inline int rgmii_get_regs_len(struct ocp_device *ocpdev)
#define RGMII_MII2_10MB ~(RGMII_SP2_100 | RGMII_SP2_1000) {
#define RGMII_MII3_100MB RGMII_SP3_100 & ~RGMII_SP3_1000 return ocpdev ? __rgmii_get_regs_len(ocpdev) : 0;
#define RGMII_MII3_1000MB RGMII_SP3_1000 & ~RGMII_SP3_100 }
#define RGMII_MII3_10MB ~(RGMII_SP3_100 | RGMII_SP3_1000)
#define RTBI 0 void *rgmii_dump_regs(struct ocp_device *ocpdev, void *buf);
#define RGMII 1 #else
#define TBI 2 # define rgmii_attach(x) 0
#define GMII 3 # define rgmii_fini(x,y) ((void)0)
# define rgmii_set_speed(x,y,z) ((void)0)
# define rgmii_get_regs_len(x) 0
# define rgmii_dump_regs(x,buf) (buf)
#endif /* !CONFIG_IBM_EMAC_RGMII */
#endif /* _IBM_EMAC_RGMII_H_ */ #endif /* _IBM_EMAC_RGMII_H_ */
/*
* drivers/net/ibm_emac/ibm_emac_tah.c
*
* Driver for PowerPC 4xx on-chip ethernet controller, TAH support.
*
* Copyright 2004 MontaVista Software, Inc.
* Matt Porter <mporter@kernel.crashing.org>
*
* Copyright (c) 2005 Eugene Surovegin <ebs@ebshome.net>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/config.h>
#include <asm/io.h>
#include "ibm_emac_core.h"
static int __init tah_init(struct ocp_device *ocpdev)
{
struct tah_regs *p;
if (ocp_get_drvdata(ocpdev)) {
printk(KERN_ERR "tah%d: already in use!\n", ocpdev->def->index);
return -EBUSY;
}
/* Initialize TAH and enable IPv4 checksum verification, no TSO yet */
p = (struct tah_regs *)ioremap(ocpdev->def->paddr, sizeof(*p));
if (!p) {
printk(KERN_ERR "tah%d: could not ioremap device registers!\n",
ocpdev->def->index);
return -ENOMEM;
}
ocp_set_drvdata(ocpdev, p);
__tah_reset(ocpdev);
return 0;
}
int __init tah_attach(void *emac)
{
struct ocp_enet_private *dev = emac;
struct ocp_func_emac_data *emacdata = dev->def->additions;
/* Check if we need to attach to a TAH */
if (emacdata->tah_idx >= 0) {
dev->tah_dev = ocp_find_device(OCP_ANY_ID, OCP_FUNC_TAH,
emacdata->tah_idx);
if (!dev->tah_dev) {
printk(KERN_ERR "emac%d: unknown tah%d!\n",
dev->def->index, emacdata->tah_idx);
return -ENODEV;
}
if (tah_init(dev->tah_dev)) {
printk(KERN_ERR
"emac%d: tah%d initialization failed!\n",
dev->def->index, emacdata->tah_idx);
return -ENODEV;
}
}
return 0;
}
void __exit __tah_fini(struct ocp_device *ocpdev)
{
struct tah_regs *p = ocp_get_drvdata(ocpdev);
BUG_ON(!p);
ocp_set_drvdata(ocpdev, NULL);
iounmap((void *)p);
}
void __tah_reset(struct ocp_device *ocpdev)
{
struct tah_regs *p = ocp_get_drvdata(ocpdev);
int n;
/* Reset TAH */
out_be32(&p->mr, TAH_MR_SR);
n = 100;
while ((in_be32(&p->mr) & TAH_MR_SR) && n)
--n;
if (unlikely(!n))
printk(KERN_ERR "tah%d: reset timeout\n", ocpdev->def->index);
/* 10KB TAH TX FIFO accomodates the max MTU of 9000 */
out_be32(&p->mr,
TAH_MR_CVR | TAH_MR_ST_768 | TAH_MR_TFS_10KB | TAH_MR_DTFP |
TAH_MR_DIG);
}
int __tah_get_regs_len(struct ocp_device *ocpdev)
{
return sizeof(struct emac_ethtool_regs_subhdr) +
sizeof(struct tah_regs);
}
void *tah_dump_regs(struct ocp_device *ocpdev, void *buf)
{
struct tah_regs *dev = ocp_get_drvdata(ocpdev);
struct emac_ethtool_regs_subhdr *hdr = buf;
struct tah_regs *regs = (struct tah_regs *)(hdr + 1);
hdr->version = 0;
hdr->index = ocpdev->def->index;
memcpy_fromio(regs, dev, sizeof(struct tah_regs));
return regs + 1;
}
/* /*
* Defines for the IBM TAH * drivers/net/ibm_emac/ibm_emac_tah.h
*
* Driver for PowerPC 4xx on-chip ethernet controller, TAH support.
* *
* Copyright 2004 MontaVista Software, Inc. * Copyright 2004 MontaVista Software, Inc.
* Matt Porter <mporter@kernel.crashing.org> * Matt Porter <mporter@kernel.crashing.org>
* *
* Copyright (c) 2005 Eugene Surovegin <ebs@ebshome.net>
*
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the * under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your * Free Software Foundation; either version 2 of the License, or (at your
...@@ -13,36 +17,72 @@ ...@@ -13,36 +17,72 @@
#ifndef _IBM_EMAC_TAH_H #ifndef _IBM_EMAC_TAH_H
#define _IBM_EMAC_TAH_H #define _IBM_EMAC_TAH_H
#include <linux/config.h>
#include <linux/init.h>
#include <asm/ocp.h>
/* TAH */ /* TAH */
typedef struct tah_regs { struct tah_regs {
u32 tah_revid; u32 revid;
u32 pad[3]; u32 pad[3];
u32 tah_mr; u32 mr;
u32 tah_ssr0; u32 ssr0;
u32 tah_ssr1; u32 ssr1;
u32 tah_ssr2; u32 ssr2;
u32 tah_ssr3; u32 ssr3;
u32 tah_ssr4; u32 ssr4;
u32 tah_ssr5; u32 ssr5;
u32 tah_tsr; u32 tsr;
} tah_t; };
/* TAH engine */ /* TAH engine */
#define TAH_MR_CVR 0x80000000 #define TAH_MR_CVR 0x80000000
#define TAH_MR_SR 0x40000000 #define TAH_MR_SR 0x40000000
#define TAH_MR_ST_256 0x01000000 #define TAH_MR_ST_256 0x01000000
#define TAH_MR_ST_512 0x02000000 #define TAH_MR_ST_512 0x02000000
#define TAH_MR_ST_768 0x03000000 #define TAH_MR_ST_768 0x03000000
#define TAH_MR_ST_1024 0x04000000 #define TAH_MR_ST_1024 0x04000000
#define TAH_MR_ST_1280 0x05000000 #define TAH_MR_ST_1280 0x05000000
#define TAH_MR_ST_1536 0x06000000 #define TAH_MR_ST_1536 0x06000000
#define TAH_MR_TFS_16KB 0x00000000 #define TAH_MR_TFS_16KB 0x00000000
#define TAH_MR_TFS_2KB 0x00200000 #define TAH_MR_TFS_2KB 0x00200000
#define TAH_MR_TFS_4KB 0x00400000 #define TAH_MR_TFS_4KB 0x00400000
#define TAH_MR_TFS_6KB 0x00600000 #define TAH_MR_TFS_6KB 0x00600000
#define TAH_MR_TFS_8KB 0x00800000 #define TAH_MR_TFS_8KB 0x00800000
#define TAH_MR_TFS_10KB 0x00a00000 #define TAH_MR_TFS_10KB 0x00a00000
#define TAH_MR_DTFP 0x00100000 #define TAH_MR_DTFP 0x00100000
#define TAH_MR_DIG 0x00080000 #define TAH_MR_DIG 0x00080000
#ifdef CONFIG_IBM_EMAC_TAH
int tah_attach(void *emac) __init;
void __tah_fini(struct ocp_device *ocpdev) __exit;
static inline void tah_fini(struct ocp_device *ocpdev)
{
if (ocpdev)
__tah_fini(ocpdev);
}
void __tah_reset(struct ocp_device *ocpdev);
static inline void tah_reset(struct ocp_device *ocpdev)
{
if (ocpdev)
__tah_reset(ocpdev);
}
int __tah_get_regs_len(struct ocp_device *ocpdev);
static inline int tah_get_regs_len(struct ocp_device *ocpdev)
{
return ocpdev ? __tah_get_regs_len(ocpdev) : 0;
}
void *tah_dump_regs(struct ocp_device *ocpdev, void *buf);
#else
# define tah_attach(x) 0
# define tah_fini(x) ((void)0)
# define tah_reset(x) ((void)0)
# define tah_get_regs_len(x) 0
# define tah_dump_regs(x,buf) (buf)
#endif /* !CONFIG_IBM_EMAC_TAH */
#endif /* _IBM_EMAC_TAH_H */ #endif /* _IBM_EMAC_TAH_H */
/*
* drivers/net/ibm_emac/ibm_emac_zmii.c
*
* Driver for PowerPC 4xx on-chip ethernet controller, ZMII bridge support.
*
* Copyright (c) 2004, 2005 Zultys Technologies.
* Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
*
* Based on original work by
* Armin Kuster <akuster@mvista.com>
* Copyright 2001 MontaVista Softare Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/ethtool.h>
#include <asm/io.h>
#include "ibm_emac_core.h"
#include "ibm_emac_debug.h"
/* ZMIIx_FER */
#define ZMII_FER_MDI(idx) (0x80000000 >> ((idx) * 4))
#define ZMII_FER_MDI_ALL (ZMII_FER_MDI(0) | ZMII_FER_MDI(1) | \
ZMII_FER_MDI(2) | ZMII_FER_MDI(3))
#define ZMII_FER_SMII(idx) (0x40000000 >> ((idx) * 4))
#define ZMII_FER_RMII(idx) (0x20000000 >> ((idx) * 4))
#define ZMII_FER_MII(idx) (0x10000000 >> ((idx) * 4))
/* ZMIIx_SSR */
#define ZMII_SSR_SCI(idx) (0x40000000 >> ((idx) * 4))
#define ZMII_SSR_FSS(idx) (0x20000000 >> ((idx) * 4))
#define ZMII_SSR_SP(idx) (0x10000000 >> ((idx) * 4))
/* ZMII only supports MII, RMII and SMII
* we also support autodetection for backward compatibility
*/
static inline int zmii_valid_mode(int mode)
{
return mode == PHY_MODE_MII ||
mode == PHY_MODE_RMII ||
mode == PHY_MODE_SMII ||
mode == PHY_MODE_NA;
}
static inline const char *zmii_mode_name(int mode)
{
switch (mode) {
case PHY_MODE_MII:
return "MII";
case PHY_MODE_RMII:
return "RMII";
case PHY_MODE_SMII:
return "SMII";
default:
BUG();
}
}
static inline u32 zmii_mode_mask(int mode, int input)
{
switch (mode) {
case PHY_MODE_MII:
return ZMII_FER_MII(input);
case PHY_MODE_RMII:
return ZMII_FER_RMII(input);
case PHY_MODE_SMII:
return ZMII_FER_SMII(input);
default:
return 0;
}
}
static int __init zmii_init(struct ocp_device *ocpdev, int input, int *mode)
{
struct ibm_ocp_zmii *dev = ocp_get_drvdata(ocpdev);
struct zmii_regs *p;
ZMII_DBG("%d: init(%d, %d)" NL, ocpdev->def->index, input, *mode);
if (!dev) {
dev = kzalloc(sizeof(struct ibm_ocp_zmii), GFP_KERNEL);
if (!dev) {
printk(KERN_ERR
"zmii%d: couldn't allocate device structure!\n",
ocpdev->def->index);
return -ENOMEM;
}
dev->mode = PHY_MODE_NA;
p = (struct zmii_regs *)ioremap(ocpdev->def->paddr,
sizeof(struct zmii_regs));
if (!p) {
printk(KERN_ERR
"zmii%d: could not ioremap device registers!\n",
ocpdev->def->index);
kfree(dev);
return -ENOMEM;
}
dev->base = p;
ocp_set_drvdata(ocpdev, dev);
/* We may need FER value for autodetection later */
dev->fer_save = in_be32(&p->fer);
/* Disable all inputs by default */
out_be32(&p->fer, 0);
} else
p = dev->base;
if (!zmii_valid_mode(*mode)) {
/* Probably an EMAC connected to RGMII,
* but it still may need ZMII for MDIO
*/
goto out;
}
/* Autodetect ZMII mode if not specified.
* This is only for backward compatibility with the old driver.
* Please, always specify PHY mode in your board port to avoid
* any surprises.
*/
if (dev->mode == PHY_MODE_NA) {
if (*mode == PHY_MODE_NA) {
u32 r = dev->fer_save;
ZMII_DBG("%d: autodetecting mode, FER = 0x%08x" NL,
ocpdev->def->index, r);
if (r & (ZMII_FER_MII(0) | ZMII_FER_MII(1)))
dev->mode = PHY_MODE_MII;
else if (r & (ZMII_FER_RMII(0) | ZMII_FER_RMII(1)))
dev->mode = PHY_MODE_RMII;
else
dev->mode = PHY_MODE_SMII;
} else
dev->mode = *mode;
printk(KERN_NOTICE "zmii%d: bridge in %s mode\n",
ocpdev->def->index, zmii_mode_name(dev->mode));
} else {
/* All inputs must use the same mode */
if (*mode != PHY_MODE_NA && *mode != dev->mode) {
printk(KERN_ERR
"zmii%d: invalid mode %d specified for input %d\n",
ocpdev->def->index, *mode, input);
return -EINVAL;
}
}
/* Report back correct PHY mode,
* it may be used during PHY initialization.
*/
*mode = dev->mode;
/* Enable this input */
out_be32(&p->fer, in_be32(&p->fer) | zmii_mode_mask(dev->mode, input));
out:
++dev->users;
return 0;
}
int __init zmii_attach(void *emac)
{
struct ocp_enet_private *dev = emac;
struct ocp_func_emac_data *emacdata = dev->def->additions;
if (emacdata->zmii_idx >= 0) {
dev->zmii_input = emacdata->zmii_mux;
dev->zmii_dev =
ocp_find_device(OCP_VENDOR_IBM, OCP_FUNC_ZMII,
emacdata->zmii_idx);
if (!dev->zmii_dev) {
printk(KERN_ERR "emac%d: unknown zmii%d!\n",
dev->def->index, emacdata->zmii_idx);
return -ENODEV;
}
if (zmii_init
(dev->zmii_dev, dev->zmii_input, &emacdata->phy_mode)) {
printk(KERN_ERR
"emac%d: zmii%d initialization failed!\n",
dev->def->index, emacdata->zmii_idx);
return -ENODEV;
}
}
return 0;
}
void __zmii_enable_mdio(struct ocp_device *ocpdev, int input)
{
struct ibm_ocp_zmii *dev = ocp_get_drvdata(ocpdev);
u32 fer = in_be32(&dev->base->fer) & ~ZMII_FER_MDI_ALL;
ZMII_DBG2("%d: mdio(%d)" NL, ocpdev->def->index, input);
out_be32(&dev->base->fer, fer | ZMII_FER_MDI(input));
}
void __zmii_set_speed(struct ocp_device *ocpdev, int input, int speed)
{
struct ibm_ocp_zmii *dev = ocp_get_drvdata(ocpdev);
u32 ssr = in_be32(&dev->base->ssr);
ZMII_DBG("%d: speed(%d, %d)" NL, ocpdev->def->index, input, speed);
if (speed == SPEED_100)
ssr |= ZMII_SSR_SP(input);
else
ssr &= ~ZMII_SSR_SP(input);
out_be32(&dev->base->ssr, ssr);
}
void __exit __zmii_fini(struct ocp_device *ocpdev, int input)
{
struct ibm_ocp_zmii *dev = ocp_get_drvdata(ocpdev);
BUG_ON(!dev || dev->users == 0);
ZMII_DBG("%d: fini(%d)" NL, ocpdev->def->index, input);
/* Disable this input */
out_be32(&dev->base->fer,
in_be32(&dev->base->fer) & ~zmii_mode_mask(dev->mode, input));
if (!--dev->users) {
/* Free everything if this is the last user */
ocp_set_drvdata(ocpdev, NULL);
iounmap((void *)dev->base);
kfree(dev);
}
}
int __zmii_get_regs_len(struct ocp_device *ocpdev)
{
return sizeof(struct emac_ethtool_regs_subhdr) +
sizeof(struct zmii_regs);
}
void *zmii_dump_regs(struct ocp_device *ocpdev, void *buf)
{
struct ibm_ocp_zmii *dev = ocp_get_drvdata(ocpdev);
struct emac_ethtool_regs_subhdr *hdr = buf;
struct zmii_regs *regs = (struct zmii_regs *)(hdr + 1);
hdr->version = 0;
hdr->index = ocpdev->def->index;
memcpy_fromio(regs, dev->base, sizeof(struct zmii_regs));
return regs + 1;
}
/* /*
* ocp_zmii.h * drivers/net/ibm_emac/ibm_emac_zmii.h
* *
* Defines for the IBM ZMII bridge * Driver for PowerPC 4xx on-chip ethernet controller, ZMII bridge support.
* *
* Armin Kuster akuster@mvista.com * Copyright (c) 2004, 2005 Zultys Technologies.
* Dec, 2001 * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
* *
* Copyright 2001 MontaVista Softare Inc. * Based on original work by
* Armin Kuster <akuster@mvista.com>
* Copyright 2001 MontaVista Softare Inc.
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the * under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your * Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. * option) any later version.
*
*/ */
#ifndef _IBM_EMAC_ZMII_H_ #ifndef _IBM_EMAC_ZMII_H_
#define _IBM_EMAC_ZMII_H_ #define _IBM_EMAC_ZMII_H_
#include <linux/config.h> #include <linux/config.h>
#include <linux/init.h>
#include <asm/ocp.h>
/* ZMII bridge registers */ /* ZMII bridge registers */
struct zmii_regs { struct zmii_regs {
...@@ -26,68 +30,54 @@ struct zmii_regs { ...@@ -26,68 +30,54 @@ struct zmii_regs {
u32 smiirs; /* SMII status reg */ u32 smiirs; /* SMII status reg */
}; };
#define ZMII_INPUTS 4
/* ZMII device */ /* ZMII device */
struct ibm_ocp_zmii { struct ibm_ocp_zmii {
struct zmii_regs *base; struct zmii_regs *base;
int mode[ZMII_INPUTS]; int mode; /* subset of PHY_MODE_XXXX */
int users; /* number of EMACs using this ZMII bridge */ int users; /* number of EMACs using this ZMII bridge */
u32 fer_save; /* FER value left by firmware */
}; };
/* Fuctional Enable Reg */ #ifdef CONFIG_IBM_EMAC_ZMII
int zmii_attach(void *emac) __init;
#define ZMII_FER_MASK(x) (0xf0000000 >> (4*x))
#define ZMII_MDI0 0x80000000
#define ZMII_SMII0 0x40000000
#define ZMII_RMII0 0x20000000
#define ZMII_MII0 0x10000000
#define ZMII_MDI1 0x08000000
#define ZMII_SMII1 0x04000000
#define ZMII_RMII1 0x02000000
#define ZMII_MII1 0x01000000
#define ZMII_MDI2 0x00800000
#define ZMII_SMII2 0x00400000
#define ZMII_RMII2 0x00200000
#define ZMII_MII2 0x00100000
#define ZMII_MDI3 0x00080000
#define ZMII_SMII3 0x00040000
#define ZMII_RMII3 0x00020000
#define ZMII_MII3 0x00010000
/* Speed Selection reg */ void __zmii_fini(struct ocp_device *ocpdev, int input) __exit;
static inline void zmii_fini(struct ocp_device *ocpdev, int input)
{
if (ocpdev)
__zmii_fini(ocpdev, input);
}
#define ZMII_SCI0 0x40000000 void __zmii_enable_mdio(struct ocp_device *ocpdev, int input);
#define ZMII_FSS0 0x20000000 static inline void zmii_enable_mdio(struct ocp_device *ocpdev, int input)
#define ZMII_SP0 0x10000000 {
#define ZMII_SCI1 0x04000000 if (ocpdev)
#define ZMII_FSS1 0x02000000 __zmii_enable_mdio(ocpdev, input);
#define ZMII_SP1 0x01000000 }
#define ZMII_SCI2 0x00400000
#define ZMII_FSS2 0x00200000
#define ZMII_SP2 0x00100000
#define ZMII_SCI3 0x00040000
#define ZMII_FSS3 0x00020000
#define ZMII_SP3 0x00010000
#define ZMII_MII0_100MB ZMII_SP0 void __zmii_set_speed(struct ocp_device *ocpdev, int input, int speed);
#define ZMII_MII0_10MB ~ZMII_SP0 static inline void zmii_set_speed(struct ocp_device *ocpdev, int input,
#define ZMII_MII1_100MB ZMII_SP1 int speed)
#define ZMII_MII1_10MB ~ZMII_SP1 {
#define ZMII_MII2_100MB ZMII_SP2 if (ocpdev)
#define ZMII_MII2_10MB ~ZMII_SP2 __zmii_set_speed(ocpdev, input, speed);
#define ZMII_MII3_100MB ZMII_SP3 }
#define ZMII_MII3_10MB ~ZMII_SP3
/* SMII Status reg */ int __zmii_get_regs_len(struct ocp_device *ocpdev);
static inline int zmii_get_regs_len(struct ocp_device *ocpdev)
{
return ocpdev ? __zmii_get_regs_len(ocpdev) : 0;
}
#define ZMII_STS0 0xFF000000 /* EMAC0 smii status mask */ void *zmii_dump_regs(struct ocp_device *ocpdev, void *buf);
#define ZMII_STS1 0x00FF0000 /* EMAC1 smii status mask */
#define SMII 0 #else
#define RMII 1 # define zmii_attach(x) 0
#define MII 2 # define zmii_fini(x,y) ((void)0)
#define MDI 3 # define zmii_enable_mdio(x,y) ((void)0)
# define zmii_set_speed(x,y,z) ((void)0)
# define zmii_get_regs_len(x) 0
# define zmii_dump_regs(x,buf) (buf)
#endif /* !CONFIG_IBM_EMAC_ZMII */
#endif /* _IBM_EMAC_ZMII_H_ */ #endif /* _IBM_EMAC_ZMII_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment