diff options
Diffstat (limited to 'kernel/drivers/rtc/rtc-sa1100.c')
-rw-r--r-- | kernel/drivers/rtc/rtc-sa1100.c | 139 |
1 files changed, 84 insertions, 55 deletions
diff --git a/kernel/drivers/rtc/rtc-sa1100.c b/kernel/drivers/rtc/rtc-sa1100.c index b6e1ca08c..c2187bf6c 100644 --- a/kernel/drivers/rtc/rtc-sa1100.c +++ b/kernel/drivers/rtc/rtc-sa1100.c @@ -35,24 +35,17 @@ #include <linux/bitops.h> #include <linux/io.h> -#include <mach/hardware.h> -#include <mach/irqs.h> +#define RTSR_HZE BIT(3) /* HZ interrupt enable */ +#define RTSR_ALE BIT(2) /* RTC alarm interrupt enable */ +#define RTSR_HZ BIT(1) /* HZ rising-edge detected */ +#define RTSR_AL BIT(0) /* RTC alarm detected */ -#if defined(CONFIG_ARCH_PXA) || defined(CONFIG_ARCH_MMP) -#include <mach/regs-rtc.h> -#endif +#include "rtc-sa1100.h" #define RTC_DEF_DIVIDER (32768 - 1) #define RTC_DEF_TRIM 0 #define RTC_FREQ 1024 -struct sa1100_rtc { - spinlock_t lock; - int irq_1hz; - int irq_alarm; - struct rtc_device *rtc; - struct clk *clk; -}; static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id) { @@ -63,16 +56,16 @@ static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id) spin_lock(&info->lock); - rtsr = RTSR; + rtsr = readl_relaxed(info->rtsr); /* clear interrupt sources */ - RTSR = 0; + writel_relaxed(0, info->rtsr); /* Fix for a nasty initialization problem the in SA11xx RTSR register. * See also the comments in sa1100_rtc_probe(). */ if (rtsr & (RTSR_ALE | RTSR_HZE)) { /* This is the original code, before there was the if test * above. This code does not clear interrupts that were not * enabled. */ - RTSR = (RTSR_AL | RTSR_HZ) & (rtsr >> 2); + writel_relaxed((RTSR_AL | RTSR_HZ) & (rtsr >> 2), info->rtsr); } else { /* For some reason, it is possible to enter this routine * without interruptions enabled, it has been tested with @@ -81,13 +74,13 @@ static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id) * This situation leads to an infinite "loop" of interrupt * routine calling and as a result the processor seems to * lock on its first call to open(). */ - RTSR = RTSR_AL | RTSR_HZ; + writel_relaxed(RTSR_AL | RTSR_HZ, info->rtsr); } /* clear alarm interrupt if it has occurred */ if (rtsr & RTSR_AL) rtsr &= ~RTSR_ALE; - RTSR = rtsr & (RTSR_ALE | RTSR_HZE); + writel_relaxed(rtsr & (RTSR_ALE | RTSR_HZE), info->rtsr); /* update irq data & counter */ if (rtsr & RTSR_AL) @@ -135,7 +128,7 @@ static void sa1100_rtc_release(struct device *dev) struct sa1100_rtc *info = dev_get_drvdata(dev); spin_lock_irq(&info->lock); - RTSR = 0; + writel_relaxed(0, info->rtsr); spin_unlock_irq(&info->lock); free_irq(info->irq_alarm, dev); @@ -144,39 +137,46 @@ static void sa1100_rtc_release(struct device *dev) static int sa1100_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) { + u32 rtsr; struct sa1100_rtc *info = dev_get_drvdata(dev); spin_lock_irq(&info->lock); + rtsr = readl_relaxed(info->rtsr); if (enabled) - RTSR |= RTSR_ALE; + rtsr |= RTSR_ALE; else - RTSR &= ~RTSR_ALE; + rtsr &= ~RTSR_ALE; + writel_relaxed(rtsr, info->rtsr); spin_unlock_irq(&info->lock); return 0; } static int sa1100_rtc_read_time(struct device *dev, struct rtc_time *tm) { - rtc_time_to_tm(RCNR, tm); + struct sa1100_rtc *info = dev_get_drvdata(dev); + + rtc_time_to_tm(readl_relaxed(info->rcnr), tm); return 0; } static int sa1100_rtc_set_time(struct device *dev, struct rtc_time *tm) { + struct sa1100_rtc *info = dev_get_drvdata(dev); unsigned long time; int ret; ret = rtc_tm_to_time(tm, &time); if (ret == 0) - RCNR = time; + writel_relaxed(time, info->rcnr); return ret; } static int sa1100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) { u32 rtsr; + struct sa1100_rtc *info = dev_get_drvdata(dev); - rtsr = RTSR; + rtsr = readl_relaxed(info->rtsr); alrm->enabled = (rtsr & RTSR_ALE) ? 1 : 0; alrm->pending = (rtsr & RTSR_AL) ? 1 : 0; return 0; @@ -192,12 +192,13 @@ static int sa1100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) ret = rtc_tm_to_time(&alrm->time, &time); if (ret != 0) goto out; - RTSR = RTSR & (RTSR_HZE|RTSR_ALE|RTSR_AL); - RTAR = time; + writel_relaxed(readl_relaxed(info->rtsr) & + (RTSR_HZE | RTSR_ALE | RTSR_AL), info->rtsr); + writel_relaxed(time, info->rtar); if (alrm->enabled) - RTSR |= RTSR_ALE; + writel_relaxed(readl_relaxed(info->rtsr) | RTSR_ALE, info->rtsr); else - RTSR &= ~RTSR_ALE; + writel_relaxed(readl_relaxed(info->rtsr) & ~RTSR_ALE, info->rtsr); out: spin_unlock_irq(&info->lock); @@ -206,8 +207,10 @@ out: static int sa1100_rtc_proc(struct device *dev, struct seq_file *seq) { - seq_printf(seq, "trim/divider\t\t: 0x%08x\n", (u32) RTTR); - seq_printf(seq, "RTSR\t\t\t: 0x%08x\n", (u32)RTSR); + struct sa1100_rtc *info = dev_get_drvdata(dev); + + seq_printf(seq, "trim/divider\t\t: 0x%08x\n", readl_relaxed(info->rttr)); + seq_printf(seq, "RTSR\t\t\t: 0x%08x\n", readl_relaxed(info->rtsr)); return 0; } @@ -223,29 +226,18 @@ static const struct rtc_class_ops sa1100_rtc_ops = { .alarm_irq_enable = sa1100_rtc_alarm_irq_enable, }; -static int sa1100_rtc_probe(struct platform_device *pdev) +int sa1100_rtc_init(struct platform_device *pdev, struct sa1100_rtc *info) { struct rtc_device *rtc; - struct sa1100_rtc *info; - int irq_1hz, irq_alarm, ret = 0; + int ret; - irq_1hz = platform_get_irq_byname(pdev, "rtc 1Hz"); - irq_alarm = platform_get_irq_byname(pdev, "rtc alarm"); - if (irq_1hz < 0 || irq_alarm < 0) - return -ENODEV; + spin_lock_init(&info->lock); - info = devm_kzalloc(&pdev->dev, sizeof(struct sa1100_rtc), GFP_KERNEL); - if (!info) - return -ENOMEM; info->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(info->clk)) { dev_err(&pdev->dev, "failed to find rtc clock source\n"); return PTR_ERR(info->clk); } - info->irq_1hz = irq_1hz; - info->irq_alarm = irq_alarm; - spin_lock_init(&info->lock); - platform_set_drvdata(pdev, info); ret = clk_prepare_enable(info->clk); if (ret) @@ -257,22 +249,19 @@ static int sa1100_rtc_probe(struct platform_device *pdev) * If the clock divider is uninitialized then reset it to the * default value to get the 1Hz clock. */ - if (RTTR == 0) { - RTTR = RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16); + if (readl_relaxed(info->rttr) == 0) { + writel_relaxed(RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16), info->rttr); dev_warn(&pdev->dev, "warning: " "initializing default clock divider/trim value\n"); /* The current RTC value probably doesn't make sense either */ - RCNR = 0; + writel_relaxed(0, info->rcnr); } - device_init_wakeup(&pdev->dev, 1); - rtc = devm_rtc_device_register(&pdev->dev, pdev->name, &sa1100_rtc_ops, THIS_MODULE); - if (IS_ERR(rtc)) { - ret = PTR_ERR(rtc); - goto err_dev; + clk_disable_unprepare(info->clk); + return PTR_ERR(rtc); } info->rtc = rtc; @@ -298,12 +287,52 @@ static int sa1100_rtc_probe(struct platform_device *pdev) * * Notice that clearing bit 1 and 0 is accomplished by writting ONES to * the corresponding bits in RTSR. */ - RTSR = RTSR_AL | RTSR_HZ; + writel_relaxed(RTSR_AL | RTSR_HZ, info->rtsr); return 0; -err_dev: - clk_disable_unprepare(info->clk); - return ret; +} +EXPORT_SYMBOL_GPL(sa1100_rtc_init); + +static int sa1100_rtc_probe(struct platform_device *pdev) +{ + struct sa1100_rtc *info; + struct resource *iores; + void __iomem *base; + int irq_1hz, irq_alarm; + + irq_1hz = platform_get_irq_byname(pdev, "rtc 1Hz"); + irq_alarm = platform_get_irq_byname(pdev, "rtc alarm"); + if (irq_1hz < 0 || irq_alarm < 0) + return -ENODEV; + + info = devm_kzalloc(&pdev->dev, sizeof(struct sa1100_rtc), GFP_KERNEL); + if (!info) + return -ENOMEM; + info->irq_1hz = irq_1hz; + info->irq_alarm = irq_alarm; + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, iores); + if (IS_ERR(base)) + return PTR_ERR(base); + + if (IS_ENABLED(CONFIG_ARCH_SA1100) || + of_device_is_compatible(pdev->dev.of_node, "mrvl,sa1100-rtc")) { + info->rcnr = base + 0x04; + info->rtsr = base + 0x10; + info->rtar = base + 0x00; + info->rttr = base + 0x08; + } else { + info->rcnr = base + 0x0; + info->rtsr = base + 0x8; + info->rtar = base + 0x4; + info->rttr = base + 0xc; + } + + platform_set_drvdata(pdev, info); + device_init_wakeup(&pdev->dev, 1); + + return sa1100_rtc_init(pdev, info); } static int sa1100_rtc_remove(struct platform_device *pdev) |