aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rtc/rtc-pl031.c
diff options
context:
space:
mode:
authorJonathan Herman <hermanjl@cs.unc.edu>2013-01-17 16:15:55 -0500
committerJonathan Herman <hermanjl@cs.unc.edu>2013-01-17 16:15:55 -0500
commit8dea78da5cee153b8af9c07a2745f6c55057fe12 (patch)
treea8f4d49d63b1ecc92f2fddceba0655b2472c5bd9 /drivers/rtc/rtc-pl031.c
parent406089d01562f1e2bf9f089fd7637009ebaad589 (diff)
Patched in Tegra support.
Diffstat (limited to 'drivers/rtc/rtc-pl031.c')
-rw-r--r--drivers/rtc/rtc-pl031.c141
1 files changed, 55 insertions, 86 deletions
diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c
index 08378e3cc21..ff1b84bd9bb 100644
--- a/drivers/rtc/rtc-pl031.c
+++ b/drivers/rtc/rtc-pl031.c
@@ -68,26 +68,11 @@
68 68
69#define RTC_TIMER_FREQ 32768 69#define RTC_TIMER_FREQ 32768
70 70
71/**
72 * struct pl031_vendor_data - per-vendor variations
73 * @ops: the vendor-specific operations used on this silicon version
74 * @clockwatch: if this is an ST Microelectronics silicon version with a
75 * clockwatch function
76 * @st_weekday: if this is an ST Microelectronics silicon version that need
77 * the weekday fix
78 * @irqflags: special IRQ flags per variant
79 */
80struct pl031_vendor_data {
81 struct rtc_class_ops ops;
82 bool clockwatch;
83 bool st_weekday;
84 unsigned long irqflags;
85};
86
87struct pl031_local { 71struct pl031_local {
88 struct pl031_vendor_data *vendor;
89 struct rtc_device *rtc; 72 struct rtc_device *rtc;
90 void __iomem *base; 73 void __iomem *base;
74 u8 hw_designer;
75 u8 hw_revision:4;
91}; 76};
92 77
93static int pl031_alarm_irq_enable(struct device *dev, 78static int pl031_alarm_irq_enable(struct device *dev,
@@ -235,9 +220,17 @@ static irqreturn_t pl031_interrupt(int irq, void *dev_id)
235 unsigned long events = 0; 220 unsigned long events = 0;
236 221
237 rtcmis = readl(ldata->base + RTC_MIS); 222 rtcmis = readl(ldata->base + RTC_MIS);
238 if (rtcmis & RTC_BIT_AI) { 223 if (rtcmis) {
239 writel(RTC_BIT_AI, ldata->base + RTC_ICR); 224 writel(rtcmis, ldata->base + RTC_ICR);
240 events |= (RTC_AF | RTC_IRQF); 225
226 if (rtcmis & RTC_BIT_AI)
227 events |= (RTC_AF | RTC_IRQF);
228
229 /* Timer interrupt is only available in ST variants */
230 if ((rtcmis & RTC_BIT_PI) &&
231 (ldata->hw_designer == AMBA_VENDOR_ST))
232 events |= (RTC_PF | RTC_IRQF);
233
241 rtc_update_irq(ldata->rtc, 1, events); 234 rtc_update_irq(ldata->rtc, 1, events);
242 235
243 return IRQ_HANDLED; 236 return IRQ_HANDLED;
@@ -318,9 +311,7 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
318{ 311{
319 int ret; 312 int ret;
320 struct pl031_local *ldata; 313 struct pl031_local *ldata;
321 struct pl031_vendor_data *vendor = id->data; 314 struct rtc_class_ops *ops = id->data;
322 struct rtc_class_ops *ops = &vendor->ops;
323 unsigned long time;
324 315
325 ret = amba_request_regions(adev, NULL); 316 ret = amba_request_regions(adev, NULL);
326 if (ret) 317 if (ret)
@@ -331,7 +322,6 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
331 ret = -ENOMEM; 322 ret = -ENOMEM;
332 goto out; 323 goto out;
333 } 324 }
334 ldata->vendor = vendor;
335 325
336 ldata->base = ioremap(adev->res.start, resource_size(&adev->res)); 326 ldata->base = ioremap(adev->res.start, resource_size(&adev->res));
337 327
@@ -342,31 +332,18 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
342 332
343 amba_set_drvdata(adev, ldata); 333 amba_set_drvdata(adev, ldata);
344 334
345 dev_dbg(&adev->dev, "designer ID = 0x%02x\n", amba_manf(adev)); 335 ldata->hw_designer = amba_manf(adev);
346 dev_dbg(&adev->dev, "revision = 0x%01x\n", amba_rev(adev)); 336 ldata->hw_revision = amba_rev(adev);
337
338 dev_dbg(&adev->dev, "designer ID = 0x%02x\n", ldata->hw_designer);
339 dev_dbg(&adev->dev, "revision = 0x%01x\n", ldata->hw_revision);
347 340
348 /* Enable the clockwatch on ST Variants */ 341 /* Enable the clockwatch on ST Variants */
349 if (vendor->clockwatch) 342 if ((ldata->hw_designer == AMBA_VENDOR_ST) &&
343 (ldata->hw_revision > 1))
350 writel(readl(ldata->base + RTC_CR) | RTC_CR_CWEN, 344 writel(readl(ldata->base + RTC_CR) | RTC_CR_CWEN,
351 ldata->base + RTC_CR); 345 ldata->base + RTC_CR);
352 346
353 /*
354 * On ST PL031 variants, the RTC reset value does not provide correct
355 * weekday for 2000-01-01. Correct the erroneous sunday to saturday.
356 */
357 if (vendor->st_weekday) {
358 if (readl(ldata->base + RTC_YDR) == 0x2000) {
359 time = readl(ldata->base + RTC_DR);
360 if ((time &
361 (RTC_MON_MASK | RTC_MDAY_MASK | RTC_WDAY_MASK))
362 == 0x02120000) {
363 time = time | (0x7 << RTC_WDAY_SHIFT);
364 writel(0x2000, ldata->base + RTC_YLR);
365 writel(time, ldata->base + RTC_LR);
366 }
367 }
368 }
369
370 ldata->rtc = rtc_device_register("pl031", &adev->dev, ops, 347 ldata->rtc = rtc_device_register("pl031", &adev->dev, ops,
371 THIS_MODULE); 348 THIS_MODULE);
372 if (IS_ERR(ldata->rtc)) { 349 if (IS_ERR(ldata->rtc)) {
@@ -375,7 +352,7 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
375 } 352 }
376 353
377 if (request_irq(adev->irq[0], pl031_interrupt, 354 if (request_irq(adev->irq[0], pl031_interrupt,
378 vendor->irqflags, "rtc-pl031", ldata)) { 355 IRQF_DISABLED, "rtc-pl031", ldata)) {
379 ret = -EIO; 356 ret = -EIO;
380 goto out_no_irq; 357 goto out_no_irq;
381 } 358 }
@@ -397,71 +374,52 @@ err_req:
397} 374}
398 375
399/* Operations for the original ARM version */ 376/* Operations for the original ARM version */
400static struct pl031_vendor_data arm_pl031 = { 377static struct rtc_class_ops arm_pl031_ops = {
401 .ops = { 378 .read_time = pl031_read_time,
402 .read_time = pl031_read_time, 379 .set_time = pl031_set_time,
403 .set_time = pl031_set_time, 380 .read_alarm = pl031_read_alarm,
404 .read_alarm = pl031_read_alarm, 381 .set_alarm = pl031_set_alarm,
405 .set_alarm = pl031_set_alarm, 382 .alarm_irq_enable = pl031_alarm_irq_enable,
406 .alarm_irq_enable = pl031_alarm_irq_enable,
407 },
408 .irqflags = IRQF_NO_SUSPEND,
409}; 383};
410 384
411/* The First ST derivative */ 385/* The First ST derivative */
412static struct pl031_vendor_data stv1_pl031 = { 386static struct rtc_class_ops stv1_pl031_ops = {
413 .ops = { 387 .read_time = pl031_read_time,
414 .read_time = pl031_read_time, 388 .set_time = pl031_set_time,
415 .set_time = pl031_set_time, 389 .read_alarm = pl031_read_alarm,
416 .read_alarm = pl031_read_alarm, 390 .set_alarm = pl031_set_alarm,
417 .set_alarm = pl031_set_alarm, 391 .alarm_irq_enable = pl031_alarm_irq_enable,
418 .alarm_irq_enable = pl031_alarm_irq_enable,
419 },
420 .clockwatch = true,
421 .st_weekday = true,
422 .irqflags = IRQF_NO_SUSPEND,
423}; 392};
424 393
425/* And the second ST derivative */ 394/* And the second ST derivative */
426static struct pl031_vendor_data stv2_pl031 = { 395static struct rtc_class_ops stv2_pl031_ops = {
427 .ops = { 396 .read_time = pl031_stv2_read_time,
428 .read_time = pl031_stv2_read_time, 397 .set_time = pl031_stv2_set_time,
429 .set_time = pl031_stv2_set_time, 398 .read_alarm = pl031_stv2_read_alarm,
430 .read_alarm = pl031_stv2_read_alarm, 399 .set_alarm = pl031_stv2_set_alarm,
431 .set_alarm = pl031_stv2_set_alarm, 400 .alarm_irq_enable = pl031_alarm_irq_enable,
432 .alarm_irq_enable = pl031_alarm_irq_enable,
433 },
434 .clockwatch = true,
435 .st_weekday = true,
436 /*
437 * This variant shares the IRQ with another block and must not
438 * suspend that IRQ line.
439 */
440 .irqflags = IRQF_SHARED | IRQF_NO_SUSPEND,
441}; 401};
442 402
443static struct amba_id pl031_ids[] = { 403static struct amba_id pl031_ids[] = {
444 { 404 {
445 .id = 0x00041031, 405 .id = 0x00041031,
446 .mask = 0x000fffff, 406 .mask = 0x000fffff,
447 .data = &arm_pl031, 407 .data = &arm_pl031_ops,
448 }, 408 },
449 /* ST Micro variants */ 409 /* ST Micro variants */
450 { 410 {
451 .id = 0x00180031, 411 .id = 0x00180031,
452 .mask = 0x00ffffff, 412 .mask = 0x00ffffff,
453 .data = &stv1_pl031, 413 .data = &stv1_pl031_ops,
454 }, 414 },
455 { 415 {
456 .id = 0x00280031, 416 .id = 0x00280031,
457 .mask = 0x00ffffff, 417 .mask = 0x00ffffff,
458 .data = &stv2_pl031, 418 .data = &stv2_pl031_ops,
459 }, 419 },
460 {0, 0}, 420 {0, 0},
461}; 421};
462 422
463MODULE_DEVICE_TABLE(amba, pl031_ids);
464
465static struct amba_driver pl031_driver = { 423static struct amba_driver pl031_driver = {
466 .drv = { 424 .drv = {
467 .name = "rtc-pl031", 425 .name = "rtc-pl031",
@@ -471,7 +429,18 @@ static struct amba_driver pl031_driver = {
471 .remove = pl031_remove, 429 .remove = pl031_remove,
472}; 430};
473 431
474module_amba_driver(pl031_driver); 432static int __init pl031_init(void)
433{
434 return amba_driver_register(&pl031_driver);
435}
436
437static void __exit pl031_exit(void)
438{
439 amba_driver_unregister(&pl031_driver);
440}
441
442module_init(pl031_init);
443module_exit(pl031_exit);
475 444
476MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net"); 445MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net");
477MODULE_DESCRIPTION("ARM AMBA PL031 RTC Driver"); 446MODULE_DESCRIPTION("ARM AMBA PL031 RTC Driver");