Commit 47dd7a54 authored by Giuseppe Cavallaro's avatar Giuseppe Cavallaro Committed by David S. Miller

net: add support for STMicroelectronics Ethernet controllers.

This is the driver for the ST MAC 10/100/1000 on-chip Ethernet
controllers (Synopsys IP blocks).

Driver documentation:
 o http://stlinux.com/drupal/kernel/network/stmmac
Revisions:
 o http://stlinux.com/drupal/kernel/network/stmmac-driver-revisions
Performances:
 o http://stlinux.com/drupal/benchmarks/networking/stmmacSigned-off-by: default avatarGiuseppe Cavallaro <peppe.cavallaro@st.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 47a01a0c
...@@ -2483,6 +2483,8 @@ config S6GMAC ...@@ -2483,6 +2483,8 @@ config S6GMAC
To compile this driver as a module, choose M here. The module To compile this driver as a module, choose M here. The module
will be called s6gmac. will be called s6gmac.
source "drivers/net/stmmac/Kconfig"
endif # NETDEV_1000 endif # NETDEV_1000
# #
......
...@@ -100,6 +100,7 @@ obj-$(CONFIG_VIA_VELOCITY) += via-velocity.o ...@@ -100,6 +100,7 @@ obj-$(CONFIG_VIA_VELOCITY) += via-velocity.o
obj-$(CONFIG_ADAPTEC_STARFIRE) += starfire.o obj-$(CONFIG_ADAPTEC_STARFIRE) += starfire.o
obj-$(CONFIG_RIONET) += rionet.o obj-$(CONFIG_RIONET) += rionet.o
obj-$(CONFIG_SH_ETH) += sh_eth.o obj-$(CONFIG_SH_ETH) += sh_eth.o
obj-$(CONFIG_STMMAC_ETH) += stmmac/
# #
# end link order section # end link order section
......
config STMMAC_ETH
tristate "STMicroelectronics 10/100/1000 Ethernet driver"
select MII
select PHYLIB
depends on NETDEVICES && CPU_SUBTYPE_ST40
help
This is the driver for the ST MAC 10/100/1000 on-chip Ethernet
controllers. ST Ethernet IPs are built around a Synopsys IP Core.
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
help
Use an external timer for mitigating the number of network
interrupts.
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
obj-$(CONFIG_STMMAC_ETH) += stmmac.o
stmmac-$(CONFIG_STMMAC_TIMER) += stmmac_timer.o
stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o \
mac100.o gmac.o $(stmmac-y)
/*******************************************************************************
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 "descs.h"
#include <linux/io.h>
/* *********************************************
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 Interrupt Enable register defines
* **************************************/
/**** 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)
/**** 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 */
/* Other defines */
#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)
/* DMA STORE-AND-FORWARD Operation Mode */
#define SF_DMA_MODE 1
#define HW_CSUM 1
#define NO_HW_CSUM 0
/* 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_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_lenght;
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;
};
/* GMAC core can compute the checksums in HW. */
enum rx_frame_status {
good_frame = 0,
discard_frame = 1,
csum_none = 2,
};
static inline void stmmac_set_mac_addr(unsigned long 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);
return;
}
static inline void stmmac_get_mac_addr(unsigned long 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;
return;
}
struct stmmac_ops {
/* MAC core initialization */
void (*core_init) (unsigned long ioaddr) ____cacheline_aligned;
/* DMA core initialization */
int (*dma_init) (unsigned long ioaddr, int pbl, u32 dma_tx, u32 dma_rx);
/* Dump MAC registers */
void (*dump_mac_regs) (unsigned long ioaddr);
/* Dump DMA registers */
void (*dump_dma_regs) (unsigned long ioaddr);
/* Set tx/rx threshold in the csr6 register
* An invalid value enables the store-and-forward mode */
void (*dma_mode) (unsigned long ioaddr, int txmode, int rxmode);
/* To track extra statistic (if supported) */
void (*dma_diagnostic_fr) (void *data, struct stmmac_extra_stats *x,
unsigned long ioaddr);
/* RX descriptor ring initialization */
void (*init_rx_desc) (struct dma_desc *p, unsigned int ring_size,
int disable_rx_ic);
/* 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, unsigned long ioaddr);
/* Get the buffer size from the descriptor */
int (*get_tx_len) (struct dma_desc *p);
/* Handle extra events on specific interrupts hw dependent */
void (*host_irq_status) (unsigned long ioaddr);
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);
/* Multicast filter setting */
void (*set_filter) (struct net_device *dev);
/* Flow control setting */
void (*flow_ctrl) (unsigned long ioaddr, unsigned int duplex,
unsigned int fc, unsigned int pause_time);
/* Set power management mode (e.g. magic frame) */
void (*pmt) (unsigned long ioaddr, unsigned long mode);
/* Set/Get Unicast MAC addresses */
void (*set_umac_addr) (unsigned long ioaddr, unsigned char *addr,
unsigned int reg_n);
void (*get_umac_addr) (unsigned long 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 hw_cap {
unsigned int version; /* Core Version register (GMAC) */
unsigned int pmt; /* Power-Down mode (GMAC) */
struct mac_link link;
struct mii_regs mii;
};
struct mac_device_info {
struct hw_cap hw;
struct stmmac_ops *ops;
};
struct mac_device_info *gmac_setup(unsigned long addr);
struct mac_device_info *mac100_setup(unsigned long addr);
/*******************************************************************************
Header File to describe the DMA descriptors
Use enhanced descriptors in case of GMAC 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 */
};
/*******************************************************************************
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.
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>
#include <linux/crc32.h>
#include <linux/mii.h>
#include <linux/phy.h>
#include "stmmac.h"
#include "gmac.h"
#undef GMAC_DEBUG
/*#define GMAC_DEBUG*/
#undef FRAME_FILTER_DEBUG
/*#define FRAME_FILTER_DEBUG*/
#ifdef GMAC_DEBUG
#define DBG(fmt, args...) printk(fmt, ## args)
#else
#define DBG(fmt, args...) do { } while (0)
#endif
static void gmac_dump_regs(unsigned long ioaddr)
{
int i;
pr_info("\t----------------------------------------------\n"
"\t GMAC registers (base addr = 0x%8x)\n"
"\t----------------------------------------------\n",
(unsigned int)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));
}
return;
}
static int gmac_dma_init(unsigned long ioaddr, int pbl, u32 dma_tx, u32 dma_rx)
{
u32 value = readl(ioaddr + DMA_BUS_MODE);
/* DMA SW reset */
value |= DMA_BUS_MODE_SFT_RESET;
writel(value, ioaddr + DMA_BUS_MODE);
do {} while ((readl(ioaddr + DMA_BUS_MODE) & DMA_BUS_MODE_SFT_RESET));
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;
}
/* Transmit FIFO flush operation */
static void gmac_flush_tx_fifo(unsigned long ioaddr)
{
u32 csr6 = readl(ioaddr + DMA_CONTROL);
writel((csr6 | DMA_CONTROL_FTF), ioaddr + DMA_CONTROL);
do {} while ((readl(ioaddr + DMA_CONTROL) & DMA_CONTROL_FTF));
}
static void gmac_dma_operation_mode(unsigned long ioaddr, int txmode,
int rxmode)
{
u32 csr6 = readl(ioaddr + DMA_CONTROL);
if (txmode == SF_DMA_MODE) {
DBG(KERN_DEBUG "GMAC: enabling 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 {
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 threashold */
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) {
DBG(KERN_DEBUG "GMAC: enabling RX store and forward mode\n");
csr6 |= DMA_CONTROL_RSF;
} else {
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);
return;
}
/* Not yet implemented --- no RMON module */
static void gmac_dma_diagnostic_fr(void *data, struct stmmac_extra_stats *x,
unsigned long ioaddr)
{
return;
}
static void gmac_dump_dma_regs(unsigned long 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));
}
}
return;
}
static int gmac_get_tx_frame_status(void *data, struct stmmac_extra_stats *x,
struct dma_desc *p, unsigned long ioaddr)
{
int ret = 0;
struct net_device_stats *stats = (struct net_device_stats *)data;
if (unlikely(p->des01.etx.error_summary)) {
DBG(KERN_ERR "GMAC TX error... 0x%08x\n", p->des01.etx);
if (unlikely(p->des01.etx.jabber_timeout)) {
DBG(KERN_ERR "\tjabber_timeout error\n");
x->tx_jabber++;
}
if (unlikely(p->des01.etx.frame_flushed)) {
DBG(KERN_ERR "\tframe_flushed error\n");
x->tx_frame_flushed++;
gmac_flush_tx_fifo(ioaddr);
}
if (unlikely(p->des01.etx.loss_carrier)) {
DBG(KERN_ERR "\tloss_carrier error\n");
x->tx_losscarrier++;
stats->tx_carrier_errors++;
}
if (unlikely(p->des01.etx.no_carrier)) {
DBG(KERN_ERR "\tno_carrier error\n");
x->tx_carrier++;
stats->tx_carrier_errors++;
}
if (unlikely(p->des01.etx.late_collision)) {
DBG(KERN_ERR "\tlate_collision error\n");
stats->collisions += p->des01.etx.collision_count;
}
if (unlikely(p->des01.etx.excessive_collisions)) {
DBG(KERN_ERR "\texcessive_collisions\n");
stats->collisions += p->des01.etx.collision_count;
}
if (unlikely(p->des01.etx.excessive_deferral)) {
DBG(KERN_INFO "\texcessive tx_deferral\n");
x->tx_deferred++;
}
if (unlikely(p->des01.etx.underflow_error)) {
DBG(KERN_ERR "\tunderflow error\n");
gmac_flush_tx_fifo(ioaddr);
x->tx_underflow++;
}
if (unlikely(p->des01.etx.ip_header_error)) {
DBG(KERN_ERR "\tTX IP header csum error\n");
x->tx_ip_header_error++;
}
if (unlikely(p->des01.etx.payload_error)) {
DBG(KERN_ERR "\tAddr/Payload csum error\n");
x->tx_payload_error++;
gmac_flush_tx_fifo(ioaddr);
}
ret = -1;
}
if (unlikely(p->des01.etx.deferred)) {
DBG(KERN_INFO "GMAC TX status: tx deferred\n");
x->tx_deferred++;
}
#ifdef STMMAC_VLAN_TAG_USED
if (p->des01.etx.vlan_frame) {
DBG(KERN_INFO "GMAC TX status: VLAN frame\n");
x->tx_vlan++;
}
#endif
return ret;
}
static int gmac_get_tx_len(struct dma_desc *p)
{
return p->des01.etx.buffer1_size;
}
static int gmac_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 (lenght < 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) {
DBG(KERN_INFO "RX Des0 status: IEEE 802.3 Type frame.\n");
ret = good_frame;
} else if (status == 0x4) {
DBG(KERN_INFO "RX Des0 status: IPv4/6 No CSUM errorS.\n");
ret = good_frame;
} else if (status == 0x5) {
DBG(KERN_ERR "RX Des0 status: IPv4/6 Payload Error.\n");
ret = csum_none;
} else if (status == 0x6) {
DBG(KERN_ERR "RX Des0 status: IPv4/6 Header Error.\n");
ret = csum_none;
} else if (status == 0x7) {
DBG(KERN_ERR
"RX Des0 status: IPv4/6 Header and Payload Error.\n");
ret = csum_none;
} else if (status == 0x1) {
DBG(KERN_ERR
"RX Des0 status: IPv4/6 unsupported IP PAYLOAD.\n");
ret = discard_frame;
} else if (status == 0x3) {
DBG(KERN_ERR "RX Des0 status: No IPv4, IPv6 frame.\n");
ret = discard_frame;
}
return ret;
}
static int gmac_get_rx_frame_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)) {
DBG(KERN_ERR "GMAC RX Error Summary... 0x%08x\n", p->des01.erx);
if (unlikely(p->des01.erx.descriptor_error)) {
DBG(KERN_ERR "\tdescriptor error\n");
x->rx_desc++;
stats->rx_length_errors++;
}
if (unlikely(p->des01.erx.overflow_error)) {
DBG(KERN_ERR "\toverflow error\n");
x->rx_gmac_overflow++;
}
if (unlikely(p->des01.erx.ipc_csum_error))
DBG(KERN_ERR "\tIPC Csum Error/Giant frame\n");
if (unlikely(p->des01.erx.late_collision)) {
DBG(KERN_ERR "\tlate_collision error\n");
stats->collisions++;
stats->collisions++;
}
if (unlikely(p->des01.erx.receive_watchdog)) {
DBG(KERN_ERR "\treceive_watchdog error\n");
x->rx_watchdog++;
}
if (unlikely(p->des01.erx.error_gmii)) {
DBG(KERN_ERR "\tReceive Error\n");
x->rx_mii++;
}
if (unlikely(p->des01.erx.crc_error)) {
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 = gmac_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)) {
DBG(KERN_ERR "GMAC RX: dribbling error\n");
ret = discard_frame;
}
if (unlikely(p->des01.erx.sa_filter_fail)) {
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)) {
DBG(KERN_ERR "GMAC RX : Destination Address filter fail\n");
x->da_rx_filter_fail++;
ret = discard_frame;
}
if (unlikely(p->des01.erx.length_error)) {
DBG(KERN_ERR "GMAC RX: length_error error\n");
x->rx_lenght++;
ret = discard_frame;
}
#ifdef STMMAC_VLAN_TAG_USED
if (p->des01.erx.vlan_tag) {
DBG(KERN_INFO "GMAC RX: VLAN frame tagged\n");
x->rx_vlan++;
}
#endif
return ret;
}
static void gmac_irq_status(unsigned long 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))
DBG(KERN_DEBUG "GMAC: MMC tx interrupt: 0x%08x\n",
readl(ioaddr + GMAC_MMC_TX_INTR));
if (unlikely(intr_status & mmc_rx_irq))
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))
DBG(KERN_DEBUG "GMAC: MMC rx csum offload: 0x%08x\n",
readl(ioaddr + GMAC_MMC_RX_CSUM_OFFLOAD));
if (unlikely(intr_status & pmt_irq)) {
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);
}
return;
}
static void gmac_core_init(unsigned long 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
return;
}
static void gmac_set_umac_addr(unsigned long 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 gmac_get_umac_addr(unsigned long 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 gmac_set_filter(struct net_device *dev)
{
unsigned long ioaddr = dev->base_addr;
unsigned int value = 0;
DBG(KERN_INFO "%s: # mcasts %d, # unicast %d\n",
__func__, dev->mc_count, dev->uc_count);
if (dev->flags & IFF_PROMISC)
value = GMAC_FRAME_FILTER_PR;
else if ((dev->mc_count > 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 (dev->mc_count > 0) {
int i;
u32 mc_filter[2];
struct dev_mc_list *mclist;
/* Hash filter for multicast */
value = GMAC_FRAME_FILTER_HMC;
memset(mc_filter, 0, sizeof(mc_filter));
for (i = 0, mclist = dev->mc_list;
mclist && i < dev->mc_count; i++, mclist = mclist->next) {
/* 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, mclist->dmi_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 (dev->uc_count > GMAC_MAX_UNICAST_ADDRESSES)
/* Switch to promiscuous mode is more than 16 addrs
are required */
value |= GMAC_FRAME_FILTER_PR;
else {
int i;
struct dev_addr_list *uc_ptr = dev->uc_list;
for (i = 0; i < dev->uc_count; i++) {
gmac_set_umac_addr(ioaddr, uc_ptr->da_addr,
i + 1);
DBG(KERN_INFO "\t%d "
"- Unicast addr %02x:%02x:%02x:%02x:%02x:"
"%02x\n", i + 1,
uc_ptr->da_addr[0], uc_ptr->da_addr[1],
uc_ptr->da_addr[2], uc_ptr->da_addr[3],
uc_ptr->da_addr[4], uc_ptr->da_addr[5]);
uc_ptr = uc_ptr->next;
}
}
#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);
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));
return;
}
static void gmac_flow_ctrl(unsigned long ioaddr, unsigned int duplex,
unsigned int fc, unsigned int pause_time)
{
unsigned int flow = 0;
DBG(KERN_DEBUG "GMAC Flow-Control:\n");
if (fc & FLOW_RX) {
DBG(KERN_DEBUG "\tReceive Flow-Control ON\n");
flow |= GMAC_FLOW_CTRL_RFE;
}
if (fc & FLOW_TX) {
DBG(KERN_DEBUG "\tTransmit Flow-Control ON\n");
flow |= GMAC_FLOW_CTRL_TFE;
}
if (duplex) {
DBG(KERN_DEBUG "\tduplex mode: pause time: %d\n", pause_time);
flow |= (pause_time << GMAC_FLOW_CTRL_PT_SHIFT);
}
writel(flow, ioaddr + GMAC_FLOW_CTRL);
return;
}
static void gmac_pmt(unsigned long ioaddr, unsigned long mode)
{
unsigned int pmt = 0;
if (mode == WAKE_MAGIC) {
DBG(KERN_DEBUG "GMAC: WOL Magic frame\n");
pmt |= power_down | magic_pkt_en;
} else if (mode == WAKE_UCAST) {
DBG(KERN_DEBUG "GMAC: WOL on global unicast\n");
pmt |= global_unicast;
}
writel(pmt, ioaddr + GMAC_PMT);
return;
}
static void gmac_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++;
}
return;
}
static void gmac_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++;
}
return;
}
static int gmac_get_tx_owner(struct dma_desc *p)
{
return p->des01.etx.own;
}
static int gmac_get_rx_owner(struct dma_desc *p)
{
return p->des01.erx.own;
}
static void gmac_set_tx_owner(struct dma_desc *p)
{
p->des01.etx.own = 1;
}
static void gmac_set_rx_owner(struct dma_desc *p)
{
p->des01.erx.own = 1;
}
static int gmac_get_tx_ls(struct dma_desc *p)
{
return p->des01.etx.last_segment;
}
static void gmac_release_tx_desc(struct dma_desc *p)
{
int ter = p->des01.etx.end_ring;
memset(p, 0, sizeof(struct dma_desc));
p->des01.etx.end_ring = ter;
return;
}
static void gmac_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 gmac_clear_tx_ic(struct dma_desc *p)
{
p->des01.etx.interrupt = 0;
}
static void gmac_close_tx_desc(struct dma_desc *p)
{
p->des01.etx.last_segment = 1;
p->des01.etx.interrupt = 1;
}
static int gmac_get_rx_frame_len(struct dma_desc *p)
{
return p->des01.erx.frame_length;
}
struct stmmac_ops gmac_driver = {
.core_init = gmac_core_init,
.dump_mac_regs = gmac_dump_regs,
.dma_init = gmac_dma_init,
.dump_dma_regs = gmac_dump_dma_regs,
.dma_mode = gmac_dma_operation_mode,
.dma_diagnostic_fr = gmac_dma_diagnostic_fr,
.tx_status = gmac_get_tx_frame_status,
.rx_status = gmac_get_rx_frame_status,
.get_tx_len = gmac_get_tx_len,
.set_filter = gmac_set_filter,
.flow_ctrl = gmac_flow_ctrl,
.pmt = gmac_pmt,
.init_rx_desc = gmac_init_rx_desc,
.init_tx_desc = gmac_init_tx_desc,
.get_tx_owner = gmac_get_tx_owner,
.get_rx_owner = gmac_get_rx_owner,
.release_tx_desc = gmac_release_tx_desc,
.prepare_tx_desc = gmac_prepare_tx_desc,
.clear_tx_ic = gmac_clear_tx_ic,
.close_tx_desc = gmac_close_tx_desc,
.get_tx_ls = gmac_get_tx_ls,
.set_tx_owner = gmac_set_tx_owner,
.set_rx_owner = gmac_set_rx_owner,
.get_rx_frame_len = gmac_get_rx_frame_len,
.host_irq_status = gmac_irq_status,
.set_umac_addr = gmac_set_umac_addr,
.get_umac_addr = gmac_get_umac_addr,
};
struct mac_device_info *gmac_setup(unsigned long ioaddr)
{
struct mac_device_info *mac;
u32 uid = readl(ioaddr + GMAC_VERSION);
pr_info("\tGMAC - user ID: 0x%x, Synopsys ID: 0x%x\n",
((uid & 0x0000ff00) >> 8), (uid & 0x000000ff));
mac = kzalloc(sizeof(const struct mac_device_info), GFP_KERNEL);
mac->ops = &gmac_driver;
mac->hw.pmt = PMT_SUPPORTED;
mac->hw.link.port = GMAC_CONTROL_PS;
mac->hw.link.duplex = GMAC_CONTROL_DM;
mac->hw.link.speed = GMAC_CONTROL_FES;
mac->hw.mii.addr = GMAC_MII_ADDR;
mac->hw.mii.data = GMAC_MII_DATA;
return mac;
}
/*******************************************************************************
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 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 gmac_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 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_IPC | 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 */
/* Theshold 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,
};
/* Theshold 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 */
#define DMA_CONTROL_FTF 0x00100000 /* Flush transmit FIFO */
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
/*******************************************************************************
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.
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>
#include <linux/crc32.h>
#include <linux/mii.h>
#include <linux/phy.h>
#include "common.h"
#include "mac100.h"
#undef MAC100_DEBUG
/*#define MAC100_DEBUG*/
#ifdef MAC100_DEBUG
#define DBG(fmt, args...) printk(fmt, ## args)
#else
#define DBG(fmt, args...) do { } while (0)
#endif
static void mac100_core_init(unsigned long 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
return;
}
static void mac100_dump_mac_regs(unsigned long ioaddr)
{
pr_info("\t----------------------------------------------\n"
"\t MAC100 CSR (base addr = 0x%8x)\n"
"\t----------------------------------------------\n",
(unsigned int)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));
return;
}
static int mac100_dma_init(unsigned long ioaddr, int pbl, u32 dma_tx,
u32 dma_rx)
{
u32 value = readl(ioaddr + DMA_BUS_MODE);
/* DMA SW reset */
value |= DMA_BUS_MODE_SFT_RESET;
writel(value, ioaddr + DMA_BUS_MODE);
do {} while ((readl(ioaddr + DMA_BUS_MODE) & DMA_BUS_MODE_SFT_RESET));
/* 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 mac100_dma_operation_mode(unsigned long 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);
return;
}
static void mac100_dump_dma_regs(unsigned long ioaddr)
{
int i;
DBG(KERN_DEBUG "MAC100 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));
DBG(KERN_DEBUG "\t CSR20 (offset 0x%x): 0x%08x\n",
DMA_CUR_TX_BUF_ADDR, readl(ioaddr + DMA_CUR_TX_BUF_ADDR));
DBG(KERN_DEBUG "\t CSR21 (offset 0x%x): 0x%08x\n",
DMA_CUR_RX_BUF_ADDR, readl(ioaddr + DMA_CUR_RX_BUF_ADDR));
return;
}
/* DMA controller has two counters to track the number of
the receive missed frames. */
static void mac100_dma_diagnostic_fr(void *data, struct stmmac_extra_stats *x,
unsigned long 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;
}
}
return;
}
static int mac100_get_tx_frame_status(void *data, struct stmmac_extra_stats *x,
struct dma_desc *p, unsigned long 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 mac100_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 becasue the device
* is not able to compute the csum in HW. */
static int mac100_get_rx_frame_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("mac100 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_lenght++;
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 mac100_irq_status(unsigned long ioaddr)
{
return;
}
static void mac100_set_umac_addr(unsigned long ioaddr, unsigned char *addr,
unsigned int reg_n)
{
stmmac_set_mac_addr(ioaddr, addr, MAC_ADDR_HIGH, MAC_ADDR_LOW);
}
static void mac100_get_umac_addr(unsigned long ioaddr, unsigned char *addr,
unsigned int reg_n)
{
stmmac_get_mac_addr(ioaddr, addr, MAC_ADDR_HIGH, MAC_ADDR_LOW);
}
static void mac100_set_filter(struct net_device *dev)
{
unsigned long ioaddr = 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 ((dev->mc_count > 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 (dev->mc_count == 0) { /* no multicast */
value &= ~(MAC_CONTROL_PM | MAC_CONTROL_PR | MAC_CONTROL_IF |
MAC_CONTROL_HO | MAC_CONTROL_HP);
} else {
int i;
u32 mc_filter[2];
struct dev_mc_list *mclist;
/* 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));
for (i = 0, mclist = dev->mc_list;
mclist && i < dev->mc_count; i++, mclist = mclist->next) {
/* 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, mclist->dmi_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);
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));
return;
}
static void mac100_flow_ctrl(unsigned long 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);
return;
}
/* No PMT module supported in our SoC for the Ethernet Controller. */
static void mac100_pmt(unsigned long ioaddr, unsigned long mode)
{
return;
}
static void mac100_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++;
}
return;
}
static void mac100_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++;
}
return;
}
static int mac100_get_tx_owner(struct dma_desc *p)
{
return p->des01.tx.own;
}
static int mac100_get_rx_owner(struct dma_desc *p)
{
return p->des01.rx.own;
}
static void mac100_set_tx_owner(struct dma_desc *p)
{
p->des01.tx.own = 1;
}
static void mac100_set_rx_owner(struct dma_desc *p)
{
p->des01.rx.own = 1;
}
static int mac100_get_tx_ls(struct dma_desc *p)
{
return p->des01.tx.last_segment;
}
static void mac100_release_tx_desc(struct dma_desc *p)
{
int ter = p->des01.tx.end_ring;
/* clean field used within the xmit */
p->des01.tx.first_segment = 0;
p->des01.tx.last_segment = 0;
p->des01.tx.buffer1_size = 0;
/* clean status reported */
p->des01.tx.error_summary = 0;
p->des01.tx.underflow_error = 0;
p->des01.tx.no_carrier = 0;
p->des01.tx.loss_carrier = 0;
p->des01.tx.excessive_deferral = 0;
p->des01.tx.excessive_collisions = 0;
p->des01.tx.late_collision = 0;
p->des01.tx.heartbeat_fail = 0;
p->des01.tx.deferred = 0;
/* set termination field */
p->des01.tx.end_ring = ter;
return;
}
static void mac100_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 mac100_clear_tx_ic(struct dma_desc *p)
{
p->des01.tx.interrupt = 0;
}
static void mac100_close_tx_desc(struct dma_desc *p)
{
p->des01.tx.last_segment = 1;
p->des01.tx.interrupt = 1;
}
static int mac100_get_rx_frame_len(struct dma_desc *p)
{
return p->des01.rx.frame_length;
}
struct stmmac_ops mac100_driver = {
.core_init = mac100_core_init,
.dump_mac_regs = mac100_dump_mac_regs,
.dma_init = mac100_dma_init,
.dump_dma_regs = mac100_dump_dma_regs,
.dma_mode = mac100_dma_operation_mode,
.dma_diagnostic_fr = mac100_dma_diagnostic_fr,
.tx_status = mac100_get_tx_frame_status,
.rx_status = mac100_get_rx_frame_status,
.get_tx_len = mac100_get_tx_len,
.set_filter = mac100_set_filter,
.flow_ctrl = mac100_flow_ctrl,
.pmt = mac100_pmt,
.init_rx_desc = mac100_init_rx_desc,
.init_tx_desc = mac100_init_tx_desc,
.get_tx_owner = mac100_get_tx_owner,
.get_rx_owner = mac100_get_rx_owner,
.release_tx_desc = mac100_release_tx_desc,
.prepare_tx_desc = mac100_prepare_tx_desc,
.clear_tx_ic = mac100_clear_tx_ic,
.close_tx_desc = mac100_close_tx_desc,
.get_tx_ls = mac100_get_tx_ls,
.set_tx_owner = mac100_set_tx_owner,
.set_rx_owner = mac100_set_rx_owner,
.get_rx_frame_len = mac100_get_rx_frame_len,
.host_irq_status = mac100_irq_status,
.set_umac_addr = mac100_set_umac_addr,
.get_umac_addr = mac100_get_umac_addr,
};
struct mac_device_info *mac100_setup(unsigned long ioaddr)
{
struct mac_device_info *mac;
mac = kzalloc(sizeof(const struct mac_device_info), GFP_KERNEL);
pr_info("\tMAC 10/100\n");
mac->ops = &mac100_driver;
mac->hw.pmt = PMT_NOT_SUPPORTED;
mac->hw.link.port = MAC_CONTROL_PS;
mac->hw.link.duplex = MAC_CONTROL_F;
mac->hw.link.speed = 0;
mac->hw.mii.addr = MAC_MII_ADDR;
mac->hw.mii.data = MAC_MII_DATA;
return mac;
}
/*******************************************************************************
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>
*******************************************************************************/
/*----------------------------------------------------------------------------
* 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 */
/*******************************************************************************
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 "Oct_09"
#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
#define STMMAC_VLAN_TAG_USED
#include <linux/if_vlan.h>
#endif
#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_coe;
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;
int is_gmac;
dma_addr_t dma_rx_phy;
unsigned int dma_rx_size;
int rx_csum;
unsigned int dma_buf_sz;
struct device *device;
struct mac_device_info *mac_type;
struct stmmac_extra_stats xstats;
struct napi_struct napi;
phy_interface_t phy_interface;
int pbl;
int bus_id;
int phy_addr;
int phy_mask;
int (*phy_reset) (void *priv);
void (*fix_mac_speed) (void *priv, unsigned int speed);
void *bsp_priv;
int phy_irq;
struct phy_device *phydev;
int oldlink;
int speed;
int oldduplex;
unsigned int flow_ctrl;
unsigned int pause;
struct mii_bus *mii;
u32 msg_enable;
spinlock_t lock;
int wolopts;
int wolenabled;
int shutdown;
#ifdef CONFIG_STMMAC_TIMER
struct stmmac_timer *tm;
#endif
#ifdef STMMAC_VLAN_TAG_USED
struct vlan_group *vlgrp;
#endif
};
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);
/*******************************************************************************
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/mii.h>
#include <linux/phy.h>
#include "stmmac.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_lenght),
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)
void stmmac_ethtool_getdrvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
struct stmmac_priv *priv = netdev_priv(dev);
if (!priv->is_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;
return;
}
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;
}
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;
}
u32 stmmac_ethtool_getmsglevel(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
return priv->msg_enable;
}
void stmmac_ethtool_setmsglevel(struct net_device *dev, u32 level)
{
struct stmmac_priv *priv = netdev_priv(dev);
priv->msg_enable = level;
}
int stmmac_check_if_running(struct net_device *dev)
{
if (!netif_running(dev))
return -EBUSY;
return 0;
}
int stmmac_ethtool_get_regs_len(struct net_device *dev)
{
return REG_SPACE_SIZE;
}
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->is_gmac) {
/* MAC registers */
for (i = 0; i < 12; i++)
reg_space[i] = readl(dev->base_addr + (i * 4));
/* DMA registers */
for (i = 0; i < 9; i++)
reg_space[i + 12] =
readl(dev->base_addr + (DMA_BUS_MODE + (i * 4)));
reg_space[22] = readl(dev->base_addr + DMA_CUR_TX_BUF_ADDR);
reg_space[23] = readl(dev->base_addr + DMA_CUR_RX_BUF_ADDR);
} else {
/* MAC registers */
for (i = 0; i < 55; i++)
reg_space[i] = readl(dev->base_addr + (i * 4));
/* DMA registers */
for (i = 0; i < 22; i++)
reg_space[i + 55] =
readl(dev->base_addr + (DMA_BUS_MODE + (i * 4)));
}
return;
}
int stmmac_ethtool_set_tx_csum(struct net_device *netdev, u32 data)
{
if (data)
netdev->features |= NETIF_F_HW_CSUM;
else
netdev->features &= ~NETIF_F_HW_CSUM;
return 0;
}
u32 stmmac_ethtool_get_rx_csum(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
return priv->rx_csum;
}
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);
return;
}
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;
if (phy->autoneg) {
if (netif_running(netdev)) {
struct ethtool_cmd cmd;
/* auto-negotiation automatically restarted */
cmd.cmd = ETHTOOL_NWAY_RST;
cmd.supported = phy->supported;
cmd.advertising = phy->advertising;
cmd.autoneg = phy->autoneg;
cmd.speed = phy->speed;
cmd.duplex = phy->duplex;
cmd.phy_address = phy->addr;
ret = phy_ethtool_sset(phy, &cmd);
}
} else {
unsigned long ioaddr = netdev->base_addr;
priv->mac_type->ops->flow_ctrl(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);
unsigned long ioaddr = dev->base_addr;
int i;
/* Update HW stats if supported */
priv->mac_type->ops->dma_diagnostic_fr(&dev->stats, &priv->xstats,
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);
}
return;
}
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;
}
return;
}
/* 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 (priv->wolenabled == PMT_SUPPORTED) {
wol->supported = WAKE_MAGIC;
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;
if (priv->wolenabled == PMT_NOT_SUPPORTED)
return -EINVAL;
if (wol->wolopts & ~support)
return -EINVAL;
if (wol->wolopts == 0)
device_set_wakeup_enable(priv->device, 0);
else
device_set_wakeup_enable(priv->device, 1);
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_rx_csum = stmmac_ethtool_get_rx_csum,
.get_tx_csum = ethtool_op_get_tx_csum,
.set_tx_csum = stmmac_ethtool_set_tx_csum,
.get_sg = ethtool_op_get_sg,
.set_sg = ethtool_op_set_sg,
.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,
#ifdef NETIF_F_TSO
.get_tso = ethtool_op_get_tso,
.set_tso = ethtool_op_set_tso,
#endif
};
void stmmac_set_ethtool_ops(struct net_device *netdev)
{
SET_ETHTOOL_OPS(netdev, &stmmac_ethtool_ops);
}
/*******************************************************************************
This is the driver for the ST MAC 10/100/1000 on-chip Ethernet controllers.
ST Ethernet IPs are built around a Synopsys IP Core.
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>
Documentation available at:
http://www.stlinux.com
Support available at:
https://bugzilla.stlinux.com/
*******************************************************************************/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/platform_device.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/skbuff.h>
#include <linux/ethtool.h>
#include <linux/if_ether.h>
#include <linux/crc32.h>
#include <linux/mii.h>
#include <linux/phy.h>
#include <linux/if_vlan.h>
#include <linux/dma-mapping.h>
#include <linux/stm/soc.h>
#include "stmmac.h"
#define STMMAC_RESOURCE_NAME "stmmaceth"
#define PHY_RESOURCE_NAME "stmmacphy"
#undef STMMAC_DEBUG
/*#define STMMAC_DEBUG*/
#ifdef STMMAC_DEBUG
#define DBG(nlevel, klevel, fmt, args...) \
((void)(netif_msg_##nlevel(priv) && \
printk(KERN_##klevel fmt, ## args)))
#else
#define DBG(nlevel, klevel, fmt, args...) do { } while (0)
#endif
#undef STMMAC_RX_DEBUG
/*#define STMMAC_RX_DEBUG*/
#ifdef STMMAC_RX_DEBUG
#define RX_DBG(fmt, args...) printk(fmt, ## args)
#else
#define RX_DBG(fmt, args...) do { } while (0)
#endif
#undef STMMAC_XMIT_DEBUG
/*#define STMMAC_XMIT_DEBUG*/
#ifdef STMMAC_TX_DEBUG
#define TX_DBG(fmt, args...) printk(fmt, ## args)
#else
#define TX_DBG(fmt, args...) do { } while (0)
#endif
#define STMMAC_ALIGN(x) L1_CACHE_ALIGN(x)
#define JUMBO_LEN 9000
/* Module parameters */
#define TX_TIMEO 5000 /* default 5 seconds */
static int watchdog = TX_TIMEO;
module_param(watchdog, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(watchdog, "Transmit timeout in milliseconds");
static int debug = -1; /* -1: default, 0: no output, 16: all */
module_param(debug, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Message Level (0: no output, 16: all)");
static int phyaddr = -1;
module_param(phyaddr, int, S_IRUGO);
MODULE_PARM_DESC(phyaddr, "Physical device address");
#define DMA_TX_SIZE 256
static int dma_txsize = DMA_TX_SIZE;
module_param(dma_txsize, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(dma_txsize, "Number of descriptors in the TX list");
#define DMA_RX_SIZE 256
static int dma_rxsize = DMA_RX_SIZE;
module_param(dma_rxsize, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(dma_rxsize, "Number of descriptors in the RX list");
static int flow_ctrl = FLOW_OFF;
module_param(flow_ctrl, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(flow_ctrl, "Flow control ability [on/off]");
static int pause = PAUSE_TIME;
module_param(pause, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(pause, "Flow Control Pause Time");
#define TC_DEFAULT 64
static int tc = TC_DEFAULT;
module_param(tc, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(tc, "DMA threshold control value");
#define RX_NO_COALESCE 1 /* Always interrupt on completion */
#define TX_NO_COALESCE -1 /* No moderation by default */
/* Pay attention to tune this parameter; take care of both
* hardware capability and network stabitily/performance impact.
* Many tests showed that ~4ms latency seems to be good enough. */
#ifdef CONFIG_STMMAC_TIMER
#define DEFAULT_PERIODIC_RATE 256
static int tmrate = DEFAULT_PERIODIC_RATE;
module_param(tmrate, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(tmrate, "External timer freq. (default: 256Hz)");
#endif
#define DMA_BUFFER_SIZE BUF_SIZE_2KiB
static int buf_sz = DMA_BUFFER_SIZE;
module_param(buf_sz, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(buf_sz, "DMA buffer size");
/* In case of Giga ETH, we can enable/disable the COE for the
* transmit HW checksum computation.
* Note that, if tx csum is off in HW, SG will be still supported. */
static int tx_coe = HW_CSUM;
module_param(tx_coe, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(tx_coe, "GMAC COE type 2 [on/off]");
static const u32 default_msg_level = (NETIF_MSG_DRV | NETIF_MSG_PROBE |
NETIF_MSG_LINK | NETIF_MSG_IFUP |
NETIF_MSG_IFDOWN | NETIF_MSG_TIMER);
static irqreturn_t stmmac_interrupt(int irq, void *dev_id);
static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev);
/**
* stmmac_verify_args - verify the driver parameters.
* Description: it verifies if some wrong parameter is passed to the driver.
* Note that wrong parameters are replaced with the default values.
*/
static void stmmac_verify_args(void)
{
if (unlikely(watchdog < 0))
watchdog = TX_TIMEO;
if (unlikely(dma_rxsize < 0))
dma_rxsize = DMA_RX_SIZE;
if (unlikely(dma_txsize < 0))
dma_txsize = DMA_TX_SIZE;
if (unlikely((buf_sz < DMA_BUFFER_SIZE) || (buf_sz > BUF_SIZE_16KiB)))
buf_sz = DMA_BUFFER_SIZE;
if (unlikely(flow_ctrl > 1))
flow_ctrl = FLOW_AUTO;
else if (likely(flow_ctrl < 0))
flow_ctrl = FLOW_OFF;
if (unlikely((pause < 0) || (pause > 0xffff)))
pause = PAUSE_TIME;
return;
}
#if defined(STMMAC_XMIT_DEBUG) || defined(STMMAC_RX_DEBUG)
static void print_pkt(unsigned char *buf, int len)
{
int j;
pr_info("len = %d byte, buf addr: 0x%p", len, buf);
for (j = 0; j < len; j++) {
if ((j % 16) == 0)
pr_info("\n %03x:", j);
pr_info(" %02x", buf[j]);
}
pr_info("\n");
return;
}
#endif
/* minimum number of free TX descriptors required to wake up TX process */
#define STMMAC_TX_THRESH(x) (x->dma_tx_size/4)
static inline u32 stmmac_tx_avail(struct stmmac_priv *priv)
{
return priv->dirty_tx + priv->dma_tx_size - priv->cur_tx - 1;
}
/**
* stmmac_adjust_link
* @dev: net device structure
* Description: it adjusts the link parameters.
*/
static void stmmac_adjust_link(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
struct phy_device *phydev = priv->phydev;
unsigned long ioaddr = dev->base_addr;
unsigned long flags;
int new_state = 0;
unsigned int fc = priv->flow_ctrl, pause_time = priv->pause;
if (phydev == NULL)
return;
DBG(probe, DEBUG, "stmmac_adjust_link: called. address %d link %d\n",
phydev->addr, phydev->link);
spin_lock_irqsave(&priv->lock, flags);
if (phydev->link) {
u32 ctrl = readl(ioaddr + MAC_CTRL_REG);
/* Now we make sure that we can be in full duplex mode.
* If not, we operate in half-duplex mode. */
if (phydev->duplex != priv->oldduplex) {
new_state = 1;
if (!(phydev->duplex))
ctrl &= ~priv->mac_type->hw.link.duplex;
else
ctrl |= priv->mac_type->hw.link.duplex;
priv->oldduplex = phydev->duplex;
}
/* Flow Control operation */
if (phydev->pause)
priv->mac_type->ops->flow_ctrl(ioaddr, phydev->duplex,
fc, pause_time);
if (phydev->speed != priv->speed) {
new_state = 1;
switch (phydev->speed) {
case 1000:
if (likely(priv->is_gmac))
ctrl &= ~priv->mac_type->hw.link.port;
break;
case 100:
case 10:
if (priv->is_gmac) {
ctrl |= priv->mac_type->hw.link.port;
if (phydev->speed == SPEED_100) {
ctrl |=
priv->mac_type->hw.link.
speed;
} else {
ctrl &=
~(priv->mac_type->hw.
link.speed);
}
} else {
ctrl &= ~priv->mac_type->hw.link.port;
}
priv->fix_mac_speed(priv->bsp_priv,
phydev->speed);
break;
default:
if (netif_msg_link(priv))
pr_warning("%s: Speed (%d) is not 10"
" or 100!\n", dev->name, phydev->speed);
break;
}
priv->speed = phydev->speed;
}
writel(ctrl, ioaddr + MAC_CTRL_REG);
if (!priv->oldlink) {
new_state = 1;
priv->oldlink = 1;
}
} else if (priv->oldlink) {
new_state = 1;
priv->oldlink = 0;
priv->speed = 0;
priv->oldduplex = -1;
}
if (new_state && netif_msg_link(priv))
phy_print_status(phydev);
spin_unlock_irqrestore(&priv->lock, flags);
DBG(probe, DEBUG, "stmmac_adjust_link: exiting\n");
}
/**
* stmmac_init_phy - PHY initialization
* @dev: net device structure
* Description: it initializes the driver's PHY state, and attaches the PHY
* to the mac driver.
* Return value:
* 0 on success
*/
static int stmmac_init_phy(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
struct phy_device *phydev;
char phy_id[BUS_ID_SIZE]; /* PHY to connect */
char bus_id[BUS_ID_SIZE];
priv->oldlink = 0;
priv->speed = 0;
priv->oldduplex = -1;
if (priv->phy_addr == -1) {
/* We don't have a PHY, so do nothing */
return 0;
}
snprintf(bus_id, MII_BUS_ID_SIZE, "%x", priv->bus_id);
snprintf(phy_id, BUS_ID_SIZE, PHY_ID_FMT, bus_id, priv->phy_addr);
pr_debug("stmmac_init_phy: trying to attach to %s\n", phy_id);
phydev = phy_connect(dev, phy_id, &stmmac_adjust_link, 0,
priv->phy_interface);
if (IS_ERR(phydev)) {
pr_err("%s: Could not attach to PHY\n", dev->name);
return PTR_ERR(phydev);
}
/*
* Broken HW is sometimes missing the pull-up resistor on the
* MDIO line, which results in reads to non-existent devices returning
* 0 rather than 0xffff. Catch this here and treat 0 as a non-existent
* device as well.
* Note: phydev->phy_id is the result of reading the UID PHY registers.
*/
if (phydev->phy_id == 0) {
phy_disconnect(phydev);
return -ENODEV;
}
pr_debug("stmmac_init_phy: %s: attached to PHY (UID 0x%x)"
" Link = %d\n", dev->name, phydev->phy_id, phydev->link);
priv->phydev = phydev;
return 0;
}
static inline void stmmac_mac_enable_rx(unsigned long ioaddr)
{
u32 value = readl(ioaddr + MAC_CTRL_REG);
value |= MAC_RNABLE_RX;
/* Set the RE (receive enable bit into the MAC CTRL register). */
writel(value, ioaddr + MAC_CTRL_REG);
}
static inline void stmmac_mac_enable_tx(unsigned long ioaddr)
{
u32 value = readl(ioaddr + MAC_CTRL_REG);
value |= MAC_ENABLE_TX;
/* Set the TE (transmit enable bit into the MAC CTRL register). */
writel(value, ioaddr + MAC_CTRL_REG);
}
static inline void stmmac_mac_disable_rx(unsigned long ioaddr)
{
u32 value = readl(ioaddr + MAC_CTRL_REG);
value &= ~MAC_RNABLE_RX;
writel(value, ioaddr + MAC_CTRL_REG);
}
static inline void stmmac_mac_disable_tx(unsigned long ioaddr)
{
u32 value = readl(ioaddr + MAC_CTRL_REG);
value &= ~MAC_ENABLE_TX;
writel(value, ioaddr + MAC_CTRL_REG);
}
/**
* display_ring
* @p: pointer to the ring.
* @size: size of the ring.
* Description: display all the descriptors within the ring.
*/
static void display_ring(struct dma_desc *p, int size)
{
struct tmp_s {
u64 a;
unsigned int b;
unsigned int c;
};
int i;
for (i = 0; i < size; i++) {
struct tmp_s *x = (struct tmp_s *)(p + i);
pr_info("\t%d [0x%x]: DES0=0x%x DES1=0x%x BUF1=0x%x BUF2=0x%x",
i, (unsigned int)virt_to_phys(&p[i]),
(unsigned int)(x->a), (unsigned int)((x->a) >> 32),
x->b, x->c);
pr_info("\n");
}
}
/**
* init_dma_desc_rings - init the RX/TX descriptor rings
* @dev: net device structure
* Description: this function initializes the DMA RX/TX descriptors
* and allocates the socket buffers.
*/
static void init_dma_desc_rings(struct net_device *dev)
{
int i;
struct stmmac_priv *priv = netdev_priv(dev);
struct sk_buff *skb;
unsigned int txsize = priv->dma_tx_size;
unsigned int rxsize = priv->dma_rx_size;
unsigned int bfsize = priv->dma_buf_sz;
int buff2_needed = 0;
int dis_ic = 0;
#ifdef CONFIG_STMMAC_TIMER
/* Using Timers disable interrupts on completion for the reception */
dis_ic = 1;
#endif
/* Set the Buffer size according to the MTU;
* indeed, in case of jumbo we need to bump-up the buffer sizes.
*/
if (unlikely(dev->mtu >= BUF_SIZE_8KiB))
bfsize = BUF_SIZE_16KiB;
else if (unlikely(dev->mtu >= BUF_SIZE_4KiB))
bfsize = BUF_SIZE_8KiB;
else if (unlikely(dev->mtu >= BUF_SIZE_2KiB))
bfsize = BUF_SIZE_4KiB;
else if (unlikely(dev->mtu >= DMA_BUFFER_SIZE))
bfsize = BUF_SIZE_2KiB;
else
bfsize = DMA_BUFFER_SIZE;
/* If the MTU exceeds 8k so use the second buffer in the chain */
if (bfsize >= BUF_SIZE_8KiB)
buff2_needed = 1;
DBG(probe, INFO, "stmmac: txsize %d, rxsize %d, bfsize %d\n",
txsize, rxsize, bfsize);
priv->rx_skbuff_dma = kmalloc(rxsize * sizeof(dma_addr_t), GFP_KERNEL);
priv->rx_skbuff =
kmalloc(sizeof(struct sk_buff *) * rxsize, GFP_KERNEL);
priv->dma_rx =
(struct dma_desc *)dma_alloc_coherent(priv->device,
rxsize *
sizeof(struct dma_desc),
&priv->dma_rx_phy,
GFP_KERNEL);
priv->tx_skbuff = kmalloc(sizeof(struct sk_buff *) * txsize,
GFP_KERNEL);
priv->dma_tx =
(struct dma_desc *)dma_alloc_coherent(priv->device,
txsize *
sizeof(struct dma_desc),
&priv->dma_tx_phy,
GFP_KERNEL);
if ((priv->dma_rx == NULL) || (priv->dma_tx == NULL)) {
pr_err("%s:ERROR allocating the DMA Tx/Rx desc\n", __func__);
return;
}
DBG(probe, INFO, "stmmac (%s) DMA desc rings: virt addr (Rx %p, "
"Tx %p)\n\tDMA phy addr (Rx 0x%08x, Tx 0x%08x)\n",
dev->name, priv->dma_rx, priv->dma_tx,
(unsigned int)priv->dma_rx_phy, (unsigned int)priv->dma_tx_phy);
/* RX INITIALIZATION */
DBG(probe, INFO, "stmmac: SKB addresses:\n"
"skb\t\tskb data\tdma data\n");
for (i = 0; i < rxsize; i++) {
struct dma_desc *p = priv->dma_rx + i;
skb = netdev_alloc_skb_ip_align(dev, bfsize);
if (unlikely(skb == NULL)) {
pr_err("%s: Rx init fails; skb is NULL\n", __func__);
break;
}
priv->rx_skbuff[i] = skb;
priv->rx_skbuff_dma[i] = dma_map_single(priv->device, skb->data,
bfsize, DMA_FROM_DEVICE);
p->des2 = priv->rx_skbuff_dma[i];
if (unlikely(buff2_needed))
p->des3 = p->des2 + BUF_SIZE_8KiB;
DBG(probe, INFO, "[%p]\t[%p]\t[%x]\n", priv->rx_skbuff[i],
priv->rx_skbuff[i]->data, priv->rx_skbuff_dma[i]);
}
priv->cur_rx = 0;
priv->dirty_rx = (unsigned int)(i - rxsize);
priv->dma_buf_sz = bfsize;
buf_sz = bfsize;
/* TX INITIALIZATION */
for (i = 0; i < txsize; i++) {
priv->tx_skbuff[i] = NULL;
priv->dma_tx[i].des2 = 0;
}
priv->dirty_tx = 0;
priv->cur_tx = 0;
/* Clear the Rx/Tx descriptors */
priv->mac_type->ops->init_rx_desc(priv->dma_rx, rxsize, dis_ic);
priv->mac_type->ops->init_tx_desc(priv->dma_tx, txsize);
if (netif_msg_hw(priv)) {
pr_info("RX descriptor ring:\n");
display_ring(priv->dma_rx, rxsize);
pr_info("TX descriptor ring:\n");
display_ring(priv->dma_tx, txsize);
}
return;
}
static void dma_free_rx_skbufs(struct stmmac_priv *priv)
{
int i;
for (i = 0; i < priv->dma_rx_size; i++) {
if (priv->rx_skbuff[i]) {
dma_unmap_single(priv->device, priv->rx_skbuff_dma[i],
priv->dma_buf_sz, DMA_FROM_DEVICE);
dev_kfree_skb_any(priv->rx_skbuff[i]);
}
priv->rx_skbuff[i] = NULL;
}
return;
}
static void dma_free_tx_skbufs(struct stmmac_priv *priv)
{
int i;
for (i = 0; i < priv->dma_tx_size; i++) {
if (priv->tx_skbuff[i] != NULL) {
struct dma_desc *p = priv->dma_tx + i;
if (p->des2)
dma_unmap_single(priv->device, p->des2,
priv->mac_type->ops->get_tx_len(p),
DMA_TO_DEVICE);
dev_kfree_skb_any(priv->tx_skbuff[i]);
priv->tx_skbuff[i] = NULL;
}
}
return;
}
static void free_dma_desc_resources(struct stmmac_priv *priv)
{
/* Release the DMA TX/RX socket buffers */
dma_free_rx_skbufs(priv);
dma_free_tx_skbufs(priv);
/* Free the region of consistent memory previously allocated for
* the DMA */
dma_free_coherent(priv->device,
priv->dma_tx_size * sizeof(struct dma_desc),
priv->dma_tx, priv->dma_tx_phy);
dma_free_coherent(priv->device,
priv->dma_rx_size * sizeof(struct dma_desc),
priv->dma_rx, priv->dma_rx_phy);
kfree(priv->rx_skbuff_dma);
kfree(priv->rx_skbuff);
kfree(priv->tx_skbuff);
return;
}
/**
* stmmac_dma_start_tx
* @ioaddr: device I/O address
* Description: this function starts the DMA tx process.
*/
static void stmmac_dma_start_tx(unsigned long ioaddr)
{
u32 value = readl(ioaddr + DMA_CONTROL);
value |= DMA_CONTROL_ST;
writel(value, ioaddr + DMA_CONTROL);
return;
}
static void stmmac_dma_stop_tx(unsigned long ioaddr)
{
u32 value = readl(ioaddr + DMA_CONTROL);
value &= ~DMA_CONTROL_ST;
writel(value, ioaddr + DMA_CONTROL);
return;
}
/**
* stmmac_dma_start_rx
* @ioaddr: device I/O address
* Description: this function starts the DMA rx process.
*/
static void stmmac_dma_start_rx(unsigned long ioaddr)
{
u32 value = readl(ioaddr + DMA_CONTROL);
value |= DMA_CONTROL_SR;
writel(value, ioaddr + DMA_CONTROL);
return;
}
static void stmmac_dma_stop_rx(unsigned long ioaddr)
{
u32 value = readl(ioaddr + DMA_CONTROL);
value &= ~DMA_CONTROL_SR;
writel(value, ioaddr + DMA_CONTROL);
return;
}
/**
* stmmac_dma_operation_mode - HW DMA operation mode
* @priv : pointer to the private device structure.
* Description: it sets the DMA operation mode: tx/rx DMA thresholds
* or Store-And-Forward capability. It also verifies the COE for the
* transmission in case of Giga ETH.
*/
static void stmmac_dma_operation_mode(struct stmmac_priv *priv)
{
if (!priv->is_gmac) {
/* MAC 10/100 */
priv->mac_type->ops->dma_mode(priv->dev->base_addr, tc, 0);
priv->tx_coe = NO_HW_CSUM;
} else {
if ((priv->dev->mtu <= ETH_DATA_LEN) && (tx_coe)) {
priv->mac_type->ops->dma_mode(priv->dev->base_addr,
SF_DMA_MODE, SF_DMA_MODE);
tc = SF_DMA_MODE;
priv->tx_coe = HW_CSUM;
} else {
/* Checksum computation is performed in software. */
priv->mac_type->ops->dma_mode(priv->dev->base_addr, tc,
SF_DMA_MODE);
priv->tx_coe = NO_HW_CSUM;
}
}
tx_coe = priv->tx_coe;
return;
}
#ifdef STMMAC_DEBUG
/**
* show_tx_process_state
* @status: tx descriptor status field
* Description: it shows the Transmit Process State for CSR5[22:20]
*/
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;
}
return;
}
/**
* show_rx_process_state
* @status: rx descriptor status field
* Description: it shows the Receive Process State for CSR5[19:17]
*/
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;
}
return;
}
#endif
/**
* stmmac_tx:
* @priv: private driver structure
* Description: it reclaims resources after transmission completes.
*/
static void stmmac_tx(struct stmmac_priv *priv)
{
unsigned int txsize = priv->dma_tx_size;
unsigned long ioaddr = priv->dev->base_addr;
while (priv->dirty_tx != priv->cur_tx) {
int last;
unsigned int entry = priv->dirty_tx % txsize;
struct sk_buff *skb = priv->tx_skbuff[entry];
struct dma_desc *p = priv->dma_tx + entry;
/* Check if the descriptor is owned by the DMA. */
if (priv->mac_type->ops->get_tx_owner(p))
break;
/* Verify tx error by looking at the last segment */
last = priv->mac_type->ops->get_tx_ls(p);
if (likely(last)) {
int tx_error =
priv->mac_type->ops->tx_status(&priv->dev->stats,
&priv->xstats,
p, ioaddr);
if (likely(tx_error == 0)) {
priv->dev->stats.tx_packets++;
priv->xstats.tx_pkt_n++;
} else
priv->dev->stats.tx_errors++;
}
TX_DBG("%s: curr %d, dirty %d\n", __func__,
priv->cur_tx, priv->dirty_tx);
if (likely(p->des2))
dma_unmap_single(priv->device, p->des2,
priv->mac_type->ops->get_tx_len(p),
DMA_TO_DEVICE);
if (unlikely(p->des3))
p->des3 = 0;
if (likely(skb != NULL)) {
/*
* If there's room in the queue (limit it to size)
* we add this skb back into the pool,
* if it's the right size.
*/
if ((skb_queue_len(&priv->rx_recycle) <
priv->dma_rx_size) &&
skb_recycle_check(skb, priv->dma_buf_sz))
__skb_queue_head(&priv->rx_recycle, skb);
else
dev_kfree_skb(skb);
priv->tx_skbuff[entry] = NULL;
}
priv->mac_type->ops->release_tx_desc(p);
entry = (++priv->dirty_tx) % txsize;
}
if (unlikely(netif_queue_stopped(priv->dev) &&
stmmac_tx_avail(priv) > STMMAC_TX_THRESH(priv))) {
netif_tx_lock(priv->dev);
if (netif_queue_stopped(priv->dev) &&
stmmac_tx_avail(priv) > STMMAC_TX_THRESH(priv)) {
TX_DBG("%s: restart transmit\n", __func__);
netif_wake_queue(priv->dev);
}
netif_tx_unlock(priv->dev);
}
return;
}
static inline void stmmac_enable_irq(struct stmmac_priv *priv)
{
#ifndef CONFIG_STMMAC_TIMER
writel(DMA_INTR_DEFAULT_MASK, priv->dev->base_addr + DMA_INTR_ENA);
#else
priv->tm->timer_start(tmrate);
#endif
}
static inline void stmmac_disable_irq(struct stmmac_priv *priv)
{
#ifndef CONFIG_STMMAC_TIMER
writel(0, priv->dev->base_addr + DMA_INTR_ENA);
#else
priv->tm->timer_stop();
#endif
}
static int stmmac_has_work(struct stmmac_priv *priv)
{
unsigned int has_work = 0;
int rxret, tx_work = 0;
rxret = priv->mac_type->ops->get_rx_owner(priv->dma_rx +
(priv->cur_rx % priv->dma_rx_size));
if (priv->dirty_tx != priv->cur_tx)
tx_work = 1;
if (likely(!rxret || tx_work))
has_work = 1;
return has_work;
}
static inline void _stmmac_schedule(struct stmmac_priv *priv)
{
if (likely(stmmac_has_work(priv))) {
stmmac_disable_irq(priv);
napi_schedule(&priv->napi);
}
}
#ifdef CONFIG_STMMAC_TIMER
void stmmac_schedule(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
priv->xstats.sched_timer_n++;
_stmmac_schedule(priv);
return;
}
static void stmmac_no_timer_started(unsigned int x)
{;
};
static void stmmac_no_timer_stopped(void)
{;
};
#endif
/**
* stmmac_tx_err:
* @priv: pointer to the private device structure
* Description: it cleans the descriptors and restarts the transmission
* in case of errors.
*/
static void stmmac_tx_err(struct stmmac_priv *priv)
{
netif_stop_queue(priv->dev);
stmmac_dma_stop_tx(priv->dev->base_addr);
dma_free_tx_skbufs(priv);
priv->mac_type->ops->init_tx_desc(priv->dma_tx, priv->dma_tx_size);
priv->dirty_tx = 0;
priv->cur_tx = 0;
stmmac_dma_start_tx(priv->dev->base_addr);
priv->dev->stats.tx_errors++;
netif_wake_queue(priv->dev);
return;
}
/**
* stmmac_dma_interrupt - Interrupt handler for the driver
* @dev: net device structure
* Description: Interrupt handler for the driver (DMA).
*/
static void stmmac_dma_interrupt(struct net_device *dev)
{
unsigned long ioaddr = dev->base_addr;
struct stmmac_priv *priv = netdev_priv(dev);
/* read the status register (CSR5) */
u32 intr_status = readl(ioaddr + DMA_STATUS);
DBG(intr, INFO, "%s: [CSR5: 0x%08x]\n", __func__, intr_status);
#ifdef STMMAC_DEBUG
/* It displays the DMA transmit process state (CSR5 register) */
if (netif_msg_tx_done(priv))
show_tx_process_state(intr_status);
if (netif_msg_rx_status(priv))
show_rx_process_state(intr_status);
#endif
/* ABNORMAL interrupts */
if (unlikely(intr_status & DMA_STATUS_AIS)) {
DBG(intr, INFO, "CSR5[15] DMA ABNORMAL IRQ: ");
if (unlikely(intr_status & DMA_STATUS_UNF)) {
DBG(intr, INFO, "transmit underflow\n");
if (unlikely(tc != SF_DMA_MODE)
&& (tc <= 256)) {
/* Try to bump up the threshold */
tc += 64;
priv->mac_type->ops->dma_mode(ioaddr, tc,
SF_DMA_MODE);
priv->xstats.threshold = tc;
}
stmmac_tx_err(priv);
priv->xstats.tx_undeflow_irq++;
}
if (unlikely(intr_status & DMA_STATUS_TJT)) {
DBG(intr, INFO, "transmit jabber\n");
priv->xstats.tx_jabber_irq++;
}
if (unlikely(intr_status & DMA_STATUS_OVF)) {
DBG(intr, INFO, "recv overflow\n");
priv->xstats.rx_overflow_irq++;
}
if (unlikely(intr_status & DMA_STATUS_RU)) {
DBG(intr, INFO, "receive buffer unavailable\n");
priv->xstats.rx_buf_unav_irq++;
}
if (unlikely(intr_status & DMA_STATUS_RPS)) {
DBG(intr, INFO, "receive process stopped\n");
priv->xstats.rx_process_stopped_irq++;
}
if (unlikely(intr_status & DMA_STATUS_RWT)) {
DBG(intr, INFO, "receive watchdog\n");
priv->xstats.rx_watchdog_irq++;
}
if (unlikely(intr_status & DMA_STATUS_ETI)) {
DBG(intr, INFO, "transmit early interrupt\n");
priv->xstats.tx_early_irq++;
}
if (unlikely(intr_status & DMA_STATUS_TPS)) {
DBG(intr, INFO, "transmit process stopped\n");
priv->xstats.tx_process_stopped_irq++;
stmmac_tx_err(priv);
}
if (unlikely(intr_status & DMA_STATUS_FBI)) {
DBG(intr, INFO, "fatal bus error\n");
priv->xstats.fatal_bus_error_irq++;
stmmac_tx_err(priv);
}
}
/* TX/RX NORMAL interrupts */
if (intr_status & DMA_STATUS_NIS) {
priv->xstats.normal_irq_n++;
if (likely((intr_status & DMA_STATUS_RI) ||
(intr_status & (DMA_STATUS_TI))))
_stmmac_schedule(priv);
}
/* 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);
DBG(intr, INFO, "\n\n");
return;
}
/**
* stmmac_open - open entry point of the driver
* @dev : pointer to the device structure.
* Description:
* This function is the open entry point of the driver.
* Return value:
* 0 on success and an appropriate (-)ve integer as defined in errno.h
* file on failure.
*/
static int stmmac_open(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
unsigned long ioaddr = dev->base_addr;
int ret;
/* Check that the MAC address is valid. If its not, refuse
* to bring the device up. The user must specify an
* address using the following linux command:
* ifconfig eth0 hw ether xx:xx:xx:xx:xx:xx */
if (!is_valid_ether_addr(dev->dev_addr)) {
random_ether_addr(dev->dev_addr);
pr_warning("%s: generated random MAC address %pM\n", dev->name,
dev->dev_addr);
}
stmmac_verify_args();
ret = stmmac_init_phy(dev);
if (unlikely(ret)) {
pr_err("%s: Cannot attach to PHY (error: %d)\n", __func__, ret);
return ret;
}
/* Request the IRQ lines */
ret = request_irq(dev->irq, &stmmac_interrupt,
IRQF_SHARED, dev->name, dev);
if (unlikely(ret < 0)) {
pr_err("%s: ERROR: allocating the IRQ %d (error: %d)\n",
__func__, dev->irq, ret);
return ret;
}
#ifdef CONFIG_STMMAC_TIMER
priv->tm = kmalloc(sizeof(struct stmmac_timer *), GFP_KERNEL);
if (unlikely(priv->tm == NULL)) {
pr_err("%s: ERROR: timer memory alloc failed \n", __func__);
return -ENOMEM;
}
priv->tm->freq = tmrate;
/* Test if the HW timer can be actually used.
* In case of failure continue with no timer. */
if (unlikely((stmmac_open_ext_timer(dev, priv->tm)) < 0)) {
pr_warning("stmmaceth: cannot attach the HW timer\n");
tmrate = 0;
priv->tm->freq = 0;
priv->tm->timer_start = stmmac_no_timer_started;
priv->tm->timer_stop = stmmac_no_timer_stopped;
}
#endif
/* Create and initialize the TX/RX descriptors chains. */
priv->dma_tx_size = STMMAC_ALIGN(dma_txsize);
priv->dma_rx_size = STMMAC_ALIGN(dma_rxsize);
priv->dma_buf_sz = STMMAC_ALIGN(buf_sz);
init_dma_desc_rings(dev);
/* DMA initialization and SW reset */
if (unlikely(priv->mac_type->ops->dma_init(ioaddr,
priv->pbl, priv->dma_tx_phy, priv->dma_rx_phy) < 0)) {
pr_err("%s: DMA initialization failed\n", __func__);
return -1;
}
/* Copy the MAC addr into the HW */
priv->mac_type->ops->set_umac_addr(ioaddr, dev->dev_addr, 0);
/* Initialize the MAC Core */
priv->mac_type->ops->core_init(ioaddr);
priv->shutdown = 0;
/* Initialise the MMC (if present) to disable all interrupts. */
writel(0xffffffff, ioaddr + MMC_HIGH_INTR_MASK);
writel(0xffffffff, ioaddr + MMC_LOW_INTR_MASK);
/* Enable the MAC Rx/Tx */
stmmac_mac_enable_rx(ioaddr);
stmmac_mac_enable_tx(ioaddr);
/* Set the HW DMA mode and the COE */
stmmac_dma_operation_mode(priv);
/* Extra statistics */
memset(&priv->xstats, 0, sizeof(struct stmmac_extra_stats));
priv->xstats.threshold = tc;
/* Start the ball rolling... */
DBG(probe, DEBUG, "%s: DMA RX/TX processes started...\n", dev->name);
stmmac_dma_start_tx(ioaddr);
stmmac_dma_start_rx(ioaddr);
#ifdef CONFIG_STMMAC_TIMER
priv->tm->timer_start(tmrate);
#endif
/* Dump DMA/MAC registers */
if (netif_msg_hw(priv)) {
priv->mac_type->ops->dump_mac_regs(ioaddr);
priv->mac_type->ops->dump_dma_regs(ioaddr);
}
if (priv->phydev)
phy_start(priv->phydev);
napi_enable(&priv->napi);
skb_queue_head_init(&priv->rx_recycle);
netif_start_queue(dev);
return 0;
}
/**
* stmmac_release - close entry point of the driver
* @dev : device pointer.
* Description:
* This is the stop entry point of the driver.
*/
static int stmmac_release(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
/* Stop and disconnect the PHY */
if (priv->phydev) {
phy_stop(priv->phydev);
phy_disconnect(priv->phydev);
priv->phydev = NULL;
}
netif_stop_queue(dev);
#ifdef CONFIG_STMMAC_TIMER
/* Stop and release the timer */
stmmac_close_ext_timer();
if (priv->tm != NULL)
kfree(priv->tm);
#endif
napi_disable(&priv->napi);
skb_queue_purge(&priv->rx_recycle);
/* Free the IRQ lines */
free_irq(dev->irq, dev);
/* Stop TX/RX DMA and clear the descriptors */
stmmac_dma_stop_tx(dev->base_addr);
stmmac_dma_stop_rx(dev->base_addr);
/* Release and free the Rx/Tx resources */
free_dma_desc_resources(priv);
/* Disable the MAC core */
stmmac_mac_disable_tx(dev->base_addr);
stmmac_mac_disable_rx(dev->base_addr);
netif_carrier_off(dev);
return 0;
}
/*
* To perform emulated hardware segmentation on skb.
*/
static int stmmac_sw_tso(struct stmmac_priv *priv, struct sk_buff *skb)
{
struct sk_buff *segs, *curr_skb;
int gso_segs = skb_shinfo(skb)->gso_segs;
/* Estimate the number of fragments in the worst case */
if (unlikely(stmmac_tx_avail(priv) < gso_segs)) {
netif_stop_queue(priv->dev);
TX_DBG(KERN_ERR "%s: TSO BUG! Tx Ring full when queue awake\n",
__func__);
if (stmmac_tx_avail(priv) < gso_segs)
return NETDEV_TX_BUSY;
netif_wake_queue(priv->dev);
}
TX_DBG("\tstmmac_sw_tso: segmenting: skb %p (len %d)\n",
skb, skb->len);
segs = skb_gso_segment(skb, priv->dev->features & ~NETIF_F_TSO);
if (unlikely(IS_ERR(segs)))
goto sw_tso_end;
do {
curr_skb = segs;
segs = segs->next;
TX_DBG("\t\tcurrent skb->len: %d, *curr %p,"
"*next %p\n", curr_skb->len, curr_skb, segs);
curr_skb->next = NULL;
stmmac_xmit(curr_skb, priv->dev);
} while (segs);
sw_tso_end:
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
static unsigned int stmmac_handle_jumbo_frames(struct sk_buff *skb,
struct net_device *dev,
int csum_insertion)
{
struct stmmac_priv *priv = netdev_priv(dev);
unsigned int nopaged_len = skb_headlen(skb);
unsigned int txsize = priv->dma_tx_size;
unsigned int entry = priv->cur_tx % txsize;
struct dma_desc *desc = priv->dma_tx + entry;
if (nopaged_len > BUF_SIZE_8KiB) {
int buf2_size = nopaged_len - BUF_SIZE_8KiB;
desc->des2 = dma_map_single(priv->device, skb->data,
BUF_SIZE_8KiB, DMA_TO_DEVICE);
desc->des3 = desc->des2 + BUF_SIZE_4KiB;
priv->mac_type->ops->prepare_tx_desc(desc, 1, BUF_SIZE_8KiB,
csum_insertion);
entry = (++priv->cur_tx) % txsize;
desc = priv->dma_tx + entry;
desc->des2 = dma_map_single(priv->device,
skb->data + BUF_SIZE_8KiB,
buf2_size, DMA_TO_DEVICE);
desc->des3 = desc->des2 + BUF_SIZE_4KiB;
priv->mac_type->ops->prepare_tx_desc(desc, 0,
buf2_size, csum_insertion);
priv->mac_type->ops->set_tx_owner(desc);
priv->tx_skbuff[entry] = NULL;
} else {
desc->des2 = dma_map_single(priv->device, skb->data,
nopaged_len, DMA_TO_DEVICE);
desc->des3 = desc->des2 + BUF_SIZE_4KiB;
priv->mac_type->ops->prepare_tx_desc(desc, 1, nopaged_len,
csum_insertion);
}
return entry;
}
/**
* stmmac_xmit:
* @skb : the socket buffer
* @dev : device pointer
* Description : Tx entry point of the driver.
*/
static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
unsigned int txsize = priv->dma_tx_size;
unsigned int entry;
int i, csum_insertion = 0;
int nfrags = skb_shinfo(skb)->nr_frags;
struct dma_desc *desc, *first;
if (unlikely(stmmac_tx_avail(priv) < nfrags + 1)) {
if (!netif_queue_stopped(dev)) {
netif_stop_queue(dev);
/* This is a hard error, log it. */
pr_err("%s: BUG! Tx Ring full when queue awake\n",
__func__);
}
return NETDEV_TX_BUSY;
}
entry = priv->cur_tx % txsize;
#ifdef STMMAC_XMIT_DEBUG
if ((skb->len > ETH_FRAME_LEN) || nfrags)
pr_info("stmmac xmit:\n"
"\tskb addr %p - len: %d - nopaged_len: %d\n"
"\tn_frags: %d - ip_summed: %d - %s gso\n",
skb, skb->len, skb_headlen(skb), nfrags, skb->ip_summed,
!skb_is_gso(skb) ? "isn't" : "is");
#endif
if (unlikely(skb_is_gso(skb)))
return stmmac_sw_tso(priv, skb);
if (likely((skb->ip_summed == CHECKSUM_PARTIAL))) {
if (likely(priv->tx_coe == NO_HW_CSUM))
skb_checksum_help(skb);
else
csum_insertion = 1;
}
desc = priv->dma_tx + entry;
first = desc;
#ifdef STMMAC_XMIT_DEBUG
if ((nfrags > 0) || (skb->len > ETH_FRAME_LEN))
pr_debug("stmmac xmit: skb len: %d, nopaged_len: %d,\n"
"\t\tn_frags: %d, ip_summed: %d\n",
skb->len, skb_headlen(skb), nfrags, skb->ip_summed);
#endif
priv->tx_skbuff[entry] = skb;
if (unlikely(skb->len >= BUF_SIZE_4KiB)) {
entry = stmmac_handle_jumbo_frames(skb, dev, csum_insertion);
desc = priv->dma_tx + entry;
} else {
unsigned int nopaged_len = skb_headlen(skb);
desc->des2 = dma_map_single(priv->device, skb->data,
nopaged_len, DMA_TO_DEVICE);
priv->mac_type->ops->prepare_tx_desc(desc, 1, nopaged_len,
csum_insertion);
}
for (i = 0; i < nfrags; i++) {
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
int len = frag->size;
entry = (++priv->cur_tx) % txsize;
desc = priv->dma_tx + entry;
TX_DBG("\t[entry %d] segment len: %d\n", entry, len);
desc->des2 = dma_map_page(priv->device, frag->page,
frag->page_offset,
len, DMA_TO_DEVICE);
priv->tx_skbuff[entry] = NULL;
priv->mac_type->ops->prepare_tx_desc(desc, 0, len,
csum_insertion);
priv->mac_type->ops->set_tx_owner(desc);
}
/* Interrupt on completition only for the latest segment */
priv->mac_type->ops->close_tx_desc(desc);
#ifdef CONFIG_STMMAC_TIMER
/* Clean IC while using timers */
priv->mac_type->ops->clear_tx_ic(desc);
#endif
/* To avoid raise condition */
priv->mac_type->ops->set_tx_owner(first);
priv->cur_tx++;
#ifdef STMMAC_XMIT_DEBUG
if (netif_msg_pktdata(priv)) {
pr_info("stmmac xmit: current=%d, dirty=%d, entry=%d, "
"first=%p, nfrags=%d\n",
(priv->cur_tx % txsize), (priv->dirty_tx % txsize),
entry, first, nfrags);
display_ring(priv->dma_tx, txsize);
pr_info(">>> frame to be transmitted: ");
print_pkt(skb->data, skb->len);
}
#endif
if (unlikely(stmmac_tx_avail(priv) <= (MAX_SKB_FRAGS + 1))) {
TX_DBG("%s: stop transmitted packets\n", __func__);
netif_stop_queue(dev);
}
dev->stats.tx_bytes += skb->len;
/* CSR1 enables the transmit DMA to check for new descriptor */
writel(1, dev->base_addr + DMA_XMT_POLL_DEMAND);
return NETDEV_TX_OK;
}
static inline void stmmac_rx_refill(struct stmmac_priv *priv)
{
unsigned int rxsize = priv->dma_rx_size;
int bfsize = priv->dma_buf_sz;
struct dma_desc *p = priv->dma_rx;
for (; priv->cur_rx - priv->dirty_rx > 0; priv->dirty_rx++) {
unsigned int entry = priv->dirty_rx % rxsize;
if (likely(priv->rx_skbuff[entry] == NULL)) {
struct sk_buff *skb;
skb = __skb_dequeue(&priv->rx_recycle);
if (skb == NULL)
skb = netdev_alloc_skb_ip_align(priv->dev,
bfsize);
if (unlikely(skb == NULL))
break;
priv->rx_skbuff[entry] = skb;
priv->rx_skbuff_dma[entry] =
dma_map_single(priv->device, skb->data, bfsize,
DMA_FROM_DEVICE);
(p + entry)->des2 = priv->rx_skbuff_dma[entry];
if (unlikely(priv->is_gmac)) {
if (bfsize >= BUF_SIZE_8KiB)
(p + entry)->des3 =
(p + entry)->des2 + BUF_SIZE_8KiB;
}
RX_DBG(KERN_INFO "\trefill entry #%d\n", entry);
}
priv->mac_type->ops->set_rx_owner(p + entry);
}
return;
}
static int stmmac_rx(struct stmmac_priv *priv, int limit)
{
unsigned int rxsize = priv->dma_rx_size;
unsigned int entry = priv->cur_rx % rxsize;
unsigned int next_entry;
unsigned int count = 0;
struct dma_desc *p = priv->dma_rx + entry;
struct dma_desc *p_next;
#ifdef STMMAC_RX_DEBUG
if (netif_msg_hw(priv)) {
pr_debug(">>> stmmac_rx: descriptor ring:\n");
display_ring(priv->dma_rx, rxsize);
}
#endif
count = 0;
while (!priv->mac_type->ops->get_rx_owner(p)) {
int status;
if (count >= limit)
break;
count++;
next_entry = (++priv->cur_rx) % rxsize;
p_next = priv->dma_rx + next_entry;
prefetch(p_next);
/* read the status of the incoming frame */
status = (priv->mac_type->ops->rx_status(&priv->dev->stats,
&priv->xstats, p));
if (unlikely(status == discard_frame))
priv->dev->stats.rx_errors++;
else {
struct sk_buff *skb;
/* Length should omit the CRC */
int frame_len =
priv->mac_type->ops->get_rx_frame_len(p) - 4;
#ifdef STMMAC_RX_DEBUG
if (frame_len > ETH_FRAME_LEN)
pr_debug("\tRX frame size %d, COE status: %d\n",
frame_len, status);
if (netif_msg_hw(priv))
pr_debug("\tdesc: %p [entry %d] buff=0x%x\n",
p, entry, p->des2);
#endif
skb = priv->rx_skbuff[entry];
if (unlikely(!skb)) {
pr_err("%s: Inconsistent Rx descriptor chain\n",
priv->dev->name);
priv->dev->stats.rx_dropped++;
break;
}
prefetch(skb->data - NET_IP_ALIGN);
priv->rx_skbuff[entry] = NULL;
skb_put(skb, frame_len);
dma_unmap_single(priv->device,
priv->rx_skbuff_dma[entry],
priv->dma_buf_sz, DMA_FROM_DEVICE);
#ifdef STMMAC_RX_DEBUG
if (netif_msg_pktdata(priv)) {
pr_info(" frame received (%dbytes)", frame_len);
print_pkt(skb->data, frame_len);
}
#endif
skb->protocol = eth_type_trans(skb, priv->dev);
if (unlikely(status == csum_none)) {
/* always for the old mac 10/100 */
skb->ip_summed = CHECKSUM_NONE;
netif_receive_skb(skb);
} else {
skb->ip_summed = CHECKSUM_UNNECESSARY;
napi_gro_receive(&priv->napi, skb);
}
priv->dev->stats.rx_packets++;
priv->dev->stats.rx_bytes += frame_len;
priv->dev->last_rx = jiffies;
}
entry = next_entry;
p = p_next; /* use prefetched values */
}
stmmac_rx_refill(priv);
priv->xstats.rx_pkt_n += count;
return count;
}
/**
* stmmac_poll - stmmac poll method (NAPI)
* @napi : pointer to the napi structure.
* @budget : maximum number of packets that the current CPU can receive from
* all interfaces.
* Description :
* This function implements the the reception process.
* Also it runs the TX completion thread
*/
static int stmmac_poll(struct napi_struct *napi, int budget)
{
struct stmmac_priv *priv = container_of(napi, struct stmmac_priv, napi);
int work_done = 0;
priv->xstats.poll_n++;
stmmac_tx(priv);
work_done = stmmac_rx(priv, budget);
if (work_done < budget) {
napi_complete(napi);
stmmac_enable_irq(priv);
}
return work_done;
}
/**
* stmmac_tx_timeout
* @dev : Pointer to net device structure
* Description: this function is called when a packet transmission fails to
* complete within a reasonable tmrate. The driver will mark the error in the
* netdev structure and arrange for the device to be reset to a sane state
* in order to transmit a new packet.
*/
static void stmmac_tx_timeout(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
/* Clear Tx resources and restart transmitting again */
stmmac_tx_err(priv);
return;
}
/* Configuration changes (passed on by ifconfig) */
static int stmmac_config(struct net_device *dev, struct ifmap *map)
{
if (dev->flags & IFF_UP) /* can't act on a running interface */
return -EBUSY;
/* Don't allow changing the I/O address */
if (map->base_addr != dev->base_addr) {
pr_warning("%s: can't change I/O address\n", dev->name);
return -EOPNOTSUPP;
}
/* Don't allow changing the IRQ */
if (map->irq != dev->irq) {
pr_warning("%s: can't change IRQ number %d\n",
dev->name, dev->irq);
return -EOPNOTSUPP;
}
/* ignore other fields */
return 0;
}
/**
* stmmac_multicast_list - entry point for multicast addressing
* @dev : pointer to the device structure
* Description:
* This function is a driver entry point which gets called by the kernel
* whenever multicast addresses must be enabled/disabled.
* Return value:
* void.
*/
static void stmmac_multicast_list(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
spin_lock(&priv->lock);
priv->mac_type->ops->set_filter(dev);
spin_unlock(&priv->lock);
return;
}
/**
* stmmac_change_mtu - entry point to change MTU size for the device.
* @dev : device pointer.
* @new_mtu : the new MTU size for the device.
* Description: the Maximum Transfer Unit (MTU) is used by the network layer
* to drive packet transmission. Ethernet has an MTU of 1500 octets
* (ETH_DATA_LEN). This value can be changed with ifconfig.
* Return value:
* 0 on success and an appropriate (-)ve integer as defined in errno.h
* file on failure.
*/
static int stmmac_change_mtu(struct net_device *dev, int new_mtu)
{
struct stmmac_priv *priv = netdev_priv(dev);
int max_mtu;
if (netif_running(dev)) {
pr_err("%s: must be stopped to change its MTU\n", dev->name);
return -EBUSY;
}
if (priv->is_gmac)
max_mtu = JUMBO_LEN;
else
max_mtu = ETH_DATA_LEN;
if ((new_mtu < 46) || (new_mtu > max_mtu)) {
pr_err("%s: invalid MTU, max MTU is: %d\n", dev->name, max_mtu);
return -EINVAL;
}
dev->mtu = new_mtu;
return 0;
}
static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
{
struct net_device *dev = (struct net_device *)dev_id;
struct stmmac_priv *priv = netdev_priv(dev);
if (unlikely(!dev)) {
pr_err("%s: invalid dev pointer\n", __func__);
return IRQ_NONE;
}
if (priv->is_gmac) {
unsigned long ioaddr = dev->base_addr;
/* To handle GMAC own interrupts */
priv->mac_type->ops->host_irq_status(ioaddr);
}
stmmac_dma_interrupt(dev);
return IRQ_HANDLED;
}
#ifdef CONFIG_NET_POLL_CONTROLLER
/* Polling receive - used by NETCONSOLE and other diagnostic tools
* to allow network I/O with interrupts disabled. */
static void stmmac_poll_controller(struct net_device *dev)
{
disable_irq(dev->irq);
stmmac_interrupt(dev->irq, dev);
enable_irq(dev->irq);
}
#endif
/**
* stmmac_ioctl - Entry point for the Ioctl
* @dev: Device pointer.
* @rq: An IOCTL specefic structure, that can contain a pointer to
* a proprietary structure used to pass information to the driver.
* @cmd: IOCTL command
* Description:
* Currently there are no special functionality supported in IOCTL, just the
* phy_mii_ioctl(...) can be invoked.
*/
static int stmmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
struct stmmac_priv *priv = netdev_priv(dev);
int ret = -EOPNOTSUPP;
if (!netif_running(dev))
return -EINVAL;
switch (cmd) {
case SIOCGMIIPHY:
case SIOCGMIIREG:
case SIOCSMIIREG:
if (!priv->phydev)
return -EINVAL;
spin_lock(&priv->lock);
ret = phy_mii_ioctl(priv->phydev, if_mii(rq), cmd);
spin_unlock(&priv->lock);
default:
break;
}
return ret;
}
#ifdef STMMAC_VLAN_TAG_USED
static void stmmac_vlan_rx_register(struct net_device *dev,
struct vlan_group *grp)
{
struct stmmac_priv *priv = netdev_priv(dev);
DBG(probe, INFO, "%s: Setting vlgrp to %p\n", dev->name, grp);
spin_lock(&priv->lock);
priv->vlgrp = grp;
spin_unlock(&priv->lock);
return;
}
#endif
static const struct net_device_ops stmmac_netdev_ops = {
.ndo_open = stmmac_open,
.ndo_start_xmit = stmmac_xmit,
.ndo_stop = stmmac_release,
.ndo_change_mtu = stmmac_change_mtu,
.ndo_set_multicast_list = stmmac_multicast_list,
.ndo_tx_timeout = stmmac_tx_timeout,
.ndo_do_ioctl = stmmac_ioctl,
.ndo_set_config = stmmac_config,
#ifdef STMMAC_VLAN_TAG_USED
.ndo_vlan_rx_register = stmmac_vlan_rx_register,
#endif
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = stmmac_poll_controller,
#endif
.ndo_set_mac_address = eth_mac_addr,
};
/**
* stmmac_probe - Initialization of the adapter .
* @dev : device pointer
* Description: The function initializes the network device structure for
* the STMMAC driver. It also calls the low level routines
* in order to init the HW (i.e. the DMA engine)
*/
static int stmmac_probe(struct net_device *dev)
{
int ret = 0;
struct stmmac_priv *priv = netdev_priv(dev);
ether_setup(dev);
dev->netdev_ops = &stmmac_netdev_ops;
stmmac_set_ethtool_ops(dev);
dev->features |= (NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA);
dev->watchdog_timeo = msecs_to_jiffies(watchdog);
#ifdef STMMAC_VLAN_TAG_USED
/* Both mac100 and gmac support receive VLAN tag detection */
dev->features |= NETIF_F_HW_VLAN_RX;
#endif
priv->msg_enable = netif_msg_init(debug, default_msg_level);
if (priv->is_gmac)
priv->rx_csum = 1;
if (flow_ctrl)
priv->flow_ctrl = FLOW_AUTO; /* RX/TX pause on */
priv->pause = pause;
netif_napi_add(dev, &priv->napi, stmmac_poll, 64);
/* Get the MAC address */
priv->mac_type->ops->get_umac_addr(dev->base_addr, dev->dev_addr, 0);
if (!is_valid_ether_addr(dev->dev_addr))
pr_warning("\tno valid MAC address;"
"please, use ifconfig or nwhwconfig!\n");
ret = register_netdev(dev);
if (ret) {
pr_err("%s: ERROR %i registering the device\n",
__func__, ret);
return -ENODEV;
}
DBG(probe, DEBUG, "%s: Scatter/Gather: %s - HW checksums: %s\n",
dev->name, (dev->features & NETIF_F_SG) ? "on" : "off",
(dev->features & NETIF_F_HW_CSUM) ? "on" : "off");
spin_lock_init(&priv->lock);
return ret;
}
/**
* stmmac_mac_device_setup
* @dev : device pointer
* Description: select and initialise the mac device (mac100 or Gmac).
*/
static int stmmac_mac_device_setup(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
unsigned long ioaddr = dev->base_addr;
struct mac_device_info *device;
if (priv->is_gmac)
device = gmac_setup(ioaddr);
else
device = mac100_setup(ioaddr);
if (!device)
return -ENOMEM;
priv->mac_type = device;
priv->wolenabled = priv->mac_type->hw.pmt; /* PMT supported */
if (priv->wolenabled == PMT_SUPPORTED)
priv->wolopts = WAKE_MAGIC; /* Magic Frame */
return 0;
}
static int stmmacphy_dvr_probe(struct platform_device *pdev)
{
struct plat_stmmacphy_data *plat_dat;
plat_dat = (struct plat_stmmacphy_data *)((pdev->dev).platform_data);
pr_debug("stmmacphy_dvr_probe: added phy for bus %d\n",
plat_dat->bus_id);
return 0;
}
static int stmmacphy_dvr_remove(struct platform_device *pdev)
{
return 0;
}
static struct platform_driver stmmacphy_driver = {
.driver = {
.name = PHY_RESOURCE_NAME,
},
.probe = stmmacphy_dvr_probe,
.remove = stmmacphy_dvr_remove,
};
/**
* stmmac_associate_phy
* @dev: pointer to device structure
* @data: points to the private structure.
* Description: Scans through all the PHYs we have registered and checks if
* any are associated with our MAC. If so, then just fill in
* the blanks in our local context structure
*/
static int stmmac_associate_phy(struct device *dev, void *data)
{
struct stmmac_priv *priv = (struct stmmac_priv *)data;
struct plat_stmmacphy_data *plat_dat;
plat_dat = (struct plat_stmmacphy_data *)(dev->platform_data);
DBG(probe, DEBUG, "%s: checking phy for bus %d\n", __func__,
plat_dat->bus_id);
/* Check that this phy is for the MAC being initialised */
if (priv->bus_id != plat_dat->bus_id)
return 0;
/* OK, this PHY is connected to the MAC.
Go ahead and get the parameters */
DBG(probe, DEBUG, "%s: OK. Found PHY config\n", __func__);
priv->phy_irq =
platform_get_irq_byname(to_platform_device(dev), "phyirq");
DBG(probe, DEBUG, "%s: PHY irq on bus %d is %d\n", __func__,
plat_dat->bus_id, priv->phy_irq);
/* Override with kernel parameters if supplied XXX CRS XXX
* this needs to have multiple instances */
if ((phyaddr >= 0) && (phyaddr <= 31))
plat_dat->phy_addr = phyaddr;
priv->phy_addr = plat_dat->phy_addr;
priv->phy_mask = plat_dat->phy_mask;
priv->phy_interface = plat_dat->interface;
priv->phy_reset = plat_dat->phy_reset;
DBG(probe, DEBUG, "%s: exiting\n", __func__);
return 1; /* forces exit of driver_for_each_device() */
}
/**
* stmmac_dvr_probe
* @pdev: platform device pointer
* Description: the driver is initialized through platform_device.
*/
static int stmmac_dvr_probe(struct platform_device *pdev)
{
int ret = 0;
struct resource *res;
unsigned int *addr = NULL;
struct net_device *ndev = NULL;
struct stmmac_priv *priv;
struct plat_stmmacenet_data *plat_dat;
pr_info("STMMAC driver:\n\tplatform registration... ");
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
ret = -ENODEV;
goto out;
}
pr_info("done!\n");
if (!request_mem_region(res->start, (res->end - res->start),
pdev->name)) {
pr_err("%s: ERROR: memory allocation failed"
"cannot get the I/O addr 0x%x\n",
__func__, (unsigned int)res->start);
ret = -EBUSY;
goto out;
}
addr = ioremap(res->start, (res->end - res->start));
if (!addr) {
pr_err("%s: ERROR: memory mapping failed \n", __func__);
ret = -ENOMEM;
goto out;
}
ndev = alloc_etherdev(sizeof(struct stmmac_priv));
if (!ndev) {
pr_err("%s: ERROR: allocating the device\n", __func__);
ret = -ENOMEM;
goto out;
}
SET_NETDEV_DEV(ndev, &pdev->dev);
/* Get the MAC information */
ndev->irq = platform_get_irq_byname(pdev, "macirq");
if (ndev->irq == -ENXIO) {
pr_err("%s: ERROR: MAC IRQ configuration "
"information not found\n", __func__);
ret = -ENODEV;
goto out;
}
priv = netdev_priv(ndev);
priv->device = &(pdev->dev);
priv->dev = ndev;
plat_dat = (struct plat_stmmacenet_data *)((pdev->dev).platform_data);
priv->bus_id = plat_dat->bus_id;
priv->pbl = plat_dat->pbl; /* TLI */
priv->is_gmac = plat_dat->has_gmac; /* GMAC is on board */
platform_set_drvdata(pdev, ndev);
/* Set the I/O base addr */
ndev->base_addr = (unsigned long)addr;
/* MAC HW revice detection */
ret = stmmac_mac_device_setup(ndev);
if (ret < 0)
goto out;
/* Network Device Registration */
ret = stmmac_probe(ndev);
if (ret < 0)
goto out;
/* associate a PHY - it is provided by another platform bus */
if (!driver_for_each_device
(&(stmmacphy_driver.driver), NULL, (void *)priv,
stmmac_associate_phy)) {
pr_err("No PHY device is associated with this MAC!\n");
ret = -ENODEV;
goto out;
}
priv->fix_mac_speed = plat_dat->fix_mac_speed;
priv->bsp_priv = plat_dat->bsp_priv;
pr_info("\t%s - (dev. name: %s - id: %d, IRQ #%d\n"
"\tIO base addr: 0x%08x)\n", ndev->name, pdev->name,
pdev->id, ndev->irq, (unsigned int)addr);
/* MDIO bus Registration */
pr_debug("\tMDIO bus (id: %d)...", priv->bus_id);
ret = stmmac_mdio_register(ndev);
if (ret < 0)
goto out;
pr_debug("registered!\n");
out:
if (ret < 0) {
platform_set_drvdata(pdev, NULL);
release_mem_region(res->start, (res->end - res->start));
if (addr != NULL)
iounmap(addr);
}
return ret;
}
/**
* stmmac_dvr_remove
* @pdev: platform device pointer
* Description: this function resets the TX/RX processes, disables the MAC RX/TX
* changes the link status, releases the DMA descriptor rings,
* unregisters the MDIO bus and unmaps the allocated memory.
*/
static int stmmac_dvr_remove(struct platform_device *pdev)
{
struct net_device *ndev = platform_get_drvdata(pdev);
struct resource *res;
pr_info("%s:\n\tremoving driver", __func__);
stmmac_dma_stop_rx(ndev->base_addr);
stmmac_dma_stop_tx(ndev->base_addr);
stmmac_mac_disable_rx(ndev->base_addr);
stmmac_mac_disable_tx(ndev->base_addr);
netif_carrier_off(ndev);
stmmac_mdio_unregister(ndev);
platform_set_drvdata(pdev, NULL);
unregister_netdev(ndev);
iounmap((void *)ndev->base_addr);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(res->start, (res->end - res->start));
free_netdev(ndev);
return 0;
}
#ifdef CONFIG_PM
static int stmmac_suspend(struct platform_device *pdev, pm_message_t state)
{
struct net_device *dev = platform_get_drvdata(pdev);
struct stmmac_priv *priv = netdev_priv(dev);
int dis_ic = 0;
if (!dev || !netif_running(dev))
return 0;
spin_lock(&priv->lock);
if (state.event == PM_EVENT_SUSPEND) {
netif_device_detach(dev);
netif_stop_queue(dev);
if (priv->phydev)
phy_stop(priv->phydev);
#ifdef CONFIG_STMMAC_TIMER
priv->tm->timer_stop();
dis_ic = 1;
#endif
napi_disable(&priv->napi);
/* Stop TX/RX DMA */
stmmac_dma_stop_tx(dev->base_addr);
stmmac_dma_stop_rx(dev->base_addr);
/* Clear the Rx/Tx descriptors */
priv->mac_type->ops->init_rx_desc(priv->dma_rx,
priv->dma_rx_size, dis_ic);
priv->mac_type->ops->init_tx_desc(priv->dma_tx,
priv->dma_tx_size);
stmmac_mac_disable_tx(dev->base_addr);
if (device_may_wakeup(&(pdev->dev))) {
/* Enable Power down mode by programming the PMT regs */
if (priv->wolenabled == PMT_SUPPORTED)
priv->mac_type->ops->pmt(dev->base_addr,
priv->wolopts);
} else {
stmmac_mac_disable_rx(dev->base_addr);
}
} else {
priv->shutdown = 1;
/* Although this can appear slightly redundant it actually
* makes fast the standby operation and guarantees the driver
* working if hibernation is on media. */
stmmac_release(dev);
}
spin_unlock(&priv->lock);
return 0;
}
static int stmmac_resume(struct platform_device *pdev)
{
struct net_device *dev = platform_get_drvdata(pdev);
struct stmmac_priv *priv = netdev_priv(dev);
unsigned long ioaddr = dev->base_addr;
if (!netif_running(dev))
return 0;
spin_lock(&priv->lock);
if (priv->shutdown) {
/* Re-open the interface and re-init the MAC/DMA
and the rings. */
stmmac_open(dev);
goto out_resume;
}
/* Power Down bit, into the PM register, is cleared
* automatically as soon as a magic packet or a Wake-up frame
* is received. Anyway, it's better to manually clear
* this bit because it can generate problems while resuming
* from another devices (e.g. serial console). */
if (device_may_wakeup(&(pdev->dev)))
if (priv->wolenabled == PMT_SUPPORTED)
priv->mac_type->ops->pmt(dev->base_addr, 0);
netif_device_attach(dev);
/* Enable the MAC and DMA */
stmmac_mac_enable_rx(ioaddr);
stmmac_mac_enable_tx(ioaddr);
stmmac_dma_start_tx(ioaddr);
stmmac_dma_start_rx(ioaddr);
#ifdef CONFIG_STMMAC_TIMER
priv->tm->timer_start(tmrate);
#endif
napi_enable(&priv->napi);
if (priv->phydev)
phy_start(priv->phydev);
netif_start_queue(dev);
out_resume:
spin_unlock(&priv->lock);
return 0;
}
#endif
static struct platform_driver stmmac_driver = {
.driver = {
.name = STMMAC_RESOURCE_NAME,
},
.probe = stmmac_dvr_probe,
.remove = stmmac_dvr_remove,
#ifdef CONFIG_PM
.suspend = stmmac_suspend,
.resume = stmmac_resume,
#endif
};
/**
* stmmac_init_module - Entry point for the driver
* Description: This function is the entry point for the driver.
*/
static int __init stmmac_init_module(void)
{
int ret;
if (platform_driver_register(&stmmacphy_driver)) {
pr_err("No PHY devices registered!\n");
return -ENODEV;
}
ret = platform_driver_register(&stmmac_driver);
return ret;
}
/**
* stmmac_cleanup_module - Cleanup routine for the driver
* Description: This function is the cleanup routine for the driver.
*/
static void __exit stmmac_cleanup_module(void)
{
platform_driver_unregister(&stmmacphy_driver);
platform_driver_unregister(&stmmac_driver);
}
#ifndef MODULE
static int __init stmmac_cmdline_opt(char *str)
{
char *opt;
if (!str || !*str)
return -EINVAL;
while ((opt = strsep(&str, ",")) != NULL) {
if (!strncmp(opt, "debug:", 6))
strict_strtoul(opt + 6, 0, (unsigned long *)&debug);
else if (!strncmp(opt, "phyaddr:", 8))
strict_strtoul(opt + 8, 0, (unsigned long *)&phyaddr);
else if (!strncmp(opt, "dma_txsize:", 11))
strict_strtoul(opt + 11, 0,
(unsigned long *)&dma_txsize);
else if (!strncmp(opt, "dma_rxsize:", 11))
strict_strtoul(opt + 11, 0,
(unsigned long *)&dma_rxsize);
else if (!strncmp(opt, "buf_sz:", 7))
strict_strtoul(opt + 7, 0, (unsigned long *)&buf_sz);
else if (!strncmp(opt, "tc:", 3))
strict_strtoul(opt + 3, 0, (unsigned long *)&tc);
else if (!strncmp(opt, "tx_coe:", 7))
strict_strtoul(opt + 7, 0, (unsigned long *)&tx_coe);
else if (!strncmp(opt, "watchdog:", 9))
strict_strtoul(opt + 9, 0, (unsigned long *)&watchdog);
else if (!strncmp(opt, "flow_ctrl:", 10))
strict_strtoul(opt + 10, 0,
(unsigned long *)&flow_ctrl);
else if (!strncmp(opt, "pause:", 6))
strict_strtoul(opt + 6, 0, (unsigned long *)&pause);
#ifdef CONFIG_STMMAC_TIMER
else if (!strncmp(opt, "tmrate:", 7))
strict_strtoul(opt + 7, 0, (unsigned long *)&tmrate);
#endif
}
return 0;
}
__setup("stmmaceth=", stmmac_cmdline_opt);
#endif
module_init(stmmac_init_module);
module_exit(stmmac_cleanup_module);
MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet driver");
MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
MODULE_LICENSE("GPL");
/*******************************************************************************
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/netdevice.h>
#include <linux/mii.h>
#include <linux/phy.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 long ioaddr = ndev->base_addr;
unsigned int mii_address = priv->mac_type->hw.mii.addr;
unsigned int mii_data = priv->mac_type->hw.mii.data;
int data;
u16 regValue = (((phyaddr << 11) & (0x0000F800)) |
((phyreg << 6) & (0x000007C0)));
regValue |= MII_BUSY; /* in case of GMAC */
do {} while (((readl(ioaddr + mii_address)) & MII_BUSY) == 1);
writel(regValue, ioaddr + mii_address);
do {} while (((readl(ioaddr + mii_address)) & MII_BUSY) == 1);
/* Read the data from the MII data register */
data = (int)readl(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 long ioaddr = ndev->base_addr;
unsigned int mii_address = priv->mac_type->hw.mii.addr;
unsigned int mii_data = priv->mac_type->hw.mii.data;
u16 value =
(((phyaddr << 11) & (0x0000F800)) | ((phyreg << 6) & (0x000007C0)))
| MII_WRITE;
value |= MII_BUSY;
/* Wait until any existing MII operation is complete */
do {} while (((readl(ioaddr + mii_address)) & MII_BUSY) == 1);
/* Set the MII address register to write */
writel(phydata, ioaddr + mii_data);
writel(value, ioaddr + mii_address);
/* Wait until any existing MII operation is complete */
do {} while (((readl(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 long ioaddr = ndev->base_addr;
unsigned int mii_address = priv->mac_type->hw.mii.addr;
if (priv->phy_reset) {
pr_debug("stmmac_mdio_reset: calling phy_reset\n");
priv->phy_reset(priv->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, 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);
int addr, found;
new_bus = mdiobus_alloc();
if (new_bus == NULL)
return -ENOMEM;
irqlist = kzalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
if (irqlist == NULL) {
err = -ENOMEM;
goto irqlist_alloc_fail;
}
/* Assign IRQ to phy at address phy_addr */
if (priv->phy_addr != -1)
irqlist[priv->phy_addr] = priv->phy_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", priv->bus_id);
new_bus->priv = ndev;
new_bus->irq = irqlist;
new_bus->phy_mask = priv->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 < 32; addr++) {
struct phy_device *phydev = new_bus->phy_map[addr];
if (phydev) {
if (priv->phy_addr == -1) {
priv->phy_addr = addr;
phydev->irq = priv->phy_irq;
irqlist[addr] = priv->phy_irq;
}
pr_info("%s: PHY ID %08x at %d IRQ %d (%s)%s\n",
ndev->name, phydev->phy_id, addr,
phydev->irq, dev_name(&phydev->dev),
(addr == priv->phy_addr) ? " active" : "");
found = 1;
}
}
if (!found)
pr_warning("%s: No PHY found\n", ndev->name);
return 0;
bus_register_fail:
kfree(irqlist);
irqlist_alloc_fail:
kfree(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;
kfree(priv->mii);
return 0;
}
/*******************************************************************************
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);
return;
}
#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);
return;
}
static void stmmac_rtc_stop(void)
{
rtc_irq_set_state(stmmac_rtc, &stmmac_task, 0);
return;
}
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_error("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_error("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);
return;
}
static void stmmac_tmu_stop(void)
{
clk_disable(timer_clock);
return;
}
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
/*******************************************************************************
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;
};
/* 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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment