162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * TI Common Platform Time Sync 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 Richard Cochran <richardcochran@gmail.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/clk-provider.h> 962306a36Sopenharmony_ci#include <linux/err.h> 1062306a36Sopenharmony_ci#include <linux/if.h> 1162306a36Sopenharmony_ci#include <linux/hrtimer.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/net_tstamp.h> 1462306a36Sopenharmony_ci#include <linux/ptp_classify.h> 1562306a36Sopenharmony_ci#include <linux/time.h> 1662306a36Sopenharmony_ci#include <linux/uaccess.h> 1762306a36Sopenharmony_ci#include <linux/workqueue.h> 1862306a36Sopenharmony_ci#include <linux/if_ether.h> 1962306a36Sopenharmony_ci#include <linux/if_vlan.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "cpts.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define CPTS_SKB_TX_WORK_TIMEOUT 1 /* jiffies */ 2462306a36Sopenharmony_ci#define CPTS_SKB_RX_TX_TMO 100 /*ms */ 2562306a36Sopenharmony_ci#define CPTS_EVENT_RX_TX_TIMEOUT (100) /* ms */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistruct cpts_skb_cb_data { 2862306a36Sopenharmony_ci u32 skb_mtype_seqid; 2962306a36Sopenharmony_ci unsigned long tmo; 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define cpts_read32(c, r) readl_relaxed(&c->reg->r) 3362306a36Sopenharmony_ci#define cpts_write32(c, v, r) writel_relaxed(v, &c->reg->r) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int cpts_event_port(struct cpts_event *event) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci return (event->high >> PORT_NUMBER_SHIFT) & PORT_NUMBER_MASK; 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic int event_expired(struct cpts_event *event) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci return time_after(jiffies, event->tmo); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int event_type(struct cpts_event *event) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci return (event->high >> EVENT_TYPE_SHIFT) & EVENT_TYPE_MASK; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int cpts_fifo_pop(struct cpts *cpts, u32 *high, u32 *low) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci u32 r = cpts_read32(cpts, intstat_raw); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (r & TS_PEND_RAW) { 5562306a36Sopenharmony_ci *high = cpts_read32(cpts, event_high); 5662306a36Sopenharmony_ci *low = cpts_read32(cpts, event_low); 5762306a36Sopenharmony_ci cpts_write32(cpts, EVENT_POP, event_pop); 5862306a36Sopenharmony_ci return 0; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci return -1; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic int cpts_purge_events(struct cpts *cpts) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct list_head *this, *next; 6662306a36Sopenharmony_ci struct cpts_event *event; 6762306a36Sopenharmony_ci int removed = 0; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci list_for_each_safe(this, next, &cpts->events) { 7062306a36Sopenharmony_ci event = list_entry(this, struct cpts_event, list); 7162306a36Sopenharmony_ci if (event_expired(event)) { 7262306a36Sopenharmony_ci list_del_init(&event->list); 7362306a36Sopenharmony_ci list_add(&event->list, &cpts->pool); 7462306a36Sopenharmony_ci ++removed; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (removed) 7962306a36Sopenharmony_ci dev_dbg(cpts->dev, "cpts: event pool cleaned up %d\n", removed); 8062306a36Sopenharmony_ci return removed ? 0 : -1; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic void cpts_purge_txq(struct cpts *cpts) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci struct cpts_skb_cb_data *skb_cb; 8662306a36Sopenharmony_ci struct sk_buff *skb, *tmp; 8762306a36Sopenharmony_ci int removed = 0; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci skb_queue_walk_safe(&cpts->txq, skb, tmp) { 9062306a36Sopenharmony_ci skb_cb = (struct cpts_skb_cb_data *)skb->cb; 9162306a36Sopenharmony_ci if (time_after(jiffies, skb_cb->tmo)) { 9262306a36Sopenharmony_ci __skb_unlink(skb, &cpts->txq); 9362306a36Sopenharmony_ci dev_consume_skb_any(skb); 9462306a36Sopenharmony_ci ++removed; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (removed) 9962306a36Sopenharmony_ci dev_dbg(cpts->dev, "txq cleaned up %d\n", removed); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* 10362306a36Sopenharmony_ci * Returns zero if matching event type was found. 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_cistatic int cpts_fifo_read(struct cpts *cpts, int match) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct ptp_clock_event pevent; 10862306a36Sopenharmony_ci bool need_schedule = false; 10962306a36Sopenharmony_ci struct cpts_event *event; 11062306a36Sopenharmony_ci unsigned long flags; 11162306a36Sopenharmony_ci int i, type = -1; 11262306a36Sopenharmony_ci u32 hi, lo; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci spin_lock_irqsave(&cpts->lock, flags); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci for (i = 0; i < CPTS_FIFO_DEPTH; i++) { 11762306a36Sopenharmony_ci if (cpts_fifo_pop(cpts, &hi, &lo)) 11862306a36Sopenharmony_ci break; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (list_empty(&cpts->pool) && cpts_purge_events(cpts)) { 12162306a36Sopenharmony_ci dev_warn(cpts->dev, "cpts: event pool empty\n"); 12262306a36Sopenharmony_ci break; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci event = list_first_entry(&cpts->pool, struct cpts_event, list); 12662306a36Sopenharmony_ci event->high = hi; 12762306a36Sopenharmony_ci event->low = lo; 12862306a36Sopenharmony_ci event->timestamp = timecounter_cyc2time(&cpts->tc, event->low); 12962306a36Sopenharmony_ci type = event_type(event); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci dev_dbg(cpts->dev, "CPTS_EV: %d high:%08X low:%08x\n", 13262306a36Sopenharmony_ci type, event->high, event->low); 13362306a36Sopenharmony_ci switch (type) { 13462306a36Sopenharmony_ci case CPTS_EV_PUSH: 13562306a36Sopenharmony_ci WRITE_ONCE(cpts->cur_timestamp, lo); 13662306a36Sopenharmony_ci timecounter_read(&cpts->tc); 13762306a36Sopenharmony_ci if (cpts->mult_new) { 13862306a36Sopenharmony_ci cpts->cc.mult = cpts->mult_new; 13962306a36Sopenharmony_ci cpts->mult_new = 0; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci if (!cpts->irq_poll) 14262306a36Sopenharmony_ci complete(&cpts->ts_push_complete); 14362306a36Sopenharmony_ci break; 14462306a36Sopenharmony_ci case CPTS_EV_TX: 14562306a36Sopenharmony_ci case CPTS_EV_RX: 14662306a36Sopenharmony_ci event->tmo = jiffies + 14762306a36Sopenharmony_ci msecs_to_jiffies(CPTS_EVENT_RX_TX_TIMEOUT); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci list_del_init(&event->list); 15062306a36Sopenharmony_ci list_add_tail(&event->list, &cpts->events); 15162306a36Sopenharmony_ci need_schedule = true; 15262306a36Sopenharmony_ci break; 15362306a36Sopenharmony_ci case CPTS_EV_ROLL: 15462306a36Sopenharmony_ci case CPTS_EV_HALF: 15562306a36Sopenharmony_ci break; 15662306a36Sopenharmony_ci case CPTS_EV_HW: 15762306a36Sopenharmony_ci pevent.timestamp = event->timestamp; 15862306a36Sopenharmony_ci pevent.type = PTP_CLOCK_EXTTS; 15962306a36Sopenharmony_ci pevent.index = cpts_event_port(event) - 1; 16062306a36Sopenharmony_ci ptp_clock_event(cpts->clock, &pevent); 16162306a36Sopenharmony_ci break; 16262306a36Sopenharmony_ci default: 16362306a36Sopenharmony_ci dev_err(cpts->dev, "cpts: unknown event type\n"); 16462306a36Sopenharmony_ci break; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci if (type == match) 16762306a36Sopenharmony_ci break; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci spin_unlock_irqrestore(&cpts->lock, flags); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (!cpts->irq_poll && need_schedule) 17362306a36Sopenharmony_ci ptp_schedule_worker(cpts->clock, 0); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return type == match ? 0 : -1; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_civoid cpts_misc_interrupt(struct cpts *cpts) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci cpts_fifo_read(cpts, -1); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cpts_misc_interrupt); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic u64 cpts_systim_read(const struct cyclecounter *cc) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct cpts *cpts = container_of(cc, struct cpts, cc); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return READ_ONCE(cpts->cur_timestamp); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic void cpts_update_cur_time(struct cpts *cpts, int match, 19262306a36Sopenharmony_ci struct ptp_system_timestamp *sts) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci unsigned long flags; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci reinit_completion(&cpts->ts_push_complete); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* use spin_lock_irqsave() here as it has to run very fast */ 19962306a36Sopenharmony_ci spin_lock_irqsave(&cpts->lock, flags); 20062306a36Sopenharmony_ci ptp_read_system_prets(sts); 20162306a36Sopenharmony_ci cpts_write32(cpts, TS_PUSH, ts_push); 20262306a36Sopenharmony_ci cpts_read32(cpts, ts_push); 20362306a36Sopenharmony_ci ptp_read_system_postts(sts); 20462306a36Sopenharmony_ci spin_unlock_irqrestore(&cpts->lock, flags); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (cpts->irq_poll && cpts_fifo_read(cpts, match) && match != -1) 20762306a36Sopenharmony_ci dev_err(cpts->dev, "cpts: unable to obtain a time stamp\n"); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (!cpts->irq_poll && 21062306a36Sopenharmony_ci !wait_for_completion_timeout(&cpts->ts_push_complete, HZ)) 21162306a36Sopenharmony_ci dev_err(cpts->dev, "cpts: obtain a time stamp timeout\n"); 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci/* PTP clock operations */ 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic int cpts_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci struct cpts *cpts = container_of(ptp, struct cpts, info); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci mutex_lock(&cpts->ptp_clk_mutex); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci cpts->mult_new = adjust_by_scaled_ppm(cpts->cc_mult, scaled_ppm); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci cpts_update_cur_time(cpts, CPTS_EV_PUSH, NULL); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci mutex_unlock(&cpts->ptp_clk_mutex); 22762306a36Sopenharmony_ci return 0; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic int cpts_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci struct cpts *cpts = container_of(ptp, struct cpts, info); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci mutex_lock(&cpts->ptp_clk_mutex); 23562306a36Sopenharmony_ci timecounter_adjtime(&cpts->tc, delta); 23662306a36Sopenharmony_ci mutex_unlock(&cpts->ptp_clk_mutex); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci return 0; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic int cpts_ptp_gettimeex(struct ptp_clock_info *ptp, 24262306a36Sopenharmony_ci struct timespec64 *ts, 24362306a36Sopenharmony_ci struct ptp_system_timestamp *sts) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci struct cpts *cpts = container_of(ptp, struct cpts, info); 24662306a36Sopenharmony_ci u64 ns; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci mutex_lock(&cpts->ptp_clk_mutex); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci cpts_update_cur_time(cpts, CPTS_EV_PUSH, sts); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci ns = timecounter_read(&cpts->tc); 25362306a36Sopenharmony_ci mutex_unlock(&cpts->ptp_clk_mutex); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci *ts = ns_to_timespec64(ns); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic int cpts_ptp_settime(struct ptp_clock_info *ptp, 26162306a36Sopenharmony_ci const struct timespec64 *ts) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci struct cpts *cpts = container_of(ptp, struct cpts, info); 26462306a36Sopenharmony_ci u64 ns; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci ns = timespec64_to_ns(ts); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci mutex_lock(&cpts->ptp_clk_mutex); 26962306a36Sopenharmony_ci timecounter_init(&cpts->tc, &cpts->cc, ns); 27062306a36Sopenharmony_ci mutex_unlock(&cpts->ptp_clk_mutex); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci return 0; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic int cpts_extts_enable(struct cpts *cpts, u32 index, int on) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci u32 v; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (((cpts->hw_ts_enable & BIT(index)) >> index) == on) 28062306a36Sopenharmony_ci return 0; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci mutex_lock(&cpts->ptp_clk_mutex); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci v = cpts_read32(cpts, control); 28562306a36Sopenharmony_ci if (on) { 28662306a36Sopenharmony_ci v |= BIT(8 + index); 28762306a36Sopenharmony_ci cpts->hw_ts_enable |= BIT(index); 28862306a36Sopenharmony_ci } else { 28962306a36Sopenharmony_ci v &= ~BIT(8 + index); 29062306a36Sopenharmony_ci cpts->hw_ts_enable &= ~BIT(index); 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci cpts_write32(cpts, v, control); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci mutex_unlock(&cpts->ptp_clk_mutex); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci return 0; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic int cpts_ptp_enable(struct ptp_clock_info *ptp, 30062306a36Sopenharmony_ci struct ptp_clock_request *rq, int on) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct cpts *cpts = container_of(ptp, struct cpts, info); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci switch (rq->type) { 30562306a36Sopenharmony_ci case PTP_CLK_REQ_EXTTS: 30662306a36Sopenharmony_ci return cpts_extts_enable(cpts, rq->extts.index, on); 30762306a36Sopenharmony_ci default: 30862306a36Sopenharmony_ci break; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci return -EOPNOTSUPP; 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic bool cpts_match_tx_ts(struct cpts *cpts, struct cpts_event *event) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct sk_buff_head txq_list; 31762306a36Sopenharmony_ci struct sk_buff *skb, *tmp; 31862306a36Sopenharmony_ci unsigned long flags; 31962306a36Sopenharmony_ci bool found = false; 32062306a36Sopenharmony_ci u32 mtype_seqid; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci mtype_seqid = event->high & 32362306a36Sopenharmony_ci ((MESSAGE_TYPE_MASK << MESSAGE_TYPE_SHIFT) | 32462306a36Sopenharmony_ci (SEQUENCE_ID_MASK << SEQUENCE_ID_SHIFT) | 32562306a36Sopenharmony_ci (EVENT_TYPE_MASK << EVENT_TYPE_SHIFT)); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci __skb_queue_head_init(&txq_list); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci spin_lock_irqsave(&cpts->txq.lock, flags); 33062306a36Sopenharmony_ci skb_queue_splice_init(&cpts->txq, &txq_list); 33162306a36Sopenharmony_ci spin_unlock_irqrestore(&cpts->txq.lock, flags); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci skb_queue_walk_safe(&txq_list, skb, tmp) { 33462306a36Sopenharmony_ci struct skb_shared_hwtstamps ssh; 33562306a36Sopenharmony_ci struct cpts_skb_cb_data *skb_cb = 33662306a36Sopenharmony_ci (struct cpts_skb_cb_data *)skb->cb; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (mtype_seqid == skb_cb->skb_mtype_seqid) { 33962306a36Sopenharmony_ci memset(&ssh, 0, sizeof(ssh)); 34062306a36Sopenharmony_ci ssh.hwtstamp = ns_to_ktime(event->timestamp); 34162306a36Sopenharmony_ci skb_tstamp_tx(skb, &ssh); 34262306a36Sopenharmony_ci found = true; 34362306a36Sopenharmony_ci __skb_unlink(skb, &txq_list); 34462306a36Sopenharmony_ci dev_consume_skb_any(skb); 34562306a36Sopenharmony_ci dev_dbg(cpts->dev, "match tx timestamp mtype_seqid %08x\n", 34662306a36Sopenharmony_ci mtype_seqid); 34762306a36Sopenharmony_ci break; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (time_after(jiffies, skb_cb->tmo)) { 35162306a36Sopenharmony_ci /* timeout any expired skbs over 1s */ 35262306a36Sopenharmony_ci dev_dbg(cpts->dev, "expiring tx timestamp from txq\n"); 35362306a36Sopenharmony_ci __skb_unlink(skb, &txq_list); 35462306a36Sopenharmony_ci dev_consume_skb_any(skb); 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci spin_lock_irqsave(&cpts->txq.lock, flags); 35962306a36Sopenharmony_ci skb_queue_splice(&txq_list, &cpts->txq); 36062306a36Sopenharmony_ci spin_unlock_irqrestore(&cpts->txq.lock, flags); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci return found; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic void cpts_process_events(struct cpts *cpts) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci struct list_head *this, *next; 36862306a36Sopenharmony_ci struct cpts_event *event; 36962306a36Sopenharmony_ci LIST_HEAD(events_free); 37062306a36Sopenharmony_ci unsigned long flags; 37162306a36Sopenharmony_ci LIST_HEAD(events); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci spin_lock_irqsave(&cpts->lock, flags); 37462306a36Sopenharmony_ci list_splice_init(&cpts->events, &events); 37562306a36Sopenharmony_ci spin_unlock_irqrestore(&cpts->lock, flags); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci list_for_each_safe(this, next, &events) { 37862306a36Sopenharmony_ci event = list_entry(this, struct cpts_event, list); 37962306a36Sopenharmony_ci if (cpts_match_tx_ts(cpts, event) || 38062306a36Sopenharmony_ci time_after(jiffies, event->tmo)) { 38162306a36Sopenharmony_ci list_del_init(&event->list); 38262306a36Sopenharmony_ci list_add(&event->list, &events_free); 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci spin_lock_irqsave(&cpts->lock, flags); 38762306a36Sopenharmony_ci list_splice_tail(&events, &cpts->events); 38862306a36Sopenharmony_ci list_splice_tail(&events_free, &cpts->pool); 38962306a36Sopenharmony_ci spin_unlock_irqrestore(&cpts->lock, flags); 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic long cpts_overflow_check(struct ptp_clock_info *ptp) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct cpts *cpts = container_of(ptp, struct cpts, info); 39562306a36Sopenharmony_ci unsigned long delay = cpts->ov_check_period; 39662306a36Sopenharmony_ci unsigned long flags; 39762306a36Sopenharmony_ci u64 ns; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci mutex_lock(&cpts->ptp_clk_mutex); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci cpts_update_cur_time(cpts, -1, NULL); 40262306a36Sopenharmony_ci ns = timecounter_read(&cpts->tc); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci cpts_process_events(cpts); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci spin_lock_irqsave(&cpts->txq.lock, flags); 40762306a36Sopenharmony_ci if (!skb_queue_empty(&cpts->txq)) { 40862306a36Sopenharmony_ci cpts_purge_txq(cpts); 40962306a36Sopenharmony_ci if (!skb_queue_empty(&cpts->txq)) 41062306a36Sopenharmony_ci delay = CPTS_SKB_TX_WORK_TIMEOUT; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci spin_unlock_irqrestore(&cpts->txq.lock, flags); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci dev_dbg(cpts->dev, "cpts overflow check at %lld\n", ns); 41562306a36Sopenharmony_ci mutex_unlock(&cpts->ptp_clk_mutex); 41662306a36Sopenharmony_ci return (long)delay; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic const struct ptp_clock_info cpts_info = { 42062306a36Sopenharmony_ci .owner = THIS_MODULE, 42162306a36Sopenharmony_ci .name = "CTPS timer", 42262306a36Sopenharmony_ci .max_adj = 1000000, 42362306a36Sopenharmony_ci .n_ext_ts = 0, 42462306a36Sopenharmony_ci .n_pins = 0, 42562306a36Sopenharmony_ci .pps = 0, 42662306a36Sopenharmony_ci .adjfine = cpts_ptp_adjfine, 42762306a36Sopenharmony_ci .adjtime = cpts_ptp_adjtime, 42862306a36Sopenharmony_ci .gettimex64 = cpts_ptp_gettimeex, 42962306a36Sopenharmony_ci .settime64 = cpts_ptp_settime, 43062306a36Sopenharmony_ci .enable = cpts_ptp_enable, 43162306a36Sopenharmony_ci .do_aux_work = cpts_overflow_check, 43262306a36Sopenharmony_ci}; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic int cpts_skb_get_mtype_seqid(struct sk_buff *skb, u32 *mtype_seqid) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci unsigned int ptp_class = ptp_classify_raw(skb); 43762306a36Sopenharmony_ci struct ptp_header *hdr; 43862306a36Sopenharmony_ci u8 msgtype; 43962306a36Sopenharmony_ci u16 seqid; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (ptp_class == PTP_CLASS_NONE) 44262306a36Sopenharmony_ci return 0; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci hdr = ptp_parse_header(skb, ptp_class); 44562306a36Sopenharmony_ci if (!hdr) 44662306a36Sopenharmony_ci return 0; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci msgtype = ptp_get_msgtype(hdr, ptp_class); 44962306a36Sopenharmony_ci seqid = ntohs(hdr->sequence_id); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci *mtype_seqid = (msgtype & MESSAGE_TYPE_MASK) << MESSAGE_TYPE_SHIFT; 45262306a36Sopenharmony_ci *mtype_seqid |= (seqid & SEQUENCE_ID_MASK) << SEQUENCE_ID_SHIFT; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci return 1; 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic u64 cpts_find_ts(struct cpts *cpts, struct sk_buff *skb, 45862306a36Sopenharmony_ci int ev_type, u32 skb_mtype_seqid) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci struct list_head *this, *next; 46162306a36Sopenharmony_ci struct cpts_event *event; 46262306a36Sopenharmony_ci unsigned long flags; 46362306a36Sopenharmony_ci u32 mtype_seqid; 46462306a36Sopenharmony_ci u64 ns = 0; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci cpts_fifo_read(cpts, -1); 46762306a36Sopenharmony_ci spin_lock_irqsave(&cpts->lock, flags); 46862306a36Sopenharmony_ci list_for_each_safe(this, next, &cpts->events) { 46962306a36Sopenharmony_ci event = list_entry(this, struct cpts_event, list); 47062306a36Sopenharmony_ci if (event_expired(event)) { 47162306a36Sopenharmony_ci list_del_init(&event->list); 47262306a36Sopenharmony_ci list_add(&event->list, &cpts->pool); 47362306a36Sopenharmony_ci continue; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci mtype_seqid = event->high & 47762306a36Sopenharmony_ci ((MESSAGE_TYPE_MASK << MESSAGE_TYPE_SHIFT) | 47862306a36Sopenharmony_ci (SEQUENCE_ID_MASK << SEQUENCE_ID_SHIFT) | 47962306a36Sopenharmony_ci (EVENT_TYPE_MASK << EVENT_TYPE_SHIFT)); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (mtype_seqid == skb_mtype_seqid) { 48262306a36Sopenharmony_ci ns = event->timestamp; 48362306a36Sopenharmony_ci list_del_init(&event->list); 48462306a36Sopenharmony_ci list_add(&event->list, &cpts->pool); 48562306a36Sopenharmony_ci break; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci spin_unlock_irqrestore(&cpts->lock, flags); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci return ns; 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_civoid cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci struct cpts_skb_cb_data *skb_cb = (struct cpts_skb_cb_data *)skb->cb; 49662306a36Sopenharmony_ci struct skb_shared_hwtstamps *ssh; 49762306a36Sopenharmony_ci int ret; 49862306a36Sopenharmony_ci u64 ns; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* cpts_rx_timestamp() is called before eth_type_trans(), so 50162306a36Sopenharmony_ci * skb MAC Hdr properties are not configured yet. Hence need to 50262306a36Sopenharmony_ci * reset skb MAC header here 50362306a36Sopenharmony_ci */ 50462306a36Sopenharmony_ci skb_reset_mac_header(skb); 50562306a36Sopenharmony_ci ret = cpts_skb_get_mtype_seqid(skb, &skb_cb->skb_mtype_seqid); 50662306a36Sopenharmony_ci if (!ret) 50762306a36Sopenharmony_ci return; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci skb_cb->skb_mtype_seqid |= (CPTS_EV_RX << EVENT_TYPE_SHIFT); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci dev_dbg(cpts->dev, "%s mtype seqid %08x\n", 51262306a36Sopenharmony_ci __func__, skb_cb->skb_mtype_seqid); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci ns = cpts_find_ts(cpts, skb, CPTS_EV_RX, skb_cb->skb_mtype_seqid); 51562306a36Sopenharmony_ci if (!ns) 51662306a36Sopenharmony_ci return; 51762306a36Sopenharmony_ci ssh = skb_hwtstamps(skb); 51862306a36Sopenharmony_ci memset(ssh, 0, sizeof(*ssh)); 51962306a36Sopenharmony_ci ssh->hwtstamp = ns_to_ktime(ns); 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cpts_rx_timestamp); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_civoid cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci struct cpts_skb_cb_data *skb_cb = (struct cpts_skb_cb_data *)skb->cb; 52662306a36Sopenharmony_ci int ret; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci if (!(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) 52962306a36Sopenharmony_ci return; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci ret = cpts_skb_get_mtype_seqid(skb, &skb_cb->skb_mtype_seqid); 53262306a36Sopenharmony_ci if (!ret) 53362306a36Sopenharmony_ci return; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci skb_cb->skb_mtype_seqid |= (CPTS_EV_TX << EVENT_TYPE_SHIFT); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci dev_dbg(cpts->dev, "%s mtype seqid %08x\n", 53862306a36Sopenharmony_ci __func__, skb_cb->skb_mtype_seqid); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* Always defer TX TS processing to PTP worker */ 54162306a36Sopenharmony_ci skb_get(skb); 54262306a36Sopenharmony_ci /* get the timestamp for timeouts */ 54362306a36Sopenharmony_ci skb_cb->tmo = jiffies + msecs_to_jiffies(CPTS_SKB_RX_TX_TMO); 54462306a36Sopenharmony_ci skb_queue_tail(&cpts->txq, skb); 54562306a36Sopenharmony_ci ptp_schedule_worker(cpts->clock, 0); 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cpts_tx_timestamp); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ciint cpts_register(struct cpts *cpts) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci int err, i; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci skb_queue_head_init(&cpts->txq); 55462306a36Sopenharmony_ci INIT_LIST_HEAD(&cpts->events); 55562306a36Sopenharmony_ci INIT_LIST_HEAD(&cpts->pool); 55662306a36Sopenharmony_ci for (i = 0; i < CPTS_MAX_EVENTS; i++) 55762306a36Sopenharmony_ci list_add(&cpts->pool_data[i].list, &cpts->pool); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci err = clk_enable(cpts->refclk); 56062306a36Sopenharmony_ci if (err) 56162306a36Sopenharmony_ci return err; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci cpts_write32(cpts, CPTS_EN, control); 56462306a36Sopenharmony_ci cpts_write32(cpts, TS_PEND_EN, int_enable); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci timecounter_init(&cpts->tc, &cpts->cc, ktime_get_real_ns()); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci cpts->clock = ptp_clock_register(&cpts->info, cpts->dev); 56962306a36Sopenharmony_ci if (IS_ERR(cpts->clock)) { 57062306a36Sopenharmony_ci err = PTR_ERR(cpts->clock); 57162306a36Sopenharmony_ci cpts->clock = NULL; 57262306a36Sopenharmony_ci goto err_ptp; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci cpts->phc_index = ptp_clock_index(cpts->clock); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci ptp_schedule_worker(cpts->clock, cpts->ov_check_period); 57762306a36Sopenharmony_ci return 0; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_cierr_ptp: 58062306a36Sopenharmony_ci clk_disable(cpts->refclk); 58162306a36Sopenharmony_ci return err; 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cpts_register); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_civoid cpts_unregister(struct cpts *cpts) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci if (WARN_ON(!cpts->clock)) 58862306a36Sopenharmony_ci return; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci ptp_clock_unregister(cpts->clock); 59162306a36Sopenharmony_ci cpts->clock = NULL; 59262306a36Sopenharmony_ci cpts->phc_index = -1; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci cpts_write32(cpts, 0, int_enable); 59562306a36Sopenharmony_ci cpts_write32(cpts, 0, control); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* Drop all packet */ 59862306a36Sopenharmony_ci skb_queue_purge(&cpts->txq); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci clk_disable(cpts->refclk); 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cpts_unregister); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistatic void cpts_calc_mult_shift(struct cpts *cpts) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci u64 frac, maxsec, ns; 60762306a36Sopenharmony_ci u32 freq; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci freq = clk_get_rate(cpts->refclk); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci /* Calc the maximum number of seconds which we can run before 61262306a36Sopenharmony_ci * wrapping around. 61362306a36Sopenharmony_ci */ 61462306a36Sopenharmony_ci maxsec = cpts->cc.mask; 61562306a36Sopenharmony_ci do_div(maxsec, freq); 61662306a36Sopenharmony_ci /* limit conversation rate to 10 sec as higher values will produce 61762306a36Sopenharmony_ci * too small mult factors and so reduce the conversion accuracy 61862306a36Sopenharmony_ci */ 61962306a36Sopenharmony_ci if (maxsec > 10) 62062306a36Sopenharmony_ci maxsec = 10; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci /* Calc overflow check period (maxsec / 2) */ 62362306a36Sopenharmony_ci cpts->ov_check_period = (HZ * maxsec) / 2; 62462306a36Sopenharmony_ci dev_info(cpts->dev, "cpts: overflow check period %lu (jiffies)\n", 62562306a36Sopenharmony_ci cpts->ov_check_period); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if (cpts->cc.mult || cpts->cc.shift) 62862306a36Sopenharmony_ci return; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci clocks_calc_mult_shift(&cpts->cc.mult, &cpts->cc.shift, 63162306a36Sopenharmony_ci freq, NSEC_PER_SEC, maxsec); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci frac = 0; 63462306a36Sopenharmony_ci ns = cyclecounter_cyc2ns(&cpts->cc, freq, cpts->cc.mask, &frac); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci dev_info(cpts->dev, 63762306a36Sopenharmony_ci "CPTS: ref_clk_freq:%u calc_mult:%u calc_shift:%u error:%lld nsec/sec\n", 63862306a36Sopenharmony_ci freq, cpts->cc.mult, cpts->cc.shift, (ns - NSEC_PER_SEC)); 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic int cpts_of_mux_clk_setup(struct cpts *cpts, struct device_node *node) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci struct device_node *refclk_np; 64462306a36Sopenharmony_ci const char **parent_names; 64562306a36Sopenharmony_ci unsigned int num_parents; 64662306a36Sopenharmony_ci struct clk_hw *clk_hw; 64762306a36Sopenharmony_ci int ret = -EINVAL; 64862306a36Sopenharmony_ci u32 *mux_table; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci refclk_np = of_get_child_by_name(node, "cpts-refclk-mux"); 65162306a36Sopenharmony_ci if (!refclk_np) 65262306a36Sopenharmony_ci /* refclk selection supported not for all SoCs */ 65362306a36Sopenharmony_ci return 0; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci num_parents = of_clk_get_parent_count(refclk_np); 65662306a36Sopenharmony_ci if (num_parents < 1) { 65762306a36Sopenharmony_ci dev_err(cpts->dev, "mux-clock %s must have parents\n", 65862306a36Sopenharmony_ci refclk_np->name); 65962306a36Sopenharmony_ci goto mux_fail; 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci parent_names = devm_kcalloc(cpts->dev, num_parents, 66362306a36Sopenharmony_ci sizeof(*parent_names), GFP_KERNEL); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci mux_table = devm_kcalloc(cpts->dev, num_parents, sizeof(*mux_table), 66662306a36Sopenharmony_ci GFP_KERNEL); 66762306a36Sopenharmony_ci if (!mux_table || !parent_names) { 66862306a36Sopenharmony_ci ret = -ENOMEM; 66962306a36Sopenharmony_ci goto mux_fail; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci of_clk_parent_fill(refclk_np, parent_names, num_parents); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci ret = of_property_read_variable_u32_array(refclk_np, "ti,mux-tbl", 67562306a36Sopenharmony_ci mux_table, 67662306a36Sopenharmony_ci num_parents, num_parents); 67762306a36Sopenharmony_ci if (ret < 0) 67862306a36Sopenharmony_ci goto mux_fail; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci clk_hw = clk_hw_register_mux_table(cpts->dev, refclk_np->name, 68162306a36Sopenharmony_ci parent_names, num_parents, 68262306a36Sopenharmony_ci 0, 68362306a36Sopenharmony_ci &cpts->reg->rftclk_sel, 0, 0x1F, 68462306a36Sopenharmony_ci 0, mux_table, NULL); 68562306a36Sopenharmony_ci if (IS_ERR(clk_hw)) { 68662306a36Sopenharmony_ci ret = PTR_ERR(clk_hw); 68762306a36Sopenharmony_ci goto mux_fail; 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci ret = devm_add_action_or_reset(cpts->dev, 69162306a36Sopenharmony_ci (void(*)(void *))clk_hw_unregister_mux, 69262306a36Sopenharmony_ci clk_hw); 69362306a36Sopenharmony_ci if (ret) { 69462306a36Sopenharmony_ci dev_err(cpts->dev, "add clkmux unreg action %d", ret); 69562306a36Sopenharmony_ci goto mux_fail; 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci ret = of_clk_add_hw_provider(refclk_np, of_clk_hw_simple_get, clk_hw); 69962306a36Sopenharmony_ci if (ret) 70062306a36Sopenharmony_ci goto mux_fail; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci ret = devm_add_action_or_reset(cpts->dev, 70362306a36Sopenharmony_ci (void(*)(void *))of_clk_del_provider, 70462306a36Sopenharmony_ci refclk_np); 70562306a36Sopenharmony_ci if (ret) { 70662306a36Sopenharmony_ci dev_err(cpts->dev, "add clkmux provider unreg action %d", ret); 70762306a36Sopenharmony_ci goto mux_fail; 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci return ret; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cimux_fail: 71362306a36Sopenharmony_ci of_node_put(refclk_np); 71462306a36Sopenharmony_ci return ret; 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic int cpts_of_parse(struct cpts *cpts, struct device_node *node) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci int ret = -EINVAL; 72062306a36Sopenharmony_ci u32 prop; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci if (!of_property_read_u32(node, "cpts_clock_mult", &prop)) 72362306a36Sopenharmony_ci cpts->cc.mult = prop; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci if (!of_property_read_u32(node, "cpts_clock_shift", &prop)) 72662306a36Sopenharmony_ci cpts->cc.shift = prop; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci if ((cpts->cc.mult && !cpts->cc.shift) || 72962306a36Sopenharmony_ci (!cpts->cc.mult && cpts->cc.shift)) 73062306a36Sopenharmony_ci goto of_error; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci return cpts_of_mux_clk_setup(cpts, node); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ciof_error: 73562306a36Sopenharmony_ci dev_err(cpts->dev, "CPTS: Missing property in the DT.\n"); 73662306a36Sopenharmony_ci return ret; 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_cistruct cpts *cpts_create(struct device *dev, void __iomem *regs, 74062306a36Sopenharmony_ci struct device_node *node, u32 n_ext_ts) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci struct cpts *cpts; 74362306a36Sopenharmony_ci int ret; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci cpts = devm_kzalloc(dev, sizeof(*cpts), GFP_KERNEL); 74662306a36Sopenharmony_ci if (!cpts) 74762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci cpts->dev = dev; 75062306a36Sopenharmony_ci cpts->reg = (struct cpsw_cpts __iomem *)regs; 75162306a36Sopenharmony_ci cpts->irq_poll = true; 75262306a36Sopenharmony_ci spin_lock_init(&cpts->lock); 75362306a36Sopenharmony_ci mutex_init(&cpts->ptp_clk_mutex); 75462306a36Sopenharmony_ci init_completion(&cpts->ts_push_complete); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci ret = cpts_of_parse(cpts, node); 75762306a36Sopenharmony_ci if (ret) 75862306a36Sopenharmony_ci return ERR_PTR(ret); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci cpts->refclk = devm_get_clk_from_child(dev, node, "cpts"); 76162306a36Sopenharmony_ci if (IS_ERR(cpts->refclk)) 76262306a36Sopenharmony_ci /* try get clk from dev node for compatibility */ 76362306a36Sopenharmony_ci cpts->refclk = devm_clk_get(dev, "cpts"); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci if (IS_ERR(cpts->refclk)) { 76662306a36Sopenharmony_ci dev_err(dev, "Failed to get cpts refclk %ld\n", 76762306a36Sopenharmony_ci PTR_ERR(cpts->refclk)); 76862306a36Sopenharmony_ci return ERR_CAST(cpts->refclk); 76962306a36Sopenharmony_ci } 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci ret = clk_prepare(cpts->refclk); 77262306a36Sopenharmony_ci if (ret) 77362306a36Sopenharmony_ci return ERR_PTR(ret); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci cpts->cc.read = cpts_systim_read; 77662306a36Sopenharmony_ci cpts->cc.mask = CLOCKSOURCE_MASK(32); 77762306a36Sopenharmony_ci cpts->info = cpts_info; 77862306a36Sopenharmony_ci cpts->phc_index = -1; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (n_ext_ts) 78162306a36Sopenharmony_ci cpts->info.n_ext_ts = n_ext_ts; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci cpts_calc_mult_shift(cpts); 78462306a36Sopenharmony_ci /* save cc.mult original value as it can be modified 78562306a36Sopenharmony_ci * by cpts_ptp_adjfine(). 78662306a36Sopenharmony_ci */ 78762306a36Sopenharmony_ci cpts->cc_mult = cpts->cc.mult; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci return cpts; 79062306a36Sopenharmony_ci} 79162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cpts_create); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_civoid cpts_release(struct cpts *cpts) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci if (!cpts) 79662306a36Sopenharmony_ci return; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci if (WARN_ON(!cpts->refclk)) 79962306a36Sopenharmony_ci return; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci clk_unprepare(cpts->refclk); 80262306a36Sopenharmony_ci} 80362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cpts_release); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 80662306a36Sopenharmony_ciMODULE_DESCRIPTION("TI CPTS driver"); 80762306a36Sopenharmony_ciMODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>"); 808