[RFC,3/8] soc/fman: Add FMan Real Time Clock support

Igal.Liberman igal.liberman at freescale.com
Wed Mar 11 16:08:18 AEDT 2015


From: Igal Liberman <Igal.Liberman at freescale.com>

Signed-off-by: Igal Liberman <Igal.Liberman at freescale.com>
---
 drivers/soc/fsl/fman/fm_drv.c           |   98 ++++
 drivers/soc/fsl/fman/fm_drv.h           |    9 +-
 drivers/soc/fsl/fman/inc/fm_rtc_ext.h   |  398 ++++++++++++++++
 drivers/soc/fsl/fman/inc/fsl_fman_drv.h |    3 +
 drivers/soc/fsl/fman/rtc/Makefile       |    2 +-
 drivers/soc/fsl/fman/rtc/fm_rtc.c       |  763 +++++++++++++++++++++++++++++++
 drivers/soc/fsl/fman/rtc/fm_rtc.h       |   89 ++++
 7 files changed, 1360 insertions(+), 2 deletions(-)
 create mode 100644 drivers/soc/fsl/fman/inc/fm_rtc_ext.h
 create mode 100644 drivers/soc/fsl/fman/rtc/fm_rtc.c
 create mode 100644 drivers/soc/fsl/fman/rtc/fm_rtc.h

diff --git a/drivers/soc/fsl/fman/fm_drv.c b/drivers/soc/fsl/fman/fm_drv.c
index 9859ac6..ffeb223 100644
--- a/drivers/soc/fsl/fman/fm_drv.c
+++ b/drivers/soc/fsl/fman/fm_drv.c
@@ -423,6 +423,32 @@ static struct lnx_wrp_fm_dev_t *read_fm_dev_tree_node(struct platform_device
 		}
 	}
 
+#ifdef CONFIG_FSL_FMAN_RTC
+	/* Get the RTC base address and size */
+	memset(&name, 0, sizeof(name));
+	if (WARN_ON(strlen("ptp-timer") >= sizeof(name.name)))
+		return NULL;
+	strcpy(name.name, "ptp-timer");
+	if (WARN_ON(strlen("fsl,fman-ptp-timer") >= sizeof(name.compatible)))
+		return NULL;
+	strcpy(name.compatible, "fsl,fman-ptp-timer");
+	for_each_child_of_node(fm_node, dev_node) {
+		if (likely(of_match_node(&name, dev_node) != NULL)) {
+			err = of_address_to_resource(dev_node, 0, &res);
+			if (unlikely(err < 0)) {
+				pr_err("of_address_to_resource() = %d",
+				       err);
+				goto _return_null;
+			}
+
+			p_lnx_wrp_fm_dev->fm_rtc_base_addr = 0;
+			p_lnx_wrp_fm_dev->fm_rtc_phys_base_addr = res.start;
+			p_lnx_wrp_fm_dev->fm_rtc_mem_size = res.end + 1 -
+							    res.start;
+		}
+	}
+#endif
+
 	of_node_put(fm_node);
 
 	p_lnx_wrp_fm_dev->active = true;
