stmmac: Move the STMicroelectronics driver

Move the STMicroelectronics driver into driver/net/ethernet/stmicro/ and
make the necessary Kconfig and Makefile changes.

CC: Giuseppe Cavallaro <peppe.cavallaro@st.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
This commit is contained in:
Jeff Kirsher
2011-05-16 00:05:19 -07:00
parent ef7f54297d
commit 7ac6653a08
27 changed files with 37 additions and 11 deletions

View File

@@ -30,6 +30,7 @@ source "drivers/net/ethernet/qlogic/Kconfig"
source "drivers/net/ethernet/racal/Kconfig"
source "drivers/net/ethernet/sfc/Kconfig"
source "drivers/net/ethernet/smsc/Kconfig"
source "drivers/net/ethernet/stmicro/Kconfig"
source "drivers/net/ethernet/sun/Kconfig"
source "drivers/net/ethernet/tehuti/Kconfig"

View File

@@ -21,5 +21,6 @@ obj-$(CONFIG_NET_VENDOR_QLOGIC) += qlogic/
obj-$(CONFIG_NET_VENDOR_RACAL) += racal/
obj-$(CONFIG_SFC) += sfc/
obj-$(CONFIG_NET_VENDOR_SMSC) += smsc/
obj-$(CONFIG_NET_VENDOR_STMICRO) += stmicro/
obj-$(CONFIG_NET_VENDOR_SUN) += sun/
obj-$(CONFIG_NET_VENDOR_TEHUTI) += tehuti/

View File

@@ -0,0 +1,22 @@
#
# STMicroelectronics device configuration
#
config NET_VENDOR_STMICRO
bool "STMicroelectronics devices"
depends on HAS_IOMEM
---help---
If you have a network (Ethernet) card belonging to this class, say Y
and read the Ethernet-HOWTO, available from
<http://www.tldp.org/docs.html#howto>.
Note that the answer to this question doesn't directly affect the
kernel: saying N will just cause the configurator to skip all
the questions about STMicroelectronics cards. If you say Y, you will
be asked for your specific card in the following questions.
if NET_VENDOR_STMICRO
source "drivers/net/ethernet/stmicro/stmmac/Kconfig"
endif # NET_VENDOR_STMICRO

View File

@@ -0,0 +1,5 @@
#
# Makefile for the STMicroelectronics device drivers.
#
obj-$(CONFIG_STMMAC_ETH) += stmmac/

View File

@@ -0,0 +1,57 @@
config STMMAC_ETH
tristate "STMicroelectronics 10/100/1000 Ethernet driver"
depends on HAS_IOMEM
select MII
select PHYLIB
select CRC32
---help---
This is the driver for the Ethernet IPs are built around a
Synopsys IP Core and only tested on the STMicroelectronics
platforms.
if STMMAC_ETH
config STMMAC_DA
bool "STMMAC DMA arbitration scheme"
default n
---help---
Selecting this option, rx has priority over Tx (only for Giga
Ethernet device).
By default, the DMA arbitration scheme is based on Round-robin
(rx:tx priority is 1:1).
config STMMAC_DUAL_MAC
bool "STMMAC: dual mac support (EXPERIMENTAL)"
default n
depends on EXPERIMENTAL && STMMAC_ETH && !STMMAC_TIMER
---help---
Some ST SoCs (for example the stx7141 and stx7200c2) have two
Ethernet Controllers. This option turns on the second Ethernet
device on this kind of platforms.
config STMMAC_TIMER
bool "STMMAC Timer optimisation"
default n
depends on RTC_HCTOSYS_DEVICE
---help---
Use an external timer for mitigating the number of network
interrupts. Currently, for SH architectures, it is possible
to use the TMU channel 2 and the SH-RTC device.
choice
prompt "Select Timer device"
depends on STMMAC_TIMER
config STMMAC_TMU_TIMER
bool "TMU channel 2"
depends on CPU_SH4
---help---
config STMMAC_RTC_TIMER
bool "Real time clock"
depends on RTC_CLASS
---help---
endchoice
endif

View File

@@ -0,0 +1,5 @@
obj-$(CONFIG_STMMAC_ETH) += stmmac.o
stmmac-$(CONFIG_STMMAC_TIMER) += stmmac_timer.o
stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o \
dwmac_lib.o dwmac1000_core.o dwmac1000_dma.o \
dwmac100_core.o dwmac100_dma.o enh_desc.o norm_desc.o $(stmmac-y)

View File

@@ -0,0 +1,252 @@
/*******************************************************************************
STMMAC Common Header File
Copyright (C) 2007-2009 STMicroelectronics Ltd
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
#include <linux/netdevice.h>
#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
#define STMMAC_VLAN_TAG_USED
#include <linux/if_vlan.h>
#endif
#include "descs.h"
#undef CHIP_DEBUG_PRINT
/* Turn-on extra printk debug for MAC core, dma and descriptors */
/* #define CHIP_DEBUG_PRINT */
#ifdef CHIP_DEBUG_PRINT
#define CHIP_DBG(fmt, args...) printk(fmt, ## args)
#else
#define CHIP_DBG(fmt, args...) do { } while (0)
#endif
#undef FRAME_FILTER_DEBUG
/* #define FRAME_FILTER_DEBUG */
struct stmmac_extra_stats {
/* Transmit errors */
unsigned long tx_underflow ____cacheline_aligned;
unsigned long tx_carrier;
unsigned long tx_losscarrier;
unsigned long tx_heartbeat;
unsigned long tx_deferred;
unsigned long tx_vlan;
unsigned long tx_jabber;
unsigned long tx_frame_flushed;
unsigned long tx_payload_error;
unsigned long tx_ip_header_error;
/* Receive errors */
unsigned long rx_desc;
unsigned long rx_partial;
unsigned long rx_runt;
unsigned long rx_toolong;
unsigned long rx_collision;
unsigned long rx_crc;
unsigned long rx_length;
unsigned long rx_mii;
unsigned long rx_multicast;
unsigned long rx_gmac_overflow;
unsigned long rx_watchdog;
unsigned long da_rx_filter_fail;
unsigned long sa_rx_filter_fail;
unsigned long rx_missed_cntr;
unsigned long rx_overflow_cntr;
unsigned long rx_vlan;
/* Tx/Rx IRQ errors */
unsigned long tx_undeflow_irq;
unsigned long tx_process_stopped_irq;
unsigned long tx_jabber_irq;
unsigned long rx_overflow_irq;
unsigned long rx_buf_unav_irq;
unsigned long rx_process_stopped_irq;
unsigned long rx_watchdog_irq;
unsigned long tx_early_irq;
unsigned long fatal_bus_error_irq;
/* Extra info */
unsigned long threshold;
unsigned long tx_pkt_n;
unsigned long rx_pkt_n;
unsigned long poll_n;
unsigned long sched_timer_n;
unsigned long normal_irq_n;
};
#define HASH_TABLE_SIZE 64
#define PAUSE_TIME 0x200
/* Flow Control defines */
#define FLOW_OFF 0
#define FLOW_RX 1
#define FLOW_TX 2
#define FLOW_AUTO (FLOW_TX | FLOW_RX)
#define SF_DMA_MODE 1 /* DMA STORE-AND-FORWARD Operation Mode */
enum rx_frame_status { /* IPC status */
good_frame = 0,
discard_frame = 1,
csum_none = 2,
llc_snap = 4,
};
enum tx_dma_irq_status {
tx_hard_error = 1,
tx_hard_error_bump_tc = 2,
handle_tx_rx = 3,
};
/* GMAC TX FIFO is 8K, Rx FIFO is 16K */
#define BUF_SIZE_16KiB 16384
#define BUF_SIZE_8KiB 8192
#define BUF_SIZE_4KiB 4096
#define BUF_SIZE_2KiB 2048
/* Power Down and WOL */
#define PMT_NOT_SUPPORTED 0
#define PMT_SUPPORTED 1
/* Common MAC defines */
#define MAC_CTRL_REG 0x00000000 /* MAC Control */
#define MAC_ENABLE_TX 0x00000008 /* Transmitter Enable */
#define MAC_RNABLE_RX 0x00000004 /* Receiver Enable */
/* MAC Management Counters register */
#define MMC_CONTROL 0x00000100 /* MMC Control */
#define MMC_HIGH_INTR 0x00000104 /* MMC High Interrupt */
#define MMC_LOW_INTR 0x00000108 /* MMC Low Interrupt */
#define MMC_HIGH_INTR_MASK 0x0000010c /* MMC High Interrupt Mask */
#define MMC_LOW_INTR_MASK 0x00000110 /* MMC Low Interrupt Mask */
#define MMC_CONTROL_MAX_FRM_MASK 0x0003ff8 /* Maximum Frame Size */
#define MMC_CONTROL_MAX_FRM_SHIFT 3
#define MMC_CONTROL_MAX_FRAME 0x7FF
struct stmmac_desc_ops {
/* DMA RX descriptor ring initialization */
void (*init_rx_desc) (struct dma_desc *p, unsigned int ring_size,
int disable_rx_ic);
/* DMA TX descriptor ring initialization */
void (*init_tx_desc) (struct dma_desc *p, unsigned int ring_size);
/* Invoked by the xmit function to prepare the tx descriptor */
void (*prepare_tx_desc) (struct dma_desc *p, int is_fs, int len,
int csum_flag);
/* Set/get the owner of the descriptor */
void (*set_tx_owner) (struct dma_desc *p);
int (*get_tx_owner) (struct dma_desc *p);
/* Invoked by the xmit function to close the tx descriptor */
void (*close_tx_desc) (struct dma_desc *p);
/* Clean the tx descriptor as soon as the tx irq is received */
void (*release_tx_desc) (struct dma_desc *p);
/* Clear interrupt on tx frame completion. When this bit is
* set an interrupt happens as soon as the frame is transmitted */
void (*clear_tx_ic) (struct dma_desc *p);
/* Last tx segment reports the transmit status */
int (*get_tx_ls) (struct dma_desc *p);
/* Return the transmit status looking at the TDES1 */
int (*tx_status) (void *data, struct stmmac_extra_stats *x,
struct dma_desc *p, void __iomem *ioaddr);
/* Get the buffer size from the descriptor */
int (*get_tx_len) (struct dma_desc *p);
/* Handle extra events on specific interrupts hw dependent */
int (*get_rx_owner) (struct dma_desc *p);
void (*set_rx_owner) (struct dma_desc *p);
/* Get the receive frame size */
int (*get_rx_frame_len) (struct dma_desc *p);
/* Return the reception status looking at the RDES1 */
int (*rx_status) (void *data, struct stmmac_extra_stats *x,
struct dma_desc *p);
};
struct stmmac_dma_ops {
/* DMA core initialization */
int (*init) (void __iomem *ioaddr, int pbl, u32 dma_tx, u32 dma_rx);
/* Dump DMA registers */
void (*dump_regs) (void __iomem *ioaddr);
/* Set tx/rx threshold in the csr6 register
* An invalid value enables the store-and-forward mode */
void (*dma_mode) (void __iomem *ioaddr, int txmode, int rxmode);
/* To track extra statistic (if supported) */
void (*dma_diagnostic_fr) (void *data, struct stmmac_extra_stats *x,
void __iomem *ioaddr);
void (*enable_dma_transmission) (void __iomem *ioaddr);
void (*enable_dma_irq) (void __iomem *ioaddr);
void (*disable_dma_irq) (void __iomem *ioaddr);
void (*start_tx) (void __iomem *ioaddr);
void (*stop_tx) (void __iomem *ioaddr);
void (*start_rx) (void __iomem *ioaddr);
void (*stop_rx) (void __iomem *ioaddr);
int (*dma_interrupt) (void __iomem *ioaddr,
struct stmmac_extra_stats *x);
};
struct stmmac_ops {
/* MAC core initialization */
void (*core_init) (void __iomem *ioaddr) ____cacheline_aligned;
/* Support checksum offload engine */
int (*rx_coe) (void __iomem *ioaddr);
/* Dump MAC registers */
void (*dump_regs) (void __iomem *ioaddr);
/* Handle extra events on specific interrupts hw dependent */
void (*host_irq_status) (void __iomem *ioaddr);
/* Multicast filter setting */
void (*set_filter) (struct net_device *dev);
/* Flow control setting */
void (*flow_ctrl) (void __iomem *ioaddr, unsigned int duplex,
unsigned int fc, unsigned int pause_time);
/* Set power management mode (e.g. magic frame) */
void (*pmt) (void __iomem *ioaddr, unsigned long mode);
/* Set/Get Unicast MAC addresses */
void (*set_umac_addr) (void __iomem *ioaddr, unsigned char *addr,
unsigned int reg_n);
void (*get_umac_addr) (void __iomem *ioaddr, unsigned char *addr,
unsigned int reg_n);
};
struct mac_link {
int port;
int duplex;
int speed;
};
struct mii_regs {
unsigned int addr; /* MII Address */
unsigned int data; /* MII Data */
};
struct mac_device_info {
const struct stmmac_ops *mac;
const struct stmmac_desc_ops *desc;
const struct stmmac_dma_ops *dma;
struct mii_regs mii; /* MII register Addresses */
struct mac_link link;
};
struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr);
struct mac_device_info *dwmac100_setup(void __iomem *ioaddr);
extern void stmmac_set_mac_addr(void __iomem *ioaddr, u8 addr[6],
unsigned int high, unsigned int low);
extern void stmmac_get_mac_addr(void __iomem *ioaddr, unsigned char *addr,
unsigned int high, unsigned int low);
extern void dwmac_dma_flush_tx_fifo(void __iomem *ioaddr);

View File

