xref: /linux/drivers/net/ethernet/xscale/ptp_ixp46x.c (revision 319d77e9)
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 
ixp_systime_read(struct ixp46x_ts_regs * regs)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 
ixp_systime_write(struct ixp46x_ts_regs * regs,u64 ns)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 
isr(int irq,void * priv)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 
ptp_ixp_adjfine(struct ptp_clock_info * ptp,long scaled_ppm)123*319d77e9SJacob Keller static int ptp_ixp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
1242785543fSArnd Bergmann {
125*319d77e9SJacob Keller 	u32 addend;
1262785543fSArnd Bergmann 	struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
1272785543fSArnd Bergmann 	struct ixp46x_ts_regs *regs = ixp_clock->regs;
1282785543fSArnd Bergmann 
129*319d77e9SJacob Keller 	addend = adjust_by_scaled_ppm(DEFAULT_ADDEND, scaled_ppm);
1302785543fSArnd Bergmann 
1312785543fSArnd Bergmann 	__raw_writel(addend, &regs->addend);
1322785543fSArnd Bergmann 
1332785543fSArnd Bergmann 	return 0;
1342785543fSArnd Bergmann }
1352785543fSArnd Bergmann 
ptp_ixp_adjtime(struct ptp_clock_info * ptp,s64 delta)1362785543fSArnd Bergmann static int ptp_ixp_adjtime(struct ptp_clock_info *ptp, s64 delta)
1372785543fSArnd Bergmann {
1382785543fSArnd Bergmann 	s64 now;
1392785543fSArnd Bergmann 	unsigned long flags;
1402785543fSArnd Bergmann 	struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
1412785543fSArnd Bergmann 	struct ixp46x_ts_regs *regs = ixp_clock->regs;
1422785543fSArnd Bergmann 
1432785543fSArnd Bergmann 	spin_lock_irqsave(&register_lock, flags);
1442785543fSArnd Bergmann 
1452785543fSArnd Bergmann 	now = ixp_systime_read(regs);
1462785543fSArnd Bergmann 	now += delta;
1472785543fSArnd Bergmann 	ixp_systime_write(regs, now);
1482785543fSArnd Bergmann 
1492785543fSArnd Bergmann 	spin_unlock_irqrestore(&register_lock, flags);
1502785543fSArnd Bergmann 
1512785543fSArnd Bergmann 	return 0;
1522785543fSArnd Bergmann }
1532785543fSArnd Bergmann 
ptp_ixp_gettime(struct ptp_clock_info * ptp,struct timespec64 * ts)1542785543fSArnd Bergmann static int ptp_ixp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
1552785543fSArnd Bergmann {
1562785543fSArnd Bergmann 	u64 ns;
1572785543fSArnd Bergmann 	unsigned long flags;
1582785543fSArnd Bergmann 	struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
1592785543fSArnd Bergmann 	struct ixp46x_ts_regs *regs = ixp_clock->regs;
1602785543fSArnd Bergmann 
1612785543fSArnd Bergmann 	spin_lock_irqsave(&register_lock, flags);
1622785543fSArnd Bergmann 
1632785543fSArnd Bergmann 	ns = ixp_systime_read(regs);
1642785543fSArnd Bergmann 
1652785543fSArnd Bergmann 	spin_unlock_irqrestore(&register_lock, flags);
1662785543fSArnd Bergmann 
1672785543fSArnd Bergmann 	*ts = ns_to_timespec64(ns);
1682785543fSArnd Bergmann 	return 0;
1692785543fSArnd Bergmann }
1702785543fSArnd Bergmann 
ptp_ixp_settime(struct ptp_clock_info * ptp,const struct timespec64 * ts)1712785543fSArnd Bergmann static int ptp_ixp_settime(struct ptp_clock_info *ptp,
1722785543fSArnd Bergmann 			   const struct timespec64 *ts)
1732785543fSArnd Bergmann {
1742785543fSArnd Bergmann 	u64 ns;
1752785543fSArnd Bergmann 	unsigned long flags;
1762785543fSArnd Bergmann 	struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
1772785543fSArnd Bergmann 	struct ixp46x_ts_regs *regs = ixp_clock->regs;
1782785543fSArnd Bergmann 
1792785543fSArnd Bergmann 	ns = timespec64_to_ns(ts);
1802785543fSArnd Bergmann 
1812785543fSArnd Bergmann 	spin_lock_irqsave(&register_lock, flags);
1822785543fSArnd Bergmann 
1832785543fSArnd Bergmann 	ixp_systime_write(regs, ns);
1842785543fSArnd Bergmann 
1852785543fSArnd Bergmann 	spin_unlock_irqrestore(&register_lock, flags);
1862785543fSArnd Bergmann 
1872785543fSArnd Bergmann 	return 0;
1882785543fSArnd Bergmann }
1892785543fSArnd Bergmann 
ptp_ixp_enable(struct ptp_clock_info * ptp,struct ptp_clock_request * rq,int on)1902785543fSArnd Bergmann static int ptp_ixp_enable(struct ptp_clock_info *ptp,
1912785543fSArnd Bergmann 			  struct ptp_clock_request *rq, int on)
1922785543fSArnd Bergmann {
1932785543fSArnd Bergmann 	struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);
1942785543fSArnd Bergmann 
1952785543fSArnd Bergmann 	switch (rq->type) {
1962785543fSArnd Bergmann 	case PTP_CLK_REQ_EXTTS:
1972785543fSArnd Bergmann 		switch (rq->extts.index) {
1982785543fSArnd Bergmann 		case 0:
1992785543fSArnd Bergmann 			ixp_clock->exts0_enabled = on ? 1 : 0;
2002785543fSArnd Bergmann 			break;
2012785543fSArnd Bergmann 		case 1:
2022785543fSArnd Bergmann 			ixp_clock->exts1_enabled = on ? 1 : 0;
2032785543fSArnd Bergmann 			break;
2042785543fSArnd Bergmann 		default:
2052785543fSArnd Bergmann 			return -EINVAL;
2062785543fSArnd Bergmann 		}
2072785543fSArnd Bergmann 		return 0;
2082785543fSArnd Bergmann 	default:
2092785543fSArnd Bergmann 		break;
2102785543fSArnd Bergmann 	}
2112785543fSArnd Bergmann 
2122785543fSArnd Bergmann 	return -EOPNOTSUPP;
2132785543fSArnd Bergmann }
2142785543fSArnd Bergmann 
2152785543fSArnd Bergmann static const struct ptp_clock_info ptp_ixp_caps = {
2162785543fSArnd Bergmann 	.owner		= THIS_MODULE,
2172785543fSArnd Bergmann 	.name		= "IXP46X timer",
2182785543fSArnd Bergmann 	.max_adj	= 66666655,
2192785543fSArnd Bergmann 	.n_ext_ts	= N_EXT_TS,
2202785543fSArnd Bergmann 	.n_pins		= 0,
2212785543fSArnd Bergmann 	.pps		= 0,
222*319d77e9SJacob Keller 	.adjfine	= ptp_ixp_adjfine,
2232785543fSArnd Bergmann 	.adjtime	= ptp_ixp_adjtime,
2242785543fSArnd Bergmann 	.gettime64	= ptp_ixp_gettime,
2252785543fSArnd Bergmann 	.settime64	= ptp_ixp_settime,
2262785543fSArnd Bergmann 	.enable		= ptp_ixp_enable,
2272785543fSArnd Bergmann };
2282785543fSArnd Bergmann 
2292785543fSArnd Bergmann /* module operations */
2302785543fSArnd Bergmann 
2312785543fSArnd Bergmann static struct ixp_clock ixp_clock;
2322785543fSArnd Bergmann 
ixp46x_ptp_find(struct ixp46x_ts_regs * __iomem * regs,int * phc_index)2339055a2f5SArnd Bergmann int ixp46x_ptp_find(struct ixp46x_ts_regs *__iomem *regs, int *phc_index)
2342785543fSArnd Bergmann {
2359055a2f5SArnd Bergmann 	*regs = ixp_clock.regs;
2369055a2f5SArnd Bergmann 	*phc_index = ptp_clock_index(ixp_clock.ptp_clock);
2379055a2f5SArnd Bergmann 
2389055a2f5SArnd Bergmann 	if (!ixp_clock.ptp_clock)
2399055a2f5SArnd Bergmann 		return -EPROBE_DEFER;
2409055a2f5SArnd Bergmann 
2419055a2f5SArnd Bergmann 	return 0;
2429055a2f5SArnd Bergmann }
2439055a2f5SArnd Bergmann EXPORT_SYMBOL_GPL(ixp46x_ptp_find);
2449055a2f5SArnd Bergmann 
24513dc9319SLinus Walleij /* Called from the registered devm action */
ptp_ixp_unregister_action(void * d)24613dc9319SLinus Walleij static void ptp_ixp_unregister_action(void *d)
2479055a2f5SArnd Bergmann {
24813dc9319SLinus Walleij 	struct ptp_clock *ptp_clock = d;
2499055a2f5SArnd Bergmann 
25013dc9319SLinus Walleij 	ptp_clock_unregister(ptp_clock);
25113dc9319SLinus Walleij 	ixp_clock.ptp_clock = NULL;
2522785543fSArnd Bergmann }
2532785543fSArnd Bergmann 
ptp_ixp_probe(struct platform_device * pdev)2549055a2f5SArnd Bergmann static int ptp_ixp_probe(struct platform_device *pdev)
2552785543fSArnd Bergmann {
25613dc9319SLinus Walleij 	struct device *dev = &pdev->dev;
25713dc9319SLinus Walleij 	int ret;
25813dc9319SLinus Walleij 
2599055a2f5SArnd Bergmann 	ixp_clock.regs = devm_platform_ioremap_resource(pdev, 0);
2609055a2f5SArnd Bergmann 	ixp_clock.master_irq = platform_get_irq(pdev, 0);
2619055a2f5SArnd Bergmann 	ixp_clock.slave_irq = platform_get_irq(pdev, 1);
2629055a2f5SArnd Bergmann 	if (IS_ERR(ixp_clock.regs) ||
263f45ba67eSLv Ruyi 	    ixp_clock.master_irq < 0 || ixp_clock.slave_irq < 0)
2649055a2f5SArnd Bergmann 		return -ENXIO;
2652785543fSArnd Bergmann 
2662785543fSArnd Bergmann 	ixp_clock.caps = ptp_ixp_caps;
2672785543fSArnd Bergmann 
2682785543fSArnd Bergmann 	ixp_clock.ptp_clock = ptp_clock_register(&ixp_clock.caps, NULL);
2692785543fSArnd Bergmann 
2702785543fSArnd Bergmann 	if (IS_ERR(ixp_clock.ptp_clock))
2712785543fSArnd Bergmann 		return PTR_ERR(ixp_clock.ptp_clock);
2722785543fSArnd Bergmann 
27313dc9319SLinus Walleij 	ret = devm_add_action_or_reset(dev, ptp_ixp_unregister_action,
27413dc9319SLinus Walleij 				       ixp_clock.ptp_clock);
27513dc9319SLinus Walleij 	if (ret) {
27613dc9319SLinus Walleij 		dev_err(dev, "failed to install clock removal handler\n");
27713dc9319SLinus Walleij 		return ret;
27813dc9319SLinus Walleij 	}
27913dc9319SLinus Walleij 
2802785543fSArnd Bergmann 	__raw_writel(DEFAULT_ADDEND, &ixp_clock.regs->addend);
2812785543fSArnd Bergmann 	__raw_writel(1, &ixp_clock.regs->trgt_lo);
2822785543fSArnd Bergmann 	__raw_writel(0, &ixp_clock.regs->trgt_hi);
2832785543fSArnd Bergmann 	__raw_writel(TTIPEND, &ixp_clock.regs->event);
2842785543fSArnd Bergmann 
28513dc9319SLinus Walleij 	ret = devm_request_irq(dev, ixp_clock.master_irq, isr,
28613dc9319SLinus Walleij 			       0, DRIVER, &ixp_clock);
28713dc9319SLinus Walleij 	if (ret)
28813dc9319SLinus Walleij 		return dev_err_probe(dev, ret,
28913dc9319SLinus Walleij 				     "request_irq failed for irq %d\n",
29013dc9319SLinus Walleij 				     ixp_clock.master_irq);
29113dc9319SLinus Walleij 
29213dc9319SLinus Walleij 	ret = devm_request_irq(dev, ixp_clock.slave_irq, isr,
29313dc9319SLinus Walleij 			       0, DRIVER, &ixp_clock);
29413dc9319SLinus Walleij 	if (ret)
29513dc9319SLinus Walleij 		return dev_err_probe(dev, ret,
29613dc9319SLinus Walleij 				     "request_irq failed for irq %d\n",
29713dc9319SLinus Walleij 				     ixp_clock.slave_irq);
2982785543fSArnd Bergmann 
2992785543fSArnd Bergmann 	return 0;
3002785543fSArnd Bergmann }
3012785543fSArnd Bergmann 
302e9e50622SLinus Walleij static const struct of_device_id ptp_ixp_match[] = {
303e9e50622SLinus Walleij 	{
304e9e50622SLinus Walleij 		.compatible = "intel,ixp46x-ptp-timer",
305e9e50622SLinus Walleij 	},
306e9e50622SLinus Walleij 	{ },
307e9e50622SLinus Walleij };
308e9e50622SLinus Walleij 
3099055a2f5SArnd Bergmann static struct platform_driver ptp_ixp_driver = {
310e9e50622SLinus Walleij 	.driver = {
311e9e50622SLinus Walleij 		.name = "ptp-ixp46x",
312e9e50622SLinus Walleij 		.of_match_table = ptp_ixp_match,
313e9e50622SLinus Walleij 		.suppress_bind_attrs = true,
314e9e50622SLinus Walleij 	},
3159055a2f5SArnd Bergmann 	.probe = ptp_ixp_probe,
3169055a2f5SArnd Bergmann };
3179055a2f5SArnd Bergmann module_platform_driver(ptp_ixp_driver);
3182785543fSArnd Bergmann 
3192785543fSArnd Bergmann MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>");
3202785543fSArnd Bergmann MODULE_DESCRIPTION("PTP clock using the IXP46X timer");
3212785543fSArnd Bergmann MODULE_LICENSE("GPL");
322