Blame view
drivers/rtc/rtc-rs5c372.c
18.3 KB
d2912cb15
|
1 |
// SPDX-License-Identifier: GPL-2.0-only |
7520b94de
|
2 |
/* |
37fc5e2c4
|
3 |
* An I2C driver for Ricoh RS5C372, R2025S/D and RV5C38[67] RTCs |
7520b94de
|
4 5 6 |
* * Copyright (C) 2005 Pavel Mironchik <pmironchik@optifacio.net> * Copyright (C) 2006 Tower Technologies |
0053dc0d1
|
7 |
* Copyright (C) 2008 Paul Mundt |
7520b94de
|
8 9 10 11 12 |
*/ #include <linux/i2c.h> #include <linux/rtc.h> #include <linux/bcd.h> |
5a0e3ad6a
|
13 |
#include <linux/slab.h> |
2113852b2
|
14 |
#include <linux/module.h> |
ff764b88e
|
15 |
#include <linux/of_device.h> |
7520b94de
|
16 |
|
cb26b572d
|
17 18 19 20 21 22 |
/* * Ricoh has a family of I2C based RTCs, which differ only slightly from * each other. Differences center on pinout (e.g. how many interrupts, * output clock, etc) and how the control registers are used. The '372 * is significant only because that's the one this driver first supported. */ |
7520b94de
|
23 24 25 26 27 28 29 30 |
#define RS5C372_REG_SECS 0 #define RS5C372_REG_MINS 1 #define RS5C372_REG_HOURS 2 #define RS5C372_REG_WDAY 3 #define RS5C372_REG_DAY 4 #define RS5C372_REG_MONTH 5 #define RS5C372_REG_YEAR 6 #define RS5C372_REG_TRIM 7 |
cb26b572d
|
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# define RS5C372_TRIM_XSL 0x80 # define RS5C372_TRIM_MASK 0x7F #define RS5C_REG_ALARM_A_MIN 8 /* or ALARM_W */ #define RS5C_REG_ALARM_A_HOURS 9 #define RS5C_REG_ALARM_A_WDAY 10 #define RS5C_REG_ALARM_B_MIN 11 /* or ALARM_D */ #define RS5C_REG_ALARM_B_HOURS 12 #define RS5C_REG_ALARM_B_WDAY 13 /* (ALARM_B only) */ #define RS5C_REG_CTRL1 14 # define RS5C_CTRL1_AALE (1 << 7) /* or WALE */ # define RS5C_CTRL1_BALE (1 << 6) /* or DALE */ # define RV5C387_CTRL1_24 (1 << 5) # define RS5C372A_CTRL1_SL1 (1 << 5) # define RS5C_CTRL1_CT_MASK (7 << 0) # define RS5C_CTRL1_CT0 (0 << 0) /* no periodic irq */ # define RS5C_CTRL1_CT4 (4 << 0) /* 1 Hz level irq */ #define RS5C_REG_CTRL2 15 # define RS5C372_CTRL2_24 (1 << 5) |
761acdda5
|
52 |
# define RS5C_CTRL2_XSTP (1 << 4) /* only if !R2x2x */ |
09832dc05
|
53 54 55 |
# define R2x2x_CTRL2_VDET (1 << 6) /* only if R2x2x */ # define R2x2x_CTRL2_XSTP (1 << 5) /* only if R2x2x */ # define R2x2x_CTRL2_PON (1 << 4) /* only if R2x2x */ |
cb26b572d
|
56 57 58 59 60 61 62 63 64 65 66 |
# define RS5C_CTRL2_CTFG (1 << 2) # define RS5C_CTRL2_AAFG (1 << 1) /* or WAFG */ # define RS5C_CTRL2_BAFG (1 << 0) /* or DAFG */ /* to read (style 1) or write registers starting at R */ #define RS5C_ADDR(R) (((R) << 4) | 0) enum rtc_type { rtc_undef = 0, |
37fc5e2c4
|
67 |
rtc_r2025sd, |
550fcb8f7
|
68 |
rtc_r2221tl, |
cb26b572d
|
69 70 71 72 73 |
rtc_rs5c372a, rtc_rs5c372b, rtc_rv5c386, rtc_rv5c387a, }; |
7520b94de
|
74 |
|
3760f7367
|
75 |
static const struct i2c_device_id rs5c372_id[] = { |
37fc5e2c4
|
76 |
{ "r2025sd", rtc_r2025sd }, |
550fcb8f7
|
77 |
{ "r2221tl", rtc_r2221tl }, |
3760f7367
|
78 79 80 81 82 83 84 |
{ "rs5c372a", rtc_rs5c372a }, { "rs5c372b", rtc_rs5c372b }, { "rv5c386", rtc_rv5c386 }, { "rv5c387a", rtc_rv5c387a }, { } }; MODULE_DEVICE_TABLE(i2c, rs5c372_id); |
ff764b88e
|
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
static const struct of_device_id rs5c372_of_match[] = { { .compatible = "ricoh,r2025sd", .data = (void *)rtc_r2025sd }, { .compatible = "ricoh,r2221tl", .data = (void *)rtc_r2221tl }, { .compatible = "ricoh,rs5c372a", .data = (void *)rtc_rs5c372a }, { .compatible = "ricoh,rs5c372b", .data = (void *)rtc_rs5c372b }, { .compatible = "ricoh,rv5c386", .data = (void *)rtc_rv5c386 }, { .compatible = "ricoh,rv5c387a", .data = (void *)rtc_rv5c387a }, { } }; MODULE_DEVICE_TABLE(of, rs5c372_of_match); |
cb26b572d
|
113 114 115 116 117 118 119 120 121 122 123 |
/* REVISIT: this assumes that: * - we're in the 21st century, so it's safe to ignore the century * bit for rv5c38[67] (REG_MONTH bit 7); * - we should use ALARM_A not ALARM_B (may be wrong on some boards) */ struct rs5c372 { struct i2c_client *client; struct rtc_device *rtc; enum rtc_type type; unsigned time24:1; unsigned has_irq:1; |
0053dc0d1
|
124 |
unsigned smbus:1; |
cb26b572d
|
125 126 |
char buf[17]; char *regs; |
cb26b572d
|
127 |
}; |
7520b94de
|
128 |
|
cb26b572d
|
129 130 131 132 |
static int rs5c_get_regs(struct rs5c372 *rs5c) { struct i2c_client *client = rs5c->client; struct i2c_msg msgs[] = { |
a606757ff
|
133 134 135 136 137 138 |
{ .addr = client->addr, .flags = I2C_M_RD, .len = sizeof(rs5c->buf), .buf = rs5c->buf }, |
cb26b572d
|
139 140 141 142 143 144 145 146 |
}; /* This implements the third reading method from the datasheet, using * an internal address that's reset after each transaction (by STOP) * to 0x0f ... so we read extra registers, and skip the first one. * * The first method doesn't work with the iop3xx adapter driver, on at * least 80219 chips; this works around that bug. |
0053dc0d1
|
147 148 149 150 |
* * The third method on the other hand doesn't work for the SMBus-only * configurations, so we use the the first method there, stripping off * the extra register in the process. |
cb26b572d
|
151 |
*/ |
0053dc0d1
|
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
if (rs5c->smbus) { int addr = RS5C_ADDR(RS5C372_REG_SECS); int size = sizeof(rs5c->buf) - 1; if (i2c_smbus_read_i2c_block_data(client, addr, size, rs5c->buf + 1) != size) { dev_warn(&client->dev, "can't read registers "); return -EIO; } } else { if ((i2c_transfer(client->adapter, msgs, 1)) != 1) { dev_warn(&client->dev, "can't read registers "); return -EIO; } |
cb26b572d
|
168 |
} |
7520b94de
|
169 |
|
cb26b572d
|
170 |
dev_dbg(&client->dev, |
b513e522c
|
171 172 173 174 175 176 |
"%3ph (%02x) %3ph (%02x), %3ph, %3ph; %02x %02x ", rs5c->regs + 0, rs5c->regs[3], rs5c->regs + 4, rs5c->regs[7], rs5c->regs + 8, rs5c->regs + 11, rs5c->regs[14], rs5c->regs[15]); |
7520b94de
|
177 |
|
cb26b572d
|
178 179 |
return 0; } |
c6f24f99c
|
180 |
|
cb26b572d
|
181 182 183 |
static unsigned rs5c_reg2hr(struct rs5c372 *rs5c, unsigned reg) { unsigned hour; |
7520b94de
|
184 |
|
cb26b572d
|
185 |
if (rs5c->time24) |
fe20ba70a
|
186 |
return bcd2bin(reg & 0x3f); |
cb26b572d
|
187 |
|
fe20ba70a
|
188 |
hour = bcd2bin(reg & 0x1f); |
cb26b572d
|
189 190 191 192 193 194 195 196 |
if (hour == 12) hour = 0; if (reg & 0x20) hour += 12; return hour; } static unsigned rs5c_hr2reg(struct rs5c372 *rs5c, unsigned hour) |
7520b94de
|
197 |
{ |
cb26b572d
|
198 |
if (rs5c->time24) |
fe20ba70a
|
199 |
return bin2bcd(hour); |
cb26b572d
|
200 201 |
if (hour > 12) |
fe20ba70a
|
202 |
return 0x20 | bin2bcd(hour - 12); |
cb26b572d
|
203 |
if (hour == 12) |
fe20ba70a
|
204 |
return 0x20 | bin2bcd(12); |
cb26b572d
|
205 |
if (hour == 0) |
fe20ba70a
|
206 207 |
return bin2bcd(12); return bin2bcd(hour); |
cb26b572d
|
208 |
} |
7520b94de
|
209 |
|
44db5c92a
|
210 |
static int rs5c372_rtc_read_time(struct device *dev, struct rtc_time *tm) |
cb26b572d
|
211 |
{ |
44db5c92a
|
212 |
struct i2c_client *client = to_i2c_client(dev); |
cb26b572d
|
213 214 |
struct rs5c372 *rs5c = i2c_get_clientdata(client); int status = rs5c_get_regs(rs5c); |
09832dc05
|
215 |
unsigned char ctrl2 = rs5c->regs[RS5C_REG_CTRL2]; |
c6f24f99c
|
216 |
|
cb26b572d
|
217 218 |
if (status < 0) return status; |
7520b94de
|
219 |
|
09832dc05
|
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
switch (rs5c->type) { case rtc_r2025sd: case rtc_r2221tl: if ((rs5c->type == rtc_r2025sd && !(ctrl2 & R2x2x_CTRL2_XSTP)) || (rs5c->type == rtc_r2221tl && (ctrl2 & R2x2x_CTRL2_XSTP))) { dev_warn(&client->dev, "rtc oscillator interruption detected. Please reset the rtc clock. "); return -EINVAL; } break; default: if (ctrl2 & RS5C_CTRL2_XSTP) { dev_warn(&client->dev, "rtc oscillator interruption detected. Please reset the rtc clock. "); return -EINVAL; } } |
fe20ba70a
|
237 238 |
tm->tm_sec = bcd2bin(rs5c->regs[RS5C372_REG_SECS] & 0x7f); tm->tm_min = bcd2bin(rs5c->regs[RS5C372_REG_MINS] & 0x7f); |
cb26b572d
|
239 |
tm->tm_hour = rs5c_reg2hr(rs5c, rs5c->regs[RS5C372_REG_HOURS]); |
7520b94de
|
240 |
|
fe20ba70a
|
241 242 |
tm->tm_wday = bcd2bin(rs5c->regs[RS5C372_REG_WDAY] & 0x07); tm->tm_mday = bcd2bin(rs5c->regs[RS5C372_REG_DAY] & 0x3f); |
7520b94de
|
243 244 |
/* tm->tm_mon is zero-based */ |
fe20ba70a
|
245 |
tm->tm_mon = bcd2bin(rs5c->regs[RS5C372_REG_MONTH] & 0x1f) - 1; |
7520b94de
|
246 247 |
/* year is 1900 + tm->tm_year */ |
fe20ba70a
|
248 |
tm->tm_year = bcd2bin(rs5c->regs[RS5C372_REG_YEAR]) + 100; |
7520b94de
|
249 250 251 252 |
dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, " "mday=%d, mon=%d, year=%d, wday=%d ", |
2a4e2b878
|
253 |
__func__, |
7520b94de
|
254 255 |
tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); |
57f454fb5
|
256 |
return 0; |
7520b94de
|
257 |
} |
44db5c92a
|
258 |
static int rs5c372_rtc_set_time(struct device *dev, struct rtc_time *tm) |
7520b94de
|
259 |
{ |
44db5c92a
|
260 |
struct i2c_client *client = to_i2c_client(dev); |
cb26b572d
|
261 |
struct rs5c372 *rs5c = i2c_get_clientdata(client); |
118364948
|
262 |
unsigned char buf[7]; |
09832dc05
|
263 |
unsigned char ctrl2; |
0053dc0d1
|
264 |
int addr; |
7520b94de
|
265 |
|
cb26b572d
|
266 |
dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d " |
7520b94de
|
267 268 |
"mday=%d, mon=%d, year=%d, wday=%d ", |
2a4e2b878
|
269 |
__func__, |
cb26b572d
|
270 |
tm->tm_sec, tm->tm_min, tm->tm_hour, |
7520b94de
|
271 |
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); |
0053dc0d1
|
272 |
addr = RS5C_ADDR(RS5C372_REG_SECS); |
fe20ba70a
|
273 274 |
buf[0] = bin2bcd(tm->tm_sec); buf[1] = bin2bcd(tm->tm_min); |
0053dc0d1
|
275 |
buf[2] = rs5c_hr2reg(rs5c, tm->tm_hour); |
fe20ba70a
|
276 277 278 279 |
buf[3] = bin2bcd(tm->tm_wday); buf[4] = bin2bcd(tm->tm_mday); buf[5] = bin2bcd(tm->tm_mon + 1); buf[6] = bin2bcd(tm->tm_year - 100); |
7520b94de
|
280 |
|
0053dc0d1
|
281 |
if (i2c_smbus_write_i2c_block_data(client, addr, sizeof(buf), buf) < 0) { |
09832dc05
|
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 |
dev_dbg(&client->dev, "%s: write error in line %i ", __func__, __LINE__); return -EIO; } addr = RS5C_ADDR(RS5C_REG_CTRL2); ctrl2 = i2c_smbus_read_byte_data(client, addr); /* clear rtc warning bits */ switch (rs5c->type) { case rtc_r2025sd: case rtc_r2221tl: ctrl2 &= ~(R2x2x_CTRL2_VDET | R2x2x_CTRL2_PON); if (rs5c->type == rtc_r2025sd) ctrl2 |= R2x2x_CTRL2_XSTP; else ctrl2 &= ~R2x2x_CTRL2_XSTP; break; default: ctrl2 &= ~RS5C_CTRL2_XSTP; break; } if (i2c_smbus_write_byte_data(client, addr, ctrl2) < 0) { dev_dbg(&client->dev, "%s: write error in line %i ", __func__, __LINE__); |
7520b94de
|
310 311 312 313 314 |
return -EIO; } return 0; } |
6fca3fc51
|
315 |
#if IS_ENABLED(CONFIG_RTC_INTF_PROC) |
cb26b572d
|
316 317 |
#define NEED_TRIM #endif |
6fca3fc51
|
318 |
#if IS_ENABLED(CONFIG_RTC_INTF_SYSFS) |
cb26b572d
|
319 320 321 322 |
#define NEED_TRIM #endif #ifdef NEED_TRIM |
7520b94de
|
323 324 |
static int rs5c372_get_trim(struct i2c_client *client, int *osc, int *trim) { |
c6f24f99c
|
325 |
struct rs5c372 *rs5c372 = i2c_get_clientdata(client); |
cb26b572d
|
326 |
u8 tmp = rs5c372->regs[RS5C372_REG_TRIM]; |
7520b94de
|
327 |
|
7520b94de
|
328 |
if (osc) |
c6f24f99c
|
329 |
*osc = (tmp & RS5C372_TRIM_XSL) ? 32000 : 32768; |
7520b94de
|
330 |
|
17ad78e59
|
331 |
if (trim) { |
2a4e2b878
|
332 333 |
dev_dbg(&client->dev, "%s: raw trim=%x ", __func__, tmp); |
cb26b572d
|
334 335 336 337 338 339 340 341 342 343 344 345 346 |
tmp &= RS5C372_TRIM_MASK; if (tmp & 0x3e) { int t = tmp & 0x3f; if (tmp & 0x40) t = (~t | (s8)0xc0) + 1; else t = t - 1; tmp = t * 2; } else tmp = 0; *trim = tmp; |
17ad78e59
|
347 |
} |
7520b94de
|
348 349 350 |
return 0; } |
cb26b572d
|
351 |
#endif |
7520b94de
|
352 |
|
16380c153
|
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 |
static int rs5c_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) { struct i2c_client *client = to_i2c_client(dev); struct rs5c372 *rs5c = i2c_get_clientdata(client); unsigned char buf; int status, addr; buf = rs5c->regs[RS5C_REG_CTRL1]; if (!rs5c->has_irq) return -EINVAL; status = rs5c_get_regs(rs5c); if (status < 0) return status; addr = RS5C_ADDR(RS5C_REG_CTRL1); if (enabled) buf |= RS5C_CTRL1_AALE; else buf &= ~RS5C_CTRL1_AALE; if (i2c_smbus_write_byte_data(client, addr, buf) < 0) { |
0c6516ea4
|
376 377 |
dev_warn(dev, "can't update alarm "); |
16380c153
|
378 379 380 381 382 383 |
status = -EIO; } else rs5c->regs[RS5C_REG_CTRL1] = buf; return status; } |
cb26b572d
|
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 |
/* NOTE: Since RTC_WKALM_{RD,SET} were originally defined for EFI, * which only exposes a polled programming interface; and since * these calls map directly to those EFI requests; we don't demand * we have an IRQ for this chip when we go through this API. * * The older x86_pc derived RTC_ALM_{READ,SET} calls require irqs * though, managed through RTC_AIE_{ON,OFF} requests. */ static int rs5c_read_alarm(struct device *dev, struct rtc_wkalrm *t) { struct i2c_client *client = to_i2c_client(dev); struct rs5c372 *rs5c = i2c_get_clientdata(client); int status; status = rs5c_get_regs(rs5c); if (status < 0) return status; /* report alarm time */ t->time.tm_sec = 0; |
fe20ba70a
|
405 |
t->time.tm_min = bcd2bin(rs5c->regs[RS5C_REG_ALARM_A_MIN] & 0x7f); |
cb26b572d
|
406 |
t->time.tm_hour = rs5c_reg2hr(rs5c, rs5c->regs[RS5C_REG_ALARM_A_HOURS]); |
cb26b572d
|
407 408 409 410 411 412 413 414 415 416 417 418 |
/* ... and status */ t->enabled = !!(rs5c->regs[RS5C_REG_CTRL1] & RS5C_CTRL1_AALE); t->pending = !!(rs5c->regs[RS5C_REG_CTRL2] & RS5C_CTRL2_AAFG); return 0; } static int rs5c_set_alarm(struct device *dev, struct rtc_wkalrm *t) { struct i2c_client *client = to_i2c_client(dev); struct rs5c372 *rs5c = i2c_get_clientdata(client); |
0053dc0d1
|
419 420 |
int status, addr, i; unsigned char buf[3]; |
cb26b572d
|
421 422 423 424 425 426 427 428 429 430 431 432 433 434 |
/* only handle up to 24 hours in the future, like RTC_ALM_SET */ if (t->time.tm_mday != -1 || t->time.tm_mon != -1 || t->time.tm_year != -1) return -EINVAL; /* REVISIT: round up tm_sec */ /* if needed, disable irq (clears pending status) */ status = rs5c_get_regs(rs5c); if (status < 0) return status; if (rs5c->regs[RS5C_REG_CTRL1] & RS5C_CTRL1_AALE) { |
0053dc0d1
|
435 436 437 |
addr = RS5C_ADDR(RS5C_REG_CTRL1); buf[0] = rs5c->regs[RS5C_REG_CTRL1] & ~RS5C_CTRL1_AALE; if (i2c_smbus_write_byte_data(client, addr, buf[0]) < 0) { |
0c6516ea4
|
438 439 |
dev_dbg(dev, "can't disable alarm "); |
cb26b572d
|
440 441 |
return -EIO; } |
0053dc0d1
|
442 |
rs5c->regs[RS5C_REG_CTRL1] = buf[0]; |
cb26b572d
|
443 444 445 |
} /* set alarm */ |
fe20ba70a
|
446 |
buf[0] = bin2bcd(t->time.tm_min); |
0053dc0d1
|
447 448 449 450 451 452 |
buf[1] = rs5c_hr2reg(rs5c, t->time.tm_hour); buf[2] = 0x7f; /* any/all days */ for (i = 0; i < sizeof(buf); i++) { addr = RS5C_ADDR(RS5C_REG_ALARM_A_MIN + i); if (i2c_smbus_write_byte_data(client, addr, buf[i]) < 0) { |
0c6516ea4
|
453 454 |
dev_dbg(dev, "can't set alarm time "); |
0053dc0d1
|
455 456 |
return -EIO; } |
cb26b572d
|
457 458 459 460 |
} /* ... and maybe enable its irq */ if (t->enabled) { |
0053dc0d1
|
461 462 463 |
addr = RS5C_ADDR(RS5C_REG_CTRL1); buf[0] = rs5c->regs[RS5C_REG_CTRL1] | RS5C_CTRL1_AALE; if (i2c_smbus_write_byte_data(client, addr, buf[0]) < 0) |
0c6516ea4
|
464 465 |
dev_warn(dev, "can't enable alarm "); |
0053dc0d1
|
466 |
rs5c->regs[RS5C_REG_CTRL1] = buf[0]; |
cb26b572d
|
467 468 469 470 |
} return 0; } |
6fca3fc51
|
471 |
#if IS_ENABLED(CONFIG_RTC_INTF_PROC) |
cb26b572d
|
472 |
|
7520b94de
|
473 474 475 |
static int rs5c372_rtc_proc(struct device *dev, struct seq_file *seq) { int err, osc, trim; |
adfb43412
|
476 477 |
err = rs5c372_get_trim(to_i2c_client(dev), &osc, &trim); if (err == 0) { |
cb26b572d
|
478 479 480 481 482 |
seq_printf(seq, "crystal\t\t: %d.%03d KHz ", osc / 1000, osc % 1000); seq_printf(seq, "trim\t\t: %d ", trim); |
7520b94de
|
483 484 485 486 |
} return 0; } |
cb26b572d
|
487 488 489 |
#else #define rs5c372_rtc_proc NULL #endif |
ff8371ac9
|
490 |
static const struct rtc_class_ops rs5c372_rtc_ops = { |
7520b94de
|
491 492 493 |
.proc = rs5c372_rtc_proc, .read_time = rs5c372_rtc_read_time, .set_time = rs5c372_rtc_set_time, |
cb26b572d
|
494 495 |
.read_alarm = rs5c_read_alarm, .set_alarm = rs5c_set_alarm, |
16380c153
|
496 |
.alarm_irq_enable = rs5c_rtc_alarm_irq_enable, |
7520b94de
|
497 |
}; |
6fca3fc51
|
498 |
#if IS_ENABLED(CONFIG_RTC_INTF_SYSFS) |
cb26b572d
|
499 |
|
7520b94de
|
500 501 502 |
static ssize_t rs5c372_sysfs_show_trim(struct device *dev, struct device_attribute *attr, char *buf) { |
828960724
|
503 |
int err, trim; |
7520b94de
|
504 |
|
828960724
|
505 506 507 |
err = rs5c372_get_trim(to_i2c_client(dev), NULL, &trim); if (err) return err; |
7520b94de
|
508 |
|
cb26b572d
|
509 510 |
return sprintf(buf, "%d ", trim); |
7520b94de
|
511 512 513 514 515 516 |
} static DEVICE_ATTR(trim, S_IRUGO, rs5c372_sysfs_show_trim, NULL); static ssize_t rs5c372_sysfs_show_osc(struct device *dev, struct device_attribute *attr, char *buf) { |
828960724
|
517 |
int err, osc; |
7520b94de
|
518 |
|
828960724
|
519 520 521 |
err = rs5c372_get_trim(to_i2c_client(dev), &osc, NULL); if (err) return err; |
7520b94de
|
522 |
|
828960724
|
523 524 |
return sprintf(buf, "%d.%03d KHz ", osc / 1000, osc % 1000); |
7520b94de
|
525 526 |
} static DEVICE_ATTR(osc, S_IRUGO, rs5c372_sysfs_show_osc, NULL); |
cb26b572d
|
527 |
static int rs5c_sysfs_register(struct device *dev) |
7520b94de
|
528 |
{ |
cb26b572d
|
529 530 531 532 533 534 535 536 537 538 539 |
int err; err = device_create_file(dev, &dev_attr_trim); if (err) return err; err = device_create_file(dev, &dev_attr_osc); if (err) device_remove_file(dev, &dev_attr_trim); return err; } |
d815461c7
|
540 541 542 543 544 |
static void rs5c_sysfs_unregister(struct device *dev) { device_remove_file(dev, &dev_attr_trim); device_remove_file(dev, &dev_attr_osc); } |
cb26b572d
|
545 546 547 548 |
#else static int rs5c_sysfs_register(struct device *dev) { return 0; |
7520b94de
|
549 |
} |
d815461c7
|
550 551 552 553 554 |
static void rs5c_sysfs_unregister(struct device *dev) { /* nothing */ } |
cb26b572d
|
555 556 557 |
#endif /* SYSFS */ static struct i2c_driver rs5c372_driver; |
7520b94de
|
558 |
|
0053dc0d1
|
559 560 561 562 |
static int rs5c_oscillator_setup(struct rs5c372 *rs5c372) { unsigned char buf[2]; int addr, i, ret = 0; |
761acdda5
|
563 564 565 |
addr = RS5C_ADDR(RS5C_REG_CTRL1); buf[0] = rs5c372->regs[RS5C_REG_CTRL1]; buf[1] = rs5c372->regs[RS5C_REG_CTRL2]; |
761acdda5
|
566 567 568 |
switch (rs5c372->type) { case rtc_r2025sd: if (buf[1] & R2x2x_CTRL2_XSTP) |
37fc5e2c4
|
569 |
return ret; |
761acdda5
|
570 571 572 573 |
break; case rtc_r2221tl: if (!(buf[1] & R2x2x_CTRL2_XSTP)) return ret; |
761acdda5
|
574 |
break; |
761acdda5
|
575 576 |
default: if (!(buf[1] & RS5C_CTRL2_XSTP)) |
37fc5e2c4
|
577 |
return ret; |
761acdda5
|
578 |
break; |
37fc5e2c4
|
579 |
} |
0053dc0d1
|
580 |
|
0053dc0d1
|
581 582 583 584 585 586 587 |
/* use 24hr mode */ switch (rs5c372->type) { case rtc_rs5c372a: case rtc_rs5c372b: buf[1] |= RS5C372_CTRL2_24; rs5c372->time24 = 1; break; |
37fc5e2c4
|
588 |
case rtc_r2025sd: |
550fcb8f7
|
589 |
case rtc_r2221tl: |
0053dc0d1
|
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 |
case rtc_rv5c386: case rtc_rv5c387a: buf[0] |= RV5C387_CTRL1_24; rs5c372->time24 = 1; break; default: /* impossible */ break; } for (i = 0; i < sizeof(buf); i++) { addr = RS5C_ADDR(RS5C_REG_CTRL1 + i); ret = i2c_smbus_write_byte_data(rs5c372->client, addr, buf[i]); if (unlikely(ret < 0)) return ret; } rs5c372->regs[RS5C_REG_CTRL1] = buf[0]; rs5c372->regs[RS5C_REG_CTRL2] = buf[1]; return 0; } |
d2653e927
|
612 613 |
static int rs5c372_probe(struct i2c_client *client, const struct i2c_device_id *id) |
7520b94de
|
614 615 |
{ int err = 0; |
0053dc0d1
|
616 |
int smbus_mode = 0; |
c6f24f99c
|
617 |
struct rs5c372 *rs5c372; |
7520b94de
|
618 |
|
2a4e2b878
|
619 620 |
dev_dbg(&client->dev, "%s ", __func__); |
7520b94de
|
621 |
|
0053dc0d1
|
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 |
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_I2C_BLOCK)) { /* * If we don't have any master mode adapter, try breaking * it down in to the barest of capabilities. */ if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_I2C_BLOCK)) smbus_mode = 1; else { /* Still no good, give up */ err = -ENODEV; goto exit; } |
7520b94de
|
637 |
} |
b8a4b4e2f
|
638 639 640 |
rs5c372 = devm_kzalloc(&client->dev, sizeof(struct rs5c372), GFP_KERNEL); if (!rs5c372) { |
7520b94de
|
641 642 643 |
err = -ENOMEM; goto exit; } |
cb26b572d
|
644 |
|
cb26b572d
|
645 |
rs5c372->client = client; |
c6f24f99c
|
646 |
i2c_set_clientdata(client, rs5c372); |
ff764b88e
|
647 648 649 650 651 |
if (client->dev.of_node) rs5c372->type = (enum rtc_type) of_device_get_match_data(&client->dev); else rs5c372->type = id->driver_data; |
c6f24f99c
|
652 |
|
e2bfe3424
|
653 654 |
/* we read registers 0x0f then 0x00-0x0f; skip the first one */ rs5c372->regs = &rs5c372->buf[1]; |
0053dc0d1
|
655 |
rs5c372->smbus = smbus_mode; |
e2bfe3424
|
656 |
|
cb26b572d
|
657 658 |
err = rs5c_get_regs(rs5c372); if (err < 0) |
b8a4b4e2f
|
659 |
goto exit; |
cb26b572d
|
660 |
|
cb26b572d
|
661 662 663 664 665 666 667 668 669 670 |
/* clock may be set for am/pm or 24 hr time */ switch (rs5c372->type) { case rtc_rs5c372a: case rtc_rs5c372b: /* alarm uses ALARM_A; and nINTRA on 372a, nINTR on 372b. * so does periodic irq, except some 327a modes. */ if (rs5c372->regs[RS5C_REG_CTRL2] & RS5C372_CTRL2_24) rs5c372->time24 = 1; break; |
37fc5e2c4
|
671 |
case rtc_r2025sd: |
550fcb8f7
|
672 |
case rtc_r2221tl: |
cb26b572d
|
673 674 675 676 677 678 679 680 681 682 683 |
case rtc_rv5c386: case rtc_rv5c387a: if (rs5c372->regs[RS5C_REG_CTRL1] & RV5C387_CTRL1_24) rs5c372->time24 = 1; /* alarm uses ALARM_W; and nINTRB for alarm and periodic * irq, on both 386 and 387 */ break; default: dev_err(&client->dev, "unknown RTC type "); |
b8a4b4e2f
|
684 |
goto exit; |
cb26b572d
|
685 686 687 688 |
} /* if the oscillator lost power and no other software (like * the bootloader) set it up, do it here. |
37fc5e2c4
|
689 690 691 |
* * The R2025S/D does this a little differently than the other * parts, so we special case that.. |
cb26b572d
|
692 |
*/ |
0053dc0d1
|
693 694 695 696 |
err = rs5c_oscillator_setup(rs5c372); if (unlikely(err < 0)) { dev_err(&client->dev, "setup error "); |
b8a4b4e2f
|
697 |
goto exit; |
cb26b572d
|
698 |
} |
fa5691131
|
699 700 |
dev_info(&client->dev, "%s found, %s ", |
cb26b572d
|
701 |
({ char *s; switch (rs5c372->type) { |
37fc5e2c4
|
702 |
case rtc_r2025sd: s = "r2025sd"; break; |
550fcb8f7
|
703 |
case rtc_r2221tl: s = "r2221tl"; break; |
cb26b572d
|
704 705 706 707 708 709 710 711 |
case rtc_rs5c372a: s = "rs5c372a"; break; case rtc_rs5c372b: s = "rs5c372b"; break; case rtc_rv5c386: s = "rv5c386"; break; case rtc_rv5c387a: s = "rv5c387a"; break; default: s = "chip"; break; }; s;}), rs5c372->time24 ? "24hr" : "am/pm" ); |
d815461c7
|
712 |
/* REVISIT use client->irq to register alarm irq ... */ |
b8a4b4e2f
|
713 714 715 |
rs5c372->rtc = devm_rtc_device_register(&client->dev, rs5c372_driver.driver.name, &rs5c372_rtc_ops, THIS_MODULE); |
7520b94de
|
716 |
|
c6f24f99c
|
717 718 |
if (IS_ERR(rs5c372->rtc)) { err = PTR_ERR(rs5c372->rtc); |
b8a4b4e2f
|
719 |
goto exit; |
7520b94de
|
720 |
} |
cb26b572d
|
721 |
err = rs5c_sysfs_register(&client->dev); |
c6f24f99c
|
722 |
if (err) |
b8a4b4e2f
|
723 |
goto exit; |
7520b94de
|
724 725 |
return 0; |
7520b94de
|
726 727 728 |
exit: return err; } |
d815461c7
|
729 |
static int rs5c372_remove(struct i2c_client *client) |
cb26b572d
|
730 |
{ |
d815461c7
|
731 |
rs5c_sysfs_unregister(&client->dev); |
7520b94de
|
732 733 |
return 0; } |
cb26b572d
|
734 735 736 |
static struct i2c_driver rs5c372_driver = { .driver = { .name = "rtc-rs5c372", |
ff764b88e
|
737 |
.of_match_table = of_match_ptr(rs5c372_of_match), |
cb26b572d
|
738 |
}, |
d815461c7
|
739 740 |
.probe = rs5c372_probe, .remove = rs5c372_remove, |
3760f7367
|
741 |
.id_table = rs5c372_id, |
cb26b572d
|
742 |
}; |
0abc92011
|
743 |
module_i2c_driver(rs5c372_driver); |
7520b94de
|
744 745 746 |
MODULE_AUTHOR( "Pavel Mironchik <pmironchik@optifacio.net>, " |
0053dc0d1
|
747 748 |
"Alessandro Zummo <a.zummo@towertech.it>, " "Paul Mundt <lethal@linux-sh.org>"); |
7520b94de
|
749 750 |
MODULE_DESCRIPTION("Ricoh RS5C372 RTC driver"); MODULE_LICENSE("GPL"); |