@@ -0,0 +1,163 @@
/*******************************************************************************
Header File to describe the DMA descriptors.
Enhanced descriptors have been in case of DWMAC1000 Cores.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
struct dma_desc {
/* Receive descriptor */
union {
struct {
/* RDES0 */
u32 reserved1:1;
u32 crc_error:1;
u32 dribbling:1;
u32 mii_error:1;
u32 receive_watchdog:1;
u32 frame_type:1;
u32 collision:1;
u32 frame_too_long:1;
u32 last_descriptor:1;
u32 first_descriptor:1;
u32 multicast_frame:1;
u32 run_frame:1;
u32 length_error:1;
u32 partial_frame_error:1;
u32 descriptor_error:1;
u32 error_summary:1;
u32 frame_length:14;
u32 filtering_fail:1;
u32 own:1;
/* RDES1 */
u32 buffer1_size:11;
u32 buffer2_size:11;
u32 reserved2:2;
u32 second_address_chained:1;
u32 end_ring:1;
u32 reserved3:5;
u32 disable_ic:1;
} rx;
struct {
/* RDES0 */
u32 payload_csum_error:1;
u32 crc_error:1;
u32 dribbling:1;
u32 error_gmii:1;
u32 receive_watchdog:1;
u32 frame_type:1;
u32 late_collision:1;
u32 ipc_csum_error:1;
u32 last_descriptor:1;
u32 first_descriptor:1;
u32 vlan_tag:1;
u32 overflow_error:1;
u32 length_error:1;
u32 sa_filter_fail:1;
u32 descriptor_error:1;
u32 error_summary:1;
u32 frame_length:14;
u32 da_filter_fail:1;
u32 own:1;
/* RDES1 */
u32 buffer1_size:13;
u32 reserved1:1;
u32 second_address_chained:1;
u32 end_ring:1;
u32 buffer2_size:13;
u32 reserved2:2;
u32 disable_ic:1;
} erx; /* -- enhanced -- */
/* Transmit descriptor */
struct {
/* TDES0 */
u32 deferred:1;
u32 underflow_error:1;
u32 excessive_deferral:1;
u32 collision_count:4;
u32 heartbeat_fail:1;
u32 excessive_collisions:1;
u32 late_collision:1;
u32 no_carrier:1;
u32 loss_carrier:1;
u32 reserved1:3;
u32 error_summary:1;
u32 reserved2:15;
u32 own:1;
/* TDES1 */
u32 buffer1_size:11;
u32 buffer2_size:11;
u32 reserved3:1;
u32 disable_padding:1;
u32 second_address_chained:1;
u32 end_ring:1;
u32 crc_disable:1;
u32 reserved4:2;
u32 first_segment:1;
u32 last_segment:1;
u32 interrupt:1;
} tx;
struct {
/* TDES0 */
u32 deferred:1;
u32 underflow_error:1;
u32 excessive_deferral:1;
u32 collision_count:4;
u32 vlan_frame:1;
u32 excessive_collisions:1;
u32 late_collision:1;
u32 no_carrier:1;
u32 loss_carrier:1;
u32 payload_error:1;
u32 frame_flushed:1;
u32 jabber_timeout:1;
u32 error_summary:1;
u32 ip_header_error:1;
u32 time_stamp_status:1;
u32 reserved1:2;
u32 second_address_chained:1;
u32 end_ring:1;
u32 checksum_insertion:2;
u32 reserved2:1;
u32 time_stamp_enable:1;
u32 disable_padding:1;
u32 crc_disable:1;
u32 first_segment:1;
u32 last_segment:1;
u32 interrupt:1;
u32 own:1;
/* TDES1 */
u32 buffer1_size:13;
u32 reserved3:3;
u32 buffer2_size:13;
u32 reserved4:3;
} etx; /* -- enhanced -- */
} des01;
unsigned int des2;
unsigned int des3;
};
/* Transmit checksum insertion control */
enum tdes_csum_insertion {
cic_disabled = 0, /* Checksum Insertion Control */
cic_only_ip = 1, /* Only IP header */
cic_no_pseudoheader = 2, /* IP header but pseudoheader
* is not calculated */
cic_full = 3, /* IP header and pseudoheader */
};

View File

@@ -0,0 +1,121 @@
/*******************************************************************************
MAC 10/100 Header File
Copyright (C) 2007-2009 STMicroelectronics Ltd
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
#include <linux/phy.h>
#include "common.h"
/*----------------------------------------------------------------------------
* MAC BLOCK defines
*---------------------------------------------------------------------------*/
/* MAC CSR offset */
#define MAC_CONTROL 0x00000000 /* MAC Control */
#define MAC_ADDR_HIGH 0x00000004 /* MAC Address High */
#define MAC_ADDR_LOW 0x00000008 /* MAC Address Low */
#define MAC_HASH_HIGH 0x0000000c /* Multicast Hash Table High */
#define MAC_HASH_LOW 0x00000010 /* Multicast Hash Table Low */
#define MAC_MII_ADDR 0x00000014 /* MII Address */
#define MAC_MII_DATA 0x00000018 /* MII Data */
#define MAC_FLOW_CTRL 0x0000001c /* Flow Control */
#define MAC_VLAN1 0x00000020 /* VLAN1 Tag */
#define MAC_VLAN2 0x00000024 /* VLAN2 Tag */
/* MAC CTRL defines */
#define MAC_CONTROL_RA 0x80000000 /* Receive All Mode */
#define MAC_CONTROL_BLE 0x40000000 /* Endian Mode */
#define MAC_CONTROL_HBD 0x10000000 /* Heartbeat Disable */
#define MAC_CONTROL_PS 0x08000000 /* Port Select */
#define MAC_CONTROL_DRO 0x00800000 /* Disable Receive Own */
#define MAC_CONTROL_EXT_LOOPBACK 0x00400000 /* Reserved (ext loopback?) */
#define MAC_CONTROL_OM 0x00200000 /* Loopback Operating Mode */
#define MAC_CONTROL_F 0x00100000 /* Full Duplex Mode */
#define MAC_CONTROL_PM 0x00080000 /* Pass All Multicast */
#define MAC_CONTROL_PR 0x00040000 /* Promiscuous Mode */
#define MAC_CONTROL_IF 0x00020000 /* Inverse Filtering */
#define MAC_CONTROL_PB 0x00010000 /* Pass Bad Frames */
#define MAC_CONTROL_HO 0x00008000 /* Hash Only Filtering Mode */
#define MAC_CONTROL_HP 0x00002000 /* Hash/Perfect Filtering Mode */
#define MAC_CONTROL_LCC 0x00001000 /* Late Collision Control */
#define MAC_CONTROL_DBF 0x00000800 /* Disable Broadcast Frames */
#define MAC_CONTROL_DRTY 0x00000400 /* Disable Retry */
#define MAC_CONTROL_ASTP 0x00000100 /* Automatic Pad Stripping */
#define MAC_CONTROL_BOLMT_10 0x00000000 /* Back Off Limit 10 */
#define MAC_CONTROL_BOLMT_8 0x00000040 /* Back Off Limit 8 */
#define MAC_CONTROL_BOLMT_4 0x00000080 /* Back Off Limit 4 */
#define MAC_CONTROL_BOLMT_1 0x000000c0 /* Back Off Limit 1 */
#define MAC_CONTROL_DC 0x00000020 /* Deferral Check */
#define MAC_CONTROL_TE 0x00000008 /* Transmitter Enable */
#define MAC_CONTROL_RE 0x00000004 /* Receiver Enable */
#define MAC_CORE_INIT (MAC_CONTROL_HBD | MAC_CONTROL_ASTP)
/* MAC FLOW CTRL defines */
#define MAC_FLOW_CTRL_PT_MASK 0xffff0000 /* Pause Time Mask */
#define MAC_FLOW_CTRL_PT_SHIFT 16
#define MAC_FLOW_CTRL_PASS 0x00000004 /* Pass Control Frames */
#define MAC_FLOW_CTRL_ENABLE 0x00000002 /* Flow Control Enable */
#define MAC_FLOW_CTRL_PAUSE 0x00000001 /* Flow Control Busy ... */
/* MII ADDR defines */
#define MAC_MII_ADDR_WRITE 0x00000002 /* MII Write */
#define MAC_MII_ADDR_BUSY 0x00000001 /* MII Busy */
/*----------------------------------------------------------------------------
* DMA BLOCK defines
*---------------------------------------------------------------------------*/
/* DMA Bus Mode register defines */
#define DMA_BUS_MODE_DBO 0x00100000 /* Descriptor Byte Ordering */
#define DMA_BUS_MODE_BLE 0x00000080 /* Big Endian/Little Endian */
#define DMA_BUS_MODE_PBL_MASK 0x00003f00 /* Programmable Burst Len */
#define DMA_BUS_MODE_PBL_SHIFT 8
#define DMA_BUS_MODE_DSL_MASK 0x0000007c /* Descriptor Skip Length */
#define DMA_BUS_MODE_DSL_SHIFT 2 /* (in DWORDS) */
#define DMA_BUS_MODE_BAR_BUS 0x00000002 /* Bar-Bus Arbitration */
#define DMA_BUS_MODE_SFT_RESET 0x00000001 /* Software Reset */
#define DMA_BUS_MODE_DEFAULT 0x00000000
/* DMA Control register defines */
#define DMA_CONTROL_SF 0x00200000 /* Store And Forward */
/* Transmit Threshold Control */
enum ttc_control {
DMA_CONTROL_TTC_DEFAULT = 0x00000000, /* Threshold is 32 DWORDS */
DMA_CONTROL_TTC_64 = 0x00004000, /* Threshold is 64 DWORDS */
DMA_CONTROL_TTC_128 = 0x00008000, /* Threshold is 128 DWORDS */
DMA_CONTROL_TTC_256 = 0x0000c000, /* Threshold is 256 DWORDS */
DMA_CONTROL_TTC_18 = 0x00400000, /* Threshold is 18 DWORDS */
DMA_CONTROL_TTC_24 = 0x00404000, /* Threshold is 24 DWORDS */
DMA_CONTROL_TTC_32 = 0x00408000, /* Threshold is 32 DWORDS */
DMA_CONTROL_TTC_40 = 0x0040c000, /* Threshold is 40 DWORDS */
DMA_CONTROL_SE = 0x00000008, /* Stop On Empty */
DMA_CONTROL_OSF = 0x00000004, /* Operate On 2nd Frame */
};
/* STMAC110 DMA Missed Frame Counter register defines */
#define DMA_MISSED_FRAME_OVE 0x10000000 /* FIFO Overflow Overflow */
#define DMA_MISSED_FRAME_OVE_CNTR 0x0ffe0000 /* Overflow Frame Counter */
#define DMA_MISSED_FRAME_OVE_M 0x00010000 /* Missed Frame Overflow */
#define DMA_MISSED_FRAME_M_CNTR 0x0000ffff /* Missed Frame Couinter */
extern const struct stmmac_dma_ops dwmac100_dma_ops;

View File

