// SPDX-License-Identifier: GPL-2.0 /* * Copyright 2013-2016 Freescale Semiconductor Inc. * Copyright 2016-2018 NXP */ #include #include #include #include #include "dpaa2-ptp.h" struct ptp_dpaa2_priv { struct fsl_mc_device *ptp_mc_dev; struct ptp_clock *clock; struct ptp_clock_info caps; u32 freq_comp; }; /* PTP clock operations */ static int ptp_dpaa2_adjfreq(struct ptp_clock_info *ptp, s32 ppb) { struct ptp_dpaa2_priv *ptp_dpaa2 = container_of(ptp, struct ptp_dpaa2_priv, caps); struct fsl_mc_device *mc_dev = ptp_dpaa2->ptp_mc_dev; struct device *dev = &mc_dev->dev; u64 adj; u32 diff, tmr_add; int neg_adj = 0; int err = 0; if (ppb < 0) { neg_adj = 1; ppb = -ppb; } tmr_add = ptp_dpaa2->freq_comp; adj = tmr_add; adj *= ppb; diff = div_u64(adj, 1000000000ULL); tmr_add = neg_adj ? tmr_add - diff : tmr_add + diff; err = dprtc_set_freq_compensation(mc_dev->mc_io, 0, mc_dev->mc_handle, tmr_add); if (err) dev_err(dev, "dprtc_set_freq_compensation err %d\n", err); return err; } static int ptp_dpaa2_adjtime(struct ptp_clock_info *ptp, s64 delta) { struct ptp_dpaa2_priv *ptp_dpaa2 = container_of(ptp, struct ptp_dpaa2_priv, caps); struct fsl_mc_device *mc_dev = ptp_dpaa2->ptp_mc_dev; struct device *dev = &mc_dev->dev; s64 now; int err = 0; err = dprtc_get_time(mc_dev->mc_io, 0, mc_dev->mc_handle, &now); if (err) { dev_err(dev, "dprtc_get_time err %d\n", err); return err; } now += delta; err = dprtc_set_time(mc_dev->mc_io, 0, mc_dev->mc_handle, now); if (err) dev_err(dev, "dprtc_set_time err %d\n", err); return err; } static int ptp_dpaa2_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) { struct ptp_dpaa2_priv *ptp_dpaa2 = container_of(ptp, struct ptp_dpaa2_priv, caps); struct fsl_mc_device *mc_dev = ptp_dpaa2->ptp_mc_dev; struct device *dev = &mc_dev->dev; u64 ns; u32 remainder; int err = 0; err = dprtc_get_time(mc_dev->mc_io, 0, mc_dev->mc_handle, &ns); if (err) { dev_err(dev, "dprtc_get_time err %d\n", err); return err; } ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder); ts->tv_nsec = remainder; return err; } static int ptp_dpaa2_settime(struct ptp_clock_info *ptp, const struct timespec64 *ts) { struct ptp_dpaa2_priv *ptp_dpaa2 = container_of(ptp, struct ptp_dpaa2_priv, caps); struct fsl_mc_device *mc_dev = ptp_dpaa2->ptp_mc_dev; struct device *dev = &mc_dev->dev; u64 ns; int err = 0; ns = ts->tv_sec * 1000000000ULL; ns += ts->tv_nsec; err = dprtc_set_time(mc_dev->mc_io, 0, mc_dev->mc_handle, ns); if (err) dev_err(dev, "dprtc_set_time err %d\n", err); return err; } static const struct ptp_clock_info ptp_dpaa2_caps = { .owner = THIS_MODULE, .name = "DPAA2 PTP Clock", .max_adj = 512000, .n_alarm = 2, .n_ext_ts = 2, .n_per_out = 3, .n_pins = 0, .pps = 1, .adjfreq = ptp_dpaa2_adjfreq, .adjtime = ptp_dpaa2_adjtime, .gettime64 = ptp_dpaa2_gettime, .settime64 = ptp_dpaa2_settime, }; static int dpaa2_ptp_probe(struct fsl_mc_device *mc_dev) { struct device *dev = &mc_dev->dev; struct ptp_dpaa2_priv *ptp_dpaa2; u32 tmr_add = 0; int err; ptp_dpaa2 = devm_kzalloc(dev, sizeof(*ptp_dpaa2), GFP_KERNEL); if (!ptp_dpaa2) return -ENOMEM; err = fsl_mc_portal_allocate(mc_dev, 0, &mc_dev->mc_io); if (err) { if (err == -ENXIO) err = -EPROBE_DEFER; else dev_err(dev, "fsl_mc_portal_allocate err %d\n", err); goto err_exit; } err = dprtc_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id, &mc_dev->mc_handle); if (err) { dev_err(dev, "dprtc_open err %d\n", err); goto err_free_mcp; } ptp_dpaa2->ptp_mc_dev = mc_dev; err = dprtc_get_freq_compensation(mc_dev->mc_io, 0, mc_dev->mc_handle, &tmr_add); if (err) { dev_err(dev, "dprtc_get_freq_compensation err %d\n", err); goto err_close; } ptp_dpaa2->freq_comp = tmr_add; ptp_dpaa2->caps = ptp_dpaa2_caps; ptp_dpaa2->clock = ptp_clock_register(&ptp_dpaa2->caps, dev); if (IS_ERR(ptp_dpaa2->clock)) { err = PTR_ERR(ptp_dpaa2->clock); goto err_close; } dpaa2_phc_index = ptp_clock_index(ptp_dpaa2->clock); dev_set_drvdata(dev, ptp_dpaa2); return 0; err_close: dprtc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); err_free_mcp: fsl_mc_portal_free(mc_dev->mc_io); err_exit: return err; } static int dpaa2_ptp_remove(struct fsl_mc_device *mc_dev) { struct ptp_dpaa2_priv *ptp_dpaa2; struct device *dev = &mc_dev->dev; ptp_dpaa2 = dev_get_drvdata(dev); ptp_clock_unregister(ptp_dpaa2->clock); dprtc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); fsl_mc_portal_free(mc_dev->mc_io); return 0; } static const struct fsl_mc_device_id dpaa2_ptp_match_id_table[] = { { .vendor = FSL_MC_VENDOR_FREESCALE, .obj_type = "dprtc", }, {} }; MODULE_DEVICE_TABLE(fslmc, dpaa2_ptp_match_id_table); static struct fsl_mc_driver dpaa2_ptp_drv = { .driver = { .name = KBUILD_MODNAME, .owner = THIS_MODULE, }, .probe = dpaa2_ptp_probe, .remove = dpaa2_ptp_remove, .match_id_table = dpaa2_ptp_match_id_table, }; module_fsl_mc_driver(dpaa2_ptp_drv); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("DPAA2 PTP Clock Driver");