162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * PTP 1588 clock for Freescale QorIQ 1588 timer
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2010 OMICRON electronics GmbH
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/device.h>
1162306a36Sopenharmony_ci#include <linux/hrtimer.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/of.h>
1562306a36Sopenharmony_ci#include <linux/platform_device.h>
1662306a36Sopenharmony_ci#include <linux/timex.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/clk.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <linux/fsl/ptp_qoriq.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/*
2362306a36Sopenharmony_ci * Register access functions
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/* Caller must hold ptp_qoriq->lock. */
2762306a36Sopenharmony_cistatic u64 tmr_cnt_read(struct ptp_qoriq *ptp_qoriq)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	struct ptp_qoriq_registers *regs = &ptp_qoriq->regs;
3062306a36Sopenharmony_ci	u64 ns;
3162306a36Sopenharmony_ci	u32 lo, hi;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	lo = ptp_qoriq->read(&regs->ctrl_regs->tmr_cnt_l);
3462306a36Sopenharmony_ci	hi = ptp_qoriq->read(&regs->ctrl_regs->tmr_cnt_h);
3562306a36Sopenharmony_ci	ns = ((u64) hi) << 32;
3662306a36Sopenharmony_ci	ns |= lo;
3762306a36Sopenharmony_ci	return ns;
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/* Caller must hold ptp_qoriq->lock. */
4162306a36Sopenharmony_cistatic void tmr_cnt_write(struct ptp_qoriq *ptp_qoriq, u64 ns)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	struct ptp_qoriq_registers *regs = &ptp_qoriq->regs;
4462306a36Sopenharmony_ci	u32 hi = ns >> 32;
4562306a36Sopenharmony_ci	u32 lo = ns & 0xffffffff;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	ptp_qoriq->write(&regs->ctrl_regs->tmr_cnt_l, lo);
4862306a36Sopenharmony_ci	ptp_qoriq->write(&regs->ctrl_regs->tmr_cnt_h, hi);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic u64 tmr_offset_read(struct ptp_qoriq *ptp_qoriq)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	struct ptp_qoriq_registers *regs = &ptp_qoriq->regs;
5462306a36Sopenharmony_ci	u32 lo, hi;
5562306a36Sopenharmony_ci	u64 ns;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	lo = ptp_qoriq->read(&regs->ctrl_regs->tmroff_l);
5862306a36Sopenharmony_ci	hi = ptp_qoriq->read(&regs->ctrl_regs->tmroff_h);
5962306a36Sopenharmony_ci	ns = ((u64) hi) << 32;
6062306a36Sopenharmony_ci	ns |= lo;
6162306a36Sopenharmony_ci	return ns;
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic void tmr_offset_write(struct ptp_qoriq *ptp_qoriq, u64 delta_ns)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	struct ptp_qoriq_registers *regs = &ptp_qoriq->regs;
6762306a36Sopenharmony_ci	u32 lo = delta_ns & 0xffffffff;
6862306a36Sopenharmony_ci	u32 hi = delta_ns >> 32;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	ptp_qoriq->write(&regs->ctrl_regs->tmroff_l, lo);
7162306a36Sopenharmony_ci	ptp_qoriq->write(&regs->ctrl_regs->tmroff_h, hi);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/* Caller must hold ptp_qoriq->lock. */
7562306a36Sopenharmony_cistatic void set_alarm(struct ptp_qoriq *ptp_qoriq)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	struct ptp_qoriq_registers *regs = &ptp_qoriq->regs;
7862306a36Sopenharmony_ci	u64 ns;
7962306a36Sopenharmony_ci	u32 lo, hi;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	ns = tmr_cnt_read(ptp_qoriq) + tmr_offset_read(ptp_qoriq)
8262306a36Sopenharmony_ci				     + 1500000000ULL;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	ns = div_u64(ns, 1000000000UL) * 1000000000ULL;
8562306a36Sopenharmony_ci	ns -= ptp_qoriq->tclk_period;
8662306a36Sopenharmony_ci	hi = ns >> 32;
8762306a36Sopenharmony_ci	lo = ns & 0xffffffff;
8862306a36Sopenharmony_ci	ptp_qoriq->write(&regs->alarm_regs->tmr_alarm1_l, lo);
8962306a36Sopenharmony_ci	ptp_qoriq->write(&regs->alarm_regs->tmr_alarm1_h, hi);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci/* Caller must hold ptp_qoriq->lock. */
9362306a36Sopenharmony_cistatic void set_fipers(struct ptp_qoriq *ptp_qoriq)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	struct ptp_qoriq_registers *regs = &ptp_qoriq->regs;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	set_alarm(ptp_qoriq);
9862306a36Sopenharmony_ci	ptp_qoriq->write(&regs->fiper_regs->tmr_fiper1, ptp_qoriq->tmr_fiper1);
9962306a36Sopenharmony_ci	ptp_qoriq->write(&regs->fiper_regs->tmr_fiper2, ptp_qoriq->tmr_fiper2);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (ptp_qoriq->fiper3_support)
10262306a36Sopenharmony_ci		ptp_qoriq->write(&regs->fiper_regs->tmr_fiper3,
10362306a36Sopenharmony_ci				 ptp_qoriq->tmr_fiper3);
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ciint extts_clean_up(struct ptp_qoriq *ptp_qoriq, int index, bool update_event)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct ptp_qoriq_registers *regs = &ptp_qoriq->regs;
10962306a36Sopenharmony_ci	struct ptp_clock_event event;
11062306a36Sopenharmony_ci	void __iomem *reg_etts_l;
11162306a36Sopenharmony_ci	void __iomem *reg_etts_h;
11262306a36Sopenharmony_ci	u32 valid, lo, hi;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	switch (index) {
11562306a36Sopenharmony_ci	case 0:
11662306a36Sopenharmony_ci		valid = ETS1_VLD;
11762306a36Sopenharmony_ci		reg_etts_l = &regs->etts_regs->tmr_etts1_l;
11862306a36Sopenharmony_ci		reg_etts_h = &regs->etts_regs->tmr_etts1_h;
11962306a36Sopenharmony_ci		break;
12062306a36Sopenharmony_ci	case 1:
12162306a36Sopenharmony_ci		valid = ETS2_VLD;
12262306a36Sopenharmony_ci		reg_etts_l = &regs->etts_regs->tmr_etts2_l;
12362306a36Sopenharmony_ci		reg_etts_h = &regs->etts_regs->tmr_etts2_h;
12462306a36Sopenharmony_ci		break;
12562306a36Sopenharmony_ci	default:
12662306a36Sopenharmony_ci		return -EINVAL;
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	event.type = PTP_CLOCK_EXTTS;
13062306a36Sopenharmony_ci	event.index = index;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	if (ptp_qoriq->extts_fifo_support)
13362306a36Sopenharmony_ci		if (!(ptp_qoriq->read(&regs->ctrl_regs->tmr_stat) & valid))
13462306a36Sopenharmony_ci			return 0;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	do {
13762306a36Sopenharmony_ci		lo = ptp_qoriq->read(reg_etts_l);
13862306a36Sopenharmony_ci		hi = ptp_qoriq->read(reg_etts_h);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci		if (update_event) {
14162306a36Sopenharmony_ci			event.timestamp = ((u64) hi) << 32;
14262306a36Sopenharmony_ci			event.timestamp |= lo;
14362306a36Sopenharmony_ci			ptp_clock_event(ptp_qoriq->clock, &event);
14462306a36Sopenharmony_ci		}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci		if (!ptp_qoriq->extts_fifo_support)
14762306a36Sopenharmony_ci			break;
14862306a36Sopenharmony_ci	} while (ptp_qoriq->read(&regs->ctrl_regs->tmr_stat) & valid);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	return 0;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(extts_clean_up);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci/*
15562306a36Sopenharmony_ci * Interrupt service routine
15662306a36Sopenharmony_ci */
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ciirqreturn_t ptp_qoriq_isr(int irq, void *priv)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	struct ptp_qoriq *ptp_qoriq = priv;
16162306a36Sopenharmony_ci	struct ptp_qoriq_registers *regs = &ptp_qoriq->regs;
16262306a36Sopenharmony_ci	struct ptp_clock_event event;
16362306a36Sopenharmony_ci	u32 ack = 0, mask, val, irqs;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	spin_lock(&ptp_qoriq->lock);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	val = ptp_qoriq->read(&regs->ctrl_regs->tmr_tevent);
16862306a36Sopenharmony_ci	mask = ptp_qoriq->read(&regs->ctrl_regs->tmr_temask);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	spin_unlock(&ptp_qoriq->lock);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	irqs = val & mask;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	if (irqs & ETS1) {
17562306a36Sopenharmony_ci		ack |= ETS1;
17662306a36Sopenharmony_ci		extts_clean_up(ptp_qoriq, 0, true);
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	if (irqs & ETS2) {
18062306a36Sopenharmony_ci		ack |= ETS2;
18162306a36Sopenharmony_ci		extts_clean_up(ptp_qoriq, 1, true);
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (irqs & PP1) {
18562306a36Sopenharmony_ci		ack |= PP1;
18662306a36Sopenharmony_ci		event.type = PTP_CLOCK_PPS;
18762306a36Sopenharmony_ci		ptp_clock_event(ptp_qoriq->clock, &event);
18862306a36Sopenharmony_ci	}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	if (ack) {
19162306a36Sopenharmony_ci		ptp_qoriq->write(&regs->ctrl_regs->tmr_tevent, ack);
19262306a36Sopenharmony_ci		return IRQ_HANDLED;
19362306a36Sopenharmony_ci	} else
19462306a36Sopenharmony_ci		return IRQ_NONE;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ptp_qoriq_isr);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci/*
19962306a36Sopenharmony_ci * PTP clock operations
20062306a36Sopenharmony_ci */
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ciint ptp_qoriq_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	u64 adj, diff;
20562306a36Sopenharmony_ci	u32 tmr_add;
20662306a36Sopenharmony_ci	int neg_adj = 0;
20762306a36Sopenharmony_ci	struct ptp_qoriq *ptp_qoriq = container_of(ptp, struct ptp_qoriq, caps);
20862306a36Sopenharmony_ci	struct ptp_qoriq_registers *regs = &ptp_qoriq->regs;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	if (scaled_ppm < 0) {
21162306a36Sopenharmony_ci		neg_adj = 1;
21262306a36Sopenharmony_ci		scaled_ppm = -scaled_ppm;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci	tmr_add = ptp_qoriq->tmr_add;
21562306a36Sopenharmony_ci	adj = tmr_add;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/*
21862306a36Sopenharmony_ci	 * Calculate diff and round() to the nearest integer
21962306a36Sopenharmony_ci	 *
22062306a36Sopenharmony_ci	 * diff = adj * (ppb / 1000000000)
22162306a36Sopenharmony_ci	 *      = adj * scaled_ppm / 65536000000
22262306a36Sopenharmony_ci	 */
22362306a36Sopenharmony_ci	diff = mul_u64_u64_div_u64(adj, scaled_ppm, 32768000000);
22462306a36Sopenharmony_ci	diff = DIV64_U64_ROUND_UP(diff, 2);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	tmr_add = neg_adj ? tmr_add - diff : tmr_add + diff;
22762306a36Sopenharmony_ci	ptp_qoriq->write(&regs->ctrl_regs->tmr_add, tmr_add);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	return 0;
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ptp_qoriq_adjfine);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ciint ptp_qoriq_adjtime(struct ptp_clock_info *ptp, s64 delta)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	struct ptp_qoriq *ptp_qoriq = container_of(ptp, struct ptp_qoriq, caps);
23662306a36Sopenharmony_ci	s64 now, curr_delta;
23762306a36Sopenharmony_ci	unsigned long flags;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	spin_lock_irqsave(&ptp_qoriq->lock, flags);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/* On LS1021A, eTSEC2 and eTSEC3 do not take into account the TMR_OFF
24262306a36Sopenharmony_ci	 * adjustment
24362306a36Sopenharmony_ci	 */
24462306a36Sopenharmony_ci	if (ptp_qoriq->etsec) {
24562306a36Sopenharmony_ci		now = tmr_cnt_read(ptp_qoriq);
24662306a36Sopenharmony_ci		now += delta;
24762306a36Sopenharmony_ci		tmr_cnt_write(ptp_qoriq, now);
24862306a36Sopenharmony_ci	} else {
24962306a36Sopenharmony_ci		curr_delta = tmr_offset_read(ptp_qoriq);
25062306a36Sopenharmony_ci		curr_delta += delta;
25162306a36Sopenharmony_ci		tmr_offset_write(ptp_qoriq, curr_delta);
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci	set_fipers(ptp_qoriq);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	spin_unlock_irqrestore(&ptp_qoriq->lock, flags);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	return 0;
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ptp_qoriq_adjtime);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ciint ptp_qoriq_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	u64 ns;
26462306a36Sopenharmony_ci	unsigned long flags;
26562306a36Sopenharmony_ci	struct ptp_qoriq *ptp_qoriq = container_of(ptp, struct ptp_qoriq, caps);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	spin_lock_irqsave(&ptp_qoriq->lock, flags);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	ns = tmr_cnt_read(ptp_qoriq) + tmr_offset_read(ptp_qoriq);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	spin_unlock_irqrestore(&ptp_qoriq->lock, flags);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	*ts = ns_to_timespec64(ns);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	return 0;
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ptp_qoriq_gettime);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ciint ptp_qoriq_settime(struct ptp_clock_info *ptp,
28062306a36Sopenharmony_ci		      const struct timespec64 *ts)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	u64 ns;
28362306a36Sopenharmony_ci	unsigned long flags;
28462306a36Sopenharmony_ci	struct ptp_qoriq *ptp_qoriq = container_of(ptp, struct ptp_qoriq, caps);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	ns = timespec64_to_ns(ts);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	spin_lock_irqsave(&ptp_qoriq->lock, flags);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	tmr_offset_write(ptp_qoriq, 0);
29162306a36Sopenharmony_ci	tmr_cnt_write(ptp_qoriq, ns);
29262306a36Sopenharmony_ci	set_fipers(ptp_qoriq);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	spin_unlock_irqrestore(&ptp_qoriq->lock, flags);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	return 0;
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ptp_qoriq_settime);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ciint ptp_qoriq_enable(struct ptp_clock_info *ptp,
30162306a36Sopenharmony_ci		     struct ptp_clock_request *rq, int on)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	struct ptp_qoriq *ptp_qoriq = container_of(ptp, struct ptp_qoriq, caps);
30462306a36Sopenharmony_ci	struct ptp_qoriq_registers *regs = &ptp_qoriq->regs;
30562306a36Sopenharmony_ci	unsigned long flags;
30662306a36Sopenharmony_ci	u32 bit, mask = 0;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	switch (rq->type) {
30962306a36Sopenharmony_ci	case PTP_CLK_REQ_EXTTS:
31062306a36Sopenharmony_ci		switch (rq->extts.index) {
31162306a36Sopenharmony_ci		case 0:
31262306a36Sopenharmony_ci			bit = ETS1EN;
31362306a36Sopenharmony_ci			break;
31462306a36Sopenharmony_ci		case 1:
31562306a36Sopenharmony_ci			bit = ETS2EN;
31662306a36Sopenharmony_ci			break;
31762306a36Sopenharmony_ci		default:
31862306a36Sopenharmony_ci			return -EINVAL;
31962306a36Sopenharmony_ci		}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci		if (on)
32262306a36Sopenharmony_ci			extts_clean_up(ptp_qoriq, rq->extts.index, false);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci		break;
32562306a36Sopenharmony_ci	case PTP_CLK_REQ_PPS:
32662306a36Sopenharmony_ci		bit = PP1EN;
32762306a36Sopenharmony_ci		break;
32862306a36Sopenharmony_ci	default:
32962306a36Sopenharmony_ci		return -EOPNOTSUPP;
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	spin_lock_irqsave(&ptp_qoriq->lock, flags);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	mask = ptp_qoriq->read(&regs->ctrl_regs->tmr_temask);
33562306a36Sopenharmony_ci	if (on) {
33662306a36Sopenharmony_ci		mask |= bit;
33762306a36Sopenharmony_ci		ptp_qoriq->write(&regs->ctrl_regs->tmr_tevent, bit);
33862306a36Sopenharmony_ci	} else {
33962306a36Sopenharmony_ci		mask &= ~bit;
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	ptp_qoriq->write(&regs->ctrl_regs->tmr_temask, mask);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	spin_unlock_irqrestore(&ptp_qoriq->lock, flags);
34562306a36Sopenharmony_ci	return 0;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ptp_qoriq_enable);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic const struct ptp_clock_info ptp_qoriq_caps = {
35062306a36Sopenharmony_ci	.owner		= THIS_MODULE,
35162306a36Sopenharmony_ci	.name		= "qoriq ptp clock",
35262306a36Sopenharmony_ci	.max_adj	= 512000,
35362306a36Sopenharmony_ci	.n_alarm	= 0,
35462306a36Sopenharmony_ci	.n_ext_ts	= N_EXT_TS,
35562306a36Sopenharmony_ci	.n_per_out	= 0,
35662306a36Sopenharmony_ci	.n_pins		= 0,
35762306a36Sopenharmony_ci	.pps		= 1,
35862306a36Sopenharmony_ci	.adjfine	= ptp_qoriq_adjfine,
35962306a36Sopenharmony_ci	.adjtime	= ptp_qoriq_adjtime,
36062306a36Sopenharmony_ci	.gettime64	= ptp_qoriq_gettime,
36162306a36Sopenharmony_ci	.settime64	= ptp_qoriq_settime,
36262306a36Sopenharmony_ci	.enable		= ptp_qoriq_enable,
36362306a36Sopenharmony_ci};
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci/**
36662306a36Sopenharmony_ci * ptp_qoriq_nominal_freq - calculate nominal frequency according to
36762306a36Sopenharmony_ci *			    reference clock frequency
36862306a36Sopenharmony_ci *
36962306a36Sopenharmony_ci * @clk_src: reference clock frequency
37062306a36Sopenharmony_ci *
37162306a36Sopenharmony_ci * The nominal frequency is the desired clock frequency.
37262306a36Sopenharmony_ci * It should be less than the reference clock frequency.
37362306a36Sopenharmony_ci * It should be a factor of 1000MHz.
37462306a36Sopenharmony_ci *
37562306a36Sopenharmony_ci * Return the nominal frequency
37662306a36Sopenharmony_ci */
37762306a36Sopenharmony_cistatic u32 ptp_qoriq_nominal_freq(u32 clk_src)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	u32 remainder = 0;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	clk_src /= 1000000;
38262306a36Sopenharmony_ci	remainder = clk_src % 100;
38362306a36Sopenharmony_ci	if (remainder) {
38462306a36Sopenharmony_ci		clk_src -= remainder;
38562306a36Sopenharmony_ci		clk_src += 100;
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	do {
38962306a36Sopenharmony_ci		clk_src -= 100;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	} while (1000 % clk_src);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	return clk_src * 1000000;
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci/**
39762306a36Sopenharmony_ci * ptp_qoriq_auto_config - calculate a set of default configurations
39862306a36Sopenharmony_ci *
39962306a36Sopenharmony_ci * @ptp_qoriq: pointer to ptp_qoriq
40062306a36Sopenharmony_ci * @node: pointer to device_node
40162306a36Sopenharmony_ci *
40262306a36Sopenharmony_ci * If below dts properties are not provided, this function will be
40362306a36Sopenharmony_ci * called to calculate a set of default configurations for them.
40462306a36Sopenharmony_ci *   "fsl,tclk-period"
40562306a36Sopenharmony_ci *   "fsl,tmr-prsc"
40662306a36Sopenharmony_ci *   "fsl,tmr-add"
40762306a36Sopenharmony_ci *   "fsl,tmr-fiper1"
40862306a36Sopenharmony_ci *   "fsl,tmr-fiper2"
40962306a36Sopenharmony_ci *   "fsl,tmr-fiper3" (required only for DPAA2 and ENETC hardware)
41062306a36Sopenharmony_ci *   "fsl,max-adj"
41162306a36Sopenharmony_ci *
41262306a36Sopenharmony_ci * Return 0 if success
41362306a36Sopenharmony_ci */
41462306a36Sopenharmony_cistatic int ptp_qoriq_auto_config(struct ptp_qoriq *ptp_qoriq,
41562306a36Sopenharmony_ci				 struct device_node *node)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	struct clk *clk;
41862306a36Sopenharmony_ci	u64 freq_comp;
41962306a36Sopenharmony_ci	u64 max_adj;
42062306a36Sopenharmony_ci	u32 nominal_freq;
42162306a36Sopenharmony_ci	u32 remainder = 0;
42262306a36Sopenharmony_ci	u32 clk_src = 0;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	ptp_qoriq->cksel = DEFAULT_CKSEL;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	clk = of_clk_get(node, 0);
42762306a36Sopenharmony_ci	if (!IS_ERR(clk)) {
42862306a36Sopenharmony_ci		clk_src = clk_get_rate(clk);
42962306a36Sopenharmony_ci		clk_put(clk);
43062306a36Sopenharmony_ci	}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	if (clk_src <= 100000000UL) {
43362306a36Sopenharmony_ci		pr_err("error reference clock value, or lower than 100MHz\n");
43462306a36Sopenharmony_ci		return -EINVAL;
43562306a36Sopenharmony_ci	}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	nominal_freq = ptp_qoriq_nominal_freq(clk_src);
43862306a36Sopenharmony_ci	if (!nominal_freq)
43962306a36Sopenharmony_ci		return -EINVAL;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	ptp_qoriq->tclk_period = 1000000000UL / nominal_freq;
44262306a36Sopenharmony_ci	ptp_qoriq->tmr_prsc = DEFAULT_TMR_PRSC;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	/* Calculate initial frequency compensation value for TMR_ADD register.
44562306a36Sopenharmony_ci	 * freq_comp = ceil(2^32 / freq_ratio)
44662306a36Sopenharmony_ci	 * freq_ratio = reference_clock_freq / nominal_freq
44762306a36Sopenharmony_ci	 */
44862306a36Sopenharmony_ci	freq_comp = ((u64)1 << 32) * nominal_freq;
44962306a36Sopenharmony_ci	freq_comp = div_u64_rem(freq_comp, clk_src, &remainder);
45062306a36Sopenharmony_ci	if (remainder)
45162306a36Sopenharmony_ci		freq_comp++;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	ptp_qoriq->tmr_add = freq_comp;
45462306a36Sopenharmony_ci	ptp_qoriq->tmr_fiper1 = DEFAULT_FIPER1_PERIOD - ptp_qoriq->tclk_period;
45562306a36Sopenharmony_ci	ptp_qoriq->tmr_fiper2 = DEFAULT_FIPER2_PERIOD - ptp_qoriq->tclk_period;
45662306a36Sopenharmony_ci	ptp_qoriq->tmr_fiper3 = DEFAULT_FIPER3_PERIOD - ptp_qoriq->tclk_period;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	/* max_adj = 1000000000 * (freq_ratio - 1.0) - 1
45962306a36Sopenharmony_ci	 * freq_ratio = reference_clock_freq / nominal_freq
46062306a36Sopenharmony_ci	 */
46162306a36Sopenharmony_ci	max_adj = 1000000000ULL * (clk_src - nominal_freq);
46262306a36Sopenharmony_ci	max_adj = div_u64(max_adj, nominal_freq) - 1;
46362306a36Sopenharmony_ci	ptp_qoriq->caps.max_adj = max_adj;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	return 0;
46662306a36Sopenharmony_ci}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ciint ptp_qoriq_init(struct ptp_qoriq *ptp_qoriq, void __iomem *base,
46962306a36Sopenharmony_ci		   const struct ptp_clock_info *caps)
47062306a36Sopenharmony_ci{
47162306a36Sopenharmony_ci	struct device_node *node = ptp_qoriq->dev->of_node;
47262306a36Sopenharmony_ci	struct ptp_qoriq_registers *regs;
47362306a36Sopenharmony_ci	struct timespec64 now;
47462306a36Sopenharmony_ci	unsigned long flags;
47562306a36Sopenharmony_ci	u32 tmr_ctrl;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	if (!node)
47862306a36Sopenharmony_ci		return -ENODEV;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	ptp_qoriq->base = base;
48162306a36Sopenharmony_ci	ptp_qoriq->caps = *caps;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	if (of_property_read_u32(node, "fsl,cksel", &ptp_qoriq->cksel))
48462306a36Sopenharmony_ci		ptp_qoriq->cksel = DEFAULT_CKSEL;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	if (of_property_read_bool(node, "fsl,extts-fifo"))
48762306a36Sopenharmony_ci		ptp_qoriq->extts_fifo_support = true;
48862306a36Sopenharmony_ci	else
48962306a36Sopenharmony_ci		ptp_qoriq->extts_fifo_support = false;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	if (of_device_is_compatible(node, "fsl,dpaa2-ptp") ||
49262306a36Sopenharmony_ci	    of_device_is_compatible(node, "fsl,enetc-ptp"))
49362306a36Sopenharmony_ci		ptp_qoriq->fiper3_support = true;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	if (of_property_read_u32(node,
49662306a36Sopenharmony_ci				 "fsl,tclk-period", &ptp_qoriq->tclk_period) ||
49762306a36Sopenharmony_ci	    of_property_read_u32(node,
49862306a36Sopenharmony_ci				 "fsl,tmr-prsc", &ptp_qoriq->tmr_prsc) ||
49962306a36Sopenharmony_ci	    of_property_read_u32(node,
50062306a36Sopenharmony_ci				 "fsl,tmr-add", &ptp_qoriq->tmr_add) ||
50162306a36Sopenharmony_ci	    of_property_read_u32(node,
50262306a36Sopenharmony_ci				 "fsl,tmr-fiper1", &ptp_qoriq->tmr_fiper1) ||
50362306a36Sopenharmony_ci	    of_property_read_u32(node,
50462306a36Sopenharmony_ci				 "fsl,tmr-fiper2", &ptp_qoriq->tmr_fiper2) ||
50562306a36Sopenharmony_ci	    of_property_read_u32(node,
50662306a36Sopenharmony_ci				 "fsl,max-adj", &ptp_qoriq->caps.max_adj) ||
50762306a36Sopenharmony_ci	    (ptp_qoriq->fiper3_support &&
50862306a36Sopenharmony_ci	     of_property_read_u32(node, "fsl,tmr-fiper3",
50962306a36Sopenharmony_ci				  &ptp_qoriq->tmr_fiper3))) {
51062306a36Sopenharmony_ci		pr_warn("device tree node missing required elements, try automatic configuration\n");
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci		if (ptp_qoriq_auto_config(ptp_qoriq, node))
51362306a36Sopenharmony_ci			return -ENODEV;
51462306a36Sopenharmony_ci	}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	if (of_property_read_bool(node, "little-endian")) {
51762306a36Sopenharmony_ci		ptp_qoriq->read = qoriq_read_le;
51862306a36Sopenharmony_ci		ptp_qoriq->write = qoriq_write_le;
51962306a36Sopenharmony_ci	} else {
52062306a36Sopenharmony_ci		ptp_qoriq->read = qoriq_read_be;
52162306a36Sopenharmony_ci		ptp_qoriq->write = qoriq_write_be;
52262306a36Sopenharmony_ci	}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	/* The eTSEC uses differnt memory map with DPAA/ENETC */
52562306a36Sopenharmony_ci	if (of_device_is_compatible(node, "fsl,etsec-ptp")) {
52662306a36Sopenharmony_ci		ptp_qoriq->etsec = true;
52762306a36Sopenharmony_ci		ptp_qoriq->regs.ctrl_regs = base + ETSEC_CTRL_REGS_OFFSET;
52862306a36Sopenharmony_ci		ptp_qoriq->regs.alarm_regs = base + ETSEC_ALARM_REGS_OFFSET;
52962306a36Sopenharmony_ci		ptp_qoriq->regs.fiper_regs = base + ETSEC_FIPER_REGS_OFFSET;
53062306a36Sopenharmony_ci		ptp_qoriq->regs.etts_regs = base + ETSEC_ETTS_REGS_OFFSET;
53162306a36Sopenharmony_ci	} else {
53262306a36Sopenharmony_ci		ptp_qoriq->regs.ctrl_regs = base + CTRL_REGS_OFFSET;
53362306a36Sopenharmony_ci		ptp_qoriq->regs.alarm_regs = base + ALARM_REGS_OFFSET;
53462306a36Sopenharmony_ci		ptp_qoriq->regs.fiper_regs = base + FIPER_REGS_OFFSET;
53562306a36Sopenharmony_ci		ptp_qoriq->regs.etts_regs = base + ETTS_REGS_OFFSET;
53662306a36Sopenharmony_ci	}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	spin_lock_init(&ptp_qoriq->lock);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	ktime_get_real_ts64(&now);
54162306a36Sopenharmony_ci	ptp_qoriq_settime(&ptp_qoriq->caps, &now);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	tmr_ctrl =
54462306a36Sopenharmony_ci	  (ptp_qoriq->tclk_period & TCLK_PERIOD_MASK) << TCLK_PERIOD_SHIFT |
54562306a36Sopenharmony_ci	  (ptp_qoriq->cksel & CKSEL_MASK) << CKSEL_SHIFT;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	spin_lock_irqsave(&ptp_qoriq->lock, flags);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	regs = &ptp_qoriq->regs;
55062306a36Sopenharmony_ci	ptp_qoriq->write(&regs->ctrl_regs->tmr_ctrl, tmr_ctrl);
55162306a36Sopenharmony_ci	ptp_qoriq->write(&regs->ctrl_regs->tmr_add, ptp_qoriq->tmr_add);
55262306a36Sopenharmony_ci	ptp_qoriq->write(&regs->ctrl_regs->tmr_prsc, ptp_qoriq->tmr_prsc);
55362306a36Sopenharmony_ci	ptp_qoriq->write(&regs->fiper_regs->tmr_fiper1, ptp_qoriq->tmr_fiper1);
55462306a36Sopenharmony_ci	ptp_qoriq->write(&regs->fiper_regs->tmr_fiper2, ptp_qoriq->tmr_fiper2);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	if (ptp_qoriq->fiper3_support)
55762306a36Sopenharmony_ci		ptp_qoriq->write(&regs->fiper_regs->tmr_fiper3,
55862306a36Sopenharmony_ci				 ptp_qoriq->tmr_fiper3);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	set_alarm(ptp_qoriq);
56162306a36Sopenharmony_ci	ptp_qoriq->write(&regs->ctrl_regs->tmr_ctrl,
56262306a36Sopenharmony_ci			 tmr_ctrl|FIPERST|RTPE|TE|FRD);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	spin_unlock_irqrestore(&ptp_qoriq->lock, flags);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	ptp_qoriq->clock = ptp_clock_register(&ptp_qoriq->caps, ptp_qoriq->dev);
56762306a36Sopenharmony_ci	if (IS_ERR(ptp_qoriq->clock))
56862306a36Sopenharmony_ci		return PTR_ERR(ptp_qoriq->clock);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	ptp_qoriq->phc_index = ptp_clock_index(ptp_qoriq->clock);
57162306a36Sopenharmony_ci	ptp_qoriq_create_debugfs(ptp_qoriq);
57262306a36Sopenharmony_ci	return 0;
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ptp_qoriq_init);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_civoid ptp_qoriq_free(struct ptp_qoriq *ptp_qoriq)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	struct ptp_qoriq_registers *regs = &ptp_qoriq->regs;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	ptp_qoriq->write(&regs->ctrl_regs->tmr_temask, 0);
58162306a36Sopenharmony_ci	ptp_qoriq->write(&regs->ctrl_regs->tmr_ctrl,   0);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	ptp_qoriq_remove_debugfs(ptp_qoriq);
58462306a36Sopenharmony_ci	ptp_clock_unregister(ptp_qoriq->clock);
58562306a36Sopenharmony_ci	iounmap(ptp_qoriq->base);
58662306a36Sopenharmony_ci	free_irq(ptp_qoriq->irq, ptp_qoriq);
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ptp_qoriq_free);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_cistatic int ptp_qoriq_probe(struct platform_device *dev)
59162306a36Sopenharmony_ci{
59262306a36Sopenharmony_ci	struct ptp_qoriq *ptp_qoriq;
59362306a36Sopenharmony_ci	int err = -ENOMEM;
59462306a36Sopenharmony_ci	void __iomem *base;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	ptp_qoriq = kzalloc(sizeof(*ptp_qoriq), GFP_KERNEL);
59762306a36Sopenharmony_ci	if (!ptp_qoriq)
59862306a36Sopenharmony_ci		goto no_memory;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	ptp_qoriq->dev = &dev->dev;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	err = -ENODEV;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	ptp_qoriq->irq = platform_get_irq(dev, 0);
60562306a36Sopenharmony_ci	if (ptp_qoriq->irq < 0) {
60662306a36Sopenharmony_ci		pr_err("irq not in device tree\n");
60762306a36Sopenharmony_ci		goto no_node;
60862306a36Sopenharmony_ci	}
60962306a36Sopenharmony_ci	if (request_irq(ptp_qoriq->irq, ptp_qoriq_isr, IRQF_SHARED,
61062306a36Sopenharmony_ci			DRIVER, ptp_qoriq)) {
61162306a36Sopenharmony_ci		pr_err("request_irq failed\n");
61262306a36Sopenharmony_ci		goto no_node;
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	ptp_qoriq->rsrc = platform_get_resource(dev, IORESOURCE_MEM, 0);
61662306a36Sopenharmony_ci	if (!ptp_qoriq->rsrc) {
61762306a36Sopenharmony_ci		pr_err("no resource\n");
61862306a36Sopenharmony_ci		goto no_resource;
61962306a36Sopenharmony_ci	}
62062306a36Sopenharmony_ci	if (request_resource(&iomem_resource, ptp_qoriq->rsrc)) {
62162306a36Sopenharmony_ci		pr_err("resource busy\n");
62262306a36Sopenharmony_ci		goto no_resource;
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	base = ioremap(ptp_qoriq->rsrc->start,
62662306a36Sopenharmony_ci		       resource_size(ptp_qoriq->rsrc));
62762306a36Sopenharmony_ci	if (!base) {
62862306a36Sopenharmony_ci		pr_err("ioremap ptp registers failed\n");
62962306a36Sopenharmony_ci		goto no_ioremap;
63062306a36Sopenharmony_ci	}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	err = ptp_qoriq_init(ptp_qoriq, base, &ptp_qoriq_caps);
63362306a36Sopenharmony_ci	if (err)
63462306a36Sopenharmony_ci		goto no_clock;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	platform_set_drvdata(dev, ptp_qoriq);
63762306a36Sopenharmony_ci	return 0;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_cino_clock:
64062306a36Sopenharmony_ci	iounmap(base);
64162306a36Sopenharmony_cino_ioremap:
64262306a36Sopenharmony_ci	release_resource(ptp_qoriq->rsrc);
64362306a36Sopenharmony_cino_resource:
64462306a36Sopenharmony_ci	free_irq(ptp_qoriq->irq, ptp_qoriq);
64562306a36Sopenharmony_cino_node:
64662306a36Sopenharmony_ci	kfree(ptp_qoriq);
64762306a36Sopenharmony_cino_memory:
64862306a36Sopenharmony_ci	return err;
64962306a36Sopenharmony_ci}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_cistatic int ptp_qoriq_remove(struct platform_device *dev)
65262306a36Sopenharmony_ci{
65362306a36Sopenharmony_ci	struct ptp_qoriq *ptp_qoriq = platform_get_drvdata(dev);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	ptp_qoriq_free(ptp_qoriq);
65662306a36Sopenharmony_ci	release_resource(ptp_qoriq->rsrc);
65762306a36Sopenharmony_ci	kfree(ptp_qoriq);
65862306a36Sopenharmony_ci	return 0;
65962306a36Sopenharmony_ci}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_cistatic const struct of_device_id match_table[] = {
66262306a36Sopenharmony_ci	{ .compatible = "fsl,etsec-ptp" },
66362306a36Sopenharmony_ci	{ .compatible = "fsl,fman-ptp-timer" },
66462306a36Sopenharmony_ci	{},
66562306a36Sopenharmony_ci};
66662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, match_table);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_cistatic struct platform_driver ptp_qoriq_driver = {
66962306a36Sopenharmony_ci	.driver = {
67062306a36Sopenharmony_ci		.name		= "ptp_qoriq",
67162306a36Sopenharmony_ci		.of_match_table	= match_table,
67262306a36Sopenharmony_ci	},
67362306a36Sopenharmony_ci	.probe       = ptp_qoriq_probe,
67462306a36Sopenharmony_ci	.remove      = ptp_qoriq_remove,
67562306a36Sopenharmony_ci};
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_cimodule_platform_driver(ptp_qoriq_driver);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ciMODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>");
68062306a36Sopenharmony_ciMODULE_DESCRIPTION("PTP clock for Freescale QorIQ 1588 timer");
68162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
682