18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PTP 1588 clock for Freescale QorIQ 1588 timer 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010 OMICRON electronics GmbH 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/device.h> 118c2ecf20Sopenharmony_ci#include <linux/hrtimer.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/of.h> 158c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 168c2ecf20Sopenharmony_ci#include <linux/timex.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/clk.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/fsl/ptp_qoriq.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* 238c2ecf20Sopenharmony_ci * Register access functions 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* Caller must hold ptp_qoriq->lock. */ 278c2ecf20Sopenharmony_cistatic u64 tmr_cnt_read(struct ptp_qoriq *ptp_qoriq) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci struct ptp_qoriq_registers *regs = &ptp_qoriq->regs; 308c2ecf20Sopenharmony_ci u64 ns; 318c2ecf20Sopenharmony_ci u32 lo, hi; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci lo = ptp_qoriq->read(®s->ctrl_regs->tmr_cnt_l); 348c2ecf20Sopenharmony_ci hi = ptp_qoriq->read(®s->ctrl_regs->tmr_cnt_h); 358c2ecf20Sopenharmony_ci ns = ((u64) hi) << 32; 368c2ecf20Sopenharmony_ci ns |= lo; 378c2ecf20Sopenharmony_ci return ns; 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* Caller must hold ptp_qoriq->lock. */ 418c2ecf20Sopenharmony_cistatic void tmr_cnt_write(struct ptp_qoriq *ptp_qoriq, u64 ns) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct ptp_qoriq_registers *regs = &ptp_qoriq->regs; 448c2ecf20Sopenharmony_ci u32 hi = ns >> 32; 458c2ecf20Sopenharmony_ci u32 lo = ns & 0xffffffff; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci ptp_qoriq->write(®s->ctrl_regs->tmr_cnt_l, lo); 488c2ecf20Sopenharmony_ci ptp_qoriq->write(®s->ctrl_regs->tmr_cnt_h, hi); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* Caller must hold ptp_qoriq->lock. */ 528c2ecf20Sopenharmony_cistatic void set_alarm(struct ptp_qoriq *ptp_qoriq) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct ptp_qoriq_registers *regs = &ptp_qoriq->regs; 558c2ecf20Sopenharmony_ci u64 ns; 568c2ecf20Sopenharmony_ci u32 lo, hi; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci ns = tmr_cnt_read(ptp_qoriq) + 1500000000ULL; 598c2ecf20Sopenharmony_ci ns = div_u64(ns, 1000000000UL) * 1000000000ULL; 608c2ecf20Sopenharmony_ci ns -= ptp_qoriq->tclk_period; 618c2ecf20Sopenharmony_ci hi = ns >> 32; 628c2ecf20Sopenharmony_ci lo = ns & 0xffffffff; 638c2ecf20Sopenharmony_ci ptp_qoriq->write(®s->alarm_regs->tmr_alarm1_l, lo); 648c2ecf20Sopenharmony_ci ptp_qoriq->write(®s->alarm_regs->tmr_alarm1_h, hi); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* Caller must hold ptp_qoriq->lock. */ 688c2ecf20Sopenharmony_cistatic void set_fipers(struct ptp_qoriq *ptp_qoriq) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct ptp_qoriq_registers *regs = &ptp_qoriq->regs; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci set_alarm(ptp_qoriq); 738c2ecf20Sopenharmony_ci ptp_qoriq->write(®s->fiper_regs->tmr_fiper1, ptp_qoriq->tmr_fiper1); 748c2ecf20Sopenharmony_ci ptp_qoriq->write(®s->fiper_regs->tmr_fiper2, ptp_qoriq->tmr_fiper2); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (ptp_qoriq->fiper3_support) 778c2ecf20Sopenharmony_ci ptp_qoriq->write(®s->fiper_regs->tmr_fiper3, 788c2ecf20Sopenharmony_ci ptp_qoriq->tmr_fiper3); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ciint extts_clean_up(struct ptp_qoriq *ptp_qoriq, int index, bool update_event) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct ptp_qoriq_registers *regs = &ptp_qoriq->regs; 848c2ecf20Sopenharmony_ci struct ptp_clock_event event; 858c2ecf20Sopenharmony_ci void __iomem *reg_etts_l; 868c2ecf20Sopenharmony_ci void __iomem *reg_etts_h; 878c2ecf20Sopenharmony_ci u32 valid, lo, hi; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci switch (index) { 908c2ecf20Sopenharmony_ci case 0: 918c2ecf20Sopenharmony_ci valid = ETS1_VLD; 928c2ecf20Sopenharmony_ci reg_etts_l = ®s->etts_regs->tmr_etts1_l; 938c2ecf20Sopenharmony_ci reg_etts_h = ®s->etts_regs->tmr_etts1_h; 948c2ecf20Sopenharmony_ci break; 958c2ecf20Sopenharmony_ci case 1: 968c2ecf20Sopenharmony_ci valid = ETS2_VLD; 978c2ecf20Sopenharmony_ci reg_etts_l = ®s->etts_regs->tmr_etts2_l; 988c2ecf20Sopenharmony_ci reg_etts_h = ®s->etts_regs->tmr_etts2_h; 998c2ecf20Sopenharmony_ci break; 1008c2ecf20Sopenharmony_ci default: 1018c2ecf20Sopenharmony_ci return -EINVAL; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci event.type = PTP_CLOCK_EXTTS; 1058c2ecf20Sopenharmony_ci event.index = index; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (ptp_qoriq->extts_fifo_support) 1088c2ecf20Sopenharmony_ci if (!(ptp_qoriq->read(®s->ctrl_regs->tmr_stat) & valid)) 1098c2ecf20Sopenharmony_ci return 0; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci do { 1128c2ecf20Sopenharmony_ci lo = ptp_qoriq->read(reg_etts_l); 1138c2ecf20Sopenharmony_ci hi = ptp_qoriq->read(reg_etts_h); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (update_event) { 1168c2ecf20Sopenharmony_ci event.timestamp = ((u64) hi) << 32; 1178c2ecf20Sopenharmony_ci event.timestamp |= lo; 1188c2ecf20Sopenharmony_ci ptp_clock_event(ptp_qoriq->clock, &event); 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (!ptp_qoriq->extts_fifo_support) 1228c2ecf20Sopenharmony_ci break; 1238c2ecf20Sopenharmony_ci } while (ptp_qoriq->read(®s->ctrl_regs->tmr_stat) & valid); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return 0; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(extts_clean_up); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* 1308c2ecf20Sopenharmony_ci * Interrupt service routine 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ciirqreturn_t ptp_qoriq_isr(int irq, void *priv) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct ptp_qoriq *ptp_qoriq = priv; 1368c2ecf20Sopenharmony_ci struct ptp_qoriq_registers *regs = &ptp_qoriq->regs; 1378c2ecf20Sopenharmony_ci struct ptp_clock_event event; 1388c2ecf20Sopenharmony_ci u32 ack = 0, mask, val, irqs; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci spin_lock(&ptp_qoriq->lock); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci val = ptp_qoriq->read(®s->ctrl_regs->tmr_tevent); 1438c2ecf20Sopenharmony_ci mask = ptp_qoriq->read(®s->ctrl_regs->tmr_temask); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci spin_unlock(&ptp_qoriq->lock); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci irqs = val & mask; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (irqs & ETS1) { 1508c2ecf20Sopenharmony_ci ack |= ETS1; 1518c2ecf20Sopenharmony_ci extts_clean_up(ptp_qoriq, 0, true); 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (irqs & ETS2) { 1558c2ecf20Sopenharmony_ci ack |= ETS2; 1568c2ecf20Sopenharmony_ci extts_clean_up(ptp_qoriq, 1, true); 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (irqs & PP1) { 1608c2ecf20Sopenharmony_ci ack |= PP1; 1618c2ecf20Sopenharmony_ci event.type = PTP_CLOCK_PPS; 1628c2ecf20Sopenharmony_ci ptp_clock_event(ptp_qoriq->clock, &event); 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (ack) { 1668c2ecf20Sopenharmony_ci ptp_qoriq->write(®s->ctrl_regs->tmr_tevent, ack); 1678c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1688c2ecf20Sopenharmony_ci } else 1698c2ecf20Sopenharmony_ci return IRQ_NONE; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ptp_qoriq_isr); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci/* 1748c2ecf20Sopenharmony_ci * PTP clock operations 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ciint ptp_qoriq_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci u64 adj, diff; 1808c2ecf20Sopenharmony_ci u32 tmr_add; 1818c2ecf20Sopenharmony_ci int neg_adj = 0; 1828c2ecf20Sopenharmony_ci struct ptp_qoriq *ptp_qoriq = container_of(ptp, struct ptp_qoriq, caps); 1838c2ecf20Sopenharmony_ci struct ptp_qoriq_registers *regs = &ptp_qoriq->regs; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (scaled_ppm < 0) { 1868c2ecf20Sopenharmony_ci neg_adj = 1; 1878c2ecf20Sopenharmony_ci scaled_ppm = -scaled_ppm; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci tmr_add = ptp_qoriq->tmr_add; 1908c2ecf20Sopenharmony_ci adj = tmr_add; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* 1938c2ecf20Sopenharmony_ci * Calculate diff and round() to the nearest integer 1948c2ecf20Sopenharmony_ci * 1958c2ecf20Sopenharmony_ci * diff = adj * (ppb / 1000000000) 1968c2ecf20Sopenharmony_ci * = adj * scaled_ppm / 65536000000 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_ci diff = mul_u64_u64_div_u64(adj, scaled_ppm, 32768000000); 1998c2ecf20Sopenharmony_ci diff = DIV64_U64_ROUND_UP(diff, 2); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci tmr_add = neg_adj ? tmr_add - diff : tmr_add + diff; 2028c2ecf20Sopenharmony_ci ptp_qoriq->write(®s->ctrl_regs->tmr_add, tmr_add); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ptp_qoriq_adjfine); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ciint ptp_qoriq_adjtime(struct ptp_clock_info *ptp, s64 delta) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci s64 now; 2118c2ecf20Sopenharmony_ci unsigned long flags; 2128c2ecf20Sopenharmony_ci struct ptp_qoriq *ptp_qoriq = container_of(ptp, struct ptp_qoriq, caps); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci spin_lock_irqsave(&ptp_qoriq->lock, flags); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci now = tmr_cnt_read(ptp_qoriq); 2178c2ecf20Sopenharmony_ci now += delta; 2188c2ecf20Sopenharmony_ci tmr_cnt_write(ptp_qoriq, now); 2198c2ecf20Sopenharmony_ci set_fipers(ptp_qoriq); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ptp_qoriq->lock, flags); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return 0; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ptp_qoriq_adjtime); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ciint ptp_qoriq_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci u64 ns; 2308c2ecf20Sopenharmony_ci unsigned long flags; 2318c2ecf20Sopenharmony_ci struct ptp_qoriq *ptp_qoriq = container_of(ptp, struct ptp_qoriq, caps); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci spin_lock_irqsave(&ptp_qoriq->lock, flags); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci ns = tmr_cnt_read(ptp_qoriq); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ptp_qoriq->lock, flags); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci *ts = ns_to_timespec64(ns); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci return 0; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ptp_qoriq_gettime); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ciint ptp_qoriq_settime(struct ptp_clock_info *ptp, 2468c2ecf20Sopenharmony_ci const struct timespec64 *ts) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci u64 ns; 2498c2ecf20Sopenharmony_ci unsigned long flags; 2508c2ecf20Sopenharmony_ci struct ptp_qoriq *ptp_qoriq = container_of(ptp, struct ptp_qoriq, caps); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci ns = timespec64_to_ns(ts); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci spin_lock_irqsave(&ptp_qoriq->lock, flags); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci tmr_cnt_write(ptp_qoriq, ns); 2578c2ecf20Sopenharmony_ci set_fipers(ptp_qoriq); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ptp_qoriq->lock, flags); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci return 0; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ptp_qoriq_settime); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ciint ptp_qoriq_enable(struct ptp_clock_info *ptp, 2668c2ecf20Sopenharmony_ci struct ptp_clock_request *rq, int on) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci struct ptp_qoriq *ptp_qoriq = container_of(ptp, struct ptp_qoriq, caps); 2698c2ecf20Sopenharmony_ci struct ptp_qoriq_registers *regs = &ptp_qoriq->regs; 2708c2ecf20Sopenharmony_ci unsigned long flags; 2718c2ecf20Sopenharmony_ci u32 bit, mask = 0; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci switch (rq->type) { 2748c2ecf20Sopenharmony_ci case PTP_CLK_REQ_EXTTS: 2758c2ecf20Sopenharmony_ci switch (rq->extts.index) { 2768c2ecf20Sopenharmony_ci case 0: 2778c2ecf20Sopenharmony_ci bit = ETS1EN; 2788c2ecf20Sopenharmony_ci break; 2798c2ecf20Sopenharmony_ci case 1: 2808c2ecf20Sopenharmony_ci bit = ETS2EN; 2818c2ecf20Sopenharmony_ci break; 2828c2ecf20Sopenharmony_ci default: 2838c2ecf20Sopenharmony_ci return -EINVAL; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (on) 2878c2ecf20Sopenharmony_ci extts_clean_up(ptp_qoriq, rq->extts.index, false); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci break; 2908c2ecf20Sopenharmony_ci case PTP_CLK_REQ_PPS: 2918c2ecf20Sopenharmony_ci bit = PP1EN; 2928c2ecf20Sopenharmony_ci break; 2938c2ecf20Sopenharmony_ci default: 2948c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci spin_lock_irqsave(&ptp_qoriq->lock, flags); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci mask = ptp_qoriq->read(®s->ctrl_regs->tmr_temask); 3008c2ecf20Sopenharmony_ci if (on) { 3018c2ecf20Sopenharmony_ci mask |= bit; 3028c2ecf20Sopenharmony_ci ptp_qoriq->write(®s->ctrl_regs->tmr_tevent, bit); 3038c2ecf20Sopenharmony_ci } else { 3048c2ecf20Sopenharmony_ci mask &= ~bit; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci ptp_qoriq->write(®s->ctrl_regs->tmr_temask, mask); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ptp_qoriq->lock, flags); 3108c2ecf20Sopenharmony_ci return 0; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ptp_qoriq_enable); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic const struct ptp_clock_info ptp_qoriq_caps = { 3158c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 3168c2ecf20Sopenharmony_ci .name = "qoriq ptp clock", 3178c2ecf20Sopenharmony_ci .max_adj = 512000, 3188c2ecf20Sopenharmony_ci .n_alarm = 0, 3198c2ecf20Sopenharmony_ci .n_ext_ts = N_EXT_TS, 3208c2ecf20Sopenharmony_ci .n_per_out = 0, 3218c2ecf20Sopenharmony_ci .n_pins = 0, 3228c2ecf20Sopenharmony_ci .pps = 1, 3238c2ecf20Sopenharmony_ci .adjfine = ptp_qoriq_adjfine, 3248c2ecf20Sopenharmony_ci .adjtime = ptp_qoriq_adjtime, 3258c2ecf20Sopenharmony_ci .gettime64 = ptp_qoriq_gettime, 3268c2ecf20Sopenharmony_ci .settime64 = ptp_qoriq_settime, 3278c2ecf20Sopenharmony_ci .enable = ptp_qoriq_enable, 3288c2ecf20Sopenharmony_ci}; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci/** 3318c2ecf20Sopenharmony_ci * ptp_qoriq_nominal_freq - calculate nominal frequency according to 3328c2ecf20Sopenharmony_ci * reference clock frequency 3338c2ecf20Sopenharmony_ci * 3348c2ecf20Sopenharmony_ci * @clk_src: reference clock frequency 3358c2ecf20Sopenharmony_ci * 3368c2ecf20Sopenharmony_ci * The nominal frequency is the desired clock frequency. 3378c2ecf20Sopenharmony_ci * It should be less than the reference clock frequency. 3388c2ecf20Sopenharmony_ci * It should be a factor of 1000MHz. 3398c2ecf20Sopenharmony_ci * 3408c2ecf20Sopenharmony_ci * Return the nominal frequency 3418c2ecf20Sopenharmony_ci */ 3428c2ecf20Sopenharmony_cistatic u32 ptp_qoriq_nominal_freq(u32 clk_src) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci u32 remainder = 0; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci clk_src /= 1000000; 3478c2ecf20Sopenharmony_ci remainder = clk_src % 100; 3488c2ecf20Sopenharmony_ci if (remainder) { 3498c2ecf20Sopenharmony_ci clk_src -= remainder; 3508c2ecf20Sopenharmony_ci clk_src += 100; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci do { 3548c2ecf20Sopenharmony_ci clk_src -= 100; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci } while (1000 % clk_src); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci return clk_src * 1000000; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci/** 3628c2ecf20Sopenharmony_ci * ptp_qoriq_auto_config - calculate a set of default configurations 3638c2ecf20Sopenharmony_ci * 3648c2ecf20Sopenharmony_ci * @ptp_qoriq: pointer to ptp_qoriq 3658c2ecf20Sopenharmony_ci * @node: pointer to device_node 3668c2ecf20Sopenharmony_ci * 3678c2ecf20Sopenharmony_ci * If below dts properties are not provided, this function will be 3688c2ecf20Sopenharmony_ci * called to calculate a set of default configurations for them. 3698c2ecf20Sopenharmony_ci * "fsl,tclk-period" 3708c2ecf20Sopenharmony_ci * "fsl,tmr-prsc" 3718c2ecf20Sopenharmony_ci * "fsl,tmr-add" 3728c2ecf20Sopenharmony_ci * "fsl,tmr-fiper1" 3738c2ecf20Sopenharmony_ci * "fsl,tmr-fiper2" 3748c2ecf20Sopenharmony_ci * "fsl,tmr-fiper3" (required only for DPAA2 and ENETC hardware) 3758c2ecf20Sopenharmony_ci * "fsl,max-adj" 3768c2ecf20Sopenharmony_ci * 3778c2ecf20Sopenharmony_ci * Return 0 if success 3788c2ecf20Sopenharmony_ci */ 3798c2ecf20Sopenharmony_cistatic int ptp_qoriq_auto_config(struct ptp_qoriq *ptp_qoriq, 3808c2ecf20Sopenharmony_ci struct device_node *node) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci struct clk *clk; 3838c2ecf20Sopenharmony_ci u64 freq_comp; 3848c2ecf20Sopenharmony_ci u64 max_adj; 3858c2ecf20Sopenharmony_ci u32 nominal_freq; 3868c2ecf20Sopenharmony_ci u32 remainder = 0; 3878c2ecf20Sopenharmony_ci u32 clk_src = 0; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci ptp_qoriq->cksel = DEFAULT_CKSEL; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci clk = of_clk_get(node, 0); 3928c2ecf20Sopenharmony_ci if (!IS_ERR(clk)) { 3938c2ecf20Sopenharmony_ci clk_src = clk_get_rate(clk); 3948c2ecf20Sopenharmony_ci clk_put(clk); 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (clk_src <= 100000000UL) { 3988c2ecf20Sopenharmony_ci pr_err("error reference clock value, or lower than 100MHz\n"); 3998c2ecf20Sopenharmony_ci return -EINVAL; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci nominal_freq = ptp_qoriq_nominal_freq(clk_src); 4038c2ecf20Sopenharmony_ci if (!nominal_freq) 4048c2ecf20Sopenharmony_ci return -EINVAL; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci ptp_qoriq->tclk_period = 1000000000UL / nominal_freq; 4078c2ecf20Sopenharmony_ci ptp_qoriq->tmr_prsc = DEFAULT_TMR_PRSC; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci /* Calculate initial frequency compensation value for TMR_ADD register. 4108c2ecf20Sopenharmony_ci * freq_comp = ceil(2^32 / freq_ratio) 4118c2ecf20Sopenharmony_ci * freq_ratio = reference_clock_freq / nominal_freq 4128c2ecf20Sopenharmony_ci */ 4138c2ecf20Sopenharmony_ci freq_comp = ((u64)1 << 32) * nominal_freq; 4148c2ecf20Sopenharmony_ci freq_comp = div_u64_rem(freq_comp, clk_src, &remainder); 4158c2ecf20Sopenharmony_ci if (remainder) 4168c2ecf20Sopenharmony_ci freq_comp++; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci ptp_qoriq->tmr_add = freq_comp; 4198c2ecf20Sopenharmony_ci ptp_qoriq->tmr_fiper1 = DEFAULT_FIPER1_PERIOD - ptp_qoriq->tclk_period; 4208c2ecf20Sopenharmony_ci ptp_qoriq->tmr_fiper2 = DEFAULT_FIPER2_PERIOD - ptp_qoriq->tclk_period; 4218c2ecf20Sopenharmony_ci ptp_qoriq->tmr_fiper3 = DEFAULT_FIPER3_PERIOD - ptp_qoriq->tclk_period; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci /* max_adj = 1000000000 * (freq_ratio - 1.0) - 1 4248c2ecf20Sopenharmony_ci * freq_ratio = reference_clock_freq / nominal_freq 4258c2ecf20Sopenharmony_ci */ 4268c2ecf20Sopenharmony_ci max_adj = 1000000000ULL * (clk_src - nominal_freq); 4278c2ecf20Sopenharmony_ci max_adj = div_u64(max_adj, nominal_freq) - 1; 4288c2ecf20Sopenharmony_ci ptp_qoriq->caps.max_adj = max_adj; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci return 0; 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ciint ptp_qoriq_init(struct ptp_qoriq *ptp_qoriq, void __iomem *base, 4348c2ecf20Sopenharmony_ci const struct ptp_clock_info *caps) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci struct device_node *node = ptp_qoriq->dev->of_node; 4378c2ecf20Sopenharmony_ci struct ptp_qoriq_registers *regs; 4388c2ecf20Sopenharmony_ci struct timespec64 now; 4398c2ecf20Sopenharmony_ci unsigned long flags; 4408c2ecf20Sopenharmony_ci u32 tmr_ctrl; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci if (!node) 4438c2ecf20Sopenharmony_ci return -ENODEV; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci ptp_qoriq->base = base; 4468c2ecf20Sopenharmony_ci ptp_qoriq->caps = *caps; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci if (of_property_read_u32(node, "fsl,cksel", &ptp_qoriq->cksel)) 4498c2ecf20Sopenharmony_ci ptp_qoriq->cksel = DEFAULT_CKSEL; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (of_property_read_bool(node, "fsl,extts-fifo")) 4528c2ecf20Sopenharmony_ci ptp_qoriq->extts_fifo_support = true; 4538c2ecf20Sopenharmony_ci else 4548c2ecf20Sopenharmony_ci ptp_qoriq->extts_fifo_support = false; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if (of_device_is_compatible(node, "fsl,dpaa2-ptp") || 4578c2ecf20Sopenharmony_ci of_device_is_compatible(node, "fsl,enetc-ptp")) 4588c2ecf20Sopenharmony_ci ptp_qoriq->fiper3_support = true; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (of_property_read_u32(node, 4618c2ecf20Sopenharmony_ci "fsl,tclk-period", &ptp_qoriq->tclk_period) || 4628c2ecf20Sopenharmony_ci of_property_read_u32(node, 4638c2ecf20Sopenharmony_ci "fsl,tmr-prsc", &ptp_qoriq->tmr_prsc) || 4648c2ecf20Sopenharmony_ci of_property_read_u32(node, 4658c2ecf20Sopenharmony_ci "fsl,tmr-add", &ptp_qoriq->tmr_add) || 4668c2ecf20Sopenharmony_ci of_property_read_u32(node, 4678c2ecf20Sopenharmony_ci "fsl,tmr-fiper1", &ptp_qoriq->tmr_fiper1) || 4688c2ecf20Sopenharmony_ci of_property_read_u32(node, 4698c2ecf20Sopenharmony_ci "fsl,tmr-fiper2", &ptp_qoriq->tmr_fiper2) || 4708c2ecf20Sopenharmony_ci of_property_read_u32(node, 4718c2ecf20Sopenharmony_ci "fsl,max-adj", &ptp_qoriq->caps.max_adj) || 4728c2ecf20Sopenharmony_ci (ptp_qoriq->fiper3_support && 4738c2ecf20Sopenharmony_ci of_property_read_u32(node, "fsl,tmr-fiper3", 4748c2ecf20Sopenharmony_ci &ptp_qoriq->tmr_fiper3))) { 4758c2ecf20Sopenharmony_ci pr_warn("device tree node missing required elements, try automatic configuration\n"); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci if (ptp_qoriq_auto_config(ptp_qoriq, node)) 4788c2ecf20Sopenharmony_ci return -ENODEV; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (of_property_read_bool(node, "little-endian")) { 4828c2ecf20Sopenharmony_ci ptp_qoriq->read = qoriq_read_le; 4838c2ecf20Sopenharmony_ci ptp_qoriq->write = qoriq_write_le; 4848c2ecf20Sopenharmony_ci } else { 4858c2ecf20Sopenharmony_ci ptp_qoriq->read = qoriq_read_be; 4868c2ecf20Sopenharmony_ci ptp_qoriq->write = qoriq_write_be; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* The eTSEC uses differnt memory map with DPAA/ENETC */ 4908c2ecf20Sopenharmony_ci if (of_device_is_compatible(node, "fsl,etsec-ptp")) { 4918c2ecf20Sopenharmony_ci ptp_qoriq->regs.ctrl_regs = base + ETSEC_CTRL_REGS_OFFSET; 4928c2ecf20Sopenharmony_ci ptp_qoriq->regs.alarm_regs = base + ETSEC_ALARM_REGS_OFFSET; 4938c2ecf20Sopenharmony_ci ptp_qoriq->regs.fiper_regs = base + ETSEC_FIPER_REGS_OFFSET; 4948c2ecf20Sopenharmony_ci ptp_qoriq->regs.etts_regs = base + ETSEC_ETTS_REGS_OFFSET; 4958c2ecf20Sopenharmony_ci } else { 4968c2ecf20Sopenharmony_ci ptp_qoriq->regs.ctrl_regs = base + CTRL_REGS_OFFSET; 4978c2ecf20Sopenharmony_ci ptp_qoriq->regs.alarm_regs = base + ALARM_REGS_OFFSET; 4988c2ecf20Sopenharmony_ci ptp_qoriq->regs.fiper_regs = base + FIPER_REGS_OFFSET; 4998c2ecf20Sopenharmony_ci ptp_qoriq->regs.etts_regs = base + ETTS_REGS_OFFSET; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci spin_lock_init(&ptp_qoriq->lock); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci ktime_get_real_ts64(&now); 5058c2ecf20Sopenharmony_ci ptp_qoriq_settime(&ptp_qoriq->caps, &now); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci tmr_ctrl = 5088c2ecf20Sopenharmony_ci (ptp_qoriq->tclk_period & TCLK_PERIOD_MASK) << TCLK_PERIOD_SHIFT | 5098c2ecf20Sopenharmony_ci (ptp_qoriq->cksel & CKSEL_MASK) << CKSEL_SHIFT; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci spin_lock_irqsave(&ptp_qoriq->lock, flags); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci regs = &ptp_qoriq->regs; 5148c2ecf20Sopenharmony_ci ptp_qoriq->write(®s->ctrl_regs->tmr_ctrl, tmr_ctrl); 5158c2ecf20Sopenharmony_ci ptp_qoriq->write(®s->ctrl_regs->tmr_add, ptp_qoriq->tmr_add); 5168c2ecf20Sopenharmony_ci ptp_qoriq->write(®s->ctrl_regs->tmr_prsc, ptp_qoriq->tmr_prsc); 5178c2ecf20Sopenharmony_ci ptp_qoriq->write(®s->fiper_regs->tmr_fiper1, ptp_qoriq->tmr_fiper1); 5188c2ecf20Sopenharmony_ci ptp_qoriq->write(®s->fiper_regs->tmr_fiper2, ptp_qoriq->tmr_fiper2); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (ptp_qoriq->fiper3_support) 5218c2ecf20Sopenharmony_ci ptp_qoriq->write(®s->fiper_regs->tmr_fiper3, 5228c2ecf20Sopenharmony_ci ptp_qoriq->tmr_fiper3); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci set_alarm(ptp_qoriq); 5258c2ecf20Sopenharmony_ci ptp_qoriq->write(®s->ctrl_regs->tmr_ctrl, 5268c2ecf20Sopenharmony_ci tmr_ctrl|FIPERST|RTPE|TE|FRD); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ptp_qoriq->lock, flags); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci ptp_qoriq->clock = ptp_clock_register(&ptp_qoriq->caps, ptp_qoriq->dev); 5318c2ecf20Sopenharmony_ci if (IS_ERR(ptp_qoriq->clock)) 5328c2ecf20Sopenharmony_ci return PTR_ERR(ptp_qoriq->clock); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci ptp_qoriq->phc_index = ptp_clock_index(ptp_qoriq->clock); 5358c2ecf20Sopenharmony_ci ptp_qoriq_create_debugfs(ptp_qoriq); 5368c2ecf20Sopenharmony_ci return 0; 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ptp_qoriq_init); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_civoid ptp_qoriq_free(struct ptp_qoriq *ptp_qoriq) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci struct ptp_qoriq_registers *regs = &ptp_qoriq->regs; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci ptp_qoriq->write(®s->ctrl_regs->tmr_temask, 0); 5458c2ecf20Sopenharmony_ci ptp_qoriq->write(®s->ctrl_regs->tmr_ctrl, 0); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci ptp_qoriq_remove_debugfs(ptp_qoriq); 5488c2ecf20Sopenharmony_ci ptp_clock_unregister(ptp_qoriq->clock); 5498c2ecf20Sopenharmony_ci iounmap(ptp_qoriq->base); 5508c2ecf20Sopenharmony_ci free_irq(ptp_qoriq->irq, ptp_qoriq); 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ptp_qoriq_free); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistatic int ptp_qoriq_probe(struct platform_device *dev) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci struct ptp_qoriq *ptp_qoriq; 5578c2ecf20Sopenharmony_ci int err = -ENOMEM; 5588c2ecf20Sopenharmony_ci void __iomem *base; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci ptp_qoriq = kzalloc(sizeof(*ptp_qoriq), GFP_KERNEL); 5618c2ecf20Sopenharmony_ci if (!ptp_qoriq) 5628c2ecf20Sopenharmony_ci goto no_memory; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci ptp_qoriq->dev = &dev->dev; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci err = -ENODEV; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci ptp_qoriq->irq = platform_get_irq(dev, 0); 5698c2ecf20Sopenharmony_ci if (ptp_qoriq->irq < 0) { 5708c2ecf20Sopenharmony_ci pr_err("irq not in device tree\n"); 5718c2ecf20Sopenharmony_ci goto no_node; 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci if (request_irq(ptp_qoriq->irq, ptp_qoriq_isr, IRQF_SHARED, 5748c2ecf20Sopenharmony_ci DRIVER, ptp_qoriq)) { 5758c2ecf20Sopenharmony_ci pr_err("request_irq failed\n"); 5768c2ecf20Sopenharmony_ci goto no_node; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci ptp_qoriq->rsrc = platform_get_resource(dev, IORESOURCE_MEM, 0); 5808c2ecf20Sopenharmony_ci if (!ptp_qoriq->rsrc) { 5818c2ecf20Sopenharmony_ci pr_err("no resource\n"); 5828c2ecf20Sopenharmony_ci goto no_resource; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci if (request_resource(&iomem_resource, ptp_qoriq->rsrc)) { 5858c2ecf20Sopenharmony_ci pr_err("resource busy\n"); 5868c2ecf20Sopenharmony_ci goto no_resource; 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci base = ioremap(ptp_qoriq->rsrc->start, 5908c2ecf20Sopenharmony_ci resource_size(ptp_qoriq->rsrc)); 5918c2ecf20Sopenharmony_ci if (!base) { 5928c2ecf20Sopenharmony_ci pr_err("ioremap ptp registers failed\n"); 5938c2ecf20Sopenharmony_ci goto no_ioremap; 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci err = ptp_qoriq_init(ptp_qoriq, base, &ptp_qoriq_caps); 5978c2ecf20Sopenharmony_ci if (err) 5988c2ecf20Sopenharmony_ci goto no_clock; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci platform_set_drvdata(dev, ptp_qoriq); 6018c2ecf20Sopenharmony_ci return 0; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cino_clock: 6048c2ecf20Sopenharmony_ci iounmap(base); 6058c2ecf20Sopenharmony_cino_ioremap: 6068c2ecf20Sopenharmony_ci release_resource(ptp_qoriq->rsrc); 6078c2ecf20Sopenharmony_cino_resource: 6088c2ecf20Sopenharmony_ci free_irq(ptp_qoriq->irq, ptp_qoriq); 6098c2ecf20Sopenharmony_cino_node: 6108c2ecf20Sopenharmony_ci kfree(ptp_qoriq); 6118c2ecf20Sopenharmony_cino_memory: 6128c2ecf20Sopenharmony_ci return err; 6138c2ecf20Sopenharmony_ci} 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_cistatic int ptp_qoriq_remove(struct platform_device *dev) 6168c2ecf20Sopenharmony_ci{ 6178c2ecf20Sopenharmony_ci struct ptp_qoriq *ptp_qoriq = platform_get_drvdata(dev); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci ptp_qoriq_free(ptp_qoriq); 6208c2ecf20Sopenharmony_ci release_resource(ptp_qoriq->rsrc); 6218c2ecf20Sopenharmony_ci kfree(ptp_qoriq); 6228c2ecf20Sopenharmony_ci return 0; 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_cistatic const struct of_device_id match_table[] = { 6268c2ecf20Sopenharmony_ci { .compatible = "fsl,etsec-ptp" }, 6278c2ecf20Sopenharmony_ci { .compatible = "fsl,fman-ptp-timer" }, 6288c2ecf20Sopenharmony_ci {}, 6298c2ecf20Sopenharmony_ci}; 6308c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, match_table); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic struct platform_driver ptp_qoriq_driver = { 6338c2ecf20Sopenharmony_ci .driver = { 6348c2ecf20Sopenharmony_ci .name = "ptp_qoriq", 6358c2ecf20Sopenharmony_ci .of_match_table = match_table, 6368c2ecf20Sopenharmony_ci }, 6378c2ecf20Sopenharmony_ci .probe = ptp_qoriq_probe, 6388c2ecf20Sopenharmony_ci .remove = ptp_qoriq_remove, 6398c2ecf20Sopenharmony_ci}; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_cimodule_platform_driver(ptp_qoriq_driver); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ciMODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>"); 6448c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PTP clock for Freescale QorIQ 1588 timer"); 6458c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 646