168cf027fSGrygorii Strashko // SPDX-License-Identifier: GPL-2.0+ 287c0e764SRichard Cochran /* 387c0e764SRichard Cochran * TI Common Platform Time Sync 487c0e764SRichard Cochran * 587c0e764SRichard Cochran * Copyright (C) 2012 Richard Cochran <richardcochran@gmail.com> 687c0e764SRichard Cochran * 787c0e764SRichard Cochran */ 8a3047a81SGrygorii Strashko #include <linux/clk-provider.h> 987c0e764SRichard Cochran #include <linux/err.h> 1087c0e764SRichard Cochran #include <linux/if.h> 1187c0e764SRichard Cochran #include <linux/hrtimer.h> 1287c0e764SRichard Cochran #include <linux/module.h> 1387c0e764SRichard Cochran #include <linux/net_tstamp.h> 1487c0e764SRichard Cochran #include <linux/ptp_classify.h> 1587c0e764SRichard Cochran #include <linux/time.h> 1687c0e764SRichard Cochran #include <linux/uaccess.h> 1787c0e764SRichard Cochran #include <linux/workqueue.h> 1879eb9d28SAlexei Starovoitov #include <linux/if_ether.h> 1979eb9d28SAlexei Starovoitov #include <linux/if_vlan.h> 2087c0e764SRichard Cochran 2187c0e764SRichard Cochran #include "cpts.h" 2287c0e764SRichard Cochran 230d5f54feSGrygorii Strashko #define CPTS_SKB_TX_WORK_TIMEOUT 1 /* jiffies */ 24c8f8e47eSGrygorii Strashko #define CPTS_SKB_RX_TX_TMO 100 /*ms */ 25c8f8e47eSGrygorii Strashko #define CPTS_EVENT_RX_TX_TIMEOUT (100) /* ms */ 260d5f54feSGrygorii Strashko 270d5f54feSGrygorii Strashko struct cpts_skb_cb_data { 283bfd41b5SGrygorii Strashko u32 skb_mtype_seqid; 290d5f54feSGrygorii Strashko unsigned long tmo; 300d5f54feSGrygorii Strashko }; 310d5f54feSGrygorii Strashko 32391fd6caSGrygorii Strashko #define cpts_read32(c, r) readl_relaxed(&c->reg->r) 33391fd6caSGrygorii Strashko #define cpts_write32(c, v, r) writel_relaxed(v, &c->reg->r) 3487c0e764SRichard Cochran 35*b78aba49SGrygorii Strashko static int cpts_event_port(struct cpts_event *event) 36*b78aba49SGrygorii Strashko { 37*b78aba49SGrygorii Strashko return (event->high >> PORT_NUMBER_SHIFT) & PORT_NUMBER_MASK; 38*b78aba49SGrygorii Strashko } 39*b78aba49SGrygorii Strashko 4087c0e764SRichard Cochran static int event_expired(struct cpts_event *event) 4187c0e764SRichard Cochran { 4287c0e764SRichard Cochran return time_after(jiffies, event->tmo); 4387c0e764SRichard Cochran } 4487c0e764SRichard Cochran 4587c0e764SRichard Cochran static int event_type(struct cpts_event *event) 4687c0e764SRichard Cochran { 4787c0e764SRichard Cochran return (event->high >> EVENT_TYPE_SHIFT) & EVENT_TYPE_MASK; 4887c0e764SRichard Cochran } 4987c0e764SRichard Cochran 5087c0e764SRichard Cochran static int cpts_fifo_pop(struct cpts *cpts, u32 *high, u32 *low) 5187c0e764SRichard Cochran { 5287c0e764SRichard Cochran u32 r = cpts_read32(cpts, intstat_raw); 5387c0e764SRichard Cochran 5487c0e764SRichard Cochran if (r & TS_PEND_RAW) { 5587c0e764SRichard Cochran *high = cpts_read32(cpts, event_high); 5687c0e764SRichard Cochran *low = cpts_read32(cpts, event_low); 5787c0e764SRichard Cochran cpts_write32(cpts, EVENT_POP, event_pop); 5887c0e764SRichard Cochran return 0; 5987c0e764SRichard Cochran } 6087c0e764SRichard Cochran return -1; 6187c0e764SRichard Cochran } 6287c0e764SRichard Cochran 63e4439fa8SWingMan Kwok static int cpts_purge_events(struct cpts *cpts) 64e4439fa8SWingMan Kwok { 65e4439fa8SWingMan Kwok struct list_head *this, *next; 66e4439fa8SWingMan Kwok struct cpts_event *event; 67e4439fa8SWingMan Kwok int removed = 0; 68e4439fa8SWingMan Kwok 69e4439fa8SWingMan Kwok list_for_each_safe(this, next, &cpts->events) { 70e4439fa8SWingMan Kwok event = list_entry(this, struct cpts_event, list); 71e4439fa8SWingMan Kwok if (event_expired(event)) { 72e4439fa8SWingMan Kwok list_del_init(&event->list); 73e4439fa8SWingMan Kwok list_add(&event->list, &cpts->pool); 74e4439fa8SWingMan Kwok ++removed; 75e4439fa8SWingMan Kwok } 76e4439fa8SWingMan Kwok } 77e4439fa8SWingMan Kwok 78e4439fa8SWingMan Kwok if (removed) 7979d6e755SGrygorii Strashko dev_dbg(cpts->dev, "cpts: event pool cleaned up %d\n", removed); 80e4439fa8SWingMan Kwok return removed ? 0 : -1; 81e4439fa8SWingMan Kwok } 82e4439fa8SWingMan Kwok 83f19dcd5fSIvan Khoronzhuk static void cpts_purge_txq(struct cpts *cpts) 84f19dcd5fSIvan Khoronzhuk { 85f19dcd5fSIvan Khoronzhuk struct cpts_skb_cb_data *skb_cb; 86f19dcd5fSIvan Khoronzhuk struct sk_buff *skb, *tmp; 87f19dcd5fSIvan Khoronzhuk int removed = 0; 88f19dcd5fSIvan Khoronzhuk 89f19dcd5fSIvan Khoronzhuk skb_queue_walk_safe(&cpts->txq, skb, tmp) { 90f19dcd5fSIvan Khoronzhuk skb_cb = (struct cpts_skb_cb_data *)skb->cb; 91f19dcd5fSIvan Khoronzhuk if (time_after(jiffies, skb_cb->tmo)) { 92f19dcd5fSIvan Khoronzhuk __skb_unlink(skb, &cpts->txq); 93f19dcd5fSIvan Khoronzhuk dev_consume_skb_any(skb); 94f19dcd5fSIvan Khoronzhuk ++removed; 95f19dcd5fSIvan Khoronzhuk } 96f19dcd5fSIvan Khoronzhuk } 97f19dcd5fSIvan Khoronzhuk 98f19dcd5fSIvan Khoronzhuk if (removed) 99f19dcd5fSIvan Khoronzhuk dev_dbg(cpts->dev, "txq cleaned up %d\n", removed); 100f19dcd5fSIvan Khoronzhuk } 101f19dcd5fSIvan Khoronzhuk 10287c0e764SRichard Cochran /* 10387c0e764SRichard Cochran * Returns zero if matching event type was found. 10487c0e764SRichard Cochran */ 10587c0e764SRichard Cochran static int cpts_fifo_read(struct cpts *cpts, int match) 10687c0e764SRichard Cochran { 107*b78aba49SGrygorii Strashko struct ptp_clock_event pevent; 10885624412SGrygorii Strashko bool need_schedule = false; 109ba107428SGrygorii Strashko struct cpts_event *event; 110ba107428SGrygorii Strashko unsigned long flags; 11187c0e764SRichard Cochran int i, type = -1; 11287c0e764SRichard Cochran u32 hi, lo; 113ba107428SGrygorii Strashko 114ba107428SGrygorii Strashko spin_lock_irqsave(&cpts->lock, flags); 11587c0e764SRichard Cochran 11687c0e764SRichard Cochran for (i = 0; i < CPTS_FIFO_DEPTH; i++) { 11787c0e764SRichard Cochran if (cpts_fifo_pop(cpts, &hi, &lo)) 11887c0e764SRichard Cochran break; 119e4439fa8SWingMan Kwok 120e4439fa8SWingMan Kwok if (list_empty(&cpts->pool) && cpts_purge_events(cpts)) { 12179d6e755SGrygorii Strashko dev_warn(cpts->dev, "cpts: event pool empty\n"); 122ba107428SGrygorii Strashko break; 12387c0e764SRichard Cochran } 124e4439fa8SWingMan Kwok 12587c0e764SRichard Cochran event = list_first_entry(&cpts->pool, struct cpts_event, list); 12687c0e764SRichard Cochran event->high = hi; 12787c0e764SRichard Cochran event->low = lo; 128e66dccceSGrygorii Strashko event->timestamp = timecounter_cyc2time(&cpts->tc, event->low); 12987c0e764SRichard Cochran type = event_type(event); 130e66dccceSGrygorii Strashko 131e66dccceSGrygorii Strashko dev_dbg(cpts->dev, "CPTS_EV: %d high:%08X low:%08x\n", 132e66dccceSGrygorii Strashko type, event->high, event->low); 13387c0e764SRichard Cochran switch (type) { 134e66dccceSGrygorii Strashko case CPTS_EV_PUSH: 135e66dccceSGrygorii Strashko WRITE_ONCE(cpts->cur_timestamp, lo); 136e66dccceSGrygorii Strashko timecounter_read(&cpts->tc); 1370d6df3e6SGrygorii Strashko if (cpts->mult_new) { 1380d6df3e6SGrygorii Strashko cpts->cc.mult = cpts->mult_new; 1390d6df3e6SGrygorii Strashko cpts->mult_new = 0; 1400d6df3e6SGrygorii Strashko } 14185624412SGrygorii Strashko if (!cpts->irq_poll) 14285624412SGrygorii Strashko complete(&cpts->ts_push_complete); 143e66dccceSGrygorii Strashko break; 1440d5f54feSGrygorii Strashko case CPTS_EV_TX: 14587c0e764SRichard Cochran case CPTS_EV_RX: 146c8f8e47eSGrygorii Strashko event->tmo = jiffies + 147c8f8e47eSGrygorii Strashko msecs_to_jiffies(CPTS_EVENT_RX_TX_TIMEOUT); 148c8f8e47eSGrygorii Strashko 14987c0e764SRichard Cochran list_del_init(&event->list); 15087c0e764SRichard Cochran list_add_tail(&event->list, &cpts->events); 15185624412SGrygorii Strashko need_schedule = true; 15287c0e764SRichard Cochran break; 15387c0e764SRichard Cochran case CPTS_EV_ROLL: 15487c0e764SRichard Cochran case CPTS_EV_HALF: 155*b78aba49SGrygorii Strashko break; 15687c0e764SRichard Cochran case CPTS_EV_HW: 157*b78aba49SGrygorii Strashko pevent.timestamp = event->timestamp; 158*b78aba49SGrygorii Strashko pevent.type = PTP_CLOCK_EXTTS; 159*b78aba49SGrygorii Strashko pevent.index = cpts_event_port(event) - 1; 160*b78aba49SGrygorii Strashko ptp_clock_event(cpts->clock, &pevent); 16187c0e764SRichard Cochran break; 16287c0e764SRichard Cochran default: 16379d6e755SGrygorii Strashko dev_err(cpts->dev, "cpts: unknown event type\n"); 16487c0e764SRichard Cochran break; 16587c0e764SRichard Cochran } 16687c0e764SRichard Cochran if (type == match) 16787c0e764SRichard Cochran break; 16887c0e764SRichard Cochran } 169ba107428SGrygorii Strashko 170ba107428SGrygorii Strashko spin_unlock_irqrestore(&cpts->lock, flags); 171ba107428SGrygorii Strashko 17285624412SGrygorii Strashko if (!cpts->irq_poll && need_schedule) 17385624412SGrygorii Strashko ptp_schedule_worker(cpts->clock, 0); 17485624412SGrygorii Strashko 17587c0e764SRichard Cochran return type == match ? 0 : -1; 17687c0e764SRichard Cochran } 17787c0e764SRichard Cochran 17885624412SGrygorii Strashko void cpts_misc_interrupt(struct cpts *cpts) 17985624412SGrygorii Strashko { 18085624412SGrygorii Strashko cpts_fifo_read(cpts, -1); 18185624412SGrygorii Strashko } 18285624412SGrygorii Strashko EXPORT_SYMBOL_GPL(cpts_misc_interrupt); 18385624412SGrygorii Strashko 184a5a1d1c2SThomas Gleixner static u64 cpts_systim_read(const struct cyclecounter *cc) 18587c0e764SRichard Cochran { 18687c0e764SRichard Cochran struct cpts *cpts = container_of(cc, struct cpts, cc); 18787c0e764SRichard Cochran 188e66dccceSGrygorii Strashko return READ_ONCE(cpts->cur_timestamp); 189e66dccceSGrygorii Strashko } 190e66dccceSGrygorii Strashko 191856e59abSGrygorii Strashko static void cpts_update_cur_time(struct cpts *cpts, int match, 192856e59abSGrygorii Strashko struct ptp_system_timestamp *sts) 193e66dccceSGrygorii Strashko { 194ba107428SGrygorii Strashko unsigned long flags; 195ba107428SGrygorii Strashko 19685624412SGrygorii Strashko reinit_completion(&cpts->ts_push_complete); 19785624412SGrygorii Strashko 198ba107428SGrygorii Strashko /* use spin_lock_irqsave() here as it has to run very fast */ 199ba107428SGrygorii Strashko spin_lock_irqsave(&cpts->lock, flags); 200856e59abSGrygorii Strashko ptp_read_system_prets(sts); 20187c0e764SRichard Cochran cpts_write32(cpts, TS_PUSH, ts_push); 202856e59abSGrygorii Strashko cpts_read32(cpts, ts_push); 203856e59abSGrygorii Strashko ptp_read_system_postts(sts); 204ba107428SGrygorii Strashko spin_unlock_irqrestore(&cpts->lock, flags); 205e66dccceSGrygorii Strashko 20685624412SGrygorii Strashko if (cpts->irq_poll && cpts_fifo_read(cpts, match) && match != -1) 20779d6e755SGrygorii Strashko dev_err(cpts->dev, "cpts: unable to obtain a time stamp\n"); 20885624412SGrygorii Strashko 20985624412SGrygorii Strashko if (!cpts->irq_poll && 21085624412SGrygorii Strashko !wait_for_completion_timeout(&cpts->ts_push_complete, HZ)) 21185624412SGrygorii Strashko dev_err(cpts->dev, "cpts: obtain a time stamp timeout\n"); 21287c0e764SRichard Cochran } 21387c0e764SRichard Cochran 21487c0e764SRichard Cochran /* PTP clock operations */ 21587c0e764SRichard Cochran 21687c0e764SRichard Cochran static int cpts_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) 21787c0e764SRichard Cochran { 21887c0e764SRichard Cochran struct cpts *cpts = container_of(ptp, struct cpts, info); 219ba107428SGrygorii Strashko int neg_adj = 0; 220ba107428SGrygorii Strashko u32 diff, mult; 221ba107428SGrygorii Strashko u64 adj; 22287c0e764SRichard Cochran 22387c0e764SRichard Cochran if (ppb < 0) { 22487c0e764SRichard Cochran neg_adj = 1; 22587c0e764SRichard Cochran ppb = -ppb; 22687c0e764SRichard Cochran } 22787c0e764SRichard Cochran mult = cpts->cc_mult; 22887c0e764SRichard Cochran adj = mult; 22987c0e764SRichard Cochran adj *= ppb; 23087c0e764SRichard Cochran diff = div_u64(adj, 1000000000ULL); 23187c0e764SRichard Cochran 232ba107428SGrygorii Strashko mutex_lock(&cpts->ptp_clk_mutex); 23387c0e764SRichard Cochran 2340d6df3e6SGrygorii Strashko cpts->mult_new = neg_adj ? mult - diff : mult + diff; 23587c0e764SRichard Cochran 236856e59abSGrygorii Strashko cpts_update_cur_time(cpts, CPTS_EV_PUSH, NULL); 23787c0e764SRichard Cochran 238ba107428SGrygorii Strashko mutex_unlock(&cpts->ptp_clk_mutex); 23987c0e764SRichard Cochran return 0; 24087c0e764SRichard Cochran } 24187c0e764SRichard Cochran 24287c0e764SRichard Cochran static int cpts_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) 24387c0e764SRichard Cochran { 24487c0e764SRichard Cochran struct cpts *cpts = container_of(ptp, struct cpts, info); 24587c0e764SRichard Cochran 246ba107428SGrygorii Strashko mutex_lock(&cpts->ptp_clk_mutex); 247f25a30beSRichard Cochran timecounter_adjtime(&cpts->tc, delta); 248ba107428SGrygorii Strashko mutex_unlock(&cpts->ptp_clk_mutex); 24987c0e764SRichard Cochran 25087c0e764SRichard Cochran return 0; 25187c0e764SRichard Cochran } 25287c0e764SRichard Cochran 253856e59abSGrygorii Strashko static int cpts_ptp_gettimeex(struct ptp_clock_info *ptp, 254856e59abSGrygorii Strashko struct timespec64 *ts, 255856e59abSGrygorii Strashko struct ptp_system_timestamp *sts) 25687c0e764SRichard Cochran { 25787c0e764SRichard Cochran struct cpts *cpts = container_of(ptp, struct cpts, info); 258856e59abSGrygorii Strashko u64 ns; 25987c0e764SRichard Cochran 260ba107428SGrygorii Strashko mutex_lock(&cpts->ptp_clk_mutex); 261e66dccceSGrygorii Strashko 262856e59abSGrygorii Strashko cpts_update_cur_time(cpts, CPTS_EV_PUSH, sts); 263e66dccceSGrygorii Strashko 26487c0e764SRichard Cochran ns = timecounter_read(&cpts->tc); 265ba107428SGrygorii Strashko mutex_unlock(&cpts->ptp_clk_mutex); 26687c0e764SRichard Cochran 26784d923ceSRichard Cochran *ts = ns_to_timespec64(ns); 26887c0e764SRichard Cochran 26987c0e764SRichard Cochran return 0; 27087c0e764SRichard Cochran } 27187c0e764SRichard Cochran 27287c0e764SRichard Cochran static int cpts_ptp_settime(struct ptp_clock_info *ptp, 273a5c79c26SRichard Cochran const struct timespec64 *ts) 27487c0e764SRichard Cochran { 27587c0e764SRichard Cochran struct cpts *cpts = container_of(ptp, struct cpts, info); 276ba107428SGrygorii Strashko u64 ns; 27787c0e764SRichard Cochran 27884d923ceSRichard Cochran ns = timespec64_to_ns(ts); 27987c0e764SRichard Cochran 280ba107428SGrygorii Strashko mutex_lock(&cpts->ptp_clk_mutex); 28187c0e764SRichard Cochran timecounter_init(&cpts->tc, &cpts->cc, ns); 282ba107428SGrygorii Strashko mutex_unlock(&cpts->ptp_clk_mutex); 28387c0e764SRichard Cochran 28487c0e764SRichard Cochran return 0; 28587c0e764SRichard Cochran } 28687c0e764SRichard Cochran 287*b78aba49SGrygorii Strashko static int cpts_extts_enable(struct cpts *cpts, u32 index, int on) 288*b78aba49SGrygorii Strashko { 289*b78aba49SGrygorii Strashko u32 v; 290*b78aba49SGrygorii Strashko 291*b78aba49SGrygorii Strashko if (((cpts->hw_ts_enable & BIT(index)) >> index) == on) 292*b78aba49SGrygorii Strashko return 0; 293*b78aba49SGrygorii Strashko 294*b78aba49SGrygorii Strashko mutex_lock(&cpts->ptp_clk_mutex); 295*b78aba49SGrygorii Strashko 296*b78aba49SGrygorii Strashko v = cpts_read32(cpts, control); 297*b78aba49SGrygorii Strashko if (on) { 298*b78aba49SGrygorii Strashko v |= BIT(8 + index); 299*b78aba49SGrygorii Strashko cpts->hw_ts_enable |= BIT(index); 300*b78aba49SGrygorii Strashko } else { 301*b78aba49SGrygorii Strashko v &= ~BIT(8 + index); 302*b78aba49SGrygorii Strashko cpts->hw_ts_enable &= ~BIT(index); 303*b78aba49SGrygorii Strashko } 304*b78aba49SGrygorii Strashko cpts_write32(cpts, v, control); 305*b78aba49SGrygorii Strashko 306*b78aba49SGrygorii Strashko mutex_unlock(&cpts->ptp_clk_mutex); 307*b78aba49SGrygorii Strashko 308*b78aba49SGrygorii Strashko return 0; 309*b78aba49SGrygorii Strashko } 310*b78aba49SGrygorii Strashko 31187c0e764SRichard Cochran static int cpts_ptp_enable(struct ptp_clock_info *ptp, 31287c0e764SRichard Cochran struct ptp_clock_request *rq, int on) 31387c0e764SRichard Cochran { 314*b78aba49SGrygorii Strashko struct cpts *cpts = container_of(ptp, struct cpts, info); 315*b78aba49SGrygorii Strashko 316*b78aba49SGrygorii Strashko switch (rq->type) { 317*b78aba49SGrygorii Strashko case PTP_CLK_REQ_EXTTS: 318*b78aba49SGrygorii Strashko return cpts_extts_enable(cpts, rq->extts.index, on); 319*b78aba49SGrygorii Strashko default: 320*b78aba49SGrygorii Strashko break; 321*b78aba49SGrygorii Strashko } 322*b78aba49SGrygorii Strashko 32387c0e764SRichard Cochran return -EOPNOTSUPP; 32487c0e764SRichard Cochran } 32587c0e764SRichard Cochran 326c8f8e47eSGrygorii Strashko static bool cpts_match_tx_ts(struct cpts *cpts, struct cpts_event *event) 327c8f8e47eSGrygorii Strashko { 328c8f8e47eSGrygorii Strashko struct sk_buff_head txq_list; 329c8f8e47eSGrygorii Strashko struct sk_buff *skb, *tmp; 330c8f8e47eSGrygorii Strashko unsigned long flags; 331c8f8e47eSGrygorii Strashko bool found = false; 332c8f8e47eSGrygorii Strashko u32 mtype_seqid; 333c8f8e47eSGrygorii Strashko 334c8f8e47eSGrygorii Strashko mtype_seqid = event->high & 335c8f8e47eSGrygorii Strashko ((MESSAGE_TYPE_MASK << MESSAGE_TYPE_SHIFT) | 336c8f8e47eSGrygorii Strashko (SEQUENCE_ID_MASK << SEQUENCE_ID_SHIFT) | 337c8f8e47eSGrygorii Strashko (EVENT_TYPE_MASK << EVENT_TYPE_SHIFT)); 338c8f8e47eSGrygorii Strashko 339c8f8e47eSGrygorii Strashko __skb_queue_head_init(&txq_list); 340c8f8e47eSGrygorii Strashko 341c8f8e47eSGrygorii Strashko spin_lock_irqsave(&cpts->txq.lock, flags); 342c8f8e47eSGrygorii Strashko skb_queue_splice_init(&cpts->txq, &txq_list); 343c8f8e47eSGrygorii Strashko spin_unlock_irqrestore(&cpts->txq.lock, flags); 344c8f8e47eSGrygorii Strashko 345c8f8e47eSGrygorii Strashko skb_queue_walk_safe(&txq_list, skb, tmp) { 346c8f8e47eSGrygorii Strashko struct skb_shared_hwtstamps ssh; 347c8f8e47eSGrygorii Strashko struct cpts_skb_cb_data *skb_cb = 348c8f8e47eSGrygorii Strashko (struct cpts_skb_cb_data *)skb->cb; 349c8f8e47eSGrygorii Strashko 350c8f8e47eSGrygorii Strashko if (mtype_seqid == skb_cb->skb_mtype_seqid) { 351c8f8e47eSGrygorii Strashko memset(&ssh, 0, sizeof(ssh)); 352c8f8e47eSGrygorii Strashko ssh.hwtstamp = ns_to_ktime(event->timestamp); 353c8f8e47eSGrygorii Strashko skb_tstamp_tx(skb, &ssh); 354c8f8e47eSGrygorii Strashko found = true; 355c8f8e47eSGrygorii Strashko __skb_unlink(skb, &txq_list); 356c8f8e47eSGrygorii Strashko dev_consume_skb_any(skb); 357c8f8e47eSGrygorii Strashko dev_dbg(cpts->dev, "match tx timestamp mtype_seqid %08x\n", 358c8f8e47eSGrygorii Strashko mtype_seqid); 359c8f8e47eSGrygorii Strashko break; 360c8f8e47eSGrygorii Strashko } 361c8f8e47eSGrygorii Strashko 362c8f8e47eSGrygorii Strashko if (time_after(jiffies, skb_cb->tmo)) { 363c8f8e47eSGrygorii Strashko /* timeout any expired skbs over 1s */ 364c8f8e47eSGrygorii Strashko dev_dbg(cpts->dev, "expiring tx timestamp from txq\n"); 365c8f8e47eSGrygorii Strashko __skb_unlink(skb, &txq_list); 366c8f8e47eSGrygorii Strashko dev_consume_skb_any(skb); 367c8f8e47eSGrygorii Strashko } 368c8f8e47eSGrygorii Strashko } 369c8f8e47eSGrygorii Strashko 370c8f8e47eSGrygorii Strashko spin_lock_irqsave(&cpts->txq.lock, flags); 371c8f8e47eSGrygorii Strashko skb_queue_splice(&txq_list, &cpts->txq); 372c8f8e47eSGrygorii Strashko spin_unlock_irqrestore(&cpts->txq.lock, flags); 373c8f8e47eSGrygorii Strashko 374c8f8e47eSGrygorii Strashko return found; 375c8f8e47eSGrygorii Strashko } 376c8f8e47eSGrygorii Strashko 377c8f8e47eSGrygorii Strashko static void cpts_process_events(struct cpts *cpts) 378c8f8e47eSGrygorii Strashko { 379c8f8e47eSGrygorii Strashko struct list_head *this, *next; 380c8f8e47eSGrygorii Strashko struct cpts_event *event; 381c8f8e47eSGrygorii Strashko LIST_HEAD(events_free); 382c8f8e47eSGrygorii Strashko unsigned long flags; 383c8f8e47eSGrygorii Strashko LIST_HEAD(events); 384c8f8e47eSGrygorii Strashko 385c8f8e47eSGrygorii Strashko spin_lock_irqsave(&cpts->lock, flags); 386c8f8e47eSGrygorii Strashko list_splice_init(&cpts->events, &events); 387c8f8e47eSGrygorii Strashko spin_unlock_irqrestore(&cpts->lock, flags); 388c8f8e47eSGrygorii Strashko 389c8f8e47eSGrygorii Strashko list_for_each_safe(this, next, &events) { 390c8f8e47eSGrygorii Strashko event = list_entry(this, struct cpts_event, list); 391c8f8e47eSGrygorii Strashko if (cpts_match_tx_ts(cpts, event) || 392c8f8e47eSGrygorii Strashko time_after(jiffies, event->tmo)) { 393c8f8e47eSGrygorii Strashko list_del_init(&event->list); 394c8f8e47eSGrygorii Strashko list_add(&event->list, &events_free); 395c8f8e47eSGrygorii Strashko } 396c8f8e47eSGrygorii Strashko } 397c8f8e47eSGrygorii Strashko 398c8f8e47eSGrygorii Strashko spin_lock_irqsave(&cpts->lock, flags); 399c8f8e47eSGrygorii Strashko list_splice_tail(&events, &cpts->events); 400c8f8e47eSGrygorii Strashko list_splice_tail(&events_free, &cpts->pool); 401c8f8e47eSGrygorii Strashko spin_unlock_irqrestore(&cpts->lock, flags); 402c8f8e47eSGrygorii Strashko } 403c8f8e47eSGrygorii Strashko 404999f1292SGrygorii Strashko static long cpts_overflow_check(struct ptp_clock_info *ptp) 405999f1292SGrygorii Strashko { 406999f1292SGrygorii Strashko struct cpts *cpts = container_of(ptp, struct cpts, info); 407999f1292SGrygorii Strashko unsigned long delay = cpts->ov_check_period; 4080d5f54feSGrygorii Strashko unsigned long flags; 409e66dccceSGrygorii Strashko u64 ns; 410999f1292SGrygorii Strashko 411ba107428SGrygorii Strashko mutex_lock(&cpts->ptp_clk_mutex); 412e66dccceSGrygorii Strashko 413ba107428SGrygorii Strashko cpts_update_cur_time(cpts, -1, NULL); 414e66dccceSGrygorii Strashko ns = timecounter_read(&cpts->tc); 4150d5f54feSGrygorii Strashko 416c8f8e47eSGrygorii Strashko cpts_process_events(cpts); 417c8f8e47eSGrygorii Strashko 418c8f8e47eSGrygorii Strashko spin_lock_irqsave(&cpts->txq.lock, flags); 419f19dcd5fSIvan Khoronzhuk if (!skb_queue_empty(&cpts->txq)) { 420f19dcd5fSIvan Khoronzhuk cpts_purge_txq(cpts); 4210d5f54feSGrygorii Strashko if (!skb_queue_empty(&cpts->txq)) 4220d5f54feSGrygorii Strashko delay = CPTS_SKB_TX_WORK_TIMEOUT; 423f19dcd5fSIvan Khoronzhuk } 424c8f8e47eSGrygorii Strashko spin_unlock_irqrestore(&cpts->txq.lock, flags); 4250d5f54feSGrygorii Strashko 426e66dccceSGrygorii Strashko dev_dbg(cpts->dev, "cpts overflow check at %lld\n", ns); 427ba107428SGrygorii Strashko mutex_unlock(&cpts->ptp_clk_mutex); 428999f1292SGrygorii Strashko return (long)delay; 429999f1292SGrygorii Strashko } 430999f1292SGrygorii Strashko 431b6d08bd8SBhumika Goyal static const struct ptp_clock_info cpts_info = { 43287c0e764SRichard Cochran .owner = THIS_MODULE, 43387c0e764SRichard Cochran .name = "CTPS timer", 43487c0e764SRichard Cochran .max_adj = 1000000, 43587c0e764SRichard Cochran .n_ext_ts = 0, 4364986b4f0SRichard Cochran .n_pins = 0, 43787c0e764SRichard Cochran .pps = 0, 43887c0e764SRichard Cochran .adjfreq = cpts_ptp_adjfreq, 43987c0e764SRichard Cochran .adjtime = cpts_ptp_adjtime, 440856e59abSGrygorii Strashko .gettimex64 = cpts_ptp_gettimeex, 441a5c79c26SRichard Cochran .settime64 = cpts_ptp_settime, 44287c0e764SRichard Cochran .enable = cpts_ptp_enable, 443999f1292SGrygorii Strashko .do_aux_work = cpts_overflow_check, 44487c0e764SRichard Cochran }; 44587c0e764SRichard Cochran 4463bfd41b5SGrygorii Strashko static int cpts_skb_get_mtype_seqid(struct sk_buff *skb, u32 *mtype_seqid) 44787c0e764SRichard Cochran { 4483bfd41b5SGrygorii Strashko unsigned int ptp_class = ptp_classify_raw(skb); 44987c0e764SRichard Cochran u8 *msgtype, *data = skb->data; 4503bfd41b5SGrygorii Strashko unsigned int offset = 0; 4513bfd41b5SGrygorii Strashko u16 *seqid; 4523bfd41b5SGrygorii Strashko 4533bfd41b5SGrygorii Strashko if (ptp_class == PTP_CLASS_NONE) 4543bfd41b5SGrygorii Strashko return 0; 45587c0e764SRichard Cochran 456ae5c6c6dSStefan Sørensen if (ptp_class & PTP_CLASS_VLAN) 457ae5c6c6dSStefan Sørensen offset += VLAN_HLEN; 458ae5c6c6dSStefan Sørensen 459ae5c6c6dSStefan Sørensen switch (ptp_class & PTP_CLASS_PMASK) { 460ae5c6c6dSStefan Sørensen case PTP_CLASS_IPV4: 461cca04b28SRichard Cochran offset += ETH_HLEN + IPV4_HLEN(data + offset) + UDP_HLEN; 46287c0e764SRichard Cochran break; 463ae5c6c6dSStefan Sørensen case PTP_CLASS_IPV6: 464ae5c6c6dSStefan Sørensen offset += ETH_HLEN + IP6_HLEN + UDP_HLEN; 46587c0e764SRichard Cochran break; 466ae5c6c6dSStefan Sørensen case PTP_CLASS_L2: 467ae5c6c6dSStefan Sørensen offset += ETH_HLEN; 46887c0e764SRichard Cochran break; 46987c0e764SRichard Cochran default: 47087c0e764SRichard Cochran return 0; 47187c0e764SRichard Cochran } 47287c0e764SRichard Cochran 47387c0e764SRichard Cochran if (skb->len + ETH_HLEN < offset + OFF_PTP_SEQUENCE_ID + sizeof(*seqid)) 47487c0e764SRichard Cochran return 0; 47587c0e764SRichard Cochran 47687c0e764SRichard Cochran if (unlikely(ptp_class & PTP_CLASS_V1)) 47787c0e764SRichard Cochran msgtype = data + offset + OFF_PTP_CONTROL; 47887c0e764SRichard Cochran else 47987c0e764SRichard Cochran msgtype = data + offset; 48087c0e764SRichard Cochran 48187c0e764SRichard Cochran seqid = (u16 *)(data + offset + OFF_PTP_SEQUENCE_ID); 4823bfd41b5SGrygorii Strashko *mtype_seqid = (*msgtype & MESSAGE_TYPE_MASK) << MESSAGE_TYPE_SHIFT; 4833bfd41b5SGrygorii Strashko *mtype_seqid |= (ntohs(*seqid) & SEQUENCE_ID_MASK) << SEQUENCE_ID_SHIFT; 48487c0e764SRichard Cochran 4853bfd41b5SGrygorii Strashko return 1; 48687c0e764SRichard Cochran } 48787c0e764SRichard Cochran 4883bfd41b5SGrygorii Strashko static u64 cpts_find_ts(struct cpts *cpts, struct sk_buff *skb, 4893bfd41b5SGrygorii Strashko int ev_type, u32 skb_mtype_seqid) 49087c0e764SRichard Cochran { 49187c0e764SRichard Cochran struct list_head *this, *next; 4923bfd41b5SGrygorii Strashko struct cpts_event *event; 49387c0e764SRichard Cochran unsigned long flags; 4943bfd41b5SGrygorii Strashko u32 mtype_seqid; 4953bfd41b5SGrygorii Strashko u64 ns = 0; 49687c0e764SRichard Cochran 497a93439ccSGrygorii Strashko cpts_fifo_read(cpts, -1); 498ba107428SGrygorii Strashko spin_lock_irqsave(&cpts->lock, flags); 49987c0e764SRichard Cochran list_for_each_safe(this, next, &cpts->events) { 50087c0e764SRichard Cochran event = list_entry(this, struct cpts_event, list); 50187c0e764SRichard Cochran if (event_expired(event)) { 50287c0e764SRichard Cochran list_del_init(&event->list); 50387c0e764SRichard Cochran list_add(&event->list, &cpts->pool); 50487c0e764SRichard Cochran continue; 50587c0e764SRichard Cochran } 5063bfd41b5SGrygorii Strashko 5073bfd41b5SGrygorii Strashko mtype_seqid = event->high & 5083bfd41b5SGrygorii Strashko ((MESSAGE_TYPE_MASK << MESSAGE_TYPE_SHIFT) | 5093bfd41b5SGrygorii Strashko (SEQUENCE_ID_MASK << SEQUENCE_ID_SHIFT) | 5103bfd41b5SGrygorii Strashko (EVENT_TYPE_MASK << EVENT_TYPE_SHIFT)); 5113bfd41b5SGrygorii Strashko 5123bfd41b5SGrygorii Strashko if (mtype_seqid == skb_mtype_seqid) { 513e66dccceSGrygorii Strashko ns = event->timestamp; 51487c0e764SRichard Cochran list_del_init(&event->list); 51587c0e764SRichard Cochran list_add(&event->list, &cpts->pool); 51687c0e764SRichard Cochran break; 51787c0e764SRichard Cochran } 51887c0e764SRichard Cochran } 51987c0e764SRichard Cochran spin_unlock_irqrestore(&cpts->lock, flags); 52087c0e764SRichard Cochran 52187c0e764SRichard Cochran return ns; 52287c0e764SRichard Cochran } 52387c0e764SRichard Cochran 52487c0e764SRichard Cochran void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb) 52587c0e764SRichard Cochran { 5263bfd41b5SGrygorii Strashko struct cpts_skb_cb_data *skb_cb = (struct cpts_skb_cb_data *)skb->cb; 52787c0e764SRichard Cochran struct skb_shared_hwtstamps *ssh; 5283bfd41b5SGrygorii Strashko int ret; 5293bfd41b5SGrygorii Strashko u64 ns; 53087c0e764SRichard Cochran 5313bfd41b5SGrygorii Strashko ret = cpts_skb_get_mtype_seqid(skb, &skb_cb->skb_mtype_seqid); 5323bfd41b5SGrygorii Strashko if (!ret) 5333bfd41b5SGrygorii Strashko return; 5343bfd41b5SGrygorii Strashko 5353bfd41b5SGrygorii Strashko skb_cb->skb_mtype_seqid |= (CPTS_EV_RX << EVENT_TYPE_SHIFT); 5363bfd41b5SGrygorii Strashko 5373bfd41b5SGrygorii Strashko dev_dbg(cpts->dev, "%s mtype seqid %08x\n", 5383bfd41b5SGrygorii Strashko __func__, skb_cb->skb_mtype_seqid); 5393bfd41b5SGrygorii Strashko 5403bfd41b5SGrygorii Strashko ns = cpts_find_ts(cpts, skb, CPTS_EV_RX, skb_cb->skb_mtype_seqid); 54187c0e764SRichard Cochran if (!ns) 54287c0e764SRichard Cochran return; 54387c0e764SRichard Cochran ssh = skb_hwtstamps(skb); 54487c0e764SRichard Cochran memset(ssh, 0, sizeof(*ssh)); 54587c0e764SRichard Cochran ssh->hwtstamp = ns_to_ktime(ns); 54687c0e764SRichard Cochran } 547c8395d4eSGrygorii Strashko EXPORT_SYMBOL_GPL(cpts_rx_timestamp); 54887c0e764SRichard Cochran 54987c0e764SRichard Cochran void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb) 55087c0e764SRichard Cochran { 5513bfd41b5SGrygorii Strashko struct cpts_skb_cb_data *skb_cb = (struct cpts_skb_cb_data *)skb->cb; 5523bfd41b5SGrygorii Strashko int ret; 55387c0e764SRichard Cochran 55487c0e764SRichard Cochran if (!(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) 55587c0e764SRichard Cochran return; 5563bfd41b5SGrygorii Strashko 5573bfd41b5SGrygorii Strashko ret = cpts_skb_get_mtype_seqid(skb, &skb_cb->skb_mtype_seqid); 5583bfd41b5SGrygorii Strashko if (!ret) 5593bfd41b5SGrygorii Strashko return; 5603bfd41b5SGrygorii Strashko 5613bfd41b5SGrygorii Strashko skb_cb->skb_mtype_seqid |= (CPTS_EV_TX << EVENT_TYPE_SHIFT); 5623bfd41b5SGrygorii Strashko 5633bfd41b5SGrygorii Strashko dev_dbg(cpts->dev, "%s mtype seqid %08x\n", 5643bfd41b5SGrygorii Strashko __func__, skb_cb->skb_mtype_seqid); 5653bfd41b5SGrygorii Strashko 566c8f8e47eSGrygorii Strashko /* Always defer TX TS processing to PTP worker */ 567c8f8e47eSGrygorii Strashko skb_get(skb); 568c8f8e47eSGrygorii Strashko /* get the timestamp for timeouts */ 569c8f8e47eSGrygorii Strashko skb_cb->tmo = jiffies + msecs_to_jiffies(CPTS_SKB_RX_TX_TMO); 570c8f8e47eSGrygorii Strashko skb_queue_tail(&cpts->txq, skb); 571c8f8e47eSGrygorii Strashko ptp_schedule_worker(cpts->clock, 0); 57287c0e764SRichard Cochran } 573c8395d4eSGrygorii Strashko EXPORT_SYMBOL_GPL(cpts_tx_timestamp); 57487c0e764SRichard Cochran 5758a2c9a5aSGrygorii Strashko int cpts_register(struct cpts *cpts) 57687c0e764SRichard Cochran { 57787c0e764SRichard Cochran int err, i; 57887c0e764SRichard Cochran 5790d5f54feSGrygorii Strashko skb_queue_head_init(&cpts->txq); 58087c0e764SRichard Cochran INIT_LIST_HEAD(&cpts->events); 58187c0e764SRichard Cochran INIT_LIST_HEAD(&cpts->pool); 58287c0e764SRichard Cochran for (i = 0; i < CPTS_MAX_EVENTS; i++) 58387c0e764SRichard Cochran list_add(&cpts->pool_data[i].list, &cpts->pool); 58487c0e764SRichard Cochran 5858a2c9a5aSGrygorii Strashko clk_enable(cpts->refclk); 5868a2c9a5aSGrygorii Strashko 58787c0e764SRichard Cochran cpts_write32(cpts, CPTS_EN, control); 58887c0e764SRichard Cochran cpts_write32(cpts, TS_PEND_EN, int_enable); 58987c0e764SRichard Cochran 590693bd8b7SIvan Khoronzhuk timecounter_init(&cpts->tc, &cpts->cc, ktime_get_real_ns()); 59187c0e764SRichard Cochran 5928a2c9a5aSGrygorii Strashko cpts->clock = ptp_clock_register(&cpts->info, cpts->dev); 5936c691405SGrygorii Strashko if (IS_ERR(cpts->clock)) { 5946c691405SGrygorii Strashko err = PTR_ERR(cpts->clock); 5956c691405SGrygorii Strashko cpts->clock = NULL; 5966c691405SGrygorii Strashko goto err_ptp; 5976c691405SGrygorii Strashko } 5986c691405SGrygorii Strashko cpts->phc_index = ptp_clock_index(cpts->clock); 5996c691405SGrygorii Strashko 600999f1292SGrygorii Strashko ptp_schedule_worker(cpts->clock, cpts->ov_check_period); 60187c0e764SRichard Cochran return 0; 6026c691405SGrygorii Strashko 6036c691405SGrygorii Strashko err_ptp: 6048a2c9a5aSGrygorii Strashko clk_disable(cpts->refclk); 6056c691405SGrygorii Strashko return err; 60687c0e764SRichard Cochran } 607c8395d4eSGrygorii Strashko EXPORT_SYMBOL_GPL(cpts_register); 60887c0e764SRichard Cochran 60987c0e764SRichard Cochran void cpts_unregister(struct cpts *cpts) 61087c0e764SRichard Cochran { 6118a2c9a5aSGrygorii Strashko if (WARN_ON(!cpts->clock)) 6128a2c9a5aSGrygorii Strashko return; 6138a2c9a5aSGrygorii Strashko 6148a2c9a5aSGrygorii Strashko ptp_clock_unregister(cpts->clock); 6158a2c9a5aSGrygorii Strashko cpts->clock = NULL; 6168fcd6891SGrygorii Strashko 6178fcd6891SGrygorii Strashko cpts_write32(cpts, 0, int_enable); 6188fcd6891SGrygorii Strashko cpts_write32(cpts, 0, control); 6198fcd6891SGrygorii Strashko 6200d5f54feSGrygorii Strashko /* Drop all packet */ 6210d5f54feSGrygorii Strashko skb_queue_purge(&cpts->txq); 6220d5f54feSGrygorii Strashko 6238a2c9a5aSGrygorii Strashko clk_disable(cpts->refclk); 62487c0e764SRichard Cochran } 625c8395d4eSGrygorii Strashko EXPORT_SYMBOL_GPL(cpts_unregister); 626c8395d4eSGrygorii Strashko 62788f0f0b0SGrygorii Strashko static void cpts_calc_mult_shift(struct cpts *cpts) 62888f0f0b0SGrygorii Strashko { 62988f0f0b0SGrygorii Strashko u64 frac, maxsec, ns; 63088f0f0b0SGrygorii Strashko u32 freq; 63188f0f0b0SGrygorii Strashko 63288f0f0b0SGrygorii Strashko freq = clk_get_rate(cpts->refclk); 63388f0f0b0SGrygorii Strashko 63488f0f0b0SGrygorii Strashko /* Calc the maximum number of seconds which we can run before 63588f0f0b0SGrygorii Strashko * wrapping around. 63688f0f0b0SGrygorii Strashko */ 63788f0f0b0SGrygorii Strashko maxsec = cpts->cc.mask; 63888f0f0b0SGrygorii Strashko do_div(maxsec, freq); 63988f0f0b0SGrygorii Strashko /* limit conversation rate to 10 sec as higher values will produce 64088f0f0b0SGrygorii Strashko * too small mult factors and so reduce the conversion accuracy 64188f0f0b0SGrygorii Strashko */ 64288f0f0b0SGrygorii Strashko if (maxsec > 10) 64388f0f0b0SGrygorii Strashko maxsec = 10; 64488f0f0b0SGrygorii Strashko 64520138cf9SGrygorii Strashko /* Calc overflow check period (maxsec / 2) */ 64620138cf9SGrygorii Strashko cpts->ov_check_period = (HZ * maxsec) / 2; 64720138cf9SGrygorii Strashko dev_info(cpts->dev, "cpts: overflow check period %lu (jiffies)\n", 64820138cf9SGrygorii Strashko cpts->ov_check_period); 64920138cf9SGrygorii Strashko 65088f0f0b0SGrygorii Strashko if (cpts->cc.mult || cpts->cc.shift) 65188f0f0b0SGrygorii Strashko return; 65288f0f0b0SGrygorii Strashko 65388f0f0b0SGrygorii Strashko clocks_calc_mult_shift(&cpts->cc.mult, &cpts->cc.shift, 65488f0f0b0SGrygorii Strashko freq, NSEC_PER_SEC, maxsec); 65588f0f0b0SGrygorii Strashko 65688f0f0b0SGrygorii Strashko frac = 0; 65788f0f0b0SGrygorii Strashko ns = cyclecounter_cyc2ns(&cpts->cc, freq, cpts->cc.mask, &frac); 65888f0f0b0SGrygorii Strashko 65988f0f0b0SGrygorii Strashko dev_info(cpts->dev, 66088f0f0b0SGrygorii Strashko "CPTS: ref_clk_freq:%u calc_mult:%u calc_shift:%u error:%lld nsec/sec\n", 66188f0f0b0SGrygorii Strashko freq, cpts->cc.mult, cpts->cc.shift, (ns - NSEC_PER_SEC)); 66288f0f0b0SGrygorii Strashko } 66388f0f0b0SGrygorii Strashko 664a3047a81SGrygorii Strashko static int cpts_of_mux_clk_setup(struct cpts *cpts, struct device_node *node) 665a3047a81SGrygorii Strashko { 666a3047a81SGrygorii Strashko struct device_node *refclk_np; 667a3047a81SGrygorii Strashko const char **parent_names; 668a3047a81SGrygorii Strashko unsigned int num_parents; 669a3047a81SGrygorii Strashko struct clk_hw *clk_hw; 670a3047a81SGrygorii Strashko int ret = -EINVAL; 671a3047a81SGrygorii Strashko u32 *mux_table; 672a3047a81SGrygorii Strashko 673a3047a81SGrygorii Strashko refclk_np = of_get_child_by_name(node, "cpts-refclk-mux"); 674a3047a81SGrygorii Strashko if (!refclk_np) 675a3047a81SGrygorii Strashko /* refclk selection supported not for all SoCs */ 676a3047a81SGrygorii Strashko return 0; 677a3047a81SGrygorii Strashko 678a3047a81SGrygorii Strashko num_parents = of_clk_get_parent_count(refclk_np); 679a3047a81SGrygorii Strashko if (num_parents < 1) { 680a3047a81SGrygorii Strashko dev_err(cpts->dev, "mux-clock %s must have parents\n", 681a3047a81SGrygorii Strashko refclk_np->name); 682a3047a81SGrygorii Strashko goto mux_fail; 683a3047a81SGrygorii Strashko } 684a3047a81SGrygorii Strashko 685a3047a81SGrygorii Strashko parent_names = devm_kzalloc(cpts->dev, (sizeof(char *) * num_parents), 686a3047a81SGrygorii Strashko GFP_KERNEL); 687a3047a81SGrygorii Strashko 688a3047a81SGrygorii Strashko mux_table = devm_kzalloc(cpts->dev, sizeof(*mux_table) * num_parents, 689a3047a81SGrygorii Strashko GFP_KERNEL); 690a3047a81SGrygorii Strashko if (!mux_table || !parent_names) { 691a3047a81SGrygorii Strashko ret = -ENOMEM; 692a3047a81SGrygorii Strashko goto mux_fail; 693a3047a81SGrygorii Strashko } 694a3047a81SGrygorii Strashko 695a3047a81SGrygorii Strashko of_clk_parent_fill(refclk_np, parent_names, num_parents); 696a3047a81SGrygorii Strashko 697a3047a81SGrygorii Strashko ret = of_property_read_variable_u32_array(refclk_np, "ti,mux-tbl", 698a3047a81SGrygorii Strashko mux_table, 699a3047a81SGrygorii Strashko num_parents, num_parents); 700a3047a81SGrygorii Strashko if (ret < 0) 701a3047a81SGrygorii Strashko goto mux_fail; 702a3047a81SGrygorii Strashko 703a3047a81SGrygorii Strashko clk_hw = clk_hw_register_mux_table(cpts->dev, refclk_np->name, 704a3047a81SGrygorii Strashko parent_names, num_parents, 705a3047a81SGrygorii Strashko 0, 706a3047a81SGrygorii Strashko &cpts->reg->rftclk_sel, 0, 0x1F, 707a3047a81SGrygorii Strashko 0, mux_table, NULL); 708a3047a81SGrygorii Strashko if (IS_ERR(clk_hw)) { 709a3047a81SGrygorii Strashko ret = PTR_ERR(clk_hw); 710a3047a81SGrygorii Strashko goto mux_fail; 711a3047a81SGrygorii Strashko } 712a3047a81SGrygorii Strashko 713a3047a81SGrygorii Strashko ret = devm_add_action_or_reset(cpts->dev, 714a3047a81SGrygorii Strashko (void(*)(void *))clk_hw_unregister_mux, 715a3047a81SGrygorii Strashko clk_hw); 716a3047a81SGrygorii Strashko if (ret) { 717a3047a81SGrygorii Strashko dev_err(cpts->dev, "add clkmux unreg action %d", ret); 718a3047a81SGrygorii Strashko goto mux_fail; 719a3047a81SGrygorii Strashko } 720a3047a81SGrygorii Strashko 721a3047a81SGrygorii Strashko ret = of_clk_add_hw_provider(refclk_np, of_clk_hw_simple_get, clk_hw); 722a3047a81SGrygorii Strashko if (ret) 723a3047a81SGrygorii Strashko goto mux_fail; 724a3047a81SGrygorii Strashko 725a3047a81SGrygorii Strashko ret = devm_add_action_or_reset(cpts->dev, 726a3047a81SGrygorii Strashko (void(*)(void *))of_clk_del_provider, 727a3047a81SGrygorii Strashko refclk_np); 728a3047a81SGrygorii Strashko if (ret) { 729a3047a81SGrygorii Strashko dev_err(cpts->dev, "add clkmux provider unreg action %d", ret); 730a3047a81SGrygorii Strashko goto mux_fail; 731a3047a81SGrygorii Strashko } 732a3047a81SGrygorii Strashko 733a3047a81SGrygorii Strashko return ret; 734a3047a81SGrygorii Strashko 735a3047a81SGrygorii Strashko mux_fail: 736a3047a81SGrygorii Strashko of_node_put(refclk_np); 737a3047a81SGrygorii Strashko return ret; 738a3047a81SGrygorii Strashko } 739a3047a81SGrygorii Strashko 7404a88fb95SGrygorii Strashko static int cpts_of_parse(struct cpts *cpts, struct device_node *node) 7414a88fb95SGrygorii Strashko { 7424a88fb95SGrygorii Strashko int ret = -EINVAL; 7434a88fb95SGrygorii Strashko u32 prop; 7444a88fb95SGrygorii Strashko 74588f0f0b0SGrygorii Strashko if (!of_property_read_u32(node, "cpts_clock_mult", &prop)) 7464a88fb95SGrygorii Strashko cpts->cc.mult = prop; 7474a88fb95SGrygorii Strashko 74888f0f0b0SGrygorii Strashko if (!of_property_read_u32(node, "cpts_clock_shift", &prop)) 7494a88fb95SGrygorii Strashko cpts->cc.shift = prop; 7504a88fb95SGrygorii Strashko 75188f0f0b0SGrygorii Strashko if ((cpts->cc.mult && !cpts->cc.shift) || 75288f0f0b0SGrygorii Strashko (!cpts->cc.mult && cpts->cc.shift)) 75388f0f0b0SGrygorii Strashko goto of_error; 75488f0f0b0SGrygorii Strashko 755a3047a81SGrygorii Strashko return cpts_of_mux_clk_setup(cpts, node); 7564a88fb95SGrygorii Strashko 7574a88fb95SGrygorii Strashko of_error: 7584a88fb95SGrygorii Strashko dev_err(cpts->dev, "CPTS: Missing property in the DT.\n"); 7594a88fb95SGrygorii Strashko return ret; 7604a88fb95SGrygorii Strashko } 7614a88fb95SGrygorii Strashko 7628a2c9a5aSGrygorii Strashko struct cpts *cpts_create(struct device *dev, void __iomem *regs, 763*b78aba49SGrygorii Strashko struct device_node *node, u32 n_ext_ts) 7648a2c9a5aSGrygorii Strashko { 7658a2c9a5aSGrygorii Strashko struct cpts *cpts; 7664a88fb95SGrygorii Strashko int ret; 7678a2c9a5aSGrygorii Strashko 7688a2c9a5aSGrygorii Strashko cpts = devm_kzalloc(dev, sizeof(*cpts), GFP_KERNEL); 7698a2c9a5aSGrygorii Strashko if (!cpts) 7708a2c9a5aSGrygorii Strashko return ERR_PTR(-ENOMEM); 7718a2c9a5aSGrygorii Strashko 7728a2c9a5aSGrygorii Strashko cpts->dev = dev; 7738a2c9a5aSGrygorii Strashko cpts->reg = (struct cpsw_cpts __iomem *)regs; 77485624412SGrygorii Strashko cpts->irq_poll = true; 7758a2c9a5aSGrygorii Strashko spin_lock_init(&cpts->lock); 776ba107428SGrygorii Strashko mutex_init(&cpts->ptp_clk_mutex); 77785624412SGrygorii Strashko init_completion(&cpts->ts_push_complete); 7788a2c9a5aSGrygorii Strashko 7794a88fb95SGrygorii Strashko ret = cpts_of_parse(cpts, node); 7804a88fb95SGrygorii Strashko if (ret) 7814a88fb95SGrygorii Strashko return ERR_PTR(ret); 7824a88fb95SGrygorii Strashko 7838a6389a5SGrygorii Strashko cpts->refclk = devm_get_clk_from_child(dev, node, "cpts"); 7848a6389a5SGrygorii Strashko if (IS_ERR(cpts->refclk)) 7858a6389a5SGrygorii Strashko /* try get clk from dev node for compatibility */ 7868a2c9a5aSGrygorii Strashko cpts->refclk = devm_clk_get(dev, "cpts"); 7878a6389a5SGrygorii Strashko 7888a2c9a5aSGrygorii Strashko if (IS_ERR(cpts->refclk)) { 7898a6389a5SGrygorii Strashko dev_err(dev, "Failed to get cpts refclk %ld\n", 7908a6389a5SGrygorii Strashko PTR_ERR(cpts->refclk)); 791bde4c563SHernán Gonzalez return ERR_CAST(cpts->refclk); 7928a2c9a5aSGrygorii Strashko } 7938a2c9a5aSGrygorii Strashko 7942d822f2dSKangjie Lu ret = clk_prepare(cpts->refclk); 7952d822f2dSKangjie Lu if (ret) 7962d822f2dSKangjie Lu return ERR_PTR(ret); 7978a2c9a5aSGrygorii Strashko 7988a2c9a5aSGrygorii Strashko cpts->cc.read = cpts_systim_read; 7998a2c9a5aSGrygorii Strashko cpts->cc.mask = CLOCKSOURCE_MASK(32); 80088f0f0b0SGrygorii Strashko cpts->info = cpts_info; 80188f0f0b0SGrygorii Strashko 802*b78aba49SGrygorii Strashko if (n_ext_ts) 803*b78aba49SGrygorii Strashko cpts->info.n_ext_ts = n_ext_ts; 804*b78aba49SGrygorii Strashko 80588f0f0b0SGrygorii Strashko cpts_calc_mult_shift(cpts); 8064a88fb95SGrygorii Strashko /* save cc.mult original value as it can be modified 8074a88fb95SGrygorii Strashko * by cpts_ptp_adjfreq(). 8084a88fb95SGrygorii Strashko */ 8094a88fb95SGrygorii Strashko cpts->cc_mult = cpts->cc.mult; 8108a2c9a5aSGrygorii Strashko 8118a2c9a5aSGrygorii Strashko return cpts; 8128a2c9a5aSGrygorii Strashko } 8138a2c9a5aSGrygorii Strashko EXPORT_SYMBOL_GPL(cpts_create); 8148a2c9a5aSGrygorii Strashko 8158a2c9a5aSGrygorii Strashko void cpts_release(struct cpts *cpts) 8168a2c9a5aSGrygorii Strashko { 8178a2c9a5aSGrygorii Strashko if (!cpts) 8188a2c9a5aSGrygorii Strashko return; 8198a2c9a5aSGrygorii Strashko 8208a2c9a5aSGrygorii Strashko if (WARN_ON(!cpts->refclk)) 8218a2c9a5aSGrygorii Strashko return; 8228a2c9a5aSGrygorii Strashko 8238a2c9a5aSGrygorii Strashko clk_unprepare(cpts->refclk); 8248a2c9a5aSGrygorii Strashko } 8258a2c9a5aSGrygorii Strashko EXPORT_SYMBOL_GPL(cpts_release); 8268a2c9a5aSGrygorii Strashko 827c8395d4eSGrygorii Strashko MODULE_LICENSE("GPL v2"); 828c8395d4eSGrygorii Strashko MODULE_DESCRIPTION("TI CPTS driver"); 829c8395d4eSGrygorii Strashko MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>"); 830