Skip to content

Commit

Permalink
stmmac: fix PTP support for GMAC4
Browse files Browse the repository at this point in the history
Due to bad management of the descriptors, when use ptp4l,
kernel panics as shown below:
-----------------------------------------------------------
 Unable to handle kernel NULL pointer dereference at virtual
 address 000001ac
 ...
 Internal error: Oops: 17 [#1] SMP ARM
 ...
 Hardware name: STi SoC with Flattened Device Tree
 task: c0c05e80 task.stack: c0c00000
 PC is at dwmac4_wrback_get_tx_timestamp_status+0x0/0xc
 LR is at stmmac_tx_clean+0x2f8/0x4d4
-----------------------------------------------------------

In case of GMAC4 the extended descriptor pointers were
used for getting the timestamp. These are NULL for this HW,
and the normal ones must be used.

The PTP also had problems on this chip due to the bad
register management and issues on the algo adopted to
setup the PTP and getting the timestamp values from the
descriptors.

Signed-off-by: Giuseppe Cavallaro <[email protected]>
Acked-by: Rayagond Kokatanur <[email protected]>
Acked-by: Alexandre TORGUE <[email protected]>
Acked-by: Richard Cochran <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
Giuseppe CAVALLARO authored and davem330 committed Nov 16, 2016
1 parent d204205 commit ba1ffd7
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 73 deletions.
5 changes: 3 additions & 2 deletions drivers/net/ethernet/stmicro/stmmac/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -482,11 +482,12 @@ struct stmmac_ops {
/* PTP and HW Timer helpers */
struct stmmac_hwtimestamp {
void (*config_hw_tstamping) (void __iomem *ioaddr, u32 data);
u32 (*config_sub_second_increment) (void __iomem *ioaddr, u32 clk_rate);
u32 (*config_sub_second_increment)(void __iomem *ioaddr, u32 ptp_clock,
int gmac4);
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);
int add_sub, int gmac4);
u64(*get_systime) (void __iomem *ioaddr);
};

Expand Down
68 changes: 57 additions & 11 deletions drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c
Original file line number Diff line number Diff line change
Expand Up @@ -204,14 +204,18 @@ static void dwmac4_rd_enable_tx_timestamp(struct dma_desc *p)

static int dwmac4_wrback_get_tx_timestamp_status(struct dma_desc *p)
{
return (p->des3 & TDES3_TIMESTAMP_STATUS)
>> TDES3_TIMESTAMP_STATUS_SHIFT;
/* Context type from W/B descriptor must be zero */
if (p->des3 & TDES3_CONTEXT_TYPE)
return -EINVAL;

/* Tx Timestamp Status is 1 so des0 and des1'll have valid values */
if (p->des3 & TDES3_TIMESTAMP_STATUS)
return 0;

return 1;
}

/* NOTE: For RX CTX bit has to be checked before
* HAVE a specific function for TX and another one for RX
*/
static u64 dwmac4_wrback_get_timestamp(void *desc, u32 ats)
static inline u64 dwmac4_get_timestamp(void *desc, u32 ats)
{
struct dma_desc *p = (struct dma_desc *)desc;
u64 ns;
Expand All @@ -223,12 +227,54 @@ static u64 dwmac4_wrback_get_timestamp(void *desc, u32 ats)
return ns;
}

static int dwmac4_context_get_rx_timestamp_status(void *desc, u32 ats)
static int dwmac4_rx_check_timestamp(void *desc)
{
struct dma_desc *p = (struct dma_desc *)desc;
u32 own, ctxt;
int ret = 1;

own = p->des3 & RDES3_OWN;
ctxt = ((p->des3 & RDES3_CONTEXT_DESCRIPTOR)
>> RDES3_CONTEXT_DESCRIPTOR_SHIFT);

if (likely(!own && ctxt)) {
if ((p->des0 == 0xffffffff) && (p->des1 == 0xffffffff))
/* Corrupted value */
ret = -EINVAL;
else
/* A valid Timestamp is ready to be read */
ret = 0;
}

/* Timestamp not ready */
return ret;
}

static int dwmac4_wrback_get_rx_timestamp_status(void *desc, u32 ats)
{
struct dma_desc *p = (struct dma_desc *)desc;
int ret = -EINVAL;

/* Get the status from normal w/b descriptor */
if (likely(p->des3 & TDES3_RS1V)) {
if (likely(p->des1 & RDES1_TIMESTAMP_AVAILABLE)) {
int i = 0;

/* Check if timestamp is OK from context descriptor */
do {
ret = dwmac4_rx_check_timestamp(desc);
if (ret < 0)
goto exit;
i++;

return (p->des1 & RDES1_TIMESTAMP_AVAILABLE)
>> RDES1_TIMESTAMP_AVAILABLE_SHIFT;
} while ((ret == 1) || (i < 10));

if (i == 10)
ret = -EBUSY;
}
}
exit:
return ret;
}

