/*
 *      Copyright (c) 2000 Nils Faerber
 *      Copyright 2004 Freescale Semiconductor, Inc. All Rights Reserved.
 *
 *      System Timer Interrupt reconfigured to run in free-run mode.
 *      Author: Vitaly Wool
 *      Copyright 2004 MontaVista Software Inc.
 *
 *      Based on rtc.c by Paul Gortmaker
 *      Date/time conversion routines taken from arch/arm/kernel/time.c
 *                      by Linus Torvalds and Russel King
 *              and the GNU C Library
 *      ( ... I love the GPL ... just take what you need! ;)
 *
 *      This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License
 *      as published by the Free Software Foundation; either version
 *      2 of the License, or (at your option) any later version.
 *
 *      1.00    2001-06-08      Nicolas Pitre <nico@cam.org>
 *      - added periodic timer capability using OSMR1
 *      - flag compatibility with other RTC chips
 *      - permission checks for ioctls
 *      - major cleanup, partial rewrite
 *
 *      0.03    2001-03-07      CIH <cih@coventive.com>
 *      - Modify the bug setups RTC clock.
 *
 *      0.02    2001-02-27      Nils Faerber <nils@@kernelconcepts.de>
 *      - removed mktime(), added alarm irq clear
 *
 *      0.01    2000-10-01      Nils Faerber <nils@@kernelconcepts.de>
 *      - initial release
 */

/*!
 * @file time.c
 * @brief This file contains RTC, OS tick and wdog timer implementations.
 *
 * This file contains RTC, OS tick and wdog timer implementations.
 *
 * @ingroup Timers
 */

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/proc_fs.h>
#include <linux/interrupt.h>
#include <linux/rtc.h>
#include <linux/errno.h>
#include <linux/timex.h>
#include <linux/signal.h>
#include <linux/time.h>
#include <linux/hrtime.h>

#include <asm/bitops.h>
#include <asm/hardware.h>
#include <asm/irq.h>
#include <asm/rtc.h>
#include <asm/system.h>
#include <asm/leds.h>
#include <asm/arch/platform.h>
#include <asm/mach/time.h>

/*
 * EPIT Control register bit definitions
 */
#define EPITCR_CLKSRC_OFF		0x00000000
#define EPITCR_CLKSRC_HIGHFREQ		0x02000000
#define EPITCR_CLKSRC_32K		0x03000000

#define EPITCR_OM_DISCONNECTED		0x00000000
#define EPITCR_OM_TOGGLE		0x00400000
#define EPITCR_OM_CLEAR			0x00800000
#define EPITCR_OM_SET			0x00C00000

#define EPITCR_STOPEN_ENABLE		0x00200000
#define EPITCR_WAITEN_ENABLE		0x00080000
#define EPITCR_DBGEN_ENABLE		0x00040000
#define EPITCR_IOVW_ENABLE		0x00020000
#define EPITCR_SWR			0x00010000

#define EPITCR_PRESCALER_MASK		0x0000FFF0
#define EPITCR_PRESCALER_SHIFT		0x00000004

#define EPITCR_RLD			0x00000008
#define EPITCR_OCIEN			0x00000004
#define EPITCR_ENMOD			0x00000002
#define EPITCR_EN			0x00000001

/*!
 * GPT Control register bit definitions
 */
#define GPTCR_FO3			(1<<31)
#define GPTCR_FO2			(1<<30)
#define GPTCR_FO1			(1<<29)

#define GPTCR_OM3_SHIFT			26
#define GPTCR_OM3_MASK			(7<<GPTCR_OM3_SHIFT)
#define GPTCR_OM3_DISCONNECTED		(0<<GPTCR_OM3_SHIFT)
#define GPTCR_OM3_TOGGLE		(1<<GPTCR_OM3_SHIFT)
#define GPTCR_OM3_CLEAR			(2<<GPTCR_OM3_SHIFT)
#define GPTCR_OM3_SET			(3<<GPTCR_OM3_SHIFT)
#define GPTCR_OM3_GENERATE_LOW		(7<<GPTCR_OM3_SHIFT)

#define GPTCR_OM2_SHIFT			23
#define GPTCR_OM2_MASK			(7<<GPTCR_OM2_SHIFT)
#define GPTCR_OM2_DISCONNECTED		(0<<GPTCR_OM2_SHIFT)
#define GPTCR_OM2_TOGGLE		(1<<GPTCR_OM2_SHIFT)
#define GPTCR_OM2_CLEAR			(2<<GPTCR_OM2_SHIFT)
#define GPTCR_OM2_SET			(3<<GPTCR_OM2_SHIFT)
#define GPTCR_OM2_GENERATE_LOW		(7<<GPTCR_OM2_SHIFT)

