Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View file

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

View file

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

View file

@ -0,0 +1,100 @@
config STMMAC_ETH
tristate "STMicroelectronics 10/100/1000 Ethernet driver"
depends on HAS_IOMEM && HAS_DMA
select MII
select PHYLIB
select CRC32
select PTP_1588_CLOCK
select RESET_CONTROLLER
---help---
This is the driver for the Ethernet IPs are built around a
Synopsys IP Core and only tested on the STMicroelectronics
platforms.
if STMMAC_ETH
config STMMAC_PLATFORM
bool "STMMAC Platform bus support"
depends on STMMAC_ETH
default y
---help---
This selects the platform specific bus support for
the stmmac device driver. This is the driver used
on many embedded STM platforms based on ARM and SuperH
processors.
If you have a controller with this interface, say Y or M here.
If unsure, say N.
config DWMAC_MESON
bool "Amlogic Meson dwmac support"
depends on STMMAC_PLATFORM && ARCH_MESON
help
Support for Ethernet controller on Amlogic Meson SoCs.
This selects the Amlogic Meson SoC glue layer support for
the stmmac device driver. This driver is used for Meson6 and
Meson8 SoCs.
config DWMAC_SOCFPGA
bool "SOCFPGA dwmac support"
depends on STMMAC_PLATFORM && MFD_SYSCON && (ARCH_SOCFPGA || COMPILE_TEST)
help
Support for ethernet controller on Altera SOCFPGA
This selects the Altera SOCFPGA SoC glue layer support
for the stmmac device driver. This driver is used for
arria5 and cyclone5 FPGA SoCs.
config DWMAC_SUNXI
bool "Allwinner GMAC support"
depends on STMMAC_PLATFORM && ARCH_SUNXI
default y
---help---
Support for Allwinner A20/A31 GMAC ethernet controllers.
This selects Allwinner SoC glue layer support for the
stmmac device driver. This driver is used for A20/A31
GMAC ethernet controller.
config DWMAC_STI
bool "STi GMAC support"
depends on STMMAC_PLATFORM && ARCH_STI
default y
---help---
Support for ethernet controller on STi SOCs.
This selects STi SoC glue layer support for the stmmac
device driver. This driver is used on for the STi series
SOCs GMAC ethernet controller.
config STMMAC_PCI
bool "STMMAC PCI bus support"
depends on STMMAC_ETH && PCI
---help---
This is to select the Synopsys DWMAC available on PCI devices,
if you have a controller with this interface, say Y or M here.
This PCI support is tested on XLINX XC2V3000 FF1152AMT0221
D1215994A VIRTEX FPGA board.
If unsure, say N.
config STMMAC_DEBUG_FS
bool "Enable monitoring via sysFS "
default n
depends on STMMAC_ETH && DEBUG_FS
---help---
The stmmac entry in /sys reports DMA TX/RX rings
or (if supported) the HW cap register.
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).
endif

View file

@ -0,0 +1,11 @@
obj-$(CONFIG_STMMAC_ETH) += stmmac.o
stmmac-$(CONFIG_STMMAC_PLATFORM) += stmmac_platform.o
stmmac-$(CONFIG_STMMAC_PCI) += stmmac_pci.o
stmmac-$(CONFIG_DWMAC_MESON) += dwmac-meson.o
stmmac-$(CONFIG_DWMAC_SUNXI) += dwmac-sunxi.o
stmmac-$(CONFIG_DWMAC_STI) += dwmac-sti.o
stmmac-$(CONFIG_DWMAC_SOCFPGA) += dwmac-socfpga.o
stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \
chain_mode.o dwmac_lib.o dwmac1000_core.o dwmac1000_dma.o \
dwmac100_core.o dwmac100_dma.o enh_desc.o norm_desc.o \
mmc_core.o stmmac_hwtstamp.o stmmac_ptp.o $(stmmac-y)

View file

@ -0,0 +1,166 @@
/*******************************************************************************
Specialised functions for managing Chained mode
Copyright(C) 2011 STMicroelectronics Ltd
It defines all the functions used to handle the normal/enhanced
descriptors in case of the DMA is configured to work in chained or
in ring mode.
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 "stmmac.h"
static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
{
struct stmmac_priv *priv = (struct stmmac_priv *)p;
unsigned int txsize = priv->dma_tx_size;
unsigned int entry = priv->cur_tx % txsize;
struct dma_desc *desc = priv->dma_tx + entry;
unsigned int nopaged_len = skb_headlen(skb);
unsigned int bmax;
unsigned int i = 1, len;
if (priv->plat->enh_desc)
bmax = BUF_SIZE_8KiB;
else
bmax = BUF_SIZE_2KiB;
len = nopaged_len - bmax;
desc->des2 = dma_map_single(priv->device, skb->data,
bmax, DMA_TO_DEVICE);
if (dma_mapping_error(priv->device, desc->des2))
return -1;
priv->tx_skbuff_dma[entry].buf = desc->des2;
priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum, STMMAC_CHAIN_MODE);
while (len != 0) {
priv->tx_skbuff[entry] = NULL;
entry = (++priv->cur_tx) % txsize;
desc = priv->dma_tx + entry;
if (len > bmax) {
desc->des2 = dma_map_single(priv->device,
(skb->data + bmax * i),
bmax, DMA_TO_DEVICE);
if (dma_mapping_error(priv->device, desc->des2))
return -1;
priv->tx_skbuff_dma[entry].buf = desc->des2;
priv->hw->desc->prepare_tx_desc(desc, 0, bmax, csum,
STMMAC_CHAIN_MODE);
priv->hw->desc->set_tx_owner(desc);
len -= bmax;
i++;
} else {
desc->des2 = dma_map_single(priv->device,
(skb->data + bmax * i), len,
DMA_TO_DEVICE);
if (dma_mapping_error(priv->device, desc->des2))
return -1;
priv->tx_skbuff_dma[entry].buf = desc->des2;
priv->hw->desc->prepare_tx_desc(desc, 0, len, csum,
STMMAC_CHAIN_MODE);
priv->hw->desc->set_tx_owner(desc);
len = 0;
}
}
return entry;
}
static unsigned int stmmac_is_jumbo_frm(int len, int enh_desc)
{
unsigned int ret = 0;
if ((enh_desc && (len > BUF_SIZE_8KiB)) ||
(!enh_desc && (len > BUF_SIZE_2KiB))) {
ret = 1;
}
return ret;
}
static void stmmac_init_dma_chain(void *des, dma_addr_t phy_addr,
unsigned int size, unsigned int extend_desc)
{
/*
* In chained mode the des3 points to the next element in the ring.
* The latest element has to point to the head.
*/
int i;
dma_addr_t dma_phy = phy_addr;
if (extend_desc) {
struct dma_extended_desc *p = (struct dma_extended_desc *)des;
for (i = 0; i < (size - 1); i++) {
dma_phy += sizeof(struct dma_extended_desc);
p->basic.des3 = (unsigned int)dma_phy;
p++;
}
p->basic.des3 = (unsigned int)phy_addr;
} else {
struct dma_desc *p = (struct dma_desc *)des;
for (i = 0; i < (size - 1); i++) {
dma_phy += sizeof(struct dma_desc);
p->des3 = (unsigned int)dma_phy;
p++;
}
p->des3 = (unsigned int)phy_addr;
}
}
static void stmmac_refill_desc3(void *priv_ptr, struct dma_desc *p)
{
struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr;
if (priv->hwts_rx_en && !priv->extend_desc)
/* NOTE: Device will overwrite des3 with timestamp value if
* 1588-2002 time stamping is enabled, hence reinitialize it
* to keep explicit chaining in the descriptor.
*/
p->des3 = (unsigned int)(priv->dma_rx_phy +
(((priv->dirty_rx) + 1) %
priv->dma_rx_size) *
sizeof(struct dma_desc));
}
static void stmmac_clean_desc3(void *priv_ptr, struct dma_desc *p)
{
struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr;
if (priv->hw->desc->get_tx_ls(p) && !priv->extend_desc)
/* NOTE: Device will overwrite des3 with timestamp value if
* 1588-2002 time stamping is enabled, hence reinitialize it
* to keep explicit chaining in the descriptor.
*/
p->des3 = (unsigned int)(priv->dma_tx_phy +
(((priv->dirty_tx + 1) %
priv->dma_tx_size) *
sizeof(struct dma_desc)));
}
const struct stmmac_mode_ops chain_mode_ops = {
.init = stmmac_init_dma_chain,
.is_jumbo_frm = stmmac_is_jumbo_frm,
.jumbo_frm = stmmac_jumbo_frm,
.refill_desc3 = stmmac_refill_desc3,
.clean_desc3 = stmmac_clean_desc3,
};

View file

@ -0,0 +1,466 @@
/*******************************************************************************
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>
*******************************************************************************/
#ifndef __COMMON_H__
#define __COMMON_H__
#include <linux/etherdevice.h>
#include <linux/netdevice.h>
#include <linux/phy.h>
#include <linux/module.h>
#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
#define STMMAC_VLAN_TAG_USED
#include <linux/if_vlan.h>
#endif
#include "descs.h"
#include "mmc.h"
/* Synopsys Core versions */
#define DWMAC_CORE_3_40 0x34
#define DWMAC_CORE_3_50 0x35
#undef FRAME_FILTER_DEBUG
/* #define FRAME_FILTER_DEBUG */
struct stmmac_extra_stats {
/* Transmit errors */
unsigned long tx_underflow ____cacheline_aligned;
unsigned long tx_carrier;
unsigned long tx_losscarrier;
unsigned long vlan_tag;
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 sa_filter_fail;
unsigned long overflow_error;
unsigned long ipc_csum_error;
unsigned long rx_collision;
unsigned long rx_crc;
unsigned long dribbling_bit;
unsigned long rx_length;
unsigned long rx_mii;
unsigned long rx_multicast;
unsigned long rx_gmac_overflow;
unsigned long rx_watchdog;
unsigned long da_rx_filter_fail;
unsigned long sa_rx_filter_fail;
unsigned long rx_missed_cntr;
unsigned long rx_overflow_cntr;
unsigned long rx_vlan;
/* Tx/Rx IRQ error info */
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;
/* Tx/Rx IRQ Events */
unsigned long rx_early_irq;
unsigned long threshold;
unsigned long tx_pkt_n;
unsigned long rx_pkt_n;
unsigned long normal_irq_n;
unsigned long rx_normal_irq_n;
unsigned long napi_poll;
unsigned long tx_normal_irq_n;
unsigned long tx_clean;
unsigned long tx_reset_ic_bit;
unsigned long irq_receive_pmt_irq_n;
/* MMC info */
unsigned long mmc_tx_irq_n;
unsigned long mmc_rx_irq_n;
unsigned long mmc_rx_csum_offload_irq_n;
/* EEE */
unsigned long irq_tx_path_in_lpi_mode_n;
unsigned long irq_tx_path_exit_lpi_mode_n;
unsigned long irq_rx_path_in_lpi_mode_n;
unsigned long irq_rx_path_exit_lpi_mode_n;
unsigned long phy_eee_wakeup_error_n;
/* Extended RDES status */
unsigned long ip_hdr_err;
unsigned long ip_payload_err;
unsigned long ip_csum_bypassed;
unsigned long ipv4_pkt_rcvd;
unsigned long ipv6_pkt_rcvd;
unsigned long rx_msg_type_ext_no_ptp;
unsigned long rx_msg_type_sync;
unsigned long rx_msg_type_follow_up;
unsigned long rx_msg_type_delay_req;
unsigned long rx_msg_type_delay_resp;
unsigned long rx_msg_type_pdelay_req;
unsigned long rx_msg_type_pdelay_resp;
unsigned long rx_msg_type_pdelay_follow_up;
unsigned long ptp_frame_type;
unsigned long ptp_ver;
unsigned long timestamp_dropped;
unsigned long av_pkt_rcvd;
unsigned long av_tagged_pkt_rcvd;
unsigned long vlan_tag_priority_val;
unsigned long l3_filter_match;
unsigned long l4_filter_match;
unsigned long l3_l4_filter_no_match;
/* PCS */
unsigned long irq_pcs_ane_n;
unsigned long irq_pcs_link_n;
unsigned long irq_rgmii_n;
unsigned long pcs_link;
unsigned long pcs_duplex;
unsigned long pcs_speed;
};
/* CSR Frequency Access Defines*/
#define CSR_F_35M 35000000
#define CSR_F_60M 60000000
#define CSR_F_100M 100000000
#define CSR_F_150M 150000000
#define CSR_F_250M 250000000
#define CSR_F_300M 300000000
#define MAC_CSR_H_FRQ_MASK 0x20
#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)
/* PCS defines */
#define STMMAC_PCS_RGMII (1 << 0)
#define STMMAC_PCS_SGMII (1 << 1)
#define STMMAC_PCS_TBI (1 << 2)
#define STMMAC_PCS_RTBI (1 << 3)
#define SF_DMA_MODE 1 /* DMA STORE-AND-FORWARD Operation Mode */
/* DAM HW feature register fields */
#define DMA_HW_FEAT_MIISEL 0x00000001 /* 10/100 Mbps Support */
#define DMA_HW_FEAT_GMIISEL 0x00000002 /* 1000 Mbps Support */
#define DMA_HW_FEAT_HDSEL 0x00000004 /* Half-Duplex Support */
#define DMA_HW_FEAT_EXTHASHEN 0x00000008 /* Expanded DA Hash Filter */
#define DMA_HW_FEAT_HASHSEL 0x00000010 /* HASH Filter */
#define DMA_HW_FEAT_ADDMAC 0x00000020 /* Multiple MAC Addr Reg */
#define DMA_HW_FEAT_PCSSEL 0x00000040 /* PCS registers */
#define DMA_HW_FEAT_L3L4FLTREN 0x00000080 /* Layer 3 & Layer 4 Feature */
#define DMA_HW_FEAT_SMASEL 0x00000100 /* SMA(MDIO) Interface */
#define DMA_HW_FEAT_RWKSEL 0x00000200 /* PMT Remote Wakeup */
#define DMA_HW_FEAT_MGKSEL 0x00000400 /* PMT Magic Packet */
#define DMA_HW_FEAT_MMCSEL 0x00000800 /* RMON Module */
#define DMA_HW_FEAT_TSVER1SEL 0x00001000 /* Only IEEE 1588-2002 */
#define DMA_HW_FEAT_TSVER2SEL 0x00002000 /* IEEE 1588-2008 PTPv2 */
#define DMA_HW_FEAT_EEESEL 0x00004000 /* Energy Efficient Ethernet */
#define DMA_HW_FEAT_AVSEL 0x00008000 /* AV Feature */
#define DMA_HW_FEAT_TXCOESEL 0x00010000 /* Checksum Offload in Tx */
#define DMA_HW_FEAT_RXTYP1COE 0x00020000 /* IP COE (Type 1) in Rx */
#define DMA_HW_FEAT_RXTYP2COE 0x00040000 /* IP COE (Type 2) in Rx */
#define DMA_HW_FEAT_RXFIFOSIZE 0x00080000 /* Rx FIFO > 2048 Bytes */
#define DMA_HW_FEAT_RXCHCNT 0x00300000 /* No. additional Rx Channels */
#define DMA_HW_FEAT_TXCHCNT 0x00c00000 /* No. additional Tx Channels */
#define DMA_HW_FEAT_ENHDESSEL 0x01000000 /* Alternate Descriptor */
/* Timestamping with Internal System Time */
#define DMA_HW_FEAT_INTTSEN 0x02000000
#define DMA_HW_FEAT_FLEXIPPSEN 0x04000000 /* Flexible PPS Output */
#define DMA_HW_FEAT_SAVLANINS 0x08000000 /* Source Addr or VLAN */
#define DMA_HW_FEAT_ACTPHYIF 0x70000000 /* Active/selected PHY iface */
#define DEFAULT_DMA_PBL 8
/* Max/Min RI Watchdog Timer count value */
#define MAX_DMA_RIWT 0xff
#define MIN_DMA_RIWT 0x20
/* Tx coalesce parameters */
#define STMMAC_COAL_TX_TIMER 40000
#define STMMAC_MAX_COAL_TX_TICK 100000
#define STMMAC_TX_MAX_FRAMES 256
#define STMMAC_TX_FRAMES 64
/* Rx IPC status */
enum rx_frame_status {
good_frame = 0,
discard_frame = 1,
csum_none = 2,
llc_snap = 4,
};
enum dma_irq_status {
tx_hard_error = 0x1,
tx_hard_error_bump_tc = 0x2,
handle_rx = 0x4,
handle_tx = 0x8,
};
#define CORE_IRQ_TX_PATH_IN_LPI_MODE (1 << 0)
#define CORE_IRQ_TX_PATH_EXIT_LPI_MODE (1 << 1)
#define CORE_IRQ_RX_PATH_IN_LPI_MODE (1 << 2)
#define CORE_IRQ_RX_PATH_EXIT_LPI_MODE (1 << 3)
#define CORE_PCS_ANE_COMPLETE (1 << 5)
#define CORE_PCS_LINK_STATUS (1 << 6)
#define CORE_RGMII_IRQ (1 << 7)
struct rgmii_adv {
unsigned int pause;
unsigned int duplex;
unsigned int lp_pause;
unsigned int lp_duplex;
};
#define STMMAC_PCS_PAUSE 1
#define STMMAC_PCS_ASYM_PAUSE 2
/* DMA HW capabilities */
struct dma_features {
unsigned int mbps_10_100;
unsigned int mbps_1000;
unsigned int half_duplex;
unsigned int hash_filter;
unsigned int multi_addr;
unsigned int pcs;
unsigned int sma_mdio;
unsigned int pmt_remote_wake_up;
unsigned int pmt_magic_frame;
unsigned int rmon;
/* IEEE 1588-2002 */
unsigned int time_stamp;
/* IEEE 1588-2008 */
unsigned int atime_stamp;
/* 802.3az - Energy-Efficient Ethernet (EEE) */
unsigned int eee;
unsigned int av;
/* TX and RX csum */
unsigned int tx_coe;
unsigned int rx_coe_type1;
unsigned int rx_coe_type2;
unsigned int rxfifo_over_2048;
/* TX and RX number of channels */
unsigned int number_rx_channel;
unsigned int number_tx_channel;
/* Alternate (enhanced) DESC mode */
unsigned int enh_desc;
};
/* 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 */
/* Default LPI timers */
#define STMMAC_DEFAULT_LIT_LS 0x3E8
#define STMMAC_DEFAULT_TWT_LS 0x1E
#define STMMAC_CHAIN_MODE 0x1
#define STMMAC_RING_MODE 0x2
#define JUMBO_LEN 9000
struct stmmac_desc_ops {
/* DMA RX descriptor ring initialization */
void (*init_rx_desc) (struct dma_desc *p, int disable_rx_ic, int mode,
int end);
/* DMA TX descriptor ring initialization */
void (*init_tx_desc) (struct dma_desc *p, int mode, int end);
/* 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, int mode);
/* 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, int mode);
/* Clear interrupt on tx frame completion. When this bit is
* set an interrupt happens as soon as the frame is transmitted */
void (*clear_tx_ic) (struct dma_desc *p);
/* Last tx segment reports the transmit status */
int (*get_tx_ls) (struct dma_desc *p);
/* Return the transmit status looking at the TDES1 */
int (*tx_status) (void *data, struct stmmac_extra_stats *x,
struct dma_desc *p, void __iomem *ioaddr);
/* Get the buffer size from the descriptor */
int (*get_tx_len) (struct dma_desc *p);
/* Handle extra events on specific interrupts hw dependent */
int (*get_rx_owner) (struct dma_desc *p);
void (*set_rx_owner) (struct dma_desc *p);
/* Get the receive frame size */
int (*get_rx_frame_len) (struct dma_desc *p, int rx_coe_type);
/* Return the reception status looking at the RDES1 */
int (*rx_status) (void *data, struct stmmac_extra_stats *x,
struct dma_desc *p);
void (*rx_extended_status) (void *data, struct stmmac_extra_stats *x,
struct dma_extended_desc *p);
/* Set tx timestamp enable bit */
void (*enable_tx_timestamp) (struct dma_desc *p);
/* get tx timestamp status */
int (*get_tx_timestamp_status) (struct dma_desc *p);
/* get timestamp value */
u64(*get_timestamp) (void *desc, u32 ats);
/* get rx timestamp status */
int (*get_rx_timestamp_status) (void *desc, u32 ats);
};
struct stmmac_dma_ops {
/* DMA core initialization */
int (*init) (void __iomem *ioaddr, int pbl, int fb, int mb,
int burst_len, u32 dma_tx, u32 dma_rx, int atds);
/* Dump DMA registers */
void (*dump_regs) (void __iomem *ioaddr);
/* Set tx/rx threshold in the csr6 register
* An invalid value enables the store-and-forward mode */
void (*dma_mode) (void __iomem *ioaddr, int txmode, int rxmode);
/* To track extra statistic (if supported) */
void (*dma_diagnostic_fr) (void *data, struct stmmac_extra_stats *x,
void __iomem *ioaddr);
void (*enable_dma_transmission) (void __iomem *ioaddr);
void (*enable_dma_irq) (void __iomem *ioaddr);
void (*disable_dma_irq) (void __iomem *ioaddr);
void (*start_tx) (void __iomem *ioaddr);
void (*stop_tx) (void __iomem *ioaddr);
void (*start_rx) (void __iomem *ioaddr);
void (*stop_rx) (void __iomem *ioaddr);
int (*dma_interrupt) (void __iomem *ioaddr,
struct stmmac_extra_stats *x);
/* If supported then get the optional core features */
unsigned int (*get_hw_feature) (void __iomem *ioaddr);
/* Program the HW RX Watchdog */
void (*rx_watchdog) (void __iomem *ioaddr, u32 riwt);
};
struct mac_device_info;
struct stmmac_ops {
/* MAC core initialization */
void (*core_init)(struct mac_device_info *hw, int mtu);
/* Enable and verify that the IPC module is supported */
int (*rx_ipc)(struct mac_device_info *hw);
/* Dump MAC registers */
void (*dump_regs)(struct mac_device_info *hw);
/* Handle extra events on specific interrupts hw dependent */
int (*host_irq_status)(struct mac_device_info *hw,
struct stmmac_extra_stats *x);
/* Multicast filter setting */
void (*set_filter)(struct mac_device_info *hw, struct net_device *dev);
/* Flow control setting */
void (*flow_ctrl)(struct mac_device_info *hw, unsigned int duplex,
unsigned int fc, unsigned int pause_time);
/* Set power management mode (e.g. magic frame) */
void (*pmt)(struct mac_device_info *hw, unsigned long mode);
/* Set/Get Unicast MAC addresses */
void (*set_umac_addr)(struct mac_device_info *hw, unsigned char *addr,
unsigned int reg_n);
void (*get_umac_addr)(struct mac_device_info *hw, unsigned char *addr,
unsigned int reg_n);
void (*set_eee_mode)(struct mac_device_info *hw);
void (*reset_eee_mode)(struct mac_device_info *hw);
void (*set_eee_timer)(struct mac_device_info *hw, int ls, int tw);
void (*set_eee_pls)(struct mac_device_info *hw, int link);
void (*ctrl_ane)(struct mac_device_info *hw, bool restart);
void (*get_adv)(struct mac_device_info *hw, struct rgmii_adv *adv);
};
struct stmmac_hwtimestamp {
void (*config_hw_tstamping) (void __iomem *ioaddr, u32 data);
void (*config_sub_second_increment) (void __iomem *ioaddr);
int (*init_systime) (void __iomem *ioaddr, u32 sec, u32 nsec);
int (*config_addend) (void __iomem *ioaddr, u32 addend);
int (*adjust_systime) (void __iomem *ioaddr, u32 sec, u32 nsec,
int add_sub);
u64(*get_systime) (void __iomem *ioaddr);
};
struct mac_link {
int port;
int duplex;
int speed;
};
struct mii_regs {
unsigned int addr; /* MII Address */
unsigned int data; /* MII Data */
};
struct stmmac_mode_ops {
void (*init) (void *des, dma_addr_t phy_addr, unsigned int size,
unsigned int extend_desc);
unsigned int (*is_jumbo_frm) (int len, int ehn_desc);
int (*jumbo_frm)(void *priv, struct sk_buff *skb, int csum);
int (*set_16kib_bfsize)(int mtu);
void (*init_desc3)(struct dma_desc *p);
void (*refill_desc3) (void *priv, struct dma_desc *p);
void (*clean_desc3) (void *priv, struct dma_desc *p);
};
struct mac_device_info {
const struct stmmac_ops *mac;
const struct stmmac_desc_ops *desc;
const struct stmmac_dma_ops *dma;
const struct stmmac_mode_ops *mode;
const struct stmmac_hwtimestamp *ptp;
struct mii_regs mii; /* MII register Addresses */
struct mac_link link;
unsigned int synopsys_uid;
void __iomem *pcsr; /* vpointer to device CSRs */
int multicast_filter_bins;
int unicast_filter_entries;
int mcast_bits_log2;
unsigned int rx_csum;
};
struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr, int mcbins,
int perfect_uc_entries);
struct mac_device_info *dwmac100_setup(void __iomem *ioaddr);
void stmmac_set_mac_addr(void __iomem *ioaddr, u8 addr[6],
unsigned int high, unsigned int low);
void stmmac_get_mac_addr(void __iomem *ioaddr, unsigned char *addr,
unsigned int high, unsigned int low);
void stmmac_set_mac(void __iomem *ioaddr, bool enable);
void dwmac_dma_flush_tx_fifo(void __iomem *ioaddr);
extern const struct stmmac_mode_ops ring_mode_ops;
extern const struct stmmac_mode_ops chain_mode_ops;
#endif /* __COMMON_H__ */

View file

