xref: /linux/drivers/net/ethernet/xscale/ptp_ixp46x.c (revision f45ba67e)
12785543fSArnd Bergmann // SPDX-License-Identifier: GPL-2.0-or-later
22785543fSArnd Bergmann /*
32785543fSArnd Bergmann  * PTP 1588 clock using the IXP46X
42785543fSArnd Bergmann  *
52785543fSArnd Bergmann  * Copyright (C) 2010 OMICRON electronics GmbH
62785543fSArnd Bergmann  */
72785543fSArnd Bergmann #include <linux/device.h>
89055a2f5SArnd Bergmann #include <linux/module.h>
9e9e50622SLinus Walleij #include <linux/mod_devicetable.h>
102785543fSArnd Bergmann #include <linux/err.h>
112785543fSArnd Bergmann #include <linux/init.h>
122785543fSArnd Bergmann #include <linux/interrupt.h>
132785543fSArnd Bergmann #include <linux/io.h>
142785543fSArnd Bergmann #include <linux/irq.h>
152785543fSArnd Bergmann #include <linux/kernel.h>
162785543fSArnd Bergmann #include <linux/ptp_clock_kernel.h>
179055a2f5SArnd Bergmann #include <linux/platform_device.h>
1809aa9aabSArnd Bergmann #include <linux/soc/ixp4xx/cpu.h>
192785543fSArnd Bergmann 
202785543fSArnd Bergmann #include "ixp46x_ts.h"
212785543fSArnd Bergmann 
222785543fSArnd Bergmann #define DRIVER		"ptp_ixp46x"
232785543fSArnd Bergmann #define N_EXT_TS	2
242785543fSArnd Bergmann 
252785543fSArnd Bergmann struct ixp_clock {
262785543fSArnd Bergmann 	struct ixp46x_ts_regs *regs;
272785543fSArnd Bergmann 	struct ptp_clock *ptp_clock;
282785543fSArnd Bergmann 	struct ptp_clock_info caps;
292785543fSArnd Bergmann 	int exts0_enabled;
302785543fSArnd Bergmann 	int exts1_enabled;
319055a2f5SArnd Bergmann 	int slave_irq;
329055a2f5SArnd Bergmann 	int master_irq;
332785543fSArnd Bergmann };
342785543fSArnd Bergmann 
359055a2f5SArnd Bergmann static DEFINE_SPINLOCK(register_lock);
362785543fSArnd Bergmann 
372785543fSArnd Bergmann /*
382785543fSArnd Bergmann  * Register access functions
392785543fSArnd Bergmann  */
402785543fSArnd Bergmann 
412785543fSArnd Bergmann static u64 ixp_systime_read(struct ixp46x_ts_regs *regs)
422785543fSArnd Bergmann {
432785543fSArnd Bergmann 	u64 ns;
442785543fSArnd Bergmann 	u32 lo, hi;
452785543fSArnd Bergmann 
462785543fSArnd Bergmann 	lo = __raw_readl(&regs->systime_lo);
472785543fSArnd Bergmann 	hi = __raw_readl(&regs->systime_hi);
482785543fSArnd Bergmann 
492785543fSArnd Bergmann 	ns = ((u64) hi) << 32;
502785543fSArnd Bergmann 	ns |= lo;
512785543fSArnd Bergmann 	ns <<= TICKS_NS_SHIFT;
522785543fSArnd Bergmann 
532785543fSArnd Bergmann 	return ns;
542785543fSArnd Bergmann }
552785543fSArnd Bergmann 
562785543fSArnd Bergmann static void ixp_systime_write(struct ixp46x_ts_regs *regs, u64 ns)
572785543fSArnd Bergmann {
582785543fSArnd Bergmann 	u32 hi, lo;
592785543fSArnd Bergmann 
602785543fSArnd Bergmann 	ns >>= TICKS_NS_SHIFT;
612785543fSArnd Bergmann 	hi = ns >> 32;
622785543fSArnd Bergmann 	lo = ns & 0xffffffff;
632785543fSArnd Bergmann 
642785543fSArnd Bergmann 	__raw_writel(lo, &regs->systime_lo);
652785543fSArnd Bergmann 	__raw_writel(hi, &regs->systime_hi);
662785543fSArnd Bergmann }
672785543fSArnd Bergmann 
682785543fSArnd Bergmann /*
692785543fSArnd Bergmann  * Interrupt service routine
702785543fSArnd Bergmann  */
712785543fSArnd Bergmann 
722785543fSArnd Bergmann static irqreturn_t isr(int irq, void *priv)
732785543fSArnd Bergmann {
742785543fSArnd Bergmann 	struct ixp_clock *ixp_clock = priv;
752785543fSArnd Bergmann 	struct ixp46x_ts_regs *regs = ixp_clock->regs;
762785543fSArnd Bergmann 	struct ptp_clock_event event;
772785543fSArnd Bergmann 	u32 ack = 0, lo, hi, val;
782785543fSArnd Bergmann 
792785543fSArnd Bergmann 	val = __raw_readl(&regs->event);
802785543fSArnd Bergmann 
812785543fSArnd Bergmann 	if (val & TSER_SNS) {
822785543fSArnd Bergmann 		ack |= TSER_SNS;
832785543fSArnd Bergmann 		if (ixp_clock->exts0_enabled) {
842785543fSArnd Bergmann 			hi = __raw_readl(&regs->asms_hi);
852785543fSArnd Bergmann 			lo = __raw_readl(&regs->asms_lo);
862785543fSArnd Bergmann 			event.type = PTP_CLOCK_EXTTS;
872785543fSArnd Bergmann 			event.index = 0;
882785543fSArnd Bergmann 			event.timestamp = ((u64) hi) << 32;
892785543fSArnd Bergmann 			event.timestamp |= lo;
902785543fSArnd Bergmann 			event.timestamp <<= TICKS_NS_SHIFT;
912785543fSArnd Bergmann 			ptp_clock_event(ixp_clock->ptp_clock, &event);
922785543fSArnd Bergmann 		}
932785543fSArnd Bergmann 	}
942785543fSArnd Bergmann 
952785543fSArnd Bergmann 	if (val & TSER_SNM) {
962785543fSArnd Bergmann 		ack |= TSER_SNM;
972785543fSArnd Bergmann 		if (ixp_clock->exts1_enabled) {
982785543fSArnd Bergmann 			hi = __raw_readl(&regs->amms_hi);
992785543fSArnd Bergmann 			lo = __raw_readl(&regs->amms_lo);
1002785543fSArnd Bergmann 			event.type = PTP_CLOCK_EXTTS;
1012785543fSArnd Bergmann 			event.index = 1;
1022785543fSArnd Bergmann 			event.timestamp = ((u64) hi) << 32;
1032785543fSArnd Bergmann 			event.timestamp |= lo;
1042785543fSArnd Bergmann 			event.timestamp <<= TICKS_NS_SHIFT;
1052785543fSArnd Bergmann 			ptp_clock_event(ixp_clock->ptp_clock, &event);
1062785543fSArnd Bergmann 		}
1072785543fSArnd Bergmann 	}
1082785543fSArnd Bergmann 
1092785543fSArnd Bergmann 	if (val & TTIPEND)
1102785543fSArnd Bergmann 		ack |= TTIPEND; /* this bit seems to be always set */
1112785543fSArnd Bergmann 
1122785543fSArnd Bergmann 	if (ack) {
1132785543fSArnd Bergmann 		__raw_writel(ack, &regs->event);
1142785543fSArnd Bergmann 		return IRQ_HANDLED;
1152785543fSArnd Bergmann 	} else
1162785543fSArnd Bergmann 		return IRQ_NONE;
1172785543fSArnd Bergmann }
1182785543fSArnd Bergmann 
1192785543fSArnd Bergmann /*
1202785543fSArnd Bergmann  * PTP clock operations
1212785543fSArnd Bergmann  */
1222785543fSArnd Bergmann 
1232785543fSArnd Bergmann static int ptp_ixp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
1242785543fSArnd Bergmann {
1252785543fSArnd Bergmann 	u64 adj;
1262785543fSArnd Bergmann 	u32 diff, addend;
1272785543fSArnd Bergmann 	int neg_adj = 0;
1282785543fSArnd Bergmann 	struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
1292785543fSArnd Bergmann 	struct ixp46x_ts_regs *regs = ixp_clock->regs;
1302785543fSArnd Bergmann 
1312785543fSArnd Bergmann 	if (ppb < 0) {
1322785543fSArnd Bergmann 		neg_adj = 1;
1332785543fSArnd Bergmann 		ppb = -ppb;
1342785543fSArnd Bergmann 	}
1352785543fSArnd Bergmann 	addend = DEFAULT_ADDEND;
1362785543fSArnd Bergmann 	adj = addend;
1372785543fSArnd Bergmann 	adj *= ppb;
1382785543fSArnd Bergmann 	diff = div_u64(adj, 1000000000ULL);
1392785543fSArnd Bergmann 
1402785543fSArnd Bergmann 	addend = neg_adj ? addend - diff : addend + diff;
1412785543fSArnd Bergmann 
1422785543fSArnd Bergmann 	__raw_writel(addend, &regs->addend);
1432785543fSArnd Bergmann 
1442785543fSArnd Bergmann 	return 0;
1452785543fSArnd Bergmann }
1462785543fSArnd Bergmann 
1472785543fSArnd Bergmann static int ptp_ixp_adjtime(struct ptp_clock_info *ptp, s64 delta)
1482785543fSArnd Bergmann {
1492785543fSArnd Bergmann 	s64 now;
1502785543fSArnd Bergmann 	unsigned long flags;
1512785543fSArnd Bergmann 	struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
1522785543fSArnd Bergmann 	struct ixp46x_ts_regs *regs = ixp_clock->regs;
1532785543fSArnd Bergmann 
1542785543fSArnd Bergmann 	spin_lock_irqsave(&register_lock, flags);
1552785543fSArnd Bergmann 
1562785543fSArnd Bergmann 	now = ixp_systime_read(regs);
1572785543fSArnd Bergmann 	now += delta;
1582785543fSArnd Bergmann 	ixp_systime_write(regs, now);
1592785543fSArnd Bergmann 
1602785543fSArnd Bergmann 	spin_unlock_irqrestore(&register_lock, flags);
1612785543fSArnd Bergmann 
1622785543fSArnd Bergmann 	return 0;
1632785543fSArnd Bergmann }
1642785543fSArnd Bergmann 
1652785543fSArnd Bergmann static int ptp_ixp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
1662785543fSArnd Bergmann {
1672785543fSArnd Bergmann 	u64 ns;
1682785543fSArnd Bergmann 	unsigned long flags;
1692785543fSArnd Bergmann 	struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
1702785543fSArnd Bergmann 	struct ixp46x_ts_regs *regs = ixp_clock->regs;
1712785543fSArnd Bergmann 
1722785543fSArnd Bergmann 	spin_lock_irqsave(&register_lock, flags);
1732785543fSArnd Bergmann 
1742785543fSArnd Bergmann 	ns = ixp_systime_read(regs);
1752785543fSArnd Bergmann 
1762785543fSArnd Bergmann 	spin_unlock_irqrestore(&register_lock, flags);
1772785543fSArnd Bergmann 
1782785543fSArnd Bergmann 	*ts = ns_to_timespec64(ns);
1792785543fSArnd Bergmann 	return 0;
1802785543fSArnd Bergmann }
1812785543fSArnd Bergmann 
1822785543fSArnd Bergmann static int ptp_ixp_settime(struct ptp_clock_info *ptp,
1832785543fSArnd Bergmann 			   const struct timespec64 *ts)
1842785543fSArnd Bergmann {
1852785543fSArnd Bergmann 	u64 ns;
1862785543fSArnd Bergmann 	unsigned long flags;
1872785543fSArnd Bergmann 	struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
1882785543fSArnd Bergmann 	struct ixp46x_ts_regs *regs = ixp_clock->regs;
1892785543fSArnd Bergmann 
1902785543fSArnd Bergmann 	ns = timespec64_to_ns(ts);
1912785543fSArnd Bergmann 
1922785543fSArnd Bergmann 	spin_lock_irqsave(&register_lock, flags);
1932785543fSArnd Bergmann 
1942785543fSArnd Bergmann 	ixp_systime_write(regs, ns);
1952785543fSArnd Bergmann 
1962785543fSArnd Bergmann 	spin_unlock_irqrestore(&register_lock, flags);
1972785543fSArnd Bergmann 
1982785543fSArnd Bergmann 	return 0;
1992785543fSArnd Bergmann }
2002785543fSArnd Bergmann 
2012785543fSArnd Bergmann static int ptp_ixp_enable(struct ptp_clock_info *ptp,
2022785543fSArnd Bergmann 			  struct ptp_clock_request *rq, int on)
2032785543fSArnd Bergmann {
2042785543fSArnd Bergmann 	struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
2052785543fSArnd Bergmann 
2062785543fSArnd Bergmann 	switch (rq->type) {
2072785543fSArnd Bergmann 	case PTP_CLK_REQ_EXTTS:
2082785543fSArnd Bergmann 		switch (rq->extts.index) {
2092785543fSArnd Bergmann 		case 0:
2102785543fSArnd Bergmann 			ixp_clock->exts0_enabled = on ? 1 : 0;
2112785543fSArnd Bergmann 			break;
2122785543fSArnd Bergmann 		case 1:
2132785543fSArnd Bergmann 			ixp_clock->exts1_enabled = on ? 1 : 0;
2142785543fSArnd Bergmann 			break;
2152785543fSArnd Bergmann 		default:
2162785543fSArnd Bergmann 			return -EINVAL;
2172785543fSArnd Bergmann 		}
2182785543fSArnd Bergmann 		return 0;
2192785543fSArnd Bergmann 	default:
2202785543fSArnd Bergmann 		break;
2212785543fSArnd Bergmann 	}
2222785543fSArnd Bergmann 
2232785543fSArnd Bergmann 	return -EOPNOTSUPP;
2242785543fSArnd Bergmann }
2252785543fSArnd Bergmann 
2262785543fSArnd Bergmann static const struct ptp_clock_info ptp_ixp_caps = {
2272785543fSArnd Bergmann 	.owner		= THIS_MODULE,
2282785543fSArnd Bergmann 	.name		= "IXP46X timer",
2292785543fSArnd Bergmann 	.max_adj	= 66666655,
2302785543fSArnd Bergmann 	.n_ext_ts	= N_EXT_TS,
2312785543fSArnd Bergmann 	.n_pins		= 0,
2322785543fSArnd Bergmann 	.pps		= 0,
2332785543fSArnd Bergmann 	.adjfreq	= ptp_ixp_adjfreq,
2342785543fSArnd Bergmann 	.adjtime	= ptp_ixp_adjtime,
2352785543fSArnd Bergmann 	.gettime64	= ptp_ixp_gettime,
2362785543fSArnd Bergmann 	.settime64	= ptp_ixp_settime,
2372785543fSArnd Bergmann 	.enable		= ptp_ixp_enable,
2382785543fSArnd Bergmann };
2392785543fSArnd Bergmann 
2402785543fSArnd Bergmann /* module operations */
2412785543fSArnd Bergmann 
2422785543fSArnd Bergmann static struct ixp_clock ixp_clock;
2432785543fSArnd Bergmann 
2449055a2f5SArnd Bergmann int ixp46x_ptp_find(struct ixp46x_ts_regs *__iomem *regs, int *phc_index)
2452785543fSArnd Bergmann {
2469055a2f5SArnd Bergmann 	*regs = ixp_clock.regs;
2479055a2f5SArnd Bergmann 	*phc_index = ptp_clock_index(ixp_clock.ptp_clock);
2489055a2f5SArnd Bergmann 
2499055a2f5SArnd Bergmann 	if (!ixp_clock.ptp_clock)
2509055a2f5SArnd Bergmann 		return -EPROBE_DEFER;
2519055a2f5SArnd Bergmann 
2529055a2f5SArnd Bergmann 	return 0;
2539055a2f5SArnd Bergmann }
2549055a2f5SArnd Bergmann EXPORT_SYMBOL_GPL(ixp46x_ptp_find);
2559055a2f5SArnd Bergmann 
25613dc9319SLinus Walleij /* Called from the registered devm action */
25713dc9319SLinus Walleij static void ptp_ixp_unregister_action(void *d)
2589055a2f5SArnd Bergmann {
25913dc9319SLinus Walleij 	struct ptp_clock *ptp_clock = d;
2609055a2f5SArnd Bergmann 
26113dc9319SLinus Walleij 	ptp_clock_unregister(ptp_clock);
26213dc9319SLinus Walleij 	ixp_clock.ptp_clock = NULL;
2632785543fSArnd Bergmann }
2642785543fSArnd Bergmann 
2659055a2f5SArnd Bergmann static int ptp_ixp_probe(struct platform_device *pdev)
2662785543fSArnd Bergmann {
26713dc9319SLinus Walleij 	struct device *dev = &pdev->dev;
26813dc9319SLinus Walleij 	int ret;
26913dc9319SLinus Walleij 
2709055a2f5SArnd Bergmann 	ixp_clock.regs = devm_platform_ioremap_resource(pdev, 0);
2719055a2f5SArnd Bergmann 	ixp_clock.master_irq = platform_get_irq(pdev, 0);
2729055a2f5SArnd Bergmann 	ixp_clock.slave_irq = platform_get_irq(pdev, 1);
2739055a2f5SArnd Bergmann 	if (IS_ERR(ixp_clock.regs) ||
274*f45ba67eSLv Ruyi 	    ixp_clock.master_irq < 0 || ixp_clock.slave_irq < 0)
2759055a2f5SArnd Bergmann 		return -ENXIO;
2762785543fSArnd Bergmann 
2772785543fSArnd Bergmann 	ixp_clock.caps = ptp_ixp_caps;
2782785543fSArnd Bergmann 
2792785543fSArnd Bergmann 	ixp_clock.ptp_clock = ptp_clock_register(&ixp_clock.caps, NULL);
2802785543fSArnd Bergmann 
2812785543fSArnd Bergmann 	if (IS_ERR(ixp_clock.ptp_clock))
2822785543fSArnd Bergmann 		return PTR_ERR(ixp_clock.ptp_clock);
2832785543fSArnd Bergmann 
28413dc9319SLinus Walleij 	ret = devm_add_action_or_reset(dev, ptp_ixp_unregister_action,
28513dc9319SLinus Walleij 				       ixp_clock.ptp_clock);
28613dc9319SLinus Walleij 	if (ret) {
28713dc9319SLinus Walleij 		dev_err(dev, "failed to install clock removal handler\n");
28813dc9319SLinus Walleij 		return ret;
28913dc9319SLinus Walleij 	}
29013dc9319SLinus Walleij 
2912785543fSArnd Bergmann 	__raw_writel(DEFAULT_ADDEND, &ixp_clock.regs->addend);
2922785543fSArnd Bergmann 	__raw_writel(1, &ixp_clock.regs->trgt_lo);
2932785543fSArnd Bergmann 	__raw_writel(0, &ixp_clock.regs->trgt_hi);
2942785543fSArnd Bergmann 	__raw_writel(TTIPEND, &ixp_clock.regs->event);
2952785543fSArnd Bergmann 
29613dc9319SLinus Walleij 	ret = devm_request_irq(dev, ixp_clock.master_irq, isr,
29713dc9319SLinus Walleij 			       0, DRIVER, &ixp_clock);
29813dc9319SLinus Walleij 	if (ret)
29913dc9319SLinus Walleij 		return dev_err_probe(dev, ret,
30013dc9319SLinus Walleij 				     "request_irq failed for irq %d\n",
30113dc9319SLinus Walleij 				     ixp_clock.master_irq);
30213dc9319SLinus Walleij 
30313dc9319SLinus Walleij 	ret = devm_request_irq(dev, ixp_clock.slave_irq, isr,
30413dc9319SLinus Walleij 			       0, DRIVER, &ixp_clock);
30513dc9319SLinus Walleij 	if (ret)
30613dc9319SLinus Walleij 		return dev_err_probe(dev, ret,
30713dc9319SLinus Walleij 				     "request_irq failed for irq %d\n",
30813dc9319SLinus Walleij 				     ixp_clock.slave_irq);
3092785543fSArnd Bergmann 
3102785543fSArnd Bergmann 	return 0;
3112785543fSArnd Bergmann }
3122785543fSArnd Bergmann 
313e9e50622SLinus Walleij static const struct of_device_id ptp_ixp_match[] = {
314e9e50622SLinus Walleij 	{
315e9e50622SLinus Walleij 		.compatible = "intel,ixp46x-ptp-timer",
316e9e50622SLinus Walleij 	},
317e9e50622SLinus Walleij 	{ },
318e9e50622SLinus Walleij };
319e9e50622SLinus Walleij 
3209055a2f5SArnd Bergmann static struct platform_driver ptp_ixp_driver = {
321e9e50622SLinus Walleij 	.driver = {
322e9e50622SLinus Walleij 		.name = "ptp-ixp46x",
323e9e50622SLinus Walleij 		.of_match_table = ptp_ixp_match,
324e9e50622SLinus Walleij 		.suppress_bind_attrs = true,
325e9e50622SLinus Walleij 	},
3269055a2f5SArnd Bergmann 	.probe = ptp_ixp_probe,
3279055a2f5SArnd Bergmann };
3289055a2f5SArnd Bergmann module_platform_driver(ptp_ixp_driver);
3292785543fSArnd Bergmann 
3302785543fSArnd Bergmann MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>");
3312785543fSArnd Bergmann MODULE_DESCRIPTION("PTP clock using the IXP46X timer");
3322785543fSArnd Bergmann MODULE_LICENSE("GPL");
333