brintos

brintos / linux-shallow public Read only

0
0
Text · 7.0 KiB · 94203eb Raw
322 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * PTP 1588 clock using the IXP46X4 *5 * Copyright (C) 2010 OMICRON electronics GmbH6 */7#include <linux/device.h>8#include <linux/module.h>9#include <linux/mod_devicetable.h>10#include <linux/err.h>11#include <linux/init.h>12#include <linux/interrupt.h>13#include <linux/io.h>14#include <linux/irq.h>15#include <linux/kernel.h>16#include <linux/ptp_clock_kernel.h>17#include <linux/platform_device.h>18#include <linux/soc/ixp4xx/cpu.h>19 20#include "ixp46x_ts.h"21 22#define DRIVER		"ptp_ixp46x"23#define N_EXT_TS	224 25struct ixp_clock {26	struct ixp46x_ts_regs *regs;27	struct ptp_clock *ptp_clock;28	struct ptp_clock_info caps;29	int exts0_enabled;30	int exts1_enabled;31	int slave_irq;32	int master_irq;33};34 35static DEFINE_SPINLOCK(register_lock);36 37/*38 * Register access functions39 */40 41static u64 ixp_systime_read(struct ixp46x_ts_regs *regs)42{43	u64 ns;44	u32 lo, hi;45 46	lo = __raw_readl(&regs->systime_lo);47	hi = __raw_readl(&regs->systime_hi);48 49	ns = ((u64) hi) << 32;50	ns |= lo;51	ns <<= TICKS_NS_SHIFT;52 53	return ns;54}55 56static void ixp_systime_write(struct ixp46x_ts_regs *regs, u64 ns)57{58	u32 hi, lo;59 60	ns >>= TICKS_NS_SHIFT;61	hi = ns >> 32;62	lo = ns & 0xffffffff;63 64	__raw_writel(lo, &regs->systime_lo);65	__raw_writel(hi, &regs->systime_hi);66}67 68/*69 * Interrupt service routine70 */71 72static irqreturn_t isr(int irq, void *priv)73{74	struct ixp_clock *ixp_clock = priv;75	struct ixp46x_ts_regs *regs = ixp_clock->regs;76	struct ptp_clock_event event;77	u32 ack = 0, lo, hi, val;78 79	val = __raw_readl(&regs->event);80 81	if (val & TSER_SNS) {82		ack |= TSER_SNS;83		if (ixp_clock->exts0_enabled) {84			hi = __raw_readl(&regs->asms_hi);85			lo = __raw_readl(&regs->asms_lo);86			event.type = PTP_CLOCK_EXTTS;87			event.index = 0;88			event.timestamp = ((u64) hi) << 32;89			event.timestamp |= lo;90			event.timestamp <<= TICKS_NS_SHIFT;91			ptp_clock_event(ixp_clock->ptp_clock, &event);92		}93	}94 95	if (val & TSER_SNM) {96		ack |= TSER_SNM;97		if (ixp_clock->exts1_enabled) {98			hi = __raw_readl(&regs->amms_hi);99			lo = __raw_readl(&regs->amms_lo);100			event.type = PTP_CLOCK_EXTTS;101			event.index = 1;102			event.timestamp = ((u64) hi) << 32;103			event.timestamp |= lo;104			event.timestamp <<= TICKS_NS_SHIFT;105			ptp_clock_event(ixp_clock->ptp_clock, &event);106		}107	}108 109	if (val & TTIPEND)110		ack |= TTIPEND; /* this bit seems to be always set */111 112	if (ack) {113		__raw_writel(ack, &regs->event);114		return IRQ_HANDLED;115	} else116		return IRQ_NONE;117}118 119/*120 * PTP clock operations121 */122 123static int ptp_ixp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)124{125	u32 addend;126	struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);127	struct ixp46x_ts_regs *regs = ixp_clock->regs;128 129	addend = adjust_by_scaled_ppm(DEFAULT_ADDEND, scaled_ppm);130 131	__raw_writel(addend, &regs->addend);132 133	return 0;134}135 136static int ptp_ixp_adjtime(struct ptp_clock_info *ptp, s64 delta)137{138	s64 now;139	unsigned long flags;140	struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);141	struct ixp46x_ts_regs *regs = ixp_clock->regs;142 143	spin_lock_irqsave(&register_lock, flags);144 145	now = ixp_systime_read(regs);146	now += delta;147	ixp_systime_write(regs, now);148 149	spin_unlock_irqrestore(&register_lock, flags);150 151	return 0;152}153 154static int ptp_ixp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)155{156	u64 ns;157	unsigned long flags;158	struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);159	struct ixp46x_ts_regs *regs = ixp_clock->regs;160 161	spin_lock_irqsave(&register_lock, flags);162 163	ns = ixp_systime_read(regs);164 165	spin_unlock_irqrestore(&register_lock, flags);166 167	*ts = ns_to_timespec64(ns);168	return 0;169}170 171static int ptp_ixp_settime(struct ptp_clock_info *ptp,172			   const struct timespec64 *ts)173{174	u64 ns;175	unsigned long flags;176	struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);177	struct ixp46x_ts_regs *regs = ixp_clock->regs;178 179	ns = timespec64_to_ns(ts);180 181	spin_lock_irqsave(&register_lock, flags);182 183	ixp_systime_write(regs, ns);184 185	spin_unlock_irqrestore(&register_lock, flags);186 187	return 0;188}189 190static int ptp_ixp_enable(struct ptp_clock_info *ptp,191			  struct ptp_clock_request *rq, int on)192{193	struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps);194 195	switch (rq->type) {196	case PTP_CLK_REQ_EXTTS:197		switch (rq->extts.index) {198		case 0:199			ixp_clock->exts0_enabled = on ? 1 : 0;200			break;201		case 1:202			ixp_clock->exts1_enabled = on ? 1 : 0;203			break;204		default:205			return -EINVAL;206		}207		return 0;208	default:209		break;210	}211 212	return -EOPNOTSUPP;213}214 215static const struct ptp_clock_info ptp_ixp_caps = {216	.owner		= THIS_MODULE,217	.name		= "IXP46X timer",218	.max_adj	= 66666655,219	.n_ext_ts	= N_EXT_TS,220	.n_pins		= 0,221	.pps		= 0,222	.adjfine	= ptp_ixp_adjfine,223	.adjtime	= ptp_ixp_adjtime,224	.gettime64	= ptp_ixp_gettime,225	.settime64	= ptp_ixp_settime,226	.enable		= ptp_ixp_enable,227};228 229/* module operations */230 231static struct ixp_clock ixp_clock;232 233int ixp46x_ptp_find(struct ixp46x_ts_regs *__iomem *regs, int *phc_index)234{235	*regs = ixp_clock.regs;236	*phc_index = ptp_clock_index(ixp_clock.ptp_clock);237 238	if (!ixp_clock.ptp_clock)239		return -EPROBE_DEFER;240 241	return 0;242}243EXPORT_SYMBOL_GPL(ixp46x_ptp_find);244 245/* Called from the registered devm action */246static void ptp_ixp_unregister_action(void *d)247{248	struct ptp_clock *ptp_clock = d;249 250	ptp_clock_unregister(ptp_clock);251	ixp_clock.ptp_clock = NULL;252}253 254static int ptp_ixp_probe(struct platform_device *pdev)255{256	struct device *dev = &pdev->dev;257	int ret;258 259	ixp_clock.regs = devm_platform_ioremap_resource(pdev, 0);260	ixp_clock.master_irq = platform_get_irq(pdev, 0);261	ixp_clock.slave_irq = platform_get_irq(pdev, 1);262	if (IS_ERR(ixp_clock.regs) ||263	    ixp_clock.master_irq < 0 || ixp_clock.slave_irq < 0)264		return -ENXIO;265 266	ixp_clock.caps = ptp_ixp_caps;267 268	ixp_clock.ptp_clock = ptp_clock_register(&ixp_clock.caps, NULL);269 270	if (IS_ERR(ixp_clock.ptp_clock))271		return PTR_ERR(ixp_clock.ptp_clock);272 273	ret = devm_add_action_or_reset(dev, ptp_ixp_unregister_action,274				       ixp_clock.ptp_clock);275	if (ret) {276		dev_err(dev, "failed to install clock removal handler\n");277		return ret;278	}279 280	__raw_writel(DEFAULT_ADDEND, &ixp_clock.regs->addend);281	__raw_writel(1, &ixp_clock.regs->trgt_lo);282	__raw_writel(0, &ixp_clock.regs->trgt_hi);283	__raw_writel(TTIPEND, &ixp_clock.regs->event);284 285	ret = devm_request_irq(dev, ixp_clock.master_irq, isr,286			       0, DRIVER, &ixp_clock);287	if (ret)288		return dev_err_probe(dev, ret,289				     "request_irq failed for irq %d\n",290				     ixp_clock.master_irq);291 292	ret = devm_request_irq(dev, ixp_clock.slave_irq, isr,293			       0, DRIVER, &ixp_clock);294	if (ret)295		return dev_err_probe(dev, ret,296				     "request_irq failed for irq %d\n",297				     ixp_clock.slave_irq);298 299	return 0;300}301 302static const struct of_device_id ptp_ixp_match[] = {303	{304		.compatible = "intel,ixp46x-ptp-timer",305	},306	{ },307};308 309static struct platform_driver ptp_ixp_driver = {310	.driver = {311		.name = "ptp-ixp46x",312		.of_match_table = ptp_ixp_match,313		.suppress_bind_attrs = true,314	},315	.probe = ptp_ixp_probe,316};317module_platform_driver(ptp_ixp_driver);318 319MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>");320MODULE_DESCRIPTION("PTP clock using the IXP46X timer");321MODULE_LICENSE("GPL");322