static void dwmac4_rd_init_rx_desc(struct dma_desc *p, int disable_rx_ic,
Expand Down Expand Up @@ -373,8 +419,8 @@ const struct stmmac_desc_ops dwmac4_desc_ops = {
.get_rx_frame_len = dwmac4_wrback_get_rx_frame_len,
.enable_tx_timestamp = dwmac4_rd_enable_tx_timestamp,
.get_tx_timestamp_status = dwmac4_wrback_get_tx_timestamp_status,
.get_timestamp = dwmac4_wrback_get_timestamp,
.get_rx_timestamp_status = dwmac4_context_get_rx_timestamp_status,
.get_rx_timestamp_status = dwmac4_wrback_get_rx_timestamp_status,
.get_timestamp = dwmac4_get_timestamp,
.set_tx_ic = dwmac4_rd_set_tx_ic,
.prepare_tx_desc = dwmac4_rd_prepare_tx_desc,
.prepare_tso_tx_desc = dwmac4_rd_prepare_tso_tx_desc,
Expand Down
4 changes: 4 additions & 0 deletions drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,13 @@
#define TDES3_CTXT_TCMSSV BIT(26)

/* TDES3 Common */
#define TDES3_RS1V BIT(26)
#define TDES3_RS1V_SHIFT 26
#define TDES3_LAST_DESCRIPTOR BIT(28)
#define TDES3_LAST_DESCRIPTOR_SHIFT 28
#define TDES3_FIRST_DESCRIPTOR BIT(29)
#define TDES3_CONTEXT_TYPE BIT(30)
#define TDES3_CONTEXT_TYPE_SHIFT 30

/* TDS3 use for both format (read and write back) */
#define TDES3_OWN BIT(31)
Expand Down Expand Up @@ -117,6 +120,7 @@
#define RDES3_LAST_DESCRIPTOR BIT(28)
#define RDES3_FIRST_DESCRIPTOR BIT(29)
#define RDES3_CONTEXT_DESCRIPTOR BIT(30)
#define RDES3_CONTEXT_DESCRIPTOR_SHIFT 30

/* RDES3 (read format) */
#define RDES3_BUFFER1_VALID_ADDR BIT(24)
Expand Down
1 change: 1 addition & 0 deletions drivers/net/ethernet/stmicro/stmmac/stmmac.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ struct stmmac_priv {
int irq_wake;
spinlock_t ptp_lock;
void __iomem *mmcaddr;
void __iomem *ptpaddr;
u32 rx_tail_addr;
u32 tx_tail_addr;
u32 mss;
Expand Down
43 changes: 34 additions & 9 deletions drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,29 @@ static void stmmac_config_hw_tstamping(void __iomem *ioaddr, u32 data)
}

static u32 stmmac_config_sub_second_increment(void __iomem *ioaddr,
u32 ptp_clock)
u32 ptp_clock, int gmac4)
{
u32 value = readl(ioaddr + PTP_TCR);
unsigned long data;

/* Convert the ptp_clock to nano second
* formula = (2/ptp_clock) * 1000000000
* where, ptp_clock = 50MHz.
/* For GMAC3.x, 4.x versions, convert the ptp_clock to nano second
* formula = (1/ptp_clock) * 1000000000
* where ptp_clock is 50MHz if fine method is used to update system
*/
data = (2000000000ULL / ptp_clock);
if (value & PTP_TCR_TSCFUPDT)
data = (1000000000ULL / 50000000);
else
data = (1000000000ULL / ptp_clock);

/* 0.465ns accuracy */
if (!(value & PTP_TCR_TSCTRLSSR))
data = (data * 1000) / 465;

data &= PTP_SSIR_SSINC_MASK;

if (gmac4)
data = data << GMAC4_PTP_SSIR_SSINC_SHIFT;

writel(data, ioaddr + PTP_SSIR);

return data;
Expand Down Expand Up @@ -104,14 +112,30 @@ static int stmmac_config_addend(void __iomem *ioaddr, u32 addend)
}

static int stmmac_adjust_systime(void __iomem *ioaddr, u32 sec, u32 nsec,
int add_sub)
int add_sub, int gmac4)
{
u32 value;
int limit;

if (add_sub) {
/* If the new sec value needs to be subtracted with
* the system time, then MAC_STSUR reg should be
* programmed with (2^32 – <new_sec_value>)
*/
if (gmac4)
sec = (100000000ULL - sec);

value = readl(ioaddr + PTP_TCR);
if (value & PTP_TCR_TSCTRLSSR)
nsec = (PTP_DIGITAL_ROLLOVER_MODE - nsec);
else
nsec = (PTP_BINARY_ROLLOVER_MODE - nsec);
}

writel(sec, ioaddr + PTP_STSUR);
writel(((add_sub << PTP_STNSUR_ADDSUB_SHIFT) | nsec),
ioaddr + PTP_STNSUR);
value = (add_sub << PTP_STNSUR_ADDSUB_SHIFT) | nsec;
writel(value, ioaddr + PTP_STNSUR);

/* issue command to initialize the system time value */
value = readl(ioaddr + PTP_TCR);
value |= PTP_TCR_TSUPDT;
Expand All @@ -134,8 +158,9 @@ static u64 stmmac_get_systime(void __iomem *ioaddr)
{
u64 ns;

/* Get the TSSS value */
ns = readl(ioaddr + PTP_STNSR);
/* convert sec time value to nanosecond */
/* Get the TSS and convert sec time value to nanosecond */
ns += readl(ioaddr + PTP_STSR) * 1000000000ULL;

return ns;
Expand Down
Loading

0 comments on commit ba1ffd7

Please sign in to comment.