Commit d1dbd82e2ff02181a7102088a9fe83e17ddbcb47

Authored by Thomas Bogendoerfer
Committed by Ralf Baechle
1 parent 3ec066cdb7

RTC: M48T35: new RTC driver

This driver replaces the broken ip27-rtc driver in drivers/char and
gives back RTC support for SGI IP27 machines.

Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Acked-by: Alessandro Zummo <alessandro.zummo@towertech.it>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>

Showing 3 changed files with 244 additions and 0 deletions Side-by-side Diff

... ... @@ -410,6 +410,15 @@
410 410 This driver can also be built as a module. If so, the module
411 411 will be called rtc-m48t86.
412 412  
  413 +config RTC_DRV_M48T35
  414 + tristate "ST M48T35"
  415 + help
  416 + If you say Y here you will get support for the
  417 + ST M48T35 RTC chip.
  418 +
  419 + This driver can also be built as a module, if so, the module
  420 + will be called "rtc-m48t35".
  421 +
413 422 config RTC_DRV_M48T59
414 423 tristate "ST M48T59/M48T08/M48T02"
415 424 help
drivers/rtc/Makefile
... ... @@ -37,6 +37,7 @@
37 37 obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
38 38 obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o
39 39 obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o
  40 +obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o
40 41 obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o
41 42 obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o
42 43 obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
drivers/rtc/rtc-m48t35.c
  1 +/*
  2 + * Driver for the SGS-Thomson M48T35 Timekeeper RAM chip
  3 + *
  4 + * Copyright (C) 2000 Silicon Graphics, Inc.
  5 + * Written by Ulf Carlsson (ulfc@engr.sgi.com)
  6 + *
  7 + * Copyright (C) 2008 Thomas Bogendoerfer
  8 + *
  9 + * Based on code written by Paul Gortmaker.
  10 + *
  11 + * This program is free software; you can redistribute it and/or modify it
  12 + * under the terms of the GNU General Public License as published by the
  13 + * Free Software Foundation; either version 2 of the License, or (at your
  14 + * option) any later version.
  15 + */
  16 +
  17 +#include <linux/module.h>
  18 +#include <linux/rtc.h>
  19 +#include <linux/platform_device.h>
  20 +#include <linux/bcd.h>
  21 +
  22 +#define DRV_VERSION "1.0"
  23 +
  24 +struct m48t35_rtc {
  25 + u8 pad[0x7ff8]; /* starts at 0x7ff8 */
  26 + u8 control;
  27 + u8 sec;
  28 + u8 min;
  29 + u8 hour;
  30 + u8 day;
  31 + u8 date;
  32 + u8 month;
  33 + u8 year;
  34 +};
  35 +
  36 +#define M48T35_RTC_SET 0x80
  37 +#define M48T35_RTC_READ 0x40
  38 +
  39 +struct m48t35_priv {
  40 + struct rtc_device *rtc;
  41 + struct m48t35_rtc __iomem *reg;
  42 + size_t size;
  43 + unsigned long baseaddr;
  44 + spinlock_t lock;
  45 +};
  46 +
  47 +static int m48t35_read_time(struct device *dev, struct rtc_time *tm)
  48 +{
  49 + struct m48t35_priv *priv = dev_get_drvdata(dev);
  50 + u8 control;
  51 +
  52 + /*
  53 + * Only the values that we read from the RTC are set. We leave
  54 + * tm_wday, tm_yday and tm_isdst untouched. Even though the
  55 + * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated
  56 + * by the RTC when initially set to a non-zero value.
  57 + */
  58 + spin_lock_irq(&priv->lock);
  59 + control = readb(&priv->reg->control);
  60 + writeb(control | M48T35_RTC_READ, &priv->reg->control);
  61 + tm->tm_sec = readb(&priv->reg->sec);
  62 + tm->tm_min = readb(&priv->reg->min);
  63 + tm->tm_hour = readb(&priv->reg->hour);
  64 + tm->tm_mday = readb(&priv->reg->date);
  65 + tm->tm_mon = readb(&priv->reg->month);
  66 + tm->tm_year = readb(&priv->reg->year);
  67 + writeb(control, &priv->reg->control);
  68 + spin_unlock_irq(&priv->lock);
  69 +
  70 + tm->tm_sec = bcd2bin(tm->tm_sec);
  71 + tm->tm_min = bcd2bin(tm->tm_min);
  72 + tm->tm_hour = bcd2bin(tm->tm_hour);
  73 + tm->tm_mday = bcd2bin(tm->tm_mday);
  74 + tm->tm_mon = bcd2bin(tm->tm_mon);
  75 + tm->tm_year = bcd2bin(tm->tm_year);
  76 +
  77 + /*
  78 + * Account for differences between how the RTC uses the values
  79 + * and how they are defined in a struct rtc_time;
  80 + */
  81 + tm->tm_year += 70;
  82 + if (tm->tm_year <= 69)
  83 + tm->tm_year += 100;
  84 +
  85 + tm->tm_mon--;
  86 + return rtc_valid_tm(tm);
  87 +}
  88 +
  89 +static int m48t35_set_time(struct device *dev, struct rtc_time *tm)
  90 +{
  91 + struct m48t35_priv *priv = dev_get_drvdata(dev);
  92 + unsigned char mon, day, hrs, min, sec;
  93 + unsigned int yrs;
  94 + u8 control;
  95 +
  96 + yrs = tm->tm_year + 1900;
  97 + mon = tm->tm_mon + 1; /* tm_mon starts at zero */
  98 + day = tm->tm_mday;
  99 + hrs = tm->tm_hour;
  100 + min = tm->tm_min;
  101 + sec = tm->tm_sec;
  102 +
  103 + if (yrs < 1970)
  104 + return -EINVAL;
  105 +
  106 + yrs -= 1970;
  107 + if (yrs > 255) /* They are unsigned */
  108 + return -EINVAL;
  109 +
  110 + if (yrs > 169)
  111 + return -EINVAL;
  112 +
  113 + if (yrs >= 100)
  114 + yrs -= 100;
  115 +
  116 + sec = bin2bcd(sec);
  117 + min = bin2bcd(min);
  118 + hrs = bin2bcd(hrs);
  119 + day = bin2bcd(day);
  120 + mon = bin2bcd(mon);
  121 + yrs = bin2bcd(yrs);
  122 +
  123 + spin_lock_irq(&priv->lock);
  124 + control = readb(&priv->reg->control);
  125 + writeb(control | M48T35_RTC_SET, &priv->reg->control);
  126 + writeb(yrs, &priv->reg->year);
  127 + writeb(mon, &priv->reg->month);
  128 + writeb(day, &priv->reg->date);
  129 + writeb(hrs, &priv->reg->hour);
  130 + writeb(min, &priv->reg->min);
  131 + writeb(sec, &priv->reg->sec);
  132 + writeb(control, &priv->reg->control);
  133 + spin_unlock_irq(&priv->lock);
  134 + return 0;
  135 +}
  136 +
  137 +static const struct rtc_class_ops m48t35_ops = {
  138 + .read_time = m48t35_read_time,
  139 + .set_time = m48t35_set_time,
  140 +};
  141 +
  142 +static int __devinit m48t35_probe(struct platform_device *pdev)
  143 +{
  144 + struct rtc_device *rtc;
  145 + struct resource *res;
  146 + struct m48t35_priv *priv;
  147 + int ret = 0;
  148 +
  149 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  150 + if (!res)
  151 + return -ENODEV;
  152 + priv = kzalloc(sizeof(struct m48t35_priv), GFP_KERNEL);
  153 + if (!priv)
  154 + return -ENOMEM;
  155 +
  156 + priv->size = res->end - res->start + 1;
  157 + /*
  158 + * kludge: remove the #ifndef after ioc3 resource
  159 + * conflicts are resolved
  160 + */
  161 +#ifndef CONFIG_SGI_IP27
  162 + if (!request_mem_region(res->start, priv->size, pdev->name)) {
  163 + ret = -EBUSY;
  164 + goto out;
  165 + }
  166 +#endif
  167 + priv->baseaddr = res->start;
  168 + priv->reg = ioremap(priv->baseaddr, priv->size);
  169 + if (!priv->reg) {
  170 + ret = -ENOMEM;
  171 + goto out;
  172 + }
  173 + spin_lock_init(&priv->lock);
  174 + rtc = rtc_device_register("m48t35", &pdev->dev,
  175 + &m48t35_ops, THIS_MODULE);
  176 + if (IS_ERR(rtc)) {
  177 + ret = PTR_ERR(rtc);
  178 + goto out;
  179 + }
  180 + priv->rtc = rtc;
  181 + platform_set_drvdata(pdev, priv);
  182 + return 0;
  183 +
  184 +out:
  185 + if (priv->rtc)
  186 + rtc_device_unregister(priv->rtc);
  187 + if (priv->reg)
  188 + iounmap(priv->reg);
  189 + if (priv->baseaddr)
  190 + release_mem_region(priv->baseaddr, priv->size);
  191 + kfree(priv);
  192 + return ret;
  193 +}
  194 +
  195 +static int __devexit m48t35_remove(struct platform_device *pdev)
  196 +{
  197 + struct m48t35_priv *priv = platform_get_drvdata(pdev);
  198 +
  199 + rtc_device_unregister(priv->rtc);
  200 + iounmap(priv->reg);
  201 +#ifndef CONFIG_SGI_IP27
  202 + release_mem_region(priv->baseaddr, priv->size);
  203 +#endif
  204 + kfree(priv);
  205 + return 0;
  206 +}
  207 +
  208 +static struct platform_driver m48t35_platform_driver = {
  209 + .driver = {
  210 + .name = "rtc-m48t35",
  211 + .owner = THIS_MODULE,
  212 + },
  213 + .probe = m48t35_probe,
  214 + .remove = __devexit_p(m48t35_remove),
  215 +};
  216 +
  217 +static int __init m48t35_init(void)
  218 +{
  219 + return platform_driver_register(&m48t35_platform_driver);
  220 +}
  221 +
  222 +static void __exit m48t35_exit(void)
  223 +{
  224 + platform_driver_unregister(&m48t35_platform_driver);
  225 +}
  226 +
  227 +MODULE_AUTHOR("Thomas Bogendoerfer <tsbogend@alpha.franken.de>");
  228 +MODULE_DESCRIPTION("M48T35 RTC driver");
  229 +MODULE_LICENSE("GPL");
  230 +MODULE_VERSION(DRV_VERSION);
  231 +MODULE_ALIAS("platform:rtc-m48t35");
  232 +
  233 +module_init(m48t35_init);
  234 +module_exit(m48t35_exit);