linux/drivers/net/ethernet/ti/cpts.c
<<
>>
Prefs
   1/*
   2 * TI Common Platform Time Sync
   3 *
   4 * Copyright (C) 2012 Richard Cochran <richardcochran@gmail.com>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * along with this program; if not, write to the Free Software
  18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  19 */
  20#include <linux/err.h>
  21#include <linux/if.h>
  22#include <linux/hrtimer.h>
  23#include <linux/module.h>
  24#include <linux/net_tstamp.h>
  25#include <linux/ptp_classify.h>
  26#include <linux/time.h>
  27#include <linux/uaccess.h>
  28#include <linux/workqueue.h>
  29
  30#include "cpts.h"
  31
  32#ifdef CONFIG_TI_CPTS
  33
  34static struct sock_filter ptp_filter[] = {
  35        PTP_FILTER
  36};
  37
  38#define cpts_read32(c, r)       __raw_readl(&c->reg->r)
  39#define cpts_write32(c, v, r)   __raw_writel(v, &c->reg->r)
  40
  41static int event_expired(struct cpts_event *event)
  42{
  43        return time_after(jiffies, event->tmo);
  44}
  45
  46static int event_type(struct cpts_event *event)
  47{
  48        return (event->high >> EVENT_TYPE_SHIFT) & EVENT_TYPE_MASK;
  49}
  50
  51static int cpts_fifo_pop(struct cpts *cpts, u32 *high, u32 *low)
  52{
  53        u32 r = cpts_read32(cpts, intstat_raw);
  54
  55        if (r & TS_PEND_RAW) {
  56                *high = cpts_read32(cpts, event_high);
  57                *low  = cpts_read32(cpts, event_low);
  58                cpts_write32(cpts, EVENT_POP, event_pop);
  59                return 0;
  60        }
  61        return -1;
  62}
  63
  64/*
  65 * Returns zero if matching event type was found.
  66 */
  67static int cpts_fifo_read(struct cpts *cpts, int match)
  68{
  69        int i, type = -1;
  70        u32 hi, lo;
  71        struct cpts_event *event;
  72
  73        for (i = 0; i < CPTS_FIFO_DEPTH; i++) {
  74                if (cpts_fifo_pop(cpts, &hi, &lo))
  75                        break;
  76                if (list_empty(&cpts->pool)) {
  77                        pr_err("cpts: event pool is empty\n");
  78                        return -1;
  79                }
  80                event = list_first_entry(&cpts->pool, struct cpts_event, list);
  81                event->tmo = jiffies + 2;
  82                event->high = hi;
  83                event->low = lo;
  84                type = event_type(event);
  85                switch (type) {
  86                case CPTS_EV_PUSH:
  87                case CPTS_EV_RX:
  88                case CPTS_EV_TX:
  89                        list_del_init(&event->list);
  90                        list_add_tail(&event->list, &cpts->events);
  91                        break;
  92                case CPTS_EV_ROLL:
  93                case CPTS_EV_HALF:
  94                case CPTS_EV_HW:
  95                        break;
  96                default:
  97                        pr_err("cpts: unknown event type\n");
  98                        break;
  99                }
 100                if (type == match)
 101                        break;
 102        }
 103        return type == match ? 0 : -1;
 104}
 105
 106static cycle_t cpts_systim_read(const struct cyclecounter *cc)
 107{
 108        u64 val = 0;
 109        struct cpts_event *event;
 110        struct list_head *this, *next;
 111        struct cpts *cpts = container_of(cc, struct cpts, cc);
 112
 113        cpts_write32(cpts, TS_PUSH, ts_push);
 114        if (cpts_fifo_read(cpts, CPTS_EV_PUSH))
 115                pr_err("cpts: unable to obtain a time stamp\n");
 116
 117        list_for_each_safe(this, next, &cpts->events) {
 118                event = list_entry(this, struct cpts_event, list);
 119                if (event_type(event) == CPTS_EV_PUSH) {
 120                        list_del_init(&event->list);
 121                        list_add(&event->list, &cpts->pool);
 122                        val = event->low;
 123                        break;
 124                }
 125        }
 126
 127        return val;
 128}
 129
 130/* PTP clock operations */
 131
 132static int cpts_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
 133{
 134        u64 adj;
 135        u32 diff, mult;
 136        int neg_adj = 0;
 137        unsigned long flags;
 138        struct cpts *cpts = container_of(ptp, struct cpts, info);
 139
 140        if (ppb < 0) {
 141                neg_adj = 1;
 142                ppb = -ppb;
 143        }
 144        mult = cpts->cc_mult;
 145        adj = mult;
 146        adj *= ppb;
 147        diff = div_u64(adj, 1000000000ULL);
 148
 149        spin_lock_irqsave(&cpts->lock, flags);
 150
 151        timecounter_read(&cpts->tc);
 152
 153        cpts->cc.mult = neg_adj ? mult - diff : mult + diff;
 154
 155        spin_unlock_irqrestore(&cpts->lock, flags);
 156
 157        return 0;
 158}
 159
 160static int cpts_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
 161{
 162        s64 now;
 163        unsigned long flags;
 164        struct cpts *cpts = container_of(ptp, struct cpts, info);
 165
 166        spin_lock_irqsave(&cpts->lock, flags);
 167        now = timecounter_read(&cpts->tc);
 168        now += delta;
 169        timecounter_init(&cpts->tc, &cpts->cc, now);
 170        spin_unlock_irqrestore(&cpts->lock, flags);
 171
 172        return 0;
 173}
 174
 175static int cpts_ptp_gettime(struct ptp_clock_info *ptp, struct timespec *ts)
 176{
 177        u64 ns;
 178        u32 remainder;
 179        unsigned long flags;
 180        struct cpts *cpts = container_of(ptp, struct cpts, info);
 181
 182        spin_lock_irqsave(&cpts->lock, flags);
 183        ns = timecounter_read(&cpts->tc);
 184        spin_unlock_irqrestore(&cpts->lock, flags);
 185
 186        ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder);
 187        ts->tv_nsec = remainder;
 188
 189        return 0;
 190}
 191
 192static int cpts_ptp_settime(struct ptp_clock_info *ptp,
 193                            const struct timespec *ts)
 194{
 195        u64 ns;
 196        unsigned long flags;
 197        struct cpts *cpts = container_of(ptp, struct cpts, info);
 198
 199        ns = ts->tv_sec * 1000000000ULL;
 200        ns += ts->tv_nsec;
 201
 202        spin_lock_irqsave(&cpts->lock, flags);
 203        timecounter_init(&cpts->tc, &cpts->cc, ns);
 204        spin_unlock_irqrestore(&cpts->lock, flags);
 205
 206        return 0;
 207}
 208
 209static int cpts_ptp_enable(struct ptp_clock_info *ptp,
 210                           struct ptp_clock_request *rq, int on)
 211{
 212        return -EOPNOTSUPP;
 213}
 214
 215static struct ptp_clock_info cpts_info = {
 216        .owner          = THIS_MODULE,
 217        .name           = "CTPS timer",
 218        .max_adj        = 1000000,
 219        .n_ext_ts       = 0,
 220        .pps            = 0,
 221        .adjfreq        = cpts_ptp_adjfreq,
 222        .adjtime        = cpts_ptp_adjtime,
 223        .gettime        = cpts_ptp_gettime,
 224        .settime        = cpts_ptp_settime,
 225        .enable         = cpts_ptp_enable,
 226};
 227
 228static void cpts_overflow_check(struct work_struct *work)
 229{
 230        struct timespec ts;
 231        struct cpts *cpts = container_of(work, struct cpts, overflow_work.work);
 232
 233        cpts_write32(cpts, CPTS_EN, control);
 234        cpts_write32(cpts, TS_PEND_EN, int_enable);
 235        cpts_ptp_gettime(&cpts->info, &ts);
 236        pr_debug("cpts overflow check at %ld.%09lu\n", ts.tv_sec, ts.tv_nsec);
 237        schedule_delayed_work(&cpts->overflow_work, CPTS_OVERFLOW_PERIOD);
 238}
 239
 240#define CPTS_REF_CLOCK_NAME "cpsw_cpts_rft_clk"
 241
 242static void cpts_clk_init(struct cpts *cpts)
 243{
 244        cpts->refclk = clk_get(NULL, CPTS_REF_CLOCK_NAME);
 245        if (IS_ERR(cpts->refclk)) {
 246                pr_err("Failed to clk_get %s\n", CPTS_REF_CLOCK_NAME);
 247                cpts->refclk = NULL;
 248                return;
 249        }
 250        clk_prepare_enable(cpts->refclk);
 251}
 252
 253static void cpts_clk_release(struct cpts *cpts)
 254{
 255        clk_disable(cpts->refclk);
 256        clk_put(cpts->refclk);
 257}
 258
 259static int cpts_match(struct sk_buff *skb, unsigned int ptp_class,
 260                      u16 ts_seqid, u8 ts_msgtype)
 261{
 262        u16 *seqid;
 263        unsigned int offset;
 264        u8 *msgtype, *data = skb->data;
 265
 266        switch (ptp_class) {
 267        case PTP_CLASS_V1_IPV4:
 268        case PTP_CLASS_V2_IPV4:
 269                offset = ETH_HLEN + IPV4_HLEN(data) + UDP_HLEN;
 270                break;
 271        case PTP_CLASS_V1_IPV6:
 272        case PTP_CLASS_V2_IPV6:
 273                offset = OFF_PTP6;
 274                break;
 275        case PTP_CLASS_V2_L2:
 276                offset = ETH_HLEN;
 277                break;
 278        case PTP_CLASS_V2_VLAN:
 279                offset = ETH_HLEN + VLAN_HLEN;
 280                break;
 281        default:
 282                return 0;
 283        }
 284
 285        if (skb->len + ETH_HLEN < offset + OFF_PTP_SEQUENCE_ID + sizeof(*seqid))
 286                return 0;
 287
 288        if (unlikely(ptp_class & PTP_CLASS_V1))
 289                msgtype = data + offset + OFF_PTP_CONTROL;
 290        else
 291                msgtype = data + offset;
 292
 293        seqid = (u16 *)(data + offset + OFF_PTP_SEQUENCE_ID);
 294
 295        return (ts_msgtype == (*msgtype & 0xf) && ts_seqid == ntohs(*seqid));
 296}
 297
 298static u64 cpts_find_ts(struct cpts *cpts, struct sk_buff *skb, int ev_type)
 299{
 300        u64 ns = 0;
 301        struct cpts_event *event;
 302        struct list_head *this, *next;
 303        unsigned int class = sk_run_filter(skb, ptp_filter);
 304        unsigned long flags;
 305        u16 seqid;
 306        u8 mtype;
 307
 308        if (class == PTP_CLASS_NONE)
 309                return 0;
 310
 311        spin_lock_irqsave(&cpts->lock, flags);
 312        cpts_fifo_read(cpts, CPTS_EV_PUSH);
 313        list_for_each_safe(this, next, &cpts->events) {
 314                event = list_entry(this, struct cpts_event, list);
 315                if (event_expired(event)) {
 316                        list_del_init(&event->list);
 317                        list_add(&event->list, &cpts->pool);
 318                        continue;
 319                }
 320                mtype = (event->high >> MESSAGE_TYPE_SHIFT) & MESSAGE_TYPE_MASK;
 321                seqid = (event->high >> SEQUENCE_ID_SHIFT) & SEQUENCE_ID_MASK;
 322                if (ev_type == event_type(event) &&
 323                    cpts_match(skb, class, seqid, mtype)) {
 324                        ns = timecounter_cyc2time(&cpts->tc, event->low);
 325                        list_del_init(&event->list);
 326                        list_add(&event->list, &cpts->pool);
 327                        break;
 328                }
 329        }
 330        spin_unlock_irqrestore(&cpts->lock, flags);
 331
 332        return ns;
 333}
 334
 335void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb)
 336{
 337        u64 ns;
 338        struct skb_shared_hwtstamps *ssh;
 339
 340        if (!cpts->rx_enable)
 341                return;
 342        ns = cpts_find_ts(cpts, skb, CPTS_EV_RX);
 343        if (!ns)
 344                return;
 345        ssh = skb_hwtstamps(skb);
 346        memset(ssh, 0, sizeof(*ssh));
 347        ssh->hwtstamp = ns_to_ktime(ns);
 348}
 349
 350void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb)
 351{
 352        u64 ns;
 353        struct skb_shared_hwtstamps ssh;
 354
 355        if (!(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS))
 356                return;
 357        ns = cpts_find_ts(cpts, skb, CPTS_EV_TX);
 358        if (!ns)
 359                return;
 360        memset(&ssh, 0, sizeof(ssh));
 361        ssh.hwtstamp = ns_to_ktime(ns);
 362        skb_tstamp_tx(skb, &ssh);
 363}
 364
 365#endif /*CONFIG_TI_CPTS*/
 366
 367int cpts_register(struct device *dev, struct cpts *cpts,
 368                  u32 mult, u32 shift)
 369{
 370#ifdef CONFIG_TI_CPTS
 371        int err, i;
 372        unsigned long flags;
 373
 374        if (ptp_filter_init(ptp_filter, ARRAY_SIZE(ptp_filter))) {
 375                pr_err("cpts: bad ptp filter\n");
 376                return -EINVAL;
 377        }
 378        cpts->info = cpts_info;
 379        cpts->clock = ptp_clock_register(&cpts->info, dev);
 380        if (IS_ERR(cpts->clock)) {
 381                err = PTR_ERR(cpts->clock);
 382                cpts->clock = NULL;
 383                return err;
 384        }
 385        spin_lock_init(&cpts->lock);
 386
 387        cpts->cc.read = cpts_systim_read;
 388        cpts->cc.mask = CLOCKSOURCE_MASK(32);
 389        cpts->cc_mult = mult;
 390        cpts->cc.mult = mult;
 391        cpts->cc.shift = shift;
 392
 393        INIT_LIST_HEAD(&cpts->events);
 394        INIT_LIST_HEAD(&cpts->pool);
 395        for (i = 0; i < CPTS_MAX_EVENTS; i++)
 396                list_add(&cpts->pool_data[i].list, &cpts->pool);
 397
 398        cpts_clk_init(cpts);
 399        cpts_write32(cpts, CPTS_EN, control);
 400        cpts_write32(cpts, TS_PEND_EN, int_enable);
 401
 402        spin_lock_irqsave(&cpts->lock, flags);
 403        timecounter_init(&cpts->tc, &cpts->cc, ktime_to_ns(ktime_get_real()));
 404        spin_unlock_irqrestore(&cpts->lock, flags);
 405
 406        INIT_DELAYED_WORK(&cpts->overflow_work, cpts_overflow_check);
 407        schedule_delayed_work(&cpts->overflow_work, CPTS_OVERFLOW_PERIOD);
 408
 409        cpts->phc_index = ptp_clock_index(cpts->clock);
 410#endif
 411        return 0;
 412}
 413
 414void cpts_unregister(struct cpts *cpts)
 415{
 416#ifdef CONFIG_TI_CPTS
 417        if (cpts->clock) {
 418                ptp_clock_unregister(cpts->clock);
 419                cancel_delayed_work_sync(&cpts->overflow_work);
 420        }
 421        if (cpts->refclk)
 422                cpts_clk_release(cpts);
 423#endif
 424}
 425