@ -0,0 +1,219 @@
/*******************************************************************************
Header File to describe the DMA descriptors.
Enhanced descriptors have been in case of DWMAC1000 Cores.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
#ifndef __DESCS_H__
#define __DESCS_H__
/* Basic descriptor structure for normal and alternate descriptors */
struct dma_desc {
/* Receive descriptor */
union {
struct {
/* RDES0 */
u32 payload_csum_error:1;
u32 crc_error:1;
u32 dribbling:1;
u32 mii_error:1;
u32 receive_watchdog:1;
u32 frame_type:1;
u32 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:11;
u32 buffer2_size:11;
u32 reserved1:2;
u32 second_address_chained:1;
u32 end_ring:1;
u32 reserved2:5;
u32 disable_ic:1;
} rx;
struct {
/* RDES0 */
u32 rx_mac_addr: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 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:13;
u32 own:1;
/* TDES1 */
u32 buffer1_size:11;
u32 buffer2_size:11;
u32 time_stamp_enable:1;
u32 disable_padding:1;
u32 second_address_chained:1;
u32 end_ring:1;
u32 crc_disable:1;
u32 checksum_insertion: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;
};
/* Extended descriptor structure (supported by new SYNP GMAC generations) */
struct dma_extended_desc {
struct dma_desc basic;
union {
struct {
u32 ip_payload_type:3;
u32 ip_hdr_err:1;
u32 ip_payload_err:1;
u32 ip_csum_bypassed:1;
u32 ipv4_pkt_rcvd:1;
u32 ipv6_pkt_rcvd:1;
u32 msg_type:4;
u32 ptp_frame_type:1;
u32 ptp_ver:1;
u32 timestamp_dropped:1;
u32 reserved:1;
u32 av_pkt_rcvd:1;
u32 av_tagged_pkt_rcvd:1;
u32 vlan_tag_priority_val:3;
u32 reserved3:3;
u32 l3_filter_match:1;
u32 l4_filter_match:1;
u32 l3_l4_filter_no_match:2;
u32 reserved4:4;
} erx;
struct {
u32 reserved;
} etx;
} des4;
unsigned int des5; /* Reserved */
unsigned int des6; /* Tx/Rx Timestamp Low */
unsigned int des7; /* Tx/Rx Timestamp High */
};
/* Transmit checksum insertion control */
enum tdes_csum_insertion {
cic_disabled = 0, /* Checksum Insertion Control */
cic_only_ip = 1, /* Only IP header */
/* IP header but pseudoheader is not calculated */
cic_no_pseudoheader = 2,
cic_full = 3, /* IP header and pseudoheader */
};
/* Extended RDES4 definitions */
#define RDES_EXT_NO_PTP 0
#define RDES_EXT_SYNC 0x1
#define RDES_EXT_FOLLOW_UP 0x2
#define RDES_EXT_DELAY_REQ 0x3
#define RDES_EXT_DELAY_RESP 0x4
#define RDES_EXT_PDELAY_REQ 0x5
#define RDES_EXT_PDELAY_RESP 0x6
#define RDES_EXT_PDELAY_FOLLOW_UP 0x7
#endif /* __DESCS_H__ */

View file

@ -0,0 +1,134 @@
/*******************************************************************************
Header File to describe Normal/enhanced descriptor functions used for RING
and CHAINED modes.
Copyright(C) 2011 STMicroelectronics Ltd
It defines all the functions used to handle the normal/enhanced
descriptors in case of the DMA is configured to work in chained or
in ring mode.
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>
*******************************************************************************/
#ifndef __DESC_COM_H__
#define __DESC_COM_H__
/* Specific functions used for Ring mode */
/* Enhanced descriptors */
static inline void ehn_desc_rx_set_on_ring(struct dma_desc *p, int end)
{
p->des01.erx.buffer2_size = BUF_SIZE_8KiB - 1;
if (end)
p->des01.erx.end_ring = 1;
}
static inline void ehn_desc_tx_set_on_ring(struct dma_desc *p, int end)
{
if (end)
p->des01.etx.end_ring = 1;
}
static inline void enh_desc_end_tx_desc_on_ring(struct dma_desc *p, int ter)
{
p->des01.etx.end_ring = ter;
}
static inline void enh_set_tx_desc_len_on_ring(struct dma_desc *p, int len)
{
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;
}
/* Normal descriptors */
static inline void ndesc_rx_set_on_ring(struct dma_desc *p, int end)
{
p->des01.rx.buffer2_size = BUF_SIZE_2KiB - 1;
if (end)
p->des01.rx.end_ring = 1;
}
static inline void ndesc_tx_set_on_ring(struct dma_desc *p, int end)
{
if (end)
p->des01.tx.end_ring = 1;
}
static inline void ndesc_end_tx_desc_on_ring(struct dma_desc *p, int ter)
{
p->des01.tx.end_ring = ter;
}
static inline void norm_set_tx_desc_len_on_ring(struct dma_desc *p, int len)
{
if (unlikely(len > BUF_SIZE_2KiB)) {
p->des01.etx.buffer1_size = BUF_SIZE_2KiB - 1;
p->des01.etx.buffer2_size = len - p->des01.etx.buffer1_size;
} else
p->des01.tx.buffer1_size = len;
}
/* Specific functions used for Chain mode */
/* Enhanced descriptors */
static inline void ehn_desc_rx_set_on_chain(struct dma_desc *p, int end)
{
p->des01.erx.second_address_chained = 1;
}
static inline void ehn_desc_tx_set_on_chain(struct dma_desc *p, int end)
{
p->des01.etx.second_address_chained = 1;
}
static inline void enh_desc_end_tx_desc_on_chain(struct dma_desc *p, int ter)
{
p->des01.etx.second_address_chained = 1;
}
static inline void enh_set_tx_desc_len_on_chain(struct dma_desc *p, int len)
{
p->des01.etx.buffer1_size = len;
}
/* Normal descriptors */
static inline void ndesc_rx_set_on_chain(struct dma_desc *p, int end)
{
p->des01.rx.second_address_chained = 1;
}
static inline void ndesc_tx_set_on_chain(struct dma_desc *p, int ring_size)
{
p->des01.tx.second_address_chained = 1;
}
static inline void ndesc_end_tx_desc_on_chain(struct dma_desc *p, int ter)
{
p->des01.tx.second_address_chained = 1;
}
static inline void norm_set_tx_desc_len_on_chain(struct dma_desc *p, int len)
{
p->des01.tx.buffer1_size = len;
}
#endif /* __DESC_COM_H__ */

View file

@ -0,0 +1,67 @@
/*
* Amlogic Meson DWMAC glue layer
*
* Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/device.h>
#include <linux/ethtool.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/stmmac.h>
#define ETHMAC_SPEED_100 BIT(1)
struct meson_dwmac {
struct device *dev;
void __iomem *reg;
};
static void meson6_dwmac_fix_mac_speed(void *priv, unsigned int speed)
{
struct meson_dwmac *dwmac = priv;
unsigned int val;
val = readl(dwmac->reg);
switch (speed) {
case SPEED_10:
val &= ~ETHMAC_SPEED_100;
break;
case SPEED_100:
val |= ETHMAC_SPEED_100;
break;
}
writel(val, dwmac->reg);
}
static void *meson6_dwmac_setup(struct platform_device *pdev)
{
struct meson_dwmac *dwmac;
struct resource *res;
dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
if (!dwmac)
return ERR_PTR(-ENOMEM);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
dwmac->reg = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(dwmac->reg))
return dwmac->reg;
return dwmac;
}
const struct stmmac_of_data meson6_dwmac_data = {
.setup = meson6_dwmac_setup,
.fix_mac_speed = meson6_dwmac_fix_mac_speed,
};

View file

@ -0,0 +1,261 @@
/* Copyright Altera Corporation (C) 2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that 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, see <http://www.gnu.org/licenses/>.
*
* Adopted from dwmac-sti.c
*/
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_net.h>
#include <linux/phy.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/stmmac.h>
#include "stmmac.h"
#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII 0x0
#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII 0x1
#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII 0x2
#define SYSMGR_EMACGRP_CTRL_PHYSEL_WIDTH 2
#define SYSMGR_EMACGRP_CTRL_PHYSEL_MASK 0x00000003
#define EMAC_SPLITTER_CTRL_REG 0x0
#define EMAC_SPLITTER_CTRL_SPEED_MASK 0x3
#define EMAC_SPLITTER_CTRL_SPEED_10 0x2
#define EMAC_SPLITTER_CTRL_SPEED_100 0x3
#define EMAC_SPLITTER_CTRL_SPEED_1000 0x0
struct socfpga_dwmac {
int interface;
u32 reg_offset;
u32 reg_shift;
struct device *dev;
struct regmap *sys_mgr_base_addr;
struct reset_control *stmmac_rst;
void __iomem *splitter_base;
};
static void socfpga_dwmac_fix_mac_speed(void *priv, unsigned int speed)
{
struct socfpga_dwmac *dwmac = (struct socfpga_dwmac *)priv;
void __iomem *splitter_base = dwmac->splitter_base;
u32 val;
if (!splitter_base)
return;
val = readl(splitter_base + EMAC_SPLITTER_CTRL_REG);
val &= ~EMAC_SPLITTER_CTRL_SPEED_MASK;
switch (speed) {
case 1000:
val |= EMAC_SPLITTER_CTRL_SPEED_1000;
break;
case 100:
val |= EMAC_SPLITTER_CTRL_SPEED_100;
break;
case 10:
val |= EMAC_SPLITTER_CTRL_SPEED_10;
break;
default:
return;
}
writel(val, splitter_base + EMAC_SPLITTER_CTRL_REG);
}
static int socfpga_dwmac_parse_data(struct socfpga_dwmac *dwmac, struct device *dev)
{
struct device_node *np = dev->of_node;
struct regmap *sys_mgr_base_addr;
u32 reg_offset, reg_shift;
int ret;
struct device_node *np_splitter;
struct resource res_splitter;
dwmac->stmmac_rst = devm_reset_control_get(dev,
STMMAC_RESOURCE_NAME);
if (IS_ERR(dwmac->stmmac_rst)) {
dev_info(dev, "Could not get reset control!\n");
return -EINVAL;
}
dwmac->interface = of_get_phy_mode(np);
sys_mgr_base_addr = syscon_regmap_lookup_by_phandle(np, "altr,sysmgr-syscon");
if (IS_ERR(sys_mgr_base_addr)) {
dev_info(dev, "No sysmgr-syscon node found\n");
return PTR_ERR(sys_mgr_base_addr);
}
ret = of_property_read_u32_index(np, "altr,sysmgr-syscon", 1, &reg_offset);
if (ret) {
dev_info(dev, "Could not read reg_offset from sysmgr-syscon!\n");
return -EINVAL;
}
ret = of_property_read_u32_index(np, "altr,sysmgr-syscon", 2, &reg_shift);
if (ret) {
dev_info(dev, "Could not read reg_shift from sysmgr-syscon!\n");
return -EINVAL;
}
np_splitter = of_parse_phandle(np, "altr,emac-splitter", 0);
if (np_splitter) {
if (of_address_to_resource(np_splitter, 0, &res_splitter)) {
dev_info(dev, "Missing emac splitter address\n");
return -EINVAL;
}
dwmac->splitter_base = devm_ioremap_resource(dev, &res_splitter);
if (IS_ERR(dwmac->splitter_base)) {
dev_info(dev, "Failed to mapping emac splitter\n");
return PTR_ERR(dwmac->splitter_base);
}
}
dwmac->reg_offset = reg_offset;
dwmac->reg_shift = reg_shift;
dwmac->sys_mgr_base_addr = sys_mgr_base_addr;
dwmac->dev = dev;
return 0;
}
static int socfpga_dwmac_setup(struct socfpga_dwmac *dwmac)
{
struct regmap *sys_mgr_base_addr = dwmac->sys_mgr_base_addr;
int phymode = dwmac->interface;
u32 reg_offset = dwmac->reg_offset;
u32 reg_shift = dwmac->reg_shift;
u32 ctrl, val;
switch (phymode) {
case PHY_INTERFACE_MODE_RGMII:
case PHY_INTERFACE_MODE_RGMII_ID:
val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII;
break;
case PHY_INTERFACE_MODE_MII:
case PHY_INTERFACE_MODE_GMII:
val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII;
break;
default:
dev_err(dwmac->dev, "bad phy mode %d\n", phymode);
return -EINVAL;
}
/* Overwrite val to GMII if splitter core is enabled. The phymode here
* is the actual phy mode on phy hardware, but phy interface from
* EMAC core is GMII.
*/
if (dwmac->splitter_base)
val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII;
regmap_read(sys_mgr_base_addr, reg_offset, &ctrl);
ctrl &= ~(SYSMGR_EMACGRP_CTRL_PHYSEL_MASK << reg_shift);
ctrl |= val << reg_shift;
regmap_write(sys_mgr_base_addr, reg_offset, ctrl);
return 0;
}
static void *socfpga_dwmac_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
int ret;
struct socfpga_dwmac *dwmac;
dwmac = devm_kzalloc(dev, sizeof(*dwmac), GFP_KERNEL);
if (!dwmac)
return ERR_PTR(-ENOMEM);
ret = socfpga_dwmac_parse_data(dwmac, dev);
if (ret) {
dev_err(dev, "Unable to parse OF data\n");
return ERR_PTR(ret);
}
ret = socfpga_dwmac_setup(dwmac);
if (ret) {
dev_err(dev, "couldn't setup SoC glue (%d)\n", ret);
return ERR_PTR(ret);
}
return dwmac;
}
static void socfpga_dwmac_exit(struct platform_device *pdev, void *priv)
{
struct socfpga_dwmac *dwmac = priv;
/* On socfpga platform exit, assert and hold reset to the
* enet controller - the default state after a hard reset.
*/
if (dwmac->stmmac_rst)
reset_control_assert(dwmac->stmmac_rst);
}
static int socfpga_dwmac_init(struct platform_device *pdev, void *priv)
{
struct socfpga_dwmac *dwmac = priv;
struct net_device *ndev = platform_get_drvdata(pdev);
struct stmmac_priv *stpriv = NULL;
int ret = 0;
if (ndev)
stpriv = netdev_priv(ndev);
/* Assert reset to the enet controller before changing the phy mode */
if (dwmac->stmmac_rst)
reset_control_assert(dwmac->stmmac_rst);
/* Setup the phy mode in the system manager registers according to
* devicetree configuration
*/
ret = socfpga_dwmac_setup(dwmac);
/* Deassert reset for the phy configuration to be sampled by
* the enet controller, and operation to start in requested mode
*/
if (dwmac->stmmac_rst)
reset_control_deassert(dwmac->stmmac_rst);
/* Before the enet controller is suspended, the phy is suspended.
* This causes the phy clock to be gated. The enet controller is
* resumed before the phy, so the clock is still gated "off" when
* the enet controller is resumed. This code makes sure the phy
* is "resumed" before reinitializing the enet controller since
* the enet controller depends on an active phy clock to complete
* a DMA reset. A DMA reset will "time out" if executed
* with no phy clock input on the Synopsys enet controller.
* Verified through Synopsys Case #8000711656.
*
* Note that the phy clock is also gated when the phy is isolated.
* Phy "suspend" and "isolate" controls are located in phy basic
* control register 0, and can be modified by the phy driver
* framework.
*/
if (stpriv && stpriv->phydev)
phy_resume(stpriv->phydev);
return ret;
}
const struct stmmac_of_data socfpga_gmac_data = {
.setup = socfpga_dwmac_probe,
.init = socfpga_dwmac_init,
.exit = socfpga_dwmac_exit,
.fix_mac_speed = socfpga_dwmac_fix_mac_speed,
};

View file

@ -0,0 +1,366 @@
/**
* dwmac-sti.c - STMicroelectronics DWMAC Specific Glue layer
*
* Copyright (C) 2003-2014 STMicroelectronics (R&D) Limited
* Author: Srinivas Kandagatla <srinivas.kandagatla@st.com>
* Contributors: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/stmmac.h>
#include <linux/phy.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/of_net.h>
#define DWMAC_125MHZ 125000000
#define DWMAC_50MHZ 50000000
#define DWMAC_25MHZ 25000000
#define DWMAC_2_5MHZ 2500000
#define IS_PHY_IF_MODE_RGMII(iface) (iface == PHY_INTERFACE_MODE_RGMII || \
iface == PHY_INTERFACE_MODE_RGMII_ID || \
iface == PHY_INTERFACE_MODE_RGMII_RXID || \
iface == PHY_INTERFACE_MODE_RGMII_TXID)
#define IS_PHY_IF_MODE_GBIT(iface) (IS_PHY_IF_MODE_RGMII(iface) || \
iface == PHY_INTERFACE_MODE_GMII)
/* STiH4xx register definitions (STiH415/STiH416/STiH407/STiH410 families) */
/**
* Below table summarizes the clock requirement and clock sources for
* supported phy interface modes with link speeds.
* ________________________________________________
*| PHY_MODE | 1000 Mbit Link | 100 Mbit Link |
* ------------------------------------------------
*| MII | n/a | 25Mhz |
*| | | txclk |
* ------------------------------------------------
*| GMII | 125Mhz | 25Mhz |
*| | clk-125/txclk | txclk |
* ------------------------------------------------
*| RGMII | 125Mhz | 25Mhz |
*| | clk-125/txclk | clkgen |
*| | clkgen | |
* ------------------------------------------------
*| RMII | n/a | 25Mhz |
*| | |clkgen/phyclk-in |
* ------------------------------------------------
*
* Register Configuration
*-------------------------------
* src |BIT(8)| BIT(7)| BIT(6)|
*-------------------------------
* txclk | 0 | n/a | 1 |
*-------------------------------
* ck_125| 0 | n/a | 0 |
*-------------------------------
* phyclk| 1 | 0 | n/a |
*-------------------------------
* clkgen| 1 | 1 | n/a |
*-------------------------------
*/
#define STIH4XX_RETIME_SRC_MASK GENMASK(8, 6)
#define STIH4XX_ETH_SEL_TX_RETIME_CLK BIT(8)
#define STIH4XX_ETH_SEL_INTERNAL_NOTEXT_PHYCLK BIT(7)
#define STIH4XX_ETH_SEL_TXCLK_NOT_CLK125 BIT(6)
/* STiD127 register definitions */
/**
*-----------------------
* src |BIT(6)| BIT(7)|
*-----------------------
* MII | 1 | n/a |
*-----------------------
* RMII | n/a | 1 |
* clkgen| | |
*-----------------------
* RMII | n/a | 0 |
* phyclk| | |
*-----------------------
* RGMII | 1 | n/a |
* clkgen| | |
*-----------------------
*/
#define STID127_RETIME_SRC_MASK GENMASK(7, 6)
#define STID127_ETH_SEL_INTERNAL_NOTEXT_PHYCLK BIT(7)
#define STID127_ETH_SEL_INTERNAL_NOTEXT_TXCLK BIT(6)
#define ENMII_MASK GENMASK(5, 5)
#define ENMII BIT(5)
#define EN_MASK GENMASK(1, 1)
#define EN BIT(1)
/**
* 3 bits [4:2]
* 000-GMII/MII
* 001-RGMII
* 010-SGMII
* 100-RMII
*/
#define MII_PHY_SEL_MASK GENMASK(4, 2)
#define ETH_PHY_SEL_RMII BIT(4)
#define ETH_PHY_SEL_SGMII BIT(3)
#define ETH_PHY_SEL_RGMII BIT(2)
#define ETH_PHY_SEL_GMII 0x0
#define ETH_PHY_SEL_MII 0x0
struct sti_dwmac {
int interface; /* MII interface */
bool ext_phyclk; /* Clock from external PHY */
u32 tx_retime_src; /* TXCLK Retiming*/
struct clk *clk; /* PHY clock */
int ctrl_reg; /* GMAC glue-logic control register */
int clk_sel_reg; /* GMAC ext clk selection register */
struct device *dev;
struct regmap *regmap;
u32 speed;
};
static u32 phy_intf_sels[] = {
[PHY_INTERFACE_MODE_MII] = ETH_PHY_SEL_MII,
[PHY_INTERFACE_MODE_GMII] = ETH_PHY_SEL_GMII,
[PHY_INTERFACE_MODE_RGMII] = ETH_PHY_SEL_RGMII,
[PHY_INTERFACE_MODE_RGMII_ID] = ETH_PHY_SEL_RGMII,
[PHY_INTERFACE_MODE_SGMII] = ETH_PHY_SEL_SGMII,
[PHY_INTERFACE_MODE_RMII] = ETH_PHY_SEL_RMII,
};
enum {
TX_RETIME_SRC_NA = 0,
TX_RETIME_SRC_TXCLK = 1,
TX_RETIME_SRC_CLK_125,
TX_RETIME_SRC_PHYCLK,
TX_RETIME_SRC_CLKGEN,
};
static u32 stih4xx_tx_retime_val[] = {
[TX_RETIME_SRC_TXCLK] = STIH4XX_ETH_SEL_TXCLK_NOT_CLK125,
[TX_RETIME_SRC_CLK_125] = 0x0,
[TX_RETIME_SRC_PHYCLK] = STIH4XX_ETH_SEL_TX_RETIME_CLK,
[TX_RETIME_SRC_CLKGEN] = STIH4XX_ETH_SEL_TX_RETIME_CLK
| STIH4XX_ETH_SEL_INTERNAL_NOTEXT_PHYCLK,
};
static void stih4xx_fix_retime_src(void *priv, u32 spd)
{
struct sti_dwmac *dwmac = priv;
u32 src = dwmac->tx_retime_src;
u32 reg = dwmac->ctrl_reg;
u32 freq = 0;
if (dwmac->interface == PHY_INTERFACE_MODE_MII) {
src = TX_RETIME_SRC_TXCLK;
} else if (dwmac->interface == PHY_INTERFACE_MODE_RMII) {
if (dwmac->ext_phyclk) {
src = TX_RETIME_SRC_PHYCLK;
} else {
src = TX_RETIME_SRC_CLKGEN;
freq = DWMAC_50MHZ;
}
} else if (IS_PHY_IF_MODE_RGMII(dwmac->interface)) {
/* On GiGa clk source can be either ext or from clkgen */
if (spd == SPEED_1000) {
freq = DWMAC_125MHZ;
} else {
/* Switch to clkgen for these speeds */
src = TX_RETIME_SRC_CLKGEN;
if (spd == SPEED_100)
freq = DWMAC_25MHZ;
else if (spd == SPEED_10)
freq = DWMAC_2_5MHZ;
}
}
if (src == TX_RETIME_SRC_CLKGEN && dwmac->clk && freq)
clk_set_rate(dwmac->clk, freq);
regmap_update_bits(dwmac->regmap, reg, STIH4XX_RETIME_SRC_MASK,
stih4xx_tx_retime_val[src]);
}
static void stid127_fix_retime_src(void *priv, u32 spd)
{
struct sti_dwmac *dwmac = priv;
u32 reg = dwmac->ctrl_reg;
u32 freq = 0;
u32 val = 0;
if (dwmac->interface == PHY_INTERFACE_MODE_MII) {
val = STID127_ETH_SEL_INTERNAL_NOTEXT_TXCLK;
} else if (dwmac->interface == PHY_INTERFACE_MODE_RMII) {
if (!dwmac->ext_phyclk) {
val = STID127_ETH_SEL_INTERNAL_NOTEXT_PHYCLK;
freq = DWMAC_50MHZ;
}
} else if (IS_PHY_IF_MODE_RGMII(dwmac->interface)) {
val = STID127_ETH_SEL_INTERNAL_NOTEXT_TXCLK;
if (spd == SPEED_1000)
freq = DWMAC_125MHZ;
else if (spd == SPEED_100)
freq = DWMAC_25MHZ;
else if (spd == SPEED_10)
freq = DWMAC_2_5MHZ;
}
if (dwmac->clk && freq)
clk_set_rate(dwmac->clk, freq);
regmap_update_bits(dwmac->regmap, reg, STID127_RETIME_SRC_MASK, val);
}
static void sti_dwmac_ctrl_init(struct sti_dwmac *dwmac)
{
struct regmap *regmap = dwmac->regmap;
int iface = dwmac->interface;
struct device *dev = dwmac->dev;
struct device_node *np = dev->of_node;
u32 reg = dwmac->ctrl_reg;
u32 val;
if (dwmac->clk)
clk_prepare_enable(dwmac->clk);
if (of_property_read_bool(np, "st,gmac_en"))
regmap_update_bits(regmap, reg, EN_MASK, EN);
regmap_update_bits(regmap, reg, MII_PHY_SEL_MASK, phy_intf_sels[iface]);
val = (iface == PHY_INTERFACE_MODE_REVMII) ? 0 : ENMII;
regmap_update_bits(regmap, reg, ENMII_MASK, val);
}
static int stix4xx_init(struct platform_device *pdev, void *priv)
{
struct sti_dwmac *dwmac = priv;
u32 spd = dwmac->speed;
sti_dwmac_ctrl_init(dwmac);
stih4xx_fix_retime_src(priv, spd);
return 0;
}
static int stid127_init(struct platform_device *pdev, void *priv)
{
struct sti_dwmac *dwmac = priv;
u32 spd = dwmac->speed;
sti_dwmac_ctrl_init(dwmac);
stid127_fix_retime_src(priv, spd);
return 0;
}
static void sti_dwmac_exit(struct platform_device *pdev, void *priv)
{
struct sti_dwmac *dwmac = priv;
if (dwmac->clk)
clk_disable_unprepare(dwmac->clk);
}
static int sti_dwmac_parse_data(struct sti_dwmac *dwmac,
struct platform_device *pdev)
{
struct resource *res;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct regmap *regmap;
int err;
if (!np)
return -EINVAL;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sti-ethconf");
if (!res)
return -ENODATA;
dwmac->ctrl_reg = res->start;
/* clk selection from extra syscfg register */
dwmac->clk_sel_reg = -ENXIO;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sti-clkconf");
if (res)
dwmac->clk_sel_reg = res->start;
regmap = syscon_regmap_lookup_by_phandle(np, "st,syscon");
if (IS_ERR(regmap))
return PTR_ERR(regmap);
dwmac->dev = dev;
dwmac->interface = of_get_phy_mode(np);
dwmac->regmap = regmap;
dwmac->ext_phyclk = of_property_read_bool(np, "st,ext-phyclk");
dwmac->tx_retime_src = TX_RETIME_SRC_NA;
dwmac->speed = SPEED_100;
if (IS_PHY_IF_MODE_GBIT(dwmac->interface)) {
const char *rs;
dwmac->tx_retime_src = TX_RETIME_SRC_CLKGEN;
err = of_property_read_string(np, "st,tx-retime-src", &rs);
if (err < 0)
dev_warn(dev, "Use internal clock source\n");
if (!strcasecmp(rs, "clk_125"))
dwmac->tx_retime_src = TX_RETIME_SRC_CLK_125;
else if (!strcasecmp(rs, "txclk"))
dwmac->tx_retime_src = TX_RETIME_SRC_TXCLK;
dwmac->speed = SPEED_1000;
}
dwmac->clk = devm_clk_get(dev, "sti-ethclk");
if (IS_ERR(dwmac->clk)) {
dev_warn(dev, "No phy clock provided...\n");
dwmac->clk = NULL;
}
return 0;
}
static void *sti_dwmac_setup(struct platform_device *pdev)
{
struct sti_dwmac *dwmac;
int ret;
dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
if (!dwmac)
return ERR_PTR(-ENOMEM);
ret = sti_dwmac_parse_data(dwmac, pdev);
if (ret) {
dev_err(&pdev->dev, "Unable to parse OF data\n");
return ERR_PTR(ret);
}
return dwmac;
}
const struct stmmac_of_data stih4xx_dwmac_data = {
.fix_mac_speed = stih4xx_fix_retime_src,
.setup = sti_dwmac_setup,
.init = stix4xx_init,
.exit = sti_dwmac_exit,
};
const struct stmmac_of_data stid127_dwmac_data = {
.fix_mac_speed = stid127_fix_retime_src,
.setup = sti_dwmac_setup,
.init = stid127_init,
.exit = sti_dwmac_exit,
};

View file

@ -0,0 +1,140 @@
/**
* dwmac-sunxi.c - Allwinner sunxi DWMAC specific glue layer
*
* Copyright (C) 2013 Chen-Yu Tsai
*
* Chen-Yu Tsai <wens@csie.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that 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.
*/
#include <linux/stmmac.h>
#include <linux/clk.h>
#include <linux/phy.h>
#include <linux/of_net.h>
#include <linux/regulator/consumer.h>
struct sunxi_priv_data {
int interface;
int clk_enabled;
struct clk *tx_clk;
struct regulator *regulator;
};
static void *sun7i_gmac_setup(struct platform_device *pdev)
{
struct sunxi_priv_data *gmac;
struct device *dev = &pdev->dev;
gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL);
if (!gmac)
return ERR_PTR(-ENOMEM);
gmac->interface = of_get_phy_mode(dev->of_node);
gmac->tx_clk = devm_clk_get(dev, "allwinner_gmac_tx");
if (IS_ERR(gmac->tx_clk)) {
dev_err(dev, "could not get tx clock\n");
return gmac->tx_clk;
}
/* Optional regulator for PHY */
gmac->regulator = devm_regulator_get_optional(dev, "phy");
if (IS_ERR(gmac->regulator)) {
if (PTR_ERR(gmac->regulator) == -EPROBE_DEFER)
return ERR_PTR(-EPROBE_DEFER);
dev_info(dev, "no regulator found\n");
gmac->regulator = NULL;
}
return gmac;
}
#define SUN7I_GMAC_GMII_RGMII_RATE 125000000
#define SUN7I_GMAC_MII_RATE 25000000
static int sun7i_gmac_init(struct platform_device *pdev, void *priv)
{
struct sunxi_priv_data *gmac = priv;
int ret;
if (gmac->regulator) {
ret = regulator_enable(gmac->regulator);
if (ret)
return ret;
}
/* Set GMAC interface port mode
*
* The GMAC TX clock lines are configured by setting the clock
* rate, which then uses the auto-reparenting feature of the
* clock driver, and enabling/disabling the clock.
*/
if (gmac->interface == PHY_INTERFACE_MODE_RGMII) {
clk_set_rate(gmac->tx_clk, SUN7I_GMAC_GMII_RGMII_RATE);
clk_prepare_enable(gmac->tx_clk);
gmac->clk_enabled = 1;
} else {
clk_set_rate(gmac->tx_clk, SUN7I_GMAC_MII_RATE);
clk_prepare(gmac->tx_clk);
}
return 0;
}
static void sun7i_gmac_exit(struct platform_device *pdev, void *priv)
{
struct sunxi_priv_data *gmac = priv;
if (gmac->clk_enabled) {
clk_disable(gmac->tx_clk);
gmac->clk_enabled = 0;
}
clk_unprepare(gmac->tx_clk);
if (gmac->regulator)
regulator_disable(gmac->regulator);
}
static void sun7i_fix_speed(void *priv, unsigned int speed)
{
struct sunxi_priv_data *gmac = priv;
/* only GMII mode requires us to reconfigure the clock lines */
if (gmac->interface != PHY_INTERFACE_MODE_GMII)
return;
if (gmac->clk_enabled) {
clk_disable(gmac->tx_clk);
gmac->clk_enabled = 0;
}
clk_unprepare(gmac->tx_clk);
if (speed == 1000) {
clk_set_rate(gmac->tx_clk, SUN7I_GMAC_GMII_RGMII_RATE);
clk_prepare_enable(gmac->tx_clk);
gmac->clk_enabled = 1;
} else {
clk_set_rate(gmac->tx_clk, SUN7I_GMAC_MII_RATE);
clk_prepare(gmac->tx_clk);
}
}
/* of_data specifying hardware features and callbacks.
* hardware features were copied from Allwinner drivers. */
const struct stmmac_of_data sun7i_gmac_data = {
.has_gmac = 1,
.tx_coe = 1,
.fix_mac_speed = sun7i_fix_speed,
.setup = sun7i_gmac_setup,
.init = sun7i_gmac_init,
.exit = sun7i_gmac_exit,
};