#define GPTCR_OM1_SHIFT			20
#define GPTCR_OM1_MASK			(7<<GPTCR_OM1_SHIFT)
#define GPTCR_OM1_DISCONNECTED		(0<<GPTCR_OM1_SHIFT)
#define GPTCR_OM1_TOGGLE		(1<<GPTCR_OM1_SHIFT)
#define GPTCR_OM1_CLEAR			(2<<GPTCR_OM1_SHIFT)
#define GPTCR_OM1_SET			(3<<GPTCR_OM1_SHIFT)
#define GPTCR_OM1_GENERATE_LOW		(7<<GPTCR_OM1_SHIFT)

#define GPTCR_IM2_SHIFT			18
#define GPTCR_IM2_MASK			(3<<GPTCR_IM2_SHIFT)
#define GPTCR_IM2_CAPTURE_DISABLE	(0<<GPTCR_IM2_SHIFT)
#define GPTCR_IM2_CAPTURE_RISING	(1<<GPTCR_IM2_SHIFT)
#define GPTCR_IM2_CAPTURE_FALLING	(2<<GPTCR_IM2_SHIFT)
#define GPTCR_IM2_CAPTURE_BOTH		(3<<GPTCR_IM2_SHIFT)

#define GPTCR_IM1_SHIFT			16
#define GPTCR_IM1_MASK			(3<<GPTCR_IM1_SHIFT)
#define GPTCR_IM1_CAPTURE_DISABLE	(0<<GPTCR_IM1_SHIFT)
#define GPTCR_IM1_CAPTURE_RISING	(1<<GPTCR_IM1_SHIFT)
#define GPTCR_IM1_CAPTURE_FALLING	(2<<GPTCR_IM1_SHIFT)
#define GPTCR_IM1_CAPTURE_BOTH		(3<<GPTCR_IM1_SHIFT)

#define GPTCR_SWR			(1<<15)
#define GPTCR_FRR			(1<<9)

#define GPTCR_CLKSRC_SHIFT		6
#define GPTCR_CLKSRC_MASK		(3<<GPTCR_CLKSRC_MASK)
#define GPTCR_CLKSRC_NOCLOCK		(0<<GPTCR_CLKSRC_SHIFT)
#define GPTCR_CLKSRC_HIGHFREQ		(2<<GPTCR_CLKSRC_SHIFT)
#define GPTCR_CLKSRC_CLKIN		(3<<GPTCR_CLKSRC_SHIFT)
#define GPTCR_CLKSRC_CLK32K		(7<<GPTCR_CLKSRC_SHIFT)

#define GPTCR_STOPEN			(1<<5)
#define GPTCR_DOZEN			(1<<4)
#define GPTCR_WAITEN			(1<<3)
#define GPTCR_DBGEN			(1<<2)

#define GPTCR_ENMOD			(1<<1)
#define GPTCR_ENABLE			(1<<0)

#define	GPTSR_OF1			(1<<0)
#define	GPTSR_OF2			(1<<1)
#define	GPTSR_OF3			(1<<2)
#define	GPTSR_IF1			(1<<3)
#define	GPTSR_IF2			(1<<4)
#define	GPTSR_ROV			(1<<5)

#define	GPTIR_OF1IE			GPTSR_OF1
#define	GPTIR_OF2IE			GPTSR_OF2
#define	GPTIR_OF3IE			GPTSR_OF3
#define	GPTIR_IF1IE			GPTSR_IF1
#define	GPTIR_IF2IE			GPTSR_IF2
#define	GPTIR_ROVIE			GPTSR_ROV

#ifndef	__noinstrument
#	define	__noinstrument
#endif

#define RTC_INPUT_CLK_32768HZ
/*
#define RTC_INPUT_CLK_32000HZ
#define RTC_INPUT_CLK_32768HZ
*/

#if defined(RTC_INPUT_CLK_32768HZ)
#define RTC_INPUT_CLK   0x00
#else
#if defined(RTC_INPUT_CLK_32000HZ)
#define RTC_INPUT_CLK   0x01
#else  /* 38.4kHz */
#define RTC_INPUT_CLK   0x02
#endif
#endif

#define RTC_SW_BIT              (1 << 0)
#define RTC_ALM_BIT             (1 << 2)
#define RTC_1HZ_BIT             (1 << 4)
#define RTC_2HZ_BIT             (1 << 7)
#define RTC_SAM0_BIT            (1 << 8)
#define RTC_SAM1_BIT            (1 << 9)
#define RTC_SAM2_BIT            (1 << 10)
#define RTC_SAM3_BIT            (1 << 11)
#define RTC_SAM4_BIT            (1 << 12)
#define RTC_SAM5_BIT            (1 << 13)
#define RTC_SAM6_BIT            (1 << 14)
#define RTC_SAM7_BIT            (1 << 15)
#define PIT_ALL_ON              (0xFF80)

#ifndef RTC_INPUT_CLK_32768HZ
#error The PIE code has to be modified to support different input clock!
#endif