@@ -0,0 +1,208 @@
/*******************************************************************************
Copyright (C) 2007-2009 STMicroelectronics Ltd
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
#include <linux/phy.h>
#include "common.h"
#define GMAC_CONTROL 0x00000000 /* Configuration */
#define GMAC_FRAME_FILTER 0x00000004 /* Frame Filter */
#define GMAC_HASH_HIGH 0x00000008 /* Multicast Hash Table High */
#define GMAC_HASH_LOW 0x0000000c /* Multicast Hash Table Low */
#define GMAC_MII_ADDR 0x00000010 /* MII Address */
#define GMAC_MII_DATA 0x00000014 /* MII Data */
#define GMAC_FLOW_CTRL 0x00000018 /* Flow Control */
#define GMAC_VLAN_TAG 0x0000001c /* VLAN Tag */
#define GMAC_VERSION 0x00000020 /* GMAC CORE Version */
#define GMAC_WAKEUP_FILTER 0x00000028 /* Wake-up Frame Filter */
#define GMAC_INT_STATUS 0x00000038 /* interrupt status register */
enum dwmac1000_irq_status {
time_stamp_irq = 0x0200,
mmc_rx_csum_offload_irq = 0x0080,
mmc_tx_irq = 0x0040,
mmc_rx_irq = 0x0020,
mmc_irq = 0x0010,
pmt_irq = 0x0008,
pcs_ane_irq = 0x0004,
pcs_link_irq = 0x0002,
rgmii_irq = 0x0001,
};
#define GMAC_INT_MASK 0x0000003c /* interrupt mask register */
/* PMT Control and Status */
#define GMAC_PMT 0x0000002c
enum power_event {
pointer_reset = 0x80000000,
global_unicast = 0x00000200,
wake_up_rx_frame = 0x00000040,
magic_frame = 0x00000020,
wake_up_frame_en = 0x00000004,
magic_pkt_en = 0x00000002,
power_down = 0x00000001,
};
/* GMAC HW ADDR regs */
#define GMAC_ADDR_HIGH(reg) (0x00000040+(reg * 8))
#define GMAC_ADDR_LOW(reg) (0x00000044+(reg * 8))
#define GMAC_MAX_UNICAST_ADDRESSES 16
#define GMAC_AN_CTRL 0x000000c0 /* AN control */
#define GMAC_AN_STATUS 0x000000c4 /* AN status */
#define GMAC_ANE_ADV 0x000000c8 /* Auto-Neg. Advertisement */
#define GMAC_ANE_LINK 0x000000cc /* Auto-Neg. link partener ability */
#define GMAC_ANE_EXP 0x000000d0 /* ANE expansion */
#define GMAC_TBI 0x000000d4 /* TBI extend status */
#define GMAC_GMII_STATUS 0x000000d8 /* S/R-GMII status */
/* GMAC Configuration defines */
#define GMAC_CONTROL_TC 0x01000000 /* Transmit Conf. in RGMII/SGMII */
#define GMAC_CONTROL_WD 0x00800000 /* Disable Watchdog on receive */
#define GMAC_CONTROL_JD 0x00400000 /* Jabber disable */
#define GMAC_CONTROL_BE 0x00200000 /* Frame Burst Enable */
#define GMAC_CONTROL_JE 0x00100000 /* Jumbo frame */
enum inter_frame_gap {
GMAC_CONTROL_IFG_88 = 0x00040000,
GMAC_CONTROL_IFG_80 = 0x00020000,
GMAC_CONTROL_IFG_40 = 0x000e0000,
};
#define GMAC_CONTROL_DCRS 0x00010000 /* Disable carrier sense during tx */
#define GMAC_CONTROL_PS 0x00008000 /* Port Select 0:GMI 1:MII */
#define GMAC_CONTROL_FES 0x00004000 /* Speed 0:10 1:100 */
#define GMAC_CONTROL_DO 0x00002000 /* Disable Rx Own */
#define GMAC_CONTROL_LM 0x00001000 /* Loop-back mode */
#define GMAC_CONTROL_DM 0x00000800 /* Duplex Mode */
#define GMAC_CONTROL_IPC 0x00000400 /* Checksum Offload */
#define GMAC_CONTROL_DR 0x00000200 /* Disable Retry */
#define GMAC_CONTROL_LUD 0x00000100 /* Link up/down */
#define GMAC_CONTROL_ACS 0x00000080 /* Automatic Pad/FCS Stripping */
#define GMAC_CONTROL_DC 0x00000010 /* Deferral Check */
#define GMAC_CONTROL_TE 0x00000008 /* Transmitter Enable */
#define GMAC_CONTROL_RE 0x00000004 /* Receiver Enable */
#define GMAC_CORE_INIT (GMAC_CONTROL_JD | GMAC_CONTROL_PS | GMAC_CONTROL_ACS | \
GMAC_CONTROL_JE | GMAC_CONTROL_BE)
/* GMAC Frame Filter defines */
#define GMAC_FRAME_FILTER_PR 0x00000001 /* Promiscuous Mode */
#define GMAC_FRAME_FILTER_HUC 0x00000002 /* Hash Unicast */
#define GMAC_FRAME_FILTER_HMC 0x00000004 /* Hash Multicast */
#define GMAC_FRAME_FILTER_DAIF 0x00000008 /* DA Inverse Filtering */
#define GMAC_FRAME_FILTER_PM 0x00000010 /* Pass all multicast */
#define GMAC_FRAME_FILTER_DBF 0x00000020 /* Disable Broadcast frames */
#define GMAC_FRAME_FILTER_SAIF 0x00000100 /* Inverse Filtering */
#define GMAC_FRAME_FILTER_SAF 0x00000200 /* Source Address Filter */
#define GMAC_FRAME_FILTER_HPF 0x00000400 /* Hash or perfect Filter */
#define GMAC_FRAME_FILTER_RA 0x80000000 /* Receive all mode */
/* GMII ADDR defines */
#define GMAC_MII_ADDR_WRITE 0x00000002 /* MII Write */
#define GMAC_MII_ADDR_BUSY 0x00000001 /* MII Busy */
/* GMAC FLOW CTRL defines */
#define GMAC_FLOW_CTRL_PT_MASK 0xffff0000 /* Pause Time Mask */
#define GMAC_FLOW_CTRL_PT_SHIFT 16
#define GMAC_FLOW_CTRL_RFE 0x00000004 /* Rx Flow Control Enable */
#define GMAC_FLOW_CTRL_TFE 0x00000002 /* Tx Flow Control Enable */
#define GMAC_FLOW_CTRL_FCB_BPA 0x00000001 /* Flow Control Busy ... */
/*--- DMA BLOCK defines ---*/
/* DMA Bus Mode register defines */
#define DMA_BUS_MODE_SFT_RESET 0x00000001 /* Software Reset */
#define DMA_BUS_MODE_DA 0x00000002 /* Arbitration scheme */
#define DMA_BUS_MODE_DSL_MASK 0x0000007c /* Descriptor Skip Length */
#define DMA_BUS_MODE_DSL_SHIFT 2 /* (in DWORDS) */
/* Programmable burst length (passed thorugh platform)*/
#define DMA_BUS_MODE_PBL_MASK 0x00003f00 /* Programmable Burst Len */
#define DMA_BUS_MODE_PBL_SHIFT 8
enum rx_tx_priority_ratio {
double_ratio = 0x00004000, /*2:1 */
triple_ratio = 0x00008000, /*3:1 */
quadruple_ratio = 0x0000c000, /*4:1 */
};
#define DMA_BUS_MODE_FB 0x00010000 /* Fixed burst */
#define DMA_BUS_MODE_RPBL_MASK 0x003e0000 /* Rx-Programmable Burst Len */
#define DMA_BUS_MODE_RPBL_SHIFT 17
#define DMA_BUS_MODE_USP 0x00800000
#define DMA_BUS_MODE_4PBL 0x01000000
#define DMA_BUS_MODE_AAL 0x02000000
/* DMA CRS Control and Status Register Mapping */
#define DMA_HOST_TX_DESC 0x00001048 /* Current Host Tx descriptor */
#define DMA_HOST_RX_DESC 0x0000104c /* Current Host Rx descriptor */
/* DMA Bus Mode register defines */
#define DMA_BUS_PR_RATIO_MASK 0x0000c000 /* Rx/Tx priority ratio */
#define DMA_BUS_PR_RATIO_SHIFT 14
#define DMA_BUS_FB 0x00010000 /* Fixed Burst */
/* DMA operation mode defines (start/stop tx/rx are placed in common header)*/
#define DMA_CONTROL_DT 0x04000000 /* Disable Drop TCP/IP csum error */
#define DMA_CONTROL_RSF 0x02000000 /* Receive Store and Forward */
#define DMA_CONTROL_DFF 0x01000000 /* Disaable flushing */
/* Threshold for Activating the FC */
enum rfa {
act_full_minus_1 = 0x00800000,
act_full_minus_2 = 0x00800200,
act_full_minus_3 = 0x00800400,
act_full_minus_4 = 0x00800600,
};
/* Threshold for Deactivating the FC */
enum rfd {
deac_full_minus_1 = 0x00400000,
deac_full_minus_2 = 0x00400800,
deac_full_minus_3 = 0x00401000,
deac_full_minus_4 = 0x00401800,
};
#define DMA_CONTROL_TSF 0x00200000 /* Transmit Store and Forward */
enum ttc_control {
DMA_CONTROL_TTC_64 = 0x00000000,
DMA_CONTROL_TTC_128 = 0x00004000,
DMA_CONTROL_TTC_192 = 0x00008000,
DMA_CONTROL_TTC_256 = 0x0000c000,
DMA_CONTROL_TTC_40 = 0x00010000,
DMA_CONTROL_TTC_32 = 0x00014000,
DMA_CONTROL_TTC_24 = 0x00018000,
DMA_CONTROL_TTC_16 = 0x0001c000,
};
#define DMA_CONTROL_TC_TX_MASK 0xfffe3fff
#define DMA_CONTROL_EFC 0x00000100
#define DMA_CONTROL_FEF 0x00000080
#define DMA_CONTROL_FUF 0x00000040
enum rtc_control {
DMA_CONTROL_RTC_64 = 0x00000000,
DMA_CONTROL_RTC_32 = 0x00000008,
DMA_CONTROL_RTC_96 = 0x00000010,
DMA_CONTROL_RTC_128 = 0x00000018,
};
#define DMA_CONTROL_TC_RX_MASK 0xffffffe7
#define DMA_CONTROL_OSF 0x00000004 /* Operate on second frame */
/* MMC registers offset */
#define GMAC_MMC_CTRL 0x100
#define GMAC_MMC_RX_INTR 0x104
#define GMAC_MMC_TX_INTR 0x108
#define GMAC_MMC_RX_CSUM_OFFLOAD 0x208
extern const struct stmmac_dma_ops dwmac1000_dma_ops;

View File

@@ -0,0 +1,251 @@
/*******************************************************************************
This is the driver for the GMAC on-chip Ethernet controller for ST SoCs.
DWC Ether MAC 10/100/1000 Universal version 3.41a has been used for
developing this code.
This only implements the mac core functions for this chip.
Copyright (C) 2007-2009 STMicroelectronics Ltd
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
#include <linux/crc32.h>
#include <linux/slab.h>
#include <asm/io.h>
#include "dwmac1000.h"
static void dwmac1000_core_init(void __iomem *ioaddr)
{
u32 value = readl(ioaddr + GMAC_CONTROL);
value |= GMAC_CORE_INIT;
writel(value, ioaddr + GMAC_CONTROL);
/* STBus Bridge Configuration */
/*writel(0xc5608, ioaddr + 0x00007000);*/
/* Freeze MMC counters */
writel(0x8, ioaddr + GMAC_MMC_CTRL);
/* Mask GMAC interrupts */
writel(0x207, ioaddr + GMAC_INT_MASK);
#ifdef STMMAC_VLAN_TAG_USED
/* Tag detection without filtering */
writel(0x0, ioaddr + GMAC_VLAN_TAG);
#endif
}
static int dwmac1000_rx_coe_supported(void __iomem *ioaddr)
{
u32 value = readl(ioaddr + GMAC_CONTROL);
value |= GMAC_CONTROL_IPC;
writel(value, ioaddr + GMAC_CONTROL);
value = readl(ioaddr + GMAC_CONTROL);
return !!(value & GMAC_CONTROL_IPC);
}
static void dwmac1000_dump_regs(void __iomem *ioaddr)
{
int i;
pr_info("\tDWMAC1000 regs (base addr = 0x%p)\n", ioaddr);
for (i = 0; i < 55; i++) {
int offset = i * 4;
pr_info("\tReg No. %d (offset 0x%x): 0x%08x\n", i,
offset, readl(ioaddr + offset));
}
}
static void dwmac1000_set_umac_addr(void __iomem *ioaddr, unsigned char *addr,
unsigned int reg_n)
{
stmmac_set_mac_addr(ioaddr, addr, GMAC_ADDR_HIGH(reg_n),
GMAC_ADDR_LOW(reg_n));
}
static void dwmac1000_get_umac_addr(void __iomem *ioaddr, unsigned char *addr,
unsigned int reg_n)
{
stmmac_get_mac_addr(ioaddr, addr, GMAC_ADDR_HIGH(reg_n),
GMAC_ADDR_LOW(reg_n));
}
static void dwmac1000_set_filter(struct net_device *dev)
{
void __iomem *ioaddr = (void __iomem *) dev->base_addr;
unsigned int value = 0;
CHIP_DBG(KERN_INFO "%s: # mcasts %d, # unicast %d\n",
__func__, netdev_mc_count(dev), netdev_uc_count(dev));
if (dev->flags & IFF_PROMISC)
value = GMAC_FRAME_FILTER_PR;
else if ((netdev_mc_count(dev) > HASH_TABLE_SIZE)
|| (dev->flags & IFF_ALLMULTI)) {
value = GMAC_FRAME_FILTER_PM; /* pass all multi */
writel(0xffffffff, ioaddr + GMAC_HASH_HIGH);
writel(0xffffffff, ioaddr + GMAC_HASH_LOW);
} else if (!netdev_mc_empty(dev)) {
u32 mc_filter[2];
struct netdev_hw_addr *ha;
/* Hash filter for multicast */
value = GMAC_FRAME_FILTER_HMC;
memset(mc_filter, 0, sizeof(mc_filter));
netdev_for_each_mc_addr(ha, dev) {
/* The upper 6 bits of the calculated CRC are used to
index the contens of the hash table */
int bit_nr =
bitrev32(~crc32_le(~0, ha->addr, 6)) >> 26;
/* The most significant bit determines the register to
* use (H/L) while the other 5 bits determine the bit
* within the register. */
mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
}
writel(mc_filter[0], ioaddr + GMAC_HASH_LOW);
writel(mc_filter[1], ioaddr + GMAC_HASH_HIGH);
}
/* Handle multiple unicast addresses (perfect filtering)*/
if (netdev_uc_count(dev) > GMAC_MAX_UNICAST_ADDRESSES)
/* Switch to promiscuous mode is more than 16 addrs
are required */
value |= GMAC_FRAME_FILTER_PR;
else {
int reg = 1;
struct netdev_hw_addr *ha;
netdev_for_each_uc_addr(ha, dev) {
dwmac1000_set_umac_addr(ioaddr, ha->addr, reg);
reg++;
}
}
#ifdef FRAME_FILTER_DEBUG
/* Enable Receive all mode (to debug filtering_fail errors) */
value |= GMAC_FRAME_FILTER_RA;
#endif
writel(value, ioaddr + GMAC_FRAME_FILTER);
CHIP_DBG(KERN_INFO "\tFrame Filter reg: 0x%08x\n\tHash regs: "
"HI 0x%08x, LO 0x%08x\n", readl(ioaddr + GMAC_FRAME_FILTER),
readl(ioaddr + GMAC_HASH_HIGH), readl(ioaddr + GMAC_HASH_LOW));
}
static void dwmac1000_flow_ctrl(void __iomem *ioaddr, unsigned int duplex,
unsigned int fc, unsigned int pause_time)
{
unsigned int flow = 0;
CHIP_DBG(KERN_DEBUG "GMAC Flow-Control:\n");
if (fc & FLOW_RX) {
CHIP_DBG(KERN_DEBUG "\tReceive Flow-Control ON\n");
flow |= GMAC_FLOW_CTRL_RFE;
}
if (fc & FLOW_TX) {
CHIP_DBG(KERN_DEBUG "\tTransmit Flow-Control ON\n");
flow |= GMAC_FLOW_CTRL_TFE;
}
if (duplex) {
CHIP_DBG(KERN_DEBUG "\tduplex mode: PAUSE %d\n", pause_time);
flow |= (pause_time << GMAC_FLOW_CTRL_PT_SHIFT);
}
writel(flow, ioaddr + GMAC_FLOW_CTRL);
}
static void dwmac1000_pmt(void __iomem *ioaddr, unsigned long mode)
{
unsigned int pmt = 0;
if (mode & WAKE_MAGIC) {
CHIP_DBG(KERN_DEBUG "GMAC: WOL Magic frame\n");
pmt |= power_down | magic_pkt_en;
}
if (mode & WAKE_UCAST) {
CHIP_DBG(KERN_DEBUG "GMAC: WOL on global unicast\n");
pmt |= global_unicast;
}
writel(pmt, ioaddr + GMAC_PMT);
}
static void dwmac1000_irq_status(void __iomem *ioaddr)
{
u32 intr_status = readl(ioaddr + GMAC_INT_STATUS);
/* Not used events (e.g. MMC interrupts) are not handled. */
if ((intr_status & mmc_tx_irq))
CHIP_DBG(KERN_DEBUG "GMAC: MMC tx interrupt: 0x%08x\n",
readl(ioaddr + GMAC_MMC_TX_INTR));
if (unlikely(intr_status & mmc_rx_irq))
CHIP_DBG(KERN_DEBUG "GMAC: MMC rx interrupt: 0x%08x\n",
readl(ioaddr + GMAC_MMC_RX_INTR));
if (unlikely(intr_status & mmc_rx_csum_offload_irq))
CHIP_DBG(KERN_DEBUG "GMAC: MMC rx csum offload: 0x%08x\n",
readl(ioaddr + GMAC_MMC_RX_CSUM_OFFLOAD));
if (unlikely(intr_status & pmt_irq)) {
CHIP_DBG(KERN_DEBUG "GMAC: received Magic frame\n");
/* clear the PMT bits 5 and 6 by reading the PMT
* status register. */
readl(ioaddr + GMAC_PMT);
}
}
static const struct stmmac_ops dwmac1000_ops = {
.core_init = dwmac1000_core_init,
.rx_coe = dwmac1000_rx_coe_supported,
.dump_regs = dwmac1000_dump_regs,
.host_irq_status = dwmac1000_irq_status,
.set_filter = dwmac1000_set_filter,
.flow_ctrl = dwmac1000_flow_ctrl,
.pmt = dwmac1000_pmt,
.set_umac_addr = dwmac1000_set_umac_addr,
.get_umac_addr = dwmac1000_get_umac_addr,
};
struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr)
{
struct mac_device_info *mac;
u32 uid = readl(ioaddr + GMAC_VERSION);
pr_info("\tDWMAC1000 - user ID: 0x%x, Synopsys ID: 0x%x\n",
((uid & 0x0000ff00) >> 8), (uid & 0x000000ff));
mac = kzalloc(sizeof(const struct mac_device_info), GFP_KERNEL);
if (!mac)
return NULL;
mac->mac = &dwmac1000_ops;
mac->dma = &dwmac1000_dma_ops;
mac->link.port = GMAC_CONTROL_PS;
mac->link.duplex = GMAC_CONTROL_DM;
mac->link.speed = GMAC_CONTROL_FES;
mac->mii.addr = GMAC_MII_ADDR;
mac->mii.data = GMAC_MII_DATA;
return mac;
}

View File

