Commit c0db64ce authored by David S. Miller's avatar David S. Miller

Merge branch 'stmmac'

Giuseppe CAVALLARO says:

====================
These patches enhance the driver adding the PTP support and the initial code
for RGMII/SGMII/TBI/RTBI modes.
Also this patches review the driver removing some Koption for selecting between
chain and ring modes. REally useful to validate the driver also at build time.
Before adding PTP, the extended descriptor support has been added because it
is mandatory to save HW timestamp in new dedicated descriptors. Also in this
case no Koption added.

Concerning the PTP, I have hacked/reviewed and tested many
part of these patches also verifying the back compatibility on
several HW and chips.

Concerning the SGMII/RGMII we have already discussed about the support
in the net.dev Mailing list with Byungho where these patchs were partially
analysed.
So I have only ported them against the latest net-next (and on
top of PTP). I have added some missing things: e.g. some parts of the
ethtool for ANE. As we clarified with Byungho, we will add further
enhancements on top of these patches if needed.

I have also built all against ARM/SH/X68 platforms and no issues on
ST-Boxes.

Thx goes to Rayagond that wrote and tested the PTP and to Byungho for SGMII.

V2: This Version 2 has the fixes discussed in the ML, for example:
    o completely remove the Koption... all the decisions are made at probe time
    o review the PTP patches and better organize them just in two patches
    o added all the fixes provided by Richard on PTP and CLK driver.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents ad999eee 94fbbbf8
