blob: 382c57b99f0a5d93f4bf91a95127e1f8c32c57fc [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* emac ptp driver
*
* Copyright (C) 2023 ASR Micro Limited
*
*/
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/pps_kernel.h>
#include <linux/timer.h>
#include <linux/time64.h>
#include <linux/types.h>
#include "emac_eth.h"
#define to_emacpriv(_ptp) container_of(_ptp, struct emac_priv, ptp_clock_ops)
#define CIU_UTC_OUT_REG1 0x54
#define CIU_UTC_OUT_REG2 0x58
#define CIU_PPS_SOURCE 0xBC
#define INCVALUE_100MHZ 10
#define INCVALUE_SHIFT_HW 19
#define INCVALUE_SHIFT_SW 23
#define INCPERIOD 1
#ifndef NS_PER_SEC
#define NS_PER_SEC 1000000000ULL
#endif
//#define EMAC_PPS_DEBUG
/* Another drawback of scaling the incvalue by a large factor is the
* 64-bit SYSTIM register overflows more quickly. This is dealt with
* by simply reading the clock before it overflows.
*
* Clock ns bits Overflows after
* ~~~~~~ ~~~~~~~ ~~~~~~~~~~~~~~~
* 100MHz (64-19)bit 2^45 / 10^9 / 3600 = 9.77 hrs
*/
#define EMAC_SYSTIM_OVERFLOW_CNT (1ULL << (64 - INCVALUE_SHIFT_HW))
#define EMAC_SYSTIM_OVERFLOW_SEC ((unsigned long)((EMAC_SYSTIM_OVERFLOW_CNT/NS_PER_SEC)/2))
#define EMAC_SYSTIM_OVERFLOW_PERIOD (HZ * EMAC_SYSTIM_OVERFLOW_SEC)
static u64 emac_timer_cyc2ns(struct emac_priv *priv, u64 cycle_delta,
u64 *frac, int backwards)
{
struct timecounter *tc = &priv->tc;
u64 delta = cycle_delta & tc->cc->mask;
u64 nsec, adj, to_adj;
int neg_adj;
if (!cycle_delta)
return 0;
if (priv->hw_adj) {
nsec = delta * tc->cc->mult;
if (priv->frac_div > 0)
nsec += div_u64(nsec, priv->frac_div);
if (frac) {
if (backwards)
nsec -= *frac;
else
nsec += *frac;
*frac = nsec & tc->mask;
}
nsec >>= tc->cc->shift;
} else {
nsec = delta * tc->cc->mult;
if (priv->addend_adj < 0) {
neg_adj = 1;
adj = -priv->addend_adj;
} else {
neg_adj = 0;
adj = priv->addend_adj;
}
to_adj = div_u64(nsec, priv->ptp_clk_inc) * adj;
if (priv->frac_div > 0)
nsec += div_u64(nsec, priv->frac_div);
if (neg_adj)
nsec -= to_adj >> tc->cc->shift;
else
nsec += to_adj >> tc->cc->shift;
if (frac)
*frac = 0;
}
return nsec;
}
static void emac_timecounter_init(struct emac_priv *priv, u64 ns)
{
timecounter_init(&priv->tc, &priv->cc, ns);
if (!priv->pps_info.enable_pps)
return;
priv->pps_info.utc_ns = ns;
}
static u64 emac_timecounter_read(struct emac_priv *priv)
{
struct timecounter *tc = &priv->tc;
u64 cycle_now, cycle_delta;
u64 ns_offset;
/* read cycle counter: */
cycle_now = tc->cc->read(tc->cc);
/* calculate the delta since the last timecounter_read_delta(): */
cycle_delta = (cycle_now - tc->cycle_last) & tc->cc->mask;
/* convert to nanoseconds: */
ns_offset = emac_timer_cyc2ns(priv, cycle_delta, &tc->frac, 0);
/* update time stamp of timecounter_read_delta() call: */
tc->cycle_last = cycle_now;
ns_offset += tc->nsec;
tc->nsec = ns_offset;
return ns_offset;
}
u64 emac_timer_cyc2time(struct emac_priv *priv, u64 cycle_tstamp)
{
struct timecounter *tc = &priv->tc;
u64 nsec, cycle_last, delta, frac;
int backwards = 0;
u64 delta_ns;
if (priv->pps_info.enable_pps) {
nsec = priv->pps_info.utc_ns;
frac = 0;
cycle_last = priv->pps_info.ppstime;
} else {
nsec = tc->nsec;
frac = tc->frac;
cycle_last = tc->cycle_last;
}
delta = (cycle_tstamp - cycle_last) & tc->cc->mask;
if (delta > tc->cc->mask / 2) {
delta = (cycle_last - cycle_tstamp) & tc->cc->mask;
backwards = 1;
}
delta_ns = emac_timer_cyc2ns(priv, delta, &frac, backwards);
if (backwards)
nsec -= delta_ns;
else
nsec += delta_ns;
return nsec;
}
void emac_hw_timestamp_config(struct emac_priv *priv, u32 enable,
u8 rx_ptp_type, u32 ptp_msg_id)
{
void __iomem *ioaddr = priv->iobase;
u32 val;
if (enable) {
/*
* enable tx/rx timestamp and config rx ptp type
*/
val = emac_rd(priv, PTP_1588_CTRL);
val |= TX_TIMESTAMP_EN | RX_TIMESTAMP_EN;
val &= ~RX_PTP_PKT_TYPE_MSK;
val |= (rx_ptp_type << RX_PTP_PKT_TYPE_OFST) &
RX_PTP_PKT_TYPE_MSK;
writel(val, ioaddr + PTP_1588_CTRL);
/* config ptp message id */
writel(ptp_msg_id, ioaddr + PTP_MSG_ID);
/* config ptp ethernet type */
writel(ETH_P_1588, ioaddr + PTP_ETH_TYPE);
/* config ptp udp port */
writel(PTP_EVENT_PORT, ioaddr + PTP_UDP_PORT);
} else {
val = emac_rd(priv, PTP_1588_CTRL);
val &= ~(TX_TIMESTAMP_EN | RX_TIMESTAMP_EN);
writel(val, ioaddr + PTP_1588_CTRL);
}
}
u32 emac_hw_config_systime_increment(struct emac_priv *priv)
{
void __iomem *ioaddr = priv->iobase;
u32 ptp_clock = priv->ptp_clk_rate;
u32 cycle_inc, cycle_mod;
u32 val;
/*
* set system time counter resolution as ns if ptp clock is 100Mhz,
* 10ns per clock cycle, so increment value should be 10, increment
* period should be 1
*/
cycle_inc = (NS_PER_SEC / ptp_clock) * INCPERIOD;
cycle_mod = NS_PER_SEC % ptp_clock;
priv->ptp_clk_inc = cycle_inc;
/*
* Assume ptp_clk_rate= 38.4M
* Tns = 1000000000/38400000 = 26 + 1/24
* nsec = (delta/26) * Tns = (delta/26) * (26 + 1/24)
* nsec = delta + delta/(26*24)
* frag = delta/div, div = 26*24
*/
if (cycle_mod > 0)
priv->frac_div = (priv->ptp_clk_rate * cycle_inc / cycle_mod);
else
priv->frac_div = 0;
if (priv->hw_adj) {
priv->default_addend = cycle_inc << INCVALUE_SHIFT_HW;
val = (priv->default_addend | (INCPERIOD << INCR_PERIOD_OFST));
} else {
priv->default_addend = cycle_inc << INCVALUE_SHIFT_SW;
priv->addend_adj = 0;
val = (cycle_inc | (INCPERIOD << INCR_PERIOD_OFST));
}
writel(val, ioaddr + PTP_INRC_ATTR);
pr_info("default_addend=%d cycle_mod=%d cycle_inc=%d frac_div=%d\n",
priv->default_addend, cycle_mod, cycle_inc, priv->frac_div);
return 0;
}
u64 emac_hw_get_systime(struct emac_priv *priv)
{
void __iomem *ioaddr = priv->iobase;
unsigned long flags;
u64 systimel, systimeh;
u64 systim;
local_irq_save(flags);
/* first read system time low register */
systimel = readl(ioaddr + SYS_TIME_GET_LOW);
systimeh = readl(ioaddr + SYS_TIME_GET_HI);
systim = (systimeh << 32) | systimel;
/* Add a dummy read to WA systimer jump issue */
systimel = emac_rd(priv, SYS_TIME_ADJ_LOW);
local_irq_restore(flags);
return systim;
}
u64 emac_hw_get_ppstime(struct emac_priv *priv)
{
void __iomem *ioaddr = priv->iobase;
u64 ppstimel, ppstimeh;
u64 ppstime;
ppstimel = readl(ioaddr + PTP_PPS_TIME_L);
ppstimeh = readl(ioaddr + PTP_PPS_TIME_H);
ppstime = (ppstimeh << 32) | ppstimel;
return ppstime;
}
u64 emac_hw_get_phc_time(struct emac_priv *priv)
{
unsigned long flags;
u64 cycles, ns;
spin_lock_irqsave(&priv->ptp_lock, flags);
cycles = emac_hw_get_systime(priv);
ns = emac_timer_cyc2time(priv, cycles);
spin_unlock_irqrestore(&priv->ptp_lock, flags);
return ns;
}
u64 emac_hw_get_tx_timestamp(struct emac_priv *priv)
{
void __iomem *ioaddr = priv->iobase;
unsigned long flags;
u64 systimel, systimeh;
u64 systim;
u64 ns;
/* first read system time low register */
systimel = readl(ioaddr + TX_TIMESTAMP_LOW);
systimeh = readl(ioaddr + TX_TIMESTAMP_HI);
systim = (systimeh << 32) | systimel;
spin_lock_irqsave(&priv->ptp_lock, flags);
ns = emac_timer_cyc2time(priv, systim);
spin_unlock_irqrestore(&priv->ptp_lock, flags);
return ns;
}
u64 emac_hw_get_rx_timestamp(struct emac_priv *priv)
{
void __iomem *ioaddr = priv->iobase;
unsigned long flags;
u64 systimel, systimeh;
u64 systim;
u64 ns;
/* first read system time low register */
systimel = readl(ioaddr + RX_TIMESTAMP_LOW);
systimeh = readl(ioaddr + RX_TIMESTAMP_HI);
systim = (systimeh << 32) | systimel;
spin_lock_irqsave(&priv->ptp_lock, flags);
ns = emac_timer_cyc2time(priv, systim);
spin_unlock_irqrestore(&priv->ptp_lock, flags);
return ns;
}
/**
* emac_cyclecounter_read - read raw cycle counter (used by time counter)
* @cc: cyclecounter structure
**/
static u64 emac_cyclecounter_read(const struct cyclecounter *cc)
{
struct emac_priv *priv = container_of(cc, struct emac_priv, cc);
return emac_hw_get_systime(priv);
}
int emac_hw_init_systime(struct emac_priv *priv, u64 set_ns)
{
unsigned long flags;
spin_lock_irqsave(&priv->ptp_lock, flags);
emac_timecounter_init(priv, set_ns);
spin_unlock_irqrestore(&priv->ptp_lock, flags);
return 0;
}
struct emac_hw_ptp emac_hwptp = {
.config_hw_tstamping = emac_hw_timestamp_config,
.config_systime_increment = emac_hw_config_systime_increment,
.init_systime = emac_hw_init_systime,
.get_phc_time = emac_hw_get_phc_time,
.get_tx_timestamp = emac_hw_get_tx_timestamp,
.get_rx_timestamp = emac_hw_get_rx_timestamp,
};
/**
* emac_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 emac_adjust_freq(struct ptp_clock_info *ptp, s32 ppb)
{
struct emac_priv *priv = to_emacpriv(ptp);
void __iomem *ioaddr = priv->iobase;
unsigned long flags;
int neg_adj = 0;
u64 incvalue, adj;
u32 addend;
/* ppb means delta time each sample cycle, in nano second */
if ((ppb > ptp->max_adj) || (ppb <= -1000000000))
return -EINVAL;
if (!ppb)
return 0;
if (ppb < 0) {
neg_adj = 1;
ppb = -ppb;
}
spin_lock_irqsave(&priv->ptp_lock, flags);
incvalue = priv->default_addend;
adj = incvalue;
adj *= ppb;
adj = div_u64(adj, 1000000000);
if (priv->hw_adj) {
addend = neg_adj ? (incvalue - adj) : (incvalue + adj);
addend = (addend | (INCPERIOD << INCR_PERIOD_OFST));
writel(addend, ioaddr + PTP_INRC_ATTR);
priv->addend_adj = 0;
} else {
/* update tc->cycle_last since adj to be changed */
emac_timecounter_read(priv);
priv->addend_adj = neg_adj ? - adj : adj;
}
#ifdef EMAC_PPS_DEBUG
pr_info("emac_adjust_freq: ppb=%d adj=%s%lld\n", ppb,
neg_adj ? "-" : "+" , adj);
#endif
spin_unlock_irqrestore(&priv->ptp_lock, flags);
return 0;
}
/**
* emac_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 emac_adjust_time(struct ptp_clock_info *ptp, s64 delta)
{
struct emac_priv *priv = to_emacpriv(ptp);
unsigned long flags;
spin_lock_irqsave(&priv->ptp_lock, flags);
if (priv->pps_info.enable_pps)
priv->pps_info.utc_ns += delta;
else
timecounter_adjtime(&priv->tc, delta);
spin_unlock_irqrestore(&priv->ptp_lock, flags);
return 0;
}
/**
* emac_phc_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 emac_phc_get_time(struct ptp_clock_info *ptp, struct timespec64 *ts)
{
struct emac_priv *priv = to_emacpriv(ptp);
unsigned long flags;
u64 cycles, ns;
spin_lock_irqsave(&priv->ptp_lock, flags);
cycles = emac_hw_get_systime(priv);
ns = emac_timer_cyc2time(priv, cycles);
spin_unlock_irqrestore(&priv->ptp_lock, flags);
*ts = ns_to_timespec64(ns);
return 0;
}
/**
* emac_phc_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 emac_phc_set_time(struct ptp_clock_info *ptp,
const struct timespec64 *ts)
{
struct emac_priv *priv = to_emacpriv(ptp);
unsigned long flags;
u64 ns;
ns = timespec64_to_ns(ts);
spin_lock_irqsave(&priv->ptp_lock, flags);
if (priv->pps_info.enable_pps &&
priv->pps_info.pps_source == EMAC_PPS_GNSS) {
u64 ppstime;
ppstime = emac_hw_get_ppstime(priv);
if (ppstime != priv->pps_info.ppstime) {
pr_warn("New PPS come, the time is out-of-date\n");
spin_unlock_irqrestore(&priv->ptp_lock, flags);
return 0;
}
}
emac_timecounter_init(priv, ns);
spin_unlock_irqrestore(&priv->ptp_lock, flags);
return 0;
}
/* structure describing a PTP hardware clock */
static struct ptp_clock_info emac_ptp_clock_ops = {
.owner = THIS_MODULE,
.name = "emac_ptp_clock",
.n_alarm = 0,
.n_ext_ts = 0,
.n_per_out = 0,
.n_pins = 0,
.pps = 0,
.adjfreq = emac_adjust_freq,
.adjtime = emac_adjust_time,
.gettime64 = emac_phc_get_time,
.settime64 = emac_phc_set_time,
};
#ifdef CONFIG_PPS
static s64 emac_read_bcode_utc(void)
{
unsigned int year, mon, day, hour, min, sec;
struct timespec64 utc;
struct tm result;
s64 utc_ns;
u32 val;
val = readl(CIU_VIRT_BASE + CIU_UTC_OUT_REG2);
year = 2000 + ((val >> 16) & 0x7F);
mon = 0;
day = val & 0x1FF;
val = readl(CIU_VIRT_BASE + CIU_UTC_OUT_REG1);
hour = (val >> 16) & 0x1F;
min = (val >> 8) & 0x3F;
sec = val & 0x3F;
utc.tv_sec = mktime64(year, mon, day, hour, min, sec);
utc.tv_nsec = 0;
utc_ns = timespec64_to_ns(&utc);
time64_to_tm(utc.tv_sec, 0, &result);
#ifdef EMAC_PPS_DEBUG
pr_info("UTC from bcode: %lldns\n", utc_ns);
pr_info("%d-%d-%d %d:%d:%d ==> %ld-%d-%d %d:%d:%d\n",
year, mon, day, hour, min, sec,
(1900 + result.tm_year), (result.tm_mon + 1), result.tm_mday,
result.tm_hour, result.tm_min, result.tm_sec);
#endif
return utc_ns;
}
static inline int emac_lost_pps_interrupt(struct emac_priv *priv, u64 *ns,
u64 interval)
{
s64 offset = 0;
*ns = 0;
if (interval < (priv->pps_info.pps_cycle * NS_PER_SEC * 3)/2)
return 0;
*ns = div_u64(interval, NS_PER_SEC);
*ns *= NS_PER_SEC;
offset = interval - *ns;
if (offset > NS_PER_SEC / 2)
*ns += NS_PER_SEC;
pr_warn("Lost PPS signal about %lldns\n", interval);
return 1;
}
static inline void emac_pps_get_ts(struct emac_priv *priv,
struct pps_event_time *ts, u64 pps_ns)
{
struct emac_pps *info = &priv->pps_info;
struct system_time_snapshot snap;
u64 ns;
ktime_get_snapshot(&snap);
if (priv->pps_info.enable_pps) {
ts->ts_real = ns_to_timespec64(info->utc_ns + pps_ns);
if (info->pps_source == EMAC_PPS_BCODE) {
info->utc_ns = emac_read_bcode_utc();
} else {
if (emac_lost_pps_interrupt(priv, &ns, pps_ns))
info->utc_ns += ns;
else
info->utc_ns += info->pps_cycle * NS_PER_SEC;
}
} else {
ts->ts_real = ktime_to_timespec64(snap.real);
}
#ifdef CONFIG_NTP_PPS
ts->ts_raw = ktime_to_timespec64(snap.raw);
#endif
}
static irqreturn_t emac_pps_irq(int irq, void *dev_id)
{
struct emac_priv *priv = (struct emac_priv *)dev_id;
struct ptp_clock *ptp = priv->ptp_clock;
struct pps_device *pps;
struct pps_event_time ts;
u32 pps_cycle, pps_cnt;
u64 ppstime, systime;
u64 ppstime_ns;
u64 pps_interval_ns, pps_interval, delay_ns;
u32 status;
status = emac_rd(priv, PTP_1588_IRQ_STS);
if (!(status & PTP_PPS_VALID))
return IRQ_NONE;
ppstime = emac_hw_get_ppstime(priv);
systime = emac_hw_get_systime(priv);
pps_cnt = emac_rd(priv, PTP_PPS_COUNTER);
pps_cycle = emac_rd(priv, PTP_PPS_VALUE);
delay_ns = emac_timer_cyc2ns(priv, systime - ppstime, NULL, 0);
pps_interval = ppstime - priv->pps_info.ppstime;
pps_interval_ns = emac_timer_cyc2ns(priv, pps_interval, NULL, 0);
ppstime_ns = emac_timer_cyc2ns(priv, ppstime, NULL, 0);
#ifdef EMAC_PPS_DEBUG
pr_info("emac_pps_irq: pps_cnt=%d interrupt_delay=%lldns\n",
pps_cnt, delay_ns);
pr_info("emac_pps_irq: ppstime_ns=%lld interval=%lldns ppstime=%lld systime=%lld\n",
ppstime_ns, pps_interval_ns, ppstime, systime);
#endif
/* report pps event */
pps = ptp_pps_device(ptp);
if (pps) {
#ifdef EMAC_PPS_DEBUG
s64 diff;
diff = NS_PER_SEC * pps_cycle * (pps_cnt - priv->pps_info.ppscnt);
diff = pps_interval_ns - diff;
/*
* Stop when |diff| < cycle, show higher accuracy for
* frequency synchronization via PPS
*/
if (abs(diff) < priv->ptp_clk_inc)
pps_interval_ns = NS_PER_SEC * pps_cycle;
pr_info("emac_pps_irq: pps width diff %lldns\n", diff);
#endif
emac_pps_get_ts(priv, &ts, pps_interval_ns);
pps_event(pps, &ts, PPS_CAPTUREASSERT, dev_id);
}
priv->pps_info.ppstime = ppstime;
priv->pps_info.ppscnt = pps_cnt;
emac_wr(priv, PTP_1588_IRQ_STS, PTP_PPS_VALID);
return IRQ_HANDLED;
}
static int emac_ptp_config_pps(struct emac_priv *priv)
{
unsigned long timeo = jiffies + msecs_to_jiffies(500);
u64 ppstime;
u32 pps_cnt;
u32 reg;
int ret;
if (priv->irq_pps) {
ret = request_irq(priv->irq_pps, emac_pps_irq,
IRQF_SHARED, "emac_pps", priv);
if (ret) {
pr_err("request irq_pps failed, ret=%d\\n", ret);
return ret;
}
}
priv->ptp_clock_ops.pps = 1;
priv->pps_info.pps_cycle = 1;
/* Config PPS source */
reg = readl(CIU_VIRT_BASE + CIU_PPS_SOURCE);
if (priv->pps_info.pps_source == EMAC_PPS_BCODE)
reg &= ~BIT(0);
else
reg |= BIT(0);
writel(reg, CIU_VIRT_BASE + CIU_PPS_SOURCE);
/* Clear PPS register */
pr_info("reset emac PPS\n");
reg = emac_rd(priv, PTP_1588_CTRL);
reg |= PPS_COUNTER_RESET;
emac_wr(priv, PTP_1588_CTRL, reg);
do {
reg = emac_rd(priv, PTP_1588_CTRL);
if (!(reg & PPS_COUNTER_RESET))
break;
} while (time_before(jiffies, timeo));
if (reg & PPS_COUNTER_RESET)
pr_err("reset PPS failed\n");
/* Config PPS interrupt cycle */
emac_wr(priv, PTP_PPS_VALUE, priv->pps_info.pps_cycle);
/* Disable PPS interrupt */
reg = emac_rd(priv, PTP_1588_IRQ_EN);
reg &= ~PTP_PPS_VALID;
emac_wr(priv, PTP_1588_IRQ_EN, reg);
reg = emac_rd(priv, PTP_1588_CTRL);
reg |= PPS_MODE_ENABLE | TX_TIMESTAMP_EN | RX_TIMESTAMP_EN;
emac_wr(priv, PTP_1588_CTRL, reg);
ppstime = emac_hw_get_ppstime(priv);
pps_cnt = emac_rd(priv, PTP_PPS_COUNTER);
pr_info("ppstime=%lld pps_cnt=%d\n", ppstime, pps_cnt);
priv->pps_info.ppscnt = pps_cnt;
priv->pps_info.ppstime = ppstime;
priv->pps_info.utc_ns = emac_read_bcode_utc();
/* Enable PPS interrupt */
reg = emac_rd(priv, PTP_1588_IRQ_EN);
reg |= PTP_PPS_VALID;
emac_wr(priv, PTP_1588_IRQ_EN, reg);
return 0;
}
#endif
static void emac_systim_overflow_work(struct work_struct *work)
{
struct emac_priv *priv = container_of(work, struct emac_priv,
systim_overflow_work.work);
struct timespec64 ts;
u64 ns;
/* Update the timecounter */
ns = emac_timecounter_read(priv);
ts = ns_to_timespec64(ns);
pr_debug("SYSTIM overflow check at %lld.%09lu\n",
(long long) ts.tv_sec, ts.tv_nsec);
#ifdef CONFIG_PPS
if (priv->pps_info.enable_pps) {
u64 systime, ppstime, interval_ns;
unsigned long flags;
spin_lock_irqsave(&priv->ptp_lock, flags);
systime = emac_hw_get_systime(priv);
ppstime = priv->pps_info.ppstime;
interval_ns = emac_timer_cyc2ns(priv, systime - ppstime, NULL, 0);
if (emac_lost_pps_interrupt(priv, &ns, interval_ns)) {
struct timecounter *tc = &priv->tc;
priv->pps_info.utc_ns += ns;
priv->pps_info.ppstime +=
div_u64(ns << tc->cc->shift, tc->cc->mult);
pr_warn("PPS overflow: add %lld seconds to UTC\n", ns);
}
spin_unlock_irqrestore(&priv->ptp_lock, flags);
}
#endif
schedule_delayed_work(&priv->systim_overflow_work,
EMAC_SYSTIM_OVERFLOW_PERIOD);
}
/**
* emac_ptp_register
* @priv: driver private structure
* Description: this function will register the ptp clock driver
* to kernel. It also does some house keeping work.
*/
void emac_ptp_register(struct emac_priv *priv)
{
unsigned long flags;
int max_adj;
priv->cc.read = emac_cyclecounter_read;
priv->cc.mask = CYCLECOUNTER_MASK(64);
priv->cc.mult = 1;
if (priv->hw_adj)
priv->cc.shift = INCVALUE_SHIFT_HW;
else
priv->cc.shift = INCVALUE_SHIFT_SW;
spin_lock_init(&priv->ptp_lock);
priv->ptp_clock_ops = emac_ptp_clock_ops;
max_adj = (1 << (24 - INCVALUE_SHIFT_HW)) - 10;
max_adj = max_adj * priv->ptp_clk_rate * INCPERIOD;
if (max_adj < 0) {
netdev_err(priv->ndev, "ptp increment too large\n");
max_adj = 1000000000;
}
priv->ptp_clock_ops.max_adj = min(max_adj, 1000000000);
INIT_DELAYED_WORK(&priv->systim_overflow_work,
emac_systim_overflow_work);
schedule_delayed_work(&priv->systim_overflow_work,
EMAC_SYSTIM_OVERFLOW_PERIOD);
#ifdef CONFIG_PPS
if (priv->pps_info.enable_pps) {
emac_hw_config_systime_increment(priv);
emac_ptp_config_pps(priv);
netdev_info(priv->ndev, "PPS enabled\n");
} else {
priv->ptp_clock_ops.pps = 0;
}
#endif
priv->ptp_clock = ptp_clock_register(&priv->ptp_clock_ops,
NULL);
if (IS_ERR(priv->ptp_clock)) {
netdev_err(priv->ndev, "ptp_clock_register failed\n");
priv->ptp_clock = NULL;
} else if (priv->ptp_clock)
netdev_info(priv->ndev, "registered PTP clock\n");
else
netdev_info(priv->ndev, "PTP_1588_CLOCK maybe not enabled\n");
spin_lock_irqsave(&priv->ptp_lock, flags);
emac_timecounter_init(priv, ktime_to_ns(ktime_get_real()));
spin_unlock_irqrestore(&priv->ptp_lock, flags);
priv->hwptp = &emac_hwptp;
pr_info("ptp max_adj:%u, overflow timeout:%ld minutes\n",
priv->ptp_clock_ops.max_adj, EMAC_SYSTIM_OVERFLOW_SEC / 60);
}
/**
* emac_ptp_unregister
* @priv: driver private structure
* Description: this function will remove/unregister the ptp clock driver
* from the kernel.
*/
void emac_ptp_unregister(struct emac_priv *priv)
{
cancel_delayed_work_sync(&priv->systim_overflow_work);
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->ndev->name);
}
if (priv->irq_pps)
free_irq(priv->irq_pps, priv);
priv->hwptp = NULL;
}