187c0e764SRichard Cochran /* 287c0e764SRichard Cochran * TI Common Platform Time Sync 387c0e764SRichard Cochran * 487c0e764SRichard Cochran * Copyright (C) 2012 Richard Cochran <richardcochran@gmail.com> 587c0e764SRichard Cochran * 687c0e764SRichard Cochran * This program is free software; you can redistribute it and/or modify 787c0e764SRichard Cochran * it under the terms of the GNU General Public License as published by 887c0e764SRichard Cochran * the Free Software Foundation; either version 2 of the License, or 987c0e764SRichard Cochran * (at your option) any later version. 1087c0e764SRichard Cochran * 1187c0e764SRichard Cochran * This program is distributed in the hope that it will be useful, 1287c0e764SRichard Cochran * but WITHOUT ANY WARRANTY; without even the implied warranty of 1387c0e764SRichard Cochran * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1487c0e764SRichard Cochran * GNU General Public License for more details. 1587c0e764SRichard Cochran * 1687c0e764SRichard Cochran * You should have received a copy of the GNU General Public License 1787c0e764SRichard Cochran * along with this program; if not, write to the Free Software 1887c0e764SRichard Cochran * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 1987c0e764SRichard Cochran */ 2087c0e764SRichard Cochran #include <linux/err.h> 2187c0e764SRichard Cochran #include <linux/if.h> 2287c0e764SRichard Cochran #include <linux/hrtimer.h> 2387c0e764SRichard Cochran #include <linux/module.h> 2487c0e764SRichard Cochran #include <linux/net_tstamp.h> 2587c0e764SRichard Cochran #include <linux/ptp_classify.h> 2687c0e764SRichard Cochran #include <linux/time.h> 2787c0e764SRichard Cochran #include <linux/uaccess.h> 2887c0e764SRichard Cochran #include <linux/workqueue.h> 2979eb9d28SAlexei Starovoitov #include <linux/if_ether.h> 3079eb9d28SAlexei Starovoitov #include <linux/if_vlan.h> 3187c0e764SRichard Cochran 3287c0e764SRichard Cochran #include "cpts.h" 3387c0e764SRichard Cochran 3487c0e764SRichard Cochran #ifdef CONFIG_TI_CPTS 3587c0e764SRichard Cochran 3687c0e764SRichard Cochran #define cpts_read32(c, r) __raw_readl(&c->reg->r) 3787c0e764SRichard Cochran #define cpts_write32(c, v, r) __raw_writel(v, &c->reg->r) 3887c0e764SRichard Cochran 3987c0e764SRichard Cochran static int event_expired(struct cpts_event *event) 4087c0e764SRichard Cochran { 4187c0e764SRichard Cochran return time_after(jiffies, event->tmo); 4287c0e764SRichard Cochran } 4387c0e764SRichard Cochran 4487c0e764SRichard Cochran static int event_type(struct cpts_event *event) 4587c0e764SRichard Cochran { 4687c0e764SRichard Cochran return (event->high >> EVENT_TYPE_SHIFT) & EVENT_TYPE_MASK; 4787c0e764SRichard Cochran } 4887c0e764SRichard Cochran 4987c0e764SRichard Cochran static int cpts_fifo_pop(struct cpts *cpts, u32 *high, u32 *low) 5087c0e764SRichard Cochran { 5187c0e764SRichard Cochran u32 r = cpts_read32(cpts, intstat_raw); 5287c0e764SRichard Cochran 5387c0e764SRichard Cochran if (r & TS_PEND_RAW) { 5487c0e764SRichard Cochran *high = cpts_read32(cpts, event_high); 5587c0e764SRichard Cochran *low = cpts_read32(cpts, event_low); 5687c0e764SRichard Cochran cpts_write32(cpts, EVENT_POP, event_pop); 5787c0e764SRichard Cochran return 0; 5887c0e764SRichard Cochran } 5987c0e764SRichard Cochran return -1; 6087c0e764SRichard Cochran } 6187c0e764SRichard Cochran 6287c0e764SRichard Cochran /* 6387c0e764SRichard Cochran * Returns zero if matching event type was found. 6487c0e764SRichard Cochran */ 6587c0e764SRichard Cochran static int cpts_fifo_read(struct cpts *cpts, int match) 6687c0e764SRichard Cochran { 6787c0e764SRichard Cochran int i, type = -1; 6887c0e764SRichard Cochran u32 hi, lo; 6987c0e764SRichard Cochran struct cpts_event *event; 7087c0e764SRichard Cochran 7187c0e764SRichard Cochran for (i = 0; i < CPTS_FIFO_DEPTH; i++) { 7287c0e764SRichard Cochran if (cpts_fifo_pop(cpts, &hi, &lo)) 7387c0e764SRichard Cochran break; 7487c0e764SRichard Cochran if (list_empty(&cpts->pool)) { 7587c0e764SRichard Cochran pr_err("cpts: event pool is empty\n"); 7687c0e764SRichard Cochran return -1; 7787c0e764SRichard Cochran } 7887c0e764SRichard Cochran event = list_first_entry(&cpts->pool, struct cpts_event, list); 7987c0e764SRichard Cochran event->tmo = jiffies + 2; 8087c0e764SRichard Cochran event->high = hi; 8187c0e764SRichard Cochran event->low = lo; 8287c0e764SRichard Cochran type = event_type(event); 8387c0e764SRichard Cochran switch (type) { 8487c0e764SRichard Cochran case CPTS_EV_PUSH: 8587c0e764SRichard Cochran case CPTS_EV_RX: 8687c0e764SRichard Cochran case CPTS_EV_TX: 8787c0e764SRichard Cochran list_del_init(&event->list); 8887c0e764SRichard Cochran list_add_tail(&event->list, &cpts->events); 8987c0e764SRichard Cochran break; 9087c0e764SRichard Cochran case CPTS_EV_ROLL: 9187c0e764SRichard Cochran case CPTS_EV_HALF: 9287c0e764SRichard Cochran case CPTS_EV_HW: 9387c0e764SRichard Cochran break; 9487c0e764SRichard Cochran default: 9507f42258SMasanari Iida pr_err("cpts: unknown event type\n"); 9687c0e764SRichard Cochran break; 9787c0e764SRichard Cochran } 9887c0e764SRichard Cochran if (type == match) 9987c0e764SRichard Cochran break; 10087c0e764SRichard Cochran } 10187c0e764SRichard Cochran return type == match ? 0 : -1; 10287c0e764SRichard Cochran } 10387c0e764SRichard Cochran 10487c0e764SRichard Cochran static cycle_t cpts_systim_read(const struct cyclecounter *cc) 10587c0e764SRichard Cochran { 10687c0e764SRichard Cochran u64 val = 0; 10787c0e764SRichard Cochran struct cpts_event *event; 10887c0e764SRichard Cochran struct list_head *this, *next; 10987c0e764SRichard Cochran struct cpts *cpts = container_of(cc, struct cpts, cc); 11087c0e764SRichard Cochran 11187c0e764SRichard Cochran cpts_write32(cpts, TS_PUSH, ts_push); 11287c0e764SRichard Cochran if (cpts_fifo_read(cpts, CPTS_EV_PUSH)) 11387c0e764SRichard Cochran pr_err("cpts: unable to obtain a time stamp\n"); 11487c0e764SRichard Cochran 11587c0e764SRichard Cochran list_for_each_safe(this, next, &cpts->events) { 11687c0e764SRichard Cochran event = list_entry(this, struct cpts_event, list); 11787c0e764SRichard Cochran if (event_type(event) == CPTS_EV_PUSH) { 11887c0e764SRichard Cochran list_del_init(&event->list); 11987c0e764SRichard Cochran list_add(&event->list, &cpts->pool); 12087c0e764SRichard Cochran val = event->low; 12187c0e764SRichard Cochran break; 12287c0e764SRichard Cochran } 12387c0e764SRichard Cochran } 12487c0e764SRichard Cochran 12587c0e764SRichard Cochran return val; 12687c0e764SRichard Cochran } 12787c0e764SRichard Cochran 12887c0e764SRichard Cochran /* PTP clock operations */ 12987c0e764SRichard Cochran 13087c0e764SRichard Cochran static int cpts_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) 13187c0e764SRichard Cochran { 13287c0e764SRichard Cochran u64 adj; 13387c0e764SRichard Cochran u32 diff, mult; 13487c0e764SRichard Cochran int neg_adj = 0; 13587c0e764SRichard Cochran unsigned long flags; 13687c0e764SRichard Cochran struct cpts *cpts = container_of(ptp, struct cpts, info); 13787c0e764SRichard Cochran 13887c0e764SRichard Cochran if (ppb < 0) { 13987c0e764SRichard Cochran neg_adj = 1; 14087c0e764SRichard Cochran ppb = -ppb; 14187c0e764SRichard Cochran } 14287c0e764SRichard Cochran mult = cpts->cc_mult; 14387c0e764SRichard Cochran adj = mult; 14487c0e764SRichard Cochran adj *= ppb; 14587c0e764SRichard Cochran diff = div_u64(adj, 1000000000ULL); 14687c0e764SRichard Cochran 14787c0e764SRichard Cochran spin_lock_irqsave(&cpts->lock, flags); 14887c0e764SRichard Cochran 14987c0e764SRichard Cochran timecounter_read(&cpts->tc); 15087c0e764SRichard Cochran 15187c0e764SRichard Cochran cpts->cc.mult = neg_adj ? mult - diff : mult + diff; 15287c0e764SRichard Cochran 15387c0e764SRichard Cochran spin_unlock_irqrestore(&cpts->lock, flags); 15487c0e764SRichard Cochran 15587c0e764SRichard Cochran return 0; 15687c0e764SRichard Cochran } 15787c0e764SRichard Cochran 15887c0e764SRichard Cochran static int cpts_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) 15987c0e764SRichard Cochran { 16087c0e764SRichard Cochran s64 now; 16187c0e764SRichard Cochran unsigned long flags; 16287c0e764SRichard Cochran struct cpts *cpts = container_of(ptp, struct cpts, info); 16387c0e764SRichard Cochran 16487c0e764SRichard Cochran spin_lock_irqsave(&cpts->lock, flags); 16587c0e764SRichard Cochran now = timecounter_read(&cpts->tc); 16687c0e764SRichard Cochran now += delta; 16787c0e764SRichard Cochran timecounter_init(&cpts->tc, &cpts->cc, now); 16887c0e764SRichard Cochran spin_unlock_irqrestore(&cpts->lock, flags); 16987c0e764SRichard Cochran 17087c0e764SRichard Cochran return 0; 17187c0e764SRichard Cochran } 17287c0e764SRichard Cochran 17387c0e764SRichard Cochran static int cpts_ptp_gettime(struct ptp_clock_info *ptp, struct timespec *ts) 17487c0e764SRichard Cochran { 17587c0e764SRichard Cochran u64 ns; 17687c0e764SRichard Cochran u32 remainder; 17787c0e764SRichard Cochran unsigned long flags; 17887c0e764SRichard Cochran struct cpts *cpts = container_of(ptp, struct cpts, info); 17987c0e764SRichard Cochran 18087c0e764SRichard Cochran spin_lock_irqsave(&cpts->lock, flags); 18187c0e764SRichard Cochran ns = timecounter_read(&cpts->tc); 18287c0e764SRichard Cochran spin_unlock_irqrestore(&cpts->lock, flags); 18387c0e764SRichard Cochran 18487c0e764SRichard Cochran ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder); 18587c0e764SRichard Cochran ts->tv_nsec = remainder; 18687c0e764SRichard Cochran 18787c0e764SRichard Cochran return 0; 18887c0e764SRichard Cochran } 18987c0e764SRichard Cochran 19087c0e764SRichard Cochran static int cpts_ptp_settime(struct ptp_clock_info *ptp, 19187c0e764SRichard Cochran const struct timespec *ts) 19287c0e764SRichard Cochran { 19387c0e764SRichard Cochran u64 ns; 19487c0e764SRichard Cochran unsigned long flags; 19587c0e764SRichard Cochran struct cpts *cpts = container_of(ptp, struct cpts, info); 19687c0e764SRichard Cochran 19787c0e764SRichard Cochran ns = ts->tv_sec * 1000000000ULL; 19887c0e764SRichard Cochran ns += ts->tv_nsec; 19987c0e764SRichard Cochran 20087c0e764SRichard Cochran spin_lock_irqsave(&cpts->lock, flags); 20187c0e764SRichard Cochran timecounter_init(&cpts->tc, &cpts->cc, ns); 20287c0e764SRichard Cochran spin_unlock_irqrestore(&cpts->lock, flags); 20387c0e764SRichard Cochran 20487c0e764SRichard Cochran return 0; 20587c0e764SRichard Cochran } 20687c0e764SRichard Cochran 20787c0e764SRichard Cochran static int cpts_ptp_enable(struct ptp_clock_info *ptp, 20887c0e764SRichard Cochran struct ptp_clock_request *rq, int on) 20987c0e764SRichard Cochran { 21087c0e764SRichard Cochran return -EOPNOTSUPP; 21187c0e764SRichard Cochran } 21287c0e764SRichard Cochran 21387c0e764SRichard Cochran static struct ptp_clock_info cpts_info = { 21487c0e764SRichard Cochran .owner = THIS_MODULE, 21587c0e764SRichard Cochran .name = "CTPS timer", 21687c0e764SRichard Cochran .max_adj = 1000000, 21787c0e764SRichard Cochran .n_ext_ts = 0, 2184986b4f0SRichard Cochran .n_pins = 0, 21987c0e764SRichard Cochran .pps = 0, 22087c0e764SRichard Cochran .adjfreq = cpts_ptp_adjfreq, 22187c0e764SRichard Cochran .adjtime = cpts_ptp_adjtime, 22287c0e764SRichard Cochran .gettime = cpts_ptp_gettime, 22387c0e764SRichard Cochran .settime = cpts_ptp_settime, 22487c0e764SRichard Cochran .enable = cpts_ptp_enable, 22587c0e764SRichard Cochran }; 22687c0e764SRichard Cochran 22787c0e764SRichard Cochran static void cpts_overflow_check(struct work_struct *work) 22887c0e764SRichard Cochran { 22987c0e764SRichard Cochran struct timespec ts; 23087c0e764SRichard Cochran struct cpts *cpts = container_of(work, struct cpts, overflow_work.work); 23187c0e764SRichard Cochran 23287c0e764SRichard Cochran cpts_write32(cpts, CPTS_EN, control); 23387c0e764SRichard Cochran cpts_write32(cpts, TS_PEND_EN, int_enable); 23487c0e764SRichard Cochran cpts_ptp_gettime(&cpts->info, &ts); 23587c0e764SRichard Cochran pr_debug("cpts overflow check at %ld.%09lu\n", ts.tv_sec, ts.tv_nsec); 23687c0e764SRichard Cochran schedule_delayed_work(&cpts->overflow_work, CPTS_OVERFLOW_PERIOD); 23787c0e764SRichard Cochran } 23887c0e764SRichard Cochran 239d0415e7cSGeorge Cherian static void cpts_clk_init(struct device *dev, struct cpts *cpts) 24087c0e764SRichard Cochran { 241d0415e7cSGeorge Cherian cpts->refclk = devm_clk_get(dev, "cpts"); 24287c0e764SRichard Cochran if (IS_ERR(cpts->refclk)) { 243d0415e7cSGeorge Cherian dev_err(dev, "Failed to get cpts refclk\n"); 24487c0e764SRichard Cochran cpts->refclk = NULL; 24587c0e764SRichard Cochran return; 24687c0e764SRichard Cochran } 247ccb6e984SRichard Cochran clk_prepare_enable(cpts->refclk); 24887c0e764SRichard Cochran } 24987c0e764SRichard Cochran 25087c0e764SRichard Cochran static void cpts_clk_release(struct cpts *cpts) 25187c0e764SRichard Cochran { 25287c0e764SRichard Cochran clk_disable(cpts->refclk); 25387c0e764SRichard Cochran } 25487c0e764SRichard Cochran 25587c0e764SRichard Cochran static int cpts_match(struct sk_buff *skb, unsigned int ptp_class, 25687c0e764SRichard Cochran u16 ts_seqid, u8 ts_msgtype) 25787c0e764SRichard Cochran { 25887c0e764SRichard Cochran u16 *seqid; 259*ae5c6c6dSStefan Sørensen unsigned int offset = 0; 26087c0e764SRichard Cochran u8 *msgtype, *data = skb->data; 26187c0e764SRichard Cochran 262*ae5c6c6dSStefan Sørensen if (ptp_class & PTP_CLASS_VLAN) 263*ae5c6c6dSStefan Sørensen offset += VLAN_HLEN; 264*ae5c6c6dSStefan Sørensen 265*ae5c6c6dSStefan Sørensen switch (ptp_class & PTP_CLASS_PMASK) { 266*ae5c6c6dSStefan Sørensen case PTP_CLASS_IPV4: 267*ae5c6c6dSStefan Sørensen offset += ETH_HLEN + IPV4_HLEN(data) + UDP_HLEN; 26887c0e764SRichard Cochran break; 269*ae5c6c6dSStefan Sørensen case PTP_CLASS_IPV6: 270*ae5c6c6dSStefan Sørensen offset += ETH_HLEN + IP6_HLEN + UDP_HLEN; 27187c0e764SRichard Cochran break; 272*ae5c6c6dSStefan Sørensen case PTP_CLASS_L2: 273*ae5c6c6dSStefan Sørensen offset += ETH_HLEN; 27487c0e764SRichard Cochran break; 27587c0e764SRichard Cochran default: 27687c0e764SRichard Cochran return 0; 27787c0e764SRichard Cochran } 27887c0e764SRichard Cochran 27987c0e764SRichard Cochran if (skb->len + ETH_HLEN < offset + OFF_PTP_SEQUENCE_ID + sizeof(*seqid)) 28087c0e764SRichard Cochran return 0; 28187c0e764SRichard Cochran 28287c0e764SRichard Cochran if (unlikely(ptp_class & PTP_CLASS_V1)) 28387c0e764SRichard Cochran msgtype = data + offset + OFF_PTP_CONTROL; 28487c0e764SRichard Cochran else 28587c0e764SRichard Cochran msgtype = data + offset; 28687c0e764SRichard Cochran 28787c0e764SRichard Cochran seqid = (u16 *)(data + offset + OFF_PTP_SEQUENCE_ID); 28887c0e764SRichard Cochran 28987c0e764SRichard Cochran return (ts_msgtype == (*msgtype & 0xf) && ts_seqid == ntohs(*seqid)); 29087c0e764SRichard Cochran } 29187c0e764SRichard Cochran 29287c0e764SRichard Cochran static u64 cpts_find_ts(struct cpts *cpts, struct sk_buff *skb, int ev_type) 29387c0e764SRichard Cochran { 29487c0e764SRichard Cochran u64 ns = 0; 29587c0e764SRichard Cochran struct cpts_event *event; 29687c0e764SRichard Cochran struct list_head *this, *next; 297164d8c66SDaniel Borkmann unsigned int class = ptp_classify_raw(skb); 29887c0e764SRichard Cochran unsigned long flags; 29987c0e764SRichard Cochran u16 seqid; 30087c0e764SRichard Cochran u8 mtype; 30187c0e764SRichard Cochran 30287c0e764SRichard Cochran if (class == PTP_CLASS_NONE) 30387c0e764SRichard Cochran return 0; 30487c0e764SRichard Cochran 30587c0e764SRichard Cochran spin_lock_irqsave(&cpts->lock, flags); 30687c0e764SRichard Cochran cpts_fifo_read(cpts, CPTS_EV_PUSH); 30787c0e764SRichard Cochran list_for_each_safe(this, next, &cpts->events) { 30887c0e764SRichard Cochran event = list_entry(this, struct cpts_event, list); 30987c0e764SRichard Cochran if (event_expired(event)) { 31087c0e764SRichard Cochran list_del_init(&event->list); 31187c0e764SRichard Cochran list_add(&event->list, &cpts->pool); 31287c0e764SRichard Cochran continue; 31387c0e764SRichard Cochran } 31487c0e764SRichard Cochran mtype = (event->high >> MESSAGE_TYPE_SHIFT) & MESSAGE_TYPE_MASK; 31587c0e764SRichard Cochran seqid = (event->high >> SEQUENCE_ID_SHIFT) & SEQUENCE_ID_MASK; 31687c0e764SRichard Cochran if (ev_type == event_type(event) && 31787c0e764SRichard Cochran cpts_match(skb, class, seqid, mtype)) { 31887c0e764SRichard Cochran ns = timecounter_cyc2time(&cpts->tc, event->low); 31987c0e764SRichard Cochran list_del_init(&event->list); 32087c0e764SRichard Cochran list_add(&event->list, &cpts->pool); 32187c0e764SRichard Cochran break; 32287c0e764SRichard Cochran } 32387c0e764SRichard Cochran } 32487c0e764SRichard Cochran spin_unlock_irqrestore(&cpts->lock, flags); 32587c0e764SRichard Cochran 32687c0e764SRichard Cochran return ns; 32787c0e764SRichard Cochran } 32887c0e764SRichard Cochran 32987c0e764SRichard Cochran void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb) 33087c0e764SRichard Cochran { 33187c0e764SRichard Cochran u64 ns; 33287c0e764SRichard Cochran struct skb_shared_hwtstamps *ssh; 33387c0e764SRichard Cochran 33487c0e764SRichard Cochran if (!cpts->rx_enable) 33587c0e764SRichard Cochran return; 33687c0e764SRichard Cochran ns = cpts_find_ts(cpts, skb, CPTS_EV_RX); 33787c0e764SRichard Cochran if (!ns) 33887c0e764SRichard Cochran return; 33987c0e764SRichard Cochran ssh = skb_hwtstamps(skb); 34087c0e764SRichard Cochran memset(ssh, 0, sizeof(*ssh)); 34187c0e764SRichard Cochran ssh->hwtstamp = ns_to_ktime(ns); 34287c0e764SRichard Cochran } 34387c0e764SRichard Cochran 34487c0e764SRichard Cochran void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb) 34587c0e764SRichard Cochran { 34687c0e764SRichard Cochran u64 ns; 34787c0e764SRichard Cochran struct skb_shared_hwtstamps ssh; 34887c0e764SRichard Cochran 34987c0e764SRichard Cochran if (!(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) 35087c0e764SRichard Cochran return; 35187c0e764SRichard Cochran ns = cpts_find_ts(cpts, skb, CPTS_EV_TX); 35287c0e764SRichard Cochran if (!ns) 35387c0e764SRichard Cochran return; 35487c0e764SRichard Cochran memset(&ssh, 0, sizeof(ssh)); 35587c0e764SRichard Cochran ssh.hwtstamp = ns_to_ktime(ns); 35687c0e764SRichard Cochran skb_tstamp_tx(skb, &ssh); 35787c0e764SRichard Cochran } 35887c0e764SRichard Cochran 35987c0e764SRichard Cochran #endif /*CONFIG_TI_CPTS*/ 36087c0e764SRichard Cochran 36187c0e764SRichard Cochran int cpts_register(struct device *dev, struct cpts *cpts, 36287c0e764SRichard Cochran u32 mult, u32 shift) 36387c0e764SRichard Cochran { 36487c0e764SRichard Cochran #ifdef CONFIG_TI_CPTS 36587c0e764SRichard Cochran int err, i; 36687c0e764SRichard Cochran unsigned long flags; 36787c0e764SRichard Cochran 36887c0e764SRichard Cochran cpts->info = cpts_info; 36987c0e764SRichard Cochran cpts->clock = ptp_clock_register(&cpts->info, dev); 37087c0e764SRichard Cochran if (IS_ERR(cpts->clock)) { 37187c0e764SRichard Cochran err = PTR_ERR(cpts->clock); 37287c0e764SRichard Cochran cpts->clock = NULL; 37387c0e764SRichard Cochran return err; 37487c0e764SRichard Cochran } 37587c0e764SRichard Cochran spin_lock_init(&cpts->lock); 37687c0e764SRichard Cochran 37787c0e764SRichard Cochran cpts->cc.read = cpts_systim_read; 37887c0e764SRichard Cochran cpts->cc.mask = CLOCKSOURCE_MASK(32); 37987c0e764SRichard Cochran cpts->cc_mult = mult; 38087c0e764SRichard Cochran cpts->cc.mult = mult; 38187c0e764SRichard Cochran cpts->cc.shift = shift; 38287c0e764SRichard Cochran 38387c0e764SRichard Cochran INIT_LIST_HEAD(&cpts->events); 38487c0e764SRichard Cochran INIT_LIST_HEAD(&cpts->pool); 38587c0e764SRichard Cochran for (i = 0; i < CPTS_MAX_EVENTS; i++) 38687c0e764SRichard Cochran list_add(&cpts->pool_data[i].list, &cpts->pool); 38787c0e764SRichard Cochran 388d0415e7cSGeorge Cherian cpts_clk_init(dev, cpts); 38987c0e764SRichard Cochran cpts_write32(cpts, CPTS_EN, control); 39087c0e764SRichard Cochran cpts_write32(cpts, TS_PEND_EN, int_enable); 39187c0e764SRichard Cochran 39287c0e764SRichard Cochran spin_lock_irqsave(&cpts->lock, flags); 39387c0e764SRichard Cochran timecounter_init(&cpts->tc, &cpts->cc, ktime_to_ns(ktime_get_real())); 39487c0e764SRichard Cochran spin_unlock_irqrestore(&cpts->lock, flags); 39587c0e764SRichard Cochran 39687c0e764SRichard Cochran INIT_DELAYED_WORK(&cpts->overflow_work, cpts_overflow_check); 39787c0e764SRichard Cochran schedule_delayed_work(&cpts->overflow_work, CPTS_OVERFLOW_PERIOD); 39887c0e764SRichard Cochran 39987c0e764SRichard Cochran cpts->phc_index = ptp_clock_index(cpts->clock); 40087c0e764SRichard Cochran #endif 40187c0e764SRichard Cochran return 0; 40287c0e764SRichard Cochran } 40387c0e764SRichard Cochran 40487c0e764SRichard Cochran void cpts_unregister(struct cpts *cpts) 40587c0e764SRichard Cochran { 40687c0e764SRichard Cochran #ifdef CONFIG_TI_CPTS 40787c0e764SRichard Cochran if (cpts->clock) { 40887c0e764SRichard Cochran ptp_clock_unregister(cpts->clock); 40987c0e764SRichard Cochran cancel_delayed_work_sync(&cpts->overflow_work); 41087c0e764SRichard Cochran } 41187c0e764SRichard Cochran if (cpts->refclk) 41287c0e764SRichard Cochran cpts_clk_release(cpts); 41387c0e764SRichard Cochran #endif 41487c0e764SRichard Cochran } 415