[rtc-linux] [PATCH 15/74] ST SPEAr: adding support for rtc

Wan ZongShun mcuos.com at gmail.com
Tue Aug 31 21:22:34 EDT 2010


2010/8/30 Viresh KUMAR <viresh.kumar at st.com>:
> From: Rajeev Kumar <rajeev-dlh.kumar at st.com>
>
> Signed-off-by: Rajeev Kumar <rajeev-dlh.kumar at st.com>
> Signed-off-by: shiraz hashim <shiraz.hashim at st.com>
> Signed-off-by: Viresh Kumar <viresh.kumar at st.com>
> ---
>  arch/arm/mach-spear13xx/clock.c                |    2 +-
>  arch/arm/mach-spear13xx/include/mach/generic.h |    1 +
>  arch/arm/mach-spear13xx/spear1300_evb.c        |    1 +
>  arch/arm/mach-spear13xx/spear13xx.c            |   19 +
>  arch/arm/mach-spear3xx/clock.c                 |    2 +-
>  arch/arm/mach-spear3xx/include/mach/generic.h  |    1 +
>  arch/arm/mach-spear3xx/spear300_evb.c          |    1 +
>  arch/arm/mach-spear3xx/spear310_evb.c          |    1 +
>  arch/arm/mach-spear3xx/spear320_evb.c          |    1 +
>  arch/arm/mach-spear3xx/spear3xx.c              |   19 +
>  arch/arm/mach-spear6xx/clock.c                 |    2 +-
>  arch/arm/mach-spear6xx/include/mach/generic.h  |    1 +
>  arch/arm/mach-spear6xx/spear600_evb.c          |    1 +
>  arch/arm/mach-spear6xx/spear6xx.c              |   19 +
>  drivers/rtc/Kconfig                            |    7 +
>  drivers/rtc/Makefile                           |    1 +
>  drivers/rtc/rtc-spear.c                        |  598 ++++++++++++++++++++++++
>  17 files changed, 674 insertions(+), 3 deletions(-)
>  create mode 100644 drivers/rtc/rtc-spear.c
>


Please split your this patch into two parts, one submitted to LAKM,
the other submitted to Linux-rtc list.