@@ -0,0 +1,155 @@
/*******************************************************************************
This is the driver for the GMAC on-chip Ethernet controller for ST SoCs.
DWC Ether MAC 10/100/1000 Universal version 3.41a has been used for
developing this code.
This contains the functions to handle the dma.
Copyright (C) 2007-2009 STMicroelectronics Ltd
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
#include <asm/io.h>
#include "dwmac1000.h"
#include "dwmac_dma.h"
static int dwmac1000_dma_init(void __iomem *ioaddr, int pbl, u32 dma_tx,
u32 dma_rx)
{
u32 value = readl(ioaddr + DMA_BUS_MODE);
int limit;
/* DMA SW reset */
value |= DMA_BUS_MODE_SFT_RESET;
writel(value, ioaddr + DMA_BUS_MODE);
limit = 15000;
while (limit--) {
if (!(readl(ioaddr + DMA_BUS_MODE) & DMA_BUS_MODE_SFT_RESET))
break;
}
if (limit < 0)
return -EBUSY;
value = /* DMA_BUS_MODE_FB | */ DMA_BUS_MODE_4PBL |
((pbl << DMA_BUS_MODE_PBL_SHIFT) |
(pbl << DMA_BUS_MODE_RPBL_SHIFT));
#ifdef CONFIG_STMMAC_DA
value |= DMA_BUS_MODE_DA; /* Rx has priority over tx */
#endif
writel(value, ioaddr + DMA_BUS_MODE);
/* Mask interrupts by writing to CSR7 */
writel(DMA_INTR_DEFAULT_MASK, ioaddr + DMA_INTR_ENA);
/* The base address of the RX/TX descriptor lists must be written into
* DMA CSR3 and CSR4, respectively. */
writel(dma_tx, ioaddr + DMA_TX_BASE_ADDR);
writel(dma_rx, ioaddr + DMA_RCV_BASE_ADDR);
return 0;
}
static void dwmac1000_dma_operation_mode(void __iomem *ioaddr, int txmode,
int rxmode)
{
u32 csr6 = readl(ioaddr + DMA_CONTROL);
if (txmode == SF_DMA_MODE) {
CHIP_DBG(KERN_DEBUG "GMAC: enable TX store and forward mode\n");
/* Transmit COE type 2 cannot be done in cut-through mode. */
csr6 |= DMA_CONTROL_TSF;
/* Operating on second frame increase the performance
* especially when transmit store-and-forward is used.*/
csr6 |= DMA_CONTROL_OSF;
} else {
CHIP_DBG(KERN_DEBUG "GMAC: disabling TX store and forward mode"
" (threshold = %d)\n", txmode);
csr6 &= ~DMA_CONTROL_TSF;
csr6 &= DMA_CONTROL_TC_TX_MASK;
/* Set the transmit threshold */
if (txmode <= 32)
csr6 |= DMA_CONTROL_TTC_32;
else if (txmode <= 64)
csr6 |= DMA_CONTROL_TTC_64;
else if (txmode <= 128)
csr6 |= DMA_CONTROL_TTC_128;
else if (txmode <= 192)
csr6 |= DMA_CONTROL_TTC_192;
else
csr6 |= DMA_CONTROL_TTC_256;
}
if (rxmode == SF_DMA_MODE) {
CHIP_DBG(KERN_DEBUG "GMAC: enable RX store and forward mode\n");
csr6 |= DMA_CONTROL_RSF;
} else {
CHIP_DBG(KERN_DEBUG "GMAC: disabling RX store and forward mode"
" (threshold = %d)\n", rxmode);
csr6 &= ~DMA_CONTROL_RSF;
csr6 &= DMA_CONTROL_TC_RX_MASK;
if (rxmode <= 32)
csr6 |= DMA_CONTROL_RTC_32;
else if (rxmode <= 64)
csr6 |= DMA_CONTROL_RTC_64;
else if (rxmode <= 96)
csr6 |= DMA_CONTROL_RTC_96;
else
csr6 |= DMA_CONTROL_RTC_128;
}
writel(csr6, ioaddr + DMA_CONTROL);
}
/* Not yet implemented --- no RMON module */
static void dwmac1000_dma_diagnostic_fr(void *data,
struct stmmac_extra_stats *x, void __iomem *ioaddr)
{
return;
}
static void dwmac1000_dump_dma_regs(void __iomem *ioaddr)
{
int i;
pr_info(" DMA registers\n");
for (i = 0; i < 22; i++) {
if ((i < 9) || (i > 17)) {
int offset = i * 4;
pr_err("\t Reg No. %d (offset 0x%x): 0x%08x\n", i,
(DMA_BUS_MODE + offset),
readl(ioaddr + DMA_BUS_MODE + offset));
}
}
}
const struct stmmac_dma_ops dwmac1000_dma_ops = {
.init = dwmac1000_dma_init,
.dump_regs = dwmac1000_dump_dma_regs,
.dma_mode = dwmac1000_dma_operation_mode,
.dma_diagnostic_fr = dwmac1000_dma_diagnostic_fr,
.enable_dma_transmission = dwmac_enable_dma_transmission,
.enable_dma_irq = dwmac_enable_dma_irq,
.disable_dma_irq = dwmac_disable_dma_irq,
.start_tx = dwmac_dma_start_tx,
.stop_tx = dwmac_dma_stop_tx,
.start_rx = dwmac_dma_start_rx,
.stop_rx = dwmac_dma_stop_rx,
.dma_interrupt = dwmac_dma_interrupt,
};

View File

@@ -0,0 +1,204 @@
/*******************************************************************************
This is the driver for the MAC 10/100 on-chip Ethernet controller
currently tested on all the ST boards based on STb7109 and stx7200 SoCs.
DWC Ether MAC 10/100 Universal version 4.0 has been used for developing
this code.
This only implements the mac core functions for this chip.
Copyright (C) 2007-2009 STMicroelectronics Ltd
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
#include <linux/crc32.h>
#include <asm/io.h>
#include "dwmac100.h"
static void dwmac100_core_init(void __iomem *ioaddr)
{
u32 value = readl(ioaddr + MAC_CONTROL);
writel((value | MAC_CORE_INIT), ioaddr + MAC_CONTROL);
#ifdef STMMAC_VLAN_TAG_USED
writel(ETH_P_8021Q, ioaddr + MAC_VLAN1);
#endif
}
static int dwmac100_rx_coe_supported(void __iomem *ioaddr)
{
return 0;
}
static void dwmac100_dump_mac_regs(void __iomem *ioaddr)
{
pr_info("\t----------------------------------------------\n"
"\t DWMAC 100 CSR (base addr = 0x%p)\n"
"\t----------------------------------------------\n",
ioaddr);
pr_info("\tcontrol reg (offset 0x%x): 0x%08x\n", MAC_CONTROL,
readl(ioaddr + MAC_CONTROL));
pr_info("\taddr HI (offset 0x%x): 0x%08x\n ", MAC_ADDR_HIGH,
readl(ioaddr + MAC_ADDR_HIGH));
pr_info("\taddr LO (offset 0x%x): 0x%08x\n", MAC_ADDR_LOW,
readl(ioaddr + MAC_ADDR_LOW));
pr_info("\tmulticast hash HI (offset 0x%x): 0x%08x\n",
MAC_HASH_HIGH, readl(ioaddr + MAC_HASH_HIGH));
pr_info("\tmulticast hash LO (offset 0x%x): 0x%08x\n",
MAC_HASH_LOW, readl(ioaddr + MAC_HASH_LOW));
pr_info("\tflow control (offset 0x%x): 0x%08x\n",
MAC_FLOW_CTRL, readl(ioaddr + MAC_FLOW_CTRL));
pr_info("\tVLAN1 tag (offset 0x%x): 0x%08x\n", MAC_VLAN1,
readl(ioaddr + MAC_VLAN1));
pr_info("\tVLAN2 tag (offset 0x%x): 0x%08x\n", MAC_VLAN2,
readl(ioaddr + MAC_VLAN2));
pr_info("\n\tMAC management counter registers\n");
pr_info("\t MMC crtl (offset 0x%x): 0x%08x\n",
MMC_CONTROL, readl(ioaddr + MMC_CONTROL));
pr_info("\t MMC High Interrupt (offset 0x%x): 0x%08x\n",
MMC_HIGH_INTR, readl(ioaddr + MMC_HIGH_INTR));
pr_info("\t MMC Low Interrupt (offset 0x%x): 0x%08x\n",
MMC_LOW_INTR, readl(ioaddr + MMC_LOW_INTR));
pr_info("\t MMC High Interrupt Mask (offset 0x%x): 0x%08x\n",
MMC_HIGH_INTR_MASK, readl(ioaddr + MMC_HIGH_INTR_MASK));
pr_info("\t MMC Low Interrupt Mask (offset 0x%x): 0x%08x\n",
MMC_LOW_INTR_MASK, readl(ioaddr + MMC_LOW_INTR_MASK));
}
static void dwmac100_irq_status(void __iomem *ioaddr)
{
return;
}
static void dwmac100_set_umac_addr(void __iomem *ioaddr, unsigned char *addr,
unsigned int reg_n)
{
stmmac_set_mac_addr(ioaddr, addr, MAC_ADDR_HIGH, MAC_ADDR_LOW);
}
static void dwmac100_get_umac_addr(void __iomem *ioaddr, unsigned char *addr,
unsigned int reg_n)
{
stmmac_get_mac_addr(ioaddr, addr, MAC_ADDR_HIGH, MAC_ADDR_LOW);
}
static void dwmac100_set_filter(struct net_device *dev)
{
void __iomem *ioaddr = (void __iomem *) dev->base_addr;
u32 value = readl(ioaddr + MAC_CONTROL);
if (dev->flags & IFF_PROMISC) {
value |= MAC_CONTROL_PR;
value &= ~(MAC_CONTROL_PM | MAC_CONTROL_IF | MAC_CONTROL_HO |
MAC_CONTROL_HP);
} else if ((netdev_mc_count(dev) > HASH_TABLE_SIZE)
|| (dev->flags & IFF_ALLMULTI)) {
value |= MAC_CONTROL_PM;
value &= ~(MAC_CONTROL_PR | MAC_CONTROL_IF | MAC_CONTROL_HO);
writel(0xffffffff, ioaddr + MAC_HASH_HIGH);
writel(0xffffffff, ioaddr + MAC_HASH_LOW);
} else if (netdev_mc_empty(dev)) { /* no multicast */
value &= ~(MAC_CONTROL_PM | MAC_CONTROL_PR | MAC_CONTROL_IF |
MAC_CONTROL_HO | MAC_CONTROL_HP);
} else {
u32 mc_filter[2];
struct netdev_hw_addr *ha;
/* Perfect filter mode for physical address and Hash
filter for multicast */
value |= MAC_CONTROL_HP;
value &= ~(MAC_CONTROL_PM | MAC_CONTROL_PR |
MAC_CONTROL_IF | MAC_CONTROL_HO);
memset(mc_filter, 0, sizeof(mc_filter));
netdev_for_each_mc_addr(ha, dev) {
/* The upper 6 bits of the calculated CRC are used to
* index the contens of the hash table */
int bit_nr =
ether_crc(ETH_ALEN, ha->addr) >> 26;
/* The most significant bit determines the register to
* use (H/L) while the other 5 bits determine the bit
* within the register. */
mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
}
writel(mc_filter[0], ioaddr + MAC_HASH_LOW);
writel(mc_filter[1], ioaddr + MAC_HASH_HIGH);
}
writel(value, ioaddr + MAC_CONTROL);
CHIP_DBG(KERN_INFO "%s: CTRL reg: 0x%08x Hash regs: "
"HI 0x%08x, LO 0x%08x\n",
__func__, readl(ioaddr + MAC_CONTROL),
readl(ioaddr + MAC_HASH_HIGH), readl(ioaddr + MAC_HASH_LOW));
}
static void dwmac100_flow_ctrl(void __iomem *ioaddr, unsigned int duplex,
unsigned int fc, unsigned int pause_time)
{
unsigned int flow = MAC_FLOW_CTRL_ENABLE;
if (duplex)
flow |= (pause_time << MAC_FLOW_CTRL_PT_SHIFT);
writel(flow, ioaddr + MAC_FLOW_CTRL);
}
/* No PMT module supported for this Ethernet Controller.
* Tested on ST platforms only.
*/
static void dwmac100_pmt(void __iomem *ioaddr, unsigned long mode)
{
return;
}
static const struct stmmac_ops dwmac100_ops = {
.core_init = dwmac100_core_init,
.rx_coe = dwmac100_rx_coe_supported,
.dump_regs = dwmac100_dump_mac_regs,
.host_irq_status = dwmac100_irq_status,
.set_filter = dwmac100_set_filter,
.flow_ctrl = dwmac100_flow_ctrl,
.pmt = dwmac100_pmt,
.set_umac_addr = dwmac100_set_umac_addr,
.get_umac_addr = dwmac100_get_umac_addr,
};
struct mac_device_info *dwmac100_setup(void __iomem *ioaddr)
{
struct mac_device_info *mac;
mac = kzalloc(sizeof(const struct mac_device_info), GFP_KERNEL);
if (!mac)
return NULL;
pr_info("\tDWMAC100\n");
mac->mac = &dwmac100_ops;
mac->dma = &dwmac100_dma_ops;
mac->link.port = MAC_CONTROL_PS;
mac->link.duplex = MAC_CONTROL_F;
mac->link.speed = 0;
mac->mii.addr = MAC_MII_ADDR;
mac->mii.data = MAC_MII_DATA;
return mac;
}

View File

