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 3587c0e764SRichard Cochran static int event_expired(struct cpts_event *event) 3687c0e764SRichard Cochran { 3787c0e764SRichard Cochran return time_after(jiffies, event->tmo); 3887c0e764SRichard Cochran } 3987c0e764SRichard Cochran 4087c0e764SRichard Cochran static int event_type(struct cpts_event *event) 4187c0e764SRichard Cochran { 4287c0e764SRichard Cochran return (event->high >> EVENT_TYPE_SHIFT) & EVENT_TYPE_MASK; 4387c0e764SRichard Cochran } 4487c0e764SRichard Cochran 4587c0e764SRichard Cochran static int cpts_fifo_pop(struct cpts *cpts, u32 *high, u32 *low) 4687c0e764SRichard Cochran { 4787c0e764SRichard Cochran u32 r = cpts_read32(cpts, intstat_raw); 4887c0e764SRichard Cochran 4987c0e764SRichard Cochran if (r & TS_PEND_RAW) { 5087c0e764SRichard Cochran *high = cpts_read32(cpts, event_high); 5187c0e764SRichard Cochran *low = cpts_read32(cpts, event_low); 5287c0e764SRichard Cochran cpts_write32(cpts, EVENT_POP, event_pop); 5387c0e764SRichard Cochran return 0; 5487c0e764SRichard Cochran } 5587c0e764SRichard Cochran return -1; 5687c0e764SRichard Cochran } 5787c0e764SRichard Cochran 58e4439fa8SWingMan Kwok static int cpts_purge_events(struct cpts *cpts) 59e4439fa8SWingMan Kwok { 60e4439fa8SWingMan Kwok struct list_head *this, *next; 61e4439fa8SWingMan Kwok struct cpts_event *event; 62e4439fa8SWingMan Kwok int removed = 0; 63e4439fa8SWingMan Kwok 64e4439fa8SWingMan Kwok list_for_each_safe(this, next, &cpts->events) { 65e4439fa8SWingMan Kwok event = list_entry(this, struct cpts_event, list); 66e4439fa8SWingMan Kwok if (event_expired(event)) { 67e4439fa8SWingMan Kwok list_del_init(&event->list); 68e4439fa8SWingMan Kwok list_add(&event->list, &cpts->pool); 69e4439fa8SWingMan Kwok ++removed; 70e4439fa8SWingMan Kwok } 71e4439fa8SWingMan Kwok } 72e4439fa8SWingMan Kwok 73e4439fa8SWingMan Kwok if (removed) 7479d6e755SGrygorii Strashko dev_dbg(cpts->dev, "cpts: event pool cleaned up %d\n", removed); 75e4439fa8SWingMan Kwok return removed ? 0 : -1; 76e4439fa8SWingMan Kwok } 77e4439fa8SWingMan Kwok 78f19dcd5fSIvan Khoronzhuk static void cpts_purge_txq(struct cpts *cpts) 79f19dcd5fSIvan Khoronzhuk { 80f19dcd5fSIvan Khoronzhuk struct cpts_skb_cb_data *skb_cb; 81f19dcd5fSIvan Khoronzhuk struct sk_buff *skb, *tmp; 82f19dcd5fSIvan Khoronzhuk int removed = 0; 83f19dcd5fSIvan Khoronzhuk 84f19dcd5fSIvan Khoronzhuk skb_queue_walk_safe(&cpts->txq, skb, tmp) { 85f19dcd5fSIvan Khoronzhuk skb_cb = (struct cpts_skb_cb_data *)skb->cb; 86f19dcd5fSIvan Khoronzhuk if (time_after(jiffies, skb_cb->tmo)) { 87f19dcd5fSIvan Khoronzhuk __skb_unlink(skb, &cpts->txq); 88f19dcd5fSIvan Khoronzhuk dev_consume_skb_any(skb); 89f19dcd5fSIvan Khoronzhuk ++removed; 90f19dcd5fSIvan Khoronzhuk } 91f19dcd5fSIvan Khoronzhuk } 92f19dcd5fSIvan Khoronzhuk 93f19dcd5fSIvan Khoronzhuk if (removed) 94f19dcd5fSIvan Khoronzhuk dev_dbg(cpts->dev, "txq cleaned up %d\n", removed); 95f19dcd5fSIvan Khoronzhuk } 96f19dcd5fSIvan Khoronzhuk 9787c0e764SRichard Cochran /* 9887c0e764SRichard Cochran * Returns zero if matching event type was found. 9987c0e764SRichard Cochran */ 10087c0e764SRichard Cochran static int cpts_fifo_read(struct cpts *cpts, int match) 10187c0e764SRichard Cochran { 102*ba107428SGrygorii Strashko struct cpts_event *event; 103*ba107428SGrygorii Strashko unsigned long flags; 10487c0e764SRichard Cochran int i, type = -1; 10587c0e764SRichard Cochran u32 hi, lo; 106*ba107428SGrygorii Strashko 107*ba107428SGrygorii Strashko spin_lock_irqsave(&cpts->lock, flags); 10887c0e764SRichard Cochran 10987c0e764SRichard Cochran for (i = 0; i < CPTS_FIFO_DEPTH; i++) { 11087c0e764SRichard Cochran if (cpts_fifo_pop(cpts, &hi, &lo)) 11187c0e764SRichard Cochran break; 112e4439fa8SWingMan Kwok 113e4439fa8SWingMan Kwok if (list_empty(&cpts->pool) && cpts_purge_events(cpts)) { 11479d6e755SGrygorii Strashko dev_warn(cpts->dev, "cpts: event pool empty\n"); 115*ba107428SGrygorii Strashko break; 11687c0e764SRichard Cochran } 117e4439fa8SWingMan Kwok 11887c0e764SRichard Cochran event = list_first_entry(&cpts->pool, struct cpts_event, list); 11987c0e764SRichard Cochran event->high = hi; 12087c0e764SRichard Cochran event->low = lo; 121e66dccceSGrygorii Strashko event->timestamp = timecounter_cyc2time(&cpts->tc, event->low); 12287c0e764SRichard Cochran type = event_type(event); 123e66dccceSGrygorii Strashko 124e66dccceSGrygorii Strashko dev_dbg(cpts->dev, "CPTS_EV: %d high:%08X low:%08x\n", 125e66dccceSGrygorii Strashko type, event->high, event->low); 12687c0e764SRichard Cochran switch (type) { 127e66dccceSGrygorii Strashko case CPTS_EV_PUSH: 128e66dccceSGrygorii Strashko WRITE_ONCE(cpts->cur_timestamp, lo); 129e66dccceSGrygorii Strashko timecounter_read(&cpts->tc); 1300d6df3e6SGrygorii Strashko if (cpts->mult_new) { 1310d6df3e6SGrygorii Strashko cpts->cc.mult = cpts->mult_new; 1320d6df3e6SGrygorii Strashko cpts->mult_new = 0; 1330d6df3e6SGrygorii Strashko } 134e66dccceSGrygorii Strashko break; 1350d5f54feSGrygorii Strashko case CPTS_EV_TX: 13687c0e764SRichard Cochran case CPTS_EV_RX: 137c8f8e47eSGrygorii Strashko event->tmo = jiffies + 138c8f8e47eSGrygorii Strashko msecs_to_jiffies(CPTS_EVENT_RX_TX_TIMEOUT); 139c8f8e47eSGrygorii Strashko 14087c0e764SRichard Cochran list_del_init(&event->list); 14187c0e764SRichard Cochran list_add_tail(&event->list, &cpts->events); 14287c0e764SRichard Cochran break; 14387c0e764SRichard Cochran case CPTS_EV_ROLL: 14487c0e764SRichard Cochran case CPTS_EV_HALF: 14587c0e764SRichard Cochran case CPTS_EV_HW: 14687c0e764SRichard Cochran break; 14787c0e764SRichard Cochran default: 14879d6e755SGrygorii Strashko dev_err(cpts->dev, "cpts: unknown event type\n"); 14987c0e764SRichard Cochran break; 15087c0e764SRichard Cochran } 15187c0e764SRichard Cochran if (type == match) 15287c0e764SRichard Cochran break; 15387c0e764SRichard Cochran } 154*ba107428SGrygorii Strashko 155*ba107428SGrygorii Strashko spin_unlock_irqrestore(&cpts->lock, flags); 156*ba107428SGrygorii Strashko 15787c0e764SRichard Cochran return type == match ? 0 : -1; 15887c0e764SRichard Cochran } 15987c0e764SRichard Cochran 160a5a1d1c2SThomas Gleixner static u64 cpts_systim_read(const struct cyclecounter *cc) 16187c0e764SRichard Cochran { 16287c0e764SRichard Cochran struct cpts *cpts = container_of(cc, struct cpts, cc); 16387c0e764SRichard Cochran 164e66dccceSGrygorii Strashko return READ_ONCE(cpts->cur_timestamp); 165e66dccceSGrygorii Strashko } 166e66dccceSGrygorii Strashko 167856e59abSGrygorii Strashko static void cpts_update_cur_time(struct cpts *cpts, int match, 168856e59abSGrygorii Strashko struct ptp_system_timestamp *sts) 169e66dccceSGrygorii Strashko { 170*ba107428SGrygorii Strashko unsigned long flags; 171*ba107428SGrygorii Strashko 172*ba107428SGrygorii Strashko /* use spin_lock_irqsave() here as it has to run very fast */ 173*ba107428SGrygorii Strashko spin_lock_irqsave(&cpts->lock, flags); 174856e59abSGrygorii Strashko ptp_read_system_prets(sts); 17587c0e764SRichard Cochran cpts_write32(cpts, TS_PUSH, ts_push); 176856e59abSGrygorii Strashko cpts_read32(cpts, ts_push); 177856e59abSGrygorii Strashko ptp_read_system_postts(sts); 178*ba107428SGrygorii Strashko spin_unlock_irqrestore(&cpts->lock, flags); 179e66dccceSGrygorii Strashko 180e66dccceSGrygorii Strashko if (cpts_fifo_read(cpts, match) && match != -1) 18179d6e755SGrygorii Strashko dev_err(cpts->dev, "cpts: unable to obtain a time stamp\n"); 18287c0e764SRichard Cochran } 18387c0e764SRichard Cochran 18487c0e764SRichard Cochran /* PTP clock operations */ 18587c0e764SRichard Cochran 18687c0e764SRichard Cochran static int cpts_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) 18787c0e764SRichard Cochran { 18887c0e764SRichard Cochran struct cpts *cpts = container_of(ptp, struct cpts, info); 189*ba107428SGrygorii Strashko int neg_adj = 0; 190*ba107428SGrygorii Strashko u32 diff, mult; 191*ba107428SGrygorii Strashko u64 adj; 19287c0e764SRichard Cochran 19387c0e764SRichard Cochran if (ppb < 0) { 19487c0e764SRichard Cochran neg_adj = 1; 19587c0e764SRichard Cochran ppb = -ppb; 19687c0e764SRichard Cochran } 19787c0e764SRichard Cochran mult = cpts->cc_mult; 19887c0e764SRichard Cochran adj = mult; 19987c0e764SRichard Cochran adj *= ppb; 20087c0e764SRichard Cochran diff = div_u64(adj, 1000000000ULL); 20187c0e764SRichard Cochran 202*ba107428SGrygorii Strashko mutex_lock(&cpts->ptp_clk_mutex); 20387c0e764SRichard Cochran 2040d6df3e6SGrygorii Strashko cpts->mult_new = neg_adj ? mult - diff : mult + diff; 20587c0e764SRichard Cochran 206856e59abSGrygorii Strashko cpts_update_cur_time(cpts, CPTS_EV_PUSH, NULL); 20787c0e764SRichard Cochran 208*ba107428SGrygorii Strashko mutex_unlock(&cpts->ptp_clk_mutex); 20987c0e764SRichard Cochran return 0; 21087c0e764SRichard Cochran } 21187c0e764SRichard Cochran 21287c0e764SRichard Cochran static int cpts_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) 21387c0e764SRichard Cochran { 21487c0e764SRichard Cochran struct cpts *cpts = container_of(ptp, struct cpts, info); 21587c0e764SRichard Cochran 216*ba107428SGrygorii Strashko mutex_lock(&cpts->ptp_clk_mutex); 217f25a30beSRichard Cochran timecounter_adjtime(&cpts->tc, delta); 218*ba107428SGrygorii Strashko mutex_unlock(&cpts->ptp_clk_mutex); 21987c0e764SRichard Cochran 22087c0e764SRichard Cochran return 0; 22187c0e764SRichard Cochran } 22287c0e764SRichard Cochran 223856e59abSGrygorii Strashko static int cpts_ptp_gettimeex(struct ptp_clock_info *ptp, 224856e59abSGrygorii Strashko struct timespec64 *ts, 225856e59abSGrygorii Strashko struct ptp_system_timestamp *sts) 22687c0e764SRichard Cochran { 22787c0e764SRichard Cochran struct cpts *cpts = container_of(ptp, struct cpts, info); 228856e59abSGrygorii Strashko u64 ns; 22987c0e764SRichard Cochran 230*ba107428SGrygorii Strashko mutex_lock(&cpts->ptp_clk_mutex); 231e66dccceSGrygorii Strashko 232856e59abSGrygorii Strashko cpts_update_cur_time(cpts, CPTS_EV_PUSH, sts); 233e66dccceSGrygorii Strashko 23487c0e764SRichard Cochran ns = timecounter_read(&cpts->tc); 235*ba107428SGrygorii Strashko mutex_unlock(&cpts->ptp_clk_mutex); 23687c0e764SRichard Cochran 23784d923ceSRichard Cochran *ts = ns_to_timespec64(ns); 23887c0e764SRichard Cochran 23987c0e764SRichard Cochran return 0; 24087c0e764SRichard Cochran } 24187c0e764SRichard Cochran 24287c0e764SRichard Cochran static int cpts_ptp_settime(struct ptp_clock_info *ptp, 243a5c79c26SRichard Cochran const struct timespec64 *ts) 24487c0e764SRichard Cochran { 24587c0e764SRichard Cochran struct cpts *cpts = container_of(ptp, struct cpts, info); 246*ba107428SGrygorii Strashko u64 ns; 24787c0e764SRichard Cochran 24884d923ceSRichard Cochran ns = timespec64_to_ns(ts); 24987c0e764SRichard Cochran 250*ba107428SGrygorii Strashko mutex_lock(&cpts->ptp_clk_mutex); 25187c0e764SRichard Cochran timecounter_init(&cpts->tc, &cpts->cc, ns); 252*ba107428SGrygorii Strashko mutex_unlock(&cpts->ptp_clk_mutex); 25387c0e764SRichard Cochran 25487c0e764SRichard Cochran return 0; 25587c0e764SRichard Cochran } 25687c0e764SRichard Cochran 25787c0e764SRichard Cochran static int cpts_ptp_enable(struct ptp_clock_info *ptp, 25887c0e764SRichard Cochran struct ptp_clock_request *rq, int on) 25987c0e764SRichard Cochran { 26087c0e764SRichard Cochran return -EOPNOTSUPP; 26187c0e764SRichard Cochran } 26287c0e764SRichard Cochran 263c8f8e47eSGrygorii Strashko static bool cpts_match_tx_ts(struct cpts *cpts, struct cpts_event *event) 264c8f8e47eSGrygorii Strashko { 265c8f8e47eSGrygorii Strashko struct sk_buff_head txq_list; 266c8f8e47eSGrygorii Strashko struct sk_buff *skb, *tmp; 267c8f8e47eSGrygorii Strashko unsigned long flags; 268c8f8e47eSGrygorii Strashko bool found = false; 269c8f8e47eSGrygorii Strashko u32 mtype_seqid; 270c8f8e47eSGrygorii Strashko 271c8f8e47eSGrygorii Strashko mtype_seqid = event->high & 272c8f8e47eSGrygorii Strashko ((MESSAGE_TYPE_MASK << MESSAGE_TYPE_SHIFT) | 273c8f8e47eSGrygorii Strashko (SEQUENCE_ID_MASK << SEQUENCE_ID_SHIFT) | 274c8f8e47eSGrygorii Strashko (EVENT_TYPE_MASK << EVENT_TYPE_SHIFT)); 275c8f8e47eSGrygorii Strashko 276c8f8e47eSGrygorii Strashko __skb_queue_head_init(&txq_list); 277c8f8e47eSGrygorii Strashko 278c8f8e47eSGrygorii Strashko spin_lock_irqsave(&cpts->txq.lock, flags); 279c8f8e47eSGrygorii Strashko skb_queue_splice_init(&cpts->txq, &txq_list); 280c8f8e47eSGrygorii Strashko spin_unlock_irqrestore(&cpts->txq.lock, flags); 281c8f8e47eSGrygorii Strashko 282c8f8e47eSGrygorii Strashko skb_queue_walk_safe(&txq_list, skb, tmp) { 283c8f8e47eSGrygorii Strashko struct skb_shared_hwtstamps ssh; 284c8f8e47eSGrygorii Strashko struct cpts_skb_cb_data *skb_cb = 285c8f8e47eSGrygorii Strashko (struct cpts_skb_cb_data *)skb->cb; 286c8f8e47eSGrygorii Strashko 287c8f8e47eSGrygorii Strashko if (mtype_seqid == skb_cb->skb_mtype_seqid) { 288c8f8e47eSGrygorii Strashko memset(&ssh, 0, sizeof(ssh)); 289c8f8e47eSGrygorii Strashko ssh.hwtstamp = ns_to_ktime(event->timestamp); 290c8f8e47eSGrygorii Strashko skb_tstamp_tx(skb, &ssh); 291c8f8e47eSGrygorii Strashko found = true; 292c8f8e47eSGrygorii Strashko __skb_unlink(skb, &txq_list); 293c8f8e47eSGrygorii Strashko dev_consume_skb_any(skb); 294c8f8e47eSGrygorii Strashko dev_dbg(cpts->dev, "match tx timestamp mtype_seqid %08x\n", 295c8f8e47eSGrygorii Strashko mtype_seqid); 296c8f8e47eSGrygorii Strashko break; 297c8f8e47eSGrygorii Strashko } 298c8f8e47eSGrygorii Strashko 299c8f8e47eSGrygorii Strashko if (time_after(jiffies, skb_cb->tmo)) { 300c8f8e47eSGrygorii Strashko /* timeout any expired skbs over 1s */ 301c8f8e47eSGrygorii Strashko dev_dbg(cpts->dev, "expiring tx timestamp from txq\n"); 302c8f8e47eSGrygorii Strashko __skb_unlink(skb, &txq_list); 303c8f8e47eSGrygorii Strashko dev_consume_skb_any(skb); 304c8f8e47eSGrygorii Strashko } 305c8f8e47eSGrygorii Strashko } 306c8f8e47eSGrygorii Strashko 307c8f8e47eSGrygorii Strashko spin_lock_irqsave(&cpts->txq.lock, flags); 308c8f8e47eSGrygorii Strashko skb_queue_splice(&txq_list, &cpts->txq); 309c8f8e47eSGrygorii Strashko spin_unlock_irqrestore(&cpts->txq.lock, flags); 310c8f8e47eSGrygorii Strashko 311c8f8e47eSGrygorii Strashko return found; 312c8f8e47eSGrygorii Strashko } 313c8f8e47eSGrygorii Strashko 314c8f8e47eSGrygorii Strashko static void cpts_process_events(struct cpts *cpts) 315c8f8e47eSGrygorii Strashko { 316c8f8e47eSGrygorii Strashko struct list_head *this, *next; 317c8f8e47eSGrygorii Strashko struct cpts_event *event; 318c8f8e47eSGrygorii Strashko LIST_HEAD(events_free); 319c8f8e47eSGrygorii Strashko unsigned long flags; 320c8f8e47eSGrygorii Strashko LIST_HEAD(events); 321c8f8e47eSGrygorii Strashko 322c8f8e47eSGrygorii Strashko spin_lock_irqsave(&cpts->lock, flags); 323c8f8e47eSGrygorii Strashko list_splice_init(&cpts->events, &events); 324c8f8e47eSGrygorii Strashko spin_unlock_irqrestore(&cpts->lock, flags); 325c8f8e47eSGrygorii Strashko 326c8f8e47eSGrygorii Strashko list_for_each_safe(this, next, &events) { 327c8f8e47eSGrygorii Strashko event = list_entry(this, struct cpts_event, list); 328c8f8e47eSGrygorii Strashko if (cpts_match_tx_ts(cpts, event) || 329c8f8e47eSGrygorii Strashko time_after(jiffies, event->tmo)) { 330c8f8e47eSGrygorii Strashko list_del_init(&event->list); 331c8f8e47eSGrygorii Strashko list_add(&event->list, &events_free); 332c8f8e47eSGrygorii Strashko } 333c8f8e47eSGrygorii Strashko } 334c8f8e47eSGrygorii Strashko 335c8f8e47eSGrygorii Strashko spin_lock_irqsave(&cpts->lock, flags); 336c8f8e47eSGrygorii Strashko list_splice_tail(&events, &cpts->events); 337c8f8e47eSGrygorii Strashko list_splice_tail(&events_free, &cpts->pool); 338c8f8e47eSGrygorii Strashko spin_unlock_irqrestore(&cpts->lock, flags); 339c8f8e47eSGrygorii Strashko } 340c8f8e47eSGrygorii Strashko 341999f1292SGrygorii Strashko static long cpts_overflow_check(struct ptp_clock_info *ptp) 342999f1292SGrygorii Strashko { 343999f1292SGrygorii Strashko struct cpts *cpts = container_of(ptp, struct cpts, info); 344999f1292SGrygorii Strashko unsigned long delay = cpts->ov_check_period; 3450d5f54feSGrygorii Strashko unsigned long flags; 346e66dccceSGrygorii Strashko u64 ns; 347999f1292SGrygorii Strashko 348*ba107428SGrygorii Strashko mutex_lock(&cpts->ptp_clk_mutex); 349e66dccceSGrygorii Strashko 350*ba107428SGrygorii Strashko cpts_update_cur_time(cpts, -1, NULL); 351e66dccceSGrygorii Strashko ns = timecounter_read(&cpts->tc); 3520d5f54feSGrygorii Strashko 353c8f8e47eSGrygorii Strashko cpts_process_events(cpts); 354c8f8e47eSGrygorii Strashko 355c8f8e47eSGrygorii Strashko spin_lock_irqsave(&cpts->txq.lock, flags); 356f19dcd5fSIvan Khoronzhuk if (!skb_queue_empty(&cpts->txq)) { 357f19dcd5fSIvan Khoronzhuk cpts_purge_txq(cpts); 3580d5f54feSGrygorii Strashko if (!skb_queue_empty(&cpts->txq)) 3590d5f54feSGrygorii Strashko delay = CPTS_SKB_TX_WORK_TIMEOUT; 360f19dcd5fSIvan Khoronzhuk } 361c8f8e47eSGrygorii Strashko spin_unlock_irqrestore(&cpts->txq.lock, flags); 3620d5f54feSGrygorii Strashko 363e66dccceSGrygorii Strashko dev_dbg(cpts->dev, "cpts overflow check at %lld\n", ns); 364*ba107428SGrygorii Strashko mutex_unlock(&cpts->ptp_clk_mutex); 365999f1292SGrygorii Strashko return (long)delay; 366999f1292SGrygorii Strashko } 367999f1292SGrygorii Strashko 368b6d08bd8SBhumika Goyal static const struct ptp_clock_info cpts_info = { 36987c0e764SRichard Cochran .owner = THIS_MODULE, 37087c0e764SRichard Cochran .name = "CTPS timer", 37187c0e764SRichard Cochran .max_adj = 1000000, 37287c0e764SRichard Cochran .n_ext_ts = 0, 3734986b4f0SRichard Cochran .n_pins = 0, 37487c0e764SRichard Cochran .pps = 0, 37587c0e764SRichard Cochran .adjfreq = cpts_ptp_adjfreq, 37687c0e764SRichard Cochran .adjtime = cpts_ptp_adjtime, 377856e59abSGrygorii Strashko .gettimex64 = cpts_ptp_gettimeex, 378a5c79c26SRichard Cochran .settime64 = cpts_ptp_settime, 37987c0e764SRichard Cochran .enable = cpts_ptp_enable, 380999f1292SGrygorii Strashko .do_aux_work = cpts_overflow_check, 38187c0e764SRichard Cochran }; 38287c0e764SRichard Cochran 3833bfd41b5SGrygorii Strashko static int cpts_skb_get_mtype_seqid(struct sk_buff *skb, u32 *mtype_seqid) 38487c0e764SRichard Cochran { 3853bfd41b5SGrygorii Strashko unsigned int ptp_class = ptp_classify_raw(skb); 38687c0e764SRichard Cochran u8 *msgtype, *data = skb->data; 3873bfd41b5SGrygorii Strashko unsigned int offset = 0; 3883bfd41b5SGrygorii Strashko u16 *seqid; 3893bfd41b5SGrygorii Strashko 3903bfd41b5SGrygorii Strashko if (ptp_class == PTP_CLASS_NONE) 3913bfd41b5SGrygorii Strashko return 0; 39287c0e764SRichard Cochran 393ae5c6c6dSStefan Sørensen if (ptp_class & PTP_CLASS_VLAN) 394ae5c6c6dSStefan Sørensen offset += VLAN_HLEN; 395ae5c6c6dSStefan Sørensen 396ae5c6c6dSStefan Sørensen switch (ptp_class & PTP_CLASS_PMASK) { 397ae5c6c6dSStefan Sørensen case PTP_CLASS_IPV4: 398cca04b28SRichard Cochran offset += ETH_HLEN + IPV4_HLEN(data + offset) + UDP_HLEN; 39987c0e764SRichard Cochran break; 400ae5c6c6dSStefan Sørensen case PTP_CLASS_IPV6: 401ae5c6c6dSStefan Sørensen offset += ETH_HLEN + IP6_HLEN + UDP_HLEN; 40287c0e764SRichard Cochran break; 403ae5c6c6dSStefan Sørensen case PTP_CLASS_L2: 404ae5c6c6dSStefan Sørensen offset += ETH_HLEN; 40587c0e764SRichard Cochran break; 40687c0e764SRichard Cochran default: 40787c0e764SRichard Cochran return 0; 40887c0e764SRichard Cochran } 40987c0e764SRichard Cochran 41087c0e764SRichard Cochran if (skb->len + ETH_HLEN < offset + OFF_PTP_SEQUENCE_ID + sizeof(*seqid)) 41187c0e764SRichard Cochran return 0; 41287c0e764SRichard Cochran 41387c0e764SRichard Cochran if (unlikely(ptp_class & PTP_CLASS_V1)) 41487c0e764SRichard Cochran msgtype = data + offset + OFF_PTP_CONTROL; 41587c0e764SRichard Cochran else 41687c0e764SRichard Cochran msgtype = data + offset; 41787c0e764SRichard Cochran 41887c0e764SRichard Cochran seqid = (u16 *)(data + offset + OFF_PTP_SEQUENCE_ID); 4193bfd41b5SGrygorii Strashko *mtype_seqid = (*msgtype & MESSAGE_TYPE_MASK) << MESSAGE_TYPE_SHIFT; 4203bfd41b5SGrygorii Strashko *mtype_seqid |= (ntohs(*seqid) & SEQUENCE_ID_MASK) << SEQUENCE_ID_SHIFT; 42187c0e764SRichard Cochran 4223bfd41b5SGrygorii Strashko return 1; 42387c0e764SRichard Cochran } 42487c0e764SRichard Cochran 4253bfd41b5SGrygorii Strashko static u64 cpts_find_ts(struct cpts *cpts, struct sk_buff *skb, 4263bfd41b5SGrygorii Strashko int ev_type, u32 skb_mtype_seqid) 42787c0e764SRichard Cochran { 42887c0e764SRichard Cochran struct list_head *this, *next; 4293bfd41b5SGrygorii Strashko struct cpts_event *event; 43087c0e764SRichard Cochran unsigned long flags; 4313bfd41b5SGrygorii Strashko u32 mtype_seqid; 4323bfd41b5SGrygorii Strashko u64 ns = 0; 43387c0e764SRichard Cochran 434a93439ccSGrygorii Strashko cpts_fifo_read(cpts, -1); 435*ba107428SGrygorii Strashko spin_lock_irqsave(&cpts->lock, flags); 43687c0e764SRichard Cochran list_for_each_safe(this, next, &cpts->events) { 43787c0e764SRichard Cochran event = list_entry(this, struct cpts_event, list); 43887c0e764SRichard Cochran if (event_expired(event)) { 43987c0e764SRichard Cochran list_del_init(&event->list); 44087c0e764SRichard Cochran list_add(&event->list, &cpts->pool); 44187c0e764SRichard Cochran continue; 44287c0e764SRichard Cochran } 4433bfd41b5SGrygorii Strashko 4443bfd41b5SGrygorii Strashko mtype_seqid = event->high & 4453bfd41b5SGrygorii Strashko ((MESSAGE_TYPE_MASK << MESSAGE_TYPE_SHIFT) | 4463bfd41b5SGrygorii Strashko (SEQUENCE_ID_MASK << SEQUENCE_ID_SHIFT) | 4473bfd41b5SGrygorii Strashko (EVENT_TYPE_MASK << EVENT_TYPE_SHIFT)); 4483bfd41b5SGrygorii Strashko 4493bfd41b5SGrygorii Strashko if (mtype_seqid == skb_mtype_seqid) { 450e66dccceSGrygorii Strashko ns = event->timestamp; 45187c0e764SRichard Cochran list_del_init(&event->list); 45287c0e764SRichard Cochran list_add(&event->list, &cpts->pool); 45387c0e764SRichard Cochran break; 45487c0e764SRichard Cochran } 45587c0e764SRichard Cochran } 45687c0e764SRichard Cochran spin_unlock_irqrestore(&cpts->lock, flags); 45787c0e764SRichard Cochran 45887c0e764SRichard Cochran return ns; 45987c0e764SRichard Cochran } 46087c0e764SRichard Cochran 46187c0e764SRichard Cochran void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb) 46287c0e764SRichard Cochran { 4633bfd41b5SGrygorii Strashko struct cpts_skb_cb_data *skb_cb = (struct cpts_skb_cb_data *)skb->cb; 46487c0e764SRichard Cochran struct skb_shared_hwtstamps *ssh; 4653bfd41b5SGrygorii Strashko int ret; 4663bfd41b5SGrygorii Strashko u64 ns; 46787c0e764SRichard Cochran 4683bfd41b5SGrygorii Strashko ret = cpts_skb_get_mtype_seqid(skb, &skb_cb->skb_mtype_seqid); 4693bfd41b5SGrygorii Strashko if (!ret) 4703bfd41b5SGrygorii Strashko return; 4713bfd41b5SGrygorii Strashko 4723bfd41b5SGrygorii Strashko skb_cb->skb_mtype_seqid |= (CPTS_EV_RX << EVENT_TYPE_SHIFT); 4733bfd41b5SGrygorii Strashko 4743bfd41b5SGrygorii Strashko dev_dbg(cpts->dev, "%s mtype seqid %08x\n", 4753bfd41b5SGrygorii Strashko __func__, skb_cb->skb_mtype_seqid); 4763bfd41b5SGrygorii Strashko 4773bfd41b5SGrygorii Strashko ns = cpts_find_ts(cpts, skb, CPTS_EV_RX, skb_cb->skb_mtype_seqid); 47887c0e764SRichard Cochran if (!ns) 47987c0e764SRichard Cochran return; 48087c0e764SRichard Cochran ssh = skb_hwtstamps(skb); 48187c0e764SRichard Cochran memset(ssh, 0, sizeof(*ssh)); 48287c0e764SRichard Cochran ssh->hwtstamp = ns_to_ktime(ns); 48387c0e764SRichard Cochran } 484c8395d4eSGrygorii Strashko EXPORT_SYMBOL_GPL(cpts_rx_timestamp); 48587c0e764SRichard Cochran 48687c0e764SRichard Cochran void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb) 48787c0e764SRichard Cochran { 4883bfd41b5SGrygorii Strashko struct cpts_skb_cb_data *skb_cb = (struct cpts_skb_cb_data *)skb->cb; 4893bfd41b5SGrygorii Strashko int ret; 49087c0e764SRichard Cochran 49187c0e764SRichard Cochran if (!(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) 49287c0e764SRichard Cochran return; 4933bfd41b5SGrygorii Strashko 4943bfd41b5SGrygorii Strashko ret = cpts_skb_get_mtype_seqid(skb, &skb_cb->skb_mtype_seqid); 4953bfd41b5SGrygorii Strashko if (!ret) 4963bfd41b5SGrygorii Strashko return; 4973bfd41b5SGrygorii Strashko 4983bfd41b5SGrygorii Strashko skb_cb->skb_mtype_seqid |= (CPTS_EV_TX << EVENT_TYPE_SHIFT); 4993bfd41b5SGrygorii Strashko 5003bfd41b5SGrygorii Strashko dev_dbg(cpts->dev, "%s mtype seqid %08x\n", 5013bfd41b5SGrygorii Strashko __func__, skb_cb->skb_mtype_seqid); 5023bfd41b5SGrygorii Strashko 503c8f8e47eSGrygorii Strashko /* Always defer TX TS processing to PTP worker */ 504c8f8e47eSGrygorii Strashko skb_get(skb); 505c8f8e47eSGrygorii Strashko /* get the timestamp for timeouts */ 506c8f8e47eSGrygorii Strashko skb_cb->tmo = jiffies + msecs_to_jiffies(CPTS_SKB_RX_TX_TMO); 507c8f8e47eSGrygorii Strashko skb_queue_tail(&cpts->txq, skb); 508c8f8e47eSGrygorii Strashko ptp_schedule_worker(cpts->clock, 0); 50987c0e764SRichard Cochran } 510c8395d4eSGrygorii Strashko EXPORT_SYMBOL_GPL(cpts_tx_timestamp); 51187c0e764SRichard Cochran 5128a2c9a5aSGrygorii Strashko int cpts_register(struct cpts *cpts) 51387c0e764SRichard Cochran { 51487c0e764SRichard Cochran int err, i; 51587c0e764SRichard Cochran 5160d5f54feSGrygorii Strashko skb_queue_head_init(&cpts->txq); 51787c0e764SRichard Cochran INIT_LIST_HEAD(&cpts->events); 51887c0e764SRichard Cochran INIT_LIST_HEAD(&cpts->pool); 51987c0e764SRichard Cochran for (i = 0; i < CPTS_MAX_EVENTS; i++) 52087c0e764SRichard Cochran list_add(&cpts->pool_data[i].list, &cpts->pool); 52187c0e764SRichard Cochran 5228a2c9a5aSGrygorii Strashko clk_enable(cpts->refclk); 5238a2c9a5aSGrygorii Strashko 52487c0e764SRichard Cochran cpts_write32(cpts, CPTS_EN, control); 52587c0e764SRichard Cochran cpts_write32(cpts, TS_PEND_EN, int_enable); 52687c0e764SRichard Cochran 527693bd8b7SIvan Khoronzhuk timecounter_init(&cpts->tc, &cpts->cc, ktime_get_real_ns()); 52887c0e764SRichard Cochran 5298a2c9a5aSGrygorii Strashko cpts->clock = ptp_clock_register(&cpts->info, cpts->dev); 5306c691405SGrygorii Strashko if (IS_ERR(cpts->clock)) { 5316c691405SGrygorii Strashko err = PTR_ERR(cpts->clock); 5326c691405SGrygorii Strashko cpts->clock = NULL; 5336c691405SGrygorii Strashko goto err_ptp; 5346c691405SGrygorii Strashko } 5356c691405SGrygorii Strashko cpts->phc_index = ptp_clock_index(cpts->clock); 5366c691405SGrygorii Strashko 537999f1292SGrygorii Strashko ptp_schedule_worker(cpts->clock, cpts->ov_check_period); 53887c0e764SRichard Cochran return 0; 5396c691405SGrygorii Strashko 5406c691405SGrygorii Strashko err_ptp: 5418a2c9a5aSGrygorii Strashko clk_disable(cpts->refclk); 5426c691405SGrygorii Strashko return err; 54387c0e764SRichard Cochran } 544c8395d4eSGrygorii Strashko EXPORT_SYMBOL_GPL(cpts_register); 54587c0e764SRichard Cochran 54687c0e764SRichard Cochran void cpts_unregister(struct cpts *cpts) 54787c0e764SRichard Cochran { 5488a2c9a5aSGrygorii Strashko if (WARN_ON(!cpts->clock)) 5498a2c9a5aSGrygorii Strashko return; 5508a2c9a5aSGrygorii Strashko 5518a2c9a5aSGrygorii Strashko ptp_clock_unregister(cpts->clock); 5528a2c9a5aSGrygorii Strashko cpts->clock = NULL; 5538fcd6891SGrygorii Strashko 5548fcd6891SGrygorii Strashko cpts_write32(cpts, 0, int_enable); 5558fcd6891SGrygorii Strashko cpts_write32(cpts, 0, control); 5568fcd6891SGrygorii Strashko 5570d5f54feSGrygorii Strashko /* Drop all packet */ 5580d5f54feSGrygorii Strashko skb_queue_purge(&cpts->txq); 5590d5f54feSGrygorii Strashko 5608a2c9a5aSGrygorii Strashko clk_disable(cpts->refclk); 56187c0e764SRichard Cochran } 562c8395d4eSGrygorii Strashko EXPORT_SYMBOL_GPL(cpts_unregister); 563c8395d4eSGrygorii Strashko 56488f0f0b0SGrygorii Strashko static void cpts_calc_mult_shift(struct cpts *cpts) 56588f0f0b0SGrygorii Strashko { 56688f0f0b0SGrygorii Strashko u64 frac, maxsec, ns; 56788f0f0b0SGrygorii Strashko u32 freq; 56888f0f0b0SGrygorii Strashko 56988f0f0b0SGrygorii Strashko freq = clk_get_rate(cpts->refclk); 57088f0f0b0SGrygorii Strashko 57188f0f0b0SGrygorii Strashko /* Calc the maximum number of seconds which we can run before 57288f0f0b0SGrygorii Strashko * wrapping around. 57388f0f0b0SGrygorii Strashko */ 57488f0f0b0SGrygorii Strashko maxsec = cpts->cc.mask; 57588f0f0b0SGrygorii Strashko do_div(maxsec, freq); 57688f0f0b0SGrygorii Strashko /* limit conversation rate to 10 sec as higher values will produce 57788f0f0b0SGrygorii Strashko * too small mult factors and so reduce the conversion accuracy 57888f0f0b0SGrygorii Strashko */ 57988f0f0b0SGrygorii Strashko if (maxsec > 10) 58088f0f0b0SGrygorii Strashko maxsec = 10; 58188f0f0b0SGrygorii Strashko 58220138cf9SGrygorii Strashko /* Calc overflow check period (maxsec / 2) */ 58320138cf9SGrygorii Strashko cpts->ov_check_period = (HZ * maxsec) / 2; 58420138cf9SGrygorii Strashko dev_info(cpts->dev, "cpts: overflow check period %lu (jiffies)\n", 58520138cf9SGrygorii Strashko cpts->ov_check_period); 58620138cf9SGrygorii Strashko 58788f0f0b0SGrygorii Strashko if (cpts->cc.mult || cpts->cc.shift) 58888f0f0b0SGrygorii Strashko return; 58988f0f0b0SGrygorii Strashko 59088f0f0b0SGrygorii Strashko clocks_calc_mult_shift(&cpts->cc.mult, &cpts->cc.shift, 59188f0f0b0SGrygorii Strashko freq, NSEC_PER_SEC, maxsec); 59288f0f0b0SGrygorii Strashko 59388f0f0b0SGrygorii Strashko frac = 0; 59488f0f0b0SGrygorii Strashko ns = cyclecounter_cyc2ns(&cpts->cc, freq, cpts->cc.mask, &frac); 59588f0f0b0SGrygorii Strashko 59688f0f0b0SGrygorii Strashko dev_info(cpts->dev, 59788f0f0b0SGrygorii Strashko "CPTS: ref_clk_freq:%u calc_mult:%u calc_shift:%u error:%lld nsec/sec\n", 59888f0f0b0SGrygorii Strashko freq, cpts->cc.mult, cpts->cc.shift, (ns - NSEC_PER_SEC)); 59988f0f0b0SGrygorii Strashko } 60088f0f0b0SGrygorii Strashko 601a3047a81SGrygorii Strashko static int cpts_of_mux_clk_setup(struct cpts *cpts, struct device_node *node) 602a3047a81SGrygorii Strashko { 603a3047a81SGrygorii Strashko struct device_node *refclk_np; 604a3047a81SGrygorii Strashko const char **parent_names; 605a3047a81SGrygorii Strashko unsigned int num_parents; 606a3047a81SGrygorii Strashko struct clk_hw *clk_hw; 607a3047a81SGrygorii Strashko int ret = -EINVAL; 608a3047a81SGrygorii Strashko u32 *mux_table; 609a3047a81SGrygorii Strashko 610a3047a81SGrygorii Strashko refclk_np = of_get_child_by_name(node, "cpts-refclk-mux"); 611a3047a81SGrygorii Strashko if (!refclk_np) 612a3047a81SGrygorii Strashko /* refclk selection supported not for all SoCs */ 613a3047a81SGrygorii Strashko return 0; 614a3047a81SGrygorii Strashko 615a3047a81SGrygorii Strashko num_parents = of_clk_get_parent_count(refclk_np); 616a3047a81SGrygorii Strashko if (num_parents < 1) { 617a3047a81SGrygorii Strashko dev_err(cpts->dev, "mux-clock %s must have parents\n", 618a3047a81SGrygorii Strashko refclk_np->name); 619a3047a81SGrygorii Strashko goto mux_fail; 620a3047a81SGrygorii Strashko } 621a3047a81SGrygorii Strashko 622a3047a81SGrygorii Strashko parent_names = devm_kzalloc(cpts->dev, (sizeof(char *) * num_parents), 623a3047a81SGrygorii Strashko GFP_KERNEL); 624a3047a81SGrygorii Strashko 625a3047a81SGrygorii Strashko mux_table = devm_kzalloc(cpts->dev, sizeof(*mux_table) * num_parents, 626a3047a81SGrygorii Strashko GFP_KERNEL); 627a3047a81SGrygorii Strashko if (!mux_table || !parent_names) { 628a3047a81SGrygorii Strashko ret = -ENOMEM; 629a3047a81SGrygorii Strashko goto mux_fail; 630a3047a81SGrygorii Strashko } 631a3047a81SGrygorii Strashko 632a3047a81SGrygorii Strashko of_clk_parent_fill(refclk_np, parent_names, num_parents); 633a3047a81SGrygorii Strashko 634a3047a81SGrygorii Strashko ret = of_property_read_variable_u32_array(refclk_np, "ti,mux-tbl", 635a3047a81SGrygorii Strashko mux_table, 636a3047a81SGrygorii Strashko num_parents, num_parents); 637a3047a81SGrygorii Strashko if (ret < 0) 638a3047a81SGrygorii Strashko goto mux_fail; 639a3047a81SGrygorii Strashko 640a3047a81SGrygorii Strashko clk_hw = clk_hw_register_mux_table(cpts->dev, refclk_np->name, 641a3047a81SGrygorii Strashko parent_names, num_parents, 642a3047a81SGrygorii Strashko 0, 643a3047a81SGrygorii Strashko &cpts->reg->rftclk_sel, 0, 0x1F, 644a3047a81SGrygorii Strashko 0, mux_table, NULL); 645a3047a81SGrygorii Strashko if (IS_ERR(clk_hw)) { 646a3047a81SGrygorii Strashko ret = PTR_ERR(clk_hw); 647a3047a81SGrygorii Strashko goto mux_fail; 648a3047a81SGrygorii Strashko } 649a3047a81SGrygorii Strashko 650a3047a81SGrygorii Strashko ret = devm_add_action_or_reset(cpts->dev, 651a3047a81SGrygorii Strashko (void(*)(void *))clk_hw_unregister_mux, 652a3047a81SGrygorii Strashko clk_hw); 653a3047a81SGrygorii Strashko if (ret) { 654a3047a81SGrygorii Strashko dev_err(cpts->dev, "add clkmux unreg action %d", ret); 655a3047a81SGrygorii Strashko goto mux_fail; 656a3047a81SGrygorii Strashko } 657a3047a81SGrygorii Strashko 658a3047a81SGrygorii Strashko ret = of_clk_add_hw_provider(refclk_np, of_clk_hw_simple_get, clk_hw); 659a3047a81SGrygorii Strashko if (ret) 660a3047a81SGrygorii Strashko goto mux_fail; 661a3047a81SGrygorii Strashko 662a3047a81SGrygorii Strashko ret = devm_add_action_or_reset(cpts->dev, 663a3047a81SGrygorii Strashko (void(*)(void *))of_clk_del_provider, 664a3047a81SGrygorii Strashko refclk_np); 665a3047a81SGrygorii Strashko if (ret) { 666a3047a81SGrygorii Strashko dev_err(cpts->dev, "add clkmux provider unreg action %d", ret); 667a3047a81SGrygorii Strashko goto mux_fail; 668a3047a81SGrygorii Strashko } 669a3047a81SGrygorii Strashko 670a3047a81SGrygorii Strashko return ret; 671a3047a81SGrygorii Strashko 672a3047a81SGrygorii Strashko mux_fail: 673a3047a81SGrygorii Strashko of_node_put(refclk_np); 674a3047a81SGrygorii Strashko return ret; 675a3047a81SGrygorii Strashko } 676a3047a81SGrygorii Strashko 6774a88fb95SGrygorii Strashko static int cpts_of_parse(struct cpts *cpts, struct device_node *node) 6784a88fb95SGrygorii Strashko { 6794a88fb95SGrygorii Strashko int ret = -EINVAL; 6804a88fb95SGrygorii Strashko u32 prop; 6814a88fb95SGrygorii Strashko 68288f0f0b0SGrygorii Strashko if (!of_property_read_u32(node, "cpts_clock_mult", &prop)) 6834a88fb95SGrygorii Strashko cpts->cc.mult = prop; 6844a88fb95SGrygorii Strashko 68588f0f0b0SGrygorii Strashko if (!of_property_read_u32(node, "cpts_clock_shift", &prop)) 6864a88fb95SGrygorii Strashko cpts->cc.shift = prop; 6874a88fb95SGrygorii Strashko 68888f0f0b0SGrygorii Strashko if ((cpts->cc.mult && !cpts->cc.shift) || 68988f0f0b0SGrygorii Strashko (!cpts->cc.mult && cpts->cc.shift)) 69088f0f0b0SGrygorii Strashko goto of_error; 69188f0f0b0SGrygorii Strashko 692a3047a81SGrygorii Strashko return cpts_of_mux_clk_setup(cpts, node); 6934a88fb95SGrygorii Strashko 6944a88fb95SGrygorii Strashko of_error: 6954a88fb95SGrygorii Strashko dev_err(cpts->dev, "CPTS: Missing property in the DT.\n"); 6964a88fb95SGrygorii Strashko return ret; 6974a88fb95SGrygorii Strashko } 6984a88fb95SGrygorii Strashko 6998a2c9a5aSGrygorii Strashko struct cpts *cpts_create(struct device *dev, void __iomem *regs, 7004a88fb95SGrygorii Strashko struct device_node *node) 7018a2c9a5aSGrygorii Strashko { 7028a2c9a5aSGrygorii Strashko struct cpts *cpts; 7034a88fb95SGrygorii Strashko int ret; 7048a2c9a5aSGrygorii Strashko 7058a2c9a5aSGrygorii Strashko cpts = devm_kzalloc(dev, sizeof(*cpts), GFP_KERNEL); 7068a2c9a5aSGrygorii Strashko if (!cpts) 7078a2c9a5aSGrygorii Strashko return ERR_PTR(-ENOMEM); 7088a2c9a5aSGrygorii Strashko 7098a2c9a5aSGrygorii Strashko cpts->dev = dev; 7108a2c9a5aSGrygorii Strashko cpts->reg = (struct cpsw_cpts __iomem *)regs; 7118a2c9a5aSGrygorii Strashko spin_lock_init(&cpts->lock); 712*ba107428SGrygorii Strashko mutex_init(&cpts->ptp_clk_mutex); 7138a2c9a5aSGrygorii Strashko 7144a88fb95SGrygorii Strashko ret = cpts_of_parse(cpts, node); 7154a88fb95SGrygorii Strashko if (ret) 7164a88fb95SGrygorii Strashko return ERR_PTR(ret); 7174a88fb95SGrygorii Strashko 7188a6389a5SGrygorii Strashko cpts->refclk = devm_get_clk_from_child(dev, node, "cpts"); 7198a6389a5SGrygorii Strashko if (IS_ERR(cpts->refclk)) 7208a6389a5SGrygorii Strashko /* try get clk from dev node for compatibility */ 7218a2c9a5aSGrygorii Strashko cpts->refclk = devm_clk_get(dev, "cpts"); 7228a6389a5SGrygorii Strashko 7238a2c9a5aSGrygorii Strashko if (IS_ERR(cpts->refclk)) { 7248a6389a5SGrygorii Strashko dev_err(dev, "Failed to get cpts refclk %ld\n", 7258a6389a5SGrygorii Strashko PTR_ERR(cpts->refclk)); 726bde4c563SHernán Gonzalez return ERR_CAST(cpts->refclk); 7278a2c9a5aSGrygorii Strashko } 7288a2c9a5aSGrygorii Strashko 7292d822f2dSKangjie Lu ret = clk_prepare(cpts->refclk); 7302d822f2dSKangjie Lu if (ret) 7312d822f2dSKangjie Lu return ERR_PTR(ret); 7328a2c9a5aSGrygorii Strashko 7338a2c9a5aSGrygorii Strashko cpts->cc.read = cpts_systim_read; 7348a2c9a5aSGrygorii Strashko cpts->cc.mask = CLOCKSOURCE_MASK(32); 73588f0f0b0SGrygorii Strashko cpts->info = cpts_info; 73688f0f0b0SGrygorii Strashko 73788f0f0b0SGrygorii Strashko cpts_calc_mult_shift(cpts); 7384a88fb95SGrygorii Strashko /* save cc.mult original value as it can be modified 7394a88fb95SGrygorii Strashko * by cpts_ptp_adjfreq(). 7404a88fb95SGrygorii Strashko */ 7414a88fb95SGrygorii Strashko cpts->cc_mult = cpts->cc.mult; 7428a2c9a5aSGrygorii Strashko 7438a2c9a5aSGrygorii Strashko return cpts; 7448a2c9a5aSGrygorii Strashko } 7458a2c9a5aSGrygorii Strashko EXPORT_SYMBOL_GPL(cpts_create); 7468a2c9a5aSGrygorii Strashko 7478a2c9a5aSGrygorii Strashko void cpts_release(struct cpts *cpts) 7488a2c9a5aSGrygorii Strashko { 7498a2c9a5aSGrygorii Strashko if (!cpts) 7508a2c9a5aSGrygorii Strashko return; 7518a2c9a5aSGrygorii Strashko 7528a2c9a5aSGrygorii Strashko if (WARN_ON(!cpts->refclk)) 7538a2c9a5aSGrygorii Strashko return; 7548a2c9a5aSGrygorii Strashko 7558a2c9a5aSGrygorii Strashko clk_unprepare(cpts->refclk); 7568a2c9a5aSGrygorii Strashko } 7578a2c9a5aSGrygorii Strashko EXPORT_SYMBOL_GPL(cpts_release); 7588a2c9a5aSGrygorii Strashko 759c8395d4eSGrygorii Strashko MODULE_LICENSE("GPL v2"); 760c8395d4eSGrygorii Strashko MODULE_DESCRIPTION("TI CPTS driver"); 761c8395d4eSGrygorii Strashko MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>"); 762