summaryrefslogtreecommitdiff
path: root/drivers/staging/fsl-dpaa2/rtc/rtc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/fsl-dpaa2/rtc/rtc.c')
-rw-r--r--drivers/staging/fsl-dpaa2/rtc/rtc.c243
1 files changed, 243 insertions, 0 deletions
diff --git a/drivers/staging/fsl-dpaa2/rtc/rtc.c b/drivers/staging/fsl-dpaa2/rtc/rtc.c
new file mode 100644
index 0000000..0afc653
--- /dev/null
+++ b/drivers/staging/fsl-dpaa2/rtc/rtc.c
@@ -0,0 +1,243 @@
+/* Copyright 2013-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the above-listed copyright holders nor the
+ * names of any contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/module.h>
+#include <linux/ptp_clock_kernel.h>
+
+#include "../../fsl-mc/include/mc.h"
+#include "../../fsl-mc/include/mc-sys.h"
+
+#include "dprtc.h"
+#include "dprtc-cmd.h"
+
+#define N_EXT_TS 2
+
+struct ptp_clock *clock;
+struct fsl_mc_device *rtc_mc_dev;
+u32 freqCompensation;
+
+/* PTP clock operations */
+static int ptp_dpaa2_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
+{
+ u64 adj;
+ u32 diff, tmr_add;
+ int neg_adj = 0;
+ int err = 0;
+ struct fsl_mc_device *mc_dev = rtc_mc_dev;
+ struct device *dev = &mc_dev->dev;
+
+ if (ppb < 0) {
+ neg_adj = 1;
+ ppb = -ppb;
+ }
+
+ tmr_add = freqCompensation;
+ 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 0;
+}
+
+static int ptp_dpaa2_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+ s64 now;
+ int err = 0;
+ struct fsl_mc_device *mc_dev = rtc_mc_dev;
+ struct device *dev = &mc_dev->dev;
+
+ 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 0;
+ }
+
+ 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 0;
+ }
+ return 0;
+}
+
+static int ptp_dpaa2_gettime(struct ptp_clock_info *ptp, struct timespec *ts)
+{
+ u64 ns;
+ u32 remainder;
+ int err = 0;
+ struct fsl_mc_device *mc_dev = rtc_mc_dev;
+ struct device *dev = &mc_dev->dev;
+
+ 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 0;
+ }
+
+ ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder);
+ ts->tv_nsec = remainder;
+ return 0;
+}
+
+static int ptp_dpaa2_settime(struct ptp_clock_info *ptp,
+ const struct timespec *ts)
+{
+ u64 ns;
+ int err = 0;
+ struct fsl_mc_device *mc_dev = rtc_mc_dev;
+ struct device *dev = &mc_dev->dev;
+
+ 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 0;
+}
+
+static struct ptp_clock_info ptp_dpaa2_caps = {
+ .owner = THIS_MODULE,
+ .name = "dpaa2 clock",
+ .max_adj = 512000,
+ .n_alarm = 0,
+ .n_ext_ts = N_EXT_TS,
+ .n_per_out = 0,
+ .n_pins = 0,
+ .pps = 1,
+ .adjfreq = ptp_dpaa2_adjfreq,
+ .adjtime = ptp_dpaa2_adjtime,
+ .gettime64 = ptp_dpaa2_gettime,
+ .settime64 = ptp_dpaa2_settime,
+};
+
+static int rtc_probe(struct fsl_mc_device *mc_dev)
+{
+ struct device *dev;
+ int err = 0;
+ int dpaa2_phc_index;
+ u32 tmr_add = 0;
+
+ if (!mc_dev)
+ return -EFAULT;
+
+ dev = &mc_dev->dev;
+
+ err = fsl_mc_portal_allocate(mc_dev, 0, &mc_dev->mc_io);
+ if (unlikely(err)) {
+ dev_err(dev, "fsl_mc_portal_allocate err %d\n", err);
+ goto err_exit;
+ }
+ if (!mc_dev->mc_io) {
+ dev_err(dev,
+ "fsl_mc_portal_allocate returned null handle but no error\n");
+ err = -EFAULT;
+ 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;
+ }
+ if (!mc_dev->mc_handle) {
+ dev_err(dev, "dprtc_open returned null handle but no error\n");
+ err = -EFAULT;
+ goto err_free_mcp;
+ }
+
+ rtc_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;
+ }
+ freqCompensation = tmr_add;
+
+ clock = ptp_clock_register(&ptp_dpaa2_caps, dev);
+ if (IS_ERR(clock)) {
+ err = PTR_ERR(clock);
+ goto err_close;
+ }
+ dpaa2_phc_index = ptp_clock_index(clock);
+
+ 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 rtc_remove(struct fsl_mc_device *mc_dev)
+{
+ ptp_clock_unregister(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 rtc_match_id_table[] = {
+ {
+ .vendor = FSL_MC_VENDOR_FREESCALE,
+ .obj_type = "dprtc",
+ },
+ {}
+};
+
+static struct fsl_mc_driver rtc_drv = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = rtc_probe,
+ .remove = rtc_remove,
+ .match_id_table = rtc_match_id_table,
+};
+
+module_fsl_mc_driver(rtc_drv);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("DPAA2 RTC (PTP 1588 clock) driver (prototype)");