@@ -0,0 +1,143 @@
/*******************************************************************************
This is the driver for the MAC 10/100 on-chip Ethernet controller
currently tested on all the ST boards based on STb7109 and stx7200 SoCs.
DWC Ether MAC 10/100 Universal version 4.0 has been used for developing
this code.
This contains the functions to handle the dma.
Copyright (C) 2007-2009 STMicroelectronics Ltd
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
#include <asm/io.h>
#include "dwmac100.h"
#include "dwmac_dma.h"
static int dwmac100_dma_init(void __iomem *ioaddr, int pbl, u32 dma_tx,
u32 dma_rx)
{
u32 value = readl(ioaddr + DMA_BUS_MODE);
int limit;
/* DMA SW reset */
value |= DMA_BUS_MODE_SFT_RESET;
writel(value, ioaddr + DMA_BUS_MODE);
limit = 15000;
while (limit--) {
if (!(readl(ioaddr + DMA_BUS_MODE) & DMA_BUS_MODE_SFT_RESET))
break;
}
if (limit < 0)
return -EBUSY;
/* Enable Application Access by writing to DMA CSR0 */
writel(DMA_BUS_MODE_DEFAULT | (pbl << DMA_BUS_MODE_PBL_SHIFT),
ioaddr + DMA_BUS_MODE);
/* Mask interrupts by writing to CSR7 */
writel(DMA_INTR_DEFAULT_MASK, ioaddr + DMA_INTR_ENA);
/* The base address of the RX/TX descriptor lists must be written into
* DMA CSR3 and CSR4, respectively. */
writel(dma_tx, ioaddr + DMA_TX_BASE_ADDR);
writel(dma_rx, ioaddr + DMA_RCV_BASE_ADDR);
return 0;
}
/* Store and Forward capability is not used at all..
* The transmit threshold can be programmed by
* setting the TTC bits in the DMA control register.*/
static void dwmac100_dma_operation_mode(void __iomem *ioaddr, int txmode,
int rxmode)
{
u32 csr6 = readl(ioaddr + DMA_CONTROL);
if (txmode <= 32)
csr6 |= DMA_CONTROL_TTC_32;
else if (txmode <= 64)
csr6 |= DMA_CONTROL_TTC_64;
else
csr6 |= DMA_CONTROL_TTC_128;
writel(csr6, ioaddr + DMA_CONTROL);
}
static void dwmac100_dump_dma_regs(void __iomem *ioaddr)
{
int i;
CHIP_DBG(KERN_DEBUG "DWMAC 100 DMA CSR\n");
for (i = 0; i < 9; i++)
pr_debug("\t CSR%d (offset 0x%x): 0x%08x\n", i,
(DMA_BUS_MODE + i * 4),
readl(ioaddr + DMA_BUS_MODE + i * 4));
CHIP_DBG(KERN_DEBUG "\t CSR20 (offset 0x%x): 0x%08x\n",
DMA_CUR_TX_BUF_ADDR, readl(ioaddr + DMA_CUR_TX_BUF_ADDR));
CHIP_DBG(KERN_DEBUG "\t CSR21 (offset 0x%x): 0x%08x\n",
DMA_CUR_RX_BUF_ADDR, readl(ioaddr + DMA_CUR_RX_BUF_ADDR));
}
/* DMA controller has two counters to track the number of
* the receive missed frames. */
static void dwmac100_dma_diagnostic_fr(void *data, struct stmmac_extra_stats *x,
void __iomem *ioaddr)
{
struct net_device_stats *stats = (struct net_device_stats *)data;
u32 csr8 = readl(ioaddr + DMA_MISSED_FRAME_CTR);
if (unlikely(csr8)) {
if (csr8 & DMA_MISSED_FRAME_OVE) {
stats->rx_over_errors += 0x800;
x->rx_overflow_cntr += 0x800;
} else {
unsigned int ove_cntr;
ove_cntr = ((csr8 & DMA_MISSED_FRAME_OVE_CNTR) >> 17);
stats->rx_over_errors += ove_cntr;
x->rx_overflow_cntr += ove_cntr;
}
if (csr8 & DMA_MISSED_FRAME_OVE_M) {
stats->rx_missed_errors += 0xffff;
x->rx_missed_cntr += 0xffff;
} else {
unsigned int miss_f = (csr8 & DMA_MISSED_FRAME_M_CNTR);
stats->rx_missed_errors += miss_f;
x->rx_missed_cntr += miss_f;
}
}
}
const struct stmmac_dma_ops dwmac100_dma_ops = {
.init = dwmac100_dma_init,
.dump_regs = dwmac100_dump_dma_regs,
.dma_mode = dwmac100_dma_operation_mode,
.dma_diagnostic_fr = dwmac100_dma_diagnostic_fr,
.enable_dma_transmission = dwmac_enable_dma_transmission,
.enable_dma_irq = dwmac_enable_dma_irq,
.disable_dma_irq = dwmac_disable_dma_irq,
.start_tx = dwmac_dma_start_tx,
.stop_tx = dwmac_dma_stop_tx,
.start_rx = dwmac_dma_start_rx,
.stop_rx = dwmac_dma_stop_rx,
.dma_interrupt = dwmac_dma_interrupt,
};

View File

@@ -0,0 +1,108 @@
/*******************************************************************************
DWMAC DMA Header file.
Copyright (C) 2007-2009 STMicroelectronics Ltd
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
/* DMA CRS Control and Status Register Mapping */
#define DMA_BUS_MODE 0x00001000 /* Bus Mode */
#define DMA_XMT_POLL_DEMAND 0x00001004 /* Transmit Poll Demand */
#define DMA_RCV_POLL_DEMAND 0x00001008 /* Received Poll Demand */
#define DMA_RCV_BASE_ADDR 0x0000100c /* Receive List Base */
#define DMA_TX_BASE_ADDR 0x00001010 /* Transmit List Base */
#define DMA_STATUS 0x00001014 /* Status Register */
#define DMA_CONTROL 0x00001018 /* Ctrl (Operational Mode) */
#define DMA_INTR_ENA 0x0000101c /* Interrupt Enable */
#define DMA_MISSED_FRAME_CTR 0x00001020 /* Missed Frame Counter */
#define DMA_CUR_TX_BUF_ADDR 0x00001050 /* Current Host Tx Buffer */
#define DMA_CUR_RX_BUF_ADDR 0x00001054 /* Current Host Rx Buffer */
/* DMA Control register defines */
#define DMA_CONTROL_ST 0x00002000 /* Start/Stop Transmission */
#define DMA_CONTROL_SR 0x00000002 /* Start/Stop Receive */
/* DMA Normal interrupt */
#define DMA_INTR_ENA_NIE 0x00010000 /* Normal Summary */
#define DMA_INTR_ENA_TIE 0x00000001 /* Transmit Interrupt */
#define DMA_INTR_ENA_TUE 0x00000004 /* Transmit Buffer Unavailable */
#define DMA_INTR_ENA_RIE 0x00000040 /* Receive Interrupt */
#define DMA_INTR_ENA_ERE 0x00004000 /* Early Receive */
#define DMA_INTR_NORMAL (DMA_INTR_ENA_NIE | DMA_INTR_ENA_RIE | \
DMA_INTR_ENA_TIE)
/* DMA Abnormal interrupt */
#define DMA_INTR_ENA_AIE 0x00008000 /* Abnormal Summary */
#define DMA_INTR_ENA_FBE 0x00002000 /* Fatal Bus Error */
#define DMA_INTR_ENA_ETE 0x00000400 /* Early Transmit */
#define DMA_INTR_ENA_RWE 0x00000200 /* Receive Watchdog */
#define DMA_INTR_ENA_RSE 0x00000100 /* Receive Stopped */
#define DMA_INTR_ENA_RUE 0x00000080 /* Receive Buffer Unavailable */
#define DMA_INTR_ENA_UNE 0x00000020 /* Tx Underflow */
#define DMA_INTR_ENA_OVE 0x00000010 /* Receive Overflow */
#define DMA_INTR_ENA_TJE 0x00000008 /* Transmit Jabber */
#define DMA_INTR_ENA_TSE 0x00000002 /* Transmit Stopped */
#define DMA_INTR_ABNORMAL (DMA_INTR_ENA_AIE | DMA_INTR_ENA_FBE | \
DMA_INTR_ENA_UNE)
/* DMA default interrupt mask */
#define DMA_INTR_DEFAULT_MASK (DMA_INTR_NORMAL | DMA_INTR_ABNORMAL)
/* DMA Status register defines */
#define DMA_STATUS_GPI 0x10000000 /* PMT interrupt */
#define DMA_STATUS_GMI 0x08000000 /* MMC interrupt */
#define DMA_STATUS_GLI 0x04000000 /* GMAC Line interface int */
#define DMA_STATUS_GMI 0x08000000
#define DMA_STATUS_GLI 0x04000000
#define DMA_STATUS_EB_MASK 0x00380000 /* Error Bits Mask */
#define DMA_STATUS_EB_TX_ABORT 0x00080000 /* Error Bits - TX Abort */
#define DMA_STATUS_EB_RX_ABORT 0x00100000 /* Error Bits - RX Abort */
#define DMA_STATUS_TS_MASK 0x00700000 /* Transmit Process State */
#define DMA_STATUS_TS_SHIFT 20
#define DMA_STATUS_RS_MASK 0x000e0000 /* Receive Process State */
#define DMA_STATUS_RS_SHIFT 17
#define DMA_STATUS_NIS 0x00010000 /* Normal Interrupt Summary */
#define DMA_STATUS_AIS 0x00008000 /* Abnormal Interrupt Summary */
#define DMA_STATUS_ERI 0x00004000 /* Early Receive Interrupt */
#define DMA_STATUS_FBI 0x00002000 /* Fatal Bus Error Interrupt */
#define DMA_STATUS_ETI 0x00000400 /* Early Transmit Interrupt */
#define DMA_STATUS_RWT 0x00000200 /* Receive Watchdog Timeout */
#define DMA_STATUS_RPS 0x00000100 /* Receive Process Stopped */
#define DMA_STATUS_RU 0x00000080 /* Receive Buffer Unavailable */
#define DMA_STATUS_RI 0x00000040 /* Receive Interrupt */
#define DMA_STATUS_UNF 0x00000020 /* Transmit Underflow */
#define DMA_STATUS_OVF 0x00000010 /* Receive Overflow */
#define DMA_STATUS_TJT 0x00000008 /* Transmit Jabber Timeout */
#define DMA_STATUS_TU 0x00000004 /* Transmit Buffer Unavailable */
#define DMA_STATUS_TPS 0x00000002 /* Transmit Process Stopped */
#define DMA_STATUS_TI 0x00000001 /* Transmit Interrupt */
#define DMA_CONTROL_FTF 0x00100000 /* Flush transmit FIFO */
extern void dwmac_enable_dma_transmission(void __iomem *ioaddr);
extern void dwmac_enable_dma_irq(void __iomem *ioaddr);
extern void dwmac_disable_dma_irq(void __iomem *ioaddr);
extern void dwmac_dma_start_tx(void __iomem *ioaddr);
extern void dwmac_dma_stop_tx(void __iomem *ioaddr);
extern void dwmac_dma_start_rx(void __iomem *ioaddr);
extern void dwmac_dma_stop_rx(void __iomem *ioaddr);
extern int dwmac_dma_interrupt(void __iomem *ioaddr,
struct stmmac_extra_stats *x);

View File

@@ -0,0 +1,258 @@
/*******************************************************************************
Copyright (C) 2007-2009 STMicroelectronics Ltd
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
#include <linux/io.h>
#include "common.h"
#include "dwmac_dma.h"
#undef DWMAC_DMA_DEBUG
#ifdef DWMAC_DMA_DEBUG
#define DWMAC_LIB_DBG(fmt, args...) printk(fmt, ## args)
#else
#define DWMAC_LIB_DBG(fmt, args...) do { } while (0)
#endif
/* CSR1 enables the transmit DMA to check for new descriptor */
void dwmac_enable_dma_transmission(void __iomem *ioaddr)
{
writel(1, ioaddr + DMA_XMT_POLL_DEMAND);
}
void dwmac_enable_dma_irq(void __iomem *ioaddr)
{
writel(DMA_INTR_DEFAULT_MASK, ioaddr + DMA_INTR_ENA);
}
void dwmac_disable_dma_irq(void __iomem *ioaddr)
{
writel(0, ioaddr + DMA_INTR_ENA);
}
void dwmac_dma_start_tx(void __iomem *ioaddr)
{
u32 value = readl(ioaddr + DMA_CONTROL);
value |= DMA_CONTROL_ST;
writel(value, ioaddr + DMA_CONTROL);
}
void dwmac_dma_stop_tx(void __iomem *ioaddr)
{
u32 value = readl(ioaddr + DMA_CONTROL);
value &= ~DMA_CONTROL_ST;
writel(value, ioaddr + DMA_CONTROL);
}
void dwmac_dma_start_rx(void __iomem *ioaddr)
{
u32 value = readl(ioaddr + DMA_CONTROL);
value |= DMA_CONTROL_SR;
writel(value, ioaddr + DMA_CONTROL);
}
void dwmac_dma_stop_rx(void __iomem *ioaddr)
{
u32 value = readl(ioaddr + DMA_CONTROL);
value &= ~DMA_CONTROL_SR;
writel(value, ioaddr + DMA_CONTROL);
}
#ifdef DWMAC_DMA_DEBUG
static void show_tx_process_state(unsigned int status)
{
unsigned int state;
state = (status & DMA_STATUS_TS_MASK) >> DMA_STATUS_TS_SHIFT;
switch (state) {
case 0:
pr_info("- TX (Stopped): Reset or Stop command\n");
break;
case 1:
pr_info("- TX (Running):Fetching the Tx desc\n");
break;
case 2:
pr_info("- TX (Running): Waiting for end of tx\n");
break;
case 3:
pr_info("- TX (Running): Reading the data "
"and queuing the data into the Tx buf\n");
break;
case 6:
pr_info("- TX (Suspended): Tx Buff Underflow "
"or an unavailable Transmit descriptor\n");
break;
case 7:
pr_info("- TX (Running): Closing Tx descriptor\n");
break;
default:
break;
}
}
static void show_rx_process_state(unsigned int status)
{
unsigned int state;
state = (status & DMA_STATUS_RS_MASK) >> DMA_STATUS_RS_SHIFT;
switch (state) {
case 0:
pr_info("- RX (Stopped): Reset or Stop command\n");
break;
case 1:
pr_info("- RX (Running): Fetching the Rx desc\n");
break;
case 2:
pr_info("- RX (Running):Checking for end of pkt\n");
break;
case 3:
pr_info("- RX (Running): Waiting for Rx pkt\n");
break;
case 4:
pr_info("- RX (Suspended): Unavailable Rx buf\n");
break;
case 5:
pr_info("- RX (Running): Closing Rx descriptor\n");
break;
case 6:
pr_info("- RX(Running): Flushing the current frame"
" from the Rx buf\n");
break;
case 7:
pr_info("- RX (Running): Queuing the Rx frame"
" from the Rx buf into memory\n");
break;
default:
break;
}
}
#endif
int dwmac_dma_interrupt(void __iomem *ioaddr,
struct stmmac_extra_stats *x)
{
int ret = 0;
/* read the status register (CSR5) */
u32 intr_status = readl(ioaddr + DMA_STATUS);
DWMAC_LIB_DBG(KERN_INFO "%s: [CSR5: 0x%08x]\n", __func__, intr_status);
#ifdef DWMAC_DMA_DEBUG
/* It displays the DMA process states (CSR5 register) */
show_tx_process_state(intr_status);
show_rx_process_state(intr_status);
#endif
/* ABNORMAL interrupts */
if (unlikely(intr_status & DMA_STATUS_AIS)) {
DWMAC_LIB_DBG(KERN_INFO "CSR5[15] DMA ABNORMAL IRQ: ");
if (unlikely(intr_status & DMA_STATUS_UNF)) {
DWMAC_LIB_DBG(KERN_INFO "transmit underflow\n");
ret = tx_hard_error_bump_tc;
x->tx_undeflow_irq++;
}
if (unlikely(intr_status & DMA_STATUS_TJT)) {
DWMAC_LIB_DBG(KERN_INFO "transmit jabber\n");
x->tx_jabber_irq++;
}
if (unlikely(intr_status & DMA_STATUS_OVF)) {
DWMAC_LIB_DBG(KERN_INFO "recv overflow\n");
x->rx_overflow_irq++;
}
if (unlikely(intr_status & DMA_STATUS_RU)) {
DWMAC_LIB_DBG(KERN_INFO "receive buffer unavailable\n");
x->rx_buf_unav_irq++;
}
if (unlikely(intr_status & DMA_STATUS_RPS)) {
DWMAC_LIB_DBG(KERN_INFO "receive process stopped\n");
x->rx_process_stopped_irq++;
}
if (unlikely(intr_status & DMA_STATUS_RWT)) {
DWMAC_LIB_DBG(KERN_INFO "receive watchdog\n");
x->rx_watchdog_irq++;
}
if (unlikely(intr_status & DMA_STATUS_ETI)) {
DWMAC_LIB_DBG(KERN_INFO "transmit early interrupt\n");
x->tx_early_irq++;
}
if (unlikely(intr_status & DMA_STATUS_TPS)) {
DWMAC_LIB_DBG(KERN_INFO "transmit process stopped\n");
x->tx_process_stopped_irq++;
ret = tx_hard_error;
}
if (unlikely(intr_status & DMA_STATUS_FBI)) {
DWMAC_LIB_DBG(KERN_INFO "fatal bus error\n");
x->fatal_bus_error_irq++;
ret = tx_hard_error;
}
}
/* TX/RX NORMAL interrupts */
if (intr_status & DMA_STATUS_NIS) {
x->normal_irq_n++;
if (likely((intr_status & DMA_STATUS_RI) ||
(intr_status & (DMA_STATUS_TI))))
ret = handle_tx_rx;
}
/* Optional hardware blocks, interrupts should be disabled */
if (unlikely(intr_status &
(DMA_STATUS_GPI | DMA_STATUS_GMI | DMA_STATUS_GLI)))
pr_info("%s: unexpected status %08x\n", __func__, intr_status);
/* Clear the interrupt by writing a logic 1 to the CSR5[15-0] */
writel((intr_status & 0x1ffff), ioaddr + DMA_STATUS);
DWMAC_LIB_DBG(KERN_INFO "\n\n");
return ret;
}
void dwmac_dma_flush_tx_fifo(void __iomem *ioaddr)
{
u32 csr6 = readl(ioaddr + DMA_CONTROL);
writel((csr6 | DMA_CONTROL_FTF), ioaddr + DMA_CONTROL);
do {} while ((readl(ioaddr + DMA_CONTROL) & DMA_CONTROL_FTF));
}
void stmmac_set_mac_addr(void __iomem *ioaddr, u8 addr[6],
unsigned int high, unsigned int low)
{
unsigned long data;
data = (addr[5] << 8) | addr[4];
writel(data, ioaddr + high);
data = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0];
writel(data, ioaddr + low);
}
void stmmac_get_mac_addr(void __iomem *ioaddr, unsigned char *addr,
unsigned int high, unsigned int low)
{
unsigned int hi_addr, lo_addr;
/* Read the MAC address from the hardware */
hi_addr = readl(ioaddr + high);
lo_addr = readl(ioaddr + low);
/* Extract the MAC address from the high and low words */
addr[0] = lo_addr & 0xff;
addr[1] = (lo_addr >> 8) & 0xff;
addr[2] = (lo_addr >> 16) & 0xff;
addr[3] = (lo_addr >> 24) & 0xff;
addr[4] = hi_addr & 0xff;
addr[5] = (hi_addr >> 8) & 0xff;
}

