Commit 1d3bb996 authored by David Gibson's avatar David Gibson Committed by David S. Miller

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: default avatarDavid Gibson <david@gibson.dropbear.id.au>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent 03233b90
...@@ -1824,6 +1824,162 @@ platforms are moved over to use the flattened-device-tree model. ...@@ -1824,6 +1824,162 @@ platforms are moved over to use the flattened-device-tree model.
fsl,has-rstcr; 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. More devices will be defined as this spec matures.
VII - Specifying interrupt information for devices VII - Specifying interrupt information for devices
......
...@@ -38,8 +38,7 @@ config 440EP ...@@ -38,8 +38,7 @@ config 440EP
config 440GP config 440GP
bool bool
# Disabled until the new EMAC Driver is merged. select IBM_NEW_EMAC_ZMII
# select IBM_NEW_EMAC_ZMII
config 440GX config 440GX
bool bool
......
...@@ -10,6 +10,10 @@ config PPC_CELL_NATIVE ...@@ -10,6 +10,10 @@ config PPC_CELL_NATIVE
select PPC_INDIRECT_IO select PPC_INDIRECT_IO
select PPC_NATIVE select PPC_NATIVE
select MPIC 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 default n
config PPC_IBM_CELL_BLADE config PPC_IBM_CELL_BLADE
......
...@@ -1247,75 +1247,8 @@ config IBMVETH ...@@ -1247,75 +1247,8 @@ config IBMVETH
<file:Documentation/networking/net-modules.txt>. The module will <file:Documentation/networking/net-modules.txt>. The module will
be called ibmveth. be called ibmveth.
config IBM_EMAC source "drivers/net/ibm_emac/Kconfig"
tristate "PowerPC 4xx on-chip Ethernet support" source "drivers/net/ibm_newemac/Kconfig"
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
config NET_PCI config NET_PCI
bool "EISA, VLB, PCI and on board controllers" bool "EISA, VLB, PCI and on board controllers"
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
obj-$(CONFIG_E1000) += e1000/ obj-$(CONFIG_E1000) += e1000/
obj-$(CONFIG_E1000E) += e1000e/ obj-$(CONFIG_E1000E) += e1000e/
obj-$(CONFIG_IBM_EMAC) += ibm_emac/ obj-$(CONFIG_IBM_EMAC) += ibm_emac/
obj-$(CONFIG_IBM_NEW_EMAC) += ibm_newemac/
obj-$(CONFIG_IXGBE) += ixgbe/ obj-$(CONFIG_IXGBE) += ixgbe/
obj-$(CONFIG_IXGB) += ixgb/ obj-$(CONFIG_IXGB) += ixgb/
obj-$(CONFIG_IP1000) += ipg.o obj-$(CONFIG_IP1000) += ipg.o
......
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
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
#
# 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
/*
* drivers/net/ibm_newemac/core.c
*
* 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
* Matt Porter <mporter@kernel.crashing.org>
* (c) 2003 Benjamin Herrenschmidt <benh@kernel.crashing.org>
* Armin Kuster <akuster@mvista.com>
* Johnnie Peters <jpeters@mvista.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/crc32.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
#include <linux/bitops.h>
#include <linux/workqueue.h>
#include <asm/processor.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/uaccess.h>
#include "core.h"
/*
* Lack of dma_unmap_???? calls is intentional.
*
* API-correct usage requires additional support state information to be
* maintained for every RX and TX buffer descriptor (BD). Unfortunately, due to
* EMAC design (e.g. TX buffer passed from network stack can be split into
* several BDs, dma_map_single/dma_map_page can be used to map particular BD),
* maintaining such information will add additional overhead.
* Current DMA API implementation for 4xx processors only ensures cache coherency
* and dma_unmap_???? routines are empty and are likely to stay this way.
* I decided to omit dma_unmap_??? calls because I don't want to add additional
* complexity just for the sake of following some abstract API, when it doesn't
* add any real benefit to the driver. I understand that this decision maybe
* controversial, but I really tried to make code API-correct and efficient
* at the same time and didn't come up with code I liked :(. --ebs
*/
#define DRV_NAME "emac"
#define DRV_VERSION "3.54"
#define DRV_DESC "PPC 4xx OCP EMAC driver"
MODULE_DESCRIPTION(DRV_DESC);
MODULE_AUTHOR
("Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>");
MODULE_LICENSE("GPL");
/*
* PPC64 doesn't (yet) have a cacheable_memcpy
*/
#ifdef CONFIG_PPC64
#define cacheable_memcpy(d,s,n) memcpy((d),(s),(n))
#endif
/* minimum number of free TX descriptors required to wake up TX process */
#define EMAC_TX_WAKEUP_THRESH (NUM_TX_BUFF / 4)
/* If packet size is less than this number, we allocate small skb and copy packet
* contents into it instead of just sending original big skb up
*/
#define EMAC_RX_COPY_THRESH CONFIG_IBM_NEW_EMAC_RX_COPY_THRESHOLD
/* Since multiple EMACs share MDIO lines in various ways, we need
* to avoid re-using the same PHY ID in cases where the arch didn't
* setup precise phy_map entries
*
* XXX This is something that needs to be reworked as we can have multiple
* EMAC "sets" (multiple ASICs containing several EMACs) though we can
* probably require in that case to have explicit PHY IDs in the device-tree
*/
static u32 busy_phy_map;
static DEFINE_MUTEX(emac_phy_map_lock);
/* This is the wait queue used to wait on any event related to probe, that
* is discovery of MALs, other EMACs, ZMII/RGMIIs, etc...
*/
static DECLARE_WAIT_QUEUE_HEAD(emac_probe_wait);
/* Having stable interface names is a doomed idea. However, it would be nice
* if we didn't have completely random interface names at boot too :-) It's
* just a matter of making everybody's life easier. Since we are doing
* threaded probing, it's a bit harder though. The base idea here is that
* we make up a list of all emacs in the device-tree before we register the
* driver. Every emac will then wait for the previous one in the list to
* initialize before itself. We should also keep that list ordered by
* cell_index.
* That list is only 4 entries long, meaning that additional EMACs don't
* get ordering guarantees unless EMAC_BOOT_LIST_SIZE is increased.
*/
#define EMAC_BOOT_LIST_SIZE 4
static struct device_node *emac_boot_list[EMAC_BOOT_LIST_SIZE];
/* How long should I wait for dependent devices ? */
#define EMAC_PROBE_DEP_TIMEOUT (HZ * 5)
/* I don't want to litter system log with timeout errors
* when we have brain-damaged PHY.
*/
static inline void emac_report_timeout_error(struct emac_instance *dev,
const char *error)
{
if (net_ratelimit())
printk(KERN_ERR "%s: %s\n", dev->ndev->name, error);
}
/* PHY polling intervals */
#define PHY_POLL_LINK_ON HZ
#define PHY_POLL_LINK_OFF (HZ / 5)
/* Graceful stop timeouts in us.
* We should allow up to 1 frame time (full-duplex, ignoring collisions)
*/
#define STOP_TIMEOUT_10 1230
#define STOP_TIMEOUT_100 124
#define STOP_TIMEOUT_1000 13
#define STOP_TIMEOUT_1000_JUMBO 73
/* Please, keep in sync with struct ibm_emac_stats/ibm_emac_error_stats */
static const char emac_stats_keys[EMAC_ETHTOOL_STATS_COUNT][ETH_GSTRING_LEN] = {
"rx_packets", "rx_bytes", "tx_packets", "tx_bytes", "rx_packets_csum",
"tx_packets_csum", "tx_undo", "rx_dropped_stack", "rx_dropped_oom",
"rx_dropped_error", "rx_dropped_resize", "rx_dropped_mtu",
"rx_stopped", "rx_bd_errors", "rx_bd_overrun", "rx_bd_bad_packet",
"rx_bd_runt_packet", "rx_bd_short_event", "rx_bd_alignment_error",
"rx_bd_bad_fcs", "rx_bd_packet_too_long", "rx_bd_out_of_range",
"rx_bd_in_range", "rx_parity", "rx_fifo_overrun", "rx_overrun",
"rx_bad_packet", "rx_runt_packet", "rx_short_event",
"rx_alignment_error", "rx_bad_fcs", "rx_packet_too_long",
"rx_out_of_range", "rx_in_range", "tx_dropped", "tx_bd_errors",
"tx_bd_bad_fcs", "tx_bd_carrier_loss", "tx_bd_excessive_deferral",
"tx_bd_excessive_collisions", "tx_bd_late_collision",
"tx_bd_multple_collisions", "tx_bd_single_collision",
"tx_bd_underrun", "tx_bd_sqe", "tx_parity", "tx_underrun", "tx_sqe",
"tx_errors"
};
static irqreturn_t emac_irq(int irq, void *dev_instance);
static void emac_clean_tx_ring(struct emac_instance *dev);
static void __emac_set_multicast_list(struct emac_instance *dev);
static inline int emac_phy_supports_gige(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 int emac_phy_gpcs(int phy_mode)
{
return phy_mode == PHY_MODE_TBI ||
phy_mode == PHY_MODE_RTBI;
}
static inline void emac_tx_enable(struct emac_instance *dev)
{
struct emac_regs __iomem *p = dev->emacp;
u32 r;
DBG(dev, "tx_enable" NL);
r = in_be32(&p->mr0);
if (!(r & EMAC_MR0_TXE))
out_be32(&p->mr0, r | EMAC_MR0_TXE);
}
static void emac_tx_disable(struct emac_instance *dev)
{
struct emac_regs __iomem *p = dev->emacp;
u32 r;
DBG(dev, "tx_disable" NL);
r = in_be32(&p->mr0);
if (r & EMAC_MR0_TXE) {
int n = dev->stop_timeout;
out_be32(&p->mr0, r & ~EMAC_MR0_TXE);
while (!(in_be32(&p->mr0) & EMAC_MR0_TXI) && n) {
udelay(1);
--n;
}
if (unlikely(!n))
emac_report_timeout_error(dev, "TX disable timeout");
}
}
static void emac_rx_enable(struct emac_instance *dev)
{
struct emac_regs __iomem *p = dev->emacp;
u32 r;
if (unlikely(test_bit(MAL_COMMAC_RX_STOPPED, &dev->commac.flags)))
goto out;
DBG(dev, "rx_enable" NL);
r = in_be32(&p->mr0);
if (!(r & EMAC_MR0_RXE)) {
if (unlikely(!(r & EMAC_MR0_RXI))) {
/* Wait if previous async disable is still in progress */
int n = dev->stop_timeout;
while (!(r = in_be32(&p->mr0) & EMAC_MR0_RXI) && n) {
udelay(1);
--n;
}
if (unlikely(!n))
emac_report_timeout_error(dev,
"RX disable timeout");
}
out_be32(&p->mr0, r | EMAC_MR0_RXE);
}
out:
;
}
static void emac_rx_disable(struct emac_instance *dev)
{
struct emac_regs __iomem *p = dev->emacp;
u32 r;
DBG(dev, "rx_disable" NL);
r = in_be32(&p->mr0);
if (r & EMAC_MR0_RXE) {
int n = dev->stop_timeout;
out_be32(&p->mr0, r & ~EMAC_MR0_RXE);
while (!(in_be32(&p->mr0) & EMAC_MR0_RXI) && n) {
udelay(1);
--n;
}
if (unlikely(!n))
emac_report_timeout_error(dev, "RX disable timeout");
}
}
static inline void emac_netif_stop(struct emac_instance *dev)
{
netif_tx_lock_bh(dev->ndev);
dev->no_mcast = 1;
netif_tx_unlock_bh(dev->ndev);
dev->ndev->trans_start = jiffies; /* prevent tx timeout */
mal_poll_disable(dev->mal, &dev->commac);
netif_tx_disable(dev->ndev);
}
static inline void emac_netif_start(struct emac_instance *dev)
{
netif_tx_lock_bh(dev->ndev);
dev->no_mcast = 0;
if (dev->mcast_pending && netif_running(dev->ndev))
__emac_set_multicast_list(dev);
netif_tx_unlock_bh(dev->ndev);
netif_wake_queue(dev->ndev);
/* NOTE: unconditional netif_wake_queue is only appropriate
* so long as all callers are assured to have free tx slots
* (taken from tg3... though the case where that is wrong is
* not terribly harmful)
*/
mal_poll_enable(dev->mal, &dev->commac);
}
static inline void emac_rx_disable_async(struct emac_instance *dev)
{
struct emac_regs __iomem *p = dev->emacp;
u32 r;
DBG(dev, "rx_disable_async" NL);
r = in_be32(&p->mr0);
if (r & EMAC_MR0_RXE)
out_be32(&p->mr0, r & ~EMAC_MR0_RXE);
}
static int emac_reset(struct emac_instance *dev)
{
struct emac_regs __iomem *p = dev->emacp;
int n = 20;
DBG(dev, "reset" NL);
if (!dev->reset_failed) {
/* 40x erratum suggests stopping RX channel before reset,
* we stop TX as well
*/
emac_rx_disable(dev);
emac_tx_disable(dev);
}
out_be32(&p->mr0, EMAC_MR0_SRST);
while ((in_be32(&p->mr0) & EMAC_MR0_SRST) && n)
--n;
if (n) {
dev->reset_failed = 0;
return 0;
} else {
emac_report_timeout_error(dev, "reset timeout");
dev->reset_failed = 1;
return -ETIMEDOUT;
}
}
static void emac_hash_mc(struct emac_instance *dev)
{
struct emac_regs __iomem *p = dev->emacp;
u16 gaht[4] = { 0 };
struct dev_mc_list *dmi;
DBG(dev, "hash_mc %d" NL, dev->ndev->mc_count);
for (dmi = dev->ndev->mc_list; dmi; dmi = dmi->next) {
int bit;
DBG2(dev, "mc %02x:%02x:%02x:%02x:%02x:%02x" NL,
dmi->dmi_addr[0], dmi->dmi_addr[1], dmi->dmi_addr[2],
dmi->dmi_addr[3], dmi->dmi_addr[4], dmi->dmi_addr[5]);
bit = 63 - (ether_crc(ETH_ALEN, dmi->dmi_addr) >> 26);
gaht[bit >> 4] |= 0x8000 >> (bit & 0x0f);
}
out_be32(&p->gaht1, gaht[0]);
out_be32(&p->gaht2, gaht[1]);
out_be32(&p->gaht3, gaht[2]);
out_be32(&p->gaht4, gaht[3]);
}
static inline u32 emac_iff2rmr(struct net_device *ndev)
{
struct emac_instance *dev = netdev_priv(ndev);
u32 r;
r = EMAC_RMR_SP | EMAC_RMR_SFCS | EMAC_RMR_IAE | EMAC_RMR_BAE;
if (emac_has_feature(dev, EMAC_FTR_EMAC4))
r |= EMAC4_RMR_BASE;
else
r |= EMAC_RMR_BASE;
if (ndev->flags & IFF_PROMISC)
r |= EMAC_RMR_PME;
else if (ndev->flags & IFF_ALLMULTI || ndev->mc_count > 32)
r |= EMAC_RMR_PMME;
else if (ndev->mc_count > 0)
r |= EMAC_RMR_MAE;
return r;
}
static u32 __emac_calc_base_mr1(struct emac_instance *dev, int tx_size, int rx_size)
{
u32 ret = EMAC_MR1_VLE | EMAC_MR1_IST | EMAC_MR1_TR0_MULT;
DBG2(dev, "__emac_calc_base_mr1" NL);
switch(tx_size) {
case 2048:
ret |= EMAC_MR1_TFS_2K;
break;
default:
printk(KERN_WARNING "%s: Unknown Rx FIFO size %d\n",
dev->ndev->name, tx_size);
}
switch(rx_size) {
case 16384:
ret |= EMAC_MR1_RFS_16K;
break;
case 4096:
ret |= EMAC_MR1_RFS_4K;
break;
default:
printk(KERN_WARNING "%s: Unknown Rx FIFO size %d\n",
dev->ndev->name, rx_size);
}
return ret;
}
static u32 __emac4_calc_base_mr1(struct emac_instance *dev, int tx_size, int rx_size)
{
u32 ret = EMAC_MR1_VLE | EMAC_MR1_IST | EMAC4_MR1_TR |
EMAC4_MR1_OBCI(dev->opb_bus_freq);
DBG2(dev, "__emac4_calc_base_mr1" NL);
switch(tx_size) {
case 4096:
ret |= EMAC4_MR1_TFS_4K;
break;
case 2048:
ret |= EMAC4_MR1_TFS_2K;
break;
default:
printk(KERN_WARNING "%s: Unknown Rx FIFO size %d\n",
dev->ndev->name, tx_size);
}
switch(rx_size) {
case 16384:
ret |= EMAC4_MR1_RFS_16K;
break;
case 4096:
ret |= EMAC4_MR1_RFS_4K;
break;
case 2048:
ret |= EMAC4_MR1_RFS_2K;
break;
default:
printk(KERN_WARNING "%s: Unknown Rx FIFO size %d\n",
dev->ndev->name, rx_size);
}
return ret;
}
static u32 emac_calc_base_mr1(struct emac_instance *dev, int tx_size, int rx_size)
{
return emac_has_feature(dev, EMAC_FTR_EMAC4) ?
__emac4_calc_base_mr1(dev, tx_size, rx_size) :
__emac_calc_base_mr1(dev, tx_size, rx_size);
}
static inline u32 emac_calc_trtr(struct emac_instance *dev, unsigned int size)
{
if (emac_has_feature(dev, EMAC_FTR_EMAC4))
return ((size >> 6) - 1) << EMAC_TRTR_SHIFT_EMAC4;
else
return ((size >> 6) - 1) << EMAC_TRTR_SHIFT;
}
static inline u32 emac_calc_rwmr(struct emac_instance *dev,
unsigned int low, unsigned int high)
{
if (emac_has_feature(dev, EMAC_FTR_EMAC4))
return (low << 22) | ( (high & 0x3ff) << 6);
else
return (low << 23) | ( (high & 0x1ff) << 7);
}
static int emac_configure(struct emac_instance *dev)
{
struct emac_regs __iomem *p = dev->emacp;
struct net_device *ndev = dev->ndev;
int tx_size, rx_size;
u32 r, mr1 = 0;
DBG(dev, "configure" NL);
if (emac_reset(dev) < 0)
return -ETIMEDOUT;
if (emac_has_feature(dev, EMAC_FTR_HAS_TAH))
tah_reset(dev->tah_dev);
DBG(dev, " duplex = %d, pause = %d, asym_pause = %d\n",
dev->phy.duplex, dev->phy.pause, dev->phy.asym_pause);
/* Default fifo sizes */
tx_size = dev->tx_fifo_size;
rx_size = dev->rx_fifo_size;
/* Check for full duplex */
if (dev->phy.duplex == DUPLEX_FULL)
mr1 |= EMAC_MR1_FDE | EMAC_MR1_MWSW_001;
/* Adjust fifo sizes, mr1 and timeouts based on link speed */
dev->stop_timeout = STOP_TIMEOUT_10;
switch (dev->phy.speed) {
case SPEED_1000:
if (emac_phy_gpcs(dev->phy.mode)) {
mr1 |= EMAC_MR1_MF_1000GPCS |
EMAC_MR1_MF_IPPA(dev->phy.address);
/* Put some arbitrary OUI, Manuf & Rev IDs so we can
* identify this GPCS PHY later.
*/
out_be32(&p->ipcr, 0xdeadbeef);
} else
mr1 |= EMAC_MR1_MF_1000;
/* Extended fifo sizes */
tx_size = dev->tx_fifo_size_gige;
rx_size = dev->rx_fifo_size_gige;
if (dev->ndev->mtu > ETH_DATA_LEN) {
mr1 |= EMAC_MR1_JPSM;
dev->stop_timeout = STOP_TIMEOUT_1000_JUMBO;
} else
dev->stop_timeout = STOP_TIMEOUT_1000;
break;
case SPEED_100:
mr1 |= EMAC_MR1_MF_100;
dev->stop_timeout = STOP_TIMEOUT_100;
break;
default: /* make gcc happy */
break;
}
if (emac_has_feature(dev, EMAC_FTR_HAS_RGMII))
rgmii_set_speed(dev->rgmii_dev, dev->rgmii_port,
dev->phy.speed);
if (emac_has_feature(dev, EMAC_FTR_HAS_ZMII))
zmii_set_speed(dev->zmii_dev, dev->zmii_port, dev->phy.speed);
/* on 40x erratum forces us to NOT use integrated flow control,
* let's hope it works on 44x ;)
*/
if (!emac_has_feature(dev, EMAC_FTR_NO_FLOW_CONTROL_40x) &&
dev->phy.duplex == DUPLEX_FULL) {
if (dev->phy.pause)
mr1 |= EMAC_MR1_EIFC | EMAC_MR1_APP;
else if (dev->phy.asym_pause)
mr1 |= EMAC_MR1_APP;
}
/* Add base settings & fifo sizes & program MR1 */
mr1 |= emac_calc_base_mr1(dev, tx_size, rx_size);
out_be32(&p->mr1, mr1);
/* Set individual MAC address */
out_be32(&p->iahr, (ndev->dev_addr[0] << 8) | ndev->dev_addr[1]);
out_be32(&p->ialr, (ndev->dev_addr[2] << 24) |
(ndev->dev_addr[3] << 16) | (ndev->dev_addr[4] << 8) |
ndev->dev_addr[5]);
/* VLAN Tag Protocol ID */
out_be32(&p->vtpid, 0x8100);
/* Receive mode register */
r = emac_iff2rmr(ndev);
if (r & EMAC_RMR_MAE)
emac_hash_mc(dev);
out_be32(&p->rmr, r);
/* FIFOs thresholds */
if (emac_has_feature(dev, EMAC_FTR_EMAC4))
r = EMAC4_TMR1((dev->mal_burst_size / dev->fifo_entry_size) + 1,
tx_size / 2 / dev->fifo_entry_size);
else
r = EMAC_TMR1((dev->mal_burst_size / dev->fifo_entry_size) + 1,
tx_size / 2 / dev->fifo_entry_size);
out_be32(&p->tmr1, r);
out_be32(&p->trtr, emac_calc_trtr(dev, tx_size / 2));
/* PAUSE frame is sent when RX FIFO reaches its high-water mark,
there should be still enough space in FIFO to allow the our link
partner time to process this frame and also time to send PAUSE
frame itself.
Here is the worst case scenario for the RX FIFO "headroom"
(from "The Switch Book") (100Mbps, without preamble, inter-frame gap):
1) One maximum-length frame on TX 1522 bytes
2) One PAUSE frame time 64 bytes
3) PAUSE frame decode time allowance 64 bytes
4) One maximum-length frame on RX 1522 bytes
5) Round-trip propagation delay of the link (100Mb) 15 bytes
----------
3187 bytes
I chose to set high-water mark to RX_FIFO_SIZE / 4 (1024 bytes)
low-water mark to RX_FIFO_SIZE / 8 (512 bytes)
*/
r = emac_calc_rwmr(dev, rx_size / 8 / dev->fifo_entry_size,
rx_size / 4 / dev->fifo_entry_size);
out_be32(&p->rwmr, r);
/* Set PAUSE timer to the maximum */
out_be32(&p->ptr, 0xffff);
/* IRQ sources */
r = EMAC_ISR_OVR | EMAC_ISR_BP | EMAC_ISR_SE |
EMAC_ISR_ALE | EMAC_ISR_BFCS | EMAC_ISR_PTLE | EMAC_ISR_ORE |
EMAC_ISR_IRE | EMAC_ISR_TE;
if (emac_has_feature(dev, EMAC_FTR_EMAC4))
r |= EMAC4_ISR_TXPE | EMAC4_ISR_RXPE /* | EMAC4_ISR_TXUE |
EMAC4_ISR_RXOE | */;
out_be32(&p->iser, r);
/* We need to take GPCS PHY out of isolate mode after EMAC reset */
if (emac_phy_gpcs(dev->phy.mode))
emac_mii_reset_phy(&dev->phy);
return 0;
}
static void emac_reinitialize(struct emac_instance *dev)
{
DBG(dev, "reinitialize" NL);
emac_netif_stop(dev);
if (!emac_configure(dev)) {
emac_tx_enable(dev);
emac_rx_enable(dev);
}
emac_netif_start(dev);
}
static void emac_full_tx_reset(struct emac_instance *dev)
{
DBG(dev, "full_tx_reset" NL);
emac_tx_disable(dev);
mal_disable_tx_channel(dev->mal, dev->mal_tx_chan);
emac_clean_tx_ring(dev);
dev->tx_cnt = dev->tx_slot = dev->ack_slot = 0;
emac_configure(dev);
mal_enable_tx_channel(dev->mal, dev->mal_tx_chan);
emac_tx_enable(dev);
emac_rx_enable(dev);
}
static void emac_reset_work(struct work_struct *work)
{
struct emac_instance *dev = container_of(work, struct emac_instance, reset_work);
DBG(dev, "reset_work" NL);
mutex_lock(&dev->link_lock);
emac_netif_stop(dev);
emac_full_tx_reset(dev);
emac_netif_start(dev);
mutex_unlock(&dev->link_lock);
}
static void emac_tx_timeout(struct net_device *ndev)
{
struct emac_instance *dev = netdev_priv(ndev);
DBG(dev, "tx_timeout" NL);
schedule_work(&dev->reset_work);
}
static inline int emac_phy_done(struct emac_instance *dev, u32 stacr)
{
int done = !!(stacr & EMAC_STACR_OC);
if (emac_has_feature(dev, EMAC_FTR_STACR_OC_INVERT))
done = !done;
return done;
};
static int __emac_mdio_read(struct emac_instance *dev, u8 id, u8 reg)
{
struct emac_regs __iomem *p = dev->emacp;
u32 r = 0;
int n, err = -ETIMEDOUT;
mutex_lock(&dev->mdio_lock);
DBG2(dev, "mdio_read(%02x,%02x)" NL, id, reg);
/* Enable proper MDIO port */
if (emac_has_feature(dev, EMAC_FTR_HAS_ZMII))
zmii_get_mdio(dev->zmii_dev, dev->zmii_port);
if (emac_has_feature(dev, EMAC_FTR_HAS_RGMII))
rgmii_get_mdio(dev->rgmii_dev, dev->rgmii_port);
/* Wait for management interface to become idle */
n = 10;
while (!emac_phy_done(dev, in_be32(&p->stacr))) {
udelay(1);
if (!--n) {
DBG2(dev, " -> timeout wait idle\n");
goto bail;
}
}
/* Issue read command */
if (emac_has_feature(dev, EMAC_FTR_EMAC4))
r = EMAC4_STACR_BASE(dev->opb_bus_freq);
else
r = EMAC_STACR_BASE(dev->opb_bus_freq);
if (emac_has_feature(dev, EMAC_FTR_STACR_OC_INVERT))
r |= EMAC_STACR_OC;
if (emac_has_feature(dev, EMAC_FTR_HAS_AXON_STACR))
r |= EMACX_STACR_STAC_READ;
else
r |= EMAC_STACR_STAC_READ;
r |= (reg & EMAC_STACR_PRA_MASK)
| ((id & EMAC_STACR_PCDA_MASK) << EMAC_STACR_PCDA_SHIFT);
out_be32(&p->stacr, r);
/* Wait for read to complete */
n = 100;
while (!emac_phy_done(dev, (r = in_be32(&p->stacr)))) {
udelay(1);
if (!--n) {
DBG2(dev, " -> timeout wait complete\n");
goto bail;
}
}
if (unlikely(r & EMAC_STACR_PHYE)) {
DBG(dev, "mdio_read(%02x, %02x) failed" NL, id, reg);
err = -EREMOTEIO;
goto bail;
}
r = ((r >> EMAC_STACR_PHYD_SHIFT) & EMAC_STACR_PHYD_MASK);
DBG2(dev, "mdio_read -> %04x" NL, r);
err = 0;
bail:
if (emac_has_feature(dev, EMAC_FTR_HAS_RGMII))
rgmii_put_mdio(dev->rgmii_dev, dev->rgmii_port);
if (emac_has_feature(dev, EMAC_FTR_HAS_ZMII))
zmii_put_mdio(dev->zmii_dev, dev->zmii_port);
mutex_unlock(&dev->mdio_lock);
return err == 0 ? r : err;
}
static void __emac_mdio_write(struct emac_instance *dev, u8 id, u8 reg,
u16 val)
{
struct emac_regs __iomem *p = dev->emacp;
u32 r = 0;
int n, err = -ETIMEDOUT;
mutex_lock(&dev->mdio_lock);
DBG2(dev, "mdio_write(%02x,%02x,%04x)" NL, id, reg, val);
/* Enable proper MDIO port */
if (emac_has_feature(dev, EMAC_FTR_HAS_ZMII))
zmii_get_mdio(dev->zmii_dev, dev->zmii_port);
if (emac_has_feature(dev, EMAC_FTR_HAS_RGMII))
rgmii_get_mdio(dev->rgmii_dev, dev->rgmii_port);
/* Wait for management interface to be idle */
n = 10;
while (!emac_phy_done(dev, in_be32(&p->stacr))) {
udelay(1);
if (!--n) {
DBG2(dev, " -> timeout wait idle\n");
goto bail;
}
}
/* Issue write command */
if (emac_has_feature(dev, EMAC_FTR_EMAC4))
r = EMAC4_STACR_BASE(dev->opb_bus_freq);
else
r = EMAC_STACR_BASE(dev->opb_bus_freq);
if (emac_has_feature(dev, EMAC_FTR_STACR_OC_INVERT))
r |= EMAC_STACR_OC;
if (emac_has_feature(dev, EMAC_FTR_HAS_AXON_STACR))
r |= EMACX_STACR_STAC_WRITE;
else
r |= EMAC_STACR_STAC_WRITE;
r |= (reg & EMAC_STACR_PRA_MASK) |
((id & EMAC_STACR_PCDA_MASK) << EMAC_STACR_PCDA_SHIFT) |
(val << EMAC_STACR_PHYD_SHIFT);
out_be32(&p->stacr, r);
/* Wait for write to complete */
n = 100;
while (!emac_phy_done(dev, in_be32(&p->stacr))) {
udelay(1);
if (!--n) {
DBG2(dev, " -> timeout wait complete\n");
goto bail;
}
}
err = 0;
bail:
if (emac_has_feature(dev, EMAC_FTR_HAS_RGMII))
rgmii_put_mdio(dev->rgmii_dev, dev->rgmii_port);
if (emac_has_feature(dev, EMAC_FTR_HAS_ZMII))
zmii_put_mdio(dev->zmii_dev, dev->zmii_port);
mutex_unlock(&dev->mdio_lock);
}
static int emac_mdio_read(struct net_device *ndev, int id, int reg)
{
struct emac_instance *dev = netdev_priv(ndev);
int res;
res = __emac_mdio_read(dev->mdio_instance ? dev->mdio_instance : dev,
(u8) id, (u8) reg);
return res;
}
static void emac_mdio_write(struct net_device *ndev, int id, int reg, int val)
{
struct emac_instance *dev = netdev_priv(ndev);
__emac_mdio_write(dev->mdio_instance ? dev->mdio_instance : dev,
(u8) id, (u8) reg, (u16) val);
}
/* Tx lock BH */
static void __emac_set_multicast_list(struct emac_instance *dev)
{
struct emac_regs __iomem *p = dev->emacp;
u32 rmr = emac_iff2rmr(dev->ndev);
DBG(dev, "__multicast %08x" NL, rmr);
/* I decided to relax register access rules here to avoid
* full EMAC reset.
*
* There is a real problem with EMAC4 core if we use MWSW_001 bit
* in MR1 register and do a full EMAC reset.
* One TX BD status update is delayed and, after EMAC reset, it
* never happens, resulting in TX hung (it'll be recovered by TX
* timeout handler eventually, but this is just gross).
* So we either have to do full TX reset or try to cheat here :)
*
* The only required change is to RX mode register, so I *think* all
* we need is just to stop RX channel. This seems to work on all
* tested SoCs. --ebs
*
* If we need the full reset, we might just trigger the workqueue
* and do it async... a bit nasty but should work --BenH
*/
dev->mcast_pending = 0;
emac_rx_disable(dev);
if (rmr & EMAC_RMR_MAE)
emac_hash_mc(dev);
out_be32(&p->rmr, rmr);
emac_rx_enable(dev);
}
/* Tx lock BH */
static void emac_set_multicast_list(struct net_device *ndev)
{
struct emac_instance *dev = netdev_priv(ndev);
DBG(dev, "multicast" NL);
BUG_ON(!netif_running(dev->ndev));
if (dev->no_mcast) {
dev->mcast_pending = 1;
return;
}
__emac_set_multicast_list(dev);
}
static int emac_resize_rx_ring(struct emac_instance *dev, int new_mtu)
{
int rx_sync_size = emac_rx_sync_size(new_mtu);
int rx_skb_size = emac_rx_skb_size(new_mtu);
int i, ret = 0;
mutex_lock(&dev->link_lock);
emac_netif_stop(dev);
emac_rx_disable(dev);
mal_disable_rx_channel(dev->mal, dev->mal_rx_chan);
if (dev->rx_sg_skb) {
++dev->estats.rx_dropped_resize;
dev_kfree_skb(dev->rx_sg_skb);
dev->rx_sg_skb = NULL;
}
/* Make a first pass over RX ring and mark BDs ready, dropping
* non-processed packets on the way. We need this as a separate pass
* to simplify error recovery in the case of allocation failure later.
*/
for (i = 0; i < NUM_RX_BUFF; ++i) {
if (dev->rx_desc[i].ctrl & MAL_RX_CTRL_FIRST)
++dev->estats.rx_dropped_resize;
dev->rx_desc[i].data_len = 0;
dev->rx_desc[i].ctrl = MAL_RX_CTRL_EMPTY |
(i == (NUM_RX_BUFF - 1) ? MAL_RX_CTRL_WRAP : 0);
}
/* Reallocate RX ring only if bigger skb buffers are required */
if (rx_skb_size <= dev->rx_skb_size)
goto skip;
/* Second pass, allocate new skbs */
for (i = 0; i < NUM_RX_BUFF; ++i) {
struct sk_buff *skb = alloc_skb(rx_skb_size, GFP_ATOMIC);
if (!skb) {
ret = -ENOMEM;
goto oom;
}
BUG_ON(!dev->rx_skb[i]);
dev_kfree_skb(dev->rx_skb[i]);
skb_reserve(skb, EMAC_RX_SKB_HEADROOM + 2);
dev->rx_desc[i].data_ptr =
dma_map_single(&dev->ofdev->dev, skb->data - 2, rx_sync_size,
DMA_FROM_DEVICE) + 2;
dev->rx_skb[i] = skb;
}
skip:
/* Check if we need to change "Jumbo" bit in MR1 */
if ((new_mtu > ETH_DATA_LEN) ^ (dev->ndev->mtu > ETH_DATA_LEN)) {
/* This is to prevent starting RX channel in emac_rx_enable() */
set_bit(MAL_COMMAC_RX_STOPPED, &dev->commac.flags);
dev->ndev->mtu = new_mtu;
emac_full_tx_reset(dev);
}
mal_set_rcbs(dev->mal, dev->mal_rx_chan, emac_rx_size(new_mtu));
oom:
/* Restart RX */
clear_bit(MAL_COMMAC_RX_STOPPED, &dev->commac.flags);
dev->rx_slot = 0;
mal_enable_rx_channel(dev->mal, dev->mal_rx_chan);
emac_rx_enable(dev);
emac_netif_start(dev);
mutex_unlock(&dev->link_lock);
return ret;
}
/* Process ctx, rtnl_lock semaphore */
static int emac_change_mtu(struct net_device *ndev, int new_mtu)
{
struct emac_instance *dev = netdev_priv(ndev);
int ret = 0;
if (new_mtu < EMAC_MIN_MTU || new_mtu > dev->max_mtu)
return -EINVAL;
DBG(dev, "change_mtu(%d)" NL, new_mtu);
if (netif_running(ndev)) {
/* Check if we really need to reinitalize RX ring */
if (emac_rx_skb_size(ndev->mtu) != emac_rx_skb_size(new_mtu))
ret = emac_resize_rx_ring(dev, new_mtu);
}
if (!ret) {
ndev->mtu = new_mtu;
dev->rx_skb_size = emac_rx_skb_size(new_mtu);
dev->rx_sync_size = emac_rx_sync_size(new_mtu);
}
return ret;
}
static void emac_clean_tx_ring(struct emac_instance *dev)
{
int i;
for (i = 0; i < NUM_TX_BUFF; ++i) {
if (dev->tx_skb[i]) {
dev_kfree_skb(dev->tx_skb[i]);
dev->tx_skb[i] = NULL;
if (dev->tx_desc[i].ctrl & MAL_TX_CTRL_READY)
++dev->estats.tx_dropped;
}
dev->tx_desc[i].ctrl = 0;
dev->tx_desc[i].data_ptr = 0;
}
}
static void emac_clean_rx_ring(struct emac_instance *dev)
{
int i;
for (i = 0; i < NUM_RX_BUFF; ++i)
if (dev->rx_skb[i]) {
dev->rx_desc[i].ctrl = 0;
dev_kfree_skb(dev->rx_skb[i]);
dev->rx_skb[i] = NULL;
dev->rx_desc[i].data_ptr = 0;
}
if (dev->rx_sg_skb) {
dev_kfree_skb(dev->rx_sg_skb);
dev->rx_sg_skb = NULL;
}
}
static inline int emac_alloc_rx_skb(struct emac_instance *dev, int slot,
gfp_t flags)
{
struct sk_buff *skb = alloc_skb(dev->rx_skb_size, flags);
if (unlikely(!skb))
return -ENOMEM;
dev->rx_skb[slot] = skb;
dev->rx_desc[slot].data_len = 0;
skb_reserve(skb, EMAC_RX_SKB_HEADROOM + 2);
dev->rx_desc[slot].data_ptr =
dma_map_single(&dev->ofdev->dev, skb->data - 2, dev->rx_sync_size,
DMA_FROM_DEVICE) + 2;
wmb();
dev->rx_desc[slot].ctrl = MAL_RX_CTRL_EMPTY |
(slot == (NUM_RX_BUFF - 1) ? MAL_RX_CTRL_WRAP : 0);
return 0;
}
static void emac_print_link_status(struct emac_instance *dev)
{
if (netif_carrier_ok(dev->ndev))
printk(KERN_INFO "%s: link is up, %d %s%s\n",
dev->ndev->name, dev->phy.speed,
dev->phy.duplex == DUPLEX_FULL ? "FDX" : "HDX",
dev->phy.pause ? ", pause enabled" :
dev->phy.asym_pause ? ", asymmetric pause enabled" : "");
else
printk(KERN_INFO "%s: link is down\n", dev->ndev->name);
}
/* Process ctx, rtnl_lock semaphore */
static int emac_open(struct net_device *ndev)
{
struct emac_instance *dev = netdev_priv(ndev);
int err, i;
DBG(dev, "open" NL);
/* Setup error IRQ handler */
err = request_irq(dev->emac_irq, emac_irq, 0, "EMAC", dev);
if (err) {
printk(KERN_ERR "%s: failed to request IRQ %d\n",
ndev->name, dev->emac_irq);
return err;
}
/* Allocate RX ring */
for (i = 0; i < NUM_RX_BUFF; ++i)
if (emac_alloc_rx_skb(dev, i, GFP_KERNEL)) {
printk(KERN_ERR "%s: failed to allocate RX ring\n",
ndev->name);
goto oom;
}
dev->tx_cnt = dev->tx_slot = dev->ack_slot = dev->rx_slot = 0;
clear_bit(MAL_COMMAC_RX_STOPPED, &dev->commac.flags);
dev->rx_sg_skb = NULL;
mutex_lock(&dev->link_lock);
/* XXX Start PHY polling now. Shouldn't wr do like sungem instead and
* always poll the PHY even when the iface is down ? That would allow
* things like laptop-net to work. --BenH
*/
if (dev->phy.address >= 0) {
int link_poll_interval;
if (dev->phy.def->ops->poll_link(&dev->phy)) {
dev->phy.def->ops->read_link(&dev->phy);
netif_carrier_on(dev->ndev);
link_poll_interval = PHY_POLL_LINK_ON;
} else {
netif_carrier_off(dev->ndev);
link_poll_interval = PHY_POLL_LINK_OFF;
}
dev->link_polling = 1;
wmb();
schedule_delayed_work(&dev->link_work, link_poll_interval);
emac_print_link_status(dev);
} else
netif_carrier_on(dev->ndev);
emac_configure(dev);
mal_poll_add(dev->mal, &dev->commac);
mal_enable_tx_channel(dev->mal, dev->mal_tx_chan);
mal_set_rcbs(dev->mal, dev->mal_rx_chan, emac_rx_size(ndev->mtu));
mal_enable_rx_channel(dev->mal, dev->mal_rx_chan);
emac_tx_enable(dev);
emac_rx_enable(dev);
emac_netif_start(dev);
mutex_unlock(&dev->link_lock);
return 0;
oom:
emac_clean_rx_ring(dev);
free_irq(dev->emac_irq, dev);
return -ENOMEM;
}
/* BHs disabled */
#if 0
static int emac_link_differs(struct emac_instance *dev)
{
u32 r = in_be32(&dev->emacp->mr1);
int duplex = r & EMAC_MR1_FDE ? DUPLEX_FULL : DUPLEX_HALF;
int speed, pause, asym_pause;
if (r & EMAC_MR1_MF_1000)
speed = SPEED_1000;
else if (r & EMAC_MR1_MF_100)
speed = SPEED_100;
else
speed = SPEED_10;
switch (r & (EMAC_MR1_EIFC | EMAC_MR1_APP)) {
case (EMAC_MR1_EIFC | EMAC_MR1_APP):
pause = 1;
asym_pause = 0;
break;
case EMAC_MR1_APP:
pause = 0;
asym_pause = 1;
break;
default:
pause = asym_pause = 0;
}
return speed != dev->phy.speed || duplex != dev->phy.duplex ||
pause != dev->phy.pause || asym_pause != dev->phy.asym_pause;
}
#endif
static void emac_link_timer(struct work_struct *work)
{
struct emac_instance *dev =
container_of((struct delayed_work *)work,
struct emac_instance, link_work);
int link_poll_interval;
mutex_lock(&dev->link_lock);
DBG2(dev, "link timer" NL);
if (dev->phy.def->ops->poll_link(&dev->phy)) {
if (!netif_carrier_ok(dev->ndev)) {
/* Get new link parameters */
dev->phy.def->ops->read_link(&dev->phy);
netif_carrier_on(dev->ndev);
emac_netif_stop(dev);
emac_full_tx_reset(dev);
emac_netif_start(dev);
emac_print_link_status(dev);
}
link_poll_interval = PHY_POLL_LINK_ON;
} else {
if (netif_carrier_ok(dev->ndev)) {
emac_reinitialize(dev);
netif_carrier_off(dev->ndev);
netif_tx_disable(dev->ndev);
emac_print_link_status(dev);
}
link_poll_interval = PHY_POLL_LINK_OFF;
}
schedule_delayed_work(&dev->link_work, link_poll_interval);
mutex_unlock(&dev->link_lock);
}
static void emac_force_link_update(struct emac_instance *dev)
{
netif_carrier_off(dev->ndev);
if (dev->link_polling) {
cancel_rearming_delayed_work(&dev->link_work);
if (dev->link_polling)
schedule_delayed_work(&dev->link_work, PHY_POLL_LINK_OFF);
}
}
/* Process ctx, rtnl_lock semaphore */
static int emac_close(struct net_device *ndev)
{
struct emac_instance *dev = netdev_priv(ndev);
DBG(dev, "close" NL);
if (dev->phy.address >= 0)
cancel_rearming_delayed_work(&dev->link_work);
emac_netif_stop(dev);
flush_scheduled_work();
emac_rx_disable(dev);
emac_tx_disable(dev);
mal_disable_rx_channel(dev->mal, dev->mal_rx_chan);
mal_disable_tx_channel(dev->mal, dev->mal_tx_chan);
mal_poll_del(dev->mal, &dev->commac);
emac_clean_tx_ring(dev);
emac_clean_rx_ring(dev);
free_irq(dev->emac_irq, dev);
return 0;
}
static inline u16 emac_tx_csum(struct emac_instance *dev,
struct sk_buff *skb)
{
if (emac_has_feature(dev, EMAC_FTR_HAS_TAH &&
skb->ip_summed == CHECKSUM_PARTIAL)) {
++dev->stats.tx_packets_csum;
return EMAC_TX_CTRL_TAH_CSUM;
}
return 0;
}
static inline int emac_xmit_finish(struct emac_instance *dev, int len)
{
struct emac_regs __iomem *p = dev->emacp;
struct net_device *ndev = dev->ndev;
/* Send the packet out. If the if makes a significant perf
* difference, then we can store the TMR0 value in "dev"
* instead
*/
if (emac_has_feature(dev, EMAC_FTR_EMAC4))
out_be32(&p->tmr0, EMAC_TMR0_XMIT);
else
out_be32(&p->tmr0, EMAC4_TMR0_XMIT);
if (unlikely(++dev->tx_cnt == NUM_TX_BUFF)) {
netif_stop_queue(ndev);
DBG2(dev, "stopped TX queue" NL);
}
ndev->trans_start = jiffies;
++dev->stats.tx_packets;
dev->stats.tx_bytes += len;
return 0;
}
/* Tx lock BH */
static int emac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct emac_instance *dev = netdev_priv(ndev);
unsigned int len = skb->len;
int slot;
u16 ctrl = EMAC_TX_CTRL_GFCS | EMAC_TX_CTRL_GP | MAL_TX_CTRL_READY |
MAL_TX_CTRL_LAST | emac_tx_csum(dev, skb);
slot = dev->tx_slot++;
if (dev->tx_slot == NUM_TX_BUFF) {
dev->tx_slot = 0;
ctrl |= MAL_TX_CTRL_WRAP;
}
DBG2(dev, "xmit(%u) %d" NL, len, slot);
dev->tx_skb[slot] = skb;
dev->tx_desc[slot].data_ptr = dma_map_single(&dev->ofdev->dev,
skb->data, len,
DMA_TO_DEVICE);
dev->tx_desc[slot].data_len = (u16) len;
wmb();
dev->tx_desc[slot].ctrl = ctrl;
return emac_xmit_finish(dev, len);
}
#ifdef CONFIG_IBM_NEW_EMAC_TAH
static inline int emac_xmit_split(struct emac_instance *dev, int slot,
u32 pd, int len, int last, u16 base_ctrl)
{
while (1) {
u16 ctrl = base_ctrl;
int chunk = min(len, MAL_MAX_TX_SIZE);
len -= chunk;
slot = (slot + 1) % NUM_TX_BUFF;
if (last && !len)
ctrl |= MAL_TX_CTRL_LAST;
if (slot == NUM_TX_BUFF - 1)
ctrl |= MAL_TX_CTRL_WRAP;
dev->tx_skb[slot] = NULL;
dev->tx_desc[slot].data_ptr = pd;
dev->tx_desc[slot].data_len = (u16) chunk;
dev->tx_desc[slot].ctrl = ctrl;
++dev->tx_cnt;
if (!len)
break;
pd += chunk;
}
return slot;
}
/* Tx lock BH disabled (SG version for TAH equipped EMACs) */
static int emac_start_xmit_sg(struct sk_buff *skb, struct net_device *ndev)
{
struct emac_instance *dev = netdev_priv(ndev);
int nr_frags = skb_shinfo(skb)->nr_frags;
int len = skb->len, chunk;
int slot, i;
u16 ctrl;
u32 pd;
/* This is common "fast" path */
if (likely(!nr_frags && len <= MAL_MAX_TX_SIZE))
return emac_start_xmit(skb, ndev);
len -= skb->data_len;
/* Note, this is only an *estimation*, we can still run out of empty
* slots because of the additional fragmentation into
* MAL_MAX_TX_SIZE-sized chunks
*/
if (unlikely(dev->tx_cnt + nr_frags + mal_tx_chunks(len) > NUM_TX_BUFF))
goto stop_queue;
ctrl = EMAC_TX_CTRL_GFCS | EMAC_TX_CTRL_GP | MAL_TX_CTRL_READY |
emac_tx_csum(dev, skb);
slot = dev->tx_slot;
/* skb data */
dev->tx_skb[slot] = NULL;
chunk = min(len, MAL_MAX_TX_SIZE);
dev->tx_desc[slot].data_ptr = pd =
dma_map_single(&dev->ofdev->dev, skb->data, len, DMA_TO_DEVICE);
dev->tx_desc[slot].data_len = (u16) chunk;
len -= chunk;
if (unlikely(len))
slot = emac_xmit_split(dev, slot, pd + chunk, len, !nr_frags,
ctrl);
/* skb fragments */
for (i = 0; i < nr_frags; ++i) {
struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i];
len = frag->size;
if (unlikely(dev->tx_cnt + mal_tx_chunks(len) >= NUM_TX_BUFF))
goto undo_frame;
pd = dma_map_page(&dev->ofdev->dev, frag->page, frag->page_offset, len,
DMA_TO_DEVICE);
slot = emac_xmit_split(dev, slot, pd, len, i == nr_frags - 1,
ctrl);
}
DBG2(dev, "xmit_sg(%u) %d - %d" NL, skb->len, dev->tx_slot, slot);
/* Attach skb to the last slot so we don't release it too early */
dev->tx_skb[slot] = skb;
/* Send the packet out */
if (dev->tx_slot == NUM_TX_BUFF - 1)
ctrl |= MAL_TX_CTRL_WRAP;
wmb();
dev->tx_desc[dev->tx_slot].ctrl = ctrl;
dev->tx_slot = (slot + 1) % NUM_TX_BUFF;
return emac_xmit_finish(dev, skb->len);
undo_frame:
/* Well, too bad. Our previous estimation was overly optimistic.
* Undo everything.
*/
while (slot != dev->tx_slot) {
dev->tx_desc[slot].ctrl = 0;
--dev->tx_cnt;
if (--slot < 0)
slot = NUM_TX_BUFF - 1;
}
++dev->estats.tx_undo;
stop_queue:
netif_stop_queue(ndev);
DBG2(dev, "stopped TX queue" NL);
return 1;
}
#else
# define emac_start_xmit_sg emac_start_xmit
#endif /* !defined(CONFIG_IBM_NEW_EMAC_TAH) */
/* Tx lock BHs */
static void emac_parse_tx_error(struct emac_instance *dev, u16 ctrl)
{
struct emac_error_stats *st = &dev->estats;
DBG(dev, "BD TX error %04x" NL, ctrl);
++st->tx_bd_errors;
if (ctrl & EMAC_TX_ST_BFCS)
++st->tx_bd_bad_fcs;
if (ctrl & EMAC_TX_ST_LCS)
++st->tx_bd_carrier_loss;
if (ctrl & EMAC_TX_ST_ED)
++st->tx_bd_excessive_deferral;
if (ctrl & EMAC_TX_ST_EC)
++st->tx_bd_excessive_collisions;
if (ctrl & EMAC_TX_ST_LC)
++st->tx_bd_late_collision;
if (ctrl & EMAC_TX_ST_MC)
++st->tx_bd_multple_collisions;
if (ctrl & EMAC_TX_ST_SC)
++st->tx_bd_single_collision;
if (ctrl & EMAC_TX_ST_UR)
++st->tx_bd_underrun;
if (ctrl & EMAC_TX_ST_SQE)
++st->tx_bd_sqe;
}
static void emac_poll_tx(void *param)
{
struct emac_instance *dev = param;
u32 bad_mask;
DBG2(dev, "poll_tx, %d %d" NL, dev->tx_cnt, dev->ack_slot);
if (emac_has_feature(dev, EMAC_FTR_HAS_TAH))
bad_mask = EMAC_IS_BAD_TX_TAH;
else
bad_mask = EMAC_IS_BAD_TX;
netif_tx_lock_bh(dev->ndev);
if (dev->tx_cnt) {
u16 ctrl;
int slot = dev->ack_slot, n = 0;
again:
ctrl = dev->tx_desc[slot].ctrl;
if (!(ctrl & MAL_TX_CTRL_READY)) {
struct sk_buff *skb = dev->tx_skb[slot];
++n;
if (skb) {
dev_kfree_skb(skb);
dev->tx_skb[slot] = NULL;
}
slot = (slot + 1) % NUM_TX_BUFF;
if (unlikely(ctrl & bad_mask))
emac_parse_tx_error(dev, ctrl);
if (--dev->tx_cnt)
goto again;
}
if (n) {
dev->ack_slot = slot;
if (netif_queue_stopped(dev->ndev) &&
dev->tx_cnt < EMAC_TX_WAKEUP_THRESH)
netif_wake_queue(dev->ndev);
DBG2(dev, "tx %d pkts" NL, n);
}
}
netif_tx_unlock_bh(dev->ndev);
}
static inline void emac_recycle_rx_skb(struct emac_instance *dev, int slot,
int len)
{
struct sk_buff *skb = dev->rx_skb[slot];
DBG2(dev, "recycle %d %d" NL, slot, len);
if (len)
dma_map_single(&dev->ofdev->dev, skb->data - 2,
EMAC_DMA_ALIGN(len + 2), DMA_FROM_DEVICE);
dev->rx_desc[slot].data_len = 0;
wmb();
dev->rx_desc[slot].ctrl = MAL_RX_CTRL_EMPTY |
(slot == (NUM_RX_BUFF - 1) ? MAL_RX_CTRL_WRAP : 0);
}
static void emac_parse_rx_error(struct emac_instance *dev, u16 ctrl)
{
struct emac_error_stats *st = &dev->estats;
DBG(dev, "BD RX error %04x" NL, ctrl);
++st->rx_bd_errors;
if (ctrl & EMAC_RX_ST_OE)
++st->rx_bd_overrun;
if (ctrl & EMAC_RX_ST_BP)
++st->rx_bd_bad_packet;
if (ctrl & EMAC_RX_ST_RP)
++st->rx_bd_runt_packet;
if (ctrl & EMAC_RX_ST_SE)
++st->rx_bd_short_event;
if (ctrl & EMAC_RX_ST_AE)
++st->rx_bd_alignment_error;
if (ctrl & EMAC_RX_ST_BFCS)
++st->rx_bd_bad_fcs;
if (ctrl & EMAC_RX_ST_PTL)
++st->rx_bd_packet_too_long;
if (ctrl & EMAC_RX_ST_ORE)
++st->rx_bd_out_of_range;
if (ctrl & EMAC_RX_ST_IRE)
++st->rx_bd_in_range;
}
static inline void emac_rx_csum(struct emac_instance *dev,
struct sk_buff *skb, u16 ctrl)
{
#ifdef CONFIG_IBM_NEW_EMAC_TAH
if (!ctrl && dev->tah_dev) {
skb->ip_summed = CHECKSUM_UNNECESSARY;
++dev->stats.rx_packets_csum;
}
#endif
}
static inline int emac_rx_sg_append(struct emac_instance *dev, int slot)
{
if (likely(dev->rx_sg_skb != NULL)) {
int len = dev->rx_desc[slot].data_len;
int tot_len = dev->rx_sg_skb->len + len;
if (unlikely(tot_len + 2 > dev->rx_skb_size)) {
++dev->estats.rx_dropped_mtu;
dev_kfree_skb(dev->rx_sg_skb);
dev->rx_sg_skb = NULL;
} else {
cacheable_memcpy(dev->rx_sg_skb->tail,
dev->rx_skb[slot]->data, len);
skb_put(dev->rx_sg_skb, len);
emac_recycle_rx_skb(dev, slot, len);
return 0;
}
}
emac_recycle_rx_skb(dev, slot, 0);
return -1;
}
/* NAPI poll context */
static int emac_poll_rx(void *param, int budget)
{
struct emac_instance *dev = param;
int slot = dev->rx_slot, received = 0;
DBG2(dev, "poll_rx(%d)" NL, budget);
again:
while (budget > 0) {
int len;
struct sk_buff *skb;
u16 ctrl = dev->rx_desc[slot].ctrl;
if (ctrl & MAL_RX_CTRL_EMPTY)
break;
skb = dev->rx_skb[slot];
mb();
len = dev->rx_desc[slot].data_len;
if (unlikely(!MAL_IS_SINGLE_RX(ctrl)))
goto sg;
ctrl &= EMAC_BAD_RX_MASK;
if (unlikely(ctrl && ctrl != EMAC_RX_TAH_BAD_CSUM)) {
emac_parse_rx_error(dev, ctrl);
++dev->estats.rx_dropped_error;
emac_recycle_rx_skb(dev, slot, 0);
len = 0;
goto next;
}
if (len && len < EMAC_RX_COPY_THRESH) {
struct sk_buff *copy_skb =
alloc_skb(len + EMAC_RX_SKB_HEADROOM + 2, GFP_ATOMIC);
if (unlikely(!copy_skb))
goto oom;
skb_reserve(copy_skb, EMAC_RX_SKB_HEADROOM + 2);
cacheable_memcpy(copy_skb->data - 2, skb->data - 2,
len + 2);
emac_recycle_rx_skb(dev, slot, len);
skb = copy_skb;
} else if (unlikely(emac_alloc_rx_skb(dev, slot, GFP_ATOMIC)))
goto oom;
skb_put(skb, len);
push_packet:
skb->dev = dev->ndev;
skb->protocol = eth_type_trans(skb, dev->ndev);
emac_rx_csum(dev, skb, ctrl);
if (unlikely(netif_receive_skb(skb) == NET_RX_DROP))
++dev->estats.rx_dropped_stack;
next:
++dev->stats.rx_packets;
skip:
dev->stats.rx_bytes += len;
slot = (slot + 1) % NUM_RX_BUFF;
--budget;
++received;
continue;
sg:
if (ctrl & MAL_RX_CTRL_FIRST) {
BUG_ON(dev->rx_sg_skb);
if (unlikely(emac_alloc_rx_skb(dev, slot, GFP_ATOMIC))) {
DBG(dev, "rx OOM %d" NL, slot);
++dev->estats.rx_dropped_oom;
emac_recycle_rx_skb(dev, slot, 0);
} else {
dev->rx_sg_skb = skb;
skb_put(skb, len);
}
} else if (!emac_rx_sg_append(dev, slot) &&
(ctrl & MAL_RX_CTRL_LAST)) {
skb = dev->rx_sg_skb;
dev->rx_sg_skb = NULL;
ctrl &= EMAC_BAD_RX_MASK;
if (unlikely(ctrl && ctrl != EMAC_RX_TAH_BAD_CSUM)) {
emac_parse_rx_error(dev, ctrl);
++dev->estats.rx_dropped_error;
dev_kfree_skb(skb);
len = 0;
} else
goto push_packet;
}
goto skip;
oom:
DBG(dev, "rx OOM %d" NL, slot);
/* Drop the packet and recycle skb */
++dev->estats.rx_dropped_oom;
emac_recycle_rx_skb(dev, slot, 0);
goto next;
}
if (received) {
DBG2(dev, "rx %d BDs" NL, received);
dev->rx_slot = slot;
}
if (unlikely(budget && test_bit(MAL_COMMAC_RX_STOPPED, &dev->commac.flags))) {
mb();
if (!(dev->rx_desc[slot].ctrl & MAL_RX_CTRL_EMPTY)) {
DBG2(dev, "rx restart" NL);
received = 0;
goto again;
}
if (dev->rx_sg_skb) {
DBG2(dev, "dropping partial rx packet" NL);
++dev->estats.rx_dropped_error;
dev_kfree_skb(dev->rx_sg_skb);
dev->rx_sg_skb = NULL;
}
clear_bit(MAL_COMMAC_RX_STOPPED, &dev->commac.flags);
mal_enable_rx_channel(dev->mal, dev->mal_rx_chan);
emac_rx_enable(dev);
dev->rx_slot = 0;
}
return received;
}
/* NAPI poll context */
static int emac_peek_rx(void *param)
{
struct emac_instance *dev = param;
return !(dev->rx_desc[dev->rx_slot].ctrl & MAL_RX_CTRL_EMPTY);
}
/* NAPI poll context */
static int emac_peek_rx_sg(void *param)
{
struct emac_instance *dev = param;
int slot = dev->rx_slot;
while (1) {
u16 ctrl = dev->rx_desc[slot].ctrl;
if (ctrl & MAL_RX_CTRL_EMPTY)
return 0;
else if (ctrl & MAL_RX_CTRL_LAST)
return 1;
slot = (slot + 1) % NUM_RX_BUFF;
/* I'm just being paranoid here :) */
if (unlikely(slot == dev->rx_slot))
return 0;
}
}
/* Hard IRQ */
static void emac_rxde(void *param)
{
struct emac_instance *dev = param;
++dev->estats.rx_stopped;
emac_rx_disable_async(dev);
}
/* Hard IRQ */
static irqreturn_t emac_irq(int irq, void *dev_instance)
{
struct emac_instance *dev = dev_instance;
struct emac_regs __iomem *p = dev->emacp;
struct emac_error_stats *st = &dev->estats;
u32 isr;
spin_lock(&dev->lock);
isr = in_be32(&p->isr);
out_be32(&p->isr, isr);
DBG(dev, "isr = %08x" NL, isr);
if (isr & EMAC4_ISR_TXPE)
++st->tx_parity;
if (isr & EMAC4_ISR_RXPE)
++st->rx_parity;
if (isr & EMAC4_ISR_TXUE)
++st->tx_underrun;
if (isr & EMAC4_ISR_RXOE)
++st->rx_fifo_overrun;
if (isr & EMAC_ISR_OVR)
++st->rx_overrun;
if (isr & EMAC_ISR_BP)
++st->rx_bad_packet;
if (isr & EMAC_ISR_RP)
++st->rx_runt_packet;
if (isr & EMAC_ISR_SE)
++st->rx_short_event;
if (isr & EMAC_ISR_ALE)
++st->rx_alignment_error;
if (isr & EMAC_ISR_BFCS)
++st->rx_bad_fcs;
if (isr & EMAC_ISR_PTLE)
++st->rx_packet_too_long;
if (isr & EMAC_ISR_ORE)
++st->rx_out_of_range;
if (isr & EMAC_ISR_IRE)
++st->rx_in_range;
if (isr & EMAC_ISR_SQE)
++st->tx_sqe;
if (isr & EMAC_ISR_TE)
++st->tx_errors;
spin_unlock(&dev->lock);
return IRQ_HANDLED;
}
static struct net_device_stats *emac_stats(struct net_device *ndev)
{
struct emac_instance *dev = netdev_priv(ndev);
struct emac_stats *st = &dev->stats;
struct emac_error_stats *est = &dev->estats;
struct net_device_stats *nst = &dev->nstats;
unsigned long flags;
DBG2(dev, "stats" NL);
/* Compute "legacy" statistics */
spin_lock_irqsave(&dev->lock, flags);
nst->rx_packets = (unsigned long)st->rx_packets;
nst->rx_bytes = (unsigned long)st->rx_bytes;
nst->tx_packets = (unsigned long)st->tx_packets;
nst->tx_bytes = (unsigned long)st->tx_bytes;
nst->rx_dropped = (unsigned long)(est->rx_dropped_oom +
est->rx_dropped_error +
est->rx_dropped_resize +
est->rx_dropped_mtu);
nst->tx_dropped = (unsigned long)est->tx_dropped;
nst->rx_errors = (unsigned long)est->rx_bd_errors;
nst->rx_fifo_errors = (unsigned long)(est->rx_bd_overrun +
est->rx_fifo_overrun +
est->rx_overrun);
nst->rx_frame_errors = (unsigned long)(est->rx_bd_alignment_error +
est->rx_alignment_error);
nst->rx_crc_errors = (unsigned long)(est->rx_bd_bad_fcs +
est->rx_bad_fcs);
nst->rx_length_errors = (unsigned long)(est->rx_bd_runt_packet +
est->rx_bd_short_event +
est->rx_bd_packet_too_long +
est->rx_bd_out_of_range +
est->rx_bd_in_range +
est->rx_runt_packet +
est->rx_short_event +
est->rx_packet_too_long +
est->rx_out_of_range +
est->rx_in_range);
nst->tx_errors = (unsigned long)(est->tx_bd_errors + est->tx_errors);
nst->tx_fifo_errors = (unsigned long)(est->tx_bd_underrun +
est->tx_underrun);
nst->tx_carrier_errors = (unsigned long)est->tx_bd_carrier_loss;
nst->collisions = (unsigned long)(est->tx_bd_excessive_deferral +
est->tx_bd_excessive_collisions +
est->tx_bd_late_collision +
est->tx_bd_multple_collisions);
spin_unlock_irqrestore(&dev->lock, flags);
return nst;
}
static struct mal_commac_ops emac_commac_ops = {
.poll_tx = &emac_poll_tx,
.poll_rx = &emac_poll_rx,
.peek_rx = &emac_peek_rx,
.rxde = &emac_rxde,
};
static struct mal_commac_ops emac_commac_sg_ops = {
.poll_tx = &emac_poll_tx,
.poll_rx = &emac_poll_rx,
.peek_rx = &emac_peek_rx_sg,
.rxde = &emac_rxde,
};
/* Ethtool support */
static int emac_ethtool_get_settings(struct net_device *ndev,
struct ethtool_cmd *cmd)
{
struct emac_instance *dev = netdev_priv(ndev);
cmd->supported = dev->phy.features;
cmd->port = PORT_MII;
cmd->phy_address = dev->phy.address;
cmd->transceiver =
dev->phy.address >= 0 ? XCVR_EXTERNAL : XCVR_INTERNAL;
mutex_lock(&dev->link_lock);
cmd->advertising = dev->phy.advertising;
cmd->autoneg = dev->phy.autoneg;
cmd->speed = dev->phy.speed;
cmd->duplex = dev->phy.duplex;
mutex_unlock(&dev->link_lock);
return 0;
}
static int emac_ethtool_set_settings(struct net_device *ndev,
struct ethtool_cmd *cmd)
{
struct emac_instance *dev = netdev_priv(ndev);
u32 f = dev->phy.features;
DBG(dev, "set_settings(%d, %d, %d, 0x%08x)" NL,
cmd->autoneg, cmd->speed, cmd->duplex, cmd->advertising);
/* Basic sanity checks */
if (dev->phy.address < 0)
return -EOPNOTSUPP;
if (cmd->autoneg != AUTONEG_ENABLE && cmd->autoneg != AUTONEG_DISABLE)
return -EINVAL;
if (cmd->autoneg == AUTONEG_ENABLE && cmd->advertising == 0)
return -EINVAL;
if (cmd->duplex != DUPLEX_HALF && cmd->duplex != DUPLEX_FULL)
return -EINVAL;
if (cmd->autoneg == AUTONEG_DISABLE) {
switch (cmd->speed) {
case SPEED_10:
if (cmd->duplex == DUPLEX_HALF
&& !(f & SUPPORTED_10baseT_Half))
return -EINVAL;
if (cmd->duplex == DUPLEX_FULL
&& !(f & SUPPORTED_10baseT_Full))
return -EINVAL;
break;
case SPEED_100:
if (cmd->duplex == DUPLEX_HALF
&& !(f & SUPPORTED_100baseT_Half))
return -EINVAL;
if (cmd->duplex == DUPLEX_FULL
&& !(f & SUPPORTED_100baseT_Full))
return -EINVAL;
break;
case SPEED_1000:
if (cmd->duplex == DUPLEX_HALF
&& !(f & SUPPORTED_1000baseT_Half))
return -EINVAL;
if (cmd->duplex == DUPLEX_FULL
&& !(f & SUPPORTED_1000baseT_Full))
return -EINVAL;
break;
default:
return -EINVAL;
}
mutex_lock(&dev->link_lock);
dev->phy.def->ops->setup_forced(&dev->phy, cmd->speed,
cmd->duplex);
mutex_unlock(&dev->link_lock);
} else {
if (!(f & SUPPORTED_Autoneg))
return -EINVAL;
mutex_lock(&dev->link_lock);
dev->phy.def->ops->setup_aneg(&dev->phy,
(cmd->advertising & f) |
(dev->phy.advertising &
(ADVERTISED_Pause |
ADVERTISED_Asym_Pause)));
mutex_unlock(&dev->link_lock);
}
emac_force_link_update(dev);
return 0;
}
static void emac_ethtool_get_ringparam(struct net_device *ndev,
struct ethtool_ringparam *rp)
{
rp->rx_max_pending = rp->rx_pending = NUM_RX_BUFF;
rp->tx_max_pending = rp->tx_pending = NUM_TX_BUFF;
}
static void emac_ethtool_get_pauseparam(struct net_device *ndev,
struct ethtool_pauseparam *pp)
{
struct emac_instance *dev = netdev_priv(ndev);
mutex_lock(&dev->link_lock);
if ((dev->phy.features & SUPPORTED_Autoneg) &&
(dev->phy.advertising & (ADVERTISED_Pause | ADVERTISED_Asym_Pause)))
pp->autoneg = 1;
if (dev->phy.duplex == DUPLEX_FULL) {
if (dev->phy.pause)
pp->rx_pause = pp->tx_pause = 1;
else if (dev->phy.asym_pause)
pp->tx_pause = 1;
}
mutex_unlock(&dev->link_lock);
}
static u32 emac_ethtool_get_rx_csum(struct net_device *ndev)
{
struct emac_instance *dev = netdev_priv(ndev);
return dev->tah_dev != 0;
}
static int emac_get_regs_len(struct emac_instance *dev)
{
if (emac_has_feature(dev, EMAC_FTR_EMAC4))
return sizeof(struct emac_ethtool_regs_subhdr) +
EMAC4_ETHTOOL_REGS_SIZE;
else
return sizeof(struct emac_ethtool_regs_subhdr) +
EMAC_ETHTOOL_REGS_SIZE;
}
static int emac_ethtool_get_regs_len(struct net_device *ndev)
{
struct emac_instance *dev = netdev_priv(ndev);
int size;
size = sizeof(struct emac_ethtool_regs_hdr) +
emac_get_regs_len(dev) + mal_get_regs_len(dev->mal);
if (emac_has_feature(dev, EMAC_FTR_HAS_ZMII))
size += zmii_get_regs_len(dev->zmii_dev);
if (emac_has_feature(dev, EMAC_FTR_HAS_RGMII))
size += rgmii_get_regs_len(dev->rgmii_dev);
if (emac_has_feature(dev, EMAC_FTR_HAS_TAH))
size += tah_get_regs_len(dev->tah_dev);
return size;
}
static void *emac_dump_regs(struct emac_instance *dev, void *buf)
{
struct emac_ethtool_regs_subhdr *hdr = buf;
hdr->index = dev->cell_index;
if (emac_has_feature(dev, EMAC_FTR_EMAC4)) {
hdr->version = EMAC4_ETHTOOL_REGS_VER;
memcpy_fromio(hdr + 1, dev->emacp, EMAC4_ETHTOOL_REGS_SIZE);
return ((void *)(hdr + 1) + EMAC4_ETHTOOL_REGS_SIZE);
} else {
hdr->version = EMAC_ETHTOOL_REGS_VER;
memcpy_fromio(hdr + 1, dev->emacp, EMAC_ETHTOOL_REGS_SIZE);
return ((void *)(hdr + 1) + EMAC_ETHTOOL_REGS_SIZE);
}
}
static void emac_ethtool_get_regs(struct net_device *ndev,
struct ethtool_regs *regs, void *buf)
{
struct emac_instance *dev = netdev_priv(ndev);
struct emac_ethtool_regs_hdr *hdr = buf;
hdr->components = 0;
buf = hdr + 1;
buf = mal_dump_regs(dev->mal, buf);
buf = emac_dump_regs(dev, buf);
if (emac_has_feature(dev, EMAC_FTR_HAS_ZMII)) {
hdr->components |= EMAC_ETHTOOL_REGS_ZMII;
buf = zmii_dump_regs(dev->zmii_dev, buf);
}
if (emac_has_feature(dev, EMAC_FTR_HAS_RGMII)) {
hdr->components |= EMAC_ETHTOOL_REGS_RGMII;
buf = rgmii_dump_regs(dev->rgmii_dev, buf);
}
if (emac_has_feature(dev, EMAC_FTR_HAS_TAH)) {
hdr->components |= EMAC_ETHTOOL_REGS_TAH;
buf = tah_dump_regs(dev->tah_dev, buf);
}
}
static int emac_ethtool_nway_reset(struct net_device *ndev)
{
struct emac_instance *dev = netdev_priv(ndev);
int res = 0;
DBG(dev, "nway_reset" NL);
if (dev->phy.address < 0)
return -EOPNOTSUPP;
mutex_lock(&dev->link_lock);
if (!dev->phy.autoneg) {
res = -EINVAL;
goto out;
}
dev->phy.def->ops->setup_aneg(&dev->phy, dev->phy.advertising);
out:
mutex_unlock(&dev->link_lock);
emac_force_link_update(dev);
return res;
}
static int emac_ethtool_get_stats_count(struct net_device *ndev)
{
return EMAC_ETHTOOL_STATS_COUNT;
}
static void emac_ethtool_get_strings(struct net_device *ndev, u32 stringset,
u8 * buf)
{
if (stringset == ETH_SS_STATS)
memcpy(buf, &emac_stats_keys, sizeof(emac_stats_keys));
}
static void emac_ethtool_get_ethtool_stats(struct net_device *ndev,
struct ethtool_stats *estats,
u64 * tmp_stats)
{
struct emac_instance *dev = netdev_priv(ndev);
memcpy(tmp_stats, &dev->stats, sizeof(dev->stats));
tmp_stats += sizeof(dev->stats) / sizeof(u64);
memcpy(tmp_stats, &dev->estats, sizeof(dev->estats));
}
static void emac_ethtool_get_drvinfo(struct net_device *ndev,
struct ethtool_drvinfo *info)
{
struct emac_instance *dev = netdev_priv(ndev);
strcpy(info->driver, "ibm_emac");
strcpy(info->version, DRV_VERSION);
info->fw_version[0] = '\0';
sprintf(info->bus_info, "PPC 4xx EMAC-%d %s",
dev->cell_index, dev->ofdev->node->full_name);
info->n_stats = emac_ethtool_get_stats_count(ndev);
info->regdump_len = emac_ethtool_get_regs_len(ndev);
}
static const struct ethtool_ops emac_ethtool_ops = {
.get_settings = emac_ethtool_get_settings,
.set_settings = emac_ethtool_set_settings,
.get_drvinfo = emac_ethtool_get_drvinfo,
.get_regs_len = emac_ethtool_get_regs_len,
.get_regs = emac_ethtool_get_regs,
.nway_reset = emac_ethtool_nway_reset,
.get_ringparam = emac_ethtool_get_ringparam,
.get_pauseparam = emac_ethtool_get_pauseparam,
.get_rx_csum = emac_ethtool_get_rx_csum,
.get_strings = emac_ethtool_get_strings,
.get_stats_count = emac_ethtool_get_stats_count,
.get_ethtool_stats = emac_ethtool_get_ethtool_stats,
.get_link = ethtool_op_get_link,
.get_tx_csum = ethtool_op_get_tx_csum,
.get_sg = ethtool_op_get_sg,
};
static int emac_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
{
struct emac_instance *dev = netdev_priv(ndev);
uint16_t *data = (uint16_t *) & rq->ifr_ifru;
DBG(dev, "ioctl %08x" NL, cmd);
if (dev->phy.address < 0)
return -EOPNOTSUPP;
switch (cmd) {
case SIOCGMIIPHY:
case SIOCDEVPRIVATE:
data[0] = dev->phy.address;
/* Fall through */
case SIOCGMIIREG:
case SIOCDEVPRIVATE + 1:
data[3] = emac_mdio_read(ndev, dev->phy.address, data[1]);
return 0;
case SIOCSMIIREG:
case SIOCDEVPRIVATE + 2:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
emac_mdio_write(ndev, dev->phy.address, data[1], data[2]);
return 0;
default:
return -EOPNOTSUPP;
}
}
struct emac_depentry {
u32 phandle;
struct device_node *node;
struct of_device *ofdev;
void *drvdata;
};
#define EMAC_DEP_MAL_IDX 0
#define EMAC_DEP_ZMII_IDX 1
#define EMAC_DEP_RGMII_IDX 2
#define EMAC_DEP_TAH_IDX 3
#define EMAC_DEP_MDIO_IDX 4
#define EMAC_DEP_PREV_IDX 5
#define EMAC_DEP_COUNT 6
static int __devinit emac_check_deps(struct emac_instance *dev,
struct emac_depentry *deps)
{
int i, there = 0;
struct device_node *np;
for (i = 0; i < EMAC_DEP_COUNT; i++) {
/* no dependency on that item, allright */
if (deps[i].phandle == 0) {
there++;
continue;
}
/* special case for blist as the dependency might go away */
if (i == EMAC_DEP_PREV_IDX) {
np = *(dev->blist - 1);
if (np == NULL) {
deps[i].phandle = 0;
there++;
continue;
}
if (deps[i].node == NULL)
deps[i].node = of_node_get(np);
}
if (deps[i].node == NULL)
deps[i].node = of_find_node_by_phandle(deps[i].phandle);
if (deps[i].node == NULL)
continue;
if (deps[i].ofdev == NULL)
deps[i].ofdev = of_find_device_by_node(deps[i].node);
if (deps[i].ofdev == NULL)
continue;
if (deps[i].drvdata == NULL)
deps[i].drvdata = dev_get_drvdata(&deps[i].ofdev->dev);
if (deps[i].drvdata != NULL)
there++;
}
return (there == EMAC_DEP_COUNT);
}
static void emac_put_deps(struct emac_instance *dev)
{
if (dev->mal_dev)
of_dev_put(dev->mal_dev);
if (dev->zmii_dev)
of_dev_put(dev->zmii_dev);
if (dev->rgmii_dev)
of_dev_put(dev->rgmii_dev);
if (dev->mdio_dev)
of_dev_put(dev->mdio_dev);
if (dev->tah_dev)
of_dev_put(dev->tah_dev);
}
static int __devinit emac_of_bus_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
/* We are only intereted in device addition */
if (action == BUS_NOTIFY_BOUND_DRIVER)
wake_up_all(&emac_probe_wait);
return 0;
}
static struct notifier_block emac_of_bus_notifier = {
.notifier_call = emac_of_bus_notify
};
static int __devinit emac_wait_deps(struct emac_instance *dev)
{
struct emac_depentry deps[EMAC_DEP_COUNT];
int i, err;
memset(&deps, 0, sizeof(deps));
deps[EMAC_DEP_MAL_IDX].phandle = dev->mal_ph;
deps[EMAC_DEP_ZMII_IDX].phandle = dev->zmii_ph;
deps[EMAC_DEP_RGMII_IDX].phandle = dev->rgmii_ph;
if (dev->tah_ph)
deps[EMAC_DEP_TAH_IDX].phandle = dev->tah_ph;
if (dev->mdio_ph)
deps[EMAC_DEP_MDIO_IDX].phandle = dev->mdio_ph;
if (dev->blist && dev->blist > emac_boot_list)
deps[EMAC_DEP_PREV_IDX].phandle = 0xffffffffu;
bus_register_notifier(&of_platform_bus_type, &emac_of_bus_notifier);
wait_event_timeout(emac_probe_wait,
emac_check_deps(dev, deps),
EMAC_PROBE_DEP_TIMEOUT);
bus_unregister_notifier(&of_platform_bus_type, &emac_of_bus_notifier);
err = emac_check_deps(dev, deps) ? 0 : -ENODEV;
for (i = 0; i < EMAC_DEP_COUNT; i++) {
if (deps[i].node)
of_node_put(deps[i].node);
if (err && deps[i].ofdev)
of_dev_put(deps[i].ofdev);
}
if (err == 0) {
dev->mal_dev = deps[EMAC_DEP_MAL_IDX].ofdev;
dev->zmii_dev = deps[EMAC_DEP_ZMII_IDX].ofdev;
dev->rgmii_dev = deps[EMAC_DEP_RGMII_IDX].ofdev;
dev->tah_dev = deps[EMAC_DEP_TAH_IDX].ofdev;
dev->mdio_dev = deps[EMAC_DEP_MDIO_IDX].ofdev;
}
if (deps[EMAC_DEP_PREV_IDX].ofdev)
of_dev_put(deps[EMAC_DEP_PREV_IDX].ofdev);
return err;
}
static int __devinit emac_read_uint_prop(struct device_node *np, const char *name,
u32 *val, int fatal)
{
int len;
const u32 *prop = of_get_property(np, name, &len);
if (prop == NULL || len < sizeof(u32)) {
if (fatal)
printk(KERN_ERR "%s: missing %s property\n",
np->full_name, name);
return -ENODEV;
}
*val = *prop;
return 0;
}
static int __devinit emac_init_phy(struct emac_instance *dev)
{
struct device_node *np = dev->ofdev->node;
struct net_device *ndev = dev->ndev;
u32 phy_map, adv;
int i;
dev->phy.dev = ndev;
dev->phy.mode = dev->phy_mode;
/* PHY-less configuration.
* XXX I probably should move these settings to the dev tree
*/
if (dev->phy_address == 0xffffffff && dev->phy_map == 0xffffffff) {
emac_reset(dev);
/* PHY-less configuration.
* XXX I probably should move these settings to the dev tree
*/
dev->phy.address = -1;
dev->phy.features = SUPPORTED_100baseT_Full | SUPPORTED_MII;
dev->phy.pause = 1;
return 0;
}
mutex_lock(&emac_phy_map_lock);
phy_map = dev->phy_map | busy_phy_map;
DBG(dev, "PHY maps %08x %08x" NL, dev->phy_map, busy_phy_map);
dev->phy.mdio_read = emac_mdio_read;
dev->phy.mdio_write = emac_mdio_write;
/* Configure EMAC with defaults so we can at least use MDIO
* This is needed mostly for 440GX
*/
if (emac_phy_gpcs(dev->phy.mode)) {
/* XXX
* Make GPCS PHY address equal to EMAC index.
* We probably should take into account busy_phy_map
* and/or phy_map here.
*
* Note that the busy_phy_map is currently global
* while it should probably be per-ASIC...
*/
dev->phy.address = dev->cell_index;
}
emac_configure(dev);
if (dev->phy_address != 0xffffffff)
phy_map = ~(1 << dev->phy_address);
for (i = 0; i < 0x20; phy_map >>= 1, ++i)
if (!(phy_map & 1)) {
int r;
busy_phy_map |= 1 << i;
/* Quick check if there is a PHY at the address */
r = emac_mdio_read(dev->ndev, i, MII_BMCR);
if (r == 0xffff || r < 0)
continue;
if (!emac_mii_phy_probe(&dev->phy, i))
break;
}
mutex_unlock(&emac_phy_map_lock);
if (i == 0x20) {
printk(KERN_WARNING "%s: can't find PHY!\n", np->full_name);
return -ENXIO;
}
/* Init PHY */
if (dev->phy.def->ops->init)
dev->phy.def->ops->init(&dev->phy);
/* Disable any PHY features not supported by the platform */
dev->phy.def->features &= ~dev->phy_feat_exc;
/* Setup initial link parameters */
if (dev->phy.features & SUPPORTED_Autoneg) {
adv = dev->phy.features;
if (!emac_has_feature(dev, EMAC_FTR_NO_FLOW_CONTROL_40x))
adv |= ADVERTISED_Pause | ADVERTISED_Asym_Pause;
/* Restart autonegotiation */
dev->phy.def->ops->setup_aneg(&dev->phy, adv);
} else {
u32 f = dev->phy.def->features;
int speed = SPEED_10, fd = DUPLEX_HALF;
/* Select highest supported speed/duplex */
if (f & SUPPORTED_1000baseT_Full) {
speed = SPEED_1000;
fd = DUPLEX_FULL;
} else if (f & SUPPORTED_1000baseT_Half)
speed = SPEED_1000;
else if (f & SUPPORTED_100baseT_Full) {
speed = SPEED_100;
fd = DUPLEX_FULL;
} else if (f & SUPPORTED_100baseT_Half)
speed = SPEED_100;
else if (f & SUPPORTED_10baseT_Full)
fd = DUPLEX_FULL;
/* Force link parameters */
dev->phy.def->ops->setup_forced(&dev->phy, speed, fd);
}
return 0;
}
static int __devinit emac_init_config(struct emac_instance *dev)
{
struct device_node *np = dev->ofdev->node;
const void *p;
unsigned int plen;
const char *pm, *phy_modes[] = {
[PHY_MODE_NA] = "",
[PHY_MODE_MII] = "mii",
[PHY_MODE_RMII] = "rmii",
[PHY_MODE_SMII] = "smii",
[PHY_MODE_RGMII] = "rgmii",
[PHY_MODE_TBI] = "tbi",
[PHY_MODE_GMII] = "gmii",
[PHY_MODE_RTBI] = "rtbi",
[PHY_MODE_SGMII] = "sgmii",
};
/* Read config from device-tree */
if (emac_read_uint_prop(np, "mal-device", &dev->mal_ph, 1))
return -ENXIO;
if (emac_read_uint_prop(np, "mal-tx-channel", &dev->mal_tx_chan, 1))
return -ENXIO;
if (emac_read_uint_prop(np, "mal-rx-channel", &dev->mal_rx_chan, 1))
return -ENXIO;
if (emac_read_uint_prop(np, "cell-index", &dev->cell_index, 1))
return -ENXIO;
if (emac_read_uint_prop(np, "max-frame-size", &dev->max_mtu, 0))
dev->max_mtu = 1500;
if (emac_read_uint_prop(np, "rx-fifo-size", &dev->rx_fifo_size, 0))
dev->rx_fifo_size = 2048;
if (emac_read_uint_prop(np, "tx-fifo-size", &dev->tx_fifo_size, 0))
dev->tx_fifo_size = 2048;
if (emac_read_uint_prop(np, "rx-fifo-size-gige", &dev->rx_fifo_size_gige, 0))
dev->rx_fifo_size_gige = dev->rx_fifo_size;
if (emac_read_uint_prop(np, "tx-fifo-size-gige", &dev->tx_fifo_size_gige, 0))
dev->tx_fifo_size_gige = dev->tx_fifo_size;
if (emac_read_uint_prop(np, "phy-address", &dev->phy_address, 0))
dev->phy_address = 0xffffffff;
if (emac_read_uint_prop(np, "phy-map", &dev->phy_map, 0))
dev->phy_map = 0xffffffff;
if (emac_read_uint_prop(np->parent, "clock-frequency", &dev->opb_bus_freq, 1))
return -ENXIO;
if (emac_read_uint_prop(np, "tah-device", &dev->tah_ph, 0))
dev->tah_ph = 0;
if (emac_read_uint_prop(np, "tah-channel", &dev->tah_port, 0))
dev->tah_ph = 0;
if (emac_read_uint_prop(np, "mdio-device", &dev->mdio_ph, 0))
dev->mdio_ph = 0;
if (emac_read_uint_prop(np, "zmii-device", &dev->zmii_ph, 0))
dev->zmii_ph = 0;;
if (emac_read_uint_prop(np, "zmii-channel", &dev->zmii_port, 0))
dev->zmii_port = 0xffffffff;;
if (emac_read_uint_prop(np, "rgmii-device", &dev->rgmii_ph, 0))
dev->rgmii_ph = 0;;
if (emac_read_uint_prop(np, "rgmii-channel", &dev->rgmii_port, 0))
dev->rgmii_port = 0xffffffff;;
if (emac_read_uint_prop(np, "fifo-entry-size", &dev->fifo_entry_size, 0))
dev->fifo_entry_size = 16;
if (emac_read_uint_prop(np, "mal-burst-size", &dev->mal_burst_size, 0))
dev->mal_burst_size = 256;
/* PHY mode needs some decoding */
dev->phy_mode = PHY_MODE_NA;
pm = of_get_property(np, "phy-mode", &plen);
if (pm != NULL) {
int i;
for (i = 0; i < ARRAY_SIZE(phy_modes); i++)
if (!strcasecmp(pm, phy_modes[i])) {
dev->phy_mode = i;
break;
}
}
/* Backward compat with non-final DT */
if (dev->phy_mode == PHY_MODE_NA && pm != NULL && plen == 4) {
u32 nmode = *(const u32 *)pm;
if (nmode > PHY_MODE_NA && nmode <= PHY_MODE_SGMII)
dev->phy_mode = nmode;
}
/* Check EMAC version */
if (of_device_is_compatible(np, "ibm,emac4"))
dev->features |= EMAC_FTR_EMAC4;
if (of_device_is_compatible(np, "ibm,emac-axon")
|| of_device_is_compatible(np, "ibm,emac-440epx"))
dev->features |= EMAC_FTR_HAS_AXON_STACR
| EMAC_FTR_STACR_OC_INVERT;
if (of_device_is_compatible(np, "ibm,emac-440spe"))
dev->features |= EMAC_FTR_STACR_OC_INVERT;
/* Fixup some feature bits based on the device tree and verify
* we have support for them compiled in
*/
if (dev->tah_ph != 0) {
#ifdef CONFIG_IBM_NEW_EMAC_TAH
dev->features |= EMAC_FTR_HAS_TAH;
#else
printk(KERN_ERR "%s: TAH support not enabled !\n",
np->full_name);
return -ENXIO;
#endif
}
if (dev->zmii_ph != 0) {
#ifdef CONFIG_IBM_NEW_EMAC_ZMII
dev->features |= EMAC_FTR_HAS_ZMII;
#else
printk(KERN_ERR "%s: ZMII support not enabled !\n",
np->full_name);
return -ENXIO;
#endif
}
if (dev->rgmii_ph != 0) {
#ifdef CONFIG_IBM_NEW_EMAC_RGMII
dev->features |= EMAC_FTR_HAS_RGMII;
#else
printk(KERN_ERR "%s: RGMII support not enabled !\n",
np->full_name);
return -ENXIO;
#endif
}
/* Read MAC-address */
p = of_get_property(np, "local-mac-address", NULL);
if (p == NULL) {
printk(KERN_ERR "%s: Can't find local-mac-address property\n",
np->full_name);
return -ENXIO;
}
memcpy(dev->ndev->dev_addr, p, 6);
DBG(dev, "features : 0x%08x / 0x%08x\n", dev->features, EMAC_FTRS_POSSIBLE);
DBG(dev, "tx_fifo_size : %d (%d gige)\n", dev->tx_fifo_size, dev->tx_fifo_size_gige);
DBG(dev, "rx_fifo_size : %d (%d gige)\n", dev->rx_fifo_size, dev->rx_fifo_size_gige);
DBG(dev, "max_mtu : %d\n", dev->max_mtu);
DBG(dev, "OPB freq : %d\n", dev->opb_bus_freq);
return 0;
}
static int __devinit emac_probe(struct of_device *ofdev,
const struct of_device_id *match)
{
struct net_device *ndev;
struct emac_instance *dev;
struct device_node *np = ofdev->node;
struct device_node **blist = NULL;
int err, i;
/* Find ourselves in the bootlist if we are there */
for (i = 0; i < EMAC_BOOT_LIST_SIZE; i++)
if (emac_boot_list[i] == np)
blist = &emac_boot_list[i];
/* Allocate our net_device structure */
err = -ENOMEM;
ndev = alloc_etherdev(sizeof(struct emac_instance));
if (!ndev) {
printk(KERN_ERR "%s: could not allocate ethernet device!\n",
np->full_name);
goto err_gone;
}
dev = netdev_priv(ndev);
dev->ndev = ndev;
dev->ofdev = ofdev;
dev->blist = blist;
SET_MODULE_OWNER(ndev);
SET_NETDEV_DEV(ndev, &ofdev->dev);
/* Initialize some embedded data structures */
mutex_init(&dev->mdio_lock);
mutex_init(&dev->link_lock);
spin_lock_init(&dev->lock);
INIT_WORK(&dev->reset_work, emac_reset_work);
/* Init various config data based on device-tree */
err = emac_init_config(dev);
if (err != 0)
goto err_free;
/* Get interrupts. EMAC irq is mandatory, WOL irq is optional */
dev->emac_irq = irq_of_parse_and_map(np, 0);
dev->wol_irq = irq_of_parse_and_map(np, 1);
if (dev->emac_irq == NO_IRQ) {
printk(KERN_ERR "%s: Can't map main interrupt\n", np->full_name);
goto err_free;
}
ndev->irq = dev->emac_irq;
/* Map EMAC regs */
if (of_address_to_resource(np, 0, &dev->rsrc_regs)) {
printk(KERN_ERR "%s: Can't get registers address\n",
np->full_name);
goto err_irq_unmap;
}
// TODO : request_mem_region
dev->emacp = ioremap(dev->rsrc_regs.start, sizeof(struct emac_regs));
if (dev->emacp == NULL) {
printk(KERN_ERR "%s: Can't map device registers!\n",
np->full_name);
err = -ENOMEM;
goto err_irq_unmap;
}
/* Wait for dependent devices */
err = emac_wait_deps(dev);
if (err) {
printk(KERN_ERR
"%s: Timeout waiting for dependent devices\n",
np->full_name);
/* display more info about what's missing ? */
goto err_reg_unmap;
}
dev->mal = dev_get_drvdata(&dev->mal_dev->dev);
if (dev->mdio_dev != NULL)
dev->mdio_instance = dev_get_drvdata(&dev->mdio_dev->dev);
/* Register with MAL */
dev->commac.ops = &emac_commac_ops;
dev->commac.dev = dev;
dev->commac.tx_chan_mask = MAL_CHAN_MASK(dev->mal_tx_chan);
dev->commac.rx_chan_mask = MAL_CHAN_MASK(dev->mal_rx_chan);
err = mal_register_commac(dev->mal, &dev->commac);
if (err) {
printk(KERN_ERR "%s: failed to register with mal %s!\n",
np->full_name, dev->mal_dev->node->full_name);
goto err_rel_deps;
}
dev->rx_skb_size = emac_rx_skb_size(ndev->mtu);
dev->rx_sync_size = emac_rx_sync_size(ndev->mtu);
/* Get pointers to BD rings */
dev->tx_desc =
dev->mal->bd_virt + mal_tx_bd_offset(dev->mal, dev->mal_tx_chan);
dev->rx_desc =
dev->mal->bd_virt + mal_rx_bd_offset(dev->mal, dev->mal_rx_chan);
DBG(dev, "tx_desc %p" NL, dev->tx_desc);
DBG(dev, "rx_desc %p" NL, dev->rx_desc);
/* Clean rings */
memset(dev->tx_desc, 0, NUM_TX_BUFF * sizeof(struct mal_descriptor));
memset(dev->rx_desc, 0, NUM_RX_BUFF * sizeof(struct mal_descriptor));
/* Attach to ZMII, if needed */
if (emac_has_feature(dev, EMAC_FTR_HAS_ZMII) &&
(err = zmii_attach(dev->zmii_dev, dev->zmii_port, &dev->phy_mode)) != 0)
goto err_unreg_commac;
/* Attach to RGMII, if needed */
if (emac_has_feature(dev, EMAC_FTR_HAS_RGMII) &&
(err = rgmii_attach(dev->rgmii_dev, dev->rgmii_port, dev->phy_mode)) != 0)
goto err_detach_zmii;
/* Attach to TAH, if needed */
if (emac_has_feature(dev, EMAC_FTR_HAS_TAH) &&
(err = tah_attach(dev->tah_dev, dev->tah_port)) != 0)
goto err_detach_rgmii;
/* Set some link defaults before we can find out real parameters */
dev->phy.speed = SPEED_100;
dev->phy.duplex = DUPLEX_FULL;
dev->phy.autoneg = AUTONEG_DISABLE;
dev->phy.pause = dev->phy.asym_pause = 0;
dev->stop_timeout = STOP_TIMEOUT_100;
INIT_DELAYED_WORK(&dev->link_work, emac_link_timer);
/* Find PHY if any */
err = emac_init_phy(dev);
if (err != 0)
goto err_detach_tah;
/* Fill in the driver function table */
ndev->open = &emac_open;
#ifdef CONFIG_IBM_NEW_EMAC_TAH
if (dev->tah_dev) {
ndev->hard_start_xmit = &emac_start_xmit_sg;
ndev->features |= NETIF_F_IP_CSUM | NETIF_F_SG;
} else
#endif
ndev->hard_start_xmit = &emac_start_xmit;
ndev->tx_timeout = &emac_tx_timeout;
ndev->watchdog_timeo = 5 * HZ;
ndev->stop = &emac_close;
ndev->get_stats = &emac_stats;
ndev->set_multicast_list = &emac_set_multicast_list;
ndev->do_ioctl = &emac_ioctl;
if (emac_phy_supports_gige(dev->phy_mode)) {
ndev->change_mtu = &emac_change_mtu;
dev->commac.ops = &emac_commac_sg_ops;
}
SET_ETHTOOL_OPS(ndev, &emac_ethtool_ops);
netif_carrier_off(ndev);
netif_stop_queue(ndev);
err = register_netdev(ndev);
if (err) {
printk(KERN_ERR "%s: failed to register net device (%d)!\n",
np->full_name, err);
goto err_detach_tah;
}
/* Set our drvdata last as we don't want them visible until we are
* fully initialized
*/
wmb();
dev_set_drvdata(&ofdev->dev, dev);
/* There's a new kid in town ! Let's tell everybody */
wake_up_all(&emac_probe_wait);
printk(KERN_INFO
"%s: EMAC-%d %s, MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
ndev->name, dev->cell_index, np->full_name,
ndev->dev_addr[0], ndev->dev_addr[1], ndev->dev_addr[2],
ndev->dev_addr[3], ndev->dev_addr[4], ndev->dev_addr[5]);
if (dev->phy.address >= 0)
printk("%s: found %s PHY (0x%02x)\n", ndev->name,
dev->phy.def->name, dev->phy.address);
emac_dbg_register(dev);
/* Life is good */
return 0;
/* I have a bad feeling about this ... */
err_detach_tah:
if (emac_has_feature(dev, EMAC_FTR_HAS_TAH))
tah_detach(dev->tah_dev, dev->tah_port);
err_detach_rgmii:
if (emac_has_feature(dev, EMAC_FTR_HAS_RGMII))
rgmii_detach(dev->rgmii_dev, dev->rgmii_port);
err_detach_zmii:
if (emac_has_feature(dev, EMAC_FTR_HAS_ZMII))
zmii_detach(dev->zmii_dev, dev->zmii_port);
err_unreg_commac:
mal_unregister_commac(dev->mal, &dev->commac);
err_rel_deps:
emac_put_deps(dev);
err_reg_unmap:
iounmap(dev->emacp);
err_irq_unmap:
if (dev->wol_irq != NO_IRQ)
irq_dispose_mapping(dev->wol_irq);
if (dev->emac_irq != NO_IRQ)
irq_dispose_mapping(dev->emac_irq);
err_free:
kfree(ndev);
err_gone:
/* if we were on the bootlist, remove us as we won't show up and
* wake up all waiters to notify them in case they were waiting
* on us
*/
if (blist) {
*blist = NULL;
wake_up_all(&emac_probe_wait);
}
return err;
}
static int __devexit emac_remove(struct of_device *ofdev)
{
struct emac_instance *dev = dev_get_drvdata(&ofdev->dev);
DBG(dev, "remove" NL);
dev_set_drvdata(&ofdev->dev, NULL);
unregister_netdev(dev->ndev);
if (emac_has_feature(dev, EMAC_FTR_HAS_TAH))
tah_detach(dev->tah_dev, dev->tah_port);
if (emac_has_feature(dev, EMAC_FTR_HAS_RGMII))
rgmii_detach(dev->rgmii_dev, dev->rgmii_port);
if (emac_has_feature(dev, EMAC_FTR_HAS_ZMII))
zmii_detach(dev->zmii_dev, dev->zmii_port);
mal_unregister_commac(dev->mal, &dev->commac);
emac_put_deps(dev);
emac_dbg_unregister(dev);
iounmap(dev->emacp);
if (dev->wol_irq != NO_IRQ)
irq_dispose_mapping(dev->wol_irq);
if (dev->emac_irq != NO_IRQ)
irq_dispose_mapping(dev->emac_irq);
kfree(dev->ndev);
return 0;
}
/* XXX Features in here should be replaced by properties... */
static struct of_device_id emac_match[] =
{
{
.type = "network",
.compatible = "ibm,emac",
},
{
.type = "network",
.compatible = "ibm,emac4",
},
{},
};
static struct of_platform_driver emac_driver = {
.name = "emac",
.match_table = emac_match,
.probe = emac_probe,
.remove = emac_remove,
};
static void __init emac_make_bootlist(void)
{
struct device_node *np = NULL;
int j, max, i = 0, k;
int cell_indices[EMAC_BOOT_LIST_SIZE];
/* Collect EMACs */
while((np = of_find_all_nodes(np)) != NULL) {
const u32 *idx;
if (of_match_node(emac_match, np) == NULL)
continue;
if (of_get_property(np, "unused", NULL))
continue;
idx = of_get_property(np, "cell-index", NULL);
if (idx == NULL)
continue;
cell_indices[i] = *idx;
emac_boot_list[i++] = of_node_get(np);
if (i >= EMAC_BOOT_LIST_SIZE) {
of_node_put(np);
break;
}
}
max = i;
/* Bubble sort them (doh, what a creative algorithm :-) */
for (i = 0; max > 1 && (i < (max - 1)); i++)
for (j = i; j < max; j++) {
if (cell_indices[i] > cell_indices[j]) {
np = emac_boot_list[i];
emac_boot_list[i] = emac_boot_list[j];
emac_boot_list[j] = np;
k = cell_indices[i];
cell_indices[i] = cell_indices[j];
cell_indices[j] = k;
}
}
}
static int __init emac_init(void)
{
int rc;
printk(KERN_INFO DRV_DESC ", version " DRV_VERSION "\n");
/* Init debug stuff */
emac_init_debug();
/* Build EMAC boot list */
emac_make_bootlist();
/* Init submodules */
rc = mal_init();
if (rc)
goto err;
rc = zmii_init();
if (rc)
goto err_mal;
rc = rgmii_init();
if (rc)
goto err_zmii;
rc = tah_init();
if (rc)
goto err_rgmii;
rc = of_register_platform_driver(&emac_driver);
if (rc)
goto err_tah;
return 0;
err_tah:
tah_exit();
err_rgmii:
rgmii_exit();
err_zmii:
zmii_exit();
err_mal:
mal_exit();
err:
return rc;
}
static void __exit emac_exit(void)
{
int i;
of_unregister_platform_driver(&emac_driver);
tah_exit();
rgmii_exit();
zmii_exit();
mal_exit();
emac_fini_debug();
/* Destroy EMAC boot list */
for (i = 0; i < EMAC_BOOT_LIST_SIZE; i++)
if (emac_boot_list[i])
of_node_put(emac_boot_list[i]);
}
module_init(emac_init);
module_exit(emac_exit);
/*
* 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 */
/*
* 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 */
/*
* 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 */
/*
* 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 */
/*
* 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);
}
/*
* 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 */
/*
* 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");
/*
* 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 */
/*
* 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, &regs)) {
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);
}
/*
* 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 */
/*
* 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, &regs)) {
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);
}
/*
* 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 */
/*
* 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, &regs)) {
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);
}
/*
* 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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment