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(®s->systime_lo);47 hi = __raw_readl(®s->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, ®s->systime_lo);65 __raw_writel(hi, ®s->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(®s->event);80 81 if (val & TSER_SNS) {82 ack |= TSER_SNS;83 if (ixp_clock->exts0_enabled) {84 hi = __raw_readl(®s->asms_hi);85 lo = __raw_readl(®s->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(®s->amms_hi);99 lo = __raw_readl(®s->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, ®s->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, ®s->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(®ister_lock, flags);144 145 now = ixp_systime_read(regs);146 now += delta;147 ixp_systime_write(regs, now);148 149 spin_unlock_irqrestore(®ister_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(®ister_lock, flags);162 163 ns = ixp_systime_read(regs);164 165 spin_unlock_irqrestore(®ister_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(®ister_lock, flags);182 183 ixp_systime_write(regs, ns);184 185 spin_unlock_irqrestore(®ister_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