View file

@ -0,0 +1,126 @@
/*******************************************************************************
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>
*******************************************************************************/
#ifndef __DWMAC100_H__
#define __DWMAC100_H__
#include <linux/phy.h>
#include "common.h"
/*----------------------------------------------------------------------------
* MAC BLOCK defines
*---------------------------------------------------------------------------*/
/* MAC CSR offset */
#define MAC_CONTROL 0x00000000 /* MAC Control */
#define MAC_ADDR_HIGH 0x00000004 /* MAC Address High */
#define MAC_ADDR_LOW 0x00000008 /* MAC Address Low */
#define MAC_HASH_HIGH 0x0000000c /* Multicast Hash Table High */
#define MAC_HASH_LOW 0x00000010 /* Multicast Hash Table Low */
#define MAC_MII_ADDR 0x00000014 /* MII Address */
#define MAC_MII_DATA 0x00000018 /* MII Data */
#define MAC_FLOW_CTRL 0x0000001c /* Flow Control */
#define MAC_VLAN1 0x00000020 /* VLAN1 Tag */
#define MAC_VLAN2 0x00000024 /* VLAN2 Tag */
/* MAC CTRL defines */
#define MAC_CONTROL_RA 0x80000000 /* Receive All Mode */
#define MAC_CONTROL_BLE 0x40000000 /* Endian Mode */
#define MAC_CONTROL_HBD 0x10000000 /* Heartbeat Disable */
#define MAC_CONTROL_PS 0x08000000 /* Port Select */
#define MAC_CONTROL_DRO 0x00800000 /* Disable Receive Own */
#define MAC_CONTROL_EXT_LOOPBACK 0x00400000 /* Reserved (ext loopback?) */
#define MAC_CONTROL_OM 0x00200000 /* Loopback Operating Mode */
#define MAC_CONTROL_F 0x00100000 /* Full Duplex Mode */
#define MAC_CONTROL_PM 0x00080000 /* Pass All Multicast */
#define MAC_CONTROL_PR 0x00040000 /* Promiscuous Mode */
#define MAC_CONTROL_IF 0x00020000 /* Inverse Filtering */
#define MAC_CONTROL_PB 0x00010000 /* Pass Bad Frames */
#define MAC_CONTROL_HO 0x00008000 /* Hash Only Filtering Mode */
#define MAC_CONTROL_HP 0x00002000 /* Hash/Perfect Filtering Mode */
#define MAC_CONTROL_LCC 0x00001000 /* Late Collision Control */
#define MAC_CONTROL_DBF 0x00000800 /* Disable Broadcast Frames */
#define MAC_CONTROL_DRTY 0x00000400 /* Disable Retry */
#define MAC_CONTROL_ASTP 0x00000100 /* Automatic Pad Stripping */
#define MAC_CONTROL_BOLMT_10 0x00000000 /* Back Off Limit 10 */
#define MAC_CONTROL_BOLMT_8 0x00000040 /* Back Off Limit 8 */
#define MAC_CONTROL_BOLMT_4 0x00000080 /* Back Off Limit 4 */
#define MAC_CONTROL_BOLMT_1 0x000000c0 /* Back Off Limit 1 */
#define MAC_CONTROL_DC 0x00000020 /* Deferral Check */
#define MAC_CONTROL_TE 0x00000008 /* Transmitter Enable */
#define MAC_CONTROL_RE 0x00000004 /* Receiver Enable */
#define MAC_CORE_INIT (MAC_CONTROL_HBD | MAC_CONTROL_ASTP)
/* MAC FLOW CTRL defines */
#define MAC_FLOW_CTRL_PT_MASK 0xffff0000 /* Pause Time Mask */
#define MAC_FLOW_CTRL_PT_SHIFT 16
#define MAC_FLOW_CTRL_PASS 0x00000004 /* Pass Control Frames */
#define MAC_FLOW_CTRL_ENABLE 0x00000002 /* Flow Control Enable */
#define MAC_FLOW_CTRL_PAUSE 0x00000001 /* Flow Control Busy ... */
/* MII ADDR defines */
#define MAC_MII_ADDR_WRITE 0x00000002 /* MII Write */
#define MAC_MII_ADDR_BUSY 0x00000001 /* MII Busy */
/*----------------------------------------------------------------------------
* DMA BLOCK defines
*---------------------------------------------------------------------------*/
/* DMA Bus Mode register defines */
#define DMA_BUS_MODE_DBO 0x00100000 /* Descriptor Byte Ordering */
#define DMA_BUS_MODE_BLE 0x00000080 /* Big Endian/Little Endian */
#define DMA_BUS_MODE_PBL_MASK 0x00003f00 /* Programmable Burst Len */
#define DMA_BUS_MODE_PBL_SHIFT 8
#define DMA_BUS_MODE_DSL_MASK 0x0000007c /* Descriptor Skip Length */
#define DMA_BUS_MODE_DSL_SHIFT 2 /* (in DWORDS) */
#define DMA_BUS_MODE_BAR_BUS 0x00000002 /* Bar-Bus Arbitration */
#define DMA_BUS_MODE_SFT_RESET 0x00000001 /* Software Reset */
#define DMA_BUS_MODE_DEFAULT 0x00000000
/* DMA Control register defines */
#define DMA_CONTROL_SF 0x00200000 /* Store And Forward */
/* Transmit Threshold Control */
enum ttc_control {
DMA_CONTROL_TTC_DEFAULT = 0x00000000, /* Threshold is 32 DWORDS */
DMA_CONTROL_TTC_64 = 0x00004000, /* Threshold is 64 DWORDS */
DMA_CONTROL_TTC_128 = 0x00008000, /* Threshold is 128 DWORDS */
DMA_CONTROL_TTC_256 = 0x0000c000, /* Threshold is 256 DWORDS */
DMA_CONTROL_TTC_18 = 0x00400000, /* Threshold is 18 DWORDS */
DMA_CONTROL_TTC_24 = 0x00404000, /* Threshold is 24 DWORDS */
DMA_CONTROL_TTC_32 = 0x00408000, /* Threshold is 32 DWORDS */
DMA_CONTROL_TTC_40 = 0x0040c000, /* Threshold is 40 DWORDS */
DMA_CONTROL_SE = 0x00000008, /* Stop On Empty */
DMA_CONTROL_OSF = 0x00000004, /* Operate On 2nd Frame */
};
/* STMAC110 DMA Missed Frame Counter register defines */
#define DMA_MISSED_FRAME_OVE 0x10000000 /* FIFO Overflow Overflow */
#define DMA_MISSED_FRAME_OVE_CNTR 0x0ffe0000 /* Overflow Frame Counter */
#define DMA_MISSED_FRAME_OVE_M 0x00010000 /* Missed Frame Overflow */
#define DMA_MISSED_FRAME_M_CNTR 0x0000ffff /* Missed Frame Couinter */
extern const struct stmmac_dma_ops dwmac100_dma_ops;
#endif /* __DWMAC100_H__ */

View file

@ -0,0 +1,267 @@
/*******************************************************************************
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>
*******************************************************************************/
#ifndef __DWMAC1000_H__
#define __DWMAC1000_H__
#include <linux/phy.h>
#include "common.h"
#define GMAC_CONTROL 0x00000000 /* Configuration */
#define GMAC_FRAME_FILTER 0x00000004 /* Frame Filter */
#define GMAC_HASH_HIGH 0x00000008 /* Multicast Hash Table High */
#define GMAC_HASH_LOW 0x0000000c /* Multicast Hash Table Low */
#define GMAC_MII_ADDR 0x00000010 /* MII Address */
#define GMAC_MII_DATA 0x00000014 /* MII Data */
#define GMAC_FLOW_CTRL 0x00000018 /* Flow Control */
#define GMAC_VLAN_TAG 0x0000001c /* VLAN Tag */
#define GMAC_VERSION 0x00000020 /* GMAC CORE Version */
#define GMAC_WAKEUP_FILTER 0x00000028 /* Wake-up Frame Filter */
#define GMAC_INT_STATUS 0x00000038 /* interrupt status register */
enum dwmac1000_irq_status {
lpiis_irq = 0x400,
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,
};
/* Energy Efficient Ethernet (EEE)
*
* LPI status, timer and control register offset
*/
#define LPI_CTRL_STATUS 0x0030
#define LPI_TIMER_CTRL 0x0034
/* LPI control and status defines */
#define LPI_CTRL_STATUS_LPITXA 0x00080000 /* Enable LPI TX Automate */
#define LPI_CTRL_STATUS_PLSEN 0x00040000 /* Enable PHY Link Status */
#define LPI_CTRL_STATUS_PLS 0x00020000 /* PHY Link Status */
#define LPI_CTRL_STATUS_LPIEN 0x00010000 /* LPI Enable */
#define LPI_CTRL_STATUS_RLPIST 0x00000200 /* Receive LPI state */
#define LPI_CTRL_STATUS_TLPIST 0x00000100 /* Transmit LPI state */
#define LPI_CTRL_STATUS_RLPIEX 0x00000008 /* Receive LPI Exit */
#define LPI_CTRL_STATUS_RLPIEN 0x00000004 /* Receive LPI Entry */
#define LPI_CTRL_STATUS_TLPIEX 0x00000002 /* Transmit LPI Exit */
#define LPI_CTRL_STATUS_TLPIEN 0x00000001 /* Transmit LPI Entry */
/* GMAC HW ADDR regs */
#define GMAC_ADDR_HIGH(reg) (((reg > 15) ? 0x00000800 : 0x00000040) + \
(reg * 8))
#define GMAC_ADDR_LOW(reg) (((reg > 15) ? 0x00000804 : 0x00000044) + \
(reg * 8))
#define GMAC_MAX_PERFECT_ADDRESSES 1
/* PCS registers (AN/TBI/SGMII/RGMII) offset */
#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_LPA 0x000000cc /* Auto-Neg. link partener ability */
#define GMAC_ANE_EXP 0x000000d0 /* ANE expansion */
#define GMAC_TBI 0x000000d4 /* TBI extend status */
#define GMAC_S_R_GMII 0x000000d8 /* SGMII RGMII status */
/* AN Configuration defines */
#define GMAC_AN_CTRL_RAN 0x00000200 /* Restart Auto-Negotiation */
#define GMAC_AN_CTRL_ANE 0x00001000 /* Auto-Negotiation Enable */
#define GMAC_AN_CTRL_ELE 0x00004000 /* External Loopback Enable */
#define GMAC_AN_CTRL_ECD 0x00010000 /* Enable Comma Detect */
#define GMAC_AN_CTRL_LR 0x00020000 /* Lock to Reference */
#define GMAC_AN_CTRL_SGMRAL 0x00040000 /* SGMII RAL Control */
/* AN Status defines */
#define GMAC_AN_STATUS_LS 0x00000004 /* Link Status 0:down 1:up */
#define GMAC_AN_STATUS_ANA 0x00000008 /* Auto-Negotiation Ability */
#define GMAC_AN_STATUS_ANC 0x00000020 /* Auto-Negotiation Complete */
#define GMAC_AN_STATUS_ES 0x00000100 /* Extended Status */
/* Register 54 (SGMII/RGMII status register) */
#define GMAC_S_R_GMII_LINK 0x8
#define GMAC_S_R_GMII_SPEED 0x5
#define GMAC_S_R_GMII_SPEED_SHIFT 0x1
#define GMAC_S_R_GMII_MODE 0x1
#define GMAC_S_R_GMII_SPEED_125 2
#define GMAC_S_R_GMII_SPEED_25 1
/* Common ADV and LPA defines */
#define GMAC_ANE_FD (1 << 5)
#define GMAC_ANE_HD (1 << 6)
#define GMAC_ANE_PSE (3 << 7)
#define GMAC_ANE_PSE_SHIFT 7
/* GMAC Configuration defines */
#define GMAC_CONTROL_2K 0x08000000 /* IEEE 802.3as 2K packets */
#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 */
#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 /* Auto Pad/FCS Stripping */
#define GMAC_CONTROL_DC 0x00000010 /* Deferral Check */
#define GMAC_CONTROL_TE 0x00000008 /* Transmitter Enable */
#define GMAC_CONTROL_RE 0x00000004 /* Receiver Enable */
#define GMAC_CORE_INIT (GMAC_CONTROL_JD | GMAC_CONTROL_PS | GMAC_CONTROL_ACS | \
GMAC_CONTROL_BE | GMAC_CONTROL_DCRS)
/* 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
#define DMA_BUS_MODE_ATDS 0x00000080 /* Alternate Descriptor Size */
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_MB 0x04000000 /* Mixed 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_PBL 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)*/
/* Disable Drop TCP/IP csum error */
#define DMA_CONTROL_DT 0x04000000
#define DMA_CONTROL_RSF 0x02000000 /* Receive Store and Forward */
#define DMA_CONTROL_DFF 0x01000000 /* Disaable flushing */
/* Threshold for Activating the FC */
enum rfa {
act_full_minus_1 = 0x00800000,
act_full_minus_2 = 0x00800200,
act_full_minus_3 = 0x00800400,
act_full_minus_4 = 0x00800600,
};
/* Threshold for Deactivating the FC */
enum rfd {
deac_full_minus_1 = 0x00400000,
deac_full_minus_2 = 0x00400800,
deac_full_minus_3 = 0x00401000,
deac_full_minus_4 = 0x00401800,
};
#define DMA_CONTROL_TSF 0x00200000 /* Transmit Store and Forward */
enum ttc_control {
DMA_CONTROL_TTC_64 = 0x00000000,
DMA_CONTROL_TTC_128 = 0x00004000,
DMA_CONTROL_TTC_192 = 0x00008000,
DMA_CONTROL_TTC_256 = 0x0000c000,
DMA_CONTROL_TTC_40 = 0x00010000,
DMA_CONTROL_TTC_32 = 0x00014000,
DMA_CONTROL_TTC_24 = 0x00018000,
DMA_CONTROL_TTC_16 = 0x0001c000,
};
#define DMA_CONTROL_TC_TX_MASK 0xfffe3fff
#define DMA_CONTROL_EFC 0x00000100
#define DMA_CONTROL_FEF 0x00000080
#define DMA_CONTROL_FUF 0x00000040
enum rtc_control {
DMA_CONTROL_RTC_64 = 0x00000000,
DMA_CONTROL_RTC_32 = 0x00000008,
DMA_CONTROL_RTC_96 = 0x00000010,
DMA_CONTROL_RTC_128 = 0x00000018,
};
#define DMA_CONTROL_TC_RX_MASK 0xffffffe7
#define DMA_CONTROL_OSF 0x00000004 /* Operate on second frame */
/* MMC registers offset */
#define GMAC_MMC_CTRL 0x100
#define GMAC_MMC_RX_INTR 0x104
#define GMAC_MMC_TX_INTR 0x108
#define GMAC_MMC_RX_CSUM_OFFLOAD 0x208
#define GMAC_EXTHASH_BASE 0x500
extern const struct stmmac_dma_ops dwmac1000_dma_ops;
#endif /* __DWMAC1000_H__ */

View file

