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#include <linux/delay.h> 30#include <linux/gpio.h> 31#include <linux/slab.h> 32 33#include <linux/io.h> 34 35#undef DEBUG 36 37struct v3020; 38 39struct v3020_chip_ops { 40 int (*map_io)(struct v3020 *chip, struct platform_device *pdev, 41 struct v3020_platform_data *pdata); 42 void (*unmap_io)(struct v3020 *chip); 43 unsigned char (*read_bit)(struct v3020 *chip); 44 void (*write_bit)(struct v3020 *chip, unsigned char bit); 45}; 46 47#define V3020_CS 0 48#define V3020_WR 1 49#define V3020_RD 2 50#define V3020_IO 3 51 52struct v3020_gpio { 53 const char *name; 54 unsigned int gpio; 55}; 56 57struct v3020 { 58 /* MMIO access */ 59 void __iomem *ioaddress; 60 int leftshift; 61 62 /* GPIO access */ 63 struct v3020_gpio *gpio; 64 65 struct v3020_chip_ops *ops; 66 67 struct rtc_device *rtc; 68}; 69 70 71static int v3020_mmio_map(struct v3020 *chip, struct platform_device *pdev, 72 struct v3020_platform_data *pdata) 73{ 74 if (pdev->num_resources != 1) 75 return -EBUSY; 76 77 if (pdev->resource[0].flags != IORESOURCE_MEM) 78 return -EBUSY; 79 80 chip->leftshift = pdata->leftshift; 81 chip->ioaddress = ioremap(pdev->resource[0].start, 1); 82 if (chip->ioaddress == NULL) 83 return -EBUSY; 84 85 return 0; 86} 87 88static void v3020_mmio_unmap(struct v3020 *chip) 89{ 90 iounmap(chip->ioaddress); 91} 92 93static void v3020_mmio_write_bit(struct v3020 *chip, unsigned char bit) 94{ 95 writel(bit << chip->leftshift, chip->ioaddress); 96} 97 98static unsigned char v3020_mmio_read_bit(struct v3020 *chip) 99{ 100 return !!(readl(chip->ioaddress) & (1 << chip->leftshift)); 101} 102 103static struct v3020_chip_ops v3020_mmio_ops = { 104 .map_io = v3020_mmio_map, 105 .unmap_io = v3020_mmio_unmap, 106 .read_bit = v3020_mmio_read_bit, 107 .write_bit = v3020_mmio_write_bit, 108}; 109 110static struct v3020_gpio v3020_gpio[] = { 111 { "RTC CS", 0 }, 112 { "RTC WR", 0 }, 113 { "RTC RD", 0 }, 114 { "RTC IO", 0 }, 115}; 116 117static int v3020_gpio_map(struct v3020 *chip, struct platform_device *pdev, 118 struct v3020_platform_data *pdata) 119{ 120 int i, err; 121 122 v3020_gpio[V3020_CS].gpio = pdata->gpio_cs; 123 v3020_gpio[V3020_WR].gpio = pdata->gpio_wr; 124 v3020_gpio[V3020_RD].gpio = pdata->gpio_rd; 125 v3020_gpio[V3020_IO].gpio = pdata->gpio_io; 126 127 for (i = 0; i < ARRAY_SIZE(v3020_gpio); i++) { 128 err = gpio_request(v3020_gpio[i].gpio, v3020_gpio[i].name); 129 if (err) 130 goto err_request; 131 132 gpio_direction_output(v3020_gpio[i].gpio, 1); 133 } 134 135 chip->gpio = v3020_gpio; 136 137 return 0; 138 139err_request: 140 while (--i >= 0) 141 gpio_free(v3020_gpio[i].gpio); 142 143 return err; 144} 145 146static void v3020_gpio_unmap(struct v3020 *chip) 147{ 148 int i; 149 150 for (i = 0; i < ARRAY_SIZE(v3020_gpio); i++) 151 gpio_free(v3020_gpio[i].gpio); 152} 153 154static void v3020_gpio_write_bit(struct v3020 *chip, unsigned char bit) 155{ 156 gpio_direction_output(chip->gpio[V3020_IO].gpio, bit); 157 gpio_set_value(chip->gpio[V3020_CS].gpio, 0); 158 gpio_set_value(chip->gpio[V3020_WR].gpio, 0); 159 udelay(1); 160 gpio_set_value(chip->gpio[V3020_WR].gpio, 1); 161 gpio_set_value(chip->gpio[V3020_CS].gpio, 1); 162} 163 164static unsigned char v3020_gpio_read_bit(struct v3020 *chip) 165{ 166 int bit; 167 168 gpio_direction_input(chip->gpio[V3020_IO].gpio); 169 gpio_set_value(chip->gpio[V3020_CS].gpio, 0); 170 gpio_set_value(chip->gpio[V3020_RD].gpio, 0); 171 udelay(1); 172 bit = !!gpio_get_value(chip->gpio[V3020_IO].gpio); 173 udelay(1); 174 gpio_set_value(chip->gpio[V3020_RD].gpio, 1); 175 gpio_set_value(chip->gpio[V3020_CS].gpio, 1); 176 177 return bit; 178} 179 180static struct v3020_chip_ops v3020_gpio_ops = { 181 .map_io = v3020_gpio_map, 182 .unmap_io = v3020_gpio_unmap, 183 .read_bit = v3020_gpio_read_bit, 184 .write_bit = v3020_gpio_write_bit, 185}; 186 187static void v3020_set_reg(struct v3020 *chip, unsigned char address, 188 unsigned char data) 189{ 190 int i; 191 unsigned char tmp; 192 193 tmp = address; 194 for (i = 0; i < 4; i++) { 195 chip->ops->write_bit(chip, (tmp & 1)); 196 tmp >>= 1; 197 udelay(1); 198 } 199 200 /* Commands dont have data */ 201 if (!V3020_IS_COMMAND(address)) { 202 for (i = 0; i < 8; i++) { 203 chip->ops->write_bit(chip, (data & 1)); 204 data >>= 1; 205 udelay(1); 206 } 207 } 208} 209 210static unsigned char v3020_get_reg(struct v3020 *chip, unsigned char address) 211{ 212 unsigned int data = 0; 213 int i; 214 215 for (i = 0; i < 4; i++) { 216 chip->ops->write_bit(chip, (address & 1)); 217 address >>= 1; 218 udelay(1); 219 } 220 221 for (i = 0; i < 8; i++) { 222 data >>= 1; 223 if (chip->ops->read_bit(chip)) 224 data |= 0x80; 225 udelay(1); 226 } 227 228 return data; 229} 230 231static int v3020_read_time(struct device *dev, struct rtc_time *dt) 232{ 233 struct v3020 *chip = dev_get_drvdata(dev); 234 int tmp; 235 236 /* Copy the current time to ram... */ 237 v3020_set_reg(chip, V3020_CMD_CLOCK2RAM, 0); 238 239 /* ...and then read constant values. */ 240 tmp = v3020_get_reg(chip, V3020_SECONDS); 241 dt->tm_sec = bcd2bin(tmp); 242 tmp = v3020_get_reg(chip, V3020_MINUTES); 243 dt->tm_min = bcd2bin(tmp); 244 tmp = v3020_get_reg(chip, V3020_HOURS); 245 dt->tm_hour = bcd2bin(tmp); 246 tmp = v3020_get_reg(chip, V3020_MONTH_DAY); 247 dt->tm_mday = bcd2bin(tmp); 248 tmp = v3020_get_reg(chip, V3020_MONTH); 249 dt->tm_mon = bcd2bin(tmp) - 1; 250 tmp = v3020_get_reg(chip, V3020_WEEK_DAY); 251 dt->tm_wday = bcd2bin(tmp); 252 tmp = v3020_get_reg(chip, V3020_YEAR); 253 dt->tm_year = bcd2bin(tmp)+100; 254 255 dev_dbg(dev, "\n%s : Read RTC values\n", __func__); 256 dev_dbg(dev, "tm_hour: %i\n", dt->tm_hour); 257 dev_dbg(dev, "tm_min : %i\n", dt->tm_min); 258 dev_dbg(dev, "tm_sec : %i\n", dt->tm_sec); 259 dev_dbg(dev, "tm_year: %i\n", dt->tm_year); 260 dev_dbg(dev, "tm_mon : %i\n", dt->tm_mon); 261 dev_dbg(dev, "tm_mday: %i\n", dt->tm_mday); 262 dev_dbg(dev, "tm_wday: %i\n", dt->tm_wday); 263 264 return 0; 265} 266 267 268static int v3020_set_time(struct device *dev, struct rtc_time *dt) 269{ 270 struct v3020 *chip = dev_get_drvdata(dev); 271 272 dev_dbg(dev, "\n%s : Setting RTC values\n", __func__); 273 dev_dbg(dev, "tm_sec : %i\n", dt->tm_sec); 274 dev_dbg(dev, "tm_min : %i\n", dt->tm_min); 275 dev_dbg(dev, "tm_hour: %i\n", dt->tm_hour); 276 dev_dbg(dev, "tm_mday: %i\n", dt->tm_mday); 277 dev_dbg(dev, "tm_wday: %i\n", dt->tm_wday); 278 dev_dbg(dev, "tm_year: %i\n", dt->tm_year); 279 280 /* Write all the values to ram... */ 281 v3020_set_reg(chip, V3020_SECONDS, bin2bcd(dt->tm_sec)); 282 v3020_set_reg(chip, V3020_MINUTES, bin2bcd(dt->tm_min)); 283 v3020_set_reg(chip, V3020_HOURS, bin2bcd(dt->tm_hour)); 284 v3020_set_reg(chip, V3020_MONTH_DAY, bin2bcd(dt->tm_mday)); 285 v3020_set_reg(chip, V3020_MONTH, bin2bcd(dt->tm_mon + 1)); 286 v3020_set_reg(chip, V3020_WEEK_DAY, bin2bcd(dt->tm_wday)); 287 v3020_set_reg(chip, V3020_YEAR, bin2bcd(dt->tm_year % 100)); 288 289 /* ...and set the clock. */ 290 v3020_set_reg(chip, V3020_CMD_RAM2CLOCK, 0); 291 292 /* Compulab used this delay here. I dont know why, 293 * the datasheet does not specify a delay. */ 294 /*mdelay(5);*/ 295 296 return 0; 297} 298 299static const struct rtc_class_ops v3020_rtc_ops = { 300 .read_time = v3020_read_time, 301 .set_time = v3020_set_time, 302}; 303 304static int rtc_probe(struct platform_device *pdev) 305{ 306 struct v3020_platform_data *pdata = dev_get_platdata(&pdev->dev); 307 struct v3020 *chip; 308 int retval = -EBUSY; 309 int i; 310 int temp; 311 312 chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); 313 if (!chip) 314 return -ENOMEM; 315 316 if (pdata->use_gpio) 317 chip->ops = &v3020_gpio_ops; 318 else 319 chip->ops = &v3020_mmio_ops; 320 321 retval = chip->ops->map_io(chip, pdev, pdata); 322 if (retval) 323 return retval; 324 325 /* Make sure the v3020 expects a communication cycle 326 * by reading 8 times */ 327 for (i = 0; i < 8; i++) 328 temp = chip->ops->read_bit(chip); 329 330 /* Test chip by doing a write/read sequence 331 * to the chip ram */ 332 v3020_set_reg(chip, V3020_SECONDS, 0x33); 333 if (v3020_get_reg(chip, V3020_SECONDS) != 0x33) { 334 retval = -ENODEV; 335 goto err_io; 336 } 337 338 /* Make sure frequency measurement mode, test modes, and lock 339 * are all disabled */ 340 v3020_set_reg(chip, V3020_STATUS_0, 0x0); 341 342 if (pdata->use_gpio) 343 dev_info(&pdev->dev, "Chip available at GPIOs " 344 "%d, %d, %d, %d\n", 345 chip->gpio[V3020_CS].gpio, chip->gpio[V3020_WR].gpio, 346 chip->gpio[V3020_RD].gpio, chip->gpio[V3020_IO].gpio); 347 else 348 dev_info(&pdev->dev, "Chip available at " 349 "physical address 0x%llx," 350 "data connected to D%d\n", 351 (unsigned long long)pdev->resource[0].start, 352 chip->leftshift); 353 354 platform_set_drvdata(pdev, chip); 355 356 chip->rtc = devm_rtc_device_register(&pdev->dev, "v3020", 357 &v3020_rtc_ops, THIS_MODULE); 358 if (IS_ERR(chip->rtc)) { 359 retval = PTR_ERR(chip->rtc); 360 goto err_io; 361 } 362 363 return 0; 364 365err_io: 366 chip->ops->unmap_io(chip); 367 368 return retval; 369} 370 371static int rtc_remove(struct platform_device *dev) 372{ 373 struct v3020 *chip = platform_get_drvdata(dev); 374 375 chip->ops->unmap_io(chip); 376 377 return 0; 378} 379 380static struct platform_driver rtc_device_driver = { 381 .probe = rtc_probe, 382 .remove = rtc_remove, 383 .driver = { 384 .name = "v3020", 385 }, 386}; 387 388module_platform_driver(rtc_device_driver); 389 390MODULE_DESCRIPTION("V3020 RTC"); 391MODULE_AUTHOR("Raphael Assenat"); 392MODULE_LICENSE("GPL"); 393MODULE_ALIAS("platform:v3020"); 394