diff options
Diffstat (limited to 'drivers/rtc')
| -rw-r--r-- | drivers/rtc/Kconfig | 11 | ||||
| -rw-r--r-- | drivers/rtc/Makefile | 1 | ||||
| -rw-r--r-- | drivers/rtc/rtc-at91sam9.c | 6 | ||||
| -rw-r--r-- | drivers/rtc/rtc-s35390a.c | 316 |
4 files changed, 332 insertions, 2 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 6402d699072b..9e7de63b26ef 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig | |||
| @@ -16,7 +16,7 @@ menuconfig RTC_CLASS | |||
| 16 | probably want to enable one or more of the interfaces below. | 16 | probably want to enable one or more of the interfaces below. |
| 17 | 17 | ||
| 18 | This driver can also be built as a module. If so, the module | 18 | This driver can also be built as a module. If so, the module |
| 19 | will be called rtc-class. | 19 | will be called rtc-core. |
| 20 | 20 | ||
| 21 | if RTC_CLASS | 21 | if RTC_CLASS |
| 22 | 22 | ||
| @@ -250,6 +250,15 @@ config RTC_DRV_TWL92330 | |||
| 250 | platforms. The support is integrated with the rest of | 250 | platforms. The support is integrated with the rest of |
| 251 | the Menelaus driver; it's not separate module. | 251 | the Menelaus driver; it's not separate module. |
| 252 | 252 | ||
| 253 | config RTC_DRV_S35390A | ||
| 254 | tristate "Seiko Instruments S-35390A" | ||
| 255 | help | ||
| 256 | If you say yes here you will get support for the Seiko | ||
| 257 | Instruments S-35390A. | ||
| 258 | |||
| 259 | This driver can also be built as a module. If so the module | ||
| 260 | will be called rtc-s35390a. | ||
| 261 | |||
| 253 | endif # I2C | 262 | endif # I2C |
| 254 | 263 | ||
| 255 | comment "SPI RTC drivers" | 264 | comment "SPI RTC drivers" |
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index ec703f34ab86..872f1218ff9f 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile | |||
| @@ -45,6 +45,7 @@ obj-$(CONFIG_RTC_DRV_R9701) += rtc-r9701.o | |||
| 45 | obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o | 45 | obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o |
| 46 | obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o | 46 | obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o |
| 47 | obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o | 47 | obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o |
| 48 | obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o | ||
| 48 | obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o | 49 | obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o |
| 49 | obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o | 50 | obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o |
| 50 | obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o | 51 | obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o |
diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c index bbf10ecf416c..56728a2a3385 100644 --- a/drivers/rtc/rtc-at91sam9.c +++ b/drivers/rtc/rtc-at91sam9.c | |||
| @@ -274,7 +274,7 @@ static irqreturn_t at91_rtc_interrupt(int irq, void *_rtc) | |||
| 274 | * SR clears it, so we must only read it in this irq handler! | 274 | * SR clears it, so we must only read it in this irq handler! |
| 275 | */ | 275 | */ |
| 276 | mr = rtt_readl(rtc, MR) & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN); | 276 | mr = rtt_readl(rtc, MR) & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN); |
| 277 | sr = rtt_readl(rtc, SR) & mr; | 277 | sr = rtt_readl(rtc, SR) & (mr >> 16); |
| 278 | if (!sr) | 278 | if (!sr) |
| 279 | return IRQ_NONE; | 279 | return IRQ_NONE; |
| 280 | 280 | ||
| @@ -321,6 +321,10 @@ static int __init at91_rtc_probe(struct platform_device *pdev) | |||
| 321 | if (!rtc) | 321 | if (!rtc) |
| 322 | return -ENOMEM; | 322 | return -ENOMEM; |
| 323 | 323 | ||
| 324 | /* platform setup code should have handled this; sigh */ | ||
| 325 | if (!device_can_wakeup(&pdev->dev)) | ||
| 326 | device_init_wakeup(&pdev->dev, 1); | ||
| 327 | |||
| 324 | platform_set_drvdata(pdev, rtc); | 328 | platform_set_drvdata(pdev, rtc); |
| 325 | rtc->rtt = (void __force __iomem *) (AT91_VA_BASE_SYS - AT91_BASE_SYS); | 329 | rtc->rtt = (void __force __iomem *) (AT91_VA_BASE_SYS - AT91_BASE_SYS); |
| 326 | rtc->rtt += r->start; | 330 | rtc->rtt += r->start; |
diff --git a/drivers/rtc/rtc-s35390a.c b/drivers/rtc/rtc-s35390a.c new file mode 100644 index 000000000000..e8abc90c32c5 --- /dev/null +++ b/drivers/rtc/rtc-s35390a.c | |||
| @@ -0,0 +1,316 @@ | |||
| 1 | /* | ||
| 2 | * Seiko Instruments S-35390A RTC Driver | ||
| 3 | * | ||
| 4 | * Copyright (c) 2007 Byron Bradley | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public License | ||
| 8 | * as published by the Free Software Foundation; either version | ||
| 9 | * 2 of the License, or (at your option) any later version. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/rtc.h> | ||
| 14 | #include <linux/i2c.h> | ||
| 15 | #include <linux/bitrev.h> | ||
| 16 | #include <linux/bcd.h> | ||
| 17 | #include <linux/slab.h> | ||
| 18 | |||
| 19 | #define S35390A_CMD_STATUS1 0 | ||
| 20 | #define S35390A_CMD_STATUS2 1 | ||
| 21 | #define S35390A_CMD_TIME1 2 | ||
| 22 | |||
| 23 | #define S35390A_BYTE_YEAR 0 | ||
| 24 | #define S35390A_BYTE_MONTH 1 | ||
| 25 | #define S35390A_BYTE_DAY 2 | ||
| 26 | #define S35390A_BYTE_WDAY 3 | ||
| 27 | #define S35390A_BYTE_HOURS 4 | ||
| 28 | #define S35390A_BYTE_MINS 5 | ||
| 29 | #define S35390A_BYTE_SECS 6 | ||
| 30 | |||
| 31 | #define S35390A_FLAG_POC 0x01 | ||
| 32 | #define S35390A_FLAG_BLD 0x02 | ||
| 33 | #define S35390A_FLAG_24H 0x40 | ||
| 34 | #define S35390A_FLAG_RESET 0x80 | ||
| 35 | #define S35390A_FLAG_TEST 0x01 | ||
| 36 | |||
| 37 | struct s35390a { | ||
| 38 | struct i2c_client *client[8]; | ||
| 39 | struct rtc_device *rtc; | ||
| 40 | int twentyfourhour; | ||
| 41 | }; | ||
| 42 | |||
| 43 | static int s35390a_set_reg(struct s35390a *s35390a, int reg, char *buf, int len) | ||
| 44 | { | ||
| 45 | struct i2c_client *client = s35390a->client[reg]; | ||
| 46 | struct i2c_msg msg[] = { | ||
| 47 | { client->addr, 0, len, buf }, | ||
| 48 | }; | ||
| 49 | |||
| 50 | if ((i2c_transfer(client->adapter, msg, 1)) != 1) | ||
| 51 | return -EIO; | ||
| 52 | |||
| 53 | return 0; | ||
| 54 | } | ||
| 55 | |||
| 56 | static int s35390a_get_reg(struct s35390a *s35390a, int reg, char *buf, int len) | ||
| 57 | { | ||
| 58 | struct i2c_client *client = s35390a->client[reg]; | ||
| 59 | struct i2c_msg msg[] = { | ||
| 60 | { client->addr, I2C_M_RD, len, buf }, | ||
| 61 | }; | ||
| 62 | |||
| 63 | if ((i2c_transfer(client->adapter, msg, 1)) != 1) | ||
| 64 | return -EIO; | ||
| 65 | |||
| 66 | return 0; | ||
| 67 | } | ||
| 68 | |||
| 69 | static int s35390a_reset(struct s35390a *s35390a) | ||
| 70 | { | ||
| 71 | char buf[1]; | ||
| 72 | |||
| 73 | if (s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, buf, sizeof(buf)) < 0) | ||
| 74 | return -EIO; | ||
| 75 | |||
| 76 | if (!(buf[0] & (S35390A_FLAG_POC | S35390A_FLAG_BLD))) | ||
| 77 | return 0; | ||
| 78 | |||
| 79 | buf[0] |= (S35390A_FLAG_RESET | S35390A_FLAG_24H); | ||
| 80 | buf[0] &= 0xf0; | ||
| 81 | return s35390a_set_reg(s35390a, S35390A_CMD_STATUS1, buf, sizeof(buf)); | ||
| 82 | } | ||
| 83 | |||
| 84 | static int s35390a_disable_test_mode(struct s35390a *s35390a) | ||
| 85 | { | ||
| 86 | char buf[1]; | ||
| 87 | |||
| 88 | if (s35390a_get_reg(s35390a, S35390A_CMD_STATUS2, buf, sizeof(buf)) < 0) | ||
| 89 | return -EIO; | ||
| 90 | |||
| 91 | if (!(buf[0] & S35390A_FLAG_TEST)) | ||
| 92 | return 0; | ||
| 93 | |||
| 94 | buf[0] &= ~S35390A_FLAG_TEST; | ||
| 95 | return s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, buf, sizeof(buf)); | ||
| 96 | } | ||
| 97 | |||
| 98 | static char s35390a_hr2reg(struct s35390a *s35390a, int hour) | ||
| 99 | { | ||
| 100 | if (s35390a->twentyfourhour) | ||
| 101 | return BIN2BCD(hour); | ||
| 102 | |||
| 103 | if (hour < 12) | ||
| 104 | return BIN2BCD(hour); | ||
| 105 | |||
| 106 | return 0x40 | BIN2BCD(hour - 12); | ||
| 107 | } | ||
| 108 | |||
| 109 | static int s35390a_reg2hr(struct s35390a *s35390a, char reg) | ||
| 110 | { | ||
| 111 | unsigned hour; | ||
| 112 | |||
| 113 | if (s35390a->twentyfourhour) | ||
| 114 | return BCD2BIN(reg & 0x3f); | ||
| 115 | |||
| 116 | hour = BCD2BIN(reg & 0x3f); | ||
| 117 | if (reg & 0x40) | ||
| 118 | hour += 12; | ||
| 119 | |||
| 120 | return hour; | ||
| 121 | } | ||
| 122 | |||
| 123 | static int s35390a_set_datetime(struct i2c_client *client, struct rtc_time *tm) | ||
| 124 | { | ||
| 125 | struct s35390a *s35390a = i2c_get_clientdata(client); | ||
| 126 | int i, err; | ||
| 127 | char buf[7]; | ||
| 128 | |||
| 129 | dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d mday=%d, " | ||
| 130 | "mon=%d, year=%d, wday=%d\n", __func__, tm->tm_sec, | ||
| 131 | tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year, | ||
| 132 | tm->tm_wday); | ||
| 133 | |||
| 134 | buf[S35390A_BYTE_YEAR] = BIN2BCD(tm->tm_year - 100); | ||
| 135 | buf[S35390A_BYTE_MONTH] = BIN2BCD(tm->tm_mon + 1); | ||
| 136 | buf[S35390A_BYTE_DAY] = BIN2BCD(tm->tm_mday); | ||
| 137 | buf[S35390A_BYTE_WDAY] = BIN2BCD(tm->tm_wday); | ||
| 138 | buf[S35390A_BYTE_HOURS] = s35390a_hr2reg(s35390a, tm->tm_hour); | ||
| 139 | buf[S35390A_BYTE_MINS] = BIN2BCD(tm->tm_min); | ||
| 140 | buf[S35390A_BYTE_SECS] = BIN2BCD(tm->tm_sec); | ||
| 141 | |||
| 142 | /* This chip expects the bits of each byte to be in reverse order */ | ||
| 143 | for (i = 0; i < 7; ++i) | ||
| 144 | buf[i] = bitrev8(buf[i]); | ||
| 145 | |||
| 146 | err = s35390a_set_reg(s35390a, S35390A_CMD_TIME1, buf, sizeof(buf)); | ||
| 147 | |||
| 148 | return err; | ||
| 149 | } | ||
| 150 | |||
| 151 | static int s35390a_get_datetime(struct i2c_client *client, struct rtc_time *tm) | ||
| 152 | { | ||
| 153 | struct s35390a *s35390a = i2c_get_clientdata(client); | ||
| 154 | char buf[7]; | ||
| 155 | int i, err; | ||
| 156 | |||
| 157 | err = s35390a_get_reg(s35390a, S35390A_CMD_TIME1, buf, sizeof(buf)); | ||
| 158 | if (err < 0) | ||
| 159 | return err; | ||
| 160 | |||
| 161 | /* This chip returns the bits of each byte in reverse order */ | ||
| 162 | for (i = 0; i < 7; ++i) | ||
| 163 | buf[i] = bitrev8(buf[i]); | ||
| 164 | |||
| 165 | tm->tm_sec = BCD2BIN(buf[S35390A_BYTE_SECS]); | ||
| 166 | tm->tm_min = BCD2BIN(buf[S35390A_BYTE_MINS]); | ||
| 167 | tm->tm_hour = s35390a_reg2hr(s35390a, buf[S35390A_BYTE_HOURS]); | ||
| 168 | tm->tm_wday = BCD2BIN(buf[S35390A_BYTE_WDAY]); | ||
| 169 | tm->tm_mday = BCD2BIN(buf[S35390A_BYTE_DAY]); | ||
| 170 | tm->tm_mon = BCD2BIN(buf[S35390A_BYTE_MONTH]) - 1; | ||
| 171 | tm->tm_year = BCD2BIN(buf[S35390A_BYTE_YEAR]) + 100; | ||
| 172 | |||
| 173 | dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, mday=%d, " | ||
| 174 | "mon=%d, year=%d, wday=%d\n", __func__, tm->tm_sec, | ||
| 175 | tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year, | ||
| 176 | tm->tm_wday); | ||
| 177 | |||
| 178 | return rtc_valid_tm(tm); | ||
| 179 | } | ||
| 180 | |||
| 181 | static int s35390a_rtc_read_time(struct device *dev, struct rtc_time *tm) | ||
| 182 | { | ||
| 183 | return s35390a_get_datetime(to_i2c_client(dev), tm); | ||
| 184 | } | ||
| 185 | |||
| 186 | static int s35390a_rtc_set_time(struct device *dev, struct rtc_time *tm) | ||
| 187 | { | ||
| 188 | return s35390a_set_datetime(to_i2c_client(dev), tm); | ||
| 189 | } | ||
| 190 | |||
| 191 | static const struct rtc_class_ops s35390a_rtc_ops = { | ||
| 192 | .read_time = s35390a_rtc_read_time, | ||
| 193 | .set_time = s35390a_rtc_set_time, | ||
| 194 | }; | ||
| 195 | |||
| 196 | static struct i2c_driver s35390a_driver; | ||
| 197 | |||
| 198 | static int s35390a_probe(struct i2c_client *client) | ||
| 199 | { | ||
| 200 | int err; | ||
| 201 | unsigned int i; | ||
| 202 | struct s35390a *s35390a; | ||
| 203 | struct rtc_time tm; | ||
| 204 | char buf[1]; | ||
| 205 | |||
| 206 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
| 207 | err = -ENODEV; | ||
| 208 | goto exit; | ||
| 209 | } | ||
| 210 | |||
| 211 | s35390a = kzalloc(sizeof(struct s35390a), GFP_KERNEL); | ||
| 212 | if (!s35390a) { | ||
| 213 | err = -ENOMEM; | ||
| 214 | goto exit; | ||
| 215 | } | ||
| 216 | |||
| 217 | s35390a->client[0] = client; | ||
| 218 | i2c_set_clientdata(client, s35390a); | ||
| 219 | |||
| 220 | /* This chip uses multiple addresses, use dummy devices for them */ | ||
| 221 | for (i = 1; i < 8; ++i) { | ||
| 222 | s35390a->client[i] = i2c_new_dummy(client->adapter, | ||
| 223 | client->addr + i, "rtc-s35390a"); | ||
| 224 | if (!s35390a->client[i]) { | ||
| 225 | dev_err(&client->dev, "Address %02x unavailable\n", | ||
| 226 | client->addr + i); | ||
| 227 | err = -EBUSY; | ||
| 228 | goto exit_dummy; | ||
| 229 | } | ||
| 230 | } | ||
| 231 | |||
| 232 | err = s35390a_reset(s35390a); | ||
| 233 | if (err < 0) { | ||
| 234 | dev_err(&client->dev, "error resetting chip\n"); | ||
| 235 | goto exit_dummy; | ||
| 236 | } | ||
| 237 | |||
| 238 | err = s35390a_disable_test_mode(s35390a); | ||
| 239 | if (err < 0) { | ||
| 240 | dev_err(&client->dev, "error disabling test mode\n"); | ||
| 241 | goto exit_dummy; | ||
| 242 | } | ||
| 243 | |||
| 244 | err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, buf, sizeof(buf)); | ||
| 245 | if (err < 0) { | ||
| 246 | dev_err(&client->dev, "error checking 12/24 hour mode\n"); | ||
| 247 | goto exit_dummy; | ||
| 248 | } | ||
| 249 | if (buf[0] & S35390A_FLAG_24H) | ||
| 250 | s35390a->twentyfourhour = 1; | ||
| 251 | else | ||
| 252 | s35390a->twentyfourhour = 0; | ||
| 253 | |||
| 254 | if (s35390a_get_datetime(client, &tm) < 0) | ||
| 255 | dev_warn(&client->dev, "clock needs to be set\n"); | ||
| 256 | |||
| 257 | s35390a->rtc = rtc_device_register(s35390a_driver.driver.name, | ||
| 258 | &client->dev, &s35390a_rtc_ops, THIS_MODULE); | ||
| 259 | |||
| 260 | if (IS_ERR(s35390a->rtc)) { | ||
| 261 | err = PTR_ERR(s35390a->rtc); | ||
| 262 | goto exit_dummy; | ||
| 263 | } | ||
| 264 | return 0; | ||
| 265 | |||
| 266 | exit_dummy: | ||
| 267 | for (i = 1; i < 8; ++i) | ||
| 268 | if (s35390a->client[i]) | ||
| 269 | i2c_unregister_device(s35390a->client[i]); | ||
| 270 | kfree(s35390a); | ||
| 271 | i2c_set_clientdata(client, NULL); | ||
| 272 | |||
| 273 | exit: | ||
| 274 | return err; | ||
| 275 | } | ||
| 276 | |||
| 277 | static int s35390a_remove(struct i2c_client *client) | ||
| 278 | { | ||
| 279 | unsigned int i; | ||
| 280 | |||
| 281 | struct s35390a *s35390a = i2c_get_clientdata(client); | ||
| 282 | for (i = 1; i < 8; ++i) | ||
| 283 | if (s35390a->client[i]) | ||
| 284 | i2c_unregister_device(s35390a->client[i]); | ||
| 285 | |||
| 286 | rtc_device_unregister(s35390a->rtc); | ||
| 287 | kfree(s35390a); | ||
| 288 | i2c_set_clientdata(client, NULL); | ||
| 289 | |||
| 290 | return 0; | ||
| 291 | } | ||
| 292 | |||
| 293 | static struct i2c_driver s35390a_driver = { | ||
| 294 | .driver = { | ||
| 295 | .name = "rtc-s35390a", | ||
| 296 | }, | ||
| 297 | .probe = s35390a_probe, | ||
| 298 | .remove = s35390a_remove, | ||
| 299 | }; | ||
| 300 | |||
| 301 | static int __init s35390a_rtc_init(void) | ||
| 302 | { | ||
| 303 | return i2c_add_driver(&s35390a_driver); | ||
| 304 | } | ||
| 305 | |||
| 306 | static void __exit s35390a_rtc_exit(void) | ||
| 307 | { | ||
| 308 | i2c_del_driver(&s35390a_driver); | ||
| 309 | } | ||
| 310 | |||
| 311 | MODULE_AUTHOR("Byron Bradley <byron.bbradley@gmail.com>"); | ||
| 312 | MODULE_DESCRIPTION("S35390A RTC driver"); | ||
| 313 | MODULE_LICENSE("GPL"); | ||
| 314 | |||
| 315 | module_init(s35390a_rtc_init); | ||
| 316 | module_exit(s35390a_rtc_exit); | ||
