187c0e764SRichard Cochran /* 287c0e764SRichard Cochran * TI Common Platform Time Sync 387c0e764SRichard Cochran * 487c0e764SRichard Cochran * Copyright (C) 2012 Richard Cochran <richardcochran@gmail.com> 587c0e764SRichard Cochran * 687c0e764SRichard Cochran * This program is free software; you can redistribute it and/or modify 787c0e764SRichard Cochran * it under the terms of the GNU General Public License as published by 887c0e764SRichard Cochran * the Free Software Foundation; either version 2 of the License, or 987c0e764SRichard Cochran * (at your option) any later version. 1087c0e764SRichard Cochran * 1187c0e764SRichard Cochran * This program is distributed in the hope that it will be useful, 1287c0e764SRichard Cochran * but WITHOUT ANY WARRANTY; without even the implied warranty of 1387c0e764SRichard Cochran * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1487c0e764SRichard Cochran * GNU General Public License for more details. 1587c0e764SRichard Cochran * 1687c0e764SRichard Cochran * You should have received a copy of the GNU General Public License 1787c0e764SRichard Cochran * along with this program; if not, write to the Free Software 1887c0e764SRichard Cochran * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 1987c0e764SRichard Cochran */ 2087c0e764SRichard Cochran #include <linux/err.h> 2187c0e764SRichard Cochran #include <linux/if.h> 2287c0e764SRichard Cochran #include <linux/hrtimer.h> 2387c0e764SRichard Cochran #include <linux/module.h> 2487c0e764SRichard Cochran #include <linux/net_tstamp.h> 2587c0e764SRichard Cochran #include <linux/ptp_classify.h> 2687c0e764SRichard Cochran #include <linux/time.h> 2787c0e764SRichard Cochran #include <linux/uaccess.h> 2887c0e764SRichard Cochran #include <linux/workqueue.h> 2979eb9d28SAlexei Starovoitov #include <linux/if_ether.h> 3079eb9d28SAlexei Starovoitov #include <linux/if_vlan.h> 3187c0e764SRichard Cochran 3287c0e764SRichard Cochran #include "cpts.h" 3387c0e764SRichard Cochran 34*0d5f54feSGrygorii Strashko #define CPTS_SKB_TX_WORK_TIMEOUT 1 /* jiffies */ 35*0d5f54feSGrygorii Strashko 36*0d5f54feSGrygorii Strashko struct cpts_skb_cb_data { 37*0d5f54feSGrygorii Strashko unsigned long tmo; 38*0d5f54feSGrygorii Strashko }; 39*0d5f54feSGrygorii Strashko 40391fd6caSGrygorii Strashko #define cpts_read32(c, r) readl_relaxed(&c->reg->r) 41391fd6caSGrygorii Strashko #define cpts_write32(c, v, r) writel_relaxed(v, &c->reg->r) 4287c0e764SRichard Cochran 43*0d5f54feSGrygorii Strashko static int cpts_match(struct sk_buff *skb, unsigned int ptp_class, 44*0d5f54feSGrygorii Strashko u16 ts_seqid, u8 ts_msgtype); 45*0d5f54feSGrygorii Strashko 4687c0e764SRichard Cochran static int event_expired(struct cpts_event *event) 4787c0e764SRichard Cochran { 4887c0e764SRichard Cochran return time_after(jiffies, event->tmo); 4987c0e764SRichard Cochran } 5087c0e764SRichard Cochran 5187c0e764SRichard Cochran static int event_type(struct cpts_event *event) 5287c0e764SRichard Cochran { 5387c0e764SRichard Cochran return (event->high >> EVENT_TYPE_SHIFT) & EVENT_TYPE_MASK; 5487c0e764SRichard Cochran } 5587c0e764SRichard Cochran 5687c0e764SRichard Cochran static int cpts_fifo_pop(struct cpts *cpts, u32 *high, u32 *low) 5787c0e764SRichard Cochran { 5887c0e764SRichard Cochran u32 r = cpts_read32(cpts, intstat_raw); 5987c0e764SRichard Cochran 6087c0e764SRichard Cochran if (r & TS_PEND_RAW) { 6187c0e764SRichard Cochran *high = cpts_read32(cpts, event_high); 6287c0e764SRichard Cochran *low = cpts_read32(cpts, event_low); 6387c0e764SRichard Cochran cpts_write32(cpts, EVENT_POP, event_pop); 6487c0e764SRichard Cochran return 0; 6587c0e764SRichard Cochran } 6687c0e764SRichard Cochran return -1; 6787c0e764SRichard Cochran } 6887c0e764SRichard Cochran 69e4439fa8SWingMan Kwok static int cpts_purge_events(struct cpts *cpts) 70e4439fa8SWingMan Kwok { 71e4439fa8SWingMan Kwok struct list_head *this, *next; 72e4439fa8SWingMan Kwok struct cpts_event *event; 73e4439fa8SWingMan Kwok int removed = 0; 74e4439fa8SWingMan Kwok 75e4439fa8SWingMan Kwok list_for_each_safe(this, next, &cpts->events) { 76e4439fa8SWingMan Kwok event = list_entry(this, struct cpts_event, list); 77e4439fa8SWingMan Kwok if (event_expired(event)) { 78e4439fa8SWingMan Kwok list_del_init(&event->list); 79e4439fa8SWingMan Kwok list_add(&event->list, &cpts->pool); 80e4439fa8SWingMan Kwok ++removed; 81e4439fa8SWingMan Kwok } 82e4439fa8SWingMan Kwok } 83e4439fa8SWingMan Kwok 84e4439fa8SWingMan Kwok if (removed) 85e4439fa8SWingMan Kwok pr_debug("cpts: event pool cleaned up %d\n", removed); 86e4439fa8SWingMan Kwok return removed ? 0 : -1; 87e4439fa8SWingMan Kwok } 88e4439fa8SWingMan Kwok 89*0d5f54feSGrygorii Strashko static bool cpts_match_tx_ts(struct cpts *cpts, struct cpts_event *event) 90*0d5f54feSGrygorii Strashko { 91*0d5f54feSGrygorii Strashko struct sk_buff *skb, *tmp; 92*0d5f54feSGrygorii Strashko u16 seqid; 93*0d5f54feSGrygorii Strashko u8 mtype; 94*0d5f54feSGrygorii Strashko bool found = false; 95*0d5f54feSGrygorii Strashko 96*0d5f54feSGrygorii Strashko mtype = (event->high >> MESSAGE_TYPE_SHIFT) & MESSAGE_TYPE_MASK; 97*0d5f54feSGrygorii Strashko seqid = (event->high >> SEQUENCE_ID_SHIFT) & SEQUENCE_ID_MASK; 98*0d5f54feSGrygorii Strashko 99*0d5f54feSGrygorii Strashko /* no need to grab txq.lock as access is always done under cpts->lock */ 100*0d5f54feSGrygorii Strashko skb_queue_walk_safe(&cpts->txq, skb, tmp) { 101*0d5f54feSGrygorii Strashko struct skb_shared_hwtstamps ssh; 102*0d5f54feSGrygorii Strashko unsigned int class = ptp_classify_raw(skb); 103*0d5f54feSGrygorii Strashko struct cpts_skb_cb_data *skb_cb = 104*0d5f54feSGrygorii Strashko (struct cpts_skb_cb_data *)skb->cb; 105*0d5f54feSGrygorii Strashko 106*0d5f54feSGrygorii Strashko if (cpts_match(skb, class, seqid, mtype)) { 107*0d5f54feSGrygorii Strashko u64 ns = timecounter_cyc2time(&cpts->tc, event->low); 108*0d5f54feSGrygorii Strashko 109*0d5f54feSGrygorii Strashko memset(&ssh, 0, sizeof(ssh)); 110*0d5f54feSGrygorii Strashko ssh.hwtstamp = ns_to_ktime(ns); 111*0d5f54feSGrygorii Strashko skb_tstamp_tx(skb, &ssh); 112*0d5f54feSGrygorii Strashko found = true; 113*0d5f54feSGrygorii Strashko __skb_unlink(skb, &cpts->txq); 114*0d5f54feSGrygorii Strashko dev_consume_skb_any(skb); 115*0d5f54feSGrygorii Strashko dev_dbg(cpts->dev, "match tx timestamp mtype %u seqid %04x\n", 116*0d5f54feSGrygorii Strashko mtype, seqid); 117*0d5f54feSGrygorii Strashko } else if (time_after(jiffies, skb_cb->tmo)) { 118*0d5f54feSGrygorii Strashko /* timeout any expired skbs over 1s */ 119*0d5f54feSGrygorii Strashko dev_dbg(cpts->dev, 120*0d5f54feSGrygorii Strashko "expiring tx timestamp mtype %u seqid %04x\n", 121*0d5f54feSGrygorii Strashko mtype, seqid); 122*0d5f54feSGrygorii Strashko __skb_unlink(skb, &cpts->txq); 123*0d5f54feSGrygorii Strashko dev_consume_skb_any(skb); 124*0d5f54feSGrygorii Strashko } 125*0d5f54feSGrygorii Strashko } 126*0d5f54feSGrygorii Strashko 127*0d5f54feSGrygorii Strashko return found; 128*0d5f54feSGrygorii Strashko } 129*0d5f54feSGrygorii Strashko 13087c0e764SRichard Cochran /* 13187c0e764SRichard Cochran * Returns zero if matching event type was found. 13287c0e764SRichard Cochran */ 13387c0e764SRichard Cochran static int cpts_fifo_read(struct cpts *cpts, int match) 13487c0e764SRichard Cochran { 13587c0e764SRichard Cochran int i, type = -1; 13687c0e764SRichard Cochran u32 hi, lo; 13787c0e764SRichard Cochran struct cpts_event *event; 13887c0e764SRichard Cochran 13987c0e764SRichard Cochran for (i = 0; i < CPTS_FIFO_DEPTH; i++) { 14087c0e764SRichard Cochran if (cpts_fifo_pop(cpts, &hi, &lo)) 14187c0e764SRichard Cochran break; 142e4439fa8SWingMan Kwok 143e4439fa8SWingMan Kwok if (list_empty(&cpts->pool) && cpts_purge_events(cpts)) { 144e4439fa8SWingMan Kwok pr_err("cpts: event pool empty\n"); 14587c0e764SRichard Cochran return -1; 14687c0e764SRichard Cochran } 147e4439fa8SWingMan Kwok 14887c0e764SRichard Cochran event = list_first_entry(&cpts->pool, struct cpts_event, list); 14987c0e764SRichard Cochran event->tmo = jiffies + 2; 15087c0e764SRichard Cochran event->high = hi; 15187c0e764SRichard Cochran event->low = lo; 15287c0e764SRichard Cochran type = event_type(event); 15387c0e764SRichard Cochran switch (type) { 154*0d5f54feSGrygorii Strashko case CPTS_EV_TX: 155*0d5f54feSGrygorii Strashko if (cpts_match_tx_ts(cpts, event)) { 156*0d5f54feSGrygorii Strashko /* if the new event matches an existing skb, 157*0d5f54feSGrygorii Strashko * then don't queue it 158*0d5f54feSGrygorii Strashko */ 159*0d5f54feSGrygorii Strashko break; 160*0d5f54feSGrygorii Strashko } 16187c0e764SRichard Cochran case CPTS_EV_PUSH: 16287c0e764SRichard Cochran case CPTS_EV_RX: 16387c0e764SRichard Cochran list_del_init(&event->list); 16487c0e764SRichard Cochran list_add_tail(&event->list, &cpts->events); 16587c0e764SRichard Cochran break; 16687c0e764SRichard Cochran case CPTS_EV_ROLL: 16787c0e764SRichard Cochran case CPTS_EV_HALF: 16887c0e764SRichard Cochran case CPTS_EV_HW: 16987c0e764SRichard Cochran break; 17087c0e764SRichard Cochran default: 17107f42258SMasanari Iida pr_err("cpts: unknown event type\n"); 17287c0e764SRichard Cochran break; 17387c0e764SRichard Cochran } 17487c0e764SRichard Cochran if (type == match) 17587c0e764SRichard Cochran break; 17687c0e764SRichard Cochran } 17787c0e764SRichard Cochran return type == match ? 0 : -1; 17887c0e764SRichard Cochran } 17987c0e764SRichard Cochran 180a5a1d1c2SThomas Gleixner static u64 cpts_systim_read(const struct cyclecounter *cc) 18187c0e764SRichard Cochran { 18287c0e764SRichard Cochran u64 val = 0; 18387c0e764SRichard Cochran struct cpts_event *event; 18487c0e764SRichard Cochran struct list_head *this, *next; 18587c0e764SRichard Cochran struct cpts *cpts = container_of(cc, struct cpts, cc); 18687c0e764SRichard Cochran 18787c0e764SRichard Cochran cpts_write32(cpts, TS_PUSH, ts_push); 18887c0e764SRichard Cochran if (cpts_fifo_read(cpts, CPTS_EV_PUSH)) 18987c0e764SRichard Cochran pr_err("cpts: unable to obtain a time stamp\n"); 19087c0e764SRichard Cochran 19187c0e764SRichard Cochran list_for_each_safe(this, next, &cpts->events) { 19287c0e764SRichard Cochran event = list_entry(this, struct cpts_event, list); 19387c0e764SRichard Cochran if (event_type(event) == CPTS_EV_PUSH) { 19487c0e764SRichard Cochran list_del_init(&event->list); 19587c0e764SRichard Cochran list_add(&event->list, &cpts->pool); 19687c0e764SRichard Cochran val = event->low; 19787c0e764SRichard Cochran break; 19887c0e764SRichard Cochran } 19987c0e764SRichard Cochran } 20087c0e764SRichard Cochran 20187c0e764SRichard Cochran return val; 20287c0e764SRichard Cochran } 20387c0e764SRichard Cochran 20487c0e764SRichard Cochran /* PTP clock operations */ 20587c0e764SRichard Cochran 20687c0e764SRichard Cochran static int cpts_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) 20787c0e764SRichard Cochran { 20887c0e764SRichard Cochran u64 adj; 20987c0e764SRichard Cochran u32 diff, mult; 21087c0e764SRichard Cochran int neg_adj = 0; 21187c0e764SRichard Cochran unsigned long flags; 21287c0e764SRichard Cochran struct cpts *cpts = container_of(ptp, struct cpts, info); 21387c0e764SRichard Cochran 21487c0e764SRichard Cochran if (ppb < 0) { 21587c0e764SRichard Cochran neg_adj = 1; 21687c0e764SRichard Cochran ppb = -ppb; 21787c0e764SRichard Cochran } 21887c0e764SRichard Cochran mult = cpts->cc_mult; 21987c0e764SRichard Cochran adj = mult; 22087c0e764SRichard Cochran adj *= ppb; 22187c0e764SRichard Cochran diff = div_u64(adj, 1000000000ULL); 22287c0e764SRichard Cochran 22387c0e764SRichard Cochran spin_lock_irqsave(&cpts->lock, flags); 22487c0e764SRichard Cochran 22587c0e764SRichard Cochran timecounter_read(&cpts->tc); 22687c0e764SRichard Cochran 22787c0e764SRichard Cochran cpts->cc.mult = neg_adj ? mult - diff : mult + diff; 22887c0e764SRichard Cochran 22987c0e764SRichard Cochran spin_unlock_irqrestore(&cpts->lock, flags); 23087c0e764SRichard Cochran 23187c0e764SRichard Cochran return 0; 23287c0e764SRichard Cochran } 23387c0e764SRichard Cochran 23487c0e764SRichard Cochran static int cpts_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) 23587c0e764SRichard Cochran { 23687c0e764SRichard Cochran unsigned long flags; 23787c0e764SRichard Cochran struct cpts *cpts = container_of(ptp, struct cpts, info); 23887c0e764SRichard Cochran 23987c0e764SRichard Cochran spin_lock_irqsave(&cpts->lock, flags); 240f25a30beSRichard Cochran timecounter_adjtime(&cpts->tc, delta); 24187c0e764SRichard Cochran spin_unlock_irqrestore(&cpts->lock, flags); 24287c0e764SRichard Cochran 24387c0e764SRichard Cochran return 0; 24487c0e764SRichard Cochran } 24587c0e764SRichard Cochran 246a5c79c26SRichard Cochran static int cpts_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) 24787c0e764SRichard Cochran { 24887c0e764SRichard Cochran u64 ns; 24987c0e764SRichard Cochran unsigned long flags; 25087c0e764SRichard Cochran struct cpts *cpts = container_of(ptp, struct cpts, info); 25187c0e764SRichard Cochran 25287c0e764SRichard Cochran spin_lock_irqsave(&cpts->lock, flags); 25387c0e764SRichard Cochran ns = timecounter_read(&cpts->tc); 25487c0e764SRichard Cochran spin_unlock_irqrestore(&cpts->lock, flags); 25587c0e764SRichard Cochran 25684d923ceSRichard Cochran *ts = ns_to_timespec64(ns); 25787c0e764SRichard Cochran 25887c0e764SRichard Cochran return 0; 25987c0e764SRichard Cochran } 26087c0e764SRichard Cochran 26187c0e764SRichard Cochran static int cpts_ptp_settime(struct ptp_clock_info *ptp, 262a5c79c26SRichard Cochran const struct timespec64 *ts) 26387c0e764SRichard Cochran { 26487c0e764SRichard Cochran u64 ns; 26587c0e764SRichard Cochran unsigned long flags; 26687c0e764SRichard Cochran struct cpts *cpts = container_of(ptp, struct cpts, info); 26787c0e764SRichard Cochran 26884d923ceSRichard Cochran ns = timespec64_to_ns(ts); 26987c0e764SRichard Cochran 27087c0e764SRichard Cochran spin_lock_irqsave(&cpts->lock, flags); 27187c0e764SRichard Cochran timecounter_init(&cpts->tc, &cpts->cc, ns); 27287c0e764SRichard Cochran spin_unlock_irqrestore(&cpts->lock, flags); 27387c0e764SRichard Cochran 27487c0e764SRichard Cochran return 0; 27587c0e764SRichard Cochran } 27687c0e764SRichard Cochran 27787c0e764SRichard Cochran static int cpts_ptp_enable(struct ptp_clock_info *ptp, 27887c0e764SRichard Cochran struct ptp_clock_request *rq, int on) 27987c0e764SRichard Cochran { 28087c0e764SRichard Cochran return -EOPNOTSUPP; 28187c0e764SRichard Cochran } 28287c0e764SRichard Cochran 283999f1292SGrygorii Strashko static long cpts_overflow_check(struct ptp_clock_info *ptp) 284999f1292SGrygorii Strashko { 285999f1292SGrygorii Strashko struct cpts *cpts = container_of(ptp, struct cpts, info); 286999f1292SGrygorii Strashko unsigned long delay = cpts->ov_check_period; 287999f1292SGrygorii Strashko struct timespec64 ts; 288*0d5f54feSGrygorii Strashko unsigned long flags; 289999f1292SGrygorii Strashko 290*0d5f54feSGrygorii Strashko spin_lock_irqsave(&cpts->lock, flags); 291*0d5f54feSGrygorii Strashko ts = ns_to_timespec64(timecounter_read(&cpts->tc)); 292*0d5f54feSGrygorii Strashko 293*0d5f54feSGrygorii Strashko if (!skb_queue_empty(&cpts->txq)) 294*0d5f54feSGrygorii Strashko delay = CPTS_SKB_TX_WORK_TIMEOUT; 295*0d5f54feSGrygorii Strashko spin_unlock_irqrestore(&cpts->lock, flags); 296*0d5f54feSGrygorii Strashko 297999f1292SGrygorii Strashko pr_debug("cpts overflow check at %lld.%09lu\n", ts.tv_sec, ts.tv_nsec); 298999f1292SGrygorii Strashko return (long)delay; 299999f1292SGrygorii Strashko } 300999f1292SGrygorii Strashko 30187c0e764SRichard Cochran static struct ptp_clock_info cpts_info = { 30287c0e764SRichard Cochran .owner = THIS_MODULE, 30387c0e764SRichard Cochran .name = "CTPS timer", 30487c0e764SRichard Cochran .max_adj = 1000000, 30587c0e764SRichard Cochran .n_ext_ts = 0, 3064986b4f0SRichard Cochran .n_pins = 0, 30787c0e764SRichard Cochran .pps = 0, 30887c0e764SRichard Cochran .adjfreq = cpts_ptp_adjfreq, 30987c0e764SRichard Cochran .adjtime = cpts_ptp_adjtime, 310a5c79c26SRichard Cochran .gettime64 = cpts_ptp_gettime, 311a5c79c26SRichard Cochran .settime64 = cpts_ptp_settime, 31287c0e764SRichard Cochran .enable = cpts_ptp_enable, 313999f1292SGrygorii Strashko .do_aux_work = cpts_overflow_check, 31487c0e764SRichard Cochran }; 31587c0e764SRichard Cochran 31687c0e764SRichard Cochran static int cpts_match(struct sk_buff *skb, unsigned int ptp_class, 31787c0e764SRichard Cochran u16 ts_seqid, u8 ts_msgtype) 31887c0e764SRichard Cochran { 31987c0e764SRichard Cochran u16 *seqid; 320ae5c6c6dSStefan Sørensen unsigned int offset = 0; 32187c0e764SRichard Cochran u8 *msgtype, *data = skb->data; 32287c0e764SRichard Cochran 323ae5c6c6dSStefan Sørensen if (ptp_class & PTP_CLASS_VLAN) 324ae5c6c6dSStefan Sørensen offset += VLAN_HLEN; 325ae5c6c6dSStefan Sørensen 326ae5c6c6dSStefan Sørensen switch (ptp_class & PTP_CLASS_PMASK) { 327ae5c6c6dSStefan Sørensen case PTP_CLASS_IPV4: 328cca04b28SRichard Cochran offset += ETH_HLEN + IPV4_HLEN(data + offset) + UDP_HLEN; 32987c0e764SRichard Cochran break; 330ae5c6c6dSStefan Sørensen case PTP_CLASS_IPV6: 331ae5c6c6dSStefan Sørensen offset += ETH_HLEN + IP6_HLEN + UDP_HLEN; 33287c0e764SRichard Cochran break; 333ae5c6c6dSStefan Sørensen case PTP_CLASS_L2: 334ae5c6c6dSStefan Sørensen offset += ETH_HLEN; 33587c0e764SRichard Cochran break; 33687c0e764SRichard Cochran default: 33787c0e764SRichard Cochran return 0; 33887c0e764SRichard Cochran } 33987c0e764SRichard Cochran 34087c0e764SRichard Cochran if (skb->len + ETH_HLEN < offset + OFF_PTP_SEQUENCE_ID + sizeof(*seqid)) 34187c0e764SRichard Cochran return 0; 34287c0e764SRichard Cochran 34387c0e764SRichard Cochran if (unlikely(ptp_class & PTP_CLASS_V1)) 34487c0e764SRichard Cochran msgtype = data + offset + OFF_PTP_CONTROL; 34587c0e764SRichard Cochran else 34687c0e764SRichard Cochran msgtype = data + offset; 34787c0e764SRichard Cochran 34887c0e764SRichard Cochran seqid = (u16 *)(data + offset + OFF_PTP_SEQUENCE_ID); 34987c0e764SRichard Cochran 35087c0e764SRichard Cochran return (ts_msgtype == (*msgtype & 0xf) && ts_seqid == ntohs(*seqid)); 35187c0e764SRichard Cochran } 35287c0e764SRichard Cochran 35387c0e764SRichard Cochran static u64 cpts_find_ts(struct cpts *cpts, struct sk_buff *skb, int ev_type) 35487c0e764SRichard Cochran { 35587c0e764SRichard Cochran u64 ns = 0; 35687c0e764SRichard Cochran struct cpts_event *event; 35787c0e764SRichard Cochran struct list_head *this, *next; 358164d8c66SDaniel Borkmann unsigned int class = ptp_classify_raw(skb); 35987c0e764SRichard Cochran unsigned long flags; 36087c0e764SRichard Cochran u16 seqid; 36187c0e764SRichard Cochran u8 mtype; 36287c0e764SRichard Cochran 36387c0e764SRichard Cochran if (class == PTP_CLASS_NONE) 36487c0e764SRichard Cochran return 0; 36587c0e764SRichard Cochran 36687c0e764SRichard Cochran spin_lock_irqsave(&cpts->lock, flags); 36787c0e764SRichard Cochran cpts_fifo_read(cpts, CPTS_EV_PUSH); 36887c0e764SRichard Cochran list_for_each_safe(this, next, &cpts->events) { 36987c0e764SRichard Cochran event = list_entry(this, struct cpts_event, list); 37087c0e764SRichard Cochran if (event_expired(event)) { 37187c0e764SRichard Cochran list_del_init(&event->list); 37287c0e764SRichard Cochran list_add(&event->list, &cpts->pool); 37387c0e764SRichard Cochran continue; 37487c0e764SRichard Cochran } 37587c0e764SRichard Cochran mtype = (event->high >> MESSAGE_TYPE_SHIFT) & MESSAGE_TYPE_MASK; 37687c0e764SRichard Cochran seqid = (event->high >> SEQUENCE_ID_SHIFT) & SEQUENCE_ID_MASK; 37787c0e764SRichard Cochran if (ev_type == event_type(event) && 37887c0e764SRichard Cochran cpts_match(skb, class, seqid, mtype)) { 37987c0e764SRichard Cochran ns = timecounter_cyc2time(&cpts->tc, event->low); 38087c0e764SRichard Cochran list_del_init(&event->list); 38187c0e764SRichard Cochran list_add(&event->list, &cpts->pool); 38287c0e764SRichard Cochran break; 38387c0e764SRichard Cochran } 38487c0e764SRichard Cochran } 385*0d5f54feSGrygorii Strashko 386*0d5f54feSGrygorii Strashko if (ev_type == CPTS_EV_TX && !ns) { 387*0d5f54feSGrygorii Strashko struct cpts_skb_cb_data *skb_cb = 388*0d5f54feSGrygorii Strashko (struct cpts_skb_cb_data *)skb->cb; 389*0d5f54feSGrygorii Strashko /* Not found, add frame to queue for processing later. 390*0d5f54feSGrygorii Strashko * The periodic FIFO check will handle this. 391*0d5f54feSGrygorii Strashko */ 392*0d5f54feSGrygorii Strashko skb_get(skb); 393*0d5f54feSGrygorii Strashko /* get the timestamp for timeouts */ 394*0d5f54feSGrygorii Strashko skb_cb->tmo = jiffies + msecs_to_jiffies(100); 395*0d5f54feSGrygorii Strashko __skb_queue_tail(&cpts->txq, skb); 396*0d5f54feSGrygorii Strashko ptp_schedule_worker(cpts->clock, 0); 397*0d5f54feSGrygorii Strashko } 39887c0e764SRichard Cochran spin_unlock_irqrestore(&cpts->lock, flags); 39987c0e764SRichard Cochran 40087c0e764SRichard Cochran return ns; 40187c0e764SRichard Cochran } 40287c0e764SRichard Cochran 40387c0e764SRichard Cochran void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb) 40487c0e764SRichard Cochran { 40587c0e764SRichard Cochran u64 ns; 40687c0e764SRichard Cochran struct skb_shared_hwtstamps *ssh; 40787c0e764SRichard Cochran 40887c0e764SRichard Cochran if (!cpts->rx_enable) 40987c0e764SRichard Cochran return; 41087c0e764SRichard Cochran ns = cpts_find_ts(cpts, skb, CPTS_EV_RX); 41187c0e764SRichard Cochran if (!ns) 41287c0e764SRichard Cochran return; 41387c0e764SRichard Cochran ssh = skb_hwtstamps(skb); 41487c0e764SRichard Cochran memset(ssh, 0, sizeof(*ssh)); 41587c0e764SRichard Cochran ssh->hwtstamp = ns_to_ktime(ns); 41687c0e764SRichard Cochran } 417c8395d4eSGrygorii Strashko EXPORT_SYMBOL_GPL(cpts_rx_timestamp); 41887c0e764SRichard Cochran 41987c0e764SRichard Cochran void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb) 42087c0e764SRichard Cochran { 42187c0e764SRichard Cochran u64 ns; 42287c0e764SRichard Cochran struct skb_shared_hwtstamps ssh; 42387c0e764SRichard Cochran 42487c0e764SRichard Cochran if (!(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) 42587c0e764SRichard Cochran return; 42687c0e764SRichard Cochran ns = cpts_find_ts(cpts, skb, CPTS_EV_TX); 42787c0e764SRichard Cochran if (!ns) 42887c0e764SRichard Cochran return; 42987c0e764SRichard Cochran memset(&ssh, 0, sizeof(ssh)); 43087c0e764SRichard Cochran ssh.hwtstamp = ns_to_ktime(ns); 43187c0e764SRichard Cochran skb_tstamp_tx(skb, &ssh); 43287c0e764SRichard Cochran } 433c8395d4eSGrygorii Strashko EXPORT_SYMBOL_GPL(cpts_tx_timestamp); 43487c0e764SRichard Cochran 4358a2c9a5aSGrygorii Strashko int cpts_register(struct cpts *cpts) 43687c0e764SRichard Cochran { 43787c0e764SRichard Cochran int err, i; 43887c0e764SRichard Cochran 439*0d5f54feSGrygorii Strashko skb_queue_head_init(&cpts->txq); 44087c0e764SRichard Cochran INIT_LIST_HEAD(&cpts->events); 44187c0e764SRichard Cochran INIT_LIST_HEAD(&cpts->pool); 44287c0e764SRichard Cochran for (i = 0; i < CPTS_MAX_EVENTS; i++) 44387c0e764SRichard Cochran list_add(&cpts->pool_data[i].list, &cpts->pool); 44487c0e764SRichard Cochran 4458a2c9a5aSGrygorii Strashko clk_enable(cpts->refclk); 4468a2c9a5aSGrygorii Strashko 44787c0e764SRichard Cochran cpts_write32(cpts, CPTS_EN, control); 44887c0e764SRichard Cochran cpts_write32(cpts, TS_PEND_EN, int_enable); 44987c0e764SRichard Cochran 45087c0e764SRichard Cochran timecounter_init(&cpts->tc, &cpts->cc, ktime_to_ns(ktime_get_real())); 45187c0e764SRichard Cochran 4528a2c9a5aSGrygorii Strashko cpts->clock = ptp_clock_register(&cpts->info, cpts->dev); 4536c691405SGrygorii Strashko if (IS_ERR(cpts->clock)) { 4546c691405SGrygorii Strashko err = PTR_ERR(cpts->clock); 4556c691405SGrygorii Strashko cpts->clock = NULL; 4566c691405SGrygorii Strashko goto err_ptp; 4576c691405SGrygorii Strashko } 4586c691405SGrygorii Strashko cpts->phc_index = ptp_clock_index(cpts->clock); 4596c691405SGrygorii Strashko 460999f1292SGrygorii Strashko ptp_schedule_worker(cpts->clock, cpts->ov_check_period); 46187c0e764SRichard Cochran return 0; 4626c691405SGrygorii Strashko 4636c691405SGrygorii Strashko err_ptp: 4648a2c9a5aSGrygorii Strashko clk_disable(cpts->refclk); 4656c691405SGrygorii Strashko return err; 46687c0e764SRichard Cochran } 467c8395d4eSGrygorii Strashko EXPORT_SYMBOL_GPL(cpts_register); 46887c0e764SRichard Cochran 46987c0e764SRichard Cochran void cpts_unregister(struct cpts *cpts) 47087c0e764SRichard Cochran { 4718a2c9a5aSGrygorii Strashko if (WARN_ON(!cpts->clock)) 4728a2c9a5aSGrygorii Strashko return; 4738a2c9a5aSGrygorii Strashko 4748a2c9a5aSGrygorii Strashko ptp_clock_unregister(cpts->clock); 4758a2c9a5aSGrygorii Strashko cpts->clock = NULL; 4768fcd6891SGrygorii Strashko 4778fcd6891SGrygorii Strashko cpts_write32(cpts, 0, int_enable); 4788fcd6891SGrygorii Strashko cpts_write32(cpts, 0, control); 4798fcd6891SGrygorii Strashko 480*0d5f54feSGrygorii Strashko /* Drop all packet */ 481*0d5f54feSGrygorii Strashko skb_queue_purge(&cpts->txq); 482*0d5f54feSGrygorii Strashko 4838a2c9a5aSGrygorii Strashko clk_disable(cpts->refclk); 48487c0e764SRichard Cochran } 485c8395d4eSGrygorii Strashko EXPORT_SYMBOL_GPL(cpts_unregister); 486c8395d4eSGrygorii Strashko 48788f0f0b0SGrygorii Strashko static void cpts_calc_mult_shift(struct cpts *cpts) 48888f0f0b0SGrygorii Strashko { 48988f0f0b0SGrygorii Strashko u64 frac, maxsec, ns; 49088f0f0b0SGrygorii Strashko u32 freq; 49188f0f0b0SGrygorii Strashko 49288f0f0b0SGrygorii Strashko freq = clk_get_rate(cpts->refclk); 49388f0f0b0SGrygorii Strashko 49488f0f0b0SGrygorii Strashko /* Calc the maximum number of seconds which we can run before 49588f0f0b0SGrygorii Strashko * wrapping around. 49688f0f0b0SGrygorii Strashko */ 49788f0f0b0SGrygorii Strashko maxsec = cpts->cc.mask; 49888f0f0b0SGrygorii Strashko do_div(maxsec, freq); 49988f0f0b0SGrygorii Strashko /* limit conversation rate to 10 sec as higher values will produce 50088f0f0b0SGrygorii Strashko * too small mult factors and so reduce the conversion accuracy 50188f0f0b0SGrygorii Strashko */ 50288f0f0b0SGrygorii Strashko if (maxsec > 10) 50388f0f0b0SGrygorii Strashko maxsec = 10; 50488f0f0b0SGrygorii Strashko 50520138cf9SGrygorii Strashko /* Calc overflow check period (maxsec / 2) */ 50620138cf9SGrygorii Strashko cpts->ov_check_period = (HZ * maxsec) / 2; 50720138cf9SGrygorii Strashko dev_info(cpts->dev, "cpts: overflow check period %lu (jiffies)\n", 50820138cf9SGrygorii Strashko cpts->ov_check_period); 50920138cf9SGrygorii Strashko 51088f0f0b0SGrygorii Strashko if (cpts->cc.mult || cpts->cc.shift) 51188f0f0b0SGrygorii Strashko return; 51288f0f0b0SGrygorii Strashko 51388f0f0b0SGrygorii Strashko clocks_calc_mult_shift(&cpts->cc.mult, &cpts->cc.shift, 51488f0f0b0SGrygorii Strashko freq, NSEC_PER_SEC, maxsec); 51588f0f0b0SGrygorii Strashko 51688f0f0b0SGrygorii Strashko frac = 0; 51788f0f0b0SGrygorii Strashko ns = cyclecounter_cyc2ns(&cpts->cc, freq, cpts->cc.mask, &frac); 51888f0f0b0SGrygorii Strashko 51988f0f0b0SGrygorii Strashko dev_info(cpts->dev, 52088f0f0b0SGrygorii Strashko "CPTS: ref_clk_freq:%u calc_mult:%u calc_shift:%u error:%lld nsec/sec\n", 52188f0f0b0SGrygorii Strashko freq, cpts->cc.mult, cpts->cc.shift, (ns - NSEC_PER_SEC)); 52288f0f0b0SGrygorii Strashko } 52388f0f0b0SGrygorii Strashko 5244a88fb95SGrygorii Strashko static int cpts_of_parse(struct cpts *cpts, struct device_node *node) 5254a88fb95SGrygorii Strashko { 5264a88fb95SGrygorii Strashko int ret = -EINVAL; 5274a88fb95SGrygorii Strashko u32 prop; 5284a88fb95SGrygorii Strashko 52988f0f0b0SGrygorii Strashko if (!of_property_read_u32(node, "cpts_clock_mult", &prop)) 5304a88fb95SGrygorii Strashko cpts->cc.mult = prop; 5314a88fb95SGrygorii Strashko 53288f0f0b0SGrygorii Strashko if (!of_property_read_u32(node, "cpts_clock_shift", &prop)) 5334a88fb95SGrygorii Strashko cpts->cc.shift = prop; 5344a88fb95SGrygorii Strashko 53588f0f0b0SGrygorii Strashko if ((cpts->cc.mult && !cpts->cc.shift) || 53688f0f0b0SGrygorii Strashko (!cpts->cc.mult && cpts->cc.shift)) 53788f0f0b0SGrygorii Strashko goto of_error; 53888f0f0b0SGrygorii Strashko 5394a88fb95SGrygorii Strashko return 0; 5404a88fb95SGrygorii Strashko 5414a88fb95SGrygorii Strashko of_error: 5424a88fb95SGrygorii Strashko dev_err(cpts->dev, "CPTS: Missing property in the DT.\n"); 5434a88fb95SGrygorii Strashko return ret; 5444a88fb95SGrygorii Strashko } 5454a88fb95SGrygorii Strashko 5468a2c9a5aSGrygorii Strashko struct cpts *cpts_create(struct device *dev, void __iomem *regs, 5474a88fb95SGrygorii Strashko struct device_node *node) 5488a2c9a5aSGrygorii Strashko { 5498a2c9a5aSGrygorii Strashko struct cpts *cpts; 5504a88fb95SGrygorii Strashko int ret; 5518a2c9a5aSGrygorii Strashko 5528a2c9a5aSGrygorii Strashko cpts = devm_kzalloc(dev, sizeof(*cpts), GFP_KERNEL); 5538a2c9a5aSGrygorii Strashko if (!cpts) 5548a2c9a5aSGrygorii Strashko return ERR_PTR(-ENOMEM); 5558a2c9a5aSGrygorii Strashko 5568a2c9a5aSGrygorii Strashko cpts->dev = dev; 5578a2c9a5aSGrygorii Strashko cpts->reg = (struct cpsw_cpts __iomem *)regs; 5588a2c9a5aSGrygorii Strashko spin_lock_init(&cpts->lock); 5598a2c9a5aSGrygorii Strashko 5604a88fb95SGrygorii Strashko ret = cpts_of_parse(cpts, node); 5614a88fb95SGrygorii Strashko if (ret) 5624a88fb95SGrygorii Strashko return ERR_PTR(ret); 5634a88fb95SGrygorii Strashko 5648a2c9a5aSGrygorii Strashko cpts->refclk = devm_clk_get(dev, "cpts"); 5658a2c9a5aSGrygorii Strashko if (IS_ERR(cpts->refclk)) { 5668a2c9a5aSGrygorii Strashko dev_err(dev, "Failed to get cpts refclk\n"); 5678a2c9a5aSGrygorii Strashko return ERR_PTR(PTR_ERR(cpts->refclk)); 5688a2c9a5aSGrygorii Strashko } 5698a2c9a5aSGrygorii Strashko 5708a2c9a5aSGrygorii Strashko clk_prepare(cpts->refclk); 5718a2c9a5aSGrygorii Strashko 5728a2c9a5aSGrygorii Strashko cpts->cc.read = cpts_systim_read; 5738a2c9a5aSGrygorii Strashko cpts->cc.mask = CLOCKSOURCE_MASK(32); 57488f0f0b0SGrygorii Strashko cpts->info = cpts_info; 57588f0f0b0SGrygorii Strashko 57688f0f0b0SGrygorii Strashko cpts_calc_mult_shift(cpts); 5774a88fb95SGrygorii Strashko /* save cc.mult original value as it can be modified 5784a88fb95SGrygorii Strashko * by cpts_ptp_adjfreq(). 5794a88fb95SGrygorii Strashko */ 5804a88fb95SGrygorii Strashko cpts->cc_mult = cpts->cc.mult; 5818a2c9a5aSGrygorii Strashko 5828a2c9a5aSGrygorii Strashko return cpts; 5838a2c9a5aSGrygorii Strashko } 5848a2c9a5aSGrygorii Strashko EXPORT_SYMBOL_GPL(cpts_create); 5858a2c9a5aSGrygorii Strashko 5868a2c9a5aSGrygorii Strashko void cpts_release(struct cpts *cpts) 5878a2c9a5aSGrygorii Strashko { 5888a2c9a5aSGrygorii Strashko if (!cpts) 5898a2c9a5aSGrygorii Strashko return; 5908a2c9a5aSGrygorii Strashko 5918a2c9a5aSGrygorii Strashko if (WARN_ON(!cpts->refclk)) 5928a2c9a5aSGrygorii Strashko return; 5938a2c9a5aSGrygorii Strashko 5948a2c9a5aSGrygorii Strashko clk_unprepare(cpts->refclk); 5958a2c9a5aSGrygorii Strashko } 5968a2c9a5aSGrygorii Strashko EXPORT_SYMBOL_GPL(cpts_release); 5978a2c9a5aSGrygorii Strashko 598c8395d4eSGrygorii Strashko MODULE_LICENSE("GPL v2"); 599c8395d4eSGrygorii Strashko MODULE_DESCRIPTION("TI CPTS driver"); 600c8395d4eSGrygorii Strashko MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>"); 601