diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-17 16:15:55 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-17 16:15:55 -0500 |
commit | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (patch) | |
tree | a8f4d49d63b1ecc92f2fddceba0655b2472c5bd9 /drivers/rtc/rtc-pl031.c | |
parent | 406089d01562f1e2bf9f089fd7637009ebaad589 (diff) |
Patched in Tegra support.
Diffstat (limited to 'drivers/rtc/rtc-pl031.c')
-rw-r--r-- | drivers/rtc/rtc-pl031.c | 141 |
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 | */ | ||
80 | struct pl031_vendor_data { | ||
81 | struct rtc_class_ops ops; | ||
82 | bool clockwatch; | ||
83 | bool st_weekday; | ||
84 | unsigned long irqflags; | ||
85 | }; | ||
86 | |||
87 | struct pl031_local { | 71 | struct 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 | ||
93 | static int pl031_alarm_irq_enable(struct device *dev, | 78 | static 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 */ |
400 | static struct pl031_vendor_data arm_pl031 = { | 377 | static 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 */ |
412 | static struct pl031_vendor_data stv1_pl031 = { | 386 | static 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 */ |
426 | static struct pl031_vendor_data stv2_pl031 = { | 395 | static 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 | ||
443 | static struct amba_id pl031_ids[] = { | 403 | static 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 | ||
463 | MODULE_DEVICE_TABLE(amba, pl031_ids); | ||
464 | |||
465 | static struct amba_driver pl031_driver = { | 423 | static 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 | ||
474 | module_amba_driver(pl031_driver); | 432 | static int __init pl031_init(void) |
433 | { | ||
434 | return amba_driver_register(&pl031_driver); | ||
435 | } | ||
436 | |||
437 | static void __exit pl031_exit(void) | ||
438 | { | ||
439 | amba_driver_unregister(&pl031_driver); | ||
440 | } | ||
441 | |||
442 | module_init(pl031_init); | ||
443 | module_exit(pl031_exit); | ||
475 | 444 | ||
476 | MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net"); | 445 | MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net"); |
477 | MODULE_DESCRIPTION("ARM AMBA PL031 RTC Driver"); | 446 | MODULE_DESCRIPTION("ARM AMBA PL031 RTC Driver"); |