@ -0,0 +1,444 @@
/*******************************************************************************
This is the driver for the GMAC on-chip Ethernet controller for ST SoCs.
DWC Ether MAC 10/100/1000 Universal version 3.41a has been used for
developing this code.
This only implements the mac core functions for this chip.
Copyright (C) 2007-2009 STMicroelectronics Ltd
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
#include <linux/crc32.h>
#include <linux/slab.h>
#include <linux/ethtool.h>
#include <asm/io.h>
#include "dwmac1000.h"
static void dwmac1000_core_init(struct mac_device_info *hw, int mtu)
{
void __iomem *ioaddr = hw->pcsr;
u32 value = readl(ioaddr + GMAC_CONTROL);
value |= GMAC_CORE_INIT;
if (mtu > 1500)
value |= GMAC_CONTROL_2K;
if (mtu > 2000)
value |= GMAC_CONTROL_JE;
writel(value, ioaddr + GMAC_CONTROL);
/* Mask GMAC interrupts */
writel(0x207, ioaddr + GMAC_INT_MASK);
#ifdef STMMAC_VLAN_TAG_USED
/* Tag detection without filtering */
writel(0x0, ioaddr + GMAC_VLAN_TAG);
#endif
}
static int dwmac1000_rx_ipc_enable(struct mac_device_info *hw)
{
void __iomem *ioaddr = hw->pcsr;
u32 value = readl(ioaddr + GMAC_CONTROL);
if (hw->rx_csum)
value |= GMAC_CONTROL_IPC;
else
value &= ~GMAC_CONTROL_IPC;
writel(value, ioaddr + GMAC_CONTROL);
value = readl(ioaddr + GMAC_CONTROL);
return !!(value & GMAC_CONTROL_IPC);
}
static void dwmac1000_dump_regs(struct mac_device_info *hw)
{
void __iomem *ioaddr = hw->pcsr;
int i;
pr_info("\tDWMAC1000 regs (base addr = 0x%p)\n", ioaddr);
for (i = 0; i < 55; i++) {
int offset = i * 4;
pr_info("\tReg No. %d (offset 0x%x): 0x%08x\n", i,
offset, readl(ioaddr + offset));
}
}
static void dwmac1000_set_umac_addr(struct mac_device_info *hw,
unsigned char *addr,
unsigned int reg_n)
{
void __iomem *ioaddr = hw->pcsr;
stmmac_set_mac_addr(ioaddr, addr, GMAC_ADDR_HIGH(reg_n),
GMAC_ADDR_LOW(reg_n));
}
static void dwmac1000_get_umac_addr(struct mac_device_info *hw,
unsigned char *addr,
unsigned int reg_n)
{
void __iomem *ioaddr = hw->pcsr;
stmmac_get_mac_addr(ioaddr, addr, GMAC_ADDR_HIGH(reg_n),
GMAC_ADDR_LOW(reg_n));
}
static void dwmac1000_set_mchash(void __iomem *ioaddr, u32 *mcfilterbits,
int mcbitslog2)
{
int numhashregs, regs;
switch (mcbitslog2) {
case 6:
writel(mcfilterbits[0], ioaddr + GMAC_HASH_LOW);
writel(mcfilterbits[1], ioaddr + GMAC_HASH_HIGH);
return;
break;
case 7:
numhashregs = 4;
break;
case 8:
numhashregs = 8;
break;
default:
pr_debug("STMMAC: err in setting mulitcast filter\n");
return;
break;
}
for (regs = 0; regs < numhashregs; regs++)
writel(mcfilterbits[regs],
ioaddr + GMAC_EXTHASH_BASE + regs * 4);
}
static void dwmac1000_set_filter(struct mac_device_info *hw,
struct net_device *dev)
{
void __iomem *ioaddr = (void __iomem *)dev->base_addr;
unsigned int value = 0;
unsigned int perfect_addr_number = hw->unicast_filter_entries;
u32 mc_filter[8];
int mcbitslog2 = hw->mcast_bits_log2;
pr_debug("%s: # mcasts %d, # unicast %d\n", __func__,
netdev_mc_count(dev), netdev_uc_count(dev));
memset(mc_filter, 0, sizeof(mc_filter));
if (dev->flags & IFF_PROMISC) {
value = GMAC_FRAME_FILTER_PR;
} else if (dev->flags & IFF_ALLMULTI) {
value = GMAC_FRAME_FILTER_PM; /* pass all multi */
} else if (!netdev_mc_empty(dev)) {
struct netdev_hw_addr *ha;
/* Hash filter for multicast */
value = GMAC_FRAME_FILTER_HMC;
netdev_for_each_mc_addr(ha, dev) {
/* The upper n bits of the calculated CRC are used to
* index the contents of the hash table. The number of
* bits used depends on the hardware configuration
* selected at core configuration time.
*/
int bit_nr = bitrev32(~crc32_le(~0, ha->addr,
ETH_ALEN)) >>
(32 - mcbitslog2);
/* 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);
}
}
dwmac1000_set_mchash(ioaddr, mc_filter, mcbitslog2);
/* Handle multiple unicast addresses (perfect filtering) */
if (netdev_uc_count(dev) > perfect_addr_number)
/* Switch to promiscuous mode if more than unicast
* addresses are requested than supported by hardware.
*/
value |= GMAC_FRAME_FILTER_PR;
else {
int reg = 1;
struct netdev_hw_addr *ha;
netdev_for_each_uc_addr(ha, dev) {
stmmac_set_mac_addr(ioaddr, ha->addr,
GMAC_ADDR_HIGH(reg),
GMAC_ADDR_LOW(reg));
reg++;
}
}
#ifdef FRAME_FILTER_DEBUG
/* Enable Receive all mode (to debug filtering_fail errors) */
value |= GMAC_FRAME_FILTER_RA;
#endif
writel(value, ioaddr + GMAC_FRAME_FILTER);
}
static void dwmac1000_flow_ctrl(struct mac_device_info *hw, unsigned int duplex,
unsigned int fc, unsigned int pause_time)
{
void __iomem *ioaddr = hw->pcsr;
unsigned int flow = 0;
pr_debug("GMAC Flow-Control:\n");
if (fc & FLOW_RX) {
pr_debug("\tReceive Flow-Control ON\n");
flow |= GMAC_FLOW_CTRL_RFE;
}
if (fc & FLOW_TX) {
pr_debug("\tTransmit Flow-Control ON\n");
flow |= GMAC_FLOW_CTRL_TFE;
}
if (duplex) {
pr_debug("\tduplex mode: PAUSE %d\n", pause_time);
flow |= (pause_time << GMAC_FLOW_CTRL_PT_SHIFT);
}
writel(flow, ioaddr + GMAC_FLOW_CTRL);
}
static void dwmac1000_pmt(struct mac_device_info *hw, unsigned long mode)
{
void __iomem *ioaddr = hw->pcsr;
unsigned int pmt = 0;
if (mode & WAKE_MAGIC) {
pr_debug("GMAC: WOL Magic frame\n");
pmt |= power_down | magic_pkt_en;
}
if (mode & WAKE_UCAST) {
pr_debug("GMAC: WOL on global unicast\n");
pmt |= global_unicast;
}
writel(pmt, ioaddr + GMAC_PMT);
}
static int dwmac1000_irq_status(struct mac_device_info *hw,
struct stmmac_extra_stats *x)
{
void __iomem *ioaddr = hw->pcsr;
u32 intr_status = readl(ioaddr + GMAC_INT_STATUS);
int ret = 0;
/* Not used events (e.g. MMC interrupts) are not handled. */
if ((intr_status & mmc_tx_irq))
x->mmc_tx_irq_n++;
if (unlikely(intr_status & mmc_rx_irq))
x->mmc_rx_irq_n++;
if (unlikely(intr_status & mmc_rx_csum_offload_irq))
x->mmc_rx_csum_offload_irq_n++;
if (unlikely(intr_status & pmt_irq)) {
/* clear the PMT bits 5 and 6 by reading the PMT status reg */
readl(ioaddr + GMAC_PMT);
x->irq_receive_pmt_irq_n++;
}
/* MAC trx/rx EEE LPI entry/exit interrupts */
if (intr_status & lpiis_irq) {
/* Clean LPI interrupt by reading the Reg 12 */
ret = readl(ioaddr + LPI_CTRL_STATUS);
if (ret & LPI_CTRL_STATUS_TLPIEN)
x->irq_tx_path_in_lpi_mode_n++;
if (ret & LPI_CTRL_STATUS_TLPIEX)
x->irq_tx_path_exit_lpi_mode_n++;
if (ret & LPI_CTRL_STATUS_RLPIEN)
x->irq_rx_path_in_lpi_mode_n++;
if (ret & LPI_CTRL_STATUS_RLPIEX)
x->irq_rx_path_exit_lpi_mode_n++;
}
if ((intr_status & pcs_ane_irq) || (intr_status & pcs_link_irq)) {
readl(ioaddr + GMAC_AN_STATUS);
x->irq_pcs_ane_n++;
}
if (intr_status & rgmii_irq) {
u32 status = readl(ioaddr + GMAC_S_R_GMII);
x->irq_rgmii_n++;
/* Save and dump the link status. */
if (status & GMAC_S_R_GMII_LINK) {
int speed_value = (status & GMAC_S_R_GMII_SPEED) >>
GMAC_S_R_GMII_SPEED_SHIFT;
x->pcs_duplex = (status & GMAC_S_R_GMII_MODE);
if (speed_value == GMAC_S_R_GMII_SPEED_125)
x->pcs_speed = SPEED_1000;
else if (speed_value == GMAC_S_R_GMII_SPEED_25)
x->pcs_speed = SPEED_100;
else
x->pcs_speed = SPEED_10;
x->pcs_link = 1;
pr_debug("%s: Link is Up - %d/%s\n", __func__,
(int)x->pcs_speed,
x->pcs_duplex ? "Full" : "Half");
} else {
x->pcs_link = 0;
pr_debug("%s: Link is Down\n", __func__);
}
}
return ret;
}
static void dwmac1000_set_eee_mode(struct mac_device_info *hw)
{
void __iomem *ioaddr = hw->pcsr;
u32 value;
/* Enable the link status receive on RGMII, SGMII ore SMII
* receive path and instruct the transmit to enter in LPI
* state.
*/
value = readl(ioaddr + LPI_CTRL_STATUS);
value |= LPI_CTRL_STATUS_LPIEN | LPI_CTRL_STATUS_LPITXA;
writel(value, ioaddr + LPI_CTRL_STATUS);
}
static void dwmac1000_reset_eee_mode(struct mac_device_info *hw)
{
void __iomem *ioaddr = hw->pcsr;
u32 value;
value = readl(ioaddr + LPI_CTRL_STATUS);
value &= ~(LPI_CTRL_STATUS_LPIEN | LPI_CTRL_STATUS_LPITXA);
writel(value, ioaddr + LPI_CTRL_STATUS);
}
static void dwmac1000_set_eee_pls(struct mac_device_info *hw, int link)
{
void __iomem *ioaddr = hw->pcsr;
u32 value;
value = readl(ioaddr + LPI_CTRL_STATUS);
if (link)
value |= LPI_CTRL_STATUS_PLS;
else
value &= ~LPI_CTRL_STATUS_PLS;
writel(value, ioaddr + LPI_CTRL_STATUS);
}
static void dwmac1000_set_eee_timer(struct mac_device_info *hw, int ls, int tw)
{
void __iomem *ioaddr = hw->pcsr;
int value = ((tw & 0xffff)) | ((ls & 0x7ff) << 16);
/* Program the timers in the LPI timer control register:
* LS: minimum time (ms) for which the link
* status from PHY should be ok before transmitting
* the LPI pattern.
* TW: minimum time (us) for which the core waits
* after it has stopped transmitting the LPI pattern.
*/
writel(value, ioaddr + LPI_TIMER_CTRL);
}
static void dwmac1000_ctrl_ane(struct mac_device_info *hw, bool restart)
{
void __iomem *ioaddr = hw->pcsr;
/* auto negotiation enable and External Loopback enable */
u32 value = GMAC_AN_CTRL_ANE | GMAC_AN_CTRL_ELE;
if (restart)
value |= GMAC_AN_CTRL_RAN;
writel(value, ioaddr + GMAC_AN_CTRL);
}
static void dwmac1000_get_adv(struct mac_device_info *hw, struct rgmii_adv *adv)
{
void __iomem *ioaddr = hw->pcsr;
u32 value = readl(ioaddr + GMAC_ANE_ADV);
if (value & GMAC_ANE_FD)
adv->duplex = DUPLEX_FULL;
if (value & GMAC_ANE_HD)
adv->duplex |= DUPLEX_HALF;
adv->pause = (value & GMAC_ANE_PSE) >> GMAC_ANE_PSE_SHIFT;
value = readl(ioaddr + GMAC_ANE_LPA);
if (value & GMAC_ANE_FD)
adv->lp_duplex = DUPLEX_FULL;
if (value & GMAC_ANE_HD)
adv->lp_duplex = DUPLEX_HALF;
adv->lp_pause = (value & GMAC_ANE_PSE) >> GMAC_ANE_PSE_SHIFT;
}
static const struct stmmac_ops dwmac1000_ops = {
.core_init = dwmac1000_core_init,
.rx_ipc = dwmac1000_rx_ipc_enable,
.dump_regs = dwmac1000_dump_regs,
.host_irq_status = dwmac1000_irq_status,
.set_filter = dwmac1000_set_filter,
.flow_ctrl = dwmac1000_flow_ctrl,
.pmt = dwmac1000_pmt,
.set_umac_addr = dwmac1000_set_umac_addr,
.get_umac_addr = dwmac1000_get_umac_addr,
.set_eee_mode = dwmac1000_set_eee_mode,
.reset_eee_mode = dwmac1000_reset_eee_mode,
.set_eee_timer = dwmac1000_set_eee_timer,
.set_eee_pls = dwmac1000_set_eee_pls,
.ctrl_ane = dwmac1000_ctrl_ane,
.get_adv = dwmac1000_get_adv,
};
struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr, int mcbins,
int perfect_uc_entries)
{
struct mac_device_info *mac;
u32 hwid = readl(ioaddr + GMAC_VERSION);
mac = kzalloc(sizeof(const struct mac_device_info), GFP_KERNEL);
if (!mac)
return NULL;
mac->pcsr = ioaddr;
mac->multicast_filter_bins = mcbins;
mac->unicast_filter_entries = perfect_uc_entries;
mac->mcast_bits_log2 = 0;
if (mac->multicast_filter_bins)
mac->mcast_bits_log2 = ilog2(mac->multicast_filter_bins);
mac->mac = &dwmac1000_ops;
mac->dma = &dwmac1000_dma_ops;
mac->link.port = GMAC_CONTROL_PS;
mac->link.duplex = GMAC_CONTROL_DM;
mac->link.speed = GMAC_CONTROL_FES;
mac->mii.addr = GMAC_MII_ADDR;
mac->mii.data = GMAC_MII_DATA;
mac->synopsys_uid = hwid;
return mac;
}

View file

@ -0,0 +1,201 @@
/*******************************************************************************
This is the driver for the GMAC on-chip Ethernet controller for ST SoCs.
DWC Ether MAC 10/100/1000 Universal version 3.41a has been used for
developing this code.
This contains the functions to handle the dma.
Copyright (C) 2007-2009 STMicroelectronics Ltd
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
#include <asm/io.h>
#include "dwmac1000.h"
#include "dwmac_dma.h"
static int dwmac1000_dma_init(void __iomem *ioaddr, int pbl, int fb, int mb,
int burst_len, u32 dma_tx, u32 dma_rx, int atds)
{
u32 value = readl(ioaddr + DMA_BUS_MODE);
int limit;
/* DMA SW reset */
value |= DMA_BUS_MODE_SFT_RESET;
writel(value, ioaddr + DMA_BUS_MODE);
limit = 10;
while (limit--) {
if (!(readl(ioaddr + DMA_BUS_MODE) & DMA_BUS_MODE_SFT_RESET))
break;
mdelay(10);
}
if (limit < 0)
return -EBUSY;
/*
* Set the DMA PBL (Programmable Burst Length) mode
* Before stmmac core 3.50 this mode bit was 4xPBL, and
* post 3.5 mode bit acts as 8*PBL.
* For core rev < 3.5, when the core is set for 4xPBL mode, the
* DMA transfers the data in 4, 8, 16, 32, 64 & 128 beats
* depending on pbl value.
* For core rev > 3.5, when the core is set for 8xPBL mode, the
* DMA transfers the data in 8, 16, 32, 64, 128 & 256 beats
* depending on pbl value.
*/
value = DMA_BUS_MODE_PBL | ((pbl << DMA_BUS_MODE_PBL_SHIFT) |
(pbl << DMA_BUS_MODE_RPBL_SHIFT));
/* Set the Fixed burst mode */
if (fb)
value |= DMA_BUS_MODE_FB;
/* Mixed Burst has no effect when fb is set */
if (mb)
value |= DMA_BUS_MODE_MB;
#ifdef CONFIG_STMMAC_DA
value |= DMA_BUS_MODE_DA; /* Rx has priority over tx */
#endif
if (atds)
value |= DMA_BUS_MODE_ATDS;
writel(value, ioaddr + DMA_BUS_MODE);
/* In case of GMAC AXI configuration, program the DMA_AXI_BUS_MODE
* for supported bursts.
*
* Note: This is applicable only for revision GMACv3.61a. For
* older version this register is reserved and shall have no
* effect.
*
* Note:
* For Fixed Burst Mode: if we directly write 0xFF to this
* register using the configurations pass from platform code,
* this would ensure that all bursts supported by core are set
* and those which are not supported would remain ineffective.
*
* For Non Fixed Burst Mode: provide the maximum value of the
* burst length. Any burst equal or below the provided burst
* length would be allowed to perform.
*/
writel(burst_len, ioaddr + DMA_AXI_BUS_MODE);
/* Mask interrupts by writing to CSR7 */
writel(DMA_INTR_DEFAULT_MASK, ioaddr + DMA_INTR_ENA);
/* RX/TX descriptor base address lists must be written into
* DMA CSR3 and CSR4, respectively
*/
writel(dma_tx, ioaddr + DMA_TX_BASE_ADDR);
writel(dma_rx, ioaddr + DMA_RCV_BASE_ADDR);
return 0;
}
static void dwmac1000_dma_operation_mode(void __iomem *ioaddr, int txmode,
int rxmode)
{
u32 csr6 = readl(ioaddr + DMA_CONTROL);
if (txmode == SF_DMA_MODE) {
pr_debug("GMAC: enable TX store and forward mode\n");
/* Transmit COE type 2 cannot be done in cut-through mode. */
csr6 |= DMA_CONTROL_TSF;
/* Operating on second frame increase the performance
* especially when transmit store-and-forward is used.
*/
csr6 |= DMA_CONTROL_OSF;
} else {
pr_debug("GMAC: disabling TX SF (threshold %d)\n", txmode);
csr6 &= ~DMA_CONTROL_TSF;
csr6 &= DMA_CONTROL_TC_TX_MASK;
/* Set the transmit threshold */
if (txmode <= 32)
csr6 |= DMA_CONTROL_TTC_32;
else if (txmode <= 64)
csr6 |= DMA_CONTROL_TTC_64;
else if (txmode <= 128)
csr6 |= DMA_CONTROL_TTC_128;
else if (txmode <= 192)
csr6 |= DMA_CONTROL_TTC_192;
else
csr6 |= DMA_CONTROL_TTC_256;
}
if (rxmode == SF_DMA_MODE) {
pr_debug("GMAC: enable RX store and forward mode\n");
csr6 |= DMA_CONTROL_RSF;
} else {
pr_debug("GMAC: disable RX SF 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);
}
static void dwmac1000_dump_dma_regs(void __iomem *ioaddr)
{
int i;
pr_info(" DMA registers\n");
for (i = 0; i < 22; i++) {
if ((i < 9) || (i > 17)) {
int offset = i * 4;
pr_err("\t Reg No. %d (offset 0x%x): 0x%08x\n", i,
(DMA_BUS_MODE + offset),
readl(ioaddr + DMA_BUS_MODE + offset));
}
}
}
static unsigned int dwmac1000_get_hw_feature(void __iomem *ioaddr)
{
return readl(ioaddr + DMA_HW_FEATURE);
}
static void dwmac1000_rx_watchdog(void __iomem *ioaddr, u32 riwt)
{
writel(riwt, ioaddr + DMA_RX_WATCHDOG);
}
const struct stmmac_dma_ops dwmac1000_dma_ops = {
.init = dwmac1000_dma_init,
.dump_regs = dwmac1000_dump_dma_regs,
.dma_mode = dwmac1000_dma_operation_mode,
.enable_dma_transmission = dwmac_enable_dma_transmission,
.enable_dma_irq = dwmac_enable_dma_irq,
.disable_dma_irq = dwmac_disable_dma_irq,
.start_tx = dwmac_dma_start_tx,
.stop_tx = dwmac_dma_stop_tx,
.start_rx = dwmac_dma_start_rx,
.stop_rx = dwmac_dma_stop_rx,
.dma_interrupt = dwmac_dma_interrupt,
.get_hw_feature = dwmac1000_get_hw_feature,
.rx_watchdog = dwmac1000_rx_watchdog,
};

View file

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

View file

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

View file

@ -0,0 +1,116 @@
/*******************************************************************************
DWMAC DMA Header file.
Copyright (C) 2007-2009 STMicroelectronics Ltd
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
#ifndef __DWMAC_DMA_H__
#define __DWMAC_DMA_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 */
/* Rx watchdog register */
#define DMA_RX_WATCHDOG 0x00001024
/* AXI Bus Mode */
#define DMA_AXI_BUS_MODE 0x00001028
#define DMA_CUR_TX_BUF_ADDR 0x00001050 /* Current Host Tx Buffer */
#define DMA_CUR_RX_BUF_ADDR 0x00001054 /* Current Host Rx Buffer */
#define DMA_HW_FEATURE 0x00001058 /* HW Feature Register */
/* DMA Control register defines */
#define DMA_CONTROL_ST 0x00002000 /* Start/Stop Transmission */
#define DMA_CONTROL_SR 0x00000002 /* Start/Stop Receive */
/* DMA Normal interrupt */
#define DMA_INTR_ENA_NIE 0x00010000 /* Normal Summary */
#define DMA_INTR_ENA_TIE 0x00000001 /* Transmit Interrupt */
#define DMA_INTR_ENA_TUE 0x00000004 /* Transmit Buffer Unavailable */
#define DMA_INTR_ENA_RIE 0x00000040 /* Receive Interrupt */
#define DMA_INTR_ENA_ERE 0x00004000 /* Early Receive */
#define DMA_INTR_NORMAL (DMA_INTR_ENA_NIE | DMA_INTR_ENA_RIE | \
DMA_INTR_ENA_TIE)
/* DMA Abnormal interrupt */
#define DMA_INTR_ENA_AIE 0x00008000 /* Abnormal Summary */
#define DMA_INTR_ENA_FBE 0x00002000 /* Fatal Bus Error */
#define DMA_INTR_ENA_ETE 0x00000400 /* Early Transmit */
#define DMA_INTR_ENA_RWE 0x00000200 /* Receive Watchdog */
#define DMA_INTR_ENA_RSE 0x00000100 /* Receive Stopped */
#define DMA_INTR_ENA_RUE 0x00000080 /* Receive Buffer Unavailable */
#define DMA_INTR_ENA_UNE 0x00000020 /* Tx Underflow */
#define DMA_INTR_ENA_OVE 0x00000010 /* Receive Overflow */
#define DMA_INTR_ENA_TJE 0x00000008 /* Transmit Jabber */
#define DMA_INTR_ENA_TSE 0x00000002 /* Transmit Stopped */
#define DMA_INTR_ABNORMAL (DMA_INTR_ENA_AIE | DMA_INTR_ENA_FBE | \
DMA_INTR_ENA_UNE)
/* DMA default interrupt mask */
#define DMA_INTR_DEFAULT_MASK (DMA_INTR_NORMAL | DMA_INTR_ABNORMAL)
/* DMA Status register defines */
#define DMA_STATUS_GLPII 0x40000000 /* GMAC LPI interrupt */
#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_EB_MASK 0x00380000 /* Error Bits Mask */
#define DMA_STATUS_EB_TX_ABORT 0x00080000 /* Error Bits - TX Abort */
#define DMA_STATUS_EB_RX_ABORT 0x00100000 /* Error Bits - RX Abort */
#define DMA_STATUS_TS_MASK 0x00700000 /* Transmit Process State */
#define DMA_STATUS_TS_SHIFT 20
#define DMA_STATUS_RS_MASK 0x000e0000 /* Receive Process State */
#define DMA_STATUS_RS_SHIFT 17
#define DMA_STATUS_NIS 0x00010000 /* Normal Interrupt Summary */
#define DMA_STATUS_AIS 0x00008000 /* Abnormal Interrupt Summary */
#define DMA_STATUS_ERI 0x00004000 /* Early Receive Interrupt */
#define DMA_STATUS_FBI 0x00002000 /* Fatal Bus Error Interrupt */
#define DMA_STATUS_ETI 0x00000400 /* Early Transmit Interrupt */
#define DMA_STATUS_RWT 0x00000200 /* Receive Watchdog Timeout */
#define DMA_STATUS_RPS 0x00000100 /* Receive Process Stopped */
#define DMA_STATUS_RU 0x00000080 /* Receive Buffer Unavailable */
#define DMA_STATUS_RI 0x00000040 /* Receive Interrupt */
#define DMA_STATUS_UNF 0x00000020 /* Transmit Underflow */
#define DMA_STATUS_OVF 0x00000010 /* Receive Overflow */
#define DMA_STATUS_TJT 0x00000008 /* Transmit Jabber Timeout */
#define DMA_STATUS_TU 0x00000004 /* Transmit Buffer Unavailable */
#define DMA_STATUS_TPS 0x00000002 /* Transmit Process Stopped */
#define DMA_STATUS_TI 0x00000001 /* Transmit Interrupt */
#define DMA_CONTROL_FTF 0x00100000 /* Flush transmit FIFO */
void dwmac_enable_dma_transmission(void __iomem *ioaddr);
void dwmac_enable_dma_irq(void __iomem *ioaddr);
void dwmac_disable_dma_irq(void __iomem *ioaddr);
void dwmac_dma_start_tx(void __iomem *ioaddr);
void dwmac_dma_stop_tx(void __iomem *ioaddr);
void dwmac_dma_start_rx(void __iomem *ioaddr);
void dwmac_dma_stop_rx(void __iomem *ioaddr);
int dwmac_dma_interrupt(void __iomem *ioaddr, struct stmmac_extra_stats *x);
#endif /* __DWMAC_DMA_H__ */

View file

@ -0,0 +1,267 @@
/*******************************************************************************
Copyright (C) 2007-2009 STMicroelectronics Ltd
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
#include <linux/io.h>
#include "common.h"
#include "dwmac_dma.h"
#define GMAC_HI_REG_AE 0x80000000
/* CSR1 enables the transmit DMA to check for new descriptor */
void dwmac_enable_dma_transmission(void __iomem *ioaddr)
{
writel(1, ioaddr + DMA_XMT_POLL_DEMAND);
}
void dwmac_enable_dma_irq(void __iomem *ioaddr)
{
writel(DMA_INTR_DEFAULT_MASK, ioaddr + DMA_INTR_ENA);
}
void dwmac_disable_dma_irq(void __iomem *ioaddr)
{
writel(0, ioaddr + DMA_INTR_ENA);
}
void dwmac_dma_start_tx(void __iomem *ioaddr)
{
u32 value = readl(ioaddr + DMA_CONTROL);
value |= DMA_CONTROL_ST;
writel(value, ioaddr + DMA_CONTROL);
}
void dwmac_dma_stop_tx(void __iomem *ioaddr)
{
u32 value = readl(ioaddr + DMA_CONTROL);
value &= ~DMA_CONTROL_ST;
writel(value, ioaddr + DMA_CONTROL);
}
void dwmac_dma_start_rx(void __iomem *ioaddr)
{
u32 value = readl(ioaddr + DMA_CONTROL);
value |= DMA_CONTROL_SR;
writel(value, ioaddr + DMA_CONTROL);
}
void dwmac_dma_stop_rx(void __iomem *ioaddr)
{
u32 value = readl(ioaddr + DMA_CONTROL);
value &= ~DMA_CONTROL_SR;
writel(value, ioaddr + DMA_CONTROL);
}
#ifdef DWMAC_DMA_DEBUG
static void show_tx_process_state(unsigned int status)
{
unsigned int state;
state = (status & DMA_STATUS_TS_MASK) >> DMA_STATUS_TS_SHIFT;
switch (state) {
case 0:
pr_debug("- TX (Stopped): Reset or Stop command\n");
break;
case 1:
pr_debug("- TX (Running):Fetching the Tx desc\n");
break;
case 2:
pr_debug("- TX (Running): Waiting for end of tx\n");
break;
case 3:
pr_debug("- TX (Running): Reading the data "
"and queuing the data into the Tx buf\n");
break;
case 6:
pr_debug("- TX (Suspended): Tx Buff Underflow "
"or an unavailable Transmit descriptor\n");
break;
case 7:
pr_debug("- TX (Running): Closing Tx descriptor\n");
break;
default:
break;
}
}
static void show_rx_process_state(unsigned int status)
{
unsigned int state;
state = (status & DMA_STATUS_RS_MASK) >> DMA_STATUS_RS_SHIFT;
switch (state) {
case 0:
pr_debug("- RX (Stopped): Reset or Stop command\n");
break;
case 1:
pr_debug("- RX (Running): Fetching the Rx desc\n");
break;
case 2:
pr_debug("- RX (Running):Checking for end of pkt\n");
break;
case 3:
pr_debug("- RX (Running): Waiting for Rx pkt\n");
break;
case 4:
pr_debug("- RX (Suspended): Unavailable Rx buf\n");
break;
case 5:
pr_debug("- RX (Running): Closing Rx descriptor\n");
break;
case 6:
pr_debug("- RX(Running): Flushing the current frame"
" from the Rx buf\n");
break;
case 7:
pr_debug("- RX (Running): Queuing the Rx frame"
" from the Rx buf into memory\n");
break;
default:
break;
}
}
#endif
int dwmac_dma_interrupt(void __iomem *ioaddr,
struct stmmac_extra_stats *x)
{
int ret = 0;
/* read the status register (CSR5) */
u32 intr_status = readl(ioaddr + DMA_STATUS);
#ifdef DWMAC_DMA_DEBUG
/* Enable it to monitor DMA rx/tx status in case of critical problems */
pr_debug("%s: [CSR5: 0x%08x]\n", __func__, intr_status);
show_tx_process_state(intr_status);
show_rx_process_state(intr_status);
#endif
/* ABNORMAL interrupts */
if (unlikely(intr_status & DMA_STATUS_AIS)) {
if (unlikely(intr_status & DMA_STATUS_UNF)) {
ret = tx_hard_error_bump_tc;
x->tx_undeflow_irq++;
}
if (unlikely(intr_status & DMA_STATUS_TJT))
x->tx_jabber_irq++;
if (unlikely(intr_status & DMA_STATUS_OVF))
x->rx_overflow_irq++;
if (unlikely(intr_status & DMA_STATUS_RU))
x->rx_buf_unav_irq++;
if (unlikely(intr_status & DMA_STATUS_RPS))
x->rx_process_stopped_irq++;
if (unlikely(intr_status & DMA_STATUS_RWT))
x->rx_watchdog_irq++;
if (unlikely(intr_status & DMA_STATUS_ETI))
x->tx_early_irq++;
if (unlikely(intr_status & DMA_STATUS_TPS)) {
x->tx_process_stopped_irq++;
ret = tx_hard_error;
}
if (unlikely(intr_status & DMA_STATUS_FBI)) {
x->fatal_bus_error_irq++;
ret = tx_hard_error;
}
}
/* TX/RX NORMAL interrupts */
if (likely(intr_status & DMA_STATUS_NIS)) {
x->normal_irq_n++;
if (likely(intr_status & DMA_STATUS_RI)) {
u32 value = readl(ioaddr + DMA_INTR_ENA);
/* to schedule NAPI on real RIE event. */
if (likely(value & DMA_INTR_ENA_RIE)) {
x->rx_normal_irq_n++;
ret |= handle_rx;
}
}
if (likely(intr_status & DMA_STATUS_TI)) {
x->tx_normal_irq_n++;
ret |= handle_tx;
}
if (unlikely(intr_status & DMA_STATUS_ERI))
x->rx_early_irq++;
}
/* Optional hardware blocks, interrupts should be disabled */
if (unlikely(intr_status &
(DMA_STATUS_GPI | DMA_STATUS_GMI | DMA_STATUS_GLI)))
pr_warn("%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);
return ret;
}
void dwmac_dma_flush_tx_fifo(void __iomem *ioaddr)
{
u32 csr6 = readl(ioaddr + DMA_CONTROL);
writel((csr6 | DMA_CONTROL_FTF), ioaddr + DMA_CONTROL);
do {} while ((readl(ioaddr + DMA_CONTROL) & DMA_CONTROL_FTF));
}
void stmmac_set_mac_addr(void __iomem *ioaddr, u8 addr[6],
unsigned int high, unsigned int low)
{
unsigned long data;
data = (addr[5] << 8) | addr[4];
/* For MAC Addr registers se have to set the Address Enable (AE)
* bit that has no effect on the High Reg 0 where the bit 31 (MO)
* is RO.
*/
writel(data | GMAC_HI_REG_AE, ioaddr + high);
data = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0];
writel(data, ioaddr + low);
}
/* Enable disable MAC RX/TX */
void stmmac_set_mac(void __iomem *ioaddr, bool enable)
{
u32 value = readl(ioaddr + MAC_CTRL_REG);
if (enable)
value |= MAC_RNABLE_RX | MAC_ENABLE_TX;
else
value &= ~(MAC_ENABLE_TX | MAC_RNABLE_RX);
writel(value, ioaddr + MAC_CTRL_REG);
}
void stmmac_get_mac_addr(void __iomem *ioaddr, unsigned char *addr,
unsigned int high, unsigned int low)
{
unsigned int hi_addr, lo_addr;
/* Read the MAC address from the hardware */
hi_addr = readl(ioaddr + high);
lo_addr = readl(ioaddr + low);
/* Extract the MAC address from the high and low words */
addr[0] = lo_addr & 0xff;
addr[1] = (lo_addr >> 8) & 0xff;
addr[2] = (lo_addr >> 16) & 0xff;
addr[3] = (lo_addr >> 24) & 0xff;
addr[4] = hi_addr & 0xff;
addr[5] = (hi_addr >> 8) & 0xff;
}

View file

