mirror of
https://github.com/FEX-Emu/linux.git
synced 2024-12-02 20:59:30 +00:00
Device tree aware EMAC driver
Based on BenH's earlier work, this is a new version of the EMAC driver for the built-in ethernet found on PowerPC 4xx embedded CPUs. The same ASIC is also found in the Axon bridge chip. This new version is designed to work in the arch/powerpc tree, using the device tree to probe the device, rather than the old and ugly arch/ppc OCP layer. This driver is designed to sit alongside the old driver (that lies in drivers/net/ibm_emac and this one in drivers/net/ibm_newemac). The old driver is left in place to support arch/ppc until arch/ppc itself reaches its final demise (not too long now, with luck). This driver still has a number of things that could do with cleaning up, but I think they can be fixed up after merging. Specifically: - Should be adjusted to properly use the dma mapping API. Axon needs this. - Probe logic needs reworking, in conjuction with the general probing code for of_platform devices. The dependencies here between EMAC, MAL, ZMII etc. make this complicated. At present, it usually works, because we initialize and register the sub-drivers before the EMAC driver itself, and (being in driver code) runs after the devices themselves have been instantiated from the device tree. Signed-off-by: David Gibson <david@gibson.dropbear.id.au> Signed-off-by: Jeff Garzik <jeff@garzik.org>
This commit is contained in:
parent
03233b90b0
commit
1d3bb99648
@ -1824,6 +1824,162 @@ platforms are moved over to use the flattened-device-tree model.
|
||||
fsl,has-rstcr;
|
||||
};
|
||||
|
||||
|
||||
h) 4xx/Axon EMAC ethernet nodes
|
||||
|
||||
The EMAC ethernet controller in IBM and AMCC 4xx chips, and also
|
||||
the Axon bridge. To operate this needs to interact with a ths
|
||||
special McMAL DMA controller, and sometimes an RGMII or ZMII
|
||||
interface. In addition to the nodes and properties described
|
||||
below, the node for the OPB bus on which the EMAC sits must have a
|
||||
correct clock-frequency property.
|
||||
|
||||
i) The EMAC node itself
|
||||
|
||||
Required properties:
|
||||
- device_type : "network"
|
||||
|
||||
- compatible : compatible list, contains 2 entries, first is
|
||||
"ibm,emac-CHIP" where CHIP is the host ASIC (440gx,
|
||||
405gp, Axon) and second is either "ibm,emac" or
|
||||
"ibm,emac4". For Axon, thus, we have: "ibm,emac-axon",
|
||||
"ibm,emac4"
|
||||
- interrupts : <interrupt mapping for EMAC IRQ and WOL IRQ>
|
||||
- interrupt-parent : optional, if needed for interrupt mapping
|
||||
- reg : <registers mapping>
|
||||
- local-mac-address : 6 bytes, MAC address
|
||||
- mal-device : phandle of the associated McMAL node
|
||||
- mal-tx-channel : 1 cell, index of the tx channel on McMAL associated
|
||||
with this EMAC
|
||||
- mal-rx-channel : 1 cell, index of the rx channel on McMAL associated
|
||||
with this EMAC
|
||||
- cell-index : 1 cell, hardware index of the EMAC cell on a given
|
||||
ASIC (typically 0x0 and 0x1 for EMAC0 and EMAC1 on
|
||||
each Axon chip)
|
||||
- max-frame-size : 1 cell, maximum frame size supported in bytes
|
||||
- rx-fifo-size : 1 cell, Rx fifo size in bytes for 10 and 100 Mb/sec
|
||||
operations.
|
||||
For Axon, 2048
|
||||
- tx-fifo-size : 1 cell, Tx fifo size in bytes for 10 and 100 Mb/sec
|
||||
operations.
|
||||
For Axon, 2048.
|
||||
- fifo-entry-size : 1 cell, size of a fifo entry (used to calculate
|
||||
thresholds).
|
||||
For Axon, 0x00000010
|
||||
- mal-burst-size : 1 cell, MAL burst size (used to calculate thresholds)
|
||||
in bytes.
|
||||
For Axon, 0x00000100 (I think ...)
|
||||
- phy-mode : string, mode of operations of the PHY interface.
|
||||
Supported values are: "mii", "rmii", "smii", "rgmii",
|
||||
"tbi", "gmii", rtbi", "sgmii".
|
||||
For Axon on CAB, it is "rgmii"
|
||||
- mdio-device : 1 cell, required iff using shared MDIO registers
|
||||
(440EP). phandle of the EMAC to use to drive the
|
||||
MDIO lines for the PHY used by this EMAC.
|
||||
- zmii-device : 1 cell, required iff connected to a ZMII. phandle of
|
||||
the ZMII device node
|
||||
- zmii-channel : 1 cell, required iff connected to a ZMII. Which ZMII
|
||||
channel or 0xffffffff if ZMII is only used for MDIO.
|
||||
- rgmii-device : 1 cell, required iff connected to an RGMII. phandle
|
||||
of the RGMII device node.
|
||||
For Axon: phandle of plb5/plb4/opb/rgmii
|
||||
- rgmii-channel : 1 cell, required iff connected to an RGMII. Which
|
||||
RGMII channel is used by this EMAC.
|
||||
Fox Axon: present, whatever value is appropriate for each
|
||||
EMAC, that is the content of the current (bogus) "phy-port"
|
||||
property.
|
||||
|
||||
Recommended properties:
|
||||
- linux,network-index : This is the intended "index" of this
|
||||
network device. This is used by the bootwrapper to interpret
|
||||
MAC addresses passed by the firmware when no information other
|
||||
than indices is available to associate an address with a device.
|
||||
|
||||
Optional properties:
|
||||
- phy-address : 1 cell, optional, MDIO address of the PHY. If absent,
|
||||
a search is performed.
|
||||
- phy-map : 1 cell, optional, bitmap of addresses to probe the PHY
|
||||
for, used if phy-address is absent. bit 0x00000001 is
|
||||
MDIO address 0.
|
||||
For Axon it can be absent, thouugh my current driver
|
||||
doesn't handle phy-address yet so for now, keep
|
||||
0x00ffffff in it.
|
||||
- rx-fifo-size-gige : 1 cell, Rx fifo size in bytes for 1000 Mb/sec
|
||||
operations (if absent the value is the same as
|
||||
rx-fifo-size). For Axon, either absent or 2048.
|
||||
- tx-fifo-size-gige : 1 cell, Tx fifo size in bytes for 1000 Mb/sec
|
||||
operations (if absent the value is the same as
|
||||
tx-fifo-size). For Axon, either absent or 2048.
|
||||
- tah-device : 1 cell, optional. If connected to a TAH engine for
|
||||
offload, phandle of the TAH device node.
|
||||
- tah-channel : 1 cell, optional. If appropriate, channel used on the
|
||||
TAH engine.
|
||||
|
||||
Example:
|
||||
|
||||
EMAC0: ethernet@40000800 {
|
||||
linux,network-index = <0>;
|
||||
device_type = "network";
|
||||
compatible = "ibm,emac-440gp", "ibm,emac";
|
||||
interrupt-parent = <&UIC1>;
|
||||
interrupts = <1c 4 1d 4>;
|
||||
reg = <40000800 70>;
|
||||
local-mac-address = [00 04 AC E3 1B 1E];
|
||||
mal-device = <&MAL0>;
|
||||
mal-tx-channel = <0 1>;
|
||||
mal-rx-channel = <0>;
|
||||
cell-index = <0>;
|
||||
max-frame-size = <5dc>;
|
||||
rx-fifo-size = <1000>;
|
||||
tx-fifo-size = <800>;
|
||||
phy-mode = "rmii";
|
||||
phy-map = <00000001>;
|
||||
zmii-device = <&ZMII0>;
|
||||
zmii-channel = <0>;
|
||||
};
|
||||
|
||||
ii) McMAL node
|
||||
|
||||
Required properties:
|
||||
- device_type : "dma-controller"
|
||||
- compatible : compatible list, containing 2 entries, first is
|
||||
"ibm,mcmal-CHIP" where CHIP is the host ASIC (like
|
||||
emac) and the second is either "ibm,mcmal" or
|
||||
"ibm,mcmal2".
|
||||
For Axon, "ibm,mcmal-axon","ibm,mcmal2"
|
||||
- interrupts : <interrupt mapping for the MAL interrupts sources:
|
||||
5 sources: tx_eob, rx_eob, serr, txde, rxde>.
|
||||
For Axon: This is _different_ from the current
|
||||
firmware. We use the "delayed" interrupts for txeob
|
||||
and rxeob. Thus we end up with mapping those 5 MPIC
|
||||
interrupts, all level positive sensitive: 10, 11, 32,
|
||||
33, 34 (in decimal)
|
||||
- dcr-reg : < DCR registers range >
|
||||
- dcr-parent : if needed for dcr-reg
|
||||
- num-tx-chans : 1 cell, number of Tx channels
|
||||
- num-rx-chans : 1 cell, number of Rx channels
|
||||
|
||||
iii) ZMII node
|
||||
|
||||
Required properties:
|
||||
- compatible : compatible list, containing 2 entries, first is
|
||||
"ibm,zmii-CHIP" where CHIP is the host ASIC (like
|
||||
EMAC) and the second is "ibm,zmii".
|
||||
For Axon, there is no ZMII node.
|
||||
- reg : <registers mapping>
|
||||
|
||||
iv) RGMII node
|
||||
|
||||
Required properties:
|
||||
- compatible : compatible list, containing 2 entries, first is
|
||||
"ibm,rgmii-CHIP" where CHIP is the host ASIC (like
|
||||
EMAC) and the second is "ibm,rgmii".
|
||||
For Axon, "ibm,rgmii-axon","ibm,rgmii"
|
||||
- reg : <registers mapping>
|
||||
- revision : as provided by the RGMII new version register if
|
||||
available.
|
||||
For Axon: 0x0000012a
|
||||
|
||||
More devices will be defined as this spec matures.
|
||||
|
||||
VII - Specifying interrupt information for devices
|
||||
|
@ -38,8 +38,7 @@ config 440EP
|
||||
|
||||
config 440GP
|
||||
bool
|
||||
# Disabled until the new EMAC Driver is merged.
|
||||
# select IBM_NEW_EMAC_ZMII
|
||||
select IBM_NEW_EMAC_ZMII
|
||||
|
||||
config 440GX
|
||||
bool
|
||||
|
@ -10,6 +10,10 @@ config PPC_CELL_NATIVE
|
||||
select PPC_INDIRECT_IO
|
||||
select PPC_NATIVE
|
||||
select MPIC
|
||||
select IBM_NEW_EMAC_EMAC4
|
||||
select IBM_NEW_EMAC_RGMII
|
||||
select IBM_NEW_EMAC_ZMII #test only
|
||||
select IBM_NEW_EMAC_TAH #test only
|
||||
default n
|
||||
|
||||
config PPC_IBM_CELL_BLADE
|
||||
|
@ -1247,75 +1247,8 @@ config IBMVETH
|
||||
<file:Documentation/networking/net-modules.txt>. The module will
|
||||
be called ibmveth.
|
||||
|
||||
config IBM_EMAC
|
||||
tristate "PowerPC 4xx on-chip Ethernet support"
|
||||
depends on 4xx && !PPC_MERGE
|
||||
help
|
||||
This driver supports the PowerPC 4xx EMAC family of on-chip
|
||||
Ethernet controllers.
|
||||
|
||||
config IBM_EMAC_RXB
|
||||
int "Number of receive buffers"
|
||||
depends on IBM_EMAC
|
||||
default "128"
|
||||
|
||||
config IBM_EMAC_TXB
|
||||
int "Number of transmit buffers"
|
||||
depends on IBM_EMAC
|
||||
default "64"
|
||||
|
||||
config IBM_EMAC_POLL_WEIGHT
|
||||
int "MAL NAPI polling weight"
|
||||
depends on IBM_EMAC
|
||||
default "32"
|
||||
|
||||
config IBM_EMAC_RX_COPY_THRESHOLD
|
||||
int "RX skb copy threshold (bytes)"
|
||||
depends on IBM_EMAC
|
||||
default "256"
|
||||
|
||||
config IBM_EMAC_RX_SKB_HEADROOM
|
||||
int "Additional RX skb headroom (bytes)"
|
||||
depends on IBM_EMAC
|
||||
default "0"
|
||||
help
|
||||
Additional receive skb headroom. Note, that driver
|
||||
will always reserve at least 2 bytes to make IP header
|
||||
aligned, so usually 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 || 440GR)
|
||||
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
|
||||
source "drivers/net/ibm_emac/Kconfig"
|
||||
source "drivers/net/ibm_newemac/Kconfig"
|
||||
|
||||
config NET_PCI
|
||||
bool "EISA, VLB, PCI and on board controllers"
|
||||
|
@ -5,6 +5,7 @@
|
||||
obj-$(CONFIG_E1000) += e1000/
|
||||
obj-$(CONFIG_E1000E) += e1000e/
|
||||
obj-$(CONFIG_IBM_EMAC) += ibm_emac/
|
||||
obj-$(CONFIG_IBM_NEW_EMAC) += ibm_newemac/
|
||||
obj-$(CONFIG_IXGBE) += ixgbe/
|
||||
obj-$(CONFIG_IXGB) += ixgb/
|
||||
obj-$(CONFIG_IP1000) += ipg.o
|
||||
|
70
drivers/net/ibm_emac/Kconfig
Normal file
70
drivers/net/ibm_emac/Kconfig
Normal file
@ -0,0 +1,70 @@
|
||||
config IBM_EMAC
|
||||
tristate "PowerPC 4xx on-chip Ethernet support"
|
||||
depends on 4xx && !PPC_MERGE
|
||||
help
|
||||
This driver supports the PowerPC 4xx EMAC family of on-chip
|
||||
Ethernet controllers.
|
||||
|
||||
config IBM_EMAC_RXB
|
||||
int "Number of receive buffers"
|
||||
depends on IBM_EMAC
|
||||
default "128"
|
||||
|
||||
config IBM_EMAC_TXB
|
||||
int "Number of transmit buffers"
|
||||
depends on IBM_EMAC
|
||||
default "64"
|
||||
|
||||
config IBM_EMAC_POLL_WEIGHT
|
||||
int "MAL NAPI polling weight"
|
||||
depends on IBM_EMAC
|
||||
default "32"
|
||||
|
||||
config IBM_EMAC_RX_COPY_THRESHOLD
|
||||
int "RX skb copy threshold (bytes)"
|
||||
depends on IBM_EMAC
|
||||
default "256"
|
||||
|
||||
config IBM_EMAC_RX_SKB_HEADROOM
|
||||
int "Additional RX skb headroom (bytes)"
|
||||
depends on IBM_EMAC
|
||||
default "0"
|
||||
help
|
||||
Additional receive skb headroom. Note, that driver
|
||||
will always reserve at least 2 bytes to make IP header
|
||||
aligned, so usually 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 || 440GR)
|
||||
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
|
||||
|
63
drivers/net/ibm_newemac/Kconfig
Normal file
63
drivers/net/ibm_newemac/Kconfig
Normal file
@ -0,0 +1,63 @@
|
||||
config IBM_NEW_EMAC
|
||||
tristate "IBM EMAC Ethernet support"
|
||||
depends on PPC_DCR && PPC_MERGE
|
||||
help
|
||||
This driver supports the IBM EMAC family of Ethernet controllers
|
||||
typically found on 4xx embedded PowerPC chips, but also on the
|
||||
Axon southbridge for Cell.
|
||||
|
||||
config IBM_NEW_EMAC_RXB
|
||||
int "Number of receive buffers"
|
||||
depends on IBM_NEW_EMAC
|
||||
default "128"
|
||||
|
||||
config IBM_NEW_EMAC_TXB
|
||||
int "Number of transmit buffers"
|
||||
depends on IBM_NEW_EMAC
|
||||
default "64"
|
||||
|
||||
config IBM_NEW_EMAC_POLL_WEIGHT
|
||||
int "MAL NAPI polling weight"
|
||||
depends on IBM_NEW_EMAC
|
||||
default "32"
|
||||
|
||||
config IBM_NEW_EMAC_RX_COPY_THRESHOLD
|
||||
int "RX skb copy threshold (bytes)"
|
||||
depends on IBM_NEW_EMAC
|
||||
default "256"
|
||||
|
||||
config IBM_NEW_EMAC_RX_SKB_HEADROOM
|
||||
int "Additional RX skb headroom (bytes)"
|
||||
depends on IBM_NEW_EMAC
|
||||
default "0"
|
||||
help
|
||||
Additional receive skb headroom. Note, that driver
|
||||
will always reserve at least 2 bytes to make IP header
|
||||
aligned, so usually there is no need to add any additional
|
||||
headroom.
|
||||
|
||||
If unsure, set to 0.
|
||||
|
||||
config IBM_NEW_EMAC_DEBUG
|
||||
bool "Debugging"
|
||||
depends on IBM_NEW_EMAC
|
||||
default n
|
||||
|
||||
# The options below has to be select'ed by the respective
|
||||
# processor types or platforms
|
||||
|
||||
config IBM_NEW_EMAC_ZMII
|
||||
bool
|
||||
default n
|
||||
|
||||
config IBM_NEW_EMAC_RGMII
|
||||
bool
|
||||
default n
|
||||
|
||||
config IBM_NEW_EMAC_TAH
|
||||
bool
|
||||
default n
|
||||
|
||||
config IBM_NEW_EMAC_EMAC4
|
||||
bool
|
||||
default n
|
11
drivers/net/ibm_newemac/Makefile
Normal file
11
drivers/net/ibm_newemac/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
#
|
||||
# Makefile for the PowerPC 4xx on-chip ethernet driver
|
||||
#
|
||||
|
||||
obj-$(CONFIG_IBM_NEW_EMAC) += ibm_newemac.o
|
||||
|
||||
ibm_newemac-y := mal.o core.o phy.o
|
||||
ibm_newemac-$(CONFIG_IBM_NEW_EMAC_ZMII) += zmii.o
|
||||
ibm_newemac-$(CONFIG_IBM_NEW_EMAC_RGMII) += rgmii.o
|
||||
ibm_newemac-$(CONFIG_IBM_NEW_EMAC_TAH) += tah.o
|
||||
ibm_newemac-$(CONFIG_IBM_NEW_EMAC_DEBUG) += debug.o
|
2907
drivers/net/ibm_newemac/core.c
Normal file
2907
drivers/net/ibm_newemac/core.c
Normal file
File diff suppressed because it is too large
Load Diff
355
drivers/net/ibm_newemac/core.h
Normal file
355
drivers/net/ibm_newemac/core.h
Normal file
@ -0,0 +1,355 @@
|
||||
/*
|
||||
* drivers/net/ibm_newemac/core.h
|
||||
*
|
||||
* Driver for PowerPC 4xx on-chip ethernet controller.
|
||||
*
|
||||
* 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>
|
||||
* Johnnie Peters <jpeters@mvista.com>
|
||||
* Copyright 2000, 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.
|
||||
*
|
||||
*/
|
||||
#ifndef __IBM_NEWEMAC_CORE_H
|
||||
#define __IBM_NEWEMAC_CORE_H
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <asm/of_platform.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/dcr.h>
|
||||
|
||||
#include "emac.h"
|
||||
#include "phy.h"
|
||||
#include "zmii.h"
|
||||
#include "rgmii.h"
|
||||
#include "mal.h"
|
||||
#include "tah.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define NUM_TX_BUFF CONFIG_IBM_NEW_EMAC_TXB
|
||||
#define NUM_RX_BUFF CONFIG_IBM_NEW_EMAC_RXB
|
||||
|
||||
/* Simple sanity check */
|
||||
#if NUM_TX_BUFF > 256 || NUM_RX_BUFF > 256
|
||||
#error Invalid number of buffer descriptors (greater than 256)
|
||||
#endif
|
||||
|
||||
#define EMAC_MIN_MTU 46
|
||||
|
||||
/* Maximum L2 header length (VLAN tagged, no FCS) */
|
||||
#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_NEW_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.
|
||||
*/
|
||||
|
||||
/* Normal TX/RX Statistics */
|
||||
struct emac_stats {
|
||||
u64 rx_packets;
|
||||
u64 rx_bytes;
|
||||
u64 tx_packets;
|
||||
u64 tx_bytes;
|
||||
u64 rx_packets_csum;
|
||||
u64 tx_packets_csum;
|
||||
};
|
||||
|
||||
/* Error statistics */
|
||||
struct emac_error_stats {
|
||||
u64 tx_undo;
|
||||
|
||||
/* Software RX Errors */
|
||||
u64 rx_dropped_stack;
|
||||
u64 rx_dropped_oom;
|
||||
u64 rx_dropped_error;
|
||||
u64 rx_dropped_resize;
|
||||
u64 rx_dropped_mtu;
|
||||
u64 rx_stopped;
|
||||
/* BD reported RX errors */
|
||||
u64 rx_bd_errors;
|
||||
u64 rx_bd_overrun;
|
||||
u64 rx_bd_bad_packet;
|
||||
u64 rx_bd_runt_packet;
|
||||
u64 rx_bd_short_event;
|
||||
u64 rx_bd_alignment_error;
|
||||
u64 rx_bd_bad_fcs;
|
||||
u64 rx_bd_packet_too_long;
|
||||
u64 rx_bd_out_of_range;
|
||||
u64 rx_bd_in_range;
|
||||
/* EMAC IRQ reported RX errors */
|
||||
u64 rx_parity;
|
||||
u64 rx_fifo_overrun;
|
||||
u64 rx_overrun;
|
||||
u64 rx_bad_packet;
|
||||
u64 rx_runt_packet;
|
||||
u64 rx_short_event;
|
||||
u64 rx_alignment_error;
|
||||
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 emac_stats) + \
|
||||
sizeof(struct emac_error_stats)) \
|
||||
/ sizeof(u64))
|
||||
|
||||
struct emac_instance {
|
||||
struct net_device *ndev;
|
||||
struct resource rsrc_regs;
|
||||
struct emac_regs __iomem *emacp;
|
||||
struct of_device *ofdev;
|
||||
struct device_node **blist; /* bootlist entry */
|
||||
|
||||
/* MAL linkage */
|
||||
u32 mal_ph;
|
||||
struct of_device *mal_dev;
|
||||
u32 mal_rx_chan;
|
||||
u32 mal_tx_chan;
|
||||
struct mal_instance *mal;
|
||||
struct mal_commac commac;
|
||||
|
||||
/* PHY infos */
|
||||
u32 phy_mode;
|
||||
u32 phy_map;
|
||||
u32 phy_address;
|
||||
u32 phy_feat_exc;
|
||||
struct mii_phy phy;
|
||||
struct mutex link_lock;
|
||||
struct delayed_work link_work;
|
||||
int link_polling;
|
||||
|
||||
/* Shared MDIO if any */
|
||||
u32 mdio_ph;
|
||||
struct of_device *mdio_dev;
|
||||
struct emac_instance *mdio_instance;
|
||||
struct mutex mdio_lock;
|
||||
|
||||
/* ZMII infos if any */
|
||||
u32 zmii_ph;
|
||||
u32 zmii_port;
|
||||
struct of_device *zmii_dev;
|
||||
|
||||
/* RGMII infos if any */
|
||||
u32 rgmii_ph;
|
||||
u32 rgmii_port;
|
||||
struct of_device *rgmii_dev;
|
||||
|
||||
/* TAH infos if any */
|
||||
u32 tah_ph;
|
||||
u32 tah_port;
|
||||
struct of_device *tah_dev;
|
||||
|
||||
/* IRQs */
|
||||
int wol_irq;
|
||||
int emac_irq;
|
||||
|
||||
/* OPB bus frequency in Mhz */
|
||||
u32 opb_bus_freq;
|
||||
|
||||
/* Cell index within an ASIC (for clk mgmnt) */
|
||||
u32 cell_index;
|
||||
|
||||
/* Max supported MTU */
|
||||
u32 max_mtu;
|
||||
|
||||
/* Feature bits (from probe table) */
|
||||
unsigned int features;
|
||||
|
||||
/* Tx and Rx fifo sizes & other infos in bytes */
|
||||
u32 tx_fifo_size;
|
||||
u32 tx_fifo_size_gige;
|
||||
u32 rx_fifo_size;
|
||||
u32 rx_fifo_size_gige;
|
||||
u32 fifo_entry_size;
|
||||
u32 mal_burst_size; /* move to MAL ? */
|
||||
|
||||
/* Descriptor management
|
||||
*/
|
||||
struct mal_descriptor *tx_desc;
|
||||
int tx_cnt;
|
||||
int tx_slot;
|
||||
int ack_slot;
|
||||
|
||||
struct mal_descriptor *rx_desc;
|
||||
int rx_slot;
|
||||
struct sk_buff *rx_sg_skb; /* 1 */
|
||||
int rx_skb_size;
|
||||
int rx_sync_size;
|
||||
|
||||
struct sk_buff *tx_skb[NUM_TX_BUFF];
|
||||
struct sk_buff *rx_skb[NUM_RX_BUFF];
|
||||
|
||||
/* Stats
|
||||
*/
|
||||
struct emac_error_stats estats;
|
||||
struct net_device_stats nstats;
|
||||
struct emac_stats stats;
|
||||
|
||||
/* Misc
|
||||
*/
|
||||
int reset_failed;
|
||||
int stop_timeout; /* in us */
|
||||
int no_mcast;
|
||||
int mcast_pending;
|
||||
struct work_struct reset_work;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
/*
|
||||
* Features of various EMAC implementations
|
||||
*/
|
||||
|
||||
/*
|
||||
* No flow control on 40x according to the original driver
|
||||
*/
|
||||
#define EMAC_FTR_NO_FLOW_CONTROL_40x 0x00000001
|
||||
/*
|
||||
* Cell is an EMAC4
|
||||
*/
|
||||
#define EMAC_FTR_EMAC4 0x00000002
|
||||
/*
|
||||
* For the 440SPe, AMCC inexplicably changed the polarity of
|
||||
* the "operation complete" bit in the MII control register.
|
||||
*/
|
||||
#define EMAC_FTR_STACR_OC_INVERT 0x00000004
|
||||
/*
|
||||
* Set if we have a TAH.
|
||||
*/
|
||||
#define EMAC_FTR_HAS_TAH 0x00000008
|
||||
/*
|
||||
* Set if we have a ZMII.
|
||||
*/
|
||||
#define EMAC_FTR_HAS_ZMII 0x00000010
|
||||
/*
|
||||
* Set if we have a RGMII.
|
||||
*/
|
||||
#define EMAC_FTR_HAS_RGMII 0x00000020
|
||||
/*
|
||||
* Set if we have axon-type STACR
|
||||
*/
|
||||
#define EMAC_FTR_HAS_AXON_STACR 0x00000040
|
||||
|
||||
|
||||
/* Right now, we don't quite handle the always/possible masks on the
|
||||
* most optimal way as we don't have a way to say something like
|
||||
* always EMAC4. Patches welcome.
|
||||
*/
|
||||
enum {
|
||||
EMAC_FTRS_ALWAYS = 0,
|
||||
|
||||
EMAC_FTRS_POSSIBLE =
|
||||
#ifdef CONFIG_IBM_NEW_EMAC_EMAC4
|
||||
EMAC_FTR_EMAC4 | EMAC_FTR_HAS_AXON_STACR |
|
||||
EMAC_FTR_STACR_OC_INVERT |
|
||||
#endif
|
||||
#ifdef CONFIG_IBM_NEW_EMAC_TAH
|
||||
EMAC_FTR_HAS_TAH |
|
||||
#endif
|
||||
#ifdef CONFIG_IBM_NEW_EMAC_ZMII
|
||||
EMAC_FTR_HAS_ZMII |
|
||||
#endif
|
||||
#ifdef CONFIG_IBM_NEW_EMAC_RGMII
|
||||
EMAC_FTR_HAS_RGMII |
|
||||
#endif
|
||||
0,
|
||||
};
|
||||
|
||||
static inline int emac_has_feature(struct emac_instance *dev,
|
||||
unsigned long feature)
|
||||
{
|
||||
return (EMAC_FTRS_ALWAYS & feature) ||
|
||||
(EMAC_FTRS_POSSIBLE & dev->features & feature);
|
||||
}
|
||||
|
||||
|
||||
/* 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_NEWEMAC_CORE_H */
|
238
drivers/net/ibm_newemac/debug.c
Normal file
238
drivers/net/ibm_newemac/debug.c
Normal file
@ -0,0 +1,238 @@
|
||||
/*
|
||||
* drivers/net/ibm_newemac/debug.c
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/sysrq.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
static spinlock_t emac_dbg_lock = SPIN_LOCK_UNLOCKED;
|
||||
|
||||
static void emac_desc_dump(struct emac_instance *p)
|
||||
{
|
||||
int i;
|
||||
printk("** EMAC %s TX BDs **\n"
|
||||
" tx_cnt = %d tx_slot = %d ack_slot = %d\n",
|
||||
p->ofdev->node->full_name,
|
||||
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 %s RX BDs **\n"
|
||||
" rx_slot = %d flags = 0x%lx rx_skb_size = %d rx_sync_size = %d\n"
|
||||
" rx_sg_skb = 0x%p\n",
|
||||
p->ofdev->node->full_name,
|
||||
p->rx_slot, p->commac.flags, 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(struct emac_instance *dev)
|
||||
{
|
||||
struct emac_regs __iomem *p = dev->emacp;
|
||||
|
||||
printk("** EMAC %s 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",
|
||||
dev->ofdev->node->full_name, 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(dev);
|
||||
}
|
||||
|
||||
static void emac_mal_dump(struct mal_instance *mal)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk("** MAL %s 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->ofdev->node->full_name,
|
||||
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 < mal->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 < mal->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 < mal->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 emac_instance *__emacs[4];
|
||||
static struct mal_instance *__mals[1];
|
||||
|
||||
void emac_dbg_register(struct emac_instance *dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(&emac_dbg_lock, flags);
|
||||
for (i = 0; i < ARRAY_SIZE(__emacs); i++)
|
||||
if (__emacs[i] == NULL) {
|
||||
__emacs[i] = dev;
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&emac_dbg_lock, flags);
|
||||
}
|
||||
|
||||
void emac_dbg_unregister(struct emac_instance *dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(&emac_dbg_lock, flags);
|
||||
for (i = 0; i < ARRAY_SIZE(__emacs); i++)
|
||||
if (__emacs[i] == dev) {
|
||||
__emacs[i] = NULL;
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&emac_dbg_lock, flags);
|
||||
}
|
||||
|
||||
void mal_dbg_register(struct mal_instance *mal)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(&emac_dbg_lock, flags);
|
||||
for (i = 0; i < ARRAY_SIZE(__mals); i++)
|
||||
if (__mals[i] == NULL) {
|
||||
__mals[i] = mal;
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&emac_dbg_lock, flags);
|
||||
}
|
||||
|
||||
void mal_dbg_unregister(struct mal_instance *mal)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(&emac_dbg_lock, flags);
|
||||
for (i = 0; i < ARRAY_SIZE(__mals); i++)
|
||||
if (__mals[i] == mal) {
|
||||
__mals[i] = NULL;
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&emac_dbg_lock, flags);
|
||||
}
|
||||
|
||||
void emac_dbg_dump_all(void)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&emac_dbg_lock, flags);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(__mals); ++i)
|
||||
if (__mals[i])
|
||||
emac_mal_dump(__mals[i]);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(__emacs); ++i)
|
||||
if (__emacs[i])
|
||||
emac_mac_dump(__emacs[i]);
|
||||
|
||||
spin_unlock_irqrestore(&emac_dbg_lock, flags);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_MAGIC_SYSRQ)
|
||||
static void emac_sysrq_handler(int key, struct tty_struct *tty)
|
||||
{
|
||||
emac_dbg_dump_all();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return register_sysrq_key('c', &emac_sysrq_op);
|
||||
}
|
||||
|
||||
void __exit emac_fini_debug(void)
|
||||
{
|
||||
unregister_sysrq_key('c', &emac_sysrq_op);
|
||||
}
|
||||
|
||||
#else
|
||||
int __init emac_init_debug(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
void __exit emac_fini_debug(void)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_MAGIC_SYSRQ */
|
78
drivers/net/ibm_newemac/debug.h
Normal file
78
drivers/net/ibm_newemac/debug.h
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* drivers/net/ibm_newemac/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_NEWEMAC_DEBUG_H
|
||||
#define __IBM_NEWEMAC_DEBUG_H
|
||||
|
||||
#include <linux/init.h>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
#if defined(CONFIG_IBM_NEW_EMAC_DEBUG)
|
||||
|
||||
struct emac_instance;
|
||||
struct mal_instance;
|
||||
|
||||
extern void emac_dbg_register(struct emac_instance *dev);
|
||||
extern void emac_dbg_unregister(struct emac_instance *dev);
|
||||
extern void mal_dbg_register(struct mal_instance *mal);
|
||||
extern void mal_dbg_unregister(struct mal_instance *mal);
|
||||
extern int emac_init_debug(void) __init;
|
||||
extern void emac_fini_debug(void) __exit;
|
||||
extern void emac_dbg_dump_all(void);
|
||||
|
||||
# define DBG_LEVEL 1
|
||||
|
||||
#else
|
||||
|
||||
# define emac_dbg_register(x) do { } while(0)
|
||||
# define emac_dbg_unregister(x) do { } while(0)
|
||||
# define mal_dbg_register(x) do { } while(0)
|
||||
# define mal_dbg_unregister(x) do { } while(0)
|
||||
# define emac_init_debug() do { } while(0)
|
||||
# define emac_fini_debug() do { } while(0)
|
||||
# define emac_dbg_dump_all() do { } while(0)
|
||||
|
||||
# define DBG_LEVEL 0
|
||||
|
||||
#endif
|
||||
|
||||
#define EMAC_DBG(dev, name, fmt, arg...) \
|
||||
printk(KERN_DEBUG #name "%s: " fmt, dev->ofdev->node->full_name, ## arg)
|
||||
|
||||
#if DBG_LEVEL > 0
|
||||
# define DBG(d,f,x...) EMAC_DBG(d, emac, f, ##x)
|
||||
# define MAL_DBG(d,f,x...) EMAC_DBG(d, mal, f, ##x)
|
||||
# define ZMII_DBG(d,f,x...) EMAC_DBG(d, zmii, f, ##x)
|
||||
# define RGMII_DBG(d,f,x...) EMAC_DBG(d, rgmii, f, ##x)
|
||||
# define NL "\n"
|
||||
#else
|
||||
# define DBG(f,x...) ((void)0)
|
||||
# define MAL_DBG(d,f,x...) ((void)0)
|
||||
# define ZMII_DBG(d,f,x...) ((void)0)
|
||||
# define RGMII_DBG(d,f,x...) ((void)0)
|
||||
#endif
|
||||
#if DBG_LEVEL > 1
|
||||
# define DBG2(d,f,x...) DBG(d,f, ##x)
|
||||
# define MAL_DBG2(d,f,x...) MAL_DBG(d,f, ##x)
|
||||
# define ZMII_DBG2(d,f,x...) ZMII_DBG(d,f, ##x)
|
||||
# define RGMII_DBG2(d,f,x...) RGMII_DBG(d,f, ##x)
|
||||
#else
|
||||
# define DBG2(f,x...) ((void)0)
|
||||
# define MAL_DBG2(d,f,x...) ((void)0)
|
||||
# define ZMII_DBG2(d,f,x...) ((void)0)
|
||||
# define RGMII_DBG2(d,f,x...) ((void)0)
|
||||
#endif
|
||||
|
||||
#endif /* __IBM_NEWEMAC_DEBUG_H */
|
268
drivers/net/ibm_newemac/emac.h
Normal file
268
drivers/net/ibm_newemac/emac.h
Normal file
@ -0,0 +1,268 @@
|
||||
/*
|
||||
* drivers/net/ibm_newemac/emac.h
|
||||
*
|
||||
* Register definitions for PowerPC 4xx on-chip ethernet contoller
|
||||
*
|
||||
* 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>
|
||||
* Armin Kuster <akuster@mvista.com>
|
||||
* Copyright 2002-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.
|
||||
*
|
||||
*/
|
||||
#ifndef __IBM_NEWEMAC_H
|
||||
#define __IBM_NEWEMAC_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* 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;
|
||||
};
|
||||
|
||||
/*
|
||||
* PHY mode settings (EMAC <-> ZMII/RGMII bridge <-> PHY)
|
||||
*/
|
||||
#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
|
||||
|
||||
|
||||
#define EMAC_ETHTOOL_REGS_VER 0
|
||||
#define EMAC_ETHTOOL_REGS_SIZE (sizeof(struct emac_regs) - sizeof(u32))
|
||||
#define EMAC4_ETHTOOL_REGS_VER 1
|
||||
#define EMAC4_ETHTOOL_REGS_SIZE sizeof(struct emac_regs)
|
||||
|
||||
/* EMACx_MR0 */
|
||||
#define EMAC_MR0_RXI 0x80000000
|
||||
#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
|
||||
|
||||
/* EMACx_MR1 */
|
||||
#define EMAC_MR1_FDE 0x80000000
|
||||
#define EMAC_MR1_ILE 0x40000000
|
||||
#define EMAC_MR1_VLE 0x20000000
|
||||
#define EMAC_MR1_EIFC 0x10000000
|
||||
#define EMAC_MR1_APP 0x08000000
|
||||
#define EMAC_MR1_IST 0x01000000
|
||||
|
||||
#define EMAC_MR1_MF_MASK 0x00c00000
|
||||
#define EMAC_MR1_MF_10 0x00000000
|
||||
#define EMAC_MR1_MF_100 0x00400000
|
||||
#define EMAC_MR1_MF_1000 0x00800000
|
||||
#define EMAC_MR1_MF_1000GPCS 0x00c00000
|
||||
#define EMAC_MR1_MF_IPPA(id) (((id) & 0x1f) << 6)
|
||||
|
||||
#define EMAC_MR1_RFS_4K 0x00300000
|
||||
#define EMAC_MR1_RFS_16K 0x00000000
|
||||
#define EMAC_MR1_TFS_2K 0x00080000
|
||||
#define EMAC_MR1_TR0_MULT 0x00008000
|
||||
#define EMAC_MR1_JPSM 0x00000000
|
||||
#define EMAC_MR1_MWSW_001 0x00000000
|
||||
#define EMAC_MR1_BASE(opb) (EMAC_MR1_TFS_2K | EMAC_MR1_TR0_MULT)
|
||||
|
||||
|
||||
#define EMAC4_MR1_RFS_2K 0x00100000
|
||||
#define EMAC4_MR1_RFS_4K 0x00180000
|
||||
#define EMAC4_MR1_RFS_16K 0x00280000
|
||||
#define EMAC4_MR1_TFS_2K 0x00020000
|
||||
#define EMAC4_MR1_TFS_4K 0x00030000
|
||||
#define EMAC4_MR1_TR 0x00008000
|
||||
#define EMAC4_MR1_MWSW_001 0x00001000
|
||||
#define EMAC4_MR1_JPSM 0x00000800
|
||||
#define EMAC4_MR1_OBCI_MASK 0x00000038
|
||||
#define EMAC4_MR1_OBCI_50 0x00000000
|
||||
#define EMAC4_MR1_OBCI_66 0x00000008
|
||||
#define EMAC4_MR1_OBCI_83 0x00000010
|
||||
#define EMAC4_MR1_OBCI_100 0x00000018
|
||||
#define EMAC4_MR1_OBCI_100P 0x00000020
|
||||
#define EMAC4_MR1_OBCI(freq) ((freq) <= 50 ? EMAC4_MR1_OBCI_50 : \
|
||||
(freq) <= 66 ? EMAC4_MR1_OBCI_66 : \
|
||||
(freq) <= 83 ? EMAC4_MR1_OBCI_83 : \
|
||||
(freq) <= 100 ? EMAC4_MR1_OBCI_100 : \
|
||||
EMAC4_MR1_OBCI_100P)
|
||||
|
||||
/* EMACx_TMR0 */
|
||||
#define EMAC_TMR0_GNP 0x80000000
|
||||
#define EMAC_TMR0_DEFAULT 0x00000000
|
||||
#define EMAC4_TMR0_TFAE_2_32 0x00000001
|
||||
#define EMAC4_TMR0_TFAE_4_64 0x00000002
|
||||
#define EMAC4_TMR0_TFAE_8_128 0x00000003
|
||||
#define EMAC4_TMR0_TFAE_16_256 0x00000004
|
||||
#define EMAC4_TMR0_TFAE_32_512 0x00000005
|
||||
#define EMAC4_TMR0_TFAE_64_1024 0x00000006
|
||||
#define EMAC4_TMR0_TFAE_128_2048 0x00000007
|
||||
#define EMAC4_TMR0_DEFAULT EMAC4_TMR0_TFAE_2_32
|
||||
#define EMAC_TMR0_XMIT (EMAC_TMR0_GNP | EMAC_TMR0_DEFAULT)
|
||||
#define EMAC4_TMR0_XMIT (EMAC_TMR0_GNP | EMAC4_TMR0_DEFAULT)
|
||||
|
||||
/* EMACx_TMR1 */
|
||||
|
||||
#define EMAC_TMR1(l,h) (((l) << 27) | (((h) & 0xff) << 16))
|
||||
#define EMAC4_TMR1(l,h) (((l) << 27) | (((h) & 0x3ff) << 14))
|
||||
|
||||
/* EMACx_RMR */
|
||||
#define EMAC_RMR_SP 0x80000000
|
||||
#define EMAC_RMR_SFCS 0x40000000
|
||||
#define EMAC_RMR_RRP 0x20000000
|
||||
#define EMAC_RMR_RFP 0x10000000
|
||||
#define EMAC_RMR_ROP 0x08000000
|
||||
#define EMAC_RMR_RPIR 0x04000000
|
||||
#define EMAC_RMR_PPP 0x02000000
|
||||
#define EMAC_RMR_PME 0x01000000
|
||||
#define EMAC_RMR_PMME 0x00800000
|
||||
#define EMAC_RMR_IAE 0x00400000
|
||||
#define EMAC_RMR_MIAE 0x00200000
|
||||
#define EMAC_RMR_BAE 0x00100000
|
||||
#define EMAC_RMR_MAE 0x00080000
|
||||
#define EMAC_RMR_BASE 0x00000000
|
||||
#define EMAC4_RMR_RFAF_2_32 0x00000001
|
||||
#define EMAC4_RMR_RFAF_4_64 0x00000002
|
||||
#define EMAC4_RMR_RFAF_8_128 0x00000003
|
||||
#define EMAC4_RMR_RFAF_16_256 0x00000004
|
||||
#define EMAC4_RMR_RFAF_32_512 0x00000005
|
||||
#define EMAC4_RMR_RFAF_64_1024 0x00000006
|
||||
#define EMAC4_RMR_RFAF_128_2048 0x00000007
|
||||
#define EMAC4_RMR_BASE EMAC4_RMR_RFAF_128_2048
|
||||
|
||||
/* EMACx_ISR & EMACx_ISER */
|
||||
#define EMAC4_ISR_TXPE 0x20000000
|
||||
#define EMAC4_ISR_RXPE 0x10000000
|
||||
#define EMAC4_ISR_TXUE 0x08000000
|
||||
#define EMAC4_ISR_RXOE 0x04000000
|
||||
#define EMAC_ISR_OVR 0x02000000
|
||||
#define EMAC_ISR_PP 0x01000000
|
||||
#define EMAC_ISR_BP 0x00800000
|
||||
#define EMAC_ISR_RP 0x00400000
|
||||
#define EMAC_ISR_SE 0x00200000
|
||||
#define EMAC_ISR_ALE 0x00100000
|
||||
#define EMAC_ISR_BFCS 0x00080000
|
||||
#define EMAC_ISR_PTLE 0x00040000
|
||||
#define EMAC_ISR_ORE 0x00020000
|
||||
#define EMAC_ISR_IRE 0x00010000
|
||||
#define EMAC_ISR_SQE 0x00000080
|
||||
#define EMAC_ISR_TE 0x00000040
|
||||
#define EMAC_ISR_MOS 0x00000002
|
||||
#define EMAC_ISR_MOF 0x00000001
|
||||
|
||||
/* EMACx_STACR */
|
||||
#define EMAC_STACR_PHYD_MASK 0xffff
|
||||
#define EMAC_STACR_PHYD_SHIFT 16
|
||||
#define EMAC_STACR_OC 0x00008000
|
||||
#define EMAC_STACR_PHYE 0x00004000
|
||||
#define EMAC_STACR_STAC_MASK 0x00003000
|
||||
#define EMAC_STACR_STAC_READ 0x00001000
|
||||
#define EMAC_STACR_STAC_WRITE 0x00002000
|
||||
#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)
|
||||
#define EMAC4_STACR_BASE(opb) 0x00000000
|
||||
#define EMAC_STACR_PCDA_MASK 0x1f
|
||||
#define EMAC_STACR_PCDA_SHIFT 5
|
||||
#define EMAC_STACR_PRA_MASK 0x1f
|
||||
#define EMACX_STACR_STAC_MASK 0x00003800
|
||||
#define EMACX_STACR_STAC_READ 0x00001000
|
||||
#define EMACX_STACR_STAC_WRITE 0x00000800
|
||||
#define EMACX_STACR_STAC_IND_ADDR 0x00002000
|
||||
#define EMACX_STACR_STAC_IND_READ 0x00003800
|
||||
#define EMACX_STACR_STAC_IND_READINC 0x00003000
|
||||
#define EMACX_STACR_STAC_IND_WRITE 0x00002800
|
||||
|
||||
|
||||
/* EMACx_TRTR */
|
||||
#define EMAC_TRTR_SHIFT_EMAC4 27
|
||||
#define EMAC_TRTR_SHIFT 24
|
||||
|
||||
/* EMAC specific TX descriptor control fields (write access) */
|
||||
#define EMAC_TX_CTRL_GFCS 0x0200
|
||||
#define EMAC_TX_CTRL_GP 0x0100
|
||||
#define EMAC_TX_CTRL_ISA 0x0080
|
||||
#define EMAC_TX_CTRL_RSA 0x0040
|
||||
#define EMAC_TX_CTRL_IVT 0x0020
|
||||
#define EMAC_TX_CTRL_RVT 0x0010
|
||||
#define EMAC_TX_CTRL_TAH_CSUM 0x000e
|
||||
|
||||
/* EMAC specific TX descriptor status fields (read access) */
|
||||
#define EMAC_TX_ST_BFCS 0x0200
|
||||
#define EMAC_TX_ST_LCS 0x0080
|
||||
#define EMAC_TX_ST_ED 0x0040
|
||||
#define EMAC_TX_ST_EC 0x0020
|
||||
#define EMAC_TX_ST_LC 0x0010
|
||||
#define EMAC_TX_ST_MC 0x0008
|
||||
#define EMAC_TX_ST_SC 0x0004
|
||||
#define EMAC_TX_ST_UR 0x0002
|
||||
#define EMAC_TX_ST_SQE 0x0001
|
||||
#define EMAC_IS_BAD_TX (EMAC_TX_ST_LCS | EMAC_TX_ST_ED | \
|
||||
EMAC_TX_ST_EC | EMAC_TX_ST_LC | \
|
||||
EMAC_TX_ST_MC | EMAC_TX_ST_UR)
|
||||
#define EMAC_IS_BAD_TX_TAH (EMAC_TX_ST_LCS | EMAC_TX_ST_ED | \
|
||||
EMAC_TX_ST_EC | EMAC_TX_ST_LC)
|
||||
|
||||
/* EMAC specific RX descriptor status fields (read access) */
|
||||
#define EMAC_RX_ST_OE 0x0200
|
||||
#define EMAC_RX_ST_PP 0x0100
|
||||
#define EMAC_RX_ST_BP 0x0080
|
||||
#define EMAC_RX_ST_RP 0x0040
|
||||
#define EMAC_RX_ST_SE 0x0020
|
||||
#define EMAC_RX_ST_AE 0x0010
|
||||
#define EMAC_RX_ST_BFCS 0x0008
|
||||
#define EMAC_RX_ST_PTL 0x0004
|
||||
#define EMAC_RX_ST_ORE 0x0002
|
||||
#define EMAC_RX_ST_IRE 0x0001
|
||||
#define EMAC_RX_TAH_BAD_CSUM 0x0003
|
||||
#define EMAC_BAD_RX_MASK (EMAC_RX_ST_OE | EMAC_RX_ST_BP | \
|
||||
EMAC_RX_ST_RP | EMAC_RX_ST_SE | \
|
||||
EMAC_RX_ST_AE | EMAC_RX_ST_BFCS | \
|
||||
EMAC_RX_ST_PTL | EMAC_RX_ST_ORE | \
|
||||
EMAC_RX_ST_IRE )
|
||||
#endif /* __IBM_NEWEMAC_H */
|
728
drivers/net/ibm_newemac/mal.c
Normal file
728
drivers/net/ibm_newemac/mal.c
Normal file
@ -0,0 +1,728 @@
|
||||
/*
|
||||
* drivers/net/ibm_newemac/mal.c
|
||||
*
|
||||
* 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
|
||||
* 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
|
||||
* 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/delay.h>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
static int mal_count;
|
||||
|
||||
int __devinit mal_register_commac(struct mal_instance *mal,
|
||||
struct mal_commac *commac)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&mal->lock, flags);
|
||||
|
||||
MAL_DBG(mal, "reg(%08x, %08x)" NL,
|
||||
commac->tx_chan_mask, commac->rx_chan_mask);
|
||||
|
||||
/* Don't let multiple commacs claim the same channel(s) */
|
||||
if ((mal->tx_chan_mask & commac->tx_chan_mask) ||
|
||||
(mal->rx_chan_mask & commac->rx_chan_mask)) {
|
||||
spin_unlock_irqrestore(&mal->lock, flags);
|
||||
printk(KERN_WARNING "mal%d: COMMAC channels conflict!\n",
|
||||
mal->index);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
mal->tx_chan_mask |= commac->tx_chan_mask;
|
||||
mal->rx_chan_mask |= commac->rx_chan_mask;
|
||||
list_add(&commac->list, &mal->list);
|
||||
|
||||
spin_unlock_irqrestore(&mal->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __devexit mal_unregister_commac(struct mal_instance *mal,
|
||||
struct mal_commac *commac)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&mal->lock, flags);
|
||||
|
||||
MAL_DBG(mal, "unreg(%08x, %08x)" NL,
|
||||
commac->tx_chan_mask, commac->rx_chan_mask);
|
||||
|
||||
mal->tx_chan_mask &= ~commac->tx_chan_mask;
|
||||
mal->rx_chan_mask &= ~commac->rx_chan_mask;
|
||||
list_del_init(&commac->list);
|
||||
|
||||
spin_unlock_irqrestore(&mal->lock, flags);
|
||||
}
|
||||
|
||||
int mal_set_rcbs(struct mal_instance *mal, int channel, unsigned long size)
|
||||
{
|
||||
BUG_ON(channel < 0 || channel >= mal->num_rx_chans ||
|
||||
size > MAL_MAX_RX_SIZE);
|
||||
|
||||
MAL_DBG(mal, "set_rbcs(%d, %lu)" NL, channel, size);
|
||||
|
||||
if (size & 0xf) {
|
||||
printk(KERN_WARNING
|
||||
"mal%d: incorrect RX size %lu for the channel %d\n",
|
||||
mal->index, size, channel);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
set_mal_dcrn(mal, MAL_RCBS(channel), size >> 4);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mal_tx_bd_offset(struct mal_instance *mal, int channel)
|
||||
{
|
||||
BUG_ON(channel < 0 || channel >= mal->num_tx_chans);
|
||||
|
||||
return channel * NUM_TX_BUFF;
|
||||
}
|
||||
|
||||
int mal_rx_bd_offset(struct mal_instance *mal, int channel)
|
||||
{
|
||||
BUG_ON(channel < 0 || channel >= mal->num_rx_chans);
|
||||
return mal->num_tx_chans * NUM_TX_BUFF + channel * NUM_RX_BUFF;
|
||||
}
|
||||
|
||||
void mal_enable_tx_channel(struct mal_instance *mal, int channel)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&mal->lock, flags);
|
||||
|
||||
MAL_DBG(mal, "enable_tx(%d)" NL, channel);
|
||||
|
||||
set_mal_dcrn(mal, MAL_TXCASR,
|
||||
get_mal_dcrn(mal, MAL_TXCASR) | MAL_CHAN_MASK(channel));
|
||||
|
||||
spin_unlock_irqrestore(&mal->lock, flags);
|
||||
}
|
||||
|
||||
void mal_disable_tx_channel(struct mal_instance *mal, int channel)
|
||||
{
|
||||
set_mal_dcrn(mal, MAL_TXCARR, MAL_CHAN_MASK(channel));
|
||||
|
||||
MAL_DBG(mal, "disable_tx(%d)" NL, channel);
|
||||
}
|
||||
|
||||
void mal_enable_rx_channel(struct mal_instance *mal, int channel)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&mal->lock, flags);
|
||||
|
||||
MAL_DBG(mal, "enable_rx(%d)" NL, channel);
|
||||
|
||||
set_mal_dcrn(mal, MAL_RXCASR,
|
||||
get_mal_dcrn(mal, MAL_RXCASR) | MAL_CHAN_MASK(channel));
|
||||
|
||||
spin_unlock_irqrestore(&mal->lock, flags);
|
||||
}
|
||||
|
||||
void mal_disable_rx_channel(struct mal_instance *mal, int channel)
|
||||
{
|
||||
set_mal_dcrn(mal, MAL_RXCARR, MAL_CHAN_MASK(channel));
|
||||
|
||||
MAL_DBG(mal, "disable_rx(%d)" NL, channel);
|
||||
}
|
||||
|
||||
void mal_poll_add(struct mal_instance *mal, struct mal_commac *commac)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&mal->lock, flags);
|
||||
|
||||
MAL_DBG(mal, "poll_add(%p)" NL, commac);
|
||||
|
||||
/* starts disabled */
|
||||
set_bit(MAL_COMMAC_POLL_DISABLED, &commac->flags);
|
||||
|
||||
list_add_tail(&commac->poll_list, &mal->poll_list);
|
||||
|
||||
spin_unlock_irqrestore(&mal->lock, flags);
|
||||
}
|
||||
|
||||
void mal_poll_del(struct mal_instance *mal, struct mal_commac *commac)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&mal->lock, flags);
|
||||
|
||||
MAL_DBG(mal, "poll_del(%p)" NL, commac);
|
||||
|
||||
list_del(&commac->poll_list);
|
||||
|
||||
spin_unlock_irqrestore(&mal->lock, flags);
|
||||
}
|
||||
|
||||
/* synchronized by mal_poll() */
|
||||
static inline void mal_enable_eob_irq(struct mal_instance *mal)
|
||||
{
|
||||
MAL_DBG2(mal, "enable_irq" NL);
|
||||
|
||||
// XXX might want to cache MAL_CFG as the DCR read can be slooooow
|
||||
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 mal_instance *mal)
|
||||
{
|
||||
// XXX might want to cache MAL_CFG as the DCR read can be slooooow
|
||||
set_mal_dcrn(mal, MAL_CFG, get_mal_dcrn(mal, MAL_CFG) & ~MAL_CFG_EOPIE);
|
||||
|
||||
MAL_DBG2(mal, "disable_irq" NL);
|
||||
}
|
||||
|
||||
static irqreturn_t mal_serr(int irq, void *dev_instance)
|
||||
{
|
||||
struct mal_instance *mal = dev_instance;
|
||||
|
||||
u32 esr = get_mal_dcrn(mal, MAL_ESR);
|
||||
|
||||
/* Clear the error status register */
|
||||
set_mal_dcrn(mal, MAL_ESR, esr);
|
||||
|
||||
MAL_DBG(mal, "SERR %08x" NL, esr);
|
||||
|
||||
if (esr & MAL_ESR_EVB) {
|
||||
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->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->index, esr);
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static inline void mal_schedule_poll(struct mal_instance *mal)
|
||||
{
|
||||
if (likely(netif_rx_schedule_prep(&mal->poll_dev))) {
|
||||
MAL_DBG2(mal, "schedule_poll" NL);
|
||||
mal_disable_eob_irq(mal);
|
||||
__netif_rx_schedule(&mal->poll_dev);
|
||||
} else
|
||||
MAL_DBG2(mal, "already in poll" NL);
|
||||
}
|
||||
|
||||
static irqreturn_t mal_txeob(int irq, void *dev_instance)
|
||||
{
|
||||
struct mal_instance *mal = dev_instance;
|
||||
|
||||
u32 r = get_mal_dcrn(mal, MAL_TXEOBISR);
|
||||
|
||||
MAL_DBG2(mal, "txeob %08x" NL, r);
|
||||
|
||||
mal_schedule_poll(mal);
|
||||
set_mal_dcrn(mal, MAL_TXEOBISR, r);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t mal_rxeob(int irq, void *dev_instance)
|
||||
{
|
||||
struct mal_instance *mal = dev_instance;
|
||||
|
||||
u32 r = get_mal_dcrn(mal, MAL_RXEOBISR);
|
||||
|
||||
MAL_DBG2(mal, "rxeob %08x" NL, r);
|
||||
|
||||
mal_schedule_poll(mal);
|
||||
set_mal_dcrn(mal, MAL_RXEOBISR, r);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t mal_txde(int irq, void *dev_instance)
|
||||
{
|
||||
struct mal_instance *mal = dev_instance;
|
||||
|
||||
u32 deir = get_mal_dcrn(mal, MAL_TXDEIR);
|
||||
set_mal_dcrn(mal, MAL_TXDEIR, deir);
|
||||
|
||||
MAL_DBG(mal, "txde %08x" NL, deir);
|
||||
|
||||
if (net_ratelimit())
|
||||
printk(KERN_ERR
|
||||
"mal%d: TX descriptor error (TXDEIR = 0x%08x)\n",
|
||||
mal->index, deir);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t mal_rxde(int irq, void *dev_instance)
|
||||
{
|
||||
struct mal_instance *mal = dev_instance;
|
||||
struct list_head *l;
|
||||
|
||||
u32 deir = get_mal_dcrn(mal, MAL_RXDEIR);
|
||||
|
||||
MAL_DBG(mal, "rxde %08x" NL, deir);
|
||||
|
||||
list_for_each(l, &mal->list) {
|
||||
struct mal_commac *mc = list_entry(l, struct mal_commac, list);
|
||||
if (deir & mc->rx_chan_mask) {
|
||||
set_bit(MAL_COMMAC_RX_STOPPED, &mc->flags);
|
||||
mc->ops->rxde(mc->dev);
|
||||
}
|
||||
}
|
||||
|
||||
mal_schedule_poll(mal);
|
||||
set_mal_dcrn(mal, MAL_RXDEIR, deir);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
void mal_poll_disable(struct mal_instance *mal, struct mal_commac *commac)
|
||||
{
|
||||
/* Spinlock-type semantics: only one caller disable poll at a time */
|
||||
while (test_and_set_bit(MAL_COMMAC_POLL_DISABLED, &commac->flags))
|
||||
msleep(1);
|
||||
|
||||
/* Synchronize with the MAL NAPI poller. */
|
||||
while (test_bit(__LINK_STATE_RX_SCHED, &mal->poll_dev.state))
|
||||
msleep(1);
|
||||
}
|
||||
|
||||
void mal_poll_enable(struct mal_instance *mal, struct mal_commac *commac)
|
||||
{
|
||||
smp_wmb();
|
||||
clear_bit(MAL_COMMAC_POLL_DISABLED, &commac->flags);
|
||||
|
||||
// XXX might want to kick a poll now...
|
||||
}
|
||||
|
||||
static int mal_poll(struct net_device *ndev, int *budget)
|
||||
{
|
||||
struct mal_instance *mal = netdev_priv(ndev);
|
||||
struct list_head *l;
|
||||
int rx_work_limit = min(ndev->quota, *budget), received = 0, done;
|
||||
unsigned long flags;
|
||||
|
||||
MAL_DBG2(mal, "poll(%d) %d ->" NL, *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.
|
||||
*
|
||||
* We _might_ need something more smart here to enforce polling
|
||||
* fairness.
|
||||
*/
|
||||
list_for_each(l, &mal->poll_list) {
|
||||
struct mal_commac *mc =
|
||||
list_entry(l, struct mal_commac, poll_list);
|
||||
int n;
|
||||
if (unlikely(test_bit(MAL_COMMAC_POLL_DISABLED, &mc->flags)))
|
||||
continue;
|
||||
n = mc->ops->poll_rx(mc->dev, rx_work_limit);
|
||||
if (n) {
|
||||
received += n;
|
||||
rx_work_limit -= n;
|
||||
if (rx_work_limit <= 0) {
|
||||
done = 0;
|
||||
// XXX What if this is the last one ?
|
||||
goto more_work;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* We need to disable IRQs to protect from RXDE IRQ here */
|
||||
spin_lock_irqsave(&mal->lock, flags);
|
||||
__netif_rx_complete(ndev);
|
||||
mal_enable_eob_irq(mal);
|
||||
spin_unlock_irqrestore(&mal->lock, flags);
|
||||
|
||||
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(test_bit(MAL_COMMAC_POLL_DISABLED, &mc->flags)))
|
||||
continue;
|
||||
if (unlikely(mc->ops->peek_rx(mc->dev) ||
|
||||
test_bit(MAL_COMMAC_RX_STOPPED, &mc->flags))) {
|
||||
MAL_DBG2(mal, "rotting packet" NL);
|
||||
if (netif_rx_reschedule(ndev, received))
|
||||
mal_disable_eob_irq(mal);
|
||||
else
|
||||
MAL_DBG2(mal, "already in poll list" NL);
|
||||
|
||||
if (rx_work_limit > 0)
|
||||
goto again;
|
||||
else
|
||||
goto more_work;
|
||||
}
|
||||
mc->ops->poll_tx(mc->dev);
|
||||
}
|
||||
|
||||
more_work:
|
||||
ndev->quota -= received;
|
||||
*budget -= received;
|
||||
|
||||
MAL_DBG2(mal, "poll() %d <- %d" NL, *budget,
|
||||
done ? 0 : 1);
|
||||
|
||||
return done ? 0 : 1;
|
||||
}
|
||||
|
||||
static void mal_reset(struct mal_instance *mal)
|
||||
{
|
||||
int n = 10;
|
||||
|
||||
MAL_DBG(mal, "reset" NL);
|
||||
|
||||
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->index);
|
||||
}
|
||||
|
||||
int mal_get_regs_len(struct mal_instance *mal)
|
||||
{
|
||||
return sizeof(struct emac_ethtool_regs_subhdr) +
|
||||
sizeof(struct mal_regs);
|
||||
}
|
||||
|
||||
void *mal_dump_regs(struct mal_instance *mal, void *buf)
|
||||
{
|
||||
struct emac_ethtool_regs_subhdr *hdr = buf;
|
||||
struct mal_regs *regs = (struct mal_regs *)(hdr + 1);
|
||||
int i;
|
||||
|
||||
hdr->version = mal->version;
|
||||
hdr->index = mal->index;
|
||||
|
||||
regs->tx_count = mal->num_tx_chans;
|
||||
regs->rx_count = mal->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 __devinit mal_probe(struct of_device *ofdev,
|
||||
const struct of_device_id *match)
|
||||
{
|
||||
struct mal_instance *mal;
|
||||
int err = 0, i, bd_size;
|
||||
int index = mal_count++;
|
||||
const u32 *prop;
|
||||
u32 cfg;
|
||||
|
||||
mal = kzalloc(sizeof(struct mal_instance), GFP_KERNEL);
|
||||
if (!mal) {
|
||||
printk(KERN_ERR
|
||||
"mal%d: out of memory allocating MAL structure!\n",
|
||||
index);
|
||||
return -ENOMEM;
|
||||
}
|
||||
mal->index = index;
|
||||
mal->ofdev = ofdev;
|
||||
mal->version = of_device_is_compatible(ofdev->node, "ibm,mcmal2") ? 2 : 1;
|
||||
|
||||
MAL_DBG(mal, "probe" NL);
|
||||
|
||||
prop = of_get_property(ofdev->node, "num-tx-chans", NULL);
|
||||
if (prop == NULL) {
|
||||
printk(KERN_ERR
|
||||
"mal%d: can't find MAL num-tx-chans property!\n",
|
||||
index);
|
||||
err = -ENODEV;
|
||||
goto fail;
|
||||
}
|
||||
mal->num_tx_chans = prop[0];
|
||||
|
||||
prop = of_get_property(ofdev->node, "num-rx-chans", NULL);
|
||||
if (prop == NULL) {
|
||||
printk(KERN_ERR
|
||||
"mal%d: can't find MAL num-rx-chans property!\n",
|
||||
index);
|
||||
err = -ENODEV;
|
||||
goto fail;
|
||||
}
|
||||
mal->num_rx_chans = prop[0];
|
||||
|
||||
mal->dcr_base = dcr_resource_start(ofdev->node, 0);
|
||||
if (mal->dcr_base == 0) {
|
||||
printk(KERN_ERR
|
||||
"mal%d: can't find DCR resource!\n", index);
|
||||
err = -ENODEV;
|
||||
goto fail;
|
||||
}
|
||||
mal->dcr_host = dcr_map(ofdev->node, mal->dcr_base, 0x100);
|
||||
if (!DCR_MAP_OK(mal->dcr_host)) {
|
||||
printk(KERN_ERR
|
||||
"mal%d: failed to map DCRs !\n", index);
|
||||
err = -ENODEV;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
mal->txeob_irq = irq_of_parse_and_map(ofdev->node, 0);
|
||||
mal->rxeob_irq = irq_of_parse_and_map(ofdev->node, 1);
|
||||
mal->serr_irq = irq_of_parse_and_map(ofdev->node, 2);
|
||||
mal->txde_irq = irq_of_parse_and_map(ofdev->node, 3);
|
||||
mal->rxde_irq = irq_of_parse_and_map(ofdev->node, 4);
|
||||
if (mal->txeob_irq == NO_IRQ || mal->rxeob_irq == NO_IRQ ||
|
||||
mal->serr_irq == NO_IRQ || mal->txde_irq == NO_IRQ ||
|
||||
mal->rxde_irq == NO_IRQ) {
|
||||
printk(KERN_ERR
|
||||
"mal%d: failed to map interrupts !\n", index);
|
||||
err = -ENODEV;
|
||||
goto fail_unmap;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&mal->poll_list);
|
||||
set_bit(__LINK_STATE_START, &mal->poll_dev.state);
|
||||
mal->poll_dev.weight = CONFIG_IBM_NEW_EMAC_POLL_WEIGHT;
|
||||
mal->poll_dev.poll = mal_poll;
|
||||
mal->poll_dev.priv = mal;
|
||||
atomic_set(&mal->poll_dev.refcnt, 1);
|
||||
INIT_LIST_HEAD(&mal->list);
|
||||
spin_lock_init(&mal->lock);
|
||||
|
||||
/* Load power-on reset defaults */
|
||||
mal_reset(mal);
|
||||
|
||||
/* Set the MAL configuration register */
|
||||
cfg = (mal->version == 2) ? MAL2_CFG_DEFAULT : MAL1_CFG_DEFAULT;
|
||||
cfg |= MAL_CFG_PLBB | MAL_CFG_OPBBL | MAL_CFG_LEA;
|
||||
|
||||
/* Current Axon is not happy with priority being non-0, it can
|
||||
* deadlock, fix it up here
|
||||
*/
|
||||
if (of_device_is_compatible(ofdev->node, "ibm,mcmal-axon"))
|
||||
cfg &= ~(MAL2_CFG_RPP_10 | MAL2_CFG_WPP_10);
|
||||
|
||||
/* Apply configuration */
|
||||
set_mal_dcrn(mal, MAL_CFG, cfg);
|
||||
|
||||
/* Allocate space for BD rings */
|
||||
BUG_ON(mal->num_tx_chans <= 0 || mal->num_tx_chans > 32);
|
||||
BUG_ON(mal->num_rx_chans <= 0 || mal->num_rx_chans > 32);
|
||||
|
||||
bd_size = sizeof(struct mal_descriptor) *
|
||||
(NUM_TX_BUFF * mal->num_tx_chans +
|
||||
NUM_RX_BUFF * mal->num_rx_chans);
|
||||
mal->bd_virt =
|
||||
dma_alloc_coherent(&ofdev->dev, bd_size, &mal->bd_dma,
|
||||
GFP_KERNEL);
|
||||
if (mal->bd_virt == NULL) {
|
||||
printk(KERN_ERR
|
||||
"mal%d: out of memory allocating RX/TX descriptors!\n",
|
||||
index);
|
||||
err = -ENOMEM;
|
||||
goto fail_unmap;
|
||||
}
|
||||
memset(mal->bd_virt, 0, bd_size);
|
||||
|
||||
for (i = 0; i < mal->num_tx_chans; ++i)
|
||||
set_mal_dcrn(mal, MAL_TXCTPR(i), mal->bd_dma +
|
||||
sizeof(struct mal_descriptor) *
|
||||
mal_tx_bd_offset(mal, i));
|
||||
|
||||
for (i = 0; i < mal->num_rx_chans; ++i)
|
||||
set_mal_dcrn(mal, MAL_RXCTPR(i), mal->bd_dma +
|
||||
sizeof(struct mal_descriptor) *
|
||||
mal_rx_bd_offset(mal, i));
|
||||
|
||||
err = request_irq(mal->serr_irq, mal_serr, 0, "MAL SERR", mal);
|
||||
if (err)
|
||||
goto fail2;
|
||||
err = request_irq(mal->txde_irq, mal_txde, 0, "MAL TX DE", mal);
|
||||
if (err)
|
||||
goto fail3;
|
||||
err = request_irq(mal->txeob_irq, mal_txeob, 0, "MAL TX EOB", mal);
|
||||
if (err)
|
||||
goto fail4;
|
||||
err = request_irq(mal->rxde_irq, mal_rxde, 0, "MAL RX DE", mal);
|
||||
if (err)
|
||||
goto fail5;
|
||||
err = request_irq(mal->rxeob_irq, mal_rxeob, 0, "MAL RX EOB", mal);
|
||||
if (err)
|
||||
goto fail6;
|
||||
|
||||
/* Enable all MAL SERR interrupt sources */
|
||||
if (mal->version == 2)
|
||||
set_mal_dcrn(mal, MAL_IER, MAL2_IER_EVENTS);
|
||||
else
|
||||
set_mal_dcrn(mal, MAL_IER, MAL1_IER_EVENTS);
|
||||
|
||||
/* Enable EOB interrupt */
|
||||
mal_enable_eob_irq(mal);
|
||||
|
||||
printk(KERN_INFO
|
||||
"MAL v%d %s, %d TX channels, %d RX channels\n",
|
||||
mal->version, ofdev->node->full_name,
|
||||
mal->num_tx_chans, mal->num_rx_chans);
|
||||
|
||||
/* Advertise this instance to the rest of the world */
|
||||
wmb();
|
||||
dev_set_drvdata(&ofdev->dev, mal);
|
||||
|
||||
mal_dbg_register(mal);
|
||||
|
||||
return 0;
|
||||
|
||||
fail6:
|
||||
free_irq(mal->rxde_irq, mal);
|
||||
fail5:
|
||||
free_irq(mal->txeob_irq, mal);
|
||||
fail4:
|
||||
free_irq(mal->txde_irq, mal);
|
||||
fail3:
|
||||
free_irq(mal->serr_irq, mal);
|
||||
fail2:
|
||||
dma_free_coherent(&ofdev->dev, bd_size, mal->bd_virt, mal->bd_dma);
|
||||
fail_unmap:
|
||||
dcr_unmap(mal->dcr_host, mal->dcr_base, 0x100);
|
||||
fail:
|
||||
kfree(mal);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devexit mal_remove(struct of_device *ofdev)
|
||||
{
|
||||
struct mal_instance *mal = dev_get_drvdata(&ofdev->dev);
|
||||
|
||||
MAL_DBG(mal, "remove" NL);
|
||||
|
||||
/* 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->index);
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
||||
dev_set_drvdata(&ofdev->dev, NULL);
|
||||
|
||||
free_irq(mal->serr_irq, mal);
|
||||
free_irq(mal->txde_irq, mal);
|
||||
free_irq(mal->txeob_irq, mal);
|
||||
free_irq(mal->rxde_irq, mal);
|
||||
free_irq(mal->rxeob_irq, mal);
|
||||
|
||||
mal_reset(mal);
|
||||
|
||||
mal_dbg_unregister(mal);
|
||||
|
||||
dma_free_coherent(&ofdev->dev,
|
||||
sizeof(struct mal_descriptor) *
|
||||
(NUM_TX_BUFF * mal->num_tx_chans +
|
||||
NUM_RX_BUFF * mal->num_rx_chans), mal->bd_virt,
|
||||
mal->bd_dma);
|
||||
kfree(mal);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id mal_platform_match[] =
|
||||
{
|
||||
{
|
||||
.compatible = "ibm,mcmal",
|
||||
},
|
||||
{
|
||||
.compatible = "ibm,mcmal2",
|
||||
},
|
||||
/* Backward compat */
|
||||
{
|
||||
.type = "mcmal-dma",
|
||||
.compatible = "ibm,mcmal",
|
||||
},
|
||||
{
|
||||
.type = "mcmal-dma",
|
||||
.compatible = "ibm,mcmal2",
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static struct of_platform_driver mal_of_driver = {
|
||||
.name = "mcmal",
|
||||
.match_table = mal_platform_match,
|
||||
|
||||
.probe = mal_probe,
|
||||
.remove = mal_remove,
|
||||
};
|
||||
|
||||
int __init mal_init(void)
|
||||
{
|
||||
return of_register_platform_driver(&mal_of_driver);
|
||||
}
|
||||
|
||||
void mal_exit(void)
|
||||
{
|
||||
of_unregister_platform_driver(&mal_of_driver);
|
||||
}
|
276
drivers/net/ibm_newemac/mal.h
Normal file
276
drivers/net/ibm_newemac/mal.h
Normal file
@ -0,0 +1,276 @@
|
||||
/*
|
||||
* drivers/net/ibm_newemac/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_NEWEMAC_MAL_H
|
||||
#define __IBM_NEWEMAC_MAL_H
|
||||
|
||||
/*
|
||||
* There are some variations on the MAL, we express them in this driver as
|
||||
* MAL Version 1 and 2 though that doesn't match any IBM terminology.
|
||||
*
|
||||
* We call MAL 1 the version in 405GP, 405GPR, 405EP, 440EP, 440GR and
|
||||
* NP405H.
|
||||
*
|
||||
* We call MAL 2 the version in 440GP, 440GX, 440SP, 440SPE and Axon
|
||||
*
|
||||
* The driver expects a "version" property in the emac node containing
|
||||
* a number 1 or 2. New device-trees for EMAC capable platforms are thus
|
||||
* required to include that when porting to arch/powerpc.
|
||||
*/
|
||||
|
||||
/* 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
|
||||
|
||||
/* MAL V1 CFG bits */
|
||||
#define MAL1_CFG_PLBP_MASK 0x00c00000
|
||||
#define MAL1_CFG_PLBP_10 0x00800000
|
||||
#define MAL1_CFG_GA 0x00200000
|
||||
#define MAL1_CFG_OA 0x00100000
|
||||
#define MAL1_CFG_PLBLE 0x00080000
|
||||
#define MAL1_CFG_PLBT_MASK 0x00078000
|
||||
#define MAL1_CFG_DEFAULT (MAL1_CFG_PLBP_10 | MAL1_CFG_PLBT_MASK)
|
||||
|
||||
/* MAL V2 CFG bits */
|
||||
#define MAL2_CFG_RPP_MASK 0x00c00000
|
||||
#define MAL2_CFG_RPP_10 0x00800000
|
||||
#define MAL2_CFG_RMBS_MASK 0x00300000
|
||||
#define MAL2_CFG_WPP_MASK 0x000c0000
|
||||
#define MAL2_CFG_WPP_10 0x00080000
|
||||
#define MAL2_CFG_WMBS_MASK 0x00030000
|
||||
#define MAL2_CFG_PLBLE 0x00008000
|
||||
#define MAL2_CFG_DEFAULT (MAL2_CFG_RMBS_MASK | MAL2_CFG_WMBS_MASK | \
|
||||
MAL2_CFG_RPP_10 | MAL2_CFG_WPP_10)
|
||||
|
||||
#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
|
||||
|
||||
/* MAL V1 ESR bits */
|
||||
#define MAL1_ESR_ONE 0x00080000
|
||||
#define MAL1_ESR_ONEI 0x00000008
|
||||
|
||||
/* MAL V2 ESR bits */
|
||||
#define MAL2_ESR_PTE 0x00800000
|
||||
#define MAL2_ESR_PRE 0x00400000
|
||||
#define MAL2_ESR_PWE 0x00200000
|
||||
#define MAL2_ESR_PTEI 0x00000080
|
||||
#define MAL2_ESR_PREI 0x00000040
|
||||
#define MAL2_ESR_PWEI 0x00000020
|
||||
|
||||
|
||||
#define MAL_IER 0x02
|
||||
#define MAL_IER_DE 0x00000010
|
||||
#define MAL_IER_OTE 0x00000004
|
||||
#define MAL_IER_OE 0x00000002
|
||||
#define MAL_IER_PE 0x00000001
|
||||
/* MAL V1 IER bits */
|
||||
#define MAL1_IER_NWE 0x00000008
|
||||
#define MAL1_IER_SOC_EVENTS MAL1_IER_NWE
|
||||
#define MAL1_IER_EVENTS (MAL1_IER_SOC_EVENTS | MAL_IER_OTE | \
|
||||
MAL_IER_OTE | MAL_IER_OE | MAL_IER_PE)
|
||||
|
||||
/* MAL V2 IER bits */
|
||||
#define MAL2_IER_PT 0x00000080
|
||||
#define MAL2_IER_PRE 0x00000040
|
||||
#define MAL2_IER_PWE 0x00000020
|
||||
#define MAL2_IER_SOC_EVENTS (MAL2_IER_PT | MAL2_IER_PRE | MAL2_IER_PWE)
|
||||
#define MAL2_IER_EVENTS (MAL2_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 */
|
||||
struct mal_descriptor {
|
||||
u16 ctrl; /* MAL / Commac status control bits */
|
||||
u16 data_len; /* Max length is 4K-1 (12 bits) */
|
||||
u32 data_ptr; /* pointer to actual data buffer */
|
||||
};
|
||||
|
||||
/* the following defines are for the MadMAL status and control registers. */
|
||||
/* MADMAL transmit and receive status/control bits */
|
||||
#define MAL_RX_CTRL_EMPTY 0x8000
|
||||
#define MAL_RX_CTRL_WRAP 0x4000
|
||||
#define MAL_RX_CTRL_CM 0x2000
|
||||
#define MAL_RX_CTRL_LAST 0x1000
|
||||
#define MAL_RX_CTRL_FIRST 0x0800
|
||||
#define MAL_RX_CTRL_INTR 0x0400
|
||||
#define MAL_RX_CTRL_SINGLE (MAL_RX_CTRL_LAST | MAL_RX_CTRL_FIRST)
|
||||
#define MAL_IS_SINGLE_RX(ctrl) (((ctrl) & MAL_RX_CTRL_SINGLE) == MAL_RX_CTRL_SINGLE)
|
||||
|
||||
#define MAL_TX_CTRL_READY 0x8000
|
||||
#define MAL_TX_CTRL_WRAP 0x4000
|
||||
#define MAL_TX_CTRL_CM 0x2000
|
||||
#define MAL_TX_CTRL_LAST 0x1000
|
||||
#define MAL_TX_CTRL_INTR 0x0400
|
||||
|
||||
struct mal_commac_ops {
|
||||
void (*poll_tx) (void *dev);
|
||||
int (*poll_rx) (void *dev, int budget);
|
||||
int (*peek_rx) (void *dev);
|
||||
void (*rxde) (void *dev);
|
||||
};
|
||||
|
||||
struct mal_commac {
|
||||
struct mal_commac_ops *ops;
|
||||
void *dev;
|
||||
struct list_head poll_list;
|
||||
long flags;
|
||||
#define MAL_COMMAC_RX_STOPPED 0
|
||||
#define MAL_COMMAC_POLL_DISABLED 1
|
||||
u32 tx_chan_mask;
|
||||
u32 rx_chan_mask;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct mal_instance {
|
||||
int version;
|
||||
int dcr_base;
|
||||
dcr_host_t dcr_host;
|
||||
|
||||
int num_tx_chans; /* Number of TX channels */
|
||||
int num_rx_chans; /* Number of RX channels */
|
||||
int txeob_irq; /* TX End Of Buffer IRQ */
|
||||
int rxeob_irq; /* RX End Of Buffer IRQ */
|
||||
int txde_irq; /* TX Descriptor Error IRQ */
|
||||
int rxde_irq; /* RX Descriptor Error IRQ */
|
||||
int serr_irq; /* MAL System Error IRQ */
|
||||
|
||||
struct list_head poll_list;
|
||||
struct net_device poll_dev;
|
||||
|
||||
struct list_head list;
|
||||
u32 tx_chan_mask;
|
||||
u32 rx_chan_mask;
|
||||
|
||||
dma_addr_t bd_dma;
|
||||
struct mal_descriptor *bd_virt;
|
||||
|
||||
struct of_device *ofdev;
|
||||
int index;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static inline u32 get_mal_dcrn(struct mal_instance *mal, int reg)
|
||||
{
|
||||
return dcr_read(mal->dcr_host, mal->dcr_base + reg);
|
||||
}
|
||||
|
||||
static inline void set_mal_dcrn(struct mal_instance *mal, int reg, u32 val)
|
||||
{
|
||||
dcr_write(mal->dcr_host, mal->dcr_base + reg, val);
|
||||
}
|
||||
|
||||
/* Register MAL devices */
|
||||
int mal_init(void);
|
||||
void mal_exit(void);
|
||||
|
||||
int mal_register_commac(struct mal_instance *mal,
|
||||
struct mal_commac *commac);
|
||||
void mal_unregister_commac(struct mal_instance *mal,
|
||||
struct mal_commac *commac);
|
||||
int mal_set_rcbs(struct mal_instance *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 mal_instance *mal, int channel);
|
||||
int mal_rx_bd_offset(struct mal_instance *mal, int channel);
|
||||
|
||||
void mal_enable_tx_channel(struct mal_instance *mal, int channel);
|
||||
void mal_disable_tx_channel(struct mal_instance *mal, int channel);
|
||||
void mal_enable_rx_channel(struct mal_instance *mal, int channel);
|
||||
void mal_disable_rx_channel(struct mal_instance *mal, int channel);
|
||||
|
||||
void mal_poll_disable(struct mal_instance *mal, struct mal_commac *commac);
|
||||
void mal_poll_enable(struct mal_instance *mal, struct mal_commac *commac);
|
||||
|
||||
/* Add/remove EMAC to/from MAL polling list */
|
||||
void mal_poll_add(struct mal_instance *mal, struct mal_commac *commac);
|
||||
void mal_poll_del(struct mal_instance *mal, struct mal_commac *commac);
|
||||
|
||||
/* Ethtool MAL registers */
|
||||
struct 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];
|
||||
};
|
||||
|
||||
int mal_get_regs_len(struct mal_instance *mal);
|
||||
void *mal_dump_regs(struct mal_instance *mal, void *buf);
|
||||
|
||||
#endif /* __IBM_NEWEMAC_MAL_H */
|
373
drivers/net/ibm_newemac/phy.c
Normal file
373
drivers/net/ibm_newemac/phy.c
Normal file
@ -0,0 +1,373 @@
|
||||
/*
|
||||
* drivers/net/ibm_newemac/phy.c
|
||||
*
|
||||
* Driver for PowerPC 4xx on-chip ethernet controller, PHY support.
|
||||
* Borrowed from sungem_phy.c, though I only kept the generic MII
|
||||
* driver for now.
|
||||
*
|
||||
* This file should be shared with other drivers or eventually
|
||||
* merged as the "low level" part of miilib
|
||||
*
|
||||
* (c) 2003, Benjamin Herrenscmidt (benh@kernel.crashing.org)
|
||||
* (c) 2004-2005, Eugene Surovegin <ebs@ebshome.net>
|
||||
*
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "emac.h"
|
||||
#include "phy.h"
|
||||
|
||||
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)
|
||||
{
|
||||
phy->mdio_write(phy->dev, phy->address, reg, val);
|
||||
}
|
||||
|
||||
int emac_mii_reset_phy(struct mii_phy *phy)
|
||||
{
|
||||
int val;
|
||||
int limit = 10000;
|
||||
|
||||
val = phy_read(phy, MII_BMCR);
|
||||
val &= ~(BMCR_ISOLATE | BMCR_ANENABLE);
|
||||
val |= BMCR_RESET;
|
||||
phy_write(phy, MII_BMCR, val);
|
||||
|
||||
udelay(300);
|
||||
|
||||
while (limit--) {
|
||||
val = phy_read(phy, MII_BMCR);
|
||||
if (val >= 0 && (val & BMCR_RESET) == 0)
|
||||
break;
|
||||
udelay(10);
|
||||
}
|
||||
if ((val & BMCR_ISOLATE) && limit > 0)
|
||||
phy_write(phy, MII_BMCR, val & ~BMCR_ISOLATE);
|
||||
|
||||
return limit <= 0;
|
||||
}
|
||||
|
||||
static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise)
|
||||
{
|
||||
int ctl, adv;
|
||||
|
||||
phy->autoneg = AUTONEG_ENABLE;
|
||||
phy->speed = SPEED_10;
|
||||
phy->duplex = DUPLEX_HALF;
|
||||
phy->pause = phy->asym_pause = 0;
|
||||
phy->advertising = advertise;
|
||||
|
||||
ctl = phy_read(phy, MII_BMCR);
|
||||
if (ctl < 0)
|
||||
return ctl;
|
||||
ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE);
|
||||
|
||||
/* First clear the PHY */
|
||||
phy_write(phy, MII_BMCR, ctl);
|
||||
|
||||
/* Setup standard advertise */
|
||||
adv = phy_read(phy, MII_ADVERTISE);
|
||||
if (adv < 0)
|
||||
return adv;
|
||||
adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP |
|
||||
ADVERTISE_PAUSE_ASYM);
|
||||
if (advertise & ADVERTISED_10baseT_Half)
|
||||
adv |= ADVERTISE_10HALF;
|
||||
if (advertise & ADVERTISED_10baseT_Full)
|
||||
adv |= ADVERTISE_10FULL;
|
||||
if (advertise & ADVERTISED_100baseT_Half)
|
||||
adv |= ADVERTISE_100HALF;
|
||||
if (advertise & ADVERTISED_100baseT_Full)
|
||||
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);
|
||||
|
||||
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 */
|
||||
ctl = phy_read(phy, MII_BMCR);
|
||||
ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
|
||||
phy_write(phy, MII_BMCR, ctl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int genmii_setup_forced(struct mii_phy *phy, int speed, int fd)
|
||||
{
|
||||
int ctl;
|
||||
|
||||
phy->autoneg = AUTONEG_DISABLE;
|
||||
phy->speed = speed;
|
||||
phy->duplex = fd;
|
||||
phy->pause = phy->asym_pause = 0;
|
||||
|
||||
ctl = phy_read(phy, MII_BMCR);
|
||||
if (ctl < 0)
|
||||
return ctl;
|
||||
ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE);
|
||||
|
||||
/* First clear the PHY */
|
||||
phy_write(phy, MII_BMCR, ctl | BMCR_RESET);
|
||||
|
||||
/* Select speed & duplex */
|
||||
switch (speed) {
|
||||
case SPEED_10:
|
||||
break;
|
||||
case SPEED_100:
|
||||
ctl |= BMCR_SPEED100;
|
||||
break;
|
||||
case SPEED_1000:
|
||||
ctl |= BMCR_SPEED1000;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (fd == DUPLEX_FULL)
|
||||
ctl |= BMCR_FULLDPLX;
|
||||
phy_write(phy, MII_BMCR, ctl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int genmii_poll_link(struct mii_phy *phy)
|
||||
{
|
||||
int status;
|
||||
|
||||
/* Clear latched value with dummy read */
|
||||
phy_read(phy, MII_BMSR);
|
||||
status = phy_read(phy, MII_BMSR);
|
||||
if (status < 0 || (status & BMSR_LSTATUS) == 0)
|
||||
return 0;
|
||||
if (phy->autoneg == AUTONEG_ENABLE && !(status & BMSR_ANEGCOMPLETE))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int genmii_read_link(struct mii_phy *phy)
|
||||
{
|
||||
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->duplex == DUPLEX_FULL) {
|
||||
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 (bmcr & BMCR_FULLDPLX)
|
||||
phy->duplex = DUPLEX_FULL;
|
||||
else
|
||||
phy->duplex = DUPLEX_HALF;
|
||||
if (bmcr & BMCR_SPEED1000)
|
||||
phy->speed = SPEED_1000;
|
||||
else if (bmcr & BMCR_SPEED100)
|
||||
phy->speed = SPEED_100;
|
||||
else
|
||||
phy->speed = SPEED_10;
|
||||
|
||||
phy->pause = phy->asym_pause = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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_10BTCSR 0x16
|
||||
#define TENBTCSR_ECHO_DISABLE 0x2000
|
||||
#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
|
||||
#define MII_CIS8201_ACSR 0x1c
|
||||
#define ACSR_PIN_PRIO_SELECT 0x0004
|
||||
|
||||
static int cis8201_init(struct mii_phy *phy)
|
||||
{
|
||||
int epcr;
|
||||
|
||||
epcr = phy_read(phy, MII_CIS8201_EPCR);
|
||||
if (epcr < 0)
|
||||
return 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);
|
||||
|
||||
/* MII regs override strap pins */
|
||||
phy_write(phy, MII_CIS8201_ACSR,
|
||||
phy_read(phy, MII_CIS8201_ACSR) | ACSR_PIN_PRIO_SELECT);
|
||||
|
||||
/* Disable TX_EN -> CRS echo mode, otherwise 10/HDX doesn't work */
|
||||
phy_write(phy, MII_CIS8201_10BTCSR,
|
||||
phy_read(phy, MII_CIS8201_10BTCSR) | TENBTCSR_ECHO_DISABLE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct mii_phy_ops cis8201_phy_ops = {
|
||||
.init = cis8201_init,
|
||||
.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 = {
|
||||
.phy_id = 0x000fc410,
|
||||
.phy_id_mask = 0x000ffff0,
|
||||
.name = "CIS8201 Gigabit Ethernet",
|
||||
.ops = &cis8201_phy_ops
|
||||
};
|
||||
|
||||
static struct mii_phy_def *mii_phy_table[] = {
|
||||
&cis8201_phy_def,
|
||||
&genmii_phy_def,
|
||||
NULL
|
||||
};
|
||||
|
||||
int emac_mii_phy_probe(struct mii_phy *phy, int address)
|
||||
{
|
||||
struct mii_phy_def *def;
|
||||
int i;
|
||||
u32 id;
|
||||
|
||||
phy->autoneg = AUTONEG_DISABLE;
|
||||
phy->advertising = 0;
|
||||
phy->address = address;
|
||||
phy->speed = SPEED_10;
|
||||
phy->duplex = DUPLEX_HALF;
|
||||
phy->pause = phy->asym_pause = 0;
|
||||
|
||||
/* Take PHY out of isolate mode and reset it. */
|
||||
if (emac_mii_reset_phy(phy))
|
||||
return -ENODEV;
|
||||
|
||||
/* Read ID and find matching entry */
|
||||
id = (phy_read(phy, MII_PHYSID1) << 16) | phy_read(phy, MII_PHYSID2);
|
||||
for (i = 0; (def = mii_phy_table[i]) != NULL; i++)
|
||||
if ((id & def->phy_id_mask) == def->phy_id)
|
||||
break;
|
||||
/* Should never be NULL (we have a generic entry), but... */
|
||||
if (!def)
|
||||
return -ENODEV;
|
||||
|
||||
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 */
|
||||
phy->advertising = phy->features;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
80
drivers/net/ibm_newemac/phy.h
Normal file
80
drivers/net/ibm_newemac/phy.h
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* drivers/net/ibm_newemac/phy.h
|
||||
*
|
||||
* Driver for PowerPC 4xx on-chip ethernet controller, PHY support
|
||||
*
|
||||
* Benjamin Herrenschmidt <benh@kernel.crashing.org>
|
||||
* February 2003
|
||||
*
|
||||
* Minor additions by Eugene Surovegin <ebs@ebshome.net>, 2004
|
||||
*
|
||||
* 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
|
||||
* supported. I'm looking into merging that in a single mii layer more
|
||||
* flexible than mii.c
|
||||
*/
|
||||
|
||||
#ifndef __IBM_NEWEMAC_PHY_H
|
||||
#define __IBM_NEWEMAC_PHY_H
|
||||
|
||||
struct mii_phy;
|
||||
|
||||
/* Operations supported by any kind of PHY */
|
||||
struct mii_phy_ops {
|
||||
int (*init) (struct mii_phy * phy);
|
||||
int (*suspend) (struct mii_phy * phy, int wol_options);
|
||||
int (*setup_aneg) (struct mii_phy * phy, u32 advertise);
|
||||
int (*setup_forced) (struct mii_phy * phy, int speed, int fd);
|
||||
int (*poll_link) (struct mii_phy * phy);
|
||||
int (*read_link) (struct mii_phy * phy);
|
||||
};
|
||||
|
||||
/* Structure used to statically define an mii/gii based PHY */
|
||||
struct mii_phy_def {
|
||||
u32 phy_id; /* Concatenated ID1 << 16 | ID2 */
|
||||
u32 phy_id_mask; /* Significant bits */
|
||||
u32 features; /* Ethtool SUPPORTED_* defines or
|
||||
0 for autodetect */
|
||||
int magic_aneg; /* Autoneg does all speed test for us */
|
||||
const char *name;
|
||||
const struct mii_phy_ops *ops;
|
||||
};
|
||||
|
||||
/* An instance of a PHY, partially borrowed from mii_if_info */
|
||||
struct mii_phy {
|
||||
struct mii_phy_def *def;
|
||||
u32 advertising; /* Ethtool ADVERTISED_* defines */
|
||||
u32 features; /* Copied from mii_phy_def.features
|
||||
or determined automaticaly */
|
||||
int address; /* PHY address */
|
||||
int mode; /* PHY mode */
|
||||
|
||||
/* 1: autoneg enabled, 0: disabled */
|
||||
int autoneg;
|
||||
|
||||
/* forced speed & duplex (no autoneg)
|
||||
* partner speed & duplex & pause (autoneg)
|
||||
*/
|
||||
int speed;
|
||||
int duplex;
|
||||
int pause;
|
||||
int asym_pause;
|
||||
|
||||
/* Provided by host chip */
|
||||
struct net_device *dev;
|
||||
int (*mdio_read) (struct net_device * dev, int addr, int reg);
|
||||
void (*mdio_write) (struct net_device * dev, int addr, int reg,
|
||||
int val);
|
||||
};
|
||||
|
||||
/* Pass in a struct mii_phy with dev, mdio_read and mdio_write
|
||||
* filled, the remaining fields will be filled on return
|
||||
*/
|
||||
int emac_mii_phy_probe(struct mii_phy *phy, int address);
|
||||
int emac_mii_reset_phy(struct mii_phy *phy);
|
||||
|
||||
#endif /* __IBM_NEWEMAC_PHY_H */
|
323
drivers/net/ibm_newemac/rgmii.c
Normal file
323
drivers/net/ibm_newemac/rgmii.c
Normal file
@ -0,0 +1,323 @@
|
||||
/*
|
||||
* drivers/net/ibm_newemac/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/kernel.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "emac.h"
|
||||
#include "debug.h"
|
||||
|
||||
// XXX FIXME: Axon seems to support a subset of the RGMII, we
|
||||
// thus need to take that into account and possibly change some
|
||||
// of the bit settings below that don't seem to quite match the
|
||||
// AXON spec
|
||||
|
||||
/* 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();
|
||||
}
|
||||
}
|
||||
|
||||
int __devinit rgmii_attach(struct of_device *ofdev, int input, int mode)
|
||||
{
|
||||
struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
|
||||
struct rgmii_regs *p = dev->base;
|
||||
|
||||
RGMII_DBG(dev, "attach(%d)" NL, input);
|
||||
|
||||
/* Check if we need to attach to a RGMII */
|
||||
if (input < 0 || !rgmii_valid_mode(mode)) {
|
||||
printk(KERN_ERR "%s: unsupported settings !\n",
|
||||
ofdev->node->full_name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
|
||||
/* Enable this input */
|
||||
out_be32(&p->fer, in_be32(&p->fer) | rgmii_mode_mask(mode, input));
|
||||
|
||||
printk(KERN_NOTICE "%s: input %d in %s mode\n",
|
||||
ofdev->node->full_name, input, rgmii_mode_name(mode));
|
||||
|
||||
++dev->users;
|
||||
|
||||
mutex_unlock(&dev->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rgmii_set_speed(struct of_device *ofdev, int input, int speed)
|
||||
{
|
||||
struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
|
||||
struct rgmii_regs *p = dev->base;
|
||||
u32 ssr;
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
|
||||
ssr = in_be32(&p->ssr) & ~RGMII_SSR_MASK(input);
|
||||
|
||||
RGMII_DBG(dev, "speed(%d, %d)" NL, input, speed);
|
||||
|
||||
if (speed == SPEED_1000)
|
||||
ssr |= RGMII_SSR_1000(input);
|
||||
else if (speed == SPEED_100)
|
||||
ssr |= RGMII_SSR_100(input);
|
||||
|
||||
out_be32(&p->ssr, ssr);
|
||||
|
||||
mutex_unlock(&dev->lock);
|
||||
}
|
||||
|
||||
void rgmii_get_mdio(struct of_device *ofdev, int input)
|
||||
{
|
||||
struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
|
||||
struct rgmii_regs *p = dev->base;
|
||||
u32 fer;
|
||||
|
||||
RGMII_DBG2(dev, "get_mdio(%d)" NL, input);
|
||||
|
||||
if (dev->type != RGMII_AXON)
|
||||
return;
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
|
||||
fer = in_be32(&p->fer);
|
||||
fer |= 0x00080000u >> input;
|
||||
out_be32(&p->fer, fer);
|
||||
(void)in_be32(&p->fer);
|
||||
|
||||
DBG2(dev, " fer = 0x%08x\n", fer);
|
||||
}
|
||||
|
||||
void rgmii_put_mdio(struct of_device *ofdev, int input)
|
||||
{
|
||||
struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
|
||||
struct rgmii_regs *p = dev->base;
|
||||
u32 fer;
|
||||
|
||||
RGMII_DBG2(dev, "put_mdio(%d)" NL, input);
|
||||
|
||||
if (dev->type != RGMII_AXON)
|
||||
return;
|
||||
|
||||
fer = in_be32(&p->fer);
|
||||
fer &= ~(0x00080000u >> input);
|
||||
out_be32(&p->fer, fer);
|
||||
(void)in_be32(&p->fer);
|
||||
|
||||
DBG2(dev, " fer = 0x%08x\n", fer);
|
||||
|
||||
mutex_unlock(&dev->lock);
|
||||
}
|
||||
|
||||
void __devexit rgmii_detach(struct of_device *ofdev, int input)
|
||||
{
|
||||
struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
|
||||
struct rgmii_regs *p = dev->base;
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
|
||||
BUG_ON(!dev || dev->users == 0);
|
||||
|
||||
RGMII_DBG(dev, "detach(%d)" NL, input);
|
||||
|
||||
/* Disable this input */
|
||||
out_be32(&p->fer, in_be32(&p->fer) & ~RGMII_FER_MASK(input));
|
||||
|
||||
--dev->users;
|
||||
|
||||
mutex_unlock(&dev->lock);
|
||||
}
|
||||
|
||||
int rgmii_get_regs_len(struct of_device *ofdev)
|
||||
{
|
||||
return sizeof(struct emac_ethtool_regs_subhdr) +
|
||||
sizeof(struct rgmii_regs);
|
||||
}
|
||||
|
||||
void *rgmii_dump_regs(struct of_device *ofdev, void *buf)
|
||||
{
|
||||
struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
|
||||
struct emac_ethtool_regs_subhdr *hdr = buf;
|
||||
struct rgmii_regs *regs = (struct rgmii_regs *)(hdr + 1);
|
||||
|
||||
hdr->version = 0;
|
||||
hdr->index = 0; /* for now, are there chips with more than one
|
||||
* rgmii ? if yes, then we'll add a cell_index
|
||||
* like we do for emac
|
||||
*/
|
||||
memcpy_fromio(regs, dev->base, sizeof(struct rgmii_regs));
|
||||
return regs + 1;
|
||||
}
|
||||
|
||||
|
||||
static int __devinit rgmii_probe(struct of_device *ofdev,
|
||||
const struct of_device_id *match)
|
||||
{
|
||||
struct device_node *np = ofdev->node;
|
||||
struct rgmii_instance *dev;
|
||||
struct resource regs;
|
||||
int rc;
|
||||
|
||||
rc = -ENOMEM;
|
||||
dev = kzalloc(sizeof(struct rgmii_instance), GFP_KERNEL);
|
||||
if (dev == NULL) {
|
||||
printk(KERN_ERR "%s: could not allocate RGMII device!\n",
|
||||
np->full_name);
|
||||
goto err_gone;
|
||||
}
|
||||
|
||||
mutex_init(&dev->lock);
|
||||
dev->ofdev = ofdev;
|
||||
|
||||
rc = -ENXIO;
|
||||
if (of_address_to_resource(np, 0, ®s)) {
|
||||
printk(KERN_ERR "%s: Can't get registers address\n",
|
||||
np->full_name);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
rc = -ENOMEM;
|
||||
dev->base = (struct rgmii_regs *)ioremap(regs.start,
|
||||
sizeof(struct rgmii_regs));
|
||||
if (dev->base == NULL) {
|
||||
printk(KERN_ERR "%s: Can't map device registers!\n",
|
||||
np->full_name);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
/* Check for RGMII type */
|
||||
if (device_is_compatible(ofdev->node, "ibm,rgmii-axon"))
|
||||
dev->type = RGMII_AXON;
|
||||
else
|
||||
dev->type = RGMII_STANDARD;
|
||||
|
||||
DBG2(dev, " Boot FER = 0x%08x, SSR = 0x%08x\n",
|
||||
in_be32(&dev->base->fer), in_be32(&dev->base->ssr));
|
||||
|
||||
/* Disable all inputs by default */
|
||||
out_be32(&dev->base->fer, 0);
|
||||
|
||||
printk(KERN_INFO
|
||||
"RGMII %s %s initialized\n",
|
||||
dev->type == RGMII_STANDARD ? "standard" : "axon",
|
||||
ofdev->node->full_name);
|
||||
|
||||
wmb();
|
||||
dev_set_drvdata(&ofdev->dev, dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
kfree(dev);
|
||||
err_gone:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __devexit rgmii_remove(struct of_device *ofdev)
|
||||
{
|
||||
struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
|
||||
|
||||
dev_set_drvdata(&ofdev->dev, NULL);
|
||||
|
||||
WARN_ON(dev->users != 0);
|
||||
|
||||
iounmap(dev->base);
|
||||
kfree(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id rgmii_match[] =
|
||||
{
|
||||
{
|
||||
.type = "rgmii-interface",
|
||||
.compatible = "ibm,rgmii",
|
||||
},
|
||||
{
|
||||
.type = "emac-rgmii",
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static struct of_platform_driver rgmii_driver = {
|
||||
.name = "emac-rgmii",
|
||||
.match_table = rgmii_match,
|
||||
|
||||
.probe = rgmii_probe,
|
||||
.remove = rgmii_remove,
|
||||
};
|
||||
|
||||
int __init rgmii_init(void)
|
||||
{
|
||||
return of_register_platform_driver(&rgmii_driver);
|
||||
}
|
||||
|
||||
void rgmii_exit(void)
|
||||
{
|
||||
of_unregister_platform_driver(&rgmii_driver);
|
||||
}
|
76
drivers/net/ibm_newemac/rgmii.h
Normal file
76
drivers/net/ibm_newemac/rgmii.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* drivers/net/ibm_newemac/rgmii.h
|
||||
*
|
||||
* Driver for PowerPC 4xx on-chip ethernet controller, RGMII bridge support.
|
||||
*
|
||||
* Based on ocp_zmii.h/ibm_emac_zmii.h
|
||||
* Armin Kuster akuster@mvista.com
|
||||
*
|
||||
* Copyright 2004 MontaVista Software, Inc.
|
||||
* 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
|
||||
* 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_NEWEMAC_RGMII_H
|
||||
#define __IBM_NEWEMAC_RGMII_H
|
||||
|
||||
/* RGMII bridge type */
|
||||
#define RGMII_STANDARD 0
|
||||
#define RGMII_AXON 1
|
||||
|
||||
/* RGMII bridge */
|
||||
struct rgmii_regs {
|
||||
u32 fer; /* Function enable register */
|
||||
u32 ssr; /* Speed select register */
|
||||
};
|
||||
|
||||
/* RGMII device */
|
||||
struct rgmii_instance {
|
||||
struct rgmii_regs __iomem *base;
|
||||
|
||||
/* Type of RGMII bridge */
|
||||
int type;
|
||||
|
||||
/* Only one EMAC whacks us at a time */
|
||||
struct mutex lock;
|
||||
|
||||
/* number of EMACs using this RGMII bridge */
|
||||
int users;
|
||||
|
||||
/* OF device instance */
|
||||
struct of_device *ofdev;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_IBM_NEW_EMAC_RGMII
|
||||
|
||||
extern int rgmii_init(void);
|
||||
extern void rgmii_exit(void);
|
||||
extern int rgmii_attach(struct of_device *ofdev, int input, int mode);
|
||||
extern void rgmii_detach(struct of_device *ofdev, int input);
|
||||
extern void rgmii_get_mdio(struct of_device *ofdev, int input);
|
||||
extern void rgmii_put_mdio(struct of_device *ofdev, int input);
|
||||
extern void rgmii_set_speed(struct of_device *ofdev, int input, int speed);
|
||||
extern int rgmii_get_regs_len(struct of_device *ofdev);
|
||||
extern void *rgmii_dump_regs(struct of_device *ofdev, void *buf);
|
||||
|
||||
#else
|
||||
|
||||
# define rgmii_init() 0
|
||||
# define rgmii_exit() do { } while(0)
|
||||
# define rgmii_attach(x,y,z) (-ENXIO)
|
||||
# define rgmii_detach(x,y) do { } while(0)
|
||||
# define rgmii_get_mdio(o,i) do { } while (0)
|
||||
# define rgmii_put_mdio(o,i) do { } while (0)
|
||||
# define rgmii_set_speed(x,y,z) do { } while(0)
|
||||
# define rgmii_get_regs_len(x) 0
|
||||
# define rgmii_dump_regs(x,buf) (buf)
|
||||
#endif /* !CONFIG_IBM_NEW_EMAC_RGMII */
|
||||
|
||||
#endif /* __IBM_NEWEMAC_RGMII_H */
|
173
drivers/net/ibm_newemac/tah.c
Normal file
173
drivers/net/ibm_newemac/tah.c
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* drivers/net/ibm_newemac/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 <asm/io.h>
|
||||
|
||||
#include "emac.h"
|
||||
#include "core.h"
|
||||
|
||||
int __devinit tah_attach(struct of_device *ofdev, int channel)
|
||||
{
|
||||
struct tah_instance *dev = dev_get_drvdata(&ofdev->dev);
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
/* Reset has been done at probe() time... nothing else to do for now */
|
||||
++dev->users;
|
||||
mutex_unlock(&dev->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __devexit tah_detach(struct of_device *ofdev, int channel)
|
||||
{
|
||||
struct tah_instance *dev = dev_get_drvdata(&ofdev->dev);
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
--dev->users;
|
||||
mutex_unlock(&dev->lock);
|
||||
}
|
||||
|
||||
void tah_reset(struct of_device *ofdev)
|
||||
{
|
||||
struct tah_instance *dev = dev_get_drvdata(&ofdev->dev);
|
||||
struct tah_regs *p = dev->base;
|
||||
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 "%s: reset timeout\n", ofdev->node->full_name);
|
||||
|
||||
/* 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 of_device *ofdev)
|
||||
{
|
||||
return sizeof(struct emac_ethtool_regs_subhdr) +
|
||||
sizeof(struct tah_regs);
|
||||
}
|
||||
|
||||
void *tah_dump_regs(struct of_device *ofdev, void *buf)
|
||||
{
|
||||
struct tah_instance *dev = dev_get_drvdata(&ofdev->dev);
|
||||
struct emac_ethtool_regs_subhdr *hdr = buf;
|
||||
struct tah_regs *regs = (struct tah_regs *)(hdr + 1);
|
||||
|
||||
hdr->version = 0;
|
||||
hdr->index = 0; /* for now, are there chips with more than one
|
||||
* zmii ? if yes, then we'll add a cell_index
|
||||
* like we do for emac
|
||||
*/
|
||||
memcpy_fromio(regs, dev->base, sizeof(struct tah_regs));
|
||||
return regs + 1;
|
||||
}
|
||||
|
||||
static int __devinit tah_probe(struct of_device *ofdev,
|
||||
const struct of_device_id *match)
|
||||
{
|
||||
struct device_node *np = ofdev->node;
|
||||
struct tah_instance *dev;
|
||||
struct resource regs;
|
||||
int rc;
|
||||
|
||||
rc = -ENOMEM;
|
||||
dev = kzalloc(sizeof(struct tah_instance), GFP_KERNEL);
|
||||
if (dev == NULL) {
|
||||
printk(KERN_ERR "%s: could not allocate TAH device!\n",
|
||||
np->full_name);
|
||||
goto err_gone;
|
||||
}
|
||||
|
||||
mutex_init(&dev->lock);
|
||||
dev->ofdev = ofdev;
|
||||
|
||||
rc = -ENXIO;
|
||||
if (of_address_to_resource(np, 0, ®s)) {
|
||||
printk(KERN_ERR "%s: Can't get registers address\n",
|
||||
np->full_name);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
rc = -ENOMEM;
|
||||
dev->base = (struct tah_regs *)ioremap(regs.start,
|
||||
sizeof(struct tah_regs));
|
||||
if (dev->base == NULL) {
|
||||
printk(KERN_ERR "%s: Can't map device registers!\n",
|
||||
np->full_name);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
/* Initialize TAH and enable IPv4 checksum verification, no TSO yet */
|
||||
tah_reset(ofdev);
|
||||
|
||||
printk(KERN_INFO
|
||||
"TAH %s initialized\n", ofdev->node->full_name);
|
||||
wmb();
|
||||
dev_set_drvdata(&ofdev->dev, dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
kfree(dev);
|
||||
err_gone:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __devexit tah_remove(struct of_device *ofdev)
|
||||
{
|
||||
struct tah_instance *dev = dev_get_drvdata(&ofdev->dev);
|
||||
|
||||
dev_set_drvdata(&ofdev->dev, NULL);
|
||||
|
||||
WARN_ON(dev->users != 0);
|
||||
|
||||
iounmap(dev->base);
|
||||
kfree(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id tah_match[] =
|
||||
{
|
||||
{
|
||||
.type = "tah",
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static struct of_platform_driver tah_driver = {
|
||||
.name = "emac-tah",
|
||||
.match_table = tah_match,
|
||||
|
||||
.probe = tah_probe,
|
||||
.remove = tah_remove,
|
||||
};
|
||||
|
||||
int __init tah_init(void)
|
||||
{
|
||||
return of_register_platform_driver(&tah_driver);
|
||||
}
|
||||
|
||||
void tah_exit(void)
|
||||
{
|
||||
of_unregister_platform_driver(&tah_driver);
|
||||
}
|
90
drivers/net/ibm_newemac/tah.h
Normal file
90
drivers/net/ibm_newemac/tah.h
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* drivers/net/ibm_newemac/tah.h
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __IBM_NEWEMAC_TAH_H
|
||||
#define __IBM_NEWEMAC_TAH_H
|
||||
|
||||
/* TAH */
|
||||
struct tah_regs {
|
||||
u32 revid;
|
||||
u32 pad[3];
|
||||
u32 mr;
|
||||
u32 ssr0;
|
||||
u32 ssr1;
|
||||
u32 ssr2;
|
||||
u32 ssr3;
|
||||
u32 ssr4;
|
||||
u32 ssr5;
|
||||
u32 tsr;
|
||||
};
|
||||
|
||||
|
||||
/* TAH device */
|
||||
struct tah_instance {
|
||||
struct tah_regs __iomem *base;
|
||||
|
||||
/* Only one EMAC whacks us at a time */
|
||||
struct mutex lock;
|
||||
|
||||
/* number of EMACs using this TAH */
|
||||
int users;
|
||||
|
||||
/* OF device instance */
|
||||
struct of_device *ofdev;
|
||||
};
|
||||
|
||||
|
||||
/* TAH engine */
|
||||
#define TAH_MR_CVR 0x80000000
|
||||
#define TAH_MR_SR 0x40000000
|
||||
#define TAH_MR_ST_256 0x01000000
|
||||
#define TAH_MR_ST_512 0x02000000
|
||||
#define TAH_MR_ST_768 0x03000000
|
||||
#define TAH_MR_ST_1024 0x04000000
|
||||
#define TAH_MR_ST_1280 0x05000000
|
||||
#define TAH_MR_ST_1536 0x06000000
|
||||
#define TAH_MR_TFS_16KB 0x00000000
|
||||
#define TAH_MR_TFS_2KB 0x00200000
|
||||
#define TAH_MR_TFS_4KB 0x00400000
|
||||
#define TAH_MR_TFS_6KB 0x00600000
|
||||
#define TAH_MR_TFS_8KB 0x00800000
|
||||
#define TAH_MR_TFS_10KB 0x00a00000
|
||||
#define TAH_MR_DTFP 0x00100000
|
||||
#define TAH_MR_DIG 0x00080000
|
||||
|
||||
#ifdef CONFIG_IBM_NEW_EMAC_TAH
|
||||
|
||||
extern int tah_init(void);
|
||||
extern void tah_exit(void);
|
||||
extern int tah_attach(struct of_device *ofdev, int channel);
|
||||
extern void tah_detach(struct of_device *ofdev, int channel);
|
||||
extern void tah_reset(struct of_device *ofdev);
|
||||
extern int tah_get_regs_len(struct of_device *ofdev);
|
||||
extern void *tah_dump_regs(struct of_device *ofdev, void *buf);
|
||||
|
||||
#else
|
||||
|
||||
# define tah_init() 0
|
||||
# define tah_exit() do { } while(0)
|
||||
# define tah_attach(x,y) (-ENXIO)
|
||||
# define tah_detach(x,y) do { } while(0)
|
||||
# define tah_reset(x) do { } while(0)
|
||||
# define tah_get_regs_len(x) 0
|
||||
# define tah_dump_regs(x,buf) (buf)
|
||||
|
||||
#endif /* !CONFIG_IBM_NEW_EMAC_TAH */
|
||||
|
||||
#endif /* __IBM_NEWEMAC_TAH_H */
|
322
drivers/net/ibm_newemac/zmii.c
Normal file
322
drivers/net/ibm_newemac/zmii.c
Normal file
@ -0,0 +1,322 @@
|
||||
/*
|
||||
* drivers/net/ibm_newemac/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/kernel.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "emac.h"
|
||||
#include "core.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;
|
||||
}
|
||||
}
|
||||
|
||||
int __devinit zmii_attach(struct of_device *ofdev, int input, int *mode)
|
||||
{
|
||||
struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
|
||||
struct zmii_regs *p = dev->base;
|
||||
|
||||
ZMII_DBG(dev, "init(%d, %d)" NL, input, *mode);
|
||||
|
||||
if (!zmii_valid_mode(*mode))
|
||||
/* Probably an EMAC connected to RGMII,
|
||||
* but it still may need ZMII for MDIO so
|
||||
* we don't fail here.
|
||||
*/
|
||||
return 0;
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
|
||||
/* 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(dev, "autodetecting mode, FER = 0x%08x" NL, 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 "%s: bridge in %s mode\n",
|
||||
ofdev->node->full_name, zmii_mode_name(dev->mode));
|
||||
} else {
|
||||
/* All inputs must use the same mode */
|
||||
if (*mode != PHY_MODE_NA && *mode != dev->mode) {
|
||||
printk(KERN_ERR
|
||||
"%s: invalid mode %d specified for input %d\n",
|
||||
ofdev->node->full_name, *mode, input);
|
||||
mutex_unlock(&dev->lock);
|
||||
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));
|
||||
++dev->users;
|
||||
|
||||
mutex_unlock(&dev->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void zmii_get_mdio(struct of_device *ofdev, int input)
|
||||
{
|
||||
struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
|
||||
u32 fer;
|
||||
|
||||
ZMII_DBG2(dev, "get_mdio(%d)" NL, input);
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
|
||||
fer = in_be32(&dev->base->fer) & ~ZMII_FER_MDI_ALL;
|
||||
out_be32(&dev->base->fer, fer | ZMII_FER_MDI(input));
|
||||
}
|
||||
|
||||
void zmii_put_mdio(struct of_device *ofdev, int input)
|
||||
{
|
||||
struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
|
||||
|
||||
ZMII_DBG2(dev, "put_mdio(%d)" NL, input);
|
||||
mutex_unlock(&dev->lock);
|
||||
}
|
||||
|
||||
|
||||
void zmii_set_speed(struct of_device *ofdev, int input, int speed)
|
||||
{
|
||||
struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
|
||||
u32 ssr;
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
|
||||
ssr = in_be32(&dev->base->ssr);
|
||||
|
||||
ZMII_DBG(dev, "speed(%d, %d)" NL, input, speed);
|
||||
|
||||
if (speed == SPEED_100)
|
||||
ssr |= ZMII_SSR_SP(input);
|
||||
else
|
||||
ssr &= ~ZMII_SSR_SP(input);
|
||||
|
||||
out_be32(&dev->base->ssr, ssr);
|
||||
|
||||
mutex_unlock(&dev->lock);
|
||||
}
|
||||
|
||||
void __devexit zmii_detach(struct of_device *ofdev, int input)
|
||||
{
|
||||
struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
|
||||
|
||||
BUG_ON(!dev || dev->users == 0);
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
|
||||
ZMII_DBG(dev, "detach(%d)" NL, input);
|
||||
|
||||
/* Disable this input */
|
||||
out_be32(&dev->base->fer,
|
||||
in_be32(&dev->base->fer) & ~zmii_mode_mask(dev->mode, input));
|
||||
|
||||
--dev->users;
|
||||
|
||||
mutex_unlock(&dev->lock);
|
||||
}
|
||||
|
||||
int zmii_get_regs_len(struct of_device *ofdev)
|
||||
{
|
||||
return sizeof(struct emac_ethtool_regs_subhdr) +
|
||||
sizeof(struct zmii_regs);
|
||||
}
|
||||
|
||||
void *zmii_dump_regs(struct of_device *ofdev, void *buf)
|
||||
{
|
||||
struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
|
||||
struct emac_ethtool_regs_subhdr *hdr = buf;
|
||||
struct zmii_regs *regs = (struct zmii_regs *)(hdr + 1);
|
||||
|
||||
hdr->version = 0;
|
||||
hdr->index = 0; /* for now, are there chips with more than one
|
||||
* zmii ? if yes, then we'll add a cell_index
|
||||
* like we do for emac
|
||||
*/
|
||||
memcpy_fromio(regs, dev->base, sizeof(struct zmii_regs));
|
||||
return regs + 1;
|
||||
}
|
||||
|
||||
static int __devinit zmii_probe(struct of_device *ofdev,
|
||||
const struct of_device_id *match)
|
||||
{
|
||||
struct device_node *np = ofdev->node;
|
||||
struct zmii_instance *dev;
|
||||
struct resource regs;
|
||||
int rc;
|
||||
|
||||
rc = -ENOMEM;
|
||||
dev = kzalloc(sizeof(struct zmii_instance), GFP_KERNEL);
|
||||
if (dev == NULL) {
|
||||
printk(KERN_ERR "%s: could not allocate ZMII device!\n",
|
||||
np->full_name);
|
||||
goto err_gone;
|
||||
}
|
||||
|
||||
mutex_init(&dev->lock);
|
||||
dev->ofdev = ofdev;
|
||||
dev->mode = PHY_MODE_NA;
|
||||
|
||||
rc = -ENXIO;
|
||||
if (of_address_to_resource(np, 0, ®s)) {
|
||||
printk(KERN_ERR "%s: Can't get registers address\n",
|
||||
np->full_name);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
rc = -ENOMEM;
|
||||
dev->base = (struct zmii_regs *)ioremap(regs.start,
|
||||
sizeof(struct zmii_regs));
|
||||
if (dev->base == NULL) {
|
||||
printk(KERN_ERR "%s: Can't map device registers!\n",
|
||||
np->full_name);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
/* We may need FER value for autodetection later */
|
||||
dev->fer_save = in_be32(&dev->base->fer);
|
||||
|
||||
/* Disable all inputs by default */
|
||||
out_be32(&dev->base->fer, 0);
|
||||
|
||||
printk(KERN_INFO
|
||||
"ZMII %s initialized\n", ofdev->node->full_name);
|
||||
wmb();
|
||||
dev_set_drvdata(&ofdev->dev, dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
kfree(dev);
|
||||
err_gone:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __devexit zmii_remove(struct of_device *ofdev)
|
||||
{
|
||||
struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev);
|
||||
|
||||
dev_set_drvdata(&ofdev->dev, NULL);
|
||||
|
||||
WARN_ON(dev->users != 0);
|
||||
|
||||
iounmap(dev->base);
|
||||
kfree(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id zmii_match[] =
|
||||
{
|
||||
{
|
||||
.compatible = "ibm,zmii",
|
||||
},
|
||||
/* For backward compat with old DT */
|
||||
{
|
||||
.type = "emac-zmii",
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static struct of_platform_driver zmii_driver = {
|
||||
.name = "emac-zmii",
|
||||
.match_table = zmii_match,
|
||||
|
||||
.probe = zmii_probe,
|
||||
.remove = zmii_remove,
|
||||
};
|
||||
|
||||
int __init zmii_init(void)
|
||||
{
|
||||
return of_register_platform_driver(&zmii_driver);
|
||||
}
|
||||
|
||||
void zmii_exit(void)
|
||||
{
|
||||
of_unregister_platform_driver(&zmii_driver);
|
||||
}
|
73
drivers/net/ibm_newemac/zmii.h
Normal file
73
drivers/net/ibm_newemac/zmii.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* drivers/net/ibm_newemac/zmii.h
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#ifndef __IBM_NEWEMAC_ZMII_H
|
||||
#define __IBM_NEWEMAC_ZMII_H
|
||||
|
||||
/* ZMII bridge registers */
|
||||
struct zmii_regs {
|
||||
u32 fer; /* Function enable reg */
|
||||
u32 ssr; /* Speed select reg */
|
||||
u32 smiirs; /* SMII status reg */
|
||||
};
|
||||
|
||||
/* ZMII device */
|
||||
struct zmii_instance {
|
||||
struct zmii_regs __iomem *base;
|
||||
|
||||
/* Only one EMAC whacks us at a time */
|
||||
struct mutex lock;
|
||||
|
||||
/* subset of PHY_MODE_XXXX */
|
||||
int mode;
|
||||
|
||||
/* number of EMACs using this ZMII bridge */
|
||||
int users;
|
||||
|
||||
/* FER value left by firmware */
|
||||
u32 fer_save;
|
||||
|
||||
/* OF device instance */
|
||||
struct of_device *ofdev;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_IBM_NEW_EMAC_ZMII
|
||||
|
||||
extern int zmii_init(void);
|
||||
extern void zmii_exit(void);
|
||||
extern int zmii_attach(struct of_device *ofdev, int input, int *mode);
|
||||
extern void zmii_detach(struct of_device *ofdev, int input);
|
||||
extern void zmii_get_mdio(struct of_device *ofdev, int input);
|
||||
extern void zmii_put_mdio(struct of_device *ofdev, int input);
|
||||
extern void zmii_set_speed(struct of_device *ofdev, int input, int speed);
|
||||
extern int zmii_get_regs_len(struct of_device *ocpdev);
|
||||
extern void *zmii_dump_regs(struct of_device *ofdev, void *buf);
|
||||
|
||||
#else
|
||||
# define zmii_init() 0
|
||||
# define zmii_exit() do { } while(0)
|
||||
# define zmii_attach(x,y,z) (-ENXIO)
|
||||
# define zmii_detach(x,y) do { } while(0)
|
||||
# define zmii_get_mdio(x,y) do { } while(0)
|
||||
# define zmii_put_mdio(x,y) do { } while(0)
|
||||
# define zmii_set_speed(x,y,z) do { } while(0)
|
||||
# define zmii_get_regs_len(x) 0
|
||||
# define zmii_dump_regs(x,buf) (buf)
|
||||
#endif /* !CONFIG_IBM_NEW_EMAC_ZMII */
|
||||
|
||||
#endif /* __IBM_NEWEMAC_ZMII_H */
|
Loading…
Reference in New Issue
Block a user