#define MAX_PIE_NUM             9
const unsigned long PIE_BIT_DEF[MAX_PIE_NUM][2] = {
        {2,   RTC_2HZ_BIT},
        {4,   RTC_SAM0_BIT},
        {8,   RTC_SAM1_BIT},
        {16,  RTC_SAM2_BIT},
        {32,  RTC_SAM3_BIT},
        {64,  RTC_SAM4_BIT},
        {128, RTC_SAM5_BIT},
        {256, RTC_SAM6_BIT},
        {512, RTC_SAM7_BIT},
};

/* Those are the bits from a classic RTC we want to mimic */
#define RTC_IRQF                0x80    /* any of the following 3 is active */
#define RTC_PF                  0x40
#define RTC_AF                  0x20
#define RTC_UF                  0x10

#undef RTC_DEBUG
#ifdef RTC_DEBUG
#define LOG_MXC(args...)        printk(KERN_DEBUG args)
#else
#define LOG_MXC(args...)
#endif

static unsigned long rtc_freq = 1;
static struct rtc_time g_rtc_alarm = {
        .tm_year        = 0,
        .tm_mon         = 0,
        .tm_mday        = 0,
        .tm_hour        = 0,
        .tm_mon         = 0,
        .tm_sec         = 0,
};

static DEFINE_RAW_SPINLOCK(rtc_lock);

#define MXC_RTC_TIME    0
#define MXC_RTC_ALARM   1

#define    _reg_RTC_HOURMIN   ((volatile unsigned long *)(IO_ADDRESS(RTC_BASE_ADDR + 0x00)))     /*  32bit rtc hour/min counter reg */
#define    _reg_RTC_SECOND    ((volatile unsigned long *)(IO_ADDRESS(RTC_BASE_ADDR + 0x04)))     /*  32bit rtc seconds counter reg */
#define    _reg_RTC_ALRM_HM   ((volatile unsigned long *)(IO_ADDRESS(RTC_BASE_ADDR + 0x08)))     /*  32bit rtc alarm hour/min reg */
#define    _reg_RTC_ALRM_SEC  ((volatile unsigned long *)(IO_ADDRESS(RTC_BASE_ADDR + 0x0C)))     /*  32bit rtc alarm seconds reg */
#define    _reg_RTC_RTCCTL    ((volatile unsigned long *)(IO_ADDRESS(RTC_BASE_ADDR + 0x10)))     /*  32bit rtc control reg */
#define    _reg_RTC_RTCISR    ((volatile unsigned long *)(IO_ADDRESS(RTC_BASE_ADDR + 0x14)))     /*  32bit rtc interrupt status reg */
#define    _reg_RTC_RTCIENR   ((volatile unsigned long *)(IO_ADDRESS(RTC_BASE_ADDR + 0x18)))     /*  32bit rtc interrupt enable reg */
#define    _reg_RTC_STPWCH    ((volatile unsigned long *)(IO_ADDRESS(RTC_BASE_ADDR + 0x1C)))     /*  32bit rtc stopwatch min reg */
#define    _reg_RTC_DAYR      ((volatile unsigned long *)(IO_ADDRESS(RTC_BASE_ADDR + 0x20)))     /*  32bit rtc days counter reg */
#define    _reg_RTC_DAYALARM  ((volatile unsigned long *)(IO_ADDRESS(RTC_BASE_ADDR + 0x24)))     /*  32bit rtc day alarm reg */
#define    _reg_RTC_TEST1     ((volatile unsigned long *)(IO_ADDRESS(RTC_BASE_ADDR + 0x28)))     /*  32bit rtc test reg 1 */
#define    _reg_RTC_TEST2     ((volatile unsigned long *)(IO_ADDRESS(RTC_BASE_ADDR + 0x2C)))     /*  32bit rtc test reg 2 */
#define    _reg_RTC_TEST3     ((volatile unsigned long *)(IO_ADDRESS(RTC_BASE_ADDR + 0x30)))     /*  32bit rtc test reg 3 */

/*!
 * This function is used to obtain the RTC time or the alarm value in
 * second.
 *
 * @param  time_alarm   use MXC_RTC_TIME for RTC time value; MXC_RTC_ALARM for alarm value
 *
 * @return The RTC time or alarm time in second.
 */
static unsigned long get_alarm_or_time(int time_alarm)
{
        unsigned int day, hr, min, sec, hr_min;

        if (time_alarm == MXC_RTC_TIME) {
                day     = *_reg_RTC_DAYR;
                hr_min  = *_reg_RTC_HOURMIN;
                sec     = *_reg_RTC_SECOND;
        } else if (time_alarm == MXC_RTC_ALARM) {
                day     = *_reg_RTC_DAYALARM;
                hr_min  = (0x0000FFFF) & (*_reg_RTC_ALRM_HM);
                sec     = *_reg_RTC_ALRM_SEC;
        } else {
                panic ("wrong value for time_alarm=%d\n", time_alarm);
        }

        hr  = hr_min >> 8;
        min = hr_min & 0x00FF;

        return ((((day * 24 + hr) * 60) + min) * 60 + sec);
}