View File

@@ -0,0 +1,337 @@
/*******************************************************************************
This contains the functions to handle the enhanced descriptors.
Copyright (C) 2007-2009 STMicroelectronics Ltd
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
#include "common.h"
static int enh_desc_get_tx_status(void *data, struct stmmac_extra_stats *x,
struct dma_desc *p, void __iomem *ioaddr)
{
int ret = 0;
struct net_device_stats *stats = (struct net_device_stats *)data;
if (unlikely(p->des01.etx.error_summary)) {
CHIP_DBG(KERN_ERR "GMAC TX error... 0x%08x\n", p->des01.etx);
if (unlikely(p->des01.etx.jabber_timeout)) {
CHIP_DBG(KERN_ERR "\tjabber_timeout error\n");
x->tx_jabber++;
}
if (unlikely(p->des01.etx.frame_flushed)) {
CHIP_DBG(KERN_ERR "\tframe_flushed error\n");
x->tx_frame_flushed++;
dwmac_dma_flush_tx_fifo(ioaddr);
}
if (unlikely(p->des01.etx.loss_carrier)) {
CHIP_DBG(KERN_ERR "\tloss_carrier error\n");
x->tx_losscarrier++;
stats->tx_carrier_errors++;
}
if (unlikely(p->des01.etx.no_carrier)) {
CHIP_DBG(KERN_ERR "\tno_carrier error\n");
x->tx_carrier++;
stats->tx_carrier_errors++;
}
if (unlikely(p->des01.etx.late_collision)) {
CHIP_DBG(KERN_ERR "\tlate_collision error\n");
stats->collisions += p->des01.etx.collision_count;
}
if (unlikely(p->des01.etx.excessive_collisions)) {
CHIP_DBG(KERN_ERR "\texcessive_collisions\n");
stats->collisions += p->des01.etx.collision_count;
}
if (unlikely(p->des01.etx.excessive_deferral)) {
CHIP_DBG(KERN_INFO "\texcessive tx_deferral\n");
x->tx_deferred++;
}
if (unlikely(p->des01.etx.underflow_error)) {
CHIP_DBG(KERN_ERR "\tunderflow error\n");
dwmac_dma_flush_tx_fifo(ioaddr);
x->tx_underflow++;
}
if (unlikely(p->des01.etx.ip_header_error)) {
CHIP_DBG(KERN_ERR "\tTX IP header csum error\n");
x->tx_ip_header_error++;
}
if (unlikely(p->des01.etx.payload_error)) {
CHIP_DBG(KERN_ERR "\tAddr/Payload csum error\n");
x->tx_payload_error++;
dwmac_dma_flush_tx_fifo(ioaddr);
}
ret = -1;
}
if (unlikely(p->des01.etx.deferred)) {
CHIP_DBG(KERN_INFO "GMAC TX status: tx deferred\n");
x->tx_deferred++;
}
#ifdef STMMAC_VLAN_TAG_USED
if (p->des01.etx.vlan_frame) {
CHIP_DBG(KERN_INFO "GMAC TX status: VLAN frame\n");
x->tx_vlan++;
}
#endif
return ret;
}
static int enh_desc_get_tx_len(struct dma_desc *p)
{
return p->des01.etx.buffer1_size;
}
static int enh_desc_coe_rdes0(int ipc_err, int type, int payload_err)
{
int ret = good_frame;
u32 status = (type << 2 | ipc_err << 1 | payload_err) & 0x7;
/* bits 5 7 0 | Frame status
* ----------------------------------------------------------
* 0 0 0 | IEEE 802.3 Type frame (length < 1536 octects)
* 1 0 0 | IPv4/6 No CSUM errorS.
* 1 0 1 | IPv4/6 CSUM PAYLOAD error
* 1 1 0 | IPv4/6 CSUM IP HR error
* 1 1 1 | IPv4/6 IP PAYLOAD AND HEADER errorS
* 0 0 1 | IPv4/6 unsupported IP PAYLOAD
* 0 1 1 | COE bypassed.. no IPv4/6 frame
* 0 1 0 | Reserved.
*/
if (status == 0x0) {
CHIP_DBG(KERN_INFO "RX Des0 status: IEEE 802.3 Type frame.\n");
ret = llc_snap;
} else if (status == 0x4) {
CHIP_DBG(KERN_INFO "RX Des0 status: IPv4/6 No CSUM errorS.\n");
ret = good_frame;
} else if (status == 0x5) {
CHIP_DBG(KERN_ERR "RX Des0 status: IPv4/6 Payload Error.\n");
ret = csum_none;
} else if (status == 0x6) {
CHIP_DBG(KERN_ERR "RX Des0 status: IPv4/6 Header Error.\n");
ret = csum_none;
} else if (status == 0x7) {
CHIP_DBG(KERN_ERR
"RX Des0 status: IPv4/6 Header and Payload Error.\n");
ret = csum_none;
} else if (status == 0x1) {
CHIP_DBG(KERN_ERR
"RX Des0 status: IPv4/6 unsupported IP PAYLOAD.\n");
ret = discard_frame;
} else if (status == 0x3) {
CHIP_DBG(KERN_ERR "RX Des0 status: No IPv4, IPv6 frame.\n");
ret = discard_frame;
}
return ret;
}
static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x,
struct dma_desc *p)
{
int ret = good_frame;
struct net_device_stats *stats = (struct net_device_stats *)data;
if (unlikely(p->des01.erx.error_summary)) {
CHIP_DBG(KERN_ERR "GMAC RX Error Summary 0x%08x\n",
p->des01.erx);
if (unlikely(p->des01.erx.descriptor_error)) {
CHIP_DBG(KERN_ERR "\tdescriptor error\n");
x->rx_desc++;
stats->rx_length_errors++;
}
if (unlikely(p->des01.erx.overflow_error)) {
CHIP_DBG(KERN_ERR "\toverflow error\n");
x->rx_gmac_overflow++;
}
if (unlikely(p->des01.erx.ipc_csum_error))
CHIP_DBG(KERN_ERR "\tIPC Csum Error/Giant frame\n");
if (unlikely(p->des01.erx.late_collision)) {
CHIP_DBG(KERN_ERR "\tlate_collision error\n");
stats->collisions++;
stats->collisions++;
}
if (unlikely(p->des01.erx.receive_watchdog)) {
CHIP_DBG(KERN_ERR "\treceive_watchdog error\n");
x->rx_watchdog++;
}
if (unlikely(p->des01.erx.error_gmii)) {
CHIP_DBG(KERN_ERR "\tReceive Error\n");
x->rx_mii++;
}
if (unlikely(p->des01.erx.crc_error)) {
CHIP_DBG(KERN_ERR "\tCRC error\n");
x->rx_crc++;
stats->rx_crc_errors++;
}
ret = discard_frame;
}
/* After a payload csum error, the ES bit is set.
* It doesn't match with the information reported into the databook.
* At any rate, we need to understand if the CSUM hw computation is ok
* and report this info to the upper layers. */
ret = enh_desc_coe_rdes0(p->des01.erx.ipc_csum_error,
p->des01.erx.frame_type, p->des01.erx.payload_csum_error);
if (unlikely(p->des01.erx.dribbling)) {
CHIP_DBG(KERN_ERR "GMAC RX: dribbling error\n");
ret = discard_frame;
}
if (unlikely(p->des01.erx.sa_filter_fail)) {
CHIP_DBG(KERN_ERR "GMAC RX : Source Address filter fail\n");
x->sa_rx_filter_fail++;
ret = discard_frame;
}
if (unlikely(p->des01.erx.da_filter_fail)) {
CHIP_DBG(KERN_ERR "GMAC RX : Dest Address filter fail\n");
x->da_rx_filter_fail++;
ret = discard_frame;
}
if (unlikely(p->des01.erx.length_error)) {
CHIP_DBG(KERN_ERR "GMAC RX: length_error error\n");
x->rx_length++;
ret = discard_frame;
}
#ifdef STMMAC_VLAN_TAG_USED
if (p->des01.erx.vlan_tag) {
CHIP_DBG(KERN_INFO "GMAC RX: VLAN frame tagged\n");
x->rx_vlan++;
}
#endif
return ret;
}
static void enh_desc_init_rx_desc(struct dma_desc *p, unsigned int ring_size,
int disable_rx_ic)
{
int i;
for (i = 0; i < ring_size; i++) {
p->des01.erx.own = 1;
p->des01.erx.buffer1_size = BUF_SIZE_8KiB - 1;
/* To support jumbo frames */
p->des01.erx.buffer2_size = BUF_SIZE_8KiB - 1;
if (i == ring_size - 1)
p->des01.erx.end_ring = 1;
if (disable_rx_ic)
p->des01.erx.disable_ic = 1;
p++;
}
}
static void enh_desc_init_tx_desc(struct dma_desc *p, unsigned int ring_size)
{
int i;
for (i = 0; i < ring_size; i++) {
p->des01.etx.own = 0;
if (i == ring_size - 1)
p->des01.etx.end_ring = 1;
p++;
}
}
static int enh_desc_get_tx_owner(struct dma_desc *p)
{
return p->des01.etx.own;
}
static int enh_desc_get_rx_owner(struct dma_desc *p)
{
return p->des01.erx.own;
}
static void enh_desc_set_tx_owner(struct dma_desc *p)
{
p->des01.etx.own = 1;
}
static void enh_desc_set_rx_owner(struct dma_desc *p)
{
p->des01.erx.own = 1;
}
static int enh_desc_get_tx_ls(struct dma_desc *p)
{
return p->des01.etx.last_segment;
}
static void enh_desc_release_tx_desc(struct dma_desc *p)
{
int ter = p->des01.etx.end_ring;
memset(p, 0, offsetof(struct dma_desc, des2));
p->des01.etx.end_ring = ter;
}
static void enh_desc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len,
int csum_flag)
{
p->des01.etx.first_segment = is_fs;
if (unlikely(len > BUF_SIZE_4KiB)) {
p->des01.etx.buffer1_size = BUF_SIZE_4KiB;
p->des01.etx.buffer2_size = len - BUF_SIZE_4KiB;
} else {
p->des01.etx.buffer1_size = len;
}
if (likely(csum_flag))
p->des01.etx.checksum_insertion = cic_full;
}
static void enh_desc_clear_tx_ic(struct dma_desc *p)
{
p->des01.etx.interrupt = 0;
}
static void enh_desc_close_tx_desc(struct dma_desc *p)
{
p->des01.etx.last_segment = 1;
p->des01.etx.interrupt = 1;
}
static int enh_desc_get_rx_frame_len(struct dma_desc *p)
{
return p->des01.erx.frame_length;
}
const struct stmmac_desc_ops enh_desc_ops = {
.tx_status = enh_desc_get_tx_status,
.rx_status = enh_desc_get_rx_status,
.get_tx_len = enh_desc_get_tx_len,
.init_rx_desc = enh_desc_init_rx_desc,
.init_tx_desc = enh_desc_init_tx_desc,
.get_tx_owner = enh_desc_get_tx_owner,
.get_rx_owner = enh_desc_get_rx_owner,
.release_tx_desc = enh_desc_release_tx_desc,
.prepare_tx_desc = enh_desc_prepare_tx_desc,
.clear_tx_ic = enh_desc_clear_tx_ic,
.close_tx_desc = enh_desc_close_tx_desc,
.get_tx_ls = enh_desc_get_tx_ls,
.set_tx_owner = enh_desc_set_tx_owner,
.set_rx_owner = enh_desc_set_rx_owner,
.get_rx_frame_len = enh_desc_get_rx_frame_len,
};

View File

