diff options
author | Biwen Li <biwen.li@nxp.com> | 2019-05-06 12:13:14 +0800 |
---|---|---|
committer | Petr Štetiar <ynezz@true.cz> | 2019-06-06 15:40:09 +0200 |
commit | 5159d71983e649a89568e46d9ff02731beedd571 (patch) | |
tree | 2c669f4d9651c1fe26955778e5fee119543a85ce /target/linux/layerscape/patches-4.14/824-ptp-support-layerscape.patch | |
parent | 639d127b831a2af29a03ab07b262abf46ada3b4e (diff) | |
download | upstream-5159d71983e649a89568e46d9ff02731beedd571.tar.gz upstream-5159d71983e649a89568e46d9ff02731beedd571.tar.bz2 upstream-5159d71983e649a89568e46d9ff02731beedd571.zip |
layerscape: update patches-4.14 to LSDK 19.03
All patches of LSDK 19.03 were ported to Openwrt kernel.
We still used an all-in-one patch for each IP/feature for
OpenWrt.
Below are the changes this patch introduced.
- Updated original IP/feature patches to LSDK 19.03.
- Added new IP/feature patches for eTSEC/PTP/TMU.
- Squashed scattered patches into IP/feature patches.
- Updated config-4.14 correspondingly.
- Refreshed all patches.
More info about LSDK and the kernel:
- https://lsdk.github.io/components.html
- https://source.codeaurora.org/external/qoriq/qoriq-components/linux
Signed-off-by: Biwen Li <biwen.li@nxp.com>
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
Diffstat (limited to 'target/linux/layerscape/patches-4.14/824-ptp-support-layerscape.patch')
-rw-r--r-- | target/linux/layerscape/patches-4.14/824-ptp-support-layerscape.patch | 1399 |
1 files changed, 1399 insertions, 0 deletions
diff --git a/target/linux/layerscape/patches-4.14/824-ptp-support-layerscape.patch b/target/linux/layerscape/patches-4.14/824-ptp-support-layerscape.patch new file mode 100644 index 0000000000..5664e18094 --- /dev/null +++ b/target/linux/layerscape/patches-4.14/824-ptp-support-layerscape.patch @@ -0,0 +1,1399 @@ +From bba7af6efb0aad1d52ee5e7d80f9e2ab59d85e20 Mon Sep 17 00:00:00 2001 +From: Biwen Li <biwen.li@nxp.com> +Date: Wed, 17 Apr 2019 18:58:52 +0800 +Subject: [PATCH] ptp: support layerscape + +This is an integrated patch of ptp for layerscape + +Signed-off-by: Arnd Bergmann <arnd@arndb.de> +Signed-off-by: Biwen Li <biwen.li@nxp.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com> +--- + drivers/net/ethernet/freescale/Makefile | 1 - + drivers/net/ethernet/freescale/gianfar_ptp.c | 572 ------------------ + drivers/ptp/Makefile | 1 + + drivers/ptp/ptp_chardev.c | 4 +- + drivers/ptp/ptp_qoriq.c | 589 +++++++++++++++++++ + include/linux/fsl/ptp_qoriq.h | 169 ++++++ + 6 files changed, 761 insertions(+), 575 deletions(-) + delete mode 100644 drivers/net/ethernet/freescale/gianfar_ptp.c + create mode 100644 drivers/ptp/ptp_qoriq.c + create mode 100644 include/linux/fsl/ptp_qoriq.h + +--- a/drivers/net/ethernet/freescale/Makefile ++++ b/drivers/net/ethernet/freescale/Makefile +@@ -14,7 +14,6 @@ obj-$(CONFIG_FS_ENET) += fs_enet/ + obj-$(CONFIG_FSL_PQ_MDIO) += fsl_pq_mdio.o + obj-$(CONFIG_FSL_XGMAC_MDIO) += xgmac_mdio.o + obj-$(CONFIG_GIANFAR) += gianfar_driver.o +-obj-$(CONFIG_PTP_1588_CLOCK_GIANFAR) += gianfar_ptp.o + gianfar_driver-objs := gianfar.o \ + gianfar_ethtool.o + obj-$(CONFIG_UCC_GETH) += ucc_geth_driver.o +--- a/drivers/net/ethernet/freescale/gianfar_ptp.c ++++ /dev/null +@@ -1,572 +0,0 @@ +-/* +- * PTP 1588 clock using the eTSEC +- * +- * Copyright (C) 2010 OMICRON electronics GmbH +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +- +-#include <linux/device.h> +-#include <linux/hrtimer.h> +-#include <linux/interrupt.h> +-#include <linux/kernel.h> +-#include <linux/module.h> +-#include <linux/of.h> +-#include <linux/of_platform.h> +-#include <linux/timex.h> +-#include <linux/io.h> +- +-#include <linux/ptp_clock_kernel.h> +- +-#include "gianfar.h" +- +-/* +- * gianfar ptp registers +- * Generated by regen.tcl on Thu May 13 01:38:57 PM CEST 2010 +- */ +-struct gianfar_ptp_registers { +- u32 tmr_ctrl; /* Timer control register */ +- u32 tmr_tevent; /* Timestamp event register */ +- u32 tmr_temask; /* Timer event mask register */ +- u32 tmr_pevent; /* Timestamp event register */ +- u32 tmr_pemask; /* Timer event mask register */ +- u32 tmr_stat; /* Timestamp status register */ +- u32 tmr_cnt_h; /* Timer counter high register */ +- u32 tmr_cnt_l; /* Timer counter low register */ +- u32 tmr_add; /* Timer drift compensation addend register */ +- u32 tmr_acc; /* Timer accumulator register */ +- u32 tmr_prsc; /* Timer prescale */ +- u8 res1[4]; +- u32 tmroff_h; /* Timer offset high */ +- u32 tmroff_l; /* Timer offset low */ +- u8 res2[8]; +- u32 tmr_alarm1_h; /* Timer alarm 1 high register */ +- u32 tmr_alarm1_l; /* Timer alarm 1 high register */ +- u32 tmr_alarm2_h; /* Timer alarm 2 high register */ +- u32 tmr_alarm2_l; /* Timer alarm 2 high register */ +- u8 res3[48]; +- u32 tmr_fiper1; /* Timer fixed period interval */ +- u32 tmr_fiper2; /* Timer fixed period interval */ +- u32 tmr_fiper3; /* Timer fixed period interval */ +- u8 res4[20]; +- u32 tmr_etts1_h; /* Timestamp of general purpose external trigger */ +- u32 tmr_etts1_l; /* Timestamp of general purpose external trigger */ +- u32 tmr_etts2_h; /* Timestamp of general purpose external trigger */ +- u32 tmr_etts2_l; /* Timestamp of general purpose external trigger */ +-}; +- +-/* Bit definitions for the TMR_CTRL register */ +-#define ALM1P (1<<31) /* Alarm1 output polarity */ +-#define ALM2P (1<<30) /* Alarm2 output polarity */ +-#define FIPERST (1<<28) /* FIPER start indication */ +-#define PP1L (1<<27) /* Fiper1 pulse loopback mode enabled. */ +-#define PP2L (1<<26) /* Fiper2 pulse loopback mode enabled. */ +-#define TCLK_PERIOD_SHIFT (16) /* 1588 timer reference clock period. */ +-#define TCLK_PERIOD_MASK (0x3ff) +-#define RTPE (1<<15) /* Record Tx Timestamp to PAL Enable. */ +-#define FRD (1<<14) /* FIPER Realignment Disable */ +-#define ESFDP (1<<11) /* External Tx/Rx SFD Polarity. */ +-#define ESFDE (1<<10) /* External Tx/Rx SFD Enable. */ +-#define ETEP2 (1<<9) /* External trigger 2 edge polarity */ +-#define ETEP1 (1<<8) /* External trigger 1 edge polarity */ +-#define COPH (1<<7) /* Generated clock output phase. */ +-#define CIPH (1<<6) /* External oscillator input clock phase */ +-#define TMSR (1<<5) /* Timer soft reset. */ +-#define BYP (1<<3) /* Bypass drift compensated clock */ +-#define TE (1<<2) /* 1588 timer enable. */ +-#define CKSEL_SHIFT (0) /* 1588 Timer reference clock source */ +-#define CKSEL_MASK (0x3) +- +-/* Bit definitions for the TMR_TEVENT register */ +-#define ETS2 (1<<25) /* External trigger 2 timestamp sampled */ +-#define ETS1 (1<<24) /* External trigger 1 timestamp sampled */ +-#define ALM2 (1<<17) /* Current time = alarm time register 2 */ +-#define ALM1 (1<<16) /* Current time = alarm time register 1 */ +-#define PP1 (1<<7) /* periodic pulse generated on FIPER1 */ +-#define PP2 (1<<6) /* periodic pulse generated on FIPER2 */ +-#define PP3 (1<<5) /* periodic pulse generated on FIPER3 */ +- +-/* Bit definitions for the TMR_TEMASK register */ +-#define ETS2EN (1<<25) /* External trigger 2 timestamp enable */ +-#define ETS1EN (1<<24) /* External trigger 1 timestamp enable */ +-#define ALM2EN (1<<17) /* Timer ALM2 event enable */ +-#define ALM1EN (1<<16) /* Timer ALM1 event enable */ +-#define PP1EN (1<<7) /* Periodic pulse event 1 enable */ +-#define PP2EN (1<<6) /* Periodic pulse event 2 enable */ +- +-/* Bit definitions for the TMR_PEVENT register */ +-#define TXP2 (1<<9) /* PTP transmitted timestamp im TXTS2 */ +-#define TXP1 (1<<8) /* PTP transmitted timestamp in TXTS1 */ +-#define RXP (1<<0) /* PTP frame has been received */ +- +-/* Bit definitions for the TMR_PEMASK register */ +-#define TXP2EN (1<<9) /* Transmit PTP packet event 2 enable */ +-#define TXP1EN (1<<8) /* Transmit PTP packet event 1 enable */ +-#define RXPEN (1<<0) /* Receive PTP packet event enable */ +- +-/* Bit definitions for the TMR_STAT register */ +-#define STAT_VEC_SHIFT (0) /* Timer general purpose status vector */ +-#define STAT_VEC_MASK (0x3f) +- +-/* Bit definitions for the TMR_PRSC register */ +-#define PRSC_OCK_SHIFT (0) /* Output clock division/prescale factor. */ +-#define PRSC_OCK_MASK (0xffff) +- +- +-#define DRIVER "gianfar_ptp" +-#define DEFAULT_CKSEL 1 +-#define N_EXT_TS 2 +-#define REG_SIZE sizeof(struct gianfar_ptp_registers) +- +-struct etsects { +- struct gianfar_ptp_registers __iomem *regs; +- spinlock_t lock; /* protects regs */ +- struct ptp_clock *clock; +- struct ptp_clock_info caps; +- struct resource *rsrc; +- int irq; +- u64 alarm_interval; /* for periodic alarm */ +- u64 alarm_value; +- u32 tclk_period; /* nanoseconds */ +- u32 tmr_prsc; +- u32 tmr_add; +- u32 cksel; +- u32 tmr_fiper1; +- u32 tmr_fiper2; +-}; +- +-/* +- * Register access functions +- */ +- +-/* Caller must hold etsects->lock. */ +-static u64 tmr_cnt_read(struct etsects *etsects) +-{ +- u64 ns; +- u32 lo, hi; +- +- lo = gfar_read(&etsects->regs->tmr_cnt_l); +- hi = gfar_read(&etsects->regs->tmr_cnt_h); +- ns = ((u64) hi) << 32; +- ns |= lo; +- return ns; +-} +- +-/* Caller must hold etsects->lock. */ +-static void tmr_cnt_write(struct etsects *etsects, u64 ns) +-{ +- u32 hi = ns >> 32; +- u32 lo = ns & 0xffffffff; +- +- gfar_write(&etsects->regs->tmr_cnt_l, lo); +- gfar_write(&etsects->regs->tmr_cnt_h, hi); +-} +- +-/* Caller must hold etsects->lock. */ +-static void set_alarm(struct etsects *etsects) +-{ +- u64 ns; +- u32 lo, hi; +- +- ns = tmr_cnt_read(etsects) + 1500000000ULL; +- ns = div_u64(ns, 1000000000UL) * 1000000000ULL; +- ns -= etsects->tclk_period; +- hi = ns >> 32; +- lo = ns & 0xffffffff; +- gfar_write(&etsects->regs->tmr_alarm1_l, lo); +- gfar_write(&etsects->regs->tmr_alarm1_h, hi); +-} +- +-/* Caller must hold etsects->lock. */ +-static void set_fipers(struct etsects *etsects) +-{ +- set_alarm(etsects); +- gfar_write(&etsects->regs->tmr_fiper1, etsects->tmr_fiper1); +- gfar_write(&etsects->regs->tmr_fiper2, etsects->tmr_fiper2); +-} +- +-/* +- * Interrupt service routine +- */ +- +-static irqreturn_t isr(int irq, void *priv) +-{ +- struct etsects *etsects = priv; +- struct ptp_clock_event event; +- u64 ns; +- u32 ack = 0, lo, hi, mask, val; +- +- val = gfar_read(&etsects->regs->tmr_tevent); +- +- if (val & ETS1) { +- ack |= ETS1; +- hi = gfar_read(&etsects->regs->tmr_etts1_h); +- lo = gfar_read(&etsects->regs->tmr_etts1_l); +- event.type = PTP_CLOCK_EXTTS; +- event.index = 0; +- event.timestamp = ((u64) hi) << 32; +- event.timestamp |= lo; +- ptp_clock_event(etsects->clock, &event); +- } +- +- if (val & ETS2) { +- ack |= ETS2; +- hi = gfar_read(&etsects->regs->tmr_etts2_h); +- lo = gfar_read(&etsects->regs->tmr_etts2_l); +- event.type = PTP_CLOCK_EXTTS; +- event.index = 1; +- event.timestamp = ((u64) hi) << 32; +- event.timestamp |= lo; +- ptp_clock_event(etsects->clock, &event); +- } +- +- if (val & ALM2) { +- ack |= ALM2; +- if (etsects->alarm_value) { +- event.type = PTP_CLOCK_ALARM; +- event.index = 0; +- event.timestamp = etsects->alarm_value; +- ptp_clock_event(etsects->clock, &event); +- } +- if (etsects->alarm_interval) { +- ns = etsects->alarm_value + etsects->alarm_interval; +- hi = ns >> 32; +- lo = ns & 0xffffffff; +- spin_lock(&etsects->lock); +- gfar_write(&etsects->regs->tmr_alarm2_l, lo); +- gfar_write(&etsects->regs->tmr_alarm2_h, hi); +- spin_unlock(&etsects->lock); +- etsects->alarm_value = ns; +- } else { +- gfar_write(&etsects->regs->tmr_tevent, ALM2); +- spin_lock(&etsects->lock); +- mask = gfar_read(&etsects->regs->tmr_temask); +- mask &= ~ALM2EN; +- gfar_write(&etsects->regs->tmr_temask, mask); +- spin_unlock(&etsects->lock); +- etsects->alarm_value = 0; +- etsects->alarm_interval = 0; +- } +- } +- +- if (val & PP1) { +- ack |= PP1; +- event.type = PTP_CLOCK_PPS; +- ptp_clock_event(etsects->clock, &event); +- } +- +- if (ack) { +- gfar_write(&etsects->regs->tmr_tevent, ack); +- return IRQ_HANDLED; +- } else +- return IRQ_NONE; +-} +- +-/* +- * PTP clock operations +- */ +- +-static int ptp_gianfar_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) +-{ +- u64 adj, diff; +- u32 tmr_add; +- int neg_adj = 0; +- struct etsects *etsects = container_of(ptp, struct etsects, caps); +- +- if (scaled_ppm < 0) { +- neg_adj = 1; +- scaled_ppm = -scaled_ppm; +- } +- tmr_add = etsects->tmr_add; +- adj = tmr_add; +- +- /* calculate diff as adj*(scaled_ppm/65536)/1000000 +- * and round() to the nearest integer +- */ +- adj *= scaled_ppm; +- diff = div_u64(adj, 8000000); +- diff = (diff >> 13) + ((diff >> 12) & 1); +- +- tmr_add = neg_adj ? tmr_add - diff : tmr_add + diff; +- +- gfar_write(&etsects->regs->tmr_add, tmr_add); +- +- return 0; +-} +- +-static int ptp_gianfar_adjtime(struct ptp_clock_info *ptp, s64 delta) +-{ +- s64 now; +- unsigned long flags; +- struct etsects *etsects = container_of(ptp, struct etsects, caps); +- +- spin_lock_irqsave(&etsects->lock, flags); +- +- now = tmr_cnt_read(etsects); +- now += delta; +- tmr_cnt_write(etsects, now); +- set_fipers(etsects); +- +- spin_unlock_irqrestore(&etsects->lock, flags); +- +- return 0; +-} +- +-static int ptp_gianfar_gettime(struct ptp_clock_info *ptp, +- struct timespec64 *ts) +-{ +- u64 ns; +- unsigned long flags; +- struct etsects *etsects = container_of(ptp, struct etsects, caps); +- +- spin_lock_irqsave(&etsects->lock, flags); +- +- ns = tmr_cnt_read(etsects); +- +- spin_unlock_irqrestore(&etsects->lock, flags); +- +- *ts = ns_to_timespec64(ns); +- +- return 0; +-} +- +-static int ptp_gianfar_settime(struct ptp_clock_info *ptp, +- const struct timespec64 *ts) +-{ +- u64 ns; +- unsigned long flags; +- struct etsects *etsects = container_of(ptp, struct etsects, caps); +- +- ns = timespec64_to_ns(ts); +- +- spin_lock_irqsave(&etsects->lock, flags); +- +- tmr_cnt_write(etsects, ns); +- set_fipers(etsects); +- +- spin_unlock_irqrestore(&etsects->lock, flags); +- +- return 0; +-} +- +-static int ptp_gianfar_enable(struct ptp_clock_info *ptp, +- struct ptp_clock_request *rq, int on) +-{ +- struct etsects *etsects = container_of(ptp, struct etsects, caps); +- unsigned long flags; +- u32 bit, mask; +- +- switch (rq->type) { +- case PTP_CLK_REQ_EXTTS: +- switch (rq->extts.index) { +- case 0: +- bit = ETS1EN; +- break; +- case 1: +- bit = ETS2EN; +- break; +- default: +- return -EINVAL; +- } +- spin_lock_irqsave(&etsects->lock, flags); +- mask = gfar_read(&etsects->regs->tmr_temask); +- if (on) +- mask |= bit; +- else +- mask &= ~bit; +- gfar_write(&etsects->regs->tmr_temask, mask); +- spin_unlock_irqrestore(&etsects->lock, flags); +- return 0; +- +- case PTP_CLK_REQ_PPS: +- spin_lock_irqsave(&etsects->lock, flags); +- mask = gfar_read(&etsects->regs->tmr_temask); +- if (on) +- mask |= PP1EN; +- else +- mask &= ~PP1EN; +- gfar_write(&etsects->regs->tmr_temask, mask); +- spin_unlock_irqrestore(&etsects->lock, flags); +- return 0; +- +- default: +- break; +- } +- +- return -EOPNOTSUPP; +-} +- +-static const struct ptp_clock_info ptp_gianfar_caps = { +- .owner = THIS_MODULE, +- .name = "gianfar clock", +- .max_adj = 512000, +- .n_alarm = 0, +- .n_ext_ts = N_EXT_TS, +- .n_per_out = 0, +- .n_pins = 0, +- .pps = 1, +- .adjfine = ptp_gianfar_adjfine, +- .adjtime = ptp_gianfar_adjtime, +- .gettime64 = ptp_gianfar_gettime, +- .settime64 = ptp_gianfar_settime, +- .enable = ptp_gianfar_enable, +-}; +- +-static int gianfar_ptp_probe(struct platform_device *dev) +-{ +- struct device_node *node = dev->dev.of_node; +- struct etsects *etsects; +- struct timespec64 now; +- int err = -ENOMEM; +- u32 tmr_ctrl; +- unsigned long flags; +- +- etsects = kzalloc(sizeof(*etsects), GFP_KERNEL); +- if (!etsects) +- goto no_memory; +- +- err = -ENODEV; +- +- etsects->caps = ptp_gianfar_caps; +- +- if (of_property_read_u32(node, "fsl,cksel", &etsects->cksel)) +- etsects->cksel = DEFAULT_CKSEL; +- +- if (of_property_read_u32(node, +- "fsl,tclk-period", &etsects->tclk_period) || +- of_property_read_u32(node, +- "fsl,tmr-prsc", &etsects->tmr_prsc) || +- of_property_read_u32(node, +- "fsl,tmr-add", &etsects->tmr_add) || +- of_property_read_u32(node, +- "fsl,tmr-fiper1", &etsects->tmr_fiper1) || +- of_property_read_u32(node, +- "fsl,tmr-fiper2", &etsects->tmr_fiper2) || +- of_property_read_u32(node, +- "fsl,max-adj", &etsects->caps.max_adj)) { +- pr_err("device tree node missing required elements\n"); +- goto no_node; +- } +- +- etsects->irq = platform_get_irq(dev, 0); +- +- if (etsects->irq < 0) { +- pr_err("irq not in device tree\n"); +- goto no_node; +- } +- if (request_irq(etsects->irq, isr, 0, DRIVER, etsects)) { +- pr_err("request_irq failed\n"); +- goto no_node; +- } +- +- etsects->rsrc = platform_get_resource(dev, IORESOURCE_MEM, 0); +- if (!etsects->rsrc) { +- pr_err("no resource\n"); +- goto no_resource; +- } +- if (request_resource(&iomem_resource, etsects->rsrc)) { +- pr_err("resource busy\n"); +- goto no_resource; +- } +- +- spin_lock_init(&etsects->lock); +- +- etsects->regs = ioremap(etsects->rsrc->start, +- resource_size(etsects->rsrc)); +- if (!etsects->regs) { +- pr_err("ioremap ptp registers failed\n"); +- goto no_ioremap; +- } +- getnstimeofday64(&now); +- ptp_gianfar_settime(&etsects->caps, &now); +- +- tmr_ctrl = +- (etsects->tclk_period & TCLK_PERIOD_MASK) << TCLK_PERIOD_SHIFT | +- (etsects->cksel & CKSEL_MASK) << CKSEL_SHIFT; +- +- spin_lock_irqsave(&etsects->lock, flags); +- +- gfar_write(&etsects->regs->tmr_ctrl, tmr_ctrl); +- gfar_write(&etsects->regs->tmr_add, etsects->tmr_add); +- gfar_write(&etsects->regs->tmr_prsc, etsects->tmr_prsc); +- gfar_write(&etsects->regs->tmr_fiper1, etsects->tmr_fiper1); +- gfar_write(&etsects->regs->tmr_fiper2, etsects->tmr_fiper2); +- set_alarm(etsects); +- gfar_write(&etsects->regs->tmr_ctrl, tmr_ctrl|FIPERST|RTPE|TE|FRD); +- +- spin_unlock_irqrestore(&etsects->lock, flags); +- +- etsects->clock = ptp_clock_register(&etsects->caps, &dev->dev); +- if (IS_ERR(etsects->clock)) { +- err = PTR_ERR(etsects->clock); +- goto no_clock; +- } +- gfar_phc_index = ptp_clock_index(etsects->clock); +- +- platform_set_drvdata(dev, etsects); +- +- return 0; +- +-no_clock: +- iounmap(etsects->regs); +-no_ioremap: +- release_resource(etsects->rsrc); +-no_resource: +- free_irq(etsects->irq, etsects); +-no_node: +- kfree(etsects); +-no_memory: +- return err; +-} +- +-static int gianfar_ptp_remove(struct platform_device *dev) +-{ +- struct etsects *etsects = platform_get_drvdata(dev); +- +- gfar_write(&etsects->regs->tmr_temask, 0); +- gfar_write(&etsects->regs->tmr_ctrl, 0); +- +- gfar_phc_index = -1; +- ptp_clock_unregister(etsects->clock); +- iounmap(etsects->regs); +- release_resource(etsects->rsrc); +- free_irq(etsects->irq, etsects); +- kfree(etsects); +- +- return 0; +-} +- +-static const struct of_device_id match_table[] = { +- { .compatible = "fsl,etsec-ptp" }, +- {}, +-}; +-MODULE_DEVICE_TABLE(of, match_table); +- +-static struct platform_driver gianfar_ptp_driver = { +- .driver = { +- .name = "gianfar_ptp", +- .of_match_table = match_table, +- }, +- .probe = gianfar_ptp_probe, +- .remove = gianfar_ptp_remove, +-}; +- +-module_platform_driver(gianfar_ptp_driver); +- +-MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>"); +-MODULE_DESCRIPTION("PTP clock using the eTSEC"); +-MODULE_LICENSE("GPL"); +--- a/drivers/ptp/Makefile ++++ b/drivers/ptp/Makefile +@@ -9,3 +9,4 @@ obj-$(CONFIG_PTP_1588_CLOCK_DTE) += ptp_ + obj-$(CONFIG_PTP_1588_CLOCK_IXP46X) += ptp_ixp46x.o + obj-$(CONFIG_PTP_1588_CLOCK_PCH) += ptp_pch.o + obj-$(CONFIG_PTP_1588_CLOCK_KVM) += ptp_kvm.o ++obj-$(CONFIG_PTP_1588_CLOCK_QORIQ) += ptp_qoriq.o +--- a/drivers/ptp/ptp_chardev.c ++++ b/drivers/ptp/ptp_chardev.c +@@ -224,7 +224,7 @@ long ptp_ioctl(struct posix_clock *pc, u + } + pct = &sysoff->ts[0]; + for (i = 0; i < sysoff->n_samples; i++) { +- getnstimeofday64(&ts); ++ ktime_get_real_ts64(&ts); + pct->sec = ts.tv_sec; + pct->nsec = ts.tv_nsec; + pct++; +@@ -235,7 +235,7 @@ long ptp_ioctl(struct posix_clock *pc, u + pct->nsec = ts.tv_nsec; + pct++; + } +- getnstimeofday64(&ts); ++ ktime_get_real_ts64(&ts); + pct->sec = ts.tv_sec; + pct->nsec = ts.tv_nsec; + if (copy_to_user((void __user *)arg, sysoff, sizeof(*sysoff))) +--- /dev/null ++++ b/drivers/ptp/ptp_qoriq.c +@@ -0,0 +1,589 @@ ++/* ++ * PTP 1588 clock for Freescale QorIQ 1588 timer ++ * ++ * Copyright (C) 2010 OMICRON electronics GmbH ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include <linux/device.h> ++#include <linux/hrtimer.h> ++#include <linux/interrupt.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/of_platform.h> ++#include <linux/timex.h> ++#include <linux/slab.h> ++#include <linux/clk.h> ++ ++#include <linux/fsl/ptp_qoriq.h> ++ ++/* ++ * Register access functions ++ */ ++ ++/* Caller must hold qoriq_ptp->lock. */ ++static u64 tmr_cnt_read(struct qoriq_ptp *qoriq_ptp) ++{ ++ struct qoriq_ptp_registers *regs = &qoriq_ptp->regs; ++ u64 ns; ++ u32 lo, hi; ++ ++ lo = qoriq_read(®s->ctrl_regs->tmr_cnt_l); ++ hi = qoriq_read(®s->ctrl_regs->tmr_cnt_h); ++ ns = ((u64) hi) << 32; ++ ns |= lo; ++ return ns; ++} ++ ++/* Caller must hold qoriq_ptp->lock. */ ++static void tmr_cnt_write(struct qoriq_ptp *qoriq_ptp, u64 ns) ++{ ++ struct qoriq_ptp_registers *regs = &qoriq_ptp->regs; ++ u32 hi = ns >> 32; ++ u32 lo = ns & 0xffffffff; ++ ++ qoriq_write(®s->ctrl_regs->tmr_cnt_l, lo); ++ qoriq_write(®s->ctrl_regs->tmr_cnt_h, hi); ++} ++ ++/* Caller must hold qoriq_ptp->lock. */ ++static void set_alarm(struct qoriq_ptp *qoriq_ptp) ++{ ++ struct qoriq_ptp_registers *regs = &qoriq_ptp->regs; ++ u64 ns; ++ u32 lo, hi; ++ ++ ns = tmr_cnt_read(qoriq_ptp) + 1500000000ULL; ++ ns = div_u64(ns, 1000000000UL) * 1000000000ULL; ++ ns -= qoriq_ptp->tclk_period; ++ hi = ns >> 32; ++ lo = ns & 0xffffffff; ++ qoriq_write(®s->alarm_regs->tmr_alarm1_l, lo); ++ qoriq_write(®s->alarm_regs->tmr_alarm1_h, hi); ++} ++ ++/* Caller must hold qoriq_ptp->lock. */ ++static void set_fipers(struct qoriq_ptp *qoriq_ptp) ++{ ++ struct qoriq_ptp_registers *regs = &qoriq_ptp->regs; ++ ++ set_alarm(qoriq_ptp); ++ qoriq_write(®s->fiper_regs->tmr_fiper1, qoriq_ptp->tmr_fiper1); ++ qoriq_write(®s->fiper_regs->tmr_fiper2, qoriq_ptp->tmr_fiper2); ++} ++ ++/* ++ * Interrupt service routine ++ */ ++ ++static irqreturn_t isr(int irq, void *priv) ++{ ++ struct qoriq_ptp *qoriq_ptp = priv; ++ struct qoriq_ptp_registers *regs = &qoriq_ptp->regs; ++ struct ptp_clock_event event; ++ u64 ns; ++ u32 ack = 0, lo, hi, mask, val; ++ ++ val = qoriq_read(®s->ctrl_regs->tmr_tevent); ++ ++ if (val & ETS1) { ++ ack |= ETS1; ++ hi = qoriq_read(®s->etts_regs->tmr_etts1_h); ++ lo = qoriq_read(®s->etts_regs->tmr_etts1_l); ++ event.type = PTP_CLOCK_EXTTS; ++ event.index = 0; ++ event.timestamp = ((u64) hi) << 32; ++ event.timestamp |= lo; ++ ptp_clock_event(qoriq_ptp->clock, &event); ++ } ++ ++ if (val & ETS2) { ++ ack |= ETS2; ++ hi = qoriq_read(®s->etts_regs->tmr_etts2_h); ++ lo = qoriq_read(®s->etts_regs->tmr_etts2_l); ++ event.type = PTP_CLOCK_EXTTS; ++ event.index = 1; ++ event.timestamp = ((u64) hi) << 32; ++ event.timestamp |= lo; ++ ptp_clock_event(qoriq_ptp->clock, &event); ++ } ++ ++ if (val & ALM2) { ++ ack |= ALM2; ++ if (qoriq_ptp->alarm_value) { ++ event.type = PTP_CLOCK_ALARM; ++ event.index = 0; ++ event.timestamp = qoriq_ptp->alarm_value; ++ ptp_clock_event(qoriq_ptp->clock, &event); ++ } ++ if (qoriq_ptp->alarm_interval) { ++ ns = qoriq_ptp->alarm_value + qoriq_ptp->alarm_interval; ++ hi = ns >> 32; ++ lo = ns & 0xffffffff; ++ spin_lock(&qoriq_ptp->lock); ++ qoriq_write(®s->alarm_regs->tmr_alarm2_l, lo); ++ qoriq_write(®s->alarm_regs->tmr_alarm2_h, hi); ++ spin_unlock(&qoriq_ptp->lock); ++ qoriq_ptp->alarm_value = ns; ++ } else { ++ qoriq_write(®s->ctrl_regs->tmr_tevent, ALM2); ++ spin_lock(&qoriq_ptp->lock); ++ mask = qoriq_read(®s->ctrl_regs->tmr_temask); ++ mask &= ~ALM2EN; ++ qoriq_write(®s->ctrl_regs->tmr_temask, mask); ++ spin_unlock(&qoriq_ptp->lock); ++ qoriq_ptp->alarm_value = 0; ++ qoriq_ptp->alarm_interval = 0; ++ } ++ } ++ ++ if (val & PP1) { ++ ack |= PP1; ++ event.type = PTP_CLOCK_PPS; ++ ptp_clock_event(qoriq_ptp->clock, &event); ++ } ++ ++ if (ack) { ++ qoriq_write(®s->ctrl_regs->tmr_tevent, ack); ++ return IRQ_HANDLED; ++ } else ++ return IRQ_NONE; ++} ++ ++/* ++ * PTP clock operations ++ */ ++ ++static int ptp_qoriq_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) ++{ ++ u64 adj, diff; ++ u32 tmr_add; ++ int neg_adj = 0; ++ struct qoriq_ptp *qoriq_ptp = container_of(ptp, struct qoriq_ptp, caps); ++ struct qoriq_ptp_registers *regs = &qoriq_ptp->regs; ++ ++ if (scaled_ppm < 0) { ++ neg_adj = 1; ++ scaled_ppm = -scaled_ppm; ++ } ++ tmr_add = qoriq_ptp->tmr_add; ++ adj = tmr_add; ++ ++ /* calculate diff as adj*(scaled_ppm/65536)/1000000 ++ * and round() to the nearest integer ++ */ ++ adj *= scaled_ppm; ++ diff = div_u64(adj, 8000000); ++ diff = (diff >> 13) + ((diff >> 12) & 1); ++ ++ tmr_add = neg_adj ? tmr_add - diff : tmr_add + diff; ++ ++ qoriq_write(®s->ctrl_regs->tmr_add, tmr_add); ++ ++ return 0; ++} ++ ++static int ptp_qoriq_adjtime(struct ptp_clock_info *ptp, s64 delta) ++{ ++ s64 now; ++ unsigned long flags; ++ struct qoriq_ptp *qoriq_ptp = container_of(ptp, struct qoriq_ptp, caps); ++ ++ spin_lock_irqsave(&qoriq_ptp->lock, flags); ++ ++ now = tmr_cnt_read(qoriq_ptp); ++ now += delta; ++ tmr_cnt_write(qoriq_ptp, now); ++ set_fipers(qoriq_ptp); ++ ++ spin_unlock_irqrestore(&qoriq_ptp->lock, flags); ++ ++ return 0; ++} ++ ++static int ptp_qoriq_gettime(struct ptp_clock_info *ptp, ++ struct timespec64 *ts) ++{ ++ u64 ns; ++ unsigned long flags; ++ struct qoriq_ptp *qoriq_ptp = container_of(ptp, struct qoriq_ptp, caps); ++ ++ spin_lock_irqsave(&qoriq_ptp->lock, flags); ++ ++ ns = tmr_cnt_read(qoriq_ptp); ++ ++ spin_unlock_irqrestore(&qoriq_ptp->lock, flags); ++ ++ *ts = ns_to_timespec64(ns); ++ ++ return 0; ++} ++ ++static int ptp_qoriq_settime(struct ptp_clock_info *ptp, ++ const struct timespec64 *ts) ++{ ++ u64 ns; ++ unsigned long flags; ++ struct qoriq_ptp *qoriq_ptp = container_of(ptp, struct qoriq_ptp, caps); ++ ++ ns = timespec64_to_ns(ts); ++ ++ spin_lock_irqsave(&qoriq_ptp->lock, flags); ++ ++ tmr_cnt_write(qoriq_ptp, ns); ++ set_fipers(qoriq_ptp); ++ ++ spin_unlock_irqrestore(&qoriq_ptp->lock, flags); ++ ++ return 0; ++} ++ ++static int ptp_qoriq_enable(struct ptp_clock_info *ptp, ++ struct ptp_clock_request *rq, int on) ++{ ++ struct qoriq_ptp *qoriq_ptp = container_of(ptp, struct qoriq_ptp, caps); ++ struct qoriq_ptp_registers *regs = &qoriq_ptp->regs; ++ unsigned long flags; ++ u32 bit, mask; ++ ++ switch (rq->type) { ++ case PTP_CLK_REQ_EXTTS: ++ switch (rq->extts.index) { ++ case 0: ++ bit = ETS1EN; ++ break; ++ case 1: ++ bit = ETS2EN; ++ break; ++ default: ++ return -EINVAL; ++ } ++ spin_lock_irqsave(&qoriq_ptp->lock, flags); ++ mask = qoriq_read(®s->ctrl_regs->tmr_temask); ++ if (on) ++ mask |= bit; ++ else ++ mask &= ~bit; ++ qoriq_write(®s->ctrl_regs->tmr_temask, mask); ++ spin_unlock_irqrestore(&qoriq_ptp->lock, flags); ++ return 0; ++ ++ case PTP_CLK_REQ_PPS: ++ spin_lock_irqsave(&qoriq_ptp->lock, flags); ++ mask = qoriq_read(®s->ctrl_regs->tmr_temask); ++ if (on) ++ mask |= PP1EN; ++ else ++ mask &= ~PP1EN; ++ qoriq_write(®s->ctrl_regs->tmr_temask, mask); ++ spin_unlock_irqrestore(&qoriq_ptp->lock, flags); ++ return 0; ++ ++ default: ++ break; ++ } ++ ++ return -EOPNOTSUPP; ++} ++ ++static const struct ptp_clock_info ptp_qoriq_caps = { ++ .owner = THIS_MODULE, ++ .name = "qoriq ptp clock", ++ .max_adj = 512000, ++ .n_alarm = 0, ++ .n_ext_ts = N_EXT_TS, ++ .n_per_out = 0, ++ .n_pins = 0, ++ .pps = 1, ++ .adjfine = ptp_qoriq_adjfine, ++ .adjtime = ptp_qoriq_adjtime, ++ .gettime64 = ptp_qoriq_gettime, ++ .settime64 = ptp_qoriq_settime, ++ .enable = ptp_qoriq_enable, ++}; ++ ++/** ++ * qoriq_ptp_nominal_freq - calculate nominal frequency according to ++ * reference clock frequency ++ * ++ * @clk_src: reference clock frequency ++ * ++ * The nominal frequency is the desired clock frequency. ++ * It should be less than the reference clock frequency. ++ * It should be a factor of 1000MHz. ++ * ++ * Return the nominal frequency ++ */ ++static u32 qoriq_ptp_nominal_freq(u32 clk_src) ++{ ++ u32 remainder = 0; ++ ++ clk_src /= 1000000; ++ remainder = clk_src % 100; ++ if (remainder) { ++ clk_src -= remainder; ++ clk_src += 100; ++ } ++ ++ do { ++ clk_src -= 100; ++ ++ } while (1000 % clk_src); ++ ++ return clk_src * 1000000; ++} ++ ++/** ++ * qoriq_ptp_auto_config - calculate a set of default configurations ++ * ++ * @qoriq_ptp: pointer to qoriq_ptp ++ * @node: pointer to device_node ++ * ++ * If below dts properties are not provided, this function will be ++ * called to calculate a set of default configurations for them. ++ * "fsl,tclk-period" ++ * "fsl,tmr-prsc" ++ * "fsl,tmr-add" ++ * "fsl,tmr-fiper1" ++ * "fsl,tmr-fiper2" ++ * "fsl,max-adj" ++ * ++ * Return 0 if success ++ */ ++static int qoriq_ptp_auto_config(struct qoriq_ptp *qoriq_ptp, ++ struct device_node *node) ++{ ++ struct clk *clk; ++ u64 freq_comp; ++ u64 max_adj; ++ u32 nominal_freq; ++ u32 remainder = 0; ++ u32 clk_src = 0; ++ ++ qoriq_ptp->cksel = DEFAULT_CKSEL; ++ ++ clk = of_clk_get(node, 0); ++ if (!IS_ERR(clk)) { ++ clk_src = clk_get_rate(clk); ++ clk_put(clk); ++ } ++ ++ if (clk_src <= 100000000UL) { ++ pr_err("error reference clock value, or lower than 100MHz\n"); ++ return -EINVAL; ++ } ++ ++ nominal_freq = qoriq_ptp_nominal_freq(clk_src); ++ if (!nominal_freq) ++ return -EINVAL; ++ ++ qoriq_ptp->tclk_period = 1000000000UL / nominal_freq; ++ qoriq_ptp->tmr_prsc = DEFAULT_TMR_PRSC; ++ ++ /* Calculate initial frequency compensation value for TMR_ADD register. ++ * freq_comp = ceil(2^32 / freq_ratio) ++ * freq_ratio = reference_clock_freq / nominal_freq ++ */ ++ freq_comp = ((u64)1 << 32) * nominal_freq; ++ freq_comp = div_u64_rem(freq_comp, clk_src, &remainder); ++ if (remainder) ++ freq_comp++; ++ ++ qoriq_ptp->tmr_add = freq_comp; ++ qoriq_ptp->tmr_fiper1 = DEFAULT_FIPER1_PERIOD - qoriq_ptp->tclk_period; ++ qoriq_ptp->tmr_fiper2 = DEFAULT_FIPER2_PERIOD - qoriq_ptp->tclk_period; ++ ++ /* max_adj = 1000000000 * (freq_ratio - 1.0) - 1 ++ * freq_ratio = reference_clock_freq / nominal_freq ++ */ ++ max_adj = 1000000000ULL * (clk_src - nominal_freq); ++ max_adj = div_u64(max_adj, nominal_freq) - 1; ++ qoriq_ptp->caps.max_adj = max_adj; ++ ++ return 0; ++} ++ ++static int qoriq_ptp_probe(struct platform_device *dev) ++{ ++ struct device_node *node = dev->dev.of_node; ++ struct qoriq_ptp *qoriq_ptp; ++ struct qoriq_ptp_registers *regs; ++ struct timespec64 now; ++ int err = -ENOMEM; ++ u32 tmr_ctrl; ++ unsigned long flags; ++ void __iomem *base; ++ ++ qoriq_ptp = kzalloc(sizeof(*qoriq_ptp), GFP_KERNEL); ++ if (!qoriq_ptp) ++ goto no_memory; ++ ++ err = -EINVAL; ++ ++ qoriq_ptp->caps = ptp_qoriq_caps; ++ ++ if (of_property_read_u32(node, "fsl,cksel", &qoriq_ptp->cksel)) ++ qoriq_ptp->cksel = DEFAULT_CKSEL; ++ ++ if (of_property_read_u32(node, ++ "fsl,tclk-period", &qoriq_ptp->tclk_period) || ++ of_property_read_u32(node, ++ "fsl,tmr-prsc", &qoriq_ptp->tmr_prsc) || ++ of_property_read_u32(node, ++ "fsl,tmr-add", &qoriq_ptp->tmr_add) || ++ of_property_read_u32(node, ++ "fsl,tmr-fiper1", &qoriq_ptp->tmr_fiper1) || ++ of_property_read_u32(node, ++ "fsl,tmr-fiper2", &qoriq_ptp->tmr_fiper2) || ++ of_property_read_u32(node, ++ "fsl,max-adj", &qoriq_ptp->caps.max_adj)) { ++ pr_warn("device tree node missing required elements, try automatic configuration\n"); ++ ++ if (qoriq_ptp_auto_config(qoriq_ptp, node)) ++ goto no_config; ++ } ++ ++ err = -ENODEV; ++ ++ qoriq_ptp->irq = platform_get_irq(dev, 0); ++ ++ if (qoriq_ptp->irq < 0) { ++ pr_err("irq not in device tree\n"); ++ goto no_node; ++ } ++ if (request_irq(qoriq_ptp->irq, isr, IRQF_SHARED, DRIVER, qoriq_ptp)) { ++ pr_err("request_irq failed\n"); ++ goto no_node; ++ } ++ ++ qoriq_ptp->rsrc = platform_get_resource(dev, IORESOURCE_MEM, 0); ++ if (!qoriq_ptp->rsrc) { ++ pr_err("no resource\n"); ++ goto no_resource; ++ } ++ if (request_resource(&iomem_resource, qoriq_ptp->rsrc)) { ++ pr_err("resource busy\n"); ++ goto no_resource; ++ } ++ ++ spin_lock_init(&qoriq_ptp->lock); ++ ++ base = ioremap(qoriq_ptp->rsrc->start, ++ resource_size(qoriq_ptp->rsrc)); ++ if (!base) { ++ pr_err("ioremap ptp registers failed\n"); ++ goto no_ioremap; ++ } ++ ++ qoriq_ptp->base = base; ++ ++ if (of_device_is_compatible(node, "fsl,fman-ptp-timer")) { ++ qoriq_ptp->regs.ctrl_regs = base + FMAN_CTRL_REGS_OFFSET; ++ qoriq_ptp->regs.alarm_regs = base + FMAN_ALARM_REGS_OFFSET; ++ qoriq_ptp->regs.fiper_regs = base + FMAN_FIPER_REGS_OFFSET; ++ qoriq_ptp->regs.etts_regs = base + FMAN_ETTS_REGS_OFFSET; ++ } else { ++ qoriq_ptp->regs.ctrl_regs = base + CTRL_REGS_OFFSET; ++ qoriq_ptp->regs.alarm_regs = base + ALARM_REGS_OFFSET; ++ qoriq_ptp->regs.fiper_regs = base + FIPER_REGS_OFFSET; ++ qoriq_ptp->regs.etts_regs = base + ETTS_REGS_OFFSET; ++ } ++ ++ ktime_get_real_ts64(&now); ++ ptp_qoriq_settime(&qoriq_ptp->caps, &now); ++ ++ tmr_ctrl = ++ (qoriq_ptp->tclk_period & TCLK_PERIOD_MASK) << TCLK_PERIOD_SHIFT | ++ (qoriq_ptp->cksel & CKSEL_MASK) << CKSEL_SHIFT; ++ ++ spin_lock_irqsave(&qoriq_ptp->lock, flags); ++ ++ regs = &qoriq_ptp->regs; ++ qoriq_write(®s->ctrl_regs->tmr_ctrl, tmr_ctrl); ++ qoriq_write(®s->ctrl_regs->tmr_add, qoriq_ptp->tmr_add); ++ qoriq_write(®s->ctrl_regs->tmr_prsc, qoriq_ptp->tmr_prsc); ++ qoriq_write(®s->fiper_regs->tmr_fiper1, qoriq_ptp->tmr_fiper1); ++ qoriq_write(®s->fiper_regs->tmr_fiper2, qoriq_ptp->tmr_fiper2); ++ set_alarm(qoriq_ptp); ++ qoriq_write(®s->ctrl_regs->tmr_ctrl, tmr_ctrl|FIPERST|RTPE|TE|FRD); ++ ++ spin_unlock_irqrestore(&qoriq_ptp->lock, flags); ++ ++ qoriq_ptp->clock = ptp_clock_register(&qoriq_ptp->caps, &dev->dev); ++ if (IS_ERR(qoriq_ptp->clock)) { ++ err = PTR_ERR(qoriq_ptp->clock); ++ goto no_clock; ++ } ++ qoriq_ptp->phc_index = ptp_clock_index(qoriq_ptp->clock); ++ ++ platform_set_drvdata(dev, qoriq_ptp); ++ ++ return 0; ++ ++no_clock: ++ iounmap(qoriq_ptp->base); ++no_ioremap: ++ release_resource(qoriq_ptp->rsrc); ++no_resource: ++ free_irq(qoriq_ptp->irq, qoriq_ptp); ++no_config: ++no_node: ++ kfree(qoriq_ptp); ++no_memory: ++ return err; ++} ++ ++static int qoriq_ptp_remove(struct platform_device *dev) ++{ ++ struct qoriq_ptp *qoriq_ptp = platform_get_drvdata(dev); ++ struct qoriq_ptp_registers *regs = &qoriq_ptp->regs; ++ ++ qoriq_write(®s->ctrl_regs->tmr_temask, 0); ++ qoriq_write(®s->ctrl_regs->tmr_ctrl, 0); ++ ++ ptp_clock_unregister(qoriq_ptp->clock); ++ iounmap(qoriq_ptp->base); ++ release_resource(qoriq_ptp->rsrc); ++ free_irq(qoriq_ptp->irq, qoriq_ptp); ++ kfree(qoriq_ptp); ++ ++ return 0; ++} ++ ++static const struct of_device_id match_table[] = { ++ { .compatible = "fsl,etsec-ptp" }, ++ { .compatible = "fsl,fman-ptp-timer" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, match_table); ++ ++static struct platform_driver qoriq_ptp_driver = { ++ .driver = { ++ .name = "ptp_qoriq", ++ .of_match_table = match_table, ++ }, ++ .probe = qoriq_ptp_probe, ++ .remove = qoriq_ptp_remove, ++}; ++ ++module_platform_driver(qoriq_ptp_driver); ++ ++MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>"); ++MODULE_DESCRIPTION("PTP clock for Freescale QorIQ 1588 timer"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/include/linux/fsl/ptp_qoriq.h +@@ -0,0 +1,169 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2010 OMICRON electronics GmbH ++ * Copyright 2018 NXP ++ */ ++#ifndef __PTP_QORIQ_H__ ++#define __PTP_QORIQ_H__ ++ ++#include <linux/io.h> ++#include <linux/ptp_clock_kernel.h> ++ ++/* ++ * qoriq ptp registers ++ */ ++struct ctrl_regs { ++ u32 tmr_ctrl; /* Timer control register */ ++ u32 tmr_tevent; /* Timestamp event register */ ++ u32 tmr_temask; /* Timer event mask register */ ++ u32 tmr_pevent; /* Timestamp event register */ ++ u32 tmr_pemask; /* Timer event mask register */ ++ u32 tmr_stat; /* Timestamp status register */ ++ u32 tmr_cnt_h; /* Timer counter high register */ ++ u32 tmr_cnt_l; /* Timer counter low register */ ++ u32 tmr_add; /* Timer drift compensation addend register */ ++ u32 tmr_acc; /* Timer accumulator register */ ++ u32 tmr_prsc; /* Timer prescale */ ++ u8 res1[4]; ++ u32 tmroff_h; /* Timer offset high */ ++ u32 tmroff_l; /* Timer offset low */ ++}; ++ ++struct alarm_regs { ++ u32 tmr_alarm1_h; /* Timer alarm 1 high register */ ++ u32 tmr_alarm1_l; /* Timer alarm 1 high register */ ++ u32 tmr_alarm2_h; /* Timer alarm 2 high register */ ++ u32 tmr_alarm2_l; /* Timer alarm 2 high register */ ++}; ++ ++struct fiper_regs { ++ u32 tmr_fiper1; /* Timer fixed period interval */ ++ u32 tmr_fiper2; /* Timer fixed period interval */ ++ u32 tmr_fiper3; /* Timer fixed period interval */ ++}; ++ ++struct etts_regs { ++ u32 tmr_etts1_h; /* Timestamp of general purpose external trigger */ ++ u32 tmr_etts1_l; /* Timestamp of general purpose external trigger */ ++ u32 tmr_etts2_h; /* Timestamp of general purpose external trigger */ ++ u32 tmr_etts2_l; /* Timestamp of general purpose external trigger */ ++}; ++ ++struct qoriq_ptp_registers { ++ struct ctrl_regs __iomem *ctrl_regs; ++ struct alarm_regs __iomem *alarm_regs; ++ struct fiper_regs __iomem *fiper_regs; ++ struct etts_regs __iomem *etts_regs; ++}; ++ ++/* Offset definitions for the four register groups */ ++#define CTRL_REGS_OFFSET 0x0 ++#define ALARM_REGS_OFFSET 0x40 ++#define FIPER_REGS_OFFSET 0x80 ++#define ETTS_REGS_OFFSET 0xa0 ++ ++#define FMAN_CTRL_REGS_OFFSET 0x80 ++#define FMAN_ALARM_REGS_OFFSET 0xb8 ++#define FMAN_FIPER_REGS_OFFSET 0xd0 ++#define FMAN_ETTS_REGS_OFFSET 0xe0 ++ ++ ++/* Bit definitions for the TMR_CTRL register */ ++#define ALM1P (1<<31) /* Alarm1 output polarity */ ++#define ALM2P (1<<30) /* Alarm2 output polarity */ ++#define FIPERST (1<<28) /* FIPER start indication */ ++#define PP1L (1<<27) /* Fiper1 pulse loopback mode enabled. */ ++#define PP2L (1<<26) /* Fiper2 pulse loopback mode enabled. */ ++#define TCLK_PERIOD_SHIFT (16) /* 1588 timer reference clock period. */ ++#define TCLK_PERIOD_MASK (0x3ff) ++#define RTPE (1<<15) /* Record Tx Timestamp to PAL Enable. */ ++#define FRD (1<<14) /* FIPER Realignment Disable */ ++#define ESFDP (1<<11) /* External Tx/Rx SFD Polarity. */ ++#define ESFDE (1<<10) /* External Tx/Rx SFD Enable. */ ++#define ETEP2 (1<<9) /* External trigger 2 edge polarity */ ++#define ETEP1 (1<<8) /* External trigger 1 edge polarity */ ++#define COPH (1<<7) /* Generated clock output phase. */ ++#define CIPH (1<<6) /* External oscillator input clock phase */ ++#define TMSR (1<<5) /* Timer soft reset. */ ++#define BYP (1<<3) /* Bypass drift compensated clock */ ++#define TE (1<<2) /* 1588 timer enable. */ ++#define CKSEL_SHIFT (0) /* 1588 Timer reference clock source */ ++#define CKSEL_MASK (0x3) ++ ++/* Bit definitions for the TMR_TEVENT register */ ++#define ETS2 (1<<25) /* External trigger 2 timestamp sampled */ ++#define ETS1 (1<<24) /* External trigger 1 timestamp sampled */ ++#define ALM2 (1<<17) /* Current time = alarm time register 2 */ ++#define ALM1 (1<<16) /* Current time = alarm time register 1 */ ++#define PP1 (1<<7) /* periodic pulse generated on FIPER1 */ ++#define PP2 (1<<6) /* periodic pulse generated on FIPER2 */ ++#define PP3 (1<<5) /* periodic pulse generated on FIPER3 */ ++ ++/* Bit definitions for the TMR_TEMASK register */ ++#define ETS2EN (1<<25) /* External trigger 2 timestamp enable */ ++#define ETS1EN (1<<24) /* External trigger 1 timestamp enable */ ++#define ALM2EN (1<<17) /* Timer ALM2 event enable */ ++#define ALM1EN (1<<16) /* Timer ALM1 event enable */ ++#define PP1EN (1<<7) /* Periodic pulse event 1 enable */ ++#define PP2EN (1<<6) /* Periodic pulse event 2 enable */ ++ ++/* Bit definitions for the TMR_PEVENT register */ ++#define TXP2 (1<<9) /* PTP transmitted timestamp im TXTS2 */ ++#define TXP1 (1<<8) /* PTP transmitted timestamp in TXTS1 */ ++#define RXP (1<<0) /* PTP frame has been received */ ++ ++/* Bit definitions for the TMR_PEMASK register */ ++#define TXP2EN (1<<9) /* Transmit PTP packet event 2 enable */ ++#define TXP1EN (1<<8) /* Transmit PTP packet event 1 enable */ ++#define RXPEN (1<<0) /* Receive PTP packet event enable */ ++ ++/* Bit definitions for the TMR_STAT register */ ++#define STAT_VEC_SHIFT (0) /* Timer general purpose status vector */ ++#define STAT_VEC_MASK (0x3f) ++ ++/* Bit definitions for the TMR_PRSC register */ ++#define PRSC_OCK_SHIFT (0) /* Output clock division/prescale factor. */ ++#define PRSC_OCK_MASK (0xffff) ++ ++ ++#define DRIVER "ptp_qoriq" ++#define N_EXT_TS 2 ++ ++#define DEFAULT_CKSEL 1 ++#define DEFAULT_TMR_PRSC 2 ++#define DEFAULT_FIPER1_PERIOD 1000000000 ++#define DEFAULT_FIPER2_PERIOD 100000 ++ ++struct qoriq_ptp { ++ void __iomem *base; ++ struct qoriq_ptp_registers regs; ++ spinlock_t lock; /* protects regs */ ++ struct ptp_clock *clock; ++ struct ptp_clock_info caps; ++ struct resource *rsrc; ++ int irq; ++ int phc_index; ++ u64 alarm_interval; /* for periodic alarm */ ++ u64 alarm_value; ++ u32 tclk_period; /* nanoseconds */ ++ u32 tmr_prsc; ++ u32 tmr_add; ++ u32 cksel; ++ u32 tmr_fiper1; ++ u32 tmr_fiper2; ++}; ++ ++static inline u32 qoriq_read(unsigned __iomem *addr) ++{ ++ u32 val; ++ ++ val = ioread32be(addr); ++ return val; ++} ++ ++static inline void qoriq_write(unsigned __iomem *addr, u32 val) ++{ ++ iowrite32be(val, addr); ++} ++ ++#endif |