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 35b78aba49SGrygorii Strashko static int cpts_event_port(struct cpts_event *event) 36b78aba49SGrygorii Strashko { 37b78aba49SGrygorii Strashko return (event->high >> PORT_NUMBER_SHIFT) & PORT_NUMBER_MASK; 38b78aba49SGrygorii Strashko } 39b78aba49SGrygorii 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 { 107b78aba49SGrygorii 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: 155b78aba49SGrygorii Strashko break; 15687c0e764SRichard Cochran case CPTS_EV_HW: 157b78aba49SGrygorii Strashko pevent.timestamp = event->timestamp; 158b78aba49SGrygorii Strashko pevent.type = PTP_CLOCK_EXTTS; 159b78aba49SGrygorii Strashko pevent.index = cpts_event_port(event) - 1; 160b78aba49SGrygorii 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 216*a4539207SJacob Keller static int cpts_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) 21787c0e764SRichard Cochran { 21887c0e764SRichard Cochran struct cpts *cpts = container_of(ptp, struct cpts, info); 21987c0e764SRichard Cochran 220ba107428SGrygorii Strashko mutex_lock(&cpts->ptp_clk_mutex); 22187c0e764SRichard Cochran 222*a4539207SJacob Keller cpts->mult_new = adjust_by_scaled_ppm(cpts->cc_mult, scaled_ppm); 22387c0e764SRichard Cochran 224856e59abSGrygorii Strashko cpts_update_cur_time(cpts, CPTS_EV_PUSH, NULL); 22587c0e764SRichard Cochran 226ba107428SGrygorii Strashko mutex_unlock(&cpts->ptp_clk_mutex); 22787c0e764SRichard Cochran return 0; 22887c0e764SRichard Cochran } 22987c0e764SRichard Cochran 23087c0e764SRichard Cochran static int cpts_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) 23187c0e764SRichard Cochran { 23287c0e764SRichard Cochran struct cpts *cpts = container_of(ptp, struct cpts, info); 23387c0e764SRichard Cochran 234ba107428SGrygorii Strashko mutex_lock(&cpts->ptp_clk_mutex); 235f25a30beSRichard Cochran timecounter_adjtime(&cpts->tc, delta); 236ba107428SGrygorii Strashko mutex_unlock(&cpts->ptp_clk_mutex); 23787c0e764SRichard Cochran 23887c0e764SRichard Cochran return 0; 23987c0e764SRichard Cochran } 24087c0e764SRichard Cochran 241856e59abSGrygorii Strashko static int cpts_ptp_gettimeex(struct ptp_clock_info *ptp, 242856e59abSGrygorii Strashko struct timespec64 *ts, 243856e59abSGrygorii Strashko struct ptp_system_timestamp *sts) 24487c0e764SRichard Cochran { 24587c0e764SRichard Cochran struct cpts *cpts = container_of(ptp, struct cpts, info); 246856e59abSGrygorii Strashko u64 ns; 24787c0e764SRichard Cochran 248ba107428SGrygorii Strashko mutex_lock(&cpts->ptp_clk_mutex); 249e66dccceSGrygorii Strashko 250856e59abSGrygorii Strashko cpts_update_cur_time(cpts, CPTS_EV_PUSH, sts); 251e66dccceSGrygorii Strashko 25287c0e764SRichard Cochran ns = timecounter_read(&cpts->tc); 253ba107428SGrygorii Strashko mutex_unlock(&cpts->ptp_clk_mutex); 25487c0e764SRichard Cochran 25584d923ceSRichard Cochran *ts = ns_to_timespec64(ns); 25687c0e764SRichard Cochran 25787c0e764SRichard Cochran return 0; 25887c0e764SRichard Cochran } 25987c0e764SRichard Cochran 26087c0e764SRichard Cochran static int cpts_ptp_settime(struct ptp_clock_info *ptp, 261a5c79c26SRichard Cochran const struct timespec64 *ts) 26287c0e764SRichard Cochran { 26387c0e764SRichard Cochran struct cpts *cpts = container_of(ptp, struct cpts, info); 264ba107428SGrygorii Strashko u64 ns; 26587c0e764SRichard Cochran 26684d923ceSRichard Cochran ns = timespec64_to_ns(ts); 26787c0e764SRichard Cochran 268ba107428SGrygorii Strashko mutex_lock(&cpts->ptp_clk_mutex); 26987c0e764SRichard Cochran timecounter_init(&cpts->tc, &cpts->cc, ns); 270ba107428SGrygorii Strashko mutex_unlock(&cpts->ptp_clk_mutex); 27187c0e764SRichard Cochran 27287c0e764SRichard Cochran return 0; 27387c0e764SRichard Cochran } 27487c0e764SRichard Cochran 275b78aba49SGrygorii Strashko static int cpts_extts_enable(struct cpts *cpts, u32 index, int on) 276b78aba49SGrygorii Strashko { 277b78aba49SGrygorii Strashko u32 v; 278b78aba49SGrygorii Strashko 279b78aba49SGrygorii Strashko if (((cpts->hw_ts_enable & BIT(index)) >> index) == on) 280b78aba49SGrygorii Strashko return 0; 281b78aba49SGrygorii Strashko 282b78aba49SGrygorii Strashko mutex_lock(&cpts->ptp_clk_mutex); 283b78aba49SGrygorii Strashko 284b78aba49SGrygorii Strashko v = cpts_read32(cpts, control); 285b78aba49SGrygorii Strashko if (on) { 286b78aba49SGrygorii Strashko v |= BIT(8 + index); 287b78aba49SGrygorii Strashko cpts->hw_ts_enable |= BIT(index); 288b78aba49SGrygorii Strashko } else { 289b78aba49SGrygorii Strashko v &= ~BIT(8 + index); 290b78aba49SGrygorii Strashko cpts->hw_ts_enable &= ~BIT(index); 291b78aba49SGrygorii Strashko } 292b78aba49SGrygorii Strashko cpts_write32(cpts, v, control); 293b78aba49SGrygorii Strashko 294b78aba49SGrygorii Strashko mutex_unlock(&cpts->ptp_clk_mutex); 295b78aba49SGrygorii Strashko 296b78aba49SGrygorii Strashko return 0; 297b78aba49SGrygorii Strashko } 298b78aba49SGrygorii Strashko 29987c0e764SRichard Cochran static int cpts_ptp_enable(struct ptp_clock_info *ptp, 30087c0e764SRichard Cochran struct ptp_clock_request *rq, int on) 30187c0e764SRichard Cochran { 302b78aba49SGrygorii Strashko struct cpts *cpts = container_of(ptp, struct cpts, info); 303b78aba49SGrygorii Strashko 304b78aba49SGrygorii Strashko switch (rq->type) { 305b78aba49SGrygorii Strashko case PTP_CLK_REQ_EXTTS: 306b78aba49SGrygorii Strashko return cpts_extts_enable(cpts, rq->extts.index, on); 307b78aba49SGrygorii Strashko default: 308b78aba49SGrygorii Strashko break; 309b78aba49SGrygorii Strashko } 310b78aba49SGrygorii Strashko 31187c0e764SRichard Cochran return -EOPNOTSUPP; 31287c0e764SRichard Cochran } 31387c0e764SRichard Cochran 314c8f8e47eSGrygorii Strashko static bool cpts_match_tx_ts(struct cpts *cpts, struct cpts_event *event) 315c8f8e47eSGrygorii Strashko { 316c8f8e47eSGrygorii Strashko struct sk_buff_head txq_list; 317c8f8e47eSGrygorii Strashko struct sk_buff *skb, *tmp; 318c8f8e47eSGrygorii Strashko unsigned long flags; 319c8f8e47eSGrygorii Strashko bool found = false; 320c8f8e47eSGrygorii Strashko u32 mtype_seqid; 321c8f8e47eSGrygorii Strashko 322c8f8e47eSGrygorii Strashko mtype_seqid = event->high & 323c8f8e47eSGrygorii Strashko ((MESSAGE_TYPE_MASK << MESSAGE_TYPE_SHIFT) | 324c8f8e47eSGrygorii Strashko (SEQUENCE_ID_MASK << SEQUENCE_ID_SHIFT) | 325c8f8e47eSGrygorii Strashko (EVENT_TYPE_MASK << EVENT_TYPE_SHIFT)); 326c8f8e47eSGrygorii Strashko 327c8f8e47eSGrygorii Strashko __skb_queue_head_init(&txq_list); 328c8f8e47eSGrygorii Strashko 329c8f8e47eSGrygorii Strashko spin_lock_irqsave(&cpts->txq.lock, flags); 330c8f8e47eSGrygorii Strashko skb_queue_splice_init(&cpts->txq, &txq_list); 331c8f8e47eSGrygorii Strashko spin_unlock_irqrestore(&cpts->txq.lock, flags); 332c8f8e47eSGrygorii Strashko 333c8f8e47eSGrygorii Strashko skb_queue_walk_safe(&txq_list, skb, tmp) { 334c8f8e47eSGrygorii Strashko struct skb_shared_hwtstamps ssh; 335c8f8e47eSGrygorii Strashko struct cpts_skb_cb_data *skb_cb = 336c8f8e47eSGrygorii Strashko (struct cpts_skb_cb_data *)skb->cb; 337c8f8e47eSGrygorii Strashko 338c8f8e47eSGrygorii Strashko if (mtype_seqid == skb_cb->skb_mtype_seqid) { 339c8f8e47eSGrygorii Strashko memset(&ssh, 0, sizeof(ssh)); 340c8f8e47eSGrygorii Strashko ssh.hwtstamp = ns_to_ktime(event->timestamp); 341c8f8e47eSGrygorii Strashko skb_tstamp_tx(skb, &ssh); 342c8f8e47eSGrygorii Strashko found = true; 343c8f8e47eSGrygorii Strashko __skb_unlink(skb, &txq_list); 344c8f8e47eSGrygorii Strashko dev_consume_skb_any(skb); 345c8f8e47eSGrygorii Strashko dev_dbg(cpts->dev, "match tx timestamp mtype_seqid %08x\n", 346c8f8e47eSGrygorii Strashko mtype_seqid); 347c8f8e47eSGrygorii Strashko break; 348c8f8e47eSGrygorii Strashko } 349c8f8e47eSGrygorii Strashko 350c8f8e47eSGrygorii Strashko if (time_after(jiffies, skb_cb->tmo)) { 351c8f8e47eSGrygorii Strashko /* timeout any expired skbs over 1s */ 352c8f8e47eSGrygorii Strashko dev_dbg(cpts->dev, "expiring tx timestamp from txq\n"); 353c8f8e47eSGrygorii Strashko __skb_unlink(skb, &txq_list); 354c8f8e47eSGrygorii Strashko dev_consume_skb_any(skb); 355c8f8e47eSGrygorii Strashko } 356c8f8e47eSGrygorii Strashko } 357c8f8e47eSGrygorii Strashko 358c8f8e47eSGrygorii Strashko spin_lock_irqsave(&cpts->txq.lock, flags); 359c8f8e47eSGrygorii Strashko skb_queue_splice(&txq_list, &cpts->txq); 360c8f8e47eSGrygorii Strashko spin_unlock_irqrestore(&cpts->txq.lock, flags); 361c8f8e47eSGrygorii Strashko 362c8f8e47eSGrygorii Strashko return found; 363c8f8e47eSGrygorii Strashko } 364c8f8e47eSGrygorii Strashko 365c8f8e47eSGrygorii Strashko static void cpts_process_events(struct cpts *cpts) 366c8f8e47eSGrygorii Strashko { 367c8f8e47eSGrygorii Strashko struct list_head *this, *next; 368c8f8e47eSGrygorii Strashko struct cpts_event *event; 369c8f8e47eSGrygorii Strashko LIST_HEAD(events_free); 370c8f8e47eSGrygorii Strashko unsigned long flags; 371c8f8e47eSGrygorii Strashko LIST_HEAD(events); 372c8f8e47eSGrygorii Strashko 373c8f8e47eSGrygorii Strashko spin_lock_irqsave(&cpts->lock, flags); 374c8f8e47eSGrygorii Strashko list_splice_init(&cpts->events, &events); 375c8f8e47eSGrygorii Strashko spin_unlock_irqrestore(&cpts->lock, flags); 376c8f8e47eSGrygorii Strashko 377c8f8e47eSGrygorii Strashko list_for_each_safe(this, next, &events) { 378c8f8e47eSGrygorii Strashko event = list_entry(this, struct cpts_event, list); 379c8f8e47eSGrygorii Strashko if (cpts_match_tx_ts(cpts, event) || 380c8f8e47eSGrygorii Strashko time_after(jiffies, event->tmo)) { 381c8f8e47eSGrygorii Strashko list_del_init(&event->list); 382c8f8e47eSGrygorii Strashko list_add(&event->list, &events_free); 383c8f8e47eSGrygorii Strashko } 384c8f8e47eSGrygorii Strashko } 385c8f8e47eSGrygorii Strashko 386c8f8e47eSGrygorii Strashko spin_lock_irqsave(&cpts->lock, flags); 387c8f8e47eSGrygorii Strashko list_splice_tail(&events, &cpts->events); 388c8f8e47eSGrygorii Strashko list_splice_tail(&events_free, &cpts->pool); 389c8f8e47eSGrygorii Strashko spin_unlock_irqrestore(&cpts->lock, flags); 390c8f8e47eSGrygorii Strashko } 391c8f8e47eSGrygorii Strashko 392999f1292SGrygorii Strashko static long cpts_overflow_check(struct ptp_clock_info *ptp) 393999f1292SGrygorii Strashko { 394999f1292SGrygorii Strashko struct cpts *cpts = container_of(ptp, struct cpts, info); 395999f1292SGrygorii Strashko unsigned long delay = cpts->ov_check_period; 3960d5f54feSGrygorii Strashko unsigned long flags; 397e66dccceSGrygorii Strashko u64 ns; 398999f1292SGrygorii Strashko 399ba107428SGrygorii Strashko mutex_lock(&cpts->ptp_clk_mutex); 400e66dccceSGrygorii Strashko 401ba107428SGrygorii Strashko cpts_update_cur_time(cpts, -1, NULL); 402e66dccceSGrygorii Strashko ns = timecounter_read(&cpts->tc); 4030d5f54feSGrygorii Strashko 404c8f8e47eSGrygorii Strashko cpts_process_events(cpts); 405c8f8e47eSGrygorii Strashko 406c8f8e47eSGrygorii Strashko spin_lock_irqsave(&cpts->txq.lock, flags); 407f19dcd5fSIvan Khoronzhuk if (!skb_queue_empty(&cpts->txq)) { 408f19dcd5fSIvan Khoronzhuk cpts_purge_txq(cpts); 4090d5f54feSGrygorii Strashko if (!skb_queue_empty(&cpts->txq)) 4100d5f54feSGrygorii Strashko delay = CPTS_SKB_TX_WORK_TIMEOUT; 411f19dcd5fSIvan Khoronzhuk } 412c8f8e47eSGrygorii Strashko spin_unlock_irqrestore(&cpts->txq.lock, flags); 4130d5f54feSGrygorii Strashko 414e66dccceSGrygorii Strashko dev_dbg(cpts->dev, "cpts overflow check at %lld\n", ns); 415ba107428SGrygorii Strashko mutex_unlock(&cpts->ptp_clk_mutex); 416999f1292SGrygorii Strashko return (long)delay; 417999f1292SGrygorii Strashko } 418999f1292SGrygorii Strashko 419b6d08bd8SBhumika Goyal static const struct ptp_clock_info cpts_info = { 42087c0e764SRichard Cochran .owner = THIS_MODULE, 42187c0e764SRichard Cochran .name = "CTPS timer", 42287c0e764SRichard Cochran .max_adj = 1000000, 42387c0e764SRichard Cochran .n_ext_ts = 0, 4244986b4f0SRichard Cochran .n_pins = 0, 42587c0e764SRichard Cochran .pps = 0, 426*a4539207SJacob Keller .adjfine = cpts_ptp_adjfine, 42787c0e764SRichard Cochran .adjtime = cpts_ptp_adjtime, 428856e59abSGrygorii Strashko .gettimex64 = cpts_ptp_gettimeex, 429a5c79c26SRichard Cochran .settime64 = cpts_ptp_settime, 43087c0e764SRichard Cochran .enable = cpts_ptp_enable, 431999f1292SGrygorii Strashko .do_aux_work = cpts_overflow_check, 43287c0e764SRichard Cochran }; 43387c0e764SRichard Cochran 4343bfd41b5SGrygorii Strashko static int cpts_skb_get_mtype_seqid(struct sk_buff *skb, u32 *mtype_seqid) 43587c0e764SRichard Cochran { 4363bfd41b5SGrygorii Strashko unsigned int ptp_class = ptp_classify_raw(skb); 43717de44c2SKurt Kanzenbach struct ptp_header *hdr; 43817de44c2SKurt Kanzenbach u8 msgtype; 43917de44c2SKurt Kanzenbach u16 seqid; 4403bfd41b5SGrygorii Strashko 4413bfd41b5SGrygorii Strashko if (ptp_class == PTP_CLASS_NONE) 4423bfd41b5SGrygorii Strashko return 0; 44387c0e764SRichard Cochran 44417de44c2SKurt Kanzenbach hdr = ptp_parse_header(skb, ptp_class); 44517de44c2SKurt Kanzenbach if (!hdr) 44687c0e764SRichard Cochran return 0; 44787c0e764SRichard Cochran 44817de44c2SKurt Kanzenbach msgtype = ptp_get_msgtype(hdr, ptp_class); 44917de44c2SKurt Kanzenbach seqid = ntohs(hdr->sequence_id); 45087c0e764SRichard Cochran 45117de44c2SKurt Kanzenbach *mtype_seqid = (msgtype & MESSAGE_TYPE_MASK) << MESSAGE_TYPE_SHIFT; 45217de44c2SKurt Kanzenbach *mtype_seqid |= (seqid & SEQUENCE_ID_MASK) << SEQUENCE_ID_SHIFT; 45387c0e764SRichard Cochran 4543bfd41b5SGrygorii Strashko return 1; 45587c0e764SRichard Cochran } 45687c0e764SRichard Cochran 4573bfd41b5SGrygorii Strashko static u64 cpts_find_ts(struct cpts *cpts, struct sk_buff *skb, 4583bfd41b5SGrygorii Strashko int ev_type, u32 skb_mtype_seqid) 45987c0e764SRichard Cochran { 46087c0e764SRichard Cochran struct list_head *this, *next; 4613bfd41b5SGrygorii Strashko struct cpts_event *event; 46287c0e764SRichard Cochran unsigned long flags; 4633bfd41b5SGrygorii Strashko u32 mtype_seqid; 4643bfd41b5SGrygorii Strashko u64 ns = 0; 46587c0e764SRichard Cochran 466a93439ccSGrygorii Strashko cpts_fifo_read(cpts, -1); 467ba107428SGrygorii Strashko spin_lock_irqsave(&cpts->lock, flags); 46887c0e764SRichard Cochran list_for_each_safe(this, next, &cpts->events) { 46987c0e764SRichard Cochran event = list_entry(this, struct cpts_event, list); 47087c0e764SRichard Cochran if (event_expired(event)) { 47187c0e764SRichard Cochran list_del_init(&event->list); 47287c0e764SRichard Cochran list_add(&event->list, &cpts->pool); 47387c0e764SRichard Cochran continue; 47487c0e764SRichard Cochran } 4753bfd41b5SGrygorii Strashko 4763bfd41b5SGrygorii Strashko mtype_seqid = event->high & 4773bfd41b5SGrygorii Strashko ((MESSAGE_TYPE_MASK << MESSAGE_TYPE_SHIFT) | 4783bfd41b5SGrygorii Strashko (SEQUENCE_ID_MASK << SEQUENCE_ID_SHIFT) | 4793bfd41b5SGrygorii Strashko (EVENT_TYPE_MASK << EVENT_TYPE_SHIFT)); 4803bfd41b5SGrygorii Strashko 4813bfd41b5SGrygorii Strashko if (mtype_seqid == skb_mtype_seqid) { 482e66dccceSGrygorii Strashko ns = event->timestamp; 48387c0e764SRichard Cochran list_del_init(&event->list); 48487c0e764SRichard Cochran list_add(&event->list, &cpts->pool); 48587c0e764SRichard Cochran break; 48687c0e764SRichard Cochran } 48787c0e764SRichard Cochran } 48887c0e764SRichard Cochran spin_unlock_irqrestore(&cpts->lock, flags); 48987c0e764SRichard Cochran 49087c0e764SRichard Cochran return ns; 49187c0e764SRichard Cochran } 49287c0e764SRichard Cochran 49387c0e764SRichard Cochran void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb) 49487c0e764SRichard Cochran { 4953bfd41b5SGrygorii Strashko struct cpts_skb_cb_data *skb_cb = (struct cpts_skb_cb_data *)skb->cb; 49687c0e764SRichard Cochran struct skb_shared_hwtstamps *ssh; 4973bfd41b5SGrygorii Strashko int ret; 4983bfd41b5SGrygorii Strashko u64 ns; 49987c0e764SRichard Cochran 50017de44c2SKurt Kanzenbach /* cpts_rx_timestamp() is called before eth_type_trans(), so 50117de44c2SKurt Kanzenbach * skb MAC Hdr properties are not configured yet. Hence need to 50217de44c2SKurt Kanzenbach * reset skb MAC header here 50317de44c2SKurt Kanzenbach */ 50417de44c2SKurt Kanzenbach skb_reset_mac_header(skb); 5053bfd41b5SGrygorii Strashko ret = cpts_skb_get_mtype_seqid(skb, &skb_cb->skb_mtype_seqid); 5063bfd41b5SGrygorii Strashko if (!ret) 5073bfd41b5SGrygorii Strashko return; 5083bfd41b5SGrygorii Strashko 5093bfd41b5SGrygorii Strashko skb_cb->skb_mtype_seqid |= (CPTS_EV_RX << EVENT_TYPE_SHIFT); 5103bfd41b5SGrygorii Strashko 5113bfd41b5SGrygorii Strashko dev_dbg(cpts->dev, "%s mtype seqid %08x\n", 5123bfd41b5SGrygorii Strashko __func__, skb_cb->skb_mtype_seqid); 5133bfd41b5SGrygorii Strashko 5143bfd41b5SGrygorii Strashko ns = cpts_find_ts(cpts, skb, CPTS_EV_RX, skb_cb->skb_mtype_seqid); 51587c0e764SRichard Cochran if (!ns) 51687c0e764SRichard Cochran return; 51787c0e764SRichard Cochran ssh = skb_hwtstamps(skb); 51887c0e764SRichard Cochran memset(ssh, 0, sizeof(*ssh)); 51987c0e764SRichard Cochran ssh->hwtstamp = ns_to_ktime(ns); 52087c0e764SRichard Cochran } 521c8395d4eSGrygorii Strashko EXPORT_SYMBOL_GPL(cpts_rx_timestamp); 52287c0e764SRichard Cochran 52387c0e764SRichard Cochran void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb) 52487c0e764SRichard Cochran { 5253bfd41b5SGrygorii Strashko struct cpts_skb_cb_data *skb_cb = (struct cpts_skb_cb_data *)skb->cb; 5263bfd41b5SGrygorii Strashko int ret; 52787c0e764SRichard Cochran 52887c0e764SRichard Cochran if (!(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) 52987c0e764SRichard Cochran return; 5303bfd41b5SGrygorii Strashko 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_TX << 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 540c8f8e47eSGrygorii Strashko /* Always defer TX TS processing to PTP worker */ 541c8f8e47eSGrygorii Strashko skb_get(skb); 542c8f8e47eSGrygorii Strashko /* get the timestamp for timeouts */ 543c8f8e47eSGrygorii Strashko skb_cb->tmo = jiffies + msecs_to_jiffies(CPTS_SKB_RX_TX_TMO); 544c8f8e47eSGrygorii Strashko skb_queue_tail(&cpts->txq, skb); 545c8f8e47eSGrygorii Strashko ptp_schedule_worker(cpts->clock, 0); 54687c0e764SRichard Cochran } 547c8395d4eSGrygorii Strashko EXPORT_SYMBOL_GPL(cpts_tx_timestamp); 54887c0e764SRichard Cochran 5498a2c9a5aSGrygorii Strashko int cpts_register(struct cpts *cpts) 55087c0e764SRichard Cochran { 55187c0e764SRichard Cochran int err, i; 55287c0e764SRichard Cochran 5530d5f54feSGrygorii Strashko skb_queue_head_init(&cpts->txq); 55487c0e764SRichard Cochran INIT_LIST_HEAD(&cpts->events); 55587c0e764SRichard Cochran INIT_LIST_HEAD(&cpts->pool); 55687c0e764SRichard Cochran for (i = 0; i < CPTS_MAX_EVENTS; i++) 55787c0e764SRichard Cochran list_add(&cpts->pool_data[i].list, &cpts->pool); 55887c0e764SRichard Cochran 5596babfc6eSJiasheng Jiang err = clk_enable(cpts->refclk); 5606babfc6eSJiasheng Jiang if (err) 5616babfc6eSJiasheng Jiang return err; 5628a2c9a5aSGrygorii Strashko 56387c0e764SRichard Cochran cpts_write32(cpts, CPTS_EN, control); 56487c0e764SRichard Cochran cpts_write32(cpts, TS_PEND_EN, int_enable); 56587c0e764SRichard Cochran 566693bd8b7SIvan Khoronzhuk timecounter_init(&cpts->tc, &cpts->cc, ktime_get_real_ns()); 56787c0e764SRichard Cochran 5688a2c9a5aSGrygorii Strashko cpts->clock = ptp_clock_register(&cpts->info, cpts->dev); 5696c691405SGrygorii Strashko if (IS_ERR(cpts->clock)) { 5706c691405SGrygorii Strashko err = PTR_ERR(cpts->clock); 5716c691405SGrygorii Strashko cpts->clock = NULL; 5726c691405SGrygorii Strashko goto err_ptp; 5736c691405SGrygorii Strashko } 5746c691405SGrygorii Strashko cpts->phc_index = ptp_clock_index(cpts->clock); 5756c691405SGrygorii Strashko 576999f1292SGrygorii Strashko ptp_schedule_worker(cpts->clock, cpts->ov_check_period); 57787c0e764SRichard Cochran return 0; 5786c691405SGrygorii Strashko 5796c691405SGrygorii Strashko err_ptp: 5808a2c9a5aSGrygorii Strashko clk_disable(cpts->refclk); 5816c691405SGrygorii Strashko return err; 58287c0e764SRichard Cochran } 583c8395d4eSGrygorii Strashko EXPORT_SYMBOL_GPL(cpts_register); 58487c0e764SRichard Cochran 58587c0e764SRichard Cochran void cpts_unregister(struct cpts *cpts) 58687c0e764SRichard Cochran { 5878a2c9a5aSGrygorii Strashko if (WARN_ON(!cpts->clock)) 5888a2c9a5aSGrygorii Strashko return; 5898a2c9a5aSGrygorii Strashko 5908a2c9a5aSGrygorii Strashko ptp_clock_unregister(cpts->clock); 5918a2c9a5aSGrygorii Strashko cpts->clock = NULL; 5924614792eSGrygorii Strashko cpts->phc_index = -1; 5938fcd6891SGrygorii Strashko 5948fcd6891SGrygorii Strashko cpts_write32(cpts, 0, int_enable); 5958fcd6891SGrygorii Strashko cpts_write32(cpts, 0, control); 5968fcd6891SGrygorii Strashko 5970d5f54feSGrygorii Strashko /* Drop all packet */ 5980d5f54feSGrygorii Strashko skb_queue_purge(&cpts->txq); 5990d5f54feSGrygorii Strashko 6008a2c9a5aSGrygorii Strashko clk_disable(cpts->refclk); 60187c0e764SRichard Cochran } 602c8395d4eSGrygorii Strashko EXPORT_SYMBOL_GPL(cpts_unregister); 603c8395d4eSGrygorii Strashko 60488f0f0b0SGrygorii Strashko static void cpts_calc_mult_shift(struct cpts *cpts) 60588f0f0b0SGrygorii Strashko { 60688f0f0b0SGrygorii Strashko u64 frac, maxsec, ns; 60788f0f0b0SGrygorii Strashko u32 freq; 60888f0f0b0SGrygorii Strashko 60988f0f0b0SGrygorii Strashko freq = clk_get_rate(cpts->refclk); 61088f0f0b0SGrygorii Strashko 61188f0f0b0SGrygorii Strashko /* Calc the maximum number of seconds which we can run before 61288f0f0b0SGrygorii Strashko * wrapping around. 61388f0f0b0SGrygorii Strashko */ 61488f0f0b0SGrygorii Strashko maxsec = cpts->cc.mask; 61588f0f0b0SGrygorii Strashko do_div(maxsec, freq); 61688f0f0b0SGrygorii Strashko /* limit conversation rate to 10 sec as higher values will produce 61788f0f0b0SGrygorii Strashko * too small mult factors and so reduce the conversion accuracy 61888f0f0b0SGrygorii Strashko */ 61988f0f0b0SGrygorii Strashko if (maxsec > 10) 62088f0f0b0SGrygorii Strashko maxsec = 10; 62188f0f0b0SGrygorii Strashko 62220138cf9SGrygorii Strashko /* Calc overflow check period (maxsec / 2) */ 62320138cf9SGrygorii Strashko cpts->ov_check_period = (HZ * maxsec) / 2; 62420138cf9SGrygorii Strashko dev_info(cpts->dev, "cpts: overflow check period %lu (jiffies)\n", 62520138cf9SGrygorii Strashko cpts->ov_check_period); 62620138cf9SGrygorii Strashko 62788f0f0b0SGrygorii Strashko if (cpts->cc.mult || cpts->cc.shift) 62888f0f0b0SGrygorii Strashko return; 62988f0f0b0SGrygorii Strashko 63088f0f0b0SGrygorii Strashko clocks_calc_mult_shift(&cpts->cc.mult, &cpts->cc.shift, 63188f0f0b0SGrygorii Strashko freq, NSEC_PER_SEC, maxsec); 63288f0f0b0SGrygorii Strashko 63388f0f0b0SGrygorii Strashko frac = 0; 63488f0f0b0SGrygorii Strashko ns = cyclecounter_cyc2ns(&cpts->cc, freq, cpts->cc.mask, &frac); 63588f0f0b0SGrygorii Strashko 63688f0f0b0SGrygorii Strashko dev_info(cpts->dev, 63788f0f0b0SGrygorii Strashko "CPTS: ref_clk_freq:%u calc_mult:%u calc_shift:%u error:%lld nsec/sec\n", 63888f0f0b0SGrygorii Strashko freq, cpts->cc.mult, cpts->cc.shift, (ns - NSEC_PER_SEC)); 63988f0f0b0SGrygorii Strashko } 64088f0f0b0SGrygorii Strashko 641a3047a81SGrygorii Strashko static int cpts_of_mux_clk_setup(struct cpts *cpts, struct device_node *node) 642a3047a81SGrygorii Strashko { 643a3047a81SGrygorii Strashko struct device_node *refclk_np; 644a3047a81SGrygorii Strashko const char **parent_names; 645a3047a81SGrygorii Strashko unsigned int num_parents; 646a3047a81SGrygorii Strashko struct clk_hw *clk_hw; 647a3047a81SGrygorii Strashko int ret = -EINVAL; 648a3047a81SGrygorii Strashko u32 *mux_table; 649a3047a81SGrygorii Strashko 650a3047a81SGrygorii Strashko refclk_np = of_get_child_by_name(node, "cpts-refclk-mux"); 651a3047a81SGrygorii Strashko if (!refclk_np) 652a3047a81SGrygorii Strashko /* refclk selection supported not for all SoCs */ 653a3047a81SGrygorii Strashko return 0; 654a3047a81SGrygorii Strashko 655a3047a81SGrygorii Strashko num_parents = of_clk_get_parent_count(refclk_np); 656a3047a81SGrygorii Strashko if (num_parents < 1) { 657a3047a81SGrygorii Strashko dev_err(cpts->dev, "mux-clock %s must have parents\n", 658a3047a81SGrygorii Strashko refclk_np->name); 659a3047a81SGrygorii Strashko goto mux_fail; 660a3047a81SGrygorii Strashko } 661a3047a81SGrygorii Strashko 662c514fbb6SGustavo A. R. Silva parent_names = devm_kcalloc(cpts->dev, num_parents, 663c514fbb6SGustavo A. R. Silva sizeof(*parent_names), GFP_KERNEL); 664a3047a81SGrygorii Strashko 665c514fbb6SGustavo A. R. Silva mux_table = devm_kcalloc(cpts->dev, num_parents, sizeof(*mux_table), 666a3047a81SGrygorii Strashko GFP_KERNEL); 667a3047a81SGrygorii Strashko if (!mux_table || !parent_names) { 668a3047a81SGrygorii Strashko ret = -ENOMEM; 669a3047a81SGrygorii Strashko goto mux_fail; 670a3047a81SGrygorii Strashko } 671a3047a81SGrygorii Strashko 672a3047a81SGrygorii Strashko of_clk_parent_fill(refclk_np, parent_names, num_parents); 673a3047a81SGrygorii Strashko 674a3047a81SGrygorii Strashko ret = of_property_read_variable_u32_array(refclk_np, "ti,mux-tbl", 675a3047a81SGrygorii Strashko mux_table, 676a3047a81SGrygorii Strashko num_parents, num_parents); 677a3047a81SGrygorii Strashko if (ret < 0) 678a3047a81SGrygorii Strashko goto mux_fail; 679a3047a81SGrygorii Strashko 680a3047a81SGrygorii Strashko clk_hw = clk_hw_register_mux_table(cpts->dev, refclk_np->name, 681a3047a81SGrygorii Strashko parent_names, num_parents, 682a3047a81SGrygorii Strashko 0, 683a3047a81SGrygorii Strashko &cpts->reg->rftclk_sel, 0, 0x1F, 684a3047a81SGrygorii Strashko 0, mux_table, NULL); 685a3047a81SGrygorii Strashko if (IS_ERR(clk_hw)) { 686a3047a81SGrygorii Strashko ret = PTR_ERR(clk_hw); 687a3047a81SGrygorii Strashko goto mux_fail; 688a3047a81SGrygorii Strashko } 689a3047a81SGrygorii Strashko 690a3047a81SGrygorii Strashko ret = devm_add_action_or_reset(cpts->dev, 691a3047a81SGrygorii Strashko (void(*)(void *))clk_hw_unregister_mux, 692a3047a81SGrygorii Strashko clk_hw); 693a3047a81SGrygorii Strashko if (ret) { 694a3047a81SGrygorii Strashko dev_err(cpts->dev, "add clkmux unreg action %d", ret); 695a3047a81SGrygorii Strashko goto mux_fail; 696a3047a81SGrygorii Strashko } 697a3047a81SGrygorii Strashko 698a3047a81SGrygorii Strashko ret = of_clk_add_hw_provider(refclk_np, of_clk_hw_simple_get, clk_hw); 699a3047a81SGrygorii Strashko if (ret) 700a3047a81SGrygorii Strashko goto mux_fail; 701a3047a81SGrygorii Strashko 702a3047a81SGrygorii Strashko ret = devm_add_action_or_reset(cpts->dev, 703a3047a81SGrygorii Strashko (void(*)(void *))of_clk_del_provider, 704a3047a81SGrygorii Strashko refclk_np); 705a3047a81SGrygorii Strashko if (ret) { 706a3047a81SGrygorii Strashko dev_err(cpts->dev, "add clkmux provider unreg action %d", ret); 707a3047a81SGrygorii Strashko goto mux_fail; 708a3047a81SGrygorii Strashko } 709a3047a81SGrygorii Strashko 710a3047a81SGrygorii Strashko return ret; 711a3047a81SGrygorii Strashko 712a3047a81SGrygorii Strashko mux_fail: 713a3047a81SGrygorii Strashko of_node_put(refclk_np); 714a3047a81SGrygorii Strashko return ret; 715a3047a81SGrygorii Strashko } 716a3047a81SGrygorii Strashko 7174a88fb95SGrygorii Strashko static int cpts_of_parse(struct cpts *cpts, struct device_node *node) 7184a88fb95SGrygorii Strashko { 7194a88fb95SGrygorii Strashko int ret = -EINVAL; 7204a88fb95SGrygorii Strashko u32 prop; 7214a88fb95SGrygorii Strashko 72288f0f0b0SGrygorii Strashko if (!of_property_read_u32(node, "cpts_clock_mult", &prop)) 7234a88fb95SGrygorii Strashko cpts->cc.mult = prop; 7244a88fb95SGrygorii Strashko 72588f0f0b0SGrygorii Strashko if (!of_property_read_u32(node, "cpts_clock_shift", &prop)) 7264a88fb95SGrygorii Strashko cpts->cc.shift = prop; 7274a88fb95SGrygorii Strashko 72888f0f0b0SGrygorii Strashko if ((cpts->cc.mult && !cpts->cc.shift) || 72988f0f0b0SGrygorii Strashko (!cpts->cc.mult && cpts->cc.shift)) 73088f0f0b0SGrygorii Strashko goto of_error; 73188f0f0b0SGrygorii Strashko 732a3047a81SGrygorii Strashko return cpts_of_mux_clk_setup(cpts, node); 7334a88fb95SGrygorii Strashko 7344a88fb95SGrygorii Strashko of_error: 7354a88fb95SGrygorii Strashko dev_err(cpts->dev, "CPTS: Missing property in the DT.\n"); 7364a88fb95SGrygorii Strashko return ret; 7374a88fb95SGrygorii Strashko } 7384a88fb95SGrygorii Strashko 7398a2c9a5aSGrygorii Strashko struct cpts *cpts_create(struct device *dev, void __iomem *regs, 740b78aba49SGrygorii Strashko struct device_node *node, u32 n_ext_ts) 7418a2c9a5aSGrygorii Strashko { 7428a2c9a5aSGrygorii Strashko struct cpts *cpts; 7434a88fb95SGrygorii Strashko int ret; 7448a2c9a5aSGrygorii Strashko 7458a2c9a5aSGrygorii Strashko cpts = devm_kzalloc(dev, sizeof(*cpts), GFP_KERNEL); 7468a2c9a5aSGrygorii Strashko if (!cpts) 7478a2c9a5aSGrygorii Strashko return ERR_PTR(-ENOMEM); 7488a2c9a5aSGrygorii Strashko 7498a2c9a5aSGrygorii Strashko cpts->dev = dev; 7508a2c9a5aSGrygorii Strashko cpts->reg = (struct cpsw_cpts __iomem *)regs; 75185624412SGrygorii Strashko cpts->irq_poll = true; 7528a2c9a5aSGrygorii Strashko spin_lock_init(&cpts->lock); 753ba107428SGrygorii Strashko mutex_init(&cpts->ptp_clk_mutex); 75485624412SGrygorii Strashko init_completion(&cpts->ts_push_complete); 7558a2c9a5aSGrygorii Strashko 7564a88fb95SGrygorii Strashko ret = cpts_of_parse(cpts, node); 7574a88fb95SGrygorii Strashko if (ret) 7584a88fb95SGrygorii Strashko return ERR_PTR(ret); 7594a88fb95SGrygorii Strashko 7608a6389a5SGrygorii Strashko cpts->refclk = devm_get_clk_from_child(dev, node, "cpts"); 7618a6389a5SGrygorii Strashko if (IS_ERR(cpts->refclk)) 7628a6389a5SGrygorii Strashko /* try get clk from dev node for compatibility */ 7638a2c9a5aSGrygorii Strashko cpts->refclk = devm_clk_get(dev, "cpts"); 7648a6389a5SGrygorii Strashko 7658a2c9a5aSGrygorii Strashko if (IS_ERR(cpts->refclk)) { 7668a6389a5SGrygorii Strashko dev_err(dev, "Failed to get cpts refclk %ld\n", 7678a6389a5SGrygorii Strashko PTR_ERR(cpts->refclk)); 768bde4c563SHernán Gonzalez return ERR_CAST(cpts->refclk); 7698a2c9a5aSGrygorii Strashko } 7708a2c9a5aSGrygorii Strashko 7712d822f2dSKangjie Lu ret = clk_prepare(cpts->refclk); 7722d822f2dSKangjie Lu if (ret) 7732d822f2dSKangjie Lu return ERR_PTR(ret); 7748a2c9a5aSGrygorii Strashko 7758a2c9a5aSGrygorii Strashko cpts->cc.read = cpts_systim_read; 7768a2c9a5aSGrygorii Strashko cpts->cc.mask = CLOCKSOURCE_MASK(32); 77788f0f0b0SGrygorii Strashko cpts->info = cpts_info; 7784614792eSGrygorii Strashko cpts->phc_index = -1; 77988f0f0b0SGrygorii Strashko 780b78aba49SGrygorii Strashko if (n_ext_ts) 781b78aba49SGrygorii Strashko cpts->info.n_ext_ts = n_ext_ts; 782b78aba49SGrygorii Strashko 78388f0f0b0SGrygorii Strashko cpts_calc_mult_shift(cpts); 7844a88fb95SGrygorii Strashko /* save cc.mult original value as it can be modified 785*a4539207SJacob Keller * by cpts_ptp_adjfine(). 7864a88fb95SGrygorii Strashko */ 7874a88fb95SGrygorii Strashko cpts->cc_mult = cpts->cc.mult; 7888a2c9a5aSGrygorii Strashko 7898a2c9a5aSGrygorii Strashko return cpts; 7908a2c9a5aSGrygorii Strashko } 7918a2c9a5aSGrygorii Strashko EXPORT_SYMBOL_GPL(cpts_create); 7928a2c9a5aSGrygorii Strashko 7938a2c9a5aSGrygorii Strashko void cpts_release(struct cpts *cpts) 7948a2c9a5aSGrygorii Strashko { 7958a2c9a5aSGrygorii Strashko if (!cpts) 7968a2c9a5aSGrygorii Strashko return; 7978a2c9a5aSGrygorii Strashko 7988a2c9a5aSGrygorii Strashko if (WARN_ON(!cpts->refclk)) 7998a2c9a5aSGrygorii Strashko return; 8008a2c9a5aSGrygorii Strashko 8018a2c9a5aSGrygorii Strashko clk_unprepare(cpts->refclk); 8028a2c9a5aSGrygorii Strashko } 8038a2c9a5aSGrygorii Strashko EXPORT_SYMBOL_GPL(cpts_release); 8048a2c9a5aSGrygorii Strashko 805c8395d4eSGrygorii Strashko MODULE_LICENSE("GPL v2"); 806c8395d4eSGrygorii Strashko MODULE_DESCRIPTION("TI CPTS driver"); 807c8395d4eSGrygorii Strashko MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>"); 808