/*!
 * This function sets the RTC alarm value or the time value.
 *
 * @param  time_alarm           the new alarm value to be updated in the RTC
 * @param  time                 use MXC_RTC_TIME for RTC time value; MXC_RTC_ALARM for alarm value
 */
static void set_alarm_or_time(int time_alarm, unsigned long time)
{
        unsigned short day, hr, min, sec, temp;

        day  = time / 86400;
        time -= day * 86400;
        /* time is within a day now*/
        hr = time / 3600;
        time -= hr * 3600;
        /* time is within an hour now*/
        min = time / 60;
        sec = time - min * 60;

        temp = (hr << 8) + min;

        if (time_alarm == MXC_RTC_TIME) {
                *_reg_RTC_DAYR = day;
                *_reg_RTC_SECOND = sec;
                *_reg_RTC_HOURMIN = temp;
        } else if (time_alarm == MXC_RTC_ALARM) {
                *_reg_RTC_DAYALARM = day;
                *_reg_RTC_ALRM_SEC = sec;
                *_reg_RTC_ALRM_HM = temp;
        } else {
                panic ("wrong value for time_alarm=%d\n", time_alarm);
        }
}

/*!
 * This function updates the RTC alarm registers and then clears all the
 * interrupt status bits.
 *
 * @param  alrm         the new alarm value to be updated in the RTC
 *
 * @return  0 if successful; non-zero otherwise.
 */
static int rtc_update_alarm(struct rtc_time *alrm)
{
        struct rtc_time alarm_tm, now_tm;
        unsigned long now, time;
        int ret;

        do {
                now = get_alarm_or_time(MXC_RTC_TIME);
                rtc_time_to_tm(now, &now_tm);
                rtc_next_alarm_time(&alarm_tm, &now_tm, alrm);
                ret = rtc_tm_to_time(&alarm_tm, &time);
                if (ret != 0) {
                        break;
                }

                /* clear all the interrupt status bits */
                *_reg_RTC_RTCISR = *_reg_RTC_RTCISR;

                set_alarm_or_time(MXC_RTC_ALARM, time);
        } while (now != get_alarm_or_time(MXC_RTC_TIME)); /* no roll over */

        return ret;
}

/*!
 * This function is the RTC interrupt service routine.
 *
 * @param  irq                  RTC IRQ number
 * @param  dev_id               device ID which is not used
 * @param  regs                 pointer to a structure containing the processor registers and state prior to
 *                              servicing the   interrupt. It is not used in this function.
 *
 * @return IRQ_HANDLED as defined in the include/linux/interrupt.h file.
 */
static irqreturn_t rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
        unsigned int status;
        unsigned long events = 0;

        spin_lock(&rtc_lock);

        status = (*_reg_RTC_RTCISR) & (*_reg_RTC_RTCIENR);
        /* clear interrupt sources */
        *_reg_RTC_RTCISR = status;

        /* clear alarm interrupt if it has occurred */
        if (status & RTC_ALM_BIT) {
                status &= ~RTC_ALM_BIT;
        }

        /* update irq data & counter */
        if (status & RTC_ALM_BIT) {
                events |= (RTC_AF|RTC_IRQF);
        }
        if (status & RTC_1HZ_BIT) {
                events |= (RTC_UF|RTC_IRQF);
        }
        if (status & PIT_ALL_ON) {
                events |= (RTC_PF|RTC_IRQF);
        }

        rtc_update(1, events);

        if ((status & RTC_ALM_BIT) && rtc_periodic_alarm(&g_rtc_alarm)) {
                rtc_update_alarm(&g_rtc_alarm);
        }

        spin_unlock(&rtc_lock);

        return IRQ_HANDLED;
}

/*!
 * This function is used to open the RTC driver by registering the RTC
 * interrupt service routine.
 *
 * @return  0 if successful; non-zero otherwise.
 */
static int mxc_rtc_open(void)
{
        int ret;

        LOG_MXC("\n%s()\n", __FUNCTION__);
        ret = request_irq(INT_RTC, rtc_interrupt, 0, "rtc", NULL);
        if (ret) {
                printk(KERN_ERR "rtc: IRQ%d already in use.\n", INT_RTC);
                return ret;
        }
        return 0;
}

/*!
 * clear all interrupts and release the IRQ
 */
static void mxc_rtc_release(void)
{
        LOG_MXC("\n%s()\n", __FUNCTION__);
        spin_lock_irq (&rtc_lock);
        /* clear all interrupt sources */
        *_reg_RTC_RTCISR = *_reg_RTC_RTCISR;
        spin_unlock_irq (&rtc_lock);

        free_irq(INT_RTC, NULL);
}

/*!
 * This function is used to support some ioctl calls directly.
 * Other ioctl calls are supported indirectly through the
 * arm/common/rtctime.c file.
 *
 * @param  cmd          ioctl command as defined in include/linux/rtc.h
 * @param  arg          value for the ioctl command
 *
 * @return  0 if successful or -EINVAL otherwise.
 */