@@ -0,0 +1,221 @@
/*******************************************************************************
This contains the functions to handle the normal descriptors.
Copyright (C) 2007-2009 STMicroelectronics Ltd
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
#include "common.h"
static int ndesc_get_tx_status(void *data, struct stmmac_extra_stats *x,
struct dma_desc *p, void __iomem *ioaddr)
{
int ret = 0;
struct net_device_stats *stats = (struct net_device_stats *)data;
if (unlikely(p->des01.tx.error_summary)) {
if (unlikely(p->des01.tx.underflow_error)) {
x->tx_underflow++;
stats->tx_fifo_errors++;
}
if (unlikely(p->des01.tx.no_carrier)) {
x->tx_carrier++;
stats->tx_carrier_errors++;
}
if (unlikely(p->des01.tx.loss_carrier)) {
x->tx_losscarrier++;
stats->tx_carrier_errors++;
}
if (unlikely((p->des01.tx.excessive_deferral) ||
(p->des01.tx.excessive_collisions) ||
(p->des01.tx.late_collision)))
stats->collisions += p->des01.tx.collision_count;
ret = -1;
}
if (unlikely(p->des01.tx.heartbeat_fail)) {
x->tx_heartbeat++;
stats->tx_heartbeat_errors++;
ret = -1;
}
if (unlikely(p->des01.tx.deferred))
x->tx_deferred++;
return ret;
}
static int ndesc_get_tx_len(struct dma_desc *p)
{
return p->des01.tx.buffer1_size;
}
/* This function verifies if each incoming frame has some errors
* and, if required, updates the multicast statistics.
* In case of success, it returns csum_none because the device
* is not able to compute the csum in HW. */
static int ndesc_get_rx_status(void *data, struct stmmac_extra_stats *x,
struct dma_desc *p)
{
int ret = csum_none;
struct net_device_stats *stats = (struct net_device_stats *)data;
if (unlikely(p->des01.rx.last_descriptor == 0)) {
pr_warning("ndesc Error: Oversized Ethernet "
"frame spanned multiple buffers\n");
stats->rx_length_errors++;
return discard_frame;
}
if (unlikely(p->des01.rx.error_summary)) {
if (unlikely(p->des01.rx.descriptor_error))
x->rx_desc++;
if (unlikely(p->des01.rx.partial_frame_error))
x->rx_partial++;
if (unlikely(p->des01.rx.run_frame))
x->rx_runt++;
if (unlikely(p->des01.rx.frame_too_long))
x->rx_toolong++;
if (unlikely(p->des01.rx.collision)) {
x->rx_collision++;
stats->collisions++;
}
if (unlikely(p->des01.rx.crc_error)) {
x->rx_crc++;
stats->rx_crc_errors++;
}
ret = discard_frame;
}
if (unlikely(p->des01.rx.dribbling))
ret = discard_frame;
if (unlikely(p->des01.rx.length_error)) {
x->rx_length++;
ret = discard_frame;
}
if (unlikely(p->des01.rx.mii_error)) {
x->rx_mii++;
ret = discard_frame;
}
if (p->des01.rx.multicast_frame) {
x->rx_multicast++;
stats->multicast++;
}
return ret;
}
static void ndesc_init_rx_desc(struct dma_desc *p, unsigned int ring_size,
int disable_rx_ic)
{
int i;
for (i = 0; i < ring_size; i++) {
p->des01.rx.own = 1;
p->des01.rx.buffer1_size = BUF_SIZE_2KiB - 1;
if (i == ring_size - 1)
p->des01.rx.end_ring = 1;
if (disable_rx_ic)
p->des01.rx.disable_ic = 1;
p++;
}
}
static void ndesc_init_tx_desc(struct dma_desc *p, unsigned int ring_size)
{
int i;
for (i = 0; i < ring_size; i++) {
p->des01.tx.own = 0;
if (i == ring_size - 1)
p->des01.tx.end_ring = 1;
p++;
}
}
static int ndesc_get_tx_owner(struct dma_desc *p)
{
return p->des01.tx.own;
}
static int ndesc_get_rx_owner(struct dma_desc *p)
{
return p->des01.rx.own;
}
static void ndesc_set_tx_owner(struct dma_desc *p)
{
p->des01.tx.own = 1;
}
static void ndesc_set_rx_owner(struct dma_desc *p)
{
p->des01.rx.own = 1;
}
static int ndesc_get_tx_ls(struct dma_desc *p)
{
return p->des01.tx.last_segment;
}
static void ndesc_release_tx_desc(struct dma_desc *p)
{
int ter = p->des01.tx.end_ring;
memset(p, 0, offsetof(struct dma_desc, des2));
/* set termination field */
p->des01.tx.end_ring = ter;
}
static void ndesc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len,
int csum_flag)
{
p->des01.tx.first_segment = is_fs;
p->des01.tx.buffer1_size = len;
}
static void ndesc_clear_tx_ic(struct dma_desc *p)
{
p->des01.tx.interrupt = 0;
}
static void ndesc_close_tx_desc(struct dma_desc *p)
{
p->des01.tx.last_segment = 1;
p->des01.tx.interrupt = 1;
}
static int ndesc_get_rx_frame_len(struct dma_desc *p)
{
return p->des01.rx.frame_length;
}
const struct stmmac_desc_ops ndesc_ops = {
.tx_status = ndesc_get_tx_status,
.rx_status = ndesc_get_rx_status,
.get_tx_len = ndesc_get_tx_len,
.init_rx_desc = ndesc_init_rx_desc,
.init_tx_desc = ndesc_init_tx_desc,
.get_tx_owner = ndesc_get_tx_owner,
.get_rx_owner = ndesc_get_rx_owner,
.release_tx_desc = ndesc_release_tx_desc,
.prepare_tx_desc = ndesc_prepare_tx_desc,
.clear_tx_ic = ndesc_clear_tx_ic,
.close_tx_desc = ndesc_close_tx_desc,
.get_tx_ls = ndesc_get_tx_ls,
.set_tx_owner = ndesc_set_tx_owner,
.set_rx_owner = ndesc_set_rx_owner,
.get_rx_frame_len = ndesc_get_rx_frame_len,
};

View File

@@ -0,0 +1,85 @@
/*******************************************************************************
Copyright (C) 2007-2009 STMicroelectronics Ltd
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
#define DRV_MODULE_VERSION "July_2011"
#include <linux/stmmac.h>
#include "common.h"
#ifdef CONFIG_STMMAC_TIMER
#include "stmmac_timer.h"
#endif
struct stmmac_priv {
/* Frequently used values are kept adjacent for cache effect */
struct dma_desc *dma_tx ____cacheline_aligned;
dma_addr_t dma_tx_phy;
struct sk_buff **tx_skbuff;
unsigned int cur_tx;
unsigned int dirty_tx;
unsigned int dma_tx_size;
int tx_coalesce;
struct dma_desc *dma_rx ;
unsigned int cur_rx;
unsigned int dirty_rx;
struct sk_buff **rx_skbuff;
dma_addr_t *rx_skbuff_dma;
struct sk_buff_head rx_recycle;
struct net_device *dev;
dma_addr_t dma_rx_phy;
unsigned int dma_rx_size;
unsigned int dma_buf_sz;
struct device *device;
struct mac_device_info *hw;
void __iomem *ioaddr;
struct stmmac_extra_stats xstats;
struct napi_struct napi;
int rx_coe;
int no_csum_insertion;
struct phy_device *phydev;
int oldlink;
int speed;
int oldduplex;
unsigned int flow_ctrl;
unsigned int pause;
struct mii_bus *mii;
int mii_irq[PHY_MAX_ADDR];
u32 msg_enable;
spinlock_t lock;
int wolopts;
int wolenabled;
#ifdef CONFIG_STMMAC_TIMER
struct stmmac_timer *tm;
#endif
struct plat_stmmacenet_data *plat;
};
extern int stmmac_mdio_unregister(struct net_device *ndev);
extern int stmmac_mdio_register(struct net_device *ndev);
extern void stmmac_set_ethtool_ops(struct net_device *netdev);
extern const struct stmmac_desc_ops enh_desc_ops;
extern const struct stmmac_desc_ops ndesc_ops;

View File