......@@ -326,6 +326,35 @@ To enter in Tx LPI mode the driver needs to have a software timer
that enable and disable the LPI mode when there is nothing to be
transmitted.
7) TODO:
7) Extended descriptors
The extended descriptors give us information about the receive Ethernet payload
when it is carrying PTP packets or TCP/UDP/ICMP over IP.
These are not available on GMAC Synopsys chips older than the 3.50.
At probe time the driver will decide if these can be actually used.
This support also is mandatory for PTPv2 because the extra descriptors 6 and 7
are used for saving the hardware timestamps.
8) Precision Time Protocol (PTP)
The driver supports the IEEE 1588-2002, Precision Time Protocol (PTP),
which enables precise synchronization of clocks in measurement and
control systems implemented with technologies such as network
communication.
In addition to the basic timestamp features mentioned in IEEE 1588-2002
Timestamps, new GMAC cores support the advanced timestamp features.
IEEE 1588-2008 that can be enabled when configure the Kernel.
9) SGMII/RGMII supports
New GMAC devices provide own way to manage RGMII/SGMII.
This information is available at run-time by looking at the
HW capability register. This means that the stmmac can manage
auto-negotiation and link status w/o using the PHYLIB stuff
In fact, the HW provides a subset of extended registers to
restart the ANE, verify Full/Half duplex mode and Speed.
Also thanks to these registers it is possible to look at the
Auto-negotiated Link Parter Ability.
10) TODO:
o XGMAC is not supported.
o Add the PTP - precision time protocol
o Complete the TBI & RTBI support.
o extened VLAN support for 3.70a SYNP GMAC.
......@@ -5,6 +5,7 @@ config STMMAC_ETH
select MII
select PHYLIB
select CRC32
select PTP_1588_CLOCK
---help---
This is the driver for the Ethernet IPs are built around a
Synopsys IP Core and only tested on the STMicroelectronics
......@@ -54,22 +55,4 @@ config STMMAC_DA
By default, the DMA arbitration scheme is based on Round-robin
(rx:tx priority is 1:1).
choice
prompt "Select the DMA TX/RX descriptor operating modes"
depends on STMMAC_ETH
---help---
This driver supports DMA descriptor to operate both in dual buffer
(RING) and linked-list(CHAINED) mode. In RING mode each descriptor
points to two data buffer pointers whereas in CHAINED mode they
points to only one data buffer pointer.
config STMMAC_RING
bool "Enable Descriptor Ring Mode"
config STMMAC_CHAINED
bool "Enable Descriptor Chained Mode"
endchoice
endif
obj-$(CONFIG_STMMAC_ETH) += stmmac.o
stmmac-$(CONFIG_STMMAC_RING) += ring_mode.o
stmmac-$(CONFIG_STMMAC_CHAINED) += chain_mode.o
stmmac-$(CONFIG_STMMAC_PLATFORM) += stmmac_platform.o
stmmac-$(CONFIG_STMMAC_PCI) += stmmac_pci.o
stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o \
dwmac_lib.o dwmac1000_core.o dwmac1000_dma.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-y)
mmc_core.o stmmac_hwtstamp.o stmmac_ptp.o $(stmmac-y)
......@@ -28,7 +28,7 @@
#include "stmmac.h"
unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
static unsigned 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;
......@@ -47,7 +47,8 @@ unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
desc->des2 = dma_map_single(priv->device, skb->data,
bmax, DMA_TO_DEVICE);
priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum);
priv->tx_skbuff_dma[entry] = desc->des2;
priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum, STMMAC_CHAIN_MODE);
while (len != 0) {
entry = (++priv->cur_tx) % txsize;
......@@ -57,8 +58,9 @@ unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
desc->des2 = dma_map_single(priv->device,
(skb->data + bmax * i),
bmax, DMA_TO_DEVICE);
priv->hw->desc->prepare_tx_desc(desc, 0, bmax,
csum);
priv->tx_skbuff_dma[entry] = desc->des2;
priv->hw->desc->prepare_tx_desc(desc, 0, bmax, csum,
STMMAC_CHAIN_MODE);
priv->hw->desc->set_tx_owner(desc);
priv->tx_skbuff[entry] = NULL;
len -= bmax;
......@@ -67,8 +69,9 @@ unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
desc->des2 = dma_map_single(priv->device,
(skb->data + bmax * i), len,
DMA_TO_DEVICE);
priv->hw->desc->prepare_tx_desc(desc, 0, len,
csum);
priv->tx_skbuff_dma[entry] = desc->des2;
priv->hw->desc->prepare_tx_desc(desc, 0, len, csum,
STMMAC_CHAIN_MODE);
priv->hw->desc->set_tx_owner(desc);
priv->tx_skbuff[entry] = NULL;
len = 0;
......@@ -89,49 +92,70 @@ static unsigned int stmmac_is_jumbo_frm(int len, int enh_desc)
return ret;
}
static void stmmac_refill_desc3(int bfsize, struct dma_desc *p)
{
}
static void stmmac_init_desc3(int des3_as_data_buf, struct dma_desc *p)
{
}
static void stmmac_clean_desc3(struct dma_desc *p)
{
}
static void stmmac_init_dma_chain(struct dma_desc *des, dma_addr_t phy_addr,
unsigned int size)
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;
struct dma_desc *p = des;
dma_addr_t dma_phy = phy_addr;
for (i = 0; i < (size - 1); i++) {
dma_phy += sizeof(struct dma_desc);
p->des3 = (unsigned int)dma_phy;
p++;
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;
}
p->des3 = (unsigned int)phy_addr;
}
static int stmmac_set_16kib_bfsize(int mtu)
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)
{
/* Not supported */
return 0;
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_ring_mode_ops ring_mode_ops = {
const struct stmmac_chain_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,
.init_desc3 = stmmac_init_desc3,
.init_dma_chain = stmmac_init_dma_chain,
.clean_desc3 = stmmac_clean_desc3,
.set_16kib_bfsize = stmmac_set_16kib_bfsize,
};
......@@ -117,6 +117,36 @@ struct stmmac_extra_stats {
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*/
......@@ -138,6 +168,12 @@ struct stmmac_extra_stats {
#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 */
......@@ -194,17 +230,25 @@ enum dma_irq_status {
handle_tx = 0x8,
};
enum core_specific_irq_mask {
core_mmc_tx_irq = 1,
core_mmc_rx_irq = 2,
core_mmc_rx_csum_offload_irq = 4,
core_irq_receive_pmt_irq = 8,
core_irq_tx_path_in_lpi_mode = 16,
core_irq_tx_path_exit_lpi_mode = 32,
core_irq_rx_path_in_lpi_mode = 64,
core_irq_rx_path_exit_lpi_mode = 128,
#define CORE_IRQ_TX_PATH_IN_LPI_MODE (1 << 1)
#define CORE_IRQ_TX_PATH_EXIT_LPI_MODE (1 << 2)
#define CORE_IRQ_RX_PATH_IN_LPI_MODE (1 << 3)
#define CORE_IRQ_RX_PATH_EXIT_LPI_MODE (1 << 4)
#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;
......@@ -255,23 +299,26 @@ struct dma_features {
#define STMMAC_DEFAULT_LIT_LS_TIMER 0x3E8
#define STMMAC_DEFAULT_TWT_LS_TIMER 0x0
#define STMMAC_CHAIN_MODE 0x1
#define STMMAC_RING_MODE 0x2
struct stmmac_desc_ops {
/* DMA RX descriptor ring initialization */
void (*init_rx_desc) (struct dma_desc *p, unsigned int ring_size,
int disable_rx_ic);
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, unsigned int ring_size);
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 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);
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);
......@@ -290,12 +337,22 @@ struct stmmac_desc_ops {
/* 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 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
......@@ -327,7 +384,8 @@ struct stmmac_ops {
/* Dump MAC registers */
void (*dump_regs) (void __iomem *ioaddr);
/* Handle extra events on specific interrupts hw dependent */
int (*host_irq_status) (void __iomem *ioaddr);
int (*host_irq_status) (void __iomem *ioaddr,
struct stmmac_extra_stats *x);
/* Multicast filter setting */
void (*set_filter) (struct net_device *dev, int id);
/* Flow control setting */
......@@ -344,6 +402,18 @@ struct stmmac_ops {
void (*reset_eee_mode) (void __iomem *ioaddr);
void (*set_eee_timer) (void __iomem *ioaddr, int ls, int tw);
void (*set_eee_pls) (void __iomem *ioaddr, int link);
void (*ctrl_ane) (void __iomem *ioaddr, bool restart);
void (*get_adv) (void __iomem *ioaddr, 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 {
......@@ -360,19 +430,28 @@ struct mii_regs {
struct stmmac_ring_mode_ops {
unsigned int (*is_jumbo_frm) (int len, int ehn_desc);
unsigned int (*jumbo_frm) (void *priv, struct sk_buff *skb, int csum);
void (*refill_desc3) (int bfsize, struct dma_desc *p);
void (*init_desc3) (int des3_as_data_buf, struct dma_desc *p);
void (*init_dma_chain) (struct dma_desc *des, dma_addr_t phy_addr,
unsigned int size);
void (*clean_desc3) (struct dma_desc *p);
void (*refill_desc3) (void *priv, struct dma_desc *p);
void (*init_desc3) (struct dma_desc *p);
void (*clean_desc3) (void *priv, struct dma_desc *p);
int (*set_16kib_bfsize) (int mtu);
};
struct stmmac_chain_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);
unsigned int (*jumbo_frm) (void *priv, struct sk_buff *skb, int csum);
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_ring_mode_ops *ring;
const struct stmmac_chain_mode_ops *chain;
const struct stmmac_hwtimestamp *ptp;
struct mii_regs mii; /* MII register Addresses */
struct mac_link link;
unsigned int synopsys_uid;
......@@ -390,5 +469,6 @@ extern void stmmac_set_mac(void __iomem *ioaddr, bool enable);
extern void dwmac_dma_flush_tx_fifo(void __iomem *ioaddr);
extern const struct stmmac_ring_mode_ops ring_mode_ops;
extern const struct stmmac_chain_mode_ops chain_mode_ops;
#endif /* __COMMON_H__ */
......@@ -24,6 +24,7 @@
#ifndef __DESCS_H__
#define __DESCS_H__
/* Basic descriptor structure for normal and alternate descriptors */
struct dma_desc {
/* Receive descriptor */
union {
......@@ -60,7 +61,7 @@ struct dma_desc {
} rx;
struct {
/* RDES0 */
u32 payload_csum_error:1;
u32 rx_mac_addr:1;
u32 crc_error:1;
u32 dribbling:1;
u32 error_gmii:1;
......@@ -162,13 +163,57 @@ struct dma_desc {
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 */
cic_no_pseudoheader = 2, /* IP header but pseudoheader
* is not calculated */
/* 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__ */
......@@ -30,26 +30,28 @@
#ifndef __DESC_COM_H__
#define __DESC_COM_H__
#if defined(CONFIG_STMMAC_RING)
static inline void ehn_desc_rx_set_on_ring_chain(struct dma_desc *p, int end)
/* 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_chain(struct dma_desc *p, int end)
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(struct dma_desc *p, int ter)
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(struct dma_desc *p, int len)
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;
......@@ -58,25 +60,26 @@ static inline void enh_set_tx_desc_len(struct dma_desc *p, int len)
p->des01.etx.buffer1_size = len;
}
static inline void ndesc_rx_set_on_ring_chain(struct dma_desc *p, int end)
/* 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_chain(struct dma_desc *p, int end)
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(struct dma_desc *p, int ter)
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(struct dma_desc *p, int len)
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;
......@@ -85,47 +88,48 @@ static inline void norm_set_tx_desc_len(struct dma_desc *p, int len)
p->des01.tx.buffer1_size = len;
}
#else
/* Specific functions used for Chain mode */
static inline void ehn_desc_rx_set_on_ring_chain(struct dma_desc *p, int end)
/* 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_ring_chain(struct dma_desc *p, int end)
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(struct dma_desc *p, int ter)
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(struct dma_desc *p, int len)
static inline void enh_set_tx_desc_len_on_chain(struct dma_desc *p, int len)
{
p->des01.etx.buffer1_size = len;
}
static inline void ndesc_rx_set_on_ring_chain(struct dma_desc *p, int end)
/* 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_ring_chain(struct dma_desc *p, int ring_size)
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(struct dma_desc *p, int ter)
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(struct dma_desc *p, int len)
static inline void norm_set_tx_desc_len_on_chain(struct dma_desc *p, int len)
{
p->des01.tx.buffer1_size = len;
}
#endif
#endif /* __DESC_COM_H__ */
......@@ -89,13 +89,46 @@ enum power_event {
(reg * 8))
#define GMAC_MAX_PERFECT_ADDRESSES 32
/* 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_LINK 0x000000cc /* Auto-Neg. link partener ability */
#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_GMII_STATUS 0x000000d8 /* S/R-GMII 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_TC 0x01000000 /* Transmit Conf. in RGMII/SGMII */
#define GMAC_CONTROL_WD 0x00800000 /* Disable Watchdog on receive */
/* GMAC Configuration defines */
#define GMAC_CONTROL_TC 0x01000000 /* Transmit Conf. in RGMII/SGMII */
......@@ -155,6 +188,7 @@ enum inter_frame_gap {
/* 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 */
......@@ -230,5 +264,7 @@ enum rtc_control {
#define GMAC_MMC_TX_INTR 0x108
#define GMAC_MMC_RX_CSUM_OFFLOAD 0x208
extern const struct stmmac_dma_ops dwmac1000_dma_ops;
#endif /* __DWMAC1000_H__ */
......@@ -28,6 +28,7 @@
#include <linux/crc32.h>
#include <linux/slab.h>
#include <linux/ethtool.h>
#include <asm/io.h>
#include "dwmac1000.h"
......@@ -193,59 +194,91 @@ static void dwmac1000_pmt(void __iomem *ioaddr, unsigned long mode)
writel(pmt, ioaddr + GMAC_PMT);
}
static int dwmac1000_irq_status(void __iomem *ioaddr)
static int dwmac1000_irq_status(void __iomem *ioaddr,
struct stmmac_extra_stats *x)
{
u32 intr_status = readl(ioaddr + GMAC_INT_STATUS);
int status = 0;
int ret = 0;
/* Not used events (e.g. MMC interrupts) are not handled. */
if ((intr_status & mmc_tx_irq)) {
CHIP_DBG(KERN_INFO "GMAC: MMC tx interrupt: 0x%08x\n",
readl(ioaddr + GMAC_MMC_TX_INTR));
status |= core_mmc_tx_irq;
x->mmc_tx_irq_n++;
}
if (unlikely(intr_status & mmc_rx_irq)) {
CHIP_DBG(KERN_INFO "GMAC: MMC rx interrupt: 0x%08x\n",
readl(ioaddr + GMAC_MMC_RX_INTR));
status |= core_mmc_rx_irq;
x->mmc_rx_irq_n++;
}
if (unlikely(intr_status & mmc_rx_csum_offload_irq)) {
CHIP_DBG(KERN_INFO "GMAC: MMC rx csum offload: 0x%08x\n",
readl(ioaddr + GMAC_MMC_RX_CSUM_OFFLOAD));
status |= core_mmc_rx_csum_offload_irq;
x->mmc_rx_csum_offload_irq_n++;
}
if (unlikely(intr_status & pmt_irq)) {
CHIP_DBG(KERN_INFO "GMAC: received Magic frame\n");
/* clear the PMT bits 5 and 6 by reading the PMT
* status register. */
readl(ioaddr + GMAC_PMT);
status |= core_irq_receive_pmt_irq;
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 */
u32 lpi_status = readl(ioaddr + LPI_CTRL_STATUS);
ret = readl(ioaddr + LPI_CTRL_STATUS);
if (lpi_status & LPI_CTRL_STATUS_TLPIEN) {
if (ret & LPI_CTRL_STATUS_TLPIEN) {
CHIP_DBG(KERN_INFO "GMAC TX entered in LPI\n");
status |= core_irq_tx_path_in_lpi_mode;
x->irq_tx_path_in_lpi_mode_n++;
}
if (lpi_status & LPI_CTRL_STATUS_TLPIEX) {
if (ret & LPI_CTRL_STATUS_TLPIEX) {
CHIP_DBG(KERN_INFO "GMAC TX exit from LPI\n");
status |= core_irq_tx_path_exit_lpi_mode;
x->irq_tx_path_exit_lpi_mode_n++;
}
if (lpi_status & LPI_CTRL_STATUS_RLPIEN) {
if (ret & LPI_CTRL_STATUS_RLPIEN) {
CHIP_DBG(KERN_INFO "GMAC RX entered in LPI\n");
status |= core_irq_rx_path_in_lpi_mode;
x->irq_rx_path_in_lpi_mode_n++;
}
if (lpi_status & LPI_CTRL_STATUS_RLPIEX) {
if (ret & LPI_CTRL_STATUS_RLPIEX) {
CHIP_DBG(KERN_INFO "GMAC RX exit from LPI\n");
status |= core_irq_rx_path_exit_lpi_mode;
x->irq_rx_path_exit_lpi_mode_n++;
}
}
if ((intr_status & pcs_ane_irq) || (intr_status & pcs_link_irq)) {
CHIP_DBG(KERN_INFO "GMAC PCS ANE IRQ\n");
readl(ioaddr + GMAC_AN_STATUS);
x->irq_pcs_ane_n++;
}
if (intr_status & rgmii_irq) {
u32 status = readl(ioaddr + GMAC_S_R_GMII);
CHIP_DBG(KERN_INFO "GMAC RGMII/SGMII interrupt\n");
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("Link is Up - %d/%s\n", (int) x->pcs_speed,
x->pcs_duplex ? "Full" : "Half");
} else {
x->pcs_link = 0;
pr_debug("Link is Down\n");
}
}
return status;
return ret;
}
static void dwmac1000_set_eee_mode(void __iomem *ioaddr)
......@@ -297,6 +330,41 @@ static void dwmac1000_set_eee_timer(void __iomem *ioaddr, int ls, int tw)
writel(value, ioaddr + LPI_TIMER_CTRL);
}
static void dwmac1000_ctrl_ane(void __iomem *ioaddr, bool restart)
{
u32 value;
value = readl(ioaddr + GMAC_AN_CTRL);
/* auto negotiation enable and External Loopback enable */
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(void __iomem *ioaddr, struct rgmii_adv *adv)
{
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,
......@@ -311,6 +379,8 @@ static const struct stmmac_ops dwmac1000_ops = {
.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)
......
......@@ -30,8 +30,8 @@
#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)
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;
......@@ -73,6 +73,10 @@ static int dwmac1000_dma_init(void __iomem *ioaddr, int pbl, int fb,
#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
......
......@@ -72,7 +72,8 @@ static int dwmac100_rx_ipc_enable(void __iomem *ioaddr)
return 0;
}
static int dwmac100_irq_status(void __iomem *ioaddr)
static int dwmac100_irq_status(void __iomem *ioaddr,
struct stmmac_extra_stats *x)
{
return 0;
}
......
......@@ -32,8 +32,8 @@
#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)
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;
......
......@@ -150,6 +150,57 @@ static int enh_desc_coe_rdes0(int ipc_err, int type, int payload_err)
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_DELAY_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)
{
......@@ -198,7 +249,7 @@ static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x,
* At any rate, we need to understand if the CSUM hw computation is ok
* and report this info to the upper layers. */
ret = enh_desc_coe_rdes0(p->des01.erx.ipc_csum_error,
p->des01.erx.frame_type, p->des01.erx.payload_csum_error);
p->des01.erx.frame_type, p->des01.erx.rx_mac_addr);
if (unlikely(p->des01.erx.dribbling)) {
CHIP_DBG(KERN_ERR "GMAC RX: dribbling error\n");
......@@ -225,34 +276,32 @@ static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x,
x->rx_vlan++;
}
#endif
return ret;
}
static void enh_desc_init_rx_desc(struct dma_desc *p, unsigned int ring_size,
int disable_rx_ic)
static void enh_desc_init_rx_desc(struct dma_desc *p, int disable_rx_ic,
int mode, int end)
{
int i;
for (i = 0; i < ring_size; i++) {
p->des01.erx.own = 1;
p->des01.erx.buffer1_size = BUF_SIZE_8KiB - 1;
p->des01.erx.own = 1;
p->des01.erx.buffer1_size = BUF_SIZE_8KiB - 1;
ehn_desc_rx_set_on_ring_chain(p, (i == ring_size - 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;
p++;
}
if (disable_rx_ic)
p->des01.erx.disable_ic = 1;
}
static void enh_desc_init_tx_desc(struct dma_desc *p, unsigned int ring_size)
static void enh_desc_init_tx_desc(struct dma_desc *p, int mode, int end)
{
int i;
for (i = 0; i < ring_size; i++) {
p->des01.etx.own = 0;
ehn_desc_tx_set_on_ring_chain(p, (i == ring_size - 1));
p++;
}
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)
......@@ -280,20 +329,26 @@ 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)
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));
enh_desc_end_tx_desc(p, ter);
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 csum_flag, int mode)
{
p->des01.etx.first_segment = is_fs;
enh_set_tx_desc_len(p, len);
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;
......@@ -323,6 +378,49 @@ static int enh_desc_get_rx_frame_len(struct dma_desc *p, int rx_coe_type)
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,
......@@ -339,4 +437,9 @@ const struct stmmac_desc_ops enh_desc_ops = {
.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,
};
......@@ -122,30 +122,28 @@ static int ndesc_get_rx_status(void *data, struct stmmac_extra_stats *x,
return ret;
}
static void ndesc_init_rx_desc(struct dma_desc *p, unsigned int ring_size,
int disable_rx_ic)
static void ndesc_init_rx_desc(struct dma_desc *p, int disable_rx_ic, int mode,
int end)
{
int i;
for (i = 0; i < ring_size; i++) {
p->des01.rx.own = 1;
p->des01.rx.buffer1_size = BUF_SIZE_2KiB - 1;
p->des01.rx.own = 1;
p->des01.rx.buffer1_size = BUF_SIZE_2KiB - 1;
ndesc_rx_set_on_ring_chain(p, (i == ring_size - 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;
p++;
}
if (disable_rx_ic)
p->des01.rx.disable_ic = 1;
}
static void ndesc_init_tx_desc(struct dma_desc *p, unsigned int ring_size)
static void ndesc_init_tx_desc(struct dma_desc *p, int mode, int end)
{
int i;
for (i = 0; i < ring_size; i++) {
p->des01.tx.own = 0;
ndesc_tx_set_on_ring_chain(p, (i == (ring_size - 1)));
p++;
}
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)
......@@ -173,19 +171,25 @@ 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)
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));
ndesc_end_tx_desc(p, ter);
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 csum_flag, int mode)
{
p->des01.tx.first_segment = is_fs;
norm_set_tx_desc_len(p, len);
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;
......@@ -215,6 +219,39 @@ static int ndesc_get_rx_frame_len(struct dma_desc *p, int rx_coe_type)
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,
......@@ -231,4 +268,8 @@ const struct stmmac_desc_ops ndesc_ops = {
.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,
};
......@@ -48,25 +48,30 @@ static unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
desc->des2 = dma_map_single(priv->device, skb->data,
bmax, DMA_TO_DEVICE);
priv->tx_skbuff_dma[entry] = desc->des2;
desc->des3 = desc->des2 + BUF_SIZE_4KiB;
priv->hw->desc->prepare_tx_desc(desc, 1, bmax,
csum);
priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum,
STMMAC_RING_MODE);
wmb();
entry = (++priv->cur_tx) % txsize;
desc = priv->dma_tx + entry;
desc->des2 = dma_map_single(priv->device, skb->data + bmax,
len, DMA_TO_DEVICE);
priv->tx_skbuff_dma[entry] = desc->des2;
desc->des3 = desc->des2 + BUF_SIZE_4KiB;
priv->hw->desc->prepare_tx_desc(desc, 0, len, csum);
priv->hw->desc->prepare_tx_desc(desc, 0, len, csum,
STMMAC_RING_MODE);
wmb();
priv->hw->desc->set_tx_owner(desc);
priv->tx_skbuff[entry] = NULL;
} else {
desc->des2 = dma_map_single(priv->device, skb->data,
nopaged_len, DMA_TO_DEVICE);
priv->tx_skbuff_dma[entry] = desc->des2;
desc->des3 = desc->des2 + BUF_SIZE_4KiB;
priv->hw->desc->prepare_tx_desc(desc, 1, nopaged_len, csum);
priv->hw->desc->prepare_tx_desc(desc, 1, nopaged_len, csum,
STMMAC_RING_MODE);
}
return entry;
......@@ -82,27 +87,23 @@ static unsigned int stmmac_is_jumbo_frm(int len, int enh_desc)
return ret;
}
static void stmmac_refill_desc3(int bfsize, struct dma_desc *p)
static void stmmac_refill_desc3(void *priv_ptr, struct dma_desc *p)
{
/* Fill DES3 in case of RING mode */
if (bfsize >= BUF_SIZE_8KiB)
p->des3 = p->des2 + BUF_SIZE_8KiB;
}
struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr;
/* In ring mode we need to fill the desc3 because it is used
* as buffer */
static void stmmac_init_desc3(int des3_as_data_buf, struct dma_desc *p)
{
if (unlikely(des3_as_data_buf))
p->des3 = p->des2 + BUF_SIZE_8KiB;
if (unlikely(priv->plat->has_gmac))
/* Fill DES3 in case of RING mode */
if (priv->dma_buf_sz >= BUF_SIZE_8KiB)
p->des3 = p->des2 + BUF_SIZE_8KiB;
}
static void stmmac_init_dma_chain(struct dma_desc *des, dma_addr_t phy_addr,
unsigned int size)
/* 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(struct dma_desc *p)
static void stmmac_clean_desc3(void *priv_ptr, struct dma_desc *p)
{
if (unlikely(p->des3))
p->des3 = 0;
......@@ -121,7 +122,6 @@ const struct stmmac_ring_mode_ops ring_mode_ops = {
.jumbo_frm = stmmac_jumbo_frm,
.refill_desc3 = stmmac_refill_desc3,
.init_desc3 = stmmac_init_desc3,
.init_dma_chain = stmmac_init_dma_chain,
.clean_desc3 = stmmac_clean_desc3,
.set_16kib_bfsize = stmmac_set_16kib_bfsize,
};
......@@ -24,25 +24,29 @@
#define __STMMAC_H__
#define STMMAC_RESOURCE_NAME "stmmaceth"
#define DRV_MODULE_VERSION "Nov_2012"
#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>
struct stmmac_priv {
/* Frequently used values are kept adjacent for cache effect */
struct dma_desc *dma_tx ____cacheline_aligned;
struct dma_desc *dma_tx ____cacheline_aligned; /* Basic TX desc */
struct dma_extended_desc *dma_etx; /* Extended TX descriptor */
dma_addr_t dma_tx_phy;
struct sk_buff **tx_skbuff;
dma_addr_t *tx_skbuff_dma;
unsigned int cur_tx;
unsigned int dirty_tx;
unsigned int dma_tx_size;
int tx_coalesce;
struct dma_desc *dma_rx ;
struct dma_desc *dma_rx; /* Basic RX descriptor */
struct dma_extended_desc *dma_erx; /* Extended RX descriptor */
unsigned int cur_rx;
unsigned int dirty_rx;
struct sk_buff **rx_skbuff;
......@@ -93,6 +97,16 @@ struct stmmac_priv {
u32 tx_coal_timer;
int use_riwt;
u32 rx_riwt;
unsigned int mode;
int extend_desc;
int pcs;
int hwts_tx_en;
int hwts_rx_en;
unsigned int default_addend;
u32 adv_ts;
struct ptp_clock *ptp_clock;
struct ptp_clock_info ptp_clock_ops;
spinlock_t ptp_lock;
};
extern int phyaddr;
......@@ -102,6 +116,9 @@ extern int stmmac_mdio_register(struct net_device *ndev);
extern void stmmac_set_ethtool_ops(struct net_device *netdev);
extern const struct stmmac_desc_ops enh_desc_ops;
extern const struct stmmac_desc_ops ndesc_ops;
extern const struct stmmac_hwtimestamp stmmac_ptp;
extern int stmmac_ptp_register(struct stmmac_priv *priv);
extern void stmmac_ptp_unregister(struct stmmac_priv *priv);
int stmmac_freeze(struct net_device *ndev);
int stmmac_restore(struct net_device *ndev);
int stmmac_resume(struct net_device *ndev);
......
......@@ -27,6 +27,7 @@
#include <linux/interrupt.h>
#include <linux/mii.h>
#include <linux/phy.h>
#include <linux/net_tstamp.h>
#include <asm/io.h>
#include "stmmac.h"
......@@ -108,6 +109,33 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = {
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)
......@@ -219,6 +247,70 @@ static int stmmac_ethtool_getsettings(struct net_device *dev,
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)
priv->hw->mac->get_adv(priv->ioaddr, &adv);
else
return -EOPNOTSUPP; /* should never happen indeed */
/* 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);
......@@ -243,6 +335,30 @@ static int stmmac_ethtool_setsettings(struct net_device *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;
if (cmd->autoneg == AUTONEG_ENABLE) {
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->ioaddr, 1);
spin_unlock(&priv->lock);
}
return 0;
}
spin_lock(&priv->lock);
rc = phy_ethtool_sset(phy, cmd);
spin_unlock(&priv->lock);
......@@ -312,6 +428,9 @@ stmmac_get_pauseparam(struct net_device *netdev,
{
struct stmmac_priv *priv = netdev_priv(netdev);
if (priv->pcs) /* FIXME */
return;
spin_lock(&priv->lock);
pause->rx_pause = 0;
......@@ -335,6 +454,9 @@ stmmac_set_pauseparam(struct net_device *netdev,
int new_pause = FLOW_OFF;
int ret = 0;
if (priv->pcs) /* FIXME */
return -EOPNOTSUPP;
spin_lock(&priv->lock);
if (pause->rx_pause)
......@@ -604,6 +726,38 @@ static int stmmac_set_coalesce(struct net_device *dev,
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,
......@@ -623,7 +777,7 @@ static const struct ethtool_ops stmmac_ethtool_ops = {
.get_eee = stmmac_ethtool_op_get_eee,
.set_eee = stmmac_ethtool_op_set_eee,
.get_sset_count = stmmac_get_sset_count,
.get_ts_info = ethtool_op_get_ts_info,
.get_ts_info = stmmac_get_ts_info,
.get_coalesce = stmmac_get_coalesce,
.set_coalesce = stmmac_set_coalesce,
};
......
/*******************************************************************************
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 * 100) / 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,
};
/*******************************************************************************
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->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->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,
.pps = 0,
.adjfreq = stmmac_adjust_freq,
.adjtime = stmmac_adjust_time,
.gettime = stmmac_get_time,
.settime = stmmac_set_time,
.enable = stmmac_enable,
};
/**
* stmmac_ptp_register
*
* @ndev: net device pointer
*
* 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
*
* @ndev: net device pointer
*
* 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);
pr_debug("Removed PTP HW clock successfully on %s\n",
priv->dev->name);
}
}
/******************************************************************************
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__
#define STMMAC_SYSCLOCK 62500000
/* 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__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment