提交 92a8da46 编写于 作者: D David S. Miller

Merge branch 'net-ethernet-ti-cpts-add-irq-and-HW_TS_PUSH-events'

Grygorii Strashko says:

====================
net: ethernet: ti: cpts: add irq and HW_TS_PUSH events

This is re-spin of patches to add CPSW IRQ and HW_TS_PUSH events support I've
sent long time ago [1]. In this series, I've tried to restructure and split changes,
and also add few additional optimizations comparing to initial RFC submission [1].

The HW_TS_PUSH events intended to serve for different timesync purposes on of
which is to add PPS generation function, which can be implemented as below:

                     +-----------------+
                     | Control         |
                     | application     |
            +------->+                 +----------+
            |        |                 |          |
            |        |                 |          |
            |        +-----------------+          |
            |                                     |
            |                                     |
            | PTP_EXTTS_REQUEST                   |
            |                                     |
            |                                     |
 +----------------------------------------------------------------+
            |                                     |    Kernel
    +-------+----------+                  +-------v--------+
    |  \dev\ptpX       |                  | /sys/class/pwm/|
    |                  |                  |                |
    +-------^----------+                  +-------+--------+
            |                                     |
            |                                     |
            |                             +-------v-------------------+
    +-------+----------+                  |                           |
    | CPTS driver      |                  |pwm/pwm-omap-dmtimer.c     |
    |                  |                  +---------------------------+
    +-------^----------+                  |clocksource/timer_ti_dm.c  |
            |                             +-------+-------------------+
            |HWx_TS_PUSH evt                      |
 +----------------------------------------------------------------+
            |                                     |         HW
    +-------+----------+                  +-------v--------+
    | CPTS             |                  | DMTimer        |
    |                  |                  |                |
    |      HWx_TS_PUSH X<-----------------+                |
    |                  +                  |                |
    +------------------+                  +-------+--------+
                                                  |
                                                  X timer4

As per my knowledge there is at least one public implemented above PPS generation
schema from Tusori Tibor [2] based on initial HW_TS_PUSH enable submission[1].
And now there is work done by Lokesh Vutla <lokeshvutla@ti.com> published to
enable PWM enable/improve PWM adjustment from user space [3][4][5].

Main changes comparing to initial submission:
- TX timestamp processing deferred to ptp worker only
- both CPTS IRQ and polling events processing supported to make it work for
  Keystone 2 also
- switch to use new .gettimex64() interface
- no DT updates as number of HWx_TS_PUSH inputs is static per HW

Testing on am571x-idk/omap2plus_defconfig/+CONFIG_PREEMPT=y:
1) testing HW_TS_PUSH
 - enable pwm in DT
	pwm16: dmtimer-pwm {
		compatible = "ti,omap-dmtimer-pwm";
		ti,timers = <&timer16>;
		#pwm-cells = <3>;
	};
 - configure and start pwm
  echo 0 > /sys/class/pwm/pwmchip0/export
  echo 1000000000 > /sys/class/pwm/pwmchip0/pwm0/period
  echo 500000000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
  echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable
 - test HWx_TS_PUSH using Kernel selftest testptp application
  ./tools/testing/selftests/ptp/testptp -d /dev/ptp0 -e 1000 -i 3

2) testing phc2sys
phc2sys[1616.791]: eth0 rms 408190379792180864 max 1580914543017209856 freq +864 +/- 4635 delay 645 +/- 29
phc2sys[1646.795]: eth0 rms 41 max 108 freq +0 +/- 36 delay 656 +/- 29
phc2sys[1676.800]: eth0 rms 43 max 83 freq +2 +/- 38 delay 650 +/- 0
phc2sys[1706.804]: eth0 rms 39 max 87 freq +4 +/- 34 delay 672 +/- 55
phc2sys[1736.808]: eth0 rms 35 max 66 freq +1 +/- 30 delay 667 +/- 49
phc2sys[1766.813]: eth0 rms 38 max 79 freq +2 +/- 33 delay 656 +/- 29
phc2sys[1796.817]: eth0 rms 45 max 98 freq +1 +/- 39 delay 656 +/- 29
phc2sys[1826.821]: eth0 rms 40 max 87 freq +5 +/- 35 delay 650 +/- 0
phc2sys[1856.826]: eth0 rms 29 max 76 freq -0 +/- 25 delay 656 +/- 29
phc2sys[1886.830]: eth0 rms 40 max 97 freq +4 +/- 35 delay 667 +/- 49
phc2sys[1916.834]: eth0 rms 42 max 94 freq +2 +/- 36 delay 661 +/- 41
phc2sys[1946.839]: eth0 rms 40 max 91 freq +2 +/- 35 delay 661 +/- 41
phc2sys[1976.843]: eth0 rms 46 max 88 freq -0 +/- 40 delay 667 +/- 49
phc2sys[2006.847]: eth0 rms 49 max 97 freq +2 +/- 43 delay 650 +/- 0

3) testing ptp4l
- 1G connection
ptp4l[862.891]: port 1: UNCALIBRATED to SLAVE on MASTER_CLOCK_SELECTED
ptp4l[923.894]: rms 1019697354682 max 5768279314068 freq +26053 +/- 72 delay 488 +/- 1
ptp4l[987.896]: rms 13 max 26 freq +26005 +/- 29 delay 488 +/- 1
ptp4l[1051.899]: rms 14 max 50 freq +25895 +/- 21 delay 488 +/- 1
ptp4l[1115.901]: rms 11 max 27 freq +25878 +/- 17 delay 488 +/- 1
ptp4l[1179.904]: rms 10 max 27 freq +25857 +/- 12 delay 488 +/- 1
ptp4l[1243.906]: rms 14 max 37 freq +25851 +/- 15 delay 488 +/- 1
ptp4l[1307.909]: rms 12 max 33 freq +25835 +/- 15 delay 488 +/- 1
ptp4l[1371.911]: rms 11 max 27 freq +25832 +/- 14 delay 488 +/- 1
ptp4l[1435.914]: rms 11 max 26 freq +25823 +/- 11 delay 488 +/- 1
ptp4l[1499.916]: rms 10 max 29 freq +25829 +/- 11 delay 489 +/- 1
ptp4l[1563.919]: rms 11 max 27 freq +25827 +/- 12 delay 488 +/- 1

- 10M connection
ptp4l[51.955]: port 1: UNCALIBRATED to SLAVE on MASTER_CLOCK_SELECTED
ptp4l[112.957]: rms 279468848453933920 max 1580914542977391360 freq +25390 +/- 3207 delay 8222 +/- 36
ptp4l[176.960]: rms 254 max 522 freq +25809 +/- 219 delay 8271 +/- 30
ptp4l[240.962]: rms 271 max 684 freq +25868 +/- 234 delay 8249 +/- 22
ptp4l[304.965]: rms 263 max 556 freq +25894 +/- 227 delay 8225 +/- 47
ptp4l[368.967]: rms 238 max 648 freq +25908 +/- 204 delay 8234 +/- 40
ptp4l[432.970]: rms 274 max 658 freq +25932 +/- 237 delay 8241 +/- 22
ptp4l[496.972]: rms 247 max 557 freq +25943 +/- 213 delay 8223 +/- 26
ptp4l[560.974]: rms 291 max 756 freq +25968 +/- 251 delay 8244 +/- 41
ptp4l[624.977]: rms 249 max 697 freq +25975 +/- 216 delay 8258 +/- 22

Changes in v5:
 - fixed build issue

Changes in v4:
 - fixed comments from Richard Cochran
 - dropped patch "net: ethernet: ti: cpts: move rx timestamp processing to ptp
   worker only"
 - added "Acked-by" from Richard Cochran <richardcochran@gmail.com>
 - dependencies resolved, patch merged

Changes in v3:
 - fixed rebase mess
 - fixed build issues

Changes in v2 (broken):
 - fixed (formatting) comments from David Miller <davem@davemloft.net>

v4: https://patchwork.ozlabs.org/project/netdev/cover/20200422201254.15232-1-grygorii.strashko@ti.com/
v3: https://patchwork.ozlabs.org/project/netdev/cover/20200320194244.4703-1-grygorii.strashko@ti.com/
v2: https://patchwork.ozlabs.org/cover/1258339/
v1: https://patchwork.ozlabs.org/cover/1254708/

[1] https://lore.kernel.org/patchwork/cover/799251/
[2] https://usermanual.wiki/Document/SetupGuide.632280828.pdf
    https://github.com/t-tibor/msc_thesis