> diff --git a/arch/arm/mach-spear13xx/clock.c b/arch/arm/mach-spear13xx/clock.c
> index cef3b13..cc692cc 100644
> --- a/arch/arm/mach-spear13xx/clock.c
> +++ b/arch/arm/mach-spear13xx/clock.c
> @@ -736,7 +736,7 @@ static struct clk_lookup spear_clk_lookups[] = {
>        {.con_id = "osc3_25m_clk",      .clk = &osc3_25m_clk},
>
>        /* clock derived from 32 KHz osc clk */
> -       {.dev_id = "rtc",               .clk = &rtc_clk},
> +       {.dev_id = "rtc-spear",         .clk = &rtc_clk},
>
>        /* clock derived from 24/25 MHz osc1/osc3 clk */
>        {.con_id = "pll1_clk",          .clk = &pll1_clk},
> diff --git a/arch/arm/mach-spear13xx/include/mach/generic.h b/arch/arm/mach-spear13xx/include/mach/generic.h
> index 41c1a53..dc80421 100644
> --- a/arch/arm/mach-spear13xx/include/mach/generic.h
> +++ b/arch/arm/mach-spear13xx/include/mach/generic.h
> @@ -30,6 +30,7 @@
>
>  /* Add spear13xx family device structure declarations here */
>  extern struct amba_device uart_device;
> +extern struct platform_device rtc_device;
>  extern struct sys_timer spear13xx_timer;
>
>  /* Add spear1300 machine device structure declarations here */
> diff --git a/arch/arm/mach-spear13xx/spear1300_evb.c b/arch/arm/mach-spear13xx/spear1300_evb.c
> index d72c8a8..60c5fee 100644
> --- a/arch/arm/mach-spear13xx/spear1300_evb.c
> +++ b/arch/arm/mach-spear13xx/spear1300_evb.c
> @@ -22,6 +22,7 @@ static struct amba_device *amba_devs[] __initdata = {
>  };
>
>  static struct platform_device *plat_devs[] __initdata = {
> +       &rtc_device,
>  };
>
>  static void __init spear1300_evb_init(void)
> diff --git a/arch/arm/mach-spear13xx/spear13xx.c b/arch/arm/mach-spear13xx/spear13xx.c
> index d11e300..bdca713 100644
> --- a/arch/arm/mach-spear13xx/spear13xx.c
> +++ b/arch/arm/mach-spear13xx/spear13xx.c
> @@ -37,6 +37,25 @@ struct amba_device uart_device = {
>        .irq = {IRQ_UART, NO_IRQ},
>  };
>
> +/* rtc device registration */
> +static struct resource rtc_resources[] = {
> +       {
> +               .start = SPEAR13XX_RTC_BASE,
> +               .end = SPEAR13XX_RTC_BASE + SZ_4K - 1,
> +               .flags = IORESOURCE_MEM,
> +       }, {
> +               .start = IRQ_RTC,
> +               .flags = IORESOURCE_IRQ,
> +       },
> +};
> +
> +struct platform_device rtc_device = {
> +       .name = "rtc-spear",
> +       .id = -1,
> +       .num_resources = ARRAY_SIZE(rtc_resources),
> +       .resource = rtc_resources,
> +};
> +
>  /* Do spear13xx familiy common initialization part here */
>  void __init spear13xx_init(void)
>  {
> diff --git a/arch/arm/mach-spear3xx/clock.c b/arch/arm/mach-spear3xx/clock.c
> index dc19666..147d0a3 100644
> --- a/arch/arm/mach-spear3xx/clock.c
> +++ b/arch/arm/mach-spear3xx/clock.c
> @@ -467,7 +467,7 @@ static struct clk_lookup spear_clk_lookups[] = {
>        { .con_id = "osc_32k_clk",      .clk = &osc_32k_clk},
>        { .con_id = "osc_24m_clk",      .clk = &osc_24m_clk},
>        /* clock derived from 32 KHz osc clk */
> -       { .dev_id = "rtc",              .clk = &rtc_clk},
> +       { .dev_id = "rtc-spear",        .clk = &rtc_clk},
>        /* clock derived from 24 MHz osc clk */
>        { .con_id = "pll1_clk",         .clk = &pll1_clk},
>        { .con_id = "pll3_48m_clk",     .clk = &pll3_48m_clk},
> diff --git a/arch/arm/mach-spear3xx/include/mach/generic.h b/arch/arm/mach-spear3xx/include/mach/generic.h
> index d76ee98..408bb8d 100644
> --- a/arch/arm/mach-spear3xx/include/mach/generic.h
> +++ b/arch/arm/mach-spear3xx/include/mach/generic.h
> @@ -33,6 +33,7 @@
>  /* Add spear3xx family device structure declarations here */
>  extern struct amba_device gpio_device;
>  extern struct amba_device uart_device;
> +extern struct platform_device rtc_device;
>  extern struct sys_timer spear3xx_timer;
>
>  /* Add spear3xx family function declarations here */
> diff --git a/arch/arm/mach-spear3xx/spear300_evb.c b/arch/arm/mach-spear3xx/spear300_evb.c
> index 3bb7fbc..392ee4a 100644
> --- a/arch/arm/mach-spear3xx/spear300_evb.c
> +++ b/arch/arm/mach-spear3xx/spear300_evb.c
> @@ -44,6 +44,7 @@ static struct amba_device *amba_devs[] __initdata = {
>
>  static struct platform_device *plat_devs[] __initdata = {
>        /* spear3xx specific devices */
> +       &rtc_device,
>
>        /* spear300 specific devices */
>  };
> diff --git a/arch/arm/mach-spear3xx/spear310_evb.c b/arch/arm/mach-spear3xx/spear310_evb.c
> index 7dd93bd..15ca8fc 100644
> --- a/arch/arm/mach-spear3xx/spear310_evb.c
> +++ b/arch/arm/mach-spear3xx/spear310_evb.c
> @@ -50,6 +50,7 @@ static struct amba_device *amba_devs[] __initdata = {
>
>  static struct platform_device *plat_devs[] __initdata = {
>        /* spear3xx specific devices */
> +       &rtc_device,
>
>        /* spear310 specific devices */
>        &plgpio_device,
> diff --git a/arch/arm/mach-spear3xx/spear320_evb.c b/arch/arm/mach-spear3xx/spear320_evb.c
> index 82f4e76..48155cc 100644
> --- a/arch/arm/mach-spear3xx/spear320_evb.c
> +++ b/arch/arm/mach-spear3xx/spear320_evb.c
> @@ -48,6 +48,7 @@ static struct amba_device *amba_devs[] __initdata = {
>
>  static struct platform_device *plat_devs[] __initdata = {
>        /* spear3xx specific devices */
> +       &rtc_device,
>
>        /* spear320 specific devices */
>        &plgpio_device,
> diff --git a/arch/arm/mach-spear3xx/spear3xx.c b/arch/arm/mach-spear3xx/spear3xx.c
> index 89cf8ea..6d8791e 100644
> --- a/arch/arm/mach-spear3xx/spear3xx.c
> +++ b/arch/arm/mach-spear3xx/spear3xx.c
> @@ -54,6 +54,25 @@ struct amba_device uart_device = {
>        .irq = {IRQ_UART, NO_IRQ},
>  };
>
> +/* rtc device registration */
> +static struct resource rtc_resources[] = {
> +       {
> +               .start = SPEAR3XX_ICM3_RTC_BASE,
> +               .end = SPEAR3XX_ICM3_RTC_BASE + SZ_4K - 1,
> +               .flags = IORESOURCE_MEM,
> +       }, {
> +               .start = IRQ_BASIC_RTC,
> +               .flags = IORESOURCE_IRQ,
> +       },
> +};
> +
> +struct platform_device rtc_device = {
> +       .name = "rtc-spear",
> +       .id = -1,
> +       .num_resources = ARRAY_SIZE(rtc_resources),
> +       .resource = rtc_resources,
> +};
> +
>  /* Do spear3xx familiy common initialization part here */
>  void __init spear3xx_init(void)
>  {
> diff --git a/arch/arm/mach-spear6xx/clock.c b/arch/arm/mach-spear6xx/clock.c
> index 4a91991..66fc622 100644
> --- a/arch/arm/mach-spear6xx/clock.c
> +++ b/arch/arm/mach-spear6xx/clock.c
> @@ -569,7 +569,7 @@ static struct clk_lookup spear_clk_lookups[] = {
>        { .con_id = "osc_32k_clk",      .clk = &osc_32k_clk},
>        { .con_id = "osc_30m_clk",      .clk = &osc_30m_clk},
>        /* clock derived from 32 KHz os          clk */
> -       { .dev_id = "rtc",              .clk = &rtc_clk},
> +       { .dev_id = "rtc-spear",        .clk = &rtc_clk},
>        /* clock derived from 30 MHz os          clk */
>        { .con_id = "pll1_clk",         .clk = &pll1_clk},
>        { .con_id = "pll3_48m_clk",     .clk = &pll3_48m_clk},
> diff --git a/arch/arm/mach-spear6xx/include/mach/generic.h b/arch/arm/mach-spear6xx/include/mach/generic.h
> index d6a04f2..674b16c 100644
> --- a/arch/arm/mach-spear6xx/include/mach/generic.h
> +++ b/arch/arm/mach-spear6xx/include/mach/generic.h
> @@ -32,6 +32,7 @@
>  extern struct amba_device clcd_device;
>  extern struct amba_device gpio_device[];
>  extern struct amba_device uart_device[];
> +extern struct platform_device rtc_device;
>  extern struct sys_timer spear6xx_timer;
>
>  /* Add spear6xx family function declarations here */
> diff --git a/arch/arm/mach-spear6xx/spear600_evb.c b/arch/arm/mach-spear6xx/spear600_evb.c
> index 88e69f4..861e83d 100644
> --- a/arch/arm/mach-spear6xx/spear600_evb.c
> +++ b/arch/arm/mach-spear6xx/spear600_evb.c
> @@ -26,6 +26,7 @@ static struct amba_device *amba_devs[] __initdata = {
>  };
>
>  static struct platform_device *plat_devs[] __initdata = {
> +       &rtc_device,
>  };
>
>  static void __init spear600_evb_init(void)
> diff --git a/arch/arm/mach-spear6xx/spear6xx.c b/arch/arm/mach-spear6xx/spear6xx.c
> index d0f6b9d..1e403cb 100644
> --- a/arch/arm/mach-spear6xx/spear6xx.c
> +++ b/arch/arm/mach-spear6xx/spear6xx.c
> @@ -121,6 +121,25 @@ struct amba_device gpio_device[] = {
>        }
>  };
>
> +/* rtc device registration */
> +static struct resource rtc_resources[] = {
> +       {
> +               .start = SPEAR6XX_ICM3_RTC_BASE,
> +               .end = SPEAR6XX_ICM3_RTC_BASE + SZ_4K - 1,
> +               .flags = IORESOURCE_MEM,
> +       }, {
> +               .start = IRQ_BASIC_RTC,
> +               .flags = IORESOURCE_IRQ,
> +       },
> +};
> +
> +struct platform_device rtc_device = {
> +       .name = "rtc-spear",
> +       .id = -1,
> +       .num_resources = ARRAY_SIZE(rtc_resources),
> +       .resource = rtc_resources,
> +};
> +
>  /* This will add devices, and do machine specific tasks */
>  void __init spear6xx_init(void)
>  {
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 48ca713..f099473 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -625,6 +625,13 @@ config RTC_DRV_WM8350
>          This driver can also be built as a module. If so, the module
>          will be called "rtc-wm8350".
>
> +config RTC_DRV_SPEAR
> +       tristate "SPEAR ST RTC"
> +       default y
> +       help
> +        If you say Y here you will get support for the RTC found on
> +        spear
> +
>  config RTC_DRV_PCF50633
>        depends on MFD_PCF50633
>        tristate "NXP PCF50633 RTC"
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 0f207b3..44df01a 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -86,6 +86,7 @@ obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o
>  obj-$(CONFIG_RTC_DRV_S3C)      += rtc-s3c.o
>  obj-$(CONFIG_RTC_DRV_SA1100)   += rtc-sa1100.o
>  obj-$(CONFIG_RTC_DRV_SH)       += rtc-sh.o
> +obj-$(CONFIG_RTC_DRV_SPEAR)    += rtc-spear.o
>  obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o
>  obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
>  obj-$(CONFIG_RTC_DRV_STMP)     += rtc-stmp3xxx.o
> diff --git a/drivers/rtc/rtc-spear.c b/drivers/rtc/rtc-spear.c
> new file mode 100644
> index 0000000..5b49124
> --- /dev/null
> +++ b/drivers/rtc/rtc-spear.c
> @@ -0,0 +1,598 @@
> +/*
> + * drivers/rtc/rtc-spear.c
> + *
> + * Copyright (C) 2010 ST Microelectronics
> + * Rajeev Kumar<rajeev-dlh.kumar at st.com>
> + *
> + * 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.
> + */
> +
> +#include <linux/bcd.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/init.h>
> +#include <linux/ioport.h>
> +#include <linux/io.h>
> +#include <linux/irq.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/rtc.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/types.h>
> +#include <linux/wait.h>
> +#include <asm/mach/time.h>
> +
> +/* RTC registers */
> +#define TIME_REG               0x00
> +#define DATE_REG               0x04
> +#define ALARM_TIME_REG         0x08
> +#define ALARM_DATE_REG         0x0C
> +#define CTRL_REG               0x10
> +#define STATUS_REG             0x14
> +
> +/* TIME_REG & ALARM_TIME_REG */
> +#define SECONDS_UNITS          (0xf<<0)        /* seconds units position */
> +#define SECONDS_TENS           (0x7<<4)        /* seconds tens position */
> +#define MINUTES_UNITS          (0xf<<8)        /* minutes units position */
> +#define MINUTES_TENS           (0x7<<12)       /* minutes tens position */
> +#define HOURS_UNITS            (0xf<<16)       /* hours units position */
> +#define HOURS_TENS             (0x3<<20)       /* hours tens position */
> +
> +/* DATE_REG & ALARM_DATE_REG */
> +#define DAYS_UNITS             (0xf<<0)        /* days units position */
> +#define DAYS_TENS              (0x3<<4)        /* days tens position */
> +#define MONTHS_UNITS           (0xf<<8)        /* months units position */
> +#define MONTHS_TENS            (0x1<<12)       /* months tens position */
> +#define YEARS_UNITS            (0xf<<16)       /* years units position */
> +#define YEARS_TENS             (0xf<<20)       /* years tens position */
> +#define YEARS_HUNDREDS         (0xf<<24)       /* years hundereds position */
> +#define YEARS_MILLENIUMS       (0xf<<28)       /* years millenium position */
> +
> +/* MASK SHIFT TIME_REG & ALARM_TIME_REG*/
> +#define SECOND_SHIFT           0x00            /* seconds units */
> +#define MINUTE_SHIFT           0x08            /* minutes units position */
> +#define HOUR_SHIFT             0x10            /* hours units position */
> +#define MDAY_SHIFT             0x00            /* Month day shift */
> +#define MONTH_SHIFT            0x08            /* Month shift */
> +#define YEAR_SHIFT             0x10            /* Year shift */
> +
> +#define SECOND_MASK            0x7F
> +#define MIN_MASK               0x7F
> +#define HOUR_MASK              0x3F
> +#define DAY_MASK               0x3F
> +#define MONTH_MASK             0x7F
> +#define YEAR_MASK              0xFFFF
> +
> +/* date reg equal to time reg, for debug only */
> +#define TIME_BYP               (1<<9)
> +#define INT_ENABLE             (1<<31)         /* interrupt enable */
> +
> +/* STATUS_REG */
> +#define CLK_UNCONNECTED                (1<<0)
> +#define PEND_WR_TIME           (1<<2)
> +#define PEND_WR_DATE           (1<<3)
> +#define LOST_WR_TIME           (1<<4)
> +#define LOST_WR_DATE           (1<<5)
> +#define RTC_INT_MASK           (1<<31)
> +#define STATUS_BUSY            (PEND_WR_TIME | PEND_WR_DATE)
> +#define STATUS_FAIL            (LOST_WR_TIME | LOST_WR_DATE)
> +
> +struct spear_rtc_config {
> +       struct clk *clk;
> +       spinlock_t lock;
> +       void __iomem *ioaddr;
> +};
> +
> +static inline void spear_rtc_clear_interrupt(struct spear_rtc_config *config)
> +{
> +       unsigned int val;
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&config->lock, flags);
> +       val = readl(config->ioaddr + STATUS_REG);
> +       val |= RTC_INT_MASK;
> +       writel(val, config->ioaddr + STATUS_REG);
> +       spin_unlock_irqrestore(&config->lock, flags);
> +}
> +
> +static inline void spear_rtc_enable_interrupt(struct spear_rtc_config *config)
> +{
> +       unsigned int val;
> +
> +       val = readl(config->ioaddr + CTRL_REG);
> +       if (!(val & INT_ENABLE)) {
> +               spear_rtc_clear_interrupt(config);
> +               val |= INT_ENABLE;
> +               writel(val, config->ioaddr + CTRL_REG);
> +       }
> +}
> +
> +static inline void spear_rtc_disable_interrupt(struct spear_rtc_config *config)
> +{
> +       unsigned int val;
> +
> +       val = readl(config->ioaddr + CTRL_REG);
> +       if (val & INT_ENABLE) {
> +               val &= ~INT_ENABLE;
> +               writel(val, config->ioaddr + CTRL_REG);
> +       }
> +}
> +
> +static inline int is_write_complete(struct spear_rtc_config *config)
> +{
> +       int ret = 0;
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&config->lock, flags);
> +       if ((readl(config->ioaddr + STATUS_REG)) & STATUS_FAIL)
> +               ret = -EIO;
> +       spin_unlock_irqrestore(&config->lock, flags);
> +
> +       return ret;
> +}
> +
> +static void rtc_wait_not_busy(struct spear_rtc_config *config)
> +{
> +       int status, count = 0;
> +       unsigned long flags;
> +
> +       /* Assuming BUSY may stay active for 80 msec) */
> +       for (count = 0; count < 80; count++) {
> +               spin_lock_irqsave(&config->lock, flags);
> +               status = readl(config->ioaddr + STATUS_REG);
> +               spin_unlock_irqrestore(&config->lock, flags);
> +               if ((status & STATUS_BUSY) == 0)
> +                       break;
> +               /* check status busy, after each msec */
> +               msleep(1);
> +       }
> +}
> +
> +static irqreturn_t spear_rtc_irq(int irq, void *dev_id)
> +{
> +       struct rtc_device *rtc = (struct rtc_device *)dev_id;
> +       struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
> +       unsigned long flags, events = 0;
> +       unsigned int irq_data;
> +
> +       spin_lock_irqsave(&config->lock, flags);
> +       irq_data = readl(config->ioaddr + STATUS_REG);
> +       spin_unlock_irqrestore(&config->lock, flags);
> +
> +       if ((irq_data & RTC_INT_MASK)) {
> +               spear_rtc_clear_interrupt(config);
> +               events = RTC_IRQF | RTC_AF;
> +               rtc_update_irq(rtc, 1, events);
> +               return IRQ_HANDLED;
> +       } else
> +               return IRQ_NONE;
> +
> +}
> +
> +static int tm2bcd(struct rtc_time *tm)
> +{
> +       if (rtc_valid_tm(tm) != 0)
> +               return -EINVAL;
> +       tm->tm_sec = bin2bcd(tm->tm_sec);
> +       tm->tm_min = bin2bcd(tm->tm_min);
> +       tm->tm_hour = bin2bcd(tm->tm_hour);
> +       tm->tm_mday = bin2bcd(tm->tm_mday);
> +       tm->tm_mon = bin2bcd(tm->tm_mon + 1);
> +       tm->tm_year = bin2bcd(tm->tm_year);
> +
> +       return 0;
> +}
> +
> +static void bcd2tm(struct rtc_time *tm)
> +{
> +       tm->tm_sec = bcd2bin(tm->tm_sec);
> +       tm->tm_min = bcd2bin(tm->tm_min);
> +       tm->tm_hour = bcd2bin(tm->tm_hour);
> +       tm->tm_mday = bcd2bin(tm->tm_mday);
> +       tm->tm_mon = bcd2bin(tm->tm_mon) - 1;
> +       /* epoch == 1900 */
> +       tm->tm_year = bcd2bin(tm->tm_year);
> +}
> +
> +/*
> + * spear_rtc_read_time - set the time
> + * @dev: rtc device in use
> + * @tm: holds date and time
> + *
> + * This function read time and date. On success it will return 0
> + * otherwise -ve error is returned.
> + */
> +static int spear_rtc_read_time(struct device *dev, struct rtc_time *tm)
> +{
> +       struct platform_device *pdev = to_platform_device(dev);
> +       struct rtc_device *rtc = platform_get_drvdata(pdev);
> +       struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
> +       unsigned int time, date;
> +
> +       /* we don't report wday/yday/isdst ... */
> +       rtc_wait_not_busy(config);
> +
> +       time = readl(config->ioaddr + TIME_REG);
> +       date = readl(config->ioaddr + DATE_REG);
> +       tm->tm_sec = (time >> SECOND_SHIFT) & SECOND_MASK;
> +       tm->tm_min = (time >> MINUTE_SHIFT) & MIN_MASK;
> +       tm->tm_hour = (time >> HOUR_SHIFT) & HOUR_MASK;
> +       tm->tm_mday = (date >> MDAY_SHIFT) & DAY_MASK;
> +       tm->tm_mon = (date >> MONTH_SHIFT) & MONTH_MASK;
> +       tm->tm_year = (date >> YEAR_SHIFT) & YEAR_MASK;
> +
> +       bcd2tm(tm);
> +       return 0;
> +}
> +
> +/*
> + * spear_rtc_set_time - set the time
> + * @dev: rtc device in use
> + * @tm: holds date and time
> + *
> + * This function set time and date. On success it will return 0
> + * otherwise -ve error is returned.
> + */
> +static int spear_rtc_set_time(struct device *dev, struct rtc_time *tm)
> +{
> +       struct platform_device *pdev = to_platform_device(dev);
> +       struct rtc_device *rtc = platform_get_drvdata(pdev);
> +       struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
> +       unsigned int time, date, err = 0;
> +
> +       if (tm2bcd(tm) < 0)
> +               return -EINVAL;
> +
> +       rtc_wait_not_busy(config);
> +       time = (tm->tm_sec << SECOND_SHIFT) | (tm->tm_min << MINUTE_SHIFT) |
> +               (tm->tm_hour << HOUR_SHIFT);
> +       date = (tm->tm_mday << MDAY_SHIFT) | (tm->tm_mon << MONTH_SHIFT) |
> +               (tm->tm_year << YEAR_SHIFT);
> +       writel(time, config->ioaddr + TIME_REG);
> +       writel(date, config->ioaddr + DATE_REG);
> +       err = is_write_complete(config);
> +       if (err < 0)
> +               return err;
> +
> +       return 0;
> +}
> +
> +/*
> + * spear_rtc_read_alarm - read the alarm time
> + * @dev: rtc device in use
> + * @alm: holds alarm date and time
> + *
> + * This function read alarm time and date. On success it will return 0
> + * otherwise -ve error is returned.
> + */
> +static int spear_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
> +{
> +       struct platform_device *pdev = to_platform_device(dev);
> +       struct rtc_device *rtc = platform_get_drvdata(pdev);
> +       struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
> +       unsigned int time, date;
> +
> +       rtc_wait_not_busy(config);
> +
> +       time = readl(config->ioaddr + ALARM_TIME_REG);
> +       date = readl(config->ioaddr + ALARM_DATE_REG);
> +       alm->time.tm_sec = (time >> SECOND_SHIFT) & SECOND_MASK;
> +       alm->time.tm_min = (time >> MINUTE_SHIFT) & MIN_MASK;
> +       alm->time.tm_hour = (time >> HOUR_SHIFT) & HOUR_MASK;
> +       alm->time.tm_mday = (date >> MDAY_SHIFT) & DAY_MASK;
> +       alm->time.tm_mon = (date >> MONTH_SHIFT) & MONTH_MASK;
> +       alm->time.tm_year = (date >> YEAR_SHIFT) & YEAR_MASK;
> +
> +       bcd2tm(&alm->time);
> +       alm->enabled = readl(config->ioaddr + CTRL_REG) & INT_ENABLE;
> +
> +       return 0;
> +}
> +
> +/*
> + * spear_rtc_set_alarm - set the alarm time
> + * @dev: rtc device in use
> + * @alm: holds alarm date and time
> + *
> + * This function set alarm time and date. On success it will return 0
> + * otherwise -ve error is returned.
> + */
> +static int spear_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
> +{
> +       struct platform_device *pdev = to_platform_device(dev);
> +       struct rtc_device *rtc = platform_get_drvdata(pdev);
> +       struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
> +       unsigned int time, date, err = 0;
> +
> +       if (tm2bcd(&alm->time) < 0)
> +               return -EINVAL;
> +
> +       rtc_wait_not_busy(config);
> +
> +       time = (alm->time.tm_sec << SECOND_SHIFT) | (alm->time.tm_min <<
> +                       MINUTE_SHIFT) | (alm->time.tm_hour << HOUR_SHIFT);
> +       date = (alm->time.tm_mday << MDAY_SHIFT) | (alm->time.tm_mon <<
> +                       MONTH_SHIFT) | (alm->time.tm_year << YEAR_SHIFT);
> +
> +       writel(time, config->ioaddr + ALARM_TIME_REG);
> +       writel(date, config->ioaddr + ALARM_DATE_REG);
> +       err = is_write_complete(config);
> +       if (err < 0)
> +               return err;
> +
> +       if (alm->enabled)
> +               spear_rtc_enable_interrupt(config);
> +       else
> +               spear_rtc_disable_interrupt(config);
> +
> +       return 0;
> +}
> +
> +#ifdef CONFIG_RTC_INTF_DEV
> +static int
> +spear_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
> +{
> +       struct platform_device *pdev = to_platform_device(dev);
> +       struct rtc_device *rtc = platform_get_drvdata(pdev);
> +       struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
> +       struct rtc_time tm;
> +       struct rtc_wkalrm alm;
> +       void __user *uarg = (void __user *)arg;
> +       int err = 0;
> +
> +       switch (cmd) {
> +               /* AIE = Alarm Interrupt Enable */
> +       case RTC_AIE_OFF:
> +               spear_rtc_disable_interrupt(config);
> +               break;
> +       case RTC_AIE_ON:
> +               spear_rtc_enable_interrupt(config);
> +               break;
> +       case RTC_SET_TIME:
> +               if (copy_from_user(&tm, uarg, sizeof(tm)))
> +                       return -EFAULT;
> +               return spear_rtc_set_time(dev, &tm);
> +       case RTC_RD_TIME:
> +               err = spear_rtc_read_time(dev, &tm);
> +               if (err < 0)
> +                       return err;
> +
> +               if (copy_to_user(uarg, &tm, sizeof(tm)))
> +                       return -EFAULT;
> +               break;
> +       case RTC_ALM_SET:
> +               if (copy_from_user(&alm.time, uarg, sizeof(tm)))
> +                       return -EFAULT;
> +               alm.enabled = 0;
> +               alm.pending = 0;
> +               spear_rtc_set_alarm(dev, &alm);
> +               break;
> +       case RTC_ALM_READ:
> +               err = spear_rtc_read_alarm(dev, &alm);
> +               if (err < 0)
> +                       return err;
> +
> +               if (copy_to_user(uarg, &alm.time, sizeof(tm)))
> +                       return -EFAULT;
> +               break;
> +       default:
> +               return -ENOIOCTLCMD;
> +       }
> +
> +       return 0;
> +}
> +
> +#else
> +#define spear_rtc_ioctl        NULL
> +#endif
> +
> +static struct rtc_class_ops spear_rtc_ops = {
> +       .ioctl = spear_rtc_ioctl,
> +       .read_time = spear_rtc_read_time,
> +       .set_time = spear_rtc_set_time,
> +       .read_alarm = spear_rtc_read_alarm,
> +       .set_alarm = spear_rtc_set_alarm,
> +};
> +
> +static int __devinit spear_rtc_probe(struct platform_device *pdev)
> +{
> +       struct resource *res;
> +       struct rtc_device *rtc;
> +       struct spear_rtc_config *config;
> +       unsigned int status = 0;
> +       int irq;
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       if (!res) {
> +               dev_err(&pdev->dev, "no resource defined\n");
> +               return -EBUSY;
> +       }
> +       if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
> +               dev_err(&pdev->dev, "rtc region already claimed\n");
> +               return -EBUSY;
> +       }
> +
> +       config = kzalloc(sizeof(*config), GFP_KERNEL);
> +       if (!config) {
> +               dev_err(&pdev->dev, "out of memory\n");
> +               status = -ENOMEM;
> +               goto err_release_region;
> +       }
> +
> +       config->clk = clk_get(&pdev->dev, NULL);
> +       if (IS_ERR(config->clk)) {
> +               status = PTR_ERR(config->clk);
> +               goto err_kfree;
> +       }
> +
> +       status = clk_enable(config->clk);
> +       if (status < 0)
> +               goto err_clk_put;
> +
> +       config->ioaddr = ioremap(res->start, resource_size(res));
> +       if (!config->ioaddr) {
> +               dev_err(&pdev->dev, "ioremap fail\n");
> +               status = -ENOMEM;
> +               goto err_disable_clock;
> +       }
> +
> +       rtc = rtc_device_register(pdev->name, &pdev->dev, &spear_rtc_ops,
> +                       THIS_MODULE);
> +       if (IS_ERR(rtc)) {
> +               dev_err(&pdev->dev, "can't register RTC device, err %ld\n",
> +                               PTR_ERR(rtc));
> +               status = PTR_ERR(rtc);
> +               goto err_iounmap;
> +       }
> +       platform_set_drvdata(pdev, rtc);
> +       dev_set_drvdata(&rtc->dev, config);
> +
> +       spin_lock_init(&config->lock);
> +
> +       /* alarm irqs */
> +       irq = platform_get_irq(pdev, 0);
> +       if (irq < 0) {
> +               dev_err(&pdev->dev, "no update irq?\n");
> +               status = irq;
> +               goto err_clear_platdata;
> +       }
> +
> +       status = request_irq(irq, spear_rtc_irq, 0, pdev->name, rtc);
> +       if (status) {
> +               dev_err(&pdev->dev, "Alarm interrupt IRQ%d already \
> +                               claimed\n", irq);
> +               goto err_clear_platdata;
> +       }
> +
> +       if (!device_can_wakeup(&pdev->dev))
> +               device_init_wakeup(&pdev->dev, 1);
> +
> +       return 0;
> +
> +err_clear_platdata:
> +       platform_set_drvdata(pdev, NULL);
> +       dev_set_drvdata(&rtc->dev, NULL);
> +       rtc_device_unregister(rtc);
> +err_iounmap:
> +       iounmap(config->ioaddr);
> +err_disable_clock:
> +       clk_disable(config->clk);
> +err_clk_put:
> +       clk_put(config->clk);
> +err_kfree:
> +       kfree(config);
> +err_release_region:
> +       release_mem_region(res->start, resource_size(res));
> +
> +       return status;
> +}
> +
> +static int __devexit spear_rtc_remove(struct platform_device *pdev)
> +{
> +       struct rtc_device *rtc = platform_get_drvdata(pdev);
> +       struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
> +       int irq;
> +       struct resource *res;
> +
> +       /* leave rtc running, but disable irqs */
> +       spear_rtc_disable_interrupt(config);
> +       device_init_wakeup(&pdev->dev, 0);
> +       irq = platform_get_irq(pdev, 0);
> +       if (irq)
> +               free_irq(irq, pdev);
> +       clk_disable(config->clk);
> +       clk_put(config->clk);
> +       iounmap(config->ioaddr);
> +       kfree(config);
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       if (res)
> +               release_mem_region(res->start, resource_size(res));
> +       platform_set_drvdata(pdev, NULL);
> +       dev_set_drvdata(&rtc->dev, NULL);
> +       rtc_device_unregister(rtc);
> +
> +       return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +
> +static int spear_rtc_suspend(struct platform_device *pdev, pm_message_t state)
> +{
> +       struct rtc_device *rtc = platform_get_drvdata(pdev);
> +       struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
> +       int irq;
> +
> +       irq = platform_get_irq(pdev, 0);
> +       if (device_may_wakeup(&pdev->dev))
> +               enable_irq_wake(irq);
> +       else {
> +               spear_rtc_disable_interrupt(config);
> +               clk_disable(config->clk);
> +       }
> +
> +       return 0;
> +}
> +
> +static int spear_rtc_resume(struct platform_device *pdev)
> +{
> +       struct rtc_device *rtc = platform_get_drvdata(pdev);
> +       struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
> +       int irq;
> +
> +       irq = platform_get_irq(pdev, 0);
> +
> +       if (device_may_wakeup(&pdev->dev))
> +               disable_irq_wake(irq);
> +       else {
> +               clk_enable(config->clk);
> +               spear_rtc_enable_interrupt(config);
> +       }
> +
> +       return 0;
> +}
> +
> +#else
> +#define spear_rtc_suspend      NULL
> +#define spear_rtc_resume       NULL
> +#endif
> +
> +static void spear_rtc_shutdown(struct platform_device *pdev)
> +{
> +       struct rtc_device *rtc = platform_get_drvdata(pdev);
> +       struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
> +
> +       spear_rtc_disable_interrupt(config);
> +       clk_disable(config->clk);
> +}
> +
> +static struct platform_driver spear_rtc_driver = {
> +       .probe = spear_rtc_probe,
> +       .remove = __devexit_p(spear_rtc_remove),
> +       .suspend = spear_rtc_suspend,
> +       .resume = spear_rtc_resume,
> +       .shutdown = spear_rtc_shutdown,
> +       .driver = {
> +               .name = "rtc-spear",
> +               .owner = THIS_MODULE,
> +       },
> +};
> +
> +static int __init rtc_init(void)
> +{
> +       return platform_driver_register(&spear_rtc_driver);
> +}
> +module_init(rtc_init);
> +
> +static void __exit rtc_exit(void)
> +{
> +       platform_driver_unregister(&spear_rtc_driver);
> +}
> +module_exit(rtc_exit);
> +
> +MODULE_ALIAS("rtc-spear");
> +MODULE_AUTHOR("Rajeev Kumar");
> +MODULE_LICENSE("GPL");
> --
> 1.7.2.2
>
> --
> You received this message because you are subscribed to "rtc-linux".
> Membership options at http://groups.google.com/group/rtc-linux .
> Please read http://groups.google.com/group/rtc-linux/web/checklist
> before submitting a driver.



-- 
*linux-arm-kernel mailing list
mail addr:linux-arm-kernel at lists.infradead.org
you can subscribe by:
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

* linux-arm-NUC900 mailing list
mail addr:NUC900 at googlegroups.com
main web: https://groups.google.com/group/NUC900
you can subscribe it by sending me mail:
mcuos.com at gmail.com



More information about the linux-arm-kernel mailing list