@ -0,0 +1,402 @@
/*******************************************************************************
This contains the functions to handle the enhanced descriptors.
Copyright (C) 2007-2009 STMicroelectronics Ltd
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
#include <linux/stmmac.h>
#include "common.h"
#include "descs_com.h"
static int enh_desc_get_tx_status(void *data, struct stmmac_extra_stats *x,
struct dma_desc *p, void __iomem *ioaddr)
{
int ret = 0;
struct net_device_stats *stats = (struct net_device_stats *)data;
if (unlikely(p->des01.etx.error_summary)) {
if (unlikely(p->des01.etx.jabber_timeout))
x->tx_jabber++;
if (unlikely(p->des01.etx.frame_flushed)) {
x->tx_frame_flushed++;
dwmac_dma_flush_tx_fifo(ioaddr);
}
if (unlikely(p->des01.etx.loss_carrier)) {
x->tx_losscarrier++;
stats->tx_carrier_errors++;
}
if (unlikely(p->des01.etx.no_carrier)) {
x->tx_carrier++;
stats->tx_carrier_errors++;
}
if (unlikely(p->des01.etx.late_collision))
stats->collisions += p->des01.etx.collision_count;
if (unlikely(p->des01.etx.excessive_collisions))
stats->collisions += p->des01.etx.collision_count;
if (unlikely(p->des01.etx.excessive_deferral))
x->tx_deferred++;
if (unlikely(p->des01.etx.underflow_error)) {
dwmac_dma_flush_tx_fifo(ioaddr);
x->tx_underflow++;
}
if (unlikely(p->des01.etx.ip_header_error))
x->tx_ip_header_error++;
if (unlikely(p->des01.etx.payload_error)) {
x->tx_payload_error++;
dwmac_dma_flush_tx_fifo(ioaddr);
}
ret = -1;
}
if (unlikely(p->des01.etx.deferred))
x->tx_deferred++;
#ifdef STMMAC_VLAN_TAG_USED
if (p->des01.etx.vlan_frame)
x->tx_vlan++;
#endif
return ret;
}
static int enh_desc_get_tx_len(struct dma_desc *p)
{
return p->des01.etx.buffer1_size;
}
static int enh_desc_coe_rdes0(int ipc_err, int type, int payload_err)
{
int ret = good_frame;
u32 status = (type << 2 | ipc_err << 1 | payload_err) & 0x7;
/* bits 5 7 0 | Frame status
* ----------------------------------------------------------
* 0 0 0 | IEEE 802.3 Type frame (length < 1536 octects)
* 1 0 0 | IPv4/6 No CSUM errorS.
* 1 0 1 | IPv4/6 CSUM PAYLOAD error
* 1 1 0 | IPv4/6 CSUM IP HR error
* 1 1 1 | IPv4/6 IP PAYLOAD AND HEADER errorS
* 0 0 1 | IPv4/6 unsupported IP PAYLOAD
* 0 1 1 | COE bypassed.. no IPv4/6 frame
* 0 1 0 | Reserved.
*/
if (status == 0x0)
ret = llc_snap;
else if (status == 0x4)
ret = good_frame;
else if (status == 0x5)
ret = csum_none;
else if (status == 0x6)
ret = csum_none;
else if (status == 0x7)
ret = csum_none;
else if (status == 0x1)
ret = discard_frame;
else if (status == 0x3)
ret = discard_frame;
return ret;
}
static void enh_desc_get_ext_status(void *data, struct stmmac_extra_stats *x,
struct dma_extended_desc *p)
{
if (unlikely(p->basic.des01.erx.rx_mac_addr)) {
if (p->des4.erx.ip_hdr_err)
x->ip_hdr_err++;
if (p->des4.erx.ip_payload_err)
x->ip_payload_err++;
if (p->des4.erx.ip_csum_bypassed)
x->ip_csum_bypassed++;
if (p->des4.erx.ipv4_pkt_rcvd)
x->ipv4_pkt_rcvd++;
if (p->des4.erx.ipv6_pkt_rcvd)
x->ipv6_pkt_rcvd++;
if (p->des4.erx.msg_type == RDES_EXT_SYNC)
x->rx_msg_type_sync++;
else if (p->des4.erx.msg_type == RDES_EXT_FOLLOW_UP)
x->rx_msg_type_follow_up++;
else if (p->des4.erx.msg_type == RDES_EXT_DELAY_REQ)
x->rx_msg_type_delay_req++;
else if (p->des4.erx.msg_type == RDES_EXT_DELAY_RESP)
x->rx_msg_type_delay_resp++;
else if (p->des4.erx.msg_type == RDES_EXT_PDELAY_REQ)
x->rx_msg_type_pdelay_req++;
else if (p->des4.erx.msg_type == RDES_EXT_PDELAY_RESP)
x->rx_msg_type_pdelay_resp++;
else if (p->des4.erx.msg_type == RDES_EXT_PDELAY_FOLLOW_UP)
x->rx_msg_type_pdelay_follow_up++;
else
x->rx_msg_type_ext_no_ptp++;
if (p->des4.erx.ptp_frame_type)
x->ptp_frame_type++;
if (p->des4.erx.ptp_ver)
x->ptp_ver++;
if (p->des4.erx.timestamp_dropped)
x->timestamp_dropped++;
if (p->des4.erx.av_pkt_rcvd)
x->av_pkt_rcvd++;
if (p->des4.erx.av_tagged_pkt_rcvd)
x->av_tagged_pkt_rcvd++;
if (p->des4.erx.vlan_tag_priority_val)
x->vlan_tag_priority_val++;
if (p->des4.erx.l3_filter_match)
x->l3_filter_match++;
if (p->des4.erx.l4_filter_match)
x->l4_filter_match++;
if (p->des4.erx.l3_l4_filter_no_match)
x->l3_l4_filter_no_match++;
}
}
static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x,
struct dma_desc *p)
{
int ret = good_frame;
struct net_device_stats *stats = (struct net_device_stats *)data;
if (unlikely(p->des01.erx.error_summary)) {
if (unlikely(p->des01.erx.descriptor_error)) {
x->rx_desc++;
stats->rx_length_errors++;
}
if (unlikely(p->des01.erx.overflow_error))
x->rx_gmac_overflow++;
if (unlikely(p->des01.erx.ipc_csum_error))
pr_err("\tIPC Csum Error/Giant frame\n");
if (unlikely(p->des01.erx.late_collision)) {
stats->collisions++;
}
if (unlikely(p->des01.erx.receive_watchdog))
x->rx_watchdog++;
if (unlikely(p->des01.erx.error_gmii))
x->rx_mii++;
if (unlikely(p->des01.erx.crc_error)) {
x->rx_crc++;
stats->rx_crc_errors++;
}
ret = discard_frame;
}
/* After a payload csum error, the ES bit is set.
* It doesn't match with the information reported into the databook.
* At any rate, we need to understand if the CSUM hw computation is ok
* and report this info to the upper layers. */
ret = enh_desc_coe_rdes0(p->des01.erx.ipc_csum_error,
p->des01.erx.frame_type, p->des01.erx.rx_mac_addr);
if (unlikely(p->des01.erx.dribbling))
x->dribbling_bit++;
if (unlikely(p->des01.erx.sa_filter_fail)) {
x->sa_rx_filter_fail++;
ret = discard_frame;
}
if (unlikely(p->des01.erx.da_filter_fail)) {
x->da_rx_filter_fail++;
ret = discard_frame;
}
if (unlikely(p->des01.erx.length_error)) {
x->rx_length++;
ret = discard_frame;
}
#ifdef STMMAC_VLAN_TAG_USED
if (p->des01.erx.vlan_tag)
x->rx_vlan++;
#endif
return ret;
}
static void enh_desc_init_rx_desc(struct dma_desc *p, int disable_rx_ic,
int mode, int end)
{
p->des01.erx.own = 1;
p->des01.erx.buffer1_size = BUF_SIZE_8KiB - 1;
if (mode == STMMAC_CHAIN_MODE)
ehn_desc_rx_set_on_chain(p, end);
else
ehn_desc_rx_set_on_ring(p, end);
if (disable_rx_ic)
p->des01.erx.disable_ic = 1;
}
static void enh_desc_init_tx_desc(struct dma_desc *p, int mode, int end)
{
p->des01.etx.own = 0;
if (mode == STMMAC_CHAIN_MODE)
ehn_desc_tx_set_on_chain(p, end);
else
ehn_desc_tx_set_on_ring(p, end);
}
static int enh_desc_get_tx_owner(struct dma_desc *p)
{
return p->des01.etx.own;
}
static int enh_desc_get_rx_owner(struct dma_desc *p)
{
return p->des01.erx.own;
}
static void enh_desc_set_tx_owner(struct dma_desc *p)
{
p->des01.etx.own = 1;
}
static void enh_desc_set_rx_owner(struct dma_desc *p)
{
p->des01.erx.own = 1;
}
static int enh_desc_get_tx_ls(struct dma_desc *p)
{
return p->des01.etx.last_segment;
}
static void enh_desc_release_tx_desc(struct dma_desc *p, int mode)
{
int ter = p->des01.etx.end_ring;
memset(p, 0, offsetof(struct dma_desc, des2));
if (mode == STMMAC_CHAIN_MODE)
enh_desc_end_tx_desc_on_chain(p, ter);
else
enh_desc_end_tx_desc_on_ring(p, ter);
}
static void enh_desc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len,
int csum_flag, int mode)
{
p->des01.etx.first_segment = is_fs;
if (mode == STMMAC_CHAIN_MODE)
enh_set_tx_desc_len_on_chain(p, len);
else
enh_set_tx_desc_len_on_ring(p, len);
if (likely(csum_flag))
p->des01.etx.checksum_insertion = cic_full;
}
static void enh_desc_clear_tx_ic(struct dma_desc *p)
{
p->des01.etx.interrupt = 0;
}
static void enh_desc_close_tx_desc(struct dma_desc *p)
{
p->des01.etx.last_segment = 1;
p->des01.etx.interrupt = 1;
}
static int enh_desc_get_rx_frame_len(struct dma_desc *p, int rx_coe_type)
{
/* The type-1 checksum offload engines append the checksum at
* the end of frame and the two bytes of checksum are added in
* the length.
* Adjust for that in the framelen for type-1 checksum offload
* engines. */
if (rx_coe_type == STMMAC_RX_COE_TYPE1)
return p->des01.erx.frame_length - 2;
else
return p->des01.erx.frame_length;
}
static void enh_desc_enable_tx_timestamp(struct dma_desc *p)
{
p->des01.etx.time_stamp_enable = 1;
}
static int enh_desc_get_tx_timestamp_status(struct dma_desc *p)
{
return p->des01.etx.time_stamp_status;
}
static u64 enh_desc_get_timestamp(void *desc, u32 ats)
{
u64 ns;
if (ats) {
struct dma_extended_desc *p = (struct dma_extended_desc *)desc;
ns = p->des6;
/* convert high/sec time stamp value to nanosecond */
ns += p->des7 * 1000000000ULL;
} else {
struct dma_desc *p = (struct dma_desc *)desc;
ns = p->des2;
ns += p->des3 * 1000000000ULL;
}
return ns;
}
static int enh_desc_get_rx_timestamp_status(void *desc, u32 ats)
{
if (ats) {
struct dma_extended_desc *p = (struct dma_extended_desc *)desc;
return p->basic.des01.erx.ipc_csum_error;
} else {
struct dma_desc *p = (struct dma_desc *)desc;
if ((p->des2 == 0xffffffff) && (p->des3 == 0xffffffff))
/* timestamp is corrupted, hence don't store it */
return 0;
else
return 1;
}
}
const struct stmmac_desc_ops enh_desc_ops = {
.tx_status = enh_desc_get_tx_status,
.rx_status = enh_desc_get_rx_status,
.get_tx_len = enh_desc_get_tx_len,
.init_rx_desc = enh_desc_init_rx_desc,
.init_tx_desc = enh_desc_init_tx_desc,
.get_tx_owner = enh_desc_get_tx_owner,
.get_rx_owner = enh_desc_get_rx_owner,
.release_tx_desc = enh_desc_release_tx_desc,
.prepare_tx_desc = enh_desc_prepare_tx_desc,
.clear_tx_ic = enh_desc_clear_tx_ic,
.close_tx_desc = enh_desc_close_tx_desc,
.get_tx_ls = enh_desc_get_tx_ls,
.set_tx_owner = enh_desc_set_tx_owner,
.set_rx_owner = enh_desc_set_rx_owner,
.get_rx_frame_len = enh_desc_get_rx_frame_len,
.rx_extended_status = enh_desc_get_ext_status,
.enable_tx_timestamp = enh_desc_enable_tx_timestamp,
.get_tx_timestamp_status = enh_desc_get_tx_timestamp_status,
.get_timestamp = enh_desc_get_timestamp,
.get_rx_timestamp_status = enh_desc_get_rx_timestamp_status,
};

View file

@ -0,0 +1,135 @@
/*******************************************************************************
MMC Header file
Copyright (C) 2011 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>
*******************************************************************************/
#ifndef __MMC_H__
#define __MMC_H__
/* MMC control register */
/* When set, all counter are reset */
#define MMC_CNTRL_COUNTER_RESET 0x1
/* When set, do not roll over zero after reaching the max value*/
#define MMC_CNTRL_COUNTER_STOP_ROLLOVER 0x2
#define MMC_CNTRL_RESET_ON_READ 0x4 /* Reset after reading */
#define MMC_CNTRL_COUNTER_FREEZER 0x8 /* Freeze counter values to the
* current value.*/
#define MMC_CNTRL_PRESET 0x10
#define MMC_CNTRL_FULL_HALF_PRESET 0x20
struct stmmac_counters {
unsigned int mmc_tx_octetcount_gb;
unsigned int mmc_tx_framecount_gb;
unsigned int mmc_tx_broadcastframe_g;
unsigned int mmc_tx_multicastframe_g;
unsigned int mmc_tx_64_octets_gb;
unsigned int mmc_tx_65_to_127_octets_gb;
unsigned int mmc_tx_128_to_255_octets_gb;
unsigned int mmc_tx_256_to_511_octets_gb;
unsigned int mmc_tx_512_to_1023_octets_gb;
unsigned int mmc_tx_1024_to_max_octets_gb;
unsigned int mmc_tx_unicast_gb;
unsigned int mmc_tx_multicast_gb;
unsigned int mmc_tx_broadcast_gb;
unsigned int mmc_tx_underflow_error;
unsigned int mmc_tx_singlecol_g;
unsigned int mmc_tx_multicol_g;
unsigned int mmc_tx_deferred;
unsigned int mmc_tx_latecol;
unsigned int mmc_tx_exesscol;
unsigned int mmc_tx_carrier_error;
unsigned int mmc_tx_octetcount_g;
unsigned int mmc_tx_framecount_g;
unsigned int mmc_tx_excessdef;
unsigned int mmc_tx_pause_frame;
unsigned int mmc_tx_vlan_frame_g;
/* MMC RX counter registers */
unsigned int mmc_rx_framecount_gb;
unsigned int mmc_rx_octetcount_gb;
unsigned int mmc_rx_octetcount_g;
unsigned int mmc_rx_broadcastframe_g;
unsigned int mmc_rx_multicastframe_g;
unsigned int mmc_rx_crc_error;
unsigned int mmc_rx_align_error;
unsigned int mmc_rx_run_error;
unsigned int mmc_rx_jabber_error;
unsigned int mmc_rx_undersize_g;
unsigned int mmc_rx_oversize_g;
unsigned int mmc_rx_64_octets_gb;
unsigned int mmc_rx_65_to_127_octets_gb;
unsigned int mmc_rx_128_to_255_octets_gb;
unsigned int mmc_rx_256_to_511_octets_gb;
unsigned int mmc_rx_512_to_1023_octets_gb;
unsigned int mmc_rx_1024_to_max_octets_gb;
unsigned int mmc_rx_unicast_g;
unsigned int mmc_rx_length_error;
unsigned int mmc_rx_autofrangetype;
unsigned int mmc_rx_pause_frames;
unsigned int mmc_rx_fifo_overflow;
unsigned int mmc_rx_vlan_frames_gb;
unsigned int mmc_rx_watchdog_error;
/* IPC */
unsigned int mmc_rx_ipc_intr_mask;
unsigned int mmc_rx_ipc_intr;
/* IPv4 */
unsigned int mmc_rx_ipv4_gd;
unsigned int mmc_rx_ipv4_hderr;
unsigned int mmc_rx_ipv4_nopay;
unsigned int mmc_rx_ipv4_frag;
unsigned int mmc_rx_ipv4_udsbl;
unsigned int mmc_rx_ipv4_gd_octets;
unsigned int mmc_rx_ipv4_hderr_octets;
unsigned int mmc_rx_ipv4_nopay_octets;
unsigned int mmc_rx_ipv4_frag_octets;
unsigned int mmc_rx_ipv4_udsbl_octets;
/* IPV6 */
unsigned int mmc_rx_ipv6_gd_octets;
unsigned int mmc_rx_ipv6_hderr_octets;
unsigned int mmc_rx_ipv6_nopay_octets;
unsigned int mmc_rx_ipv6_gd;
unsigned int mmc_rx_ipv6_hderr;
unsigned int mmc_rx_ipv6_nopay;
/* Protocols */
unsigned int mmc_rx_udp_gd;
unsigned int mmc_rx_udp_err;
unsigned int mmc_rx_tcp_gd;
unsigned int mmc_rx_tcp_err;
unsigned int mmc_rx_icmp_gd;
unsigned int mmc_rx_icmp_err;
unsigned int mmc_rx_udp_gd_octets;
unsigned int mmc_rx_udp_err_octets;
unsigned int mmc_rx_tcp_gd_octets;
unsigned int mmc_rx_tcp_err_octets;
unsigned int mmc_rx_icmp_gd_octets;
unsigned int mmc_rx_icmp_err_octets;
};
void dwmac_mmc_ctrl(void __iomem *ioaddr, unsigned int mode);
void dwmac_mmc_intr_all_mask(void __iomem *ioaddr);
void dwmac_mmc_read(void __iomem *ioaddr, struct stmmac_counters *mmc);
#endif /* __MMC_H__ */

View file

@ -0,0 +1,267 @@
/*******************************************************************************
DWMAC Management Counters
Copyright (C) 2011 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/io.h>
#include "mmc.h"
/* MAC Management Counters register offset */
#define MMC_CNTRL 0x00000100 /* MMC Control */
#define MMC_RX_INTR 0x00000104 /* MMC RX Interrupt */
#define MMC_TX_INTR 0x00000108 /* MMC TX Interrupt */
#define MMC_RX_INTR_MASK 0x0000010c /* MMC Interrupt Mask */
#define MMC_TX_INTR_MASK 0x00000110 /* MMC Interrupt Mask */
#define MMC_DEFAULT_MASK 0xffffffff
/* MMC TX counter registers */
/* Note:
* _GB register stands for good and bad frames
* _G is for good only.
*/
#define MMC_TX_OCTETCOUNT_GB 0x00000114
#define MMC_TX_FRAMECOUNT_GB 0x00000118
#define MMC_TX_BROADCASTFRAME_G 0x0000011c
#define MMC_TX_MULTICASTFRAME_G 0x00000120
#define MMC_TX_64_OCTETS_GB 0x00000124
#define MMC_TX_65_TO_127_OCTETS_GB 0x00000128
#define MMC_TX_128_TO_255_OCTETS_GB 0x0000012c
#define MMC_TX_256_TO_511_OCTETS_GB 0x00000130
#define MMC_TX_512_TO_1023_OCTETS_GB 0x00000134
#define MMC_TX_1024_TO_MAX_OCTETS_GB 0x00000138
#define MMC_TX_UNICAST_GB 0x0000013c
#define MMC_TX_MULTICAST_GB 0x00000140
#define MMC_TX_BROADCAST_GB 0x00000144
#define MMC_TX_UNDERFLOW_ERROR 0x00000148
#define MMC_TX_SINGLECOL_G 0x0000014c
#define MMC_TX_MULTICOL_G 0x00000150
#define MMC_TX_DEFERRED 0x00000154
#define MMC_TX_LATECOL 0x00000158
#define MMC_TX_EXESSCOL 0x0000015c
#define MMC_TX_CARRIER_ERROR 0x00000160
#define MMC_TX_OCTETCOUNT_G 0x00000164
#define MMC_TX_FRAMECOUNT_G 0x00000168
#define MMC_TX_EXCESSDEF 0x0000016c
#define MMC_TX_PAUSE_FRAME 0x00000170
#define MMC_TX_VLAN_FRAME_G 0x00000174
/* MMC RX counter registers */
#define MMC_RX_FRAMECOUNT_GB 0x00000180
#define MMC_RX_OCTETCOUNT_GB 0x00000184
#define MMC_RX_OCTETCOUNT_G 0x00000188
#define MMC_RX_BROADCASTFRAME_G 0x0000018c
#define MMC_RX_MULTICASTFRAME_G 0x00000190
#define MMC_RX_CRC_ERRROR 0x00000194
#define MMC_RX_ALIGN_ERROR 0x00000198
#define MMC_RX_RUN_ERROR 0x0000019C
#define MMC_RX_JABBER_ERROR 0x000001A0
#define MMC_RX_UNDERSIZE_G 0x000001A4
#define MMC_RX_OVERSIZE_G 0x000001A8
#define MMC_RX_64_OCTETS_GB 0x000001AC
#define MMC_RX_65_TO_127_OCTETS_GB 0x000001b0
#define MMC_RX_128_TO_255_OCTETS_GB 0x000001b4
#define MMC_RX_256_TO_511_OCTETS_GB 0x000001b8
#define MMC_RX_512_TO_1023_OCTETS_GB 0x000001bc
#define MMC_RX_1024_TO_MAX_OCTETS_GB 0x000001c0
#define MMC_RX_UNICAST_G 0x000001c4
#define MMC_RX_LENGTH_ERROR 0x000001c8
#define MMC_RX_AUTOFRANGETYPE 0x000001cc
#define MMC_RX_PAUSE_FRAMES 0x000001d0
#define MMC_RX_FIFO_OVERFLOW 0x000001d4
#define MMC_RX_VLAN_FRAMES_GB 0x000001d8
#define MMC_RX_WATCHDOG_ERROR 0x000001dc
/* IPC*/
#define MMC_RX_IPC_INTR_MASK 0x00000200
#define MMC_RX_IPC_INTR 0x00000208
/* IPv4*/
#define MMC_RX_IPV4_GD 0x00000210
#define MMC_RX_IPV4_HDERR 0x00000214
#define MMC_RX_IPV4_NOPAY 0x00000218
#define MMC_RX_IPV4_FRAG 0x0000021C
#define MMC_RX_IPV4_UDSBL 0x00000220
#define MMC_RX_IPV4_GD_OCTETS 0x00000250
#define MMC_RX_IPV4_HDERR_OCTETS 0x00000254
#define MMC_RX_IPV4_NOPAY_OCTETS 0x00000258
#define MMC_RX_IPV4_FRAG_OCTETS 0x0000025c
#define MMC_RX_IPV4_UDSBL_OCTETS 0x00000260
/* IPV6*/
#define MMC_RX_IPV6_GD_OCTETS 0x00000264
#define MMC_RX_IPV6_HDERR_OCTETS 0x00000268
#define MMC_RX_IPV6_NOPAY_OCTETS 0x0000026c
#define MMC_RX_IPV6_GD 0x00000224
#define MMC_RX_IPV6_HDERR 0x00000228
#define MMC_RX_IPV6_NOPAY 0x0000022c
/* Protocols*/
#define MMC_RX_UDP_GD 0x00000230
#define MMC_RX_UDP_ERR 0x00000234
#define MMC_RX_TCP_GD 0x00000238
#define MMC_RX_TCP_ERR 0x0000023c
#define MMC_RX_ICMP_GD 0x00000240
#define MMC_RX_ICMP_ERR 0x00000244
#define MMC_RX_UDP_GD_OCTETS 0x00000270
#define MMC_RX_UDP_ERR_OCTETS 0x00000274
#define MMC_RX_TCP_GD_OCTETS 0x00000278
#define MMC_RX_TCP_ERR_OCTETS 0x0000027c
#define MMC_RX_ICMP_GD_OCTETS 0x00000280
#define MMC_RX_ICMP_ERR_OCTETS 0x00000284
void dwmac_mmc_ctrl(void __iomem *ioaddr, unsigned int mode)
{
u32 value = readl(ioaddr + MMC_CNTRL);
value |= (mode & 0x3F);
writel(value, ioaddr + MMC_CNTRL);
pr_debug("stmmac: MMC ctrl register (offset 0x%x): 0x%08x\n",
MMC_CNTRL, value);
}
/* To mask all all interrupts.*/
void dwmac_mmc_intr_all_mask(void __iomem *ioaddr)
{
writel(MMC_DEFAULT_MASK, ioaddr + MMC_RX_INTR_MASK);
writel(MMC_DEFAULT_MASK, ioaddr + MMC_TX_INTR_MASK);
writel(MMC_DEFAULT_MASK, ioaddr + MMC_RX_IPC_INTR_MASK);
}
/* This reads the MAC core counters (if actaully supported).
* by default the MMC core is programmed to reset each
* counter after a read. So all the field of the mmc struct
* have to be incremented.
*/
void dwmac_mmc_read(void __iomem *ioaddr, struct stmmac_counters *mmc)
{
mmc->mmc_tx_octetcount_gb += readl(ioaddr + MMC_TX_OCTETCOUNT_GB);
mmc->mmc_tx_framecount_gb += readl(ioaddr + MMC_TX_FRAMECOUNT_GB);
mmc->mmc_tx_broadcastframe_g += readl(ioaddr + MMC_TX_BROADCASTFRAME_G);
mmc->mmc_tx_multicastframe_g += readl(ioaddr + MMC_TX_MULTICASTFRAME_G);
mmc->mmc_tx_64_octets_gb += readl(ioaddr + MMC_TX_64_OCTETS_GB);
mmc->mmc_tx_65_to_127_octets_gb +=
readl(ioaddr + MMC_TX_65_TO_127_OCTETS_GB);
mmc->mmc_tx_128_to_255_octets_gb +=
readl(ioaddr + MMC_TX_128_TO_255_OCTETS_GB);
mmc->mmc_tx_256_to_511_octets_gb +=
readl(ioaddr + MMC_TX_256_TO_511_OCTETS_GB);
mmc->mmc_tx_512_to_1023_octets_gb +=
readl(ioaddr + MMC_TX_512_TO_1023_OCTETS_GB);
mmc->mmc_tx_1024_to_max_octets_gb +=
readl(ioaddr + MMC_TX_1024_TO_MAX_OCTETS_GB);
mmc->mmc_tx_unicast_gb += readl(ioaddr + MMC_TX_UNICAST_GB);
mmc->mmc_tx_multicast_gb += readl(ioaddr + MMC_TX_MULTICAST_GB);
mmc->mmc_tx_broadcast_gb += readl(ioaddr + MMC_TX_BROADCAST_GB);
mmc->mmc_tx_underflow_error += readl(ioaddr + MMC_TX_UNDERFLOW_ERROR);
mmc->mmc_tx_singlecol_g += readl(ioaddr + MMC_TX_SINGLECOL_G);
mmc->mmc_tx_multicol_g += readl(ioaddr + MMC_TX_MULTICOL_G);
mmc->mmc_tx_deferred += readl(ioaddr + MMC_TX_DEFERRED);
mmc->mmc_tx_latecol += readl(ioaddr + MMC_TX_LATECOL);
mmc->mmc_tx_exesscol += readl(ioaddr + MMC_TX_EXESSCOL);
mmc->mmc_tx_carrier_error += readl(ioaddr + MMC_TX_CARRIER_ERROR);
mmc->mmc_tx_octetcount_g += readl(ioaddr + MMC_TX_OCTETCOUNT_G);
mmc->mmc_tx_framecount_g += readl(ioaddr + MMC_TX_FRAMECOUNT_G);
mmc->mmc_tx_excessdef += readl(ioaddr + MMC_TX_EXCESSDEF);
mmc->mmc_tx_pause_frame += readl(ioaddr + MMC_TX_PAUSE_FRAME);
mmc->mmc_tx_vlan_frame_g += readl(ioaddr + MMC_TX_VLAN_FRAME_G);
/* MMC RX counter registers */
mmc->mmc_rx_framecount_gb += readl(ioaddr + MMC_RX_FRAMECOUNT_GB);
mmc->mmc_rx_octetcount_gb += readl(ioaddr + MMC_RX_OCTETCOUNT_GB);
mmc->mmc_rx_octetcount_g += readl(ioaddr + MMC_RX_OCTETCOUNT_G);
mmc->mmc_rx_broadcastframe_g += readl(ioaddr + MMC_RX_BROADCASTFRAME_G);
mmc->mmc_rx_multicastframe_g += readl(ioaddr + MMC_RX_MULTICASTFRAME_G);
mmc->mmc_rx_crc_error += readl(ioaddr + MMC_RX_CRC_ERRROR);
mmc->mmc_rx_align_error += readl(ioaddr + MMC_RX_ALIGN_ERROR);
mmc->mmc_rx_run_error += readl(ioaddr + MMC_RX_RUN_ERROR);
mmc->mmc_rx_jabber_error += readl(ioaddr + MMC_RX_JABBER_ERROR);
mmc->mmc_rx_undersize_g += readl(ioaddr + MMC_RX_UNDERSIZE_G);
mmc->mmc_rx_oversize_g += readl(ioaddr + MMC_RX_OVERSIZE_G);
mmc->mmc_rx_64_octets_gb += readl(ioaddr + MMC_RX_64_OCTETS_GB);
mmc->mmc_rx_65_to_127_octets_gb +=
readl(ioaddr + MMC_RX_65_TO_127_OCTETS_GB);
mmc->mmc_rx_128_to_255_octets_gb +=
readl(ioaddr + MMC_RX_128_TO_255_OCTETS_GB);
mmc->mmc_rx_256_to_511_octets_gb +=
readl(ioaddr + MMC_RX_256_TO_511_OCTETS_GB);
mmc->mmc_rx_512_to_1023_octets_gb +=
readl(ioaddr + MMC_RX_512_TO_1023_OCTETS_GB);
mmc->mmc_rx_1024_to_max_octets_gb +=
readl(ioaddr + MMC_RX_1024_TO_MAX_OCTETS_GB);
mmc->mmc_rx_unicast_g += readl(ioaddr + MMC_RX_UNICAST_G);
mmc->mmc_rx_length_error += readl(ioaddr + MMC_RX_LENGTH_ERROR);
mmc->mmc_rx_autofrangetype += readl(ioaddr + MMC_RX_AUTOFRANGETYPE);
mmc->mmc_rx_pause_frames += readl(ioaddr + MMC_RX_PAUSE_FRAMES);
mmc->mmc_rx_fifo_overflow += readl(ioaddr + MMC_RX_FIFO_OVERFLOW);
mmc->mmc_rx_vlan_frames_gb += readl(ioaddr + MMC_RX_VLAN_FRAMES_GB);
mmc->mmc_rx_watchdog_error += readl(ioaddr + MMC_RX_WATCHDOG_ERROR);
/* IPC */
mmc->mmc_rx_ipc_intr_mask += readl(ioaddr + MMC_RX_IPC_INTR_MASK);
mmc->mmc_rx_ipc_intr += readl(ioaddr + MMC_RX_IPC_INTR);
/* IPv4 */
mmc->mmc_rx_ipv4_gd += readl(ioaddr + MMC_RX_IPV4_GD);
mmc->mmc_rx_ipv4_hderr += readl(ioaddr + MMC_RX_IPV4_HDERR);
mmc->mmc_rx_ipv4_nopay += readl(ioaddr + MMC_RX_IPV4_NOPAY);
mmc->mmc_rx_ipv4_frag += readl(ioaddr + MMC_RX_IPV4_FRAG);
mmc->mmc_rx_ipv4_udsbl += readl(ioaddr + MMC_RX_IPV4_UDSBL);
mmc->mmc_rx_ipv4_gd_octets += readl(ioaddr + MMC_RX_IPV4_GD_OCTETS);
mmc->mmc_rx_ipv4_hderr_octets +=
readl(ioaddr + MMC_RX_IPV4_HDERR_OCTETS);
mmc->mmc_rx_ipv4_nopay_octets +=
readl(ioaddr + MMC_RX_IPV4_NOPAY_OCTETS);
mmc->mmc_rx_ipv4_frag_octets += readl(ioaddr + MMC_RX_IPV4_FRAG_OCTETS);
mmc->mmc_rx_ipv4_udsbl_octets +=
readl(ioaddr + MMC_RX_IPV4_UDSBL_OCTETS);
/* IPV6 */
mmc->mmc_rx_ipv6_gd_octets += readl(ioaddr + MMC_RX_IPV6_GD_OCTETS);
mmc->mmc_rx_ipv6_hderr_octets +=
readl(ioaddr + MMC_RX_IPV6_HDERR_OCTETS);
mmc->mmc_rx_ipv6_nopay_octets +=
readl(ioaddr + MMC_RX_IPV6_NOPAY_OCTETS);
mmc->mmc_rx_ipv6_gd += readl(ioaddr + MMC_RX_IPV6_GD);
mmc->mmc_rx_ipv6_hderr += readl(ioaddr + MMC_RX_IPV6_HDERR);
mmc->mmc_rx_ipv6_nopay += readl(ioaddr + MMC_RX_IPV6_NOPAY);
/* Protocols */
mmc->mmc_rx_udp_gd += readl(ioaddr + MMC_RX_UDP_GD);
mmc->mmc_rx_udp_err += readl(ioaddr + MMC_RX_UDP_ERR);
mmc->mmc_rx_tcp_gd += readl(ioaddr + MMC_RX_TCP_GD);
mmc->mmc_rx_tcp_err += readl(ioaddr + MMC_RX_TCP_ERR);
mmc->mmc_rx_icmp_gd += readl(ioaddr + MMC_RX_ICMP_GD);
mmc->mmc_rx_icmp_err += readl(ioaddr + MMC_RX_ICMP_ERR);
mmc->mmc_rx_udp_gd_octets += readl(ioaddr + MMC_RX_UDP_GD_OCTETS);
mmc->mmc_rx_udp_err_octets += readl(ioaddr + MMC_RX_UDP_ERR_OCTETS);
mmc->mmc_rx_tcp_gd_octets += readl(ioaddr + MMC_RX_TCP_GD_OCTETS);
mmc->mmc_rx_tcp_err_octets += readl(ioaddr + MMC_RX_TCP_ERR_OCTETS);
mmc->mmc_rx_icmp_gd_octets += readl(ioaddr + MMC_RX_ICMP_GD_OCTETS);
mmc->mmc_rx_icmp_err_octets += readl(ioaddr + MMC_RX_ICMP_ERR_OCTETS);
}