[3] https://patchwork.kernel.org/cover/11421329/
[4] https://patchwork.kernel.org/cover/11433197/
[5] https://sourceforge.net/p/linuxptp/mailman/message/36943248/
====================
Signed-off-by: NDavid S. Miller <davem@davemloft.net>
...@@ -1569,6 +1569,12 @@ static int cpsw_probe(struct platform_device *pdev) ...@@ -1569,6 +1569,12 @@ static int cpsw_probe(struct platform_device *pdev)
return irq; return irq;
cpsw->irqs_table[1] = irq; cpsw->irqs_table[1] = irq;
/* get misc irq*/
irq = platform_get_irq(pdev, 3);
if (irq <= 0)
return irq;
cpsw->misc_irq = irq;
/* /*
* This may be required here for child devices. * This may be required here for child devices.
*/ */
...@@ -1703,6 +1709,21 @@ static int cpsw_probe(struct platform_device *pdev) ...@@ -1703,6 +1709,21 @@ static int cpsw_probe(struct platform_device *pdev)
goto clean_unregister_netdev_ret; goto clean_unregister_netdev_ret;
} }
if (!cpsw->cpts)
goto skip_cpts;
ret = devm_request_irq(&pdev->dev, cpsw->misc_irq, cpsw_misc_interrupt,
0, dev_name(&pdev->dev), cpsw);
if (ret < 0) {
dev_err(dev, "error attaching misc irq (%d)\n", ret);
goto clean_unregister_netdev_ret;
}
/* Enable misc CPTS evnt_pend IRQ */
cpts_set_irqpoll(cpsw->cpts, false);
writel(0x10, &cpsw->wr_regs->misc_en);
skip_cpts:
cpsw_notice(priv, probe, cpsw_notice(priv, probe,
"initialized device (regs %pa, irq %d, pool size %d)\n", "initialized device (regs %pa, irq %d, pool size %d)\n",
&ss_res->start, cpsw->irqs_table[0], descs_pool_size); &ss_res->start, cpsw->irqs_table[0], descs_pool_size);
......
...@@ -1896,6 +1896,11 @@ static int cpsw_probe(struct platform_device *pdev) ...@@ -1896,6 +1896,11 @@ static int cpsw_probe(struct platform_device *pdev)
return irq; return irq;
cpsw->irqs_table[1] = irq; cpsw->irqs_table[1] = irq;
irq = platform_get_irq_byname(pdev, "misc");
if (irq <= 0)
return irq;
cpsw->misc_irq = irq;
platform_set_drvdata(pdev, cpsw); platform_set_drvdata(pdev, cpsw);
/* This may be required here for child devices. */ /* This may be required here for child devices. */
pm_runtime_enable(dev); pm_runtime_enable(dev);
...@@ -1975,6 +1980,21 @@ static int cpsw_probe(struct platform_device *pdev) ...@@ -1975,6 +1980,21 @@ static int cpsw_probe(struct platform_device *pdev)
goto clean_unregister_netdev; goto clean_unregister_netdev;
} }
if (!cpsw->cpts)
goto skip_cpts;
ret = devm_request_irq(dev, cpsw->misc_irq, cpsw_misc_interrupt,
0, dev_name(&pdev->dev), cpsw);
if (ret < 0) {
dev_err(dev, "error attaching misc irq (%d)\n", ret);
goto clean_unregister_netdev;
}
/* Enable misc CPTS evnt_pend IRQ */
cpts_set_irqpoll(cpsw->cpts, false);
writel(0x10, &cpsw->wr_regs->misc_en);
skip_cpts:
ret = cpsw_register_notifiers(cpsw); ret = cpsw_register_notifiers(cpsw);
if (ret) if (ret)
goto clean_unregister_netdev; goto clean_unregister_netdev;
......
...@@ -28,6 +28,8 @@ ...@@ -28,6 +28,8 @@
#include "cpsw_sl.h" #include "cpsw_sl.h"
#include "davinci_cpdma.h" #include "davinci_cpdma.h"
#define CPTS_N_ETX_TS 4
int (*cpsw_slave_index)(struct cpsw_common *cpsw, struct cpsw_priv *priv); int (*cpsw_slave_index)(struct cpsw_common *cpsw, struct cpsw_priv *priv);
void cpsw_intr_enable(struct cpsw_common *cpsw) void cpsw_intr_enable(struct cpsw_common *cpsw)
...@@ -112,6 +114,18 @@ irqreturn_t cpsw_rx_interrupt(int irq, void *dev_id) ...@@ -112,6 +114,18 @@ irqreturn_t cpsw_rx_interrupt(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
irqreturn_t cpsw_misc_interrupt(int irq, void *dev_id)
{
struct cpsw_common *cpsw = dev_id;
writel(0, &cpsw->wr_regs->misc_en);
cpdma_ctlr_eoi(cpsw->dma, CPDMA_EOI_MISC);
cpts_misc_interrupt(cpsw->cpts);
writel(0x10, &cpsw->wr_regs->misc_en);
return IRQ_HANDLED;
}
int cpsw_tx_mq_poll(struct napi_struct *napi_tx, int budget) int cpsw_tx_mq_poll(struct napi_struct *napi_tx, int budget)
{ {
struct cpsw_common *cpsw = napi_to_cpsw(napi_tx); struct cpsw_common *cpsw = napi_to_cpsw(napi_tx);
...@@ -522,7 +536,8 @@ int cpsw_init_common(struct cpsw_common *cpsw, void __iomem *ss_regs, ...@@ -522,7 +536,8 @@ int cpsw_init_common(struct cpsw_common *cpsw, void __iomem *ss_regs,
if (!cpts_node) if (!cpts_node)
cpts_node = cpsw->dev->of_node; cpts_node = cpsw->dev->of_node;
cpsw->cpts = cpts_create(cpsw->dev, cpts_regs, cpts_node); cpsw->cpts = cpts_create(cpsw->dev, cpts_regs, cpts_node,
CPTS_N_ETX_TS);
if (IS_ERR(cpsw->cpts)) { if (IS_ERR(cpsw->cpts)) {
ret = PTR_ERR(cpsw->cpts); ret = PTR_ERR(cpsw->cpts);
cpdma_ctlr_destroy(cpsw->dma); cpdma_ctlr_destroy(cpsw->dma);
......
...@@ -350,6 +350,7 @@ struct cpsw_common { ...@@ -350,6 +350,7 @@ struct cpsw_common {
bool rx_irq_disabled; bool rx_irq_disabled;
bool tx_irq_disabled; bool tx_irq_disabled;
u32 irqs_table[IRQ_NUM]; u32 irqs_table[IRQ_NUM];
int misc_irq;
struct cpts *cpts; struct cpts *cpts;
struct devlink *devlink; struct devlink *devlink;
int rx_ch_num, tx_ch_num; int rx_ch_num, tx_ch_num;
...@@ -442,6 +443,7 @@ int cpsw_run_xdp(struct cpsw_priv *priv, int ch, struct xdp_buff *xdp, ...@@ -442,6 +443,7 @@ int cpsw_run_xdp(struct cpsw_priv *priv, int ch, struct xdp_buff *xdp,
struct page *page, int port); struct page *page, int port);
irqreturn_t cpsw_tx_interrupt(int irq, void *dev_id); irqreturn_t cpsw_tx_interrupt(int irq, void *dev_id);
irqreturn_t cpsw_rx_interrupt(int irq, void *dev_id); irqreturn_t cpsw_rx_interrupt(int irq, void *dev_id);
irqreturn_t cpsw_misc_interrupt(int irq, void *dev_id);
int cpsw_tx_mq_poll(struct napi_struct *napi_tx, int budget); int cpsw_tx_mq_poll(struct napi_struct *napi_tx, int budget);
int cpsw_tx_poll(struct napi_struct *napi_tx, int budget); int cpsw_tx_poll(struct napi_struct *napi_tx, int budget);
int cpsw_rx_mq_poll(struct napi_struct *napi_rx, int budget); int cpsw_rx_mq_poll(struct napi_struct *napi_rx, int budget);
......
...@@ -21,16 +21,21 @@ ...@@ -21,16 +21,21 @@
#include "cpts.h" #include "cpts.h"
#define CPTS_SKB_TX_WORK_TIMEOUT 1 /* jiffies */ #define CPTS_SKB_TX_WORK_TIMEOUT 1 /* jiffies */
#define CPTS_SKB_RX_TX_TMO 100 /*ms */
#define CPTS_EVENT_RX_TX_TIMEOUT (100) /* ms */
struct cpts_skb_cb_data { struct cpts_skb_cb_data {
u32 skb_mtype_seqid;
unsigned long tmo; unsigned long tmo;
}; };
#define cpts_read32(c, r) readl_relaxed(&c->reg->r) #define cpts_read32(c, r) readl_relaxed(&c->reg->r)
#define cpts_write32(c, v, r) writel_relaxed(v, &c->reg->r) #define cpts_write32(c, v, r) writel_relaxed(v, &c->reg->r)
static int cpts_match(struct sk_buff *skb, unsigned int ptp_class, static int cpts_event_port(struct cpts_event *event)
u16 ts_seqid, u8 ts_msgtype); {
return (event->high >> PORT_NUMBER_SHIFT) & PORT_NUMBER_MASK;
}
static int event_expired(struct cpts_event *event) static int event_expired(struct cpts_event *event)
{ {
...@@ -71,7 +76,7 @@ static int cpts_purge_events(struct cpts *cpts) ...@@ -71,7 +76,7 @@ static int cpts_purge_events(struct cpts *cpts)
} }
if (removed) if (removed)
pr_debug("cpts: event pool cleaned up %d\n", removed); dev_dbg(cpts->dev, "cpts: event pool cleaned up %d\n", removed);
return removed ? 0 : -1; return removed ? 0 : -1;
} }
...@@ -94,132 +99,126 @@ static void cpts_purge_txq(struct cpts *cpts) ...@@ -94,132 +99,126 @@ static void cpts_purge_txq(struct cpts *cpts)
dev_dbg(cpts->dev, "txq cleaned up %d\n", removed); dev_dbg(cpts->dev, "txq cleaned up %d\n", removed);
} }
static bool cpts_match_tx_ts(struct cpts *cpts, struct cpts_event *event)
{
struct sk_buff *skb, *tmp;
u16 seqid;
u8 mtype;
bool found = false;
mtype = (event->high >> MESSAGE_TYPE_SHIFT) & MESSAGE_TYPE_MASK;
seqid = (event->high >> SEQUENCE_ID_SHIFT) & SEQUENCE_ID_MASK;
/* no need to grab txq.lock as access is always done under cpts->lock */
skb_queue_walk_safe(&cpts->txq, skb, tmp) {
struct skb_shared_hwtstamps ssh;
unsigned int class = ptp_classify_raw(skb);
struct cpts_skb_cb_data *skb_cb =
(struct cpts_skb_cb_data *)skb->cb;
if (cpts_match(skb, class, seqid, mtype)) {
u64 ns = timecounter_cyc2time(&cpts->tc, event->low);
memset(&ssh, 0, sizeof(ssh));
ssh.hwtstamp = ns_to_ktime(ns);
skb_tstamp_tx(skb, &ssh);
found = true;
__skb_unlink(skb, &cpts->txq);
dev_consume_skb_any(skb);
dev_dbg(cpts->dev, "match tx timestamp mtype %u seqid %04x\n",
mtype, seqid);
break;
}
if (time_after(jiffies, skb_cb->tmo)) {
/* timeout any expired skbs over 1s */
dev_dbg(cpts->dev, "expiring tx timestamp from txq\n");
__skb_unlink(skb, &cpts->txq);
dev_consume_skb_any(skb);
}
}
return found;
}
/* /*
* Returns zero if matching event type was found. * Returns zero if matching event type was found.
*/ */
static int cpts_fifo_read(struct cpts *cpts, int match) static int cpts_fifo_read(struct cpts *cpts, int match)
{ {
struct ptp_clock_event pevent;
bool need_schedule = false;
struct cpts_event *event;
unsigned long flags;
int i, type = -1; int i, type = -1;
u32 hi, lo; u32 hi, lo;
struct cpts_event *event;
spin_lock_irqsave(&cpts->lock, flags);
for (i = 0; i < CPTS_FIFO_DEPTH; i++) { for (i = 0; i < CPTS_FIFO_DEPTH; i++) {
if (cpts_fifo_pop(cpts, &hi, &lo)) if (cpts_fifo_pop(cpts, &hi, &lo))
break; break;
if (list_empty(&cpts->pool) && cpts_purge_events(cpts)) { if (list_empty(&cpts->pool) && cpts_purge_events(cpts)) {
pr_err("cpts: event pool empty\n"); dev_warn(cpts->dev, "cpts: event pool empty\n");
return -1; break;
} }
event = list_first_entry(&cpts->pool, struct cpts_event, list); event = list_first_entry(&cpts->pool, struct cpts_event, list);
event->tmo = jiffies + 2;
event->high = hi; event->high = hi;
event->low = lo; event->low = lo;
event->timestamp = timecounter_cyc2time(&cpts->tc, event->low);
type = event_type(event); type = event_type(event);
dev_dbg(cpts->dev, "CPTS_EV: %d high:%08X low:%08x\n",
type, event->high, event->low);
switch (type) { switch (type) {
case CPTS_EV_TX:
if (cpts_match_tx_ts(cpts, event)) {
/* if the new event matches an existing skb,
* then don't queue it
*/
break;
}
/* fall through */
case CPTS_EV_PUSH: case CPTS_EV_PUSH:
WRITE_ONCE(cpts->cur_timestamp, lo);
timecounter_read(&cpts->tc);
if (cpts->mult_new) {
cpts->cc.mult = cpts->mult_new;
cpts->mult_new = 0;
}
if (!cpts->irq_poll)
complete(&cpts->ts_push_complete);
break;
case CPTS_EV_TX:
case CPTS_EV_RX: case CPTS_EV_RX:
event->tmo = jiffies +
msecs_to_jiffies(CPTS_EVENT_RX_TX_TIMEOUT);
list_del_init(&event->list); list_del_init(&event->list);
list_add_tail(&event->list, &cpts->events); list_add_tail(&event->list, &cpts->events);
need_schedule = true;
break; break;
case CPTS_EV_ROLL: case CPTS_EV_ROLL:
case CPTS_EV_HALF: case CPTS_EV_HALF:
break;
case CPTS_EV_HW: case CPTS_EV_HW:
pevent.timestamp = event->timestamp;
pevent.type = PTP_CLOCK_EXTTS;
pevent.index = cpts_event_port(event) - 1;
ptp_clock_event(cpts->clock, &pevent);
break; break;
default: default:
pr_err("cpts: unknown event type\n"); dev_err(cpts->dev, "cpts: unknown event type\n");
break; break;
} }
if (type == match) if (type == match)
break; break;
} }
spin_unlock_irqrestore(&cpts->lock, flags);
if (!cpts->irq_poll && need_schedule)
ptp_schedule_worker(cpts->clock, 0);
return type == match ? 0 : -1; return type == match ? 0 : -1;
} }
void cpts_misc_interrupt(struct cpts *cpts)
{
cpts_fifo_read(cpts, -1);
}
EXPORT_SYMBOL_GPL(cpts_misc_interrupt);
static u64 cpts_systim_read(const struct cyclecounter *cc) static u64 cpts_systim_read(const struct cyclecounter *cc)
{ {
u64 val = 0;
struct cpts_event *event;
struct list_head *this, *next;
struct cpts *cpts = container_of(cc, struct cpts, cc); struct cpts *cpts = container_of(cc, struct cpts, cc);
return READ_ONCE(cpts->cur_timestamp);
}
static void cpts_update_cur_time(struct cpts *cpts, int match,
struct ptp_system_timestamp *sts)
{
unsigned long flags;
reinit_completion(&cpts->ts_push_complete);
/* use spin_lock_irqsave() here as it has to run very fast */
spin_lock_irqsave(&cpts->lock, flags);
ptp_read_system_prets(sts);
cpts_write32(cpts, TS_PUSH, ts_push); cpts_write32(cpts, TS_PUSH, ts_push);
if (cpts_fifo_read(cpts, CPTS_EV_PUSH)) cpts_read32(cpts, ts_push);
pr_err("cpts: unable to obtain a time stamp\n"); ptp_read_system_postts(sts);
spin_unlock_irqrestore(&cpts->lock, flags);
list_for_each_safe(this, next, &cpts->events) { if (cpts->irq_poll && cpts_fifo_read(cpts, match) && match != -1)
event = list_entry(this, struct cpts_event, list); dev_err(cpts->dev, "cpts: unable to obtain a time stamp\n");
if (event_type(event) == CPTS_EV_PUSH) {
list_del_init(&event->list);
list_add(&event->list, &cpts->pool);
val = event->low;
break;
}
}
return val; if (!cpts->irq_poll &&
!wait_for_completion_timeout(&cpts->ts_push_complete, HZ))
dev_err(cpts->dev, "cpts: obtain a time stamp timeout\n");
} }
/* PTP clock operations */ /* PTP clock operations */
static int cpts_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) static int cpts_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
{ {
u64 adj;
u32 diff, mult;
int neg_adj = 0;
unsigned long flags;
struct cpts *cpts = container_of(ptp, struct cpts, info); struct cpts *cpts = container_of(ptp, struct cpts, info);
int neg_adj = 0;
u32 diff, mult;
u64 adj;
if (ppb < 0) { if (ppb < 0) {
neg_adj = 1; neg_adj = 1;
...@@ -230,38 +229,40 @@ static int cpts_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) ...@@ -230,38 +229,40 @@ static int cpts_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
adj *= ppb; adj *= ppb;
diff = div_u64(adj, 1000000000ULL); diff = div_u64(adj, 1000000000ULL);
spin_lock_irqsave(&cpts->lock, flags); mutex_lock(&cpts->ptp_clk_mutex);
timecounter_read(&cpts->tc); cpts->mult_new = neg_adj ? mult - diff : mult + diff;
cpts->cc.mult = neg_adj ? mult - diff : mult + diff; cpts_update_cur_time(cpts, CPTS_EV_PUSH, NULL);
spin_unlock_irqrestore(&cpts->lock, flags);
mutex_unlock(&cpts->ptp_clk_mutex);
return 0; return 0;
} }
static int cpts_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) static int cpts_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
{ {
unsigned long flags;
struct cpts *cpts = container_of(ptp, struct cpts, info); struct cpts *cpts = container_of(ptp, struct cpts, info);
spin_lock_irqsave(&cpts->lock, flags); mutex_lock(&cpts->ptp_clk_mutex);
timecounter_adjtime(&cpts->tc, delta); timecounter_adjtime(&cpts->tc, delta);
spin_unlock_irqrestore(&cpts->lock, flags); mutex_unlock(&cpts->ptp_clk_mutex);
return 0; return 0;
} }
static int cpts_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) static int cpts_ptp_gettimeex(struct ptp_clock_info *ptp,
struct timespec64 *ts,
struct ptp_system_timestamp *sts)
{ {
u64 ns;
unsigned long flags;
struct cpts *cpts = container_of(ptp, struct cpts, info); struct cpts *cpts = container_of(ptp, struct cpts, info);
u64 ns;
mutex_lock(&cpts->ptp_clk_mutex);
cpts_update_cur_time(cpts, CPTS_EV_PUSH, sts);
spin_lock_irqsave(&cpts->lock, flags);
ns = timecounter_read(&cpts->tc); ns = timecounter_read(&cpts->tc);
spin_unlock_irqrestore(&cpts->lock, flags); mutex_unlock(&cpts->ptp_clk_mutex);
*ts = ns_to_timespec64(ns); *ts = ns_to_timespec64(ns);
...@@ -271,15 +272,38 @@ static int cpts_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) ...@@ -271,15 +272,38 @@ static int cpts_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
static int cpts_ptp_settime(struct ptp_clock_info *ptp, static int cpts_ptp_settime(struct ptp_clock_info *ptp,
const struct timespec64 *ts) const struct timespec64 *ts)
{ {
u64 ns;
unsigned long flags;
struct cpts *cpts = container_of(ptp, struct cpts, info); struct cpts *cpts = container_of(ptp, struct cpts, info);
u64 ns;
ns = timespec64_to_ns(ts); ns = timespec64_to_ns(ts);
spin_lock_irqsave(&cpts->lock, flags); mutex_lock(&cpts->ptp_clk_mutex);
timecounter_init(&cpts->tc, &cpts->cc, ns); timecounter_init(&cpts->tc, &cpts->cc, ns);
spin_unlock_irqrestore(&cpts->lock, flags); mutex_unlock(&cpts->ptp_clk_mutex);
return 0;
}
static int cpts_extts_enable(struct cpts *cpts, u32 index, int on)
{
u32 v;
if (((cpts->hw_ts_enable & BIT(index)) >> index) == on)
return 0;
mutex_lock(&cpts->ptp_clk_mutex);
v = cpts_read32(cpts, control);
if (on) {
v |= BIT(8 + index);
cpts->hw_ts_enable |= BIT(index);
} else {
v &= ~BIT(8 + index);
cpts->hw_ts_enable &= ~BIT(index);
}
cpts_write32(cpts, v, control);
mutex_unlock(&cpts->ptp_clk_mutex);
return 0; return 0;
} }
...@@ -287,28 +311,120 @@ static int cpts_ptp_settime(struct ptp_clock_info *ptp, ...@@ -287,28 +311,120 @@ static int cpts_ptp_settime(struct ptp_clock_info *ptp,
static int cpts_ptp_enable(struct ptp_clock_info *ptp, static int cpts_ptp_enable(struct ptp_clock_info *ptp,
struct ptp_clock_request *rq, int on) struct ptp_clock_request *rq, int on)
{ {
struct cpts *cpts = container_of(ptp, struct cpts, info);
switch (rq->type) {
case PTP_CLK_REQ_EXTTS:
return cpts_extts_enable(cpts, rq->extts.index, on);
default:
break;
}
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
static bool cpts_match_tx_ts(struct cpts *cpts, struct cpts_event *event)
{
struct sk_buff_head txq_list;
struct sk_buff *skb, *tmp;
unsigned long flags;
bool found = false;
u32 mtype_seqid;
mtype_seqid = event->high &
((MESSAGE_TYPE_MASK << MESSAGE_TYPE_SHIFT) |
(SEQUENCE_ID_MASK << SEQUENCE_ID_SHIFT) |
(EVENT_TYPE_MASK << EVENT_TYPE_SHIFT));
__skb_queue_head_init(&txq_list);
spin_lock_irqsave(&cpts->txq.lock, flags);
skb_queue_splice_init(&cpts->txq, &txq_list);
spin_unlock_irqrestore(&cpts->txq.lock, flags);
skb_queue_walk_safe(&txq_list, skb, tmp) {
struct skb_shared_hwtstamps ssh;
struct cpts_skb_cb_data *skb_cb =
(struct cpts_skb_cb_data *)skb->cb;
if (mtype_seqid == skb_cb->skb_mtype_seqid) {
memset(&ssh, 0, sizeof(ssh));
ssh.hwtstamp = ns_to_ktime(event->timestamp);
skb_tstamp_tx(skb, &ssh);
found = true;
__skb_unlink(skb, &txq_list);
dev_consume_skb_any(skb);
dev_dbg(cpts->dev, "match tx timestamp mtype_seqid %08x\n",
mtype_seqid);
break;
}
if (time_after(jiffies, skb_cb->tmo)) {
/* timeout any expired skbs over 1s */
dev_dbg(cpts->dev, "expiring tx timestamp from txq\n");
__skb_unlink(skb, &txq_list);
dev_consume_skb_any(skb);
}
}
spin_lock_irqsave(&cpts->txq.lock, flags);
skb_queue_splice(&txq_list, &cpts->txq);
spin_unlock_irqrestore(&cpts->txq.lock, flags);
return found;
}
static void cpts_process_events(struct cpts *cpts)
{
struct list_head *this, *next;
struct cpts_event *event;
LIST_HEAD(events_free);
unsigned long flags;
LIST_HEAD(events);
spin_lock_irqsave(&cpts->lock, flags);
list_splice_init(&cpts->events, &events);
spin_unlock_irqrestore(&cpts->lock, flags);
list_for_each_safe(this, next, &events) {
event = list_entry(this, struct cpts_event, list);
if (cpts_match_tx_ts(cpts, event) ||
time_after(jiffies, event->tmo)) {
list_del_init(&event->list);
list_add(&event->list, &events_free);
}
}
spin_lock_irqsave(&cpts->lock, flags);
list_splice_tail(&events, &cpts->events);
list_splice_tail(&events_free, &cpts->pool);
spin_unlock_irqrestore(&cpts->lock, flags);
}
static long cpts_overflow_check(struct ptp_clock_info *ptp) static long cpts_overflow_check(struct ptp_clock_info *ptp)
{ {
struct cpts *cpts = container_of(ptp, struct cpts, info); struct cpts *cpts = container_of(ptp, struct cpts, info);
unsigned long delay = cpts->ov_check_period; unsigned long delay = cpts->ov_check_period;
struct timespec64 ts;
unsigned long flags; unsigned long flags;
u64 ns;
spin_lock_irqsave(&cpts->lock, flags); mutex_lock(&cpts->ptp_clk_mutex);
ts = ns_to_timespec64(timecounter_read(&cpts->tc));
cpts_update_cur_time(cpts, -1, NULL);
ns = timecounter_read(&cpts->tc);
cpts_process_events(cpts);
spin_lock_irqsave(&cpts->txq.lock, flags);
if (!skb_queue_empty(&cpts->txq)) { if (!skb_queue_empty(&cpts->txq)) {
cpts_purge_txq(cpts); cpts_purge_txq(cpts);
if (!skb_queue_empty(&cpts->txq)) if (!skb_queue_empty(&cpts->txq))
delay = CPTS_SKB_TX_WORK_TIMEOUT; delay = CPTS_SKB_TX_WORK_TIMEOUT;
} }
spin_unlock_irqrestore(&cpts->lock, flags); spin_unlock_irqrestore(&cpts->txq.lock, flags);
pr_debug("cpts overflow check at %lld.%09ld\n", dev_dbg(cpts->dev, "cpts overflow check at %lld\n", ns);
(long long)ts.tv_sec, ts.tv_nsec); mutex_unlock(&cpts->ptp_clk_mutex);
return (long)delay; return (long)delay;
} }
...@@ -321,18 +437,21 @@ static const struct ptp_clock_info cpts_info = { ...@@ -321,18 +437,21 @@ static const struct ptp_clock_info cpts_info = {
.pps = 0, .pps = 0,
.adjfreq = cpts_ptp_adjfreq, .adjfreq = cpts_ptp_adjfreq,
.adjtime = cpts_ptp_adjtime, .adjtime = cpts_ptp_adjtime,
.gettime64 = cpts_ptp_gettime, .gettimex64 = cpts_ptp_gettimeex,
.settime64 = cpts_ptp_settime, .settime64 = cpts_ptp_settime,
.enable = cpts_ptp_enable, .enable = cpts_ptp_enable,
.do_aux_work = cpts_overflow_check, .do_aux_work = cpts_overflow_check,
}; };
static int cpts_match(struct sk_buff *skb, unsigned int ptp_class, static int cpts_skb_get_mtype_seqid(struct sk_buff *skb, u32 *mtype_seqid)
u16 ts_seqid, u8 ts_msgtype)
{ {
u16 *seqid; unsigned int ptp_class = ptp_classify_raw(skb);
unsigned int offset = 0;
u8 *msgtype, *data = skb->data; u8 *msgtype, *data = skb->data;
unsigned int offset = 0;
u16 *seqid;
if (ptp_class == PTP_CLASS_NONE)
return 0;
if (ptp_class & PTP_CLASS_VLAN) if (ptp_class & PTP_CLASS_VLAN)
offset += VLAN_HLEN; offset += VLAN_HLEN;
...@@ -360,25 +479,23 @@ static int cpts_match(struct sk_buff *skb, unsigned int ptp_class, ...@@ -360,25 +479,23 @@ static int cpts_match(struct sk_buff *skb, unsigned int ptp_class,
msgtype = data + offset; msgtype = data + offset;
seqid = (u16 *)(data + offset + OFF_PTP_SEQUENCE_ID); seqid = (u16 *)(data + offset + OFF_PTP_SEQUENCE_ID);
*mtype_seqid = (*msgtype & MESSAGE_TYPE_MASK) << MESSAGE_TYPE_SHIFT;
*mtype_seqid |= (ntohs(*seqid) & SEQUENCE_ID_MASK) << SEQUENCE_ID_SHIFT;
return (ts_msgtype == (*msgtype & 0xf) && ts_seqid == ntohs(*seqid)); return 1;
} }
static u64 cpts_find_ts(struct cpts *cpts, struct sk_buff *skb, int ev_type) static u64 cpts_find_ts(struct cpts *cpts, struct sk_buff *skb,
int ev_type, u32 skb_mtype_seqid)
{ {
u64 ns = 0;
struct cpts_event *event;
struct list_head *this, *next; struct list_head *this, *next;
unsigned int class = ptp_classify_raw(skb); struct cpts_event *event;
unsigned long flags; unsigned long flags;
u16 seqid; u32 mtype_seqid;
u8 mtype; u64 ns = 0;
if (class == PTP_CLASS_NONE)
return 0;
spin_lock_irqsave(&cpts->lock, flags);
cpts_fifo_read(cpts, -1); cpts_fifo_read(cpts, -1);
spin_lock_irqsave(&cpts->lock, flags);
list_for_each_safe(this, next, &cpts->events) { list_for_each_safe(this, next, &cpts->events) {
event = list_entry(this, struct cpts_event, list); event = list_entry(this, struct cpts_event, list);
if (event_expired(event)) { if (event_expired(event)) {
...@@ -386,29 +503,19 @@ static u64 cpts_find_ts(struct cpts *cpts, struct sk_buff *skb, int ev_type) ...@@ -386,29 +503,19 @@ static u64 cpts_find_ts(struct cpts *cpts, struct sk_buff *skb, int ev_type)
list_add(&event->list, &cpts->pool); list_add(&event->list, &cpts->pool);
continue; continue;
} }
mtype = (event->high >> MESSAGE_TYPE_SHIFT) & MESSAGE_TYPE_MASK;
seqid = (event->high >> SEQUENCE_ID_SHIFT) & SEQUENCE_ID_MASK; mtype_seqid = event->high &
if (ev_type == event_type(event) && ((MESSAGE_TYPE_MASK << MESSAGE_TYPE_SHIFT) |
cpts_match(skb, class, seqid, mtype)) { (SEQUENCE_ID_MASK << SEQUENCE_ID_SHIFT) |
ns = timecounter_cyc2time(&cpts->tc, event->low); (EVENT_TYPE_MASK << EVENT_TYPE_SHIFT));
if (mtype_seqid == skb_mtype_seqid) {
ns = event->timestamp;
list_del_init(&event->list); list_del_init(&event->list);
list_add(&event->list, &cpts->pool); list_add(&event->list, &cpts->pool);
break; break;
} }
} }
if (ev_type == CPTS_EV_TX && !ns) {
struct cpts_skb_cb_data *skb_cb =
(struct cpts_skb_cb_data *)skb->cb;
/* Not found, add frame to queue for processing later.
* The periodic FIFO check will handle this.
*/
skb_get(skb);
/* get the timestamp for timeouts */
skb_cb->tmo = jiffies + msecs_to_jiffies(100);
__skb_queue_tail(&cpts->txq, skb);
ptp_schedule_worker(cpts->clock, 0);
}
spin_unlock_irqrestore(&cpts->lock, flags); spin_unlock_irqrestore(&cpts->lock, flags);
return ns; return ns;
...@@ -416,10 +523,21 @@ static u64 cpts_find_ts(struct cpts *cpts, struct sk_buff *skb, int ev_type) ...@@ -416,10 +523,21 @@ static u64 cpts_find_ts(struct cpts *cpts, struct sk_buff *skb, int ev_type)
void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb) void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb)
{ {
u64 ns; struct cpts_skb_cb_data *skb_cb = (struct cpts_skb_cb_data *)skb->cb;
struct skb_shared_hwtstamps *ssh; struct skb_shared_hwtstamps *ssh;
int ret;
u64 ns;
ns = cpts_find_ts(cpts, skb, CPTS_EV_RX); ret = cpts_skb_get_mtype_seqid(skb, &skb_cb->skb_mtype_seqid);
if (!ret)
return;
skb_cb->skb_mtype_seqid |= (CPTS_EV_RX << EVENT_TYPE_SHIFT);
dev_dbg(cpts->dev, "%s mtype seqid %08x\n",
__func__, skb_cb->skb_mtype_seqid);
ns = cpts_find_ts(cpts, skb, CPTS_EV_RX, skb_cb->skb_mtype_seqid);
if (!ns) if (!ns)
return; return;
ssh = skb_hwtstamps(skb); ssh = skb_hwtstamps(skb);
...@@ -430,17 +548,27 @@ EXPORT_SYMBOL_GPL(cpts_rx_timestamp); ...@@ -430,17 +548,27 @@ EXPORT_SYMBOL_GPL(cpts_rx_timestamp);
void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb) void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb)
{ {
u64 ns; struct cpts_skb_cb_data *skb_cb = (struct cpts_skb_cb_data *)skb->cb;
struct skb_shared_hwtstamps ssh; int ret;
if (!(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) if (!(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS))
return; return;
ns = cpts_find_ts(cpts, skb, CPTS_EV_TX);
if (!ns) ret = cpts_skb_get_mtype_seqid(skb, &skb_cb->skb_mtype_seqid);
if (!ret)
return; return;
memset(&ssh, 0, sizeof(ssh));
ssh.hwtstamp = ns_to_ktime(ns); skb_cb->skb_mtype_seqid |= (CPTS_EV_TX << EVENT_TYPE_SHIFT);
skb_tstamp_tx(skb, &ssh);
dev_dbg(cpts->dev, "%s mtype seqid %08x\n",
__func__, skb_cb->skb_mtype_seqid);
/* Always defer TX TS processing to PTP worker */
skb_get(skb);
/* get the timestamp for timeouts */
skb_cb->tmo = jiffies + msecs_to_jiffies(CPTS_SKB_RX_TX_TMO);
skb_queue_tail(&cpts->txq, skb);
ptp_schedule_worker(cpts->clock, 0);
} }
EXPORT_SYMBOL_GPL(cpts_tx_timestamp); EXPORT_SYMBOL_GPL(cpts_tx_timestamp);
...@@ -632,7 +760,7 @@ static int cpts_of_parse(struct cpts *cpts, struct device_node *node) ...@@ -632,7 +760,7 @@ static int cpts_of_parse(struct cpts *cpts, struct device_node *node)
} }
struct cpts *cpts_create(struct device *dev, void __iomem *regs, struct cpts *cpts_create(struct device *dev, void __iomem *regs,
struct device_node *node) struct device_node *node, u32 n_ext_ts)
{ {
struct cpts *cpts; struct cpts *cpts;
int ret; int ret;
...@@ -643,7 +771,10 @@ struct cpts *cpts_create(struct device *dev, void __iomem *regs, ...@@ -643,7 +771,10 @@ struct cpts *cpts_create(struct device *dev, void __iomem *regs,
cpts->dev = dev; cpts->dev = dev;
cpts->reg = (struct cpsw_cpts __iomem *)regs; cpts->reg = (struct cpsw_cpts __iomem *)regs;
cpts->irq_poll = true;
spin_lock_init(&cpts->lock); spin_lock_init(&cpts->lock);
mutex_init(&cpts->ptp_clk_mutex);
init_completion(&cpts->ts_push_complete);
ret = cpts_of_parse(cpts, node); ret = cpts_of_parse(cpts, node);
if (ret) if (ret)
...@@ -668,6 +799,9 @@ struct cpts *cpts_create(struct device *dev, void __iomem *regs, ...@@ -668,6 +799,9 @@ struct cpts *cpts_create(struct device *dev, void __iomem *regs,
cpts->cc.mask = CLOCKSOURCE_MASK(32); cpts->cc.mask = CLOCKSOURCE_MASK(32);
cpts->info = cpts_info; cpts->info = cpts_info;
if (n_ext_ts)
cpts->info.n_ext_ts = n_ext_ts;
cpts_calc_mult_shift(cpts); cpts_calc_mult_shift(cpts);
/* save cc.mult original value as it can be modified /* save cc.mult original value as it can be modified
* by cpts_ptp_adjfreq(). * by cpts_ptp_adjfreq().
......
...@@ -94,6 +94,7 @@ struct cpts_event { ...@@ -94,6 +94,7 @@ struct cpts_event {
unsigned long tmo; unsigned long tmo;
u32 high; u32 high;
u32 low; u32 low;
u64 timestamp;
}; };
struct cpts { struct cpts {
...@@ -103,7 +104,7 @@ struct cpts { ...@@ -103,7 +104,7 @@ struct cpts {
int rx_enable; int rx_enable;
struct ptp_clock_info info; struct ptp_clock_info info;
struct ptp_clock *clock; struct ptp_clock *clock;
spinlock_t lock; /* protects time registers */ spinlock_t lock; /* protects fifo/events */
u32 cc_mult; /* for the nominal frequency */ u32 cc_mult; /* for the nominal frequency */
struct cyclecounter cc; struct cyclecounter cc;
struct timecounter tc; struct timecounter tc;
...@@ -114,6 +115,12 @@ struct cpts { ...@@ -114,6 +115,12 @@ struct cpts {
struct cpts_event pool_data[CPTS_MAX_EVENTS]; struct cpts_event pool_data[CPTS_MAX_EVENTS];
unsigned long ov_check_period; unsigned long ov_check_period;
struct sk_buff_head txq; struct sk_buff_head txq;
u64 cur_timestamp;
u32 mult_new;
struct mutex ptp_clk_mutex; /* sync PTP interface and worker */
bool irq_poll;
struct completion ts_push_complete;
u32 hw_ts_enable;
}; };
void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb); void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb);
...@@ -121,8 +128,9 @@ void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb); ...@@ -121,8 +128,9 @@ void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb);
int cpts_register(struct cpts *cpts); int cpts_register(struct cpts *cpts);
void cpts_unregister(struct cpts *cpts); void cpts_unregister(struct cpts *cpts);
struct cpts *cpts_create(struct device *dev, void __iomem *regs, struct cpts *cpts_create(struct device *dev, void __iomem *regs,
struct device_node *node); struct device_node *node, u32 n_ext_ts);
void cpts_release(struct cpts *cpts); void cpts_release(struct cpts *cpts);
void cpts_misc_interrupt(struct cpts *cpts);
static inline bool cpts_can_timestamp(struct cpts *cpts, struct sk_buff *skb) static inline bool cpts_can_timestamp(struct cpts *cpts, struct sk_buff *skb)
{ {
...@@ -134,6 +142,11 @@ static inline bool cpts_can_timestamp(struct cpts *cpts, struct sk_buff *skb) ...@@ -134,6 +142,11 @@ static inline bool cpts_can_timestamp(struct cpts *cpts, struct sk_buff *skb)
return true; return true;
} }
static inline void cpts_set_irqpoll(struct cpts *cpts, bool en)
{
cpts->irq_poll = en;
}
#else #else
struct cpts; struct cpts;
...@@ -146,7 +159,7 @@ static inline void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb) ...@@ -146,7 +159,7 @@ static inline void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb)
static inline static inline
struct cpts *cpts_create(struct device *dev, void __iomem *regs, struct cpts *cpts_create(struct device *dev, void __iomem *regs,
struct device_node *node) struct device_node *node, u32 n_ext_ts)
{ {
return NULL; return NULL;
} }
...@@ -169,6 +182,14 @@ static inline bool cpts_can_timestamp(struct cpts *cpts, struct sk_buff *skb) ...@@ -169,6 +182,14 @@ static inline bool cpts_can_timestamp(struct cpts *cpts, struct sk_buff *skb)
{ {
return false; return false;
} }
static inline void cpts_misc_interrupt(struct cpts *cpts)
{
}
static inline void cpts_set_irqpoll(struct cpts *cpts, bool en)
{
}
#endif #endif
......
...@@ -3716,7 +3716,8 @@ static int gbe_probe(struct netcp_device *netcp_device, struct device *dev, ...@@ -3716,7 +3716,8 @@ static int gbe_probe(struct netcp_device *netcp_device, struct device *dev,
if (!cpts_node) if (!cpts_node)
cpts_node = of_node_get(node); cpts_node = of_node_get(node);
gbe_dev->cpts = cpts_create(gbe_dev->dev, gbe_dev->cpts_reg, cpts_node); gbe_dev->cpts = cpts_create(gbe_dev->dev, gbe_dev->cpts_reg,
cpts_node, 0);
of_node_put(cpts_node); of_node_put(cpts_node);
if (IS_ENABLED(CONFIG_TI_CPTS) && IS_ERR(gbe_dev->cpts)) { if (IS_ENABLED(CONFIG_TI_CPTS) && IS_ERR(gbe_dev->cpts)) {
ret = PTR_ERR(gbe_dev->cpts); ret = PTR_ERR(gbe_dev->cpts);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册