Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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>

authored by

Raphael Assenat and committed by
Linus Torvalds
362600fe 9be05b57

+310
+10
drivers/rtc/Kconfig
··· 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
+1
drivers/rtc/Makefile
··· 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
+264
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");
+35
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 */