View file

@ -0,0 +1,273 @@
/*******************************************************************************
This contains the functions to handle the normal descriptors.
Copyright (C) 2007-2009 STMicroelectronics Ltd
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
#include <linux/stmmac.h>
#include "common.h"
#include "descs_com.h"
static int ndesc_get_tx_status(void *data, struct stmmac_extra_stats *x,
struct dma_desc *p, void __iomem *ioaddr)
{
int ret = 0;
struct net_device_stats *stats = (struct net_device_stats *)data;
if (unlikely(p->des01.tx.error_summary)) {
if (unlikely(p->des01.tx.underflow_error)) {
x->tx_underflow++;
stats->tx_fifo_errors++;
}
if (unlikely(p->des01.tx.no_carrier)) {
x->tx_carrier++;
stats->tx_carrier_errors++;
}
if (unlikely(p->des01.tx.loss_carrier)) {
x->tx_losscarrier++;
stats->tx_carrier_errors++;
}
if (unlikely((p->des01.tx.excessive_deferral) ||
(p->des01.tx.excessive_collisions) ||
(p->des01.tx.late_collision)))
stats->collisions += p->des01.tx.collision_count;
ret = -1;
}
if (p->des01.etx.vlan_frame)
x->tx_vlan++;
if (unlikely(p->des01.tx.deferred))
x->tx_deferred++;
return ret;
}
static int ndesc_get_tx_len(struct dma_desc *p)
{
return p->des01.tx.buffer1_size;
}
/* This function verifies if each incoming frame has some errors
* and, if required, updates the multicast statistics.
* In case of success, it returns good_frame because the GMAC device
* is supposed to be able to compute the csum in HW. */
static int ndesc_get_rx_status(void *data, struct stmmac_extra_stats *x,
struct dma_desc *p)
{
int ret = good_frame;
struct net_device_stats *stats = (struct net_device_stats *)data;
if (unlikely(p->des01.rx.last_descriptor == 0)) {
pr_warn("%s: Oversized frame spanned multiple buffers\n",
__func__);
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.sa_filter_fail))
x->sa_filter_fail++;
if (unlikely(p->des01.rx.overflow_error))
x->overflow_error++;
if (unlikely(p->des01.rx.ipc_csum_error))
x->ipc_csum_error++;
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))
x->dribbling_bit++;
if (unlikely(p->des01.rx.length_error)) {
x->rx_length++;
ret = discard_frame;
}
if (unlikely(p->des01.rx.mii_error)) {
x->rx_mii++;
ret = discard_frame;
}
#ifdef STMMAC_VLAN_TAG_USED
if (p->des01.rx.vlan_tag)
x->vlan_tag++;
#endif
return ret;
}
static void ndesc_init_rx_desc(struct dma_desc *p, int disable_rx_ic, int mode,
int end)
{
p->des01.rx.own = 1;
p->des01.rx.buffer1_size = BUF_SIZE_2KiB - 1;
if (mode == STMMAC_CHAIN_MODE)
ndesc_rx_set_on_chain(p, end);
else
ndesc_rx_set_on_ring(p, end);
if (disable_rx_ic)
p->des01.rx.disable_ic = 1;
}
static void ndesc_init_tx_desc(struct dma_desc *p, int mode, int end)
{
p->des01.tx.own = 0;
if (mode == STMMAC_CHAIN_MODE)
ndesc_tx_set_on_chain(p, end);
else
ndesc_tx_set_on_ring(p, end);
}
static int ndesc_get_tx_owner(struct dma_desc *p)
{
return p->des01.tx.own;
}
static int ndesc_get_rx_owner(struct dma_desc *p)
{
return p->des01.rx.own;
}
static void ndesc_set_tx_owner(struct dma_desc *p)
{
p->des01.tx.own = 1;
}
static void ndesc_set_rx_owner(struct dma_desc *p)
{
p->des01.rx.own = 1;
}
static int ndesc_get_tx_ls(struct dma_desc *p)
{
return p->des01.tx.last_segment;
}
static void ndesc_release_tx_desc(struct dma_desc *p, int mode)
{
int ter = p->des01.tx.end_ring;
memset(p, 0, offsetof(struct dma_desc, des2));
if (mode == STMMAC_CHAIN_MODE)
ndesc_end_tx_desc_on_chain(p, ter);
else
ndesc_end_tx_desc_on_ring(p, ter);
}
static void ndesc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len,
int csum_flag, int mode)
{
p->des01.tx.first_segment = is_fs;
if (mode == STMMAC_CHAIN_MODE)
norm_set_tx_desc_len_on_chain(p, len);
else
norm_set_tx_desc_len_on_ring(p, len);
if (likely(csum_flag))
p->des01.tx.checksum_insertion = cic_full;
}
static void ndesc_clear_tx_ic(struct dma_desc *p)
{
p->des01.tx.interrupt = 0;
}
static void ndesc_close_tx_desc(struct dma_desc *p)
{
p->des01.tx.last_segment = 1;
p->des01.tx.interrupt = 1;
}
static int ndesc_get_rx_frame_len(struct dma_desc *p, int rx_coe_type)
{
/* The type-1 checksum offload engines append the checksum at
* the end of frame and the two bytes of checksum are added in
* the length.
* Adjust for that in the framelen for type-1 checksum offload
* engines. */
if (rx_coe_type == STMMAC_RX_COE_TYPE1)
return p->des01.rx.frame_length - 2;
else
return p->des01.rx.frame_length;
}
static void ndesc_enable_tx_timestamp(struct dma_desc *p)
{
p->des01.tx.time_stamp_enable = 1;
}
static int ndesc_get_tx_timestamp_status(struct dma_desc *p)
{
return p->des01.tx.time_stamp_status;
}
static u64 ndesc_get_timestamp(void *desc, u32 ats)
{
struct dma_desc *p = (struct dma_desc *)desc;
u64 ns;
ns = p->des2;
/* convert high/sec time stamp value to nanosecond */
ns += p->des3 * 1000000000ULL;
return ns;
}
static int ndesc_get_rx_timestamp_status(void *desc, u32 ats)
{
struct dma_desc *p = (struct dma_desc *)desc;
if ((p->des2 == 0xffffffff) && (p->des3 == 0xffffffff))
/* timestamp is corrupted, hence don't store it */
return 0;
else
return 1;
}
const struct stmmac_desc_ops ndesc_ops = {
.tx_status = ndesc_get_tx_status,
.rx_status = ndesc_get_rx_status,
.get_tx_len = ndesc_get_tx_len,
.init_rx_desc = ndesc_init_rx_desc,
.init_tx_desc = ndesc_init_tx_desc,
.get_tx_owner = ndesc_get_tx_owner,
.get_rx_owner = ndesc_get_rx_owner,
.release_tx_desc = ndesc_release_tx_desc,
.prepare_tx_desc = ndesc_prepare_tx_desc,
.clear_tx_ic = ndesc_clear_tx_ic,
.close_tx_desc = ndesc_close_tx_desc,
.get_tx_ls = ndesc_get_tx_ls,
.set_tx_owner = ndesc_set_tx_owner,
.set_rx_owner = ndesc_set_rx_owner,
.get_rx_frame_len = ndesc_get_rx_frame_len,
.enable_tx_timestamp = ndesc_enable_tx_timestamp,
.get_tx_timestamp_status = ndesc_get_tx_timestamp_status,
.get_timestamp = ndesc_get_timestamp,
.get_rx_timestamp_status = ndesc_get_rx_timestamp_status,
};

View file

@ -0,0 +1,142 @@
/*******************************************************************************
Specialised functions for managing Ring mode
Copyright(C) 2011 STMicroelectronics Ltd
It defines all the functions used to handle the normal/enhanced
descriptors in case of the DMA is configured to work in chained or
in ring mode.
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 "stmmac.h"
static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
{
struct stmmac_priv *priv = (struct stmmac_priv *)p;
unsigned int txsize = priv->dma_tx_size;
unsigned int entry = priv->cur_tx % txsize;
struct dma_desc *desc;
unsigned int nopaged_len = skb_headlen(skb);
unsigned int bmax, len;
if (priv->extend_desc)
desc = (struct dma_desc *)(priv->dma_etx + entry);
else
desc = priv->dma_tx + entry;
if (priv->plat->enh_desc)
bmax = BUF_SIZE_8KiB;
else
bmax = BUF_SIZE_2KiB;
len = nopaged_len - bmax;
if (nopaged_len > BUF_SIZE_8KiB) {
desc->des2 = dma_map_single(priv->device, skb->data,
bmax, DMA_TO_DEVICE);
if (dma_mapping_error(priv->device, desc->des2))
return -1;
priv->tx_skbuff_dma[entry].buf = desc->des2;
desc->des3 = desc->des2 + BUF_SIZE_4KiB;
priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum,
STMMAC_RING_MODE);
wmb();
priv->tx_skbuff[entry] = NULL;
entry = (++priv->cur_tx) % txsize;
if (priv->extend_desc)
desc = (struct dma_desc *)(priv->dma_etx + entry);
else
desc = priv->dma_tx + entry;
desc->des2 = dma_map_single(priv->device, skb->data + bmax,
len, DMA_TO_DEVICE);
if (dma_mapping_error(priv->device, desc->des2))
return -1;
priv->tx_skbuff_dma[entry].buf = desc->des2;
desc->des3 = desc->des2 + BUF_SIZE_4KiB;
priv->hw->desc->prepare_tx_desc(desc, 0, len, csum,
STMMAC_RING_MODE);
wmb();
priv->hw->desc->set_tx_owner(desc);
} else {
desc->des2 = dma_map_single(priv->device, skb->data,
nopaged_len, DMA_TO_DEVICE);
if (dma_mapping_error(priv->device, desc->des2))
return -1;
priv->tx_skbuff_dma[entry].buf = desc->des2;
desc->des3 = desc->des2 + BUF_SIZE_4KiB;
priv->hw->desc->prepare_tx_desc(desc, 1, nopaged_len, csum,
STMMAC_RING_MODE);
}
return entry;
}
static unsigned int stmmac_is_jumbo_frm(int len, int enh_desc)
{
unsigned int ret = 0;
if (len >= BUF_SIZE_4KiB)
ret = 1;
return ret;
}
static void stmmac_refill_desc3(void *priv_ptr, struct dma_desc *p)
{
struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr;
/* Fill DES3 in case of RING mode */
if (priv->dma_buf_sz >= BUF_SIZE_8KiB)
p->des3 = p->des2 + BUF_SIZE_8KiB;
}
/* In ring mode we need to fill the desc3 because it is used as buffer */
static void stmmac_init_desc3(struct dma_desc *p)
{
p->des3 = p->des2 + BUF_SIZE_8KiB;
}
static void stmmac_clean_desc3(void *priv_ptr, struct dma_desc *p)
{
if (unlikely(p->des3))
p->des3 = 0;
}
static int stmmac_set_16kib_bfsize(int mtu)
{
int ret = 0;
if (unlikely(mtu >= BUF_SIZE_8KiB))
ret = BUF_SIZE_16KiB;
return ret;
}
const struct stmmac_mode_ops ring_mode_ops = {
.is_jumbo_frm = stmmac_is_jumbo_frm,
.jumbo_frm = stmmac_jumbo_frm,
.refill_desc3 = stmmac_refill_desc3,
.init_desc3 = stmmac_init_desc3,
.clean_desc3 = stmmac_clean_desc3,
.set_16kib_bfsize = stmmac_set_16kib_bfsize,
};

View file

@ -0,0 +1,212 @@
/*******************************************************************************
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>
*******************************************************************************/
#ifndef __STMMAC_H__
#define __STMMAC_H__
#define STMMAC_RESOURCE_NAME "stmmaceth"
#define DRV_MODULE_VERSION "March_2013"
#include <linux/clk.h>
#include <linux/stmmac.h>
#include <linux/phy.h>
#include <linux/pci.h>
#include "common.h"
#include <linux/ptp_clock_kernel.h>
#include <linux/reset.h>
struct stmmac_tx_info {
dma_addr_t buf;
bool map_as_page;
};
struct stmmac_priv {
/* Frequently used values are kept adjacent for cache effect */
struct dma_extended_desc *dma_etx ____cacheline_aligned_in_smp;
struct dma_desc *dma_tx;
struct sk_buff **tx_skbuff;
unsigned int cur_tx;
unsigned int dirty_tx;
unsigned int dma_tx_size;
u32 tx_count_frames;
u32 tx_coal_frames;
u32 tx_coal_timer;
struct stmmac_tx_info *tx_skbuff_dma;
dma_addr_t dma_tx_phy;
int tx_coalesce;
int hwts_tx_en;
spinlock_t tx_lock;
bool tx_path_in_lpi_mode;
struct timer_list txtimer;
struct dma_desc *dma_rx ____cacheline_aligned_in_smp;
struct dma_extended_desc *dma_erx;
struct sk_buff **rx_skbuff;
unsigned int cur_rx;
unsigned int dirty_rx;
unsigned int dma_rx_size;
unsigned int dma_buf_sz;
u32 rx_riwt;
int hwts_rx_en;
dma_addr_t *rx_skbuff_dma;
dma_addr_t dma_rx_phy;
struct napi_struct napi ____cacheline_aligned_in_smp;
void __iomem *ioaddr;
struct net_device *dev;
struct device *device;
struct mac_device_info *hw;
spinlock_t lock;
struct phy_device *phydev ____cacheline_aligned_in_smp;
int oldlink;
int speed;
int oldduplex;
unsigned int flow_ctrl;
unsigned int pause;
struct mii_bus *mii;
int mii_irq[PHY_MAX_ADDR];
struct stmmac_extra_stats xstats ____cacheline_aligned_in_smp;
struct plat_stmmacenet_data *plat;
struct dma_features dma_cap;
struct stmmac_counters mmc;
int hw_cap_support;
int synopsys_id;
u32 msg_enable;
int wolopts;
int wol_irq;
struct clk *stmmac_clk;
struct reset_control *stmmac_rst;
int clk_csr;
struct timer_list eee_ctrl_timer;
int lpi_irq;
int eee_enabled;
int eee_active;
int tx_lpi_timer;
int pcs;
unsigned int mode;
int extend_desc;
struct ptp_clock *ptp_clock;
struct ptp_clock_info ptp_clock_ops;
unsigned int default_addend;
struct clk *clk_ptp_ref;
unsigned int clk_ptp_rate;
u32 adv_ts;
int use_riwt;
int irq_wake;
spinlock_t ptp_lock;
};
int stmmac_mdio_unregister(struct net_device *ndev);
int stmmac_mdio_register(struct net_device *ndev);
int stmmac_mdio_reset(struct mii_bus *mii);
void stmmac_set_ethtool_ops(struct net_device *netdev);
extern const struct stmmac_desc_ops enh_desc_ops;
extern const struct stmmac_desc_ops ndesc_ops;
extern const struct stmmac_hwtimestamp stmmac_ptp;
int stmmac_ptp_register(struct stmmac_priv *priv);
void stmmac_ptp_unregister(struct stmmac_priv *priv);
int stmmac_resume(struct net_device *ndev);
int stmmac_suspend(struct net_device *ndev);
int stmmac_dvr_remove(struct net_device *ndev);
struct stmmac_priv *stmmac_dvr_probe(struct device *device,
struct plat_stmmacenet_data *plat_dat,
void __iomem *addr);
void stmmac_disable_eee_mode(struct stmmac_priv *priv);
bool stmmac_eee_init(struct stmmac_priv *priv);
#ifdef CONFIG_STMMAC_PLATFORM
#ifdef CONFIG_DWMAC_MESON
extern const struct stmmac_of_data meson6_dwmac_data;
#endif
#ifdef CONFIG_DWMAC_SUNXI
extern const struct stmmac_of_data sun7i_gmac_data;
#endif
#ifdef CONFIG_DWMAC_STI
extern const struct stmmac_of_data stih4xx_dwmac_data;
extern const struct stmmac_of_data stid127_dwmac_data;
#endif
#ifdef CONFIG_DWMAC_SOCFPGA
extern const struct stmmac_of_data socfpga_gmac_data;
#endif
extern struct platform_driver stmmac_pltfr_driver;
static inline int stmmac_register_platform(void)
{
int err;
err = platform_driver_register(&stmmac_pltfr_driver);
if (err)
pr_err("stmmac: failed to register the platform driver\n");
return err;
}
static inline void stmmac_unregister_platform(void)
{
platform_driver_unregister(&stmmac_pltfr_driver);
}
#else
static inline int stmmac_register_platform(void)
{
pr_debug("stmmac: do not register the platf driver\n");
return 0;
}
static inline void stmmac_unregister_platform(void)
{
}
#endif /* CONFIG_STMMAC_PLATFORM */
#ifdef CONFIG_STMMAC_PCI
extern struct pci_driver stmmac_pci_driver;
static inline int stmmac_register_pci(void)
{
int err;
err = pci_register_driver(&stmmac_pci_driver);
if (err)
pr_err("stmmac: failed to register the PCI driver\n");
return err;
}
static inline void stmmac_unregister_pci(void)
{
pci_unregister_driver(&stmmac_pci_driver);
}
#else
static inline int stmmac_register_pci(void)
{
pr_debug("stmmac: do not register the PCI driver\n");
return 0;
}
static inline void stmmac_unregister_pci(void)
{
}
#endif /* CONFIG_STMMAC_PCI */
#endif /* __STMMAC_H__ */

View file

