Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ba1ffd74 authored by Giuseppe CAVALLARO's avatar Giuseppe CAVALLARO Committed by David S. Miller
Browse files

stmmac: fix PTP support for GMAC4



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: default avatarGiuseppe Cavallaro <peppe.cavallaro@st.com>
Acked-by: default avatarRayagond Kokatanur <rayagond@vayavyalabs.com>
Acked-by: default avatarAlexandre TORGUE <alexandre.torgue@st.com>
Acked-by: default avatarRichard Cochran <richardcochran@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d2042052
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -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);
};

+57 −11
Original line number Diff line number Diff line
@@ -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;
@@ -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;
}

	return (p->des1 & RDES1_TIMESTAMP_AVAILABLE)
		>> RDES1_TIMESTAMP_AVAILABLE_SHIFT;
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++;

			} 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,
@@ -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,
+4 −0
Original line number Diff line number Diff line
@@ -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)
@@ -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)
+1 −0
Original line number Diff line number Diff line
@@ -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;
+34 −9
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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;
Loading