diff options
Diffstat (limited to 'drivers/rtc/rtc-at91sam9.c')
| -rw-r--r-- | drivers/rtc/rtc-at91sam9.c | 138 | 
1 files changed, 109 insertions, 29 deletions
| diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c index 596374304532..abac38abd38e 100644 --- a/drivers/rtc/rtc-at91sam9.c +++ b/drivers/rtc/rtc-at91sam9.c | |||
| @@ -21,10 +21,9 @@ | |||
| 21 | #include <linux/slab.h> | 21 | #include <linux/slab.h> | 
| 22 | #include <linux/platform_data/atmel.h> | 22 | #include <linux/platform_data/atmel.h> | 
| 23 | #include <linux/io.h> | 23 | #include <linux/io.h> | 
| 24 | 24 | #include <linux/mfd/syscon.h> | |
| 25 | #include <mach/at91_rtt.h> | 25 | #include <linux/regmap.h> | 
| 26 | #include <mach/cpu.h> | 26 | #include <linux/clk.h> | 
| 27 | #include <mach/hardware.h> | ||
| 28 | 27 | ||
| 29 | /* | 28 | /* | 
| 30 | * This driver uses two configurable hardware resources that live in the | 29 | * This driver uses two configurable hardware resources that live in the | 
| @@ -47,6 +46,22 @@ | |||
| 47 | * registers available, likewise usable for more than "RTC" support. | 46 | * registers available, likewise usable for more than "RTC" support. | 
| 48 | */ | 47 | */ | 
| 49 | 48 | ||
| 49 | #define AT91_RTT_MR 0x00 /* Real-time Mode Register */ | ||
| 50 | #define AT91_RTT_RTPRES (0xffff << 0) /* Real-time Timer Prescaler Value */ | ||
| 51 | #define AT91_RTT_ALMIEN (1 << 16) /* Alarm Interrupt Enable */ | ||
| 52 | #define AT91_RTT_RTTINCIEN (1 << 17) /* Real Time Timer Increment Interrupt Enable */ | ||
| 53 | #define AT91_RTT_RTTRST (1 << 18) /* Real Time Timer Restart */ | ||
| 54 | |||
| 55 | #define AT91_RTT_AR 0x04 /* Real-time Alarm Register */ | ||
| 56 | #define AT91_RTT_ALMV (0xffffffff) /* Alarm Value */ | ||
| 57 | |||
| 58 | #define AT91_RTT_VR 0x08 /* Real-time Value Register */ | ||
| 59 | #define AT91_RTT_CRTV (0xffffffff) /* Current Real-time Value */ | ||
| 60 | |||
| 61 | #define AT91_RTT_SR 0x0c /* Real-time Status Register */ | ||
| 62 | #define AT91_RTT_ALMS (1 << 0) /* Real-time Alarm Status */ | ||
| 63 | #define AT91_RTT_RTTINC (1 << 1) /* Real-time Timer Increment */ | ||
| 64 | |||
| 50 | /* | 65 | /* | 
| 51 | * We store ALARM_DISABLED in ALMV to record that no alarm is set. | 66 | * We store ALARM_DISABLED in ALMV to record that no alarm is set. | 
| 52 | * It's also the reset value for that field. | 67 | * It's also the reset value for that field. | 
| @@ -58,19 +73,30 @@ struct sam9_rtc { | |||
| 58 | void __iomem *rtt; | 73 | void __iomem *rtt; | 
| 59 | struct rtc_device *rtcdev; | 74 | struct rtc_device *rtcdev; | 
| 60 | u32 imr; | 75 | u32 imr; | 
| 61 | void __iomem *gpbr; | 76 | struct regmap *gpbr; | 
| 77 | unsigned int gpbr_offset; | ||
| 62 | int irq; | 78 | int irq; | 
| 79 | struct clk *sclk; | ||
| 63 | }; | 80 | }; | 
| 64 | 81 | ||
| 65 | #define rtt_readl(rtc, field) \ | 82 | #define rtt_readl(rtc, field) \ | 
| 66 | __raw_readl((rtc)->rtt + AT91_RTT_ ## field) | 83 | readl((rtc)->rtt + AT91_RTT_ ## field) | 
| 67 | #define rtt_writel(rtc, field, val) \ | 84 | #define rtt_writel(rtc, field, val) \ | 
| 68 | __raw_writel((val), (rtc)->rtt + AT91_RTT_ ## field) | 85 | writel((val), (rtc)->rtt + AT91_RTT_ ## field) | 
| 86 | |||
| 87 | static inline unsigned int gpbr_readl(struct sam9_rtc *rtc) | ||
| 88 | { | ||
| 89 | unsigned int val; | ||
| 90 | |||
| 91 | regmap_read(rtc->gpbr, rtc->gpbr_offset, &val); | ||
| 69 | 92 | ||
| 70 | #define gpbr_readl(rtc) \ | 93 | return val; | 
| 71 | __raw_readl((rtc)->gpbr) | 94 | } | 
| 72 | #define gpbr_writel(rtc, val) \ | 95 | |
| 73 | __raw_writel((val), (rtc)->gpbr) | 96 | static inline void gpbr_writel(struct sam9_rtc *rtc, unsigned int val) | 
| 97 | { | ||
| 98 | regmap_write(rtc->gpbr, rtc->gpbr_offset, val); | ||
| 99 | } | ||
| 74 | 100 | ||
| 75 | /* | 101 | /* | 
| 76 | * Read current time and date in RTC | 102 | * Read current time and date in RTC | 
| @@ -287,22 +313,22 @@ static const struct rtc_class_ops at91_rtc_ops = { | |||
| 287 | .alarm_irq_enable = at91_rtc_alarm_irq_enable, | 313 | .alarm_irq_enable = at91_rtc_alarm_irq_enable, | 
| 288 | }; | 314 | }; | 
| 289 | 315 | ||
| 316 | static struct regmap_config gpbr_regmap_config = { | ||
| 317 | .reg_bits = 32, | ||
| 318 | .val_bits = 32, | ||
| 319 | .reg_stride = 4, | ||
| 320 | }; | ||
| 321 | |||
| 290 | /* | 322 | /* | 
| 291 | * Initialize and install RTC driver | 323 | * Initialize and install RTC driver | 
| 292 | */ | 324 | */ | 
| 293 | static int at91_rtc_probe(struct platform_device *pdev) | 325 | static int at91_rtc_probe(struct platform_device *pdev) | 
| 294 | { | 326 | { | 
| 295 | struct resource *r, *r_gpbr; | 327 | struct resource *r; | 
| 296 | struct sam9_rtc *rtc; | 328 | struct sam9_rtc *rtc; | 
| 297 | int ret, irq; | 329 | int ret, irq; | 
| 298 | u32 mr; | 330 | u32 mr; | 
| 299 | 331 | unsigned int sclk_rate; | |
| 300 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 301 | r_gpbr = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
| 302 | if (!r || !r_gpbr) { | ||
| 303 | dev_err(&pdev->dev, "need 2 ressources\n"); | ||
| 304 | return -ENODEV; | ||
| 305 | } | ||
| 306 | 332 | ||
| 307 | irq = platform_get_irq(pdev, 0); | 333 | irq = platform_get_irq(pdev, 0); | 
| 308 | if (irq < 0) { | 334 | if (irq < 0) { | 
| @@ -321,24 +347,66 @@ static int at91_rtc_probe(struct platform_device *pdev) | |||
| 321 | device_init_wakeup(&pdev->dev, 1); | 347 | device_init_wakeup(&pdev->dev, 1); | 
| 322 | 348 | ||
| 323 | platform_set_drvdata(pdev, rtc); | 349 | platform_set_drvdata(pdev, rtc); | 
| 324 | rtc->rtt = devm_ioremap(&pdev->dev, r->start, resource_size(r)); | 350 | |
| 325 | if (!rtc->rtt) { | 351 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
| 326 | dev_err(&pdev->dev, "failed to map registers, aborting.\n"); | 352 | rtc->rtt = devm_ioremap_resource(&pdev->dev, r); | 
| 327 | return -ENOMEM; | 353 | if (IS_ERR(rtc->rtt)) | 
| 354 | return PTR_ERR(rtc->rtt); | ||
| 355 | |||
| 356 | if (!pdev->dev.of_node) { | ||
| 357 | /* | ||
| 358 | * TODO: Remove this code chunk when removing non DT board | ||
| 359 | * support. Remember to remove the gpbr_regmap_config | ||
| 360 | * variable too. | ||
| 361 | */ | ||
| 362 | void __iomem *gpbr; | ||
| 363 | |||
| 364 | r = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
| 365 | gpbr = devm_ioremap_resource(&pdev->dev, r); | ||
| 366 | if (IS_ERR(gpbr)) | ||
| 367 | return PTR_ERR(gpbr); | ||
| 368 | |||
| 369 | rtc->gpbr = regmap_init_mmio(NULL, gpbr, | ||
| 370 | &gpbr_regmap_config); | ||
| 371 | } else { | ||
| 372 | struct of_phandle_args args; | ||
| 373 | |||
| 374 | ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, | ||
| 375 | "atmel,rtt-rtc-time-reg", 1, 0, | ||
| 376 | &args); | ||
| 377 | if (ret) | ||
| 378 | return ret; | ||
| 379 | |||
| 380 | rtc->gpbr = syscon_node_to_regmap(args.np); | ||
| 381 | rtc->gpbr_offset = args.args[0]; | ||
| 328 | } | 382 | } | 
| 329 | 383 | ||
| 330 | rtc->gpbr = devm_ioremap(&pdev->dev, r_gpbr->start, | 384 | if (IS_ERR(rtc->gpbr)) { | 
| 331 | resource_size(r_gpbr)); | 385 | dev_err(&pdev->dev, "failed to retrieve gpbr regmap, aborting.\n"); | 
| 332 | if (!rtc->gpbr) { | ||
| 333 | dev_err(&pdev->dev, "failed to map gpbr registers, aborting.\n"); | ||
| 334 | return -ENOMEM; | 386 | return -ENOMEM; | 
| 335 | } | 387 | } | 
| 336 | 388 | ||
| 389 | rtc->sclk = devm_clk_get(&pdev->dev, NULL); | ||
| 390 | if (IS_ERR(rtc->sclk)) | ||
| 391 | return PTR_ERR(rtc->sclk); | ||
| 392 | |||
| 393 | sclk_rate = clk_get_rate(rtc->sclk); | ||
| 394 | if (!sclk_rate || sclk_rate > AT91_RTT_RTPRES) { | ||
| 395 | dev_err(&pdev->dev, "Invalid slow clock rate\n"); | ||
| 396 | return -EINVAL; | ||
| 397 | } | ||
| 398 | |||
| 399 | ret = clk_prepare_enable(rtc->sclk); | ||
| 400 | if (ret) { | ||
| 401 | dev_err(&pdev->dev, "Could not enable slow clock\n"); | ||
| 402 | return ret; | ||
| 403 | } | ||
| 404 | |||
| 337 | mr = rtt_readl(rtc, MR); | 405 | mr = rtt_readl(rtc, MR); | 
| 338 | 406 | ||
| 339 | /* unless RTT is counting at 1 Hz, re-initialize it */ | 407 | /* unless RTT is counting at 1 Hz, re-initialize it */ | 
| 340 | if ((mr & AT91_RTT_RTPRES) != AT91_SLOW_CLOCK) { | 408 | if ((mr & AT91_RTT_RTPRES) != sclk_rate) { | 
| 341 | mr = AT91_RTT_RTTRST | (AT91_SLOW_CLOCK & AT91_RTT_RTPRES); | 409 | mr = AT91_RTT_RTTRST | (sclk_rate & AT91_RTT_RTPRES); | 
| 342 | gpbr_writel(rtc, 0); | 410 | gpbr_writel(rtc, 0); | 
| 343 | } | 411 | } | 
| 344 | 412 | ||
| @@ -383,6 +451,9 @@ static int at91_rtc_remove(struct platform_device *pdev) | |||
| 383 | /* disable all interrupts */ | 451 | /* disable all interrupts */ | 
| 384 | rtt_writel(rtc, MR, mr & ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN)); | 452 | rtt_writel(rtc, MR, mr & ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN)); | 
| 385 | 453 | ||
| 454 | if (!IS_ERR(rtc->sclk)) | ||
| 455 | clk_disable_unprepare(rtc->sclk); | ||
| 456 | |||
| 386 | return 0; | 457 | return 0; | 
| 387 | } | 458 | } | 
| 388 | 459 | ||
| @@ -440,6 +511,14 @@ static int at91_rtc_resume(struct device *dev) | |||
| 440 | 511 | ||
| 441 | static SIMPLE_DEV_PM_OPS(at91_rtc_pm_ops, at91_rtc_suspend, at91_rtc_resume); | 512 | static SIMPLE_DEV_PM_OPS(at91_rtc_pm_ops, at91_rtc_suspend, at91_rtc_resume); | 
| 442 | 513 | ||
| 514 | #ifdef CONFIG_OF | ||
| 515 | static const struct of_device_id at91_rtc_dt_ids[] = { | ||
| 516 | { .compatible = "atmel,at91sam9260-rtt" }, | ||
| 517 | { /* sentinel */ } | ||
| 518 | }; | ||
| 519 | MODULE_DEVICE_TABLE(of, at91_rtc_dt_ids); | ||
| 520 | #endif | ||
| 521 | |||
| 443 | static struct platform_driver at91_rtc_driver = { | 522 | static struct platform_driver at91_rtc_driver = { | 
| 444 | .probe = at91_rtc_probe, | 523 | .probe = at91_rtc_probe, | 
| 445 | .remove = at91_rtc_remove, | 524 | .remove = at91_rtc_remove, | 
| @@ -448,6 +527,7 @@ static struct platform_driver at91_rtc_driver = { | |||
| 448 | .name = "rtc-at91sam9", | 527 | .name = "rtc-at91sam9", | 
| 449 | .owner = THIS_MODULE, | 528 | .owner = THIS_MODULE, | 
| 450 | .pm = &at91_rtc_pm_ops, | 529 | .pm = &at91_rtc_pm_ops, | 
| 530 | .of_match_table = of_match_ptr(at91_rtc_dt_ids), | ||
| 451 | }, | 531 | }, | 
| 452 | }; | 532 | }; | 
| 453 | 533 | ||
