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 | ||