static int mxc_rtc_ioctl(unsigned int cmd, unsigned long arg)
{
        int i;

        switch (cmd) {
        case RTC_PIE_OFF:
                *_reg_RTC_RTCIENR &= ~PIT_ALL_ON;
                return 0;
        case RTC_PIE_ON:
                if ((arg < PIE_BIT_DEF[0][0]) &&
                    (arg > PIE_BIT_DEF[MAX_PIE_NUM-1][0])) {
                        return -EACCES;
                }
                for (i = 0; i < MAX_PIE_NUM; i++) {
                        if (PIE_BIT_DEF[i][0] == arg) {
                                break;
                        }
                }
                if (i == MAX_PIE_NUM) {
                        return -EACCES;
                }

                spin_lock_irq(&rtc_lock);
                *_reg_RTC_RTCIENR |= PIE_BIT_DEF[i][1];
                spin_unlock_irq(&rtc_lock);
                return 0;
        case RTC_AIE_OFF:
                spin_lock_irq(&rtc_lock);
                *_reg_RTC_RTCIENR &= ~RTC_ALM_BIT;
                spin_unlock_irq(&rtc_lock);
                return 0;

        case RTC_AIE_ON:
                spin_lock_irq(&rtc_lock);
                *_reg_RTC_RTCIENR |= RTC_ALM_BIT;
                spin_unlock_irq(&rtc_lock);
                return 0;

        case RTC_UIE_OFF:       /* UIE is for the 1Hz interrupt */
                spin_lock_irq(&rtc_lock);
                *_reg_RTC_RTCIENR &= ~RTC_1HZ_BIT;
                spin_unlock_irq(&rtc_lock);
                return 0;

        case RTC_UIE_ON:
                spin_lock_irq(&rtc_lock);
                *_reg_RTC_RTCIENR |= RTC_1HZ_BIT;
                spin_unlock_irq(&rtc_lock);
                return 0;
        }

        return -EINVAL;
}

/*!
 * This function reads the current RTC time into tm in Gregorian date.
 *
 * @param  tm           contains the RTC time value upon return
 */
static void mxc_rtc_read_time(struct rtc_time *tm)
{
        rtc_time_to_tm(get_alarm_or_time(MXC_RTC_TIME), tm);
}

/*!
 * This function sets the current RTC time based on tm in Gregorian date.
 *
 * @param  tm           the time value to be set in the RTC
 *
 * @return  0 if successful; non-zero otherwise.
 */
static int mxc_rtc_set_time(struct rtc_time *tm)
{
        unsigned long time;
        int ret;

        if (tm->tm_sec > 59 || tm->tm_hour > 23 || tm->tm_min > 59
            || tm->tm_mon > 12 || tm->tm_mday > 31 || tm->tm_year < 0) {
                return -EINVAL;
        }

        ret = rtc_tm_to_time(tm, &time);
        if (ret == 0) {
                set_alarm_or_time(MXC_RTC_TIME, time);
        }
        return ret;
}

/*!
 * This function reads the current alarm value into the passed in \b alrm
 * argument. It updates the \b alrm's pending field value based on the whether
 * an alarm interrupt occurs or not.
 *
 * @param  alrm         contains the RTC alarm value upon return
 */
static void mxc_rtc_read_alarm(struct rtc_wkalrm *alrm)
{
        memcpy(&alrm->time, &g_rtc_alarm, sizeof(struct rtc_time));
        alrm->pending = (((*_reg_RTC_RTCISR) & RTC_1HZ_BIT) != 0) ? 1 : 0;
}

/*!
 * This function sets the RTC alarm based on passed in alrm.
 *
 * @param  alrm         the alarm value to be set in the RTC
 *
 * @return  0 if successful; non-zero otherwise.
 */
static int mxc_rtc_set_alarm(struct rtc_wkalrm *alrm)
{
        int ret;

        if (alrm->time.tm_sec > 59 || alrm->time.tm_hour > 23 ||
            alrm->time.tm_min > 59 ) {
                return -EFAULT;
        }
        spin_lock_irq(&rtc_lock);
        ret = rtc_update_alarm(&alrm->time);
        if (ret == 0) {
                memcpy(&g_rtc_alarm, &alrm->time, sizeof(struct rtc_time));

                if (alrm->enabled) {
                        *_reg_RTC_RTCIENR |= RTC_ALM_BIT;
                } else {
                        *_reg_RTC_RTCIENR &= ~RTC_ALM_BIT;
                }
        }
        spin_unlock_irq(&rtc_lock);

        return ret;
}

/*!
 * This function is used to provide the content for the /proc/driver/rtc
 * file.
 *
 * @param  buf          the buffer to hold the information that the driver wants to write
 *
 * @return  The number of bytes written into the rtc file.
 */