@@ -0,0 +1,359 @@
/*******************************************************************************
STMMAC Ethtool support
Copyright (C) 2007-2009 STMicroelectronics Ltd
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/interrupt.h>
#include <linux/mii.h>
#include <linux/phy.h>
#include <asm/io.h>
#include "stmmac.h"
#include "dwmac_dma.h"
#define REG_SPACE_SIZE 0x1054
#define MAC100_ETHTOOL_NAME "st_mac100"
#define GMAC_ETHTOOL_NAME "st_gmac"
struct stmmac_stats {
char stat_string[ETH_GSTRING_LEN];
int sizeof_stat;
int stat_offset;
};
#define STMMAC_STAT(m) \
{ #m, FIELD_SIZEOF(struct stmmac_extra_stats, m), \
offsetof(struct stmmac_priv, xstats.m)}
static const struct stmmac_stats stmmac_gstrings_stats[] = {
STMMAC_STAT(tx_underflow),
STMMAC_STAT(tx_carrier),
STMMAC_STAT(tx_losscarrier),
STMMAC_STAT(tx_heartbeat),
STMMAC_STAT(tx_deferred),
STMMAC_STAT(tx_vlan),
STMMAC_STAT(rx_vlan),
STMMAC_STAT(tx_jabber),
STMMAC_STAT(tx_frame_flushed),
STMMAC_STAT(tx_payload_error),
STMMAC_STAT(tx_ip_header_error),
STMMAC_STAT(rx_desc),
STMMAC_STAT(rx_partial),
STMMAC_STAT(rx_runt),
STMMAC_STAT(rx_toolong),
STMMAC_STAT(rx_collision),
STMMAC_STAT(rx_crc),
STMMAC_STAT(rx_length),
STMMAC_STAT(rx_mii),
STMMAC_STAT(rx_multicast),
STMMAC_STAT(rx_gmac_overflow),
STMMAC_STAT(rx_watchdog),
STMMAC_STAT(da_rx_filter_fail),
STMMAC_STAT(sa_rx_filter_fail),
STMMAC_STAT(rx_missed_cntr),
STMMAC_STAT(rx_overflow_cntr),
STMMAC_STAT(tx_undeflow_irq),
STMMAC_STAT(tx_process_stopped_irq),
STMMAC_STAT(tx_jabber_irq),
STMMAC_STAT(rx_overflow_irq),
STMMAC_STAT(rx_buf_unav_irq),
STMMAC_STAT(rx_process_stopped_irq),
STMMAC_STAT(rx_watchdog_irq),
STMMAC_STAT(tx_early_irq),
STMMAC_STAT(fatal_bus_error_irq),
STMMAC_STAT(threshold),
STMMAC_STAT(tx_pkt_n),
STMMAC_STAT(rx_pkt_n),
STMMAC_STAT(poll_n),
STMMAC_STAT(sched_timer_n),
STMMAC_STAT(normal_irq_n),
};
#define STMMAC_STATS_LEN ARRAY_SIZE(stmmac_gstrings_stats)
static void stmmac_ethtool_getdrvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
struct stmmac_priv *priv = netdev_priv(dev);
if (!priv->plat->has_gmac)
strcpy(info->driver, MAC100_ETHTOOL_NAME);
else
strcpy(info->driver, GMAC_ETHTOOL_NAME);
strcpy(info->version, DRV_MODULE_VERSION);
info->fw_version[0] = '\0';
info->n_stats = STMMAC_STATS_LEN;
}
static int stmmac_ethtool_getsettings(struct net_device *dev,
struct ethtool_cmd *cmd)
{
struct stmmac_priv *priv = netdev_priv(dev);
struct phy_device *phy = priv->phydev;
int rc;
if (phy == NULL) {
pr_err("%s: %s: PHY is not registered\n",
__func__, dev->name);
return -ENODEV;
}
if (!netif_running(dev)) {
pr_err("%s: interface is disabled: we cannot track "
"link speed / duplex setting\n", dev->name);
return -EBUSY;
}
cmd->transceiver = XCVR_INTERNAL;
spin_lock_irq(&priv->lock);
rc = phy_ethtool_gset(phy, cmd);
spin_unlock_irq(&priv->lock);
return rc;
}
static int stmmac_ethtool_setsettings(struct net_device *dev,
struct ethtool_cmd *cmd)
{
struct stmmac_priv *priv = netdev_priv(dev);
struct phy_device *phy = priv->phydev;
int rc;
spin_lock(&priv->lock);
rc = phy_ethtool_sset(phy, cmd);
spin_unlock(&priv->lock);
return rc;
}
static u32 stmmac_ethtool_getmsglevel(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
return priv->msg_enable;
}
static void stmmac_ethtool_setmsglevel(struct net_device *dev, u32 level)
{
struct stmmac_priv *priv = netdev_priv(dev);
priv->msg_enable = level;
}
static int stmmac_check_if_running(struct net_device *dev)
{
if (!netif_running(dev))
return -EBUSY;
return 0;
}
static int stmmac_ethtool_get_regs_len(struct net_device *dev)
{
return REG_SPACE_SIZE;
}
static void stmmac_ethtool_gregs(struct net_device *dev,
struct ethtool_regs *regs, void *space)
{
int i;
u32 *reg_space = (u32 *) space;
struct stmmac_priv *priv = netdev_priv(dev);
memset(reg_space, 0x0, REG_SPACE_SIZE);
if (!priv->plat->has_gmac) {
/* MAC registers */
for (i = 0; i < 12; i++)
reg_space[i] = readl(priv->ioaddr + (i * 4));
/* DMA registers */
for (i = 0; i < 9; i++)
reg_space[i + 12] =
readl(priv->ioaddr + (DMA_BUS_MODE + (i * 4)));
reg_space[22] = readl(priv->ioaddr + DMA_CUR_TX_BUF_ADDR);
reg_space[23] = readl(priv->ioaddr + DMA_CUR_RX_BUF_ADDR);
} else {
/* MAC registers */
for (i = 0; i < 55; i++)
reg_space[i] = readl(priv->ioaddr + (i * 4));
/* DMA registers */
for (i = 0; i < 22; i++)
reg_space[i + 55] =
readl(priv->ioaddr + (DMA_BUS_MODE + (i * 4)));
}
}
static void
stmmac_get_pauseparam(struct net_device *netdev,
struct ethtool_pauseparam *pause)
{
struct stmmac_priv *priv = netdev_priv(netdev);
spin_lock(&priv->lock);
pause->rx_pause = 0;
pause->tx_pause = 0;
pause->autoneg = priv->phydev->autoneg;
if (priv->flow_ctrl & FLOW_RX)
pause->rx_pause = 1;
if (priv->flow_ctrl & FLOW_TX)
pause->tx_pause = 1;
spin_unlock(&priv->lock);
}
static int
stmmac_set_pauseparam(struct net_device *netdev,
struct ethtool_pauseparam *pause)
{
struct stmmac_priv *priv = netdev_priv(netdev);
struct phy_device *phy = priv->phydev;
int new_pause = FLOW_OFF;
int ret = 0;
spin_lock(&priv->lock);
if (pause->rx_pause)
new_pause |= FLOW_RX;
if (pause->tx_pause)
new_pause |= FLOW_TX;
priv->flow_ctrl = new_pause;
phy->autoneg = pause->autoneg;
if (phy->autoneg) {
if (netif_running(netdev))
ret = phy_start_aneg(phy);
} else
priv->hw->mac->flow_ctrl(priv->ioaddr, phy->duplex,
priv->flow_ctrl, priv->pause);
spin_unlock(&priv->lock);
return ret;
}
static void stmmac_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *dummy, u64 *data)
{
struct stmmac_priv *priv = netdev_priv(dev);
int i;
/* Update HW stats if supported */
priv->hw->dma->dma_diagnostic_fr(&dev->stats, (void *) &priv->xstats,
priv->ioaddr);
for (i = 0; i < STMMAC_STATS_LEN; i++) {
char *p = (char *)priv + stmmac_gstrings_stats[i].stat_offset;
data[i] = (stmmac_gstrings_stats[i].sizeof_stat ==
sizeof(u64)) ? (*(u64 *)p) : (*(u32 *)p);
}
}
static int stmmac_get_sset_count(struct net_device *netdev, int sset)
{
switch (sset) {
case ETH_SS_STATS:
return STMMAC_STATS_LEN;
default:
return -EOPNOTSUPP;
}
}
static void stmmac_get_strings(struct net_device *dev, u32 stringset, u8 *data)
{
int i;
u8 *p = data;
switch (stringset) {
case ETH_SS_STATS:
for (i = 0; i < STMMAC_STATS_LEN; i++) {
memcpy(p, stmmac_gstrings_stats[i].stat_string,
ETH_GSTRING_LEN);
p += ETH_GSTRING_LEN;
}
break;
default:
WARN_ON(1);
break;
}
}
/* Currently only support WOL through Magic packet. */
static void stmmac_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct stmmac_priv *priv = netdev_priv(dev);
spin_lock_irq(&priv->lock);
if (device_can_wakeup(priv->device)) {
wol->supported = WAKE_MAGIC | WAKE_UCAST;
wol->wolopts = priv->wolopts;
}
spin_unlock_irq(&priv->lock);
}
static int stmmac_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct stmmac_priv *priv = netdev_priv(dev);
u32 support = WAKE_MAGIC | WAKE_UCAST;
if (!device_can_wakeup(priv->device))
return -EINVAL;
if (wol->wolopts & ~support)
return -EINVAL;
if (wol->wolopts) {
pr_info("stmmac: wakeup enable\n");
device_set_wakeup_enable(priv->device, 1);
enable_irq_wake(dev->irq);
} else {
device_set_wakeup_enable(priv->device, 0);
disable_irq_wake(dev->irq);
}
spin_lock_irq(&priv->lock);
priv->wolopts = wol->wolopts;
spin_unlock_irq(&priv->lock);
return 0;
}
static struct ethtool_ops stmmac_ethtool_ops = {
.begin = stmmac_check_if_running,
.get_drvinfo = stmmac_ethtool_getdrvinfo,
.get_settings = stmmac_ethtool_getsettings,
.set_settings = stmmac_ethtool_setsettings,
.get_msglevel = stmmac_ethtool_getmsglevel,
.set_msglevel = stmmac_ethtool_setmsglevel,
.get_regs = stmmac_ethtool_gregs,
.get_regs_len = stmmac_ethtool_get_regs_len,
.get_link = ethtool_op_get_link,
.get_pauseparam = stmmac_get_pauseparam,
.set_pauseparam = stmmac_set_pauseparam,
.get_ethtool_stats = stmmac_get_ethtool_stats,
.get_strings = stmmac_get_strings,
.get_wol = stmmac_get_wol,
.set_wol = stmmac_set_wol,
.get_sset_count = stmmac_get_sset_count,
};
void stmmac_set_ethtool_ops(struct net_device *netdev)
{
SET_ETHTOOL_OPS(netdev, &stmmac_ethtool_ops);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,247 @@
/*******************************************************************************
STMMAC Ethernet Driver -- MDIO bus implementation
Provides Bus interface for MII registers
Copyright (C) 2007-2009 STMicroelectronics Ltd
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Author: Carl Shaw <carl.shaw@st.com>
Maintainer: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
#include <linux/mii.h>
#include <linux/phy.h>
#include <linux/slab.h>
#include <asm/io.h>
#include "stmmac.h"
#define MII_BUSY 0x00000001
#define MII_WRITE 0x00000002
/**
* stmmac_mdio_read
* @bus: points to the mii_bus structure
* @phyaddr: MII addr reg bits 15-11
* @phyreg: MII addr reg bits 10-6
* Description: it reads data from the MII register from within the phy device.
* For the 7111 GMAC, we must set the bit 0 in the MII address register while
* accessing the PHY registers.
* Fortunately, it seems this has no drawback for the 7109 MAC.
*/
static int stmmac_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg)
{
struct net_device *ndev = bus->priv;
struct stmmac_priv *priv = netdev_priv(ndev);
unsigned int mii_address = priv->hw->mii.addr;
unsigned int mii_data = priv->hw->mii.data;
int data;
u16 regValue = (((phyaddr << 11) & (0x0000F800)) |
((phyreg << 6) & (0x000007C0)));
regValue |= MII_BUSY | ((priv->plat->clk_csr & 7) << 2);
do {} while (((readl(priv->ioaddr + mii_address)) & MII_BUSY) == 1);
writel(regValue, priv->ioaddr + mii_address);
do {} while (((readl(priv->ioaddr + mii_address)) & MII_BUSY) == 1);
/* Read the data from the MII data register */
data = (int)readl(priv->ioaddr + mii_data);
return data;
}
/**
* stmmac_mdio_write
* @bus: points to the mii_bus structure
* @phyaddr: MII addr reg bits 15-11
* @phyreg: MII addr reg bits 10-6
* @phydata: phy data
* Description: it writes the data into the MII register from within the device.
*/
static int stmmac_mdio_write(struct mii_bus *bus, int phyaddr, int phyreg,
u16 phydata)
{
struct net_device *ndev = bus->priv;
struct stmmac_priv *priv = netdev_priv(ndev);
unsigned int mii_address = priv->hw->mii.addr;
unsigned int mii_data = priv->hw->mii.data;
u16 value =
(((phyaddr << 11) & (0x0000F800)) | ((phyreg << 6) & (0x000007C0)))
| MII_WRITE;
value |= MII_BUSY | ((priv->plat->clk_csr & 7) << 2);
/* Wait until any existing MII operation is complete */
do {} while (((readl(priv->ioaddr + mii_address)) & MII_BUSY) == 1);
/* Set the MII address register to write */
writel(phydata, priv->ioaddr + mii_data);
writel(value, priv->ioaddr + mii_address);
/* Wait until any existing MII operation is complete */
do {} while (((readl(priv->ioaddr + mii_address)) & MII_BUSY) == 1);
return 0;
}
/**
* stmmac_mdio_reset
* @bus: points to the mii_bus structure
* Description: reset the MII bus
*/
static int stmmac_mdio_reset(struct mii_bus *bus)
{
struct net_device *ndev = bus->priv;
struct stmmac_priv *priv = netdev_priv(ndev);
unsigned int mii_address = priv->hw->mii.addr;
if (priv->plat->mdio_bus_data->phy_reset) {
pr_debug("stmmac_mdio_reset: calling phy_reset\n");
priv->plat->mdio_bus_data->phy_reset(priv->plat->bsp_priv);
}
/* This is a workaround for problems with the STE101P PHY.
* It doesn't complete its reset until at least one clock cycle
* on MDC, so perform a dummy mdio read.
*/
writel(0, priv->ioaddr + mii_address);
return 0;
}
/**
* stmmac_mdio_register
* @ndev: net device structure
* Description: it registers the MII bus
*/
int stmmac_mdio_register(struct net_device *ndev)
{
int err = 0;
struct mii_bus *new_bus;
int *irqlist;
struct stmmac_priv *priv = netdev_priv(ndev);
struct stmmac_mdio_bus_data *mdio_bus_data = priv->plat->mdio_bus_data;
int addr, found;
if (!mdio_bus_data)
return 0;
new_bus = mdiobus_alloc();
if (new_bus == NULL)
return -ENOMEM;
if (mdio_bus_data->irqs)
irqlist = mdio_bus_data->irqs;
else
irqlist = priv->mii_irq;
new_bus->name = "STMMAC MII Bus";
new_bus->read = &stmmac_mdio_read;
new_bus->write = &stmmac_mdio_write;
new_bus->reset = &stmmac_mdio_reset;
snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", mdio_bus_data->bus_id);
new_bus->priv = ndev;
new_bus->irq = irqlist;
new_bus->phy_mask = mdio_bus_data->phy_mask;
new_bus->parent = priv->device;
err = mdiobus_register(new_bus);
if (err != 0) {
pr_err("%s: Cannot register as MDIO bus\n", new_bus->name);
goto bus_register_fail;
}
priv->mii = new_bus;
found = 0;
for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
struct phy_device *phydev = new_bus->phy_map[addr];
if (phydev) {
int act = 0;
char irq_num[4];
char *irq_str;
/*
* If an IRQ was provided to be assigned after
* the bus probe, do it here.
*/
if ((mdio_bus_data->irqs == NULL) &&
(mdio_bus_data->probed_phy_irq > 0)) {
irqlist[addr] = mdio_bus_data->probed_phy_irq;
phydev->irq = mdio_bus_data->probed_phy_irq;
}
/*
* If we're going to bind the MAC to this PHY bus,
* and no PHY number was provided to the MAC,
* use the one probed here.
*/
if ((priv->plat->bus_id == mdio_bus_data->bus_id) &&
(priv->plat->phy_addr == -1))
priv->plat->phy_addr = addr;
act = (priv->plat->bus_id == mdio_bus_data->bus_id) &&
(priv->plat->phy_addr == addr);
switch (phydev->irq) {
case PHY_POLL:
irq_str = "POLL";
break;
case PHY_IGNORE_INTERRUPT:
irq_str = "IGNORE";
break;
default:
sprintf(irq_num, "%d", phydev->irq);
irq_str = irq_num;
break;
}
pr_info("%s: PHY ID %08x at %d IRQ %s (%s)%s\n",
ndev->name, phydev->phy_id, addr,
irq_str, dev_name(&phydev->dev),
act ? " active" : "");
found = 1;
}
}
if (!found)
pr_warning("%s: No PHY found\n", ndev->name);
return 0;
bus_register_fail:
mdiobus_free(new_bus);
return err;
}
/**
* stmmac_mdio_unregister
* @ndev: net device structure
* Description: it unregisters the MII bus
*/
int stmmac_mdio_unregister(struct net_device *ndev)
{
struct stmmac_priv *priv = netdev_priv(ndev);
mdiobus_unregister(priv->mii);
priv->mii->priv = NULL;
mdiobus_free(priv->mii);
priv->mii = NULL;
return 0;
}

View File

@@ -0,0 +1,134 @@
/*******************************************************************************
STMMAC external timer support.
Copyright (C) 2007-2009 STMicroelectronics Ltd
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
#include <linux/kernel.h>
#include <linux/etherdevice.h>
#include "stmmac_timer.h"
static void stmmac_timer_handler(void *data)
{
struct net_device *dev = (struct net_device *)data;
stmmac_schedule(dev);
}
#define STMMAC_TIMER_MSG(timer, freq) \
printk(KERN_INFO "stmmac_timer: %s Timer ON (freq %dHz)\n", timer, freq);
#if defined(CONFIG_STMMAC_RTC_TIMER)
#include <linux/rtc.h>
static struct rtc_device *stmmac_rtc;
static rtc_task_t stmmac_task;
static void stmmac_rtc_start(unsigned int new_freq)
{
rtc_irq_set_freq(stmmac_rtc, &stmmac_task, new_freq);
rtc_irq_set_state(stmmac_rtc, &stmmac_task, 1);
}
static void stmmac_rtc_stop(void)
{
rtc_irq_set_state(stmmac_rtc, &stmmac_task, 0);
}
int stmmac_open_ext_timer(struct net_device *dev, struct stmmac_timer *tm)
{
stmmac_task.private_data = dev;
stmmac_task.func = stmmac_timer_handler;
stmmac_rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
if (stmmac_rtc == NULL) {
pr_err("open rtc device failed\n");
return -ENODEV;
}
rtc_irq_register(stmmac_rtc, &stmmac_task);
/* Periodic mode is not supported */
if ((rtc_irq_set_freq(stmmac_rtc, &stmmac_task, tm->freq) < 0)) {
pr_err("set periodic failed\n");
rtc_irq_unregister(stmmac_rtc, &stmmac_task);
rtc_class_close(stmmac_rtc);
return -1;
}
STMMAC_TIMER_MSG(CONFIG_RTC_HCTOSYS_DEVICE, tm->freq);
tm->timer_start = stmmac_rtc_start;
tm->timer_stop = stmmac_rtc_stop;
return 0;
}
int stmmac_close_ext_timer(void)
{
rtc_irq_set_state(stmmac_rtc, &stmmac_task, 0);
rtc_irq_unregister(stmmac_rtc, &stmmac_task);
rtc_class_close(stmmac_rtc);
return 0;
}
#elif defined(CONFIG_STMMAC_TMU_TIMER)
#include <linux/clk.h>
#define TMU_CHANNEL "tmu2_clk"
static struct clk *timer_clock;
static void stmmac_tmu_start(unsigned int new_freq)
{
clk_set_rate(timer_clock, new_freq);
clk_enable(timer_clock);
}
static void stmmac_tmu_stop(void)
{
clk_disable(timer_clock);
}
int stmmac_open_ext_timer(struct net_device *dev, struct stmmac_timer *tm)
{
timer_clock = clk_get(NULL, TMU_CHANNEL);
if (timer_clock == NULL)
return -1;
if (tmu2_register_user(stmmac_timer_handler, (void *)dev) < 0) {
timer_clock = NULL;
return -1;
}
STMMAC_TIMER_MSG("TMU2", tm->freq);
tm->timer_start = stmmac_tmu_start;
tm->timer_stop = stmmac_tmu_stop;
return 0;
}
int stmmac_close_ext_timer(void)
{
clk_disable(timer_clock);
tmu2_unregister_user();
clk_put(timer_clock);
return 0;
}
#endif

View File

@@ -0,0 +1,42 @@
/*******************************************************************************
STMMAC external timer Header File.
Copyright (C) 2007-2009 STMicroelectronics Ltd
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
struct stmmac_timer {
void (*timer_start) (unsigned int new_freq);
void (*timer_stop) (void);
unsigned int freq;
unsigned int enable;
};
/* Open the HW timer device and return 0 in case of success */
int stmmac_open_ext_timer(struct net_device *dev, struct stmmac_timer *tm);
/* Stop the timer and release it */
int stmmac_close_ext_timer(void);
/* Function used for scheduling task within the stmmac */
void stmmac_schedule(struct net_device *dev);
#if defined(CONFIG_STMMAC_TMU_TIMER)
extern int tmu2_register_user(void *fnt, void *data);
extern void tmu2_unregister_user(void);
#endif