177 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			177 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*******************************************************************************
 | ||
|   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.
 | ||
| 
 | ||
|   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 config_hw_tstamping(void __iomem *ioaddr, u32 data)
 | ||
| {
 | ||
| 	writel(data, ioaddr + PTP_TCR);
 | ||
| }
 | ||
| 
 | ||
| static void config_sub_second_increment(void __iomem *ioaddr,
 | ||
| 		u32 ptp_clock, int gmac4, u32 *ssinc)
 | ||
| {
 | ||
| 	u32 value = readl(ioaddr + PTP_TCR);
 | ||
| 	unsigned long data;
 | ||
| 	u32 reg_value;
 | ||
| 
 | ||
| 	/* 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
 | ||
| 	 */
 | ||
| 	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;
 | ||
| 
 | ||
| 	reg_value = data;
 | ||
| 	if (gmac4)
 | ||
| 		reg_value <<= GMAC4_PTP_SSIR_SSINC_SHIFT;
 | ||
| 
 | ||
| 	writel(reg_value, ioaddr + PTP_SSIR);
 | ||
| 
 | ||
| 	if (ssinc)
 | ||
| 		*ssinc = data;
 | ||
| }
 | ||
| 
 | ||
| static int 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 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 adjust_systime(void __iomem *ioaddr, u32 sec, u32 nsec,
 | ||
| 		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 = -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);
 | ||
| 	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;
 | ||
| 	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 void get_systime(void __iomem *ioaddr, u64 *systime)
 | ||
| {
 | ||
| 	u64 ns;
 | ||
| 
 | ||
| 	/* Get the TSSS value */
 | ||
| 	ns = readl(ioaddr + PTP_STNSR);
 | ||
| 	/* Get the TSS and convert sec time value to nanosecond */
 | ||
| 	ns += readl(ioaddr + PTP_STSR) * 1000000000ULL;
 | ||
| 
 | ||
| 	if (systime)
 | ||
| 		*systime = ns;
 | ||
| }
 | ||
| 
 | ||
| const struct stmmac_hwtimestamp stmmac_ptp = {
 | ||
| 	.config_hw_tstamping = config_hw_tstamping,
 | ||
| 	.init_systime = init_systime,
 | ||
| 	.config_sub_second_increment = config_sub_second_increment,
 | ||
| 	.config_addend = config_addend,
 | ||
| 	.adjust_systime = adjust_systime,
 | ||
| 	.get_systime = get_systime,
 | ||
| };
 | 
