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 34struct v3020 { 35 void __iomem *ioaddress; 36 int leftshift; 37 struct rtc_device *rtc; 38}; 39 40static 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 61static 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 80static 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 119static 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 152static const struct rtc_class_ops v3020_rtc_ops = { 153 .read_time = v3020_read_time, 154 .set_time = v3020_set_time, 155}; 156 157static 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 chip = kzalloc(sizeof *chip, GFP_KERNEL); 173 if (!chip) 174 return -ENOMEM; 175 176 chip->leftshift = pdata->leftshift; 177 chip->ioaddress = ioremap(pdev->resource[0].start, 1); 178 if (chip->ioaddress == NULL) 179 goto err_chip; 180 181 /* Make sure the v3020 expects a communication cycle 182 * by reading 8 times */ 183 for (i = 0; i < 8; i++) 184 temp = readl(chip->ioaddress); 185 186 /* Test chip by doing a write/read sequence 187 * to the chip ram */ 188 v3020_set_reg(chip, V3020_SECONDS, 0x33); 189 if(v3020_get_reg(chip, V3020_SECONDS) != 0x33) { 190 retval = -ENODEV; 191 goto err_io; 192 } 193 194 /* Make sure frequency measurment mode, test modes, and lock 195 * are all disabled */ 196 v3020_set_reg(chip, V3020_STATUS_0, 0x0); 197 198 dev_info(&pdev->dev, "Chip available at physical address 0x%llx," 199 "data connected to D%d\n", 200 (unsigned long long)pdev->resource[0].start, 201 chip->leftshift); 202 203 platform_set_drvdata(pdev, chip); 204 205 rtc = rtc_device_register("v3020", 206 &pdev->dev, &v3020_rtc_ops, THIS_MODULE); 207 if (IS_ERR(rtc)) { 208 retval = PTR_ERR(rtc); 209 goto err_io; 210 } 211 chip->rtc = rtc; 212 213 return 0; 214 215err_io: 216 iounmap(chip->ioaddress); 217err_chip: 218 kfree(chip); 219 220 return retval; 221} 222 223static int rtc_remove(struct platform_device *dev) 224{ 225 struct v3020 *chip = platform_get_drvdata(dev); 226 struct rtc_device *rtc = chip->rtc; 227 228 if (rtc) 229 rtc_device_unregister(rtc); 230 231 iounmap(chip->ioaddress); 232 kfree(chip); 233 234 return 0; 235} 236 237static struct platform_driver rtc_device_driver = { 238 .probe = rtc_probe, 239 .remove = rtc_remove, 240 .driver = { 241 .name = "v3020", 242 .owner = THIS_MODULE, 243 }, 244}; 245 246static __init int v3020_init(void) 247{ 248 return platform_driver_register(&rtc_device_driver); 249} 250 251static __exit void v3020_exit(void) 252{ 253 platform_driver_unregister(&rtc_device_driver); 254} 255 256module_init(v3020_init); 257module_exit(v3020_exit); 258 259MODULE_DESCRIPTION("V3020 RTC"); 260MODULE_AUTHOR("Raphael Assenat"); 261MODULE_LICENSE("GPL"); 262