static int mxc_rtc_proc(char *buf)
{
        char *p = buf;

        p += sprintf(p, "alarm_IRQ\t: %s\n", (((*_reg_RTC_RTCIENR) &
                     RTC_ALM_BIT) != 0) ? "yes" : "no" );
        p += sprintf(p, "update_IRQ\t: %s\n", (((*_reg_RTC_RTCIENR) &
                     RTC_1HZ_BIT) != 0) ? "yes" : "no");
        p += sprintf(p, "periodic_freq\t: %ld\n", rtc_freq);

        return p - buf;
}

/*!
 * The RTC driver structure
 */
static struct rtc_ops mxc_rtc_ops = {
        .owner          = THIS_MODULE,
        .open           = mxc_rtc_open,
        .release        = mxc_rtc_release,
        .ioctl          = mxc_rtc_ioctl,

        .read_time      = mxc_rtc_read_time,
        .set_time       = mxc_rtc_set_time,
        .read_alarm     = mxc_rtc_read_alarm,
        .set_alarm      = mxc_rtc_set_alarm,
        .proc           = mxc_rtc_proc,
};

/*!
 * This function creates the /proc/driver/rtc file and registers the device RTC
 * in the /dev/misc directory.
 *
 * @return  -1 if RTC is failed to initialize; 0 is successful.
 */
static int __init rtc_init(void)
{
        /* reset and enable the RTC */
        *_reg_RTC_RTCCTL = (RTC_INPUT_CLK << 5) + (1 << 7);
        if (((*_reg_RTC_RTCCTL) & (1 << 7)) == 0) {
                return -1;
        }
        register_rtc(&mxc_rtc_ops);

        return 0;
}

/*!
 * This function removes the /proc/driver/rtc file and un-registers the
 * device RTC from the /dev/misc directory.
 */
static void __exit rtc_exit(void)
{
        unregister_rtc(&mxc_rtc_ops);
}

module_init(rtc_init);
module_exit(rtc_exit);

MODULE_AUTHOR("Freescale Semiconductor");
MODULE_DESCRIPTION("Realtime Clock Driver (RTC)");
MODULE_LICENSE("GPL");

/*
 * This implements the OS tick timer to generate interrupts every 10ms
 */

#if !defined(HZ) || (HZ != 100)
#error HZ is not defined or not equal to 100
#endif

#define    _reg_RTC_SECOND    ((volatile unsigned long *)(IO_ADDRESS(RTC_BASE_ADDR + 0x04)))     /*  32bit rtc seconds counter reg */

/*
 *****************************************
 * WatchDog  Register definitions        *
 *****************************************
 */
typedef struct {
        volatile __u16 WDOG_WCR;  /* 16bit watchdog control reg */
        volatile __u16 WDOG_WSR;  /* 16bit watchdog service reg */
        volatile __u16 WDOG_WRSR; /* 16bit watchdog reset status reg */
} wdog_reg_t;

/*!
 * The base addresses for the WDOG module
 */
static volatile wdog_reg_t *wdog_base[1] = {
        (volatile wdog_reg_t *) IO_ADDRESS(WDOG_BASE_ADDR),
};

#if 0  /* not enabled the two wdogs by default */
#define WDOG1_ENABLE                                /* not defined by default */
#endif
#define WDOG1_TIMEOUT           4000                /* WDOG1 timeout in ms */
#define WDOG_SERVICE_PERIOD     (WDOG1_TIMEOUT / 2)  /* time interval in ms to service WDOGs */

#if (WDOG_SERVICE_PERIOD >= (WDOG1_TIMEOUT - 1000 / HZ))
#error WDOG_SERVICE_PERIOD is too large!
#endif

/* maximum timeout is 128s based on 2Hz clock */
#if ((WDOG1_TIMEOUT/1000) > 128)
#error WDOG time out has to be less than 128 seconds!
#endif

#define WDOG_WDE                0x4     /* WDOG enable bit inside WCR */
#define WDOG_WT                 0x8     /* WDOG WT starting bit inside WCR */

/*!
 * The corresponding WDOG won't be serviced unless the corresponding global
 * variable is set to a non-zero value.
 */
volatile unsigned short g_wdog1_enabled;

/* WDOG WCR register's WT value */
static int wdog_tmout[1] = {WDOG1_TIMEOUT};

/*!
 * This function provides the required service for the watchdog to avoid
 * the timeout.
 */
static inline void kick_wd(void)
{
        if (g_wdog1_enabled) {
                /* issue the service sequence instructions */
                wdog_base[0]->WDOG_WSR = 0x5555;
                wdog_base[0]->WDOG_WSR = 0xAAAA;
        }
}

/*!
 * This function converts system timer ticks to microseconds
 *
 * @param  x	system timer ticks
 *
 * @return elapsed microseconds
 */
unsigned long __noinstrument clock_to_usecs(unsigned long x)
{
        return (unsigned long)(x * (tick_nsec / 1000))/LATCH;
}

/*!
 * This function returns the current RTC seconds counter value
 *
 * @return RTC seconds
 */
