Commit 362600fe60fd18a25b4de8ec544b9e24e77e1484

Authored by Raphael Assenat
Committed by Linus Torvalds
1 parent 9be05b57bd

[PATCH] Add v3020 RTC support

This patch adds support for the v3020 RTC from EM Microelectronic.

The v3020 RTC is designed to be connected on a bus using only one data bit.
 Since any data bit may be used, it is necessary to specify this to the
driver by passing a struct v3020_platform_data pointer (see
include/linux/rtc-v3020.h) to the driver.

Part of the following code comes from the kernel patchs produced by
Compulab for their products.  The original file (available here:
http://raph.people.8d.com/misc/emv3020.c) was released under the terms of
the GPL license.

[akpm@osdl.org: cleanups]
Signed-off-by: Raphael Assenat <raph@raphnet.net>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

Showing 4 changed files with 310 additions and 0 deletions Side-by-side Diff

... ... @@ -227,5 +227,15 @@
227 227 This driver can also be built as a module. If so, the module
228 228 will be called rtc-max6902.
229 229  
  230 +config RTC_DRV_V3020
  231 + tristate "EM Microelectronic V3020"
  232 + depends on RTC_CLASS
  233 + help
  234 + If you say yes here you will get support for the
  235 + EM Microelectronic v3020 RTC chip.
  236 +
  237 + This driver can also be built as a module. If so, the module
  238 + will be called rtc-v3020.
  239 +
230 240 endmenu
drivers/rtc/Makefile
... ... @@ -24,4 +24,5 @@
24 24 obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o
25 25 obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o
26 26 obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
  27 +obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o
drivers/rtc/rtc-v3020.c
  1 +/* drivers/rtc/rtc-v3020.c
  2 + *
  3 + * Copyright (C) 2006 8D Technologies inc.
  4 + * Copyright (C) 2004 Compulab Ltd.
  5 + *
  6 + * This program is free software; you can redistribute it and/or modify
  7 + * it under the terms of the GNU General Public License version 2 as
  8 + * published by the Free Software Foundation.
  9 + *
  10 + * Driver for the V3020 RTC
  11 + *
  12 + * Changelog:
  13 + *
  14 + * 10-May-2006: Raphael Assenat <raph@8d.com>
  15 + * - Converted to platform driver
  16 + * - Use the generic rtc class
  17 + *
  18 + * ??-???-2004: Someone at Compulab
  19 + * - Initial driver creation.
  20 + *
  21 + */
  22 +#include <linux/platform_device.h>
  23 +#include <linux/module.h>
  24 +#include <linux/init.h>
  25 +#include <linux/rtc.h>
  26 +#include <linux/types.h>
  27 +#include <linux/bcd.h>
  28 +#include <linux/rtc-v3020.h>
  29 +
  30 +#include <asm/io.h>
  31 +
  32 +#undef DEBUG
  33 +
  34 +struct v3020 {
  35 + void __iomem *ioaddress;
  36 + int leftshift;
  37 + struct rtc_device *rtc;
  38 +};
  39 +
  40 +static void v3020_set_reg(struct v3020 *chip, unsigned char address,
  41 + unsigned char data)
  42 +{
  43 + int i;
  44 + unsigned char tmp;
  45 +
  46 + tmp = address;
  47 + for (i = 0; i < 4; i++) {
  48 + writel((tmp & 1) << chip->leftshift, chip->ioaddress);
  49 + tmp >>= 1;
  50 + }
  51 +
  52 + /* Commands dont have data */
  53 + if (!V3020_IS_COMMAND(address)) {
  54 + for (i = 0; i < 8; i++) {
  55 + writel((data & 1) << chip->leftshift, chip->ioaddress);
  56 + data >>= 1;
  57 + }
  58 + }
  59 +}
  60 +
  61 +static unsigned char v3020_get_reg(struct v3020 *chip, unsigned char address)
  62 +{
  63 + unsigned int data=0;
  64 + int i;
  65 +
  66 + for (i = 0; i < 4; i++) {
  67 + writel((address & 1) << chip->leftshift, chip->ioaddress);
  68 + address >>= 1;
  69 + }
  70 +
  71 + for (i = 0; i < 8; i++) {
  72 + data >>= 1;
  73 + if (readl(chip->ioaddress) & (1 << chip->leftshift))
  74 + data |= 0x80;
  75 + }
  76 +
  77 + return data;
  78 +}
  79 +
  80 +static int v3020_read_time(struct device *dev, struct rtc_time *dt)
  81 +{
  82 + struct v3020 *chip = dev_get_drvdata(dev);
  83 + int tmp;
  84 +
  85 + /* Copy the current time to ram... */
  86 + v3020_set_reg(chip, V3020_CMD_CLOCK2RAM, 0);
  87 +
  88 + /* ...and then read constant values. */
  89 + tmp = v3020_get_reg(chip, V3020_SECONDS);
  90 + dt->tm_sec = BCD2BIN(tmp);
  91 + tmp = v3020_get_reg(chip, V3020_MINUTES);
  92 + dt->tm_min = BCD2BIN(tmp);
  93 + tmp = v3020_get_reg(chip, V3020_HOURS);
  94 + dt->tm_hour = BCD2BIN(tmp);
  95 + tmp = v3020_get_reg(chip, V3020_MONTH_DAY);
  96 + dt->tm_mday = BCD2BIN(tmp);
  97 + tmp = v3020_get_reg(chip, V3020_MONTH);
  98 + dt->tm_mon = BCD2BIN(tmp);
  99 + tmp = v3020_get_reg(chip, V3020_WEEK_DAY);
  100 + dt->tm_wday = BCD2BIN(tmp);
  101 + tmp = v3020_get_reg(chip, V3020_YEAR);
  102 + dt->tm_year = BCD2BIN(tmp)+100;
  103 +
  104 +#ifdef DEBUG
  105 + printk("\n%s : Read RTC values\n",__FUNCTION__);
  106 + printk("tm_hour: %i\n",dt->tm_hour);
  107 + printk("tm_min : %i\n",dt->tm_min);
  108 + printk("tm_sec : %i\n",dt->tm_sec);
  109 + printk("tm_year: %i\n",dt->tm_year);
  110 + printk("tm_mon : %i\n",dt->tm_mon);
  111 + printk("tm_mday: %i\n",dt->tm_mday);
  112 + printk("tm_wday: %i\n",dt->tm_wday);
  113 +#endif
  114 +
  115 + return 0;
  116 +}
  117 +
  118 +
  119 +static int v3020_set_time(struct device *dev, struct rtc_time *dt)
  120 +{
  121 + struct v3020 *chip = dev_get_drvdata(dev);
  122 +
  123 +#ifdef DEBUG
  124 + printk("\n%s : Setting RTC values\n",__FUNCTION__);
  125 + printk("tm_sec : %i\n",dt->tm_sec);
  126 + printk("tm_min : %i\n",dt->tm_min);
  127 + printk("tm_hour: %i\n",dt->tm_hour);
  128 + printk("tm_mday: %i\n",dt->tm_mday);
  129 + printk("tm_wday: %i\n",dt->tm_wday);
  130 + printk("tm_year: %i\n",dt->tm_year);
  131 +#endif
  132 +
  133 + /* Write all the values to ram... */
  134 + v3020_set_reg(chip, V3020_SECONDS, BIN2BCD(dt->tm_sec));
  135 + v3020_set_reg(chip, V3020_MINUTES, BIN2BCD(dt->tm_min));
  136 + v3020_set_reg(chip, V3020_HOURS, BIN2BCD(dt->tm_hour));
  137 + v3020_set_reg(chip, V3020_MONTH_DAY, BIN2BCD(dt->tm_mday));
  138 + v3020_set_reg(chip, V3020_MONTH, BIN2BCD(dt->tm_mon));
  139 + v3020_set_reg(chip, V3020_WEEK_DAY, BIN2BCD(dt->tm_wday));
  140 + v3020_set_reg(chip, V3020_YEAR, BIN2BCD(dt->tm_year % 100));
  141 +
  142 + /* ...and set the clock. */
  143 + v3020_set_reg(chip, V3020_CMD_RAM2CLOCK, 0);
  144 +
  145 + /* Compulab used this delay here. I dont know why,
  146 + * the datasheet does not specify a delay. */
  147 + /*mdelay(5);*/
  148 +
  149 + return 0;
  150 +}
  151 +
  152 +static struct rtc_class_ops v3020_rtc_ops = {
  153 + .read_time = v3020_read_time,
  154 + .set_time = v3020_set_time,
  155 +};
  156 +
  157 +static int rtc_probe(struct platform_device *pdev)
  158 +{
  159 + struct v3020_platform_data *pdata = pdev->dev.platform_data;
  160 + struct v3020 *chip;
  161 + struct rtc_device *rtc;
  162 + int retval = -EBUSY;
  163 + int i;
  164 + int temp;
  165 +
  166 + if (pdev->num_resources != 1)
  167 + return -EBUSY;
  168 +
  169 + if (pdev->resource[0].flags != IORESOURCE_MEM)
  170 + return -EBUSY;
  171 +
  172 + if (pdev == NULL)
  173 + return -EBUSY;
  174 +
  175 + chip = kzalloc(sizeof *chip, GFP_KERNEL);
  176 + if (!chip)
  177 + return -ENOMEM;
  178 +
  179 + chip->leftshift = pdata->leftshift;
  180 + chip->ioaddress = ioremap(pdev->resource[0].start, 1);
  181 + if (chip->ioaddress == NULL)
  182 + goto err_chip;
  183 +
  184 + /* Make sure the v3020 expects a communication cycle
  185 + * by reading 8 times */
  186 + for (i = 0; i < 8; i++)
  187 + temp = readl(chip->ioaddress);
  188 +
  189 + /* Test chip by doing a write/read sequence
  190 + * to the chip ram */
  191 + v3020_set_reg(chip, V3020_SECONDS, 0x33);
  192 + if(v3020_get_reg(chip, V3020_SECONDS) != 0x33) {
  193 + retval = -ENODEV;
  194 + goto err_io;
  195 + }
  196 +
  197 + /* Make sure frequency measurment mode, test modes, and lock
  198 + * are all disabled */
  199 + v3020_set_reg(chip, V3020_STATUS_0, 0x0);
  200 +
  201 + dev_info(&pdev->dev, "Chip available at physical address 0x%p,"
  202 + "data connected to D%d\n",
  203 + (void*)pdev->resource[0].start,
  204 + chip->leftshift);
  205 +
  206 + platform_set_drvdata(pdev, chip);
  207 +
  208 + rtc = rtc_device_register("v3020",
  209 + &pdev->dev, &v3020_rtc_ops, THIS_MODULE);
  210 + if (IS_ERR(rtc)) {
  211 + retval = PTR_ERR(rtc);
  212 + goto err_io;
  213 + }
  214 + chip->rtc = rtc;
  215 +
  216 + return 0;
  217 +
  218 +err_io:
  219 + iounmap(chip->ioaddress);
  220 +err_chip:
  221 + kfree(chip);
  222 +
  223 + return retval;
  224 +}
  225 +
  226 +static int rtc_remove(struct platform_device *dev)
  227 +{
  228 + struct v3020 *chip = platform_get_drvdata(dev);
  229 + struct rtc_device *rtc = chip->rtc;
  230 +
  231 + if (rtc)
  232 + rtc_device_unregister(rtc);
  233 +
  234 + iounmap(chip->ioaddress);
  235 + kfree(chip);
  236 +
  237 + return 0;
  238 +}
  239 +
  240 +static struct platform_driver rtc_device_driver = {
  241 + .probe = rtc_probe,
  242 + .remove = rtc_remove,
  243 + .driver = {
  244 + .name = "v3020",
  245 + .owner = THIS_MODULE,
  246 + },
  247 +};
  248 +
  249 +static __init int v3020_init(void)
  250 +{
  251 + return platform_driver_register(&rtc_device_driver);
  252 +}
  253 +
  254 +static __exit void v3020_exit(void)
  255 +{
  256 + platform_driver_unregister(&rtc_device_driver);
  257 +}
  258 +
  259 +module_init(v3020_init);
  260 +module_exit(v3020_exit);
  261 +
  262 +MODULE_DESCRIPTION("V3020 RTC");
  263 +MODULE_AUTHOR("Raphael Assenat");
  264 +MODULE_LICENSE("GPL");
include/linux/rtc-v3020.h
  1 +/*
  2 + * v3020.h - Registers definition and platform data structure for the v3020 RTC.
  3 + *
  4 + * This file is subject to the terms and conditions of the GNU General Public
  5 + * License. See the file "COPYING" in the main directory of this archive
  6 + * for more details.
  7 + *
  8 + * Copyright (C) 2006, 8D Technologies inc.
  9 + */
  10 +#ifndef __LINUX_V3020_H
  11 +#define __LINUX_V3020_H
  12 +
  13 +/* The v3020 has only one data pin but which one
  14 + * is used depends on the board. */
  15 +struct v3020_platform_data {
  16 + int leftshift; /* (1<<(leftshift)) & readl() */
  17 +};
  18 +
  19 +#define V3020_STATUS_0 0x00
  20 +#define V3020_STATUS_1 0x01
  21 +#define V3020_SECONDS 0x02
  22 +#define V3020_MINUTES 0x03
  23 +#define V3020_HOURS 0x04
  24 +#define V3020_MONTH_DAY 0x05
  25 +#define V3020_MONTH 0x06
  26 +#define V3020_YEAR 0x07
  27 +#define V3020_WEEK_DAY 0x08
  28 +#define V3020_WEEK 0x09
  29 +
  30 +#define V3020_IS_COMMAND(val) ((val)>=0x0E)
  31 +
  32 +#define V3020_CMD_RAM2CLOCK 0x0E
  33 +#define V3020_CMD_CLOCK2RAM 0x0F
  34 +
  35 +#endif /* __LINUX_V3020_H */