forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for the MAX6902 SPI RTC chip. Tested on a pxa2xx cpu. The compulab code comes from the kernel patch the produce for their cn-x255 board. (inside a zip file on the http://www.compulab.co.il/x255/html/x255-developer.htm) The original file (drivers/char/max6902.c) was GPL, which is of course an appropriate licence: /* * max6902.c * * Driver for MAX6902 RTC * * Copyright (C) 2004 Compulab Ltd. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * */ For reference, you can get the original file here: http://raph.people.8d.com/misc/max6902.c [[email protected]: cleanups] Signed-off-by: Raphael Assenat <[email protected]> Cc: Alessandro Zummo <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
- Loading branch information
Showing
3 changed files
with
297 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,286 @@ | ||
/* drivers/char/max6902.c | ||
* | ||
* Copyright (C) 2006 8D Technologies inc. | ||
* Copyright (C) 2004 Compulab Ltd. | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 as | ||
* published by the Free Software Foundation. | ||
* | ||
* Driver for MAX6902 spi RTC | ||
* | ||
* Changelog: | ||
* | ||
* 24-May-2006: Raphael Assenat <[email protected]> | ||
* - Major rework | ||
* Converted to rtc_device and uses the SPI layer. | ||
* | ||
* ??-???-2005: Someone at Compulab | ||
* - Initial driver creation. | ||
*/ | ||
|
||
#include <linux/config.h> | ||
#include <linux/module.h> | ||
#include <linux/version.h> | ||
|
||
#include <linux/kernel.h> | ||
#include <linux/platform_device.h> | ||
#include <linux/init.h> | ||
#include <linux/rtc.h> | ||
#include <linux/spi/spi.h> | ||
#include <linux/bcd.h> | ||
#include <linux/delay.h> | ||
|
||
#define MAX6902_REG_SECONDS 0x01 | ||
#define MAX6902_REG_MINUTES 0x03 | ||
#define MAX6902_REG_HOURS 0x05 | ||
#define MAX6902_REG_DATE 0x07 | ||
#define MAX6902_REG_MONTH 0x09 | ||
#define MAX6902_REG_DAY 0x0B | ||
#define MAX6902_REG_YEAR 0x0D | ||
#define MAX6902_REG_CONTROL 0x0F | ||
#define MAX6902_REG_CENTURY 0x13 | ||
|
||
#undef MAX6902_DEBUG | ||
|
||
struct max6902 { | ||
struct rtc_device *rtc; | ||
u8 buf[9]; /* Burst read cmd + 8 registers */ | ||
u8 tx_buf[2]; | ||
u8 rx_buf[2]; | ||
}; | ||
|
||
static void max6902_set_reg(struct device *dev, unsigned char address, | ||
unsigned char data) | ||
{ | ||
struct spi_device *spi = to_spi_device(dev); | ||
unsigned char buf[2]; | ||
|
||
/* MSB must be '0' to write */ | ||
buf[0] = address & 0x7f; | ||
buf[1] = data; | ||
|
||
spi_write(spi, buf, 2); | ||
} | ||
|
||
static int max6902_get_reg(struct device *dev, unsigned char address, | ||
unsigned char *data) | ||
{ | ||
struct spi_device *spi = to_spi_device(dev); | ||
struct max6902 *chip = dev_get_drvdata(dev); | ||
struct spi_message message; | ||
struct spi_transfer xfer; | ||
int status; | ||
|
||
if (!data) | ||
return -EINVAL; | ||
|
||
/* Build our spi message */ | ||
spi_message_init(&message); | ||
memset(&xfer, 0, sizeof(xfer)); | ||
xfer.len = 2; | ||
/* Can tx_buf and rx_buf be equal? The doc in spi.h is not sure... */ | ||
xfer.tx_buf = chip->tx_buf; | ||
xfer.rx_buf = chip->rx_buf; | ||
|
||
/* Set MSB to indicate read */ | ||
chip->tx_buf[0] = address | 0x80; | ||
|
||
spi_message_add_tail(&xfer, &message); | ||
|
||
/* do the i/o */ | ||
status = spi_sync(spi, &message); | ||
if (status == 0) | ||
status = message.status; | ||
else | ||
return status; | ||
|
||
*data = chip->rx_buf[1]; | ||
|
||
return status; | ||
} | ||
|
||
static int max6902_get_datetime(struct device *dev, struct rtc_time *dt) | ||
{ | ||
unsigned char tmp; | ||
int century; | ||
int err; | ||
struct spi_device *spi = to_spi_device(dev); | ||
struct max6902 *chip = dev_get_drvdata(dev); | ||
struct spi_message message; | ||
struct spi_transfer xfer; | ||
int status; | ||
|
||
err = max6902_get_reg(dev, MAX6902_REG_CENTURY, &tmp); | ||
if (err) | ||
return err; | ||
|
||
/* build the message */ | ||
spi_message_init(&message); | ||
memset(&xfer, 0, sizeof(xfer)); | ||
xfer.len = 1 + 7; /* Burst read command + 7 registers */ | ||
xfer.tx_buf = chip->buf; | ||
xfer.rx_buf = chip->buf; | ||
chip->buf[0] = 0xbf; /* Burst read */ | ||
spi_message_add_tail(&xfer, &message); | ||
|
||
/* do the i/o */ | ||
status = spi_sync(spi, &message); | ||
if (status == 0) | ||
status = message.status; | ||
else | ||
return status; | ||
|
||
/* The chip sends data in this order: | ||
* Seconds, Minutes, Hours, Date, Month, Day, Year */ | ||
dt->tm_sec = BCD2BIN(chip->buf[1]); | ||
dt->tm_min = BCD2BIN(chip->buf[2]); | ||
dt->tm_hour = BCD2BIN(chip->buf[3]); | ||
dt->tm_mday = BCD2BIN(chip->buf[4]); | ||
dt->tm_mon = BCD2BIN(chip->buf[5] - 1); | ||
dt->tm_wday = BCD2BIN(chip->buf[6]); | ||
dt->tm_year = BCD2BIN(chip->buf[7]); | ||
|
||
century = BCD2BIN(tmp) * 100; | ||
|
||
dt->tm_year += century; | ||
dt->tm_year -= 1900; | ||
|
||
#ifdef MAX6902_DEBUG | ||
printk("\n%s : Read RTC values\n",__FUNCTION__); | ||
printk("tm_hour: %i\n",dt->tm_hour); | ||
printk("tm_min : %i\n",dt->tm_min); | ||
printk("tm_sec : %i\n",dt->tm_sec); | ||
printk("tm_year: %i\n",dt->tm_year); | ||
printk("tm_mon : %i\n",dt->tm_mon); | ||
printk("tm_mday: %i\n",dt->tm_mday); | ||
printk("tm_wday: %i\n",dt->tm_wday); | ||
#endif | ||
|
||
return 0; | ||
} | ||
|
||
static int max6902_set_datetime(struct device *dev, struct rtc_time *dt) | ||
{ | ||
dt->tm_year = dt->tm_year+1900; | ||
|
||
#ifdef MAX6902_DEBUG | ||
printk("\n%s : Setting RTC values\n",__FUNCTION__); | ||
printk("tm_sec : %i\n",dt->tm_sec); | ||
printk("tm_min : %i\n",dt->tm_min); | ||
printk("tm_hour: %i\n",dt->tm_hour); | ||
printk("tm_mday: %i\n",dt->tm_mday); | ||
printk("tm_wday: %i\n",dt->tm_wday); | ||
printk("tm_year: %i\n",dt->tm_year); | ||
#endif | ||
|
||
/* Remove write protection */ | ||
max6902_set_reg(dev, 0xF, 0); | ||
|
||
max6902_set_reg(dev, 0x01, BIN2BCD(dt->tm_sec)); | ||
max6902_set_reg(dev, 0x03, BIN2BCD(dt->tm_min)); | ||
max6902_set_reg(dev, 0x05, BIN2BCD(dt->tm_hour)); | ||
|
||
max6902_set_reg(dev, 0x07, BIN2BCD(dt->tm_mday)); | ||
max6902_set_reg(dev, 0x09, BIN2BCD(dt->tm_mon+1)); | ||
max6902_set_reg(dev, 0x0B, BIN2BCD(dt->tm_wday)); | ||
max6902_set_reg(dev, 0x0D, BIN2BCD(dt->tm_year%100)); | ||
max6902_set_reg(dev, 0x13, BIN2BCD(dt->tm_year/100)); | ||
|
||
/* Compulab used a delay here. However, the datasheet | ||
* does not mention a delay being required anywhere... */ | ||
/* delay(2000); */ | ||
|
||
/* Write protect */ | ||
max6902_set_reg(dev, 0xF, 0x80); | ||
|
||
return 0; | ||
} | ||
|
||
static int max6902_read_time(struct device *dev, struct rtc_time *tm) | ||
{ | ||
return max6902_get_datetime(dev, tm); | ||
} | ||
|
||
static int max6902_set_time(struct device *dev, struct rtc_time *tm) | ||
{ | ||
return max6902_set_datetime(dev, tm); | ||
} | ||
|
||
static struct rtc_class_ops max6902_rtc_ops = { | ||
.read_time = max6902_read_time, | ||
.set_time = max6902_set_time, | ||
}; | ||
|
||
static int __devinit max6902_probe(struct spi_device *spi) | ||
{ | ||
struct rtc_device *rtc; | ||
unsigned char tmp; | ||
struct max6902 *chip; | ||
int res; | ||
|
||
rtc = rtc_device_register("max6902", | ||
&spi->dev, &max6902_rtc_ops, THIS_MODULE); | ||
if (IS_ERR(rtc)) | ||
return PTR_ERR(rtc); | ||
|
||
spi->mode = SPI_MODE_3; | ||
spi->bits_per_word = 8; | ||
spi_setup(spi); | ||
|
||
chip = kzalloc(sizeof *chip, GFP_KERNEL); | ||
if (!chip) { | ||
rtc_device_unregister(rtc); | ||
return -ENOMEM; | ||
} | ||
chip->rtc = rtc; | ||
dev_set_drvdata(&spi->dev, chip); | ||
|
||
res = max6902_get_reg(&spi->dev, MAX6902_REG_SECONDS, &tmp); | ||
if (res) { | ||
rtc_device_unregister(rtc); | ||
return res; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static int __devexit max6902_remove(struct spi_device *spi) | ||
{ | ||
struct max6902 *chip = platform_get_drvdata(spi); | ||
struct rtc_device *rtc = chip->rtc; | ||
|
||
if (rtc) | ||
rtc_device_unregister(rtc); | ||
|
||
kfree(chip); | ||
|
||
return 0; | ||
} | ||
|
||
static struct spi_driver max6902_driver = { | ||
.driver = { | ||
.name = "max6902", | ||
.bus = &spi_bus_type, | ||
.owner = THIS_MODULE, | ||
}, | ||
.probe = max6902_probe, | ||
.remove = __devexit_p(max6902_remove), | ||
}; | ||
|
||
static __init int max6902_init(void) | ||
{ | ||
printk("max6902 spi driver\n"); | ||
return spi_register_driver(&max6902_driver); | ||
} | ||
module_init(max6902_init); | ||
|
||
static __exit void max6902_exit(void) | ||
{ | ||
spi_unregister_driver(&max6902_driver); | ||
} | ||
module_exit(max6902_exit); | ||
|
||
MODULE_DESCRIPTION ("max6902 spi RTC driver"); | ||
MODULE_AUTHOR ("Raphael Assenat"); | ||
MODULE_LICENSE ("GPL"); |