@ -0,0 +1,778 @@
/*******************************************************************************
STMMAC Ethtool support
Copyright (C) 2007-2009 STMicroelectronics Ltd
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/interrupt.h>
#include <linux/mii.h>
#include <linux/phy.h>
#include <linux/net_tstamp.h>
#include <asm/io.h>
#include "stmmac.h"
#include "dwmac_dma.h"
#define REG_SPACE_SIZE 0x1054
#define MAC100_ETHTOOL_NAME "st_mac100"
#define GMAC_ETHTOOL_NAME "st_gmac"
struct stmmac_stats {
char stat_string[ETH_GSTRING_LEN];
int sizeof_stat;
int stat_offset;
};
#define STMMAC_STAT(m) \
{ #m, FIELD_SIZEOF(struct stmmac_extra_stats, m), \
offsetof(struct stmmac_priv, xstats.m)}
static const struct stmmac_stats stmmac_gstrings_stats[] = {
/* Transmit errors */
STMMAC_STAT(tx_underflow),
STMMAC_STAT(tx_carrier),
STMMAC_STAT(tx_losscarrier),
STMMAC_STAT(vlan_tag),
STMMAC_STAT(tx_deferred),
STMMAC_STAT(tx_vlan),
STMMAC_STAT(tx_jabber),
STMMAC_STAT(tx_frame_flushed),
STMMAC_STAT(tx_payload_error),
STMMAC_STAT(tx_ip_header_error),
/* Receive errors */
STMMAC_STAT(rx_desc),
STMMAC_STAT(sa_filter_fail),
STMMAC_STAT(overflow_error),
STMMAC_STAT(ipc_csum_error),
STMMAC_STAT(rx_collision),
STMMAC_STAT(rx_crc),
STMMAC_STAT(dribbling_bit),
STMMAC_STAT(rx_length),
STMMAC_STAT(rx_mii),
STMMAC_STAT(rx_multicast),
STMMAC_STAT(rx_gmac_overflow),
STMMAC_STAT(rx_watchdog),
STMMAC_STAT(da_rx_filter_fail),
STMMAC_STAT(sa_rx_filter_fail),
STMMAC_STAT(rx_missed_cntr),
STMMAC_STAT(rx_overflow_cntr),
STMMAC_STAT(rx_vlan),
/* Tx/Rx IRQ error info */
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),
/* Tx/Rx IRQ Events */
STMMAC_STAT(rx_early_irq),
STMMAC_STAT(threshold),
STMMAC_STAT(tx_pkt_n),
STMMAC_STAT(rx_pkt_n),
STMMAC_STAT(normal_irq_n),
STMMAC_STAT(rx_normal_irq_n),
STMMAC_STAT(napi_poll),
STMMAC_STAT(tx_normal_irq_n),
STMMAC_STAT(tx_clean),
STMMAC_STAT(tx_reset_ic_bit),
STMMAC_STAT(irq_receive_pmt_irq_n),
/* MMC info */
STMMAC_STAT(mmc_tx_irq_n),
STMMAC_STAT(mmc_rx_irq_n),
STMMAC_STAT(mmc_rx_csum_offload_irq_n),
/* EEE */
STMMAC_STAT(irq_tx_path_in_lpi_mode_n),
STMMAC_STAT(irq_tx_path_exit_lpi_mode_n),
STMMAC_STAT(irq_rx_path_in_lpi_mode_n),
STMMAC_STAT(irq_rx_path_exit_lpi_mode_n),
STMMAC_STAT(phy_eee_wakeup_error_n),
/* Extended RDES status */
STMMAC_STAT(ip_hdr_err),
STMMAC_STAT(ip_payload_err),
STMMAC_STAT(ip_csum_bypassed),
STMMAC_STAT(ipv4_pkt_rcvd),
STMMAC_STAT(ipv6_pkt_rcvd),
STMMAC_STAT(rx_msg_type_ext_no_ptp),
STMMAC_STAT(rx_msg_type_sync),
STMMAC_STAT(rx_msg_type_follow_up),
STMMAC_STAT(rx_msg_type_delay_req),
STMMAC_STAT(rx_msg_type_delay_resp),
STMMAC_STAT(rx_msg_type_pdelay_req),
STMMAC_STAT(rx_msg_type_pdelay_resp),
STMMAC_STAT(rx_msg_type_pdelay_follow_up),
STMMAC_STAT(ptp_frame_type),
STMMAC_STAT(ptp_ver),
STMMAC_STAT(timestamp_dropped),
STMMAC_STAT(av_pkt_rcvd),
STMMAC_STAT(av_tagged_pkt_rcvd),
STMMAC_STAT(vlan_tag_priority_val),
STMMAC_STAT(l3_filter_match),
STMMAC_STAT(l4_filter_match),
STMMAC_STAT(l3_l4_filter_no_match),
/* PCS */
STMMAC_STAT(irq_pcs_ane_n),
STMMAC_STAT(irq_pcs_link_n),
STMMAC_STAT(irq_rgmii_n),
};
#define STMMAC_STATS_LEN ARRAY_SIZE(stmmac_gstrings_stats)
/* HW MAC Management counters (if supported) */
#define STMMAC_MMC_STAT(m) \
{ #m, FIELD_SIZEOF(struct stmmac_counters, m), \
offsetof(struct stmmac_priv, mmc.m)}
static const struct stmmac_stats stmmac_mmc[] = {
STMMAC_MMC_STAT(mmc_tx_octetcount_gb),
STMMAC_MMC_STAT(mmc_tx_framecount_gb),
STMMAC_MMC_STAT(mmc_tx_broadcastframe_g),
STMMAC_MMC_STAT(mmc_tx_multicastframe_g),
STMMAC_MMC_STAT(mmc_tx_64_octets_gb),
STMMAC_MMC_STAT(mmc_tx_65_to_127_octets_gb),
STMMAC_MMC_STAT(mmc_tx_128_to_255_octets_gb),
STMMAC_MMC_STAT(mmc_tx_256_to_511_octets_gb),
STMMAC_MMC_STAT(mmc_tx_512_to_1023_octets_gb),
STMMAC_MMC_STAT(mmc_tx_1024_to_max_octets_gb),
STMMAC_MMC_STAT(mmc_tx_unicast_gb),
STMMAC_MMC_STAT(mmc_tx_multicast_gb),
STMMAC_MMC_STAT(mmc_tx_broadcast_gb),
STMMAC_MMC_STAT(mmc_tx_underflow_error),
STMMAC_MMC_STAT(mmc_tx_singlecol_g),
STMMAC_MMC_STAT(mmc_tx_multicol_g),
STMMAC_MMC_STAT(mmc_tx_deferred),
STMMAC_MMC_STAT(mmc_tx_latecol),
STMMAC_MMC_STAT(mmc_tx_exesscol),
STMMAC_MMC_STAT(mmc_tx_carrier_error),
STMMAC_MMC_STAT(mmc_tx_octetcount_g),
STMMAC_MMC_STAT(mmc_tx_framecount_g),
STMMAC_MMC_STAT(mmc_tx_excessdef),
STMMAC_MMC_STAT(mmc_tx_pause_frame),
STMMAC_MMC_STAT(mmc_tx_vlan_frame_g),
STMMAC_MMC_STAT(mmc_rx_framecount_gb),
STMMAC_MMC_STAT(mmc_rx_octetcount_gb),
STMMAC_MMC_STAT(mmc_rx_octetcount_g),
STMMAC_MMC_STAT(mmc_rx_broadcastframe_g),
STMMAC_MMC_STAT(mmc_rx_multicastframe_g),
STMMAC_MMC_STAT(mmc_rx_crc_error),
STMMAC_MMC_STAT(mmc_rx_align_error),
STMMAC_MMC_STAT(mmc_rx_run_error),
STMMAC_MMC_STAT(mmc_rx_jabber_error),
STMMAC_MMC_STAT(mmc_rx_undersize_g),
STMMAC_MMC_STAT(mmc_rx_oversize_g),
STMMAC_MMC_STAT(mmc_rx_64_octets_gb),
STMMAC_MMC_STAT(mmc_rx_65_to_127_octets_gb),
STMMAC_MMC_STAT(mmc_rx_128_to_255_octets_gb),
STMMAC_MMC_STAT(mmc_rx_256_to_511_octets_gb),
STMMAC_MMC_STAT(mmc_rx_512_to_1023_octets_gb),
STMMAC_MMC_STAT(mmc_rx_1024_to_max_octets_gb),
STMMAC_MMC_STAT(mmc_rx_unicast_g),
STMMAC_MMC_STAT(mmc_rx_length_error),
STMMAC_MMC_STAT(mmc_rx_autofrangetype),
STMMAC_MMC_STAT(mmc_rx_pause_frames),
STMMAC_MMC_STAT(mmc_rx_fifo_overflow),
STMMAC_MMC_STAT(mmc_rx_vlan_frames_gb),
STMMAC_MMC_STAT(mmc_rx_watchdog_error),
STMMAC_MMC_STAT(mmc_rx_ipc_intr_mask),
STMMAC_MMC_STAT(mmc_rx_ipc_intr),
STMMAC_MMC_STAT(mmc_rx_ipv4_gd),
STMMAC_MMC_STAT(mmc_rx_ipv4_hderr),
STMMAC_MMC_STAT(mmc_rx_ipv4_nopay),
STMMAC_MMC_STAT(mmc_rx_ipv4_frag),
STMMAC_MMC_STAT(mmc_rx_ipv4_udsbl),
STMMAC_MMC_STAT(mmc_rx_ipv4_gd_octets),
STMMAC_MMC_STAT(mmc_rx_ipv4_hderr_octets),
STMMAC_MMC_STAT(mmc_rx_ipv4_nopay_octets),
STMMAC_MMC_STAT(mmc_rx_ipv4_frag_octets),
STMMAC_MMC_STAT(mmc_rx_ipv4_udsbl_octets),
STMMAC_MMC_STAT(mmc_rx_ipv6_gd_octets),
STMMAC_MMC_STAT(mmc_rx_ipv6_hderr_octets),
STMMAC_MMC_STAT(mmc_rx_ipv6_nopay_octets),
STMMAC_MMC_STAT(mmc_rx_ipv6_gd),
STMMAC_MMC_STAT(mmc_rx_ipv6_hderr),
STMMAC_MMC_STAT(mmc_rx_ipv6_nopay),
STMMAC_MMC_STAT(mmc_rx_udp_gd),
STMMAC_MMC_STAT(mmc_rx_udp_err),
STMMAC_MMC_STAT(mmc_rx_tcp_gd),
STMMAC_MMC_STAT(mmc_rx_tcp_err),
STMMAC_MMC_STAT(mmc_rx_icmp_gd),
STMMAC_MMC_STAT(mmc_rx_icmp_err),
STMMAC_MMC_STAT(mmc_rx_udp_gd_octets),
STMMAC_MMC_STAT(mmc_rx_udp_err_octets),
STMMAC_MMC_STAT(mmc_rx_tcp_gd_octets),
STMMAC_MMC_STAT(mmc_rx_tcp_err_octets),
STMMAC_MMC_STAT(mmc_rx_icmp_gd_octets),
STMMAC_MMC_STAT(mmc_rx_icmp_err_octets),
};
#define STMMAC_MMC_STATS_LEN ARRAY_SIZE(stmmac_mmc)
static void stmmac_ethtool_getdrvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
struct stmmac_priv *priv = netdev_priv(dev);
if (priv->plat->has_gmac)
strlcpy(info->driver, GMAC_ETHTOOL_NAME, sizeof(info->driver));
else
strlcpy(info->driver, MAC100_ETHTOOL_NAME,
sizeof(info->driver));
strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version));
}
static int stmmac_ethtool_getsettings(struct net_device *dev,
struct ethtool_cmd *cmd)
{
struct stmmac_priv *priv = netdev_priv(dev);
struct phy_device *phy = priv->phydev;
int rc;
if ((priv->pcs & STMMAC_PCS_RGMII) || (priv->pcs & STMMAC_PCS_SGMII)) {
struct rgmii_adv adv;
if (!priv->xstats.pcs_link) {
ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN);
cmd->duplex = DUPLEX_UNKNOWN;
return 0;
}
cmd->duplex = priv->xstats.pcs_duplex;
ethtool_cmd_speed_set(cmd, priv->xstats.pcs_speed);
/* Get and convert ADV/LP_ADV from the HW AN registers */
if (!priv->hw->mac->get_adv)
return -EOPNOTSUPP; /* should never happen indeed */
priv->hw->mac->get_adv(priv->hw, &adv);
/* Encoding of PSE bits is defined in 802.3z, 37.2.1.4 */
if (adv.pause & STMMAC_PCS_PAUSE)
cmd->advertising |= ADVERTISED_Pause;
if (adv.pause & STMMAC_PCS_ASYM_PAUSE)
cmd->advertising |= ADVERTISED_Asym_Pause;
if (adv.lp_pause & STMMAC_PCS_PAUSE)
cmd->lp_advertising |= ADVERTISED_Pause;
if (adv.lp_pause & STMMAC_PCS_ASYM_PAUSE)
cmd->lp_advertising |= ADVERTISED_Asym_Pause;
/* Reg49[3] always set because ANE is always supported */
cmd->autoneg = ADVERTISED_Autoneg;
cmd->supported |= SUPPORTED_Autoneg;
cmd->advertising |= ADVERTISED_Autoneg;
cmd->lp_advertising |= ADVERTISED_Autoneg;
if (adv.duplex) {
cmd->supported |= (SUPPORTED_1000baseT_Full |
SUPPORTED_100baseT_Full |
SUPPORTED_10baseT_Full);
cmd->advertising |= (ADVERTISED_1000baseT_Full |
ADVERTISED_100baseT_Full |
ADVERTISED_10baseT_Full);
} else {
cmd->supported |= (SUPPORTED_1000baseT_Half |
SUPPORTED_100baseT_Half |
SUPPORTED_10baseT_Half);
cmd->advertising |= (ADVERTISED_1000baseT_Half |
ADVERTISED_100baseT_Half |
ADVERTISED_10baseT_Half);
}
if (adv.lp_duplex)
cmd->lp_advertising |= (ADVERTISED_1000baseT_Full |
ADVERTISED_100baseT_Full |
ADVERTISED_10baseT_Full);
else
cmd->lp_advertising |= (ADVERTISED_1000baseT_Half |
ADVERTISED_100baseT_Half |
ADVERTISED_10baseT_Half);
cmd->port = PORT_OTHER;
return 0;
}
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;
rc = phy_ethtool_gset(phy, cmd);
return rc;
}
static int stmmac_ethtool_setsettings(struct net_device *dev,
struct ethtool_cmd *cmd)
{
struct stmmac_priv *priv = netdev_priv(dev);
struct phy_device *phy = priv->phydev;
int rc;
if ((priv->pcs & STMMAC_PCS_RGMII) || (priv->pcs & STMMAC_PCS_SGMII)) {
u32 mask = ADVERTISED_Autoneg | ADVERTISED_Pause;
/* Only support ANE */
if (cmd->autoneg != AUTONEG_ENABLE)
return -EINVAL;
mask &= (ADVERTISED_1000baseT_Half |
ADVERTISED_1000baseT_Full |
ADVERTISED_100baseT_Half |
ADVERTISED_100baseT_Full |
ADVERTISED_10baseT_Half |
ADVERTISED_10baseT_Full);
spin_lock(&priv->lock);
if (priv->hw->mac->ctrl_ane)
priv->hw->mac->ctrl_ane(priv->hw, 1);
spin_unlock(&priv->lock);
return 0;
}
spin_lock(&priv->lock);
rc = phy_ethtool_sset(phy, cmd);
spin_unlock(&priv->lock);
return rc;
}
static u32 stmmac_ethtool_getmsglevel(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
return priv->msg_enable;
}
static void stmmac_ethtool_setmsglevel(struct net_device *dev, u32 level)
{
struct stmmac_priv *priv = netdev_priv(dev);
priv->msg_enable = level;
}
static int stmmac_check_if_running(struct net_device *dev)
{
if (!netif_running(dev))
return -EBUSY;
return 0;
}
static int stmmac_ethtool_get_regs_len(struct net_device *dev)
{
return REG_SPACE_SIZE;
}
static void stmmac_ethtool_gregs(struct net_device *dev,
struct ethtool_regs *regs, void *space)
{
int i;
u32 *reg_space = (u32 *) space;
struct stmmac_priv *priv = netdev_priv(dev);
memset(reg_space, 0x0, REG_SPACE_SIZE);
if (!priv->plat->has_gmac) {
/* MAC registers */
for (i = 0; i < 12; i++)
reg_space[i] = readl(priv->ioaddr + (i * 4));
/* DMA registers */
for (i = 0; i < 9; i++)
reg_space[i + 12] =
readl(priv->ioaddr + (DMA_BUS_MODE + (i * 4)));
reg_space[22] = readl(priv->ioaddr + DMA_CUR_TX_BUF_ADDR);
reg_space[23] = readl(priv->ioaddr + DMA_CUR_RX_BUF_ADDR);
} else {
/* MAC registers */
for (i = 0; i < 55; i++)
reg_space[i] = readl(priv->ioaddr + (i * 4));
/* DMA registers */
for (i = 0; i < 22; i++)
reg_space[i + 55] =
readl(priv->ioaddr + (DMA_BUS_MODE + (i * 4)));
}
}
static void
stmmac_get_pauseparam(struct net_device *netdev,
struct ethtool_pauseparam *pause)
{
struct stmmac_priv *priv = netdev_priv(netdev);
if (priv->pcs) /* FIXME */
return;
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;
}
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;
if (priv->pcs) /* FIXME */
return -EOPNOTSUPP;
if (pause->rx_pause)
new_pause |= FLOW_RX;
if (pause->tx_pause)
new_pause |= FLOW_TX;
priv->flow_ctrl = new_pause;
phy->autoneg = pause->autoneg;
if (phy->autoneg) {
if (netif_running(netdev))
ret = phy_start_aneg(phy);
} else
priv->hw->mac->flow_ctrl(priv->hw, phy->duplex,
priv->flow_ctrl, priv->pause);
return ret;
}
static void stmmac_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *dummy, u64 *data)
{
struct stmmac_priv *priv = netdev_priv(dev);
int i, j = 0;
/* Update the DMA HW counters for dwmac10/100 */
if (!priv->plat->has_gmac)
priv->hw->dma->dma_diagnostic_fr(&dev->stats,
(void *) &priv->xstats,
priv->ioaddr);
else {
/* If supported, for new GMAC chips expose the MMC counters */
if (priv->dma_cap.rmon) {
dwmac_mmc_read(priv->ioaddr, &priv->mmc);
for (i = 0; i < STMMAC_MMC_STATS_LEN; i++) {
char *p;
p = (char *)priv + stmmac_mmc[i].stat_offset;
data[j++] = (stmmac_mmc[i].sizeof_stat ==
sizeof(u64)) ? (*(u64 *)p) :
(*(u32 *)p);
}
}
if (priv->eee_enabled) {
int val = phy_get_eee_err(priv->phydev);
if (val)
priv->xstats.phy_eee_wakeup_error_n = val;
}
}
for (i = 0; i < STMMAC_STATS_LEN; i++) {
char *p = (char *)priv + stmmac_gstrings_stats[i].stat_offset;
data[j++] = (stmmac_gstrings_stats[i].sizeof_stat ==
sizeof(u64)) ? (*(u64 *)p) : (*(u32 *)p);
}
}
static int stmmac_get_sset_count(struct net_device *netdev, int sset)
{
struct stmmac_priv *priv = netdev_priv(netdev);
int len;
switch (sset) {
case ETH_SS_STATS:
len = STMMAC_STATS_LEN;
if (priv->dma_cap.rmon)
len += STMMAC_MMC_STATS_LEN;
return len;
default:
return -EOPNOTSUPP;
}
}
static void stmmac_get_strings(struct net_device *dev, u32 stringset, u8 *data)
{
int i;
u8 *p = data;
struct stmmac_priv *priv = netdev_priv(dev);
switch (stringset) {
case ETH_SS_STATS:
if (priv->dma_cap.rmon)
for (i = 0; i < STMMAC_MMC_STATS_LEN; i++) {
memcpy(p, stmmac_mmc[i].stat_string,
ETH_GSTRING_LEN);
p += ETH_GSTRING_LEN;
}
for (i = 0; i < STMMAC_STATS_LEN; i++) {
memcpy(p, stmmac_gstrings_stats[i].stat_string,
ETH_GSTRING_LEN);
p += ETH_GSTRING_LEN;
}
break;
default:
WARN_ON(1);
break;
}
}
/* Currently only support WOL through Magic packet. */
static void stmmac_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct stmmac_priv *priv = netdev_priv(dev);
spin_lock_irq(&priv->lock);
if (device_can_wakeup(priv->device)) {
wol->supported = WAKE_MAGIC | WAKE_UCAST;
wol->wolopts = priv->wolopts;
}
spin_unlock_irq(&priv->lock);
}
static int stmmac_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct stmmac_priv *priv = netdev_priv(dev);
u32 support = WAKE_MAGIC | WAKE_UCAST;
/* By default almost all GMAC devices support the WoL via
* magic frame but we can disable it if the HW capability
* register shows no support for pmt_magic_frame. */
if ((priv->hw_cap_support) && (!priv->dma_cap.pmt_magic_frame))
wol->wolopts &= ~WAKE_MAGIC;
if (!device_can_wakeup(priv->device))
return -EINVAL;
if (wol->wolopts & ~support)
return -EINVAL;
if (wol->wolopts) {
pr_info("stmmac: wakeup enable\n");
device_set_wakeup_enable(priv->device, 1);
enable_irq_wake(priv->wol_irq);
} else {
device_set_wakeup_enable(priv->device, 0);
disable_irq_wake(priv->wol_irq);
}
spin_lock_irq(&priv->lock);
priv->wolopts = wol->wolopts;
spin_unlock_irq(&priv->lock);
return 0;
}
static int stmmac_ethtool_op_get_eee(struct net_device *dev,
struct ethtool_eee *edata)
{
struct stmmac_priv *priv = netdev_priv(dev);
if (!priv->dma_cap.eee)
return -EOPNOTSUPP;
edata->eee_enabled = priv->eee_enabled;
edata->eee_active = priv->eee_active;
edata->tx_lpi_timer = priv->tx_lpi_timer;
return phy_ethtool_get_eee(priv->phydev, edata);
}
static int stmmac_ethtool_op_set_eee(struct net_device *dev,
struct ethtool_eee *edata)
{
struct stmmac_priv *priv = netdev_priv(dev);
priv->eee_enabled = edata->eee_enabled;
if (!priv->eee_enabled)
stmmac_disable_eee_mode(priv);
else {
/* We are asking for enabling the EEE but it is safe
* to verify all by invoking the eee_init function.
* In case of failure it will return an error.
*/
priv->eee_enabled = stmmac_eee_init(priv);
if (!priv->eee_enabled)
return -EOPNOTSUPP;
/* Do not change tx_lpi_timer in case of failure */
priv->tx_lpi_timer = edata->tx_lpi_timer;
}
return phy_ethtool_set_eee(priv->phydev, edata);
}
static u32 stmmac_usec2riwt(u32 usec, struct stmmac_priv *priv)
{
unsigned long clk = clk_get_rate(priv->stmmac_clk);
if (!clk)
return 0;
return (usec * (clk / 1000000)) / 256;
}
static u32 stmmac_riwt2usec(u32 riwt, struct stmmac_priv *priv)
{
unsigned long clk = clk_get_rate(priv->stmmac_clk);
if (!clk)
return 0;
return (riwt * 256) / (clk / 1000000);
}
static int stmmac_get_coalesce(struct net_device *dev,
struct ethtool_coalesce *ec)
{
struct stmmac_priv *priv = netdev_priv(dev);
ec->tx_coalesce_usecs = priv->tx_coal_timer;
ec->tx_max_coalesced_frames = priv->tx_coal_frames;
if (priv->use_riwt)
ec->rx_coalesce_usecs = stmmac_riwt2usec(priv->rx_riwt, priv);
return 0;
}
static int stmmac_set_coalesce(struct net_device *dev,
struct ethtool_coalesce *ec)
{
struct stmmac_priv *priv = netdev_priv(dev);
unsigned int rx_riwt;
/* Check not supported parameters */
if ((ec->rx_max_coalesced_frames) || (ec->rx_coalesce_usecs_irq) ||
(ec->rx_max_coalesced_frames_irq) || (ec->tx_coalesce_usecs_irq) ||
(ec->use_adaptive_rx_coalesce) || (ec->use_adaptive_tx_coalesce) ||
(ec->pkt_rate_low) || (ec->rx_coalesce_usecs_low) ||
(ec->rx_max_coalesced_frames_low) || (ec->tx_coalesce_usecs_high) ||
(ec->tx_max_coalesced_frames_low) || (ec->pkt_rate_high) ||
(ec->tx_coalesce_usecs_low) || (ec->rx_coalesce_usecs_high) ||
(ec->rx_max_coalesced_frames_high) ||
(ec->tx_max_coalesced_frames_irq) ||
(ec->stats_block_coalesce_usecs) ||
(ec->tx_max_coalesced_frames_high) || (ec->rate_sample_interval))
return -EOPNOTSUPP;
if (ec->rx_coalesce_usecs == 0)
return -EINVAL;
if ((ec->tx_coalesce_usecs == 0) &&
(ec->tx_max_coalesced_frames == 0))
return -EINVAL;
if ((ec->tx_coalesce_usecs > STMMAC_COAL_TX_TIMER) ||
(ec->tx_max_coalesced_frames > STMMAC_TX_MAX_FRAMES))
return -EINVAL;
rx_riwt = stmmac_usec2riwt(ec->rx_coalesce_usecs, priv);
if ((rx_riwt > MAX_DMA_RIWT) || (rx_riwt < MIN_DMA_RIWT))
return -EINVAL;
else if (!priv->use_riwt)
return -EOPNOTSUPP;
/* Only copy relevant parameters, ignore all others. */
priv->tx_coal_frames = ec->tx_max_coalesced_frames;
priv->tx_coal_timer = ec->tx_coalesce_usecs;
priv->rx_riwt = rx_riwt;
priv->hw->dma->rx_watchdog(priv->ioaddr, priv->rx_riwt);
return 0;
}
static int stmmac_get_ts_info(struct net_device *dev,
struct ethtool_ts_info *info)
{
struct stmmac_priv *priv = netdev_priv(dev);
if ((priv->hwts_tx_en) && (priv->hwts_rx_en)) {
info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
if (priv->ptp_clock)
info->phc_index = ptp_clock_index(priv->ptp_clock);
info->tx_types = (1 << HWTSTAMP_TX_OFF) | (1 << HWTSTAMP_TX_ON);
info->rx_filters = ((1 << HWTSTAMP_FILTER_NONE) |
(1 << HWTSTAMP_FILTER_PTP_V1_L4_EVENT) |
(1 << HWTSTAMP_FILTER_PTP_V1_L4_SYNC) |
(1 << HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) |
(1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT) |
(1 << HWTSTAMP_FILTER_PTP_V2_L4_SYNC) |
(1 << HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ) |
(1 << HWTSTAMP_FILTER_PTP_V2_EVENT) |
(1 << HWTSTAMP_FILTER_PTP_V2_SYNC) |
(1 << HWTSTAMP_FILTER_PTP_V2_DELAY_REQ) |
(1 << HWTSTAMP_FILTER_ALL));
return 0;
} else
return ethtool_op_get_ts_info(dev, info);
}
static const struct ethtool_ops stmmac_ethtool_ops = {
.begin = stmmac_check_if_running,
.get_drvinfo = stmmac_ethtool_getdrvinfo,
.get_settings = stmmac_ethtool_getsettings,
.set_settings = stmmac_ethtool_setsettings,
.get_msglevel = stmmac_ethtool_getmsglevel,
.set_msglevel = stmmac_ethtool_setmsglevel,
.get_regs = stmmac_ethtool_gregs,
.get_regs_len = stmmac_ethtool_get_regs_len,
.get_link = ethtool_op_get_link,
.get_pauseparam = stmmac_get_pauseparam,
.set_pauseparam = stmmac_set_pauseparam,
.get_ethtool_stats = stmmac_get_ethtool_stats,
.get_strings = stmmac_get_strings,
.get_wol = stmmac_get_wol,
.set_wol = stmmac_set_wol,
.get_eee = stmmac_ethtool_op_get_eee,
.set_eee = stmmac_ethtool_op_set_eee,
.get_sset_count = stmmac_get_sset_count,
.get_ts_info = stmmac_get_ts_info,
.get_coalesce = stmmac_get_coalesce,
.set_coalesce = stmmac_set_coalesce,
};
void stmmac_set_ethtool_ops(struct net_device *netdev)
{
netdev->ethtool_ops = &stmmac_ethtool_ops;
}

View file

@ -0,0 +1,148 @@
/*******************************************************************************
Copyright (C) 2013 Vayavya Labs Pvt Ltd
This implements all the API for managing HW timestamp & PTP.
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: Rayagond Kokatanur <rayagond@vayavyalabs.com>
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
#include <linux/io.h>
#include <linux/delay.h>
#include "common.h"
#include "stmmac_ptp.h"
static void stmmac_config_hw_tstamping(void __iomem *ioaddr, u32 data)
{
writel(data, ioaddr + PTP_TCR);
}
static void stmmac_config_sub_second_increment(void __iomem *ioaddr)
{
u32 value = readl(ioaddr + PTP_TCR);
unsigned long data;
/* Convert the ptp_clock to nano second
* formula = (1/ptp_clock) * 1000000000
* where, ptp_clock = 50MHz.
*/
data = (1000000000ULL / 50000000);
/* 0.465ns accuracy */
if (!(value & PTP_TCR_TSCTRLSSR))
data = (data * 1000) / 465;
writel(data, ioaddr + PTP_SSIR);
}
static int stmmac_init_systime(void __iomem *ioaddr, u32 sec, u32 nsec)
{
int limit;
u32 value;
writel(sec, ioaddr + PTP_STSUR);
writel(nsec, ioaddr + PTP_STNSUR);
/* issue command to initialize the system time value */
value = readl(ioaddr + PTP_TCR);
value |= PTP_TCR_TSINIT;
writel(value, ioaddr + PTP_TCR);
/* wait for present system time initialize to complete */
limit = 10;
while (limit--) {
if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSINIT))
break;
mdelay(10);
}
if (limit < 0)
return -EBUSY;
return 0;
}
static int stmmac_config_addend(void __iomem *ioaddr, u32 addend)
{
u32 value;
int limit;
writel(addend, ioaddr + PTP_TAR);
/* issue command to update the addend value */
value = readl(ioaddr + PTP_TCR);
value |= PTP_TCR_TSADDREG;
writel(value, ioaddr + PTP_TCR);
/* wait for present addend update to complete */
limit = 10;
while (limit--) {
if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSADDREG))
break;
mdelay(10);
}
if (limit < 0)
return -EBUSY;
return 0;
}
static int stmmac_adjust_systime(void __iomem *ioaddr, u32 sec, u32 nsec,
int add_sub)
{
u32 value;
int limit;
writel(sec, ioaddr + PTP_STSUR);
writel(((add_sub << PTP_STNSUR_ADDSUB_SHIFT) | nsec),
ioaddr + PTP_STNSUR);
/* issue command to initialize the system time value */
value = readl(ioaddr + PTP_TCR);
value |= PTP_TCR_TSUPDT;
writel(value, ioaddr + PTP_TCR);
/* wait for present system time adjust/update to complete */
limit = 10;
while (limit--) {
if (!(readl(ioaddr + PTP_TCR) & PTP_TCR_TSUPDT))
break;
mdelay(10);
}
if (limit < 0)
return -EBUSY;
return 0;
}
static u64 stmmac_get_systime(void __iomem *ioaddr)
{
u64 ns;
ns = readl(ioaddr + PTP_STNSR);
/* convert sec time value to nanosecond */
ns += readl(ioaddr + PTP_STSR) * 1000000000ULL;
return ns;
}
const struct stmmac_hwtimestamp stmmac_ptp = {
.config_hw_tstamping = stmmac_config_hw_tstamping,
.init_systime = stmmac_init_systime,
.config_sub_second_increment = stmmac_config_sub_second_increment,
.config_addend = stmmac_config_addend,
.adjust_systime = stmmac_adjust_systime,
.get_systime = stmmac_get_systime,
};