static inline unsigned long mxc_get_rtc_time(void)
{
        return *_reg_RTC_SECOND;
}

#ifdef CONFIG_KFI
/*!
 * OS timer state flag 
 */
static int os_timer_initialized = 0;

/*!
 * This function is needed by KFI to convert machine cycles to microseconds
 *
 * @param mputicks	number of machine cycles
 *
 * @return elapsed microseconds
 */
unsigned long __noinstrument machinecycles_to_usecs(unsigned long mputicks)
{
	return clock_to_usecs(mputicks);
}

/*!
 * This function is needed by KFI to obtain current number of machine cycles elapsed
 *
 * @return elapsed machine cycles, or 0 if GPT timer is not initialized
 */
unsigned long __noinstrument do_getmachinecycles(void)
{
	return os_timer_initialized ? *_reg_GPT_GPTCNT : 0;
}
#endif

/*!
 * This is the watchdog initialization routine to setup the timeout
 * value and enable it.
 */
void mxc_wd_init(int port)
{
        unsigned volatile short timeout = ((wdog_tmout[port]/1000) * 2) << WDOG_WT;

        if (port == 0) {
                /* enable WD, suspend WD in DEBUG and low power modes */
                wdog_base[port]->WDOG_WCR |= timeout + 0x0007;
        }
}

#ifdef WDOG_SERVICE_PERIOD
static int g_wdog_count = 0;
#endif

/*!
 * This is the timer interrupt service routine to do required tasks.
 * It also services the WDOG timer at the frequency of twice per WDOG
 * timeout value. For example, if the WDOG's timeout value is 4 (2
 * seconds since the WDOG runs at 0.5Hz), it will be serviced once
 * every 2/2=1 second.
 *
 * @param  irq          GPT interrupt source number (not used)
 * @param  dev_id       this parameter is not used
 * @param  regs         pointer to a structure containing the processor registers and state prior to servicing the
 *                      interrupt
 * @return always returns \b IRQ_HANDLED as defined in include/linux/interrupt.h.
 */
static irqreturn_t mxc_timer_interrupt(int irq, void *dev_id,
                                       struct pt_regs *regs)
{
        unsigned int next_match;

#ifdef CONFIG_HIGH_RES_TIMERS
	if (*_reg_GPT_GPTSR & GPTSR_OF2) {
		*_reg_GPT_GPTSR = GPTSR_OF2;
		*_reg_GPT_GPTIR &= ~GPTIR_OF2IE;
		do_hr_timer_int();
	}
#endif

	write_seqlock(&xtime_lock);

	if (*_reg_GPT_GPTSR & GPTSR_OF1) do {
#ifdef WDOG_SERVICE_PERIOD
                if (g_wdog1_enabled &&
                    (++g_wdog_count >= ((WDOG_SERVICE_PERIOD / 1000) * HZ))) {
                        kick_wd();
                        g_wdog_count = 0; /* reset */
                }
#else
                kick_wd();
#endif /* WDOG_SERVICE_PERIOD */
		timer_tick(regs);
		next_match = *_reg_GPT_GPTOCR1 + LATCH;
		*_reg_GPT_GPTSR = GPTSR_OF1;
		*_reg_GPT_GPTOCR1 = next_match;
	} while ((signed long)(next_match - *_reg_GPT_GPTCNT) <= 0);
			
	write_sequnlock(&xtime_lock);

        return IRQ_HANDLED;
}

/*!
 * This function is used to obtain the number of microseconds since the last timer
 * interrupt. Note that interrupts is disabled by do_gettimeofday().
 *
 * @return the number of microseconds since the last timer interrupt.
 */
static unsigned long __noinstrument mxc_gettimeoffset(void)
{
        unsigned long ticks_to_match, elapsed, usec;

        /* Get ticks before next timer match */
	ticks_to_match = *_reg_GPT_GPTOCR1 - *_reg_GPT_GPTCNT;

        /* We need elapsed ticks since last match */
        elapsed = LATCH - ticks_to_match;

        /* Now convert them to usec */
        usec = (unsigned long)(elapsed * (tick_nsec / 1000))/LATCH;

        return usec;
}

/*!
 * The OS tick timer interrupt structure.
 */
static struct irqaction timer_irq = {
        .name           = "MXC Timer Tick",
        .flags          = SA_INTERRUPT,
        .handler        = mxc_timer_interrupt
};

/*!
 * This function is used to initialize the GPT to produce an interrupt
 * every 10 msec. It is called by the start_kernel() during system startup.
 */
