linux/drivers/rtc/rtc-ep93xx.c
<<
>>
Prefs
   1/*
   2 * A driver for the RTC embedded in the Cirrus Logic EP93XX processors
   3 * Copyright (c) 2006 Tower Technologies
   4 *
   5 * Author: Alessandro Zummo <a.zummo@towertech.it>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 */
  11
  12#include <linux/module.h>
  13#include <linux/rtc.h>
  14#include <linux/platform_device.h>
  15#include <linux/io.h>
  16#include <linux/gfp.h>
  17
  18#define EP93XX_RTC_DATA                 0x000
  19#define EP93XX_RTC_MATCH                0x004
  20#define EP93XX_RTC_STATUS               0x008
  21#define  EP93XX_RTC_STATUS_INTR          (1<<0)
  22#define EP93XX_RTC_LOAD                 0x00C
  23#define EP93XX_RTC_CONTROL              0x010
  24#define  EP93XX_RTC_CONTROL_MIE          (1<<0)
  25#define EP93XX_RTC_SWCOMP               0x108
  26#define  EP93XX_RTC_SWCOMP_DEL_MASK      0x001f0000
  27#define  EP93XX_RTC_SWCOMP_DEL_SHIFT     16
  28#define  EP93XX_RTC_SWCOMP_INT_MASK      0x0000ffff
  29#define  EP93XX_RTC_SWCOMP_INT_SHIFT     0
  30
  31#define DRV_VERSION "0.3"
  32
  33/*
  34 * struct device dev.platform_data is used to store our private data
  35 * because struct rtc_device does not have a variable to hold it.
  36 */
  37struct ep93xx_rtc {
  38        void __iomem    *mmio_base;
  39        struct rtc_device *rtc;
  40};
  41
  42static int ep93xx_rtc_get_swcomp(struct device *dev, unsigned short *preload,
  43                                unsigned short *delete)
  44{
  45        struct ep93xx_rtc *ep93xx_rtc = dev->platform_data;
  46        unsigned long comp;
  47
  48        comp = __raw_readl(ep93xx_rtc->mmio_base + EP93XX_RTC_SWCOMP);
  49
  50        if (preload)
  51                *preload = (comp & EP93XX_RTC_SWCOMP_INT_MASK)
  52                                >> EP93XX_RTC_SWCOMP_INT_SHIFT;
  53
  54        if (delete)
  55                *delete = (comp & EP93XX_RTC_SWCOMP_DEL_MASK)
  56                                >> EP93XX_RTC_SWCOMP_DEL_SHIFT;
  57
  58        return 0;
  59}
  60
  61static int ep93xx_rtc_read_time(struct device *dev, struct rtc_time *tm)
  62{
  63        struct ep93xx_rtc *ep93xx_rtc = dev->platform_data;
  64        unsigned long time;
  65
  66         time = __raw_readl(ep93xx_rtc->mmio_base + EP93XX_RTC_DATA);
  67
  68        rtc_time_to_tm(time, tm);
  69        return 0;
  70}
  71
  72static int ep93xx_rtc_set_mmss(struct device *dev, unsigned long secs)
  73{
  74        struct ep93xx_rtc *ep93xx_rtc = dev->platform_data;
  75
  76        __raw_writel(secs + 1, ep93xx_rtc->mmio_base + EP93XX_RTC_LOAD);
  77        return 0;
  78}
  79
  80static int ep93xx_rtc_proc(struct device *dev, struct seq_file *seq)
  81{
  82        unsigned short preload, delete;
  83
  84        ep93xx_rtc_get_swcomp(dev, &preload, &delete);
  85
  86        seq_printf(seq, "preload\t\t: %d\n", preload);
  87        seq_printf(seq, "delete\t\t: %d\n", delete);
  88
  89        return 0;
  90}
  91
  92static const struct rtc_class_ops ep93xx_rtc_ops = {
  93        .read_time      = ep93xx_rtc_read_time,
  94        .set_mmss       = ep93xx_rtc_set_mmss,
  95        .proc           = ep93xx_rtc_proc,
  96};
  97
  98static ssize_t ep93xx_rtc_show_comp_preload(struct device *dev,
  99                        struct device_attribute *attr, char *buf)
 100{
 101        unsigned short preload;
 102
 103        ep93xx_rtc_get_swcomp(dev, &preload, NULL);
 104
 105        return sprintf(buf, "%d\n", preload);
 106}
 107static DEVICE_ATTR(comp_preload, S_IRUGO, ep93xx_rtc_show_comp_preload, NULL);
 108
 109static ssize_t ep93xx_rtc_show_comp_delete(struct device *dev,
 110                        struct device_attribute *attr, char *buf)
 111{
 112        unsigned short delete;
 113
 114        ep93xx_rtc_get_swcomp(dev, NULL, &delete);
 115
 116        return sprintf(buf, "%d\n", delete);
 117}
 118static DEVICE_ATTR(comp_delete, S_IRUGO, ep93xx_rtc_show_comp_delete, NULL);
 119
 120static struct attribute *ep93xx_rtc_attrs[] = {
 121        &dev_attr_comp_preload.attr,
 122        &dev_attr_comp_delete.attr,
 123        NULL
 124};
 125
 126static const struct attribute_group ep93xx_rtc_sysfs_files = {
 127        .attrs  = ep93xx_rtc_attrs,
 128};
 129
 130static int ep93xx_rtc_probe(struct platform_device *pdev)
 131{
 132        struct ep93xx_rtc *ep93xx_rtc;
 133        struct resource *res;
 134        int err;
 135
 136        ep93xx_rtc = devm_kzalloc(&pdev->dev, sizeof(*ep93xx_rtc), GFP_KERNEL);
 137        if (!ep93xx_rtc)
 138                return -ENOMEM;
 139
 140        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 141        if (!res)
 142                return -ENXIO;
 143
 144        if (!devm_request_mem_region(&pdev->dev, res->start,
 145                                     resource_size(res), pdev->name))
 146                return -EBUSY;
 147
 148        ep93xx_rtc->mmio_base = devm_ioremap(&pdev->dev, res->start,
 149                                             resource_size(res));
 150        if (!ep93xx_rtc->mmio_base)
 151                return -ENXIO;
 152
 153        pdev->dev.platform_data = ep93xx_rtc;
 154        platform_set_drvdata(pdev, ep93xx_rtc);
 155
 156        ep93xx_rtc->rtc = devm_rtc_device_register(&pdev->dev,
 157                                pdev->name, &ep93xx_rtc_ops, THIS_MODULE);
 158        if (IS_ERR(ep93xx_rtc->rtc)) {
 159                err = PTR_ERR(ep93xx_rtc->rtc);
 160                goto exit;
 161        }
 162
 163        err = sysfs_create_group(&pdev->dev.kobj, &ep93xx_rtc_sysfs_files);
 164        if (err)
 165                goto exit;
 166
 167        return 0;
 168
 169exit:
 170        platform_set_drvdata(pdev, NULL);
 171        pdev->dev.platform_data = NULL;
 172        return err;
 173}
 174
 175static int ep93xx_rtc_remove(struct platform_device *pdev)
 176{
 177        sysfs_remove_group(&pdev->dev.kobj, &ep93xx_rtc_sysfs_files);
 178        platform_set_drvdata(pdev, NULL);
 179        pdev->dev.platform_data = NULL;
 180
 181        return 0;
 182}
 183
 184static struct platform_driver ep93xx_rtc_driver = {
 185        .driver         = {
 186                .name   = "ep93xx-rtc",
 187                .owner  = THIS_MODULE,
 188        },
 189        .probe          = ep93xx_rtc_probe,
 190        .remove         = ep93xx_rtc_remove,
 191};
 192
 193module_platform_driver(ep93xx_rtc_driver);
 194
 195MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
 196MODULE_DESCRIPTION("EP93XX RTC driver");
 197MODULE_LICENSE("GPL");
 198MODULE_VERSION(DRV_VERSION);
 199MODULE_ALIAS("platform:ep93xx-rtc");
 200