From: Arnd Bergmann Date: Sun, 12 Jan 2020 12:04:44 +0000 (+0100) Subject: ptp: ixp46x: move adjacent to ethernet driver X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=2785543fbf0498eb749adee3d129ba8ac139c729;p=linux.git ptp: ixp46x: move adjacent to ethernet driver The ixp46x ptp driver has a somewhat unusual setup, where the ptp driver and the ethernet driver are in different directories but access the same registers that are defined a platform specific header file. Moving everything into drivers/net/ makes it look more like most other ptp drivers and allows compile-testing this driver on other targets. Signed-off-by: Arnd Bergmann Signed-off-by: Linus Walleij Signed-off-by: Jakub Kicinski --- diff --git a/arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h b/arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h deleted file mode 100644 index d792130e27b06..0000000000000 --- a/arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h +++ /dev/null @@ -1,68 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * PTP 1588 clock using the IXP46X - * - * Copyright (C) 2010 OMICRON electronics GmbH - */ - -#ifndef _IXP46X_TS_H_ -#define _IXP46X_TS_H_ - -#define DEFAULT_ADDEND 0xF0000029 -#define TICKS_NS_SHIFT 4 - -struct ixp46x_channel_ctl { - u32 ch_control; /* 0x40 Time Synchronization Channel Control */ - u32 ch_event; /* 0x44 Time Synchronization Channel Event */ - u32 tx_snap_lo; /* 0x48 Transmit Snapshot Low Register */ - u32 tx_snap_hi; /* 0x4C Transmit Snapshot High Register */ - u32 rx_snap_lo; /* 0x50 Receive Snapshot Low Register */ - u32 rx_snap_hi; /* 0x54 Receive Snapshot High Register */ - u32 src_uuid_lo; /* 0x58 Source UUID0 Low Register */ - u32 src_uuid_hi; /* 0x5C Sequence Identifier/Source UUID0 High */ -}; - -struct ixp46x_ts_regs { - u32 control; /* 0x00 Time Sync Control Register */ - u32 event; /* 0x04 Time Sync Event Register */ - u32 addend; /* 0x08 Time Sync Addend Register */ - u32 accum; /* 0x0C Time Sync Accumulator Register */ - u32 test; /* 0x10 Time Sync Test Register */ - u32 unused; /* 0x14 */ - u32 rsystime_lo; /* 0x18 RawSystemTime_Low Register */ - u32 rsystime_hi; /* 0x1C RawSystemTime_High Register */ - u32 systime_lo; /* 0x20 SystemTime_Low Register */ - u32 systime_hi; /* 0x24 SystemTime_High Register */ - u32 trgt_lo; /* 0x28 TargetTime_Low Register */ - u32 trgt_hi; /* 0x2C TargetTime_High Register */ - u32 asms_lo; /* 0x30 Auxiliary Slave Mode Snapshot Low */ - u32 asms_hi; /* 0x34 Auxiliary Slave Mode Snapshot High */ - u32 amms_lo; /* 0x38 Auxiliary Master Mode Snapshot Low */ - u32 amms_hi; /* 0x3C Auxiliary Master Mode Snapshot High */ - - struct ixp46x_channel_ctl channel[3]; -}; - -/* 0x00 Time Sync Control Register Bits */ -#define TSCR_AMM (1<<3) -#define TSCR_ASM (1<<2) -#define TSCR_TTM (1<<1) -#define TSCR_RST (1<<0) - -/* 0x04 Time Sync Event Register Bits */ -#define TSER_SNM (1<<3) -#define TSER_SNS (1<<2) -#define TTIPEND (1<<1) - -/* 0x40 Time Synchronization Channel Control Register Bits */ -#define MASTER_MODE (1<<0) -#define TIMESTAMP_ALL (1<<1) - -/* 0x44 Time Synchronization Channel Event Register Bits */ -#define TX_SNAPSHOT_LOCKED (1<<0) -#define RX_SNAPSHOT_LOCKED (1<<1) - -/* The ptp_ixp46x module will set this variable */ -extern int ixp46x_phc_index; - -#endif diff --git a/drivers/net/ethernet/xscale/Kconfig b/drivers/net/ethernet/xscale/Kconfig index cd0a8f46e7c6c..98aa7b8ddb062 100644 --- a/drivers/net/ethernet/xscale/Kconfig +++ b/drivers/net/ethernet/xscale/Kconfig @@ -27,4 +27,18 @@ config IXP4XX_ETH Say Y here if you want to use built-in Ethernet ports on IXP4xx processor. +config PTP_1588_CLOCK_IXP46X + tristate "Intel IXP46x as PTP clock" + depends on IXP4XX_ETH + depends on PTP_1588_CLOCK + default y + help + This driver adds support for using the IXP46X as a PTP + clock. This clock is only useful if your PTP programs are + getting hardware time stamps on the PTP Ethernet packets + using the SO_TIMESTAMPING API. + + To compile this driver as a module, choose M here: the module + will be called ptp_ixp46x. + endif # NET_VENDOR_XSCALE diff --git a/drivers/net/ethernet/xscale/Makefile b/drivers/net/ethernet/xscale/Makefile index 794a519d07b3e..607f91b1e8786 100644 --- a/drivers/net/ethernet/xscale/Makefile +++ b/drivers/net/ethernet/xscale/Makefile @@ -3,4 +3,5 @@ # Makefile for the Intel XScale IXP device drivers. # -obj-$(CONFIG_IXP4XX_ETH) += ixp4xx_eth.o +obj-$(CONFIG_IXP4XX_ETH) += ixp4xx_eth.o +obj-$(CONFIG_PTP_1588_CLOCK_IXP46X) += ptp_ixp46x.o diff --git a/drivers/net/ethernet/xscale/ixp4xx_eth.c b/drivers/net/ethernet/xscale/ixp4xx_eth.c index 6fc04ffb22c2a..0075ecdb21f4d 100644 --- a/drivers/net/ethernet/xscale/ixp4xx_eth.c +++ b/drivers/net/ethernet/xscale/ixp4xx_eth.c @@ -33,10 +33,11 @@ #include #include #include -#include #include #include +#include "ixp46x_ts.h" + #define DEBUG_DESC 0 #define DEBUG_RX 0 #define DEBUG_TX 0 diff --git a/drivers/net/ethernet/xscale/ptp_ixp46x.c b/drivers/net/ethernet/xscale/ptp_ixp46x.c new file mode 100644 index 0000000000000..9ecc395239e98 --- /dev/null +++ b/drivers/net/ethernet/xscale/ptp_ixp46x.c @@ -0,0 +1,329 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * PTP 1588 clock using the IXP46X + * + * Copyright (C) 2010 OMICRON electronics GmbH + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ixp46x_ts.h" + +#define DRIVER "ptp_ixp46x" +#define N_EXT_TS 2 +#define MASTER_GPIO 8 +#define MASTER_IRQ 25 +#define SLAVE_GPIO 7 +#define SLAVE_IRQ 24 + +struct ixp_clock { + struct ixp46x_ts_regs *regs; + struct ptp_clock *ptp_clock; + struct ptp_clock_info caps; + int exts0_enabled; + int exts1_enabled; +}; + +DEFINE_SPINLOCK(register_lock); + +/* + * Register access functions + */ + +static u64 ixp_systime_read(struct ixp46x_ts_regs *regs) +{ + u64 ns; + u32 lo, hi; + + lo = __raw_readl(®s->systime_lo); + hi = __raw_readl(®s->systime_hi); + + ns = ((u64) hi) << 32; + ns |= lo; + ns <<= TICKS_NS_SHIFT; + + return ns; +} + +static void ixp_systime_write(struct ixp46x_ts_regs *regs, u64 ns) +{ + u32 hi, lo; + + ns >>= TICKS_NS_SHIFT; + hi = ns >> 32; + lo = ns & 0xffffffff; + + __raw_writel(lo, ®s->systime_lo); + __raw_writel(hi, ®s->systime_hi); +} + +/* + * Interrupt service routine + */ + +static irqreturn_t isr(int irq, void *priv) +{ + struct ixp_clock *ixp_clock = priv; + struct ixp46x_ts_regs *regs = ixp_clock->regs; + struct ptp_clock_event event; + u32 ack = 0, lo, hi, val; + + val = __raw_readl(®s->event); + + if (val & TSER_SNS) { + ack |= TSER_SNS; + if (ixp_clock->exts0_enabled) { + hi = __raw_readl(®s->asms_hi); + lo = __raw_readl(®s->asms_lo); + event.type = PTP_CLOCK_EXTTS; + event.index = 0; + event.timestamp = ((u64) hi) << 32; + event.timestamp |= lo; + event.timestamp <<= TICKS_NS_SHIFT; + ptp_clock_event(ixp_clock->ptp_clock, &event); + } + } + + if (val & TSER_SNM) { + ack |= TSER_SNM; + if (ixp_clock->exts1_enabled) { + hi = __raw_readl(®s->amms_hi); + lo = __raw_readl(®s->amms_lo); + event.type = PTP_CLOCK_EXTTS; + event.index = 1; + event.timestamp = ((u64) hi) << 32; + event.timestamp |= lo; + event.timestamp <<= TICKS_NS_SHIFT; + ptp_clock_event(ixp_clock->ptp_clock, &event); + } + } + + if (val & TTIPEND) + ack |= TTIPEND; /* this bit seems to be always set */ + + if (ack) { + __raw_writel(ack, ®s->event); + return IRQ_HANDLED; + } else + return IRQ_NONE; +} + +/* + * PTP clock operations + */ + +static int ptp_ixp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) +{ + u64 adj; + u32 diff, addend; + int neg_adj = 0; + struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps); + struct ixp46x_ts_regs *regs = ixp_clock->regs; + + if (ppb < 0) { + neg_adj = 1; + ppb = -ppb; + } + addend = DEFAULT_ADDEND; + adj = addend; + adj *= ppb; + diff = div_u64(adj, 1000000000ULL); + + addend = neg_adj ? addend - diff : addend + diff; + + __raw_writel(addend, ®s->addend); + + return 0; +} + +static int ptp_ixp_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + s64 now; + unsigned long flags; + struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps); + struct ixp46x_ts_regs *regs = ixp_clock->regs; + + spin_lock_irqsave(®ister_lock, flags); + + now = ixp_systime_read(regs); + now += delta; + ixp_systime_write(regs, now); + + spin_unlock_irqrestore(®ister_lock, flags); + + return 0; +} + +static int ptp_ixp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) +{ + u64 ns; + unsigned long flags; + struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps); + struct ixp46x_ts_regs *regs = ixp_clock->regs; + + spin_lock_irqsave(®ister_lock, flags); + + ns = ixp_systime_read(regs); + + spin_unlock_irqrestore(®ister_lock, flags); + + *ts = ns_to_timespec64(ns); + return 0; +} + +static int ptp_ixp_settime(struct ptp_clock_info *ptp, + const struct timespec64 *ts) +{ + u64 ns; + unsigned long flags; + struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps); + struct ixp46x_ts_regs *regs = ixp_clock->regs; + + ns = timespec64_to_ns(ts); + + spin_lock_irqsave(®ister_lock, flags); + + ixp_systime_write(regs, ns); + + spin_unlock_irqrestore(®ister_lock, flags); + + return 0; +} + +static int ptp_ixp_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) +{ + struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps); + + switch (rq->type) { + case PTP_CLK_REQ_EXTTS: + switch (rq->extts.index) { + case 0: + ixp_clock->exts0_enabled = on ? 1 : 0; + break; + case 1: + ixp_clock->exts1_enabled = on ? 1 : 0; + break; + default: + return -EINVAL; + } + return 0; + default: + break; + } + + return -EOPNOTSUPP; +} + +static const struct ptp_clock_info ptp_ixp_caps = { + .owner = THIS_MODULE, + .name = "IXP46X timer", + .max_adj = 66666655, + .n_ext_ts = N_EXT_TS, + .n_pins = 0, + .pps = 0, + .adjfreq = ptp_ixp_adjfreq, + .adjtime = ptp_ixp_adjtime, + .gettime64 = ptp_ixp_gettime, + .settime64 = ptp_ixp_settime, + .enable = ptp_ixp_enable, +}; + +/* module operations */ + +static struct ixp_clock ixp_clock; + +static int setup_interrupt(int gpio) +{ + int irq; + int err; + + err = gpio_request(gpio, "ixp4-ptp"); + if (err) + return err; + + err = gpio_direction_input(gpio); + if (err) + return err; + + irq = gpio_to_irq(gpio); + if (irq < 0) + return irq; + + err = irq_set_irq_type(irq, IRQF_TRIGGER_FALLING); + if (err) { + pr_err("cannot set trigger type for irq %d\n", irq); + return err; + } + + err = request_irq(irq, isr, 0, DRIVER, &ixp_clock); + if (err) { + pr_err("request_irq failed for irq %d\n", irq); + return err; + } + + return irq; +} + +static void __exit ptp_ixp_exit(void) +{ + free_irq(MASTER_IRQ, &ixp_clock); + free_irq(SLAVE_IRQ, &ixp_clock); + ixp46x_phc_index = -1; + ptp_clock_unregister(ixp_clock.ptp_clock); +} + +static int __init ptp_ixp_init(void) +{ + if (!cpu_is_ixp46x()) + return -ENODEV; + + ixp_clock.regs = + (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT; + + ixp_clock.caps = ptp_ixp_caps; + + ixp_clock.ptp_clock = ptp_clock_register(&ixp_clock.caps, NULL); + + if (IS_ERR(ixp_clock.ptp_clock)) + return PTR_ERR(ixp_clock.ptp_clock); + + ixp46x_phc_index = ptp_clock_index(ixp_clock.ptp_clock); + + __raw_writel(DEFAULT_ADDEND, &ixp_clock.regs->addend); + __raw_writel(1, &ixp_clock.regs->trgt_lo); + __raw_writel(0, &ixp_clock.regs->trgt_hi); + __raw_writel(TTIPEND, &ixp_clock.regs->event); + + if (MASTER_IRQ != setup_interrupt(MASTER_GPIO)) { + pr_err("failed to setup gpio %d as irq\n", MASTER_GPIO); + goto no_master; + } + if (SLAVE_IRQ != setup_interrupt(SLAVE_GPIO)) { + pr_err("failed to setup gpio %d as irq\n", SLAVE_GPIO); + goto no_slave; + } + + return 0; +no_slave: + free_irq(MASTER_IRQ, &ixp_clock); +no_master: + ptp_clock_unregister(ixp_clock.ptp_clock); + return -ENODEV; +} + +module_init(ptp_ixp_init); +module_exit(ptp_ixp_exit); + +MODULE_AUTHOR("Richard Cochran "); +MODULE_DESCRIPTION("PTP clock using the IXP46X timer"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/xscale/ptp_ixp46x.h b/drivers/net/ethernet/xscale/ptp_ixp46x.h new file mode 100644 index 0000000000000..d792130e27b06 --- /dev/null +++ b/drivers/net/ethernet/xscale/ptp_ixp46x.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * PTP 1588 clock using the IXP46X + * + * Copyright (C) 2010 OMICRON electronics GmbH + */ + +#ifndef _IXP46X_TS_H_ +#define _IXP46X_TS_H_ + +#define DEFAULT_ADDEND 0xF0000029 +#define TICKS_NS_SHIFT 4 + +struct ixp46x_channel_ctl { + u32 ch_control; /* 0x40 Time Synchronization Channel Control */ + u32 ch_event; /* 0x44 Time Synchronization Channel Event */ + u32 tx_snap_lo; /* 0x48 Transmit Snapshot Low Register */ + u32 tx_snap_hi; /* 0x4C Transmit Snapshot High Register */ + u32 rx_snap_lo; /* 0x50 Receive Snapshot Low Register */ + u32 rx_snap_hi; /* 0x54 Receive Snapshot High Register */ + u32 src_uuid_lo; /* 0x58 Source UUID0 Low Register */ + u32 src_uuid_hi; /* 0x5C Sequence Identifier/Source UUID0 High */ +}; + +struct ixp46x_ts_regs { + u32 control; /* 0x00 Time Sync Control Register */ + u32 event; /* 0x04 Time Sync Event Register */ + u32 addend; /* 0x08 Time Sync Addend Register */ + u32 accum; /* 0x0C Time Sync Accumulator Register */ + u32 test; /* 0x10 Time Sync Test Register */ + u32 unused; /* 0x14 */ + u32 rsystime_lo; /* 0x18 RawSystemTime_Low Register */ + u32 rsystime_hi; /* 0x1C RawSystemTime_High Register */ + u32 systime_lo; /* 0x20 SystemTime_Low Register */ + u32 systime_hi; /* 0x24 SystemTime_High Register */ + u32 trgt_lo; /* 0x28 TargetTime_Low Register */ + u32 trgt_hi; /* 0x2C TargetTime_High Register */ + u32 asms_lo; /* 0x30 Auxiliary Slave Mode Snapshot Low */ + u32 asms_hi; /* 0x34 Auxiliary Slave Mode Snapshot High */ + u32 amms_lo; /* 0x38 Auxiliary Master Mode Snapshot Low */ + u32 amms_hi; /* 0x3C Auxiliary Master Mode Snapshot High */ + + struct ixp46x_channel_ctl channel[3]; +}; + +/* 0x00 Time Sync Control Register Bits */ +#define TSCR_AMM (1<<3) +#define TSCR_ASM (1<<2) +#define TSCR_TTM (1<<1) +#define TSCR_RST (1<<0) + +/* 0x04 Time Sync Event Register Bits */ +#define TSER_SNM (1<<3) +#define TSER_SNS (1<<2) +#define TTIPEND (1<<1) + +/* 0x40 Time Synchronization Channel Control Register Bits */ +#define MASTER_MODE (1<<0) +#define TIMESTAMP_ALL (1<<1) + +/* 0x44 Time Synchronization Channel Event Register Bits */ +#define TX_SNAPSHOT_LOCKED (1<<0) +#define RX_SNAPSHOT_LOCKED (1<<1) + +/* The ptp_ixp46x module will set this variable */ +extern int ixp46x_phc_index; + +#endif diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig index c382158f587da..475c60dccaa4f 100644 --- a/drivers/ptp/Kconfig +++ b/drivers/ptp/Kconfig @@ -56,20 +56,6 @@ config PTP_1588_CLOCK_QORIQ To compile this driver as a module, choose M here: the module will be called ptp-qoriq. -config PTP_1588_CLOCK_IXP46X - tristate "Intel IXP46x as PTP clock" - depends on IXP4XX_ETH - depends on PTP_1588_CLOCK - default y - help - This driver adds support for using the IXP46X as a PTP - clock. This clock is only useful if your PTP programs are - getting hardware time stamps on the PTP Ethernet packets - using the SO_TIMESTAMPING API. - - To compile this driver as a module, choose M here: the module - will be called ptp_ixp46x. - comment "Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks." depends on PHYLIB=n || NETWORK_PHY_TIMESTAMPING=n diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile index 3fb91bebbaf7b..8c830336f1783 100644 --- a/drivers/ptp/Makefile +++ b/drivers/ptp/Makefile @@ -7,10 +7,9 @@ ptp-y := ptp_clock.o ptp_chardev.o ptp_sysfs.o obj-$(CONFIG_PTP_1588_CLOCK) += ptp.o obj-$(CONFIG_PTP_1588_CLOCK_DTE) += ptp_dte.o obj-$(CONFIG_PTP_1588_CLOCK_INES) += ptp_ines.o -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 ptp-qoriq-y += ptp_qoriq.o ptp-qoriq-$(CONFIG_DEBUG_FS) += ptp_qoriq_debugfs.o -obj-$(CONFIG_PTP_1588_CLOCK_IDTCM) += ptp_clockmatrix.o \ No newline at end of file +obj-$(CONFIG_PTP_1588_CLOCK_IDTCM) += ptp_clockmatrix.o diff --git a/drivers/ptp/ptp_ixp46x.c b/drivers/ptp/ptp_ixp46x.c deleted file mode 100644 index 67028484e9a08..0000000000000 --- a/drivers/ptp/ptp_ixp46x.c +++ /dev/null @@ -1,328 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * PTP 1588 clock using the IXP46X - * - * Copyright (C) 2010 OMICRON electronics GmbH - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define DRIVER "ptp_ixp46x" -#define N_EXT_TS 2 -#define MASTER_GPIO 8 -#define MASTER_IRQ 25 -#define SLAVE_GPIO 7 -#define SLAVE_IRQ 24 - -struct ixp_clock { - struct ixp46x_ts_regs *regs; - struct ptp_clock *ptp_clock; - struct ptp_clock_info caps; - int exts0_enabled; - int exts1_enabled; -}; - -DEFINE_SPINLOCK(register_lock); - -/* - * Register access functions - */ - -static u64 ixp_systime_read(struct ixp46x_ts_regs *regs) -{ - u64 ns; - u32 lo, hi; - - lo = __raw_readl(®s->systime_lo); - hi = __raw_readl(®s->systime_hi); - - ns = ((u64) hi) << 32; - ns |= lo; - ns <<= TICKS_NS_SHIFT; - - return ns; -} - -static void ixp_systime_write(struct ixp46x_ts_regs *regs, u64 ns) -{ - u32 hi, lo; - - ns >>= TICKS_NS_SHIFT; - hi = ns >> 32; - lo = ns & 0xffffffff; - - __raw_writel(lo, ®s->systime_lo); - __raw_writel(hi, ®s->systime_hi); -} - -/* - * Interrupt service routine - */ - -static irqreturn_t isr(int irq, void *priv) -{ - struct ixp_clock *ixp_clock = priv; - struct ixp46x_ts_regs *regs = ixp_clock->regs; - struct ptp_clock_event event; - u32 ack = 0, lo, hi, val; - - val = __raw_readl(®s->event); - - if (val & TSER_SNS) { - ack |= TSER_SNS; - if (ixp_clock->exts0_enabled) { - hi = __raw_readl(®s->asms_hi); - lo = __raw_readl(®s->asms_lo); - event.type = PTP_CLOCK_EXTTS; - event.index = 0; - event.timestamp = ((u64) hi) << 32; - event.timestamp |= lo; - event.timestamp <<= TICKS_NS_SHIFT; - ptp_clock_event(ixp_clock->ptp_clock, &event); - } - } - - if (val & TSER_SNM) { - ack |= TSER_SNM; - if (ixp_clock->exts1_enabled) { - hi = __raw_readl(®s->amms_hi); - lo = __raw_readl(®s->amms_lo); - event.type = PTP_CLOCK_EXTTS; - event.index = 1; - event.timestamp = ((u64) hi) << 32; - event.timestamp |= lo; - event.timestamp <<= TICKS_NS_SHIFT; - ptp_clock_event(ixp_clock->ptp_clock, &event); - } - } - - if (val & TTIPEND) - ack |= TTIPEND; /* this bit seems to be always set */ - - if (ack) { - __raw_writel(ack, ®s->event); - return IRQ_HANDLED; - } else - return IRQ_NONE; -} - -/* - * PTP clock operations - */ - -static int ptp_ixp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) -{ - u64 adj; - u32 diff, addend; - int neg_adj = 0; - struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps); - struct ixp46x_ts_regs *regs = ixp_clock->regs; - - if (ppb < 0) { - neg_adj = 1; - ppb = -ppb; - } - addend = DEFAULT_ADDEND; - adj = addend; - adj *= ppb; - diff = div_u64(adj, 1000000000ULL); - - addend = neg_adj ? addend - diff : addend + diff; - - __raw_writel(addend, ®s->addend); - - return 0; -} - -static int ptp_ixp_adjtime(struct ptp_clock_info *ptp, s64 delta) -{ - s64 now; - unsigned long flags; - struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps); - struct ixp46x_ts_regs *regs = ixp_clock->regs; - - spin_lock_irqsave(®ister_lock, flags); - - now = ixp_systime_read(regs); - now += delta; - ixp_systime_write(regs, now); - - spin_unlock_irqrestore(®ister_lock, flags); - - return 0; -} - -static int ptp_ixp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) -{ - u64 ns; - unsigned long flags; - struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps); - struct ixp46x_ts_regs *regs = ixp_clock->regs; - - spin_lock_irqsave(®ister_lock, flags); - - ns = ixp_systime_read(regs); - - spin_unlock_irqrestore(®ister_lock, flags); - - *ts = ns_to_timespec64(ns); - return 0; -} - -static int ptp_ixp_settime(struct ptp_clock_info *ptp, - const struct timespec64 *ts) -{ - u64 ns; - unsigned long flags; - struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps); - struct ixp46x_ts_regs *regs = ixp_clock->regs; - - ns = timespec64_to_ns(ts); - - spin_lock_irqsave(®ister_lock, flags); - - ixp_systime_write(regs, ns); - - spin_unlock_irqrestore(®ister_lock, flags); - - return 0; -} - -static int ptp_ixp_enable(struct ptp_clock_info *ptp, - struct ptp_clock_request *rq, int on) -{ - struct ixp_clock *ixp_clock = container_of(ptp, struct ixp_clock, caps); - - switch (rq->type) { - case PTP_CLK_REQ_EXTTS: - switch (rq->extts.index) { - case 0: - ixp_clock->exts0_enabled = on ? 1 : 0; - break; - case 1: - ixp_clock->exts1_enabled = on ? 1 : 0; - break; - default: - return -EINVAL; - } - return 0; - default: - break; - } - - return -EOPNOTSUPP; -} - -static const struct ptp_clock_info ptp_ixp_caps = { - .owner = THIS_MODULE, - .name = "IXP46X timer", - .max_adj = 66666655, - .n_ext_ts = N_EXT_TS, - .n_pins = 0, - .pps = 0, - .adjfreq = ptp_ixp_adjfreq, - .adjtime = ptp_ixp_adjtime, - .gettime64 = ptp_ixp_gettime, - .settime64 = ptp_ixp_settime, - .enable = ptp_ixp_enable, -}; - -/* module operations */ - -static struct ixp_clock ixp_clock; - -static int setup_interrupt(int gpio) -{ - int irq; - int err; - - err = gpio_request(gpio, "ixp4-ptp"); - if (err) - return err; - - err = gpio_direction_input(gpio); - if (err) - return err; - - irq = gpio_to_irq(gpio); - if (irq < 0) - return irq; - - err = irq_set_irq_type(irq, IRQF_TRIGGER_FALLING); - if (err) { - pr_err("cannot set trigger type for irq %d\n", irq); - return err; - } - - err = request_irq(irq, isr, 0, DRIVER, &ixp_clock); - if (err) { - pr_err("request_irq failed for irq %d\n", irq); - return err; - } - - return irq; -} - -static void __exit ptp_ixp_exit(void) -{ - free_irq(MASTER_IRQ, &ixp_clock); - free_irq(SLAVE_IRQ, &ixp_clock); - ixp46x_phc_index = -1; - ptp_clock_unregister(ixp_clock.ptp_clock); -} - -static int __init ptp_ixp_init(void) -{ - if (!cpu_is_ixp46x()) - return -ENODEV; - - ixp_clock.regs = - (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT; - - ixp_clock.caps = ptp_ixp_caps; - - ixp_clock.ptp_clock = ptp_clock_register(&ixp_clock.caps, NULL); - - if (IS_ERR(ixp_clock.ptp_clock)) - return PTR_ERR(ixp_clock.ptp_clock); - - ixp46x_phc_index = ptp_clock_index(ixp_clock.ptp_clock); - - __raw_writel(DEFAULT_ADDEND, &ixp_clock.regs->addend); - __raw_writel(1, &ixp_clock.regs->trgt_lo); - __raw_writel(0, &ixp_clock.regs->trgt_hi); - __raw_writel(TTIPEND, &ixp_clock.regs->event); - - if (MASTER_IRQ != setup_interrupt(MASTER_GPIO)) { - pr_err("failed to setup gpio %d as irq\n", MASTER_GPIO); - goto no_master; - } - if (SLAVE_IRQ != setup_interrupt(SLAVE_GPIO)) { - pr_err("failed to setup gpio %d as irq\n", SLAVE_GPIO); - goto no_slave; - } - - return 0; -no_slave: - free_irq(MASTER_IRQ, &ixp_clock); -no_master: - ptp_clock_unregister(ixp_clock.ptp_clock); - return -ENODEV; -} - -module_init(ptp_ixp_init); -module_exit(ptp_ixp_exit); - -MODULE_AUTHOR("Richard Cochran "); -MODULE_DESCRIPTION("PTP clock using the IXP46X timer"); -MODULE_LICENSE("GPL");