File diff suppressed because it is too large Load diff

View file

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

View file

@ -0,0 +1,202 @@
/*******************************************************************************
This contains the functions to handle the pci driver.
Copyright (C) 2011-2012 Vayavya Labs Pvt 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: Rayagond Kokatanur <rayagond@vayavyalabs.com>
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
#include <linux/pci.h>
#include "stmmac.h"
static struct plat_stmmacenet_data plat_dat;
static struct stmmac_mdio_bus_data mdio_data;
static struct stmmac_dma_cfg dma_cfg;
static void stmmac_default_data(void)
{
memset(&plat_dat, 0, sizeof(struct plat_stmmacenet_data));
plat_dat.bus_id = 1;
plat_dat.phy_addr = 0;
plat_dat.interface = PHY_INTERFACE_MODE_GMII;
plat_dat.clk_csr = 2; /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */
plat_dat.has_gmac = 1;
plat_dat.force_sf_dma_mode = 1;
mdio_data.phy_reset = NULL;
mdio_data.phy_mask = 0;
plat_dat.mdio_bus_data = &mdio_data;
dma_cfg.pbl = 32;
dma_cfg.burst_len = DMA_AXI_BLEN_256;
plat_dat.dma_cfg = &dma_cfg;
/* Set default value for multicast hash bins */
plat_dat.multicast_filter_bins = HASH_TABLE_SIZE;
/* Set default value for unicast filter entries */
plat_dat.unicast_filter_entries = 1;
}
/**
* stmmac_pci_probe
*
* @pdev: pci device pointer
* @id: pointer to table of device id/id's.
*
* Description: This probing function gets called for all PCI devices which
* match the ID table and are not "owned" by other driver yet. This function
* gets passed a "struct pci_dev *" for each device whose entry in the ID table
* matches the device. The probe functions returns zero when the driver choose
* to take "ownership" of the device or an error code(-ve no) otherwise.
*/
static int stmmac_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
int ret = 0;
void __iomem *addr = NULL;
struct stmmac_priv *priv = NULL;
int i;
/* Enable pci device */
ret = pci_enable_device(pdev);
if (ret) {
pr_err("%s : ERROR: failed to enable %s device\n", __func__,
pci_name(pdev));
return ret;
}
if (pci_request_regions(pdev, STMMAC_RESOURCE_NAME)) {
pr_err("%s: ERROR: failed to get PCI region\n", __func__);
ret = -ENODEV;
goto err_out_req_reg_failed;
}
/* Get the base address of device */
for (i = 0; i <= 5; i++) {
if (pci_resource_len(pdev, i) == 0)
continue;
addr = pci_iomap(pdev, i, 0);
if (addr == NULL) {
pr_err("%s: ERROR: cannot map register memory aborting",
__func__);
ret = -EIO;
goto err_out_map_failed;
}
break;
}
pci_set_master(pdev);
stmmac_default_data();
priv = stmmac_dvr_probe(&(pdev->dev), &plat_dat, addr);
if (IS_ERR(priv)) {
pr_err("%s: main driver probe failed", __func__);
ret = PTR_ERR(priv);
goto err_out;
}
priv->dev->irq = pdev->irq;
priv->wol_irq = pdev->irq;
pci_set_drvdata(pdev, priv->dev);
pr_debug("STMMAC platform driver registration completed");
return 0;
err_out:
pci_clear_master(pdev);
err_out_map_failed:
pci_release_regions(pdev);
err_out_req_reg_failed:
pci_disable_device(pdev);
return ret;
}
/**
* stmmac_pci_remove
*
* @pdev: platform device pointer
* Description: this function calls the main to free the net resources
* and releases the PCI resources.
*/
static void stmmac_pci_remove(struct pci_dev *pdev)
{
struct net_device *ndev = pci_get_drvdata(pdev);
struct stmmac_priv *priv = netdev_priv(ndev);
stmmac_dvr_remove(ndev);
pci_iounmap(pdev, priv->ioaddr);
pci_release_regions(pdev);
pci_disable_device(pdev);
}
#ifdef CONFIG_PM
static int stmmac_pci_suspend(struct pci_dev *pdev, pm_message_t state)
{
struct net_device *ndev = pci_get_drvdata(pdev);
int ret;
ret = stmmac_suspend(ndev);
pci_save_state(pdev);
pci_set_power_state(pdev, pci_choose_state(pdev, state));
return ret;
}
static int stmmac_pci_resume(struct pci_dev *pdev)
{
struct net_device *ndev = pci_get_drvdata(pdev);
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
return stmmac_resume(ndev);
}
#endif
#define STMMAC_VENDOR_ID 0x700
#define STMMAC_DEVICE_ID 0x1108
static const struct pci_device_id stmmac_id_table[] = {
{PCI_DEVICE(STMMAC_VENDOR_ID, STMMAC_DEVICE_ID)},
{PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_MAC)},
{}
};
MODULE_DEVICE_TABLE(pci, stmmac_id_table);
struct pci_driver stmmac_pci_driver = {
.name = STMMAC_RESOURCE_NAME,
.id_table = stmmac_id_table,
.probe = stmmac_pci_probe,
.remove = stmmac_pci_remove,
#ifdef CONFIG_PM
.suspend = stmmac_pci_suspend,
.resume = stmmac_pci_resume,
#endif
};
MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet PCI driver");
MODULE_AUTHOR("Rayagond Kokatanur <rayagond.kokatanur@vayavyalabs.com>");
MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,417 @@
/*******************************************************************************
This contains the functions to handle the platform driver.
Copyright (C) 2007-2011 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/platform_device.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_net.h>
#include <linux/of_device.h>
#include "stmmac.h"
static const struct of_device_id stmmac_dt_ids[] = {
#ifdef CONFIG_DWMAC_MESON
{ .compatible = "amlogic,meson6-dwmac", .data = &meson6_dwmac_data},
#endif
#ifdef CONFIG_DWMAC_SUNXI
{ .compatible = "allwinner,sun7i-a20-gmac", .data = &sun7i_gmac_data},
#endif
#ifdef CONFIG_DWMAC_STI
{ .compatible = "st,stih415-dwmac", .data = &stih4xx_dwmac_data},
{ .compatible = "st,stih416-dwmac", .data = &stih4xx_dwmac_data},
{ .compatible = "st,stid127-dwmac", .data = &stid127_dwmac_data},
{ .compatible = "st,stih407-dwmac", .data = &stih4xx_dwmac_data},
#endif
#ifdef CONFIG_DWMAC_SOCFPGA
{ .compatible = "altr,socfpga-stmmac", .data = &socfpga_gmac_data },
#endif
/* SoC specific glue layers should come before generic bindings */
{ .compatible = "st,spear600-gmac"},
{ .compatible = "snps,dwmac-3.610"},
{ .compatible = "snps,dwmac-3.70a"},
{ .compatible = "snps,dwmac-3.710"},
{ .compatible = "snps,dwmac"},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, stmmac_dt_ids);
#ifdef CONFIG_OF
/* This function validates the number of Multicast filtering bins specified
* by the configuration through the device tree. The Synopsys GMAC supports
* 64 bins, 128 bins, or 256 bins. "bins" refer to the division of CRC
* number space. 64 bins correspond to 6 bits of the CRC, 128 corresponds
* to 7 bits, and 256 refers to 8 bits of the CRC. Any other setting is
* invalid and will cause the filtering algorithm to use Multicast
* promiscuous mode.
*/
static int dwmac1000_validate_mcast_bins(int mcast_bins)
{
int x = mcast_bins;
switch (x) {
case HASH_TABLE_SIZE:
case 128:
case 256:
break;
default:
x = 0;
pr_info("Hash table entries set to unexpected value %d",
mcast_bins);
break;
}
return x;
}
/* This function validates the number of Unicast address entries supported
* by a particular Synopsys 10/100/1000 controller. The Synopsys controller
* supports 1, 32, 64, or 128 Unicast filter entries for it's Unicast filter
* logic. This function validates a valid, supported configuration is
* selected, and defaults to 1 Unicast address if an unsupported
* configuration is selected.
*/
static int dwmac1000_validate_ucast_entries(int ucast_entries)
{
int x = ucast_entries;
switch (x) {
case 1:
case 32:
case 64:
case 128:
break;
default:
x = 1;
pr_info("Unicast table entries set to unexpected value %d\n",
ucast_entries);
break;
}
return x;
}
static int stmmac_probe_config_dt(struct platform_device *pdev,
struct plat_stmmacenet_data *plat,
const char **mac)
{
struct device_node *np = pdev->dev.of_node;
struct stmmac_dma_cfg *dma_cfg;
const struct of_device_id *device;
if (!np)
return -ENODEV;
device = of_match_device(stmmac_dt_ids, &pdev->dev);
if (!device)
return -ENODEV;
if (device->data) {
const struct stmmac_of_data *data = device->data;
plat->has_gmac = data->has_gmac;
plat->enh_desc = data->enh_desc;
plat->tx_coe = data->tx_coe;
plat->rx_coe = data->rx_coe;
plat->bugged_jumbo = data->bugged_jumbo;
plat->pmt = data->pmt;
plat->riwt_off = data->riwt_off;
plat->fix_mac_speed = data->fix_mac_speed;
plat->bus_setup = data->bus_setup;
plat->setup = data->setup;
plat->free = data->free;
plat->init = data->init;
plat->exit = data->exit;
}
*mac = of_get_mac_address(np);
plat->interface = of_get_phy_mode(np);
/* Get max speed of operation from device tree */
if (of_property_read_u32(np, "max-speed", &plat->max_speed))
plat->max_speed = -1;
plat->bus_id = of_alias_get_id(np, "ethernet");
if (plat->bus_id < 0)
plat->bus_id = 0;
/* Default to phy auto-detection */
plat->phy_addr = -1;
/* "snps,phy-addr" is not a standard property. Mark it as deprecated
* and warn of its use. Remove this when phy node support is added.
*/
if (of_property_read_u32(np, "snps,phy-addr", &plat->phy_addr) == 0)
dev_warn(&pdev->dev, "snps,phy-addr property is deprecated\n");
if (plat->phy_bus_name)
plat->mdio_bus_data = NULL;
else
plat->mdio_bus_data =
devm_kzalloc(&pdev->dev,
sizeof(struct stmmac_mdio_bus_data),
GFP_KERNEL);
plat->force_sf_dma_mode =
of_property_read_bool(np, "snps,force_sf_dma_mode");
/* Set the maxmtu to a default of JUMBO_LEN in case the
* parameter is not present in the device tree.
*/
plat->maxmtu = JUMBO_LEN;
/*
* Currently only the properties needed on SPEAr600
* are provided. All other properties should be added
* once needed on other platforms.
*/
if (of_device_is_compatible(np, "st,spear600-gmac") ||
of_device_is_compatible(np, "snps,dwmac-3.70a") ||
of_device_is_compatible(np, "snps,dwmac")) {
/* Note that the max-frame-size parameter as defined in the
* ePAPR v1.1 spec is defined as max-frame-size, it's
* actually used as the IEEE definition of MAC Client
* data, or MTU. The ePAPR specification is confusing as
* the definition is max-frame-size, but usage examples
* are clearly MTUs
*/
of_property_read_u32(np, "max-frame-size", &plat->maxmtu);
of_property_read_u32(np, "snps,multicast-filter-bins",
&plat->multicast_filter_bins);
of_property_read_u32(np, "snps,perfect-filter-entries",
&plat->unicast_filter_entries);
plat->unicast_filter_entries = dwmac1000_validate_ucast_entries(
plat->unicast_filter_entries);
plat->multicast_filter_bins = dwmac1000_validate_mcast_bins(
plat->multicast_filter_bins);
plat->has_gmac = 1;
plat->pmt = 1;
}
if (of_device_is_compatible(np, "snps,dwmac-3.610") ||
of_device_is_compatible(np, "snps,dwmac-3.710")) {
plat->enh_desc = 1;
plat->bugged_jumbo = 1;
plat->force_sf_dma_mode = 1;
}
if (of_find_property(np, "snps,pbl", NULL)) {
dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg),
GFP_KERNEL);
if (!dma_cfg)
return -ENOMEM;
plat->dma_cfg = dma_cfg;
of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl);
dma_cfg->fixed_burst =
of_property_read_bool(np, "snps,fixed-burst");
dma_cfg->mixed_burst =
of_property_read_bool(np, "snps,mixed-burst");
}
plat->force_thresh_dma_mode = of_property_read_bool(np, "snps,force_thresh_dma_mode");
if (plat->force_thresh_dma_mode) {
plat->force_sf_dma_mode = 0;
pr_warn("force_sf_dma_mode is ignored if force_thresh_dma_mode is set.");
}
return 0;
}
#else
static int stmmac_probe_config_dt(struct platform_device *pdev,
struct plat_stmmacenet_data *plat,
const char **mac)
{
return -ENOSYS;
}
#endif /* CONFIG_OF */
/**
* stmmac_pltfr_probe
* @pdev: platform device pointer
* Description: platform_device probe function. It allocates
* the necessary resources and invokes the main to init
* the net device, register the mdio bus etc.
*/
static int stmmac_pltfr_probe(struct platform_device *pdev)
{
int ret = 0;
struct resource *res;
struct device *dev = &pdev->dev;
void __iomem *addr = NULL;
struct stmmac_priv *priv = NULL;
struct plat_stmmacenet_data *plat_dat = NULL;
const char *mac = NULL;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
addr = devm_ioremap_resource(dev, res);
if (IS_ERR(addr))
return PTR_ERR(addr);
plat_dat = dev_get_platdata(&pdev->dev);
if (!plat_dat)
plat_dat = devm_kzalloc(&pdev->dev,
sizeof(struct plat_stmmacenet_data),
GFP_KERNEL);
if (!plat_dat) {
pr_err("%s: ERROR: no memory", __func__);
return -ENOMEM;
}
/* Set default value for multicast hash bins */
plat_dat->multicast_filter_bins = HASH_TABLE_SIZE;
/* Set default value for unicast filter entries */
plat_dat->unicast_filter_entries = 1;
if (pdev->dev.of_node) {
ret = stmmac_probe_config_dt(pdev, plat_dat, &mac);
if (ret) {
pr_err("%s: main dt probe failed", __func__);
return ret;
}
}
/* Custom setup (if needed) */
if (plat_dat->setup) {
plat_dat->bsp_priv = plat_dat->setup(pdev);
if (IS_ERR(plat_dat->bsp_priv))
return PTR_ERR(plat_dat->bsp_priv);
}
/* Custom initialisation (if needed)*/
if (plat_dat->init) {
ret = plat_dat->init(pdev, plat_dat->bsp_priv);
if (unlikely(ret))
return ret;
}
priv = stmmac_dvr_probe(&(pdev->dev), plat_dat, addr);
if (IS_ERR(priv)) {
pr_err("%s: main driver probe failed", __func__);
return PTR_ERR(priv);
}
/* Get MAC address if available (DT) */
if (mac)
memcpy(priv->dev->dev_addr, mac, ETH_ALEN);
/* Get the MAC information */
priv->dev->irq = platform_get_irq_byname(pdev, "macirq");
if (priv->dev->irq < 0) {
if (priv->dev->irq != -EPROBE_DEFER) {
netdev_err(priv->dev,
"MAC IRQ configuration information not found\n");
}
return priv->dev->irq;
}
/*
* On some platforms e.g. SPEAr the wake up irq differs from the mac irq
* The external wake up irq can be passed through the platform code
* named as "eth_wake_irq"
*
* In case the wake up interrupt is not passed from the platform
* so the driver will continue to use the mac irq (ndev->irq)
*/
priv->wol_irq = platform_get_irq_byname(pdev, "eth_wake_irq");
if (priv->wol_irq < 0) {
if (priv->wol_irq == -EPROBE_DEFER)
return -EPROBE_DEFER;
priv->wol_irq = priv->dev->irq;
}
priv->lpi_irq = platform_get_irq_byname(pdev, "eth_lpi");
if (priv->lpi_irq == -EPROBE_DEFER)
return -EPROBE_DEFER;
platform_set_drvdata(pdev, priv->dev);
pr_debug("STMMAC platform driver registration completed");
return 0;
}
/**
* stmmac_pltfr_remove
* @pdev: platform device pointer
* Description: this function calls the main to free the net resources
* and calls the platforms hook and release the resources (e.g. mem).
*/
static int stmmac_pltfr_remove(struct platform_device *pdev)
{
struct net_device *ndev = platform_get_drvdata(pdev);
struct stmmac_priv *priv = netdev_priv(ndev);
int ret = stmmac_dvr_remove(ndev);
if (priv->plat->exit)
priv->plat->exit(pdev, priv->plat->bsp_priv);
if (priv->plat->free)
priv->plat->free(pdev, priv->plat->bsp_priv);
return ret;
}
#ifdef CONFIG_PM
static int stmmac_pltfr_suspend(struct device *dev)
{
int ret;
struct net_device *ndev = dev_get_drvdata(dev);
struct stmmac_priv *priv = netdev_priv(ndev);
struct platform_device *pdev = to_platform_device(dev);
ret = stmmac_suspend(ndev);
if (priv->plat->exit)
priv->plat->exit(pdev, priv->plat->bsp_priv);
return ret;
}
static int stmmac_pltfr_resume(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
struct stmmac_priv *priv = netdev_priv(ndev);
struct platform_device *pdev = to_platform_device(dev);
if (priv->plat->init)
priv->plat->init(pdev, priv->plat->bsp_priv);
return stmmac_resume(ndev);
}
#endif /* CONFIG_PM */
static SIMPLE_DEV_PM_OPS(stmmac_pltfr_pm_ops,
stmmac_pltfr_suspend, stmmac_pltfr_resume);
struct platform_driver stmmac_pltfr_driver = {
.probe = stmmac_pltfr_probe,
.remove = stmmac_pltfr_remove,
.driver = {
.name = STMMAC_RESOURCE_NAME,
.owner = THIS_MODULE,
.pm = &stmmac_pltfr_pm_ops,
.of_match_table = of_match_ptr(stmmac_dt_ids),
},
};
MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet PLATFORM driver");
MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,213 @@
/*******************************************************************************
PTP 1588 clock using the STMMAC.
Copyright (C) 2013 Vayavya Labs Pvt 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: Rayagond Kokatanur <rayagond@vayavyalabs.com>
*******************************************************************************/
#include "stmmac.h"
#include "stmmac_ptp.h"
/**
* stmmac_adjust_freq
*
* @ptp: pointer to ptp_clock_info structure
* @ppb: desired period change in parts ber billion
*
* Description: this function will adjust the frequency of hardware clock.
*/
static int stmmac_adjust_freq(struct ptp_clock_info *ptp, s32 ppb)
{
struct stmmac_priv *priv =
container_of(ptp, struct stmmac_priv, ptp_clock_ops);
unsigned long flags;
u32 diff, addend;
int neg_adj = 0;
u64 adj;
if (ppb < 0) {
neg_adj = 1;
ppb = -ppb;
}
addend = priv->default_addend;
adj = addend;
adj *= ppb;
diff = div_u64(adj, 1000000000ULL);
addend = neg_adj ? (addend - diff) : (addend + diff);
spin_lock_irqsave(&priv->ptp_lock, flags);
priv->hw->ptp->config_addend(priv->ioaddr, addend);
spin_unlock_irqrestore(&priv->ptp_lock, flags);
return 0;
}
/**
* stmmac_adjust_time
*
* @ptp: pointer to ptp_clock_info structure
* @delta: desired change in nanoseconds
*
* Description: this function will shift/adjust the hardware clock time.
*/
static int stmmac_adjust_time(struct ptp_clock_info *ptp, s64 delta)
{
struct stmmac_priv *priv =
container_of(ptp, struct stmmac_priv, ptp_clock_ops);
unsigned long flags;
u32 sec, nsec;
u32 quotient, reminder;
int neg_adj = 0;
if (delta < 0) {
neg_adj = 1;
delta = -delta;
}
quotient = div_u64_rem(delta, 1000000000ULL, &reminder);
sec = quotient;
nsec = reminder;
spin_lock_irqsave(&priv->ptp_lock, flags);
priv->hw->ptp->adjust_systime(priv->ioaddr, sec, nsec, neg_adj);
spin_unlock_irqrestore(&priv->ptp_lock, flags);
return 0;
}
/**
* stmmac_get_time
*
* @ptp: pointer to ptp_clock_info structure
* @ts: pointer to hold time/result
*
* Description: this function will read the current time from the
* hardware clock and store it in @ts.
*/
static int stmmac_get_time(struct ptp_clock_info *ptp, struct timespec *ts)
{
struct stmmac_priv *priv =
container_of(ptp, struct stmmac_priv, ptp_clock_ops);
unsigned long flags;
u64 ns;
u32 reminder;
spin_lock_irqsave(&priv->ptp_lock, flags);
ns = priv->hw->ptp->get_systime(priv->ioaddr);
spin_unlock_irqrestore(&priv->ptp_lock, flags);
ts->tv_sec = div_u64_rem(ns, 1000000000ULL, &reminder);
ts->tv_nsec = reminder;
return 0;
}
/**
* stmmac_set_time
*
* @ptp: pointer to ptp_clock_info structure
* @ts: time value to set
*
* Description: this function will set the current time on the
* hardware clock.
*/
static int stmmac_set_time(struct ptp_clock_info *ptp,
const struct timespec *ts)
{
struct stmmac_priv *priv =
container_of(ptp, struct stmmac_priv, ptp_clock_ops);
unsigned long flags;
spin_lock_irqsave(&priv->ptp_lock, flags);
priv->hw->ptp->init_systime(priv->ioaddr, ts->tv_sec, ts->tv_nsec);
spin_unlock_irqrestore(&priv->ptp_lock, flags);
return 0;
}
static int stmmac_enable(struct ptp_clock_info *ptp,
struct ptp_clock_request *rq, int on)
{
return -EOPNOTSUPP;
}
/* structure describing a PTP hardware clock */
static struct ptp_clock_info stmmac_ptp_clock_ops = {
.owner = THIS_MODULE,
.name = "stmmac_ptp_clock",
.max_adj = 62500000,
.n_alarm = 0,
.n_ext_ts = 0,
.n_per_out = 0,
.n_pins = 0,
.pps = 0,
.adjfreq = stmmac_adjust_freq,
.adjtime = stmmac_adjust_time,
.gettime = stmmac_get_time,
.settime = stmmac_set_time,
.enable = stmmac_enable,
};
/**
* stmmac_ptp_register
* @priv: driver private structure
* Description: this function will register the ptp clock driver
* to kernel. It also does some house keeping work.
*/
int stmmac_ptp_register(struct stmmac_priv *priv)
{
spin_lock_init(&priv->ptp_lock);
priv->ptp_clock_ops = stmmac_ptp_clock_ops;
priv->ptp_clock = ptp_clock_register(&priv->ptp_clock_ops,
priv->device);
if (IS_ERR(priv->ptp_clock)) {
priv->ptp_clock = NULL;
pr_err("ptp_clock_register() failed on %s\n", priv->dev->name);
} else
pr_debug("Added PTP HW clock successfully on %s\n",
priv->dev->name);
return 0;
}
/**
* stmmac_ptp_unregister
* @priv: driver private structure
* Description: this function will remove/unregister the ptp clock driver
* from the kernel.
*/
void stmmac_ptp_unregister(struct stmmac_priv *priv)
{
if (priv->ptp_clock) {
ptp_clock_unregister(priv->ptp_clock);
priv->ptp_clock = NULL;
pr_debug("Removed PTP HW clock successfully on %s\n",
priv->dev->name);
}
}

View file

@ -0,0 +1,72 @@
/******************************************************************************
PTP Header file
Copyright (C) 2013 Vayavya Labs Pvt 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: Rayagond Kokatanur <rayagond@vayavyalabs.com>
******************************************************************************/
#ifndef __STMMAC_PTP_H__
#define __STMMAC_PTP_H__
/* IEEE 1588 PTP register offsets */
#define PTP_TCR 0x0700 /* Timestamp Control Reg */
#define PTP_SSIR 0x0704 /* Sub-Second Increment Reg */
#define PTP_STSR 0x0708 /* System Time Seconds Regr */
#define PTP_STNSR 0x070C /* System Time Nanoseconds Reg */
#define PTP_STSUR 0x0710 /* System Time Seconds Update Reg */
#define PTP_STNSUR 0x0714 /* System Time Nanoseconds Update Reg */
#define PTP_TAR 0x0718 /* Timestamp Addend Reg */
#define PTP_TTSR 0x071C /* Target Time Seconds Reg */
#define PTP_TTNSR 0x0720 /* Target Time Nanoseconds Reg */
#define PTP_STHWSR 0x0724 /* System Time - Higher Word Seconds Reg */
#define PTP_TSR 0x0728 /* Timestamp Status */
#define PTP_STNSUR_ADDSUB_SHIFT 31
/* PTP TCR defines */
#define PTP_TCR_TSENA 0x00000001 /* Timestamp Enable */
#define PTP_TCR_TSCFUPDT 0x00000002 /* Timestamp Fine/Coarse Update */
#define PTP_TCR_TSINIT 0x00000004 /* Timestamp Initialize */
#define PTP_TCR_TSUPDT 0x00000008 /* Timestamp Update */
/* Timestamp Interrupt Trigger Enable */
#define PTP_TCR_TSTRIG 0x00000010
#define PTP_TCR_TSADDREG 0x00000020 /* Addend Reg Update */
#define PTP_TCR_TSENALL 0x00000100 /* Enable Timestamp for All Frames */
/* Timestamp Digital or Binary Rollover Control */
#define PTP_TCR_TSCTRLSSR 0x00000200
/* Enable PTP packet Processing for Version 2 Format */
#define PTP_TCR_TSVER2ENA 0x00000400
/* Enable Processing of PTP over Ethernet Frames */
#define PTP_TCR_TSIPENA 0x00000800
/* Enable Processing of PTP Frames Sent over IPv6-UDP */
#define PTP_TCR_TSIPV6ENA 0x00001000
/* Enable Processing of PTP Frames Sent over IPv4-UDP */
#define PTP_TCR_TSIPV4ENA 0x00002000
/* Enable Timestamp Snapshot for Event Messages */
#define PTP_TCR_TSEVNTENA 0x00004000
/* Enable Snapshot for Messages Relevant to Master */
#define PTP_TCR_TSMSTRENA 0x00008000
/* Select PTP packets for Taking Snapshots */
#define PTP_TCR_SNAPTYPSEL_1 0x00010000
/* Enable MAC address for PTP Frame Filtering */
#define PTP_TCR_TSENMACADDR 0x00040000
#endif /* __STMMAC_PTP_H__ */