@@ -485,6 +511,9 @@ uint32_t get_qman_channel_id(struct lnx_wrp_fm_dev_t *p_lnx_wrp_fm_dev,
 
 static int configure_fm_dev(struct lnx_wrp_fm_dev_t *p_lnx_wrp_fm_dev)
 {
+#ifdef CONFIG_FSL_FMAN_RTC
+	struct resource *dev_res;
+#endif
 	int err;
 
 	if (!p_lnx_wrp_fm_dev->active) {
@@ -551,6 +580,32 @@ static int configure_fm_dev(struct lnx_wrp_fm_dev_t *p_lnx_wrp_fm_dev)
 		return -ENOSYS;
 	}
 
+#ifdef CONFIG_FSL_FMAN_RTC
+	if (p_lnx_wrp_fm_dev->fm_rtc_phys_base_addr) {
+		dev_res =
+		    __devm_request_region(p_lnx_wrp_fm_dev->dev,
+					  p_lnx_wrp_fm_dev->res,
+					  p_lnx_wrp_fm_dev->
+						fm_rtc_phys_base_addr,
+					  p_lnx_wrp_fm_dev->fm_rtc_mem_size,
+					  "fman-ptp-timer");
+		if (unlikely(!dev_res)) {
+			pr_err("__devm_request_region() failed\n");
+			return -ENOSYS;
+		}
+
+		p_lnx_wrp_fm_dev->fm_rtc_base_addr =
+		    PTR_TO_UINT(devm_ioremap
+				(p_lnx_wrp_fm_dev->dev,
+				 p_lnx_wrp_fm_dev->fm_rtc_phys_base_addr,
+				 p_lnx_wrp_fm_dev->fm_rtc_mem_size));
+		if (unlikely(p_lnx_wrp_fm_dev->fm_rtc_base_addr == 0)) {
+			pr_err("devm_ioremap() failed\n");
+			return -ENOSYS;
+		}
+	}
+#endif
+
 	p_lnx_wrp_fm_dev->params.base_addr = p_lnx_wrp_fm_dev->fm_base_addr;
 	p_lnx_wrp_fm_dev->params.fm_id = p_lnx_wrp_fm_dev->id;
 	p_lnx_wrp_fm_dev->params.f_exception = lnxwrp_fm_dev_exceptions_cb;
@@ -669,6 +724,34 @@ static int init_fm_dev(struct lnx_wrp_fm_dev_t *p_lnx_wrp_fm_dev)
 		 */
 	}
 
+#ifdef CONFIG_FSL_FMAN_RTC
+	if (p_lnx_wrp_fm_dev->fm_rtc_base_addr) {
+		struct fm_rtc_params_t fm_rtc_param;
+
+		memset(&fm_rtc_param, 0, sizeof(fm_rtc_param));
+		fm_rtc_param.h_app = p_lnx_wrp_fm_dev;
+		fm_rtc_param.h_fm = p_lnx_wrp_fm_dev->h_dev;
+		fm_rtc_param.base_address = p_lnx_wrp_fm_dev->fm_rtc_base_addr;
+		p_lnx_wrp_fm_dev->h_rtc_dev = fm_rtc_config(&fm_rtc_param);
+
+		if (!(p_lnx_wrp_fm_dev->h_rtc_dev)) {
+			pr_err("FM-RTC\n");
+			return -ENOSYS;
+		}
+
+		if (fm_rtc_cfg_period((struct fm *)p_lnx_wrp_fm_dev,
+			DPA_PTP_NOMINAL_FREQ_PERIOD_NS) != 0) {
+			pr_err("FM-RTC\n");
+			return -ENOSYS;
+		}
+
+		if (fm_rtc_init((struct fm *)p_lnx_wrp_fm_dev) != 0) {
+			pr_err("FM-RTC\n");
+			return -ENOSYS;
+		}
+	}
+#endif
+
 	if (unlikely(fill_qman_channhels_info(p_lnx_wrp_fm_dev) < 0)) {
 		pr_err("can't fill qman channel info\n");
 		return -ENOSYS;
@@ -682,6 +765,11 @@ static void free_fm_dev(struct lnx_wrp_fm_dev_t *p_lnx_wrp_fm_dev)
 	if (!p_lnx_wrp_fm_dev->active)
 		return;
 
+#ifdef CONFIG_FSL_FMAN_RTC
+	if (p_lnx_wrp_fm_dev->h_rtc_dev)
+		fm_rtc_free((struct fm *)p_lnx_wrp_fm_dev);
+#endif
+
 	if (p_lnx_wrp_fm_dev->h_dev)
 		fm_free(p_lnx_wrp_fm_dev->h_dev);
 
@@ -813,6 +901,16 @@ void *fm_get_handle(struct fm *fm)
 }
 EXPORT_SYMBOL(fm_get_handle);
 
+#ifdef CONFIG_FSL_FMAN_RTC
+void *fm_get_rtc_handle(struct fm *fm)
+{
+	struct lnx_wrp_fm_dev_t *p_lnx_wrp_fm_dev =
+		(struct lnx_wrp_fm_dev_t *)fm;
+
+	return (void *)p_lnx_wrp_fm_dev->h_rtc_dev;
+}
+EXPORT_SYMBOL(fm_get_rtc_handle);
+#endif
 
 void fm_mutex_lock(void)
 {
diff --git a/drivers/soc/fsl/fman/fm_drv.h b/drivers/soc/fsl/fman/fm_drv.h
index e0be922..c2831a7 100644
--- a/drivers/soc/fsl/fman/fm_drv.h
+++ b/drivers/soc/fsl/fman/fm_drv.h
@@ -92,11 +92,18 @@ struct lnx_wrp_fm_dev_t {
 	phys_addr_t fm_muram_phys_base_addr;
 	uint64_t fm_muram_base_addr;
 	resource_size_t fm_muram_mem_size;
+#ifdef CONFIG_FSL_FMAN_RTC
+	uint64_t fm_rtc_phys_base_addr;
+	uint64_t fm_rtc_base_addr;
+	resource_size_t fm_rtc_mem_size;
+#endif
 	int irq;
 	int err_irq;
 	struct fm_params_t params;
 	void *h_dev;
-
+#ifdef CONFIG_FSL_FMAN_RTC
+	void *h_rtc_dev;
+#endif
 	struct muram_info *p_muram;
 
 	struct lnx_wrp_fm_port_dev_t ports[NUM_OF_FM_PORTS];
diff --git a/drivers/soc/fsl/fman/inc/fm_rtc_ext.h b/drivers/soc/fsl/fman/inc/fm_rtc_ext.h
new file mode 100644
index 0000000..46768c6
--- /dev/null
+++ b/drivers/soc/fsl/fman/inc/fm_rtc_ext.h
@@ -0,0 +1,398 @@
+/*
+ * Copyright 2008-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 Freescale Semiconductor nor the
+ *       names of its 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 Freescale Semiconductor ``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 Freescale Semiconductor 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.
+ */
+
+/*File          fm_rtc_ext.h
+ *External definitions and API for FM RTC IEEE1588 Timer Module.
+ */
+
+#ifndef __FM_RTC_EXT_H__
+#define __FM_RTC_EXT_H__
+
+#include "service.h"
+#include "fsl_fman_rtc.h"
+
+struct fm;
+
+/* FM RTC functions, definitions and enums. */
+
+/* FM RTC initialization API. */
+
+/* FM RTC Alarm Polarity Options.*/
+enum fm_rtc_alarm_polarity {
+	FM_RTC_ALARM_POLARITY_ACTIVE_HIGH =
+		/*< Active-high output polarity */
+		FMAN_RTC_ALARM_POLARITY_ACTIVE_HIGH,
+	FM_RTC_ALARM_POLARITY_ACTIVE_LOW =
+		/*< Active-low output polarity */
+		FMAN_RTC_ALARM_POLARITY_ACTIVE_LOW
+};
+
+/* FM RTC Trigger Polarity Options.*/
+enum fm_rtc_trigger_polarity {
+	FM_RTC_TRIGGER_ON_RISING_EDGE =
+		/*< Trigger on rising edge */
+		FMAN_RTC_TRIGGER_ON_RISING_EDGE,
+	FM_RTC_TRIGGER_ON_FALLING_EDGE =
+		/*< Trigger on falling edge */
+		FMAN_RTC_TRIGGER_ON_FALLING_EDGE
+};
+
+/* IEEE1588 Timer Module FM RTC Optional Clock Sources.*/
+enum fm_src_clock {
+	/* external high precision timer reference clock */
+	FM_RTC_SOURCE_CLOCK_EXTERNAL = FMAN_RTC_SOURCE_CLOCK_EXTERNAL,
+	/* MAC system clock */
+	FM_RTC_SOURCE_CLOCK_SYSTEM = FMAN_RTC_SOURCE_CLOCK_SYSTEM,
+	/* RTC clock oscilator */
+	FM_RTC_SOURCE_CLOCK_OSCILATOR = FMAN_RTC_SOURCE_CLOCK_OSCILATOR
+};
+
+/* FM RTC configuration parameters structure.
+ * This structure should be passed to fm_rtc_config().
+ */
+struct fm_rtc_params_t {
+	void *h_fm;		/* FM Handle*/
+	uintptr_t base_address;	/* Base address of FM RTC registers */
+	/* A handle to an application layer object; This handle will
+	 * be passed by the driver upon calling the above callbacks
+	 */
+	void *h_app;
+};
+
+/* Function	fm_rtc_config
+ * Description	  Configures the FM RTC module according to user's parameters.
+ *		  The driver assigns default values to some FM RTC parameters.
+ *		  These parameters can be overwritten using the advanced
+ *		  configuration routines.
+ * Param[in]	  p_fm_rtc_param - FM RTC configuration parameters.
+ * Return	  Handle to the new FM RTC object; NULL pointer on failure.
+ * Cautions	  None
+ */
+void *fm_rtc_config(struct fm_rtc_params_t *p_fm_rtc_param);
+
+/* Function	fm_rtc_init
+ * Description	  Initializes the FM RTC driver and hardware.
+ * Param[in]	  fm_dev - Pointer to FM object
+ * Return	  0 on success; Error code otherwise.
+ * Cautions	  Must be called after fm_rtc_config and before fm_rt_init ().
+ */
+int fm_rtc_init(struct fm *fm_dev);
+
+/*Function	fm_rtc_free
+ *Description	  Frees the FM RTC object and all allocated resources.
+ * Param[in]	  fm_dev - Pointer to FM object
+ * Return	  0 on success; Error code otherwise.
+ * Cautions	  Must be called after fm_rtc_config and before fm_rt_init ().
+ */
+int fm_rtc_free(struct fm *fm_dev);
+
+/* FM RTC advanced configuration functions. */
+
+/* Function	fm_rtc_cfg_period
+ * Description	  Configures the period of the timestamp if different than
+ *		  default [DEFAULT_clockPeriod].
+ * Param[in]	  fm_dev - Pointer to FM object
+ * Param[in]	  period - Period in nano-seconds.
+ * Return	  0 on success; Error code otherwise.
+ * Cautions	  Must be called after fm_rtc_config and before fm_rt_init ().
+ */
+int fm_rtc_cfg_period(struct fm *fm_dev, uint32_t period);
+
+/* Function	fm_rtc_cfg_src_clk
+ * Description	  Configures the source clock of the RTC.
+ * Param[in]	  fm_dev - Pointer to FM object
+ * Param[in]	  src_clk - Source clock selection.
+ * Param[in]	  freq_in_mhz - the source-clock frequency (in MHz).
+ * Return	  0 on success; Error code otherwise.
+ * Cautions	  Must be called after fm_rtc_config and before fm_rt_init ().
+ */
+int fm_rtc_cfg_src_clk(struct fm *fm_dev, enum fm_src_clock src_clk,
+		       uint32_t freq_in_mhz);
+
+/* Function	fm_rtc_cfg_pulse_realign
+ * Description	  Configures the RTC to automatic FIPER pulse realignment in
+ *		  response to timer adjustments [DEFAULT_pulseRealign]
+ *		  In this mode, the RTC clock is identical to the source clock.
+ *		  This feature can be useful when the system contains an
+ *		  external RTC with inherent frequency compensation.
+ * Param[in]	  fm_dev - Pointer to FM object
+ * Param[in]	  enable - true to enable automatic realignment.
+ * Return	  0 on success; Error code otherwise.
+ * Cautions	  Must be called after fm_rtc_config and before fm_rt_init ().
+ */
+int fm_rtc_cfg_pulse_realign(struct fm *fm_dev, bool enable);
+
+/* Function	fm_rtc_cfg_freq_bypass
+ * Description	  Configures the RTC to bypass the frequency compensation
+ *		  mechanism. [DEFAULT_bypass]
+ *		  In this mode, the RTC clock is identical to the source clock.
+ *		  This feature can be useful when the system contains an
+ *		  external RTC with inherent frequency compensation.
+ * Param[in]	  fm_dev - Pointer to FM object
+ * Param[in]	  enabled - true to bypass frequency compensation;
+ * Return	  0 on success; Error code otherwise.
+ * Cautions	  Must be called after fm_rtc_config and before fm_rt_init ().
+ */
+int fm_rtc_cfg_freq_bypass(struct fm *fm_dev, bool enabled);
+
+/* Function      fm_rtc_cfg_inverted_in_clk_phase
+ * Description	  Configures the RTC to invert the source clock phase on input.
+ *		  [DEFAULT_invertInputClkPhase]
+ * Param[in]	  fm_dev - Pointer to FM object
+ * Param[in]	  inverted - true to invert the source clock phase on input.
+ *		  false otherwise.
+ * Return	  0 on success; Error code otherwise.
+ * Cautions	  Must be called after fm_rtc_config and before fm_rt_init ().
+ */
+int fm_rtc_cfg_inverted_in_clk_phase(struct fm *fm_dev, bool inverted);
+
+/* Function	fm_rtc_cfg_inverted_out_clk_phase
+ * Description	  Configures the RTC to invert the output clock phase.
+ *		  [DEFAULT_invertOutputClkPhase]
+ * Param[in]	  fm_dev - Pointer to FM object
+ * Param[in]	  inverted - true to invert the output clock phase.
+ * Return	  0 on success; Error code otherwise.
+ * Cautions	  Must be called after fm_rtc_config and before fm_rt_init ().
+ */
+int fm_rtc_cfg_inverted_out_clk_phase(struct fm *fm_dev, bool inverted);
+
+/* Function	  fm_rtc_cfg_out_clk_div
+ * Description	  Configures the divisor for generating the output clock from
+ *		  the RTC clock. [DEFAULT_output_clock_divisor]
+ * Param[in]	  fm_dev - Pointer to FM object
+ * Param[in]	  divisor - Divisor for generation of the output clock.
+ * Return	  0 on success; Error code otherwise.
+ * Cautions	  Must be called after fm_rtc_config and before fm_rt_init ().
+ */
+int fm_rtc_cfg_out_clk_div(struct fm *fm_dev, uint16_t div);
+
+/* Function	fm_rtc_cfg_alarm_polarity
+ * Description	  Configures the polarity (active-high/active-low) of a
+ *		  specific alarm signal. [DEFAULT_alarm_polarity]
+ * Param[in]	  fm_dev - Pointer to FM object
+ * Param[in]	  alarm_id - Alarm ID.
+ * Param[in]	  alarm_polarity - Alarm polarity.
+ * Return	  0 on success; Error code otherwise.
+ * Cautions	  Must be called after fm_rtc_config and before fm_rt_init ().
+ */
+int fm_rtc_cfg_alarm_polarity(struct fm *fm_dev, uint8_t alarm_id,
+			      enum fm_rtc_alarm_polarity alarm_polarity);
+
+/* Function	fm_rtc_cfg_extern_trigger_polarity
+ * Description	  Configures the polarity (rising/falling edge) of a specific
+ *		  external trigger signal. [DEFAULT_trigger_polarity]
+ * Param[in]	  fm_dev - Pointer to FM object
+ * Param[in]	  trigger_id - Trigger ID.
+ * Param[in]	  trigger_polarity - Trigger polarity.
+ * Return	  0 on success; Error code otherwise.
+ * Cautions	  Must be called after fm_rtc_config and before fm_rt_init ().
+ */
+int fm_rtc_cfg_extern_trigger_polarity(struct fm *fm_dev, uint8_t trigger_id,
+				       enum fm_rtc_trigger_polarity
+				       trigger_polarity);
+
+/* FM RTC runtime control API. */
+
+/* Function	  fm_rtc_exceptions_cb
+ * Description	  Exceptions user callback routine, used for RTC different
+ *		  mechanisms.
+ * Param[in]	  h_app - User's application descriptor.
+ * Param[in]	  id - source id.
+ */
+typedef void (fm_rtc_exceptions_cb) (void *h_app, uint8_t id);
+
+/* FM RTC alarm parameters */
+struct fm_rtc_alarm_params_t {
+	uint8_t alarm_id;   /*< 0 or 1 */
+	uint64_t alarm_time; /* In nanoseconds, the time when the alarm
+			      * should go off - must be a multiple of
+			      * the RTC period
+			      */
+	/* This routine will be called when RTC reaches alarm_time */
+	fm_rtc_exceptions_cb *f_alarm_callback;
+	bool clear_on_expiration; /* true to turn off the alarm once expired.*/
+};
+
+/* FM RTC Periodic Pulse parameters. */
+struct fm_rtc_periodic_pulse_params_t {
+	/* 0 or 1 */
+	uint8_t periodic_pulse_id;
+	/* In Nanoseconds. Must be a multiple of the RTC period */
+	uint64_t periodic_pulse_period;
+	/* This routine will be called every periodic_pulse_period. */
+	fm_rtc_exceptions_cb *f_periodic_pulse_callback;
+};
+
+/* FM RTC Periodic Pulse parameters. */
+struct fm_rtc_external_trigger_params_t {
+	uint8_t external_trigger_id; /* 0 or 1 */
+	bool use_pulse_as_input; /* Use the pulse interrupt instead of
+				  * an external signal
+				  */
+	/* This routine will be called every periodic_pulse_period. */
+	fm_rtc_exceptions_cb *f_external_trigger_callback;
+};
+
+/* Function	fm_rtc_enable
+ * Description	  Enable the RTC (time count is started).
+ * Param[in]	  fm_dev - Pointer to FM object
+ * Return	  0 on success; Error code otherwise.
+ * Cautions	  Must be called only after fm_rtc_init().
+ */
+int fm_rtc_enable(struct fm *fm_dev);
+
+/* Function	fm_rtc_disable
+ * Description	  Disable the RTC (time count is stopped).
+ * Param[in]	  fm_dev - Pointer to FM object
+ * Return	  0 on success; Error code otherwise.
+ * Cautions	  Must be called only after fm_rtc_init().
+ */
+int fm_rtc_disable(struct fm *fm_dev);
+
+/* Function	fm_rtc_set_clk_offset
+ * Description	  Sets the clock offset (usually relative to another clock).
+ *		  The user can pass a negative offset value.
+ * Param[in]	  fm_dev - Pointer to FM object
+ * Param[in]	  offset - New clock offset (in nanoseconds).
+ * Return	  0 on success; Error code otherwise.
+ * Cautions	  Must be called only after fm_rtc_init().
+ */
+int fm_rtc_set_clk_offset(struct fm *fm_dev, int64_t offset);
+
+/* Function	fm_rtc_set_alarm
+ * Description	  schedules an alarm event to a given RTC time.
+ * Param[in]	  fm_dev - Pointer to FM object
+ * Param[in]	  p_fm_rtc_alarm_params - Alarm parameters.
+ * Return	  0 on success; Error code otherwise.
+ * Cautions	  Must be called only after fm_rtc_init() and prior
+ *		  to fm_rtc_enable().
+ */
+int fm_rtc_set_alarm(struct fm *fm_dev,
+		     struct fm_rtc_alarm_params_t *p_fm_rtc_alarm_params);
+
+/* Function	fm_rtc_set_periodic_pulse
+ * Description	  Sets a periodic pulse.
+ * Param[in]	  fm_dev - Pointer to FM object
+ * Param[in]	  p_fm_rtc_periodic_pulse_params - Periodic pulse parameters.
+ * Return	  0 on success; Error code otherwise.
+ * Cautions	  Must be called only after fm_rtc_init() and prior
+ *		  to fm_rtc_enable().
+ */
+int fm_rtc_set_periodic_pulse(struct fm *fm_dev,
+			      struct fm_rtc_periodic_pulse_params_t *
+			      p_fm_rtc_periodic_pulse_params);
+
+/* Function	  fm_rtc_clear_periodic_pulse
+ * Description	  Clears a periodic pulse.
+ * Param[in]	  fm_dev - Pointer to FM object
+ * Param[in]	  periodic_pulse_id                 - Periodic pulse id.
+ * Return	  0 on success; Error code otherwise.
+ * Cautions	  Must be called only after fm_rtc_init().
+ */
+int fm_rtc_clear_periodic_pulse(struct fm *fm_dev,
+				uint8_t periodic_pulse_id);
+
+/* Function	fm_rtc_set_external_trigger
+ * Description	  Sets an external trigger indication and define a callback
+ *		  routine to be called on such event.
+ * Param[in]	  fm_dev - Pointer to FM object
+ * Param[in]	  p_fm_rtc_external_trigger_params- External Trigger
+ *		  parameters.
+ * Return	  0 on success; Error code otherwise.
+ * Cautions	  Must be called only after fm_rtc_init().
+ */
+int fm_rtc_set_external_trigger(struct fm *fm_dev,
+				struct fm_rtc_external_trigger_params_t *
+				p_fm_rtc_external_trigger_params);
+
+/* Function	fm_rtc_clear_external_trigger
+ * Description	  Clears external trigger indication.
+ * Param[in]	  fm_dev - Pointer to FM object
+ * Param[in]	  id - External Trigger id.
+ * Return	  0 on success; Error code otherwise.
+ * Cautions	  Must be called only after fm_rtc_init().
+ */
+int fm_rtc_clear_external_trigger(struct fm *fm_dev, uint8_t id);
+
+/* Function	  fm_rtc_get_external_trigger_time_stamp
+ * Description	  Reads the External Trigger TimeStamp.
+ * Param[in]	  fm_dev - Pointer to FM object
+ * Param[in]	  trigger_id - External Trigger id.
+ * Param[out]	  p_time_stamp - External Trigger timestamp
+ * Return	  0 on success; Error code otherwise.
+ * Cautions	  Must be called only after fm_rtc_init().
+ */
+int fm_rtc_get_external_trigger_time_stamp(struct fm *fm_dev,
+					   uint8_t trigger_id,
+					   uint64_t *p_time_stamp);
+
+/* Function	fm_rtc_get_cnt
+ * Description	  Returns the current RTC time.
+ * Param[in]	  fm_dev - Pointer to FM object
+ * Param[out]	  p_ts - returned time stamp (in nanoseconds).
+ * Return	  0 on success; Error code otherwise.
+ * Cautions	  Must be called only after fm_rtc_init().
+ */
+int fm_rtc_get_current_time(struct fm *fm_dev, uint64_t *p_ts);
+
+/* Function	  fm_rtc_set_current_time
+ * Description	  Sets the current RTC time.
+ * Param[in]	  fm_dev - Pointer to FM object
+ * Param[in]	  ts - The new time stamp (in nanoseconds).
+ * Return	  0 on success; Error code otherwise.
+ * Cautions	  Must be called only after fm_rtc_init().
+ */
+int fm_rtc_set_current_time(struct fm *fm_dev, uint64_t ts);
+
+/* Function	fm_rtc_get_freq_compensation
+ * Description	  Retrieves the frequency compensation value
+ * Param[in]	  fm_dev - Pointer to FM object
+ * Param[out]	  p_compensation - A pointer to the returned value of
+ *		  compensation.
+ * Return	  0 on success; Error code otherwise.
+ * Cautions	  Must be called only after fm_rtc_init().
+ */
+int fm_rtc_get_freq_compensation(struct fm *fm_dev,
+				 uint32_t *p_compensation);
+
+/* Function	fm_rtc_set_freq_compensation
+ * Description	  Sets a new frequency compensation value.
+ * Param[in]	  fm_dev - Pointer to FM object
+ * Param[in]	  freq_compensation - The new frequency compensation value
+ *		  to set.
+ * Return	  0 on success; Error code otherwise.
+ * Cautions	  Must be called only after fm_rtc_init().
+ */
+int fm_rtc_set_freq_compensation(struct fm *fm_dev,
+				 uint32_t freq_compensation);
+
+#endif /* __FM_RTC_EXT_H__ */
diff --git a/drivers/soc/fsl/fman/inc/fsl_fman_drv.h b/drivers/soc/fsl/fman/inc/fsl_fman_drv.h
index a0b88c2..4bef301 100644
--- a/drivers/soc/fsl/fman/inc/fsl_fman_drv.h
+++ b/drivers/soc/fsl/fman/inc/fsl_fman_drv.h
@@ -62,6 +62,9 @@ struct fm *fm_bind(struct device *fm_dev);
 void fm_unbind(struct fm *fm);
 
 void *fm_get_handle(struct fm *fm);
+#ifdef CONFIG_FSL_FMAN_RTC
+void *fm_get_rtc_handle(struct fm *fm);
+#endif
 struct resource *fm_get_mem_region(struct fm *fm);
 
 /*	fm_mutex_lock
diff --git a/drivers/soc/fsl/fman/rtc/Makefile b/drivers/soc/fsl/fman/rtc/Makefile
index c8b4477..b4b2fce 100644
--- a/drivers/soc/fsl/fman/rtc/Makefile
+++ b/drivers/soc/fsl/fman/rtc/Makefile
@@ -1,3 +1,3 @@
 obj-$(CONFIG_FSL_FMAN_RTC)	+= fsl_fman_rtc.o
 
-fsl_fman_rtc-objs		:= fman_rtc.o
+fsl_fman_rtc-objs		:= fman_rtc.o fm_rtc.o
diff --git a/drivers/soc/fsl/fman/rtc/fm_rtc.c b/drivers/soc/fsl/fman/rtc/fm_rtc.c
new file mode 100644
index 0000000..dc64527
--- /dev/null
+++ b/drivers/soc/fsl/fman/rtc/fm_rtc.c
@@ -0,0 +1,763 @@
+/*
+ * Copyright 2008 - 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 Freescale Semiconductor nor the
+ *	 names of its 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 Freescale Semiconductor ``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 Freescale Semiconductor 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "service.h"
+
+#include "fm_rtc.h"
+#include "fm_common.h"
+
+#include <linux/string.h>
+#include <linux/slab.h>
+
+static int check_init_parameters(struct fm_rtc_t *p_rtc)
+{
+	struct rtc_cfg *p_rtc_drv_param = p_rtc->p_rtc_drv_param;
+	int i;
+
+	if ((p_rtc_drv_param->src_clk != FMAN_RTC_SOURCE_CLOCK_EXTERNAL) &&
+	    (p_rtc_drv_param->src_clk != FMAN_RTC_SOURCE_CLOCK_SYSTEM) &&
+	    (p_rtc_drv_param->src_clk != FMAN_RTC_SOURCE_CLOCK_OSCILATOR)) {
+		pr_err("Source clock undefined\n");
+		return -EINVAL;
+	}
+	if (p_rtc->output_clock_divisor == 0) {
+		pr_err("Divisor for output clock (should be positive)\n");
+		return -EDOM;
+	}
+
+	for (i = 0; i < p_rtc->rtc_intg.fm_rtc_num_of_alarms; i++) {
+		if ((p_rtc_drv_param->alarm_polarity[i] !=
+		     FMAN_RTC_ALARM_POLARITY_ACTIVE_LOW) &&
+		    (p_rtc_drv_param->alarm_polarity[i] !=
+		     FMAN_RTC_ALARM_POLARITY_ACTIVE_HIGH)) {
+			pr_err("Alarm %d signal polarity\n", i);
+			return -EBADRQC;
+		}
+	}
+	for (i = 0; i < p_rtc->rtc_intg.fm_rtc_num_of_ext_triggers; i++) {
+		if ((p_rtc_drv_param->trigger_polarity[i] !=
+		     FMAN_RTC_TRIGGER_ON_FALLING_EDGE) &&
+		     (p_rtc_drv_param->trigger_polarity[i] !=
+			FMAN_RTC_TRIGGER_ON_RISING_EDGE)) {
+			pr_err("Trigger %d signal polarity\n", i);
+			return -EBADRQC;
+		}
+	}
+
+	return 0;
+}
+
+/* Checks if p_rtc driver parameters were initialized
+ * returns 0 if success else returns error code
+ */
+static int is_init_done(struct rtc_cfg *p_rtc_drv_parameters)
+{
+	if (!p_rtc_drv_parameters)
+		return 0;
+	return -ENOSYS;
+}
+
+static void rtc_exceptions(void *h_fm_rtc)
+{
+	struct fm_rtc_t *p_rtc = (struct fm_rtc_t *)h_fm_rtc;
+	struct rtc_regs __iomem *p_mem_map;
+
+	register uint32_t events;
+
+	p_mem_map = p_rtc->p_mem_map;
+
+	events = fman_rtc_check_and_clear_event(p_mem_map);
+	if (events&FMAN_RTC_TMR_TEVENT_ALM1) {
+		if (p_rtc->alarm_params[0].clear_on_expiration) {
+			fman_rtc_set_timer_alarm_l(p_mem_map, 0, 0);
+			fman_rtc_disable_interrupt(p_mem_map,
+						   FMAN_RTC_TMR_TEVENT_ALM1);
+		}
+		ASSERT(p_rtc->alarm_params[0].f_alarm_callback);
+		p_rtc->alarm_params[0].f_alarm_callback(p_rtc->h_app, 0);
+	}
+	if (events&FMAN_RTC_TMR_TEVENT_ALM2) {
+		if (p_rtc->alarm_params[1].clear_on_expiration) {
+			fman_rtc_set_timer_alarm_l(p_mem_map, 1, 0);
+			fman_rtc_disable_interrupt(p_mem_map,
+						   FMAN_RTC_TMR_TEVENT_ALM2);
+		}
+		ASSERT(p_rtc->alarm_params[1].f_alarm_callback);
+		p_rtc->alarm_params[1].f_alarm_callback(p_rtc->h_app, 1);
+	}
+	if (events&FMAN_RTC_TMR_TEVENT_PP1) {
+		ASSERT(p_rtc->periodic_pulse_params[0].
+			    f_periodic_pulse_callback);
+		p_rtc->
+		periodic_pulse_params[0].f_periodic_pulse_callback(p_rtc->
+								   h_app, 0);
+	}
+	if (events&FMAN_RTC_TMR_TEVENT_PP2) {
+		ASSERT(p_rtc->periodic_pulse_params[1].
+			    f_periodic_pulse_callback);
+		p_rtc->
+		periodic_pulse_params[1].f_periodic_pulse_callback(p_rtc->
+								   h_app, 1);
+	}
+	if (events&FMAN_RTC_TMR_TEVENT_ETS1) {
+		ASSERT(p_rtc->external_trigger_params[0].
+			    f_external_trigger_callback);
+		p_rtc->external_trigger_params[0].
+		    f_external_trigger_callback(p_rtc->h_app, 0);
+	}
+	if (events&FMAN_RTC_TMR_TEVENT_ETS2) {
+		ASSERT(p_rtc->external_trigger_params[1].
+			    f_external_trigger_callback);
+		p_rtc->external_trigger_params[1].
+		    f_external_trigger_callback(p_rtc->h_app, 1);
+	}
+}
+
+static void set_rtc_intg_params(struct fm_rtc_t *p_rtc)
+{
+	struct fm_revision_info_t rev_info;
+
+	fm_get_revision(p_rtc->h_fm, &rev_info);
+
+	p_rtc->rtc_intg.fm_rtc_num_of_alarms = 2;
+	p_rtc->rtc_intg.fm_rtc_num_of_ext_triggers = 2;
+
+	if (rev_info.major_rev == 6)
+		p_rtc->rtc_intg.fm_rtc_num_of_periodic_pulses = 3;
+	else
+		p_rtc->rtc_intg.fm_rtc_num_of_periodic_pulses = 2;
+}
+
+void *fm_get_rtc_handle(struct fm *fm);
+
+void *fm_rtc_config(struct fm_rtc_params_t *p_fm_rtc_param)
+{
+	struct fm_rtc_t *p_rtc;
+
+	/* Allocate memory for the FM RTC driver parameters */
+	p_rtc = kzalloc(sizeof(*p_rtc), GFP_KERNEL);
+	if (!p_rtc)
+		return NULL;
+
+	/* Allocate memory for the FM RTC driver parameters */
+	p_rtc->p_rtc_drv_param = kzalloc(sizeof(*p_rtc->p_rtc_drv_param),
+					  GFP_KERNEL);
+	if (!p_rtc->p_rtc_drv_param) {
+		kfree(p_rtc);
+		return NULL;
+	}
+
+	/* Store RTC configuration parameters */
+	p_rtc->h_fm = p_fm_rtc_param->h_fm;
+
+	/* Set default RTC configuration parameters */
+	fman_rtc_defconfig(p_rtc->p_rtc_drv_param);
+
+	p_rtc->output_clock_divisor = DEFAULT_OUTPUT_CLOCK_DIVISOR;
+	p_rtc->p_rtc_drv_param->bypass = DEFAULT_BYPASS;
+	p_rtc->clk_period_nano_sec = DEFAULT_CLOCK_PERIOD;	/* 1 usec */
+
+	/* Store RTC parameters in the RTC control structure */
+	p_rtc->p_mem_map = (struct rtc_regs __iomem *)
+	    UINT_TO_PTR(p_fm_rtc_param->base_address);
+	p_rtc->h_app = p_fm_rtc_param->h_app;
+
+	set_rtc_intg_params(p_rtc);
+
+	return p_rtc;
+}
+
+int fm_rtc_init(struct fm *fm_dev)
+{
+	struct fm_rtc_t *p_rtc = fm_get_rtc_handle(fm_dev);
+	struct rtc_cfg *p_rtc_drv_param;
+	struct rtc_regs __iomem *p_mem_map;
+	uint32_t freq_compensation = 0;
+	uint64_t tmp_double;
+	bool init_freq_comp = false;
+
+	p_rtc_drv_param = p_rtc->p_rtc_drv_param;
+	p_mem_map = p_rtc->p_mem_map;
+
+	if (check_init_parameters(p_rtc) != 0) {
+		pr_err("Init Parameters are not Valid\n");
+		return -EINVAL;
+	}
+	/* TODO check that no timestamping MACs are working in this stage. */
+
+	/* find source clock frequency in Mhz */
+	if (p_rtc->p_rtc_drv_param->src_clk != FMAN_RTC_SOURCE_CLOCK_SYSTEM)
+		p_rtc->src_clk_freq_mhz =
+		    p_rtc->p_rtc_drv_param->ext_src_clk_freq;
+	else
+		p_rtc->src_clk_freq_mhz =
+		    (uint32_t)(fm_get_clock_freq(p_rtc->h_fm) / 2);
+
+	/* if timer in Master mode Initialize TMR_CTRL */
+	/* We want the counter (TMR_CNT) to count in nano-seconds */
+	if (!p_rtc_drv_param->timer_slave_mode &&
+	    p_rtc->p_rtc_drv_param->bypass)
+		p_rtc->clk_period_nano_sec = (1000 / p_rtc->src_clk_freq_mhz);
+	else {
+		/* Initialize TMR_ADD with the initial frequency compensation
+		 * value:freq_compensation = (2^32 / frequency ratio)
+		 * frequency ratio = sorce clock/rtc clock =
+		 * (p_rtc->src_clk_freq_mhz*1000000))/ 1/(p_rtc->
+		 * clk_period_nano_sec*1000000000)
+		 */
+		init_freq_comp = true;
+		freq_compensation =
+		    (uint32_t)DIV_CEIL(ACCUMULATOR_OVERFLOW*1000,
+					p_rtc->clk_period_nano_sec *
+					p_rtc->src_clk_freq_mhz);
+	}
+
+	/* check the legality of the relation between source
+	 * and destination clocks
+	 */
+	/* should be larger than 1.0001 */
+	tmp_double = 10000*(uint64_t)p_rtc->clk_period_nano_sec *
+					(uint64_t)p_rtc->src_clk_freq_mhz;
+	if ((tmp_double) <= 10001) {
+		pr_err("Relation bet src, dest clk should be >1.0001\n");
+		return -EINVAL;
+	}
+	fman_rtc_init(p_rtc_drv_param,
+		      p_mem_map,
+		      p_rtc->rtc_intg.fm_rtc_num_of_alarms,
+		      p_rtc->rtc_intg.fm_rtc_num_of_periodic_pulses,
+		      p_rtc->rtc_intg.fm_rtc_num_of_ext_triggers,
+		      init_freq_comp,
+		      freq_compensation,
+		      p_rtc->output_clock_divisor);
+
+	/* Register the FM RTC interrupt */
+	fm_register_intr(p_rtc->h_fm, FM_MOD_TMR, 0, FM_INTR_TYPE_NORMAL,
+			 rtc_exceptions, p_rtc);
+
+	/* Free parameters structures */
+	kfree(p_rtc->p_rtc_drv_param);
+	p_rtc->p_rtc_drv_param = NULL;
+
+	return 0;
+}
+
+int fm_rtc_free(struct fm *fm_dev)
+{
+	struct fm_rtc_t *p_rtc = fm_get_rtc_handle(fm_dev);
+
+	if (!p_rtc->p_rtc_drv_param)
+		fman_rtc_disable(p_rtc->p_mem_map);
+	else
+		kfree(p_rtc->p_rtc_drv_param);
+
+	/* Unregister FM RTC interrupt */
+	fm_unregister_intr(p_rtc->h_fm, FM_MOD_TMR, 0, FM_INTR_TYPE_NORMAL);
+	kfree(p_rtc);
+
+	return 0;
+}
+
+int fm_rtc_cfg_src_clk(struct fm *fm_dev, enum fm_src_clock src_clk,
+		       uint32_t freq_in_mhz)
+{
+	struct fm_rtc_t *p_rtc = fm_get_rtc_handle(fm_dev);
+	int ret;
+
+	ret = is_init_done(p_rtc->p_rtc_drv_param);
+	if (!ret)
+		return -ENOSYS;
+
+	p_rtc->p_rtc_drv_param->src_clk = (enum fman_src_clock)src_clk;
+	if (src_clk != FM_RTC_SOURCE_CLOCK_SYSTEM)
+		p_rtc->p_rtc_drv_param->ext_src_clk_freq = freq_in_mhz;
+
+	return 0;
+}
+
+int fm_rtc_cfg_period(struct fm *fm_dev, uint32_t period)
+{
+	struct fm_rtc_t *p_rtc = fm_get_rtc_handle(fm_dev);
+	int ret;
+
+	ret = is_init_done(p_rtc->p_rtc_drv_param);
+	if (!ret)
+		return -ENOSYS;
+
+	p_rtc->clk_period_nano_sec = period;
+
+	return 0;
+}
+
+int fm_rtc_cfg_freq_bypass(struct fm *fm_dev, bool enabled)
+{
+	struct fm_rtc_t *p_rtc = fm_get_rtc_handle(fm_dev);
+	int ret;
+
+	ret = is_init_done(p_rtc->p_rtc_drv_param);
+	if (!ret)
+		return -ENOSYS;
+
+	p_rtc->p_rtc_drv_param->bypass = enabled;
+
+	return 0;
+}
+
+int fm_rtc_cfg_inverted_in_clk_phase(struct fm *fm_dev, bool inverted)
+{
+	struct fm_rtc_t *p_rtc = fm_get_rtc_handle(fm_dev);
+	int ret;
+
+	ret = is_init_done(p_rtc->p_rtc_drv_param);
+	if (!ret)
+		return -ENOSYS;
+
+	p_rtc->p_rtc_drv_param->invert_input_clk_phase = inverted;
+
+	return 0;
+}
+
+int fm_rtc_cfg_inverted_out_clk_phase(struct fm *fm_dev, bool inverted)
+{
+	struct fm_rtc_t *p_rtc = fm_get_rtc_handle(fm_dev);
+	int ret;
+
+	ret = is_init_done(p_rtc->p_rtc_drv_param);
+	if (!ret)
+		return -ENOSYS;
+
+	p_rtc->p_rtc_drv_param->invert_output_clk_phase = inverted;
+
+	return 0;
+}
+
+int fm_rtc_cfg_out_clk_div(struct fm *fm_dev, uint16_t div)
+{
+	struct fm_rtc_t *p_rtc = fm_get_rtc_handle(fm_dev);
+	int ret;
+
+	ret = is_init_done(p_rtc->p_rtc_drv_param);
+	if (!ret)
+		return -ENOSYS;
+
+	p_rtc->output_clock_divisor = div;
+
+	return 0;
+}
+
+int fm_rtc_cfg_pulse_realign(struct fm *fm_dev, bool enable)
+{
+	struct fm_rtc_t *p_rtc = fm_get_rtc_handle(fm_dev);
+	int ret;
+
+	ret = is_init_done(p_rtc->p_rtc_drv_param);
+	if (!ret)
+		return -ENOSYS;
+
+	p_rtc->p_rtc_drv_param->pulse_realign = enable;
+
+	return 0;
+}
+
+int fm_rtc_cfg_alarm_polarity(struct fm *fm_dev, uint8_t alarm_id,
+			      enum fm_rtc_alarm_polarity alarm_polarity)
+{
+	struct fm_rtc_t *p_rtc = fm_get_rtc_handle(fm_dev);
+	int ret;
+
+	ret = is_init_done(p_rtc->p_rtc_drv_param);
+	if (!ret)
+		return -ENOSYS;
+
+	if (alarm_id >= p_rtc->rtc_intg.fm_rtc_num_of_alarms) {
+		pr_err("Alarm ID\n");
+		return -EBADRQC;
+	}
+
+	p_rtc->p_rtc_drv_param->alarm_polarity[alarm_id] =
+	    (enum fman_rtc_alarm_polarity)alarm_polarity;
+
+	return 0;
+}
+
+int fm_rtc_cfg_extern_trigger_polarity(struct fm *fm_dev, uint8_t trigger_id,
+				       enum fm_rtc_trigger_polarity
+				       trigger_polarity)
+{
+	struct fm_rtc_t *p_rtc = fm_get_rtc_handle(fm_dev);
+	int ret;
+
+	ret = is_init_done(p_rtc->p_rtc_drv_param);
+	if (!ret)
+		return -ENOSYS;
+
+	if (trigger_id >= p_rtc->rtc_intg.fm_rtc_num_of_ext_triggers) {
+		pr_err("External trigger ID\n");
+		return -EBADRQC;
+	}
+
+	p_rtc->p_rtc_drv_param->trigger_polarity[trigger_id] =
+	    (enum fman_rtc_trigger_polarity)trigger_polarity;
+
+	return 0;
+}
+
+int fm_rtc_enable(struct fm *fm_dev)
+{
+	bool reset_clock = 0;
+	struct fm_rtc_t *p_rtc = fm_get_rtc_handle(fm_dev);
+	int ret;
+
+	ret = is_init_done(p_rtc->p_rtc_drv_param);
+	if (ret)
+		return ret;
+
+	fman_rtc_enable(p_rtc->p_mem_map, reset_clock);
+	return 0;
+}
+EXPORT_SYMBOL(fm_rtc_enable);
+
+int fm_rtc_disable(struct fm *fm_dev)
+{
+	struct fm_rtc_t *p_rtc = fm_get_rtc_handle(fm_dev);
+	int ret;
+
+	ret = is_init_done(p_rtc->p_rtc_drv_param);
+	if (ret)
+		return ret;
+
+	/* TODO A check must be added here, that no timestamping MAC's
+	 * are working in this stage.
+	 */
+	fman_rtc_disable(p_rtc->p_mem_map);
+	return 0;
+}
+EXPORT_SYMBOL(fm_rtc_disable);
+
+int fm_rtc_set_clk_offset(struct fm *fm_dev, int64_t offset)
+{
+	struct fm_rtc_t *p_rtc = fm_get_rtc_handle(fm_dev);
+	int ret;
+
+	ret = is_init_done(p_rtc->p_rtc_drv_param);
+	if (ret)
+		return ret;
+
+	fman_rtc_set_timer_offset(p_rtc->p_mem_map, offset);
+	return 0;
+}
+
+int fm_rtc_set_alarm(struct fm *fm_dev,
+		     struct fm_rtc_alarm_params_t *p_fm_rtc_alarm_params)
+{
+	struct fm_rtc_t *p_rtc = fm_get_rtc_handle(fm_dev);
+	uint64_t tmp_alarm;
+	bool enable = false;
+	int ret;
+	u64 reminder;
+
+	ret = is_init_done(p_rtc->p_rtc_drv_param);
+	if (ret)
+		return ret;
+
+	if (p_fm_rtc_alarm_params->alarm_id >=
+		p_rtc->rtc_intg.fm_rtc_num_of_alarms) {
+		pr_err("Alarm ID\n");
+		return -EBADRQC;
+	}
+	if (p_fm_rtc_alarm_params->alarm_time < p_rtc->clk_period_nano_sec) {
+		pr_err("Alarm time must be >= RTC period - %d ns\n",
+		       p_rtc->clk_period_nano_sec);
+		return -EBADRQC;
+	}
+
+	div64_u64_rem(p_fm_rtc_alarm_params->alarm_time,
+		      (uint64_t)p_rtc->clk_period_nano_sec,
+		      &reminder);
+
+	if (reminder) {
+		pr_err("Alarm time must be multiple of RTCperiod-%d ns\n",
+		       p_rtc->clk_period_nano_sec);
+		return -EBADRQC;
+	}
+	tmp_alarm = div64_u64(p_fm_rtc_alarm_params->alarm_time,
+			      (uint64_t)p_rtc->clk_period_nano_sec);
+
+	if (p_fm_rtc_alarm_params->f_alarm_callback) {
+		p_rtc->alarm_params[p_fm_rtc_alarm_params->alarm_id].
+		    f_alarm_callback = p_fm_rtc_alarm_params->f_alarm_callback;
+		p_rtc->alarm_params[p_fm_rtc_alarm_params->alarm_id].
+		    clear_on_expiration = p_fm_rtc_alarm_params->
+					  clear_on_expiration;
+		enable = true;
+	}
+
+	fman_rtc_set_alarm(p_rtc->p_mem_map, p_fm_rtc_alarm_params->alarm_id,
+			   (unsigned long)tmp_alarm, enable);
+
+	return 0;
+}
+EXPORT_SYMBOL(fm_rtc_set_alarm);
+
+int fm_rtc_set_periodic_pulse(struct fm *fm_dev,
+			      struct fm_rtc_periodic_pulse_params_t *
+			      p_fm_rtc_periodic_pulse_params)
+{
+	struct fm_rtc_t *p_rtc = fm_get_rtc_handle(fm_dev);
+	bool enable = false;
+	uint64_t tmp_fiper;
+	int ret;
+	u64 reminder;
+
+	ret = is_init_done(p_rtc->p_rtc_drv_param);
+	if (ret)
+		return ret;
+
+	if (p_fm_rtc_periodic_pulse_params->periodic_pulse_id >=
+	    p_rtc->rtc_intg.fm_rtc_num_of_periodic_pulses) {
+		pr_err("Periodic pulse ID\n");
+		return -EBADRQC;
+	}
+	if (fman_rtc_is_enabled(p_rtc->p_mem_map)) {
+		pr_err("Can't set Periodic pulse when RTC is enable.\n");
+		return -EBADRQC;
+	}
+	if (p_fm_rtc_periodic_pulse_params->periodic_pulse_period <
+	      p_rtc->clk_period_nano_sec) {
+		pr_err("Periodic pulse must be >= RTC period - %d ns\n",
+		       p_rtc->clk_period_nano_sec);
+		return -EBADRQC;
+	}
+	div64_u64_rem(p_fm_rtc_periodic_pulse_params->periodic_pulse_period,
+		      (uint64_t)p_rtc->clk_period_nano_sec,
+		      &reminder);
+	if (reminder) {
+		pr_err("Periodic pulse is multiple of RTC period-%dns\n",
+		       p_rtc->clk_period_nano_sec);
+		return -EBADRQC;
+	}
+	tmp_fiper = div64_u64(
+			p_fm_rtc_periodic_pulse_params->periodic_pulse_period,
+			(uint64_t)p_rtc->clk_period_nano_sec);
+	if (tmp_fiper&0xffffffff00000000LL) {
+		pr_err("Periodic pulse/RTC Period (=%d) > 4294967296\n",
+		       p_rtc->clk_period_nano_sec);
+		return -EBADRQC;
+	}
+	if (p_fm_rtc_periodic_pulse_params->f_periodic_pulse_callback) {
+		p_rtc->periodic_pulse_params[p_fm_rtc_periodic_pulse_params->
+		periodic_pulse_id].f_periodic_pulse_callback =
+		    p_fm_rtc_periodic_pulse_params->f_periodic_pulse_callback;
+		    enable = true;
+	}
+	fman_rtc_set_periodic_pulse(p_rtc->p_mem_map,
+				    p_fm_rtc_periodic_pulse_params->
+				    periodic_pulse_id,
+				    (uint32_t)tmp_fiper, enable);
+	return 0;
+}
+EXPORT_SYMBOL(fm_rtc_set_periodic_pulse);
+
+int fm_rtc_clear_periodic_pulse(struct fm *fm_dev,
+				uint8_t periodic_pulse_id)
+{
+	struct fm_rtc_t *p_rtc = fm_get_rtc_handle(fm_dev);
+	int ret;
+
+	ret = is_init_done(p_rtc->p_rtc_drv_param);
+	if (ret)
+		return ret;
+
+	if (periodic_pulse_id >=
+		p_rtc->rtc_intg.fm_rtc_num_of_periodic_pulses) {
+		pr_err("Periodic pulse ID\n");
+		return -EBADRQC;
+	}
+
+	p_rtc->
+	periodic_pulse_params[periodic_pulse_id].f_periodic_pulse_callback =
+	    NULL;
+	fman_rtc_clear_periodic_pulse(p_rtc->p_mem_map, periodic_pulse_id);
+
+	return 0;
+}
+
+int fm_rtc_set_external_trigger(struct fm *fm_dev,
+				struct fm_rtc_external_trigger_params_t *
+				p_fm_rtc_external_trigger_params)
+{
+	struct fm_rtc_t *p_rtc = fm_get_rtc_handle(fm_dev);
+	bool enable = false;
+	int ret;
+
+	ret = is_init_done(p_rtc->p_rtc_drv_param);
+	if (ret)
+		return ret;
+
+	if (p_fm_rtc_external_trigger_params->external_trigger_id >=
+	    p_rtc->rtc_intg.fm_rtc_num_of_ext_triggers) {
+		pr_err("External Trigger ID\n");
+		return -EBADRQC;
+	}
+
+	if (p_fm_rtc_external_trigger_params->f_external_trigger_callback) {
+		p_rtc->
+		external_trigger_params[p_fm_rtc_external_trigger_params->
+					     external_trigger_id].
+		    f_external_trigger_callback =
+		    p_fm_rtc_external_trigger_params->
+		    f_external_trigger_callback;
+		enable = true;
+	}
+
+	fman_rtc_set_ext_trigger(p_rtc->p_mem_map,
+				 p_fm_rtc_external_trigger_params->
+				 external_trigger_id, enable,
+				 p_fm_rtc_external_trigger_params->
+				 use_pulse_as_input);
+	return 0;
+}
+
+int fm_rtc_clear_external_trigger(struct fm *fm_dev,
+				  uint8_t external_trigger_id)
+{
+	struct fm_rtc_t *p_rtc = fm_get_rtc_handle(fm_dev);
+	int ret;
+
+	ret = is_init_done(p_rtc->p_rtc_drv_param);
+	if (ret)
+		return ret;
+
+	if (external_trigger_id >= p_rtc->rtc_intg.
+	    fm_rtc_num_of_ext_triggers) {
+		pr_err("External Trigger ID\n");
+		return -EBADRQC;
+	}
+
+	p_rtc->external_trigger_params[external_trigger_id].
+	    f_external_trigger_callback = NULL;
+
+	fman_rtc_clear_external_trigger(p_rtc->p_mem_map, external_trigger_id);
+
+	return 0;
+}
+
+int fm_rtc_get_external_trigger_time_stamp(struct fm *fm_dev,
+					   uint8_t trigger_id,
+					   uint64_t *p_time_stamp)
+{
+	struct fm_rtc_t *p_rtc = fm_get_rtc_handle(fm_dev);
+	int ret;
+
+	ret = is_init_done(p_rtc->p_rtc_drv_param);
+	if (ret)
+		return ret;
+
+	if (trigger_id >= p_rtc->rtc_intg.fm_rtc_num_of_ext_triggers) {
+		pr_err("External trigger ID");
+		return -EBADRQC;
+	}
+	*p_time_stamp =
+	    fman_rtc_get_trigger_stamp(p_rtc->p_mem_map,
+				       trigger_id)*p_rtc->
+				       clk_period_nano_sec;
+
+	return 0;
+}
+
+int fm_rtc_get_current_time(struct fm *fm_dev, uint64_t *p_ts)
+{
+	struct fm_rtc_t *p_rtc = fm_get_rtc_handle(fm_dev);
+	int ret;
+
+	ret = is_init_done(p_rtc->p_rtc_drv_param);
+	if (ret)
+		return ret;
+
+	*p_ts = fman_rtc_get_timer(p_rtc->p_mem_map) *
+						p_rtc->clk_period_nano_sec;
+
+	return 0;
+}
+EXPORT_SYMBOL(fm_rtc_get_current_time);
+
+int fm_rtc_set_current_time(struct fm *fm_dev, uint64_t ts)
+{
+	struct fm_rtc_t *p_rtc = fm_get_rtc_handle(fm_dev);
+	int ret;
+
+	ret = is_init_done(p_rtc->p_rtc_drv_param);
+	if (ret)
+		return ret;
+
+	ts = div64_u64(ts, p_rtc->clk_period_nano_sec);
+	fman_rtc_set_timer(p_rtc->p_mem_map, (unsigned long)ts);
+
+	return 0;
+}
+EXPORT_SYMBOL(fm_rtc_set_current_time);
+
+int fm_rtc_get_freq_compensation(struct fm *fm_dev,
+				 uint32_t *p_compensation)
+{
+	struct fm_rtc_t *p_rtc = fm_get_rtc_handle(fm_dev);
+	int ret;
+
+	ret = is_init_done(p_rtc->p_rtc_drv_param);
+	if (ret)
+		return ret;
+
+	*p_compensation =
+			fman_rtc_get_frequency_compensation(p_rtc->p_mem_map);
+
+	return 0;
+}
+EXPORT_SYMBOL(fm_rtc_get_freq_compensation);
+
+int fm_rtc_set_freq_compensation(struct fm *fm_dev,
+				 uint32_t freq_compensation)
+{
+	struct fm_rtc_t *p_rtc = fm_get_rtc_handle(fm_dev);
+	int ret;
+
+	ret = is_init_done(p_rtc->p_rtc_drv_param);
+	if (ret)
+		return ret;
+
+	/* set the new freq_compensation */
+	fman_rtc_set_frequency_compensation(p_rtc->p_mem_map,
+					    freq_compensation);
+
+	return 0;
+}
+EXPORT_SYMBOL(fm_rtc_set_freq_compensation);
diff --git a/drivers/soc/fsl/fman/rtc/fm_rtc.h b/drivers/soc/fsl/fman/rtc/fm_rtc.h
new file mode 100644
index 0000000..c4774e7
--- /dev/null
+++ b/drivers/soc/fsl/fman/rtc/fm_rtc.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2008 - 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 Freescale Semiconductor nor the
+ *	 names of its 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 Freescale Semiconductor ``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 Freescale Semiconductor 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.
+ */
+
+/* Memory map and internal definitions for FM RTC IEEE1588 Timer driver. */
+
+#ifndef __FM_RTC_H__
+#define __FM_RTC_H__
+
+#include "service.h"
+#include "fm_rtc_ext.h"
+#include "dpaa_integration_ext.h"
+
+/* General definitions */
+
+#define ACCUMULATOR_OVERFLOW            ((uint64_t)(1LL << 32))
+#define DEFAULT_OUTPUT_CLOCK_DIVISOR     0x00000002
+#define DEFAULT_BYPASS			 false
+#define DEFAULT_CLOCK_PERIOD             1000
+
+struct fm_rtc_alarm_t {
+	fm_rtc_exceptions_cb *f_alarm_callback;
+	bool clear_on_expiration;
+};
+
+struct fm_rtc_periodic_pulse_t {
+	fm_rtc_exceptions_cb *f_periodic_pulse_callback;
+};
+
+struct fm_rtc_external_trigger_t {
+	fm_rtc_exceptions_cb *f_external_trigger_callback;
+};
+
+struct fm_rtc_intg_t {
+	uint32_t fm_rtc_num_of_alarms;
+	uint32_t fm_rtc_num_of_periodic_pulses;
+	uint32_t fm_rtc_num_of_ext_triggers;
+};
+
+/* RTC FM driver control structure. */
+struct fm_rtc_t {
+	void *h_fm;
+	/* Application handle */
+	void *h_app;
+	struct rtc_regs __iomem *p_mem_map;
+	/* RTC clock period in nano-seconds (for FS mode) */
+	uint32_t clk_period_nano_sec;
+	uint32_t src_clk_freq_mhz;
+	/* Output clock divisor (for FS mode) */
+	uint16_t output_clock_divisor;
+	struct fm_rtc_alarm_t alarm_params[FM_RTC_NUM_OF_ALARMS];
+	struct fm_rtc_periodic_pulse_t
+	    periodic_pulse_params[FM_RTC_NUM_OF_PERIODIC_PULSES];
+	struct fm_rtc_external_trigger_t
+	    external_trigger_params[FM_RTC_NUM_OF_EXT_TRIGGERS];
+	/* RTC Driver parameters (for Init phase) */
+	struct rtc_cfg *p_rtc_drv_param;
+	/* RTC Driver integration params */
+	struct fm_rtc_intg_t rtc_intg;
+};
+
+#endif /* __FM_RTC_H__ */
-- 
1.7.9.5



More information about the Linuxppc-dev mailing list