void __init mxc_init_time(void)
{
        struct timespec tv;

        set_rtc = (int (*)(void))mxc_rtc_set_time;
        tv.tv_nsec = 0;
        tv.tv_sec = mxc_get_rtc_time();
        do_settimeofday(&tv);

	*_reg_GPT_GPTCR &= ~GPTCR_ENABLE;
	*_reg_GPT_GPTCR |= GPTCR_SWR;

	while ((*_reg_GPT_GPTCR & GPTCR_SWR) != 0)
		mb();

	*_reg_GPT_GPTCR = 0*GPTCR_OM3_CLEAR |
			  GPTCR_FRR |
			  GPTCR_CLKSRC_HIGHFREQ;

	*_reg_GPT_GPTOCR1 = *_reg_GPT_GPTCNT + LATCH;

	setup_irq(INT_GPT, &timer_irq);

	*_reg_GPT_GPTCR = GPTCR_FRR |
			  GPTCR_CLKSRC_HIGHFREQ |
			  GPTCR_STOPEN |
			  GPTCR_DOZEN |
			  GPTCR_WAITEN |
			  GPTCR_ENMOD |
			  GPTCR_ENABLE;

	*_reg_GPT_GPTIR = GPTIR_OF1IE;

#ifdef WDOG1_ENABLE
        mxc_wd_init(0);
        g_wdog1_enabled = 1;
#else
        g_wdog1_enabled = (wdog_base[0]->WDOG_WCR) & WDOG_WDE;
#endif

        kick_wd();

#ifdef CONFIG_KFI
	os_timer_initialized = 1;
#endif
}

EXPORT_SYMBOL(g_wdog1_enabled);
EXPORT_SYMBOL(mxc_wd_init);

struct sys_timer mxc_timer = {
	.init		= mxc_init_time,
	.offset		= mxc_gettimeoffset,
};

#ifdef CONFIG_HIGH_RES_TIMERS
/*
 * High-Res Timer Implementation
 *
 * 2004-2005 (c) MontaVista Software, Inc.
 *
 * This file is licensed under the terms of the GNU General Public
 * License version 2. This program is licensed "as is" without any
 * warranty of any kind, whether express or implied.
 */

/*!
 * This function schedules the next hr clock interrupt at the time specified
 * by ref_jiffies and ref_cycles.
 *
 *	If a previously scheduled interrupt has not happened yet, it is
 *	discarded and replaced by the new one.  (In practice, this will
 *	happen when a new timer is entered prior to the one last asked
 *	for.)
 *
 *	Arch can assume that
 *		0 <= hr_clock_cycles <= arch_cycles_per_jiffy
 *		-10 <= (jiffies - ref_jiffies) <= 10 jiffies
 *
 *	Locking: This function is called under the spin_lock_irq for the
 *	timer list, thus interrupts are off.
 *
 * @param  ref_jiffies  jiffy (Hz) boundary during which to schedule subHz
 *			interrupt
 * @param  ref_cycles   number of system timer clock ticks to delay from
 *			jiffy (Hz) boundary
 *
 * @return non-zero (-ETIME) if the specified time is already passed; 0 on
 *	   success
 */
int
schedule_hr_timer_int(unsigned ref_jiffies, int ref_cycles)
{
	int temp_cycles;

	BUG_ON(ref_cycles < 0);

	/*
	 * Get offset from next jiffy
	 * NOTE: GPTOCR1 is always the GPT match value for jiffies+1
	 */
	temp_cycles = (ref_jiffies - jiffies - 1) * arch_cycles_per_jiffy +
		ref_cycles;

	*_reg_GPT_GPTOCR2 = *_reg_GPT_GPTOCR1 + temp_cycles;
	*_reg_GPT_GPTIR |= GPTIR_OF2IE;

	if(unlikely((signed)(*_reg_GPT_GPTOCR2 - *_reg_GPT_GPTCNT) <= 0)) {
		*_reg_GPT_GPTIR &= ~GPTIR_OF2IE;
		return -ETIME;
	}

	return 0;
}

/*!
 * This function returns time elapsed from the reference jiffies to present
 * in the units of hr_clock_cycles.
 *
 *	Arch code can assume
 *		0 <= (jiffies - ref_jiffies) <= 10 jiffies
 *		ref_jiffies may not increase monotonically.
 *
 *	Locking: The caller will provide locking equivalent to read_lock
 *	on the xtime_lock.  The callee need only lock hardware if needed.
 *
 * @param  ref_jiffies  reference jiffies
 *
 * @return number of hr_clock_cycles elapsed from the reference jiffies to
 *	   present.
 */
int
get_arch_cycles(unsigned ref_jiffies)
{
	int ret;
	unsigned temp_jiffies;
	unsigned diff_jiffies;

	do {
		/* snapshot jiffies */
		temp_jiffies = jiffies;
		barrier();

		/* calculate cycles since the current jiffy */
		ret = *_reg_GPT_GPTOCR1 - *_reg_GPT_GPTCNT;
		ret = arch_cycles_per_jiffy - ret;

		/* compensate for ref_jiffies in the past */
		if (unlikely(diff_jiffies = jiffies - ref_jiffies))
			ret += diff_jiffies * arch_cycles_per_jiffy;

		barrier();
		/* repeat if we didn't have a consistent view of the world */
	} while (unlikely(temp_jiffies != jiffies));

	return ret;
}

#endif
