diff options
Diffstat (limited to 'drivers/rtc')
| -rw-r--r-- | drivers/rtc/Kconfig | 9 | ||||
| -rw-r--r-- | drivers/rtc/Makefile | 1 | ||||
| -rw-r--r-- | drivers/rtc/rtc-bq32k.c | 204 |
3 files changed, 214 insertions, 0 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 9930d79fa51..40c168bf698 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig | |||
| @@ -242,6 +242,15 @@ config RTC_DRV_M41T80_WDT | |||
| 242 | If you say Y here you will get support for the | 242 | If you say Y here you will get support for the |
| 243 | watchdog timer in the ST M41T60 and M41T80 RTC chips series. | 243 | watchdog timer in the ST M41T60 and M41T80 RTC chips series. |
| 244 | 244 | ||
| 245 | config RTC_DRV_BQ32K | ||
| 246 | tristate "TI BQ32000" | ||
| 247 | help | ||
| 248 | If you say Y here you will get support for the TI | ||
| 249 | BQ32000 I2C RTC chip. | ||
| 250 | |||
| 251 | This driver can also be built as a module. If so, the module | ||
| 252 | will be called rtc-bq32k. | ||
| 253 | |||
| 245 | config RTC_DRV_DM355EVM | 254 | config RTC_DRV_DM355EVM |
| 246 | tristate "TI DaVinci DM355 EVM RTC" | 255 | tristate "TI DaVinci DM355 EVM RTC" |
| 247 | depends on MFD_DM355EVM_MSP | 256 | depends on MFD_DM355EVM_MSP |
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 6fa20f4ac3f..52f5f3c3b8e 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile | |||
| @@ -23,6 +23,7 @@ obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o | |||
| 23 | obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o | 23 | obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o |
| 24 | obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o | 24 | obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o |
| 25 | obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o | 25 | obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o |
| 26 | obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o | ||
| 26 | obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o | 27 | obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o |
| 27 | obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o | 28 | obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o |
| 28 | obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o | 29 | obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o |
diff --git a/drivers/rtc/rtc-bq32k.c b/drivers/rtc/rtc-bq32k.c new file mode 100644 index 00000000000..408cc8f735b --- /dev/null +++ b/drivers/rtc/rtc-bq32k.c | |||
| @@ -0,0 +1,204 @@ | |||
| 1 | /* | ||
| 2 | * Driver for TI BQ32000 RTC. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Semihalf. | ||
| 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 | |||
| 11 | #include <linux/module.h> | ||
| 12 | #include <linux/i2c.h> | ||
| 13 | #include <linux/rtc.h> | ||
| 14 | #include <linux/init.h> | ||
| 15 | #include <linux/errno.h> | ||
| 16 | #include <linux/bcd.h> | ||
| 17 | |||
| 18 | #define BQ32K_SECONDS 0x00 /* Seconds register address */ | ||
| 19 | #define BQ32K_SECONDS_MASK 0x7F /* Mask over seconds value */ | ||
| 20 | #define BQ32K_STOP 0x80 /* Oscillator Stop flat */ | ||
| 21 | |||
| 22 | #define BQ32K_MINUTES 0x01 /* Minutes register address */ | ||
| 23 | #define BQ32K_MINUTES_MASK 0x7F /* Mask over minutes value */ | ||
| 24 | #define BQ32K_OF 0x80 /* Oscillator Failure flag */ | ||
| 25 | |||
| 26 | #define BQ32K_HOURS_MASK 0x3F /* Mask over hours value */ | ||
| 27 | #define BQ32K_CENT 0x40 /* Century flag */ | ||
| 28 | #define BQ32K_CENT_EN 0x80 /* Century flag enable bit */ | ||
| 29 | |||
| 30 | struct bq32k_regs { | ||
| 31 | uint8_t seconds; | ||
| 32 | uint8_t minutes; | ||
| 33 | uint8_t cent_hours; | ||
| 34 | uint8_t day; | ||
| 35 | uint8_t date; | ||
| 36 | uint8_t month; | ||
| 37 | uint8_t years; | ||
| 38 | }; | ||
| 39 | |||
| 40 | static struct i2c_driver bq32k_driver; | ||
| 41 | |||
| 42 | static int bq32k_read(struct device *dev, void *data, uint8_t off, uint8_t len) | ||
| 43 | { | ||
| 44 | struct i2c_client *client = to_i2c_client(dev); | ||
| 45 | struct i2c_msg msgs[] = { | ||
| 46 | { | ||
| 47 | .addr = client->addr, | ||
| 48 | .flags = 0, | ||
| 49 | .len = 1, | ||
| 50 | .buf = &off, | ||
| 51 | }, { | ||
| 52 | .addr = client->addr, | ||
| 53 | .flags = I2C_M_RD, | ||
| 54 | .len = len, | ||
| 55 | .buf = data, | ||
| 56 | } | ||
| 57 | }; | ||
| 58 | |||
| 59 | if (i2c_transfer(client->adapter, msgs, 2) == 2) | ||
| 60 | return 0; | ||
| 61 | |||
| 62 | return -EIO; | ||
| 63 | } | ||
| 64 | |||
| 65 | static int bq32k_write(struct device *dev, void *data, uint8_t off, uint8_t len) | ||
| 66 | { | ||
| 67 | struct i2c_client *client = to_i2c_client(dev); | ||
| 68 | uint8_t buffer[len + 1]; | ||
| 69 | |||
| 70 | buffer[0] = off; | ||
| 71 | memcpy(&buffer[1], data, len); | ||
| 72 | |||
| 73 | if (i2c_master_send(client, buffer, len + 1) == len + 1) | ||
| 74 | return 0; | ||
| 75 | |||
| 76 | return -EIO; | ||
| 77 | } | ||
| 78 | |||
| 79 | static int bq32k_rtc_read_time(struct device *dev, struct rtc_time *tm) | ||
| 80 | { | ||
| 81 | struct bq32k_regs regs; | ||
| 82 | int error; | ||
| 83 | |||
| 84 | error = bq32k_read(dev, ®s, 0, sizeof(regs)); | ||
| 85 | if (error) | ||
| 86 | return error; | ||
| 87 | |||
| 88 | tm->tm_sec = bcd2bin(regs.seconds & BQ32K_SECONDS_MASK); | ||
| 89 | tm->tm_min = bcd2bin(regs.minutes & BQ32K_SECONDS_MASK); | ||
| 90 | tm->tm_hour = bcd2bin(regs.cent_hours & BQ32K_HOURS_MASK); | ||
| 91 | tm->tm_mday = bcd2bin(regs.date); | ||
| 92 | tm->tm_wday = bcd2bin(regs.day) - 1; | ||
| 93 | tm->tm_mon = bcd2bin(regs.month) - 1; | ||
| 94 | tm->tm_year = bcd2bin(regs.years) + | ||
| 95 | ((regs.cent_hours & BQ32K_CENT) ? 100 : 0); | ||
| 96 | |||
| 97 | return rtc_valid_tm(tm); | ||
| 98 | } | ||
| 99 | |||
| 100 | static int bq32k_rtc_set_time(struct device *dev, struct rtc_time *tm) | ||
| 101 | { | ||
| 102 | struct bq32k_regs regs; | ||
| 103 | |||
| 104 | regs.seconds = bin2bcd(tm->tm_sec); | ||
| 105 | regs.minutes = bin2bcd(tm->tm_min); | ||
| 106 | regs.cent_hours = bin2bcd(tm->tm_hour) | BQ32K_CENT_EN; | ||
| 107 | regs.day = bin2bcd(tm->tm_wday + 1); | ||
| 108 | regs.date = bin2bcd(tm->tm_mday); | ||
| 109 | regs.month = bin2bcd(tm->tm_mon + 1); | ||
| 110 | |||
| 111 | if (tm->tm_year >= 100) { | ||
| 112 | regs.cent_hours |= BQ32K_CENT; | ||
| 113 | regs.years = bin2bcd(tm->tm_year - 100); | ||
| 114 | } else | ||
| 115 | regs.years = bin2bcd(tm->tm_year); | ||
| 116 | |||
| 117 | return bq32k_write(dev, ®s, 0, sizeof(regs)); | ||
| 118 | } | ||
| 119 | |||
| 120 | static const struct rtc_class_ops bq32k_rtc_ops = { | ||
| 121 | .read_time = bq32k_rtc_read_time, | ||
| 122 | .set_time = bq32k_rtc_set_time, | ||
| 123 | }; | ||
| 124 | |||
| 125 | static int bq32k_probe(struct i2c_client *client, | ||
| 126 | const struct i2c_device_id *id) | ||
| 127 | { | ||
| 128 | struct device *dev = &client->dev; | ||
| 129 | struct rtc_device *rtc; | ||
| 130 | uint8_t reg; | ||
| 131 | int error; | ||
| 132 | |||
| 133 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) | ||
| 134 | return -ENODEV; | ||
| 135 | |||
| 136 | /* Check Oscillator Stop flag */ | ||
| 137 | error = bq32k_read(dev, ®, BQ32K_SECONDS, 1); | ||
| 138 | if (!error && (reg & BQ32K_STOP)) { | ||
| 139 | dev_warn(dev, "Oscillator was halted. Restarting...\n"); | ||
| 140 | reg &= ~BQ32K_STOP; | ||
| 141 | error = bq32k_write(dev, ®, BQ32K_SECONDS, 1); | ||
| 142 | } | ||
| 143 | if (error) | ||
| 144 | return error; | ||
| 145 | |||
| 146 | /* Check Oscillator Failure flag */ | ||
| 147 | error = bq32k_read(dev, ®, BQ32K_MINUTES, 1); | ||
| 148 | if (!error && (reg & BQ32K_OF)) { | ||
| 149 | dev_warn(dev, "Oscillator Failure. Check RTC battery.\n"); | ||
| 150 | reg &= ~BQ32K_OF; | ||
| 151 | error = bq32k_write(dev, ®, BQ32K_MINUTES, 1); | ||
| 152 | } | ||
| 153 | if (error) | ||
| 154 | return error; | ||
| 155 | |||
| 156 | rtc = rtc_device_register(bq32k_driver.driver.name, &client->dev, | ||
| 157 | &bq32k_rtc_ops, THIS_MODULE); | ||
| 158 | if (IS_ERR(rtc)) | ||
| 159 | return PTR_ERR(rtc); | ||
| 160 | |||
| 161 | i2c_set_clientdata(client, rtc); | ||
| 162 | |||
| 163 | return 0; | ||
| 164 | } | ||
| 165 | |||
| 166 | static int __devexit bq32k_remove(struct i2c_client *client) | ||
| 167 | { | ||
| 168 | struct rtc_device *rtc = i2c_get_clientdata(client); | ||
| 169 | |||
| 170 | rtc_device_unregister(rtc); | ||
| 171 | return 0; | ||
| 172 | } | ||
| 173 | |||
| 174 | static const struct i2c_device_id bq32k_id[] = { | ||
| 175 | { "bq32000", 0 }, | ||
| 176 | { } | ||
| 177 | }; | ||
| 178 | MODULE_DEVICE_TABLE(i2c, bq32k_id); | ||
| 179 | |||
| 180 | static struct i2c_driver bq32k_driver = { | ||
| 181 | .driver = { | ||
| 182 | .name = "bq32k", | ||
| 183 | .owner = THIS_MODULE, | ||
| 184 | }, | ||
| 185 | .probe = bq32k_probe, | ||
| 186 | .remove = __devexit_p(bq32k_remove), | ||
| 187 | .id_table = bq32k_id, | ||
| 188 | }; | ||
| 189 | |||
| 190 | static __init int bq32k_init(void) | ||
| 191 | { | ||
| 192 | return i2c_add_driver(&bq32k_driver); | ||
| 193 | } | ||
| 194 | module_init(bq32k_init); | ||
| 195 | |||
| 196 | static __exit void bq32k_exit(void) | ||
| 197 | { | ||
| 198 | i2c_del_driver(&bq32k_driver); | ||
| 199 | } | ||
| 200 | module_exit(bq32k_exit); | ||
| 201 | |||
| 202 | MODULE_AUTHOR("Semihalf, Piotr Ziecik <kosmo@semihalf.com>"); | ||
| 203 | MODULE_DESCRIPTION("TI BQ32000 I2C RTC driver"); | ||
| 204 | MODULE_LICENSE("GPL"); | ||
