From 034dd340b08be1f2f0477ad16131d609f9dbd53c Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 30 Aug 2026 09:22:00 -0700 Subject: Merge tag 'trace-v7.3-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace Pull tracing fixes from Steven Rostedt: - Fix error output of boot instance creation failure Currently if a boot instance creation fails, instead of printing out the name of the instance that failed, it prints "(null)". That is because it prints "cur_str" that had already been processed by strsep(). Print the saved name instead. While at it, print the error code of the failure. - Fix use-after-free for same named historgrams Histograms can be named so that they can be used in multiple events. But if the named histogram has a variable attached, the second event that uses the named histogram which duplicates it and needs to free the original after duplication leaves the old variable in place and still visible. If another histogram uses than variable, it will use the stale one which will try to reference the freed duplicate histogram and crash the kernel. Free the duplicate variables along with the duplicated histogram data. - Check return value of kthread_run() in event self test The events self tests uses a kthread for testing but does not check if it succeeded in creating a kthread. If the kthread creation were to fail, the code will still try to call kthread_stop() on the error returned. - Fix race between reading trace_pipe and updating subbuffer size If a user is reading the trace_pipe file at the same time they update the ring buffer sub-buffer size, can cause the trace_pipe read to read stale data. Add trace_access_lock() around updating the ring buffer sub-buffer size. - Fix eventfs_inode on failure path in creation of the events directory In the creation of the "events" directory, if after allocating the eventfs_inode a failure is detected, it calls cleanup_ei() which calls free_ei(). The free_ei() will test if eventfs_inode being freed has no children. It is a bug if it does. But on the failure case of the creation of the "events" directory, the children lists have not yet been initialized and the free will trigger a warning because list_empty() on an uninitialized list returns false. Move the initialization into init_ei() where it makes more sense and makes sure that a created eventfs_inode has its lists initialized upon creation. - Check return value of kthread_run() in ftrace direct sample code The sample code that shows how to use the ftrace direct calls does not test the return of kthread_run() to see if it succeeds. Return a failure if the kthread_run() doesn't succeed. - Clear user events state on fork in case of alloc failure On fork, the child gets a pointer to the parent's user events state. It makes a copy of it then updates the child's pointer to it. But if the allocation fails, the duplication function leaves the child with a pointer to its parent's descriptor. When the child cleans up its data, it will free the parent's descriptor while the parent is still using it. In the duplication function, set the child's user_event_mm to NULL before testing if the allocation succeeded, and when it exits it will not free the parent's descriptor. - Fix retry exhaustion in simple ring buffer reader swap simple_ring_buffer_swap_reader_page() starts with retry set to 8 and post-decrements it only after a failed link replacement. On the final attempt, a successful replacement leaves retry at zero, while a failed replacement leaves it at -1. But the check for success expects the retry value to be non-zero and exits with an error on zero. This is the opposite result. Fix it. - Fail nicely when the remote swap_reader_page() returns an error Currently, if the swap_reader_page() of a remote buffer fails, it triggers a WARN_ON_ONCE() and continues normally. Instead, have it exit with an error and a pr_warn() print instead of a full WARNING. * tag 'trace-v7.3-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: ring-buffer: Stop remote reader update when page swap fails tracing: Fix retry exhaustion in simple ring buffer reader swap tracing/user_events: Clear copied tracing state before fork duplication samples/ftrace: Fix kthread_stop() on ERR_PTR in ftrace-direct-multi-modify samples/ftrace: Fix kthread_stop() on ERR_PTR in ftrace-direct-modify eventfs: Initialize ei->children and ei->list in init_ei() tracing: Fix use-after-free in trace_pipe read on sub-buffer order change tracing: Fix crash passing ERR_PTR to kthread_stop() tracing: Fix use-after-free with same-name named triggers tracing: Fix logged instance name on creation failure --- drivers/rtc/Kconfig | 2177 ++++++++++++++++++++++++++++++++++ drivers/rtc/Makefile | 201 ++++ drivers/rtc/class.c | 491 ++++++++ drivers/rtc/dev.c | 576 +++++++++ drivers/rtc/interface.c | 1159 +++++++++++++++++++ drivers/rtc/lib.c | 211 ++++ drivers/rtc/nvmem.c | 31 + drivers/rtc/proc.c | 92 ++ drivers/rtc/rtc-88pm80x.c | 340 ++++++ drivers/rtc/rtc-88pm860x.c | 381 ++++++ drivers/rtc/rtc-88pm886.c | 96 ++ drivers/rtc/rtc-ab-b5ze-s3.c | 954 +++++++++++++++ drivers/rtc/rtc-ab-eoz9.c | 567 +++++++++ drivers/rtc/rtc-ab8500.c | 405 +++++++ drivers/rtc/rtc-abx80x.c | 1010 ++++++++++++++++ drivers/rtc/rtc-ac100.c | 642 +++++++++++ drivers/rtc/rtc-amlogic-a4.c | 443 +++++++ drivers/rtc/rtc-armada38x.c | 580 ++++++++++ drivers/rtc/rtc-as3722.c | 252 ++++ drivers/rtc/rtc-asm9260.c | 339 ++++++ drivers/rtc/rtc-aspeed.c | 130 +++ drivers/rtc/rtc-at91rm9200.c | 656 +++++++++++ drivers/rtc/rtc-at91sam9.c | 546 +++++++++ drivers/rtc/rtc-atcrtc100.c | 381 ++++++ drivers/rtc/rtc-au1xxx.c | 118 ++ drivers/rtc/rtc-bd70528.c | 364 ++++++ drivers/rtc/rtc-bq32k.c | 341 ++++++ drivers/rtc/rtc-bq4802.c | 202 ++++ drivers/rtc/rtc-brcmstb-waketimer.c | 433 +++++++ drivers/rtc/rtc-cadence.c | 412 +++++++ drivers/rtc/rtc-cmos.c | 1517 ++++++++++++++++++++++++ drivers/rtc/rtc-core.h | 43 + drivers/rtc/rtc-cpcap.c | 324 ++++++ drivers/rtc/rtc-cros-ec.c | 409 +++++++ drivers/rtc/rtc-cv1800.c | 218 ++++ drivers/rtc/rtc-da9052.c | 333 ++++++ drivers/rtc/rtc-da9055.c | 399 +++++++ drivers/rtc/rtc-da9063.c | 497 ++++++++ drivers/rtc/rtc-digicolor.c | 224 ++++ drivers/rtc/rtc-ds1216.c | 174 +++ drivers/rtc/rtc-ds1286.c | 358 ++++++ drivers/rtc/rtc-ds1302.c | 213 ++++ drivers/rtc/rtc-ds1305.c | 747 ++++++++++++ drivers/rtc/rtc-ds1307.c | 2178 +++++++++++++++++++++++++++++++++++ drivers/rtc/rtc-ds1343.c | 477 ++++++++ drivers/rtc/rtc-ds1347.c | 183 +++ drivers/rtc/rtc-ds1374.c | 584 ++++++++++ drivers/rtc/rtc-ds1390.c | 242 ++++ drivers/rtc/rtc-ds1511.c | 368 ++++++ drivers/rtc/rtc-ds1553.c | 331 ++++++ drivers/rtc/rtc-ds1672.c | 160 +++ drivers/rtc/rtc-ds1685.c | 1440 +++++++++++++++++++++++ drivers/rtc/rtc-ds1742.c | 220 ++++ drivers/rtc/rtc-ds2404.c | 225 ++++ drivers/rtc/rtc-ds3232.c | 745 ++++++++++++ drivers/rtc/rtc-efi.c | 224 ++++ drivers/rtc/rtc-em3027.c | 158 +++ drivers/rtc/rtc-ep93xx.c | 170 +++ drivers/rtc/rtc-fm3130.c | 529 +++++++++ drivers/rtc/rtc-fsl-ftm-alarm.c | 330 ++++++ drivers/rtc/rtc-ftrtc010.c | 218 ++++ drivers/rtc/rtc-gamecube.c | 380 ++++++ drivers/rtc/rtc-generic.c | 39 + drivers/rtc/rtc-goldfish.c | 207 ++++ drivers/rtc/rtc-hid-sensor-time.c | 329 ++++++ drivers/rtc/rtc-hym8563.c | 592 ++++++++++ drivers/rtc/rtc-imx-sc.c | 184 +++ drivers/rtc/rtc-imx-sm-bbm.c | 162 +++ drivers/rtc/rtc-imxdi.c | 871 ++++++++++++++ drivers/rtc/rtc-isl12022.c | 625 ++++++++++ drivers/rtc/rtc-isl12026.c | 506 ++++++++ drivers/rtc/rtc-isl1208.c | 1001 ++++++++++++++++ drivers/rtc/rtc-jz4740.c | 439 +++++++ drivers/rtc/rtc-loongson.c | 431 +++++++ drivers/rtc/rtc-lp8788.c | 322 ++++++ drivers/rtc/rtc-lpc24xx.c | 297 +++++ drivers/rtc/rtc-lpc32xx.c | 360 ++++++ drivers/rtc/rtc-m41t80.c | 1044 +++++++++++++++++ drivers/rtc/rtc-m41t93.c | 590 ++++++++++ drivers/rtc/rtc-m41t94.c | 145 +++ drivers/rtc/rtc-m48t35.c | 193 ++++ drivers/rtc/rtc-m48t59.c | 472 ++++++++ drivers/rtc/rtc-m48t86.c | 291 +++++ drivers/rtc/rtc-ma35d1.c | 304 +++++ drivers/rtc/rtc-macsmc.c | 140 +++ drivers/rtc/rtc-max31335.c | 776 +++++++++++++ drivers/rtc/rtc-max6900.c | 235 ++++ drivers/rtc/rtc-max6902.c | 154 +++ drivers/rtc/rtc-max6916.c | 160 +++ drivers/rtc/rtc-max77686.c | 891 ++++++++++++++ drivers/rtc/rtc-max8907.c | 220 ++++ drivers/rtc/rtc-max8925.c | 322 ++++++ drivers/rtc/rtc-max8997.c | 533 +++++++++ drivers/rtc/rtc-max8998.c | 321 ++++++ drivers/rtc/rtc-mc13xxx.c | 349 ++++++ drivers/rtc/rtc-mc146818-lib.c | 309 +++++ drivers/rtc/rtc-mcp795.c | 452 ++++++++ drivers/rtc/rtc-meson-vrtc.c | 150 +++ drivers/rtc/rtc-meson.c | 404 +++++++ drivers/rtc/rtc-moxart.c | 307 +++++ drivers/rtc/rtc-mpc5121.c | 408 +++++++ drivers/rtc/rtc-mpfs.c | 297 +++++ drivers/rtc/rtc-msc313.c | 248 ++++ drivers/rtc/rtc-msm6242.c | 228 ++++ drivers/rtc/rtc-mt2712.c | 413 +++++++ drivers/rtc/rtc-mt6397.c | 354 ++++++ drivers/rtc/rtc-mt7622.c | 408 +++++++ drivers/rtc/rtc-mv.c | 321 ++++++ drivers/rtc/rtc-mxc.c | 404 +++++++ drivers/rtc/rtc-mxc_v2.c | 390 +++++++ drivers/rtc/rtc-nct3018y.c | 600 ++++++++++ drivers/rtc/rtc-nct6694.c | 297 +++++ drivers/rtc/rtc-ntxec.c | 145 +++ drivers/rtc/rtc-nvidia-vrs10.c | 542 +++++++++ drivers/rtc/rtc-nxp-bbnsm.c | 231 ++++ drivers/rtc/rtc-omap.c | 1031 +++++++++++++++++ drivers/rtc/rtc-opal.c | 301 +++++ drivers/rtc/rtc-optee.c | 735 ++++++++++++ drivers/rtc/rtc-palmas.c | 362 ++++++ drivers/rtc/rtc-pcf2123.c | 481 ++++++++ drivers/rtc/rtc-pcf2127.c | 1631 ++++++++++++++++++++++++++ drivers/rtc/rtc-pcf85063.c | 832 +++++++++++++ drivers/rtc/rtc-pcf8523.c | 523 +++++++++ drivers/rtc/rtc-pcf85363.c | 503 ++++++++ drivers/rtc/rtc-pcf8563.c | 593 ++++++++++ drivers/rtc/rtc-pcf8583.c | 317 +++++ drivers/rtc/rtc-pic32.c | 381 ++++++ drivers/rtc/rtc-pl030.c | 174 +++ drivers/rtc/rtc-pl031.c | 470 ++++++++ drivers/rtc/rtc-pm8xxx.c | 689 +++++++++++ drivers/rtc/rtc-ps3.c | 73 ++ drivers/rtc/rtc-pxa.c | 425 +++++++ drivers/rtc/rtc-r7301.c | 479 ++++++++ drivers/rtc/rtc-r9701.c | 145 +++ drivers/rtc/rtc-rc5t583.c | 312 +++++ drivers/rtc/rtc-rc5t619.c | 451 ++++++++ drivers/rtc/rtc-renesas-rtca3.c | 900 +++++++++++++++ drivers/rtc/rtc-rk808.c | 460 ++++++++ drivers/rtc/rtc-rp5c01.c | 276 +++++ drivers/rtc/rtc-rs5c313.c | 392 +++++++ drivers/rtc/rtc-rs5c348.c | 215 ++++ drivers/rtc/rtc-rs5c372.c | 930 +++++++++++++++ drivers/rtc/rtc-rtd119x.c | 237 ++++ drivers/rtc/rtc-rv3028.c | 1063 +++++++++++++++++ drivers/rtc/rtc-rv3029c2.c | 924 +++++++++++++++ drivers/rtc/rtc-rv3032.c | 1004 ++++++++++++++++ drivers/rtc/rtc-rv8803.c | 772 +++++++++++++ drivers/rtc/rtc-rx4581.c | 292 +++++ drivers/rtc/rtc-rx6110.c | 511 ++++++++ drivers/rtc/rtc-rx8010.c | 434 +++++++ drivers/rtc/rtc-rx8025.c | 588 ++++++++++ drivers/rtc/rtc-rx8111.c | 368 ++++++ drivers/rtc/rtc-rx8581.c | 322 ++++++ drivers/rtc/rtc-rzn1.c | 552 +++++++++ drivers/rtc/rtc-s32g.c | 386 +++++++ drivers/rtc/rtc-s35390a.c | 541 +++++++++ drivers/rtc/rtc-s3c.c | 566 +++++++++ drivers/rtc/rtc-s3c.h | 48 + drivers/rtc/rtc-s5m.c | 833 ++++++++++++++ drivers/rtc/rtc-sa1100.c | 354 ++++++ drivers/rtc/rtc-sa1100.h | 24 + drivers/rtc/rtc-sc27xx.c | 647 +++++++++++ drivers/rtc/rtc-sd2405al.c | 229 ++++ drivers/rtc/rtc-sd3078.c | 213 ++++ drivers/rtc/rtc-sh.c | 506 ++++++++ drivers/rtc/rtc-snvs.c | 446 +++++++ drivers/rtc/rtc-spacemit-p1.c | 168 +++ drivers/rtc/rtc-spear.c | 492 ++++++++ drivers/rtc/rtc-ssd202d.c | 248 ++++ drivers/rtc/rtc-st-lpc.c | 311 +++++ drivers/rtc/rtc-starfire.c | 60 + drivers/rtc/rtc-stk17ta8.c | 337 ++++++ drivers/rtc/rtc-stm32.c | 1288 +++++++++++++++++++++ drivers/rtc/rtc-stmp3xxx.c | 419 +++++++ drivers/rtc/rtc-sun4v.c | 98 ++ drivers/rtc/rtc-sun6i.c | 883 ++++++++++++++ drivers/rtc/rtc-sunplus.c | 359 ++++++ drivers/rtc/rtc-sunxi.c | 486 ++++++++ drivers/rtc/rtc-tegra.c | 402 +++++++ drivers/rtc/rtc-test.c | 201 ++++ drivers/rtc/rtc-ti-k3.c | 670 +++++++++++ drivers/rtc/rtc-tps6586x.c | 327 ++++++ drivers/rtc/rtc-tps65910.c | 470 ++++++++ drivers/rtc/rtc-tps6594.c | 504 ++++++++ drivers/rtc/rtc-twl.c | 689 +++++++++++ drivers/rtc/rtc-vt8500.c | 266 +++++ drivers/rtc/rtc-wilco-ec.c | 194 ++++ drivers/rtc/rtc-wm831x.c | 475 ++++++++ drivers/rtc/rtc-wm8350.c | 474 ++++++++ drivers/rtc/rtc-x1205.c | 693 +++++++++++ drivers/rtc/rtc-xgene.c | 278 +++++ drivers/rtc/rtc-zynqmp.c | 422 +++++++ drivers/rtc/sysfs.c | 351 ++++++ drivers/rtc/test_rtc_lib.c | 106 ++ 194 files changed, 87173 insertions(+) create mode 100644 drivers/rtc/Kconfig create mode 100644 drivers/rtc/Makefile create mode 100644 drivers/rtc/class.c create mode 100644 drivers/rtc/dev.c create mode 100644 drivers/rtc/interface.c create mode 100644 drivers/rtc/lib.c create mode 100644 drivers/rtc/nvmem.c create mode 100644 drivers/rtc/proc.c create mode 100644 drivers/rtc/rtc-88pm80x.c create mode 100644 drivers/rtc/rtc-88pm860x.c create mode 100644 drivers/rtc/rtc-88pm886.c create mode 100644 drivers/rtc/rtc-ab-b5ze-s3.c create mode 100644 drivers/rtc/rtc-ab-eoz9.c create mode 100644 drivers/rtc/rtc-ab8500.c create mode 100644 drivers/rtc/rtc-abx80x.c create mode 100644 drivers/rtc/rtc-ac100.c create mode 100644 drivers/rtc/rtc-amlogic-a4.c create mode 100644 drivers/rtc/rtc-armada38x.c create mode 100644 drivers/rtc/rtc-as3722.c create mode 100644 drivers/rtc/rtc-asm9260.c create mode 100644 drivers/rtc/rtc-aspeed.c create mode 100644 drivers/rtc/rtc-at91rm9200.c create mode 100644 drivers/rtc/rtc-at91sam9.c create mode 100644 drivers/rtc/rtc-atcrtc100.c create mode 100644 drivers/rtc/rtc-au1xxx.c create mode 100644 drivers/rtc/rtc-bd70528.c create mode 100644 drivers/rtc/rtc-bq32k.c create mode 100644 drivers/rtc/rtc-bq4802.c create mode 100644 drivers/rtc/rtc-brcmstb-waketimer.c create mode 100644 drivers/rtc/rtc-cadence.c create mode 100644 drivers/rtc/rtc-cmos.c create mode 100644 drivers/rtc/rtc-core.h create mode 100644 drivers/rtc/rtc-cpcap.c create mode 100644 drivers/rtc/rtc-cros-ec.c create mode 100644 drivers/rtc/rtc-cv1800.c create mode 100644 drivers/rtc/rtc-da9052.c create mode 100644 drivers/rtc/rtc-da9055.c create mode 100644 drivers/rtc/rtc-da9063.c create mode 100644 drivers/rtc/rtc-digicolor.c create mode 100644 drivers/rtc/rtc-ds1216.c create mode 100644 drivers/rtc/rtc-ds1286.c create mode 100644 drivers/rtc/rtc-ds1302.c create mode 100644 drivers/rtc/rtc-ds1305.c create mode 100644 drivers/rtc/rtc-ds1307.c create mode 100644 drivers/rtc/rtc-ds1343.c create mode 100644 drivers/rtc/rtc-ds1347.c create mode 100644 drivers/rtc/rtc-ds1374.c create mode 100644 drivers/rtc/rtc-ds1390.c create mode 100644 drivers/rtc/rtc-ds1511.c create mode 100644 drivers/rtc/rtc-ds1553.c create mode 100644 drivers/rtc/rtc-ds1672.c create mode 100644 drivers/rtc/rtc-ds1685.c create mode 100644 drivers/rtc/rtc-ds1742.c create mode 100644 drivers/rtc/rtc-ds2404.c create mode 100644 drivers/rtc/rtc-ds3232.c create mode 100644 drivers/rtc/rtc-efi.c create mode 100644 drivers/rtc/rtc-em3027.c create mode 100644 drivers/rtc/rtc-ep93xx.c create mode 100644 drivers/rtc/rtc-fm3130.c create mode 100644 drivers/rtc/rtc-fsl-ftm-alarm.c create mode 100644 drivers/rtc/rtc-ftrtc010.c create mode 100644 drivers/rtc/rtc-gamecube.c create mode 100644 drivers/rtc/rtc-generic.c create mode 100644 drivers/rtc/rtc-goldfish.c create mode 100644 drivers/rtc/rtc-hid-sensor-time.c create mode 100644 drivers/rtc/rtc-hym8563.c create mode 100644 drivers/rtc/rtc-imx-sc.c create mode 100644 drivers/rtc/rtc-imx-sm-bbm.c create mode 100644 drivers/rtc/rtc-imxdi.c create mode 100644 drivers/rtc/rtc-isl12022.c create mode 100644 drivers/rtc/rtc-isl12026.c create mode 100644 drivers/rtc/rtc-isl1208.c create mode 100644 drivers/rtc/rtc-jz4740.c create mode 100644 drivers/rtc/rtc-loongson.c create mode 100644 drivers/rtc/rtc-lp8788.c create mode 100644 drivers/rtc/rtc-lpc24xx.c create mode 100644 drivers/rtc/rtc-lpc32xx.c create mode 100644 drivers/rtc/rtc-m41t80.c create mode 100644 drivers/rtc/rtc-m41t93.c create mode 100644 drivers/rtc/rtc-m41t94.c create mode 100644 drivers/rtc/rtc-m48t35.c create mode 100644 drivers/rtc/rtc-m48t59.c create mode 100644 drivers/rtc/rtc-m48t86.c create mode 100644 drivers/rtc/rtc-ma35d1.c create mode 100644 drivers/rtc/rtc-macsmc.c create mode 100644 drivers/rtc/rtc-max31335.c create mode 100644 drivers/rtc/rtc-max6900.c create mode 100644 drivers/rtc/rtc-max6902.c create mode 100644 drivers/rtc/rtc-max6916.c create mode 100644 drivers/rtc/rtc-max77686.c create mode 100644 drivers/rtc/rtc-max8907.c create mode 100644 drivers/rtc/rtc-max8925.c create mode 100644 drivers/rtc/rtc-max8997.c create mode 100644 drivers/rtc/rtc-max8998.c create mode 100644 drivers/rtc/rtc-mc13xxx.c create mode 100644 drivers/rtc/rtc-mc146818-lib.c create mode 100644 drivers/rtc/rtc-mcp795.c create mode 100644 drivers/rtc/rtc-meson-vrtc.c create mode 100644 drivers/rtc/rtc-meson.c create mode 100644 drivers/rtc/rtc-moxart.c create mode 100644 drivers/rtc/rtc-mpc5121.c create mode 100644 drivers/rtc/rtc-mpfs.c create mode 100644 drivers/rtc/rtc-msc313.c create mode 100644 drivers/rtc/rtc-msm6242.c create mode 100644 drivers/rtc/rtc-mt2712.c create mode 100644 drivers/rtc/rtc-mt6397.c create mode 100644 drivers/rtc/rtc-mt7622.c create mode 100644 drivers/rtc/rtc-mv.c create mode 100644 drivers/rtc/rtc-mxc.c create mode 100644 drivers/rtc/rtc-mxc_v2.c create mode 100644 drivers/rtc/rtc-nct3018y.c create mode 100644 drivers/rtc/rtc-nct6694.c create mode 100644 drivers/rtc/rtc-ntxec.c create mode 100644 drivers/rtc/rtc-nvidia-vrs10.c create mode 100644 drivers/rtc/rtc-nxp-bbnsm.c create mode 100644 drivers/rtc/rtc-omap.c create mode 100644 drivers/rtc/rtc-opal.c create mode 100644 drivers/rtc/rtc-optee.c create mode 100644 drivers/rtc/rtc-palmas.c create mode 100644 drivers/rtc/rtc-pcf2123.c create mode 100644 drivers/rtc/rtc-pcf2127.c create mode 100644 drivers/rtc/rtc-pcf85063.c create mode 100644 drivers/rtc/rtc-pcf8523.c create mode 100644 drivers/rtc/rtc-pcf85363.c create mode 100644 drivers/rtc/rtc-pcf8563.c create mode 100644 drivers/rtc/rtc-pcf8583.c create mode 100644 drivers/rtc/rtc-pic32.c create mode 100644 drivers/rtc/rtc-pl030.c create mode 100644 drivers/rtc/rtc-pl031.c create mode 100644 drivers/rtc/rtc-pm8xxx.c create mode 100644 drivers/rtc/rtc-ps3.c create mode 100644 drivers/rtc/rtc-pxa.c create mode 100644 drivers/rtc/rtc-r7301.c create mode 100644 drivers/rtc/rtc-r9701.c create mode 100644 drivers/rtc/rtc-rc5t583.c create mode 100644 drivers/rtc/rtc-rc5t619.c create mode 100644 drivers/rtc/rtc-renesas-rtca3.c create mode 100644 drivers/rtc/rtc-rk808.c create mode 100644 drivers/rtc/rtc-rp5c01.c create mode 100644 drivers/rtc/rtc-rs5c313.c create mode 100644 drivers/rtc/rtc-rs5c348.c create mode 100644 drivers/rtc/rtc-rs5c372.c create mode 100644 drivers/rtc/rtc-rtd119x.c create mode 100644 drivers/rtc/rtc-rv3028.c create mode 100644 drivers/rtc/rtc-rv3029c2.c create mode 100644 drivers/rtc/rtc-rv3032.c create mode 100644 drivers/rtc/rtc-rv8803.c create mode 100644 drivers/rtc/rtc-rx4581.c create mode 100644 drivers/rtc/rtc-rx6110.c create mode 100644 drivers/rtc/rtc-rx8010.c create mode 100644 drivers/rtc/rtc-rx8025.c create mode 100644 drivers/rtc/rtc-rx8111.c create mode 100644 drivers/rtc/rtc-rx8581.c create mode 100644 drivers/rtc/rtc-rzn1.c create mode 100644 drivers/rtc/rtc-s32g.c create mode 100644 drivers/rtc/rtc-s35390a.c create mode 100644 drivers/rtc/rtc-s3c.c create mode 100644 drivers/rtc/rtc-s3c.h create mode 100644 drivers/rtc/rtc-s5m.c create mode 100644 drivers/rtc/rtc-sa1100.c create mode 100644 drivers/rtc/rtc-sa1100.h create mode 100644 drivers/rtc/rtc-sc27xx.c create mode 100644 drivers/rtc/rtc-sd2405al.c create mode 100644 drivers/rtc/rtc-sd3078.c create mode 100644 drivers/rtc/rtc-sh.c create mode 100644 drivers/rtc/rtc-snvs.c create mode 100644 drivers/rtc/rtc-spacemit-p1.c create mode 100644 drivers/rtc/rtc-spear.c create mode 100644 drivers/rtc/rtc-ssd202d.c create mode 100644 drivers/rtc/rtc-st-lpc.c create mode 100644 drivers/rtc/rtc-starfire.c create mode 100644 drivers/rtc/rtc-stk17ta8.c create mode 100644 drivers/rtc/rtc-stm32.c create mode 100644 drivers/rtc/rtc-stmp3xxx.c create mode 100644 drivers/rtc/rtc-sun4v.c create mode 100644 drivers/rtc/rtc-sun6i.c create mode 100644 drivers/rtc/rtc-sunplus.c create mode 100644 drivers/rtc/rtc-sunxi.c create mode 100644 drivers/rtc/rtc-tegra.c create mode 100644 drivers/rtc/rtc-test.c create mode 100644 drivers/rtc/rtc-ti-k3.c create mode 100644 drivers/rtc/rtc-tps6586x.c create mode 100644 drivers/rtc/rtc-tps65910.c create mode 100644 drivers/rtc/rtc-tps6594.c create mode 100644 drivers/rtc/rtc-twl.c create mode 100644 drivers/rtc/rtc-vt8500.c create mode 100644 drivers/rtc/rtc-wilco-ec.c create mode 100644 drivers/rtc/rtc-wm831x.c create mode 100644 drivers/rtc/rtc-wm8350.c create mode 100644 drivers/rtc/rtc-x1205.c create mode 100644 drivers/rtc/rtc-xgene.c create mode 100644 drivers/rtc/rtc-zynqmp.c create mode 100644 drivers/rtc/sysfs.c create mode 100644 drivers/rtc/test_rtc_lib.c (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig new file mode 100644 index 000000000..05b9233b9 --- /dev/null +++ b/drivers/rtc/Kconfig @@ -0,0 +1,2177 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# RTC class/drivers configuration +# + +config RTC_LIB + bool + +config RTC_MC146818_LIB + bool + select RTC_LIB + +menuconfig RTC_CLASS + bool "Real Time Clock" + default n + depends on !S390 + select RTC_LIB + help + Generic RTC class support. If you say yes here, you will + be allowed to plug one or more RTCs to your system. You will + probably want to enable one or more of the interfaces below. + +if RTC_CLASS + +config RTC_HCTOSYS + bool "Set system time from RTC on startup and resume" + default y + help + If you say yes here, the system time (wall clock) will be set using + the value read from a specified RTC device. This is useful to avoid + unnecessary fsck runs at boot time, and to network better. + +config RTC_HCTOSYS_DEVICE + string "RTC used to set the system time" + depends on RTC_HCTOSYS + default "rtc0" + help + The RTC device that will be used to (re)initialize the system + clock, usually rtc0. Initialization is done when the system + starts up, and when it resumes from a low power state. This + device should record time in UTC, since the kernel won't do + timezone correction. + + This clock should be battery-backed, so that it reads the correct + time when the system boots from a power-off state. Otherwise, your + system will need an external clock source (like an NTP server). + + If the clock you specify here is not battery backed, it may still + be useful to reinitialize system time when resuming from system + sleep states. Do not specify an RTC here unless it stays powered + during all this system's supported sleep states. + +config RTC_SYSTOHC + bool "Set the RTC time based on NTP synchronization" + default y + help + If you say yes here, the system time (wall clock) will be stored + in the RTC specified by RTC_HCTOSYS_DEVICE approximately every 11 + minutes if userspace reports synchronized NTP status. + +config RTC_SYSTOHC_DEVICE + string "RTC used to synchronize NTP adjustment" + depends on RTC_SYSTOHC + default RTC_HCTOSYS_DEVICE if RTC_HCTOSYS + default "rtc0" + help + The RTC device used for NTP synchronization. The main difference + between RTC_HCTOSYS_DEVICE and RTC_SYSTOHC_DEVICE is that this + one can sleep when setting time, because it runs in the workqueue + context. + +config RTC_DEBUG + bool "RTC debug support" + help + Say yes here to enable debugging support in the RTC framework + and individual RTC drivers. + +config RTC_LIB_KUNIT_TEST + tristate "KUnit test for RTC lib functions" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS + help + Enable this option to test RTC library functions. + + If unsure, say N. + +config RTC_NVMEM + bool "RTC non volatile storage support" + select NVMEM + default RTC_CLASS + help + Say yes here to add support for the non volatile (often battery + backed) storage present on RTCs. + +comment "RTC interfaces" + +config RTC_INTF_SYSFS + bool "/sys/class/rtc/rtcN (sysfs)" + depends on SYSFS + default RTC_CLASS + help + Say yes here if you want to use your RTCs using sysfs interfaces, + /sys/class/rtc/rtc0 through /sys/.../rtcN. + + If unsure, say Y. + +config RTC_INTF_PROC + bool "/proc/driver/rtc (procfs for rtcN)" + depends on PROC_FS + default RTC_CLASS + help + Say yes here if you want to use your system clock RTC through + the proc interface, /proc/driver/rtc. + Other RTCs will not be available through that API. + If there is no RTC for the system clock, then the first RTC(rtc0) + is used by default. + + If unsure, say Y. + +config RTC_INTF_DEV + bool "/dev/rtcN (character devices)" + default RTC_CLASS + help + Say yes here if you want to use your RTCs using the /dev + interfaces, which "udev" sets up as /dev/rtc0 through + /dev/rtcN. + + You may want to set up a symbolic link so one of these + can be accessed as /dev/rtc, which is a name + expected by "hwclock" and some other programs. Recent + versions of "udev" are known to set up the symlink for you. + + If unsure, say Y. + +config RTC_INTF_DEV_UIE_EMUL + bool "RTC UIE emulation on dev interface" + depends on RTC_INTF_DEV + help + Provides an emulation for RTC_UIE if the underlying rtc chip + driver does not expose RTC_UIE ioctls. Those requests generate + once-per-second update interrupts, used for synchronization. + + The emulation code will read the time from the hardware + clock several times per second, please enable this option + only if you know that you really need it. + +config RTC_DRV_TEST + tristate "Test driver/device" + help + If you say yes here you get support for the + RTC test driver. It's a software RTC which can be + used to test the RTC subsystem APIs. It gets + the time from the system clock. + You want this driver only if you are doing development + on the RTC subsystem. Please read the source code + for further details. + + This driver can also be built as a module. If so, the module + will be called rtc-test. + +comment "I2C RTC drivers" + +if I2C + +config RTC_DRV_88PM860X + tristate "Marvell 88PM860x" + depends on MFD_88PM860X + help + If you say yes here you get support for RTC function in Marvell + 88PM860x chips. + + This driver can also be built as a module. If so, the module + will be called rtc-88pm860x. + +config RTC_DRV_88PM80X + tristate "Marvell 88PM80x" + depends on MFD_88PM800 + help + If you say yes here you get support for RTC function in Marvell + 88PM80x chips. + + This driver can also be built as a module. If so, the module + will be called rtc-88pm80x. + +config RTC_DRV_88PM886 + tristate "Marvell 88PM886 RTC driver" + depends on MFD_88PM886_PMIC + help + If you say yes here you will get support for the RTC function in the + Marvell 88PM886 chip. + + This driver can also be built as a module. If so, the module + will be called rtc-88pm886. + +config RTC_DRV_ABB5ZES3 + select REGMAP_I2C + tristate "Abracon AB-RTCMC-32.768kHz-B5ZE-S3" + help + If you say yes here you get support for the Abracon + AB-RTCMC-32.768kHz-B5ZE-S3 I2C RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-ab-b5ze-s3. + +config RTC_DRV_ABEOZ9 + select REGMAP_I2C + tristate "Abracon AB-RTCMC-32.768kHz-EOZ9" + help + If you say yes here you get support for the Abracon + AB-RTCMC-32.768kHz-EOA9 I2C RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-ab-e0z9. + +config RTC_DRV_ABX80X + tristate "Abracon ABx80x" + select WATCHDOG_CORE if WATCHDOG + help + If you say yes here you get support for Abracon AB080X and AB180X + families of ultra-low-power battery- and capacitor-backed real-time + clock chips. + + This driver can also be built as a module. If so, the module + will be called rtc-abx80x. + +config RTC_DRV_AC100 + tristate "X-Powers AC100" + depends on MFD_AC100 + help + If you say yes here you get support for the real-time clock found + in X-Powers AC100 family peripheral ICs. + + This driver can also be built as a module. If so, the module + will be called rtc-ac100. + +config RTC_DRV_BRCMSTB + tristate "Broadcom STB wake-timer" + depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST + default ARCH_BRCMSTB || BMIPS_GENERIC + help + If you say yes here you get support for the wake-timer found on + Broadcom STB SoCs (BCM7xxx). + + This driver can also be built as a module. If so, the module will + be called rtc-brcmstb-waketimer. + +config RTC_DRV_AS3722 + tristate "ams AS3722 RTC driver" + depends on MFD_AS3722 + help + If you say yes here you get support for the RTC of ams AS3722 PMIC + chips. + + This driver can also be built as a module. If so, the module + will be called rtc-as3722. + +config RTC_DRV_DS1307 + tristate "Dallas/Maxim DS1307/37/38/39/40/41, ST M41T00, EPSON RX-8025, ISL12057" + select REGMAP_I2C + select WATCHDOG_CORE if WATCHDOG + help + If you say yes here you get support for various compatible RTC + chips (often with battery backup) connected with I2C. This driver + should handle DS1307, DS1337, DS1338, DS1339, DS1340, DS1341, + ST M41T00, EPSON RX-8025, Intersil ISL12057 and probably other chips. + In some cases the RTC must already have been initialized (by + manufacturing or a bootloader). + + The first seven registers on these chips hold an RTC, and other + registers may add features such as NVRAM, a trickle charger for + the RTC/NVRAM backup power, and alarms. NVRAM is visible in + sysfs, but other chip features may not be available. + + This driver can also be built as a module. If so, the module + will be called rtc-ds1307. + +config RTC_DRV_DS1307_CENTURY + bool "Century bit support for rtc-ds1307" + depends on RTC_DRV_DS1307 + default n + help + The DS1307 driver suffered from a bug where it was enabling the + century bit inconditionnally but never used it when reading the time. + It made the driver unable to support dates beyond 2099. + Setting this option will add proper support for the century bit but if + the time was previously set using a kernel predating this option, + reading the date will return a date in the next century. + To solve that, you could boot a kernel without this option set, set + the RTC date and then boot a kernel with this option set. + +config RTC_DRV_DS1374 + tristate "Dallas/Maxim DS1374" + help + If you say yes here you get support for Dallas Semiconductor + DS1374 real-time clock chips. If an interrupt is associated + with the device, the alarm functionality is supported. + + This driver can also be built as a module. If so, the module + will be called rtc-ds1374. + +config RTC_DRV_DS1374_WDT + bool "Dallas/Maxim DS1374 watchdog timer" + depends on RTC_DRV_DS1374 && WATCHDOG + select WATCHDOG_CORE + help + If you say Y here you will get support for the + watchdog timer in the Dallas Semiconductor DS1374 + real-time clock chips. + +config RTC_DRV_DS1672 + tristate "Dallas/Maxim DS1672" + help + If you say yes here you get support for the + Dallas/Maxim DS1672 timekeeping chip. + + This driver can also be built as a module. If so, the module + will be called rtc-ds1672. + +config RTC_DRV_HYM8563 + tristate "Haoyu Microelectronics HYM8563" + depends on OF + help + Say Y to enable support for the HYM8563 I2C RTC chip. Apart + from the usual rtc functions it provides a clock output of + up to 32kHz. + + This driver can also be built as a module. If so, the module + will be called rtc-hym8563. + +config RTC_DRV_LP8788 + tristate "TI LP8788 RTC driver" + depends on MFD_LP8788 + help + Say Y to enable support for the LP8788 RTC/ALARM driver. + +config RTC_DRV_MAX6900 + tristate "Maxim MAX6900" + help + If you say yes here you will get support for the + Maxim MAX6900 I2C RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-max6900. + +config RTC_DRV_MAX8907 + tristate "Maxim MAX8907" + depends on MFD_MAX8907 || COMPILE_TEST + select REGMAP_IRQ + help + If you say yes here you will get support for the + RTC of Maxim MAX8907 PMIC. + + This driver can also be built as a module. If so, the module + will be called rtc-max8907. + +config RTC_DRV_MAX8925 + tristate "Maxim MAX8925" + depends on MFD_MAX8925 + help + If you say yes here you will get support for the + RTC of Maxim MAX8925 PMIC. + + This driver can also be built as a module. If so, the module + will be called rtc-max8925. + +config RTC_DRV_MAX8998 + tristate "Maxim MAX8998" + depends on MFD_MAX8998 + help + If you say yes here you will get support for the + RTC of Maxim MAX8998 PMIC. + + This driver can also be built as a module. If so, the module + will be called rtc-max8998. + +config RTC_DRV_MAX8997 + tristate "Maxim MAX8997" + depends on MFD_MAX8997 + help + If you say yes here you will get support for the + RTC of Maxim MAX8997 PMIC. + + This driver can also be built as a module. If so, the module + will be called rtc-max8997. + +config RTC_DRV_MAX31335 + tristate "Analog Devices MAX31335" + depends on I2C + depends on COMMON_CLK + depends on HWMON || HWMON=n + select REGMAP_I2C + help + If you say yes here you get support for the Analog Devices + MAX31335. + + This driver can also be built as a module. If so, the module + will be called rtc-max31335. + +config RTC_DRV_MAX77686 + tristate "Maxim MAX77686" + depends on MFD_MAX77686 || MFD_MAX77620 || MFD_MAX77714 || COMPILE_TEST + help + If you say yes here you will get support for the + RTC of Maxim MAX77686/MAX77620/MAX77802 PMIC. + + This driver can also be built as a module. If so, the module + will be called rtc-max77686. + +config RTC_DRV_SPACEMIT_P1 + tristate "SpacemiT P1 RTC" + depends on ARCH_SPACEMIT || COMPILE_TEST + depends on MFD_SPACEMIT_P1 + default MFD_SPACEMIT_P1 + help + Enable support for the RTC function in the SpacemiT P1 PMIC. + This driver can also be built as a module, which will be called + "spacemit-p1-rtc". + +config RTC_DRV_NVIDIA_VRS10 + tristate "NVIDIA VRS10 RTC device" + depends on ARCH_TEGRA || COMPILE_TEST + help + If you say yes here you will get support for the battery backed RTC device + of NVIDIA VRS (Voltage Regulator Specification). The RTC is connected via + I2C interface and supports alarm functionality. + This driver can also be built as a module. If so, the module will be called + rtc-nvidia-vrs10. + +config RTC_DRV_NCT3018Y + tristate "Nuvoton NCT3018Y" + depends on OF + help + If you say yes here you get support for the Nuvoton NCT3018Y I2C RTC + chip. + + This driver can also be built as a module, if so, the module will be + called "rtc-nct3018y". + +config RTC_DRV_NCT6694 + tristate "Nuvoton NCT6694 RTC support" + depends on MFD_NCT6694 + help + If you say yes to this option, support will be included for Nuvoton + NCT6694, a USB device to RTC. + + This driver can also be built as a module. If so, the module will + be called rtc-nct6694. + +config RTC_DRV_RK808 + tristate "Rockchip RK805/RK808/RK809/RK817/RK818 RTC" + depends on MFD_RK8XX + help + If you say yes here you will get support for the + RTC of RK805, RK809 and RK817, RK808 and RK818 PMIC. + + This driver can also be built as a module. If so, the module + will be called rk808-rtc. + +config RTC_DRV_RS5C372 + tristate "Ricoh R2025S/D, RS5C372A/B, RV5C386, RV5C387A" + help + If you say yes here you get support for the + Ricoh R2025S/D, RS5C372A, RS5C372B, RV5C386, and RV5C387A RTC chips. + + This driver can also be built as a module. If so, the module + will be called rtc-rs5c372. + +config RTC_DRV_ISL1208 + tristate "Intersil ISL1208" + help + If you say yes here you get support for the + Intersil ISL1208 RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-isl1208. + +config RTC_DRV_ISL12022 + tristate "Intersil ISL12022" + select REGMAP_I2C + help + If you say yes here you get support for the + Intersil ISL12022 RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-isl12022. + +config RTC_DRV_ISL12026 + tristate "Intersil ISL12026" + depends on OF + help + If you say yes here you get support for the + Intersil ISL12026 RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-isl12026. + +config RTC_DRV_X1205 + tristate "Xicor/Intersil X1205" + help + If you say yes here you get support for the + Xicor/Intersil X1205 RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-x1205. + +config RTC_DRV_PCF8523 + tristate "NXP PCF8523" + select REGMAP_I2C + help + If you say yes here you get support for the NXP PCF8523 RTC + chips. + + This driver can also be built as a module. If so, the module + will be called rtc-pcf8523. + +config RTC_DRV_PCF85363 + tristate "NXP PCF85363" + select REGMAP_I2C + help + If you say yes here you get support for the PCF85363 RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-pcf85363. + + The nvmem interface will be named pcf85363-#, where # is the + zero-based instance number. + +config RTC_DRV_PCF8563 + tristate "Philips PCF8563/Epson RTC8564" + select REGMAP_I2C + help + If you say yes here you get support for the + Philips PCF8563 RTC chip. The Epson RTC8564 + should work as well. + + This driver can also be built as a module. If so, the module + will be called rtc-pcf8563. + +config RTC_DRV_PCF8583 + tristate "Philips PCF8583" + help + If you say yes here you get support for the Philips PCF8583 + RTC chip found on Acorn RiscPCs. This driver supports the + platform specific method of retrieving the current year from + the RTC's SRAM. It will work on other platforms with the same + chip, but the year will probably have to be tweaked. + + This driver can also be built as a module. If so, the module + will be called rtc-pcf8583. + +config RTC_DRV_M41T80 + tristate "ST M41T62/65/M41T80/81/82/83/84/85/87 and compatible" + help + If you say Y here you will get support for the ST M41T60 + and M41T80 RTC chips series. Currently, the following chips are + supported: M41T62, M41T65, M41T80, M41T81, M41T82, M41T83, M41ST84, + M41ST85, M41ST87, and MicroCrystal RV4162. + + This driver can also be built as a module. If so, the module + will be called rtc-m41t80. + +config RTC_DRV_M41T80_WDT + bool "ST M41T65/M41T80 series RTC watchdog timer" + depends on RTC_DRV_M41T80 + help + If you say Y here you will get support for the + watchdog timer in the ST M41T60 and M41T80 RTC chips series. + +config RTC_DRV_BD70528 + tristate "ROHM BD71815 and BD71828 PMIC RTC" + depends on MFD_ROHM_BD71828 + help + If you say Y here you will get support for the RTC + block on ROHM BD71815, BD71828 and BD72720 Power + Management ICs. + + This driver can also be built as a module. If so, the module + will be called rtc-bd70528. + +config RTC_DRV_BQ32K + tristate "TI BQ32000" + help + If you say Y here you will get support for the TI + BQ32000 I2C RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-bq32k. + +config RTC_DRV_TWL92330 + bool "TI TWL92330/Menelaus" + depends on MENELAUS + help + If you say yes here you get support for the RTC on the + TWL92330 "Menelaus" power management chip, used with OMAP2 + platforms. The support is integrated with the rest of + the Menelaus driver; it's not separate module. + +config RTC_DRV_TWL4030 + tristate "TI TWL4030/TWL5030/TWL6030/TPS659x0" + depends on TWL4030_CORE + depends on OF + help + If you say yes here you get support for the RTC on the + TWL4030/TWL5030/TWL6030 family chips, used mostly with OMAP3 platforms. + + This driver can also be built as a module. If so, the module + will be called rtc-twl. + +config RTC_DRV_PALMAS + tristate "TI Palmas RTC driver" + depends on MFD_PALMAS + help + If you say yes here you get support for the RTC of TI PALMA series PMIC + chips. + + This driver can also be built as a module. If so, the module + will be called rtc-palma. + +config RTC_DRV_TPS6586X + tristate "TI TPS6586X RTC driver" + depends on MFD_TPS6586X + help + TI Power Management IC TPS6586X supports RTC functionality + along with alarm. This driver supports the RTC driver for + the TPS6586X RTC module. + +config RTC_DRV_TPS6594 + tristate "TI TPS6594 RTC driver" + depends on MFD_TPS6594 + default MFD_TPS6594 + help + TI Power Management IC TPS6594 supports RTC functionality + along with alarm. This driver supports the RTC driver for + the TPS6594 RTC module. + + This driver can also be built as a module. If so, the module + will be called rtc-tps6594. + +config RTC_DRV_TPS65910 + tristate "TI TPS65910 RTC driver" + depends on MFD_TPS65910 + help + If you say yes here you get support for the RTC on the + TPS65910 chips. + + This driver can also be built as a module. If so, the module + will be called rtc-tps65910. + +config RTC_DRV_RC5T583 + tristate "RICOH 5T583 RTC driver" + depends on MFD_RC5T583 + help + If you say yes here you get support for the RTC on the + RICOH 5T583 chips. + + This driver can also be built as a module. If so, the module + will be called rtc-rc5t583. + +config RTC_DRV_RC5T619 + tristate "RICOH RC5T619 RTC driver" + depends on MFD_RN5T618 + help + If you say yes here you get support for the RTC on the + RICOH RC5T619 chips. + + This driver can also be built as a module. If so, the module + will be called rtc-rc5t619. + +config RTC_DRV_S35390A + tristate "Seiko Instruments S-35390A" + select BITREVERSE + help + If you say yes here you will get support for the Seiko + Instruments S-35390A. + + This driver can also be built as a module. If so the module + will be called rtc-s35390a. + +config RTC_DRV_FM3130 + tristate "Ramtron FM3130" + help + If you say Y here you will get support for the + Ramtron FM3130 RTC chips. + Ramtron FM3130 is a chip with two separate devices inside, + RTC clock and FRAM. This driver provides only RTC functionality. + + This driver can also be built as a module. If so the module + will be called rtc-fm3130. + +config RTC_DRV_RX8010 + tristate "Epson RX8010SJ" + select REGMAP_I2C + help + If you say yes here you get support for the Epson RX8010SJ RTC + chip. + + This driver can also be built as a module. If so, the module + will be called rtc-rx8010. + +config RTC_DRV_RX8111 + tristate "Epson RX8111" + select REGMAP_I2C + depends on I2C + help + If you say yes here you will get support for the Epson RX8111 RTC. + + This driver can also be built as a module. If so, the module will be + called rtc-rx8111. + +config RTC_DRV_RX8581 + tristate "Epson RX-8571/RX-8581" + select REGMAP_I2C + help + If you say yes here you will get support for the Epson RX-8571/ + RX-8581. + + This driver can also be built as a module. If so the module + will be called rtc-rx8581. + +config RTC_DRV_RX8025 + tristate "Epson RX-8025SA/NB" + help + If you say yes here you get support for the Epson + RX-8025SA/NB RTC chips. + + This driver can also be built as a module. If so, the module + will be called rtc-rx8025. + +config RTC_DRV_EM3027 + tristate "EM Microelectronic EM3027" + help + If you say yes here you get support for the EM + Microelectronic EM3027 RTC chips. + + This driver can also be built as a module. If so, the module + will be called rtc-em3027. + +config RTC_DRV_RV3028 + tristate "Micro Crystal RV3028" + select REGMAP_I2C + help + If you say yes here you get support for the Micro Crystal + RV3028. + + This driver can also be built as a module. If so, the module + will be called rtc-rv3028. + +config RTC_DRV_RV3032 + tristate "Micro Crystal RV3032" + select REGMAP_I2C + help + If you say yes here you get support for the Micro Crystal + RV3032. + + This driver can also be built as a module. If so, the module + will be called rtc-rv3032. + +config RTC_DRV_RV8803 + tristate "Micro Crystal RV8803, Epson RX8900" + help + If you say yes here you get support for the Micro Crystal RV8803 and + Epson RX8900 RTC chips. + + This driver can also be built as a module. If so, the module + will be called rtc-rv8803. + +config RTC_DRV_S5M + tristate "Samsung S2M/S5M series" + depends on MFD_SEC_CORE || COMPILE_TEST + select REGMAP_IRQ + select REGMAP_I2C + help + If you say yes here you will get support for the + RTC of Samsung S2MPS14 and S5M PMIC series. + + This driver can also be built as a module. If so, the module + will be called rtc-s5m. + +config RTC_DRV_SD2405AL + tristate "DFRobot SD2405AL" + select REGMAP_I2C + help + If you say yes here you will get support for the + DFRobot SD2405AL I2C RTC Module. + + This driver can also be built as a module. If so, the module + will be called rtc-sd2405al. + +config RTC_DRV_SD3078 + tristate "ZXW Shenzhen whwave SD3078" + select REGMAP_I2C + help + If you say yes here you get support for the ZXW Shenzhen whwave + SD3078 RTC chips. + + This driver can also be built as a module. If so, the module + will be called rtc-sd3078 + +endif # I2C + +comment "SPI RTC drivers" + +if SPI_MASTER + +config RTC_DRV_M41T93 + tristate "ST M41T93" + help + If you say yes here you will get support for the + ST M41T93 SPI RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-m41t93. + +config RTC_DRV_M41T94 + tristate "ST M41T94" + help + If you say yes here you will get support for the + ST M41T94 SPI RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-m41t94. + +config RTC_DRV_DS1302 + tristate "Dallas/Maxim DS1302" + depends on SPI + help + If you say yes here you get support for the Dallas DS1302 RTC chips. + + This driver can also be built as a module. If so, the module + will be called rtc-ds1302. + +config RTC_DRV_DS1305 + tristate "Dallas/Maxim DS1305/DS1306" + help + Select this driver to get support for the Dallas/Maxim DS1305 + and DS1306 real time clock chips. These support a trickle + charger, alarms, and NVRAM in addition to the clock. + + This driver can also be built as a module. If so, the module + will be called rtc-ds1305. + +config RTC_DRV_DS1343 + select REGMAP_SPI + tristate "Dallas/Maxim DS1343/DS1344" + help + If you say yes here you get support for the + Dallas/Maxim DS1343 and DS1344 real time clock chips. + Support for trickle charger, alarm is provided. + + This driver can also be built as a module. If so, the module + will be called rtc-ds1343. + +config RTC_DRV_DS1347 + select REGMAP_SPI + tristate "Dallas/Maxim DS1347" + help + If you say yes here you get support for the + Dallas/Maxim DS1347 chips. + + This driver only supports the RTC feature, and not other chip + features such as alarms. + + This driver can also be built as a module. If so, the module + will be called rtc-ds1347. + +config RTC_DRV_DS1390 + tristate "Dallas/Maxim DS1390/93/94" + help + If you say yes here you get support for the + Dallas/Maxim DS1390/93/94 chips. + + This driver supports the RTC feature and trickle charging but not + other chip features such as alarms. + + This driver can also be built as a module. If so, the module + will be called rtc-ds1390. + +config RTC_DRV_MAX6916 + tristate "Maxim MAX6916" + help + If you say yes here you will get support for the + Maxim MAX6916 SPI RTC chip. + + This driver only supports the RTC feature, and not other chip + features such as alarms. + + This driver can also be built as a module. If so, the module + will be called rtc-max6916. + +config RTC_DRV_R9701 + tristate "Epson RTC-9701JE" + help + If you say yes here you will get support for the + Epson RTC-9701JE SPI RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-r9701. + +config RTC_DRV_RX4581 + tristate "Epson RX-4581" + help + If you say yes here you will get support for the Epson RX-4581. + + This driver can also be built as a module. If so the module + will be called rtc-rx4581. + +config RTC_DRV_RS5C348 + tristate "Ricoh RS5C348A/B" + help + If you say yes here you get support for the + Ricoh RS5C348A and RS5C348B RTC chips. + + This driver can also be built as a module. If so, the module + will be called rtc-rs5c348. + +config RTC_DRV_MAX6902 + tristate "Maxim MAX6902" + help + If you say yes here you will get support for the + Maxim MAX6902 SPI RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-max6902. + +config RTC_DRV_PCF2123 + tristate "NXP PCF2123" + select REGMAP_SPI + help + If you say yes here you get support for the NXP PCF2123 + RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-pcf2123. + +config RTC_DRV_MCP795 + tristate "Microchip MCP795" + help + If you say yes here you will get support for the Microchip MCP795. + + This driver can also be built as a module. If so the module + will be called rtc-mcp795. + +endif # SPI_MASTER + +# +# Helper to resolve issues with configs that have SPI enabled but I2C +# modular. See SND_SOC_I2C_AND_SPI for more information +# +config RTC_I2C_AND_SPI + tristate + default m if I2C=m + default y if I2C=y + default y if SPI_MASTER=y + +comment "SPI and I2C RTC drivers" + +config RTC_DRV_DS3232 + tristate "Dallas/Maxim DS3232/DS3234" + depends on RTC_I2C_AND_SPI + select REGMAP_I2C if I2C + select REGMAP_SPI if SPI_MASTER + help + If you say yes here you get support for Dallas Semiconductor + DS3232 and DS3234 real-time clock chips. If an interrupt is associated + with the device, the alarm functionality is supported. + + This driver can also be built as a module. If so, the module + will be called rtc-ds3232. + +config RTC_DRV_DS3232_HWMON + bool "HWMON support for Dallas/Maxim DS3232/DS3234" + depends on RTC_DRV_DS3232 && HWMON && !(RTC_DRV_DS3232=y && HWMON=m) + default y + help + Say Y here if you want to expose temperature sensor data on + rtc-ds3232 + +config RTC_DRV_PCF2127 + tristate "NXP PCF2127" + depends on RTC_I2C_AND_SPI + select REGMAP_I2C if I2C + select REGMAP_SPI if SPI_MASTER + select WATCHDOG_CORE if WATCHDOG + help + If you say yes here you get support for the NXP PCF2127/29/31 RTC + chips with integrated quartz crystal for industrial applications. + These chips also have watchdog timer and tamper switch detection + features. + + PCF2127 has an additional feature of 512 bytes battery backed + memory that's accessible using nvmem interface. + + This driver can also be built as a module. If so, the module + will be called rtc-pcf2127. + +config RTC_DRV_PCF85063 + tristate "NXP PCF85063" + depends on RTC_I2C_AND_SPI + select REGMAP_I2C if I2C + select REGMAP_SPI if SPI_MASTER + help + If you say yes here you get support for the PCF85063 and RV8063 + RTC chips. + + This driver can also be built as a module. If so, the module + will be called rtc-pcf85063. + +config RTC_DRV_RV3029C2 + tristate "Micro Crystal RV3029/3049" + depends on RTC_I2C_AND_SPI + select REGMAP_I2C if I2C + select REGMAP_SPI if SPI_MASTER + help + If you say yes here you get support for the Micro Crystal + RV3029 and RV3049 RTC chips. + + This driver can also be built as a module. If so, the module + will be called rtc-rv3029c2. + +config RTC_DRV_RV3029_HWMON + bool "HWMON support for RV3029/3049" + depends on RTC_DRV_RV3029C2 && HWMON + depends on !(RTC_DRV_RV3029C2=y && HWMON=m) + default y + help + Say Y here if you want to expose temperature sensor data on + rtc-rv3029. + +config RTC_DRV_RX6110 + tristate "Epson RX-6110" + depends on RTC_I2C_AND_SPI + select REGMAP_SPI if SPI_MASTER + select REGMAP_I2C if I2C + help + If you say yes here you will get support for the Epson RX-6110. + + This driver can also be built as a module. If so the module + will be called rtc-rx6110. + +comment "Platform RTC drivers" + +# this 'CMOS' RTC driver is arch dependent because it requires +# defining CMOS_READ/CMOS_WRITE, and a +# global rtc_lock ... it's not yet just another platform_device. + +config RTC_DRV_CMOS + tristate "PC-style 'CMOS'" + depends on X86 || ARM || PPC || MIPS || SPARC64 + depends on HAS_IOPORT || MACH_DECSTATION + default y if X86 + select RTC_MC146818_LIB + help + Say "yes" here to get direct support for the real time clock + found in every PC or ACPI-based system, and some other boards. + Specifically the original MC146818, compatibles like those in + PC south bridges, the DS12887 or M48T86, some multifunction + or LPC bus chips, and so on. + + Your system will need to define the platform device used by + this driver, otherwise it won't be accessible. This means + you can safely enable this driver if you don't know whether + or not your board has this kind of hardware. + + This driver can also be built as a module. If so, the module + will be called rtc-cmos. + +config RTC_DRV_ALPHA + bool "Alpha PC-style CMOS" + depends on ALPHA + depends on HAS_IOPORT + select RTC_MC146818_LIB + default y + help + Direct support for the real-time clock found on every Alpha + system, specifically MC146818 compatibles. If in doubt, say Y. + +config RTC_DRV_ATCRTC100 + tristate "Andes ATCRTC100" + depends on ARCH_ANDES || COMPILE_TEST + select REGMAP_MMIO + help + If you say yes here you will get support for the Andes ATCRTC100 + RTC driver. + + This driver provides support for the Andes ATCRTC100 real-time clock + device. It allows setting and retrieving the time and date, as well + as setting alarms. + + To compile this driver as a module, choose M here: the module will + be called rtc-atcrtc100. + +config RTC_DRV_DS1216 + tristate "Dallas DS1216" + depends on SNI_RM + help + If you say yes here you get support for the Dallas DS1216 RTC chips. + +config RTC_DRV_DS1286 + tristate "Dallas DS1286" + depends on HAS_IOMEM + help + If you say yes here you get support for the Dallas DS1286 RTC chips. + +config RTC_DRV_DS1511 + tristate "Dallas DS1511" + depends on HAS_IOMEM + help + If you say yes here you get support for the + Dallas DS1511 timekeeping/watchdog chip. + + This driver can also be built as a module. If so, the module + will be called rtc-ds1511. + +config RTC_DRV_DS1553 + tristate "Maxim/Dallas DS1553" + depends on HAS_IOMEM + help + If you say yes here you get support for the + Maxim/Dallas DS1553 timekeeping chip. + + This driver can also be built as a module. If so, the module + will be called rtc-ds1553. + +config RTC_DRV_DS1685_FAMILY + tristate "Dallas/Maxim DS1685 Family" + depends on HAS_IOMEM + help + If you say yes here you get support for the Dallas/Maxim DS1685 + family of real time chips. This family includes the DS1685/DS1687, + DS1689/DS1693, DS17285/DS17287, DS17485/DS17487, and + DS17885/DS17887 chips. + + This driver can also be built as a module. If so, the module + will be called rtc-ds1685. + +choice + prompt "Subtype" + depends on RTC_DRV_DS1685_FAMILY + default RTC_DRV_DS1685 + +config RTC_DRV_DS1685 + bool "DS1685/DS1687" + help + This enables support for the Dallas/Maxim DS1685/DS1687 real time + clock chip. + + This chip is commonly found in SGI O2 (IP32) and SGI Octane (IP30) + systems, as well as EPPC-405-UC modules by electronic system design + GmbH. + +config RTC_DRV_DS1689 + bool "DS1689/DS1693" + help + This enables support for the Dallas/Maxim DS1689/DS1693 real time + clock chip. + + This is an older RTC chip, supplanted by the DS1685/DS1687 above, + which supports a few minor features such as Vcc, Vbat, and Power + Cycle counters, plus a customer-specific, 8-byte ROM/Serial number. + + It also works for the even older DS1688/DS1691 RTC chips, which are + virtually the same and carry the same model number. Both chips + have 114 bytes of user NVRAM. + +config RTC_DRV_DS17285 + bool "DS17285/DS17287" + help + This enables support for the Dallas/Maxim DS17285/DS17287 real time + clock chip. + + This chip features 2kb of extended NV-SRAM. It may possibly be + found in some SGI O2 systems (rare). + +config RTC_DRV_DS17485 + bool "DS17485/DS17487" + help + This enables support for the Dallas/Maxim DS17485/DS17487 real time + clock chip. + + This chip features 4kb of extended NV-SRAM. + +config RTC_DRV_DS17885 + bool "DS17885/DS17887" + help + This enables support for the Dallas/Maxim DS17885/DS17887 real time + clock chip. + + This chip features 8kb of extended NV-SRAM. + +endchoice + +config RTC_DRV_DS1742 + tristate "Maxim/Dallas DS1742/1743" + depends on HAS_IOMEM + help + If you say yes here you get support for the + Maxim/Dallas DS1742/1743 timekeeping chip. + + This driver can also be built as a module. If so, the module + will be called rtc-ds1742. + +config RTC_DRV_DS2404 + tristate "Maxim/Dallas DS2404" + help + If you say yes here you get support for the + Dallas DS2404 RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-ds2404. + +config RTC_DRV_DA9052 + tristate "Dialog DA9052/DA9053 RTC" + depends on PMIC_DA9052 + help + Say y here to support the RTC driver for Dialog Semiconductor + DA9052-BC and DA9053-AA/Bx PMICs. + +config RTC_DRV_DA9055 + tristate "Dialog Semiconductor DA9055 RTC" + depends on MFD_DA9055 + help + If you say yes here you will get support for the + RTC of the Dialog DA9055 PMIC. + + This driver can also be built as a module. If so, the module + will be called rtc-da9055 + +config RTC_DRV_DA9063 + tristate "Dialog Semiconductor DA9063/DA9062 RTC" + depends on MFD_DA9063 || MFD_DA9062 + help + If you say yes here you will get support for the RTC subsystem + for the Dialog Semiconductor PMIC chips DA9063 and DA9062. + + This driver can also be built as a module. If so, the module + will be called "rtc-da9063". + +config RTC_DRV_EFI + tristate "EFI RTC" + depends on EFI && !X86 + help + If you say yes here you will get support for the EFI + Real Time Clock. + + This driver can also be built as a module. If so, the module + will be called rtc-efi. + +config RTC_DRV_STK17TA8 + tristate "Simtek STK17TA8" + depends on HAS_IOMEM + help + If you say yes here you get support for the + Simtek STK17TA8 timekeeping chip. + + This driver can also be built as a module. If so, the module + will be called rtc-stk17ta8. + +config RTC_DRV_M48T86 + tristate "ST M48T86/Dallas DS12887" + depends on HAS_IOMEM + help + If you say Y here you will get support for the + ST M48T86 and Dallas DS12887 RTC chips. + + This driver can also be built as a module. If so, the module + will be called rtc-m48t86. + +config RTC_DRV_M48T35 + tristate "ST M48T35" + depends on HAS_IOMEM + help + If you say Y here you will get support for the + ST M48T35 RTC chip. + + This driver can also be built as a module, if so, the module + will be called "rtc-m48t35". + +config RTC_DRV_M48T59 + tristate "ST M48T59/M48T08/M48T02" + depends on HAS_IOMEM + help + If you say Y here you will get support for the + ST M48T59 RTC chip and compatible ST M48T08 and M48T02. + + These chips are usually found in Sun SPARC and UltraSPARC + workstations. + + This driver can also be built as a module, if so, the module + will be called "rtc-m48t59". + +config RTC_DRV_MSM6242 + tristate "Oki MSM6242" + depends on HAS_IOMEM + help + If you say yes here you get support for the Oki MSM6242 + timekeeping chip. It is used in some Amiga models (e.g. A2000). + + This driver can also be built as a module. If so, the module + will be called rtc-msm6242. + +config RTC_DRV_BQ4802 + tristate "TI BQ4802" + depends on HAS_IOMEM && HAS_IOPORT + depends on SPARC || COMPILE_TEST + help + If you say Y here you will get support for the TI + BQ4802 RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-bq4802. + +config RTC_DRV_RP5C01 + tristate "Ricoh RP5C01" + depends on HAS_IOMEM + help + If you say yes here you get support for the Ricoh RP5C01 + timekeeping chip. It is used in some Amiga models (e.g. A3000 + and A4000). + + This driver can also be built as a module. If so, the module + will be called rtc-rp5c01. + +config RTC_DRV_GAMECUBE + tristate "Nintendo GameCube, Wii and Wii U RTC" + depends on GAMECUBE || WII || COMPILE_TEST + select REGMAP + help + If you say yes here you will get support for the RTC subsystem + of the Nintendo GameCube, Wii and Wii U. + + This driver can also be built as a module. If so, the module + will be called "rtc-gamecube". + +config RTC_DRV_WM831X + tristate "Wolfson Microelectronics WM831x RTC" + depends on MFD_WM831X + help + If you say yes here you will get support for the RTC subsystem + of the Wolfson Microelectronics WM831X series PMICs. + + This driver can also be built as a module. If so, the module + will be called "rtc-wm831x". + +config RTC_DRV_WM8350 + tristate "Wolfson Microelectronics WM8350 RTC" + depends on MFD_WM8350 + help + If you say yes here you will get support for the RTC subsystem + of the Wolfson Microelectronics WM8350. + + This driver can also be built as a module. If so, the module + will be called "rtc-wm8350". + +config RTC_DRV_SC27XX + tristate "Spreadtrum SC27xx RTC" + depends on MFD_SC27XX_PMIC || COMPILE_TEST + help + If you say Y here you will get support for the RTC subsystem + of the Spreadtrum SC27xx series PMICs. The SC27xx series PMICs + includes the SC2720, SC2721, SC2723, SC2730 and SC2731 chips. + + This driver can also be built as a module. If so, the module + will be called rtc-sc27xx. + +config RTC_DRV_SPEAR + tristate "SPEAR ST RTC" + depends on PLAT_SPEAR || COMPILE_TEST + default PLAT_SPEAR + help + If you say Y here you will get support for the RTC found on + spear + +config RTC_DRV_AB8500 + tristate "ST-Ericsson AB8500 RTC" + depends on AB8500_CORE + select RTC_INTF_DEV + select RTC_INTF_DEV_UIE_EMUL + help + Select this to enable the ST-Ericsson AB8500 power management IC RTC + support. This chip contains a battery- and capacitor-backed RTC. + +config RTC_DRV_OPAL + tristate "IBM OPAL RTC driver" + depends on PPC_POWERNV + default y + help + If you say yes here you get support for the PowerNV platform RTC + driver based on OPAL interfaces. + + This driver can also be built as a module. If so, the module + will be called rtc-opal. + +config RTC_DRV_OPTEE + tristate "OP-TEE based RTC driver" + depends on OPTEE + help + Select this to get support for OP-TEE based RTC control on SoCs where + RTC are not accessible to the normal world (Linux). + + This driver can also be built as a module. If so, the module + will be called rtc-optee. + +config RTC_DRV_ZYNQMP + tristate "Xilinx Zynq Ultrascale+ MPSoC RTC" + depends on OF && HAS_IOMEM + help + If you say yes here you get support for the RTC controller found on + Xilinx Zynq Ultrascale+ MPSoC. + +config RTC_DRV_CROS_EC + tristate "Chrome OS EC RTC driver" + depends on CROS_EC + help + If you say yes here you will get support for the + Chrome OS Embedded Controller's RTC. + + This driver can also be built as a module. If so, the module + will be called rtc-cros-ec. + +config RTC_DRV_NTXEC + tristate "Netronix embedded controller RTC" + depends on MFD_NTXEC + help + Say yes here if you want to support the RTC functionality of the + embedded controller found in certain e-book readers designed by the + original design manufacturer Netronix. + +comment "on-CPU RTC drivers" + +config RTC_DRV_ASM9260 + tristate "Alphascale asm9260 RTC" + depends on MACH_ASM9260 || COMPILE_TEST + help + If you say yes here you get support for the RTC on the + Alphascale asm9260 SoC. + + This driver can also be built as a module. If so, the module + will be called rtc-asm9260. + +config RTC_DRV_CV1800 + tristate "Sophgo CV1800 RTC" + depends on SOPHGO_CV1800_RTCSYS || COMPILE_TEST + select MFD_SYSCON + select REGMAP + help + If you say yes here you get support the RTC driver for Sophgo CV1800 + series SoC. + + This driver can also be built as a module. If so, the module will be + called rtc-cv1800. + +config RTC_DRV_DIGICOLOR + tristate "Conexant Digicolor RTC" + depends on ARCH_DIGICOLOR || COMPILE_TEST + help + If you say yes here you get support for the RTC on Conexant + Digicolor platforms. This currently includes the CX92755 SoC. + + This driver can also be built as a module. If so, the module + will be called rtc-digicolor. + +config RTC_DRV_IMXDI + tristate "Freescale IMX DryIce Real Time Clock" + depends on ARCH_MXC || COMPILE_TEST + depends on OF + help + Support for Freescale IMX DryIce RTC + + This driver can also be built as a module, if so, the module + will be called "rtc-imxdi". + +config RTC_DRV_FSL_FTM_ALARM + tristate "Freescale FlexTimer alarm timer" + depends on ARCH_LAYERSCAPE || SOC_LS1021A || COMPILE_TEST + help + For the FlexTimer in LS1012A, LS1021A, LS1028A, LS1043A, LS1046A, + LS1088A, LS208xA, we can use FTM as the wakeup source. + + Say y here to enable FTM alarm support. The FTM alarm provides + alarm functions for wakeup system from deep sleep. + + This driver can also be built as a module, if so, the module + will be called "rtc-fsl-ftm-alarm". + +config RTC_DRV_MESON + tristate "Amlogic Meson RTC" + depends on (ARM && ARCH_MESON) || COMPILE_TEST + select REGMAP_MMIO + help + Support for the RTC block on the Amlogic Meson6, Meson8, Meson8b + and Meson8m2 SoCs. + + This driver can also be built as a module, if so, the module + will be called "rtc-meson". + +config RTC_DRV_MESON_VRTC + tristate "Amlogic Meson Virtual RTC" + depends on ARCH_MESON || COMPILE_TEST + default m if ARCH_MESON + help + If you say yes here you will get support for the + Virtual RTC of Amlogic SoCs. + + This driver can also be built as a module. If so, the module + will be called rtc-meson-vrtc. + +config RTC_DRV_OMAP + tristate "TI OMAP Real Time Clock" + depends on ARCH_OMAP || ARCH_DAVINCI || COMPILE_TEST + depends on OF + depends on PINCTRL + select GENERIC_PINCONF + help + Say "yes" here to support the on chip real time clock + present on TI OMAP1, AM33xx, DA8xx/OMAP-L13x, AM43xx and DRA7xx. + + This driver can also be built as a module, if so, module + will be called rtc-omap. + +config RTC_DRV_S3C + tristate "Samsung S3C series SoC RTC" + depends on ARCH_EXYNOS || ARCH_S3C64XX || ARCH_S5PV210 || \ + COMPILE_TEST + help + RTC (Realtime Clock) driver for the clock inbuilt into the + Samsung S3C64XX series of SoCs. This can provide periodic + interrupt rates from 1Hz to 64Hz for user programs, and + wakeup from Alarm. + + This driver can also be build as a module. If so, the module + will be called rtc-s3c. + +config RTC_DRV_EP93XX + tristate "Cirrus Logic EP93XX" + depends on ARCH_EP93XX || COMPILE_TEST + help + If you say yes here you get support for the + RTC embedded in the Cirrus Logic EP93XX processors. + + This driver can also be built as a module. If so, the module + will be called rtc-ep93xx. + +config RTC_DRV_SA1100 + tristate "SA11x0/PXA2xx/PXA910" + depends on ARCH_SA1100 || ARCH_PXA || ARCH_MMP + help + If you say Y here you will get access to the real time clock + built into your SA11x0 or PXA2xx CPU. + + To compile this driver as a module, choose M here: the + module will be called rtc-sa1100. + +config RTC_DRV_SH + tristate "SuperH On-Chip RTC" + depends on SUPERH || ARCH_RENESAS + help + Say Y here to enable support for the on-chip RTC found in + most SuperH processors. This RTC is also found in RZ/A SoCs. + + To compile this driver as a module, choose M here: the + module will be called rtc-sh. + +config RTC_DRV_SUNPLUS + tristate "Sunplus SP7021 RTC" + depends on SOC_SP7021 + help + Say 'yes' to get support for the real-time clock present in + Sunplus SP7021 - a SoC for industrial applications. It provides + RTC status check, timer/alarm functionalities, user data + reservation with the battery over 2.5V, RTC power status check + and battery charge. + + This driver can also be built as a module. If so, the module + will be called rtc-sunplus. + +config RTC_DRV_PL030 + tristate "ARM AMBA PL030 RTC" + depends on ARM_AMBA + help + If you say Y here you will get access to ARM AMBA + PrimeCell PL030 RTC found on certain ARM SOCs. + + To compile this driver as a module, choose M here: the + module will be called rtc-pl030. + +config RTC_DRV_PL031 + tristate "ARM AMBA PL031 RTC" + depends on ARM_AMBA + help + If you say Y here you will get access to ARM AMBA + PrimeCell PL031 RTC found on certain ARM SOCs. + + To compile this driver as a module, choose M here: the + module will be called rtc-pl031. + +config RTC_DRV_AT91RM9200 + tristate "AT91RM9200 or some AT91SAM9 RTC" + depends on ARCH_AT91 || COMPILE_TEST + depends on OF + help + Driver for the internal RTC (Realtime Clock) module found on + Atmel AT91RM9200's and some AT91SAM9 chips. On AT91SAM9 chips + this is powered by the backup power supply. + +config RTC_DRV_AT91SAM9 + tristate "AT91SAM9 RTT as RTC" + depends on ARCH_AT91 || COMPILE_TEST + depends on OF && HAS_IOMEM + select MFD_SYSCON + help + Some AT91SAM9 SoCs provide an RTT (Real Time Timer) block which + can be used as an RTC thanks to the backup power supply (e.g. a + small coin cell battery) which keeps this block and the GPBR + (General Purpose Backup Registers) block powered when the device + is shutdown. + Some AT91SAM9 SoCs provide a real RTC block, on those ones you'd + probably want to use the real RTC block instead of the "RTT as an + RTC" driver. + +config RTC_DRV_AU1XXX + tristate "Au1xxx Counter0 RTC support" + depends on MIPS_ALCHEMY + help + This is a driver for the Au1xxx on-chip Counter0 (Time-Of-Year + counter) to be used as a RTC. + + This driver can also be built as a module. If so, the module + will be called rtc-au1xxx. + +config RTC_DRV_RS5C313 + tristate "Ricoh RS5C313" + depends on SH_LANDISK + help + If you say yes here you get support for the Ricoh RS5C313 RTC chips. + +config RTC_DRV_RZN1 + tristate "Renesas RZ/N1 RTC" + depends on ARCH_RENESAS || COMPILE_TEST + depends on OF && HAS_IOMEM + help + If you say yes here you get support for the RTC initially found on + Renesas RZ/N1 SoCs. + +config RTC_DRV_GENERIC + tristate "Generic RTC support" + # Please consider writing a new RTC driver instead of using the generic + # RTC abstraction + depends on PARISC || M68K || PPC || SUPERH || COMPILE_TEST + help + Say Y or M here to enable RTC support on systems using the generic + RTC abstraction. If you do not know what you are doing, you should + just say Y. + +config RTC_DRV_PXA + tristate "PXA27x/PXA3xx" + depends on ARCH_PXA + select RTC_DRV_SA1100 + help + If you say Y here you will get access to the real time clock + built into your PXA27x or PXA3xx CPU. This RTC is actually 2 RTCs + consisting of an SA1100 compatible RTC and the extended PXA RTC. + + This RTC driver uses PXA RTC registers available since pxa27x + series (RDxR, RYxR) instead of legacy RCNR, RTAR. + +config RTC_DRV_VT8500 + tristate "VIA/WonderMedia 85xx SoC RTC" + depends on ARCH_VT8500 || COMPILE_TEST + help + If you say Y here you will get access to the real time clock + built into your VIA VT8500 SoC or its relatives. + + +config RTC_DRV_SUN4V + bool "SUN4V Hypervisor RTC" + depends on SPARC64 + help + If you say Y here you will get support for the Hypervisor + based RTC on SUN4V systems. + +config RTC_DRV_SUN6I + bool "Allwinner A31 RTC" + default MACH_SUN6I || MACH_SUN8I + depends on COMMON_CLK + depends on ARCH_SUNXI || COMPILE_TEST + help + If you say Y here you will get support for the RTC found in + some Allwinner SoCs like the A31 or the A64. + +config RTC_DRV_SUNXI + tristate "Allwinner sun4i/sun7i RTC" + depends on MACH_SUN4I || MACH_SUN7I || COMPILE_TEST + help + If you say Y here you will get support for the RTC found on + Allwinner A10/A20. + +config RTC_DRV_STARFIRE + bool "Starfire RTC" + depends on SPARC64 + help + If you say Y here you will get support for the RTC found on + Starfire systems. + +config RTC_DRV_MV + tristate "Marvell SoC RTC" + depends on ARCH_DOVE || ARCH_MVEBU || COMPILE_TEST + help + If you say yes here you will get support for the in-chip RTC + that can be found in some of Marvell's SoC devices, such as + the Kirkwood 88F6281 and 88F6192. + + This driver can also be built as a module. If so, the module + will be called rtc-mv. + +config RTC_DRV_ARMADA38X + tristate "Armada 38x Marvell SoC RTC" + depends on ARCH_MVEBU || COMPILE_TEST + depends on OF + help + If you say yes here you will get support for the in-chip RTC + that can be found in the Armada 38x Marvell's SoC device + + This driver can also be built as a module. If so, the module + will be called armada38x-rtc. + +config RTC_DRV_CADENCE + tristate "Cadence RTC driver" + depends on OF && HAS_IOMEM + help + If you say Y here you will get access to Cadence RTC IP + found on certain SOCs. + + To compile this driver as a module, choose M here: the + module will be called rtc-cadence. + +config RTC_DRV_FTRTC010 + tristate "Faraday Technology FTRTC010 RTC" + depends on HAS_IOMEM + default ARCH_GEMINI + help + If you say Y here you will get support for the + Faraday Technolog FTRTC010 found on e.g. Gemini SoC's. + + This driver can also be built as a module. If so, the module + will be called rtc-ftrtc010. + +config RTC_DRV_PS3 + tristate "PS3 RTC" + depends on PPC_PS3 + help + If you say yes here you will get support for the RTC on PS3. + + This driver can also be built as a module. If so, the module + will be called rtc-ps3. + +config RTC_DRV_STMP + tristate "Freescale STMP3xxx/i.MX23/i.MX28 RTC" + depends on ARCH_MXS || COMPILE_TEST + select STMP_DEVICE + help + If you say yes here you will get support for the onboard + STMP3xxx/i.MX23/i.MX28 RTC. + + This driver can also be built as a module. If so, the module + will be called rtc-stmp3xxx. + +config RTC_DRV_MC13XXX + depends on MFD_MC13XXX + tristate "Freescale MC13xxx RTC" + help + This enables support for the RTCs found on Freescale's PMICs + MC13783, MC13892 and MC34708. + +config RTC_DRV_MPC5121 + tristate "Freescale MPC5121 built-in RTC" + depends on PPC_MPC512x || PPC_MPC52xx + help + If you say yes here you will get support for the + built-in RTC on MPC5121 or on MPC5200. + + This driver can also be built as a module. If so, the module + will be called rtc-mpc5121. + +config RTC_DRV_JZ4740 + tristate "Ingenic JZ4740 SoC" + depends on MIPS || COMPILE_TEST + depends on OF && COMMON_CLK + help + If you say yes here you get support for the Ingenic JZ47xx SoCs RTC + controllers. + + This driver can also be built as a module. If so, the module + will be called rtc-jz4740. + +config RTC_DRV_LOONGSON + tristate "Loongson On-chip RTC" + depends on MACH_LOONGSON32 || MACH_LOONGSON64 || COMPILE_TEST + select REGMAP_MMIO + help + This is a driver for the Loongson on-chip Counter0 (Time-Of-Year + counter) to be used as a RTC. + It can be found on Loongson-1 series cpu, Loongson-2K series cpu + and Loongson LS7A bridge chips. + + This driver can also be built as a module. If so, the module + will be called rtc-loongson. + +config RTC_DRV_LPC24XX + tristate "NXP RTC for LPC178x/18xx/408x/43xx" + depends on ARCH_LPC18XX || COMPILE_TEST + depends on OF && HAS_IOMEM + depends on COMMON_CLK + help + This enables support for the NXP RTC found which can be found on + NXP LPC178x/18xx/408x/43xx devices. + + If you have one of the devices above enable this driver to use + the hardware RTC. This driver can also be built as a module. If + so, the module will be called rtc-lpc24xx. + +config RTC_DRV_LPC32XX + depends on ARCH_LPC32XX || COMPILE_TEST + tristate "NXP LPC32XX RTC" + help + This enables support for the NXP RTC in the LPC32XX + + This driver can also be built as a module. If so, the module + will be called rtc-lpc32xx. + +config RTC_DRV_PM8XXX + tristate "Qualcomm PMIC8XXX RTC" + depends on MFD_PM8XXX || MFD_SPMI_PMIC || COMPILE_TEST + help + If you say yes here you get support for the + Qualcomm PMIC8XXX RTC. + + To compile this driver as a module, choose M here: the + module will be called rtc-pm8xxx. + +config RTC_DRV_TEGRA + tristate "NVIDIA Tegra Internal RTC driver" + depends on ARCH_TEGRA || COMPILE_TEST + help + If you say yes here you get support for the + Tegra 200 series internal RTC module. + + This drive can also be built as a module. If so, the module + will be called rtc-tegra. + +config RTC_DRV_MXC + tristate "Freescale MXC Real Time Clock" + depends on ARCH_MXC || COMPILE_TEST + depends on HAS_IOMEM + depends on OF + help + If you say yes here you get support for the Freescale MXC + RTC module. + + This driver can also be built as a module, if so, the module + will be called "rtc-mxc". + +config RTC_DRV_MXC_V2 + tristate "Freescale MXC Real Time Clock for i.MX53" + depends on ARCH_MXC || COMPILE_TEST + depends on HAS_IOMEM + depends on OF + help + If you say yes here you get support for the Freescale MXC + SRTC module in i.MX53 processor. + + This driver can also be built as a module, if so, the module + will be called "rtc-mxc_v2". + +config RTC_DRV_SNVS + tristate "Freescale SNVS RTC support" + select REGMAP_MMIO + depends on ARCH_MXC || COMPILE_TEST + depends on HAS_IOMEM + depends on OF + help + If you say yes here you get support for the Freescale SNVS + Low Power (LP) RTC module. + + This driver can also be built as a module, if so, the module + will be called "rtc-snvs". + +config RTC_DRV_BBNSM + tristate "NXP BBNSM RTC support" + select REGMAP_MMIO + depends on ARCH_MXC || COMPILE_TEST + depends on HAS_IOMEM + depends on OF + help + If you say yes here you get support for the NXP BBNSM RTC module. + + This driver can also be built as a module, if so, the module + will be called "rtc-bbnsm". + +config RTC_DRV_IMX_BBM_SCMI + depends on IMX_SCMI_BBM_EXT || COMPILE_TEST + default y if ARCH_MXC + tristate "NXP i.MX BBM SCMI RTC support" + help + If you say yes here you get support for the NXP i.MX BBSM SCMI + RTC module. + + To compile this driver as a module, choose M here: the + module will be called rtc-imx-sm-bbm. + +config RTC_DRV_IMX_SC + depends on IMX_SCU + depends on HAVE_ARM_SMCCC + tristate "NXP i.MX System Controller RTC support" + help + If you say yes here you get support for the NXP i.MX System + Controller RTC module. + +config RTC_DRV_ST_LPC + tristate "STMicroelectronics LPC RTC" + depends on ARCH_STI + depends on OF + help + Say Y here to include STMicroelectronics Low Power Controller + (LPC) based RTC support. + + To compile this driver as a module, choose M here: the + module will be called rtc-st-lpc. + +config RTC_DRV_MOXART + tristate "MOXA ART RTC" + depends on ARCH_MOXART || COMPILE_TEST + help + If you say yes here you get support for the MOXA ART + RTC module. + + This driver can also be built as a module. If so, the module + will be called rtc-moxart + +config RTC_DRV_MT2712 + tristate "MediaTek MT2712 SoC based RTC" + depends on ARCH_MEDIATEK || COMPILE_TEST + help + This enables support for the real time clock built in the MediaTek + SoCs for MT2712. + + This drive can also be built as a module. If so, the module + will be called rtc-mt2712. + +config RTC_DRV_MT6397 + tristate "MediaTek PMIC based RTC" + depends on MFD_MT6397 || COMPILE_TEST + select IRQ_DOMAIN + help + This selects the MediaTek(R) RTC driver. RTC is part of MediaTek + MT6397 PMIC. You should enable MT6397 PMIC MFD before select + MediaTek(R) RTC driver. + + If you want to use MediaTek(R) RTC interface, select Y or M here. + +config RTC_DRV_MT7622 + tristate "MediaTek SoC based RTC" + depends on ARCH_MEDIATEK || COMPILE_TEST + help + This enables support for the real time clock built in the MediaTek + SoCs. + + This drive can also be built as a module. If so, the module + will be called rtc-mt7622. + +config RTC_DRV_XGENE + tristate "APM X-Gene RTC" + depends on HAS_IOMEM + depends on ARCH_XGENE || COMPILE_TEST + help + If you say yes here you get support for the APM X-Gene SoC real time + clock. + + This driver can also be built as a module, if so, the module + will be called "rtc-xgene". + +config RTC_DRV_PIC32 + tristate "Microchip PIC32 RTC" + depends on MACH_PIC32 || COMPILE_TEST + default y + help + If you say yes here you get support for the PIC32 RTC module. + + This driver can also be built as a module. If so, the module + will be called rtc-pic32 + +config RTC_DRV_R7301 + tristate "EPSON TOYOCOM RTC-7301SF/DG" + select REGMAP_MMIO + depends on OF && HAS_IOMEM + help + If you say yes here you get support for the EPSON TOYOCOM + RTC-7301SF/DG chips. + + This driver can also be built as a module. If so, the module + will be called rtc-r7301. + +config RTC_DRV_STM32 + tristate "STM32 RTC" + select REGMAP_MMIO + depends on ARCH_STM32 || COMPILE_TEST + depends on OF + depends on PINCTRL + select PINMUX + select PINCONF + select GENERIC_PINCONF + depends on COMMON_CLK + help + If you say yes here you get support for the STM32 On-Chip + Real Time Clock. + + This driver can also be built as a module, if so, the module + will be called "rtc-stm32". + +config RTC_DRV_CPCAP + depends on MFD_CPCAP + tristate "Motorola CPCAP RTC" + help + Say y here for CPCAP rtc found on some Motorola phones + and tablets such as Droid 4. + +config RTC_DRV_RTD119X + bool "Realtek RTD129x RTC" + depends on ARCH_REALTEK || COMPILE_TEST + default ARCH_REALTEK + help + If you say yes here, you get support for the RTD1295 SoC + Real Time Clock. + +config RTC_DRV_ASPEED + tristate "ASPEED RTC" + depends on OF + depends on ARCH_ASPEED || COMPILE_TEST + help + If you say yes here you get support for the ASPEED BMC SoC real time + clocks. + + This driver can also be built as a module, if so, the module + will be called "rtc-aspeed". + +config RTC_DRV_TI_K3 + tristate "TI K3 RTC" + depends on ARCH_K3 || COMPILE_TEST + select REGMAP_MMIO + help + If you say yes here you get support for the Texas Instruments's + Real Time Clock for K3 architecture. + + This driver can also be built as a module, if so, the module + will be called "rtc-ti-k3". + +config RTC_DRV_MA35D1 + tristate "Nuvoton MA35D1 RTC" + depends on ARCH_MA35 || COMPILE_TEST + select REGMAP_MMIO + help + If you say yes here you get support for the Nuvoton MA35D1 + On-Chip Real Time Clock. + + This driver can also be built as a module, if so, the module + will be called "rtc-ma35d1". + +config RTC_DRV_RENESAS_RTCA3 + tristate "Renesas RTCA-3 RTC" + depends on ARCH_RENESAS + help + If you say yes here you get support for the Renesas RTCA-3 RTC + available on the Renesas RZ/G3S SoC. + + This driver can also be built as a module, if so, the module + will be called "rtc-rtca3". + +config RTC_DRV_GOLDFISH + tristate "Goldfish Real Time Clock" + depends on HAS_IOMEM + help + Say yes to enable RTC driver for the Goldfish based virtual platform. + + Goldfish is a code name for the virtual platform developed by Google + for Android emulation. + +config RTC_DRV_WILCO_EC + tristate "Wilco EC RTC" + depends on WILCO_EC + default m + help + If you say yes here, you get read/write support for the Real Time + Clock on the Wilco Embedded Controller (Wilco is a kind of Chromebook) + + This can also be built as a module. If so, the module will + be named "rtc_wilco_ec". + +config RTC_DRV_MACSMC + tristate "Apple Mac System Management Controller RTC" + depends on MFD_MACSMC + help + If you say yes here you get support for RTC functions + inside Apple SPMI PMUs accessed through the SoC's + System Management Controller + + To compile this driver as a module, choose M here: the + module will be called rtc-macsmc. + +config RTC_DRV_MSC313 + tristate "MStar MSC313 RTC" + depends on ARCH_MSTARV7 || COMPILE_TEST + default ARCH_MSTARV7 + help + If you say yes here you get support for the Mstar MSC313e On-Chip + Real Time Clock. + + This driver can also be built as a module, if so, the module + will be called "rtc-msc313". + +config RTC_DRV_POLARFIRE_SOC + tristate "Microchip PolarFire SoC built-in RTC" + depends on ARCH_MICROCHIP_POLARFIRE + help + If you say yes here you will get support for the + built-in RTC on Polarfire SoC. + + This driver can also be built as a module, if so, the module + will be called "rtc-mpfs". + +config RTC_DRV_SSD202D + tristate "SigmaStar SSD202D RTC" + depends on ARCH_MSTARV7 || COMPILE_TEST + default ARCH_MSTARV7 + help + If you say yes here you get support for the SigmaStar SSD202D On-Chip + Real Time Clock. + + This driver can also be built as a module, if so, the module + will be called "rtc-ssd20xd". + +config RTC_DRV_AMLOGIC_A4 + tristate "Amlogic RTC" + depends on ARCH_MESON || COMPILE_TEST + select REGMAP_MMIO + default ARCH_MESON + help + If you say yes here you get support for the RTC block on the + Amlogic A113L2(A4) and A113X2(A5) SoCs. + + This driver can also be built as a module. If so, the module + will be called "rtc-amlogic-a4". + +config RTC_DRV_S32G + tristate "RTC driver for S32G2/S32G3 SoCs" + depends on ARCH_S32 || COMPILE_TEST + depends on COMMON_CLK + help + Say yes to enable RTC driver for platforms based on the + S32G2/S32G3 SoC family. + + This RTC module can be used as a wakeup source. + Please note that it is not battery-powered. + +comment "HID Sensor RTC drivers" + +config RTC_DRV_HID_SENSOR_TIME + tristate "HID Sensor Time" + depends on USB_HID + depends on HID_SENSOR_HUB && IIO + select HID_SENSOR_IIO_COMMON + help + Say yes here to build support for the HID Sensors of type Time. + This drivers makes such sensors available as RTCs. + + If this driver is compiled as a module, it will be named + rtc-hid-sensor-time. + +endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile new file mode 100644 index 000000000..0347645b0 --- /dev/null +++ b/drivers/rtc/Makefile @@ -0,0 +1,201 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for RTC class/drivers. +# + +ccflags-$(CONFIG_RTC_DEBUG) := -DDEBUG + +obj-$(CONFIG_RTC_LIB) += lib.o +obj-$(CONFIG_RTC_CLASS) += rtc-core.o +obj-$(CONFIG_RTC_MC146818_LIB) += rtc-mc146818-lib.o +rtc-core-y := class.o interface.o + +rtc-core-$(CONFIG_RTC_NVMEM) += nvmem.o +rtc-core-$(CONFIG_RTC_INTF_DEV) += dev.o +rtc-core-$(CONFIG_RTC_INTF_PROC) += proc.o +rtc-core-$(CONFIG_RTC_INTF_SYSFS) += sysfs.o + +obj-$(CONFIG_RTC_LIB_KUNIT_TEST) += test_rtc_lib.o + +# Keep the list ordered. + +obj-$(CONFIG_RTC_DRV_88PM80X) += rtc-88pm80x.o +obj-$(CONFIG_RTC_DRV_88PM860X) += rtc-88pm860x.o +obj-$(CONFIG_RTC_DRV_88PM886) += rtc-88pm886.o +obj-$(CONFIG_RTC_DRV_AB8500) += rtc-ab8500.o +obj-$(CONFIG_RTC_DRV_ABB5ZES3) += rtc-ab-b5ze-s3.o +obj-$(CONFIG_RTC_DRV_ABEOZ9) += rtc-ab-eoz9.o +obj-$(CONFIG_RTC_DRV_ABX80X) += rtc-abx80x.o +obj-$(CONFIG_RTC_DRV_AC100) += rtc-ac100.o +obj-$(CONFIG_RTC_DRV_AMLOGIC_A4) += rtc-amlogic-a4.o +obj-$(CONFIG_RTC_DRV_ARMADA38X) += rtc-armada38x.o +obj-$(CONFIG_RTC_DRV_AS3722) += rtc-as3722.o +obj-$(CONFIG_RTC_DRV_ASM9260) += rtc-asm9260.o +obj-$(CONFIG_RTC_DRV_ASPEED) += rtc-aspeed.o +obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o +obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o +obj-$(CONFIG_RTC_DRV_ATCRTC100) += rtc-atcrtc100.o +obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o +obj-$(CONFIG_RTC_DRV_BBNSM) += rtc-nxp-bbnsm.o +obj-$(CONFIG_RTC_DRV_BD70528) += rtc-bd70528.o +obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o +obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o +obj-$(CONFIG_RTC_DRV_BRCMSTB) += rtc-brcmstb-waketimer.o +obj-$(CONFIG_RTC_DRV_CADENCE) += rtc-cadence.o +obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o +obj-$(CONFIG_RTC_DRV_CPCAP) += rtc-cpcap.o +obj-$(CONFIG_RTC_DRV_CROS_EC) += rtc-cros-ec.o +obj-$(CONFIG_RTC_DRV_CV1800) += rtc-cv1800.o +obj-$(CONFIG_RTC_DRV_DA9052) += rtc-da9052.o +obj-$(CONFIG_RTC_DRV_DA9055) += rtc-da9055.o +obj-$(CONFIG_RTC_DRV_DA9063) += rtc-da9063.o +obj-$(CONFIG_RTC_DRV_DIGICOLOR) += rtc-digicolor.o +obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o +obj-$(CONFIG_RTC_DRV_DS1286) += rtc-ds1286.o +obj-$(CONFIG_RTC_DRV_DS1302) += rtc-ds1302.o +obj-$(CONFIG_RTC_DRV_DS1305) += rtc-ds1305.o +obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o +obj-$(CONFIG_RTC_DRV_DS1343) += rtc-ds1343.o +obj-$(CONFIG_RTC_DRV_DS1347) += rtc-ds1347.o +obj-$(CONFIG_RTC_DRV_DS1374) += rtc-ds1374.o +obj-$(CONFIG_RTC_DRV_DS1390) += rtc-ds1390.o +obj-$(CONFIG_RTC_DRV_DS1511) += rtc-ds1511.o +obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o +obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o +obj-$(CONFIG_RTC_DRV_DS1685_FAMILY) += rtc-ds1685.o +obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o +obj-$(CONFIG_RTC_DRV_DS2404) += rtc-ds2404.o +obj-$(CONFIG_RTC_DRV_DS3232) += rtc-ds3232.o +obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o +obj-$(CONFIG_RTC_DRV_EM3027) += rtc-em3027.o +obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o +obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o +obj-$(CONFIG_RTC_DRV_FSL_FTM_ALARM) += rtc-fsl-ftm-alarm.o +obj-$(CONFIG_RTC_DRV_FTRTC010) += rtc-ftrtc010.o +obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o +obj-$(CONFIG_RTC_DRV_GOLDFISH) += rtc-goldfish.o +obj-$(CONFIG_RTC_DRV_HID_SENSOR_TIME) += rtc-hid-sensor-time.o +obj-$(CONFIG_RTC_DRV_HYM8563) += rtc-hym8563.o +obj-$(CONFIG_RTC_DRV_IMXDI) += rtc-imxdi.o +obj-$(CONFIG_RTC_DRV_IMX_SC) += rtc-imx-sc.o +obj-$(CONFIG_RTC_DRV_IMX_BBM_SCMI) += rtc-imx-sm-bbm.o +obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o +obj-$(CONFIG_RTC_DRV_ISL12026) += rtc-isl12026.o +obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o +obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o +obj-$(CONFIG_RTC_DRV_LOONGSON) += rtc-loongson.o +obj-$(CONFIG_RTC_DRV_LP8788) += rtc-lp8788.o +obj-$(CONFIG_RTC_DRV_LPC24XX) += rtc-lpc24xx.o +obj-$(CONFIG_RTC_DRV_LPC32XX) += rtc-lpc32xx.o +obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o +obj-$(CONFIG_RTC_DRV_M41T93) += rtc-m41t93.o +obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o +obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o +obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o +obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o +obj-$(CONFIG_RTC_DRV_MA35D1) += rtc-ma35d1.o +obj-$(CONFIG_RTC_DRV_MACSMC) += rtc-macsmc.o +obj-$(CONFIG_RTC_DRV_MAX31335) += rtc-max31335.o +obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o +obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o +obj-$(CONFIG_RTC_DRV_MAX6916) += rtc-max6916.o +obj-$(CONFIG_RTC_DRV_MAX77686) += rtc-max77686.o +obj-$(CONFIG_RTC_DRV_MAX8907) += rtc-max8907.o +obj-$(CONFIG_RTC_DRV_MAX8925) += rtc-max8925.o +obj-$(CONFIG_RTC_DRV_MAX8997) += rtc-max8997.o +obj-$(CONFIG_RTC_DRV_MAX8998) += rtc-max8998.o +obj-$(CONFIG_RTC_DRV_MESON_VRTC)+= rtc-meson-vrtc.o +obj-$(CONFIG_RTC_DRV_MC13XXX) += rtc-mc13xxx.o +obj-$(CONFIG_RTC_DRV_MCP795) += rtc-mcp795.o +obj-$(CONFIG_RTC_DRV_MESON) += rtc-meson.o +obj-$(CONFIG_RTC_DRV_MOXART) += rtc-moxart.o +obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o +obj-$(CONFIG_RTC_DRV_MSC313) += rtc-msc313.o +obj-$(CONFIG_RTC_DRV_SSD202D) += rtc-ssd202d.o +obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o +obj-$(CONFIG_RTC_DRV_MT2712) += rtc-mt2712.o +obj-$(CONFIG_RTC_DRV_MT6397) += rtc-mt6397.o +obj-$(CONFIG_RTC_DRV_MT7622) += rtc-mt7622.o +obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o +obj-$(CONFIG_RTC_DRV_MXC) += rtc-mxc.o +obj-$(CONFIG_RTC_DRV_MXC_V2) += rtc-mxc_v2.o +obj-$(CONFIG_RTC_DRV_GAMECUBE) += rtc-gamecube.o +obj-$(CONFIG_RTC_DRV_NCT3018Y) += rtc-nct3018y.o +obj-$(CONFIG_RTC_DRV_NCT6694) += rtc-nct6694.o +obj-$(CONFIG_RTC_DRV_NTXEC) += rtc-ntxec.o +obj-$(CONFIG_RTC_DRV_NVIDIA_VRS10)+= rtc-nvidia-vrs10.o +obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o +obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o +obj-$(CONFIG_RTC_DRV_OPTEE) += rtc-optee.o +obj-$(CONFIG_RTC_DRV_PALMAS) += rtc-palmas.o +obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o +obj-$(CONFIG_RTC_DRV_PCF2127) += rtc-pcf2127.o +obj-$(CONFIG_RTC_DRV_PCF85063) += rtc-pcf85063.o +obj-$(CONFIG_RTC_DRV_PCF8523) += rtc-pcf8523.o +obj-$(CONFIG_RTC_DRV_PCF85363) += rtc-pcf85363.o +obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o +obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o +obj-$(CONFIG_RTC_DRV_PIC32) += rtc-pic32.o +obj-$(CONFIG_RTC_DRV_PL030) += rtc-pl030.o +obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o +obj-$(CONFIG_RTC_DRV_PM8XXX) += rtc-pm8xxx.o +obj-$(CONFIG_RTC_DRV_POLARFIRE_SOC) += rtc-mpfs.o +obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o +obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o +obj-$(CONFIG_RTC_DRV_R7301) += rtc-r7301.o +obj-$(CONFIG_RTC_DRV_R9701) += rtc-r9701.o +obj-$(CONFIG_RTC_DRV_RC5T583) += rtc-rc5t583.o +obj-$(CONFIG_RTC_DRV_RC5T619) += rtc-rc5t619.o +obj-$(CONFIG_RTC_DRV_RK808) += rtc-rk808.o +obj-$(CONFIG_RTC_DRV_RP5C01) += rtc-rp5c01.o +obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o +obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o +obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o +obj-$(CONFIG_RTC_DRV_RTD119X) += rtc-rtd119x.o +obj-$(CONFIG_RTC_DRV_RV3028) += rtc-rv3028.o +obj-$(CONFIG_RTC_DRV_RV3029C2) += rtc-rv3029c2.o +obj-$(CONFIG_RTC_DRV_RV3032) += rtc-rv3032.o +obj-$(CONFIG_RTC_DRV_RV8803) += rtc-rv8803.o +obj-$(CONFIG_RTC_DRV_RX4581) += rtc-rx4581.o +obj-$(CONFIG_RTC_DRV_RX6110) += rtc-rx6110.o +obj-$(CONFIG_RTC_DRV_RX8010) += rtc-rx8010.o +obj-$(CONFIG_RTC_DRV_RX8025) += rtc-rx8025.o +obj-$(CONFIG_RTC_DRV_RX8111) += rtc-rx8111.o +obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o +obj-$(CONFIG_RTC_DRV_RZN1) += rtc-rzn1.o +obj-$(CONFIG_RTC_DRV_RENESAS_RTCA3) += rtc-renesas-rtca3.o +obj-$(CONFIG_RTC_DRV_S32G) += rtc-s32g.o +obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o +obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o +obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o +obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o +obj-$(CONFIG_RTC_DRV_SC27XX) += rtc-sc27xx.o +obj-$(CONFIG_RTC_DRV_SD2405AL) += rtc-sd2405al.o +obj-$(CONFIG_RTC_DRV_SD3078) += rtc-sd3078.o +obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o +obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o +obj-$(CONFIG_RTC_DRV_SPACEMIT_P1) += rtc-spacemit-p1.o +obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o +obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o +obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o +obj-$(CONFIG_RTC_DRV_ST_LPC) += rtc-st-lpc.o +obj-$(CONFIG_RTC_DRV_STM32) += rtc-stm32.o +obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o +obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o +obj-$(CONFIG_RTC_DRV_SUN6I) += rtc-sun6i.o +obj-$(CONFIG_RTC_DRV_SUNPLUS) += rtc-sunplus.o +obj-$(CONFIG_RTC_DRV_SUNXI) += rtc-sunxi.o +obj-$(CONFIG_RTC_DRV_TEGRA) += rtc-tegra.o +obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o +obj-$(CONFIG_RTC_DRV_TI_K3) += rtc-ti-k3.o +obj-$(CONFIG_RTC_DRV_TPS6586X) += rtc-tps6586x.o +obj-$(CONFIG_RTC_DRV_TPS6594) += rtc-tps6594.o +obj-$(CONFIG_RTC_DRV_TPS65910) += rtc-tps65910.o +obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl.o +obj-$(CONFIG_RTC_DRV_VT8500) += rtc-vt8500.o +obj-$(CONFIG_RTC_DRV_WILCO_EC) += rtc-wilco-ec.o +obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o +obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o +obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o +obj-$(CONFIG_RTC_DRV_XGENE) += rtc-xgene.o +obj-$(CONFIG_RTC_DRV_ZYNQMP) += rtc-zynqmp.o diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c new file mode 100644 index 000000000..01ba04028 --- /dev/null +++ b/drivers/rtc/class.c @@ -0,0 +1,491 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RTC subsystem, base class + * + * Copyright (C) 2005 Tower Technologies + * Author: Alessandro Zummo + * + * class skeleton from drivers/hwmon/hwmon.c + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include + +#include "rtc-core.h" + +static DEFINE_IDA(rtc_ida); + +static void rtc_device_release(struct device *dev) +{ + struct rtc_device *rtc = to_rtc_device(dev); + struct timerqueue_head *head = &rtc->timerqueue; + struct timerqueue_node *node; + + mutex_lock(&rtc->ops_lock); + while ((node = timerqueue_getnext(head))) + timerqueue_del(head, node); + mutex_unlock(&rtc->ops_lock); + + cancel_work_sync(&rtc->irqwork); + + ida_free(&rtc_ida, rtc->id); + mutex_destroy(&rtc->ops_lock); + kfree(rtc); +} + +#ifdef CONFIG_RTC_HCTOSYS_DEVICE +/* Result of the last RTC to system clock attempt. */ +int rtc_hctosys_ret = -ENODEV; + +/* IMPORTANT: the RTC only stores whole seconds. It is arbitrary + * whether it stores the most close value or the value with partial + * seconds truncated. However, it is important that we use it to store + * the truncated value. This is because otherwise it is necessary, + * in an rtc sync function, to read both xtime.tv_sec and + * xtime.tv_nsec. On some processors (i.e. ARM), an atomic read + * of >32bits is not possible. So storing the most close value would + * slow down the sync API. So here we have the truncated value and + * the best guess is to add 0.5s. + */ + +static void rtc_hctosys(struct rtc_device *rtc) +{ + int err; + struct rtc_time tm; + struct timespec64 tv64 = { + .tv_nsec = NSEC_PER_SEC >> 1, + }; + + err = rtc_read_time(rtc, &tm); + if (err) { + dev_err(rtc->dev.parent, + "hctosys: unable to read the hardware clock\n"); + goto err_read; + } + + tv64.tv_sec = rtc_tm_to_time64(&tm); + +#if BITS_PER_LONG == 32 + if (tv64.tv_sec > INT_MAX) { + err = -ERANGE; + goto err_read; + } +#endif + + err = do_settimeofday64(&tv64); + + dev_info(rtc->dev.parent, "setting system clock to %ptR UTC (%lld)\n", + &tm, (long long)tv64.tv_sec); + +err_read: + rtc_hctosys_ret = err; +} +#endif + +#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_RTC_HCTOSYS_DEVICE) +/* + * On suspend(), measure the delta between one RTC and the + * system's wall clock; restore it on resume(). + */ + +static struct timespec64 old_rtc, old_system, old_delta; + +static int rtc_suspend(struct device *dev) +{ + struct rtc_device *rtc = to_rtc_device(dev); + struct rtc_time tm; + struct timespec64 delta, delta_delta; + int err; + + if (timekeeping_rtc_skipsuspend()) + return 0; + + if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0) + return 0; + + /* snapshot the current RTC and system time at suspend*/ + err = rtc_read_time(rtc, &tm); + if (err < 0) { + pr_debug("%s: fail to read rtc time\n", dev_name(&rtc->dev)); + return 0; + } + + ktime_get_real_ts64(&old_system); + old_rtc.tv_sec = rtc_tm_to_time64(&tm); + + /* + * To avoid drift caused by repeated suspend/resumes, + * which each can add ~1 second drift error, + * try to compensate so the difference in system time + * and rtc time stays close to constant. + */ + delta = timespec64_sub(old_system, old_rtc); + delta_delta = timespec64_sub(delta, old_delta); + if (delta_delta.tv_sec < -2 || delta_delta.tv_sec >= 2) { + /* + * if delta_delta is too large, assume time correction + * has occurred and set old_delta to the current delta. + */ + old_delta = delta; + } else { + /* Otherwise try to adjust old_system to compensate */ + old_system = timespec64_sub(old_system, delta_delta); + } + + return 0; +} + +static int rtc_resume(struct device *dev) +{ + struct rtc_device *rtc = to_rtc_device(dev); + struct rtc_time tm; + struct timespec64 new_system, new_rtc; + struct timespec64 sleep_time; + int err; + + if (timekeeping_rtc_skipresume()) + return 0; + + rtc_hctosys_ret = -ENODEV; + if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0) + return 0; + + /* snapshot the current rtc and system time at resume */ + ktime_get_real_ts64(&new_system); + err = rtc_read_time(rtc, &tm); + if (err < 0) { + pr_debug("%s: fail to read rtc time\n", dev_name(&rtc->dev)); + return 0; + } + + new_rtc.tv_sec = rtc_tm_to_time64(&tm); + new_rtc.tv_nsec = 0; + + if (new_rtc.tv_sec < old_rtc.tv_sec) { + pr_debug("%s: time travel!\n", dev_name(&rtc->dev)); + return 0; + } + + /* calculate the RTC time delta (sleep time)*/ + sleep_time = timespec64_sub(new_rtc, old_rtc); + + /* + * Since these RTC suspend/resume handlers are not called + * at the very end of suspend or the start of resume, + * some run-time may pass on either sides of the sleep time + * so subtract kernel run-time between rtc_suspend to rtc_resume + * to keep things accurate. + */ + sleep_time = timespec64_sub(sleep_time, + timespec64_sub(new_system, old_system)); + + if (sleep_time.tv_sec >= 0) + timekeeping_inject_sleeptime64(&sleep_time); + rtc_hctosys_ret = 0; + return 0; +} + +static SIMPLE_DEV_PM_OPS(rtc_class_dev_pm_ops, rtc_suspend, rtc_resume); +#define RTC_CLASS_DEV_PM_OPS (&rtc_class_dev_pm_ops) +#else +#define RTC_CLASS_DEV_PM_OPS NULL +#endif + +const struct class rtc_class = { + .name = "rtc", + .pm = RTC_CLASS_DEV_PM_OPS, +}; + +/* Ensure the caller will set the id before releasing the device */ +static struct rtc_device *rtc_allocate_device(void) +{ + struct rtc_device *rtc; + + rtc = kzalloc_obj(*rtc); + if (!rtc) + return NULL; + + device_initialize(&rtc->dev); + + /* + * Drivers can revise this default after allocating the device. + * The default is what most RTCs do: Increment seconds exactly one + * second after the write happened. This adds a default transport + * time of 5ms which is at least halfways close to reality. + */ + rtc->set_offset_nsec = NSEC_PER_SEC + 5 * NSEC_PER_MSEC; + + rtc->irq_freq = 1; + rtc->max_user_freq = 64; + rtc->dev.class = &rtc_class; + rtc->dev.groups = rtc_get_dev_attribute_groups(); + rtc->dev.release = rtc_device_release; + + mutex_init(&rtc->ops_lock); + spin_lock_init(&rtc->irq_lock); + init_waitqueue_head(&rtc->irq_queue); + + /* Init timerqueue */ + timerqueue_init_head(&rtc->timerqueue); + INIT_WORK(&rtc->irqwork, rtc_timer_do_work); + /* Init aie timer */ + rtc_timer_init(&rtc->aie_timer, rtc_aie_update_irq, rtc); + /* Init uie timer */ + rtc_timer_init(&rtc->uie_rtctimer, rtc_uie_update_irq, rtc); + /* Init pie timer */ + hrtimer_setup(&rtc->pie_timer, rtc_pie_update_irq, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + rtc->pie_enabled = 0; + + set_bit(RTC_FEATURE_ALARM, rtc->features); + set_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features); + + return rtc; +} + +static int rtc_device_get_id(struct device *dev) +{ + int of_id = -1, id = -1; + + if (dev->of_node) + of_id = of_alias_get_id(dev->of_node, "rtc"); + else if (dev->parent && dev->parent->of_node) + of_id = of_alias_get_id(dev->parent->of_node, "rtc"); + + if (of_id >= 0) { + id = ida_alloc_range(&rtc_ida, of_id, of_id, GFP_KERNEL); + if (id < 0) + dev_warn(dev, "/aliases ID %d not available\n", of_id); + } + + if (id < 0) + id = ida_alloc(&rtc_ida, GFP_KERNEL); + + return id; +} + +static void rtc_device_get_offset(struct rtc_device *rtc) +{ + time64_t range_secs; + u32 start_year; + int ret; + + /* + * If RTC driver did not implement the range of RTC hardware device, + * then we can not expand the RTC range by adding or subtracting one + * offset. + */ + if (rtc->range_min == rtc->range_max) + return; + + ret = device_property_read_u32(rtc->dev.parent, "start-year", + &start_year); + if (!ret) { + rtc->start_secs = mktime64(start_year, 1, 1, 0, 0, 0); + rtc->set_start_time = true; + } + + /* + * If user did not implement the start time for RTC driver, then no + * need to expand the RTC range. + */ + if (!rtc->set_start_time) + return; + + range_secs = rtc->range_max - rtc->range_min + 1; + + /* + * If the start_secs is larger than the maximum seconds (rtc->range_max) + * supported by RTC hardware or the maximum seconds of new expanded + * range (start_secs + rtc->range_max - rtc->range_min) is less than + * rtc->range_min, which means the minimum seconds (rtc->range_min) of + * RTC hardware will be mapped to start_secs by adding one offset, so + * the offset seconds calculation formula should be: + * rtc->offset_secs = rtc->start_secs - rtc->range_min; + * + * If the start_secs is larger than the minimum seconds (rtc->range_min) + * supported by RTC hardware, then there is one region is overlapped + * between the original RTC hardware range and the new expanded range, + * and this overlapped region do not need to be mapped into the new + * expanded range due to it is valid for RTC device. So the minimum + * seconds of RTC hardware (rtc->range_min) should be mapped to + * rtc->range_max + 1, then the offset seconds formula should be: + * rtc->offset_secs = rtc->range_max - rtc->range_min + 1; + * + * If the start_secs is less than the minimum seconds (rtc->range_min), + * which is similar to case 2. So the start_secs should be mapped to + * start_secs + rtc->range_max - rtc->range_min + 1, then the + * offset seconds formula should be: + * rtc->offset_secs = -(rtc->range_max - rtc->range_min + 1); + * + * Otherwise the offset seconds should be 0. + */ + if ((rtc->start_secs >= 0 && rtc->start_secs > rtc->range_max) || + rtc->start_secs + range_secs - 1 < rtc->range_min) + rtc->offset_secs = rtc->start_secs - rtc->range_min; + else if (rtc->start_secs > rtc->range_min) + rtc->offset_secs = range_secs; + else if (rtc->start_secs < rtc->range_min) + rtc->offset_secs = -range_secs; + else + rtc->offset_secs = 0; +} + +static void devm_rtc_unregister_device(void *data) +{ + struct rtc_device *rtc = data; + + mutex_lock(&rtc->ops_lock); + /* + * Remove innards of this RTC, then disable it, before + * letting any rtc_class_open() users access it again + */ + rtc_proc_del_device(rtc); + if (!test_bit(RTC_NO_CDEV, &rtc->flags)) + cdev_device_del(&rtc->char_dev, &rtc->dev); + rtc->ops = NULL; + mutex_unlock(&rtc->ops_lock); +} + +static void devm_rtc_release_device(void *res) +{ + struct rtc_device *rtc = res; + + put_device(&rtc->dev); +} + +struct rtc_device *devm_rtc_allocate_device(struct device *dev) +{ + struct rtc_device *rtc; + int id, err; + + id = rtc_device_get_id(dev); + if (id < 0) + return ERR_PTR(id); + + rtc = rtc_allocate_device(); + if (!rtc) { + ida_free(&rtc_ida, id); + return ERR_PTR(-ENOMEM); + } + + rtc->id = id; + rtc->dev.parent = dev; + err = devm_add_action_or_reset(dev, devm_rtc_release_device, rtc); + if (err) + return ERR_PTR(err); + + err = dev_set_name(&rtc->dev, "rtc%d", id); + if (err) + return ERR_PTR(err); + + return rtc; +} +EXPORT_SYMBOL_GPL(devm_rtc_allocate_device); + +int __devm_rtc_register_device(struct module *owner, struct rtc_device *rtc) +{ + struct rtc_wkalrm alrm; + int err; + + if (!rtc->ops) { + dev_dbg(&rtc->dev, "no ops set\n"); + return -EINVAL; + } + + if (!rtc->ops->set_alarm) + clear_bit(RTC_FEATURE_ALARM, rtc->features); + + if (rtc->ops->set_offset) + set_bit(RTC_FEATURE_CORRECTION, rtc->features); + + rtc->owner = owner; + rtc_device_get_offset(rtc); + + /* Check to see if there is an ALARM already set in hw */ + err = __rtc_read_alarm(rtc, &alrm); + if (!err) + rtc_initialize_alarm(rtc, &alrm); + + rtc_dev_prepare(rtc); + + err = cdev_device_add(&rtc->char_dev, &rtc->dev); + if (err) { + set_bit(RTC_NO_CDEV, &rtc->flags); + dev_warn(rtc->dev.parent, "failed to add char device %d:%d\n", + MAJOR(rtc->dev.devt), rtc->id); + } else { + dev_dbg(rtc->dev.parent, "char device (%d:%d)\n", + MAJOR(rtc->dev.devt), rtc->id); + } + + rtc_proc_add_device(rtc); + + dev_info(rtc->dev.parent, "registered as %s\n", + dev_name(&rtc->dev)); + +#ifdef CONFIG_RTC_HCTOSYS_DEVICE + if (!strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE)) + rtc_hctosys(rtc); +#endif + + return devm_add_action_or_reset(rtc->dev.parent, + devm_rtc_unregister_device, rtc); +} +EXPORT_SYMBOL_GPL(__devm_rtc_register_device); + +/** + * devm_rtc_device_register - resource managed rtc_device_register() + * @dev: the device to register + * @name: the name of the device (unused) + * @ops: the rtc operations structure + * @owner: the module owner + * + * @return a struct rtc on success, or an ERR_PTR on error + * + * Managed rtc_device_register(). The rtc_device returned from this function + * are automatically freed on driver detach. + * This function is deprecated, use devm_rtc_allocate_device and + * rtc_register_device instead + */ +struct rtc_device *devm_rtc_device_register(struct device *dev, + const char *name, + const struct rtc_class_ops *ops, + struct module *owner) +{ + struct rtc_device *rtc; + int err; + + rtc = devm_rtc_allocate_device(dev); + if (IS_ERR(rtc)) + return rtc; + + rtc->ops = ops; + + err = __devm_rtc_register_device(owner, rtc); + if (err) + return ERR_PTR(err); + + return rtc; +} +EXPORT_SYMBOL_GPL(devm_rtc_device_register); + +static int __init rtc_init(void) +{ + int err; + + err = class_register(&rtc_class); + if (err) + return err; + + rtc_dev_init(); + + return 0; +} +subsys_initcall(rtc_init); diff --git a/drivers/rtc/dev.c b/drivers/rtc/dev.c new file mode 100644 index 000000000..8ba7c25d2 --- /dev/null +++ b/drivers/rtc/dev.c @@ -0,0 +1,576 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RTC subsystem, dev interface + * + * Copyright (C) 2005 Tower Technologies + * Author: Alessandro Zummo + * + * based on arch/arm/common/rtctime.c + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include "rtc-core.h" + +static dev_t rtc_devt; + +#define RTC_DEV_MAX 16 /* 16 RTCs should be enough for everyone... */ + +static int rtc_dev_open(struct inode *inode, struct file *file) +{ + struct rtc_device *rtc = container_of(inode->i_cdev, + struct rtc_device, char_dev); + + if (test_and_set_bit_lock(RTC_DEV_BUSY, &rtc->flags)) + return -EBUSY; + + file->private_data = rtc; + + spin_lock_irq(&rtc->irq_lock); + rtc->irq_data = 0; + spin_unlock_irq(&rtc->irq_lock); + + return 0; +} + +#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL +/* + * Routine to poll RTC seconds field for change as often as possible, + * after first RTC_UIE use timer to reduce polling + */ +static void rtc_uie_task(struct work_struct *work) +{ + struct rtc_device *rtc = + container_of(work, struct rtc_device, uie_task); + struct rtc_time tm; + int num = 0; + int err; + + err = rtc_read_time(rtc, &tm); + + spin_lock_irq(&rtc->irq_lock); + if (rtc->stop_uie_polling || err) { + rtc->uie_task_active = 0; + } else if (rtc->oldsecs != tm.tm_sec) { + num = (tm.tm_sec + 60 - rtc->oldsecs) % 60; + rtc->oldsecs = tm.tm_sec; + rtc->uie_timer.expires = jiffies + HZ - (HZ / 10); + rtc->uie_timer_active = 1; + rtc->uie_task_active = 0; + add_timer(&rtc->uie_timer); + } else if (schedule_work(&rtc->uie_task) == 0) { + rtc->uie_task_active = 0; + } + spin_unlock_irq(&rtc->irq_lock); + if (num) + rtc_handle_legacy_irq(rtc, num, RTC_UF); +} + +static void rtc_uie_timer(struct timer_list *t) +{ + struct rtc_device *rtc = timer_container_of(rtc, t, uie_timer); + unsigned long flags; + + spin_lock_irqsave(&rtc->irq_lock, flags); + rtc->uie_timer_active = 0; + rtc->uie_task_active = 1; + if ((schedule_work(&rtc->uie_task) == 0)) + rtc->uie_task_active = 0; + spin_unlock_irqrestore(&rtc->irq_lock, flags); +} + +static int clear_uie(struct rtc_device *rtc) +{ + spin_lock_irq(&rtc->irq_lock); + if (rtc->uie_irq_active) { + rtc->stop_uie_polling = 1; + if (rtc->uie_timer_active) { + spin_unlock_irq(&rtc->irq_lock); + timer_delete_sync(&rtc->uie_timer); + spin_lock_irq(&rtc->irq_lock); + rtc->uie_timer_active = 0; + } + if (rtc->uie_task_active) { + spin_unlock_irq(&rtc->irq_lock); + flush_work(&rtc->uie_task); + spin_lock_irq(&rtc->irq_lock); + } + rtc->uie_irq_active = 0; + } + spin_unlock_irq(&rtc->irq_lock); + return 0; +} + +static int set_uie(struct rtc_device *rtc) +{ + struct rtc_time tm; + int err; + + err = rtc_read_time(rtc, &tm); + if (err) + return err; + spin_lock_irq(&rtc->irq_lock); + if (!rtc->uie_irq_active) { + rtc->uie_irq_active = 1; + rtc->stop_uie_polling = 0; + rtc->oldsecs = tm.tm_sec; + rtc->uie_task_active = 1; + if (schedule_work(&rtc->uie_task) == 0) + rtc->uie_task_active = 0; + } + rtc->irq_data = 0; + spin_unlock_irq(&rtc->irq_lock); + return 0; +} + +int rtc_dev_update_irq_enable_emul(struct rtc_device *rtc, unsigned int enabled) +{ + if (enabled) + return set_uie(rtc); + else + return clear_uie(rtc); +} +EXPORT_SYMBOL(rtc_dev_update_irq_enable_emul); + +#endif /* CONFIG_RTC_INTF_DEV_UIE_EMUL */ + +static ssize_t +rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ + struct rtc_device *rtc = file->private_data; + + DECLARE_WAITQUEUE(wait, current); + unsigned long data; + ssize_t ret; + + if (count != sizeof(unsigned int) && count < sizeof(unsigned long)) + return -EINVAL; + + add_wait_queue(&rtc->irq_queue, &wait); + do { + __set_current_state(TASK_INTERRUPTIBLE); + + spin_lock_irq(&rtc->irq_lock); + data = rtc->irq_data; + rtc->irq_data = 0; + spin_unlock_irq(&rtc->irq_lock); + + if (data != 0) { + ret = 0; + break; + } + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + break; + } + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + schedule(); + } while (1); + set_current_state(TASK_RUNNING); + remove_wait_queue(&rtc->irq_queue, &wait); + + if (ret == 0) { + if (sizeof(int) != sizeof(long) && + count == sizeof(unsigned int)) + ret = put_user(data, (unsigned int __user *)buf) ?: + sizeof(unsigned int); + else + ret = put_user(data, (unsigned long __user *)buf) ?: + sizeof(unsigned long); + } + return ret; +} + +static __poll_t rtc_dev_poll(struct file *file, poll_table *wait) +{ + struct rtc_device *rtc = file->private_data; + unsigned long data; + + poll_wait(file, &rtc->irq_queue, wait); + + /* + * This read can race with the write in rtc_handle_legacy_irq(). + * + * - If this check misses a zero to non-zero transition the next check + * will pick it up (rtc_handle_legacy_irq() wakes up rtc->irq_queue). + * - Non-zero to non-zero transition misses do not change return value. + * - And a non-zero to zero transition is unlikely to be missed, since + * it occurs on rtc_dev_read(), during which polling is not expected. + */ + data = data_race(rtc->irq_data); + + return (data != 0) ? (EPOLLIN | EPOLLRDNORM) : 0; +} + +static long rtc_dev_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err = 0; + struct rtc_device *rtc = file->private_data; + const struct rtc_class_ops *ops = rtc->ops; + struct rtc_time tm; + struct rtc_wkalrm alarm; + struct rtc_param param; + void __user *uarg = (void __user *)arg; + + err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return err; + + /* check that the calling task has appropriate permissions + * for certain ioctls. doing this check here is useful + * to avoid duplicate code in each driver. + */ + switch (cmd) { + case RTC_EPOCH_SET: + case RTC_SET_TIME: + case RTC_PARAM_SET: + if (!capable(CAP_SYS_TIME)) + err = -EACCES; + break; + + case RTC_IRQP_SET: + if (arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE)) + err = -EACCES; + break; + + case RTC_PIE_ON: + if (rtc->irq_freq > rtc->max_user_freq && + !capable(CAP_SYS_RESOURCE)) + err = -EACCES; + break; + } + + if (err) + goto done; + + /* + * Drivers *SHOULD NOT* provide ioctl implementations + * for these requests. Instead, provide methods to + * support the following code, so that the RTC's main + * features are accessible without using ioctls. + * + * RTC and alarm times will be in UTC, by preference, + * but dual-booting with MS-Windows implies RTCs must + * use the local wall clock time. + */ + + switch (cmd) { + case RTC_ALM_READ: + mutex_unlock(&rtc->ops_lock); + + err = rtc_read_alarm(rtc, &alarm); + if (err < 0) + return err; + + if (copy_to_user(uarg, &alarm.time, sizeof(tm))) + err = -EFAULT; + return err; + + case RTC_ALM_SET: + mutex_unlock(&rtc->ops_lock); + + if (copy_from_user(&alarm.time, uarg, sizeof(tm))) + return -EFAULT; + + alarm.enabled = 0; + alarm.pending = 0; + alarm.time.tm_wday = -1; + alarm.time.tm_yday = -1; + alarm.time.tm_isdst = -1; + + /* RTC_ALM_SET alarms may be up to 24 hours in the future. + * Rather than expecting every RTC to implement "don't care" + * for day/month/year fields, just force the alarm to have + * the right values for those fields. + * + * RTC_WKALM_SET should be used instead. Not only does it + * eliminate the need for a separate RTC_AIE_ON call, it + * doesn't have the "alarm 23:59:59 in the future" race. + * + * NOTE: some legacy code may have used invalid fields as + * wildcards, exposing hardware "periodic alarm" capabilities. + * Not supported here. + */ + { + time64_t now, then; + + err = rtc_read_time(rtc, &tm); + if (err < 0) + return err; + now = rtc_tm_to_time64(&tm); + + alarm.time.tm_mday = tm.tm_mday; + alarm.time.tm_mon = tm.tm_mon; + alarm.time.tm_year = tm.tm_year; + err = rtc_valid_tm(&alarm.time); + if (err < 0) + return err; + then = rtc_tm_to_time64(&alarm.time); + + /* alarm may need to wrap into tomorrow */ + if (then < now) { + rtc_time64_to_tm(now + 24 * 60 * 60, &tm); + alarm.time.tm_mday = tm.tm_mday; + alarm.time.tm_mon = tm.tm_mon; + alarm.time.tm_year = tm.tm_year; + } + } + + return rtc_set_alarm(rtc, &alarm); + + case RTC_RD_TIME: + mutex_unlock(&rtc->ops_lock); + + err = rtc_read_time(rtc, &tm); + if (err < 0) + return err; + + if (copy_to_user(uarg, &tm, sizeof(tm))) + err = -EFAULT; + return err; + + case RTC_SET_TIME: + mutex_unlock(&rtc->ops_lock); + + if (copy_from_user(&tm, uarg, sizeof(tm))) + return -EFAULT; + + return rtc_set_time(rtc, &tm); + + case RTC_PIE_ON: + err = rtc_irq_set_state(rtc, 1); + break; + + case RTC_PIE_OFF: + err = rtc_irq_set_state(rtc, 0); + break; + + case RTC_AIE_ON: + mutex_unlock(&rtc->ops_lock); + return rtc_alarm_irq_enable(rtc, 1); + + case RTC_AIE_OFF: + mutex_unlock(&rtc->ops_lock); + return rtc_alarm_irq_enable(rtc, 0); + + case RTC_UIE_ON: + mutex_unlock(&rtc->ops_lock); + return rtc_update_irq_enable(rtc, 1); + + case RTC_UIE_OFF: + mutex_unlock(&rtc->ops_lock); + return rtc_update_irq_enable(rtc, 0); + + case RTC_IRQP_SET: + err = rtc_irq_set_freq(rtc, arg); + break; + case RTC_IRQP_READ: + err = put_user(rtc->irq_freq, (unsigned long __user *)uarg); + break; + + case RTC_WKALM_SET: + mutex_unlock(&rtc->ops_lock); + if (copy_from_user(&alarm, uarg, sizeof(alarm))) + return -EFAULT; + + return rtc_set_alarm(rtc, &alarm); + + case RTC_WKALM_RD: + mutex_unlock(&rtc->ops_lock); + err = rtc_read_alarm(rtc, &alarm); + if (err < 0) + return err; + + if (copy_to_user(uarg, &alarm, sizeof(alarm))) + err = -EFAULT; + return err; + + case RTC_PARAM_GET: + if (copy_from_user(¶m, uarg, sizeof(param))) { + mutex_unlock(&rtc->ops_lock); + return -EFAULT; + } + + switch(param.param) { + case RTC_PARAM_FEATURES: + if (param.index != 0) + err = -EINVAL; + param.uvalue = rtc->features[0]; + break; + + case RTC_PARAM_CORRECTION: { + long offset; + mutex_unlock(&rtc->ops_lock); + if (param.index != 0) + return -EINVAL; + err = rtc_read_offset(rtc, &offset); + mutex_lock(&rtc->ops_lock); + if (err == 0) + param.svalue = offset; + break; + } + default: + if (rtc->ops->param_get) + err = rtc->ops->param_get(rtc->dev.parent, ¶m); + else + err = -EINVAL; + } + + if (!err) + if (copy_to_user(uarg, ¶m, sizeof(param))) + err = -EFAULT; + + break; + + case RTC_PARAM_SET: + if (copy_from_user(¶m, uarg, sizeof(param))) { + mutex_unlock(&rtc->ops_lock); + return -EFAULT; + } + + switch(param.param) { + case RTC_PARAM_FEATURES: + err = -EINVAL; + break; + + case RTC_PARAM_CORRECTION: + mutex_unlock(&rtc->ops_lock); + if (param.index != 0) + return -EINVAL; + return rtc_set_offset(rtc, param.svalue); + + default: + if (rtc->ops->param_set) + err = rtc->ops->param_set(rtc->dev.parent, ¶m); + else + err = -EINVAL; + } + + break; + + default: + /* Finally try the driver's ioctl interface */ + if (ops->ioctl) { + err = ops->ioctl(rtc->dev.parent, cmd, arg); + if (err == -ENOIOCTLCMD) + err = -ENOTTY; + } else { + err = -ENOTTY; + } + break; + } + +done: + mutex_unlock(&rtc->ops_lock); + return err; +} + +#ifdef CONFIG_COMPAT +#define RTC_IRQP_SET32 _IOW('p', 0x0c, __u32) +#define RTC_IRQP_READ32 _IOR('p', 0x0b, __u32) +#define RTC_EPOCH_SET32 _IOW('p', 0x0e, __u32) + +static long rtc_dev_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct rtc_device *rtc = file->private_data; + void __user *uarg = compat_ptr(arg); + + switch (cmd) { + case RTC_IRQP_READ32: + return put_user(rtc->irq_freq, (__u32 __user *)uarg); + + case RTC_IRQP_SET32: + /* arg is a plain integer, not pointer */ + return rtc_dev_ioctl(file, RTC_IRQP_SET, arg); + + case RTC_EPOCH_SET32: + /* arg is a plain integer, not pointer */ + return rtc_dev_ioctl(file, RTC_EPOCH_SET, arg); + } + + return rtc_dev_ioctl(file, cmd, (unsigned long)uarg); +} +#endif + +static int rtc_dev_fasync(int fd, struct file *file, int on) +{ + struct rtc_device *rtc = file->private_data; + + return fasync_helper(fd, file, on, &rtc->async_queue); +} + +static int rtc_dev_release(struct inode *inode, struct file *file) +{ + struct rtc_device *rtc = file->private_data; + + /* We shut down the repeating IRQs that userspace enabled, + * since nothing is listening to them. + * - Update (UIE) ... currently only managed through ioctls + * - Periodic (PIE) ... also used through rtc_*() interface calls + * + * Leave the alarm alone; it may be set to trigger a system wakeup + * later, or be used by kernel code, and is a one-shot event anyway. + */ + + /* Keep ioctl until all drivers are converted */ + rtc_dev_ioctl(file, RTC_UIE_OFF, 0); + rtc_update_irq_enable(rtc, 0); + rtc_irq_set_state(rtc, 0); + + clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags); + return 0; +} + +static const struct file_operations rtc_dev_fops = { + .owner = THIS_MODULE, + .read = rtc_dev_read, + .poll = rtc_dev_poll, + .unlocked_ioctl = rtc_dev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = rtc_dev_compat_ioctl, +#endif + .open = rtc_dev_open, + .release = rtc_dev_release, + .fasync = rtc_dev_fasync, +}; + +/* insertion/removal hooks */ + +void rtc_dev_prepare(struct rtc_device *rtc) +{ + if (!rtc_devt) + return; + + if (rtc->id >= RTC_DEV_MAX) { + dev_dbg(&rtc->dev, "too many RTC devices\n"); + return; + } + + rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id); + +#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL + INIT_WORK(&rtc->uie_task, rtc_uie_task); + timer_setup(&rtc->uie_timer, rtc_uie_timer, 0); +#endif + + cdev_init(&rtc->char_dev, &rtc_dev_fops); + rtc->char_dev.owner = rtc->owner; +} + +void __init rtc_dev_init(void) +{ + int err; + + err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc"); + if (err < 0) + pr_err("failed to allocate char dev region\n"); +} diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c new file mode 100644 index 000000000..96626f806 --- /dev/null +++ b/drivers/rtc/interface.c @@ -0,0 +1,1159 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RTC subsystem, interface functions + * + * Copyright (C) 2005 Tower Technologies + * Author: Alessandro Zummo + * + * based on arch/arm/common/rtctime.c + */ + +#include +#include +#include +#include +#include + +#define CREATE_TRACE_POINTS +#include + +static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer); +static void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer); + +static void rtc_add_offset(struct rtc_device *rtc, struct rtc_time *tm) +{ + time64_t secs; + + if (!rtc->offset_secs) + return; + + secs = rtc_tm_to_time64(tm); + + /* + * Since the reading time values from RTC device are always in the RTC + * original valid range, but we need to skip the overlapped region + * between expanded range and original range, which is no need to add + * the offset. + */ + if ((rtc->start_secs > rtc->range_min && secs >= rtc->start_secs) || + (rtc->start_secs < rtc->range_min && + secs <= (rtc->start_secs + rtc->range_max - rtc->range_min))) + return; + + rtc_time64_to_tm(secs + rtc->offset_secs, tm); +} + +static void rtc_subtract_offset(struct rtc_device *rtc, struct rtc_time *tm) +{ + time64_t secs; + + if (!rtc->offset_secs) + return; + + secs = rtc_tm_to_time64(tm); + + /* + * If the setting time values are in the valid range of RTC hardware + * device, then no need to subtract the offset when setting time to RTC + * device. Otherwise we need to subtract the offset to make the time + * values are valid for RTC hardware device. + */ + if (secs >= rtc->range_min && secs <= rtc->range_max) + return; + + rtc_time64_to_tm(secs - rtc->offset_secs, tm); +} + +static int rtc_valid_range(struct rtc_device *rtc, struct rtc_time *tm) +{ + if (rtc->range_min != rtc->range_max) { + time64_t time = rtc_tm_to_time64(tm); + time64_t range_min = rtc->set_start_time ? rtc->start_secs : + rtc->range_min; + timeu64_t range_max = rtc->set_start_time ? + (rtc->start_secs + rtc->range_max - rtc->range_min) : + rtc->range_max; + + if (time < range_min || time > range_max) + return -ERANGE; + } + + return 0; +} + +static int __rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm) +{ + int err; + + if (!rtc->ops) { + err = -ENODEV; + } else if (!rtc->ops->read_time) { + err = -EINVAL; + } else { + memset(tm, 0, sizeof(struct rtc_time)); + err = rtc->ops->read_time(rtc->dev.parent, tm); + if (err < 0) { + dev_dbg(&rtc->dev, "read_time: fail to read: %d\n", + err); + return err; + } + + rtc_add_offset(rtc, tm); + + err = rtc_valid_tm(tm); + if (err < 0) + dev_dbg(&rtc->dev, "read_time: rtc_time isn't valid\n"); + } + return err; +} + +int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm) +{ + int err; + + err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return err; + + err = __rtc_read_time(rtc, tm); + mutex_unlock(&rtc->ops_lock); + + trace_rtc_read_time(rtc_tm_to_time64(tm), err); + return err; +} +EXPORT_SYMBOL_GPL(rtc_read_time); + +int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm) +{ + int err, uie; + + err = rtc_valid_tm(tm); + if (err != 0) + return err; + + err = rtc_valid_range(rtc, tm); + if (err) + return err; + + rtc_subtract_offset(rtc, tm); + +#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL + uie = rtc->uie_rtctimer.enabled || rtc->uie_irq_active; +#else + uie = rtc->uie_rtctimer.enabled; +#endif + if (uie) { + err = rtc_update_irq_enable(rtc, 0); + if (err) + return err; + } + + err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return err; + + if (!rtc->ops) + err = -ENODEV; + else if (rtc->ops->set_time) + err = rtc->ops->set_time(rtc->dev.parent, tm); + else + err = -EINVAL; + + pm_stay_awake(rtc->dev.parent); + mutex_unlock(&rtc->ops_lock); + /* A timer might have just expired */ + schedule_work(&rtc->irqwork); + + if (uie) { + err = rtc_update_irq_enable(rtc, 1); + if (err) + return err; + } + + trace_rtc_set_time(rtc_tm_to_time64(tm), err); + return err; +} +EXPORT_SYMBOL_GPL(rtc_set_time); + +static int rtc_read_alarm_internal(struct rtc_device *rtc, + struct rtc_wkalrm *alarm) +{ + int err; + + err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return err; + + if (!rtc->ops) { + err = -ENODEV; + } else if (!test_bit(RTC_FEATURE_ALARM, rtc->features) || !rtc->ops->read_alarm) { + err = -EINVAL; + } else { + alarm->enabled = 0; + alarm->pending = 0; + alarm->time.tm_sec = -1; + alarm->time.tm_min = -1; + alarm->time.tm_hour = -1; + alarm->time.tm_mday = -1; + alarm->time.tm_mon = -1; + alarm->time.tm_year = -1; + alarm->time.tm_wday = -1; + alarm->time.tm_yday = -1; + alarm->time.tm_isdst = -1; + err = rtc->ops->read_alarm(rtc->dev.parent, alarm); + } + + mutex_unlock(&rtc->ops_lock); + + trace_rtc_read_alarm(err?0:rtc_tm_to_time64(&alarm->time), err); + return err; +} + +int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) +{ + int err; + struct rtc_time before, now; + int first_time = 1; + time64_t t_now, t_alm; + enum { none, day, month, year } missing = none; + unsigned int days; + + /* The lower level RTC driver may return -1 in some fields, + * creating invalid alarm->time values, for reasons like: + * + * - The hardware may not be capable of filling them in; + * many alarms match only on time-of-day fields, not + * day/month/year calendar data. + * + * - Some hardware uses illegal values as "wildcard" match + * values, which non-Linux firmware (like a BIOS) may try + * to set up as e.g. "alarm 15 minutes after each hour". + * Linux uses only oneshot alarms. + * + * When we see that here, we deal with it by using values from + * a current RTC timestamp for any missing (-1) values. The + * RTC driver prevents "periodic alarm" modes. + * + * But this can be racey, because some fields of the RTC timestamp + * may have wrapped in the interval since we read the RTC alarm, + * which would lead to us inserting inconsistent values in place + * of the -1 fields. + * + * Reading the alarm and timestamp in the reverse sequence + * would have the same race condition, and not solve the issue. + * + * So, we must first read the RTC timestamp, + * then read the RTC alarm value, + * and then read a second RTC timestamp. + * + * If any fields of the second timestamp have changed + * when compared with the first timestamp, then we know + * our timestamp may be inconsistent with that used by + * the low-level rtc_read_alarm_internal() function. + * + * So, when the two timestamps disagree, we just loop and do + * the process again to get a fully consistent set of values. + * + * This could all instead be done in the lower level driver, + * but since more than one lower level RTC implementation needs it, + * then it's probably best to do it here instead of there.. + */ + + /* Get the "before" timestamp */ + err = rtc_read_time(rtc, &before); + if (err < 0) + return err; + do { + if (!first_time) + memcpy(&before, &now, sizeof(struct rtc_time)); + first_time = 0; + + /* get the RTC alarm values, which may be incomplete */ + err = rtc_read_alarm_internal(rtc, alarm); + if (err) + return err; + + /* full-function RTCs won't have such missing fields */ + err = rtc_valid_tm(&alarm->time); + if (!err) + goto done; + + /* get the "after" timestamp, to detect wrapped fields */ + err = rtc_read_time(rtc, &now); + if (err < 0) + return err; + + /* note that tm_sec is a "don't care" value here: */ + } while (before.tm_min != now.tm_min || + before.tm_hour != now.tm_hour || + before.tm_mon != now.tm_mon || + before.tm_year != now.tm_year); + + /* Fill in the missing alarm fields using the timestamp; we + * know there's at least one since alarm->time is invalid. + */ + if (alarm->time.tm_sec == -1) + alarm->time.tm_sec = now.tm_sec; + if (alarm->time.tm_min == -1) + alarm->time.tm_min = now.tm_min; + if (alarm->time.tm_hour == -1) + alarm->time.tm_hour = now.tm_hour; + + /* For simplicity, only support date rollover for now */ + if (alarm->time.tm_mday < 1 || alarm->time.tm_mday > 31) { + alarm->time.tm_mday = now.tm_mday; + missing = day; + } + if ((unsigned int)alarm->time.tm_mon >= 12) { + alarm->time.tm_mon = now.tm_mon; + if (missing == none) + missing = month; + } + if (alarm->time.tm_year == -1) { + alarm->time.tm_year = now.tm_year; + if (missing == none) + missing = year; + } + + /* Can't proceed if alarm is still invalid after replacing + * missing fields. + */ + err = rtc_valid_tm(&alarm->time); + if (err) + goto done; + + /* with luck, no rollover is needed */ + t_now = rtc_tm_to_time64(&now); + t_alm = rtc_tm_to_time64(&alarm->time); + if (t_now < t_alm) + goto done; + + switch (missing) { + /* 24 hour rollover ... if it's now 10am Monday, an alarm that + * that will trigger at 5am will do so at 5am Tuesday, which + * could also be in the next month or year. This is a common + * case, especially for PCs. + */ + case day: + dev_dbg(&rtc->dev, "alarm rollover: %s\n", "day"); + t_alm += 24 * 60 * 60; + rtc_time64_to_tm(t_alm, &alarm->time); + break; + + /* Month rollover ... if it's the 31th, an alarm on the 3rd will + * be next month. An alarm matching on the 30th, 29th, or 28th + * may end up in the month after that! Many newer PCs support + * this type of alarm. + */ + case month: + dev_dbg(&rtc->dev, "alarm rollover: %s\n", "month"); + do { + if (alarm->time.tm_mon < 11) { + alarm->time.tm_mon++; + } else { + alarm->time.tm_mon = 0; + alarm->time.tm_year++; + } + days = rtc_month_days(alarm->time.tm_mon, + alarm->time.tm_year); + } while (days < alarm->time.tm_mday); + break; + + /* Year rollover ... easy except for leap years! */ + case year: + dev_dbg(&rtc->dev, "alarm rollover: %s\n", "year"); + do { + alarm->time.tm_year++; + } while (!is_leap_year(alarm->time.tm_year + 1900) && + rtc_valid_tm(&alarm->time) != 0); + break; + + default: + dev_warn(&rtc->dev, "alarm rollover not handled\n"); + } + + err = rtc_valid_tm(&alarm->time); + +done: + if (err && alarm->enabled) + dev_warn(&rtc->dev, "invalid alarm value: %ptR\n", + &alarm->time); + else + rtc_add_offset(rtc, &alarm->time); + + return err; +} + +/** + * rtc_read_next_alarm - read the next expiring alarm + * @rtc: RTC device + * @alarm: storage for the alarm information + * + * Read the next expiring alarm from the RTC timerqueue. This returns + * the alarm that will actually fire next, which may be different from + * rtc_read_alarm() if multiple timers are queued (e.g., alarmtimer + * and wakealarm sysfs both active). + * + * Returns: 0 on success, -ENOENT if no alarm is pending, or other error. + */ +int rtc_read_next_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) +{ + struct timerqueue_node *next; + int err; + + if (!rtc || !alarm) + return -EINVAL; + + err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return err; + + next = timerqueue_getnext(&rtc->timerqueue); + if (!next) { + err = -ENOENT; + goto unlock; + } + + memset(alarm, 0, sizeof(struct rtc_wkalrm)); + alarm->time = rtc_ktime_to_tm(next->expires); + alarm->enabled = 1; + +unlock: + mutex_unlock(&rtc->ops_lock); + return err; +} +EXPORT_SYMBOL_GPL(rtc_read_next_alarm); + +int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) +{ + int err; + + err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return err; + if (!rtc->ops) { + err = -ENODEV; + } else if (!test_bit(RTC_FEATURE_ALARM, rtc->features)) { + err = -EINVAL; + } else { + memset(alarm, 0, sizeof(struct rtc_wkalrm)); + alarm->enabled = rtc->aie_timer.enabled; + alarm->time = rtc_ktime_to_tm(rtc->aie_timer.node.expires); + } + mutex_unlock(&rtc->ops_lock); + + trace_rtc_read_alarm(rtc_tm_to_time64(&alarm->time), err); + return err; +} +EXPORT_SYMBOL_GPL(rtc_read_alarm); + +static int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) +{ + struct rtc_time tm; + time64_t now, scheduled; + int err; + + err = rtc_valid_tm(&alarm->time); + if (err) + return err; + + scheduled = rtc_tm_to_time64(&alarm->time); + + /* Make sure we're not setting alarms in the past */ + err = __rtc_read_time(rtc, &tm); + if (err) + return err; + now = rtc_tm_to_time64(&tm); + + if (scheduled <= now) + return -ETIME; + /* + * XXX - We just checked to make sure the alarm time is not + * in the past, but there is still a race window where if + * the is alarm set for the next second and the second ticks + * over right here, before we set the alarm. + */ + + rtc_subtract_offset(rtc, &alarm->time); + + if (!rtc->ops) + err = -ENODEV; + else if (!test_bit(RTC_FEATURE_ALARM, rtc->features)) + err = -EINVAL; + else + err = rtc->ops->set_alarm(rtc->dev.parent, alarm); + + /* + * Check for potential race described above. If the waiting for next + * second, and the second just ticked since the check above, either + * + * 1) It ticked after the alarm was set, and an alarm irq should be + * generated. + * + * 2) It ticked before the alarm was set, and alarm irq most likely will + * not be generated. + * + * While we cannot easily check for which of these two scenarios we + * are in, we can return -ETIME to signal that the timer has already + * expired, which is true in both cases. + */ + if (!err && (scheduled - now) <= 1) { + err = __rtc_read_time(rtc, &tm); + if (err) + return err; + now = rtc_tm_to_time64(&tm); + if (scheduled <= now) + return -ETIME; + } + + trace_rtc_set_alarm(rtc_tm_to_time64(&alarm->time), err); + return err; +} + +int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) +{ + ktime_t alarm_time; + int err; + + if (!rtc->ops) + return -ENODEV; + else if (!test_bit(RTC_FEATURE_ALARM, rtc->features)) + return -EINVAL; + + err = rtc_valid_tm(&alarm->time); + if (err != 0) + return err; + + err = rtc_valid_range(rtc, &alarm->time); + if (err) + return err; + + err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return err; + if (rtc->aie_timer.enabled) + rtc_timer_remove(rtc, &rtc->aie_timer); + + alarm_time = rtc_tm_to_ktime(alarm->time); + /* + * Round down so we never miss a deadline, checking for past deadline is + * done in __rtc_set_alarm + */ + if (test_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->features)) + alarm_time = ktime_sub_ns(alarm_time, (u64)alarm->time.tm_sec * NSEC_PER_SEC); + + rtc->aie_timer.node.expires = alarm_time; + rtc->aie_timer.period = 0; + if (alarm->enabled) + err = rtc_timer_enqueue(rtc, &rtc->aie_timer); + + mutex_unlock(&rtc->ops_lock); + + return err; +} +EXPORT_SYMBOL_GPL(rtc_set_alarm); + +/* Called once per device from rtc_device_register */ +int rtc_initialize_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) +{ + int err; + struct rtc_time now; + + err = rtc_valid_tm(&alarm->time); + if (err != 0) + return err; + + err = rtc_read_time(rtc, &now); + if (err) + return err; + + err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return err; + + rtc->aie_timer.node.expires = rtc_tm_to_ktime(alarm->time); + rtc->aie_timer.period = 0; + + /* Alarm has to be enabled & in the future for us to enqueue it */ + if (alarm->enabled && (rtc_tm_to_ktime(now) < + rtc->aie_timer.node.expires)) { + rtc->aie_timer.enabled = 1; + timerqueue_add(&rtc->timerqueue, &rtc->aie_timer.node); + trace_rtc_timer_enqueue(&rtc->aie_timer); + } + mutex_unlock(&rtc->ops_lock); + return err; +} +EXPORT_SYMBOL_GPL(rtc_initialize_alarm); + +int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled) +{ + int err; + + err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return err; + + if (rtc->aie_timer.enabled != enabled) { + if (enabled) + err = rtc_timer_enqueue(rtc, &rtc->aie_timer); + else + rtc_timer_remove(rtc, &rtc->aie_timer); + } + + if (err) + /* nothing */; + else if (!rtc->ops) + err = -ENODEV; + else if (!test_bit(RTC_FEATURE_ALARM, rtc->features) || !rtc->ops->alarm_irq_enable) + err = -EINVAL; + else + err = rtc->ops->alarm_irq_enable(rtc->dev.parent, enabled); + + mutex_unlock(&rtc->ops_lock); + + trace_rtc_alarm_irq_enable(enabled, err); + return err; +} +EXPORT_SYMBOL_GPL(rtc_alarm_irq_enable); + +int rtc_update_irq_enable(struct rtc_device *rtc, unsigned int enabled) +{ + int err; + + err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return err; + +#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL + if (enabled == 0 && rtc->uie_irq_active) { + mutex_unlock(&rtc->ops_lock); + return rtc_dev_update_irq_enable_emul(rtc, 0); + } +#endif + /* make sure we're changing state */ + if (rtc->uie_rtctimer.enabled == enabled) + goto out; + + if (!test_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features) || + !test_bit(RTC_FEATURE_ALARM, rtc->features)) { + mutex_unlock(&rtc->ops_lock); +#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL + return rtc_dev_update_irq_enable_emul(rtc, enabled); +#else + return -EINVAL; +#endif + } + + if (enabled) { + struct rtc_time tm; + ktime_t now, onesec; + + err = __rtc_read_time(rtc, &tm); + if (err) + goto out; + onesec = ktime_set(1, 0); + now = rtc_tm_to_ktime(tm); + rtc->uie_rtctimer.node.expires = ktime_add(now, onesec); + rtc->uie_rtctimer.period = ktime_set(1, 0); + err = rtc_timer_enqueue(rtc, &rtc->uie_rtctimer); + if (!err && rtc->ops && rtc->ops->alarm_irq_enable) + err = rtc->ops->alarm_irq_enable(rtc->dev.parent, 1); + if (err) + goto out; + } else { + rtc_timer_remove(rtc, &rtc->uie_rtctimer); + } + +out: + mutex_unlock(&rtc->ops_lock); + + return err; +} +EXPORT_SYMBOL_GPL(rtc_update_irq_enable); + +/** + * rtc_handle_legacy_irq - AIE, UIE and PIE event hook + * @rtc: pointer to the rtc device + * @num: number of occurrence of the event + * @mode: type of the event, RTC_AF, RTC_UF or RTC_PF + * + * This function is called when an AIE, UIE or PIE mode interrupt + * has occurred (or been emulated). + * + */ +void rtc_handle_legacy_irq(struct rtc_device *rtc, int num, int mode) +{ + unsigned long flags; + + /* mark one irq of the appropriate mode */ + spin_lock_irqsave(&rtc->irq_lock, flags); + rtc->irq_data = (rtc->irq_data + (num << 8)) | (RTC_IRQF | mode); + spin_unlock_irqrestore(&rtc->irq_lock, flags); + + wake_up_interruptible(&rtc->irq_queue); + kill_fasync(&rtc->async_queue, SIGIO, POLL_IN); +} + +/** + * rtc_aie_update_irq - AIE mode rtctimer hook + * @rtc: pointer to the rtc_device + * + * This functions is called when the aie_timer expires. + */ +void rtc_aie_update_irq(struct rtc_device *rtc) +{ + rtc_handle_legacy_irq(rtc, 1, RTC_AF); +} + +/** + * rtc_uie_update_irq - UIE mode rtctimer hook + * @rtc: pointer to the rtc_device + * + * This functions is called when the uie_timer expires. + */ +void rtc_uie_update_irq(struct rtc_device *rtc) +{ + rtc_handle_legacy_irq(rtc, 1, RTC_UF); +} + +/** + * rtc_pie_update_irq - PIE mode hrtimer hook + * @timer: pointer to the pie mode hrtimer + * + * This function is used to emulate PIE mode interrupts + * using an hrtimer. This function is called when the periodic + * hrtimer expires. + */ +enum hrtimer_restart rtc_pie_update_irq(struct hrtimer *timer) +{ + struct rtc_device *rtc; + ktime_t period; + u64 count; + + rtc = container_of(timer, struct rtc_device, pie_timer); + + period = NSEC_PER_SEC / rtc->irq_freq; + count = hrtimer_forward_now(timer, period); + + rtc_handle_legacy_irq(rtc, count, RTC_PF); + + return HRTIMER_RESTART; +} + +/** + * rtc_update_irq - Triggered when a RTC interrupt occurs. + * @rtc: the rtc device + * @num: how many irqs are being reported (usually one) + * @events: mask of RTC_IRQF with one or more of RTC_PF, RTC_AF, RTC_UF + * Context: any + */ +void rtc_update_irq(struct rtc_device *rtc, + unsigned long num, unsigned long events) +{ + if (IS_ERR_OR_NULL(rtc)) + return; + + pm_stay_awake(rtc->dev.parent); + schedule_work(&rtc->irqwork); +} +EXPORT_SYMBOL_GPL(rtc_update_irq); + +struct rtc_device *rtc_class_open(const char *name) +{ + struct device *dev; + struct rtc_device *rtc = NULL; + + dev = class_find_device_by_name(&rtc_class, name); + if (dev) + rtc = to_rtc_device(dev); + + if (rtc) { + if (!try_module_get(rtc->owner)) { + put_device(dev); + rtc = NULL; + } + } + + return rtc; +} +EXPORT_SYMBOL_GPL(rtc_class_open); + +void rtc_class_close(struct rtc_device *rtc) +{ + module_put(rtc->owner); + put_device(&rtc->dev); +} +EXPORT_SYMBOL_GPL(rtc_class_close); + +static int rtc_update_hrtimer(struct rtc_device *rtc, int enabled) +{ + /* + * We always cancel the timer here first, because otherwise + * we could run into BUG_ON(timer->state != HRTIMER_STATE_CALLBACK); + * when we manage to start the timer before the callback + * returns HRTIMER_RESTART. + * + * We cannot use hrtimer_cancel() here as a running callback + * could be blocked on rtc->irq_task_lock and hrtimer_cancel() + * would spin forever. + */ + if (hrtimer_try_to_cancel(&rtc->pie_timer) < 0) + return -1; + + if (enabled) { + ktime_t period = NSEC_PER_SEC / rtc->irq_freq; + + hrtimer_start(&rtc->pie_timer, period, HRTIMER_MODE_REL); + } + return 0; +} + +/** + * rtc_irq_set_state - enable/disable 2^N Hz periodic IRQs + * @rtc: the rtc device + * @enabled: true to enable periodic IRQs + * Context: any + * + * Note that rtc_irq_set_freq() should previously have been used to + * specify the desired frequency of periodic IRQ. + */ +int rtc_irq_set_state(struct rtc_device *rtc, int enabled) +{ + int err = 0; + + while (rtc_update_hrtimer(rtc, enabled) < 0) + cpu_relax(); + + rtc->pie_enabled = enabled; + + trace_rtc_irq_set_state(enabled, err); + return err; +} + +/** + * rtc_irq_set_freq - set 2^N Hz periodic IRQ frequency for IRQ + * @rtc: the rtc device + * @freq: positive frequency + * Context: any + * + * Note that rtc_irq_set_state() is used to enable or disable the + * periodic IRQs. + */ +int rtc_irq_set_freq(struct rtc_device *rtc, int freq) +{ + int err = 0; + + if (freq <= 0 || freq > RTC_MAX_FREQ) + return -EINVAL; + + rtc->irq_freq = freq; + while (rtc->pie_enabled && rtc_update_hrtimer(rtc, 1) < 0) + cpu_relax(); + + trace_rtc_irq_set_freq(freq, err); + return err; +} + +/** + * rtc_timer_enqueue - Adds a rtc_timer to the rtc_device timerqueue + * @rtc: rtc device + * @timer: timer being added. + * + * Enqueues a timer onto the rtc devices timerqueue and sets + * the next alarm event appropriately. + * + * Sets the enabled bit on the added timer. + * + * Must hold ops_lock for proper serialization of timerqueue + */ +static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) +{ + struct timerqueue_node *next = timerqueue_getnext(&rtc->timerqueue); + struct rtc_time tm; + ktime_t now; + int err; + + err = __rtc_read_time(rtc, &tm); + if (err) + return err; + + timer->enabled = 1; + now = rtc_tm_to_ktime(tm); + + /* Skip over expired timers */ + while (next) { + if (next->expires >= now) + break; + next = timerqueue_iterate_next(next); + } + + timerqueue_add(&rtc->timerqueue, &timer->node); + trace_rtc_timer_enqueue(timer); + if (!next || ktime_before(timer->node.expires, next->expires)) { + struct rtc_wkalrm alarm; + + alarm.time = rtc_ktime_to_tm(timer->node.expires); + alarm.enabled = 1; + err = __rtc_set_alarm(rtc, &alarm); + if (err == -ETIME) { + pm_stay_awake(rtc->dev.parent); + schedule_work(&rtc->irqwork); + } else if (err) { + timerqueue_del(&rtc->timerqueue, &timer->node); + trace_rtc_timer_dequeue(timer); + timer->enabled = 0; + return err; + } + } + return 0; +} + +static void rtc_alarm_disable(struct rtc_device *rtc) +{ + if (!rtc->ops || !test_bit(RTC_FEATURE_ALARM, rtc->features) || !rtc->ops->alarm_irq_enable) + return; + + rtc->ops->alarm_irq_enable(rtc->dev.parent, false); + trace_rtc_alarm_irq_enable(0, 0); +} + +/** + * rtc_timer_remove - Removes a rtc_timer from the rtc_device timerqueue + * @rtc: rtc device + * @timer: timer being removed. + * + * Removes a timer onto the rtc devices timerqueue and sets + * the next alarm event appropriately. + * + * Clears the enabled bit on the removed timer. + * + * Must hold ops_lock for proper serialization of timerqueue + */ +static void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer) +{ + struct timerqueue_node *next = timerqueue_getnext(&rtc->timerqueue); + + timerqueue_del(&rtc->timerqueue, &timer->node); + trace_rtc_timer_dequeue(timer); + timer->enabled = 0; + if (next == &timer->node) { + struct rtc_wkalrm alarm; + int err; + + next = timerqueue_getnext(&rtc->timerqueue); + if (!next) { + rtc_alarm_disable(rtc); + return; + } + alarm.time = rtc_ktime_to_tm(next->expires); + alarm.enabled = 1; + err = __rtc_set_alarm(rtc, &alarm); + if (err == -ETIME) { + pm_stay_awake(rtc->dev.parent); + schedule_work(&rtc->irqwork); + } + } +} + +/** + * rtc_timer_do_work - Expires rtc timers + * @work: work item + * + * Expires rtc timers. Reprograms next alarm event if needed. + * Called via worktask. + * + * Serializes access to timerqueue via ops_lock mutex + */ +void rtc_timer_do_work(struct work_struct *work) +{ + struct rtc_timer *timer; + struct timerqueue_node *next; + ktime_t now; + struct rtc_time tm; + int err; + + struct rtc_device *rtc = + container_of(work, struct rtc_device, irqwork); + + mutex_lock(&rtc->ops_lock); +again: + err = __rtc_read_time(rtc, &tm); + if (err) { + mutex_unlock(&rtc->ops_lock); + return; + } + now = rtc_tm_to_ktime(tm); + while ((next = timerqueue_getnext(&rtc->timerqueue))) { + if (next->expires > now) + break; + + /* expire timer */ + timer = container_of(next, struct rtc_timer, node); + timerqueue_del(&rtc->timerqueue, &timer->node); + trace_rtc_timer_dequeue(timer); + timer->enabled = 0; + if (timer->func) + timer->func(timer->rtc); + + trace_rtc_timer_fired(timer); + /* Re-add/fwd periodic timers */ + if (ktime_to_ns(timer->period)) { + timer->node.expires = ktime_add(timer->node.expires, + timer->period); + timer->enabled = 1; + timerqueue_add(&rtc->timerqueue, &timer->node); + trace_rtc_timer_enqueue(timer); + } + } + + /* Set next alarm */ + if (next) { + struct rtc_wkalrm alarm; + int err; + int retry = 3; + + alarm.time = rtc_ktime_to_tm(next->expires); + alarm.enabled = 1; +reprogram: + err = __rtc_set_alarm(rtc, &alarm); + if (err == -ETIME) { + goto again; + } else if (err) { + if (retry-- > 0) + goto reprogram; + + timer = container_of(next, struct rtc_timer, node); + timerqueue_del(&rtc->timerqueue, &timer->node); + trace_rtc_timer_dequeue(timer); + timer->enabled = 0; + dev_err(&rtc->dev, "__rtc_set_alarm: err=%d\n", err); + goto again; + } + } else { + rtc_alarm_disable(rtc); + } + + pm_relax(rtc->dev.parent); + mutex_unlock(&rtc->ops_lock); +} + +/* rtc_timer_init - Initializes an rtc_timer + * @timer: timer to be intiialized + * @f: function pointer to be called when timer fires + * @rtc: pointer to the rtc_device + * + * Kernel interface to initializing an rtc_timer. + */ +void rtc_timer_init(struct rtc_timer *timer, void (*f)(struct rtc_device *r), + struct rtc_device *rtc) +{ + timerqueue_init(&timer->node); + timer->enabled = 0; + timer->func = f; + timer->rtc = rtc; +} + +/* rtc_timer_start - Sets an rtc_timer to fire in the future + * @ rtc: rtc device to be used + * @ timer: timer being set + * @ expires: time at which to expire the timer + * @ period: period that the timer will recur + * + * Kernel interface to set an rtc_timer + */ +int rtc_timer_start(struct rtc_device *rtc, struct rtc_timer *timer, + ktime_t expires, ktime_t period) +{ + int ret = 0; + + mutex_lock(&rtc->ops_lock); + if (timer->enabled) + rtc_timer_remove(rtc, timer); + + timer->node.expires = expires; + timer->period = period; + + ret = rtc_timer_enqueue(rtc, timer); + + mutex_unlock(&rtc->ops_lock); + return ret; +} + +/* rtc_timer_cancel - Stops an rtc_timer + * @ rtc: rtc device to be used + * @ timer: timer being set + * + * Kernel interface to cancel an rtc_timer + */ +void rtc_timer_cancel(struct rtc_device *rtc, struct rtc_timer *timer) +{ + mutex_lock(&rtc->ops_lock); + if (timer->enabled) + rtc_timer_remove(rtc, timer); + mutex_unlock(&rtc->ops_lock); +} + +/** + * rtc_read_offset - Read the amount of rtc offset in parts per billion + * @rtc: rtc device to be used + * @offset: the offset in parts per billion + * + * see below for details. + * + * Kernel interface to read rtc clock offset + * Returns 0 on success, or a negative number on error. + * If read_offset() is not implemented for the rtc, return -EINVAL + */ +int rtc_read_offset(struct rtc_device *rtc, long *offset) +{ + int ret; + + if (!rtc->ops) + return -ENODEV; + + if (!rtc->ops->read_offset) + return -EINVAL; + + mutex_lock(&rtc->ops_lock); + ret = rtc->ops->read_offset(rtc->dev.parent, offset); + mutex_unlock(&rtc->ops_lock); + + trace_rtc_read_offset(*offset, ret); + return ret; +} + +/** + * rtc_set_offset - Adjusts the duration of the average second + * @rtc: rtc device to be used + * @offset: the offset in parts per billion + * + * Some rtc's allow an adjustment to the average duration of a second + * to compensate for differences in the actual clock rate due to temperature, + * the crystal, capacitor, etc. + * + * The adjustment applied is as follows: + * t = t0 * (1 + offset * 1e-9) + * where t0 is the measured length of 1 RTC second with offset = 0 + * + * Kernel interface to adjust an rtc clock offset. + * Return 0 on success, or a negative number on error. + * If the rtc offset is not setable (or not implemented), return -EINVAL + */ +int rtc_set_offset(struct rtc_device *rtc, long offset) +{ + int ret; + + if (!rtc->ops) + return -ENODEV; + + if (!rtc->ops->set_offset) + return -EINVAL; + + mutex_lock(&rtc->ops_lock); + ret = rtc->ops->set_offset(rtc->dev.parent, offset); + mutex_unlock(&rtc->ops_lock); + + trace_rtc_set_offset(offset, ret); + return ret; +} diff --git a/drivers/rtc/lib.c b/drivers/rtc/lib.c new file mode 100644 index 000000000..f7051592a --- /dev/null +++ b/drivers/rtc/lib.c @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * rtc and date/time utility functions + * + * Copyright (C) 2005-06 Tower Technologies + * Author: Alessandro Zummo + * + * based on arch/arm/common/rtctime.c and other bits + * + * Author: Cassio Neri (rtc_time64_to_tm) + */ + +#include +#include + +static const unsigned char rtc_days_in_month[] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +static const unsigned short rtc_ydays[2][13] = { + /* Normal years */ + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + /* Leap years */ + { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } +}; + +/* + * The number of days in the month. + */ +int rtc_month_days(unsigned int month, unsigned int year) +{ + return rtc_days_in_month[month] + (is_leap_year(year) && month == 1); +} +EXPORT_SYMBOL(rtc_month_days); + +/* + * The number of days since January 1. (0 to 365) + */ +int rtc_year_days(unsigned int day, unsigned int month, unsigned int year) +{ + return rtc_ydays[is_leap_year(year)][month] + day - 1; +} +EXPORT_SYMBOL(rtc_year_days); + +/** + * rtc_time64_to_tm - converts time64_t to rtc_time. + * + * @time: The number of seconds since 01-01-1970 00:00:00. + * Works for values since at least 1900 + * @tm: Pointer to the struct rtc_time. + */ +void rtc_time64_to_tm(time64_t time, struct rtc_time *tm) +{ + int secs; + + u64 u64tmp; + u32 u32tmp, udays, century, day_of_century, year_of_century, year, + day_of_year, month, day; + bool is_Jan_or_Feb, is_leap_year; + + /* + * The time represented by `time` is given in seconds since 1970-01-01 + * (UTC). As the division done below might misbehave for negative + * values, we convert it to seconds since 0000-03-01 and then assume it + * will be non-negative. + * Below we do 4 * udays + 3 which should fit into a 32 bit unsigned + * variable. So the latest date this algorithm works for is 1073741823 + * days after 0000-03-01 which is in the year 2939805. + */ + time += (u64)719468 * 86400; + + udays = div_s64_rem(time, 86400, &secs); + + /* + * day of the week, 0000-03-01 was a Wednesday (in the proleptic + * Gregorian calendar) + */ + tm->tm_wday = (udays + 3) % 7; + + /* + * The following algorithm is, basically, Figure 12 of Neri + * and Schneider [1]. In a few words: it works on the computational + * (fictitious) calendar where the year starts in March, month = 2 + * (*), and finishes in February, month = 13. This calendar is + * mathematically convenient because the day of the year does not + * depend on whether the year is leap or not. For instance: + * + * March 1st 0-th day of the year; + * ... + * April 1st 31-st day of the year; + * ... + * January 1st 306-th day of the year; (Important!) + * ... + * February 28th 364-th day of the year; + * February 29th 365-th day of the year (if it exists). + * + * After having worked out the date in the computational calendar + * (using just arithmetics) it's easy to convert it to the + * corresponding date in the Gregorian calendar. + * + * [1] Neri C, Schneider L. Euclidean affine functions and their + * application to calendar algorithms. Softw Pract Exper. + * 2023;53(4):937-970. doi: 10.1002/spe.3172 + * https://doi.org/10.1002/spe.3172 + * + * (*) The numbering of months follows rtc_time more closely and + * thus, is slightly different from [1]. + */ + + u32tmp = 4 * udays + 3; + century = u32tmp / 146097; + day_of_century = u32tmp % 146097 / 4; + + u32tmp = 4 * day_of_century + 3; + u64tmp = 2939745ULL * u32tmp; + year_of_century = upper_32_bits(u64tmp); + day_of_year = lower_32_bits(u64tmp) / 2939745 / 4; + + year = 100 * century + year_of_century; + is_leap_year = year_of_century != 0 ? + year_of_century % 4 == 0 : century % 4 == 0; + + u32tmp = 2141 * day_of_year + 132377; + month = u32tmp >> 16; + day = ((u16) u32tmp) / 2141; + + /* + * Recall that January 01 is the 306-th day of the year in the + * computational (not Gregorian) calendar. + */ + is_Jan_or_Feb = day_of_year >= 306; + + /* Converts to the Gregorian calendar. */ + year = year + is_Jan_or_Feb; + month = is_Jan_or_Feb ? month - 12 : month; + day = day + 1; + + day_of_year = is_Jan_or_Feb ? + day_of_year - 306 : day_of_year + 31 + 28 + is_leap_year; + + /* Converts to rtc_time's format. */ + tm->tm_year = (int) (year - 1900); + tm->tm_mon = (int) month; + tm->tm_mday = (int) day; + tm->tm_yday = (int) day_of_year + 1; + + tm->tm_hour = secs / 3600; + secs -= tm->tm_hour * 3600; + tm->tm_min = secs / 60; + tm->tm_sec = secs - tm->tm_min * 60; + + tm->tm_isdst = 0; +} +EXPORT_SYMBOL(rtc_time64_to_tm); + +/* + * Does the rtc_time represent a valid date/time? + */ +int rtc_valid_tm(struct rtc_time *tm) +{ + if (tm->tm_year < 70 || + tm->tm_year > (INT_MAX - 1900) || + ((unsigned int)tm->tm_mon) >= 12 || + tm->tm_mday < 1 || + tm->tm_mday > rtc_month_days(tm->tm_mon, + ((unsigned int)tm->tm_year + 1900)) || + ((unsigned int)tm->tm_hour) >= 24 || + ((unsigned int)tm->tm_min) >= 60 || + ((unsigned int)tm->tm_sec) >= 60) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL(rtc_valid_tm); + +/* + * rtc_tm_to_time64 - Converts rtc_time to time64_t. + * Convert Gregorian date to seconds since 01-01-1970 00:00:00. + */ +time64_t rtc_tm_to_time64(struct rtc_time *tm) +{ + return mktime64(((unsigned int)tm->tm_year + 1900), tm->tm_mon + 1, + tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); +} +EXPORT_SYMBOL(rtc_tm_to_time64); + +/* + * Convert rtc_time to ktime + */ +ktime_t rtc_tm_to_ktime(struct rtc_time tm) +{ + return ktime_set(rtc_tm_to_time64(&tm), 0); +} +EXPORT_SYMBOL_GPL(rtc_tm_to_ktime); + +/* + * Convert ktime to rtc_time + */ +struct rtc_time rtc_ktime_to_tm(ktime_t kt) +{ + struct timespec64 ts; + struct rtc_time ret; + + ts = ktime_to_timespec64(kt); + /* Round up any ns */ + if (ts.tv_nsec) + ts.tv_sec++; + rtc_time64_to_tm(ts.tv_sec, &ret); + return ret; +} +EXPORT_SYMBOL_GPL(rtc_ktime_to_tm); diff --git a/drivers/rtc/nvmem.c b/drivers/rtc/nvmem.c new file mode 100644 index 000000000..37df7e805 --- /dev/null +++ b/drivers/rtc/nvmem.c @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RTC subsystem, nvmem interface + * + * Copyright (C) 2017 Alexandre Belloni + */ + +#include +#include +#include +#include + +int devm_rtc_nvmem_register(struct rtc_device *rtc, + struct nvmem_config *nvmem_config) +{ + struct device *dev = rtc->dev.parent; + struct nvmem_device *nvmem; + + if (!nvmem_config) + return -ENODEV; + + nvmem_config->dev = dev; + nvmem_config->owner = rtc->owner; + nvmem_config->add_legacy_fixed_of_cells = true; + nvmem = devm_nvmem_register(dev, nvmem_config); + if (IS_ERR(nvmem)) + dev_err(dev, "failed to register nvmem device for RTC\n"); + + return PTR_ERR_OR_ZERO(nvmem); +} +EXPORT_SYMBOL_GPL(devm_rtc_nvmem_register); diff --git a/drivers/rtc/proc.c b/drivers/rtc/proc.c new file mode 100644 index 000000000..cbcdbb19d --- /dev/null +++ b/drivers/rtc/proc.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RTC subsystem, proc interface + * + * Copyright (C) 2005-06 Tower Technologies + * Author: Alessandro Zummo + * + * based on arch/arm/common/rtctime.c + */ + +#include +#include +#include +#include + +#include "rtc-core.h" + +#define NAME_SIZE 10 + +#if defined(CONFIG_RTC_HCTOSYS_DEVICE) +static bool is_rtc_hctosys(struct rtc_device *rtc) +{ + int size; + char name[NAME_SIZE]; + + size = snprintf(name, NAME_SIZE, "rtc%d", rtc->id); + if (size >= NAME_SIZE) + return false; + + return !strncmp(name, CONFIG_RTC_HCTOSYS_DEVICE, NAME_SIZE); +} +#else +static bool is_rtc_hctosys(struct rtc_device *rtc) +{ + return (rtc->id == 0); +} +#endif + +static int rtc_proc_show(struct seq_file *seq, void *offset) +{ + int err; + struct rtc_device *rtc = seq->private; + const struct rtc_class_ops *ops = rtc->ops; + struct rtc_wkalrm alrm; + struct rtc_time tm; + + err = rtc_read_time(rtc, &tm); + if (err == 0) { + seq_printf(seq, + "rtc_time\t: %ptRt\n" + "rtc_date\t: %ptRd\n", + &tm, &tm); + } + + err = rtc_read_alarm(rtc, &alrm); + if (err == 0) { + seq_printf(seq, "alrm_time\t: %ptRt\n", &alrm.time); + seq_printf(seq, "alrm_date\t: %ptRd\n", &alrm.time); + seq_printf(seq, "alarm_IRQ\t: %s\n", + alrm.enabled ? "yes" : "no"); + seq_printf(seq, "alrm_pending\t: %s\n", + alrm.pending ? "yes" : "no"); + seq_printf(seq, "update IRQ enabled\t: %s\n", + (rtc->uie_rtctimer.enabled) ? "yes" : "no"); + seq_printf(seq, "periodic IRQ enabled\t: %s\n", + (rtc->pie_enabled) ? "yes" : "no"); + seq_printf(seq, "periodic IRQ frequency\t: %d\n", + rtc->irq_freq); + seq_printf(seq, "max user IRQ frequency\t: %d\n", + rtc->max_user_freq); + } + + seq_printf(seq, "24hr\t\t: yes\n"); + + if (ops->proc) + ops->proc(rtc->dev.parent, seq); + + return 0; +} + +void rtc_proc_add_device(struct rtc_device *rtc) +{ + if (is_rtc_hctosys(rtc)) + proc_create_single_data("driver/rtc", 0, NULL, rtc_proc_show, + rtc); +} + +void rtc_proc_del_device(struct rtc_device *rtc) +{ + if (is_rtc_hctosys(rtc)) + remove_proc_entry("driver/rtc", NULL); +} diff --git a/drivers/rtc/rtc-88pm80x.c b/drivers/rtc/rtc-88pm80x.c new file mode 100644 index 000000000..a3e52a5a7 --- /dev/null +++ b/drivers/rtc/rtc-88pm80x.c @@ -0,0 +1,340 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Real Time Clock driver for Marvell 88PM80x PMIC + * + * Copyright (c) 2012 Marvell International Ltd. + * Wenzeng Chen + * Qiao Zhou + */ + +#include +#include +#include +#include +#include +#include +#include + +#define PM800_RTC_COUNTER1 (0xD1) +#define PM800_RTC_COUNTER2 (0xD2) +#define PM800_RTC_COUNTER3 (0xD3) +#define PM800_RTC_COUNTER4 (0xD4) +#define PM800_RTC_EXPIRE1_1 (0xD5) +#define PM800_RTC_EXPIRE1_2 (0xD6) +#define PM800_RTC_EXPIRE1_3 (0xD7) +#define PM800_RTC_EXPIRE1_4 (0xD8) +#define PM800_RTC_TRIM1 (0xD9) +#define PM800_RTC_TRIM2 (0xDA) +#define PM800_RTC_TRIM3 (0xDB) +#define PM800_RTC_TRIM4 (0xDC) +#define PM800_RTC_EXPIRE2_1 (0xDD) +#define PM800_RTC_EXPIRE2_2 (0xDE) +#define PM800_RTC_EXPIRE2_3 (0xDF) +#define PM800_RTC_EXPIRE2_4 (0xE0) + +#define PM800_POWER_DOWN_LOG1 (0xE5) +#define PM800_POWER_DOWN_LOG2 (0xE6) + +struct pm80x_rtc_info { + struct pm80x_chip *chip; + struct regmap *map; + struct rtc_device *rtc_dev; + struct device *dev; + + int irq; +}; + +static irqreturn_t rtc_update_handler(int irq, void *data) +{ + struct pm80x_rtc_info *info = (struct pm80x_rtc_info *)data; + int mask; + + mask = PM800_ALARM | PM800_ALARM_WAKEUP; + regmap_update_bits(info->map, PM800_RTC_CONTROL, mask | PM800_ALARM1_EN, + mask); + rtc_update_irq(info->rtc_dev, 1, RTC_AF); + return IRQ_HANDLED; +} + +static int pm80x_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct pm80x_rtc_info *info = dev_get_drvdata(dev); + + if (enabled) + regmap_update_bits(info->map, PM800_RTC_CONTROL, + PM800_ALARM1_EN, PM800_ALARM1_EN); + else + regmap_update_bits(info->map, PM800_RTC_CONTROL, + PM800_ALARM1_EN, 0); + return 0; +} + +/* + * Calculate the next alarm time given the requested alarm time mask + * and the current time. + */ +static void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now, + struct rtc_time *alrm) +{ + unsigned long next_time; + unsigned long now_time; + + next->tm_year = now->tm_year; + next->tm_mon = now->tm_mon; + next->tm_mday = now->tm_mday; + next->tm_hour = alrm->tm_hour; + next->tm_min = alrm->tm_min; + next->tm_sec = alrm->tm_sec; + + now_time = rtc_tm_to_time64(now); + next_time = rtc_tm_to_time64(next); + + if (next_time < now_time) { + /* Advance one day */ + next_time += 60 * 60 * 24; + rtc_time64_to_tm(next_time, next); + } +} + +static int pm80x_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct pm80x_rtc_info *info = dev_get_drvdata(dev); + unsigned char buf[4]; + unsigned long ticks, base, data; + regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4); + base = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; + dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]); + + /* load 32-bit read-only counter */ + regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4); + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; + ticks = base + data; + dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", + base, data, ticks); + rtc_time64_to_tm(ticks, tm); + return 0; +} + +static int pm80x_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct pm80x_rtc_info *info = dev_get_drvdata(dev); + unsigned char buf[4]; + unsigned long ticks, base, data; + + ticks = rtc_tm_to_time64(tm); + + /* load 32-bit read-only counter */ + regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4); + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; + base = ticks - data; + dev_dbg(info->dev, "set base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", + base, data, ticks); + buf[0] = base & 0xFF; + buf[1] = (base >> 8) & 0xFF; + buf[2] = (base >> 16) & 0xFF; + buf[3] = (base >> 24) & 0xFF; + regmap_raw_write(info->map, PM800_RTC_EXPIRE2_1, buf, 4); + + return 0; +} + +static int pm80x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pm80x_rtc_info *info = dev_get_drvdata(dev); + unsigned char buf[4]; + unsigned long ticks, base, data; + int ret; + + regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4); + base = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; + dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]); + + regmap_raw_read(info->map, PM800_RTC_EXPIRE1_1, buf, 4); + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; + ticks = base + data; + dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", + base, data, ticks); + + rtc_time64_to_tm(ticks, &alrm->time); + regmap_read(info->map, PM800_RTC_CONTROL, &ret); + alrm->enabled = (ret & PM800_ALARM1_EN) ? 1 : 0; + alrm->pending = (ret & (PM800_ALARM | PM800_ALARM_WAKEUP)) ? 1 : 0; + return 0; +} + +static int pm80x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pm80x_rtc_info *info = dev_get_drvdata(dev); + struct rtc_time now_tm, alarm_tm; + unsigned long ticks, base, data; + unsigned char buf[4]; + int mask; + + regmap_update_bits(info->map, PM800_RTC_CONTROL, PM800_ALARM1_EN, 0); + + regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4); + base = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; + dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]); + + /* load 32-bit read-only counter */ + regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4); + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; + ticks = base + data; + dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", + base, data, ticks); + + rtc_time64_to_tm(ticks, &now_tm); + dev_dbg(info->dev, "%s, now time : %lu\n", __func__, ticks); + rtc_next_alarm_time(&alarm_tm, &now_tm, &alrm->time); + /* get new ticks for alarm in 24 hours */ + ticks = rtc_tm_to_time64(&alarm_tm); + dev_dbg(info->dev, "%s, alarm time: %lu\n", __func__, ticks); + data = ticks - base; + + buf[0] = data & 0xff; + buf[1] = (data >> 8) & 0xff; + buf[2] = (data >> 16) & 0xff; + buf[3] = (data >> 24) & 0xff; + regmap_raw_write(info->map, PM800_RTC_EXPIRE1_1, buf, 4); + if (alrm->enabled) { + mask = PM800_ALARM | PM800_ALARM_WAKEUP | PM800_ALARM1_EN; + regmap_update_bits(info->map, PM800_RTC_CONTROL, mask, mask); + } else { + mask = PM800_ALARM | PM800_ALARM_WAKEUP | PM800_ALARM1_EN; + regmap_update_bits(info->map, PM800_RTC_CONTROL, mask, + PM800_ALARM | PM800_ALARM_WAKEUP); + } + return 0; +} + +static const struct rtc_class_ops pm80x_rtc_ops = { + .read_time = pm80x_rtc_read_time, + .set_time = pm80x_rtc_set_time, + .read_alarm = pm80x_rtc_read_alarm, + .set_alarm = pm80x_rtc_set_alarm, + .alarm_irq_enable = pm80x_rtc_alarm_irq_enable, +}; + +#ifdef CONFIG_PM_SLEEP +static int pm80x_rtc_suspend(struct device *dev) +{ + return pm80x_dev_suspend(dev); +} + +static int pm80x_rtc_resume(struct device *dev) +{ + return pm80x_dev_resume(dev); +} +#endif + +static SIMPLE_DEV_PM_OPS(pm80x_rtc_pm_ops, pm80x_rtc_suspend, pm80x_rtc_resume); + +static int pm80x_rtc_probe(struct platform_device *pdev) +{ + struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct pm80x_rtc_pdata *pdata = dev_get_platdata(&pdev->dev); + struct pm80x_rtc_info *info; + struct device_node *node = pdev->dev.of_node; + int ret; + + if (!pdata && !node) { + dev_err(&pdev->dev, + "pm80x-rtc requires platform data or of_node\n"); + return -EINVAL; + } + + if (!pdata) { + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + } + + info = + devm_kzalloc(&pdev->dev, sizeof(struct pm80x_rtc_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + info->irq = platform_get_irq(pdev, 0); + if (info->irq < 0) { + ret = -EINVAL; + goto out; + } + + info->chip = chip; + info->map = chip->regmap; + if (!info->map) { + dev_err(&pdev->dev, "no regmap!\n"); + ret = -EINVAL; + goto out; + } + + info->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, info); + + info->rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(info->rtc_dev)) + return PTR_ERR(info->rtc_dev); + + ret = pm80x_request_irq(chip, info->irq, rtc_update_handler, + IRQF_ONESHOT, "rtc", info); + if (ret < 0) { + dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", + info->irq, ret); + goto out; + } + + info->rtc_dev->ops = &pm80x_rtc_ops; + info->rtc_dev->range_max = U32_MAX; + + ret = devm_rtc_register_device(info->rtc_dev); + if (ret) + goto out_rtc; + + /* + * enable internal XO instead of internal 3.25MHz clock since it can + * free running in PMIC power-down state. + */ + regmap_update_bits(info->map, PM800_RTC_CONTROL, PM800_RTC1_USE_XO, + PM800_RTC1_USE_XO); + + /* remember whether this power up is caused by PMIC RTC or not */ + info->rtc_dev->dev.platform_data = &pdata->rtc_wakeup; + + device_init_wakeup(&pdev->dev, true); + + return 0; +out_rtc: + pm80x_free_irq(chip, info->irq, info); +out: + return ret; +} + +static void pm80x_rtc_remove(struct platform_device *pdev) +{ + struct pm80x_rtc_info *info = platform_get_drvdata(pdev); + pm80x_free_irq(info->chip, info->irq, info); +} + +static struct platform_driver pm80x_rtc_driver = { + .driver = { + .name = "88pm80x-rtc", + .pm = &pm80x_rtc_pm_ops, + }, + .probe = pm80x_rtc_probe, + .remove = pm80x_rtc_remove, +}; + +module_platform_driver(pm80x_rtc_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Marvell 88PM80x RTC driver"); +MODULE_AUTHOR("Qiao Zhou "); +MODULE_ALIAS("platform:88pm80x-rtc"); diff --git a/drivers/rtc/rtc-88pm860x.c b/drivers/rtc/rtc-88pm860x.c new file mode 100644 index 000000000..964cd048f --- /dev/null +++ b/drivers/rtc/rtc-88pm860x.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Real Time Clock driver for Marvell 88PM860x PMIC + * + * Copyright (c) 2010 Marvell International Ltd. + * Author: Haojian Zhuang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VRTC_CALIBRATION + +struct pm860x_rtc_info { + struct pm860x_chip *chip; + struct i2c_client *i2c; + struct rtc_device *rtc_dev; + struct device *dev; + struct delayed_work calib_work; + + int irq; + int vrtc; +}; + +#define REG_VRTC_MEAS1 0x7D + +#define REG0_ADDR 0xB0 +#define REG1_ADDR 0xB2 +#define REG2_ADDR 0xB4 +#define REG3_ADDR 0xB6 + +#define REG0_DATA 0xB1 +#define REG1_DATA 0xB3 +#define REG2_DATA 0xB5 +#define REG3_DATA 0xB7 + +/* bit definitions of Measurement Enable Register 2 (0x51) */ +#define MEAS2_VRTC (1 << 0) + +/* bit definitions of RTC Register 1 (0xA0) */ +#define ALARM_EN (1 << 3) +#define ALARM_WAKEUP (1 << 4) +#define ALARM (1 << 5) +#define RTC1_USE_XO (1 << 7) + +#define VRTC_CALIB_INTERVAL (HZ * 60 * 10) /* 10 minutes */ + +static irqreturn_t rtc_update_handler(int irq, void *data) +{ + struct pm860x_rtc_info *info = (struct pm860x_rtc_info *)data; + int mask; + + mask = ALARM | ALARM_WAKEUP; + pm860x_set_bits(info->i2c, PM8607_RTC1, mask | ALARM_EN, mask); + rtc_update_irq(info->rtc_dev, 1, RTC_AF); + return IRQ_HANDLED; +} + +static int pm860x_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct pm860x_rtc_info *info = dev_get_drvdata(dev); + + if (enabled) + pm860x_set_bits(info->i2c, PM8607_RTC1, ALARM_EN, ALARM_EN); + else + pm860x_set_bits(info->i2c, PM8607_RTC1, ALARM_EN, 0); + return 0; +} + +static int pm860x_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct pm860x_rtc_info *info = dev_get_drvdata(dev); + unsigned char buf[8]; + unsigned long ticks, base, data; + + pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf); + dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1], + buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); + base = ((unsigned long)buf[1] << 24) | (buf[3] << 16) | + (buf[5] << 8) | buf[7]; + + /* load 32-bit read-only counter */ + pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf); + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; + ticks = base + data; + dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", + base, data, ticks); + + rtc_time64_to_tm(ticks, tm); + + return 0; +} + +static int pm860x_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct pm860x_rtc_info *info = dev_get_drvdata(dev); + unsigned char buf[4]; + unsigned long ticks, base, data; + + ticks = rtc_tm_to_time64(tm); + + /* load 32-bit read-only counter */ + pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf); + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; + base = ticks - data; + dev_dbg(info->dev, "set base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", + base, data, ticks); + + pm860x_page_reg_write(info->i2c, REG0_DATA, (base >> 24) & 0xFF); + pm860x_page_reg_write(info->i2c, REG1_DATA, (base >> 16) & 0xFF); + pm860x_page_reg_write(info->i2c, REG2_DATA, (base >> 8) & 0xFF); + pm860x_page_reg_write(info->i2c, REG3_DATA, base & 0xFF); + + return 0; +} + +static int pm860x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pm860x_rtc_info *info = dev_get_drvdata(dev); + unsigned char buf[8]; + unsigned long ticks, base, data; + int ret; + + pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf); + dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1], + buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); + base = ((unsigned long)buf[1] << 24) | (buf[3] << 16) | + (buf[5] << 8) | buf[7]; + + pm860x_bulk_read(info->i2c, PM8607_RTC_EXPIRE1, 4, buf); + data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; + ticks = base + data; + dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n", + base, data, ticks); + + rtc_time64_to_tm(ticks, &alrm->time); + ret = pm860x_reg_read(info->i2c, PM8607_RTC1); + alrm->enabled = (ret & ALARM_EN) ? 1 : 0; + alrm->pending = (ret & (ALARM | ALARM_WAKEUP)) ? 1 : 0; + return 0; +} + +static int pm860x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pm860x_rtc_info *info = dev_get_drvdata(dev); + unsigned long ticks, base, data; + unsigned char buf[8]; + int mask; + + pm860x_set_bits(info->i2c, PM8607_RTC1, ALARM_EN, 0); + + pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf); + dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1], + buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); + base = ((unsigned long)buf[1] << 24) | (buf[3] << 16) | + (buf[5] << 8) | buf[7]; + + ticks = rtc_tm_to_time64(&alrm->time); + data = ticks - base; + + buf[0] = data & 0xff; + buf[1] = (data >> 8) & 0xff; + buf[2] = (data >> 16) & 0xff; + buf[3] = (data >> 24) & 0xff; + pm860x_bulk_write(info->i2c, PM8607_RTC_EXPIRE1, 4, buf); + if (alrm->enabled) { + mask = ALARM | ALARM_WAKEUP | ALARM_EN; + pm860x_set_bits(info->i2c, PM8607_RTC1, mask, mask); + } else { + mask = ALARM | ALARM_WAKEUP | ALARM_EN; + pm860x_set_bits(info->i2c, PM8607_RTC1, mask, + ALARM | ALARM_WAKEUP); + } + return 0; +} + +static const struct rtc_class_ops pm860x_rtc_ops = { + .read_time = pm860x_rtc_read_time, + .set_time = pm860x_rtc_set_time, + .read_alarm = pm860x_rtc_read_alarm, + .set_alarm = pm860x_rtc_set_alarm, + .alarm_irq_enable = pm860x_rtc_alarm_irq_enable, +}; + +#ifdef VRTC_CALIBRATION +static void calibrate_vrtc_work(struct work_struct *work) +{ + struct pm860x_rtc_info *info = container_of(work, + struct pm860x_rtc_info, calib_work.work); + unsigned char buf[2]; + unsigned int sum, data, mean, vrtc_set; + int i; + + for (i = 0, sum = 0; i < 16; i++) { + msleep(100); + pm860x_bulk_read(info->i2c, REG_VRTC_MEAS1, 2, buf); + data = (buf[0] << 4) | buf[1]; + data = (data * 5400) >> 12; /* convert to mv */ + sum += data; + } + mean = sum >> 4; + vrtc_set = 2700 + (info->vrtc & 0x3) * 200; + dev_dbg(info->dev, "mean:%d, vrtc_set:%d\n", mean, vrtc_set); + + sum = pm860x_reg_read(info->i2c, PM8607_RTC_MISC1); + data = sum & 0x3; + if ((mean + 200) < vrtc_set) { + /* try higher voltage */ + if (++data == 4) + goto out; + data = (sum & 0xf8) | (data & 0x3); + pm860x_reg_write(info->i2c, PM8607_RTC_MISC1, data); + } else if ((mean - 200) > vrtc_set) { + /* try lower voltage */ + if (data-- == 0) + goto out; + data = (sum & 0xf8) | (data & 0x3); + pm860x_reg_write(info->i2c, PM8607_RTC_MISC1, data); + } else + goto out; + dev_dbg(info->dev, "set 0x%x to RTC_MISC1\n", data); + /* trigger next calibration since VRTC is updated */ + schedule_delayed_work(&info->calib_work, VRTC_CALIB_INTERVAL); + return; +out: + /* disable measurement */ + pm860x_set_bits(info->i2c, PM8607_MEAS_EN2, MEAS2_VRTC, 0); + dev_dbg(info->dev, "finish VRTC calibration\n"); + return; +} +#endif + +#ifdef CONFIG_OF +static int pm860x_rtc_dt_init(struct platform_device *pdev, + struct pm860x_rtc_info *info) +{ + struct device_node *np = pdev->dev.parent->of_node; + int ret; + if (!np) + return -ENODEV; + np = of_get_child_by_name(np, "rtc"); + if (!np) { + dev_err(&pdev->dev, "failed to find rtc node\n"); + return -ENODEV; + } + ret = of_property_read_u32(np, "marvell,88pm860x-vrtc", &info->vrtc); + if (ret) + info->vrtc = 0; + of_node_put(np); + return 0; +} +#else +#define pm860x_rtc_dt_init(x, y) do { } while (0) +#endif + +static int pm860x_rtc_probe(struct platform_device *pdev) +{ + struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct pm860x_rtc_info *info; + int ret; + + info = devm_kzalloc(&pdev->dev, sizeof(struct pm860x_rtc_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + info->irq = platform_get_irq(pdev, 0); + if (info->irq < 0) + return info->irq; + + info->chip = chip; + info->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion; + info->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, info); + + info->rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(info->rtc_dev)) + return PTR_ERR(info->rtc_dev); + + ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL, + rtc_update_handler, IRQF_ONESHOT, "rtc", + info); + if (ret < 0) { + dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", + info->irq, ret); + return ret; + } + + /* set addresses of 32-bit base value for RTC time */ + pm860x_page_reg_write(info->i2c, REG0_ADDR, REG0_DATA); + pm860x_page_reg_write(info->i2c, REG1_ADDR, REG1_DATA); + pm860x_page_reg_write(info->i2c, REG2_ADDR, REG2_DATA); + pm860x_page_reg_write(info->i2c, REG3_ADDR, REG3_DATA); + + pm860x_rtc_dt_init(pdev, info); + + info->rtc_dev->ops = &pm860x_rtc_ops; + info->rtc_dev->range_max = U32_MAX; + + ret = devm_rtc_register_device(info->rtc_dev); + if (ret) + return ret; + + /* + * enable internal XO instead of internal 3.25MHz clock since it can + * free running in PMIC power-down state. + */ + pm860x_set_bits(info->i2c, PM8607_RTC1, RTC1_USE_XO, RTC1_USE_XO); + +#ifdef VRTC_CALIBRATION + /* <00> -- 2.7V, <01> -- 2.9V, <10> -- 3.1V, <11> -- 3.3V */ + pm860x_set_bits(info->i2c, PM8607_MEAS_EN2, MEAS2_VRTC, MEAS2_VRTC); + + /* calibrate VRTC */ + INIT_DELAYED_WORK(&info->calib_work, calibrate_vrtc_work); + schedule_delayed_work(&info->calib_work, VRTC_CALIB_INTERVAL); +#endif /* VRTC_CALIBRATION */ + + device_init_wakeup(&pdev->dev, true); + + return 0; +} + +static void pm860x_rtc_remove(struct platform_device *pdev) +{ + struct pm860x_rtc_info *info = platform_get_drvdata(pdev); + +#ifdef VRTC_CALIBRATION + cancel_delayed_work_sync(&info->calib_work); + /* disable measurement */ + pm860x_set_bits(info->i2c, PM8607_MEAS_EN2, MEAS2_VRTC, 0); +#endif /* VRTC_CALIBRATION */ +} + +#ifdef CONFIG_PM_SLEEP +static int pm860x_rtc_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); + + if (device_may_wakeup(dev)) + chip->wakeup_flag |= 1 << PM8607_IRQ_RTC; + return 0; +} +static int pm860x_rtc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); + + if (device_may_wakeup(dev)) + chip->wakeup_flag &= ~(1 << PM8607_IRQ_RTC); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(pm860x_rtc_pm_ops, pm860x_rtc_suspend, pm860x_rtc_resume); + +static struct platform_driver pm860x_rtc_driver = { + .driver = { + .name = "88pm860x-rtc", + .pm = &pm860x_rtc_pm_ops, + }, + .probe = pm860x_rtc_probe, + .remove = pm860x_rtc_remove, +}; + +module_platform_driver(pm860x_rtc_driver); + +MODULE_DESCRIPTION("Marvell 88PM860x RTC driver"); +MODULE_AUTHOR("Haojian Zhuang "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-88pm886.c b/drivers/rtc/rtc-88pm886.c new file mode 100644 index 000000000..71443ae1b --- /dev/null +++ b/drivers/rtc/rtc-88pm886.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include + +#include + +/* + * Time is calculated as the sum of a 32-bit read-only advancing counter and a + * writeable constant offset stored in the chip's spare registers. + */ + +static int pm886_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct regmap *regmap = dev_get_drvdata(dev); + u32 time; + u32 buf; + int ret; + + ret = regmap_bulk_read(regmap, PM886_REG_RTC_SPARE1, &buf, 4); + if (ret) + return ret; + time = buf; + + ret = regmap_bulk_read(regmap, PM886_REG_RTC_CNT1, &buf, 4); + if (ret) + return ret; + time += buf; + + rtc_time64_to_tm(time, tm); + + return 0; +} + +static int pm886_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct regmap *regmap = dev_get_drvdata(dev); + u32 buf; + int ret; + + ret = regmap_bulk_read(regmap, PM886_REG_RTC_CNT1, &buf, 4); + if (ret) + return ret; + + buf = rtc_tm_to_time64(tm) - buf; + + return regmap_bulk_write(regmap, PM886_REG_RTC_SPARE1, &buf, 4); +} + +static const struct rtc_class_ops pm886_rtc_ops = { + .read_time = pm886_rtc_read_time, + .set_time = pm886_rtc_set_time, +}; + +static int pm886_rtc_probe(struct platform_device *pdev) +{ + struct pm886_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct rtc_device *rtc; + int ret; + + platform_set_drvdata(pdev, chip->regmap); + + rtc = devm_rtc_allocate_device(dev); + if (IS_ERR(rtc)) + return dev_err_probe(dev, PTR_ERR(rtc), + "Failed to allocate RTC device\n"); + + rtc->ops = &pm886_rtc_ops; + rtc->range_max = U32_MAX; + + ret = devm_rtc_register_device(rtc); + if (ret) + return dev_err_probe(dev, ret, "Failed to register RTC device\n"); + + return 0; +} + +static const struct platform_device_id pm886_rtc_id_table[] = { + { .name = "88pm886-rtc" }, + { } +}; +MODULE_DEVICE_TABLE(platform, pm886_rtc_id_table); + +static struct platform_driver pm886_rtc_driver = { + .driver = { + .name = "88pm886-rtc", + }, + .probe = pm886_rtc_probe, + .id_table = pm886_rtc_id_table, +}; +module_platform_driver(pm886_rtc_driver); + +MODULE_DESCRIPTION("Marvell 88PM886 RTC driver"); +MODULE_AUTHOR("Karel Balej "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-ab-b5ze-s3.c b/drivers/rtc/rtc-ab-b5ze-s3.c new file mode 100644 index 000000000..6439ca427 --- /dev/null +++ b/drivers/rtc/rtc-ab-b5ze-s3.c @@ -0,0 +1,954 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * rtc-ab-b5ze-s3 - Driver for Abracon AB-RTCMC-32.768Khz-B5ZE-S3 + * I2C RTC / Alarm chip + * + * Copyright (C) 2014, Arnaud EBALARD + * + * Detailed datasheet of the chip is available here: + * + * https://www.abracon.com/realtimeclock/AB-RTCMC-32.768kHz-B5ZE-S3-Application-Manual.pdf + * + * This work is based on ISL12057 driver (drivers/rtc/rtc-isl12057.c). + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "rtc-ab-b5ze-s3" + +/* Control section */ +#define ABB5ZES3_REG_CTRL1 0x00 /* Control 1 register */ +#define ABB5ZES3_REG_CTRL1_CIE BIT(0) /* Pulse interrupt enable */ +#define ABB5ZES3_REG_CTRL1_AIE BIT(1) /* Alarm interrupt enable */ +#define ABB5ZES3_REG_CTRL1_SIE BIT(2) /* Second interrupt enable */ +#define ABB5ZES3_REG_CTRL1_PM BIT(3) /* 24h/12h mode */ +#define ABB5ZES3_REG_CTRL1_SR BIT(4) /* Software reset */ +#define ABB5ZES3_REG_CTRL1_STOP BIT(5) /* RTC circuit enable */ +#define ABB5ZES3_REG_CTRL1_CAP BIT(7) + +#define ABB5ZES3_REG_CTRL2 0x01 /* Control 2 register */ +#define ABB5ZES3_REG_CTRL2_CTBIE BIT(0) /* Countdown timer B int. enable */ +#define ABB5ZES3_REG_CTRL2_CTAIE BIT(1) /* Countdown timer A int. enable */ +#define ABB5ZES3_REG_CTRL2_WTAIE BIT(2) /* Watchdog timer A int. enable */ +#define ABB5ZES3_REG_CTRL2_AF BIT(3) /* Alarm interrupt status */ +#define ABB5ZES3_REG_CTRL2_SF BIT(4) /* Second interrupt status */ +#define ABB5ZES3_REG_CTRL2_CTBF BIT(5) /* Countdown timer B int. status */ +#define ABB5ZES3_REG_CTRL2_CTAF BIT(6) /* Countdown timer A int. status */ +#define ABB5ZES3_REG_CTRL2_WTAF BIT(7) /* Watchdog timer A int. status */ + +#define ABB5ZES3_REG_CTRL3 0x02 /* Control 3 register */ +#define ABB5ZES3_REG_CTRL3_PM2 BIT(7) /* Power Management bit 2 */ +#define ABB5ZES3_REG_CTRL3_PM1 BIT(6) /* Power Management bit 1 */ +#define ABB5ZES3_REG_CTRL3_PM0 BIT(5) /* Power Management bit 0 */ +#define ABB5ZES3_REG_CTRL3_BSF BIT(3) /* Battery switchover int. status */ +#define ABB5ZES3_REG_CTRL3_BLF BIT(2) /* Battery low int. status */ +#define ABB5ZES3_REG_CTRL3_BSIE BIT(1) /* Battery switchover int. enable */ +#define ABB5ZES3_REG_CTRL3_BLIE BIT(0) /* Battery low int. enable */ + +#define ABB5ZES3_CTRL_SEC_LEN 3 + +/* RTC section */ +#define ABB5ZES3_REG_RTC_SC 0x03 /* RTC Seconds register */ +#define ABB5ZES3_REG_RTC_SC_OSC BIT(7) /* Clock integrity status */ +#define ABB5ZES3_REG_RTC_MN 0x04 /* RTC Minutes register */ +#define ABB5ZES3_REG_RTC_HR 0x05 /* RTC Hours register */ +#define ABB5ZES3_REG_RTC_HR_PM BIT(5) /* RTC Hours PM bit */ +#define ABB5ZES3_REG_RTC_DT 0x06 /* RTC Date register */ +#define ABB5ZES3_REG_RTC_DW 0x07 /* RTC Day of the week register */ +#define ABB5ZES3_REG_RTC_MO 0x08 /* RTC Month register */ +#define ABB5ZES3_REG_RTC_YR 0x09 /* RTC Year register */ + +#define ABB5ZES3_RTC_SEC_LEN 7 + +/* Alarm section (enable bits are all active low) */ +#define ABB5ZES3_REG_ALRM_MN 0x0A /* Alarm - minute register */ +#define ABB5ZES3_REG_ALRM_MN_AE BIT(7) /* Minute enable */ +#define ABB5ZES3_REG_ALRM_HR 0x0B /* Alarm - hours register */ +#define ABB5ZES3_REG_ALRM_HR_AE BIT(7) /* Hour enable */ +#define ABB5ZES3_REG_ALRM_DT 0x0C /* Alarm - date register */ +#define ABB5ZES3_REG_ALRM_DT_AE BIT(7) /* Date (day of the month) enable */ +#define ABB5ZES3_REG_ALRM_DW 0x0D /* Alarm - day of the week reg. */ +#define ABB5ZES3_REG_ALRM_DW_AE BIT(7) /* Day of the week enable */ + +#define ABB5ZES3_ALRM_SEC_LEN 4 + +/* Frequency offset section */ +#define ABB5ZES3_REG_FREQ_OF 0x0E /* Frequency offset register */ +#define ABB5ZES3_REG_FREQ_OF_MODE 0x0E /* Offset mode: 2 hours / minute */ + +/* CLOCKOUT section */ +#define ABB5ZES3_REG_TIM_CLK 0x0F /* Timer & Clockout register */ +#define ABB5ZES3_REG_TIM_CLK_TAM BIT(7) /* Permanent/pulsed timer A/int. 2 */ +#define ABB5ZES3_REG_TIM_CLK_TBM BIT(6) /* Permanent/pulsed timer B */ +#define ABB5ZES3_REG_TIM_CLK_COF2 BIT(5) /* Clkout Freq bit 2 */ +#define ABB5ZES3_REG_TIM_CLK_COF1 BIT(4) /* Clkout Freq bit 1 */ +#define ABB5ZES3_REG_TIM_CLK_COF0 BIT(3) /* Clkout Freq bit 0 */ +#define ABB5ZES3_REG_TIM_CLK_TAC1 BIT(2) /* Timer A: - 01 : countdown */ +#define ABB5ZES3_REG_TIM_CLK_TAC0 BIT(1) /* - 10 : timer */ +#define ABB5ZES3_REG_TIM_CLK_TBC BIT(0) /* Timer B enable */ + +/* Timer A Section */ +#define ABB5ZES3_REG_TIMA_CLK 0x10 /* Timer A clock register */ +#define ABB5ZES3_REG_TIMA_CLK_TAQ2 BIT(2) /* Freq bit 2 */ +#define ABB5ZES3_REG_TIMA_CLK_TAQ1 BIT(1) /* Freq bit 1 */ +#define ABB5ZES3_REG_TIMA_CLK_TAQ0 BIT(0) /* Freq bit 0 */ +#define ABB5ZES3_REG_TIMA 0x11 /* Timer A register */ + +#define ABB5ZES3_TIMA_SEC_LEN 2 + +/* Timer B Section */ +#define ABB5ZES3_REG_TIMB_CLK 0x12 /* Timer B clock register */ +#define ABB5ZES3_REG_TIMB_CLK_TBW2 BIT(6) +#define ABB5ZES3_REG_TIMB_CLK_TBW1 BIT(5) +#define ABB5ZES3_REG_TIMB_CLK_TBW0 BIT(4) +#define ABB5ZES3_REG_TIMB_CLK_TAQ2 BIT(2) +#define ABB5ZES3_REG_TIMB_CLK_TAQ1 BIT(1) +#define ABB5ZES3_REG_TIMB_CLK_TAQ0 BIT(0) +#define ABB5ZES3_REG_TIMB 0x13 /* Timer B register */ +#define ABB5ZES3_TIMB_SEC_LEN 2 + +#define ABB5ZES3_MEM_MAP_LEN 0x14 + +struct abb5zes3_rtc_data { + struct rtc_device *rtc; + struct regmap *regmap; + + int irq; + + bool battery_low; + bool timer_alarm; /* current alarm is via timer A */ +}; + +/* + * Try and match register bits w/ fixed null values to see whether we + * are dealing with an ABB5ZES3. + */ +static int abb5zes3_i2c_validate_chip(struct regmap *regmap) +{ + u8 regs[ABB5ZES3_MEM_MAP_LEN]; + static const u8 mask[ABB5ZES3_MEM_MAP_LEN] = { 0x00, 0x00, 0x10, 0x00, + 0x80, 0xc0, 0xc0, 0xf8, + 0xe0, 0x00, 0x00, 0x40, + 0x40, 0x78, 0x00, 0x00, + 0xf8, 0x00, 0x88, 0x00 }; + int ret, i; + + ret = regmap_bulk_read(regmap, 0, regs, ABB5ZES3_MEM_MAP_LEN); + if (ret) + return ret; + + for (i = 0; i < ABB5ZES3_MEM_MAP_LEN; ++i) { + if (regs[i] & mask[i]) /* check if bits are cleared */ + return -ENODEV; + } + + return 0; +} + +/* Clear alarm status bit. */ +static int _abb5zes3_rtc_clear_alarm(struct device *dev) +{ + struct abb5zes3_rtc_data *data = dev_get_drvdata(dev); + int ret; + + ret = regmap_update_bits(data->regmap, ABB5ZES3_REG_CTRL2, + ABB5ZES3_REG_CTRL2_AF, 0); + if (ret) + dev_err(dev, "%s: clearing alarm failed (%d)\n", __func__, ret); + + return ret; +} + +/* Enable or disable alarm (i.e. alarm interrupt generation) */ +static int _abb5zes3_rtc_update_alarm(struct device *dev, bool enable) +{ + struct abb5zes3_rtc_data *data = dev_get_drvdata(dev); + int ret; + + ret = regmap_update_bits(data->regmap, ABB5ZES3_REG_CTRL1, + ABB5ZES3_REG_CTRL1_AIE, + enable ? ABB5ZES3_REG_CTRL1_AIE : 0); + if (ret) + dev_err(dev, "%s: writing alarm INT failed (%d)\n", + __func__, ret); + + return ret; +} + +/* Enable or disable timer (watchdog timer A interrupt generation) */ +static int _abb5zes3_rtc_update_timer(struct device *dev, bool enable) +{ + struct abb5zes3_rtc_data *data = dev_get_drvdata(dev); + int ret; + + ret = regmap_update_bits(data->regmap, ABB5ZES3_REG_CTRL2, + ABB5ZES3_REG_CTRL2_WTAIE, + enable ? ABB5ZES3_REG_CTRL2_WTAIE : 0); + if (ret) + dev_err(dev, "%s: writing timer INT failed (%d)\n", + __func__, ret); + + return ret; +} + +/* + * Note: we only read, so regmap inner lock protection is sufficient, i.e. + * we do not need driver's main lock protection. + */ +static int _abb5zes3_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct abb5zes3_rtc_data *data = dev_get_drvdata(dev); + u8 regs[ABB5ZES3_REG_RTC_SC + ABB5ZES3_RTC_SEC_LEN]; + int ret = 0; + + /* + * As we need to read CTRL1 register anyway to access 24/12h + * mode bit, we do a single bulk read of both control and RTC + * sections (they are consecutive). This also ease indexing + * of register values after bulk read. + */ + ret = regmap_bulk_read(data->regmap, ABB5ZES3_REG_CTRL1, regs, + sizeof(regs)); + if (ret) { + dev_err(dev, "%s: reading RTC time failed (%d)\n", + __func__, ret); + return ret; + } + + /* If clock integrity is not guaranteed, do not return a time value */ + if (regs[ABB5ZES3_REG_RTC_SC] & ABB5ZES3_REG_RTC_SC_OSC) + return -ENODATA; + + tm->tm_sec = bcd2bin(regs[ABB5ZES3_REG_RTC_SC] & 0x7F); + tm->tm_min = bcd2bin(regs[ABB5ZES3_REG_RTC_MN]); + + if (regs[ABB5ZES3_REG_CTRL1] & ABB5ZES3_REG_CTRL1_PM) { /* 12hr mode */ + tm->tm_hour = bcd2bin(regs[ABB5ZES3_REG_RTC_HR] & 0x1f); + if (regs[ABB5ZES3_REG_RTC_HR] & ABB5ZES3_REG_RTC_HR_PM) /* PM */ + tm->tm_hour += 12; + } else { /* 24hr mode */ + tm->tm_hour = bcd2bin(regs[ABB5ZES3_REG_RTC_HR]); + } + + tm->tm_mday = bcd2bin(regs[ABB5ZES3_REG_RTC_DT]); + tm->tm_wday = bcd2bin(regs[ABB5ZES3_REG_RTC_DW]); + tm->tm_mon = bcd2bin(regs[ABB5ZES3_REG_RTC_MO]) - 1; /* starts at 1 */ + tm->tm_year = bcd2bin(regs[ABB5ZES3_REG_RTC_YR]) + 100; + + return ret; +} + +static int abb5zes3_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct abb5zes3_rtc_data *data = dev_get_drvdata(dev); + u8 regs[ABB5ZES3_REG_RTC_SC + ABB5ZES3_RTC_SEC_LEN]; + int ret; + + regs[ABB5ZES3_REG_RTC_SC] = bin2bcd(tm->tm_sec); /* MSB=0 clears OSC */ + regs[ABB5ZES3_REG_RTC_MN] = bin2bcd(tm->tm_min); + regs[ABB5ZES3_REG_RTC_HR] = bin2bcd(tm->tm_hour); /* 24-hour format */ + regs[ABB5ZES3_REG_RTC_DT] = bin2bcd(tm->tm_mday); + regs[ABB5ZES3_REG_RTC_DW] = bin2bcd(tm->tm_wday); + regs[ABB5ZES3_REG_RTC_MO] = bin2bcd(tm->tm_mon + 1); + regs[ABB5ZES3_REG_RTC_YR] = bin2bcd(tm->tm_year - 100); + + ret = regmap_bulk_write(data->regmap, ABB5ZES3_REG_RTC_SC, + regs + ABB5ZES3_REG_RTC_SC, + ABB5ZES3_RTC_SEC_LEN); + + return ret; +} + +/* + * Set provided TAQ and Timer A registers (TIMA_CLK and TIMA) based on + * given number of seconds. + */ +static inline void sec_to_timer_a(u8 secs, u8 *taq, u8 *timer_a) +{ + *taq = ABB5ZES3_REG_TIMA_CLK_TAQ1; /* 1Hz */ + *timer_a = secs; +} + +/* + * Return current number of seconds in Timer A. As we only use + * timer A with a 1Hz freq, this is what we expect to have. + */ +static inline int sec_from_timer_a(u8 *secs, u8 taq, u8 timer_a) +{ + if (taq != ABB5ZES3_REG_TIMA_CLK_TAQ1) /* 1Hz */ + return -EINVAL; + + *secs = timer_a; + + return 0; +} + +/* + * Read alarm currently configured via a watchdog timer using timer A. This + * is done by reading current RTC time and adding remaining timer time. + */ +static int _abb5zes3_rtc_read_timer(struct device *dev, + struct rtc_wkalrm *alarm) +{ + struct abb5zes3_rtc_data *data = dev_get_drvdata(dev); + struct rtc_time rtc_tm, *alarm_tm = &alarm->time; + u8 regs[ABB5ZES3_TIMA_SEC_LEN + 1]; + unsigned long rtc_secs; + unsigned int reg; + u8 timer_secs; + int ret; + + /* + * Instead of doing two separate calls, because they are consecutive, + * we grab both clockout register and Timer A section. The latter is + * used to decide if timer A is enabled (as a watchdog timer). + */ + ret = regmap_bulk_read(data->regmap, ABB5ZES3_REG_TIM_CLK, regs, + ABB5ZES3_TIMA_SEC_LEN + 1); + if (ret) { + dev_err(dev, "%s: reading Timer A section failed (%d)\n", + __func__, ret); + return ret; + } + + /* get current time ... */ + ret = _abb5zes3_rtc_read_time(dev, &rtc_tm); + if (ret) + return ret; + + /* ... convert to seconds ... */ + rtc_secs = rtc_tm_to_time64(&rtc_tm); + + /* ... add remaining timer A time ... */ + ret = sec_from_timer_a(&timer_secs, regs[1], regs[2]); + if (ret) + return ret; + + /* ... and convert back. */ + rtc_time64_to_tm(rtc_secs + timer_secs, alarm_tm); + + ret = regmap_read(data->regmap, ABB5ZES3_REG_CTRL2, ®); + if (ret) { + dev_err(dev, "%s: reading ctrl reg failed (%d)\n", + __func__, ret); + return ret; + } + + alarm->enabled = !!(reg & ABB5ZES3_REG_CTRL2_WTAIE); + + return 0; +} + +/* Read alarm currently configured via a RTC alarm registers. */ +static int _abb5zes3_rtc_read_alarm(struct device *dev, + struct rtc_wkalrm *alarm) +{ + struct abb5zes3_rtc_data *data = dev_get_drvdata(dev); + struct rtc_time rtc_tm, *alarm_tm = &alarm->time; + unsigned long rtc_secs, alarm_secs; + u8 regs[ABB5ZES3_ALRM_SEC_LEN]; + unsigned int reg; + int ret; + + ret = regmap_bulk_read(data->regmap, ABB5ZES3_REG_ALRM_MN, regs, + ABB5ZES3_ALRM_SEC_LEN); + if (ret) { + dev_err(dev, "%s: reading alarm section failed (%d)\n", + __func__, ret); + return ret; + } + + alarm_tm->tm_sec = 0; + alarm_tm->tm_min = bcd2bin(regs[0] & 0x7f); + alarm_tm->tm_hour = bcd2bin(regs[1] & 0x3f); + alarm_tm->tm_mday = bcd2bin(regs[2] & 0x3f); + alarm_tm->tm_wday = -1; + + /* + * The alarm section does not store year/month. We use the ones in rtc + * section as a basis and increment month and then year if needed to get + * alarm after current time. + */ + ret = _abb5zes3_rtc_read_time(dev, &rtc_tm); + if (ret) + return ret; + + alarm_tm->tm_year = rtc_tm.tm_year; + alarm_tm->tm_mon = rtc_tm.tm_mon; + + rtc_secs = rtc_tm_to_time64(&rtc_tm); + alarm_secs = rtc_tm_to_time64(alarm_tm); + + if (alarm_secs < rtc_secs) { + if (alarm_tm->tm_mon == 11) { + alarm_tm->tm_mon = 0; + alarm_tm->tm_year += 1; + } else { + alarm_tm->tm_mon += 1; + } + } + + ret = regmap_read(data->regmap, ABB5ZES3_REG_CTRL1, ®); + if (ret) { + dev_err(dev, "%s: reading ctrl reg failed (%d)\n", + __func__, ret); + return ret; + } + + alarm->enabled = !!(reg & ABB5ZES3_REG_CTRL1_AIE); + + return 0; +} + +/* + * As the Alarm mechanism supported by the chip is only accurate to the + * minute, we use the watchdog timer mechanism provided by timer A + * (up to 256 seconds w/ a second accuracy) for low alarm values (below + * 4 minutes). Otherwise, we use the common alarm mechanism provided + * by the chip. In order for that to work, we keep track of currently + * configured timer type via 'timer_alarm' flag in our private data + * structure. + */ +static int abb5zes3_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct abb5zes3_rtc_data *data = dev_get_drvdata(dev); + int ret; + + if (data->timer_alarm) + ret = _abb5zes3_rtc_read_timer(dev, alarm); + else + ret = _abb5zes3_rtc_read_alarm(dev, alarm); + + return ret; +} + +/* + * Set alarm using chip alarm mechanism. It is only accurate to the + * minute (not the second). The function expects alarm interrupt to + * be disabled. + */ +static int _abb5zes3_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct abb5zes3_rtc_data *data = dev_get_drvdata(dev); + struct rtc_time *alarm_tm = &alarm->time; + u8 regs[ABB5ZES3_ALRM_SEC_LEN]; + struct rtc_time rtc_tm; + int ret, enable = 1; + + if (!alarm->enabled) { + enable = 0; + } else { + unsigned long rtc_secs, alarm_secs; + + /* + * Chip only support alarms up to one month in the future. Let's + * return an error if we get something after that limit. + * Comparison is done by incrementing rtc_tm month field by one + * and checking alarm value is still below. + */ + ret = _abb5zes3_rtc_read_time(dev, &rtc_tm); + if (ret) + return ret; + + if (rtc_tm.tm_mon == 11) { /* handle year wrapping */ + rtc_tm.tm_mon = 0; + rtc_tm.tm_year += 1; + } else { + rtc_tm.tm_mon += 1; + } + + rtc_secs = rtc_tm_to_time64(&rtc_tm); + alarm_secs = rtc_tm_to_time64(alarm_tm); + + if (alarm_secs > rtc_secs) { + dev_err(dev, "%s: alarm maximum is one month in the future (%d)\n", + __func__, ret); + return -EINVAL; + } + } + + /* + * Program all alarm registers but DW one. For each register, setting + * MSB to 0 enables associated alarm. + */ + regs[0] = bin2bcd(alarm_tm->tm_min) & 0x7f; + regs[1] = bin2bcd(alarm_tm->tm_hour) & 0x3f; + regs[2] = bin2bcd(alarm_tm->tm_mday) & 0x3f; + regs[3] = ABB5ZES3_REG_ALRM_DW_AE; /* do not match day of the week */ + + ret = regmap_bulk_write(data->regmap, ABB5ZES3_REG_ALRM_MN, regs, + ABB5ZES3_ALRM_SEC_LEN); + if (ret < 0) { + dev_err(dev, "%s: writing ALARM section failed (%d)\n", + __func__, ret); + return ret; + } + + /* Record currently configured alarm is not a timer */ + data->timer_alarm = 0; + + /* Enable or disable alarm interrupt generation */ + return _abb5zes3_rtc_update_alarm(dev, enable); +} + +/* + * Set alarm using timer watchdog (via timer A) mechanism. The function expects + * timer A interrupt to be disabled. + */ +static int _abb5zes3_rtc_set_timer(struct device *dev, struct rtc_wkalrm *alarm, + u8 secs) +{ + struct abb5zes3_rtc_data *data = dev_get_drvdata(dev); + u8 regs[ABB5ZES3_TIMA_SEC_LEN]; + u8 mask = ABB5ZES3_REG_TIM_CLK_TAC0 | ABB5ZES3_REG_TIM_CLK_TAC1; + int ret = 0; + + /* Program given number of seconds to Timer A registers */ + sec_to_timer_a(secs, ®s[0], ®s[1]); + ret = regmap_bulk_write(data->regmap, ABB5ZES3_REG_TIMA_CLK, regs, + ABB5ZES3_TIMA_SEC_LEN); + if (ret < 0) { + dev_err(dev, "%s: writing timer section failed\n", __func__); + return ret; + } + + /* Configure Timer A as a watchdog timer */ + ret = regmap_update_bits(data->regmap, ABB5ZES3_REG_TIM_CLK, + mask, ABB5ZES3_REG_TIM_CLK_TAC1); + if (ret) + dev_err(dev, "%s: failed to update timer\n", __func__); + + /* Record currently configured alarm is a timer */ + data->timer_alarm = 1; + + /* Enable or disable timer interrupt generation */ + return _abb5zes3_rtc_update_timer(dev, alarm->enabled); +} + +/* + * The chip has an alarm which is only accurate to the minute. In order to + * handle alarms below that limit, we use the watchdog timer function of + * timer A. More precisely, the timer method is used for alarms below 240 + * seconds. + */ +static int abb5zes3_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct abb5zes3_rtc_data *data = dev_get_drvdata(dev); + struct rtc_time *alarm_tm = &alarm->time; + unsigned long rtc_secs, alarm_secs; + struct rtc_time rtc_tm; + int ret; + + ret = _abb5zes3_rtc_read_time(dev, &rtc_tm); + if (ret) + return ret; + + rtc_secs = rtc_tm_to_time64(&rtc_tm); + alarm_secs = rtc_tm_to_time64(alarm_tm); + + /* Let's first disable both the alarm and the timer interrupts */ + ret = _abb5zes3_rtc_update_alarm(dev, false); + if (ret < 0) { + dev_err(dev, "%s: unable to disable alarm (%d)\n", __func__, + ret); + return ret; + } + ret = _abb5zes3_rtc_update_timer(dev, false); + if (ret < 0) { + dev_err(dev, "%s: unable to disable timer (%d)\n", __func__, + ret); + return ret; + } + + data->timer_alarm = 0; + + /* + * Let's now configure the alarm; if we are expected to ring in + * more than 240s, then we setup an alarm. Otherwise, a timer. + */ + if ((alarm_secs > rtc_secs) && ((alarm_secs - rtc_secs) <= 240)) + ret = _abb5zes3_rtc_set_timer(dev, alarm, + alarm_secs - rtc_secs); + else + ret = _abb5zes3_rtc_set_alarm(dev, alarm); + + if (ret) + dev_err(dev, "%s: unable to configure alarm (%d)\n", __func__, + ret); + + return ret; +} + +/* Enable or disable battery low irq generation */ +static inline int _abb5zes3_rtc_battery_low_irq_enable(struct regmap *regmap, + bool enable) +{ + return regmap_update_bits(regmap, ABB5ZES3_REG_CTRL3, + ABB5ZES3_REG_CTRL3_BLIE, + enable ? ABB5ZES3_REG_CTRL3_BLIE : 0); +} + +/* + * Check current RTC status and enable/disable what needs to be. Return 0 if + * everything went ok and a negative value upon error. + */ +static int abb5zes3_rtc_check_setup(struct device *dev) +{ + struct abb5zes3_rtc_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + unsigned int reg; + int ret; + u8 mask; + + /* + * By default, the devices generates a 32.768KHz signal on IRQ#1 pin. It + * is disabled here to prevent polluting the interrupt line and + * uselessly triggering the IRQ handler we install for alarm and battery + * low events. Note: this is done before clearing int. status below + * in this function. + * We also disable all timers and set timer interrupt to permanent (not + * pulsed). + */ + mask = (ABB5ZES3_REG_TIM_CLK_TBC | ABB5ZES3_REG_TIM_CLK_TAC0 | + ABB5ZES3_REG_TIM_CLK_TAC1 | ABB5ZES3_REG_TIM_CLK_COF0 | + ABB5ZES3_REG_TIM_CLK_COF1 | ABB5ZES3_REG_TIM_CLK_COF2 | + ABB5ZES3_REG_TIM_CLK_TBM | ABB5ZES3_REG_TIM_CLK_TAM); + ret = regmap_update_bits(regmap, ABB5ZES3_REG_TIM_CLK, mask, + ABB5ZES3_REG_TIM_CLK_COF0 | + ABB5ZES3_REG_TIM_CLK_COF1 | + ABB5ZES3_REG_TIM_CLK_COF2); + if (ret < 0) { + dev_err(dev, "%s: unable to initialize clkout register (%d)\n", + __func__, ret); + return ret; + } + + /* + * Each component of the alarm (MN, HR, DT, DW) can be enabled/disabled + * individually by clearing/setting MSB of each associated register. So, + * we set all alarm enable bits to disable current alarm setting. + */ + mask = (ABB5ZES3_REG_ALRM_MN_AE | ABB5ZES3_REG_ALRM_HR_AE | + ABB5ZES3_REG_ALRM_DT_AE | ABB5ZES3_REG_ALRM_DW_AE); + ret = regmap_update_bits(regmap, ABB5ZES3_REG_CTRL2, mask, mask); + if (ret < 0) { + dev_err(dev, "%s: unable to disable alarm setting (%d)\n", + __func__, ret); + return ret; + } + + /* Set Control 1 register (RTC enabled, 24hr mode, all int. disabled) */ + mask = (ABB5ZES3_REG_CTRL1_CIE | ABB5ZES3_REG_CTRL1_AIE | + ABB5ZES3_REG_CTRL1_SIE | ABB5ZES3_REG_CTRL1_PM | + ABB5ZES3_REG_CTRL1_CAP | ABB5ZES3_REG_CTRL1_STOP); + ret = regmap_update_bits(regmap, ABB5ZES3_REG_CTRL1, mask, 0); + if (ret < 0) { + dev_err(dev, "%s: unable to initialize CTRL1 register (%d)\n", + __func__, ret); + return ret; + } + + /* + * Set Control 2 register (timer int. disabled, alarm status cleared). + * WTAF is read-only and cleared automatically by reading the register. + */ + mask = (ABB5ZES3_REG_CTRL2_CTBIE | ABB5ZES3_REG_CTRL2_CTAIE | + ABB5ZES3_REG_CTRL2_WTAIE | ABB5ZES3_REG_CTRL2_AF | + ABB5ZES3_REG_CTRL2_SF | ABB5ZES3_REG_CTRL2_CTBF | + ABB5ZES3_REG_CTRL2_CTAF); + ret = regmap_update_bits(regmap, ABB5ZES3_REG_CTRL2, mask, 0); + if (ret < 0) { + dev_err(dev, "%s: unable to initialize CTRL2 register (%d)\n", + __func__, ret); + return ret; + } + + /* + * Enable battery low detection function and battery switchover function + * (standard mode). Disable associated interrupts. Clear battery + * switchover flag but not battery low flag. The latter is checked + * later below. + */ + mask = (ABB5ZES3_REG_CTRL3_PM0 | ABB5ZES3_REG_CTRL3_PM1 | + ABB5ZES3_REG_CTRL3_PM2 | ABB5ZES3_REG_CTRL3_BLIE | + ABB5ZES3_REG_CTRL3_BSIE | ABB5ZES3_REG_CTRL3_BSF); + ret = regmap_update_bits(regmap, ABB5ZES3_REG_CTRL3, mask, 0); + if (ret < 0) { + dev_err(dev, "%s: unable to initialize CTRL3 register (%d)\n", + __func__, ret); + return ret; + } + + /* Check oscillator integrity flag */ + ret = regmap_read(regmap, ABB5ZES3_REG_RTC_SC, ®); + if (ret < 0) { + dev_err(dev, "%s: unable to read osc. integrity flag (%d)\n", + __func__, ret); + return ret; + } + + if (reg & ABB5ZES3_REG_RTC_SC_OSC) { + dev_err(dev, "clock integrity not guaranteed. Osc. has stopped or has been interrupted.\n"); + dev_err(dev, "change battery (if not already done) and then set time to reset osc. failure flag.\n"); + } + + /* + * Check battery low flag at startup: this allows reporting battery + * is low at startup when IRQ line is not connected. Note: we record + * current status to avoid reenabling this interrupt later in probe + * function if battery is low. + */ + ret = regmap_read(regmap, ABB5ZES3_REG_CTRL3, ®); + if (ret < 0) { + dev_err(dev, "%s: unable to read battery low flag (%d)\n", + __func__, ret); + return ret; + } + + data->battery_low = reg & ABB5ZES3_REG_CTRL3_BLF; + if (data->battery_low) { + dev_err(dev, "RTC battery is low; please, consider changing it!\n"); + + ret = _abb5zes3_rtc_battery_low_irq_enable(regmap, false); + if (ret) + dev_err(dev, "%s: disabling battery low interrupt generation failed (%d)\n", + __func__, ret); + } + + return ret; +} + +static int abb5zes3_rtc_alarm_irq_enable(struct device *dev, + unsigned int enable) +{ + struct abb5zes3_rtc_data *rtc_data = dev_get_drvdata(dev); + int ret = 0; + + if (rtc_data->irq) { + if (rtc_data->timer_alarm) + ret = _abb5zes3_rtc_update_timer(dev, enable); + else + ret = _abb5zes3_rtc_update_alarm(dev, enable); + } + + return ret; +} + +static irqreturn_t _abb5zes3_rtc_interrupt(int irq, void *data) +{ + struct i2c_client *client = data; + struct device *dev = &client->dev; + struct abb5zes3_rtc_data *rtc_data = dev_get_drvdata(dev); + struct rtc_device *rtc = rtc_data->rtc; + u8 regs[ABB5ZES3_CTRL_SEC_LEN]; + int ret, handled = IRQ_NONE; + + ret = regmap_bulk_read(rtc_data->regmap, 0, regs, + ABB5ZES3_CTRL_SEC_LEN); + if (ret) { + dev_err(dev, "%s: unable to read control section (%d)!\n", + __func__, ret); + return handled; + } + + /* + * Check battery low detection flag and disable battery low interrupt + * generation if flag is set (interrupt can only be cleared when + * battery is replaced). + */ + if (regs[ABB5ZES3_REG_CTRL3] & ABB5ZES3_REG_CTRL3_BLF) { + dev_err(dev, "RTC battery is low; please change it!\n"); + + _abb5zes3_rtc_battery_low_irq_enable(rtc_data->regmap, false); + + handled = IRQ_HANDLED; + } + + /* Check alarm flag */ + if (regs[ABB5ZES3_REG_CTRL2] & ABB5ZES3_REG_CTRL2_AF) { + dev_dbg(dev, "RTC alarm!\n"); + + rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF); + + /* Acknowledge and disable the alarm */ + _abb5zes3_rtc_clear_alarm(dev); + _abb5zes3_rtc_update_alarm(dev, 0); + + handled = IRQ_HANDLED; + } + + /* Check watchdog Timer A flag */ + if (regs[ABB5ZES3_REG_CTRL2] & ABB5ZES3_REG_CTRL2_WTAF) { + dev_dbg(dev, "RTC timer!\n"); + + rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF); + + /* + * Acknowledge and disable the alarm. Note: WTAF + * flag had been cleared when reading CTRL2 + */ + _abb5zes3_rtc_update_timer(dev, 0); + + rtc_data->timer_alarm = 0; + + handled = IRQ_HANDLED; + } + + return handled; +} + +static const struct rtc_class_ops rtc_ops = { + .read_time = _abb5zes3_rtc_read_time, + .set_time = abb5zes3_rtc_set_time, + .read_alarm = abb5zes3_rtc_read_alarm, + .set_alarm = abb5zes3_rtc_set_alarm, + .alarm_irq_enable = abb5zes3_rtc_alarm_irq_enable, +}; + +static const struct regmap_config abb5zes3_rtc_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int abb5zes3_probe(struct i2c_client *client) +{ + struct abb5zes3_rtc_data *data = NULL; + struct device *dev = &client->dev; + struct regmap *regmap; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK)) + return -ENODEV; + + regmap = devm_regmap_init_i2c(client, &abb5zes3_rtc_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(dev, "%s: regmap allocation failed: %d\n", + __func__, ret); + return ret; + } + + ret = abb5zes3_i2c_validate_chip(regmap); + if (ret) + return ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->regmap = regmap; + dev_set_drvdata(dev, data); + + ret = abb5zes3_rtc_check_setup(dev); + if (ret) + return ret; + + data->rtc = devm_rtc_allocate_device(dev); + ret = PTR_ERR_OR_ZERO(data->rtc); + if (ret) { + dev_err(dev, "%s: unable to allocate RTC device (%d)\n", + __func__, ret); + return ret; + } + + if (client->irq > 0) { + ret = devm_request_threaded_irq(dev, client->irq, NULL, + _abb5zes3_rtc_interrupt, + IRQF_SHARED | IRQF_ONESHOT, + DRV_NAME, client); + if (!ret) { + device_init_wakeup(dev, true); + data->irq = client->irq; + dev_dbg(dev, "%s: irq %d used by RTC\n", __func__, + client->irq); + } else { + dev_err(dev, "%s: irq %d unavailable (%d)\n", + __func__, client->irq, ret); + goto err; + } + } + + data->rtc->ops = &rtc_ops; + data->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + data->rtc->range_max = RTC_TIMESTAMP_END_2099; + + /* Enable battery low detection interrupt if battery not already low */ + if (!data->battery_low && data->irq) { + ret = _abb5zes3_rtc_battery_low_irq_enable(regmap, true); + if (ret) { + dev_err(dev, "%s: enabling battery low interrupt generation failed (%d)\n", + __func__, ret); + goto err; + } + } + + ret = devm_rtc_register_device(data->rtc); + +err: + if (ret && data->irq) + device_init_wakeup(dev, false); + return ret; +} + +#ifdef CONFIG_PM_SLEEP +static int abb5zes3_rtc_suspend(struct device *dev) +{ + struct abb5zes3_rtc_data *rtc_data = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + return enable_irq_wake(rtc_data->irq); + + return 0; +} + +static int abb5zes3_rtc_resume(struct device *dev) +{ + struct abb5zes3_rtc_data *rtc_data = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + return disable_irq_wake(rtc_data->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(abb5zes3_rtc_pm_ops, abb5zes3_rtc_suspend, + abb5zes3_rtc_resume); + +#ifdef CONFIG_OF +static const struct of_device_id abb5zes3_dt_match[] = { + { .compatible = "abracon,abb5zes3" }, + { }, +}; +MODULE_DEVICE_TABLE(of, abb5zes3_dt_match); +#endif + +static const struct i2c_device_id abb5zes3_id[] = { + { .name = "abb5zes3" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, abb5zes3_id); + +static struct i2c_driver abb5zes3_driver = { + .driver = { + .name = DRV_NAME, + .pm = &abb5zes3_rtc_pm_ops, + .of_match_table = of_match_ptr(abb5zes3_dt_match), + }, + .probe = abb5zes3_probe, + .id_table = abb5zes3_id, +}; +module_i2c_driver(abb5zes3_driver); + +MODULE_AUTHOR("Arnaud EBALARD "); +MODULE_DESCRIPTION("Abracon AB-RTCMC-32.768kHz-B5ZE-S3 RTC/Alarm driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-ab-eoz9.c b/drivers/rtc/rtc-ab-eoz9.c new file mode 100644 index 000000000..eb6d267c2 --- /dev/null +++ b/drivers/rtc/rtc-ab-eoz9.c @@ -0,0 +1,567 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Real Time Clock driver for AB-RTCMC-32.768kHz-EOZ9 chip. + * Copyright (C) 2019 Orolia + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ABEOZ9_REG_CTRL1 0x00 +#define ABEOZ9_REG_CTRL1_MASK GENMASK(7, 0) +#define ABEOZ9_REG_CTRL1_WE BIT(0) +#define ABEOZ9_REG_CTRL1_TE BIT(1) +#define ABEOZ9_REG_CTRL1_TAR BIT(2) +#define ABEOZ9_REG_CTRL1_EERE BIT(3) +#define ABEOZ9_REG_CTRL1_SRON BIT(4) +#define ABEOZ9_REG_CTRL1_TD0 BIT(5) +#define ABEOZ9_REG_CTRL1_TD1 BIT(6) +#define ABEOZ9_REG_CTRL1_CLKINT BIT(7) + +#define ABEOZ9_REG_CTRL_INT 0x01 +#define ABEOZ9_REG_CTRL_INT_AIE BIT(0) +#define ABEOZ9_REG_CTRL_INT_TIE BIT(1) +#define ABEOZ9_REG_CTRL_INT_V1IE BIT(2) +#define ABEOZ9_REG_CTRL_INT_V2IE BIT(3) +#define ABEOZ9_REG_CTRL_INT_SRIE BIT(4) + +#define ABEOZ9_REG_CTRL_INT_FLAG 0x02 +#define ABEOZ9_REG_CTRL_INT_FLAG_AF BIT(0) +#define ABEOZ9_REG_CTRL_INT_FLAG_TF BIT(1) +#define ABEOZ9_REG_CTRL_INT_FLAG_V1IF BIT(2) +#define ABEOZ9_REG_CTRL_INT_FLAG_V2IF BIT(3) +#define ABEOZ9_REG_CTRL_INT_FLAG_SRF BIT(4) + +#define ABEOZ9_REG_CTRL_STATUS 0x03 +#define ABEOZ9_REG_CTRL_STATUS_V1F BIT(2) +#define ABEOZ9_REG_CTRL_STATUS_V2F BIT(3) +#define ABEOZ9_REG_CTRL_STATUS_SR BIT(4) +#define ABEOZ9_REG_CTRL_STATUS_PON BIT(5) +#define ABEOZ9_REG_CTRL_STATUS_EEBUSY BIT(7) + +#define ABEOZ9_REG_SEC 0x08 +#define ABEOZ9_REG_MIN 0x09 +#define ABEOZ9_REG_HOURS 0x0A +#define ABEOZ9_HOURS_PM BIT(6) +#define ABEOZ9_REG_DAYS 0x0B +#define ABEOZ9_REG_WEEKDAYS 0x0C +#define ABEOZ9_REG_MONTHS 0x0D +#define ABEOZ9_REG_YEARS 0x0E + +#define ABEOZ9_SEC_LEN 7 + +#define ABEOZ9_REG_ALARM_SEC 0x10 +#define ABEOZ9_BIT_ALARM_SEC GENMASK(6, 0) +#define ABEOZ9_REG_ALARM_MIN 0x11 +#define ABEOZ9_BIT_ALARM_MIN GENMASK(6, 0) +#define ABEOZ9_REG_ALARM_HOURS 0x12 +#define ABEOZ9_BIT_ALARM_HOURS_PM BIT(5) +#define ABEOZ9_BIT_ALARM_HOURS GENMASK(5, 0) +#define ABEOZ9_REG_ALARM_DAYS 0x13 +#define ABEOZ9_BIT_ALARM_DAYS GENMASK(5, 0) +#define ABEOZ9_REG_ALARM_WEEKDAYS 0x14 +#define ABEOZ9_BIT_ALARM_WEEKDAYS GENMASK(2, 0) +#define ABEOZ9_REG_ALARM_MONTHS 0x15 +#define ABEOZ9_BIT_ALARM_MONTHS GENMASK(4, 0) +#define ABEOZ9_REG_ALARM_YEARS 0x16 + +#define ABEOZ9_ALARM_LEN 7 +#define ABEOZ9_BIT_ALARM_AE BIT(7) + +#define ABEOZ9_REG_REG_TEMP 0x20 +#define ABEOZ953_TEMP_MAX 120 +#define ABEOZ953_TEMP_MIN -60 + +#define ABEOZ9_REG_EEPROM 0x30 +#define ABEOZ9_REG_EEPROM_MASK GENMASK(8, 0) +#define ABEOZ9_REG_EEPROM_THP BIT(0) +#define ABEOZ9_REG_EEPROM_THE BIT(1) +#define ABEOZ9_REG_EEPROM_FD0 BIT(2) +#define ABEOZ9_REG_EEPROM_FD1 BIT(3) +#define ABEOZ9_REG_EEPROM_R1K BIT(4) +#define ABEOZ9_REG_EEPROM_R5K BIT(5) +#define ABEOZ9_REG_EEPROM_R20K BIT(6) +#define ABEOZ9_REG_EEPROM_R80K BIT(7) + +struct abeoz9_rtc_data { + struct rtc_device *rtc; + struct regmap *regmap; + struct device *hwmon_dev; +}; + +static int abeoz9_check_validity(struct device *dev) +{ + struct abeoz9_rtc_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + int ret; + int val; + + ret = regmap_read(regmap, ABEOZ9_REG_CTRL_STATUS, &val); + if (ret < 0) { + dev_err(dev, + "unable to get CTRL_STATUS register (%d)\n", ret); + return ret; + } + + if (val & ABEOZ9_REG_CTRL_STATUS_PON) { + dev_warn(dev, "power-on reset detected, date is invalid\n"); + return -EINVAL; + } + + if (val & ABEOZ9_REG_CTRL_STATUS_V1F) { + dev_warn(dev, + "voltage drops below VLOW1 threshold, date is invalid\n"); + return -EINVAL; + } + + if ((val & ABEOZ9_REG_CTRL_STATUS_V2F)) { + dev_warn(dev, + "voltage drops below VLOW2 threshold, date is invalid\n"); + return -EINVAL; + } + + return 0; +} + +static int abeoz9_reset_validity(struct regmap *regmap) +{ + return regmap_update_bits(regmap, ABEOZ9_REG_CTRL_STATUS, + ABEOZ9_REG_CTRL_STATUS_V1F | + ABEOZ9_REG_CTRL_STATUS_V2F | + ABEOZ9_REG_CTRL_STATUS_PON, + 0); +} + +static int abeoz9_rtc_get_time(struct device *dev, struct rtc_time *tm) +{ + struct abeoz9_rtc_data *data = dev_get_drvdata(dev); + u8 regs[ABEOZ9_SEC_LEN]; + int ret; + + ret = abeoz9_check_validity(dev); + if (ret) + return ret; + + ret = regmap_bulk_read(data->regmap, ABEOZ9_REG_SEC, + regs, + sizeof(regs)); + if (ret) { + dev_err(dev, "reading RTC time failed (%d)\n", ret); + return ret; + } + + tm->tm_sec = bcd2bin(regs[ABEOZ9_REG_SEC - ABEOZ9_REG_SEC] & 0x7F); + tm->tm_min = bcd2bin(regs[ABEOZ9_REG_MIN - ABEOZ9_REG_SEC] & 0x7F); + + if (regs[ABEOZ9_REG_HOURS - ABEOZ9_REG_SEC] & ABEOZ9_HOURS_PM) { + tm->tm_hour = + bcd2bin(regs[ABEOZ9_REG_HOURS - ABEOZ9_REG_SEC] & 0x1f); + if (regs[ABEOZ9_REG_HOURS - ABEOZ9_REG_SEC] & ABEOZ9_HOURS_PM) + tm->tm_hour += 12; + } else { + tm->tm_hour = bcd2bin(regs[ABEOZ9_REG_HOURS - ABEOZ9_REG_SEC]); + } + + tm->tm_mday = bcd2bin(regs[ABEOZ9_REG_DAYS - ABEOZ9_REG_SEC]); + tm->tm_wday = bcd2bin(regs[ABEOZ9_REG_WEEKDAYS - ABEOZ9_REG_SEC]); + tm->tm_mon = bcd2bin(regs[ABEOZ9_REG_MONTHS - ABEOZ9_REG_SEC]) - 1; + tm->tm_year = bcd2bin(regs[ABEOZ9_REG_YEARS - ABEOZ9_REG_SEC]) + 100; + + return ret; +} + +static int abeoz9_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct abeoz9_rtc_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + u8 regs[ABEOZ9_SEC_LEN]; + int ret; + + regs[ABEOZ9_REG_SEC - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_sec); + regs[ABEOZ9_REG_MIN - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_min); + regs[ABEOZ9_REG_HOURS - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_hour); + regs[ABEOZ9_REG_DAYS - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_mday); + regs[ABEOZ9_REG_WEEKDAYS - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_wday); + regs[ABEOZ9_REG_MONTHS - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_mon + 1); + regs[ABEOZ9_REG_YEARS - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_year - 100); + + ret = regmap_bulk_write(data->regmap, ABEOZ9_REG_SEC, + regs, + sizeof(regs)); + + if (ret) { + dev_err(dev, "set RTC time failed (%d)\n", ret); + return ret; + } + + return abeoz9_reset_validity(regmap); +} + +static int abeoz9_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct abeoz9_rtc_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + u8 regs[ABEOZ9_ALARM_LEN]; + u8 val[2]; + int ret; + + ret = abeoz9_check_validity(dev); + if (ret) + return ret; + + ret = regmap_bulk_read(regmap, ABEOZ9_REG_CTRL_INT, val, sizeof(val)); + if (ret) + return ret; + + alarm->enabled = val[0] & ABEOZ9_REG_CTRL_INT_AIE; + alarm->pending = val[1] & ABEOZ9_REG_CTRL_INT_FLAG_AF; + + ret = regmap_bulk_read(regmap, ABEOZ9_REG_ALARM_SEC, regs, sizeof(regs)); + if (ret) + return ret; + + alarm->time.tm_sec = bcd2bin(FIELD_GET(ABEOZ9_BIT_ALARM_SEC, regs[0])); + alarm->time.tm_min = bcd2bin(FIELD_GET(ABEOZ9_BIT_ALARM_MIN, regs[1])); + alarm->time.tm_hour = bcd2bin(FIELD_GET(ABEOZ9_BIT_ALARM_HOURS, regs[2])); + + alarm->time.tm_mday = bcd2bin(FIELD_GET(ABEOZ9_BIT_ALARM_DAYS, regs[3])); + + return 0; +} + +static int abeoz9_rtc_alarm_irq_enable(struct device *dev, u32 enable) +{ + struct abeoz9_rtc_data *data = dev_get_drvdata(dev); + + return regmap_update_bits(data->regmap, ABEOZ9_REG_CTRL_INT, + ABEOZ9_REG_CTRL_INT_AIE, + FIELD_PREP(ABEOZ9_REG_CTRL_INT_AIE, enable)); +} + +static int abeoz9_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct abeoz9_rtc_data *data = dev_get_drvdata(dev); + u8 regs[ABEOZ9_ALARM_LEN] = {0}; + int ret; + + ret = regmap_update_bits(data->regmap, ABEOZ9_REG_CTRL_INT_FLAG, + ABEOZ9_REG_CTRL_INT_FLAG_AF, 0); + if (ret) + return ret; + + regs[0] = ABEOZ9_BIT_ALARM_AE | FIELD_PREP(ABEOZ9_BIT_ALARM_SEC, + bin2bcd(alarm->time.tm_sec)); + regs[1] = ABEOZ9_BIT_ALARM_AE | FIELD_PREP(ABEOZ9_BIT_ALARM_MIN, + bin2bcd(alarm->time.tm_min)); + regs[2] = ABEOZ9_BIT_ALARM_AE | FIELD_PREP(ABEOZ9_BIT_ALARM_HOURS, + bin2bcd(alarm->time.tm_hour)); + regs[3] = ABEOZ9_BIT_ALARM_AE | FIELD_PREP(ABEOZ9_BIT_ALARM_DAYS, + bin2bcd(alarm->time.tm_mday)); + + ret = regmap_bulk_write(data->regmap, ABEOZ9_REG_ALARM_SEC, regs, + sizeof(regs)); + if (ret) + return ret; + + return abeoz9_rtc_alarm_irq_enable(dev, alarm->enabled); +} + +static irqreturn_t abeoz9_rtc_irq(int irq, void *dev) +{ + struct abeoz9_rtc_data *data = dev_get_drvdata(dev); + unsigned int val; + int ret; + + ret = regmap_read(data->regmap, ABEOZ9_REG_CTRL_INT_FLAG, &val); + if (ret) + return IRQ_NONE; + + if (!FIELD_GET(ABEOZ9_REG_CTRL_INT_FLAG_AF, val)) + return IRQ_NONE; + + regmap_update_bits(data->regmap, ABEOZ9_REG_CTRL_INT_FLAG, + ABEOZ9_REG_CTRL_INT_FLAG_AF, 0); + + rtc_update_irq(data->rtc, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static int abeoz9_trickle_parse_dt(struct device_node *node) +{ + u32 ohms = 0; + + if (of_property_read_u32(node, "trickle-resistor-ohms", &ohms)) + return 0; + + switch (ohms) { + case 1000: + return ABEOZ9_REG_EEPROM_R1K; + case 5000: + return ABEOZ9_REG_EEPROM_R5K; + case 20000: + return ABEOZ9_REG_EEPROM_R20K; + case 80000: + return ABEOZ9_REG_EEPROM_R80K; + default: + return 0; + } +} + +static int abeoz9_rtc_setup(struct device *dev, struct device_node *node) +{ + struct abeoz9_rtc_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + int ret; + + /* Enable Self Recovery, Clock for Watch and EEPROM refresh functions */ + ret = regmap_update_bits(regmap, ABEOZ9_REG_CTRL1, + ABEOZ9_REG_CTRL1_MASK, + ABEOZ9_REG_CTRL1_WE | + ABEOZ9_REG_CTRL1_EERE | + ABEOZ9_REG_CTRL1_SRON); + if (ret < 0) { + dev_err(dev, "unable to set CTRL_1 register (%d)\n", ret); + return ret; + } + + ret = regmap_write(regmap, ABEOZ9_REG_CTRL_INT, 0); + if (ret < 0) { + dev_err(dev, + "unable to set control CTRL_INT register (%d)\n", + ret); + return ret; + } + + ret = regmap_write(regmap, ABEOZ9_REG_CTRL_INT_FLAG, 0); + if (ret < 0) { + dev_err(dev, + "unable to set control CTRL_INT_FLAG register (%d)\n", + ret); + return ret; + } + + ret = abeoz9_trickle_parse_dt(node); + + /* Enable built-in termometer */ + ret |= ABEOZ9_REG_EEPROM_THE; + + ret = regmap_update_bits(regmap, ABEOZ9_REG_EEPROM, + ABEOZ9_REG_EEPROM_MASK, + ret); + if (ret < 0) { + dev_err(dev, "unable to set EEPROM register (%d)\n", ret); + return ret; + } + + return ret; +} + +static const struct rtc_class_ops rtc_ops = { + .read_time = abeoz9_rtc_get_time, + .set_time = abeoz9_rtc_set_time, + .read_alarm = abeoz9_rtc_read_alarm, + .set_alarm = abeoz9_rtc_set_alarm, + .alarm_irq_enable = abeoz9_rtc_alarm_irq_enable, +}; + +static const struct regmap_config abeoz9_rtc_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x3f, +}; + +#if IS_REACHABLE(CONFIG_HWMON) + +static int abeoz9z3_temp_read(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, long *temp) +{ + struct abeoz9_rtc_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + int ret; + unsigned int val; + + ret = regmap_read(regmap, ABEOZ9_REG_CTRL_STATUS, &val); + if (ret < 0) + return ret; + + switch (attr) { + case hwmon_temp_input: + ret = regmap_read(regmap, ABEOZ9_REG_REG_TEMP, &val); + if (ret < 0) + return ret; + *temp = 1000 * (val + ABEOZ953_TEMP_MIN); + return 0; + case hwmon_temp_max: + *temp = 1000 * ABEOZ953_TEMP_MAX; + return 0; + case hwmon_temp_min: + *temp = 1000 * ABEOZ953_TEMP_MIN; + return 0; + default: + return -EOPNOTSUPP; + } +} + +static umode_t abeoz9_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_max: + case hwmon_temp_min: + return 0444; + default: + return 0; + } +} + +static const struct hwmon_channel_info * const abeoz9_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN), + NULL +}; + +static const struct hwmon_ops abeoz9_hwmon_ops = { + .is_visible = abeoz9_is_visible, + .read = abeoz9z3_temp_read, +}; + +static const struct hwmon_chip_info abeoz9_chip_info = { + .ops = &abeoz9_hwmon_ops, + .info = abeoz9_info, +}; + +static void abeoz9_hwmon_register(struct device *dev, + struct abeoz9_rtc_data *data) +{ + data->hwmon_dev = + devm_hwmon_device_register_with_info(dev, + "abeoz9", + data, + &abeoz9_chip_info, + NULL); + if (IS_ERR(data->hwmon_dev)) { + dev_warn(dev, "unable to register hwmon device %ld\n", + PTR_ERR(data->hwmon_dev)); + } +} + +#else + +static void abeoz9_hwmon_register(struct device *dev, + struct abeoz9_rtc_data *data) +{ +} + +#endif + +static int abeoz9_probe(struct i2c_client *client) +{ + struct abeoz9_rtc_data *data = NULL; + struct device *dev = &client->dev; + struct regmap *regmap; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK)) + return -ENODEV; + + regmap = devm_regmap_init_i2c(client, &abeoz9_rtc_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(dev, "regmap allocation failed: %d\n", ret); + return ret; + } + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->regmap = regmap; + dev_set_drvdata(dev, data); + + ret = abeoz9_rtc_setup(dev, client->dev.of_node); + if (ret) + return ret; + + data->rtc = devm_rtc_allocate_device(dev); + ret = PTR_ERR_OR_ZERO(data->rtc); + if (ret) + return ret; + + data->rtc->ops = &rtc_ops; + data->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + data->rtc->range_max = RTC_TIMESTAMP_END_2099; + clear_bit(RTC_FEATURE_ALARM, data->rtc->features); + + if (client->irq > 0) { + unsigned long irqflags = IRQF_TRIGGER_LOW; + + if (dev_fwnode(&client->dev)) + irqflags = 0; + + ret = devm_request_threaded_irq(dev, client->irq, NULL, + abeoz9_rtc_irq, + irqflags | IRQF_ONESHOT, + dev_name(dev), dev); + if (ret) { + dev_err(dev, "failed to request alarm irq\n"); + return ret; + } + } else { + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, data->rtc->features); + } + + if (client->irq > 0 || device_property_read_bool(dev, "wakeup-source")) { + ret = device_init_wakeup(dev, true); + set_bit(RTC_FEATURE_ALARM, data->rtc->features); + } + + ret = devm_rtc_register_device(data->rtc); + if (ret) + return ret; + + abeoz9_hwmon_register(dev, data); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id abeoz9_dt_match[] = { + { .compatible = "abracon,abeoz9" }, + { }, +}; +MODULE_DEVICE_TABLE(of, abeoz9_dt_match); +#endif + +static const struct i2c_device_id abeoz9_id[] = { + { .name = "abeoz9" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, abeoz9_id); + +static struct i2c_driver abeoz9_driver = { + .driver = { + .name = "rtc-ab-eoz9", + .of_match_table = of_match_ptr(abeoz9_dt_match), + }, + .probe = abeoz9_probe, + .id_table = abeoz9_id, +}; + +module_i2c_driver(abeoz9_driver); + +MODULE_AUTHOR("Artem Panfilov "); +MODULE_DESCRIPTION("Abracon AB-RTCMC-32.768kHz-EOZ9 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-ab8500.c b/drivers/rtc/rtc-ab8500.c new file mode 100644 index 000000000..0978bd0a3 --- /dev/null +++ b/drivers/rtc/rtc-ab8500.c @@ -0,0 +1,405 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Virupax Sadashivpetimath + * + * RTC clock driver for the RTC part of the AB8500 Power management chip. + * Based on RTC clock driver for the AB3100 Analog Baseband Chip by + * Linus Walleij + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AB8500_RTC_SOFF_STAT_REG 0x00 +#define AB8500_RTC_CC_CONF_REG 0x01 +#define AB8500_RTC_READ_REQ_REG 0x02 +#define AB8500_RTC_WATCH_TSECMID_REG 0x03 +#define AB8500_RTC_WATCH_TSECHI_REG 0x04 +#define AB8500_RTC_WATCH_TMIN_LOW_REG 0x05 +#define AB8500_RTC_WATCH_TMIN_MID_REG 0x06 +#define AB8500_RTC_WATCH_TMIN_HI_REG 0x07 +#define AB8500_RTC_ALRM_MIN_LOW_REG 0x08 +#define AB8500_RTC_ALRM_MIN_MID_REG 0x09 +#define AB8500_RTC_ALRM_MIN_HI_REG 0x0A +#define AB8500_RTC_STAT_REG 0x0B +#define AB8500_RTC_BKUP_CHG_REG 0x0C +#define AB8500_RTC_FORCE_BKUP_REG 0x0D +#define AB8500_RTC_CALIB_REG 0x0E +#define AB8500_RTC_SWITCH_STAT_REG 0x0F + +/* RtcReadRequest bits */ +#define RTC_READ_REQUEST 0x01 +#define RTC_WRITE_REQUEST 0x02 + +/* RtcCtrl bits */ +#define RTC_ALARM_ENA 0x04 +#define RTC_STATUS_DATA 0x01 + +#define COUNTS_PER_SEC (0xF000 / 60) + +static const u8 ab8500_rtc_time_regs[] = { + AB8500_RTC_WATCH_TMIN_HI_REG, AB8500_RTC_WATCH_TMIN_MID_REG, + AB8500_RTC_WATCH_TMIN_LOW_REG, AB8500_RTC_WATCH_TSECHI_REG, + AB8500_RTC_WATCH_TSECMID_REG +}; + +static const u8 ab8500_rtc_alarm_regs[] = { + AB8500_RTC_ALRM_MIN_HI_REG, AB8500_RTC_ALRM_MIN_MID_REG, + AB8500_RTC_ALRM_MIN_LOW_REG +}; + +static int ab8500_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + unsigned long timeout = jiffies + HZ; + int retval, i; + unsigned long mins, secs; + unsigned char buf[ARRAY_SIZE(ab8500_rtc_time_regs)]; + u8 value; + + /* Request a data read */ + retval = abx500_set_register_interruptible(dev, + AB8500_RTC, AB8500_RTC_READ_REQ_REG, RTC_READ_REQUEST); + if (retval < 0) + return retval; + + /* Wait for some cycles after enabling the rtc read in ab8500 */ + while (time_before(jiffies, timeout)) { + retval = abx500_get_register_interruptible(dev, + AB8500_RTC, AB8500_RTC_READ_REQ_REG, &value); + if (retval < 0) + return retval; + + if (!(value & RTC_READ_REQUEST)) + break; + + usleep_range(1000, 5000); + } + + /* Read the Watchtime registers */ + for (i = 0; i < ARRAY_SIZE(ab8500_rtc_time_regs); i++) { + retval = abx500_get_register_interruptible(dev, + AB8500_RTC, ab8500_rtc_time_regs[i], &value); + if (retval < 0) + return retval; + buf[i] = value; + } + + mins = (buf[0] << 16) | (buf[1] << 8) | buf[2]; + + secs = (buf[3] << 8) | buf[4]; + secs = secs / COUNTS_PER_SEC; + secs = secs + (mins * 60); + + rtc_time64_to_tm(secs, tm); + return 0; +} + +static int ab8500_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + int retval, i; + unsigned char buf[ARRAY_SIZE(ab8500_rtc_time_regs)]; + unsigned long no_secs, no_mins, secs = 0; + + secs = rtc_tm_to_time64(tm); + + no_mins = secs / 60; + + no_secs = secs % 60; + /* Make the seconds count as per the RTC resolution */ + no_secs = no_secs * COUNTS_PER_SEC; + + buf[4] = no_secs & 0xFF; + buf[3] = (no_secs >> 8) & 0xFF; + + buf[2] = no_mins & 0xFF; + buf[1] = (no_mins >> 8) & 0xFF; + buf[0] = (no_mins >> 16) & 0xFF; + + for (i = 0; i < ARRAY_SIZE(ab8500_rtc_time_regs); i++) { + retval = abx500_set_register_interruptible(dev, AB8500_RTC, + ab8500_rtc_time_regs[i], buf[i]); + if (retval < 0) + return retval; + } + + /* Request a data write */ + return abx500_set_register_interruptible(dev, AB8500_RTC, + AB8500_RTC_READ_REQ_REG, RTC_WRITE_REQUEST); +} + +static int ab8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + int retval, i; + u8 rtc_ctrl, value; + unsigned char buf[ARRAY_SIZE(ab8500_rtc_alarm_regs)]; + unsigned long secs, mins; + + /* Check if the alarm is enabled or not */ + retval = abx500_get_register_interruptible(dev, AB8500_RTC, + AB8500_RTC_STAT_REG, &rtc_ctrl); + if (retval < 0) + return retval; + + if (rtc_ctrl & RTC_ALARM_ENA) + alarm->enabled = 1; + else + alarm->enabled = 0; + + alarm->pending = 0; + + for (i = 0; i < ARRAY_SIZE(ab8500_rtc_alarm_regs); i++) { + retval = abx500_get_register_interruptible(dev, AB8500_RTC, + ab8500_rtc_alarm_regs[i], &value); + if (retval < 0) + return retval; + buf[i] = value; + } + + mins = (buf[0] << 16) | (buf[1] << 8) | (buf[2]); + secs = mins * 60; + + rtc_time64_to_tm(secs, &alarm->time); + + return 0; +} + +static int ab8500_rtc_irq_enable(struct device *dev, unsigned int enabled) +{ + return abx500_mask_and_set_register_interruptible(dev, AB8500_RTC, + AB8500_RTC_STAT_REG, RTC_ALARM_ENA, + enabled ? RTC_ALARM_ENA : 0); +} + +static int ab8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + int retval, i; + unsigned char buf[ARRAY_SIZE(ab8500_rtc_alarm_regs)]; + unsigned long mins; + + mins = (unsigned long)rtc_tm_to_time64(&alarm->time) / 60; + + buf[2] = mins & 0xFF; + buf[1] = (mins >> 8) & 0xFF; + buf[0] = (mins >> 16) & 0xFF; + + /* Set the alarm time */ + for (i = 0; i < ARRAY_SIZE(ab8500_rtc_alarm_regs); i++) { + retval = abx500_set_register_interruptible(dev, AB8500_RTC, + ab8500_rtc_alarm_regs[i], buf[i]); + if (retval < 0) + return retval; + } + + return ab8500_rtc_irq_enable(dev, alarm->enabled); +} + +static int ab8500_rtc_set_calibration(struct device *dev, int calibration) +{ + int retval; + u8 rtccal = 0; + + /* + * Check that the calibration value (which is in units of 0.5 + * parts-per-million) is in the AB8500's range for RtcCalibration + * register. -128 (0x80) is not permitted because the AB8500 uses + * a sign-bit rather than two's complement, so 0x80 is just another + * representation of zero. + */ + if ((calibration < -127) || (calibration > 127)) { + dev_err(dev, "RtcCalibration value outside permitted range\n"); + return -EINVAL; + } + + /* + * The AB8500 uses sign (in bit7) and magnitude (in bits0-7) + * so need to convert to this sort of representation before writing + * into RtcCalibration register... + */ + if (calibration >= 0) + rtccal = 0x7F & calibration; + else + rtccal = ~(calibration - 1) | 0x80; + + retval = abx500_set_register_interruptible(dev, AB8500_RTC, + AB8500_RTC_CALIB_REG, rtccal); + + return retval; +} + +static int ab8500_rtc_get_calibration(struct device *dev, int *calibration) +{ + int retval; + u8 rtccal = 0; + + retval = abx500_get_register_interruptible(dev, AB8500_RTC, + AB8500_RTC_CALIB_REG, &rtccal); + if (retval >= 0) { + /* + * The AB8500 uses sign (in bit7) and magnitude (in bits0-7) + * so need to convert value from RtcCalibration register into + * a two's complement signed value... + */ + if (rtccal & 0x80) + *calibration = 0 - (rtccal & 0x7F); + else + *calibration = 0x7F & rtccal; + } + + return retval; +} + +static ssize_t ab8500_sysfs_store_rtc_calibration(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int retval; + int calibration = 0; + + if (sscanf(buf, " %i ", &calibration) != 1) { + dev_err(dev, "Failed to store RTC calibration attribute\n"); + return -EINVAL; + } + + retval = ab8500_rtc_set_calibration(dev, calibration); + + return retval ? retval : count; +} + +static ssize_t ab8500_sysfs_show_rtc_calibration(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval = 0; + int calibration = 0; + + retval = ab8500_rtc_get_calibration(dev, &calibration); + if (retval < 0) { + dev_err(dev, "Failed to read RTC calibration attribute\n"); + return retval; + } + + return sysfs_emit(buf, "%d\n", calibration); +} + +static DEVICE_ATTR(rtc_calibration, S_IRUGO | S_IWUSR, + ab8500_sysfs_show_rtc_calibration, + ab8500_sysfs_store_rtc_calibration); + +static struct attribute *ab8500_rtc_attrs[] = { + &dev_attr_rtc_calibration.attr, + NULL +}; + +static const struct attribute_group ab8500_rtc_sysfs_files = { + .attrs = ab8500_rtc_attrs, +}; + +static irqreturn_t rtc_alarm_handler(int irq, void *data) +{ + struct rtc_device *rtc = data; + unsigned long events = RTC_IRQF | RTC_AF; + + dev_dbg(&rtc->dev, "%s\n", __func__); + rtc_update_irq(rtc, 1, events); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops ab8500_rtc_ops = { + .read_time = ab8500_rtc_read_time, + .set_time = ab8500_rtc_set_time, + .read_alarm = ab8500_rtc_read_alarm, + .set_alarm = ab8500_rtc_set_alarm, + .alarm_irq_enable = ab8500_rtc_irq_enable, +}; + +static const struct platform_device_id ab85xx_rtc_ids[] = { + { .name = "ab8500-rtc" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, ab85xx_rtc_ids); + +static int ab8500_rtc_probe(struct platform_device *pdev) +{ + int err; + struct rtc_device *rtc; + u8 rtc_ctrl; + int irq; + + irq = platform_get_irq_byname(pdev, "ALARM"); + if (irq < 0) + return irq; + + /* For RTC supply test */ + err = abx500_mask_and_set_register_interruptible(&pdev->dev, AB8500_RTC, + AB8500_RTC_STAT_REG, RTC_STATUS_DATA, RTC_STATUS_DATA); + if (err < 0) + return err; + + /* Wait for reset by the PorRtc */ + usleep_range(1000, 5000); + + err = abx500_get_register_interruptible(&pdev->dev, AB8500_RTC, + AB8500_RTC_STAT_REG, &rtc_ctrl); + if (err < 0) + return err; + + /* Check if the RTC Supply fails */ + if (!(rtc_ctrl & RTC_STATUS_DATA)) { + dev_err(&pdev->dev, "RTC supply failure\n"); + return -ENODEV; + } + + devm_device_init_wakeup(&pdev->dev); + + rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + rtc->ops = &ab8500_rtc_ops; + + err = devm_request_threaded_irq(&pdev->dev, irq, NULL, + rtc_alarm_handler, IRQF_ONESHOT, + "ab8500-rtc", rtc); + if (err < 0) + return err; + + devm_pm_set_wake_irq(&pdev->dev, irq); + platform_set_drvdata(pdev, rtc); + + set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->features); + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features); + + rtc->range_max = (1ULL << 24) * 60 - 1; // 24-bit minutes + 59 secs + rtc->start_secs = RTC_TIMESTAMP_BEGIN_2000; + rtc->set_start_time = true; + + err = rtc_add_group(rtc, &ab8500_rtc_sysfs_files); + if (err) + return err; + + return devm_rtc_register_device(rtc); +} + +static struct platform_driver ab8500_rtc_driver = { + .driver = { + .name = "ab8500-rtc", + }, + .probe = ab8500_rtc_probe, + .id_table = ab85xx_rtc_ids, +}; + +module_platform_driver(ab8500_rtc_driver); + +MODULE_AUTHOR("Virupax Sadashivpetimath "); +MODULE_DESCRIPTION("AB8500 RTC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c new file mode 100644 index 000000000..5486d9d0b --- /dev/null +++ b/drivers/rtc/rtc-abx80x.c @@ -0,0 +1,1010 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A driver for the I2C members of the Abracon AB x8xx RTC family, + * and compatible: AB 1805 and AB 0805 + * + * Copyright 2014-2015 Macq S.A. + * + * Author: Philippe De Muyter + * Author: Alexandre Belloni + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define ABX8XX_REG_HTH 0x00 +#define ABX8XX_REG_SC 0x01 +#define ABX8XX_REG_MN 0x02 +#define ABX8XX_REG_HR 0x03 +#define ABX8XX_REG_DA 0x04 +#define ABX8XX_REG_MO 0x05 +#define ABX8XX_REG_YR 0x06 +#define ABX8XX_REG_WD 0x07 + +#define ABX8XX_REG_AHTH 0x08 +#define ABX8XX_REG_ASC 0x09 +#define ABX8XX_REG_AMN 0x0a +#define ABX8XX_REG_AHR 0x0b +#define ABX8XX_REG_ADA 0x0c +#define ABX8XX_REG_AMO 0x0d +#define ABX8XX_REG_AWD 0x0e + +#define ABX8XX_REG_STATUS 0x0f +#define ABX8XX_STATUS_AF BIT(2) +#define ABX8XX_STATUS_BLF BIT(4) +#define ABX8XX_STATUS_WDT BIT(5) + +#define ABX8XX_REG_CTRL1 0x10 +#define ABX8XX_CTRL_WRITE BIT(0) +#define ABX8XX_CTRL_ARST BIT(2) +#define ABX8XX_CTRL_12_24 BIT(6) + +#define ABX8XX_REG_CTRL2 0x11 +#define ABX8XX_CTRL2_RSVD BIT(5) + +#define ABX8XX_REG_IRQ 0x12 +#define ABX8XX_IRQ_AIE BIT(2) +#define ABX8XX_IRQ_IM_1_4 (0x3 << 5) + +#define ABX8XX_REG_CD_TIMER_CTL 0x18 + +#define ABX8XX_REG_OSC 0x1c +#define ABX8XX_OSC_FOS BIT(3) +#define ABX8XX_OSC_BOS BIT(4) +#define ABX8XX_OSC_ACAL_512 BIT(5) +#define ABX8XX_OSC_ACAL_1024 BIT(6) + +#define ABX8XX_OSC_OSEL BIT(7) + +#define ABX8XX_REG_OSS 0x1d +#define ABX8XX_OSS_OF BIT(1) +#define ABX8XX_OSS_OMODE BIT(4) + +#define ABX8XX_REG_WDT 0x1b +#define ABX8XX_WDT_WDS BIT(7) +#define ABX8XX_WDT_BMB_MASK 0x7c +#define ABX8XX_WDT_BMB_SHIFT 2 +#define ABX8XX_WDT_MAX_TIME (ABX8XX_WDT_BMB_MASK >> ABX8XX_WDT_BMB_SHIFT) +#define ABX8XX_WDT_WRB_MASK 0x03 +#define ABX8XX_WDT_WRB_1HZ 0x02 + +#define ABX8XX_REG_CFG_KEY 0x1f +#define ABX8XX_CFG_KEY_OSC 0xa1 +#define ABX8XX_CFG_KEY_MISC 0x9d + +#define ABX8XX_REG_ID0 0x28 + +#define ABX8XX_REG_OUT_CTRL 0x30 +#define ABX8XX_OUT_CTRL_EXDS BIT(4) + +#define ABX8XX_REG_TRICKLE 0x20 +#define ABX8XX_TRICKLE_CHARGE_ENABLE 0xa0 +#define ABX8XX_TRICKLE_STANDARD_DIODE 0x8 +#define ABX8XX_TRICKLE_SCHOTTKY_DIODE 0x4 + +#define ABX8XX_REG_EXTRAM 0x3f +#define ABX8XX_EXTRAM_XADS GENMASK(1, 0) + +#define ABX8XX_SRAM_BASE 0x40 +#define ABX8XX_SRAM_WIN_SIZE 0x40 +#define ABX8XX_RAM_SIZE 256 + +#define NVMEM_ADDR_LOWER GENMASK(5, 0) +#define NVMEM_ADDR_UPPER GENMASK(7, 6) + +static u8 trickle_resistors[] = {0, 3, 6, 11}; + +enum abx80x_chip {AB0801, AB0803, AB0804, AB0805, + AB1801, AB1803, AB1804, AB1805, RV1805, ABX80X}; + +struct abx80x_cap { + u16 pn; + bool has_tc; + bool has_wdog; +}; + +static struct abx80x_cap abx80x_caps[] = { + [AB0801] = {.pn = 0x0801}, + [AB0803] = {.pn = 0x0803}, + [AB0804] = {.pn = 0x0804, .has_tc = true, .has_wdog = true}, + [AB0805] = {.pn = 0x0805, .has_tc = true, .has_wdog = true}, + [AB1801] = {.pn = 0x1801}, + [AB1803] = {.pn = 0x1803}, + [AB1804] = {.pn = 0x1804, .has_tc = true, .has_wdog = true}, + [AB1805] = {.pn = 0x1805, .has_tc = true, .has_wdog = true}, + [RV1805] = {.pn = 0x1805, .has_tc = true, .has_wdog = true}, + [ABX80X] = {.pn = 0} +}; + +struct abx80x_priv { + struct rtc_device *rtc; + struct i2c_client *client; + struct watchdog_device wdog; +}; + +static int abx80x_write_config_key(struct i2c_client *client, u8 key) +{ + if (i2c_smbus_write_byte_data(client, ABX8XX_REG_CFG_KEY, key) < 0) { + dev_err(&client->dev, "Unable to write configuration key\n"); + return -EIO; + } + + return 0; +} + +static int abx80x_is_rc_mode(struct i2c_client *client) +{ + int flags = 0; + + flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSS); + if (flags < 0) { + dev_err(&client->dev, + "Failed to read autocalibration attribute\n"); + return flags; + } + + return (flags & ABX8XX_OSS_OMODE) ? 1 : 0; +} + +static int abx80x_enable_trickle_charger(struct i2c_client *client, + u8 trickle_cfg) +{ + int err; + + /* + * Write the configuration key register to enable access to the Trickle + * register + */ + if (abx80x_write_config_key(client, ABX8XX_CFG_KEY_MISC) < 0) + return -EIO; + + err = i2c_smbus_write_byte_data(client, ABX8XX_REG_TRICKLE, + ABX8XX_TRICKLE_CHARGE_ENABLE | + trickle_cfg); + if (err < 0) { + dev_err(&client->dev, "Unable to write trickle register\n"); + return -EIO; + } + + return 0; +} + +static int abx80x_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned char buf[8]; + int err, flags, rc_mode = 0; + + /* Read the Oscillator Failure only in XT mode */ + rc_mode = abx80x_is_rc_mode(client); + if (rc_mode < 0) + return rc_mode; + + if (!rc_mode) { + flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSS); + if (flags < 0) + return flags; + + if (flags & ABX8XX_OSS_OF) { + dev_err(dev, "Oscillator failure, data is invalid.\n"); + return -EINVAL; + } + } + + err = i2c_smbus_read_i2c_block_data(client, ABX8XX_REG_HTH, + sizeof(buf), buf); + if (err < 0) { + dev_err(&client->dev, "Unable to read date\n"); + return -EIO; + } + + tm->tm_sec = bcd2bin(buf[ABX8XX_REG_SC] & 0x7F); + tm->tm_min = bcd2bin(buf[ABX8XX_REG_MN] & 0x7F); + tm->tm_hour = bcd2bin(buf[ABX8XX_REG_HR] & 0x3F); + tm->tm_wday = buf[ABX8XX_REG_WD] & 0x7; + tm->tm_mday = bcd2bin(buf[ABX8XX_REG_DA] & 0x3F); + tm->tm_mon = bcd2bin(buf[ABX8XX_REG_MO] & 0x1F) - 1; + tm->tm_year = bcd2bin(buf[ABX8XX_REG_YR]) + 100; + + return 0; +} + +static int abx80x_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned char buf[8]; + int err, flags; + + if (tm->tm_year < 100) + return -EINVAL; + + buf[ABX8XX_REG_HTH] = 0; + buf[ABX8XX_REG_SC] = bin2bcd(tm->tm_sec); + buf[ABX8XX_REG_MN] = bin2bcd(tm->tm_min); + buf[ABX8XX_REG_HR] = bin2bcd(tm->tm_hour); + buf[ABX8XX_REG_DA] = bin2bcd(tm->tm_mday); + buf[ABX8XX_REG_MO] = bin2bcd(tm->tm_mon + 1); + buf[ABX8XX_REG_YR] = bin2bcd(tm->tm_year - 100); + buf[ABX8XX_REG_WD] = tm->tm_wday; + + err = i2c_smbus_write_i2c_block_data(client, ABX8XX_REG_HTH, + sizeof(buf), buf); + if (err < 0) { + dev_err(&client->dev, "Unable to write to date registers\n"); + return -EIO; + } + + /* Clear the OF bit of Oscillator Status Register */ + flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSS); + if (flags < 0) + return flags; + + err = i2c_smbus_write_byte_data(client, ABX8XX_REG_OSS, + flags & ~ABX8XX_OSS_OF); + if (err < 0) { + dev_err(&client->dev, "Unable to write oscillator status register\n"); + return err; + } + + return 0; +} + +static irqreturn_t abx80x_handle_irq(int irq, void *dev_id) +{ + struct i2c_client *client = dev_id; + struct abx80x_priv *priv = i2c_get_clientdata(client); + struct rtc_device *rtc = priv->rtc; + int status; + + status = i2c_smbus_read_byte_data(client, ABX8XX_REG_STATUS); + if (status < 0) + return IRQ_NONE; + + if (status & ABX8XX_STATUS_AF) + rtc_update_irq(rtc, 1, RTC_AF | RTC_IRQF); + + /* + * It is unclear if we'll get an interrupt before the external + * reset kicks in. + */ + if (status & ABX8XX_STATUS_WDT) + dev_alert(&client->dev, "watchdog timeout interrupt.\n"); + + i2c_smbus_write_byte_data(client, ABX8XX_REG_STATUS, 0); + + return IRQ_HANDLED; +} + +static int abx80x_read_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned char buf[7]; + + int irq_mask, err; + + if (client->irq <= 0) + return -EINVAL; + + err = i2c_smbus_read_i2c_block_data(client, ABX8XX_REG_ASC, + sizeof(buf), buf); + if (err) + return err; + + irq_mask = i2c_smbus_read_byte_data(client, ABX8XX_REG_IRQ); + if (irq_mask < 0) + return irq_mask; + + t->time.tm_sec = bcd2bin(buf[0] & 0x7F); + t->time.tm_min = bcd2bin(buf[1] & 0x7F); + t->time.tm_hour = bcd2bin(buf[2] & 0x3F); + t->time.tm_mday = bcd2bin(buf[3] & 0x3F); + t->time.tm_mon = bcd2bin(buf[4] & 0x1F) - 1; + t->time.tm_wday = buf[5] & 0x7; + + t->enabled = !!(irq_mask & ABX8XX_IRQ_AIE); + t->pending = (buf[6] & ABX8XX_STATUS_AF) && t->enabled; + + return err; +} + +static int abx80x_set_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 alarm[6]; + int err; + + if (client->irq <= 0) + return -EINVAL; + + alarm[0] = 0x0; + alarm[1] = bin2bcd(t->time.tm_sec); + alarm[2] = bin2bcd(t->time.tm_min); + alarm[3] = bin2bcd(t->time.tm_hour); + alarm[4] = bin2bcd(t->time.tm_mday); + alarm[5] = bin2bcd(t->time.tm_mon + 1); + + err = i2c_smbus_write_i2c_block_data(client, ABX8XX_REG_AHTH, + sizeof(alarm), alarm); + if (err < 0) { + dev_err(&client->dev, "Unable to write alarm registers\n"); + return -EIO; + } + + if (t->enabled) { + err = i2c_smbus_write_byte_data(client, ABX8XX_REG_IRQ, + (ABX8XX_IRQ_IM_1_4 | + ABX8XX_IRQ_AIE)); + if (err) + return err; + } + + return 0; +} + +static int abx80x_rtc_set_autocalibration(struct device *dev, + int autocalibration) +{ + struct i2c_client *client = to_i2c_client(dev); + int retval, flags = 0; + + if ((autocalibration != 0) && (autocalibration != 1024) && + (autocalibration != 512)) { + dev_err(dev, "autocalibration value outside permitted range\n"); + return -EINVAL; + } + + flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSC); + if (flags < 0) + return flags; + + if (autocalibration == 0) { + flags &= ~(ABX8XX_OSC_ACAL_512 | ABX8XX_OSC_ACAL_1024); + } else if (autocalibration == 1024) { + /* 1024 autocalibration is 0x10 */ + flags |= ABX8XX_OSC_ACAL_1024; + flags &= ~(ABX8XX_OSC_ACAL_512); + } else { + /* 512 autocalibration is 0x11 */ + flags |= (ABX8XX_OSC_ACAL_1024 | ABX8XX_OSC_ACAL_512); + } + + /* Unlock write access to Oscillator Control Register */ + if (abx80x_write_config_key(client, ABX8XX_CFG_KEY_OSC) < 0) + return -EIO; + + retval = i2c_smbus_write_byte_data(client, ABX8XX_REG_OSC, flags); + + return retval; +} + +static int abx80x_rtc_get_autocalibration(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + int flags = 0, autocalibration; + + flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSC); + if (flags < 0) + return flags; + + if (flags & ABX8XX_OSC_ACAL_512) + autocalibration = 512; + else if (flags & ABX8XX_OSC_ACAL_1024) + autocalibration = 1024; + else + autocalibration = 0; + + return autocalibration; +} + +static ssize_t autocalibration_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int retval; + unsigned long autocalibration = 0; + + retval = kstrtoul(buf, 10, &autocalibration); + if (retval < 0) { + dev_err(dev, "Failed to store RTC autocalibration attribute\n"); + return -EINVAL; + } + + retval = abx80x_rtc_set_autocalibration(dev->parent, autocalibration); + + return retval ? retval : count; +} + +static ssize_t autocalibration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int autocalibration = 0; + + autocalibration = abx80x_rtc_get_autocalibration(dev->parent); + if (autocalibration < 0) { + dev_err(dev, "Failed to read RTC autocalibration\n"); + sprintf(buf, "0\n"); + return autocalibration; + } + + return sprintf(buf, "%d\n", autocalibration); +} + +static DEVICE_ATTR_RW(autocalibration); + +static ssize_t oscillator_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev->parent); + int retval, flags, rc_mode = 0; + + if (strncmp(buf, "rc", 2) == 0) { + rc_mode = 1; + } else if (strncmp(buf, "xtal", 4) == 0) { + rc_mode = 0; + } else { + dev_err(dev, "Oscillator selection value outside permitted ones\n"); + return -EINVAL; + } + + flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSC); + if (flags < 0) + return flags; + + if (rc_mode == 0) + flags &= ~(ABX8XX_OSC_OSEL); + else + flags |= (ABX8XX_OSC_OSEL); + + /* Unlock write access on Oscillator Control register */ + if (abx80x_write_config_key(client, ABX8XX_CFG_KEY_OSC) < 0) + return -EIO; + + retval = i2c_smbus_write_byte_data(client, ABX8XX_REG_OSC, flags); + if (retval < 0) { + dev_err(dev, "Failed to write Oscillator Control register\n"); + return retval; + } + + return retval ? retval : count; +} + +static ssize_t oscillator_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int rc_mode = 0; + struct i2c_client *client = to_i2c_client(dev->parent); + + rc_mode = abx80x_is_rc_mode(client); + + if (rc_mode < 0) { + dev_err(dev, "Failed to read RTC oscillator selection\n"); + sprintf(buf, "\n"); + return rc_mode; + } + + if (rc_mode) + return sprintf(buf, "rc\n"); + else + return sprintf(buf, "xtal\n"); +} + +static DEVICE_ATTR_RW(oscillator); + +static struct attribute *rtc_calib_attrs[] = { + &dev_attr_autocalibration.attr, + &dev_attr_oscillator.attr, + NULL, +}; + +static const struct attribute_group rtc_calib_attr_group = { + .attrs = rtc_calib_attrs, +}; + +static int abx80x_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct i2c_client *client = to_i2c_client(dev); + int err; + + if (enabled) + err = i2c_smbus_write_byte_data(client, ABX8XX_REG_IRQ, + (ABX8XX_IRQ_IM_1_4 | + ABX8XX_IRQ_AIE)); + else + err = i2c_smbus_write_byte_data(client, ABX8XX_REG_IRQ, + ABX8XX_IRQ_IM_1_4); + return err; +} + +static int abx80x_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct i2c_client *client = to_i2c_client(dev); + int status, tmp; + + switch (cmd) { + case RTC_VL_READ: + status = i2c_smbus_read_byte_data(client, ABX8XX_REG_STATUS); + if (status < 0) + return status; + + tmp = status & ABX8XX_STATUS_BLF ? RTC_VL_BACKUP_LOW : 0; + + return put_user(tmp, (unsigned int __user *)arg); + + case RTC_VL_CLR: + status = i2c_smbus_read_byte_data(client, ABX8XX_REG_STATUS); + if (status < 0) + return status; + + status &= ~ABX8XX_STATUS_BLF; + + tmp = i2c_smbus_write_byte_data(client, ABX8XX_REG_STATUS, + status); + if (tmp < 0) + return tmp; + + return 0; + + default: + return -ENOIOCTLCMD; + } +} + +static const struct rtc_class_ops abx80x_rtc_ops = { + .read_time = abx80x_rtc_read_time, + .set_time = abx80x_rtc_set_time, + .read_alarm = abx80x_read_alarm, + .set_alarm = abx80x_set_alarm, + .alarm_irq_enable = abx80x_alarm_irq_enable, + .ioctl = abx80x_ioctl, +}; + +static int abx80x_dt_trickle_cfg(struct i2c_client *client) +{ + struct device_node *np = client->dev.of_node; + const char *diode; + int trickle_cfg = 0; + int i, ret; + u32 tmp; + + ret = of_property_read_string(np, "abracon,tc-diode", &diode); + if (ret) + return ret; + + if (!strcmp(diode, "standard")) { + trickle_cfg |= ABX8XX_TRICKLE_STANDARD_DIODE; + } else if (!strcmp(diode, "schottky")) { + trickle_cfg |= ABX8XX_TRICKLE_SCHOTTKY_DIODE; + } else { + dev_dbg(&client->dev, "Invalid tc-diode value: %s\n", diode); + return -EINVAL; + } + + ret = of_property_read_u32(np, "abracon,tc-resistor", &tmp); + if (ret) + return ret; + + for (i = 0; i < sizeof(trickle_resistors); i++) + if (trickle_resistors[i] == tmp) + break; + + if (i == sizeof(trickle_resistors)) { + dev_dbg(&client->dev, "Invalid tc-resistor value: %u\n", tmp); + return -EINVAL; + } + + return (trickle_cfg | i); +} + +#ifdef CONFIG_WATCHDOG + +static inline u8 timeout_bits(unsigned int timeout) +{ + return ((timeout << ABX8XX_WDT_BMB_SHIFT) & ABX8XX_WDT_BMB_MASK) | + ABX8XX_WDT_WRB_1HZ; +} + +static int __abx80x_wdog_set_timeout(struct watchdog_device *wdog, + unsigned int timeout) +{ + struct abx80x_priv *priv = watchdog_get_drvdata(wdog); + u8 val = ABX8XX_WDT_WDS | timeout_bits(timeout); + + /* + * Writing any timeout to the WDT register resets the watchdog timer. + * Writing 0 disables it. + */ + return i2c_smbus_write_byte_data(priv->client, ABX8XX_REG_WDT, val); +} + +static int abx80x_wdog_set_timeout(struct watchdog_device *wdog, + unsigned int new_timeout) +{ + int err = 0; + + if (watchdog_hw_running(wdog)) + err = __abx80x_wdog_set_timeout(wdog, new_timeout); + + if (err == 0) + wdog->timeout = new_timeout; + + return err; +} + +static int abx80x_wdog_ping(struct watchdog_device *wdog) +{ + return __abx80x_wdog_set_timeout(wdog, wdog->timeout); +} + +static int abx80x_wdog_start(struct watchdog_device *wdog) +{ + return __abx80x_wdog_set_timeout(wdog, wdog->timeout); +} + +static int abx80x_wdog_stop(struct watchdog_device *wdog) +{ + return __abx80x_wdog_set_timeout(wdog, 0); +} + +static const struct watchdog_info abx80x_wdog_info = { + .identity = "abx80x watchdog", + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, +}; + +static const struct watchdog_ops abx80x_wdog_ops = { + .owner = THIS_MODULE, + .start = abx80x_wdog_start, + .stop = abx80x_wdog_stop, + .ping = abx80x_wdog_ping, + .set_timeout = abx80x_wdog_set_timeout, +}; + +static int abx80x_setup_watchdog(struct abx80x_priv *priv) +{ + priv->wdog.parent = &priv->client->dev; + priv->wdog.ops = &abx80x_wdog_ops; + priv->wdog.info = &abx80x_wdog_info; + priv->wdog.min_timeout = 1; + priv->wdog.max_timeout = ABX8XX_WDT_MAX_TIME; + priv->wdog.timeout = ABX8XX_WDT_MAX_TIME; + + watchdog_set_drvdata(&priv->wdog, priv); + + return devm_watchdog_register_device(&priv->client->dev, &priv->wdog); +} +#else +static int abx80x_setup_watchdog(struct abx80x_priv *priv) +{ + return 0; +} +#endif + +static int abx80x_nvmem_xfer(struct abx80x_priv *priv, unsigned int offset, + void *val, size_t bytes, bool write) +{ + int ret; + + while (bytes) { + u8 extram, reg, len, lower, upper; + + lower = FIELD_GET(NVMEM_ADDR_LOWER, offset); + upper = FIELD_GET(NVMEM_ADDR_UPPER, offset); + extram = FIELD_PREP(ABX8XX_EXTRAM_XADS, upper); + reg = ABX8XX_SRAM_BASE + lower; + len = min(lower + bytes, (size_t)ABX8XX_SRAM_WIN_SIZE) - lower; + len = min_t(u8, len, I2C_SMBUS_BLOCK_MAX); + + ret = i2c_smbus_write_byte_data(priv->client, ABX8XX_REG_EXTRAM, + extram); + if (ret) + return ret; + + if (write) { + ret = i2c_smbus_write_i2c_block_data(priv->client, reg, + len, val); + if (ret) + return ret; + } else { + ret = i2c_smbus_read_i2c_block_data(priv->client, reg, + len, val); + if (ret <= 0) + return ret ? ret : -EIO; + len = ret; + } + + offset += len; + val += len; + bytes -= len; + } + + return 0; +} + +static int abx80x_nvmem_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + return abx80x_nvmem_xfer(priv, offset, val, bytes, false); +} + +static int abx80x_nvmem_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + return abx80x_nvmem_xfer(priv, offset, val, bytes, true); +} + +static int abx80x_setup_nvmem(struct abx80x_priv *priv) +{ + struct nvmem_config config = { + .type = NVMEM_TYPE_BATTERY_BACKED, + .reg_read = abx80x_nvmem_read, + .reg_write = abx80x_nvmem_write, + .size = ABX8XX_RAM_SIZE, + .priv = priv, + }; + + return devm_rtc_nvmem_register(priv->rtc, &config); +} + +static const struct i2c_device_id abx80x_id[] = { + { .name = "abx80x", .driver_data = ABX80X }, + { .name = "ab0801", .driver_data = AB0801 }, + { .name = "ab0803", .driver_data = AB0803 }, + { .name = "ab0804", .driver_data = AB0804 }, + { .name = "ab0805", .driver_data = AB0805 }, + { .name = "ab1801", .driver_data = AB1801 }, + { .name = "ab1803", .driver_data = AB1803 }, + { .name = "ab1804", .driver_data = AB1804 }, + { .name = "ab1805", .driver_data = AB1805 }, + { .name = "rv1805", .driver_data = RV1805 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, abx80x_id); + +static int abx80x_probe(struct i2c_client *client) +{ + struct device_node *np = client->dev.of_node; + struct abx80x_priv *priv; + int i, data, err, trickle_cfg = -EINVAL; + char buf[7]; + unsigned int part = (uintptr_t)i2c_get_match_data(client); + unsigned int partnumber; + unsigned int majrev, minrev; + unsigned int lot; + unsigned int wafer; + unsigned int uid; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + err = i2c_smbus_read_i2c_block_data(client, ABX8XX_REG_ID0, + sizeof(buf), buf); + if (err < 0) { + dev_err(&client->dev, "Unable to read partnumber\n"); + return -EIO; + } + + partnumber = (buf[0] << 8) | buf[1]; + majrev = buf[2] >> 3; + minrev = buf[2] & 0x7; + lot = ((buf[4] & 0x80) << 2) | ((buf[6] & 0x80) << 1) | buf[3]; + uid = ((buf[4] & 0x7f) << 8) | buf[5]; + wafer = (buf[6] & 0x7c) >> 2; + dev_info(&client->dev, "model %04x, revision %u.%u, lot %x, wafer %x, uid %x\n", + partnumber, majrev, minrev, lot, wafer, uid); + + data = i2c_smbus_read_byte_data(client, ABX8XX_REG_CTRL1); + if (data < 0) { + dev_err(&client->dev, "Unable to read control register\n"); + return -EIO; + } + + err = i2c_smbus_write_byte_data(client, ABX8XX_REG_CTRL1, + ((data & ~(ABX8XX_CTRL_12_24 | + ABX8XX_CTRL_ARST)) | + ABX8XX_CTRL_WRITE)); + if (err < 0) { + dev_err(&client->dev, "Unable to write control register\n"); + return -EIO; + } + + /* Configure RV1805 specifics */ + if (part == RV1805) { + /* + * Avoid accidentally entering test mode. This can happen + * on the RV1805 in case the reserved bit 5 in control2 + * register is set. RV-1805-C3 datasheet indicates that + * the bit should be cleared in section 11h - Control2. + */ + data = i2c_smbus_read_byte_data(client, ABX8XX_REG_CTRL2); + if (data < 0) { + dev_err(&client->dev, + "Unable to read control2 register\n"); + return -EIO; + } + + err = i2c_smbus_write_byte_data(client, ABX8XX_REG_CTRL2, + data & ~ABX8XX_CTRL2_RSVD); + if (err < 0) { + dev_err(&client->dev, + "Unable to write control2 register\n"); + return -EIO; + } + + /* + * Avoid extra power leakage. The RV1805 uses smaller + * 10pin package and the EXTI input is not present. + * Disable it to avoid leakage. + */ + data = i2c_smbus_read_byte_data(client, ABX8XX_REG_OUT_CTRL); + if (data < 0) { + dev_err(&client->dev, + "Unable to read output control register\n"); + return -EIO; + } + + /* + * Write the configuration key register to enable access to + * the config2 register + */ + if (abx80x_write_config_key(client, ABX8XX_CFG_KEY_MISC) < 0) + return -EIO; + + err = i2c_smbus_write_byte_data(client, ABX8XX_REG_OUT_CTRL, + data | ABX8XX_OUT_CTRL_EXDS); + if (err < 0) { + dev_err(&client->dev, + "Unable to write output control register\n"); + return -EIO; + } + } + + /* part autodetection */ + if (part == ABX80X) { + for (i = 0; abx80x_caps[i].pn; i++) + if (partnumber == abx80x_caps[i].pn) + break; + if (abx80x_caps[i].pn == 0) { + dev_err(&client->dev, "Unknown part: %04x\n", + partnumber); + return -EINVAL; + } + part = i; + } + + if (partnumber != abx80x_caps[part].pn) { + dev_err(&client->dev, "partnumber mismatch %04x != %04x\n", + partnumber, abx80x_caps[part].pn); + return -EINVAL; + } + + if (np && abx80x_caps[part].has_tc) + trickle_cfg = abx80x_dt_trickle_cfg(client); + + if (trickle_cfg > 0) { + dev_info(&client->dev, "Enabling trickle charger: %02x\n", + trickle_cfg); + abx80x_enable_trickle_charger(client, trickle_cfg); + } + + err = i2c_smbus_write_byte_data(client, ABX8XX_REG_CD_TIMER_CTL, + BIT(2)); + if (err) + return err; + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + priv->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(priv->rtc)) + return PTR_ERR(priv->rtc); + + priv->rtc->ops = &abx80x_rtc_ops; + priv->client = client; + + i2c_set_clientdata(client, priv); + + if (abx80x_caps[part].has_wdog) { + err = abx80x_setup_watchdog(priv); + if (err) + return err; + } + + err = abx80x_setup_nvmem(priv); + if (err) + return err; + + if (client->irq > 0) { + dev_info(&client->dev, "IRQ %d supplied\n", client->irq); + err = devm_request_threaded_irq(&client->dev, client->irq, NULL, + abx80x_handle_irq, + IRQF_SHARED | IRQF_ONESHOT, + "abx8xx", + client); + if (err) { + dev_err(&client->dev, "unable to request IRQ, alarms disabled\n"); + client->irq = 0; + } + } + if (client->irq <= 0) + clear_bit(RTC_FEATURE_ALARM, priv->rtc->features); + + err = rtc_add_group(priv->rtc, &rtc_calib_attr_group); + if (err) { + dev_err(&client->dev, "Failed to create sysfs group: %d\n", + err); + return err; + } + + return devm_rtc_register_device(priv->rtc); +} + +#ifdef CONFIG_OF +static const struct of_device_id abx80x_of_match[] = { + { + .compatible = "abracon,abx80x", + .data = (void *)ABX80X + }, + { + .compatible = "abracon,ab0801", + .data = (void *)AB0801 + }, + { + .compatible = "abracon,ab0803", + .data = (void *)AB0803 + }, + { + .compatible = "abracon,ab0804", + .data = (void *)AB0804 + }, + { + .compatible = "abracon,ab0805", + .data = (void *)AB0805 + }, + { + .compatible = "abracon,ab1801", + .data = (void *)AB1801 + }, + { + .compatible = "abracon,ab1803", + .data = (void *)AB1803 + }, + { + .compatible = "abracon,ab1804", + .data = (void *)AB1804 + }, + { + .compatible = "abracon,ab1805", + .data = (void *)AB1805 + }, + { + .compatible = "microcrystal,rv1805", + .data = (void *)RV1805 + }, + { } +}; +MODULE_DEVICE_TABLE(of, abx80x_of_match); +#endif + +static struct i2c_driver abx80x_driver = { + .driver = { + .name = "rtc-abx80x", + .of_match_table = of_match_ptr(abx80x_of_match), + }, + .probe = abx80x_probe, + .id_table = abx80x_id, +}; + +module_i2c_driver(abx80x_driver); + +MODULE_AUTHOR("Philippe De Muyter "); +MODULE_AUTHOR("Alexandre Belloni "); +MODULE_DESCRIPTION("Abracon ABX80X RTC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-ac100.c b/drivers/rtc/rtc-ac100.c new file mode 100644 index 000000000..bba7115ff --- /dev/null +++ b/drivers/rtc/rtc-ac100.c @@ -0,0 +1,642 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * RTC Driver for X-Powers AC100 + * + * Copyright (c) 2016 Chen-Yu Tsai + * + * Chen-Yu Tsai + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Control register */ +#define AC100_RTC_CTRL_24HOUR BIT(0) + +/* Clock output register bits */ +#define AC100_CLKOUT_PRE_DIV_SHIFT 5 +#define AC100_CLKOUT_PRE_DIV_WIDTH 3 +#define AC100_CLKOUT_MUX_SHIFT 4 +#define AC100_CLKOUT_MUX_WIDTH 1 +#define AC100_CLKOUT_DIV_SHIFT 1 +#define AC100_CLKOUT_DIV_WIDTH 3 +#define AC100_CLKOUT_EN BIT(0) + +/* RTC */ +#define AC100_RTC_SEC_MASK GENMASK(6, 0) +#define AC100_RTC_MIN_MASK GENMASK(6, 0) +#define AC100_RTC_HOU_MASK GENMASK(5, 0) +#define AC100_RTC_WEE_MASK GENMASK(2, 0) +#define AC100_RTC_DAY_MASK GENMASK(5, 0) +#define AC100_RTC_MON_MASK GENMASK(4, 0) +#define AC100_RTC_YEA_MASK GENMASK(7, 0) +#define AC100_RTC_YEA_LEAP BIT(15) +#define AC100_RTC_UPD_TRIGGER BIT(15) + +/* Alarm (wall clock) */ +#define AC100_ALM_INT_ENABLE BIT(0) + +#define AC100_ALM_SEC_MASK GENMASK(6, 0) +#define AC100_ALM_MIN_MASK GENMASK(6, 0) +#define AC100_ALM_HOU_MASK GENMASK(5, 0) +#define AC100_ALM_WEE_MASK GENMASK(2, 0) +#define AC100_ALM_DAY_MASK GENMASK(5, 0) +#define AC100_ALM_MON_MASK GENMASK(4, 0) +#define AC100_ALM_YEA_MASK GENMASK(7, 0) +#define AC100_ALM_ENABLE_FLAG BIT(15) +#define AC100_ALM_UPD_TRIGGER BIT(15) + +/* + * The year parameter passed to the driver is usually an offset relative to + * the year 1900. This macro is used to convert this offset to another one + * relative to the minimum year allowed by the hardware. + * + * The year range is 1970 - 2069. This range is selected to match Allwinner's + * driver. + */ +#define AC100_YEAR_MIN 1970 +#define AC100_YEAR_MAX 2069 +#define AC100_YEAR_OFF (AC100_YEAR_MIN - 1900) + +struct ac100_clkout { + struct clk_hw hw; + struct regmap *regmap; + u8 offset; +}; + +#define to_ac100_clkout(_hw) container_of(_hw, struct ac100_clkout, hw) + +#define AC100_RTC_32K_NAME "ac100-rtc-32k" +#define AC100_RTC_32K_RATE 32768 +#define AC100_CLKOUT_NUM 3 + +static const char * const ac100_clkout_names[AC100_CLKOUT_NUM] = { + "ac100-cko1-rtc", + "ac100-cko2-rtc", + "ac100-cko3-rtc", +}; + +struct ac100_rtc_dev { + struct rtc_device *rtc; + struct device *dev; + struct regmap *regmap; + int irq; + unsigned long alarm; + + struct clk_hw *rtc_32k_clk; + struct ac100_clkout clks[AC100_CLKOUT_NUM]; + struct clk_hw_onecell_data *clk_data; +}; + +/* + * Clock controls for 3 clock output pins + */ + +static const struct clk_div_table ac100_clkout_prediv[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 8 }, + { .val = 4, .div = 16 }, + { .val = 5, .div = 32 }, + { .val = 6, .div = 64 }, + { .val = 7, .div = 122 }, + { }, +}; + +/* Abuse the fact that one parent is 32768 Hz, and the other is 4 MHz */ +static unsigned long ac100_clkout_recalc_rate(struct clk_hw *hw, + unsigned long prate) +{ + struct ac100_clkout *clk = to_ac100_clkout(hw); + unsigned int reg, div; + + regmap_read(clk->regmap, clk->offset, ®); + + /* Handle pre-divider first */ + if (prate != AC100_RTC_32K_RATE) { + div = (reg >> AC100_CLKOUT_PRE_DIV_SHIFT) & + ((1 << AC100_CLKOUT_PRE_DIV_WIDTH) - 1); + prate = divider_recalc_rate(hw, prate, div, + ac100_clkout_prediv, 0, + AC100_CLKOUT_PRE_DIV_WIDTH); + } + + div = (reg >> AC100_CLKOUT_DIV_SHIFT) & + (BIT(AC100_CLKOUT_DIV_WIDTH) - 1); + return divider_recalc_rate(hw, prate, div, NULL, + CLK_DIVIDER_POWER_OF_TWO, + AC100_CLKOUT_DIV_WIDTH); +} + +static int ac100_clkout_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + int i, ret, num_parents = clk_hw_get_num_parents(hw); + struct clk_hw *best_parent = NULL; + unsigned long best = 0; + + for (i = 0; i < num_parents; i++) { + struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i); + unsigned long prate; + + /* + * The clock has two parents, one is a fixed clock which is + * internally registered by the ac100 driver. The other parent + * is a clock from the codec side of the chip, which we + * properly declare and reference in the devicetree and is + * not implemented in any driver right now. + * If the clock core looks for the parent of that second + * missing clock, it can't find one that is registered and + * returns NULL. + * So we end up in a situation where clk_hw_get_num_parents + * returns the amount of clocks we can be parented to, but + * clk_hw_get_parent_by_index will not return the orphan + * clocks. + * Thus we need to check if the parent exists before + * we get the parent rate, so we could use the RTC + * without waiting for the codec to be supported. + */ + if (!parent) + continue; + + prate = clk_hw_get_rate(parent); + + if (prate == AC100_RTC_32K_RATE) { + struct clk_rate_request div_req = *req; + + div_req.best_parent_rate = prate; + + ret = divider_determine_rate(hw, &div_req, NULL, + AC100_CLKOUT_DIV_WIDTH, + CLK_DIVIDER_POWER_OF_TWO); + if (ret != 0 || div_req.rate > req->rate) { + continue; + } else if (req->rate - div_req.rate < req->rate - best) { + best = div_req.rate; + best_parent = parent; + } + } else { + int j; + + for (j = 0; ac100_clkout_prediv[j].div; j++) { + struct clk_rate_request div_req = *req; + unsigned long tmp_prate; + + tmp_prate = DIV_ROUND_UP(prate, ac100_clkout_prediv[j].div); + div_req.best_parent_rate = tmp_prate; + + ret = divider_determine_rate(hw, &div_req, NULL, + AC100_CLKOUT_DIV_WIDTH, + CLK_DIVIDER_POWER_OF_TWO); + if (ret != 0 || div_req.rate > req->rate) { + continue; + } else if (req->rate - div_req.rate < req->rate - best) { + best = div_req.rate; + best_parent = parent; + } + } + } + } + + if (!best) + return -EINVAL; + + req->best_parent_hw = best_parent; + req->best_parent_rate = clk_hw_get_rate(best_parent); + req->rate = best; + + return 0; +} + +static int ac100_clkout_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long prate) +{ + struct ac100_clkout *clk = to_ac100_clkout(hw); + int div = 0, pre_div = 0; + + do { + div = divider_get_val(rate * ac100_clkout_prediv[pre_div].div, + prate, NULL, AC100_CLKOUT_DIV_WIDTH, + CLK_DIVIDER_POWER_OF_TWO); + if (div >= 0) + break; + } while (prate != AC100_RTC_32K_RATE && + ac100_clkout_prediv[++pre_div].div); + + if (div < 0) + return div; + + pre_div = ac100_clkout_prediv[pre_div].val; + + regmap_update_bits(clk->regmap, clk->offset, + ((1 << AC100_CLKOUT_DIV_WIDTH) - 1) << AC100_CLKOUT_DIV_SHIFT | + ((1 << AC100_CLKOUT_PRE_DIV_WIDTH) - 1) << AC100_CLKOUT_PRE_DIV_SHIFT, + (div - 1) << AC100_CLKOUT_DIV_SHIFT | + (pre_div - 1) << AC100_CLKOUT_PRE_DIV_SHIFT); + + return 0; +} + +static int ac100_clkout_prepare(struct clk_hw *hw) +{ + struct ac100_clkout *clk = to_ac100_clkout(hw); + + return regmap_update_bits(clk->regmap, clk->offset, AC100_CLKOUT_EN, + AC100_CLKOUT_EN); +} + +static void ac100_clkout_unprepare(struct clk_hw *hw) +{ + struct ac100_clkout *clk = to_ac100_clkout(hw); + + regmap_update_bits(clk->regmap, clk->offset, AC100_CLKOUT_EN, 0); +} + +static int ac100_clkout_is_prepared(struct clk_hw *hw) +{ + struct ac100_clkout *clk = to_ac100_clkout(hw); + unsigned int reg; + + regmap_read(clk->regmap, clk->offset, ®); + + return reg & AC100_CLKOUT_EN; +} + +static u8 ac100_clkout_get_parent(struct clk_hw *hw) +{ + struct ac100_clkout *clk = to_ac100_clkout(hw); + unsigned int reg; + + regmap_read(clk->regmap, clk->offset, ®); + + return (reg >> AC100_CLKOUT_MUX_SHIFT) & 0x1; +} + +static int ac100_clkout_set_parent(struct clk_hw *hw, u8 index) +{ + struct ac100_clkout *clk = to_ac100_clkout(hw); + + return regmap_update_bits(clk->regmap, clk->offset, + BIT(AC100_CLKOUT_MUX_SHIFT), + index ? BIT(AC100_CLKOUT_MUX_SHIFT) : 0); +} + +static const struct clk_ops ac100_clkout_ops = { + .prepare = ac100_clkout_prepare, + .unprepare = ac100_clkout_unprepare, + .is_prepared = ac100_clkout_is_prepared, + .recalc_rate = ac100_clkout_recalc_rate, + .determine_rate = ac100_clkout_determine_rate, + .get_parent = ac100_clkout_get_parent, + .set_parent = ac100_clkout_set_parent, + .set_rate = ac100_clkout_set_rate, +}; + +static int ac100_rtc_register_clks(struct ac100_rtc_dev *chip) +{ + struct device_node *np = chip->dev->of_node; + const char *parents[2] = {AC100_RTC_32K_NAME}; + int i, ret; + + chip->clk_data = devm_kzalloc(chip->dev, + struct_size(chip->clk_data, hws, + AC100_CLKOUT_NUM), + GFP_KERNEL); + if (!chip->clk_data) + return -ENOMEM; + + chip->rtc_32k_clk = clk_hw_register_fixed_rate(chip->dev, + AC100_RTC_32K_NAME, + NULL, 0, + AC100_RTC_32K_RATE); + if (IS_ERR(chip->rtc_32k_clk)) { + ret = PTR_ERR(chip->rtc_32k_clk); + dev_err(chip->dev, "Failed to register RTC-32k clock: %d\n", + ret); + return ret; + } + + parents[1] = of_clk_get_parent_name(np, 0); + if (!parents[1]) { + dev_err(chip->dev, "Failed to get ADDA 4M clock\n"); + return -EINVAL; + } + + for (i = 0; i < AC100_CLKOUT_NUM; i++) { + struct ac100_clkout *clk = &chip->clks[i]; + struct clk_init_data init = { + .name = ac100_clkout_names[i], + .ops = &ac100_clkout_ops, + .parent_names = parents, + .num_parents = ARRAY_SIZE(parents), + .flags = 0, + }; + + of_property_read_string_index(np, "clock-output-names", + i, &init.name); + clk->regmap = chip->regmap; + clk->offset = AC100_CLKOUT_CTRL1 + i; + clk->hw.init = &init; + + ret = devm_clk_hw_register(chip->dev, &clk->hw); + if (ret) { + dev_err(chip->dev, "Failed to register clk '%s': %d\n", + init.name, ret); + goto err_unregister_rtc_32k; + } + + chip->clk_data->hws[i] = &clk->hw; + } + + chip->clk_data->num = i; + ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, chip->clk_data); + if (ret) + goto err_unregister_rtc_32k; + + return 0; + +err_unregister_rtc_32k: + clk_unregister_fixed_rate(chip->rtc_32k_clk->clk); + + return ret; +} + +static void ac100_rtc_unregister_clks(struct ac100_rtc_dev *chip) +{ + of_clk_del_provider(chip->dev->of_node); + clk_unregister_fixed_rate(chip->rtc_32k_clk->clk); +} + +/* + * RTC related bits + */ +static int ac100_rtc_get_time(struct device *dev, struct rtc_time *rtc_tm) +{ + struct ac100_rtc_dev *chip = dev_get_drvdata(dev); + struct regmap *regmap = chip->regmap; + u16 reg[7]; + int ret; + + ret = regmap_bulk_read(regmap, AC100_RTC_SEC, reg, 7); + if (ret) + return ret; + + rtc_tm->tm_sec = bcd2bin(reg[0] & AC100_RTC_SEC_MASK); + rtc_tm->tm_min = bcd2bin(reg[1] & AC100_RTC_MIN_MASK); + rtc_tm->tm_hour = bcd2bin(reg[2] & AC100_RTC_HOU_MASK); + rtc_tm->tm_wday = bcd2bin(reg[3] & AC100_RTC_WEE_MASK); + rtc_tm->tm_mday = bcd2bin(reg[4] & AC100_RTC_DAY_MASK); + rtc_tm->tm_mon = bcd2bin(reg[5] & AC100_RTC_MON_MASK) - 1; + rtc_tm->tm_year = bcd2bin(reg[6] & AC100_RTC_YEA_MASK) + + AC100_YEAR_OFF; + + return 0; +} + +static int ac100_rtc_set_time(struct device *dev, struct rtc_time *rtc_tm) +{ + struct ac100_rtc_dev *chip = dev_get_drvdata(dev); + struct regmap *regmap = chip->regmap; + int year; + u16 reg[8]; + + /* our RTC has a limited year range... */ + year = rtc_tm->tm_year - AC100_YEAR_OFF; + if (year < 0 || year > (AC100_YEAR_MAX - 1900)) { + dev_err(dev, "rtc only supports year in range %d - %d\n", + AC100_YEAR_MIN, AC100_YEAR_MAX); + return -EINVAL; + } + + /* convert to BCD */ + reg[0] = bin2bcd(rtc_tm->tm_sec) & AC100_RTC_SEC_MASK; + reg[1] = bin2bcd(rtc_tm->tm_min) & AC100_RTC_MIN_MASK; + reg[2] = bin2bcd(rtc_tm->tm_hour) & AC100_RTC_HOU_MASK; + reg[3] = bin2bcd(rtc_tm->tm_wday) & AC100_RTC_WEE_MASK; + reg[4] = bin2bcd(rtc_tm->tm_mday) & AC100_RTC_DAY_MASK; + reg[5] = bin2bcd(rtc_tm->tm_mon + 1) & AC100_RTC_MON_MASK; + reg[6] = bin2bcd(year) & AC100_RTC_YEA_MASK; + /* trigger write */ + reg[7] = AC100_RTC_UPD_TRIGGER; + + /* Is it a leap year? */ + if (is_leap_year(year + AC100_YEAR_OFF + 1900)) + reg[6] |= AC100_RTC_YEA_LEAP; + + return regmap_bulk_write(regmap, AC100_RTC_SEC, reg, 8); +} + +static int ac100_rtc_alarm_irq_enable(struct device *dev, unsigned int en) +{ + struct ac100_rtc_dev *chip = dev_get_drvdata(dev); + struct regmap *regmap = chip->regmap; + unsigned int val; + + val = en ? AC100_ALM_INT_ENABLE : 0; + + return regmap_write(regmap, AC100_ALM_INT_ENA, val); +} + +static int ac100_rtc_get_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct ac100_rtc_dev *chip = dev_get_drvdata(dev); + struct regmap *regmap = chip->regmap; + struct rtc_time *alrm_tm = &alrm->time; + u16 reg[7]; + unsigned int val; + int ret; + + ret = regmap_read(regmap, AC100_ALM_INT_ENA, &val); + if (ret) + return ret; + + alrm->enabled = !!(val & AC100_ALM_INT_ENABLE); + + ret = regmap_bulk_read(regmap, AC100_ALM_SEC, reg, 7); + if (ret) + return ret; + + alrm_tm->tm_sec = bcd2bin(reg[0] & AC100_ALM_SEC_MASK); + alrm_tm->tm_min = bcd2bin(reg[1] & AC100_ALM_MIN_MASK); + alrm_tm->tm_hour = bcd2bin(reg[2] & AC100_ALM_HOU_MASK); + alrm_tm->tm_wday = bcd2bin(reg[3] & AC100_ALM_WEE_MASK); + alrm_tm->tm_mday = bcd2bin(reg[4] & AC100_ALM_DAY_MASK); + alrm_tm->tm_mon = bcd2bin(reg[5] & AC100_ALM_MON_MASK) - 1; + alrm_tm->tm_year = bcd2bin(reg[6] & AC100_ALM_YEA_MASK) + + AC100_YEAR_OFF; + + return 0; +} + +static int ac100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct ac100_rtc_dev *chip = dev_get_drvdata(dev); + struct regmap *regmap = chip->regmap; + struct rtc_time *alrm_tm = &alrm->time; + u16 reg[8]; + int year; + int ret; + + /* our alarm has a limited year range... */ + year = alrm_tm->tm_year - AC100_YEAR_OFF; + if (year < 0 || year > (AC100_YEAR_MAX - 1900)) { + dev_err(dev, "alarm only supports year in range %d - %d\n", + AC100_YEAR_MIN, AC100_YEAR_MAX); + return -EINVAL; + } + + /* convert to BCD */ + reg[0] = (bin2bcd(alrm_tm->tm_sec) & AC100_ALM_SEC_MASK) | + AC100_ALM_ENABLE_FLAG; + reg[1] = (bin2bcd(alrm_tm->tm_min) & AC100_ALM_MIN_MASK) | + AC100_ALM_ENABLE_FLAG; + reg[2] = (bin2bcd(alrm_tm->tm_hour) & AC100_ALM_HOU_MASK) | + AC100_ALM_ENABLE_FLAG; + /* Do not enable weekday alarm */ + reg[3] = bin2bcd(alrm_tm->tm_wday) & AC100_ALM_WEE_MASK; + reg[4] = (bin2bcd(alrm_tm->tm_mday) & AC100_ALM_DAY_MASK) | + AC100_ALM_ENABLE_FLAG; + reg[5] = (bin2bcd(alrm_tm->tm_mon + 1) & AC100_ALM_MON_MASK) | + AC100_ALM_ENABLE_FLAG; + reg[6] = (bin2bcd(year) & AC100_ALM_YEA_MASK) | + AC100_ALM_ENABLE_FLAG; + /* trigger write */ + reg[7] = AC100_ALM_UPD_TRIGGER; + + ret = regmap_bulk_write(regmap, AC100_ALM_SEC, reg, 8); + if (ret) + return ret; + + return ac100_rtc_alarm_irq_enable(dev, alrm->enabled); +} + +static irqreturn_t ac100_rtc_irq(int irq, void *data) +{ + struct ac100_rtc_dev *chip = data; + struct regmap *regmap = chip->regmap; + unsigned int val = 0; + int ret; + + rtc_lock(chip->rtc); + + /* read status */ + ret = regmap_read(regmap, AC100_ALM_INT_STA, &val); + if (ret) + goto out; + + if (val & AC100_ALM_INT_ENABLE) { + /* signal rtc framework */ + rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF); + + /* clear status */ + ret = regmap_write(regmap, AC100_ALM_INT_STA, val); + if (ret) + goto out; + + /* disable interrupt */ + ret = ac100_rtc_alarm_irq_enable(chip->dev, 0); + if (ret) + goto out; + } + +out: + rtc_unlock(chip->rtc); + return IRQ_HANDLED; +} + +static const struct rtc_class_ops ac100_rtc_ops = { + .read_time = ac100_rtc_get_time, + .set_time = ac100_rtc_set_time, + .read_alarm = ac100_rtc_get_alarm, + .set_alarm = ac100_rtc_set_alarm, + .alarm_irq_enable = ac100_rtc_alarm_irq_enable, +}; + +static int ac100_rtc_probe(struct platform_device *pdev) +{ + struct ac100_dev *ac100 = dev_get_drvdata(pdev->dev.parent); + struct ac100_rtc_dev *chip; + int ret; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + platform_set_drvdata(pdev, chip); + chip->dev = &pdev->dev; + chip->regmap = ac100->regmap; + + chip->irq = platform_get_irq(pdev, 0); + if (chip->irq < 0) + return chip->irq; + + chip->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(chip->rtc)) + return PTR_ERR(chip->rtc); + + chip->rtc->ops = &ac100_rtc_ops; + + ret = devm_request_threaded_irq(&pdev->dev, chip->irq, NULL, + ac100_rtc_irq, + IRQF_SHARED | IRQF_ONESHOT, + dev_name(&pdev->dev), chip); + if (ret) { + dev_err(&pdev->dev, "Could not request IRQ\n"); + return ret; + } + + /* always use 24 hour mode */ + regmap_write_bits(chip->regmap, AC100_RTC_CTRL, AC100_RTC_CTRL_24HOUR, + AC100_RTC_CTRL_24HOUR); + + /* disable counter alarm interrupt */ + regmap_write(chip->regmap, AC100_ALM_INT_ENA, 0); + + /* clear counter alarm pending interrupts */ + regmap_write(chip->regmap, AC100_ALM_INT_STA, AC100_ALM_INT_ENABLE); + + ret = ac100_rtc_register_clks(chip); + if (ret) + return ret; + + return devm_rtc_register_device(chip->rtc); +} + +static void ac100_rtc_remove(struct platform_device *pdev) +{ + struct ac100_rtc_dev *chip = platform_get_drvdata(pdev); + + ac100_rtc_unregister_clks(chip); +} + +static const struct of_device_id ac100_rtc_match[] = { + { .compatible = "x-powers,ac100-rtc" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ac100_rtc_match); + +static struct platform_driver ac100_rtc_driver = { + .probe = ac100_rtc_probe, + .remove = ac100_rtc_remove, + .driver = { + .name = "ac100-rtc", + .of_match_table = of_match_ptr(ac100_rtc_match), + }, +}; +module_platform_driver(ac100_rtc_driver); + +MODULE_DESCRIPTION("X-Powers AC100 RTC driver"); +MODULE_AUTHOR("Chen-Yu Tsai "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-amlogic-a4.c b/drivers/rtc/rtc-amlogic-a4.c new file mode 100644 index 000000000..50938c35a --- /dev/null +++ b/drivers/rtc/rtc-amlogic-a4.c @@ -0,0 +1,443 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) +/* + * Copyright (C) 2024 Amlogic, Inc. All rights reserved + * Author: Yiting Deng + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* rtc oscillator rate */ +#define OSC_32K 32768 +#define OSC_24M 24000000 + +#define RTC_CTRL (0x0 << 2) /* Control RTC */ +#define RTC_ALRM0_EN BIT(0) +#define RTC_OSC_SEL BIT(8) +#define RTC_ENABLE BIT(12) + +#define RTC_COUNTER_REG (0x1 << 2) /* Program RTC counter initial value */ + +#define RTC_ALARM0_REG (0x2 << 2) /* Program RTC alarm0 value */ + +#define RTC_SEC_ADJUST_REG (0x6 << 2) /* Control second-based timing adjustment */ +#define RTC_MATCH_COUNTER GENMASK(18, 0) +#define RTC_SEC_ADJUST_CTRL GENMASK(20, 19) +#define RTC_ADJ_VALID BIT(23) + +#define RTC_INT_MASK (0x8 << 2) /* RTC interrupt mask */ +#define RTC_ALRM0_IRQ_MSK BIT(0) + +#define RTC_INT_CLR (0x9 << 2) /* Clear RTC interrupt */ +#define RTC_ALRM0_IRQ_CLR BIT(0) + +#define RTC_OSCIN_CTRL0 (0xa << 2) /* Control RTC clk from 24M */ +#define RTC_OSCIN_CTRL1 (0xb << 2) /* Control RTC clk from 24M */ +#define RTC_OSCIN_IN_EN BIT(31) +#define RTC_OSCIN_OUT_CFG GENMASK(29, 28) +#define RTC_OSCIN_OUT_N0M0 GENMASK(11, 0) +#define RTC_OSCIN_OUT_N1M1 GENMASK(23, 12) + +#define RTC_INT_STATUS (0xc << 2) /* RTC interrupt status */ +#define RTC_ALRM0_IRQ_STATUS BIT(0) + +#define RTC_REAL_TIME (0xd << 2) /* RTC time value */ + +#define RTC_OSCIN_OUT_32K_N0 0x2dc +#define RTC_OSCIN_OUT_32K_N1 0x2db +#define RTC_OSCIN_OUT_32K_M0 0x1 +#define RTC_OSCIN_OUT_32K_M1 0x2 + +#define RTC_SWALLOW_SECOND 0x2 +#define RTC_INSERT_SECOND 0x3 + +struct aml_rtc_config { + bool gray_stored; +}; + +struct aml_rtc_data { + struct regmap *map; + struct rtc_device *rtc_dev; + int irq; + struct clk *rtc_clk; + struct clk *sys_clk; + int rtc_enabled; + const struct aml_rtc_config *config; +}; + +static inline u32 gray_to_binary(u32 gray) +{ + u32 bcd = gray; + int size = sizeof(bcd) * 8; + int i; + + for (i = 0; (1 << i) < size; i++) + bcd ^= bcd >> (1 << i); + + return bcd; +} + +static inline u32 binary_to_gray(u32 bcd) +{ + return bcd ^ (bcd >> 1); +} + +static int aml_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct aml_rtc_data *rtc = dev_get_drvdata(dev); + u32 time_sec; + + /* if RTC disabled, read time failed */ + if (!rtc->rtc_enabled) + return -EINVAL; + + regmap_read(rtc->map, RTC_REAL_TIME, &time_sec); + if (rtc->config->gray_stored) + time_sec = gray_to_binary(time_sec); + rtc_time64_to_tm(time_sec, tm); + dev_dbg(dev, "%s: read time = %us\n", __func__, time_sec); + + return 0; +} + +static int aml_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct aml_rtc_data *rtc = dev_get_drvdata(dev); + u32 time_sec; + + /* if RTC disabled, first enable it */ + if (!rtc->rtc_enabled) { + regmap_write_bits(rtc->map, RTC_CTRL, RTC_ENABLE, RTC_ENABLE); + usleep_range(100, 200); + rtc->rtc_enabled = regmap_test_bits(rtc->map, RTC_CTRL, RTC_ENABLE); + if (!rtc->rtc_enabled) + return -EINVAL; + } + + time_sec = rtc_tm_to_time64(tm); + if (rtc->config->gray_stored) + time_sec = binary_to_gray(time_sec); + regmap_write(rtc->map, RTC_COUNTER_REG, time_sec); + dev_dbg(dev, "%s: set time = %us\n", __func__, time_sec); + + return 0; +} + +static int aml_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct aml_rtc_data *rtc = dev_get_drvdata(dev); + time64_t alarm_sec; + + /* if RTC disabled, set alarm failed */ + if (!rtc->rtc_enabled) + return -EINVAL; + + regmap_update_bits(rtc->map, RTC_CTRL, + RTC_ALRM0_EN, RTC_ALRM0_EN); + regmap_update_bits(rtc->map, RTC_INT_MASK, + RTC_ALRM0_IRQ_MSK, 0); + + alarm_sec = rtc_tm_to_time64(&alarm->time); + if (rtc->config->gray_stored) + alarm_sec = binary_to_gray(alarm_sec); + regmap_write(rtc->map, RTC_ALARM0_REG, alarm_sec); + + dev_dbg(dev, "%s: alarm->enabled=%d alarm_set=%llds\n", __func__, + alarm->enabled, alarm_sec); + + return 0; +} + +static int aml_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct aml_rtc_data *rtc = dev_get_drvdata(dev); + u32 alarm_sec; + int alarm_enable; + int alarm_mask; + + /* if RTC disabled, read alarm failed */ + if (!rtc->rtc_enabled) + return -EINVAL; + + regmap_read(rtc->map, RTC_ALARM0_REG, &alarm_sec); + if (rtc->config->gray_stored) + alarm_sec = gray_to_binary(alarm_sec); + rtc_time64_to_tm(alarm_sec, &alarm->time); + + alarm_enable = regmap_test_bits(rtc->map, RTC_CTRL, RTC_ALRM0_EN); + alarm_mask = regmap_test_bits(rtc->map, RTC_INT_MASK, RTC_ALRM0_IRQ_MSK); + alarm->enabled = (alarm_enable && !alarm_mask) ? 1 : 0; + dev_dbg(dev, "%s: alarm->enabled=%d alarm=%us\n", __func__, + alarm->enabled, alarm_sec); + + return 0; +} + +static int aml_rtc_read_offset(struct device *dev, long *offset) +{ + struct aml_rtc_data *rtc = dev_get_drvdata(dev); + u32 reg_val; + long val; + int sign, match_counter, enable; + + /* if RTC disabled, read offset failed */ + if (!rtc->rtc_enabled) + return -EINVAL; + + regmap_read(rtc->map, RTC_SEC_ADJUST_REG, ®_val); + enable = FIELD_GET(RTC_ADJ_VALID, reg_val); + if (!enable) { + val = 0; + } else { + sign = FIELD_GET(RTC_SEC_ADJUST_CTRL, reg_val); + match_counter = FIELD_GET(RTC_MATCH_COUNTER, reg_val); + val = 1000000000 / (match_counter + 1); + if (sign == RTC_SWALLOW_SECOND) + val = -val; + } + *offset = val; + + return 0; +} + +static int aml_rtc_set_offset(struct device *dev, long offset) +{ + struct aml_rtc_data *rtc = dev_get_drvdata(dev); + int sign = 0; + int match_counter = 0; + int enable = 0; + u32 reg_val; + + /* if RTC disabled, set offset failed */ + if (!rtc->rtc_enabled) + return -EINVAL; + + if (offset) { + enable = 1; + sign = offset < 0 ? RTC_SWALLOW_SECOND : RTC_INSERT_SECOND; + match_counter = 1000000000 / abs(offset) - 1; + if (match_counter < 0 || match_counter > RTC_MATCH_COUNTER) + return -EINVAL; + } + + reg_val = FIELD_PREP(RTC_ADJ_VALID, enable) | + FIELD_PREP(RTC_SEC_ADJUST_CTRL, sign) | + FIELD_PREP(RTC_MATCH_COUNTER, match_counter); + regmap_write(rtc->map, RTC_SEC_ADJUST_REG, reg_val); + + return 0; +} + +static int aml_rtc_alarm_enable(struct device *dev, unsigned int enabled) +{ + struct aml_rtc_data *rtc = dev_get_drvdata(dev); + + if (enabled) { + regmap_update_bits(rtc->map, RTC_CTRL, + RTC_ALRM0_EN, RTC_ALRM0_EN); + regmap_update_bits(rtc->map, RTC_INT_MASK, + RTC_ALRM0_IRQ_MSK, 0); + } else { + regmap_update_bits(rtc->map, RTC_INT_MASK, + RTC_ALRM0_IRQ_MSK, RTC_ALRM0_IRQ_MSK); + regmap_update_bits(rtc->map, RTC_CTRL, + RTC_ALRM0_EN, 0); + } + + return 0; +} + +static const struct rtc_class_ops aml_rtc_ops = { + .read_time = aml_rtc_read_time, + .set_time = aml_rtc_set_time, + .read_alarm = aml_rtc_read_alarm, + .set_alarm = aml_rtc_set_alarm, + .alarm_irq_enable = aml_rtc_alarm_enable, + .read_offset = aml_rtc_read_offset, + .set_offset = aml_rtc_set_offset, +}; + +static irqreturn_t aml_rtc_handler(int irq, void *data) +{ + struct aml_rtc_data *rtc = (struct aml_rtc_data *)data; + + regmap_write(rtc->map, RTC_ALARM0_REG, 0); + regmap_write(rtc->map, RTC_INT_CLR, RTC_ALRM0_IRQ_STATUS); + + rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF); + + return IRQ_HANDLED; +} + +static void aml_rtc_init(struct aml_rtc_data *rtc) +{ + u32 reg_val = 0; + + rtc->rtc_enabled = regmap_test_bits(rtc->map, RTC_CTRL, RTC_ENABLE); + if (!rtc->rtc_enabled) { + if (clk_get_rate(rtc->rtc_clk) == OSC_24M) { + /* select 24M oscillator */ + regmap_write_bits(rtc->map, RTC_CTRL, RTC_OSC_SEL, RTC_OSC_SEL); + + /* + * Set RTC oscillator to freq_out to freq_in/((N0*M0+N1*M1)/(M0+M1)) + * Enable clock_in gate of oscillator 24MHz + * Set N0 to 733, N1 to 732 + */ + reg_val = FIELD_PREP(RTC_OSCIN_IN_EN, 1) + | FIELD_PREP(RTC_OSCIN_OUT_CFG, 1) + | FIELD_PREP(RTC_OSCIN_OUT_N0M0, RTC_OSCIN_OUT_32K_N0) + | FIELD_PREP(RTC_OSCIN_OUT_N1M1, RTC_OSCIN_OUT_32K_N1); + regmap_write_bits(rtc->map, RTC_OSCIN_CTRL0, RTC_OSCIN_IN_EN + | RTC_OSCIN_OUT_CFG | RTC_OSCIN_OUT_N0M0 + | RTC_OSCIN_OUT_N1M1, reg_val); + + /* Set M0 to 2, M1 to 3, so freq_out = 32768 Hz*/ + reg_val = FIELD_PREP(RTC_OSCIN_OUT_N0M0, RTC_OSCIN_OUT_32K_M0) + | FIELD_PREP(RTC_OSCIN_OUT_N1M1, RTC_OSCIN_OUT_32K_M1); + regmap_write_bits(rtc->map, RTC_OSCIN_CTRL1, RTC_OSCIN_OUT_N0M0 + | RTC_OSCIN_OUT_N1M1, reg_val); + } else { + /* select 32K oscillator */ + regmap_write_bits(rtc->map, RTC_CTRL, RTC_OSC_SEL, 0); + } + } + regmap_write_bits(rtc->map, RTC_INT_MASK, + RTC_ALRM0_IRQ_MSK, RTC_ALRM0_IRQ_MSK); + regmap_write_bits(rtc->map, RTC_CTRL, RTC_ALRM0_EN, 0); +} + +static int aml_rtc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct aml_rtc_data *rtc; + void __iomem *base; + int ret = 0; + + const struct regmap_config aml_rtc_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = RTC_REAL_TIME, + }; + + rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->config = of_device_get_match_data(dev); + if (!rtc->config) + return -ENODEV; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return dev_err_probe(dev, PTR_ERR(base), "resource ioremap failed\n"); + + rtc->map = devm_regmap_init_mmio(dev, base, &aml_rtc_regmap_config); + if (IS_ERR(rtc->map)) + return dev_err_probe(dev, PTR_ERR(rtc->map), "regmap init failed\n"); + + rtc->irq = platform_get_irq(pdev, 0); + if (rtc->irq < 0) + return rtc->irq; + + rtc->rtc_clk = devm_clk_get(dev, "osc"); + if (IS_ERR(rtc->rtc_clk)) + return dev_err_probe(dev, PTR_ERR(rtc->rtc_clk), + "failed to find rtc clock\n"); + if (clk_get_rate(rtc->rtc_clk) != OSC_32K && clk_get_rate(rtc->rtc_clk) != OSC_24M) + return dev_err_probe(dev, -EINVAL, "Invalid clock configuration\n"); + + rtc->sys_clk = devm_clk_get_enabled(dev, "sys"); + if (IS_ERR(rtc->sys_clk)) + return dev_err_probe(dev, PTR_ERR(rtc->sys_clk), + "failed to get_enable rtc sys clk\n"); + aml_rtc_init(rtc); + + devm_device_init_wakeup(dev); + platform_set_drvdata(pdev, rtc); + + rtc->rtc_dev = devm_rtc_allocate_device(dev); + if (IS_ERR(rtc->rtc_dev)) + return PTR_ERR(rtc->rtc_dev); + + ret = devm_request_irq(dev, rtc->irq, aml_rtc_handler, + 0, "aml-rtc alarm", rtc); + if (ret) { + dev_err_probe(dev, ret, "IRQ%d request failed, ret = %d\n", + rtc->irq, ret); + return ret; + } + + rtc->rtc_dev->ops = &aml_rtc_ops; + rtc->rtc_dev->range_min = 0; + rtc->rtc_dev->range_max = U32_MAX; + + return devm_rtc_register_device(rtc->rtc_dev); +} + +#ifdef CONFIG_PM_SLEEP +static int aml_rtc_suspend(struct device *dev) +{ + struct aml_rtc_data *rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(rtc->irq); + + return 0; +} + +static int aml_rtc_resume(struct device *dev) +{ + struct aml_rtc_data *rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(rtc->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(aml_rtc_pm_ops, + aml_rtc_suspend, aml_rtc_resume); + +static const struct aml_rtc_config a5_rtc_config = { +}; + +static const struct aml_rtc_config a4_rtc_config = { + .gray_stored = true, +}; + +static const struct of_device_id aml_rtc_device_id[] = { + { + .compatible = "amlogic,a4-rtc", + .data = &a4_rtc_config, + }, + { + .compatible = "amlogic,a5-rtc", + .data = &a5_rtc_config, + }, + { } +}; +MODULE_DEVICE_TABLE(of, aml_rtc_device_id); + +static struct platform_driver aml_rtc_driver = { + .probe = aml_rtc_probe, + .driver = { + .name = "aml-rtc", + .pm = &aml_rtc_pm_ops, + .of_match_table = aml_rtc_device_id, + }, +}; + +module_platform_driver(aml_rtc_driver); +MODULE_DESCRIPTION("Amlogic RTC driver"); +MODULE_AUTHOR("Yiting Deng "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-armada38x.c b/drivers/rtc/rtc-armada38x.c new file mode 100644 index 000000000..245290ae1 --- /dev/null +++ b/drivers/rtc/rtc-armada38x.c @@ -0,0 +1,580 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * RTC driver for the Armada 38x Marvell SoCs + * + * Copyright (C) 2015 Marvell + * + * Gregory Clement + */ + +#include +#include +#include +#include +#include +#include + +#define RTC_STATUS 0x0 +#define RTC_STATUS_ALARM1 BIT(0) +#define RTC_STATUS_ALARM2 BIT(1) +#define RTC_IRQ1_CONF 0x4 +#define RTC_IRQ2_CONF 0x8 +#define RTC_IRQ_AL_EN BIT(0) +#define RTC_IRQ_FREQ_EN BIT(1) +#define RTC_IRQ_FREQ_1HZ BIT(2) +#define RTC_CCR 0x18 +#define RTC_CCR_MODE BIT(15) +#define RTC_CONF_TEST 0x1C +#define RTC_NOMINAL_TIMING BIT(13) + +#define RTC_TIME 0xC +#define RTC_ALARM1 0x10 +#define RTC_ALARM2 0x14 + +/* Armada38x SoC registers */ +#define RTC_38X_BRIDGE_TIMING_CTL 0x0 +#define RTC_38X_PERIOD_OFFS 0 +#define RTC_38X_PERIOD_MASK (0x3FF << RTC_38X_PERIOD_OFFS) +#define RTC_38X_READ_DELAY_OFFS 26 +#define RTC_38X_READ_DELAY_MASK (0x1F << RTC_38X_READ_DELAY_OFFS) + +/* Armada 7K/8K registers */ +#define RTC_8K_BRIDGE_TIMING_CTL0 0x0 +#define RTC_8K_WRCLK_PERIOD_OFFS 0 +#define RTC_8K_WRCLK_PERIOD_MASK (0xFFFF << RTC_8K_WRCLK_PERIOD_OFFS) +#define RTC_8K_WRCLK_SETUP_OFFS 16 +#define RTC_8K_WRCLK_SETUP_MASK (0xFFFF << RTC_8K_WRCLK_SETUP_OFFS) +#define RTC_8K_BRIDGE_TIMING_CTL1 0x4 +#define RTC_8K_READ_DELAY_OFFS 0 +#define RTC_8K_READ_DELAY_MASK (0xFFFF << RTC_8K_READ_DELAY_OFFS) + +#define RTC_8K_ISR 0x10 +#define RTC_8K_IMR 0x14 +#define RTC_8K_ALARM2 BIT(0) + +#define SOC_RTC_INTERRUPT 0x8 +#define SOC_RTC_ALARM1 BIT(0) +#define SOC_RTC_ALARM2 BIT(1) +#define SOC_RTC_ALARM1_MASK BIT(2) +#define SOC_RTC_ALARM2_MASK BIT(3) + +#define SAMPLE_NR 100 + +struct value_to_freq { + u32 value; + u8 freq; +}; + +struct armada38x_rtc { + struct rtc_device *rtc_dev; + void __iomem *regs; + void __iomem *regs_soc; + spinlock_t lock; + int irq; + bool initialized; + const struct armada38x_rtc_data *data; + struct value_to_freq val_to_freq[]; +}; + +#define ALARM1 0 +#define ALARM2 1 + +#define ALARM_REG(base, alarm) ((base) + (alarm) * sizeof(u32)) + +struct armada38x_rtc_data { + /* Initialize the RTC-MBUS bridge timing */ + void (*update_mbus_timing)(struct armada38x_rtc *rtc); + u32 (*read_rtc_reg)(struct armada38x_rtc *rtc, u8 rtc_reg); + void (*clear_isr)(struct armada38x_rtc *rtc); + void (*unmask_interrupt)(struct armada38x_rtc *rtc); + u32 alarm; +}; + +/* + * According to the datasheet, the OS should wait 5us after every + * register write to the RTC hard macro so that the required update + * can occur without holding off the system bus + * According to errata RES-3124064, Write to any RTC register + * may fail. As a workaround, before writing to RTC + * register, issue a dummy write of 0x0 twice to RTC Status + * register. + */ + +static void rtc_delayed_write(u32 val, struct armada38x_rtc *rtc, int offset) +{ + writel(0, rtc->regs + RTC_STATUS); + writel(0, rtc->regs + RTC_STATUS); + writel(val, rtc->regs + offset); + udelay(5); +} + +/* Update RTC-MBUS bridge timing parameters */ +static void rtc_update_38x_mbus_timing_params(struct armada38x_rtc *rtc) +{ + u32 reg; + + reg = readl(rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL); + reg &= ~RTC_38X_PERIOD_MASK; + reg |= 0x3FF << RTC_38X_PERIOD_OFFS; /* Maximum value */ + reg &= ~RTC_38X_READ_DELAY_MASK; + reg |= 0x1F << RTC_38X_READ_DELAY_OFFS; /* Maximum value */ + writel(reg, rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL); +} + +static void rtc_update_8k_mbus_timing_params(struct armada38x_rtc *rtc) +{ + u32 reg; + + reg = readl(rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL0); + reg &= ~RTC_8K_WRCLK_PERIOD_MASK; + reg |= 0x3FF << RTC_8K_WRCLK_PERIOD_OFFS; + reg &= ~RTC_8K_WRCLK_SETUP_MASK; + reg |= 0x29 << RTC_8K_WRCLK_SETUP_OFFS; + writel(reg, rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL0); + + reg = readl(rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL1); + reg &= ~RTC_8K_READ_DELAY_MASK; + reg |= 0x3F << RTC_8K_READ_DELAY_OFFS; + writel(reg, rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL1); +} + +static u32 read_rtc_register(struct armada38x_rtc *rtc, u8 rtc_reg) +{ + return readl(rtc->regs + rtc_reg); +} + +static u32 read_rtc_register_38x_wa(struct armada38x_rtc *rtc, u8 rtc_reg) +{ + int i, index_max = 0, max = 0; + + for (i = 0; i < SAMPLE_NR; i++) { + rtc->val_to_freq[i].value = readl(rtc->regs + rtc_reg); + rtc->val_to_freq[i].freq = 0; + } + + for (i = 0; i < SAMPLE_NR; i++) { + int j = 0; + u32 value = rtc->val_to_freq[i].value; + + while (rtc->val_to_freq[j].freq) { + if (rtc->val_to_freq[j].value == value) { + rtc->val_to_freq[j].freq++; + break; + } + j++; + } + + if (!rtc->val_to_freq[j].freq) { + rtc->val_to_freq[j].value = value; + rtc->val_to_freq[j].freq = 1; + } + + if (rtc->val_to_freq[j].freq > max) { + index_max = j; + max = rtc->val_to_freq[j].freq; + } + + /* + * If a value already has half of the sample this is the most + * frequent one and we can stop the research right now + */ + if (max > SAMPLE_NR / 2) + break; + } + + return rtc->val_to_freq[index_max].value; +} + +static void armada38x_clear_isr(struct armada38x_rtc *rtc) +{ + u32 val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT); + + writel(val & ~SOC_RTC_ALARM1, rtc->regs_soc + SOC_RTC_INTERRUPT); +} + +static void armada38x_unmask_interrupt(struct armada38x_rtc *rtc) +{ + u32 val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT); + + writel(val | SOC_RTC_ALARM1_MASK, rtc->regs_soc + SOC_RTC_INTERRUPT); +} + +static void armada8k_clear_isr(struct armada38x_rtc *rtc) +{ + writel(RTC_8K_ALARM2, rtc->regs_soc + RTC_8K_ISR); +} + +static void armada8k_unmask_interrupt(struct armada38x_rtc *rtc) +{ + writel(RTC_8K_ALARM2, rtc->regs_soc + RTC_8K_IMR); +} + +static int armada38x_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct armada38x_rtc *rtc = dev_get_drvdata(dev); + unsigned long time, flags; + + spin_lock_irqsave(&rtc->lock, flags); + time = rtc->data->read_rtc_reg(rtc, RTC_TIME); + spin_unlock_irqrestore(&rtc->lock, flags); + + rtc_time64_to_tm(time, tm); + + return 0; +} + +static void armada38x_rtc_reset(struct armada38x_rtc *rtc) +{ + u32 reg; + + reg = rtc->data->read_rtc_reg(rtc, RTC_CONF_TEST); + /* If bits [7:0] are non-zero, assume RTC was uninitialized */ + if (reg & 0xff) { + rtc_delayed_write(0, rtc, RTC_CONF_TEST); + msleep(500); /* Oscillator startup time */ + rtc_delayed_write(0, rtc, RTC_TIME); + rtc_delayed_write(SOC_RTC_ALARM1 | SOC_RTC_ALARM2, rtc, + RTC_STATUS); + rtc_delayed_write(RTC_NOMINAL_TIMING, rtc, RTC_CCR); + } + rtc->initialized = true; +} + +static int armada38x_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct armada38x_rtc *rtc = dev_get_drvdata(dev); + unsigned long time, flags; + + time = rtc_tm_to_time64(tm); + + if (!rtc->initialized) + armada38x_rtc_reset(rtc); + + spin_lock_irqsave(&rtc->lock, flags); + rtc_delayed_write(time, rtc, RTC_TIME); + spin_unlock_irqrestore(&rtc->lock, flags); + + return 0; +} + +static int armada38x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct armada38x_rtc *rtc = dev_get_drvdata(dev); + unsigned long time, flags; + u32 reg = ALARM_REG(RTC_ALARM1, rtc->data->alarm); + u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm); + u32 val; + + spin_lock_irqsave(&rtc->lock, flags); + + time = rtc->data->read_rtc_reg(rtc, reg); + val = rtc->data->read_rtc_reg(rtc, reg_irq) & RTC_IRQ_AL_EN; + + spin_unlock_irqrestore(&rtc->lock, flags); + + alrm->enabled = val ? 1 : 0; + rtc_time64_to_tm(time, &alrm->time); + + return 0; +} + +static int armada38x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct armada38x_rtc *rtc = dev_get_drvdata(dev); + u32 reg = ALARM_REG(RTC_ALARM1, rtc->data->alarm); + u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm); + unsigned long time, flags; + + time = rtc_tm_to_time64(&alrm->time); + + spin_lock_irqsave(&rtc->lock, flags); + + rtc_delayed_write(time, rtc, reg); + + if (alrm->enabled) { + rtc_delayed_write(RTC_IRQ_AL_EN, rtc, reg_irq); + rtc->data->unmask_interrupt(rtc); + } + + spin_unlock_irqrestore(&rtc->lock, flags); + + return 0; +} + +static int armada38x_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct armada38x_rtc *rtc = dev_get_drvdata(dev); + u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm); + unsigned long flags; + + spin_lock_irqsave(&rtc->lock, flags); + + if (enabled) + rtc_delayed_write(RTC_IRQ_AL_EN, rtc, reg_irq); + else + rtc_delayed_write(0, rtc, reg_irq); + + spin_unlock_irqrestore(&rtc->lock, flags); + + return 0; +} + +static irqreturn_t armada38x_rtc_alarm_irq(int irq, void *data) +{ + struct armada38x_rtc *rtc = data; + u32 val; + int event = RTC_IRQF | RTC_AF; + u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm); + + dev_dbg(&rtc->rtc_dev->dev, "%s:irq(%d)\n", __func__, irq); + + spin_lock(&rtc->lock); + + rtc->data->clear_isr(rtc); + val = rtc->data->read_rtc_reg(rtc, reg_irq); + /* disable all the interrupts for alarm*/ + rtc_delayed_write(0, rtc, reg_irq); + /* Ack the event */ + rtc_delayed_write(1 << rtc->data->alarm, rtc, RTC_STATUS); + + spin_unlock(&rtc->lock); + + if (val & RTC_IRQ_FREQ_EN) { + if (val & RTC_IRQ_FREQ_1HZ) + event |= RTC_UF; + else + event |= RTC_PF; + } + + rtc_update_irq(rtc->rtc_dev, 1, event); + + return IRQ_HANDLED; +} + +/* + * The information given in the Armada 388 functional spec is complex. + * They give two different formulas for calculating the offset value, + * but when considering "Offset" as an 8-bit signed integer, they both + * reduce down to (we shall rename "Offset" as "val" here): + * + * val = (f_ideal / f_measured - 1) / resolution where f_ideal = 32768 + * + * Converting to time, f = 1/t: + * val = (t_measured / t_ideal - 1) / resolution where t_ideal = 1/32768 + * + * => t_measured / t_ideal = val * resolution + 1 + * + * "offset" in the RTC interface is defined as: + * t = t0 * (1 + offset * 1e-9) + * where t is the desired period, t0 is the measured period with a zero + * offset, which is t_measured above. With t0 = t_measured and t = t_ideal, + * offset = (t_ideal / t_measured - 1) / 1e-9 + * + * => t_ideal / t_measured = offset * 1e-9 + 1 + * + * so: + * + * offset * 1e-9 + 1 = 1 / (val * resolution + 1) + * + * We want "resolution" to be an integer, so resolution = R * 1e-9, giving + * offset = 1e18 / (val * R + 1e9) - 1e9 + * val = (1e18 / (offset + 1e9) - 1e9) / R + * with a common transformation: + * f(x) = 1e18 / (x + 1e9) - 1e9 + * offset = f(val * R) + * val = f(offset) / R + * + * Armada 38x supports two modes, fine mode (954ppb) and coarse mode (3815ppb). + */ +static long armada38x_ppb_convert(long ppb) +{ + long div = ppb + 1000000000L; + + return div_s64(1000000000000000000LL + div / 2, div) - 1000000000L; +} + +static int armada38x_rtc_read_offset(struct device *dev, long *offset) +{ + struct armada38x_rtc *rtc = dev_get_drvdata(dev); + unsigned long ccr, flags; + long ppb_cor; + + spin_lock_irqsave(&rtc->lock, flags); + ccr = rtc->data->read_rtc_reg(rtc, RTC_CCR); + spin_unlock_irqrestore(&rtc->lock, flags); + + ppb_cor = (ccr & RTC_CCR_MODE ? 3815 : 954) * (s8)ccr; + /* ppb_cor + 1000000000L can never be zero */ + *offset = armada38x_ppb_convert(ppb_cor); + + return 0; +} + +static int armada38x_rtc_set_offset(struct device *dev, long offset) +{ + struct armada38x_rtc *rtc = dev_get_drvdata(dev); + unsigned long ccr = 0; + long ppb_cor, off; + + /* + * The maximum ppb_cor is -128 * 3815 .. 127 * 3815, but we + * need to clamp the input. This equates to -484270 .. 488558. + * Not only is this to stop out of range "off" but also to + * avoid the division by zero in armada38x_ppb_convert(). + */ + offset = clamp(offset, -484270L, 488558L); + + ppb_cor = armada38x_ppb_convert(offset); + + /* + * Use low update mode where possible, which gives a better + * resolution of correction. + */ + off = DIV_ROUND_CLOSEST(ppb_cor, 954); + if (off > 127 || off < -128) { + ccr = RTC_CCR_MODE; + off = DIV_ROUND_CLOSEST(ppb_cor, 3815); + } + + /* + * Armada 388 requires a bit pattern in bits 14..8 depending on + * the sign bit: { 0, ~S, S, S, S, S, S } + */ + ccr |= (off & 0x3fff) ^ 0x2000; + rtc_delayed_write(ccr, rtc, RTC_CCR); + + return 0; +} + +static const struct rtc_class_ops armada38x_rtc_ops = { + .read_time = armada38x_rtc_read_time, + .set_time = armada38x_rtc_set_time, + .read_alarm = armada38x_rtc_read_alarm, + .set_alarm = armada38x_rtc_set_alarm, + .alarm_irq_enable = armada38x_rtc_alarm_irq_enable, + .read_offset = armada38x_rtc_read_offset, + .set_offset = armada38x_rtc_set_offset, +}; + +static const struct armada38x_rtc_data armada38x_data = { + .update_mbus_timing = rtc_update_38x_mbus_timing_params, + .read_rtc_reg = read_rtc_register_38x_wa, + .clear_isr = armada38x_clear_isr, + .unmask_interrupt = armada38x_unmask_interrupt, + .alarm = ALARM1, +}; + +static const struct armada38x_rtc_data armada8k_data = { + .update_mbus_timing = rtc_update_8k_mbus_timing_params, + .read_rtc_reg = read_rtc_register, + .clear_isr = armada8k_clear_isr, + .unmask_interrupt = armada8k_unmask_interrupt, + .alarm = ALARM2, +}; + +static const struct of_device_id armada38x_rtc_of_match_table[] = { + { + .compatible = "marvell,armada-380-rtc", + .data = &armada38x_data, + }, + { + .compatible = "marvell,armada-8k-rtc", + .data = &armada8k_data, + }, + {} +}; +MODULE_DEVICE_TABLE(of, armada38x_rtc_of_match_table); + +static __init int armada38x_rtc_probe(struct platform_device *pdev) +{ + struct armada38x_rtc *rtc; + + rtc = devm_kzalloc(&pdev->dev, struct_size(rtc, val_to_freq, SAMPLE_NR), + GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->data = of_device_get_match_data(&pdev->dev); + + spin_lock_init(&rtc->lock); + + rtc->regs = devm_platform_ioremap_resource_byname(pdev, "rtc"); + if (IS_ERR(rtc->regs)) + return PTR_ERR(rtc->regs); + rtc->regs_soc = devm_platform_ioremap_resource_byname(pdev, "rtc-soc"); + if (IS_ERR(rtc->regs_soc)) + return PTR_ERR(rtc->regs_soc); + + rtc->irq = platform_get_irq(pdev, 0); + if (rtc->irq < 0) + return rtc->irq; + + rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc->rtc_dev)) + return PTR_ERR(rtc->rtc_dev); + + if (devm_request_irq(&pdev->dev, rtc->irq, armada38x_rtc_alarm_irq, + 0, pdev->name, rtc) < 0) { + dev_warn(&pdev->dev, "Interrupt not available.\n"); + rtc->irq = -1; + } + platform_set_drvdata(pdev, rtc); + + if (rtc->irq != -1) + device_init_wakeup(&pdev->dev, true); + else + clear_bit(RTC_FEATURE_ALARM, rtc->rtc_dev->features); + + /* Update RTC-MBUS bridge timing parameters */ + rtc->data->update_mbus_timing(rtc); + + rtc->rtc_dev->ops = &armada38x_rtc_ops; + rtc->rtc_dev->range_max = U32_MAX; + + return devm_rtc_register_device(rtc->rtc_dev); +} + +#ifdef CONFIG_PM_SLEEP +static int armada38x_rtc_suspend(struct device *dev) +{ + if (device_may_wakeup(dev)) { + struct armada38x_rtc *rtc = dev_get_drvdata(dev); + + return enable_irq_wake(rtc->irq); + } + + return 0; +} + +static int armada38x_rtc_resume(struct device *dev) +{ + if (device_may_wakeup(dev)) { + struct armada38x_rtc *rtc = dev_get_drvdata(dev); + + /* Update RTC-MBUS bridge timing parameters */ + rtc->data->update_mbus_timing(rtc); + + return disable_irq_wake(rtc->irq); + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(armada38x_rtc_pm_ops, + armada38x_rtc_suspend, armada38x_rtc_resume); + +static struct platform_driver armada38x_rtc_driver = { + .driver = { + .name = "armada38x-rtc", + .pm = &armada38x_rtc_pm_ops, + .of_match_table = armada38x_rtc_of_match_table, + }, +}; + +module_platform_driver_probe(armada38x_rtc_driver, armada38x_rtc_probe); + +MODULE_DESCRIPTION("Marvell Armada 38x RTC driver"); +MODULE_AUTHOR("Gregory CLEMENT "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-as3722.c b/drivers/rtc/rtc-as3722.c new file mode 100644 index 000000000..9682d6457 --- /dev/null +++ b/drivers/rtc/rtc-as3722.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * rtc-as3722.c - Real Time Clock driver for ams AS3722 PMICs + * + * Copyright (C) 2013 ams AG + * Copyright (c) 2013, NVIDIA Corporation. All rights reserved. + * + * Author: Florian Lobmaier + * Author: Laxman Dewangan + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AS3722_RTC_START_YEAR 2000 +struct as3722_rtc { + struct rtc_device *rtc; + struct device *dev; + struct as3722 *as3722; + int alarm_irq; + bool irq_enable; +}; + +static void as3722_time_to_reg(u8 *rbuff, struct rtc_time *tm) +{ + rbuff[0] = bin2bcd(tm->tm_sec); + rbuff[1] = bin2bcd(tm->tm_min); + rbuff[2] = bin2bcd(tm->tm_hour); + rbuff[3] = bin2bcd(tm->tm_mday); + rbuff[4] = bin2bcd(tm->tm_mon + 1); + rbuff[5] = bin2bcd(tm->tm_year - (AS3722_RTC_START_YEAR - 1900)); +} + +static void as3722_reg_to_time(u8 *rbuff, struct rtc_time *tm) +{ + tm->tm_sec = bcd2bin(rbuff[0] & 0x7F); + tm->tm_min = bcd2bin(rbuff[1] & 0x7F); + tm->tm_hour = bcd2bin(rbuff[2] & 0x3F); + tm->tm_mday = bcd2bin(rbuff[3] & 0x3F); + tm->tm_mon = bcd2bin(rbuff[4] & 0x1F) - 1; + tm->tm_year = (AS3722_RTC_START_YEAR - 1900) + bcd2bin(rbuff[5] & 0x7F); + return; +} + +static int as3722_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev); + struct as3722 *as3722 = as3722_rtc->as3722; + u8 as_time_array[6]; + int ret; + + ret = as3722_block_read(as3722, AS3722_RTC_SECOND_REG, + 6, as_time_array); + if (ret < 0) { + dev_err(dev, "RTC_SECOND reg block read failed %d\n", ret); + return ret; + } + as3722_reg_to_time(as_time_array, tm); + return 0; +} + +static int as3722_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev); + struct as3722 *as3722 = as3722_rtc->as3722; + u8 as_time_array[6]; + int ret; + + if (tm->tm_year < (AS3722_RTC_START_YEAR - 1900)) + return -EINVAL; + + as3722_time_to_reg(as_time_array, tm); + ret = as3722_block_write(as3722, AS3722_RTC_SECOND_REG, 6, + as_time_array); + if (ret < 0) + dev_err(dev, "RTC_SECOND reg block write failed %d\n", ret); + return ret; +} + +static int as3722_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev); + + if (enabled && !as3722_rtc->irq_enable) { + enable_irq(as3722_rtc->alarm_irq); + as3722_rtc->irq_enable = true; + } else if (!enabled && as3722_rtc->irq_enable) { + disable_irq(as3722_rtc->alarm_irq); + as3722_rtc->irq_enable = false; + } + return 0; +} + +static int as3722_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev); + struct as3722 *as3722 = as3722_rtc->as3722; + u8 as_time_array[6]; + int ret; + + ret = as3722_block_read(as3722, AS3722_RTC_ALARM_SECOND_REG, 6, + as_time_array); + if (ret < 0) { + dev_err(dev, "RTC_ALARM_SECOND block read failed %d\n", ret); + return ret; + } + + as3722_reg_to_time(as_time_array, &alrm->time); + return 0; +} + +static int as3722_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev); + struct as3722 *as3722 = as3722_rtc->as3722; + u8 as_time_array[6]; + int ret; + + if (alrm->time.tm_year < (AS3722_RTC_START_YEAR - 1900)) + return -EINVAL; + + ret = as3722_rtc_alarm_irq_enable(dev, 0); + if (ret < 0) { + dev_err(dev, "Disable RTC alarm failed\n"); + return ret; + } + + as3722_time_to_reg(as_time_array, &alrm->time); + ret = as3722_block_write(as3722, AS3722_RTC_ALARM_SECOND_REG, 6, + as_time_array); + if (ret < 0) { + dev_err(dev, "RTC_ALARM_SECOND block write failed %d\n", ret); + return ret; + } + + if (alrm->enabled) + ret = as3722_rtc_alarm_irq_enable(dev, alrm->enabled); + return ret; +} + +static irqreturn_t as3722_alarm_irq(int irq, void *data) +{ + struct as3722_rtc *as3722_rtc = data; + + rtc_update_irq(as3722_rtc->rtc, 1, RTC_IRQF | RTC_AF); + return IRQ_HANDLED; +} + +static const struct rtc_class_ops as3722_rtc_ops = { + .read_time = as3722_rtc_read_time, + .set_time = as3722_rtc_set_time, + .read_alarm = as3722_rtc_read_alarm, + .set_alarm = as3722_rtc_set_alarm, + .alarm_irq_enable = as3722_rtc_alarm_irq_enable, +}; + +static int as3722_rtc_probe(struct platform_device *pdev) +{ + struct as3722 *as3722 = dev_get_drvdata(pdev->dev.parent); + struct as3722_rtc *as3722_rtc; + int ret; + + as3722_rtc = devm_kzalloc(&pdev->dev, sizeof(*as3722_rtc), GFP_KERNEL); + if (!as3722_rtc) + return -ENOMEM; + + as3722_rtc->as3722 = as3722; + as3722_rtc->dev = &pdev->dev; + platform_set_drvdata(pdev, as3722_rtc); + + /* Enable the RTC to make sure it is running. */ + ret = as3722_update_bits(as3722, AS3722_RTC_CONTROL_REG, + AS3722_RTC_ON | AS3722_RTC_ALARM_WAKEUP_EN, + AS3722_RTC_ON | AS3722_RTC_ALARM_WAKEUP_EN); + if (ret < 0) { + dev_err(&pdev->dev, "RTC_CONTROL reg write failed: %d\n", ret); + return ret; + } + + device_init_wakeup(&pdev->dev, true); + + as3722_rtc->rtc = devm_rtc_device_register(&pdev->dev, "as3722-rtc", + &as3722_rtc_ops, THIS_MODULE); + if (IS_ERR(as3722_rtc->rtc)) { + ret = PTR_ERR(as3722_rtc->rtc); + dev_err(&pdev->dev, "RTC register failed: %d\n", ret); + return ret; + } + + as3722_rtc->alarm_irq = platform_get_irq(pdev, 0); + dev_info(&pdev->dev, "RTC interrupt %d\n", as3722_rtc->alarm_irq); + + ret = devm_request_threaded_irq(&pdev->dev, as3722_rtc->alarm_irq, NULL, + as3722_alarm_irq, IRQF_ONESHOT, + "rtc-alarm", as3722_rtc); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n", + as3722_rtc->alarm_irq, ret); + return ret; + } + disable_irq(as3722_rtc->alarm_irq); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int as3722_rtc_suspend(struct device *dev) +{ + struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(as3722_rtc->alarm_irq); + + return 0; +} + +static int as3722_rtc_resume(struct device *dev) +{ + struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(as3722_rtc->alarm_irq); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(as3722_rtc_pm_ops, as3722_rtc_suspend, + as3722_rtc_resume); + +static struct platform_driver as3722_rtc_driver = { + .probe = as3722_rtc_probe, + .driver = { + .name = "as3722-rtc", + .pm = &as3722_rtc_pm_ops, + }, +}; +module_platform_driver(as3722_rtc_driver); + +MODULE_DESCRIPTION("RTC driver for AS3722 PMICs"); +MODULE_ALIAS("platform:as3722-rtc"); +MODULE_AUTHOR("Florian Lobmaier "); +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-asm9260.c b/drivers/rtc/rtc-asm9260.c new file mode 100644 index 000000000..705470ae8 --- /dev/null +++ b/drivers/rtc/rtc-asm9260.c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Oleksij Rempel + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Miscellaneous registers */ +/* Interrupt Location Register */ +#define HW_ILR 0x00 +#define BM_RTCALF BIT(1) +#define BM_RTCCIF BIT(0) + +/* Clock Control Register */ +#define HW_CCR 0x08 +/* Calibration counter disable */ +#define BM_CCALOFF BIT(4) +/* Reset internal oscillator divider */ +#define BM_CTCRST BIT(1) +/* Clock Enable */ +#define BM_CLKEN BIT(0) + +/* Counter Increment Interrupt Register */ +#define HW_CIIR 0x0C +#define BM_CIIR_IMYEAR BIT(7) +#define BM_CIIR_IMMON BIT(6) +#define BM_CIIR_IMDOY BIT(5) +#define BM_CIIR_IMDOW BIT(4) +#define BM_CIIR_IMDOM BIT(3) +#define BM_CIIR_IMHOUR BIT(2) +#define BM_CIIR_IMMIN BIT(1) +#define BM_CIIR_IMSEC BIT(0) + +/* Alarm Mask Register */ +#define HW_AMR 0x10 +#define BM_AMR_IMYEAR BIT(7) +#define BM_AMR_IMMON BIT(6) +#define BM_AMR_IMDOY BIT(5) +#define BM_AMR_IMDOW BIT(4) +#define BM_AMR_IMDOM BIT(3) +#define BM_AMR_IMHOUR BIT(2) +#define BM_AMR_IMMIN BIT(1) +#define BM_AMR_IMSEC BIT(0) +#define BM_AMR_OFF 0xff + +/* Consolidated time registers */ +#define HW_CTIME0 0x14 +#define BM_CTIME0_DOW_S 24 +#define BM_CTIME0_DOW_M 0x7 +#define BM_CTIME0_HOUR_S 16 +#define BM_CTIME0_HOUR_M 0x1f +#define BM_CTIME0_MIN_S 8 +#define BM_CTIME0_MIN_M 0x3f +#define BM_CTIME0_SEC_S 0 +#define BM_CTIME0_SEC_M 0x3f + +#define HW_CTIME1 0x18 +#define BM_CTIME1_YEAR_S 16 +#define BM_CTIME1_YEAR_M 0xfff +#define BM_CTIME1_MON_S 8 +#define BM_CTIME1_MON_M 0xf +#define BM_CTIME1_DOM_S 0 +#define BM_CTIME1_DOM_M 0x1f + +#define HW_CTIME2 0x1C +#define BM_CTIME2_DOY_S 0 +#define BM_CTIME2_DOY_M 0xfff + +/* Time counter registers */ +#define HW_SEC 0x20 +#define HW_MIN 0x24 +#define HW_HOUR 0x28 +#define HW_DOM 0x2C +#define HW_DOW 0x30 +#define HW_DOY 0x34 +#define HW_MONTH 0x38 +#define HW_YEAR 0x3C + +#define HW_CALIBRATION 0x40 +#define BM_CALDIR_BACK BIT(17) +#define BM_CALVAL_M 0x1ffff + +/* General purpose registers */ +#define HW_GPREG0 0x44 +#define HW_GPREG1 0x48 +#define HW_GPREG2 0x4C +#define HW_GPREG3 0x50 +#define HW_GPREG4 0x54 + +/* Alarm register group */ +#define HW_ALSEC 0x60 +#define HW_ALMIN 0x64 +#define HW_ALHOUR 0x68 +#define HW_ALDOM 0x6C +#define HW_ALDOW 0x70 +#define HW_ALDOY 0x74 +#define HW_ALMON 0x78 +#define HW_ALYEAR 0x7C + +struct asm9260_rtc_priv { + struct device *dev; + void __iomem *iobase; + struct rtc_device *rtc; + struct clk *clk; +}; + +static irqreturn_t asm9260_rtc_irq(int irq, void *dev_id) +{ + struct asm9260_rtc_priv *priv = dev_id; + u32 isr; + unsigned long events = 0; + + rtc_lock(priv->rtc); + isr = ioread32(priv->iobase + HW_CIIR); + if (!isr) { + rtc_unlock(priv->rtc); + return IRQ_NONE; + } + + iowrite32(0, priv->iobase + HW_CIIR); + rtc_unlock(priv->rtc); + + events |= RTC_AF | RTC_IRQF; + + rtc_update_irq(priv->rtc, 1, events); + + return IRQ_HANDLED; +} + +static int asm9260_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct asm9260_rtc_priv *priv = dev_get_drvdata(dev); + u32 ctime0, ctime1, ctime2; + + ctime0 = ioread32(priv->iobase + HW_CTIME0); + ctime1 = ioread32(priv->iobase + HW_CTIME1); + ctime2 = ioread32(priv->iobase + HW_CTIME2); + + if (ctime1 != ioread32(priv->iobase + HW_CTIME1)) { + /* + * woops, counter flipped right now. Now we are safe + * to reread. + */ + ctime0 = ioread32(priv->iobase + HW_CTIME0); + ctime1 = ioread32(priv->iobase + HW_CTIME1); + ctime2 = ioread32(priv->iobase + HW_CTIME2); + } + + tm->tm_sec = (ctime0 >> BM_CTIME0_SEC_S) & BM_CTIME0_SEC_M; + tm->tm_min = (ctime0 >> BM_CTIME0_MIN_S) & BM_CTIME0_MIN_M; + tm->tm_hour = (ctime0 >> BM_CTIME0_HOUR_S) & BM_CTIME0_HOUR_M; + tm->tm_wday = (ctime0 >> BM_CTIME0_DOW_S) & BM_CTIME0_DOW_M; + + tm->tm_mday = (ctime1 >> BM_CTIME1_DOM_S) & BM_CTIME1_DOM_M; + tm->tm_mon = (ctime1 >> BM_CTIME1_MON_S) & BM_CTIME1_MON_M; + tm->tm_year = (ctime1 >> BM_CTIME1_YEAR_S) & BM_CTIME1_YEAR_M; + + tm->tm_yday = (ctime2 >> BM_CTIME2_DOY_S) & BM_CTIME2_DOY_M; + + return 0; +} + +static int asm9260_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct asm9260_rtc_priv *priv = dev_get_drvdata(dev); + + /* + * make sure SEC counter will not flip other counter on write time, + * real value will be written at the enf of sequence. + */ + iowrite32(0, priv->iobase + HW_SEC); + + iowrite32(tm->tm_year, priv->iobase + HW_YEAR); + iowrite32(tm->tm_mon, priv->iobase + HW_MONTH); + iowrite32(tm->tm_mday, priv->iobase + HW_DOM); + iowrite32(tm->tm_wday, priv->iobase + HW_DOW); + iowrite32(tm->tm_yday, priv->iobase + HW_DOY); + iowrite32(tm->tm_hour, priv->iobase + HW_HOUR); + iowrite32(tm->tm_min, priv->iobase + HW_MIN); + iowrite32(tm->tm_sec, priv->iobase + HW_SEC); + + return 0; +} + +static int asm9260_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct asm9260_rtc_priv *priv = dev_get_drvdata(dev); + + alrm->time.tm_year = ioread32(priv->iobase + HW_ALYEAR); + alrm->time.tm_mon = ioread32(priv->iobase + HW_ALMON); + alrm->time.tm_mday = ioread32(priv->iobase + HW_ALDOM); + alrm->time.tm_wday = ioread32(priv->iobase + HW_ALDOW); + alrm->time.tm_yday = ioread32(priv->iobase + HW_ALDOY); + alrm->time.tm_hour = ioread32(priv->iobase + HW_ALHOUR); + alrm->time.tm_min = ioread32(priv->iobase + HW_ALMIN); + alrm->time.tm_sec = ioread32(priv->iobase + HW_ALSEC); + + alrm->enabled = ioread32(priv->iobase + HW_AMR) ? 1 : 0; + alrm->pending = ioread32(priv->iobase + HW_CIIR) ? 1 : 0; + + return rtc_valid_tm(&alrm->time); +} + +static int asm9260_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct asm9260_rtc_priv *priv = dev_get_drvdata(dev); + + iowrite32(alrm->time.tm_year, priv->iobase + HW_ALYEAR); + iowrite32(alrm->time.tm_mon, priv->iobase + HW_ALMON); + iowrite32(alrm->time.tm_mday, priv->iobase + HW_ALDOM); + iowrite32(alrm->time.tm_wday, priv->iobase + HW_ALDOW); + iowrite32(alrm->time.tm_yday, priv->iobase + HW_ALDOY); + iowrite32(alrm->time.tm_hour, priv->iobase + HW_ALHOUR); + iowrite32(alrm->time.tm_min, priv->iobase + HW_ALMIN); + iowrite32(alrm->time.tm_sec, priv->iobase + HW_ALSEC); + + iowrite32(alrm->enabled ? 0 : BM_AMR_OFF, priv->iobase + HW_AMR); + + return 0; +} + +static int asm9260_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct asm9260_rtc_priv *priv = dev_get_drvdata(dev); + + iowrite32(enabled ? 0 : BM_AMR_OFF, priv->iobase + HW_AMR); + return 0; +} + +static const struct rtc_class_ops asm9260_rtc_ops = { + .read_time = asm9260_rtc_read_time, + .set_time = asm9260_rtc_set_time, + .read_alarm = asm9260_rtc_read_alarm, + .set_alarm = asm9260_rtc_set_alarm, + .alarm_irq_enable = asm9260_alarm_irq_enable, +}; + +static int asm9260_rtc_probe(struct platform_device *pdev) +{ + struct asm9260_rtc_priv *priv; + struct device *dev = &pdev->dev; + int irq_alarm, ret; + u32 ccr; + + priv = devm_kzalloc(dev, sizeof(struct asm9260_rtc_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &pdev->dev; + platform_set_drvdata(pdev, priv); + + irq_alarm = platform_get_irq(pdev, 0); + if (irq_alarm < 0) + return irq_alarm; + + priv->iobase = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->iobase)) + return PTR_ERR(priv->iobase); + + priv->clk = devm_clk_get(dev, "ahb"); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + + ret = clk_prepare_enable(priv->clk); + if (ret) { + dev_err(dev, "Failed to enable clk!\n"); + return ret; + } + + ccr = ioread32(priv->iobase + HW_CCR); + /* if dev is not enabled, reset it */ + if ((ccr & (BM_CLKEN | BM_CTCRST)) != BM_CLKEN) { + iowrite32(BM_CTCRST, priv->iobase + HW_CCR); + ccr = 0; + } + + iowrite32(BM_CLKEN | ccr, priv->iobase + HW_CCR); + iowrite32(0, priv->iobase + HW_CIIR); + iowrite32(BM_AMR_OFF, priv->iobase + HW_AMR); + + priv->rtc = devm_rtc_device_register(dev, dev_name(dev), + &asm9260_rtc_ops, THIS_MODULE); + if (IS_ERR(priv->rtc)) { + ret = PTR_ERR(priv->rtc); + dev_err(dev, "Failed to register RTC device: %d\n", ret); + goto err_return; + } + + ret = devm_request_threaded_irq(dev, irq_alarm, NULL, + asm9260_rtc_irq, IRQF_ONESHOT, + dev_name(dev), priv); + if (ret < 0) { + dev_err(dev, "can't get irq %i, err %d\n", + irq_alarm, ret); + goto err_return; + } + + return 0; + +err_return: + clk_disable_unprepare(priv->clk); + return ret; +} + +static void asm9260_rtc_remove(struct platform_device *pdev) +{ + struct asm9260_rtc_priv *priv = platform_get_drvdata(pdev); + + /* Disable alarm matching */ + iowrite32(BM_AMR_OFF, priv->iobase + HW_AMR); + clk_disable_unprepare(priv->clk); +} + +static const struct of_device_id asm9260_dt_ids[] = { + { .compatible = "alphascale,asm9260-rtc", }, + {} +}; +MODULE_DEVICE_TABLE(of, asm9260_dt_ids); + +static struct platform_driver asm9260_rtc_driver = { + .probe = asm9260_rtc_probe, + .remove = asm9260_rtc_remove, + .driver = { + .name = "asm9260-rtc", + .of_match_table = asm9260_dt_ids, + }, +}; + +module_platform_driver(asm9260_rtc_driver); + +MODULE_AUTHOR("Oleksij Rempel "); +MODULE_DESCRIPTION("Alphascale asm9260 SoC Realtime Clock Driver (RTC)"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-aspeed.c b/drivers/rtc/rtc-aspeed.c new file mode 100644 index 000000000..8f5b440f8 --- /dev/null +++ b/drivers/rtc/rtc-aspeed.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright 2015 IBM Corp. + +#include +#include +#include +#include +#include + +struct aspeed_rtc { + void __iomem *base; +}; + +#define RTC_TIME 0x00 +#define RTC_YEAR 0x04 +#define RTC_CTRL 0x10 + +#define RTC_UNLOCK BIT(1) +#define RTC_ENABLE BIT(0) + +static int aspeed_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct aspeed_rtc *rtc = dev_get_drvdata(dev); + unsigned int cent, year; + u32 reg1, reg2; + + if (!(readl(rtc->base + RTC_CTRL) & RTC_ENABLE)) { + dev_dbg(dev, "%s failing as rtc disabled\n", __func__); + return -EINVAL; + } + + do { + reg2 = readl(rtc->base + RTC_YEAR); + reg1 = readl(rtc->base + RTC_TIME); + } while (reg2 != readl(rtc->base + RTC_YEAR)); + + tm->tm_mday = (reg1 >> 24) & 0x1f; + tm->tm_hour = (reg1 >> 16) & 0x1f; + tm->tm_min = (reg1 >> 8) & 0x3f; + tm->tm_sec = (reg1 >> 0) & 0x3f; + + cent = (reg2 >> 16) & 0x1f; + year = (reg2 >> 8) & 0x7f; + tm->tm_mon = ((reg2 >> 0) & 0x0f) - 1; + tm->tm_year = year + (cent * 100) - 1900; + + dev_dbg(dev, "%s %ptR", __func__, tm); + + return 0; +} + +static int aspeed_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct aspeed_rtc *rtc = dev_get_drvdata(dev); + u32 reg1, reg2, ctrl; + int year, cent; + + cent = (tm->tm_year + 1900) / 100; + year = tm->tm_year % 100; + + reg1 = (tm->tm_mday << 24) | (tm->tm_hour << 16) | (tm->tm_min << 8) | + tm->tm_sec; + + reg2 = ((cent & 0x1f) << 16) | ((year & 0x7f) << 8) | + ((tm->tm_mon + 1) & 0xf); + + ctrl = readl(rtc->base + RTC_CTRL); + writel(ctrl | RTC_UNLOCK, rtc->base + RTC_CTRL); + + writel(reg1, rtc->base + RTC_TIME); + writel(reg2, rtc->base + RTC_YEAR); + + /* Re-lock and ensure enable is set now that a time is programmed */ + writel(ctrl | RTC_ENABLE, rtc->base + RTC_CTRL); + + return 0; +} + +static const struct rtc_class_ops aspeed_rtc_ops = { + .read_time = aspeed_rtc_read_time, + .set_time = aspeed_rtc_set_time, +}; + +static int aspeed_rtc_probe(struct platform_device *pdev) +{ + struct aspeed_rtc *rtc; + struct rtc_device *rtc_dev; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rtc->base)) + return PTR_ERR(rtc->base); + + rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc_dev)) + return PTR_ERR(rtc_dev); + + platform_set_drvdata(pdev, rtc); + + rtc_dev->ops = &aspeed_rtc_ops; + rtc_dev->range_min = RTC_TIMESTAMP_BEGIN_1900; + rtc_dev->range_max = 38814989399LL; /* 3199-12-31 23:59:59 */ + + return devm_rtc_register_device(rtc_dev); +} + +static const struct of_device_id aspeed_rtc_match[] = { + { .compatible = "aspeed,ast2400-rtc", }, + { .compatible = "aspeed,ast2500-rtc", }, + { .compatible = "aspeed,ast2600-rtc", }, + { .compatible = "aspeed,ast2700-rtc", }, + {} +}; +MODULE_DEVICE_TABLE(of, aspeed_rtc_match); + +static struct platform_driver aspeed_rtc_driver = { + .driver = { + .name = "aspeed-rtc", + .of_match_table = aspeed_rtc_match, + }, +}; + +module_platform_driver_probe(aspeed_rtc_driver, aspeed_rtc_probe); + +MODULE_DESCRIPTION("ASPEED RTC driver"); +MODULE_AUTHOR("Joel Stanley "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c new file mode 100644 index 000000000..643734dba --- /dev/null +++ b/drivers/rtc/rtc-at91rm9200.c @@ -0,0 +1,656 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Real Time Clock interface for Linux on Atmel AT91RM9200 + * + * Copyright (C) 2002 Rick Bronson + * + * Converted to RTC class model by Andrew Victor + * + * Ported to Linux 2.6 by Steven Scholz + * Based on s3c2410-rtc.c Simtec Electronics + * + * Based on sa1100-rtc.c by Nils Faerber + * Based on rtc.c by Paul Gortmaker + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AT91_RTC_CR 0x00 /* Control Register */ +#define AT91_RTC_UPDTIM BIT(0) /* Update Request Time Register */ +#define AT91_RTC_UPDCAL BIT(1) /* Update Request Calendar Register */ + +#define AT91_RTC_MR 0x04 /* Mode Register */ +#define AT91_RTC_HRMOD BIT(0) /* 12/24 hour mode */ +#define AT91_RTC_NEGPPM BIT(4) /* Negative PPM correction */ +#define AT91_RTC_CORRECTION GENMASK(14, 8) /* Slow clock correction */ +#define AT91_RTC_HIGHPPM BIT(15) /* High PPM correction */ + +#define AT91_RTC_TIMR 0x08 /* Time Register */ +#define AT91_RTC_SEC GENMASK(6, 0) /* Current Second */ +#define AT91_RTC_MIN GENMASK(14, 8) /* Current Minute */ +#define AT91_RTC_HOUR GENMASK(21, 16) /* Current Hour */ +#define AT91_RTC_AMPM BIT(22) /* Ante Meridiem Post Meridiem Indicator */ + +#define AT91_RTC_CALR 0x0c /* Calendar Register */ +#define AT91_RTC_CENT GENMASK(6, 0) /* Current Century */ +#define AT91_RTC_YEAR GENMASK(15, 8) /* Current Year */ +#define AT91_RTC_MONTH GENMASK(20, 16) /* Current Month */ +#define AT91_RTC_DAY GENMASK(23, 21) /* Current Day */ +#define AT91_RTC_DATE GENMASK(29, 24) /* Current Date */ + +#define AT91_RTC_TIMALR 0x10 /* Time Alarm Register */ +#define AT91_RTC_SECEN BIT(7) /* Second Alarm Enable */ +#define AT91_RTC_MINEN BIT(15) /* Minute Alarm Enable */ +#define AT91_RTC_HOUREN BIT(23) /* Hour Alarm Enable */ + +#define AT91_RTC_CALALR 0x14 /* Calendar Alarm Register */ +#define AT91_RTC_MTHEN BIT(23) /* Month Alarm Enable */ +#define AT91_RTC_DATEEN BIT(31) /* Date Alarm Enable */ + +#define AT91_RTC_SR 0x18 /* Status Register */ +#define AT91_RTC_ACKUPD BIT(0) /* Acknowledge for Update */ +#define AT91_RTC_ALARM BIT(1) /* Alarm Flag */ +#define AT91_RTC_SECEV BIT(2) /* Second Event */ +#define AT91_RTC_TIMEV BIT(3) /* Time Event */ +#define AT91_RTC_CALEV BIT(4) /* Calendar Event */ + +#define AT91_RTC_SCCR 0x1c /* Status Clear Command Register */ +#define AT91_RTC_IER 0x20 /* Interrupt Enable Register */ +#define AT91_RTC_IDR 0x24 /* Interrupt Disable Register */ +#define AT91_RTC_IMR 0x28 /* Interrupt Mask Register */ + +#define AT91_RTC_VER 0x2c /* Valid Entry Register */ +#define AT91_RTC_NVTIM BIT(0) /* Non valid Time */ +#define AT91_RTC_NVCAL BIT(1) /* Non valid Calendar */ +#define AT91_RTC_NVTIMALR BIT(2) /* Non valid Time Alarm */ +#define AT91_RTC_NVCALALR BIT(3) /* Non valid Calendar Alarm */ + +#define AT91_RTC_CORR_DIVIDEND 3906000 +#define AT91_RTC_CORR_LOW_RATIO 20 + +#define at91_rtc_read(field) \ + readl_relaxed(at91_rtc_regs + field) +#define at91_rtc_write(field, val) \ + writel_relaxed((val), at91_rtc_regs + field) + +struct at91_rtc_config { + bool use_shadow_imr; + bool has_correction; +}; + +static const struct at91_rtc_config *at91_rtc_config; +static DECLARE_COMPLETION(at91_rtc_updated); +static DECLARE_COMPLETION(at91_rtc_upd_rdy); +static void __iomem *at91_rtc_regs; +static int irq; +static DEFINE_SPINLOCK(at91_rtc_lock); +static u32 at91_rtc_shadow_imr; +static bool suspended; +static DEFINE_SPINLOCK(suspended_lock); +static unsigned long cached_events; +static u32 at91_rtc_imr; +static struct clk *sclk; + +static void at91_rtc_write_ier(u32 mask) +{ + unsigned long flags; + + spin_lock_irqsave(&at91_rtc_lock, flags); + at91_rtc_shadow_imr |= mask; + at91_rtc_write(AT91_RTC_IER, mask); + spin_unlock_irqrestore(&at91_rtc_lock, flags); +} + +static void at91_rtc_write_idr(u32 mask) +{ + unsigned long flags; + + spin_lock_irqsave(&at91_rtc_lock, flags); + at91_rtc_write(AT91_RTC_IDR, mask); + /* + * Register read back (of any RTC-register) needed to make sure + * IDR-register write has reached the peripheral before updating + * shadow mask. + * + * Note that there is still a possibility that the mask is updated + * before interrupts have actually been disabled in hardware. The only + * way to be certain would be to poll the IMR-register, which is + * the very register we are trying to emulate. The register read back + * is a reasonable heuristic. + */ + at91_rtc_read(AT91_RTC_SR); + at91_rtc_shadow_imr &= ~mask; + spin_unlock_irqrestore(&at91_rtc_lock, flags); +} + +static u32 at91_rtc_read_imr(void) +{ + unsigned long flags; + u32 mask; + + if (at91_rtc_config->use_shadow_imr) { + spin_lock_irqsave(&at91_rtc_lock, flags); + mask = at91_rtc_shadow_imr; + spin_unlock_irqrestore(&at91_rtc_lock, flags); + } else { + mask = at91_rtc_read(AT91_RTC_IMR); + } + + return mask; +} + +/* + * Decode time/date into rtc_time structure + */ +static void at91_rtc_decodetime(unsigned int timereg, unsigned int calreg, + struct rtc_time *tm) +{ + unsigned int time, date; + + /* must read twice in case it changes */ + do { + time = at91_rtc_read(timereg); + date = at91_rtc_read(calreg); + } while ((time != at91_rtc_read(timereg)) || + (date != at91_rtc_read(calreg))); + + tm->tm_sec = bcd2bin(FIELD_GET(AT91_RTC_SEC, time)); + tm->tm_min = bcd2bin(FIELD_GET(AT91_RTC_MIN, time)); + tm->tm_hour = bcd2bin(FIELD_GET(AT91_RTC_HOUR, time)); + + /* + * The Calendar Alarm register does not have a field for + * the year - so these will return an invalid value. + */ + tm->tm_year = bcd2bin(date & AT91_RTC_CENT) * 100; /* century */ + tm->tm_year += bcd2bin(FIELD_GET(AT91_RTC_YEAR, date)); /* year */ + + tm->tm_wday = bcd2bin(FIELD_GET(AT91_RTC_DAY, date)) - 1; /* day of the week [0-6], Sunday=0 */ + tm->tm_mon = bcd2bin(FIELD_GET(AT91_RTC_MONTH, date)) - 1; + tm->tm_mday = bcd2bin(FIELD_GET(AT91_RTC_DATE, date)); +} + +/* + * Read current time and date in RTC + */ +static int at91_rtc_readtime(struct device *dev, struct rtc_time *tm) +{ + at91_rtc_decodetime(AT91_RTC_TIMR, AT91_RTC_CALR, tm); + tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year); + tm->tm_year = tm->tm_year - 1900; + + dev_dbg(dev, "%s(): %ptR\n", __func__, tm); + + return 0; +} + +/* + * Set current time and date in RTC + */ +static int at91_rtc_settime(struct device *dev, struct rtc_time *tm) +{ + unsigned long cr; + + dev_dbg(dev, "%s(): %ptR\n", __func__, tm); + + wait_for_completion(&at91_rtc_upd_rdy); + + /* Stop Time/Calendar from counting */ + cr = at91_rtc_read(AT91_RTC_CR); + at91_rtc_write(AT91_RTC_CR, cr | AT91_RTC_UPDCAL | AT91_RTC_UPDTIM); + + at91_rtc_write_ier(AT91_RTC_ACKUPD); + wait_for_completion(&at91_rtc_updated); /* wait for ACKUPD interrupt */ + at91_rtc_write_idr(AT91_RTC_ACKUPD); + + at91_rtc_write(AT91_RTC_TIMR, + FIELD_PREP(AT91_RTC_SEC, bin2bcd(tm->tm_sec)) + | FIELD_PREP(AT91_RTC_MIN, bin2bcd(tm->tm_min)) + | FIELD_PREP(AT91_RTC_HOUR, bin2bcd(tm->tm_hour))); + + at91_rtc_write(AT91_RTC_CALR, + FIELD_PREP(AT91_RTC_CENT, + bin2bcd((tm->tm_year + 1900) / 100)) + | FIELD_PREP(AT91_RTC_YEAR, bin2bcd(tm->tm_year % 100)) + | FIELD_PREP(AT91_RTC_MONTH, bin2bcd(tm->tm_mon + 1)) + | FIELD_PREP(AT91_RTC_DAY, bin2bcd(tm->tm_wday + 1)) + | FIELD_PREP(AT91_RTC_DATE, bin2bcd(tm->tm_mday))); + + /* Restart Time/Calendar */ + cr = at91_rtc_read(AT91_RTC_CR); + at91_rtc_write(AT91_RTC_SCCR, AT91_RTC_SECEV); + at91_rtc_write(AT91_RTC_CR, cr & ~(AT91_RTC_UPDCAL | AT91_RTC_UPDTIM)); + at91_rtc_write_ier(AT91_RTC_SECEV); + + return 0; +} + +/* + * Read alarm time and date in RTC + */ +static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rtc_time *tm = &alrm->time; + + at91_rtc_decodetime(AT91_RTC_TIMALR, AT91_RTC_CALALR, tm); + tm->tm_year = -1; + + alrm->enabled = (at91_rtc_read_imr() & AT91_RTC_ALARM) + ? 1 : 0; + + dev_dbg(dev, "%s(): %ptR %sabled\n", __func__, tm, + alrm->enabled ? "en" : "dis"); + + return 0; +} + +/* + * Set alarm time and date in RTC + */ +static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rtc_time tm = alrm->time; + + at91_rtc_write_idr(AT91_RTC_ALARM); + at91_rtc_write(AT91_RTC_TIMALR, + FIELD_PREP(AT91_RTC_SEC, bin2bcd(alrm->time.tm_sec)) + | FIELD_PREP(AT91_RTC_MIN, bin2bcd(alrm->time.tm_min)) + | FIELD_PREP(AT91_RTC_HOUR, bin2bcd(alrm->time.tm_hour)) + | AT91_RTC_HOUREN | AT91_RTC_MINEN | AT91_RTC_SECEN); + at91_rtc_write(AT91_RTC_CALALR, + FIELD_PREP(AT91_RTC_MONTH, bin2bcd(alrm->time.tm_mon + 1)) + | FIELD_PREP(AT91_RTC_DATE, bin2bcd(alrm->time.tm_mday)) + | AT91_RTC_DATEEN | AT91_RTC_MTHEN); + + if (alrm->enabled) { + at91_rtc_write(AT91_RTC_SCCR, AT91_RTC_ALARM); + at91_rtc_write_ier(AT91_RTC_ALARM); + } + + dev_dbg(dev, "%s(): %ptR\n", __func__, &tm); + + return 0; +} + +static int at91_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + dev_dbg(dev, "%s(): cmd=%08x\n", __func__, enabled); + + if (enabled) { + at91_rtc_write(AT91_RTC_SCCR, AT91_RTC_ALARM); + at91_rtc_write_ier(AT91_RTC_ALARM); + } else + at91_rtc_write_idr(AT91_RTC_ALARM); + + return 0; +} + +static int at91_rtc_readoffset(struct device *dev, long *offset) +{ + u32 mr = at91_rtc_read(AT91_RTC_MR); + long val = FIELD_GET(AT91_RTC_CORRECTION, mr); + + if (!val) { + *offset = 0; + return 0; + } + + val++; + + if (!(mr & AT91_RTC_NEGPPM)) + val = -val; + + if (!(mr & AT91_RTC_HIGHPPM)) + val *= AT91_RTC_CORR_LOW_RATIO; + + *offset = DIV_ROUND_CLOSEST(AT91_RTC_CORR_DIVIDEND, val); + + return 0; +} + +static int at91_rtc_setoffset(struct device *dev, long offset) +{ + long corr; + u32 mr; + + if (offset > AT91_RTC_CORR_DIVIDEND / 2) + return -ERANGE; + if (offset < -AT91_RTC_CORR_DIVIDEND / 2) + return -ERANGE; + + mr = at91_rtc_read(AT91_RTC_MR); + mr &= ~(AT91_RTC_NEGPPM | AT91_RTC_CORRECTION | AT91_RTC_HIGHPPM); + + if (offset > 0) + mr |= AT91_RTC_NEGPPM; + else + offset = -offset; + + /* offset less than 764 ppb, disable correction*/ + if (offset < 764) { + at91_rtc_write(AT91_RTC_MR, mr & ~AT91_RTC_NEGPPM); + + return 0; + } + + /* + * 29208 ppb is the perfect cutoff between low range and high range + * low range values are never better than high range value after that. + */ + if (offset < 29208) { + corr = DIV_ROUND_CLOSEST(AT91_RTC_CORR_DIVIDEND, offset * AT91_RTC_CORR_LOW_RATIO); + } else { + corr = DIV_ROUND_CLOSEST(AT91_RTC_CORR_DIVIDEND, offset); + mr |= AT91_RTC_HIGHPPM; + } + + if (corr > 128) + corr = 128; + + mr |= FIELD_PREP(AT91_RTC_CORRECTION, corr - 1); + + at91_rtc_write(AT91_RTC_MR, mr); + + return 0; +} + +/* + * IRQ handler for the RTC + */ +static irqreturn_t at91_rtc_interrupt(int irq, void *dev_id) +{ + struct platform_device *pdev = dev_id; + struct rtc_device *rtc = platform_get_drvdata(pdev); + unsigned int rtsr; + unsigned long events = 0; + int ret = IRQ_NONE; + + spin_lock(&suspended_lock); + rtsr = at91_rtc_read(AT91_RTC_SR) & at91_rtc_read_imr(); + if (rtsr) { /* this interrupt is shared! Is it ours? */ + if (rtsr & AT91_RTC_ALARM) + events |= (RTC_AF | RTC_IRQF); + if (rtsr & AT91_RTC_SECEV) { + complete(&at91_rtc_upd_rdy); + at91_rtc_write_idr(AT91_RTC_SECEV); + } + if (rtsr & AT91_RTC_ACKUPD) + complete(&at91_rtc_updated); + + at91_rtc_write(AT91_RTC_SCCR, rtsr); /* clear status reg */ + + if (!suspended) { + rtc_update_irq(rtc, 1, events); + + dev_dbg(&pdev->dev, "%s(): num=%ld, events=0x%02lx\n", + __func__, events >> 8, events & 0x000000FF); + } else { + cached_events |= events; + at91_rtc_write_idr(at91_rtc_imr); + pm_system_wakeup(); + } + + ret = IRQ_HANDLED; + } + spin_unlock(&suspended_lock); + + return ret; +} + +static const struct at91_rtc_config at91rm9200_config = { +}; + +static const struct at91_rtc_config at91sam9x5_config = { + .use_shadow_imr = true, +}; + +static const struct at91_rtc_config sama5d4_config = { + .has_correction = true, +}; + +static const struct of_device_id at91_rtc_dt_ids[] = { + { + .compatible = "atmel,at91rm9200-rtc", + .data = &at91rm9200_config, + }, { + .compatible = "atmel,at91sam9x5-rtc", + .data = &at91sam9x5_config, + }, { + .compatible = "atmel,sama5d4-rtc", + .data = &sama5d4_config, + }, { + .compatible = "atmel,sama5d2-rtc", + .data = &sama5d4_config, + }, { + .compatible = "microchip,sam9x60-rtc", + .data = &sama5d4_config, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, at91_rtc_dt_ids); + +static const struct rtc_class_ops at91_rtc_ops = { + .read_time = at91_rtc_readtime, + .set_time = at91_rtc_settime, + .read_alarm = at91_rtc_readalarm, + .set_alarm = at91_rtc_setalarm, + .alarm_irq_enable = at91_rtc_alarm_irq_enable, +}; + +static const struct rtc_class_ops sama5d4_rtc_ops = { + .read_time = at91_rtc_readtime, + .set_time = at91_rtc_settime, + .read_alarm = at91_rtc_readalarm, + .set_alarm = at91_rtc_setalarm, + .alarm_irq_enable = at91_rtc_alarm_irq_enable, + .set_offset = at91_rtc_setoffset, + .read_offset = at91_rtc_readoffset, +}; + +/* + * Initialize and install RTC driver + */ +static int __init at91_rtc_probe(struct platform_device *pdev) +{ + struct rtc_device *rtc; + struct resource *regs; + int ret = 0; + + at91_rtc_config = of_device_get_match_data(&pdev->dev); + if (!at91_rtc_config) + return -ENODEV; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_err(&pdev->dev, "no mmio resource defined\n"); + return -ENXIO; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return -ENXIO; + + at91_rtc_regs = devm_ioremap(&pdev->dev, regs->start, + resource_size(regs)); + if (!at91_rtc_regs) { + dev_err(&pdev->dev, "failed to map registers, aborting.\n"); + return -ENOMEM; + } + + rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + platform_set_drvdata(pdev, rtc); + + sclk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(sclk)) + return PTR_ERR(sclk); + + ret = clk_prepare_enable(sclk); + if (ret) { + dev_err(&pdev->dev, "Could not enable slow clock\n"); + return ret; + } + + at91_rtc_write(AT91_RTC_CR, 0); + at91_rtc_write(AT91_RTC_MR, at91_rtc_read(AT91_RTC_MR) & ~AT91_RTC_HRMOD); + + /* Disable all interrupts */ + at91_rtc_write_idr(AT91_RTC_ACKUPD | AT91_RTC_ALARM | + AT91_RTC_SECEV | AT91_RTC_TIMEV | + AT91_RTC_CALEV); + + ret = devm_request_irq(&pdev->dev, irq, at91_rtc_interrupt, + IRQF_SHARED | IRQF_COND_SUSPEND, + "at91_rtc", pdev); + if (ret) { + dev_err(&pdev->dev, "IRQ %d already in use.\n", irq); + goto err_clk; + } + + /* cpu init code should really have flagged this device as + * being wake-capable; if it didn't, do that here. + */ + if (!device_can_wakeup(&pdev->dev)) + device_init_wakeup(&pdev->dev, true); + + if (at91_rtc_config->has_correction) + rtc->ops = &sama5d4_rtc_ops; + else + rtc->ops = &at91_rtc_ops; + + rtc->range_min = RTC_TIMESTAMP_BEGIN_1900; + rtc->range_max = RTC_TIMESTAMP_END_2099; + ret = devm_rtc_register_device(rtc); + if (ret) + goto err_clk; + + /* enable SECEV interrupt in order to initialize at91_rtc_upd_rdy + * completion. + */ + at91_rtc_write_ier(AT91_RTC_SECEV); + + dev_info(&pdev->dev, "AT91 Real Time Clock driver.\n"); + return 0; + +err_clk: + clk_disable_unprepare(sclk); + + return ret; +} + +/* + * Disable and remove the RTC driver + */ +static void __exit at91_rtc_remove(struct platform_device *pdev) +{ + /* Disable all interrupts */ + at91_rtc_write_idr(AT91_RTC_ACKUPD | AT91_RTC_ALARM | + AT91_RTC_SECEV | AT91_RTC_TIMEV | + AT91_RTC_CALEV); + + clk_disable_unprepare(sclk); +} + +static void at91_rtc_shutdown(struct platform_device *pdev) +{ + /* Disable all interrupts */ + at91_rtc_write(AT91_RTC_IDR, AT91_RTC_ACKUPD | AT91_RTC_ALARM | + AT91_RTC_SECEV | AT91_RTC_TIMEV | + AT91_RTC_CALEV); +} + +#ifdef CONFIG_PM_SLEEP + +/* AT91RM9200 RTC Power management control */ + +static int at91_rtc_suspend(struct device *dev) +{ + /* this IRQ is shared with DBGU and other hardware which isn't + * necessarily doing PM like we are... + */ + at91_rtc_write(AT91_RTC_SCCR, AT91_RTC_ALARM); + + at91_rtc_imr = at91_rtc_read_imr() + & (AT91_RTC_ALARM|AT91_RTC_SECEV); + if (at91_rtc_imr) { + if (device_may_wakeup(dev)) { + unsigned long flags; + + enable_irq_wake(irq); + + spin_lock_irqsave(&suspended_lock, flags); + suspended = true; + spin_unlock_irqrestore(&suspended_lock, flags); + } else { + at91_rtc_write_idr(at91_rtc_imr); + } + } + return 0; +} + +static int at91_rtc_resume(struct device *dev) +{ + struct rtc_device *rtc = dev_get_drvdata(dev); + + if (at91_rtc_imr) { + if (device_may_wakeup(dev)) { + unsigned long flags; + + spin_lock_irqsave(&suspended_lock, flags); + + if (cached_events) { + rtc_update_irq(rtc, 1, cached_events); + cached_events = 0; + } + + suspended = false; + spin_unlock_irqrestore(&suspended_lock, flags); + + disable_irq_wake(irq); + } + at91_rtc_write_ier(at91_rtc_imr); + } + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(at91_rtc_pm_ops, at91_rtc_suspend, at91_rtc_resume); + +/* + * at91_rtc_remove() lives in .exit.text. For drivers registered via + * module_platform_driver_probe() this is ok because they cannot get unbound at + * runtime. So mark the driver struct with __refdata to prevent modpost + * triggering a section mismatch warning. + */ +static struct platform_driver at91_rtc_driver __refdata = { + .remove = __exit_p(at91_rtc_remove), + .shutdown = at91_rtc_shutdown, + .driver = { + .name = "at91_rtc", + .pm = &at91_rtc_pm_ops, + .of_match_table = at91_rtc_dt_ids, + }, +}; + +module_platform_driver_probe(at91_rtc_driver, at91_rtc_probe); + +MODULE_AUTHOR("Rick Bronson"); +MODULE_DESCRIPTION("RTC driver for Atmel AT91RM9200"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c new file mode 100644 index 000000000..38991cca5 --- /dev/null +++ b/drivers/rtc/rtc-at91sam9.c @@ -0,0 +1,546 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * "RTT as Real Time Clock" driver for AT91SAM9 SoC family + * + * (C) 2007 Michel Benoit + * + * Based on rtc-at91rm9200.c by Rick Bronson + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This driver uses two configurable hardware resources that live in the + * AT91SAM9 backup power domain (intended to be powered at all times) + * to implement the Real Time Clock interfaces + * + * - A "Real-time Timer" (RTT) counts up in seconds from a base time. + * We can't assign the counter value (CRTV) ... but we can reset it. + * + * - One of the "General Purpose Backup Registers" (GPBRs) holds the + * base time, normally an offset from the beginning of the POSIX + * epoch (1970-Jan-1 00:00:00 UTC). Some systems also include the + * local timezone's offset. + * + * The RTC's value is the RTT counter plus that offset. The RTC's alarm + * is likewise a base (ALMV) plus that offset. + * + * Not all RTTs will be used as RTCs; some systems have multiple RTTs to + * choose from, or a "real" RTC module. All systems have multiple GPBR + * registers available, likewise usable for more than "RTC" support. + */ + +#define AT91_RTT_MR 0x00 /* Real-time Mode Register */ +#define AT91_RTT_RTPRES (0xffff << 0) /* Timer Prescaler Value */ +#define AT91_RTT_ALMIEN BIT(16) /* Alarm Interrupt Enable */ +#define AT91_RTT_RTTINCIEN BIT(17) /* Increment Interrupt Enable */ +#define AT91_RTT_RTTRST BIT(18) /* Timer Restart */ + +#define AT91_RTT_AR 0x04 /* Real-time Alarm Register */ +#define AT91_RTT_ALMV (0xffffffff) /* Alarm Value */ + +#define AT91_RTT_VR 0x08 /* Real-time Value Register */ +#define AT91_RTT_CRTV (0xffffffff) /* Current Real-time Value */ + +#define AT91_RTT_SR 0x0c /* Real-time Status Register */ +#define AT91_RTT_ALMS BIT(0) /* Alarm Status */ +#define AT91_RTT_RTTINC BIT(1) /* Timer Increment */ + +/* + * We store ALARM_DISABLED in ALMV to record that no alarm is set. + * It's also the reset value for that field. + */ +#define ALARM_DISABLED ((u32)~0) + +struct sam9_rtc { + void __iomem *rtt; + struct rtc_device *rtcdev; + u32 imr; + struct regmap *gpbr; + unsigned int gpbr_offset; + int irq; + struct clk *sclk; + bool suspended; + unsigned long events; + spinlock_t lock; +}; + +#define rtt_readl(rtc, field) \ + readl((rtc)->rtt + AT91_RTT_ ## field) +#define rtt_writel(rtc, field, val) \ + writel((val), (rtc)->rtt + AT91_RTT_ ## field) + +static inline unsigned int gpbr_readl(struct sam9_rtc *rtc) +{ + unsigned int val; + + regmap_read(rtc->gpbr, rtc->gpbr_offset, &val); + + return val; +} + +static inline void gpbr_writel(struct sam9_rtc *rtc, unsigned int val) +{ + regmap_write(rtc->gpbr, rtc->gpbr_offset, val); +} + +/* + * Read current time and date in RTC + */ +static int at91_rtc_readtime(struct device *dev, struct rtc_time *tm) +{ + struct sam9_rtc *rtc = dev_get_drvdata(dev); + u32 secs, secs2; + u32 offset; + + /* read current time offset */ + offset = gpbr_readl(rtc); + if (offset == 0) + return -EILSEQ; + + /* reread the counter to help sync the two clock domains */ + secs = rtt_readl(rtc, VR); + secs2 = rtt_readl(rtc, VR); + if (secs != secs2) + secs = rtt_readl(rtc, VR); + + rtc_time64_to_tm(offset + secs, tm); + + dev_dbg(dev, "%s: %ptR\n", __func__, tm); + + return 0; +} + +/* + * Set current time and date in RTC + */ +static int at91_rtc_settime(struct device *dev, struct rtc_time *tm) +{ + struct sam9_rtc *rtc = dev_get_drvdata(dev); + u32 offset, alarm, mr; + unsigned long secs; + + dev_dbg(dev, "%s: %ptR\n", __func__, tm); + + secs = rtc_tm_to_time64(tm); + + mr = rtt_readl(rtc, MR); + + /* disable interrupts */ + rtt_writel(rtc, MR, mr & ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN)); + + /* read current time offset */ + offset = gpbr_readl(rtc); + + /* store the new base time in a battery backup register */ + secs += 1; + gpbr_writel(rtc, secs); + + /* adjust the alarm time for the new base */ + alarm = rtt_readl(rtc, AR); + if (alarm != ALARM_DISABLED) { + if (offset > secs) { + /* time jumped backwards, increase time until alarm */ + alarm += (offset - secs); + } else if ((alarm + offset) > secs) { + /* time jumped forwards, decrease time until alarm */ + alarm -= (secs - offset); + } else { + /* time jumped past the alarm, disable alarm */ + alarm = ALARM_DISABLED; + mr &= ~AT91_RTT_ALMIEN; + } + rtt_writel(rtc, AR, alarm); + } + + /* reset the timer, and re-enable interrupts */ + rtt_writel(rtc, MR, mr | AT91_RTT_RTTRST); + + return 0; +} + +static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct sam9_rtc *rtc = dev_get_drvdata(dev); + struct rtc_time *tm = &alrm->time; + u32 alarm = rtt_readl(rtc, AR); + u32 offset; + + offset = gpbr_readl(rtc); + if (offset == 0) + return -EILSEQ; + + memset(alrm, 0, sizeof(*alrm)); + if (alarm != ALARM_DISABLED) { + rtc_time64_to_tm(offset + alarm, tm); + + dev_dbg(dev, "%s: %ptR\n", __func__, tm); + + if (rtt_readl(rtc, MR) & AT91_RTT_ALMIEN) + alrm->enabled = 1; + } + + return 0; +} + +static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct sam9_rtc *rtc = dev_get_drvdata(dev); + struct rtc_time *tm = &alrm->time; + unsigned long secs; + u32 offset; + u32 mr; + + secs = rtc_tm_to_time64(tm); + + offset = gpbr_readl(rtc); + if (offset == 0) { + /* time is not set */ + return -EILSEQ; + } + mr = rtt_readl(rtc, MR); + rtt_writel(rtc, MR, mr & ~AT91_RTT_ALMIEN); + + /* alarm in the past? finish and leave disabled */ + if (secs <= offset) { + rtt_writel(rtc, AR, ALARM_DISABLED); + return 0; + } + + /* else set alarm and maybe enable it */ + rtt_writel(rtc, AR, secs - offset); + if (alrm->enabled) + rtt_writel(rtc, MR, mr | AT91_RTT_ALMIEN); + + dev_dbg(dev, "%s: %ptR\n", __func__, tm); + + return 0; +} + +static int at91_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct sam9_rtc *rtc = dev_get_drvdata(dev); + u32 mr = rtt_readl(rtc, MR); + + dev_dbg(dev, "alarm_irq_enable: enabled=%08x, mr %08x\n", enabled, mr); + if (enabled) + rtt_writel(rtc, MR, mr | AT91_RTT_ALMIEN); + else + rtt_writel(rtc, MR, mr & ~AT91_RTT_ALMIEN); + return 0; +} + +/* + * Provide additional RTC information in /proc/driver/rtc + */ +static int at91_rtc_proc(struct device *dev, struct seq_file *seq) +{ + struct sam9_rtc *rtc = dev_get_drvdata(dev); + u32 mr = rtt_readl(rtc, MR); + + seq_printf(seq, "update_IRQ\t: %s\n", + (mr & AT91_RTT_RTTINCIEN) ? "yes" : "no"); + return 0; +} + +static irqreturn_t at91_rtc_cache_events(struct sam9_rtc *rtc) +{ + u32 sr, mr; + + /* Shared interrupt may be for another device. Note: reading + * SR clears it, so we must only read it in this irq handler! + */ + mr = rtt_readl(rtc, MR) & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN); + sr = rtt_readl(rtc, SR) & (mr >> 16); + if (!sr) + return IRQ_NONE; + + /* alarm status */ + if (sr & AT91_RTT_ALMS) + rtc->events |= (RTC_AF | RTC_IRQF); + + /* timer update/increment */ + if (sr & AT91_RTT_RTTINC) + rtc->events |= (RTC_UF | RTC_IRQF); + + return IRQ_HANDLED; +} + +static void at91_rtc_flush_events(struct sam9_rtc *rtc) +{ + if (!rtc->events) + return; + + rtc_update_irq(rtc->rtcdev, 1, rtc->events); + rtc->events = 0; + + pr_debug("%s: num=%ld, events=0x%02lx\n", __func__, + rtc->events >> 8, rtc->events & 0x000000FF); +} + +/* + * IRQ handler for the RTC + */ +static irqreturn_t at91_rtc_interrupt(int irq, void *_rtc) +{ + struct sam9_rtc *rtc = _rtc; + int ret; + + spin_lock(&rtc->lock); + + ret = at91_rtc_cache_events(rtc); + + /* We're called in suspended state */ + if (rtc->suspended) { + /* Mask irqs coming from this peripheral */ + rtt_writel(rtc, MR, + rtt_readl(rtc, MR) & + ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN)); + /* Trigger a system wakeup */ + pm_system_wakeup(); + } else { + at91_rtc_flush_events(rtc); + } + + spin_unlock(&rtc->lock); + + return ret; +} + +static const struct rtc_class_ops at91_rtc_ops = { + .read_time = at91_rtc_readtime, + .set_time = at91_rtc_settime, + .read_alarm = at91_rtc_readalarm, + .set_alarm = at91_rtc_setalarm, + .proc = at91_rtc_proc, + .alarm_irq_enable = at91_rtc_alarm_irq_enable, +}; + +/* + * Initialize and install RTC driver + */ +static int at91_rtc_probe(struct platform_device *pdev) +{ + struct sam9_rtc *rtc; + int ret, irq; + u32 mr; + unsigned int sclk_rate; + struct of_phandle_args args; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + spin_lock_init(&rtc->lock); + rtc->irq = irq; + + /* platform setup code should have handled this; sigh */ + if (!device_can_wakeup(&pdev->dev)) + device_init_wakeup(&pdev->dev, true); + + platform_set_drvdata(pdev, rtc); + + rtc->rtt = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rtc->rtt)) + return PTR_ERR(rtc->rtt); + + ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, + "atmel,rtt-rtc-time-reg", 1, 0, + &args); + if (ret) + return ret; + + rtc->gpbr = syscon_node_to_regmap(args.np); + of_node_put(args.np); + rtc->gpbr_offset = args.args[0]; + if (IS_ERR(rtc->gpbr)) { + dev_err(&pdev->dev, "failed to retrieve gpbr regmap, aborting.\n"); + return -ENOMEM; + } + + rtc->sclk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(rtc->sclk)) + return PTR_ERR(rtc->sclk); + + ret = clk_prepare_enable(rtc->sclk); + if (ret) { + dev_err(&pdev->dev, "Could not enable slow clock\n"); + return ret; + } + + sclk_rate = clk_get_rate(rtc->sclk); + if (!sclk_rate || sclk_rate > AT91_RTT_RTPRES) { + dev_err(&pdev->dev, "Invalid slow clock rate\n"); + ret = -EINVAL; + goto err_clk; + } + + mr = rtt_readl(rtc, MR); + + /* unless RTT is counting at 1 Hz, re-initialize it */ + if ((mr & AT91_RTT_RTPRES) != sclk_rate) { + mr = AT91_RTT_RTTRST | (sclk_rate & AT91_RTT_RTPRES); + gpbr_writel(rtc, 0); + } + + /* disable all interrupts (same as on shutdown path) */ + mr &= ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN); + rtt_writel(rtc, MR, mr); + + rtc->rtcdev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc->rtcdev)) { + ret = PTR_ERR(rtc->rtcdev); + goto err_clk; + } + + rtc->rtcdev->ops = &at91_rtc_ops; + rtc->rtcdev->range_max = U32_MAX; + + /* register irq handler after we know what name we'll use */ + ret = devm_request_irq(&pdev->dev, rtc->irq, at91_rtc_interrupt, + IRQF_SHARED | IRQF_COND_SUSPEND, + dev_name(&rtc->rtcdev->dev), rtc); + if (ret) { + dev_dbg(&pdev->dev, "can't share IRQ %d?\n", rtc->irq); + goto err_clk; + } + + /* NOTE: sam9260 rev A silicon has a ROM bug which resets the + * RTT on at least some reboots. If you have that chip, you must + * initialize the time from some external source like a GPS, wall + * clock, discrete RTC, etc + */ + + if (gpbr_readl(rtc) == 0) + dev_warn(&pdev->dev, "%s: SET TIME!\n", + dev_name(&rtc->rtcdev->dev)); + + return devm_rtc_register_device(rtc->rtcdev); + +err_clk: + clk_disable_unprepare(rtc->sclk); + + return ret; +} + +/* + * Disable and remove the RTC driver + */ +static void at91_rtc_remove(struct platform_device *pdev) +{ + struct sam9_rtc *rtc = platform_get_drvdata(pdev); + u32 mr = rtt_readl(rtc, MR); + + /* disable all interrupts */ + rtt_writel(rtc, MR, mr & ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN)); + + clk_disable_unprepare(rtc->sclk); +} + +static void at91_rtc_shutdown(struct platform_device *pdev) +{ + struct sam9_rtc *rtc = platform_get_drvdata(pdev); + u32 mr = rtt_readl(rtc, MR); + + rtc->imr = mr & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN); + rtt_writel(rtc, MR, mr & ~rtc->imr); +} + +#ifdef CONFIG_PM_SLEEP + +/* AT91SAM9 RTC Power management control */ + +static int at91_rtc_suspend(struct device *dev) +{ + struct sam9_rtc *rtc = dev_get_drvdata(dev); + u32 mr = rtt_readl(rtc, MR); + + /* + * This IRQ is shared with DBGU and other hardware which isn't + * necessarily a wakeup event source. + */ + rtc->imr = mr & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN); + if (rtc->imr) { + if (device_may_wakeup(dev) && (mr & AT91_RTT_ALMIEN)) { + unsigned long flags; + + enable_irq_wake(rtc->irq); + spin_lock_irqsave(&rtc->lock, flags); + rtc->suspended = true; + spin_unlock_irqrestore(&rtc->lock, flags); + /* don't let RTTINC cause wakeups */ + if (mr & AT91_RTT_RTTINCIEN) + rtt_writel(rtc, MR, mr & ~AT91_RTT_RTTINCIEN); + } else { + rtt_writel(rtc, MR, mr & ~rtc->imr); + } + } + + return 0; +} + +static int at91_rtc_resume(struct device *dev) +{ + struct sam9_rtc *rtc = dev_get_drvdata(dev); + u32 mr; + + if (rtc->imr) { + unsigned long flags; + + if (device_may_wakeup(dev)) + disable_irq_wake(rtc->irq); + mr = rtt_readl(rtc, MR); + rtt_writel(rtc, MR, mr | rtc->imr); + + spin_lock_irqsave(&rtc->lock, flags); + rtc->suspended = false; + at91_rtc_cache_events(rtc); + at91_rtc_flush_events(rtc); + spin_unlock_irqrestore(&rtc->lock, flags); + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(at91_rtc_pm_ops, at91_rtc_suspend, at91_rtc_resume); + +static const struct of_device_id at91_rtc_dt_ids[] = { + { .compatible = "atmel,at91sam9260-rtt" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, at91_rtc_dt_ids); + +static struct platform_driver at91_rtc_driver = { + .probe = at91_rtc_probe, + .remove = at91_rtc_remove, + .shutdown = at91_rtc_shutdown, + .driver = { + .name = "rtc-at91sam9", + .pm = &at91_rtc_pm_ops, + .of_match_table = at91_rtc_dt_ids, + }, +}; + +module_platform_driver(at91_rtc_driver); + +MODULE_AUTHOR("Michel Benoit"); +MODULE_DESCRIPTION("RTC driver for Atmel AT91SAM9x"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-atcrtc100.c b/drivers/rtc/rtc-atcrtc100.c new file mode 100644 index 000000000..9808fc2c5 --- /dev/null +++ b/drivers/rtc/rtc-atcrtc100.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Andes ATCRTC100 real time clock. + * + * Copyright (C) 2025 Andes Technology Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register Offsets */ +#define RTC_ID 0x00 /* ID and Revision Register */ +#define RTC_RSV 0x04 /* Reserved Register */ +#define RTC_CNT 0x10 /* Counter Register */ +#define RTC_ALM 0x14 /* Alarm Register */ +#define RTC_CR 0x18 /* Control Register */ +#define RTC_STA 0x1C /* Status Register */ +#define RTC_TRIM 0x20 /* Digital Trimming Register */ + +/* RTC_ID Register */ +#define ID_MSK GENMASK(31, 8) +#define ID_ATCRTC100 0x030110 + +/* RTC_CNT and RTC_ALM Register Fields */ +#define SEC_MSK GENMASK(5, 0) +#define MIN_MSK GENMASK(11, 6) +#define HOUR_MSK GENMASK(16, 12) +#define DAY_MSK GENMASK(31, 17) +#define RTC_SEC_GET(x) FIELD_GET(SEC_MSK, x) +#define RTC_MIN_GET(x) FIELD_GET(MIN_MSK, x) +#define RTC_HOUR_GET(x) FIELD_GET(HOUR_MSK, x) +#define RTC_DAY_GET(x) FIELD_GET(DAY_MSK, x) +#define RTC_SEC_SET(x) FIELD_PREP(SEC_MSK, x) +#define RTC_MIN_SET(x) FIELD_PREP(MIN_MSK, x) +#define RTC_HOUR_SET(x) FIELD_PREP(HOUR_MSK, x) +#define RTC_DAY_SET(x) FIELD_PREP(DAY_MSK, x) + +/* RTC_CR Register Bits */ +#define RTC_EN BIT(0) /* RTC Enable */ +#define ALARM_WAKEUP BIT(1) /* Alarm Wakeup Enable */ +#define ALARM_INT BIT(2) /* Alarm Interrupt Enable */ +#define DAY_INT BIT(3) /* Day Interrupt Enable */ +#define HOUR_INT BIT(4) /* Hour Interrupt Enable */ +#define MIN_INT BIT(5) /* Minute Interrupt Enable */ +#define SEC_INT BIT(6) /* Second Periodic Interrupt Enable */ +#define HSEC_INT BIT(7) /* Half-Second Periodic Interrupt Enable */ + +/* RTC_STA Register Bits */ +#define WRITE_DONE BIT(16) /* Register write completion status */ + +/* Time conversion macro */ +#define ATCRTC_TIME_TO_SEC(D, H, M, S) \ + ((time64_t)(D) * 86400 + (H) * 3600 + (M) * 60 + (S)) + +/* Timeout for waiting for the write_done bit */ +#define ATCRTC_TIMEOUT_US 1000000 +#define ATCRTC_TIMEOUT_USLEEP_MIN 20 +#define ATCRTC_TIMEOUT_USLEEP_MAX 30 + +struct atcrtc_dev { + struct rtc_device *rtc_dev; + struct regmap *regmap; + struct work_struct rtc_work; + unsigned int alarm_irq; + bool alarm_en; +}; + +static const struct regmap_config atcrtc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = RTC_TRIM, + .cache_type = REGCACHE_NONE, +}; + +/** + * atcrtc_check_write_done - Wait for RTC registers to be synchronized. + * @rtc: Pointer to the atcrtc_dev structure. + * + * The WriteDone bit in the status register indicates the synchronization + * progress of RTC register updates. This bit is cleared to zero whenever + * any RTC control register (Counter, Alarm, Control, etc.) is written. + * It returns to one only after all previous updates have been fully + * synchronized to the RTC clock domain. This function polls the WriteDone + * bit with a timeout to ensure the device is ready for the next operation. + * + * Return: 0 on success, or -EBUSY on timeout. + */ +static int atcrtc_check_write_done(struct atcrtc_dev *rtc) +{ + unsigned int val; + + /* + * Using read_poll_timeout is more efficient than a manual loop + * with usleep_range. + */ + return regmap_read_poll_timeout(rtc->regmap, RTC_STA, val, + val & WRITE_DONE, + ATCRTC_TIMEOUT_USLEEP_MIN, + ATCRTC_TIMEOUT_US); +} + +static irqreturn_t atcrtc_alarm_isr(int irq, void *dev) +{ + struct atcrtc_dev *rtc = dev; + unsigned int status; + + regmap_read(rtc->regmap, RTC_STA, &status); + if (status & ALARM_INT) { + regmap_write(rtc->regmap, RTC_STA, ALARM_INT); + rtc->alarm_en = false; + schedule_work(&rtc->rtc_work); + rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +static int atcrtc_alarm_irq_enable(struct device *dev, unsigned int enable) +{ + struct atcrtc_dev *rtc = dev_get_drvdata(dev); + unsigned int mask; + int ret; + + ret = atcrtc_check_write_done(rtc); + if (ret) + return ret; + + mask = ALARM_WAKEUP | ALARM_INT; + regmap_update_bits(rtc->regmap, RTC_CR, mask, enable ? mask : 0); + + return 0; +} + +static void atcrtc_alarm_clear(struct work_struct *work) +{ + struct atcrtc_dev *rtc = + container_of(work, struct atcrtc_dev, rtc_work); + int ret; + + rtc_lock(rtc->rtc_dev); + + if (!rtc->alarm_en) { + ret = atcrtc_check_write_done(rtc); + if (ret) + dev_info(&rtc->rtc_dev->dev, + "failed to sync before clearing alarm: %d\n", + ret); + else + regmap_update_bits(rtc->regmap, RTC_CR, + ALARM_WAKEUP | ALARM_INT, 0); + } + rtc_unlock(rtc->rtc_dev); +} + +static int atcrtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct atcrtc_dev *rtc = dev_get_drvdata(dev); + time64_t time; + unsigned int rtc_cnt; + + if (!regmap_test_bits(rtc->regmap, RTC_CR, RTC_EN)) + return -EIO; + + regmap_read(rtc->regmap, RTC_CNT, &rtc_cnt); + time = ATCRTC_TIME_TO_SEC(RTC_DAY_GET(rtc_cnt), + RTC_HOUR_GET(rtc_cnt), + RTC_MIN_GET(rtc_cnt), + RTC_SEC_GET(rtc_cnt)); + rtc_time64_to_tm(time, tm); + + return 0; +} + +static int atcrtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct atcrtc_dev *rtc = dev_get_drvdata(dev); + time64_t time; + unsigned int counter; + unsigned int day; + int ret; + + time = rtc_tm_to_time64(tm); + day = div_s64(time, 86400); + counter = RTC_DAY_SET(day) | + RTC_HOUR_SET(tm->tm_hour) | + RTC_MIN_SET(tm->tm_min) | + RTC_SEC_SET(tm->tm_sec); + ret = atcrtc_check_write_done(rtc); + if (ret) + return ret; + regmap_write(rtc->regmap, RTC_CNT, counter); + + ret = atcrtc_check_write_done(rtc); + if (ret) + return ret; + regmap_update_bits(rtc->regmap, RTC_CR, RTC_EN, RTC_EN); + + return 0; +} + +static int atcrtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct atcrtc_dev *rtc = dev_get_drvdata(dev); + struct rtc_time *tm = &wkalrm->time; + unsigned int rtc_alarm; + + wkalrm->enabled = regmap_test_bits(rtc->regmap, RTC_CR, ALARM_INT); + regmap_read(rtc->regmap, RTC_ALM, &rtc_alarm); + tm->tm_hour = RTC_HOUR_GET(rtc_alarm); + tm->tm_min = RTC_MIN_GET(rtc_alarm); + tm->tm_sec = RTC_SEC_GET(rtc_alarm); + + /* The RTC alarm does not support day/month/year fields */ + tm->tm_mday = -1; + tm->tm_mon = -1; + tm->tm_year = -1; + + return 0; +} + +static int atcrtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct atcrtc_dev *rtc = dev_get_drvdata(dev); + struct rtc_time *tm = &wkalrm->time; + unsigned int rtc_alarm; + int ret; + + /* Disable alarm first before setting a new one */ + ret = atcrtc_alarm_irq_enable(dev, 0); + if (ret) + return ret; + + rtc->alarm_en = false; + + rtc_alarm = RTC_SEC_SET(tm->tm_sec) | + RTC_MIN_SET(tm->tm_min) | + RTC_HOUR_SET(tm->tm_hour); + + ret = atcrtc_check_write_done(rtc); + if (ret) + return ret; + + regmap_write(rtc->regmap, RTC_ALM, rtc_alarm); + + rtc->alarm_en = wkalrm->enabled; + ret = atcrtc_alarm_irq_enable(dev, wkalrm->enabled); + + return ret; +} + +static const struct rtc_class_ops rtc_ops = { + .read_time = atcrtc_read_time, + .set_time = atcrtc_set_time, + .read_alarm = atcrtc_read_alarm, + .set_alarm = atcrtc_set_alarm, + .alarm_irq_enable = atcrtc_alarm_irq_enable, +}; + +static int atcrtc_probe(struct platform_device *pdev) +{ + struct atcrtc_dev *atcrtc_dev; + void __iomem *reg_base; + unsigned int rtc_id; + int ret; + + atcrtc_dev = devm_kzalloc(&pdev->dev, sizeof(*atcrtc_dev), GFP_KERNEL); + if (!atcrtc_dev) + return -ENOMEM; + platform_set_drvdata(pdev, atcrtc_dev); + + reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(reg_base)) + return dev_err_probe(&pdev->dev, PTR_ERR(reg_base), + "Failed to map I/O space\n"); + + atcrtc_dev->regmap = devm_regmap_init_mmio(&pdev->dev, + reg_base, + &atcrtc_regmap_config); + if (IS_ERR(atcrtc_dev->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(atcrtc_dev->regmap), + "Failed to initialize regmap\n"); + + regmap_read(atcrtc_dev->regmap, RTC_ID, &rtc_id); + if (FIELD_GET(ID_MSK, rtc_id) != ID_ATCRTC100) + return dev_err_probe(&pdev->dev, -ENODEV, + "Failed to initialize RTC: unsupported hardware ID 0x%x\n", + rtc_id); + + ret = platform_get_irq(pdev, 1); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Failed to get IRQ for alarm\n"); + atcrtc_dev->alarm_irq = ret; + + ret = devm_request_irq(&pdev->dev, + atcrtc_dev->alarm_irq, + atcrtc_alarm_isr, + 0, + "atcrtc_alarm", + atcrtc_dev); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to request IRQ %d for alarm\n", + atcrtc_dev->alarm_irq); + + atcrtc_dev->rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(atcrtc_dev->rtc_dev)) + return dev_err_probe(&pdev->dev, PTR_ERR(atcrtc_dev->rtc_dev), + "Failed to allocate RTC device\n"); + + set_bit(RTC_FEATURE_ALARM, atcrtc_dev->rtc_dev->features); + ret = device_init_wakeup(&pdev->dev, true); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to initialize wake capability\n"); + + ret = dev_pm_set_wake_irq(&pdev->dev, atcrtc_dev->alarm_irq); + if (ret) { + device_init_wakeup(&pdev->dev, false); + return dev_err_probe(&pdev->dev, ret, + "Failed to set wake IRQ\n"); + } + + atcrtc_dev->rtc_dev->ops = &rtc_ops; + + INIT_WORK(&atcrtc_dev->rtc_work, atcrtc_alarm_clear); + return devm_rtc_register_device(atcrtc_dev->rtc_dev); +} + +static int atcrtc_resume(struct device *dev) +{ + struct atcrtc_dev *rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(rtc->alarm_irq); + + return 0; +} + +static int atcrtc_suspend(struct device *dev) +{ + struct atcrtc_dev *rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(rtc->alarm_irq); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(atcrtc_pm_ops, atcrtc_suspend, atcrtc_resume); + +static const struct of_device_id atcrtc_dt_match[] = { + { .compatible = "andestech,atcrtc100" }, + { }, +}; +MODULE_DEVICE_TABLE(of, atcrtc_dt_match); + +static struct platform_driver atcrtc_platform_driver = { + .driver = { + .name = "atcrtc100", + .of_match_table = atcrtc_dt_match, + .pm = pm_sleep_ptr(&atcrtc_pm_ops), + }, + .probe = atcrtc_probe, +}; + +module_platform_driver(atcrtc_platform_driver); + +MODULE_AUTHOR("CL Wang "); +MODULE_DESCRIPTION("Andes ATCRTC100 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-au1xxx.c b/drivers/rtc/rtc-au1xxx.c new file mode 100644 index 000000000..630ea5de6 --- /dev/null +++ b/drivers/rtc/rtc-au1xxx.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Au1xxx counter0 (aka Time-Of-Year counter) RTC interface driver. + * + * Copyright (C) 2008 Manuel Lauss + */ + +/* All current Au1xxx SoCs have 2 counters fed by an external 32.768 kHz + * crystal. Counter 0, which keeps counting during sleep/powerdown, is + * used to count seconds since the beginning of the unix epoch. + * + * The counters must be configured and enabled by bootloader/board code; + * no checks as to whether they really get a proper 32.768kHz clock are + * made as this would take far too long. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* 32kHz clock enabled and detected */ +#define CNTR_OK (SYS_CNTRL_E0 | SYS_CNTRL_32S) + +static int au1xtoy_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + unsigned long t; + + t = alchemy_rdsys(AU1000_SYS_TOYREAD); + + rtc_time64_to_tm(t, tm); + + return 0; +} + +static int au1xtoy_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + unsigned long t; + + t = rtc_tm_to_time64(tm); + + alchemy_wrsys(t, AU1000_SYS_TOYWRITE); + + /* wait for the pending register write to succeed. This can + * take up to 6 seconds... + */ + while (alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_C0S) + msleep(1); + + return 0; +} + +static const struct rtc_class_ops au1xtoy_rtc_ops = { + .read_time = au1xtoy_rtc_read_time, + .set_time = au1xtoy_rtc_set_time, +}; + +static int au1xtoy_rtc_probe(struct platform_device *pdev) +{ + struct rtc_device *rtcdev; + unsigned long t; + + t = alchemy_rdsys(AU1000_SYS_CNTRCTRL); + if (!(t & CNTR_OK)) { + dev_err(&pdev->dev, "counters not working; aborting.\n"); + return -ENODEV; + } + + /* set counter0 tickrate to 1Hz if necessary */ + if (alchemy_rdsys(AU1000_SYS_TOYTRIM) != 32767) { + /* wait until hardware gives access to TRIM register */ + t = 0x00100000; + while ((alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_T0S) && --t) + msleep(1); + + if (!t) { + /* timed out waiting for register access; assume + * counters are unusable. + */ + dev_err(&pdev->dev, "timeout waiting for access\n"); + return -ETIMEDOUT; + } + + /* set 1Hz TOY tick rate */ + alchemy_wrsys(32767, AU1000_SYS_TOYTRIM); + } + + /* wait until the hardware allows writes to the counter reg */ + while (alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_C0S) + msleep(1); + + rtcdev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtcdev)) + return PTR_ERR(rtcdev); + + rtcdev->ops = &au1xtoy_rtc_ops; + rtcdev->range_max = U32_MAX; + + platform_set_drvdata(pdev, rtcdev); + + return devm_rtc_register_device(rtcdev); +} + +static struct platform_driver au1xrtc_driver = { + .driver = { + .name = "rtc-au1xxx", + }, +}; + +module_platform_driver_probe(au1xrtc_driver, au1xtoy_rtc_probe); + +MODULE_DESCRIPTION("Au1xxx TOY-counter-based RTC driver"); +MODULE_AUTHOR("Manuel Lauss "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rtc-au1xxx"); diff --git a/drivers/rtc/rtc-bd70528.c b/drivers/rtc/rtc-bd70528.c new file mode 100644 index 000000000..482810b61 --- /dev/null +++ b/drivers/rtc/rtc-bd70528.c @@ -0,0 +1,364 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Copyright (C) 2018 ROHM Semiconductors +// +// RTC driver for ROHM BD71828 and BD71815 PMIC + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * On BD71828 and BD71815 the ALM0 MASK is 14 bytes after the ALM0 + * block start + */ +#define BD718XX_ALM_EN_OFFSET 14 + +/* + * We read regs RTC_SEC => RTC_YEAR + * this struct is ordered according to chip registers. + * Keep it u8 only (or packed) to avoid padding issues. + */ +struct bd70528_rtc_day { + u8 sec; + u8 min; + u8 hour; +} __packed; + +struct bd70528_rtc_data { + struct bd70528_rtc_day time; + u8 week; + u8 day; + u8 month; + u8 year; +} __packed; + +struct bd71828_rtc_alm { + struct bd70528_rtc_data alm0; + struct bd70528_rtc_data alm1; + u8 alm_mask; + u8 alm1_mask; +} __packed; + +struct bd70528_rtc { + struct rohm_regmap_dev *parent; + struct regmap *regmap; + struct device *dev; + u8 reg_time_start; + u8 bd718xx_alm_block_start; +}; + +static inline void tmday2rtc(struct rtc_time *t, struct bd70528_rtc_day *d) +{ + d->sec &= ~BD70528_MASK_RTC_SEC; + d->min &= ~BD70528_MASK_RTC_MINUTE; + d->hour &= ~BD70528_MASK_RTC_HOUR; + d->sec |= bin2bcd(t->tm_sec); + d->min |= bin2bcd(t->tm_min); + d->hour |= bin2bcd(t->tm_hour); +} + +static inline void tm2rtc(struct rtc_time *t, struct bd70528_rtc_data *r) +{ + r->day &= ~BD70528_MASK_RTC_DAY; + r->week &= ~BD70528_MASK_RTC_WEEK; + r->month &= ~BD70528_MASK_RTC_MONTH; + /* + * PM and 24H bits are not used by Wake - thus we clear them + * here and not in tmday2rtc() which is also used by wake. + */ + r->time.hour &= ~(BD70528_MASK_RTC_HOUR_PM | BD70528_MASK_RTC_HOUR_24H); + + tmday2rtc(t, &r->time); + /* + * We do always set time in 24H mode. + */ + r->time.hour |= BD70528_MASK_RTC_HOUR_24H; + r->day |= bin2bcd(t->tm_mday); + r->week |= bin2bcd(t->tm_wday); + r->month |= bin2bcd(t->tm_mon + 1); + r->year = bin2bcd(t->tm_year - 100); +} + +static inline void rtc2tm(struct bd70528_rtc_data *r, struct rtc_time *t) +{ + t->tm_sec = bcd2bin(r->time.sec & BD70528_MASK_RTC_SEC); + t->tm_min = bcd2bin(r->time.min & BD70528_MASK_RTC_MINUTE); + t->tm_hour = bcd2bin(r->time.hour & BD70528_MASK_RTC_HOUR); + /* + * If RTC is in 12H mode, then bit BD70528_MASK_RTC_HOUR_PM + * is not BCD value but tells whether it is AM or PM + */ + if (!(r->time.hour & BD70528_MASK_RTC_HOUR_24H)) { + t->tm_hour %= 12; + if (r->time.hour & BD70528_MASK_RTC_HOUR_PM) + t->tm_hour += 12; + } + t->tm_mday = bcd2bin(r->day & BD70528_MASK_RTC_DAY); + t->tm_mon = bcd2bin(r->month & BD70528_MASK_RTC_MONTH) - 1; + t->tm_year = 100 + bcd2bin(r->year & BD70528_MASK_RTC_YEAR); + t->tm_wday = bcd2bin(r->week & BD70528_MASK_RTC_WEEK); +} + +static int bd71828_set_alarm(struct device *dev, struct rtc_wkalrm *a) +{ + int ret; + struct bd71828_rtc_alm alm; + struct bd70528_rtc *r = dev_get_drvdata(dev); + + ret = regmap_bulk_read(r->regmap, r->bd718xx_alm_block_start, &alm, + sizeof(alm)); + if (ret) { + dev_err(dev, "Failed to read alarm regs\n"); + return ret; + } + + tm2rtc(&a->time, &alm.alm0); + + if (!a->enabled) + alm.alm_mask &= ~BD70528_MASK_ALM_EN; + else + alm.alm_mask |= BD70528_MASK_ALM_EN; + + ret = regmap_bulk_write(r->regmap, r->bd718xx_alm_block_start, &alm, + sizeof(alm)); + if (ret) + dev_err(dev, "Failed to set alarm time\n"); + + return ret; + +} + +static int bd71828_read_alarm(struct device *dev, struct rtc_wkalrm *a) +{ + int ret; + struct bd71828_rtc_alm alm; + struct bd70528_rtc *r = dev_get_drvdata(dev); + + ret = regmap_bulk_read(r->regmap, r->bd718xx_alm_block_start, &alm, + sizeof(alm)); + if (ret) { + dev_err(dev, "Failed to read alarm regs\n"); + return ret; + } + + rtc2tm(&alm.alm0, &a->time); + a->time.tm_mday = -1; + a->time.tm_mon = -1; + a->time.tm_year = -1; + a->enabled = !!(alm.alm_mask & BD70528_MASK_ALM_EN); + a->pending = 0; + + return 0; +} + +static int bd71828_set_time(struct device *dev, struct rtc_time *t) +{ + int ret; + struct bd70528_rtc_data rtc_data; + struct bd70528_rtc *r = dev_get_drvdata(dev); + + ret = regmap_bulk_read(r->regmap, r->reg_time_start, &rtc_data, + sizeof(rtc_data)); + if (ret) { + dev_err(dev, "Failed to read RTC time registers\n"); + return ret; + } + tm2rtc(t, &rtc_data); + + ret = regmap_bulk_write(r->regmap, r->reg_time_start, &rtc_data, + sizeof(rtc_data)); + if (ret) + dev_err(dev, "Failed to set RTC time\n"); + + return ret; +} + +static int bd70528_get_time(struct device *dev, struct rtc_time *t) +{ + struct bd70528_rtc *r = dev_get_drvdata(dev); + struct bd70528_rtc_data rtc_data; + int ret; + + /* read the RTC date and time registers all at once */ + ret = regmap_bulk_read(r->regmap, r->reg_time_start, &rtc_data, + sizeof(rtc_data)); + if (ret) { + dev_err(dev, "Failed to read RTC time (err %d)\n", ret); + return ret; + } + + rtc2tm(&rtc_data, t); + + return 0; +} + +static int bd71828_alm_enable(struct device *dev, unsigned int enabled) +{ + int ret; + struct bd70528_rtc *r = dev_get_drvdata(dev); + unsigned int enableval = BD70528_MASK_ALM_EN; + + if (!enabled) + enableval = 0; + + ret = regmap_update_bits(r->regmap, r->bd718xx_alm_block_start + + BD718XX_ALM_EN_OFFSET, BD70528_MASK_ALM_EN, + enableval); + if (ret) + dev_err(dev, "Failed to change alarm state\n"); + + return ret; +} + +static const struct rtc_class_ops bd71828_rtc_ops = { + .read_time = bd70528_get_time, + .set_time = bd71828_set_time, + .read_alarm = bd71828_read_alarm, + .set_alarm = bd71828_set_alarm, + .alarm_irq_enable = bd71828_alm_enable, +}; + +static irqreturn_t alm_hndlr(int irq, void *data) +{ + struct rtc_device *rtc = data; + + rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF | RTC_PF); + return IRQ_HANDLED; +} + +static int bd70528_probe(struct platform_device *pdev) +{ + struct bd70528_rtc *bd_rtc; + const struct rtc_class_ops *rtc_ops; + int ret; + struct rtc_device *rtc; + int irq; + unsigned int hr; + u8 hour_reg; + enum rohm_chip_type chip = platform_get_device_id(pdev)->driver_data; + + bd_rtc = devm_kzalloc(&pdev->dev, sizeof(*bd_rtc), GFP_KERNEL); + if (!bd_rtc) + return -ENOMEM; + + bd_rtc->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!bd_rtc->regmap) { + dev_err(&pdev->dev, "No regmap\n"); + return -EINVAL; + } + + bd_rtc->dev = &pdev->dev; + rtc_ops = &bd71828_rtc_ops; + + switch (chip) { + case ROHM_CHIP_TYPE_BD71815: + bd_rtc->reg_time_start = BD71815_REG_RTC_START; + + /* + * See also BD718XX_ALM_EN_OFFSET: + * This works for BD71828, BD71815, and BD72720 as they all + * have same offset between the ALM0 start and the ALM0_MASK. + * If new ICs are to be added this requires proper check as + * the ALM0_MASK is not located at the end of ALM0 block - + * but after all ALM blocks. If amount of ALMs differ, the + * offset to enable/disable is likely to be incorrect and + * enable/disable must be given as own reg address here. + */ + bd_rtc->bd718xx_alm_block_start = BD71815_REG_RTC_ALM_START; + hour_reg = BD71815_REG_HOUR; + break; + case ROHM_CHIP_TYPE_BD71828: + bd_rtc->reg_time_start = BD71828_REG_RTC_START; + bd_rtc->bd718xx_alm_block_start = BD71828_REG_RTC_ALM_START; + hour_reg = BD71828_REG_RTC_HOUR; + break; + case ROHM_CHIP_TYPE_BD72720: + bd_rtc->reg_time_start = BD72720_REG_RTC_START; + bd_rtc->bd718xx_alm_block_start = BD72720_REG_RTC_ALM_START; + hour_reg = BD72720_REG_RTC_HOUR; + break; + default: + dev_err(&pdev->dev, "Unknown chip\n"); + return -ENOENT; + } + + irq = platform_get_irq_byname(pdev, "bd70528-rtc-alm-0"); + + if (irq < 0) + return irq; + + platform_set_drvdata(pdev, bd_rtc); + + ret = regmap_read(bd_rtc->regmap, hour_reg, &hr); + + if (ret) { + dev_err(&pdev->dev, "Failed to reag RTC clock\n"); + return ret; + } + + if (!(hr & BD70528_MASK_RTC_HOUR_24H)) { + struct rtc_time t; + + ret = rtc_ops->read_time(&pdev->dev, &t); + + if (!ret) + ret = rtc_ops->set_time(&pdev->dev, &t); + + if (ret) { + dev_err(&pdev->dev, + "Setting 24H clock for RTC failed\n"); + return ret; + } + } + + device_set_wakeup_capable(&pdev->dev, true); + device_wakeup_enable(&pdev->dev); + + rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc)) { + dev_err(&pdev->dev, "RTC device creation failed\n"); + return PTR_ERR(rtc); + } + + rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rtc->range_max = RTC_TIMESTAMP_END_2099; + rtc->ops = rtc_ops; + + /* Request alarm IRQ prior to registerig the RTC */ + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, &alm_hndlr, + IRQF_ONESHOT, "bd70528-rtc", rtc); + if (ret) + return ret; + + return devm_rtc_register_device(rtc); +} + +static const struct platform_device_id bd718x7_rtc_id[] = { + { .name = "bd71828-rtc", .driver_data = ROHM_CHIP_TYPE_BD71828 }, + { .name = "bd71815-rtc", .driver_data = ROHM_CHIP_TYPE_BD71815 }, + { .name = "bd72720-rtc", .driver_data = ROHM_CHIP_TYPE_BD72720 }, + { } +}; +MODULE_DEVICE_TABLE(platform, bd718x7_rtc_id); + +static struct platform_driver bd70528_rtc = { + .driver = { + .name = "bd70528-rtc" + }, + .probe = bd70528_probe, + .id_table = bd718x7_rtc_id, +}; + +module_platform_driver(bd70528_rtc); + +MODULE_AUTHOR("Matti Vaittinen "); +MODULE_DESCRIPTION("ROHM BD71828 and BD71815 PMIC RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:bd70528-rtc"); diff --git a/drivers/rtc/rtc-bq32k.c b/drivers/rtc/rtc-bq32k.c new file mode 100644 index 000000000..573231613 --- /dev/null +++ b/drivers/rtc/rtc-bq32k.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for TI BQ32000 RTC. + * + * Copyright (C) 2009 Semihalf. + * Copyright (C) 2014 Pavel Machek + * + * You can get hardware description at + * https://www.ti.com/lit/ds/symlink/bq32000.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define BQ32K_SECONDS 0x00 /* Seconds register address */ +#define BQ32K_SECONDS_MASK 0x7F /* Mask over seconds value */ +#define BQ32K_STOP 0x80 /* Oscillator Stop flat */ + +#define BQ32K_MINUTES 0x01 /* Minutes register address */ +#define BQ32K_MINUTES_MASK 0x7F /* Mask over minutes value */ +#define BQ32K_OF 0x80 /* Oscillator Failure flag */ + +#define BQ32K_HOURS_MASK 0x3F /* Mask over hours value */ +#define BQ32K_CENT 0x40 /* Century flag */ +#define BQ32K_CENT_EN 0x80 /* Century flag enable bit */ + +#define BQ32K_CALIBRATION 0x07 /* CAL_CFG1, calibration and control */ +#define BQ32K_TCH2 0x08 /* Trickle charge enable */ +#define BQ32K_CFG2 0x09 /* Trickle charger control */ +#define BQ32K_TCFE BIT(6) /* Trickle charge FET bypass */ + +#define MAX_LEN 10 /* Maximum number of consecutive + * register for this particular RTC. + */ + +struct bq32k_regs { + uint8_t seconds; + uint8_t minutes; + uint8_t cent_hours; + uint8_t day; + uint8_t date; + uint8_t month; + uint8_t years; +}; + +static struct i2c_driver bq32k_driver; + +static int bq32k_read(struct device *dev, void *data, uint8_t off, uint8_t len) +{ + struct i2c_client *client = to_i2c_client(dev); + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &off, + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = data, + } + }; + + if (i2c_transfer(client->adapter, msgs, 2) == 2) + return 0; + + return -EIO; +} + +static int bq32k_write(struct device *dev, void *data, uint8_t off, uint8_t len) +{ + struct i2c_client *client = to_i2c_client(dev); + uint8_t buffer[MAX_LEN + 1]; + + buffer[0] = off; + memcpy(&buffer[1], data, len); + + if (i2c_master_send(client, buffer, len + 1) == len + 1) + return 0; + + return -EIO; +} + +static int bq32k_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bq32k_regs regs; + int error; + + /* + * When the device doesn't have the interrupt connected, prevent + * userpace from polling the RTC registers too frequently. + */ + if (client->irq <= 0) + usleep_range(2000, 2500); + + error = bq32k_read(dev, ®s, 0, sizeof(regs)); + if (error) + return error; + + /* + * In case of oscillator failure, the register contents should be + * considered invalid. The flag is cleared the next time the RTC is set. + */ + if (regs.minutes & BQ32K_OF) + return -EINVAL; + + tm->tm_sec = bcd2bin(regs.seconds & BQ32K_SECONDS_MASK); + tm->tm_min = bcd2bin(regs.minutes & BQ32K_MINUTES_MASK); + tm->tm_hour = bcd2bin(regs.cent_hours & BQ32K_HOURS_MASK); + tm->tm_mday = bcd2bin(regs.date); + tm->tm_wday = bcd2bin(regs.day) - 1; + tm->tm_mon = bcd2bin(regs.month) - 1; + tm->tm_year = bcd2bin(regs.years) + + ((regs.cent_hours & BQ32K_CENT) ? 100 : 0); + + return 0; +} + +static int bq32k_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct bq32k_regs regs; + + regs.seconds = bin2bcd(tm->tm_sec); + regs.minutes = bin2bcd(tm->tm_min); + regs.cent_hours = bin2bcd(tm->tm_hour) | BQ32K_CENT_EN; + regs.day = bin2bcd(tm->tm_wday + 1); + regs.date = bin2bcd(tm->tm_mday); + regs.month = bin2bcd(tm->tm_mon + 1); + + if (tm->tm_year >= 100) { + regs.cent_hours |= BQ32K_CENT; + regs.years = bin2bcd(tm->tm_year - 100); + } else + regs.years = bin2bcd(tm->tm_year); + + return bq32k_write(dev, ®s, 0, sizeof(regs)); +} + +static const struct rtc_class_ops bq32k_rtc_ops = { + .read_time = bq32k_rtc_read_time, + .set_time = bq32k_rtc_set_time, +}; + +static int trickle_charger_of_init(struct device *dev, struct device_node *node) +{ + unsigned char reg; + int error; + u32 ohms = 0; + + if (of_property_read_u32(node, "trickle-resistor-ohms" , &ohms)) + return 0; + + switch (ohms) { + case 180+940: + /* + * TCHE[3:0] == 0x05, TCH2 == 1, TCFE == 0 (charging + * over diode and 940ohm resistor) + */ + + if (of_property_read_bool(node, "trickle-diode-disable")) { + dev_err(dev, "diode and resistor mismatch\n"); + return -EINVAL; + } + reg = 0x05; + break; + + case 180+20000: + /* diode disabled */ + + if (!of_property_read_bool(node, "trickle-diode-disable")) { + dev_err(dev, "bq32k: diode and resistor mismatch\n"); + return -EINVAL; + } + reg = 0x45; + break; + + default: + dev_err(dev, "invalid resistor value (%d)\n", ohms); + return -EINVAL; + } + + error = bq32k_write(dev, ®, BQ32K_CFG2, 1); + if (error) + return error; + + reg = 0x20; + error = bq32k_write(dev, ®, BQ32K_TCH2, 1); + if (error) + return error; + + dev_info(dev, "Enabled trickle RTC battery charge.\n"); + return 0; +} + +static ssize_t bq32k_sysfs_show_tricklecharge_bypass(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int reg, error; + + error = bq32k_read(dev, ®, BQ32K_CFG2, 1); + if (error) + return error; + + return sprintf(buf, "%d\n", (reg & BQ32K_TCFE) ? 1 : 0); +} + +static ssize_t bq32k_sysfs_store_tricklecharge_bypass(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int reg, enable, error; + + if (kstrtoint(buf, 0, &enable)) + return -EINVAL; + + error = bq32k_read(dev, ®, BQ32K_CFG2, 1); + if (error) + return error; + + if (enable) { + reg |= BQ32K_TCFE; + error = bq32k_write(dev, ®, BQ32K_CFG2, 1); + if (error) + return error; + + dev_info(dev, "Enabled trickle charge FET bypass.\n"); + } else { + reg &= ~BQ32K_TCFE; + error = bq32k_write(dev, ®, BQ32K_CFG2, 1); + if (error) + return error; + + dev_info(dev, "Disabled trickle charge FET bypass.\n"); + } + + return count; +} + +static DEVICE_ATTR(trickle_charge_bypass, 0644, + bq32k_sysfs_show_tricklecharge_bypass, + bq32k_sysfs_store_tricklecharge_bypass); + +static int bq32k_sysfs_register(struct device *dev) +{ + return device_create_file(dev, &dev_attr_trickle_charge_bypass); +} + +static void bq32k_sysfs_unregister(struct device *dev) +{ + device_remove_file(dev, &dev_attr_trickle_charge_bypass); +} + +static int bq32k_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct rtc_device *rtc; + uint8_t reg; + int error; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + /* Check Oscillator Stop flag */ + error = bq32k_read(dev, ®, BQ32K_SECONDS, 1); + if (!error && (reg & BQ32K_STOP)) { + dev_warn(dev, "Oscillator was halted. Restarting...\n"); + reg &= ~BQ32K_STOP; + error = bq32k_write(dev, ®, BQ32K_SECONDS, 1); + } + if (error) + return error; + + /* Check Oscillator Failure flag */ + error = bq32k_read(dev, ®, BQ32K_MINUTES, 1); + if (error) + return error; + if (reg & BQ32K_OF) + dev_warn(dev, "Oscillator Failure. Check RTC battery.\n"); + + if (client->dev.of_node) + trickle_charger_of_init(dev, client->dev.of_node); + + rtc = devm_rtc_device_register(&client->dev, bq32k_driver.driver.name, + &bq32k_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + error = bq32k_sysfs_register(&client->dev); + if (error) { + dev_err(&client->dev, + "Unable to create sysfs entries for rtc bq32000\n"); + return error; + } + + + i2c_set_clientdata(client, rtc); + + return 0; +} + +static void bq32k_remove(struct i2c_client *client) +{ + bq32k_sysfs_unregister(&client->dev); +} + +static const struct i2c_device_id bq32k_id[] = { + { .name = "bq32000" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, bq32k_id); + +static const __maybe_unused struct of_device_id bq32k_of_match[] = { + { .compatible = "ti,bq32000" }, + { } +}; +MODULE_DEVICE_TABLE(of, bq32k_of_match); + +static struct i2c_driver bq32k_driver = { + .driver = { + .name = "bq32k", + .of_match_table = of_match_ptr(bq32k_of_match), + }, + .probe = bq32k_probe, + .remove = bq32k_remove, + .id_table = bq32k_id, +}; + +module_i2c_driver(bq32k_driver); + +MODULE_AUTHOR("Semihalf, Piotr Ziecik "); +MODULE_DESCRIPTION("TI BQ32000 I2C RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-bq4802.c b/drivers/rtc/rtc-bq4802.c new file mode 100644 index 000000000..472e75668 --- /dev/null +++ b/drivers/rtc/rtc-bq4802.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* rtc-bq4802.c: TI BQ4802 RTC driver. + * + * Copyright (C) 2008 David S. Miller + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("David S. Miller "); +MODULE_DESCRIPTION("TI BQ4802 RTC driver"); +MODULE_LICENSE("GPL"); + +struct bq4802 { + void __iomem *regs; + unsigned long ioport; + struct rtc_device *rtc; + spinlock_t lock; + struct resource *r; + u8 (*read)(struct bq4802 *, int); + void (*write)(struct bq4802 *, int, u8); +}; + +static u8 bq4802_read_io(struct bq4802 *p, int off) +{ + return inb(p->ioport + off); +} + +static void bq4802_write_io(struct bq4802 *p, int off, u8 val) +{ + outb(val, p->ioport + off); +} + +static u8 bq4802_read_mem(struct bq4802 *p, int off) +{ + return readb(p->regs + off); +} + +static void bq4802_write_mem(struct bq4802 *p, int off, u8 val) +{ + writeb(val, p->regs + off); +} + +static int bq4802_read_time(struct device *dev, struct rtc_time *tm) +{ + struct bq4802 *p = dev_get_drvdata(dev); + unsigned long flags; + unsigned int century; + u8 val; + + spin_lock_irqsave(&p->lock, flags); + + val = p->read(p, 0x0e); + p->write(p, 0xe, val | 0x08); + + tm->tm_sec = p->read(p, 0x00); + tm->tm_min = p->read(p, 0x02); + tm->tm_hour = p->read(p, 0x04); + tm->tm_mday = p->read(p, 0x06); + tm->tm_mon = p->read(p, 0x09); + tm->tm_year = p->read(p, 0x0a); + tm->tm_wday = p->read(p, 0x08); + century = p->read(p, 0x0f); + + p->write(p, 0x0e, val); + + spin_unlock_irqrestore(&p->lock, flags); + + tm->tm_sec = bcd2bin(tm->tm_sec); + tm->tm_min = bcd2bin(tm->tm_min); + tm->tm_hour = bcd2bin(tm->tm_hour); + tm->tm_mday = bcd2bin(tm->tm_mday); + tm->tm_mon = bcd2bin(tm->tm_mon); + tm->tm_year = bcd2bin(tm->tm_year); + tm->tm_wday = bcd2bin(tm->tm_wday); + century = bcd2bin(century); + + tm->tm_year += (century * 100); + tm->tm_year -= 1900; + + tm->tm_mon--; + + return 0; +} + +static int bq4802_set_time(struct device *dev, struct rtc_time *tm) +{ + struct bq4802 *p = dev_get_drvdata(dev); + u8 sec, min, hrs, day, mon, yrs, century, val; + unsigned long flags; + unsigned int year; + + year = tm->tm_year + 1900; + century = year / 100; + yrs = year % 100; + + mon = tm->tm_mon + 1; /* tm_mon starts at zero */ + day = tm->tm_mday; + hrs = tm->tm_hour; + min = tm->tm_min; + sec = tm->tm_sec; + + sec = bin2bcd(sec); + min = bin2bcd(min); + hrs = bin2bcd(hrs); + day = bin2bcd(day); + mon = bin2bcd(mon); + yrs = bin2bcd(yrs); + century = bin2bcd(century); + + spin_lock_irqsave(&p->lock, flags); + + val = p->read(p, 0x0e); + p->write(p, 0x0e, val | 0x08); + + p->write(p, 0x00, sec); + p->write(p, 0x02, min); + p->write(p, 0x04, hrs); + p->write(p, 0x06, day); + p->write(p, 0x09, mon); + p->write(p, 0x0a, yrs); + p->write(p, 0x0f, century); + + p->write(p, 0x0e, val); + + spin_unlock_irqrestore(&p->lock, flags); + + return 0; +} + +static const struct rtc_class_ops bq4802_ops = { + .read_time = bq4802_read_time, + .set_time = bq4802_set_time, +}; + +static int bq4802_probe(struct platform_device *pdev) +{ + struct bq4802 *p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); + int err = -ENOMEM; + + if (!p) + goto out; + + spin_lock_init(&p->lock); + + p->r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!p->r) { + p->r = platform_get_resource(pdev, IORESOURCE_IO, 0); + err = -EINVAL; + if (!p->r) + goto out; + } + if (p->r->flags & IORESOURCE_IO) { + p->ioport = p->r->start; + p->read = bq4802_read_io; + p->write = bq4802_write_io; + } else if (p->r->flags & IORESOURCE_MEM) { + p->regs = devm_ioremap(&pdev->dev, p->r->start, + resource_size(p->r)); + if (!p->regs){ + err = -ENOMEM; + goto out; + } + p->read = bq4802_read_mem; + p->write = bq4802_write_mem; + } else { + err = -EINVAL; + goto out; + } + + platform_set_drvdata(pdev, p); + + p->rtc = devm_rtc_device_register(&pdev->dev, "bq4802", + &bq4802_ops, THIS_MODULE); + if (IS_ERR(p->rtc)) { + err = PTR_ERR(p->rtc); + goto out; + } + + err = 0; +out: + return err; + +} + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:rtc-bq4802"); + +static struct platform_driver bq4802_driver = { + .driver = { + .name = "rtc-bq4802", + }, + .probe = bq4802_probe, +}; + +module_platform_driver(bq4802_driver); diff --git a/drivers/rtc/rtc-brcmstb-waketimer.c b/drivers/rtc/rtc-brcmstb-waketimer.c new file mode 100644 index 000000000..55c15aad4 --- /dev/null +++ b/drivers/rtc/rtc-brcmstb-waketimer.c @@ -0,0 +1,433 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright © 2014-2023 Broadcom + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct brcmstb_waketmr { + struct rtc_device *rtc; + struct device *dev; + void __iomem *base; + unsigned int wake_irq; + unsigned int alarm_irq; + struct notifier_block reboot_notifier; + struct clk *clk; + u32 rate; + unsigned long rtc_alarm; + bool alarm_en; + bool alarm_expired; +}; + +#define BRCMSTB_WKTMR_EVENT 0x00 +#define WKTMR_ALARM_EVENT BIT(0) +#define BRCMSTB_WKTMR_COUNTER 0x04 +#define BRCMSTB_WKTMR_ALARM 0x08 +#define BRCMSTB_WKTMR_PRESCALER 0x0C +#define BRCMSTB_WKTMR_PRESCALER_VAL 0x10 + +#define BRCMSTB_WKTMR_DEFAULT_FREQ 27000000 + +static inline bool brcmstb_waketmr_is_pending(struct brcmstb_waketmr *timer) +{ + u32 reg; + + reg = readl_relaxed(timer->base + BRCMSTB_WKTMR_EVENT); + return !!(reg & WKTMR_ALARM_EVENT); +} + +static inline void brcmstb_waketmr_clear_alarm(struct brcmstb_waketmr *timer) +{ + u32 reg; + + if (timer->alarm_en && timer->alarm_irq) + disable_irq(timer->alarm_irq); + timer->alarm_en = false; + reg = readl_relaxed(timer->base + BRCMSTB_WKTMR_COUNTER); + writel_relaxed(reg - 1, timer->base + BRCMSTB_WKTMR_ALARM); + writel_relaxed(WKTMR_ALARM_EVENT, timer->base + BRCMSTB_WKTMR_EVENT); + (void)readl_relaxed(timer->base + BRCMSTB_WKTMR_EVENT); + if (timer->alarm_expired) { + timer->alarm_expired = false; + /* maintain call balance */ + enable_irq(timer->alarm_irq); + } +} + +static void brcmstb_waketmr_set_alarm(struct brcmstb_waketmr *timer, + unsigned int secs) +{ + unsigned int now; + + brcmstb_waketmr_clear_alarm(timer); + + /* Make sure we are actually counting in seconds */ + writel_relaxed(timer->rate, timer->base + BRCMSTB_WKTMR_PRESCALER); + + writel_relaxed(secs, timer->base + BRCMSTB_WKTMR_ALARM); + now = readl_relaxed(timer->base + BRCMSTB_WKTMR_COUNTER); + + while ((int)(secs - now) <= 0 && + !brcmstb_waketmr_is_pending(timer)) { + secs = now + 1; + writel_relaxed(secs, timer->base + BRCMSTB_WKTMR_ALARM); + now = readl_relaxed(timer->base + BRCMSTB_WKTMR_COUNTER); + } +} + +static irqreturn_t brcmstb_waketmr_irq(int irq, void *data) +{ + struct brcmstb_waketmr *timer = data; + + if (!timer->alarm_irq) + pm_wakeup_event(timer->dev, 0); + return IRQ_HANDLED; +} + +static irqreturn_t brcmstb_alarm_irq(int irq, void *data) +{ + struct brcmstb_waketmr *timer = data; + + /* Ignore spurious interrupts */ + if (!brcmstb_waketmr_is_pending(timer)) + return IRQ_HANDLED; + + if (timer->alarm_en) { + if (device_may_wakeup(timer->dev)) { + disable_irq_nosync(irq); + timer->alarm_expired = true; + } else { + writel_relaxed(WKTMR_ALARM_EVENT, + timer->base + BRCMSTB_WKTMR_EVENT); + } + rtc_update_irq(timer->rtc, 1, RTC_IRQF | RTC_AF); + } else { + writel_relaxed(WKTMR_ALARM_EVENT, + timer->base + BRCMSTB_WKTMR_EVENT); + } + + return IRQ_HANDLED; +} + +struct wktmr_time { + u32 sec; + u32 pre; +}; + +static void wktmr_read(struct brcmstb_waketmr *timer, + struct wktmr_time *t) +{ + u32 tmp; + + do { + t->sec = readl_relaxed(timer->base + BRCMSTB_WKTMR_COUNTER); + tmp = readl_relaxed(timer->base + BRCMSTB_WKTMR_PRESCALER_VAL); + } while (tmp >= timer->rate); + + t->pre = timer->rate - tmp; +} + +static int brcmstb_waketmr_prepare_suspend(struct brcmstb_waketmr *timer) +{ + struct device *dev = timer->dev; + int ret; + + if (device_may_wakeup(dev)) { + ret = enable_irq_wake(timer->wake_irq); + if (ret) { + dev_err(dev, "failed to enable wake-up interrupt\n"); + return ret; + } + if (timer->alarm_en && timer->alarm_irq) { + ret = enable_irq_wake(timer->alarm_irq); + if (ret) { + dev_err(dev, "failed to enable rtc interrupt\n"); + disable_irq_wake(timer->wake_irq); + return ret; + } + } + } + + return 0; +} + +/* If enabled as a wakeup-source, arm the timer when powering off */ +static int brcmstb_waketmr_reboot(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct brcmstb_waketmr *timer; + + timer = container_of(nb, struct brcmstb_waketmr, reboot_notifier); + + /* Set timer for cold boot */ + if (action == SYS_POWER_OFF) + brcmstb_waketmr_prepare_suspend(timer); + + return NOTIFY_DONE; +} + +static int brcmstb_waketmr_gettime(struct device *dev, + struct rtc_time *tm) +{ + struct brcmstb_waketmr *timer = dev_get_drvdata(dev); + struct wktmr_time now; + + wktmr_read(timer, &now); + + rtc_time64_to_tm(now.sec, tm); + + return 0; +} + +static int brcmstb_waketmr_settime(struct device *dev, + struct rtc_time *tm) +{ + struct brcmstb_waketmr *timer = dev_get_drvdata(dev); + time64_t sec; + + sec = rtc_tm_to_time64(tm); + + writel_relaxed(sec, timer->base + BRCMSTB_WKTMR_COUNTER); + + return 0; +} + +static int brcmstb_waketmr_getalarm(struct device *dev, + struct rtc_wkalrm *alarm) +{ + struct brcmstb_waketmr *timer = dev_get_drvdata(dev); + + alarm->enabled = timer->alarm_en; + rtc_time64_to_tm(timer->rtc_alarm, &alarm->time); + + alarm->pending = brcmstb_waketmr_is_pending(timer); + + return 0; +} + +static int brcmstb_waketmr_alarm_enable(struct device *dev, + unsigned int enabled) +{ + struct brcmstb_waketmr *timer = dev_get_drvdata(dev); + + if (enabled && !timer->alarm_en) { + if ((int)(readl_relaxed(timer->base + BRCMSTB_WKTMR_COUNTER) - + readl_relaxed(timer->base + BRCMSTB_WKTMR_ALARM)) >= 0 && + !brcmstb_waketmr_is_pending(timer)) + return -EINVAL; + timer->alarm_en = true; + if (timer->alarm_irq) { + if (timer->alarm_expired) { + timer->alarm_expired = false; + /* maintain call balance */ + enable_irq(timer->alarm_irq); + } + enable_irq(timer->alarm_irq); + } + } else if (!enabled && timer->alarm_en) { + if (timer->alarm_irq) + disable_irq(timer->alarm_irq); + timer->alarm_en = false; + } + + return 0; +} + +static int brcmstb_waketmr_setalarm(struct device *dev, + struct rtc_wkalrm *alarm) +{ + struct brcmstb_waketmr *timer = dev_get_drvdata(dev); + + timer->rtc_alarm = rtc_tm_to_time64(&alarm->time); + + brcmstb_waketmr_set_alarm(timer, timer->rtc_alarm); + + return brcmstb_waketmr_alarm_enable(dev, alarm->enabled); +} + +static const struct rtc_class_ops brcmstb_waketmr_ops = { + .read_time = brcmstb_waketmr_gettime, + .set_time = brcmstb_waketmr_settime, + .read_alarm = brcmstb_waketmr_getalarm, + .set_alarm = brcmstb_waketmr_setalarm, + .alarm_irq_enable = brcmstb_waketmr_alarm_enable, +}; + +static int brcmstb_waketmr_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct brcmstb_waketmr *timer; + int ret; + + timer = devm_kzalloc(dev, sizeof(*timer), GFP_KERNEL); + if (!timer) + return -ENOMEM; + + platform_set_drvdata(pdev, timer); + timer->dev = dev; + + timer->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(timer->base)) + return PTR_ERR(timer->base); + + timer->rtc = devm_rtc_allocate_device(dev); + if (IS_ERR(timer->rtc)) + return PTR_ERR(timer->rtc); + + /* + * Set wakeup capability before requesting wakeup interrupt, so we can + * process boot-time "wakeups" (e.g., from S5 soft-off) + */ + device_init_wakeup(dev, true); + + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return -ENODEV; + timer->wake_irq = (unsigned int)ret; + + timer->clk = devm_clk_get(dev, NULL); + if (!IS_ERR(timer->clk)) { + ret = clk_prepare_enable(timer->clk); + if (ret) + return ret; + timer->rate = clk_get_rate(timer->clk); + if (!timer->rate) + timer->rate = BRCMSTB_WKTMR_DEFAULT_FREQ; + } else { + timer->rate = BRCMSTB_WKTMR_DEFAULT_FREQ; + timer->clk = NULL; + } + + ret = devm_request_irq(dev, timer->wake_irq, brcmstb_waketmr_irq, 0, + "brcmstb-waketimer", timer); + if (ret < 0) + goto err_clk; + + brcmstb_waketmr_clear_alarm(timer); + + /* Attempt to initialize non-wake irq */ + ret = platform_get_irq(pdev, 1); + if (ret > 0) { + timer->alarm_irq = (unsigned int)ret; + ret = devm_request_irq(dev, timer->alarm_irq, brcmstb_alarm_irq, + IRQF_NO_AUTOEN, "brcmstb-waketimer-rtc", + timer); + if (ret < 0) + timer->alarm_irq = 0; + } + + timer->reboot_notifier.notifier_call = brcmstb_waketmr_reboot; + register_reboot_notifier(&timer->reboot_notifier); + + timer->rtc->ops = &brcmstb_waketmr_ops; + timer->rtc->range_max = U32_MAX; + + ret = devm_rtc_register_device(timer->rtc); + if (ret) + goto err_notifier; + + return 0; + +err_notifier: + unregister_reboot_notifier(&timer->reboot_notifier); + +err_clk: + clk_disable_unprepare(timer->clk); + + return ret; +} + +static void brcmstb_waketmr_remove(struct platform_device *pdev) +{ + struct brcmstb_waketmr *timer = dev_get_drvdata(&pdev->dev); + + unregister_reboot_notifier(&timer->reboot_notifier); + clk_disable_unprepare(timer->clk); +} + +#ifdef CONFIG_PM_SLEEP +static int brcmstb_waketmr_suspend(struct device *dev) +{ + struct brcmstb_waketmr *timer = dev_get_drvdata(dev); + + return brcmstb_waketmr_prepare_suspend(timer); +} + +static int brcmstb_waketmr_suspend_noirq(struct device *dev) +{ + struct brcmstb_waketmr *timer = dev_get_drvdata(dev); + + /* Catch any alarms occurring prior to noirq */ + if (timer->alarm_expired && device_may_wakeup(dev)) + return -EBUSY; + + return 0; +} + +static int brcmstb_waketmr_resume(struct device *dev) +{ + struct brcmstb_waketmr *timer = dev_get_drvdata(dev); + int ret; + + if (!device_may_wakeup(dev)) + return 0; + + ret = disable_irq_wake(timer->wake_irq); + if (timer->alarm_en && timer->alarm_irq) + disable_irq_wake(timer->alarm_irq); + + brcmstb_waketmr_clear_alarm(timer); + + return ret; +} +#else +#define brcmstb_waketmr_suspend NULL +#define brcmstb_waketmr_suspend_noirq NULL +#define brcmstb_waketmr_resume NULL +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops brcmstb_waketmr_pm_ops = { + .suspend = brcmstb_waketmr_suspend, + .suspend_noirq = brcmstb_waketmr_suspend_noirq, + .resume = brcmstb_waketmr_resume, +}; + +static const __maybe_unused struct of_device_id brcmstb_waketmr_of_match[] = { + { .compatible = "brcm,brcmstb-waketimer" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, brcmstb_waketmr_of_match); + +static struct platform_driver brcmstb_waketmr_driver = { + .probe = brcmstb_waketmr_probe, + .remove = brcmstb_waketmr_remove, + .driver = { + .name = "brcmstb-waketimer", + .pm = &brcmstb_waketmr_pm_ops, + .of_match_table = of_match_ptr(brcmstb_waketmr_of_match), + } +}; +module_platform_driver(brcmstb_waketmr_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Brian Norris"); +MODULE_AUTHOR("Markus Mayer"); +MODULE_AUTHOR("Doug Berger"); +MODULE_DESCRIPTION("Wake-up timer driver for STB chips"); diff --git a/drivers/rtc/rtc-cadence.c b/drivers/rtc/rtc-cadence.c new file mode 100644 index 000000000..8634eea79 --- /dev/null +++ b/drivers/rtc/rtc-cadence.c @@ -0,0 +1,412 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright 2019 Cadence + * + * Authors: + * Jan Kotas + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Registers */ +#define CDNS_RTC_CTLR 0x00 +#define CDNS_RTC_HMR 0x04 +#define CDNS_RTC_TIMR 0x08 +#define CDNS_RTC_CALR 0x0C +#define CDNS_RTC_TIMAR 0x10 +#define CDNS_RTC_CALAR 0x14 +#define CDNS_RTC_AENR 0x18 +#define CDNS_RTC_EFLR 0x1C +#define CDNS_RTC_IENR 0x20 +#define CDNS_RTC_IDISR 0x24 +#define CDNS_RTC_IMSKR 0x28 +#define CDNS_RTC_STSR 0x2C +#define CDNS_RTC_KRTCR 0x30 + +/* Control */ +#define CDNS_RTC_CTLR_TIME BIT(0) +#define CDNS_RTC_CTLR_CAL BIT(1) +#define CDNS_RTC_CTLR_TIME_CAL (CDNS_RTC_CTLR_TIME | CDNS_RTC_CTLR_CAL) + +/* Status */ +#define CDNS_RTC_STSR_VT BIT(0) +#define CDNS_RTC_STSR_VC BIT(1) +#define CDNS_RTC_STSR_VTA BIT(2) +#define CDNS_RTC_STSR_VCA BIT(3) +#define CDNS_RTC_STSR_VT_VC (CDNS_RTC_STSR_VT | CDNS_RTC_STSR_VC) +#define CDNS_RTC_STSR_VTA_VCA (CDNS_RTC_STSR_VTA | CDNS_RTC_STSR_VCA) + +/* Keep RTC */ +#define CDNS_RTC_KRTCR_KRTC BIT(0) + +/* Alarm, Event, Interrupt */ +#define CDNS_RTC_AEI_HOS BIT(0) +#define CDNS_RTC_AEI_SEC BIT(1) +#define CDNS_RTC_AEI_MIN BIT(2) +#define CDNS_RTC_AEI_HOUR BIT(3) +#define CDNS_RTC_AEI_DATE BIT(4) +#define CDNS_RTC_AEI_MNTH BIT(5) +#define CDNS_RTC_AEI_ALRM BIT(6) + +/* Time */ +#define CDNS_RTC_TIME_H GENMASK(7, 0) +#define CDNS_RTC_TIME_S GENMASK(14, 8) +#define CDNS_RTC_TIME_M GENMASK(22, 16) +#define CDNS_RTC_TIME_HR GENMASK(29, 24) +#define CDNS_RTC_TIME_PM BIT(30) +#define CDNS_RTC_TIME_CH BIT(31) + +/* Calendar */ +#define CDNS_RTC_CAL_DAY GENMASK(2, 0) +#define CDNS_RTC_CAL_M GENMASK(7, 3) +#define CDNS_RTC_CAL_D GENMASK(13, 8) +#define CDNS_RTC_CAL_Y GENMASK(23, 16) +#define CDNS_RTC_CAL_C GENMASK(29, 24) +#define CDNS_RTC_CAL_CH BIT(31) + +#define CDNS_RTC_MAX_REGS_TRIES 3 + +struct cdns_rtc { + struct rtc_device *rtc_dev; + struct clk *pclk; + struct clk *ref_clk; + void __iomem *regs; + int irq; +}; + +static void cdns_rtc_set_enabled(struct cdns_rtc *crtc, bool enabled) +{ + u32 reg = enabled ? 0x0 : CDNS_RTC_CTLR_TIME_CAL; + + writel(reg, crtc->regs + CDNS_RTC_CTLR); +} + +static bool cdns_rtc_get_enabled(struct cdns_rtc *crtc) +{ + return !(readl(crtc->regs + CDNS_RTC_CTLR) & CDNS_RTC_CTLR_TIME_CAL); +} + +static irqreturn_t cdns_rtc_irq_handler(int irq, void *id) +{ + struct device *dev = id; + struct cdns_rtc *crtc = dev_get_drvdata(dev); + + /* Reading the register clears it */ + if (!(readl(crtc->regs + CDNS_RTC_EFLR) & CDNS_RTC_AEI_ALRM)) + return IRQ_NONE; + + rtc_update_irq(crtc->rtc_dev, 1, RTC_IRQF | RTC_AF); + return IRQ_HANDLED; +} + +static u32 cdns_rtc_time2reg(struct rtc_time *tm) +{ + return FIELD_PREP(CDNS_RTC_TIME_S, bin2bcd(tm->tm_sec)) + | FIELD_PREP(CDNS_RTC_TIME_M, bin2bcd(tm->tm_min)) + | FIELD_PREP(CDNS_RTC_TIME_HR, bin2bcd(tm->tm_hour)); +} + +static void cdns_rtc_reg2time(u32 reg, struct rtc_time *tm) +{ + tm->tm_sec = bcd2bin(FIELD_GET(CDNS_RTC_TIME_S, reg)); + tm->tm_min = bcd2bin(FIELD_GET(CDNS_RTC_TIME_M, reg)); + tm->tm_hour = bcd2bin(FIELD_GET(CDNS_RTC_TIME_HR, reg)); +} + +static int cdns_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct cdns_rtc *crtc = dev_get_drvdata(dev); + u32 reg; + + /* If the RTC is disabled, assume the values are invalid */ + if (!cdns_rtc_get_enabled(crtc)) + return -EINVAL; + + cdns_rtc_set_enabled(crtc, false); + + reg = readl(crtc->regs + CDNS_RTC_TIMR); + cdns_rtc_reg2time(reg, tm); + + reg = readl(crtc->regs + CDNS_RTC_CALR); + tm->tm_mday = bcd2bin(FIELD_GET(CDNS_RTC_CAL_D, reg)); + tm->tm_mon = bcd2bin(FIELD_GET(CDNS_RTC_CAL_M, reg)) - 1; + tm->tm_year = bcd2bin(FIELD_GET(CDNS_RTC_CAL_Y, reg)) + + bcd2bin(FIELD_GET(CDNS_RTC_CAL_C, reg)) * 100 - 1900; + tm->tm_wday = bcd2bin(FIELD_GET(CDNS_RTC_CAL_DAY, reg)) - 1; + + cdns_rtc_set_enabled(crtc, true); + return 0; +} + +static int cdns_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct cdns_rtc *crtc = dev_get_drvdata(dev); + u32 timr, calr, stsr; + int ret = -EIO; + int year = tm->tm_year + 1900; + int tries; + + cdns_rtc_set_enabled(crtc, false); + + timr = cdns_rtc_time2reg(tm); + + calr = FIELD_PREP(CDNS_RTC_CAL_D, bin2bcd(tm->tm_mday)) + | FIELD_PREP(CDNS_RTC_CAL_M, bin2bcd(tm->tm_mon + 1)) + | FIELD_PREP(CDNS_RTC_CAL_Y, bin2bcd(year % 100)) + | FIELD_PREP(CDNS_RTC_CAL_C, bin2bcd(year / 100)) + | FIELD_PREP(CDNS_RTC_CAL_DAY, tm->tm_wday + 1); + + /* Update registers, check valid flags */ + for (tries = 0; tries < CDNS_RTC_MAX_REGS_TRIES; tries++) { + writel(timr, crtc->regs + CDNS_RTC_TIMR); + writel(calr, crtc->regs + CDNS_RTC_CALR); + stsr = readl(crtc->regs + CDNS_RTC_STSR); + + if ((stsr & CDNS_RTC_STSR_VT_VC) == CDNS_RTC_STSR_VT_VC) { + ret = 0; + break; + } + } + + cdns_rtc_set_enabled(crtc, true); + return ret; +} + +static int cdns_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct cdns_rtc *crtc = dev_get_drvdata(dev); + + if (enabled) { + writel((CDNS_RTC_AEI_SEC | CDNS_RTC_AEI_MIN | CDNS_RTC_AEI_HOUR + | CDNS_RTC_AEI_DATE | CDNS_RTC_AEI_MNTH), + crtc->regs + CDNS_RTC_AENR); + writel(CDNS_RTC_AEI_ALRM, crtc->regs + CDNS_RTC_IENR); + } else { + writel(0, crtc->regs + CDNS_RTC_AENR); + writel(CDNS_RTC_AEI_ALRM, crtc->regs + CDNS_RTC_IDISR); + } + + return 0; +} + +static int cdns_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct cdns_rtc *crtc = dev_get_drvdata(dev); + u32 reg; + + reg = readl(crtc->regs + CDNS_RTC_TIMAR); + cdns_rtc_reg2time(reg, &alarm->time); + + reg = readl(crtc->regs + CDNS_RTC_CALAR); + alarm->time.tm_mday = bcd2bin(FIELD_GET(CDNS_RTC_CAL_D, reg)); + alarm->time.tm_mon = bcd2bin(FIELD_GET(CDNS_RTC_CAL_M, reg)) - 1; + + return 0; +} + +static int cdns_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct cdns_rtc *crtc = dev_get_drvdata(dev); + int ret = -EIO; + int tries; + u32 timar, calar, stsr; + + cdns_rtc_alarm_irq_enable(dev, 0); + + timar = cdns_rtc_time2reg(&alarm->time); + calar = FIELD_PREP(CDNS_RTC_CAL_D, bin2bcd(alarm->time.tm_mday)) + | FIELD_PREP(CDNS_RTC_CAL_M, bin2bcd(alarm->time.tm_mon + 1)); + + /* Update registers, check valid alarm flags */ + for (tries = 0; tries < CDNS_RTC_MAX_REGS_TRIES; tries++) { + writel(timar, crtc->regs + CDNS_RTC_TIMAR); + writel(calar, crtc->regs + CDNS_RTC_CALAR); + stsr = readl(crtc->regs + CDNS_RTC_STSR); + + if ((stsr & CDNS_RTC_STSR_VTA_VCA) == CDNS_RTC_STSR_VTA_VCA) { + ret = 0; + break; + } + } + + if (!ret) + cdns_rtc_alarm_irq_enable(dev, alarm->enabled); + return ret; +} + +static const struct rtc_class_ops cdns_rtc_ops = { + .read_time = cdns_rtc_read_time, + .set_time = cdns_rtc_set_time, + .read_alarm = cdns_rtc_read_alarm, + .set_alarm = cdns_rtc_set_alarm, + .alarm_irq_enable = cdns_rtc_alarm_irq_enable, +}; + +static int cdns_rtc_probe(struct platform_device *pdev) +{ + struct cdns_rtc *crtc; + int ret; + unsigned long ref_clk_freq; + + crtc = devm_kzalloc(&pdev->dev, sizeof(*crtc), GFP_KERNEL); + if (!crtc) + return -ENOMEM; + + crtc->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(crtc->regs)) + return PTR_ERR(crtc->regs); + + crtc->irq = platform_get_irq(pdev, 0); + if (crtc->irq < 0) + return -EINVAL; + + crtc->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(crtc->pclk)) { + ret = PTR_ERR(crtc->pclk); + dev_err(&pdev->dev, + "Failed to retrieve the peripheral clock, %d\n", ret); + return ret; + } + + crtc->ref_clk = devm_clk_get(&pdev->dev, "ref_clk"); + if (IS_ERR(crtc->ref_clk)) { + ret = PTR_ERR(crtc->ref_clk); + dev_err(&pdev->dev, + "Failed to retrieve the reference clock, %d\n", ret); + return ret; + } + + crtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(crtc->rtc_dev)) + return PTR_ERR(crtc->rtc_dev); + + platform_set_drvdata(pdev, crtc); + + ret = clk_prepare_enable(crtc->pclk); + if (ret) { + dev_err(&pdev->dev, + "Failed to enable the peripheral clock, %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(crtc->ref_clk); + if (ret) { + dev_err(&pdev->dev, + "Failed to enable the reference clock, %d\n", ret); + goto err_disable_pclk; + } + + ref_clk_freq = clk_get_rate(crtc->ref_clk); + if ((ref_clk_freq != 1) && (ref_clk_freq != 100)) { + dev_err(&pdev->dev, + "Invalid reference clock frequency %lu Hz.\n", + ref_clk_freq); + ret = -EINVAL; + goto err_disable_ref_clk; + } + + ret = devm_request_irq(&pdev->dev, crtc->irq, + cdns_rtc_irq_handler, 0, + dev_name(&pdev->dev), &pdev->dev); + if (ret) { + dev_err(&pdev->dev, + "Failed to request interrupt for the device, %d\n", + ret); + goto err_disable_ref_clk; + } + + /* The RTC supports 01.01.1900 - 31.12.2999 */ + crtc->rtc_dev->range_min = mktime64(1900, 1, 1, 0, 0, 0); + crtc->rtc_dev->range_max = mktime64(2999, 12, 31, 23, 59, 59); + + crtc->rtc_dev->ops = &cdns_rtc_ops; + device_init_wakeup(&pdev->dev, true); + + /* Always use 24-hour mode and keep the RTC values */ + writel(0, crtc->regs + CDNS_RTC_HMR); + writel(CDNS_RTC_KRTCR_KRTC, crtc->regs + CDNS_RTC_KRTCR); + + ret = devm_rtc_register_device(crtc->rtc_dev); + if (ret) + goto err_disable_wakeup; + + return 0; + +err_disable_wakeup: + device_init_wakeup(&pdev->dev, false); + +err_disable_ref_clk: + clk_disable_unprepare(crtc->ref_clk); + +err_disable_pclk: + clk_disable_unprepare(crtc->pclk); + + return ret; +} + +static void cdns_rtc_remove(struct platform_device *pdev) +{ + struct cdns_rtc *crtc = platform_get_drvdata(pdev); + + cdns_rtc_alarm_irq_enable(&pdev->dev, 0); + device_init_wakeup(&pdev->dev, false); + + clk_disable_unprepare(crtc->pclk); + clk_disable_unprepare(crtc->ref_clk); +} + +#ifdef CONFIG_PM_SLEEP +static int cdns_rtc_suspend(struct device *dev) +{ + struct cdns_rtc *crtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(crtc->irq); + + return 0; +} + +static int cdns_rtc_resume(struct device *dev) +{ + struct cdns_rtc *crtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(crtc->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(cdns_rtc_pm_ops, cdns_rtc_suspend, cdns_rtc_resume); + +static const struct of_device_id cdns_rtc_of_match[] = { + { .compatible = "cdns,rtc-r109v3" }, + { }, +}; +MODULE_DEVICE_TABLE(of, cdns_rtc_of_match); + +static struct platform_driver cdns_rtc_driver = { + .driver = { + .name = "cdns-rtc", + .of_match_table = cdns_rtc_of_match, + .pm = &cdns_rtc_pm_ops, + }, + .probe = cdns_rtc_probe, + .remove = cdns_rtc_remove, +}; +module_platform_driver(cdns_rtc_driver); + +MODULE_AUTHOR("Jan Kotas "); +MODULE_DESCRIPTION("Cadence RTC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cdns-rtc"); diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c new file mode 100644 index 000000000..fa04ece15 --- /dev/null +++ b/drivers/rtc/rtc-cmos.c @@ -0,0 +1,1517 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * RTC class driver for "CMOS RTC": PCs, ACPI, etc + * + * Copyright (C) 1996 Paul Gortmaker (drivers/char/rtc.c) + * Copyright (C) 2006 David Brownell (convert to new framework) + */ + +/* + * The original "cmos clock" chip was an MC146818 chip, now obsolete. + * That defined the register interface now provided by all PCs, some + * non-PC systems, and incorporated into ACPI. Modern PC chipsets + * integrate an MC146818 clone in their southbridge, and boards use + * that instead of discrete clones like the DS12887 or M48T86. There + * are also clones that connect using the LPC bus. + * + * That register API is also used directly by various other drivers + * (notably for integrated NVRAM), infrastructure (x86 has code to + * bypass the RTC framework, directly reading the RTC during boot + * and updating minutes/seconds for systems using NTP synch) and + * utilities (like userspace 'hwclock', if no /dev node exists). + * + * So **ALL** calls to CMOS_READ and CMOS_WRITE must be done with + * interrupts disabled, holding the global rtc_lock, to exclude those + * other drivers and utilities on correctly configured systems. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_X86 +#include +#include +#include +#endif + +/* this is for "generic access to PC-style RTC" using CMOS_READ/CMOS_WRITE */ +#include + +#ifdef CONFIG_ACPI +/* + * Use ACPI SCI to replace HPET interrupt for RTC Alarm event + * + * If cleared, ACPI SCI is only used to wake up the system from suspend + * + * If set, ACPI SCI is used to handle UIE/AIE and system wakeup + */ + +static bool use_acpi_alarm; +module_param(use_acpi_alarm, bool, 0444); + +static inline int cmos_use_acpi_alarm(void) +{ + return use_acpi_alarm; +} +#else /* !CONFIG_ACPI */ + +static inline int cmos_use_acpi_alarm(void) +{ + return 0; +} +#endif + +struct cmos_rtc { + struct rtc_device *rtc; + struct device *dev; + int irq; + struct resource *iomem; + time64_t alarm_expires; + + void (*wake_on)(struct device *); + void (*wake_off)(struct device *); + + u8 enabled_wake; + u8 suspend_ctrl; + + /* newer hardware extends the original register set */ + u8 day_alrm; + u8 mon_alrm; + u8 century; + + struct rtc_wkalrm saved_wkalrm; +}; + +/* both platform and pnp busses use negative numbers for invalid irqs */ +#define is_valid_irq(n) ((n) > 0) + +static const char driver_name[] = "rtc_cmos"; + +/* The RTC_INTR register may have e.g. RTC_PF set even if RTC_PIE is clear; + * always mask it against the irq enable bits in RTC_CONTROL. Bit values + * are the same: PF==PIE, AF=AIE, UF=UIE; so RTC_IRQMASK works with both. + */ +#define RTC_IRQMASK (RTC_PF | RTC_AF | RTC_UF) + +static inline int is_intr(u8 rtc_intr) +{ + if (!(rtc_intr & RTC_IRQF)) + return 0; + return rtc_intr & RTC_IRQMASK; +} + +/*----------------------------------------------------------------*/ + +/* Much modern x86 hardware has HPETs (10+ MHz timers) which, because + * many BIOS programmers don't set up "sane mode" IRQ routing, are mostly + * used in a broken "legacy replacement" mode. The breakage includes + * HPET #1 hijacking the IRQ for this RTC, and being unavailable for + * other (better) use. + * + * When that broken mode is in use, platform glue provides a partial + * emulation of hardware RTC IRQ facilities using HPET #1. We don't + * want to use HPET for anything except those IRQs though... + */ +#ifdef CONFIG_HPET_EMULATE_RTC +#include +#else + +static inline int is_hpet_enabled(void) +{ + return 0; +} + +static inline int hpet_mask_rtc_irq_bit(unsigned long mask) +{ + return 0; +} + +static inline int hpet_set_rtc_irq_bit(unsigned long mask) +{ + return 0; +} + +static inline int +hpet_set_alarm_time(unsigned char hrs, unsigned char min, unsigned char sec) +{ + return 0; +} + +static inline int hpet_set_periodic_freq(unsigned long freq) +{ + return 0; +} + +static inline int hpet_rtc_timer_init(void) +{ + return 0; +} + +extern irq_handler_t hpet_rtc_interrupt; + +static inline int hpet_register_irq_handler(irq_handler_t handler) +{ + return 0; +} + +static inline int hpet_unregister_irq_handler(irq_handler_t handler) +{ + return 0; +} + +#endif + +/* Don't use HPET for RTC Alarm event if ACPI Fixed event is used */ +static inline int use_hpet_alarm(void) +{ + return is_hpet_enabled() && !cmos_use_acpi_alarm(); +} + +/*----------------------------------------------------------------*/ + +#ifdef RTC_PORT + +/* Most newer x86 systems have two register banks, the first used + * for RTC and NVRAM and the second only for NVRAM. Caller must + * own rtc_lock ... and we won't worry about access during NMI. + */ +#define can_bank2 true + +static inline unsigned char cmos_read_bank2(unsigned char addr) +{ + outb(addr, RTC_PORT(2)); + return inb(RTC_PORT(3)); +} + +static inline void cmos_write_bank2(unsigned char val, unsigned char addr) +{ + outb(addr, RTC_PORT(2)); + outb(val, RTC_PORT(3)); +} + +#else + +#define can_bank2 false + +static inline unsigned char cmos_read_bank2(unsigned char addr) +{ + return 0; +} + +static inline void cmos_write_bank2(unsigned char val, unsigned char addr) +{ +} + +#endif + +/*----------------------------------------------------------------*/ + +static bool cmos_no_alarm(struct cmos_rtc *cmos) +{ + return !is_valid_irq(cmos->irq) && !cmos_use_acpi_alarm(); +} + +static int cmos_read_time(struct device *dev, struct rtc_time *t) +{ + int ret; + + /* + * If pm_trace abused the RTC for storage, set the timespec to 0, + * which tells the caller that this RTC value is unusable. + */ + if (!pm_trace_rtc_valid()) + return -EIO; + + ret = mc146818_get_time(t, 1000); + if (ret < 0) { + dev_err_ratelimited(dev, "unable to read current time\n"); + return ret; + } + + return 0; +} + +static int cmos_set_time(struct device *dev, struct rtc_time *t) +{ + /* NOTE: this ignores the issue whereby updating the seconds + * takes effect exactly 500ms after we write the register. + * (Also queueing and other delays before we get this far.) + */ + return mc146818_set_time(t); +} + +struct cmos_read_alarm_callback_param { + struct cmos_rtc *cmos; + struct rtc_time *time; + unsigned char rtc_control; +}; + +static void cmos_read_alarm_callback(unsigned char __always_unused seconds, + void *param_in) +{ + struct cmos_read_alarm_callback_param *p = + (struct cmos_read_alarm_callback_param *)param_in; + struct rtc_time *time = p->time; + + time->tm_sec = CMOS_READ(RTC_SECONDS_ALARM); + time->tm_min = CMOS_READ(RTC_MINUTES_ALARM); + time->tm_hour = CMOS_READ(RTC_HOURS_ALARM); + + if (p->cmos->day_alrm) { + /* ignore upper bits on readback per ACPI spec */ + time->tm_mday = CMOS_READ(p->cmos->day_alrm) & 0x3f; + if (!time->tm_mday) + time->tm_mday = -1; + + if (p->cmos->mon_alrm) { + time->tm_mon = CMOS_READ(p->cmos->mon_alrm); + if (!time->tm_mon) + time->tm_mon = -1; + } + } + + p->rtc_control = CMOS_READ(RTC_CONTROL); +} + +static int cmos_read_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct cmos_rtc *cmos = dev_get_drvdata(dev); + struct cmos_read_alarm_callback_param p = { + .cmos = cmos, + .time = &t->time, + }; + + /* This not only a rtc_op, but also called directly */ + if (cmos_no_alarm(cmos)) + return -ETIMEDOUT; + + /* Basic alarms only support hour, minute, and seconds fields. + * Some also support day and month, for alarms up to a year in + * the future. + */ + + /* Some Intel chipsets disconnect the alarm registers when the clock + * update is in progress - during this time reads return bogus values + * and writes may fail silently. See for example "7th Generation Intel® + * Processor Family I/O for U/Y Platforms [...] Datasheet", section + * 27.7.1 + * + * Use the mc146818_avoid_UIP() function to avoid this. + */ + if (!mc146818_avoid_UIP(cmos_read_alarm_callback, 10, &p)) + return -EIO; + + if (!(p.rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + if (((unsigned)t->time.tm_sec) < 0x60) + t->time.tm_sec = bcd2bin(t->time.tm_sec); + else + t->time.tm_sec = -1; + if (((unsigned)t->time.tm_min) < 0x60) + t->time.tm_min = bcd2bin(t->time.tm_min); + else + t->time.tm_min = -1; + if (((unsigned)t->time.tm_hour) < 0x24) + t->time.tm_hour = bcd2bin(t->time.tm_hour); + else + t->time.tm_hour = -1; + + if (cmos->day_alrm) { + if (((unsigned)t->time.tm_mday) <= 0x31) + t->time.tm_mday = bcd2bin(t->time.tm_mday); + else + t->time.tm_mday = -1; + + if (cmos->mon_alrm) { + if (((unsigned)t->time.tm_mon) <= 0x12) + t->time.tm_mon = bcd2bin(t->time.tm_mon)-1; + else + t->time.tm_mon = -1; + } + } + } + + t->enabled = !!(p.rtc_control & RTC_AIE); + t->pending = 0; + + return 0; +} + +static void cmos_checkintr(struct cmos_rtc *cmos, unsigned char rtc_control) +{ + unsigned char rtc_intr; + + /* NOTE after changing RTC_xIE bits we always read INTR_FLAGS; + * allegedly some older rtcs need that to handle irqs properly + */ + rtc_intr = CMOS_READ(RTC_INTR_FLAGS); + + if (use_hpet_alarm()) + return; + + rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; + if (is_intr(rtc_intr)) + rtc_update_irq(cmos->rtc, 1, rtc_intr); +} + +static void cmos_irq_enable(struct cmos_rtc *cmos, unsigned char mask) +{ + unsigned char rtc_control; + + /* flush any pending IRQ status, notably for update irqs, + * before we enable new IRQs + */ + rtc_control = CMOS_READ(RTC_CONTROL); + cmos_checkintr(cmos, rtc_control); + + rtc_control |= mask; + CMOS_WRITE(rtc_control, RTC_CONTROL); + if (use_hpet_alarm()) + hpet_set_rtc_irq_bit(mask); + + if ((mask & RTC_AIE) && cmos_use_acpi_alarm()) { + if (cmos->wake_on) + cmos->wake_on(cmos->dev); + } + + cmos_checkintr(cmos, rtc_control); +} + +static void cmos_irq_disable(struct cmos_rtc *cmos, unsigned char mask) +{ + unsigned char rtc_control; + + rtc_control = CMOS_READ(RTC_CONTROL); + rtc_control &= ~mask; + CMOS_WRITE(rtc_control, RTC_CONTROL); + if (use_hpet_alarm()) + hpet_mask_rtc_irq_bit(mask); + + if ((mask & RTC_AIE) && cmos_use_acpi_alarm()) { + if (cmos->wake_off) + cmos->wake_off(cmos->dev); + } + + cmos_checkintr(cmos, rtc_control); +} + +static int cmos_validate_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct cmos_rtc *cmos = dev_get_drvdata(dev); + struct rtc_time now; + + cmos_read_time(dev, &now); + + if (!cmos->day_alrm) { + time64_t t_max_date; + time64_t t_alrm; + + t_max_date = rtc_tm_to_time64(&now); + t_max_date += 24 * 60 * 60 - 1; + t_alrm = rtc_tm_to_time64(&t->time); + if (t_alrm > t_max_date) { + dev_err(dev, + "Alarms can be up to one day in the future\n"); + return -EINVAL; + } + } else if (!cmos->mon_alrm) { + struct rtc_time max_date = now; + time64_t t_max_date; + time64_t t_alrm; + int max_mday; + + if (max_date.tm_mon == 11) { + max_date.tm_mon = 0; + max_date.tm_year += 1; + } else { + max_date.tm_mon += 1; + } + max_mday = rtc_month_days(max_date.tm_mon, max_date.tm_year); + if (max_date.tm_mday > max_mday) + max_date.tm_mday = max_mday; + + t_max_date = rtc_tm_to_time64(&max_date); + t_max_date -= 1; + t_alrm = rtc_tm_to_time64(&t->time); + if (t_alrm > t_max_date) { + dev_err(dev, + "Alarms can be up to one month in the future\n"); + return -EINVAL; + } + } else { + struct rtc_time max_date = now; + time64_t t_max_date; + time64_t t_alrm; + int max_mday; + + max_date.tm_year += 1; + max_mday = rtc_month_days(max_date.tm_mon, max_date.tm_year); + if (max_date.tm_mday > max_mday) + max_date.tm_mday = max_mday; + + t_max_date = rtc_tm_to_time64(&max_date); + t_max_date -= 1; + t_alrm = rtc_tm_to_time64(&t->time); + if (t_alrm > t_max_date) { + dev_err(dev, + "Alarms can be up to one year in the future\n"); + return -EINVAL; + } + } + + return 0; +} + +struct cmos_set_alarm_callback_param { + struct cmos_rtc *cmos; + unsigned char mon, mday, hrs, min, sec; + struct rtc_wkalrm *t; +}; + +/* Note: this function may be executed by mc146818_avoid_UIP() more then + * once + */ +static void cmos_set_alarm_callback(unsigned char __always_unused seconds, + void *param_in) +{ + struct cmos_set_alarm_callback_param *p = + (struct cmos_set_alarm_callback_param *)param_in; + + /* next rtc irq must not be from previous alarm setting */ + cmos_irq_disable(p->cmos, RTC_AIE); + + /* update alarm */ + CMOS_WRITE(p->hrs, RTC_HOURS_ALARM); + CMOS_WRITE(p->min, RTC_MINUTES_ALARM); + CMOS_WRITE(p->sec, RTC_SECONDS_ALARM); + + /* the system may support an "enhanced" alarm */ + if (p->cmos->day_alrm) { + CMOS_WRITE(p->mday, p->cmos->day_alrm); + if (p->cmos->mon_alrm) + CMOS_WRITE(p->mon, p->cmos->mon_alrm); + } + + if (use_hpet_alarm()) { + /* + * FIXME the HPET alarm glue currently ignores day_alrm + * and mon_alrm ... + */ + hpet_set_alarm_time(p->t->time.tm_hour, p->t->time.tm_min, + p->t->time.tm_sec); + } + + if (p->t->enabled) + cmos_irq_enable(p->cmos, RTC_AIE); +} + +static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct cmos_rtc *cmos = dev_get_drvdata(dev); + struct cmos_set_alarm_callback_param p = { + .cmos = cmos, + .t = t + }; + unsigned char rtc_control; + int ret; + + /* This not only a rtc_op, but also called directly */ + if (cmos_no_alarm(cmos)) + return -EIO; + + ret = cmos_validate_alarm(dev, t); + if (ret < 0) + return ret; + + p.mon = t->time.tm_mon + 1; + p.mday = t->time.tm_mday; + p.hrs = t->time.tm_hour; + p.min = t->time.tm_min; + p.sec = t->time.tm_sec; + + spin_lock_irq(&rtc_lock); + rtc_control = CMOS_READ(RTC_CONTROL); + spin_unlock_irq(&rtc_lock); + + if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + /* Writing 0xff means "don't care" or "match all". */ + p.mon = (p.mon <= 12) ? bin2bcd(p.mon) : 0xff; + p.mday = (p.mday >= 1 && p.mday <= 31) ? bin2bcd(p.mday) : 0xff; + p.hrs = (p.hrs < 24) ? bin2bcd(p.hrs) : 0xff; + p.min = (p.min < 60) ? bin2bcd(p.min) : 0xff; + p.sec = (p.sec < 60) ? bin2bcd(p.sec) : 0xff; + } + + /* + * Some Intel chipsets disconnect the alarm registers when the clock + * update is in progress - during this time writes fail silently. + * + * Use mc146818_avoid_UIP() to avoid this. + */ + if (!mc146818_avoid_UIP(cmos_set_alarm_callback, 10, &p)) + return -ETIMEDOUT; + + cmos->alarm_expires = rtc_tm_to_time64(&t->time); + + return 0; +} + +static int cmos_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct cmos_rtc *cmos = dev_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&rtc_lock, flags); + + if (enabled) + cmos_irq_enable(cmos, RTC_AIE); + else + cmos_irq_disable(cmos, RTC_AIE); + + spin_unlock_irqrestore(&rtc_lock, flags); + return 0; +} + +#if IS_ENABLED(CONFIG_RTC_INTF_PROC) + +static int cmos_procfs(struct device *dev, struct seq_file *seq) +{ + struct cmos_rtc *cmos = dev_get_drvdata(dev); + unsigned char rtc_control, valid; + + spin_lock_irq(&rtc_lock); + rtc_control = CMOS_READ(RTC_CONTROL); + valid = CMOS_READ(RTC_VALID); + spin_unlock_irq(&rtc_lock); + + /* NOTE: at least ICH6 reports battery status using a different + * (non-RTC) bit; and SQWE is ignored on many current systems. + */ + seq_printf(seq, + "periodic_IRQ\t: %s\n" + "update_IRQ\t: %s\n" + "HPET_emulated\t: %s\n" + // "square_wave\t: %s\n" + "BCD\t\t: %s\n" + "DST_enable\t: %s\n" + "periodic_freq\t: %d\n" + "batt_status\t: %s\n", + (rtc_control & RTC_PIE) ? "yes" : "no", + (rtc_control & RTC_UIE) ? "yes" : "no", + use_hpet_alarm() ? "yes" : "no", + // (rtc_control & RTC_SQWE) ? "yes" : "no", + (rtc_control & RTC_DM_BINARY) ? "no" : "yes", + (rtc_control & RTC_DST_EN) ? "yes" : "no", + cmos->rtc->irq_freq, + (valid & RTC_VRT) ? "okay" : "dead"); + + return 0; +} + +#else +#define cmos_procfs NULL +#endif + +static const struct rtc_class_ops cmos_rtc_ops = { + .read_time = cmos_read_time, + .set_time = cmos_set_time, + .read_alarm = cmos_read_alarm, + .set_alarm = cmos_set_alarm, + .proc = cmos_procfs, + .alarm_irq_enable = cmos_alarm_irq_enable, +}; + +/*----------------------------------------------------------------*/ + +/* + * All these chips have at least 64 bytes of address space, shared by + * RTC registers and NVRAM. Most of those bytes of NVRAM are used + * by boot firmware. Modern chips have 128 or 256 bytes. + */ + +#define NVRAM_OFFSET (RTC_REG_D + 1) + +static int cmos_nvram_read(void *priv, unsigned int off, void *val, + size_t count) +{ + unsigned char *buf = val; + + off += NVRAM_OFFSET; + for (; count; count--, off++, buf++) { + guard(spinlock_irq)(&rtc_lock); + if (off < 128) + *buf = CMOS_READ(off); + else if (can_bank2) + *buf = cmos_read_bank2(off); + else + return -EIO; + } + + return 0; +} + +static int cmos_nvram_write(void *priv, unsigned int off, void *val, + size_t count) +{ + struct cmos_rtc *cmos = priv; + unsigned char *buf = val; + + /* NOTE: on at least PCs and Ataris, the boot firmware uses a + * checksum on part of the NVRAM data. That's currently ignored + * here. If userspace is smart enough to know what fields of + * NVRAM to update, updating checksums is also part of its job. + */ + off += NVRAM_OFFSET; + for (; count; count--, off++, buf++) { + /* don't trash RTC registers */ + if (off == cmos->day_alrm + || off == cmos->mon_alrm + || off == cmos->century) + continue; + + guard(spinlock_irq)(&rtc_lock); + if (off < 128) + CMOS_WRITE(*buf, off); + else if (can_bank2) + cmos_write_bank2(*buf, off); + else + return -EIO; + } + + return 0; +} + +/*----------------------------------------------------------------*/ + +static struct cmos_rtc cmos_rtc; + +static irqreturn_t cmos_interrupt(int irq, void *p) +{ + u8 irqstat; + u8 rtc_control; + unsigned long flags; + + /* We cannot use spin_lock() here, as cmos_interrupt() is also called + * in a non-irq context. + */ + spin_lock_irqsave(&rtc_lock, flags); + + /* When the HPET interrupt handler calls us, the interrupt + * status is passed as arg1 instead of the irq number. But + * always clear irq status, even when HPET is in the way. + * + * Note that HPET and RTC are almost certainly out of phase, + * giving different IRQ status ... + */ + irqstat = CMOS_READ(RTC_INTR_FLAGS); + rtc_control = CMOS_READ(RTC_CONTROL); + if (use_hpet_alarm()) + irqstat = (unsigned long)irq & 0xF0; + + /* If we were suspended, RTC_CONTROL may not be accurate since the + * bios may have cleared it. + */ + if (!cmos_rtc.suspend_ctrl) + irqstat &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; + else + irqstat &= (cmos_rtc.suspend_ctrl & RTC_IRQMASK) | RTC_IRQF; + + /* All Linux RTC alarms should be treated as if they were oneshot. + * Similar code may be needed in system wakeup paths, in case the + * alarm woke the system. + */ + if (irqstat & RTC_AIE) { + cmos_rtc.suspend_ctrl &= ~RTC_AIE; + rtc_control &= ~RTC_AIE; + CMOS_WRITE(rtc_control, RTC_CONTROL); + if (use_hpet_alarm()) + hpet_mask_rtc_irq_bit(RTC_AIE); + CMOS_READ(RTC_INTR_FLAGS); + } + spin_unlock_irqrestore(&rtc_lock, flags); + + if (is_intr(irqstat)) { + rtc_update_irq(p, 1, irqstat); + return IRQ_HANDLED; + } else + return IRQ_NONE; +} + +#ifdef CONFIG_ACPI + +#include + +static u32 rtc_handler(void *context) +{ + struct device *dev = context; + struct cmos_rtc *cmos = dev_get_drvdata(dev); + unsigned char rtc_control = 0; + unsigned char rtc_intr; + unsigned long flags; + + + /* + * Always update rtc irq when ACPI is used as RTC Alarm. + * Or else, ACPI SCI is enabled during suspend/resume only, + * update rtc irq in that case. + */ + if (cmos_use_acpi_alarm()) + cmos_interrupt(0, (void *)cmos->rtc); + else { + /* Fix me: can we use cmos_interrupt() here as well? */ + spin_lock_irqsave(&rtc_lock, flags); + if (cmos_rtc.suspend_ctrl) + rtc_control = CMOS_READ(RTC_CONTROL); + if (rtc_control & RTC_AIE) { + cmos_rtc.suspend_ctrl &= ~RTC_AIE; + CMOS_WRITE(rtc_control, RTC_CONTROL); + rtc_intr = CMOS_READ(RTC_INTR_FLAGS); + rtc_update_irq(cmos->rtc, 1, rtc_intr); + } + spin_unlock_irqrestore(&rtc_lock, flags); + } + + pm_wakeup_hard_event(dev); + acpi_clear_event(ACPI_EVENT_RTC); + acpi_disable_event(ACPI_EVENT_RTC, 0); + return ACPI_INTERRUPT_HANDLED; +} + +static void acpi_rtc_event_setup(struct device *dev) +{ + if (acpi_disabled) + return; + + acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, dev); + /* + * After the RTC handler is installed, the Fixed_RTC event should + * be disabled. Only when the RTC alarm is set will it be enabled. + */ + acpi_clear_event(ACPI_EVENT_RTC); + acpi_disable_event(ACPI_EVENT_RTC, 0); +} + +static void acpi_rtc_event_cleanup(void) +{ + if (acpi_disabled) + return; + + acpi_remove_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler); +} + +static void rtc_wake_on(struct device *dev) +{ + acpi_clear_event(ACPI_EVENT_RTC); + acpi_enable_event(ACPI_EVENT_RTC, 0); +} + +static void rtc_wake_off(struct device *dev) +{ + acpi_disable_event(ACPI_EVENT_RTC, 0); +} + +#ifdef CONFIG_X86 +static void use_acpi_alarm_quirks(void) +{ + if (acpi_gbl_FADT.flags & ACPI_FADT_FIXED_RTC) + return; + + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_INTEL: + if (dmi_get_bios_year() < 2015) + return; + break; + case X86_VENDOR_AMD: + case X86_VENDOR_HYGON: + if (dmi_get_bios_year() < 2021) + return; + break; + default: + return; + } + + use_acpi_alarm = true; +} +#else +static inline void use_acpi_alarm_quirks(void) { } +#endif + +static void acpi_cmos_wake_setup(struct device *dev) +{ + if (acpi_disabled) + return; + + use_acpi_alarm_quirks(); + + cmos_rtc.wake_on = rtc_wake_on; + cmos_rtc.wake_off = rtc_wake_off; + + /* ACPI tables bug workaround. */ + if (acpi_gbl_FADT.month_alarm && !acpi_gbl_FADT.day_alarm) { + dev_dbg(dev, "bogus FADT month_alarm (%d)\n", + acpi_gbl_FADT.month_alarm); + acpi_gbl_FADT.month_alarm = 0; + } + + cmos_rtc.day_alrm = acpi_gbl_FADT.day_alarm; + cmos_rtc.mon_alrm = acpi_gbl_FADT.month_alarm; + cmos_rtc.century = acpi_gbl_FADT.century; + + if (acpi_gbl_FADT.flags & ACPI_FADT_S4_RTC_WAKE) + dev_info(dev, "RTC can wake from S4\n"); + + /* RTC always wakes from S1/S2/S3, and often S4/STD */ + device_init_wakeup(dev, true); +} + +static void cmos_check_acpi_rtc_status(struct device *dev, + unsigned char *rtc_control) +{ + struct cmos_rtc *cmos = dev_get_drvdata(dev); + acpi_event_status rtc_status; + acpi_status status; + + if (acpi_gbl_FADT.flags & ACPI_FADT_FIXED_RTC) + return; + + status = acpi_get_event_status(ACPI_EVENT_RTC, &rtc_status); + if (ACPI_FAILURE(status)) { + dev_err(dev, "Could not get RTC status\n"); + } else if (rtc_status & ACPI_EVENT_FLAG_SET) { + unsigned char mask; + *rtc_control &= ~RTC_AIE; + CMOS_WRITE(*rtc_control, RTC_CONTROL); + mask = CMOS_READ(RTC_INTR_FLAGS); + rtc_update_irq(cmos->rtc, 1, mask); + } +} + +#else /* !CONFIG_ACPI */ + +static inline void acpi_rtc_event_setup(struct device *dev) +{ +} + +static inline void acpi_rtc_event_cleanup(void) +{ +} + +static inline void acpi_cmos_wake_setup(struct device *dev) +{ +} + +static inline void cmos_check_acpi_rtc_status(struct device *dev, + unsigned char *rtc_control) +{ +} +#endif /* CONFIG_ACPI */ + +#ifdef CONFIG_PNP +#define INITSECTION + +#else +#define INITSECTION __init +#endif + +#define SECS_PER_DAY (24 * 60 * 60) +#define SECS_PER_MONTH (28 * SECS_PER_DAY) +#define SECS_PER_YEAR (365 * SECS_PER_DAY) + +static int INITSECTION +cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) +{ + struct cmos_rtc_board_info *info = dev_get_platdata(dev); + int retval = 0; + unsigned char rtc_control; + unsigned address_space; + u32 flags = 0; + bool hpet_registered = false; + struct nvmem_config nvmem_cfg = { + .name = "cmos_nvram", + .word_size = 1, + .stride = 1, + .reg_read = cmos_nvram_read, + .reg_write = cmos_nvram_write, + .priv = &cmos_rtc, + }; + + /* there can be only one ... */ + if (cmos_rtc.dev) + return -EBUSY; + + if (!ports) + return -ENODEV; + + /* Claim I/O ports ASAP, minimizing conflict with legacy driver. + * + * REVISIT non-x86 systems may instead use memory space resources + * (needing ioremap etc), not i/o space resources like this ... + */ + if (RTC_IOMAPPED) + ports = request_region(ports->start, resource_size(ports), + driver_name); + else + ports = request_mem_region(ports->start, resource_size(ports), + driver_name); + if (!ports) { + dev_dbg(dev, "i/o registers already in use\n"); + return -EBUSY; + } + + cmos_rtc.irq = rtc_irq; + cmos_rtc.iomem = ports; + + /* Heuristic to deduce NVRAM size ... do what the legacy NVRAM + * driver did, but don't reject unknown configs. Old hardware + * won't address 128 bytes. Newer chips have multiple banks, + * though they may not be listed in one I/O resource. + */ +#if defined(CONFIG_ATARI) + address_space = 64; +#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__) \ + || defined(__sparc__) || defined(__mips__) \ + || defined(__powerpc__) + address_space = 128; +#else +#warning Assuming 128 bytes of RTC+NVRAM address space, not 64 bytes. + address_space = 128; +#endif + if (can_bank2 && ports->end > (ports->start + 1)) + address_space = 256; + + /* For ACPI systems extension info comes from the FADT. On others, + * board specific setup provides it as appropriate. Systems where + * the alarm IRQ isn't automatically a wakeup IRQ (like ACPI, and + * some almost-clones) can provide hooks to make that behave. + * + * Note that ACPI doesn't preclude putting these registers into + * "extended" areas of the chip, including some that we won't yet + * expect CMOS_READ and friends to handle. + */ + if (info) { + if (info->flags) + flags = info->flags; + if (info->address_space) + address_space = info->address_space; + + cmos_rtc.day_alrm = info->rtc_day_alarm; + cmos_rtc.mon_alrm = info->rtc_mon_alarm; + cmos_rtc.century = info->rtc_century; + + if (info->wake_on && info->wake_off) { + cmos_rtc.wake_on = info->wake_on; + cmos_rtc.wake_off = info->wake_off; + } + } else { + acpi_cmos_wake_setup(dev); + } + + if (cmos_rtc.day_alrm >= 128) + cmos_rtc.day_alrm = 0; + + if (cmos_rtc.mon_alrm >= 128) + cmos_rtc.mon_alrm = 0; + + if (cmos_rtc.century >= 128) + cmos_rtc.century = 0; + + cmos_rtc.dev = dev; + dev_set_drvdata(dev, &cmos_rtc); + + cmos_rtc.rtc = devm_rtc_allocate_device(dev); + if (IS_ERR(cmos_rtc.rtc)) { + retval = PTR_ERR(cmos_rtc.rtc); + goto cleanup0; + } + + if (cmos_rtc.mon_alrm) + cmos_rtc.rtc->alarm_offset_max = SECS_PER_YEAR - 1; + else if (cmos_rtc.day_alrm) + cmos_rtc.rtc->alarm_offset_max = SECS_PER_MONTH - 1; + else + cmos_rtc.rtc->alarm_offset_max = SECS_PER_DAY - 1; + + rename_region(ports, dev_name(&cmos_rtc.rtc->dev)); + + if (!mc146818_does_rtc_work()) { + dev_warn(dev, "broken or not accessible\n"); + retval = -ENXIO; + goto cleanup1; + } + + spin_lock_irq(&rtc_lock); + + if (!(flags & CMOS_RTC_FLAGS_NOFREQ)) { + /* force periodic irq to CMOS reset default of 1024Hz; + * + * REVISIT it's been reported that at least one x86_64 ALI + * mobo doesn't use 32KHz here ... for portability we might + * need to do something about other clock frequencies. + */ + cmos_rtc.rtc->irq_freq = 1024; + if (use_hpet_alarm()) + hpet_set_periodic_freq(cmos_rtc.rtc->irq_freq); + CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT); + } + + /* disable irqs */ + if (is_valid_irq(rtc_irq)) + cmos_irq_disable(&cmos_rtc, RTC_PIE | RTC_AIE | RTC_UIE); + + rtc_control = CMOS_READ(RTC_CONTROL); + + spin_unlock_irq(&rtc_lock); + + if (is_valid_irq(rtc_irq) && !(rtc_control & RTC_24H)) { + dev_warn(dev, "only 24-hr supported\n"); + retval = -ENXIO; + goto cleanup1; + } + + if (use_hpet_alarm()) + hpet_rtc_timer_init(); + + if (is_valid_irq(rtc_irq)) { + irq_handler_t rtc_cmos_int_handler; + + if (use_hpet_alarm()) { + rtc_cmos_int_handler = hpet_rtc_interrupt; + retval = hpet_register_irq_handler(cmos_interrupt); + if (retval) { + hpet_mask_rtc_irq_bit(RTC_IRQMASK); + dev_warn(dev, "hpet_register_irq_handler " + " failed in rtc_init()."); + goto cleanup1; + } + hpet_registered = true; + } else + rtc_cmos_int_handler = cmos_interrupt; + + retval = request_irq(rtc_irq, rtc_cmos_int_handler, + 0, dev_name(&cmos_rtc.rtc->dev), + cmos_rtc.rtc); + if (retval < 0) { + dev_dbg(dev, "IRQ %d is already in use\n", rtc_irq); + goto cleanup1; + } + } else if (!cmos_use_acpi_alarm()) { + clear_bit(RTC_FEATURE_ALARM, cmos_rtc.rtc->features); + } + + cmos_rtc.rtc->ops = &cmos_rtc_ops; + + retval = devm_rtc_register_device(cmos_rtc.rtc); + if (retval) + goto cleanup2; + + /* Set the sync offset for the periodic 11min update correct */ + cmos_rtc.rtc->set_offset_nsec = NSEC_PER_SEC / 2; + + /* export at least the first block of NVRAM */ + nvmem_cfg.size = address_space - NVRAM_OFFSET; + devm_rtc_nvmem_register(cmos_rtc.rtc, &nvmem_cfg); + + /* + * Everything has gone well so far, so by default register a handler for + * the ACPI RTC fixed event. + */ + if (!info) + acpi_rtc_event_setup(dev); + + dev_info(dev, "%s%s, %d bytes nvram%s\n", + cmos_no_alarm(&cmos_rtc) ? "no alarms" : + cmos_rtc.mon_alrm ? "alarms up to one year" : + cmos_rtc.day_alrm ? "alarms up to one month" : + "alarms up to one day", + cmos_rtc.century ? ", y3k" : "", + nvmem_cfg.size, + use_hpet_alarm() ? ", hpet irqs" : ""); + + return 0; + +cleanup2: + if (is_valid_irq(rtc_irq)) + free_irq(rtc_irq, cmos_rtc.rtc); +cleanup1: + if (hpet_registered) { + hpet_mask_rtc_irq_bit(RTC_IRQMASK); + hpet_unregister_irq_handler(cmos_interrupt); + } + cmos_rtc.dev = NULL; +cleanup0: + if (RTC_IOMAPPED) + release_region(ports->start, resource_size(ports)); + else + release_mem_region(ports->start, resource_size(ports)); + return retval; +} + +static void cmos_do_shutdown(int rtc_irq) +{ + spin_lock_irq(&rtc_lock); + if (!cmos_no_alarm(&cmos_rtc)) + cmos_irq_disable(&cmos_rtc, RTC_IRQMASK); + spin_unlock_irq(&rtc_lock); +} + +static void cmos_do_remove(struct device *dev) +{ + struct cmos_rtc *cmos = dev_get_drvdata(dev); + struct resource *ports; + + cmos_do_shutdown(cmos->irq); + + if (is_valid_irq(cmos->irq)) { + free_irq(cmos->irq, cmos->rtc); + if (use_hpet_alarm()) + hpet_unregister_irq_handler(cmos_interrupt); + } + + if (!dev_get_platdata(dev)) + acpi_rtc_event_cleanup(); + + cmos->rtc = NULL; + + ports = cmos->iomem; + if (RTC_IOMAPPED) + release_region(ports->start, resource_size(ports)); + else + release_mem_region(ports->start, resource_size(ports)); + cmos->iomem = NULL; + + cmos->dev = NULL; +} + +static int cmos_aie_poweroff(struct device *dev) +{ + struct cmos_rtc *cmos = dev_get_drvdata(dev); + struct rtc_time now; + time64_t t_now; + int retval = 0; + unsigned char rtc_control; + + if (!cmos->alarm_expires) + return -EINVAL; + + spin_lock_irq(&rtc_lock); + rtc_control = CMOS_READ(RTC_CONTROL); + spin_unlock_irq(&rtc_lock); + + /* We only care about the situation where AIE is disabled. */ + if (rtc_control & RTC_AIE) + return -EBUSY; + + cmos_read_time(dev, &now); + t_now = rtc_tm_to_time64(&now); + + /* + * When enabling "RTC wake-up" in BIOS setup, the machine reboots + * automatically right after shutdown on some buggy boxes. + * This automatic rebooting issue won't happen when the alarm + * time is larger than now+1 seconds. + * + * If the alarm time is equal to now+1 seconds, the issue can be + * prevented by cancelling the alarm. + */ + if (cmos->alarm_expires == t_now + 1) { + struct rtc_wkalrm alarm; + + /* Cancel the AIE timer by configuring the past time. */ + rtc_time64_to_tm(t_now - 1, &alarm.time); + alarm.enabled = 0; + retval = cmos_set_alarm(dev, &alarm); + } else if (cmos->alarm_expires > t_now + 1) { + retval = -EBUSY; + } + + return retval; +} + +static int cmos_suspend(struct device *dev) +{ + struct cmos_rtc *cmos = dev_get_drvdata(dev); + unsigned char tmp; + + /* only the alarm might be a wakeup event source */ + spin_lock_irq(&rtc_lock); + cmos->suspend_ctrl = tmp = CMOS_READ(RTC_CONTROL); + if (tmp & (RTC_PIE|RTC_AIE|RTC_UIE)) { + unsigned char mask; + + if (device_may_wakeup(dev)) + mask = RTC_IRQMASK & ~RTC_AIE; + else + mask = RTC_IRQMASK; + tmp &= ~mask; + CMOS_WRITE(tmp, RTC_CONTROL); + if (use_hpet_alarm()) + hpet_mask_rtc_irq_bit(mask); + cmos_checkintr(cmos, tmp); + } + spin_unlock_irq(&rtc_lock); + + if ((tmp & RTC_AIE) && !cmos_use_acpi_alarm()) { + cmos->enabled_wake = 1; + if (cmos->wake_on) + cmos->wake_on(dev); + else + enable_irq_wake(cmos->irq); + } + + memset(&cmos->saved_wkalrm, 0, sizeof(struct rtc_wkalrm)); + cmos_read_alarm(dev, &cmos->saved_wkalrm); + + dev_dbg(dev, "suspend%s, ctrl %02x\n", + (tmp & RTC_AIE) ? ", alarm may wake" : "", + tmp); + + return 0; +} + +/* We want RTC alarms to wake us from e.g. ACPI G2/S5 "soft off", even + * after a detour through G3 "mechanical off", although the ACPI spec + * says wakeup should only work from G1/S4 "hibernate". To most users, + * distinctions between S4 and S5 are pointless. So when the hardware + * allows, don't draw that distinction. + */ +static inline int cmos_poweroff(struct device *dev) +{ + if (!IS_ENABLED(CONFIG_PM)) + return -ENOSYS; + + return cmos_suspend(dev); +} + +static void cmos_check_wkalrm(struct device *dev) +{ + struct cmos_rtc *cmos = dev_get_drvdata(dev); + struct rtc_wkalrm current_alarm; + time64_t t_now; + time64_t t_current_expires; + time64_t t_saved_expires; + struct rtc_time now; + + /* Check if we have RTC Alarm armed */ + if (!(cmos->suspend_ctrl & RTC_AIE)) + return; + + cmos_read_time(dev, &now); + t_now = rtc_tm_to_time64(&now); + + /* + * ACPI RTC wake event is cleared after resume from STR, + * ACK the rtc irq here + */ + if (t_now >= cmos->alarm_expires && cmos_use_acpi_alarm()) { + cmos_interrupt(0, (void *)cmos->rtc); + return; + } + + memset(¤t_alarm, 0, sizeof(struct rtc_wkalrm)); + cmos_read_alarm(dev, ¤t_alarm); + t_current_expires = rtc_tm_to_time64(¤t_alarm.time); + t_saved_expires = rtc_tm_to_time64(&cmos->saved_wkalrm.time); + if (t_current_expires != t_saved_expires || + cmos->saved_wkalrm.enabled != current_alarm.enabled) { + cmos_set_alarm(dev, &cmos->saved_wkalrm); + } +} + +static int __maybe_unused cmos_resume(struct device *dev) +{ + struct cmos_rtc *cmos = dev_get_drvdata(dev); + unsigned char tmp; + + if (cmos->enabled_wake && !cmos_use_acpi_alarm()) { + if (cmos->wake_off) + cmos->wake_off(dev); + else + disable_irq_wake(cmos->irq); + cmos->enabled_wake = 0; + } + + /* The BIOS might have changed the alarm, restore it */ + cmos_check_wkalrm(dev); + + spin_lock_irq(&rtc_lock); + tmp = cmos->suspend_ctrl; + cmos->suspend_ctrl = 0; + /* re-enable any irqs previously active */ + if (tmp & RTC_IRQMASK) { + unsigned char mask; + + if (device_may_wakeup(dev) && use_hpet_alarm()) + hpet_rtc_timer_init(); + + do { + CMOS_WRITE(tmp, RTC_CONTROL); + if (use_hpet_alarm()) + hpet_set_rtc_irq_bit(tmp & RTC_IRQMASK); + + mask = CMOS_READ(RTC_INTR_FLAGS); + mask &= (tmp & RTC_IRQMASK) | RTC_IRQF; + if (!use_hpet_alarm() || !is_intr(mask)) + break; + + /* force one-shot behavior if HPET blocked + * the wake alarm's irq + */ + rtc_update_irq(cmos->rtc, 1, mask); + tmp &= ~RTC_AIE; + hpet_mask_rtc_irq_bit(RTC_AIE); + } while (mask & RTC_AIE); + + if (tmp & RTC_AIE) + cmos_check_acpi_rtc_status(dev, &tmp); + } + spin_unlock_irq(&rtc_lock); + + dev_dbg(dev, "resume, ctrl %02x\n", tmp); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(cmos_pm_ops, cmos_suspend, cmos_resume); + +#ifdef CONFIG_OF +static const struct of_device_id of_cmos_match[] = { + { + .compatible = "motorola,mc146818", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, of_cmos_match); + +static __init void cmos_of_init(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + const __be32 *val; + + if (!node) + return; + + val = of_get_property(node, "ctrl-reg", NULL); + if (val) + CMOS_WRITE(be32_to_cpup(val), RTC_CONTROL); + + val = of_get_property(node, "freq-reg", NULL); + if (val) + CMOS_WRITE(be32_to_cpup(val), RTC_FREQ_SELECT); +} +#else +static inline void cmos_of_init(struct platform_device *pdev) {} +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id acpi_cmos_rtc_ids[] = { + ACPI_CMOS_RTC_IDS +}; +MODULE_DEVICE_TABLE(acpi, acpi_cmos_rtc_ids); +#endif + +/*----------------------------------------------------------------*/ + +/* Platform setup should have set up an RTC device, when PNP is + * unavailable ... this could happen even on (older) PCs. + */ + +static int __init cmos_platform_probe(struct platform_device *pdev) +{ + struct resource *resource; + int irq; + + cmos_of_init(pdev); + + if (RTC_IOMAPPED) + resource = platform_get_resource(pdev, IORESOURCE_IO, 0); + else + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq_optional(pdev, 0); + if (irq < 0) { + irq = -1; +#ifdef CONFIG_X86 + /* + * On some x86 systems, the IRQ is not defined, but it should + * always be safe to hardcode it on systems with a legacy PIC. + */ + if (nr_legacy_irqs()) + irq = RTC_IRQ; +#endif + } + + return cmos_do_probe(&pdev->dev, resource, irq); +} + +static void cmos_platform_remove(struct platform_device *pdev) +{ + cmos_do_remove(&pdev->dev); +} + +static void cmos_platform_shutdown(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cmos_rtc *cmos = dev_get_drvdata(dev); + + if (system_state == SYSTEM_POWER_OFF) { + int retval = cmos_poweroff(dev); + + if (cmos_aie_poweroff(dev) < 0 && !retval) + return; + } + + cmos_do_shutdown(cmos->irq); +} + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:rtc_cmos"); + +static struct platform_driver cmos_platform_driver = { + .remove = cmos_platform_remove, + .shutdown = cmos_platform_shutdown, + .driver = { + .name = driver_name, + .pm = &cmos_pm_ops, + .of_match_table = of_match_ptr(of_cmos_match), + .acpi_match_table = ACPI_PTR(acpi_cmos_rtc_ids), + } +}; + +static bool platform_driver_registered; + +static int __init cmos_init(void) +{ + int retval; + + if (cmos_rtc.dev) + return 0; + + retval = platform_driver_probe(&cmos_platform_driver, cmos_platform_probe); + if (retval) + return retval; + + platform_driver_registered = true; + + return 0; +} +module_init(cmos_init); + +static void __exit cmos_exit(void) +{ + if (platform_driver_registered) + platform_driver_unregister(&cmos_platform_driver); +} +module_exit(cmos_exit); + + +MODULE_AUTHOR("David Brownell"); +MODULE_DESCRIPTION("Driver for PC-style 'CMOS' RTCs"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-core.h b/drivers/rtc/rtc-core.h new file mode 100644 index 000000000..4b10a1b8f --- /dev/null +++ b/drivers/rtc/rtc-core.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifdef CONFIG_RTC_INTF_DEV + +extern void __init rtc_dev_init(void); +extern void rtc_dev_prepare(struct rtc_device *rtc); + +#else + +static inline void rtc_dev_init(void) +{ +} + +static inline void rtc_dev_prepare(struct rtc_device *rtc) +{ +} + +#endif + +#ifdef CONFIG_RTC_INTF_PROC + +extern void rtc_proc_add_device(struct rtc_device *rtc); +extern void rtc_proc_del_device(struct rtc_device *rtc); + +#else + +static inline void rtc_proc_add_device(struct rtc_device *rtc) +{ +} + +static inline void rtc_proc_del_device(struct rtc_device *rtc) +{ +} + +#endif + +#ifdef CONFIG_RTC_INTF_SYSFS +const struct attribute_group **rtc_get_dev_attribute_groups(void); +#else +static inline const struct attribute_group **rtc_get_dev_attribute_groups(void) +{ + return NULL; +} +#endif diff --git a/drivers/rtc/rtc-cpcap.c b/drivers/rtc/rtc-cpcap.c new file mode 100644 index 000000000..a7db3173d --- /dev/null +++ b/drivers/rtc/rtc-cpcap.c @@ -0,0 +1,324 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Motorola CPCAP PMIC RTC driver + * + * Based on cpcap-regulator.c from Motorola Linux kernel tree + * Copyright (C) 2009 Motorola, Inc. + * + * Rewritten for mainline kernel + * - use DT + * - use regmap + * - use standard interrupt framework + * - use managed device resources + * - remove custom "secure clock daemon" helpers + * + * Copyright (C) 2017 Sebastian Reichel + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SECS_PER_DAY 86400 +#define DAY_MASK 0x7FFF +#define TOD1_MASK 0x00FF +#define TOD2_MASK 0x01FF + +struct cpcap_time { + int day; + int tod1; + int tod2; +}; + +struct cpcap_rtc { + struct regmap *regmap; + struct rtc_device *rtc_dev; + u16 vendor; + int alarm_irq; + bool alarm_enabled; + int update_irq; + bool update_enabled; +}; + +static void cpcap2rtc_time(struct rtc_time *rtc, struct cpcap_time *cpcap) +{ + unsigned long int tod; + unsigned long int time; + + tod = (cpcap->tod1 & TOD1_MASK) | ((cpcap->tod2 & TOD2_MASK) << 8); + time = tod + ((cpcap->day & DAY_MASK) * SECS_PER_DAY); + + rtc_time64_to_tm(time, rtc); +} + +static void rtc2cpcap_time(struct cpcap_time *cpcap, struct rtc_time *rtc) +{ + unsigned long time; + + time = rtc_tm_to_time64(rtc); + + cpcap->day = time / SECS_PER_DAY; + time %= SECS_PER_DAY; + cpcap->tod2 = (time >> 8) & TOD2_MASK; + cpcap->tod1 = time & TOD1_MASK; +} + +static int cpcap_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct cpcap_rtc *rtc = dev_get_drvdata(dev); + + if (rtc->alarm_enabled == enabled) + return 0; + + if (enabled) + enable_irq(rtc->alarm_irq); + else + disable_irq(rtc->alarm_irq); + + rtc->alarm_enabled = !!enabled; + + return 0; +} + +static int cpcap_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct cpcap_rtc *rtc; + struct cpcap_time cpcap_tm; + int temp_tod2; + int ret; + + rtc = dev_get_drvdata(dev); + + ret = regmap_read(rtc->regmap, CPCAP_REG_TOD2, &temp_tod2); + ret |= regmap_read(rtc->regmap, CPCAP_REG_DAY, &cpcap_tm.day); + ret |= regmap_read(rtc->regmap, CPCAP_REG_TOD1, &cpcap_tm.tod1); + ret |= regmap_read(rtc->regmap, CPCAP_REG_TOD2, &cpcap_tm.tod2); + + if (temp_tod2 > cpcap_tm.tod2) + ret |= regmap_read(rtc->regmap, CPCAP_REG_DAY, &cpcap_tm.day); + + if (ret) { + dev_err(dev, "Failed to read time\n"); + return -EIO; + } + + cpcap2rtc_time(tm, &cpcap_tm); + + return 0; +} + +static int cpcap_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct cpcap_rtc *rtc; + struct cpcap_time cpcap_tm; + int ret = 0; + + rtc = dev_get_drvdata(dev); + + rtc2cpcap_time(&cpcap_tm, tm); + + if (rtc->alarm_enabled) + disable_irq(rtc->alarm_irq); + if (rtc->update_enabled) + disable_irq(rtc->update_irq); + + if (rtc->vendor == CPCAP_VENDOR_ST) { + /* The TOD1 and TOD2 registers MUST be written in this order + * for the change to properly set. + */ + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1, + TOD1_MASK, cpcap_tm.tod1); + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD2, + TOD2_MASK, cpcap_tm.tod2); + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_DAY, + DAY_MASK, cpcap_tm.day); + } else { + /* Clearing the upper lower 8 bits of the TOD guarantees that + * the upper half of TOD (TOD2) will not increment for 0xFF RTC + * ticks (255 seconds). During this time we can safely write + * to DAY, TOD2, then TOD1 (in that order) and expect RTC to be + * synchronized to the exact time requested upon the final write + * to TOD1. + */ + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1, + TOD1_MASK, 0); + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_DAY, + DAY_MASK, cpcap_tm.day); + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD2, + TOD2_MASK, cpcap_tm.tod2); + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1, + TOD1_MASK, cpcap_tm.tod1); + } + + if (rtc->update_enabled) + enable_irq(rtc->update_irq); + if (rtc->alarm_enabled) + enable_irq(rtc->alarm_irq); + + return ret; +} + +static int cpcap_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct cpcap_rtc *rtc; + struct cpcap_time cpcap_tm; + int ret; + + rtc = dev_get_drvdata(dev); + + alrm->enabled = rtc->alarm_enabled; + + ret = regmap_read(rtc->regmap, CPCAP_REG_DAYA, &cpcap_tm.day); + ret |= regmap_read(rtc->regmap, CPCAP_REG_TODA2, &cpcap_tm.tod2); + ret |= regmap_read(rtc->regmap, CPCAP_REG_TODA1, &cpcap_tm.tod1); + + if (ret) { + dev_err(dev, "Failed to read time\n"); + return -EIO; + } + + cpcap2rtc_time(&alrm->time, &cpcap_tm); + return rtc_valid_tm(&alrm->time); +} + +static int cpcap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct cpcap_rtc *rtc; + struct cpcap_time cpcap_tm; + int ret; + + rtc = dev_get_drvdata(dev); + + rtc2cpcap_time(&cpcap_tm, &alrm->time); + + if (rtc->alarm_enabled) + disable_irq(rtc->alarm_irq); + + ret = regmap_update_bits(rtc->regmap, CPCAP_REG_DAYA, DAY_MASK, + cpcap_tm.day); + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TODA2, TOD2_MASK, + cpcap_tm.tod2); + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TODA1, TOD1_MASK, + cpcap_tm.tod1); + + if (!ret) { + enable_irq(rtc->alarm_irq); + rtc->alarm_enabled = true; + } + + return ret; +} + +static const struct rtc_class_ops cpcap_rtc_ops = { + .read_time = cpcap_rtc_read_time, + .set_time = cpcap_rtc_set_time, + .read_alarm = cpcap_rtc_read_alarm, + .set_alarm = cpcap_rtc_set_alarm, + .alarm_irq_enable = cpcap_rtc_alarm_irq_enable, +}; + +static irqreturn_t cpcap_rtc_alarm_irq(int irq, void *data) +{ + struct cpcap_rtc *rtc = data; + + rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF); + return IRQ_HANDLED; +} + +static irqreturn_t cpcap_rtc_update_irq(int irq, void *data) +{ + struct cpcap_rtc *rtc = data; + + rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF); + return IRQ_HANDLED; +} + +static int cpcap_rtc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cpcap_rtc *rtc; + int err; + + rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->regmap = dev_get_regmap(dev->parent, NULL); + if (!rtc->regmap) + return -ENODEV; + + platform_set_drvdata(pdev, rtc); + rtc->rtc_dev = devm_rtc_allocate_device(dev); + if (IS_ERR(rtc->rtc_dev)) + return PTR_ERR(rtc->rtc_dev); + + rtc->rtc_dev->ops = &cpcap_rtc_ops; + rtc->rtc_dev->range_max = (timeu64_t) (DAY_MASK + 1) * SECS_PER_DAY - 1; + + err = cpcap_get_vendor(dev, rtc->regmap, &rtc->vendor); + if (err) + return err; + + rtc->alarm_irq = platform_get_irq(pdev, 0); + err = devm_request_threaded_irq(dev, rtc->alarm_irq, NULL, + cpcap_rtc_alarm_irq, + IRQF_TRIGGER_NONE | IRQF_ONESHOT, + "rtc_alarm", rtc); + if (err) { + dev_err(dev, "Could not request alarm irq: %d\n", err); + return err; + } + disable_irq(rtc->alarm_irq); + + /* Stock Android uses the 1 Hz interrupt for "secure clock daemon", + * which is not supported by the mainline kernel. The mainline kernel + * does not use the irq at the moment, but we explicitly request and + * disable it, so that its masked and does not wake up the processor + * every second. + */ + rtc->update_irq = platform_get_irq(pdev, 1); + err = devm_request_threaded_irq(dev, rtc->update_irq, NULL, + cpcap_rtc_update_irq, + IRQF_TRIGGER_NONE | IRQF_ONESHOT, + "rtc_1hz", rtc); + if (err) { + dev_err(dev, "Could not request update irq: %d\n", err); + return err; + } + disable_irq(rtc->update_irq); + + err = device_init_wakeup(dev, true); + if (err) { + dev_err(dev, "wakeup initialization failed (%d)\n", err); + /* ignore error and continue without wakeup support */ + } + + return devm_rtc_register_device(rtc->rtc_dev); +} + +static const struct of_device_id cpcap_rtc_of_match[] = { + { .compatible = "motorola,cpcap-rtc", }, + {}, +}; +MODULE_DEVICE_TABLE(of, cpcap_rtc_of_match); + +static struct platform_driver cpcap_rtc_driver = { + .probe = cpcap_rtc_probe, + .driver = { + .name = "cpcap-rtc", + .of_match_table = cpcap_rtc_of_match, + }, +}; + +module_platform_driver(cpcap_rtc_driver); + +MODULE_DESCRIPTION("CPCAP RTC driver"); +MODULE_AUTHOR("Sebastian Reichel "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-cros-ec.c b/drivers/rtc/rtc-cros-ec.c new file mode 100644 index 000000000..f56650899 --- /dev/null +++ b/drivers/rtc/rtc-cros-ec.c @@ -0,0 +1,409 @@ +// SPDX-License-Identifier: GPL-2.0 +// RTC driver for ChromeOS Embedded Controller. +// +// Copyright (C) 2017 Google, Inc. +// Author: Stephen Barber + +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "cros-ec-rtc" + +#define SECS_PER_DAY (24 * 60 * 60) + +/** + * struct cros_ec_rtc - Driver data for EC RTC + * + * @cros_ec: Pointer to EC device + * @rtc: Pointer to RTC device + * @notifier: Notifier info for responding to EC events + * @saved_alarm: Alarm to restore when interrupts are reenabled + */ +struct cros_ec_rtc { + struct cros_ec_device *cros_ec; + struct rtc_device *rtc; + struct notifier_block notifier; + u32 saved_alarm; +}; + +static int cros_ec_rtc_get(struct cros_ec_device *cros_ec, u32 command, + u32 *response) +{ + DEFINE_RAW_FLEX(struct cros_ec_command, msg, data, + sizeof(struct ec_response_rtc)); + int ret; + + msg->command = command; + msg->insize = sizeof(struct ec_response_rtc); + + ret = cros_ec_cmd_xfer_status(cros_ec, msg); + if (ret < 0) + return ret; + + *response = ((struct ec_response_rtc *)msg->data)->time; + + return 0; +} + +static int cros_ec_rtc_set(struct cros_ec_device *cros_ec, u32 command, + u32 param) +{ + DEFINE_RAW_FLEX(struct cros_ec_command, msg, data, + sizeof(struct ec_response_rtc)); + int ret; + + msg->command = command; + msg->outsize = sizeof(struct ec_response_rtc); + ((struct ec_response_rtc *)msg->data)->time = param; + + ret = cros_ec_cmd_xfer_status(cros_ec, msg); + if (ret < 0) + return ret; + return 0; +} + +/* Read the current time from the EC. */ +static int cros_ec_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev); + struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec; + int ret; + u32 time; + + ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, &time); + if (ret) { + dev_err(dev, "error getting time: %d\n", ret); + return ret; + } + + rtc_time64_to_tm(time, tm); + + return 0; +} + +/* Set the current EC time. */ +static int cros_ec_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev); + struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec; + int ret; + time64_t time = rtc_tm_to_time64(tm); + + ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_VALUE, (u32)time); + if (ret < 0) { + dev_err(dev, "error setting time: %d\n", ret); + return ret; + } + + return 0; +} + +/* Read alarm time from RTC. */ +static int cros_ec_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev); + struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec; + int ret; + u32 current_time, alarm_offset; + + /* + * The EC host command for getting the alarm is relative (i.e. 5 + * seconds from now) whereas rtc_wkalrm is absolute. Get the current + * RTC time first so we can calculate the relative time. + */ + ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, ¤t_time); + if (ret < 0) { + dev_err(dev, "error getting time: %d\n", ret); + return ret; + } + + ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_ALARM, &alarm_offset); + if (ret < 0) { + dev_err(dev, "error getting alarm: %d\n", ret); + return ret; + } + + rtc_time64_to_tm(current_time + alarm_offset, &alrm->time); + + return 0; +} + +/* Set the EC's RTC alarm. */ +static int cros_ec_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev); + struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec; + int ret; + time64_t alarm_time; + u32 current_time, alarm_offset; + + /* + * The EC host command for setting the alarm is relative + * (i.e. 5 seconds from now) whereas rtc_wkalrm is absolute. + * Get the current RTC time first so we can calculate the + * relative time. + */ + ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, ¤t_time); + if (ret < 0) { + dev_err(dev, "error getting time: %d\n", ret); + return ret; + } + + alarm_time = rtc_tm_to_time64(&alrm->time); + + if (alarm_time < 0 || alarm_time > U32_MAX) + return -EINVAL; + + if (!alrm->enabled) { + /* + * If the alarm is being disabled, send an alarm + * clear command. + */ + alarm_offset = EC_RTC_ALARM_CLEAR; + cros_ec_rtc->saved_alarm = (u32)alarm_time; + } else { + /* Don't set an alarm in the past. */ + if ((u32)alarm_time <= current_time) + return -ETIME; + + alarm_offset = (u32)alarm_time - current_time; + } + + ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, alarm_offset); + if (ret < 0) { + dev_err(dev, "error setting alarm in %u seconds: %d\n", + alarm_offset, ret); + /* + * The EC code returns -EINVAL if the alarm time is too + * far in the future. Convert it to the expected error code. + */ + if (ret == -EINVAL) + ret = -ERANGE; + return ret; + } + + return 0; +} + +static int cros_ec_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev); + struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec; + int ret; + u32 current_time, alarm_offset, alarm_value; + + ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, ¤t_time); + if (ret < 0) { + dev_err(dev, "error getting time: %d\n", ret); + return ret; + } + + if (enabled) { + /* Restore saved alarm if it's still in the future. */ + if (cros_ec_rtc->saved_alarm < current_time) + alarm_offset = EC_RTC_ALARM_CLEAR; + else + alarm_offset = cros_ec_rtc->saved_alarm - current_time; + + ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, + alarm_offset); + if (ret < 0) { + dev_err(dev, "error restoring alarm: %d\n", ret); + return ret; + } + } else { + /* Disable alarm, saving the old alarm value. */ + ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_ALARM, + &alarm_offset); + if (ret < 0) { + dev_err(dev, "error saving alarm: %d\n", ret); + return ret; + } + + alarm_value = current_time + alarm_offset; + + /* + * If the current EC alarm is already past, we don't want + * to set an alarm when we go through the alarm irq enable + * path. + */ + if (alarm_value < current_time) + cros_ec_rtc->saved_alarm = EC_RTC_ALARM_CLEAR; + else + cros_ec_rtc->saved_alarm = alarm_value; + + alarm_offset = EC_RTC_ALARM_CLEAR; + ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, + alarm_offset); + if (ret < 0) { + dev_err(dev, "error disabling alarm: %d\n", ret); + return ret; + } + } + + return 0; +} + +static int cros_ec_rtc_event(struct notifier_block *nb, + unsigned long queued_during_suspend, + void *_notify) +{ + struct cros_ec_rtc *cros_ec_rtc; + struct rtc_device *rtc; + struct cros_ec_device *cros_ec; + u32 host_event; + + cros_ec_rtc = container_of(nb, struct cros_ec_rtc, notifier); + rtc = cros_ec_rtc->rtc; + cros_ec = cros_ec_rtc->cros_ec; + + host_event = cros_ec_get_host_event(cros_ec); + if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_RTC)) { + rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF); + return NOTIFY_OK; + } else { + return NOTIFY_DONE; + } +} + +static const struct rtc_class_ops cros_ec_rtc_ops = { + .read_time = cros_ec_rtc_read_time, + .set_time = cros_ec_rtc_set_time, + .read_alarm = cros_ec_rtc_read_alarm, + .set_alarm = cros_ec_rtc_set_alarm, + .alarm_irq_enable = cros_ec_rtc_alarm_irq_enable, +}; + +#ifdef CONFIG_PM_SLEEP +static int cros_ec_rtc_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(&pdev->dev); + + if (device_may_wakeup(dev)) + return enable_irq_wake(cros_ec_rtc->cros_ec->irq); + + return 0; +} + +static int cros_ec_rtc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(&pdev->dev); + + if (device_may_wakeup(dev)) + return disable_irq_wake(cros_ec_rtc->cros_ec->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(cros_ec_rtc_pm_ops, cros_ec_rtc_suspend, + cros_ec_rtc_resume); + +static int cros_ec_rtc_probe(struct platform_device *pdev) +{ + struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent); + struct cros_ec_device *cros_ec = ec_dev->ec_dev; + struct cros_ec_rtc *cros_ec_rtc; + struct rtc_time tm; + int ret; + + cros_ec_rtc = devm_kzalloc(&pdev->dev, sizeof(*cros_ec_rtc), + GFP_KERNEL); + if (!cros_ec_rtc) + return -ENOMEM; + + platform_set_drvdata(pdev, cros_ec_rtc); + cros_ec_rtc->cros_ec = cros_ec; + + /* Get initial time */ + ret = cros_ec_rtc_read_time(&pdev->dev, &tm); + if (ret) { + dev_err(&pdev->dev, "failed to read RTC time\n"); + return ret; + } + + ret = device_init_wakeup(&pdev->dev, true); + if (ret) { + dev_err(&pdev->dev, "failed to initialize wakeup\n"); + return ret; + } + + cros_ec_rtc->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(cros_ec_rtc->rtc)) + return PTR_ERR(cros_ec_rtc->rtc); + + cros_ec_rtc->rtc->ops = &cros_ec_rtc_ops; + cros_ec_rtc->rtc->range_max = U32_MAX; + + /* + * The RTC on some older Chromebooks can only handle alarms less than + * 24 hours in the future. The only way to find out is to try to set an + * alarm further in the future. If that fails, assume that the RTC + * connected to the EC can only handle less than 24 hours of alarm + * window. + */ + ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, SECS_PER_DAY * 2); + if (ret == -EINVAL) + cros_ec_rtc->rtc->alarm_offset_max = SECS_PER_DAY - 1; + + (void)cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, + EC_RTC_ALARM_CLEAR); + + ret = devm_rtc_register_device(cros_ec_rtc->rtc); + if (ret) + return ret; + + /* Get RTC events from the EC. */ + cros_ec_rtc->notifier.notifier_call = cros_ec_rtc_event; + ret = blocking_notifier_chain_register(&cros_ec->event_notifier, + &cros_ec_rtc->notifier); + if (ret) { + dev_err(&pdev->dev, "failed to register notifier\n"); + return ret; + } + + return 0; +} + +static void cros_ec_rtc_remove(struct platform_device *pdev) +{ + struct cros_ec_rtc *cros_ec_rtc = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int ret; + + ret = blocking_notifier_chain_unregister( + &cros_ec_rtc->cros_ec->event_notifier, + &cros_ec_rtc->notifier); + if (ret) + dev_err(dev, "failed to unregister notifier\n"); +} + +static const struct platform_device_id cros_ec_rtc_id[] = { + { .name = DRV_NAME }, + { } +}; +MODULE_DEVICE_TABLE(platform, cros_ec_rtc_id); + +static struct platform_driver cros_ec_rtc_driver = { + .probe = cros_ec_rtc_probe, + .remove = cros_ec_rtc_remove, + .driver = { + .name = DRV_NAME, + .pm = &cros_ec_rtc_pm_ops, + }, + .id_table = cros_ec_rtc_id, +}; + +module_platform_driver(cros_ec_rtc_driver); + +MODULE_DESCRIPTION("RTC driver for Chrome OS ECs"); +MODULE_AUTHOR("Stephen Barber "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-cv1800.c b/drivers/rtc/rtc-cv1800.c new file mode 100644 index 000000000..678c2c10b --- /dev/null +++ b/drivers/rtc/rtc-cv1800.c @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rtc-cv1800.c: RTC driver for Sophgo cv1800 RTC + * + * Author: Jingbao Qiu + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SEC_PULSE_GEN 0x1004 +#define ALARM_TIME 0x1008 +#define ALARM_ENABLE 0x100C +#define SET_SEC_CNTR_VAL 0x1010 +#define SET_SEC_CNTR_TRIG 0x1014 +#define SEC_CNTR_VAL 0x1018 + +/* + * When in VDDBKUP domain, this MACRO register + * does not power down + */ +#define MACRO_RO_T 0x14A8 +#define MACRO_RG_SET_T 0x1498 + +#define ALARM_ENABLE_MASK BIT(0) +#define SEL_SEC_PULSE BIT(31) + +struct cv1800_rtc_priv { + struct rtc_device *rtc_dev; + struct regmap *rtc_map; + struct clk *clk; + int irq; +}; + +static bool cv1800_rtc_enabled(struct device *dev) +{ + struct cv1800_rtc_priv *info = dev_get_drvdata(dev); + u32 reg; + + regmap_read(info->rtc_map, SEC_PULSE_GEN, ®); + + return (reg & SEL_SEC_PULSE) == 0; +} + +static void cv1800_rtc_enable(struct device *dev) +{ + struct cv1800_rtc_priv *info = dev_get_drvdata(dev); + + /* Sec pulse generated internally */ + regmap_update_bits(info->rtc_map, SEC_PULSE_GEN, SEL_SEC_PULSE, 0); +} + +static int cv1800_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct cv1800_rtc_priv *info = dev_get_drvdata(dev); + + regmap_write(info->rtc_map, ALARM_ENABLE, enabled); + + return 0; +} + +static int cv1800_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct cv1800_rtc_priv *info = dev_get_drvdata(dev); + unsigned long alarm_time; + + alarm_time = rtc_tm_to_time64(&alrm->time); + + cv1800_rtc_alarm_irq_enable(dev, 0); + + regmap_write(info->rtc_map, ALARM_TIME, alarm_time); + + cv1800_rtc_alarm_irq_enable(dev, alrm->enabled); + + return 0; +} + +static int cv1800_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct cv1800_rtc_priv *info = dev_get_drvdata(dev); + u32 enabled; + u32 time; + + if (!cv1800_rtc_enabled(dev)) { + alarm->enabled = 0; + return 0; + } + + regmap_read(info->rtc_map, ALARM_ENABLE, &enabled); + + alarm->enabled = enabled & ALARM_ENABLE_MASK; + + regmap_read(info->rtc_map, ALARM_TIME, &time); + + rtc_time64_to_tm(time, &alarm->time); + + return 0; +} + +static int cv1800_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct cv1800_rtc_priv *info = dev_get_drvdata(dev); + u32 sec; + + if (!cv1800_rtc_enabled(dev)) + return -EINVAL; + + regmap_read(info->rtc_map, SEC_CNTR_VAL, &sec); + + rtc_time64_to_tm(sec, tm); + + return 0; +} + +static int cv1800_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct cv1800_rtc_priv *info = dev_get_drvdata(dev); + unsigned long sec; + + sec = rtc_tm_to_time64(tm); + + regmap_write(info->rtc_map, SET_SEC_CNTR_VAL, sec); + regmap_write(info->rtc_map, SET_SEC_CNTR_TRIG, 1); + + regmap_write(info->rtc_map, MACRO_RG_SET_T, sec); + + cv1800_rtc_enable(dev); + + return 0; +} + +static irqreturn_t cv1800_rtc_irq_handler(int irq, void *dev_id) +{ + struct cv1800_rtc_priv *info = dev_id; + + rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF); + + regmap_write(info->rtc_map, ALARM_ENABLE, 0); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops cv1800_rtc_ops = { + .read_time = cv1800_rtc_read_time, + .set_time = cv1800_rtc_set_time, + .read_alarm = cv1800_rtc_read_alarm, + .set_alarm = cv1800_rtc_set_alarm, + .alarm_irq_enable = cv1800_rtc_alarm_irq_enable, +}; + +static int cv1800_rtc_probe(struct platform_device *pdev) +{ + struct cv1800_rtc_priv *rtc; + int ret; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->rtc_map = device_node_to_regmap(pdev->dev.parent->of_node); + if (IS_ERR(rtc->rtc_map)) + return dev_err_probe(&pdev->dev, PTR_ERR(rtc->rtc_map), + "cannot get parent regmap\n"); + + rtc->irq = platform_get_irq(pdev, 0); + if (rtc->irq < 0) + return rtc->irq; + + rtc->clk = devm_clk_get_enabled(pdev->dev.parent, "rtc"); + if (IS_ERR(rtc->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(rtc->clk), + "rtc clk not found\n"); + + platform_set_drvdata(pdev, rtc); + + device_init_wakeup(&pdev->dev, 1); + + rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc->rtc_dev)) + return PTR_ERR(rtc->rtc_dev); + + rtc->rtc_dev->ops = &cv1800_rtc_ops; + rtc->rtc_dev->range_max = U32_MAX; + + ret = devm_request_irq(&pdev->dev, rtc->irq, cv1800_rtc_irq_handler, + IRQF_TRIGGER_HIGH, "rtc alarm", rtc); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "cannot register interrupt handler\n"); + + return devm_rtc_register_device(rtc->rtc_dev); +} + +static const struct platform_device_id cv1800_rtc_id[] = { + { .name = "cv1800b-rtc" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(platform, cv1800_rtc_id); + +static struct platform_driver cv1800_rtc_driver = { + .driver = { + .name = "sophgo-cv1800-rtc", + }, + .probe = cv1800_rtc_probe, + .id_table = cv1800_rtc_id, +}; + +module_platform_driver(cv1800_rtc_driver); +MODULE_AUTHOR("Jingbao Qiu"); +MODULE_DESCRIPTION("Sophgo cv1800 RTC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-da9052.c b/drivers/rtc/rtc-da9052.c new file mode 100644 index 000000000..9ca99bd35 --- /dev/null +++ b/drivers/rtc/rtc-da9052.c @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Real time clock driver for DA9052 + * + * Copyright(c) 2012 Dialog Semiconductor Ltd. + * + * Author: Dajun Dajun Chen + */ + +#include +#include +#include +#include +#include + +#include +#include + +#define rtc_err(rtc, fmt, ...) \ + dev_err(rtc->da9052->dev, "%s: " fmt, __func__, ##__VA_ARGS__) + +#define DA9052_GET_TIME_RETRIES 5 + +struct da9052_rtc { + struct rtc_device *rtc; + struct da9052 *da9052; +}; + +static int da9052_rtc_enable_alarm(struct da9052_rtc *rtc, bool enable) +{ + int ret; + if (enable) { + ret = da9052_reg_update(rtc->da9052, DA9052_ALARM_Y_REG, + DA9052_ALARM_Y_ALARM_ON|DA9052_ALARM_Y_TICK_ON, + DA9052_ALARM_Y_ALARM_ON); + if (ret != 0) + rtc_err(rtc, "Failed to enable ALM: %d\n", ret); + } else { + ret = da9052_reg_update(rtc->da9052, DA9052_ALARM_Y_REG, + DA9052_ALARM_Y_ALARM_ON|DA9052_ALARM_Y_TICK_ON, 0); + if (ret != 0) + rtc_err(rtc, "Write error: %d\n", ret); + } + return ret; +} + +static irqreturn_t da9052_rtc_irq(int irq, void *data) +{ + struct da9052_rtc *rtc = data; + + rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static int da9052_read_alarm(struct da9052_rtc *rtc, struct rtc_time *rtc_tm) +{ + int ret; + uint8_t v[2][5]; + int idx = 1; + int timeout = DA9052_GET_TIME_RETRIES; + + ret = da9052_group_read(rtc->da9052, DA9052_ALARM_MI_REG, 5, &v[0][0]); + if (ret) { + rtc_err(rtc, "Failed to group read ALM: %d\n", ret); + return ret; + } + + do { + ret = da9052_group_read(rtc->da9052, + DA9052_ALARM_MI_REG, 5, &v[idx][0]); + if (ret) { + rtc_err(rtc, "Failed to group read ALM: %d\n", ret); + return ret; + } + + if (memcmp(&v[0][0], &v[1][0], 5) == 0) { + rtc_tm->tm_year = (v[0][4] & DA9052_RTC_YEAR) + 100; + rtc_tm->tm_mon = (v[0][3] & DA9052_RTC_MONTH) - 1; + rtc_tm->tm_mday = v[0][2] & DA9052_RTC_DAY; + rtc_tm->tm_hour = v[0][1] & DA9052_RTC_HOUR; + rtc_tm->tm_min = v[0][0] & DA9052_RTC_MIN; + rtc_tm->tm_sec = 0; + + ret = rtc_valid_tm(rtc_tm); + return ret; + } + + idx = (1-idx); + msleep(20); + + } while (timeout--); + + rtc_err(rtc, "Timed out reading alarm time\n"); + + return -EIO; +} + +static int da9052_set_alarm(struct da9052_rtc *rtc, struct rtc_time *rtc_tm) +{ + struct da9052 *da9052 = rtc->da9052; + unsigned long alm_time; + int ret; + uint8_t v[3]; + + alm_time = rtc_tm_to_time64(rtc_tm); + + if (rtc_tm->tm_sec > 0) { + alm_time += 60 - rtc_tm->tm_sec; + rtc_time64_to_tm(alm_time, rtc_tm); + } + BUG_ON(rtc_tm->tm_sec); /* it will cause repeated irqs if not zero */ + + rtc_tm->tm_year -= 100; + rtc_tm->tm_mon += 1; + + ret = da9052_reg_update(da9052, DA9052_ALARM_MI_REG, + DA9052_RTC_MIN, rtc_tm->tm_min); + if (ret != 0) { + rtc_err(rtc, "Failed to write ALRM MIN: %d\n", ret); + return ret; + } + + v[0] = rtc_tm->tm_hour; + v[1] = rtc_tm->tm_mday; + v[2] = rtc_tm->tm_mon; + + ret = da9052_group_write(da9052, DA9052_ALARM_H_REG, 3, v); + if (ret < 0) + return ret; + + ret = da9052_reg_update(da9052, DA9052_ALARM_Y_REG, + DA9052_RTC_YEAR, rtc_tm->tm_year); + if (ret != 0) + rtc_err(rtc, "Failed to write ALRM YEAR: %d\n", ret); + + return ret; +} + +static int da9052_rtc_get_alarm_status(struct da9052_rtc *rtc) +{ + int ret; + + ret = da9052_reg_read(rtc->da9052, DA9052_ALARM_Y_REG); + if (ret < 0) { + rtc_err(rtc, "Failed to read ALM: %d\n", ret); + return ret; + } + + return !!(ret&DA9052_ALARM_Y_ALARM_ON); +} + +static int da9052_rtc_read_time(struct device *dev, struct rtc_time *rtc_tm) +{ + struct da9052_rtc *rtc = dev_get_drvdata(dev); + int ret; + uint8_t v[2][6]; + int idx = 1; + int timeout = DA9052_GET_TIME_RETRIES; + + ret = da9052_group_read(rtc->da9052, DA9052_COUNT_S_REG, 6, &v[0][0]); + if (ret) { + rtc_err(rtc, "Failed to read RTC time : %d\n", ret); + return ret; + } + + do { + ret = da9052_group_read(rtc->da9052, + DA9052_COUNT_S_REG, 6, &v[idx][0]); + if (ret) { + rtc_err(rtc, "Failed to read RTC time : %d\n", ret); + return ret; + } + + if (memcmp(&v[0][0], &v[1][0], 6) == 0) { + rtc_tm->tm_year = (v[0][5] & DA9052_RTC_YEAR) + 100; + rtc_tm->tm_mon = (v[0][4] & DA9052_RTC_MONTH) - 1; + rtc_tm->tm_mday = v[0][3] & DA9052_RTC_DAY; + rtc_tm->tm_hour = v[0][2] & DA9052_RTC_HOUR; + rtc_tm->tm_min = v[0][1] & DA9052_RTC_MIN; + rtc_tm->tm_sec = v[0][0] & DA9052_RTC_SEC; + + return 0; + } + + idx = (1-idx); + msleep(20); + + } while (timeout--); + + rtc_err(rtc, "Timed out reading time\n"); + + return -EIO; +} + +static int da9052_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct da9052_rtc *rtc; + uint8_t v[6]; + int ret; + + /* DA9052 only has 6 bits for year - to represent 2000-2063 */ + if ((tm->tm_year < 100) || (tm->tm_year > 163)) + return -EINVAL; + + rtc = dev_get_drvdata(dev); + + v[0] = tm->tm_sec; + v[1] = tm->tm_min; + v[2] = tm->tm_hour; + v[3] = tm->tm_mday; + v[4] = tm->tm_mon + 1; + v[5] = tm->tm_year - 100; + + ret = da9052_group_write(rtc->da9052, DA9052_COUNT_S_REG, 6, v); + if (ret < 0) + rtc_err(rtc, "failed to set RTC time: %d\n", ret); + return ret; +} + +static int da9052_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + int ret; + struct rtc_time *tm = &alrm->time; + struct da9052_rtc *rtc = dev_get_drvdata(dev); + + ret = da9052_read_alarm(rtc, tm); + if (ret < 0) { + rtc_err(rtc, "failed to read RTC alarm: %d\n", ret); + return ret; + } + + alrm->enabled = da9052_rtc_get_alarm_status(rtc); + return 0; +} + +static int da9052_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + int ret; + struct rtc_time *tm = &alrm->time; + struct da9052_rtc *rtc = dev_get_drvdata(dev); + + /* DA9052 only has 6 bits for year - to represent 2000-2063 */ + if ((tm->tm_year < 100) || (tm->tm_year > 163)) + return -EINVAL; + + ret = da9052_rtc_enable_alarm(rtc, 0); + if (ret < 0) + return ret; + + ret = da9052_set_alarm(rtc, tm); + if (ret < 0) + return ret; + + ret = da9052_rtc_enable_alarm(rtc, 1); + return ret; +} + +static int da9052_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct da9052_rtc *rtc = dev_get_drvdata(dev); + + return da9052_rtc_enable_alarm(rtc, enabled); +} + +static const struct rtc_class_ops da9052_rtc_ops = { + .read_time = da9052_rtc_read_time, + .set_time = da9052_rtc_set_time, + .read_alarm = da9052_rtc_read_alarm, + .set_alarm = da9052_rtc_set_alarm, + .alarm_irq_enable = da9052_rtc_alarm_irq_enable, +}; + +static int da9052_rtc_probe(struct platform_device *pdev) +{ + struct da9052_rtc *rtc; + int ret; + + rtc = devm_kzalloc(&pdev->dev, sizeof(struct da9052_rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->da9052 = dev_get_drvdata(pdev->dev.parent); + platform_set_drvdata(pdev, rtc); + + ret = da9052_reg_write(rtc->da9052, DA9052_BBAT_CONT_REG, 0xFE); + if (ret < 0) { + rtc_err(rtc, + "Failed to setup RTC battery charging: %d\n", ret); + return ret; + } + + ret = da9052_reg_update(rtc->da9052, DA9052_ALARM_Y_REG, + DA9052_ALARM_Y_TICK_ON, 0); + if (ret != 0) + rtc_err(rtc, "Failed to disable TICKS: %d\n", ret); + + device_init_wakeup(&pdev->dev, true); + rtc->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc->rtc)) + return PTR_ERR(rtc->rtc); + + rtc->rtc->ops = &da9052_rtc_ops; + rtc->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rtc->rtc->range_max = RTC_TIMESTAMP_END_2063; + + ret = devm_rtc_register_device(rtc->rtc); + if (ret) + return ret; + + ret = da9052_request_irq(rtc->da9052, DA9052_IRQ_ALARM, "ALM", + da9052_rtc_irq, rtc); + if (ret != 0) { + rtc_err(rtc, "irq registration failed: %d\n", ret); + return ret; + } + + return 0; +} + +static struct platform_driver da9052_rtc_driver = { + .probe = da9052_rtc_probe, + .driver = { + .name = "da9052-rtc", + }, +}; + +module_platform_driver(da9052_rtc_driver); + +MODULE_AUTHOR("Anthony Olech "); +MODULE_DESCRIPTION("RTC driver for Dialog DA9052 PMIC"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:da9052-rtc"); diff --git a/drivers/rtc/rtc-da9055.c b/drivers/rtc/rtc-da9055.c new file mode 100644 index 000000000..05adec6b7 --- /dev/null +++ b/drivers/rtc/rtc-da9055.c @@ -0,0 +1,399 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Real time clock driver for DA9055 + * + * Copyright(c) 2012 Dialog Semiconductor Ltd. + * + * Author: Dajun Dajun Chen + */ + +#include +#include +#include + +#include +#include +#include + +struct da9055_rtc { + struct rtc_device *rtc; + struct da9055 *da9055; + int alarm_enable; +}; + +static int da9055_rtc_enable_alarm(struct da9055_rtc *rtc, bool enable) +{ + int ret; + if (enable) { + ret = da9055_reg_update(rtc->da9055, DA9055_REG_ALARM_Y, + DA9055_RTC_ALM_EN, + DA9055_RTC_ALM_EN); + if (ret != 0) + dev_err(rtc->da9055->dev, "Failed to enable ALM: %d\n", + ret); + rtc->alarm_enable = 1; + } else { + ret = da9055_reg_update(rtc->da9055, DA9055_REG_ALARM_Y, + DA9055_RTC_ALM_EN, 0); + if (ret != 0) + dev_err(rtc->da9055->dev, + "Failed to disable ALM: %d\n", ret); + rtc->alarm_enable = 0; + } + return ret; +} + +static irqreturn_t da9055_rtc_alm_irq(int irq, void *data) +{ + struct da9055_rtc *rtc = data; + + da9055_rtc_enable_alarm(rtc, 0); + rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static int da9055_read_alarm(struct da9055 *da9055, struct rtc_time *rtc_tm) +{ + int ret; + uint8_t v[5]; + + ret = da9055_group_read(da9055, DA9055_REG_ALARM_MI, 5, v); + if (ret != 0) { + dev_err(da9055->dev, "Failed to group read ALM: %d\n", ret); + return ret; + } + + rtc_tm->tm_year = (v[4] & DA9055_RTC_ALM_YEAR) + 100; + rtc_tm->tm_mon = (v[3] & DA9055_RTC_ALM_MONTH) - 1; + rtc_tm->tm_mday = v[2] & DA9055_RTC_ALM_DAY; + rtc_tm->tm_hour = v[1] & DA9055_RTC_ALM_HOUR; + rtc_tm->tm_min = v[0] & DA9055_RTC_ALM_MIN; + rtc_tm->tm_sec = 0; + + return rtc_valid_tm(rtc_tm); +} + +static int da9055_set_alarm(struct da9055 *da9055, struct rtc_time *rtc_tm) +{ + int ret; + uint8_t v[2]; + + rtc_tm->tm_year -= 100; + rtc_tm->tm_mon += 1; + + ret = da9055_reg_update(da9055, DA9055_REG_ALARM_MI, + DA9055_RTC_ALM_MIN, rtc_tm->tm_min); + if (ret != 0) { + dev_err(da9055->dev, "Failed to write ALRM MIN: %d\n", ret); + return ret; + } + + v[0] = rtc_tm->tm_hour; + v[1] = rtc_tm->tm_mday; + + ret = da9055_group_write(da9055, DA9055_REG_ALARM_H, 2, v); + if (ret < 0) + return ret; + + ret = da9055_reg_update(da9055, DA9055_REG_ALARM_MO, + DA9055_RTC_ALM_MONTH, rtc_tm->tm_mon); + if (ret < 0) + dev_err(da9055->dev, "Failed to write ALM Month:%d\n", ret); + + ret = da9055_reg_update(da9055, DA9055_REG_ALARM_Y, + DA9055_RTC_ALM_YEAR, rtc_tm->tm_year); + if (ret < 0) + dev_err(da9055->dev, "Failed to write ALM Year:%d\n", ret); + + return ret; +} + +static int da9055_rtc_get_alarm_status(struct da9055 *da9055) +{ + int ret; + + ret = da9055_reg_read(da9055, DA9055_REG_ALARM_Y); + if (ret < 0) { + dev_err(da9055->dev, "Failed to read ALM: %d\n", ret); + return ret; + } + ret &= DA9055_RTC_ALM_EN; + return (ret > 0) ? 1 : 0; +} + +static int da9055_rtc_read_time(struct device *dev, struct rtc_time *rtc_tm) +{ + struct da9055_rtc *rtc = dev_get_drvdata(dev); + uint8_t v[6]; + int ret; + + ret = da9055_reg_read(rtc->da9055, DA9055_REG_COUNT_S); + if (ret < 0) + return ret; + + /* + * Registers are only valid when RTC_READ + * status bit is asserted + */ + if (!(ret & DA9055_RTC_READ)) + return -EBUSY; + + ret = da9055_group_read(rtc->da9055, DA9055_REG_COUNT_S, 6, v); + if (ret < 0) { + dev_err(rtc->da9055->dev, "Failed to read RTC time : %d\n", + ret); + return ret; + } + + rtc_tm->tm_year = (v[5] & DA9055_RTC_YEAR) + 100; + rtc_tm->tm_mon = (v[4] & DA9055_RTC_MONTH) - 1; + rtc_tm->tm_mday = v[3] & DA9055_RTC_DAY; + rtc_tm->tm_hour = v[2] & DA9055_RTC_HOUR; + rtc_tm->tm_min = v[1] & DA9055_RTC_MIN; + rtc_tm->tm_sec = v[0] & DA9055_RTC_SEC; + + return 0; +} + +static int da9055_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct da9055_rtc *rtc; + uint8_t v[6]; + + rtc = dev_get_drvdata(dev); + + v[0] = tm->tm_sec; + v[1] = tm->tm_min; + v[2] = tm->tm_hour; + v[3] = tm->tm_mday; + v[4] = tm->tm_mon + 1; + v[5] = tm->tm_year - 100; + + return da9055_group_write(rtc->da9055, DA9055_REG_COUNT_S, 6, v); +} + +static int da9055_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + int ret; + struct rtc_time *tm = &alrm->time; + struct da9055_rtc *rtc = dev_get_drvdata(dev); + + ret = da9055_read_alarm(rtc->da9055, tm); + + if (ret) + return ret; + + alrm->enabled = da9055_rtc_get_alarm_status(rtc->da9055); + + return 0; +} + +static int da9055_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + int ret; + struct rtc_time *tm = &alrm->time; + struct da9055_rtc *rtc = dev_get_drvdata(dev); + + ret = da9055_rtc_enable_alarm(rtc, 0); + if (ret < 0) + return ret; + + ret = da9055_set_alarm(rtc->da9055, tm); + if (ret) + return ret; + + ret = da9055_rtc_enable_alarm(rtc, 1); + + return ret; +} + +static int da9055_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct da9055_rtc *rtc = dev_get_drvdata(dev); + + return da9055_rtc_enable_alarm(rtc, enabled); +} + +static const struct rtc_class_ops da9055_rtc_ops = { + .read_time = da9055_rtc_read_time, + .set_time = da9055_rtc_set_time, + .read_alarm = da9055_rtc_read_alarm, + .set_alarm = da9055_rtc_set_alarm, + .alarm_irq_enable = da9055_rtc_alarm_irq_enable, +}; + +static int da9055_rtc_device_init(struct da9055 *da9055, + struct da9055_pdata *pdata) +{ + int ret; + + /* Enable RTC and the internal Crystal */ + ret = da9055_reg_update(da9055, DA9055_REG_CONTROL_B, + DA9055_RTC_EN, DA9055_RTC_EN); + if (ret < 0) + return ret; + ret = da9055_reg_update(da9055, DA9055_REG_EN_32K, + DA9055_CRYSTAL_EN, DA9055_CRYSTAL_EN); + if (ret < 0) + return ret; + + /* Enable RTC in Power Down mode */ + ret = da9055_reg_update(da9055, DA9055_REG_CONTROL_B, + DA9055_RTC_MODE_PD, DA9055_RTC_MODE_PD); + if (ret < 0) + return ret; + + /* Enable RTC in Reset mode */ + if (pdata && pdata->reset_enable) { + ret = da9055_reg_update(da9055, DA9055_REG_CONTROL_B, + DA9055_RTC_MODE_SD, + DA9055_RTC_MODE_SD << + DA9055_RTC_MODE_SD_SHIFT); + if (ret < 0) + return ret; + } + + /* Disable the RTC TICK ALM */ + ret = da9055_reg_update(da9055, DA9055_REG_ALARM_MO, + DA9055_RTC_TICK_WAKE_MASK, 0); + if (ret < 0) + return ret; + + return 0; +} + +static int da9055_rtc_probe(struct platform_device *pdev) +{ + struct da9055_rtc *rtc; + struct da9055_pdata *pdata = NULL; + int ret, alm_irq; + + rtc = devm_kzalloc(&pdev->dev, sizeof(struct da9055_rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->da9055 = dev_get_drvdata(pdev->dev.parent); + pdata = dev_get_platdata(rtc->da9055->dev); + platform_set_drvdata(pdev, rtc); + + ret = da9055_rtc_device_init(rtc->da9055, pdata); + if (ret < 0) + goto err_rtc; + + ret = da9055_reg_read(rtc->da9055, DA9055_REG_ALARM_Y); + if (ret < 0) + goto err_rtc; + + if (ret & DA9055_RTC_ALM_EN) + rtc->alarm_enable = 1; + + device_init_wakeup(&pdev->dev, true); + + rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + &da9055_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc->rtc)) { + ret = PTR_ERR(rtc->rtc); + goto err_rtc; + } + + alm_irq = platform_get_irq_byname(pdev, "ALM"); + if (alm_irq < 0) + return alm_irq; + + ret = devm_request_threaded_irq(&pdev->dev, alm_irq, NULL, + da9055_rtc_alm_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "ALM", rtc); + if (ret != 0) + dev_err(rtc->da9055->dev, "irq registration failed: %d\n", ret); + +err_rtc: + return ret; + +} + +#ifdef CONFIG_PM +/* Turn off the alarm if it should not be a wake source. */ +static int da9055_rtc_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct da9055_rtc *rtc = dev_get_drvdata(&pdev->dev); + int ret; + + if (!device_may_wakeup(&pdev->dev)) { + /* Disable the ALM IRQ */ + ret = da9055_rtc_enable_alarm(rtc, 0); + if (ret < 0) + dev_err(&pdev->dev, "Failed to disable RTC ALM\n"); + } + + return 0; +} + +/* Enable the alarm if it should be enabled (in case it was disabled to + * prevent use as a wake source). + */ +static int da9055_rtc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct da9055_rtc *rtc = dev_get_drvdata(&pdev->dev); + int ret; + + if (!device_may_wakeup(&pdev->dev)) { + if (rtc->alarm_enable) { + ret = da9055_rtc_enable_alarm(rtc, 1); + if (ret < 0) + dev_err(&pdev->dev, + "Failed to restart RTC ALM\n"); + } + } + + return 0; +} + +/* Unconditionally disable the alarm */ +static int da9055_rtc_freeze(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct da9055_rtc *rtc = dev_get_drvdata(&pdev->dev); + int ret; + + ret = da9055_rtc_enable_alarm(rtc, 0); + if (ret < 0) + dev_err(&pdev->dev, "Failed to freeze RTC ALMs\n"); + + return 0; + +} +#else +#define da9055_rtc_suspend NULL +#define da9055_rtc_resume NULL +#define da9055_rtc_freeze NULL +#endif + +static const struct dev_pm_ops da9055_rtc_pm_ops = { + .suspend = da9055_rtc_suspend, + .resume = da9055_rtc_resume, + + .freeze = da9055_rtc_freeze, + .thaw = da9055_rtc_resume, + .restore = da9055_rtc_resume, + + .poweroff = da9055_rtc_suspend, +}; + +static struct platform_driver da9055_rtc_driver = { + .probe = da9055_rtc_probe, + .driver = { + .name = "da9055-rtc", + .pm = &da9055_rtc_pm_ops, + }, +}; + +module_platform_driver(da9055_rtc_driver); + +MODULE_AUTHOR("David Dajun Chen "); +MODULE_DESCRIPTION("RTC driver for Dialog DA9055 PMIC"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:da9055-rtc"); diff --git a/drivers/rtc/rtc-da9063.c b/drivers/rtc/rtc-da9063.c new file mode 100644 index 000000000..557c9b29d --- /dev/null +++ b/drivers/rtc/rtc-da9063.c @@ -0,0 +1,497 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Real time clock device driver for DA9063 + * Copyright (C) 2013-2015 Dialog Semiconductor Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define YEARS_TO_DA9063(year) ((year) - 100) +#define MONTHS_TO_DA9063(month) ((month) + 1) +#define YEARS_FROM_DA9063(year) ((year) + 100) +#define MONTHS_FROM_DA9063(month) ((month) - 1) + +enum { + RTC_SEC = 0, + RTC_MIN = 1, + RTC_HOUR = 2, + RTC_DAY = 3, + RTC_MONTH = 4, + RTC_YEAR = 5, + RTC_DATA_LEN +}; + +struct da9063_compatible_rtc_regmap { + /* REGS */ + int rtc_enable_reg; + int rtc_enable_32k_crystal_reg; + int rtc_alarm_secs_reg; + int rtc_alarm_year_reg; + int rtc_count_secs_reg; + int rtc_count_year_reg; + int rtc_event_reg; + /* MASKS */ + int rtc_enable_mask; + int rtc_crystal_mask; + int rtc_event_alarm_mask; + int rtc_alarm_on_mask; + int rtc_alarm_status_mask; + int rtc_tick_on_mask; + int rtc_ready_to_read_mask; + int rtc_count_sec_mask; + int rtc_count_min_mask; + int rtc_count_hour_mask; + int rtc_count_day_mask; + int rtc_count_month_mask; + int rtc_count_year_mask; + /* ALARM CONFIG */ + int rtc_data_start; + int rtc_alarm_len; +}; + +struct da9063_compatible_rtc { + struct rtc_device *rtc_dev; + struct rtc_time alarm_time; + struct regmap *regmap; + const struct da9063_compatible_rtc_regmap *config; + bool rtc_sync; +}; + +static const struct da9063_compatible_rtc_regmap da9063_ad_regs = { + /* REGS */ + .rtc_enable_reg = DA9063_REG_CONTROL_E, + .rtc_alarm_secs_reg = DA9063_AD_REG_ALARM_MI, + .rtc_alarm_year_reg = DA9063_AD_REG_ALARM_Y, + .rtc_count_secs_reg = DA9063_REG_COUNT_S, + .rtc_count_year_reg = DA9063_REG_COUNT_Y, + .rtc_event_reg = DA9063_REG_EVENT_A, + /* MASKS */ + .rtc_enable_mask = DA9063_RTC_EN, + .rtc_crystal_mask = DA9063_CRYSTAL, + .rtc_enable_32k_crystal_reg = DA9063_REG_EN_32K, + .rtc_event_alarm_mask = DA9063_E_ALARM, + .rtc_alarm_on_mask = DA9063_ALARM_ON, + .rtc_alarm_status_mask = DA9063_ALARM_STATUS_ALARM | + DA9063_ALARM_STATUS_TICK, + .rtc_tick_on_mask = DA9063_TICK_ON, + .rtc_ready_to_read_mask = DA9063_RTC_READ, + .rtc_count_sec_mask = DA9063_COUNT_SEC_MASK, + .rtc_count_min_mask = DA9063_COUNT_MIN_MASK, + .rtc_count_hour_mask = DA9063_COUNT_HOUR_MASK, + .rtc_count_day_mask = DA9063_COUNT_DAY_MASK, + .rtc_count_month_mask = DA9063_COUNT_MONTH_MASK, + .rtc_count_year_mask = DA9063_COUNT_YEAR_MASK, + /* ALARM CONFIG */ + .rtc_data_start = RTC_MIN, + .rtc_alarm_len = RTC_DATA_LEN - 1, +}; + +static const struct da9063_compatible_rtc_regmap da9063_bb_regs = { + /* REGS */ + .rtc_enable_reg = DA9063_REG_CONTROL_E, + .rtc_alarm_secs_reg = DA9063_BB_REG_ALARM_S, + .rtc_alarm_year_reg = DA9063_BB_REG_ALARM_Y, + .rtc_count_secs_reg = DA9063_REG_COUNT_S, + .rtc_count_year_reg = DA9063_REG_COUNT_Y, + .rtc_event_reg = DA9063_REG_EVENT_A, + /* MASKS */ + .rtc_enable_mask = DA9063_RTC_EN, + .rtc_crystal_mask = DA9063_CRYSTAL, + .rtc_enable_32k_crystal_reg = DA9063_REG_EN_32K, + .rtc_event_alarm_mask = DA9063_E_ALARM, + .rtc_alarm_on_mask = DA9063_ALARM_ON, + .rtc_alarm_status_mask = DA9063_ALARM_STATUS_ALARM | + DA9063_ALARM_STATUS_TICK, + .rtc_tick_on_mask = DA9063_TICK_ON, + .rtc_ready_to_read_mask = DA9063_RTC_READ, + .rtc_count_sec_mask = DA9063_COUNT_SEC_MASK, + .rtc_count_min_mask = DA9063_COUNT_MIN_MASK, + .rtc_count_hour_mask = DA9063_COUNT_HOUR_MASK, + .rtc_count_day_mask = DA9063_COUNT_DAY_MASK, + .rtc_count_month_mask = DA9063_COUNT_MONTH_MASK, + .rtc_count_year_mask = DA9063_COUNT_YEAR_MASK, + /* ALARM CONFIG */ + .rtc_data_start = RTC_SEC, + .rtc_alarm_len = RTC_DATA_LEN, +}; + +static const struct da9063_compatible_rtc_regmap da9062_aa_regs = { + /* REGS */ + .rtc_enable_reg = DA9062AA_CONTROL_E, + .rtc_alarm_secs_reg = DA9062AA_ALARM_S, + .rtc_alarm_year_reg = DA9062AA_ALARM_Y, + .rtc_count_secs_reg = DA9062AA_COUNT_S, + .rtc_count_year_reg = DA9062AA_COUNT_Y, + .rtc_event_reg = DA9062AA_EVENT_A, + /* MASKS */ + .rtc_enable_mask = DA9062AA_RTC_EN_MASK, + .rtc_crystal_mask = DA9062AA_CRYSTAL_MASK, + .rtc_enable_32k_crystal_reg = DA9062AA_EN_32K, + .rtc_event_alarm_mask = DA9062AA_M_ALARM_MASK, + .rtc_alarm_on_mask = DA9062AA_ALARM_ON_MASK, + .rtc_alarm_status_mask = (0x02 << 6), + .rtc_tick_on_mask = DA9062AA_TICK_ON_MASK, + .rtc_ready_to_read_mask = DA9062AA_RTC_READ_MASK, + .rtc_count_sec_mask = DA9062AA_COUNT_SEC_MASK, + .rtc_count_min_mask = DA9062AA_COUNT_MIN_MASK, + .rtc_count_hour_mask = DA9062AA_COUNT_HOUR_MASK, + .rtc_count_day_mask = DA9062AA_COUNT_DAY_MASK, + .rtc_count_month_mask = DA9062AA_COUNT_MONTH_MASK, + .rtc_count_year_mask = DA9062AA_COUNT_YEAR_MASK, + /* ALARM CONFIG */ + .rtc_data_start = RTC_SEC, + .rtc_alarm_len = RTC_DATA_LEN, +}; + +static const struct of_device_id da9063_compatible_reg_id_table[] = { + { .compatible = "dlg,da9063-rtc", .data = &da9063_bb_regs }, + { .compatible = "dlg,da9062-rtc", .data = &da9062_aa_regs }, + { }, +}; +MODULE_DEVICE_TABLE(of, da9063_compatible_reg_id_table); + +static void da9063_data_to_tm(u8 *data, struct rtc_time *tm, + struct da9063_compatible_rtc *rtc) +{ + const struct da9063_compatible_rtc_regmap *config = rtc->config; + + tm->tm_sec = data[RTC_SEC] & config->rtc_count_sec_mask; + tm->tm_min = data[RTC_MIN] & config->rtc_count_min_mask; + tm->tm_hour = data[RTC_HOUR] & config->rtc_count_hour_mask; + tm->tm_mday = data[RTC_DAY] & config->rtc_count_day_mask; + tm->tm_mon = MONTHS_FROM_DA9063(data[RTC_MONTH] & + config->rtc_count_month_mask); + tm->tm_year = YEARS_FROM_DA9063(data[RTC_YEAR] & + config->rtc_count_year_mask); +} + +static void da9063_tm_to_data(struct rtc_time *tm, u8 *data, + struct da9063_compatible_rtc *rtc) +{ + const struct da9063_compatible_rtc_regmap *config = rtc->config; + + data[RTC_SEC] = tm->tm_sec & config->rtc_count_sec_mask; + data[RTC_MIN] = tm->tm_min & config->rtc_count_min_mask; + data[RTC_HOUR] = tm->tm_hour & config->rtc_count_hour_mask; + data[RTC_DAY] = tm->tm_mday & config->rtc_count_day_mask; + data[RTC_MONTH] = MONTHS_TO_DA9063(tm->tm_mon) & + config->rtc_count_month_mask; + data[RTC_YEAR] = YEARS_TO_DA9063(tm->tm_year) & + config->rtc_count_year_mask; +} + +static int da9063_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct da9063_compatible_rtc *rtc = dev_get_drvdata(dev); + const struct da9063_compatible_rtc_regmap *config = rtc->config; + u8 set_bit = enabled ? config->rtc_alarm_on_mask : 0; + + return regmap_update_bits(rtc->regmap, + config->rtc_alarm_year_reg, + config->rtc_alarm_on_mask, + set_bit); +} + +static int da9063_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct da9063_compatible_rtc *rtc = dev_get_drvdata(dev); + const struct da9063_compatible_rtc_regmap *config = rtc->config; + unsigned long tm_secs; + unsigned long al_secs; + u8 data[RTC_DATA_LEN]; + int ret; + + ret = regmap_bulk_read(rtc->regmap, + config->rtc_count_secs_reg, + data, RTC_DATA_LEN); + if (ret < 0) { + dev_err(dev, "Failed to read RTC time data: %d\n", ret); + return ret; + } + + if (!(data[RTC_SEC] & config->rtc_ready_to_read_mask)) { + dev_dbg(dev, "RTC not yet ready to be read by the host\n"); + return -EINVAL; + } + + da9063_data_to_tm(data, tm, rtc); + + tm_secs = rtc_tm_to_time64(tm); + al_secs = rtc_tm_to_time64(&rtc->alarm_time); + + /* handle the rtc synchronisation delay */ + if (rtc->rtc_sync && al_secs - tm_secs == 1) + memcpy(tm, &rtc->alarm_time, sizeof(struct rtc_time)); + else + rtc->rtc_sync = false; + + return 0; +} + +static int da9063_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct da9063_compatible_rtc *rtc = dev_get_drvdata(dev); + const struct da9063_compatible_rtc_regmap *config = rtc->config; + u8 data[RTC_DATA_LEN]; + int ret; + + da9063_tm_to_data(tm, data, rtc); + ret = regmap_bulk_write(rtc->regmap, + config->rtc_count_secs_reg, + data, RTC_DATA_LEN); + if (ret < 0) + dev_err(dev, "Failed to set RTC time data: %d\n", ret); + + return ret; +} + +static int da9063_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct da9063_compatible_rtc *rtc = dev_get_drvdata(dev); + const struct da9063_compatible_rtc_regmap *config = rtc->config; + u8 data[RTC_DATA_LEN]; + int ret; + unsigned int val; + + data[RTC_SEC] = 0; + ret = regmap_bulk_read(rtc->regmap, + config->rtc_alarm_secs_reg, + &data[config->rtc_data_start], + config->rtc_alarm_len); + if (ret < 0) + return ret; + + da9063_data_to_tm(data, &alrm->time, rtc); + + alrm->enabled = !!(data[RTC_YEAR] & config->rtc_alarm_on_mask); + + ret = regmap_read(rtc->regmap, + config->rtc_event_reg, + &val); + if (ret < 0) + return ret; + + if (val & config->rtc_event_alarm_mask) + alrm->pending = 1; + else + alrm->pending = 0; + + return 0; +} + +static int da9063_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct da9063_compatible_rtc *rtc = dev_get_drvdata(dev); + const struct da9063_compatible_rtc_regmap *config = rtc->config; + u8 data[RTC_DATA_LEN]; + int ret; + + da9063_tm_to_data(&alrm->time, data, rtc); + + ret = da9063_rtc_alarm_irq_enable(dev, 0); + if (ret < 0) { + dev_err(dev, "Failed to stop alarm: %d\n", ret); + return ret; + } + + ret = regmap_bulk_write(rtc->regmap, + config->rtc_alarm_secs_reg, + &data[config->rtc_data_start], + config->rtc_alarm_len); + if (ret < 0) { + dev_err(dev, "Failed to write alarm: %d\n", ret); + return ret; + } + + da9063_data_to_tm(data, &rtc->alarm_time, rtc); + + if (alrm->enabled) { + ret = da9063_rtc_alarm_irq_enable(dev, 1); + if (ret < 0) { + dev_err(dev, "Failed to start alarm: %d\n", ret); + return ret; + } + } + + return ret; +} + +static irqreturn_t da9063_alarm_event(int irq, void *data) +{ + struct da9063_compatible_rtc *rtc = data; + const struct da9063_compatible_rtc_regmap *config = rtc->config; + + regmap_update_bits(rtc->regmap, + config->rtc_alarm_year_reg, + config->rtc_alarm_on_mask, + 0); + + rtc->rtc_sync = true; + rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops da9063_rtc_ops = { + .read_time = da9063_rtc_read_time, + .set_time = da9063_rtc_set_time, + .read_alarm = da9063_rtc_read_alarm, + .set_alarm = da9063_rtc_set_alarm, + .alarm_irq_enable = da9063_rtc_alarm_irq_enable, +}; + +static int da9063_rtc_probe(struct platform_device *pdev) +{ + struct da9063_compatible_rtc *rtc; + const struct da9063_compatible_rtc_regmap *config; + int irq_alarm; + u8 data[RTC_DATA_LEN]; + int ret; + + if (!pdev->dev.of_node) + return -ENXIO; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->config = device_get_match_data(&pdev->dev); + if (of_device_is_compatible(pdev->dev.of_node, "dlg,da9063-rtc")) { + struct da9063 *chip = dev_get_drvdata(pdev->dev.parent); + + if (chip->variant_code == PMIC_DA9063_AD) + rtc->config = &da9063_ad_regs; + } + + rtc->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!rtc->regmap) { + dev_warn(&pdev->dev, "Parent regmap unavailable.\n"); + return -ENXIO; + } + + config = rtc->config; + ret = regmap_update_bits(rtc->regmap, + config->rtc_enable_reg, + config->rtc_enable_mask, + config->rtc_enable_mask); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "Failed to enable RTC\n"); + + ret = regmap_update_bits(rtc->regmap, + config->rtc_enable_32k_crystal_reg, + config->rtc_crystal_mask, + config->rtc_crystal_mask); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Failed to run 32kHz oscillator\n"); + + ret = regmap_update_bits(rtc->regmap, + config->rtc_alarm_secs_reg, + config->rtc_alarm_status_mask, + 0); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Failed to access RTC alarm register\n"); + + ret = regmap_update_bits(rtc->regmap, + config->rtc_alarm_secs_reg, + DA9063_ALARM_STATUS_ALARM, + DA9063_ALARM_STATUS_ALARM); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Failed to access RTC alarm register\n"); + + ret = regmap_update_bits(rtc->regmap, + config->rtc_alarm_year_reg, + config->rtc_tick_on_mask, + 0); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Failed to disable TICKs\n"); + + data[RTC_SEC] = 0; + ret = regmap_bulk_read(rtc->regmap, + config->rtc_alarm_secs_reg, + &data[config->rtc_data_start], + config->rtc_alarm_len); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Failed to read initial alarm data\n"); + + platform_set_drvdata(pdev, rtc); + + rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc->rtc_dev)) + return PTR_ERR(rtc->rtc_dev); + + rtc->rtc_dev->ops = &da9063_rtc_ops; + rtc->rtc_dev->range_min = RTC_TIMESTAMP_BEGIN_2000; + rtc->rtc_dev->range_max = RTC_TIMESTAMP_END_2063; + + da9063_data_to_tm(data, &rtc->alarm_time, rtc); + rtc->rtc_sync = false; + + if (config->rtc_data_start != RTC_SEC) { + set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->rtc_dev->features); + /* + * TODO: some models have alarms on a minute boundary but still + * support real hardware interrupts. + */ + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->rtc_dev->features); + } + + irq_alarm = platform_get_irq_byname_optional(pdev, "ALARM"); + if (irq_alarm >= 0) { + ret = devm_request_threaded_irq(&pdev->dev, irq_alarm, NULL, + da9063_alarm_event, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "ALARM", rtc); + if (ret) + dev_err(&pdev->dev, + "Failed to request ALARM IRQ %d: %d\n", + irq_alarm, ret); + + ret = dev_pm_set_wake_irq(&pdev->dev, irq_alarm); + if (ret) + dev_warn(&pdev->dev, + "Failed to set IRQ %d as a wake IRQ: %d\n", + irq_alarm, ret); + + device_init_wakeup(&pdev->dev, true); + } else if (irq_alarm != -ENXIO) { + return irq_alarm; + } else { + clear_bit(RTC_FEATURE_ALARM, rtc->rtc_dev->features); + } + + return devm_rtc_register_device(rtc->rtc_dev); +} + +static struct platform_driver da9063_rtc_driver = { + .probe = da9063_rtc_probe, + .driver = { + .name = DA9063_DRVNAME_RTC, + .of_match_table = da9063_compatible_reg_id_table, + }, +}; + +module_platform_driver(da9063_rtc_driver); + +MODULE_AUTHOR("S Twiss "); +MODULE_DESCRIPTION("Real time clock device driver for Dialog DA9063"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-digicolor.c b/drivers/rtc/rtc-digicolor.c new file mode 100644 index 000000000..218a6de19 --- /dev/null +++ b/drivers/rtc/rtc-digicolor.c @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Real Time Clock driver for Conexant Digicolor + * + * Copyright (C) 2015 Paradox Innovation Ltd. + * + * Author: Baruch Siach + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DC_RTC_CONTROL 0x0 +#define DC_RTC_TIME 0x8 +#define DC_RTC_REFERENCE 0xc +#define DC_RTC_ALARM 0x10 +#define DC_RTC_INTFLAG_CLEAR 0x14 +#define DC_RTC_INTENABLE 0x16 + +#define DC_RTC_CMD_MASK 0xf +#define DC_RTC_GO_BUSY BIT(7) + +#define CMD_NOP 0 +#define CMD_RESET 1 +#define CMD_WRITE 3 +#define CMD_READ 4 + +#define CMD_DELAY_US (10*1000) +#define CMD_TIMEOUT_US (500*CMD_DELAY_US) + +struct dc_rtc { + struct rtc_device *rtc_dev; + void __iomem *regs; +}; + +static int dc_rtc_cmds(struct dc_rtc *rtc, const u8 *cmds, int len) +{ + u8 val; + int i, ret; + + for (i = 0; i < len; i++) { + writeb_relaxed((cmds[i] & DC_RTC_CMD_MASK) | DC_RTC_GO_BUSY, + rtc->regs + DC_RTC_CONTROL); + ret = readb_relaxed_poll_timeout( + rtc->regs + DC_RTC_CONTROL, val, + !(val & DC_RTC_GO_BUSY), CMD_DELAY_US, CMD_TIMEOUT_US); + if (ret < 0) + return ret; + } + + return 0; +} + +static int dc_rtc_read(struct dc_rtc *rtc, unsigned long *val) +{ + static const u8 read_cmds[] = {CMD_READ, CMD_NOP}; + u32 reference, time1, time2; + int ret; + + ret = dc_rtc_cmds(rtc, read_cmds, ARRAY_SIZE(read_cmds)); + if (ret < 0) + return ret; + + reference = readl_relaxed(rtc->regs + DC_RTC_REFERENCE); + time1 = readl_relaxed(rtc->regs + DC_RTC_TIME); + /* Read twice to ensure consistency */ + while (1) { + time2 = readl_relaxed(rtc->regs + DC_RTC_TIME); + if (time1 == time2) + break; + time1 = time2; + } + + *val = reference + time1; + return 0; +} + +static int dc_rtc_write(struct dc_rtc *rtc, u32 val) +{ + static const u8 write_cmds[] = {CMD_WRITE, CMD_NOP, CMD_RESET, CMD_NOP}; + + writel_relaxed(val, rtc->regs + DC_RTC_REFERENCE); + return dc_rtc_cmds(rtc, write_cmds, ARRAY_SIZE(write_cmds)); +} + +static int dc_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct dc_rtc *rtc = dev_get_drvdata(dev); + unsigned long now; + int ret; + + ret = dc_rtc_read(rtc, &now); + if (ret < 0) + return ret; + rtc_time64_to_tm(now, tm); + + return 0; +} + +static int dc_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct dc_rtc *rtc = dev_get_drvdata(dev); + + return dc_rtc_write(rtc, rtc_tm_to_time64(tm)); +} + +static int dc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct dc_rtc *rtc = dev_get_drvdata(dev); + u32 alarm_reg, reference; + unsigned long now; + int ret; + + alarm_reg = readl_relaxed(rtc->regs + DC_RTC_ALARM); + reference = readl_relaxed(rtc->regs + DC_RTC_REFERENCE); + rtc_time64_to_tm(reference + alarm_reg, &alarm->time); + + ret = dc_rtc_read(rtc, &now); + if (ret < 0) + return ret; + + alarm->pending = alarm_reg + reference > now; + alarm->enabled = readl_relaxed(rtc->regs + DC_RTC_INTENABLE); + + return 0; +} + +static int dc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct dc_rtc *rtc = dev_get_drvdata(dev); + time64_t alarm_time; + u32 reference; + + alarm_time = rtc_tm_to_time64(&alarm->time); + + reference = readl_relaxed(rtc->regs + DC_RTC_REFERENCE); + writel_relaxed(alarm_time - reference, rtc->regs + DC_RTC_ALARM); + + writeb_relaxed(!!alarm->enabled, rtc->regs + DC_RTC_INTENABLE); + + return 0; +} + +static int dc_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct dc_rtc *rtc = dev_get_drvdata(dev); + + writeb_relaxed(!!enabled, rtc->regs + DC_RTC_INTENABLE); + + return 0; +} + +static const struct rtc_class_ops dc_rtc_ops = { + .read_time = dc_rtc_read_time, + .set_time = dc_rtc_set_time, + .read_alarm = dc_rtc_read_alarm, + .set_alarm = dc_rtc_set_alarm, + .alarm_irq_enable = dc_rtc_alarm_irq_enable, +}; + +static irqreturn_t dc_rtc_irq(int irq, void *dev_id) +{ + struct dc_rtc *rtc = dev_id; + + writeb_relaxed(1, rtc->regs + DC_RTC_INTFLAG_CLEAR); + rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF); + + return IRQ_HANDLED; +} + +static int __init dc_rtc_probe(struct platform_device *pdev) +{ + struct dc_rtc *rtc; + int irq, ret; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rtc->regs)) + return PTR_ERR(rtc->regs); + + rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc->rtc_dev)) + return PTR_ERR(rtc->rtc_dev); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + ret = devm_request_irq(&pdev->dev, irq, dc_rtc_irq, 0, pdev->name, rtc); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, rtc); + + rtc->rtc_dev->ops = &dc_rtc_ops; + rtc->rtc_dev->range_max = U32_MAX; + + return devm_rtc_register_device(rtc->rtc_dev); +} + +static const __maybe_unused struct of_device_id dc_dt_ids[] = { + { .compatible = "cnxt,cx92755-rtc" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dc_dt_ids); + +static struct platform_driver dc_rtc_driver = { + .driver = { + .name = "digicolor_rtc", + .of_match_table = of_match_ptr(dc_dt_ids), + }, +}; +module_platform_driver_probe(dc_rtc_driver, dc_rtc_probe); + +MODULE_AUTHOR("Baruch Siach "); +MODULE_DESCRIPTION("Conexant Digicolor Realtime Clock Driver (RTC)"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-ds1216.c b/drivers/rtc/rtc-ds1216.c new file mode 100644 index 000000000..7eeb3f359 --- /dev/null +++ b/drivers/rtc/rtc-ds1216.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Dallas DS1216 RTC driver + * + * Copyright (c) 2007 Thomas Bogendoerfer + * + */ + +#include +#include +#include +#include +#include + +struct ds1216_regs { + u8 tsec; + u8 sec; + u8 min; + u8 hour; + u8 wday; + u8 mday; + u8 month; + u8 year; +}; + +#define DS1216_HOUR_1224 (1 << 7) +#define DS1216_HOUR_AMPM (1 << 5) + +struct ds1216_priv { + struct rtc_device *rtc; + void __iomem *ioaddr; +}; + +static const u8 magic[] = { + 0xc5, 0x3a, 0xa3, 0x5c, 0xc5, 0x3a, 0xa3, 0x5c +}; + +/* + * Read the 64 bit we'd like to have - It a series + * of 64 bits showing up in the LSB of the base register. + * + */ +static void ds1216_read(u8 __iomem *ioaddr, u8 *buf) +{ + unsigned char c; + int i, j; + + for (i = 0; i < 8; i++) { + c = 0; + for (j = 0; j < 8; j++) + c |= (readb(ioaddr) & 0x1) << j; + buf[i] = c; + } +} + +static void ds1216_write(u8 __iomem *ioaddr, const u8 *buf) +{ + unsigned char c; + int i, j; + + for (i = 0; i < 8; i++) { + c = buf[i]; + for (j = 0; j < 8; j++) { + writeb(c, ioaddr); + c = c >> 1; + } + } +} + +static void ds1216_switch_ds_to_clock(u8 __iomem *ioaddr) +{ + /* Reset magic pointer */ + readb(ioaddr); + /* Write 64 bit magic to DS1216 */ + ds1216_write(ioaddr, magic); +} + +static int ds1216_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct ds1216_priv *priv = dev_get_drvdata(dev); + struct ds1216_regs regs; + + ds1216_switch_ds_to_clock(priv->ioaddr); + ds1216_read(priv->ioaddr, (u8 *)®s); + + tm->tm_sec = bcd2bin(regs.sec); + tm->tm_min = bcd2bin(regs.min); + if (regs.hour & DS1216_HOUR_1224) { + /* AM/PM mode */ + tm->tm_hour = bcd2bin(regs.hour & 0x1f); + if (regs.hour & DS1216_HOUR_AMPM) + tm->tm_hour += 12; + } else + tm->tm_hour = bcd2bin(regs.hour & 0x3f); + tm->tm_wday = (regs.wday & 7) - 1; + tm->tm_mday = bcd2bin(regs.mday & 0x3f); + tm->tm_mon = bcd2bin(regs.month & 0x1f); + tm->tm_year = bcd2bin(regs.year); + if (tm->tm_year < 70) + tm->tm_year += 100; + + return 0; +} + +static int ds1216_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct ds1216_priv *priv = dev_get_drvdata(dev); + struct ds1216_regs regs; + + ds1216_switch_ds_to_clock(priv->ioaddr); + ds1216_read(priv->ioaddr, (u8 *)®s); + + regs.tsec = 0; /* clear 0.1 and 0.01 seconds */ + regs.sec = bin2bcd(tm->tm_sec); + regs.min = bin2bcd(tm->tm_min); + regs.hour &= DS1216_HOUR_1224; + if (regs.hour && tm->tm_hour > 12) { + regs.hour |= DS1216_HOUR_AMPM; + tm->tm_hour -= 12; + } + regs.hour |= bin2bcd(tm->tm_hour); + regs.wday &= ~7; + regs.wday |= tm->tm_wday; + regs.mday = bin2bcd(tm->tm_mday); + regs.month = bin2bcd(tm->tm_mon); + regs.year = bin2bcd(tm->tm_year % 100); + + ds1216_switch_ds_to_clock(priv->ioaddr); + ds1216_write(priv->ioaddr, (u8 *)®s); + return 0; +} + +static const struct rtc_class_ops ds1216_rtc_ops = { + .read_time = ds1216_rtc_read_time, + .set_time = ds1216_rtc_set_time, +}; + +static int __init ds1216_rtc_probe(struct platform_device *pdev) +{ + struct ds1216_priv *priv; + u8 dummy[8]; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + + priv->ioaddr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->ioaddr)) + return PTR_ERR(priv->ioaddr); + + priv->rtc = devm_rtc_device_register(&pdev->dev, "ds1216", + &ds1216_rtc_ops, THIS_MODULE); + if (IS_ERR(priv->rtc)) + return PTR_ERR(priv->rtc); + + /* dummy read to get clock into a known state */ + ds1216_read(priv->ioaddr, dummy); + return 0; +} + +static struct platform_driver ds1216_rtc_platform_driver = { + .driver = { + .name = "rtc-ds1216", + }, +}; + +module_platform_driver_probe(ds1216_rtc_platform_driver, ds1216_rtc_probe); + +MODULE_AUTHOR("Thomas Bogendoerfer "); +MODULE_DESCRIPTION("DS1216 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rtc-ds1216"); diff --git a/drivers/rtc/rtc-ds1286.c b/drivers/rtc/rtc-ds1286.c new file mode 100644 index 000000000..7acf849d4 --- /dev/null +++ b/drivers/rtc/rtc-ds1286.c @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * DS1286 Real Time Clock interface for Linux + * + * Copyright (C) 1998, 1999, 2000 Ralf Baechle + * Copyright (C) 2008 Thomas Bogendoerfer + * + * Based on code written by Paul Gortmaker. + */ + +#include +#include +#include +#include +#include +#include +#include + +struct ds1286_priv { + struct rtc_device *rtc; + u32 __iomem *rtcregs; + spinlock_t lock; +}; + +static inline u8 ds1286_rtc_read(struct ds1286_priv *priv, int reg) +{ + return __raw_readl(&priv->rtcregs[reg]) & 0xff; +} + +static inline void ds1286_rtc_write(struct ds1286_priv *priv, u8 data, int reg) +{ + __raw_writel(data, &priv->rtcregs[reg]); +} + + +static int ds1286_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct ds1286_priv *priv = dev_get_drvdata(dev); + unsigned long flags; + unsigned char val; + + /* Allow or mask alarm interrupts */ + spin_lock_irqsave(&priv->lock, flags); + val = ds1286_rtc_read(priv, RTC_CMD); + if (enabled) + val &= ~RTC_TDM; + else + val |= RTC_TDM; + ds1286_rtc_write(priv, val, RTC_CMD); + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +#ifdef CONFIG_RTC_INTF_DEV + +static int ds1286_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct ds1286_priv *priv = dev_get_drvdata(dev); + unsigned long flags; + unsigned char val; + + switch (cmd) { + case RTC_WIE_OFF: + /* Mask watchdog int. enab. bit */ + spin_lock_irqsave(&priv->lock, flags); + val = ds1286_rtc_read(priv, RTC_CMD); + val |= RTC_WAM; + ds1286_rtc_write(priv, val, RTC_CMD); + spin_unlock_irqrestore(&priv->lock, flags); + break; + case RTC_WIE_ON: + /* Allow watchdog interrupts. */ + spin_lock_irqsave(&priv->lock, flags); + val = ds1286_rtc_read(priv, RTC_CMD); + val &= ~RTC_WAM; + ds1286_rtc_write(priv, val, RTC_CMD); + spin_unlock_irqrestore(&priv->lock, flags); + break; + default: + return -ENOIOCTLCMD; + } + return 0; +} + +#else +#define ds1286_ioctl NULL +#endif + +#ifdef CONFIG_PROC_FS + +static int ds1286_proc(struct device *dev, struct seq_file *seq) +{ + struct ds1286_priv *priv = dev_get_drvdata(dev); + unsigned char month, cmd, amode; + const char *s; + + month = ds1286_rtc_read(priv, RTC_MONTH); + seq_printf(seq, + "oscillator\t: %s\n" + "square_wave\t: %s\n", + (month & RTC_EOSC) ? "disabled" : "enabled", + (month & RTC_ESQW) ? "disabled" : "enabled"); + + amode = ((ds1286_rtc_read(priv, RTC_MINUTES_ALARM) & 0x80) >> 5) | + ((ds1286_rtc_read(priv, RTC_HOURS_ALARM) & 0x80) >> 6) | + ((ds1286_rtc_read(priv, RTC_DAY_ALARM) & 0x80) >> 7); + switch (amode) { + case 7: + s = "each minute"; + break; + case 3: + s = "minutes match"; + break; + case 1: + s = "hours and minutes match"; + break; + case 0: + s = "days, hours and minutes match"; + break; + default: + s = "invalid"; + break; + } + seq_printf(seq, "alarm_mode\t: %s\n", s); + + cmd = ds1286_rtc_read(priv, RTC_CMD); + seq_printf(seq, + "alarm_enable\t: %s\n" + "wdog_alarm\t: %s\n" + "alarm_mask\t: %s\n" + "wdog_alarm_mask\t: %s\n" + "interrupt_mode\t: %s\n" + "INTB_mode\t: %s_active\n" + "interrupt_pins\t: %s\n", + (cmd & RTC_TDF) ? "yes" : "no", + (cmd & RTC_WAF) ? "yes" : "no", + (cmd & RTC_TDM) ? "disabled" : "enabled", + (cmd & RTC_WAM) ? "disabled" : "enabled", + (cmd & RTC_PU_LVL) ? "pulse" : "level", + (cmd & RTC_IBH_LO) ? "low" : "high", + (cmd & RTC_IPSW) ? "unswapped" : "swapped"); + return 0; +} + +#else +#define ds1286_proc NULL +#endif + +static int ds1286_read_time(struct device *dev, struct rtc_time *tm) +{ + struct ds1286_priv *priv = dev_get_drvdata(dev); + unsigned char save_control; + unsigned long flags; + unsigned long uip_watchdog = jiffies; + + /* + * read RTC once any update in progress is done. The update + * can take just over 2ms. We wait 10 to 20ms. There is no need to + * to poll-wait (up to 1s - eeccch) for the falling edge of RTC_UIP. + * If you need to know *exactly* when a second has started, enable + * periodic update complete interrupts, (via ioctl) and then + * immediately read /dev/rtc which will block until you get the IRQ. + * Once the read clears, read the RTC time (again via ioctl). Easy. + */ + + if (ds1286_rtc_read(priv, RTC_CMD) & RTC_TE) + while (time_before(jiffies, uip_watchdog + 2*HZ/100)) + barrier(); + + /* + * Only the values that we read from the RTC are set. We leave + * tm_wday, tm_yday and tm_isdst untouched. Even though the + * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated + * by the RTC when initially set to a non-zero value. + */ + spin_lock_irqsave(&priv->lock, flags); + save_control = ds1286_rtc_read(priv, RTC_CMD); + ds1286_rtc_write(priv, (save_control|RTC_TE), RTC_CMD); + + tm->tm_sec = ds1286_rtc_read(priv, RTC_SECONDS); + tm->tm_min = ds1286_rtc_read(priv, RTC_MINUTES); + tm->tm_hour = ds1286_rtc_read(priv, RTC_HOURS) & 0x3f; + tm->tm_mday = ds1286_rtc_read(priv, RTC_DATE); + tm->tm_mon = ds1286_rtc_read(priv, RTC_MONTH) & 0x1f; + tm->tm_year = ds1286_rtc_read(priv, RTC_YEAR); + + ds1286_rtc_write(priv, save_control, RTC_CMD); + spin_unlock_irqrestore(&priv->lock, flags); + + tm->tm_sec = bcd2bin(tm->tm_sec); + tm->tm_min = bcd2bin(tm->tm_min); + tm->tm_hour = bcd2bin(tm->tm_hour); + tm->tm_mday = bcd2bin(tm->tm_mday); + tm->tm_mon = bcd2bin(tm->tm_mon); + tm->tm_year = bcd2bin(tm->tm_year); + + /* + * Account for differences between how the RTC uses the values + * and how they are defined in a struct rtc_time; + */ + if (tm->tm_year < 45) + tm->tm_year += 30; + tm->tm_year += 40; + if (tm->tm_year < 70) + tm->tm_year += 100; + + tm->tm_mon--; + + return 0; +} + +static int ds1286_set_time(struct device *dev, struct rtc_time *tm) +{ + struct ds1286_priv *priv = dev_get_drvdata(dev); + unsigned char mon, day, hrs, min, sec; + unsigned char save_control; + unsigned int yrs; + unsigned long flags; + + yrs = tm->tm_year + 1900; + mon = tm->tm_mon + 1; /* tm_mon starts at zero */ + day = tm->tm_mday; + hrs = tm->tm_hour; + min = tm->tm_min; + sec = tm->tm_sec; + + if (yrs < 1970) + return -EINVAL; + + yrs -= 1940; + if (yrs > 255) /* They are unsigned */ + return -EINVAL; + + if (yrs >= 100) + yrs -= 100; + + sec = bin2bcd(sec); + min = bin2bcd(min); + hrs = bin2bcd(hrs); + day = bin2bcd(day); + mon = bin2bcd(mon); + yrs = bin2bcd(yrs); + + spin_lock_irqsave(&priv->lock, flags); + save_control = ds1286_rtc_read(priv, RTC_CMD); + ds1286_rtc_write(priv, (save_control|RTC_TE), RTC_CMD); + + ds1286_rtc_write(priv, yrs, RTC_YEAR); + ds1286_rtc_write(priv, mon, RTC_MONTH); + ds1286_rtc_write(priv, day, RTC_DATE); + ds1286_rtc_write(priv, hrs, RTC_HOURS); + ds1286_rtc_write(priv, min, RTC_MINUTES); + ds1286_rtc_write(priv, sec, RTC_SECONDS); + ds1286_rtc_write(priv, 0, RTC_HUNDREDTH_SECOND); + + ds1286_rtc_write(priv, save_control, RTC_CMD); + spin_unlock_irqrestore(&priv->lock, flags); + return 0; +} + +static int ds1286_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct ds1286_priv *priv = dev_get_drvdata(dev); + unsigned long flags; + + /* + * Only the values that we read from the RTC are set. That + * means only tm_wday, tm_hour, tm_min. + */ + spin_lock_irqsave(&priv->lock, flags); + alm->time.tm_min = ds1286_rtc_read(priv, RTC_MINUTES_ALARM) & 0x7f; + alm->time.tm_hour = ds1286_rtc_read(priv, RTC_HOURS_ALARM) & 0x1f; + alm->time.tm_wday = ds1286_rtc_read(priv, RTC_DAY_ALARM) & 0x07; + ds1286_rtc_read(priv, RTC_CMD); + spin_unlock_irqrestore(&priv->lock, flags); + + alm->time.tm_min = bcd2bin(alm->time.tm_min); + alm->time.tm_hour = bcd2bin(alm->time.tm_hour); + alm->time.tm_sec = 0; + return 0; +} + +static int ds1286_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct ds1286_priv *priv = dev_get_drvdata(dev); + unsigned char hrs, min, sec; + + hrs = alm->time.tm_hour; + min = alm->time.tm_min; + sec = alm->time.tm_sec; + + if (hrs >= 24) + hrs = 0xff; + + if (min >= 60) + min = 0xff; + + if (sec != 0) + return -EINVAL; + + min = bin2bcd(min); + hrs = bin2bcd(hrs); + + spin_lock(&priv->lock); + ds1286_rtc_write(priv, hrs, RTC_HOURS_ALARM); + ds1286_rtc_write(priv, min, RTC_MINUTES_ALARM); + spin_unlock(&priv->lock); + + return 0; +} + +static const struct rtc_class_ops ds1286_ops = { + .ioctl = ds1286_ioctl, + .proc = ds1286_proc, + .read_time = ds1286_read_time, + .set_time = ds1286_set_time, + .read_alarm = ds1286_read_alarm, + .set_alarm = ds1286_set_alarm, + .alarm_irq_enable = ds1286_alarm_irq_enable, +}; + +static int ds1286_probe(struct platform_device *pdev) +{ + struct rtc_device *rtc; + struct ds1286_priv *priv; + + priv = devm_kzalloc(&pdev->dev, sizeof(struct ds1286_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->rtcregs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->rtcregs)) + return PTR_ERR(priv->rtcregs); + + spin_lock_init(&priv->lock); + platform_set_drvdata(pdev, priv); + rtc = devm_rtc_device_register(&pdev->dev, "ds1286", &ds1286_ops, + THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + priv->rtc = rtc; + return 0; +} + +static struct platform_driver ds1286_platform_driver = { + .driver = { + .name = "rtc-ds1286", + }, + .probe = ds1286_probe, +}; + +module_platform_driver(ds1286_platform_driver); + +MODULE_AUTHOR("Thomas Bogendoerfer "); +MODULE_DESCRIPTION("DS1286 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rtc-ds1286"); diff --git a/drivers/rtc/rtc-ds1302.c b/drivers/rtc/rtc-ds1302.c new file mode 100644 index 000000000..ecc7d0307 --- /dev/null +++ b/drivers/rtc/rtc-ds1302.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Dallas DS1302 RTC Support + * + * Copyright (C) 2002 David McCullough + * Copyright (C) 2003 - 2007 Paul Mundt + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define RTC_CMD_READ 0x81 /* Read command */ +#define RTC_CMD_WRITE 0x80 /* Write command */ + +#define RTC_CMD_WRITE_ENABLE 0x00 /* Write enable */ +#define RTC_CMD_WRITE_DISABLE 0x80 /* Write disable */ + +#define RTC_ADDR_RAM0 0x20 /* Address of RAM0 */ +#define RTC_ADDR_TCR 0x08 /* Address of trickle charge register */ +#define RTC_CLCK_BURST 0x1F /* Address of clock burst */ +#define RTC_CLCK_LEN 0x08 /* Size of clock burst */ +#define RTC_ADDR_CTRL 0x07 /* Address of control register */ +#define RTC_ADDR_YEAR 0x06 /* Address of year register */ +#define RTC_ADDR_DAY 0x05 /* Address of day of week register */ +#define RTC_ADDR_MON 0x04 /* Address of month register */ +#define RTC_ADDR_DATE 0x03 /* Address of day of month register */ +#define RTC_ADDR_HOUR 0x02 /* Address of hour register */ +#define RTC_ADDR_MIN 0x01 /* Address of minute register */ +#define RTC_ADDR_SEC 0x00 /* Address of second register */ + +static int ds1302_rtc_set_time(struct device *dev, struct rtc_time *time) +{ + struct spi_device *spi = dev_get_drvdata(dev); + u8 buf[1 + RTC_CLCK_LEN]; + u8 *bp; + int status; + + /* Enable writing */ + bp = buf; + *bp++ = RTC_ADDR_CTRL << 1 | RTC_CMD_WRITE; + *bp++ = RTC_CMD_WRITE_ENABLE; + + status = spi_write_then_read(spi, buf, 2, + NULL, 0); + if (status) + return status; + + /* Write registers starting at the first time/date address. */ + bp = buf; + *bp++ = RTC_CLCK_BURST << 1 | RTC_CMD_WRITE; + + *bp++ = bin2bcd(time->tm_sec); + *bp++ = bin2bcd(time->tm_min); + *bp++ = bin2bcd(time->tm_hour); + *bp++ = bin2bcd(time->tm_mday); + *bp++ = bin2bcd(time->tm_mon + 1); + *bp++ = time->tm_wday + 1; + *bp++ = bin2bcd(time->tm_year % 100); + *bp++ = RTC_CMD_WRITE_DISABLE; + + /* use write-then-read since dma from stack is nonportable */ + return spi_write_then_read(spi, buf, sizeof(buf), + NULL, 0); +} + +static int ds1302_rtc_get_time(struct device *dev, struct rtc_time *time) +{ + struct spi_device *spi = dev_get_drvdata(dev); + u8 addr = RTC_CLCK_BURST << 1 | RTC_CMD_READ; + u8 buf[RTC_CLCK_LEN - 1]; + int status; + + /* Use write-then-read to get all the date/time registers + * since dma from stack is nonportable + */ + status = spi_write_then_read(spi, &addr, sizeof(addr), + buf, sizeof(buf)); + if (status < 0) + return status; + + /* Decode the registers */ + time->tm_sec = bcd2bin(buf[RTC_ADDR_SEC]); + time->tm_min = bcd2bin(buf[RTC_ADDR_MIN]); + time->tm_hour = bcd2bin(buf[RTC_ADDR_HOUR]); + time->tm_wday = buf[RTC_ADDR_DAY] - 1; + time->tm_mday = bcd2bin(buf[RTC_ADDR_DATE]); + time->tm_mon = bcd2bin(buf[RTC_ADDR_MON]) - 1; + time->tm_year = bcd2bin(buf[RTC_ADDR_YEAR]) + 100; + + return 0; +} + +static const struct rtc_class_ops ds1302_rtc_ops = { + .read_time = ds1302_rtc_get_time, + .set_time = ds1302_rtc_set_time, +}; + +static int ds1302_probe(struct spi_device *spi) +{ + struct rtc_device *rtc; + u8 addr; + u8 buf[4]; + u8 *bp; + int status; + + /* Sanity check board setup data. This may be hooked up + * in 3wire mode, but we don't care. Note that unless + * there's an inverter in place, this needs SPI_CS_HIGH! + */ + if (spi->bits_per_word && (spi->bits_per_word != 8)) { + dev_err(&spi->dev, "bad word length\n"); + return -EINVAL; + } else if (spi->max_speed_hz > 2000000) { + dev_err(&spi->dev, "speed is too high\n"); + return -EINVAL; + } else if (spi->mode & SPI_CPHA) { + dev_err(&spi->dev, "bad mode\n"); + return -EINVAL; + } + + addr = RTC_ADDR_CTRL << 1 | RTC_CMD_READ; + status = spi_write_then_read(spi, &addr, sizeof(addr), buf, 1); + if (status < 0) { + dev_err(&spi->dev, "control register read error %d\n", + status); + return status; + } + + if ((buf[0] & ~RTC_CMD_WRITE_DISABLE) != 0) { + status = spi_write_then_read(spi, &addr, sizeof(addr), buf, 1); + if (status < 0) { + dev_err(&spi->dev, "control register read error %d\n", + status); + return status; + } + + if ((buf[0] & ~RTC_CMD_WRITE_DISABLE) != 0) { + dev_err(&spi->dev, "junk in control register\n"); + return -ENODEV; + } + } + if (buf[0] == 0) { + bp = buf; + *bp++ = RTC_ADDR_CTRL << 1 | RTC_CMD_WRITE; + *bp++ = RTC_CMD_WRITE_DISABLE; + + status = spi_write_then_read(spi, buf, 2, NULL, 0); + if (status < 0) { + dev_err(&spi->dev, "control register write error %d\n", + status); + return status; + } + + addr = RTC_ADDR_CTRL << 1 | RTC_CMD_READ; + status = spi_write_then_read(spi, &addr, sizeof(addr), buf, 1); + if (status < 0) { + dev_err(&spi->dev, + "error %d reading control register\n", + status); + return status; + } + + if (buf[0] != RTC_CMD_WRITE_DISABLE) { + dev_err(&spi->dev, "failed to detect chip\n"); + return -ENODEV; + } + } + + spi_set_drvdata(spi, spi); + + rtc = devm_rtc_device_register(&spi->dev, "ds1302", + &ds1302_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) { + status = PTR_ERR(rtc); + dev_err(&spi->dev, "error %d registering rtc\n", status); + return status; + } + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id ds1302_dt_ids[] = { + { .compatible = "maxim,ds1302", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ds1302_dt_ids); +#endif + +static const struct spi_device_id ds1302_spi_ids[] = { + { .name = "ds1302", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(spi, ds1302_spi_ids); + +static struct spi_driver ds1302_driver = { + .driver.name = "rtc-ds1302", + .driver.of_match_table = of_match_ptr(ds1302_dt_ids), + .probe = ds1302_probe, + .id_table = ds1302_spi_ids, +}; + +module_spi_driver(ds1302_driver); + +MODULE_DESCRIPTION("Dallas DS1302 RTC driver"); +MODULE_AUTHOR("Paul Mundt, David McCullough"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c new file mode 100644 index 000000000..d4de40154 --- /dev/null +++ b/drivers/rtc/rtc-ds1305.c @@ -0,0 +1,747 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rtc-ds1305.c -- driver for DS1305 and DS1306 SPI RTC chips + * + * Copyright (C) 2008 David Brownell + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +/* + * Registers ... mask DS1305_WRITE into register address to write, + * otherwise you're reading it. All non-bitmask values are BCD. + */ +#define DS1305_WRITE 0x80 + + +/* RTC date/time ... the main special cases are that we: + * - Need fancy "hours" encoding in 12hour mode + * - Don't rely on the "day-of-week" field (or tm_wday) + * - Are a 21st-century clock (2000 <= year < 2100) + */ +#define DS1305_RTC_LEN 7 /* bytes for RTC regs */ + +#define DS1305_SEC 0x00 /* register addresses */ +#define DS1305_MIN 0x01 +#define DS1305_HOUR 0x02 +# define DS1305_HR_12 0x40 /* set == 12 hr mode */ +# define DS1305_HR_PM 0x20 /* set == PM (12hr mode) */ +#define DS1305_WDAY 0x03 +#define DS1305_MDAY 0x04 +#define DS1305_MON 0x05 +#define DS1305_YEAR 0x06 + + +/* The two alarms have only sec/min/hour/wday fields (ALM_LEN). + * DS1305_ALM_DISABLE disables a match field (some combos are bad). + * + * NOTE that since we don't use WDAY, we limit ourselves to alarms + * only one day into the future (vs potentially up to a week). + * + * NOTE ALSO that while we could generate once-a-second IRQs (UIE), we + * don't currently support them. We'd either need to do it only when + * no alarm is pending (not the standard model), or to use the second + * alarm (implying that this is a DS1305 not DS1306, *and* that either + * it's wired up a second IRQ we know, or that INTCN is set) + */ +#define DS1305_ALM_LEN 4 /* bytes for ALM regs */ +#define DS1305_ALM_DISABLE 0x80 + +#define DS1305_ALM0(r) (0x07 + (r)) /* register addresses */ +#define DS1305_ALM1(r) (0x0b + (r)) + + +/* three control registers */ +#define DS1305_CONTROL_LEN 3 /* bytes of control regs */ + +#define DS1305_CONTROL 0x0f /* register addresses */ +# define DS1305_nEOSC 0x80 /* low enables oscillator */ +# define DS1305_WP 0x40 /* write protect */ +# define DS1305_INTCN 0x04 /* clear == only int0 used */ +# define DS1306_1HZ 0x04 /* enable 1Hz output */ +# define DS1305_AEI1 0x02 /* enable ALM1 IRQ */ +# define DS1305_AEI0 0x01 /* enable ALM0 IRQ */ +#define DS1305_STATUS 0x10 +/* status has just AEIx bits, mirrored as IRQFx */ +#define DS1305_TRICKLE 0x11 +/* trickle bits are defined in */ + +/* a bunch of NVRAM */ +#define DS1305_NVRAM_LEN 96 /* bytes of NVRAM */ + +#define DS1305_NVRAM 0x20 /* register addresses */ + + +struct ds1305 { + struct spi_device *spi; + struct rtc_device *rtc; + + struct work_struct work; + + unsigned long flags; +#define FLAG_EXITING 0 + + bool hr12; + u8 ctrl[DS1305_CONTROL_LEN]; +}; + + +/*----------------------------------------------------------------------*/ + +/* + * Utilities ... tolerate 12-hour AM/PM notation in case of non-Linux + * software (like a bootloader) which may require it. + */ + +static unsigned bcd2hour(u8 bcd) +{ + if (bcd & DS1305_HR_12) { + unsigned hour = 0; + + bcd &= ~DS1305_HR_12; + if (bcd & DS1305_HR_PM) { + hour = 12; + bcd &= ~DS1305_HR_PM; + } + hour += bcd2bin(bcd); + return hour - 1; + } + return bcd2bin(bcd); +} + +static u8 hour2bcd(bool hr12, int hour) +{ + if (hr12) { + hour++; + if (hour <= 12) + return DS1305_HR_12 | bin2bcd(hour); + hour -= 12; + return DS1305_HR_12 | DS1305_HR_PM | bin2bcd(hour); + } + return bin2bcd(hour); +} + +/*----------------------------------------------------------------------*/ + +/* + * Interface to RTC framework + */ + +static int ds1305_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct ds1305 *ds1305 = dev_get_drvdata(dev); + u8 buf[2]; + long err = -EINVAL; + + buf[0] = DS1305_WRITE | DS1305_CONTROL; + buf[1] = ds1305->ctrl[0]; + + if (enabled) { + if (ds1305->ctrl[0] & DS1305_AEI0) + goto done; + buf[1] |= DS1305_AEI0; + } else { + if (!(buf[1] & DS1305_AEI0)) + goto done; + buf[1] &= ~DS1305_AEI0; + } + err = spi_write_then_read(ds1305->spi, buf, sizeof(buf), NULL, 0); + if (err >= 0) + ds1305->ctrl[0] = buf[1]; +done: + return err; + +} + + +/* + * Get/set of date and time is pretty normal. + */ + +static int ds1305_get_time(struct device *dev, struct rtc_time *time) +{ + struct ds1305 *ds1305 = dev_get_drvdata(dev); + u8 addr = DS1305_SEC; + u8 buf[DS1305_RTC_LEN]; + int status; + + /* Use write-then-read to get all the date/time registers + * since dma from stack is nonportable + */ + status = spi_write_then_read(ds1305->spi, &addr, sizeof(addr), + buf, sizeof(buf)); + if (status < 0) + return status; + + dev_vdbg(dev, "%s: %3ph, %4ph\n", "read", &buf[0], &buf[3]); + + /* Decode the registers */ + time->tm_sec = bcd2bin(buf[DS1305_SEC]); + time->tm_min = bcd2bin(buf[DS1305_MIN]); + time->tm_hour = bcd2hour(buf[DS1305_HOUR]); + time->tm_wday = buf[DS1305_WDAY] - 1; + time->tm_mday = bcd2bin(buf[DS1305_MDAY]); + time->tm_mon = bcd2bin(buf[DS1305_MON]) - 1; + time->tm_year = bcd2bin(buf[DS1305_YEAR]) + 100; + + dev_vdbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "read", time->tm_sec, time->tm_min, + time->tm_hour, time->tm_mday, + time->tm_mon, time->tm_year, time->tm_wday); + + return 0; +} + +static int ds1305_set_time(struct device *dev, struct rtc_time *time) +{ + struct ds1305 *ds1305 = dev_get_drvdata(dev); + u8 buf[1 + DS1305_RTC_LEN]; + u8 *bp = buf; + + dev_vdbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "write", time->tm_sec, time->tm_min, + time->tm_hour, time->tm_mday, + time->tm_mon, time->tm_year, time->tm_wday); + + /* Write registers starting at the first time/date address. */ + *bp++ = DS1305_WRITE | DS1305_SEC; + + *bp++ = bin2bcd(time->tm_sec); + *bp++ = bin2bcd(time->tm_min); + *bp++ = hour2bcd(ds1305->hr12, time->tm_hour); + *bp++ = (time->tm_wday < 7) ? (time->tm_wday + 1) : 1; + *bp++ = bin2bcd(time->tm_mday); + *bp++ = bin2bcd(time->tm_mon + 1); + *bp++ = bin2bcd(time->tm_year - 100); + + dev_dbg(dev, "%s: %3ph, %4ph\n", "write", &buf[1], &buf[4]); + + /* use write-then-read since dma from stack is nonportable */ + return spi_write_then_read(ds1305->spi, buf, sizeof(buf), + NULL, 0); +} + +/* + * Get/set of alarm is a bit funky: + * + * - First there's the inherent raciness of getting the (partitioned) + * status of an alarm that could trigger while we're reading parts + * of that status. + * + * - Second there's its limited range (we could increase it a bit by + * relying on WDAY), which means it will easily roll over. + * + * - Third there's the choice of two alarms and alarm signals. + * Here we use ALM0 and expect that nINT0 (open drain) is used; + * that's the only real option for DS1306 runtime alarms, and is + * natural on DS1305. + * + * - Fourth, there's also ALM1, and a second interrupt signal: + * + On DS1305 ALM1 uses nINT1 (when INTCN=1) else nINT0; + * + On DS1306 ALM1 only uses INT1 (an active high pulse) + * and it won't work when VCC1 is active. + * + * So to be most general, we should probably set both alarms to the + * same value, letting ALM1 be the wakeup event source on DS1306 + * and handling several wiring options on DS1305. + * + * - Fifth, we support the polled mode (as well as possible; why not?) + * even when no interrupt line is wired to an IRQ. + */ + +/* + * Context: caller holds rtc->ops_lock (to protect ds1305->ctrl) + */ +static int ds1305_get_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct ds1305 *ds1305 = dev_get_drvdata(dev); + struct spi_device *spi = ds1305->spi; + u8 addr; + int status; + u8 buf[DS1305_ALM_LEN]; + + /* Refresh control register cache BEFORE reading ALM0 registers, + * since reading alarm registers acks any pending IRQ. That + * makes returning "pending" status a bit of a lie, but that bit + * of EFI status is at best fragile anyway (given IRQ handlers). + */ + addr = DS1305_CONTROL; + status = spi_write_then_read(spi, &addr, sizeof(addr), + ds1305->ctrl, sizeof(ds1305->ctrl)); + if (status < 0) + return status; + + alm->enabled = !!(ds1305->ctrl[0] & DS1305_AEI0); + alm->pending = !!(ds1305->ctrl[1] & DS1305_AEI0); + + /* get and check ALM0 registers */ + addr = DS1305_ALM0(DS1305_SEC); + status = spi_write_then_read(spi, &addr, sizeof(addr), + buf, sizeof(buf)); + if (status < 0) + return status; + + dev_vdbg(dev, "%s: %02x %02x %02x %02x\n", + "alm0 read", buf[DS1305_SEC], buf[DS1305_MIN], + buf[DS1305_HOUR], buf[DS1305_WDAY]); + + if ((DS1305_ALM_DISABLE & buf[DS1305_SEC]) + || (DS1305_ALM_DISABLE & buf[DS1305_MIN]) + || (DS1305_ALM_DISABLE & buf[DS1305_HOUR])) + return -EIO; + + /* Stuff these values into alm->time and let RTC framework code + * fill in the rest ... and also handle rollover to tomorrow when + * that's needed. + */ + alm->time.tm_sec = bcd2bin(buf[DS1305_SEC]); + alm->time.tm_min = bcd2bin(buf[DS1305_MIN]); + alm->time.tm_hour = bcd2hour(buf[DS1305_HOUR]); + + return 0; +} + +/* + * Context: caller holds rtc->ops_lock (to protect ds1305->ctrl) + */ +static int ds1305_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct ds1305 *ds1305 = dev_get_drvdata(dev); + struct spi_device *spi = ds1305->spi; + unsigned long now, later; + struct rtc_time tm; + int status; + u8 buf[1 + DS1305_ALM_LEN]; + + /* convert desired alarm to time_t */ + later = rtc_tm_to_time64(&alm->time); + + /* Read current time as time_t */ + status = ds1305_get_time(dev, &tm); + if (status < 0) + return status; + now = rtc_tm_to_time64(&tm); + + /* make sure alarm fires within the next 24 hours */ + if (later <= now) + return -EINVAL; + if ((later - now) > ds1305->rtc->alarm_offset_max) + return -ERANGE; + + /* disable alarm if needed */ + if (ds1305->ctrl[0] & DS1305_AEI0) { + ds1305->ctrl[0] &= ~DS1305_AEI0; + + buf[0] = DS1305_WRITE | DS1305_CONTROL; + buf[1] = ds1305->ctrl[0]; + status = spi_write_then_read(ds1305->spi, buf, 2, NULL, 0); + if (status < 0) + return status; + } + + /* write alarm */ + buf[0] = DS1305_WRITE | DS1305_ALM0(DS1305_SEC); + buf[1 + DS1305_SEC] = bin2bcd(alm->time.tm_sec); + buf[1 + DS1305_MIN] = bin2bcd(alm->time.tm_min); + buf[1 + DS1305_HOUR] = hour2bcd(ds1305->hr12, alm->time.tm_hour); + buf[1 + DS1305_WDAY] = DS1305_ALM_DISABLE; + + dev_dbg(dev, "%s: %02x %02x %02x %02x\n", + "alm0 write", buf[1 + DS1305_SEC], buf[1 + DS1305_MIN], + buf[1 + DS1305_HOUR], buf[1 + DS1305_WDAY]); + + status = spi_write_then_read(spi, buf, sizeof(buf), NULL, 0); + if (status < 0) + return status; + + /* enable alarm if requested */ + if (alm->enabled) { + ds1305->ctrl[0] |= DS1305_AEI0; + + buf[0] = DS1305_WRITE | DS1305_CONTROL; + buf[1] = ds1305->ctrl[0]; + status = spi_write_then_read(ds1305->spi, buf, 2, NULL, 0); + } + + return status; +} + +#ifdef CONFIG_PROC_FS + +static int ds1305_proc(struct device *dev, struct seq_file *seq) +{ + struct ds1305 *ds1305 = dev_get_drvdata(dev); + char *diodes = "no"; + char *resistors = ""; + + /* ctrl[2] is treated as read-only; no locking needed */ + if ((ds1305->ctrl[2] & 0xf0) == DS1305_TRICKLE_MAGIC) { + switch (ds1305->ctrl[2] & 0x0c) { + case DS1305_TRICKLE_DS2: + diodes = "2 diodes, "; + break; + case DS1305_TRICKLE_DS1: + diodes = "1 diode, "; + break; + default: + goto done; + } + switch (ds1305->ctrl[2] & 0x03) { + case DS1305_TRICKLE_2K: + resistors = "2k Ohm"; + break; + case DS1305_TRICKLE_4K: + resistors = "4k Ohm"; + break; + case DS1305_TRICKLE_8K: + resistors = "8k Ohm"; + break; + default: + diodes = "no"; + break; + } + } + +done: + seq_printf(seq, "trickle_charge\t: %s%s\n", diodes, resistors); + + return 0; +} + +#else +#define ds1305_proc NULL +#endif + +static const struct rtc_class_ops ds1305_ops = { + .read_time = ds1305_get_time, + .set_time = ds1305_set_time, + .read_alarm = ds1305_get_alarm, + .set_alarm = ds1305_set_alarm, + .proc = ds1305_proc, + .alarm_irq_enable = ds1305_alarm_irq_enable, +}; + +static void ds1305_work(struct work_struct *work) +{ + struct ds1305 *ds1305 = container_of(work, struct ds1305, work); + struct spi_device *spi = ds1305->spi; + u8 buf[3]; + int status; + + /* lock to protect ds1305->ctrl */ + rtc_lock(ds1305->rtc); + + /* Disable the IRQ, and clear its status ... for now, we "know" + * that if more than one alarm is active, they're in sync. + * Note that reading ALM data registers also clears IRQ status. + */ + ds1305->ctrl[0] &= ~(DS1305_AEI1 | DS1305_AEI0); + ds1305->ctrl[1] = 0; + + buf[0] = DS1305_WRITE | DS1305_CONTROL; + buf[1] = ds1305->ctrl[0]; + buf[2] = 0; + + status = spi_write_then_read(spi, buf, sizeof(buf), + NULL, 0); + if (status < 0) + dev_dbg(&spi->dev, "clear irq --> %d\n", status); + + rtc_unlock(ds1305->rtc); + + if (!test_bit(FLAG_EXITING, &ds1305->flags)) + enable_irq(spi->irq); + + rtc_update_irq(ds1305->rtc, 1, RTC_AF | RTC_IRQF); +} + +/* + * This "real" IRQ handler hands off to a workqueue mostly to allow + * mutex locking for ds1305->ctrl ... unlike I2C, we could issue async + * I/O requests in IRQ context (to clear the IRQ status). + */ +static irqreturn_t ds1305_irq(int irq, void *p) +{ + struct ds1305 *ds1305 = p; + + disable_irq(irq); + schedule_work(&ds1305->work); + return IRQ_HANDLED; +} + +/*----------------------------------------------------------------------*/ + +/* + * Interface for NVRAM + */ + +static void msg_init(struct spi_message *m, struct spi_transfer *x, + u8 *addr, size_t count, char *tx, char *rx) +{ + spi_message_init(m); + memset(x, 0, 2 * sizeof(*x)); + + x->tx_buf = addr; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + + x->tx_buf = tx; + x->rx_buf = rx; + x->len = count; + spi_message_add_tail(x, m); +} + +static int ds1305_nvram_read(void *priv, unsigned int off, void *buf, + size_t count) +{ + struct ds1305 *ds1305 = priv; + struct spi_device *spi = ds1305->spi; + u8 addr; + struct spi_message m; + struct spi_transfer x[2]; + + addr = DS1305_NVRAM + off; + msg_init(&m, x, &addr, count, NULL, buf); + + return spi_sync(spi, &m); +} + +static int ds1305_nvram_write(void *priv, unsigned int off, void *buf, + size_t count) +{ + struct ds1305 *ds1305 = priv; + struct spi_device *spi = ds1305->spi; + u8 addr; + struct spi_message m; + struct spi_transfer x[2]; + + addr = (DS1305_WRITE | DS1305_NVRAM) + off; + msg_init(&m, x, &addr, count, buf, NULL); + + return spi_sync(spi, &m); +} + +/*----------------------------------------------------------------------*/ + +/* + * Interface to SPI stack + */ + +static int ds1305_probe(struct spi_device *spi) +{ + struct ds1305 *ds1305; + int status; + u8 addr, value; + struct ds1305_platform_data *pdata = dev_get_platdata(&spi->dev); + bool write_ctrl = false; + struct nvmem_config ds1305_nvmem_cfg = { + .name = "ds1305_nvram", + .word_size = 1, + .stride = 1, + .size = DS1305_NVRAM_LEN, + .reg_read = ds1305_nvram_read, + .reg_write = ds1305_nvram_write, + }; + + /* Sanity check board setup data. This may be hooked up + * in 3wire mode, but we don't care. Note that unless + * there's an inverter in place, this needs SPI_CS_HIGH! + */ + if ((spi->bits_per_word && spi->bits_per_word != 8) + || (spi->max_speed_hz > 2000000) + || !(spi->mode & SPI_CPHA)) + return -EINVAL; + + /* set up driver data */ + ds1305 = devm_kzalloc(&spi->dev, sizeof(*ds1305), GFP_KERNEL); + if (!ds1305) + return -ENOMEM; + ds1305->spi = spi; + spi_set_drvdata(spi, ds1305); + + /* read and cache control registers */ + addr = DS1305_CONTROL; + status = spi_write_then_read(spi, &addr, sizeof(addr), + ds1305->ctrl, sizeof(ds1305->ctrl)); + if (status < 0) { + dev_dbg(&spi->dev, "can't %s, %d\n", + "read", status); + return status; + } + + dev_dbg(&spi->dev, "ctrl %s: %3ph\n", "read", ds1305->ctrl); + + /* Sanity check register values ... partially compensating for the + * fact that SPI has no device handshake. A pullup on MISO would + * make these tests fail; but not all systems will have one. If + * some register is neither 0x00 nor 0xff, a chip is likely there. + */ + if ((ds1305->ctrl[0] & 0x38) != 0 || (ds1305->ctrl[1] & 0xfc) != 0) { + dev_dbg(&spi->dev, "RTC chip is not present\n"); + return -ENODEV; + } + if (ds1305->ctrl[2] == 0) + dev_dbg(&spi->dev, "chip may not be present\n"); + + /* enable writes if needed ... if we were paranoid it would + * make sense to enable them only when absolutely necessary. + */ + if (ds1305->ctrl[0] & DS1305_WP) { + u8 buf[2]; + + ds1305->ctrl[0] &= ~DS1305_WP; + + buf[0] = DS1305_WRITE | DS1305_CONTROL; + buf[1] = ds1305->ctrl[0]; + status = spi_write_then_read(spi, buf, sizeof(buf), NULL, 0); + + dev_dbg(&spi->dev, "clear WP --> %d\n", status); + if (status < 0) + return status; + } + + /* on DS1305, maybe start oscillator; like most low power + * oscillators, it may take a second to stabilize + */ + if (ds1305->ctrl[0] & DS1305_nEOSC) { + ds1305->ctrl[0] &= ~DS1305_nEOSC; + write_ctrl = true; + dev_warn(&spi->dev, "SET TIME!\n"); + } + + /* ack any pending IRQs */ + if (ds1305->ctrl[1]) { + ds1305->ctrl[1] = 0; + write_ctrl = true; + } + + /* this may need one-time (re)init */ + if (pdata) { + /* maybe enable trickle charge */ + if (((ds1305->ctrl[2] & 0xf0) != DS1305_TRICKLE_MAGIC)) { + ds1305->ctrl[2] = DS1305_TRICKLE_MAGIC + | pdata->trickle; + write_ctrl = true; + } + + /* on DS1306, configure 1 Hz signal */ + if (pdata->is_ds1306) { + if (pdata->en_1hz) { + if (!(ds1305->ctrl[0] & DS1306_1HZ)) { + ds1305->ctrl[0] |= DS1306_1HZ; + write_ctrl = true; + } + } else { + if (ds1305->ctrl[0] & DS1306_1HZ) { + ds1305->ctrl[0] &= ~DS1306_1HZ; + write_ctrl = true; + } + } + } + } + + if (write_ctrl) { + u8 buf[4]; + + buf[0] = DS1305_WRITE | DS1305_CONTROL; + buf[1] = ds1305->ctrl[0]; + buf[2] = ds1305->ctrl[1]; + buf[3] = ds1305->ctrl[2]; + status = spi_write_then_read(spi, buf, sizeof(buf), NULL, 0); + if (status < 0) { + dev_dbg(&spi->dev, "can't %s, %d\n", + "write", status); + return status; + } + + dev_dbg(&spi->dev, "ctrl %s: %3ph\n", "write", ds1305->ctrl); + } + + /* see if non-Linux software set up AM/PM mode */ + addr = DS1305_HOUR; + status = spi_write_then_read(spi, &addr, sizeof(addr), + &value, sizeof(value)); + if (status < 0) { + dev_dbg(&spi->dev, "read HOUR --> %d\n", status); + return status; + } + + ds1305->hr12 = (DS1305_HR_12 & value) != 0; + if (ds1305->hr12) + dev_dbg(&spi->dev, "AM/PM\n"); + + /* register RTC ... from here on, ds1305->ctrl needs locking */ + ds1305->rtc = devm_rtc_allocate_device(&spi->dev); + if (IS_ERR(ds1305->rtc)) + return PTR_ERR(ds1305->rtc); + + ds1305->rtc->ops = &ds1305_ops; + ds1305->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + ds1305->rtc->range_max = RTC_TIMESTAMP_END_2099; + ds1305->rtc->alarm_offset_max = 24 * 60 * 60; + + ds1305_nvmem_cfg.priv = ds1305; + status = devm_rtc_register_device(ds1305->rtc); + if (status) + return status; + + devm_rtc_nvmem_register(ds1305->rtc, &ds1305_nvmem_cfg); + + /* Maybe set up alarm IRQ; be ready to handle it triggering right + * away. NOTE that we don't share this. The signal is active low, + * and we can't ack it before a SPI message delay. We temporarily + * disable the IRQ until it's acked, which lets us work with more + * IRQ trigger modes (not all IRQ controllers can do falling edge). + */ + if (spi->irq) { + INIT_WORK(&ds1305->work, ds1305_work); + status = devm_request_irq(&spi->dev, spi->irq, ds1305_irq, + 0, dev_name(&ds1305->rtc->dev), ds1305); + if (status < 0) { + dev_err(&spi->dev, "request_irq %d --> %d\n", + spi->irq, status); + } else { + device_set_wakeup_capable(&spi->dev, 1); + } + } + + return 0; +} + +static void ds1305_remove(struct spi_device *spi) +{ + struct ds1305 *ds1305 = spi_get_drvdata(spi); + + /* carefully shut down irq and workqueue, if present */ + if (spi->irq) { + set_bit(FLAG_EXITING, &ds1305->flags); + devm_free_irq(&spi->dev, spi->irq, ds1305); + cancel_work_sync(&ds1305->work); + } +} + +static struct spi_driver ds1305_driver = { + .driver.name = "rtc-ds1305", + .probe = ds1305_probe, + .remove = ds1305_remove, + /* REVISIT add suspend/resume */ +}; + +module_spi_driver(ds1305_driver); + +MODULE_DESCRIPTION("RTC driver for DS1305 and DS1306 chips"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:rtc-ds1305"); diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c new file mode 100644 index 000000000..522f7eef3 --- /dev/null +++ b/drivers/rtc/rtc-ds1307.c @@ -0,0 +1,2178 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rtc-ds1307.c - RTC driver for some mostly-compatible I2C chips. + * + * Copyright (C) 2005 James Chapman (ds1337 core) + * Copyright (C) 2006 David Brownell + * Copyright (C) 2009 Matthias Fuchs (rx8025 support) + * Copyright (C) 2012 Bertrand Achard (nvram access fixes) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * We can't determine type by probing, but if we expect pre-Linux code + * to have set the chip up as a clock (turning on the oscillator and + * setting the date and time), Linux can ignore the non-clock features. + * That's a natural job for a factory or repair bench. + */ +enum ds_type { + unknown_ds_type, /* always first and 0 */ + ds_1307, + ds_1308, + ds_1337, + ds_1338, + ds_1339, + ds_1340, + ds_1341, + ds_1388, + ds_3231, + m41t0, + m41t00, + m41t11, + mcp794xx, + rx_8025, + rx_8130, + last_ds_type /* always last */ + /* rs5c372 too? different address... */ +}; + +/* RTC registers don't differ much, except for the century flag */ +#define DS1307_REG_SECS 0x00 /* 00-59 */ +# define DS1307_BIT_CH 0x80 +# define DS1340_BIT_nEOSC 0x80 +# define MCP794XX_BIT_ST 0x80 +#define DS1307_REG_MIN 0x01 /* 00-59 */ +# define M41T0_BIT_OF 0x80 +#define DS1307_REG_HOUR 0x02 /* 00-23, or 1-12{am,pm} */ +# define DS1307_BIT_12HR 0x40 /* in REG_HOUR */ +# define DS1307_BIT_PM 0x20 /* in REG_HOUR */ +# define DS1340_BIT_CENTURY_EN 0x80 /* in REG_HOUR */ +# define DS1340_BIT_CENTURY 0x40 /* in REG_HOUR */ +#define DS1307_REG_WDAY 0x03 /* 01-07 */ +# define MCP794XX_BIT_OSCRUN BIT(5) +# define MCP794XX_BIT_VBATEN 0x08 +#define DS1307_REG_MDAY 0x04 /* 01-31 */ +#define DS1307_REG_MONTH 0x05 /* 01-12 */ +# define DS1337_BIT_CENTURY 0x80 /* in REG_MONTH */ +#define DS1307_REG_YEAR 0x06 /* 00-99 */ + +/* + * Other registers (control, status, alarms, trickle charge, NVRAM, etc) + * start at 7, and they differ a LOT. Only control and status matter for + * basic RTC date and time functionality; be careful using them. + */ +#define DS1307_REG_CONTROL 0x07 /* or ds1338 */ +# define DS1307_BIT_OUT 0x80 +# define DS1338_BIT_OSF 0x20 +# define DS1307_BIT_SQWE 0x10 +# define DS1307_BIT_RS1 0x02 +# define DS1307_BIT_RS0 0x01 +#define DS1337_REG_CONTROL 0x0e +# define DS1337_BIT_nEOSC 0x80 +# define DS1339_BIT_BBSQI 0x20 +# define DS3231_BIT_BBSQW 0x40 /* same as BBSQI */ +# define DS1337_BIT_RS2 0x10 +# define DS1337_BIT_RS1 0x08 +# define DS1337_BIT_INTCN 0x04 +# define DS1337_BIT_A2IE 0x02 +# define DS1337_BIT_A1IE 0x01 +#define DS1340_REG_CONTROL 0x07 +# define DS1340_BIT_OUT 0x80 +# define DS1340_BIT_FT 0x40 +# define DS1340_BIT_CALIB_SIGN 0x20 +# define DS1340_M_CALIBRATION 0x1f +#define DS1340_REG_FLAG 0x09 +# define DS1340_BIT_OSF 0x80 +#define DS1337_REG_STATUS 0x0f +# define DS1337_BIT_OSF 0x80 +# define DS3231_BIT_EN32KHZ 0x08 +# define DS1337_BIT_A2I 0x02 +# define DS1337_BIT_A1I 0x01 +#define DS1339_REG_ALARM1_SECS 0x07 + +#define DS13XX_TRICKLE_CHARGER_MAGIC 0xa0 + +#define RX8025_REG_CTRL1 0x0e +# define RX8025_BIT_2412 0x20 +#define RX8025_REG_CTRL2 0x0f +# define RX8025_BIT_PON 0x10 +# define RX8025_BIT_VDET 0x40 +# define RX8025_BIT_XST 0x20 + +#define RX8130_REG_ALARM_MIN 0x17 +#define RX8130_REG_ALARM_HOUR 0x18 +#define RX8130_REG_ALARM_WEEK_OR_DAY 0x19 +#define RX8130_REG_EXTENSION 0x1c +#define RX8130_REG_EXTENSION_WADA BIT(3) +#define RX8130_REG_FLAG 0x1d +#define RX8130_REG_FLAG_VLF BIT(1) +#define RX8130_REG_FLAG_AF BIT(3) +#define RX8130_REG_CONTROL0 0x1e +#define RX8130_REG_CONTROL0_AIE BIT(3) +#define RX8130_REG_CONTROL1 0x1f +#define RX8130_REG_CONTROL1_INIEN BIT(4) +#define RX8130_REG_CONTROL1_CHGEN BIT(5) + +#define MCP794XX_REG_CONTROL 0x07 +# define MCP794XX_BIT_ALM0_EN 0x10 +# define MCP794XX_BIT_ALM1_EN 0x20 +#define MCP794XX_REG_ALARM0_BASE 0x0a +#define MCP794XX_REG_ALARM0_CTRL 0x0d +#define MCP794XX_REG_ALARM1_BASE 0x11 +#define MCP794XX_REG_ALARM1_CTRL 0x14 +# define MCP794XX_BIT_ALMX_IF BIT(3) +# define MCP794XX_BIT_ALMX_C0 BIT(4) +# define MCP794XX_BIT_ALMX_C1 BIT(5) +# define MCP794XX_BIT_ALMX_C2 BIT(6) +# define MCP794XX_BIT_ALMX_POL BIT(7) +# define MCP794XX_MSK_ALMX_MATCH (MCP794XX_BIT_ALMX_C0 | \ + MCP794XX_BIT_ALMX_C1 | \ + MCP794XX_BIT_ALMX_C2) + +#define M41TXX_REG_CONTROL 0x07 +# define M41TXX_BIT_OUT BIT(7) +# define M41TXX_BIT_FT BIT(6) +# define M41TXX_BIT_CALIB_SIGN BIT(5) +# define M41TXX_M_CALIBRATION GENMASK(4, 0) + +#define DS1388_REG_WDOG_HUN_SECS 0x08 +#define DS1388_REG_WDOG_SECS 0x09 +#define DS1388_REG_FLAG 0x0b +# define DS1388_BIT_WF BIT(6) +# define DS1388_BIT_OSF BIT(7) +#define DS1388_REG_CONTROL 0x0c +# define DS1388_BIT_RST BIT(0) +# define DS1388_BIT_WDE BIT(1) +# define DS1388_BIT_nEOSC BIT(7) + +/* negative offset step is -2.034ppm */ +#define M41TXX_NEG_OFFSET_STEP_PPB 2034 +/* positive offset step is +4.068ppm */ +#define M41TXX_POS_OFFSET_STEP_PPB 4068 +/* Min and max values supported with 'offset' interface by M41TXX */ +#define M41TXX_MIN_OFFSET ((-31) * M41TXX_NEG_OFFSET_STEP_PPB) +#define M41TXX_MAX_OFFSET ((31) * M41TXX_POS_OFFSET_STEP_PPB) + +struct ds1307 { + enum ds_type type; + struct device *dev; + struct regmap *regmap; + const char *name; + struct rtc_device *rtc; +#ifdef CONFIG_COMMON_CLK + struct clk_hw clks[2]; +#endif +}; + +struct chip_desc { + unsigned alarm:1; + u16 nvram_offset; + u16 nvram_size; + u8 offset; /* register's offset */ + u8 century_reg; + u8 century_enable_bit; + u8 century_bit; + u8 bbsqi_bit; + irq_handler_t irq_handler; + const struct rtc_class_ops *rtc_ops; + u16 trickle_charger_reg; + u8 (*do_trickle_setup)(struct ds1307 *, u32, + bool); + /* Does the RTC require trickle-resistor-ohms to select the value of + * the resistor between Vcc and Vbackup? + */ + bool requires_trickle_resistor; + /* Some RTC's batteries and supercaps were charged by default, others + * allow charging but were not configured previously to do so. + * Remember this behavior to stay backwards compatible. + */ + bool charge_default; +}; + +static const struct chip_desc chips[last_ds_type]; + +static int ds1307_get_time(struct device *dev, struct rtc_time *t) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + int tmp, ret; + const struct chip_desc *chip = &chips[ds1307->type]; + u8 regs[7]; + + if (ds1307->type == rx_8130) { + unsigned int regflag; + ret = regmap_read(ds1307->regmap, RX8130_REG_FLAG, ®flag); + if (ret) { + dev_err(dev, "%s error %d\n", "read", ret); + return ret; + } + + if (regflag & RX8130_REG_FLAG_VLF) { + dev_warn_once(dev, "oscillator failed, set time!\n"); + return -EINVAL; + } + } + + /* read the RTC date and time registers all at once */ + ret = regmap_bulk_read(ds1307->regmap, chip->offset, regs, + sizeof(regs)); + if (ret) { + dev_err(dev, "%s error %d\n", "read", ret); + return ret; + } + + dev_dbg(dev, "%s: %7ph\n", "read", regs); + + /* if oscillator fail bit is set, no data can be trusted */ + if (ds1307->type == m41t0 && + regs[DS1307_REG_MIN] & M41T0_BIT_OF) { + dev_warn_once(dev, "oscillator failed, set time!\n"); + return -EINVAL; + } else if (ds1307->type == mcp794xx && + !(regs[DS1307_REG_WDAY] & MCP794XX_BIT_OSCRUN)) { + dev_warn_once(dev, "oscillator failed, set time!\n"); + return -EINVAL; + } + + tmp = regs[DS1307_REG_SECS]; + switch (ds1307->type) { + case ds_1307: + case m41t0: + case m41t00: + case m41t11: + if (tmp & DS1307_BIT_CH) + return -EINVAL; + break; + case ds_1308: + case ds_1338: + if (tmp & DS1307_BIT_CH) + return -EINVAL; + + ret = regmap_read(ds1307->regmap, DS1307_REG_CONTROL, &tmp); + if (ret) + return ret; + if (tmp & DS1338_BIT_OSF) + return -EINVAL; + break; + case ds_1337: + case ds_1339: + case ds_1341: + case ds_3231: + ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &tmp); + if (ret) + return ret; + if (tmp & DS1337_BIT_OSF) + return -EINVAL; + break; + case ds_1340: + if (tmp & DS1340_BIT_nEOSC) + return -EINVAL; + + ret = regmap_read(ds1307->regmap, DS1340_REG_FLAG, &tmp); + if (ret) + return ret; + if (tmp & DS1340_BIT_OSF) + return -EINVAL; + break; + case ds_1388: + ret = regmap_read(ds1307->regmap, DS1388_REG_FLAG, &tmp); + if (ret) + return ret; + if (tmp & DS1388_BIT_OSF) + return -EINVAL; + break; + case mcp794xx: + if (!(tmp & MCP794XX_BIT_ST)) + return -EINVAL; + + break; + default: + break; + } + + t->tm_sec = bcd2bin(regs[DS1307_REG_SECS] & 0x7f); + t->tm_min = bcd2bin(regs[DS1307_REG_MIN] & 0x7f); + tmp = regs[DS1307_REG_HOUR] & 0x3f; + t->tm_hour = bcd2bin(tmp); + /* rx8130 is bit position, not BCD */ + if (ds1307->type == rx_8130) + t->tm_wday = fls(regs[DS1307_REG_WDAY] & 0x7f) - 1; + else + t->tm_wday = bcd2bin(regs[DS1307_REG_WDAY] & 0x07) - 1; + t->tm_mday = bcd2bin(regs[DS1307_REG_MDAY] & 0x3f); + tmp = regs[DS1307_REG_MONTH] & 0x1f; + t->tm_mon = bcd2bin(tmp) - 1; + t->tm_year = bcd2bin(regs[DS1307_REG_YEAR]) + 100; + + if (regs[chip->century_reg] & chip->century_bit && + IS_ENABLED(CONFIG_RTC_DRV_DS1307_CENTURY)) + t->tm_year += 100; + + dev_dbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "read", t->tm_sec, t->tm_min, + t->tm_hour, t->tm_mday, + t->tm_mon, t->tm_year, t->tm_wday); + + return 0; +} + +static int ds1307_set_time(struct device *dev, struct rtc_time *t) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + const struct chip_desc *chip = &chips[ds1307->type]; + int result; + int tmp; + u8 regs[7]; + + dev_dbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "write", t->tm_sec, t->tm_min, + t->tm_hour, t->tm_mday, + t->tm_mon, t->tm_year, t->tm_wday); + + if (t->tm_year < 100) + return -EINVAL; + +#ifdef CONFIG_RTC_DRV_DS1307_CENTURY + if (t->tm_year > (chip->century_bit ? 299 : 199)) + return -EINVAL; +#else + if (t->tm_year > 199) + return -EINVAL; +#endif + + regs[DS1307_REG_SECS] = bin2bcd(t->tm_sec); + regs[DS1307_REG_MIN] = bin2bcd(t->tm_min); + regs[DS1307_REG_HOUR] = bin2bcd(t->tm_hour); + /* rx8130 is bit position, not BCD */ + if (ds1307->type == rx_8130) + regs[DS1307_REG_WDAY] = 1 << t->tm_wday; + else + regs[DS1307_REG_WDAY] = bin2bcd(t->tm_wday + 1); + regs[DS1307_REG_MDAY] = bin2bcd(t->tm_mday); + regs[DS1307_REG_MONTH] = bin2bcd(t->tm_mon + 1); + + /* assume 20YY not 19YY */ + tmp = t->tm_year % 100; + regs[DS1307_REG_YEAR] = bin2bcd(tmp); + + if (chip->century_enable_bit) + regs[chip->century_reg] |= chip->century_enable_bit; + if (t->tm_year > 199 && chip->century_bit) + regs[chip->century_reg] |= chip->century_bit; + + switch (ds1307->type) { + case ds_1308: + case ds_1338: + regmap_update_bits(ds1307->regmap, DS1307_REG_CONTROL, + DS1338_BIT_OSF, 0); + break; + case ds_1337: + case ds_1339: + case ds_1341: + case ds_3231: + regmap_update_bits(ds1307->regmap, DS1337_REG_STATUS, + DS1337_BIT_OSF, 0); + break; + case ds_1340: + regmap_update_bits(ds1307->regmap, DS1340_REG_FLAG, + DS1340_BIT_OSF, 0); + break; + case ds_1388: + regmap_update_bits(ds1307->regmap, DS1388_REG_FLAG, + DS1388_BIT_OSF, 0); + break; + case mcp794xx: + /* + * these bits were cleared when preparing the date/time + * values and need to be set again before writing the + * regsfer out to the device. + */ + regs[DS1307_REG_SECS] |= MCP794XX_BIT_ST; + regs[DS1307_REG_WDAY] |= MCP794XX_BIT_VBATEN; + break; + default: + break; + } + + dev_dbg(dev, "%s: %7ph\n", "write", regs); + + result = regmap_bulk_write(ds1307->regmap, chip->offset, regs, + sizeof(regs)); + if (result) { + dev_err(dev, "%s error %d\n", "write", result); + return result; + } + + if (ds1307->type == rx_8130) { + /* clear Voltage Loss Flag as data is available now */ + result = regmap_write(ds1307->regmap, RX8130_REG_FLAG, + ~(u8)RX8130_REG_FLAG_VLF); + if (result) { + dev_err(dev, "%s error %d\n", "write", result); + return result; + } + } + + return 0; +} + +static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + int ret; + u8 regs[9]; + + /* read all ALARM1, ALARM2, and status registers at once */ + ret = regmap_bulk_read(ds1307->regmap, DS1339_REG_ALARM1_SECS, + regs, sizeof(regs)); + if (ret) { + dev_err(dev, "%s error %d\n", "alarm read", ret); + return ret; + } + + dev_dbg(dev, "%s: %4ph, %3ph, %2ph\n", "alarm read", + ®s[0], ®s[4], ®s[7]); + + /* + * report alarm time (ALARM1); assume 24 hour and day-of-month modes, + * and that all four fields are checked matches + */ + t->time.tm_sec = bcd2bin(regs[0] & 0x7f); + t->time.tm_min = bcd2bin(regs[1] & 0x7f); + t->time.tm_hour = bcd2bin(regs[2] & 0x3f); + t->time.tm_mday = bcd2bin(regs[3] & 0x3f); + + /* ... and status */ + t->enabled = !!(regs[7] & DS1337_BIT_A1IE); + t->pending = !!(regs[8] & DS1337_BIT_A1I); + + dev_dbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, enabled=%d, pending=%d\n", + "alarm read", t->time.tm_sec, t->time.tm_min, + t->time.tm_hour, t->time.tm_mday, + t->enabled, t->pending); + + return 0; +} + +static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + unsigned char regs[9]; + u8 control, status; + int ret; + + dev_dbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, enabled=%d, pending=%d\n", + "alarm set", t->time.tm_sec, t->time.tm_min, + t->time.tm_hour, t->time.tm_mday, + t->enabled, t->pending); + + /* read current status of both alarms and the chip */ + ret = regmap_bulk_read(ds1307->regmap, DS1339_REG_ALARM1_SECS, regs, + sizeof(regs)); + if (ret) { + dev_err(dev, "%s error %d\n", "alarm write", ret); + return ret; + } + control = regs[7]; + status = regs[8]; + + dev_dbg(dev, "%s: %4ph, %3ph, %02x %02x\n", "alarm set (old status)", + ®s[0], ®s[4], control, status); + + /* set ALARM1, using 24 hour and day-of-month modes */ + regs[0] = bin2bcd(t->time.tm_sec); + regs[1] = bin2bcd(t->time.tm_min); + regs[2] = bin2bcd(t->time.tm_hour); + regs[3] = bin2bcd(t->time.tm_mday); + + /* set ALARM2 to non-garbage */ + regs[4] = 0; + regs[5] = 0; + regs[6] = 0; + + /* disable alarms */ + regs[7] = control & ~(DS1337_BIT_A1IE | DS1337_BIT_A2IE); + regs[8] = status & ~(DS1337_BIT_A1I | DS1337_BIT_A2I); + + ret = regmap_bulk_write(ds1307->regmap, DS1339_REG_ALARM1_SECS, regs, + sizeof(regs)); + if (ret) { + dev_err(dev, "can't set alarm time\n"); + return ret; + } + + /* optionally enable ALARM1 */ + if (t->enabled) { + dev_dbg(dev, "alarm IRQ armed\n"); + regs[7] |= DS1337_BIT_A1IE; /* only ALARM1 is used */ + regmap_write(ds1307->regmap, DS1337_REG_CONTROL, regs[7]); + } + + return 0; +} + +static int ds1307_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + + return regmap_update_bits(ds1307->regmap, DS1337_REG_CONTROL, + DS1337_BIT_A1IE, + enabled ? DS1337_BIT_A1IE : 0); +} + +static u8 do_trickle_setup_ds1339(struct ds1307 *ds1307, u32 ohms, bool diode) +{ + u8 setup = (diode) ? DS1307_TRICKLE_CHARGER_DIODE : + DS1307_TRICKLE_CHARGER_NO_DIODE; + + setup |= DS13XX_TRICKLE_CHARGER_MAGIC; + + switch (ohms) { + case 250: + setup |= DS1307_TRICKLE_CHARGER_250_OHM; + break; + case 2000: + setup |= DS1307_TRICKLE_CHARGER_2K_OHM; + break; + case 4000: + setup |= DS1307_TRICKLE_CHARGER_4K_OHM; + break; + default: + dev_warn(ds1307->dev, + "Unsupported ohm value %u in dt\n", ohms); + return 0; + } + return setup; +} + +static u8 do_trickle_setup_rx8130(struct ds1307 *ds1307, u32 ohms, bool diode) +{ + /* make sure that the backup battery is enabled */ + u8 setup = RX8130_REG_CONTROL1_INIEN; + if (diode) + setup |= RX8130_REG_CONTROL1_CHGEN; + + return setup; +} + +static irqreturn_t rx8130_irq(int irq, void *dev_id) +{ + struct ds1307 *ds1307 = dev_id; + u8 ctl[3]; + int ret; + + rtc_lock(ds1307->rtc); + + /* Read control registers. */ + ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); + if (ret < 0) + goto out; + if (!(ctl[1] & RX8130_REG_FLAG_AF)) + goto out; + ctl[1] &= ~RX8130_REG_FLAG_AF; + ctl[2] &= ~RX8130_REG_CONTROL0_AIE; + + ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); + if (ret < 0) + goto out; + + rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF); + +out: + rtc_unlock(ds1307->rtc); + + return IRQ_HANDLED; +} + +static int rx8130_read_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + u8 ald[3], ctl[3]; + int ret; + + /* Read alarm registers. */ + ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_ALARM_MIN, ald, + sizeof(ald)); + if (ret < 0) + return ret; + + /* Read control registers. */ + ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); + if (ret < 0) + return ret; + + t->enabled = !!(ctl[2] & RX8130_REG_CONTROL0_AIE); + t->pending = !!(ctl[1] & RX8130_REG_FLAG_AF); + + /* Report alarm 0 time assuming 24-hour and day-of-month modes. */ + t->time.tm_sec = -1; + t->time.tm_min = bcd2bin(ald[0] & 0x7f); + t->time.tm_hour = bcd2bin(ald[1] & 0x7f); + t->time.tm_wday = -1; + t->time.tm_mday = bcd2bin(ald[2] & 0x7f); + t->time.tm_mon = -1; + t->time.tm_year = -1; + t->time.tm_yday = -1; + t->time.tm_isdst = -1; + + dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d enabled=%d\n", + __func__, t->time.tm_sec, t->time.tm_min, t->time.tm_hour, + t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, t->enabled); + + return 0; +} + +static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + u8 ald[3], ctl[3]; + int ret; + + dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d " + "enabled=%d pending=%d\n", __func__, + t->time.tm_sec, t->time.tm_min, t->time.tm_hour, + t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, + t->enabled, t->pending); + + /* Read control registers. */ + ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); + if (ret < 0) + return ret; + + ctl[0] |= RX8130_REG_EXTENSION_WADA; + ctl[1] &= ~RX8130_REG_FLAG_AF; + ctl[2] &= ~RX8130_REG_CONTROL0_AIE; + + ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); + if (ret < 0) + return ret; + + /* Hardware alarm precision is 1 minute! */ + ald[0] = bin2bcd(t->time.tm_min); + ald[1] = bin2bcd(t->time.tm_hour); + ald[2] = bin2bcd(t->time.tm_mday); + + ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_ALARM_MIN, ald, + sizeof(ald)); + if (ret < 0) + return ret; + + if (!t->enabled) + return 0; + + ctl[2] |= RX8130_REG_CONTROL0_AIE; + + return regmap_write(ds1307->regmap, RX8130_REG_CONTROL0, ctl[2]); +} + +static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + int ret, reg; + + ret = regmap_read(ds1307->regmap, RX8130_REG_CONTROL0, ®); + if (ret < 0) + return ret; + + if (enabled) + reg |= RX8130_REG_CONTROL0_AIE; + else + reg &= ~RX8130_REG_CONTROL0_AIE; + + return regmap_write(ds1307->regmap, RX8130_REG_CONTROL0, reg); +} + +static irqreturn_t mcp794xx_irq(int irq, void *dev_id) +{ + struct ds1307 *ds1307 = dev_id; + struct mutex *lock = &ds1307->rtc->ops_lock; + int reg, ret; + + mutex_lock(lock); + + /* Check and clear alarm 0 interrupt flag. */ + ret = regmap_read(ds1307->regmap, MCP794XX_REG_ALARM0_CTRL, ®); + if (ret) + goto out; + if (!(reg & MCP794XX_BIT_ALMX_IF)) + goto out; + reg &= ~MCP794XX_BIT_ALMX_IF; + ret = regmap_write(ds1307->regmap, MCP794XX_REG_ALARM0_CTRL, reg); + if (ret) + goto out; + + /* Disable alarm 0. */ + ret = regmap_update_bits(ds1307->regmap, MCP794XX_REG_CONTROL, + MCP794XX_BIT_ALM0_EN, 0); + if (ret) + goto out; + + rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF); + +out: + mutex_unlock(lock); + + return IRQ_HANDLED; +} + +static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + u8 regs[10]; + int ret; + + /* Read control and alarm 0 registers. */ + ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs, + sizeof(regs)); + if (ret) + return ret; + + t->enabled = !!(regs[0] & MCP794XX_BIT_ALM0_EN); + + /* Report alarm 0 time assuming 24-hour and day-of-month modes. */ + t->time.tm_sec = bcd2bin(regs[3] & 0x7f); + t->time.tm_min = bcd2bin(regs[4] & 0x7f); + t->time.tm_hour = bcd2bin(regs[5] & 0x3f); + t->time.tm_wday = bcd2bin(regs[6] & 0x7) - 1; + t->time.tm_mday = bcd2bin(regs[7] & 0x3f); + t->time.tm_mon = bcd2bin(regs[8] & 0x1f) - 1; + t->time.tm_year = -1; + t->time.tm_yday = -1; + t->time.tm_isdst = -1; + + dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d " + "enabled=%d polarity=%d irq=%d match=%lu\n", __func__, + t->time.tm_sec, t->time.tm_min, t->time.tm_hour, + t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, t->enabled, + !!(regs[6] & MCP794XX_BIT_ALMX_POL), + !!(regs[6] & MCP794XX_BIT_ALMX_IF), + (regs[6] & MCP794XX_MSK_ALMX_MATCH) >> 4); + + return 0; +} + +/* + * We may have a random RTC weekday, therefore calculate alarm weekday based + * on current weekday we read from the RTC timekeeping regs + */ +static int mcp794xx_alm_weekday(struct device *dev, struct rtc_time *tm_alarm) +{ + struct rtc_time tm_now; + int days_now, days_alarm, ret; + + ret = ds1307_get_time(dev, &tm_now); + if (ret) + return ret; + + days_now = div_s64(rtc_tm_to_time64(&tm_now), 24 * 60 * 60); + days_alarm = div_s64(rtc_tm_to_time64(tm_alarm), 24 * 60 * 60); + + return (tm_now.tm_wday + days_alarm - days_now) % 7 + 1; +} + +static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + unsigned char regs[10]; + int wday, ret; + + wday = mcp794xx_alm_weekday(dev, &t->time); + if (wday < 0) + return wday; + + dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d " + "enabled=%d pending=%d\n", __func__, + t->time.tm_sec, t->time.tm_min, t->time.tm_hour, + t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, + t->enabled, t->pending); + + /* Read control and alarm 0 registers. */ + ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs, + sizeof(regs)); + if (ret) + return ret; + + /* Set alarm 0, using 24-hour and day-of-month modes. */ + regs[3] = bin2bcd(t->time.tm_sec); + regs[4] = bin2bcd(t->time.tm_min); + regs[5] = bin2bcd(t->time.tm_hour); + regs[6] = wday; + regs[7] = bin2bcd(t->time.tm_mday); + regs[8] = bin2bcd(t->time.tm_mon + 1); + + /* Clear the alarm 0 interrupt flag. */ + regs[6] &= ~MCP794XX_BIT_ALMX_IF; + /* Set alarm match: second, minute, hour, day, date, month. */ + regs[6] |= MCP794XX_MSK_ALMX_MATCH; + /* Disable interrupt. We will not enable until completely programmed */ + regs[0] &= ~MCP794XX_BIT_ALM0_EN; + + ret = regmap_bulk_write(ds1307->regmap, MCP794XX_REG_CONTROL, regs, + sizeof(regs)); + if (ret) + return ret; + + if (!t->enabled) + return 0; + regs[0] |= MCP794XX_BIT_ALM0_EN; + return regmap_write(ds1307->regmap, MCP794XX_REG_CONTROL, regs[0]); +} + +static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + + return regmap_update_bits(ds1307->regmap, MCP794XX_REG_CONTROL, + MCP794XX_BIT_ALM0_EN, + enabled ? MCP794XX_BIT_ALM0_EN : 0); +} + +static int m41txx_rtc_read_offset(struct device *dev, long *offset) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + unsigned int ctrl_reg; + u8 val; + + regmap_read(ds1307->regmap, M41TXX_REG_CONTROL, &ctrl_reg); + + val = ctrl_reg & M41TXX_M_CALIBRATION; + + /* check if positive */ + if (ctrl_reg & M41TXX_BIT_CALIB_SIGN) + *offset = (val * M41TXX_POS_OFFSET_STEP_PPB); + else + *offset = -(val * M41TXX_NEG_OFFSET_STEP_PPB); + + return 0; +} + +static int m41txx_rtc_set_offset(struct device *dev, long offset) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + unsigned int ctrl_reg; + + if ((offset < M41TXX_MIN_OFFSET) || (offset > M41TXX_MAX_OFFSET)) + return -ERANGE; + + if (offset >= 0) { + ctrl_reg = DIV_ROUND_CLOSEST(offset, + M41TXX_POS_OFFSET_STEP_PPB); + ctrl_reg |= M41TXX_BIT_CALIB_SIGN; + } else { + ctrl_reg = DIV_ROUND_CLOSEST(abs(offset), + M41TXX_NEG_OFFSET_STEP_PPB); + } + + return regmap_update_bits(ds1307->regmap, M41TXX_REG_CONTROL, + M41TXX_M_CALIBRATION | M41TXX_BIT_CALIB_SIGN, + ctrl_reg); +} + +#ifdef CONFIG_WATCHDOG_CORE +static int ds1388_wdt_start(struct watchdog_device *wdt_dev) +{ + struct ds1307 *ds1307 = watchdog_get_drvdata(wdt_dev); + u8 regs[2]; + int ret; + + ret = regmap_update_bits(ds1307->regmap, DS1388_REG_FLAG, + DS1388_BIT_WF, 0); + if (ret) + return ret; + + ret = regmap_update_bits(ds1307->regmap, DS1388_REG_CONTROL, + DS1388_BIT_WDE | DS1388_BIT_RST, 0); + if (ret) + return ret; + + /* + * watchdog timeouts are measured in seconds. So ignore hundredths of + * seconds field. + */ + regs[0] = 0; + regs[1] = bin2bcd(wdt_dev->timeout); + + ret = regmap_bulk_write(ds1307->regmap, DS1388_REG_WDOG_HUN_SECS, regs, + sizeof(regs)); + if (ret) + return ret; + + return regmap_update_bits(ds1307->regmap, DS1388_REG_CONTROL, + DS1388_BIT_WDE | DS1388_BIT_RST, + DS1388_BIT_WDE | DS1388_BIT_RST); +} + +static int ds1388_wdt_stop(struct watchdog_device *wdt_dev) +{ + struct ds1307 *ds1307 = watchdog_get_drvdata(wdt_dev); + + return regmap_update_bits(ds1307->regmap, DS1388_REG_CONTROL, + DS1388_BIT_WDE | DS1388_BIT_RST, 0); +} + +static int ds1388_wdt_ping(struct watchdog_device *wdt_dev) +{ + struct ds1307 *ds1307 = watchdog_get_drvdata(wdt_dev); + u8 regs[2]; + + return regmap_bulk_read(ds1307->regmap, DS1388_REG_WDOG_HUN_SECS, regs, + sizeof(regs)); +} + +static int ds1388_wdt_set_timeout(struct watchdog_device *wdt_dev, + unsigned int val) +{ + struct ds1307 *ds1307 = watchdog_get_drvdata(wdt_dev); + u8 regs[2]; + + wdt_dev->timeout = val; + regs[0] = 0; + regs[1] = bin2bcd(wdt_dev->timeout); + + return regmap_bulk_write(ds1307->regmap, DS1388_REG_WDOG_HUN_SECS, regs, + sizeof(regs)); +} +#endif + +static const struct rtc_class_ops rx8130_rtc_ops = { + .read_time = ds1307_get_time, + .set_time = ds1307_set_time, + .read_alarm = rx8130_read_alarm, + .set_alarm = rx8130_set_alarm, + .alarm_irq_enable = rx8130_alarm_irq_enable, +}; + +static const struct rtc_class_ops mcp794xx_rtc_ops = { + .read_time = ds1307_get_time, + .set_time = ds1307_set_time, + .read_alarm = mcp794xx_read_alarm, + .set_alarm = mcp794xx_set_alarm, + .alarm_irq_enable = mcp794xx_alarm_irq_enable, +}; + +static const struct rtc_class_ops m41txx_rtc_ops = { + .read_time = ds1307_get_time, + .set_time = ds1307_set_time, + .read_alarm = ds1337_read_alarm, + .set_alarm = ds1337_set_alarm, + .alarm_irq_enable = ds1307_alarm_irq_enable, + .read_offset = m41txx_rtc_read_offset, + .set_offset = m41txx_rtc_set_offset, +}; + +static const struct chip_desc chips[last_ds_type] = { + [ds_1307] = { + .nvram_offset = 8, + .nvram_size = 56, + }, + [ds_1308] = { + .nvram_offset = 8, + .nvram_size = 56, + }, + [ds_1337] = { + .alarm = 1, + .century_reg = DS1307_REG_MONTH, + .century_bit = DS1337_BIT_CENTURY, + }, + [ds_1338] = { + .nvram_offset = 8, + .nvram_size = 56, + }, + [ds_1339] = { + .alarm = 1, + .century_reg = DS1307_REG_MONTH, + .century_bit = DS1337_BIT_CENTURY, + .bbsqi_bit = DS1339_BIT_BBSQI, + .trickle_charger_reg = 0x10, + .do_trickle_setup = &do_trickle_setup_ds1339, + .requires_trickle_resistor = true, + .charge_default = true, + }, + [ds_1340] = { + .century_reg = DS1307_REG_HOUR, + .century_enable_bit = DS1340_BIT_CENTURY_EN, + .century_bit = DS1340_BIT_CENTURY, + .do_trickle_setup = &do_trickle_setup_ds1339, + .trickle_charger_reg = 0x08, + .requires_trickle_resistor = true, + .charge_default = true, + }, + [ds_1341] = { + .century_reg = DS1307_REG_MONTH, + .century_bit = DS1337_BIT_CENTURY, + }, + [ds_1388] = { + .offset = 1, + .trickle_charger_reg = 0x0a, + }, + [ds_3231] = { + .alarm = 1, + .century_reg = DS1307_REG_MONTH, + .century_bit = DS1337_BIT_CENTURY, + .bbsqi_bit = DS3231_BIT_BBSQW, + }, + [rx_8130] = { + .alarm = 1, + /* this is battery backed SRAM */ + .nvram_offset = 0x20, + .nvram_size = 4, /* 32bit (4 word x 8 bit) */ + .offset = 0x10, + .irq_handler = rx8130_irq, + .rtc_ops = &rx8130_rtc_ops, + .trickle_charger_reg = RX8130_REG_CONTROL1, + .do_trickle_setup = &do_trickle_setup_rx8130, + }, + [m41t0] = { + .rtc_ops = &m41txx_rtc_ops, + }, + [m41t00] = { + .rtc_ops = &m41txx_rtc_ops, + }, + [m41t11] = { + /* this is battery backed SRAM */ + .nvram_offset = 8, + .nvram_size = 56, + .rtc_ops = &m41txx_rtc_ops, + }, + [mcp794xx] = { + .alarm = 1, + /* this is battery backed SRAM */ + .nvram_offset = 0x20, + .nvram_size = 0x40, + .irq_handler = mcp794xx_irq, + .rtc_ops = &mcp794xx_rtc_ops, + }, +}; + +static const struct i2c_device_id ds1307_id[] = { + { .name = "ds1307", .driver_data = ds_1307 }, + { .name = "ds1308", .driver_data = ds_1308 }, + { .name = "ds1337", .driver_data = ds_1337 }, + { .name = "ds1338", .driver_data = ds_1338 }, + { .name = "ds1339", .driver_data = ds_1339 }, + { .name = "ds1388", .driver_data = ds_1388 }, + { .name = "ds1340", .driver_data = ds_1340 }, + { .name = "ds1341", .driver_data = ds_1341 }, + { .name = "ds3231", .driver_data = ds_3231 }, + { .name = "m41t0", .driver_data = m41t0 }, + { .name = "m41t00", .driver_data = m41t00 }, + { .name = "m41t11", .driver_data = m41t11 }, + { .name = "mcp7940x", .driver_data = mcp794xx }, + { .name = "mcp7941x", .driver_data = mcp794xx }, + { .name = "pt7c4338", .driver_data = ds_1307 }, + { .name = "rx8025", .driver_data = rx_8025 }, + { .name = "isl12057", .driver_data = ds_1337 }, + { .name = "rx8130", .driver_data = rx_8130 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ds1307_id); + +static const struct of_device_id ds1307_of_match[] = { + { + .compatible = "dallas,ds1307", + .data = (void *)ds_1307 + }, + { + .compatible = "dallas,ds1308", + .data = (void *)ds_1308 + }, + { + .compatible = "dallas,ds1337", + .data = (void *)ds_1337 + }, + { + .compatible = "dallas,ds1338", + .data = (void *)ds_1338 + }, + { + .compatible = "dallas,ds1339", + .data = (void *)ds_1339 + }, + { + .compatible = "dallas,ds1388", + .data = (void *)ds_1388 + }, + { + .compatible = "dallas,ds1340", + .data = (void *)ds_1340 + }, + { + .compatible = "dallas,ds1341", + .data = (void *)ds_1341 + }, + { + .compatible = "maxim,ds3231", + .data = (void *)ds_3231 + }, + { + .compatible = "st,m41t0", + .data = (void *)m41t0 + }, + { + .compatible = "st,m41t00", + .data = (void *)m41t00 + }, + { + .compatible = "st,m41t11", + .data = (void *)m41t11 + }, + { + .compatible = "microchip,mcp7940x", + .data = (void *)mcp794xx + }, + { + .compatible = "microchip,mcp7941x", + .data = (void *)mcp794xx + }, + { + .compatible = "pericom,pt7c4338", + .data = (void *)ds_1307 + }, + { + .compatible = "epson,rx8025", + .data = (void *)rx_8025 + }, + { + .compatible = "isil,isl12057", + .data = (void *)ds_1337 + }, + { + .compatible = "epson,rx8130", + .data = (void *)rx_8130 + }, + { } +}; +MODULE_DEVICE_TABLE(of, ds1307_of_match); + +/* + * The ds1337 and ds1339 both have two alarms, but we only use the first + * one (with a "seconds" field). For ds1337 we expect nINTA is our alarm + * signal; ds1339 chips have only one alarm signal. + */ +static irqreturn_t ds1307_irq(int irq, void *dev_id) +{ + struct ds1307 *ds1307 = dev_id; + struct mutex *lock = &ds1307->rtc->ops_lock; + int stat, ret; + + mutex_lock(lock); + ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &stat); + if (ret) + goto out; + + if (stat & DS1337_BIT_A1I) { + stat &= ~DS1337_BIT_A1I; + regmap_write(ds1307->regmap, DS1337_REG_STATUS, stat); + + ret = regmap_update_bits(ds1307->regmap, DS1337_REG_CONTROL, + DS1337_BIT_A1IE, 0); + if (ret) + goto out; + + rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF); + } + +out: + mutex_unlock(lock); + + return IRQ_HANDLED; +} + +/*----------------------------------------------------------------------*/ + +static const struct rtc_class_ops ds13xx_rtc_ops = { + .read_time = ds1307_get_time, + .set_time = ds1307_set_time, + .read_alarm = ds1337_read_alarm, + .set_alarm = ds1337_set_alarm, + .alarm_irq_enable = ds1307_alarm_irq_enable, +}; + +static ssize_t frequency_test_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev->parent); + bool freq_test_en; + int ret; + + ret = kstrtobool(buf, &freq_test_en); + if (ret) { + dev_err(dev, "Failed to store RTC Frequency Test attribute\n"); + return ret; + } + + regmap_update_bits(ds1307->regmap, M41TXX_REG_CONTROL, M41TXX_BIT_FT, + freq_test_en ? M41TXX_BIT_FT : 0); + + return count; +} + +static ssize_t frequency_test_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev->parent); + unsigned int ctrl_reg; + + regmap_read(ds1307->regmap, M41TXX_REG_CONTROL, &ctrl_reg); + + return sysfs_emit(buf, (ctrl_reg & M41TXX_BIT_FT) ? "on\n" : "off\n"); +} + +static DEVICE_ATTR_RW(frequency_test); + +static struct attribute *rtc_freq_test_attrs[] = { + &dev_attr_frequency_test.attr, + NULL, +}; + +static const struct attribute_group rtc_freq_test_attr_group = { + .attrs = rtc_freq_test_attrs, +}; + +static int ds1307_add_frequency_test(struct ds1307 *ds1307) +{ + int err; + + switch (ds1307->type) { + case m41t0: + case m41t00: + case m41t11: + err = rtc_add_group(ds1307->rtc, &rtc_freq_test_attr_group); + if (err) + return err; + break; + default: + break; + } + + return 0; +} + +/*----------------------------------------------------------------------*/ + +static int ds1307_nvram_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct ds1307 *ds1307 = priv; + const struct chip_desc *chip = &chips[ds1307->type]; + + return regmap_bulk_read(ds1307->regmap, chip->nvram_offset + offset, + val, bytes); +} + +static int ds1307_nvram_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct ds1307 *ds1307 = priv; + const struct chip_desc *chip = &chips[ds1307->type]; + + return regmap_bulk_write(ds1307->regmap, chip->nvram_offset + offset, + val, bytes); +} + +/*----------------------------------------------------------------------*/ + +static u8 ds1307_trickle_init(struct ds1307 *ds1307, + const struct chip_desc *chip) +{ + u32 ohms, chargeable; + bool diode = chip->charge_default; + + if (!chip->do_trickle_setup) + return 0; + + if (device_property_read_u32(ds1307->dev, "trickle-resistor-ohms", + &ohms) && chip->requires_trickle_resistor) + return 0; + + /* aux-voltage-chargeable takes precedence over the deprecated + * trickle-diode-disable + */ + if (!device_property_read_u32(ds1307->dev, "aux-voltage-chargeable", + &chargeable)) { + switch (chargeable) { + case 0: + diode = false; + break; + case 1: + diode = true; + break; + default: + dev_warn(ds1307->dev, + "unsupported aux-voltage-chargeable value\n"); + break; + } + } else if (device_property_read_bool(ds1307->dev, + "trickle-diode-disable")) { + diode = false; + } + + return chip->do_trickle_setup(ds1307, ohms, diode); +} + +/*----------------------------------------------------------------------*/ + +#if IS_REACHABLE(CONFIG_HWMON) + +/* + * Temperature sensor support for ds3231 devices. + */ + +#define DS3231_REG_TEMPERATURE 0x11 + +/* + * A user-initiated temperature conversion is not started by this function, + * so the temperature is updated once every 64 seconds. + */ +static int ds3231_hwmon_read_temp(struct device *dev, s32 *mC) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + u8 temp_buf[2]; + s16 temp; + int ret; + + ret = regmap_bulk_read(ds1307->regmap, DS3231_REG_TEMPERATURE, + temp_buf, sizeof(temp_buf)); + if (ret) + return ret; + /* + * Temperature is represented as a 10-bit code with a resolution of + * 0.25 degree celsius and encoded in two's complement format. + */ + temp = (temp_buf[0] << 8) | temp_buf[1]; + temp >>= 6; + *mC = temp * 250; + + return 0; +} + +static ssize_t ds3231_hwmon_show_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + s32 temp; + + ret = ds3231_hwmon_read_temp(dev, &temp); + if (ret) + return ret; + + return sprintf(buf, "%d\n", temp); +} +static SENSOR_DEVICE_ATTR(temp1_input, 0444, ds3231_hwmon_show_temp, + NULL, 0); + +static struct attribute *ds3231_hwmon_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(ds3231_hwmon); + +static void ds1307_hwmon_register(struct ds1307 *ds1307) +{ + struct device *dev; + + if (ds1307->type != ds_3231) + return; + + dev = devm_hwmon_device_register_with_groups(ds1307->dev, ds1307->name, + ds1307, + ds3231_hwmon_groups); + if (IS_ERR(dev)) { + dev_warn(ds1307->dev, "unable to register hwmon device %ld\n", + PTR_ERR(dev)); + } +} + +#else + +static void ds1307_hwmon_register(struct ds1307 *ds1307) +{ +} + +#endif /* IS_REACHABLE(CONFIG_HWMON) */ + +/*----------------------------------------------------------------------*/ + +/* + * Square-wave output support for DS3231 + * Datasheet: https://datasheets.maximintegrated.com/en/ds/DS3231.pdf + */ +#ifdef CONFIG_COMMON_CLK + +enum { + DS3231_CLK_SQW = 0, + DS3231_CLK_32KHZ, +}; + +#define clk_sqw_to_ds1307(clk) \ + container_of(clk, struct ds1307, clks[DS3231_CLK_SQW]) +#define clk_32khz_to_ds1307(clk) \ + container_of(clk, struct ds1307, clks[DS3231_CLK_32KHZ]) + +static int ds3231_clk_sqw_rates[] = { + 1, + 1024, + 4096, + 8192, +}; + +static int ds1337_write_control(struct ds1307 *ds1307, u8 mask, u8 value) +{ + struct mutex *lock = &ds1307->rtc->ops_lock; + int ret; + + mutex_lock(lock); + ret = regmap_update_bits(ds1307->regmap, DS1337_REG_CONTROL, + mask, value); + mutex_unlock(lock); + + return ret; +} + +static unsigned long ds3231_clk_sqw_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + int control, ret; + int rate_sel = 0; + + ret = regmap_read(ds1307->regmap, DS1337_REG_CONTROL, &control); + if (ret) + return ret; + if (control & DS1337_BIT_RS1) + rate_sel += 1; + if (control & DS1337_BIT_RS2) + rate_sel += 2; + + return ds3231_clk_sqw_rates[rate_sel]; +} + +static int ds3231_clk_sqw_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + int i; + + for (i = ARRAY_SIZE(ds3231_clk_sqw_rates) - 1; i >= 0; i--) { + if (ds3231_clk_sqw_rates[i] <= req->rate) { + req->rate = ds3231_clk_sqw_rates[i]; + + return 0; + } + } + + req->rate = ds3231_clk_sqw_rates[ARRAY_SIZE(ds3231_clk_sqw_rates) - 1]; + + return 0; +} + +static int ds3231_clk_sqw_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + int control = 0; + int rate_sel; + + for (rate_sel = 0; rate_sel < ARRAY_SIZE(ds3231_clk_sqw_rates); + rate_sel++) { + if (ds3231_clk_sqw_rates[rate_sel] == rate) + break; + } + + if (rate_sel == ARRAY_SIZE(ds3231_clk_sqw_rates)) + return -EINVAL; + + if (rate_sel & 1) + control |= DS1337_BIT_RS1; + if (rate_sel & 2) + control |= DS1337_BIT_RS2; + + return ds1337_write_control(ds1307, DS1337_BIT_RS1 | DS1337_BIT_RS2, + control); +} + +static int ds3231_clk_sqw_prepare(struct clk_hw *hw) +{ + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + + return ds1337_write_control(ds1307, DS1337_BIT_INTCN, 0); +} + +static void ds3231_clk_sqw_unprepare(struct clk_hw *hw) +{ + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + + ds1337_write_control(ds1307, DS1337_BIT_INTCN, DS1337_BIT_INTCN); +} + +static int ds3231_clk_sqw_is_prepared(struct clk_hw *hw) +{ + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + int control, ret; + + ret = regmap_read(ds1307->regmap, DS1337_REG_CONTROL, &control); + if (ret) + return ret; + + return !(control & DS1337_BIT_INTCN); +} + +static const struct clk_ops ds3231_clk_sqw_ops = { + .prepare = ds3231_clk_sqw_prepare, + .unprepare = ds3231_clk_sqw_unprepare, + .is_prepared = ds3231_clk_sqw_is_prepared, + .recalc_rate = ds3231_clk_sqw_recalc_rate, + .determine_rate = ds3231_clk_sqw_determine_rate, + .set_rate = ds3231_clk_sqw_set_rate, +}; + +static unsigned long ds3231_clk_32khz_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return 32768; +} + +static int ds3231_clk_32khz_control(struct ds1307 *ds1307, bool enable) +{ + struct mutex *lock = &ds1307->rtc->ops_lock; + int ret; + + mutex_lock(lock); + ret = regmap_update_bits(ds1307->regmap, DS1337_REG_STATUS, + DS3231_BIT_EN32KHZ, + enable ? DS3231_BIT_EN32KHZ : 0); + mutex_unlock(lock); + + return ret; +} + +static int ds3231_clk_32khz_prepare(struct clk_hw *hw) +{ + struct ds1307 *ds1307 = clk_32khz_to_ds1307(hw); + + return ds3231_clk_32khz_control(ds1307, true); +} + +static void ds3231_clk_32khz_unprepare(struct clk_hw *hw) +{ + struct ds1307 *ds1307 = clk_32khz_to_ds1307(hw); + + ds3231_clk_32khz_control(ds1307, false); +} + +static int ds3231_clk_32khz_is_prepared(struct clk_hw *hw) +{ + struct ds1307 *ds1307 = clk_32khz_to_ds1307(hw); + int status, ret; + + ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &status); + if (ret) + return ret; + + return !!(status & DS3231_BIT_EN32KHZ); +} + +static const struct clk_ops ds3231_clk_32khz_ops = { + .prepare = ds3231_clk_32khz_prepare, + .unprepare = ds3231_clk_32khz_unprepare, + .is_prepared = ds3231_clk_32khz_is_prepared, + .recalc_rate = ds3231_clk_32khz_recalc_rate, +}; + +static const char *ds3231_clks_names[] = { + [DS3231_CLK_SQW] = "ds3231_clk_sqw", + [DS3231_CLK_32KHZ] = "ds3231_clk_32khz", +}; + +static struct clk_init_data ds3231_clks_init[] = { + [DS3231_CLK_SQW] = { + .ops = &ds3231_clk_sqw_ops, + }, + [DS3231_CLK_32KHZ] = { + .ops = &ds3231_clk_32khz_ops, + }, +}; + +static int ds3231_clks_register(struct ds1307 *ds1307) +{ + struct device_node *node = ds1307->dev->of_node; + struct clk_onecell_data *onecell; + int i; + + onecell = devm_kzalloc(ds1307->dev, sizeof(*onecell), GFP_KERNEL); + if (!onecell) + return -ENOMEM; + + onecell->clk_num = ARRAY_SIZE(ds3231_clks_init); + onecell->clks = devm_kcalloc(ds1307->dev, onecell->clk_num, + sizeof(onecell->clks[0]), GFP_KERNEL); + if (!onecell->clks) + return -ENOMEM; + + /* optional override of the clockname */ + device_property_read_string_array(ds1307->dev, "clock-output-names", + ds3231_clks_names, + ARRAY_SIZE(ds3231_clks_names)); + + for (i = 0; i < ARRAY_SIZE(ds3231_clks_init); i++) { + struct clk_init_data init = ds3231_clks_init[i]; + + /* + * Interrupt signal due to alarm conditions and square-wave + * output share same pin, so don't initialize both. + */ + if (i == DS3231_CLK_SQW && test_bit(RTC_FEATURE_ALARM, ds1307->rtc->features)) + continue; + + init.name = ds3231_clks_names[i]; + ds1307->clks[i].init = &init; + + onecell->clks[i] = devm_clk_register(ds1307->dev, + &ds1307->clks[i]); + if (IS_ERR(onecell->clks[i])) + return PTR_ERR(onecell->clks[i]); + } + + if (node) + of_clk_add_provider(node, of_clk_src_onecell_get, onecell); + + return 0; +} + +/* ds1307 RTC clock output support */ +static unsigned long ds1307_clk_rates[] = { + 1, + 4096, + 8192, + 32768, +}; + +static unsigned long ds1307_clk_sqw_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + int ret; + unsigned int rate_id; + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + + ret = regmap_read(ds1307->regmap, DS1307_REG_CONTROL, &rate_id); + if (ret) + return ret; + + rate_id &= (DS1307_BIT_RS1 | DS1307_BIT_RS0); + + return ds1307_clk_rates[rate_id]; +} + +static int ds1307_clk_sqw_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ds1307_clk_rates); i++) { + if (req->rate <= ds1307_clk_rates[i]) { + req->rate = ds1307_clk_rates[i]; + return 0; + } + } + + /* Default rate 1Hz */ + req->rate = ds1307_clk_rates[0]; + + return 0; +} + +static int ds1307_clk_sqw_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int id, ret; + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + + for (id = 0; id < ARRAY_SIZE(ds1307_clk_rates); id++) { + if (ds1307_clk_rates[id] == rate) + break; + } + + if (id >= ARRAY_SIZE(ds1307_clk_rates)) + return -EINVAL; + + ret = regmap_update_bits(ds1307->regmap, DS1307_REG_CONTROL, + DS1307_BIT_RS0 | DS1307_BIT_RS1, id); + + return ret; +} + +static int ds1307_clk_sqw_prepare(struct clk_hw *hw) +{ + int ret; + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + + ret = regmap_update_bits(ds1307->regmap, DS1307_REG_CONTROL, + DS1307_BIT_SQWE, DS1307_BIT_SQWE); + + return ret; +} + +static void ds1307_clk_sqw_unprepare(struct clk_hw *hw) +{ + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + + regmap_update_bits(ds1307->regmap, DS1307_REG_CONTROL, + DS1307_BIT_SQWE, ~DS1307_BIT_SQWE); +} + +static int ds1307_clk_sqw_is_prepared(struct clk_hw *hw) +{ + int ret; + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + unsigned int status; + + ret = regmap_read(ds1307->regmap, DS1307_REG_CONTROL, &status); + if (ret) + return ret; + + return !!(status & DS1307_BIT_SQWE); +} + +static const struct clk_ops ds1307_clk_sqw_ops = { + .prepare = ds1307_clk_sqw_prepare, + .unprepare = ds1307_clk_sqw_unprepare, + .is_prepared = ds1307_clk_sqw_is_prepared, + .recalc_rate = ds1307_clk_sqw_recalc_rate, + .set_rate = ds1307_clk_sqw_set_rate, + .determine_rate = ds1307_clk_sqw_determine_rate, +}; + +static int rtc_ds1307_clks_register(struct ds1307 *ds1307) +{ + struct device_node *node = ds1307->dev->of_node; + struct clk *clk; + struct clk_init_data init = {0}; + + init.name = "ds1307_clk_sqw"; + init.ops = &ds1307_clk_sqw_ops; + + ds1307->clks[0].init = &init; + + /* Register the clock with CCF */ + clk = devm_clk_register(ds1307->dev, &ds1307->clks[0]); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + if (node) + of_clk_add_provider(node, of_clk_src_simple_get, clk); + + return 0; +} + +static void ds1307_clks_register(struct ds1307 *ds1307) +{ + int ret; + + switch (ds1307->type) { + case ds_3231: + ret = ds3231_clks_register(ds1307); + break; + + case ds_1307: + ret = rtc_ds1307_clks_register(ds1307); + break; + + default: + return; + } + + if (ret) { + dev_warn(ds1307->dev, "unable to register clock device %d\n", + ret); + } + +} + +#else + +static void ds1307_clks_register(struct ds1307 *ds1307) +{ +} + +#endif /* CONFIG_COMMON_CLK */ + +#ifdef CONFIG_WATCHDOG_CORE +static const struct watchdog_info ds1388_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "DS1388 watchdog", +}; + +static const struct watchdog_ops ds1388_wdt_ops = { + .owner = THIS_MODULE, + .start = ds1388_wdt_start, + .stop = ds1388_wdt_stop, + .ping = ds1388_wdt_ping, + .set_timeout = ds1388_wdt_set_timeout, + +}; + +static void ds1307_wdt_register(struct ds1307 *ds1307) +{ + struct watchdog_device *wdt; + int err; + int val; + + if (ds1307->type != ds_1388) + return; + + wdt = devm_kzalloc(ds1307->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return; + + err = regmap_read(ds1307->regmap, DS1388_REG_FLAG, &val); + if (!err && val & DS1388_BIT_WF) + wdt->bootstatus = WDIOF_CARDRESET; + + wdt->info = &ds1388_wdt_info; + wdt->ops = &ds1388_wdt_ops; + wdt->timeout = 99; + wdt->max_timeout = 99; + wdt->min_timeout = 1; + + watchdog_init_timeout(wdt, 0, ds1307->dev); + watchdog_set_drvdata(wdt, ds1307); + devm_watchdog_register_device(ds1307->dev, wdt); +} +#else +static void ds1307_wdt_register(struct ds1307 *ds1307) +{ +} +#endif /* CONFIG_WATCHDOG_CORE */ + +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int ds1307_probe(struct i2c_client *client) +{ + const struct i2c_device_id *id = i2c_client_get_device_id(client); + struct ds1307 *ds1307; + const void *match; + int err = -ENODEV; + int tmp; + const struct chip_desc *chip; + bool want_irq; + bool ds1307_can_wakeup_device = false; + unsigned char regs[8]; + struct ds1307_platform_data *pdata = dev_get_platdata(&client->dev); + u8 trickle_charger_setup = 0; + + ds1307 = devm_kzalloc(&client->dev, sizeof(struct ds1307), GFP_KERNEL); + if (!ds1307) + return -ENOMEM; + + dev_set_drvdata(&client->dev, ds1307); + ds1307->dev = &client->dev; + ds1307->name = client->name; + + ds1307->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(ds1307->regmap)) { + dev_err(ds1307->dev, "regmap allocation failed\n"); + return PTR_ERR(ds1307->regmap); + } + + i2c_set_clientdata(client, ds1307); + + match = device_get_match_data(&client->dev); + if (match) { + ds1307->type = (uintptr_t)match; + chip = &chips[ds1307->type]; + } else if (id) { + chip = &chips[id->driver_data]; + ds1307->type = id->driver_data; + } else { + return -ENODEV; + } + + want_irq = client->irq > 0 && chip->alarm; + + if (!pdata) + trickle_charger_setup = ds1307_trickle_init(ds1307, chip); + else if (pdata->trickle_charger_setup) + trickle_charger_setup = pdata->trickle_charger_setup; + + if (trickle_charger_setup && chip->trickle_charger_reg) { + dev_dbg(ds1307->dev, + "writing trickle charger info 0x%x to 0x%x\n", + trickle_charger_setup, chip->trickle_charger_reg); + regmap_write(ds1307->regmap, chip->trickle_charger_reg, + trickle_charger_setup); + } + +/* + * For devices with no IRQ directly connected to the SoC, the RTC chip + * can be forced as a wakeup source by stating that explicitly in + * the device's .dts file using the "wakeup-source" boolean property. + * If the "wakeup-source" property is set, don't request an IRQ. + * This will guarantee the 'wakealarm' sysfs entry is available on the device, + * if supported by the RTC. + */ + if (chip->alarm && device_property_read_bool(&client->dev, "wakeup-source")) + ds1307_can_wakeup_device = true; + + switch (ds1307->type) { + case ds_1337: + case ds_1339: + case ds_1341: + case ds_3231: + /* get registers that the "rtc" read below won't read... */ + err = regmap_bulk_read(ds1307->regmap, DS1337_REG_CONTROL, + regs, 2); + if (err) { + dev_dbg(ds1307->dev, "read error %d\n", err); + goto exit; + } + + /* oscillator off? turn it on, so clock can tick. */ + if (regs[0] & DS1337_BIT_nEOSC) + regs[0] &= ~DS1337_BIT_nEOSC; + + /* + * Using IRQ or defined as wakeup-source? + * Disable the square wave and both alarms. + * For some variants, be sure alarms can trigger when we're + * running on Vbackup (BBSQI/BBSQW) + */ + if (want_irq || ds1307_can_wakeup_device) + regs[0] |= DS1337_BIT_INTCN | chip->bbsqi_bit; + + regmap_write(ds1307->regmap, DS1337_REG_CONTROL, + regs[0]); + + /* oscillator fault? warn */ + if (regs[1] & DS1337_BIT_OSF) { + dev_warn(ds1307->dev, "SET TIME!\n"); + } + break; + + case rx_8025: + err = regmap_bulk_read(ds1307->regmap, + RX8025_REG_CTRL1 << 4 | 0x08, regs, 2); + if (err) { + dev_dbg(ds1307->dev, "read error %d\n", err); + goto exit; + } + + /* oscillator off? turn it on, so clock can tick. */ + if (!(regs[1] & RX8025_BIT_XST)) { + regs[1] |= RX8025_BIT_XST; + regmap_write(ds1307->regmap, + RX8025_REG_CTRL2 << 4 | 0x08, + regs[1]); + dev_warn(ds1307->dev, + "oscillator stop detected - SET TIME!\n"); + } + + if (regs[1] & RX8025_BIT_PON) { + regs[1] &= ~RX8025_BIT_PON; + regmap_write(ds1307->regmap, + RX8025_REG_CTRL2 << 4 | 0x08, + regs[1]); + dev_warn(ds1307->dev, "power-on detected\n"); + } + + if (regs[1] & RX8025_BIT_VDET) { + regs[1] &= ~RX8025_BIT_VDET; + regmap_write(ds1307->regmap, + RX8025_REG_CTRL2 << 4 | 0x08, + regs[1]); + dev_warn(ds1307->dev, "voltage drop detected\n"); + } + + /* make sure we are running in 24hour mode */ + if (!(regs[0] & RX8025_BIT_2412)) { + u8 hour; + + /* switch to 24 hour mode */ + regmap_write(ds1307->regmap, + RX8025_REG_CTRL1 << 4 | 0x08, + regs[0] | RX8025_BIT_2412); + + err = regmap_bulk_read(ds1307->regmap, + RX8025_REG_CTRL1 << 4 | 0x08, + regs, 2); + if (err) { + dev_dbg(ds1307->dev, "read error %d\n", err); + goto exit; + } + + /* correct hour */ + hour = bcd2bin(regs[DS1307_REG_HOUR]); + if (hour == 12) + hour = 0; + if (regs[DS1307_REG_HOUR] & DS1307_BIT_PM) + hour += 12; + + regmap_write(ds1307->regmap, + DS1307_REG_HOUR << 4 | 0x08, hour); + } + break; + case ds_1388: + err = regmap_read(ds1307->regmap, DS1388_REG_CONTROL, &tmp); + if (err) { + dev_dbg(ds1307->dev, "read error %d\n", err); + goto exit; + } + + /* oscillator off? turn it on, so clock can tick. */ + if (tmp & DS1388_BIT_nEOSC) { + tmp &= ~DS1388_BIT_nEOSC; + regmap_write(ds1307->regmap, DS1388_REG_CONTROL, tmp); + } + break; + default: + break; + } + + /* read RTC registers */ + err = regmap_bulk_read(ds1307->regmap, chip->offset, regs, + sizeof(regs)); + if (err) { + dev_dbg(ds1307->dev, "read error %d\n", err); + goto exit; + } + + if (ds1307->type == mcp794xx && + !(regs[DS1307_REG_WDAY] & MCP794XX_BIT_VBATEN)) { + regmap_write(ds1307->regmap, DS1307_REG_WDAY, + regs[DS1307_REG_WDAY] | + MCP794XX_BIT_VBATEN); + } + + tmp = regs[DS1307_REG_HOUR]; + switch (ds1307->type) { + case ds_1340: + case m41t0: + case m41t00: + case m41t11: + /* + * NOTE: ignores century bits; fix before deploying + * systems that will run through year 2100. + */ + break; + case rx_8025: + break; + default: + if (!(tmp & DS1307_BIT_12HR)) + break; + + /* + * Be sure we're in 24 hour mode. Multi-master systems + * take note... + */ + tmp = bcd2bin(tmp & 0x1f); + if (tmp == 12) + tmp = 0; + if (regs[DS1307_REG_HOUR] & DS1307_BIT_PM) + tmp += 12; + regmap_write(ds1307->regmap, chip->offset + DS1307_REG_HOUR, + bin2bcd(tmp)); + } + + ds1307->rtc = devm_rtc_allocate_device(ds1307->dev); + if (IS_ERR(ds1307->rtc)) + return PTR_ERR(ds1307->rtc); + + if (want_irq || ds1307_can_wakeup_device) + device_set_wakeup_capable(ds1307->dev, true); + else + clear_bit(RTC_FEATURE_ALARM, ds1307->rtc->features); + + if (ds1307_can_wakeup_device && !want_irq) { + dev_info(ds1307->dev, + "'wakeup-source' is set, request for an IRQ is disabled!\n"); + /* We cannot support UIE mode if we do not have an IRQ line */ + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, ds1307->rtc->features); + } + + if (want_irq) { + err = devm_request_threaded_irq(ds1307->dev, client->irq, NULL, + chip->irq_handler ?: ds1307_irq, + IRQF_SHARED | IRQF_ONESHOT, + ds1307->name, ds1307); + if (err) { + client->irq = 0; + device_set_wakeup_capable(ds1307->dev, false); + clear_bit(RTC_FEATURE_ALARM, ds1307->rtc->features); + dev_err(ds1307->dev, "unable to request IRQ!\n"); + } else { + dev_dbg(ds1307->dev, "got IRQ %d\n", client->irq); + } + } + + ds1307->rtc->ops = chip->rtc_ops ?: &ds13xx_rtc_ops; + err = ds1307_add_frequency_test(ds1307); + if (err) + return err; + + err = devm_rtc_register_device(ds1307->rtc); + if (err) + return err; + + if (chip->nvram_size) { + struct nvmem_config nvmem_cfg = { + .name = "ds1307_nvram", + .word_size = 1, + .stride = 1, + .size = chip->nvram_size, + .reg_read = ds1307_nvram_read, + .reg_write = ds1307_nvram_write, + .priv = ds1307, + }; + + devm_rtc_nvmem_register(ds1307->rtc, &nvmem_cfg); + } + + ds1307_hwmon_register(ds1307); + ds1307_clks_register(ds1307); + ds1307_wdt_register(ds1307); + + return 0; + +exit: + return err; +} + +static struct i2c_driver ds1307_driver = { + .driver = { + .name = "rtc-ds1307", + .of_match_table = ds1307_of_match, + }, + .probe = ds1307_probe, + .id_table = ds1307_id, +}; + +module_i2c_driver(ds1307_driver); + +MODULE_DESCRIPTION("RTC driver for DS1307 and similar chips"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-ds1343.c b/drivers/rtc/rtc-ds1343.c new file mode 100644 index 000000000..54f6af788 --- /dev/null +++ b/drivers/rtc/rtc-ds1343.c @@ -0,0 +1,477 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* rtc-ds1343.c + * + * Driver for Dallas Semiconductor DS1343 Low Current, SPI Compatible + * Real Time Clock + * + * Author : Raghavendra Chandra Ganiga + * Ankur Srivastava : DS1343 Nvram Support + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DALLAS_MAXIM_DS1343 0 +#define DALLAS_MAXIM_DS1344 1 + +/* RTC DS1343 Registers */ +#define DS1343_SECONDS_REG 0x00 +#define DS1343_MINUTES_REG 0x01 +#define DS1343_HOURS_REG 0x02 +#define DS1343_DAY_REG 0x03 +#define DS1343_DATE_REG 0x04 +#define DS1343_MONTH_REG 0x05 +#define DS1343_YEAR_REG 0x06 +#define DS1343_ALM0_SEC_REG 0x07 +#define DS1343_ALM0_MIN_REG 0x08 +#define DS1343_ALM0_HOUR_REG 0x09 +#define DS1343_ALM0_DAY_REG 0x0A +#define DS1343_ALM1_SEC_REG 0x0B +#define DS1343_ALM1_MIN_REG 0x0C +#define DS1343_ALM1_HOUR_REG 0x0D +#define DS1343_ALM1_DAY_REG 0x0E +#define DS1343_CONTROL_REG 0x0F +#define DS1343_STATUS_REG 0x10 +#define DS1343_TRICKLE_REG 0x11 +#define DS1343_NVRAM 0x20 + +#define DS1343_NVRAM_LEN 96 + +/* DS1343 Control Registers bits */ +#define DS1343_EOSC 0x80 +#define DS1343_DOSF 0x20 +#define DS1343_EGFIL 0x10 +#define DS1343_SQW 0x08 +#define DS1343_INTCN 0x04 +#define DS1343_A1IE 0x02 +#define DS1343_A0IE 0x01 + +/* DS1343 Status Registers bits */ +#define DS1343_OSF 0x80 +#define DS1343_IRQF1 0x02 +#define DS1343_IRQF0 0x01 + +/* DS1343 Trickle Charger Registers bits */ +#define DS1343_TRICKLE_MAGIC 0xa0 +#define DS1343_TRICKLE_DS1 0x08 +#define DS1343_TRICKLE_1K 0x01 +#define DS1343_TRICKLE_2K 0x02 +#define DS1343_TRICKLE_4K 0x03 + +static const struct spi_device_id ds1343_id[] = { + { "ds1343", DALLAS_MAXIM_DS1343 }, + { "ds1344", DALLAS_MAXIM_DS1344 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ds1343_id); + +struct ds1343_priv { + struct rtc_device *rtc; + struct regmap *map; + int irq; +}; + +static ssize_t ds1343_show_glitchfilter(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ds1343_priv *priv = dev_get_drvdata(dev->parent); + int glitch_filt_status, data; + int res; + + res = regmap_read(priv->map, DS1343_CONTROL_REG, &data); + if (res) + return res; + + glitch_filt_status = !!(data & DS1343_EGFIL); + + if (glitch_filt_status) + return sprintf(buf, "enabled\n"); + else + return sprintf(buf, "disabled\n"); +} + +static ssize_t ds1343_store_glitchfilter(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ds1343_priv *priv = dev_get_drvdata(dev->parent); + int data = 0; + int res; + + if (strncmp(buf, "enabled", 7) == 0) + data = DS1343_EGFIL; + else if (strncmp(buf, "disabled", 8)) + return -EINVAL; + + res = regmap_update_bits(priv->map, DS1343_CONTROL_REG, + DS1343_EGFIL, data); + if (res) + return res; + + return count; +} + +static DEVICE_ATTR(glitch_filter, 0644, ds1343_show_glitchfilter, + ds1343_store_glitchfilter); + +static int ds1343_nvram_write(void *priv, unsigned int off, void *val, + size_t bytes) +{ + struct ds1343_priv *ds1343 = priv; + + return regmap_bulk_write(ds1343->map, DS1343_NVRAM + off, val, bytes); +} + +static int ds1343_nvram_read(void *priv, unsigned int off, void *val, + size_t bytes) +{ + struct ds1343_priv *ds1343 = priv; + + return regmap_bulk_read(ds1343->map, DS1343_NVRAM + off, val, bytes); +} + +static ssize_t ds1343_show_tricklecharger(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ds1343_priv *priv = dev_get_drvdata(dev->parent); + int res, data; + char *diodes = "disabled", *resistors = " "; + + res = regmap_read(priv->map, DS1343_TRICKLE_REG, &data); + if (res) + return res; + + if ((data & 0xf0) == DS1343_TRICKLE_MAGIC) { + switch (data & 0x0c) { + case DS1343_TRICKLE_DS1: + diodes = "one diode,"; + break; + + default: + diodes = "no diode,"; + break; + } + + switch (data & 0x03) { + case DS1343_TRICKLE_1K: + resistors = "1k Ohm"; + break; + + case DS1343_TRICKLE_2K: + resistors = "2k Ohm"; + break; + + case DS1343_TRICKLE_4K: + resistors = "4k Ohm"; + break; + + default: + diodes = "disabled"; + break; + } + } + + return sprintf(buf, "%s %s\n", diodes, resistors); +} + +static DEVICE_ATTR(trickle_charger, 0444, ds1343_show_tricklecharger, NULL); + +static struct attribute *ds1343_attrs[] = { + &dev_attr_glitch_filter.attr, + &dev_attr_trickle_charger.attr, + NULL +}; + +static const struct attribute_group ds1343_attr_group = { + .attrs = ds1343_attrs, +}; + +static int ds1343_read_time(struct device *dev, struct rtc_time *dt) +{ + struct ds1343_priv *priv = dev_get_drvdata(dev); + unsigned char buf[7]; + int res; + + res = regmap_bulk_read(priv->map, DS1343_SECONDS_REG, buf, 7); + if (res) + return res; + + dt->tm_sec = bcd2bin(buf[0]); + dt->tm_min = bcd2bin(buf[1]); + dt->tm_hour = bcd2bin(buf[2] & 0x3F); + dt->tm_wday = bcd2bin(buf[3]) - 1; + dt->tm_mday = bcd2bin(buf[4]); + dt->tm_mon = bcd2bin(buf[5] & 0x1F) - 1; + dt->tm_year = bcd2bin(buf[6]) + 100; /* year offset from 1900 */ + + return 0; +} + +static int ds1343_set_time(struct device *dev, struct rtc_time *dt) +{ + struct ds1343_priv *priv = dev_get_drvdata(dev); + u8 buf[7]; + + buf[0] = bin2bcd(dt->tm_sec); + buf[1] = bin2bcd(dt->tm_min); + buf[2] = bin2bcd(dt->tm_hour) & 0x3F; + buf[3] = bin2bcd(dt->tm_wday + 1); + buf[4] = bin2bcd(dt->tm_mday); + buf[5] = bin2bcd(dt->tm_mon + 1); + buf[6] = bin2bcd(dt->tm_year - 100); + + return regmap_bulk_write(priv->map, DS1343_SECONDS_REG, + buf, sizeof(buf)); +} + +static int ds1343_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct ds1343_priv *priv = dev_get_drvdata(dev); + unsigned char buf[4]; + unsigned int val; + int res; + + if (priv->irq <= 0) + return -EINVAL; + + res = regmap_read(priv->map, DS1343_STATUS_REG, &val); + if (res) + return res; + + alarm->pending = !!(val & DS1343_IRQF0); + + res = regmap_read(priv->map, DS1343_CONTROL_REG, &val); + if (res) + return res; + alarm->enabled = !!(val & DS1343_A0IE); + + res = regmap_bulk_read(priv->map, DS1343_ALM0_SEC_REG, buf, 4); + if (res) + return res; + + alarm->time.tm_sec = bcd2bin(buf[0]) & 0x7f; + alarm->time.tm_min = bcd2bin(buf[1]) & 0x7f; + alarm->time.tm_hour = bcd2bin(buf[2]) & 0x3f; + alarm->time.tm_mday = bcd2bin(buf[3]) & 0x3f; + + return 0; +} + +static int ds1343_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct ds1343_priv *priv = dev_get_drvdata(dev); + unsigned char buf[4]; + int res = 0; + + if (priv->irq <= 0) + return -EINVAL; + + res = regmap_update_bits(priv->map, DS1343_CONTROL_REG, DS1343_A0IE, 0); + if (res) + return res; + + buf[0] = bin2bcd(alarm->time.tm_sec); + buf[1] = bin2bcd(alarm->time.tm_min); + buf[2] = bin2bcd(alarm->time.tm_hour); + buf[3] = bin2bcd(alarm->time.tm_mday); + + res = regmap_bulk_write(priv->map, DS1343_ALM0_SEC_REG, buf, 4); + if (res) + return res; + + if (alarm->enabled) + res = regmap_update_bits(priv->map, DS1343_CONTROL_REG, + DS1343_A0IE, DS1343_A0IE); + + return res; +} + +static int ds1343_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct ds1343_priv *priv = dev_get_drvdata(dev); + + if (priv->irq <= 0) + return -EINVAL; + + return regmap_update_bits(priv->map, DS1343_CONTROL_REG, + DS1343_A0IE, enabled ? DS1343_A0IE : 0); +} + +static irqreturn_t ds1343_thread(int irq, void *dev_id) +{ + struct ds1343_priv *priv = dev_id; + unsigned int stat; + int res = 0; + + rtc_lock(priv->rtc); + + res = regmap_read(priv->map, DS1343_STATUS_REG, &stat); + if (res) + goto out; + + if (stat & DS1343_IRQF0) { + stat &= ~DS1343_IRQF0; + regmap_write(priv->map, DS1343_STATUS_REG, stat); + + rtc_update_irq(priv->rtc, 1, RTC_AF | RTC_IRQF); + + regmap_update_bits(priv->map, DS1343_CONTROL_REG, + DS1343_A0IE, 0); + } + +out: + rtc_unlock(priv->rtc); + return IRQ_HANDLED; +} + +static const struct rtc_class_ops ds1343_rtc_ops = { + .read_time = ds1343_read_time, + .set_time = ds1343_set_time, + .read_alarm = ds1343_read_alarm, + .set_alarm = ds1343_set_alarm, + .alarm_irq_enable = ds1343_alarm_irq_enable, +}; + +static int ds1343_probe(struct spi_device *spi) +{ + struct ds1343_priv *priv; + struct regmap_config config = { .reg_bits = 8, .val_bits = 8, + .write_flag_mask = 0x80, }; + unsigned int data; + int res; + struct nvmem_config nvmem_cfg = { + .name = "ds1343-", + .word_size = 1, + .stride = 1, + .size = DS1343_NVRAM_LEN, + .reg_read = ds1343_nvram_read, + .reg_write = ds1343_nvram_write, + }; + + priv = devm_kzalloc(&spi->dev, sizeof(struct ds1343_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* RTC DS1347 works in spi mode 3 and + * its chip select is active high. Active high should be defined as + * "inverse polarity" as GPIO-based chip selects can be logically + * active high but inverted by the GPIO library. + */ + spi->mode |= SPI_MODE_3; + spi->mode ^= SPI_CS_HIGH; + spi->bits_per_word = 8; + res = spi_setup(spi); + if (res) + return res; + + spi_set_drvdata(spi, priv); + + priv->map = devm_regmap_init_spi(spi, &config); + + if (IS_ERR(priv->map)) { + dev_err(&spi->dev, "spi regmap init failed for rtc ds1343\n"); + return PTR_ERR(priv->map); + } + + res = regmap_read(priv->map, DS1343_SECONDS_REG, &data); + if (res) + return res; + + regmap_read(priv->map, DS1343_CONTROL_REG, &data); + data |= DS1343_INTCN; + data &= ~(DS1343_EOSC | DS1343_A1IE | DS1343_A0IE); + regmap_write(priv->map, DS1343_CONTROL_REG, data); + + regmap_read(priv->map, DS1343_STATUS_REG, &data); + data &= ~(DS1343_OSF | DS1343_IRQF1 | DS1343_IRQF0); + regmap_write(priv->map, DS1343_STATUS_REG, data); + + priv->rtc = devm_rtc_allocate_device(&spi->dev); + if (IS_ERR(priv->rtc)) + return PTR_ERR(priv->rtc); + + priv->rtc->ops = &ds1343_rtc_ops; + priv->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + priv->rtc->range_max = RTC_TIMESTAMP_END_2099; + + res = rtc_add_group(priv->rtc, &ds1343_attr_group); + if (res) + dev_err(&spi->dev, + "unable to create sysfs entries for rtc ds1343\n"); + + res = devm_rtc_register_device(priv->rtc); + if (res) + return res; + + nvmem_cfg.priv = priv; + devm_rtc_nvmem_register(priv->rtc, &nvmem_cfg); + + priv->irq = spi->irq; + + if (priv->irq >= 0) { + res = devm_request_threaded_irq(&spi->dev, spi->irq, NULL, + ds1343_thread, IRQF_ONESHOT, + "ds1343", priv); + if (res) { + priv->irq = -1; + dev_err(&spi->dev, + "unable to request irq for rtc ds1343\n"); + } else { + device_init_wakeup(&spi->dev, true); + devm_pm_set_wake_irq(&spi->dev, spi->irq); + } + } + + return 0; +} + +#ifdef CONFIG_PM_SLEEP + +static int ds1343_suspend(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + + if (spi->irq >= 0 && device_may_wakeup(dev)) + enable_irq_wake(spi->irq); + + return 0; +} + +static int ds1343_resume(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + + if (spi->irq >= 0 && device_may_wakeup(dev)) + disable_irq_wake(spi->irq); + + return 0; +} + +#endif + +static SIMPLE_DEV_PM_OPS(ds1343_pm, ds1343_suspend, ds1343_resume); + +static struct spi_driver ds1343_driver = { + .driver = { + .name = "ds1343", + .pm = &ds1343_pm, + }, + .probe = ds1343_probe, + .id_table = ds1343_id, +}; + +module_spi_driver(ds1343_driver); + +MODULE_DESCRIPTION("DS1343 RTC SPI Driver"); +MODULE_AUTHOR("Raghavendra Chandra Ganiga ," + "Ankur Srivastava "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-ds1347.c b/drivers/rtc/rtc-ds1347.c new file mode 100644 index 000000000..a40c1a52d --- /dev/null +++ b/drivers/rtc/rtc-ds1347.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* rtc-ds1347.c + * + * Driver for Dallas Semiconductor DS1347 Low Current, SPI Compatible + * Real Time Clock + * + * Author : Raghavendra Chandra Ganiga + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Registers in ds1347 rtc */ + +#define DS1347_SECONDS_REG 0x01 +#define DS1347_MINUTES_REG 0x03 +#define DS1347_HOURS_REG 0x05 +#define DS1347_DATE_REG 0x07 +#define DS1347_MONTH_REG 0x09 +#define DS1347_DAY_REG 0x0B +#define DS1347_YEAR_REG 0x0D +#define DS1347_CONTROL_REG 0x0F +#define DS1347_CENTURY_REG 0x13 +#define DS1347_STATUS_REG 0x17 +#define DS1347_CLOCK_BURST 0x3F + +#define DS1347_WP_BIT BIT(7) + +#define DS1347_NEOSC_BIT BIT(7) +#define DS1347_OSF_BIT BIT(2) + +static const struct regmap_range ds1347_ranges[] = { + { + .range_min = DS1347_SECONDS_REG, + .range_max = DS1347_STATUS_REG, + }, +}; + +static const struct regmap_access_table ds1347_access_table = { + .yes_ranges = ds1347_ranges, + .n_yes_ranges = ARRAY_SIZE(ds1347_ranges), +}; + +static int ds1347_read_time(struct device *dev, struct rtc_time *dt) +{ + struct regmap *map = dev_get_drvdata(dev); + unsigned int status, century, secs; + unsigned char buf[8]; + int err; + + err = regmap_read(map, DS1347_STATUS_REG, &status); + if (err) + return err; + + if (status & DS1347_OSF_BIT) + return -EINVAL; + + do { + err = regmap_bulk_read(map, DS1347_CLOCK_BURST, buf, 8); + if (err) + return err; + + err = regmap_read(map, DS1347_CENTURY_REG, ¢ury); + if (err) + return err; + + err = regmap_read(map, DS1347_SECONDS_REG, &secs); + if (err) + return err; + } while (buf[0] != secs); + + dt->tm_sec = bcd2bin(buf[0]); + dt->tm_min = bcd2bin(buf[1] & 0x7f); + dt->tm_hour = bcd2bin(buf[2] & 0x3F); + dt->tm_mday = bcd2bin(buf[3]); + dt->tm_mon = bcd2bin(buf[4]) - 1; + dt->tm_wday = bcd2bin(buf[5]) - 1; + dt->tm_year = (bcd2bin(century) * 100) + bcd2bin(buf[6]) - 1900; + + return 0; +} + +static int ds1347_set_time(struct device *dev, struct rtc_time *dt) +{ + struct regmap *map = dev_get_drvdata(dev); + unsigned int century; + unsigned char buf[8]; + int err; + + err = regmap_update_bits(map, DS1347_STATUS_REG, + DS1347_NEOSC_BIT, DS1347_NEOSC_BIT); + if (err) + return err; + + buf[0] = bin2bcd(dt->tm_sec); + buf[1] = bin2bcd(dt->tm_min); + buf[2] = (bin2bcd(dt->tm_hour) & 0x3F); + buf[3] = bin2bcd(dt->tm_mday); + buf[4] = bin2bcd(dt->tm_mon + 1); + buf[5] = bin2bcd(dt->tm_wday + 1); + buf[6] = bin2bcd(dt->tm_year % 100); + buf[7] = bin2bcd(0x00); + + err = regmap_bulk_write(map, DS1347_CLOCK_BURST, buf, 8); + if (err) + return err; + + century = (dt->tm_year / 100) + 19; + err = regmap_write(map, DS1347_CENTURY_REG, bin2bcd(century)); + if (err) + return err; + + return regmap_update_bits(map, DS1347_STATUS_REG, + DS1347_NEOSC_BIT | DS1347_OSF_BIT, 0); +} + +static const struct rtc_class_ops ds1347_rtc_ops = { + .read_time = ds1347_read_time, + .set_time = ds1347_set_time, +}; + +static int ds1347_probe(struct spi_device *spi) +{ + struct rtc_device *rtc; + struct regmap_config config; + struct regmap *map; + int err; + + memset(&config, 0, sizeof(config)); + config.reg_bits = 8; + config.val_bits = 8; + config.read_flag_mask = 0x80; + config.max_register = 0x3F; + config.wr_table = &ds1347_access_table; + + /* spi setup with ds1347 in mode 3 and bits per word as 8 */ + spi->mode = SPI_MODE_3; + spi->bits_per_word = 8; + spi_setup(spi); + + map = devm_regmap_init_spi(spi, &config); + + if (IS_ERR(map)) { + dev_err(&spi->dev, "ds1347 regmap init spi failed\n"); + return PTR_ERR(map); + } + + spi_set_drvdata(spi, map); + + /* Disable the write protect of rtc */ + err = regmap_update_bits(map, DS1347_CONTROL_REG, DS1347_WP_BIT, 0); + if (err) + return err; + + rtc = devm_rtc_allocate_device(&spi->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + rtc->ops = &ds1347_rtc_ops; + rtc->range_min = RTC_TIMESTAMP_BEGIN_0000; + rtc->range_max = RTC_TIMESTAMP_END_9999; + + return devm_rtc_register_device(rtc); +} + +static struct spi_driver ds1347_driver = { + .driver = { + .name = "ds1347", + }, + .probe = ds1347_probe, +}; + +module_spi_driver(ds1347_driver); + +MODULE_DESCRIPTION("DS1347 SPI RTC DRIVER"); +MODULE_AUTHOR("Raghavendra C Ganiga "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c new file mode 100644 index 000000000..8c247215d --- /dev/null +++ b/drivers/rtc/rtc-ds1374.c @@ -0,0 +1,584 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * RTC client/driver for the Maxim/Dallas DS1374 Real-Time Clock over I2C + * + * Based on code by Randy Vinson , + * which was based on the m41t00.c by Mark Greer . + * + * Copyright (C) 2014 Rose Technology + * Copyright (C) 2006-2007 Freescale Semiconductor + * Copyright (c) 2005 MontaVista Software, Inc. + */ +/* + * It would be more efficient to use i2c msgs/i2c_transfer directly but, as + * recommended in .../Documentation/i2c/writing-clients.rst section + * "Sending and receiving", using SMBus level communication is preferred. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_RTC_DRV_DS1374_WDT +#include +#include +#include +#include +#include +#endif + +#define DS1374_REG_TOD0 0x00 /* Time of Day */ +#define DS1374_REG_TOD1 0x01 +#define DS1374_REG_TOD2 0x02 +#define DS1374_REG_TOD3 0x03 +#define DS1374_REG_WDALM0 0x04 /* Watchdog/Alarm */ +#define DS1374_REG_WDALM1 0x05 +#define DS1374_REG_WDALM2 0x06 +#define DS1374_REG_CR 0x07 /* Control */ +#define DS1374_REG_CR_AIE 0x01 /* Alarm Int. Enable */ +#define DS1374_REG_CR_WDSTR 0x08 /* 1=INT, 0=RST */ +#define DS1374_REG_CR_WDALM 0x20 /* 1=Watchdog, 0=Alarm */ +#define DS1374_REG_CR_WACE 0x40 /* WD/Alarm counter enable */ +#define DS1374_REG_SR 0x08 /* Status */ +#define DS1374_REG_SR_OSF 0x80 /* Oscillator Stop Flag */ +#define DS1374_REG_SR_AF 0x01 /* Alarm Flag */ +#define DS1374_REG_TCR 0x09 /* Trickle Charge */ + +static const struct i2c_device_id ds1374_id[] = { + { .name = "ds1374" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ds1374_id); + +#ifdef CONFIG_OF +static const struct of_device_id ds1374_of_match[] = { + { .compatible = "dallas,ds1374" }, + { } +}; +MODULE_DEVICE_TABLE(of, ds1374_of_match); +#endif + +struct ds1374 { + struct i2c_client *client; + struct rtc_device *rtc; + struct work_struct work; +#ifdef CONFIG_RTC_DRV_DS1374_WDT + struct watchdog_device wdt; +#endif + /* The mutex protects alarm operations, and prevents a race + * between the enable_irq() in the workqueue and the free_irq() + * in the remove function. + */ + struct mutex mutex; + int exiting; +}; + +static struct i2c_driver ds1374_driver; + +static int ds1374_read_rtc(struct i2c_client *client, u32 *time, + int reg, int nbytes) +{ + u8 buf[4]; + int ret; + int i; + + if (WARN_ON(nbytes > 4)) + return -EINVAL; + + ret = i2c_smbus_read_i2c_block_data(client, reg, nbytes, buf); + + if (ret < 0) + return ret; + if (ret < nbytes) + return -EIO; + + for (i = nbytes - 1, *time = 0; i >= 0; i--) + *time = (*time << 8) | buf[i]; + + return 0; +} + +static int ds1374_write_rtc(struct i2c_client *client, u32 time, + int reg, int nbytes) +{ + u8 buf[4]; + int i; + + if (nbytes > 4) { + WARN_ON(1); + return -EINVAL; + } + + for (i = 0; i < nbytes; i++) { + buf[i] = time & 0xff; + time >>= 8; + } + + return i2c_smbus_write_i2c_block_data(client, reg, nbytes, buf); +} + +static int ds1374_check_rtc_status(struct i2c_client *client) +{ + int ret = 0; + int control, stat; + + stat = i2c_smbus_read_byte_data(client, DS1374_REG_SR); + if (stat < 0) + return stat; + + if (stat & DS1374_REG_SR_OSF) + dev_warn(&client->dev, + "oscillator discontinuity flagged, time unreliable\n"); + + stat &= ~(DS1374_REG_SR_OSF | DS1374_REG_SR_AF); + + ret = i2c_smbus_write_byte_data(client, DS1374_REG_SR, stat); + if (ret < 0) + return ret; + + /* If the alarm is pending, clear it before requesting + * the interrupt, so an interrupt event isn't reported + * before everything is initialized. + */ + + control = i2c_smbus_read_byte_data(client, DS1374_REG_CR); + if (control < 0) + return control; + + control &= ~(DS1374_REG_CR_WACE | DS1374_REG_CR_AIE); + return i2c_smbus_write_byte_data(client, DS1374_REG_CR, control); +} + +static int ds1374_read_time(struct device *dev, struct rtc_time *time) +{ + struct i2c_client *client = to_i2c_client(dev); + u32 itime; + int ret; + + ret = ds1374_read_rtc(client, &itime, DS1374_REG_TOD0, 4); + if (!ret) + rtc_time64_to_tm(itime, time); + + return ret; +} + +static int ds1374_set_time(struct device *dev, struct rtc_time *time) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned long itime = rtc_tm_to_time64(time); + + return ds1374_write_rtc(client, itime, DS1374_REG_TOD0, 4); +} + +#ifndef CONFIG_RTC_DRV_DS1374_WDT +/* The ds1374 has a decrementer for an alarm, rather than a comparator. + * If the time of day is changed, then the alarm will need to be + * reset. + */ +static int ds1374_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ds1374 *ds1374 = i2c_get_clientdata(client); + u32 now, cur_alarm; + int cr, sr; + int ret = 0; + + if (client->irq <= 0) + return -EINVAL; + + mutex_lock(&ds1374->mutex); + + cr = ret = i2c_smbus_read_byte_data(client, DS1374_REG_CR); + if (ret < 0) + goto out; + + sr = ret = i2c_smbus_read_byte_data(client, DS1374_REG_SR); + if (ret < 0) + goto out; + + ret = ds1374_read_rtc(client, &now, DS1374_REG_TOD0, 4); + if (ret) + goto out; + + ret = ds1374_read_rtc(client, &cur_alarm, DS1374_REG_WDALM0, 3); + if (ret) + goto out; + + rtc_time64_to_tm(now + cur_alarm, &alarm->time); + alarm->enabled = !!(cr & DS1374_REG_CR_WACE); + alarm->pending = !!(sr & DS1374_REG_SR_AF); + +out: + mutex_unlock(&ds1374->mutex); + return ret; +} + +static int ds1374_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ds1374 *ds1374 = i2c_get_clientdata(client); + struct rtc_time now; + unsigned long new_alarm, itime; + int cr; + int ret = 0; + + if (client->irq <= 0) + return -EINVAL; + + ret = ds1374_read_time(dev, &now); + if (ret < 0) + return ret; + + new_alarm = rtc_tm_to_time64(&alarm->time); + itime = rtc_tm_to_time64(&now); + + /* This can happen due to races, in addition to dates that are + * truly in the past. To avoid requiring the caller to check for + * races, dates in the past are assumed to be in the recent past + * (i.e. not something that we'd rather the caller know about via + * an error), and the alarm is set to go off as soon as possible. + */ + if (time_before_eq(new_alarm, itime)) + new_alarm = 1; + else + new_alarm -= itime; + + mutex_lock(&ds1374->mutex); + + ret = cr = i2c_smbus_read_byte_data(client, DS1374_REG_CR); + if (ret < 0) + goto out; + + /* Disable any existing alarm before setting the new one + * (or lack thereof). */ + cr &= ~DS1374_REG_CR_WACE; + + ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr); + if (ret < 0) + goto out; + + ret = ds1374_write_rtc(client, new_alarm, DS1374_REG_WDALM0, 3); + if (ret) + goto out; + + if (alarm->enabled) { + cr |= DS1374_REG_CR_WACE | DS1374_REG_CR_AIE; + cr &= ~DS1374_REG_CR_WDALM; + + ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr); + } + +out: + mutex_unlock(&ds1374->mutex); + return ret; +} +#endif + +static irqreturn_t ds1374_irq(int irq, void *dev_id) +{ + struct i2c_client *client = dev_id; + struct ds1374 *ds1374 = i2c_get_clientdata(client); + + disable_irq_nosync(irq); + schedule_work(&ds1374->work); + return IRQ_HANDLED; +} + +static void ds1374_work(struct work_struct *work) +{ + struct ds1374 *ds1374 = container_of(work, struct ds1374, work); + struct i2c_client *client = ds1374->client; + int stat, control; + + mutex_lock(&ds1374->mutex); + + stat = i2c_smbus_read_byte_data(client, DS1374_REG_SR); + if (stat < 0) + goto unlock; + + if (stat & DS1374_REG_SR_AF) { + stat &= ~DS1374_REG_SR_AF; + i2c_smbus_write_byte_data(client, DS1374_REG_SR, stat); + + control = i2c_smbus_read_byte_data(client, DS1374_REG_CR); + if (control < 0) + goto out; + + control &= ~(DS1374_REG_CR_WACE | DS1374_REG_CR_AIE); + i2c_smbus_write_byte_data(client, DS1374_REG_CR, control); + + rtc_update_irq(ds1374->rtc, 1, RTC_AF | RTC_IRQF); + } + +out: + if (!ds1374->exiting) + enable_irq(client->irq); +unlock: + mutex_unlock(&ds1374->mutex); +} + +#ifndef CONFIG_RTC_DRV_DS1374_WDT +static int ds1374_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ds1374 *ds1374 = i2c_get_clientdata(client); + int ret; + + mutex_lock(&ds1374->mutex); + + ret = i2c_smbus_read_byte_data(client, DS1374_REG_CR); + if (ret < 0) + goto out; + + if (enabled) { + ret |= DS1374_REG_CR_WACE | DS1374_REG_CR_AIE; + ret &= ~DS1374_REG_CR_WDALM; + } else { + ret &= ~DS1374_REG_CR_WACE; + } + ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, ret); + +out: + mutex_unlock(&ds1374->mutex); + return ret; +} +#endif + +static const struct rtc_class_ops ds1374_rtc_ops = { + .read_time = ds1374_read_time, + .set_time = ds1374_set_time, +#ifndef CONFIG_RTC_DRV_DS1374_WDT + .read_alarm = ds1374_read_alarm, + .set_alarm = ds1374_set_alarm, + .alarm_irq_enable = ds1374_alarm_irq_enable, +#endif +}; + +#ifdef CONFIG_RTC_DRV_DS1374_WDT +/* + ***************************************************************************** + * + * Watchdog Driver + * + ***************************************************************************** + */ +/* Default margin */ +#define TIMER_MARGIN_DEFAULT 32 +#define TIMER_MARGIN_MIN 1 +#define TIMER_MARGIN_MAX 4095 /* 24-bit value */ + +static int wdt_margin; +module_param(wdt_margin, int, 0); +MODULE_PARM_DESC(wdt_margin, "Watchdog timeout in seconds (default 32s)"); + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default =" + __MODULE_STRING(WATCHDOG_NOWAYOUT)")"); + +static const struct watchdog_info ds1374_wdt_info = { + .identity = "DS1374 Watchdog", + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, +}; + +static int ds1374_wdt_settimeout(struct watchdog_device *wdt, unsigned int timeout) +{ + struct ds1374 *ds1374 = watchdog_get_drvdata(wdt); + struct i2c_client *client = ds1374->client; + int ret, cr; + + wdt->timeout = timeout; + + cr = i2c_smbus_read_byte_data(client, DS1374_REG_CR); + if (cr < 0) + return cr; + + /* Disable any existing watchdog/alarm before setting the new one */ + cr &= ~DS1374_REG_CR_WACE; + + ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr); + if (ret < 0) + return ret; + + /* Set new watchdog time */ + timeout = timeout * 4096; + ret = ds1374_write_rtc(client, timeout, DS1374_REG_WDALM0, 3); + if (ret) + return ret; + + /* Enable watchdog timer */ + cr |= DS1374_REG_CR_WACE | DS1374_REG_CR_WDALM; + cr &= ~DS1374_REG_CR_WDSTR;/* for RST PIN */ + cr &= ~DS1374_REG_CR_AIE; + + ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr); + if (ret < 0) + return ret; + + return 0; +} + +/* + * Reload the watchdog timer. (ie, pat the watchdog) + */ +static int ds1374_wdt_start(struct watchdog_device *wdt) +{ + struct ds1374 *ds1374 = watchdog_get_drvdata(wdt); + u32 val; + + return ds1374_read_rtc(ds1374->client, &val, DS1374_REG_WDALM0, 3); +} + +static int ds1374_wdt_stop(struct watchdog_device *wdt) +{ + struct ds1374 *ds1374 = watchdog_get_drvdata(wdt); + struct i2c_client *client = ds1374->client; + int cr; + + cr = i2c_smbus_read_byte_data(client, DS1374_REG_CR); + if (cr < 0) + return cr; + + /* Disable watchdog timer */ + cr &= ~DS1374_REG_CR_WACE; + + return i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr); +} + +static const struct watchdog_ops ds1374_wdt_ops = { + .owner = THIS_MODULE, + .start = ds1374_wdt_start, + .stop = ds1374_wdt_stop, + .set_timeout = ds1374_wdt_settimeout, +}; +#endif /*CONFIG_RTC_DRV_DS1374_WDT*/ +/* + ***************************************************************************** + * + * Driver Interface + * + ***************************************************************************** + */ +static int ds1374_probe(struct i2c_client *client) +{ + struct ds1374 *ds1374; + int ret; + + ds1374 = devm_kzalloc(&client->dev, sizeof(struct ds1374), GFP_KERNEL); + if (!ds1374) + return -ENOMEM; + + ds1374->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(ds1374->rtc)) + return PTR_ERR(ds1374->rtc); + + ds1374->client = client; + i2c_set_clientdata(client, ds1374); + + INIT_WORK(&ds1374->work, ds1374_work); + mutex_init(&ds1374->mutex); + + ret = ds1374_check_rtc_status(client); + if (ret) + return ret; + + if (client->irq > 0) { + ret = devm_request_irq(&client->dev, client->irq, ds1374_irq, 0, + "ds1374", client); + if (ret) { + dev_err(&client->dev, "unable to request IRQ\n"); + return ret; + } + + device_set_wakeup_capable(&client->dev, 1); + } + + ds1374->rtc->ops = &ds1374_rtc_ops; + ds1374->rtc->range_max = U32_MAX; + + ret = devm_rtc_register_device(ds1374->rtc); + if (ret) + return ret; + +#ifdef CONFIG_RTC_DRV_DS1374_WDT + ds1374->wdt.info = &ds1374_wdt_info; + ds1374->wdt.ops = &ds1374_wdt_ops; + ds1374->wdt.timeout = TIMER_MARGIN_DEFAULT; + ds1374->wdt.min_timeout = TIMER_MARGIN_MIN; + ds1374->wdt.max_timeout = TIMER_MARGIN_MAX; + + watchdog_init_timeout(&ds1374->wdt, wdt_margin, &client->dev); + watchdog_set_nowayout(&ds1374->wdt, nowayout); + watchdog_stop_on_reboot(&ds1374->wdt); + watchdog_stop_on_unregister(&ds1374->wdt); + watchdog_set_drvdata(&ds1374->wdt, ds1374); + ds1374_wdt_settimeout(&ds1374->wdt, ds1374->wdt.timeout); + + ret = devm_watchdog_register_device(&client->dev, &ds1374->wdt); + if (ret) + return ret; +#endif + + return 0; +} + +static void ds1374_remove(struct i2c_client *client) +{ + struct ds1374 *ds1374 = i2c_get_clientdata(client); + + if (client->irq > 0) { + mutex_lock(&ds1374->mutex); + ds1374->exiting = 1; + mutex_unlock(&ds1374->mutex); + + devm_free_irq(&client->dev, client->irq, client); + cancel_work_sync(&ds1374->work); + } +} + +#ifdef CONFIG_PM_SLEEP +static int ds1374_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (client->irq > 0 && device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + return 0; +} + +static int ds1374_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (client->irq > 0 && device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(ds1374_pm, ds1374_suspend, ds1374_resume); + +static struct i2c_driver ds1374_driver = { + .driver = { + .name = "rtc-ds1374", + .of_match_table = of_match_ptr(ds1374_of_match), + .pm = &ds1374_pm, + }, + .probe = ds1374_probe, + .remove = ds1374_remove, + .id_table = ds1374_id, +}; + +module_i2c_driver(ds1374_driver); + +MODULE_AUTHOR("Scott Wood "); +MODULE_DESCRIPTION("Maxim/Dallas DS1374 RTC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-ds1390.c b/drivers/rtc/rtc-ds1390.c new file mode 100644 index 000000000..f7afd6bde --- /dev/null +++ b/drivers/rtc/rtc-ds1390.c @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rtc-ds1390.c -- driver for the Dallas/Maxim DS1390/93/94 SPI RTC + * + * Copyright (C) 2008 Mercury IMC Ltd + * Written by Mark Jackson + * + * NOTE: Currently this driver only supports the bare minimum for read + * and write the RTC. The extra features provided by the chip family + * (alarms, trickle charger, different control registers) are unavailable. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DS1390_REG_100THS 0x00 +#define DS1390_REG_SECONDS 0x01 +#define DS1390_REG_MINUTES 0x02 +#define DS1390_REG_HOURS 0x03 +#define DS1390_REG_DAY 0x04 +#define DS1390_REG_DATE 0x05 +#define DS1390_REG_MONTH_CENT 0x06 +#define DS1390_REG_YEAR 0x07 + +#define DS1390_REG_ALARM_100THS 0x08 +#define DS1390_REG_ALARM_SECONDS 0x09 +#define DS1390_REG_ALARM_MINUTES 0x0A +#define DS1390_REG_ALARM_HOURS 0x0B +#define DS1390_REG_ALARM_DAY_DATE 0x0C + +#define DS1390_REG_CONTROL 0x0D +#define DS1390_REG_STATUS 0x0E +#define DS1390_REG_TRICKLE 0x0F + +#define DS1390_TRICKLE_CHARGER_ENABLE 0xA0 +#define DS1390_TRICKLE_CHARGER_250_OHM 0x01 +#define DS1390_TRICKLE_CHARGER_2K_OHM 0x02 +#define DS1390_TRICKLE_CHARGER_4K_OHM 0x03 +#define DS1390_TRICKLE_CHARGER_NO_DIODE 0x04 +#define DS1390_TRICKLE_CHARGER_DIODE 0x08 + +struct ds1390 { + struct rtc_device *rtc; + u8 txrx_buf[9]; /* cmd + 8 registers */ +}; + +static void ds1390_set_reg(struct device *dev, unsigned char address, + unsigned char data) +{ + struct spi_device *spi = to_spi_device(dev); + unsigned char buf[2]; + + /* MSB must be '1' to write */ + buf[0] = address | 0x80; + buf[1] = data; + + spi_write(spi, buf, 2); +} + +static int ds1390_get_reg(struct device *dev, unsigned char address, + unsigned char *data) +{ + struct spi_device *spi = to_spi_device(dev); + struct ds1390 *chip = dev_get_drvdata(dev); + int status; + + if (!data) + return -EINVAL; + + /* Clear MSB to indicate read */ + chip->txrx_buf[0] = address & 0x7f; + /* do the i/o */ + status = spi_write_then_read(spi, chip->txrx_buf, 1, chip->txrx_buf, 1); + if (status != 0) + return status; + + *data = chip->txrx_buf[0]; + + return 0; +} + +static void ds1390_trickle_of_init(struct spi_device *spi) +{ + u32 ohms = 0; + u8 value; + + if (of_property_read_u32(spi->dev.of_node, "trickle-resistor-ohms", + &ohms)) + goto out; + + /* Enable charger */ + value = DS1390_TRICKLE_CHARGER_ENABLE; + if (of_property_read_bool(spi->dev.of_node, "trickle-diode-disable")) + value |= DS1390_TRICKLE_CHARGER_NO_DIODE; + else + value |= DS1390_TRICKLE_CHARGER_DIODE; + + /* Resistor select */ + switch (ohms) { + case 250: + value |= DS1390_TRICKLE_CHARGER_250_OHM; + break; + case 2000: + value |= DS1390_TRICKLE_CHARGER_2K_OHM; + break; + case 4000: + value |= DS1390_TRICKLE_CHARGER_4K_OHM; + break; + default: + dev_warn(&spi->dev, + "Unsupported ohm value %02ux in dt\n", ohms); + return; + } + + ds1390_set_reg(&spi->dev, DS1390_REG_TRICKLE, value); + +out: + return; +} + +static int ds1390_read_time(struct device *dev, struct rtc_time *dt) +{ + struct spi_device *spi = to_spi_device(dev); + struct ds1390 *chip = dev_get_drvdata(dev); + int status; + + /* build the message */ + chip->txrx_buf[0] = DS1390_REG_SECONDS; + + /* do the i/o */ + status = spi_write_then_read(spi, chip->txrx_buf, 1, chip->txrx_buf, 7); + if (status != 0) + return status; + + /* The chip sends data in this order: + * Seconds, Minutes, Hours, Day, Date, Month / Century, Year */ + dt->tm_sec = bcd2bin(chip->txrx_buf[0]); + dt->tm_min = bcd2bin(chip->txrx_buf[1]); + dt->tm_hour = bcd2bin(chip->txrx_buf[2]); + dt->tm_wday = bcd2bin(chip->txrx_buf[3]); + dt->tm_mday = bcd2bin(chip->txrx_buf[4]); + /* mask off century bit */ + dt->tm_mon = bcd2bin(chip->txrx_buf[5] & 0x7f) - 1; + /* adjust for century bit */ + dt->tm_year = bcd2bin(chip->txrx_buf[6]) + ((chip->txrx_buf[5] & 0x80) ? 100 : 0); + + return 0; +} + +static int ds1390_set_time(struct device *dev, struct rtc_time *dt) +{ + struct spi_device *spi = to_spi_device(dev); + struct ds1390 *chip = dev_get_drvdata(dev); + + /* build the message */ + chip->txrx_buf[0] = DS1390_REG_SECONDS | 0x80; + chip->txrx_buf[1] = bin2bcd(dt->tm_sec); + chip->txrx_buf[2] = bin2bcd(dt->tm_min); + chip->txrx_buf[3] = bin2bcd(dt->tm_hour); + chip->txrx_buf[4] = bin2bcd(dt->tm_wday); + chip->txrx_buf[5] = bin2bcd(dt->tm_mday); + chip->txrx_buf[6] = bin2bcd(dt->tm_mon + 1) | + ((dt->tm_year > 99) ? 0x80 : 0x00); + chip->txrx_buf[7] = bin2bcd(dt->tm_year % 100); + + /* do the i/o */ + return spi_write_then_read(spi, chip->txrx_buf, 8, NULL, 0); +} + +static const struct rtc_class_ops ds1390_rtc_ops = { + .read_time = ds1390_read_time, + .set_time = ds1390_set_time, +}; + +static int ds1390_probe(struct spi_device *spi) +{ + unsigned char tmp; + struct ds1390 *chip; + int res; + + spi->mode = SPI_MODE_3; + spi->bits_per_word = 8; + spi_setup(spi); + + chip = devm_kzalloc(&spi->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + spi_set_drvdata(spi, chip); + + res = ds1390_get_reg(&spi->dev, DS1390_REG_SECONDS, &tmp); + if (res != 0) { + dev_err(&spi->dev, "unable to read device\n"); + return res; + } + + if (spi->dev.of_node) + ds1390_trickle_of_init(spi); + + chip->rtc = devm_rtc_device_register(&spi->dev, "ds1390", + &ds1390_rtc_ops, THIS_MODULE); + if (IS_ERR(chip->rtc)) { + dev_err(&spi->dev, "unable to register device\n"); + res = PTR_ERR(chip->rtc); + } + + return res; +} + +static const struct of_device_id ds1390_of_match[] __maybe_unused = { + { .compatible = "dallas,ds1390" }, + {} +}; +MODULE_DEVICE_TABLE(of, ds1390_of_match); + +static const struct spi_device_id ds1390_spi_ids[] = { + { .name = "ds1390" }, + {} +}; +MODULE_DEVICE_TABLE(spi, ds1390_spi_ids); + +static struct spi_driver ds1390_driver = { + .driver = { + .name = "rtc-ds1390", + .of_match_table = of_match_ptr(ds1390_of_match), + }, + .probe = ds1390_probe, + .id_table = ds1390_spi_ids, +}; + +module_spi_driver(ds1390_driver); + +MODULE_DESCRIPTION("Dallas/Maxim DS1390/93/94 SPI RTC driver"); +MODULE_AUTHOR("Mark Jackson "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:rtc-ds1390"); diff --git a/drivers/rtc/rtc-ds1511.c b/drivers/rtc/rtc-ds1511.c new file mode 100644 index 000000000..8b087d955 --- /dev/null +++ b/drivers/rtc/rtc-ds1511.c @@ -0,0 +1,368 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * An rtc driver for the Dallas DS1511 + * + * Copyright (C) 2006 Atsushi Nemoto + * Copyright (C) 2007 Andrew Sharp + * + * Real time clock driver for the Dallas 1511 chip, which also + * contains a watchdog timer. There is a tiny amount of code that + * platform code could use to mess with the watchdog device a little + * bit, but not a full watchdog driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DS1511_SEC 0x0 +#define DS1511_MIN 0x1 +#define DS1511_HOUR 0x2 +#define DS1511_DOW 0x3 +#define DS1511_DOM 0x4 +#define DS1511_MONTH 0x5 +#define DS1511_YEAR 0x6 +#define DS1511_CENTURY 0x7 +#define DS1511_AM1_SEC 0x8 +#define DS1511_AM2_MIN 0x9 +#define DS1511_AM3_HOUR 0xa +#define DS1511_AM4_DATE 0xb +#define DS1511_WD_MSEC 0xc +#define DS1511_WD_SEC 0xd +#define DS1511_CONTROL_A 0xe +#define DS1511_CONTROL_B 0xf +#define DS1511_RAMADDR_LSB 0x10 +#define DS1511_RAMDATA 0x13 + +#define DS1511_BLF1 0x80 +#define DS1511_BLF2 0x40 +#define DS1511_PRS 0x20 +#define DS1511_PAB 0x10 +#define DS1511_TDF 0x08 +#define DS1511_KSF 0x04 +#define DS1511_WDF 0x02 +#define DS1511_IRQF 0x01 +#define DS1511_TE 0x80 +#define DS1511_CS 0x40 +#define DS1511_BME 0x20 +#define DS1511_TPE 0x10 +#define DS1511_TIE 0x08 +#define DS1511_KIE 0x04 +#define DS1511_WDE 0x02 +#define DS1511_WDS 0x01 +#define DS1511_RAM_MAX 0x100 + +struct ds1511_data { + struct rtc_device *rtc; + void __iomem *ioaddr; /* virtual base address */ + int irq; + spinlock_t lock; +}; + +static DEFINE_SPINLOCK(ds1511_lock); + +static __iomem char *ds1511_base; +static u32 reg_spacing = 1; + +static void rtc_write(uint8_t val, uint32_t reg) +{ + writeb(val, ds1511_base + (reg * reg_spacing)); +} + +static uint8_t rtc_read(uint32_t reg) +{ + return readb(ds1511_base + (reg * reg_spacing)); +} + +static void rtc_disable_update(void) +{ + rtc_write((rtc_read(DS1511_CONTROL_B) & ~DS1511_TE), DS1511_CONTROL_B); +} + +static void rtc_enable_update(void) +{ + rtc_write((rtc_read(DS1511_CONTROL_B) | DS1511_TE), DS1511_CONTROL_B); +} + +static int ds1511_rtc_set_time(struct device *dev, struct rtc_time *rtc_tm) +{ + u8 mon, day, dow, hrs, min, sec, yrs, cen; + unsigned long flags; + + yrs = rtc_tm->tm_year % 100; + cen = 19 + rtc_tm->tm_year / 100; + mon = rtc_tm->tm_mon + 1; /* tm_mon starts at zero */ + day = rtc_tm->tm_mday; + dow = rtc_tm->tm_wday & 0x7; /* automatic BCD */ + hrs = rtc_tm->tm_hour; + min = rtc_tm->tm_min; + sec = rtc_tm->tm_sec; + + /* + * each register is a different number of valid bits + */ + sec = bin2bcd(sec) & 0x7f; + min = bin2bcd(min) & 0x7f; + hrs = bin2bcd(hrs) & 0x3f; + day = bin2bcd(day) & 0x3f; + mon = bin2bcd(mon) & 0x1f; + yrs = bin2bcd(yrs) & 0xff; + cen = bin2bcd(cen) & 0xff; + + spin_lock_irqsave(&ds1511_lock, flags); + rtc_disable_update(); + rtc_write(cen, DS1511_CENTURY); + rtc_write(yrs, DS1511_YEAR); + rtc_write((rtc_read(DS1511_MONTH) & 0xe0) | mon, DS1511_MONTH); + rtc_write(day, DS1511_DOM); + rtc_write(hrs, DS1511_HOUR); + rtc_write(min, DS1511_MIN); + rtc_write(sec, DS1511_SEC); + rtc_write(dow, DS1511_DOW); + rtc_enable_update(); + spin_unlock_irqrestore(&ds1511_lock, flags); + + return 0; +} + +static int ds1511_rtc_read_time(struct device *dev, struct rtc_time *rtc_tm) +{ + unsigned int century; + unsigned long flags; + + spin_lock_irqsave(&ds1511_lock, flags); + rtc_disable_update(); + + rtc_tm->tm_sec = rtc_read(DS1511_SEC) & 0x7f; + rtc_tm->tm_min = rtc_read(DS1511_MIN) & 0x7f; + rtc_tm->tm_hour = rtc_read(DS1511_HOUR) & 0x3f; + rtc_tm->tm_mday = rtc_read(DS1511_DOM) & 0x3f; + rtc_tm->tm_wday = rtc_read(DS1511_DOW) & 0x7; + rtc_tm->tm_mon = rtc_read(DS1511_MONTH) & 0x1f; + rtc_tm->tm_year = rtc_read(DS1511_YEAR) & 0x7f; + century = rtc_read(DS1511_CENTURY); + + rtc_enable_update(); + spin_unlock_irqrestore(&ds1511_lock, flags); + + rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec); + rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min); + rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour); + rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday); + rtc_tm->tm_wday = bcd2bin(rtc_tm->tm_wday); + rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon); + rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year); + century = bcd2bin(century) * 100; + + /* + * Account for differences between how the RTC uses the values + * and how they are defined in a struct rtc_time; + */ + century += rtc_tm->tm_year; + rtc_tm->tm_year = century - 1900; + + rtc_tm->tm_mon--; + + return 0; +} + +static void ds1511_rtc_alarm_enable(unsigned int enabled) +{ + rtc_write(rtc_read(DS1511_CONTROL_B) | (enabled ? DS1511_TIE : 0), DS1511_CONTROL_B); +} + +static int ds1511_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct ds1511_data *ds1511 = dev_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&ds1511->lock, flags); + rtc_write(bin2bcd(alrm->time.tm_mday) & 0x3f, DS1511_AM4_DATE); + rtc_write(bin2bcd(alrm->time.tm_hour) & 0x3f, DS1511_AM3_HOUR); + rtc_write(bin2bcd(alrm->time.tm_min) & 0x7f, DS1511_AM2_MIN); + rtc_write(bin2bcd(alrm->time.tm_sec) & 0x7f, DS1511_AM1_SEC); + ds1511_rtc_alarm_enable(alrm->enabled); + + rtc_read(DS1511_CONTROL_A); /* clear interrupts */ + spin_unlock_irqrestore(&ds1511->lock, flags); + + return 0; +} + +static int ds1511_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + alrm->time.tm_mday = bcd2bin(rtc_read(DS1511_AM4_DATE) & 0x3f); + alrm->time.tm_hour = bcd2bin(rtc_read(DS1511_AM3_HOUR) & 0x3f); + alrm->time.tm_min = bcd2bin(rtc_read(DS1511_AM2_MIN) & 0x7f); + alrm->time.tm_sec = bcd2bin(rtc_read(DS1511_AM1_SEC) & 0x7f); + alrm->enabled = !!(rtc_read(DS1511_CONTROL_B) & DS1511_TIE); + + return 0; +} + +static irqreturn_t ds1511_interrupt(int irq, void *dev_id) +{ + struct platform_device *pdev = dev_id; + struct ds1511_data *ds1511 = platform_get_drvdata(pdev); + unsigned long events = 0; + + spin_lock(&ds1511->lock); + /* + * read and clear interrupt + */ + if (rtc_read(DS1511_CONTROL_A) & DS1511_IRQF) { + events = RTC_IRQF | RTC_AF; + rtc_update_irq(ds1511->rtc, 1, events); + } + spin_unlock(&ds1511->lock); + return events ? IRQ_HANDLED : IRQ_NONE; +} + +static int ds1511_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct ds1511_data *ds1511 = dev_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&ds1511->lock, flags); + ds1511_rtc_alarm_enable(enabled); + spin_unlock_irqrestore(&ds1511->lock, flags); + + return 0; +} + +static const struct rtc_class_ops ds1511_rtc_ops = { + .read_time = ds1511_rtc_read_time, + .set_time = ds1511_rtc_set_time, + .read_alarm = ds1511_rtc_read_alarm, + .set_alarm = ds1511_rtc_set_alarm, + .alarm_irq_enable = ds1511_rtc_alarm_irq_enable, +}; + +static int ds1511_nvram_read(void *priv, unsigned int pos, void *buf, + size_t size) +{ + int i; + + rtc_write(pos, DS1511_RAMADDR_LSB); + for (i = 0; i < size; i++) + *(char *)buf++ = rtc_read(DS1511_RAMDATA); + + return 0; +} + +static int ds1511_nvram_write(void *priv, unsigned int pos, void *buf, + size_t size) +{ + int i; + + rtc_write(pos, DS1511_RAMADDR_LSB); + for (i = 0; i < size; i++) + rtc_write(*(char *)buf++, DS1511_RAMDATA); + + return 0; +} + +static int ds1511_rtc_probe(struct platform_device *pdev) +{ + struct ds1511_data *ds1511; + int ret = 0; + struct nvmem_config ds1511_nvmem_cfg = { + .name = "ds1511_nvram", + .word_size = 1, + .stride = 1, + .size = DS1511_RAM_MAX, + .reg_read = ds1511_nvram_read, + .reg_write = ds1511_nvram_write, + .priv = &pdev->dev, + }; + + ds1511 = devm_kzalloc(&pdev->dev, sizeof(*ds1511), GFP_KERNEL); + if (!ds1511) + return -ENOMEM; + + ds1511_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ds1511_base)) + return PTR_ERR(ds1511_base); + ds1511->ioaddr = ds1511_base; + ds1511->irq = platform_get_irq(pdev, 0); + + /* + * turn on the clock and the crystal, etc. + */ + rtc_write(DS1511_BME, DS1511_CONTROL_B); + rtc_write(0, DS1511_CONTROL_A); + /* + * clear the wdog counter + */ + rtc_write(0, DS1511_WD_MSEC); + rtc_write(0, DS1511_WD_SEC); + /* + * start the clock + */ + rtc_enable_update(); + + /* + * check for a dying bat-tree + */ + if (rtc_read(DS1511_CONTROL_A) & DS1511_BLF1) + dev_warn(&pdev->dev, "voltage-low detected.\n"); + + spin_lock_init(&ds1511->lock); + platform_set_drvdata(pdev, ds1511); + + ds1511->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(ds1511->rtc)) + return PTR_ERR(ds1511->rtc); + + ds1511->rtc->ops = &ds1511_rtc_ops; + ds1511->rtc->range_max = RTC_TIMESTAMP_END_2099; + ds1511->rtc->alarm_offset_max = 28 * 24 * 60 * 60 - 1; + + /* + * if the platform has an interrupt in mind for this device, + * then by all means, set it + */ + if (ds1511->irq > 0) { + rtc_read(DS1511_CONTROL_A); + if (devm_request_irq(&pdev->dev, ds1511->irq, ds1511_interrupt, + IRQF_SHARED, pdev->name, pdev) < 0) { + + dev_warn(&pdev->dev, "interrupt not available.\n"); + ds1511->irq = 0; + } + } + + if (ds1511->irq == 0) + clear_bit(RTC_FEATURE_ALARM, ds1511->rtc->features); + + ret = devm_rtc_register_device(ds1511->rtc); + if (ret) + return ret; + + devm_rtc_nvmem_register(ds1511->rtc, &ds1511_nvmem_cfg); + + return 0; +} + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:ds1511"); + +static struct platform_driver ds1511_rtc_driver = { + .probe = ds1511_rtc_probe, + .driver = { + .name = "ds1511", + }, +}; + +module_platform_driver(ds1511_rtc_driver); + +MODULE_AUTHOR("Andrew Sharp "); +MODULE_DESCRIPTION("Dallas DS1511 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c new file mode 100644 index 000000000..dbff5b621 --- /dev/null +++ b/drivers/rtc/rtc-ds1553.c @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * An rtc driver for the Dallas DS1553 + * + * Copyright (C) 2006 Atsushi Nemoto + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RTC_REG_SIZE 0x2000 +#define RTC_OFFSET 0x1ff0 + +#define RTC_FLAGS (RTC_OFFSET + 0) +#define RTC_SECONDS_ALARM (RTC_OFFSET + 2) +#define RTC_MINUTES_ALARM (RTC_OFFSET + 3) +#define RTC_HOURS_ALARM (RTC_OFFSET + 4) +#define RTC_DATE_ALARM (RTC_OFFSET + 5) +#define RTC_INTERRUPTS (RTC_OFFSET + 6) +#define RTC_WATCHDOG (RTC_OFFSET + 7) +#define RTC_CONTROL (RTC_OFFSET + 8) +#define RTC_CENTURY (RTC_OFFSET + 8) +#define RTC_SECONDS (RTC_OFFSET + 9) +#define RTC_MINUTES (RTC_OFFSET + 10) +#define RTC_HOURS (RTC_OFFSET + 11) +#define RTC_DAY (RTC_OFFSET + 12) +#define RTC_DATE (RTC_OFFSET + 13) +#define RTC_MONTH (RTC_OFFSET + 14) +#define RTC_YEAR (RTC_OFFSET + 15) + +#define RTC_CENTURY_MASK 0x3f +#define RTC_SECONDS_MASK 0x7f +#define RTC_DAY_MASK 0x07 + +/* Bits in the Control/Century register */ +#define RTC_WRITE 0x80 +#define RTC_READ 0x40 + +/* Bits in the Seconds register */ +#define RTC_STOP 0x80 + +/* Bits in the Flags register */ +#define RTC_FLAGS_AF 0x40 +#define RTC_FLAGS_BLF 0x10 + +/* Bits in the Interrupts register */ +#define RTC_INTS_AE 0x80 + +struct rtc_plat_data { + struct rtc_device *rtc; + void __iomem *ioaddr; + unsigned long last_jiffies; + int irq; + unsigned int irqen; + int alrm_sec; + int alrm_min; + int alrm_hour; + int alrm_mday; + spinlock_t lock; +}; + +static int ds1553_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + void __iomem *ioaddr = pdata->ioaddr; + u8 century; + + century = bin2bcd((tm->tm_year + 1900) / 100); + + writeb(RTC_WRITE, pdata->ioaddr + RTC_CONTROL); + + writeb(bin2bcd(tm->tm_year % 100), ioaddr + RTC_YEAR); + writeb(bin2bcd(tm->tm_mon + 1), ioaddr + RTC_MONTH); + writeb(bin2bcd(tm->tm_wday) & RTC_DAY_MASK, ioaddr + RTC_DAY); + writeb(bin2bcd(tm->tm_mday), ioaddr + RTC_DATE); + writeb(bin2bcd(tm->tm_hour), ioaddr + RTC_HOURS); + writeb(bin2bcd(tm->tm_min), ioaddr + RTC_MINUTES); + writeb(bin2bcd(tm->tm_sec) & RTC_SECONDS_MASK, ioaddr + RTC_SECONDS); + + /* RTC_CENTURY and RTC_CONTROL share same register */ + writeb(RTC_WRITE | (century & RTC_CENTURY_MASK), ioaddr + RTC_CENTURY); + writeb(century & RTC_CENTURY_MASK, ioaddr + RTC_CONTROL); + return 0; +} + +static int ds1553_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + void __iomem *ioaddr = pdata->ioaddr; + unsigned int year, month, day, hour, minute, second, week; + unsigned int century; + + /* give enough time to update RTC in case of continuous read */ + if (pdata->last_jiffies == jiffies) + msleep(1); + pdata->last_jiffies = jiffies; + writeb(RTC_READ, ioaddr + RTC_CONTROL); + second = readb(ioaddr + RTC_SECONDS) & RTC_SECONDS_MASK; + minute = readb(ioaddr + RTC_MINUTES); + hour = readb(ioaddr + RTC_HOURS); + day = readb(ioaddr + RTC_DATE); + week = readb(ioaddr + RTC_DAY) & RTC_DAY_MASK; + month = readb(ioaddr + RTC_MONTH); + year = readb(ioaddr + RTC_YEAR); + century = readb(ioaddr + RTC_CENTURY) & RTC_CENTURY_MASK; + writeb(0, ioaddr + RTC_CONTROL); + tm->tm_sec = bcd2bin(second); + tm->tm_min = bcd2bin(minute); + tm->tm_hour = bcd2bin(hour); + tm->tm_mday = bcd2bin(day); + tm->tm_wday = bcd2bin(week); + tm->tm_mon = bcd2bin(month) - 1; + /* year is 1900 + tm->tm_year */ + tm->tm_year = bcd2bin(year) + bcd2bin(century) * 100 - 1900; + + return 0; +} + +static void ds1553_rtc_update_alarm(struct rtc_plat_data *pdata) +{ + void __iomem *ioaddr = pdata->ioaddr; + unsigned long flags; + + spin_lock_irqsave(&pdata->lock, flags); + writeb(pdata->alrm_mday < 0 || (pdata->irqen & RTC_UF) ? + 0x80 : bin2bcd(pdata->alrm_mday), + ioaddr + RTC_DATE_ALARM); + writeb(pdata->alrm_hour < 0 || (pdata->irqen & RTC_UF) ? + 0x80 : bin2bcd(pdata->alrm_hour), + ioaddr + RTC_HOURS_ALARM); + writeb(pdata->alrm_min < 0 || (pdata->irqen & RTC_UF) ? + 0x80 : bin2bcd(pdata->alrm_min), + ioaddr + RTC_MINUTES_ALARM); + writeb(pdata->alrm_sec < 0 || (pdata->irqen & RTC_UF) ? + 0x80 : bin2bcd(pdata->alrm_sec), + ioaddr + RTC_SECONDS_ALARM); + writeb(pdata->irqen ? RTC_INTS_AE : 0, ioaddr + RTC_INTERRUPTS); + readb(ioaddr + RTC_FLAGS); /* clear interrupts */ + spin_unlock_irqrestore(&pdata->lock, flags); +} + +static int ds1553_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + + if (pdata->irq <= 0) + return -EINVAL; + pdata->alrm_mday = alrm->time.tm_mday; + pdata->alrm_hour = alrm->time.tm_hour; + pdata->alrm_min = alrm->time.tm_min; + pdata->alrm_sec = alrm->time.tm_sec; + if (alrm->enabled) + pdata->irqen |= RTC_AF; + ds1553_rtc_update_alarm(pdata); + return 0; +} + +static int ds1553_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + + if (pdata->irq <= 0) + return -EINVAL; + alrm->time.tm_mday = pdata->alrm_mday < 0 ? 0 : pdata->alrm_mday; + alrm->time.tm_hour = pdata->alrm_hour < 0 ? 0 : pdata->alrm_hour; + alrm->time.tm_min = pdata->alrm_min < 0 ? 0 : pdata->alrm_min; + alrm->time.tm_sec = pdata->alrm_sec < 0 ? 0 : pdata->alrm_sec; + alrm->enabled = (pdata->irqen & RTC_AF) ? 1 : 0; + return 0; +} + +static irqreturn_t ds1553_rtc_interrupt(int irq, void *dev_id) +{ + struct platform_device *pdev = dev_id; + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + unsigned long events = 0; + + spin_lock(&pdata->lock); + /* read and clear interrupt */ + if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_AF) { + events = RTC_IRQF; + if (readb(ioaddr + RTC_SECONDS_ALARM) & 0x80) + events |= RTC_UF; + else + events |= RTC_AF; + rtc_update_irq(pdata->rtc, 1, events); + } + spin_unlock(&pdata->lock); + return events ? IRQ_HANDLED : IRQ_NONE; +} + +static int ds1553_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + + if (pdata->irq <= 0) + return -EINVAL; + if (enabled) + pdata->irqen |= RTC_AF; + else + pdata->irqen &= ~RTC_AF; + ds1553_rtc_update_alarm(pdata); + return 0; +} + +static const struct rtc_class_ops ds1553_rtc_ops = { + .read_time = ds1553_rtc_read_time, + .set_time = ds1553_rtc_set_time, + .read_alarm = ds1553_rtc_read_alarm, + .set_alarm = ds1553_rtc_set_alarm, + .alarm_irq_enable = ds1553_rtc_alarm_irq_enable, +}; + +static int ds1553_nvram_read(void *priv, unsigned int pos, void *val, + size_t bytes) +{ + struct platform_device *pdev = priv; + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + u8 *buf = val; + + for (; bytes; bytes--) + *buf++ = readb(ioaddr + pos++); + return 0; +} + +static int ds1553_nvram_write(void *priv, unsigned int pos, void *val, + size_t bytes) +{ + struct platform_device *pdev = priv; + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + u8 *buf = val; + + for (; bytes; bytes--) + writeb(*buf++, ioaddr + pos++); + return 0; +} + +static int ds1553_rtc_probe(struct platform_device *pdev) +{ + unsigned int cen, sec; + struct rtc_plat_data *pdata; + void __iomem *ioaddr; + int ret = 0; + struct nvmem_config nvmem_cfg = { + .name = "ds1553_nvram", + .word_size = 1, + .stride = 1, + .size = RTC_OFFSET, + .reg_read = ds1553_nvram_read, + .reg_write = ds1553_nvram_write, + .priv = pdev, + }; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + ioaddr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ioaddr)) + return PTR_ERR(ioaddr); + pdata->ioaddr = ioaddr; + pdata->irq = platform_get_irq(pdev, 0); + + /* turn RTC on if it was not on */ + sec = readb(ioaddr + RTC_SECONDS); + if (sec & RTC_STOP) { + sec &= RTC_SECONDS_MASK; + cen = readb(ioaddr + RTC_CENTURY) & RTC_CENTURY_MASK; + writeb(RTC_WRITE, ioaddr + RTC_CONTROL); + writeb(sec, ioaddr + RTC_SECONDS); + writeb(cen & RTC_CENTURY_MASK, ioaddr + RTC_CONTROL); + } + if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_BLF) + dev_warn(&pdev->dev, "voltage-low detected.\n"); + + spin_lock_init(&pdata->lock); + pdata->last_jiffies = jiffies; + platform_set_drvdata(pdev, pdata); + + pdata->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(pdata->rtc)) + return PTR_ERR(pdata->rtc); + + pdata->rtc->ops = &ds1553_rtc_ops; + + ret = devm_rtc_register_device(pdata->rtc); + if (ret) + return ret; + + if (pdata->irq > 0) { + writeb(0, ioaddr + RTC_INTERRUPTS); + if (devm_request_irq(&pdev->dev, pdata->irq, + ds1553_rtc_interrupt, + 0, pdev->name, pdev) < 0) { + dev_warn(&pdev->dev, "interrupt not available.\n"); + pdata->irq = 0; + } + } + + devm_rtc_nvmem_register(pdata->rtc, &nvmem_cfg); + + return 0; +} + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:rtc-ds1553"); + +static struct platform_driver ds1553_rtc_driver = { + .probe = ds1553_rtc_probe, + .driver = { + .name = "rtc-ds1553", + }, +}; + +module_platform_driver(ds1553_rtc_driver); + +MODULE_AUTHOR("Atsushi Nemoto "); +MODULE_DESCRIPTION("Dallas DS1553 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c new file mode 100644 index 000000000..c610beb55 --- /dev/null +++ b/drivers/rtc/rtc-ds1672.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * An rtc/i2c driver for the Dallas DS1672 + * Copyright 2005-06 Tower Technologies + * + * Author: Alessandro Zummo + */ + +#include +#include +#include + +/* Registers */ + +#define DS1672_REG_CNT_BASE 0 +#define DS1672_REG_CONTROL 4 +#define DS1672_REG_TRICKLE 5 + +#define DS1672_REG_CONTROL_EOSC 0x80 + +/* + * In the routines that deal directly with the ds1672 hardware, we use + * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch + * Time is set to UTC. + */ +static int ds1672_read_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned long time; + unsigned char addr = DS1672_REG_CONTROL; + unsigned char buf[4]; + + struct i2c_msg msgs[] = { + {/* setup read ptr */ + .addr = client->addr, + .len = 1, + .buf = &addr + }, + {/* read date */ + .addr = client->addr, + .flags = I2C_M_RD, + .len = 1, + .buf = buf + }, + }; + + /* read control register */ + if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) { + dev_warn(&client->dev, "Unable to read the control register\n"); + return -EIO; + } + + if (buf[0] & DS1672_REG_CONTROL_EOSC) { + dev_warn(&client->dev, "Oscillator not enabled. Set time to enable.\n"); + return -EINVAL; + } + + addr = DS1672_REG_CNT_BASE; + msgs[1].len = 4; + + /* read date registers */ + if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) { + dev_err(&client->dev, "%s: read error\n", __func__); + return -EIO; + } + + dev_dbg(&client->dev, + "%s: raw read data - counters=%02x,%02x,%02x,%02x\n", + __func__, buf[0], buf[1], buf[2], buf[3]); + + time = ((unsigned long)buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; + + rtc_time64_to_tm(time, tm); + + dev_dbg(&client->dev, "%s: tm is %ptR\n", __func__, tm); + + return 0; +} + +static int ds1672_set_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + int xfer; + unsigned char buf[6]; + unsigned long secs = rtc_tm_to_time64(tm); + + buf[0] = DS1672_REG_CNT_BASE; + buf[1] = secs & 0x000000FF; + buf[2] = (secs & 0x0000FF00) >> 8; + buf[3] = (secs & 0x00FF0000) >> 16; + buf[4] = (secs & 0xFF000000) >> 24; + buf[5] = 0; /* set control reg to enable counting */ + + xfer = i2c_master_send(client, buf, 6); + if (xfer != 6) { + dev_err(&client->dev, "%s: send: %d\n", __func__, xfer); + return -EIO; + } + + return 0; +} + +static const struct rtc_class_ops ds1672_rtc_ops = { + .read_time = ds1672_read_time, + .set_time = ds1672_set_time, +}; + +static int ds1672_probe(struct i2c_client *client) +{ + int err = 0; + struct rtc_device *rtc; + + dev_dbg(&client->dev, "%s\n", __func__); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + rtc->ops = &ds1672_rtc_ops; + rtc->range_max = U32_MAX; + + err = devm_rtc_register_device(rtc); + if (err) + return err; + + i2c_set_clientdata(client, rtc); + + return 0; +} + +static const struct i2c_device_id ds1672_id[] = { + { .name = "ds1672" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ds1672_id); + +static const __maybe_unused struct of_device_id ds1672_of_match[] = { + { .compatible = "dallas,ds1672" }, + { } +}; +MODULE_DEVICE_TABLE(of, ds1672_of_match); + +static struct i2c_driver ds1672_driver = { + .driver = { + .name = "rtc-ds1672", + .of_match_table = of_match_ptr(ds1672_of_match), + }, + .probe = ds1672_probe, + .id_table = ds1672_id, +}; + +module_i2c_driver(ds1672_driver); + +MODULE_AUTHOR("Alessandro Zummo "); +MODULE_DESCRIPTION("Dallas/Maxim DS1672 timekeeper driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-ds1685.c b/drivers/rtc/rtc-ds1685.c new file mode 100644 index 000000000..5fc8e36b1 --- /dev/null +++ b/drivers/rtc/rtc-ds1685.c @@ -0,0 +1,1440 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * An rtc driver for the Dallas/Maxim DS1685/DS1687 and related real-time + * chips. + * + * Copyright (C) 2011-2014 Joshua Kinard . + * Copyright (C) 2009 Matthias Fuchs . + * + * References: + * DS1685/DS1687 3V/5V Real-Time Clocks, 19-5215, Rev 4/10. + * DS17x85/DS17x87 3V/5V Real-Time Clocks, 19-5222, Rev 4/10. + * DS1689/DS1693 3V/5V Serialized Real-Time Clocks, Rev 112105. + * Application Note 90, Using the Multiplex Bus RTC Extended Features. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef CONFIG_PROC_FS +#include +#endif + + +/* ----------------------------------------------------------------------- */ +/* + * Standard read/write + * all registers are mapped in CPU address space + */ + +/** + * ds1685_read - read a value from an rtc register. + * @rtc: pointer to the ds1685 rtc structure. + * @reg: the register address to read. + */ +static u8 +ds1685_read(struct ds1685_priv *rtc, int reg) +{ + return readb((u8 __iomem *)rtc->regs + + (reg * rtc->regstep)); +} + +/** + * ds1685_write - write a value to an rtc register. + * @rtc: pointer to the ds1685 rtc structure. + * @reg: the register address to write. + * @value: value to write to the register. + */ +static void +ds1685_write(struct ds1685_priv *rtc, int reg, u8 value) +{ + writeb(value, ((u8 __iomem *)rtc->regs + + (reg * rtc->regstep))); +} +/* ----------------------------------------------------------------------- */ + +/* + * Indirect read/write functions + * access happens via address and data register mapped in CPU address space + */ + +/** + * ds1685_indirect_read - read a value from an rtc register. + * @rtc: pointer to the ds1685 rtc structure. + * @reg: the register address to read. + */ +static u8 +ds1685_indirect_read(struct ds1685_priv *rtc, int reg) +{ + writeb(reg, rtc->regs); + return readb(rtc->data); +} + +/** + * ds1685_indirect_write - write a value to an rtc register. + * @rtc: pointer to the ds1685 rtc structure. + * @reg: the register address to write. + * @value: value to write to the register. + */ +static void +ds1685_indirect_write(struct ds1685_priv *rtc, int reg, u8 value) +{ + writeb(reg, rtc->regs); + writeb(value, rtc->data); +} + +/* ----------------------------------------------------------------------- */ +/* Inlined functions */ + +/** + * ds1685_rtc_bcd2bin - bcd2bin wrapper in case platform doesn't support BCD. + * @rtc: pointer to the ds1685 rtc structure. + * @val: u8 time value to consider converting. + * @bcd_mask: u8 mask value if BCD mode is used. + * @bin_mask: u8 mask value if BIN mode is used. + * + * Returns the value, converted to BIN if originally in BCD and bcd_mode TRUE. + */ +static inline u8 +ds1685_rtc_bcd2bin(struct ds1685_priv *rtc, u8 val, u8 bcd_mask, u8 bin_mask) +{ + if (rtc->bcd_mode) + return (bcd2bin(val) & bcd_mask); + + return (val & bin_mask); +} + +/** + * ds1685_rtc_bin2bcd - bin2bcd wrapper in case platform doesn't support BCD. + * @rtc: pointer to the ds1685 rtc structure. + * @val: u8 time value to consider converting. + * @bin_mask: u8 mask value if BIN mode is used. + * @bcd_mask: u8 mask value if BCD mode is used. + * + * Returns the value, converted to BCD if originally in BIN and bcd_mode TRUE. + */ +static inline u8 +ds1685_rtc_bin2bcd(struct ds1685_priv *rtc, u8 val, u8 bin_mask, u8 bcd_mask) +{ + if (rtc->bcd_mode) + return (bin2bcd(val) & bcd_mask); + + return (val & bin_mask); +} + +/** + * ds1685_rtc_check_mday - check validity of the day of month. + * @rtc: pointer to the ds1685 rtc structure. + * @mday: day of month. + * + * Returns -EDOM if the day of month is not within 1..31 range. + */ +static inline int +ds1685_rtc_check_mday(struct ds1685_priv *rtc, u8 mday) +{ + if (rtc->bcd_mode) { + if (mday < 0x01 || mday > 0x31 || (mday & 0x0f) > 0x09) + return -EDOM; + } else { + if (mday < 1 || mday > 31) + return -EDOM; + } + return 0; +} + +/** + * ds1685_rtc_switch_to_bank0 - switch the rtc to bank 0. + * @rtc: pointer to the ds1685 rtc structure. + */ +static inline void +ds1685_rtc_switch_to_bank0(struct ds1685_priv *rtc) +{ + rtc->write(rtc, RTC_CTRL_A, + (rtc->read(rtc, RTC_CTRL_A) & ~(RTC_CTRL_A_DV0))); +} + +/** + * ds1685_rtc_switch_to_bank1 - switch the rtc to bank 1. + * @rtc: pointer to the ds1685 rtc structure. + */ +static inline void +ds1685_rtc_switch_to_bank1(struct ds1685_priv *rtc) +{ + rtc->write(rtc, RTC_CTRL_A, + (rtc->read(rtc, RTC_CTRL_A) | RTC_CTRL_A_DV0)); +} + +/** + * ds1685_rtc_begin_data_access - prepare the rtc for data access. + * @rtc: pointer to the ds1685 rtc structure. + * + * This takes several steps to prepare the rtc for access to get/set time + * and alarm values from the rtc registers: + * - Sets the SET bit in Control Register B. + * - Reads Ext Control Register 4A and checks the INCR bit. + * - If INCR is active, a short delay is added before Ext Control Register 4A + * is read again in a loop until INCR is inactive. + * - Switches the rtc to bank 1. This allows access to all relevant + * data for normal rtc operation, as bank 0 contains only the nvram. + */ +static inline void +ds1685_rtc_begin_data_access(struct ds1685_priv *rtc) +{ + /* Set the SET bit in Ctrl B */ + rtc->write(rtc, RTC_CTRL_B, + (rtc->read(rtc, RTC_CTRL_B) | RTC_CTRL_B_SET)); + + /* Switch to Bank 1 */ + ds1685_rtc_switch_to_bank1(rtc); + + /* Read Ext Ctrl 4A and check the INCR bit to avoid a lockout. */ + while (rtc->read(rtc, RTC_EXT_CTRL_4A) & RTC_CTRL_4A_INCR) + cpu_relax(); +} + +/** + * ds1685_rtc_end_data_access - end data access on the rtc. + * @rtc: pointer to the ds1685 rtc structure. + * + * This ends what was started by ds1685_rtc_begin_data_access: + * - Switches the rtc back to bank 0. + * - Clears the SET bit in Control Register B. + */ +static inline void +ds1685_rtc_end_data_access(struct ds1685_priv *rtc) +{ + /* Switch back to Bank 0 */ + ds1685_rtc_switch_to_bank0(rtc); + + /* Clear the SET bit in Ctrl B */ + rtc->write(rtc, RTC_CTRL_B, + (rtc->read(rtc, RTC_CTRL_B) & ~(RTC_CTRL_B_SET))); +} + +/** + * ds1685_rtc_get_ssn - retrieve the silicon serial number. + * @rtc: pointer to the ds1685 rtc structure. + * @ssn: u8 array to hold the bits of the silicon serial number. + * + * This number starts at 0x40, and is 8-bytes long, ending at 0x47. The + * first byte is the model number, the next six bytes are the serial number + * digits, and the final byte is a CRC check byte. Together, they form the + * silicon serial number. + * + * These values are stored in bank1, so ds1685_rtc_switch_to_bank1 must be + * called first before calling this function, else data will be read out of + * the bank0 NVRAM. Be sure to call ds1685_rtc_switch_to_bank0 when done. + */ +static inline void +ds1685_rtc_get_ssn(struct ds1685_priv *rtc, u8 *ssn) +{ + ssn[0] = rtc->read(rtc, RTC_BANK1_SSN_MODEL); + ssn[1] = rtc->read(rtc, RTC_BANK1_SSN_BYTE_1); + ssn[2] = rtc->read(rtc, RTC_BANK1_SSN_BYTE_2); + ssn[3] = rtc->read(rtc, RTC_BANK1_SSN_BYTE_3); + ssn[4] = rtc->read(rtc, RTC_BANK1_SSN_BYTE_4); + ssn[5] = rtc->read(rtc, RTC_BANK1_SSN_BYTE_5); + ssn[6] = rtc->read(rtc, RTC_BANK1_SSN_BYTE_6); + ssn[7] = rtc->read(rtc, RTC_BANK1_SSN_CRC); +} +/* ----------------------------------------------------------------------- */ + + +/* ----------------------------------------------------------------------- */ +/* Read/Set Time & Alarm functions */ + +/** + * ds1685_rtc_read_time - reads the time registers. + * @dev: pointer to device structure. + * @tm: pointer to rtc_time structure. + */ +static int +ds1685_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct ds1685_priv *rtc = dev_get_drvdata(dev); + u8 century; + u8 seconds, minutes, hours, wday, mday, month, years; + + /* Fetch the time info from the RTC registers. */ + ds1685_rtc_begin_data_access(rtc); + seconds = rtc->read(rtc, RTC_SECS); + minutes = rtc->read(rtc, RTC_MINS); + hours = rtc->read(rtc, RTC_HRS); + wday = rtc->read(rtc, RTC_WDAY); + mday = rtc->read(rtc, RTC_MDAY); + month = rtc->read(rtc, RTC_MONTH); + years = rtc->read(rtc, RTC_YEAR); + century = rtc->read(rtc, RTC_CENTURY); + ds1685_rtc_end_data_access(rtc); + + /* bcd2bin if needed, perform fixups, and store to rtc_time. */ + years = ds1685_rtc_bcd2bin(rtc, years, RTC_YEAR_BCD_MASK, + RTC_YEAR_BIN_MASK); + century = ds1685_rtc_bcd2bin(rtc, century, RTC_CENTURY_MASK, + RTC_CENTURY_MASK); + tm->tm_sec = ds1685_rtc_bcd2bin(rtc, seconds, RTC_SECS_BCD_MASK, + RTC_SECS_BIN_MASK); + tm->tm_min = ds1685_rtc_bcd2bin(rtc, minutes, RTC_MINS_BCD_MASK, + RTC_MINS_BIN_MASK); + tm->tm_hour = ds1685_rtc_bcd2bin(rtc, hours, RTC_HRS_24_BCD_MASK, + RTC_HRS_24_BIN_MASK); + tm->tm_wday = (ds1685_rtc_bcd2bin(rtc, wday, RTC_WDAY_MASK, + RTC_WDAY_MASK) - 1); + tm->tm_mday = ds1685_rtc_bcd2bin(rtc, mday, RTC_MDAY_BCD_MASK, + RTC_MDAY_BIN_MASK); + tm->tm_mon = (ds1685_rtc_bcd2bin(rtc, month, RTC_MONTH_BCD_MASK, + RTC_MONTH_BIN_MASK) - 1); + tm->tm_year = ((years + (century * 100)) - 1900); + tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year); + tm->tm_isdst = 0; /* RTC has hardcoded timezone, so don't use. */ + + return 0; +} + +/** + * ds1685_rtc_set_time - sets the time registers. + * @dev: pointer to device structure. + * @tm: pointer to rtc_time structure. + */ +static int +ds1685_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct ds1685_priv *rtc = dev_get_drvdata(dev); + u8 ctrlb, seconds, minutes, hours, wday, mday, month, years, century; + + /* Fetch the time info from rtc_time. */ + seconds = ds1685_rtc_bin2bcd(rtc, tm->tm_sec, RTC_SECS_BIN_MASK, + RTC_SECS_BCD_MASK); + minutes = ds1685_rtc_bin2bcd(rtc, tm->tm_min, RTC_MINS_BIN_MASK, + RTC_MINS_BCD_MASK); + hours = ds1685_rtc_bin2bcd(rtc, tm->tm_hour, RTC_HRS_24_BIN_MASK, + RTC_HRS_24_BCD_MASK); + wday = ds1685_rtc_bin2bcd(rtc, (tm->tm_wday + 1), RTC_WDAY_MASK, + RTC_WDAY_MASK); + mday = ds1685_rtc_bin2bcd(rtc, tm->tm_mday, RTC_MDAY_BIN_MASK, + RTC_MDAY_BCD_MASK); + month = ds1685_rtc_bin2bcd(rtc, (tm->tm_mon + 1), RTC_MONTH_BIN_MASK, + RTC_MONTH_BCD_MASK); + years = ds1685_rtc_bin2bcd(rtc, (tm->tm_year % 100), + RTC_YEAR_BIN_MASK, RTC_YEAR_BCD_MASK); + century = ds1685_rtc_bin2bcd(rtc, ((tm->tm_year + 1900) / 100), + RTC_CENTURY_MASK, RTC_CENTURY_MASK); + + /* + * Perform Sanity Checks: + * - Months: !> 12, Month Day != 0. + * - Month Day !> Max days in current month. + * - Hours !>= 24, Mins !>= 60, Secs !>= 60, & Weekday !> 7. + */ + if ((tm->tm_mon > 11) || (mday == 0)) + return -EDOM; + + if (tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year)) + return -EDOM; + + if ((tm->tm_hour >= 24) || (tm->tm_min >= 60) || + (tm->tm_sec >= 60) || (wday > 7)) + return -EDOM; + + /* + * Set the data mode to use and store the time values in the + * RTC registers. + */ + ds1685_rtc_begin_data_access(rtc); + ctrlb = rtc->read(rtc, RTC_CTRL_B); + if (rtc->bcd_mode) + ctrlb &= ~(RTC_CTRL_B_DM); + else + ctrlb |= RTC_CTRL_B_DM; + rtc->write(rtc, RTC_CTRL_B, ctrlb); + rtc->write(rtc, RTC_SECS, seconds); + rtc->write(rtc, RTC_MINS, minutes); + rtc->write(rtc, RTC_HRS, hours); + rtc->write(rtc, RTC_WDAY, wday); + rtc->write(rtc, RTC_MDAY, mday); + rtc->write(rtc, RTC_MONTH, month); + rtc->write(rtc, RTC_YEAR, years); + rtc->write(rtc, RTC_CENTURY, century); + ds1685_rtc_end_data_access(rtc); + + return 0; +} + +/** + * ds1685_rtc_read_alarm - reads the alarm registers. + * @dev: pointer to device structure. + * @alrm: pointer to rtc_wkalrm structure. + * + * There are three primary alarm registers: seconds, minutes, and hours. + * A fourth alarm register for the month date is also available in bank1 for + * kickstart/wakeup features. The DS1685/DS1687 manual states that a + * "don't care" value ranging from 0xc0 to 0xff may be written into one or + * more of the three alarm bytes to act as a wildcard value. The fourth + * byte doesn't support a "don't care" value. + */ +static int +ds1685_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct ds1685_priv *rtc = dev_get_drvdata(dev); + u8 seconds, minutes, hours, mday, ctrlb, ctrlc; + int ret; + + /* Fetch the alarm info from the RTC alarm registers. */ + ds1685_rtc_begin_data_access(rtc); + seconds = rtc->read(rtc, RTC_SECS_ALARM); + minutes = rtc->read(rtc, RTC_MINS_ALARM); + hours = rtc->read(rtc, RTC_HRS_ALARM); + mday = rtc->read(rtc, RTC_MDAY_ALARM); + ctrlb = rtc->read(rtc, RTC_CTRL_B); + ctrlc = rtc->read(rtc, RTC_CTRL_C); + ds1685_rtc_end_data_access(rtc); + + /* Check the month date for validity. */ + ret = ds1685_rtc_check_mday(rtc, mday); + if (ret) + return ret; + + /* + * Check the three alarm bytes. + * + * The Linux RTC system doesn't support the "don't care" capability + * of this RTC chip. We check for it anyways in case support is + * added in the future and only assign when we care. + */ + if (likely(seconds < 0xc0)) + alrm->time.tm_sec = ds1685_rtc_bcd2bin(rtc, seconds, + RTC_SECS_BCD_MASK, + RTC_SECS_BIN_MASK); + + if (likely(minutes < 0xc0)) + alrm->time.tm_min = ds1685_rtc_bcd2bin(rtc, minutes, + RTC_MINS_BCD_MASK, + RTC_MINS_BIN_MASK); + + if (likely(hours < 0xc0)) + alrm->time.tm_hour = ds1685_rtc_bcd2bin(rtc, hours, + RTC_HRS_24_BCD_MASK, + RTC_HRS_24_BIN_MASK); + + /* Write the data to rtc_wkalrm. */ + alrm->time.tm_mday = ds1685_rtc_bcd2bin(rtc, mday, RTC_MDAY_BCD_MASK, + RTC_MDAY_BIN_MASK); + alrm->enabled = !!(ctrlb & RTC_CTRL_B_AIE); + alrm->pending = !!(ctrlc & RTC_CTRL_C_AF); + + return 0; +} + +/** + * ds1685_rtc_set_alarm - sets the alarm in registers. + * @dev: pointer to device structure. + * @alrm: pointer to rtc_wkalrm structure. + */ +static int +ds1685_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct ds1685_priv *rtc = dev_get_drvdata(dev); + u8 ctrlb, seconds, minutes, hours, mday; + int ret; + + /* Fetch the alarm info and convert to BCD. */ + seconds = ds1685_rtc_bin2bcd(rtc, alrm->time.tm_sec, + RTC_SECS_BIN_MASK, + RTC_SECS_BCD_MASK); + minutes = ds1685_rtc_bin2bcd(rtc, alrm->time.tm_min, + RTC_MINS_BIN_MASK, + RTC_MINS_BCD_MASK); + hours = ds1685_rtc_bin2bcd(rtc, alrm->time.tm_hour, + RTC_HRS_24_BIN_MASK, + RTC_HRS_24_BCD_MASK); + mday = ds1685_rtc_bin2bcd(rtc, alrm->time.tm_mday, + RTC_MDAY_BIN_MASK, + RTC_MDAY_BCD_MASK); + + /* Check the month date for validity. */ + ret = ds1685_rtc_check_mday(rtc, mday); + if (ret) + return ret; + + /* + * Check the three alarm bytes. + * + * The Linux RTC system doesn't support the "don't care" capability + * of this RTC chip because rtc_valid_tm tries to validate every + * field, and we only support four fields. We put the support + * here anyways for the future. + */ + if (unlikely(seconds >= 0xc0)) + seconds = 0xff; + + if (unlikely(minutes >= 0xc0)) + minutes = 0xff; + + if (unlikely(hours >= 0xc0)) + hours = 0xff; + + alrm->time.tm_mon = -1; + alrm->time.tm_year = -1; + alrm->time.tm_wday = -1; + alrm->time.tm_yday = -1; + alrm->time.tm_isdst = -1; + + /* Disable the alarm interrupt first. */ + ds1685_rtc_begin_data_access(rtc); + ctrlb = rtc->read(rtc, RTC_CTRL_B); + rtc->write(rtc, RTC_CTRL_B, (ctrlb & ~(RTC_CTRL_B_AIE))); + + /* Read ctrlc to clear RTC_CTRL_C_AF. */ + rtc->read(rtc, RTC_CTRL_C); + + /* + * Set the data mode to use and store the time values in the + * RTC registers. + */ + ctrlb = rtc->read(rtc, RTC_CTRL_B); + if (rtc->bcd_mode) + ctrlb &= ~(RTC_CTRL_B_DM); + else + ctrlb |= RTC_CTRL_B_DM; + rtc->write(rtc, RTC_CTRL_B, ctrlb); + rtc->write(rtc, RTC_SECS_ALARM, seconds); + rtc->write(rtc, RTC_MINS_ALARM, minutes); + rtc->write(rtc, RTC_HRS_ALARM, hours); + rtc->write(rtc, RTC_MDAY_ALARM, mday); + + /* Re-enable the alarm if needed. */ + if (alrm->enabled) { + ctrlb = rtc->read(rtc, RTC_CTRL_B); + ctrlb |= RTC_CTRL_B_AIE; + rtc->write(rtc, RTC_CTRL_B, ctrlb); + } + + /* Done! */ + ds1685_rtc_end_data_access(rtc); + + return 0; +} +/* ----------------------------------------------------------------------- */ + + +/* ----------------------------------------------------------------------- */ +/* /dev/rtcX Interface functions */ + +/** + * ds1685_rtc_alarm_irq_enable - replaces ioctl() RTC_AIE on/off. + * @dev: pointer to device structure. + * @enabled: flag indicating whether to enable or disable. + */ +static int +ds1685_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct ds1685_priv *rtc = dev_get_drvdata(dev); + + /* Flip the requisite interrupt-enable bit. */ + if (enabled) + rtc->write(rtc, RTC_CTRL_B, (rtc->read(rtc, RTC_CTRL_B) | + RTC_CTRL_B_AIE)); + else + rtc->write(rtc, RTC_CTRL_B, (rtc->read(rtc, RTC_CTRL_B) & + ~(RTC_CTRL_B_AIE))); + + /* Read Control C to clear all the flag bits. */ + rtc->read(rtc, RTC_CTRL_C); + + return 0; +} +/* ----------------------------------------------------------------------- */ + + +/* ----------------------------------------------------------------------- */ +/* IRQ handler */ + +/** + * ds1685_rtc_extended_irq - take care of extended interrupts + * @rtc: pointer to the ds1685 rtc structure. + * @pdev: platform device pointer. + */ +static void +ds1685_rtc_extended_irq(struct ds1685_priv *rtc, struct platform_device *pdev) +{ + u8 ctrl4a, ctrl4b; + + ds1685_rtc_switch_to_bank1(rtc); + ctrl4a = rtc->read(rtc, RTC_EXT_CTRL_4A); + ctrl4b = rtc->read(rtc, RTC_EXT_CTRL_4B); + + /* + * Check for a kickstart interrupt. With Vcc applied, this + * typically means that the power button was pressed, so we + * begin the shutdown sequence. + */ + if ((ctrl4b & RTC_CTRL_4B_KSE) && (ctrl4a & RTC_CTRL_4A_KF)) { + /* Briefly disable kickstarts to debounce button presses. */ + rtc->write(rtc, RTC_EXT_CTRL_4B, + (rtc->read(rtc, RTC_EXT_CTRL_4B) & + ~(RTC_CTRL_4B_KSE))); + + /* Clear the kickstart flag. */ + rtc->write(rtc, RTC_EXT_CTRL_4A, + (ctrl4a & ~(RTC_CTRL_4A_KF))); + + + /* + * Sleep 500ms before re-enabling kickstarts. This allows + * adequate time to avoid reading signal jitter as additional + * button presses. + */ + msleep(500); + rtc->write(rtc, RTC_EXT_CTRL_4B, + (rtc->read(rtc, RTC_EXT_CTRL_4B) | + RTC_CTRL_4B_KSE)); + + /* Call the platform pre-poweroff function. Else, shutdown. */ + if (rtc->prepare_poweroff != NULL) + rtc->prepare_poweroff(); + else + ds1685_rtc_poweroff(pdev); + } + + /* + * Check for a wake-up interrupt. With Vcc applied, this is + * essentially a second alarm interrupt, except it takes into + * account the 'date' register in bank1 in addition to the + * standard three alarm registers. + */ + if ((ctrl4b & RTC_CTRL_4B_WIE) && (ctrl4a & RTC_CTRL_4A_WF)) { + rtc->write(rtc, RTC_EXT_CTRL_4A, + (ctrl4a & ~(RTC_CTRL_4A_WF))); + + /* Call the platform wake_alarm function if defined. */ + if (rtc->wake_alarm != NULL) + rtc->wake_alarm(); + else + dev_warn(&pdev->dev, + "Wake Alarm IRQ just occurred!\n"); + } + + /* + * Check for a ram-clear interrupt. This happens if RIE=1 and RF=0 + * when RCE=1 in 4B. This clears all NVRAM bytes in bank0 by setting + * each byte to a logic 1. This has no effect on any extended + * NV-SRAM that might be present, nor on the time/calendar/alarm + * registers. After a ram-clear is completed, there is a minimum + * recovery time of ~150ms in which all reads/writes are locked out. + * NOTE: A ram-clear can still occur if RCE=1 and RIE=0. We cannot + * catch this scenario. + */ + if ((ctrl4b & RTC_CTRL_4B_RIE) && (ctrl4a & RTC_CTRL_4A_RF)) { + rtc->write(rtc, RTC_EXT_CTRL_4A, + (ctrl4a & ~(RTC_CTRL_4A_RF))); + msleep(150); + + /* Call the platform post_ram_clear function if defined. */ + if (rtc->post_ram_clear != NULL) + rtc->post_ram_clear(); + else + dev_warn(&pdev->dev, + "RAM-Clear IRQ just occurred!\n"); + } + ds1685_rtc_switch_to_bank0(rtc); +} + +/** + * ds1685_rtc_irq_handler - IRQ handler. + * @irq: IRQ number. + * @dev_id: platform device pointer. + */ +static irqreturn_t +ds1685_rtc_irq_handler(int irq, void *dev_id) +{ + struct platform_device *pdev = dev_id; + struct ds1685_priv *rtc = platform_get_drvdata(pdev); + u8 ctrlb, ctrlc; + unsigned long events = 0; + u8 num_irqs = 0; + + /* Abort early if the device isn't ready yet (i.e., DEBUG_SHIRQ). */ + if (unlikely(!rtc)) + return IRQ_HANDLED; + + rtc_lock(rtc->dev); + + /* Ctrlb holds the interrupt-enable bits and ctrlc the flag bits. */ + ctrlb = rtc->read(rtc, RTC_CTRL_B); + ctrlc = rtc->read(rtc, RTC_CTRL_C); + + /* Is the IRQF bit set? */ + if (likely(ctrlc & RTC_CTRL_C_IRQF)) { + /* + * We need to determine if it was one of the standard + * events: PF, AF, or UF. If so, we handle them and + * update the RTC core. + */ + if (likely(ctrlc & RTC_CTRL_B_PAU_MASK)) { + events = RTC_IRQF; + + /* Check for a periodic interrupt. */ + if ((ctrlb & RTC_CTRL_B_PIE) && + (ctrlc & RTC_CTRL_C_PF)) { + events |= RTC_PF; + num_irqs++; + } + + /* Check for an alarm interrupt. */ + if ((ctrlb & RTC_CTRL_B_AIE) && + (ctrlc & RTC_CTRL_C_AF)) { + events |= RTC_AF; + num_irqs++; + } + + /* Check for an update interrupt. */ + if ((ctrlb & RTC_CTRL_B_UIE) && + (ctrlc & RTC_CTRL_C_UF)) { + events |= RTC_UF; + num_irqs++; + } + } else { + /* + * One of the "extended" interrupts was received that + * is not recognized by the RTC core. + */ + ds1685_rtc_extended_irq(rtc, pdev); + } + } + rtc_update_irq(rtc->dev, num_irqs, events); + rtc_unlock(rtc->dev); + + return events ? IRQ_HANDLED : IRQ_NONE; +} +/* ----------------------------------------------------------------------- */ + + +/* ----------------------------------------------------------------------- */ +/* ProcFS interface */ + +#ifdef CONFIG_PROC_FS +#define NUM_REGS 6 /* Num of control registers. */ +#define NUM_BITS 8 /* Num bits per register. */ +#define NUM_SPACES 4 /* Num spaces between each bit. */ + +/* + * Periodic Interrupt Rates. + */ +static const char *ds1685_rtc_pirq_rate[16] = { + "none", "3.90625ms", "7.8125ms", "0.122070ms", "0.244141ms", + "0.488281ms", "0.9765625ms", "1.953125ms", "3.90625ms", "7.8125ms", + "15.625ms", "31.25ms", "62.5ms", "125ms", "250ms", "500ms" +}; + +/* + * Square-Wave Output Frequencies. + */ +static const char *ds1685_rtc_sqw_freq[16] = { + "none", "256Hz", "128Hz", "8192Hz", "4096Hz", "2048Hz", "1024Hz", + "512Hz", "256Hz", "128Hz", "64Hz", "32Hz", "16Hz", "8Hz", "4Hz", "2Hz" +}; + +/** + * ds1685_rtc_proc - procfs access function. + * @dev: pointer to device structure. + * @seq: pointer to seq_file structure. + */ +static int +ds1685_rtc_proc(struct device *dev, struct seq_file *seq) +{ + struct ds1685_priv *rtc = dev_get_drvdata(dev); + u8 ctrla, ctrlb, ctrld, ctrl4a, ctrl4b, ssn[8]; + char *model; + + /* Read all the relevant data from the control registers. */ + ds1685_rtc_switch_to_bank1(rtc); + ds1685_rtc_get_ssn(rtc, ssn); + ctrla = rtc->read(rtc, RTC_CTRL_A); + ctrlb = rtc->read(rtc, RTC_CTRL_B); + ctrld = rtc->read(rtc, RTC_CTRL_D); + ctrl4a = rtc->read(rtc, RTC_EXT_CTRL_4A); + ctrl4b = rtc->read(rtc, RTC_EXT_CTRL_4B); + ds1685_rtc_switch_to_bank0(rtc); + + /* Determine the RTC model. */ + switch (ssn[0]) { + case RTC_MODEL_DS1685: + model = "DS1685/DS1687\0"; + break; + case RTC_MODEL_DS1689: + model = "DS1689/DS1693\0"; + break; + case RTC_MODEL_DS17285: + model = "DS17285/DS17287\0"; + break; + case RTC_MODEL_DS17485: + model = "DS17485/DS17487\0"; + break; + case RTC_MODEL_DS17885: + model = "DS17885/DS17887\0"; + break; + default: + model = "Unknown\0"; + break; + } + + /* Print out the information. */ + seq_printf(seq, + "Model\t\t: %s\n" + "Oscillator\t: %s\n" + "12/24hr\t\t: %s\n" + "DST\t\t: %s\n" + "Data mode\t: %s\n" + "Battery\t\t: %s\n" + "Aux batt\t: %s\n" + "Update IRQ\t: %s\n" + "Periodic IRQ\t: %s\n" + "Periodic Rate\t: %s\n" + "SQW Freq\t: %s\n" + "Serial #\t: %8phC\n", + model, + ((ctrla & RTC_CTRL_A_DV1) ? "enabled" : "disabled"), + ((ctrlb & RTC_CTRL_B_2412) ? "24-hour" : "12-hour"), + ((ctrlb & RTC_CTRL_B_DSE) ? "enabled" : "disabled"), + ((ctrlb & RTC_CTRL_B_DM) ? "binary" : "BCD"), + ((ctrld & RTC_CTRL_D_VRT) ? "ok" : "exhausted or n/a"), + ((ctrl4a & RTC_CTRL_4A_VRT2) ? "ok" : "exhausted or n/a"), + ((ctrlb & RTC_CTRL_B_UIE) ? "yes" : "no"), + ((ctrlb & RTC_CTRL_B_PIE) ? "yes" : "no"), + (!(ctrl4b & RTC_CTRL_4B_E32K) ? + ds1685_rtc_pirq_rate[(ctrla & RTC_CTRL_A_RS_MASK)] : "none"), + (!((ctrl4b & RTC_CTRL_4B_E32K)) ? + ds1685_rtc_sqw_freq[(ctrla & RTC_CTRL_A_RS_MASK)] : "32768Hz"), + ssn); + return 0; +} +#else +#define ds1685_rtc_proc NULL +#endif /* CONFIG_PROC_FS */ +/* ----------------------------------------------------------------------- */ + + +/* ----------------------------------------------------------------------- */ +/* RTC Class operations */ + +static const struct rtc_class_ops +ds1685_rtc_ops = { + .proc = ds1685_rtc_proc, + .read_time = ds1685_rtc_read_time, + .set_time = ds1685_rtc_set_time, + .read_alarm = ds1685_rtc_read_alarm, + .set_alarm = ds1685_rtc_set_alarm, + .alarm_irq_enable = ds1685_rtc_alarm_irq_enable, +}; +/* ----------------------------------------------------------------------- */ + +static int ds1685_nvram_read(void *priv, unsigned int pos, void *val, + size_t size) +{ + struct ds1685_priv *rtc = priv; + struct mutex *rtc_mutex = &rtc->dev->ops_lock; + ssize_t count; + u8 *buf = val; + int err; + + err = mutex_lock_interruptible(rtc_mutex); + if (err) + return err; + + ds1685_rtc_switch_to_bank0(rtc); + + /* Read NVRAM in time and bank0 registers. */ + for (count = 0; size > 0 && pos < NVRAM_TOTAL_SZ_BANK0; + count++, size--) { + if (count < NVRAM_SZ_TIME) + *buf++ = rtc->read(rtc, (NVRAM_TIME_BASE + pos++)); + else + *buf++ = rtc->read(rtc, (NVRAM_BANK0_BASE + pos++)); + } + +#ifndef CONFIG_RTC_DRV_DS1689 + if (size > 0) { + ds1685_rtc_switch_to_bank1(rtc); + +#ifndef CONFIG_RTC_DRV_DS1685 + /* Enable burst-mode on DS17x85/DS17x87 */ + rtc->write(rtc, RTC_EXT_CTRL_4A, + (rtc->read(rtc, RTC_EXT_CTRL_4A) | + RTC_CTRL_4A_BME)); + + /* We need one write to RTC_BANK1_RAM_ADDR_LSB to start + * reading with burst-mode */ + rtc->write(rtc, RTC_BANK1_RAM_ADDR_LSB, + (pos - NVRAM_TOTAL_SZ_BANK0)); +#endif + + /* Read NVRAM in bank1 registers. */ + for (count = 0; size > 0 && pos < NVRAM_TOTAL_SZ; + count++, size--) { +#ifdef CONFIG_RTC_DRV_DS1685 + /* DS1685/DS1687 has to write to RTC_BANK1_RAM_ADDR + * before each read. */ + rtc->write(rtc, RTC_BANK1_RAM_ADDR, + (pos - NVRAM_TOTAL_SZ_BANK0)); +#endif + *buf++ = rtc->read(rtc, RTC_BANK1_RAM_DATA_PORT); + pos++; + } + +#ifndef CONFIG_RTC_DRV_DS1685 + /* Disable burst-mode on DS17x85/DS17x87 */ + rtc->write(rtc, RTC_EXT_CTRL_4A, + (rtc->read(rtc, RTC_EXT_CTRL_4A) & + ~(RTC_CTRL_4A_BME))); +#endif + ds1685_rtc_switch_to_bank0(rtc); + } +#endif /* !CONFIG_RTC_DRV_DS1689 */ + mutex_unlock(rtc_mutex); + + return 0; +} + +static int ds1685_nvram_write(void *priv, unsigned int pos, void *val, + size_t size) +{ + struct ds1685_priv *rtc = priv; + struct mutex *rtc_mutex = &rtc->dev->ops_lock; + ssize_t count; + u8 *buf = val; + int err; + + err = mutex_lock_interruptible(rtc_mutex); + if (err) + return err; + + ds1685_rtc_switch_to_bank0(rtc); + + /* Write NVRAM in time and bank0 registers. */ + for (count = 0; size > 0 && pos < NVRAM_TOTAL_SZ_BANK0; + count++, size--) + if (count < NVRAM_SZ_TIME) + rtc->write(rtc, (NVRAM_TIME_BASE + pos++), + *buf++); + else + rtc->write(rtc, (NVRAM_BANK0_BASE), *buf++); + +#ifndef CONFIG_RTC_DRV_DS1689 + if (size > 0) { + ds1685_rtc_switch_to_bank1(rtc); + +#ifndef CONFIG_RTC_DRV_DS1685 + /* Enable burst-mode on DS17x85/DS17x87 */ + rtc->write(rtc, RTC_EXT_CTRL_4A, + (rtc->read(rtc, RTC_EXT_CTRL_4A) | + RTC_CTRL_4A_BME)); + + /* We need one write to RTC_BANK1_RAM_ADDR_LSB to start + * writing with burst-mode */ + rtc->write(rtc, RTC_BANK1_RAM_ADDR_LSB, + (pos - NVRAM_TOTAL_SZ_BANK0)); +#endif + + /* Write NVRAM in bank1 registers. */ + for (count = 0; size > 0 && pos < NVRAM_TOTAL_SZ; + count++, size--) { +#ifdef CONFIG_RTC_DRV_DS1685 + /* DS1685/DS1687 has to write to RTC_BANK1_RAM_ADDR + * before each read. */ + rtc->write(rtc, RTC_BANK1_RAM_ADDR, + (pos - NVRAM_TOTAL_SZ_BANK0)); +#endif + rtc->write(rtc, RTC_BANK1_RAM_DATA_PORT, *buf++); + pos++; + } + +#ifndef CONFIG_RTC_DRV_DS1685 + /* Disable burst-mode on DS17x85/DS17x87 */ + rtc->write(rtc, RTC_EXT_CTRL_4A, + (rtc->read(rtc, RTC_EXT_CTRL_4A) & + ~(RTC_CTRL_4A_BME))); +#endif + ds1685_rtc_switch_to_bank0(rtc); + } +#endif /* !CONFIG_RTC_DRV_DS1689 */ + mutex_unlock(rtc_mutex); + + return 0; +} + +/* ----------------------------------------------------------------------- */ +/* SysFS interface */ + +/** + * ds1685_rtc_sysfs_battery_show - sysfs file for main battery status. + * @dev: pointer to device structure. + * @attr: pointer to device_attribute structure. + * @buf: pointer to char array to hold the output. + */ +static ssize_t +ds1685_rtc_sysfs_battery_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ds1685_priv *rtc = dev_get_drvdata(dev->parent); + u8 ctrld; + + ctrld = rtc->read(rtc, RTC_CTRL_D); + + return sprintf(buf, "%s\n", + (ctrld & RTC_CTRL_D_VRT) ? "ok" : "not ok or N/A"); +} +static DEVICE_ATTR(battery, S_IRUGO, ds1685_rtc_sysfs_battery_show, NULL); + +/** + * ds1685_rtc_sysfs_auxbatt_show - sysfs file for aux battery status. + * @dev: pointer to device structure. + * @attr: pointer to device_attribute structure. + * @buf: pointer to char array to hold the output. + */ +static ssize_t +ds1685_rtc_sysfs_auxbatt_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ds1685_priv *rtc = dev_get_drvdata(dev->parent); + u8 ctrl4a; + + ds1685_rtc_switch_to_bank1(rtc); + ctrl4a = rtc->read(rtc, RTC_EXT_CTRL_4A); + ds1685_rtc_switch_to_bank0(rtc); + + return sprintf(buf, "%s\n", + (ctrl4a & RTC_CTRL_4A_VRT2) ? "ok" : "not ok or N/A"); +} +static DEVICE_ATTR(auxbatt, S_IRUGO, ds1685_rtc_sysfs_auxbatt_show, NULL); + +/** + * ds1685_rtc_sysfs_serial_show - sysfs file for silicon serial number. + * @dev: pointer to device structure. + * @attr: pointer to device_attribute structure. + * @buf: pointer to char array to hold the output. + */ +static ssize_t +ds1685_rtc_sysfs_serial_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ds1685_priv *rtc = dev_get_drvdata(dev->parent); + u8 ssn[8]; + + ds1685_rtc_switch_to_bank1(rtc); + ds1685_rtc_get_ssn(rtc, ssn); + ds1685_rtc_switch_to_bank0(rtc); + + return sprintf(buf, "%8phC\n", ssn); +} +static DEVICE_ATTR(serial, S_IRUGO, ds1685_rtc_sysfs_serial_show, NULL); + +/* + * struct ds1685_rtc_sysfs_misc_attrs - list for misc RTC features. + */ +static struct attribute* +ds1685_rtc_sysfs_misc_attrs[] = { + &dev_attr_battery.attr, + &dev_attr_auxbatt.attr, + &dev_attr_serial.attr, + NULL, +}; + +/* + * struct ds1685_rtc_sysfs_misc_grp - attr group for misc RTC features. + */ +static const struct attribute_group +ds1685_rtc_sysfs_misc_grp = { + .name = "misc", + .attrs = ds1685_rtc_sysfs_misc_attrs, +}; + +/* ----------------------------------------------------------------------- */ +/* Driver Probe/Removal */ + +/** + * ds1685_rtc_probe - initializes rtc driver. + * @pdev: pointer to platform_device structure. + */ +static int +ds1685_rtc_probe(struct platform_device *pdev) +{ + struct rtc_device *rtc_dev; + struct ds1685_priv *rtc; + struct ds1685_rtc_platform_data *pdata; + u8 ctrla, ctrlb, hours; + unsigned char am_pm; + int ret = 0; + struct nvmem_config nvmem_cfg = { + .name = "ds1685_nvram", + .size = NVRAM_TOTAL_SZ, + .reg_read = ds1685_nvram_read, + .reg_write = ds1685_nvram_write, + }; + + /* Get the platform data. */ + pdata = (struct ds1685_rtc_platform_data *) pdev->dev.platform_data; + if (!pdata) + return -ENODEV; + + /* Allocate memory for the rtc device. */ + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + /* Setup resources and access functions */ + switch (pdata->access_type) { + case ds1685_reg_direct: + rtc->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rtc->regs)) + return PTR_ERR(rtc->regs); + rtc->read = ds1685_read; + rtc->write = ds1685_write; + break; + case ds1685_reg_indirect: + rtc->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rtc->regs)) + return PTR_ERR(rtc->regs); + rtc->data = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(rtc->data)) + return PTR_ERR(rtc->data); + rtc->read = ds1685_indirect_read; + rtc->write = ds1685_indirect_write; + break; + } + + if (!rtc->read || !rtc->write) + return -ENXIO; + + /* Get the register step size. */ + if (pdata->regstep > 0) + rtc->regstep = pdata->regstep; + else + rtc->regstep = 1; + + /* Platform pre-shutdown function, if defined. */ + if (pdata->plat_prepare_poweroff) + rtc->prepare_poweroff = pdata->plat_prepare_poweroff; + + /* Platform wake_alarm function, if defined. */ + if (pdata->plat_wake_alarm) + rtc->wake_alarm = pdata->plat_wake_alarm; + + /* Platform post_ram_clear function, if defined. */ + if (pdata->plat_post_ram_clear) + rtc->post_ram_clear = pdata->plat_post_ram_clear; + + /* set the driver data. */ + platform_set_drvdata(pdev, rtc); + + /* Turn the oscillator on if is not already on (DV1 = 1). */ + ctrla = rtc->read(rtc, RTC_CTRL_A); + if (!(ctrla & RTC_CTRL_A_DV1)) + ctrla |= RTC_CTRL_A_DV1; + + /* Enable the countdown chain (DV2 = 0) */ + ctrla &= ~(RTC_CTRL_A_DV2); + + /* Clear RS3-RS0 in Control A. */ + ctrla &= ~(RTC_CTRL_A_RS_MASK); + + /* + * All done with Control A. Switch to Bank 1 for the remainder of + * the RTC setup so we have access to the extended functions. + */ + ctrla |= RTC_CTRL_A_DV0; + rtc->write(rtc, RTC_CTRL_A, ctrla); + + /* Default to 32768kHz output. */ + rtc->write(rtc, RTC_EXT_CTRL_4B, + (rtc->read(rtc, RTC_EXT_CTRL_4B) | RTC_CTRL_4B_E32K)); + + /* Set the SET bit in Control B so we can do some housekeeping. */ + rtc->write(rtc, RTC_CTRL_B, + (rtc->read(rtc, RTC_CTRL_B) | RTC_CTRL_B_SET)); + + /* Read Ext Ctrl 4A and check the INCR bit to avoid a lockout. */ + while (rtc->read(rtc, RTC_EXT_CTRL_4A) & RTC_CTRL_4A_INCR) + cpu_relax(); + + /* + * If the platform supports BCD mode, then set DM=0 in Control B. + * Otherwise, set DM=1 for BIN mode. + */ + ctrlb = rtc->read(rtc, RTC_CTRL_B); + if (pdata->bcd_mode) + ctrlb &= ~(RTC_CTRL_B_DM); + else + ctrlb |= RTC_CTRL_B_DM; + rtc->bcd_mode = pdata->bcd_mode; + + /* + * Disable Daylight Savings Time (DSE = 0). + * The RTC has hardcoded timezone information that is rendered + * obselete. We'll let the OS deal with DST settings instead. + */ + if (ctrlb & RTC_CTRL_B_DSE) + ctrlb &= ~(RTC_CTRL_B_DSE); + + /* Force 24-hour mode (2412 = 1). */ + if (!(ctrlb & RTC_CTRL_B_2412)) { + /* Reinitialize the time hours. */ + hours = rtc->read(rtc, RTC_HRS); + am_pm = hours & RTC_HRS_AMPM_MASK; + hours = ds1685_rtc_bcd2bin(rtc, hours, RTC_HRS_12_BCD_MASK, + RTC_HRS_12_BIN_MASK); + hours = ((hours == 12) ? 0 : ((am_pm) ? hours + 12 : hours)); + + /* Enable 24-hour mode. */ + ctrlb |= RTC_CTRL_B_2412; + + /* Write back to Control B, including DM & DSE bits. */ + rtc->write(rtc, RTC_CTRL_B, ctrlb); + + /* Write the time hours back. */ + rtc->write(rtc, RTC_HRS, + ds1685_rtc_bin2bcd(rtc, hours, + RTC_HRS_24_BIN_MASK, + RTC_HRS_24_BCD_MASK)); + + /* Reinitialize the alarm hours. */ + hours = rtc->read(rtc, RTC_HRS_ALARM); + am_pm = hours & RTC_HRS_AMPM_MASK; + hours = ds1685_rtc_bcd2bin(rtc, hours, RTC_HRS_12_BCD_MASK, + RTC_HRS_12_BIN_MASK); + hours = ((hours == 12) ? 0 : ((am_pm) ? hours + 12 : hours)); + + /* Write the alarm hours back. */ + rtc->write(rtc, RTC_HRS_ALARM, + ds1685_rtc_bin2bcd(rtc, hours, + RTC_HRS_24_BIN_MASK, + RTC_HRS_24_BCD_MASK)); + } else { + /* 24-hour mode is already set, so write Control B back. */ + rtc->write(rtc, RTC_CTRL_B, ctrlb); + } + + /* Unset the SET bit in Control B so the RTC can update. */ + rtc->write(rtc, RTC_CTRL_B, + (rtc->read(rtc, RTC_CTRL_B) & ~(RTC_CTRL_B_SET))); + + /* Check the main battery. */ + if (!(rtc->read(rtc, RTC_CTRL_D) & RTC_CTRL_D_VRT)) + dev_warn(&pdev->dev, + "Main battery is exhausted! RTC may be invalid!\n"); + + /* Check the auxillary battery. It is optional. */ + if (!(rtc->read(rtc, RTC_EXT_CTRL_4A) & RTC_CTRL_4A_VRT2)) + dev_warn(&pdev->dev, + "Aux battery is exhausted or not available.\n"); + + /* Read Ctrl B and clear PIE/AIE/UIE. */ + rtc->write(rtc, RTC_CTRL_B, + (rtc->read(rtc, RTC_CTRL_B) & ~(RTC_CTRL_B_PAU_MASK))); + + /* Reading Ctrl C auto-clears PF/AF/UF. */ + rtc->read(rtc, RTC_CTRL_C); + + /* Read Ctrl 4B and clear RIE/WIE/KSE. */ + rtc->write(rtc, RTC_EXT_CTRL_4B, + (rtc->read(rtc, RTC_EXT_CTRL_4B) & ~(RTC_CTRL_4B_RWK_MASK))); + + /* Clear RF/WF/KF in Ctrl 4A. */ + rtc->write(rtc, RTC_EXT_CTRL_4A, + (rtc->read(rtc, RTC_EXT_CTRL_4A) & ~(RTC_CTRL_4A_RWK_MASK))); + + /* + * Re-enable KSE to handle power button events. We do not enable + * WIE or RIE by default. + */ + rtc->write(rtc, RTC_EXT_CTRL_4B, + (rtc->read(rtc, RTC_EXT_CTRL_4B) | RTC_CTRL_4B_KSE)); + + rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc_dev)) + return PTR_ERR(rtc_dev); + + rtc_dev->ops = &ds1685_rtc_ops; + + /* Century bit is useless because leap year fails in 1900 and 2100 */ + rtc_dev->range_min = RTC_TIMESTAMP_BEGIN_2000; + rtc_dev->range_max = RTC_TIMESTAMP_END_2099; + + /* See if the platform doesn't support UIE. */ + if (pdata->uie_unsupported) + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc_dev->features); + + rtc->dev = rtc_dev; + + /* + * Fetch the IRQ and setup the interrupt handler. + * + * Not all platforms have the IRQF pin tied to something. If not, the + * RTC will still set the *IE / *F flags and raise IRQF in ctrlc, but + * there won't be an automatic way of notifying the kernel about it, + * unless ctrlc is explicitly polled. + */ + rtc->irq_num = platform_get_irq(pdev, 0); + if (rtc->irq_num <= 0) { + clear_bit(RTC_FEATURE_ALARM, rtc_dev->features); + } else { + /* Request an IRQ. */ + ret = devm_request_threaded_irq(&pdev->dev, rtc->irq_num, + NULL, ds1685_rtc_irq_handler, + IRQF_SHARED | IRQF_ONESHOT, + pdev->name, pdev); + + /* Check to see if something came back. */ + if (unlikely(ret)) { + dev_warn(&pdev->dev, + "RTC interrupt not available\n"); + rtc->irq_num = 0; + } + } + + /* Setup complete. */ + ds1685_rtc_switch_to_bank0(rtc); + + ret = rtc_add_group(rtc_dev, &ds1685_rtc_sysfs_misc_grp); + if (ret) + return ret; + + nvmem_cfg.priv = rtc; + ret = devm_rtc_nvmem_register(rtc_dev, &nvmem_cfg); + if (ret) + return ret; + + return devm_rtc_register_device(rtc_dev); +} + +/** + * ds1685_rtc_remove - removes rtc driver. + * @pdev: pointer to platform_device structure. + */ +static void +ds1685_rtc_remove(struct platform_device *pdev) +{ + struct ds1685_priv *rtc = platform_get_drvdata(pdev); + + /* Read Ctrl B and clear PIE/AIE/UIE. */ + rtc->write(rtc, RTC_CTRL_B, + (rtc->read(rtc, RTC_CTRL_B) & + ~(RTC_CTRL_B_PAU_MASK))); + + /* Reading Ctrl C auto-clears PF/AF/UF. */ + rtc->read(rtc, RTC_CTRL_C); + + /* Read Ctrl 4B and clear RIE/WIE/KSE. */ + rtc->write(rtc, RTC_EXT_CTRL_4B, + (rtc->read(rtc, RTC_EXT_CTRL_4B) & + ~(RTC_CTRL_4B_RWK_MASK))); + + /* Manually clear RF/WF/KF in Ctrl 4A. */ + rtc->write(rtc, RTC_EXT_CTRL_4A, + (rtc->read(rtc, RTC_EXT_CTRL_4A) & + ~(RTC_CTRL_4A_RWK_MASK))); +} + +/* + * ds1685_rtc_driver - rtc driver properties. + */ +static struct platform_driver ds1685_rtc_driver = { + .driver = { + .name = "rtc-ds1685", + }, + .probe = ds1685_rtc_probe, + .remove = ds1685_rtc_remove, +}; +module_platform_driver(ds1685_rtc_driver); +/* ----------------------------------------------------------------------- */ + + +/* ----------------------------------------------------------------------- */ +/* Poweroff function */ + +/** + * ds1685_rtc_poweroff - uses the RTC chip to power the system off. + * @pdev: pointer to platform_device structure. + */ +void __noreturn +ds1685_rtc_poweroff(struct platform_device *pdev) +{ + u8 ctrla, ctrl4a, ctrl4b; + struct ds1685_priv *rtc; + + /* Check for valid RTC data, else, spin forever. */ + if (unlikely(!pdev)) { + pr_emerg("platform device data not available, spinning forever ...\n"); + while(1); + unreachable(); + } else { + /* Get the rtc data. */ + rtc = platform_get_drvdata(pdev); + + /* + * Disable our IRQ. We're powering down, so we're not + * going to worry about cleaning up. Most of that should + * have been taken care of by the shutdown scripts and this + * is the final function call. + */ + if (rtc->irq_num) + disable_irq_nosync(rtc->irq_num); + + /* Oscillator must be on and the countdown chain enabled. */ + ctrla = rtc->read(rtc, RTC_CTRL_A); + ctrla |= RTC_CTRL_A_DV1; + ctrla &= ~(RTC_CTRL_A_DV2); + rtc->write(rtc, RTC_CTRL_A, ctrla); + + /* + * Read Control 4A and check the status of the auxillary + * battery. This must be present and working (VRT2 = 1) + * for wakeup and kickstart functionality to be useful. + */ + ds1685_rtc_switch_to_bank1(rtc); + ctrl4a = rtc->read(rtc, RTC_EXT_CTRL_4A); + if (ctrl4a & RTC_CTRL_4A_VRT2) { + /* Clear all of the interrupt flags on Control 4A. */ + ctrl4a &= ~(RTC_CTRL_4A_RWK_MASK); + rtc->write(rtc, RTC_EXT_CTRL_4A, ctrl4a); + + /* + * The auxillary battery is present and working. + * Enable extended functions (ABE=1), enable + * wake-up (WIE=1), and enable kickstart (KSE=1) + * in Control 4B. + */ + ctrl4b = rtc->read(rtc, RTC_EXT_CTRL_4B); + ctrl4b |= (RTC_CTRL_4B_ABE | RTC_CTRL_4B_WIE | + RTC_CTRL_4B_KSE); + rtc->write(rtc, RTC_EXT_CTRL_4B, ctrl4b); + } + + /* Set PAB to 1 in Control 4A to power the system down. */ + dev_warn(&pdev->dev, "Powerdown.\n"); + msleep(20); + rtc->write(rtc, RTC_EXT_CTRL_4A, + (ctrl4a | RTC_CTRL_4A_PAB)); + + /* Spin ... we do not switch back to bank0. */ + while(1); + unreachable(); + } +} +EXPORT_SYMBOL_GPL(ds1685_rtc_poweroff); +/* ----------------------------------------------------------------------- */ + + +MODULE_AUTHOR("Joshua Kinard "); +MODULE_AUTHOR("Matthias Fuchs "); +MODULE_DESCRIPTION("Dallas/Maxim DS1685/DS1687-series RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rtc-ds1685"); diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c new file mode 100644 index 000000000..6ae8b9a29 --- /dev/null +++ b/drivers/rtc/rtc-ds1742.c @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * An rtc driver for the Dallas DS1742 + * + * Copyright (C) 2006 Atsushi Nemoto + * + * Copyright (C) 2006 Torsten Ertbjerg Rasmussen + * - nvram size determined from resource + * - this ds1742 driver now supports ds1743. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RTC_SIZE 8 + +#define RTC_CONTROL 0 +#define RTC_CENTURY 0 +#define RTC_SECONDS 1 +#define RTC_MINUTES 2 +#define RTC_HOURS 3 +#define RTC_DAY 4 +#define RTC_DATE 5 +#define RTC_MONTH 6 +#define RTC_YEAR 7 + +#define RTC_CENTURY_MASK 0x3f +#define RTC_SECONDS_MASK 0x7f +#define RTC_DAY_MASK 0x07 + +/* Bits in the Control/Century register */ +#define RTC_WRITE 0x80 +#define RTC_READ 0x40 + +/* Bits in the Seconds register */ +#define RTC_STOP 0x80 + +/* Bits in the Day register */ +#define RTC_BATT_FLAG 0x80 + +struct rtc_plat_data { + void __iomem *ioaddr_nvram; + void __iomem *ioaddr_rtc; + unsigned long last_jiffies; +}; + +static int ds1742_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + void __iomem *ioaddr = pdata->ioaddr_rtc; + u8 century; + + century = bin2bcd((tm->tm_year + 1900) / 100); + + writeb(RTC_WRITE, ioaddr + RTC_CONTROL); + + writeb(bin2bcd(tm->tm_year % 100), ioaddr + RTC_YEAR); + writeb(bin2bcd(tm->tm_mon + 1), ioaddr + RTC_MONTH); + writeb(bin2bcd(tm->tm_wday) & RTC_DAY_MASK, ioaddr + RTC_DAY); + writeb(bin2bcd(tm->tm_mday), ioaddr + RTC_DATE); + writeb(bin2bcd(tm->tm_hour), ioaddr + RTC_HOURS); + writeb(bin2bcd(tm->tm_min), ioaddr + RTC_MINUTES); + writeb(bin2bcd(tm->tm_sec) & RTC_SECONDS_MASK, ioaddr + RTC_SECONDS); + + /* RTC_CENTURY and RTC_CONTROL share same register */ + writeb(RTC_WRITE | (century & RTC_CENTURY_MASK), ioaddr + RTC_CENTURY); + writeb(century & RTC_CENTURY_MASK, ioaddr + RTC_CONTROL); + return 0; +} + +static int ds1742_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + void __iomem *ioaddr = pdata->ioaddr_rtc; + unsigned int year, month, day, hour, minute, second, week; + unsigned int century; + + /* give enough time to update RTC in case of continuous read */ + if (pdata->last_jiffies == jiffies) + msleep(1); + pdata->last_jiffies = jiffies; + writeb(RTC_READ, ioaddr + RTC_CONTROL); + second = readb(ioaddr + RTC_SECONDS) & RTC_SECONDS_MASK; + minute = readb(ioaddr + RTC_MINUTES); + hour = readb(ioaddr + RTC_HOURS); + day = readb(ioaddr + RTC_DATE); + week = readb(ioaddr + RTC_DAY) & RTC_DAY_MASK; + month = readb(ioaddr + RTC_MONTH); + year = readb(ioaddr + RTC_YEAR); + century = readb(ioaddr + RTC_CENTURY) & RTC_CENTURY_MASK; + writeb(0, ioaddr + RTC_CONTROL); + tm->tm_sec = bcd2bin(second); + tm->tm_min = bcd2bin(minute); + tm->tm_hour = bcd2bin(hour); + tm->tm_mday = bcd2bin(day); + tm->tm_wday = bcd2bin(week); + tm->tm_mon = bcd2bin(month) - 1; + /* year is 1900 + tm->tm_year */ + tm->tm_year = bcd2bin(year) + bcd2bin(century) * 100 - 1900; + + return 0; +} + +static const struct rtc_class_ops ds1742_rtc_ops = { + .read_time = ds1742_rtc_read_time, + .set_time = ds1742_rtc_set_time, +}; + +static int ds1742_nvram_read(void *priv, unsigned int pos, void *val, + size_t bytes) +{ + struct rtc_plat_data *pdata = priv; + void __iomem *ioaddr = pdata->ioaddr_nvram; + u8 *buf = val; + + for (; bytes; bytes--) + *buf++ = readb(ioaddr + pos++); + return 0; +} + +static int ds1742_nvram_write(void *priv, unsigned int pos, void *val, + size_t bytes) +{ + struct rtc_plat_data *pdata = priv; + void __iomem *ioaddr = pdata->ioaddr_nvram; + u8 *buf = val; + + for (; bytes; bytes--) + writeb(*buf++, ioaddr + pos++); + return 0; +} + +static int ds1742_rtc_probe(struct platform_device *pdev) +{ + struct rtc_device *rtc; + struct resource *res; + unsigned int cen, sec; + struct rtc_plat_data *pdata; + void __iomem *ioaddr; + int ret = 0; + struct nvmem_config nvmem_cfg = { + .name = "ds1742_nvram", + .reg_read = ds1742_nvram_read, + .reg_write = ds1742_nvram_write, + }; + + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + ioaddr = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(ioaddr)) + return PTR_ERR(ioaddr); + + pdata->ioaddr_nvram = ioaddr; + pdata->ioaddr_rtc = ioaddr + resource_size(res) - RTC_SIZE; + + nvmem_cfg.size = resource_size(res) - RTC_SIZE; + nvmem_cfg.priv = pdata; + + /* turn RTC on if it was not on */ + ioaddr = pdata->ioaddr_rtc; + sec = readb(ioaddr + RTC_SECONDS); + if (sec & RTC_STOP) { + sec &= RTC_SECONDS_MASK; + cen = readb(ioaddr + RTC_CENTURY) & RTC_CENTURY_MASK; + writeb(RTC_WRITE, ioaddr + RTC_CONTROL); + writeb(sec, ioaddr + RTC_SECONDS); + writeb(cen & RTC_CENTURY_MASK, ioaddr + RTC_CONTROL); + } + if (!(readb(ioaddr + RTC_DAY) & RTC_BATT_FLAG)) + dev_warn(&pdev->dev, "voltage-low detected.\n"); + + pdata->last_jiffies = jiffies; + platform_set_drvdata(pdev, pdata); + + rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + rtc->ops = &ds1742_rtc_ops; + + ret = devm_rtc_register_device(rtc); + if (ret) + return ret; + + devm_rtc_nvmem_register(rtc, &nvmem_cfg); + + return 0; +} + +static const struct of_device_id __maybe_unused ds1742_rtc_of_match[] = { + { .compatible = "maxim,ds1742", }, + { } +}; +MODULE_DEVICE_TABLE(of, ds1742_rtc_of_match); + +static struct platform_driver ds1742_rtc_driver = { + .probe = ds1742_rtc_probe, + .driver = { + .name = "rtc-ds1742", + .of_match_table = of_match_ptr(ds1742_rtc_of_match), + }, +}; + +module_platform_driver(ds1742_rtc_driver); + +MODULE_AUTHOR("Atsushi Nemoto "); +MODULE_DESCRIPTION("Dallas DS1742 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rtc-ds1742"); diff --git a/drivers/rtc/rtc-ds2404.c b/drivers/rtc/rtc-ds2404.c new file mode 100644 index 000000000..217694eca --- /dev/null +++ b/drivers/rtc/rtc-ds2404.c @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2012 Sven Schnelle + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DS2404_STATUS_REG 0x200 +#define DS2404_CONTROL_REG 0x201 +#define DS2404_RTC_REG 0x202 + +#define DS2404_WRITE_SCRATCHPAD_CMD 0x0f +#define DS2404_READ_SCRATCHPAD_CMD 0xaa +#define DS2404_COPY_SCRATCHPAD_CMD 0x55 +#define DS2404_READ_MEMORY_CMD 0xf0 + +#define DS2404_RST 0 +#define DS2404_CLK 1 +#define DS2404_DQ 2 + +struct ds2404 { + struct device *dev; + struct gpio_desc *rst_gpiod; + struct gpio_desc *clk_gpiod; + struct gpio_desc *dq_gpiod; +}; + +static int ds2404_gpio_map(struct ds2404 *chip, struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + /* This will de-assert RESET, declare this GPIO as GPIOD_ACTIVE_LOW */ + chip->rst_gpiod = devm_gpiod_get(dev, "rst", GPIOD_OUT_LOW); + if (IS_ERR(chip->rst_gpiod)) + return PTR_ERR(chip->rst_gpiod); + + chip->clk_gpiod = devm_gpiod_get(dev, "clk", GPIOD_OUT_HIGH); + if (IS_ERR(chip->clk_gpiod)) + return PTR_ERR(chip->clk_gpiod); + + chip->dq_gpiod = devm_gpiod_get(dev, "dq", GPIOD_ASIS); + if (IS_ERR(chip->dq_gpiod)) + return PTR_ERR(chip->dq_gpiod); + + return 0; +} + +static void ds2404_reset(struct ds2404 *chip) +{ + gpiod_set_value(chip->rst_gpiod, 1); + udelay(1000); + gpiod_set_value(chip->rst_gpiod, 0); + gpiod_set_value(chip->clk_gpiod, 0); + gpiod_direction_output(chip->dq_gpiod, 0); + udelay(10); +} + +static void ds2404_write_byte(struct ds2404 *chip, u8 byte) +{ + int i; + + gpiod_direction_output(chip->dq_gpiod, 1); + for (i = 0; i < 8; i++) { + gpiod_set_value(chip->dq_gpiod, byte & (1 << i)); + udelay(10); + gpiod_set_value(chip->clk_gpiod, 1); + udelay(10); + gpiod_set_value(chip->clk_gpiod, 0); + udelay(10); + } +} + +static u8 ds2404_read_byte(struct ds2404 *chip) +{ + int i; + u8 ret = 0; + + gpiod_direction_input(chip->dq_gpiod); + + for (i = 0; i < 8; i++) { + gpiod_set_value(chip->clk_gpiod, 0); + udelay(10); + if (gpiod_get_value(chip->dq_gpiod)) + ret |= 1 << i; + gpiod_set_value(chip->clk_gpiod, 1); + udelay(10); + } + return ret; +} + +static void ds2404_read_memory(struct ds2404 *chip, u16 offset, + int length, u8 *out) +{ + ds2404_reset(chip); + ds2404_write_byte(chip, DS2404_READ_MEMORY_CMD); + ds2404_write_byte(chip, offset & 0xff); + ds2404_write_byte(chip, (offset >> 8) & 0xff); + while (length--) + *out++ = ds2404_read_byte(chip); +} + +static void ds2404_write_memory(struct ds2404 *chip, u16 offset, + int length, u8 *out) +{ + int i; + u8 ta01, ta02, es; + + ds2404_reset(chip); + ds2404_write_byte(chip, DS2404_WRITE_SCRATCHPAD_CMD); + ds2404_write_byte(chip, offset & 0xff); + ds2404_write_byte(chip, (offset >> 8) & 0xff); + + for (i = 0; i < length; i++) + ds2404_write_byte(chip, out[i]); + + ds2404_reset(chip); + ds2404_write_byte(chip, DS2404_READ_SCRATCHPAD_CMD); + + ta01 = ds2404_read_byte(chip); + ta02 = ds2404_read_byte(chip); + es = ds2404_read_byte(chip); + + for (i = 0; i < length; i++) { + if (out[i] != ds2404_read_byte(chip)) { + dev_err(chip->dev, "read invalid data\n"); + return; + } + } + + ds2404_reset(chip); + ds2404_write_byte(chip, DS2404_COPY_SCRATCHPAD_CMD); + ds2404_write_byte(chip, ta01); + ds2404_write_byte(chip, ta02); + ds2404_write_byte(chip, es); + + while (gpiod_get_value(chip->dq_gpiod)) + ; +} + +static void ds2404_enable_osc(struct ds2404 *chip) +{ + u8 in[1] = { 0x10 }; /* enable oscillator */ + + ds2404_write_memory(chip, 0x201, 1, in); +} + +static int ds2404_read_time(struct device *dev, struct rtc_time *dt) +{ + struct ds2404 *chip = dev_get_drvdata(dev); + unsigned long time = 0; + __le32 hw_time = 0; + + ds2404_read_memory(chip, 0x203, 4, (u8 *)&hw_time); + time = le32_to_cpu(hw_time); + + rtc_time64_to_tm(time, dt); + return 0; +} + +static int ds2404_set_time(struct device *dev, struct rtc_time *dt) +{ + struct ds2404 *chip = dev_get_drvdata(dev); + u32 time = cpu_to_le32(rtc_tm_to_time64(dt)); + ds2404_write_memory(chip, 0x203, 4, (u8 *)&time); + return 0; +} + +static const struct rtc_class_ops ds2404_rtc_ops = { + .read_time = ds2404_read_time, + .set_time = ds2404_set_time, +}; + +static int rtc_probe(struct platform_device *pdev) +{ + struct ds2404 *chip; + struct rtc_device *rtc; + int retval = -EBUSY; + + chip = devm_kzalloc(&pdev->dev, sizeof(struct ds2404), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = &pdev->dev; + + rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + retval = ds2404_gpio_map(chip, pdev); + if (retval) + return retval; + + platform_set_drvdata(pdev, chip); + + rtc->ops = &ds2404_rtc_ops; + rtc->range_max = U32_MAX; + + retval = devm_rtc_register_device(rtc); + if (retval) + return retval; + + ds2404_enable_osc(chip); + return 0; +} + +static struct platform_driver rtc_device_driver = { + .probe = rtc_probe, + .driver = { + .name = "ds2404", + }, +}; +module_platform_driver(rtc_device_driver); + +MODULE_DESCRIPTION("DS2404 RTC"); +MODULE_AUTHOR("Sven Schnelle"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ds2404"); diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c new file mode 100644 index 000000000..d1ef9e0da --- /dev/null +++ b/drivers/rtc/rtc-ds3232.c @@ -0,0 +1,745 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * RTC client/driver for the Maxim/Dallas DS3232/DS3234 Real-Time Clock + * + * Copyright (C) 2009-2011 Freescale Semiconductor. + * Author: Jack Lan + * Copyright (C) 2008 MIMOMax Wireless Ltd. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DS3232_REG_SECONDS 0x00 +#define DS3232_REG_MINUTES 0x01 +#define DS3232_REG_HOURS 0x02 +#define DS3232_REG_AMPM 0x02 +#define DS3232_REG_DAY 0x03 +#define DS3232_REG_DATE 0x04 +#define DS3232_REG_MONTH 0x05 +#define DS3232_REG_CENTURY 0x05 +#define DS3232_REG_YEAR 0x06 +#define DS3232_REG_ALARM1 0x07 /* Alarm 1 BASE */ +#define DS3232_REG_ALARM2 0x0B /* Alarm 2 BASE */ +#define DS3232_REG_CR 0x0E /* Control register */ +# define DS3232_REG_CR_nEOSC 0x80 +# define DS3232_REG_CR_INTCN 0x04 +# define DS3232_REG_CR_A2IE 0x02 +# define DS3232_REG_CR_A1IE 0x01 + +#define DS3232_REG_SR 0x0F /* control/status register */ +# define DS3232_REG_SR_OSF 0x80 +# define DS3232_REG_SR_BSY 0x04 +# define DS3232_REG_SR_A2F 0x02 +# define DS3232_REG_SR_A1F 0x01 + +#define DS3232_REG_TEMPERATURE 0x11 +#define DS3232_REG_SRAM_START 0x14 +#define DS3232_REG_SRAM_END 0xFF + +#define DS3232_REG_SRAM_SIZE 236 + +struct ds3232 { + struct device *dev; + struct regmap *regmap; + int irq; + struct rtc_device *rtc; + + bool suspended; +}; + +static int ds3232_check_rtc_status(struct device *dev) +{ + struct ds3232 *ds3232 = dev_get_drvdata(dev); + int ret = 0; + int control, stat; + + ret = regmap_read(ds3232->regmap, DS3232_REG_SR, &stat); + if (ret) + return ret; + + if (stat & DS3232_REG_SR_OSF) + dev_warn(dev, + "oscillator discontinuity flagged, " + "time unreliable\n"); + + stat &= ~(DS3232_REG_SR_OSF | DS3232_REG_SR_A1F | DS3232_REG_SR_A2F); + + ret = regmap_write(ds3232->regmap, DS3232_REG_SR, stat); + if (ret) + return ret; + + /* If the alarm is pending, clear it before requesting + * the interrupt, so an interrupt event isn't reported + * before everything is initialized. + */ + + ret = regmap_read(ds3232->regmap, DS3232_REG_CR, &control); + if (ret) + return ret; + + control &= ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE); + control |= DS3232_REG_CR_INTCN; + + return regmap_write(ds3232->regmap, DS3232_REG_CR, control); +} + +static int ds3232_read_time(struct device *dev, struct rtc_time *time) +{ + struct ds3232 *ds3232 = dev_get_drvdata(dev); + int ret; + u8 buf[7]; + unsigned int year, month, day, hour, minute, second; + unsigned int week, twelve_hr, am_pm; + unsigned int century, add_century = 0; + + ret = regmap_bulk_read(ds3232->regmap, DS3232_REG_SECONDS, buf, 7); + if (ret) + return ret; + + second = buf[0]; + minute = buf[1]; + hour = buf[2]; + week = buf[3]; + day = buf[4]; + month = buf[5]; + year = buf[6]; + + /* Extract additional information for AM/PM and century */ + + twelve_hr = hour & 0x40; + am_pm = hour & 0x20; + century = month & 0x80; + + /* Write to rtc_time structure */ + + time->tm_sec = bcd2bin(second); + time->tm_min = bcd2bin(minute); + if (twelve_hr) { + /* Convert to 24 hr */ + if (am_pm) + time->tm_hour = bcd2bin(hour & 0x1F) + 12; + else + time->tm_hour = bcd2bin(hour & 0x1F); + } else { + time->tm_hour = bcd2bin(hour); + } + + /* Day of the week in linux range is 0~6 while 1~7 in RTC chip */ + time->tm_wday = bcd2bin(week) - 1; + time->tm_mday = bcd2bin(day); + /* linux tm_mon range:0~11, while month range is 1~12 in RTC chip */ + time->tm_mon = bcd2bin(month & 0x7F) - 1; + if (century) + add_century = 100; + + time->tm_year = bcd2bin(year) + add_century; + + return 0; +} + +static int ds3232_set_time(struct device *dev, struct rtc_time *time) +{ + struct ds3232 *ds3232 = dev_get_drvdata(dev); + u8 buf[7]; + + /* Extract time from rtc_time and load into ds3232*/ + + buf[0] = bin2bcd(time->tm_sec); + buf[1] = bin2bcd(time->tm_min); + buf[2] = bin2bcd(time->tm_hour); + /* Day of the week in linux range is 0~6 while 1~7 in RTC chip */ + buf[3] = bin2bcd(time->tm_wday + 1); + buf[4] = bin2bcd(time->tm_mday); /* Date */ + /* linux tm_mon range:0~11, while month range is 1~12 in RTC chip */ + buf[5] = bin2bcd(time->tm_mon + 1); + if (time->tm_year >= 100) { + buf[5] |= 0x80; + buf[6] = bin2bcd(time->tm_year - 100); + } else { + buf[6] = bin2bcd(time->tm_year); + } + + return regmap_bulk_write(ds3232->regmap, DS3232_REG_SECONDS, buf, 7); +} + +/* + * DS3232 has two alarm, we only use alarm1 + * According to linux specification, only support one-shot alarm + * no periodic alarm mode + */ +static int ds3232_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct ds3232 *ds3232 = dev_get_drvdata(dev); + int control, stat; + int ret; + u8 buf[4]; + + ret = regmap_read(ds3232->regmap, DS3232_REG_SR, &stat); + if (ret) + goto out; + ret = regmap_read(ds3232->regmap, DS3232_REG_CR, &control); + if (ret) + goto out; + ret = regmap_bulk_read(ds3232->regmap, DS3232_REG_ALARM1, buf, 4); + if (ret) + goto out; + + alarm->time.tm_sec = bcd2bin(buf[0] & 0x7F); + alarm->time.tm_min = bcd2bin(buf[1] & 0x7F); + alarm->time.tm_hour = bcd2bin(buf[2] & 0x7F); + alarm->time.tm_mday = bcd2bin(buf[3] & 0x7F); + + alarm->enabled = !!(control & DS3232_REG_CR_A1IE); + alarm->pending = !!(stat & DS3232_REG_SR_A1F); + + ret = 0; +out: + return ret; +} + +/* + * linux rtc-module does not support wday alarm + * and only 24h time mode supported indeed + */ +static int ds3232_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct ds3232 *ds3232 = dev_get_drvdata(dev); + int control, stat; + int ret; + u8 buf[4]; + + if (ds3232->irq <= 0) + return -EINVAL; + + buf[0] = bin2bcd(alarm->time.tm_sec); + buf[1] = bin2bcd(alarm->time.tm_min); + buf[2] = bin2bcd(alarm->time.tm_hour); + buf[3] = bin2bcd(alarm->time.tm_mday); + + /* clear alarm interrupt enable bit */ + ret = regmap_read(ds3232->regmap, DS3232_REG_CR, &control); + if (ret) + goto out; + control &= ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE); + ret = regmap_write(ds3232->regmap, DS3232_REG_CR, control); + if (ret) + goto out; + + /* clear any pending alarm flag */ + ret = regmap_read(ds3232->regmap, DS3232_REG_SR, &stat); + if (ret) + goto out; + stat &= ~(DS3232_REG_SR_A1F | DS3232_REG_SR_A2F); + ret = regmap_write(ds3232->regmap, DS3232_REG_SR, stat); + if (ret) + goto out; + + ret = regmap_bulk_write(ds3232->regmap, DS3232_REG_ALARM1, buf, 4); + if (ret) + goto out; + + if (alarm->enabled) { + control |= DS3232_REG_CR_A1IE; + ret = regmap_write(ds3232->regmap, DS3232_REG_CR, control); + } +out: + return ret; +} + +static int ds3232_update_alarm(struct device *dev, unsigned int enabled) +{ + struct ds3232 *ds3232 = dev_get_drvdata(dev); + int control; + int ret; + + ret = regmap_read(ds3232->regmap, DS3232_REG_CR, &control); + if (ret) + return ret; + + if (enabled) + /* enable alarm1 interrupt */ + control |= DS3232_REG_CR_A1IE; + else + /* disable alarm1 interrupt */ + control &= ~(DS3232_REG_CR_A1IE); + ret = regmap_write(ds3232->regmap, DS3232_REG_CR, control); + + return ret; +} + +/* + * Temperature sensor support for ds3232/ds3234 devices. + * A user-initiated temperature conversion is not started by this function, + * so the temperature is updated once every 64 seconds. + */ +static int ds3232_hwmon_read_temp(struct device *dev, long int *mC) +{ + struct ds3232 *ds3232 = dev_get_drvdata(dev); + u8 temp_buf[2]; + s16 temp; + int ret; + + ret = regmap_bulk_read(ds3232->regmap, DS3232_REG_TEMPERATURE, temp_buf, + sizeof(temp_buf)); + if (ret < 0) + return ret; + + /* + * Temperature is represented as a 10-bit code with a resolution of + * 0.25 degree celsius and encoded in two's complement format. + */ + temp = (temp_buf[0] << 8) | temp_buf[1]; + temp >>= 6; + *mC = temp * 250; + + return 0; +} + +static umode_t ds3232_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (type != hwmon_temp) + return 0; + + switch (attr) { + case hwmon_temp_input: + return 0444; + default: + return 0; + } +} + +static int ds3232_hwmon_read(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, long *temp) +{ + int err; + + switch (attr) { + case hwmon_temp_input: + err = ds3232_hwmon_read_temp(dev, temp); + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +static const struct hwmon_channel_info * const ds3232_hwmon_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), + NULL +}; + +static const struct hwmon_ops ds3232_hwmon_hwmon_ops = { + .is_visible = ds3232_hwmon_is_visible, + .read = ds3232_hwmon_read, +}; + +static const struct hwmon_chip_info ds3232_hwmon_chip_info = { + .ops = &ds3232_hwmon_hwmon_ops, + .info = ds3232_hwmon_info, +}; + +static void ds3232_hwmon_register(struct device *dev, const char *name) +{ + struct ds3232 *ds3232 = dev_get_drvdata(dev); + struct device *hwmon_dev; + + if (!IS_ENABLED(CONFIG_RTC_DRV_DS3232_HWMON)) + return; + + hwmon_dev = devm_hwmon_device_register_with_info(dev, name, ds3232, + &ds3232_hwmon_chip_info, + NULL); + if (IS_ERR(hwmon_dev)) { + dev_err(dev, "unable to register hwmon device %ld\n", + PTR_ERR(hwmon_dev)); + } +} + +static int ds3232_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct ds3232 *ds3232 = dev_get_drvdata(dev); + + if (ds3232->irq <= 0) + return -EINVAL; + + return ds3232_update_alarm(dev, enabled); +} + +static irqreturn_t ds3232_irq(int irq, void *dev_id) +{ + struct device *dev = dev_id; + struct ds3232 *ds3232 = dev_get_drvdata(dev); + int ret; + int stat, control; + + rtc_lock(ds3232->rtc); + + ret = regmap_read(ds3232->regmap, DS3232_REG_SR, &stat); + if (ret) + goto unlock; + + if (stat & DS3232_REG_SR_A1F) { + ret = regmap_read(ds3232->regmap, DS3232_REG_CR, &control); + if (ret) { + dev_warn(ds3232->dev, + "Read Control Register error %d\n", ret); + } else { + /* disable alarm1 interrupt */ + control &= ~(DS3232_REG_CR_A1IE); + ret = regmap_write(ds3232->regmap, DS3232_REG_CR, + control); + if (ret) { + dev_warn(ds3232->dev, + "Write Control Register error %d\n", + ret); + goto unlock; + } + + /* clear the alarm pend flag */ + stat &= ~DS3232_REG_SR_A1F; + ret = regmap_write(ds3232->regmap, DS3232_REG_SR, stat); + if (ret) { + dev_warn(ds3232->dev, + "Write Status Register error %d\n", + ret); + goto unlock; + } + + rtc_update_irq(ds3232->rtc, 1, RTC_AF | RTC_IRQF); + } + } + +unlock: + rtc_unlock(ds3232->rtc); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops ds3232_rtc_ops = { + .read_time = ds3232_read_time, + .set_time = ds3232_set_time, + .read_alarm = ds3232_read_alarm, + .set_alarm = ds3232_set_alarm, + .alarm_irq_enable = ds3232_alarm_irq_enable, +}; + +static int ds3232_nvmem_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct regmap *ds3232_regmap = (struct regmap *)priv; + + return regmap_bulk_read(ds3232_regmap, DS3232_REG_SRAM_START + offset, + val, bytes); +} + +static int ds3232_nvmem_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct regmap *ds3232_regmap = (struct regmap *)priv; + + return regmap_bulk_write(ds3232_regmap, DS3232_REG_SRAM_START + offset, + val, bytes); +} + +static int ds3232_probe(struct device *dev, struct regmap *regmap, int irq, + const char *name) +{ + struct ds3232 *ds3232; + int ret; + struct nvmem_config nvmem_cfg = { + .name = "ds3232_sram", + .stride = 1, + .size = DS3232_REG_SRAM_SIZE, + .word_size = 1, + .reg_read = ds3232_nvmem_read, + .reg_write = ds3232_nvmem_write, + .priv = regmap, + .type = NVMEM_TYPE_BATTERY_BACKED + }; + + ds3232 = devm_kzalloc(dev, sizeof(*ds3232), GFP_KERNEL); + if (!ds3232) + return -ENOMEM; + + ds3232->regmap = regmap; + ds3232->irq = irq; + ds3232->dev = dev; + dev_set_drvdata(dev, ds3232); + + ret = ds3232_check_rtc_status(dev); + if (ret) + return ret; + + if (ds3232->irq > 0) + device_init_wakeup(dev, true); + + ds3232_hwmon_register(dev, name); + + ds3232->rtc = devm_rtc_device_register(dev, name, &ds3232_rtc_ops, + THIS_MODULE); + if (IS_ERR(ds3232->rtc)) + return PTR_ERR(ds3232->rtc); + + ret = devm_rtc_nvmem_register(ds3232->rtc, &nvmem_cfg); + if(ret) + return ret; + + if (ds3232->irq > 0) { + ret = devm_request_threaded_irq(dev, ds3232->irq, NULL, + ds3232_irq, + IRQF_SHARED | IRQF_ONESHOT, + name, dev); + if (ret) { + device_set_wakeup_capable(dev, 0); + ds3232->irq = 0; + dev_err(dev, "unable to request IRQ\n"); + } + } + + return 0; +} + +#if IS_ENABLED(CONFIG_I2C) + +#ifdef CONFIG_PM_SLEEP +static int ds3232_suspend(struct device *dev) +{ + struct ds3232 *ds3232 = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) { + if (enable_irq_wake(ds3232->irq)) + dev_warn_once(dev, "Cannot set wakeup source\n"); + } + + return 0; +} + +static int ds3232_resume(struct device *dev) +{ + struct ds3232 *ds3232 = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(ds3232->irq); + + return 0; +} +#endif + +static const struct dev_pm_ops ds3232_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ds3232_suspend, ds3232_resume) +}; + +static int ds3232_i2c_probe(struct i2c_client *client) +{ + struct regmap *regmap; + static const struct regmap_config config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = DS3232_REG_SRAM_END, + }; + + regmap = devm_regmap_init_i2c(client, &config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "%s: regmap allocation failed: %ld\n", + __func__, PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + return ds3232_probe(&client->dev, regmap, client->irq, client->name); +} + +static const struct i2c_device_id ds3232_id[] = { + { .name = "ds3232" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ds3232_id); + +static const __maybe_unused struct of_device_id ds3232_of_match[] = { + { .compatible = "dallas,ds3232" }, + { } +}; +MODULE_DEVICE_TABLE(of, ds3232_of_match); + +static struct i2c_driver ds3232_driver = { + .driver = { + .name = "rtc-ds3232", + .of_match_table = of_match_ptr(ds3232_of_match), + .pm = &ds3232_pm_ops, + }, + .probe = ds3232_i2c_probe, + .id_table = ds3232_id, +}; + +static int ds3232_register_driver(void) +{ + return i2c_add_driver(&ds3232_driver); +} + +static void ds3232_unregister_driver(void) +{ + i2c_del_driver(&ds3232_driver); +} + +#else + +static int ds3232_register_driver(void) +{ + return 0; +} + +static void ds3232_unregister_driver(void) +{ +} + +#endif + +#if IS_ENABLED(CONFIG_SPI_MASTER) + +static int ds3234_probe(struct spi_device *spi) +{ + int res; + unsigned int tmp; + static const struct regmap_config config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = DS3232_REG_SRAM_END, + .write_flag_mask = 0x80, + }; + struct regmap *regmap; + + regmap = devm_regmap_init_spi(spi, &config); + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "%s: regmap allocation failed: %ld\n", + __func__, PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + spi->mode = SPI_MODE_3; + spi->bits_per_word = 8; + spi_setup(spi); + + res = regmap_read(regmap, DS3232_REG_SECONDS, &tmp); + if (res) + return res; + + /* Control settings + * + * CONTROL_REG + * BIT 7 6 5 4 3 2 1 0 + * EOSC BBSQW CONV RS2 RS1 INTCN A2IE A1IE + * + * 0 0 0 1 1 1 0 0 + * + * CONTROL_STAT_REG + * BIT 7 6 5 4 3 2 1 0 + * OSF BB32kHz CRATE1 CRATE0 EN32kHz BSY A2F A1F + * + * 1 0 0 0 1 0 0 0 + */ + res = regmap_read(regmap, DS3232_REG_CR, &tmp); + if (res) + return res; + res = regmap_write(regmap, DS3232_REG_CR, tmp & 0x1c); + if (res) + return res; + + res = regmap_read(regmap, DS3232_REG_SR, &tmp); + if (res) + return res; + res = regmap_write(regmap, DS3232_REG_SR, tmp & 0x88); + if (res) + return res; + + /* Print our settings */ + res = regmap_read(regmap, DS3232_REG_CR, &tmp); + if (res) + return res; + dev_info(&spi->dev, "Control Reg: 0x%02x\n", tmp); + + res = regmap_read(regmap, DS3232_REG_SR, &tmp); + if (res) + return res; + dev_info(&spi->dev, "Ctrl/Stat Reg: 0x%02x\n", tmp); + + return ds3232_probe(&spi->dev, regmap, spi->irq, "ds3234"); +} + +static struct spi_driver ds3234_driver = { + .driver = { + .name = "ds3234", + }, + .probe = ds3234_probe, +}; + +static int ds3234_register_driver(void) +{ + return spi_register_driver(&ds3234_driver); +} + +static void ds3234_unregister_driver(void) +{ + spi_unregister_driver(&ds3234_driver); +} + +#else + +static int ds3234_register_driver(void) +{ + return 0; +} + +static void ds3234_unregister_driver(void) +{ +} + +#endif + +static int __init ds323x_init(void) +{ + int ret; + + ret = ds3232_register_driver(); + if (ret) { + pr_err("Failed to register ds3232 driver: %d\n", ret); + return ret; + } + + ret = ds3234_register_driver(); + if (ret) { + pr_err("Failed to register ds3234 driver: %d\n", ret); + ds3232_unregister_driver(); + } + + return ret; +} +module_init(ds323x_init) + +static void __exit ds323x_exit(void) +{ + ds3234_unregister_driver(); + ds3232_unregister_driver(); +} +module_exit(ds323x_exit) + +MODULE_AUTHOR("Srikanth Srinivasan "); +MODULE_AUTHOR("Dennis Aberilla "); +MODULE_DESCRIPTION("Maxim/Dallas DS3232/DS3234 RTC Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:ds3234"); diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c new file mode 100644 index 000000000..b4f44999e --- /dev/null +++ b/drivers/rtc/rtc-efi.c @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * rtc-efi: RTC Class Driver for EFI-based systems + * + * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. + * + * Author: dann frazier + * Based on efirtc.c by Stephane Eranian + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include + +#define EFI_ISDST (EFI_TIME_ADJUST_DAYLIGHT|EFI_TIME_IN_DAYLIGHT) + +/* + * returns day of the year [0-365] + */ +static inline int +compute_yday(efi_time_t *eft) +{ + /* efi_time_t.month is in the [1-12] so, we need -1 */ + return rtc_year_days(eft->day, eft->month - 1, eft->year); +} + +/* + * returns day of the week [0-6] 0=Sunday + */ +static int +compute_wday(efi_time_t *eft, int yday) +{ + int ndays = eft->year * (365 % 7) + + (eft->year - 1) / 4 + - (eft->year - 1) / 100 + + (eft->year - 1) / 400 + + yday; + + /* + * 1/1/0000 may or may not have been a Sunday (if it ever existed at + * all) but assuming it was makes this calculation work correctly. + */ + return ndays % 7; +} + +static void +convert_to_efi_time(struct rtc_time *wtime, efi_time_t *eft) +{ + eft->year = wtime->tm_year + 1900; + eft->month = wtime->tm_mon + 1; + eft->day = wtime->tm_mday; + eft->hour = wtime->tm_hour; + eft->minute = wtime->tm_min; + eft->second = wtime->tm_sec; + eft->nanosecond = 0; + eft->daylight = wtime->tm_isdst ? EFI_ISDST : 0; + eft->timezone = EFI_UNSPECIFIED_TIMEZONE; +} + +static bool +convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime) +{ + memset(wtime, 0, sizeof(*wtime)); + + if (eft->second >= 60) + return false; + wtime->tm_sec = eft->second; + + if (eft->minute >= 60) + return false; + wtime->tm_min = eft->minute; + + if (eft->hour >= 24) + return false; + wtime->tm_hour = eft->hour; + + if (!eft->day || eft->day > 31) + return false; + wtime->tm_mday = eft->day; + + if (!eft->month || eft->month > 12) + return false; + wtime->tm_mon = eft->month - 1; + + if (eft->year < 1900 || eft->year > 9999) + return false; + wtime->tm_year = eft->year - 1900; + + /* day in the year [1-365]*/ + wtime->tm_yday = compute_yday(eft); + + /* day of the week [0-6], Sunday=0 */ + wtime->tm_wday = compute_wday(eft, wtime->tm_yday); + + switch (eft->daylight & EFI_ISDST) { + case EFI_ISDST: + wtime->tm_isdst = 1; + break; + case EFI_TIME_ADJUST_DAYLIGHT: + wtime->tm_isdst = 0; + break; + default: + wtime->tm_isdst = -1; + } + + return true; +} + +static int efi_read_time(struct device *dev, struct rtc_time *tm) +{ + efi_status_t status; + efi_time_t eft; + efi_time_cap_t cap; + + status = efi.get_time(&eft, &cap); + + if (status != EFI_SUCCESS) { + /* should never happen */ + dev_err_once(dev, "can't read time\n"); + return -EINVAL; + } + + if (!convert_from_efi_time(&eft, tm)) + return -EIO; + + return 0; +} + +static int efi_set_time(struct device *dev, struct rtc_time *tm) +{ + efi_status_t status; + efi_time_t eft; + + convert_to_efi_time(tm, &eft); + + status = efi.set_time(&eft); + + return status == EFI_SUCCESS ? 0 : -EINVAL; +} + +static int efi_procfs(struct device *dev, struct seq_file *seq) +{ + efi_time_t eft; + efi_time_cap_t cap; + + memset(&eft, 0, sizeof(eft)); + memset(&cap, 0, sizeof(cap)); + + efi.get_time(&eft, &cap); + + seq_printf(seq, + "Time\t\t: %u:%u:%u.%09u\n" + "Date\t\t: %u-%u-%u\n" + "Daylight\t: %u\n", + eft.hour, eft.minute, eft.second, eft.nanosecond, + eft.year, eft.month, eft.day, + eft.daylight); + + if (eft.timezone == EFI_UNSPECIFIED_TIMEZONE) + seq_puts(seq, "Timezone\t: unspecified\n"); + else + /* XXX fixme: convert to string? */ + seq_printf(seq, "Timezone\t: %u\n", eft.timezone); + + /* + * now prints the capabilities + */ + seq_printf(seq, + "Resolution\t: %u\n" + "Accuracy\t: %u\n" + "SetstoZero\t: %u\n", + cap.resolution, cap.accuracy, cap.sets_to_zero); + + return 0; +} + +static const struct rtc_class_ops efi_rtc_ops = { + .read_time = efi_read_time, + .set_time = efi_set_time, + .proc = efi_procfs, +}; + +static int __init efi_rtc_probe(struct platform_device *dev) +{ + struct rtc_device *rtc; + efi_time_t eft; + efi_time_cap_t cap; + + /* First check if the RTC is usable */ + if (efi.get_time(&eft, &cap) != EFI_SUCCESS) + return -ENODEV; + + rtc = devm_rtc_allocate_device(&dev->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + platform_set_drvdata(dev, rtc); + + rtc->ops = &efi_rtc_ops; + clear_bit(RTC_FEATURE_ALARM, rtc->features); + + device_init_wakeup(&dev->dev, true); + + return devm_rtc_register_device(rtc); +} + +static struct platform_driver efi_rtc_driver = { + .driver = { + .name = "rtc-efi", + }, +}; + +module_platform_driver_probe(efi_rtc_driver, efi_rtc_probe); + +MODULE_AUTHOR("dann frazier "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("EFI RTC driver"); +MODULE_ALIAS("platform:rtc-efi"); diff --git a/drivers/rtc/rtc-em3027.c b/drivers/rtc/rtc-em3027.c new file mode 100644 index 000000000..d555e5d59 --- /dev/null +++ b/drivers/rtc/rtc-em3027.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * An rtc/i2c driver for the EM Microelectronic EM3027 + * Copyright 2011 CompuLab, Ltd. + * + * Author: Mike Rapoport + * + * Based on rtc-ds1672.c by Alessandro Zummo + */ + +#include +#include +#include +#include +#include + +/* Registers */ +#define EM3027_REG_ON_OFF_CTRL 0x00 +#define EM3027_REG_IRQ_CTRL 0x01 +#define EM3027_REG_IRQ_FLAGS 0x02 +#define EM3027_REG_STATUS 0x03 +#define EM3027_REG_RST_CTRL 0x04 + +#define EM3027_REG_WATCH_SEC 0x08 +#define EM3027_REG_WATCH_MIN 0x09 +#define EM3027_REG_WATCH_HOUR 0x0a +#define EM3027_REG_WATCH_DATE 0x0b +#define EM3027_REG_WATCH_DAY 0x0c +#define EM3027_REG_WATCH_MON 0x0d +#define EM3027_REG_WATCH_YEAR 0x0e + +#define EM3027_REG_ALARM_SEC 0x10 +#define EM3027_REG_ALARM_MIN 0x11 +#define EM3027_REG_ALARM_HOUR 0x12 +#define EM3027_REG_ALARM_DATE 0x13 +#define EM3027_REG_ALARM_DAY 0x14 +#define EM3027_REG_ALARM_MON 0x15 +#define EM3027_REG_ALARM_YEAR 0x16 + +static struct i2c_driver em3027_driver; + +static int em3027_get_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + + unsigned char addr = EM3027_REG_WATCH_SEC; + unsigned char buf[7]; + + struct i2c_msg msgs[] = { + {/* setup read addr */ + .addr = client->addr, + .len = 1, + .buf = &addr + }, + {/* read time/date */ + .addr = client->addr, + .flags = I2C_M_RD, + .len = 7, + .buf = buf + }, + }; + + /* read time/date registers */ + if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) { + dev_err(&client->dev, "%s: read error\n", __func__); + return -EIO; + } + + tm->tm_sec = bcd2bin(buf[0]); + tm->tm_min = bcd2bin(buf[1]); + tm->tm_hour = bcd2bin(buf[2]); + tm->tm_mday = bcd2bin(buf[3]); + tm->tm_wday = bcd2bin(buf[4]); + tm->tm_mon = bcd2bin(buf[5]) - 1; + tm->tm_year = bcd2bin(buf[6]) + 100; + + return 0; +} + +static int em3027_set_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned char buf[8]; + + struct i2c_msg msg = { + .addr = client->addr, + .len = 8, + .buf = buf, /* write time/date */ + }; + + buf[0] = EM3027_REG_WATCH_SEC; + buf[1] = bin2bcd(tm->tm_sec); + buf[2] = bin2bcd(tm->tm_min); + buf[3] = bin2bcd(tm->tm_hour); + buf[4] = bin2bcd(tm->tm_mday); + buf[5] = bin2bcd(tm->tm_wday); + buf[6] = bin2bcd(tm->tm_mon + 1); + buf[7] = bin2bcd(tm->tm_year % 100); + + /* write time/date registers */ + if ((i2c_transfer(client->adapter, &msg, 1)) != 1) { + dev_err(&client->dev, "%s: write error\n", __func__); + return -EIO; + } + + return 0; +} + +static const struct rtc_class_ops em3027_rtc_ops = { + .read_time = em3027_get_time, + .set_time = em3027_set_time, +}; + +static int em3027_probe(struct i2c_client *client) +{ + struct rtc_device *rtc; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + rtc = devm_rtc_device_register(&client->dev, em3027_driver.driver.name, + &em3027_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + i2c_set_clientdata(client, rtc); + + return 0; +} + +static const struct i2c_device_id em3027_id[] = { + { .name = "em3027" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, em3027_id); + +#ifdef CONFIG_OF +static const struct of_device_id em3027_of_match[] = { + { .compatible = "emmicro,em3027", }, + {} +}; +MODULE_DEVICE_TABLE(of, em3027_of_match); +#endif + +static struct i2c_driver em3027_driver = { + .driver = { + .name = "rtc-em3027", + .of_match_table = of_match_ptr(em3027_of_match), + }, + .probe = em3027_probe, + .id_table = em3027_id, +}; + +module_i2c_driver(em3027_driver); + +MODULE_AUTHOR("Mike Rapoport "); +MODULE_DESCRIPTION("EM Microelectronic EM3027 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-ep93xx.c b/drivers/rtc/rtc-ep93xx.c new file mode 100644 index 000000000..507c9f220 --- /dev/null +++ b/drivers/rtc/rtc-ep93xx.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A driver for the RTC embedded in the Cirrus Logic EP93XX processors + * Copyright (c) 2006 Tower Technologies + * + * Author: Alessandro Zummo + */ + +#include +#include +#include +#include +#include + +#define EP93XX_RTC_DATA 0x000 +#define EP93XX_RTC_MATCH 0x004 +#define EP93XX_RTC_STATUS 0x008 +#define EP93XX_RTC_STATUS_INTR BIT(0) +#define EP93XX_RTC_LOAD 0x00C +#define EP93XX_RTC_CONTROL 0x010 +#define EP93XX_RTC_CONTROL_MIE BIT(0) +#define EP93XX_RTC_SWCOMP 0x108 +#define EP93XX_RTC_SWCOMP_DEL_MASK 0x001f0000 +#define EP93XX_RTC_SWCOMP_DEL_SHIFT 16 +#define EP93XX_RTC_SWCOMP_INT_MASK 0x0000ffff +#define EP93XX_RTC_SWCOMP_INT_SHIFT 0 + +struct ep93xx_rtc { + void __iomem *mmio_base; +}; + +static int ep93xx_rtc_get_swcomp(struct device *dev, unsigned short *preload, + unsigned short *delete) +{ + struct ep93xx_rtc *ep93xx_rtc = dev_get_drvdata(dev); + unsigned long comp; + + comp = readl(ep93xx_rtc->mmio_base + EP93XX_RTC_SWCOMP); + + if (preload) + *preload = (comp & EP93XX_RTC_SWCOMP_INT_MASK) + >> EP93XX_RTC_SWCOMP_INT_SHIFT; + + if (delete) + *delete = (comp & EP93XX_RTC_SWCOMP_DEL_MASK) + >> EP93XX_RTC_SWCOMP_DEL_SHIFT; + + return 0; +} + +static int ep93xx_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct ep93xx_rtc *ep93xx_rtc = dev_get_drvdata(dev); + unsigned long time; + + time = readl(ep93xx_rtc->mmio_base + EP93XX_RTC_DATA); + + rtc_time64_to_tm(time, tm); + return 0; +} + +static int ep93xx_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct ep93xx_rtc *ep93xx_rtc = dev_get_drvdata(dev); + unsigned long secs = rtc_tm_to_time64(tm); + + writel(secs + 1, ep93xx_rtc->mmio_base + EP93XX_RTC_LOAD); + return 0; +} + +static int ep93xx_rtc_proc(struct device *dev, struct seq_file *seq) +{ + unsigned short preload, delete; + + ep93xx_rtc_get_swcomp(dev, &preload, &delete); + + seq_printf(seq, "preload\t\t: %d\n", preload); + seq_printf(seq, "delete\t\t: %d\n", delete); + + return 0; +} + +static const struct rtc_class_ops ep93xx_rtc_ops = { + .read_time = ep93xx_rtc_read_time, + .set_time = ep93xx_rtc_set_time, + .proc = ep93xx_rtc_proc, +}; + +static ssize_t comp_preload_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned short preload; + + ep93xx_rtc_get_swcomp(dev->parent, &preload, NULL); + + return sprintf(buf, "%d\n", preload); +} +static DEVICE_ATTR_RO(comp_preload); + +static ssize_t comp_delete_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned short delete; + + ep93xx_rtc_get_swcomp(dev->parent, NULL, &delete); + + return sprintf(buf, "%d\n", delete); +} +static DEVICE_ATTR_RO(comp_delete); + +static struct attribute *ep93xx_rtc_attrs[] = { + &dev_attr_comp_preload.attr, + &dev_attr_comp_delete.attr, + NULL +}; + +static const struct attribute_group ep93xx_rtc_sysfs_files = { + .attrs = ep93xx_rtc_attrs, +}; + +static int ep93xx_rtc_probe(struct platform_device *pdev) +{ + struct ep93xx_rtc *ep93xx_rtc; + struct rtc_device *rtc; + int err; + + ep93xx_rtc = devm_kzalloc(&pdev->dev, sizeof(*ep93xx_rtc), GFP_KERNEL); + if (!ep93xx_rtc) + return -ENOMEM; + + ep93xx_rtc->mmio_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ep93xx_rtc->mmio_base)) + return PTR_ERR(ep93xx_rtc->mmio_base); + + platform_set_drvdata(pdev, ep93xx_rtc); + + rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + rtc->ops = &ep93xx_rtc_ops; + rtc->range_max = U32_MAX; + + err = rtc_add_group(rtc, &ep93xx_rtc_sysfs_files); + if (err) + return err; + + return devm_rtc_register_device(rtc); +} + +static const struct of_device_id ep93xx_rtc_of_ids[] = { + { .compatible = "cirrus,ep9301-rtc" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ep93xx_rtc_of_ids); + +static struct platform_driver ep93xx_rtc_driver = { + .driver = { + .name = "ep93xx-rtc", + .of_match_table = ep93xx_rtc_of_ids, + }, + .probe = ep93xx_rtc_probe, +}; + +module_platform_driver(ep93xx_rtc_driver); + +MODULE_AUTHOR("Alessandro Zummo "); +MODULE_DESCRIPTION("EP93XX RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ep93xx-rtc"); diff --git a/drivers/rtc/rtc-fm3130.c b/drivers/rtc/rtc-fm3130.c new file mode 100644 index 000000000..28bb6d3f6 --- /dev/null +++ b/drivers/rtc/rtc-fm3130.c @@ -0,0 +1,529 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rtc-fm3130.c - RTC driver for Ramtron FM3130 I2C chip. + * + * Copyright (C) 2008 Sergey Lapin + * Based on ds1307 driver by James Chapman and David Brownell + */ + +#include +#include +#include +#include +#include + +#define FM3130_RTC_CONTROL (0x0) +#define FM3130_CAL_CONTROL (0x1) +#define FM3130_RTC_SECONDS (0x2) +#define FM3130_RTC_MINUTES (0x3) +#define FM3130_RTC_HOURS (0x4) +#define FM3130_RTC_DAY (0x5) +#define FM3130_RTC_DATE (0x6) +#define FM3130_RTC_MONTHS (0x7) +#define FM3130_RTC_YEARS (0x8) + +#define FM3130_ALARM_SECONDS (0x9) +#define FM3130_ALARM_MINUTES (0xa) +#define FM3130_ALARM_HOURS (0xb) +#define FM3130_ALARM_DATE (0xc) +#define FM3130_ALARM_MONTHS (0xd) +#define FM3130_ALARM_WP_CONTROL (0xe) + +#define FM3130_CAL_CONTROL_BIT_nOSCEN (1 << 7) /* Osciallator enabled */ +#define FM3130_RTC_CONTROL_BIT_LB (1 << 7) /* Low battery */ +#define FM3130_RTC_CONTROL_BIT_AF (1 << 6) /* Alarm flag */ +#define FM3130_RTC_CONTROL_BIT_CF (1 << 5) /* Century overflow */ +#define FM3130_RTC_CONTROL_BIT_POR (1 << 4) /* Power on reset */ +#define FM3130_RTC_CONTROL_BIT_AEN (1 << 3) /* Alarm enable */ +#define FM3130_RTC_CONTROL_BIT_CAL (1 << 2) /* Calibration mode */ +#define FM3130_RTC_CONTROL_BIT_WRITE (1 << 1) /* W=1 -> write mode W=0 normal */ +#define FM3130_RTC_CONTROL_BIT_READ (1 << 0) /* R=1 -> read mode R=0 normal */ + +#define FM3130_CLOCK_REGS 7 +#define FM3130_ALARM_REGS 5 + +struct fm3130 { + u8 reg_addr_time; + u8 reg_addr_alarm; + u8 regs[15]; + struct i2c_msg msg[4]; + struct i2c_client *client; + struct rtc_device *rtc; + int alarm_valid; + int data_valid; +}; +static const struct i2c_device_id fm3130_id[] = { + { .name = "fm3130" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, fm3130_id); + +#define FM3130_MODE_NORMAL 0 +#define FM3130_MODE_WRITE 1 +#define FM3130_MODE_READ 2 + +static void fm3130_rtc_mode(struct device *dev, int mode) +{ + struct fm3130 *fm3130 = dev_get_drvdata(dev); + + fm3130->regs[FM3130_RTC_CONTROL] = + i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL); + switch (mode) { + case FM3130_MODE_NORMAL: + fm3130->regs[FM3130_RTC_CONTROL] &= + ~(FM3130_RTC_CONTROL_BIT_WRITE | + FM3130_RTC_CONTROL_BIT_READ); + break; + case FM3130_MODE_WRITE: + fm3130->regs[FM3130_RTC_CONTROL] |= FM3130_RTC_CONTROL_BIT_WRITE; + break; + case FM3130_MODE_READ: + fm3130->regs[FM3130_RTC_CONTROL] |= FM3130_RTC_CONTROL_BIT_READ; + break; + default: + dev_dbg(dev, "invalid mode %d\n", mode); + break; + } + + i2c_smbus_write_byte_data(fm3130->client, + FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL]); +} + +static int fm3130_get_time(struct device *dev, struct rtc_time *t) +{ + struct fm3130 *fm3130 = dev_get_drvdata(dev); + int tmp; + + if (!fm3130->data_valid) { + /* We have invalid data in RTC, probably due + to battery faults or other problems. Return EIO + for now, it will allow us to set data later instead + of error during probing which disables device */ + return -EIO; + } + fm3130_rtc_mode(dev, FM3130_MODE_READ); + + /* read the RTC date and time registers all at once */ + tmp = i2c_transfer(fm3130->client->adapter, fm3130->msg, 2); + if (tmp != 2) { + dev_err(dev, "%s error %d\n", "read", tmp); + return -EIO; + } + + fm3130_rtc_mode(dev, FM3130_MODE_NORMAL); + + dev_dbg(dev, "%s: %15ph\n", "read", fm3130->regs); + + t->tm_sec = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f); + t->tm_min = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f); + tmp = fm3130->regs[FM3130_RTC_HOURS] & 0x3f; + t->tm_hour = bcd2bin(tmp); + t->tm_wday = bcd2bin(fm3130->regs[FM3130_RTC_DAY] & 0x07) - 1; + t->tm_mday = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f); + tmp = fm3130->regs[FM3130_RTC_MONTHS] & 0x1f; + t->tm_mon = bcd2bin(tmp) - 1; + + /* assume 20YY not 19YY, and ignore CF bit */ + t->tm_year = bcd2bin(fm3130->regs[FM3130_RTC_YEARS]) + 100; + + dev_dbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "read", t->tm_sec, t->tm_min, + t->tm_hour, t->tm_mday, + t->tm_mon, t->tm_year, t->tm_wday); + + return 0; +} + + +static int fm3130_set_time(struct device *dev, struct rtc_time *t) +{ + struct fm3130 *fm3130 = dev_get_drvdata(dev); + int tmp, i; + u8 *buf = fm3130->regs; + + dev_dbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "write", t->tm_sec, t->tm_min, + t->tm_hour, t->tm_mday, + t->tm_mon, t->tm_year, t->tm_wday); + + /* first register addr */ + buf[FM3130_RTC_SECONDS] = bin2bcd(t->tm_sec); + buf[FM3130_RTC_MINUTES] = bin2bcd(t->tm_min); + buf[FM3130_RTC_HOURS] = bin2bcd(t->tm_hour); + buf[FM3130_RTC_DAY] = bin2bcd(t->tm_wday + 1); + buf[FM3130_RTC_DATE] = bin2bcd(t->tm_mday); + buf[FM3130_RTC_MONTHS] = bin2bcd(t->tm_mon + 1); + + /* assume 20YY not 19YY */ + tmp = t->tm_year - 100; + buf[FM3130_RTC_YEARS] = bin2bcd(tmp); + + dev_dbg(dev, "%s: %15ph\n", "write", buf); + + fm3130_rtc_mode(dev, FM3130_MODE_WRITE); + + /* Writing time registers, we don't support multibyte transfers */ + for (i = 0; i < FM3130_CLOCK_REGS; i++) { + i2c_smbus_write_byte_data(fm3130->client, + FM3130_RTC_SECONDS + i, + fm3130->regs[FM3130_RTC_SECONDS + i]); + } + + fm3130_rtc_mode(dev, FM3130_MODE_NORMAL); + + /* We assume here that data are valid once written */ + if (!fm3130->data_valid) + fm3130->data_valid = 1; + return 0; +} + +static int fm3130_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct fm3130 *fm3130 = dev_get_drvdata(dev); + int tmp; + struct rtc_time *tm = &alrm->time; + + if (!fm3130->alarm_valid) { + /* + * We have invalid alarm in RTC, probably due to battery faults + * or other problems. Return EIO for now, it will allow us to + * set alarm value later instead of error during probing which + * disables device + */ + return -EIO; + } + + /* read the RTC alarm registers all at once */ + tmp = i2c_transfer(fm3130->client->adapter, &fm3130->msg[2], 2); + if (tmp != 2) { + dev_err(dev, "%s error %d\n", "read", tmp); + return -EIO; + } + dev_dbg(dev, "alarm read %02x %02x %02x %02x %02x\n", + fm3130->regs[FM3130_ALARM_SECONDS], + fm3130->regs[FM3130_ALARM_MINUTES], + fm3130->regs[FM3130_ALARM_HOURS], + fm3130->regs[FM3130_ALARM_DATE], + fm3130->regs[FM3130_ALARM_MONTHS]); + + tm->tm_sec = bcd2bin(fm3130->regs[FM3130_ALARM_SECONDS] & 0x7F); + tm->tm_min = bcd2bin(fm3130->regs[FM3130_ALARM_MINUTES] & 0x7F); + tm->tm_hour = bcd2bin(fm3130->regs[FM3130_ALARM_HOURS] & 0x3F); + tm->tm_mday = bcd2bin(fm3130->regs[FM3130_ALARM_DATE] & 0x3F); + tm->tm_mon = bcd2bin(fm3130->regs[FM3130_ALARM_MONTHS] & 0x1F); + + if (tm->tm_mon > 0) + tm->tm_mon -= 1; /* RTC is 1-12, tm_mon is 0-11 */ + + dev_dbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "read alarm", tm->tm_sec, tm->tm_min, + tm->tm_hour, tm->tm_mday, + tm->tm_mon, tm->tm_year, tm->tm_wday); + + /* check if alarm enabled */ + fm3130->regs[FM3130_RTC_CONTROL] = + i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL); + + if ((fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AEN) && + (~fm3130->regs[FM3130_RTC_CONTROL] & + FM3130_RTC_CONTROL_BIT_CAL)) { + alrm->enabled = 1; + } + + return 0; +} + +static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct fm3130 *fm3130 = dev_get_drvdata(dev); + struct rtc_time *tm = &alrm->time; + int i; + + dev_dbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "write alarm", tm->tm_sec, tm->tm_min, + tm->tm_hour, tm->tm_mday, + tm->tm_mon, tm->tm_year, tm->tm_wday); + + fm3130->regs[FM3130_ALARM_SECONDS] = + (tm->tm_sec != -1) ? bin2bcd(tm->tm_sec) : 0x80; + + fm3130->regs[FM3130_ALARM_MINUTES] = + (tm->tm_min != -1) ? bin2bcd(tm->tm_min) : 0x80; + + fm3130->regs[FM3130_ALARM_HOURS] = + (tm->tm_hour != -1) ? bin2bcd(tm->tm_hour) : 0x80; + + fm3130->regs[FM3130_ALARM_DATE] = + (tm->tm_mday != -1) ? bin2bcd(tm->tm_mday) : 0x80; + + fm3130->regs[FM3130_ALARM_MONTHS] = + (tm->tm_mon != -1) ? bin2bcd(tm->tm_mon + 1) : 0x80; + + dev_dbg(dev, "alarm write %02x %02x %02x %02x %02x\n", + fm3130->regs[FM3130_ALARM_SECONDS], + fm3130->regs[FM3130_ALARM_MINUTES], + fm3130->regs[FM3130_ALARM_HOURS], + fm3130->regs[FM3130_ALARM_DATE], + fm3130->regs[FM3130_ALARM_MONTHS]); + /* Writing time registers, we don't support multibyte transfers */ + for (i = 0; i < FM3130_ALARM_REGS; i++) { + i2c_smbus_write_byte_data(fm3130->client, + FM3130_ALARM_SECONDS + i, + fm3130->regs[FM3130_ALARM_SECONDS + i]); + } + fm3130->regs[FM3130_RTC_CONTROL] = + i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL); + + /* enable or disable alarm */ + if (alrm->enabled) { + i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL, + (fm3130->regs[FM3130_RTC_CONTROL] & + ~(FM3130_RTC_CONTROL_BIT_CAL)) | + FM3130_RTC_CONTROL_BIT_AEN); + } else { + i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL, + fm3130->regs[FM3130_RTC_CONTROL] & + ~(FM3130_RTC_CONTROL_BIT_CAL) & + ~(FM3130_RTC_CONTROL_BIT_AEN)); + } + + /* We assume here that data is valid once written */ + if (!fm3130->alarm_valid) + fm3130->alarm_valid = 1; + + return 0; +} + +static int fm3130_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct fm3130 *fm3130 = dev_get_drvdata(dev); + int ret = 0; + + fm3130->regs[FM3130_RTC_CONTROL] = + i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL); + + dev_dbg(dev, "alarm_irq_enable: enable=%d, FM3130_RTC_CONTROL=%02x\n", + enabled, fm3130->regs[FM3130_RTC_CONTROL]); + + switch (enabled) { + case 0: /* alarm off */ + ret = i2c_smbus_write_byte_data(fm3130->client, + FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL] & + ~(FM3130_RTC_CONTROL_BIT_CAL) & + ~(FM3130_RTC_CONTROL_BIT_AEN)); + break; + case 1: /* alarm on */ + ret = i2c_smbus_write_byte_data(fm3130->client, + FM3130_RTC_CONTROL, (fm3130->regs[FM3130_RTC_CONTROL] & + ~(FM3130_RTC_CONTROL_BIT_CAL)) | + FM3130_RTC_CONTROL_BIT_AEN); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct rtc_class_ops fm3130_rtc_ops = { + .read_time = fm3130_get_time, + .set_time = fm3130_set_time, + .read_alarm = fm3130_read_alarm, + .set_alarm = fm3130_set_alarm, + .alarm_irq_enable = fm3130_alarm_irq_enable, +}; + +static struct i2c_driver fm3130_driver; + +static int fm3130_probe(struct i2c_client *client) +{ + struct fm3130 *fm3130; + int err = -ENODEV; + int tmp; + struct i2c_adapter *adapter = client->adapter; + + if (!i2c_check_functionality(adapter, + I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -EIO; + + fm3130 = devm_kzalloc(&client->dev, sizeof(struct fm3130), GFP_KERNEL); + + if (!fm3130) + return -ENOMEM; + + fm3130->client = client; + i2c_set_clientdata(client, fm3130); + fm3130->reg_addr_time = FM3130_RTC_SECONDS; + fm3130->reg_addr_alarm = FM3130_ALARM_SECONDS; + + /* Messages to read time */ + fm3130->msg[0].addr = client->addr; + fm3130->msg[0].flags = 0; + fm3130->msg[0].len = 1; + fm3130->msg[0].buf = &fm3130->reg_addr_time; + + fm3130->msg[1].addr = client->addr; + fm3130->msg[1].flags = I2C_M_RD; + fm3130->msg[1].len = FM3130_CLOCK_REGS; + fm3130->msg[1].buf = &fm3130->regs[FM3130_RTC_SECONDS]; + + /* Messages to read alarm */ + fm3130->msg[2].addr = client->addr; + fm3130->msg[2].flags = 0; + fm3130->msg[2].len = 1; + fm3130->msg[2].buf = &fm3130->reg_addr_alarm; + + fm3130->msg[3].addr = client->addr; + fm3130->msg[3].flags = I2C_M_RD; + fm3130->msg[3].len = FM3130_ALARM_REGS; + fm3130->msg[3].buf = &fm3130->regs[FM3130_ALARM_SECONDS]; + + fm3130->alarm_valid = 0; + fm3130->data_valid = 0; + + tmp = i2c_transfer(adapter, fm3130->msg, 4); + if (tmp != 4) { + dev_dbg(&client->dev, "read error %d\n", tmp); + err = -EIO; + goto exit_free; + } + + fm3130->regs[FM3130_RTC_CONTROL] = + i2c_smbus_read_byte_data(client, FM3130_RTC_CONTROL); + fm3130->regs[FM3130_CAL_CONTROL] = + i2c_smbus_read_byte_data(client, FM3130_CAL_CONTROL); + + /* Disabling calibration mode */ + if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_CAL) { + i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL, + fm3130->regs[FM3130_RTC_CONTROL] & + ~(FM3130_RTC_CONTROL_BIT_CAL)); + dev_warn(&client->dev, "Disabling calibration mode!\n"); + } + + /* Disabling read and write modes */ + if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_WRITE || + fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_READ) { + i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL, + fm3130->regs[FM3130_RTC_CONTROL] & + ~(FM3130_RTC_CONTROL_BIT_READ | + FM3130_RTC_CONTROL_BIT_WRITE)); + dev_warn(&client->dev, "Disabling READ or WRITE mode!\n"); + } + + /* oscillator off? turn it on, so clock can tick. */ + if (fm3130->regs[FM3130_CAL_CONTROL] & FM3130_CAL_CONTROL_BIT_nOSCEN) + i2c_smbus_write_byte_data(client, FM3130_CAL_CONTROL, + fm3130->regs[FM3130_CAL_CONTROL] & + ~(FM3130_CAL_CONTROL_BIT_nOSCEN)); + + /* low battery? clear flag, and warn */ + if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_LB) { + i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL, + fm3130->regs[FM3130_RTC_CONTROL] & + ~(FM3130_RTC_CONTROL_BIT_LB)); + dev_warn(&client->dev, "Low battery!\n"); + } + + /* check if Power On Reset bit is set */ + if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_POR) { + i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL, + fm3130->regs[FM3130_RTC_CONTROL] & + ~FM3130_RTC_CONTROL_BIT_POR); + dev_dbg(&client->dev, "POR bit is set\n"); + } + /* ACS is controlled by alarm */ + i2c_smbus_write_byte_data(client, FM3130_ALARM_WP_CONTROL, 0x80); + + /* alarm registers sanity check */ + tmp = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f); + if (tmp > 59) + goto bad_alarm; + + tmp = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f); + if (tmp > 59) + goto bad_alarm; + + tmp = bcd2bin(fm3130->regs[FM3130_RTC_HOURS] & 0x3f); + if (tmp > 23) + goto bad_alarm; + + tmp = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f); + if (tmp == 0 || tmp > 31) + goto bad_alarm; + + tmp = bcd2bin(fm3130->regs[FM3130_RTC_MONTHS] & 0x1f); + if (tmp == 0 || tmp > 12) + goto bad_alarm; + + fm3130->alarm_valid = 1; + +bad_alarm: + + /* clock registers sanity chek */ + tmp = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f); + if (tmp > 59) + goto bad_clock; + + tmp = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f); + if (tmp > 59) + goto bad_clock; + + tmp = bcd2bin(fm3130->regs[FM3130_RTC_HOURS] & 0x3f); + if (tmp > 23) + goto bad_clock; + + tmp = bcd2bin(fm3130->regs[FM3130_RTC_DAY] & 0x7); + if (tmp == 0 || tmp > 7) + goto bad_clock; + + tmp = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f); + if (tmp == 0 || tmp > 31) + goto bad_clock; + + tmp = bcd2bin(fm3130->regs[FM3130_RTC_MONTHS] & 0x1f); + if (tmp == 0 || tmp > 12) + goto bad_clock; + + fm3130->data_valid = 1; + +bad_clock: + + if (!fm3130->data_valid || !fm3130->alarm_valid) + dev_dbg(&client->dev, "%s: %15ph\n", "bogus registers", + fm3130->regs); + + /* We won't bail out here because we just got invalid data. + Time setting from u-boot doesn't work anyway */ + fm3130->rtc = devm_rtc_device_register(&client->dev, client->name, + &fm3130_rtc_ops, THIS_MODULE); + if (IS_ERR(fm3130->rtc)) { + err = PTR_ERR(fm3130->rtc); + dev_err(&client->dev, + "unable to register the class device\n"); + goto exit_free; + } + return 0; +exit_free: + return err; +} + +static struct i2c_driver fm3130_driver = { + .driver = { + .name = "rtc-fm3130", + }, + .probe = fm3130_probe, + .id_table = fm3130_id, +}; + +module_i2c_driver(fm3130_driver); + +MODULE_DESCRIPTION("RTC driver for FM3130"); +MODULE_AUTHOR("Sergey Lapin "); +MODULE_LICENSE("GPL"); + diff --git a/drivers/rtc/rtc-fsl-ftm-alarm.c b/drivers/rtc/rtc-fsl-ftm-alarm.c new file mode 100644 index 000000000..dcb7ef663 --- /dev/null +++ b/drivers/rtc/rtc-fsl-ftm-alarm.c @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Freescale FlexTimer Module (FTM) alarm device driver. + * + * Copyright 2014 Freescale Semiconductor, Inc. + * Copyright 2019-2020 NXP + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FTM_SC_CLK(c) ((c) << FTM_SC_CLK_MASK_SHIFT) + +/* + * Select Fixed frequency clock (32KHz) as clock source + * of FlexTimer Module + */ +#define FTM_SC_CLKS_FIXED_FREQ 0x02 +#define FIXED_FREQ_CLK 32000 + +/* Select 128 (2^7) as divider factor */ +#define MAX_FREQ_DIV (1 << FTM_SC_PS_MASK) + +/* Maximum counter value in FlexTimer's CNT registers */ +#define MAX_COUNT_VAL 0xffff + +struct ftm_rtc { + struct rtc_device *rtc_dev; + void __iomem *base; + bool big_endian; + u32 alarm_freq; +}; + +static inline u32 rtc_readl(struct ftm_rtc *dev, u32 reg) +{ + if (dev->big_endian) + return ioread32be(dev->base + reg); + else + return ioread32(dev->base + reg); +} + +static inline void rtc_writel(struct ftm_rtc *dev, u32 reg, u32 val) +{ + if (dev->big_endian) + iowrite32be(val, dev->base + reg); + else + iowrite32(val, dev->base + reg); +} + +static inline void ftm_counter_enable(struct ftm_rtc *rtc) +{ + u32 val; + + /* select and enable counter clock source */ + val = rtc_readl(rtc, FTM_SC); + val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK); + val |= (FTM_SC_PS_MASK | FTM_SC_CLK(FTM_SC_CLKS_FIXED_FREQ)); + rtc_writel(rtc, FTM_SC, val); +} + +static inline void ftm_counter_disable(struct ftm_rtc *rtc) +{ + u32 val; + + /* disable counter clock source */ + val = rtc_readl(rtc, FTM_SC); + val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK); + rtc_writel(rtc, FTM_SC, val); +} + +static inline void ftm_irq_acknowledge(struct ftm_rtc *rtc) +{ + unsigned int timeout = 100; + + /* + *Fix errata A-007728 for flextimer + * If the FTM counter reaches the FTM_MOD value between + * the reading of the TOF bit and the writing of 0 to + * the TOF bit, the process of clearing the TOF bit + * does not work as expected when FTMx_CONF[NUMTOF] != 0 + * and the current TOF count is less than FTMx_CONF[NUMTOF]. + * If the above condition is met, the TOF bit remains set. + * If the TOF interrupt is enabled (FTMx_SC[TOIE] = 1),the + * TOF interrupt also remains asserted. + * + * Above is the errata discription + * + * In one word: software clearing TOF bit not works when + * FTMx_CONF[NUMTOF] was seted as nonzero and FTM counter + * reaches the FTM_MOD value. + * + * The workaround is clearing TOF bit until it works + * (FTM counter doesn't always reache the FTM_MOD anyway), + * which may cost some cycles. + */ + while ((FTM_SC_TOF & rtc_readl(rtc, FTM_SC)) && timeout--) + rtc_writel(rtc, FTM_SC, rtc_readl(rtc, FTM_SC) & (~FTM_SC_TOF)); +} + +static inline void ftm_irq_enable(struct ftm_rtc *rtc) +{ + u32 val; + + val = rtc_readl(rtc, FTM_SC); + val |= FTM_SC_TOIE; + rtc_writel(rtc, FTM_SC, val); +} + +static inline void ftm_irq_disable(struct ftm_rtc *rtc) +{ + u32 val; + + val = rtc_readl(rtc, FTM_SC); + val &= ~FTM_SC_TOIE; + rtc_writel(rtc, FTM_SC, val); +} + +static inline void ftm_reset_counter(struct ftm_rtc *rtc) +{ + /* + * The CNT register contains the FTM counter value. + * Reset clears the CNT register. Writing any value to COUNT + * updates the counter with its initial value, CNTIN. + */ + rtc_writel(rtc, FTM_CNT, 0x00); +} + +static void ftm_clean_alarm(struct ftm_rtc *rtc) +{ + ftm_counter_disable(rtc); + + rtc_writel(rtc, FTM_CNTIN, 0x00); + rtc_writel(rtc, FTM_MOD, ~0U); + + ftm_reset_counter(rtc); +} + +static irqreturn_t ftm_rtc_alarm_interrupt(int irq, void *dev) +{ + struct ftm_rtc *rtc = dev; + + rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF); + + ftm_irq_acknowledge(rtc); + ftm_irq_disable(rtc); + ftm_clean_alarm(rtc); + + return IRQ_HANDLED; +} + +static int ftm_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct ftm_rtc *rtc = dev_get_drvdata(dev); + + if (enabled) + ftm_irq_enable(rtc); + else + ftm_irq_disable(rtc); + + return 0; +} + +/* + * Note: + * The function is not really getting time from the RTC + * since FlexTimer is not a RTC device, but we need to + * get time to setup alarm, so we are using system time + * for now. + */ +static int ftm_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + rtc_time64_to_tm(ktime_get_real_seconds(), tm); + + return 0; +} + +static int ftm_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + return 0; +} + +/* + * 1. Select fixed frequency clock (32KHz) as clock source; + * 2. Select 128 (2^7) as divider factor; + * So clock is 250 Hz (32KHz/128). + * + * 3. FlexTimer's CNT register is a 32bit register, + * but the register's 16 bit as counter value,it's other 16 bit + * is reserved.So minimum counter value is 0x0,maximum counter + * value is 0xffff. + * So max alarm value is 262 (65536 / 250) seconds + */ +static int ftm_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + time64_t alm_time; + unsigned long long cycle; + struct ftm_rtc *rtc = dev_get_drvdata(dev); + + alm_time = rtc_tm_to_time64(&alm->time); + + ftm_clean_alarm(rtc); + cycle = (alm_time - ktime_get_real_seconds()) * rtc->alarm_freq; + if (cycle > MAX_COUNT_VAL) { + pr_err("Out of alarm range {0~262} seconds.\n"); + return -ERANGE; + } + + ftm_irq_disable(rtc); + + /* + * The counter increments until the value of MOD is reached, + * at which point the counter is reloaded with the value of CNTIN. + * The TOF (the overflow flag) bit is set when the FTM counter + * changes from MOD to CNTIN. So we should using the cycle - 1. + */ + rtc_writel(rtc, FTM_MOD, cycle - 1); + + ftm_counter_enable(rtc); + ftm_irq_enable(rtc); + + return 0; + +} + +static const struct rtc_class_ops ftm_rtc_ops = { + .read_time = ftm_rtc_read_time, + .read_alarm = ftm_rtc_read_alarm, + .set_alarm = ftm_rtc_set_alarm, + .alarm_irq_enable = ftm_rtc_alarm_irq_enable, +}; + +static int ftm_rtc_probe(struct platform_device *pdev) +{ + int irq; + int ret; + struct ftm_rtc *rtc; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (unlikely(!rtc)) { + dev_err(&pdev->dev, "cannot alloc memory for rtc\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, rtc); + + rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc->rtc_dev)) + return PTR_ERR(rtc->rtc_dev); + + rtc->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rtc->base)) { + dev_err(&pdev->dev, "cannot ioremap resource for rtc\n"); + return PTR_ERR(rtc->base); + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(&pdev->dev, irq, ftm_rtc_alarm_interrupt, + 0, dev_name(&pdev->dev), rtc); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request irq\n"); + return ret; + } + + rtc->big_endian = + device_property_read_bool(&pdev->dev, "big-endian"); + + rtc->alarm_freq = (u32)FIXED_FREQ_CLK / (u32)MAX_FREQ_DIV; + rtc->rtc_dev->ops = &ftm_rtc_ops; + + device_init_wakeup(&pdev->dev, true); + ret = dev_pm_set_wake_irq(&pdev->dev, irq); + if (ret) + dev_err(&pdev->dev, "failed to enable irq wake\n"); + + ret = devm_rtc_register_device(rtc->rtc_dev); + if (ret) { + dev_err(&pdev->dev, "can't register rtc device\n"); + return ret; + } + + return 0; +} + +static const struct of_device_id ftm_rtc_match[] = { + { .compatible = "fsl,ls1012a-ftm-alarm", }, + { .compatible = "fsl,ls1021a-ftm-alarm", }, + { .compatible = "fsl,ls1028a-ftm-alarm", }, + { .compatible = "fsl,ls1043a-ftm-alarm", }, + { .compatible = "fsl,ls1046a-ftm-alarm", }, + { .compatible = "fsl,ls1088a-ftm-alarm", }, + { .compatible = "fsl,ls208xa-ftm-alarm", }, + { .compatible = "fsl,lx2160a-ftm-alarm", }, + { }, +}; +MODULE_DEVICE_TABLE(of, ftm_rtc_match); + +static const struct acpi_device_id ftm_imx_acpi_ids[] __maybe_unused = { + {"NXP0014",}, + { } +}; +MODULE_DEVICE_TABLE(acpi, ftm_imx_acpi_ids); + +static struct platform_driver ftm_rtc_driver = { + .probe = ftm_rtc_probe, + .driver = { + .name = "ftm-alarm", + .of_match_table = ftm_rtc_match, + .acpi_match_table = ACPI_PTR(ftm_imx_acpi_ids), + }, +}; + +module_platform_driver(ftm_rtc_driver); + +MODULE_DESCRIPTION("NXP/Freescale FlexTimer alarm driver"); +MODULE_AUTHOR("Biwen Li "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-ftrtc010.c b/drivers/rtc/rtc-ftrtc010.c new file mode 100644 index 000000000..98fa0f518 --- /dev/null +++ b/drivers/rtc/rtc-ftrtc010.c @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Faraday Technology FTRTC010 driver + * + * Copyright (C) 2009 Janos Laube + * + * Original code for older kernel 2.6.15 are from Stormlinksemi + * first update from Janos Laube for > 2.6.29 kernels + * + * checkpatch fixes and usage of rtc-lib code + * Hans Ulli Kroll + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "rtc-ftrtc010" + +MODULE_AUTHOR("Hans Ulli Kroll "); +MODULE_DESCRIPTION("RTC driver for Gemini SoC"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); + +struct ftrtc010_rtc { + void __iomem *rtc_base; + int rtc_irq; + struct clk *pclk; + struct clk *extclk; +}; + +enum ftrtc010_rtc_offsets { + FTRTC010_RTC_SECOND = 0x00, + FTRTC010_RTC_MINUTE = 0x04, + FTRTC010_RTC_HOUR = 0x08, + FTRTC010_RTC_DAYS = 0x0C, + FTRTC010_RTC_ALARM_SECOND = 0x10, + FTRTC010_RTC_ALARM_MINUTE = 0x14, + FTRTC010_RTC_ALARM_HOUR = 0x18, + FTRTC010_RTC_RECORD = 0x1C, + FTRTC010_RTC_CR = 0x20, +}; + +static irqreturn_t ftrtc010_rtc_interrupt(int irq, void *dev) +{ + return IRQ_HANDLED; +} + +/* + * Looks like the RTC in the Gemini SoC is (totaly) broken + * We can't read/write directly the time from RTC registers. + * We must do some "offset" calculation to get the real time + * + * This FIX works pretty fine and Stormlinksemi aka Cortina-Networks does + * the same thing, without the rtc-lib.c calls. + */ + +static int ftrtc010_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct ftrtc010_rtc *rtc = dev_get_drvdata(dev); + + u32 days, hour, min, sec, offset; + timeu64_t time; + + sec = readl(rtc->rtc_base + FTRTC010_RTC_SECOND); + min = readl(rtc->rtc_base + FTRTC010_RTC_MINUTE); + hour = readl(rtc->rtc_base + FTRTC010_RTC_HOUR); + days = readl(rtc->rtc_base + FTRTC010_RTC_DAYS); + offset = readl(rtc->rtc_base + FTRTC010_RTC_RECORD); + + time = offset + days * 86400 + hour * 3600 + min * 60 + sec; + + rtc_time64_to_tm(time, tm); + + return 0; +} + +static int ftrtc010_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct ftrtc010_rtc *rtc = dev_get_drvdata(dev); + u32 sec, min, hour, day, offset; + timeu64_t time; + + time = rtc_tm_to_time64(tm); + + sec = readl(rtc->rtc_base + FTRTC010_RTC_SECOND); + min = readl(rtc->rtc_base + FTRTC010_RTC_MINUTE); + hour = readl(rtc->rtc_base + FTRTC010_RTC_HOUR); + day = readl(rtc->rtc_base + FTRTC010_RTC_DAYS); + + offset = time - (day * 86400 + hour * 3600 + min * 60 + sec); + + writel(offset, rtc->rtc_base + FTRTC010_RTC_RECORD); + writel(0x01, rtc->rtc_base + FTRTC010_RTC_CR); + + return 0; +} + +static const struct rtc_class_ops ftrtc010_rtc_ops = { + .read_time = ftrtc010_rtc_read_time, + .set_time = ftrtc010_rtc_set_time, +}; + +static int ftrtc010_rtc_probe(struct platform_device *pdev) +{ + u32 days, hour, min, sec; + struct ftrtc010_rtc *rtc; + struct device *dev = &pdev->dev; + struct resource *res; + struct rtc_device *rtc_dev; + int ret; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (unlikely(!rtc)) + return -ENOMEM; + platform_set_drvdata(pdev, rtc); + + rtc->pclk = devm_clk_get(dev, "PCLK"); + if (IS_ERR(rtc->pclk)) { + dev_err(dev, "could not get PCLK\n"); + } else { + ret = clk_prepare_enable(rtc->pclk); + if (ret) { + dev_err(dev, "failed to enable PCLK\n"); + return ret; + } + } + rtc->extclk = devm_clk_get(dev, "EXTCLK"); + if (IS_ERR(rtc->extclk)) { + dev_err(dev, "could not get EXTCLK\n"); + } else { + ret = clk_prepare_enable(rtc->extclk); + if (ret) { + dev_err(dev, "failed to enable EXTCLK\n"); + goto err_disable_pclk; + } + } + + rtc->rtc_irq = platform_get_irq(pdev, 0); + if (rtc->rtc_irq < 0) { + ret = rtc->rtc_irq; + goto err_disable_extclk; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENODEV; + goto err_disable_extclk; + } + + rtc->rtc_base = devm_ioremap(dev, res->start, + resource_size(res)); + if (!rtc->rtc_base) { + ret = -ENOMEM; + goto err_disable_extclk; + } + + rtc_dev = devm_rtc_allocate_device(dev); + if (IS_ERR(rtc_dev)) { + ret = PTR_ERR(rtc_dev); + goto err_disable_extclk; + } + + rtc_dev->ops = &ftrtc010_rtc_ops; + + sec = readl(rtc->rtc_base + FTRTC010_RTC_SECOND); + min = readl(rtc->rtc_base + FTRTC010_RTC_MINUTE); + hour = readl(rtc->rtc_base + FTRTC010_RTC_HOUR); + days = readl(rtc->rtc_base + FTRTC010_RTC_DAYS); + + rtc_dev->range_min = (u64)days * 86400 + hour * 3600 + min * 60 + sec; + rtc_dev->range_max = U32_MAX + rtc_dev->range_min; + + ret = devm_request_irq(dev, rtc->rtc_irq, ftrtc010_rtc_interrupt, + IRQF_SHARED, pdev->name, dev); + if (unlikely(ret)) + goto err_disable_extclk; + + return devm_rtc_register_device(rtc_dev); + +err_disable_extclk: + clk_disable_unprepare(rtc->extclk); +err_disable_pclk: + clk_disable_unprepare(rtc->pclk); + return ret; +} + +static void ftrtc010_rtc_remove(struct platform_device *pdev) +{ + struct ftrtc010_rtc *rtc = platform_get_drvdata(pdev); + + if (!IS_ERR(rtc->extclk)) + clk_disable_unprepare(rtc->extclk); + if (!IS_ERR(rtc->pclk)) + clk_disable_unprepare(rtc->pclk); +} + +static const struct of_device_id ftrtc010_rtc_dt_match[] = { + { .compatible = "cortina,gemini-rtc" }, + { .compatible = "faraday,ftrtc010" }, + { } +}; +MODULE_DEVICE_TABLE(of, ftrtc010_rtc_dt_match); + +static struct platform_driver ftrtc010_rtc_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = ftrtc010_rtc_dt_match, + }, + .probe = ftrtc010_rtc_probe, + .remove = ftrtc010_rtc_remove, +}; + +module_platform_driver_probe(ftrtc010_rtc_driver, ftrtc010_rtc_probe); diff --git a/drivers/rtc/rtc-gamecube.c b/drivers/rtc/rtc-gamecube.c new file mode 100644 index 000000000..889028cec --- /dev/null +++ b/drivers/rtc/rtc-gamecube.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Nintendo GameCube, Wii and Wii U RTC driver + * + * This driver is for the MX23L4005, more specifically its real-time clock and + * SRAM storage. The value returned by the RTC counter must be added with the + * offset stored in a bias register in SRAM (on the GameCube and Wii) or in + * /config/rtc.xml (on the Wii U). The latter being very impractical to access + * from Linux, this driver assumes the bootloader has read it and stored it in + * SRAM like for the other two consoles. + * + * This device sits on a bus named EXI (which is similar to SPI), channel 0, + * device 1. This driver assumes no other user of the EXI bus, which is + * currently the case but would have to be reworked to add support for other + * GameCube hardware exposed on this bus. + * + * References: + * - https://wiiubrew.org/wiki/Hardware/RTC + * - https://wiibrew.org/wiki/MX23L4005 + * + * Copyright (C) 2018 rw-r-r-0644 + * Copyright (C) 2021 Emmanuel Gil Peyrot + * + * Based on rtc-gcn.c + * Copyright (C) 2004-2009 The GameCube Linux Team + * Copyright (C) 2005,2008,2009 Albert Herranz + * Based on gamecube_time.c from Torben Nielsen. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* EXI registers */ +#define EXICSR 0 +#define EXICR 12 +#define EXIDATA 16 + +/* EXI register values */ +#define EXICSR_DEV 0x380 + #define EXICSR_DEV1 0x100 +#define EXICSR_CLK 0x070 + #define EXICSR_CLK_1MHZ 0x000 + #define EXICSR_CLK_2MHZ 0x010 + #define EXICSR_CLK_4MHZ 0x020 + #define EXICSR_CLK_8MHZ 0x030 + #define EXICSR_CLK_16MHZ 0x040 + #define EXICSR_CLK_32MHZ 0x050 +#define EXICSR_INT 0x008 + #define EXICSR_INTSET 0x008 + +#define EXICR_TSTART 0x001 +#define EXICR_TRSMODE 0x002 + #define EXICR_TRSMODE_IMM 0x000 +#define EXICR_TRSTYPE 0x00C + #define EXICR_TRSTYPE_R 0x000 + #define EXICR_TRSTYPE_W 0x004 +#define EXICR_TLEN 0x030 + #define EXICR_TLEN32 0x030 + +/* EXI registers values to access the RTC */ +#define RTC_EXICSR (EXICSR_DEV1 | EXICSR_CLK_8MHZ | EXICSR_INTSET) +#define RTC_EXICR_W (EXICR_TSTART | EXICR_TRSMODE_IMM | EXICR_TRSTYPE_W | EXICR_TLEN32) +#define RTC_EXICR_R (EXICR_TSTART | EXICR_TRSMODE_IMM | EXICR_TRSTYPE_R | EXICR_TLEN32) +#define RTC_EXIDATA_W 0x80000000 + +/* RTC registers */ +#define RTC_COUNTER 0x200000 +#define RTC_SRAM 0x200001 +#define RTC_SRAM_BIAS 0x200004 +#define RTC_SNAPSHOT 0x204000 +#define RTC_ONTMR 0x210000 +#define RTC_OFFTMR 0x210001 +#define RTC_TEST0 0x210004 +#define RTC_TEST1 0x210005 +#define RTC_TEST2 0x210006 +#define RTC_TEST3 0x210007 +#define RTC_CONTROL0 0x21000c +#define RTC_CONTROL1 0x21000d + +/* RTC flags */ +#define RTC_CONTROL0_UNSTABLE_POWER 0x00000800 +#define RTC_CONTROL0_LOW_BATTERY 0x00000200 + +struct priv { + struct regmap *regmap; + void __iomem *iob; + u32 rtc_bias; +}; + +static int exi_read(void *context, u32 reg, u32 *data) +{ + struct priv *d = (struct priv *)context; + void __iomem *iob = d->iob; + + /* The spin loops here loop about 15~16 times each, so there is no need + * to use a more expensive sleep method. + */ + + /* Write register offset */ + iowrite32be(RTC_EXICSR, iob + EXICSR); + iowrite32be(reg << 8, iob + EXIDATA); + iowrite32be(RTC_EXICR_W, iob + EXICR); + while (!(ioread32be(iob + EXICSR) & EXICSR_INTSET)) + cpu_relax(); + + /* Read data */ + iowrite32be(RTC_EXICSR, iob + EXICSR); + iowrite32be(RTC_EXICR_R, iob + EXICR); + while (!(ioread32be(iob + EXICSR) & EXICSR_INTSET)) + cpu_relax(); + *data = ioread32be(iob + EXIDATA); + + /* Clear channel parameters */ + iowrite32be(0, iob + EXICSR); + + return 0; +} + +static int exi_write(void *context, u32 reg, u32 data) +{ + struct priv *d = (struct priv *)context; + void __iomem *iob = d->iob; + + /* The spin loops here loop about 15~16 times each, so there is no need + * to use a more expensive sleep method. + */ + + /* Write register offset */ + iowrite32be(RTC_EXICSR, iob + EXICSR); + iowrite32be(RTC_EXIDATA_W | (reg << 8), iob + EXIDATA); + iowrite32be(RTC_EXICR_W, iob + EXICR); + while (!(ioread32be(iob + EXICSR) & EXICSR_INTSET)) + cpu_relax(); + + /* Write data */ + iowrite32be(RTC_EXICSR, iob + EXICSR); + iowrite32be(data, iob + EXIDATA); + iowrite32be(RTC_EXICR_W, iob + EXICR); + while (!(ioread32be(iob + EXICSR) & EXICSR_INTSET)) + cpu_relax(); + + /* Clear channel parameters */ + iowrite32be(0, iob + EXICSR); + + return 0; +} + +static const struct regmap_bus exi_bus = { + /* TODO: is that true? Not that it matters here, but still. */ + .fast_io = true, + .reg_read = exi_read, + .reg_write = exi_write, +}; + +static int gamecube_rtc_read_time(struct device *dev, struct rtc_time *t) +{ + struct priv *d = dev_get_drvdata(dev); + int ret; + u32 counter; + time64_t timestamp; + + ret = regmap_read(d->regmap, RTC_COUNTER, &counter); + if (ret) + return ret; + + /* Add the counter and the bias to obtain the timestamp */ + timestamp = (time64_t)d->rtc_bias + counter; + rtc_time64_to_tm(timestamp, t); + + return 0; +} + +static int gamecube_rtc_set_time(struct device *dev, struct rtc_time *t) +{ + struct priv *d = dev_get_drvdata(dev); + time64_t timestamp; + + /* Subtract the timestamp and the bias to obtain the counter value */ + timestamp = rtc_tm_to_time64(t); + return regmap_write(d->regmap, RTC_COUNTER, timestamp - d->rtc_bias); +} + +static int gamecube_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct priv *d = dev_get_drvdata(dev); + int value; + int control0; + int ret; + + switch (cmd) { + case RTC_VL_READ: + ret = regmap_read(d->regmap, RTC_CONTROL0, &control0); + if (ret) + return ret; + + value = 0; + if (control0 & RTC_CONTROL0_UNSTABLE_POWER) + value |= RTC_VL_DATA_INVALID; + if (control0 & RTC_CONTROL0_LOW_BATTERY) + value |= RTC_VL_BACKUP_LOW; + return put_user(value, (unsigned int __user *)arg); + + default: + return -ENOIOCTLCMD; + } +} + +static const struct rtc_class_ops gamecube_rtc_ops = { + .read_time = gamecube_rtc_read_time, + .set_time = gamecube_rtc_set_time, + .ioctl = gamecube_rtc_ioctl, +}; + +static int gamecube_rtc_read_offset_from_sram(struct priv *d) +{ + struct device_node *np; + int ret; + struct resource res; + void __iomem *hw_srnprot; + u32 old; + + np = of_find_compatible_node(NULL, NULL, "nintendo,latte-srnprot"); + if (!np) + np = of_find_compatible_node(NULL, NULL, + "nintendo,hollywood-srnprot"); + if (!np) { + pr_info("HW_SRNPROT not found, assuming a GameCube\n"); + return regmap_read(d->regmap, RTC_SRAM_BIAS, &d->rtc_bias); + } + + ret = of_address_to_resource(np, 0, &res); + of_node_put(np); + if (ret) { + pr_err("no io memory range found\n"); + return -1; + } + + hw_srnprot = ioremap(res.start, resource_size(&res)); + if (!hw_srnprot) { + pr_err("failed to ioremap hw_srnprot\n"); + return -ENOMEM; + } + old = ioread32be(hw_srnprot); + + /* TODO: figure out why we use this magic constant. I obtained it by + * reading the leftover value after boot, after IOSU already ran. + * + * On my Wii U, setting this register to 1 prevents the console from + * rebooting properly, so wiiubrew.org must be missing something. + * + * See https://wiiubrew.org/wiki/Hardware/Latte_registers + */ + if (old != 0x7bf) + iowrite32be(0x7bf, hw_srnprot); + + /* Get the offset from RTC SRAM. + * + * Its default location on the GameCube and on the Wii is in the SRAM, + * while on the Wii U the bootloader needs to fill it with the contents + * of /config/rtc.xml on the SLC (the eMMC). We don’t do that from + * Linux since it requires implementing a proprietary filesystem and do + * file decryption, instead we require the bootloader to fill the same + * SRAM address as on previous consoles. + */ + ret = regmap_read(d->regmap, RTC_SRAM_BIAS, &d->rtc_bias); + + /* Reset SRAM access to how it was before, our job here is done. */ + if (old != 0x7bf) + iowrite32be(old, hw_srnprot); + + iounmap(hw_srnprot); + + if (ret) + pr_err("failed to get the RTC bias\n"); + + return ret; +} + +static const struct regmap_range rtc_rd_ranges[] = { + regmap_reg_range(0x200000, 0x200010), + regmap_reg_range(0x204000, 0x204000), + regmap_reg_range(0x210000, 0x210001), + regmap_reg_range(0x210004, 0x210007), + regmap_reg_range(0x21000c, 0x21000d), +}; + +static const struct regmap_access_table rtc_rd_regs = { + .yes_ranges = rtc_rd_ranges, + .n_yes_ranges = ARRAY_SIZE(rtc_rd_ranges), +}; + +static const struct regmap_range rtc_wr_ranges[] = { + regmap_reg_range(0x200000, 0x200010), + regmap_reg_range(0x204000, 0x204000), + regmap_reg_range(0x210000, 0x210001), + regmap_reg_range(0x21000d, 0x21000d), +}; + +static const struct regmap_access_table rtc_wr_regs = { + .yes_ranges = rtc_wr_ranges, + .n_yes_ranges = ARRAY_SIZE(rtc_wr_ranges), +}; + +static const struct regmap_config gamecube_rtc_regmap_config = { + .reg_bits = 24, + .val_bits = 32, + .rd_table = &rtc_rd_regs, + .wr_table = &rtc_wr_regs, + .max_register = 0x21000d, + .name = "gamecube-rtc", +}; + +static int gamecube_rtc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rtc_device *rtc; + struct priv *d; + int ret; + + d = devm_kzalloc(dev, sizeof(struct priv), GFP_KERNEL); + if (!d) + return -ENOMEM; + + d->iob = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(d->iob)) + return PTR_ERR(d->iob); + + d->regmap = devm_regmap_init(dev, &exi_bus, d, + &gamecube_rtc_regmap_config); + if (IS_ERR(d->regmap)) + return PTR_ERR(d->regmap); + + ret = gamecube_rtc_read_offset_from_sram(d); + if (ret) + return ret; + dev_dbg(dev, "SRAM bias: 0x%x", d->rtc_bias); + + dev_set_drvdata(dev, d); + + rtc = devm_rtc_allocate_device(dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + /* We can represent further than that, but it depends on the stored + * bias and we can’t modify it persistently on all supported consoles, + * so here we pretend to be limited to 2106. + */ + rtc->range_min = 0; + rtc->range_max = U32_MAX; + rtc->ops = &gamecube_rtc_ops; + + return devm_rtc_register_device(rtc); +} + +static const struct of_device_id gamecube_rtc_of_match[] = { + {.compatible = "nintendo,latte-exi" }, + {.compatible = "nintendo,hollywood-exi" }, + {.compatible = "nintendo,flipper-exi" }, + { } +}; +MODULE_DEVICE_TABLE(of, gamecube_rtc_of_match); + +static struct platform_driver gamecube_rtc_driver = { + .probe = gamecube_rtc_probe, + .driver = { + .name = "rtc-gamecube", + .of_match_table = gamecube_rtc_of_match, + }, +}; +module_platform_driver(gamecube_rtc_driver); + +MODULE_AUTHOR("Emmanuel Gil Peyrot "); +MODULE_DESCRIPTION("Nintendo GameCube, Wii and Wii U RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-generic.c b/drivers/rtc/rtc-generic.c new file mode 100644 index 000000000..89ae78e93 --- /dev/null +++ b/drivers/rtc/rtc-generic.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* rtc-generic: RTC driver using the generic RTC abstraction + * + * Copyright (C) 2008 Kyle McMartin + */ + +#include +#include +#include +#include +#include + +static int __init generic_rtc_probe(struct platform_device *dev) +{ + struct rtc_device *rtc; + const struct rtc_class_ops *ops = dev_get_platdata(&dev->dev); + + rtc = devm_rtc_device_register(&dev->dev, "rtc-generic", + ops, THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + platform_set_drvdata(dev, rtc); + + return 0; +} + +static struct platform_driver generic_rtc_driver = { + .driver = { + .name = "rtc-generic", + }, +}; + +module_platform_driver_probe(generic_rtc_driver, generic_rtc_probe); + +MODULE_AUTHOR("Kyle McMartin "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic RTC driver"); +MODULE_ALIAS("platform:rtc-generic"); diff --git a/drivers/rtc/rtc-goldfish.c b/drivers/rtc/rtc-goldfish.c new file mode 100644 index 000000000..53ec7173c --- /dev/null +++ b/drivers/rtc/rtc-goldfish.c @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: GPL-2.0 +/* drivers/rtc/rtc-goldfish.c + * + * Copyright (C) 2007 Google, Inc. + * Copyright (C) 2017 Imagination Technologies Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include + +struct goldfish_rtc { + void __iomem *base; + int irq; + struct rtc_device *rtc; +}; + +static int goldfish_rtc_read_alarm(struct device *dev, + struct rtc_wkalrm *alrm) +{ + u64 rtc_alarm; + u64 rtc_alarm_low; + u64 rtc_alarm_high; + void __iomem *base; + struct goldfish_rtc *rtcdrv; + + rtcdrv = dev_get_drvdata(dev); + base = rtcdrv->base; + + rtc_alarm_low = gf_ioread32(base + TIMER_ALARM_LOW); + rtc_alarm_high = gf_ioread32(base + TIMER_ALARM_HIGH); + rtc_alarm = (rtc_alarm_high << 32) | rtc_alarm_low; + + do_div(rtc_alarm, NSEC_PER_SEC); + memset(alrm, 0, sizeof(struct rtc_wkalrm)); + + rtc_time64_to_tm(rtc_alarm, &alrm->time); + + if (gf_ioread32(base + TIMER_ALARM_STATUS)) + alrm->enabled = 1; + else + alrm->enabled = 0; + + return 0; +} + +static int goldfish_rtc_set_alarm(struct device *dev, + struct rtc_wkalrm *alrm) +{ + struct goldfish_rtc *rtcdrv; + u64 rtc_alarm64; + u64 rtc_status_reg; + void __iomem *base; + + rtcdrv = dev_get_drvdata(dev); + base = rtcdrv->base; + + if (alrm->enabled) { + rtc_alarm64 = rtc_tm_to_time64(&alrm->time) * NSEC_PER_SEC; + gf_iowrite32((rtc_alarm64 >> 32), base + TIMER_ALARM_HIGH); + gf_iowrite32(rtc_alarm64, base + TIMER_ALARM_LOW); + gf_iowrite32(1, base + TIMER_IRQ_ENABLED); + } else { + /* + * if this function was called with enabled=0 + * then it could mean that the application is + * trying to cancel an ongoing alarm + */ + rtc_status_reg = gf_ioread32(base + TIMER_ALARM_STATUS); + if (rtc_status_reg) + gf_iowrite32(1, base + TIMER_CLEAR_ALARM); + } + + return 0; +} + +static int goldfish_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + void __iomem *base; + struct goldfish_rtc *rtcdrv; + + rtcdrv = dev_get_drvdata(dev); + base = rtcdrv->base; + + if (enabled) + gf_iowrite32(1, base + TIMER_IRQ_ENABLED); + else + gf_iowrite32(0, base + TIMER_IRQ_ENABLED); + + return 0; +} + +static irqreturn_t goldfish_rtc_interrupt(int irq, void *dev_id) +{ + struct goldfish_rtc *rtcdrv = dev_id; + void __iomem *base = rtcdrv->base; + + gf_iowrite32(1, base + TIMER_CLEAR_INTERRUPT); + + rtc_update_irq(rtcdrv->rtc, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static int goldfish_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct goldfish_rtc *rtcdrv; + void __iomem *base; + u64 time_high; + u64 time_low; + u64 time; + + rtcdrv = dev_get_drvdata(dev); + base = rtcdrv->base; + + time_low = gf_ioread32(base + TIMER_TIME_LOW); + time_high = gf_ioread32(base + TIMER_TIME_HIGH); + time = (time_high << 32) | time_low; + + do_div(time, NSEC_PER_SEC); + + rtc_time64_to_tm(time, tm); + + return 0; +} + +static int goldfish_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct goldfish_rtc *rtcdrv; + void __iomem *base; + u64 now64; + + rtcdrv = dev_get_drvdata(dev); + base = rtcdrv->base; + + now64 = rtc_tm_to_time64(tm) * NSEC_PER_SEC; + gf_iowrite32((now64 >> 32), base + TIMER_TIME_HIGH); + gf_iowrite32(now64, base + TIMER_TIME_LOW); + + return 0; +} + +static const struct rtc_class_ops goldfish_rtc_ops = { + .read_time = goldfish_rtc_read_time, + .set_time = goldfish_rtc_set_time, + .read_alarm = goldfish_rtc_read_alarm, + .set_alarm = goldfish_rtc_set_alarm, + .alarm_irq_enable = goldfish_rtc_alarm_irq_enable +}; + +static int goldfish_rtc_probe(struct platform_device *pdev) +{ + struct goldfish_rtc *rtcdrv; + int err; + + rtcdrv = devm_kzalloc(&pdev->dev, sizeof(*rtcdrv), GFP_KERNEL); + if (!rtcdrv) + return -ENOMEM; + + platform_set_drvdata(pdev, rtcdrv); + rtcdrv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rtcdrv->base)) + return PTR_ERR(rtcdrv->base); + + rtcdrv->irq = platform_get_irq(pdev, 0); + if (rtcdrv->irq < 0) + return -ENODEV; + + rtcdrv->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtcdrv->rtc)) + return PTR_ERR(rtcdrv->rtc); + + rtcdrv->rtc->ops = &goldfish_rtc_ops; + rtcdrv->rtc->range_max = U64_MAX / NSEC_PER_SEC; + + err = devm_request_irq(&pdev->dev, rtcdrv->irq, + goldfish_rtc_interrupt, + 0, pdev->name, rtcdrv); + if (err) + return err; + + return devm_rtc_register_device(rtcdrv->rtc); +} + +static const struct of_device_id goldfish_rtc_of_match[] = { + { .compatible = "google,goldfish-rtc", }, + {}, +}; +MODULE_DEVICE_TABLE(of, goldfish_rtc_of_match); + +static struct platform_driver goldfish_rtc = { + .probe = goldfish_rtc_probe, + .driver = { + .name = "goldfish_rtc", + .of_match_table = goldfish_rtc_of_match, + } +}; + +module_platform_driver(goldfish_rtc); + +MODULE_DESCRIPTION("Android Goldfish Real Time Clock driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-hid-sensor-time.c b/drivers/rtc/rtc-hid-sensor-time.c new file mode 100644 index 000000000..6228d0b24 --- /dev/null +++ b/drivers/rtc/rtc-hid-sensor-time.c @@ -0,0 +1,329 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * HID Sensor Time Driver + * Copyright (c) 2012, Alexander Holler. + */ +#include +#include +#include +#include +#include +#include + +enum hid_time_channel { + CHANNEL_SCAN_INDEX_YEAR, + CHANNEL_SCAN_INDEX_MONTH, + CHANNEL_SCAN_INDEX_DAY, + CHANNEL_SCAN_INDEX_HOUR, + CHANNEL_SCAN_INDEX_MINUTE, + CHANNEL_SCAN_INDEX_SECOND, + TIME_RTC_CHANNEL_MAX, +}; + +struct hid_time_state { + struct hid_sensor_hub_callbacks callbacks; + struct hid_sensor_common common_attributes; + struct hid_sensor_hub_attribute_info info[TIME_RTC_CHANNEL_MAX]; + struct rtc_time last_time; + spinlock_t lock_last_time; + struct completion comp_last_time; + struct rtc_time time_buf; + struct rtc_device *rtc; +}; + +static const u32 hid_time_addresses[TIME_RTC_CHANNEL_MAX] = { + HID_USAGE_SENSOR_TIME_YEAR, + HID_USAGE_SENSOR_TIME_MONTH, + HID_USAGE_SENSOR_TIME_DAY, + HID_USAGE_SENSOR_TIME_HOUR, + HID_USAGE_SENSOR_TIME_MINUTE, + HID_USAGE_SENSOR_TIME_SECOND, +}; + +/* Channel names for verbose error messages */ +static const char * const hid_time_channel_names[TIME_RTC_CHANNEL_MAX] = { + "year", "month", "day", "hour", "minute", "second", +}; + +/* Callback handler to send event after all samples are received and captured */ +static int hid_time_proc_event(struct hid_sensor_hub_device *hsdev, + unsigned usage_id, void *priv) +{ + unsigned long flags; + struct hid_time_state *time_state = platform_get_drvdata(priv); + + spin_lock_irqsave(&time_state->lock_last_time, flags); + time_state->last_time = time_state->time_buf; + spin_unlock_irqrestore(&time_state->lock_last_time, flags); + complete(&time_state->comp_last_time); + return 0; +} + +static u32 hid_time_value(size_t raw_len, char *raw_data) +{ + switch (raw_len) { + case 1: + return *(u8 *)raw_data; + case 2: + return *(u16 *)raw_data; + case 4: + return *(u32 *)raw_data; + default: + return (u32)(~0U); /* 0xff... or -1 to denote an error */ + } +} + +static int hid_time_capture_sample(struct hid_sensor_hub_device *hsdev, + unsigned usage_id, size_t raw_len, + char *raw_data, void *priv) +{ + struct hid_time_state *time_state = platform_get_drvdata(priv); + struct rtc_time *time_buf = &time_state->time_buf; + + switch (usage_id) { + case HID_USAGE_SENSOR_TIME_YEAR: + /* + * The draft for HID-sensors (HUTRR39) currently doesn't define + * the range for the year attribute. Therefor we support + * 8 bit (0-99) and 16 or 32 bits (full) as size for the year. + */ + if (raw_len == 1) { + time_buf->tm_year = *(u8 *)raw_data; + if (time_buf->tm_year < 70) + /* assume we are in 1970...2069 */ + time_buf->tm_year += 100; + } else + time_buf->tm_year = + (int)hid_time_value(raw_len, raw_data)-1900; + break; + case HID_USAGE_SENSOR_TIME_MONTH: + /* sensors are sending the month as 1-12, we need 0-11 */ + time_buf->tm_mon = (int)hid_time_value(raw_len, raw_data)-1; + break; + case HID_USAGE_SENSOR_TIME_DAY: + time_buf->tm_mday = (int)hid_time_value(raw_len, raw_data); + break; + case HID_USAGE_SENSOR_TIME_HOUR: + time_buf->tm_hour = (int)hid_time_value(raw_len, raw_data); + break; + case HID_USAGE_SENSOR_TIME_MINUTE: + time_buf->tm_min = (int)hid_time_value(raw_len, raw_data); + break; + case HID_USAGE_SENSOR_TIME_SECOND: + time_buf->tm_sec = (int)hid_time_value(raw_len, raw_data); + break; + default: + return -EINVAL; + } + return 0; +} + +/* small helper, haven't found any other way */ +static const char *hid_time_attrib_name(u32 attrib_id) +{ + static const char unknown[] = "unknown"; + unsigned i; + + for (i = 0; i < TIME_RTC_CHANNEL_MAX; ++i) { + if (hid_time_addresses[i] == attrib_id) + return hid_time_channel_names[i]; + } + return unknown; /* should never happen */ +} + +static int hid_time_parse_report(struct platform_device *pdev, + struct hid_sensor_hub_device *hsdev, + unsigned usage_id, + struct hid_time_state *time_state) +{ + int report_id, i; + + for (i = 0; i < TIME_RTC_CHANNEL_MAX; ++i) + if (sensor_hub_input_get_attribute_info(hsdev, + HID_INPUT_REPORT, usage_id, + hid_time_addresses[i], + &time_state->info[i]) < 0) + return -EINVAL; + /* Check the (needed) attributes for sanity */ + report_id = time_state->info[0].report_id; + if (report_id < 0) { + dev_err(&pdev->dev, "bad report ID!\n"); + return -EINVAL; + } + for (i = 0; i < TIME_RTC_CHANNEL_MAX; ++i) { + if (time_state->info[i].report_id != report_id) { + dev_err(&pdev->dev, + "not all needed attributes inside the same report!\n"); + return -EINVAL; + } + if (time_state->info[i].size == 3 || + time_state->info[i].size > 4) { + dev_err(&pdev->dev, + "attribute '%s' not 8, 16 or 32 bits wide!\n", + hid_time_attrib_name( + time_state->info[i].attrib_id)); + return -EINVAL; + } + if (time_state->info[i].units != + HID_USAGE_SENSOR_UNITS_NOT_SPECIFIED && + /* allow attribute seconds with unit seconds */ + !(time_state->info[i].attrib_id == + HID_USAGE_SENSOR_TIME_SECOND && + time_state->info[i].units == + HID_USAGE_SENSOR_UNITS_SECOND)) { + dev_err(&pdev->dev, + "attribute '%s' hasn't a unit of type 'none'!\n", + hid_time_attrib_name( + time_state->info[i].attrib_id)); + return -EINVAL; + } + if (time_state->info[i].unit_expo) { + dev_err(&pdev->dev, + "attribute '%s' hasn't a unit exponent of 1!\n", + hid_time_attrib_name( + time_state->info[i].attrib_id)); + return -EINVAL; + } + } + + return 0; +} + +static int hid_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + unsigned long flags; + struct hid_time_state *time_state = dev_get_drvdata(dev); + int ret; + + reinit_completion(&time_state->comp_last_time); + /* get a report with all values through requesting one value */ + sensor_hub_input_attr_get_raw_value(time_state->common_attributes.hsdev, + HID_USAGE_SENSOR_TIME, hid_time_addresses[0], + time_state->info[0].report_id, SENSOR_HUB_SYNC, false); + /* wait for all values (event) */ + ret = wait_for_completion_killable_timeout( + &time_state->comp_last_time, HZ*6); + if (ret > 0) { + /* no error */ + spin_lock_irqsave(&time_state->lock_last_time, flags); + *tm = time_state->last_time; + spin_unlock_irqrestore(&time_state->lock_last_time, flags); + return 0; + } + if (!ret) + return -EIO; /* timeouted */ + return ret; /* killed (-ERESTARTSYS) */ +} + +static const struct rtc_class_ops hid_time_rtc_ops = { + .read_time = hid_rtc_read_time, +}; + +static int hid_time_probe(struct platform_device *pdev) +{ + int ret = 0; + struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); + struct hid_time_state *time_state = devm_kzalloc(&pdev->dev, + sizeof(struct hid_time_state), GFP_KERNEL); + + if (time_state == NULL) + return -ENOMEM; + + platform_set_drvdata(pdev, time_state); + + spin_lock_init(&time_state->lock_last_time); + init_completion(&time_state->comp_last_time); + time_state->common_attributes.hsdev = hsdev; + time_state->common_attributes.pdev = pdev; + + ret = hid_sensor_parse_common_attributes(hsdev, + HID_USAGE_SENSOR_TIME, + &time_state->common_attributes, + NULL, + 0); + if (ret) { + dev_err(&pdev->dev, "failed to setup common attributes!\n"); + return ret; + } + + ret = hid_time_parse_report(pdev, hsdev, HID_USAGE_SENSOR_TIME, + time_state); + if (ret) { + dev_err(&pdev->dev, "failed to setup attributes!\n"); + return ret; + } + + time_state->callbacks.send_event = hid_time_proc_event; + time_state->callbacks.capture_sample = hid_time_capture_sample; + time_state->callbacks.pdev = pdev; + ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_TIME, + &time_state->callbacks); + if (ret < 0) { + dev_err(&pdev->dev, "register callback failed!\n"); + return ret; + } + + ret = sensor_hub_device_open(hsdev); + if (ret) { + dev_err(&pdev->dev, "failed to open sensor hub device!\n"); + goto err_open; + } + + /* + * Enable HID input processing early in order to be able to read the + * clock already in devm_rtc_device_register(). + */ + hid_device_io_start(hsdev->hdev); + + time_state->rtc = devm_rtc_device_register(&pdev->dev, + "hid-sensor-time", &hid_time_rtc_ops, + THIS_MODULE); + + if (IS_ERR(time_state->rtc)) { + hid_device_io_stop(hsdev->hdev); + ret = PTR_ERR(time_state->rtc); + time_state->rtc = NULL; + dev_err(&pdev->dev, "rtc device register failed!\n"); + goto err_rtc; + } + + return ret; + +err_rtc: + sensor_hub_device_close(hsdev); +err_open: + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_TIME); + return ret; +} + +static void hid_time_remove(struct platform_device *pdev) +{ + struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); + + sensor_hub_device_close(hsdev); + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_TIME); +} + +static const struct platform_device_id hid_time_ids[] = { + { + /* Format: HID-SENSOR-usage_id_in_hex_lowercase */ + .name = "HID-SENSOR-2000a0", + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, hid_time_ids); + +static struct platform_driver hid_time_platform_driver = { + .id_table = hid_time_ids, + .driver = { + .name = KBUILD_MODNAME, + }, + .probe = hid_time_probe, + .remove = hid_time_remove, +}; +module_platform_driver(hid_time_platform_driver); + +MODULE_DESCRIPTION("HID Sensor Time"); +MODULE_AUTHOR("Alexander Holler "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_HID"); diff --git a/drivers/rtc/rtc-hym8563.c b/drivers/rtc/rtc-hym8563.c new file mode 100644 index 000000000..1d52cc3de --- /dev/null +++ b/drivers/rtc/rtc-hym8563.c @@ -0,0 +1,592 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Haoyu HYM8563 RTC driver + * + * Copyright (C) 2013 MundoReader S.L. + * Author: Heiko Stuebner + * + * based on rtc-HYM8563 + * Copyright (C) 2010 ROCKCHIP, Inc. + */ + +#include +#include +#include +#include +#include + +#define HYM8563_CTL1 0x00 +#define HYM8563_CTL1_TEST BIT(7) +#define HYM8563_CTL1_STOP BIT(5) +#define HYM8563_CTL1_TESTC BIT(3) + +#define HYM8563_CTL2 0x01 +#define HYM8563_CTL2_TI_TP BIT(4) +#define HYM8563_CTL2_AF BIT(3) +#define HYM8563_CTL2_TF BIT(2) +#define HYM8563_CTL2_AIE BIT(1) +#define HYM8563_CTL2_TIE BIT(0) + +#define HYM8563_SEC 0x02 +#define HYM8563_SEC_VL BIT(7) +#define HYM8563_SEC_MASK 0x7f + +#define HYM8563_MIN 0x03 +#define HYM8563_MIN_MASK 0x7f + +#define HYM8563_HOUR 0x04 +#define HYM8563_HOUR_MASK 0x3f + +#define HYM8563_DAY 0x05 +#define HYM8563_DAY_MASK 0x3f + +#define HYM8563_WEEKDAY 0x06 +#define HYM8563_WEEKDAY_MASK 0x07 + +#define HYM8563_MONTH 0x07 +#define HYM8563_MONTH_CENTURY BIT(7) +#define HYM8563_MONTH_MASK 0x1f + +#define HYM8563_YEAR 0x08 + +#define HYM8563_ALM_MIN 0x09 +#define HYM8563_ALM_HOUR 0x0a +#define HYM8563_ALM_DAY 0x0b +#define HYM8563_ALM_WEEK 0x0c + +/* Each alarm check can be disabled by setting this bit in the register */ +#define HYM8563_ALM_BIT_DISABLE BIT(7) + +#define HYM8563_CLKOUT 0x0d +#define HYM8563_CLKOUT_ENABLE BIT(7) +#define HYM8563_CLKOUT_32768 0 +#define HYM8563_CLKOUT_1024 1 +#define HYM8563_CLKOUT_32 2 +#define HYM8563_CLKOUT_1 3 +#define HYM8563_CLKOUT_MASK 3 + +#define HYM8563_TMR_CTL 0x0e +#define HYM8563_TMR_CTL_ENABLE BIT(7) +#define HYM8563_TMR_CTL_4096 0 +#define HYM8563_TMR_CTL_64 1 +#define HYM8563_TMR_CTL_1 2 +#define HYM8563_TMR_CTL_1_60 3 +#define HYM8563_TMR_CTL_MASK 3 + +#define HYM8563_TMR_CNT 0x0f + +struct hym8563 { + struct i2c_client *client; + struct rtc_device *rtc; +#ifdef CONFIG_COMMON_CLK + struct clk_hw clkout_hw; +#endif +}; + +/* + * RTC handling + */ + +static int hym8563_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 buf[7]; + int ret; + + ret = i2c_smbus_read_i2c_block_data(client, HYM8563_SEC, 7, buf); + if (ret < 0) + return ret; + + if (buf[0] & HYM8563_SEC_VL) { + dev_warn(&client->dev, + "no valid clock/calendar values available\n"); + return -EINVAL; + } + + tm->tm_sec = bcd2bin(buf[0] & HYM8563_SEC_MASK); + tm->tm_min = bcd2bin(buf[1] & HYM8563_MIN_MASK); + tm->tm_hour = bcd2bin(buf[2] & HYM8563_HOUR_MASK); + tm->tm_mday = bcd2bin(buf[3] & HYM8563_DAY_MASK); + tm->tm_wday = bcd2bin(buf[4] & HYM8563_WEEKDAY_MASK); /* 0 = Sun */ + tm->tm_mon = bcd2bin(buf[5] & HYM8563_MONTH_MASK) - 1; /* 0 = Jan */ + tm->tm_year = bcd2bin(buf[6]) + 100; + + return 0; +} + +static int hym8563_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 buf[7]; + int ret; + + /* Years >= 2100 are to far in the future, 19XX is to early */ + if (tm->tm_year < 100 || tm->tm_year >= 200) + return -EINVAL; + + buf[0] = bin2bcd(tm->tm_sec); + buf[1] = bin2bcd(tm->tm_min); + buf[2] = bin2bcd(tm->tm_hour); + buf[3] = bin2bcd(tm->tm_mday); + buf[4] = bin2bcd(tm->tm_wday); + buf[5] = bin2bcd(tm->tm_mon + 1); + + /* + * While the HYM8563 has a century flag in the month register, + * it does not seem to carry it over a subsequent write/read. + * So we'll limit ourself to 100 years, starting at 2000 for now. + */ + buf[6] = bin2bcd(tm->tm_year - 100); + + /* + * CTL1 only contains TEST-mode bits apart from stop, + * so no need to read the value first + */ + ret = i2c_smbus_write_byte_data(client, HYM8563_CTL1, + HYM8563_CTL1_STOP); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_i2c_block_data(client, HYM8563_SEC, 7, buf); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, HYM8563_CTL1, 0); + if (ret < 0) + return ret; + + return 0; +} + +static int hym8563_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct i2c_client *client = to_i2c_client(dev); + int data; + + data = i2c_smbus_read_byte_data(client, HYM8563_CTL2); + if (data < 0) + return data; + + if (enabled) + data |= HYM8563_CTL2_AIE; + else + data &= ~HYM8563_CTL2_AIE; + + return i2c_smbus_write_byte_data(client, HYM8563_CTL2, data); +}; + +static int hym8563_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rtc_time *alm_tm = &alm->time; + u8 buf[4]; + int ret; + + ret = i2c_smbus_read_i2c_block_data(client, HYM8563_ALM_MIN, 4, buf); + if (ret < 0) + return ret; + + /* The alarm only has a minute accuracy */ + alm_tm->tm_sec = 0; + + alm_tm->tm_min = (buf[0] & HYM8563_ALM_BIT_DISABLE) ? + -1 : + bcd2bin(buf[0] & HYM8563_MIN_MASK); + alm_tm->tm_hour = (buf[1] & HYM8563_ALM_BIT_DISABLE) ? + -1 : + bcd2bin(buf[1] & HYM8563_HOUR_MASK); + alm_tm->tm_mday = (buf[2] & HYM8563_ALM_BIT_DISABLE) ? + -1 : + bcd2bin(buf[2] & HYM8563_DAY_MASK); + alm_tm->tm_wday = (buf[3] & HYM8563_ALM_BIT_DISABLE) ? + -1 : + bcd2bin(buf[3] & HYM8563_WEEKDAY_MASK); + + ret = i2c_smbus_read_byte_data(client, HYM8563_CTL2); + if (ret < 0) + return ret; + + if (ret & HYM8563_CTL2_AIE) + alm->enabled = 1; + + return 0; +} + +static int hym8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rtc_time *alm_tm = &alm->time; + u8 buf[4]; + int ret; + + ret = i2c_smbus_read_byte_data(client, HYM8563_CTL2); + if (ret < 0) + return ret; + + ret &= ~HYM8563_CTL2_AIE; + + ret = i2c_smbus_write_byte_data(client, HYM8563_CTL2, ret); + if (ret < 0) + return ret; + + buf[0] = (alm_tm->tm_min < 60 && alm_tm->tm_min >= 0) ? + bin2bcd(alm_tm->tm_min) : HYM8563_ALM_BIT_DISABLE; + + buf[1] = (alm_tm->tm_hour < 24 && alm_tm->tm_hour >= 0) ? + bin2bcd(alm_tm->tm_hour) : HYM8563_ALM_BIT_DISABLE; + + buf[2] = (alm_tm->tm_mday <= 31 && alm_tm->tm_mday >= 1) ? + bin2bcd(alm_tm->tm_mday) : HYM8563_ALM_BIT_DISABLE; + + buf[3] = (alm_tm->tm_wday < 7 && alm_tm->tm_wday >= 0) ? + bin2bcd(alm_tm->tm_wday) : HYM8563_ALM_BIT_DISABLE; + + ret = i2c_smbus_write_i2c_block_data(client, HYM8563_ALM_MIN, 4, buf); + if (ret < 0) + return ret; + + return hym8563_rtc_alarm_irq_enable(dev, alm->enabled); +} + +static const struct rtc_class_ops hym8563_rtc_ops = { + .read_time = hym8563_rtc_read_time, + .set_time = hym8563_rtc_set_time, + .alarm_irq_enable = hym8563_rtc_alarm_irq_enable, + .read_alarm = hym8563_rtc_read_alarm, + .set_alarm = hym8563_rtc_set_alarm, +}; + +/* + * Handling of the clkout + */ + +#ifdef CONFIG_COMMON_CLK +#define clkout_hw_to_hym8563(_hw) container_of(_hw, struct hym8563, clkout_hw) + +static int clkout_rates[] = { + 32768, + 1024, + 32, + 1, +}; + +static unsigned long hym8563_clkout_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct hym8563 *hym8563 = clkout_hw_to_hym8563(hw); + struct i2c_client *client = hym8563->client; + int ret = i2c_smbus_read_byte_data(client, HYM8563_CLKOUT); + + if (ret < 0) + return 0; + + ret &= HYM8563_CLKOUT_MASK; + return clkout_rates[ret]; +} + +static int hym8563_clkout_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(clkout_rates); i++) + if (clkout_rates[i] <= req->rate) { + req->rate = clkout_rates[i]; + + return 0; + } + + req->rate = clkout_rates[0]; + + return 0; +} + +static int hym8563_clkout_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct hym8563 *hym8563 = clkout_hw_to_hym8563(hw); + struct i2c_client *client = hym8563->client; + int ret = i2c_smbus_read_byte_data(client, HYM8563_CLKOUT); + int i; + + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(clkout_rates); i++) + if (clkout_rates[i] == rate) { + ret &= ~HYM8563_CLKOUT_MASK; + ret |= i; + return i2c_smbus_write_byte_data(client, + HYM8563_CLKOUT, ret); + } + + return -EINVAL; +} + +static int hym8563_clkout_control(struct clk_hw *hw, bool enable) +{ + struct hym8563 *hym8563 = clkout_hw_to_hym8563(hw); + struct i2c_client *client = hym8563->client; + int ret = i2c_smbus_read_byte_data(client, HYM8563_CLKOUT); + + if (ret < 0) + return ret; + + if (enable) + ret |= HYM8563_CLKOUT_ENABLE; + else + ret &= ~HYM8563_CLKOUT_ENABLE; + + return i2c_smbus_write_byte_data(client, HYM8563_CLKOUT, ret); +} + +static int hym8563_clkout_prepare(struct clk_hw *hw) +{ + return hym8563_clkout_control(hw, 1); +} + +static void hym8563_clkout_unprepare(struct clk_hw *hw) +{ + hym8563_clkout_control(hw, 0); +} + +static int hym8563_clkout_is_prepared(struct clk_hw *hw) +{ + struct hym8563 *hym8563 = clkout_hw_to_hym8563(hw); + struct i2c_client *client = hym8563->client; + int ret = i2c_smbus_read_byte_data(client, HYM8563_CLKOUT); + + if (ret < 0) + return ret; + + return !!(ret & HYM8563_CLKOUT_ENABLE); +} + +static const struct clk_ops hym8563_clkout_ops = { + .prepare = hym8563_clkout_prepare, + .unprepare = hym8563_clkout_unprepare, + .is_prepared = hym8563_clkout_is_prepared, + .recalc_rate = hym8563_clkout_recalc_rate, + .determine_rate = hym8563_clkout_determine_rate, + .set_rate = hym8563_clkout_set_rate, +}; + +static struct clk *hym8563_clkout_register_clk(struct hym8563 *hym8563) +{ + struct i2c_client *client = hym8563->client; + struct device_node *node = client->dev.of_node; + struct clk_init_data init = {}; + struct clk *clk; + int ret; + + ret = i2c_smbus_write_byte_data(client, HYM8563_CLKOUT, + 0); + if (ret < 0) + return ERR_PTR(ret); + + init.name = "hym8563-clkout"; + init.ops = &hym8563_clkout_ops; + init.flags = 0; + init.parent_names = NULL; + init.num_parents = 0; + hym8563->clkout_hw.init = &init; + + /* optional override of the clockname */ + of_property_read_string(node, "clock-output-names", &init.name); + + /* register the clock */ + clk = clk_register(&client->dev, &hym8563->clkout_hw); + + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); + + return clk; +} +#endif + +/* + * The alarm interrupt is implemented as a level-low interrupt in the + * hym8563, while the timer interrupt uses a falling edge. + * We don't use the timer at all, so the interrupt is requested to + * use the level-low trigger. + */ +static irqreturn_t hym8563_irq(int irq, void *dev_id) +{ + struct hym8563 *hym8563 = (struct hym8563 *)dev_id; + struct i2c_client *client = hym8563->client; + int data, ret; + + rtc_lock(hym8563->rtc); + + /* Clear the alarm flag */ + + data = i2c_smbus_read_byte_data(client, HYM8563_CTL2); + if (data < 0) { + dev_err(&client->dev, "%s: error reading i2c data %d\n", + __func__, data); + goto out; + } + + data &= ~HYM8563_CTL2_AF; + + ret = i2c_smbus_write_byte_data(client, HYM8563_CTL2, data); + if (ret < 0) { + dev_err(&client->dev, "%s: error writing i2c data %d\n", + __func__, ret); + } + +out: + rtc_unlock(hym8563->rtc); + return IRQ_HANDLED; +} + +static int hym8563_init_device(struct i2c_client *client) +{ + int ret; + + /* Clear stop flag if present */ + ret = i2c_smbus_write_byte_data(client, HYM8563_CTL1, 0); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_byte_data(client, HYM8563_CTL2); + if (ret < 0) + return ret; + + /* Disable alarm and timer interrupts */ + ret &= ~HYM8563_CTL2_AIE; + ret &= ~HYM8563_CTL2_TIE; + + /* Clear any pending alarm and timer flags */ + if (ret & HYM8563_CTL2_AF) + ret &= ~HYM8563_CTL2_AF; + + if (ret & HYM8563_CTL2_TF) + ret &= ~HYM8563_CTL2_TF; + + ret &= ~HYM8563_CTL2_TI_TP; + + return i2c_smbus_write_byte_data(client, HYM8563_CTL2, ret); +} + +#ifdef CONFIG_PM_SLEEP +static int hym8563_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + int ret; + + if (device_may_wakeup(dev)) { + ret = enable_irq_wake(client->irq); + if (ret) { + dev_err(dev, "enable_irq_wake failed, %d\n", ret); + return ret; + } + } + + return 0; +} + +static int hym8563_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(client->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(hym8563_pm_ops, hym8563_suspend, hym8563_resume); + +static int hym8563_probe(struct i2c_client *client) +{ + struct hym8563 *hym8563; + int ret; + + hym8563 = devm_kzalloc(&client->dev, sizeof(*hym8563), GFP_KERNEL); + if (!hym8563) + return -ENOMEM; + + hym8563->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(hym8563->rtc)) + return PTR_ERR(hym8563->rtc); + + hym8563->client = client; + i2c_set_clientdata(client, hym8563); + + ret = hym8563_init_device(client); + if (ret) { + dev_err(&client->dev, "could not init device, %d\n", ret); + return ret; + } + + if (client->irq > 0) { + unsigned long irqflags = IRQF_TRIGGER_LOW; + + if (dev_fwnode(&client->dev)) + irqflags = 0; + + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, hym8563_irq, + irqflags | IRQF_ONESHOT, + client->name, hym8563); + if (ret < 0) { + dev_err(&client->dev, "irq %d request failed, %d\n", + client->irq, ret); + return ret; + } + } + + if (client->irq > 0 || + device_property_read_bool(&client->dev, "wakeup-source")) { + device_init_wakeup(&client->dev, true); + } + + /* check state of calendar information */ + ret = i2c_smbus_read_byte_data(client, HYM8563_SEC); + if (ret < 0) + return ret; + + dev_dbg(&client->dev, "rtc information is %s\n", + (ret & HYM8563_SEC_VL) ? "invalid" : "valid"); + + hym8563->rtc->ops = &hym8563_rtc_ops; + set_bit(RTC_FEATURE_ALARM_RES_MINUTE, hym8563->rtc->features); + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, hym8563->rtc->features); + +#ifdef CONFIG_COMMON_CLK + hym8563_clkout_register_clk(hym8563); +#endif + + return devm_rtc_register_device(hym8563->rtc); +} + +static const struct i2c_device_id hym8563_id[] = { + { .name = "hym8563" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, hym8563_id); + +static const struct of_device_id hym8563_dt_idtable[] = { + { .compatible = "haoyu,hym8563" }, + {}, +}; +MODULE_DEVICE_TABLE(of, hym8563_dt_idtable); + +static struct i2c_driver hym8563_driver = { + .driver = { + .name = "rtc-hym8563", + .pm = &hym8563_pm_ops, + .of_match_table = hym8563_dt_idtable, + }, + .probe = hym8563_probe, + .id_table = hym8563_id, +}; + +module_i2c_driver(hym8563_driver); + +MODULE_AUTHOR("Heiko Stuebner "); +MODULE_DESCRIPTION("HYM8563 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-imx-sc.c b/drivers/rtc/rtc-imx-sc.c new file mode 100644 index 000000000..814d51664 --- /dev/null +++ b/drivers/rtc/rtc-imx-sc.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 NXP. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define IMX_SC_TIMER_FUNC_GET_RTC_SEC1970 9 +#define IMX_SC_TIMER_FUNC_SET_RTC_ALARM 8 +#define IMX_SC_TIMER_FUNC_SET_RTC_TIME 6 + +#define IMX_SIP_SRTC 0xC2000002 +#define IMX_SIP_SRTC_SET_TIME 0x0 + +#define SC_IRQ_GROUP_RTC 2 +#define SC_IRQ_RTC 1 + +static struct imx_sc_ipc *rtc_ipc_handle; +static struct rtc_device *imx_sc_rtc; + +struct imx_sc_msg_timer_get_rtc_time { + struct imx_sc_rpc_msg hdr; + u32 time; +} __packed; + +struct imx_sc_msg_timer_rtc_set_alarm { + struct imx_sc_rpc_msg hdr; + u16 year; + u8 mon; + u8 day; + u8 hour; + u8 min; + u8 sec; +} __packed __aligned(4); + +static int imx_sc_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct imx_sc_msg_timer_get_rtc_time msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + int ret; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_TIMER; + hdr->func = IMX_SC_TIMER_FUNC_GET_RTC_SEC1970; + hdr->size = 1; + + ret = imx_scu_call_rpc(rtc_ipc_handle, &msg, true); + if (ret) { + dev_err(dev, "read rtc time failed, ret %d\n", ret); + return ret; + } + + rtc_time64_to_tm(msg.time, tm); + + return 0; +} + +static int imx_sc_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct arm_smccc_res res; + + /* pack 2 time parameters into 1 register, 16 bits for each */ + arm_smccc_smc(IMX_SIP_SRTC, IMX_SIP_SRTC_SET_TIME, + ((tm->tm_year + 1900) << 16) | (tm->tm_mon + 1), + (tm->tm_mday << 16) | tm->tm_hour, + (tm->tm_min << 16) | tm->tm_sec, + 0, 0, 0, &res); + + return res.a0; +} + +static int imx_sc_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) +{ + return imx_scu_irq_group_enable(SC_IRQ_GROUP_RTC, SC_IRQ_RTC, enable); +} + +static int imx_sc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct imx_sc_msg_timer_rtc_set_alarm msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + int ret; + struct rtc_time *alrm_tm = &alrm->time; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_TIMER; + hdr->func = IMX_SC_TIMER_FUNC_SET_RTC_ALARM; + hdr->size = 3; + + msg.year = alrm_tm->tm_year + 1900; + msg.mon = alrm_tm->tm_mon + 1; + msg.day = alrm_tm->tm_mday; + msg.hour = alrm_tm->tm_hour; + msg.min = alrm_tm->tm_min; + msg.sec = alrm_tm->tm_sec; + + ret = imx_scu_call_rpc(rtc_ipc_handle, &msg, true); + if (ret) { + dev_err(dev, "set rtc alarm failed, ret %d\n", ret); + return ret; + } + + ret = imx_sc_rtc_alarm_irq_enable(dev, alrm->enabled); + if (ret) { + dev_err(dev, "enable rtc alarm failed, ret %d\n", ret); + return ret; + } + + return 0; +} + +static const struct rtc_class_ops imx_sc_rtc_ops = { + .read_time = imx_sc_rtc_read_time, + .set_time = imx_sc_rtc_set_time, + .set_alarm = imx_sc_rtc_set_alarm, + .alarm_irq_enable = imx_sc_rtc_alarm_irq_enable, +}; + +static int imx_sc_rtc_alarm_notify(struct notifier_block *nb, + unsigned long event, void *group) +{ + /* ignore non-rtc irq */ + if (!((event & SC_IRQ_RTC) && (*(u8 *)group == SC_IRQ_GROUP_RTC))) + return 0; + + rtc_update_irq(imx_sc_rtc, 1, RTC_IRQF | RTC_AF); + + return 0; +} + +static struct notifier_block imx_sc_rtc_alarm_sc_notifier = { + .notifier_call = imx_sc_rtc_alarm_notify, +}; + +static int imx_sc_rtc_probe(struct platform_device *pdev) +{ + int ret; + + ret = imx_scu_get_handle(&rtc_ipc_handle); + if (ret) + return ret; + + device_init_wakeup(&pdev->dev, true); + + imx_sc_rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(imx_sc_rtc)) + return PTR_ERR(imx_sc_rtc); + + imx_sc_rtc->ops = &imx_sc_rtc_ops; + imx_sc_rtc->range_min = 0; + imx_sc_rtc->range_max = U32_MAX; + + ret = devm_rtc_register_device(imx_sc_rtc); + if (ret) + return ret; + + imx_scu_irq_register_notifier(&imx_sc_rtc_alarm_sc_notifier); + + return 0; +} + +static const struct of_device_id imx_sc_dt_ids[] = { + { .compatible = "fsl,imx8qxp-sc-rtc", }, + {} +}; +MODULE_DEVICE_TABLE(of, imx_sc_dt_ids); + +static struct platform_driver imx_sc_rtc_driver = { + .driver = { + .name = "imx-sc-rtc", + .of_match_table = imx_sc_dt_ids, + }, + .probe = imx_sc_rtc_probe, +}; +module_platform_driver(imx_sc_rtc_driver); + +MODULE_AUTHOR("Anson Huang "); +MODULE_DESCRIPTION("NXP i.MX System Controller RTC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-imx-sm-bbm.c b/drivers/rtc/rtc-imx-sm-bbm.c new file mode 100644 index 000000000..daa472be7 --- /dev/null +++ b/drivers/rtc/rtc-imx-sm-bbm.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP. + */ + +#include +#include +#include +#include +#include +#include + +struct scmi_imx_bbm { + const struct scmi_imx_bbm_proto_ops *ops; + struct rtc_device *rtc_dev; + struct scmi_protocol_handle *ph; + struct notifier_block nb; +}; + +static int scmi_imx_bbm_read_time(struct device *dev, struct rtc_time *tm) +{ + struct scmi_imx_bbm *bbnsm = dev_get_drvdata(dev); + struct scmi_protocol_handle *ph = bbnsm->ph; + u64 val; + int ret; + + ret = bbnsm->ops->rtc_time_get(ph, 0, &val); + if (ret) + return ret; + + rtc_time64_to_tm(val, tm); + + return 0; +} + +static int scmi_imx_bbm_set_time(struct device *dev, struct rtc_time *tm) +{ + struct scmi_imx_bbm *bbnsm = dev_get_drvdata(dev); + struct scmi_protocol_handle *ph = bbnsm->ph; + u64 val; + + val = rtc_tm_to_time64(tm); + + return bbnsm->ops->rtc_time_set(ph, 0, val); +} + +static int scmi_imx_bbm_alarm_irq_enable(struct device *dev, unsigned int enable) +{ + struct scmi_imx_bbm *bbnsm = dev_get_drvdata(dev); + struct scmi_protocol_handle *ph = bbnsm->ph; + + /* scmi_imx_bbm_set_alarm enables the irq, just handle disable here */ + if (!enable) + return bbnsm->ops->rtc_alarm_set(ph, 0, false, 0); + + return 0; +} + +static int scmi_imx_bbm_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct scmi_imx_bbm *bbnsm = dev_get_drvdata(dev); + struct scmi_protocol_handle *ph = bbnsm->ph; + struct rtc_time *alrm_tm = &alrm->time; + u64 val; + + val = rtc_tm_to_time64(alrm_tm); + + return bbnsm->ops->rtc_alarm_set(ph, 0, true, val); +} + +static const struct rtc_class_ops smci_imx_bbm_rtc_ops = { + .read_time = scmi_imx_bbm_read_time, + .set_time = scmi_imx_bbm_set_time, + .set_alarm = scmi_imx_bbm_set_alarm, + .alarm_irq_enable = scmi_imx_bbm_alarm_irq_enable, +}; + +static int scmi_imx_bbm_rtc_notifier(struct notifier_block *nb, unsigned long event, void *data) +{ + struct scmi_imx_bbm *bbnsm = container_of(nb, struct scmi_imx_bbm, nb); + struct scmi_imx_bbm_notif_report *r = data; + + if (r->is_rtc) + rtc_update_irq(bbnsm->rtc_dev, 1, RTC_AF | RTC_IRQF); + else + pr_err("Unexpected bbm event: %s\n", __func__); + + return 0; +} + +static int scmi_imx_bbm_rtc_init(struct scmi_device *sdev) +{ + const struct scmi_handle *handle = sdev->handle; + struct device *dev = &sdev->dev; + struct scmi_imx_bbm *bbnsm = dev_get_drvdata(dev); + int ret; + + bbnsm->rtc_dev = devm_rtc_allocate_device(dev); + if (IS_ERR(bbnsm->rtc_dev)) + return PTR_ERR(bbnsm->rtc_dev); + + bbnsm->rtc_dev->ops = &smci_imx_bbm_rtc_ops; + bbnsm->rtc_dev->range_max = U32_MAX; + + bbnsm->nb.notifier_call = &scmi_imx_bbm_rtc_notifier; + ret = handle->notify_ops->devm_event_notifier_register(sdev, SCMI_PROTOCOL_IMX_BBM, + SCMI_EVENT_IMX_BBM_RTC, + NULL, &bbnsm->nb); + if (ret) + return ret; + + return devm_rtc_register_device(bbnsm->rtc_dev); +} + +static int scmi_imx_bbm_rtc_probe(struct scmi_device *sdev) +{ + const struct scmi_handle *handle = sdev->handle; + struct device *dev = &sdev->dev; + struct scmi_protocol_handle *ph; + struct scmi_imx_bbm *bbnsm; + int ret; + + if (!handle) + return -ENODEV; + + bbnsm = devm_kzalloc(dev, sizeof(*bbnsm), GFP_KERNEL); + if (!bbnsm) + return -ENOMEM; + + bbnsm->ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_IMX_BBM, &ph); + if (IS_ERR(bbnsm->ops)) + return PTR_ERR(bbnsm->ops); + + bbnsm->ph = ph; + + device_init_wakeup(dev, true); + + dev_set_drvdata(dev, bbnsm); + + ret = scmi_imx_bbm_rtc_init(sdev); + if (ret) + device_init_wakeup(dev, false); + + return ret; +} + +static const struct scmi_device_id scmi_id_table[] = { + { SCMI_PROTOCOL_IMX_BBM, "imx-bbm-rtc" }, + { }, +}; +MODULE_DEVICE_TABLE(scmi, scmi_id_table); + +static struct scmi_driver scmi_imx_bbm_rtc_driver = { + .name = "scmi-imx-bbm-rtc", + .probe = scmi_imx_bbm_rtc_probe, + .id_table = scmi_id_table, +}; +module_scmi_driver(scmi_imx_bbm_rtc_driver); + +MODULE_AUTHOR("Peng Fan "); +MODULE_DESCRIPTION("IMX SM BBM RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c new file mode 100644 index 000000000..ca4a0af95 --- /dev/null +++ b/drivers/rtc/rtc-imxdi.c @@ -0,0 +1,871 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2010 Orex Computed Radiography + */ + +/* + * This driver uses the 47-bit 32 kHz counter in the Freescale DryIce block + * to implement a Linux RTC. Times and alarms are truncated to seconds. + * Since the RTC framework performs API locking via rtc->ops_lock the + * only simultaneous accesses we need to deal with is updating DryIce + * registers while servicing an alarm. + * + * Note that reading the DSR (DryIce Status Register) automatically clears + * the WCF (Write Complete Flag). All DryIce writes are synchronized to the + * LP (Low Power) domain and set the WCF upon completion. Writes to the + * DIER (DryIce Interrupt Enable Register) are the only exception. These + * occur at normal bus speeds and do not set WCF. Periodic interrupts are + * not supported by the hardware. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* DryIce Register Definitions */ + +#define DTCMR 0x00 /* Time Counter MSB Reg */ +#define DTCLR 0x04 /* Time Counter LSB Reg */ + +#define DCAMR 0x08 /* Clock Alarm MSB Reg */ +#define DCALR 0x0c /* Clock Alarm LSB Reg */ +#define DCAMR_UNSET 0xFFFFFFFF /* doomsday - 1 sec */ + +#define DCR 0x10 /* Control Reg */ +#define DCR_TDCHL (1 << 30) /* Tamper-detect configuration hard lock */ +#define DCR_TDCSL (1 << 29) /* Tamper-detect configuration soft lock */ +#define DCR_KSSL (1 << 27) /* Key-select soft lock */ +#define DCR_MCHL (1 << 20) /* Monotonic-counter hard lock */ +#define DCR_MCSL (1 << 19) /* Monotonic-counter soft lock */ +#define DCR_TCHL (1 << 18) /* Timer-counter hard lock */ +#define DCR_TCSL (1 << 17) /* Timer-counter soft lock */ +#define DCR_FSHL (1 << 16) /* Failure state hard lock */ +#define DCR_TCE (1 << 3) /* Time Counter Enable */ +#define DCR_MCE (1 << 2) /* Monotonic Counter Enable */ + +#define DSR 0x14 /* Status Reg */ +#define DSR_WTD (1 << 23) /* Wire-mesh tamper detected */ +#define DSR_ETBD (1 << 22) /* External tamper B detected */ +#define DSR_ETAD (1 << 21) /* External tamper A detected */ +#define DSR_EBD (1 << 20) /* External boot detected */ +#define DSR_SAD (1 << 19) /* SCC alarm detected */ +#define DSR_TTD (1 << 18) /* Temperature tamper detected */ +#define DSR_CTD (1 << 17) /* Clock tamper detected */ +#define DSR_VTD (1 << 16) /* Voltage tamper detected */ +#define DSR_WBF (1 << 10) /* Write Busy Flag (synchronous) */ +#define DSR_WNF (1 << 9) /* Write Next Flag (synchronous) */ +#define DSR_WCF (1 << 8) /* Write Complete Flag (synchronous)*/ +#define DSR_WEF (1 << 7) /* Write Error Flag */ +#define DSR_CAF (1 << 4) /* Clock Alarm Flag */ +#define DSR_MCO (1 << 3) /* monotonic counter overflow */ +#define DSR_TCO (1 << 2) /* time counter overflow */ +#define DSR_NVF (1 << 1) /* Non-Valid Flag */ +#define DSR_SVF (1 << 0) /* Security Violation Flag */ + +#define DIER 0x18 /* Interrupt Enable Reg (synchronous) */ +#define DIER_WNIE (1 << 9) /* Write Next Interrupt Enable */ +#define DIER_WCIE (1 << 8) /* Write Complete Interrupt Enable */ +#define DIER_WEIE (1 << 7) /* Write Error Interrupt Enable */ +#define DIER_CAIE (1 << 4) /* Clock Alarm Interrupt Enable */ +#define DIER_SVIE (1 << 0) /* Security-violation Interrupt Enable */ + +#define DMCR 0x1c /* DryIce Monotonic Counter Reg */ + +#define DTCR 0x28 /* DryIce Tamper Configuration Reg */ +#define DTCR_MOE (1 << 9) /* monotonic overflow enabled */ +#define DTCR_TOE (1 << 8) /* time overflow enabled */ +#define DTCR_WTE (1 << 7) /* wire-mesh tamper enabled */ +#define DTCR_ETBE (1 << 6) /* external B tamper enabled */ +#define DTCR_ETAE (1 << 5) /* external A tamper enabled */ +#define DTCR_EBE (1 << 4) /* external boot tamper enabled */ +#define DTCR_SAIE (1 << 3) /* SCC enabled */ +#define DTCR_TTE (1 << 2) /* temperature tamper enabled */ +#define DTCR_CTE (1 << 1) /* clock tamper enabled */ +#define DTCR_VTE (1 << 0) /* voltage tamper enabled */ + +#define DGPR 0x3c /* DryIce General Purpose Reg */ + +/** + * struct imxdi_dev - private imxdi rtc data + * @pdev: pointer to platform dev + * @rtc: pointer to rtc struct + * @ioaddr: IO registers pointer + * @clk: input reference clock + * @dsr: copy of the DSR register + * @irq_lock: interrupt enable register (DIER) lock + * @write_wait: registers write complete queue + * @write_mutex: serialize registers write + * @work: schedule alarm work + */ +struct imxdi_dev { + struct platform_device *pdev; + struct rtc_device *rtc; + void __iomem *ioaddr; + struct clk *clk; + u32 dsr; + spinlock_t irq_lock; + wait_queue_head_t write_wait; + struct mutex write_mutex; + struct work_struct work; +}; + +/* Some background: + * + * The DryIce unit is a complex security/tamper monitor device. To be able do + * its job in a useful manner it runs a bigger statemachine to bring it into + * security/tamper failure state and once again to bring it out of this state. + * + * This unit can be in one of three states: + * + * - "NON-VALID STATE" + * always after the battery power was removed + * - "FAILURE STATE" + * if one of the enabled security events has happened + * - "VALID STATE" + * if the unit works as expected + * + * Everything stops when the unit enters the failure state including the RTC + * counter (to be able to detect the time the security event happened). + * + * The following events (when enabled) let the DryIce unit enter the failure + * state: + * + * - wire-mesh-tamper detect + * - external tamper B detect + * - external tamper A detect + * - temperature tamper detect + * - clock tamper detect + * - voltage tamper detect + * - RTC counter overflow + * - monotonic counter overflow + * - external boot + * + * If we find the DryIce unit in "FAILURE STATE" and the TDCHL cleared, we + * can only detect this state. In this case the unit is completely locked and + * must force a second "SYSTEM POR" to bring the DryIce into the + * "NON-VALID STATE" + "FAILURE STATE" where a recovery is possible. + * If the TDCHL is set in the "FAILURE STATE" we are out of luck. In this case + * a battery power cycle is required. + * + * In the "NON-VALID STATE" + "FAILURE STATE" we can clear the "FAILURE STATE" + * and recover the DryIce unit. By clearing the "NON-VALID STATE" as the last + * task, we bring back this unit into life. + */ + +/* + * Do a write into the unit without interrupt support. + * We do not need to check the WEF here, because the only reason this kind of + * write error can happen is if we write to the unit twice within the 122 us + * interval. This cannot happen, since we are using this function only while + * setting up the unit. + */ +static void di_write_busy_wait(const struct imxdi_dev *imxdi, u32 val, + unsigned reg) +{ + /* do the register write */ + writel(val, imxdi->ioaddr + reg); + + /* + * now it takes four 32,768 kHz clock cycles to take + * the change into effect = 122 us + */ + usleep_range(130, 200); +} + +static void di_report_tamper_info(struct imxdi_dev *imxdi, u32 dsr) +{ + u32 dtcr; + + dtcr = readl(imxdi->ioaddr + DTCR); + + dev_emerg(&imxdi->pdev->dev, "DryIce tamper event detected\n"); + /* the following flags force a transition into the "FAILURE STATE" */ + if (dsr & DSR_VTD) + dev_emerg(&imxdi->pdev->dev, "%sVoltage Tamper Event\n", + dtcr & DTCR_VTE ? "" : "Spurious "); + + if (dsr & DSR_CTD) + dev_emerg(&imxdi->pdev->dev, "%s32768 Hz Clock Tamper Event\n", + dtcr & DTCR_CTE ? "" : "Spurious "); + + if (dsr & DSR_TTD) + dev_emerg(&imxdi->pdev->dev, "%sTemperature Tamper Event\n", + dtcr & DTCR_TTE ? "" : "Spurious "); + + if (dsr & DSR_SAD) + dev_emerg(&imxdi->pdev->dev, + "%sSecure Controller Alarm Event\n", + dtcr & DTCR_SAIE ? "" : "Spurious "); + + if (dsr & DSR_EBD) + dev_emerg(&imxdi->pdev->dev, "%sExternal Boot Tamper Event\n", + dtcr & DTCR_EBE ? "" : "Spurious "); + + if (dsr & DSR_ETAD) + dev_emerg(&imxdi->pdev->dev, "%sExternal Tamper A Event\n", + dtcr & DTCR_ETAE ? "" : "Spurious "); + + if (dsr & DSR_ETBD) + dev_emerg(&imxdi->pdev->dev, "%sExternal Tamper B Event\n", + dtcr & DTCR_ETBE ? "" : "Spurious "); + + if (dsr & DSR_WTD) + dev_emerg(&imxdi->pdev->dev, "%sWire-mesh Tamper Event\n", + dtcr & DTCR_WTE ? "" : "Spurious "); + + if (dsr & DSR_MCO) + dev_emerg(&imxdi->pdev->dev, + "%sMonotonic-counter Overflow Event\n", + dtcr & DTCR_MOE ? "" : "Spurious "); + + if (dsr & DSR_TCO) + dev_emerg(&imxdi->pdev->dev, "%sTimer-counter Overflow Event\n", + dtcr & DTCR_TOE ? "" : "Spurious "); +} + +static void di_what_is_to_be_done(struct imxdi_dev *imxdi, + const char *power_supply) +{ + dev_emerg(&imxdi->pdev->dev, "Please cycle the %s power supply in order to get the DryIce/RTC unit working again\n", + power_supply); +} + +static int di_handle_failure_state(struct imxdi_dev *imxdi, u32 dsr) +{ + u32 dcr; + + dev_dbg(&imxdi->pdev->dev, "DSR register reports: %08X\n", dsr); + + /* report the cause */ + di_report_tamper_info(imxdi, dsr); + + dcr = readl(imxdi->ioaddr + DCR); + + if (dcr & DCR_FSHL) { + /* we are out of luck */ + di_what_is_to_be_done(imxdi, "battery"); + return -ENODEV; + } + /* + * with the next SYSTEM POR we will transit from the "FAILURE STATE" + * into the "NON-VALID STATE" + "FAILURE STATE" + */ + di_what_is_to_be_done(imxdi, "main"); + + return -ENODEV; +} + +static int di_handle_valid_state(struct imxdi_dev *imxdi, u32 dsr) +{ + /* initialize alarm */ + di_write_busy_wait(imxdi, DCAMR_UNSET, DCAMR); + di_write_busy_wait(imxdi, 0, DCALR); + + /* clear alarm flag */ + if (dsr & DSR_CAF) + di_write_busy_wait(imxdi, DSR_CAF, DSR); + + return 0; +} + +static int di_handle_invalid_state(struct imxdi_dev *imxdi, u32 dsr) +{ + u32 dcr, sec; + + /* + * lets disable all sources which can force the DryIce unit into + * the "FAILURE STATE" for now + */ + di_write_busy_wait(imxdi, 0x00000000, DTCR); + /* and lets protect them at runtime from any change */ + di_write_busy_wait(imxdi, DCR_TDCSL, DCR); + + sec = readl(imxdi->ioaddr + DTCMR); + if (sec != 0) + dev_warn(&imxdi->pdev->dev, + "The security violation has happened at %u seconds\n", + sec); + /* + * the timer cannot be set/modified if + * - the TCHL or TCSL bit is set in DCR + */ + dcr = readl(imxdi->ioaddr + DCR); + if (!(dcr & DCR_TCE)) { + if (dcr & DCR_TCHL) { + /* we are out of luck */ + di_what_is_to_be_done(imxdi, "battery"); + return -ENODEV; + } + if (dcr & DCR_TCSL) { + di_what_is_to_be_done(imxdi, "main"); + return -ENODEV; + } + } + /* + * - the timer counter stops/is stopped if + * - its overflow flag is set (TCO in DSR) + * -> clear overflow bit to make it count again + * - NVF is set in DSR + * -> clear non-valid bit to make it count again + * - its TCE (DCR) is cleared + * -> set TCE to make it count + * - it was never set before + * -> write a time into it (required again if the NVF was set) + */ + /* state handled */ + di_write_busy_wait(imxdi, DSR_NVF, DSR); + /* clear overflow flag */ + di_write_busy_wait(imxdi, DSR_TCO, DSR); + /* enable the counter */ + di_write_busy_wait(imxdi, dcr | DCR_TCE, DCR); + /* set and trigger it to make it count */ + di_write_busy_wait(imxdi, sec, DTCMR); + + /* now prepare for the valid state */ + return di_handle_valid_state(imxdi, __raw_readl(imxdi->ioaddr + DSR)); +} + +static int di_handle_invalid_and_failure_state(struct imxdi_dev *imxdi, u32 dsr) +{ + u32 dcr; + + /* + * now we must first remove the tamper sources in order to get the + * device out of the "FAILURE STATE" + * To disable any of the following sources we need to modify the DTCR + */ + if (dsr & (DSR_WTD | DSR_ETBD | DSR_ETAD | DSR_EBD | DSR_SAD | + DSR_TTD | DSR_CTD | DSR_VTD | DSR_MCO | DSR_TCO)) { + dcr = __raw_readl(imxdi->ioaddr + DCR); + if (dcr & DCR_TDCHL) { + /* + * the tamper register is locked. We cannot disable the + * tamper detection. The TDCHL can only be reset by a + * DRYICE POR, but we cannot force a DRYICE POR in + * software because we are still in "FAILURE STATE". + * We need a DRYICE POR via battery power cycling.... + */ + /* + * out of luck! + * we cannot disable them without a DRYICE POR + */ + di_what_is_to_be_done(imxdi, "battery"); + return -ENODEV; + } + if (dcr & DCR_TDCSL) { + /* a soft lock can be removed by a SYSTEM POR */ + di_what_is_to_be_done(imxdi, "main"); + return -ENODEV; + } + } + + /* disable all sources */ + di_write_busy_wait(imxdi, 0x00000000, DTCR); + + /* clear the status bits now */ + di_write_busy_wait(imxdi, dsr & (DSR_WTD | DSR_ETBD | DSR_ETAD | + DSR_EBD | DSR_SAD | DSR_TTD | DSR_CTD | DSR_VTD | + DSR_MCO | DSR_TCO), DSR); + + dsr = readl(imxdi->ioaddr + DSR); + if ((dsr & ~(DSR_NVF | DSR_SVF | DSR_WBF | DSR_WNF | + DSR_WCF | DSR_WEF)) != 0) + dev_warn(&imxdi->pdev->dev, + "There are still some sources of pain in DSR: %08x!\n", + dsr & ~(DSR_NVF | DSR_SVF | DSR_WBF | DSR_WNF | + DSR_WCF | DSR_WEF)); + + /* + * now we are trying to clear the "Security-violation flag" to + * get the DryIce out of this state + */ + di_write_busy_wait(imxdi, DSR_SVF, DSR); + + /* success? */ + dsr = readl(imxdi->ioaddr + DSR); + if (dsr & DSR_SVF) { + dev_crit(&imxdi->pdev->dev, + "Cannot clear the security violation flag. We are ending up in an endless loop!\n"); + /* last resort */ + di_what_is_to_be_done(imxdi, "battery"); + return -ENODEV; + } + + /* + * now we have left the "FAILURE STATE" and ending up in the + * "NON-VALID STATE" time to recover everything + */ + return di_handle_invalid_state(imxdi, dsr); +} + +static int di_handle_state(struct imxdi_dev *imxdi) +{ + int rc; + u32 dsr; + + dsr = readl(imxdi->ioaddr + DSR); + + switch (dsr & (DSR_NVF | DSR_SVF)) { + case DSR_NVF: + dev_warn(&imxdi->pdev->dev, "Invalid stated unit detected\n"); + rc = di_handle_invalid_state(imxdi, dsr); + break; + case DSR_SVF: + dev_warn(&imxdi->pdev->dev, "Failure stated unit detected\n"); + rc = di_handle_failure_state(imxdi, dsr); + break; + case DSR_NVF | DSR_SVF: + dev_warn(&imxdi->pdev->dev, + "Failure+Invalid stated unit detected\n"); + rc = di_handle_invalid_and_failure_state(imxdi, dsr); + break; + default: + dev_notice(&imxdi->pdev->dev, "Unlocked unit detected\n"); + rc = di_handle_valid_state(imxdi, dsr); + } + + return rc; +} + +/* + * enable a dryice interrupt + */ +static void di_int_enable(struct imxdi_dev *imxdi, u32 intr) +{ + unsigned long flags; + + spin_lock_irqsave(&imxdi->irq_lock, flags); + writel(readl(imxdi->ioaddr + DIER) | intr, + imxdi->ioaddr + DIER); + spin_unlock_irqrestore(&imxdi->irq_lock, flags); +} + +/* + * disable a dryice interrupt + */ +static void di_int_disable(struct imxdi_dev *imxdi, u32 intr) +{ + unsigned long flags; + + spin_lock_irqsave(&imxdi->irq_lock, flags); + writel(readl(imxdi->ioaddr + DIER) & ~intr, + imxdi->ioaddr + DIER); + spin_unlock_irqrestore(&imxdi->irq_lock, flags); +} + +/* + * This function attempts to clear the dryice write-error flag. + * + * A dryice write error is similar to a bus fault and should not occur in + * normal operation. Clearing the flag requires another write, so the root + * cause of the problem may need to be fixed before the flag can be cleared. + */ +static void clear_write_error(struct imxdi_dev *imxdi) +{ + int cnt; + + dev_warn(&imxdi->pdev->dev, "WARNING: Register write error!\n"); + + /* clear the write error flag */ + writel(DSR_WEF, imxdi->ioaddr + DSR); + + /* wait for it to take effect */ + for (cnt = 0; cnt < 1000; cnt++) { + if ((readl(imxdi->ioaddr + DSR) & DSR_WEF) == 0) + return; + udelay(10); + } + dev_err(&imxdi->pdev->dev, + "ERROR: Cannot clear write-error flag!\n"); +} + +/* + * Write a dryice register and wait until it completes. + * + * This function uses interrupts to determine when the + * write has completed. + */ +static int di_write_wait(struct imxdi_dev *imxdi, u32 val, int reg) +{ + int ret; + int rc = 0; + + /* serialize register writes */ + mutex_lock(&imxdi->write_mutex); + + /* enable the write-complete interrupt */ + di_int_enable(imxdi, DIER_WCIE); + + imxdi->dsr = 0; + + /* do the register write */ + writel(val, imxdi->ioaddr + reg); + + /* wait for the write to finish */ + ret = wait_event_interruptible_timeout(imxdi->write_wait, + imxdi->dsr & (DSR_WCF | DSR_WEF), msecs_to_jiffies(1)); + if (ret < 0) { + rc = ret; + goto out; + } else if (ret == 0) { + dev_warn(&imxdi->pdev->dev, + "Write-wait timeout " + "val = 0x%08x reg = 0x%08x\n", val, reg); + } + + /* check for write error */ + if (imxdi->dsr & DSR_WEF) { + clear_write_error(imxdi); + rc = -EIO; + } + +out: + mutex_unlock(&imxdi->write_mutex); + + return rc; +} + +/* + * read the seconds portion of the current time from the dryice time counter + */ +static int dryice_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct imxdi_dev *imxdi = dev_get_drvdata(dev); + unsigned long now; + + now = readl(imxdi->ioaddr + DTCMR); + rtc_time64_to_tm(now, tm); + + return 0; +} + +/* + * set the seconds portion of dryice time counter and clear the + * fractional part. + */ +static int dryice_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct imxdi_dev *imxdi = dev_get_drvdata(dev); + u32 dcr, dsr; + int rc; + + dcr = readl(imxdi->ioaddr + DCR); + dsr = readl(imxdi->ioaddr + DSR); + + if (!(dcr & DCR_TCE) || (dsr & DSR_SVF)) { + if (dcr & DCR_TCHL) { + /* we are even more out of luck */ + di_what_is_to_be_done(imxdi, "battery"); + return -EPERM; + } + if ((dcr & DCR_TCSL) || (dsr & DSR_SVF)) { + /* we are out of luck for now */ + di_what_is_to_be_done(imxdi, "main"); + return -EPERM; + } + } + + /* zero the fractional part first */ + rc = di_write_wait(imxdi, 0, DTCLR); + if (rc != 0) + return rc; + + rc = di_write_wait(imxdi, rtc_tm_to_time64(tm), DTCMR); + if (rc != 0) + return rc; + + return di_write_wait(imxdi, readl(imxdi->ioaddr + DCR) | DCR_TCE, DCR); +} + +static int dryice_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct imxdi_dev *imxdi = dev_get_drvdata(dev); + + if (enabled) + di_int_enable(imxdi, DIER_CAIE); + else + di_int_disable(imxdi, DIER_CAIE); + + return 0; +} + +/* + * read the seconds portion of the alarm register. + * the fractional part of the alarm register is always zero. + */ +static int dryice_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct imxdi_dev *imxdi = dev_get_drvdata(dev); + u32 dcamr; + + dcamr = readl(imxdi->ioaddr + DCAMR); + rtc_time64_to_tm(dcamr, &alarm->time); + + /* alarm is enabled if the interrupt is enabled */ + alarm->enabled = (readl(imxdi->ioaddr + DIER) & DIER_CAIE) != 0; + + /* don't allow the DSR read to mess up DSR_WCF */ + mutex_lock(&imxdi->write_mutex); + + /* alarm is pending if the alarm flag is set */ + alarm->pending = (readl(imxdi->ioaddr + DSR) & DSR_CAF) != 0; + + mutex_unlock(&imxdi->write_mutex); + + return 0; +} + +/* + * set the seconds portion of dryice alarm register + */ +static int dryice_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct imxdi_dev *imxdi = dev_get_drvdata(dev); + int rc; + + /* write the new alarm time */ + rc = di_write_wait(imxdi, rtc_tm_to_time64(&alarm->time), DCAMR); + if (rc) + return rc; + + if (alarm->enabled) + di_int_enable(imxdi, DIER_CAIE); /* enable alarm intr */ + else + di_int_disable(imxdi, DIER_CAIE); /* disable alarm intr */ + + return 0; +} + +static const struct rtc_class_ops dryice_rtc_ops = { + .read_time = dryice_rtc_read_time, + .set_time = dryice_rtc_set_time, + .alarm_irq_enable = dryice_rtc_alarm_irq_enable, + .read_alarm = dryice_rtc_read_alarm, + .set_alarm = dryice_rtc_set_alarm, +}; + +/* + * interrupt handler for dryice "normal" and security violation interrupt + */ +static irqreturn_t dryice_irq(int irq, void *dev_id) +{ + struct imxdi_dev *imxdi = dev_id; + u32 dsr, dier; + irqreturn_t rc = IRQ_NONE; + + dier = readl(imxdi->ioaddr + DIER); + dsr = readl(imxdi->ioaddr + DSR); + + /* handle the security violation event */ + if (dier & DIER_SVIE) { + if (dsr & DSR_SVF) { + /* + * Disable the interrupt when this kind of event has + * happened. + * There cannot be more than one event of this type, + * because it needs a complex state change + * including a main power cycle to get again out of + * this state. + */ + di_int_disable(imxdi, DIER_SVIE); + /* report the violation */ + di_report_tamper_info(imxdi, dsr); + rc = IRQ_HANDLED; + } + } + + /* handle write complete and write error cases */ + if (dier & DIER_WCIE) { + /*If the write wait queue is empty then there is no pending + operations. It means the interrupt is for DryIce -Security. + IRQ must be returned as none.*/ + if (list_empty_careful(&imxdi->write_wait.head)) + return rc; + + /* DSR_WCF clears itself on DSR read */ + if (dsr & (DSR_WCF | DSR_WEF)) { + /* mask the interrupt */ + di_int_disable(imxdi, DIER_WCIE); + + /* save the dsr value for the wait queue */ + imxdi->dsr |= dsr; + + wake_up_interruptible(&imxdi->write_wait); + rc = IRQ_HANDLED; + } + } + + /* handle the alarm case */ + if (dier & DIER_CAIE) { + /* DSR_WCF clears itself on DSR read */ + if (dsr & DSR_CAF) { + /* mask the interrupt */ + di_int_disable(imxdi, DIER_CAIE); + + /* finish alarm in user context */ + schedule_work(&imxdi->work); + rc = IRQ_HANDLED; + } + } + return rc; +} + +/* + * post the alarm event from user context so it can sleep + * on the write completion. + */ +static void dryice_work(struct work_struct *work) +{ + struct imxdi_dev *imxdi = container_of(work, + struct imxdi_dev, work); + + /* dismiss the interrupt (ignore error) */ + di_write_wait(imxdi, DSR_CAF, DSR); + + /* pass the alarm event to the rtc framework. */ + rtc_update_irq(imxdi->rtc, 1, RTC_AF | RTC_IRQF); +} + +/* + * probe for dryice rtc device + */ +static int __init dryice_rtc_probe(struct platform_device *pdev) +{ + struct imxdi_dev *imxdi; + int norm_irq, sec_irq; + int rc; + + imxdi = devm_kzalloc(&pdev->dev, sizeof(*imxdi), GFP_KERNEL); + if (!imxdi) + return -ENOMEM; + + imxdi->pdev = pdev; + + imxdi->ioaddr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(imxdi->ioaddr)) + return PTR_ERR(imxdi->ioaddr); + + spin_lock_init(&imxdi->irq_lock); + + norm_irq = platform_get_irq(pdev, 0); + if (norm_irq < 0) + return norm_irq; + + /* the 2nd irq is the security violation irq + * make this optional, don't break the device tree ABI + */ + sec_irq = platform_get_irq(pdev, 1); + if (sec_irq <= 0) + sec_irq = IRQ_NOTCONNECTED; + + init_waitqueue_head(&imxdi->write_wait); + + INIT_WORK(&imxdi->work, dryice_work); + + mutex_init(&imxdi->write_mutex); + + imxdi->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(imxdi->rtc)) + return PTR_ERR(imxdi->rtc); + + imxdi->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(imxdi->clk)) + return PTR_ERR(imxdi->clk); + rc = clk_prepare_enable(imxdi->clk); + if (rc) + return rc; + + /* + * Initialize dryice hardware + */ + + /* mask all interrupts */ + writel(0, imxdi->ioaddr + DIER); + + rc = di_handle_state(imxdi); + if (rc != 0) + goto err; + + rc = devm_request_irq(&pdev->dev, norm_irq, dryice_irq, + IRQF_SHARED, pdev->name, imxdi); + if (rc) { + dev_warn(&pdev->dev, "interrupt not available.\n"); + goto err; + } + + rc = devm_request_irq(&pdev->dev, sec_irq, dryice_irq, + IRQF_SHARED, pdev->name, imxdi); + if (rc) { + dev_warn(&pdev->dev, "security violation interrupt not available.\n"); + /* this is not an error, see above */ + } + + platform_set_drvdata(pdev, imxdi); + + device_init_wakeup(&pdev->dev, true); + dev_pm_set_wake_irq(&pdev->dev, norm_irq); + + imxdi->rtc->ops = &dryice_rtc_ops; + imxdi->rtc->range_max = U32_MAX; + + rc = devm_rtc_register_device(imxdi->rtc); + if (rc) + goto err; + + return 0; + +err: + clk_disable_unprepare(imxdi->clk); + + return rc; +} + +static void __exit dryice_rtc_remove(struct platform_device *pdev) +{ + struct imxdi_dev *imxdi = platform_get_drvdata(pdev); + + flush_work(&imxdi->work); + + /* mask all interrupts */ + writel(0, imxdi->ioaddr + DIER); + + clk_disable_unprepare(imxdi->clk); +} + +static const struct of_device_id dryice_dt_ids[] = { + { .compatible = "fsl,imx25-rtc" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, dryice_dt_ids); + +/* + * dryice_rtc_remove() lives in .exit.text. For drivers registered via + * module_platform_driver_probe() this is ok because they cannot get unbound at + * runtime. So mark the driver struct with __refdata to prevent modpost + * triggering a section mismatch warning. + */ +static struct platform_driver dryice_rtc_driver __refdata = { + .driver = { + .name = "imxdi_rtc", + .of_match_table = dryice_dt_ids, + }, + .remove = __exit_p(dryice_rtc_remove), +}; + +module_platform_driver_probe(dryice_rtc_driver, dryice_rtc_probe); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_AUTHOR("Baruch Siach "); +MODULE_DESCRIPTION("IMX DryIce Realtime Clock Driver (RTC)"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-isl12022.c b/drivers/rtc/rtc-isl12022.c new file mode 100644 index 000000000..bc3628885 --- /dev/null +++ b/drivers/rtc/rtc-isl12022.c @@ -0,0 +1,625 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * An I2C driver for the Intersil ISL 12022 + * + * Author: Roman Fietze + * + * Based on the Philips PCF8563 RTC + * by Alessandro Zummo . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* RTC - Real time clock registers */ +#define ISL12022_REG_SC 0x00 +#define ISL12022_REG_MN 0x01 +#define ISL12022_REG_HR 0x02 +#define ISL12022_REG_DT 0x03 +#define ISL12022_REG_MO 0x04 +#define ISL12022_REG_YR 0x05 +#define ISL12022_REG_DW 0x06 + +/* CSR - Control and status registers */ +#define ISL12022_REG_SR 0x07 +#define ISL12022_REG_INT 0x08 +#define ISL12022_REG_PWR_VBAT 0x0a +#define ISL12022_REG_BETA 0x0d + +/* ALARM - Alarm registers */ +#define ISL12022_REG_SCA0 0x10 +#define ISL12022_REG_MNA0 0x11 +#define ISL12022_REG_HRA0 0x12 +#define ISL12022_REG_DTA0 0x13 +#define ISL12022_REG_MOA0 0x14 +#define ISL12022_REG_DWA0 0x15 +#define ISL12022_ALARM ISL12022_REG_SCA0 +#define ISL12022_ALARM_LEN (ISL12022_REG_DWA0 - ISL12022_REG_SCA0 + 1) + +/* TEMP - Temperature sensor registers */ +#define ISL12022_REG_TEMP_L 0x28 + +/* ISL register bits */ +#define ISL12022_HR_MIL (1 << 7) /* military or 24 hour time */ + +#define ISL12022_SR_ALM (1 << 4) +#define ISL12022_SR_LBAT85 (1 << 2) +#define ISL12022_SR_LBAT75 (1 << 1) + +#define ISL12022_INT_ARST (1 << 7) +#define ISL12022_INT_WRTC (1 << 6) +#define ISL12022_INT_IM (1 << 5) +#define ISL12022_INT_FOBATB (1 << 4) +#define ISL12022_INT_FO_MASK GENMASK(3, 0) +#define ISL12022_INT_FO_OFF 0x0 +#define ISL12022_INT_FO_32K 0x1 + +#define ISL12022_REG_VB85_MASK GENMASK(5, 3) +#define ISL12022_REG_VB75_MASK GENMASK(2, 0) + +#define ISL12022_ALARM_ENABLE (1 << 7) /* for all ALARM registers */ + +#define ISL12022_BETA_TSE (1 << 7) + +static struct i2c_driver isl12022_driver; + +struct isl12022 { + struct rtc_device *rtc; + struct regmap *regmap; + int irq; + bool irq_enabled; +}; + +static umode_t isl12022_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (type == hwmon_temp && attr == hwmon_temp_input) + return 0444; + + return 0; +} + +/* + * A user-initiated temperature conversion is not started by this function, + * so the temperature is updated once every ~60 seconds. + */ +static int isl12022_hwmon_read_temp(struct device *dev, long *mC) +{ + struct regmap *regmap = dev_get_drvdata(dev); + int temp, ret; + __le16 buf; + + ret = regmap_bulk_read(regmap, ISL12022_REG_TEMP_L, &buf, sizeof(buf)); + if (ret) + return ret; + /* + * Temperature is represented as a 10-bit number, unit half-Kelvins. + */ + temp = le16_to_cpu(buf); + temp *= 500; + temp -= 273000; + + *mC = temp; + + return 0; +} + +static int isl12022_hwmon_read(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + if (type == hwmon_temp && attr == hwmon_temp_input) + return isl12022_hwmon_read_temp(dev, val); + + return -EOPNOTSUPP; +} + +static const struct hwmon_channel_info * const isl12022_hwmon_info[] = { + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), + NULL +}; + +static const struct hwmon_ops isl12022_hwmon_ops = { + .is_visible = isl12022_hwmon_is_visible, + .read = isl12022_hwmon_read, +}; + +static const struct hwmon_chip_info isl12022_hwmon_chip_info = { + .ops = &isl12022_hwmon_ops, + .info = isl12022_hwmon_info, +}; + +static void isl12022_hwmon_register(struct device *dev) +{ + struct isl12022 *isl12022 = dev_get_drvdata(dev); + struct regmap *regmap = isl12022->regmap; + struct device *hwmon; + int ret; + + if (!IS_REACHABLE(CONFIG_HWMON)) + return; + + ret = regmap_update_bits(regmap, ISL12022_REG_BETA, + ISL12022_BETA_TSE, ISL12022_BETA_TSE); + if (ret) { + dev_warn(dev, "unable to enable temperature sensor\n"); + return; + } + + hwmon = devm_hwmon_device_register_with_info(dev, "isl12022", regmap, + &isl12022_hwmon_chip_info, + NULL); + if (IS_ERR(hwmon)) + dev_warn(dev, "unable to register hwmon device: %pe\n", hwmon); +} + +/* + * In the routines that deal directly with the isl12022 hardware, we use + * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. + */ +static int isl12022_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct isl12022 *isl12022 = dev_get_drvdata(dev); + struct regmap *regmap = isl12022->regmap; + u8 buf[ISL12022_REG_INT + 1]; + int ret; + + ret = regmap_bulk_read(regmap, ISL12022_REG_SC, buf, sizeof(buf)); + if (ret) + return ret; + + dev_dbg(dev, + "raw data is sec=%02x, min=%02x, hr=%02x, mday=%02x, mon=%02x, year=%02x, wday=%02x, sr=%02x, int=%02x", + buf[ISL12022_REG_SC], + buf[ISL12022_REG_MN], + buf[ISL12022_REG_HR], + buf[ISL12022_REG_DT], + buf[ISL12022_REG_MO], + buf[ISL12022_REG_YR], + buf[ISL12022_REG_DW], + buf[ISL12022_REG_SR], + buf[ISL12022_REG_INT]); + + tm->tm_sec = bcd2bin(buf[ISL12022_REG_SC] & 0x7F); + tm->tm_min = bcd2bin(buf[ISL12022_REG_MN] & 0x7F); + tm->tm_hour = bcd2bin(buf[ISL12022_REG_HR] & 0x3F); + tm->tm_mday = bcd2bin(buf[ISL12022_REG_DT] & 0x3F); + tm->tm_wday = buf[ISL12022_REG_DW] & 0x07; + tm->tm_mon = bcd2bin(buf[ISL12022_REG_MO] & 0x1F) - 1; + tm->tm_year = bcd2bin(buf[ISL12022_REG_YR]) + 100; + + dev_dbg(dev, "%s: %ptR\n", __func__, tm); + + return 0; +} + +static int isl12022_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct isl12022 *isl12022 = dev_get_drvdata(dev); + struct regmap *regmap = isl12022->regmap; + int ret; + u8 buf[ISL12022_REG_DW + 1]; + + dev_dbg(dev, "%s: %ptR\n", __func__, tm); + + /* Ensure the write enable bit is set. */ + ret = regmap_update_bits(regmap, ISL12022_REG_INT, + ISL12022_INT_WRTC, ISL12022_INT_WRTC); + if (ret) + return ret; + + /* hours, minutes and seconds */ + buf[ISL12022_REG_SC] = bin2bcd(tm->tm_sec); + buf[ISL12022_REG_MN] = bin2bcd(tm->tm_min); + buf[ISL12022_REG_HR] = bin2bcd(tm->tm_hour) | ISL12022_HR_MIL; + + buf[ISL12022_REG_DT] = bin2bcd(tm->tm_mday); + + /* month, 1 - 12 */ + buf[ISL12022_REG_MO] = bin2bcd(tm->tm_mon + 1); + + /* year and century */ + buf[ISL12022_REG_YR] = bin2bcd(tm->tm_year % 100); + + buf[ISL12022_REG_DW] = tm->tm_wday & 0x07; + + return regmap_bulk_write(regmap, ISL12022_REG_SC, buf, sizeof(buf)); +} + +static int isl12022_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct rtc_time *tm = &alarm->time; + struct isl12022 *isl12022 = dev_get_drvdata(dev); + struct regmap *regmap = isl12022->regmap; + u8 buf[ISL12022_ALARM_LEN]; + unsigned int i, yr; + int ret; + + ret = regmap_bulk_read(regmap, ISL12022_ALARM, buf, sizeof(buf)); + if (ret) { + dev_dbg(dev, "%s: reading ALARM registers failed\n", + __func__); + return ret; + } + + /* The alarm doesn't store the year so get it from the rtc section */ + ret = regmap_read(regmap, ISL12022_REG_YR, &yr); + if (ret) { + dev_dbg(dev, "%s: reading YR register failed\n", __func__); + return ret; + } + + dev_dbg(dev, + "%s: sc=%02x, mn=%02x, hr=%02x, dt=%02x, mo=%02x, dw=%02x yr=%u\n", + __func__, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], yr); + + tm->tm_sec = bcd2bin(buf[ISL12022_REG_SCA0 - ISL12022_ALARM] & 0x7F); + tm->tm_min = bcd2bin(buf[ISL12022_REG_MNA0 - ISL12022_ALARM] & 0x7F); + tm->tm_hour = bcd2bin(buf[ISL12022_REG_HRA0 - ISL12022_ALARM] & 0x3F); + tm->tm_mday = bcd2bin(buf[ISL12022_REG_DTA0 - ISL12022_ALARM] & 0x3F); + tm->tm_mon = bcd2bin(buf[ISL12022_REG_MOA0 - ISL12022_ALARM] & 0x1F) - 1; + tm->tm_wday = buf[ISL12022_REG_DWA0 - ISL12022_ALARM] & 0x07; + tm->tm_year = bcd2bin(yr) + 100; + + for (i = 0; i < ISL12022_ALARM_LEN; i++) { + if (buf[i] & ISL12022_ALARM_ENABLE) { + alarm->enabled = 1; + break; + } + } + + dev_dbg(dev, "%s: %ptR\n", __func__, tm); + + return 0; +} + +static int isl12022_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct rtc_time *alarm_tm = &alarm->time; + struct isl12022 *isl12022 = dev_get_drvdata(dev); + struct regmap *regmap = isl12022->regmap; + u8 regs[ISL12022_ALARM_LEN] = { 0, }; + struct rtc_time rtc_tm; + int ret, enable, dw; + + ret = isl12022_rtc_read_time(dev, &rtc_tm); + if (ret) + return ret; + + /* If the alarm time is before the current time disable the alarm */ + if (!alarm->enabled || rtc_tm_sub(alarm_tm, &rtc_tm) <= 0) + enable = 0; + else + enable = ISL12022_ALARM_ENABLE; + + /* + * Set non-matching day of the week to safeguard against early false + * matching while setting all the alarm registers (this rtc lacks a + * general alarm/irq enable/disable bit). + */ + ret = regmap_read(regmap, ISL12022_REG_DW, &dw); + if (ret) { + dev_dbg(dev, "%s: reading DW failed\n", __func__); + return ret; + } + /* ~4 days into the future should be enough to avoid match */ + dw = ((dw + 4) % 7) | ISL12022_ALARM_ENABLE; + ret = regmap_write(regmap, ISL12022_REG_DWA0, dw); + if (ret) { + dev_dbg(dev, "%s: writing DWA0 failed\n", __func__); + return ret; + } + + /* Program the alarm and enable it for each setting */ + regs[ISL12022_REG_SCA0 - ISL12022_ALARM] = bin2bcd(alarm_tm->tm_sec) | enable; + regs[ISL12022_REG_MNA0 - ISL12022_ALARM] = bin2bcd(alarm_tm->tm_min) | enable; + regs[ISL12022_REG_HRA0 - ISL12022_ALARM] = bin2bcd(alarm_tm->tm_hour) | enable; + regs[ISL12022_REG_DTA0 - ISL12022_ALARM] = bin2bcd(alarm_tm->tm_mday) | enable; + regs[ISL12022_REG_MOA0 - ISL12022_ALARM] = bin2bcd(alarm_tm->tm_mon + 1) | enable; + regs[ISL12022_REG_DWA0 - ISL12022_ALARM] = bin2bcd(alarm_tm->tm_wday & 7) | enable; + + /* write ALARM registers */ + ret = regmap_bulk_write(regmap, ISL12022_ALARM, ®s, sizeof(regs)); + if (ret) { + dev_dbg(dev, "%s: writing ALARM registers failed\n", __func__); + return ret; + } + + return 0; +} + +static irqreturn_t isl12022_rtc_interrupt(int irq, void *data) +{ + struct isl12022 *isl12022 = data; + struct rtc_device *rtc = isl12022->rtc; + struct device *dev = &rtc->dev; + struct regmap *regmap = isl12022->regmap; + u32 val = 0; + unsigned long events = 0; + int ret; + + ret = regmap_read(regmap, ISL12022_REG_SR, &val); + if (ret) { + dev_dbg(dev, "%s: reading SR failed\n", __func__); + return IRQ_HANDLED; + } + + if (val & ISL12022_SR_ALM) + events |= RTC_IRQF | RTC_AF; + + if (events & RTC_AF) + dev_dbg(dev, "alarm!\n"); + + if (!events) + return IRQ_NONE; + + rtc_update_irq(rtc, 1, events); + return IRQ_HANDLED; +} + +static int isl12022_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct isl12022 *isl12022 = dev_get_drvdata(dev); + + /* Make sure enabled is 0 or 1 */ + enabled = !!enabled; + + if (isl12022->irq_enabled == enabled) + return 0; + + if (enabled) + enable_irq(isl12022->irq); + else + disable_irq(isl12022->irq); + + isl12022->irq_enabled = enabled; + + return 0; +} + +static int isl12022_setup_irq(struct device *dev, int irq) +{ + struct isl12022 *isl12022 = dev_get_drvdata(dev); + struct regmap *regmap = isl12022->regmap; + unsigned int reg_mask, reg_val; + u8 buf[ISL12022_ALARM_LEN] = { 0, }; + int ret; + + /* Clear and disable all alarm registers */ + ret = regmap_bulk_write(regmap, ISL12022_ALARM, buf, sizeof(buf)); + if (ret) + return ret; + + /* + * Enable automatic reset of ALM bit and enable single event interrupt + * mode. + */ + reg_mask = ISL12022_INT_ARST | ISL12022_INT_IM | ISL12022_INT_FO_MASK; + reg_val = ISL12022_INT_ARST | ISL12022_INT_FO_OFF; + ret = regmap_write_bits(regmap, ISL12022_REG_INT, + reg_mask, reg_val); + if (ret) + return ret; + + isl12022->irq_enabled = true; + ret = devm_request_threaded_irq(dev, irq, NULL, + isl12022_rtc_interrupt, + IRQF_SHARED | IRQF_ONESHOT, + isl12022_driver.driver.name, + isl12022); + if (ret) + return dev_err_probe(dev, ret, "Unable to request irq %d\n", irq); + + isl12022->irq = irq; + return 0; +} + +static int isl12022_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct isl12022 *isl12022 = dev_get_drvdata(dev); + struct regmap *regmap = isl12022->regmap; + u32 user, val; + int ret; + + switch (cmd) { + case RTC_VL_READ: + ret = regmap_read(regmap, ISL12022_REG_SR, &val); + if (ret) + return ret; + + user = 0; + if (val & ISL12022_SR_LBAT85) + user |= RTC_VL_BACKUP_LOW; + + if (val & ISL12022_SR_LBAT75) + user |= RTC_VL_BACKUP_EMPTY; + + return put_user(user, (u32 __user *)arg); + + default: + return -ENOIOCTLCMD; + } +} + +static const struct rtc_class_ops isl12022_rtc_ops = { + .ioctl = isl12022_rtc_ioctl, + .read_time = isl12022_rtc_read_time, + .set_time = isl12022_rtc_set_time, + .read_alarm = isl12022_rtc_read_alarm, + .set_alarm = isl12022_rtc_set_alarm, + .alarm_irq_enable = isl12022_rtc_alarm_irq_enable, +}; + +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .use_single_write = true, +}; + +static int isl12022_register_clock(struct device *dev) +{ + struct isl12022 *isl12022 = dev_get_drvdata(dev); + struct regmap *regmap = isl12022->regmap; + struct clk_hw *hw; + int ret; + + if (!device_property_present(dev, "#clock-cells")) { + /* + * Disabling the F_OUT pin reduces the power + * consumption in battery mode by ~25%. + */ + regmap_update_bits(regmap, ISL12022_REG_INT, ISL12022_INT_FO_MASK, + ISL12022_INT_FO_OFF); + + return 0; + } + + if (!IS_ENABLED(CONFIG_COMMON_CLK)) + return 0; + + /* + * For now, only support a fixed clock of 32768Hz (the reset default). + */ + ret = regmap_update_bits(regmap, ISL12022_REG_INT, + ISL12022_INT_FO_MASK, ISL12022_INT_FO_32K); + if (ret) + return ret; + + hw = devm_clk_hw_register_fixed_rate(dev, "isl12022", NULL, 0, 32768); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw); +} + +static const u32 trip_levels[2][7] = { + { 2125000, 2295000, 2550000, 2805000, 3060000, 4250000, 4675000 }, + { 1875000, 2025000, 2250000, 2475000, 2700000, 3750000, 4125000 }, +}; + +static void isl12022_set_trip_levels(struct device *dev) +{ + struct isl12022 *isl12022 = dev_get_drvdata(dev); + struct regmap *regmap = isl12022->regmap; + u32 levels[2] = {0, 0}; + int ret, i, j, x[2]; + u8 val, mask; + + device_property_read_u32_array(dev, "isil,battery-trip-levels-microvolt", + levels, 2); + + for (i = 0; i < 2; i++) { + for (j = 0; j < ARRAY_SIZE(trip_levels[i]) - 1; j++) { + if (levels[i] <= trip_levels[i][j]) + break; + } + x[i] = j; + } + + val = FIELD_PREP(ISL12022_REG_VB85_MASK, x[0]) | + FIELD_PREP(ISL12022_REG_VB75_MASK, x[1]); + mask = ISL12022_REG_VB85_MASK | ISL12022_REG_VB75_MASK; + + ret = regmap_update_bits(regmap, ISL12022_REG_PWR_VBAT, mask, val); + if (ret) + dev_warn(dev, "unable to set battery alarm levels: %d\n", ret); + + /* + * Force a write of the TSE bit in the BETA register, in order + * to trigger an update of the LBAT75 and LBAT85 bits in the + * status register. In battery backup mode, those bits have + * another meaning, so without this, they may contain stale + * values for up to a minute after power-on. + */ + regmap_write_bits(regmap, ISL12022_REG_BETA, + ISL12022_BETA_TSE, ISL12022_BETA_TSE); +} + +static int isl12022_probe(struct i2c_client *client) +{ + struct isl12022 *isl12022; + struct rtc_device *rtc; + struct regmap *regmap; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + /* Allocate driver state */ + isl12022 = devm_kzalloc(&client->dev, sizeof(*isl12022), GFP_KERNEL); + if (!isl12022) + return -ENOMEM; + + regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(regmap)) + return dev_err_probe(&client->dev, PTR_ERR(regmap), "regmap allocation failed\n"); + isl12022->regmap = regmap; + + dev_set_drvdata(&client->dev, isl12022); + + ret = isl12022_register_clock(&client->dev); + if (ret) + return ret; + + isl12022_set_trip_levels(&client->dev); + isl12022_hwmon_register(&client->dev); + + rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + isl12022->rtc = rtc; + + rtc->ops = &isl12022_rtc_ops; + rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rtc->range_max = RTC_TIMESTAMP_END_2099; + + if (client->irq > 0) { + ret = isl12022_setup_irq(&client->dev, client->irq); + if (ret) + return ret; + } else { + clear_bit(RTC_FEATURE_ALARM, rtc->features); + } + + return devm_rtc_register_device(rtc); +} + +static const struct of_device_id isl12022_dt_match[] = { + { .compatible = "isl,isl12022" }, /* for backward compat., don't use */ + { .compatible = "isil,isl12022" }, + { }, +}; +MODULE_DEVICE_TABLE(of, isl12022_dt_match); + +static const struct i2c_device_id isl12022_id[] = { + { .name = "isl12022" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, isl12022_id); + +static struct i2c_driver isl12022_driver = { + .driver = { + .name = "rtc-isl12022", + .of_match_table = isl12022_dt_match, + }, + .probe = isl12022_probe, + .id_table = isl12022_id, +}; + +module_i2c_driver(isl12022_driver); + +MODULE_AUTHOR("roman.fietze@telemotive.de"); +MODULE_DESCRIPTION("ISL 12022 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-isl12026.c b/drivers/rtc/rtc-isl12026.c new file mode 100644 index 000000000..b86a325d8 --- /dev/null +++ b/drivers/rtc/rtc-isl12026.c @@ -0,0 +1,506 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * An I2C driver for the Intersil ISL 12026 + * + * Copyright (c) 2018 Cavium, Inc. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* register offsets */ +#define ISL12026_REG_PWR 0x14 +# define ISL12026_REG_PWR_BSW BIT(6) +# define ISL12026_REG_PWR_SBIB BIT(7) +#define ISL12026_REG_SC 0x30 +#define ISL12026_REG_HR 0x32 +# define ISL12026_REG_HR_MIL BIT(7) /* military or 24 hour time */ +#define ISL12026_REG_SR 0x3f +# define ISL12026_REG_SR_RTCF BIT(0) +# define ISL12026_REG_SR_WEL BIT(1) +# define ISL12026_REG_SR_RWEL BIT(2) +# define ISL12026_REG_SR_MBZ BIT(3) +# define ISL12026_REG_SR_OSCF BIT(4) + +/* The EEPROM array responds at i2c address 0x57 */ +#define ISL12026_EEPROM_ADDR 0x57 + +#define ISL12026_PAGESIZE 16 +#define ISL12026_NVMEM_WRITE_TIME 20 + +struct isl12026 { + struct rtc_device *rtc; + struct i2c_client *nvm_client; +}; + +static int isl12026_read_reg(struct i2c_client *client, int reg) +{ + u8 addr[] = {0, reg}; + u8 val; + int ret; + + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = sizeof(addr), + .buf = addr + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .len = 1, + .buf = &val + } + }; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) { + dev_err(&client->dev, "read reg error, ret=%d\n", ret); + ret = ret < 0 ? ret : -EIO; + } else { + ret = val; + } + + return ret; +} + +static int isl12026_arm_write(struct i2c_client *client) +{ + int ret; + u8 op[3]; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = op + }; + + /* Set SR.WEL */ + op[0] = 0; + op[1] = ISL12026_REG_SR; + op[2] = ISL12026_REG_SR_WEL; + msg.len = 3; + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret != 1) { + dev_err(&client->dev, "write error SR.WEL, ret=%d\n", ret); + ret = ret < 0 ? ret : -EIO; + goto out; + } + + /* Set SR.WEL and SR.RWEL */ + op[2] = ISL12026_REG_SR_WEL | ISL12026_REG_SR_RWEL; + msg.len = 3; + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret != 1) { + dev_err(&client->dev, + "write error SR.WEL|SR.RWEL, ret=%d\n", ret); + ret = ret < 0 ? ret : -EIO; + goto out; + } else { + ret = 0; + } +out: + return ret; +} + +static int isl12026_disarm_write(struct i2c_client *client) +{ + int ret; + u8 op[3] = {0, ISL12026_REG_SR, 0}; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = sizeof(op), + .buf = op + }; + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret != 1) { + dev_err(&client->dev, + "write error SR, ret=%d\n", ret); + ret = ret < 0 ? ret : -EIO; + } else { + ret = 0; + } + + return ret; +} + +static int isl12026_write_reg(struct i2c_client *client, int reg, u8 val) +{ + int ret; + u8 op[3] = {0, reg, val}; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = sizeof(op), + .buf = op + }; + + ret = isl12026_arm_write(client); + if (ret) + return ret; + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret != 1) { + dev_err(&client->dev, "write error CCR, ret=%d\n", ret); + ret = ret < 0 ? ret : -EIO; + goto out; + } + + msleep(ISL12026_NVMEM_WRITE_TIME); + + ret = isl12026_disarm_write(client); +out: + return ret; +} + +static int isl12026_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + int ret; + u8 op[10]; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = sizeof(op), + .buf = op + }; + + ret = isl12026_arm_write(client); + if (ret) + return ret; + + /* Set the CCR registers */ + op[0] = 0; + op[1] = ISL12026_REG_SC; + op[2] = bin2bcd(tm->tm_sec); /* SC */ + op[3] = bin2bcd(tm->tm_min); /* MN */ + op[4] = bin2bcd(tm->tm_hour) | ISL12026_REG_HR_MIL; /* HR */ + op[5] = bin2bcd(tm->tm_mday); /* DT */ + op[6] = bin2bcd(tm->tm_mon + 1); /* MO */ + op[7] = bin2bcd(tm->tm_year % 100); /* YR */ + op[8] = bin2bcd(tm->tm_wday & 7); /* DW */ + op[9] = bin2bcd(tm->tm_year >= 100 ? 20 : 19); /* Y2K */ + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret != 1) { + dev_err(&client->dev, "write error CCR, ret=%d\n", ret); + ret = ret < 0 ? ret : -EIO; + goto out; + } + + ret = isl12026_disarm_write(client); +out: + return ret; +} + +static int isl12026_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 ccr[8]; + u8 addr[2]; + u8 sr; + int ret; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = sizeof(addr), + .buf = addr + }, { + .addr = client->addr, + .flags = I2C_M_RD, + } + }; + + /* First, read SR */ + addr[0] = 0; + addr[1] = ISL12026_REG_SR; + msgs[1].len = 1; + msgs[1].buf = &sr; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) { + dev_err(&client->dev, "read error, ret=%d\n", ret); + ret = ret < 0 ? ret : -EIO; + goto out; + } + + if (sr & ISL12026_REG_SR_RTCF) + dev_warn(&client->dev, "Real-Time Clock Failure on read\n"); + if (sr & ISL12026_REG_SR_OSCF) + dev_warn(&client->dev, "Oscillator Failure on read\n"); + + /* Second, CCR regs */ + addr[0] = 0; + addr[1] = ISL12026_REG_SC; + msgs[1].len = sizeof(ccr); + msgs[1].buf = ccr; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) { + dev_err(&client->dev, "read error, ret=%d\n", ret); + ret = ret < 0 ? ret : -EIO; + goto out; + } + + tm->tm_sec = bcd2bin(ccr[0] & 0x7F); + tm->tm_min = bcd2bin(ccr[1] & 0x7F); + if (ccr[2] & ISL12026_REG_HR_MIL) + tm->tm_hour = bcd2bin(ccr[2] & 0x3F); + else + tm->tm_hour = bcd2bin(ccr[2] & 0x1F) + + ((ccr[2] & 0x20) ? 12 : 0); + tm->tm_mday = bcd2bin(ccr[3] & 0x3F); + tm->tm_mon = bcd2bin(ccr[4] & 0x1F) - 1; + tm->tm_year = bcd2bin(ccr[5]); + if (bcd2bin(ccr[7]) == 20) + tm->tm_year += 100; + tm->tm_wday = ccr[6] & 0x07; + + ret = 0; +out: + return ret; +} + +static const struct rtc_class_ops isl12026_rtc_ops = { + .read_time = isl12026_rtc_read_time, + .set_time = isl12026_rtc_set_time, +}; + +static int isl12026_nvm_read(void *p, unsigned int offset, + void *val, size_t bytes) +{ + struct isl12026 *priv = p; + int ret; + u8 addr[2]; + struct i2c_msg msgs[] = { + { + .addr = priv->nvm_client->addr, + .flags = 0, + .len = sizeof(addr), + .buf = addr + }, { + .addr = priv->nvm_client->addr, + .flags = I2C_M_RD, + .buf = val + } + }; + + /* + * offset and bytes checked and limited by nvmem core, so + * proceed without further checks. + */ + ret = mutex_lock_interruptible(&priv->rtc->ops_lock); + if (ret) + return ret; + + /* 2 bytes of address, most significant first */ + addr[0] = offset >> 8; + addr[1] = offset; + msgs[1].len = bytes; + ret = i2c_transfer(priv->nvm_client->adapter, msgs, ARRAY_SIZE(msgs)); + + mutex_unlock(&priv->rtc->ops_lock); + + if (ret != ARRAY_SIZE(msgs)) { + dev_err(&priv->nvm_client->dev, + "nvmem read error, ret=%d\n", ret); + return ret < 0 ? ret : -EIO; + } + + return 0; +} + +static int isl12026_nvm_write(void *p, unsigned int offset, + void *val, size_t bytes) +{ + struct isl12026 *priv = p; + int ret; + u8 *v = val; + size_t chunk_size, num_written; + u8 payload[ISL12026_PAGESIZE + 2]; /* page + 2 address bytes */ + struct i2c_msg msgs[] = { + { + .addr = priv->nvm_client->addr, + .flags = 0, + .buf = payload + } + }; + + /* + * offset and bytes checked and limited by nvmem core, so + * proceed without further checks. + */ + ret = mutex_lock_interruptible(&priv->rtc->ops_lock); + if (ret) + return ret; + + num_written = 0; + while (bytes) { + chunk_size = round_down(offset, ISL12026_PAGESIZE) + + ISL12026_PAGESIZE - offset; + chunk_size = min(bytes, chunk_size); + /* + * 2 bytes of address, most significant first, followed + * by page data bytes + */ + memcpy(payload + 2, v + num_written, chunk_size); + payload[0] = offset >> 8; + payload[1] = offset; + msgs[0].len = chunk_size + 2; + ret = i2c_transfer(priv->nvm_client->adapter, + msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) { + dev_err(&priv->nvm_client->dev, + "nvmem write error, ret=%d\n", ret); + ret = ret < 0 ? ret : -EIO; + break; + } + ret = 0; + bytes -= chunk_size; + offset += chunk_size; + num_written += chunk_size; + msleep(ISL12026_NVMEM_WRITE_TIME); + } + + mutex_unlock(&priv->rtc->ops_lock); + + return ret; +} + +static void isl12026_force_power_modes(struct i2c_client *client) +{ + int ret; + int pwr, requested_pwr; + u32 bsw_val, sbib_val; + bool set_bsw, set_sbib; + + /* + * If we can read the of_property, set the specified value. + * If there is an error reading the of_property (likely + * because it does not exist), keep the current value. + */ + ret = of_property_read_u32(client->dev.of_node, + "isil,pwr-bsw", &bsw_val); + set_bsw = (ret == 0); + + ret = of_property_read_u32(client->dev.of_node, + "isil,pwr-sbib", &sbib_val); + set_sbib = (ret == 0); + + /* Check if PWR.BSW and/or PWR.SBIB need specified values */ + if (!set_bsw && !set_sbib) + return; + + pwr = isl12026_read_reg(client, ISL12026_REG_PWR); + if (pwr < 0) { + dev_warn(&client->dev, "Error: Failed to read PWR %d\n", pwr); + return; + } + + requested_pwr = pwr; + + if (set_bsw) { + if (bsw_val) + requested_pwr |= ISL12026_REG_PWR_BSW; + else + requested_pwr &= ~ISL12026_REG_PWR_BSW; + } /* else keep current BSW */ + + if (set_sbib) { + if (sbib_val) + requested_pwr |= ISL12026_REG_PWR_SBIB; + else + requested_pwr &= ~ISL12026_REG_PWR_SBIB; + } /* else keep current SBIB */ + + if (pwr >= 0 && pwr != requested_pwr) { + dev_dbg(&client->dev, "PWR: %02x\n", pwr); + dev_dbg(&client->dev, "Updating PWR to: %02x\n", requested_pwr); + isl12026_write_reg(client, ISL12026_REG_PWR, requested_pwr); + } +} + +static int isl12026_probe(struct i2c_client *client) +{ + struct isl12026 *priv; + int ret; + struct nvmem_config nvm_cfg = { + .name = "isl12026-", + .base_dev = &client->dev, + .stride = 1, + .word_size = 1, + .size = 512, + .reg_read = isl12026_nvm_read, + .reg_write = isl12026_nvm_write, + }; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + i2c_set_clientdata(client, priv); + + isl12026_force_power_modes(client); + + priv->nvm_client = i2c_new_dummy_device(client->adapter, ISL12026_EEPROM_ADDR); + if (IS_ERR(priv->nvm_client)) + return PTR_ERR(priv->nvm_client); + + priv->rtc = devm_rtc_allocate_device(&client->dev); + ret = PTR_ERR_OR_ZERO(priv->rtc); + if (ret) + return ret; + + priv->rtc->ops = &isl12026_rtc_ops; + nvm_cfg.priv = priv; + ret = devm_rtc_nvmem_register(priv->rtc, &nvm_cfg); + if (ret) + return ret; + + return devm_rtc_register_device(priv->rtc); +} + +static void isl12026_remove(struct i2c_client *client) +{ + struct isl12026 *priv = i2c_get_clientdata(client); + + i2c_unregister_device(priv->nvm_client); +} + +static const struct of_device_id isl12026_dt_match[] = { + { .compatible = "isil,isl12026" }, + { } +}; +MODULE_DEVICE_TABLE(of, isl12026_dt_match); + +static const struct i2c_device_id isl12026_id[] = { + { .name = "isl12026" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, isl12026_id); + +static struct i2c_driver isl12026_driver = { + .driver = { + .name = "rtc-isl12026", + .of_match_table = isl12026_dt_match, + }, + .probe = isl12026_probe, + .remove = isl12026_remove, + .id_table = isl12026_id, +}; + +module_i2c_driver(isl12026_driver); + +MODULE_DESCRIPTION("ISL 12026 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c new file mode 100644 index 000000000..9bdd5d121 --- /dev/null +++ b/drivers/rtc/rtc-isl1208.c @@ -0,0 +1,1001 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Intersil ISL1208 rtc class driver + * + * Copyright 2005,2006 Hebert Valerio Riedel + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register map */ +/* rtc section */ +#define ISL1208_REG_SC 0x00 +#define ISL1208_REG_MN 0x01 +#define ISL1208_REG_HR 0x02 +#define ISL1208_REG_HR_MIL (1<<7) /* 24h/12h mode */ +#define ISL1208_REG_HR_PM (1<<5) /* PM/AM bit in 12h mode */ +#define ISL1208_REG_DT 0x03 +#define ISL1208_REG_MO 0x04 +#define ISL1208_REG_YR 0x05 +#define ISL1208_REG_DW 0x06 +#define ISL1208_RTC_SECTION_LEN 7 + +/* control/status section */ +#define ISL1208_REG_SR 0x07 +#define ISL1208_REG_SR_ARST (1<<7) /* auto reset */ +#define ISL1208_REG_SR_XTOSCB (1<<6) /* crystal oscillator */ +#define ISL1208_REG_SR_WRTC (1<<4) /* write rtc */ +#define ISL1208_REG_SR_EVT (1<<3) /* event */ +#define ISL1208_REG_SR_ALM (1<<2) /* alarm */ +#define ISL1208_REG_SR_BAT (1<<1) /* battery */ +#define ISL1208_REG_SR_RTCF (1<<0) /* rtc fail */ +#define ISL1208_REG_INT 0x08 +#define ISL1208_REG_INT_ALME (1<<6) /* alarm enable */ +#define ISL1208_REG_INT_IM (1<<7) /* interrupt/alarm mode */ +#define ISL1219_REG_EV 0x09 +#define ISL1219_REG_EV_EVEN (1<<4) /* event detection enable */ +#define ISL1219_REG_EV_EVIENB (1<<7) /* event in pull-up disable */ +#define ISL1208_REG_ATR 0x0a +#define ISL1208_REG_DTR 0x0b + +/* alarm section */ +#define ISL1208_REG_SCA 0x0c +#define ISL1208_REG_MNA 0x0d +#define ISL1208_REG_HRA 0x0e +#define ISL1208_REG_DTA 0x0f +#define ISL1208_REG_MOA 0x10 +#define ISL1208_REG_DWA 0x11 +#define ISL1208_ALARM_SECTION_LEN 6 + +/* user section */ +#define ISL1208_REG_USR1 0x12 +#define ISL1208_REG_USR2 0x13 +#define ISL1208_USR_SECTION_LEN 2 + +/* event section */ +#define ISL1219_REG_SCT 0x14 +#define ISL1219_REG_MNT 0x15 +#define ISL1219_REG_HRT 0x16 +#define ISL1219_REG_DTT 0x17 +#define ISL1219_REG_MOT 0x18 +#define ISL1219_REG_YRT 0x19 +#define ISL1219_EVT_SECTION_LEN 6 + +static struct i2c_driver isl1208_driver; + +/* Chip capabilities table */ +struct isl1208_config { + unsigned int nvmem_length; + unsigned has_tamper:1; + unsigned has_timestamp:1; + unsigned has_inverted_osc_bit:1; +}; + +static const struct isl1208_config config_isl1208 = { + .nvmem_length = 2, + .has_tamper = false, + .has_timestamp = false +}; + +static const struct isl1208_config config_isl1209 = { + .nvmem_length = 2, + .has_tamper = true, + .has_timestamp = false +}; + +static const struct isl1208_config config_isl1218 = { + .nvmem_length = 8, + .has_tamper = false, + .has_timestamp = false +}; + +static const struct isl1208_config config_isl1219 = { + .nvmem_length = 2, + .has_tamper = true, + .has_timestamp = true +}; + +static const struct isl1208_config config_raa215300_a0 = { + .nvmem_length = 2, + .has_tamper = false, + .has_timestamp = false, + .has_inverted_osc_bit = true +}; + +static const struct i2c_device_id isl1208_id[] = { + { .name = "isl1208", .driver_data = (kernel_ulong_t)&config_isl1208 }, + { .name = "isl1209", .driver_data = (kernel_ulong_t)&config_isl1209 }, + { .name = "isl1218", .driver_data = (kernel_ulong_t)&config_isl1218 }, + { .name = "isl1219", .driver_data = (kernel_ulong_t)&config_isl1219 }, + { .name = "raa215300_a0", .driver_data = (kernel_ulong_t)&config_raa215300_a0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, isl1208_id); + +static const __maybe_unused struct of_device_id isl1208_of_match[] = { + { .compatible = "isil,isl1208", .data = &config_isl1208 }, + { .compatible = "isil,isl1209", .data = &config_isl1209 }, + { .compatible = "isil,isl1218", .data = &config_isl1218 }, + { .compatible = "isil,isl1219", .data = &config_isl1219 }, + { } +}; +MODULE_DEVICE_TABLE(of, isl1208_of_match); + +/* Device state */ +struct isl1208_state { + struct nvmem_config nvmem_config; + struct rtc_device *rtc; + const struct isl1208_config *config; +}; + +/* block read */ +static int +isl1208_i2c_read_regs(struct i2c_client *client, u8 reg, u8 buf[], + unsigned len) +{ + int ret; + + WARN_ON(reg > ISL1219_REG_YRT); + WARN_ON(reg + len > ISL1219_REG_YRT + 1); + + ret = i2c_smbus_read_i2c_block_data(client, reg, len, buf); + return (ret < 0) ? ret : 0; +} + +/* block write */ +static int +isl1208_i2c_set_regs(struct i2c_client *client, u8 reg, u8 const buf[], + unsigned len) +{ + int ret; + + WARN_ON(reg > ISL1219_REG_YRT); + WARN_ON(reg + len > ISL1219_REG_YRT + 1); + + ret = i2c_smbus_write_i2c_block_data(client, reg, len, buf); + return (ret < 0) ? ret : 0; +} + +/* simple check to see whether we have a isl1208 */ +static int +isl1208_i2c_validate_client(struct i2c_client *client) +{ + u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, }; + u8 zero_mask[ISL1208_RTC_SECTION_LEN] = { + 0x80, 0x80, 0x40, 0xc0, 0xe0, 0x00, 0xf8 + }; + int i; + int ret; + + ret = isl1208_i2c_read_regs(client, 0, regs, ISL1208_RTC_SECTION_LEN); + if (ret < 0) + return ret; + + for (i = 0; i < ISL1208_RTC_SECTION_LEN; ++i) { + if (regs[i] & zero_mask[i]) /* check if bits are cleared */ + return -ENODEV; + } + + return 0; +} + +static int isl1208_set_xtoscb(struct i2c_client *client, int sr, int xtosb_val) +{ + /* Do nothing if bit is already set to desired value */ + if (!!(sr & ISL1208_REG_SR_XTOSCB) == xtosb_val) + return 0; + + if (xtosb_val) + sr |= ISL1208_REG_SR_XTOSCB; + else + sr &= ~ISL1208_REG_SR_XTOSCB; + + return i2c_smbus_write_byte_data(client, ISL1208_REG_SR, sr); +} + +static int +isl1208_i2c_get_sr(struct i2c_client *client) +{ + return i2c_smbus_read_byte_data(client, ISL1208_REG_SR); +} + +static int +isl1208_i2c_get_atr(struct i2c_client *client) +{ + int atr = i2c_smbus_read_byte_data(client, ISL1208_REG_ATR); + if (atr < 0) + return atr; + + /* The 6bit value in the ATR register controls the load + * capacitance C_load * in steps of 0.25pF + * + * bit (1<<5) of the ATR register is inverted + * + * C_load(ATR=0x20) = 4.50pF + * C_load(ATR=0x00) = 12.50pF + * C_load(ATR=0x1f) = 20.25pF + * + */ + + atr &= 0x3f; /* mask out lsb */ + atr ^= 1 << 5; /* invert 6th bit */ + atr += 2 * 9; /* add offset of 4.5pF; unit[atr] = 0.25pF */ + + return atr; +} + +/* returns adjustment value + 100 */ +static int +isl1208_i2c_get_dtr(struct i2c_client *client) +{ + int dtr = i2c_smbus_read_byte_data(client, ISL1208_REG_DTR); + if (dtr < 0) + return -EIO; + + /* dtr encodes adjustments of {-60,-40,-20,0,20,40,60} ppm */ + dtr = ((dtr & 0x3) * 20) * (dtr & (1 << 2) ? -1 : 1); + + return dtr + 100; +} + +static int +isl1208_i2c_get_usr(struct i2c_client *client) +{ + u8 buf[ISL1208_USR_SECTION_LEN] = { 0, }; + int ret; + + ret = isl1208_i2c_read_regs(client, ISL1208_REG_USR1, buf, + ISL1208_USR_SECTION_LEN); + if (ret < 0) + return ret; + + return (buf[1] << 8) | buf[0]; +} + +static int +isl1208_i2c_set_usr(struct i2c_client *client, u16 usr) +{ + u8 buf[ISL1208_USR_SECTION_LEN]; + + buf[0] = usr & 0xff; + buf[1] = (usr >> 8) & 0xff; + + return isl1208_i2c_set_regs(client, ISL1208_REG_USR1, buf, + ISL1208_USR_SECTION_LEN); +} + +static int +isl1208_rtc_toggle_alarm(struct i2c_client *client, int enable) +{ + int icr = i2c_smbus_read_byte_data(client, ISL1208_REG_INT); + + if (icr < 0) { + dev_err(&client->dev, "%s: reading INT failed\n", __func__); + return icr; + } + + if (enable) + icr |= ISL1208_REG_INT_ALME | ISL1208_REG_INT_IM; + else + icr &= ~(ISL1208_REG_INT_ALME | ISL1208_REG_INT_IM); + + icr = i2c_smbus_write_byte_data(client, ISL1208_REG_INT, icr); + if (icr < 0) { + dev_err(&client->dev, "%s: writing INT failed\n", __func__); + return icr; + } + + return 0; +} + +static int +isl1208_rtc_proc(struct device *dev, struct seq_file *seq) +{ + struct i2c_client *const client = to_i2c_client(dev); + int sr, dtr, atr, usr; + + sr = isl1208_i2c_get_sr(client); + if (sr < 0) { + dev_err(&client->dev, "%s: reading SR failed\n", __func__); + return sr; + } + + seq_printf(seq, "status_reg\t:%s%s%s%s%s%s (0x%.2x)\n", + (sr & ISL1208_REG_SR_RTCF) ? " RTCF" : "", + (sr & ISL1208_REG_SR_BAT) ? " BAT" : "", + (sr & ISL1208_REG_SR_ALM) ? " ALM" : "", + (sr & ISL1208_REG_SR_WRTC) ? " WRTC" : "", + (sr & ISL1208_REG_SR_XTOSCB) ? " XTOSCB" : "", + (sr & ISL1208_REG_SR_ARST) ? " ARST" : "", sr); + + seq_printf(seq, "batt_status\t: %s\n", + (sr & ISL1208_REG_SR_RTCF) ? "bad" : "okay"); + + dtr = isl1208_i2c_get_dtr(client); + if (dtr >= 0) + seq_printf(seq, "digital_trim\t: %d ppm\n", dtr - 100); + + atr = isl1208_i2c_get_atr(client); + if (atr >= 0) + seq_printf(seq, "analog_trim\t: %d.%.2d pF\n", + atr >> 2, (atr & 0x3) * 25); + + usr = isl1208_i2c_get_usr(client); + if (usr >= 0) + seq_printf(seq, "user_data\t: 0x%.4x\n", usr); + + return 0; +} + +static int +isl1208_i2c_read_time(struct i2c_client *client, struct rtc_time *tm) +{ + int sr; + u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, }; + + sr = isl1208_i2c_get_sr(client); + if (sr < 0) { + dev_err(&client->dev, "%s: reading SR failed\n", __func__); + return -EIO; + } + + sr = isl1208_i2c_read_regs(client, 0, regs, ISL1208_RTC_SECTION_LEN); + if (sr < 0) { + dev_err(&client->dev, "%s: reading RTC section failed\n", + __func__); + return sr; + } + + tm->tm_sec = bcd2bin(regs[ISL1208_REG_SC]); + tm->tm_min = bcd2bin(regs[ISL1208_REG_MN]); + + /* HR field has a more complex interpretation */ + { + const u8 _hr = regs[ISL1208_REG_HR]; + if (_hr & ISL1208_REG_HR_MIL) /* 24h format */ + tm->tm_hour = bcd2bin(_hr & 0x3f); + else { + /* 12h format */ + tm->tm_hour = bcd2bin(_hr & 0x1f); + if (_hr & ISL1208_REG_HR_PM) /* PM flag set */ + tm->tm_hour += 12; + } + } + + tm->tm_mday = bcd2bin(regs[ISL1208_REG_DT]); + tm->tm_mon = bcd2bin(regs[ISL1208_REG_MO]) - 1; /* rtc starts at 1 */ + tm->tm_year = bcd2bin(regs[ISL1208_REG_YR]) + 100; + tm->tm_wday = bcd2bin(regs[ISL1208_REG_DW]); + + return 0; +} + +static int +isl1208_i2c_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm) +{ + struct rtc_time *const tm = &alarm->time; + u8 regs[ISL1208_ALARM_SECTION_LEN] = { 0, }; + int icr, yr, sr = isl1208_i2c_get_sr(client); + + if (sr < 0) { + dev_err(&client->dev, "%s: reading SR failed\n", __func__); + return sr; + } + + sr = isl1208_i2c_read_regs(client, ISL1208_REG_SCA, regs, + ISL1208_ALARM_SECTION_LEN); + if (sr < 0) { + dev_err(&client->dev, "%s: reading alarm section failed\n", + __func__); + return sr; + } + + /* MSB of each alarm register is an enable bit */ + tm->tm_sec = bcd2bin(regs[ISL1208_REG_SCA - ISL1208_REG_SCA] & 0x7f); + tm->tm_min = bcd2bin(regs[ISL1208_REG_MNA - ISL1208_REG_SCA] & 0x7f); + tm->tm_hour = bcd2bin(regs[ISL1208_REG_HRA - ISL1208_REG_SCA] & 0x3f); + tm->tm_mday = bcd2bin(regs[ISL1208_REG_DTA - ISL1208_REG_SCA] & 0x3f); + tm->tm_mon = + bcd2bin(regs[ISL1208_REG_MOA - ISL1208_REG_SCA] & 0x1f) - 1; + tm->tm_wday = bcd2bin(regs[ISL1208_REG_DWA - ISL1208_REG_SCA] & 0x03); + + /* The alarm doesn't store the year so get it from the rtc section */ + yr = i2c_smbus_read_byte_data(client, ISL1208_REG_YR); + if (yr < 0) { + dev_err(&client->dev, "%s: reading RTC YR failed\n", __func__); + return yr; + } + tm->tm_year = bcd2bin(yr) + 100; + + icr = i2c_smbus_read_byte_data(client, ISL1208_REG_INT); + if (icr < 0) { + dev_err(&client->dev, "%s: reading INT failed\n", __func__); + return icr; + } + alarm->enabled = !!(icr & ISL1208_REG_INT_ALME); + + return 0; +} + +static int +isl1208_i2c_set_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm) +{ + struct rtc_time *alarm_tm = &alarm->time; + u8 regs[ISL1208_ALARM_SECTION_LEN] = { 0, }; + const int offs = ISL1208_REG_SCA; + struct rtc_time rtc_tm; + int err, enable; + + err = isl1208_i2c_read_time(client, &rtc_tm); + if (err) + return err; + + /* If the alarm time is before the current time disable the alarm */ + if (!alarm->enabled || rtc_tm_sub(alarm_tm, &rtc_tm) <= 0) + enable = 0x00; + else + enable = 0x80; + + /* Program the alarm and enable it for each setting */ + regs[ISL1208_REG_SCA - offs] = bin2bcd(alarm_tm->tm_sec) | enable; + regs[ISL1208_REG_MNA - offs] = bin2bcd(alarm_tm->tm_min) | enable; + regs[ISL1208_REG_HRA - offs] = bin2bcd(alarm_tm->tm_hour) | + ISL1208_REG_HR_MIL | enable; + + regs[ISL1208_REG_DTA - offs] = bin2bcd(alarm_tm->tm_mday) | enable; + regs[ISL1208_REG_MOA - offs] = bin2bcd(alarm_tm->tm_mon + 1) | enable; + regs[ISL1208_REG_DWA - offs] = bin2bcd(alarm_tm->tm_wday & 7) | enable; + + /* write ALARM registers */ + err = isl1208_i2c_set_regs(client, offs, regs, + ISL1208_ALARM_SECTION_LEN); + if (err < 0) { + dev_err(&client->dev, "%s: writing ALARM section failed\n", + __func__); + return err; + } + + err = isl1208_rtc_toggle_alarm(client, enable); + if (err) + return err; + + return 0; +} + +static int +isl1208_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + return isl1208_i2c_read_time(to_i2c_client(dev), tm); +} + +static int +isl1208_i2c_set_time(struct i2c_client *client, struct rtc_time const *tm) +{ + int sr; + u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, }; + + /* The clock has an 8 bit wide bcd-coded register (they never learn) + * for the year. tm_year is an offset from 1900 and we are interested + * in the 2000-2099 range, so any value less than 100 is invalid. + */ + if (tm->tm_year < 100) + return -EINVAL; + + regs[ISL1208_REG_SC] = bin2bcd(tm->tm_sec); + regs[ISL1208_REG_MN] = bin2bcd(tm->tm_min); + regs[ISL1208_REG_HR] = bin2bcd(tm->tm_hour) | ISL1208_REG_HR_MIL; + + regs[ISL1208_REG_DT] = bin2bcd(tm->tm_mday); + regs[ISL1208_REG_MO] = bin2bcd(tm->tm_mon + 1); + regs[ISL1208_REG_YR] = bin2bcd(tm->tm_year - 100); + + regs[ISL1208_REG_DW] = bin2bcd(tm->tm_wday & 7); + + sr = isl1208_i2c_get_sr(client); + if (sr < 0) { + dev_err(&client->dev, "%s: reading SR failed\n", __func__); + return sr; + } + + /* set WRTC */ + sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR, + sr | ISL1208_REG_SR_WRTC); + if (sr < 0) { + dev_err(&client->dev, "%s: writing SR failed\n", __func__); + return sr; + } + + /* write RTC registers */ + sr = isl1208_i2c_set_regs(client, 0, regs, ISL1208_RTC_SECTION_LEN); + if (sr < 0) { + dev_err(&client->dev, "%s: writing RTC section failed\n", + __func__); + return sr; + } + + /* clear WRTC again */ + sr = isl1208_i2c_get_sr(client); + if (sr < 0) { + dev_err(&client->dev, "%s: reading SR failed\n", __func__); + return sr; + } + sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR, + sr & ~ISL1208_REG_SR_WRTC); + if (sr < 0) { + dev_err(&client->dev, "%s: writing SR failed\n", __func__); + return sr; + } + + return 0; +} + +static int +isl1208_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + return isl1208_i2c_set_time(to_i2c_client(dev), tm); +} + +static int +isl1208_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + return isl1208_i2c_read_alarm(to_i2c_client(dev), alarm); +} + +static int +isl1208_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + return isl1208_i2c_set_alarm(to_i2c_client(dev), alarm); +} + +static ssize_t timestamp0_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev->parent); + int sr; + + sr = isl1208_i2c_get_sr(client); + if (sr < 0) { + dev_err(dev, "%s: reading SR failed\n", __func__); + return sr; + } + + sr &= ~ISL1208_REG_SR_EVT; + + sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR, sr); + if (sr < 0) + dev_err(dev, "%s: writing SR failed\n", + __func__); + + return count; +}; + +static ssize_t timestamp0_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev->parent); + u8 regs[ISL1219_EVT_SECTION_LEN] = { 0, }; + struct rtc_time tm; + int sr; + + sr = isl1208_i2c_get_sr(client); + if (sr < 0) { + dev_err(dev, "%s: reading SR failed\n", __func__); + return sr; + } + + if (!(sr & ISL1208_REG_SR_EVT)) + return 0; + + sr = isl1208_i2c_read_regs(client, ISL1219_REG_SCT, regs, + ISL1219_EVT_SECTION_LEN); + if (sr < 0) { + dev_err(dev, "%s: reading event section failed\n", + __func__); + return 0; + } + + /* MSB of each alarm register is an enable bit */ + tm.tm_sec = bcd2bin(regs[ISL1219_REG_SCT - ISL1219_REG_SCT] & 0x7f); + tm.tm_min = bcd2bin(regs[ISL1219_REG_MNT - ISL1219_REG_SCT] & 0x7f); + tm.tm_hour = bcd2bin(regs[ISL1219_REG_HRT - ISL1219_REG_SCT] & 0x3f); + tm.tm_mday = bcd2bin(regs[ISL1219_REG_DTT - ISL1219_REG_SCT] & 0x3f); + tm.tm_mon = + bcd2bin(regs[ISL1219_REG_MOT - ISL1219_REG_SCT] & 0x1f) - 1; + tm.tm_year = bcd2bin(regs[ISL1219_REG_YRT - ISL1219_REG_SCT]) + 100; + + sr = rtc_valid_tm(&tm); + if (sr) + return sr; + + return sprintf(buf, "%llu\n", + (unsigned long long)rtc_tm_to_time64(&tm)); +}; + +static DEVICE_ATTR_RW(timestamp0); + +static irqreturn_t +isl1208_rtc_interrupt(int irq, void *data) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + struct i2c_client *client = data; + struct isl1208_state *isl1208 = i2c_get_clientdata(client); + int handled = 0, sr, err; + + if (!isl1208->config->has_tamper) { + /* + * The INT# output is pulled low 250ms after the alarm is + * triggered. After the INT# output is pulled low, it is low for + * at least 250ms, even if the correct action is taken to clear + * it. It is impossible to clear ALM if it is still active. The + * host must wait for the RTC to progress past the alarm time + * plus the 250ms delay before clearing ALM. + */ + msleep(250); + } + + /* + * I2C reads get NAK'ed if we read straight away after an interrupt? + * Using a mdelay/msleep didn't seem to help either, so we work around + * this by continually trying to read the register for a short time. + */ + while (1) { + sr = isl1208_i2c_get_sr(client); + if (sr >= 0) + break; + + if (time_after(jiffies, timeout)) { + dev_err(&client->dev, "%s: reading SR failed\n", + __func__); + return sr; + } + } + + if (sr & ISL1208_REG_SR_ALM) { + dev_dbg(&client->dev, "alarm!\n"); + + rtc_update_irq(isl1208->rtc, 1, RTC_IRQF | RTC_AF); + + /* Disable the alarm */ + err = isl1208_rtc_toggle_alarm(client, 0); + if (err) + return err; + + fsleep(275); + + /* Clear the alarm */ + sr &= ~ISL1208_REG_SR_ALM; + sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR, sr); + if (sr < 0) + dev_err(&client->dev, "%s: writing SR failed\n", + __func__); + else + handled = 1; + } + + if (isl1208->config->has_tamper && (sr & ISL1208_REG_SR_EVT)) { + dev_warn(&client->dev, "event detected"); + handled = 1; + if (isl1208->config->has_timestamp) + sysfs_notify(&isl1208->rtc->dev.kobj, NULL, + dev_attr_timestamp0.attr.name); + } + + return handled ? IRQ_HANDLED : IRQ_NONE; +} + +static const struct rtc_class_ops isl1208_rtc_ops = { + .proc = isl1208_rtc_proc, + .read_time = isl1208_rtc_read_time, + .set_time = isl1208_rtc_set_time, + .read_alarm = isl1208_rtc_read_alarm, + .set_alarm = isl1208_rtc_set_alarm, +}; + +/* sysfs interface */ + +static ssize_t +isl1208_sysfs_show_atrim(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int atr = isl1208_i2c_get_atr(to_i2c_client(dev->parent)); + if (atr < 0) + return atr; + + return sprintf(buf, "%d.%.2d pF\n", atr >> 2, (atr & 0x3) * 25); +} + +static DEVICE_ATTR(atrim, S_IRUGO, isl1208_sysfs_show_atrim, NULL); + +static ssize_t +isl1208_sysfs_show_dtrim(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int dtr = isl1208_i2c_get_dtr(to_i2c_client(dev->parent)); + if (dtr < 0) + return dtr; + + return sprintf(buf, "%d ppm\n", dtr - 100); +} + +static DEVICE_ATTR(dtrim, S_IRUGO, isl1208_sysfs_show_dtrim, NULL); + +static ssize_t +isl1208_sysfs_show_usr(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int usr = isl1208_i2c_get_usr(to_i2c_client(dev->parent)); + if (usr < 0) + return usr; + + return sprintf(buf, "0x%.4x\n", usr); +} + +static ssize_t +isl1208_sysfs_store_usr(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int usr = -1; + + if (buf[0] == '0' && (buf[1] == 'x' || buf[1] == 'X')) { + if (sscanf(buf, "%x", &usr) != 1) + return -EINVAL; + } else { + if (sscanf(buf, "%d", &usr) != 1) + return -EINVAL; + } + + if (usr < 0 || usr > 0xffff) + return -EINVAL; + + if (isl1208_i2c_set_usr(to_i2c_client(dev->parent), usr)) + return -EIO; + + return count; +} + +static DEVICE_ATTR(usr, S_IRUGO | S_IWUSR, isl1208_sysfs_show_usr, + isl1208_sysfs_store_usr); + +static struct attribute *isl1208_rtc_attrs[] = { + &dev_attr_atrim.attr, + &dev_attr_dtrim.attr, + &dev_attr_usr.attr, + NULL +}; + +static const struct attribute_group isl1208_rtc_sysfs_files = { + .attrs = isl1208_rtc_attrs, +}; + +static struct attribute *isl1219_rtc_attrs[] = { + &dev_attr_timestamp0.attr, + NULL +}; + +static const struct attribute_group isl1219_rtc_sysfs_files = { + .attrs = isl1219_rtc_attrs, +}; + +static int isl1208_nvmem_read(void *priv, unsigned int off, void *buf, + size_t count) +{ + struct isl1208_state *isl1208 = priv; + struct i2c_client *client = to_i2c_client(isl1208->rtc->dev.parent); + + /* nvmem sanitizes offset/count for us, but count==0 is possible */ + if (!count) + return count; + + return isl1208_i2c_read_regs(client, ISL1208_REG_USR1 + off, buf, + count); +} + +static int isl1208_nvmem_write(void *priv, unsigned int off, void *buf, + size_t count) +{ + struct isl1208_state *isl1208 = priv; + struct i2c_client *client = to_i2c_client(isl1208->rtc->dev.parent); + + /* nvmem sanitizes off/count for us, but count==0 is possible */ + if (!count) + return count; + + return isl1208_i2c_set_regs(client, ISL1208_REG_USR1 + off, buf, + count); +} + +static const struct nvmem_config isl1208_nvmem_config = { + .name = "isl1208_nvram", + .word_size = 1, + .stride = 1, + /* .size from chip specific config */ + .reg_read = isl1208_nvmem_read, + .reg_write = isl1208_nvmem_write, +}; + +static void isl1208_disable_irq_wake_action(void *data) +{ + disable_irq_wake((unsigned long)data); +} + +static int isl1208_setup_irq(struct i2c_client *client, int irq) +{ + int rc = devm_request_threaded_irq(&client->dev, irq, NULL, + isl1208_rtc_interrupt, + IRQF_SHARED | IRQF_ONESHOT, + isl1208_driver.driver.name, + client); + if (!rc) { + device_init_wakeup(&client->dev, true); + rc = enable_irq_wake(irq); + if (rc) + return rc; + + rc = devm_add_action_or_reset(&client->dev, + isl1208_disable_irq_wake_action, + (void *)(unsigned long)irq); + if (rc) + return rc; + } else { + dev_err(&client->dev, + "Unable to request irq %d, no alarm support\n", + irq); + } + return rc; +} + +static int +isl1208_clk_present(struct i2c_client *client, const char *name) +{ + struct clk *clk; + + clk = devm_clk_get_optional(&client->dev, name); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + return !!clk; +} + +static int +isl1208_probe(struct i2c_client *client) +{ + struct isl1208_state *isl1208; + int evdet_irq = -1; + int xtosb_val = 0; + int rc = 0; + int sr; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + if (isl1208_i2c_validate_client(client) < 0) + return -ENODEV; + + /* Allocate driver state, point i2c client data to it */ + isl1208 = devm_kzalloc(&client->dev, sizeof(*isl1208), GFP_KERNEL); + if (!isl1208) + return -ENOMEM; + i2c_set_clientdata(client, isl1208); + + /* Determine which chip we have */ + isl1208->config = i2c_get_match_data(client); + if (!isl1208->config) + return -ENODEV; + + rc = isl1208_clk_present(client, "xin"); + if (rc < 0) + return rc; + + if (!rc) { + rc = isl1208_clk_present(client, "clkin"); + if (rc < 0) + return rc; + + if (rc) + xtosb_val = 1; + } + + isl1208->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(isl1208->rtc)) + return PTR_ERR(isl1208->rtc); + + isl1208->rtc->ops = &isl1208_rtc_ops; + + /* Setup nvmem configuration in driver state struct */ + isl1208->nvmem_config = isl1208_nvmem_config; + isl1208->nvmem_config.size = isl1208->config->nvmem_length; + isl1208->nvmem_config.priv = isl1208; + + sr = isl1208_i2c_get_sr(client); + if (sr < 0) { + dev_err(&client->dev, "reading status failed\n"); + return sr; + } + + if (isl1208->config->has_inverted_osc_bit) + xtosb_val = !xtosb_val; + + rc = isl1208_set_xtoscb(client, sr, xtosb_val); + if (rc) + return rc; + + if (sr & ISL1208_REG_SR_RTCF) + dev_warn(&client->dev, "rtc power failure detected, " + "please set clock.\n"); + + if (isl1208->config->has_tamper) { + struct device_node *np = client->dev.of_node; + u32 evienb; + + rc = i2c_smbus_read_byte_data(client, ISL1219_REG_EV); + if (rc < 0) { + dev_err(&client->dev, "failed to read EV reg\n"); + return rc; + } + rc |= ISL1219_REG_EV_EVEN; + if (!of_property_read_u32(np, "isil,ev-evienb", &evienb)) { + if (evienb) + rc |= ISL1219_REG_EV_EVIENB; + else + rc &= ~ISL1219_REG_EV_EVIENB; + } + rc = i2c_smbus_write_byte_data(client, ISL1219_REG_EV, rc); + if (rc < 0) { + dev_err(&client->dev, "could not enable tamper detection\n"); + return rc; + } + evdet_irq = of_irq_get_byname(np, "evdet"); + } + if (isl1208->config->has_timestamp) { + rc = rtc_add_group(isl1208->rtc, &isl1219_rtc_sysfs_files); + if (rc) + return rc; + } + + rc = rtc_add_group(isl1208->rtc, &isl1208_rtc_sysfs_files); + if (rc) + return rc; + + if (client->irq > 0) { + rc = isl1208_setup_irq(client, client->irq); + if (rc) + return rc; + } else { + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, isl1208->rtc->features); + } + + if (evdet_irq > 0 && evdet_irq != client->irq) + rc = isl1208_setup_irq(client, evdet_irq); + if (rc) + return rc; + + rc = devm_rtc_nvmem_register(isl1208->rtc, &isl1208->nvmem_config); + if (rc) + return rc; + + return devm_rtc_register_device(isl1208->rtc); +} + +static struct i2c_driver isl1208_driver = { + .driver = { + .name = "rtc-isl1208", + .of_match_table = of_match_ptr(isl1208_of_match), + }, + .probe = isl1208_probe, + .id_table = isl1208_id, +}; + +module_i2c_driver(isl1208_driver); + +MODULE_AUTHOR("Herbert Valerio Riedel "); +MODULE_DESCRIPTION("Intersil ISL1208 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c new file mode 100644 index 000000000..11fce47be --- /dev/null +++ b/drivers/rtc/rtc-jz4740.c @@ -0,0 +1,439 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2009-2010, Lars-Peter Clausen + * Copyright (C) 2010, Paul Cercueil + * JZ4740 SoC RTC driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define JZ_REG_RTC_CTRL 0x00 +#define JZ_REG_RTC_SEC 0x04 +#define JZ_REG_RTC_SEC_ALARM 0x08 +#define JZ_REG_RTC_REGULATOR 0x0C +#define JZ_REG_RTC_HIBERNATE 0x20 +#define JZ_REG_RTC_WAKEUP_FILTER 0x24 +#define JZ_REG_RTC_RESET_COUNTER 0x28 +#define JZ_REG_RTC_SCRATCHPAD 0x34 +#define JZ_REG_RTC_CKPCR 0x40 + +/* The following are present on the jz4780 */ +#define JZ_REG_RTC_WENR 0x3C +#define JZ_RTC_WENR_WEN BIT(31) + +#define JZ_RTC_CTRL_WRDY BIT(7) +#define JZ_RTC_CTRL_1HZ BIT(6) +#define JZ_RTC_CTRL_1HZ_IRQ BIT(5) +#define JZ_RTC_CTRL_AF BIT(4) +#define JZ_RTC_CTRL_AF_IRQ BIT(3) +#define JZ_RTC_CTRL_AE BIT(2) +#define JZ_RTC_CTRL_ENABLE BIT(0) + +/* Magic value to enable writes on jz4780 */ +#define JZ_RTC_WENR_MAGIC 0xA55A + +#define JZ_RTC_WAKEUP_FILTER_MASK 0x0000FFE0 +#define JZ_RTC_RESET_COUNTER_MASK 0x00000FE0 + +#define JZ_RTC_CKPCR_CK32PULL_DIS BIT(4) +#define JZ_RTC_CKPCR_CK32CTL_EN (BIT(2) | BIT(1)) + +enum jz4740_rtc_type { + ID_JZ4740, + ID_JZ4760, + ID_JZ4780, +}; + +struct jz4740_rtc { + void __iomem *base; + enum jz4740_rtc_type type; + + struct rtc_device *rtc; + + struct clk_hw clk32k; + + spinlock_t lock; +}; + +static struct device *dev_for_power_off; + +static inline uint32_t jz4740_rtc_reg_read(struct jz4740_rtc *rtc, size_t reg) +{ + return readl(rtc->base + reg); +} + +static int jz4740_rtc_wait_write_ready(struct jz4740_rtc *rtc) +{ + uint32_t ctrl; + + return readl_poll_timeout(rtc->base + JZ_REG_RTC_CTRL, ctrl, + ctrl & JZ_RTC_CTRL_WRDY, 0, 1000); +} + +static inline int jz4780_rtc_enable_write(struct jz4740_rtc *rtc) +{ + uint32_t ctrl; + int ret; + + ret = jz4740_rtc_wait_write_ready(rtc); + if (ret != 0) + return ret; + + writel(JZ_RTC_WENR_MAGIC, rtc->base + JZ_REG_RTC_WENR); + + return readl_poll_timeout(rtc->base + JZ_REG_RTC_WENR, ctrl, + ctrl & JZ_RTC_WENR_WEN, 0, 1000); +} + +static inline int jz4740_rtc_reg_write(struct jz4740_rtc *rtc, size_t reg, + uint32_t val) +{ + int ret = 0; + + if (rtc->type >= ID_JZ4760) + ret = jz4780_rtc_enable_write(rtc); + if (ret == 0) + ret = jz4740_rtc_wait_write_ready(rtc); + if (ret == 0) + writel(val, rtc->base + reg); + + return ret; +} + +static int jz4740_rtc_ctrl_set_bits(struct jz4740_rtc *rtc, uint32_t mask, + bool set) +{ + int ret; + unsigned long flags; + uint32_t ctrl; + + spin_lock_irqsave(&rtc->lock, flags); + + ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); + + /* Don't clear interrupt flags by accident */ + ctrl |= JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF; + + if (set) + ctrl |= mask; + else + ctrl &= ~mask; + + ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_CTRL, ctrl); + + spin_unlock_irqrestore(&rtc->lock, flags); + + return ret; +} + +static int jz4740_rtc_read_time(struct device *dev, struct rtc_time *time) +{ + struct jz4740_rtc *rtc = dev_get_drvdata(dev); + uint32_t secs, secs2; + int timeout = 5; + + if (jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SCRATCHPAD) != 0x12345678) + return -EINVAL; + + /* If the seconds register is read while it is updated, it can contain a + * bogus value. This can be avoided by making sure that two consecutive + * reads have the same value. + */ + secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC); + secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC); + + while (secs != secs2 && --timeout) { + secs = secs2; + secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC); + } + + if (timeout == 0) + return -EIO; + + rtc_time64_to_tm(secs, time); + + return 0; +} + +static int jz4740_rtc_set_time(struct device *dev, struct rtc_time *time) +{ + struct jz4740_rtc *rtc = dev_get_drvdata(dev); + int ret; + + ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, rtc_tm_to_time64(time)); + if (ret) + return ret; + + return jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SCRATCHPAD, 0x12345678); +} + +static int jz4740_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct jz4740_rtc *rtc = dev_get_drvdata(dev); + uint32_t secs; + uint32_t ctrl; + + secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC_ALARM); + + ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); + + alrm->enabled = !!(ctrl & JZ_RTC_CTRL_AE); + alrm->pending = !!(ctrl & JZ_RTC_CTRL_AF); + + rtc_time64_to_tm(secs, &alrm->time); + + return 0; +} + +static int jz4740_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + int ret; + struct jz4740_rtc *rtc = dev_get_drvdata(dev); + uint32_t secs = lower_32_bits(rtc_tm_to_time64(&alrm->time)); + + ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC_ALARM, secs); + if (!ret) + ret = jz4740_rtc_ctrl_set_bits(rtc, + JZ_RTC_CTRL_AE | JZ_RTC_CTRL_AF_IRQ, alrm->enabled); + + return ret; +} + +static int jz4740_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) +{ + struct jz4740_rtc *rtc = dev_get_drvdata(dev); + return jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_AF_IRQ, enable); +} + +static const struct rtc_class_ops jz4740_rtc_ops = { + .read_time = jz4740_rtc_read_time, + .set_time = jz4740_rtc_set_time, + .read_alarm = jz4740_rtc_read_alarm, + .set_alarm = jz4740_rtc_set_alarm, + .alarm_irq_enable = jz4740_rtc_alarm_irq_enable, +}; + +static irqreturn_t jz4740_rtc_irq(int irq, void *data) +{ + struct jz4740_rtc *rtc = data; + uint32_t ctrl; + unsigned long events = 0; + + ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); + + if (ctrl & JZ_RTC_CTRL_1HZ) + events |= (RTC_UF | RTC_IRQF); + + if (ctrl & JZ_RTC_CTRL_AF) + events |= (RTC_AF | RTC_IRQF); + + rtc_update_irq(rtc->rtc, 1, events); + + jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF, false); + + return IRQ_HANDLED; +} + +static void jz4740_rtc_poweroff(struct device *dev) +{ + struct jz4740_rtc *rtc = dev_get_drvdata(dev); + jz4740_rtc_reg_write(rtc, JZ_REG_RTC_HIBERNATE, 1); +} + +static void jz4740_rtc_power_off(void) +{ + jz4740_rtc_poweroff(dev_for_power_off); + kernel_halt(); +} + +static const struct of_device_id jz4740_rtc_of_match[] = { + { .compatible = "ingenic,jz4740-rtc", .data = (void *)ID_JZ4740 }, + { .compatible = "ingenic,jz4760-rtc", .data = (void *)ID_JZ4760 }, + { .compatible = "ingenic,jz4770-rtc", .data = (void *)ID_JZ4780 }, + { .compatible = "ingenic,jz4780-rtc", .data = (void *)ID_JZ4780 }, + {}, +}; +MODULE_DEVICE_TABLE(of, jz4740_rtc_of_match); + +static void jz4740_rtc_set_wakeup_params(struct jz4740_rtc *rtc, + struct device_node *np, + unsigned long rate) +{ + unsigned long wakeup_ticks, reset_ticks; + unsigned int min_wakeup_pin_assert_time = 60; /* Default: 60ms */ + unsigned int reset_pin_assert_time = 100; /* Default: 100ms */ + + of_property_read_u32(np, "ingenic,reset-pin-assert-time-ms", + &reset_pin_assert_time); + of_property_read_u32(np, "ingenic,min-wakeup-pin-assert-time-ms", + &min_wakeup_pin_assert_time); + + /* + * Set minimum wakeup pin assertion time: 100 ms. + * Range is 0 to 2 sec if RTC is clocked at 32 kHz. + */ + wakeup_ticks = (min_wakeup_pin_assert_time * rate) / 1000; + if (wakeup_ticks < JZ_RTC_WAKEUP_FILTER_MASK) + wakeup_ticks &= JZ_RTC_WAKEUP_FILTER_MASK; + else + wakeup_ticks = JZ_RTC_WAKEUP_FILTER_MASK; + jz4740_rtc_reg_write(rtc, JZ_REG_RTC_WAKEUP_FILTER, wakeup_ticks); + + /* + * Set reset pin low-level assertion time after wakeup: 60 ms. + * Range is 0 to 125 ms if RTC is clocked at 32 kHz. + */ + reset_ticks = (reset_pin_assert_time * rate) / 1000; + if (reset_ticks < JZ_RTC_RESET_COUNTER_MASK) + reset_ticks &= JZ_RTC_RESET_COUNTER_MASK; + else + reset_ticks = JZ_RTC_RESET_COUNTER_MASK; + jz4740_rtc_reg_write(rtc, JZ_REG_RTC_RESET_COUNTER, reset_ticks); +} + +static int jz4740_rtc_clk32k_enable(struct clk_hw *hw) +{ + struct jz4740_rtc *rtc = container_of(hw, struct jz4740_rtc, clk32k); + + return jz4740_rtc_reg_write(rtc, JZ_REG_RTC_CKPCR, + JZ_RTC_CKPCR_CK32PULL_DIS | + JZ_RTC_CKPCR_CK32CTL_EN); +} + +static void jz4740_rtc_clk32k_disable(struct clk_hw *hw) +{ + struct jz4740_rtc *rtc = container_of(hw, struct jz4740_rtc, clk32k); + + jz4740_rtc_reg_write(rtc, JZ_REG_RTC_CKPCR, 0); +} + +static int jz4740_rtc_clk32k_is_enabled(struct clk_hw *hw) +{ + struct jz4740_rtc *rtc = container_of(hw, struct jz4740_rtc, clk32k); + u32 ckpcr; + + ckpcr = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CKPCR); + + return !!(ckpcr & JZ_RTC_CKPCR_CK32CTL_EN); +} + +static const struct clk_ops jz4740_rtc_clk32k_ops = { + .enable = jz4740_rtc_clk32k_enable, + .disable = jz4740_rtc_clk32k_disable, + .is_enabled = jz4740_rtc_clk32k_is_enabled, +}; + +static int jz4740_rtc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct jz4740_rtc *rtc; + unsigned long rate; + struct clk *clk; + int ret, irq; + + rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->type = (uintptr_t)device_get_match_data(dev); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + rtc->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rtc->base)) + return PTR_ERR(rtc->base); + + clk = devm_clk_get_enabled(dev, "rtc"); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "Failed to get RTC clock\n"); + + spin_lock_init(&rtc->lock); + + platform_set_drvdata(pdev, rtc); + + device_init_wakeup(dev, true); + + ret = dev_pm_set_wake_irq(dev, irq); + if (ret) + return dev_err_probe(dev, ret, "Failed to set wake irq\n"); + + rtc->rtc = devm_rtc_allocate_device(dev); + if (IS_ERR(rtc->rtc)) + return dev_err_probe(dev, PTR_ERR(rtc->rtc), + "Failed to allocate rtc device\n"); + + rtc->rtc->ops = &jz4740_rtc_ops; + rtc->rtc->range_max = U32_MAX; + + rate = clk_get_rate(clk); + jz4740_rtc_set_wakeup_params(rtc, np, rate); + + /* Each 1 Hz pulse should happen after (rate) ticks */ + jz4740_rtc_reg_write(rtc, JZ_REG_RTC_REGULATOR, rate - 1); + + ret = devm_rtc_register_device(rtc->rtc); + if (ret) + return ret; + + ret = devm_request_irq(dev, irq, jz4740_rtc_irq, 0, + pdev->name, rtc); + if (ret) + return dev_err_probe(dev, ret, "Failed to request rtc irq\n"); + + if (of_device_is_system_power_controller(np)) { + dev_for_power_off = dev; + + if (!pm_power_off) + pm_power_off = jz4740_rtc_power_off; + else + dev_warn(dev, "Poweroff handler already present!\n"); + } + + if (device_property_present(dev, "#clock-cells")) { + rtc->clk32k.init = CLK_HW_INIT_HW("clk32k", __clk_get_hw(clk), + &jz4740_rtc_clk32k_ops, 0); + + ret = devm_clk_hw_register(dev, &rtc->clk32k); + if (ret) + return dev_err_probe(dev, ret, + "Unable to register clk32k clock\n"); + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + &rtc->clk32k); + if (ret) + return dev_err_probe(dev, ret, + "Unable to register clk32k clock provider\n"); + } + + return 0; +} + +static struct platform_driver jz4740_rtc_driver = { + .probe = jz4740_rtc_probe, + .driver = { + .name = "jz4740-rtc", + .of_match_table = jz4740_rtc_of_match, + }, +}; + +module_platform_driver(jz4740_rtc_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("RTC driver for the JZ4740 SoC\n"); diff --git a/drivers/rtc/rtc-loongson.c b/drivers/rtc/rtc-loongson.c new file mode 100644 index 000000000..066f0644d --- /dev/null +++ b/drivers/rtc/rtc-loongson.c @@ -0,0 +1,431 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Loongson RTC driver + * + * Maintained out-of-tree by Huacai Chen . + * Rewritten for mainline by WANG Xuerui . + * Binbin Zhou + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Time Of Year(TOY) counters registers */ +#define TOY_TRIM_REG 0x20 /* Must be initialized to 0 */ +#define TOY_WRITE0_REG 0x24 /* TOY low 32-bits value (write-only) */ +#define TOY_WRITE1_REG 0x28 /* TOY high 32-bits value (write-only) */ +#define TOY_READ0_REG 0x2c /* TOY low 32-bits value (read-only) */ +#define TOY_READ1_REG 0x30 /* TOY high 32-bits value (read-only) */ +#define TOY_MATCH0_REG 0x34 /* TOY timing interrupt 0 */ +#define TOY_MATCH1_REG 0x38 /* TOY timing interrupt 1 */ +#define TOY_MATCH2_REG 0x3c /* TOY timing interrupt 2 */ + +/* RTC counters registers */ +#define RTC_CTRL_REG 0x40 /* TOY and RTC control register */ +#define RTC_TRIM_REG 0x60 /* Must be initialized to 0 */ +#define RTC_WRITE0_REG 0x64 /* RTC counters value (write-only) */ +#define RTC_READ0_REG 0x68 /* RTC counters value (read-only) */ +#define RTC_MATCH0_REG 0x6c /* RTC timing interrupt 0 */ +#define RTC_MATCH1_REG 0x70 /* RTC timing interrupt 1 */ +#define RTC_MATCH2_REG 0x74 /* RTC timing interrupt 2 */ + +/* bitmask of TOY_WRITE0_REG */ +#define TOY_MON GENMASK(31, 26) +#define TOY_DAY GENMASK(25, 21) +#define TOY_HOUR GENMASK(20, 16) +#define TOY_MIN GENMASK(15, 10) +#define TOY_SEC GENMASK(9, 4) +#define TOY_MSEC GENMASK(3, 0) + +/* bitmask of TOY_MATCH0/1/2_REG */ +#define TOY_MATCH_YEAR GENMASK(31, 26) +#define TOY_MATCH_MON GENMASK(25, 22) +#define TOY_MATCH_DAY GENMASK(21, 17) +#define TOY_MATCH_HOUR GENMASK(16, 12) +#define TOY_MATCH_MIN GENMASK(11, 6) +#define TOY_MATCH_SEC GENMASK(5, 0) + +/* bitmask of RTC_CTRL_REG */ +#define RTC_ENABLE BIT(13) /* 1: RTC counters enable */ +#define TOY_ENABLE BIT(11) /* 1: TOY counters enable */ +#define OSC_ENABLE BIT(8) /* 1: 32.768k crystal enable */ +#define TOY_ENABLE_MASK (TOY_ENABLE | OSC_ENABLE) + +/* PM domain registers */ +#define PM1_STS_REG 0x0c /* Power management 1 status register */ +#define RTC_STS BIT(10) /* RTC status */ +#define PM1_EN_REG 0x10 /* Power management 1 enable register */ +#define RTC_EN BIT(10) /* RTC event enable */ + +/* + * According to the LS1C manual, RTC_CTRL and alarm-related registers are not defined. + * Accessing the relevant registers will cause the system to hang. + */ +#define LOONGSON_RTC_CTRL_WORKAROUND BIT(0) +#define LOONGSON_RTC_ALARM_WORKAROUND BIT(1) + +struct loongson_rtc_config { + u32 pm_offset; /* Offset of PM domain, for RTC alarm wakeup */ + u32 flags; /* Workaround bits */ +}; + +struct loongson_rtc_priv { + spinlock_t lock; /* protects PM registers access */ + u32 fix_year; /* RTC alarm year compensation value */ + struct rtc_device *rtcdev; + struct regmap *regmap; + void __iomem *pm_base; /* PM domain base, for RTC alarm wakeup */ + const struct loongson_rtc_config *config; +}; + +static const struct loongson_rtc_config ls1b_rtc_config = { + .pm_offset = 0, + .flags = 0, +}; + +static const struct loongson_rtc_config ls1c_rtc_config = { + .pm_offset = 0, + .flags = LOONGSON_RTC_CTRL_WORKAROUND | LOONGSON_RTC_ALARM_WORKAROUND, +}; + +static const struct loongson_rtc_config generic_rtc_config = { + .pm_offset = 0x100, + .flags = 0, +}; + +static const struct loongson_rtc_config ls2k0300_rtc_config = { + .pm_offset = 0x0, + .flags = LOONGSON_RTC_ALARM_WORKAROUND, +}; + +static const struct loongson_rtc_config ls2k1000_rtc_config = { + .pm_offset = 0x800, + .flags = 0, +}; + +static const struct regmap_config loongson_rtc_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +/* RTC alarm irq handler */ +static irqreturn_t loongson_rtc_isr(int irq, void *id) +{ + struct loongson_rtc_priv *priv = (struct loongson_rtc_priv *)id; + + rtc_update_irq(priv->rtcdev, 1, RTC_AF | RTC_IRQF); + + /* + * The TOY_MATCH0_REG should be cleared 0 here, + * otherwise the interrupt cannot be cleared. + */ + regmap_write(priv->regmap, TOY_MATCH0_REG, 0); + + return IRQ_HANDLED; +} + +/* For ACPI fixed event handler */ +static u32 loongson_rtc_handler(void *id) +{ + struct loongson_rtc_priv *priv = (struct loongson_rtc_priv *)id; + + rtc_update_irq(priv->rtcdev, 1, RTC_AF | RTC_IRQF); + + /* + * The TOY_MATCH0_REG should be cleared 0 here, + * otherwise the interrupt cannot be cleared. + */ + regmap_write(priv->regmap, TOY_MATCH0_REG, 0); + + spin_lock(&priv->lock); + /* Disable RTC alarm wakeup and interrupt */ + writel(readl(priv->pm_base + PM1_EN_REG) & ~RTC_EN, + priv->pm_base + PM1_EN_REG); + + /* Clear RTC interrupt status */ + writel(RTC_STS, priv->pm_base + PM1_STS_REG); + spin_unlock(&priv->lock); + + return ACPI_INTERRUPT_HANDLED; +} + +static int loongson_rtc_set_enabled(struct device *dev) +{ + struct loongson_rtc_priv *priv = dev_get_drvdata(dev); + + if (priv->config->flags & LOONGSON_RTC_CTRL_WORKAROUND) + return 0; + + /* Enable RTC TOY counters and crystal */ + return regmap_update_bits(priv->regmap, RTC_CTRL_REG, TOY_ENABLE_MASK, + TOY_ENABLE_MASK); +} + +static bool loongson_rtc_get_enabled(struct device *dev) +{ + int ret; + u32 ctrl_data; + struct loongson_rtc_priv *priv = dev_get_drvdata(dev); + + if (priv->config->flags & LOONGSON_RTC_CTRL_WORKAROUND) + return true; + + ret = regmap_read(priv->regmap, RTC_CTRL_REG, &ctrl_data); + if (ret < 0) + return false; + + return ctrl_data & TOY_ENABLE_MASK; +} + +static int loongson_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + int ret; + u32 rtc_data[2]; + struct loongson_rtc_priv *priv = dev_get_drvdata(dev); + + if (!loongson_rtc_get_enabled(dev)) + return -EINVAL; + + ret = regmap_bulk_read(priv->regmap, TOY_READ0_REG, rtc_data, + ARRAY_SIZE(rtc_data)); + if (ret < 0) + return ret; + + tm->tm_sec = FIELD_GET(TOY_SEC, rtc_data[0]); + tm->tm_min = FIELD_GET(TOY_MIN, rtc_data[0]); + tm->tm_hour = FIELD_GET(TOY_HOUR, rtc_data[0]); + tm->tm_mday = FIELD_GET(TOY_DAY, rtc_data[0]); + tm->tm_mon = FIELD_GET(TOY_MON, rtc_data[0]) - 1; + tm->tm_year = rtc_data[1]; + + /* Prepare for RTC alarm year compensation value. */ + priv->fix_year = tm->tm_year / 64 * 64; + return 0; +} + +static int loongson_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + int ret; + u32 rtc_data[2]; + struct loongson_rtc_priv *priv = dev_get_drvdata(dev); + + rtc_data[0] = FIELD_PREP(TOY_SEC, tm->tm_sec) + | FIELD_PREP(TOY_MIN, tm->tm_min) + | FIELD_PREP(TOY_HOUR, tm->tm_hour) + | FIELD_PREP(TOY_DAY, tm->tm_mday) + | FIELD_PREP(TOY_MON, tm->tm_mon + 1); + rtc_data[1] = tm->tm_year; + + ret = regmap_bulk_write(priv->regmap, TOY_WRITE0_REG, rtc_data, + ARRAY_SIZE(rtc_data)); + if (ret < 0) + return ret; + + return loongson_rtc_set_enabled(dev); +} + +static int loongson_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + int ret; + u32 alarm_data; + struct loongson_rtc_priv *priv = dev_get_drvdata(dev); + + ret = regmap_read(priv->regmap, TOY_MATCH0_REG, &alarm_data); + if (ret < 0) + return ret; + + alrm->time.tm_sec = FIELD_GET(TOY_MATCH_SEC, alarm_data); + alrm->time.tm_min = FIELD_GET(TOY_MATCH_MIN, alarm_data); + alrm->time.tm_hour = FIELD_GET(TOY_MATCH_HOUR, alarm_data); + alrm->time.tm_mday = FIELD_GET(TOY_MATCH_DAY, alarm_data); + alrm->time.tm_mon = FIELD_GET(TOY_MATCH_MON, alarm_data) - 1; + /* + * This is a hardware bug: the year field of SYS_TOYMATCH is only 6 bits, + * making it impossible to save year values larger than 64. + * + * SYS_TOYMATCH is used to match the alarm time value and determine if + * an alarm is triggered, so we must keep the lower 6 bits of the year + * value constant during the value conversion. + * + * In summary, we need to manually add 64(or a multiple of 64) to the + * year value to avoid the invalid alarm prompt at startup. + */ + alrm->time.tm_year = FIELD_GET(TOY_MATCH_YEAR, alarm_data) + priv->fix_year; + + alrm->enabled = !!(readl(priv->pm_base + PM1_EN_REG) & RTC_EN); + return 0; +} + +static int loongson_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + u32 val; + struct loongson_rtc_priv *priv = dev_get_drvdata(dev); + + spin_lock(&priv->lock); + val = readl(priv->pm_base + PM1_EN_REG); + /* Enable RTC alarm wakeup */ + writel(enabled ? val | RTC_EN : val & ~RTC_EN, + priv->pm_base + PM1_EN_REG); + spin_unlock(&priv->lock); + + return 0; +} + +static int loongson_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + int ret; + u32 alarm_data; + struct loongson_rtc_priv *priv = dev_get_drvdata(dev); + + alarm_data = FIELD_PREP(TOY_MATCH_SEC, alrm->time.tm_sec) + | FIELD_PREP(TOY_MATCH_MIN, alrm->time.tm_min) + | FIELD_PREP(TOY_MATCH_HOUR, alrm->time.tm_hour) + | FIELD_PREP(TOY_MATCH_DAY, alrm->time.tm_mday) + | FIELD_PREP(TOY_MATCH_MON, alrm->time.tm_mon + 1) + | FIELD_PREP(TOY_MATCH_YEAR, alrm->time.tm_year - priv->fix_year); + + ret = regmap_write(priv->regmap, TOY_MATCH0_REG, alarm_data); + if (ret < 0) + return ret; + + return loongson_rtc_alarm_irq_enable(dev, alrm->enabled); +} + +static const struct rtc_class_ops loongson_rtc_ops = { + .read_time = loongson_rtc_read_time, + .set_time = loongson_rtc_set_time, + .read_alarm = loongson_rtc_read_alarm, + .set_alarm = loongson_rtc_set_alarm, + .alarm_irq_enable = loongson_rtc_alarm_irq_enable, +}; + +static int loongson_rtc_alarm_setting(struct platform_device *pdev, void __iomem *regs) +{ + int ret = 0, alarm_irq; + struct device *dev = &pdev->dev; + struct loongson_rtc_priv *priv = dev_get_drvdata(dev); + + if (priv->config->flags & LOONGSON_RTC_ALARM_WORKAROUND) { + /* Loongson-1C/Loongson-2K0300 RTC does not support alarm */ + clear_bit(RTC_FEATURE_ALARM, priv->rtcdev->features); + return 0; + } + + /* Get RTC alarm irq */ + alarm_irq = platform_get_irq(pdev, 0); + if (alarm_irq < 0) + return alarm_irq; + + ret = devm_request_irq(dev, alarm_irq, loongson_rtc_isr, 0, "loongson-alarm", + priv); + if (ret < 0) + return ret; + + priv->pm_base = regs - priv->config->pm_offset; + device_init_wakeup(dev, true); + + if (has_acpi_companion(dev)) + acpi_install_fixed_event_handler(ACPI_EVENT_RTC, + loongson_rtc_handler, priv); + + return ret; +} + +static int loongson_rtc_probe(struct platform_device *pdev) +{ + int ret; + void __iomem *regs; + struct loongson_rtc_priv *priv; + struct device *dev = &pdev->dev; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return dev_err_probe(dev, PTR_ERR(regs), + "devm_platform_ioremap_resource failed\n"); + + priv->regmap = devm_regmap_init_mmio(dev, regs, + &loongson_rtc_regmap_config); + if (IS_ERR(priv->regmap)) + return dev_err_probe(dev, PTR_ERR(priv->regmap), + "devm_regmap_init_mmio failed\n"); + + priv->config = device_get_match_data(dev); + spin_lock_init(&priv->lock); + platform_set_drvdata(pdev, priv); + + priv->rtcdev = devm_rtc_allocate_device(dev); + if (IS_ERR(priv->rtcdev)) + return dev_err_probe(dev, PTR_ERR(priv->rtcdev), + "devm_rtc_allocate_device failed\n"); + + ret = loongson_rtc_alarm_setting(pdev, regs); + if (ret) + return ret; + + /* Loongson RTC does not support UIE */ + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, priv->rtcdev->features); + priv->rtcdev->ops = &loongson_rtc_ops; + priv->rtcdev->range_min = RTC_TIMESTAMP_BEGIN_2000; + priv->rtcdev->range_max = RTC_TIMESTAMP_END_2099; + + return devm_rtc_register_device(priv->rtcdev); +} + +static void loongson_rtc_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct loongson_rtc_priv *priv = dev_get_drvdata(dev); + + if (!test_bit(RTC_FEATURE_ALARM, priv->rtcdev->features)) + return; + + if (has_acpi_companion(dev)) + acpi_remove_fixed_event_handler(ACPI_EVENT_RTC, + loongson_rtc_handler); + + device_init_wakeup(dev, false); + loongson_rtc_alarm_irq_enable(dev, 0); +} + +static const struct of_device_id loongson_rtc_of_match[] = { + { .compatible = "loongson,ls1b-rtc", .data = &ls1b_rtc_config }, + { .compatible = "loongson,ls1c-rtc", .data = &ls1c_rtc_config }, + { .compatible = "loongson,ls7a-rtc", .data = &generic_rtc_config }, + { .compatible = "loongson,ls2k0300-rtc", .data = &ls2k0300_rtc_config }, + { .compatible = "loongson,ls2k1000-rtc", .data = &ls2k1000_rtc_config }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, loongson_rtc_of_match); + +static const struct acpi_device_id loongson_rtc_acpi_match[] = { + { "LOON0001", .driver_data = (kernel_ulong_t)&generic_rtc_config }, + { } +}; +MODULE_DEVICE_TABLE(acpi, loongson_rtc_acpi_match); + +static struct platform_driver loongson_rtc_driver = { + .probe = loongson_rtc_probe, + .remove = loongson_rtc_remove, + .driver = { + .name = "loongson-rtc", + .of_match_table = loongson_rtc_of_match, + .acpi_match_table = loongson_rtc_acpi_match, + }, +}; +module_platform_driver(loongson_rtc_driver); + +MODULE_DESCRIPTION("Loongson RTC driver"); +MODULE_AUTHOR("Binbin Zhou "); +MODULE_AUTHOR("WANG Xuerui "); +MODULE_AUTHOR("Huacai Chen "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-lp8788.c b/drivers/rtc/rtc-lp8788.c new file mode 100644 index 000000000..0793d7050 --- /dev/null +++ b/drivers/rtc/rtc-lp8788.c @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TI LP8788 MFD - rtc driver + * + * Copyright 2012 Texas Instruments + * + * Author: Milo(Woogyom) Kim + */ + +#include +#include +#include +#include +#include +#include +#include + +/* register address */ +#define LP8788_INTEN_3 0x05 +#define LP8788_RTC_UNLOCK 0x64 +#define LP8788_RTC_SEC 0x70 +#define LP8788_ALM1_SEC 0x77 +#define LP8788_ALM1_EN 0x7D +#define LP8788_ALM2_SEC 0x7E +#define LP8788_ALM2_EN 0x84 + +/* mask/shift bits */ +#define LP8788_INT_RTC_ALM1_M BIT(1) /* Addr 05h */ +#define LP8788_INT_RTC_ALM1_S 1 +#define LP8788_INT_RTC_ALM2_M BIT(2) /* Addr 05h */ +#define LP8788_INT_RTC_ALM2_S 2 +#define LP8788_ALM_EN_M BIT(7) /* Addr 7Dh or 84h */ +#define LP8788_ALM_EN_S 7 + +#define DEFAULT_ALARM_SEL LP8788_ALARM_1 +#define LP8788_MONTH_OFFSET 1 +#define LP8788_BASE_YEAR 2000 +#define MAX_WDAY_BITS 7 +#define LP8788_WDAY_SET 1 +#define RTC_UNLOCK 0x1 +#define RTC_LATCH 0x2 +#define ALARM_IRQ_FLAG (RTC_IRQF | RTC_AF) + +enum lp8788_time { + LPTIME_SEC, + LPTIME_MIN, + LPTIME_HOUR, + LPTIME_MDAY, + LPTIME_MON, + LPTIME_YEAR, + LPTIME_WDAY, + LPTIME_MAX, +}; + +struct lp8788_rtc { + struct lp8788 *lp; + struct rtc_device *rdev; + enum lp8788_alarm_sel alarm; + int irq; +}; + +static const u8 addr_alarm_sec[LP8788_ALARM_MAX] = { + LP8788_ALM1_SEC, + LP8788_ALM2_SEC, +}; + +static const u8 addr_alarm_en[LP8788_ALARM_MAX] = { + LP8788_ALM1_EN, + LP8788_ALM2_EN, +}; + +static const u8 mask_alarm_en[LP8788_ALARM_MAX] = { + LP8788_INT_RTC_ALM1_M, + LP8788_INT_RTC_ALM2_M, +}; + +static const u8 shift_alarm_en[LP8788_ALARM_MAX] = { + LP8788_INT_RTC_ALM1_S, + LP8788_INT_RTC_ALM2_S, +}; + +static int _to_tm_wday(u8 lp8788_wday) +{ + int i; + + if (lp8788_wday == 0) + return 0; + + /* lookup defined weekday from read register value */ + for (i = 0; i < MAX_WDAY_BITS; i++) { + if ((lp8788_wday >> i) == LP8788_WDAY_SET) + break; + } + + return i + 1; +} + +static inline int _to_lp8788_wday(int tm_wday) +{ + return LP8788_WDAY_SET << (tm_wday - 1); +} + +static void lp8788_rtc_unlock(struct lp8788 *lp) +{ + lp8788_write_byte(lp, LP8788_RTC_UNLOCK, RTC_UNLOCK); + lp8788_write_byte(lp, LP8788_RTC_UNLOCK, RTC_LATCH); +} + +static int lp8788_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct lp8788_rtc *rtc = dev_get_drvdata(dev); + struct lp8788 *lp = rtc->lp; + u8 data[LPTIME_MAX]; + int ret; + + lp8788_rtc_unlock(lp); + + ret = lp8788_read_multi_bytes(lp, LP8788_RTC_SEC, data, LPTIME_MAX); + if (ret) + return ret; + + tm->tm_sec = data[LPTIME_SEC]; + tm->tm_min = data[LPTIME_MIN]; + tm->tm_hour = data[LPTIME_HOUR]; + tm->tm_mday = data[LPTIME_MDAY]; + tm->tm_mon = data[LPTIME_MON] - LP8788_MONTH_OFFSET; + tm->tm_year = data[LPTIME_YEAR] + LP8788_BASE_YEAR - 1900; + tm->tm_wday = _to_tm_wday(data[LPTIME_WDAY]); + + return 0; +} + +static int lp8788_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct lp8788_rtc *rtc = dev_get_drvdata(dev); + struct lp8788 *lp = rtc->lp; + u8 data[LPTIME_MAX - 1]; + int ret, i, year; + + year = tm->tm_year + 1900 - LP8788_BASE_YEAR; + if (year < 0) { + dev_err(lp->dev, "invalid year: %d\n", year); + return -EINVAL; + } + + /* because rtc weekday is a readonly register, do not update */ + data[LPTIME_SEC] = tm->tm_sec; + data[LPTIME_MIN] = tm->tm_min; + data[LPTIME_HOUR] = tm->tm_hour; + data[LPTIME_MDAY] = tm->tm_mday; + data[LPTIME_MON] = tm->tm_mon + LP8788_MONTH_OFFSET; + data[LPTIME_YEAR] = year; + + for (i = 0; i < ARRAY_SIZE(data); i++) { + ret = lp8788_write_byte(lp, LP8788_RTC_SEC + i, data[i]); + if (ret) + return ret; + } + + return 0; +} + +static int lp8788_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct lp8788_rtc *rtc = dev_get_drvdata(dev); + struct lp8788 *lp = rtc->lp; + struct rtc_time *tm = &alarm->time; + u8 addr, data[LPTIME_MAX]; + int ret; + + addr = addr_alarm_sec[rtc->alarm]; + ret = lp8788_read_multi_bytes(lp, addr, data, LPTIME_MAX); + if (ret) + return ret; + + tm->tm_sec = data[LPTIME_SEC]; + tm->tm_min = data[LPTIME_MIN]; + tm->tm_hour = data[LPTIME_HOUR]; + tm->tm_mday = data[LPTIME_MDAY]; + tm->tm_mon = data[LPTIME_MON] - LP8788_MONTH_OFFSET; + tm->tm_year = data[LPTIME_YEAR] + LP8788_BASE_YEAR - 1900; + tm->tm_wday = _to_tm_wday(data[LPTIME_WDAY]); + alarm->enabled = data[LPTIME_WDAY] & LP8788_ALM_EN_M; + + return 0; +} + +static int lp8788_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct lp8788_rtc *rtc = dev_get_drvdata(dev); + struct lp8788 *lp = rtc->lp; + struct rtc_time *tm = &alarm->time; + u8 addr, data[LPTIME_MAX]; + int ret, i, year; + + year = tm->tm_year + 1900 - LP8788_BASE_YEAR; + if (year < 0) { + dev_err(lp->dev, "invalid year: %d\n", year); + return -EINVAL; + } + + data[LPTIME_SEC] = tm->tm_sec; + data[LPTIME_MIN] = tm->tm_min; + data[LPTIME_HOUR] = tm->tm_hour; + data[LPTIME_MDAY] = tm->tm_mday; + data[LPTIME_MON] = tm->tm_mon + LP8788_MONTH_OFFSET; + data[LPTIME_YEAR] = year; + data[LPTIME_WDAY] = _to_lp8788_wday(tm->tm_wday); + + for (i = 0; i < ARRAY_SIZE(data); i++) { + addr = addr_alarm_sec[rtc->alarm] + i; + ret = lp8788_write_byte(lp, addr, data[i]); + if (ret) + return ret; + } + + alarm->enabled = 1; + addr = addr_alarm_en[rtc->alarm]; + + return lp8788_update_bits(lp, addr, LP8788_ALM_EN_M, + alarm->enabled << LP8788_ALM_EN_S); +} + +static int lp8788_alarm_irq_enable(struct device *dev, unsigned int enable) +{ + struct lp8788_rtc *rtc = dev_get_drvdata(dev); + struct lp8788 *lp = rtc->lp; + u8 mask, shift; + + if (!rtc->irq) + return -EIO; + + mask = mask_alarm_en[rtc->alarm]; + shift = shift_alarm_en[rtc->alarm]; + + return lp8788_update_bits(lp, LP8788_INTEN_3, mask, enable << shift); +} + +static const struct rtc_class_ops lp8788_rtc_ops = { + .read_time = lp8788_rtc_read_time, + .set_time = lp8788_rtc_set_time, + .read_alarm = lp8788_read_alarm, + .set_alarm = lp8788_set_alarm, + .alarm_irq_enable = lp8788_alarm_irq_enable, +}; + +static irqreturn_t lp8788_alarm_irq_handler(int irq, void *ptr) +{ + struct lp8788_rtc *rtc = ptr; + + rtc_update_irq(rtc->rdev, 1, ALARM_IRQ_FLAG); + return IRQ_HANDLED; +} + +static int lp8788_alarm_irq_register(struct platform_device *pdev, + struct lp8788_rtc *rtc) +{ + struct resource *r; + struct lp8788 *lp = rtc->lp; + struct irq_domain *irqdm = lp->irqdm; + int irq; + + rtc->irq = 0; + + /* even the alarm IRQ number is not specified, rtc time should work */ + r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, LP8788_ALM_IRQ); + if (!r) + return 0; + + if (rtc->alarm == LP8788_ALARM_1) + irq = r->start; + else + irq = r->end; + + rtc->irq = irq_create_mapping(irqdm, irq); + + return devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL, + lp8788_alarm_irq_handler, + 0, LP8788_ALM_IRQ, rtc); +} + +static int lp8788_rtc_probe(struct platform_device *pdev) +{ + struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent); + struct lp8788_rtc *rtc; + struct device *dev = &pdev->dev; + + rtc = devm_kzalloc(dev, sizeof(struct lp8788_rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->lp = lp; + rtc->alarm = lp->pdata ? lp->pdata->alarm_sel : DEFAULT_ALARM_SEL; + platform_set_drvdata(pdev, rtc); + + device_init_wakeup(dev, true); + + rtc->rdev = devm_rtc_device_register(dev, "lp8788_rtc", + &lp8788_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc->rdev)) { + dev_err(dev, "can not register rtc device\n"); + return PTR_ERR(rtc->rdev); + } + + if (lp8788_alarm_irq_register(pdev, rtc)) + dev_warn(lp->dev, "no rtc irq handler\n"); + + return 0; +} + +static struct platform_driver lp8788_rtc_driver = { + .probe = lp8788_rtc_probe, + .driver = { + .name = LP8788_DEV_RTC, + }, +}; +module_platform_driver(lp8788_rtc_driver); + +MODULE_DESCRIPTION("Texas Instruments LP8788 RTC Driver"); +MODULE_AUTHOR("Milo Kim"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:lp8788-rtc"); diff --git a/drivers/rtc/rtc-lpc24xx.c b/drivers/rtc/rtc-lpc24xx.c new file mode 100644 index 000000000..a1266986d --- /dev/null +++ b/drivers/rtc/rtc-lpc24xx.c @@ -0,0 +1,297 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * RTC driver for NXP LPC178x/18xx/43xx Real-Time Clock (RTC) + * + * Copyright (C) 2011 NXP Semiconductors + * Copyright (C) 2015 Joachim Eastwood + */ + +#include +#include +#include +#include +#include +#include + +/* LPC24xx RTC register offsets and bits */ +#define LPC24XX_ILR 0x00 +#define LPC24XX_RTCCIF BIT(0) +#define LPC24XX_RTCALF BIT(1) +#define LPC24XX_CTC 0x04 +#define LPC24XX_CCR 0x08 +#define LPC24XX_CLKEN BIT(0) +#define LPC178X_CCALEN BIT(4) +#define LPC24XX_CIIR 0x0c +#define LPC24XX_AMR 0x10 +#define LPC24XX_ALARM_DISABLE 0xff +#define LPC24XX_CTIME0 0x14 +#define LPC24XX_CTIME1 0x18 +#define LPC24XX_CTIME2 0x1c +#define LPC24XX_SEC 0x20 +#define LPC24XX_MIN 0x24 +#define LPC24XX_HOUR 0x28 +#define LPC24XX_DOM 0x2c +#define LPC24XX_DOW 0x30 +#define LPC24XX_DOY 0x34 +#define LPC24XX_MONTH 0x38 +#define LPC24XX_YEAR 0x3c +#define LPC24XX_ALSEC 0x60 +#define LPC24XX_ALMIN 0x64 +#define LPC24XX_ALHOUR 0x68 +#define LPC24XX_ALDOM 0x6c +#define LPC24XX_ALDOW 0x70 +#define LPC24XX_ALDOY 0x74 +#define LPC24XX_ALMON 0x78 +#define LPC24XX_ALYEAR 0x7c + +/* Macros to read fields in consolidated time (CT) registers */ +#define CT0_SECS(x) (((x) >> 0) & 0x3f) +#define CT0_MINS(x) (((x) >> 8) & 0x3f) +#define CT0_HOURS(x) (((x) >> 16) & 0x1f) +#define CT0_DOW(x) (((x) >> 24) & 0x07) +#define CT1_DOM(x) (((x) >> 0) & 0x1f) +#define CT1_MONTH(x) (((x) >> 8) & 0x0f) +#define CT1_YEAR(x) (((x) >> 16) & 0xfff) +#define CT2_DOY(x) (((x) >> 0) & 0xfff) + +#define rtc_readl(dev, reg) readl((dev)->rtc_base + (reg)) +#define rtc_writel(dev, reg, val) writel((val), (dev)->rtc_base + (reg)) + +struct lpc24xx_rtc { + void __iomem *rtc_base; + struct rtc_device *rtc; + struct clk *clk_rtc; + struct clk *clk_reg; +}; + +static int lpc24xx_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct lpc24xx_rtc *rtc = dev_get_drvdata(dev); + + /* Disable RTC during update */ + rtc_writel(rtc, LPC24XX_CCR, LPC178X_CCALEN); + + rtc_writel(rtc, LPC24XX_SEC, tm->tm_sec); + rtc_writel(rtc, LPC24XX_MIN, tm->tm_min); + rtc_writel(rtc, LPC24XX_HOUR, tm->tm_hour); + rtc_writel(rtc, LPC24XX_DOW, tm->tm_wday); + rtc_writel(rtc, LPC24XX_DOM, tm->tm_mday); + rtc_writel(rtc, LPC24XX_DOY, tm->tm_yday); + rtc_writel(rtc, LPC24XX_MONTH, tm->tm_mon); + rtc_writel(rtc, LPC24XX_YEAR, tm->tm_year); + + rtc_writel(rtc, LPC24XX_CCR, LPC24XX_CLKEN | LPC178X_CCALEN); + + return 0; +} + +static int lpc24xx_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct lpc24xx_rtc *rtc = dev_get_drvdata(dev); + u32 ct0, ct1, ct2; + + ct0 = rtc_readl(rtc, LPC24XX_CTIME0); + ct1 = rtc_readl(rtc, LPC24XX_CTIME1); + ct2 = rtc_readl(rtc, LPC24XX_CTIME2); + + tm->tm_sec = CT0_SECS(ct0); + tm->tm_min = CT0_MINS(ct0); + tm->tm_hour = CT0_HOURS(ct0); + tm->tm_wday = CT0_DOW(ct0); + tm->tm_mon = CT1_MONTH(ct1); + tm->tm_mday = CT1_DOM(ct1); + tm->tm_year = CT1_YEAR(ct1); + tm->tm_yday = CT2_DOY(ct2); + + return 0; +} + +static int lpc24xx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct lpc24xx_rtc *rtc = dev_get_drvdata(dev); + struct rtc_time *tm = &wkalrm->time; + + tm->tm_sec = rtc_readl(rtc, LPC24XX_ALSEC); + tm->tm_min = rtc_readl(rtc, LPC24XX_ALMIN); + tm->tm_hour = rtc_readl(rtc, LPC24XX_ALHOUR); + tm->tm_mday = rtc_readl(rtc, LPC24XX_ALDOM); + tm->tm_wday = rtc_readl(rtc, LPC24XX_ALDOW); + tm->tm_yday = rtc_readl(rtc, LPC24XX_ALDOY); + tm->tm_mon = rtc_readl(rtc, LPC24XX_ALMON); + tm->tm_year = rtc_readl(rtc, LPC24XX_ALYEAR); + + wkalrm->enabled = rtc_readl(rtc, LPC24XX_AMR) == 0; + wkalrm->pending = !!(rtc_readl(rtc, LPC24XX_ILR) & LPC24XX_RTCCIF); + + return rtc_valid_tm(&wkalrm->time); +} + +static int lpc24xx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct lpc24xx_rtc *rtc = dev_get_drvdata(dev); + struct rtc_time *tm = &wkalrm->time; + + /* Disable alarm irq during update */ + rtc_writel(rtc, LPC24XX_AMR, LPC24XX_ALARM_DISABLE); + + rtc_writel(rtc, LPC24XX_ALSEC, tm->tm_sec); + rtc_writel(rtc, LPC24XX_ALMIN, tm->tm_min); + rtc_writel(rtc, LPC24XX_ALHOUR, tm->tm_hour); + rtc_writel(rtc, LPC24XX_ALDOM, tm->tm_mday); + rtc_writel(rtc, LPC24XX_ALDOW, tm->tm_wday); + rtc_writel(rtc, LPC24XX_ALDOY, tm->tm_yday); + rtc_writel(rtc, LPC24XX_ALMON, tm->tm_mon); + rtc_writel(rtc, LPC24XX_ALYEAR, tm->tm_year); + + if (wkalrm->enabled) + rtc_writel(rtc, LPC24XX_AMR, 0); + + return 0; +} + +static int lpc24xx_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) +{ + struct lpc24xx_rtc *rtc = dev_get_drvdata(dev); + + if (enable) + rtc_writel(rtc, LPC24XX_AMR, 0); + else + rtc_writel(rtc, LPC24XX_AMR, LPC24XX_ALARM_DISABLE); + + return 0; +} + +static irqreturn_t lpc24xx_rtc_interrupt(int irq, void *data) +{ + unsigned long events = RTC_IRQF; + struct lpc24xx_rtc *rtc = data; + u32 rtc_iir; + + /* Check interrupt cause */ + rtc_iir = rtc_readl(rtc, LPC24XX_ILR); + if (rtc_iir & LPC24XX_RTCALF) { + events |= RTC_AF; + rtc_writel(rtc, LPC24XX_AMR, LPC24XX_ALARM_DISABLE); + } + + /* Clear interrupt status and report event */ + rtc_writel(rtc, LPC24XX_ILR, rtc_iir); + rtc_update_irq(rtc->rtc, 1, events); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops lpc24xx_rtc_ops = { + .read_time = lpc24xx_rtc_read_time, + .set_time = lpc24xx_rtc_set_time, + .read_alarm = lpc24xx_rtc_read_alarm, + .set_alarm = lpc24xx_rtc_set_alarm, + .alarm_irq_enable = lpc24xx_rtc_alarm_irq_enable, +}; + +static int lpc24xx_rtc_probe(struct platform_device *pdev) +{ + struct lpc24xx_rtc *rtc; + int irq, ret; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->rtc_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rtc->rtc_base)) + return PTR_ERR(rtc->rtc_base); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + rtc->clk_rtc = devm_clk_get(&pdev->dev, "rtc"); + if (IS_ERR(rtc->clk_rtc)) { + dev_err(&pdev->dev, "error getting rtc clock\n"); + return PTR_ERR(rtc->clk_rtc); + } + + rtc->clk_reg = devm_clk_get(&pdev->dev, "reg"); + if (IS_ERR(rtc->clk_reg)) { + dev_err(&pdev->dev, "error getting reg clock\n"); + return PTR_ERR(rtc->clk_reg); + } + + ret = clk_prepare_enable(rtc->clk_rtc); + if (ret) { + dev_err(&pdev->dev, "unable to enable rtc clock\n"); + return ret; + } + + ret = clk_prepare_enable(rtc->clk_reg); + if (ret) { + dev_err(&pdev->dev, "unable to enable reg clock\n"); + goto disable_rtc_clk; + } + + platform_set_drvdata(pdev, rtc); + + /* Clear any pending interrupts */ + rtc_writel(rtc, LPC24XX_ILR, LPC24XX_RTCCIF | LPC24XX_RTCALF); + + /* Enable RTC count */ + rtc_writel(rtc, LPC24XX_CCR, LPC24XX_CLKEN | LPC178X_CCALEN); + + ret = devm_request_irq(&pdev->dev, irq, lpc24xx_rtc_interrupt, 0, + pdev->name, rtc); + if (ret < 0) { + dev_warn(&pdev->dev, "can't request interrupt\n"); + goto disable_clks; + } + + rtc->rtc = devm_rtc_device_register(&pdev->dev, "lpc24xx-rtc", + &lpc24xx_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc->rtc)) { + dev_err(&pdev->dev, "can't register rtc device\n"); + ret = PTR_ERR(rtc->rtc); + goto disable_clks; + } + + return 0; + +disable_clks: + clk_disable_unprepare(rtc->clk_reg); +disable_rtc_clk: + clk_disable_unprepare(rtc->clk_rtc); + return ret; +} + +static void lpc24xx_rtc_remove(struct platform_device *pdev) +{ + struct lpc24xx_rtc *rtc = platform_get_drvdata(pdev); + + /* Ensure all interrupt sources are masked */ + rtc_writel(rtc, LPC24XX_AMR, LPC24XX_ALARM_DISABLE); + rtc_writel(rtc, LPC24XX_CIIR, 0); + + rtc_writel(rtc, LPC24XX_CCR, LPC178X_CCALEN); + + clk_disable_unprepare(rtc->clk_rtc); + clk_disable_unprepare(rtc->clk_reg); +} + +static const struct of_device_id lpc24xx_rtc_match[] = { + { .compatible = "nxp,lpc1788-rtc" }, + { } +}; +MODULE_DEVICE_TABLE(of, lpc24xx_rtc_match); + +static struct platform_driver lpc24xx_rtc_driver = { + .probe = lpc24xx_rtc_probe, + .remove = lpc24xx_rtc_remove, + .driver = { + .name = "lpc24xx-rtc", + .of_match_table = lpc24xx_rtc_match, + }, +}; +module_platform_driver(lpc24xx_rtc_driver); + +MODULE_AUTHOR("Kevin Wells "); +MODULE_DESCRIPTION("RTC driver for the LPC178x/18xx/408x/43xx SoCs"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-lpc32xx.c b/drivers/rtc/rtc-lpc32xx.c new file mode 100644 index 000000000..74280bffe --- /dev/null +++ b/drivers/rtc/rtc-lpc32xx.c @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2010 NXP Semiconductors + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Clock and Power control register offsets + */ +#define LPC32XX_RTC_UCOUNT 0x00 +#define LPC32XX_RTC_DCOUNT 0x04 +#define LPC32XX_RTC_MATCH0 0x08 +#define LPC32XX_RTC_MATCH1 0x0C +#define LPC32XX_RTC_CTRL 0x10 +#define LPC32XX_RTC_INTSTAT 0x14 +#define LPC32XX_RTC_KEY 0x18 +#define LPC32XX_RTC_SRAM 0x80 + +#define LPC32XX_RTC_CTRL_MATCH0 (1 << 0) +#define LPC32XX_RTC_CTRL_MATCH1 (1 << 1) +#define LPC32XX_RTC_CTRL_ONSW_MATCH0 (1 << 2) +#define LPC32XX_RTC_CTRL_ONSW_MATCH1 (1 << 3) +#define LPC32XX_RTC_CTRL_SW_RESET (1 << 4) +#define LPC32XX_RTC_CTRL_CNTR_DIS (1 << 6) +#define LPC32XX_RTC_CTRL_ONSW_FORCE_HI (1 << 7) + +#define LPC32XX_RTC_INTSTAT_MATCH0 (1 << 0) +#define LPC32XX_RTC_INTSTAT_MATCH1 (1 << 1) +#define LPC32XX_RTC_INTSTAT_ONSW (1 << 2) + +#define LPC32XX_RTC_KEY_ONSW_LOADVAL 0xB5C13F27 + +#define rtc_readl(dev, reg) \ + __raw_readl((dev)->rtc_base + (reg)) +#define rtc_writel(dev, reg, val) \ + __raw_writel((val), (dev)->rtc_base + (reg)) + +struct lpc32xx_rtc { + void __iomem *rtc_base; + int irq; + unsigned char alarm_enabled; + struct rtc_device *rtc; + spinlock_t lock; +}; + +static int lpc32xx_rtc_read_time(struct device *dev, struct rtc_time *time) +{ + unsigned long elapsed_sec; + struct lpc32xx_rtc *rtc = dev_get_drvdata(dev); + + elapsed_sec = rtc_readl(rtc, LPC32XX_RTC_UCOUNT); + rtc_time64_to_tm(elapsed_sec, time); + + return 0; +} + +static int lpc32xx_rtc_set_time(struct device *dev, struct rtc_time *time) +{ + struct lpc32xx_rtc *rtc = dev_get_drvdata(dev); + u32 secs = rtc_tm_to_time64(time); + u32 tmp; + + spin_lock_irq(&rtc->lock); + + /* RTC must be disabled during count update */ + tmp = rtc_readl(rtc, LPC32XX_RTC_CTRL); + rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp | LPC32XX_RTC_CTRL_CNTR_DIS); + rtc_writel(rtc, LPC32XX_RTC_UCOUNT, secs); + rtc_writel(rtc, LPC32XX_RTC_DCOUNT, 0xFFFFFFFF - secs); + rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp &= ~LPC32XX_RTC_CTRL_CNTR_DIS); + + spin_unlock_irq(&rtc->lock); + + return 0; +} + +static int lpc32xx_rtc_read_alarm(struct device *dev, + struct rtc_wkalrm *wkalrm) +{ + struct lpc32xx_rtc *rtc = dev_get_drvdata(dev); + + rtc_time64_to_tm(rtc_readl(rtc, LPC32XX_RTC_MATCH0), &wkalrm->time); + wkalrm->enabled = rtc->alarm_enabled; + wkalrm->pending = !!(rtc_readl(rtc, LPC32XX_RTC_INTSTAT) & + LPC32XX_RTC_INTSTAT_MATCH0); + + return rtc_valid_tm(&wkalrm->time); +} + +static int lpc32xx_rtc_set_alarm(struct device *dev, + struct rtc_wkalrm *wkalrm) +{ + struct lpc32xx_rtc *rtc = dev_get_drvdata(dev); + unsigned long alarmsecs; + u32 tmp; + + alarmsecs = rtc_tm_to_time64(&wkalrm->time); + + spin_lock_irq(&rtc->lock); + + /* Disable alarm during update */ + tmp = rtc_readl(rtc, LPC32XX_RTC_CTRL); + rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp & ~LPC32XX_RTC_CTRL_MATCH0); + + rtc_writel(rtc, LPC32XX_RTC_MATCH0, alarmsecs); + + rtc->alarm_enabled = wkalrm->enabled; + if (wkalrm->enabled) { + rtc_writel(rtc, LPC32XX_RTC_INTSTAT, + LPC32XX_RTC_INTSTAT_MATCH0); + rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp | + LPC32XX_RTC_CTRL_MATCH0); + } + + spin_unlock_irq(&rtc->lock); + + return 0; +} + +static int lpc32xx_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct lpc32xx_rtc *rtc = dev_get_drvdata(dev); + u32 tmp; + + spin_lock_irq(&rtc->lock); + tmp = rtc_readl(rtc, LPC32XX_RTC_CTRL); + + if (enabled) { + rtc->alarm_enabled = 1; + tmp |= LPC32XX_RTC_CTRL_MATCH0; + } else { + rtc->alarm_enabled = 0; + tmp &= ~LPC32XX_RTC_CTRL_MATCH0; + } + + rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp); + spin_unlock_irq(&rtc->lock); + + return 0; +} + +static irqreturn_t lpc32xx_rtc_alarm_interrupt(int irq, void *dev) +{ + struct lpc32xx_rtc *rtc = dev; + + spin_lock(&rtc->lock); + + /* Disable alarm interrupt */ + rtc_writel(rtc, LPC32XX_RTC_CTRL, + rtc_readl(rtc, LPC32XX_RTC_CTRL) & + ~LPC32XX_RTC_CTRL_MATCH0); + rtc->alarm_enabled = 0; + + /* + * Write a large value to the match value so the RTC won't + * keep firing the match status + */ + rtc_writel(rtc, LPC32XX_RTC_MATCH0, 0xFFFFFFFF); + rtc_writel(rtc, LPC32XX_RTC_INTSTAT, LPC32XX_RTC_INTSTAT_MATCH0); + + spin_unlock(&rtc->lock); + + rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops lpc32xx_rtc_ops = { + .read_time = lpc32xx_rtc_read_time, + .set_time = lpc32xx_rtc_set_time, + .read_alarm = lpc32xx_rtc_read_alarm, + .set_alarm = lpc32xx_rtc_set_alarm, + .alarm_irq_enable = lpc32xx_rtc_alarm_irq_enable, +}; + +static int lpc32xx_rtc_probe(struct platform_device *pdev) +{ + struct lpc32xx_rtc *rtc; + int err; + u32 tmp; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (unlikely(!rtc)) + return -ENOMEM; + + rtc->rtc_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rtc->rtc_base)) + return PTR_ERR(rtc->rtc_base); + + spin_lock_init(&rtc->lock); + + /* + * The RTC is on a separate power domain and can keep it's state + * across a chip power cycle. If the RTC has never been previously + * setup, then set it up now for the first time. + */ + tmp = rtc_readl(rtc, LPC32XX_RTC_CTRL); + if (rtc_readl(rtc, LPC32XX_RTC_KEY) != LPC32XX_RTC_KEY_ONSW_LOADVAL) { + tmp &= ~(LPC32XX_RTC_CTRL_SW_RESET | + LPC32XX_RTC_CTRL_CNTR_DIS | + LPC32XX_RTC_CTRL_MATCH0 | + LPC32XX_RTC_CTRL_MATCH1 | + LPC32XX_RTC_CTRL_ONSW_MATCH0 | + LPC32XX_RTC_CTRL_ONSW_MATCH1 | + LPC32XX_RTC_CTRL_ONSW_FORCE_HI); + rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp); + + /* Clear latched interrupt states */ + rtc_writel(rtc, LPC32XX_RTC_MATCH0, 0xFFFFFFFF); + rtc_writel(rtc, LPC32XX_RTC_INTSTAT, + LPC32XX_RTC_INTSTAT_MATCH0 | + LPC32XX_RTC_INTSTAT_MATCH1 | + LPC32XX_RTC_INTSTAT_ONSW); + + /* Write key value to RTC so it won't reload on reset */ + rtc_writel(rtc, LPC32XX_RTC_KEY, + LPC32XX_RTC_KEY_ONSW_LOADVAL); + } else { + rtc_writel(rtc, LPC32XX_RTC_CTRL, + tmp & ~LPC32XX_RTC_CTRL_MATCH0); + } + + platform_set_drvdata(pdev, rtc); + + rtc->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc->rtc)) + return PTR_ERR(rtc->rtc); + + rtc->rtc->ops = &lpc32xx_rtc_ops; + rtc->rtc->range_max = U32_MAX; + + err = devm_rtc_register_device(rtc->rtc); + if (err) + return err; + + /* + * IRQ is enabled after device registration in case alarm IRQ + * is pending upon suspend exit. + */ + rtc->irq = platform_get_irq(pdev, 0); + if (rtc->irq < 0) { + dev_warn(&pdev->dev, "Can't get interrupt resource\n"); + } else { + if (devm_request_irq(&pdev->dev, rtc->irq, + lpc32xx_rtc_alarm_interrupt, + 0, pdev->name, rtc) < 0) { + dev_warn(&pdev->dev, "Can't request interrupt.\n"); + rtc->irq = -1; + } else { + device_init_wakeup(&pdev->dev, true); + } + } + + return 0; +} + +#ifdef CONFIG_PM +static int lpc32xx_rtc_suspend(struct device *dev) +{ + struct lpc32xx_rtc *rtc = dev_get_drvdata(dev); + + if (rtc->irq >= 0) { + if (device_may_wakeup(dev)) + enable_irq_wake(rtc->irq); + else + disable_irq_wake(rtc->irq); + } + + return 0; +} + +static int lpc32xx_rtc_resume(struct device *dev) +{ + struct lpc32xx_rtc *rtc = dev_get_drvdata(dev); + + if (rtc->irq >= 0 && device_may_wakeup(dev)) + disable_irq_wake(rtc->irq); + + return 0; +} + +/* Unconditionally disable the alarm */ +static int lpc32xx_rtc_freeze(struct device *dev) +{ + struct lpc32xx_rtc *rtc = dev_get_drvdata(dev); + + spin_lock_irq(&rtc->lock); + + rtc_writel(rtc, LPC32XX_RTC_CTRL, + rtc_readl(rtc, LPC32XX_RTC_CTRL) & + ~LPC32XX_RTC_CTRL_MATCH0); + + spin_unlock_irq(&rtc->lock); + + return 0; +} + +static int lpc32xx_rtc_thaw(struct device *dev) +{ + struct lpc32xx_rtc *rtc = dev_get_drvdata(dev); + + if (rtc->alarm_enabled) { + spin_lock_irq(&rtc->lock); + + rtc_writel(rtc, LPC32XX_RTC_CTRL, + rtc_readl(rtc, LPC32XX_RTC_CTRL) | + LPC32XX_RTC_CTRL_MATCH0); + + spin_unlock_irq(&rtc->lock); + } + + return 0; +} + +static const struct dev_pm_ops lpc32xx_rtc_pm_ops = { + .suspend = lpc32xx_rtc_suspend, + .resume = lpc32xx_rtc_resume, + .freeze = lpc32xx_rtc_freeze, + .thaw = lpc32xx_rtc_thaw, + .restore = lpc32xx_rtc_resume +}; + +#define LPC32XX_RTC_PM_OPS (&lpc32xx_rtc_pm_ops) +#else +#define LPC32XX_RTC_PM_OPS NULL +#endif + +#ifdef CONFIG_OF +static const struct of_device_id lpc32xx_rtc_match[] = { + { .compatible = "nxp,lpc3220-rtc" }, + { } +}; +MODULE_DEVICE_TABLE(of, lpc32xx_rtc_match); +#endif + +static struct platform_driver lpc32xx_rtc_driver = { + .probe = lpc32xx_rtc_probe, + .driver = { + .name = "rtc-lpc32xx", + .pm = LPC32XX_RTC_PM_OPS, + .of_match_table = of_match_ptr(lpc32xx_rtc_match), + }, +}; + +module_platform_driver(lpc32xx_rtc_driver); + +MODULE_AUTHOR("Kevin Wells + * + * Based on m41t00.c by Mark A. Greer + * + * 2006 (c) mycable GmbH + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_RTC_DRV_M41T80_WDT +#include +#include +#include +#include +#include +#endif + +#define M41T80_REG_SSEC 0x00 +#define M41T80_REG_SEC 0x01 +#define M41T80_REG_MIN 0x02 +#define M41T80_REG_HOUR 0x03 +#define M41T80_REG_WDAY 0x04 +#define M41T80_REG_DAY 0x05 +#define M41T80_REG_MON 0x06 +#define M41T80_REG_YEAR 0x07 +#define M41T80_REG_ALARM_MON 0x0a +#define M41T80_REG_ALARM_DAY 0x0b +#define M41T80_REG_ALARM_HOUR 0x0c +#define M41T80_REG_ALARM_MIN 0x0d +#define M41T80_REG_ALARM_SEC 0x0e +#define M41T80_REG_FLAGS 0x0f +#define M41T80_REG_SQW 0x13 + +#define M41T80_DATETIME_REG_SIZE (M41T80_REG_YEAR + 1) +#define M41T80_ALARM_REG_SIZE \ + (M41T80_REG_ALARM_SEC + 1 - M41T80_REG_ALARM_MON) + +#define M41T80_SQW_MAX_FREQ 32768 + +#define M41T80_SEC_ST BIT(7) /* ST: Stop Bit */ +#define M41T80_ALMON_AFE BIT(7) /* AFE: AF Enable Bit */ +#define M41T80_ALMON_SQWE BIT(6) /* SQWE: SQW Enable Bit */ +#define M41T80_ALHOUR_HT BIT(6) /* HT: Halt Update Bit */ +#define M41T80_FLAGS_OF BIT(2) /* OF: Oscillator Failure Bit */ +#define M41T80_FLAGS_AF BIT(6) /* AF: Alarm Flag Bit */ +#define M41T80_FLAGS_BATT_LOW BIT(4) /* BL: Battery Low Bit */ +#define M41T80_WATCHDOG_RB2 BIT(7) /* RB: Watchdog resolution */ +#define M41T80_WATCHDOG_RB1 BIT(1) /* RB: Watchdog resolution */ +#define M41T80_WATCHDOG_RB0 BIT(0) /* RB: Watchdog resolution */ + +#define M41T80_FEATURE_HT BIT(0) /* Halt feature */ +#define M41T80_FEATURE_BL BIT(1) /* Battery low indicator */ +#define M41T80_FEATURE_SQ BIT(2) /* Squarewave feature */ +#define M41T80_FEATURE_WD BIT(3) /* Extra watchdog resolution */ +#define M41T80_FEATURE_SQ_ALT BIT(4) /* RSx bits are in reg 4 */ + +static const struct i2c_device_id m41t80_id[] = { + { .name = "m41t62", .driver_data = M41T80_FEATURE_SQ | M41T80_FEATURE_SQ_ALT }, + { .name = "m41t65", .driver_data = M41T80_FEATURE_WD }, + { .name = "m41t80", .driver_data = M41T80_FEATURE_SQ }, + { .name = "m41t81", .driver_data = M41T80_FEATURE_HT | M41T80_FEATURE_SQ}, + { .name = "m41t81s", .driver_data = M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { .name = "m41t82", .driver_data = M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { .name = "m41t83", .driver_data = M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { .name = "m41st84", .driver_data = M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { .name = "m41st85", .driver_data = M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { .name = "m41st87", .driver_data = M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { .name = "rv4162", .driver_data = M41T80_FEATURE_SQ | M41T80_FEATURE_WD | M41T80_FEATURE_SQ_ALT }, + { } +}; +MODULE_DEVICE_TABLE(i2c, m41t80_id); + +static const __maybe_unused struct of_device_id m41t80_of_match[] = { + { + .compatible = "st,m41t62", + .data = (void *)(M41T80_FEATURE_SQ | M41T80_FEATURE_SQ_ALT) + }, + { + .compatible = "st,m41t65", + .data = (void *)(M41T80_FEATURE_WD) + }, + { + .compatible = "st,m41t80", + .data = (void *)(M41T80_FEATURE_SQ) + }, + { + .compatible = "st,m41t81", + .data = (void *)(M41T80_FEATURE_HT | M41T80_FEATURE_SQ) + }, + { + .compatible = "st,m41t81s", + .data = (void *)(M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ) + }, + { + .compatible = "st,m41t82", + .data = (void *)(M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ) + }, + { + .compatible = "st,m41t83", + .data = (void *)(M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ) + }, + { + .compatible = "st,m41t84", + .data = (void *)(M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ) + }, + { + .compatible = "st,m41t85", + .data = (void *)(M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ) + }, + { + .compatible = "st,m41t87", + .data = (void *)(M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ) + }, + { + .compatible = "microcrystal,rv4162", + .data = (void *)(M41T80_FEATURE_SQ | M41T80_FEATURE_WD | M41T80_FEATURE_SQ_ALT) + }, + /* DT compatibility only, do not use compatibles below: */ + { + .compatible = "st,rv4162", + .data = (void *)(M41T80_FEATURE_SQ | M41T80_FEATURE_WD | M41T80_FEATURE_SQ_ALT) + }, + { + .compatible = "rv4162", + .data = (void *)(M41T80_FEATURE_SQ | M41T80_FEATURE_WD | M41T80_FEATURE_SQ_ALT) + }, + { } +}; +MODULE_DEVICE_TABLE(of, m41t80_of_match); + +struct m41t80_data { + unsigned long features; + struct i2c_client *client; + struct rtc_device *rtc; +#ifdef CONFIG_COMMON_CLK + struct clk_hw sqw; + unsigned long freq; + unsigned int sqwe; +#endif +}; + +static irqreturn_t m41t80_handle_irq(int irq, void *dev_id) +{ + struct i2c_client *client = dev_id; + struct m41t80_data *m41t80 = i2c_get_clientdata(client); + unsigned long events = 0; + int flags, flags_afe; + + rtc_lock(m41t80->rtc); + + flags_afe = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON); + if (flags_afe < 0) { + rtc_unlock(m41t80->rtc); + return IRQ_NONE; + } + + flags = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); + if (flags <= 0) { + rtc_unlock(m41t80->rtc); + return IRQ_NONE; + } + + if (flags & M41T80_FLAGS_AF) { + flags &= ~M41T80_FLAGS_AF; + flags_afe &= ~M41T80_ALMON_AFE; + events |= RTC_AF; + } + + if (events) { + rtc_update_irq(m41t80->rtc, 1, events); + i2c_smbus_write_byte_data(client, M41T80_REG_FLAGS, flags); + i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, + flags_afe); + } + + rtc_unlock(m41t80->rtc); + + return IRQ_HANDLED; +} + +static int m41t80_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned char buf[8]; + int err, flags; + + flags = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); + if (flags < 0) + return flags; + + if (flags & M41T80_FLAGS_OF) { + dev_err(&client->dev, "Oscillator failure, time may not be accurate, write time to RTC to fix it.\n"); + return -EINVAL; + } + + err = i2c_smbus_read_i2c_block_data(client, M41T80_REG_SSEC, + sizeof(buf), buf); + if (err < 0) { + dev_dbg(&client->dev, "Unable to read date\n"); + return err; + } + + tm->tm_sec = bcd2bin(buf[M41T80_REG_SEC] & 0x7f); + tm->tm_min = bcd2bin(buf[M41T80_REG_MIN] & 0x7f); + tm->tm_hour = bcd2bin(buf[M41T80_REG_HOUR] & 0x3f); + tm->tm_mday = bcd2bin(buf[M41T80_REG_DAY] & 0x3f); + tm->tm_wday = buf[M41T80_REG_WDAY] & 0x07; + tm->tm_mon = bcd2bin(buf[M41T80_REG_MON] & 0x1f) - 1; + + /* assume 20YY not 19YY, and ignore the Century Bit */ + tm->tm_year = bcd2bin(buf[M41T80_REG_YEAR]) + 100; + return 0; +} + +static int m41t80_rtc_set_time(struct device *dev, struct rtc_time *in_tm) +{ + struct i2c_client *client = to_i2c_client(dev); + struct m41t80_data *clientdata = i2c_get_clientdata(client); + struct rtc_time tm = *in_tm; + unsigned char buf[8]; + int err, flags; + time64_t time = 0; + + flags = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); + if (flags < 0) + return flags; + if (flags & M41T80_FLAGS_OF) { + /* add 4sec of oscillator stablize time otherwise we are behind 4sec */ + time = rtc_tm_to_time64(&tm); + rtc_time64_to_tm(time + 4, &tm); + } + buf[M41T80_REG_SSEC] = 0; + buf[M41T80_REG_SEC] = bin2bcd(tm.tm_sec); + buf[M41T80_REG_MIN] = bin2bcd(tm.tm_min); + buf[M41T80_REG_HOUR] = bin2bcd(tm.tm_hour); + buf[M41T80_REG_DAY] = bin2bcd(tm.tm_mday); + buf[M41T80_REG_MON] = bin2bcd(tm.tm_mon + 1); + buf[M41T80_REG_YEAR] = bin2bcd(tm.tm_year - 100); + buf[M41T80_REG_WDAY] = tm.tm_wday; + + /* If the square wave output is controlled in the weekday register */ + if (clientdata->features & M41T80_FEATURE_SQ_ALT) { + int val; + + val = i2c_smbus_read_byte_data(client, M41T80_REG_WDAY); + if (val < 0) + return val; + + buf[M41T80_REG_WDAY] |= (val & 0xf0); + } + + err = i2c_smbus_write_i2c_block_data(client, M41T80_REG_SSEC, + sizeof(buf), buf); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write to date registers\n"); + return err; + } + if (flags & M41T80_FLAGS_OF) { + /* OF cannot be immediately reset: oscillator has to be restarted. */ + dev_warn(&client->dev, "OF bit is still set, kickstarting clock.\n"); + err = i2c_smbus_write_byte_data(client, M41T80_REG_SEC, M41T80_SEC_ST); + if (err < 0) { + dev_dbg(&client->dev, "Can't set ST bit\n"); + return err; + } + err = i2c_smbus_write_byte_data(client, M41T80_REG_SEC, flags & ~M41T80_SEC_ST); + if (err < 0) { + dev_dbg(&client->dev, "Can't clear ST bit\n"); + return err; + } + /* oscillator must run for 4sec before we attempt to reset OF bit */ + msleep(4000); + /* Clear the OF bit of Flags Register */ + err = i2c_smbus_write_byte_data(client, M41T80_REG_FLAGS, flags & ~M41T80_FLAGS_OF); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write flags register\n"); + return err; + } + flags = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); + if (flags < 0) { + return flags; + } else if (flags & M41T80_FLAGS_OF) { + dev_dbg(&client->dev, "Can't clear the OF bit check battery\n"); + return err; + } + } + + return err; +} + +static int m41t80_rtc_proc(struct device *dev, struct seq_file *seq) +{ + struct i2c_client *client = to_i2c_client(dev); + struct m41t80_data *clientdata = i2c_get_clientdata(client); + int reg; + + if (clientdata->features & M41T80_FEATURE_BL) { + reg = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); + if (reg < 0) + return reg; + seq_printf(seq, "battery\t\t: %s\n", + (reg & M41T80_FLAGS_BATT_LOW) ? "exhausted" : "ok"); + } + return 0; +} + +static int m41t80_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct i2c_client *client = to_i2c_client(dev); + int flags, retval; + + flags = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON); + if (flags < 0) + return flags; + + if (enabled) + flags |= M41T80_ALMON_AFE; + else + flags &= ~M41T80_ALMON_AFE; + + retval = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, flags); + if (retval < 0) { + dev_dbg(dev, "Unable to enable alarm IRQ %d\n", retval); + return retval; + } + return 0; +} + +static int m41t80_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 alarmvals[5]; + int ret, err; + + alarmvals[0] = bin2bcd(alrm->time.tm_mon + 1); + alarmvals[1] = bin2bcd(alrm->time.tm_mday); + alarmvals[2] = bin2bcd(alrm->time.tm_hour); + alarmvals[3] = bin2bcd(alrm->time.tm_min); + alarmvals[4] = bin2bcd(alrm->time.tm_sec); + + /* Clear AF and AFE flags */ + ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON); + if (ret < 0) + return ret; + err = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, + ret & ~(M41T80_ALMON_AFE)); + if (err < 0) { + dev_dbg(dev, "Unable to clear AFE bit\n"); + return err; + } + + /* Keep SQWE bit value */ + alarmvals[0] |= (ret & M41T80_ALMON_SQWE); + + ret = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); + if (ret < 0) + return ret; + + err = i2c_smbus_write_byte_data(client, M41T80_REG_FLAGS, + ret & ~(M41T80_FLAGS_AF)); + if (err < 0) { + dev_dbg(dev, "Unable to clear AF bit\n"); + return err; + } + + /* Write the alarm */ + err = i2c_smbus_write_i2c_block_data(client, M41T80_REG_ALARM_MON, + 5, alarmvals); + if (err) + return err; + + /* Enable the alarm interrupt */ + if (alrm->enabled) { + alarmvals[0] |= M41T80_ALMON_AFE; + err = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, + alarmvals[0]); + if (err) + return err; + } + + return 0; +} + +static int m41t80_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 alarmvals[5]; + int flags, ret; + + ret = i2c_smbus_read_i2c_block_data(client, M41T80_REG_ALARM_MON, + 5, alarmvals); + if (ret != 5) + return ret < 0 ? ret : -EIO; + + flags = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); + if (flags < 0) + return flags; + + alrm->time.tm_sec = bcd2bin(alarmvals[4] & 0x7f); + alrm->time.tm_min = bcd2bin(alarmvals[3] & 0x7f); + alrm->time.tm_hour = bcd2bin(alarmvals[2] & 0x3f); + alrm->time.tm_mday = bcd2bin(alarmvals[1] & 0x3f); + alrm->time.tm_mon = bcd2bin(alarmvals[0] & 0x3f) - 1; + + alrm->enabled = !!(alarmvals[0] & M41T80_ALMON_AFE); + alrm->pending = (flags & M41T80_FLAGS_AF) && alrm->enabled; + + return 0; +} + +static const struct rtc_class_ops m41t80_rtc_ops = { + .read_time = m41t80_rtc_read_time, + .set_time = m41t80_rtc_set_time, + .proc = m41t80_rtc_proc, + .read_alarm = m41t80_read_alarm, + .set_alarm = m41t80_set_alarm, + .alarm_irq_enable = m41t80_alarm_irq_enable, +}; + +#ifdef CONFIG_PM_SLEEP +static int m41t80_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (client->irq >= 0 && device_may_wakeup(dev)) + enable_irq_wake(client->irq); + + return 0; +} + +static int m41t80_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (client->irq >= 0 && device_may_wakeup(dev)) + disable_irq_wake(client->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(m41t80_pm, m41t80_suspend, m41t80_resume); + +#ifdef CONFIG_COMMON_CLK +#define sqw_to_m41t80_data(_hw) container_of(_hw, struct m41t80_data, sqw) + +static unsigned long m41t80_decode_freq(int setting) +{ + return (setting == 0) ? 0 : (setting == 1) ? M41T80_SQW_MAX_FREQ : + M41T80_SQW_MAX_FREQ >> setting; +} + +static unsigned long m41t80_get_freq(struct m41t80_data *m41t80) +{ + struct i2c_client *client = m41t80->client; + int reg_sqw = (m41t80->features & M41T80_FEATURE_SQ_ALT) ? + M41T80_REG_WDAY : M41T80_REG_SQW; + int ret = i2c_smbus_read_byte_data(client, reg_sqw); + + if (ret < 0) + return 0; + return m41t80_decode_freq(ret >> 4); +} + +static unsigned long m41t80_sqw_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return sqw_to_m41t80_data(hw)->freq; +} + +static int m41t80_sqw_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + if (req->rate >= M41T80_SQW_MAX_FREQ) + req->rate = M41T80_SQW_MAX_FREQ; + else if (req->rate >= M41T80_SQW_MAX_FREQ / 4) + req->rate = M41T80_SQW_MAX_FREQ / 4; + else if (req->rate) + req->rate = 1 << ilog2(req->rate); + + return 0; +} + +static int m41t80_sqw_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw); + struct i2c_client *client = m41t80->client; + int reg_sqw = (m41t80->features & M41T80_FEATURE_SQ_ALT) ? + M41T80_REG_WDAY : M41T80_REG_SQW; + int reg, ret, val = 0; + + if (rate >= M41T80_SQW_MAX_FREQ) + val = 1; + else if (rate >= M41T80_SQW_MAX_FREQ / 4) + val = 2; + else if (rate) + val = 15 - ilog2(rate); + + reg = i2c_smbus_read_byte_data(client, reg_sqw); + if (reg < 0) + return reg; + + reg = (reg & 0x0f) | (val << 4); + + ret = i2c_smbus_write_byte_data(client, reg_sqw, reg); + if (!ret) + m41t80->freq = m41t80_decode_freq(val); + return ret; +} + +static int m41t80_sqw_control(struct clk_hw *hw, bool enable) +{ + struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw); + struct i2c_client *client = m41t80->client; + int ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON); + + if (ret < 0) + return ret; + + if (enable) + ret |= M41T80_ALMON_SQWE; + else + ret &= ~M41T80_ALMON_SQWE; + + ret = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, ret); + if (!ret) + m41t80->sqwe = enable; + return ret; +} + +static int m41t80_sqw_prepare(struct clk_hw *hw) +{ + return m41t80_sqw_control(hw, 1); +} + +static void m41t80_sqw_unprepare(struct clk_hw *hw) +{ + m41t80_sqw_control(hw, 0); +} + +static int m41t80_sqw_is_prepared(struct clk_hw *hw) +{ + return sqw_to_m41t80_data(hw)->sqwe; +} + +static const struct clk_ops m41t80_sqw_ops = { + .prepare = m41t80_sqw_prepare, + .unprepare = m41t80_sqw_unprepare, + .is_prepared = m41t80_sqw_is_prepared, + .recalc_rate = m41t80_sqw_recalc_rate, + .determine_rate = m41t80_sqw_determine_rate, + .set_rate = m41t80_sqw_set_rate, +}; + +static struct clk *m41t80_sqw_register_clk(struct m41t80_data *m41t80) +{ + struct i2c_client *client = m41t80->client; + struct device_node *node = client->dev.of_node; + struct device_node *fixed_clock; + struct clk_init_data init = {}; + struct clk *clk; + int ret; + + fixed_clock = of_get_child_by_name(node, "clock"); + if (fixed_clock) { + /* + * skip registering square wave clock when a fixed + * clock has been registered. The fixed clock is + * registered automatically when being referenced. + */ + of_node_put(fixed_clock); + return NULL; + } + + /* First disable the clock */ + ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON); + if (ret < 0) + return ERR_PTR(ret); + ret = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, + ret & ~(M41T80_ALMON_SQWE)); + if (ret < 0) + return ERR_PTR(ret); + + init.name = "m41t80-sqw"; + init.ops = &m41t80_sqw_ops; + init.flags = 0; + init.parent_names = NULL; + init.num_parents = 0; + m41t80->sqw.init = &init; + m41t80->freq = m41t80_get_freq(m41t80); + + /* optional override of the clockname */ + of_property_read_string(node, "clock-output-names", &init.name); + + /* register the clock */ + clk = clk_register(&client->dev, &m41t80->sqw); + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); + + return clk; +} +#endif + +#ifdef CONFIG_RTC_DRV_M41T80_WDT +/* + ***************************************************************************** + * + * Watchdog Driver + * + ***************************************************************************** + */ +static DEFINE_MUTEX(m41t80_rtc_mutex); +static struct i2c_client *save_client; + +/* Default margin */ +#define WD_TIMO 60 /* 1..31 seconds */ + +static int wdt_margin = WD_TIMO; +module_param(wdt_margin, int, 0); +MODULE_PARM_DESC(wdt_margin, "Watchdog timeout in seconds (default 60s)"); + +static unsigned long wdt_is_open; +static int boot_flag; + +/** + * wdt_ping - Reload counter one with the watchdog timeout. + * We don't bother reloading the cascade counter. + */ +static void wdt_ping(void) +{ + unsigned char i2c_data[2]; + struct i2c_msg msgs1[1] = { + { + .addr = save_client->addr, + .flags = 0, + .len = 2, + .buf = i2c_data, + }, + }; + struct m41t80_data *clientdata = i2c_get_clientdata(save_client); + + i2c_data[0] = 0x09; /* watchdog register */ + + if (wdt_margin > 31) + i2c_data[1] = (wdt_margin & 0xFC) | 0x83; /* resolution = 4s */ + else + /* + * WDS = 1 (0x80), mulitplier = WD_TIMO, resolution = 1s (0x02) + */ + i2c_data[1] = wdt_margin << 2 | 0x82; + + /* + * M41T65 has three bits for watchdog resolution. Don't set bit 7, as + * that would be an invalid resolution. + */ + if (clientdata->features & M41T80_FEATURE_WD) + i2c_data[1] &= ~M41T80_WATCHDOG_RB2; + + i2c_transfer(save_client->adapter, msgs1, 1); +} + +/** + * wdt_disable - disables watchdog. + */ +static void wdt_disable(void) +{ + unsigned char i2c_data[2], i2c_buf[0x10]; + struct i2c_msg msgs0[2] = { + { + .addr = save_client->addr, + .flags = 0, + .len = 1, + .buf = i2c_data, + }, + { + .addr = save_client->addr, + .flags = I2C_M_RD, + .len = 1, + .buf = i2c_buf, + }, + }; + struct i2c_msg msgs1[1] = { + { + .addr = save_client->addr, + .flags = 0, + .len = 2, + .buf = i2c_data, + }, + }; + + i2c_data[0] = 0x09; + i2c_transfer(save_client->adapter, msgs0, 2); + + i2c_data[0] = 0x09; + i2c_data[1] = 0x00; + i2c_transfer(save_client->adapter, msgs1, 1); +} + +/** + * wdt_write - write to watchdog. + * @file: file handle to the watchdog + * @buf: buffer to write (unused as data does not matter here + * @count: count of bytes + * @ppos: pointer to the position to write. No seeks allowed + * + * A write to a watchdog device is defined as a keepalive signal. Any + * write of data will do, as we don't define content meaning. + */ +static ssize_t wdt_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + if (count) { + wdt_ping(); + return 1; + } + return 0; +} + +static ssize_t wdt_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + return 0; +} + +/** + * wdt_ioctl - ioctl handler to set watchdog. + * @file: file handle to the device + * @cmd: watchdog command + * @arg: argument pointer + * + * The watchdog API defines a common set of functions for all watchdogs + * according to their available features. We only actually usefully support + * querying capabilities and current status. + */ +static int wdt_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int new_margin, rv; + static struct watchdog_info ident = { + .options = WDIOF_POWERUNDER | WDIOF_KEEPALIVEPING | + WDIOF_SETTIMEOUT, + .firmware_version = 1, + .identity = "M41T80 WTD" + }; + + switch (cmd) { + case WDIOC_GETSUPPORT: + return copy_to_user((struct watchdog_info __user *)arg, &ident, + sizeof(ident)) ? -EFAULT : 0; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(boot_flag, (int __user *)arg); + case WDIOC_KEEPALIVE: + wdt_ping(); + return 0; + case WDIOC_SETTIMEOUT: + if (get_user(new_margin, (int __user *)arg)) + return -EFAULT; + /* Arbitrary, can't find the card's limits */ + if (new_margin < 1 || new_margin > 124) + return -EINVAL; + wdt_margin = new_margin; + wdt_ping(); + fallthrough; + case WDIOC_GETTIMEOUT: + return put_user(wdt_margin, (int __user *)arg); + + case WDIOC_SETOPTIONS: + if (copy_from_user(&rv, (int __user *)arg, sizeof(int))) + return -EFAULT; + + if (rv & WDIOS_DISABLECARD) { + pr_info("disable watchdog\n"); + wdt_disable(); + } + + if (rv & WDIOS_ENABLECARD) { + pr_info("enable watchdog\n"); + wdt_ping(); + } + + return -EINVAL; + } + return -ENOTTY; +} + +static long wdt_unlocked_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret; + + mutex_lock(&m41t80_rtc_mutex); + ret = wdt_ioctl(file, cmd, arg); + mutex_unlock(&m41t80_rtc_mutex); + + return ret; +} + +/** + * wdt_open - open a watchdog. + * @inode: inode of device + * @file: file handle to device + * + */ +static int wdt_open(struct inode *inode, struct file *file) +{ + if (iminor(inode) == WATCHDOG_MINOR) { + mutex_lock(&m41t80_rtc_mutex); + if (test_and_set_bit(0, &wdt_is_open)) { + mutex_unlock(&m41t80_rtc_mutex); + return -EBUSY; + } + /* + * Activate + */ + wdt_is_open = 1; + mutex_unlock(&m41t80_rtc_mutex); + return stream_open(inode, file); + } + return -ENODEV; +} + +/** + * wdt_release - release a watchdog. + * @inode: inode to board + * @file: file handle to board + * + */ +static int wdt_release(struct inode *inode, struct file *file) +{ + if (iminor(inode) == WATCHDOG_MINOR) + clear_bit(0, &wdt_is_open); + return 0; +} + +/** + * wdt_notify_sys - notify to watchdog. + * @this: our notifier block + * @code: the event being reported + * @unused: unused + * + * Our notifier is called on system shutdowns. We want to turn the card + * off at reboot otherwise the machine will reboot again during memory + * test or worse yet during the following fsck. This would suck, in fact + * trust me - if it happens it does suck. + */ +static int wdt_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) + /* Disable Watchdog */ + wdt_disable(); + return NOTIFY_DONE; +} + +static const struct file_operations wdt_fops = { + .owner = THIS_MODULE, + .read = wdt_read, + .unlocked_ioctl = wdt_unlocked_ioctl, + .compat_ioctl = compat_ptr_ioctl, + .write = wdt_write, + .open = wdt_open, + .release = wdt_release, +}; + +static struct miscdevice wdt_dev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &wdt_fops, +}; + +/* + * The WDT card needs to learn about soft shutdowns in order to + * turn the timebomb registers off. + */ +static struct notifier_block wdt_notifier = { + .notifier_call = wdt_notify_sys, +}; +#endif /* CONFIG_RTC_DRV_M41T80_WDT */ + +/* + ***************************************************************************** + * + * Driver Interface + * + ***************************************************************************** + */ + +static int m41t80_probe(struct i2c_client *client) +{ + struct i2c_adapter *adapter = client->adapter; + int rc = 0; + struct rtc_time tm; + struct m41t80_data *m41t80_data = NULL; + bool wakeup_source = false; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK | + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&adapter->dev, "doesn't support I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_I2C_BLOCK\n"); + return -ENODEV; + } + + m41t80_data = devm_kzalloc(&client->dev, sizeof(*m41t80_data), + GFP_KERNEL); + if (!m41t80_data) + return -ENOMEM; + + m41t80_data->client = client; + m41t80_data->features = (unsigned long)i2c_get_match_data(client); + i2c_set_clientdata(client, m41t80_data); + + m41t80_data->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(m41t80_data->rtc)) + return PTR_ERR(m41t80_data->rtc); + + wakeup_source = device_property_read_bool(&client->dev, "wakeup-source"); + if (client->irq > 0) { + unsigned long irqflags = IRQF_TRIGGER_LOW; + + if (dev_fwnode(&client->dev)) + irqflags = 0; + + rc = devm_request_threaded_irq(&client->dev, client->irq, + NULL, m41t80_handle_irq, + irqflags | IRQF_ONESHOT, + "m41t80", client); + if (rc) { + dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n"); + client->irq = 0; + wakeup_source = false; + } + } + if (client->irq > 0 || wakeup_source) + device_init_wakeup(&client->dev, true); + else + clear_bit(RTC_FEATURE_ALARM, m41t80_data->rtc->features); + + m41t80_data->rtc->ops = &m41t80_rtc_ops; + m41t80_data->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + m41t80_data->rtc->range_max = RTC_TIMESTAMP_END_2099; + + if (client->irq <= 0) + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, m41t80_data->rtc->features); + + /* Make sure HT (Halt Update) bit is cleared */ + rc = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_HOUR); + + if (rc >= 0 && rc & M41T80_ALHOUR_HT) { + if (m41t80_data->features & M41T80_FEATURE_HT) { + m41t80_rtc_read_time(&client->dev, &tm); + dev_info(&client->dev, "HT bit was set!\n"); + dev_info(&client->dev, "Power Down at %ptR\n", &tm); + } + rc = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_HOUR, + rc & ~M41T80_ALHOUR_HT); + } + + if (rc < 0) { + dev_err(&client->dev, "Can't clear HT bit\n"); + return rc; + } + + /* Make sure ST (stop) bit is cleared */ + rc = i2c_smbus_read_byte_data(client, M41T80_REG_SEC); + + if (rc >= 0 && rc & M41T80_SEC_ST) + rc = i2c_smbus_write_byte_data(client, M41T80_REG_SEC, + rc & ~M41T80_SEC_ST); + if (rc < 0) { + dev_err(&client->dev, "Can't clear ST bit\n"); + return rc; + } + +#ifdef CONFIG_RTC_DRV_M41T80_WDT + if (m41t80_data->features & M41T80_FEATURE_HT) { + save_client = client; + rc = misc_register(&wdt_dev); + if (rc) + return rc; + rc = register_reboot_notifier(&wdt_notifier); + if (rc) { + misc_deregister(&wdt_dev); + return rc; + } + } +#endif +#ifdef CONFIG_COMMON_CLK + if (m41t80_data->features & M41T80_FEATURE_SQ) + m41t80_sqw_register_clk(m41t80_data); +#endif + + rc = devm_rtc_register_device(m41t80_data->rtc); + if (rc) + return rc; + + return 0; +} + +static void m41t80_remove(struct i2c_client *client) +{ +#ifdef CONFIG_RTC_DRV_M41T80_WDT + struct m41t80_data *clientdata = i2c_get_clientdata(client); + + if (clientdata->features & M41T80_FEATURE_HT) { + misc_deregister(&wdt_dev); + unregister_reboot_notifier(&wdt_notifier); + } +#endif +} + +static struct i2c_driver m41t80_driver = { + .driver = { + .name = "rtc-m41t80", + .of_match_table = of_match_ptr(m41t80_of_match), + .pm = &m41t80_pm, + }, + .probe = m41t80_probe, + .remove = m41t80_remove, + .id_table = m41t80_id, +}; + +module_i2c_driver(m41t80_driver); + +MODULE_AUTHOR("Alexander Bigga "); +MODULE_DESCRIPTION("ST Microelectronics M41T80 series RTC I2C Client Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-m41t93.c b/drivers/rtc/rtc-m41t93.c new file mode 100644 index 000000000..fa7a4b5f8 --- /dev/null +++ b/drivers/rtc/rtc-m41t93.c @@ -0,0 +1,590 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * + * Driver for ST M41T93 SPI RTC + * + * (c) 2010 Nikolaus Voss, Weinmann Medical GmbH + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define M41T93_REG_SSEC 0 +#define M41T93_REG_ST_SEC 1 +#define M41T93_REG_MIN 2 +#define M41T93_REG_CENT_HOUR 3 +#define M41T93_REG_WDAY 4 +#define M41T93_REG_DAY 5 +#define M41T93_REG_MON 6 +#define M41T93_REG_YEAR 7 +#define M41T93_REG_AL1_MONTH 0xa +#define M41T93_REG_AL1_DATE 0xb +#define M41T93_REG_AL1_HOUR 0xc +#define M41T93_REG_AL1_MIN 0xd +#define M41T93_REG_AL1_SEC 0xe +#define M41T93_BIT_A1IE BIT(7) +#define M41T93_BIT_ABE BIT(5) +#define M41T93_FLAG_AF1 BIT(6) +#define M41T93_SRAM_BASE 0x19 +#define M41T93_REG_SQW 0x13 +#define M41T93_SQW_RS_MASK 0xf0 +#define M41T93_SQW_RS_SHIFT 4 +#define M41T93_BIT_SQWE BIT(6) +#define M41T93_REG_WATCHDOG 0x9 +#define M41T93_WDT_RB_MASK 0x3 +#define M41T93_WDT_BMB_MASK 0x7c +#define M41T93_WDT_BMB_SHIFT 2 + +#define M41T93_REG_ALM_HOUR_HT 0xc +#define M41T93_REG_FLAGS 0xf + +#define M41T93_FLAG_ST (1 << 7) +#define M41T93_FLAG_OF (1 << 2) +#define M41T93_FLAG_BL (1 << 4) +#define M41T93_FLAG_HT (1 << 6) + +struct m41t93_data { + struct rtc_device *rtc; + struct regmap *regmap; +#ifdef CONFIG_COMMON_CLK + struct clk_hw clks; +#endif +#ifdef CONFIG_WATCHDOG + struct watchdog_device wdd; +#endif +}; + +static int m41t93_set_time(struct device *dev, struct rtc_time *tm) +{ + struct m41t93_data *m41t93 = dev_get_drvdata(dev); + int tmp, ret; + u8 buf[8] = {0}; /* 8 data bytes */ + u8 * const data = &buf[0]; /* ptr to first data byte */ + + dev_dbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "write", tm->tm_sec, tm->tm_min, + tm->tm_hour, tm->tm_mday, + tm->tm_mon, tm->tm_year, tm->tm_wday); + + if (tm->tm_year < 100) { + dev_warn(dev, "unsupported date (before 2000-01-01).\n"); + return -EINVAL; + } + + ret = regmap_read(m41t93->regmap, M41T93_REG_FLAGS, &tmp); + if (ret < 0) + return ret; + + if (tmp & M41T93_FLAG_OF) { + dev_warn(dev, "OF bit is set, resetting.\n"); + regmap_write(m41t93->regmap, M41T93_REG_FLAGS, tmp & ~M41T93_FLAG_OF); + + ret = regmap_read(m41t93->regmap, M41T93_REG_FLAGS, &tmp); + if (ret < 0) { + return ret; + } else if (tmp & M41T93_FLAG_OF) { + /* OF cannot be immediately reset: oscillator has to be + * restarted. */ + u8 reset_osc = buf[M41T93_REG_ST_SEC] | M41T93_FLAG_ST; + + dev_warn(dev, + "OF bit is still set, kickstarting clock.\n"); + regmap_write(m41t93->regmap, M41T93_REG_ST_SEC, reset_osc); + reset_osc &= ~M41T93_FLAG_ST; + regmap_write(m41t93->regmap, M41T93_REG_ST_SEC, reset_osc); + } + } + + data[M41T93_REG_SSEC] = 0; + data[M41T93_REG_ST_SEC] = bin2bcd(tm->tm_sec); + data[M41T93_REG_MIN] = bin2bcd(tm->tm_min); + data[M41T93_REG_CENT_HOUR] = bin2bcd(tm->tm_hour) | + ((tm->tm_year/100-1) << 6); + data[M41T93_REG_DAY] = bin2bcd(tm->tm_mday); + data[M41T93_REG_WDAY] = bin2bcd(tm->tm_wday + 1); + data[M41T93_REG_MON] = bin2bcd(tm->tm_mon + 1); + data[M41T93_REG_YEAR] = bin2bcd(tm->tm_year % 100); + + return regmap_bulk_write(m41t93->regmap, M41T93_REG_SSEC, buf, sizeof(buf)); +} + + +static int m41t93_get_time(struct device *dev, struct rtc_time *tm) +{ + struct m41t93_data *m41t93 = dev_get_drvdata(dev); + u8 buf[8]; + int century_after_1900; + int tmp; + int ret = 0; + + /* Check status of clock. Two states must be considered: + 1. halt bit (HT) is set: the clock is running but update of readout + registers has been disabled due to power failure. This is normal + case after poweron. Time is valid after resetting HT bit. + 2. oscillator fail bit (OF) is set: time is invalid. + */ + ret = regmap_read(m41t93->regmap, M41T93_REG_ALM_HOUR_HT, &tmp); + if (ret < 0) + return ret; + + if (tmp & M41T93_FLAG_HT) { + dev_dbg(dev, "HT bit is set, reenable clock update.\n"); + regmap_write(m41t93->regmap, M41T93_REG_ALM_HOUR_HT, + tmp & ~M41T93_FLAG_HT); + } + + ret = regmap_read(m41t93->regmap, M41T93_REG_FLAGS, &tmp); + if (ret < 0) + return ret; + + if (tmp & M41T93_FLAG_OF) { + ret = -EINVAL; + dev_warn(dev, "OF bit is set, write time to restart.\n"); + } + + if (tmp & M41T93_FLAG_BL) + dev_warn(dev, "BL bit is set, replace battery.\n"); + + /* read actual time/date */ + ret = regmap_bulk_read(m41t93->regmap, M41T93_REG_SSEC, buf, sizeof(buf)); + if (ret < 0) + return ret; + + tm->tm_sec = bcd2bin(buf[M41T93_REG_ST_SEC]); + tm->tm_min = bcd2bin(buf[M41T93_REG_MIN]); + tm->tm_hour = bcd2bin(buf[M41T93_REG_CENT_HOUR] & 0x3f); + tm->tm_mday = bcd2bin(buf[M41T93_REG_DAY]); + tm->tm_mon = bcd2bin(buf[M41T93_REG_MON]) - 1; + tm->tm_wday = bcd2bin(buf[M41T93_REG_WDAY] & 0x0f) - 1; + + century_after_1900 = (buf[M41T93_REG_CENT_HOUR] >> 6) + 1; + tm->tm_year = bcd2bin(buf[M41T93_REG_YEAR]) + century_after_1900 * 100; + + dev_dbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "read", tm->tm_sec, tm->tm_min, + tm->tm_hour, tm->tm_mday, + tm->tm_mon, tm->tm_year, tm->tm_wday); + + return ret; +} + +static int m41t93_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct m41t93_data *m41t93 = dev_get_drvdata(dev); + int ret; + unsigned int val; + u8 alarm_vals[5] = {0}; + + ret = regmap_bulk_write(m41t93->regmap, M41T93_REG_AL1_DATE, alarm_vals, 4); + if (ret) + return ret; + + /* Set alarm values */ + alarm_vals[0] = bin2bcd(alrm->time.tm_mon + 1) & 0x1f; + alarm_vals[1] = bin2bcd(alrm->time.tm_mday) & 0x3f; + alarm_vals[2] = bin2bcd(alrm->time.tm_hour) & 0x3f; + alarm_vals[3] = bin2bcd(alrm->time.tm_min) & 0x7f; + alarm_vals[4] = bin2bcd(alrm->time.tm_sec) & 0x7f; + + if (alrm->enabled) { + /* Enable alarm IRQ generation */ + alarm_vals[0] |= M41T93_BIT_A1IE | M41T93_BIT_ABE; + } + + /* Preserve SQWE bit */ + ret = regmap_read(m41t93->regmap, M41T93_REG_AL1_MONTH, &val); + if (ret) + return ret; + + alarm_vals[0] |= val & 0x40; + + ret = regmap_bulk_write(m41t93->regmap, M41T93_REG_AL1_MONTH, + alarm_vals, sizeof(alarm_vals)); + if (ret) + return ret; + + /* Device address pointer is now at FLAG register, move it to other location + * to finish setting alarm, as recommended by the datasheet. + * We do read of AL1_MONTH register to achieve this. + */ + ret = regmap_read(m41t93->regmap, M41T93_REG_AL1_MONTH, &val); + if (ret) + return ret; + + if (bcd2bin(val & 0x1f) == (alrm->time.tm_mon & 0x1f)) + dev_notice(dev, "Alarm set successfully\n"); + + return 0; +} + +static int m41t93_get_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct m41t93_data *m41t93 = dev_get_drvdata(dev); + int ret; + unsigned int val; + u8 alarm_vals[5] = {0}; + + ret = regmap_bulk_read(m41t93->regmap, M41T93_REG_AL1_MONTH, + alarm_vals, sizeof(alarm_vals)); + if (ret) + return ret; + + alrm->time.tm_mon = bcd2bin(alarm_vals[0] & 0x1f) - 1; + alrm->time.tm_mday = bcd2bin(alarm_vals[1] & 0x3f); + alrm->time.tm_hour = bcd2bin(alarm_vals[2] & 0x3f); + alrm->time.tm_min = bcd2bin(alarm_vals[3] & 0x7f); + alrm->time.tm_sec = bcd2bin(alarm_vals[4] & 0x7f); + + alrm->enabled = !!(alarm_vals[0] & M41T93_BIT_A1IE); + + ret = regmap_read(m41t93->regmap, M41T93_REG_FLAGS, &val); + if (ret) + return ret; + + alrm->pending = (val & M41T93_FLAG_AF1) && alrm->enabled; + + return 0; +} + +static int m41t93_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct m41t93_data *m41t93 = dev_get_drvdata(dev); + unsigned int val; + int ret; + + val = enabled ? M41T93_BIT_A1IE | M41T93_BIT_ABE : 0; + + ret = regmap_update_bits(m41t93->regmap, M41T93_REG_AL1_MONTH, + M41T93_BIT_A1IE | M41T93_BIT_ABE, val); + if (ret) + return ret; + + return 0; +} + + +static const struct rtc_class_ops m41t93_rtc_ops = { + .read_time = m41t93_get_time, + .set_time = m41t93_set_time, + .set_alarm = m41t93_set_alarm, + .read_alarm = m41t93_get_alarm, + .alarm_irq_enable = m41t93_alarm_irq_enable, +}; + +#ifdef CONFIG_COMMON_CLK +#define clk_sqw_to_m41t93_data(clk) \ + container_of(clk, struct m41t93_data, clks) + +/* m41t93 RTC clock output support */ +static unsigned long m41t93_clk_rates[] = { + 0, + 32768, /* RS3:RS0 = 0b0001 */ + 8192, + 4096, + 2048, + 1024, + 512, + 256, + 128, + 64, + 32, + 16, + 8, + 4, + 2, + 1, /* RS3:RS0 = 0b1111 */ +}; + +static unsigned long m41t93_clk_sqw_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + int ret; + unsigned int rate_id; + struct m41t93_data *m41t93 = clk_sqw_to_m41t93_data(hw); + + ret = regmap_read(m41t93->regmap, M41T93_REG_SQW, &rate_id); + if (ret) + return ret; + + rate_id &= M41T93_SQW_RS_MASK; + rate_id >>= M41T93_SQW_RS_SHIFT; + + return m41t93_clk_rates[rate_id]; +} + +static int m41t93_clk_sqw_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + int i; + + for (i = 1; i < ARRAY_SIZE(m41t93_clk_rates); i++) { + if (req->rate >= m41t93_clk_rates[i]) { + req->rate = m41t93_clk_rates[i]; + return 0; + } + } + + return 0; +} + +static int m41t93_clk_sqw_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int id, ret; + struct m41t93_data *m41t93 = clk_sqw_to_m41t93_data(hw); + + for (id = 0; id < ARRAY_SIZE(m41t93_clk_rates); id++) { + if (m41t93_clk_rates[id] == rate) + break; + } + + if (id >= ARRAY_SIZE(m41t93_clk_rates)) + return -EINVAL; + + ret = regmap_update_bits(m41t93->regmap, M41T93_REG_SQW, + M41T93_SQW_RS_MASK, id << M41T93_SQW_RS_SHIFT); + + return ret; +} + +static int m41t93_clk_sqw_prepare(struct clk_hw *hw) +{ + int ret; + struct m41t93_data *m41t93 = clk_sqw_to_m41t93_data(hw); + + ret = regmap_update_bits(m41t93->regmap, M41T93_REG_AL1_MONTH, + M41T93_BIT_SQWE, M41T93_BIT_SQWE); + + return ret; +} + +static void m41t93_clk_sqw_unprepare(struct clk_hw *hw) +{ + struct m41t93_data *m41t93 = clk_sqw_to_m41t93_data(hw); + + regmap_update_bits(m41t93->regmap, M41T93_REG_AL1_MONTH, + M41T93_BIT_SQWE, 0); +} + +static int m41t93_clk_sqw_is_prepared(struct clk_hw *hw) +{ + int ret; + struct m41t93_data *m41t93 = clk_sqw_to_m41t93_data(hw); + unsigned int status; + + ret = regmap_read(m41t93->regmap, M41T93_REG_AL1_MONTH, &status); + if (ret) + return ret; + + return !!(status & M41T93_BIT_SQWE); +} + +static const struct clk_ops m41t93_clk_sqw_ops = { + .prepare = m41t93_clk_sqw_prepare, + .unprepare = m41t93_clk_sqw_unprepare, + .is_prepared = m41t93_clk_sqw_is_prepared, + .recalc_rate = m41t93_clk_sqw_recalc_rate, + .set_rate = m41t93_clk_sqw_set_rate, + .determine_rate = m41t93_clk_sqw_determine_rate, +}; + +static int rtc_m41t93_clks_register(struct device *dev, struct m41t93_data *m41t93) +{ + struct device_node *node = dev->of_node; + struct clk *clk; + struct clk_init_data init = {0}; + + init.name = "m41t93_clk_sqw"; + init.ops = &m41t93_clk_sqw_ops; + + m41t93->clks.init = &init; + + /* Register the clock with CCF */ + clk = devm_clk_register(dev, &m41t93->clks); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + if (node) + of_clk_add_provider(node, of_clk_src_simple_get, clk); + + return 0; +} +#endif + +#ifdef CONFIG_WATCHDOG +static int m41t93_wdt_ping(struct watchdog_device *wdd) +{ + u8 resolution, mult; + u8 val = 0; + int ret; + struct m41t93_data *m41t93 = watchdog_get_drvdata(wdd); + + /* Resolution supported by hardware + * 0b00 : 1/16 seconds + * 0b01 : 1/4 second + * 0b10 : 1 second + * 0b11 : 4 seconds + */ + resolution = 0x2; /* hardcode resolution to 1s */ + mult = wdd->timeout; + val = resolution | (mult << M41T93_WDT_BMB_SHIFT & M41T93_WDT_BMB_MASK); + + ret = regmap_write_bits(m41t93->regmap, M41T93_REG_WATCHDOG, + M41T93_WDT_RB_MASK | M41T93_WDT_BMB_MASK, val); + + return ret; +} + +static int m41t93_wdt_start(struct watchdog_device *wdd) +{ + return m41t93_wdt_ping(wdd); +} + +static int m41t93_wdt_stop(struct watchdog_device *wdd) +{ + struct m41t93_data *m41t93 = watchdog_get_drvdata(wdd); + + /* Write 0 to watchdog register */ + return regmap_write_bits(m41t93->regmap, M41T93_REG_WATCHDOG, + M41T93_WDT_RB_MASK | M41T93_WDT_BMB_MASK, 0); +} + +static int m41t93_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int new_timeout) +{ + wdd->timeout = new_timeout; + + return 0; +} + +static const struct watchdog_info m41t93_wdt_info = { + .identity = "m41t93 rtc Watchdog", + .options = WDIOF_ALARMONLY | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, +}; + +static const struct watchdog_ops m41t93_watchdog_ops = { + .owner = THIS_MODULE, + .start = m41t93_wdt_start, + .stop = m41t93_wdt_stop, + .ping = m41t93_wdt_ping, + .set_timeout = m41t93_wdt_set_timeout, +}; + +static int m41t93_watchdog_register(struct device *dev, struct m41t93_data *m41t93) +{ + int ret; + + m41t93->wdd.parent = dev; + m41t93->wdd.info = &m41t93_wdt_info; + m41t93->wdd.ops = &m41t93_watchdog_ops; + m41t93->wdd.min_timeout = 0; + m41t93->wdd.max_timeout = 10; + m41t93->wdd.timeout = 3; /* Default timeout is 3 sec */ + m41t93->wdd.status = WATCHDOG_NOWAYOUT_INIT_STATUS; + + watchdog_set_drvdata(&m41t93->wdd, m41t93); + + ret = devm_watchdog_register_device(dev, &m41t93->wdd); + if (ret) { + dev_warn(dev, "Failed to register watchdog\n"); + return ret; + } + + /* Disable watchdog at start */ + ret = m41t93_wdt_stop(&m41t93->wdd); + + return ret; +} +#endif + +static struct spi_driver m41t93_driver; + +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .read_flag_mask = 0x00, + .write_flag_mask = 0x80, + .zero_flag_mask = true, +}; + +static int m41t93_probe(struct spi_device *spi) +{ + int res, ret; + struct m41t93_data *m41t93; + + spi->bits_per_word = 8; + spi_setup(spi); + + m41t93 = devm_kzalloc(&spi->dev, sizeof(struct m41t93_data), GFP_KERNEL); + + if (!m41t93) + return -ENOMEM; + + /* Set up regmap to access device registers*/ + m41t93->regmap = devm_regmap_init_spi(spi, ®map_config); + if (IS_ERR(m41t93->regmap)) { + dev_err(&spi->dev, "regmap init failure\n"); + return PTR_ERR(m41t93->regmap); + } + + ret = regmap_read(m41t93->regmap, M41T93_REG_WDAY, &res); + if (ret < 0) { + dev_err(&spi->dev, "IO error\n"); + return -EIO; + } + + if (res < 0 || (res & 0xf8) != 0) { + dev_err(&spi->dev, "not found 0x%x.\n", res); + return -ENODEV; + } + + spi_set_drvdata(spi, m41t93); + + m41t93->rtc = devm_rtc_device_register(&spi->dev, m41t93_driver.driver.name, + &m41t93_rtc_ops, THIS_MODULE); + if (IS_ERR(m41t93->rtc)) + return PTR_ERR(m41t93->rtc); + +#ifdef CONFIG_COMMON_CLK + ret = rtc_m41t93_clks_register(&spi->dev, m41t93); + if (ret) + dev_warn(&spi->dev, "Unable to register clock\n"); +#endif +#ifdef CONFIG_WATCHDOG + ret = m41t93_watchdog_register(&spi->dev, m41t93); + if (ret) + dev_warn(&spi->dev, "Unable to register watchdog\n"); +#endif + + return 0; +} + +static const struct of_device_id m41t93_dt_match[] = { + { .compatible = "st,m41t93" }, + { } +}; +MODULE_DEVICE_TABLE(of, m41t93_dt_match); + +static struct spi_driver m41t93_driver = { + .driver = { + .name = "rtc-m41t93", + .of_match_table = m41t93_dt_match, + }, + .probe = m41t93_probe, +}; + +module_spi_driver(m41t93_driver); + +MODULE_AUTHOR("Nikolaus Voss "); +MODULE_DESCRIPTION("Driver for ST M41T93 SPI RTC"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:rtc-m41t93"); diff --git a/drivers/rtc/rtc-m41t94.c b/drivers/rtc/rtc-m41t94.c new file mode 100644 index 000000000..6803b0273 --- /dev/null +++ b/drivers/rtc/rtc-m41t94.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for ST M41T94 SPI RTC + * + * Copyright (C) 2008 Kim B. Heino + */ + +#include +#include +#include +#include +#include +#include + +#define M41T94_REG_SECONDS 0x01 +#define M41T94_REG_MINUTES 0x02 +#define M41T94_REG_HOURS 0x03 +#define M41T94_REG_WDAY 0x04 +#define M41T94_REG_DAY 0x05 +#define M41T94_REG_MONTH 0x06 +#define M41T94_REG_YEAR 0x07 +#define M41T94_REG_HT 0x0c + +#define M41T94_BIT_HALT 0x40 +#define M41T94_BIT_STOP 0x80 +#define M41T94_BIT_CB 0x40 +#define M41T94_BIT_CEB 0x80 + +static int m41t94_set_time(struct device *dev, struct rtc_time *tm) +{ + struct spi_device *spi = to_spi_device(dev); + u8 buf[8]; /* write cmd + 7 registers */ + + dev_dbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "write", tm->tm_sec, tm->tm_min, + tm->tm_hour, tm->tm_mday, + tm->tm_mon, tm->tm_year, tm->tm_wday); + + buf[0] = 0x80 | M41T94_REG_SECONDS; /* write time + date */ + buf[M41T94_REG_SECONDS] = bin2bcd(tm->tm_sec); + buf[M41T94_REG_MINUTES] = bin2bcd(tm->tm_min); + buf[M41T94_REG_HOURS] = bin2bcd(tm->tm_hour); + buf[M41T94_REG_WDAY] = bin2bcd(tm->tm_wday + 1); + buf[M41T94_REG_DAY] = bin2bcd(tm->tm_mday); + buf[M41T94_REG_MONTH] = bin2bcd(tm->tm_mon + 1); + + buf[M41T94_REG_HOURS] |= M41T94_BIT_CEB; + if (tm->tm_year >= 100) + buf[M41T94_REG_HOURS] |= M41T94_BIT_CB; + buf[M41T94_REG_YEAR] = bin2bcd(tm->tm_year % 100); + + return spi_write(spi, buf, 8); +} + +static int m41t94_read_time(struct device *dev, struct rtc_time *tm) +{ + struct spi_device *spi = to_spi_device(dev); + u8 buf[2]; + int ret, hour; + + /* clear halt update bit */ + ret = spi_w8r8(spi, M41T94_REG_HT); + if (ret < 0) + return ret; + if (ret & M41T94_BIT_HALT) { + buf[0] = 0x80 | M41T94_REG_HT; + buf[1] = ret & ~M41T94_BIT_HALT; + spi_write(spi, buf, 2); + } + + /* clear stop bit */ + ret = spi_w8r8(spi, M41T94_REG_SECONDS); + if (ret < 0) + return ret; + if (ret & M41T94_BIT_STOP) { + buf[0] = 0x80 | M41T94_REG_SECONDS; + buf[1] = ret & ~M41T94_BIT_STOP; + spi_write(spi, buf, 2); + } + + tm->tm_sec = bcd2bin(spi_w8r8(spi, M41T94_REG_SECONDS)); + tm->tm_min = bcd2bin(spi_w8r8(spi, M41T94_REG_MINUTES)); + hour = spi_w8r8(spi, M41T94_REG_HOURS); + tm->tm_hour = bcd2bin(hour & 0x3f); + tm->tm_wday = bcd2bin(spi_w8r8(spi, M41T94_REG_WDAY)) - 1; + tm->tm_mday = bcd2bin(spi_w8r8(spi, M41T94_REG_DAY)); + tm->tm_mon = bcd2bin(spi_w8r8(spi, M41T94_REG_MONTH)) - 1; + tm->tm_year = bcd2bin(spi_w8r8(spi, M41T94_REG_YEAR)); + if ((hour & M41T94_BIT_CB) || !(hour & M41T94_BIT_CEB)) + tm->tm_year += 100; + + dev_dbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + "read", tm->tm_sec, tm->tm_min, + tm->tm_hour, tm->tm_mday, + tm->tm_mon, tm->tm_year, tm->tm_wday); + + return 0; +} + +static const struct rtc_class_ops m41t94_rtc_ops = { + .read_time = m41t94_read_time, + .set_time = m41t94_set_time, +}; + +static struct spi_driver m41t94_driver; + +static int m41t94_probe(struct spi_device *spi) +{ + struct rtc_device *rtc; + int res; + + spi->bits_per_word = 8; + spi_setup(spi); + + res = spi_w8r8(spi, M41T94_REG_SECONDS); + if (res < 0) { + dev_err(&spi->dev, "not found.\n"); + return res; + } + + rtc = devm_rtc_device_register(&spi->dev, m41t94_driver.driver.name, + &m41t94_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + spi_set_drvdata(spi, rtc); + + return 0; +} + +static struct spi_driver m41t94_driver = { + .driver = { + .name = "rtc-m41t94", + }, + .probe = m41t94_probe, +}; + +module_spi_driver(m41t94_driver); + +MODULE_AUTHOR("Kim B. Heino "); +MODULE_DESCRIPTION("Driver for ST M41T94 SPI RTC"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:rtc-m41t94"); diff --git a/drivers/rtc/rtc-m48t35.c b/drivers/rtc/rtc-m48t35.c new file mode 100644 index 000000000..92f19bf99 --- /dev/null +++ b/drivers/rtc/rtc-m48t35.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for the SGS-Thomson M48T35 Timekeeper RAM chip + * + * Copyright (C) 2000 Silicon Graphics, Inc. + * Written by Ulf Carlsson (ulfc@engr.sgi.com) + * + * Copyright (C) 2008 Thomas Bogendoerfer + * + * Based on code written by Paul Gortmaker. + */ + +#include +#include +#include +#include +#include +#include +#include + +struct m48t35_rtc { + u8 pad[0x7ff8]; /* starts at 0x7ff8 */ +#ifdef CONFIG_SGI_IP27 + u8 hour; + u8 min; + u8 sec; + u8 control; + u8 year; + u8 month; + u8 date; + u8 day; +#else + u8 control; + u8 sec; + u8 min; + u8 hour; + u8 day; + u8 date; + u8 month; + u8 year; +#endif +}; + +#define M48T35_RTC_SET 0x80 +#define M48T35_RTC_READ 0x40 + +struct m48t35_priv { + struct rtc_device *rtc; + struct m48t35_rtc __iomem *reg; + size_t size; + unsigned long baseaddr; + spinlock_t lock; +}; + +static int m48t35_read_time(struct device *dev, struct rtc_time *tm) +{ + struct m48t35_priv *priv = dev_get_drvdata(dev); + u8 control; + + /* + * Only the values that we read from the RTC are set. We leave + * tm_wday, tm_yday and tm_isdst untouched. Even though the + * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated + * by the RTC when initially set to a non-zero value. + */ + spin_lock_irq(&priv->lock); + control = readb(&priv->reg->control); + writeb(control | M48T35_RTC_READ, &priv->reg->control); + tm->tm_sec = readb(&priv->reg->sec); + tm->tm_min = readb(&priv->reg->min); + tm->tm_hour = readb(&priv->reg->hour); + tm->tm_mday = readb(&priv->reg->date); + tm->tm_mon = readb(&priv->reg->month); + tm->tm_year = readb(&priv->reg->year); + writeb(control, &priv->reg->control); + spin_unlock_irq(&priv->lock); + + tm->tm_sec = bcd2bin(tm->tm_sec); + tm->tm_min = bcd2bin(tm->tm_min); + tm->tm_hour = bcd2bin(tm->tm_hour); + tm->tm_mday = bcd2bin(tm->tm_mday); + tm->tm_mon = bcd2bin(tm->tm_mon); + tm->tm_year = bcd2bin(tm->tm_year); + + /* + * Account for differences between how the RTC uses the values + * and how they are defined in a struct rtc_time; + */ + tm->tm_year += 70; + if (tm->tm_year <= 69) + tm->tm_year += 100; + + tm->tm_mon--; + return 0; +} + +static int m48t35_set_time(struct device *dev, struct rtc_time *tm) +{ + struct m48t35_priv *priv = dev_get_drvdata(dev); + unsigned char mon, day, hrs, min, sec; + unsigned int yrs; + u8 control; + + yrs = tm->tm_year + 1900; + mon = tm->tm_mon + 1; /* tm_mon starts at zero */ + day = tm->tm_mday; + hrs = tm->tm_hour; + min = tm->tm_min; + sec = tm->tm_sec; + + if (yrs < 1970) + return -EINVAL; + + yrs -= 1970; + if (yrs > 255) /* They are unsigned */ + return -EINVAL; + + if (yrs > 169) + return -EINVAL; + + if (yrs >= 100) + yrs -= 100; + + sec = bin2bcd(sec); + min = bin2bcd(min); + hrs = bin2bcd(hrs); + day = bin2bcd(day); + mon = bin2bcd(mon); + yrs = bin2bcd(yrs); + + spin_lock_irq(&priv->lock); + control = readb(&priv->reg->control); + writeb(control | M48T35_RTC_SET, &priv->reg->control); + writeb(yrs, &priv->reg->year); + writeb(mon, &priv->reg->month); + writeb(day, &priv->reg->date); + writeb(hrs, &priv->reg->hour); + writeb(min, &priv->reg->min); + writeb(sec, &priv->reg->sec); + writeb(control, &priv->reg->control); + spin_unlock_irq(&priv->lock); + return 0; +} + +static const struct rtc_class_ops m48t35_ops = { + .read_time = m48t35_read_time, + .set_time = m48t35_set_time, +}; + +static int m48t35_probe(struct platform_device *pdev) +{ + struct resource *res; + struct m48t35_priv *priv; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + priv = devm_kzalloc(&pdev->dev, sizeof(struct m48t35_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->size = resource_size(res); + if (!devm_request_mem_region(&pdev->dev, res->start, priv->size, + pdev->name)) + return -EBUSY; + + priv->baseaddr = res->start; + priv->reg = devm_ioremap(&pdev->dev, priv->baseaddr, priv->size); + if (!priv->reg) + return -ENOMEM; + + spin_lock_init(&priv->lock); + + platform_set_drvdata(pdev, priv); + + priv->rtc = devm_rtc_device_register(&pdev->dev, "m48t35", + &m48t35_ops, THIS_MODULE); + return PTR_ERR_OR_ZERO(priv->rtc); +} + +static struct platform_driver m48t35_platform_driver = { + .driver = { + .name = "rtc-m48t35", + }, + .probe = m48t35_probe, +}; + +module_platform_driver(m48t35_platform_driver); + +MODULE_AUTHOR("Thomas Bogendoerfer "); +MODULE_DESCRIPTION("M48T35 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rtc-m48t35"); diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c new file mode 100644 index 000000000..4e608bc8b --- /dev/null +++ b/drivers/rtc/rtc-m48t59.c @@ -0,0 +1,472 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ST M48T59 RTC driver + * + * Copyright (c) 2007 Wind River Systems, Inc. + * + * Author: Mark Zhan + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef NO_IRQ +#define NO_IRQ (-1) +#endif + +#define M48T59_READ(reg) (pdata->read_byte(dev, pdata->offset + reg)) +#define M48T59_WRITE(val, reg) \ + (pdata->write_byte(dev, pdata->offset + reg, val)) + +#define M48T59_SET_BITS(mask, reg) \ + M48T59_WRITE((M48T59_READ(reg) | (mask)), (reg)) +#define M48T59_CLEAR_BITS(mask, reg) \ + M48T59_WRITE((M48T59_READ(reg) & ~(mask)), (reg)) + +struct m48t59_private { + void __iomem *ioaddr; + int irq; + struct rtc_device *rtc; + spinlock_t lock; /* serialize the NVRAM and RTC access */ +}; + +/* + * This is the generic access method when the chip is memory-mapped + */ +static void +m48t59_mem_writeb(struct device *dev, u32 ofs, u8 val) +{ + struct m48t59_private *m48t59 = dev_get_drvdata(dev); + + writeb(val, m48t59->ioaddr+ofs); +} + +static u8 +m48t59_mem_readb(struct device *dev, u32 ofs) +{ + struct m48t59_private *m48t59 = dev_get_drvdata(dev); + + return readb(m48t59->ioaddr+ofs); +} + +/* + * NOTE: M48T59 only uses BCD mode + */ +static int m48t59_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct m48t59_plat_data *pdata = dev_get_platdata(dev); + struct m48t59_private *m48t59 = dev_get_drvdata(dev); + unsigned long flags; + u8 val; + + spin_lock_irqsave(&m48t59->lock, flags); + /* Issue the READ command */ + M48T59_SET_BITS(M48T59_CNTL_READ, M48T59_CNTL); + + tm->tm_year = bcd2bin(M48T59_READ(M48T59_YEAR)) + pdata->yy_offset; + /* tm_mon is 0-11 */ + tm->tm_mon = bcd2bin(M48T59_READ(M48T59_MONTH)) - 1; + tm->tm_mday = bcd2bin(M48T59_READ(M48T59_MDAY)); + + val = M48T59_READ(M48T59_WDAY); + if ((pdata->type == M48T59RTC_TYPE_M48T59) && + (val & M48T59_WDAY_CEB) && (val & M48T59_WDAY_CB)) { + dev_dbg(dev, "Century bit is enabled\n"); + tm->tm_year += 100; /* one century */ + } + + tm->tm_wday = bcd2bin(val & 0x07); + tm->tm_hour = bcd2bin(M48T59_READ(M48T59_HOUR) & 0x3F); + tm->tm_min = bcd2bin(M48T59_READ(M48T59_MIN) & 0x7F); + tm->tm_sec = bcd2bin(M48T59_READ(M48T59_SEC) & 0x7F); + + /* Clear the READ bit */ + M48T59_CLEAR_BITS(M48T59_CNTL_READ, M48T59_CNTL); + spin_unlock_irqrestore(&m48t59->lock, flags); + + dev_dbg(dev, "RTC read time %ptR\n", tm); + return 0; +} + +static int m48t59_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct m48t59_plat_data *pdata = dev_get_platdata(dev); + struct m48t59_private *m48t59 = dev_get_drvdata(dev); + unsigned long flags; + u8 val = 0; + int year = tm->tm_year - pdata->yy_offset; + + dev_dbg(dev, "RTC set time %04d-%02d-%02d %02d/%02d/%02d\n", + year + 1900, tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + if (year < 0) + return -EINVAL; + + spin_lock_irqsave(&m48t59->lock, flags); + /* Issue the WRITE command */ + M48T59_SET_BITS(M48T59_CNTL_WRITE, M48T59_CNTL); + + M48T59_WRITE((bin2bcd(tm->tm_sec) & 0x7F), M48T59_SEC); + M48T59_WRITE((bin2bcd(tm->tm_min) & 0x7F), M48T59_MIN); + M48T59_WRITE((bin2bcd(tm->tm_hour) & 0x3F), M48T59_HOUR); + M48T59_WRITE((bin2bcd(tm->tm_mday) & 0x3F), M48T59_MDAY); + /* tm_mon is 0-11 */ + M48T59_WRITE((bin2bcd(tm->tm_mon + 1) & 0x1F), M48T59_MONTH); + M48T59_WRITE(bin2bcd(year % 100), M48T59_YEAR); + + if (pdata->type == M48T59RTC_TYPE_M48T59 && (year >= 100)) + val = (M48T59_WDAY_CEB | M48T59_WDAY_CB); + val |= (bin2bcd(tm->tm_wday) & 0x07); + M48T59_WRITE(val, M48T59_WDAY); + + /* Clear the WRITE bit */ + M48T59_CLEAR_BITS(M48T59_CNTL_WRITE, M48T59_CNTL); + spin_unlock_irqrestore(&m48t59->lock, flags); + return 0; +} + +/* + * Read alarm time and date in RTC + */ +static int m48t59_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct m48t59_plat_data *pdata = dev_get_platdata(dev); + struct m48t59_private *m48t59 = dev_get_drvdata(dev); + struct rtc_time *tm = &alrm->time; + unsigned long flags; + u8 val; + + /* If no irq, we don't support ALARM */ + if (m48t59->irq == NO_IRQ) + return -EIO; + + spin_lock_irqsave(&m48t59->lock, flags); + /* Issue the READ command */ + M48T59_SET_BITS(M48T59_CNTL_READ, M48T59_CNTL); + + tm->tm_year = bcd2bin(M48T59_READ(M48T59_YEAR)) + pdata->yy_offset; + /* tm_mon is 0-11 */ + tm->tm_mon = bcd2bin(M48T59_READ(M48T59_MONTH)) - 1; + + val = M48T59_READ(M48T59_WDAY); + if ((val & M48T59_WDAY_CEB) && (val & M48T59_WDAY_CB)) + tm->tm_year += 100; /* one century */ + + tm->tm_mday = bcd2bin(M48T59_READ(M48T59_ALARM_DATE)); + tm->tm_hour = bcd2bin(M48T59_READ(M48T59_ALARM_HOUR)); + tm->tm_min = bcd2bin(M48T59_READ(M48T59_ALARM_MIN)); + tm->tm_sec = bcd2bin(M48T59_READ(M48T59_ALARM_SEC)); + + /* Clear the READ bit */ + M48T59_CLEAR_BITS(M48T59_CNTL_READ, M48T59_CNTL); + spin_unlock_irqrestore(&m48t59->lock, flags); + + dev_dbg(dev, "RTC read alarm time %ptR\n", tm); + return rtc_valid_tm(tm); +} + +/* + * Set alarm time and date in RTC + */ +static int m48t59_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct m48t59_plat_data *pdata = dev_get_platdata(dev); + struct m48t59_private *m48t59 = dev_get_drvdata(dev); + struct rtc_time *tm = &alrm->time; + u8 mday, hour, min, sec; + unsigned long flags; + int year = tm->tm_year - pdata->yy_offset; + + /* If no irq, we don't support ALARM */ + if (m48t59->irq == NO_IRQ) + return -EIO; + + if (year < 0) + return -EINVAL; + + /* + * 0xff means "always match" + */ + mday = tm->tm_mday; + mday = (mday >= 1 && mday <= 31) ? bin2bcd(mday) : 0xff; + if (mday == 0xff) + mday = M48T59_READ(M48T59_MDAY); + + hour = tm->tm_hour; + hour = (hour < 24) ? bin2bcd(hour) : 0x00; + + min = tm->tm_min; + min = (min < 60) ? bin2bcd(min) : 0x00; + + sec = tm->tm_sec; + sec = (sec < 60) ? bin2bcd(sec) : 0x00; + + spin_lock_irqsave(&m48t59->lock, flags); + /* Issue the WRITE command */ + M48T59_SET_BITS(M48T59_CNTL_WRITE, M48T59_CNTL); + + M48T59_WRITE(mday, M48T59_ALARM_DATE); + M48T59_WRITE(hour, M48T59_ALARM_HOUR); + M48T59_WRITE(min, M48T59_ALARM_MIN); + M48T59_WRITE(sec, M48T59_ALARM_SEC); + + /* Clear the WRITE bit */ + M48T59_CLEAR_BITS(M48T59_CNTL_WRITE, M48T59_CNTL); + spin_unlock_irqrestore(&m48t59->lock, flags); + + dev_dbg(dev, "RTC set alarm time %04d-%02d-%02d %02d/%02d/%02d\n", + year + 1900, tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + return 0; +} + +/* + * Handle commands from user-space + */ +static int m48t59_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct m48t59_plat_data *pdata = dev_get_platdata(dev); + struct m48t59_private *m48t59 = dev_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&m48t59->lock, flags); + if (enabled) + M48T59_WRITE(M48T59_INTR_AFE, M48T59_INTR); + else + M48T59_WRITE(0x00, M48T59_INTR); + spin_unlock_irqrestore(&m48t59->lock, flags); + + return 0; +} + +static int m48t59_rtc_proc(struct device *dev, struct seq_file *seq) +{ + struct m48t59_plat_data *pdata = dev_get_platdata(dev); + struct m48t59_private *m48t59 = dev_get_drvdata(dev); + unsigned long flags; + u8 val; + + spin_lock_irqsave(&m48t59->lock, flags); + val = M48T59_READ(M48T59_FLAGS); + spin_unlock_irqrestore(&m48t59->lock, flags); + + seq_printf(seq, "battery\t\t: %s\n", + (val & M48T59_FLAGS_BF) ? "low" : "normal"); + return 0; +} + +/* + * IRQ handler for the RTC + */ +static irqreturn_t m48t59_rtc_interrupt(int irq, void *dev_id) +{ + struct device *dev = (struct device *)dev_id; + struct m48t59_plat_data *pdata = dev_get_platdata(dev); + struct m48t59_private *m48t59 = dev_get_drvdata(dev); + u8 event; + + spin_lock(&m48t59->lock); + event = M48T59_READ(M48T59_FLAGS); + spin_unlock(&m48t59->lock); + + if (event & M48T59_FLAGS_AF) { + rtc_update_irq(m48t59->rtc, 1, (RTC_AF | RTC_IRQF)); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static const struct rtc_class_ops m48t59_rtc_ops = { + .read_time = m48t59_rtc_read_time, + .set_time = m48t59_rtc_set_time, + .read_alarm = m48t59_rtc_readalarm, + .set_alarm = m48t59_rtc_setalarm, + .proc = m48t59_rtc_proc, + .alarm_irq_enable = m48t59_rtc_alarm_irq_enable, +}; + +static int m48t59_nvram_read(void *priv, unsigned int offset, void *val, + size_t size) +{ + struct platform_device *pdev = priv; + struct device *dev = &pdev->dev; + struct m48t59_plat_data *pdata = dev_get_platdata(&pdev->dev); + struct m48t59_private *m48t59 = platform_get_drvdata(pdev); + ssize_t cnt = 0; + unsigned long flags; + u8 *buf = val; + + spin_lock_irqsave(&m48t59->lock, flags); + + for (; cnt < size; cnt++) + *buf++ = M48T59_READ(cnt); + + spin_unlock_irqrestore(&m48t59->lock, flags); + + return 0; +} + +static int m48t59_nvram_write(void *priv, unsigned int offset, void *val, + size_t size) +{ + struct platform_device *pdev = priv; + struct device *dev = &pdev->dev; + struct m48t59_plat_data *pdata = dev_get_platdata(&pdev->dev); + struct m48t59_private *m48t59 = platform_get_drvdata(pdev); + ssize_t cnt = 0; + unsigned long flags; + u8 *buf = val; + + spin_lock_irqsave(&m48t59->lock, flags); + + for (; cnt < size; cnt++) + M48T59_WRITE(*buf++, cnt); + + spin_unlock_irqrestore(&m48t59->lock, flags); + + return 0; +} + +static int m48t59_rtc_probe(struct platform_device *pdev) +{ + struct m48t59_plat_data *pdata = dev_get_platdata(&pdev->dev); + struct m48t59_private *m48t59 = NULL; + struct resource *res; + int ret = -ENOMEM; + struct nvmem_config nvmem_cfg = { + .name = "m48t59-", + .word_size = 1, + .stride = 1, + .reg_read = m48t59_nvram_read, + .reg_write = m48t59_nvram_write, + .priv = pdev, + }; + + /* This chip could be memory-mapped or I/O-mapped */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) + return -EINVAL; + } + + if (res->flags & IORESOURCE_IO) { + /* If we are I/O-mapped, the platform should provide + * the operations accessing chip registers. + */ + if (!pdata || !pdata->write_byte || !pdata->read_byte) + return -EINVAL; + } else if (res->flags & IORESOURCE_MEM) { + /* we are memory-mapped */ + if (!pdata) { + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + /* Ensure we only kmalloc platform data once */ + pdev->dev.platform_data = pdata; + } + if (!pdata->type) + pdata->type = M48T59RTC_TYPE_M48T59; + + /* Try to use the generic memory read/write ops */ + if (!pdata->write_byte) + pdata->write_byte = m48t59_mem_writeb; + if (!pdata->read_byte) + pdata->read_byte = m48t59_mem_readb; + } + + m48t59 = devm_kzalloc(&pdev->dev, sizeof(*m48t59), GFP_KERNEL); + if (!m48t59) + return -ENOMEM; + + m48t59->ioaddr = pdata->ioaddr; + + if (!m48t59->ioaddr) { + /* ioaddr not mapped externally */ + m48t59->ioaddr = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!m48t59->ioaddr) + return ret; + } + + /* Try to get irq number. We also can work in + * the mode without IRQ. + */ + m48t59->irq = platform_get_irq_optional(pdev, 0); + if (m48t59->irq <= 0) + m48t59->irq = NO_IRQ; + + if (m48t59->irq != NO_IRQ) { + ret = devm_request_irq(&pdev->dev, m48t59->irq, + m48t59_rtc_interrupt, IRQF_SHARED, + "rtc-m48t59", &pdev->dev); + if (ret) + return ret; + } + + m48t59->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(m48t59->rtc)) + return PTR_ERR(m48t59->rtc); + + switch (pdata->type) { + case M48T59RTC_TYPE_M48T59: + pdata->offset = 0x1ff0; + break; + case M48T59RTC_TYPE_M48T02: + clear_bit(RTC_FEATURE_ALARM, m48t59->rtc->features); + pdata->offset = 0x7f0; + break; + case M48T59RTC_TYPE_M48T08: + clear_bit(RTC_FEATURE_ALARM, m48t59->rtc->features); + pdata->offset = 0x1ff0; + break; + default: + dev_err(&pdev->dev, "Unknown RTC type\n"); + return -ENODEV; + } + + spin_lock_init(&m48t59->lock); + platform_set_drvdata(pdev, m48t59); + + m48t59->rtc->ops = &m48t59_rtc_ops; + m48t59->rtc->range_min = RTC_TIMESTAMP_BEGIN_1900; + m48t59->rtc->range_max = RTC_TIMESTAMP_END_2099; + + nvmem_cfg.size = pdata->offset; + ret = devm_rtc_nvmem_register(m48t59->rtc, &nvmem_cfg); + if (ret) + return ret; + + ret = devm_rtc_register_device(m48t59->rtc); + if (ret) + return ret; + + return 0; +} + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:rtc-m48t59"); + +static struct platform_driver m48t59_rtc_driver = { + .driver = { + .name = "rtc-m48t59", + }, + .probe = m48t59_rtc_probe, +}; + +module_platform_driver(m48t59_rtc_driver); + +MODULE_AUTHOR("Mark Zhan "); +MODULE_DESCRIPTION("M48T59/M48T02/M48T08 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-m48t86.c b/drivers/rtc/rtc-m48t86.c new file mode 100644 index 000000000..db3bdb171 --- /dev/null +++ b/drivers/rtc/rtc-m48t86.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ST M48T86 / Dallas DS12887 RTC driver + * Copyright (c) 2006 Tower Technologies + * + * Author: Alessandro Zummo + * + * This drivers only supports the clock running in BCD and 24H mode. + * If it will be ever adapted to binary and 12H mode, care must be taken + * to not introduce bugs. + */ + +#include +#include +#include +#include +#include + +#define M48T86_SEC 0x00 +#define M48T86_SECALRM 0x01 +#define M48T86_MIN 0x02 +#define M48T86_MINALRM 0x03 +#define M48T86_HOUR 0x04 +#define M48T86_HOURALRM 0x05 +#define M48T86_DOW 0x06 /* 1 = sunday */ +#define M48T86_DOM 0x07 +#define M48T86_MONTH 0x08 /* 1 - 12 */ +#define M48T86_YEAR 0x09 /* 0 - 99 */ +#define M48T86_A 0x0a +#define M48T86_B 0x0b +#define M48T86_B_SET BIT(7) +#define M48T86_B_DM BIT(2) +#define M48T86_B_H24 BIT(1) +#define M48T86_C 0x0c +#define M48T86_D 0x0d +#define M48T86_D_VRT BIT(7) +#define M48T86_NVRAM(x) (0x0e + (x)) +#define M48T86_NVRAM_LEN 114 + +struct m48t86_rtc_info { + void __iomem *index_reg; + void __iomem *data_reg; +}; + +static unsigned char m48t86_readb(struct device *dev, unsigned long addr) +{ + struct m48t86_rtc_info *info = dev_get_drvdata(dev); + unsigned char value; + + writeb(addr, info->index_reg); + value = readb(info->data_reg); + + return value; +} + +static void m48t86_writeb(struct device *dev, + unsigned char value, unsigned long addr) +{ + struct m48t86_rtc_info *info = dev_get_drvdata(dev); + + writeb(addr, info->index_reg); + writeb(value, info->data_reg); +} + +static int m48t86_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char reg; + + reg = m48t86_readb(dev, M48T86_B); + + if (reg & M48T86_B_DM) { + /* data (binary) mode */ + tm->tm_sec = m48t86_readb(dev, M48T86_SEC); + tm->tm_min = m48t86_readb(dev, M48T86_MIN); + tm->tm_hour = m48t86_readb(dev, M48T86_HOUR) & 0x3f; + tm->tm_mday = m48t86_readb(dev, M48T86_DOM); + /* tm_mon is 0-11 */ + tm->tm_mon = m48t86_readb(dev, M48T86_MONTH) - 1; + tm->tm_year = m48t86_readb(dev, M48T86_YEAR) + 100; + tm->tm_wday = m48t86_readb(dev, M48T86_DOW); + } else { + /* bcd mode */ + tm->tm_sec = bcd2bin(m48t86_readb(dev, M48T86_SEC)); + tm->tm_min = bcd2bin(m48t86_readb(dev, M48T86_MIN)); + tm->tm_hour = bcd2bin(m48t86_readb(dev, M48T86_HOUR) & + 0x3f); + tm->tm_mday = bcd2bin(m48t86_readb(dev, M48T86_DOM)); + /* tm_mon is 0-11 */ + tm->tm_mon = bcd2bin(m48t86_readb(dev, M48T86_MONTH)) - 1; + tm->tm_year = bcd2bin(m48t86_readb(dev, M48T86_YEAR)) + 100; + tm->tm_wday = bcd2bin(m48t86_readb(dev, M48T86_DOW)); + } + + /* correct the hour if the clock is in 12h mode */ + if (!(reg & M48T86_B_H24)) + if (m48t86_readb(dev, M48T86_HOUR) & 0x80) + tm->tm_hour += 12; + + return 0; +} + +static int m48t86_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char reg; + + reg = m48t86_readb(dev, M48T86_B); + + /* update flag and 24h mode */ + reg |= M48T86_B_SET | M48T86_B_H24; + m48t86_writeb(dev, reg, M48T86_B); + + if (reg & M48T86_B_DM) { + /* data (binary) mode */ + m48t86_writeb(dev, tm->tm_sec, M48T86_SEC); + m48t86_writeb(dev, tm->tm_min, M48T86_MIN); + m48t86_writeb(dev, tm->tm_hour, M48T86_HOUR); + m48t86_writeb(dev, tm->tm_mday, M48T86_DOM); + m48t86_writeb(dev, tm->tm_mon + 1, M48T86_MONTH); + m48t86_writeb(dev, tm->tm_year % 100, M48T86_YEAR); + m48t86_writeb(dev, tm->tm_wday, M48T86_DOW); + } else { + /* bcd mode */ + m48t86_writeb(dev, bin2bcd(tm->tm_sec), M48T86_SEC); + m48t86_writeb(dev, bin2bcd(tm->tm_min), M48T86_MIN); + m48t86_writeb(dev, bin2bcd(tm->tm_hour), M48T86_HOUR); + m48t86_writeb(dev, bin2bcd(tm->tm_mday), M48T86_DOM); + m48t86_writeb(dev, bin2bcd(tm->tm_mon + 1), M48T86_MONTH); + m48t86_writeb(dev, bin2bcd(tm->tm_year % 100), M48T86_YEAR); + m48t86_writeb(dev, bin2bcd(tm->tm_wday), M48T86_DOW); + } + + /* update ended */ + reg &= ~M48T86_B_SET; + m48t86_writeb(dev, reg, M48T86_B); + + return 0; +} + +static int m48t86_rtc_proc(struct device *dev, struct seq_file *seq) +{ + unsigned char reg; + + reg = m48t86_readb(dev, M48T86_B); + + seq_printf(seq, "mode\t\t: %s\n", + (reg & M48T86_B_DM) ? "binary" : "bcd"); + + reg = m48t86_readb(dev, M48T86_D); + + seq_printf(seq, "battery\t\t: %s\n", + (reg & M48T86_D_VRT) ? "ok" : "exhausted"); + + return 0; +} + +static const struct rtc_class_ops m48t86_rtc_ops = { + .read_time = m48t86_rtc_read_time, + .set_time = m48t86_rtc_set_time, + .proc = m48t86_rtc_proc, +}; + +static int m48t86_nvram_read(void *priv, unsigned int off, void *buf, + size_t count) +{ + struct device *dev = priv; + unsigned int i; + + for (i = 0; i < count; i++) + ((u8 *)buf)[i] = m48t86_readb(dev, M48T86_NVRAM(off + i)); + + return 0; +} + +static int m48t86_nvram_write(void *priv, unsigned int off, void *buf, + size_t count) +{ + struct device *dev = priv; + unsigned int i; + + for (i = 0; i < count; i++) + m48t86_writeb(dev, ((u8 *)buf)[i], M48T86_NVRAM(off + i)); + + return 0; +} + +/* + * The RTC is an optional feature at purchase time on some Technologic Systems + * boards. Verify that it actually exists by checking if the last two bytes + * of the NVRAM can be changed. + * + * This is based on the method used in their rtc7800.c example. + */ +static bool m48t86_verify_chip(struct platform_device *pdev) +{ + unsigned int offset0 = M48T86_NVRAM(M48T86_NVRAM_LEN - 2); + unsigned int offset1 = M48T86_NVRAM(M48T86_NVRAM_LEN - 1); + unsigned char tmp0, tmp1; + + tmp0 = m48t86_readb(&pdev->dev, offset0); + tmp1 = m48t86_readb(&pdev->dev, offset1); + + m48t86_writeb(&pdev->dev, 0x00, offset0); + m48t86_writeb(&pdev->dev, 0x55, offset1); + if (m48t86_readb(&pdev->dev, offset1) == 0x55) { + m48t86_writeb(&pdev->dev, 0xaa, offset1); + if (m48t86_readb(&pdev->dev, offset1) == 0xaa && + m48t86_readb(&pdev->dev, offset0) == 0x00) { + m48t86_writeb(&pdev->dev, tmp0, offset0); + m48t86_writeb(&pdev->dev, tmp1, offset1); + + return true; + } + } + return false; +} + +static int m48t86_rtc_probe(struct platform_device *pdev) +{ + struct m48t86_rtc_info *info; + struct rtc_device *rtc; + unsigned char reg; + int err; + struct nvmem_config m48t86_nvmem_cfg = { + .name = "m48t86_nvram", + .word_size = 1, + .stride = 1, + .size = M48T86_NVRAM_LEN, + .reg_read = m48t86_nvram_read, + .reg_write = m48t86_nvram_write, + .priv = &pdev->dev, + }; + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->index_reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(info->index_reg)) + return PTR_ERR(info->index_reg); + + info->data_reg = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(info->data_reg)) + return PTR_ERR(info->data_reg); + + dev_set_drvdata(&pdev->dev, info); + + if (!m48t86_verify_chip(pdev)) { + dev_info(&pdev->dev, "RTC not present\n"); + return -ENODEV; + } + + rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + rtc->ops = &m48t86_rtc_ops; + + err = devm_rtc_register_device(rtc); + if (err) + return err; + + devm_rtc_nvmem_register(rtc, &m48t86_nvmem_cfg); + + /* read battery status */ + reg = m48t86_readb(&pdev->dev, M48T86_D); + dev_info(&pdev->dev, "battery %s\n", + (reg & M48T86_D_VRT) ? "ok" : "exhausted"); + + return 0; +} + +static const struct of_device_id m48t86_rtc_of_ids[] = { + { .compatible = "st,m48t86" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, m48t86_rtc_of_ids); + +static struct platform_driver m48t86_rtc_platform_driver = { + .driver = { + .name = "rtc-m48t86", + .of_match_table = m48t86_rtc_of_ids, + }, + .probe = m48t86_rtc_probe, +}; + +module_platform_driver(m48t86_rtc_platform_driver); + +MODULE_AUTHOR("Alessandro Zummo "); +MODULE_DESCRIPTION("M48T86 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rtc-m48t86"); diff --git a/drivers/rtc/rtc-ma35d1.c b/drivers/rtc/rtc-ma35d1.c new file mode 100644 index 000000000..cfcfc2806 --- /dev/null +++ b/drivers/rtc/rtc-ma35d1.c @@ -0,0 +1,304 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RTC driver for Nuvoton MA35D1 + * + * Copyright (C) 2023 Nuvoton Technology Corp. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* MA35D1 RTC Control Registers */ +#define MA35_REG_RTC_INIT 0x00 +#define MA35_REG_RTC_SINFASTS 0x04 +#define MA35_REG_RTC_FREQADJ 0x08 +#define MA35_REG_RTC_TIME 0x0c +#define MA35_REG_RTC_CAL 0x10 +#define MA35_REG_RTC_CLKFMT 0x14 +#define MA35_REG_RTC_WEEKDAY 0x18 +#define MA35_REG_RTC_TALM 0x1c +#define MA35_REG_RTC_CALM 0x20 +#define MA35_REG_RTC_LEAPYEAR 0x24 +#define MA35_REG_RTC_INTEN 0x28 +#define MA35_REG_RTC_INTSTS 0x2c + +/* register MA35_REG_RTC_INIT */ +#define RTC_INIT_ACTIVE BIT(0) +#define RTC_INIT_MAGIC_CODE 0xa5eb1357 + +/* register MA35_REG_RTC_CLKFMT */ +#define RTC_CLKFMT_24HEN BIT(0) +#define RTC_CLKFMT_DCOMPEN BIT(16) + +/* register MA35_REG_RTC_INTEN */ +#define RTC_INTEN_ALMIEN BIT(0) +#define RTC_INTEN_UIEN BIT(1) +#define RTC_INTEN_CLKFIEN BIT(24) +#define RTC_INTEN_CLKSTIEN BIT(25) + +/* register MA35_REG_RTC_INTSTS */ +#define RTC_INTSTS_ALMIF BIT(0) +#define RTC_INTSTS_UIF BIT(1) +#define RTC_INTSTS_CLKFIF BIT(24) +#define RTC_INTSTS_CLKSTIF BIT(25) + +#define RTC_INIT_TIMEOUT 250 + +struct ma35_rtc { + int irq_num; + void __iomem *rtc_reg; + struct rtc_device *rtcdev; +}; + +static u32 rtc_reg_read(struct ma35_rtc *p, u32 offset) +{ + return __raw_readl(p->rtc_reg + offset); +} + +static inline void rtc_reg_write(struct ma35_rtc *p, u32 offset, u32 value) +{ + __raw_writel(value, p->rtc_reg + offset); +} + +static irqreturn_t ma35d1_rtc_interrupt(int irq, void *data) +{ + struct ma35_rtc *rtc = (struct ma35_rtc *)data; + unsigned long events = 0, rtc_irq; + + rtc_irq = rtc_reg_read(rtc, MA35_REG_RTC_INTSTS); + + if (rtc_irq & RTC_INTSTS_ALMIF) { + rtc_reg_write(rtc, MA35_REG_RTC_INTSTS, RTC_INTSTS_ALMIF); + events |= RTC_AF | RTC_IRQF; + } + + rtc_update_irq(rtc->rtcdev, 1, events); + + return IRQ_HANDLED; +} + +static int ma35d1_rtc_init(struct ma35_rtc *rtc, u32 ms_timeout) +{ + const unsigned long timeout = jiffies + msecs_to_jiffies(ms_timeout); + + do { + if (rtc_reg_read(rtc, MA35_REG_RTC_INIT) & RTC_INIT_ACTIVE) + return 0; + + rtc_reg_write(rtc, MA35_REG_RTC_INIT, RTC_INIT_MAGIC_CODE); + + mdelay(1); + + } while (time_before(jiffies, timeout)); + + return -ETIMEDOUT; +} + +static int ma35d1_alarm_irq_enable(struct device *dev, u32 enabled) +{ + struct ma35_rtc *rtc = dev_get_drvdata(dev); + u32 reg_ien; + + reg_ien = rtc_reg_read(rtc, MA35_REG_RTC_INTEN); + + if (enabled) + rtc_reg_write(rtc, MA35_REG_RTC_INTEN, reg_ien | RTC_INTEN_ALMIEN); + else + rtc_reg_write(rtc, MA35_REG_RTC_INTEN, reg_ien & ~RTC_INTEN_ALMIEN); + + return 0; +} + +static int ma35d1_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct ma35_rtc *rtc = dev_get_drvdata(dev); + u32 time, cal, wday; + + do { + time = rtc_reg_read(rtc, MA35_REG_RTC_TIME); + cal = rtc_reg_read(rtc, MA35_REG_RTC_CAL); + wday = rtc_reg_read(rtc, MA35_REG_RTC_WEEKDAY); + } while (time != rtc_reg_read(rtc, MA35_REG_RTC_TIME) || + cal != rtc_reg_read(rtc, MA35_REG_RTC_CAL)); + + tm->tm_mday = bcd2bin(cal >> 0); + tm->tm_wday = wday; + tm->tm_mon = bcd2bin(cal >> 8); + tm->tm_mon = tm->tm_mon - 1; + tm->tm_year = bcd2bin(cal >> 16) + 100; + + tm->tm_sec = bcd2bin(time >> 0); + tm->tm_min = bcd2bin(time >> 8); + tm->tm_hour = bcd2bin(time >> 16); + + return rtc_valid_tm(tm); +} + +static int ma35d1_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct ma35_rtc *rtc = dev_get_drvdata(dev); + u32 val; + + val = bin2bcd(tm->tm_mday) << 0 | bin2bcd(tm->tm_mon + 1) << 8 | + bin2bcd(tm->tm_year - 100) << 16; + rtc_reg_write(rtc, MA35_REG_RTC_CAL, val); + + val = bin2bcd(tm->tm_sec) << 0 | bin2bcd(tm->tm_min) << 8 | + bin2bcd(tm->tm_hour) << 16; + rtc_reg_write(rtc, MA35_REG_RTC_TIME, val); + + val = tm->tm_wday; + rtc_reg_write(rtc, MA35_REG_RTC_WEEKDAY, val); + + return 0; +} + +static int ma35d1_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct ma35_rtc *rtc = dev_get_drvdata(dev); + u32 talm, calm; + + talm = rtc_reg_read(rtc, MA35_REG_RTC_TALM); + calm = rtc_reg_read(rtc, MA35_REG_RTC_CALM); + + alrm->time.tm_mday = bcd2bin(calm >> 0); + alrm->time.tm_mon = bcd2bin(calm >> 8); + alrm->time.tm_mon = alrm->time.tm_mon - 1; + + alrm->time.tm_year = bcd2bin(calm >> 16) + 100; + + alrm->time.tm_sec = bcd2bin(talm >> 0); + alrm->time.tm_min = bcd2bin(talm >> 8); + alrm->time.tm_hour = bcd2bin(talm >> 16); + + return rtc_valid_tm(&alrm->time); +} + +static int ma35d1_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct ma35_rtc *rtc = dev_get_drvdata(dev); + unsigned long val; + + val = bin2bcd(alrm->time.tm_mday) << 0 | bin2bcd(alrm->time.tm_mon + 1) << 8 | + bin2bcd(alrm->time.tm_year - 100) << 16; + rtc_reg_write(rtc, MA35_REG_RTC_CALM, val); + + val = bin2bcd(alrm->time.tm_sec) << 0 | bin2bcd(alrm->time.tm_min) << 8 | + bin2bcd(alrm->time.tm_hour) << 16; + rtc_reg_write(rtc, MA35_REG_RTC_TALM, val); + + ma35d1_alarm_irq_enable(dev, alrm->enabled); + + return 0; +} + +static const struct rtc_class_ops ma35d1_rtc_ops = { + .read_time = ma35d1_rtc_read_time, + .set_time = ma35d1_rtc_set_time, + .read_alarm = ma35d1_rtc_read_alarm, + .set_alarm = ma35d1_rtc_set_alarm, + .alarm_irq_enable = ma35d1_alarm_irq_enable, +}; + +static int ma35d1_rtc_probe(struct platform_device *pdev) +{ + struct ma35_rtc *rtc; + struct clk *clk; + int ret; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->rtc_reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rtc->rtc_reg)) + return PTR_ERR(rtc->rtc_reg); + + clk = of_clk_get(pdev->dev.of_node, 0); + if (IS_ERR(clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(clk), "failed to find rtc clock\n"); + + ret = clk_prepare_enable(clk); + if (ret) + return ret; + + if (!(rtc_reg_read(rtc, MA35_REG_RTC_INIT) & RTC_INIT_ACTIVE)) { + ret = ma35d1_rtc_init(rtc, RTC_INIT_TIMEOUT); + if (ret) + return dev_err_probe(&pdev->dev, ret, "rtc init failed\n"); + } + + rtc->irq_num = platform_get_irq(pdev, 0); + + ret = devm_request_irq(&pdev->dev, rtc->irq_num, ma35d1_rtc_interrupt, + IRQF_NO_SUSPEND, "ma35d1rtc", rtc); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to request rtc irq\n"); + + platform_set_drvdata(pdev, rtc); + + device_init_wakeup(&pdev->dev, true); + + rtc->rtcdev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc->rtcdev)) + return PTR_ERR(rtc->rtcdev); + + rtc->rtcdev->ops = &ma35d1_rtc_ops; + rtc->rtcdev->range_min = RTC_TIMESTAMP_BEGIN_2000; + rtc->rtcdev->range_max = RTC_TIMESTAMP_END_2099; + + ret = devm_rtc_register_device(rtc->rtcdev); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to register rtc device\n"); + + return 0; +} + +static int ma35d1_rtc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct ma35_rtc *rtc = platform_get_drvdata(pdev); + + if (device_may_wakeup(&pdev->dev)) + enable_irq_wake(rtc->irq_num); + + return 0; +} + +static int ma35d1_rtc_resume(struct platform_device *pdev) +{ + struct ma35_rtc *rtc = platform_get_drvdata(pdev); + + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(rtc->irq_num); + + return 0; +} + +static const struct of_device_id ma35d1_rtc_of_match[] = { + { .compatible = "nuvoton,ma35d1-rtc", }, + {}, +}; +MODULE_DEVICE_TABLE(of, ma35d1_rtc_of_match); + +static struct platform_driver ma35d1_rtc_driver = { + .suspend = ma35d1_rtc_suspend, + .resume = ma35d1_rtc_resume, + .probe = ma35d1_rtc_probe, + .driver = { + .name = "rtc-ma35d1", + .of_match_table = ma35d1_rtc_of_match, + }, +}; + +module_platform_driver(ma35d1_rtc_driver); + +MODULE_AUTHOR("Ming-Jen Chen "); +MODULE_DESCRIPTION("MA35D1 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-macsmc.c b/drivers/rtc/rtc-macsmc.c new file mode 100644 index 000000000..8fe883066 --- /dev/null +++ b/drivers/rtc/rtc-macsmc.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple SMC RTC driver + * Copyright The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* 48-bit RTC */ +#define RTC_BYTES 6 +#define RTC_BITS (8 * RTC_BYTES) + +/* 32768 Hz clock */ +#define RTC_SEC_SHIFT 15 + +struct macsmc_rtc { + struct device *dev; + struct apple_smc *smc; + struct rtc_device *rtc_dev; + struct nvmem_cell *rtc_offset; +}; + +static int macsmc_rtc_get_time(struct device *dev, struct rtc_time *tm) +{ + struct macsmc_rtc *rtc = dev_get_drvdata(dev); + u64 ctr = 0, off = 0; + time64_t now; + void *p_off; + size_t len; + int ret; + + ret = apple_smc_read(rtc->smc, SMC_KEY(CLKM), &ctr, RTC_BYTES); + if (ret < 0) + return ret; + if (ret != RTC_BYTES) + return -EIO; + + p_off = nvmem_cell_read(rtc->rtc_offset, &len); + if (IS_ERR(p_off)) + return PTR_ERR(p_off); + if (len < RTC_BYTES) { + kfree(p_off); + return -EIO; + } + + memcpy(&off, p_off, RTC_BYTES); + kfree(p_off); + + /* Sign extend from 48 to 64 bits, then arithmetic shift right 15 bits to get seconds */ + now = sign_extend64(ctr + off, RTC_BITS - 1) >> RTC_SEC_SHIFT; + rtc_time64_to_tm(now, tm); + + return ret; +} + +static int macsmc_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct macsmc_rtc *rtc = dev_get_drvdata(dev); + u64 ctr = 0, off = 0; + int ret; + + ret = apple_smc_read(rtc->smc, SMC_KEY(CLKM), &ctr, RTC_BYTES); + if (ret < 0) + return ret; + if (ret != RTC_BYTES) + return -EIO; + + /* This sets the offset such that the set second begins now */ + off = (rtc_tm_to_time64(tm) << RTC_SEC_SHIFT) - ctr; + return nvmem_cell_write(rtc->rtc_offset, &off, RTC_BYTES); +} + +static const struct rtc_class_ops macsmc_rtc_ops = { + .read_time = macsmc_rtc_get_time, + .set_time = macsmc_rtc_set_time, +}; + +static int macsmc_rtc_probe(struct platform_device *pdev) +{ + struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent); + struct macsmc_rtc *rtc; + + /* + * MFD will probe this device even without a node in the device tree, + * thus bail out early if the SMC on the current machines does not + * support RTC and has no node in the device tree. + */ + if (!pdev->dev.of_node) + return -ENODEV; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->dev = &pdev->dev; + rtc->smc = smc; + + rtc->rtc_offset = devm_nvmem_cell_get(&pdev->dev, "rtc_offset"); + if (IS_ERR(rtc->rtc_offset)) + return dev_err_probe(&pdev->dev, PTR_ERR(rtc->rtc_offset), + "Failed to get rtc_offset NVMEM cell\n"); + + rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc->rtc_dev)) + return PTR_ERR(rtc->rtc_dev); + + rtc->rtc_dev->ops = &macsmc_rtc_ops; + rtc->rtc_dev->range_min = S64_MIN >> (RTC_SEC_SHIFT + (64 - RTC_BITS)); + rtc->rtc_dev->range_max = S64_MAX >> (RTC_SEC_SHIFT + (64 - RTC_BITS)); + + platform_set_drvdata(pdev, rtc); + + return devm_rtc_register_device(rtc->rtc_dev); +} + +static const struct of_device_id macsmc_rtc_of_table[] = { + { .compatible = "apple,smc-rtc", }, + {} +}; +MODULE_DEVICE_TABLE(of, macsmc_rtc_of_table); + +static struct platform_driver macsmc_rtc_driver = { + .driver = { + .name = "macsmc-rtc", + .of_match_table = macsmc_rtc_of_table, + }, + .probe = macsmc_rtc_probe, +}; +module_platform_driver(macsmc_rtc_driver); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("Apple SMC RTC driver"); +MODULE_AUTHOR("Hector Martin "); diff --git a/drivers/rtc/rtc-max31335.c b/drivers/rtc/rtc-max31335.c new file mode 100644 index 000000000..595816973 --- /dev/null +++ b/drivers/rtc/rtc-max31335.c @@ -0,0 +1,776 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RTC driver for the MAX31335 + * + * Copyright (C) 2023 Analog Devices + * + * Antoniu Miclaus + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* MAX31335 Register Map */ +#define MAX31335_STATUS1 0x00 +#define MAX31335_INT_EN1 0x01 +#define MAX31335_STATUS2 0x02 +#define MAX31335_INT_EN2 0x03 +#define MAX31335_RTC_RESET 0x04 +#define MAX31335_RTC_CONFIG 0x05 +#define MAX31335_RTC_CONFIG2 0x06 +#define MAX31335_TIMESTAMP_CONFIG 0x07 +#define MAX31335_TIMER_CONFIG 0x08 +#define MAX31335_SECONDS_1_128 0x09 +#define MAX31335_SECONDS 0x0A +#define MAX31335_MINUTES 0x0B +#define MAX31335_HOURS 0x0C +#define MAX31335_DAY 0x0D +#define MAX31335_DATE 0x0E +#define MAX31335_MONTH 0x0F +#define MAX31335_YEAR 0x0F +#define MAX31335_ALM1_SEC 0x11 +#define MAX31335_ALM1_MIN 0x12 +#define MAX31335_ALM1_HRS 0x13 +#define MAX31335_ALM1_DAY_DATE 0x14 +#define MAX31335_ALM1_MON 0x15 +#define MAX31335_ALM1_YEAR 0x16 +#define MAX31335_ALM2_MIN 0x17 +#define MAX31335_ALM2_HRS 0x18 +#define MAX31335_ALM2_DAY_DATE 0x19 +#define MAX31335_TIMER_COUNT 0x1A +#define MAX31335_TIMER_INIT 0x1B +#define MAX31335_PWR_MGMT 0x1C +#define MAX31335_TRICKLE_REG 0x1D +#define MAX31335_AGING_OFFSET 0x1E +#define MAX31335_TS_CONFIG 0x30 +#define MAX31335_TEMP_ALARM_HIGH_MSB 0x31 +#define MAX31335_TEMP_ALARM_HIGH_LSB 0x32 +#define MAX31335_TEMP_ALARM_LOW_MSB 0x33 +#define MAX31335_TEMP_ALARM_LOW_LSB 0x34 +#define MAX31335_TEMP_DATA_MSB 0x35 +#define MAX31335_TEMP_DATA_LSB 0x36 +#define MAX31335_TS0_SEC_1_128 0x40 +#define MAX31335_TS0_SEC 0x41 +#define MAX31335_TS0_MIN 0x42 +#define MAX31335_TS0_HOUR 0x43 +#define MAX31335_TS0_DATE 0x44 +#define MAX31335_TS0_MONTH 0x45 +#define MAX31335_TS0_YEAR 0x46 +#define MAX31335_TS0_FLAGS 0x47 +#define MAX31335_TS1_SEC_1_128 0x48 +#define MAX31335_TS1_SEC 0x49 +#define MAX31335_TS1_MIN 0x4A +#define MAX31335_TS1_HOUR 0x4B +#define MAX31335_TS1_DATE 0x4C +#define MAX31335_TS1_MONTH 0x4D +#define MAX31335_TS1_YEAR 0x4E +#define MAX31335_TS1_FLAGS 0x4F +#define MAX31335_TS2_SEC_1_128 0x50 +#define MAX31335_TS2_SEC 0x51 +#define MAX31335_TS2_MIN 0x52 +#define MAX31335_TS2_HOUR 0x53 +#define MAX31335_TS2_DATE 0x54 +#define MAX31335_TS2_MONTH 0x55 +#define MAX31335_TS2_YEAR 0x56 +#define MAX31335_TS2_FLAGS 0x57 +#define MAX31335_TS3_SEC_1_128 0x58 +#define MAX31335_TS3_SEC 0x59 +#define MAX31335_TS3_MIN 0x5A +#define MAX31335_TS3_HOUR 0x5B +#define MAX31335_TS3_DATE 0x5C +#define MAX31335_TS3_MONTH 0x5D +#define MAX31335_TS3_YEAR 0x5E +#define MAX31335_TS3_FLAGS 0x5F + +/* MAX31335_STATUS1 Bit Definitions */ +#define MAX31335_STATUS1_PSDECT BIT(7) +#define MAX31335_STATUS1_OSF BIT(6) +#define MAX31335_STATUS1_PFAIL BIT(5) +#define MAX31335_STATUS1_VBATLOW BIT(4) +#define MAX31335_STATUS1_DIF BIT(3) +#define MAX31335_STATUS1_TIF BIT(2) +#define MAX31335_STATUS1_A2F BIT(1) +#define MAX31335_STATUS1_A1F BIT(0) + +/* MAX31335_INT_EN1 Bit Definitions */ +#define MAX31335_INT_EN1_DOSF BIT(6) +#define MAX31335_INT_EN1_PFAILE BIT(5) +#define MAX31335_INT_EN1_VBATLOWE BIT(4) +#define MAX31335_INT_EN1_DIE BIT(3) +#define MAX31335_INT_EN1_TIE BIT(2) +#define MAX31335_INT_EN1_A2IE BIT(1) +#define MAX31335_INT_EN1_A1IE BIT(0) + +/* MAX31335_STATUS2 Bit Definitions */ +#define MAX31335_STATUS2_TEMP_RDY BIT(2) +#define MAX31335_STATUS2_OTF BIT(1) +#define MAX31335_STATUS2_UTF BIT(0) + +/* MAX31335_INT_EN2 Bit Definitions */ +#define MAX31335_INT_EN2_TEMP_RDY_EN BIT(2) +#define MAX31335_INT_EN2_OTIE BIT(1) +#define MAX31335_INT_EN2_UTIE BIT(0) + +/* MAX31335_RTC_RESET Bit Definitions */ +#define MAX31335_RTC_RESET_SWRST BIT(0) + +/* MAX31335_RTC_CONFIG1 Bit Definitions */ +#define MAX31335_RTC_CONFIG1_EN_IO BIT(6) +#define MAX31335_RTC_CONFIG1_A1AC GENMASK(5, 4) +#define MAX31335_RTC_CONFIG1_DIP BIT(3) +#define MAX31335_RTC_CONFIG1_I2C_TIMEOUT BIT(1) +#define MAX31335_RTC_CONFIG1_EN_OSC BIT(0) + +/* MAX31335_RTC_CONFIG2 Bit Definitions */ +#define MAX31335_RTC_CONFIG2_ENCLKO BIT(2) +#define MAX31335_RTC_CONFIG2_CLKO_HZ GENMASK(1, 0) + +/* MAX31335_TIMESTAMP_CONFIG Bit Definitions */ +#define MAX31335_TIMESTAMP_CONFIG_TSVLOW BIT(5) +#define MAX31335_TIMESTAMP_CONFIG_TSPWM BIT(4) +#define MAX31335_TIMESTAMP_CONFIG_TSDIN BIT(3) +#define MAX31335_TIMESTAMP_CONFIG_TSOW BIT(2) +#define MAX31335_TIMESTAMP_CONFIG_TSR BIT(1) +#define MAX31335_TIMESTAMP_CONFIG_TSE BIT(0) + +/* MAX31335_TIMER_CONFIG Bit Definitions */ +#define MAX31335_TIMER_CONFIG_TE BIT(4) +#define MAX31335_TIMER_CONFIG_TPAUSE BIT(3) +#define MAX31335_TIMER_CONFIG_TRPT BIT(2) +#define MAX31335_TIMER_CONFIG_TFS GENMASK(1, 0) + +/* MAX31335_HOURS Bit Definitions */ +#define MAX31335_HOURS_F_24_12 BIT(6) +#define MAX31335_HOURS_HR_20_AM_PM BIT(5) + +/* MAX31335_MONTH Bit Definitions */ +#define MAX31335_MONTH_CENTURY BIT(7) + +/* MAX31335_PWR_MGMT Bit Definitions */ +#define MAX31335_PWR_MGMT_PFVT BIT(0) + +/* MAX31335_TRICKLE_REG Bit Definitions */ +#define MAX31335_TRICKLE_REG_TRICKLE GENMASK(3, 1) +#define MAX31335_TRICKLE_REG_EN_TRICKLE BIT(0) + +/* MAX31335_TS_CONFIG Bit Definitions */ +#define MAX31335_TS_CONFIG_AUTO BIT(4) +#define MAX31335_TS_CONFIG_CONVERT_T BIT(3) +#define MAX31335_TS_CONFIG_TSINT GENMASK(2, 0) + +/* MAX31335_TS_FLAGS Bit Definitions */ +#define MAX31335_TS_FLAGS_VLOWF BIT(3) +#define MAX31335_TS_FLAGS_VBATF BIT(2) +#define MAX31335_TS_FLAGS_VCCF BIT(1) +#define MAX31335_TS_FLAGS_DINF BIT(0) + +/* MAX31335 Miscellaneous Definitions */ +#define MAX31335_TRICKLE_SCHOTTKY_DIODE 1 +#define MAX31335_TRICKLE_STANDARD_DIODE 4 +#define MAX31335_RAM_SIZE 32 +#define MAX31335_TIME_SIZE 0x07 + +/* MAX31331 Register Map */ +#define MAX31331_RTC_CONFIG2 0x04 + +#define clk_hw_to_max31335(_hw) container_of(_hw, struct max31335_data, clkout) + +/* Supported Maxim RTC */ +enum max_rtc_ids { + ID_MAX31331, + ID_MAX31335, + MAX_RTC_ID_NR +}; + +struct chip_desc { + u8 sec_reg; + u8 alarm1_sec_reg; + + u8 int_en_reg; + u8 int_status_reg; + + u8 ram_reg; + u8 ram_size; + + u8 temp_reg; + + u8 trickle_reg; + + u8 clkout_reg; + + enum max_rtc_ids id; +}; + +struct max31335_data { + struct regmap *regmap; + struct rtc_device *rtc; + struct clk_hw clkout; + struct clk *clkin; + const struct chip_desc *chip; + int irq; +}; + +static const int max31335_clkout_freq[] = { 1, 64, 1024, 32768 }; + +static const struct chip_desc chip[MAX_RTC_ID_NR] = { + [ID_MAX31331] = { + .id = ID_MAX31331, + .int_en_reg = 0x01, + .int_status_reg = 0x00, + .sec_reg = 0x08, + .alarm1_sec_reg = 0x0F, + .ram_reg = 0x20, + .ram_size = 32, + .trickle_reg = 0x1B, + .clkout_reg = 0x04, + }, + [ID_MAX31335] = { + .id = ID_MAX31335, + .int_en_reg = 0x01, + .int_status_reg = 0x00, + .sec_reg = 0x0A, + .alarm1_sec_reg = 0x11, + .ram_reg = 0x40, + .ram_size = 32, + .temp_reg = 0x35, + .trickle_reg = 0x1D, + .clkout_reg = 0x06, + }, +}; + +static const u16 max31335_trickle_resistors[] = {3000, 6000, 11000}; + +static bool max31335_volatile_reg(struct device *dev, unsigned int reg) +{ + struct max31335_data *max31335 = dev_get_drvdata(dev); + const struct chip_desc *chip = max31335->chip; + + /* time keeping registers */ + if (reg >= chip->sec_reg && reg < chip->sec_reg + MAX31335_TIME_SIZE) + return true; + + /* interrupt status register */ + if (reg == chip->int_status_reg) + return true; + + /* temperature registers if valid */ + if (chip->temp_reg && (reg == chip->temp_reg || reg == chip->temp_reg + 1)) + return true; + + return false; +} + +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x5F, + .volatile_reg = max31335_volatile_reg, +}; + +static int max31335_read_time(struct device *dev, struct rtc_time *tm) +{ + struct max31335_data *max31335 = dev_get_drvdata(dev); + u8 date[7]; + int ret; + + ret = regmap_bulk_read(max31335->regmap, max31335->chip->sec_reg, date, + sizeof(date)); + if (ret) + return ret; + + tm->tm_sec = bcd2bin(date[0] & 0x7f); + tm->tm_min = bcd2bin(date[1] & 0x7f); + tm->tm_hour = bcd2bin(date[2] & 0x3f); + tm->tm_wday = bcd2bin(date[3] & 0x7) - 1; + tm->tm_mday = bcd2bin(date[4] & 0x3f); + tm->tm_mon = bcd2bin(date[5] & 0x1f) - 1; + tm->tm_year = bcd2bin(date[6]) + 100; + + if (FIELD_GET(MAX31335_MONTH_CENTURY, date[5])) + tm->tm_year += 100; + + return 0; +} + +static int max31335_set_time(struct device *dev, struct rtc_time *tm) +{ + struct max31335_data *max31335 = dev_get_drvdata(dev); + u8 date[7]; + + date[0] = bin2bcd(tm->tm_sec); + date[1] = bin2bcd(tm->tm_min); + date[2] = bin2bcd(tm->tm_hour); + date[3] = bin2bcd(tm->tm_wday + 1); + date[4] = bin2bcd(tm->tm_mday); + date[5] = bin2bcd(tm->tm_mon + 1); + date[6] = bin2bcd(tm->tm_year % 100); + + if (tm->tm_year >= 200) + date[5] |= FIELD_PREP(MAX31335_MONTH_CENTURY, 1); + + return regmap_bulk_write(max31335->regmap, max31335->chip->sec_reg, date, + sizeof(date)); +} + +static int max31335_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct max31335_data *max31335 = dev_get_drvdata(dev); + int ret, ctrl, status; + struct rtc_time time; + u8 regs[6]; + + ret = regmap_bulk_read(max31335->regmap, max31335->chip->alarm1_sec_reg, regs, + sizeof(regs)); + if (ret) + return ret; + + alrm->time.tm_sec = bcd2bin(regs[0] & 0x7f); + alrm->time.tm_min = bcd2bin(regs[1] & 0x7f); + alrm->time.tm_hour = bcd2bin(regs[2] & 0x3f); + alrm->time.tm_mday = bcd2bin(regs[3] & 0x3f); + alrm->time.tm_mon = bcd2bin(regs[4] & 0x1f) - 1; + alrm->time.tm_year = bcd2bin(regs[5]) + 100; + + ret = max31335_read_time(dev, &time); + if (ret) + return ret; + + if (time.tm_year >= 200) + alrm->time.tm_year += 100; + + ret = regmap_read(max31335->regmap, max31335->chip->int_en_reg, &ctrl); + if (ret) + return ret; + + ret = regmap_read(max31335->regmap, max31335->chip->int_status_reg, &status); + if (ret) + return ret; + + alrm->enabled = FIELD_GET(MAX31335_INT_EN1_A1IE, ctrl); + alrm->pending = FIELD_GET(MAX31335_STATUS1_A1F, status); + + return 0; +} + +static int max31335_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct max31335_data *max31335 = dev_get_drvdata(dev); + unsigned int reg; + u8 regs[6]; + int ret; + + regs[0] = bin2bcd(alrm->time.tm_sec); + regs[1] = bin2bcd(alrm->time.tm_min); + regs[2] = bin2bcd(alrm->time.tm_hour); + regs[3] = bin2bcd(alrm->time.tm_mday); + regs[4] = bin2bcd(alrm->time.tm_mon + 1); + regs[5] = bin2bcd(alrm->time.tm_year % 100); + + ret = regmap_bulk_write(max31335->regmap, max31335->chip->alarm1_sec_reg, + regs, sizeof(regs)); + if (ret) + return ret; + + reg = FIELD_PREP(MAX31335_INT_EN1_A1IE, alrm->enabled); + ret = regmap_update_bits(max31335->regmap, max31335->chip->int_en_reg, + MAX31335_INT_EN1_A1IE, reg); + if (ret) + return ret; + + return regmap_update_bits(max31335->regmap, max31335->chip->int_status_reg, + MAX31335_STATUS1_A1F, 0); +} + +static int max31335_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct max31335_data *max31335 = dev_get_drvdata(dev); + + return regmap_update_bits(max31335->regmap, max31335->chip->int_en_reg, + MAX31335_INT_EN1_A1IE, enabled); +} + +static irqreturn_t max31335_handle_irq(int irq, void *dev_id) +{ + struct max31335_data *max31335 = dev_id; + struct mutex *lock = &max31335->rtc->ops_lock; + int ret, status; + + mutex_lock(lock); + + ret = regmap_read(max31335->regmap, max31335->chip->int_status_reg, &status); + if (ret) + goto exit; + + if (FIELD_GET(MAX31335_STATUS1_A1F, status)) { + ret = regmap_update_bits(max31335->regmap, max31335->chip->int_status_reg, + MAX31335_STATUS1_A1F, 0); + if (ret) + goto exit; + + rtc_update_irq(max31335->rtc, 1, RTC_AF | RTC_IRQF); + } + +exit: + mutex_unlock(lock); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops max31335_rtc_ops = { + .read_time = max31335_read_time, + .set_time = max31335_set_time, + .read_alarm = max31335_read_alarm, + .set_alarm = max31335_set_alarm, + .alarm_irq_enable = max31335_alarm_irq_enable, +}; + +static int max31335_trickle_charger_setup(struct device *dev, + struct max31335_data *max31335) +{ + u32 ohms, chargeable; + int i, trickle_cfg; + const char *diode; + + if (device_property_read_u32(dev, "aux-voltage-chargeable", + &chargeable)) + return 0; + + if (device_property_read_u32(dev, "trickle-resistor-ohms", &ohms)) + return 0; + + if (device_property_read_string(dev, "adi,tc-diode", &diode)) + return 0; + + if (!strcmp(diode, "schottky")) + trickle_cfg = MAX31335_TRICKLE_SCHOTTKY_DIODE; + else if (!strcmp(diode, "standard+schottky")) + trickle_cfg = MAX31335_TRICKLE_STANDARD_DIODE; + else + return dev_err_probe(dev, -EINVAL, + "Invalid tc-diode value: %s\n", diode); + + for (i = 0; i < ARRAY_SIZE(max31335_trickle_resistors); i++) + if (ohms == max31335_trickle_resistors[i]) + break; + + if (i >= ARRAY_SIZE(max31335_trickle_resistors)) + return 0; + + i = i + trickle_cfg; + + return regmap_write(max31335->regmap, max31335->chip->trickle_reg, + FIELD_PREP(MAX31335_TRICKLE_REG_TRICKLE, i) | + FIELD_PREP(MAX31335_TRICKLE_REG_EN_TRICKLE, + chargeable)); +} + +static unsigned long max31335_clkout_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct max31335_data *max31335 = clk_hw_to_max31335(hw); + unsigned int freq_mask; + unsigned int reg; + int ret; + + ret = regmap_read(max31335->regmap, max31335->chip->clkout_reg, ®); + if (ret) + return 0; + + freq_mask = __roundup_pow_of_two(ARRAY_SIZE(max31335_clkout_freq)) - 1; + + return max31335_clkout_freq[reg & freq_mask]; +} + +static int max31335_clkout_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + int index; + + index = find_closest(req->rate, max31335_clkout_freq, + ARRAY_SIZE(max31335_clkout_freq)); + + req->rate = max31335_clkout_freq[index]; + + return 0; +} + +static int max31335_clkout_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct max31335_data *max31335 = clk_hw_to_max31335(hw); + unsigned int freq_mask; + int index; + + index = find_closest(rate, max31335_clkout_freq, + ARRAY_SIZE(max31335_clkout_freq)); + freq_mask = __roundup_pow_of_two(ARRAY_SIZE(max31335_clkout_freq)) - 1; + + return regmap_update_bits(max31335->regmap, max31335->chip->clkout_reg, + freq_mask, index); +} + +static int max31335_clkout_enable(struct clk_hw *hw) +{ + struct max31335_data *max31335 = clk_hw_to_max31335(hw); + + return regmap_set_bits(max31335->regmap, max31335->chip->clkout_reg, + MAX31335_RTC_CONFIG2_ENCLKO); +} + +static void max31335_clkout_disable(struct clk_hw *hw) +{ + struct max31335_data *max31335 = clk_hw_to_max31335(hw); + + regmap_clear_bits(max31335->regmap, max31335->chip->clkout_reg, + MAX31335_RTC_CONFIG2_ENCLKO); +} + +static int max31335_clkout_is_enabled(struct clk_hw *hw) +{ + struct max31335_data *max31335 = clk_hw_to_max31335(hw); + unsigned int reg; + int ret; + + ret = regmap_read(max31335->regmap, max31335->chip->clkout_reg, ®); + if (ret) + return ret; + + return !!(reg & MAX31335_RTC_CONFIG2_ENCLKO); +} + +static const struct clk_ops max31335_clkout_ops = { + .recalc_rate = max31335_clkout_recalc_rate, + .determine_rate = max31335_clkout_determine_rate, + .set_rate = max31335_clkout_set_rate, + .enable = max31335_clkout_enable, + .disable = max31335_clkout_disable, + .is_enabled = max31335_clkout_is_enabled, +}; + +static struct clk_init_data max31335_clk_init = { + .name = "max31335-clkout", + .ops = &max31335_clkout_ops, +}; + +static int max31335_nvmem_reg_read(void *priv, unsigned int offset, + void *val, size_t bytes) +{ + struct max31335_data *max31335 = priv; + unsigned int reg = max31335->chip->ram_reg + offset; + + return regmap_bulk_read(max31335->regmap, reg, val, bytes); +} + +static int max31335_nvmem_reg_write(void *priv, unsigned int offset, + void *val, size_t bytes) +{ + struct max31335_data *max31335 = priv; + unsigned int reg = max31335->chip->ram_reg + offset; + + return regmap_bulk_write(max31335->regmap, reg, val, bytes); +} + +static struct nvmem_config max31335_nvmem_cfg = { + .reg_read = max31335_nvmem_reg_read, + .reg_write = max31335_nvmem_reg_write, + .word_size = 8, + .size = MAX31335_RAM_SIZE, +}; + +#if IS_REACHABLE(CONFIG_HWMON) +static int max31335_read_temp(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct max31335_data *max31335 = dev_get_drvdata(dev); + u8 reg[2]; + s16 temp; + int ret; + + if (type != hwmon_temp || attr != hwmon_temp_input) + return -EOPNOTSUPP; + + ret = regmap_bulk_read(max31335->regmap, max31335->chip->temp_reg, + reg, 2); + if (ret) + return ret; + + temp = get_unaligned_be16(reg); + + *val = (temp / 64) * 250; + + return 0; +} + +static umode_t max31335_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (type == hwmon_temp && attr == hwmon_temp_input) + return 0444; + + return 0; +} + +static const struct hwmon_channel_info *max31335_info[] = { + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), + NULL +}; + +static const struct hwmon_ops max31335_hwmon_ops = { + .is_visible = max31335_is_visible, + .read = max31335_read_temp, +}; + +static const struct hwmon_chip_info max31335_chip_info = { + .ops = &max31335_hwmon_ops, + .info = max31335_info, +}; +#endif + +static int max31335_clkout_register(struct device *dev) +{ + struct max31335_data *max31335 = dev_get_drvdata(dev); + int ret; + + if (!device_property_present(dev, "#clock-cells")) + return regmap_clear_bits(max31335->regmap, max31335->chip->clkout_reg, + MAX31335_RTC_CONFIG2_ENCLKO); + + max31335->clkout.init = &max31335_clk_init; + + ret = devm_clk_hw_register(dev, &max31335->clkout); + if (ret) + return dev_err_probe(dev, ret, "cannot register clock\n"); + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + &max31335->clkout); + if (ret) + return dev_err_probe(dev, ret, "cannot add hw provider\n"); + + max31335->clkout.clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(max31335->clkout.clk)) + return dev_err_probe(dev, PTR_ERR(max31335->clkout.clk), + "cannot enable clkout\n"); + + return 0; +} + +static int max31335_probe(struct i2c_client *client) +{ + struct max31335_data *max31335; +#if IS_REACHABLE(CONFIG_HWMON) + struct device *hwmon; +#endif + const struct chip_desc *match; + int ret; + + max31335 = devm_kzalloc(&client->dev, sizeof(*max31335), GFP_KERNEL); + if (!max31335) + return -ENOMEM; + + max31335->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(max31335->regmap)) + return PTR_ERR(max31335->regmap); + + i2c_set_clientdata(client, max31335); + match = i2c_get_match_data(client); + if (!match) + return -ENODEV; + max31335->chip = match; + max31335->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(max31335->rtc)) + return PTR_ERR(max31335->rtc); + + max31335->rtc->ops = &max31335_rtc_ops; + max31335->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + max31335->rtc->range_max = RTC_TIMESTAMP_END_2199; + max31335->rtc->alarm_offset_max = 24 * 60 * 60; + + ret = max31335_clkout_register(&client->dev); + if (ret) + return ret; + + if (client->irq > 0) { + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, max31335_handle_irq, + IRQF_ONESHOT, + "max31335", max31335); + if (ret) { + dev_warn(&client->dev, + "unable to request IRQ, alarm max31335 disabled\n"); + client->irq = 0; + } else { + max31335->irq = client->irq; + } + } + + if (!client->irq) + clear_bit(RTC_FEATURE_ALARM, max31335->rtc->features); + + max31335_nvmem_cfg.priv = max31335; + ret = devm_rtc_nvmem_register(max31335->rtc, &max31335_nvmem_cfg); + if (ret) + return dev_err_probe(&client->dev, ret, + "cannot register rtc nvmem\n"); + +#if IS_REACHABLE(CONFIG_HWMON) + if (max31335->chip->temp_reg) { + hwmon = devm_hwmon_device_register_with_info(&client->dev, client->name, max31335, + &max31335_chip_info, NULL); + if (IS_ERR(hwmon)) + return dev_err_probe(&client->dev, PTR_ERR(hwmon), + "cannot register hwmon device\n"); + } +#endif + + ret = max31335_trickle_charger_setup(&client->dev, max31335); + if (ret) + return ret; + + return devm_rtc_register_device(max31335->rtc); +} + +static const struct i2c_device_id max31335_id[] = { + { .name = "max31331", .driver_data = (kernel_ulong_t)&chip[ID_MAX31331] }, + { .name = "max31335", .driver_data = (kernel_ulong_t)&chip[ID_MAX31335] }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, max31335_id); + +static const struct of_device_id max31335_of_match[] = { + { .compatible = "adi,max31331", .data = &chip[ID_MAX31331] }, + { .compatible = "adi,max31335", .data = &chip[ID_MAX31335] }, + { } +}; + +MODULE_DEVICE_TABLE(of, max31335_of_match); + +static struct i2c_driver max31335_driver = { + .driver = { + .name = "rtc-max31335", + .of_match_table = max31335_of_match, + }, + .probe = max31335_probe, + .id_table = max31335_id, +}; +module_i2c_driver(max31335_driver); + +MODULE_AUTHOR("Antoniu Miclaus "); +MODULE_AUTHOR("Saket Kumar Purwar "); +MODULE_DESCRIPTION("MAX31335 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-max6900.c b/drivers/rtc/rtc-max6900.c new file mode 100644 index 000000000..8ef6d0fcd --- /dev/null +++ b/drivers/rtc/rtc-max6900.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rtc class driver for the Maxim MAX6900 chip + * + * Copyright (c) 2007 MontaVista, Software, Inc. + * + * Author: Dale Farnsworth + * + * based on previously existing rtc class drivers + */ + +#include +#include +#include +#include +#include + +/* + * register indices + */ +#define MAX6900_REG_SC 0 /* seconds 00-59 */ +#define MAX6900_REG_MN 1 /* minutes 00-59 */ +#define MAX6900_REG_HR 2 /* hours 00-23 */ +#define MAX6900_REG_DT 3 /* day of month 00-31 */ +#define MAX6900_REG_MO 4 /* month 01-12 */ +#define MAX6900_REG_DW 5 /* day of week 1-7 */ +#define MAX6900_REG_YR 6 /* year 00-99 */ +#define MAX6900_REG_CT 7 /* control */ + /* register 8 is undocumented */ +#define MAX6900_REG_CENTURY 9 /* century */ +#define MAX6900_REG_LEN 10 + +#define MAX6900_BURST_LEN 8 /* can burst r/w first 8 regs */ + +#define MAX6900_REG_CT_WP (1 << 7) /* Write Protect */ + +/* + * register read/write commands + */ +#define MAX6900_REG_CONTROL_WRITE 0x8e +#define MAX6900_REG_CENTURY_WRITE 0x92 +#define MAX6900_REG_CENTURY_READ 0x93 +#define MAX6900_REG_RESERVED_READ 0x96 +#define MAX6900_REG_BURST_WRITE 0xbe +#define MAX6900_REG_BURST_READ 0xbf + +#define MAX6900_IDLE_TIME_AFTER_WRITE 3 /* specification says 2.5 mS */ + +static struct i2c_driver max6900_driver; + +static int max6900_i2c_read_regs(struct i2c_client *client, u8 *buf) +{ + u8 reg_burst_read[1] = { MAX6900_REG_BURST_READ }; + u8 reg_century_read[1] = { MAX6900_REG_CENTURY_READ }; + struct i2c_msg msgs[4] = { + { + .addr = client->addr, + .flags = 0, /* write */ + .len = sizeof(reg_burst_read), + .buf = reg_burst_read} + , + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = MAX6900_BURST_LEN, + .buf = buf} + , + { + .addr = client->addr, + .flags = 0, /* write */ + .len = sizeof(reg_century_read), + .buf = reg_century_read} + , + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = sizeof(buf[MAX6900_REG_CENTURY]), + .buf = &buf[MAX6900_REG_CENTURY] + } + }; + int rc; + + rc = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (rc != ARRAY_SIZE(msgs)) { + dev_err(&client->dev, "%s: register read failed\n", __func__); + return -EIO; + } + return 0; +} + +static int max6900_i2c_write_regs(struct i2c_client *client, u8 const *buf) +{ + u8 i2c_century_buf[1 + 1] = { MAX6900_REG_CENTURY_WRITE }; + struct i2c_msg century_msgs[1] = { + { + .addr = client->addr, + .flags = 0, /* write */ + .len = sizeof(i2c_century_buf), + .buf = i2c_century_buf} + }; + u8 i2c_burst_buf[MAX6900_BURST_LEN + 1] = { MAX6900_REG_BURST_WRITE }; + struct i2c_msg burst_msgs[1] = { + { + .addr = client->addr, + .flags = 0, /* write */ + .len = sizeof(i2c_burst_buf), + .buf = i2c_burst_buf} + }; + int rc; + + /* + * We have to make separate calls to i2c_transfer because of + * the need to delay after each write to the chip. Also, + * we write the century byte first, since we set the write-protect + * bit as part of the burst write. + */ + i2c_century_buf[1] = buf[MAX6900_REG_CENTURY]; + + rc = i2c_transfer(client->adapter, century_msgs, + ARRAY_SIZE(century_msgs)); + if (rc != ARRAY_SIZE(century_msgs)) + goto write_failed; + + msleep(MAX6900_IDLE_TIME_AFTER_WRITE); + + memcpy(&i2c_burst_buf[1], buf, MAX6900_BURST_LEN); + + rc = i2c_transfer(client->adapter, burst_msgs, ARRAY_SIZE(burst_msgs)); + if (rc != ARRAY_SIZE(burst_msgs)) + goto write_failed; + msleep(MAX6900_IDLE_TIME_AFTER_WRITE); + + return 0; + + write_failed: + dev_err(&client->dev, "%s: register write failed\n", __func__); + return -EIO; +} + +static int max6900_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + int rc; + u8 regs[MAX6900_REG_LEN]; + + rc = max6900_i2c_read_regs(client, regs); + if (rc < 0) + return rc; + + tm->tm_sec = bcd2bin(regs[MAX6900_REG_SC]); + tm->tm_min = bcd2bin(regs[MAX6900_REG_MN]); + tm->tm_hour = bcd2bin(regs[MAX6900_REG_HR] & 0x3f); + tm->tm_mday = bcd2bin(regs[MAX6900_REG_DT]); + tm->tm_mon = bcd2bin(regs[MAX6900_REG_MO]) - 1; + tm->tm_year = bcd2bin(regs[MAX6900_REG_YR]) + + bcd2bin(regs[MAX6900_REG_CENTURY]) * 100 - 1900; + tm->tm_wday = bcd2bin(regs[MAX6900_REG_DW]); + + return 0; +} + +static int max6900_i2c_clear_write_protect(struct i2c_client *client) +{ + return i2c_smbus_write_byte_data(client, MAX6900_REG_CONTROL_WRITE, 0); +} + +static int max6900_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 regs[MAX6900_REG_LEN]; + int rc; + + rc = max6900_i2c_clear_write_protect(client); + if (rc < 0) + return rc; + + regs[MAX6900_REG_SC] = bin2bcd(tm->tm_sec); + regs[MAX6900_REG_MN] = bin2bcd(tm->tm_min); + regs[MAX6900_REG_HR] = bin2bcd(tm->tm_hour); + regs[MAX6900_REG_DT] = bin2bcd(tm->tm_mday); + regs[MAX6900_REG_MO] = bin2bcd(tm->tm_mon + 1); + regs[MAX6900_REG_DW] = bin2bcd(tm->tm_wday); + regs[MAX6900_REG_YR] = bin2bcd(tm->tm_year % 100); + regs[MAX6900_REG_CENTURY] = bin2bcd((tm->tm_year + 1900) / 100); + /* set write protect */ + regs[MAX6900_REG_CT] = MAX6900_REG_CT_WP; + + rc = max6900_i2c_write_regs(client, regs); + if (rc < 0) + return rc; + + return 0; +} + +static const struct rtc_class_ops max6900_rtc_ops = { + .read_time = max6900_rtc_read_time, + .set_time = max6900_rtc_set_time, +}; + +static int max6900_probe(struct i2c_client *client) +{ + struct rtc_device *rtc; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + rtc = devm_rtc_device_register(&client->dev, max6900_driver.driver.name, + &max6900_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + i2c_set_clientdata(client, rtc); + + return 0; +} + +static const struct i2c_device_id max6900_id[] = { + { .name = "max6900" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max6900_id); + +static struct i2c_driver max6900_driver = { + .driver = { + .name = "rtc-max6900", + }, + .probe = max6900_probe, + .id_table = max6900_id, +}; + +module_i2c_driver(max6900_driver); + +MODULE_DESCRIPTION("Maxim MAX6900 RTC driver"); +MODULE_AUTHOR("Dale Farnsworth "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-max6902.c b/drivers/rtc/rtc-max6902.c new file mode 100644 index 000000000..daaeb6fb6 --- /dev/null +++ b/drivers/rtc/rtc-max6902.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* drivers/rtc/rtc-max6902.c + * + * Copyright (C) 2006 8D Technologies inc. + * Copyright (C) 2004 Compulab Ltd. + * + * Driver for MAX6902 spi RTC + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MAX6902_REG_SECONDS 0x01 +#define MAX6902_REG_MINUTES 0x03 +#define MAX6902_REG_HOURS 0x05 +#define MAX6902_REG_DATE 0x07 +#define MAX6902_REG_MONTH 0x09 +#define MAX6902_REG_DAY 0x0B +#define MAX6902_REG_YEAR 0x0D +#define MAX6902_REG_CONTROL 0x0F +#define MAX6902_REG_CENTURY 0x13 + +static int max6902_set_reg(struct device *dev, unsigned char address, + unsigned char data) +{ + struct spi_device *spi = to_spi_device(dev); + unsigned char buf[2]; + + /* MSB must be '0' to write */ + buf[0] = address & 0x7f; + buf[1] = data; + + return spi_write_then_read(spi, buf, 2, NULL, 0); +} + +static int max6902_get_reg(struct device *dev, unsigned char address, + unsigned char *data) +{ + struct spi_device *spi = to_spi_device(dev); + + /* Set MSB to indicate read */ + *data = address | 0x80; + + return spi_write_then_read(spi, data, 1, data, 1); +} + +static int max6902_read_time(struct device *dev, struct rtc_time *dt) +{ + int err, century; + struct spi_device *spi = to_spi_device(dev); + unsigned char buf[8]; + + buf[0] = 0xbf; /* Burst read */ + + err = spi_write_then_read(spi, buf, 1, buf, 8); + if (err != 0) + return err; + + /* The chip sends data in this order: + * Seconds, Minutes, Hours, Date, Month, Day, Year */ + dt->tm_sec = bcd2bin(buf[0]); + dt->tm_min = bcd2bin(buf[1]); + dt->tm_hour = bcd2bin(buf[2]); + dt->tm_mday = bcd2bin(buf[3]); + dt->tm_mon = bcd2bin(buf[4]) - 1; + dt->tm_wday = bcd2bin(buf[5]); + dt->tm_year = bcd2bin(buf[6]); + + /* Read century */ + err = max6902_get_reg(dev, MAX6902_REG_CENTURY, &buf[0]); + if (err != 0) + return err; + + century = bcd2bin(buf[0]) * 100; + + dt->tm_year += century; + dt->tm_year -= 1900; + + return 0; +} + +static int max6902_set_time(struct device *dev, struct rtc_time *dt) +{ + dt->tm_year = dt->tm_year + 1900; + + /* Remove write protection */ + max6902_set_reg(dev, MAX6902_REG_CONTROL, 0); + + max6902_set_reg(dev, MAX6902_REG_SECONDS, bin2bcd(dt->tm_sec)); + max6902_set_reg(dev, MAX6902_REG_MINUTES, bin2bcd(dt->tm_min)); + max6902_set_reg(dev, MAX6902_REG_HOURS, bin2bcd(dt->tm_hour)); + + max6902_set_reg(dev, MAX6902_REG_DATE, bin2bcd(dt->tm_mday)); + max6902_set_reg(dev, MAX6902_REG_MONTH, bin2bcd(dt->tm_mon + 1)); + max6902_set_reg(dev, MAX6902_REG_DAY, bin2bcd(dt->tm_wday)); + max6902_set_reg(dev, MAX6902_REG_YEAR, bin2bcd(dt->tm_year % 100)); + max6902_set_reg(dev, MAX6902_REG_CENTURY, bin2bcd(dt->tm_year / 100)); + + /* Compulab used a delay here. However, the datasheet + * does not mention a delay being required anywhere... */ + /* delay(2000); */ + + /* Write protect */ + max6902_set_reg(dev, MAX6902_REG_CONTROL, 0x80); + + return 0; +} + +static const struct rtc_class_ops max6902_rtc_ops = { + .read_time = max6902_read_time, + .set_time = max6902_set_time, +}; + +static int max6902_probe(struct spi_device *spi) +{ + struct rtc_device *rtc; + unsigned char tmp; + int res; + + spi->mode = SPI_MODE_3; + spi->bits_per_word = 8; + spi_setup(spi); + + res = max6902_get_reg(&spi->dev, MAX6902_REG_SECONDS, &tmp); + if (res != 0) + return res; + + rtc = devm_rtc_device_register(&spi->dev, "max6902", + &max6902_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + spi_set_drvdata(spi, rtc); + return 0; +} + +static struct spi_driver max6902_driver = { + .driver = { + .name = "rtc-max6902", + }, + .probe = max6902_probe, +}; + +module_spi_driver(max6902_driver); + +MODULE_DESCRIPTION("max6902 spi RTC driver"); +MODULE_AUTHOR("Raphael Assenat"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:rtc-max6902"); diff --git a/drivers/rtc/rtc-max6916.c b/drivers/rtc/rtc-max6916.c new file mode 100644 index 000000000..e72e768ab --- /dev/null +++ b/drivers/rtc/rtc-max6916.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* rtc-max6916.c + * + * Driver for MAXIM max6916 Low Current, SPI Compatible + * Real Time Clock + * + * Author : Venkat Prashanth B U + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Registers in max6916 rtc */ + +#define MAX6916_SECONDS_REG 0x01 +#define MAX6916_MINUTES_REG 0x02 +#define MAX6916_HOURS_REG 0x03 +#define MAX6916_DATE_REG 0x04 +#define MAX6916_MONTH_REG 0x05 +#define MAX6916_DAY_REG 0x06 +#define MAX6916_YEAR_REG 0x07 +#define MAX6916_CONTROL_REG 0x08 +#define MAX6916_STATUS_REG 0x0C +#define MAX6916_CLOCK_BURST 0x3F + +static int max6916_read_reg(struct device *dev, unsigned char address, + unsigned char *data) +{ + struct spi_device *spi = to_spi_device(dev); + + *data = address | 0x80; + + return spi_write_then_read(spi, data, 1, data, 1); +} + +static int max6916_write_reg(struct device *dev, unsigned char address, + unsigned char data) +{ + struct spi_device *spi = to_spi_device(dev); + unsigned char buf[2]; + + buf[0] = address & 0x7F; + buf[1] = data; + + return spi_write_then_read(spi, buf, 2, NULL, 0); +} + +static int max6916_read_time(struct device *dev, struct rtc_time *dt) +{ + struct spi_device *spi = to_spi_device(dev); + int err; + unsigned char buf[8]; + + buf[0] = MAX6916_CLOCK_BURST | 0x80; + + err = spi_write_then_read(spi, buf, 1, buf, 8); + + if (err) + return err; + + dt->tm_sec = bcd2bin(buf[0]); + dt->tm_min = bcd2bin(buf[1]); + dt->tm_hour = bcd2bin(buf[2] & 0x3F); + dt->tm_mday = bcd2bin(buf[3]); + dt->tm_mon = bcd2bin(buf[4]) - 1; + dt->tm_wday = bcd2bin(buf[5]) - 1; + dt->tm_year = bcd2bin(buf[6]) + 100; + + return 0; +} + +static int max6916_set_time(struct device *dev, struct rtc_time *dt) +{ + struct spi_device *spi = to_spi_device(dev); + unsigned char buf[9]; + + if (dt->tm_year < 100 || dt->tm_year > 199) { + dev_err(&spi->dev, "Year must be between 2000 and 2099. It's %d.\n", + dt->tm_year + 1900); + return -EINVAL; + } + + buf[0] = MAX6916_CLOCK_BURST & 0x7F; + buf[1] = bin2bcd(dt->tm_sec); + buf[2] = bin2bcd(dt->tm_min); + buf[3] = (bin2bcd(dt->tm_hour) & 0X3F); + buf[4] = bin2bcd(dt->tm_mday); + buf[5] = bin2bcd(dt->tm_mon + 1); + buf[6] = bin2bcd(dt->tm_wday + 1); + buf[7] = bin2bcd(dt->tm_year % 100); + buf[8] = bin2bcd(0x00); + + /* write the rtc settings */ + return spi_write_then_read(spi, buf, 9, NULL, 0); +} + +static const struct rtc_class_ops max6916_rtc_ops = { + .read_time = max6916_read_time, + .set_time = max6916_set_time, +}; + +static int max6916_probe(struct spi_device *spi) +{ + struct rtc_device *rtc; + unsigned char data; + int res; + + /* spi setup with max6916 in mode 3 and bits per word as 8 */ + spi->mode = SPI_MODE_3; + spi->bits_per_word = 8; + spi_setup(spi); + + /* RTC Settings */ + res = max6916_read_reg(&spi->dev, MAX6916_SECONDS_REG, &data); + if (res) + return res; + + /* Disable the write protect of rtc */ + max6916_read_reg(&spi->dev, MAX6916_CONTROL_REG, &data); + data = data & ~(1 << 7); + max6916_write_reg(&spi->dev, MAX6916_CONTROL_REG, data); + + /*Enable oscillator,disable oscillator stop flag, glitch filter*/ + max6916_read_reg(&spi->dev, MAX6916_STATUS_REG, &data); + data = data & 0x1B; + max6916_write_reg(&spi->dev, MAX6916_STATUS_REG, data); + + /* display the settings */ + max6916_read_reg(&spi->dev, MAX6916_CONTROL_REG, &data); + dev_info(&spi->dev, "MAX6916 RTC CTRL Reg = 0x%02x\n", data); + + max6916_read_reg(&spi->dev, MAX6916_STATUS_REG, &data); + dev_info(&spi->dev, "MAX6916 RTC Status Reg = 0x%02x\n", data); + + rtc = devm_rtc_device_register(&spi->dev, "max6916", + &max6916_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + spi_set_drvdata(spi, rtc); + + return 0; +} + +static struct spi_driver max6916_driver = { + .driver = { + .name = "max6916", + }, + .probe = max6916_probe, +}; +module_spi_driver(max6916_driver); + +MODULE_DESCRIPTION("MAX6916 SPI RTC DRIVER"); +MODULE_AUTHOR("Venkat Prashanth B U "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c new file mode 100644 index 000000000..375565a3b --- /dev/null +++ b/drivers/rtc/rtc-max77686.c @@ -0,0 +1,891 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// RTC driver for Maxim MAX77686 and MAX77802 +// +// Copyright (C) 2012 Samsung Electronics Co.Ltd +// +// based on rtc-max8997.c + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX77686_I2C_ADDR_RTC (0x0C >> 1) +#define MAX77620_I2C_ADDR_RTC 0x68 +#define MAX77714_I2C_ADDR_RTC 0x48 +#define MAX77686_INVALID_I2C_ADDR (-1) + +/* Define non existing register */ +#define MAX77686_INVALID_REG (-1) + +/* RTC Control Register */ +#define BCD_EN_SHIFT 0 +#define BCD_EN_MASK BIT(BCD_EN_SHIFT) +#define MODEL24_SHIFT 1 +#define MODEL24_MASK BIT(MODEL24_SHIFT) +/* RTC Update Register1 */ +#define RTC_UDR_SHIFT 0 +#define RTC_UDR_MASK BIT(RTC_UDR_SHIFT) +#define RTC_RBUDR_SHIFT 4 +#define RTC_RBUDR_MASK BIT(RTC_RBUDR_SHIFT) +/* RTC Alarm Enable */ +#define ALARM_ENABLE_SHIFT 7 +#define ALARM_ENABLE_MASK BIT(ALARM_ENABLE_SHIFT) + +#define REG_RTC_NONE 0xdeadbeef + +/* + * MAX77802 has separate register (RTCAE1) for alarm enable instead + * using 1 bit from registers RTC{SEC,MIN,HOUR,DAY,MONTH,YEAR,DATE} + * as in done in MAX77686. + */ +#define MAX77802_ALARM_ENABLE_VALUE 0x77 + +enum { + RTC_SEC = 0, + RTC_MIN, + RTC_HOUR, + RTC_WEEKDAY, + RTC_MONTH, + RTC_YEAR, + RTC_MONTHDAY, + RTC_NR_TIME +}; + +/** + * struct max77686_rtc_driver_data - model-specific configuration + * @delay: Minimum usecs needed for a RTC update + * @mask: Mask used to read RTC registers value + * @map: Registers offset to I2C addresses map + * @alarm_enable_reg: Has a separate alarm enable register? + * @rtc_i2c_addr: I2C address for RTC block + * @rtc_irq_from_platform: RTC interrupt via platform resource + * @alarm_pending_status_reg: Pending alarm status register + * @rtc_irq_chip: RTC IRQ CHIP for regmap + * @regmap_config: regmap configuration for the chip + */ +struct max77686_rtc_driver_data { + unsigned long delay; + u8 mask; + const unsigned int *map; + bool alarm_enable_reg; + int rtc_i2c_addr; + bool rtc_irq_from_platform; + int alarm_pending_status_reg; + const struct regmap_irq_chip *rtc_irq_chip; + const struct regmap_config *regmap_config; +}; + +struct max77686_rtc_info { + struct device *dev; + struct rtc_device *rtc_dev; + struct mutex lock; + + struct regmap *regmap; + struct regmap *rtc_regmap; + + const struct max77686_rtc_driver_data *drv_data; + struct regmap_irq_chip_data *rtc_irq_data; + + int rtc_irq; + int virq; +}; + +enum MAX77686_RTC_OP { + MAX77686_RTC_WRITE, + MAX77686_RTC_READ, +}; + +/* These are not registers but just offsets that are mapped to addresses */ +enum max77686_rtc_reg_offset { + REG_RTC_CONTROLM = 0, + REG_RTC_CONTROL, + REG_RTC_UPDATE0, + REG_WTSR_SMPL_CNTL, + REG_RTC_SEC, + REG_RTC_MIN, + REG_RTC_HOUR, + REG_RTC_WEEKDAY, + REG_RTC_MONTH, + REG_RTC_YEAR, + REG_RTC_MONTHDAY, + REG_ALARM1_SEC, + REG_ALARM1_MIN, + REG_ALARM1_HOUR, + REG_ALARM1_WEEKDAY, + REG_ALARM1_MONTH, + REG_ALARM1_YEAR, + REG_ALARM1_DATE, + REG_ALARM2_SEC, + REG_ALARM2_MIN, + REG_ALARM2_HOUR, + REG_ALARM2_WEEKDAY, + REG_ALARM2_MONTH, + REG_ALARM2_YEAR, + REG_ALARM2_DATE, + REG_RTC_AE1, + REG_RTC_END, +}; + +/* Maps RTC registers offset to the MAX77686 register addresses */ +static const unsigned int max77686_map[REG_RTC_END] = { + [REG_RTC_CONTROLM] = MAX77686_RTC_CONTROLM, + [REG_RTC_CONTROL] = MAX77686_RTC_CONTROL, + [REG_RTC_UPDATE0] = MAX77686_RTC_UPDATE0, + [REG_WTSR_SMPL_CNTL] = MAX77686_WTSR_SMPL_CNTL, + [REG_RTC_SEC] = MAX77686_RTC_SEC, + [REG_RTC_MIN] = MAX77686_RTC_MIN, + [REG_RTC_HOUR] = MAX77686_RTC_HOUR, + [REG_RTC_WEEKDAY] = MAX77686_RTC_WEEKDAY, + [REG_RTC_MONTH] = MAX77686_RTC_MONTH, + [REG_RTC_YEAR] = MAX77686_RTC_YEAR, + [REG_RTC_MONTHDAY] = MAX77686_RTC_MONTHDAY, + [REG_ALARM1_SEC] = MAX77686_ALARM1_SEC, + [REG_ALARM1_MIN] = MAX77686_ALARM1_MIN, + [REG_ALARM1_HOUR] = MAX77686_ALARM1_HOUR, + [REG_ALARM1_WEEKDAY] = MAX77686_ALARM1_WEEKDAY, + [REG_ALARM1_MONTH] = MAX77686_ALARM1_MONTH, + [REG_ALARM1_YEAR] = MAX77686_ALARM1_YEAR, + [REG_ALARM1_DATE] = MAX77686_ALARM1_DATE, + [REG_ALARM2_SEC] = MAX77686_ALARM2_SEC, + [REG_ALARM2_MIN] = MAX77686_ALARM2_MIN, + [REG_ALARM2_HOUR] = MAX77686_ALARM2_HOUR, + [REG_ALARM2_WEEKDAY] = MAX77686_ALARM2_WEEKDAY, + [REG_ALARM2_MONTH] = MAX77686_ALARM2_MONTH, + [REG_ALARM2_YEAR] = MAX77686_ALARM2_YEAR, + [REG_ALARM2_DATE] = MAX77686_ALARM2_DATE, + [REG_RTC_AE1] = REG_RTC_NONE, +}; + +static const struct regmap_irq max77686_rtc_irqs[] = { + /* RTC interrupts */ + REGMAP_IRQ_REG(0, 0, MAX77686_RTCINT_RTC60S_MSK), + REGMAP_IRQ_REG(1, 0, MAX77686_RTCINT_RTCA1_MSK), + REGMAP_IRQ_REG(2, 0, MAX77686_RTCINT_RTCA2_MSK), + REGMAP_IRQ_REG(3, 0, MAX77686_RTCINT_SMPL_MSK), + REGMAP_IRQ_REG(4, 0, MAX77686_RTCINT_RTC1S_MSK), + REGMAP_IRQ_REG(5, 0, MAX77686_RTCINT_WTSR_MSK), +}; + +static const struct regmap_irq_chip max77686_rtc_irq_chip = { + .name = "max77686-rtc", + .status_base = MAX77686_RTC_INT, + .mask_base = MAX77686_RTC_INTM, + .num_regs = 1, + .irqs = max77686_rtc_irqs, + .num_irqs = ARRAY_SIZE(max77686_rtc_irqs), +}; + +static const struct regmap_config max77686_rtc_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static const struct max77686_rtc_driver_data max77686_drv_data = { + .delay = 16000, + .mask = 0x7f, + .map = max77686_map, + .alarm_enable_reg = false, + .rtc_irq_from_platform = false, + .alarm_pending_status_reg = MAX77686_REG_STATUS2, + .rtc_i2c_addr = MAX77686_I2C_ADDR_RTC, + .rtc_irq_chip = &max77686_rtc_irq_chip, + .regmap_config = &max77686_rtc_regmap_config, +}; + +static const struct regmap_irq_chip max77714_rtc_irq_chip = { + .name = "max77714-rtc", + .status_base = MAX77686_RTC_INT, + .mask_base = MAX77686_RTC_INTM, + .num_regs = 1, + .irqs = max77686_rtc_irqs, + .num_irqs = ARRAY_SIZE(max77686_rtc_irqs) - 1, /* no WTSR on 77714 */ +}; + +static const struct max77686_rtc_driver_data max77714_drv_data = { + .delay = 16000, + .mask = 0x7f, + .map = max77686_map, + .alarm_enable_reg = false, + .rtc_irq_from_platform = false, + /* On MAX77714 RTCA1 is BIT 1 of RTCINT (0x00). Not supported by this driver. */ + .alarm_pending_status_reg = MAX77686_INVALID_REG, + .rtc_i2c_addr = MAX77714_I2C_ADDR_RTC, + .rtc_irq_chip = &max77714_rtc_irq_chip, + .regmap_config = &max77686_rtc_regmap_config, +}; + +static const struct regmap_config max77620_rtc_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .use_single_write = true, +}; + +static const struct max77686_rtc_driver_data max77620_drv_data = { + .delay = 16000, + .mask = 0x7f, + .map = max77686_map, + .alarm_enable_reg = false, + .rtc_irq_from_platform = true, + .alarm_pending_status_reg = MAX77686_INVALID_REG, + .rtc_i2c_addr = MAX77620_I2C_ADDR_RTC, + .rtc_irq_chip = &max77686_rtc_irq_chip, + .regmap_config = &max77620_rtc_regmap_config, +}; + +static const unsigned int max77802_map[REG_RTC_END] = { + [REG_RTC_CONTROLM] = MAX77802_RTC_CONTROLM, + [REG_RTC_CONTROL] = MAX77802_RTC_CONTROL, + [REG_RTC_UPDATE0] = MAX77802_RTC_UPDATE0, + [REG_WTSR_SMPL_CNTL] = MAX77802_WTSR_SMPL_CNTL, + [REG_RTC_SEC] = MAX77802_RTC_SEC, + [REG_RTC_MIN] = MAX77802_RTC_MIN, + [REG_RTC_HOUR] = MAX77802_RTC_HOUR, + [REG_RTC_WEEKDAY] = MAX77802_RTC_WEEKDAY, + [REG_RTC_MONTH] = MAX77802_RTC_MONTH, + [REG_RTC_YEAR] = MAX77802_RTC_YEAR, + [REG_RTC_MONTHDAY] = MAX77802_RTC_MONTHDAY, + [REG_ALARM1_SEC] = MAX77802_ALARM1_SEC, + [REG_ALARM1_MIN] = MAX77802_ALARM1_MIN, + [REG_ALARM1_HOUR] = MAX77802_ALARM1_HOUR, + [REG_ALARM1_WEEKDAY] = MAX77802_ALARM1_WEEKDAY, + [REG_ALARM1_MONTH] = MAX77802_ALARM1_MONTH, + [REG_ALARM1_YEAR] = MAX77802_ALARM1_YEAR, + [REG_ALARM1_DATE] = MAX77802_ALARM1_DATE, + [REG_ALARM2_SEC] = MAX77802_ALARM2_SEC, + [REG_ALARM2_MIN] = MAX77802_ALARM2_MIN, + [REG_ALARM2_HOUR] = MAX77802_ALARM2_HOUR, + [REG_ALARM2_WEEKDAY] = MAX77802_ALARM2_WEEKDAY, + [REG_ALARM2_MONTH] = MAX77802_ALARM2_MONTH, + [REG_ALARM2_YEAR] = MAX77802_ALARM2_YEAR, + [REG_ALARM2_DATE] = MAX77802_ALARM2_DATE, + [REG_RTC_AE1] = MAX77802_RTC_AE1, +}; + +static const struct regmap_irq_chip max77802_rtc_irq_chip = { + .name = "max77802-rtc", + .status_base = MAX77802_RTC_INT, + .mask_base = MAX77802_RTC_INTM, + .num_regs = 1, + .irqs = max77686_rtc_irqs, /* same masks as 77686 */ + .num_irqs = ARRAY_SIZE(max77686_rtc_irqs), +}; + +static const struct max77686_rtc_driver_data max77802_drv_data = { + .delay = 200, + .mask = 0xff, + .map = max77802_map, + .alarm_enable_reg = true, + .rtc_irq_from_platform = false, + .alarm_pending_status_reg = MAX77686_REG_STATUS2, + .rtc_i2c_addr = MAX77686_INVALID_I2C_ADDR, + .rtc_irq_chip = &max77802_rtc_irq_chip, +}; + +static void max77686_rtc_data_to_tm(u8 *data, struct rtc_time *tm, + struct max77686_rtc_info *info) +{ + u8 mask = info->drv_data->mask; + + tm->tm_sec = data[RTC_SEC] & mask; + tm->tm_min = data[RTC_MIN] & mask; + tm->tm_hour = data[RTC_HOUR] & 0x1f; + + /* Only a single bit is set in data[], so fls() would be equivalent */ + tm->tm_wday = ffs(data[RTC_WEEKDAY] & mask) - 1; + tm->tm_mday = data[RTC_MONTHDAY] & 0x1f; + tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1; + tm->tm_year = data[RTC_YEAR] & mask; + tm->tm_yday = 0; + tm->tm_isdst = 0; + + /* + * MAX77686 uses 1 bit from sec/min/hour/etc RTC registers and the + * year values are just 0..99 so add 100 to support up to 2099. + */ + if (!info->drv_data->alarm_enable_reg) + tm->tm_year += 100; +} + +static int max77686_rtc_tm_to_data(struct rtc_time *tm, u8 *data, + struct max77686_rtc_info *info) +{ + data[RTC_SEC] = tm->tm_sec; + data[RTC_MIN] = tm->tm_min; + data[RTC_HOUR] = tm->tm_hour; + data[RTC_WEEKDAY] = 1 << tm->tm_wday; + data[RTC_MONTHDAY] = tm->tm_mday; + data[RTC_MONTH] = tm->tm_mon + 1; + + if (info->drv_data->alarm_enable_reg) { + data[RTC_YEAR] = tm->tm_year; + return 0; + } + + data[RTC_YEAR] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0; + + if (tm->tm_year < 100) { + dev_err(info->dev, "RTC cannot handle the year %d.\n", + 1900 + tm->tm_year); + return -EINVAL; + } + + return 0; +} + +static int max77686_rtc_update(struct max77686_rtc_info *info, + enum MAX77686_RTC_OP op) +{ + int ret; + unsigned int data; + unsigned long delay = info->drv_data->delay; + + if (op == MAX77686_RTC_WRITE) + data = 1 << RTC_UDR_SHIFT; + else + data = 1 << RTC_RBUDR_SHIFT; + + ret = regmap_update_bits(info->rtc_regmap, + info->drv_data->map[REG_RTC_UPDATE0], + data, data); + if (ret < 0) + dev_err(info->dev, "Fail to write update reg(ret=%d, data=0x%x)\n", + ret, data); + else { + /* Minimum delay required before RTC update. */ + usleep_range(delay, delay * 2); + } + + return ret; +} + +static int max77686_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct max77686_rtc_info *info = dev_get_drvdata(dev); + u8 data[RTC_NR_TIME]; + int ret; + + mutex_lock(&info->lock); + + ret = max77686_rtc_update(info, MAX77686_RTC_READ); + if (ret < 0) + goto out; + + ret = regmap_bulk_read(info->rtc_regmap, + info->drv_data->map[REG_RTC_SEC], + data, ARRAY_SIZE(data)); + if (ret < 0) { + dev_err(info->dev, "Fail to read time reg(%d)\n", ret); + goto out; + } + + max77686_rtc_data_to_tm(data, tm, info); + +out: + mutex_unlock(&info->lock); + return ret; +} + +static int max77686_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct max77686_rtc_info *info = dev_get_drvdata(dev); + u8 data[RTC_NR_TIME]; + int ret; + + ret = max77686_rtc_tm_to_data(tm, data, info); + if (ret < 0) + return ret; + + mutex_lock(&info->lock); + + ret = regmap_bulk_write(info->rtc_regmap, + info->drv_data->map[REG_RTC_SEC], + data, ARRAY_SIZE(data)); + if (ret < 0) { + dev_err(info->dev, "Fail to write time reg(%d)\n", ret); + goto out; + } + + ret = max77686_rtc_update(info, MAX77686_RTC_WRITE); + +out: + mutex_unlock(&info->lock); + return ret; +} + +static int max77686_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct max77686_rtc_info *info = dev_get_drvdata(dev); + u8 data[RTC_NR_TIME]; + unsigned int val; + const unsigned int *map = info->drv_data->map; + int i, ret; + + mutex_lock(&info->lock); + + ret = max77686_rtc_update(info, MAX77686_RTC_READ); + if (ret < 0) + goto out; + + ret = regmap_bulk_read(info->rtc_regmap, map[REG_ALARM1_SEC], + data, ARRAY_SIZE(data)); + if (ret < 0) { + dev_err(info->dev, "Fail to read alarm reg(%d)\n", ret); + goto out; + } + + max77686_rtc_data_to_tm(data, &alrm->time, info); + + alrm->enabled = 0; + + if (info->drv_data->alarm_enable_reg) { + if (map[REG_RTC_AE1] == REG_RTC_NONE) { + ret = -EINVAL; + dev_err(info->dev, + "alarm enable register not set(%d)\n", ret); + goto out; + } + + ret = regmap_read(info->rtc_regmap, map[REG_RTC_AE1], &val); + if (ret < 0) { + dev_err(info->dev, + "fail to read alarm enable(%d)\n", ret); + goto out; + } + + if (val) + alrm->enabled = 1; + } else { + for (i = 0; i < ARRAY_SIZE(data); i++) { + if (data[i] & ALARM_ENABLE_MASK) { + alrm->enabled = 1; + break; + } + } + } + + alrm->pending = 0; + + if (info->drv_data->alarm_pending_status_reg == MAX77686_INVALID_REG) + goto out; + + ret = regmap_read(info->regmap, + info->drv_data->alarm_pending_status_reg, &val); + if (ret < 0) { + dev_err(info->dev, + "Fail to read alarm pending status reg(%d)\n", ret); + goto out; + } + + if (val & (1 << 4)) /* RTCA1 */ + alrm->pending = 1; + +out: + mutex_unlock(&info->lock); + return ret; +} + +static int max77686_rtc_stop_alarm(struct max77686_rtc_info *info) +{ + u8 data[RTC_NR_TIME]; + int ret, i; + struct rtc_time tm; + const unsigned int *map = info->drv_data->map; + + if (!mutex_is_locked(&info->lock)) + dev_warn(info->dev, "%s: should have mutex locked\n", __func__); + + ret = max77686_rtc_update(info, MAX77686_RTC_READ); + if (ret < 0) + goto out; + + if (info->drv_data->alarm_enable_reg) { + if (map[REG_RTC_AE1] == REG_RTC_NONE) { + ret = -EINVAL; + dev_err(info->dev, + "alarm enable register not set(%d)\n", ret); + goto out; + } + + ret = regmap_write(info->rtc_regmap, map[REG_RTC_AE1], 0); + } else { + ret = regmap_bulk_read(info->rtc_regmap, map[REG_ALARM1_SEC], + data, ARRAY_SIZE(data)); + if (ret < 0) { + dev_err(info->dev, "Fail to read alarm reg(%d)\n", ret); + goto out; + } + + max77686_rtc_data_to_tm(data, &tm, info); + + for (i = 0; i < ARRAY_SIZE(data); i++) + data[i] &= ~ALARM_ENABLE_MASK; + + ret = regmap_bulk_write(info->rtc_regmap, map[REG_ALARM1_SEC], + data, ARRAY_SIZE(data)); + } + + if (ret < 0) { + dev_err(info->dev, "Fail to write alarm reg(%d)\n", ret); + goto out; + } + + ret = max77686_rtc_update(info, MAX77686_RTC_WRITE); +out: + return ret; +} + +static int max77686_rtc_start_alarm(struct max77686_rtc_info *info) +{ + u8 data[RTC_NR_TIME]; + int ret; + struct rtc_time tm; + const unsigned int *map = info->drv_data->map; + + if (!mutex_is_locked(&info->lock)) + dev_warn(info->dev, "%s: should have mutex locked\n", __func__); + + ret = max77686_rtc_update(info, MAX77686_RTC_READ); + if (ret < 0) + goto out; + + if (info->drv_data->alarm_enable_reg) { + ret = regmap_write(info->rtc_regmap, map[REG_RTC_AE1], + MAX77802_ALARM_ENABLE_VALUE); + } else { + ret = regmap_bulk_read(info->rtc_regmap, map[REG_ALARM1_SEC], + data, ARRAY_SIZE(data)); + if (ret < 0) { + dev_err(info->dev, "Fail to read alarm reg(%d)\n", ret); + goto out; + } + + max77686_rtc_data_to_tm(data, &tm, info); + + data[RTC_SEC] |= (1 << ALARM_ENABLE_SHIFT); + data[RTC_MIN] |= (1 << ALARM_ENABLE_SHIFT); + data[RTC_HOUR] |= (1 << ALARM_ENABLE_SHIFT); + data[RTC_WEEKDAY] &= ~ALARM_ENABLE_MASK; + if (data[RTC_MONTH] & 0xf) + data[RTC_MONTH] |= (1 << ALARM_ENABLE_SHIFT); + if (data[RTC_YEAR] & info->drv_data->mask) + data[RTC_YEAR] |= (1 << ALARM_ENABLE_SHIFT); + if (data[RTC_MONTHDAY] & 0x1f) + data[RTC_MONTHDAY] |= (1 << ALARM_ENABLE_SHIFT); + + ret = regmap_bulk_write(info->rtc_regmap, map[REG_ALARM1_SEC], + data, ARRAY_SIZE(data)); + } + + if (ret < 0) { + dev_err(info->dev, "Fail to write alarm reg(%d)\n", ret); + goto out; + } + + ret = max77686_rtc_update(info, MAX77686_RTC_WRITE); +out: + return ret; +} + +static int max77686_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct max77686_rtc_info *info = dev_get_drvdata(dev); + u8 data[RTC_NR_TIME]; + int ret; + + ret = max77686_rtc_tm_to_data(&alrm->time, data, info); + if (ret < 0) + return ret; + + mutex_lock(&info->lock); + + ret = max77686_rtc_stop_alarm(info); + if (ret < 0) + goto out; + + ret = regmap_bulk_write(info->rtc_regmap, + info->drv_data->map[REG_ALARM1_SEC], + data, ARRAY_SIZE(data)); + + if (ret < 0) { + dev_err(info->dev, "Fail to write alarm reg(%d)\n", ret); + goto out; + } + + ret = max77686_rtc_update(info, MAX77686_RTC_WRITE); + if (ret < 0) + goto out; + + if (alrm->enabled) + ret = max77686_rtc_start_alarm(info); +out: + mutex_unlock(&info->lock); + return ret; +} + +static int max77686_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct max77686_rtc_info *info = dev_get_drvdata(dev); + int ret; + + mutex_lock(&info->lock); + if (enabled) + ret = max77686_rtc_start_alarm(info); + else + ret = max77686_rtc_stop_alarm(info); + mutex_unlock(&info->lock); + + return ret; +} + +static irqreturn_t max77686_rtc_alarm_irq(int irq, void *data) +{ + struct max77686_rtc_info *info = data; + + dev_dbg(info->dev, "RTC alarm IRQ: %d\n", irq); + + rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops max77686_rtc_ops = { + .read_time = max77686_rtc_read_time, + .set_time = max77686_rtc_set_time, + .read_alarm = max77686_rtc_read_alarm, + .set_alarm = max77686_rtc_set_alarm, + .alarm_irq_enable = max77686_rtc_alarm_irq_enable, +}; + +static int max77686_rtc_init_reg(struct max77686_rtc_info *info) +{ + u8 data[2]; + int ret; + + /* Set RTC control register : Binary mode, 24hour mdoe */ + data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); + data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); + + ret = regmap_bulk_write(info->rtc_regmap, + info->drv_data->map[REG_RTC_CONTROLM], + data, ARRAY_SIZE(data)); + if (ret < 0) { + dev_err(info->dev, "Fail to write controlm reg(%d)\n", ret); + return ret; + } + + ret = max77686_rtc_update(info, MAX77686_RTC_WRITE); + return ret; +} + +static void max77686_rtc_release_dev(void *client) +{ + i2c_unregister_device(client); +} + +static int max77686_init_rtc_regmap(struct max77686_rtc_info *info) +{ + struct device *parent = info->dev->parent; + struct i2c_client *parent_i2c = to_i2c_client(parent); + struct i2c_client *client; + int ret; + + if (info->drv_data->rtc_irq_from_platform) { + struct platform_device *pdev = to_platform_device(info->dev); + + info->rtc_irq = platform_get_irq(pdev, 0); + if (info->rtc_irq < 0) + return info->rtc_irq; + } else { + info->rtc_irq = parent_i2c->irq; + } + + info->regmap = dev_get_regmap(parent, NULL); + if (!info->regmap) + return dev_err_probe(info->dev, -ENODEV, + "Failed to get rtc regmap\n"); + + if (info->drv_data->rtc_i2c_addr == MAX77686_INVALID_I2C_ADDR) { + info->rtc_regmap = info->regmap; + goto add_rtc_irq; + } + + client = i2c_new_ancillary_device(parent_i2c, "rtc", + info->drv_data->rtc_i2c_addr); + if (IS_ERR(client)) + return dev_err_probe(info->dev, PTR_ERR(client), + "Failed to allocate I2C device for RTC\n"); + + ret = devm_add_action_or_reset(info->dev, max77686_rtc_release_dev, + client); + if (ret) + return ret; + + info->rtc_regmap = devm_regmap_init_i2c(client, + info->drv_data->regmap_config); + if (IS_ERR(info->rtc_regmap)) + return dev_err_probe(info->dev, PTR_ERR(info->rtc_regmap), + "Failed to allocate RTC regmap\n"); + +add_rtc_irq: + ret = regmap_add_irq_chip(info->rtc_regmap, info->rtc_irq, + IRQF_ONESHOT | IRQF_SHARED, + 0, info->drv_data->rtc_irq_chip, + &info->rtc_irq_data); + if (ret < 0) + return dev_err_probe(info->dev, ret, + "Failed to add RTC irq chip\n"); + + return 0; +} + +static int max77686_rtc_probe(struct platform_device *pdev) +{ + struct max77686_rtc_info *info; + const struct platform_device_id *id = platform_get_device_id(pdev); + int ret; + + info = devm_kzalloc(&pdev->dev, sizeof(struct max77686_rtc_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + mutex_init(&info->lock); + info->dev = &pdev->dev; + info->drv_data = (const struct max77686_rtc_driver_data *) + id->driver_data; + + ret = max77686_init_rtc_regmap(info); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, info); + + ret = max77686_rtc_init_reg(info); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to initialize RTC reg:%d\n", ret); + goto err_rtc; + } + + device_init_wakeup(&pdev->dev, true); + + info->rtc_dev = devm_rtc_device_register(&pdev->dev, id->name, + &max77686_rtc_ops, THIS_MODULE); + + if (IS_ERR(info->rtc_dev)) { + ret = PTR_ERR(info->rtc_dev); + dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret); + if (ret == 0) + ret = -EINVAL; + goto err_rtc; + } + + info->virq = regmap_irq_get_virq(info->rtc_irq_data, + MAX77686_RTCIRQ_RTCA1); + if (info->virq <= 0) { + ret = -ENXIO; + goto err_rtc; + } + + ret = request_threaded_irq(info->virq, NULL, max77686_rtc_alarm_irq, 0, + "rtc-alarm1", info); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", + info->virq, ret); + goto err_rtc; + } + + return 0; + +err_rtc: + regmap_del_irq_chip(info->rtc_irq, info->rtc_irq_data); + + return ret; +} + +static void max77686_rtc_remove(struct platform_device *pdev) +{ + struct max77686_rtc_info *info = platform_get_drvdata(pdev); + + free_irq(info->virq, info); + regmap_del_irq_chip(info->rtc_irq, info->rtc_irq_data); +} + +#ifdef CONFIG_PM_SLEEP +static int max77686_rtc_suspend(struct device *dev) +{ + struct max77686_rtc_info *info = dev_get_drvdata(dev); + int ret = 0; + + if (device_may_wakeup(dev)) { + struct max77686_rtc_info *info = dev_get_drvdata(dev); + + ret = enable_irq_wake(info->virq); + } + + /* + * If the main IRQ (not virtual) is the parent IRQ, then it must be + * disabled during suspend because if it happens while suspended it + * will be handled before resuming I2C. + * + * Since Main IRQ is shared, all its users should disable it to be sure + * it won't fire while one of them is still suspended. + */ + if (!info->drv_data->rtc_irq_from_platform) + disable_irq(info->rtc_irq); + + return ret; +} + +static int max77686_rtc_resume(struct device *dev) +{ + struct max77686_rtc_info *info = dev_get_drvdata(dev); + + if (!info->drv_data->rtc_irq_from_platform) + enable_irq(info->rtc_irq); + + if (device_may_wakeup(dev)) { + struct max77686_rtc_info *info = dev_get_drvdata(dev); + + return disable_irq_wake(info->virq); + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(max77686_rtc_pm_ops, + max77686_rtc_suspend, max77686_rtc_resume); + +static const struct platform_device_id rtc_id[] = { + { .name = "max77686-rtc", .driver_data = (kernel_ulong_t)&max77686_drv_data }, + { .name = "max77802-rtc", .driver_data = (kernel_ulong_t)&max77802_drv_data }, + { .name = "max77620-rtc", .driver_data = (kernel_ulong_t)&max77620_drv_data }, + { .name = "max77714-rtc", .driver_data = (kernel_ulong_t)&max77714_drv_data }, + { } +}; +MODULE_DEVICE_TABLE(platform, rtc_id); + +static struct platform_driver max77686_rtc_driver = { + .driver = { + .name = "max77686-rtc", + .pm = &max77686_rtc_pm_ops, + }, + .probe = max77686_rtc_probe, + .remove = max77686_rtc_remove, + .id_table = rtc_id, +}; + +module_platform_driver(max77686_rtc_driver); + +MODULE_DESCRIPTION("Maxim MAX77686 RTC driver"); +MODULE_AUTHOR("Chiwoong Byun "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-max8907.c b/drivers/rtc/rtc-max8907.c new file mode 100644 index 000000000..af97140dd --- /dev/null +++ b/drivers/rtc/rtc-max8907.c @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * RTC driver for Maxim MAX8907 + * + * Copyright (c) 2011-2012, NVIDIA Corporation. + * + * Based on drivers/rtc/rtc-max8925.c, + * Copyright (C) 2009-2010 Marvell International Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include + +enum { + RTC_SEC = 0, + RTC_MIN, + RTC_HOUR, + RTC_WEEKDAY, + RTC_DATE, + RTC_MONTH, + RTC_YEAR1, + RTC_YEAR2, +}; + +#define TIME_NUM 8 +#define ALARM_1SEC (1 << 7) +#define HOUR_12 (1 << 7) +#define HOUR_AM_PM (1 << 5) +#define ALARM0_IRQ (1 << 3) +#define ALARM1_IRQ (1 << 2) +#define ALARM0_STATUS (1 << 2) +#define ALARM1_STATUS (1 << 1) + +struct max8907_rtc { + struct max8907 *max8907; + struct regmap *regmap; + struct rtc_device *rtc_dev; + int irq; +}; + +static irqreturn_t max8907_irq_handler(int irq, void *data) +{ + struct max8907_rtc *rtc = data; + + regmap_write(rtc->regmap, MAX8907_REG_ALARM0_CNTL, 0); + + rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static void regs_to_tm(u8 *regs, struct rtc_time *tm) +{ + tm->tm_year = bcd2bin(regs[RTC_YEAR2]) * 100 + + bcd2bin(regs[RTC_YEAR1]) - 1900; + tm->tm_mon = bcd2bin(regs[RTC_MONTH] & 0x1f) - 1; + tm->tm_mday = bcd2bin(regs[RTC_DATE] & 0x3f); + tm->tm_wday = (regs[RTC_WEEKDAY] & 0x07); + if (regs[RTC_HOUR] & HOUR_12) { + tm->tm_hour = bcd2bin(regs[RTC_HOUR] & 0x01f); + if (tm->tm_hour == 12) + tm->tm_hour = 0; + if (regs[RTC_HOUR] & HOUR_AM_PM) + tm->tm_hour += 12; + } else { + tm->tm_hour = bcd2bin(regs[RTC_HOUR] & 0x03f); + } + tm->tm_min = bcd2bin(regs[RTC_MIN] & 0x7f); + tm->tm_sec = bcd2bin(regs[RTC_SEC] & 0x7f); +} + +static void tm_to_regs(struct rtc_time *tm, u8 *regs) +{ + u8 high, low; + + high = (tm->tm_year + 1900) / 100; + low = tm->tm_year % 100; + regs[RTC_YEAR2] = bin2bcd(high); + regs[RTC_YEAR1] = bin2bcd(low); + regs[RTC_MONTH] = bin2bcd(tm->tm_mon + 1); + regs[RTC_DATE] = bin2bcd(tm->tm_mday); + regs[RTC_WEEKDAY] = tm->tm_wday; + regs[RTC_HOUR] = bin2bcd(tm->tm_hour); + regs[RTC_MIN] = bin2bcd(tm->tm_min); + regs[RTC_SEC] = bin2bcd(tm->tm_sec); +} + +static int max8907_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct max8907_rtc *rtc = dev_get_drvdata(dev); + u8 regs[TIME_NUM]; + int ret; + + ret = regmap_bulk_read(rtc->regmap, MAX8907_REG_RTC_SEC, regs, + TIME_NUM); + if (ret < 0) + return ret; + + regs_to_tm(regs, tm); + + return 0; +} + +static int max8907_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct max8907_rtc *rtc = dev_get_drvdata(dev); + u8 regs[TIME_NUM]; + + tm_to_regs(tm, regs); + + return regmap_bulk_write(rtc->regmap, MAX8907_REG_RTC_SEC, regs, + TIME_NUM); +} + +static int max8907_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct max8907_rtc *rtc = dev_get_drvdata(dev); + u8 regs[TIME_NUM]; + unsigned int val; + int ret; + + ret = regmap_bulk_read(rtc->regmap, MAX8907_REG_ALARM0_SEC, regs, + TIME_NUM); + if (ret < 0) + return ret; + + regs_to_tm(regs, &alrm->time); + + ret = regmap_read(rtc->regmap, MAX8907_REG_ALARM0_CNTL, &val); + if (ret < 0) + return ret; + + alrm->enabled = !!(val & 0x7f); + + return 0; +} + +static int max8907_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct max8907_rtc *rtc = dev_get_drvdata(dev); + u8 regs[TIME_NUM]; + int ret; + + tm_to_regs(&alrm->time, regs); + + /* Disable alarm while we update the target time */ + ret = regmap_write(rtc->regmap, MAX8907_REG_ALARM0_CNTL, 0); + if (ret < 0) + return ret; + + ret = regmap_bulk_write(rtc->regmap, MAX8907_REG_ALARM0_SEC, regs, + TIME_NUM); + if (ret < 0) + return ret; + + if (alrm->enabled) + ret = regmap_write(rtc->regmap, MAX8907_REG_ALARM0_CNTL, 0x77); + + return ret; +} + +static const struct rtc_class_ops max8907_rtc_ops = { + .read_time = max8907_rtc_read_time, + .set_time = max8907_rtc_set_time, + .read_alarm = max8907_rtc_read_alarm, + .set_alarm = max8907_rtc_set_alarm, +}; + +static int max8907_rtc_probe(struct platform_device *pdev) +{ + struct max8907 *max8907 = dev_get_drvdata(pdev->dev.parent); + struct max8907_rtc *rtc; + int ret; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + platform_set_drvdata(pdev, rtc); + + rtc->max8907 = max8907; + rtc->regmap = max8907->regmap_rtc; + + rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, "max8907-rtc", + &max8907_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc->rtc_dev)) { + ret = PTR_ERR(rtc->rtc_dev); + dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret); + return ret; + } + + rtc->irq = regmap_irq_get_virq(max8907->irqc_rtc, + MAX8907_IRQ_RTC_ALARM0); + if (rtc->irq < 0) + return rtc->irq; + + ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL, + max8907_irq_handler, + IRQF_ONESHOT, "max8907-alarm0", rtc); + if (ret < 0) + dev_err(&pdev->dev, "Failed to request IRQ%d: %d\n", + rtc->irq, ret); + + return ret; +} + +static struct platform_driver max8907_rtc_driver = { + .driver = { + .name = "max8907-rtc", + }, + .probe = max8907_rtc_probe, +}; +module_platform_driver(max8907_rtc_driver); + +MODULE_DESCRIPTION("Maxim MAX8907 RTC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-max8925.c b/drivers/rtc/rtc-max8925.c new file mode 100644 index 000000000..6ce8afbea --- /dev/null +++ b/drivers/rtc/rtc-max8925.c @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * RTC driver for Maxim MAX8925 + * + * Copyright (C) 2009-2010 Marvell International Ltd. + * Haojian Zhuang + */ + +#include +#include +#include +#include +#include +#include + +enum { + RTC_SEC = 0, + RTC_MIN, + RTC_HOUR, + RTC_WEEKDAY, + RTC_DATE, + RTC_MONTH, + RTC_YEAR1, + RTC_YEAR2, +}; + +#define MAX8925_RTC_SEC 0x00 +#define MAX8925_RTC_MIN 0x01 +#define MAX8925_RTC_HOUR 0x02 +#define MAX8925_RTC_WEEKDAY 0x03 +#define MAX8925_RTC_DATE 0x04 +#define MAX8925_RTC_MONTH 0x05 +#define MAX8925_RTC_YEAR1 0x06 +#define MAX8925_RTC_YEAR2 0x07 +#define MAX8925_ALARM0_SEC 0x08 +#define MAX8925_ALARM0_MIN 0x09 +#define MAX8925_ALARM0_HOUR 0x0a +#define MAX8925_ALARM0_WEEKDAY 0x0b +#define MAX8925_ALARM0_DATE 0x0c +#define MAX8925_ALARM0_MON 0x0d +#define MAX8925_ALARM0_YEAR1 0x0e +#define MAX8925_ALARM0_YEAR2 0x0f +#define MAX8925_ALARM1_SEC 0x10 +#define MAX8925_ALARM1_MIN 0x11 +#define MAX8925_ALARM1_HOUR 0x12 +#define MAX8925_ALARM1_WEEKDAY 0x13 +#define MAX8925_ALARM1_DATE 0x14 +#define MAX8925_ALARM1_MON 0x15 +#define MAX8925_ALARM1_YEAR1 0x16 +#define MAX8925_ALARM1_YEAR2 0x17 +#define MAX8925_RTC_CNTL 0x1b +#define MAX8925_RTC_STATUS 0x20 + +#define TIME_NUM 8 +#define ALARM_1SEC (1 << 7) +#define HOUR_12 (1 << 7) +#define HOUR_AM_PM (1 << 5) +#define ALARM0_IRQ (1 << 3) +#define ALARM1_IRQ (1 << 2) +#define ALARM0_STATUS (1 << 2) +#define ALARM1_STATUS (1 << 1) + + +struct max8925_rtc_info { + struct rtc_device *rtc_dev; + struct max8925_chip *chip; + struct i2c_client *rtc; + struct device *dev; + int irq; +}; + +static irqreturn_t rtc_update_handler(int irq, void *data) +{ + struct max8925_rtc_info *info = (struct max8925_rtc_info *)data; + + /* disable ALARM0 except for 1SEC alarm */ + max8925_set_bits(info->rtc, MAX8925_ALARM0_CNTL, 0x7f, 0); + rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF); + return IRQ_HANDLED; +} + +static int tm_calc(struct rtc_time *tm, unsigned char *buf, int len) +{ + if (len < TIME_NUM) + return -EINVAL; + tm->tm_year = (buf[RTC_YEAR2] >> 4) * 1000 + + (buf[RTC_YEAR2] & 0xf) * 100 + + (buf[RTC_YEAR1] >> 4) * 10 + + (buf[RTC_YEAR1] & 0xf); + tm->tm_year -= 1900; + tm->tm_mon = ((buf[RTC_MONTH] >> 4) & 0x01) * 10 + + (buf[RTC_MONTH] & 0x0f); + tm->tm_mday = ((buf[RTC_DATE] >> 4) & 0x03) * 10 + + (buf[RTC_DATE] & 0x0f); + tm->tm_wday = buf[RTC_WEEKDAY] & 0x07; + if (buf[RTC_HOUR] & HOUR_12) { + tm->tm_hour = ((buf[RTC_HOUR] >> 4) & 0x1) * 10 + + (buf[RTC_HOUR] & 0x0f); + if (buf[RTC_HOUR] & HOUR_AM_PM) + tm->tm_hour += 12; + } else + tm->tm_hour = ((buf[RTC_HOUR] >> 4) & 0x03) * 10 + + (buf[RTC_HOUR] & 0x0f); + tm->tm_min = ((buf[RTC_MIN] >> 4) & 0x7) * 10 + + (buf[RTC_MIN] & 0x0f); + tm->tm_sec = ((buf[RTC_SEC] >> 4) & 0x7) * 10 + + (buf[RTC_SEC] & 0x0f); + return 0; +} + +static int data_calc(unsigned char *buf, struct rtc_time *tm, int len) +{ + unsigned char high, low; + + if (len < TIME_NUM) + return -EINVAL; + + high = (tm->tm_year + 1900) / 1000; + low = (tm->tm_year + 1900) / 100; + low = low - high * 10; + buf[RTC_YEAR2] = (high << 4) + low; + high = (tm->tm_year + 1900) / 10; + low = tm->tm_year + 1900; + low = low - high * 10; + high = high - (high / 10) * 10; + buf[RTC_YEAR1] = (high << 4) + low; + high = tm->tm_mon / 10; + low = tm->tm_mon; + low = low - high * 10; + buf[RTC_MONTH] = (high << 4) + low; + high = tm->tm_mday / 10; + low = tm->tm_mday; + low = low - high * 10; + buf[RTC_DATE] = (high << 4) + low; + buf[RTC_WEEKDAY] = tm->tm_wday; + high = tm->tm_hour / 10; + low = tm->tm_hour; + low = low - high * 10; + buf[RTC_HOUR] = (high << 4) + low; + high = tm->tm_min / 10; + low = tm->tm_min; + low = low - high * 10; + buf[RTC_MIN] = (high << 4) + low; + high = tm->tm_sec / 10; + low = tm->tm_sec; + low = low - high * 10; + buf[RTC_SEC] = (high << 4) + low; + return 0; +} + +static int max8925_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct max8925_rtc_info *info = dev_get_drvdata(dev); + unsigned char buf[TIME_NUM]; + int ret; + + ret = max8925_bulk_read(info->rtc, MAX8925_RTC_SEC, TIME_NUM, buf); + if (ret < 0) + goto out; + ret = tm_calc(tm, buf, TIME_NUM); +out: + return ret; +} + +static int max8925_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct max8925_rtc_info *info = dev_get_drvdata(dev); + unsigned char buf[TIME_NUM]; + int ret; + + ret = data_calc(buf, tm, TIME_NUM); + if (ret < 0) + goto out; + ret = max8925_bulk_write(info->rtc, MAX8925_RTC_SEC, TIME_NUM, buf); +out: + return ret; +} + +static int max8925_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct max8925_rtc_info *info = dev_get_drvdata(dev); + unsigned char buf[TIME_NUM]; + int ret; + + ret = max8925_bulk_read(info->rtc, MAX8925_ALARM0_SEC, TIME_NUM, buf); + if (ret < 0) + goto out; + ret = tm_calc(&alrm->time, buf, TIME_NUM); + if (ret < 0) + goto out; + ret = max8925_reg_read(info->rtc, MAX8925_RTC_IRQ_MASK); + if (ret < 0) + goto out; + if (ret & ALARM0_IRQ) { + alrm->enabled = 0; + } else { + ret = max8925_reg_read(info->rtc, MAX8925_ALARM0_CNTL); + if (ret < 0) + goto out; + if (!ret) + alrm->enabled = 0; + else + alrm->enabled = 1; + } + ret = max8925_reg_read(info->rtc, MAX8925_RTC_STATUS); + if (ret < 0) + goto out; + if (ret & ALARM0_STATUS) + alrm->pending = 1; + else + alrm->pending = 0; + return 0; +out: + return ret; +} + +static int max8925_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct max8925_rtc_info *info = dev_get_drvdata(dev); + unsigned char buf[TIME_NUM]; + int ret; + + ret = data_calc(buf, &alrm->time, TIME_NUM); + if (ret < 0) + goto out; + ret = max8925_bulk_write(info->rtc, MAX8925_ALARM0_SEC, TIME_NUM, buf); + if (ret < 0) + goto out; + if (alrm->enabled) + /* only enable alarm on year/month/day/hour/min/sec */ + ret = max8925_reg_write(info->rtc, MAX8925_ALARM0_CNTL, 0x77); + else + ret = max8925_reg_write(info->rtc, MAX8925_ALARM0_CNTL, 0x0); +out: + return ret; +} + +static const struct rtc_class_ops max8925_rtc_ops = { + .read_time = max8925_rtc_read_time, + .set_time = max8925_rtc_set_time, + .read_alarm = max8925_rtc_read_alarm, + .set_alarm = max8925_rtc_set_alarm, +}; + +static int max8925_rtc_probe(struct platform_device *pdev) +{ + struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct max8925_rtc_info *info; + int ret; + + info = devm_kzalloc(&pdev->dev, sizeof(struct max8925_rtc_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + info->chip = chip; + info->rtc = chip->rtc; + info->dev = &pdev->dev; + info->irq = platform_get_irq(pdev, 0); + + ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL, + rtc_update_handler, IRQF_ONESHOT, + "rtc-alarm0", info); + if (ret < 0) { + dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", + info->irq, ret); + return ret; + } + + dev_set_drvdata(&pdev->dev, info); + /* XXX - isn't this redundant? */ + platform_set_drvdata(pdev, info); + + device_init_wakeup(&pdev->dev, true); + + info->rtc_dev = devm_rtc_device_register(&pdev->dev, "max8925-rtc", + &max8925_rtc_ops, THIS_MODULE); + ret = PTR_ERR(info->rtc_dev); + if (IS_ERR(info->rtc_dev)) { + dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret); + return ret; + } + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int max8925_rtc_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); + + if (device_may_wakeup(dev)) + chip->wakeup_flag |= 1 << MAX8925_IRQ_RTC_ALARM0; + return 0; +} +static int max8925_rtc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); + + if (device_may_wakeup(dev)) + chip->wakeup_flag &= ~(1 << MAX8925_IRQ_RTC_ALARM0); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(max8925_rtc_pm_ops, max8925_rtc_suspend, max8925_rtc_resume); + +static struct platform_driver max8925_rtc_driver = { + .driver = { + .name = "max8925-rtc", + .pm = &max8925_rtc_pm_ops, + }, + .probe = max8925_rtc_probe, +}; + +module_platform_driver(max8925_rtc_driver); + +MODULE_DESCRIPTION("Maxim MAX8925 RTC driver"); +MODULE_AUTHOR("Haojian Zhuang "); +MODULE_LICENSE("GPL"); + diff --git a/drivers/rtc/rtc-max8997.c b/drivers/rtc/rtc-max8997.c new file mode 100644 index 000000000..89203c92e --- /dev/null +++ b/drivers/rtc/rtc-max8997.c @@ -0,0 +1,533 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// RTC driver for Maxim MAX8997 +// +// Copyright (C) 2013 Samsung Electronics Co.Ltd +// +// based on rtc-max8998.c + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Module parameter for WTSR function control */ +static int wtsr_en = 1; +module_param(wtsr_en, int, 0444); +MODULE_PARM_DESC(wtsr_en, "Watchdog Timeout & Software Reset (default=on)"); +/* Module parameter for SMPL function control */ +static int smpl_en = 1; +module_param(smpl_en, int, 0444); +MODULE_PARM_DESC(smpl_en, "Sudden Momentary Power Loss (default=on)"); + +/* RTC Control Register */ +#define BCD_EN_SHIFT 0 +#define BCD_EN_MASK (1 << BCD_EN_SHIFT) +#define MODEL24_SHIFT 1 +#define MODEL24_MASK (1 << MODEL24_SHIFT) +/* RTC Update Register1 */ +#define RTC_UDR_SHIFT 0 +#define RTC_UDR_MASK (1 << RTC_UDR_SHIFT) +/* WTSR and SMPL Register */ +#define WTSRT_SHIFT 0 +#define SMPLT_SHIFT 2 +#define WTSR_EN_SHIFT 6 +#define SMPL_EN_SHIFT 7 +#define WTSRT_MASK (3 << WTSRT_SHIFT) +#define SMPLT_MASK (3 << SMPLT_SHIFT) +#define WTSR_EN_MASK (1 << WTSR_EN_SHIFT) +#define SMPL_EN_MASK (1 << SMPL_EN_SHIFT) +/* RTC Hour register */ +#define HOUR_PM_SHIFT 6 +#define HOUR_PM_MASK (1 << HOUR_PM_SHIFT) +/* RTC Alarm Enable */ +#define ALARM_ENABLE_SHIFT 7 +#define ALARM_ENABLE_MASK (1 << ALARM_ENABLE_SHIFT) + +enum { + RTC_SEC = 0, + RTC_MIN, + RTC_HOUR, + RTC_WEEKDAY, + RTC_MONTH, + RTC_YEAR, + RTC_DATE, + RTC_NR_TIME +}; + +struct max8997_rtc_info { + struct device *dev; + struct max8997_dev *max8997; + struct i2c_client *rtc; + struct rtc_device *rtc_dev; + struct mutex lock; + int virq; + int rtc_24hr_mode; +}; + +static void max8997_rtc_data_to_tm(u8 *data, struct rtc_time *tm, + int rtc_24hr_mode) +{ + tm->tm_sec = data[RTC_SEC] & 0x7f; + tm->tm_min = data[RTC_MIN] & 0x7f; + if (rtc_24hr_mode) + tm->tm_hour = data[RTC_HOUR] & 0x1f; + else { + tm->tm_hour = data[RTC_HOUR] & 0x0f; + if (data[RTC_HOUR] & HOUR_PM_MASK) + tm->tm_hour += 12; + } + + tm->tm_wday = fls(data[RTC_WEEKDAY] & 0x7f) - 1; + tm->tm_mday = data[RTC_DATE] & 0x1f; + tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1; + tm->tm_year = (data[RTC_YEAR] & 0x7f) + 100; + tm->tm_yday = 0; + tm->tm_isdst = 0; +} + +static int max8997_rtc_tm_to_data(struct rtc_time *tm, u8 *data) +{ + data[RTC_SEC] = tm->tm_sec; + data[RTC_MIN] = tm->tm_min; + data[RTC_HOUR] = tm->tm_hour; + data[RTC_WEEKDAY] = 1 << tm->tm_wday; + data[RTC_DATE] = tm->tm_mday; + data[RTC_MONTH] = tm->tm_mon + 1; + data[RTC_YEAR] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0; + + if (tm->tm_year < 100) { + pr_warn("RTC cannot handle the year %d. Assume it's 2000.\n", + 1900 + tm->tm_year); + return -EINVAL; + } + return 0; +} + +static inline int max8997_rtc_set_update_reg(struct max8997_rtc_info *info) +{ + int ret; + + ret = max8997_write_reg(info->rtc, MAX8997_RTC_UPDATE1, + RTC_UDR_MASK); + if (ret < 0) + dev_err(info->dev, "%s: fail to write update reg(%d)\n", + __func__, ret); + else { + /* Minimum 16ms delay required before RTC update. + * Otherwise, we may read and update based on out-of-date + * value */ + msleep(20); + } + + return ret; +} + +static int max8997_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct max8997_rtc_info *info = dev_get_drvdata(dev); + u8 data[RTC_NR_TIME]; + int ret; + + mutex_lock(&info->lock); + ret = max8997_bulk_read(info->rtc, MAX8997_RTC_SEC, RTC_NR_TIME, data); + mutex_unlock(&info->lock); + + if (ret < 0) { + dev_err(info->dev, "%s: fail to read time reg(%d)\n", __func__, + ret); + return ret; + } + + max8997_rtc_data_to_tm(data, tm, info->rtc_24hr_mode); + + return 0; +} + +static int max8997_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct max8997_rtc_info *info = dev_get_drvdata(dev); + u8 data[RTC_NR_TIME]; + int ret; + + ret = max8997_rtc_tm_to_data(tm, data); + if (ret < 0) + return ret; + + mutex_lock(&info->lock); + + ret = max8997_bulk_write(info->rtc, MAX8997_RTC_SEC, RTC_NR_TIME, data); + if (ret < 0) { + dev_err(info->dev, "%s: fail to write time reg(%d)\n", __func__, + ret); + goto out; + } + + ret = max8997_rtc_set_update_reg(info); +out: + mutex_unlock(&info->lock); + return ret; +} + +static int max8997_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct max8997_rtc_info *info = dev_get_drvdata(dev); + u8 data[RTC_NR_TIME]; + u8 val; + int i, ret; + + mutex_lock(&info->lock); + + ret = max8997_bulk_read(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME, + data); + if (ret < 0) { + dev_err(info->dev, "%s:%d fail to read alarm reg(%d)\n", + __func__, __LINE__, ret); + goto out; + } + + max8997_rtc_data_to_tm(data, &alrm->time, info->rtc_24hr_mode); + + alrm->enabled = 0; + for (i = 0; i < RTC_NR_TIME; i++) { + if (data[i] & ALARM_ENABLE_MASK) { + alrm->enabled = 1; + break; + } + } + + alrm->pending = 0; + ret = max8997_read_reg(info->max8997->i2c, MAX8997_REG_STATUS1, &val); + if (ret < 0) { + dev_err(info->dev, "%s:%d fail to read status1 reg(%d)\n", + __func__, __LINE__, ret); + goto out; + } + + if (val & (1 << 4)) /* RTCA1 */ + alrm->pending = 1; + +out: + mutex_unlock(&info->lock); + return ret; +} + +static int max8997_rtc_stop_alarm(struct max8997_rtc_info *info) +{ + u8 data[RTC_NR_TIME]; + int ret, i; + + if (!mutex_is_locked(&info->lock)) + dev_warn(info->dev, "%s: should have mutex locked\n", __func__); + + ret = max8997_bulk_read(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME, + data); + if (ret < 0) { + dev_err(info->dev, "%s: fail to read alarm reg(%d)\n", + __func__, ret); + goto out; + } + + for (i = 0; i < RTC_NR_TIME; i++) + data[i] &= ~ALARM_ENABLE_MASK; + + ret = max8997_bulk_write(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME, + data); + if (ret < 0) { + dev_err(info->dev, "%s: fail to write alarm reg(%d)\n", + __func__, ret); + goto out; + } + + ret = max8997_rtc_set_update_reg(info); +out: + return ret; +} + +static int max8997_rtc_start_alarm(struct max8997_rtc_info *info) +{ + u8 data[RTC_NR_TIME]; + int ret; + + if (!mutex_is_locked(&info->lock)) + dev_warn(info->dev, "%s: should have mutex locked\n", __func__); + + ret = max8997_bulk_read(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME, + data); + if (ret < 0) { + dev_err(info->dev, "%s: fail to read alarm reg(%d)\n", + __func__, ret); + goto out; + } + + data[RTC_SEC] |= (1 << ALARM_ENABLE_SHIFT); + data[RTC_MIN] |= (1 << ALARM_ENABLE_SHIFT); + data[RTC_HOUR] |= (1 << ALARM_ENABLE_SHIFT); + data[RTC_WEEKDAY] &= ~ALARM_ENABLE_MASK; + if (data[RTC_MONTH] & 0xf) + data[RTC_MONTH] |= (1 << ALARM_ENABLE_SHIFT); + if (data[RTC_YEAR] & 0x7f) + data[RTC_YEAR] |= (1 << ALARM_ENABLE_SHIFT); + if (data[RTC_DATE] & 0x1f) + data[RTC_DATE] |= (1 << ALARM_ENABLE_SHIFT); + + ret = max8997_bulk_write(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME, + data); + if (ret < 0) { + dev_err(info->dev, "%s: fail to write alarm reg(%d)\n", + __func__, ret); + goto out; + } + + ret = max8997_rtc_set_update_reg(info); +out: + return ret; +} +static int max8997_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct max8997_rtc_info *info = dev_get_drvdata(dev); + u8 data[RTC_NR_TIME]; + int ret; + + ret = max8997_rtc_tm_to_data(&alrm->time, data); + if (ret < 0) + return ret; + + dev_info(info->dev, "%s: %d-%02d-%02d %02d:%02d:%02d\n", __func__, + data[RTC_YEAR] + 2000, data[RTC_MONTH], data[RTC_DATE], + data[RTC_HOUR], data[RTC_MIN], data[RTC_SEC]); + + mutex_lock(&info->lock); + + ret = max8997_rtc_stop_alarm(info); + if (ret < 0) + goto out; + + ret = max8997_bulk_write(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME, + data); + if (ret < 0) { + dev_err(info->dev, "%s: fail to write alarm reg(%d)\n", + __func__, ret); + goto out; + } + + ret = max8997_rtc_set_update_reg(info); + if (ret < 0) + goto out; + + if (alrm->enabled) + ret = max8997_rtc_start_alarm(info); +out: + mutex_unlock(&info->lock); + return ret; +} + +static int max8997_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct max8997_rtc_info *info = dev_get_drvdata(dev); + int ret; + + mutex_lock(&info->lock); + if (enabled) + ret = max8997_rtc_start_alarm(info); + else + ret = max8997_rtc_stop_alarm(info); + mutex_unlock(&info->lock); + + return ret; +} + +static irqreturn_t max8997_rtc_alarm_irq(int irq, void *data) +{ + struct max8997_rtc_info *info = data; + + dev_info(info->dev, "%s:irq(%d)\n", __func__, irq); + + rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops max8997_rtc_ops = { + .read_time = max8997_rtc_read_time, + .set_time = max8997_rtc_set_time, + .read_alarm = max8997_rtc_read_alarm, + .set_alarm = max8997_rtc_set_alarm, + .alarm_irq_enable = max8997_rtc_alarm_irq_enable, +}; + +static void max8997_rtc_enable_wtsr(struct max8997_rtc_info *info, bool enable) +{ + int ret; + u8 val, mask; + + if (!wtsr_en) + return; + + if (enable) + val = (1 << WTSR_EN_SHIFT) | (3 << WTSRT_SHIFT); + else + val = 0; + + mask = WTSR_EN_MASK | WTSRT_MASK; + + dev_info(info->dev, "%s: %s WTSR\n", __func__, + enable ? "enable" : "disable"); + + ret = max8997_update_reg(info->rtc, MAX8997_RTC_WTSR_SMPL, val, mask); + if (ret < 0) { + dev_err(info->dev, "%s: fail to update WTSR reg(%d)\n", + __func__, ret); + return; + } + + max8997_rtc_set_update_reg(info); +} + +static void max8997_rtc_enable_smpl(struct max8997_rtc_info *info, bool enable) +{ + int ret; + u8 val, mask; + + if (!smpl_en) + return; + + if (enable) + val = (1 << SMPL_EN_SHIFT) | (0 << SMPLT_SHIFT); + else + val = 0; + + mask = SMPL_EN_MASK | SMPLT_MASK; + + dev_info(info->dev, "%s: %s SMPL\n", __func__, + enable ? "enable" : "disable"); + + ret = max8997_update_reg(info->rtc, MAX8997_RTC_WTSR_SMPL, val, mask); + if (ret < 0) { + dev_err(info->dev, "%s: fail to update SMPL reg(%d)\n", + __func__, ret); + return; + } + + max8997_rtc_set_update_reg(info); + + val = 0; + max8997_read_reg(info->rtc, MAX8997_RTC_WTSR_SMPL, &val); + pr_info("WTSR_SMPL(0x%02x)\n", val); +} + +static int max8997_rtc_init_reg(struct max8997_rtc_info *info) +{ + u8 data[2]; + int ret; + + /* Set RTC control register : Binary mode, 24hour mdoe */ + data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); + data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); + + info->rtc_24hr_mode = 1; + + ret = max8997_bulk_write(info->rtc, MAX8997_RTC_CTRLMASK, 2, data); + if (ret < 0) { + dev_err(info->dev, "%s: fail to write controlm reg(%d)\n", + __func__, ret); + return ret; + } + + ret = max8997_rtc_set_update_reg(info); + return ret; +} + +static int max8997_rtc_probe(struct platform_device *pdev) +{ + struct max8997_dev *max8997 = dev_get_drvdata(pdev->dev.parent); + struct max8997_rtc_info *info; + int ret, virq; + + info = devm_kzalloc(&pdev->dev, sizeof(struct max8997_rtc_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + mutex_init(&info->lock); + info->dev = &pdev->dev; + info->max8997 = max8997; + info->rtc = max8997->rtc; + + platform_set_drvdata(pdev, info); + + ret = max8997_rtc_init_reg(info); + + if (ret < 0) { + dev_err(&pdev->dev, "Failed to initialize RTC reg:%d\n", ret); + return ret; + } + + max8997_rtc_enable_wtsr(info, true); + max8997_rtc_enable_smpl(info, true); + + device_init_wakeup(&pdev->dev, true); + + info->rtc_dev = devm_rtc_device_register(&pdev->dev, "max8997-rtc", + &max8997_rtc_ops, THIS_MODULE); + + if (IS_ERR(info->rtc_dev)) { + ret = PTR_ERR(info->rtc_dev); + dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret); + return ret; + } + + virq = irq_create_mapping(max8997->irq_domain, MAX8997_PMICIRQ_RTCA1); + if (!virq) { + dev_err(&pdev->dev, "Failed to create mapping alarm IRQ\n"); + ret = -ENXIO; + goto err_out; + } + info->virq = virq; + + ret = devm_request_threaded_irq(&pdev->dev, virq, NULL, + max8997_rtc_alarm_irq, 0, + "rtc-alarm0", info); + if (ret < 0) + dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", + info->virq, ret); + +err_out: + return ret; +} + +static void max8997_rtc_shutdown(struct platform_device *pdev) +{ + struct max8997_rtc_info *info = platform_get_drvdata(pdev); + + max8997_rtc_enable_wtsr(info, false); + max8997_rtc_enable_smpl(info, false); +} + +static const struct platform_device_id rtc_id[] = { + { .name = "max8997-rtc" }, + { } +}; +MODULE_DEVICE_TABLE(platform, rtc_id); + +static struct platform_driver max8997_rtc_driver = { + .driver = { + .name = "max8997-rtc", + }, + .probe = max8997_rtc_probe, + .shutdown = max8997_rtc_shutdown, + .id_table = rtc_id, +}; + +module_platform_driver(max8997_rtc_driver); + +MODULE_DESCRIPTION("Maxim MAX8997 RTC driver"); +MODULE_AUTHOR(""); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-max8998.c b/drivers/rtc/rtc-max8998.c new file mode 100644 index 000000000..a2c946edc --- /dev/null +++ b/drivers/rtc/rtc-max8998.c @@ -0,0 +1,321 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// RTC driver for Maxim MAX8998 +// +// Copyright (C) 2010 Samsung Electronics Co.Ltd +// Author: Minkyu Kang +// Author: Joonyoung Shim + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX8998_RTC_SEC 0x00 +#define MAX8998_RTC_MIN 0x01 +#define MAX8998_RTC_HOUR 0x02 +#define MAX8998_RTC_WEEKDAY 0x03 +#define MAX8998_RTC_DATE 0x04 +#define MAX8998_RTC_MONTH 0x05 +#define MAX8998_RTC_YEAR1 0x06 +#define MAX8998_RTC_YEAR2 0x07 +#define MAX8998_ALARM0_SEC 0x08 +#define MAX8998_ALARM0_MIN 0x09 +#define MAX8998_ALARM0_HOUR 0x0a +#define MAX8998_ALARM0_WEEKDAY 0x0b +#define MAX8998_ALARM0_DATE 0x0c +#define MAX8998_ALARM0_MONTH 0x0d +#define MAX8998_ALARM0_YEAR1 0x0e +#define MAX8998_ALARM0_YEAR2 0x0f +#define MAX8998_ALARM1_SEC 0x10 +#define MAX8998_ALARM1_MIN 0x11 +#define MAX8998_ALARM1_HOUR 0x12 +#define MAX8998_ALARM1_WEEKDAY 0x13 +#define MAX8998_ALARM1_DATE 0x14 +#define MAX8998_ALARM1_MONTH 0x15 +#define MAX8998_ALARM1_YEAR1 0x16 +#define MAX8998_ALARM1_YEAR2 0x17 +#define MAX8998_ALARM0_CONF 0x18 +#define MAX8998_ALARM1_CONF 0x19 +#define MAX8998_RTC_STATUS 0x1a +#define MAX8998_WTSR_SMPL_CNTL 0x1b +#define MAX8998_TEST 0x1f + +#define HOUR_12 (1 << 7) +#define HOUR_PM (1 << 5) +#define ALARM0_STATUS (1 << 1) +#define ALARM1_STATUS (1 << 2) + +enum { + RTC_SEC = 0, + RTC_MIN, + RTC_HOUR, + RTC_WEEKDAY, + RTC_DATE, + RTC_MONTH, + RTC_YEAR1, + RTC_YEAR2, +}; + +struct max8998_rtc_info { + struct device *dev; + struct max8998_dev *max8998; + struct i2c_client *rtc; + struct rtc_device *rtc_dev; + int irq; + bool lp3974_bug_workaround; +}; + +static void max8998_data_to_tm(u8 *data, struct rtc_time *tm) +{ + tm->tm_sec = bcd2bin(data[RTC_SEC]); + tm->tm_min = bcd2bin(data[RTC_MIN]); + if (data[RTC_HOUR] & HOUR_12) { + tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x1f); + if (data[RTC_HOUR] & HOUR_PM) + tm->tm_hour += 12; + } else + tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x3f); + + tm->tm_wday = data[RTC_WEEKDAY] & 0x07; + tm->tm_mday = bcd2bin(data[RTC_DATE]); + tm->tm_mon = bcd2bin(data[RTC_MONTH]); + tm->tm_year = bcd2bin(data[RTC_YEAR1]) + bcd2bin(data[RTC_YEAR2]) * 100; + tm->tm_year -= 1900; +} + +static void max8998_tm_to_data(struct rtc_time *tm, u8 *data) +{ + data[RTC_SEC] = bin2bcd(tm->tm_sec); + data[RTC_MIN] = bin2bcd(tm->tm_min); + data[RTC_HOUR] = bin2bcd(tm->tm_hour); + data[RTC_WEEKDAY] = tm->tm_wday; + data[RTC_DATE] = bin2bcd(tm->tm_mday); + data[RTC_MONTH] = bin2bcd(tm->tm_mon); + data[RTC_YEAR1] = bin2bcd(tm->tm_year % 100); + data[RTC_YEAR2] = bin2bcd((tm->tm_year + 1900) / 100); +} + +static int max8998_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct max8998_rtc_info *info = dev_get_drvdata(dev); + u8 data[8]; + int ret; + + ret = max8998_bulk_read(info->rtc, MAX8998_RTC_SEC, 8, data); + if (ret < 0) + return ret; + + max8998_data_to_tm(data, tm); + + return 0; +} + +static int max8998_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct max8998_rtc_info *info = dev_get_drvdata(dev); + u8 data[8]; + int ret; + + max8998_tm_to_data(tm, data); + + ret = max8998_bulk_write(info->rtc, MAX8998_RTC_SEC, 8, data); + + if (info->lp3974_bug_workaround) + msleep(2000); + + return ret; +} + +static int max8998_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct max8998_rtc_info *info = dev_get_drvdata(dev); + u8 data[8]; + u8 val; + int ret; + + ret = max8998_bulk_read(info->rtc, MAX8998_ALARM0_SEC, 8, data); + if (ret < 0) + return ret; + + max8998_data_to_tm(data, &alrm->time); + + ret = max8998_read_reg(info->rtc, MAX8998_ALARM0_CONF, &val); + if (ret < 0) + return ret; + + alrm->enabled = !!val; + + ret = max8998_read_reg(info->rtc, MAX8998_RTC_STATUS, &val); + if (ret < 0) + return ret; + + if (val & ALARM0_STATUS) + alrm->pending = 1; + else + alrm->pending = 0; + + return 0; +} + +static int max8998_rtc_stop_alarm(struct max8998_rtc_info *info) +{ + int ret = max8998_write_reg(info->rtc, MAX8998_ALARM0_CONF, 0); + + if (info->lp3974_bug_workaround) + msleep(2000); + + return ret; +} + +static int max8998_rtc_start_alarm(struct max8998_rtc_info *info) +{ + int ret; + u8 alarm0_conf = 0x77; + + /* LP3974 with delay bug chips has rtc alarm bugs with "MONTH" field */ + if (info->lp3974_bug_workaround) + alarm0_conf = 0x57; + + ret = max8998_write_reg(info->rtc, MAX8998_ALARM0_CONF, alarm0_conf); + + if (info->lp3974_bug_workaround) + msleep(2000); + + return ret; +} + +static int max8998_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct max8998_rtc_info *info = dev_get_drvdata(dev); + u8 data[8]; + int ret; + + max8998_tm_to_data(&alrm->time, data); + + ret = max8998_rtc_stop_alarm(info); + if (ret < 0) + return ret; + + ret = max8998_bulk_write(info->rtc, MAX8998_ALARM0_SEC, 8, data); + if (ret < 0) + return ret; + + if (info->lp3974_bug_workaround) + msleep(2000); + + if (alrm->enabled) + ret = max8998_rtc_start_alarm(info); + + return ret; +} + +static int max8998_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct max8998_rtc_info *info = dev_get_drvdata(dev); + + if (enabled) + return max8998_rtc_start_alarm(info); + else + return max8998_rtc_stop_alarm(info); +} + +static irqreturn_t max8998_rtc_alarm_irq(int irq, void *data) +{ + struct max8998_rtc_info *info = data; + + rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops max8998_rtc_ops = { + .read_time = max8998_rtc_read_time, + .set_time = max8998_rtc_set_time, + .read_alarm = max8998_rtc_read_alarm, + .set_alarm = max8998_rtc_set_alarm, + .alarm_irq_enable = max8998_rtc_alarm_irq_enable, +}; + +static int max8998_rtc_probe(struct platform_device *pdev) +{ + struct max8998_dev *max8998 = dev_get_drvdata(pdev->dev.parent); + struct max8998_platform_data *pdata = max8998->pdata; + struct max8998_rtc_info *info; + int ret; + + info = devm_kzalloc(&pdev->dev, sizeof(struct max8998_rtc_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->dev = &pdev->dev; + info->max8998 = max8998; + info->rtc = max8998->rtc; + + platform_set_drvdata(pdev, info); + + info->rtc_dev = devm_rtc_device_register(&pdev->dev, "max8998-rtc", + &max8998_rtc_ops, THIS_MODULE); + + if (IS_ERR(info->rtc_dev)) { + ret = PTR_ERR(info->rtc_dev); + dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret); + return ret; + } + + if (!max8998->irq_domain) + goto no_irq; + + info->irq = irq_create_mapping(max8998->irq_domain, MAX8998_IRQ_ALARM0); + if (!info->irq) { + dev_warn(&pdev->dev, "Failed to map alarm IRQ\n"); + goto no_irq; + } + + ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL, + max8998_rtc_alarm_irq, 0, "rtc-alarm0", info); + + if (ret < 0) + dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", + info->irq, ret); + +no_irq: + dev_info(&pdev->dev, "RTC CHIP NAME: %s\n", pdev->id_entry->name); + if (pdata && pdata->rtc_delay) { + info->lp3974_bug_workaround = true; + dev_warn(&pdev->dev, "LP3974 with RTC REGERR option." + " RTC updates will be extremely slow.\n"); + } + + return 0; +} + +static const struct platform_device_id max8998_rtc_id[] = { + { .name = "max8998-rtc", .driver_data = TYPE_MAX8998 }, + { .name = "lp3974-rtc", .driver_data = TYPE_LP3974 }, + { } +}; +MODULE_DEVICE_TABLE(platform, max8998_rtc_id); + +static struct platform_driver max8998_rtc_driver = { + .driver = { + .name = "max8998-rtc", + }, + .probe = max8998_rtc_probe, + .id_table = max8998_rtc_id, +}; + +module_platform_driver(max8998_rtc_driver); + +MODULE_AUTHOR("Minkyu Kang "); +MODULE_AUTHOR("Joonyoung Shim "); +MODULE_DESCRIPTION("Maxim MAX8998 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-mc13xxx.c b/drivers/rtc/rtc-mc13xxx.c new file mode 100644 index 000000000..7960d5fa8 --- /dev/null +++ b/drivers/rtc/rtc-mc13xxx.c @@ -0,0 +1,349 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Real Time Clock driver for Freescale MC13XXX PMIC + * + * (C) 2009 Sascha Hauer, Pengutronix + * (C) 2009 Uwe Kleine-Koenig, Pengutronix + */ + +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "mc13xxx-rtc" + +#define MC13XXX_RTCTOD 20 +#define MC13XXX_RTCTODA 21 +#define MC13XXX_RTCDAY 22 +#define MC13XXX_RTCDAYA 23 + +#define SEC_PER_DAY (24 * 60 * 60) + +struct mc13xxx_rtc { + struct rtc_device *rtc; + struct mc13xxx *mc13xxx; + int valid; +}; + +static int mc13xxx_rtc_irq_enable_unlocked(struct device *dev, + unsigned int enabled, int irq) +{ + struct mc13xxx_rtc *priv = dev_get_drvdata(dev); + int (*func)(struct mc13xxx *mc13xxx, int irq); + + if (!priv->valid) + return -ENODATA; + + func = enabled ? mc13xxx_irq_unmask : mc13xxx_irq_mask; + return func(priv->mc13xxx, irq); +} + +static int mc13xxx_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct mc13xxx_rtc *priv = dev_get_drvdata(dev); + int ret; + + mc13xxx_lock(priv->mc13xxx); + + ret = mc13xxx_rtc_irq_enable_unlocked(dev, enabled, MC13XXX_IRQ_TODA); + + mc13xxx_unlock(priv->mc13xxx); + + return ret; +} + +static int mc13xxx_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct mc13xxx_rtc *priv = dev_get_drvdata(dev); + unsigned int seconds, days1, days2; + + if (!priv->valid) + return -ENODATA; + + do { + int ret; + + ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCDAY, &days1); + if (ret) + return ret; + + ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCTOD, &seconds); + if (ret) + return ret; + + ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCDAY, &days2); + if (ret) + return ret; + } while (days1 != days2); + + rtc_time64_to_tm((time64_t)days1 * SEC_PER_DAY + seconds, tm); + + return 0; +} + +static int mc13xxx_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct mc13xxx_rtc *priv = dev_get_drvdata(dev); + unsigned int seconds, days; + unsigned int alarmseconds; + int ret; + + days = div_s64_rem(rtc_tm_to_time64(tm), SEC_PER_DAY, &seconds); + + mc13xxx_lock(priv->mc13xxx); + + /* + * temporarily invalidate alarm to prevent triggering it when the day is + * already updated while the time isn't yet. + */ + ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCTODA, &alarmseconds); + if (unlikely(ret)) + goto out; + + if (alarmseconds < SEC_PER_DAY) { + ret = mc13xxx_reg_write(priv->mc13xxx, + MC13XXX_RTCTODA, 0x1ffff); + if (unlikely(ret)) + goto out; + } + + /* + * write seconds=0 to prevent a day switch between writing days + * and seconds below + */ + ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCTOD, 0); + if (unlikely(ret)) + goto out; + + ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCDAY, days); + if (unlikely(ret)) + goto out; + + ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCTOD, seconds); + if (unlikely(ret)) + goto out; + + /* restore alarm */ + if (alarmseconds < SEC_PER_DAY) { + ret = mc13xxx_reg_write(priv->mc13xxx, + MC13XXX_RTCTODA, alarmseconds); + if (unlikely(ret)) + goto out; + } + + if (!priv->valid) { + ret = mc13xxx_irq_unmask(priv->mc13xxx, MC13XXX_IRQ_RTCRST); + } + +out: + priv->valid = !ret; + + mc13xxx_unlock(priv->mc13xxx); + + return ret; +} + +static int mc13xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct mc13xxx_rtc *priv = dev_get_drvdata(dev); + unsigned int seconds, days; + time64_t s1970; + int enabled, pending; + int ret; + + mc13xxx_lock(priv->mc13xxx); + + ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCTODA, &seconds); + if (unlikely(ret)) + goto out; + if (seconds >= SEC_PER_DAY) { + ret = -ENODATA; + goto out; + } + + ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCDAY, &days); + if (unlikely(ret)) + goto out; + + ret = mc13xxx_irq_status(priv->mc13xxx, MC13XXX_IRQ_TODA, + &enabled, &pending); + +out: + mc13xxx_unlock(priv->mc13xxx); + + if (ret) + return ret; + + alarm->enabled = enabled; + alarm->pending = pending; + + s1970 = (time64_t)days * SEC_PER_DAY + seconds; + + rtc_time64_to_tm(s1970, &alarm->time); + dev_dbg(dev, "%s: %lld\n", __func__, (long long)s1970); + + return 0; +} + +static int mc13xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct mc13xxx_rtc *priv = dev_get_drvdata(dev); + time64_t s1970; + u32 seconds, days; + int ret; + + mc13xxx_lock(priv->mc13xxx); + + /* disable alarm to prevent false triggering */ + ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCTODA, 0x1ffff); + if (unlikely(ret)) + goto out; + + s1970 = rtc_tm_to_time64(&alarm->time); + + dev_dbg(dev, "%s: %s %lld\n", __func__, alarm->enabled ? "on" : "off", + (long long)s1970); + + ret = mc13xxx_rtc_irq_enable_unlocked(dev, alarm->enabled, + MC13XXX_IRQ_TODA); + if (unlikely(ret)) + goto out; + + days = div_s64_rem(s1970, SEC_PER_DAY, &seconds); + + ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCDAYA, days); + if (unlikely(ret)) + goto out; + + ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCTODA, seconds); + +out: + mc13xxx_unlock(priv->mc13xxx); + + return ret; +} + +static irqreturn_t mc13xxx_rtc_alarm_handler(int irq, void *dev) +{ + struct mc13xxx_rtc *priv = dev; + + rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops mc13xxx_rtc_ops = { + .read_time = mc13xxx_rtc_read_time, + .set_time = mc13xxx_rtc_set_time, + .read_alarm = mc13xxx_rtc_read_alarm, + .set_alarm = mc13xxx_rtc_set_alarm, + .alarm_irq_enable = mc13xxx_rtc_alarm_irq_enable, +}; + +static irqreturn_t mc13xxx_rtc_reset_handler(int irq, void *dev) +{ + struct mc13xxx_rtc *priv = dev; + struct mc13xxx *mc13xxx = priv->mc13xxx; + + priv->valid = 0; + + mc13xxx_irq_mask(mc13xxx, irq); + + return IRQ_HANDLED; +} + +static int __init mc13xxx_rtc_probe(struct platform_device *pdev) +{ + int ret; + struct mc13xxx_rtc *priv; + struct mc13xxx *mc13xxx; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mc13xxx = dev_get_drvdata(pdev->dev.parent); + priv->mc13xxx = mc13xxx; + priv->valid = 1; + + priv->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(priv->rtc)) + return PTR_ERR(priv->rtc); + platform_set_drvdata(pdev, priv); + + priv->rtc->ops = &mc13xxx_rtc_ops; + /* 15bit days + hours, minutes, seconds */ + priv->rtc->range_max = (timeu64_t)(1 << 15) * SEC_PER_DAY - 1; + + mc13xxx_lock(mc13xxx); + + ret = mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_RTCRST, + mc13xxx_rtc_reset_handler, DRIVER_NAME, priv); + if (ret) + goto err_irq_request; + + ret = mc13xxx_irq_request_nounmask(mc13xxx, MC13XXX_IRQ_TODA, + mc13xxx_rtc_alarm_handler, DRIVER_NAME, priv); + if (ret) + goto err_irq_request; + + mc13xxx_unlock(mc13xxx); + + ret = devm_rtc_register_device(priv->rtc); + if (ret) { + mc13xxx_lock(mc13xxx); + goto err_irq_request; + } + + return 0; + +err_irq_request: + mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_TODA, priv); + mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_RTCRST, priv); + + mc13xxx_unlock(mc13xxx); + + return ret; +} + +static void mc13xxx_rtc_remove(struct platform_device *pdev) +{ + struct mc13xxx_rtc *priv = platform_get_drvdata(pdev); + + mc13xxx_lock(priv->mc13xxx); + + mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_TODA, priv); + mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_RTCRST, priv); + + mc13xxx_unlock(priv->mc13xxx); +} + +static const struct platform_device_id mc13xxx_rtc_idtable[] = { + { + .name = "mc13783-rtc", + }, { + .name = "mc13892-rtc", + }, { + .name = "mc34708-rtc", + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, mc13xxx_rtc_idtable); + +static struct platform_driver mc13xxx_rtc_driver = { + .id_table = mc13xxx_rtc_idtable, + .remove = mc13xxx_rtc_remove, + .driver = { + .name = DRIVER_NAME, + }, +}; + +module_platform_driver_probe(mc13xxx_rtc_driver, &mc13xxx_rtc_probe); + +MODULE_AUTHOR("Sascha Hauer "); +MODULE_DESCRIPTION("RTC driver for Freescale MC13XXX PMIC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-mc146818-lib.c b/drivers/rtc/rtc-mc146818-lib.c new file mode 100644 index 000000000..dbd2d5835 --- /dev/null +++ b/drivers/rtc/rtc-mc146818-lib.c @@ -0,0 +1,309 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include + +#ifdef CONFIG_ACPI +#include +#endif + +#define UIP_RECHECK_DELAY 100 /* usec */ +#define UIP_RECHECK_DELAY_MS (USEC_PER_MSEC / UIP_RECHECK_DELAY) +#define UIP_RECHECK_LOOPS_MS(x) (x / UIP_RECHECK_DELAY_MS) + +/* + * Execute a function while the UIP (Update-in-progress) bit of the RTC is + * unset. The timeout is configurable by the caller in ms. + * + * Warning: callback may be executed more then once. + */ +bool mc146818_avoid_UIP(void (*callback)(unsigned char seconds, void *param), + int timeout, + void *param) +{ + int i; + unsigned long flags; + unsigned char seconds; + + for (i = 0; UIP_RECHECK_LOOPS_MS(i) < timeout; i++) { + spin_lock_irqsave(&rtc_lock, flags); + + /* + * Check whether there is an update in progress during which the + * readout is unspecified. The maximum update time is ~2ms. Poll + * for completion. + * + * Store the second value before checking UIP so a long lasting + * NMI which happens to hit after the UIP check cannot make + * an update cycle invisible. + */ + seconds = CMOS_READ(RTC_SECONDS); + + if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) { + spin_unlock_irqrestore(&rtc_lock, flags); + udelay(UIP_RECHECK_DELAY); + continue; + } + + /* Revalidate the above readout */ + if (seconds != CMOS_READ(RTC_SECONDS)) { + spin_unlock_irqrestore(&rtc_lock, flags); + continue; + } + + if (callback) + callback(seconds, param); + + /* + * Check for the UIP bit again. If it is set now then + * the above values may contain garbage. + */ + if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) { + spin_unlock_irqrestore(&rtc_lock, flags); + udelay(UIP_RECHECK_DELAY); + continue; + } + + /* + * A NMI might have interrupted the above sequence so check + * whether the seconds value has changed which indicates that + * the NMI took longer than the UIP bit was set. Unlikely, but + * possible and there is also virt... + */ + if (seconds != CMOS_READ(RTC_SECONDS)) { + spin_unlock_irqrestore(&rtc_lock, flags); + continue; + } + spin_unlock_irqrestore(&rtc_lock, flags); + + if (UIP_RECHECK_LOOPS_MS(i) >= 100) + pr_warn("Reading current time from RTC took around %li ms\n", + UIP_RECHECK_LOOPS_MS(i)); + + return true; + } + return false; +} +EXPORT_SYMBOL_GPL(mc146818_avoid_UIP); + +/* + * If the UIP (Update-in-progress) bit of the RTC is set for more then + * 10ms, the RTC is apparently broken or not present. + */ +bool mc146818_does_rtc_work(void) +{ + return mc146818_avoid_UIP(NULL, 1000, NULL); +} +EXPORT_SYMBOL_GPL(mc146818_does_rtc_work); + +struct mc146818_get_time_callback_param { + struct rtc_time *time; + unsigned char ctrl; +#ifdef CONFIG_ACPI + unsigned char century; +#endif +#ifdef CONFIG_MACH_DECSTATION + unsigned int real_year; +#endif +}; + +static void mc146818_get_time_callback(unsigned char seconds, void *param_in) +{ + struct mc146818_get_time_callback_param *p = param_in; + + /* + * Only the values that we read from the RTC are set. We leave + * tm_wday, tm_yday and tm_isdst untouched. Even though the + * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated + * by the RTC when initially set to a non-zero value. + */ + p->time->tm_sec = seconds; + p->time->tm_min = CMOS_READ(RTC_MINUTES); + p->time->tm_hour = CMOS_READ(RTC_HOURS); + p->time->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH); + p->time->tm_mon = CMOS_READ(RTC_MONTH); + p->time->tm_year = CMOS_READ(RTC_YEAR); +#ifdef CONFIG_MACH_DECSTATION + p->real_year = CMOS_READ(RTC_DEC_YEAR); +#endif +#ifdef CONFIG_ACPI + if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID && + acpi_gbl_FADT.century) { + p->century = CMOS_READ(acpi_gbl_FADT.century); + } else { + p->century = 0; + } +#endif + + p->ctrl = CMOS_READ(RTC_CONTROL); +} + +/** + * mc146818_get_time - Get the current time from the RTC + * @time: pointer to struct rtc_time to store the current time + * @timeout: timeout value in ms + * + * This function reads the current time from the RTC and stores it in the + * provided struct rtc_time. The timeout parameter specifies the maximum + * time to wait for the RTC to become ready. + * + * Return: 0 on success, -ETIMEDOUT if the RTC did not become ready within + * the specified timeout, or another error code if an error occurred. + */ +int mc146818_get_time(struct rtc_time *time, int timeout) +{ + struct mc146818_get_time_callback_param p = { + .time = time + }; + + if (!mc146818_avoid_UIP(mc146818_get_time_callback, timeout, &p)) { + memset(time, 0, sizeof(*time)); + return -ETIMEDOUT; + } + + if (!(p.ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) + { + time->tm_sec = bcd2bin(time->tm_sec); + time->tm_min = bcd2bin(time->tm_min); + time->tm_hour = bcd2bin(time->tm_hour); + time->tm_mday = bcd2bin(time->tm_mday); + time->tm_mon = bcd2bin(time->tm_mon); + time->tm_year = bcd2bin(time->tm_year); +#ifdef CONFIG_ACPI + p.century = bcd2bin(p.century); +#endif + } + +#ifdef CONFIG_MACH_DECSTATION + time->tm_year += p.real_year - 72; +#endif + +#ifdef CONFIG_ACPI + if (p.century > 19) + time->tm_year += (p.century - 19) * 100; +#endif + + /* + * Account for differences between how the RTC uses the values + * and how they are defined in a struct rtc_time; + */ + if (time->tm_year <= 69) + time->tm_year += 100; + + time->tm_mon--; + + return 0; +} +EXPORT_SYMBOL_GPL(mc146818_get_time); + +/* AMD systems don't allow access to AltCentury with DV1 */ +static bool apply_amd_register_a_behavior(void) +{ +#ifdef CONFIG_X86 + if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD || + boot_cpu_data.x86_vendor == X86_VENDOR_HYGON) + return true; +#endif + return false; +} + +/* Set the current date and time in the real time clock. */ +int mc146818_set_time(struct rtc_time *time) +{ + unsigned long flags; + unsigned char mon, day, hrs, min, sec; + unsigned char save_control, save_freq_select; + unsigned int yrs; +#ifdef CONFIG_MACH_DECSTATION + unsigned int real_yrs; +#endif + unsigned char century = 0; + + yrs = time->tm_year; + mon = time->tm_mon + 1; /* tm_mon starts at zero */ + day = time->tm_mday; + hrs = time->tm_hour; + min = time->tm_min; + sec = time->tm_sec; + + if (yrs > 255) /* They are unsigned */ + return -EINVAL; + +#ifdef CONFIG_MACH_DECSTATION + real_yrs = yrs; + yrs = 72; + + /* + * We want to keep the year set to 73 until March + * for non-leap years, so that Feb, 29th is handled + * correctly. + */ + if (!is_leap_year(real_yrs + 1900) && mon < 3) { + real_yrs--; + yrs = 73; + } +#endif + +#ifdef CONFIG_ACPI + if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID && + acpi_gbl_FADT.century) { + century = (yrs + 1900) / 100; + yrs %= 100; + } +#endif + + /* These limits and adjustments are independent of + * whether the chip is in binary mode or not. + */ + if (yrs > 169) + return -EINVAL; + + if (yrs >= 100) + yrs -= 100; + + spin_lock_irqsave(&rtc_lock, flags); + save_control = CMOS_READ(RTC_CONTROL); + spin_unlock_irqrestore(&rtc_lock, flags); + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + sec = bin2bcd(sec); + min = bin2bcd(min); + hrs = bin2bcd(hrs); + day = bin2bcd(day); + mon = bin2bcd(mon); + yrs = bin2bcd(yrs); + century = bin2bcd(century); + } + + spin_lock_irqsave(&rtc_lock, flags); + save_control = CMOS_READ(RTC_CONTROL); + CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); + save_freq_select = CMOS_READ(RTC_FREQ_SELECT); + if (apply_amd_register_a_behavior()) + CMOS_WRITE((save_freq_select & ~RTC_AMD_BANK_SELECT), RTC_FREQ_SELECT); + else + CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); + +#ifdef CONFIG_MACH_DECSTATION + CMOS_WRITE(real_yrs, RTC_DEC_YEAR); +#endif + CMOS_WRITE(yrs, RTC_YEAR); + CMOS_WRITE(mon, RTC_MONTH); + CMOS_WRITE(day, RTC_DAY_OF_MONTH); + CMOS_WRITE(hrs, RTC_HOURS); + CMOS_WRITE(min, RTC_MINUTES); + CMOS_WRITE(sec, RTC_SECONDS); +#ifdef CONFIG_ACPI + if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID && + acpi_gbl_FADT.century) + CMOS_WRITE(century, acpi_gbl_FADT.century); +#endif + + CMOS_WRITE(save_control, RTC_CONTROL); + CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); + + spin_unlock_irqrestore(&rtc_lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(mc146818_set_time); diff --git a/drivers/rtc/rtc-mcp795.c b/drivers/rtc/rtc-mcp795.c new file mode 100644 index 000000000..e12f0f806 --- /dev/null +++ b/drivers/rtc/rtc-mcp795.c @@ -0,0 +1,452 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * SPI Driver for Microchip MCP795 RTC + * + * Copyright (C) Josef Gajdusek + * + * based on other Linux RTC drivers + * + * Device datasheet: + * https://ww1.microchip.com/downloads/en/DeviceDoc/22280A.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* MCP795 Instructions, see datasheet table 3-1 */ +#define MCP795_EEREAD 0x03 +#define MCP795_EEWRITE 0x02 +#define MCP795_EEWRDI 0x04 +#define MCP795_EEWREN 0x06 +#define MCP795_SRREAD 0x05 +#define MCP795_SRWRITE 0x01 +#define MCP795_READ 0x13 +#define MCP795_WRITE 0x12 +#define MCP795_UNLOCK 0x14 +#define MCP795_IDWRITE 0x32 +#define MCP795_IDREAD 0x33 +#define MCP795_CLRWDT 0x44 +#define MCP795_CLRRAM 0x54 + +/* MCP795 RTCC registers, see datasheet table 4-1 */ +#define MCP795_REG_SECONDS 0x01 +#define MCP795_REG_DAY 0x04 +#define MCP795_REG_MONTH 0x06 +#define MCP795_REG_CONTROL 0x08 +#define MCP795_REG_ALM0_SECONDS 0x0C +#define MCP795_REG_ALM0_DAY 0x0F + +#define MCP795_ST_BIT BIT(7) +#define MCP795_24_BIT BIT(6) +#define MCP795_LP_BIT BIT(5) +#define MCP795_EXTOSC_BIT BIT(3) +#define MCP795_OSCON_BIT BIT(5) +#define MCP795_ALM0_BIT BIT(4) +#define MCP795_ALM1_BIT BIT(5) +#define MCP795_ALM0IF_BIT BIT(3) +#define MCP795_ALM0C0_BIT BIT(4) +#define MCP795_ALM0C1_BIT BIT(5) +#define MCP795_ALM0C2_BIT BIT(6) + +#define SEC_PER_DAY (24 * 60 * 60) + +static int mcp795_rtcc_read(struct device *dev, u8 addr, u8 *buf, u8 count) +{ + struct spi_device *spi = to_spi_device(dev); + int ret; + u8 tx[2]; + + tx[0] = MCP795_READ; + tx[1] = addr; + ret = spi_write_then_read(spi, tx, sizeof(tx), buf, count); + + if (ret) + dev_err(dev, "Failed reading %d bytes from address %x.\n", + count, addr); + + return ret; +} + +static int mcp795_rtcc_write(struct device *dev, u8 addr, u8 *data, u8 count) +{ + struct spi_device *spi = to_spi_device(dev); + int ret; + u8 tx[257]; + + tx[0] = MCP795_WRITE; + tx[1] = addr; + memcpy(&tx[2], data, count); + + ret = spi_write(spi, tx, 2 + count); + + if (ret) + dev_err(dev, "Failed to write %d bytes to address %x.\n", + count, addr); + + return ret; +} + +static int mcp795_rtcc_set_bits(struct device *dev, u8 addr, u8 mask, u8 state) +{ + int ret; + u8 tmp; + + ret = mcp795_rtcc_read(dev, addr, &tmp, 1); + if (ret) + return ret; + + if ((tmp & mask) != state) { + tmp = (tmp & ~mask) | state; + ret = mcp795_rtcc_write(dev, addr, &tmp, 1); + } + + return ret; +} + +static int mcp795_stop_oscillator(struct device *dev, bool *extosc) +{ + int retries = 5; + int ret; + u8 data; + + ret = mcp795_rtcc_set_bits(dev, MCP795_REG_SECONDS, MCP795_ST_BIT, 0); + if (ret) + return ret; + ret = mcp795_rtcc_read(dev, MCP795_REG_CONTROL, &data, 1); + if (ret) + return ret; + *extosc = !!(data & MCP795_EXTOSC_BIT); + ret = mcp795_rtcc_set_bits( + dev, MCP795_REG_CONTROL, MCP795_EXTOSC_BIT, 0); + if (ret) + return ret; + /* wait for the OSCON bit to clear */ + do { + usleep_range(700, 800); + ret = mcp795_rtcc_read(dev, MCP795_REG_DAY, &data, 1); + if (ret) + break; + if (!(data & MCP795_OSCON_BIT)) + break; + + } while (--retries); + + return !retries ? -EIO : ret; +} + +static int mcp795_start_oscillator(struct device *dev, bool *extosc) +{ + if (extosc) { + u8 data = *extosc ? MCP795_EXTOSC_BIT : 0; + int ret; + + ret = mcp795_rtcc_set_bits( + dev, MCP795_REG_CONTROL, MCP795_EXTOSC_BIT, data); + if (ret) + return ret; + } + return mcp795_rtcc_set_bits( + dev, MCP795_REG_SECONDS, MCP795_ST_BIT, MCP795_ST_BIT); +} + +/* Enable or disable Alarm 0 in RTC */ +static int mcp795_update_alarm(struct device *dev, bool enable) +{ + int ret; + + dev_dbg(dev, "%s alarm\n", enable ? "Enable" : "Disable"); + + if (enable) { + /* clear ALM0IF (Alarm 0 Interrupt Flag) bit */ + ret = mcp795_rtcc_set_bits(dev, MCP795_REG_ALM0_DAY, + MCP795_ALM0IF_BIT, 0); + if (ret) + return ret; + /* enable alarm 0 */ + ret = mcp795_rtcc_set_bits(dev, MCP795_REG_CONTROL, + MCP795_ALM0_BIT, MCP795_ALM0_BIT); + } else { + /* disable alarm 0 and alarm 1 */ + ret = mcp795_rtcc_set_bits(dev, MCP795_REG_CONTROL, + MCP795_ALM0_BIT | MCP795_ALM1_BIT, 0); + } + return ret; +} + +static int mcp795_set_time(struct device *dev, struct rtc_time *tim) +{ + int ret; + u8 data[7]; + bool extosc; + + /* Stop RTC and store current value of EXTOSC bit */ + ret = mcp795_stop_oscillator(dev, &extosc); + if (ret) + return ret; + + /* Read first, so we can leave config bits untouched */ + ret = mcp795_rtcc_read(dev, MCP795_REG_SECONDS, data, sizeof(data)); + + if (ret) + return ret; + + data[0] = (data[0] & 0x80) | bin2bcd(tim->tm_sec); + data[1] = (data[1] & 0x80) | bin2bcd(tim->tm_min); + data[2] = bin2bcd(tim->tm_hour); + data[3] = (data[3] & 0xF8) | bin2bcd(tim->tm_wday + 1); + data[4] = bin2bcd(tim->tm_mday); + data[5] = (data[5] & MCP795_LP_BIT) | bin2bcd(tim->tm_mon + 1); + + if (tim->tm_year > 100) + tim->tm_year -= 100; + + data[6] = bin2bcd(tim->tm_year); + + /* Always write the date and month using a separate Write command. + * This is a workaround for a know silicon issue that some combinations + * of date and month values may result in the date being reset to 1. + */ + ret = mcp795_rtcc_write(dev, MCP795_REG_SECONDS, data, 5); + if (ret) + return ret; + + ret = mcp795_rtcc_write(dev, MCP795_REG_MONTH, &data[5], 2); + if (ret) + return ret; + + /* Start back RTC and restore previous value of EXTOSC bit. + * There is no need to clear EXTOSC bit when the previous value was 0 + * because it was already cleared when stopping the RTC oscillator. + */ + ret = mcp795_start_oscillator(dev, extosc ? &extosc : NULL); + if (ret) + return ret; + + dev_dbg(dev, "Set mcp795: %ptR\n", tim); + + return 0; +} + +static int mcp795_read_time(struct device *dev, struct rtc_time *tim) +{ + int ret; + u8 data[7]; + + ret = mcp795_rtcc_read(dev, MCP795_REG_SECONDS, data, sizeof(data)); + + if (ret) + return ret; + + tim->tm_sec = bcd2bin(data[0] & 0x7F); + tim->tm_min = bcd2bin(data[1] & 0x7F); + tim->tm_hour = bcd2bin(data[2] & 0x3F); + tim->tm_wday = bcd2bin(data[3] & 0x07) - 1; + tim->tm_mday = bcd2bin(data[4] & 0x3F); + tim->tm_mon = bcd2bin(data[5] & 0x1F) - 1; + tim->tm_year = bcd2bin(data[6]) + 100; /* Assume we are in 20xx */ + + dev_dbg(dev, "Read from mcp795: %ptR\n", tim); + + return 0; +} + +static int mcp795_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct rtc_time now_tm; + time64_t now; + time64_t later; + u8 tmp[6]; + int ret; + + /* Read current time from RTC hardware */ + ret = mcp795_read_time(dev, &now_tm); + if (ret) + return ret; + /* Get the number of seconds since 1970 */ + now = rtc_tm_to_time64(&now_tm); + later = rtc_tm_to_time64(&alm->time); + if (later <= now) + return -EINVAL; + /* make sure alarm fires within the next one year */ + if ((later - now) >= + (SEC_PER_DAY * (365 + is_leap_year(alm->time.tm_year)))) + return -EDOM; + /* disable alarm */ + ret = mcp795_update_alarm(dev, false); + if (ret) + return ret; + /* Read registers, so we can leave configuration bits untouched */ + ret = mcp795_rtcc_read(dev, MCP795_REG_ALM0_SECONDS, tmp, sizeof(tmp)); + if (ret) + return ret; + + alm->time.tm_year = -1; + alm->time.tm_isdst = -1; + alm->time.tm_yday = -1; + + tmp[0] = (tmp[0] & 0x80) | bin2bcd(alm->time.tm_sec); + tmp[1] = (tmp[1] & 0x80) | bin2bcd(alm->time.tm_min); + tmp[2] = (tmp[2] & 0xE0) | bin2bcd(alm->time.tm_hour); + tmp[3] = (tmp[3] & 0x80) | bin2bcd(alm->time.tm_wday + 1); + /* set alarm match: seconds, minutes, hour, day, date and month */ + tmp[3] |= (MCP795_ALM0C2_BIT | MCP795_ALM0C1_BIT | MCP795_ALM0C0_BIT); + tmp[4] = (tmp[4] & 0xC0) | bin2bcd(alm->time.tm_mday); + tmp[5] = (tmp[5] & 0xE0) | bin2bcd(alm->time.tm_mon + 1); + + ret = mcp795_rtcc_write(dev, MCP795_REG_ALM0_SECONDS, tmp, sizeof(tmp)); + if (ret) + return ret; + + /* enable alarm if requested */ + if (alm->enabled) { + ret = mcp795_update_alarm(dev, true); + if (ret) + return ret; + dev_dbg(dev, "Alarm IRQ armed\n"); + } + dev_dbg(dev, "Set alarm: %ptRdr(%d) %ptRt\n", + &alm->time, alm->time.tm_wday, &alm->time); + return 0; +} + +static int mcp795_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + u8 data[6]; + int ret; + + ret = mcp795_rtcc_read( + dev, MCP795_REG_ALM0_SECONDS, data, sizeof(data)); + if (ret) + return ret; + + alm->time.tm_sec = bcd2bin(data[0] & 0x7F); + alm->time.tm_min = bcd2bin(data[1] & 0x7F); + alm->time.tm_hour = bcd2bin(data[2] & 0x1F); + alm->time.tm_wday = bcd2bin(data[3] & 0x07) - 1; + alm->time.tm_mday = bcd2bin(data[4] & 0x3F); + alm->time.tm_mon = bcd2bin(data[5] & 0x1F) - 1; + alm->time.tm_year = -1; + alm->time.tm_isdst = -1; + alm->time.tm_yday = -1; + + dev_dbg(dev, "Read alarm: %ptRdr(%d) %ptRt\n", + &alm->time, alm->time.tm_wday, &alm->time); + return 0; +} + +static int mcp795_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + return mcp795_update_alarm(dev, !!enabled); +} + +static irqreturn_t mcp795_irq(int irq, void *data) +{ + struct spi_device *spi = data; + struct rtc_device *rtc = spi_get_drvdata(spi); + int ret; + + rtc_lock(rtc); + + /* Disable alarm. + * There is no need to clear ALM0IF (Alarm 0 Interrupt Flag) bit, + * because it is done every time when alarm is enabled. + */ + ret = mcp795_update_alarm(&spi->dev, false); + if (ret) + dev_err(&spi->dev, + "Failed to disable alarm in IRQ (ret=%d)\n", ret); + rtc_update_irq(rtc, 1, RTC_AF | RTC_IRQF); + + rtc_unlock(rtc); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops mcp795_rtc_ops = { + .read_time = mcp795_read_time, + .set_time = mcp795_set_time, + .read_alarm = mcp795_read_alarm, + .set_alarm = mcp795_set_alarm, + .alarm_irq_enable = mcp795_alarm_irq_enable +}; + +static int mcp795_probe(struct spi_device *spi) +{ + struct rtc_device *rtc; + int ret; + + spi->mode = SPI_MODE_0; + spi->bits_per_word = 8; + ret = spi_setup(spi); + if (ret) { + dev_err(&spi->dev, "Unable to setup SPI\n"); + return ret; + } + + /* Start the oscillator but don't set the value of EXTOSC bit */ + mcp795_start_oscillator(&spi->dev, NULL); + /* Clear the 12 hour mode flag*/ + mcp795_rtcc_set_bits(&spi->dev, 0x03, MCP795_24_BIT, 0); + + rtc = devm_rtc_device_register(&spi->dev, "rtc-mcp795", + &mcp795_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + spi_set_drvdata(spi, rtc); + + if (spi->irq > 0) { + dev_dbg(&spi->dev, "Alarm support enabled\n"); + + /* Clear any pending alarm (ALM0IF bit) before requesting + * the interrupt. + */ + mcp795_rtcc_set_bits(&spi->dev, MCP795_REG_ALM0_DAY, + MCP795_ALM0IF_BIT, 0); + ret = devm_request_threaded_irq(&spi->dev, spi->irq, NULL, + mcp795_irq, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(&rtc->dev), spi); + if (ret) + dev_err(&spi->dev, "Failed to request IRQ: %d: %d\n", + spi->irq, ret); + else + device_init_wakeup(&spi->dev, true); + } + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id mcp795_of_match[] = { + { .compatible = "maxim,mcp795" }, + { } +}; +MODULE_DEVICE_TABLE(of, mcp795_of_match); +#endif + +static const struct spi_device_id mcp795_spi_ids[] = { + { .name = "mcp795" }, + { } +}; +MODULE_DEVICE_TABLE(spi, mcp795_spi_ids); + +static struct spi_driver mcp795_driver = { + .driver = { + .name = "rtc-mcp795", + .of_match_table = of_match_ptr(mcp795_of_match), + }, + .probe = mcp795_probe, + .id_table = mcp795_spi_ids, +}; + +module_spi_driver(mcp795_driver); + +MODULE_DESCRIPTION("MCP795 RTC SPI Driver"); +MODULE_AUTHOR("Josef Gajdusek "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-meson-vrtc.c b/drivers/rtc/rtc-meson-vrtc.c new file mode 100644 index 000000000..7d38258cb --- /dev/null +++ b/drivers/rtc/rtc-meson-vrtc.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 BayLibre, SAS + * Author: Neil Armstrong + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + */ +#include +#include +#include +#include +#include +#include + +struct meson_vrtc_data { + void __iomem *io_alarm; + unsigned long alarm_time; + bool enabled; +}; + +static int meson_vrtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct timespec64 time; + + dev_dbg(dev, "%s\n", __func__); + ktime_get_real_ts64(&time); + rtc_time64_to_tm(time.tv_sec, tm); + + return 0; +} + +static void meson_vrtc_set_wakeup_time(struct meson_vrtc_data *vrtc, + unsigned long time) +{ + writel_relaxed(time, vrtc->io_alarm); +} + +static int meson_vrtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct meson_vrtc_data *vrtc = dev_get_drvdata(dev); + + dev_dbg(dev, "%s: alarm->enabled=%d\n", __func__, alarm->enabled); + if (alarm->enabled) + vrtc->alarm_time = rtc_tm_to_time64(&alarm->time); + else + vrtc->alarm_time = 0; + + return 0; +} + +static int meson_vrtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct meson_vrtc_data *vrtc = dev_get_drvdata(dev); + + vrtc->enabled = enabled; + return 0; +} + +static const struct rtc_class_ops meson_vrtc_ops = { + .read_time = meson_vrtc_read_time, + .set_alarm = meson_vrtc_set_alarm, + .alarm_irq_enable = meson_vrtc_alarm_irq_enable, +}; + +static int meson_vrtc_probe(struct platform_device *pdev) +{ + struct meson_vrtc_data *vrtc; + struct rtc_device *rtc; + + vrtc = devm_kzalloc(&pdev->dev, sizeof(*vrtc), GFP_KERNEL); + if (!vrtc) + return -ENOMEM; + + vrtc->io_alarm = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(vrtc->io_alarm)) + return PTR_ERR(vrtc->io_alarm); + + device_init_wakeup(&pdev->dev, true); + + platform_set_drvdata(pdev, vrtc); + + rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + rtc->ops = &meson_vrtc_ops; + return devm_rtc_register_device(rtc); +} + +static int __maybe_unused meson_vrtc_suspend(struct device *dev) +{ + struct meson_vrtc_data *vrtc = dev_get_drvdata(dev); + + dev_dbg(dev, "%s\n", __func__); + if (vrtc->alarm_time) { + unsigned long local_time; + long alarm_secs; + struct timespec64 time; + + ktime_get_real_ts64(&time); + local_time = time.tv_sec; + + dev_dbg(dev, "alarm_time = %lus, local_time=%lus\n", + vrtc->alarm_time, local_time); + alarm_secs = vrtc->alarm_time - local_time; + if (alarm_secs > 0) { + meson_vrtc_set_wakeup_time(vrtc, alarm_secs); + dev_dbg(dev, "system will wakeup in %lds.\n", + alarm_secs); + } else { + dev_err(dev, "alarm time already passed: %lds.\n", + alarm_secs); + } + } + + return 0; +} + +static int __maybe_unused meson_vrtc_resume(struct device *dev) +{ + struct meson_vrtc_data *vrtc = dev_get_drvdata(dev); + + dev_dbg(dev, "%s\n", __func__); + + vrtc->alarm_time = 0; + meson_vrtc_set_wakeup_time(vrtc, 0); + return 0; +} + +static SIMPLE_DEV_PM_OPS(meson_vrtc_pm_ops, + meson_vrtc_suspend, meson_vrtc_resume); + +static const struct of_device_id meson_vrtc_dt_match[] = { + { .compatible = "amlogic,meson-vrtc"}, + {}, +}; +MODULE_DEVICE_TABLE(of, meson_vrtc_dt_match); + +static struct platform_driver meson_vrtc_driver = { + .probe = meson_vrtc_probe, + .driver = { + .name = "meson-vrtc", + .of_match_table = meson_vrtc_dt_match, + .pm = &meson_vrtc_pm_ops, + }, +}; + +module_platform_driver(meson_vrtc_driver); + +MODULE_DESCRIPTION("Amlogic Virtual Wakeup RTC Timer driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-meson.c b/drivers/rtc/rtc-meson.c new file mode 100644 index 000000000..21eceb9e2 --- /dev/null +++ b/drivers/rtc/rtc-meson.c @@ -0,0 +1,404 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RTC driver for the interal RTC block in the Amlogic Meson6, Meson8, + * Meson8b and Meson8m2 SoCs. + * + * The RTC is split in to two parts, the AHB front end and a simple serial + * connection to the actual registers. This driver manages both parts. + * + * Copyright (c) 2018 Martin Blumenstingl + * Copyright (c) 2015 Ben Dooks for Codethink Ltd + * Based on origin by Carlo Caione + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* registers accessed from cpu bus */ +#define RTC_ADDR0 0x00 + #define RTC_ADDR0_LINE_SCLK BIT(0) + #define RTC_ADDR0_LINE_SEN BIT(1) + #define RTC_ADDR0_LINE_SDI BIT(2) + #define RTC_ADDR0_START_SER BIT(17) + #define RTC_ADDR0_WAIT_SER BIT(22) + #define RTC_ADDR0_DATA GENMASK(31, 24) + +#define RTC_ADDR1 0x04 + #define RTC_ADDR1_SDO BIT(0) + #define RTC_ADDR1_S_READY BIT(1) + +#define RTC_ADDR2 0x08 +#define RTC_ADDR3 0x0c + +#define RTC_REG4 0x10 + #define RTC_REG4_STATIC_VALUE GENMASK(7, 0) + +/* rtc registers accessed via rtc-serial interface */ +#define RTC_COUNTER (0) +#define RTC_SEC_ADJ (2) +#define RTC_REGMEM_0 (4) +#define RTC_REGMEM_1 (5) +#define RTC_REGMEM_2 (6) +#define RTC_REGMEM_3 (7) + +#define RTC_ADDR_BITS (3) /* number of address bits to send */ +#define RTC_DATA_BITS (32) /* number of data bits to tx/rx */ + +#define MESON_STATIC_BIAS_CUR (0x5 << 1) +#define MESON_STATIC_VOLTAGE (0x3 << 11) +#define MESON_STATIC_DEFAULT (MESON_STATIC_BIAS_CUR | MESON_STATIC_VOLTAGE) + +struct meson_rtc { + struct device *dev; /* device we bound from */ + struct reset_control *reset; /* reset source */ + struct regulator *vdd; /* voltage input */ + struct regmap *peripheral; /* peripheral registers */ + struct regmap *serial; /* serial registers */ +}; + +static const struct regmap_config meson_rtc_peripheral_regmap_config = { + .name = "peripheral-registers", + .reg_bits = 8, + .val_bits = 32, + .reg_stride = 4, + .max_register = RTC_REG4, +}; + +/* RTC front-end serialiser controls */ + +static void meson_rtc_sclk_pulse(struct meson_rtc *rtc) +{ + udelay(5); + regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SCLK, 0); + udelay(5); + regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SCLK, + RTC_ADDR0_LINE_SCLK); +} + +static void meson_rtc_send_bit(struct meson_rtc *rtc, unsigned int bit) +{ + regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SDI, + bit ? RTC_ADDR0_LINE_SDI : 0); + meson_rtc_sclk_pulse(rtc); +} + +static void meson_rtc_send_bits(struct meson_rtc *rtc, u32 data, + unsigned int nr) +{ + u32 bit = 1 << (nr - 1); + + while (bit) { + meson_rtc_send_bit(rtc, data & bit); + bit >>= 1; + } +} + +static void meson_rtc_set_dir(struct meson_rtc *rtc, u32 mode) +{ + regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SEN, 0); + regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SDI, 0); + meson_rtc_send_bit(rtc, mode); + regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SDI, 0); +} + +static u32 meson_rtc_get_data(struct meson_rtc *rtc) +{ + u32 tmp, val = 0; + int bit; + + for (bit = 0; bit < RTC_DATA_BITS; bit++) { + meson_rtc_sclk_pulse(rtc); + val <<= 1; + + regmap_read(rtc->peripheral, RTC_ADDR1, &tmp); + val |= tmp & RTC_ADDR1_SDO; + } + + return val; +} + +static int meson_rtc_get_bus(struct meson_rtc *rtc) +{ + int ret, retries; + u32 val; + + /* prepare bus for transfers, set all lines low */ + val = RTC_ADDR0_LINE_SDI | RTC_ADDR0_LINE_SEN | RTC_ADDR0_LINE_SCLK; + regmap_update_bits(rtc->peripheral, RTC_ADDR0, val, 0); + + for (retries = 0; retries < 3; retries++) { + /* wait for the bus to be ready */ + if (!regmap_read_poll_timeout(rtc->peripheral, RTC_ADDR1, val, + val & RTC_ADDR1_S_READY, 10, + 10000)) + return 0; + + dev_warn(rtc->dev, "failed to get bus, resetting RTC\n"); + + ret = reset_control_reset(rtc->reset); + if (ret) + return ret; + } + + dev_err(rtc->dev, "bus is not ready\n"); + return -ETIMEDOUT; +} + +static int meson_rtc_serial_bus_reg_read(void *context, unsigned int reg, + unsigned int *data) +{ + struct meson_rtc *rtc = context; + int ret; + + ret = meson_rtc_get_bus(rtc); + if (ret) + return ret; + + regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SEN, + RTC_ADDR0_LINE_SEN); + meson_rtc_send_bits(rtc, reg, RTC_ADDR_BITS); + meson_rtc_set_dir(rtc, 0); + *data = meson_rtc_get_data(rtc); + + return 0; +} + +static int meson_rtc_serial_bus_reg_write(void *context, unsigned int reg, + unsigned int data) +{ + struct meson_rtc *rtc = context; + int ret; + + ret = meson_rtc_get_bus(rtc); + if (ret) + return ret; + + regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SEN, + RTC_ADDR0_LINE_SEN); + meson_rtc_send_bits(rtc, data, RTC_DATA_BITS); + meson_rtc_send_bits(rtc, reg, RTC_ADDR_BITS); + meson_rtc_set_dir(rtc, 1); + + return 0; +} + +static const struct regmap_bus meson_rtc_serial_bus = { + .reg_read = meson_rtc_serial_bus_reg_read, + .reg_write = meson_rtc_serial_bus_reg_write, +}; + +static const struct regmap_config meson_rtc_serial_regmap_config = { + .name = "serial-registers", + .reg_bits = 4, + .reg_stride = 1, + .val_bits = 32, + .max_register = RTC_REGMEM_3, + .fast_io = false, +}; + +static int meson_rtc_write_static(struct meson_rtc *rtc, u32 data) +{ + u32 tmp; + + regmap_write(rtc->peripheral, RTC_REG4, + FIELD_PREP(RTC_REG4_STATIC_VALUE, (data >> 8))); + + /* write the static value and start the auto serializer */ + tmp = FIELD_PREP(RTC_ADDR0_DATA, (data & 0xff)) | RTC_ADDR0_START_SER; + regmap_update_bits(rtc->peripheral, RTC_ADDR0, + RTC_ADDR0_DATA | RTC_ADDR0_START_SER, tmp); + + /* wait for the auto serializer to complete */ + return regmap_read_poll_timeout(rtc->peripheral, RTC_REG4, tmp, + !(tmp & RTC_ADDR0_WAIT_SER), 10, + 10000); +} + +/* RTC interface layer functions */ + +static int meson_rtc_gettime(struct device *dev, struct rtc_time *tm) +{ + struct meson_rtc *rtc = dev_get_drvdata(dev); + u32 time; + int ret; + + ret = regmap_read(rtc->serial, RTC_COUNTER, &time); + if (!ret) + rtc_time64_to_tm(time, tm); + + return ret; +} + +static int meson_rtc_settime(struct device *dev, struct rtc_time *tm) +{ + struct meson_rtc *rtc = dev_get_drvdata(dev); + + return regmap_write(rtc->serial, RTC_COUNTER, rtc_tm_to_time64(tm)); +} + +static const struct rtc_class_ops meson_rtc_ops = { + .read_time = meson_rtc_gettime, + .set_time = meson_rtc_settime, +}; + +/* NVMEM interface layer functions */ + +static int meson_rtc_regmem_read(void *context, unsigned int offset, + void *buf, size_t bytes) +{ + struct meson_rtc *rtc = context; + unsigned int read_offset, read_size; + + read_offset = RTC_REGMEM_0 + (offset / 4); + read_size = bytes / 4; + + return regmap_bulk_read(rtc->serial, read_offset, buf, read_size); +} + +static int meson_rtc_regmem_write(void *context, unsigned int offset, + void *buf, size_t bytes) +{ + struct meson_rtc *rtc = context; + unsigned int write_offset, write_size; + + write_offset = RTC_REGMEM_0 + (offset / 4); + write_size = bytes / 4; + + return regmap_bulk_write(rtc->serial, write_offset, buf, write_size); +} + +static int meson_rtc_probe(struct platform_device *pdev) +{ + struct nvmem_config meson_rtc_nvmem_config = { + .name = "meson-rtc-regmem", + .type = NVMEM_TYPE_BATTERY_BACKED, + .word_size = 4, + .stride = 4, + .size = 4 * 4, + .reg_read = meson_rtc_regmem_read, + .reg_write = meson_rtc_regmem_write, + }; + struct device *dev = &pdev->dev; + struct meson_rtc *rtc; + struct rtc_device *rtc_dev; + void __iomem *base; + int ret; + u32 tm; + + rtc = devm_kzalloc(dev, sizeof(struct meson_rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc_dev = devm_rtc_allocate_device(dev); + if (IS_ERR(rtc_dev)) + return PTR_ERR(rtc_dev); + + platform_set_drvdata(pdev, rtc); + + rtc->dev = dev; + + rtc_dev->ops = &meson_rtc_ops; + rtc_dev->range_max = U32_MAX; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + rtc->peripheral = devm_regmap_init_mmio(dev, base, + &meson_rtc_peripheral_regmap_config); + if (IS_ERR(rtc->peripheral)) { + dev_err(dev, "failed to create peripheral regmap\n"); + return PTR_ERR(rtc->peripheral); + } + + rtc->reset = devm_reset_control_get(dev, NULL); + if (IS_ERR(rtc->reset)) { + dev_err(dev, "missing reset line\n"); + return PTR_ERR(rtc->reset); + } + + rtc->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(rtc->vdd)) { + dev_err(dev, "failed to get the vdd-supply\n"); + return PTR_ERR(rtc->vdd); + } + + ret = regulator_enable(rtc->vdd); + if (ret) { + dev_err(dev, "failed to enable vdd-supply\n"); + return ret; + } + + ret = meson_rtc_write_static(rtc, MESON_STATIC_DEFAULT); + if (ret) { + dev_err(dev, "failed to set static values\n"); + goto out_disable_vdd; + } + + rtc->serial = devm_regmap_init(dev, &meson_rtc_serial_bus, rtc, + &meson_rtc_serial_regmap_config); + if (IS_ERR(rtc->serial)) { + dev_err(dev, "failed to create serial regmap\n"); + ret = PTR_ERR(rtc->serial); + goto out_disable_vdd; + } + + /* + * check if we can read RTC counter, if not then the RTC is probably + * not functional. If it isn't probably best to not bind. + */ + ret = regmap_read(rtc->serial, RTC_COUNTER, &tm); + if (ret) { + dev_err(dev, "cannot read RTC counter, RTC not functional\n"); + goto out_disable_vdd; + } + + meson_rtc_nvmem_config.priv = rtc; + ret = devm_rtc_nvmem_register(rtc_dev, &meson_rtc_nvmem_config); + if (ret) + goto out_disable_vdd; + + ret = devm_rtc_register_device(rtc_dev); + if (ret) + goto out_disable_vdd; + + return 0; + +out_disable_vdd: + regulator_disable(rtc->vdd); + return ret; +} + +static const __maybe_unused struct of_device_id meson_rtc_dt_match[] = { + { .compatible = "amlogic,meson6-rtc", }, + { .compatible = "amlogic,meson8-rtc", }, + { .compatible = "amlogic,meson8b-rtc", }, + { .compatible = "amlogic,meson8m2-rtc", }, + { }, +}; +MODULE_DEVICE_TABLE(of, meson_rtc_dt_match); + +static struct platform_driver meson_rtc_driver = { + .probe = meson_rtc_probe, + .driver = { + .name = "meson-rtc", + .of_match_table = of_match_ptr(meson_rtc_dt_match), + }, +}; +module_platform_driver(meson_rtc_driver); + +MODULE_DESCRIPTION("Amlogic Meson RTC Driver"); +MODULE_AUTHOR("Ben Dooks "); +MODULE_AUTHOR("Martin Blumenstingl "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:meson-rtc"); diff --git a/drivers/rtc/rtc-moxart.c b/drivers/rtc/rtc-moxart.c new file mode 100644 index 000000000..bc0345db6 --- /dev/null +++ b/drivers/rtc/rtc-moxart.c @@ -0,0 +1,307 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MOXA ART RTC driver. + * + * Copyright (C) 2013 Jonas Jensen + * + * Jonas Jensen + * + * Based on code from + * Moxa Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define GPIO_RTC_RESERVED 0x0C +#define GPIO_RTC_DATA_SET 0x10 +#define GPIO_RTC_DATA_CLEAR 0x14 +#define GPIO_RTC_PIN_PULL_ENABLE 0x18 +#define GPIO_RTC_PIN_PULL_TYPE 0x1C +#define GPIO_RTC_INT_ENABLE 0x20 +#define GPIO_RTC_INT_RAW_STATE 0x24 +#define GPIO_RTC_INT_MASKED_STATE 0x28 +#define GPIO_RTC_INT_MASK 0x2C +#define GPIO_RTC_INT_CLEAR 0x30 +#define GPIO_RTC_INT_TRIGGER 0x34 +#define GPIO_RTC_INT_BOTH 0x38 +#define GPIO_RTC_INT_RISE_NEG 0x3C +#define GPIO_RTC_BOUNCE_ENABLE 0x40 +#define GPIO_RTC_BOUNCE_PRE_SCALE 0x44 +#define GPIO_RTC_PROTECT_W 0x8E +#define GPIO_RTC_PROTECT_R 0x8F +#define GPIO_RTC_YEAR_W 0x8C +#define GPIO_RTC_YEAR_R 0x8D +#define GPIO_RTC_DAY_W 0x8A +#define GPIO_RTC_DAY_R 0x8B +#define GPIO_RTC_MONTH_W 0x88 +#define GPIO_RTC_MONTH_R 0x89 +#define GPIO_RTC_DATE_W 0x86 +#define GPIO_RTC_DATE_R 0x87 +#define GPIO_RTC_HOURS_W 0x84 +#define GPIO_RTC_HOURS_R 0x85 +#define GPIO_RTC_MINUTES_W 0x82 +#define GPIO_RTC_MINUTES_R 0x83 +#define GPIO_RTC_SECONDS_W 0x80 +#define GPIO_RTC_SECONDS_R 0x81 +#define GPIO_RTC_DELAY_TIME 8 + +struct moxart_rtc { + struct rtc_device *rtc; + spinlock_t rtc_lock; + struct gpio_desc *gpio_data; + struct gpio_desc *gpio_sclk; + struct gpio_desc *gpio_reset; +}; + +static int day_of_year[12] = { 0, 31, 59, 90, 120, 151, 181, + 212, 243, 273, 304, 334 }; + +static void moxart_rtc_write_byte(struct device *dev, u8 data) +{ + struct moxart_rtc *moxart_rtc = dev_get_drvdata(dev); + int i; + + for (i = 0; i < 8; i++, data >>= 1) { + gpiod_set_value(moxart_rtc->gpio_sclk, 0); + gpiod_set_value(moxart_rtc->gpio_data, ((data & 1) == 1)); + udelay(GPIO_RTC_DELAY_TIME); + gpiod_set_value(moxart_rtc->gpio_sclk, 1); + udelay(GPIO_RTC_DELAY_TIME); + } +} + +static u8 moxart_rtc_read_byte(struct device *dev) +{ + struct moxart_rtc *moxart_rtc = dev_get_drvdata(dev); + int i; + u8 data = 0; + + for (i = 0; i < 8; i++) { + gpiod_set_value(moxart_rtc->gpio_sclk, 0); + udelay(GPIO_RTC_DELAY_TIME); + gpiod_set_value(moxart_rtc->gpio_sclk, 1); + udelay(GPIO_RTC_DELAY_TIME); + if (gpiod_get_value(moxart_rtc->gpio_data)) + data |= (1 << i); + udelay(GPIO_RTC_DELAY_TIME); + } + return data; +} + +static u8 moxart_rtc_read_register(struct device *dev, u8 cmd) +{ + struct moxart_rtc *moxart_rtc = dev_get_drvdata(dev); + u8 data; + unsigned long flags; + + local_irq_save(flags); + + gpiod_direction_output(moxart_rtc->gpio_data, 0); + gpiod_set_value(moxart_rtc->gpio_reset, 1); + udelay(GPIO_RTC_DELAY_TIME); + moxart_rtc_write_byte(dev, cmd); + gpiod_direction_input(moxart_rtc->gpio_data); + udelay(GPIO_RTC_DELAY_TIME); + data = moxart_rtc_read_byte(dev); + gpiod_set_value(moxart_rtc->gpio_sclk, 0); + gpiod_set_value(moxart_rtc->gpio_reset, 0); + udelay(GPIO_RTC_DELAY_TIME); + + local_irq_restore(flags); + + return data; +} + +static void moxart_rtc_write_register(struct device *dev, u8 cmd, u8 data) +{ + struct moxart_rtc *moxart_rtc = dev_get_drvdata(dev); + unsigned long flags; + + local_irq_save(flags); + + gpiod_direction_output(moxart_rtc->gpio_data, 0); + gpiod_set_value(moxart_rtc->gpio_reset, 1); + udelay(GPIO_RTC_DELAY_TIME); + moxart_rtc_write_byte(dev, cmd); + moxart_rtc_write_byte(dev, data); + gpiod_set_value(moxart_rtc->gpio_sclk, 0); + gpiod_set_value(moxart_rtc->gpio_reset, 0); + udelay(GPIO_RTC_DELAY_TIME); + + local_irq_restore(flags); +} + +static int moxart_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct moxart_rtc *moxart_rtc = dev_get_drvdata(dev); + + spin_lock_irq(&moxart_rtc->rtc_lock); + + moxart_rtc_write_register(dev, GPIO_RTC_PROTECT_W, 0); + moxart_rtc_write_register(dev, GPIO_RTC_YEAR_W, + (((tm->tm_year - 100) / 10) << 4) | + ((tm->tm_year - 100) % 10)); + + moxart_rtc_write_register(dev, GPIO_RTC_MONTH_W, + (((tm->tm_mon + 1) / 10) << 4) | + ((tm->tm_mon + 1) % 10)); + + moxart_rtc_write_register(dev, GPIO_RTC_DATE_W, + ((tm->tm_mday / 10) << 4) | + (tm->tm_mday % 10)); + + moxart_rtc_write_register(dev, GPIO_RTC_HOURS_W, + ((tm->tm_hour / 10) << 4) | + (tm->tm_hour % 10)); + + moxart_rtc_write_register(dev, GPIO_RTC_MINUTES_W, + ((tm->tm_min / 10) << 4) | + (tm->tm_min % 10)); + + moxart_rtc_write_register(dev, GPIO_RTC_SECONDS_W, + ((tm->tm_sec / 10) << 4) | + (tm->tm_sec % 10)); + + moxart_rtc_write_register(dev, GPIO_RTC_PROTECT_W, 0x80); + + spin_unlock_irq(&moxart_rtc->rtc_lock); + + dev_dbg(dev, "%s: success tm_year=%d tm_mon=%d\n" + "tm_mday=%d tm_hour=%d tm_min=%d tm_sec=%d\n", + __func__, tm->tm_year, tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + return 0; +} + +static int moxart_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct moxart_rtc *moxart_rtc = dev_get_drvdata(dev); + unsigned char v; + + spin_lock_irq(&moxart_rtc->rtc_lock); + + v = moxart_rtc_read_register(dev, GPIO_RTC_SECONDS_R); + tm->tm_sec = (((v & 0x70) >> 4) * 10) + (v & 0x0F); + + v = moxart_rtc_read_register(dev, GPIO_RTC_MINUTES_R); + tm->tm_min = (((v & 0x70) >> 4) * 10) + (v & 0x0F); + + v = moxart_rtc_read_register(dev, GPIO_RTC_HOURS_R); + if (v & 0x80) { /* 12-hour mode */ + tm->tm_hour = (((v & 0x10) >> 4) * 10) + (v & 0x0F); + if (v & 0x20) { /* PM mode */ + tm->tm_hour += 12; + if (tm->tm_hour >= 24) + tm->tm_hour = 0; + } + } else { /* 24-hour mode */ + tm->tm_hour = (((v & 0x30) >> 4) * 10) + (v & 0x0F); + } + + v = moxart_rtc_read_register(dev, GPIO_RTC_DATE_R); + tm->tm_mday = (((v & 0x30) >> 4) * 10) + (v & 0x0F); + + v = moxart_rtc_read_register(dev, GPIO_RTC_MONTH_R); + tm->tm_mon = (((v & 0x10) >> 4) * 10) + (v & 0x0F); + tm->tm_mon--; + + v = moxart_rtc_read_register(dev, GPIO_RTC_YEAR_R); + tm->tm_year = (((v & 0xF0) >> 4) * 10) + (v & 0x0F); + tm->tm_year += 100; + if (tm->tm_year <= 69) + tm->tm_year += 100; + + v = moxart_rtc_read_register(dev, GPIO_RTC_DAY_R); + tm->tm_wday = (v & 0x0f) - 1; + tm->tm_yday = day_of_year[tm->tm_mon]; + tm->tm_yday += (tm->tm_mday - 1); + if (tm->tm_mon >= 2) { + if (!(tm->tm_year % 4) && (tm->tm_year % 100)) + tm->tm_yday++; + } + + tm->tm_isdst = 0; + + spin_unlock_irq(&moxart_rtc->rtc_lock); + + return 0; +} + +static const struct rtc_class_ops moxart_rtc_ops = { + .read_time = moxart_rtc_read_time, + .set_time = moxart_rtc_set_time, +}; + +static int moxart_rtc_probe(struct platform_device *pdev) +{ + struct moxart_rtc *moxart_rtc; + int ret = 0; + + moxart_rtc = devm_kzalloc(&pdev->dev, sizeof(*moxart_rtc), GFP_KERNEL); + if (!moxart_rtc) + return -ENOMEM; + + moxart_rtc->gpio_data = devm_gpiod_get(&pdev->dev, "rtc-data", + GPIOD_IN); + ret = PTR_ERR_OR_ZERO(moxart_rtc->gpio_data); + if (ret) { + dev_err(&pdev->dev, "can't get rtc data gpio: %d\n", ret); + return ret; + } + + moxart_rtc->gpio_sclk = devm_gpiod_get(&pdev->dev, "rtc-sclk", + GPIOD_ASIS); + ret = PTR_ERR_OR_ZERO(moxart_rtc->gpio_sclk); + if (ret) { + dev_err(&pdev->dev, "can't get rtc sclk gpio: %d\n", ret); + return ret; + } + + moxart_rtc->gpio_reset = devm_gpiod_get(&pdev->dev, "rtc-reset", + GPIOD_ASIS); + ret = PTR_ERR_OR_ZERO(moxart_rtc->gpio_reset); + if (ret) { + dev_err(&pdev->dev, "can't get rtc reset gpio: %d\n", ret); + return ret; + } + + spin_lock_init(&moxart_rtc->rtc_lock); + platform_set_drvdata(pdev, moxart_rtc); + + moxart_rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + &moxart_rtc_ops, + THIS_MODULE); + if (IS_ERR(moxart_rtc->rtc)) { + dev_err(&pdev->dev, "devm_rtc_device_register failed\n"); + return PTR_ERR(moxart_rtc->rtc); + } + + return 0; +} + +static const struct of_device_id moxart_rtc_match[] = { + { .compatible = "moxa,moxart-rtc" }, + { }, +}; +MODULE_DEVICE_TABLE(of, moxart_rtc_match); + +static struct platform_driver moxart_rtc_driver = { + .probe = moxart_rtc_probe, + .driver = { + .name = "moxart-rtc", + .of_match_table = moxart_rtc_match, + }, +}; +module_platform_driver(moxart_rtc_driver); + +MODULE_DESCRIPTION("MOXART RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jonas Jensen "); diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c new file mode 100644 index 000000000..b90f8337a --- /dev/null +++ b/drivers/rtc/rtc-mpc5121.c @@ -0,0 +1,408 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Real-time clock driver for MPC5121 + * + * Copyright 2007, Domen Puncer + * Copyright 2008, Freescale Semiconductor, Inc. All rights reserved. + * Copyright 2011, Dmitry Eremin-Solenikov + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct mpc5121_rtc_regs { + u8 set_time; /* RTC + 0x00 */ + u8 hour_set; /* RTC + 0x01 */ + u8 minute_set; /* RTC + 0x02 */ + u8 second_set; /* RTC + 0x03 */ + + u8 set_date; /* RTC + 0x04 */ + u8 month_set; /* RTC + 0x05 */ + u8 weekday_set; /* RTC + 0x06 */ + u8 date_set; /* RTC + 0x07 */ + + u8 write_sw; /* RTC + 0x08 */ + u8 sw_set; /* RTC + 0x09 */ + u16 year_set; /* RTC + 0x0a */ + + u8 alm_enable; /* RTC + 0x0c */ + u8 alm_hour_set; /* RTC + 0x0d */ + u8 alm_min_set; /* RTC + 0x0e */ + u8 int_enable; /* RTC + 0x0f */ + + u8 reserved1; + u8 hour; /* RTC + 0x11 */ + u8 minute; /* RTC + 0x12 */ + u8 second; /* RTC + 0x13 */ + + u8 month; /* RTC + 0x14 */ + u8 wday_mday; /* RTC + 0x15 */ + u16 year; /* RTC + 0x16 */ + + u8 int_alm; /* RTC + 0x18 */ + u8 int_sw; /* RTC + 0x19 */ + u8 alm_status; /* RTC + 0x1a */ + u8 sw_minute; /* RTC + 0x1b */ + + u8 bus_error_1; /* RTC + 0x1c */ + u8 int_day; /* RTC + 0x1d */ + u8 int_min; /* RTC + 0x1e */ + u8 int_sec; /* RTC + 0x1f */ + + /* + * target_time: + * intended to be used for hibernation but hibernation + * does not work on silicon rev 1.5 so use it for non-volatile + * storage of offset between the actual_time register and linux + * time + */ + u32 target_time; /* RTC + 0x20 */ + /* + * actual_time: + * readonly time since VBAT_RTC was last connected + */ + u32 actual_time; /* RTC + 0x24 */ + u32 keep_alive; /* RTC + 0x28 */ +}; + +struct mpc5121_rtc_data { + unsigned irq; + unsigned irq_periodic; + struct mpc5121_rtc_regs __iomem *regs; + struct rtc_device *rtc; + struct rtc_wkalrm wkalarm; +}; + +/* + * Update second/minute/hour registers. + * + * This is just so alarm will work. + */ +static void mpc5121_rtc_update_smh(struct mpc5121_rtc_regs __iomem *regs, + struct rtc_time *tm) +{ + out_8(®s->second_set, tm->tm_sec); + out_8(®s->minute_set, tm->tm_min); + out_8(®s->hour_set, tm->tm_hour); + + /* set time sequence */ + out_8(®s->set_time, 0x1); + out_8(®s->set_time, 0x3); + out_8(®s->set_time, 0x1); + out_8(®s->set_time, 0x0); +} + +static int mpc5121_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev); + struct mpc5121_rtc_regs __iomem *regs = rtc->regs; + unsigned long now; + + /* + * linux time is actual_time plus the offset saved in target_time + */ + now = in_be32(®s->actual_time) + in_be32(®s->target_time); + + rtc_time64_to_tm(now, tm); + + /* + * update second minute hour registers + * so alarms will work + */ + mpc5121_rtc_update_smh(regs, tm); + + return 0; +} + +static int mpc5121_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev); + struct mpc5121_rtc_regs __iomem *regs = rtc->regs; + unsigned long now; + + /* + * The actual_time register is read only so we write the offset + * between it and linux time to the target_time register. + */ + now = rtc_tm_to_time64(tm); + out_be32(®s->target_time, now - in_be32(®s->actual_time)); + + /* + * update second minute hour registers + * so alarms will work + */ + mpc5121_rtc_update_smh(regs, tm); + + return 0; +} + +static int mpc5200_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev); + struct mpc5121_rtc_regs __iomem *regs = rtc->regs; + int tmp; + + tm->tm_sec = in_8(®s->second); + tm->tm_min = in_8(®s->minute); + + /* 12 hour format? */ + if (in_8(®s->hour) & 0x20) + tm->tm_hour = (in_8(®s->hour) >> 1) + + (in_8(®s->hour) & 1 ? 12 : 0); + else + tm->tm_hour = in_8(®s->hour); + + tmp = in_8(®s->wday_mday); + tm->tm_mday = tmp & 0x1f; + tm->tm_mon = in_8(®s->month) - 1; + tm->tm_year = in_be16(®s->year) - 1900; + tm->tm_wday = (tmp >> 5) % 7; + tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year); + tm->tm_isdst = 0; + + return 0; +} + +static int mpc5200_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev); + struct mpc5121_rtc_regs __iomem *regs = rtc->regs; + + mpc5121_rtc_update_smh(regs, tm); + + /* date */ + out_8(®s->month_set, tm->tm_mon + 1); + out_8(®s->weekday_set, tm->tm_wday ? tm->tm_wday : 7); + out_8(®s->date_set, tm->tm_mday); + out_be16(®s->year_set, tm->tm_year + 1900); + + /* set date sequence */ + out_8(®s->set_date, 0x1); + out_8(®s->set_date, 0x3); + out_8(®s->set_date, 0x1); + out_8(®s->set_date, 0x0); + + return 0; +} + +static int mpc5121_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev); + struct mpc5121_rtc_regs __iomem *regs = rtc->regs; + + *alarm = rtc->wkalarm; + + alarm->pending = in_8(®s->alm_status); + + return 0; +} + +static int mpc5121_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev); + struct mpc5121_rtc_regs __iomem *regs = rtc->regs; + + alarm->time.tm_mday = -1; + alarm->time.tm_mon = -1; + alarm->time.tm_year = -1; + + out_8(®s->alm_min_set, alarm->time.tm_min); + out_8(®s->alm_hour_set, alarm->time.tm_hour); + + out_8(®s->alm_enable, alarm->enabled); + + rtc->wkalarm = *alarm; + return 0; +} + +static irqreturn_t mpc5121_rtc_handler(int irq, void *dev) +{ + struct mpc5121_rtc_data *rtc = dev_get_drvdata((struct device *)dev); + struct mpc5121_rtc_regs __iomem *regs = rtc->regs; + + if (in_8(®s->int_alm)) { + /* acknowledge and clear status */ + out_8(®s->int_alm, 1); + out_8(®s->alm_status, 1); + + rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static irqreturn_t mpc5121_rtc_handler_upd(int irq, void *dev) +{ + struct mpc5121_rtc_data *rtc = dev_get_drvdata((struct device *)dev); + struct mpc5121_rtc_regs __iomem *regs = rtc->regs; + + if (in_8(®s->int_sec) && (in_8(®s->int_enable) & 0x1)) { + /* acknowledge */ + out_8(®s->int_sec, 1); + + rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_UF); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int mpc5121_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev); + struct mpc5121_rtc_regs __iomem *regs = rtc->regs; + int val; + + if (enabled) + val = 1; + else + val = 0; + + out_8(®s->alm_enable, val); + rtc->wkalarm.enabled = val; + + return 0; +} + +static const struct rtc_class_ops mpc5121_rtc_ops = { + .read_time = mpc5121_rtc_read_time, + .set_time = mpc5121_rtc_set_time, + .read_alarm = mpc5121_rtc_read_alarm, + .set_alarm = mpc5121_rtc_set_alarm, + .alarm_irq_enable = mpc5121_rtc_alarm_irq_enable, +}; + +static const struct rtc_class_ops mpc5200_rtc_ops = { + .read_time = mpc5200_rtc_read_time, + .set_time = mpc5200_rtc_set_time, + .read_alarm = mpc5121_rtc_read_alarm, + .set_alarm = mpc5121_rtc_set_alarm, + .alarm_irq_enable = mpc5121_rtc_alarm_irq_enable, +}; + +static int mpc5121_rtc_probe(struct platform_device *op) +{ + struct mpc5121_rtc_data *rtc; + int err = 0; + + rtc = devm_kzalloc(&op->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->regs = devm_platform_ioremap_resource(op, 0); + if (IS_ERR(rtc->regs)) { + dev_err(&op->dev, "%s: couldn't map io space\n", __func__); + return PTR_ERR(rtc->regs); + } + + device_init_wakeup(&op->dev, true); + + platform_set_drvdata(op, rtc); + + rtc->irq = irq_of_parse_and_map(op->dev.of_node, 1); + err = devm_request_irq(&op->dev, rtc->irq, mpc5121_rtc_handler, 0, + "mpc5121-rtc", &op->dev); + if (err) { + dev_err(&op->dev, "%s: could not request irq: %i\n", + __func__, rtc->irq); + goto out_dispose; + } + + rtc->irq_periodic = irq_of_parse_and_map(op->dev.of_node, 0); + err = devm_request_irq(&op->dev, rtc->irq_periodic, + mpc5121_rtc_handler_upd, 0, "mpc5121-rtc_upd", + &op->dev); + if (err) { + dev_err(&op->dev, "%s: could not request irq: %i\n", + __func__, rtc->irq_periodic); + goto out_dispose2; + } + + rtc->rtc = devm_rtc_allocate_device(&op->dev); + if (IS_ERR(rtc->rtc)) { + err = PTR_ERR(rtc->rtc); + goto out_dispose2; + } + + rtc->rtc->ops = &mpc5200_rtc_ops; + set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->rtc->features); + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->rtc->features); + rtc->rtc->range_min = RTC_TIMESTAMP_BEGIN_0000; + rtc->rtc->range_max = 65733206399ULL; /* 4052-12-31 23:59:59 */ + + if (of_device_is_compatible(op->dev.of_node, "fsl,mpc5121-rtc")) { + u32 ka; + ka = in_be32(&rtc->regs->keep_alive); + if (ka & 0x02) { + dev_warn(&op->dev, + "mpc5121-rtc: Battery or oscillator failure!\n"); + out_be32(&rtc->regs->keep_alive, ka); + } + rtc->rtc->ops = &mpc5121_rtc_ops; + /* + * This is a limitation of the driver that abuses the target + * time register, the actual maximum year for the mpc5121 is + * also 4052. + */ + rtc->rtc->range_min = 0; + rtc->rtc->range_max = U32_MAX; + } + + err = devm_rtc_register_device(rtc->rtc); + if (err) + goto out_dispose2; + + return 0; + +out_dispose2: + irq_dispose_mapping(rtc->irq_periodic); +out_dispose: + irq_dispose_mapping(rtc->irq); + + return err; +} + +static void mpc5121_rtc_remove(struct platform_device *op) +{ + struct mpc5121_rtc_data *rtc = platform_get_drvdata(op); + struct mpc5121_rtc_regs __iomem *regs = rtc->regs; + + /* disable interrupt, so there are no nasty surprises */ + out_8(®s->alm_enable, 0); + out_8(®s->int_enable, in_8(®s->int_enable) & ~0x1); + + irq_dispose_mapping(rtc->irq); + irq_dispose_mapping(rtc->irq_periodic); +} + +#ifdef CONFIG_OF +static const struct of_device_id mpc5121_rtc_match[] = { + { .compatible = "fsl,mpc5121-rtc", }, + { .compatible = "fsl,mpc5200-rtc", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mpc5121_rtc_match); +#endif + +static struct platform_driver mpc5121_rtc_driver = { + .driver = { + .name = "mpc5121-rtc", + .of_match_table = of_match_ptr(mpc5121_rtc_match), + }, + .probe = mpc5121_rtc_probe, + .remove = mpc5121_rtc_remove, +}; + +module_platform_driver(mpc5121_rtc_driver); + +MODULE_DESCRIPTION("Freescale MPC5121 built-in RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("John Rigby "); diff --git a/drivers/rtc/rtc-mpfs.c b/drivers/rtc/rtc-mpfs.c new file mode 100644 index 000000000..ece6de4a6 --- /dev/null +++ b/drivers/rtc/rtc-mpfs.c @@ -0,0 +1,297 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip MPFS RTC driver + * + * Copyright (c) 2021-2022 Microchip Corporation. All rights reserved. + * + * Author: Daire McNamara + * & Conor Dooley + */ +#include "linux/bits.h" +#include "linux/iopoll.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CONTROL_REG 0x00 +#define MODE_REG 0x04 +#define PRESCALER_REG 0x08 +#define ALARM_LOWER_REG 0x0c +#define ALARM_UPPER_REG 0x10 +#define COMPARE_LOWER_REG 0x14 +#define COMPARE_UPPER_REG 0x18 +#define DATETIME_LOWER_REG 0x20 +#define DATETIME_UPPER_REG 0x24 + +#define CONTROL_RUNNING_BIT BIT(0) +#define CONTROL_START_BIT BIT(0) +#define CONTROL_STOP_BIT BIT(1) +#define CONTROL_ALARM_ON_BIT BIT(2) +#define CONTROL_ALARM_OFF_BIT BIT(3) +#define CONTROL_RESET_BIT BIT(4) +#define CONTROL_UPLOAD_BIT BIT(5) +#define CONTROL_DOWNLOAD_BIT BIT(6) +#define CONTROL_MATCH_BIT BIT(7) +#define CONTROL_WAKEUP_CLR_BIT BIT(8) +#define CONTROL_WAKEUP_SET_BIT BIT(9) +#define CONTROL_UPDATED_BIT BIT(10) + +#define MODE_CLOCK_CALENDAR BIT(0) +#define MODE_WAKE_EN BIT(1) +#define MODE_WAKE_RESET BIT(2) +#define MODE_WAKE_CONTINUE BIT(3) + +#define MAX_PRESCALER_COUNT GENMASK(25, 0) +#define DATETIME_UPPER_MASK GENMASK(29, 0) +#define ALARM_UPPER_MASK GENMASK(10, 0) + +#define UPLOAD_TIMEOUT_US 50 + +struct mpfs_rtc_dev { + struct rtc_device *rtc; + void __iomem *base; +}; + +static void mpfs_rtc_start(struct mpfs_rtc_dev *rtcdev) +{ + u32 ctrl; + + ctrl = readl(rtcdev->base + CONTROL_REG); + ctrl &= ~CONTROL_STOP_BIT; + ctrl |= CONTROL_START_BIT; + writel(ctrl, rtcdev->base + CONTROL_REG); +} + +static void mpfs_rtc_clear_irq(struct mpfs_rtc_dev *rtcdev) +{ + u32 val = readl(rtcdev->base + CONTROL_REG); + + val &= ~(CONTROL_ALARM_ON_BIT | CONTROL_STOP_BIT); + val |= CONTROL_ALARM_OFF_BIT; + writel(val, rtcdev->base + CONTROL_REG); + /* + * Ensure that the posted write to the CONTROL_REG register completed before + * returning from this function. Not doing this may result in the interrupt + * only being cleared some time after this function returns. + */ + (void)readl(rtcdev->base + CONTROL_REG); +} + +static int mpfs_rtc_readtime(struct device *dev, struct rtc_time *tm) +{ + struct mpfs_rtc_dev *rtcdev = dev_get_drvdata(dev); + u64 time; + + time = readl(rtcdev->base + DATETIME_LOWER_REG); + time |= ((u64)readl(rtcdev->base + DATETIME_UPPER_REG) & DATETIME_UPPER_MASK) << 32; + rtc_time64_to_tm(time, tm); + + return 0; +} + +static int mpfs_rtc_settime(struct device *dev, struct rtc_time *tm) +{ + struct mpfs_rtc_dev *rtcdev = dev_get_drvdata(dev); + u32 ctrl, prog; + u64 time; + int ret; + + time = rtc_tm_to_time64(tm); + + writel((u32)time, rtcdev->base + DATETIME_LOWER_REG); + writel((u32)(time >> 32) & DATETIME_UPPER_MASK, rtcdev->base + DATETIME_UPPER_REG); + + ctrl = readl(rtcdev->base + CONTROL_REG); + ctrl &= ~CONTROL_STOP_BIT; + ctrl |= CONTROL_UPLOAD_BIT; + writel(ctrl, rtcdev->base + CONTROL_REG); + + ret = read_poll_timeout(readl, prog, !(prog & CONTROL_UPLOAD_BIT), 0, UPLOAD_TIMEOUT_US, + false, rtcdev->base + CONTROL_REG); + if (ret) { + dev_err(dev, "timed out uploading time to rtc"); + return ret; + } + mpfs_rtc_start(rtcdev); + + return 0; +} + +static int mpfs_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct mpfs_rtc_dev *rtcdev = dev_get_drvdata(dev); + u32 mode = readl(rtcdev->base + MODE_REG); + u64 time; + + alrm->enabled = mode & MODE_WAKE_EN; + + time = (u64)readl(rtcdev->base + ALARM_LOWER_REG) << 32; + time |= (readl(rtcdev->base + ALARM_UPPER_REG) & ALARM_UPPER_MASK); + rtc_time64_to_tm(time, &alrm->time); + + return 0; +} + +static int mpfs_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct mpfs_rtc_dev *rtcdev = dev_get_drvdata(dev); + u32 mode, ctrl; + u64 time; + + /* Disable the alarm before updating */ + ctrl = readl(rtcdev->base + CONTROL_REG); + ctrl |= CONTROL_ALARM_OFF_BIT; + writel(ctrl, rtcdev->base + CONTROL_REG); + + time = rtc_tm_to_time64(&alrm->time); + + writel((u32)time, rtcdev->base + ALARM_LOWER_REG); + writel((u32)(time >> 32) & ALARM_UPPER_MASK, rtcdev->base + ALARM_UPPER_REG); + + /* Bypass compare register in alarm mode */ + writel(GENMASK(31, 0), rtcdev->base + COMPARE_LOWER_REG); + writel(GENMASK(29, 0), rtcdev->base + COMPARE_UPPER_REG); + + /* Configure the RTC to enable the alarm. */ + ctrl = readl(rtcdev->base + CONTROL_REG); + mode = readl(rtcdev->base + MODE_REG); + if (alrm->enabled) { + mode = MODE_WAKE_EN | MODE_WAKE_CONTINUE; + /* Enable the alarm */ + ctrl &= ~CONTROL_ALARM_OFF_BIT; + ctrl |= CONTROL_ALARM_ON_BIT; + } + ctrl &= ~CONTROL_STOP_BIT; + ctrl |= CONTROL_START_BIT; + writel(ctrl, rtcdev->base + CONTROL_REG); + writel(mode, rtcdev->base + MODE_REG); + + return 0; +} + +static int mpfs_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct mpfs_rtc_dev *rtcdev = dev_get_drvdata(dev); + u32 ctrl; + + ctrl = readl(rtcdev->base + CONTROL_REG); + ctrl &= ~(CONTROL_ALARM_ON_BIT | CONTROL_ALARM_OFF_BIT | CONTROL_STOP_BIT); + + if (enabled) + ctrl |= CONTROL_ALARM_ON_BIT; + else + ctrl |= CONTROL_ALARM_OFF_BIT; + + writel(ctrl, rtcdev->base + CONTROL_REG); + + return 0; +} + +static irqreturn_t mpfs_rtc_wakeup_irq_handler(int irq, void *dev) +{ + struct mpfs_rtc_dev *rtcdev = dev; + + mpfs_rtc_clear_irq(rtcdev); + + rtc_update_irq(rtcdev->rtc, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops mpfs_rtc_ops = { + .read_time = mpfs_rtc_readtime, + .set_time = mpfs_rtc_settime, + .read_alarm = mpfs_rtc_readalarm, + .set_alarm = mpfs_rtc_setalarm, + .alarm_irq_enable = mpfs_rtc_alarm_irq_enable, +}; + +static int mpfs_rtc_probe(struct platform_device *pdev) +{ + struct mpfs_rtc_dev *rtcdev; + struct clk *clk; + unsigned long prescaler; + int wakeup_irq, ret; + + rtcdev = devm_kzalloc(&pdev->dev, sizeof(struct mpfs_rtc_dev), GFP_KERNEL); + if (!rtcdev) + return -ENOMEM; + + platform_set_drvdata(pdev, rtcdev); + + rtcdev->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtcdev->rtc)) + return PTR_ERR(rtcdev->rtc); + + rtcdev->rtc->ops = &mpfs_rtc_ops; + + /* range is capped by alarm max, lower reg is 31:0 & upper is 10:0 */ + rtcdev->rtc->range_max = GENMASK_ULL(42, 0); + + clk = devm_clk_get_enabled(&pdev->dev, "rtc"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + rtcdev->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rtcdev->base)) { + dev_dbg(&pdev->dev, "invalid ioremap resources\n"); + return PTR_ERR(rtcdev->base); + } + + wakeup_irq = platform_get_irq(pdev, 0); + if (wakeup_irq <= 0) { + dev_dbg(&pdev->dev, "could not get wakeup irq\n"); + return wakeup_irq; + } + ret = devm_request_irq(&pdev->dev, wakeup_irq, mpfs_rtc_wakeup_irq_handler, 0, + dev_name(&pdev->dev), rtcdev); + if (ret) { + dev_dbg(&pdev->dev, "could not request wakeup irq\n"); + return ret; + } + + /* prescaler hardware adds 1 to reg value */ + prescaler = clk_get_rate(devm_clk_get(&pdev->dev, "rtcref")) - 1; + if (prescaler > MAX_PRESCALER_COUNT) { + dev_dbg(&pdev->dev, "invalid prescaler %lu\n", prescaler); + return -EINVAL; + } + + writel(prescaler, rtcdev->base + PRESCALER_REG); + dev_info(&pdev->dev, "prescaler set to: %lu\n", prescaler); + + devm_device_init_wakeup(&pdev->dev); + ret = devm_pm_set_wake_irq(&pdev->dev, wakeup_irq); + if (ret) + dev_err(&pdev->dev, "failed to enable irq wake\n"); + + return devm_rtc_register_device(rtcdev->rtc); +} + +static const struct of_device_id mpfs_rtc_of_match[] = { + { .compatible = "microchip,mpfs-rtc" }, + { } +}; + +MODULE_DEVICE_TABLE(of, mpfs_rtc_of_match); + +static struct platform_driver mpfs_rtc_driver = { + .probe = mpfs_rtc_probe, + .driver = { + .name = "mpfs_rtc", + .of_match_table = mpfs_rtc_of_match, + }, +}; + +module_platform_driver(mpfs_rtc_driver); + +MODULE_DESCRIPTION("Real time clock for Microchip Polarfire SoC"); +MODULE_AUTHOR("Daire McNamara "); +MODULE_AUTHOR("Conor Dooley "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-msc313.c b/drivers/rtc/rtc-msc313.c new file mode 100644 index 000000000..912a46c6f --- /dev/null +++ b/drivers/rtc/rtc-msc313.c @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Real time clocks driver for MStar/SigmaStar ARMv7 SoCs. + * Based on "Real Time Clock driver for msb252x." that was contained + * in various MStar kernels. + * + * (C) 2019 Daniel Palmer + * (C) 2021 Romain Perier + */ + +#include +#include +#include +#include +#include +#include + +/* Registers */ +#define REG_RTC_CTRL 0x00 +#define REG_RTC_FREQ_CW_L 0x04 +#define REG_RTC_FREQ_CW_H 0x08 +#define REG_RTC_LOAD_VAL_L 0x0C +#define REG_RTC_LOAD_VAL_H 0x10 +#define REG_RTC_MATCH_VAL_L 0x14 +#define REG_RTC_MATCH_VAL_H 0x18 +#define REG_RTC_STATUS_INT 0x1C +#define REG_RTC_CNT_VAL_L 0x20 +#define REG_RTC_CNT_VAL_H 0x24 + +/* Control bits for REG_RTC_CTRL */ +#define SOFT_RSTZ_BIT BIT(0) +#define CNT_EN_BIT BIT(1) +#define WRAP_EN_BIT BIT(2) +#define LOAD_EN_BIT BIT(3) +#define READ_EN_BIT BIT(4) +#define INT_MASK_BIT BIT(5) +#define INT_FORCE_BIT BIT(6) +#define INT_CLEAR_BIT BIT(7) + +/* Control bits for REG_RTC_STATUS_INT */ +#define RAW_INT_BIT BIT(0) +#define ALM_INT_BIT BIT(1) + +struct msc313_rtc { + struct rtc_device *rtc_dev; + void __iomem *rtc_base; +}; + +static int msc313_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct msc313_rtc *priv = dev_get_drvdata(dev); + unsigned long seconds; + + seconds = readw(priv->rtc_base + REG_RTC_MATCH_VAL_L) + | ((unsigned long)readw(priv->rtc_base + REG_RTC_MATCH_VAL_H) << 16); + + rtc_time64_to_tm(seconds, &alarm->time); + + if (!(readw(priv->rtc_base + REG_RTC_CTRL) & INT_MASK_BIT)) + alarm->enabled = 1; + + return 0; +} + +static int msc313_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct msc313_rtc *priv = dev_get_drvdata(dev); + u16 reg; + + reg = readw(priv->rtc_base + REG_RTC_CTRL); + if (enabled) + reg &= ~INT_MASK_BIT; + else + reg |= INT_MASK_BIT; + writew(reg, priv->rtc_base + REG_RTC_CTRL); + return 0; +} + +static int msc313_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct msc313_rtc *priv = dev_get_drvdata(dev); + unsigned long seconds; + + seconds = rtc_tm_to_time64(&alarm->time); + writew((seconds & 0xFFFF), priv->rtc_base + REG_RTC_MATCH_VAL_L); + writew((seconds >> 16) & 0xFFFF, priv->rtc_base + REG_RTC_MATCH_VAL_H); + + msc313_rtc_alarm_irq_enable(dev, alarm->enabled); + + return 0; +} + +static bool msc313_rtc_get_enabled(struct msc313_rtc *priv) +{ + return readw(priv->rtc_base + REG_RTC_CTRL) & CNT_EN_BIT; +} + +static void msc313_rtc_set_enabled(struct msc313_rtc *priv) +{ + u16 reg; + + reg = readw(priv->rtc_base + REG_RTC_CTRL); + reg |= CNT_EN_BIT; + writew(reg, priv->rtc_base + REG_RTC_CTRL); +} + +static int msc313_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct msc313_rtc *priv = dev_get_drvdata(dev); + u32 seconds; + u16 reg; + + if (!msc313_rtc_get_enabled(priv)) + return -EINVAL; + + reg = readw(priv->rtc_base + REG_RTC_CTRL); + writew(reg | READ_EN_BIT, priv->rtc_base + REG_RTC_CTRL); + + /* Wait for HW latch done */ + while (readw(priv->rtc_base + REG_RTC_CTRL) & READ_EN_BIT) + udelay(1); + + seconds = readw(priv->rtc_base + REG_RTC_CNT_VAL_L) + | ((unsigned long)readw(priv->rtc_base + REG_RTC_CNT_VAL_H) << 16); + + rtc_time64_to_tm(seconds, tm); + + return 0; +} + +static int msc313_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct msc313_rtc *priv = dev_get_drvdata(dev); + unsigned long seconds; + u16 reg; + + seconds = rtc_tm_to_time64(tm); + writew(seconds & 0xFFFF, priv->rtc_base + REG_RTC_LOAD_VAL_L); + writew((seconds >> 16) & 0xFFFF, priv->rtc_base + REG_RTC_LOAD_VAL_H); + + /* Enable load for loading value into internal RTC counter */ + reg = readw(priv->rtc_base + REG_RTC_CTRL); + writew(reg | LOAD_EN_BIT, priv->rtc_base + REG_RTC_CTRL); + + /* Wait for HW latch done */ + while (readw(priv->rtc_base + REG_RTC_CTRL) & LOAD_EN_BIT) + udelay(1); + msc313_rtc_set_enabled(priv); + return 0; +} + +static const struct rtc_class_ops msc313_rtc_ops = { + .read_time = msc313_rtc_read_time, + .set_time = msc313_rtc_set_time, + .read_alarm = msc313_rtc_read_alarm, + .set_alarm = msc313_rtc_set_alarm, + .alarm_irq_enable = msc313_rtc_alarm_irq_enable, +}; + +static irqreturn_t msc313_rtc_interrupt(s32 irq, void *dev_id) +{ + struct msc313_rtc *priv = dev_id; + u16 reg; + + reg = readw(priv->rtc_base + REG_RTC_STATUS_INT); + if (!(reg & ALM_INT_BIT)) + return IRQ_NONE; + + reg = readw(priv->rtc_base + REG_RTC_CTRL); + reg |= INT_CLEAR_BIT; + reg &= ~INT_FORCE_BIT; + writew(reg, priv->rtc_base + REG_RTC_CTRL); + + rtc_update_irq(priv->rtc_dev, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static int msc313_rtc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct msc313_rtc *priv; + unsigned long rate; + struct clk *clk; + int ret; + int irq; + + priv = devm_kzalloc(&pdev->dev, sizeof(struct msc313_rtc), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->rtc_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->rtc_base)) + return PTR_ERR(priv->rtc_base); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return -EINVAL; + + priv->rtc_dev = devm_rtc_allocate_device(dev); + if (IS_ERR(priv->rtc_dev)) + return PTR_ERR(priv->rtc_dev); + + priv->rtc_dev->ops = &msc313_rtc_ops; + priv->rtc_dev->range_max = U32_MAX; + + ret = devm_request_irq(dev, irq, msc313_rtc_interrupt, IRQF_SHARED, + dev_name(&pdev->dev), priv); + if (ret) { + dev_err(dev, "Could not request IRQ\n"); + return ret; + } + + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) { + dev_err(dev, "No input reference clock\n"); + return PTR_ERR(clk); + } + + rate = clk_get_rate(clk); + writew(rate & 0xFFFF, priv->rtc_base + REG_RTC_FREQ_CW_L); + writew((rate >> 16) & 0xFFFF, priv->rtc_base + REG_RTC_FREQ_CW_H); + + platform_set_drvdata(pdev, priv); + + return devm_rtc_register_device(priv->rtc_dev); +} + +static const struct of_device_id msc313_rtc_of_match_table[] = { + { .compatible = "mstar,msc313-rtc" }, + { } +}; +MODULE_DEVICE_TABLE(of, msc313_rtc_of_match_table); + +static struct platform_driver msc313_rtc_driver = { + .probe = msc313_rtc_probe, + .driver = { + .name = "msc313-rtc", + .of_match_table = msc313_rtc_of_match_table, + }, +}; + +module_platform_driver(msc313_rtc_driver); + +MODULE_AUTHOR("Daniel Palmer "); +MODULE_AUTHOR("Romain Perier "); +MODULE_DESCRIPTION("MStar RTC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-msm6242.c b/drivers/rtc/rtc-msm6242.c new file mode 100644 index 000000000..80e364baa --- /dev/null +++ b/drivers/rtc/rtc-msm6242.c @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Oki MSM6242 RTC Driver + * + * Copyright 2009 Geert Uytterhoeven + * + * Based on the A2000 TOD code in arch/m68k/amiga/config.c + * Copyright (C) 1993 Hamish Macdonald + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include + + +enum { + MSM6242_SECOND1 = 0x0, /* 1-second digit register */ + MSM6242_SECOND10 = 0x1, /* 10-second digit register */ + MSM6242_MINUTE1 = 0x2, /* 1-minute digit register */ + MSM6242_MINUTE10 = 0x3, /* 10-minute digit register */ + MSM6242_HOUR1 = 0x4, /* 1-hour digit register */ + MSM6242_HOUR10 = 0x5, /* PM/AM, 10-hour digit register */ + MSM6242_DAY1 = 0x6, /* 1-day digit register */ + MSM6242_DAY10 = 0x7, /* 10-day digit register */ + MSM6242_MONTH1 = 0x8, /* 1-month digit register */ + MSM6242_MONTH10 = 0x9, /* 10-month digit register */ + MSM6242_YEAR1 = 0xa, /* 1-year digit register */ + MSM6242_YEAR10 = 0xb, /* 10-year digit register */ + MSM6242_WEEK = 0xc, /* Week register */ + MSM6242_CD = 0xd, /* Control Register D */ + MSM6242_CE = 0xe, /* Control Register E */ + MSM6242_CF = 0xf, /* Control Register F */ +}; + +#define MSM6242_HOUR10_AM (0 << 2) +#define MSM6242_HOUR10_PM (1 << 2) +#define MSM6242_HOUR10_HR_MASK (3 << 0) + +#define MSM6242_WEEK_SUNDAY 0 +#define MSM6242_WEEK_MONDAY 1 +#define MSM6242_WEEK_TUESDAY 2 +#define MSM6242_WEEK_WEDNESDAY 3 +#define MSM6242_WEEK_THURSDAY 4 +#define MSM6242_WEEK_FRIDAY 5 +#define MSM6242_WEEK_SATURDAY 6 + +#define MSM6242_CD_30_S_ADJ (1 << 3) /* 30-second adjustment */ +#define MSM6242_CD_IRQ_FLAG (1 << 2) +#define MSM6242_CD_BUSY (1 << 1) +#define MSM6242_CD_HOLD (1 << 0) + +#define MSM6242_CE_T_MASK (3 << 2) +#define MSM6242_CE_T_64HZ (0 << 2) /* period 1/64 second */ +#define MSM6242_CE_T_1HZ (1 << 2) /* period 1 second */ +#define MSM6242_CE_T_1MINUTE (2 << 2) /* period 1 minute */ +#define MSM6242_CE_T_1HOUR (3 << 2) /* period 1 hour */ + +#define MSM6242_CE_ITRPT_STND (1 << 1) +#define MSM6242_CE_MASK (1 << 0) /* STD.P output control */ + +#define MSM6242_CF_TEST (1 << 3) +#define MSM6242_CF_12H (0 << 2) +#define MSM6242_CF_24H (1 << 2) +#define MSM6242_CF_STOP (1 << 1) +#define MSM6242_CF_REST (1 << 0) /* reset */ + + +struct msm6242_priv { + u32 __iomem *regs; + struct rtc_device *rtc; +}; + +static inline unsigned int msm6242_read(struct msm6242_priv *priv, + unsigned int reg) +{ + return __raw_readl(&priv->regs[reg]) & 0xf; +} + +static inline void msm6242_write(struct msm6242_priv *priv, unsigned int val, + unsigned int reg) +{ + __raw_writel(val, &priv->regs[reg]); +} + +static void msm6242_lock(struct msm6242_priv *priv) +{ + int cnt = 5; + + msm6242_write(priv, MSM6242_CD_HOLD|MSM6242_CD_IRQ_FLAG, MSM6242_CD); + + while ((msm6242_read(priv, MSM6242_CD) & MSM6242_CD_BUSY) && cnt) { + msm6242_write(priv, MSM6242_CD_IRQ_FLAG, MSM6242_CD); + udelay(70); + msm6242_write(priv, MSM6242_CD_HOLD|MSM6242_CD_IRQ_FLAG, MSM6242_CD); + cnt--; + } + + if (!cnt) + pr_warn("timed out waiting for RTC (0x%x)\n", + msm6242_read(priv, MSM6242_CD)); +} + +static void msm6242_unlock(struct msm6242_priv *priv) +{ + msm6242_write(priv, MSM6242_CD_IRQ_FLAG, MSM6242_CD); +} + +static int msm6242_read_time(struct device *dev, struct rtc_time *tm) +{ + struct msm6242_priv *priv = dev_get_drvdata(dev); + + msm6242_lock(priv); + + tm->tm_sec = msm6242_read(priv, MSM6242_SECOND10) * 10 + + msm6242_read(priv, MSM6242_SECOND1); + tm->tm_min = msm6242_read(priv, MSM6242_MINUTE10) * 10 + + msm6242_read(priv, MSM6242_MINUTE1); + tm->tm_hour = (msm6242_read(priv, MSM6242_HOUR10) & + MSM6242_HOUR10_HR_MASK) * 10 + + msm6242_read(priv, MSM6242_HOUR1); + tm->tm_mday = msm6242_read(priv, MSM6242_DAY10) * 10 + + msm6242_read(priv, MSM6242_DAY1); + tm->tm_wday = msm6242_read(priv, MSM6242_WEEK); + tm->tm_mon = msm6242_read(priv, MSM6242_MONTH10) * 10 + + msm6242_read(priv, MSM6242_MONTH1) - 1; + tm->tm_year = msm6242_read(priv, MSM6242_YEAR10) * 10 + + msm6242_read(priv, MSM6242_YEAR1); + if (tm->tm_year <= 69) + tm->tm_year += 100; + + if (!(msm6242_read(priv, MSM6242_CF) & MSM6242_CF_24H)) { + unsigned int pm = msm6242_read(priv, MSM6242_HOUR10) & + MSM6242_HOUR10_PM; + if (!pm && tm->tm_hour == 12) + tm->tm_hour = 0; + else if (pm && tm->tm_hour != 12) + tm->tm_hour += 12; + } + + msm6242_unlock(priv); + + return 0; +} + +static int msm6242_set_time(struct device *dev, struct rtc_time *tm) +{ + struct msm6242_priv *priv = dev_get_drvdata(dev); + + msm6242_lock(priv); + + msm6242_write(priv, tm->tm_sec / 10, MSM6242_SECOND10); + msm6242_write(priv, tm->tm_sec % 10, MSM6242_SECOND1); + msm6242_write(priv, tm->tm_min / 10, MSM6242_MINUTE10); + msm6242_write(priv, tm->tm_min % 10, MSM6242_MINUTE1); + if (msm6242_read(priv, MSM6242_CF) & MSM6242_CF_24H) + msm6242_write(priv, tm->tm_hour / 10, MSM6242_HOUR10); + else if (tm->tm_hour >= 12) + msm6242_write(priv, MSM6242_HOUR10_PM + (tm->tm_hour - 12) / 10, + MSM6242_HOUR10); + else + msm6242_write(priv, tm->tm_hour / 10, MSM6242_HOUR10); + msm6242_write(priv, tm->tm_hour % 10, MSM6242_HOUR1); + msm6242_write(priv, tm->tm_mday / 10, MSM6242_DAY10); + msm6242_write(priv, tm->tm_mday % 10, MSM6242_DAY1); + if (tm->tm_wday != -1) + msm6242_write(priv, tm->tm_wday, MSM6242_WEEK); + msm6242_write(priv, (tm->tm_mon + 1) / 10, MSM6242_MONTH10); + msm6242_write(priv, (tm->tm_mon + 1) % 10, MSM6242_MONTH1); + if (tm->tm_year >= 100) + tm->tm_year -= 100; + msm6242_write(priv, tm->tm_year / 10, MSM6242_YEAR10); + msm6242_write(priv, tm->tm_year % 10, MSM6242_YEAR1); + + msm6242_unlock(priv); + return 0; +} + +static const struct rtc_class_ops msm6242_rtc_ops = { + .read_time = msm6242_read_time, + .set_time = msm6242_set_time, +}; + +static int __init msm6242_rtc_probe(struct platform_device *pdev) +{ + struct resource *res; + struct msm6242_priv *priv; + struct rtc_device *rtc; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!priv->regs) + return -ENOMEM; + platform_set_drvdata(pdev, priv); + + rtc = devm_rtc_device_register(&pdev->dev, "rtc-msm6242", + &msm6242_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + priv->rtc = rtc; + return 0; +} + +static struct platform_driver msm6242_rtc_driver = { + .driver = { + .name = "rtc-msm6242", + }, +}; + +module_platform_driver_probe(msm6242_rtc_driver, msm6242_rtc_probe); + +MODULE_AUTHOR("Geert Uytterhoeven "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Oki MSM6242 RTC driver"); +MODULE_ALIAS("platform:rtc-msm6242"); diff --git a/drivers/rtc/rtc-mt2712.c b/drivers/rtc/rtc-mt2712.c new file mode 100644 index 000000000..cd92a9788 --- /dev/null +++ b/drivers/rtc/rtc-mt2712.c @@ -0,0 +1,413 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 MediaTek Inc. + * Author: Ran Bi + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MT2712_BBPU 0x0000 +#define MT2712_BBPU_CLRPKY BIT(4) +#define MT2712_BBPU_RELOAD BIT(5) +#define MT2712_BBPU_CBUSY BIT(6) +#define MT2712_BBPU_KEY (0x43 << 8) + +#define MT2712_IRQ_STA 0x0004 +#define MT2712_IRQ_STA_AL BIT(0) +#define MT2712_IRQ_STA_TC BIT(1) + +#define MT2712_IRQ_EN 0x0008 +#define MT2712_IRQ_EN_AL BIT(0) +#define MT2712_IRQ_EN_TC BIT(1) +#define MT2712_IRQ_EN_ONESHOT BIT(2) + +#define MT2712_CII_EN 0x000c + +#define MT2712_AL_MASK 0x0010 +#define MT2712_AL_MASK_DOW BIT(4) + +#define MT2712_TC_SEC 0x0014 +#define MT2712_TC_MIN 0x0018 +#define MT2712_TC_HOU 0x001c +#define MT2712_TC_DOM 0x0020 +#define MT2712_TC_DOW 0x0024 +#define MT2712_TC_MTH 0x0028 +#define MT2712_TC_YEA 0x002c + +#define MT2712_AL_SEC 0x0030 +#define MT2712_AL_MIN 0x0034 +#define MT2712_AL_HOU 0x0038 +#define MT2712_AL_DOM 0x003c +#define MT2712_AL_DOW 0x0040 +#define MT2712_AL_MTH 0x0044 +#define MT2712_AL_YEA 0x0048 + +#define MT2712_SEC_MASK 0x003f +#define MT2712_MIN_MASK 0x003f +#define MT2712_HOU_MASK 0x001f +#define MT2712_DOM_MASK 0x001f +#define MT2712_DOW_MASK 0x0007 +#define MT2712_MTH_MASK 0x000f +#define MT2712_YEA_MASK 0x007f + +#define MT2712_POWERKEY1 0x004c +#define MT2712_POWERKEY2 0x0050 +#define MT2712_POWERKEY1_KEY 0xa357 +#define MT2712_POWERKEY2_KEY 0x67d2 + +#define MT2712_CON0 0x005c +#define MT2712_CON1 0x0060 + +#define MT2712_PROT 0x0070 +#define MT2712_PROT_UNLOCK1 0x9136 +#define MT2712_PROT_UNLOCK2 0x586a + +#define MT2712_WRTGR 0x0078 + +#define MT2712_RTC_TIMESTAMP_END_2127 4985971199LL + +struct mt2712_rtc { + struct rtc_device *rtc; + void __iomem *base; + int irq; + u8 irq_wake_enabled; + u8 powerlost; +}; + +static inline u32 mt2712_readl(struct mt2712_rtc *mt2712_rtc, u32 reg) +{ + return readl(mt2712_rtc->base + reg); +} + +static inline void mt2712_writel(struct mt2712_rtc *mt2712_rtc, + u32 reg, u32 val) +{ + writel(val, mt2712_rtc->base + reg); +} + +static void mt2712_rtc_write_trigger(struct mt2712_rtc *mt2712_rtc) +{ + unsigned long timeout = jiffies + HZ / 10; + + mt2712_writel(mt2712_rtc, MT2712_WRTGR, 1); + while (1) { + if (!(mt2712_readl(mt2712_rtc, MT2712_BBPU) + & MT2712_BBPU_CBUSY)) + break; + + if (time_after(jiffies, timeout)) { + dev_err(&mt2712_rtc->rtc->dev, + "%s time out!\n", __func__); + break; + } + cpu_relax(); + } +} + +static void mt2712_rtc_writeif_unlock(struct mt2712_rtc *mt2712_rtc) +{ + mt2712_writel(mt2712_rtc, MT2712_PROT, MT2712_PROT_UNLOCK1); + mt2712_rtc_write_trigger(mt2712_rtc); + mt2712_writel(mt2712_rtc, MT2712_PROT, MT2712_PROT_UNLOCK2); + mt2712_rtc_write_trigger(mt2712_rtc); +} + +static irqreturn_t rtc_irq_handler_thread(int irq, void *data) +{ + struct mt2712_rtc *mt2712_rtc = data; + u16 irqsta; + + /* Clear interrupt */ + irqsta = mt2712_readl(mt2712_rtc, MT2712_IRQ_STA); + if (irqsta & MT2712_IRQ_STA_AL) { + rtc_update_irq(mt2712_rtc->rtc, 1, RTC_IRQF | RTC_AF); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static void __mt2712_rtc_read_time(struct mt2712_rtc *mt2712_rtc, + struct rtc_time *tm, int *sec) +{ + tm->tm_sec = mt2712_readl(mt2712_rtc, MT2712_TC_SEC) + & MT2712_SEC_MASK; + tm->tm_min = mt2712_readl(mt2712_rtc, MT2712_TC_MIN) + & MT2712_MIN_MASK; + tm->tm_hour = mt2712_readl(mt2712_rtc, MT2712_TC_HOU) + & MT2712_HOU_MASK; + tm->tm_mday = mt2712_readl(mt2712_rtc, MT2712_TC_DOM) + & MT2712_DOM_MASK; + tm->tm_mon = (mt2712_readl(mt2712_rtc, MT2712_TC_MTH) - 1) + & MT2712_MTH_MASK; + tm->tm_year = (mt2712_readl(mt2712_rtc, MT2712_TC_YEA) + 100) + & MT2712_YEA_MASK; + + *sec = mt2712_readl(mt2712_rtc, MT2712_TC_SEC) & MT2712_SEC_MASK; +} + +static int mt2712_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct mt2712_rtc *mt2712_rtc = dev_get_drvdata(dev); + int sec; + + if (mt2712_rtc->powerlost) + return -EINVAL; + + do { + __mt2712_rtc_read_time(mt2712_rtc, tm, &sec); + } while (sec < tm->tm_sec); /* SEC has carried */ + + return 0; +} + +static int mt2712_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct mt2712_rtc *mt2712_rtc = dev_get_drvdata(dev); + + mt2712_writel(mt2712_rtc, MT2712_TC_SEC, tm->tm_sec & MT2712_SEC_MASK); + mt2712_writel(mt2712_rtc, MT2712_TC_MIN, tm->tm_min & MT2712_MIN_MASK); + mt2712_writel(mt2712_rtc, MT2712_TC_HOU, tm->tm_hour & MT2712_HOU_MASK); + mt2712_writel(mt2712_rtc, MT2712_TC_DOM, tm->tm_mday & MT2712_DOM_MASK); + mt2712_writel(mt2712_rtc, MT2712_TC_MTH, + (tm->tm_mon + 1) & MT2712_MTH_MASK); + mt2712_writel(mt2712_rtc, MT2712_TC_YEA, + (tm->tm_year - 100) & MT2712_YEA_MASK); + + mt2712_rtc_write_trigger(mt2712_rtc); + + if (mt2712_rtc->powerlost) + mt2712_rtc->powerlost = false; + + return 0; +} + +static int mt2712_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct mt2712_rtc *mt2712_rtc = dev_get_drvdata(dev); + struct rtc_time *tm = &alm->time; + u16 irqen; + + irqen = mt2712_readl(mt2712_rtc, MT2712_IRQ_EN); + alm->enabled = !!(irqen & MT2712_IRQ_EN_AL); + + tm->tm_sec = mt2712_readl(mt2712_rtc, MT2712_AL_SEC) & MT2712_SEC_MASK; + tm->tm_min = mt2712_readl(mt2712_rtc, MT2712_AL_MIN) & MT2712_MIN_MASK; + tm->tm_hour = mt2712_readl(mt2712_rtc, MT2712_AL_HOU) & MT2712_HOU_MASK; + tm->tm_mday = mt2712_readl(mt2712_rtc, MT2712_AL_DOM) & MT2712_DOM_MASK; + tm->tm_mon = (mt2712_readl(mt2712_rtc, MT2712_AL_MTH) - 1) + & MT2712_MTH_MASK; + tm->tm_year = (mt2712_readl(mt2712_rtc, MT2712_AL_YEA) + 100) + & MT2712_YEA_MASK; + + return 0; +} + +static int mt2712_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct mt2712_rtc *mt2712_rtc = dev_get_drvdata(dev); + u16 irqen; + + irqen = mt2712_readl(mt2712_rtc, MT2712_IRQ_EN); + if (enabled) + irqen |= MT2712_IRQ_EN_AL; + else + irqen &= ~MT2712_IRQ_EN_AL; + mt2712_writel(mt2712_rtc, MT2712_IRQ_EN, irqen); + mt2712_rtc_write_trigger(mt2712_rtc); + + return 0; +} + +static int mt2712_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct mt2712_rtc *mt2712_rtc = dev_get_drvdata(dev); + struct rtc_time *tm = &alm->time; + + dev_dbg(&mt2712_rtc->rtc->dev, "set al time: %ptR, alm en: %d\n", + tm, alm->enabled); + + mt2712_writel(mt2712_rtc, MT2712_AL_SEC, + (mt2712_readl(mt2712_rtc, MT2712_AL_SEC) + & ~(MT2712_SEC_MASK)) | (tm->tm_sec & MT2712_SEC_MASK)); + mt2712_writel(mt2712_rtc, MT2712_AL_MIN, + (mt2712_readl(mt2712_rtc, MT2712_AL_MIN) + & ~(MT2712_MIN_MASK)) | (tm->tm_min & MT2712_MIN_MASK)); + mt2712_writel(mt2712_rtc, MT2712_AL_HOU, + (mt2712_readl(mt2712_rtc, MT2712_AL_HOU) + & ~(MT2712_HOU_MASK)) | (tm->tm_hour & MT2712_HOU_MASK)); + mt2712_writel(mt2712_rtc, MT2712_AL_DOM, + (mt2712_readl(mt2712_rtc, MT2712_AL_DOM) + & ~(MT2712_DOM_MASK)) | (tm->tm_mday & MT2712_DOM_MASK)); + mt2712_writel(mt2712_rtc, MT2712_AL_MTH, + (mt2712_readl(mt2712_rtc, MT2712_AL_MTH) + & ~(MT2712_MTH_MASK)) + | ((tm->tm_mon + 1) & MT2712_MTH_MASK)); + mt2712_writel(mt2712_rtc, MT2712_AL_YEA, + (mt2712_readl(mt2712_rtc, MT2712_AL_YEA) + & ~(MT2712_YEA_MASK)) + | ((tm->tm_year - 100) & MT2712_YEA_MASK)); + + /* mask day of week */ + mt2712_writel(mt2712_rtc, MT2712_AL_MASK, MT2712_AL_MASK_DOW); + mt2712_rtc_write_trigger(mt2712_rtc); + + mt2712_rtc_alarm_irq_enable(dev, alm->enabled); + + return 0; +} + +/* Init RTC register */ +static void mt2712_rtc_hw_init(struct mt2712_rtc *mt2712_rtc) +{ + u32 p1, p2; + + mt2712_writel(mt2712_rtc, MT2712_BBPU, + MT2712_BBPU_KEY | MT2712_BBPU_RELOAD); + + mt2712_writel(mt2712_rtc, MT2712_CII_EN, 0); + mt2712_writel(mt2712_rtc, MT2712_AL_MASK, 0); + /* necessary before set MT2712_POWERKEY */ + mt2712_writel(mt2712_rtc, MT2712_CON0, 0x4848); + mt2712_writel(mt2712_rtc, MT2712_CON1, 0x0048); + + mt2712_rtc_write_trigger(mt2712_rtc); + + p1 = mt2712_readl(mt2712_rtc, MT2712_POWERKEY1); + p2 = mt2712_readl(mt2712_rtc, MT2712_POWERKEY2); + if (p1 != MT2712_POWERKEY1_KEY || p2 != MT2712_POWERKEY2_KEY) { + mt2712_rtc->powerlost = true; + dev_dbg(&mt2712_rtc->rtc->dev, + "powerkey not set (lost power)\n"); + } else { + mt2712_rtc->powerlost = false; + } + + /* RTC need POWERKEY1/2 match, then goto normal work mode */ + mt2712_writel(mt2712_rtc, MT2712_POWERKEY1, MT2712_POWERKEY1_KEY); + mt2712_writel(mt2712_rtc, MT2712_POWERKEY2, MT2712_POWERKEY2_KEY); + mt2712_rtc_write_trigger(mt2712_rtc); + + mt2712_rtc_writeif_unlock(mt2712_rtc); +} + +static const struct rtc_class_ops mt2712_rtc_ops = { + .read_time = mt2712_rtc_read_time, + .set_time = mt2712_rtc_set_time, + .read_alarm = mt2712_rtc_read_alarm, + .set_alarm = mt2712_rtc_set_alarm, + .alarm_irq_enable = mt2712_rtc_alarm_irq_enable, +}; + +static int mt2712_rtc_probe(struct platform_device *pdev) +{ + struct mt2712_rtc *mt2712_rtc; + int ret; + + mt2712_rtc = devm_kzalloc(&pdev->dev, + sizeof(struct mt2712_rtc), GFP_KERNEL); + if (!mt2712_rtc) + return -ENOMEM; + + mt2712_rtc->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mt2712_rtc->base)) + return PTR_ERR(mt2712_rtc->base); + + /* rtc hw init */ + mt2712_rtc_hw_init(mt2712_rtc); + + mt2712_rtc->irq = platform_get_irq(pdev, 0); + if (mt2712_rtc->irq < 0) + return mt2712_rtc->irq; + + platform_set_drvdata(pdev, mt2712_rtc); + + mt2712_rtc->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(mt2712_rtc->rtc)) + return PTR_ERR(mt2712_rtc->rtc); + + ret = devm_request_threaded_irq(&pdev->dev, mt2712_rtc->irq, NULL, + rtc_irq_handler_thread, + IRQF_ONESHOT | IRQF_TRIGGER_LOW, + dev_name(&mt2712_rtc->rtc->dev), + mt2712_rtc); + if (ret) { + dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", + mt2712_rtc->irq, ret); + return ret; + } + + device_init_wakeup(&pdev->dev, true); + + mt2712_rtc->rtc->ops = &mt2712_rtc_ops; + mt2712_rtc->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + mt2712_rtc->rtc->range_max = MT2712_RTC_TIMESTAMP_END_2127; + + return devm_rtc_register_device(mt2712_rtc->rtc); +} + +#ifdef CONFIG_PM_SLEEP +static int mt2712_rtc_suspend(struct device *dev) +{ + int wake_status = 0; + struct mt2712_rtc *mt2712_rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) { + wake_status = enable_irq_wake(mt2712_rtc->irq); + if (!wake_status) + mt2712_rtc->irq_wake_enabled = true; + } + + return 0; +} + +static int mt2712_rtc_resume(struct device *dev) +{ + int wake_status = 0; + struct mt2712_rtc *mt2712_rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev) && mt2712_rtc->irq_wake_enabled) { + wake_status = disable_irq_wake(mt2712_rtc->irq); + if (!wake_status) + mt2712_rtc->irq_wake_enabled = false; + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(mt2712_pm_ops, mt2712_rtc_suspend, + mt2712_rtc_resume); +#endif + +static const struct of_device_id mt2712_rtc_of_match[] = { + { .compatible = "mediatek,mt2712-rtc", }, + { }, +}; + +MODULE_DEVICE_TABLE(of, mt2712_rtc_of_match); + +static struct platform_driver mt2712_rtc_driver = { + .driver = { + .name = "mt2712-rtc", + .of_match_table = mt2712_rtc_of_match, +#ifdef CONFIG_PM_SLEEP + .pm = &mt2712_pm_ops, +#endif + }, + .probe = mt2712_rtc_probe, +}; + +module_platform_driver(mt2712_rtc_driver); + +MODULE_DESCRIPTION("MediaTek MT2712 SoC based RTC Driver"); +MODULE_AUTHOR("Ran Bi "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-mt6397.c b/drivers/rtc/rtc-mt6397.c new file mode 100644 index 000000000..3d857681f --- /dev/null +++ b/drivers/rtc/rtc-mt6397.c @@ -0,0 +1,354 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* +* Copyright (c) 2014-2015 MediaTek Inc. +* Author: Tianping.Fang +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int mtk_rtc_write_trigger(struct mt6397_rtc *rtc) +{ + int ret; + u32 data; + + ret = regmap_write(rtc->regmap, rtc->addr_base + rtc->data->wrtgr, 1); + if (ret < 0) + return ret; + + ret = regmap_read_poll_timeout(rtc->regmap, + rtc->addr_base + RTC_BBPU, data, + !(data & RTC_BBPU_CBUSY), + MTK_RTC_POLL_DELAY_US, + MTK_RTC_POLL_TIMEOUT); + if (ret < 0) + dev_err(rtc->rtc_dev->dev.parent, + "failed to write WRTGR: %d\n", ret); + + return ret; +} + +static irqreturn_t mtk_rtc_irq_handler_thread(int irq, void *data) +{ + struct mt6397_rtc *rtc = data; + u32 irqsta, irqen; + int ret; + + ret = regmap_read(rtc->regmap, rtc->addr_base + RTC_IRQ_STA, &irqsta); + if ((ret >= 0) && (irqsta & RTC_IRQ_STA_AL)) { + rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF); + irqen = irqsta & ~RTC_IRQ_EN_AL; + mutex_lock(&rtc->lock); + if (regmap_write(rtc->regmap, rtc->addr_base + RTC_IRQ_EN, + irqen) == 0) + mtk_rtc_write_trigger(rtc); + mutex_unlock(&rtc->lock); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int __mtk_rtc_read_time(struct mt6397_rtc *rtc, + struct rtc_time *tm, int *sec) +{ + int ret; + u16 data[RTC_OFFSET_COUNT]; + + mutex_lock(&rtc->lock); + ret = regmap_bulk_read(rtc->regmap, rtc->addr_base + RTC_TC_SEC, + data, RTC_OFFSET_COUNT); + if (ret < 0) + goto exit; + + tm->tm_sec = data[RTC_OFFSET_SEC]; + tm->tm_min = data[RTC_OFFSET_MIN]; + tm->tm_hour = data[RTC_OFFSET_HOUR]; + tm->tm_mday = data[RTC_OFFSET_DOM]; + tm->tm_wday = data[RTC_OFFSET_DOW]; + tm->tm_mon = data[RTC_OFFSET_MTH] & RTC_TC_MTH_MASK; + tm->tm_year = data[RTC_OFFSET_YEAR]; + + ret = regmap_read(rtc->regmap, rtc->addr_base + RTC_TC_SEC, sec); +exit: + mutex_unlock(&rtc->lock); + return ret; +} + +static int mtk_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct mt6397_rtc *rtc = dev_get_drvdata(dev); + int sec, ret; + + do { + ret = __mtk_rtc_read_time(rtc, tm, &sec); + if (ret < 0) + goto exit; + } while (sec < tm->tm_sec); + + /* HW register start mon/wday from one, but tm_mon/tm_wday start from zero. */ + tm->tm_mon--; + tm->tm_wday--; + +exit: + return ret; +} + +static int mtk_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct mt6397_rtc *rtc = dev_get_drvdata(dev); + int ret; + u16 data[RTC_OFFSET_COUNT]; + + tm->tm_mon++; + tm->tm_wday++; + + data[RTC_OFFSET_SEC] = tm->tm_sec; + data[RTC_OFFSET_MIN] = tm->tm_min; + data[RTC_OFFSET_HOUR] = tm->tm_hour; + data[RTC_OFFSET_DOM] = tm->tm_mday; + data[RTC_OFFSET_DOW] = tm->tm_wday; + data[RTC_OFFSET_MTH] = tm->tm_mon; + data[RTC_OFFSET_YEAR] = tm->tm_year; + + mutex_lock(&rtc->lock); + ret = regmap_bulk_write(rtc->regmap, rtc->addr_base + RTC_TC_SEC, + data, RTC_OFFSET_COUNT); + if (ret < 0) + goto exit; + + /* Time register write to hardware after call trigger function */ + ret = mtk_rtc_write_trigger(rtc); + +exit: + mutex_unlock(&rtc->lock); + return ret; +} + +static int mtk_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct rtc_time *tm = &alm->time; + struct mt6397_rtc *rtc = dev_get_drvdata(dev); + u32 irqen, pdn2; + int ret; + u16 data[RTC_OFFSET_COUNT]; + + mutex_lock(&rtc->lock); + ret = regmap_read(rtc->regmap, rtc->addr_base + RTC_IRQ_EN, &irqen); + if (ret < 0) + goto err_exit; + ret = regmap_read(rtc->regmap, rtc->addr_base + RTC_PDN2, &pdn2); + if (ret < 0) + goto err_exit; + + ret = regmap_bulk_read(rtc->regmap, rtc->addr_base + RTC_AL_SEC, + data, RTC_OFFSET_COUNT); + if (ret < 0) + goto err_exit; + + alm->enabled = !!(irqen & RTC_IRQ_EN_AL); + alm->pending = !!(pdn2 & RTC_PDN2_PWRON_ALARM); + mutex_unlock(&rtc->lock); + + tm->tm_sec = data[RTC_OFFSET_SEC] & RTC_AL_SEC_MASK; + tm->tm_min = data[RTC_OFFSET_MIN] & RTC_AL_MIN_MASK; + tm->tm_hour = data[RTC_OFFSET_HOUR] & RTC_AL_HOU_MASK; + tm->tm_mday = data[RTC_OFFSET_DOM] & RTC_AL_DOM_MASK; + tm->tm_mon = data[RTC_OFFSET_MTH] & RTC_AL_MTH_MASK; + tm->tm_year = data[RTC_OFFSET_YEAR] & RTC_AL_YEA_MASK; + + tm->tm_mon--; + + return 0; +err_exit: + mutex_unlock(&rtc->lock); + return ret; +} + +static int mtk_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct rtc_time *tm = &alm->time; + struct mt6397_rtc *rtc = dev_get_drvdata(dev); + int ret; + u16 data[RTC_OFFSET_COUNT]; + + tm->tm_mon++; + + mutex_lock(&rtc->lock); + ret = regmap_bulk_read(rtc->regmap, rtc->addr_base + RTC_AL_SEC, + data, RTC_OFFSET_COUNT); + if (ret < 0) + goto exit; + + data[RTC_OFFSET_SEC] = ((data[RTC_OFFSET_SEC] & ~(RTC_AL_SEC_MASK)) | + (tm->tm_sec & RTC_AL_SEC_MASK)); + data[RTC_OFFSET_MIN] = ((data[RTC_OFFSET_MIN] & ~(RTC_AL_MIN_MASK)) | + (tm->tm_min & RTC_AL_MIN_MASK)); + data[RTC_OFFSET_HOUR] = ((data[RTC_OFFSET_HOUR] & ~(RTC_AL_HOU_MASK)) | + (tm->tm_hour & RTC_AL_HOU_MASK)); + data[RTC_OFFSET_DOM] = ((data[RTC_OFFSET_DOM] & ~(RTC_AL_DOM_MASK)) | + (tm->tm_mday & RTC_AL_DOM_MASK)); + data[RTC_OFFSET_MTH] = ((data[RTC_OFFSET_MTH] & ~(RTC_AL_MTH_MASK)) | + (tm->tm_mon & RTC_AL_MTH_MASK)); + data[RTC_OFFSET_YEAR] = ((data[RTC_OFFSET_YEAR] & ~(RTC_AL_YEA_MASK)) | + (tm->tm_year & RTC_AL_YEA_MASK)); + + if (alm->enabled) { + ret = regmap_bulk_write(rtc->regmap, + rtc->addr_base + RTC_AL_SEC, + data, RTC_OFFSET_COUNT); + if (ret < 0) + goto exit; + ret = regmap_write(rtc->regmap, rtc->addr_base + RTC_AL_MASK, + RTC_AL_MASK_DOW); + if (ret < 0) + goto exit; + ret = regmap_update_bits(rtc->regmap, + rtc->addr_base + RTC_IRQ_EN, + RTC_IRQ_EN_ONESHOT_AL, + RTC_IRQ_EN_ONESHOT_AL); + if (ret < 0) + goto exit; + } else { + ret = regmap_update_bits(rtc->regmap, + rtc->addr_base + RTC_IRQ_EN, + RTC_IRQ_EN_ONESHOT_AL, 0); + if (ret < 0) + goto exit; + } + + /* All alarm time register write to hardware after calling + * mtk_rtc_write_trigger. This can avoid race condition if alarm + * occur happen during writing alarm time register. + */ + ret = mtk_rtc_write_trigger(rtc); +exit: + mutex_unlock(&rtc->lock); + return ret; +} + +static const struct rtc_class_ops mtk_rtc_ops = { + .read_time = mtk_rtc_read_time, + .set_time = mtk_rtc_set_time, + .read_alarm = mtk_rtc_read_alarm, + .set_alarm = mtk_rtc_set_alarm, +}; + +static int mtk_rtc_probe(struct platform_device *pdev) +{ + struct resource *res; + struct mt6397_chip *mt6397_chip = dev_get_drvdata(pdev->dev.parent); + struct mt6397_rtc *rtc; + int ret; + + rtc = devm_kzalloc(&pdev->dev, sizeof(struct mt6397_rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + rtc->addr_base = res->start; + + rtc->data = of_device_get_match_data(&pdev->dev); + + rtc->irq = platform_get_irq(pdev, 0); + if (rtc->irq < 0) + return rtc->irq; + + rtc->regmap = mt6397_chip->regmap; + mutex_init(&rtc->lock); + + platform_set_drvdata(pdev, rtc); + + rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc->rtc_dev)) + return PTR_ERR(rtc->rtc_dev); + + ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL, + mtk_rtc_irq_handler_thread, + IRQF_ONESHOT | IRQF_TRIGGER_HIGH, + "mt6397-rtc", rtc); + + if (ret) { + dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", + rtc->irq, ret); + return ret; + } + + device_init_wakeup(&pdev->dev, true); + + rtc->rtc_dev->ops = &mtk_rtc_ops; + rtc->rtc_dev->range_min = RTC_TIMESTAMP_BEGIN_1900; + rtc->rtc_dev->range_max = mktime64(2027, 12, 31, 23, 59, 59); + rtc->rtc_dev->start_secs = mktime64(1968, 1, 2, 0, 0, 0); + rtc->rtc_dev->set_start_time = true; + + return devm_rtc_register_device(rtc->rtc_dev); +} + +#ifdef CONFIG_PM_SLEEP +static int mt6397_rtc_suspend(struct device *dev) +{ + struct mt6397_rtc *rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(rtc->irq); + + return 0; +} + +static int mt6397_rtc_resume(struct device *dev) +{ + struct mt6397_rtc *rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(rtc->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(mt6397_pm_ops, mt6397_rtc_suspend, + mt6397_rtc_resume); + +static const struct mtk_rtc_data mt6358_rtc_data = { + .wrtgr = RTC_WRTGR_MT6358, +}; + +static const struct mtk_rtc_data mt6397_rtc_data = { + .wrtgr = RTC_WRTGR_MT6397, +}; + +static const struct of_device_id mt6397_rtc_of_match[] = { + { .compatible = "mediatek,mt6323-rtc", .data = &mt6397_rtc_data }, + { .compatible = "mediatek,mt6357-rtc", .data = &mt6358_rtc_data }, + { .compatible = "mediatek,mt6358-rtc", .data = &mt6358_rtc_data }, + { .compatible = "mediatek,mt6397-rtc", .data = &mt6397_rtc_data }, + { } +}; +MODULE_DEVICE_TABLE(of, mt6397_rtc_of_match); + +static struct platform_driver mtk_rtc_driver = { + .driver = { + .name = "mt6397-rtc", + .of_match_table = mt6397_rtc_of_match, + .pm = &mt6397_pm_ops, + }, + .probe = mtk_rtc_probe, +}; + +module_platform_driver(mtk_rtc_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Tianping Fang "); +MODULE_DESCRIPTION("RTC Driver for MediaTek MT6397 PMIC"); diff --git a/drivers/rtc/rtc-mt7622.c b/drivers/rtc/rtc-mt7622.c new file mode 100644 index 000000000..9e17dd09d --- /dev/null +++ b/drivers/rtc/rtc-mt7622.c @@ -0,0 +1,408 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for MediaTek SoC based RTC + * + * Copyright (C) 2017 Sean Wang + */ + +#include +#include +#include +#include +#include +#include + +#define MTK_RTC_DEV KBUILD_MODNAME + +#define MTK_RTC_PWRCHK1 0x4 +#define RTC_PWRCHK1_MAGIC 0xc6 + +#define MTK_RTC_PWRCHK2 0x8 +#define RTC_PWRCHK2_MAGIC 0x9a + +#define MTK_RTC_KEY 0xc +#define RTC_KEY_MAGIC 0x59 + +#define MTK_RTC_PROT1 0x10 +#define RTC_PROT1_MAGIC 0xa3 + +#define MTK_RTC_PROT2 0x14 +#define RTC_PROT2_MAGIC 0x57 + +#define MTK_RTC_PROT3 0x18 +#define RTC_PROT3_MAGIC 0x67 + +#define MTK_RTC_PROT4 0x1c +#define RTC_PROT4_MAGIC 0xd2 + +#define MTK_RTC_CTL 0x20 +#define RTC_RC_STOP BIT(0) + +#define MTK_RTC_DEBNCE 0x2c +#define RTC_DEBNCE_MASK GENMASK(2, 0) + +#define MTK_RTC_INT 0x30 +#define RTC_INT_AL_STA BIT(4) + +/* + * Ranges from 0x40 to 0x78 provide RTC time setup for year, month, + * day of month, day of week, hour, minute and second. + */ +#define MTK_RTC_TREG(_t, _f) (0x40 + (0x4 * (_f)) + ((_t) * 0x20)) + +#define MTK_RTC_AL_CTL 0x7c +#define RTC_AL_EN BIT(0) +#define RTC_AL_ALL GENMASK(7, 0) + +/* + * The offset is used in the translation for the year between in struct + * rtc_time and in hardware register MTK_RTC_TREG(x,MTK_YEA) + */ +#define MTK_RTC_TM_YR_OFFSET 100 + +/* + * The lowest value for the valid tm_year. RTC hardware would take incorrectly + * tm_year 100 as not a leap year and thus it is also required being excluded + * from the valid options. + */ +#define MTK_RTC_TM_YR_L (MTK_RTC_TM_YR_OFFSET + 1) + +/* + * The most year the RTC can hold is 99 and the next to 99 in year register + * would be wraparound to 0, for MT7622. + */ +#define MTK_RTC_HW_YR_LIMIT 99 + +/* The highest value for the valid tm_year */ +#define MTK_RTC_TM_YR_H (MTK_RTC_TM_YR_OFFSET + MTK_RTC_HW_YR_LIMIT) + +/* Simple macro helps to check whether the hardware supports the tm_year */ +#define MTK_RTC_TM_YR_VALID(_y) ((_y) >= MTK_RTC_TM_YR_L && \ + (_y) <= MTK_RTC_TM_YR_H) + +/* Types of the function the RTC provides are time counter and alarm. */ +enum { + MTK_TC, + MTK_AL, +}; + +/* Indexes are used for the pointer to relevant registers in MTK_RTC_TREG */ +enum { + MTK_YEA, + MTK_MON, + MTK_DOM, + MTK_DOW, + MTK_HOU, + MTK_MIN, + MTK_SEC +}; + +struct mtk_rtc { + struct rtc_device *rtc; + void __iomem *base; + int irq; + struct clk *clk; +}; + +static void mtk_w32(struct mtk_rtc *rtc, u32 reg, u32 val) +{ + writel_relaxed(val, rtc->base + reg); +} + +static u32 mtk_r32(struct mtk_rtc *rtc, u32 reg) +{ + return readl_relaxed(rtc->base + reg); +} + +static void mtk_rmw(struct mtk_rtc *rtc, u32 reg, u32 mask, u32 set) +{ + u32 val; + + val = mtk_r32(rtc, reg); + val &= ~mask; + val |= set; + mtk_w32(rtc, reg, val); +} + +static void mtk_set(struct mtk_rtc *rtc, u32 reg, u32 val) +{ + mtk_rmw(rtc, reg, 0, val); +} + +static void mtk_clr(struct mtk_rtc *rtc, u32 reg, u32 val) +{ + mtk_rmw(rtc, reg, val, 0); +} + +static void mtk_rtc_hw_init(struct mtk_rtc *hw) +{ + /* The setup of the init sequence is for allowing RTC got to work */ + mtk_w32(hw, MTK_RTC_PWRCHK1, RTC_PWRCHK1_MAGIC); + mtk_w32(hw, MTK_RTC_PWRCHK2, RTC_PWRCHK2_MAGIC); + mtk_w32(hw, MTK_RTC_KEY, RTC_KEY_MAGIC); + mtk_w32(hw, MTK_RTC_PROT1, RTC_PROT1_MAGIC); + mtk_w32(hw, MTK_RTC_PROT2, RTC_PROT2_MAGIC); + mtk_w32(hw, MTK_RTC_PROT3, RTC_PROT3_MAGIC); + mtk_w32(hw, MTK_RTC_PROT4, RTC_PROT4_MAGIC); + mtk_rmw(hw, MTK_RTC_DEBNCE, RTC_DEBNCE_MASK, 0); + mtk_clr(hw, MTK_RTC_CTL, RTC_RC_STOP); +} + +static void mtk_rtc_get_alarm_or_time(struct mtk_rtc *hw, struct rtc_time *tm, + int time_alarm) +{ + u32 year, mon, mday, wday, hour, min, sec; + + /* + * Read again until the field of the second is not changed which + * ensures all fields in the consistent state. Note that MTK_SEC must + * be read first. In this way, it guarantees the others remain not + * changed when the results for two MTK_SEC consecutive reads are same. + */ + do { + sec = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_SEC)); + min = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_MIN)); + hour = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_HOU)); + wday = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_DOW)); + mday = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_DOM)); + mon = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_MON)); + year = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_YEA)); + } while (sec != mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_SEC))); + + tm->tm_sec = sec; + tm->tm_min = min; + tm->tm_hour = hour; + tm->tm_wday = wday; + tm->tm_mday = mday; + tm->tm_mon = mon - 1; + + /* Rebase to the absolute year which userspace queries */ + tm->tm_year = year + MTK_RTC_TM_YR_OFFSET; +} + +static void mtk_rtc_set_alarm_or_time(struct mtk_rtc *hw, struct rtc_time *tm, + int time_alarm) +{ + u32 year; + + /* Rebase to the relative year which RTC hardware requires */ + year = tm->tm_year - MTK_RTC_TM_YR_OFFSET; + + mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_YEA), year); + mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_MON), tm->tm_mon + 1); + mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_DOW), tm->tm_wday); + mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_DOM), tm->tm_mday); + mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_HOU), tm->tm_hour); + mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_MIN), tm->tm_min); + mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_SEC), tm->tm_sec); +} + +static irqreturn_t mtk_rtc_alarmirq(int irq, void *id) +{ + struct mtk_rtc *hw = (struct mtk_rtc *)id; + u32 irq_sta; + + irq_sta = mtk_r32(hw, MTK_RTC_INT); + if (irq_sta & RTC_INT_AL_STA) { + /* Stop alarm also implicitly disables the alarm interrupt */ + mtk_w32(hw, MTK_RTC_AL_CTL, 0); + rtc_update_irq(hw->rtc, 1, RTC_IRQF | RTC_AF); + + /* Ack alarm interrupt status */ + mtk_w32(hw, MTK_RTC_INT, RTC_INT_AL_STA); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int mtk_rtc_gettime(struct device *dev, struct rtc_time *tm) +{ + struct mtk_rtc *hw = dev_get_drvdata(dev); + + mtk_rtc_get_alarm_or_time(hw, tm, MTK_TC); + + return 0; +} + +static int mtk_rtc_settime(struct device *dev, struct rtc_time *tm) +{ + struct mtk_rtc *hw = dev_get_drvdata(dev); + + if (!MTK_RTC_TM_YR_VALID(tm->tm_year)) + return -EINVAL; + + /* Stop time counter before setting a new one*/ + mtk_set(hw, MTK_RTC_CTL, RTC_RC_STOP); + + mtk_rtc_set_alarm_or_time(hw, tm, MTK_TC); + + /* Restart the time counter */ + mtk_clr(hw, MTK_RTC_CTL, RTC_RC_STOP); + + return 0; +} + +static int mtk_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct mtk_rtc *hw = dev_get_drvdata(dev); + struct rtc_time *alrm_tm = &wkalrm->time; + + mtk_rtc_get_alarm_or_time(hw, alrm_tm, MTK_AL); + + wkalrm->enabled = !!(mtk_r32(hw, MTK_RTC_AL_CTL) & RTC_AL_EN); + wkalrm->pending = !!(mtk_r32(hw, MTK_RTC_INT) & RTC_INT_AL_STA); + + return 0; +} + +static int mtk_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct mtk_rtc *hw = dev_get_drvdata(dev); + struct rtc_time *alrm_tm = &wkalrm->time; + + if (!MTK_RTC_TM_YR_VALID(alrm_tm->tm_year)) + return -EINVAL; + + /* + * Stop the alarm also implicitly including disables interrupt before + * setting a new one. + */ + mtk_clr(hw, MTK_RTC_AL_CTL, RTC_AL_EN); + + /* + * Avoid contention between mtk_rtc_setalarm and IRQ handler so that + * disabling the interrupt and awaiting for pending IRQ handler to + * complete. + */ + synchronize_irq(hw->irq); + + mtk_rtc_set_alarm_or_time(hw, alrm_tm, MTK_AL); + + /* Restart the alarm with the new setup */ + mtk_w32(hw, MTK_RTC_AL_CTL, RTC_AL_ALL); + + return 0; +} + +static const struct rtc_class_ops mtk_rtc_ops = { + .read_time = mtk_rtc_gettime, + .set_time = mtk_rtc_settime, + .read_alarm = mtk_rtc_getalarm, + .set_alarm = mtk_rtc_setalarm, +}; + +static const struct of_device_id mtk_rtc_match[] = { + { .compatible = "mediatek,mt7622-rtc" }, + { .compatible = "mediatek,soc-rtc" }, + {}, +}; +MODULE_DEVICE_TABLE(of, mtk_rtc_match); + +static int mtk_rtc_probe(struct platform_device *pdev) +{ + struct mtk_rtc *hw; + int ret; + + hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL); + if (!hw) + return -ENOMEM; + + platform_set_drvdata(pdev, hw); + + hw->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(hw->base)) + return PTR_ERR(hw->base); + + hw->clk = devm_clk_get(&pdev->dev, "rtc"); + if (IS_ERR(hw->clk)) { + dev_err(&pdev->dev, "No clock\n"); + return PTR_ERR(hw->clk); + } + + ret = clk_prepare_enable(hw->clk); + if (ret) + return ret; + + hw->irq = platform_get_irq(pdev, 0); + if (hw->irq < 0) { + ret = hw->irq; + goto err; + } + + ret = devm_request_irq(&pdev->dev, hw->irq, mtk_rtc_alarmirq, + 0, dev_name(&pdev->dev), hw); + if (ret) { + dev_err(&pdev->dev, "Can't request IRQ\n"); + goto err; + } + + mtk_rtc_hw_init(hw); + + device_init_wakeup(&pdev->dev, true); + + hw->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + &mtk_rtc_ops, THIS_MODULE); + if (IS_ERR(hw->rtc)) { + ret = PTR_ERR(hw->rtc); + dev_err(&pdev->dev, "Unable to register device\n"); + goto err; + } + + return 0; +err: + clk_disable_unprepare(hw->clk); + + return ret; +} + +static void mtk_rtc_remove(struct platform_device *pdev) +{ + struct mtk_rtc *hw = platform_get_drvdata(pdev); + + clk_disable_unprepare(hw->clk); +} + +#ifdef CONFIG_PM_SLEEP +static int mtk_rtc_suspend(struct device *dev) +{ + struct mtk_rtc *hw = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(hw->irq); + + return 0; +} + +static int mtk_rtc_resume(struct device *dev) +{ + struct mtk_rtc *hw = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(hw->irq); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(mtk_rtc_pm_ops, mtk_rtc_suspend, mtk_rtc_resume); + +#define MTK_RTC_PM_OPS (&mtk_rtc_pm_ops) +#else /* CONFIG_PM */ +#define MTK_RTC_PM_OPS NULL +#endif /* CONFIG_PM */ + +static struct platform_driver mtk_rtc_driver = { + .probe = mtk_rtc_probe, + .remove = mtk_rtc_remove, + .driver = { + .name = MTK_RTC_DEV, + .of_match_table = mtk_rtc_match, + .pm = MTK_RTC_PM_OPS, + }, +}; + +module_platform_driver(mtk_rtc_driver); + +MODULE_DESCRIPTION("MediaTek SoC based RTC Driver"); +MODULE_AUTHOR("Sean Wang "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-mv.c b/drivers/rtc/rtc-mv.c new file mode 100644 index 000000000..db64c459e --- /dev/null +++ b/drivers/rtc/rtc-mv.c @@ -0,0 +1,321 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the RTC in Marvell SoCs. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define RTC_TIME_REG_OFFS 0 +#define RTC_SECONDS_OFFS 0 +#define RTC_MINUTES_OFFS 8 +#define RTC_HOURS_OFFS 16 +#define RTC_WDAY_OFFS 24 +#define RTC_HOURS_12H_MODE BIT(22) /* 12 hour mode */ + +#define RTC_DATE_REG_OFFS 4 +#define RTC_MDAY_OFFS 0 +#define RTC_MONTH_OFFS 8 +#define RTC_YEAR_OFFS 16 + +#define RTC_ALARM_TIME_REG_OFFS 8 +#define RTC_ALARM_DATE_REG_OFFS 0xc +#define RTC_ALARM_VALID BIT(7) + +#define RTC_ALARM_INTERRUPT_MASK_REG_OFFS 0x10 +#define RTC_ALARM_INTERRUPT_CASUE_REG_OFFS 0x14 + +struct rtc_plat_data { + struct rtc_device *rtc; + void __iomem *ioaddr; + int irq; + struct clk *clk; +}; + +static int mv_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + void __iomem *ioaddr = pdata->ioaddr; + u32 rtc_reg; + + rtc_reg = (bin2bcd(tm->tm_sec) << RTC_SECONDS_OFFS) | + (bin2bcd(tm->tm_min) << RTC_MINUTES_OFFS) | + (bin2bcd(tm->tm_hour) << RTC_HOURS_OFFS) | + (bin2bcd(tm->tm_wday) << RTC_WDAY_OFFS); + writel(rtc_reg, ioaddr + RTC_TIME_REG_OFFS); + + rtc_reg = (bin2bcd(tm->tm_mday) << RTC_MDAY_OFFS) | + (bin2bcd(tm->tm_mon + 1) << RTC_MONTH_OFFS) | + (bin2bcd(tm->tm_year - 100) << RTC_YEAR_OFFS); + writel(rtc_reg, ioaddr + RTC_DATE_REG_OFFS); + + return 0; +} + +static int mv_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + void __iomem *ioaddr = pdata->ioaddr; + u32 rtc_time, rtc_date; + unsigned int year, month, day, hour, minute, second, wday; + + rtc_time = readl(ioaddr + RTC_TIME_REG_OFFS); + rtc_date = readl(ioaddr + RTC_DATE_REG_OFFS); + + second = rtc_time & 0x7f; + minute = (rtc_time >> RTC_MINUTES_OFFS) & 0x7f; + hour = (rtc_time >> RTC_HOURS_OFFS) & 0x3f; /* assume 24 hour mode */ + wday = (rtc_time >> RTC_WDAY_OFFS) & 0x7; + + day = rtc_date & 0x3f; + month = (rtc_date >> RTC_MONTH_OFFS) & 0x3f; + year = (rtc_date >> RTC_YEAR_OFFS) & 0xff; + + tm->tm_sec = bcd2bin(second); + tm->tm_min = bcd2bin(minute); + tm->tm_hour = bcd2bin(hour); + tm->tm_mday = bcd2bin(day); + tm->tm_wday = bcd2bin(wday); + tm->tm_mon = bcd2bin(month) - 1; + /* hw counts from year 2000, but tm_year is relative to 1900 */ + tm->tm_year = bcd2bin(year) + 100; + + return 0; +} + +static int mv_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + void __iomem *ioaddr = pdata->ioaddr; + u32 rtc_time, rtc_date; + unsigned int year, month, day, hour, minute, second, wday; + + rtc_time = readl(ioaddr + RTC_ALARM_TIME_REG_OFFS); + rtc_date = readl(ioaddr + RTC_ALARM_DATE_REG_OFFS); + + second = rtc_time & 0x7f; + minute = (rtc_time >> RTC_MINUTES_OFFS) & 0x7f; + hour = (rtc_time >> RTC_HOURS_OFFS) & 0x3f; /* assume 24 hour mode */ + wday = (rtc_time >> RTC_WDAY_OFFS) & 0x7; + + day = rtc_date & 0x3f; + month = (rtc_date >> RTC_MONTH_OFFS) & 0x3f; + year = (rtc_date >> RTC_YEAR_OFFS) & 0xff; + + alm->time.tm_sec = bcd2bin(second); + alm->time.tm_min = bcd2bin(minute); + alm->time.tm_hour = bcd2bin(hour); + alm->time.tm_mday = bcd2bin(day); + alm->time.tm_wday = bcd2bin(wday); + alm->time.tm_mon = bcd2bin(month) - 1; + /* hw counts from year 2000, but tm_year is relative to 1900 */ + alm->time.tm_year = bcd2bin(year) + 100; + + alm->enabled = !!readl(ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS); + + return rtc_valid_tm(&alm->time); +} + +static int mv_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + void __iomem *ioaddr = pdata->ioaddr; + u32 rtc_reg = 0; + + if (alm->time.tm_sec >= 0) + rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_sec)) + << RTC_SECONDS_OFFS; + if (alm->time.tm_min >= 0) + rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_min)) + << RTC_MINUTES_OFFS; + if (alm->time.tm_hour >= 0) + rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_hour)) + << RTC_HOURS_OFFS; + + writel(rtc_reg, ioaddr + RTC_ALARM_TIME_REG_OFFS); + + if (alm->time.tm_mday >= 0) + rtc_reg = (RTC_ALARM_VALID | bin2bcd(alm->time.tm_mday)) + << RTC_MDAY_OFFS; + else + rtc_reg = 0; + + if (alm->time.tm_mon >= 0) + rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_mon + 1)) + << RTC_MONTH_OFFS; + + if (alm->time.tm_year >= 0) + rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_year - 100)) + << RTC_YEAR_OFFS; + + writel(rtc_reg, ioaddr + RTC_ALARM_DATE_REG_OFFS); + writel(0, ioaddr + RTC_ALARM_INTERRUPT_CASUE_REG_OFFS); + writel(alm->enabled ? 1 : 0, + ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS); + + return 0; +} + +static int mv_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + void __iomem *ioaddr = pdata->ioaddr; + + if (pdata->irq < 0) + return -EINVAL; /* fall back into rtc-dev's emulation */ + + if (enabled) + writel(1, ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS); + else + writel(0, ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS); + return 0; +} + +static irqreturn_t mv_rtc_interrupt(int irq, void *data) +{ + struct rtc_plat_data *pdata = data; + void __iomem *ioaddr = pdata->ioaddr; + + /* alarm irq? */ + if (!readl(ioaddr + RTC_ALARM_INTERRUPT_CASUE_REG_OFFS)) + return IRQ_NONE; + + /* clear interrupt */ + writel(0, ioaddr + RTC_ALARM_INTERRUPT_CASUE_REG_OFFS); + rtc_update_irq(pdata->rtc, 1, RTC_IRQF | RTC_AF); + return IRQ_HANDLED; +} + +static const struct rtc_class_ops mv_rtc_ops = { + .read_time = mv_rtc_read_time, + .set_time = mv_rtc_set_time, + .read_alarm = mv_rtc_read_alarm, + .set_alarm = mv_rtc_set_alarm, + .alarm_irq_enable = mv_rtc_alarm_irq_enable, +}; + +static int __init mv_rtc_probe(struct platform_device *pdev) +{ + struct rtc_plat_data *pdata; + u32 rtc_time; + int ret = 0; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->ioaddr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pdata->ioaddr)) + return PTR_ERR(pdata->ioaddr); + + pdata->clk = devm_clk_get_optional_prepared(&pdev->dev, NULL); + if (IS_ERR(pdata->clk)) + return PTR_ERR(pdata->clk); + + /* make sure the 24 hour mode is enabled */ + rtc_time = readl(pdata->ioaddr + RTC_TIME_REG_OFFS); + if (rtc_time & RTC_HOURS_12H_MODE) { + dev_err(&pdev->dev, "12 Hour mode is enabled but not supported.\n"); + return -EINVAL; + } + + /* make sure it is actually functional */ + if (rtc_time == 0x01000000) { + ssleep(1); + rtc_time = readl(pdata->ioaddr + RTC_TIME_REG_OFFS); + if (rtc_time == 0x01000000) { + dev_err(&pdev->dev, "internal RTC not ticking\n"); + return -ENODEV; + } + } + + pdata->irq = platform_get_irq(pdev, 0); + + platform_set_drvdata(pdev, pdata); + + pdata->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(pdata->rtc)) { + return PTR_ERR(pdata->rtc); + } + + if (pdata->irq >= 0) { + writel(0, pdata->ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS); + if (devm_request_irq(&pdev->dev, pdata->irq, mv_rtc_interrupt, + IRQF_SHARED, + pdev->name, pdata) < 0) { + dev_warn(&pdev->dev, "interrupt not available.\n"); + pdata->irq = -1; + } + } + + if (pdata->irq >= 0) + devm_device_init_wakeup(&pdev->dev); + else + clear_bit(RTC_FEATURE_ALARM, pdata->rtc->features); + + pdata->rtc->ops = &mv_rtc_ops; + pdata->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + pdata->rtc->range_max = RTC_TIMESTAMP_END_2099; + + ret = devm_rtc_register_device(pdata->rtc); + if (!ret) + return 0; + + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id rtc_mv_of_match_table[] = { + { .compatible = "marvell,orion-rtc", }, + {} +}; +MODULE_DEVICE_TABLE(of, rtc_mv_of_match_table); +#endif + +#ifdef CONFIG_PM_SLEEP +static int mv_rtc_suspend(struct device *dev) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + + if (device_may_wakeup(dev) && pdata->irq >= 0) + enable_irq_wake(pdata->irq); + return 0; +} + +static int mv_rtc_resume(struct device *dev) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + + if (device_may_wakeup(dev) && pdata->irq >= 0) + disable_irq_wake(pdata->irq); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(mv_rtc_pm_ops, mv_rtc_suspend, mv_rtc_resume); + +static struct platform_driver mv_rtc_driver = { + .driver = { + .name = "rtc-mv", + .of_match_table = of_match_ptr(rtc_mv_of_match_table), + .pm = &mv_rtc_pm_ops, + }, +}; + +module_platform_driver_probe(mv_rtc_driver, mv_rtc_probe); + +MODULE_AUTHOR("Saeed Bishara "); +MODULE_DESCRIPTION("Marvell RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rtc-mv"); diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c new file mode 100644 index 000000000..608db97d4 --- /dev/null +++ b/drivers/rtc/rtc-mxc.c @@ -0,0 +1,404 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RTC_INPUT_CLK_32768HZ (0x00 << 5) +#define RTC_INPUT_CLK_32000HZ (0x01 << 5) +#define RTC_INPUT_CLK_38400HZ (0x02 << 5) + +#define RTC_SW_BIT (1 << 0) +#define RTC_ALM_BIT (1 << 2) +#define RTC_1HZ_BIT (1 << 4) +#define RTC_2HZ_BIT (1 << 7) +#define RTC_SAM0_BIT (1 << 8) +#define RTC_SAM1_BIT (1 << 9) +#define RTC_SAM2_BIT (1 << 10) +#define RTC_SAM3_BIT (1 << 11) +#define RTC_SAM4_BIT (1 << 12) +#define RTC_SAM5_BIT (1 << 13) +#define RTC_SAM6_BIT (1 << 14) +#define RTC_SAM7_BIT (1 << 15) +#define PIT_ALL_ON (RTC_2HZ_BIT | RTC_SAM0_BIT | RTC_SAM1_BIT | \ + RTC_SAM2_BIT | RTC_SAM3_BIT | RTC_SAM4_BIT | \ + RTC_SAM5_BIT | RTC_SAM6_BIT | RTC_SAM7_BIT) + +#define RTC_ENABLE_BIT (1 << 7) + +#define MAX_PIE_NUM 9 +#define MAX_PIE_FREQ 512 + +#define MXC_RTC_TIME 0 +#define MXC_RTC_ALARM 1 + +#define RTC_HOURMIN 0x00 /* 32bit rtc hour/min counter reg */ +#define RTC_SECOND 0x04 /* 32bit rtc seconds counter reg */ +#define RTC_ALRM_HM 0x08 /* 32bit rtc alarm hour/min reg */ +#define RTC_ALRM_SEC 0x0C /* 32bit rtc alarm seconds reg */ +#define RTC_RTCCTL 0x10 /* 32bit rtc control reg */ +#define RTC_RTCISR 0x14 /* 32bit rtc interrupt status reg */ +#define RTC_RTCIENR 0x18 /* 32bit rtc interrupt enable reg */ +#define RTC_STPWCH 0x1C /* 32bit rtc stopwatch min reg */ +#define RTC_DAYR 0x20 /* 32bit rtc days counter reg */ +#define RTC_DAYALARM 0x24 /* 32bit rtc day alarm reg */ +#define RTC_TEST1 0x28 /* 32bit rtc test reg 1 */ +#define RTC_TEST2 0x2C /* 32bit rtc test reg 2 */ +#define RTC_TEST3 0x30 /* 32bit rtc test reg 3 */ + +enum imx_rtc_type { + IMX1_RTC, + IMX21_RTC, +}; + +struct rtc_plat_data { + struct rtc_device *rtc; + void __iomem *ioaddr; + int irq; + struct clk *clk_ref; + struct clk *clk_ipg; + struct rtc_time g_rtc_alarm; + enum imx_rtc_type devtype; +}; + +static const struct of_device_id imx_rtc_dt_ids[] = { + { .compatible = "fsl,imx1-rtc", .data = (const void *)IMX1_RTC }, + { .compatible = "fsl,imx21-rtc", .data = (const void *)IMX21_RTC }, + {} +}; +MODULE_DEVICE_TABLE(of, imx_rtc_dt_ids); + +static inline int is_imx1_rtc(struct rtc_plat_data *data) +{ + return data->devtype == IMX1_RTC; +} + +/* + * This function is used to obtain the RTC time or the alarm value in + * second. + */ +static time64_t get_alarm_or_time(struct device *dev, int time_alarm) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + void __iomem *ioaddr = pdata->ioaddr; + u32 day = 0, hr = 0, min = 0, sec = 0, hr_min = 0; + + switch (time_alarm) { + case MXC_RTC_TIME: + day = readw(ioaddr + RTC_DAYR); + hr_min = readw(ioaddr + RTC_HOURMIN); + sec = readw(ioaddr + RTC_SECOND); + break; + case MXC_RTC_ALARM: + day = readw(ioaddr + RTC_DAYALARM); + hr_min = readw(ioaddr + RTC_ALRM_HM) & 0xffff; + sec = readw(ioaddr + RTC_ALRM_SEC); + break; + } + + hr = hr_min >> 8; + min = hr_min & 0xff; + + return ((((time64_t)day * 24 + hr) * 60) + min) * 60 + sec; +} + +/* + * This function sets the RTC alarm value or the time value. + */ +static void set_alarm_or_time(struct device *dev, int time_alarm, time64_t time) +{ + u32 tod, day, hr, min, sec, temp; + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + void __iomem *ioaddr = pdata->ioaddr; + + day = div_s64_rem(time, 86400, &tod); + + /* time is within a day now */ + hr = tod / 3600; + tod -= hr * 3600; + + /* time is within an hour now */ + min = tod / 60; + sec = tod - min * 60; + + temp = (hr << 8) + min; + + switch (time_alarm) { + case MXC_RTC_TIME: + writew(day, ioaddr + RTC_DAYR); + writew(sec, ioaddr + RTC_SECOND); + writew(temp, ioaddr + RTC_HOURMIN); + break; + case MXC_RTC_ALARM: + writew(day, ioaddr + RTC_DAYALARM); + writew(sec, ioaddr + RTC_ALRM_SEC); + writew(temp, ioaddr + RTC_ALRM_HM); + break; + } +} + +/* + * This function updates the RTC alarm registers and then clears all the + * interrupt status bits. + */ +static void rtc_update_alarm(struct device *dev, struct rtc_time *alrm) +{ + time64_t time; + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + void __iomem *ioaddr = pdata->ioaddr; + + time = rtc_tm_to_time64(alrm); + + /* clear all the interrupt status bits */ + writew(readw(ioaddr + RTC_RTCISR), ioaddr + RTC_RTCISR); + set_alarm_or_time(dev, MXC_RTC_ALARM, time); +} + +static void mxc_rtc_irq_enable(struct device *dev, unsigned int bit, + unsigned int enabled) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + void __iomem *ioaddr = pdata->ioaddr; + u32 reg; + unsigned long flags; + + spin_lock_irqsave(&pdata->rtc->irq_lock, flags); + reg = readw(ioaddr + RTC_RTCIENR); + + if (enabled) + reg |= bit; + else + reg &= ~bit; + + writew(reg, ioaddr + RTC_RTCIENR); + spin_unlock_irqrestore(&pdata->rtc->irq_lock, flags); +} + +/* This function is the RTC interrupt service routine. */ +static irqreturn_t mxc_rtc_interrupt(int irq, void *dev_id) +{ + struct platform_device *pdev = dev_id; + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + u32 status; + u32 events = 0; + + spin_lock(&pdata->rtc->irq_lock); + status = readw(ioaddr + RTC_RTCISR) & readw(ioaddr + RTC_RTCIENR); + /* clear interrupt sources */ + writew(status, ioaddr + RTC_RTCISR); + + /* update irq data & counter */ + if (status & RTC_ALM_BIT) { + events |= (RTC_AF | RTC_IRQF); + /* RTC alarm should be one-shot */ + mxc_rtc_irq_enable(&pdev->dev, RTC_ALM_BIT, 0); + } + + if (status & PIT_ALL_ON) + events |= (RTC_PF | RTC_IRQF); + + rtc_update_irq(pdata->rtc, 1, events); + spin_unlock(&pdata->rtc->irq_lock); + + return IRQ_HANDLED; +} + +static int mxc_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + mxc_rtc_irq_enable(dev, RTC_ALM_BIT, enabled); + return 0; +} + +/* + * This function reads the current RTC time into tm in Gregorian date. + */ +static int mxc_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + time64_t val; + + /* Avoid roll-over from reading the different registers */ + do { + val = get_alarm_or_time(dev, MXC_RTC_TIME); + } while (val != get_alarm_or_time(dev, MXC_RTC_TIME)); + + rtc_time64_to_tm(val, tm); + + return 0; +} + +/* + * This function sets the internal RTC time based on tm in Gregorian date. + */ +static int mxc_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + time64_t time = rtc_tm_to_time64(tm); + + /* Avoid roll-over from reading the different registers */ + do { + set_alarm_or_time(dev, MXC_RTC_TIME, time); + } while (time != get_alarm_or_time(dev, MXC_RTC_TIME)); + + return 0; +} + +/* + * This function reads the current alarm value into the passed in 'alrm' + * argument. It updates the alrm's pending field value based on the whether + * an alarm interrupt occurs or not. + */ +static int mxc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + void __iomem *ioaddr = pdata->ioaddr; + + rtc_time64_to_tm(get_alarm_or_time(dev, MXC_RTC_ALARM), &alrm->time); + alrm->pending = ((readw(ioaddr + RTC_RTCISR) & RTC_ALM_BIT)) ? 1 : 0; + + return 0; +} + +/* + * This function sets the RTC alarm based on passed in alrm. + */ +static int mxc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + + rtc_update_alarm(dev, &alrm->time); + + memcpy(&pdata->g_rtc_alarm, &alrm->time, sizeof(struct rtc_time)); + mxc_rtc_irq_enable(dev, RTC_ALM_BIT, alrm->enabled); + + return 0; +} + +/* RTC layer */ +static const struct rtc_class_ops mxc_rtc_ops = { + .read_time = mxc_rtc_read_time, + .set_time = mxc_rtc_set_time, + .read_alarm = mxc_rtc_read_alarm, + .set_alarm = mxc_rtc_set_alarm, + .alarm_irq_enable = mxc_rtc_alarm_irq_enable, +}; + +static int mxc_rtc_probe(struct platform_device *pdev) +{ + struct rtc_device *rtc; + struct rtc_plat_data *pdata = NULL; + u32 reg; + unsigned long rate; + int ret; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->devtype = (uintptr_t)of_device_get_match_data(&pdev->dev); + + pdata->ioaddr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pdata->ioaddr)) + return PTR_ERR(pdata->ioaddr); + + rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + pdata->rtc = rtc; + rtc->ops = &mxc_rtc_ops; + if (is_imx1_rtc(pdata)) { + struct rtc_time tm; + + /* 9bit days + hours minutes seconds */ + rtc->range_max = (1 << 9) * 86400 - 1; + + /* + * Set the start date as beginning of the current year. This can + * be overridden using device tree. + */ + rtc_time64_to_tm(ktime_get_real_seconds(), &tm); + rtc->start_secs = mktime64(tm.tm_year, 1, 1, 0, 0, 0); + rtc->set_start_time = true; + } else { + /* 16bit days + hours minutes seconds */ + rtc->range_max = (1 << 16) * 86400ULL - 1; + } + + pdata->clk_ipg = devm_clk_get_enabled(&pdev->dev, "ipg"); + if (IS_ERR(pdata->clk_ipg)) { + dev_err(&pdev->dev, "unable to get ipg clock!\n"); + return PTR_ERR(pdata->clk_ipg); + } + + pdata->clk_ref = devm_clk_get_enabled(&pdev->dev, "ref"); + if (IS_ERR(pdata->clk_ref)) { + dev_err(&pdev->dev, "unable to get ref clock!\n"); + return PTR_ERR(pdata->clk_ref); + } + + rate = clk_get_rate(pdata->clk_ref); + + if (rate == 32768) + reg = RTC_INPUT_CLK_32768HZ; + else if (rate == 32000) + reg = RTC_INPUT_CLK_32000HZ; + else if (rate == 38400) + reg = RTC_INPUT_CLK_38400HZ; + else { + dev_err(&pdev->dev, "rtc clock is not valid (%lu)\n", rate); + return -EINVAL; + } + + reg |= RTC_ENABLE_BIT; + writew(reg, (pdata->ioaddr + RTC_RTCCTL)); + if (((readw(pdata->ioaddr + RTC_RTCCTL)) & RTC_ENABLE_BIT) == 0) { + dev_err(&pdev->dev, "hardware module can't be enabled!\n"); + return -EIO; + } + + platform_set_drvdata(pdev, pdata); + + /* Configure and enable the RTC */ + pdata->irq = platform_get_irq(pdev, 0); + + if (pdata->irq >= 0 && + devm_request_irq(&pdev->dev, pdata->irq, mxc_rtc_interrupt, + IRQF_SHARED, pdev->name, pdev) < 0) { + dev_warn(&pdev->dev, "interrupt not available.\n"); + pdata->irq = -1; + } + + if (pdata->irq >= 0) { + device_init_wakeup(&pdev->dev, true); + ret = dev_pm_set_wake_irq(&pdev->dev, pdata->irq); + if (ret) + dev_err(&pdev->dev, "failed to enable irq wake\n"); + } + + ret = devm_rtc_register_device(rtc); + + return ret; +} + +static struct platform_driver mxc_rtc_driver = { + .driver = { + .name = "mxc_rtc", + .of_match_table = imx_rtc_dt_ids, + }, + .probe = mxc_rtc_probe, +}; + +module_platform_driver(mxc_rtc_driver) + +MODULE_AUTHOR("Daniel Mack "); +MODULE_DESCRIPTION("RTC driver for Freescale MXC"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/rtc/rtc-mxc_v2.c b/drivers/rtc/rtc-mxc_v2.c new file mode 100644 index 000000000..a07acbbcf --- /dev/null +++ b/drivers/rtc/rtc-mxc_v2.c @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Real Time Clock (RTC) Driver for i.MX53 + * Copyright (c) 2004-2011 Freescale Semiconductor, Inc. + * Copyright (c) 2017 Beckhoff Automation GmbH & Co. KG + */ + +#include +#include +#include +#include +#include +#include + +#define SRTC_LPPDR_INIT 0x41736166 /* init for glitch detect */ + +#define SRTC_LPCR_EN_LP BIT(3) /* lp enable */ +#define SRTC_LPCR_WAE BIT(4) /* lp wakeup alarm enable */ +#define SRTC_LPCR_ALP BIT(7) /* lp alarm flag */ +#define SRTC_LPCR_NSA BIT(11) /* lp non secure access */ +#define SRTC_LPCR_NVE BIT(14) /* lp non valid state exit bit */ +#define SRTC_LPCR_IE BIT(15) /* lp init state exit bit */ + +#define SRTC_LPSR_ALP BIT(3) /* lp alarm flag */ +#define SRTC_LPSR_NVES BIT(14) /* lp non-valid state exit status */ +#define SRTC_LPSR_IES BIT(15) /* lp init state exit status */ + +#define SRTC_LPSCMR 0x00 /* LP Secure Counter MSB Reg */ +#define SRTC_LPSCLR 0x04 /* LP Secure Counter LSB Reg */ +#define SRTC_LPSAR 0x08 /* LP Secure Alarm Reg */ +#define SRTC_LPCR 0x10 /* LP Control Reg */ +#define SRTC_LPSR 0x14 /* LP Status Reg */ +#define SRTC_LPPDR 0x18 /* LP Power Supply Glitch Detector Reg */ + +/* max. number of retries to read registers, 120 was max during test */ +#define REG_READ_TIMEOUT 2000 + +struct mxc_rtc_data { + struct rtc_device *rtc; + void __iomem *ioaddr; + struct clk *clk; + spinlock_t lock; /* protects register access */ + int irq; +}; + +/* + * This function does write synchronization for writes to the lp srtc block. + * To take care of the asynchronous CKIL clock, all writes from the IP domain + * will be synchronized to the CKIL domain. + * The caller should hold the pdata->lock + */ +static void mxc_rtc_sync_lp_locked(struct device *dev, void __iomem *ioaddr) +{ + unsigned int i; + + /* Wait for 3 CKIL cycles */ + for (i = 0; i < 3; i++) { + const u32 count = readl(ioaddr + SRTC_LPSCLR); + unsigned int timeout = REG_READ_TIMEOUT; + + while ((readl(ioaddr + SRTC_LPSCLR)) == count) { + if (!--timeout) { + dev_err_once(dev, "SRTC_LPSCLR stuck! Check your hw.\n"); + return; + } + } + } +} + +/* This function is the RTC interrupt service routine. */ +static irqreturn_t mxc_rtc_interrupt(int irq, void *dev_id) +{ + struct device *dev = dev_id; + struct mxc_rtc_data *pdata = dev_get_drvdata(dev); + void __iomem *ioaddr = pdata->ioaddr; + u32 lp_status; + u32 lp_cr; + + spin_lock(&pdata->lock); + if (clk_enable(pdata->clk)) { + spin_unlock(&pdata->lock); + return IRQ_NONE; + } + + lp_status = readl(ioaddr + SRTC_LPSR); + lp_cr = readl(ioaddr + SRTC_LPCR); + + /* update irq data & counter */ + if (lp_status & SRTC_LPSR_ALP) { + if (lp_cr & SRTC_LPCR_ALP) + rtc_update_irq(pdata->rtc, 1, RTC_AF | RTC_IRQF); + + /* disable further lp alarm interrupts */ + lp_cr &= ~(SRTC_LPCR_ALP | SRTC_LPCR_WAE); + } + + /* Update interrupt enables */ + writel(lp_cr, ioaddr + SRTC_LPCR); + + /* clear interrupt status */ + writel(lp_status, ioaddr + SRTC_LPSR); + + mxc_rtc_sync_lp_locked(dev, ioaddr); + clk_disable(pdata->clk); + spin_unlock(&pdata->lock); + return IRQ_HANDLED; +} + +/* + * Enable clk and aquire spinlock + * @return 0 if successful; non-zero otherwise. + */ +static int mxc_rtc_lock(struct mxc_rtc_data *const pdata) +{ + int ret; + + spin_lock_irq(&pdata->lock); + ret = clk_enable(pdata->clk); + if (ret) { + spin_unlock_irq(&pdata->lock); + return ret; + } + return 0; +} + +static int mxc_rtc_unlock(struct mxc_rtc_data *const pdata) +{ + clk_disable(pdata->clk); + spin_unlock_irq(&pdata->lock); + return 0; +} + +/* + * This function reads the current RTC time into tm in Gregorian date. + * + * @param tm contains the RTC time value upon return + * + * @return 0 if successful; non-zero otherwise. + */ +static int mxc_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct mxc_rtc_data *pdata = dev_get_drvdata(dev); + const int clk_failed = clk_enable(pdata->clk); + + if (!clk_failed) { + const time64_t now = readl(pdata->ioaddr + SRTC_LPSCMR); + + rtc_time64_to_tm(now, tm); + clk_disable(pdata->clk); + return 0; + } + return clk_failed; +} + +/* + * This function sets the internal RTC time based on tm in Gregorian date. + * + * @param tm the time value to be set in the RTC + * + * @return 0 if successful; non-zero otherwise. + */ +static int mxc_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct mxc_rtc_data *pdata = dev_get_drvdata(dev); + time64_t time = rtc_tm_to_time64(tm); + int ret; + + ret = mxc_rtc_lock(pdata); + if (ret) + return ret; + + writel(time, pdata->ioaddr + SRTC_LPSCMR); + mxc_rtc_sync_lp_locked(dev, pdata->ioaddr); + return mxc_rtc_unlock(pdata); +} + +/* + * This function reads the current alarm value into the passed in \b alrm + * argument. It updates the \b alrm's pending field value based on the whether + * an alarm interrupt occurs or not. + * + * @param alrm contains the RTC alarm value upon return + * + * @return 0 if successful; non-zero otherwise. + */ +static int mxc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct mxc_rtc_data *pdata = dev_get_drvdata(dev); + void __iomem *ioaddr = pdata->ioaddr; + int ret; + + ret = mxc_rtc_lock(pdata); + if (ret) + return ret; + + rtc_time64_to_tm(readl(ioaddr + SRTC_LPSAR), &alrm->time); + alrm->pending = !!(readl(ioaddr + SRTC_LPSR) & SRTC_LPSR_ALP); + return mxc_rtc_unlock(pdata); +} + +/* + * Enable/Disable alarm interrupt + * The caller should hold the pdata->lock + */ +static void mxc_rtc_alarm_irq_enable_locked(struct mxc_rtc_data *pdata, + unsigned int enable) +{ + u32 lp_cr = readl(pdata->ioaddr + SRTC_LPCR); + + if (enable) + lp_cr |= (SRTC_LPCR_ALP | SRTC_LPCR_WAE); + else + lp_cr &= ~(SRTC_LPCR_ALP | SRTC_LPCR_WAE); + + writel(lp_cr, pdata->ioaddr + SRTC_LPCR); +} + +static int mxc_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) +{ + struct mxc_rtc_data *pdata = dev_get_drvdata(dev); + int ret = mxc_rtc_lock(pdata); + + if (ret) + return ret; + + mxc_rtc_alarm_irq_enable_locked(pdata, enable); + return mxc_rtc_unlock(pdata); +} + +/* + * This function sets the RTC alarm based on passed in alrm. + * + * @param alrm the alarm value to be set in the RTC + * + * @return 0 if successful; non-zero otherwise. + */ +static int mxc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + const time64_t time = rtc_tm_to_time64(&alrm->time); + struct mxc_rtc_data *pdata = dev_get_drvdata(dev); + int ret = mxc_rtc_lock(pdata); + + if (ret) + return ret; + + writel((u32)time, pdata->ioaddr + SRTC_LPSAR); + + /* clear alarm interrupt status bit */ + writel(SRTC_LPSR_ALP, pdata->ioaddr + SRTC_LPSR); + mxc_rtc_sync_lp_locked(dev, pdata->ioaddr); + + mxc_rtc_alarm_irq_enable_locked(pdata, alrm->enabled); + mxc_rtc_sync_lp_locked(dev, pdata->ioaddr); + mxc_rtc_unlock(pdata); + return ret; +} + +static const struct rtc_class_ops mxc_rtc_ops = { + .read_time = mxc_rtc_read_time, + .set_time = mxc_rtc_set_time, + .read_alarm = mxc_rtc_read_alarm, + .set_alarm = mxc_rtc_set_alarm, + .alarm_irq_enable = mxc_rtc_alarm_irq_enable, +}; + +static int mxc_rtc_wait_for_flag(void __iomem *ioaddr, int flag) +{ + unsigned int timeout = REG_READ_TIMEOUT; + + while (!(readl(ioaddr) & flag)) { + if (!--timeout) + return -EBUSY; + } + return 0; +} + +static int mxc_rtc_probe(struct platform_device *pdev) +{ + struct mxc_rtc_data *pdata; + void __iomem *ioaddr; + int ret = 0; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->ioaddr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pdata->ioaddr)) + return PTR_ERR(pdata->ioaddr); + + ioaddr = pdata->ioaddr; + + pdata->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pdata->clk)) { + dev_err(&pdev->dev, "unable to get rtc clock!\n"); + return PTR_ERR(pdata->clk); + } + + spin_lock_init(&pdata->lock); + pdata->irq = platform_get_irq(pdev, 0); + if (pdata->irq < 0) + return pdata->irq; + + device_init_wakeup(&pdev->dev, true); + ret = dev_pm_set_wake_irq(&pdev->dev, pdata->irq); + if (ret) + dev_err(&pdev->dev, "failed to enable irq wake\n"); + + ret = clk_prepare_enable(pdata->clk); + if (ret) + return ret; + /* initialize glitch detect */ + writel(SRTC_LPPDR_INIT, ioaddr + SRTC_LPPDR); + + /* clear lp interrupt status */ + writel(0xFFFFFFFF, ioaddr + SRTC_LPSR); + + /* move out of init state */ + writel((SRTC_LPCR_IE | SRTC_LPCR_NSA), ioaddr + SRTC_LPCR); + ret = mxc_rtc_wait_for_flag(ioaddr + SRTC_LPSR, SRTC_LPSR_IES); + if (ret) { + dev_err(&pdev->dev, "Timeout waiting for SRTC_LPSR_IES\n"); + clk_disable_unprepare(pdata->clk); + return ret; + } + + /* move out of non-valid state */ + writel((SRTC_LPCR_IE | SRTC_LPCR_NVE | SRTC_LPCR_NSA | + SRTC_LPCR_EN_LP), ioaddr + SRTC_LPCR); + ret = mxc_rtc_wait_for_flag(ioaddr + SRTC_LPSR, SRTC_LPSR_NVES); + if (ret) { + dev_err(&pdev->dev, "Timeout waiting for SRTC_LPSR_NVES\n"); + clk_disable_unprepare(pdata->clk); + return ret; + } + + pdata->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(pdata->rtc)) { + clk_disable_unprepare(pdata->clk); + return PTR_ERR(pdata->rtc); + } + + pdata->rtc->ops = &mxc_rtc_ops; + pdata->rtc->range_max = U32_MAX; + + clk_disable(pdata->clk); + platform_set_drvdata(pdev, pdata); + ret = + devm_request_irq(&pdev->dev, pdata->irq, mxc_rtc_interrupt, 0, + pdev->name, &pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "interrupt not available.\n"); + clk_unprepare(pdata->clk); + return ret; + } + + ret = devm_rtc_register_device(pdata->rtc); + if (ret < 0) + clk_unprepare(pdata->clk); + + return ret; +} + +static void mxc_rtc_remove(struct platform_device *pdev) +{ + struct mxc_rtc_data *pdata = platform_get_drvdata(pdev); + + clk_disable_unprepare(pdata->clk); +} + +static const struct of_device_id mxc_ids[] = { + { .compatible = "fsl,imx53-rtc", }, + {} +}; +MODULE_DEVICE_TABLE(of, mxc_ids); + +static struct platform_driver mxc_rtc_driver = { + .driver = { + .name = "mxc_rtc_v2", + .of_match_table = mxc_ids, + }, + .probe = mxc_rtc_probe, + .remove = mxc_rtc_remove, +}; + +module_platform_driver(mxc_rtc_driver); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("Real Time Clock (RTC) Driver for i.MX53"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-nct3018y.c b/drivers/rtc/rtc-nct3018y.c new file mode 100644 index 000000000..2f7ad5705 --- /dev/null +++ b/drivers/rtc/rtc-nct3018y.c @@ -0,0 +1,600 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2022 Nuvoton Technology Corporation + +#include +#include +#include +#include +#include +#include +#include +#include + +#define NCT3018Y_REG_SC 0x00 /* seconds */ +#define NCT3018Y_REG_SCA 0x01 /* alarm */ +#define NCT3018Y_REG_MN 0x02 +#define NCT3018Y_REG_MNA 0x03 /* alarm */ +#define NCT3018Y_REG_HR 0x04 +#define NCT3018Y_REG_HRA 0x05 /* alarm */ +#define NCT3018Y_REG_DW 0x06 +#define NCT3018Y_REG_DM 0x07 +#define NCT3018Y_REG_MO 0x08 +#define NCT3018Y_REG_YR 0x09 +#define NCT3018Y_REG_CTRL 0x0A /* timer control */ +#define NCT3018Y_REG_ST 0x0B /* status */ +#define NCT3018Y_REG_CLKO 0x0C /* clock out */ +#define NCT3018Y_REG_PART 0x21 /* part info */ + +#define NCT3018Y_BIT_AF BIT(7) +#define NCT3018Y_BIT_ST BIT(7) +#define NCT3018Y_BIT_DM BIT(6) +#define NCT3018Y_BIT_HF BIT(5) +#define NCT3018Y_BIT_DSM BIT(4) +#define NCT3018Y_BIT_AIE BIT(3) +#define NCT3018Y_BIT_OFIE BIT(2) +#define NCT3018Y_BIT_CIE BIT(1) +#define NCT3018Y_BIT_TWO BIT(0) + +#define NCT3018Y_REG_BAT_MASK 0x07 +#define NCT3018Y_REG_CLKO_F_MASK 0x03 /* frequenc mask */ +#define NCT3018Y_REG_CLKO_CKE 0x80 /* clock out enabled */ +#define NCT3018Y_REG_PART_NCT3018Y 0x02 + +struct nct3018y { + struct rtc_device *rtc; + struct i2c_client *client; + int part_num; +#ifdef CONFIG_COMMON_CLK + struct clk_hw clkout_hw; +#endif +}; + +static int nct3018y_set_alarm_mode(struct i2c_client *client, bool on) +{ + int err, flags; + + dev_dbg(&client->dev, "%s:on:%d\n", __func__, on); + + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CTRL); + if (flags < 0) { + dev_dbg(&client->dev, + "Failed to read NCT3018Y_REG_CTRL\n"); + return flags; + } + + if (on) + flags |= NCT3018Y_BIT_AIE; + else + flags &= ~NCT3018Y_BIT_AIE; + + flags |= NCT3018Y_BIT_CIE; + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_CTRL, flags); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_CTRL\n"); + return err; + } + + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_ST); + if (flags < 0) { + dev_dbg(&client->dev, + "Failed to read NCT3018Y_REG_ST\n"); + return flags; + } + + flags &= ~(NCT3018Y_BIT_AF); + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_ST, flags); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_ST\n"); + return err; + } + + return 0; +} + +static int nct3018y_get_alarm_mode(struct i2c_client *client, unsigned char *alarm_enable, + unsigned char *alarm_flag) +{ + int flags; + + if (alarm_enable) { + dev_dbg(&client->dev, "%s:NCT3018Y_REG_CTRL\n", __func__); + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CTRL); + if (flags < 0) + return flags; + *alarm_enable = flags & NCT3018Y_BIT_AIE; + dev_dbg(&client->dev, "%s:alarm_enable:%x\n", __func__, *alarm_enable); + + } + + if (alarm_flag) { + dev_dbg(&client->dev, "%s:NCT3018Y_REG_ST\n", __func__); + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_ST); + if (flags < 0) + return flags; + *alarm_flag = flags & NCT3018Y_BIT_AF; + dev_dbg(&client->dev, "%s:alarm_flag:%x\n", __func__, *alarm_flag); + } + + return 0; +} + +static irqreturn_t nct3018y_irq(int irq, void *dev_id) +{ + struct nct3018y *nct3018y = i2c_get_clientdata(dev_id); + struct i2c_client *client = nct3018y->client; + int err; + unsigned char alarm_flag; + unsigned char alarm_enable; + + dev_dbg(&client->dev, "%s:irq:%d\n", __func__, irq); + err = nct3018y_get_alarm_mode(nct3018y->client, &alarm_enable, &alarm_flag); + if (err) + return IRQ_NONE; + + if (alarm_flag) { + dev_dbg(&client->dev, "%s:alarm flag:%x\n", + __func__, alarm_flag); + rtc_update_irq(nct3018y->rtc, 1, RTC_IRQF | RTC_AF); + nct3018y_set_alarm_mode(nct3018y->client, 0); + dev_dbg(&client->dev, "%s:IRQ_HANDLED\n", __func__); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +/* + * In the routines that deal directly with the nct3018y hardware, we use + * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. + */ +static int nct3018y_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned char buf[10]; + int err; + + err = i2c_smbus_read_i2c_block_data(client, NCT3018Y_REG_ST, 1, buf); + if (err < 0) + return err; + + if (!buf[0]) { + dev_dbg(&client->dev, " voltage <=1.7, date/time is not reliable.\n"); + return -EINVAL; + } + + err = i2c_smbus_read_i2c_block_data(client, NCT3018Y_REG_SC, sizeof(buf), buf); + if (err < 0) + return err; + + tm->tm_sec = bcd2bin(buf[0] & 0x7F); + tm->tm_min = bcd2bin(buf[2] & 0x7F); + tm->tm_hour = bcd2bin(buf[4] & 0x3F); + tm->tm_wday = buf[6] & 0x07; + tm->tm_mday = bcd2bin(buf[7] & 0x3F); + tm->tm_mon = bcd2bin(buf[8] & 0x1F) - 1; + tm->tm_year = bcd2bin(buf[9]) + 100; + + return 0; +} + +static int nct3018y_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + struct nct3018y *nct3018y = dev_get_drvdata(dev); + unsigned char buf[4] = {0}; + int err, flags; + int restore_flags = 0; + + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CTRL); + if (flags < 0) { + dev_dbg(&client->dev, "Failed to read NCT3018Y_REG_CTRL.\n"); + return flags; + } + + /* Check and set TWO bit */ + if (nct3018y->part_num == NCT3018Y_REG_PART_NCT3018Y && !(flags & NCT3018Y_BIT_TWO)) { + restore_flags = 1; + flags |= NCT3018Y_BIT_TWO; + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_CTRL, flags); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_CTRL.\n"); + return err; + } + } + + buf[0] = bin2bcd(tm->tm_sec); + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_SC, buf[0]); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_SC\n"); + return err; + } + + buf[0] = bin2bcd(tm->tm_min); + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_MN, buf[0]); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_MN\n"); + return err; + } + + buf[0] = bin2bcd(tm->tm_hour); + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_HR, buf[0]); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_HR\n"); + return err; + } + + buf[0] = tm->tm_wday & 0x07; + buf[1] = bin2bcd(tm->tm_mday); + buf[2] = bin2bcd(tm->tm_mon + 1); + buf[3] = bin2bcd(tm->tm_year - 100); + err = i2c_smbus_write_i2c_block_data(client, NCT3018Y_REG_DW, + sizeof(buf), buf); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write for day and mon and year\n"); + return -EIO; + } + + /* Restore TWO bit */ + if (restore_flags) { + if (nct3018y->part_num == NCT3018Y_REG_PART_NCT3018Y) + flags &= ~NCT3018Y_BIT_TWO; + + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_CTRL, flags); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_CTRL.\n"); + return err; + } + } + + return err; +} + +static int nct3018y_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned char buf[5]; + int err; + + err = i2c_smbus_read_i2c_block_data(client, NCT3018Y_REG_SCA, + sizeof(buf), buf); + if (err < 0) { + dev_dbg(&client->dev, "Unable to read date\n"); + return -EIO; + } + + dev_dbg(&client->dev, "%s: raw data is sec=%02x, min=%02x hr=%02x\n", + __func__, buf[0], buf[2], buf[4]); + + tm->time.tm_sec = bcd2bin(buf[0] & 0x7F); + tm->time.tm_min = bcd2bin(buf[2] & 0x7F); + tm->time.tm_hour = bcd2bin(buf[4] & 0x3F); + + err = nct3018y_get_alarm_mode(client, &tm->enabled, &tm->pending); + if (err < 0) + return err; + + dev_dbg(&client->dev, "%s:s=%d m=%d, hr=%d, enabled=%d, pending=%d\n", + __func__, tm->time.tm_sec, tm->time.tm_min, + tm->time.tm_hour, tm->enabled, tm->pending); + + return 0; +} + +static int nct3018y_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + int err; + + dev_dbg(dev, "%s, sec=%d, min=%d hour=%d tm->enabled:%d\n", + __func__, tm->time.tm_sec, tm->time.tm_min, tm->time.tm_hour, + tm->enabled); + + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_SCA, bin2bcd(tm->time.tm_sec)); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_SCA\n"); + return err; + } + + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_MNA, bin2bcd(tm->time.tm_min)); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_MNA\n"); + return err; + } + + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_HRA, bin2bcd(tm->time.tm_hour)); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_HRA\n"); + return err; + } + + return nct3018y_set_alarm_mode(client, tm->enabled); +} + +static int nct3018y_irq_enable(struct device *dev, unsigned int enabled) +{ + dev_dbg(dev, "%s: alarm enable=%d\n", __func__, enabled); + + return nct3018y_set_alarm_mode(to_i2c_client(dev), enabled); +} + +static int nct3018y_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct i2c_client *client = to_i2c_client(dev); + int status, flags = 0; + + switch (cmd) { + case RTC_VL_READ: + status = i2c_smbus_read_byte_data(client, NCT3018Y_REG_ST); + if (status < 0) + return status; + + if (!(status & NCT3018Y_REG_BAT_MASK)) + flags |= RTC_VL_DATA_INVALID; + + return put_user(flags, (unsigned int __user *)arg); + + default: + return -ENOIOCTLCMD; + } +} + +#ifdef CONFIG_COMMON_CLK +/* + * Handling of the clkout + */ + +#define clkout_hw_to_nct3018y(_hw) container_of(_hw, struct nct3018y, clkout_hw) + +static const int clkout_rates[] = { + 32768, + 1024, + 32, + 1, +}; + +static unsigned long nct3018y_clkout_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct nct3018y *nct3018y = clkout_hw_to_nct3018y(hw); + struct i2c_client *client = nct3018y->client; + int flags; + + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CLKO); + if (flags < 0) + return 0; + + flags &= NCT3018Y_REG_CLKO_F_MASK; + return clkout_rates[flags]; +} + +static int nct3018y_clkout_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(clkout_rates); i++) + if (clkout_rates[i] <= req->rate) { + req->rate = clkout_rates[i]; + + return 0; + } + + req->rate = clkout_rates[0]; + + return 0; +} + +static int nct3018y_clkout_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct nct3018y *nct3018y = clkout_hw_to_nct3018y(hw); + struct i2c_client *client = nct3018y->client; + int i, flags; + + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CLKO); + if (flags < 0) + return flags; + + for (i = 0; i < ARRAY_SIZE(clkout_rates); i++) + if (clkout_rates[i] == rate) { + flags &= ~NCT3018Y_REG_CLKO_F_MASK; + flags |= i; + return i2c_smbus_write_byte_data(client, NCT3018Y_REG_CLKO, flags); + } + + return -EINVAL; +} + +static int nct3018y_clkout_control(struct clk_hw *hw, bool enable) +{ + struct nct3018y *nct3018y = clkout_hw_to_nct3018y(hw); + struct i2c_client *client = nct3018y->client; + int flags; + + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CLKO); + if (flags < 0) + return flags; + + if (enable) + flags |= NCT3018Y_REG_CLKO_CKE; + else + flags &= ~NCT3018Y_REG_CLKO_CKE; + + return i2c_smbus_write_byte_data(client, NCT3018Y_REG_CLKO, flags); +} + +static int nct3018y_clkout_prepare(struct clk_hw *hw) +{ + return nct3018y_clkout_control(hw, 1); +} + +static void nct3018y_clkout_unprepare(struct clk_hw *hw) +{ + nct3018y_clkout_control(hw, 0); +} + +static int nct3018y_clkout_is_prepared(struct clk_hw *hw) +{ + struct nct3018y *nct3018y = clkout_hw_to_nct3018y(hw); + struct i2c_client *client = nct3018y->client; + int flags; + + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CLKO); + if (flags < 0) + return flags; + + return flags & NCT3018Y_REG_CLKO_CKE; +} + +static const struct clk_ops nct3018y_clkout_ops = { + .prepare = nct3018y_clkout_prepare, + .unprepare = nct3018y_clkout_unprepare, + .is_prepared = nct3018y_clkout_is_prepared, + .recalc_rate = nct3018y_clkout_recalc_rate, + .determine_rate = nct3018y_clkout_determine_rate, + .set_rate = nct3018y_clkout_set_rate, +}; + +static struct clk *nct3018y_clkout_register_clk(struct nct3018y *nct3018y) +{ + struct i2c_client *client = nct3018y->client; + struct device_node *node = client->dev.of_node; + struct clk_init_data init = {}; + struct clk *clk; + + init.name = "nct3018y-clkout"; + init.ops = &nct3018y_clkout_ops; + init.flags = 0; + init.parent_names = NULL; + init.num_parents = 0; + nct3018y->clkout_hw.init = &init; + + /* optional override of the clockname */ + of_property_read_string(node, "clock-output-names", &init.name); + + /* register the clock */ + clk = devm_clk_register(&client->dev, &nct3018y->clkout_hw); + + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); + + return clk; +} +#endif + +static const struct rtc_class_ops nct3018y_rtc_ops = { + .read_time = nct3018y_rtc_read_time, + .set_time = nct3018y_rtc_set_time, + .read_alarm = nct3018y_rtc_read_alarm, + .set_alarm = nct3018y_rtc_set_alarm, + .alarm_irq_enable = nct3018y_irq_enable, + .ioctl = nct3018y_ioctl, +}; + +static int nct3018y_probe(struct i2c_client *client) +{ + struct nct3018y *nct3018y; + int err, flags; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BLOCK_DATA)) + return -ENODEV; + + nct3018y = devm_kzalloc(&client->dev, sizeof(struct nct3018y), + GFP_KERNEL); + if (!nct3018y) + return -ENOMEM; + + i2c_set_clientdata(client, nct3018y); + nct3018y->client = client; + device_set_wakeup_capable(&client->dev, 1); + + flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CTRL); + if (flags < 0) { + dev_dbg(&client->dev, "%s: read error\n", __func__); + return flags; + } else if (flags & NCT3018Y_BIT_TWO) { + dev_dbg(&client->dev, "%s: NCT3018Y_BIT_TWO is set\n", __func__); + } + + nct3018y->part_num = i2c_smbus_read_byte_data(client, NCT3018Y_REG_PART); + if (nct3018y->part_num < 0) { + dev_dbg(&client->dev, "Failed to read NCT3018Y_REG_PART.\n"); + return nct3018y->part_num; + } else { + nct3018y->part_num &= 0x03; /* Part number is corresponding to bit 0 and 1 */ + if (nct3018y->part_num == NCT3018Y_REG_PART_NCT3018Y) { + flags = NCT3018Y_BIT_HF; + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_CTRL, flags); + if (err < 0) { + dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_CTRL.\n"); + return err; + } + } + } + + flags = 0; + err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_ST, flags); + if (err < 0) { + dev_dbg(&client->dev, "%s: write error\n", __func__); + return err; + } + + nct3018y->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(nct3018y->rtc)) + return PTR_ERR(nct3018y->rtc); + + nct3018y->rtc->ops = &nct3018y_rtc_ops; + nct3018y->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + nct3018y->rtc->range_max = RTC_TIMESTAMP_END_2099; + + if (client->irq > 0) { + err = devm_request_threaded_irq(&client->dev, client->irq, + NULL, nct3018y_irq, + IRQF_ONESHOT | IRQF_TRIGGER_FALLING, + "nct3018y", client); + if (err) { + dev_dbg(&client->dev, "unable to request IRQ %d\n", client->irq); + return err; + } + } else { + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, nct3018y->rtc->features); + clear_bit(RTC_FEATURE_ALARM, nct3018y->rtc->features); + } + +#ifdef CONFIG_COMMON_CLK + /* register clk in common clk framework */ + nct3018y_clkout_register_clk(nct3018y); +#endif + + return devm_rtc_register_device(nct3018y->rtc); +} + +static const struct i2c_device_id nct3018y_id[] = { + { .name = "nct3018y" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, nct3018y_id); + +static const struct of_device_id nct3018y_of_match[] = { + { .compatible = "nuvoton,nct3018y" }, + {} +}; +MODULE_DEVICE_TABLE(of, nct3018y_of_match); + +static struct i2c_driver nct3018y_driver = { + .driver = { + .name = "rtc-nct3018y", + .of_match_table = nct3018y_of_match, + }, + .probe = nct3018y_probe, + .id_table = nct3018y_id, +}; + +module_i2c_driver(nct3018y_driver); + +MODULE_AUTHOR("Medad CChien "); +MODULE_AUTHOR("Mia Lin "); +MODULE_DESCRIPTION("Nuvoton NCT3018Y RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-nct6694.c b/drivers/rtc/rtc-nct6694.c new file mode 100644 index 000000000..35401a0d9 --- /dev/null +++ b/drivers/rtc/rtc-nct6694.c @@ -0,0 +1,297 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Nuvoton NCT6694 RTC driver based on USB interface. + * + * Copyright (C) 2025 Nuvoton Technology Corp. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * USB command module type for NCT6694 RTC controller. + * This defines the module type used for communication with the NCT6694 + * RTC controller over the USB interface. + */ +#define NCT6694_RTC_MOD 0x08 + +/* Command 00h - RTC Time */ +#define NCT6694_RTC_TIME 0x0000 +#define NCT6694_RTC_TIME_SEL 0x00 + +/* Command 01h - RTC Alarm */ +#define NCT6694_RTC_ALARM 0x01 +#define NCT6694_RTC_ALARM_SEL 0x00 + +/* Command 02h - RTC Status */ +#define NCT6694_RTC_STATUS 0x02 +#define NCT6694_RTC_STATUS_SEL 0x00 + +#define NCT6694_RTC_IRQ_INT_EN BIT(0) /* Transmit a USB INT-in when RTC alarm */ +#define NCT6694_RTC_IRQ_GPO_EN BIT(5) /* Trigger a GPO Low Pulse when RTC alarm */ + +#define NCT6694_RTC_IRQ_EN (NCT6694_RTC_IRQ_INT_EN | NCT6694_RTC_IRQ_GPO_EN) +#define NCT6694_RTC_IRQ_STS BIT(0) /* Write 1 clear IRQ status */ + +struct __packed nct6694_rtc_time { + u8 sec; + u8 min; + u8 hour; + u8 week; + u8 day; + u8 month; + u8 year; +}; + +struct __packed nct6694_rtc_alarm { + u8 sec; + u8 min; + u8 hour; + u8 alarm_en; + u8 alarm_pend; +}; + +struct __packed nct6694_rtc_status { + u8 irq_en; + u8 irq_pend; +}; + +union __packed nct6694_rtc_msg { + struct nct6694_rtc_time time; + struct nct6694_rtc_alarm alarm; + struct nct6694_rtc_status sts; +}; + +struct nct6694_rtc_data { + struct nct6694 *nct6694; + struct rtc_device *rtc; + union nct6694_rtc_msg *msg; + int irq; +}; + +static int nct6694_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct nct6694_rtc_data *data = dev_get_drvdata(dev); + struct nct6694_rtc_time *time = &data->msg->time; + static const struct nct6694_cmd_header cmd_hd = { + .mod = NCT6694_RTC_MOD, + .cmd = NCT6694_RTC_TIME, + .sel = NCT6694_RTC_TIME_SEL, + .len = cpu_to_le16(sizeof(*time)) + }; + int ret; + + ret = nct6694_read_msg(data->nct6694, &cmd_hd, time); + if (ret) + return ret; + + tm->tm_sec = bcd2bin(time->sec); /* tm_sec expect 0 ~ 59 */ + tm->tm_min = bcd2bin(time->min); /* tm_min expect 0 ~ 59 */ + tm->tm_hour = bcd2bin(time->hour); /* tm_hour expect 0 ~ 23 */ + tm->tm_wday = bcd2bin(time->week) - 1; /* tm_wday expect 0 ~ 6 */ + tm->tm_mday = bcd2bin(time->day); /* tm_mday expect 1 ~ 31 */ + tm->tm_mon = bcd2bin(time->month) - 1; /* tm_month expect 0 ~ 11 */ + tm->tm_year = bcd2bin(time->year) + 100; /* tm_year expect since 1900 */ + + return ret; +} + +static int nct6694_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct nct6694_rtc_data *data = dev_get_drvdata(dev); + struct nct6694_rtc_time *time = &data->msg->time; + static const struct nct6694_cmd_header cmd_hd = { + .mod = NCT6694_RTC_MOD, + .cmd = NCT6694_RTC_TIME, + .sel = NCT6694_RTC_TIME_SEL, + .len = cpu_to_le16(sizeof(*time)) + }; + + time->sec = bin2bcd(tm->tm_sec); + time->min = bin2bcd(tm->tm_min); + time->hour = bin2bcd(tm->tm_hour); + time->week = bin2bcd(tm->tm_wday + 1); + time->day = bin2bcd(tm->tm_mday); + time->month = bin2bcd(tm->tm_mon + 1); + time->year = bin2bcd(tm->tm_year - 100); + + return nct6694_write_msg(data->nct6694, &cmd_hd, time); +} + +static int nct6694_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct nct6694_rtc_data *data = dev_get_drvdata(dev); + struct nct6694_rtc_alarm *alarm = &data->msg->alarm; + static const struct nct6694_cmd_header cmd_hd = { + .mod = NCT6694_RTC_MOD, + .cmd = NCT6694_RTC_ALARM, + .sel = NCT6694_RTC_ALARM_SEL, + .len = cpu_to_le16(sizeof(*alarm)) + }; + int ret; + + ret = nct6694_read_msg(data->nct6694, &cmd_hd, alarm); + if (ret) + return ret; + + alrm->time.tm_sec = bcd2bin(alarm->sec); + alrm->time.tm_min = bcd2bin(alarm->min); + alrm->time.tm_hour = bcd2bin(alarm->hour); + alrm->enabled = alarm->alarm_en; + alrm->pending = alarm->alarm_pend; + + return ret; +} + +static int nct6694_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct nct6694_rtc_data *data = dev_get_drvdata(dev); + struct nct6694_rtc_alarm *alarm = &data->msg->alarm; + static const struct nct6694_cmd_header cmd_hd = { + .mod = NCT6694_RTC_MOD, + .cmd = NCT6694_RTC_ALARM, + .sel = NCT6694_RTC_ALARM_SEL, + .len = cpu_to_le16(sizeof(*alarm)) + }; + + alarm->sec = bin2bcd(alrm->time.tm_sec); + alarm->min = bin2bcd(alrm->time.tm_min); + alarm->hour = bin2bcd(alrm->time.tm_hour); + alarm->alarm_en = alrm->enabled ? NCT6694_RTC_IRQ_EN : 0; + alarm->alarm_pend = 0; + + return nct6694_write_msg(data->nct6694, &cmd_hd, alarm); +} + +static int nct6694_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct nct6694_rtc_data *data = dev_get_drvdata(dev); + struct nct6694_rtc_status *sts = &data->msg->sts; + static const struct nct6694_cmd_header cmd_hd = { + .mod = NCT6694_RTC_MOD, + .cmd = NCT6694_RTC_STATUS, + .sel = NCT6694_RTC_STATUS_SEL, + .len = cpu_to_le16(sizeof(*sts)) + }; + + if (enabled) + sts->irq_en |= NCT6694_RTC_IRQ_EN; + else + sts->irq_en &= ~NCT6694_RTC_IRQ_EN; + + sts->irq_pend = 0; + + return nct6694_write_msg(data->nct6694, &cmd_hd, sts); +} + +static const struct rtc_class_ops nct6694_rtc_ops = { + .read_time = nct6694_rtc_read_time, + .set_time = nct6694_rtc_set_time, + .read_alarm = nct6694_rtc_read_alarm, + .set_alarm = nct6694_rtc_set_alarm, + .alarm_irq_enable = nct6694_rtc_alarm_irq_enable, +}; + +static irqreturn_t nct6694_irq(int irq, void *dev_id) +{ + struct nct6694_rtc_data *data = dev_id; + struct nct6694_rtc_status *sts = &data->msg->sts; + static const struct nct6694_cmd_header cmd_hd = { + .mod = NCT6694_RTC_MOD, + .cmd = NCT6694_RTC_STATUS, + .sel = NCT6694_RTC_STATUS_SEL, + .len = cpu_to_le16(sizeof(*sts)) + }; + int ret; + + rtc_lock(data->rtc); + + sts->irq_en = NCT6694_RTC_IRQ_EN; + sts->irq_pend = NCT6694_RTC_IRQ_STS; + ret = nct6694_write_msg(data->nct6694, &cmd_hd, sts); + if (ret) { + rtc_unlock(data->rtc); + return IRQ_NONE; + } + + rtc_update_irq(data->rtc, 1, RTC_IRQF | RTC_AF); + + rtc_unlock(data->rtc); + + return IRQ_HANDLED; +} + +static void nct6694_irq_dispose_mapping(void *d) +{ + struct nct6694_rtc_data *data = d; + + irq_dispose_mapping(data->irq); +} + +static int nct6694_rtc_probe(struct platform_device *pdev) +{ + struct nct6694_rtc_data *data; + struct nct6694 *nct6694 = dev_get_drvdata(pdev->dev.parent); + int ret; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->msg = devm_kzalloc(&pdev->dev, sizeof(union nct6694_rtc_msg), + GFP_KERNEL); + if (!data->msg) + return -ENOMEM; + + data->irq = irq_create_mapping(nct6694->domain, NCT6694_IRQ_RTC); + if (!data->irq) + return -EINVAL; + + ret = devm_add_action_or_reset(&pdev->dev, nct6694_irq_dispose_mapping, + data); + if (ret) + return ret; + + ret = devm_device_init_wakeup(&pdev->dev); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to init wakeup\n"); + + data->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(data->rtc)) + return PTR_ERR(data->rtc); + + data->nct6694 = nct6694; + data->rtc->ops = &nct6694_rtc_ops; + data->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + data->rtc->range_max = RTC_TIMESTAMP_END_2099; + + platform_set_drvdata(pdev, data); + + ret = devm_request_threaded_irq(&pdev->dev, data->irq, NULL, + nct6694_irq, IRQF_ONESHOT, + "rtc-nct6694", data); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "Failed to request irq\n"); + + return devm_rtc_register_device(data->rtc); +} + +static struct platform_driver nct6694_rtc_driver = { + .driver = { + .name = "nct6694-rtc", + }, + .probe = nct6694_rtc_probe, +}; + +module_platform_driver(nct6694_rtc_driver); + +MODULE_DESCRIPTION("USB-RTC driver for NCT6694"); +MODULE_AUTHOR("Ming Yu "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:nct6694-rtc"); diff --git a/drivers/rtc/rtc-ntxec.c b/drivers/rtc/rtc-ntxec.c new file mode 100644 index 000000000..d28ddb34e --- /dev/null +++ b/drivers/rtc/rtc-ntxec.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * The Netronix embedded controller is a microcontroller found in some + * e-book readers designed by the original design manufacturer Netronix, Inc. + * It contains RTC, battery monitoring, system power management, and PWM + * functionality. + * + * This driver implements access to the RTC time and date. + * + * Copyright 2020 Jonathan Neuschäfer + */ + +#include +#include +#include +#include +#include +#include + +struct ntxec_rtc { + struct device *dev; + struct ntxec *ec; +}; + +#define NTXEC_REG_WRITE_YEAR 0x10 +#define NTXEC_REG_WRITE_MONTH 0x11 +#define NTXEC_REG_WRITE_DAY 0x12 +#define NTXEC_REG_WRITE_HOUR 0x13 +#define NTXEC_REG_WRITE_MINUTE 0x14 +#define NTXEC_REG_WRITE_SECOND 0x15 + +#define NTXEC_REG_READ_YEAR_MONTH 0x20 +#define NTXEC_REG_READ_MDAY_HOUR 0x21 +#define NTXEC_REG_READ_MINUTE_SECOND 0x23 + +static int ntxec_read_time(struct device *dev, struct rtc_time *tm) +{ + struct ntxec_rtc *rtc = dev_get_drvdata(dev); + unsigned int value; + int res; + +retry: + res = regmap_read(rtc->ec->regmap, NTXEC_REG_READ_MINUTE_SECOND, &value); + if (res < 0) + return res; + + tm->tm_min = value >> 8; + tm->tm_sec = value & 0xff; + + res = regmap_read(rtc->ec->regmap, NTXEC_REG_READ_MDAY_HOUR, &value); + if (res < 0) + return res; + + tm->tm_mday = value >> 8; + tm->tm_hour = value & 0xff; + + res = regmap_read(rtc->ec->regmap, NTXEC_REG_READ_YEAR_MONTH, &value); + if (res < 0) + return res; + + tm->tm_year = (value >> 8) + 100; + tm->tm_mon = (value & 0xff) - 1; + + /* + * Read the minutes/seconds field again. If it changed since the first + * read, we can't assume that the values read so far are consistent, + * and should start from the beginning. + */ + res = regmap_read(rtc->ec->regmap, NTXEC_REG_READ_MINUTE_SECOND, &value); + if (res < 0) + return res; + + if (tm->tm_min != value >> 8 || tm->tm_sec != (value & 0xff)) + goto retry; + + return 0; +} + +static int ntxec_set_time(struct device *dev, struct rtc_time *tm) +{ + struct ntxec_rtc *rtc = dev_get_drvdata(dev); + + /* + * To avoid time overflows while we're writing the full date/time, + * set the seconds field to zero before doing anything else. For the + * next 59 seconds (plus however long it takes until the RTC's next + * update of the second field), the seconds field will not overflow + * into the other fields. + */ + struct reg_sequence regs[] = { + { NTXEC_REG_WRITE_SECOND, ntxec_reg8(0) }, + { NTXEC_REG_WRITE_YEAR, ntxec_reg8(tm->tm_year - 100) }, + { NTXEC_REG_WRITE_MONTH, ntxec_reg8(tm->tm_mon + 1) }, + { NTXEC_REG_WRITE_DAY, ntxec_reg8(tm->tm_mday) }, + { NTXEC_REG_WRITE_HOUR, ntxec_reg8(tm->tm_hour) }, + { NTXEC_REG_WRITE_MINUTE, ntxec_reg8(tm->tm_min) }, + { NTXEC_REG_WRITE_SECOND, ntxec_reg8(tm->tm_sec) }, + }; + + return regmap_multi_reg_write(rtc->ec->regmap, regs, ARRAY_SIZE(regs)); +} + +static const struct rtc_class_ops ntxec_rtc_ops = { + .read_time = ntxec_read_time, + .set_time = ntxec_set_time, +}; + +static int ntxec_rtc_probe(struct platform_device *pdev) +{ + struct rtc_device *dev; + struct ntxec_rtc *rtc; + + device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent); + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->dev = &pdev->dev; + rtc->ec = dev_get_drvdata(pdev->dev.parent); + platform_set_drvdata(pdev, rtc); + + dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(dev)) + return PTR_ERR(dev); + + dev->ops = &ntxec_rtc_ops; + dev->range_min = RTC_TIMESTAMP_BEGIN_2000; + dev->range_max = 9025257599LL; /* 2255-12-31 23:59:59 */ + + return devm_rtc_register_device(dev); +} + +static struct platform_driver ntxec_rtc_driver = { + .driver = { + .name = "ntxec-rtc", + }, + .probe = ntxec_rtc_probe, +}; +module_platform_driver(ntxec_rtc_driver); + +MODULE_AUTHOR("Jonathan Neuschäfer "); +MODULE_DESCRIPTION("RTC driver for Netronix EC"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ntxec-rtc"); diff --git a/drivers/rtc/rtc-nvidia-vrs10.c b/drivers/rtc/rtc-nvidia-vrs10.c new file mode 100644 index 000000000..b15796698 --- /dev/null +++ b/drivers/rtc/rtc-nvidia-vrs10.c @@ -0,0 +1,542 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NVIDIA Voltage Regulator Specification RTC + * + * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. + * All rights reserved. + */ + +#include +#include +#include +#include +#include +#include + +#define NVVRS_REG_VENDOR_ID 0x00 +#define NVVRS_REG_MODEL_REV 0x01 + +/* Interrupts registers */ +#define NVVRS_REG_INT_SRC1 0x10 +#define NVVRS_REG_INT_SRC2 0x11 +#define NVVRS_REG_INT_VENDOR 0x12 + +/* Control Registers */ +#define NVVRS_REG_CTL_1 0x28 +#define NVVRS_REG_CTL_2 0x29 + +/* RTC Registers */ +#define NVVRS_REG_RTC_T3 0x70 +#define NVVRS_REG_RTC_T2 0x71 +#define NVVRS_REG_RTC_T1 0x72 +#define NVVRS_REG_RTC_T0 0x73 +#define NVVRS_REG_RTC_A3 0x74 +#define NVVRS_REG_RTC_A2 0x75 +#define NVVRS_REG_RTC_A1 0x76 +#define NVVRS_REG_RTC_A0 0x77 + +/* Interrupt Mask */ +#define NVVRS_INT_SRC1_RSTIRQ_MASK BIT(0) +#define NVVRS_INT_SRC1_OSC_MASK BIT(1) +#define NVVRS_INT_SRC1_EN_MASK BIT(2) +#define NVVRS_INT_SRC1_RTC_MASK BIT(3) +#define NVVRS_INT_SRC1_PEC_MASK BIT(4) +#define NVVRS_INT_SRC1_WDT_MASK BIT(5) +#define NVVRS_INT_SRC1_EM_PD_MASK BIT(6) +#define NVVRS_INT_SRC1_INTERNAL_MASK BIT(7) +#define NVVRS_INT_SRC2_PBSP_MASK BIT(0) +#define NVVRS_INT_SRC2_ECC_DED_MASK BIT(1) +#define NVVRS_INT_SRC2_TSD_MASK BIT(2) +#define NVVRS_INT_SRC2_LDO_MASK BIT(3) +#define NVVRS_INT_SRC2_BIST_MASK BIT(4) +#define NVVRS_INT_SRC2_RT_CRC_MASK BIT(5) +#define NVVRS_INT_SRC2_VENDOR_MASK BIT(7) +#define NVVRS_INT_VENDOR0_MASK BIT(0) +#define NVVRS_INT_VENDOR1_MASK BIT(1) +#define NVVRS_INT_VENDOR2_MASK BIT(2) +#define NVVRS_INT_VENDOR3_MASK BIT(3) +#define NVVRS_INT_VENDOR4_MASK BIT(4) +#define NVVRS_INT_VENDOR5_MASK BIT(5) +#define NVVRS_INT_VENDOR6_MASK BIT(6) +#define NVVRS_INT_VENDOR7_MASK BIT(7) + +/* Controller Register Mask */ +#define NVVRS_REG_CTL_1_FORCE_SHDN (BIT(0) | BIT(1)) +#define NVVRS_REG_CTL_1_FORCE_ACT BIT(2) +#define NVVRS_REG_CTL_1_FORCE_INT BIT(3) +#define NVVRS_REG_CTL_2_EN_PEC BIT(0) +#define NVVRS_REG_CTL_2_REQ_PEC BIT(1) +#define NVVRS_REG_CTL_2_RTC_PU BIT(2) +#define NVVRS_REG_CTL_2_RTC_WAKE BIT(3) +#define NVVRS_REG_CTL_2_RST_DLY 0xF0 + +#define ALARM_RESET_VAL 0xffffffff +#define NVVRS_MIN_MODEL_REV 0x40 + +enum nvvrs_irq_regs { + NVVRS_IRQ_REG_INT_SRC1 = 0, + NVVRS_IRQ_REG_INT_SRC2 = 1, + NVVRS_IRQ_REG_INT_VENDOR = 2, + NVVRS_IRQ_REG_COUNT = 3, +}; + +struct nvvrs_rtc_info { + struct device *dev; + struct i2c_client *client; + struct rtc_device *rtc; + unsigned int irq; +}; + +static int nvvrs_update_bits(struct nvvrs_rtc_info *info, u8 reg, + u8 mask, u8 value) +{ + int ret; + u8 val; + + ret = i2c_smbus_read_byte_data(info->client, reg); + if (ret < 0) + return ret; + + val = (u8)ret; + val &= ~mask; + val |= (value & mask); + + return i2c_smbus_write_byte_data(info->client, reg, val); +} + +static int nvvrs_rtc_write_alarm(struct i2c_client *client, u8 *time) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, NVVRS_REG_RTC_A3, time[3]); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, NVVRS_REG_RTC_A2, time[2]); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, NVVRS_REG_RTC_A1, time[1]); + if (ret < 0) + return ret; + + return i2c_smbus_write_byte_data(client, NVVRS_REG_RTC_A0, time[0]); +} + +static int nvvrs_rtc_enable_alarm(struct nvvrs_rtc_info *info) +{ + int ret; + + /* Set RTC_WAKE bit for autonomous wake from sleep */ + ret = nvvrs_update_bits(info, NVVRS_REG_CTL_2, NVVRS_REG_CTL_2_RTC_WAKE, + NVVRS_REG_CTL_2_RTC_WAKE); + if (ret < 0) + return ret; + + /* Set RTC_PU bit for autonomous wake from shutdown */ + ret = nvvrs_update_bits(info, NVVRS_REG_CTL_2, NVVRS_REG_CTL_2_RTC_PU, + NVVRS_REG_CTL_2_RTC_PU); + if (ret < 0) + return ret; + + return 0; +} + +static int nvvrs_rtc_disable_alarm(struct nvvrs_rtc_info *info) +{ + struct i2c_client *client = info->client; + u8 val[4]; + int ret; + + /* Clear RTC_WAKE bit */ + ret = nvvrs_update_bits(info, NVVRS_REG_CTL_2, NVVRS_REG_CTL_2_RTC_WAKE, + 0); + if (ret < 0) + return ret; + + /* Clear RTC_PU bit */ + ret = nvvrs_update_bits(info, NVVRS_REG_CTL_2, NVVRS_REG_CTL_2_RTC_PU, + 0); + if (ret < 0) + return ret; + + /* Write ALARM_RESET_VAL in RTC Alarm register to disable alarm */ + val[0] = 0xff; + val[1] = 0xff; + val[2] = 0xff; + val[3] = 0xff; + + ret = nvvrs_rtc_write_alarm(client, val); + if (ret < 0) + return ret; + + return 0; +} + +static int nvvrs_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct nvvrs_rtc_info *info = dev_get_drvdata(dev); + time64_t secs = 0; + int ret; + u8 val; + + /* + * Multi-byte transfers are not supported with PEC enabled + * Read MSB first to avoid coherency issues + */ + ret = i2c_smbus_read_byte_data(info->client, NVVRS_REG_RTC_T3); + if (ret < 0) + return ret; + + val = (u8)ret; + secs |= (time64_t)val << 24; + + ret = i2c_smbus_read_byte_data(info->client, NVVRS_REG_RTC_T2); + if (ret < 0) + return ret; + + val = (u8)ret; + secs |= (time64_t)val << 16; + + ret = i2c_smbus_read_byte_data(info->client, NVVRS_REG_RTC_T1); + if (ret < 0) + return ret; + + val = (u8)ret; + secs |= (time64_t)val << 8; + + ret = i2c_smbus_read_byte_data(info->client, NVVRS_REG_RTC_T0); + if (ret < 0) + return ret; + + val = (u8)ret; + secs |= val; + + rtc_time64_to_tm(secs, tm); + + return 0; +} + +static int nvvrs_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct nvvrs_rtc_info *info = dev_get_drvdata(dev); + time64_t secs; + u8 time[4]; + int ret; + + secs = rtc_tm_to_time64(tm); + time[0] = secs & 0xff; + time[1] = (secs >> 8) & 0xff; + time[2] = (secs >> 16) & 0xff; + time[3] = (secs >> 24) & 0xff; + + ret = i2c_smbus_write_byte_data(info->client, NVVRS_REG_RTC_T3, time[3]); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(info->client, NVVRS_REG_RTC_T2, time[2]); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(info->client, NVVRS_REG_RTC_T1, time[1]); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(info->client, NVVRS_REG_RTC_T0, time[0]); + + return ret; +} + +static int nvvrs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct nvvrs_rtc_info *info = dev_get_drvdata(dev); + time64_t alarm_val = 0; + int ret; + u8 val; + + /* Multi-byte transfers are not supported with PEC enabled */ + ret = i2c_smbus_read_byte_data(info->client, NVVRS_REG_RTC_A3); + if (ret < 0) + return ret; + + val = (u8)ret; + alarm_val |= (time64_t)val << 24; + + ret = i2c_smbus_read_byte_data(info->client, NVVRS_REG_RTC_A2); + if (ret < 0) + return ret; + + val = (u8)ret; + alarm_val |= (time64_t)val << 16; + + ret = i2c_smbus_read_byte_data(info->client, NVVRS_REG_RTC_A1); + if (ret < 0) + return ret; + + val = (u8)ret; + alarm_val |= (time64_t)val << 8; + + ret = i2c_smbus_read_byte_data(info->client, NVVRS_REG_RTC_A0); + if (ret < 0) + return ret; + + val = (u8)ret; + alarm_val |= val; + + if (alarm_val == ALARM_RESET_VAL) + alrm->enabled = 0; + else + alrm->enabled = 1; + + rtc_time64_to_tm(alarm_val, &alrm->time); + + return 0; +} + +static int nvvrs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct nvvrs_rtc_info *info = dev_get_drvdata(dev); + time64_t secs; + u8 time[4]; + int ret; + + if (!alrm->enabled) { + ret = nvvrs_rtc_disable_alarm(info); + if (ret < 0) + return ret; + } + + ret = nvvrs_rtc_enable_alarm(info); + if (ret < 0) + return ret; + + secs = rtc_tm_to_time64(&alrm->time); + time[0] = secs & 0xff; + time[1] = (secs >> 8) & 0xff; + time[2] = (secs >> 16) & 0xff; + time[3] = (secs >> 24) & 0xff; + + ret = nvvrs_rtc_write_alarm(info->client, time); + + return ret; +} + +static int nvvrs_pseq_irq_clear(struct nvvrs_rtc_info *info) +{ + unsigned int i; + int ret; + + for (i = 0; i < NVVRS_IRQ_REG_COUNT; i++) { + ret = i2c_smbus_read_byte_data(info->client, + NVVRS_REG_INT_SRC1 + i); + if (ret < 0) { + dev_err(info->dev, "Failed to read INT_SRC%d : %d\n", + i + 1, ret); + return ret; + } + + ret = i2c_smbus_write_byte_data(info->client, + NVVRS_REG_INT_SRC1 + i, + (u8)ret); + if (ret < 0) { + dev_err(info->dev, "Failed to clear INT_SRC%d : %d\n", + i + 1, ret); + return ret; + } + } + + return 0; +} + +static irqreturn_t nvvrs_rtc_irq_handler(int irq, void *data) +{ + struct nvvrs_rtc_info *info = data; + int ret; + + /* Check for RTC alarm interrupt */ + ret = i2c_smbus_read_byte_data(info->client, NVVRS_REG_INT_SRC1); + if (ret < 0) + return IRQ_NONE; + + if (ret & NVVRS_INT_SRC1_RTC_MASK) { + rtc_lock(info->rtc); + rtc_update_irq(info->rtc, 1, RTC_IRQF | RTC_AF); + rtc_unlock(info->rtc); + } + + /* Clear all interrupts */ + if (nvvrs_pseq_irq_clear(info) < 0) + return IRQ_NONE; + + return IRQ_HANDLED; +} + +static int nvvrs_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + /* + * This hardware does not support enabling/disabling the alarm IRQ + * independently. The alarm is disabled by clearing the alarm time + * via set_alarm(). + */ + return 0; +} + +static const struct rtc_class_ops nvvrs_rtc_ops = { + .read_time = nvvrs_rtc_read_time, + .set_time = nvvrs_rtc_set_time, + .read_alarm = nvvrs_rtc_read_alarm, + .set_alarm = nvvrs_rtc_set_alarm, + .alarm_irq_enable = nvvrs_rtc_alarm_irq_enable, +}; + +static int nvvrs_pseq_vendor_info(struct nvvrs_rtc_info *info) +{ + struct i2c_client *client = info->client; + u8 vendor_id, model_rev; + int ret; + + ret = i2c_smbus_read_byte_data(client, NVVRS_REG_VENDOR_ID); + if (ret < 0) + return dev_err_probe(&client->dev, ret, + "Failed to read Vendor ID\n"); + + vendor_id = (u8)ret; + + ret = i2c_smbus_read_byte_data(client, NVVRS_REG_MODEL_REV); + if (ret < 0) + return dev_err_probe(&client->dev, ret, + "Failed to read Model Revision\n"); + + model_rev = (u8)ret; + + if (model_rev < NVVRS_MIN_MODEL_REV) { + return dev_err_probe(&client->dev, -ENODEV, + "Chip revision 0x%02x is not supported!\n", + model_rev); + } + + dev_dbg(&client->dev, "NVVRS Vendor ID: 0x%02x, Model Rev: 0x%02x\n", + vendor_id, model_rev); + + return 0; +} + +static int nvvrs_rtc_probe(struct i2c_client *client) +{ + struct nvvrs_rtc_info *info; + int ret; + + info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + if (client->irq <= 0) + return dev_err_probe(&client->dev, -EINVAL, "No IRQ specified\n"); + + info->irq = client->irq; + info->dev = &client->dev; + client->flags |= I2C_CLIENT_PEC; + i2c_set_clientdata(client, info); + info->client = client; + + /* Check vendor info */ + if (nvvrs_pseq_vendor_info(info) < 0) + return dev_err_probe(&client->dev, -EINVAL, + "Failed to get vendor info\n"); + + /* Clear any pending IRQs before requesting IRQ handler */ + if (nvvrs_pseq_irq_clear(info) < 0) + return dev_err_probe(&client->dev, -EINVAL, + "Failed to clear interrupts\n"); + + /* Allocate RTC device */ + info->rtc = devm_rtc_allocate_device(info->dev); + if (IS_ERR(info->rtc)) + return PTR_ERR(info->rtc); + + info->rtc->ops = &nvvrs_rtc_ops; + info->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + info->rtc->range_max = RTC_TIMESTAMP_END_2099; + + /* Request RTC IRQ */ + ret = devm_request_threaded_irq(info->dev, info->irq, NULL, + nvvrs_rtc_irq_handler, IRQF_ONESHOT, + "nvvrs-rtc", info); + if (ret < 0) { + dev_err_probe(info->dev, ret, "Failed to request RTC IRQ\n"); + return ret; + } + + /* RTC as a wakeup source */ + devm_device_init_wakeup(info->dev); + + return devm_rtc_register_device(info->rtc); +} + +#ifdef CONFIG_PM_SLEEP +static int nvvrs_rtc_suspend(struct device *dev) +{ + struct nvvrs_rtc_info *info = dev_get_drvdata(dev); + int ret; + + if (device_may_wakeup(dev)) { + /* Set RTC_WAKE bit for auto wake system from suspend state */ + ret = nvvrs_update_bits(info, NVVRS_REG_CTL_2, + NVVRS_REG_CTL_2_RTC_WAKE, + NVVRS_REG_CTL_2_RTC_WAKE); + if (ret < 0) { + dev_err(info->dev, "Failed to set RTC_WAKE bit (%d)\n", + ret); + return ret; + } + + return enable_irq_wake(info->irq); + } + + return 0; +} + +static int nvvrs_rtc_resume(struct device *dev) +{ + struct nvvrs_rtc_info *info = dev_get_drvdata(dev); + int ret; + + if (device_may_wakeup(dev)) { + /* Clear FORCE_ACT bit */ + ret = nvvrs_update_bits(info, NVVRS_REG_CTL_1, + NVVRS_REG_CTL_1_FORCE_ACT, 0); + if (ret < 0) { + dev_err(info->dev, "Failed to clear FORCE_ACT bit (%d)\n", + ret); + return ret; + } + + return disable_irq_wake(info->irq); + } + + return 0; +} + +#endif +static SIMPLE_DEV_PM_OPS(nvvrs_rtc_pm_ops, nvvrs_rtc_suspend, nvvrs_rtc_resume); + +static const struct of_device_id nvvrs_rtc_of_match[] = { + { .compatible = "nvidia,vrs-10" }, + { }, +}; +MODULE_DEVICE_TABLE(of, nvvrs_rtc_of_match); + +static struct i2c_driver nvvrs_rtc_driver = { + .driver = { + .name = "rtc-nvidia-vrs10", + .pm = &nvvrs_rtc_pm_ops, + .of_match_table = nvvrs_rtc_of_match, + }, + .probe = nvvrs_rtc_probe, +}; + +module_i2c_driver(nvvrs_rtc_driver); + +MODULE_AUTHOR("Shubhi Garg "); +MODULE_DESCRIPTION("NVIDIA Voltage Regulator Specification RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-nxp-bbnsm.c b/drivers/rtc/rtc-nxp-bbnsm.c new file mode 100644 index 000000000..d4fc9dc58 --- /dev/null +++ b/drivers/rtc/rtc-nxp-bbnsm.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright 2022 NXP. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BBNSM_CTRL 0x8 +#define BBNSM_INT_EN 0x10 +#define BBNSM_EVENTS 0x14 +#define BBNSM_RTC_LS 0x40 +#define BBNSM_RTC_MS 0x44 +#define BBNSM_TA 0x50 + +#define RTC_EN 0x2 +#define RTC_EN_MSK 0x3 +#define TA_EN (0x2 << 2) +#define TA_DIS (0x1 << 2) +#define TA_EN_MSK (0x3 << 2) +#define RTC_INT_EN 0x2 +#define TA_INT_EN (0x2 << 2) + +#define BBNSM_EVENT_TA (0x2 << 2) + +#define CNTR_TO_SECS_SH 15 + +struct bbnsm_rtc { + struct rtc_device *rtc; + struct regmap *regmap; + int irq; + struct clk *clk; +}; + +static u32 bbnsm_read_counter(struct bbnsm_rtc *bbnsm) +{ + u32 rtc_msb, rtc_lsb; + unsigned int timeout = 100; + u32 time; + u32 tmp = 0; + + do { + time = tmp; + /* read the msb */ + regmap_read(bbnsm->regmap, BBNSM_RTC_MS, &rtc_msb); + /* read the lsb */ + regmap_read(bbnsm->regmap, BBNSM_RTC_LS, &rtc_lsb); + /* convert to seconds */ + tmp = (rtc_msb << 17) | (rtc_lsb >> 15); + } while (tmp != time && --timeout); + + return time; +} + +static int bbnsm_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct bbnsm_rtc *bbnsm = dev_get_drvdata(dev); + unsigned long time; + u32 val; + + regmap_read(bbnsm->regmap, BBNSM_CTRL, &val); + if ((val & RTC_EN_MSK) != RTC_EN) + return -EINVAL; + + time = bbnsm_read_counter(bbnsm); + rtc_time64_to_tm(time, tm); + + return 0; +} + +static int bbnsm_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct bbnsm_rtc *bbnsm = dev_get_drvdata(dev); + unsigned long time = rtc_tm_to_time64(tm); + + /* disable the RTC first */ + regmap_update_bits(bbnsm->regmap, BBNSM_CTRL, RTC_EN_MSK, 0); + + /* write the 32bit sec time to 47 bit timer counter, leaving 15 LSBs blank */ + regmap_write(bbnsm->regmap, BBNSM_RTC_LS, time << CNTR_TO_SECS_SH); + regmap_write(bbnsm->regmap, BBNSM_RTC_MS, time >> (32 - CNTR_TO_SECS_SH)); + + /* Enable the RTC again */ + regmap_update_bits(bbnsm->regmap, BBNSM_CTRL, RTC_EN_MSK, RTC_EN); + + return 0; +} + +static int bbnsm_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct bbnsm_rtc *bbnsm = dev_get_drvdata(dev); + u32 bbnsm_events, bbnsm_ta; + + regmap_read(bbnsm->regmap, BBNSM_TA, &bbnsm_ta); + rtc_time64_to_tm(bbnsm_ta, &alrm->time); + + regmap_read(bbnsm->regmap, BBNSM_EVENTS, &bbnsm_events); + alrm->pending = (bbnsm_events & BBNSM_EVENT_TA) ? 1 : 0; + + return 0; +} + +static int bbnsm_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) +{ + struct bbnsm_rtc *bbnsm = dev_get_drvdata(dev); + + /* enable the alarm event */ + regmap_update_bits(bbnsm->regmap, BBNSM_CTRL, TA_EN_MSK, enable ? TA_EN : TA_DIS); + /* enable the alarm interrupt */ + regmap_update_bits(bbnsm->regmap, BBNSM_INT_EN, TA_EN_MSK, enable ? TA_EN : TA_DIS); + + return 0; +} + +static int bbnsm_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct bbnsm_rtc *bbnsm = dev_get_drvdata(dev); + unsigned long time = rtc_tm_to_time64(&alrm->time); + + /* disable the alarm */ + regmap_update_bits(bbnsm->regmap, BBNSM_CTRL, TA_EN, TA_EN); + + /* write the seconds to TA */ + regmap_write(bbnsm->regmap, BBNSM_TA, time); + + return bbnsm_rtc_alarm_irq_enable(dev, alrm->enabled); +} + +static const struct rtc_class_ops bbnsm_rtc_ops = { + .read_time = bbnsm_rtc_read_time, + .set_time = bbnsm_rtc_set_time, + .read_alarm = bbnsm_rtc_read_alarm, + .set_alarm = bbnsm_rtc_set_alarm, + .alarm_irq_enable = bbnsm_rtc_alarm_irq_enable, +}; + +static irqreturn_t bbnsm_rtc_irq_handler(int irq, void *dev_id) +{ + struct device *dev = dev_id; + struct bbnsm_rtc *bbnsm = dev_get_drvdata(dev); + u32 val; + + regmap_read(bbnsm->regmap, BBNSM_EVENTS, &val); + if (val & BBNSM_EVENT_TA) { + bbnsm_rtc_alarm_irq_enable(dev, false); + /* clear the alarm event */ + regmap_write_bits(bbnsm->regmap, BBNSM_EVENTS, TA_EN_MSK, BBNSM_EVENT_TA); + rtc_update_irq(bbnsm->rtc, 1, RTC_AF | RTC_IRQF); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int bbnsm_rtc_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct bbnsm_rtc *bbnsm; + int ret; + + bbnsm = devm_kzalloc(&pdev->dev, sizeof(*bbnsm), GFP_KERNEL); + if (!bbnsm) + return -ENOMEM; + + bbnsm->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(bbnsm->rtc)) + return PTR_ERR(bbnsm->rtc); + + bbnsm->regmap = syscon_node_to_regmap(np->parent); + if (IS_ERR(bbnsm->regmap)) { + dev_dbg(&pdev->dev, "bbnsm get regmap failed\n"); + return PTR_ERR(bbnsm->regmap); + } + + bbnsm->irq = platform_get_irq(pdev, 0); + if (bbnsm->irq < 0) + return bbnsm->irq; + + platform_set_drvdata(pdev, bbnsm); + + /* clear all the pending events */ + regmap_write(bbnsm->regmap, BBNSM_EVENTS, 0x7A); + + ret = devm_device_init_wakeup(&pdev->dev); + if (ret) + dev_err(&pdev->dev, "failed to init wakeup, %d\n", ret); + + ret = devm_pm_set_wake_irq(&pdev->dev, bbnsm->irq); + if (ret) + dev_err(&pdev->dev, "failed to set wake irq, %d\n", ret); + + ret = devm_request_irq(&pdev->dev, bbnsm->irq, bbnsm_rtc_irq_handler, + IRQF_SHARED, "rtc alarm", &pdev->dev); + if (ret) { + dev_err(&pdev->dev, "failed to request irq %d: %d\n", + bbnsm->irq, ret); + return ret; + } + + bbnsm->rtc->ops = &bbnsm_rtc_ops; + bbnsm->rtc->range_max = U32_MAX; + + return devm_rtc_register_device(bbnsm->rtc); +} + +static const struct of_device_id bbnsm_dt_ids[] = { + { .compatible = "nxp,imx93-bbnsm-rtc" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, bbnsm_dt_ids); + +static struct platform_driver bbnsm_rtc_driver = { + .driver = { + .name = "bbnsm_rtc", + .of_match_table = bbnsm_dt_ids, + }, + .probe = bbnsm_rtc_probe, +}; +module_platform_driver(bbnsm_rtc_driver); + +MODULE_AUTHOR("Jacky Bai "); +MODULE_DESCRIPTION("NXP BBNSM RTC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c new file mode 100644 index 000000000..0f90065e3 --- /dev/null +++ b/drivers/rtc/rtc-omap.c @@ -0,0 +1,1031 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * TI OMAP Real Time Clock interface for Linux + * + * Copyright (C) 2003 MontaVista Software, Inc. + * Author: George G. Davis or + * + * Copyright (C) 2006 David Brownell (new RTC framework) + * Copyright (C) 2014 Johan Hovold + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The OMAP RTC is a year/month/day/hours/minutes/seconds BCD clock + * with century-range alarm matching, driven by the 32kHz clock. + * + * The main user-visible ways it differs from PC RTCs are by omitting + * "don't care" alarm fields and sub-second periodic IRQs, and having + * an autoadjust mechanism to calibrate to the true oscillator rate. + * + * Board-specific wiring options include using split power mode with + * RTC_OFF_NOFF used as the reset signal (so the RTC won't be reset), + * and wiring RTC_WAKE_INT (so the RTC alarm can wake the system from + * low power modes) for OMAP1 boards (OMAP-L138 has this built into + * the SoC). See the BOARD-SPECIFIC CUSTOMIZATION comment. + */ + +/* RTC registers */ +#define OMAP_RTC_SECONDS_REG 0x00 +#define OMAP_RTC_MINUTES_REG 0x04 +#define OMAP_RTC_HOURS_REG 0x08 +#define OMAP_RTC_DAYS_REG 0x0C +#define OMAP_RTC_MONTHS_REG 0x10 +#define OMAP_RTC_YEARS_REG 0x14 +#define OMAP_RTC_WEEKS_REG 0x18 + +#define OMAP_RTC_ALARM_SECONDS_REG 0x20 +#define OMAP_RTC_ALARM_MINUTES_REG 0x24 +#define OMAP_RTC_ALARM_HOURS_REG 0x28 +#define OMAP_RTC_ALARM_DAYS_REG 0x2c +#define OMAP_RTC_ALARM_MONTHS_REG 0x30 +#define OMAP_RTC_ALARM_YEARS_REG 0x34 + +#define OMAP_RTC_CTRL_REG 0x40 +#define OMAP_RTC_STATUS_REG 0x44 +#define OMAP_RTC_INTERRUPTS_REG 0x48 + +#define OMAP_RTC_COMP_LSB_REG 0x4c +#define OMAP_RTC_COMP_MSB_REG 0x50 +#define OMAP_RTC_OSC_REG 0x54 + +#define OMAP_RTC_SCRATCH0_REG 0x60 +#define OMAP_RTC_SCRATCH1_REG 0x64 +#define OMAP_RTC_SCRATCH2_REG 0x68 + +#define OMAP_RTC_KICK0_REG 0x6c +#define OMAP_RTC_KICK1_REG 0x70 + +#define OMAP_RTC_IRQWAKEEN 0x7c + +#define OMAP_RTC_ALARM2_SECONDS_REG 0x80 +#define OMAP_RTC_ALARM2_MINUTES_REG 0x84 +#define OMAP_RTC_ALARM2_HOURS_REG 0x88 +#define OMAP_RTC_ALARM2_DAYS_REG 0x8c +#define OMAP_RTC_ALARM2_MONTHS_REG 0x90 +#define OMAP_RTC_ALARM2_YEARS_REG 0x94 + +#define OMAP_RTC_PMIC_REG 0x98 + +/* OMAP_RTC_CTRL_REG bit fields: */ +#define OMAP_RTC_CTRL_SPLIT BIT(7) +#define OMAP_RTC_CTRL_DISABLE BIT(6) +#define OMAP_RTC_CTRL_SET_32_COUNTER BIT(5) +#define OMAP_RTC_CTRL_TEST BIT(4) +#define OMAP_RTC_CTRL_MODE_12_24 BIT(3) +#define OMAP_RTC_CTRL_AUTO_COMP BIT(2) +#define OMAP_RTC_CTRL_ROUND_30S BIT(1) +#define OMAP_RTC_CTRL_STOP BIT(0) + +/* OMAP_RTC_STATUS_REG bit fields: */ +#define OMAP_RTC_STATUS_POWER_UP BIT(7) +#define OMAP_RTC_STATUS_ALARM2 BIT(7) +#define OMAP_RTC_STATUS_ALARM BIT(6) +#define OMAP_RTC_STATUS_1D_EVENT BIT(5) +#define OMAP_RTC_STATUS_1H_EVENT BIT(4) +#define OMAP_RTC_STATUS_1M_EVENT BIT(3) +#define OMAP_RTC_STATUS_1S_EVENT BIT(2) +#define OMAP_RTC_STATUS_RUN BIT(1) +#define OMAP_RTC_STATUS_BUSY BIT(0) + +/* OMAP_RTC_INTERRUPTS_REG bit fields: */ +#define OMAP_RTC_INTERRUPTS_IT_ALARM2 BIT(4) +#define OMAP_RTC_INTERRUPTS_IT_ALARM BIT(3) +#define OMAP_RTC_INTERRUPTS_IT_TIMER BIT(2) + +/* OMAP_RTC_OSC_REG bit fields: */ +#define OMAP_RTC_OSC_32KCLK_EN BIT(6) +#define OMAP_RTC_OSC_SEL_32KCLK_SRC BIT(3) +#define OMAP_RTC_OSC_OSC32K_GZ_DISABLE BIT(4) + +/* OMAP_RTC_IRQWAKEEN bit fields: */ +#define OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN BIT(1) + +/* OMAP_RTC_PMIC bit fields: */ +#define OMAP_RTC_PMIC_POWER_EN_EN BIT(16) +#define OMAP_RTC_PMIC_EXT_WKUP_EN(x) BIT(x) +#define OMAP_RTC_PMIC_EXT_WKUP_POL(x) BIT(4 + x) + +/* OMAP_RTC_KICKER values */ +#define KICK0_VALUE 0x83e70b13 +#define KICK1_VALUE 0x95a4f1e0 + +struct omap_rtc; + +struct omap_rtc_device_type { + bool has_32kclk_en; + bool has_irqwakeen; + bool has_pmic_mode; + bool has_power_up_reset; + void (*lock)(struct omap_rtc *rtc); + void (*unlock)(struct omap_rtc *rtc); +}; + +struct omap_rtc { + struct rtc_device *rtc; + void __iomem *base; + struct clk *clk; + int irq_alarm; + int irq_timer; + u8 interrupts_reg; + bool is_pmic_controller; + bool has_ext_clk; + bool is_suspending; + const struct omap_rtc_device_type *type; + struct pinctrl_dev *pctldev; +}; + +static inline u8 rtc_read(struct omap_rtc *rtc, unsigned int reg) +{ + return readb(rtc->base + reg); +} + +static inline u32 rtc_readl(struct omap_rtc *rtc, unsigned int reg) +{ + return readl(rtc->base + reg); +} + +static inline void rtc_write(struct omap_rtc *rtc, unsigned int reg, u8 val) +{ + writeb(val, rtc->base + reg); +} + +static inline void rtc_writel(struct omap_rtc *rtc, unsigned int reg, u32 val) +{ + writel(val, rtc->base + reg); +} + +static void am3352_rtc_unlock(struct omap_rtc *rtc) +{ + rtc_writel(rtc, OMAP_RTC_KICK0_REG, KICK0_VALUE); + rtc_writel(rtc, OMAP_RTC_KICK1_REG, KICK1_VALUE); +} + +static void am3352_rtc_lock(struct omap_rtc *rtc) +{ + rtc_writel(rtc, OMAP_RTC_KICK0_REG, 0); + rtc_writel(rtc, OMAP_RTC_KICK1_REG, 0); +} + +static void default_rtc_unlock(struct omap_rtc *rtc) +{ +} + +static void default_rtc_lock(struct omap_rtc *rtc) +{ +} + +/* + * We rely on the rtc framework to handle locking (rtc->ops_lock), + * so the only other requirement is that register accesses which + * require BUSY to be clear are made with IRQs locally disabled + */ +static void rtc_wait_not_busy(struct omap_rtc *rtc) +{ + int count; + u8 status; + + /* BUSY may stay active for 1/32768 second (~30 usec) */ + for (count = 0; count < 50; count++) { + status = rtc_read(rtc, OMAP_RTC_STATUS_REG); + if (!(status & OMAP_RTC_STATUS_BUSY)) + break; + udelay(1); + } + /* now we have ~15 usec to read/write various registers */ +} + +static irqreturn_t rtc_irq(int irq, void *dev_id) +{ + struct omap_rtc *rtc = dev_id; + unsigned long events = 0; + u8 irq_data; + + irq_data = rtc_read(rtc, OMAP_RTC_STATUS_REG); + + /* alarm irq? */ + if (irq_data & OMAP_RTC_STATUS_ALARM) { + rtc->type->unlock(rtc); + rtc_write(rtc, OMAP_RTC_STATUS_REG, OMAP_RTC_STATUS_ALARM); + rtc->type->lock(rtc); + events |= RTC_IRQF | RTC_AF; + } + + /* 1/sec periodic/update irq? */ + if (irq_data & OMAP_RTC_STATUS_1S_EVENT) + events |= RTC_IRQF | RTC_UF; + + rtc_update_irq(rtc->rtc, 1, events); + + return IRQ_HANDLED; +} + +static int omap_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct omap_rtc *rtc = dev_get_drvdata(dev); + u8 reg, irqwake_reg = 0; + + local_irq_disable(); + rtc_wait_not_busy(rtc); + reg = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); + if (rtc->type->has_irqwakeen) + irqwake_reg = rtc_read(rtc, OMAP_RTC_IRQWAKEEN); + + if (enabled) { + reg |= OMAP_RTC_INTERRUPTS_IT_ALARM; + irqwake_reg |= OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN; + } else { + reg &= ~OMAP_RTC_INTERRUPTS_IT_ALARM; + irqwake_reg &= ~OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN; + } + rtc_wait_not_busy(rtc); + rtc->type->unlock(rtc); + rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, reg); + if (rtc->type->has_irqwakeen) + rtc_write(rtc, OMAP_RTC_IRQWAKEEN, irqwake_reg); + rtc->type->lock(rtc); + local_irq_enable(); + + return 0; +} + +/* this hardware doesn't support "don't care" alarm fields */ +static void tm2bcd(struct rtc_time *tm) +{ + tm->tm_sec = bin2bcd(tm->tm_sec); + tm->tm_min = bin2bcd(tm->tm_min); + tm->tm_hour = bin2bcd(tm->tm_hour); + tm->tm_mday = bin2bcd(tm->tm_mday); + + tm->tm_mon = bin2bcd(tm->tm_mon + 1); + tm->tm_year = bin2bcd(tm->tm_year - 100); +} + +static void bcd2tm(struct rtc_time *tm) +{ + tm->tm_sec = bcd2bin(tm->tm_sec); + tm->tm_min = bcd2bin(tm->tm_min); + tm->tm_hour = bcd2bin(tm->tm_hour); + tm->tm_mday = bcd2bin(tm->tm_mday); + tm->tm_mon = bcd2bin(tm->tm_mon) - 1; + /* epoch == 1900 */ + tm->tm_year = bcd2bin(tm->tm_year) + 100; +} + +static void omap_rtc_read_time_raw(struct omap_rtc *rtc, struct rtc_time *tm) +{ + tm->tm_sec = rtc_read(rtc, OMAP_RTC_SECONDS_REG); + tm->tm_min = rtc_read(rtc, OMAP_RTC_MINUTES_REG); + tm->tm_hour = rtc_read(rtc, OMAP_RTC_HOURS_REG); + tm->tm_mday = rtc_read(rtc, OMAP_RTC_DAYS_REG); + tm->tm_mon = rtc_read(rtc, OMAP_RTC_MONTHS_REG); + tm->tm_year = rtc_read(rtc, OMAP_RTC_YEARS_REG); +} + +static int omap_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct omap_rtc *rtc = dev_get_drvdata(dev); + + /* we don't report wday/yday/isdst ... */ + local_irq_disable(); + rtc_wait_not_busy(rtc); + omap_rtc_read_time_raw(rtc, tm); + local_irq_enable(); + + bcd2tm(tm); + + return 0; +} + +static int omap_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct omap_rtc *rtc = dev_get_drvdata(dev); + + tm2bcd(tm); + + local_irq_disable(); + rtc_wait_not_busy(rtc); + + rtc->type->unlock(rtc); + rtc_write(rtc, OMAP_RTC_YEARS_REG, tm->tm_year); + rtc_write(rtc, OMAP_RTC_MONTHS_REG, tm->tm_mon); + rtc_write(rtc, OMAP_RTC_DAYS_REG, tm->tm_mday); + rtc_write(rtc, OMAP_RTC_HOURS_REG, tm->tm_hour); + rtc_write(rtc, OMAP_RTC_MINUTES_REG, tm->tm_min); + rtc_write(rtc, OMAP_RTC_SECONDS_REG, tm->tm_sec); + rtc->type->lock(rtc); + + local_irq_enable(); + + return 0; +} + +static int omap_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct omap_rtc *rtc = dev_get_drvdata(dev); + u8 interrupts; + + local_irq_disable(); + rtc_wait_not_busy(rtc); + + alm->time.tm_sec = rtc_read(rtc, OMAP_RTC_ALARM_SECONDS_REG); + alm->time.tm_min = rtc_read(rtc, OMAP_RTC_ALARM_MINUTES_REG); + alm->time.tm_hour = rtc_read(rtc, OMAP_RTC_ALARM_HOURS_REG); + alm->time.tm_mday = rtc_read(rtc, OMAP_RTC_ALARM_DAYS_REG); + alm->time.tm_mon = rtc_read(rtc, OMAP_RTC_ALARM_MONTHS_REG); + alm->time.tm_year = rtc_read(rtc, OMAP_RTC_ALARM_YEARS_REG); + + local_irq_enable(); + + bcd2tm(&alm->time); + + interrupts = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); + alm->enabled = !!(interrupts & OMAP_RTC_INTERRUPTS_IT_ALARM); + + return 0; +} + +static int omap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct omap_rtc *rtc = dev_get_drvdata(dev); + u8 reg, irqwake_reg = 0; + + tm2bcd(&alm->time); + + local_irq_disable(); + rtc_wait_not_busy(rtc); + + rtc->type->unlock(rtc); + rtc_write(rtc, OMAP_RTC_ALARM_YEARS_REG, alm->time.tm_year); + rtc_write(rtc, OMAP_RTC_ALARM_MONTHS_REG, alm->time.tm_mon); + rtc_write(rtc, OMAP_RTC_ALARM_DAYS_REG, alm->time.tm_mday); + rtc_write(rtc, OMAP_RTC_ALARM_HOURS_REG, alm->time.tm_hour); + rtc_write(rtc, OMAP_RTC_ALARM_MINUTES_REG, alm->time.tm_min); + rtc_write(rtc, OMAP_RTC_ALARM_SECONDS_REG, alm->time.tm_sec); + + reg = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); + if (rtc->type->has_irqwakeen) + irqwake_reg = rtc_read(rtc, OMAP_RTC_IRQWAKEEN); + + if (alm->enabled) { + reg |= OMAP_RTC_INTERRUPTS_IT_ALARM; + irqwake_reg |= OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN; + } else { + reg &= ~OMAP_RTC_INTERRUPTS_IT_ALARM; + irqwake_reg &= ~OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN; + } + rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, reg); + if (rtc->type->has_irqwakeen) + rtc_write(rtc, OMAP_RTC_IRQWAKEEN, irqwake_reg); + rtc->type->lock(rtc); + + local_irq_enable(); + + return 0; +} + +static struct omap_rtc *omap_rtc_power_off_rtc; + +/** + * omap_rtc_power_off_program: Set the pmic power off sequence. The RTC + * generates pmic_pwr_enable control, which can be used to control an external + * PMIC. + */ +int omap_rtc_power_off_program(struct device *dev) +{ + struct omap_rtc *rtc = omap_rtc_power_off_rtc; + struct rtc_time tm; + unsigned long now; + int seconds; + u32 val; + + rtc->type->unlock(rtc); + /* enable pmic_power_en control */ + val = rtc_readl(rtc, OMAP_RTC_PMIC_REG); + rtc_writel(rtc, OMAP_RTC_PMIC_REG, val | OMAP_RTC_PMIC_POWER_EN_EN); + +again: + /* Clear any existing ALARM2 event */ + rtc_writel(rtc, OMAP_RTC_STATUS_REG, OMAP_RTC_STATUS_ALARM2); + + /* set alarm one second from now */ + omap_rtc_read_time_raw(rtc, &tm); + seconds = tm.tm_sec; + bcd2tm(&tm); + now = rtc_tm_to_time64(&tm); + rtc_time64_to_tm(now + 1, &tm); + + tm2bcd(&tm); + + rtc_wait_not_busy(rtc); + + rtc_write(rtc, OMAP_RTC_ALARM2_SECONDS_REG, tm.tm_sec); + rtc_write(rtc, OMAP_RTC_ALARM2_MINUTES_REG, tm.tm_min); + rtc_write(rtc, OMAP_RTC_ALARM2_HOURS_REG, tm.tm_hour); + rtc_write(rtc, OMAP_RTC_ALARM2_DAYS_REG, tm.tm_mday); + rtc_write(rtc, OMAP_RTC_ALARM2_MONTHS_REG, tm.tm_mon); + rtc_write(rtc, OMAP_RTC_ALARM2_YEARS_REG, tm.tm_year); + + /* + * enable ALARM2 interrupt + * + * NOTE: this fails on AM3352 if rtc_write (writeb) is used + */ + val = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); + rtc_writel(rtc, OMAP_RTC_INTERRUPTS_REG, + val | OMAP_RTC_INTERRUPTS_IT_ALARM2); + + /* Retry in case roll over happened before alarm was armed. */ + if (rtc_read(rtc, OMAP_RTC_SECONDS_REG) != seconds) { + val = rtc_read(rtc, OMAP_RTC_STATUS_REG); + if (!(val & OMAP_RTC_STATUS_ALARM2)) + goto again; + } + + rtc->type->lock(rtc); + + return 0; +} +EXPORT_SYMBOL(omap_rtc_power_off_program); + +/* + * omap_rtc_poweroff: RTC-controlled power off + * + * The RTC can be used to control an external PMIC via the pmic_power_en pin, + * which can be configured to transition to OFF on ALARM2 events. + * + * Notes: + * The one-second alarm offset is the shortest offset possible as the alarm + * registers must be set before the next timer update and the offset + * calculation is too heavy for everything to be done within a single access + * period (~15 us). + * + * Called with local interrupts disabled. + */ +static void omap_rtc_power_off(void) +{ + struct rtc_device *rtc = omap_rtc_power_off_rtc->rtc; + u32 val; + + omap_rtc_power_off_program(rtc->dev.parent); + + /* Set PMIC power enable and EXT_WAKEUP in case PB power on is used */ + omap_rtc_power_off_rtc->type->unlock(omap_rtc_power_off_rtc); + val = rtc_readl(omap_rtc_power_off_rtc, OMAP_RTC_PMIC_REG); + val |= OMAP_RTC_PMIC_POWER_EN_EN | OMAP_RTC_PMIC_EXT_WKUP_POL(0) | + OMAP_RTC_PMIC_EXT_WKUP_EN(0); + rtc_writel(omap_rtc_power_off_rtc, OMAP_RTC_PMIC_REG, val); + omap_rtc_power_off_rtc->type->lock(omap_rtc_power_off_rtc); + + /* + * Wait for alarm to trigger (within one second) and external PMIC to + * power off the system. Add a 500 ms margin for external latencies + * (e.g. debounce circuits). + */ + mdelay(1500); +} + +static const struct rtc_class_ops omap_rtc_ops = { + .read_time = omap_rtc_read_time, + .set_time = omap_rtc_set_time, + .read_alarm = omap_rtc_read_alarm, + .set_alarm = omap_rtc_set_alarm, + .alarm_irq_enable = omap_rtc_alarm_irq_enable, +}; + +static const struct omap_rtc_device_type omap_rtc_default_type = { + .has_power_up_reset = true, + .lock = default_rtc_lock, + .unlock = default_rtc_unlock, +}; + +static const struct omap_rtc_device_type omap_rtc_am3352_type = { + .has_32kclk_en = true, + .has_irqwakeen = true, + .has_pmic_mode = true, + .lock = am3352_rtc_lock, + .unlock = am3352_rtc_unlock, +}; + +static const struct omap_rtc_device_type omap_rtc_da830_type = { + .lock = am3352_rtc_lock, + .unlock = am3352_rtc_unlock, +}; + +static const struct platform_device_id omap_rtc_id_table[] = { + { + .name = "omap_rtc", + .driver_data = (kernel_ulong_t)&omap_rtc_default_type, + }, { + .name = "am3352-rtc", + .driver_data = (kernel_ulong_t)&omap_rtc_am3352_type, + }, { + .name = "da830-rtc", + .driver_data = (kernel_ulong_t)&omap_rtc_da830_type, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, omap_rtc_id_table); + +static const struct of_device_id omap_rtc_of_match[] = { + { + .compatible = "ti,am3352-rtc", + .data = &omap_rtc_am3352_type, + }, { + .compatible = "ti,da830-rtc", + .data = &omap_rtc_da830_type, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, omap_rtc_of_match); + +static const struct pinctrl_pin_desc rtc_pins_desc[] = { + PINCTRL_PIN(0, "ext_wakeup0"), + PINCTRL_PIN(1, "ext_wakeup1"), + PINCTRL_PIN(2, "ext_wakeup2"), + PINCTRL_PIN(3, "ext_wakeup3"), +}; + +static int rtc_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + return 0; +} + +static const char *rtc_pinctrl_get_group_name(struct pinctrl_dev *pctldev, + unsigned int group) +{ + return NULL; +} + +static const struct pinctrl_ops rtc_pinctrl_ops = { + .get_groups_count = rtc_pinctrl_get_groups_count, + .get_group_name = rtc_pinctrl_get_group_name, + .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, + .dt_free_map = pinconf_generic_dt_free_map, +}; + +#define PIN_CONFIG_ACTIVE_HIGH (PIN_CONFIG_END + 1) + +static const struct pinconf_generic_params rtc_params[] = { + {"ti,active-high", PIN_CONFIG_ACTIVE_HIGH, 0}, +}; + +#ifdef CONFIG_DEBUG_FS +static const struct pin_config_item rtc_conf_items[ARRAY_SIZE(rtc_params)] = { + PCONFDUMP(PIN_CONFIG_ACTIVE_HIGH, "input active high", NULL, false), +}; +#endif + +static int rtc_pinconf_get(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long *config) +{ + struct omap_rtc *rtc = pinctrl_dev_get_drvdata(pctldev); + unsigned int param = pinconf_to_config_param(*config); + u32 val; + u16 arg = 0; + + val = rtc_readl(rtc, OMAP_RTC_PMIC_REG); + + switch (param) { + case PIN_CONFIG_INPUT_ENABLE: + if (!(val & OMAP_RTC_PMIC_EXT_WKUP_EN(pin))) + return -EINVAL; + break; + case PIN_CONFIG_ACTIVE_HIGH: + if (val & OMAP_RTC_PMIC_EXT_WKUP_POL(pin)) + return -EINVAL; + break; + default: + return -ENOTSUPP; + } + + *config = pinconf_to_config_packed(param, arg); + + return 0; +} + +static int rtc_pinconf_set(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long *configs, + unsigned int num_configs) +{ + struct omap_rtc *rtc = pinctrl_dev_get_drvdata(pctldev); + u32 val; + unsigned int param; + u32 param_val; + int i; + + val = rtc_readl(rtc, OMAP_RTC_PMIC_REG); + + /* active low by default */ + val |= OMAP_RTC_PMIC_EXT_WKUP_POL(pin); + + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + param_val = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_INPUT_ENABLE: + if (param_val) + val |= OMAP_RTC_PMIC_EXT_WKUP_EN(pin); + else + val &= ~OMAP_RTC_PMIC_EXT_WKUP_EN(pin); + break; + case PIN_CONFIG_ACTIVE_HIGH: + val &= ~OMAP_RTC_PMIC_EXT_WKUP_POL(pin); + break; + default: + dev_err(&rtc->rtc->dev, "Property %u not supported\n", + param); + return -ENOTSUPP; + } + } + + rtc->type->unlock(rtc); + rtc_writel(rtc, OMAP_RTC_PMIC_REG, val); + rtc->type->lock(rtc); + + return 0; +} + +static const struct pinconf_ops rtc_pinconf_ops = { + .is_generic = true, + .pin_config_get = rtc_pinconf_get, + .pin_config_set = rtc_pinconf_set, +}; + +static struct pinctrl_desc rtc_pinctrl_desc = { + .pins = rtc_pins_desc, + .npins = ARRAY_SIZE(rtc_pins_desc), + .pctlops = &rtc_pinctrl_ops, + .confops = &rtc_pinconf_ops, + .custom_params = rtc_params, + .num_custom_params = ARRAY_SIZE(rtc_params), +#ifdef CONFIG_DEBUG_FS + .custom_conf_items = rtc_conf_items, +#endif + .owner = THIS_MODULE, +}; + +static int omap_rtc_scratch_read(void *priv, unsigned int offset, void *_val, + size_t bytes) +{ + struct omap_rtc *rtc = priv; + u32 *val = _val; + int i; + + for (i = 0; i < bytes / 4; i++) + val[i] = rtc_readl(rtc, + OMAP_RTC_SCRATCH0_REG + offset + (i * 4)); + + return 0; +} + +static int omap_rtc_scratch_write(void *priv, unsigned int offset, void *_val, + size_t bytes) +{ + struct omap_rtc *rtc = priv; + u32 *val = _val; + int i; + + rtc->type->unlock(rtc); + for (i = 0; i < bytes / 4; i++) + rtc_writel(rtc, + OMAP_RTC_SCRATCH0_REG + offset + (i * 4), val[i]); + rtc->type->lock(rtc); + + return 0; +} + +static struct nvmem_config omap_rtc_nvmem_config = { + .name = "omap_rtc_scratch", + .word_size = 4, + .stride = 4, + .size = OMAP_RTC_KICK0_REG - OMAP_RTC_SCRATCH0_REG, + .reg_read = omap_rtc_scratch_read, + .reg_write = omap_rtc_scratch_write, +}; + +static int omap_rtc_probe(struct platform_device *pdev) +{ + struct omap_rtc *rtc; + u8 reg, mask, new_ctrl; + const struct platform_device_id *id_entry; + int ret; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->type = device_get_match_data(&pdev->dev); + if (rtc->type) { + rtc->is_pmic_controller = rtc->type->has_pmic_mode && + of_device_is_system_power_controller(pdev->dev.of_node); + } else { + id_entry = platform_get_device_id(pdev); + rtc->type = (void *)id_entry->driver_data; + } + + rtc->irq_timer = platform_get_irq(pdev, 0); + if (rtc->irq_timer < 0) + return rtc->irq_timer; + + rtc->irq_alarm = platform_get_irq(pdev, 1); + if (rtc->irq_alarm < 0) + return rtc->irq_alarm; + + rtc->clk = devm_clk_get(&pdev->dev, "ext-clk"); + if (!IS_ERR(rtc->clk)) + rtc->has_ext_clk = true; + else + rtc->clk = devm_clk_get(&pdev->dev, "int-clk"); + + if (!IS_ERR(rtc->clk)) + clk_prepare_enable(rtc->clk); + + rtc->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rtc->base)) { + clk_disable_unprepare(rtc->clk); + return PTR_ERR(rtc->base); + } + + platform_set_drvdata(pdev, rtc); + + /* Enable the clock/module so that we can access the registers */ + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + + rtc->type->unlock(rtc); + + /* + * disable interrupts + * + * NOTE: ALARM2 is not cleared on AM3352 if rtc_write (writeb) is used + */ + rtc_writel(rtc, OMAP_RTC_INTERRUPTS_REG, 0); + + /* enable RTC functional clock */ + if (rtc->type->has_32kclk_en) { + reg = rtc_read(rtc, OMAP_RTC_OSC_REG); + rtc_write(rtc, OMAP_RTC_OSC_REG, reg | OMAP_RTC_OSC_32KCLK_EN); + } + + /* clear old status */ + reg = rtc_read(rtc, OMAP_RTC_STATUS_REG); + + mask = OMAP_RTC_STATUS_ALARM; + + if (rtc->type->has_pmic_mode) + mask |= OMAP_RTC_STATUS_ALARM2; + + if (rtc->type->has_power_up_reset) { + mask |= OMAP_RTC_STATUS_POWER_UP; + if (reg & OMAP_RTC_STATUS_POWER_UP) + dev_info(&pdev->dev, "RTC power up reset detected\n"); + } + + if (reg & mask) + rtc_write(rtc, OMAP_RTC_STATUS_REG, reg & mask); + + /* On boards with split power, RTC_ON_NOFF won't reset the RTC */ + reg = rtc_read(rtc, OMAP_RTC_CTRL_REG); + if (reg & OMAP_RTC_CTRL_STOP) + dev_info(&pdev->dev, "already running\n"); + + /* force to 24 hour mode */ + new_ctrl = reg & (OMAP_RTC_CTRL_SPLIT | OMAP_RTC_CTRL_AUTO_COMP); + new_ctrl |= OMAP_RTC_CTRL_STOP; + + /* + * BOARD-SPECIFIC CUSTOMIZATION CAN GO HERE: + * + * - Device wake-up capability setting should come through chip + * init logic. OMAP1 boards should initialize the "wakeup capable" + * flag in the platform device if the board is wired right for + * being woken up by RTC alarm. For OMAP-L138, this capability + * is built into the SoC by the "Deep Sleep" capability. + * + * - Boards wired so RTC_ON_nOFF is used as the reset signal, + * rather than nPWRON_RESET, should forcibly enable split + * power mode. (Some chip errata report that RTC_CTRL_SPLIT + * is write-only, and always reads as zero...) + */ + + if (new_ctrl & OMAP_RTC_CTRL_SPLIT) + dev_info(&pdev->dev, "split power mode\n"); + + if (reg != new_ctrl) + rtc_write(rtc, OMAP_RTC_CTRL_REG, new_ctrl); + + /* + * If we have the external clock then switch to it so we can keep + * ticking across suspend. + */ + if (rtc->has_ext_clk) { + reg = rtc_read(rtc, OMAP_RTC_OSC_REG); + reg &= ~OMAP_RTC_OSC_OSC32K_GZ_DISABLE; + reg |= OMAP_RTC_OSC_32KCLK_EN | OMAP_RTC_OSC_SEL_32KCLK_SRC; + rtc_write(rtc, OMAP_RTC_OSC_REG, reg); + } + + rtc->type->lock(rtc); + + device_init_wakeup(&pdev->dev, true); + + rtc->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc->rtc)) { + ret = PTR_ERR(rtc->rtc); + goto err; + } + + rtc->rtc->ops = &omap_rtc_ops; + rtc->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rtc->rtc->range_max = RTC_TIMESTAMP_END_2099; + omap_rtc_nvmem_config.priv = rtc; + + /* handle periodic and alarm irqs */ + ret = devm_request_irq(&pdev->dev, rtc->irq_timer, rtc_irq, 0, + dev_name(&rtc->rtc->dev), rtc); + if (ret) + goto err; + + if (rtc->irq_timer != rtc->irq_alarm) { + ret = devm_request_irq(&pdev->dev, rtc->irq_alarm, rtc_irq, 0, + dev_name(&rtc->rtc->dev), rtc); + if (ret) + goto err; + } + + /* Support ext_wakeup pinconf */ + rtc_pinctrl_desc.name = dev_name(&pdev->dev); + + rtc->pctldev = devm_pinctrl_register(&pdev->dev, &rtc_pinctrl_desc, rtc); + if (IS_ERR(rtc->pctldev)) { + dev_err(&pdev->dev, "Couldn't register pinctrl driver\n"); + ret = PTR_ERR(rtc->pctldev); + goto err; + } + + ret = devm_rtc_register_device(rtc->rtc); + if (ret) + goto err; + + devm_rtc_nvmem_register(rtc->rtc, &omap_rtc_nvmem_config); + + if (rtc->is_pmic_controller) { + if (!pm_power_off) { + omap_rtc_power_off_rtc = rtc; + pm_power_off = omap_rtc_power_off; + } + } + + return 0; + +err: + clk_disable_unprepare(rtc->clk); + device_init_wakeup(&pdev->dev, false); + rtc->type->lock(rtc); + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static void omap_rtc_remove(struct platform_device *pdev) +{ + struct omap_rtc *rtc = platform_get_drvdata(pdev); + u8 reg; + + if (pm_power_off == omap_rtc_power_off && + omap_rtc_power_off_rtc == rtc) { + pm_power_off = NULL; + omap_rtc_power_off_rtc = NULL; + } + + device_init_wakeup(&pdev->dev, false); + + if (!IS_ERR(rtc->clk)) + clk_disable_unprepare(rtc->clk); + + rtc->type->unlock(rtc); + /* leave rtc running, but disable irqs */ + rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, 0); + + if (rtc->has_ext_clk) { + reg = rtc_read(rtc, OMAP_RTC_OSC_REG); + reg &= ~OMAP_RTC_OSC_SEL_32KCLK_SRC; + rtc_write(rtc, OMAP_RTC_OSC_REG, reg); + } + + rtc->type->lock(rtc); + + /* Disable the clock/module */ + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); +} + +static int __maybe_unused omap_rtc_suspend(struct device *dev) +{ + struct omap_rtc *rtc = dev_get_drvdata(dev); + + rtc->interrupts_reg = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); + + rtc->type->unlock(rtc); + /* + * FIXME: the RTC alarm is not currently acting as a wakeup event + * source on some platforms, and in fact this enable() call is just + * saving a flag that's never used... + */ + if (device_may_wakeup(dev)) + enable_irq_wake(rtc->irq_alarm); + else + rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, 0); + rtc->type->lock(rtc); + + rtc->is_suspending = true; + + return 0; +} + +static int __maybe_unused omap_rtc_resume(struct device *dev) +{ + struct omap_rtc *rtc = dev_get_drvdata(dev); + + rtc->type->unlock(rtc); + if (device_may_wakeup(dev)) + disable_irq_wake(rtc->irq_alarm); + else + rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, rtc->interrupts_reg); + rtc->type->lock(rtc); + + rtc->is_suspending = false; + + return 0; +} + +static int __maybe_unused omap_rtc_runtime_suspend(struct device *dev) +{ + struct omap_rtc *rtc = dev_get_drvdata(dev); + + if (rtc->is_suspending && !rtc->has_ext_clk) + return -EBUSY; + + return 0; +} + +static const struct dev_pm_ops omap_rtc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(omap_rtc_suspend, omap_rtc_resume) + SET_RUNTIME_PM_OPS(omap_rtc_runtime_suspend, NULL, NULL) +}; + +static void omap_rtc_shutdown(struct platform_device *pdev) +{ + struct omap_rtc *rtc = platform_get_drvdata(pdev); + u8 mask; + + /* + * Keep the ALARM interrupt enabled to allow the system to power up on + * alarm events. + */ + rtc->type->unlock(rtc); + mask = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); + mask &= OMAP_RTC_INTERRUPTS_IT_ALARM; + rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, mask); + rtc->type->lock(rtc); +} + +static struct platform_driver omap_rtc_driver = { + .probe = omap_rtc_probe, + .remove = omap_rtc_remove, + .shutdown = omap_rtc_shutdown, + .driver = { + .name = "omap_rtc", + .pm = &omap_rtc_pm_ops, + .of_match_table = omap_rtc_of_match, + }, + .id_table = omap_rtc_id_table, +}; + +module_platform_driver(omap_rtc_driver); + +MODULE_AUTHOR("George G. Davis (and others)"); +MODULE_DESCRIPTION("TI OMAP1, AM33xx, DA8xx/OMAP-L13x, AM43xx and DRA7xx RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-opal.c b/drivers/rtc/rtc-opal.c new file mode 100644 index 000000000..ad41aaf8a --- /dev/null +++ b/drivers/rtc/rtc-opal.c @@ -0,0 +1,301 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * IBM OPAL RTC driver + * Copyright (C) 2014 IBM + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define DRVNAME "rtc-opal" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void opal_to_tm(u32 y_m_d, u64 h_m_s_ms, struct rtc_time *tm) +{ + tm->tm_year = ((bcd2bin(y_m_d >> 24) * 100) + + bcd2bin((y_m_d >> 16) & 0xff)) - 1900; + tm->tm_mon = bcd2bin((y_m_d >> 8) & 0xff) - 1; + tm->tm_mday = bcd2bin(y_m_d & 0xff); + tm->tm_hour = bcd2bin((h_m_s_ms >> 56) & 0xff); + tm->tm_min = bcd2bin((h_m_s_ms >> 48) & 0xff); + tm->tm_sec = bcd2bin((h_m_s_ms >> 40) & 0xff); + + tm->tm_wday = -1; +} + +static void tm_to_opal(struct rtc_time *tm, u32 *y_m_d, u64 *h_m_s_ms) +{ + *y_m_d |= ((u32)bin2bcd((tm->tm_year + 1900) / 100)) << 24; + *y_m_d |= ((u32)bin2bcd((tm->tm_year + 1900) % 100)) << 16; + *y_m_d |= ((u32)bin2bcd((tm->tm_mon + 1))) << 8; + *y_m_d |= ((u32)bin2bcd(tm->tm_mday)); + + *h_m_s_ms |= ((u64)bin2bcd(tm->tm_hour)) << 56; + *h_m_s_ms |= ((u64)bin2bcd(tm->tm_min)) << 48; + *h_m_s_ms |= ((u64)bin2bcd(tm->tm_sec)) << 40; +} + +static int opal_get_rtc_time(struct device *dev, struct rtc_time *tm) +{ + s64 rc = OPAL_BUSY; + int retries = 10; + u32 y_m_d; + u64 h_m_s_ms; + __be32 __y_m_d; + __be64 __h_m_s_ms; + + while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { + rc = opal_rtc_read(&__y_m_d, &__h_m_s_ms); + if (rc == OPAL_BUSY_EVENT) { + msleep(OPAL_BUSY_DELAY_MS); + opal_poll_events(NULL); + } else if (rc == OPAL_BUSY) { + msleep(OPAL_BUSY_DELAY_MS); + } else if (rc == OPAL_HARDWARE || rc == OPAL_INTERNAL_ERROR) { + if (retries--) { + msleep(10); /* Wait 10ms before retry */ + rc = OPAL_BUSY; /* go around again */ + } + } + } + + if (rc != OPAL_SUCCESS) + return -EIO; + + y_m_d = be32_to_cpu(__y_m_d); + h_m_s_ms = be64_to_cpu(__h_m_s_ms); + opal_to_tm(y_m_d, h_m_s_ms, tm); + + return 0; +} + +static int opal_set_rtc_time(struct device *dev, struct rtc_time *tm) +{ + s64 rc = OPAL_BUSY; + int retries = 10; + u32 y_m_d = 0; + u64 h_m_s_ms = 0; + + tm_to_opal(tm, &y_m_d, &h_m_s_ms); + + while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { + rc = opal_rtc_write(y_m_d, h_m_s_ms); + if (rc == OPAL_BUSY_EVENT) { + msleep(OPAL_BUSY_DELAY_MS); + opal_poll_events(NULL); + } else if (rc == OPAL_BUSY) { + msleep(OPAL_BUSY_DELAY_MS); + } else if (rc == OPAL_HARDWARE || rc == OPAL_INTERNAL_ERROR) { + if (retries--) { + msleep(10); /* Wait 10ms before retry */ + rc = OPAL_BUSY; /* go around again */ + } + } + } + + return rc == OPAL_SUCCESS ? 0 : -EIO; +} + +/* + * TPO Timed Power-On + * + * TPO get/set OPAL calls care about the hour and min and to make it consistent + * with the rtc utility time conversion functions, we use the 'u64' to store + * its value and perform bit shift by 32 before use.. + */ +static int opal_get_tpo_time(struct device *dev, struct rtc_wkalrm *alarm) +{ + __be32 __y_m_d, __h_m; + struct opal_msg msg; + int rc, token; + u64 h_m_s_ms; + u32 y_m_d; + + token = opal_async_get_token_interruptible(); + if (token < 0) { + if (token != -ERESTARTSYS) + pr_err("Failed to get the async token\n"); + + return token; + } + + rc = opal_tpo_read(token, &__y_m_d, &__h_m); + if (rc != OPAL_ASYNC_COMPLETION) { + rc = -EIO; + goto exit; + } + + rc = opal_async_wait_response(token, &msg); + if (rc) { + rc = -EIO; + goto exit; + } + + rc = opal_get_async_rc(msg); + if (rc != OPAL_SUCCESS) { + rc = -EIO; + goto exit; + } + + y_m_d = be32_to_cpu(__y_m_d); + h_m_s_ms = ((u64)be32_to_cpu(__h_m) << 32); + + /* check if no alarm is set */ + if (y_m_d == 0 && h_m_s_ms == 0) { + pr_debug("No alarm is set\n"); + rc = -ENOENT; + goto exit; + } else { + pr_debug("Alarm set to %x %llx\n", y_m_d, h_m_s_ms); + } + + opal_to_tm(y_m_d, h_m_s_ms, &alarm->time); + +exit: + opal_async_release_token(token); + return rc; +} + +/* Set Timed Power-On */ +static int opal_set_tpo_time(struct device *dev, struct rtc_wkalrm *alarm) +{ + u64 h_m_s_ms = 0; + struct opal_msg msg; + u32 y_m_d = 0; + int token, rc; + + /* if alarm is enabled */ + if (alarm->enabled) { + tm_to_opal(&alarm->time, &y_m_d, &h_m_s_ms); + pr_debug("Alarm set to %x %llx\n", y_m_d, h_m_s_ms); + + } else { + pr_debug("Alarm getting disabled\n"); + } + + token = opal_async_get_token_interruptible(); + if (token < 0) { + if (token != -ERESTARTSYS) + pr_err("Failed to get the async token\n"); + + return token; + } + + /* TPO, we care about hour and minute */ + rc = opal_tpo_write(token, y_m_d, + (u32)((h_m_s_ms >> 32) & 0xffff0000)); + if (rc != OPAL_ASYNC_COMPLETION) { + rc = -EIO; + goto exit; + } + + rc = opal_async_wait_response(token, &msg); + if (rc) { + rc = -EIO; + goto exit; + } + + rc = opal_get_async_rc(msg); + if (rc != OPAL_SUCCESS) + rc = -EIO; + +exit: + opal_async_release_token(token); + return rc; +} + +static int opal_tpo_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct rtc_wkalrm alarm = { .enabled = 0 }; + + /* + * TPO is automatically enabled when opal_set_tpo_time() is called with + * non-zero rtc-time. We only handle disable case which needs to be + * explicitly told to opal. + */ + return enabled ? 0 : opal_set_tpo_time(dev, &alarm); +} + +static const struct rtc_class_ops opal_rtc_ops = { + .read_time = opal_get_rtc_time, + .set_time = opal_set_rtc_time, + .read_alarm = opal_get_tpo_time, + .set_alarm = opal_set_tpo_time, + .alarm_irq_enable = opal_tpo_alarm_irq_enable, +}; + +static int opal_rtc_probe(struct platform_device *pdev) +{ + struct rtc_device *rtc; + + rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + if (pdev->dev.of_node && + (of_property_read_bool(pdev->dev.of_node, "wakeup-source") || + of_property_read_bool(pdev->dev.of_node, "has-tpo")/* legacy */)) + device_set_wakeup_capable(&pdev->dev, true); + else + clear_bit(RTC_FEATURE_ALARM, rtc->features); + + rtc->ops = &opal_rtc_ops; + rtc->range_min = RTC_TIMESTAMP_BEGIN_0000; + rtc->range_max = RTC_TIMESTAMP_END_9999; + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features); + + return devm_rtc_register_device(rtc); +} + +static const struct of_device_id opal_rtc_match[] = { + { + .compatible = "ibm,opal-rtc", + }, + { } +}; +MODULE_DEVICE_TABLE(of, opal_rtc_match); + +static const struct platform_device_id opal_rtc_driver_ids[] = { + { + .name = "opal-rtc", + }, + { } +}; +MODULE_DEVICE_TABLE(platform, opal_rtc_driver_ids); + +static struct platform_driver opal_rtc_driver = { + .probe = opal_rtc_probe, + .id_table = opal_rtc_driver_ids, + .driver = { + .name = DRVNAME, + .of_match_table = opal_rtc_match, + }, +}; + +static int __init opal_rtc_init(void) +{ + if (!firmware_has_feature(FW_FEATURE_OPAL)) + return -ENODEV; + + return platform_driver_register(&opal_rtc_driver); +} + +static void __exit opal_rtc_exit(void) +{ + platform_driver_unregister(&opal_rtc_driver); +} + +MODULE_AUTHOR("Neelesh Gupta "); +MODULE_DESCRIPTION("IBM OPAL RTC driver"); +MODULE_LICENSE("GPL"); + +module_init(opal_rtc_init); +module_exit(opal_rtc_exit); diff --git a/drivers/rtc/rtc-optee.c b/drivers/rtc/rtc-optee.c new file mode 100644 index 000000000..6c908c0d8 --- /dev/null +++ b/drivers/rtc/rtc-optee.c @@ -0,0 +1,735 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Microchip. + */ + +#include +#include +#include +#include +#include +#include + +#define RTC_INFO_VERSION 0x1 + +#define TA_RTC_FEATURE_CORRECTION BIT(0) +#define TA_RTC_FEATURE_ALARM BIT(1) +#define TA_RTC_FEATURE_WAKEUP_ALARM BIT(2) + +enum rtc_optee_pta_cmd { + /* PTA_CMD_RTC_GET_INFO - Get RTC information + * + * [out] memref[0] RTC buffer memory reference containing a struct pta_rtc_info + */ + PTA_CMD_RTC_GET_INF = 0x0, + + /* + * PTA_CMD_RTC_GET_TIME - Get time from RTC + * + * [out] memref[0] RTC buffer memory reference containing a struct pta_rtc_time + */ + PTA_CMD_RTC_GET_TIME = 0x1, + + /* + * PTA_CMD_RTC_SET_TIME - Set time from RTC + * + * [in] memref[0] RTC buffer memory reference containing a struct pta_rtc_time to be + * used as RTC time + */ + PTA_CMD_RTC_SET_TIME = 0x2, + + /* + * PTA_CMD_RTC_GET_OFFSET - Get RTC offset + * + * [out] value[0].a RTC offset (signed 32bit value) + */ + PTA_CMD_RTC_GET_OFFSET = 0x3, + + /* + * PTA_CMD_RTC_SET_OFFSET - Set RTC offset + * + * [in] value[0].a RTC offset to be set (signed 32bit value) + */ + PTA_CMD_RTC_SET_OFFSET = 0x4, + + /* + * PTA_CMD_RTC_READ_ALARM - Read RTC alarm + * + * [out] memref[0] RTC buffer memory reference containing a struct pta_rtc_alarm + */ + PTA_CMD_RTC_READ_ALARM = 0x5, + + /* + * PTA_CMD_RTC_SET_ALARM - Set RTC alarm + * + * [in] memref[0] RTC buffer memory reference containing a struct pta_rtc_alarm to be + * used as RTC alarm + */ + PTA_CMD_RTC_SET_ALARM = 0x6, + + /* + * PTA_CMD_RTC_ENABLE_ALARM - Enable Alarm + * + * [in] value[0].a RTC IRQ flag (uint32_t), 0 to disable the alarm, 1 to enable + */ + PTA_CMD_RTC_ENABLE_ALARM = 0x7, + + /* + * PTA_CMD_RTC_WAIT_ALARM - Get alarm event + * + * [out] value[0].a RTC wait alarm return status (uint32_t): + * - 0: No alarm event + * - 1: Alarm event occurred + * - 2: Alarm event canceled + */ + PTA_CMD_RTC_WAIT_ALARM = 0x8, + + /* + * PTA_CMD_RTC_CANCEL_WAIT - Cancel wait for alarm event + */ + PTA_CMD_RTC_CANCEL_WAIT = 0x9, + + /* + * PTA_CMD_RTC_SET_WAKE_ALARM_STATUS - Set RTC wake alarm status flag + * + * [in] value[0].a RTC IRQ wake alarm flag (uint32_t), 0 to disable the wake up + * capability, 1 to enable. + */ + PTA_CMD_RTC_SET_WAKE_ALARM_STATUS = 0xA, +}; + +enum rtc_wait_alarm_status { + WAIT_ALARM_RESET = 0x0, + WAIT_ALARM_ALARM_OCCURRED = 0x1, + WAIT_ALARM_CANCELED = 0x2, +}; + +struct optee_rtc_time { + u32 tm_sec; + u32 tm_min; + u32 tm_hour; + u32 tm_mday; + u32 tm_mon; + u32 tm_year; + u32 tm_wday; +}; + +struct optee_rtc_alarm { + u8 enabled; + u8 pending; + struct optee_rtc_time time; +}; + +struct optee_rtc_info { + u64 version; + u64 features; + struct optee_rtc_time range_min; + struct optee_rtc_time range_max; +}; + +/** + * struct optee_rtc - OP-TEE RTC private data + * @dev: OP-TEE based RTC device. + * @ctx: OP-TEE context handler. + * @session_id: RTC TA session identifier. + * @session2_id: RTC wait alarm session identifier. + * @shm: Memory pool shared with RTC device. + * @features: Bitfield of RTC features + * @alarm_task: RTC wait alamr task. + * @rtc: RTC device. + */ +struct optee_rtc { + struct device *dev; + struct tee_context *ctx; + u32 session_id; + u32 session2_id; + struct tee_shm *shm; + u64 features; + struct task_struct *alarm_task; + struct rtc_device *rtc; +}; + +static int optee_rtc_readtime(struct device *dev, struct rtc_time *tm) +{ + struct optee_rtc *priv = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = {0}; + struct optee_rtc_time *optee_tm; + struct tee_param param[4] = {0}; + int ret; + + inv_arg.func = PTA_CMD_RTC_GET_TIME; + inv_arg.session = priv->session_id; + inv_arg.num_params = 4; + + /* Fill invoke cmd params */ + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT; + param[0].u.memref.shm = priv->shm; + param[0].u.memref.size = sizeof(struct optee_rtc_time); + + ret = tee_client_invoke_func(priv->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) + return ret ? ret : -EPROTO; + + optee_tm = tee_shm_get_va(priv->shm, 0); + if (IS_ERR(optee_tm)) + return PTR_ERR(optee_tm); + + if (param[0].u.memref.size != sizeof(*optee_tm)) + return -EPROTO; + + tm->tm_sec = optee_tm->tm_sec; + tm->tm_min = optee_tm->tm_min; + tm->tm_hour = optee_tm->tm_hour; + tm->tm_mday = optee_tm->tm_mday; + tm->tm_mon = optee_tm->tm_mon; + tm->tm_year = optee_tm->tm_year - 1900; + tm->tm_wday = optee_tm->tm_wday; + tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year); + + return 0; +} + +static int optee_rtc_settime(struct device *dev, struct rtc_time *tm) +{ + struct optee_rtc *priv = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = {0}; + struct tee_param param[4] = {0}; + struct optee_rtc_time *optee_tm; + int ret; + + inv_arg.func = PTA_CMD_RTC_SET_TIME; + inv_arg.session = priv->session_id; + inv_arg.num_params = 4; + + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; + param[0].u.memref.shm = priv->shm; + param[0].u.memref.size = sizeof(struct optee_rtc_time); + + optee_tm = tee_shm_get_va(priv->shm, 0); + if (IS_ERR(optee_tm)) + return PTR_ERR(optee_tm); + + optee_tm->tm_min = tm->tm_min; + optee_tm->tm_sec = tm->tm_sec; + optee_tm->tm_hour = tm->tm_hour; + optee_tm->tm_mday = tm->tm_mday; + optee_tm->tm_mon = tm->tm_mon; + optee_tm->tm_year = tm->tm_year + 1900; + optee_tm->tm_wday = tm->tm_wday; + + ret = tee_client_invoke_func(priv->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) + return ret ? ret : -EPROTO; + + return 0; +} + +static int optee_rtc_readoffset(struct device *dev, long *offset) +{ + struct optee_rtc *priv = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = {0}; + struct tee_param param[4] = {0}; + int ret; + + if (!(priv->features & TA_RTC_FEATURE_CORRECTION)) + return -EOPNOTSUPP; + + inv_arg.func = PTA_CMD_RTC_GET_OFFSET; + inv_arg.session = priv->session_id; + inv_arg.num_params = 4; + + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT; + + ret = tee_client_invoke_func(priv->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) + return ret ? ret : -EPROTO; + + *offset = param[0].u.value.a; + + return 0; +} + +static int optee_rtc_setoffset(struct device *dev, long offset) +{ + struct optee_rtc *priv = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = {0}; + struct tee_param param[4] = {0}; + int ret; + + if (!(priv->features & TA_RTC_FEATURE_CORRECTION)) + return -EOPNOTSUPP; + + inv_arg.func = PTA_CMD_RTC_SET_OFFSET; + inv_arg.session = priv->session_id; + inv_arg.num_params = 4; + + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; + param[0].u.value.a = offset; + + ret = tee_client_invoke_func(priv->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) + return ret ? ret : -EPROTO; + + return 0; +} + +static int optee_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct optee_rtc *priv = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = {0}; + struct optee_rtc_alarm *optee_alarm; + struct tee_param param[1] = {0}; + int ret; + + if (!(priv->features & TA_RTC_FEATURE_ALARM)) + return -EOPNOTSUPP; + + inv_arg.func = PTA_CMD_RTC_READ_ALARM; + inv_arg.session = priv->session_id; + inv_arg.num_params = 1; + + /* Fill invoke cmd params */ + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT; + param[0].u.memref.shm = priv->shm; + param[0].u.memref.size = sizeof(struct optee_rtc_alarm); + + ret = tee_client_invoke_func(priv->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) + return ret ? ret : -EPROTO; + + optee_alarm = tee_shm_get_va(priv->shm, 0); + if (IS_ERR(optee_alarm)) + return PTR_ERR(optee_alarm); + + if (param[0].u.memref.size != sizeof(*optee_alarm)) + return -EPROTO; + + alarm->enabled = optee_alarm->enabled; + alarm->pending = optee_alarm->pending; + alarm->time.tm_sec = optee_alarm->time.tm_sec; + alarm->time.tm_min = optee_alarm->time.tm_min; + alarm->time.tm_hour = optee_alarm->time.tm_hour; + alarm->time.tm_mday = optee_alarm->time.tm_mday; + alarm->time.tm_mon = optee_alarm->time.tm_mon; + alarm->time.tm_year = optee_alarm->time.tm_year - 1900; + alarm->time.tm_wday = optee_alarm->time.tm_wday; + alarm->time.tm_yday = rtc_year_days(alarm->time.tm_mday, + alarm->time.tm_mon, + alarm->time.tm_year); + + return 0; +} + +static int optee_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct optee_rtc *priv = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = {0}; + struct optee_rtc_alarm *optee_alarm; + struct tee_param param[1] = {0}; + int ret; + + if (!(priv->features & TA_RTC_FEATURE_ALARM)) + return -EOPNOTSUPP; + + inv_arg.func = PTA_CMD_RTC_SET_ALARM; + inv_arg.session = priv->session_id; + inv_arg.num_params = 1; + + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; + param[0].u.memref.shm = priv->shm; + param[0].u.memref.size = sizeof(struct optee_rtc_alarm); + + optee_alarm = tee_shm_get_va(priv->shm, 0); + if (IS_ERR(optee_alarm)) + return PTR_ERR(optee_alarm); + + optee_alarm->enabled = alarm->enabled; + optee_alarm->pending = alarm->pending; + optee_alarm->time.tm_sec = alarm->time.tm_sec; + optee_alarm->time.tm_min = alarm->time.tm_min; + optee_alarm->time.tm_hour = alarm->time.tm_hour; + optee_alarm->time.tm_mday = alarm->time.tm_mday; + optee_alarm->time.tm_mon = alarm->time.tm_mon; + optee_alarm->time.tm_year = alarm->time.tm_year + 1900; + optee_alarm->time.tm_wday = alarm->time.tm_wday; + + ret = tee_client_invoke_func(priv->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) + return ret ? ret : -EPROTO; + + return 0; +} + +static int optee_rtc_enable_alarm(struct device *dev, unsigned int enabled) +{ + struct optee_rtc *priv = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = {0}; + struct tee_param param[1] = {0}; + int ret; + + if (!(priv->features & TA_RTC_FEATURE_ALARM)) + return -EOPNOTSUPP; + + inv_arg.func = PTA_CMD_RTC_ENABLE_ALARM; + inv_arg.session = priv->session_id; + inv_arg.num_params = 1; + + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; + param[0].u.value.a = (bool)enabled; + + ret = tee_client_invoke_func(priv->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) + return ret ? ret : -EPROTO; + + return 0; +} + +static const struct rtc_class_ops optee_rtc_ops = { + .read_time = optee_rtc_readtime, + .set_time = optee_rtc_settime, + .set_offset = optee_rtc_setoffset, + .read_offset = optee_rtc_readoffset, + .read_alarm = optee_rtc_read_alarm, + .set_alarm = optee_rtc_set_alarm, + .alarm_irq_enable = optee_rtc_enable_alarm, +}; + +static int optee_rtc_wait_alarm(struct device *dev, int *return_status) +{ + struct optee_rtc *priv = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = {0}; + struct tee_param param[1] = {0}; + int ret; + + if (!(priv->features & TA_RTC_FEATURE_ALARM)) + return -EOPNOTSUPP; + + inv_arg.func = PTA_CMD_RTC_WAIT_ALARM; + inv_arg.session = priv->session2_id; + inv_arg.num_params = 1; + + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT; + + ret = tee_client_invoke_func(priv->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) + return ret ? ret : -EPROTO; + + *return_status = param[0].u.value.a; + + return 0; +} + +static int optee_rtc_cancel_wait_alarm(struct device *dev) +{ + struct optee_rtc *priv = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = {0}; + struct tee_param param[1] = {0}; + int ret; + + if (!(priv->features & TA_RTC_FEATURE_ALARM)) + return -EOPNOTSUPP; + + inv_arg.func = PTA_CMD_RTC_CANCEL_WAIT; + inv_arg.session = priv->session_id; + inv_arg.num_params = 0; + + ret = tee_client_invoke_func(priv->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) + return ret ? ret : -EPROTO; + + return 0; +} + +static int optee_rtc_set_alarm_wake_status(struct device *dev, bool status) +{ + struct optee_rtc *priv = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = {0}; + struct tee_param param[1] = {0}; + int ret; + + if (!(priv->features & TA_RTC_FEATURE_ALARM)) + return -EOPNOTSUPP; + + inv_arg.func = PTA_CMD_RTC_SET_WAKE_ALARM_STATUS; + inv_arg.session = priv->session_id; + inv_arg.num_params = 1; + + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; + param[0].u.value.a = status; + + ret = tee_client_invoke_func(priv->ctx, &inv_arg, param); + + if (ret < 0 || inv_arg.ret != 0) + return ret ? ret : -EPROTO; + + return 0; +} + +static int optee_rtc_handle_alarm_event(void *data) +{ + struct optee_rtc *priv = (struct optee_rtc *)data; + int wait_alarm_return_status = 0; + int ret; + + while (!kthread_should_stop()) { + ret = optee_rtc_wait_alarm(priv->dev, &wait_alarm_return_status); + if (ret) { + dev_err(priv->dev, "Failed to wait for alarm: %d\n", ret); + return ret; + } + switch (wait_alarm_return_status) { + case WAIT_ALARM_ALARM_OCCURRED: + dev_dbg(priv->dev, "Alarm occurred\n"); + rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_AF); + break; + case WAIT_ALARM_CANCELED: + dev_dbg(priv->dev, "Alarm canceled\n"); + break; + default: + dev_warn(priv->dev, "Unknown return status: %d\n", + wait_alarm_return_status); + break; + } + } + + return 0; +} + +static int optee_rtc_read_info(struct device *dev, struct rtc_device *rtc, + u64 *features) +{ + struct optee_rtc *priv = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = {0}; + struct tee_param param[4] = {0}; + struct optee_rtc_info *info; + struct optee_rtc_time *tm; + int ret; + + inv_arg.func = PTA_CMD_RTC_GET_INF; + inv_arg.session = priv->session_id; + inv_arg.num_params = 4; + + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT; + param[0].u.memref.shm = priv->shm; + param[0].u.memref.size = sizeof(*info); + + ret = tee_client_invoke_func(priv->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) + return ret ? ret : -EPROTO; + + info = tee_shm_get_va(priv->shm, 0); + if (IS_ERR(info)) + return PTR_ERR(info); + + if (param[0].u.memref.size != sizeof(*info)) + return -EPROTO; + + if (info->version != RTC_INFO_VERSION) + return -EPROTO; + + *features = info->features; + + tm = &info->range_min; + rtc->range_min = mktime64(tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, + tm->tm_sec); + tm = &info->range_max; + rtc->range_max = mktime64(tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, + tm->tm_sec); + + return 0; +} + +static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) +{ + return (ver->impl_id == TEE_IMPL_ID_OPTEE); +} + +static int optee_rtc_probe(struct tee_client_device *rtc_device) +{ + struct device *dev = &rtc_device->dev; + struct tee_ioctl_open_session_arg sess2_arg = {0}; + struct tee_ioctl_open_session_arg sess_arg = {0}; + struct optee_rtc *priv; + struct rtc_device *rtc; + struct tee_shm *shm; + int ret, err; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + rtc = devm_rtc_allocate_device(dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + priv->rtc = rtc; + + /* Open context with TEE driver */ + priv->ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL); + if (IS_ERR(priv->ctx)) + return -ENODEV; + + /* Open first session with rtc Pseudo Trusted App */ + export_uuid(sess_arg.uuid, &rtc_device->id.uuid); + sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL; + + ret = tee_client_open_session(priv->ctx, &sess_arg, NULL); + if (ret < 0 || sess_arg.ret != 0) { + dev_err(dev, "tee_client_open_session failed, err: %x\n", sess_arg.ret); + err = -EINVAL; + goto out_ctx; + } + priv->session_id = sess_arg.session; + + /* + * Shared memory is used for passing an instance of either struct optee_rtc_info, + * struct optee_rtc_time or struct optee_rtc_alarm to OP-TEE service. + * The former is by definition large enough to cover both parameter cases. + */ + shm = tee_shm_alloc_kernel_buf(priv->ctx, sizeof(struct optee_rtc_info)); + if (IS_ERR(shm)) { + dev_err(priv->dev, "tee_shm_alloc_kernel_buf failed\n"); + err = PTR_ERR(shm); + goto out_sess; + } + + priv->shm = shm; + priv->dev = dev; + dev_set_drvdata(dev, priv); + + rtc->ops = &optee_rtc_ops; + + err = optee_rtc_read_info(dev, rtc, &priv->features); + if (err) { + dev_err(dev, "Failed to get RTC features from OP-TEE\n"); + goto out_shm; + } + + /* Handle feature's related setup before registering to rtc framework */ + if (priv->features & TA_RTC_FEATURE_ALARM) { + priv->alarm_task = kthread_create(optee_rtc_handle_alarm_event, + priv, "rtc_alarm_evt"); + if (IS_ERR(priv->alarm_task)) { + dev_err(dev, "Failed to create alarm thread\n"); + err = PTR_ERR(priv->alarm_task); + goto out_shm; + } + + /* + * In case of supported alarm feature on optee side, we create a kthread + * that will, in a new optee session, call a PTA interface "rtc_wait_alarm". + * This call return in case of alarm and in case of canceled alarm. + * The new optee session is therefore only needed in this case as we cannot + * use the same session for parallel calls to optee PTA. + * Hence one session is reserved to wait for alarms and the other to make + * standard calls to RTC PTA. + */ + + /* Open second session with rtc Trusted App */ + export_uuid(sess2_arg.uuid, &rtc_device->id.uuid); + sess2_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL; + + ret = tee_client_open_session(priv->ctx, &sess2_arg, NULL); + if (ret < 0 || sess2_arg.ret != 0) { + dev_err(dev, "tee_client_open_session failed, err: %x\n", sess2_arg.ret); + err = -EINVAL; + goto out_thrd; + } + priv->session2_id = sess2_arg.session; + + if (priv->features & TA_RTC_FEATURE_WAKEUP_ALARM) + device_init_wakeup(dev, true); + } + + err = devm_rtc_register_device(rtc); + if (err) + goto out_wk; + + /* + * We must clear those bits after registering because registering a rtc_device + * will set them if it sees that .set_offset and .set_alarm are provided. + */ + if (!(priv->features & TA_RTC_FEATURE_CORRECTION)) + clear_bit(RTC_FEATURE_CORRECTION, rtc->features); + if (!(priv->features & TA_RTC_FEATURE_ALARM)) + clear_bit(RTC_FEATURE_ALARM, rtc->features); + + /* Start the thread after the rtc is setup */ + if (priv->alarm_task) { + wake_up_process(priv->alarm_task); + dev_dbg(dev, "Wait alarm thread successfully started\n"); + } + + return 0; +out_wk: + if (priv->features & TA_RTC_FEATURE_ALARM) { + device_init_wakeup(dev, false); + tee_client_close_session(priv->ctx, priv->session2_id); + } +out_thrd: + if (priv->features & TA_RTC_FEATURE_ALARM) + kthread_stop(priv->alarm_task); +out_shm: + tee_shm_free(priv->shm); +out_sess: + tee_client_close_session(priv->ctx, priv->session_id); +out_ctx: + tee_client_close_context(priv->ctx); + + return err; +} + +static void optee_rtc_remove(struct tee_client_device *rtc_device) +{ + struct device *dev = &rtc_device->dev; + struct optee_rtc *priv = dev_get_drvdata(dev); + + if (priv->features & TA_RTC_FEATURE_ALARM) { + optee_rtc_cancel_wait_alarm(dev); + kthread_stop(priv->alarm_task); + device_init_wakeup(dev, false); + tee_client_close_session(priv->ctx, priv->session2_id); + } + + tee_shm_free(priv->shm); + tee_client_close_session(priv->ctx, priv->session_id); + tee_client_close_context(priv->ctx); +} + +static int optee_rtc_suspend(struct device *dev) +{ + int res = optee_rtc_set_alarm_wake_status(dev, device_may_wakeup(dev)); + + if (res) { + dev_err(dev, "Unable to transmit wakeup information to optee rtc\n"); + return res; + } + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(optee_rtc_pm_ops, optee_rtc_suspend, NULL); + +static const struct tee_client_device_id optee_rtc_id_table[] = { + {UUID_INIT(0xf389f8c8, 0x845f, 0x496c, + 0x8b, 0xbe, 0xd6, 0x4b, 0xd2, 0x4c, 0x92, 0xfd)}, + {} +}; + +MODULE_DEVICE_TABLE(tee, optee_rtc_id_table); + +static struct tee_client_driver optee_rtc_driver = { + .id_table = optee_rtc_id_table, + .probe = optee_rtc_probe, + .remove = optee_rtc_remove, + .driver = { + .name = "optee_rtc", + .pm = pm_sleep_ptr(&optee_rtc_pm_ops), + }, +}; + +module_tee_client_driver(optee_rtc_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Clément Léger "); +MODULE_DESCRIPTION("OP-TEE based RTC driver"); diff --git a/drivers/rtc/rtc-palmas.c b/drivers/rtc/rtc-palmas.c new file mode 100644 index 000000000..aecada6bc --- /dev/null +++ b/drivers/rtc/rtc-palmas.c @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rtc-palmas.c -- Palmas Real Time Clock driver. + + * RTC driver for TI Palma series devices like TPS65913, + * TPS65914 power management IC. + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * Author: Laxman Dewangan + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct palmas_rtc { + struct rtc_device *rtc; + struct device *dev; + unsigned int irq; +}; + +/* Total number of RTC registers needed to set time*/ +#define PALMAS_NUM_TIME_REGS (PALMAS_YEARS_REG - PALMAS_SECONDS_REG + 1) + +static int palmas_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char rtc_data[PALMAS_NUM_TIME_REGS]; + struct palmas *palmas = dev_get_drvdata(dev->parent); + int ret; + + /* Copy RTC counting registers to static registers or latches */ + ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG, + PALMAS_RTC_CTRL_REG_GET_TIME, PALMAS_RTC_CTRL_REG_GET_TIME); + if (ret < 0) { + dev_err(dev, "RTC CTRL reg update failed, err: %d\n", ret); + return ret; + } + + ret = palmas_bulk_read(palmas, PALMAS_RTC_BASE, PALMAS_SECONDS_REG, + rtc_data, PALMAS_NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "RTC_SECONDS reg read failed, err = %d\n", ret); + return ret; + } + + tm->tm_sec = bcd2bin(rtc_data[0]); + tm->tm_min = bcd2bin(rtc_data[1]); + tm->tm_hour = bcd2bin(rtc_data[2]); + tm->tm_mday = bcd2bin(rtc_data[3]); + tm->tm_mon = bcd2bin(rtc_data[4]) - 1; + tm->tm_year = bcd2bin(rtc_data[5]) + 100; + + return ret; +} + +static int palmas_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char rtc_data[PALMAS_NUM_TIME_REGS]; + struct palmas *palmas = dev_get_drvdata(dev->parent); + int ret; + + rtc_data[0] = bin2bcd(tm->tm_sec); + rtc_data[1] = bin2bcd(tm->tm_min); + rtc_data[2] = bin2bcd(tm->tm_hour); + rtc_data[3] = bin2bcd(tm->tm_mday); + rtc_data[4] = bin2bcd(tm->tm_mon + 1); + rtc_data[5] = bin2bcd(tm->tm_year - 100); + + /* Stop RTC while updating the RTC time registers */ + ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG, + PALMAS_RTC_CTRL_REG_STOP_RTC, 0); + if (ret < 0) { + dev_err(dev, "RTC stop failed, err = %d\n", ret); + return ret; + } + + ret = palmas_bulk_write(palmas, PALMAS_RTC_BASE, PALMAS_SECONDS_REG, + rtc_data, PALMAS_NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "RTC_SECONDS reg write failed, err = %d\n", ret); + return ret; + } + + /* Start back RTC */ + ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG, + PALMAS_RTC_CTRL_REG_STOP_RTC, PALMAS_RTC_CTRL_REG_STOP_RTC); + if (ret < 0) + dev_err(dev, "RTC start failed, err = %d\n", ret); + return ret; +} + +static int palmas_rtc_alarm_irq_enable(struct device *dev, unsigned enabled) +{ + struct palmas *palmas = dev_get_drvdata(dev->parent); + u8 val; + + val = enabled ? PALMAS_RTC_INTERRUPTS_REG_IT_ALARM : 0; + return palmas_write(palmas, PALMAS_RTC_BASE, + PALMAS_RTC_INTERRUPTS_REG, val); +} + +static int palmas_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + unsigned char alarm_data[PALMAS_NUM_TIME_REGS]; + u32 int_val; + struct palmas *palmas = dev_get_drvdata(dev->parent); + int ret; + + ret = palmas_bulk_read(palmas, PALMAS_RTC_BASE, + PALMAS_ALARM_SECONDS_REG, + alarm_data, PALMAS_NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "RTC_ALARM_SECONDS read failed, err = %d\n", ret); + return ret; + } + + alm->time.tm_sec = bcd2bin(alarm_data[0]); + alm->time.tm_min = bcd2bin(alarm_data[1]); + alm->time.tm_hour = bcd2bin(alarm_data[2]); + alm->time.tm_mday = bcd2bin(alarm_data[3]); + alm->time.tm_mon = bcd2bin(alarm_data[4]) - 1; + alm->time.tm_year = bcd2bin(alarm_data[5]) + 100; + + ret = palmas_read(palmas, PALMAS_RTC_BASE, PALMAS_RTC_INTERRUPTS_REG, + &int_val); + if (ret < 0) { + dev_err(dev, "RTC_INTERRUPTS reg read failed, err = %d\n", ret); + return ret; + } + + if (int_val & PALMAS_RTC_INTERRUPTS_REG_IT_ALARM) + alm->enabled = 1; + return ret; +} + +static int palmas_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + unsigned char alarm_data[PALMAS_NUM_TIME_REGS]; + struct palmas *palmas = dev_get_drvdata(dev->parent); + int ret; + + ret = palmas_rtc_alarm_irq_enable(dev, 0); + if (ret < 0) { + dev_err(dev, "Disable RTC alarm failed\n"); + return ret; + } + + alarm_data[0] = bin2bcd(alm->time.tm_sec); + alarm_data[1] = bin2bcd(alm->time.tm_min); + alarm_data[2] = bin2bcd(alm->time.tm_hour); + alarm_data[3] = bin2bcd(alm->time.tm_mday); + alarm_data[4] = bin2bcd(alm->time.tm_mon + 1); + alarm_data[5] = bin2bcd(alm->time.tm_year - 100); + + ret = palmas_bulk_write(palmas, PALMAS_RTC_BASE, + PALMAS_ALARM_SECONDS_REG, alarm_data, PALMAS_NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "ALARM_SECONDS_REG write failed, err = %d\n", ret); + return ret; + } + + if (alm->enabled) + ret = palmas_rtc_alarm_irq_enable(dev, 1); + return ret; +} + +static int palmas_clear_interrupts(struct device *dev) +{ + struct palmas *palmas = dev_get_drvdata(dev->parent); + unsigned int rtc_reg; + int ret; + + ret = palmas_read(palmas, PALMAS_RTC_BASE, PALMAS_RTC_STATUS_REG, + &rtc_reg); + if (ret < 0) { + dev_err(dev, "RTC_STATUS read failed, err = %d\n", ret); + return ret; + } + + ret = palmas_write(palmas, PALMAS_RTC_BASE, PALMAS_RTC_STATUS_REG, + rtc_reg); + if (ret < 0) { + dev_err(dev, "RTC_STATUS write failed, err = %d\n", ret); + return ret; + } + return 0; +} + +static irqreturn_t palmas_rtc_interrupt(int irq, void *context) +{ + struct palmas_rtc *palmas_rtc = context; + struct device *dev = palmas_rtc->dev; + int ret; + + ret = palmas_clear_interrupts(dev); + if (ret < 0) { + dev_err(dev, "RTC interrupt clear failed, err = %d\n", ret); + return IRQ_NONE; + } + + rtc_update_irq(palmas_rtc->rtc, 1, RTC_IRQF | RTC_AF); + return IRQ_HANDLED; +} + +static const struct rtc_class_ops palmas_rtc_ops = { + .read_time = palmas_rtc_read_time, + .set_time = palmas_rtc_set_time, + .read_alarm = palmas_rtc_read_alarm, + .set_alarm = palmas_rtc_set_alarm, + .alarm_irq_enable = palmas_rtc_alarm_irq_enable, +}; + +static int palmas_rtc_probe(struct platform_device *pdev) +{ + struct palmas *palmas = dev_get_drvdata(pdev->dev.parent); + struct palmas_rtc *palmas_rtc = NULL; + int ret; + bool enable_bb_charging = false; + bool high_bb_charging = false; + + if (pdev->dev.of_node) { + enable_bb_charging = of_property_read_bool(pdev->dev.of_node, + "ti,backup-battery-chargeable"); + high_bb_charging = of_property_read_bool(pdev->dev.of_node, + "ti,backup-battery-charge-high-current"); + } + + palmas_rtc = devm_kzalloc(&pdev->dev, sizeof(struct palmas_rtc), + GFP_KERNEL); + if (!palmas_rtc) + return -ENOMEM; + + /* Clear pending interrupts */ + ret = palmas_clear_interrupts(&pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "clear RTC int failed, err = %d\n", ret); + return ret; + } + + palmas_rtc->dev = &pdev->dev; + platform_set_drvdata(pdev, palmas_rtc); + + if (enable_bb_charging) { + unsigned reg = PALMAS_BACKUP_BATTERY_CTRL_BBS_BBC_LOW_ICHRG; + + if (high_bb_charging) + reg = 0; + + ret = palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE, + PALMAS_BACKUP_BATTERY_CTRL, + PALMAS_BACKUP_BATTERY_CTRL_BBS_BBC_LOW_ICHRG, reg); + if (ret < 0) { + dev_err(&pdev->dev, + "BACKUP_BATTERY_CTRL update failed, %d\n", ret); + return ret; + } + + ret = palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE, + PALMAS_BACKUP_BATTERY_CTRL, + PALMAS_BACKUP_BATTERY_CTRL_BB_CHG_EN, + PALMAS_BACKUP_BATTERY_CTRL_BB_CHG_EN); + if (ret < 0) { + dev_err(&pdev->dev, + "BACKUP_BATTERY_CTRL update failed, %d\n", ret); + return ret; + } + } + + /* Start RTC */ + ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG, + PALMAS_RTC_CTRL_REG_STOP_RTC, + PALMAS_RTC_CTRL_REG_STOP_RTC); + if (ret < 0) { + dev_err(&pdev->dev, "RTC_CTRL write failed, err = %d\n", ret); + return ret; + } + + palmas_rtc->irq = platform_get_irq(pdev, 0); + + device_init_wakeup(&pdev->dev, true); + palmas_rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + &palmas_rtc_ops, THIS_MODULE); + if (IS_ERR(palmas_rtc->rtc)) { + ret = PTR_ERR(palmas_rtc->rtc); + dev_err(&pdev->dev, "RTC register failed, err = %d\n", ret); + return ret; + } + + ret = devm_request_threaded_irq(&pdev->dev, palmas_rtc->irq, NULL, + palmas_rtc_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + dev_name(&pdev->dev), palmas_rtc); + if (ret < 0) { + dev_err(&pdev->dev, "IRQ request failed, err = %d\n", ret); + return ret; + } + + return 0; +} + +static void palmas_rtc_remove(struct platform_device *pdev) +{ + palmas_rtc_alarm_irq_enable(&pdev->dev, 0); +} + +#ifdef CONFIG_PM_SLEEP +static int palmas_rtc_suspend(struct device *dev) +{ + struct palmas_rtc *palmas_rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(palmas_rtc->irq); + return 0; +} + +static int palmas_rtc_resume(struct device *dev) +{ + struct palmas_rtc *palmas_rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(palmas_rtc->irq); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(palmas_rtc_pm_ops, palmas_rtc_suspend, + palmas_rtc_resume); + +#ifdef CONFIG_OF +static const struct of_device_id of_palmas_rtc_match[] = { + { .compatible = "ti,palmas-rtc"}, + { }, +}; +MODULE_DEVICE_TABLE(of, of_palmas_rtc_match); +#endif + +static struct platform_driver palmas_rtc_driver = { + .probe = palmas_rtc_probe, + .remove = palmas_rtc_remove, + .driver = { + .name = "palmas-rtc", + .pm = &palmas_rtc_pm_ops, + .of_match_table = of_match_ptr(of_palmas_rtc_match), + }, +}; + +module_platform_driver(palmas_rtc_driver); + +MODULE_ALIAS("platform:palmas_rtc"); +MODULE_DESCRIPTION("TI PALMAS series RTC driver"); +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-pcf2123.c b/drivers/rtc/rtc-pcf2123.c new file mode 100644 index 000000000..e714661e6 --- /dev/null +++ b/drivers/rtc/rtc-pcf2123.c @@ -0,0 +1,481 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * An SPI driver for the Philips PCF2123 RTC + * Copyright 2009 Cyber Switching, Inc. + * + * Author: Chris Verges + * Maintainers: http://www.cyberswitching.com + * + * based on the RS5C348 driver in this same directory. + * + * Thanks to Christian Pellegrin for + * the sysfs contributions to this driver. + * + * Please note that the CS is active high, so platform data + * should look something like: + * + * static struct spi_board_info ek_spi_devices[] = { + * ... + * { + * .modalias = "rtc-pcf2123", + * .chip_select = 1, + * .controller_data = (void *)AT91_PIN_PA10, + * .max_speed_hz = 1000 * 1000, + * .mode = SPI_CS_HIGH, + * .bus_num = 0, + * }, + * ... + *}; + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* REGISTERS */ +#define PCF2123_REG_CTRL1 (0x00) /* Control Register 1 */ +#define PCF2123_REG_CTRL2 (0x01) /* Control Register 2 */ +#define PCF2123_REG_SC (0x02) /* datetime */ +#define PCF2123_REG_MN (0x03) +#define PCF2123_REG_HR (0x04) +#define PCF2123_REG_DM (0x05) +#define PCF2123_REG_DW (0x06) +#define PCF2123_REG_MO (0x07) +#define PCF2123_REG_YR (0x08) +#define PCF2123_REG_ALRM_MN (0x09) /* Alarm Registers */ +#define PCF2123_REG_ALRM_HR (0x0a) +#define PCF2123_REG_ALRM_DM (0x0b) +#define PCF2123_REG_ALRM_DW (0x0c) +#define PCF2123_REG_OFFSET (0x0d) /* Clock Rate Offset Register */ +#define PCF2123_REG_TMR_CLKOUT (0x0e) /* Timer Registers */ +#define PCF2123_REG_CTDWN_TMR (0x0f) + +/* PCF2123_REG_CTRL1 BITS */ +#define CTRL1_CLEAR (0) /* Clear */ +#define CTRL1_CORR_INT BIT(1) /* Correction irq enable */ +#define CTRL1_12_HOUR BIT(2) /* 12 hour time */ +#define CTRL1_SW_RESET (BIT(3) | BIT(4) | BIT(6)) /* Software reset */ +#define CTRL1_STOP BIT(5) /* Stop the clock */ +#define CTRL1_EXT_TEST BIT(7) /* External clock test mode */ + +/* PCF2123_REG_CTRL2 BITS */ +#define CTRL2_TIE BIT(0) /* Countdown timer irq enable */ +#define CTRL2_AIE BIT(1) /* Alarm irq enable */ +#define CTRL2_TF BIT(2) /* Countdown timer flag */ +#define CTRL2_AF BIT(3) /* Alarm flag */ +#define CTRL2_TI_TP BIT(4) /* Irq pin generates pulse */ +#define CTRL2_MSF BIT(5) /* Minute or second irq flag */ +#define CTRL2_SI BIT(6) /* Second irq enable */ +#define CTRL2_MI BIT(7) /* Minute irq enable */ + +/* PCF2123_REG_SC BITS */ +#define OSC_HAS_STOPPED BIT(7) /* Clock has been stopped */ + +/* PCF2123_REG_ALRM_XX BITS */ +#define ALRM_DISABLE BIT(7) /* MN, HR, DM, or DW alarm matching */ + +/* PCF2123_REG_TMR_CLKOUT BITS */ +#define CD_TMR_4096KHZ (0) /* 4096 KHz countdown timer */ +#define CD_TMR_64HZ (1) /* 64 Hz countdown timer */ +#define CD_TMR_1HZ (2) /* 1 Hz countdown timer */ +#define CD_TMR_60th_HZ (3) /* 60th Hz countdown timer */ +#define CD_TMR_TE BIT(3) /* Countdown timer enable */ + +/* PCF2123_REG_OFFSET BITS */ +#define OFFSET_SIGN_BIT 6 /* 2's complement sign bit */ +#define OFFSET_COARSE BIT(7) /* Coarse mode offset */ +#define OFFSET_STEP (2170) /* Offset step in parts per billion */ +#define OFFSET_MASK GENMASK(6, 0) /* Offset value */ + +/* READ/WRITE ADDRESS BITS */ +#define PCF2123_WRITE BIT(4) +#define PCF2123_READ (BIT(4) | BIT(7)) + + +static struct spi_driver pcf2123_driver; + +struct pcf2123_data { + struct rtc_device *rtc; + struct regmap *map; +}; + +static const struct regmap_config pcf2123_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .read_flag_mask = PCF2123_READ, + .write_flag_mask = PCF2123_WRITE, + .max_register = PCF2123_REG_CTDWN_TMR, +}; + +static int pcf2123_read_offset(struct device *dev, long *offset) +{ + struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); + int ret, val; + unsigned int reg; + + ret = regmap_read(pcf2123->map, PCF2123_REG_OFFSET, ®); + if (ret) + return ret; + + val = sign_extend32((reg & OFFSET_MASK), OFFSET_SIGN_BIT); + + if (reg & OFFSET_COARSE) + val *= 2; + + *offset = ((long)val) * OFFSET_STEP; + + return 0; +} + +/* + * The offset register is a 7 bit signed value with a coarse bit in bit 7. + * The main difference between the two is normal offset adjusts the first + * second of n minutes every other hour, with 61, 62 and 63 being shoved + * into the 60th minute. + * The coarse adjustment does the same, but every hour. + * the two overlap, with every even normal offset value corresponding + * to a coarse offset. Based on this algorithm, it seems that despite the + * name, coarse offset is a better fit for overlapping values. + */ +static int pcf2123_set_offset(struct device *dev, long offset) +{ + struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); + s8 reg; + + if (offset > OFFSET_STEP * 127) + reg = 127; + else if (offset < OFFSET_STEP * -128) + reg = -128; + else + reg = DIV_ROUND_CLOSEST(offset, OFFSET_STEP); + + /* choose fine offset only for odd values in the normal range */ + if (reg & 1 && reg <= 63 && reg >= -64) { + /* Normal offset. Clear the coarse bit */ + reg &= ~OFFSET_COARSE; + } else { + /* Coarse offset. Divide by 2 and set the coarse bit */ + reg >>= 1; + reg |= OFFSET_COARSE; + } + + return regmap_write(pcf2123->map, PCF2123_REG_OFFSET, (unsigned int)reg); +} + +static int pcf2123_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); + u8 rxbuf[7]; + int ret; + + ret = regmap_bulk_read(pcf2123->map, PCF2123_REG_SC, rxbuf, + sizeof(rxbuf)); + if (ret) + return ret; + + if (rxbuf[0] & OSC_HAS_STOPPED) { + dev_info(dev, "clock was stopped. Time is not valid\n"); + return -EINVAL; + } + + tm->tm_sec = bcd2bin(rxbuf[0] & 0x7F); + tm->tm_min = bcd2bin(rxbuf[1] & 0x7F); + tm->tm_hour = bcd2bin(rxbuf[2] & 0x3F); /* rtc hr 0-23 */ + tm->tm_mday = bcd2bin(rxbuf[3] & 0x3F); + tm->tm_wday = rxbuf[4] & 0x07; + tm->tm_mon = bcd2bin(rxbuf[5] & 0x1F) - 1; /* rtc mn 1-12 */ + tm->tm_year = bcd2bin(rxbuf[6]) + 100; + + dev_dbg(dev, "%s: tm is %ptR\n", __func__, tm); + + return 0; +} + +static int pcf2123_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); + u8 txbuf[7]; + int ret; + + dev_dbg(dev, "%s: tm is %ptR\n", __func__, tm); + + /* Stop the counter first */ + ret = regmap_write(pcf2123->map, PCF2123_REG_CTRL1, CTRL1_STOP); + if (ret) + return ret; + + /* Set the new time */ + txbuf[0] = bin2bcd(tm->tm_sec & 0x7F); + txbuf[1] = bin2bcd(tm->tm_min & 0x7F); + txbuf[2] = bin2bcd(tm->tm_hour & 0x3F); + txbuf[3] = bin2bcd(tm->tm_mday & 0x3F); + txbuf[4] = tm->tm_wday & 0x07; + txbuf[5] = bin2bcd((tm->tm_mon + 1) & 0x1F); /* rtc mn 1-12 */ + txbuf[6] = bin2bcd(tm->tm_year - 100); + + ret = regmap_bulk_write(pcf2123->map, PCF2123_REG_SC, txbuf, + sizeof(txbuf)); + if (ret) + return ret; + + /* Start the counter */ + ret = regmap_write(pcf2123->map, PCF2123_REG_CTRL1, CTRL1_CLEAR); + if (ret) + return ret; + + return 0; +} + +static int pcf2123_rtc_alarm_irq_enable(struct device *dev, unsigned int en) +{ + struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); + + return regmap_update_bits(pcf2123->map, PCF2123_REG_CTRL2, CTRL2_AIE, + en ? CTRL2_AIE : 0); +} + +static int pcf2123_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); + u8 rxbuf[4]; + int ret; + unsigned int val = 0; + + ret = regmap_bulk_read(pcf2123->map, PCF2123_REG_ALRM_MN, rxbuf, + sizeof(rxbuf)); + if (ret) + return ret; + + alm->time.tm_min = bcd2bin(rxbuf[0] & 0x7F); + alm->time.tm_hour = bcd2bin(rxbuf[1] & 0x3F); + alm->time.tm_mday = bcd2bin(rxbuf[2] & 0x3F); + alm->time.tm_wday = bcd2bin(rxbuf[3] & 0x07); + + dev_dbg(dev, "%s: alm is %ptR\n", __func__, &alm->time); + + ret = regmap_read(pcf2123->map, PCF2123_REG_CTRL2, &val); + if (ret) + return ret; + + alm->enabled = !!(val & CTRL2_AIE); + + return 0; +} + +static int pcf2123_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); + u8 txbuf[4]; + int ret; + + dev_dbg(dev, "%s: alm is %ptR\n", __func__, &alm->time); + + /* Disable alarm interrupt */ + ret = regmap_update_bits(pcf2123->map, PCF2123_REG_CTRL2, CTRL2_AIE, 0); + if (ret) + return ret; + + /* Ensure alarm flag is clear */ + ret = regmap_update_bits(pcf2123->map, PCF2123_REG_CTRL2, CTRL2_AF, 0); + if (ret) + return ret; + + /* Set new alarm */ + txbuf[0] = bin2bcd(alm->time.tm_min & 0x7F); + txbuf[1] = bin2bcd(alm->time.tm_hour & 0x3F); + txbuf[2] = bin2bcd(alm->time.tm_mday & 0x3F); + txbuf[3] = ALRM_DISABLE; + + ret = regmap_bulk_write(pcf2123->map, PCF2123_REG_ALRM_MN, txbuf, + sizeof(txbuf)); + if (ret) + return ret; + + return pcf2123_rtc_alarm_irq_enable(dev, alm->enabled); +} + +static irqreturn_t pcf2123_rtc_irq(int irq, void *dev) +{ + struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); + unsigned int val = 0; + int ret = IRQ_NONE; + + rtc_lock(pcf2123->rtc); + regmap_read(pcf2123->map, PCF2123_REG_CTRL2, &val); + + /* Alarm? */ + if (val & CTRL2_AF) { + ret = IRQ_HANDLED; + + /* Clear alarm flag */ + regmap_update_bits(pcf2123->map, PCF2123_REG_CTRL2, CTRL2_AF, 0); + + rtc_update_irq(pcf2123->rtc, 1, RTC_IRQF | RTC_AF); + } + + rtc_unlock(pcf2123->rtc); + + return ret; +} + +static int pcf2123_reset(struct device *dev) +{ + struct pcf2123_data *pcf2123 = dev_get_drvdata(dev); + int ret; + unsigned int val = 0; + + ret = regmap_write(pcf2123->map, PCF2123_REG_CTRL1, CTRL1_SW_RESET); + if (ret) + return ret; + + /* Stop the counter */ + dev_dbg(dev, "stopping RTC\n"); + ret = regmap_write(pcf2123->map, PCF2123_REG_CTRL1, CTRL1_STOP); + if (ret) + return ret; + + /* See if the counter was actually stopped */ + dev_dbg(dev, "checking for presence of RTC\n"); + ret = regmap_read(pcf2123->map, PCF2123_REG_CTRL1, &val); + if (ret) + return ret; + + dev_dbg(dev, "received data from RTC (0x%08X)\n", val); + if (!(val & CTRL1_STOP)) + return -ENODEV; + + /* Start the counter */ + ret = regmap_write(pcf2123->map, PCF2123_REG_CTRL1, CTRL1_CLEAR); + if (ret) + return ret; + + return 0; +} + +static const struct rtc_class_ops pcf2123_rtc_ops = { + .read_time = pcf2123_rtc_read_time, + .set_time = pcf2123_rtc_set_time, + .read_offset = pcf2123_read_offset, + .set_offset = pcf2123_set_offset, + .read_alarm = pcf2123_rtc_read_alarm, + .set_alarm = pcf2123_rtc_set_alarm, + .alarm_irq_enable = pcf2123_rtc_alarm_irq_enable, +}; + +static int pcf2123_probe(struct spi_device *spi) +{ + struct rtc_device *rtc; + struct rtc_time tm; + struct pcf2123_data *pcf2123; + int ret = 0; + + pcf2123 = devm_kzalloc(&spi->dev, sizeof(struct pcf2123_data), + GFP_KERNEL); + if (!pcf2123) + return -ENOMEM; + + dev_set_drvdata(&spi->dev, pcf2123); + + pcf2123->map = devm_regmap_init_spi(spi, &pcf2123_regmap_config); + if (IS_ERR(pcf2123->map)) { + dev_err(&spi->dev, "regmap init failed.\n"); + return PTR_ERR(pcf2123->map); + } + + ret = pcf2123_rtc_read_time(&spi->dev, &tm); + if (ret < 0) { + ret = pcf2123_reset(&spi->dev); + if (ret < 0) { + dev_err(&spi->dev, "chip not found\n"); + return ret; + } + } + + dev_info(&spi->dev, "spiclk %u KHz.\n", + (spi->max_speed_hz + 500) / 1000); + + /* Finalize the initialization */ + rtc = devm_rtc_allocate_device(&spi->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + pcf2123->rtc = rtc; + + /* Register alarm irq */ + if (spi->irq > 0) { + unsigned long irqflags = IRQF_TRIGGER_LOW; + + if (dev_fwnode(&spi->dev)) + irqflags = 0; + + ret = devm_request_threaded_irq(&spi->dev, spi->irq, NULL, + pcf2123_rtc_irq, + irqflags | IRQF_ONESHOT, + pcf2123_driver.driver.name, &spi->dev); + if (!ret) + device_init_wakeup(&spi->dev, true); + else + dev_err(&spi->dev, "could not request irq.\n"); + } + + /* The PCF2123's alarm only has minute accuracy. Must add timer + * support to this driver to generate interrupts more than once + * per minute. + */ + set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->features); + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features); + rtc->ops = &pcf2123_rtc_ops; + rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rtc->range_max = RTC_TIMESTAMP_END_2099; + rtc->set_start_time = true; + + ret = devm_rtc_register_device(rtc); + if (ret) + return ret; + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id pcf2123_dt_ids[] = { + { .compatible = "nxp,pcf2123", }, + { .compatible = "microcrystal,rv2123", }, + /* Deprecated, do not use */ + { .compatible = "nxp,rtc-pcf2123", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, pcf2123_dt_ids); +#endif + +static const struct spi_device_id pcf2123_spi_ids[] = { + { .name = "pcf2123", }, + { .name = "rv2123", }, + { .name = "rtc-pcf2123", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(spi, pcf2123_spi_ids); + +static struct spi_driver pcf2123_driver = { + .driver = { + .name = "rtc-pcf2123", + .of_match_table = of_match_ptr(pcf2123_dt_ids), + }, + .probe = pcf2123_probe, + .id_table = pcf2123_spi_ids, +}; + +module_spi_driver(pcf2123_driver); + +MODULE_AUTHOR("Chris Verges "); +MODULE_DESCRIPTION("NXP PCF2123 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c new file mode 100644 index 000000000..ff2023908 --- /dev/null +++ b/drivers/rtc/rtc-pcf2127.c @@ -0,0 +1,1631 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * An I2C and SPI driver for the NXP PCF2127/29/31 RTC + * Copyright 2013 Til-Technologies + * + * Author: Renaud Cerrato + * + * Watchdog and tamper functions + * Author: Bruno Thomsen + * + * PCF2131 support + * Author: Hugo Villeneuve + * + * based on the other drivers in this same directory. + * + * Datasheets: https://www.nxp.com/docs/en/data-sheet/PCF2127.pdf + * https://www.nxp.com/docs/en/data-sheet/PCF2131DS.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Control register 1 */ +#define PCF2127_REG_CTRL1 0x00 +#define PCF2127_BIT_CTRL1_POR_OVRD BIT(3) +#define PCF2127_BIT_CTRL1_TSF1 BIT(4) +#define PCF2127_BIT_CTRL1_STOP BIT(5) +/* Control register 2 */ +#define PCF2127_REG_CTRL2 0x01 +#define PCF2127_BIT_CTRL2_AIE BIT(1) +#define PCF2127_BIT_CTRL2_TSIE BIT(2) +#define PCF2127_BIT_CTRL2_AF BIT(4) +#define PCF2127_BIT_CTRL2_TSF2 BIT(5) +#define PCF2127_BIT_CTRL2_WDTF BIT(6) +#define PCF2127_BIT_CTRL2_MSF BIT(7) +/* Control register 3 */ +#define PCF2127_REG_CTRL3 0x02 +#define PCF2127_BIT_CTRL3_BLIE BIT(0) +#define PCF2127_BIT_CTRL3_BIE BIT(1) +#define PCF2127_BIT_CTRL3_BLF BIT(2) +#define PCF2127_BIT_CTRL3_BF BIT(3) +#define PCF2127_BIT_CTRL3_BTSE BIT(4) +#define PCF2127_CTRL3_PM GENMASK(7, 5) +/* Time and date registers */ +#define PCF2127_REG_TIME_BASE 0x03 +#define PCF2127_BIT_SC_OSF BIT(7) +/* Alarm registers */ +#define PCF2127_REG_ALARM_BASE 0x0A +#define PCF2127_BIT_ALARM_AE BIT(7) +/* CLKOUT control register */ +#define PCF2127_REG_CLKOUT 0x0f +#define PCF2127_BIT_CLKOUT_OTPR BIT(5) +/* Watchdog registers */ +#define PCF2127_REG_WD_CTL 0x10 +#define PCF2127_BIT_WD_CTL_TF0 BIT(0) +#define PCF2127_BIT_WD_CTL_TF1 BIT(1) +#define PCF2127_BIT_WD_CTL_CD0 BIT(6) +#define PCF2127_BIT_WD_CTL_CD1 BIT(7) +#define PCF2127_REG_WD_VAL 0x11 +/* Tamper timestamp1 registers */ +#define PCF2127_REG_TS1_BASE 0x12 +#define PCF2127_BIT_TS_CTRL_TSOFF BIT(6) +#define PCF2127_BIT_TS_CTRL_TSM BIT(7) +/* + * RAM registers + * PCF2127 has 512 bytes general-purpose static RAM (SRAM) that is + * battery backed and can survive a power outage. + * PCF2129/31 doesn't have this feature. + */ +#define PCF2127_REG_RAM_ADDR_MSB 0x1A +#define PCF2127_REG_RAM_WRT_CMD 0x1C +#define PCF2127_REG_RAM_RD_CMD 0x1D + +/* Watchdog timer value constants */ +#define PCF2127_WD_VAL_STOP 0 +/* PCF2127/29 watchdog timer value constants */ +#define PCF2127_WD_CLOCK_HZ_X1000 1000 /* 1Hz */ +#define PCF2127_WD_MIN_HW_HEARTBEAT_MS 500 +/* PCF2131 watchdog timer value constants */ +#define PCF2131_WD_CLOCK_HZ_X1000 250 /* 1/4Hz */ +#define PCF2131_WD_MIN_HW_HEARTBEAT_MS 4000 + +#define PCF2127_WD_DEFAULT_TIMEOUT_S 60 + +/* Mask for currently enabled interrupts */ +#define PCF2127_CTRL1_IRQ_MASK (PCF2127_BIT_CTRL1_TSF1) +#define PCF2127_CTRL2_IRQ_MASK ( \ + PCF2127_BIT_CTRL2_AF | \ + PCF2127_BIT_CTRL2_WDTF | \ + PCF2127_BIT_CTRL2_TSF2 | \ + PCF2127_BIT_CTRL2_MSF) + +#define PCF2127_MAX_TS_SUPPORTED 4 + +/* Control register 4 */ +#define PCF2131_REG_CTRL4 0x03 +#define PCF2131_BIT_CTRL4_TSF4 BIT(4) +#define PCF2131_BIT_CTRL4_TSF3 BIT(5) +#define PCF2131_BIT_CTRL4_TSF2 BIT(6) +#define PCF2131_BIT_CTRL4_TSF1 BIT(7) +/* Control register 5 */ +#define PCF2131_REG_CTRL5 0x04 +#define PCF2131_BIT_CTRL5_TSIE4 BIT(4) +#define PCF2131_BIT_CTRL5_TSIE3 BIT(5) +#define PCF2131_BIT_CTRL5_TSIE2 BIT(6) +#define PCF2131_BIT_CTRL5_TSIE1 BIT(7) +/* Software reset register */ +#define PCF2131_REG_SR_RESET 0x05 +#define PCF2131_SR_RESET_READ_PATTERN (BIT(2) | BIT(5)) +#define PCF2131_SR_RESET_CPR_CMD (PCF2131_SR_RESET_READ_PATTERN | BIT(7)) +/* Time and date registers */ +#define PCF2131_REG_TIME_BASE 0x07 +/* Alarm registers */ +#define PCF2131_REG_ALARM_BASE 0x0E +/* CLKOUT control register */ +#define PCF2131_REG_CLKOUT 0x13 +/* Watchdog registers */ +#define PCF2131_REG_WD_CTL 0x35 +#define PCF2131_REG_WD_VAL 0x36 +/* Tamper timestamp1 registers */ +#define PCF2131_REG_TS1_BASE 0x14 +/* Tamper timestamp2 registers */ +#define PCF2131_REG_TS2_BASE 0x1B +/* Tamper timestamp3 registers */ +#define PCF2131_REG_TS3_BASE 0x22 +/* Tamper timestamp4 registers */ +#define PCF2131_REG_TS4_BASE 0x29 +/* Interrupt mask registers */ +#define PCF2131_REG_INT_A_MASK1 0x31 +#define PCF2131_REG_INT_A_MASK2 0x32 +#define PCF2131_REG_INT_B_MASK1 0x33 +#define PCF2131_REG_INT_B_MASK2 0x34 +#define PCF2131_BIT_INT_BLIE BIT(0) +#define PCF2131_BIT_INT_BIE BIT(1) +#define PCF2131_BIT_INT_AIE BIT(2) +#define PCF2131_BIT_INT_WD_CD BIT(3) +#define PCF2131_BIT_INT_SI BIT(4) +#define PCF2131_BIT_INT_MI BIT(5) +#define PCF2131_CTRL2_IRQ_MASK ( \ + PCF2127_BIT_CTRL2_AF | \ + PCF2127_BIT_CTRL2_WDTF) +#define PCF2131_CTRL4_IRQ_MASK ( \ + PCF2131_BIT_CTRL4_TSF4 | \ + PCF2131_BIT_CTRL4_TSF3 | \ + PCF2131_BIT_CTRL4_TSF2 | \ + PCF2131_BIT_CTRL4_TSF1) + +enum pcf21xx_type { + PCF2127, + PCF2129, + PCF2131, + PCF21XX_LAST_ID +}; + +struct pcf21xx_ts_config { + u8 reg_base; /* Base register to read timestamp values. */ + + /* + * If the TS input pin is driven to GND, an interrupt can be generated + * (supported by all variants). + */ + u8 gnd_detect_reg; /* Interrupt control register address. */ + u8 gnd_detect_bit; /* Interrupt bit. */ + + /* + * If the TS input pin is driven to an intermediate level between GND + * and supply, an interrupt can be generated (optional feature depending + * on variant). + */ + u8 inter_detect_reg; /* Interrupt control register address. */ + u8 inter_detect_bit; /* Interrupt bit. */ + + u8 ie_reg; /* Interrupt enable control register. */ + u8 ie_bit; /* Interrupt enable bit. */ +}; + +struct pcf21xx_config { + int type; /* IC variant */ + int max_register; + unsigned int has_nvmem:1; + unsigned int has_bit_wd_ctl_cd0:1; + unsigned int wd_val_reg_readable:1; /* If watchdog value register can be read. */ + unsigned int has_int_a_b:1; /* PCF2131 supports two interrupt outputs. */ + u8 reg_time_base; /* Time/date base register. */ + u8 regs_alarm_base; /* Alarm function base registers. */ + u8 reg_wd_ctl; /* Watchdog control register. */ + u8 reg_wd_val; /* Watchdog value register. */ + u8 reg_clkout; /* Clkout register. */ + int wdd_clock_hz_x1000; /* Watchdog clock in Hz multiplicated by 1000 */ + int wdd_min_hw_heartbeat_ms; + unsigned int ts_count; + struct pcf21xx_ts_config ts[PCF2127_MAX_TS_SUPPORTED]; + struct attribute_group attribute_group; +}; + +struct pcf2127 { + struct rtc_device *rtc; + struct watchdog_device wdd; + struct regmap *regmap; + const struct pcf21xx_config *cfg; + bool irq_enabled; + time64_t ts[PCF2127_MAX_TS_SUPPORTED]; /* Timestamp values. */ + bool ts_valid[PCF2127_MAX_TS_SUPPORTED]; /* Timestamp valid indication. */ +}; + +/* + * In the routines that deal directly with the pcf2127 hardware, we use + * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. + */ +static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct pcf2127 *pcf2127 = dev_get_drvdata(dev); + unsigned char buf[7]; + int ret; + + /* + * Avoid reading CTRL2 register as it causes WD_VAL register + * value to reset to 0 which means watchdog is stopped. + */ + ret = regmap_bulk_read(pcf2127->regmap, pcf2127->cfg->reg_time_base, + buf, sizeof(buf)); + if (ret) { + dev_err(dev, "%s: read error\n", __func__); + return ret; + } + + /* Clock integrity is not guaranteed when OSF flag is set. */ + if (buf[0] & PCF2127_BIT_SC_OSF) { + /* + * no need clear the flag here, + * it will be cleared once the new date is saved + */ + dev_warn(dev, + "oscillator stop detected, date/time is not reliable\n"); + return -EINVAL; + } + + dev_dbg(dev, + "%s: raw data is sec=%02x, min=%02x, hr=%02x, " + "mday=%02x, wday=%02x, mon=%02x, year=%02x\n", + __func__, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); + + tm->tm_sec = bcd2bin(buf[0] & 0x7F); + tm->tm_min = bcd2bin(buf[1] & 0x7F); + tm->tm_hour = bcd2bin(buf[2] & 0x3F); + tm->tm_mday = bcd2bin(buf[3] & 0x3F); + tm->tm_wday = buf[4] & 0x07; + tm->tm_mon = bcd2bin(buf[5] & 0x1F) - 1; + tm->tm_year = bcd2bin(buf[6]); + tm->tm_year += 100; + + dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + return 0; +} + +static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct pcf2127 *pcf2127 = dev_get_drvdata(dev); + unsigned char buf[7]; + int i = 0, err; + + dev_dbg(dev, "%s: secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + /* hours, minutes and seconds */ + buf[i++] = bin2bcd(tm->tm_sec); /* this will also clear OSF flag */ + buf[i++] = bin2bcd(tm->tm_min); + buf[i++] = bin2bcd(tm->tm_hour); + buf[i++] = bin2bcd(tm->tm_mday); + buf[i++] = tm->tm_wday & 0x07; + + /* month, 1 - 12 */ + buf[i++] = bin2bcd(tm->tm_mon + 1); + + /* year */ + buf[i++] = bin2bcd(tm->tm_year - 100); + + /* Write access to time registers: + * PCF2127/29: no special action required. + * PCF2131: requires setting the STOP and CPR bits. STOP bit needs to + * be cleared after time registers are updated. + */ + if (pcf2127->cfg->type == PCF2131) { + err = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL1, + PCF2127_BIT_CTRL1_STOP, + PCF2127_BIT_CTRL1_STOP); + if (err) { + dev_dbg(dev, "setting STOP bit failed\n"); + return err; + } + + err = regmap_write(pcf2127->regmap, PCF2131_REG_SR_RESET, + PCF2131_SR_RESET_CPR_CMD); + if (err) { + dev_dbg(dev, "sending CPR cmd failed\n"); + return err; + } + } + + /* write time register's data */ + err = regmap_bulk_write(pcf2127->regmap, pcf2127->cfg->reg_time_base, buf, i); + if (err) { + dev_dbg(dev, "%s: err=%d", __func__, err); + return err; + } + + if (pcf2127->cfg->type == PCF2131) { + /* Clear STOP bit (PCF2131 only) after write is completed. */ + err = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL1, + PCF2127_BIT_CTRL1_STOP, 0); + if (err) { + dev_dbg(dev, "clearing STOP bit failed\n"); + return err; + } + } + + return 0; +} + +static int pcf2127_param_get(struct device *dev, struct rtc_param *param) +{ + struct pcf2127 *pcf2127 = dev_get_drvdata(dev); + u32 value; + int ret; + + switch (param->param) { + case RTC_PARAM_BACKUP_SWITCH_MODE: + ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL3, &value); + if (ret < 0) + return ret; + + value = FIELD_GET(PCF2127_CTRL3_PM, value); + + if (value < 0x3) + param->uvalue = RTC_BSM_LEVEL; + else if (value < 0x6) + param->uvalue = RTC_BSM_DIRECT; + else + param->uvalue = RTC_BSM_DISABLED; + + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int pcf2127_param_set(struct device *dev, struct rtc_param *param) +{ + struct pcf2127 *pcf2127 = dev_get_drvdata(dev); + u8 mode = 0; + u32 value; + int ret; + + switch (param->param) { + case RTC_PARAM_BACKUP_SWITCH_MODE: + ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL3, &value); + if (ret < 0) + return ret; + + value = FIELD_GET(PCF2127_CTRL3_PM, value); + + if (value > 5) + value -= 5; + else if (value > 2) + value -= 3; + + switch (param->uvalue) { + case RTC_BSM_LEVEL: + break; + case RTC_BSM_DIRECT: + mode = 3; + break; + case RTC_BSM_DISABLED: + if (value == 0) + value = 1; + mode = 5; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL3, + PCF2127_CTRL3_PM, + FIELD_PREP(PCF2127_CTRL3_PM, mode + value)); + + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int pcf2127_rtc_ioctl(struct device *dev, + unsigned int cmd, unsigned long arg) +{ + struct pcf2127 *pcf2127 = dev_get_drvdata(dev); + int val, touser = 0; + int ret; + + switch (cmd) { + case RTC_VL_READ: + ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL3, &val); + if (ret) + return ret; + + if (val & PCF2127_BIT_CTRL3_BLF) + touser |= RTC_VL_BACKUP_LOW; + + if (val & PCF2127_BIT_CTRL3_BF) + touser |= RTC_VL_BACKUP_SWITCH; + + return put_user(touser, (unsigned int __user *)arg); + + case RTC_VL_CLR: + return regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL3, + PCF2127_BIT_CTRL3_BF, 0); + + default: + return -ENOIOCTLCMD; + } +} + +static int pcf2127_nvmem_read(void *priv, unsigned int offset, + void *val, size_t bytes) +{ + struct pcf2127 *pcf2127 = priv; + int ret; + unsigned char offsetbuf[] = { offset >> 8, offset }; + + ret = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_RAM_ADDR_MSB, + offsetbuf, 2); + if (ret) + return ret; + + return regmap_bulk_read(pcf2127->regmap, PCF2127_REG_RAM_RD_CMD, + val, bytes); +} + +static int pcf2127_nvmem_write(void *priv, unsigned int offset, + void *val, size_t bytes) +{ + struct pcf2127 *pcf2127 = priv; + int ret; + unsigned char offsetbuf[] = { offset >> 8, offset }; + + ret = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_RAM_ADDR_MSB, + offsetbuf, 2); + if (ret) + return ret; + + return regmap_bulk_write(pcf2127->regmap, PCF2127_REG_RAM_WRT_CMD, + val, bytes); +} + +/* watchdog driver */ + +static int pcf2127_wdt_ping(struct watchdog_device *wdd) +{ + int wd_val; + struct pcf2127 *pcf2127 = watchdog_get_drvdata(wdd); + + /* + * Compute counter value of WATCHDG_TIM_VAL to obtain desired period + * in seconds, depending on the source clock frequency. + */ + wd_val = ((wdd->timeout * pcf2127->cfg->wdd_clock_hz_x1000) / 1000) + 1; + + return regmap_write(pcf2127->regmap, pcf2127->cfg->reg_wd_val, wd_val); +} + +/* + * Restart watchdog timer if feature is active. + * + * Note: Reading CTRL2 register causes watchdog to stop which is unfortunate, + * since register also contain control/status flags for other features. + * Always call this function after reading CTRL2 register. + */ +static int pcf2127_wdt_active_ping(struct watchdog_device *wdd) +{ + int ret = 0; + + if (watchdog_active(wdd)) { + ret = pcf2127_wdt_ping(wdd); + if (ret) + dev_err(wdd->parent, + "%s: watchdog restart failed, ret=%d\n", + __func__, ret); + } + + return ret; +} + +static int pcf2127_wdt_start(struct watchdog_device *wdd) +{ + return pcf2127_wdt_ping(wdd); +} + +static int pcf2127_wdt_stop(struct watchdog_device *wdd) +{ + struct pcf2127 *pcf2127 = watchdog_get_drvdata(wdd); + + return regmap_write(pcf2127->regmap, pcf2127->cfg->reg_wd_val, + PCF2127_WD_VAL_STOP); +} + +static int pcf2127_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int new_timeout) +{ + dev_dbg(wdd->parent, "new watchdog timeout: %is (old: %is)\n", + new_timeout, wdd->timeout); + + wdd->timeout = new_timeout; + + return pcf2127_wdt_active_ping(wdd); +} + +static const struct watchdog_info pcf2127_wdt_info = { + .identity = "NXP PCF2127/PCF2129 Watchdog", + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, +}; + +static const struct watchdog_ops pcf2127_watchdog_ops = { + .owner = THIS_MODULE, + .start = pcf2127_wdt_start, + .stop = pcf2127_wdt_stop, + .ping = pcf2127_wdt_ping, + .set_timeout = pcf2127_wdt_set_timeout, +}; + +/* + * Compute watchdog period, t, in seconds, from the WATCHDG_TIM_VAL register + * value, n, and the clock frequency, f1000, in Hz x 1000. + * + * The PCF2127/29 datasheet gives t as: + * t = n / f + * The PCF2131 datasheet gives t as: + * t = (n - 1) / f + * For both variants, the watchdog is triggered when the WATCHDG_TIM_VAL reaches + * the value 1, and not zero. Consequently, the equation from the PCF2131 + * datasheet seems to be the correct one for both variants. + */ +static int pcf2127_watchdog_get_period(int n, int f1000) +{ + return (1000 * (n - 1)) / f1000; +} + +static int pcf2127_watchdog_init(struct device *dev, struct pcf2127 *pcf2127) +{ + int ret; + + if (!IS_ENABLED(CONFIG_WATCHDOG) || + !device_property_read_bool(dev, "reset-source")) + return 0; + + pcf2127->wdd.parent = dev; + pcf2127->wdd.info = &pcf2127_wdt_info; + pcf2127->wdd.ops = &pcf2127_watchdog_ops; + + pcf2127->wdd.min_timeout = + pcf2127_watchdog_get_period( + 2, pcf2127->cfg->wdd_clock_hz_x1000); + pcf2127->wdd.max_timeout = + pcf2127_watchdog_get_period( + 255, pcf2127->cfg->wdd_clock_hz_x1000); + pcf2127->wdd.timeout = PCF2127_WD_DEFAULT_TIMEOUT_S; + + dev_dbg(dev, "%s clock = %d Hz / 1000\n", __func__, + pcf2127->cfg->wdd_clock_hz_x1000); + + pcf2127->wdd.min_hw_heartbeat_ms = pcf2127->cfg->wdd_min_hw_heartbeat_ms; + pcf2127->wdd.status = WATCHDOG_NOWAYOUT_INIT_STATUS; + + watchdog_set_drvdata(&pcf2127->wdd, pcf2127); + + /* Test if watchdog timer is started by bootloader */ + if (pcf2127->cfg->wd_val_reg_readable) { + u32 wdd_timeout; + + ret = regmap_read(pcf2127->regmap, pcf2127->cfg->reg_wd_val, + &wdd_timeout); + if (ret) + return ret; + + if (wdd_timeout) + set_bit(WDOG_HW_RUNNING, &pcf2127->wdd.status); + } + + /* + * When using interrupt pin (INT A) as watchdog output, only allow + * watchdog interrupt (PCF2131_BIT_INT_WD_CD) and disable (mask) all + * other interrupts. + */ + if (pcf2127->cfg->type == PCF2131) { + ret = regmap_write(pcf2127->regmap, + PCF2131_REG_INT_A_MASK1, + PCF2131_BIT_INT_BLIE | + PCF2131_BIT_INT_BIE | + PCF2131_BIT_INT_AIE | + PCF2131_BIT_INT_SI | + PCF2131_BIT_INT_MI); + } + + return devm_watchdog_register_device(dev, &pcf2127->wdd); +} + +/* Alarm */ +static int pcf2127_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pcf2127 *pcf2127 = dev_get_drvdata(dev); + u8 buf[5]; + unsigned int ctrl2; + int ret; + + ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL2, &ctrl2); + if (ret) + return ret; + + ret = pcf2127_wdt_active_ping(&pcf2127->wdd); + if (ret) + return ret; + + ret = regmap_bulk_read(pcf2127->regmap, pcf2127->cfg->regs_alarm_base, + buf, sizeof(buf)); + if (ret) + return ret; + + alrm->enabled = ctrl2 & PCF2127_BIT_CTRL2_AIE; + alrm->pending = ctrl2 & PCF2127_BIT_CTRL2_AF; + + alrm->time.tm_sec = bcd2bin(buf[0] & 0x7F); + alrm->time.tm_min = bcd2bin(buf[1] & 0x7F); + alrm->time.tm_hour = bcd2bin(buf[2] & 0x3F); + alrm->time.tm_mday = bcd2bin(buf[3] & 0x3F); + + return 0; +} + +static int pcf2127_rtc_alarm_irq_enable(struct device *dev, u32 enable) +{ + struct pcf2127 *pcf2127 = dev_get_drvdata(dev); + int ret; + + ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL2, + PCF2127_BIT_CTRL2_AIE, + enable ? PCF2127_BIT_CTRL2_AIE : 0); + if (ret) + return ret; + + return pcf2127_wdt_active_ping(&pcf2127->wdd); +} + +static int pcf2127_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pcf2127 *pcf2127 = dev_get_drvdata(dev); + uint8_t buf[5]; + int ret; + + ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL2, + PCF2127_BIT_CTRL2_AF, 0); + if (ret) + return ret; + + ret = pcf2127_wdt_active_ping(&pcf2127->wdd); + if (ret) + return ret; + + buf[0] = bin2bcd(alrm->time.tm_sec); + buf[1] = bin2bcd(alrm->time.tm_min); + buf[2] = bin2bcd(alrm->time.tm_hour); + buf[3] = bin2bcd(alrm->time.tm_mday); + buf[4] = PCF2127_BIT_ALARM_AE; /* Do not match on week day */ + + ret = regmap_bulk_write(pcf2127->regmap, pcf2127->cfg->regs_alarm_base, + buf, sizeof(buf)); + if (ret) + return ret; + + return pcf2127_rtc_alarm_irq_enable(dev, alrm->enabled); +} + +/* + * This function reads one timestamp function data, caller is responsible for + * calling pcf2127_wdt_active_ping() + */ +static int pcf2127_rtc_ts_read(struct device *dev, time64_t *ts, + int ts_id) +{ + struct pcf2127 *pcf2127 = dev_get_drvdata(dev); + struct rtc_time tm; + int ret; + unsigned char data[7]; + + ret = regmap_bulk_read(pcf2127->regmap, pcf2127->cfg->ts[ts_id].reg_base, + data, sizeof(data)); + if (ret) { + dev_err(dev, "%s: read error ret=%d\n", __func__, ret); + return ret; + } + + dev_dbg(dev, + "%s: raw data is ts_sc=%02x, ts_mn=%02x, ts_hr=%02x, ts_dm=%02x, ts_mo=%02x, ts_yr=%02x\n", + __func__, data[1], data[2], data[3], data[4], data[5], data[6]); + + tm.tm_sec = bcd2bin(data[1] & 0x7F); + tm.tm_min = bcd2bin(data[2] & 0x7F); + tm.tm_hour = bcd2bin(data[3] & 0x3F); + tm.tm_mday = bcd2bin(data[4] & 0x3F); + /* TS_MO register (month) value range: 1-12 */ + tm.tm_mon = bcd2bin(data[5] & 0x1F) - 1; + tm.tm_year = bcd2bin(data[6]); + if (tm.tm_year < 70) + tm.tm_year += 100; /* assume we are in 1970...2069 */ + + ret = rtc_valid_tm(&tm); + if (ret) { + dev_err(dev, "Invalid timestamp. ret=%d\n", ret); + return ret; + } + + *ts = rtc_tm_to_time64(&tm); + return 0; +}; + +static void pcf2127_rtc_ts_snapshot(struct device *dev, int ts_id) +{ + struct pcf2127 *pcf2127 = dev_get_drvdata(dev); + int ret; + + if (ts_id >= pcf2127->cfg->ts_count) + return; + + /* Let userspace read the first timestamp */ + if (pcf2127->ts_valid[ts_id]) + return; + + ret = pcf2127_rtc_ts_read(dev, &pcf2127->ts[ts_id], ts_id); + if (!ret) + pcf2127->ts_valid[ts_id] = true; +} + +static irqreturn_t pcf2127_rtc_irq(int irq, void *dev) +{ + struct pcf2127 *pcf2127 = dev_get_drvdata(dev); + unsigned int ctrl2; + int ret = 0; + + ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL2, &ctrl2); + if (ret) + return IRQ_NONE; + + if (pcf2127->cfg->ts_count == 1) { + /* PCF2127/29 */ + unsigned int ctrl1; + + ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL1, &ctrl1); + if (ret) + return IRQ_NONE; + + if (!(ctrl1 & PCF2127_CTRL1_IRQ_MASK || ctrl2 & PCF2127_CTRL2_IRQ_MASK)) + return IRQ_NONE; + + if (ctrl1 & PCF2127_BIT_CTRL1_TSF1 || ctrl2 & PCF2127_BIT_CTRL2_TSF2) + pcf2127_rtc_ts_snapshot(dev, 0); + + if (ctrl1 & PCF2127_CTRL1_IRQ_MASK) + regmap_write(pcf2127->regmap, PCF2127_REG_CTRL1, + ctrl1 & ~PCF2127_CTRL1_IRQ_MASK); + + if (ctrl2 & PCF2127_CTRL2_IRQ_MASK) + regmap_write(pcf2127->regmap, PCF2127_REG_CTRL2, + ctrl2 & ~PCF2127_CTRL2_IRQ_MASK); + } else { + /* PCF2131. */ + unsigned int ctrl4; + + ret = regmap_read(pcf2127->regmap, PCF2131_REG_CTRL4, &ctrl4); + if (ret) + return IRQ_NONE; + + if (!(ctrl4 & PCF2131_CTRL4_IRQ_MASK || ctrl2 & PCF2131_CTRL2_IRQ_MASK)) + return IRQ_NONE; + + if (ctrl4 & PCF2131_CTRL4_IRQ_MASK) { + int i; + int tsf_bit = PCF2131_BIT_CTRL4_TSF1; /* Start at bit 7. */ + + for (i = 0; i < pcf2127->cfg->ts_count; i++) { + if (ctrl4 & tsf_bit) + pcf2127_rtc_ts_snapshot(dev, i); + + tsf_bit = tsf_bit >> 1; + } + + regmap_write(pcf2127->regmap, PCF2131_REG_CTRL4, + ctrl4 & ~PCF2131_CTRL4_IRQ_MASK); + } + + if (ctrl2 & PCF2131_CTRL2_IRQ_MASK) + regmap_write(pcf2127->regmap, PCF2127_REG_CTRL2, + ctrl2 & ~PCF2131_CTRL2_IRQ_MASK); + } + + if (ctrl2 & PCF2127_BIT_CTRL2_AF) + rtc_update_irq(pcf2127->rtc, 1, RTC_IRQF | RTC_AF); + + pcf2127_wdt_active_ping(&pcf2127->wdd); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops pcf2127_rtc_ops = { + .ioctl = pcf2127_rtc_ioctl, + .read_time = pcf2127_rtc_read_time, + .set_time = pcf2127_rtc_set_time, + .read_alarm = pcf2127_rtc_read_alarm, + .set_alarm = pcf2127_rtc_set_alarm, + .alarm_irq_enable = pcf2127_rtc_alarm_irq_enable, + .param_get = pcf2127_param_get, + .param_set = pcf2127_param_set, +}; + +/* sysfs interface */ + +static ssize_t timestamp_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count, int ts_id) +{ + struct pcf2127 *pcf2127 = dev_get_drvdata(dev->parent); + int ret; + + if (ts_id >= pcf2127->cfg->ts_count) + return 0; + + if (pcf2127->irq_enabled) { + pcf2127->ts_valid[ts_id] = false; + } else { + /* Always clear GND interrupt bit. */ + ret = regmap_update_bits(pcf2127->regmap, + pcf2127->cfg->ts[ts_id].gnd_detect_reg, + pcf2127->cfg->ts[ts_id].gnd_detect_bit, + 0); + + if (ret) { + dev_err(dev, "%s: update TS gnd detect ret=%d\n", __func__, ret); + return ret; + } + + if (pcf2127->cfg->ts[ts_id].inter_detect_bit) { + /* Clear intermediate level interrupt bit if supported. */ + ret = regmap_update_bits(pcf2127->regmap, + pcf2127->cfg->ts[ts_id].inter_detect_reg, + pcf2127->cfg->ts[ts_id].inter_detect_bit, + 0); + if (ret) { + dev_err(dev, "%s: update TS intermediate level detect ret=%d\n", + __func__, ret); + return ret; + } + } + + ret = pcf2127_wdt_active_ping(&pcf2127->wdd); + if (ret) + return ret; + } + + return count; +} + +static ssize_t timestamp0_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return timestamp_store(dev, attr, buf, count, 0); +}; + +static ssize_t timestamp1_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return timestamp_store(dev, attr, buf, count, 1); +}; + +static ssize_t timestamp2_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return timestamp_store(dev, attr, buf, count, 2); +}; + +static ssize_t timestamp3_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return timestamp_store(dev, attr, buf, count, 3); +}; + +static ssize_t timestamp_show(struct device *dev, + struct device_attribute *attr, char *buf, + int ts_id) +{ + struct pcf2127 *pcf2127 = dev_get_drvdata(dev->parent); + int ret; + time64_t ts; + + if (ts_id >= pcf2127->cfg->ts_count) + return 0; + + if (pcf2127->irq_enabled) { + if (!pcf2127->ts_valid[ts_id]) + return 0; + ts = pcf2127->ts[ts_id]; + } else { + u8 valid_low = 0; + u8 valid_inter = 0; + unsigned int ctrl; + + /* Check if TS input pin is driven to GND, supported by all + * variants. + */ + ret = regmap_read(pcf2127->regmap, + pcf2127->cfg->ts[ts_id].gnd_detect_reg, + &ctrl); + if (ret) + return 0; + + valid_low = ctrl & pcf2127->cfg->ts[ts_id].gnd_detect_bit; + + if (pcf2127->cfg->ts[ts_id].inter_detect_bit) { + /* Check if TS input pin is driven to intermediate level + * between GND and supply, if supported by variant. + */ + ret = regmap_read(pcf2127->regmap, + pcf2127->cfg->ts[ts_id].inter_detect_reg, + &ctrl); + if (ret) + return 0; + + valid_inter = ctrl & pcf2127->cfg->ts[ts_id].inter_detect_bit; + } + + if (!valid_low && !valid_inter) + return 0; + + ret = pcf2127_rtc_ts_read(dev->parent, &ts, ts_id); + if (ret) + return 0; + + ret = pcf2127_wdt_active_ping(&pcf2127->wdd); + if (ret) + return ret; + } + return sprintf(buf, "%llu\n", (unsigned long long)ts); +} + +static ssize_t timestamp0_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return timestamp_show(dev, attr, buf, 0); +}; + +static ssize_t timestamp1_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return timestamp_show(dev, attr, buf, 1); +}; + +static ssize_t timestamp2_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return timestamp_show(dev, attr, buf, 2); +}; + +static ssize_t timestamp3_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return timestamp_show(dev, attr, buf, 3); +}; + +static DEVICE_ATTR_RW(timestamp0); +static DEVICE_ATTR_RW(timestamp1); +static DEVICE_ATTR_RW(timestamp2); +static DEVICE_ATTR_RW(timestamp3); + +static struct attribute *pcf2127_attrs[] = { + &dev_attr_timestamp0.attr, + NULL +}; + +static struct attribute *pcf2131_attrs[] = { + &dev_attr_timestamp0.attr, + &dev_attr_timestamp1.attr, + &dev_attr_timestamp2.attr, + &dev_attr_timestamp3.attr, + NULL +}; + +static struct pcf21xx_config pcf21xx_cfg[] = { + [PCF2127] = { + .type = PCF2127, + .max_register = 0x1d, + .has_nvmem = 1, + .has_bit_wd_ctl_cd0 = 1, + .wd_val_reg_readable = 1, + .has_int_a_b = 0, + .reg_time_base = PCF2127_REG_TIME_BASE, + .regs_alarm_base = PCF2127_REG_ALARM_BASE, + .reg_wd_ctl = PCF2127_REG_WD_CTL, + .reg_wd_val = PCF2127_REG_WD_VAL, + .reg_clkout = PCF2127_REG_CLKOUT, + .wdd_clock_hz_x1000 = PCF2127_WD_CLOCK_HZ_X1000, + .wdd_min_hw_heartbeat_ms = PCF2127_WD_MIN_HW_HEARTBEAT_MS, + .ts_count = 1, + .ts[0] = { + .reg_base = PCF2127_REG_TS1_BASE, + .gnd_detect_reg = PCF2127_REG_CTRL1, + .gnd_detect_bit = PCF2127_BIT_CTRL1_TSF1, + .inter_detect_reg = PCF2127_REG_CTRL2, + .inter_detect_bit = PCF2127_BIT_CTRL2_TSF2, + .ie_reg = PCF2127_REG_CTRL2, + .ie_bit = PCF2127_BIT_CTRL2_TSIE, + }, + .attribute_group = { + .attrs = pcf2127_attrs, + }, + }, + [PCF2129] = { + .type = PCF2129, + .max_register = 0x19, + .has_nvmem = 0, + .has_bit_wd_ctl_cd0 = 0, + .wd_val_reg_readable = 1, + .has_int_a_b = 0, + .reg_time_base = PCF2127_REG_TIME_BASE, + .regs_alarm_base = PCF2127_REG_ALARM_BASE, + .reg_wd_ctl = PCF2127_REG_WD_CTL, + .reg_wd_val = PCF2127_REG_WD_VAL, + .reg_clkout = PCF2127_REG_CLKOUT, + .wdd_clock_hz_x1000 = PCF2127_WD_CLOCK_HZ_X1000, + .wdd_min_hw_heartbeat_ms = PCF2127_WD_MIN_HW_HEARTBEAT_MS, + .ts_count = 1, + .ts[0] = { + .reg_base = PCF2127_REG_TS1_BASE, + .gnd_detect_reg = PCF2127_REG_CTRL1, + .gnd_detect_bit = PCF2127_BIT_CTRL1_TSF1, + .inter_detect_reg = PCF2127_REG_CTRL2, + .inter_detect_bit = PCF2127_BIT_CTRL2_TSF2, + .ie_reg = PCF2127_REG_CTRL2, + .ie_bit = PCF2127_BIT_CTRL2_TSIE, + }, + .attribute_group = { + .attrs = pcf2127_attrs, + }, + }, + [PCF2131] = { + .type = PCF2131, + .max_register = 0x36, + .has_nvmem = 0, + .has_bit_wd_ctl_cd0 = 0, + .wd_val_reg_readable = 0, + .has_int_a_b = 1, + .reg_time_base = PCF2131_REG_TIME_BASE, + .regs_alarm_base = PCF2131_REG_ALARM_BASE, + .reg_wd_ctl = PCF2131_REG_WD_CTL, + .reg_wd_val = PCF2131_REG_WD_VAL, + .reg_clkout = PCF2131_REG_CLKOUT, + .wdd_clock_hz_x1000 = PCF2131_WD_CLOCK_HZ_X1000, + .wdd_min_hw_heartbeat_ms = PCF2131_WD_MIN_HW_HEARTBEAT_MS, + .ts_count = 4, + .ts[0] = { + .reg_base = PCF2131_REG_TS1_BASE, + .gnd_detect_reg = PCF2131_REG_CTRL4, + .gnd_detect_bit = PCF2131_BIT_CTRL4_TSF1, + .inter_detect_bit = 0, + .ie_reg = PCF2131_REG_CTRL5, + .ie_bit = PCF2131_BIT_CTRL5_TSIE1, + }, + .ts[1] = { + .reg_base = PCF2131_REG_TS2_BASE, + .gnd_detect_reg = PCF2131_REG_CTRL4, + .gnd_detect_bit = PCF2131_BIT_CTRL4_TSF2, + .inter_detect_bit = 0, + .ie_reg = PCF2131_REG_CTRL5, + .ie_bit = PCF2131_BIT_CTRL5_TSIE2, + }, + .ts[2] = { + .reg_base = PCF2131_REG_TS3_BASE, + .gnd_detect_reg = PCF2131_REG_CTRL4, + .gnd_detect_bit = PCF2131_BIT_CTRL4_TSF3, + .inter_detect_bit = 0, + .ie_reg = PCF2131_REG_CTRL5, + .ie_bit = PCF2131_BIT_CTRL5_TSIE3, + }, + .ts[3] = { + .reg_base = PCF2131_REG_TS4_BASE, + .gnd_detect_reg = PCF2131_REG_CTRL4, + .gnd_detect_bit = PCF2131_BIT_CTRL4_TSF4, + .inter_detect_bit = 0, + .ie_reg = PCF2131_REG_CTRL5, + .ie_bit = PCF2131_BIT_CTRL5_TSIE4, + }, + .attribute_group = { + .attrs = pcf2131_attrs, + }, + }, +}; + +/* + * Enable timestamp function and corresponding interrupt(s). + */ +static int pcf2127_enable_ts(struct device *dev, int ts_id) +{ + struct pcf2127 *pcf2127 = dev_get_drvdata(dev); + int ret; + + if (ts_id >= pcf2127->cfg->ts_count) { + dev_err(dev, "%s: invalid tamper detection ID (%d)\n", + __func__, ts_id); + return -EINVAL; + } + + /* Enable timestamp function. */ + ret = regmap_update_bits(pcf2127->regmap, + pcf2127->cfg->ts[ts_id].reg_base, + PCF2127_BIT_TS_CTRL_TSOFF | + PCF2127_BIT_TS_CTRL_TSM, + PCF2127_BIT_TS_CTRL_TSM); + if (ret) { + dev_err(dev, "%s: tamper detection config (ts%d_ctrl) failed\n", + __func__, ts_id); + return ret; + } + + /* + * Enable interrupt generation when TSF timestamp flag is set. + * Interrupt signals are open-drain outputs and can be left floating if + * unused. + */ + ret = regmap_update_bits(pcf2127->regmap, pcf2127->cfg->ts[ts_id].ie_reg, + pcf2127->cfg->ts[ts_id].ie_bit, + pcf2127->cfg->ts[ts_id].ie_bit); + if (ret) { + dev_err(dev, "%s: tamper detection TSIE%d config failed\n", + __func__, ts_id); + return ret; + } + + return ret; +} + +/* Route all interrupt sources to INT A pin. */ +static int pcf2127_configure_interrupt_pins(struct device *dev) +{ + struct pcf2127 *pcf2127 = dev_get_drvdata(dev); + int ret; + + /* Mask bits need to be cleared to enable corresponding + * interrupt source. + */ + ret = regmap_write(pcf2127->regmap, + PCF2131_REG_INT_A_MASK1, 0); + if (ret) + return ret; + + return regmap_write(pcf2127->regmap, PCF2131_REG_INT_A_MASK2, 0); +} + +static int pcf2127_probe(struct device *dev, struct regmap *regmap, + int alarm_irq, const struct pcf21xx_config *config) +{ + struct pcf2127 *pcf2127; + int ret = 0; + unsigned int val; + + dev_dbg(dev, "%s\n", __func__); + + pcf2127 = devm_kzalloc(dev, sizeof(*pcf2127), GFP_KERNEL); + if (!pcf2127) + return -ENOMEM; + + pcf2127->regmap = regmap; + pcf2127->cfg = config; + + dev_set_drvdata(dev, pcf2127); + + pcf2127->rtc = devm_rtc_allocate_device(dev); + if (IS_ERR(pcf2127->rtc)) + return PTR_ERR(pcf2127->rtc); + + pcf2127->rtc->ops = &pcf2127_rtc_ops; + pcf2127->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + pcf2127->rtc->range_max = RTC_TIMESTAMP_END_2099; + pcf2127->rtc->set_start_time = true; /* Sets actual start to 1970 */ + + /* + * PCF2127/29 do not work correctly when setting alarms at 1s intervals. + * PCF2131 is ok. + */ + if (pcf2127->cfg->type == PCF2127 || pcf2127->cfg->type == PCF2129) { + set_bit(RTC_FEATURE_ALARM_RES_2S, pcf2127->rtc->features); + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, pcf2127->rtc->features); + } + + clear_bit(RTC_FEATURE_ALARM, pcf2127->rtc->features); + + if (alarm_irq > 0) { + unsigned long flags; + + /* + * If flags = 0, devm_request_threaded_irq() will use IRQ flags + * obtained from device tree. + */ + if (dev_fwnode(dev)) + flags = 0; + else + flags = IRQF_TRIGGER_LOW; + + ret = devm_request_threaded_irq(dev, alarm_irq, NULL, + pcf2127_rtc_irq, + flags | IRQF_ONESHOT, + dev_name(dev), dev); + if (ret) { + dev_err(dev, "failed to request alarm irq\n"); + return ret; + } + pcf2127->irq_enabled = true; + } + + if (alarm_irq > 0 || device_property_read_bool(dev, "wakeup-source")) { + device_init_wakeup(dev, true); + set_bit(RTC_FEATURE_ALARM, pcf2127->rtc->features); + } + + if (pcf2127->cfg->has_int_a_b) { + /* Configure int A/B pins, independently of alarm_irq. */ + ret = pcf2127_configure_interrupt_pins(dev); + if (ret) { + dev_err(dev, "failed to configure interrupt pins\n"); + return ret; + } + } + + if (pcf2127->cfg->has_nvmem) { + struct nvmem_config nvmem_cfg = { + .priv = pcf2127, + .reg_read = pcf2127_nvmem_read, + .reg_write = pcf2127_nvmem_write, + .size = 512, + }; + + ret = devm_rtc_nvmem_register(pcf2127->rtc, &nvmem_cfg); + } + + /* + * The "Power-On Reset Override" facility prevents the RTC to do a reset + * after power on. For normal operation the PORO must be disabled. + */ + ret = regmap_clear_bits(pcf2127->regmap, PCF2127_REG_CTRL1, + PCF2127_BIT_CTRL1_POR_OVRD); + if (ret < 0) + return ret; + + ret = regmap_read(pcf2127->regmap, pcf2127->cfg->reg_clkout, &val); + if (ret < 0) + return ret; + + if (!(val & PCF2127_BIT_CLKOUT_OTPR)) { + ret = regmap_set_bits(pcf2127->regmap, pcf2127->cfg->reg_clkout, + PCF2127_BIT_CLKOUT_OTPR); + if (ret < 0) + return ret; + + msleep(100); + } + + /* + * Watchdog timer enabled and reset pin /RST activated when timed out. + * Select 1Hz clock source for watchdog timer (1/4Hz for PCF2131). + * Note: Countdown timer disabled and not available. + * For pca2129, pcf2129 and pcf2131, only bit[7] is for Symbol WD_CD + * of register watchdg_tim_ctl. The bit[6] is labeled + * as T. Bits labeled as T must always be written with + * logic 0. + */ + ret = regmap_update_bits(pcf2127->regmap, pcf2127->cfg->reg_wd_ctl, + PCF2127_BIT_WD_CTL_CD1 | + PCF2127_BIT_WD_CTL_CD0 | + PCF2127_BIT_WD_CTL_TF1 | + PCF2127_BIT_WD_CTL_TF0, + PCF2127_BIT_WD_CTL_CD1 | + (pcf2127->cfg->has_bit_wd_ctl_cd0 ? PCF2127_BIT_WD_CTL_CD0 : 0) | + PCF2127_BIT_WD_CTL_TF1); + if (ret) { + dev_err(dev, "%s: watchdog config (wd_ctl) failed\n", __func__); + return ret; + } + + pcf2127_watchdog_init(dev, pcf2127); + + /* + * Disable battery low/switch-over timestamp and interrupts. + * Clear battery interrupt flags which can block new trigger events. + * Note: This is the default chip behaviour but added to ensure + * correct tamper timestamp and interrupt function. + */ + ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL3, + PCF2127_BIT_CTRL3_BTSE | + PCF2127_BIT_CTRL3_BIE | + PCF2127_BIT_CTRL3_BLIE, 0); + if (ret) { + dev_err(dev, "%s: interrupt config (ctrl3) failed\n", + __func__); + return ret; + } + + /* + * Enable timestamp functions 1 to 4. + */ + for (int i = 0; i < pcf2127->cfg->ts_count; i++) { + ret = pcf2127_enable_ts(dev, i); + if (ret) + return ret; + } + + ret = rtc_add_group(pcf2127->rtc, &pcf2127->cfg->attribute_group); + if (ret) { + dev_err(dev, "%s: tamper sysfs registering failed\n", + __func__); + return ret; + } + + return devm_rtc_register_device(pcf2127->rtc); +} + +#ifdef CONFIG_OF +static const struct of_device_id pcf2127_of_match[] = { + { .compatible = "nxp,pcf2127", .data = &pcf21xx_cfg[PCF2127] }, + { .compatible = "nxp,pcf2129", .data = &pcf21xx_cfg[PCF2129] }, + { .compatible = "nxp,pca2129", .data = &pcf21xx_cfg[PCF2129] }, + { .compatible = "nxp,pcf2131", .data = &pcf21xx_cfg[PCF2131] }, + {} +}; +MODULE_DEVICE_TABLE(of, pcf2127_of_match); +#endif + +#if IS_ENABLED(CONFIG_I2C) + +static int pcf2127_i2c_write(void *context, const void *data, size_t count) +{ + struct device *dev = context; + struct i2c_client *client = to_i2c_client(dev); + int ret; + + ret = i2c_master_send(client, data, count); + if (ret != count) + return ret < 0 ? ret : -EIO; + + return 0; +} + +static int pcf2127_i2c_gather_write(void *context, + const void *reg, size_t reg_size, + const void *val, size_t val_size) +{ + struct device *dev = context; + struct i2c_client *client = to_i2c_client(dev); + int ret; + void *buf; + + if (WARN_ON(reg_size != 1)) + return -EINVAL; + + buf = kmalloc(val_size + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memcpy(buf, reg, 1); + memcpy(buf + 1, val, val_size); + + ret = i2c_master_send(client, buf, val_size + 1); + + kfree(buf); + + if (ret != val_size + 1) + return ret < 0 ? ret : -EIO; + + return 0; +} + +static int pcf2127_i2c_read(void *context, const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + struct device *dev = context; + struct i2c_client *client = to_i2c_client(dev); + int ret; + + if (WARN_ON(reg_size != 1)) + return -EINVAL; + + ret = i2c_master_send(client, reg, 1); + if (ret != 1) + return ret < 0 ? ret : -EIO; + + ret = i2c_master_recv(client, val, val_size); + if (ret != val_size) + return ret < 0 ? ret : -EIO; + + return 0; +} + +/* + * The reason we need this custom regmap_bus instead of using regmap_init_i2c() + * is that the STOP condition is required between set register address and + * read register data when reading from registers. + */ +static const struct regmap_bus pcf2127_i2c_regmap = { + .write = pcf2127_i2c_write, + .gather_write = pcf2127_i2c_gather_write, + .read = pcf2127_i2c_read, +}; + +static struct i2c_driver pcf2127_i2c_driver; + +static const struct i2c_device_id pcf2127_i2c_id[] = { + { .name = "pcf2127", .driver_data = (kernel_ulong_t)&pcf21xx_cfg[PCF2127] }, + { .name = "pcf2129", .driver_data = (kernel_ulong_t)&pcf21xx_cfg[PCF2129] }, + { .name = "pca2129", .driver_data = (kernel_ulong_t)&pcf21xx_cfg[PCF2129] }, + { .name = "pcf2131", .driver_data = (kernel_ulong_t)&pcf21xx_cfg[PCF2131] }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcf2127_i2c_id); + +static int pcf2127_i2c_probe(struct i2c_client *client) +{ + struct regmap *regmap; + static struct regmap_config config = { + .reg_bits = 8, + .val_bits = 8, + }; + const struct pcf21xx_config *variant; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + variant = i2c_get_match_data(client); + if (!variant) + return -ENODEV; + + config.max_register = variant->max_register, + + regmap = devm_regmap_init(&client->dev, &pcf2127_i2c_regmap, + &client->dev, &config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "%s: regmap allocation failed: %ld\n", + __func__, PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + return pcf2127_probe(&client->dev, regmap, client->irq, variant); +} + +static struct i2c_driver pcf2127_i2c_driver = { + .driver = { + .name = "rtc-pcf2127-i2c", + .of_match_table = of_match_ptr(pcf2127_of_match), + }, + .probe = pcf2127_i2c_probe, + .id_table = pcf2127_i2c_id, +}; + +static int pcf2127_i2c_register_driver(void) +{ + return i2c_add_driver(&pcf2127_i2c_driver); +} + +static void pcf2127_i2c_unregister_driver(void) +{ + i2c_del_driver(&pcf2127_i2c_driver); +} + +#else + +static int pcf2127_i2c_register_driver(void) +{ + return 0; +} + +static void pcf2127_i2c_unregister_driver(void) +{ +} + +#endif + +#if IS_ENABLED(CONFIG_SPI_MASTER) + +static struct spi_driver pcf2127_spi_driver; +static const struct spi_device_id pcf2127_spi_id[]; + +static int pcf2127_spi_probe(struct spi_device *spi) +{ + static struct regmap_config config = { + .reg_bits = 8, + .val_bits = 8, + .read_flag_mask = 0xa0, + .write_flag_mask = 0x20, + }; + struct regmap *regmap; + const struct pcf21xx_config *variant; + + if (spi->dev.of_node) { + variant = of_device_get_match_data(&spi->dev); + if (!variant) + return -ENODEV; + } else { + enum pcf21xx_type type = spi_get_device_id(spi)->driver_data; + + if (type >= PCF21XX_LAST_ID) + return -ENODEV; + variant = &pcf21xx_cfg[type]; + } + + if (variant->type == PCF2131) { + config.read_flag_mask = 0x0; + config.write_flag_mask = 0x0; + } + + config.max_register = variant->max_register; + + regmap = devm_regmap_init_spi(spi, &config); + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "%s: regmap allocation failed: %ld\n", + __func__, PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + return pcf2127_probe(&spi->dev, regmap, spi->irq, variant); +} + +static const struct spi_device_id pcf2127_spi_id[] = { + { "pcf2127", PCF2127 }, + { "pcf2129", PCF2129 }, + { "pca2129", PCF2129 }, + { "pcf2131", PCF2131 }, + { } +}; +MODULE_DEVICE_TABLE(spi, pcf2127_spi_id); + +static struct spi_driver pcf2127_spi_driver = { + .driver = { + .name = "rtc-pcf2127-spi", + .of_match_table = of_match_ptr(pcf2127_of_match), + }, + .probe = pcf2127_spi_probe, + .id_table = pcf2127_spi_id, +}; + +static int pcf2127_spi_register_driver(void) +{ + return spi_register_driver(&pcf2127_spi_driver); +} + +static void pcf2127_spi_unregister_driver(void) +{ + spi_unregister_driver(&pcf2127_spi_driver); +} + +#else + +static int pcf2127_spi_register_driver(void) +{ + return 0; +} + +static void pcf2127_spi_unregister_driver(void) +{ +} + +#endif + +static int __init pcf2127_init(void) +{ + int ret; + + ret = pcf2127_i2c_register_driver(); + if (ret) { + pr_err("Failed to register pcf2127 i2c driver: %d\n", ret); + return ret; + } + + ret = pcf2127_spi_register_driver(); + if (ret) { + pr_err("Failed to register pcf2127 spi driver: %d\n", ret); + pcf2127_i2c_unregister_driver(); + } + + return ret; +} +module_init(pcf2127_init) + +static void __exit pcf2127_exit(void) +{ + pcf2127_spi_unregister_driver(); + pcf2127_i2c_unregister_driver(); +} +module_exit(pcf2127_exit) + +MODULE_AUTHOR("Renaud Cerrato "); +MODULE_DESCRIPTION("NXP PCF2127/29/31 RTC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-pcf85063.c b/drivers/rtc/rtc-pcf85063.c new file mode 100644 index 000000000..8cb9ffc73 --- /dev/null +++ b/drivers/rtc/rtc-pcf85063.c @@ -0,0 +1,832 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * An I2C driver for the PCF85063 RTC + * Copyright 2014 Rose Technology + * + * Author: Søren Andersen + * Maintainers: http://www.nslu2-linux.org/ + * + * Copyright (C) 2019 Micro Crystal AG + * Author: Alexandre Belloni + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Information for this driver was pulled from the following datasheets. + * + * https://www.nxp.com/docs/en/data-sheet/PCF85063A.pdf + * https://www.nxp.com/docs/en/data-sheet/PCF85063TP.pdf + * + * PCF85063A -- Rev. 7 — 30 March 2018 + * PCF85063TP -- Rev. 4 — 6 May 2015 + * + * https://www.microcrystal.com/fileadmin/Media/Products/RTC/App.Manual/RV-8263-C7_App-Manual.pdf + * RV8263 -- Rev. 1.0 — January 2019 + * + * https://www.microcrystal.com/fileadmin/Media/Products/RTC/App.Manual/RV-8063-C7_App-Manual.pdf + * RV8063 -- Rev. 1.1 - October 2018 + */ + +#define PCF85063_REG_CTRL1 0x00 /* status */ +#define PCF85063_REG_CTRL1_CAP_SEL BIT(0) +#define PCF85063_REG_CTRL1_STOP BIT(5) +#define PCF85063_REG_CTRL1_EXT_TEST BIT(7) +#define PCF85063_REG_CTRL1_SWR 0x58 + +#define PCF85063_REG_CTRL2 0x01 +#define PCF85063_CTRL2_AF BIT(6) +#define PCF85063_CTRL2_AIE BIT(7) + +#define PCF85063_REG_OFFSET 0x02 +#define PCF85063_OFFSET_SIGN_BIT 6 /* 2's complement sign bit */ +#define PCF85063_OFFSET_MODE BIT(7) +#define PCF85063_OFFSET_STEP0 4340 +#define PCF85063_OFFSET_STEP1 4069 + +#define PCF85063_REG_CLKO_F_MASK 0x07 /* frequency mask */ +#define PCF85063_REG_CLKO_F_32768HZ 0x00 +#define PCF85063_REG_CLKO_F_OFF 0x07 + +#define PCF85063_REG_RAM 0x03 + +#define PCF85063_REG_SC 0x04 /* datetime */ +#define PCF85063_REG_SC_OS 0x80 + +#define PCF85063_REG_ALM_S 0x0b +#define PCF85063_AEN BIT(7) + +struct pcf85063_config { + struct regmap_config regmap; + unsigned has_alarms:1; + unsigned force_cap_7000:1; +}; + +struct pcf85063 { + struct rtc_device *rtc; + struct regmap *regmap; +#ifdef CONFIG_COMMON_CLK + struct clk_hw clkout_hw; +#endif +}; + +static int pcf85063_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct pcf85063 *pcf85063 = dev_get_drvdata(dev); + int rc; + u8 regs[7]; + + /* + * while reading, the time/date registers are blocked and not updated + * anymore until the access is finished. To not lose a second + * event, the access must be finished within one second. So, read all + * time/date registers in one turn. + */ + rc = regmap_bulk_read(pcf85063->regmap, PCF85063_REG_SC, regs, + sizeof(regs)); + if (rc) + return rc; + + /* if the clock has lost its power it makes no sense to use its time */ + if (regs[0] & PCF85063_REG_SC_OS) { + dev_warn(&pcf85063->rtc->dev, "Power loss detected, invalid time\n"); + return -EINVAL; + } + + tm->tm_sec = bcd2bin(regs[0] & 0x7F); + tm->tm_min = bcd2bin(regs[1] & 0x7F); + tm->tm_hour = bcd2bin(regs[2] & 0x3F); /* rtc hr 0-23 */ + tm->tm_mday = bcd2bin(regs[3] & 0x3F); + tm->tm_wday = regs[4] & 0x07; + tm->tm_mon = bcd2bin(regs[5] & 0x1F) - 1; /* rtc mn 1-12 */ + tm->tm_year = bcd2bin(regs[6]); + tm->tm_year += 100; + + return 0; +} + +static int pcf85063_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct pcf85063 *pcf85063 = dev_get_drvdata(dev); + int rc; + u8 regs[7]; + + /* + * to accurately set the time, reset the divider chain and keep it in + * reset state until all time/date registers are written + */ + rc = regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL1, + PCF85063_REG_CTRL1_EXT_TEST | + PCF85063_REG_CTRL1_STOP, + PCF85063_REG_CTRL1_STOP); + if (rc) + return rc; + + /* hours, minutes and seconds */ + regs[0] = bin2bcd(tm->tm_sec) & 0x7F; /* clear OS flag */ + + regs[1] = bin2bcd(tm->tm_min); + regs[2] = bin2bcd(tm->tm_hour); + + /* Day of month, 1 - 31 */ + regs[3] = bin2bcd(tm->tm_mday); + + /* Day, 0 - 6 */ + regs[4] = tm->tm_wday & 0x07; + + /* month, 1 - 12 */ + regs[5] = bin2bcd(tm->tm_mon + 1); + + /* year and century */ + regs[6] = bin2bcd(tm->tm_year - 100); + + /* write all registers at once */ + rc = regmap_bulk_write(pcf85063->regmap, PCF85063_REG_SC, + regs, sizeof(regs)); + if (rc) + return rc; + + /* + * Write the control register as a separate action since the size of + * the register space is different between the PCF85063TP and + * PCF85063A devices. The rollover point can not be used. + */ + return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL1, + PCF85063_REG_CTRL1_STOP, 0); +} + +static int pcf85063_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pcf85063 *pcf85063 = dev_get_drvdata(dev); + u8 buf[4]; + unsigned int val; + int ret; + + ret = regmap_bulk_read(pcf85063->regmap, PCF85063_REG_ALM_S, + buf, sizeof(buf)); + if (ret) + return ret; + + alrm->time.tm_sec = bcd2bin(buf[0] & 0x7f); + alrm->time.tm_min = bcd2bin(buf[1] & 0x7f); + alrm->time.tm_hour = bcd2bin(buf[2] & 0x3f); + alrm->time.tm_mday = bcd2bin(buf[3] & 0x3f); + + ret = regmap_read(pcf85063->regmap, PCF85063_REG_CTRL2, &val); + if (ret) + return ret; + + alrm->enabled = !!(val & PCF85063_CTRL2_AIE); + + return 0; +} + +static int pcf85063_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pcf85063 *pcf85063 = dev_get_drvdata(dev); + u8 buf[5]; + int ret; + + buf[0] = bin2bcd(alrm->time.tm_sec); + buf[1] = bin2bcd(alrm->time.tm_min); + buf[2] = bin2bcd(alrm->time.tm_hour); + buf[3] = bin2bcd(alrm->time.tm_mday); + buf[4] = PCF85063_AEN; /* Do not match on week day */ + + ret = regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2, + PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF, 0); + if (ret) + return ret; + + ret = regmap_bulk_write(pcf85063->regmap, PCF85063_REG_ALM_S, + buf, sizeof(buf)); + if (ret) + return ret; + + return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2, + PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF, + alrm->enabled ? PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF : PCF85063_CTRL2_AF); +} + +static int pcf85063_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct pcf85063 *pcf85063 = dev_get_drvdata(dev); + + return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2, + PCF85063_CTRL2_AIE, + enabled ? PCF85063_CTRL2_AIE : 0); +} + +static irqreturn_t pcf85063_rtc_handle_irq(int irq, void *dev_id) +{ + struct pcf85063 *pcf85063 = dev_id; + unsigned int val; + int err; + + err = regmap_read(pcf85063->regmap, PCF85063_REG_CTRL2, &val); + if (err) + return IRQ_NONE; + + if (val & PCF85063_CTRL2_AF) { + rtc_update_irq(pcf85063->rtc, 1, RTC_IRQF | RTC_AF); + regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2, + PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF, + 0); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int pcf85063_read_offset(struct device *dev, long *offset) +{ + struct pcf85063 *pcf85063 = dev_get_drvdata(dev); + long val; + u32 reg; + int ret; + + ret = regmap_read(pcf85063->regmap, PCF85063_REG_OFFSET, ®); + if (ret < 0) + return ret; + + val = sign_extend32(reg & ~PCF85063_OFFSET_MODE, + PCF85063_OFFSET_SIGN_BIT); + + if (reg & PCF85063_OFFSET_MODE) + *offset = val * PCF85063_OFFSET_STEP1; + else + *offset = val * PCF85063_OFFSET_STEP0; + + return 0; +} + +static int pcf85063_set_offset(struct device *dev, long offset) +{ + struct pcf85063 *pcf85063 = dev_get_drvdata(dev); + s8 mode0, mode1, reg; + unsigned int error0, error1; + + if (offset > PCF85063_OFFSET_STEP0 * 63) + return -ERANGE; + if (offset < PCF85063_OFFSET_STEP0 * -64) + return -ERANGE; + + mode0 = DIV_ROUND_CLOSEST(offset, PCF85063_OFFSET_STEP0); + mode1 = DIV_ROUND_CLOSEST(offset, PCF85063_OFFSET_STEP1); + + error0 = abs(offset - (mode0 * PCF85063_OFFSET_STEP0)); + error1 = abs(offset - (mode1 * PCF85063_OFFSET_STEP1)); + if (mode1 > 63 || mode1 < -64 || error0 < error1) + reg = mode0 & ~PCF85063_OFFSET_MODE; + else + reg = mode1 | PCF85063_OFFSET_MODE; + + return regmap_write(pcf85063->regmap, PCF85063_REG_OFFSET, reg); +} + +static int pcf85063_ioctl(struct device *dev, unsigned int cmd, + unsigned long arg) +{ + struct pcf85063 *pcf85063 = dev_get_drvdata(dev); + int status, ret = 0; + + switch (cmd) { + case RTC_VL_READ: + ret = regmap_read(pcf85063->regmap, PCF85063_REG_SC, &status); + if (ret < 0) + return ret; + + status = (status & PCF85063_REG_SC_OS) ? RTC_VL_DATA_INVALID : 0; + + return put_user(status, (unsigned int __user *)arg); + + default: + return -ENOIOCTLCMD; + } +} + +static const struct rtc_class_ops pcf85063_rtc_ops = { + .read_time = pcf85063_rtc_read_time, + .set_time = pcf85063_rtc_set_time, + .read_offset = pcf85063_read_offset, + .set_offset = pcf85063_set_offset, + .read_alarm = pcf85063_rtc_read_alarm, + .set_alarm = pcf85063_rtc_set_alarm, + .alarm_irq_enable = pcf85063_rtc_alarm_irq_enable, + .ioctl = pcf85063_ioctl, +}; + +static int pcf85063_nvmem_read(void *priv, unsigned int offset, + void *val, size_t bytes) +{ + unsigned int tmp; + int ret; + + ret = regmap_read(priv, PCF85063_REG_RAM, &tmp); + if (ret < 0) + return ret; + + *(u8 *)val = tmp; + + return 0; +} + +static int pcf85063_nvmem_write(void *priv, unsigned int offset, + void *val, size_t bytes) +{ + return regmap_write(priv, PCF85063_REG_RAM, *(u8 *)val); +} + +static int pcf85063_load_capacitance(struct pcf85063 *pcf85063, + const struct device_node *np, + unsigned int force_cap) +{ + u32 load = 7000; + u8 reg = 0; + + if (force_cap) + load = force_cap; + else + of_property_read_u32(np, "quartz-load-femtofarads", &load); + + switch (load) { + default: + dev_warn(&pcf85063->rtc->dev, "Unknown quartz-load-femtofarads value: %d. Assuming 7000", + load); + fallthrough; + case 7000: + break; + case 12500: + reg = PCF85063_REG_CTRL1_CAP_SEL; + break; + } + + return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL1, + PCF85063_REG_CTRL1_CAP_SEL, reg); +} + +#ifdef CONFIG_COMMON_CLK +/* + * Handling of the clkout + */ + +#define clkout_hw_to_pcf85063(_hw) container_of(_hw, struct pcf85063, clkout_hw) + +static int clkout_rates[] = { + 32768, + 16384, + 8192, + 4096, + 2048, + 1024, + 1, + 0 +}; + +static unsigned long pcf85063_clkout_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct pcf85063 *pcf85063 = clkout_hw_to_pcf85063(hw); + unsigned int buf; + int ret = regmap_read(pcf85063->regmap, PCF85063_REG_CTRL2, &buf); + + if (ret < 0) + return 0; + + buf &= PCF85063_REG_CLKO_F_MASK; + return clkout_rates[buf]; +} + +static int pcf85063_clkout_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(clkout_rates); i++) + if (clkout_rates[i] <= req->rate) { + req->rate = clkout_rates[i]; + + return 0; + } + + req->rate = clkout_rates[0]; + + return 0; +} + +static int pcf85063_clkout_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct pcf85063 *pcf85063 = clkout_hw_to_pcf85063(hw); + int i; + + for (i = 0; i < ARRAY_SIZE(clkout_rates); i++) + if (clkout_rates[i] == rate) + return regmap_update_bits(pcf85063->regmap, + PCF85063_REG_CTRL2, + PCF85063_REG_CLKO_F_MASK, i); + + return -EINVAL; +} + +static int pcf85063_clkout_control(struct clk_hw *hw, bool enable) +{ + struct pcf85063 *pcf85063 = clkout_hw_to_pcf85063(hw); + unsigned int buf; + int ret; + + ret = regmap_read(pcf85063->regmap, PCF85063_REG_CTRL2, &buf); + if (ret < 0) + return ret; + buf &= PCF85063_REG_CLKO_F_MASK; + + if (enable) { + if (buf == PCF85063_REG_CLKO_F_OFF) + buf = PCF85063_REG_CLKO_F_32768HZ; + else + return 0; + } else { + if (buf != PCF85063_REG_CLKO_F_OFF) + buf = PCF85063_REG_CLKO_F_OFF; + else + return 0; + } + + return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2, + PCF85063_REG_CLKO_F_MASK, buf); +} + +static int pcf85063_clkout_prepare(struct clk_hw *hw) +{ + return pcf85063_clkout_control(hw, 1); +} + +static void pcf85063_clkout_unprepare(struct clk_hw *hw) +{ + pcf85063_clkout_control(hw, 0); +} + +static int pcf85063_clkout_is_prepared(struct clk_hw *hw) +{ + struct pcf85063 *pcf85063 = clkout_hw_to_pcf85063(hw); + unsigned int buf; + int ret = regmap_read(pcf85063->regmap, PCF85063_REG_CTRL2, &buf); + + if (ret < 0) + return 0; + + return (buf & PCF85063_REG_CLKO_F_MASK) != PCF85063_REG_CLKO_F_OFF; +} + +static const struct clk_ops pcf85063_clkout_ops = { + .prepare = pcf85063_clkout_prepare, + .unprepare = pcf85063_clkout_unprepare, + .is_prepared = pcf85063_clkout_is_prepared, + .recalc_rate = pcf85063_clkout_recalc_rate, + .determine_rate = pcf85063_clkout_determine_rate, + .set_rate = pcf85063_clkout_set_rate, +}; + +static struct clk *pcf85063_clkout_register_clk(struct pcf85063 *pcf85063) +{ + struct clk *clk; + struct clk_init_data init = {}; + struct device_node *node = pcf85063->rtc->dev.parent->of_node; + struct device_node *fixed_clock; + + fixed_clock = of_get_child_by_name(node, "clock"); + if (fixed_clock) { + /* + * skip registering square wave clock when a fixed + * clock has been registered. The fixed clock is + * registered automatically when being referenced. + */ + of_node_put(fixed_clock); + return NULL; + } + + init.name = "pcf85063-clkout"; + init.ops = &pcf85063_clkout_ops; + init.flags = 0; + init.parent_names = NULL; + init.num_parents = 0; + pcf85063->clkout_hw.init = &init; + + /* optional override of the clockname */ + of_property_read_string(node, "clock-output-names", &init.name); + + /* register the clock */ + clk = devm_clk_register(&pcf85063->rtc->dev, &pcf85063->clkout_hw); + + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); + + return clk; +} +#endif + +static int pcf85063_probe(struct device *dev, struct regmap *regmap, int irq, + const struct pcf85063_config *config) +{ + struct pcf85063 *pcf85063; + unsigned int tmp; + int err; + struct nvmem_config nvmem_cfg = { + .name = "pcf85063_nvram", + .reg_read = pcf85063_nvmem_read, + .reg_write = pcf85063_nvmem_write, + .type = NVMEM_TYPE_BATTERY_BACKED, + .size = 1, + }; + + dev_dbg(dev, "%s\n", __func__); + + pcf85063 = devm_kzalloc(dev, sizeof(struct pcf85063), + GFP_KERNEL); + if (!pcf85063) + return -ENOMEM; + + pcf85063->regmap = regmap; + + dev_set_drvdata(dev, pcf85063); + + err = regmap_read(pcf85063->regmap, PCF85063_REG_SC, &tmp); + if (err) + return dev_err_probe(dev, err, "RTC chip is not present\n"); + + pcf85063->rtc = devm_rtc_allocate_device(dev); + if (IS_ERR(pcf85063->rtc)) + return PTR_ERR(pcf85063->rtc); + + /* + * If a Power loss is detected, SW reset the device. + * From PCF85063A datasheet: + * There is a low probability that some devices will have corruption + * of the registers after the automatic power-on reset... + */ + if (tmp & PCF85063_REG_SC_OS) { + dev_warn(dev, "POR issue detected, sending a SW reset\n"); + err = regmap_write(pcf85063->regmap, PCF85063_REG_CTRL1, + PCF85063_REG_CTRL1_SWR); + if (err < 0) + dev_warn(dev, "SW reset failed, trying to continue\n"); + } + + err = pcf85063_load_capacitance(pcf85063, dev->of_node, + config->force_cap_7000 ? 7000 : 0); + if (err < 0) + dev_warn(dev, "failed to set xtal load capacitance: %d", + err); + + pcf85063->rtc->ops = &pcf85063_rtc_ops; + pcf85063->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + pcf85063->rtc->range_max = RTC_TIMESTAMP_END_2099; + set_bit(RTC_FEATURE_ALARM_RES_2S, pcf85063->rtc->features); + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, pcf85063->rtc->features); + clear_bit(RTC_FEATURE_ALARM, pcf85063->rtc->features); + + if (config->has_alarms && irq > 0) { + unsigned long irqflags = IRQF_TRIGGER_LOW; + + if (dev_fwnode(dev)) + irqflags = 0; + + err = devm_request_threaded_irq(dev, irq, + NULL, pcf85063_rtc_handle_irq, + irqflags | IRQF_ONESHOT, + "pcf85063", pcf85063); + if (err) { + dev_warn(&pcf85063->rtc->dev, + "unable to request IRQ, alarms disabled\n"); + } else { + set_bit(RTC_FEATURE_ALARM, pcf85063->rtc->features); + device_init_wakeup(dev, true); + err = dev_pm_set_wake_irq(dev, irq); + if (err) + dev_err(&pcf85063->rtc->dev, + "failed to enable irq wake\n"); + } + } + + nvmem_cfg.priv = pcf85063->regmap; + devm_rtc_nvmem_register(pcf85063->rtc, &nvmem_cfg); + +#ifdef CONFIG_COMMON_CLK + /* register clk in common clk framework */ + pcf85063_clkout_register_clk(pcf85063); +#endif + + return devm_rtc_register_device(pcf85063->rtc); +} + +#if IS_ENABLED(CONFIG_I2C) + +static const struct pcf85063_config config_pcf85063 = { + .regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x0a, + }, +}; + +static const struct pcf85063_config config_pcf85063tp = { + .regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x0a, + }, +}; + +static const struct pcf85063_config config_pcf85063a = { + .regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x11, + }, + .has_alarms = 1, +}; + +static const struct pcf85063_config config_rv8263 = { + .regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x11, + }, + .has_alarms = 1, + .force_cap_7000 = 1, +}; + +static const struct i2c_device_id pcf85063_ids[] = { + { .name = "pca85073a", .driver_data = (kernel_ulong_t)&config_pcf85063a }, + { .name = "pcf85063", .driver_data = (kernel_ulong_t)&config_pcf85063 }, + { .name = "pcf85063tp", .driver_data = (kernel_ulong_t)&config_pcf85063tp }, + { .name = "pcf85063a", .driver_data = (kernel_ulong_t)&config_pcf85063a }, + { .name = "rv8263", .driver_data = (kernel_ulong_t)&config_rv8263 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcf85063_ids); + +#ifdef CONFIG_OF +static const struct of_device_id pcf85063_of_match[] = { + { .compatible = "nxp,pca85073a", .data = &config_pcf85063a }, + { .compatible = "nxp,pcf85063", .data = &config_pcf85063 }, + { .compatible = "nxp,pcf85063tp", .data = &config_pcf85063tp }, + { .compatible = "nxp,pcf85063a", .data = &config_pcf85063a }, + { .compatible = "microcrystal,rv8263", .data = &config_rv8263 }, + {} +}; +MODULE_DEVICE_TABLE(of, pcf85063_of_match); +#endif + +static int pcf85063_i2c_probe(struct i2c_client *client) +{ + const struct pcf85063_config *config; + struct regmap *regmap; + + config = i2c_get_match_data(client); + if (!config) + return -ENODEV; + + regmap = devm_regmap_init_i2c(client, &config->regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return pcf85063_probe(&client->dev, regmap, client->irq, config); +} + +static struct i2c_driver pcf85063_driver = { + .driver = { + .name = "rtc-pcf85063", + .of_match_table = of_match_ptr(pcf85063_of_match), + }, + .probe = pcf85063_i2c_probe, + .id_table = pcf85063_ids, +}; + +static int pcf85063_register_driver(void) +{ + return i2c_add_driver(&pcf85063_driver); +} + +static void pcf85063_unregister_driver(void) +{ + i2c_del_driver(&pcf85063_driver); +} + +#else + +static int pcf85063_register_driver(void) +{ + return 0; +} + +static void pcf85063_unregister_driver(void) +{ +} + +#endif /* IS_ENABLED(CONFIG_I2C) */ + +#if IS_ENABLED(CONFIG_SPI_MASTER) + +static const struct pcf85063_config config_rv8063 = { + .regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x11, + .read_flag_mask = BIT(7) | BIT(5), + .write_flag_mask = BIT(5), + }, + .has_alarms = 1, + .force_cap_7000 = 1, +}; + +static const struct spi_device_id rv8063_id[] = { + { "rv8063" }, + {} +}; +MODULE_DEVICE_TABLE(spi, rv8063_id); + +static const struct of_device_id rv8063_of_match[] = { + { .compatible = "microcrystal,rv8063" }, + {} +}; +MODULE_DEVICE_TABLE(of, rv8063_of_match); + +static int rv8063_probe(struct spi_device *spi) +{ + const struct pcf85063_config *config = &config_rv8063; + struct regmap *regmap; + + regmap = devm_regmap_init_spi(spi, &config->regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return pcf85063_probe(&spi->dev, regmap, spi->irq, config); +} + +static struct spi_driver rv8063_driver = { + .driver = { + .name = "rv8063", + .of_match_table = rv8063_of_match, + }, + .probe = rv8063_probe, + .id_table = rv8063_id, +}; + +static int __init rv8063_register_driver(void) +{ + return spi_register_driver(&rv8063_driver); +} + +static void __exit rv8063_unregister_driver(void) +{ + spi_unregister_driver(&rv8063_driver); +} + +#else + +static int __init rv8063_register_driver(void) +{ + return 0; +} + +static void __exit rv8063_unregister_driver(void) +{ +} + +#endif /* IS_ENABLED(CONFIG_SPI_MASTER) */ + +static int __init pcf85063_init(void) +{ + int ret; + + ret = pcf85063_register_driver(); + if (ret) + return ret; + + ret = rv8063_register_driver(); + if (ret) + pcf85063_unregister_driver(); + + return ret; +} +module_init(pcf85063_init); + +static void __exit pcf85063_exit(void) +{ + rv8063_unregister_driver(); + pcf85063_unregister_driver(); +} +module_exit(pcf85063_exit); + +MODULE_AUTHOR("Søren Andersen "); +MODULE_DESCRIPTION("PCF85063 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c new file mode 100644 index 000000000..e83549538 --- /dev/null +++ b/drivers/rtc/rtc-pcf8523.c @@ -0,0 +1,523 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Avionic Design GmbH + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define PCF8523_REG_CONTROL1 0x00 +#define PCF8523_CONTROL1_CAP_SEL BIT(7) +#define PCF8523_CONTROL1_STOP BIT(5) +#define PCF8523_CONTROL1_AIE BIT(1) + +#define PCF8523_REG_CONTROL2 0x01 +#define PCF8523_CONTROL2_AF BIT(3) + +#define PCF8523_REG_CONTROL3 0x02 +#define PCF8523_CONTROL3_PM GENMASK(7, 5) +#define PCF8523_PM_STANDBY 0x7 +#define PCF8523_CONTROL3_BLF BIT(2) /* battery low bit, read-only */ +#define PCF8523_CONTROL3_BSF BIT(3) + +#define PCF8523_REG_SECONDS 0x03 +#define PCF8523_SECONDS_OS BIT(7) + +#define PCF8523_REG_MINUTES 0x04 +#define PCF8523_REG_HOURS 0x05 +#define PCF8523_REG_DAYS 0x06 +#define PCF8523_REG_WEEKDAYS 0x07 +#define PCF8523_REG_MONTHS 0x08 +#define PCF8523_REG_YEARS 0x09 + +#define PCF8523_REG_MINUTE_ALARM 0x0a +#define PCF8523_REG_HOUR_ALARM 0x0b +#define PCF8523_REG_DAY_ALARM 0x0c +#define PCF8523_REG_WEEKDAY_ALARM 0x0d +#define ALARM_DIS BIT(7) + +#define PCF8523_REG_OFFSET 0x0e +#define PCF8523_OFFSET_MODE BIT(7) + +#define PCF8523_TMR_CLKOUT_CTRL 0x0f + +struct pcf8523 { + struct rtc_device *rtc; + struct regmap *regmap; +}; + +static int pcf8523_load_capacitance(struct pcf8523 *pcf8523, struct device_node *node) +{ + u32 load, value = 0; + + load = 12500; + of_property_read_u32(node, "quartz-load-femtofarads", &load); + + switch (load) { + default: + dev_warn(&pcf8523->rtc->dev, "Unknown quartz-load-femtofarads value: %d. Assuming 12500", + load); + fallthrough; + case 12500: + value = PCF8523_CONTROL1_CAP_SEL; + break; + case 7000: + break; + } + + return regmap_update_bits(pcf8523->regmap, PCF8523_REG_CONTROL1, + PCF8523_CONTROL1_CAP_SEL, value); +} + +static irqreturn_t pcf8523_irq(int irq, void *dev_id) +{ + struct pcf8523 *pcf8523 = dev_id; + u32 value; + int err; + + err = regmap_read(pcf8523->regmap, PCF8523_REG_CONTROL2, &value); + if (err < 0) + return IRQ_HANDLED; + + if (value & PCF8523_CONTROL2_AF) { + value &= ~PCF8523_CONTROL2_AF; + regmap_write(pcf8523->regmap, PCF8523_REG_CONTROL2, value); + rtc_update_irq(pcf8523->rtc, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int pcf8523_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct pcf8523 *pcf8523 = dev_get_drvdata(dev); + u8 regs[10]; + int err; + + err = regmap_bulk_read(pcf8523->regmap, PCF8523_REG_CONTROL1, regs, + sizeof(regs)); + if (err < 0) + return err; + + if ((regs[0] & PCF8523_CONTROL1_STOP) || (regs[3] & PCF8523_SECONDS_OS)) + return -EINVAL; + + tm->tm_sec = bcd2bin(regs[3] & 0x7f); + tm->tm_min = bcd2bin(regs[4] & 0x7f); + tm->tm_hour = bcd2bin(regs[5] & 0x3f); + tm->tm_mday = bcd2bin(regs[6] & 0x3f); + tm->tm_wday = regs[7] & 0x7; + tm->tm_mon = bcd2bin(regs[8] & 0x1f) - 1; + tm->tm_year = bcd2bin(regs[9]) + 100; + + return 0; +} + +static int pcf8523_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct pcf8523 *pcf8523 = dev_get_drvdata(dev); + u8 regs[7]; + int err; + + err = regmap_update_bits(pcf8523->regmap, PCF8523_REG_CONTROL1, + PCF8523_CONTROL1_STOP, PCF8523_CONTROL1_STOP); + if (err < 0) + return err; + + /* This will purposely overwrite PCF8523_SECONDS_OS */ + regs[0] = bin2bcd(tm->tm_sec); + regs[1] = bin2bcd(tm->tm_min); + regs[2] = bin2bcd(tm->tm_hour); + regs[3] = bin2bcd(tm->tm_mday); + regs[4] = tm->tm_wday; + regs[5] = bin2bcd(tm->tm_mon + 1); + regs[6] = bin2bcd(tm->tm_year - 100); + + err = regmap_bulk_write(pcf8523->regmap, PCF8523_REG_SECONDS, regs, + sizeof(regs)); + if (err < 0) { + /* + * If the time cannot be set, restart the RTC anyway. Note + * that errors are ignored if the RTC cannot be started so + * that we have a chance to propagate the original error. + */ + regmap_update_bits(pcf8523->regmap, PCF8523_REG_CONTROL1, + PCF8523_CONTROL1_STOP, 0); + return err; + } + + return regmap_update_bits(pcf8523->regmap, PCF8523_REG_CONTROL1, + PCF8523_CONTROL1_STOP, 0); +} + +static int pcf8523_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *tm) +{ + struct pcf8523 *pcf8523 = dev_get_drvdata(dev); + u8 regs[4]; + u32 value; + int err; + + err = regmap_bulk_read(pcf8523->regmap, PCF8523_REG_MINUTE_ALARM, regs, + sizeof(regs)); + if (err < 0) + return err; + + tm->time.tm_sec = 0; + tm->time.tm_min = bcd2bin(regs[0] & 0x7F); + tm->time.tm_hour = bcd2bin(regs[1] & 0x3F); + tm->time.tm_mday = bcd2bin(regs[2] & 0x3F); + tm->time.tm_wday = bcd2bin(regs[3] & 0x7); + + err = regmap_read(pcf8523->regmap, PCF8523_REG_CONTROL1, &value); + if (err < 0) + return err; + tm->enabled = !!(value & PCF8523_CONTROL1_AIE); + + err = regmap_read(pcf8523->regmap, PCF8523_REG_CONTROL2, &value); + if (err < 0) + return err; + tm->pending = !!(value & PCF8523_CONTROL2_AF); + + return 0; +} + +static int pcf8523_irq_enable(struct device *dev, unsigned int enabled) +{ + struct pcf8523 *pcf8523 = dev_get_drvdata(dev); + + return regmap_update_bits(pcf8523->regmap, PCF8523_REG_CONTROL1, + PCF8523_CONTROL1_AIE, enabled ? + PCF8523_CONTROL1_AIE : 0); +} + +static int pcf8523_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm) +{ + struct pcf8523 *pcf8523 = dev_get_drvdata(dev); + u8 regs[5]; + int err; + + err = pcf8523_irq_enable(dev, 0); + if (err) + return err; + + err = regmap_write(pcf8523->regmap, PCF8523_REG_CONTROL2, 0); + if (err < 0) + return err; + + regs[0] = bin2bcd(tm->time.tm_min); + regs[1] = bin2bcd(tm->time.tm_hour); + regs[2] = bin2bcd(tm->time.tm_mday); + regs[3] = ALARM_DIS; + + err = regmap_bulk_write(pcf8523->regmap, PCF8523_REG_MINUTE_ALARM, regs, + sizeof(regs)); + if (err < 0) + return err; + + if (tm->enabled) + return pcf8523_irq_enable(dev, tm->enabled); + + return 0; +} + +static int pcf8523_param_get(struct device *dev, struct rtc_param *param) +{ + struct pcf8523 *pcf8523 = dev_get_drvdata(dev); + int ret; + u32 value; + + switch (param->param) { + case RTC_PARAM_BACKUP_SWITCH_MODE: + ret = regmap_read(pcf8523->regmap, PCF8523_REG_CONTROL3, &value); + if (ret < 0) + return ret; + + value = FIELD_GET(PCF8523_CONTROL3_PM, value); + + switch (value) { + case 0x0: + case 0x4: + param->uvalue = RTC_BSM_LEVEL; + break; + case 0x1: + case 0x5: + param->uvalue = RTC_BSM_DIRECT; + break; + case PCF8523_PM_STANDBY: + param->uvalue = RTC_BSM_STANDBY; + break; + default: + param->uvalue = RTC_BSM_DISABLED; + } + + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int pcf8523_param_set(struct device *dev, struct rtc_param *param) +{ + struct pcf8523 *pcf8523 = dev_get_drvdata(dev); + u8 mode; + + switch (param->param) { + case RTC_PARAM_BACKUP_SWITCH_MODE: + switch (param->uvalue) { + case RTC_BSM_DISABLED: + mode = 0x2; + break; + case RTC_BSM_DIRECT: + mode = 0x1; + break; + case RTC_BSM_LEVEL: + mode = 0x0; + break; + case RTC_BSM_STANDBY: + mode = PCF8523_PM_STANDBY; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(pcf8523->regmap, PCF8523_REG_CONTROL3, + PCF8523_CONTROL3_PM, + FIELD_PREP(PCF8523_CONTROL3_PM, mode)); + + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int pcf8523_rtc_ioctl(struct device *dev, unsigned int cmd, + unsigned long arg) +{ + struct pcf8523 *pcf8523 = dev_get_drvdata(dev); + unsigned int flags = 0; + u32 value; + int ret; + + switch (cmd) { + case RTC_VL_READ: + ret = regmap_read(pcf8523->regmap, PCF8523_REG_CONTROL3, &value); + if (ret < 0) + return ret; + + if (value & PCF8523_CONTROL3_BLF) + flags |= RTC_VL_BACKUP_LOW; + + ret = regmap_read(pcf8523->regmap, PCF8523_REG_SECONDS, &value); + if (ret < 0) + return ret; + + if (value & PCF8523_SECONDS_OS) + flags |= RTC_VL_DATA_INVALID; + + return put_user(flags, (unsigned int __user *)arg); + + default: + return -ENOIOCTLCMD; + } +} + +static int pcf8523_rtc_read_offset(struct device *dev, long *offset) +{ + struct pcf8523 *pcf8523 = dev_get_drvdata(dev); + int err; + u32 value; + s8 val; + + err = regmap_read(pcf8523->regmap, PCF8523_REG_OFFSET, &value); + if (err < 0) + return err; + + /* sign extend the 7-bit offset value */ + val = value << 1; + *offset = (value & PCF8523_OFFSET_MODE ? 4069 : 4340) * (val >> 1); + + return 0; +} + +static int pcf8523_rtc_set_offset(struct device *dev, long offset) +{ + struct pcf8523 *pcf8523 = dev_get_drvdata(dev); + long reg_m0, reg_m1; + u32 value; + + reg_m0 = clamp(DIV_ROUND_CLOSEST(offset, 4340), -64L, 63L); + reg_m1 = clamp(DIV_ROUND_CLOSEST(offset, 4069), -64L, 63L); + + if (abs(reg_m0 * 4340 - offset) < abs(reg_m1 * 4069 - offset)) + value = reg_m0 & 0x7f; + else + value = (reg_m1 & 0x7f) | PCF8523_OFFSET_MODE; + + return regmap_write(pcf8523->regmap, PCF8523_REG_OFFSET, value); +} + +#ifdef CONFIG_PM_SLEEP +static int pcf8523_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (client->irq > 0 && device_may_wakeup(dev)) + enable_irq_wake(client->irq); + + return 0; +} + +static int pcf8523_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (client->irq > 0 && device_may_wakeup(dev)) + disable_irq_wake(client->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(pcf8523_pm, pcf8523_suspend, pcf8523_resume); + +static const struct rtc_class_ops pcf8523_rtc_ops = { + .read_time = pcf8523_rtc_read_time, + .set_time = pcf8523_rtc_set_time, + .read_alarm = pcf8523_rtc_read_alarm, + .set_alarm = pcf8523_rtc_set_alarm, + .alarm_irq_enable = pcf8523_irq_enable, + .ioctl = pcf8523_rtc_ioctl, + .read_offset = pcf8523_rtc_read_offset, + .set_offset = pcf8523_rtc_set_offset, + .param_get = pcf8523_param_get, + .param_set = pcf8523_param_set, +}; + +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x13, +}; + +static int pcf8523_probe(struct i2c_client *client) +{ + struct pcf8523 *pcf8523; + struct rtc_device *rtc; + bool wakeup_source = false; + u32 value; + int err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + pcf8523 = devm_kzalloc(&client->dev, sizeof(struct pcf8523), GFP_KERNEL); + if (!pcf8523) + return -ENOMEM; + + pcf8523->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(pcf8523->regmap)) + return PTR_ERR(pcf8523->regmap); + + i2c_set_clientdata(client, pcf8523); + + rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + pcf8523->rtc = rtc; + + err = pcf8523_load_capacitance(pcf8523, client->dev.of_node); + if (err < 0) + dev_warn(&client->dev, "failed to set xtal load capacitance: %d", + err); + + err = regmap_read(pcf8523->regmap, PCF8523_REG_SECONDS, &value); + if (err < 0) + return err; + + if (value & PCF8523_SECONDS_OS) { + err = regmap_read(pcf8523->regmap, PCF8523_REG_CONTROL3, &value); + if (err < 0) + return err; + + if (FIELD_GET(PCF8523_CONTROL3_PM, value) == PCF8523_PM_STANDBY) { + err = regmap_write(pcf8523->regmap, PCF8523_REG_CONTROL3, + value & ~PCF8523_CONTROL3_PM); + if (err < 0) + return err; + } + } + + rtc->ops = &pcf8523_rtc_ops; + rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rtc->range_max = RTC_TIMESTAMP_END_2099; + set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->features); + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features); + + if (client->irq > 0) { + unsigned long irqflags = IRQF_TRIGGER_LOW; + + if (dev_fwnode(&client->dev)) + irqflags = 0; + + err = regmap_write(pcf8523->regmap, PCF8523_TMR_CLKOUT_CTRL, 0x38); + if (err < 0) + return err; + + err = devm_request_threaded_irq(&client->dev, client->irq, + NULL, pcf8523_irq, + IRQF_SHARED | IRQF_ONESHOT | irqflags, + dev_name(&rtc->dev), pcf8523); + if (err) + return err; + + dev_pm_set_wake_irq(&client->dev, client->irq); + } + + wakeup_source = of_property_read_bool(client->dev.of_node, "wakeup-source"); + if (client->irq > 0 || wakeup_source) + device_init_wakeup(&client->dev, true); + + return devm_rtc_register_device(rtc); +} + +static const struct i2c_device_id pcf8523_id[] = { + { .name = "pcf8523" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcf8523_id); + +static const struct of_device_id pcf8523_of_match[] = { + { .compatible = "nxp,pcf8523" }, + { .compatible = "microcrystal,rv8523" }, + { } +}; +MODULE_DEVICE_TABLE(of, pcf8523_of_match); + +static struct i2c_driver pcf8523_driver = { + .driver = { + .name = "rtc-pcf8523", + .of_match_table = pcf8523_of_match, + .pm = &pcf8523_pm, + }, + .probe = pcf8523_probe, + .id_table = pcf8523_id, +}; +module_i2c_driver(pcf8523_driver); + +MODULE_AUTHOR("Thierry Reding "); +MODULE_DESCRIPTION("NXP PCF8523 RTC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-pcf85363.c b/drivers/rtc/rtc-pcf85363.c new file mode 100644 index 000000000..ccc7834e5 --- /dev/null +++ b/drivers/rtc/rtc-pcf85363.c @@ -0,0 +1,503 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * drivers/rtc/rtc-pcf85363.c + * + * Driver for NXP PCF85363 real-time clock. + * + * Copyright (C) 2017 Eric Nelson + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Date/Time registers + */ +#define DT_100THS 0x00 +#define DT_SECS 0x01 +#define DT_MINUTES 0x02 +#define DT_HOURS 0x03 +#define DT_DAYS 0x04 +#define DT_WEEKDAYS 0x05 +#define DT_MONTHS 0x06 +#define DT_YEARS 0x07 + +/* + * Alarm registers + */ +#define DT_SECOND_ALM1 0x08 +#define DT_MINUTE_ALM1 0x09 +#define DT_HOUR_ALM1 0x0a +#define DT_DAY_ALM1 0x0b +#define DT_MONTH_ALM1 0x0c +#define DT_MINUTE_ALM2 0x0d +#define DT_HOUR_ALM2 0x0e +#define DT_WEEKDAY_ALM2 0x0f +#define DT_ALARM_EN 0x10 + +/* + * Time stamp registers + */ +#define DT_TIMESTAMP1 0x11 +#define DT_TIMESTAMP2 0x17 +#define DT_TIMESTAMP3 0x1d +#define DT_TS_MODE 0x23 + +/* + * control registers + */ +#define CTRL_OFFSET 0x24 +#define CTRL_OSCILLATOR 0x25 +#define CTRL_BATTERY 0x26 +#define CTRL_PIN_IO 0x27 +#define CTRL_FUNCTION 0x28 +#define CTRL_INTA_EN 0x29 +#define CTRL_INTB_EN 0x2a +#define CTRL_FLAGS 0x2b +#define CTRL_RAMBYTE 0x2c +#define CTRL_WDOG 0x2d +#define CTRL_STOP_EN 0x2e +#define CTRL_RESETS 0x2f +#define CTRL_RAM 0x40 + +#define ALRM_SEC_A1E BIT(0) +#define ALRM_MIN_A1E BIT(1) +#define ALRM_HR_A1E BIT(2) +#define ALRM_DAY_A1E BIT(3) +#define ALRM_MON_A1E BIT(4) +#define ALRM_MIN_A2E BIT(5) +#define ALRM_HR_A2E BIT(6) +#define ALRM_DAY_A2E BIT(7) + +#define INT_WDIE BIT(0) +#define INT_BSIE BIT(1) +#define INT_TSRIE BIT(2) +#define INT_A2IE BIT(3) +#define INT_A1IE BIT(4) +#define INT_OIE BIT(5) +#define INT_PIE BIT(6) +#define INT_ILP BIT(7) + +#define FLAGS_TSR1F BIT(0) +#define FLAGS_TSR2F BIT(1) +#define FLAGS_TSR3F BIT(2) +#define FLAGS_BSF BIT(3) +#define FLAGS_WDF BIT(4) +#define FLAGS_A1F BIT(5) +#define FLAGS_A2F BIT(6) +#define FLAGS_PIF BIT(7) + +#define PIN_IO_INTAPM GENMASK(1, 0) +#define PIN_IO_INTA_CLK 0 +#define PIN_IO_INTA_BAT 1 +#define PIN_IO_INTA_OUT 2 +#define PIN_IO_INTA_HIZ 3 + +#define OSC_CAP_SEL GENMASK(1, 0) +#define OSC_CAP_6000 0x01 +#define OSC_CAP_12500 0x02 + +#define STOP_EN_STOP BIT(0) + +#define RESET_CPR 0xa4 + +#define NVRAM_SIZE 0x40 + +struct pcf85363 { + struct rtc_device *rtc; + struct regmap *regmap; +}; + +struct pcf85x63_config { + struct regmap_config regmap; + unsigned int num_nvram; +}; + +static int pcf85363_load_capacitance(struct pcf85363 *pcf85363, struct device_node *node) +{ + u32 load = 7000; + u8 value = 0; + + of_property_read_u32(node, "quartz-load-femtofarads", &load); + + switch (load) { + default: + dev_warn(&pcf85363->rtc->dev, "Unknown quartz-load-femtofarads value: %d. Assuming 7000", + load); + fallthrough; + case 7000: + break; + case 6000: + value = OSC_CAP_6000; + break; + case 12500: + value = OSC_CAP_12500; + break; + } + + return regmap_update_bits(pcf85363->regmap, CTRL_OSCILLATOR, + OSC_CAP_SEL, value); +} + +static int pcf85363_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct pcf85363 *pcf85363 = dev_get_drvdata(dev); + unsigned char buf[DT_YEARS + 1]; + int ret, len = sizeof(buf); + + /* read the RTC date and time registers all at once */ + ret = regmap_bulk_read(pcf85363->regmap, DT_100THS, buf, len); + if (ret) { + dev_err(dev, "%s: error %d\n", __func__, ret); + return ret; + } + + tm->tm_year = bcd2bin(buf[DT_YEARS]); + /* adjust for 1900 base of rtc_time */ + tm->tm_year += 100; + + tm->tm_wday = buf[DT_WEEKDAYS] & 7; + buf[DT_SECS] &= 0x7F; + tm->tm_sec = bcd2bin(buf[DT_SECS]); + buf[DT_MINUTES] &= 0x7F; + tm->tm_min = bcd2bin(buf[DT_MINUTES]); + tm->tm_hour = bcd2bin(buf[DT_HOURS]); + tm->tm_mday = bcd2bin(buf[DT_DAYS]); + tm->tm_mon = bcd2bin(buf[DT_MONTHS]) - 1; + + return 0; +} + +static int pcf85363_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct pcf85363 *pcf85363 = dev_get_drvdata(dev); + unsigned char tmp[11]; + unsigned char *buf = &tmp[2]; + int ret; + + tmp[0] = STOP_EN_STOP; + tmp[1] = RESET_CPR; + + buf[DT_100THS] = 0; + buf[DT_SECS] = bin2bcd(tm->tm_sec); + buf[DT_MINUTES] = bin2bcd(tm->tm_min); + buf[DT_HOURS] = bin2bcd(tm->tm_hour); + buf[DT_DAYS] = bin2bcd(tm->tm_mday); + buf[DT_WEEKDAYS] = tm->tm_wday; + buf[DT_MONTHS] = bin2bcd(tm->tm_mon + 1); + buf[DT_YEARS] = bin2bcd(tm->tm_year % 100); + + ret = regmap_bulk_write(pcf85363->regmap, CTRL_STOP_EN, + tmp, 2); + if (ret) + return ret; + + ret = regmap_bulk_write(pcf85363->regmap, DT_100THS, + buf, sizeof(tmp) - 2); + if (ret) + return ret; + + return regmap_write(pcf85363->regmap, CTRL_STOP_EN, 0); +} + +static int pcf85363_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pcf85363 *pcf85363 = dev_get_drvdata(dev); + unsigned char buf[DT_MONTH_ALM1 - DT_SECOND_ALM1 + 1]; + unsigned int val; + int ret; + + ret = regmap_bulk_read(pcf85363->regmap, DT_SECOND_ALM1, buf, + sizeof(buf)); + if (ret) + return ret; + + alrm->time.tm_sec = bcd2bin(buf[0]); + alrm->time.tm_min = bcd2bin(buf[1]); + alrm->time.tm_hour = bcd2bin(buf[2]); + alrm->time.tm_mday = bcd2bin(buf[3]); + alrm->time.tm_mon = bcd2bin(buf[4]) - 1; + + ret = regmap_read(pcf85363->regmap, CTRL_INTA_EN, &val); + if (ret) + return ret; + + alrm->enabled = !!(val & INT_A1IE); + + return 0; +} + +static int _pcf85363_rtc_alarm_irq_enable(struct pcf85363 *pcf85363, unsigned + int enabled) +{ + unsigned int alarm_flags = ALRM_SEC_A1E | ALRM_MIN_A1E | ALRM_HR_A1E | + ALRM_DAY_A1E | ALRM_MON_A1E; + int ret; + + ret = regmap_update_bits(pcf85363->regmap, DT_ALARM_EN, alarm_flags, + enabled ? alarm_flags : 0); + if (ret) + return ret; + + ret = regmap_update_bits(pcf85363->regmap, CTRL_INTA_EN, + INT_A1IE, enabled ? INT_A1IE : 0); + + if (ret || enabled) + return ret; + + /* clear current flags */ + return regmap_update_bits(pcf85363->regmap, CTRL_FLAGS, FLAGS_A1F, 0); +} + +static int pcf85363_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct pcf85363 *pcf85363 = dev_get_drvdata(dev); + + return _pcf85363_rtc_alarm_irq_enable(pcf85363, enabled); +} + +static int pcf85363_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pcf85363 *pcf85363 = dev_get_drvdata(dev); + unsigned char buf[DT_MONTH_ALM1 - DT_SECOND_ALM1 + 1]; + int ret; + + buf[0] = bin2bcd(alrm->time.tm_sec); + buf[1] = bin2bcd(alrm->time.tm_min); + buf[2] = bin2bcd(alrm->time.tm_hour); + buf[3] = bin2bcd(alrm->time.tm_mday); + buf[4] = bin2bcd(alrm->time.tm_mon + 1); + + /* + * Disable the alarm interrupt before changing the value to avoid + * spurious interrupts + */ + ret = _pcf85363_rtc_alarm_irq_enable(pcf85363, 0); + if (ret) + return ret; + + ret = regmap_bulk_write(pcf85363->regmap, DT_SECOND_ALM1, buf, + sizeof(buf)); + if (ret) + return ret; + + return _pcf85363_rtc_alarm_irq_enable(pcf85363, alrm->enabled); +} + +static irqreturn_t pcf85363_rtc_handle_irq(int irq, void *dev_id) +{ + struct pcf85363 *pcf85363 = i2c_get_clientdata(dev_id); + unsigned int flags; + int err; + + err = regmap_read(pcf85363->regmap, CTRL_FLAGS, &flags); + if (err) + return IRQ_NONE; + + if (flags & FLAGS_A1F) { + rtc_update_irq(pcf85363->rtc, 1, RTC_IRQF | RTC_AF); + regmap_update_bits(pcf85363->regmap, CTRL_FLAGS, FLAGS_A1F, 0); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static const struct rtc_class_ops rtc_ops = { + .read_time = pcf85363_rtc_read_time, + .set_time = pcf85363_rtc_set_time, + .read_alarm = pcf85363_rtc_read_alarm, + .set_alarm = pcf85363_rtc_set_alarm, + .alarm_irq_enable = pcf85363_rtc_alarm_irq_enable, +}; + +static int pcf85363_nvram_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct pcf85363 *pcf85363 = priv; + + return regmap_bulk_read(pcf85363->regmap, CTRL_RAM + offset, + val, bytes); +} + +static int pcf85363_nvram_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct pcf85363 *pcf85363 = priv; + + return regmap_bulk_write(pcf85363->regmap, CTRL_RAM + offset, + val, bytes); +} + +static int pcf85x63_nvram_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct pcf85363 *pcf85363 = priv; + unsigned int tmp_val; + int ret; + + ret = regmap_read(pcf85363->regmap, CTRL_RAMBYTE, &tmp_val); + (*(unsigned char *) val) = (unsigned char) tmp_val; + + return ret; +} + +static int pcf85x63_nvram_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct pcf85363 *pcf85363 = priv; + unsigned char tmp_val; + + tmp_val = *((unsigned char *)val); + return regmap_write(pcf85363->regmap, CTRL_RAMBYTE, + (unsigned int)tmp_val); +} + +static const struct pcf85x63_config pcf_85263_config = { + .regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x2f, + }, + .num_nvram = 1 +}; + +static const struct pcf85x63_config pcf_85363_config = { + .regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x7f, + }, + .num_nvram = 2 +}; + +static int pcf85363_probe(struct i2c_client *client) +{ + struct pcf85363 *pcf85363; + const struct pcf85x63_config *config = &pcf_85363_config; + const void *data = of_device_get_match_data(&client->dev); + static struct nvmem_config nvmem_cfg[] = { + { + .name = "pcf85x63-", + .word_size = 1, + .stride = 1, + .size = 1, + .reg_read = pcf85x63_nvram_read, + .reg_write = pcf85x63_nvram_write, + }, { + .name = "pcf85363-", + .word_size = 1, + .stride = 1, + .size = NVRAM_SIZE, + .reg_read = pcf85363_nvram_read, + .reg_write = pcf85363_nvram_write, + }, + }; + int ret, i, err; + bool wakeup_source; + + if (data) + config = data; + + pcf85363 = devm_kzalloc(&client->dev, sizeof(struct pcf85363), + GFP_KERNEL); + if (!pcf85363) + return -ENOMEM; + + pcf85363->regmap = devm_regmap_init_i2c(client, &config->regmap); + if (IS_ERR(pcf85363->regmap)) { + dev_err(&client->dev, "regmap allocation failed\n"); + return PTR_ERR(pcf85363->regmap); + } + + i2c_set_clientdata(client, pcf85363); + + pcf85363->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(pcf85363->rtc)) + return PTR_ERR(pcf85363->rtc); + + err = pcf85363_load_capacitance(pcf85363, client->dev.of_node); + if (err < 0) + return dev_err_probe(&client->dev, err, + "failed to set xtal load capacitance\n"); + + pcf85363->rtc->ops = &rtc_ops; + pcf85363->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + pcf85363->rtc->range_max = RTC_TIMESTAMP_END_2099; + + wakeup_source = device_property_read_bool(&client->dev, + "wakeup-source"); + if (client->irq > 0 || wakeup_source) { + err = regmap_write(pcf85363->regmap, CTRL_FLAGS, 0); + if (err) + return dev_err_probe(&client->dev, err, + "failed to clear flags\n"); + + err = regmap_update_bits(pcf85363->regmap, CTRL_PIN_IO, + PIN_IO_INTAPM, PIN_IO_INTA_OUT); + if (err) + return dev_err_probe(&client->dev, err, + "failed to set interrupt pin mode\n"); + } + + if (client->irq > 0) { + unsigned long irqflags = IRQF_TRIGGER_LOW; + + if (dev_fwnode(&client->dev)) + irqflags = 0; + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, pcf85363_rtc_handle_irq, + irqflags | IRQF_ONESHOT, + "pcf85363", client); + if (ret) { + dev_warn(&client->dev, + "unable to request IRQ, alarms disabled\n"); + client->irq = 0; + } + } + + if (client->irq > 0 || wakeup_source) { + device_init_wakeup(&client->dev, true); + set_bit(RTC_FEATURE_ALARM, pcf85363->rtc->features); + } else { + clear_bit(RTC_FEATURE_ALARM, pcf85363->rtc->features); + } + + ret = devm_rtc_register_device(pcf85363->rtc); + + for (i = 0; i < config->num_nvram; i++) { + nvmem_cfg[i].priv = pcf85363; + devm_rtc_nvmem_register(pcf85363->rtc, &nvmem_cfg[i]); + } + + return ret; +} + +static const __maybe_unused struct of_device_id dev_ids[] = { + { .compatible = "nxp,pcf85263", .data = &pcf_85263_config }, + { .compatible = "nxp,pcf85363", .data = &pcf_85363_config }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dev_ids); + +static struct i2c_driver pcf85363_driver = { + .driver = { + .name = "pcf85363", + .of_match_table = of_match_ptr(dev_ids), + }, + .probe = pcf85363_probe, +}; + +module_i2c_driver(pcf85363_driver); + +MODULE_AUTHOR("Eric Nelson"); +MODULE_DESCRIPTION("pcf85263/pcf85363 I2C RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c new file mode 100644 index 000000000..7eebdb058 --- /dev/null +++ b/drivers/rtc/rtc-pcf8563.c @@ -0,0 +1,593 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * An I2C driver for the Philips PCF8563 RTC + * Copyright 2005-06 Tower Technologies + * + * Author: Alessandro Zummo + * Maintainers: http://www.nslu2-linux.org/ + * + * based on the other drivers in this same directory. + * + * https://www.nxp.com/docs/en/data-sheet/PCF8563.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PCF8563_REG_ST1 0x00 /* status */ +#define PCF8563_REG_ST2 0x01 +#define PCF8563_BIT_AIE BIT(1) +#define PCF8563_BIT_AF BIT(3) +#define PCF8563_BITS_ST2_N (7 << 5) + +#define PCF8563_REG_SC 0x02 /* datetime */ +#define PCF8563_REG_MN 0x03 +#define PCF8563_REG_HR 0x04 +#define PCF8563_REG_DM 0x05 +#define PCF8563_REG_DW 0x06 +#define PCF8563_REG_MO 0x07 +#define PCF8563_REG_YR 0x08 + +#define PCF8563_REG_AMN 0x09 /* alarm */ + +#define PCF8563_REG_CLKO 0x0D /* clock out */ +#define PCF8563_REG_CLKO_FE 0x80 /* clock out enabled */ +#define PCF8563_REG_CLKO_F_MASK 0x03 /* frequenc mask */ +#define PCF8563_REG_CLKO_F_32768HZ 0x00 +#define PCF8563_REG_CLKO_F_1024HZ 0x01 +#define PCF8563_REG_CLKO_F_32HZ 0x02 +#define PCF8563_REG_CLKO_F_1HZ 0x03 + +#define PCF8563_REG_TMRC 0x0E /* timer control */ +#define PCF8563_TMRC_ENABLE BIT(7) +#define PCF8563_TMRC_4096 0 +#define PCF8563_TMRC_64 1 +#define PCF8563_TMRC_1 2 +#define PCF8563_TMRC_1_60 3 +#define PCF8563_TMRC_MASK 3 + +#define PCF8563_REG_TMR 0x0F /* timer */ + +#define PCF8563_SC_LV 0x80 /* low voltage */ +#define PCF8563_MO_C 0x80 /* century */ + +static struct i2c_driver pcf8563_driver; + +struct pcf8563 { + struct rtc_device *rtc; + /* + * The meaning of MO_C bit varies by the chip type. + * From PCF8563 datasheet: this bit is toggled when the years + * register overflows from 99 to 00 + * 0 indicates the century is 20xx + * 1 indicates the century is 19xx + * From RTC8564 datasheet: this bit indicates change of + * century. When the year digit data overflows from 99 to 00, + * this bit is set. By presetting it to 0 while still in the + * 20th century, it will be set in year 2000, ... + * There seems no reliable way to know how the system use this + * bit. So let's do it heuristically, assuming we are live in + * 1970...2069. + */ + int c_polarity; /* 0: MO_C=1 means 19xx, otherwise MO_C=1 means 20xx */ + + struct regmap *regmap; +#ifdef CONFIG_COMMON_CLK + struct clk_hw clkout_hw; +#endif +}; + +static int pcf8563_set_alarm_mode(struct pcf8563 *pcf8563, bool on) +{ + u32 buf; + int err; + + err = regmap_read(pcf8563->regmap, PCF8563_REG_ST2, &buf); + if (err < 0) + return err; + + if (on) + buf |= PCF8563_BIT_AIE; + else + buf &= ~PCF8563_BIT_AIE; + + buf &= ~(PCF8563_BIT_AF | PCF8563_BITS_ST2_N); + + return regmap_write(pcf8563->regmap, PCF8563_REG_ST2, buf); +} + +static int pcf8563_get_alarm_mode(struct pcf8563 *pcf8563, unsigned char *en, + unsigned char *pen) +{ + u32 buf; + int err; + + err = regmap_read(pcf8563->regmap, PCF8563_REG_ST2, &buf); + if (err < 0) + return err; + + if (en) + *en = !!(buf & PCF8563_BIT_AIE); + if (pen) + *pen = !!(buf & PCF8563_BIT_AF); + + return 0; +} + +static irqreturn_t pcf8563_irq(int irq, void *dev_id) +{ + struct pcf8563 *pcf8563 = dev_id; + char pending; + int err; + + err = pcf8563_get_alarm_mode(pcf8563, NULL, &pending); + if (err) + return IRQ_NONE; + + if (pending) { + rtc_update_irq(pcf8563->rtc, 1, RTC_IRQF | RTC_AF); + pcf8563_set_alarm_mode(pcf8563, 1); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +/* + * In the routines that deal directly with the pcf8563 hardware, we use + * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. + */ +static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct pcf8563 *pcf8563 = dev_get_drvdata(dev); + unsigned char buf[9]; + int err; + + err = regmap_bulk_read(pcf8563->regmap, PCF8563_REG_ST1, buf, + sizeof(buf)); + if (err < 0) + return err; + + if (buf[PCF8563_REG_SC] & PCF8563_SC_LV) { + dev_err(dev, + "low voltage detected, date/time is not reliable.\n"); + return -EINVAL; + } + + dev_dbg(dev, + "%s: raw data is st1=%02x, st2=%02x, sec=%02x, min=%02x, hr=%02x, " + "mday=%02x, wday=%02x, mon=%02x, year=%02x\n", + __func__, + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7], + buf[8]); + + tm->tm_sec = bcd2bin(buf[PCF8563_REG_SC] & 0x7F); + tm->tm_min = bcd2bin(buf[PCF8563_REG_MN] & 0x7F); + tm->tm_hour = bcd2bin(buf[PCF8563_REG_HR] & 0x3F); /* rtc hr 0-23 */ + tm->tm_mday = bcd2bin(buf[PCF8563_REG_DM] & 0x3F); + tm->tm_wday = buf[PCF8563_REG_DW] & 0x07; + tm->tm_mon = bcd2bin(buf[PCF8563_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */ + tm->tm_year = bcd2bin(buf[PCF8563_REG_YR]) + 100; + /* detect the polarity heuristically. see note above. */ + pcf8563->c_polarity = (buf[PCF8563_REG_MO] & PCF8563_MO_C) ? + (tm->tm_year >= 100) : (tm->tm_year < 100); + + dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + return 0; +} + +static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct pcf8563 *pcf8563 = dev_get_drvdata(dev); + unsigned char buf[9]; + + dev_dbg(dev, "%s: secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + /* hours, minutes and seconds */ + buf[PCF8563_REG_SC] = bin2bcd(tm->tm_sec); + buf[PCF8563_REG_MN] = bin2bcd(tm->tm_min); + buf[PCF8563_REG_HR] = bin2bcd(tm->tm_hour); + + buf[PCF8563_REG_DM] = bin2bcd(tm->tm_mday); + + /* month, 1 - 12 */ + buf[PCF8563_REG_MO] = bin2bcd(tm->tm_mon + 1); + + /* year and century */ + buf[PCF8563_REG_YR] = bin2bcd(tm->tm_year - 100); + if (pcf8563->c_polarity ? (tm->tm_year >= 100) : (tm->tm_year < 100)) + buf[PCF8563_REG_MO] |= PCF8563_MO_C; + + buf[PCF8563_REG_DW] = tm->tm_wday & 0x07; + + return regmap_bulk_write(pcf8563->regmap, PCF8563_REG_SC, + buf + PCF8563_REG_SC, + sizeof(buf) - PCF8563_REG_SC); +} + +static int pcf8563_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct pcf8563 *pcf8563 = dev_get_drvdata(dev); + int ret; + + switch (cmd) { + case RTC_VL_READ: + ret = regmap_test_bits(pcf8563->regmap, PCF8563_REG_SC, + PCF8563_SC_LV); + if (ret < 0) + return ret; + + return put_user(ret ? RTC_VL_DATA_INVALID : 0, + (unsigned int __user *)arg); + default: + return -ENOIOCTLCMD; + } +} + +static int pcf8563_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *tm) +{ + struct pcf8563 *pcf8563 = dev_get_drvdata(dev); + unsigned char buf[4]; + int err; + + err = regmap_bulk_read(pcf8563->regmap, PCF8563_REG_AMN, buf, + sizeof(buf)); + if (err < 0) + return err; + + dev_dbg(dev, + "%s: raw data is min=%02x, hr=%02x, mday=%02x, wday=%02x\n", + __func__, buf[0], buf[1], buf[2], buf[3]); + + tm->time.tm_sec = 0; + tm->time.tm_min = bcd2bin(buf[0] & 0x7F); + tm->time.tm_hour = bcd2bin(buf[1] & 0x3F); + tm->time.tm_mday = bcd2bin(buf[2] & 0x3F); + tm->time.tm_wday = bcd2bin(buf[3] & 0x7); + + err = pcf8563_get_alarm_mode(pcf8563, &tm->enabled, &tm->pending); + if (err < 0) + return err; + + dev_dbg(dev, "%s: tm is mins=%d, hours=%d, mday=%d, wday=%d," + " enabled=%d, pending=%d\n", __func__, tm->time.tm_min, + tm->time.tm_hour, tm->time.tm_mday, tm->time.tm_wday, + tm->enabled, tm->pending); + + return 0; +} + +static int pcf8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm) +{ + struct pcf8563 *pcf8563 = dev_get_drvdata(dev); + unsigned char buf[4]; + int err; + + buf[0] = bin2bcd(tm->time.tm_min); + buf[1] = bin2bcd(tm->time.tm_hour); + buf[2] = bin2bcd(tm->time.tm_mday); + buf[3] = tm->time.tm_wday & 0x07; + + err = regmap_bulk_write(pcf8563->regmap, PCF8563_REG_AMN, buf, + sizeof(buf)); + if (err) + return err; + + return pcf8563_set_alarm_mode(pcf8563, !!tm->enabled); +} + +static int pcf8563_irq_enable(struct device *dev, unsigned int enabled) +{ + struct pcf8563 *pcf8563 = dev_get_drvdata(dev); + + dev_dbg(dev, "%s: en=%d\n", __func__, enabled); + return pcf8563_set_alarm_mode(pcf8563, !!enabled); +} + +#ifdef CONFIG_COMMON_CLK +/* + * Handling of the clkout + */ + +#define clkout_hw_to_pcf8563(_hw) container_of(_hw, struct pcf8563, clkout_hw) + +static const int clkout_rates[] = { + 32768, + 1024, + 32, + 1, +}; + +static unsigned long pcf8563_clkout_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw); + u32 buf; + int ret; + + ret = regmap_read(pcf8563->regmap, PCF8563_REG_CLKO, &buf); + if (ret < 0) + return 0; + + buf &= PCF8563_REG_CLKO_F_MASK; + return clkout_rates[buf]; +} + +static int pcf8563_clkout_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(clkout_rates); i++) + if (clkout_rates[i] <= req->rate) { + req->rate = clkout_rates[i]; + + return 0; + } + + req->rate = clkout_rates[0]; + + return 0; +} + +static int pcf8563_clkout_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw); + int i, ret; + u32 buf; + + ret = regmap_read(pcf8563->regmap, PCF8563_REG_CLKO, &buf); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(clkout_rates); i++) + if (clkout_rates[i] == rate) { + buf &= ~PCF8563_REG_CLKO_F_MASK; + buf |= i; + return regmap_update_bits(pcf8563->regmap, + PCF8563_REG_CLKO, + PCF8563_REG_CLKO_F_MASK, + buf); + } + + return -EINVAL; +} + +static int pcf8563_clkout_control(struct clk_hw *hw, bool enable) +{ + struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw); + u32 buf; + int ret; + + ret = regmap_read(pcf8563->regmap, PCF8563_REG_CLKO, &buf); + if (ret < 0) + return ret; + + if (enable) + buf |= PCF8563_REG_CLKO_FE; + else + buf &= ~PCF8563_REG_CLKO_FE; + + return regmap_update_bits(pcf8563->regmap, PCF8563_REG_CLKO, + PCF8563_REG_CLKO_FE, buf); +} + +static int pcf8563_clkout_prepare(struct clk_hw *hw) +{ + return pcf8563_clkout_control(hw, 1); +} + +static void pcf8563_clkout_unprepare(struct clk_hw *hw) +{ + pcf8563_clkout_control(hw, 0); +} + +static int pcf8563_clkout_is_prepared(struct clk_hw *hw) +{ + struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw); + u32 buf; + int ret; + + ret = regmap_read(pcf8563->regmap, PCF8563_REG_CLKO, &buf); + if (ret < 0) + return ret; + + return !!(buf & PCF8563_REG_CLKO_FE); +} + +static const struct clk_ops pcf8563_clkout_ops = { + .prepare = pcf8563_clkout_prepare, + .unprepare = pcf8563_clkout_unprepare, + .is_prepared = pcf8563_clkout_is_prepared, + .recalc_rate = pcf8563_clkout_recalc_rate, + .determine_rate = pcf8563_clkout_determine_rate, + .set_rate = pcf8563_clkout_set_rate, +}; + +static struct clk *pcf8563_clkout_register_clk(struct pcf8563 *pcf8563) +{ + struct device_node *node = pcf8563->rtc->dev.parent->of_node; + struct clk_init_data init = {}; + struct clk *clk; + int ret; + + /* disable the clkout output */ + ret = regmap_clear_bits(pcf8563->regmap, PCF8563_REG_CLKO, + PCF8563_REG_CLKO_FE); + if (ret < 0) + return ERR_PTR(ret); + + init.name = "pcf8563-clkout"; + init.ops = &pcf8563_clkout_ops; + init.flags = 0; + init.parent_names = NULL; + init.num_parents = 0; + pcf8563->clkout_hw.init = &init; + + /* optional override of the clockname */ + of_property_read_string(node, "clock-output-names", &init.name); + + /* register the clock */ + clk = devm_clk_register(&pcf8563->rtc->dev, &pcf8563->clkout_hw); + + if (!IS_ERR(clk)) + devm_of_clk_add_hw_provider(pcf8563->rtc->dev.parent, + of_clk_hw_simple_get, + &pcf8563->clkout_hw); + + return clk; +} +#endif + +static const struct rtc_class_ops pcf8563_rtc_ops = { + .ioctl = pcf8563_rtc_ioctl, + .read_time = pcf8563_rtc_read_time, + .set_time = pcf8563_rtc_set_time, + .read_alarm = pcf8563_rtc_read_alarm, + .set_alarm = pcf8563_rtc_set_alarm, + .alarm_irq_enable = pcf8563_irq_enable, +}; + +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xF, +}; + +static int pcf8563_probe(struct i2c_client *client) +{ + struct pcf8563 *pcf8563; + int err; + + dev_dbg(&client->dev, "%s\n", __func__); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + pcf8563 = devm_kzalloc(&client->dev, sizeof(struct pcf8563), + GFP_KERNEL); + if (!pcf8563) + return -ENOMEM; + + pcf8563->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(pcf8563->regmap)) + return PTR_ERR(pcf8563->regmap); + + i2c_set_clientdata(client, pcf8563); + device_set_wakeup_capable(&client->dev, 1); + + /* Set timer to lowest frequency to save power (ref Haoyu datasheet) */ + err = regmap_set_bits(pcf8563->regmap, PCF8563_REG_TMRC, + PCF8563_TMRC_1_60); + if (err < 0) { + dev_err(&client->dev, "%s: write error\n", __func__); + return err; + } + + /* Clear flags and disable interrupts */ + err = regmap_write(pcf8563->regmap, PCF8563_REG_ST2, 0); + if (err < 0) { + dev_err(&client->dev, "%s: write error\n", __func__); + return err; + } + + pcf8563->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(pcf8563->rtc)) + return PTR_ERR(pcf8563->rtc); + + pcf8563->rtc->ops = &pcf8563_rtc_ops; + /* the pcf8563 alarm only supports a minute accuracy */ + set_bit(RTC_FEATURE_ALARM_RES_MINUTE, pcf8563->rtc->features); + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, pcf8563->rtc->features); + clear_bit(RTC_FEATURE_ALARM, pcf8563->rtc->features); + pcf8563->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + pcf8563->rtc->range_max = RTC_TIMESTAMP_END_2099; + pcf8563->rtc->set_start_time = true; + + if (client->irq > 0) { + unsigned long irqflags = IRQF_TRIGGER_LOW; + + if (dev_fwnode(&client->dev)) + irqflags = 0; + + err = devm_request_threaded_irq(&client->dev, client->irq, + NULL, pcf8563_irq, + IRQF_SHARED | IRQF_ONESHOT | irqflags, + pcf8563_driver.driver.name, client); + if (err) { + dev_err(&client->dev, "unable to request IRQ %d\n", + client->irq); + return err; + } + } else { + client->irq = 0; + } + + if (client->irq > 0 || device_property_read_bool(&client->dev, "wakeup-source")) { + device_init_wakeup(&client->dev, true); + set_bit(RTC_FEATURE_ALARM, pcf8563->rtc->features); + } + + err = devm_rtc_register_device(pcf8563->rtc); + if (err) + return err; + +#ifdef CONFIG_COMMON_CLK + /* register clk in common clk framework */ + pcf8563_clkout_register_clk(pcf8563); +#endif + + return 0; +} + +static const struct i2c_device_id pcf8563_id[] = { + { .name = "pcf8563" }, + { .name = "rtc8564" }, + { .name = "pca8565" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcf8563_id); + +#ifdef CONFIG_OF +static const struct of_device_id pcf8563_of_match[] = { + { .compatible = "nxp,pcf8563" }, + { .compatible = "epson,rtc8564" }, + { .compatible = "microcrystal,rv8564" }, + { .compatible = "nxp,pca8565" }, + {} +}; +MODULE_DEVICE_TABLE(of, pcf8563_of_match); +#endif + +static struct i2c_driver pcf8563_driver = { + .driver = { + .name = "rtc-pcf8563", + .of_match_table = of_match_ptr(pcf8563_of_match), + }, + .probe = pcf8563_probe, + .id_table = pcf8563_id, +}; + +module_i2c_driver(pcf8563_driver); + +MODULE_AUTHOR("Alessandro Zummo "); +MODULE_DESCRIPTION("Philips PCF8563/Epson RTC8564 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-pcf8583.c b/drivers/rtc/rtc-pcf8583.c new file mode 100644 index 000000000..df5e20cbc --- /dev/null +++ b/drivers/rtc/rtc-pcf8583.c @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * drivers/rtc/rtc-pcf8583.c + * + * Copyright (C) 2000 Russell King + * Copyright (C) 2008 Wolfram Sang & Juergen Beisert, Pengutronix + * + * Driver for PCF8583 RTC & RAM chip + * + * Converted to the generic RTC susbsystem by G. Liakhovetski (2006) + */ +#include +#include +#include +#include +#include +#include +#include +#include + +struct rtc_mem { + unsigned int loc; + unsigned int nr; + unsigned char *data; +}; + +struct pcf8583 { + struct rtc_device *rtc; + unsigned char ctrl; +}; + +#define CTRL_STOP 0x80 +#define CTRL_HOLD 0x40 +#define CTRL_32KHZ 0x00 +#define CTRL_MASK 0x08 +#define CTRL_ALARMEN 0x04 +#define CTRL_ALARM 0x02 +#define CTRL_TIMER 0x01 + + +static struct i2c_driver pcf8583_driver; + +#define get_ctrl(x) ((struct pcf8583 *)i2c_get_clientdata(x))->ctrl +#define set_ctrl(x, v) get_ctrl(x) = v + +#define CMOS_YEAR (64 + 128) +#define CMOS_CHECKSUM (63) + +static int pcf8583_get_datetime(struct i2c_client *client, struct rtc_time *dt) +{ + unsigned char buf[8], addr[1] = { 1 }; + struct i2c_msg msgs[2] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = addr, + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .len = 6, + .buf = buf, + } + }; + int ret; + + memset(buf, 0, sizeof(buf)); + + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret == 2) { + dt->tm_year = buf[4] >> 6; + dt->tm_wday = buf[5] >> 5; + + buf[4] &= 0x3f; + buf[5] &= 0x1f; + + dt->tm_sec = bcd2bin(buf[1]); + dt->tm_min = bcd2bin(buf[2]); + dt->tm_hour = bcd2bin(buf[3]); + dt->tm_mday = bcd2bin(buf[4]); + dt->tm_mon = bcd2bin(buf[5]) - 1; + } + + return ret == 2 ? 0 : -EIO; +} + +static int pcf8583_set_datetime(struct i2c_client *client, struct rtc_time *dt, int datetoo) +{ + unsigned char buf[8]; + int ret, len = 6; + + buf[0] = 0; + buf[1] = get_ctrl(client) | 0x80; + buf[2] = 0; + buf[3] = bin2bcd(dt->tm_sec); + buf[4] = bin2bcd(dt->tm_min); + buf[5] = bin2bcd(dt->tm_hour); + + if (datetoo) { + len = 8; + buf[6] = bin2bcd(dt->tm_mday) | (dt->tm_year << 6); + buf[7] = bin2bcd(dt->tm_mon + 1) | (dt->tm_wday << 5); + } + + ret = i2c_master_send(client, (char *)buf, len); + if (ret != len) + return -EIO; + + buf[1] = get_ctrl(client); + ret = i2c_master_send(client, (char *)buf, 2); + + return ret == 2 ? 0 : -EIO; +} + +static int pcf8583_get_ctrl(struct i2c_client *client, unsigned char *ctrl) +{ + *ctrl = get_ctrl(client); + return 0; +} + +static int pcf8583_set_ctrl(struct i2c_client *client, unsigned char *ctrl) +{ + unsigned char buf[2]; + + buf[0] = 0; + buf[1] = *ctrl; + set_ctrl(client, *ctrl); + + return i2c_master_send(client, (char *)buf, 2); +} + +static int pcf8583_read_mem(struct i2c_client *client, struct rtc_mem *mem) +{ + unsigned char addr[1]; + struct i2c_msg msgs[2] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = addr, + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .len = mem->nr, + .buf = mem->data, + } + }; + + if (mem->loc < 8) + return -EINVAL; + + addr[0] = mem->loc; + + return i2c_transfer(client->adapter, msgs, 2) == 2 ? 0 : -EIO; +} + +static int pcf8583_write_mem(struct i2c_client *client, struct rtc_mem *mem) +{ + unsigned char buf[9]; + int ret; + + if (mem->loc < 8 || mem->nr > 8) + return -EINVAL; + + buf[0] = mem->loc; + memcpy(buf + 1, mem->data, mem->nr); + + ret = i2c_master_send(client, buf, mem->nr + 1); + return ret == mem->nr + 1 ? 0 : -EIO; +} + +static int pcf8583_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned char ctrl, year[2]; + struct rtc_mem mem = { + .loc = CMOS_YEAR, + .nr = sizeof(year), + .data = year + }; + int real_year, year_offset, err; + + /* + * Ensure that the RTC is running. + */ + pcf8583_get_ctrl(client, &ctrl); + if (ctrl & (CTRL_STOP | CTRL_HOLD)) { + unsigned char new_ctrl = ctrl & ~(CTRL_STOP | CTRL_HOLD); + + dev_warn(dev, "resetting control %02x -> %02x\n", + ctrl, new_ctrl); + + err = pcf8583_set_ctrl(client, &new_ctrl); + if (err < 0) + return err; + } + + if (pcf8583_get_datetime(client, tm) || + pcf8583_read_mem(client, &mem)) + return -EIO; + + real_year = year[0]; + + /* + * The RTC year holds the LSB two bits of the current + * year, which should reflect the LSB two bits of the + * CMOS copy of the year. Any difference indicates + * that we have to correct the CMOS version. + */ + year_offset = tm->tm_year - (real_year & 3); + if (year_offset < 0) + /* + * RTC year wrapped. Adjust it appropriately. + */ + year_offset += 4; + + tm->tm_year = (real_year + year_offset + year[1] * 100) - 1900; + + return 0; +} + +static int pcf8583_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned char year[2], chk; + struct rtc_mem cmos_year = { + .loc = CMOS_YEAR, + .nr = sizeof(year), + .data = year + }; + struct rtc_mem cmos_check = { + .loc = CMOS_CHECKSUM, + .nr = 1, + .data = &chk + }; + unsigned int proper_year = tm->tm_year + 1900; + int ret; + + /* + * The RTC's own 2-bit year must reflect the least + * significant two bits of the CMOS year. + */ + + ret = pcf8583_set_datetime(client, tm, 1); + if (ret) + return ret; + + ret = pcf8583_read_mem(client, &cmos_check); + if (ret) + return ret; + + ret = pcf8583_read_mem(client, &cmos_year); + if (ret) + return ret; + + chk -= year[1] + year[0]; + + year[1] = proper_year / 100; + year[0] = proper_year % 100; + + chk += year[1] + year[0]; + + ret = pcf8583_write_mem(client, &cmos_year); + + if (ret) + return ret; + + ret = pcf8583_write_mem(client, &cmos_check); + + return ret; +} + +static const struct rtc_class_ops pcf8583_rtc_ops = { + .read_time = pcf8583_rtc_read_time, + .set_time = pcf8583_rtc_set_time, +}; + +static int pcf8583_probe(struct i2c_client *client) +{ + struct pcf8583 *pcf8583; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + pcf8583 = devm_kzalloc(&client->dev, sizeof(struct pcf8583), + GFP_KERNEL); + if (!pcf8583) + return -ENOMEM; + + i2c_set_clientdata(client, pcf8583); + + pcf8583->rtc = devm_rtc_device_register(&client->dev, + pcf8583_driver.driver.name, + &pcf8583_rtc_ops, THIS_MODULE); + + return PTR_ERR_OR_ZERO(pcf8583->rtc); +} + +static const struct i2c_device_id pcf8583_id[] = { + { .name = "pcf8583" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcf8583_id); + +static struct i2c_driver pcf8583_driver = { + .driver = { + .name = "pcf8583", + }, + .probe = pcf8583_probe, + .id_table = pcf8583_id, +}; + +module_i2c_driver(pcf8583_driver); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("PCF8583 I2C RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-pic32.c b/drivers/rtc/rtc-pic32.c new file mode 100644 index 000000000..3c7a38a4a --- /dev/null +++ b/drivers/rtc/rtc-pic32.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * PIC32 RTC driver + * + * Joshua Henderson + * Copyright (C) 2016 Microchip Technology Inc. All rights reserved. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PIC32_RTCCON 0x00 +#define PIC32_RTCCON_ON BIT(15) +#define PIC32_RTCCON_SIDL BIT(13) +#define PIC32_RTCCON_RTCCLKSEL (3 << 9) +#define PIC32_RTCCON_RTCCLKON BIT(6) +#define PIC32_RTCCON_RTCWREN BIT(3) +#define PIC32_RTCCON_RTCSYNC BIT(2) +#define PIC32_RTCCON_HALFSEC BIT(1) +#define PIC32_RTCCON_RTCOE BIT(0) + +#define PIC32_RTCALRM 0x10 +#define PIC32_RTCALRM_ALRMEN BIT(15) +#define PIC32_RTCALRM_CHIME BIT(14) +#define PIC32_RTCALRM_PIV BIT(13) +#define PIC32_RTCALRM_ALARMSYNC BIT(12) +#define PIC32_RTCALRM_AMASK 0x0F00 +#define PIC32_RTCALRM_ARPT 0xFF + +#define PIC32_RTCHOUR 0x23 +#define PIC32_RTCMIN 0x22 +#define PIC32_RTCSEC 0x21 +#define PIC32_RTCYEAR 0x33 +#define PIC32_RTCMON 0x32 +#define PIC32_RTCDAY 0x31 + +#define PIC32_ALRMTIME 0x40 +#define PIC32_ALRMDATE 0x50 + +#define PIC32_ALRMHOUR 0x43 +#define PIC32_ALRMMIN 0x42 +#define PIC32_ALRMSEC 0x41 +#define PIC32_ALRMYEAR 0x53 +#define PIC32_ALRMMON 0x52 +#define PIC32_ALRMDAY 0x51 + +struct pic32_rtc_dev { + struct rtc_device *rtc; + void __iomem *reg_base; + struct clk *clk; + spinlock_t alarm_lock; + int alarm_irq; + bool alarm_clk_enabled; +}; + +static void pic32_rtc_alarm_clk_enable(struct pic32_rtc_dev *pdata, + bool enable) +{ + unsigned long flags; + + spin_lock_irqsave(&pdata->alarm_lock, flags); + if (enable) { + if (!pdata->alarm_clk_enabled) { + clk_enable(pdata->clk); + pdata->alarm_clk_enabled = true; + } + } else { + if (pdata->alarm_clk_enabled) { + clk_disable(pdata->clk); + pdata->alarm_clk_enabled = false; + } + } + spin_unlock_irqrestore(&pdata->alarm_lock, flags); +} + +static irqreturn_t pic32_rtc_alarmirq(int irq, void *id) +{ + struct pic32_rtc_dev *pdata = (struct pic32_rtc_dev *)id; + + clk_enable(pdata->clk); + rtc_update_irq(pdata->rtc, 1, RTC_AF | RTC_IRQF); + clk_disable(pdata->clk); + + pic32_rtc_alarm_clk_enable(pdata, false); + + return IRQ_HANDLED; +} + +static int pic32_rtc_setaie(struct device *dev, unsigned int enabled) +{ + struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); + void __iomem *base = pdata->reg_base; + + clk_enable(pdata->clk); + + writel(PIC32_RTCALRM_ALRMEN, + base + (enabled ? PIC32_SET(PIC32_RTCALRM) : + PIC32_CLR(PIC32_RTCALRM))); + + clk_disable(pdata->clk); + + pic32_rtc_alarm_clk_enable(pdata, enabled); + + return 0; +} + +static int pic32_rtc_setfreq(struct device *dev, int freq) +{ + struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); + void __iomem *base = pdata->reg_base; + + clk_enable(pdata->clk); + + writel(PIC32_RTCALRM_AMASK, base + PIC32_CLR(PIC32_RTCALRM)); + writel(freq << 8, base + PIC32_SET(PIC32_RTCALRM)); + writel(PIC32_RTCALRM_CHIME, base + PIC32_SET(PIC32_RTCALRM)); + + clk_disable(pdata->clk); + + return 0; +} + +static int pic32_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) +{ + struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); + void __iomem *base = pdata->reg_base; + unsigned int tries = 0; + + clk_enable(pdata->clk); + + do { + rtc_tm->tm_hour = readb(base + PIC32_RTCHOUR); + rtc_tm->tm_min = readb(base + PIC32_RTCMIN); + rtc_tm->tm_mon = readb(base + PIC32_RTCMON); + rtc_tm->tm_mday = readb(base + PIC32_RTCDAY); + rtc_tm->tm_year = readb(base + PIC32_RTCYEAR); + rtc_tm->tm_sec = readb(base + PIC32_RTCSEC); + + /* + * The only way to work out whether the system was mid-update + * when we read it is to check the second counter, and if it + * is zero, then we re-try the entire read. + */ + tries += 1; + } while (rtc_tm->tm_sec == 0 && tries < 2); + + rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec); + rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min); + rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour); + rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday); + rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon) - 1; + rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year); + + rtc_tm->tm_year += 100; + + dev_dbg(dev, "read time %ptR\n", rtc_tm); + + clk_disable(pdata->clk); + return 0; +} + +static int pic32_rtc_settime(struct device *dev, struct rtc_time *tm) +{ + struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); + void __iomem *base = pdata->reg_base; + + dev_dbg(dev, "set time %ptR\n", tm); + + clk_enable(pdata->clk); + writeb(bin2bcd(tm->tm_sec), base + PIC32_RTCSEC); + writeb(bin2bcd(tm->tm_min), base + PIC32_RTCMIN); + writeb(bin2bcd(tm->tm_hour), base + PIC32_RTCHOUR); + writeb(bin2bcd(tm->tm_mday), base + PIC32_RTCDAY); + writeb(bin2bcd(tm->tm_mon + 1), base + PIC32_RTCMON); + writeb(bin2bcd(tm->tm_year - 100), base + PIC32_RTCYEAR); + clk_disable(pdata->clk); + + return 0; +} + +static int pic32_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); + struct rtc_time *alm_tm = &alrm->time; + void __iomem *base = pdata->reg_base; + unsigned int alm_en; + + clk_enable(pdata->clk); + alm_tm->tm_sec = readb(base + PIC32_ALRMSEC); + alm_tm->tm_min = readb(base + PIC32_ALRMMIN); + alm_tm->tm_hour = readb(base + PIC32_ALRMHOUR); + alm_tm->tm_mon = readb(base + PIC32_ALRMMON); + alm_tm->tm_mday = readb(base + PIC32_ALRMDAY); + alm_tm->tm_year = readb(base + PIC32_ALRMYEAR); + + alm_en = readb(base + PIC32_RTCALRM); + + alrm->enabled = (alm_en & PIC32_RTCALRM_ALRMEN) ? 1 : 0; + + dev_dbg(dev, "getalarm: %d, %ptR\n", alm_en, alm_tm); + + alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec); + alm_tm->tm_min = bcd2bin(alm_tm->tm_min); + alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour); + alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday); + alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon) - 1; + alm_tm->tm_year = bcd2bin(alm_tm->tm_year); + + clk_disable(pdata->clk); + return 0; +} + +static int pic32_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); + struct rtc_time *tm = &alrm->time; + void __iomem *base = pdata->reg_base; + + clk_enable(pdata->clk); + dev_dbg(dev, "setalarm: %d, %ptR\n", alrm->enabled, tm); + + writel(0x00, base + PIC32_ALRMTIME); + writel(0x00, base + PIC32_ALRMDATE); + + pic32_rtc_setaie(dev, alrm->enabled); + + clk_disable(pdata->clk); + return 0; +} + +static int pic32_rtc_proc(struct device *dev, struct seq_file *seq) +{ + struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); + void __iomem *base = pdata->reg_base; + unsigned int repeat; + + clk_enable(pdata->clk); + + repeat = readw(base + PIC32_RTCALRM); + repeat &= PIC32_RTCALRM_ARPT; + seq_printf(seq, "periodic_IRQ\t: %s\n", repeat ? "yes" : "no"); + + clk_disable(pdata->clk); + return 0; +} + +static const struct rtc_class_ops pic32_rtcops = { + .read_time = pic32_rtc_gettime, + .set_time = pic32_rtc_settime, + .read_alarm = pic32_rtc_getalarm, + .set_alarm = pic32_rtc_setalarm, + .proc = pic32_rtc_proc, + .alarm_irq_enable = pic32_rtc_setaie, +}; + +static void pic32_rtc_enable(struct pic32_rtc_dev *pdata, int en) +{ + void __iomem *base = pdata->reg_base; + + if (!base) + return; + + clk_enable(pdata->clk); + if (!en) { + writel(PIC32_RTCCON_ON, base + PIC32_CLR(PIC32_RTCCON)); + } else { + pic32_syskey_unlock(); + + writel(PIC32_RTCCON_RTCWREN, base + PIC32_SET(PIC32_RTCCON)); + writel(3 << 9, base + PIC32_CLR(PIC32_RTCCON)); + + if (!(readl(base + PIC32_RTCCON) & PIC32_RTCCON_ON)) + writel(PIC32_RTCCON_ON, base + PIC32_SET(PIC32_RTCCON)); + } + clk_disable(pdata->clk); +} + +static void pic32_rtc_remove(struct platform_device *pdev) +{ + struct pic32_rtc_dev *pdata = platform_get_drvdata(pdev); + + pic32_rtc_setaie(&pdev->dev, 0); + clk_unprepare(pdata->clk); + pdata->clk = NULL; +} + +static int pic32_rtc_probe(struct platform_device *pdev) +{ + struct pic32_rtc_dev *pdata; + int ret; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + platform_set_drvdata(pdev, pdata); + + pdata->alarm_irq = platform_get_irq(pdev, 0); + if (pdata->alarm_irq < 0) + return pdata->alarm_irq; + + pdata->reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pdata->reg_base)) + return PTR_ERR(pdata->reg_base); + + pdata->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pdata->clk)) { + dev_err(&pdev->dev, "failed to find rtc clock source\n"); + ret = PTR_ERR(pdata->clk); + pdata->clk = NULL; + return ret; + } + + spin_lock_init(&pdata->alarm_lock); + + pdata->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(pdata->rtc)) + return PTR_ERR(pdata->rtc); + + clk_prepare_enable(pdata->clk); + + pic32_rtc_enable(pdata, 1); + + device_init_wakeup(&pdev->dev, true); + + pdata->rtc->ops = &pic32_rtcops; + pdata->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + pdata->rtc->range_max = RTC_TIMESTAMP_END_2099; + + ret = devm_rtc_register_device(pdata->rtc); + if (ret) + goto err_nortc; + + pic32_rtc_setfreq(&pdev->dev, 1); + ret = devm_request_irq(&pdev->dev, pdata->alarm_irq, + pic32_rtc_alarmirq, 0, + dev_name(&pdev->dev), pdata); + if (ret) { + dev_err(&pdev->dev, + "IRQ %d error %d\n", pdata->alarm_irq, ret); + goto err_nortc; + } + + clk_disable(pdata->clk); + + return 0; + +err_nortc: + pic32_rtc_enable(pdata, 0); + clk_disable_unprepare(pdata->clk); + + return ret; +} + +static const struct of_device_id pic32_rtc_dt_ids[] = { + { .compatible = "microchip,pic32mzda-rtc" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, pic32_rtc_dt_ids); + +static struct platform_driver pic32_rtc_driver = { + .probe = pic32_rtc_probe, + .remove = pic32_rtc_remove, + .driver = { + .name = "pic32-rtc", + .of_match_table = of_match_ptr(pic32_rtc_dt_ids), + }, +}; +module_platform_driver(pic32_rtc_driver); + +MODULE_DESCRIPTION("Microchip PIC32 RTC Driver"); +MODULE_AUTHOR("Joshua Henderson "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-pl030.c b/drivers/rtc/rtc-pl030.c new file mode 100644 index 000000000..5caaa714f --- /dev/null +++ b/drivers/rtc/rtc-pl030.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/drivers/rtc/rtc-pl030.c + * + * Copyright (C) 2000-2001 Deep Blue Solutions Ltd. + */ +#include +#include +#include +#include +#include +#include +#include + +#define RTC_DR (0) +#define RTC_MR (4) +#define RTC_STAT (8) +#define RTC_EOI (8) +#define RTC_LR (12) +#define RTC_CR (16) +#define RTC_CR_MIE (1 << 0) + +struct pl030_rtc { + void __iomem *base; +}; + +static irqreturn_t pl030_interrupt(int irq, void *dev_id) +{ + struct pl030_rtc *rtc = dev_id; + writel(0, rtc->base + RTC_EOI); + return IRQ_HANDLED; +} + +static int pl030_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pl030_rtc *rtc = dev_get_drvdata(dev); + + rtc_time64_to_tm(readl(rtc->base + RTC_MR), &alrm->time); + return 0; +} + +static int pl030_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pl030_rtc *rtc = dev_get_drvdata(dev); + + writel(rtc_tm_to_time64(&alrm->time), rtc->base + RTC_MR); + + return 0; +} + +static int pl030_read_time(struct device *dev, struct rtc_time *tm) +{ + struct pl030_rtc *rtc = dev_get_drvdata(dev); + + rtc_time64_to_tm(readl(rtc->base + RTC_DR), tm); + + return 0; +} + +/* + * Set the RTC time. Unfortunately, we can't accurately set + * the point at which the counter updates. + * + * Also, since RTC_LR is transferred to RTC_CR on next rising + * edge of the 1Hz clock, we must write the time one second + * in advance. + */ +static int pl030_set_time(struct device *dev, struct rtc_time *tm) +{ + struct pl030_rtc *rtc = dev_get_drvdata(dev); + + writel(rtc_tm_to_time64(tm) + 1, rtc->base + RTC_LR); + + return 0; +} + +static const struct rtc_class_ops pl030_ops = { + .read_time = pl030_read_time, + .set_time = pl030_set_time, + .read_alarm = pl030_read_alarm, + .set_alarm = pl030_set_alarm, +}; + +static int pl030_probe(struct amba_device *dev, const struct amba_id *id) +{ + struct pl030_rtc *rtc; + int ret; + struct rtc_device *rtc_dev; + + ret = amba_request_regions(dev, NULL); + if (ret) + goto err_req; + + rtc = devm_kzalloc(&dev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) { + ret = -ENOMEM; + goto err_rtc; + } + + rtc_dev = devm_rtc_allocate_device(&dev->dev); + if (IS_ERR(rtc_dev)) { + ret = PTR_ERR(rtc_dev); + goto err_rtc; + } + + rtc_dev->ops = &pl030_ops; + rtc_dev->range_max = U32_MAX; + rtc->base = ioremap(dev->res.start, resource_size(&dev->res)); + if (!rtc->base) { + ret = -ENOMEM; + goto err_rtc; + } + + __raw_writel(0, rtc->base + RTC_CR); + __raw_writel(0, rtc->base + RTC_EOI); + + amba_set_drvdata(dev, rtc); + + ret = request_irq(dev->irq[0], pl030_interrupt, 0, + "rtc-pl030", rtc); + if (ret) + goto err_irq; + + ret = devm_rtc_register_device(rtc_dev); + if (ret) + goto err_reg; + + return 0; + + err_reg: + free_irq(dev->irq[0], rtc); + err_irq: + iounmap(rtc->base); + err_rtc: + amba_release_regions(dev); + err_req: + return ret; +} + +static void pl030_remove(struct amba_device *dev) +{ + struct pl030_rtc *rtc = amba_get_drvdata(dev); + + writel(0, rtc->base + RTC_CR); + + free_irq(dev->irq[0], rtc); + iounmap(rtc->base); + amba_release_regions(dev); +} + +static const struct amba_id pl030_ids[] = { + { + .id = 0x00041030, + .mask = 0x000fffff, + }, + { 0, 0 }, +}; + +MODULE_DEVICE_TABLE(amba, pl030_ids); + +static struct amba_driver pl030_driver = { + .drv = { + .name = "rtc-pl030", + }, + .probe = pl030_probe, + .remove = pl030_remove, + .id_table = pl030_ids, +}; + +module_amba_driver(pl030_driver); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("ARM AMBA PL030 RTC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c new file mode 100644 index 000000000..eab39dfa4 --- /dev/null +++ b/drivers/rtc/rtc-pl031.c @@ -0,0 +1,470 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * drivers/rtc/rtc-pl031.c + * + * Real Time Clock interface for ARM AMBA PrimeCell 031 RTC + * + * Author: Deepak Saxena + * + * Copyright 2006 (c) MontaVista Software, Inc. + * + * Author: Mian Yousaf Kaukab + * Copyright 2010 (c) ST-Ericsson AB + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Register definitions + */ +#define RTC_DR 0x00 /* Data read register */ +#define RTC_MR 0x04 /* Match register */ +#define RTC_LR 0x08 /* Data load register */ +#define RTC_CR 0x0c /* Control register */ +#define RTC_IMSC 0x10 /* Interrupt mask and set register */ +#define RTC_RIS 0x14 /* Raw interrupt status register */ +#define RTC_MIS 0x18 /* Masked interrupt status register */ +#define RTC_ICR 0x1c /* Interrupt clear register */ +/* ST variants have additional timer functionality */ +#define RTC_TDR 0x20 /* Timer data read register */ +#define RTC_TLR 0x24 /* Timer data load register */ +#define RTC_TCR 0x28 /* Timer control register */ +#define RTC_YDR 0x30 /* Year data read register */ +#define RTC_YMR 0x34 /* Year match register */ +#define RTC_YLR 0x38 /* Year data load register */ + +#define RTC_CR_EN (1 << 0) /* counter enable bit */ +#define RTC_CR_CWEN (1 << 26) /* Clockwatch enable bit */ + +#define RTC_TCR_EN (1 << 1) /* Periodic timer enable bit */ + +/* Common bit definitions for Interrupt status and control registers */ +#define RTC_BIT_AI (1 << 0) /* Alarm interrupt bit */ +#define RTC_BIT_PI (1 << 1) /* Periodic interrupt bit. ST variants only. */ + +/* Common bit definations for ST v2 for reading/writing time */ +#define RTC_SEC_SHIFT 0 +#define RTC_SEC_MASK (0x3F << RTC_SEC_SHIFT) /* Second [0-59] */ +#define RTC_MIN_SHIFT 6 +#define RTC_MIN_MASK (0x3F << RTC_MIN_SHIFT) /* Minute [0-59] */ +#define RTC_HOUR_SHIFT 12 +#define RTC_HOUR_MASK (0x1F << RTC_HOUR_SHIFT) /* Hour [0-23] */ +#define RTC_WDAY_SHIFT 17 +#define RTC_WDAY_MASK (0x7 << RTC_WDAY_SHIFT) /* Day of Week [1-7] 1=Sunday */ +#define RTC_MDAY_SHIFT 20 +#define RTC_MDAY_MASK (0x1F << RTC_MDAY_SHIFT) /* Day of Month [1-31] */ +#define RTC_MON_SHIFT 25 +#define RTC_MON_MASK (0xF << RTC_MON_SHIFT) /* Month [1-12] 1=January */ + +#define RTC_TIMER_FREQ 32768 + +/** + * struct pl031_vendor_data - per-vendor variations + * @ops: the vendor-specific operations used on this silicon version + * @clockwatch: if this is an ST Microelectronics silicon version with a + * clockwatch function + * @st_weekday: if this is an ST Microelectronics silicon version that need + * the weekday fix + * @irqflags: special IRQ flags per variant + * @range_min: minimum date/time supported by the RTC + * @range_max: maximum date/time supported by the RTC + */ +struct pl031_vendor_data { + struct rtc_class_ops ops; + bool clockwatch; + bool st_weekday; + unsigned long irqflags; + time64_t range_min; + timeu64_t range_max; +}; + +struct pl031_local { + struct pl031_vendor_data *vendor; + struct rtc_device *rtc; + void __iomem *base; +}; + +static int pl031_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct pl031_local *ldata = dev_get_drvdata(dev); + unsigned long imsc; + + /* Clear any pending alarm interrupts. */ + writel(RTC_BIT_AI, ldata->base + RTC_ICR); + + imsc = readl(ldata->base + RTC_IMSC); + + if (enabled == 1) + writel(imsc | RTC_BIT_AI, ldata->base + RTC_IMSC); + else + writel(imsc & ~RTC_BIT_AI, ldata->base + RTC_IMSC); + + return 0; +} + +/* + * Convert Gregorian date to ST v2 RTC format. + */ +static int pl031_stv2_tm_to_time(struct device *dev, + struct rtc_time *tm, unsigned long *st_time, + unsigned long *bcd_year) +{ + int year = tm->tm_year + 1900; + int wday = tm->tm_wday; + + /* wday masking is not working in hardware so wday must be valid */ + if (wday < -1 || wday > 6) { + dev_err(dev, "invalid wday value %d\n", tm->tm_wday); + return -EINVAL; + } else if (wday == -1) { + /* wday is not provided, calculate it here */ + struct rtc_time calc_tm; + + rtc_time64_to_tm(rtc_tm_to_time64(tm), &calc_tm); + wday = calc_tm.tm_wday; + } + + *bcd_year = (bin2bcd(year % 100) | bin2bcd(year / 100) << 8); + + *st_time = ((tm->tm_mon + 1) << RTC_MON_SHIFT) + | (tm->tm_mday << RTC_MDAY_SHIFT) + | ((wday + 1) << RTC_WDAY_SHIFT) + | (tm->tm_hour << RTC_HOUR_SHIFT) + | (tm->tm_min << RTC_MIN_SHIFT) + | (tm->tm_sec << RTC_SEC_SHIFT); + + return 0; +} + +/* + * Convert ST v2 RTC format to Gregorian date. + */ +static int pl031_stv2_time_to_tm(unsigned long st_time, unsigned long bcd_year, + struct rtc_time *tm) +{ + tm->tm_year = bcd2bin(bcd_year) + (bcd2bin(bcd_year >> 8) * 100); + tm->tm_mon = ((st_time & RTC_MON_MASK) >> RTC_MON_SHIFT) - 1; + tm->tm_mday = ((st_time & RTC_MDAY_MASK) >> RTC_MDAY_SHIFT); + tm->tm_wday = ((st_time & RTC_WDAY_MASK) >> RTC_WDAY_SHIFT) - 1; + tm->tm_hour = ((st_time & RTC_HOUR_MASK) >> RTC_HOUR_SHIFT); + tm->tm_min = ((st_time & RTC_MIN_MASK) >> RTC_MIN_SHIFT); + tm->tm_sec = ((st_time & RTC_SEC_MASK) >> RTC_SEC_SHIFT); + + tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year); + tm->tm_year -= 1900; + + return 0; +} + +static int pl031_stv2_read_time(struct device *dev, struct rtc_time *tm) +{ + struct pl031_local *ldata = dev_get_drvdata(dev); + + pl031_stv2_time_to_tm(readl(ldata->base + RTC_DR), + readl(ldata->base + RTC_YDR), tm); + + return 0; +} + +static int pl031_stv2_set_time(struct device *dev, struct rtc_time *tm) +{ + unsigned long time; + unsigned long bcd_year; + struct pl031_local *ldata = dev_get_drvdata(dev); + int ret; + + ret = pl031_stv2_tm_to_time(dev, tm, &time, &bcd_year); + if (ret == 0) { + writel(bcd_year, ldata->base + RTC_YLR); + writel(time, ldata->base + RTC_LR); + } + + return ret; +} + +static int pl031_stv2_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct pl031_local *ldata = dev_get_drvdata(dev); + int ret; + + ret = pl031_stv2_time_to_tm(readl(ldata->base + RTC_MR), + readl(ldata->base + RTC_YMR), &alarm->time); + + alarm->pending = readl(ldata->base + RTC_RIS) & RTC_BIT_AI; + alarm->enabled = readl(ldata->base + RTC_IMSC) & RTC_BIT_AI; + + return ret; +} + +static int pl031_stv2_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct pl031_local *ldata = dev_get_drvdata(dev); + unsigned long time; + unsigned long bcd_year; + int ret; + + ret = pl031_stv2_tm_to_time(dev, &alarm->time, + &time, &bcd_year); + if (ret == 0) { + writel(bcd_year, ldata->base + RTC_YMR); + writel(time, ldata->base + RTC_MR); + + pl031_alarm_irq_enable(dev, alarm->enabled); + } + + return ret; +} + +static irqreturn_t pl031_interrupt(int irq, void *dev_id) +{ + struct pl031_local *ldata = dev_id; + unsigned long rtcmis; + unsigned long events = 0; + + rtcmis = readl(ldata->base + RTC_MIS); + if (rtcmis & RTC_BIT_AI) { + writel(RTC_BIT_AI, ldata->base + RTC_ICR); + events |= (RTC_AF | RTC_IRQF); + rtc_update_irq(ldata->rtc, 1, events); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int pl031_read_time(struct device *dev, struct rtc_time *tm) +{ + struct pl031_local *ldata = dev_get_drvdata(dev); + + rtc_time64_to_tm(readl(ldata->base + RTC_DR), tm); + + return 0; +} + +static int pl031_set_time(struct device *dev, struct rtc_time *tm) +{ + struct pl031_local *ldata = dev_get_drvdata(dev); + + writel(rtc_tm_to_time64(tm), ldata->base + RTC_LR); + + return 0; +} + +static int pl031_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct pl031_local *ldata = dev_get_drvdata(dev); + + rtc_time64_to_tm(readl(ldata->base + RTC_MR), &alarm->time); + + alarm->pending = readl(ldata->base + RTC_RIS) & RTC_BIT_AI; + alarm->enabled = readl(ldata->base + RTC_IMSC) & RTC_BIT_AI; + + return 0; +} + +static int pl031_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct pl031_local *ldata = dev_get_drvdata(dev); + + writel(rtc_tm_to_time64(&alarm->time), ldata->base + RTC_MR); + pl031_alarm_irq_enable(dev, alarm->enabled); + + return 0; +} + +static void pl031_remove(struct amba_device *adev) +{ + struct pl031_local *ldata = dev_get_drvdata(&adev->dev); + + if (adev->irq[0]) + free_irq(adev->irq[0], ldata); + amba_release_regions(adev); +} + +static int pl031_probe(struct amba_device *adev, const struct amba_id *id) +{ + int ret; + struct pl031_local *ldata; + struct pl031_vendor_data *vendor = id->data; + struct rtc_class_ops *ops; + unsigned long time, data; + + ret = amba_request_regions(adev, NULL); + if (ret) + goto err_req; + + ldata = devm_kzalloc(&adev->dev, sizeof(struct pl031_local), + GFP_KERNEL); + ops = devm_kmemdup(&adev->dev, &vendor->ops, sizeof(vendor->ops), + GFP_KERNEL); + if (!ldata || !ops) { + ret = -ENOMEM; + goto out; + } + + ldata->vendor = vendor; + ldata->base = devm_ioremap(&adev->dev, adev->res.start, + resource_size(&adev->res)); + if (!ldata->base) { + ret = -ENOMEM; + goto out; + } + + amba_set_drvdata(adev, ldata); + + dev_dbg(&adev->dev, "designer ID = 0x%02x\n", amba_manf(adev)); + dev_dbg(&adev->dev, "revision = 0x%01x\n", amba_rev(adev)); + + data = readl(ldata->base + RTC_CR); + /* Enable the clockwatch on ST Variants */ + if (vendor->clockwatch) + data |= RTC_CR_CWEN; + else + data |= RTC_CR_EN; + writel(data, ldata->base + RTC_CR); + + /* + * On ST PL031 variants, the RTC reset value does not provide correct + * weekday for 2000-01-01. Correct the erroneous sunday to saturday. + */ + if (vendor->st_weekday) { + if (readl(ldata->base + RTC_YDR) == 0x2000) { + time = readl(ldata->base + RTC_DR); + if ((time & + (RTC_MON_MASK | RTC_MDAY_MASK | RTC_WDAY_MASK)) + == 0x02120000) { + time = time | (0x7 << RTC_WDAY_SHIFT); + writel(0x2000, ldata->base + RTC_YLR); + writel(time, ldata->base + RTC_LR); + } + } + } + + devm_device_init_wakeup(&adev->dev); + ldata->rtc = devm_rtc_allocate_device(&adev->dev); + if (IS_ERR(ldata->rtc)) { + ret = PTR_ERR(ldata->rtc); + goto out; + } + + if (!adev->irq[0]) + clear_bit(RTC_FEATURE_ALARM, ldata->rtc->features); + + ldata->rtc->ops = ops; + ldata->rtc->range_min = vendor->range_min; + ldata->rtc->range_max = vendor->range_max; + + ret = devm_rtc_register_device(ldata->rtc); + if (ret) + goto out; + + if (adev->irq[0]) { + ret = request_irq(adev->irq[0], pl031_interrupt, + vendor->irqflags, "rtc-pl031", ldata); + if (ret) + goto out; + devm_pm_set_wake_irq(&adev->dev, adev->irq[0]); + } + return 0; + +out: + amba_release_regions(adev); +err_req: + + return ret; +} + +/* Operations for the original ARM version */ +static struct pl031_vendor_data arm_pl031 = { + .ops = { + .read_time = pl031_read_time, + .set_time = pl031_set_time, + .read_alarm = pl031_read_alarm, + .set_alarm = pl031_set_alarm, + .alarm_irq_enable = pl031_alarm_irq_enable, + }, + .range_max = U32_MAX, +}; + +/* The First ST derivative */ +static struct pl031_vendor_data stv1_pl031 = { + .ops = { + .read_time = pl031_read_time, + .set_time = pl031_set_time, + .read_alarm = pl031_read_alarm, + .set_alarm = pl031_set_alarm, + .alarm_irq_enable = pl031_alarm_irq_enable, + }, + .clockwatch = true, + .st_weekday = true, + .range_max = U32_MAX, +}; + +/* And the second ST derivative */ +static struct pl031_vendor_data stv2_pl031 = { + .ops = { + .read_time = pl031_stv2_read_time, + .set_time = pl031_stv2_set_time, + .read_alarm = pl031_stv2_read_alarm, + .set_alarm = pl031_stv2_set_alarm, + .alarm_irq_enable = pl031_alarm_irq_enable, + }, + .clockwatch = true, + .st_weekday = true, + /* + * This variant shares the IRQ with another block and must not + * suspend that IRQ line. + * TODO check if it shares with IRQF_NO_SUSPEND user, else we can + * remove IRQF_COND_SUSPEND + */ + .irqflags = IRQF_SHARED | IRQF_COND_SUSPEND, + .range_min = RTC_TIMESTAMP_BEGIN_0000, + .range_max = RTC_TIMESTAMP_END_9999, +}; + +static const struct amba_id pl031_ids[] = { + { + .id = 0x00041031, + .mask = 0x000fffff, + .data = &arm_pl031, + }, + /* ST Micro variants */ + { + .id = 0x00180031, + .mask = 0x00ffffff, + .data = &stv1_pl031, + }, + { + .id = 0x00280031, + .mask = 0x00ffffff, + .data = &stv2_pl031, + }, + {0, 0}, +}; + +MODULE_DEVICE_TABLE(amba, pl031_ids); + +static struct amba_driver pl031_driver = { + .drv = { + .name = "rtc-pl031", + }, + .id_table = pl031_ids, + .probe = pl031_probe, + .remove = pl031_remove, +}; + +module_amba_driver(pl031_driver); + +MODULE_AUTHOR("Deepak Saxena "); +MODULE_DESCRIPTION("ARM AMBA PL031 RTC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-pm8xxx.c b/drivers/rtc/rtc-pm8xxx.c new file mode 100644 index 000000000..e624f848c --- /dev/null +++ b/drivers/rtc/rtc-pm8xxx.c @@ -0,0 +1,689 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * pm8xxx RTC driver + * + * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * Copyright (c) 2023, Linaro Limited + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* RTC_CTRL register bit fields */ +#define PM8xxx_RTC_ENABLE BIT(7) +#define PM8xxx_RTC_ALARM_CLEAR BIT(0) +#define PM8xxx_RTC_ALARM_ENABLE BIT(7) + +#define NUM_8_BIT_RTC_REGS 0x4 + +/** + * struct pm8xxx_rtc_regs - describe RTC registers per PMIC versions + * @ctrl: address of control register + * @write: base address of write registers + * @read: base address of read registers + * @alarm_ctrl: address of alarm control register + * @alarm_ctrl2: address of alarm control2 register + * @alarm_rw: base address of alarm read-write registers + * @alarm_en: alarm enable mask + */ +struct pm8xxx_rtc_regs { + unsigned int ctrl; + unsigned int write; + unsigned int read; + unsigned int alarm_ctrl; + unsigned int alarm_ctrl2; + unsigned int alarm_rw; + unsigned int alarm_en; +}; + +struct qcom_uefi_rtc_info { + __le32 offset_gps; + u8 reserved[8]; +} __packed; + +/** + * struct pm8xxx_rtc - RTC driver internal structure + * @rtc: RTC device + * @regmap: regmap used to access registers + * @allow_set_time: whether the time can be set + * @use_uefi: use UEFI variable as fallback for offset + * @alarm_irq: alarm irq number + * @regs: register description + * @dev: device structure + * @rtc_info: qcom uefi rtc-info structure + * @nvmem_cell: nvmem cell for offset + * @offset: offset from epoch in seconds + * @offset_dirty: offset needs to be stored on shutdown + */ +struct pm8xxx_rtc { + struct rtc_device *rtc; + struct regmap *regmap; + bool allow_set_time; + bool use_uefi; + int alarm_irq; + const struct pm8xxx_rtc_regs *regs; + struct device *dev; + struct qcom_uefi_rtc_info rtc_info; + struct nvmem_cell *nvmem_cell; + u32 offset; + bool offset_dirty; +}; + +#ifdef CONFIG_EFI + +MODULE_IMPORT_NS("EFIVAR"); + +#define QCOM_UEFI_NAME L"RTCInfo" +#define QCOM_UEFI_GUID EFI_GUID(0x882f8c2b, 0x9646, 0x435f, \ + 0x8d, 0xe5, 0xf2, 0x08, 0xff, 0x80, 0xc1, 0xbd) +#define QCOM_UEFI_ATTRS (EFI_VARIABLE_NON_VOLATILE | \ + EFI_VARIABLE_BOOTSERVICE_ACCESS | \ + EFI_VARIABLE_RUNTIME_ACCESS) + +static int pm8xxx_rtc_read_uefi_offset(struct pm8xxx_rtc *rtc_dd) +{ + struct qcom_uefi_rtc_info *rtc_info = &rtc_dd->rtc_info; + unsigned long size = sizeof(*rtc_info); + struct device *dev = rtc_dd->dev; + efi_status_t status; + u32 offset_gps; + int rc; + + rc = efivar_lock(); + if (rc) + return rc; + + status = efivar_get_variable(QCOM_UEFI_NAME, &QCOM_UEFI_GUID, NULL, + &size, rtc_info); + efivar_unlock(); + + if (status != EFI_SUCCESS) { + dev_dbg(dev, "failed to read UEFI offset: %lu\n", status); + return efi_status_to_err(status); + } + + if (size != sizeof(*rtc_info)) { + dev_dbg(dev, "unexpected UEFI structure size %lu\n", size); + return -EINVAL; + } + + dev_dbg(dev, "uefi_rtc_info = %*ph\n", (int)size, rtc_info); + + /* Convert from GPS to Unix time offset */ + offset_gps = le32_to_cpu(rtc_info->offset_gps); + rtc_dd->offset = offset_gps + (u32)RTC_TIMESTAMP_EPOCH_GPS; + + return 0; +} + +static int pm8xxx_rtc_write_uefi_offset(struct pm8xxx_rtc *rtc_dd, u32 offset) +{ + struct qcom_uefi_rtc_info *rtc_info = &rtc_dd->rtc_info; + unsigned long size = sizeof(*rtc_info); + struct device *dev = rtc_dd->dev; + efi_status_t status; + u32 offset_gps; + + /* Convert from Unix to GPS time offset */ + offset_gps = offset - (u32)RTC_TIMESTAMP_EPOCH_GPS; + + rtc_info->offset_gps = cpu_to_le32(offset_gps); + + dev_dbg(dev, "efi_rtc_info = %*ph\n", (int)size, rtc_info); + + status = efivar_set_variable(QCOM_UEFI_NAME, &QCOM_UEFI_GUID, + QCOM_UEFI_ATTRS, size, rtc_info); + if (status != EFI_SUCCESS) { + dev_dbg(dev, "failed to write UEFI offset: %lx\n", status); + return efi_status_to_err(status); + } + + return 0; +} + +#else /* CONFIG_EFI */ + +static int pm8xxx_rtc_read_uefi_offset(struct pm8xxx_rtc *rtc_dd) +{ + return -ENODEV; +} + +static int pm8xxx_rtc_write_uefi_offset(struct pm8xxx_rtc *rtc_dd, u32 offset) +{ + return -ENODEV; +} + +#endif /* CONFIG_EFI */ + +static int pm8xxx_rtc_read_nvmem_offset(struct pm8xxx_rtc *rtc_dd) +{ + size_t len; + void *buf; + int rc; + + buf = nvmem_cell_read(rtc_dd->nvmem_cell, &len); + if (IS_ERR(buf)) { + rc = PTR_ERR(buf); + dev_dbg(rtc_dd->dev, "failed to read nvmem offset: %d\n", rc); + return rc; + } + + if (len != sizeof(u32)) { + dev_dbg(rtc_dd->dev, "unexpected nvmem cell size %zu\n", len); + kfree(buf); + return -EINVAL; + } + + rtc_dd->offset = get_unaligned_le32(buf); + + kfree(buf); + + return 0; +} + +static int pm8xxx_rtc_write_nvmem_offset(struct pm8xxx_rtc *rtc_dd, u32 offset) +{ + u8 buf[sizeof(u32)]; + int rc; + + put_unaligned_le32(offset, buf); + + rc = nvmem_cell_write(rtc_dd->nvmem_cell, buf, sizeof(buf)); + if (rc < 0) { + dev_dbg(rtc_dd->dev, "failed to write nvmem offset: %d\n", rc); + return rc; + } + + return 0; +} + +static int pm8xxx_rtc_read_raw(struct pm8xxx_rtc *rtc_dd, u32 *secs) +{ + const struct pm8xxx_rtc_regs *regs = rtc_dd->regs; + u8 value[NUM_8_BIT_RTC_REGS]; + unsigned int reg; + int rc; + + rc = regmap_bulk_read(rtc_dd->regmap, regs->read, value, sizeof(value)); + if (rc) + return rc; + + /* + * Read the LSB again and check if there has been a carry over. + * If there has, redo the read operation. + */ + rc = regmap_read(rtc_dd->regmap, regs->read, ®); + if (rc < 0) + return rc; + + if (reg < value[0]) { + rc = regmap_bulk_read(rtc_dd->regmap, regs->read, value, + sizeof(value)); + if (rc) + return rc; + } + + *secs = get_unaligned_le32(value); + + return 0; +} + +static int pm8xxx_rtc_update_offset(struct pm8xxx_rtc *rtc_dd, u32 secs) +{ + u32 raw_secs; + u32 offset; + int rc; + + if (!rtc_dd->nvmem_cell && !rtc_dd->use_uefi) + return -ENODEV; + + rc = pm8xxx_rtc_read_raw(rtc_dd, &raw_secs); + if (rc) + return rc; + + offset = secs - raw_secs; + + if (offset == rtc_dd->offset) + return 0; + + /* + * Reduce flash wear by deferring updates due to clock drift until + * shutdown. + */ + if (abs_diff(offset, rtc_dd->offset) < 30) { + rtc_dd->offset_dirty = true; + goto out; + } + + if (rtc_dd->nvmem_cell) + rc = pm8xxx_rtc_write_nvmem_offset(rtc_dd, offset); + else + rc = pm8xxx_rtc_write_uefi_offset(rtc_dd, offset); + + if (rc) + return rc; + + rtc_dd->offset_dirty = false; +out: + rtc_dd->offset = offset; + + return 0; +} + +/* + * Steps to write the RTC registers. + * 1. Disable alarm if enabled. + * 2. Disable rtc if enabled. + * 3. Write 0x00 to LSB. + * 4. Write Byte[1], Byte[2], Byte[3] then Byte[0]. + * 5. Enable rtc if disabled in step 2. + * 6. Enable alarm if disabled in step 1. + */ +static int __pm8xxx_rtc_set_time(struct pm8xxx_rtc *rtc_dd, u32 secs) +{ + const struct pm8xxx_rtc_regs *regs = rtc_dd->regs; + u8 value[NUM_8_BIT_RTC_REGS]; + bool alarm_enabled; + int rc; + + put_unaligned_le32(secs, value); + + rc = regmap_update_bits_check(rtc_dd->regmap, regs->alarm_ctrl, + regs->alarm_en, 0, &alarm_enabled); + if (rc) + return rc; + + /* Disable RTC */ + rc = regmap_update_bits(rtc_dd->regmap, regs->ctrl, PM8xxx_RTC_ENABLE, 0); + if (rc) + return rc; + + /* Write 0 to Byte[0] */ + rc = regmap_write(rtc_dd->regmap, regs->write, 0); + if (rc) + return rc; + + /* Write Byte[1], Byte[2], Byte[3] */ + rc = regmap_bulk_write(rtc_dd->regmap, regs->write + 1, + &value[1], sizeof(value) - 1); + if (rc) + return rc; + + /* Write Byte[0] */ + rc = regmap_write(rtc_dd->regmap, regs->write, value[0]); + if (rc) + return rc; + + /* Enable RTC */ + rc = regmap_update_bits(rtc_dd->regmap, regs->ctrl, PM8xxx_RTC_ENABLE, + PM8xxx_RTC_ENABLE); + if (rc) + return rc; + + if (alarm_enabled) { + rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl, + regs->alarm_en, regs->alarm_en); + if (rc) + return rc; + } + + return 0; +} + +static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); + u32 secs; + int rc; + + secs = rtc_tm_to_time64(tm); + + if (rtc_dd->allow_set_time) + rc = __pm8xxx_rtc_set_time(rtc_dd, secs); + else + rc = pm8xxx_rtc_update_offset(rtc_dd, secs); + + if (rc) + return rc; + + dev_dbg(dev, "set time: %ptRd %ptRt (%u + %u)\n", tm, tm, + secs - rtc_dd->offset, rtc_dd->offset); + return 0; +} + +static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); + u32 secs; + int rc; + + rc = pm8xxx_rtc_read_raw(rtc_dd, &secs); + if (rc) + return rc; + + secs += rtc_dd->offset; + rtc_time64_to_tm(secs, tm); + + dev_dbg(dev, "read time: %ptRd %ptRt (%u + %u)\n", tm, tm, + secs - rtc_dd->offset, rtc_dd->offset); + return 0; +} + +static int pm8xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); + const struct pm8xxx_rtc_regs *regs = rtc_dd->regs; + u8 value[NUM_8_BIT_RTC_REGS]; + u32 secs; + int rc; + + secs = rtc_tm_to_time64(&alarm->time); + secs -= rtc_dd->offset; + put_unaligned_le32(secs, value); + + rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl, + regs->alarm_en, 0); + if (rc) + return rc; + + rc = regmap_bulk_write(rtc_dd->regmap, regs->alarm_rw, value, + sizeof(value)); + if (rc) + return rc; + + if (alarm->enabled) { + rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl, + regs->alarm_en, regs->alarm_en); + if (rc) + return rc; + } + + dev_dbg(dev, "set alarm: %ptRd %ptRt\n", &alarm->time, &alarm->time); + + return 0; +} + +static int pm8xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); + const struct pm8xxx_rtc_regs *regs = rtc_dd->regs; + u8 value[NUM_8_BIT_RTC_REGS]; + unsigned int ctrl_reg; + u32 secs; + int rc; + + rc = regmap_bulk_read(rtc_dd->regmap, regs->alarm_rw, value, + sizeof(value)); + if (rc) + return rc; + + secs = get_unaligned_le32(value); + secs += rtc_dd->offset; + rtc_time64_to_tm(secs, &alarm->time); + + rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl, &ctrl_reg); + if (rc) + return rc; + + alarm->enabled = !!(ctrl_reg & PM8xxx_RTC_ALARM_ENABLE); + + dev_dbg(dev, "read alarm: %ptRd %ptRt\n", &alarm->time, &alarm->time); + + return 0; +} + +static int pm8xxx_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) +{ + struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev); + const struct pm8xxx_rtc_regs *regs = rtc_dd->regs; + u8 value[NUM_8_BIT_RTC_REGS] = {0}; + unsigned int val; + int rc; + + if (enable) + val = regs->alarm_en; + else + val = 0; + + rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl, + regs->alarm_en, val); + if (rc) + return rc; + + /* Clear alarm register */ + if (!enable) { + rc = regmap_bulk_write(rtc_dd->regmap, regs->alarm_rw, value, + sizeof(value)); + if (rc) + return rc; + } + + return 0; +} + +static const struct rtc_class_ops pm8xxx_rtc_ops = { + .read_time = pm8xxx_rtc_read_time, + .set_time = pm8xxx_rtc_set_time, + .set_alarm = pm8xxx_rtc_set_alarm, + .read_alarm = pm8xxx_rtc_read_alarm, + .alarm_irq_enable = pm8xxx_rtc_alarm_irq_enable, +}; + +static irqreturn_t pm8xxx_alarm_trigger(int irq, void *dev_id) +{ + struct pm8xxx_rtc *rtc_dd = dev_id; + const struct pm8xxx_rtc_regs *regs = rtc_dd->regs; + int rc; + + rtc_update_irq(rtc_dd->rtc, 1, RTC_IRQF | RTC_AF); + + /* Disable alarm */ + rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl, + regs->alarm_en, 0); + if (rc) + return IRQ_NONE; + + /* Clear alarm status */ + rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl2, + PM8xxx_RTC_ALARM_CLEAR, 0); + if (rc) + return IRQ_NONE; + + return IRQ_HANDLED; +} + +static int pm8xxx_rtc_enable(struct pm8xxx_rtc *rtc_dd) +{ + const struct pm8xxx_rtc_regs *regs = rtc_dd->regs; + + return regmap_update_bits(rtc_dd->regmap, regs->ctrl, PM8xxx_RTC_ENABLE, + PM8xxx_RTC_ENABLE); +} + +static const struct pm8xxx_rtc_regs pm8921_regs = { + .ctrl = 0x11d, + .write = 0x11f, + .read = 0x123, + .alarm_rw = 0x127, + .alarm_ctrl = 0x11d, + .alarm_ctrl2 = 0x11e, + .alarm_en = BIT(1), +}; + +static const struct pm8xxx_rtc_regs pm8058_regs = { + .ctrl = 0x1e8, + .write = 0x1ea, + .read = 0x1ee, + .alarm_rw = 0x1f2, + .alarm_ctrl = 0x1e8, + .alarm_ctrl2 = 0x1e9, + .alarm_en = BIT(1), +}; + +static const struct pm8xxx_rtc_regs pm8941_regs = { + .ctrl = 0x6046, + .write = 0x6040, + .read = 0x6048, + .alarm_rw = 0x6140, + .alarm_ctrl = 0x6146, + .alarm_ctrl2 = 0x6148, + .alarm_en = BIT(7), +}; + +static const struct pm8xxx_rtc_regs pmk8350_regs = { + .ctrl = 0x6146, + .write = 0x6140, + .read = 0x6148, + .alarm_rw = 0x6240, + .alarm_ctrl = 0x6246, + .alarm_ctrl2 = 0x6248, + .alarm_en = BIT(7), +}; + +static const struct of_device_id pm8xxx_id_table[] = { + { .compatible = "qcom,pm8921-rtc", .data = &pm8921_regs }, + { .compatible = "qcom,pm8058-rtc", .data = &pm8058_regs }, + { .compatible = "qcom,pm8941-rtc", .data = &pm8941_regs }, + { .compatible = "qcom,pmk8350-rtc", .data = &pmk8350_regs }, + { }, +}; +MODULE_DEVICE_TABLE(of, pm8xxx_id_table); + +static int pm8xxx_rtc_probe_offset(struct pm8xxx_rtc *rtc_dd) +{ + int rc; + + rtc_dd->nvmem_cell = devm_nvmem_cell_get(rtc_dd->dev, "offset"); + if (IS_ERR(rtc_dd->nvmem_cell)) { + rc = PTR_ERR(rtc_dd->nvmem_cell); + if (rc != -ENOENT) + return rc; + rtc_dd->nvmem_cell = NULL; + } else { + return pm8xxx_rtc_read_nvmem_offset(rtc_dd); + } + + /* Use UEFI storage as fallback if available */ + rtc_dd->use_uefi = of_property_read_bool(rtc_dd->dev->of_node, + "qcom,uefi-rtc-info"); + if (!rtc_dd->use_uefi) + return 0; + + if (!efivar_is_available()) { + if (IS_ENABLED(CONFIG_EFI)) + return -EPROBE_DEFER; + + dev_warn(rtc_dd->dev, "efivars not available\n"); + rtc_dd->use_uefi = false; + } + + return pm8xxx_rtc_read_uefi_offset(rtc_dd); +} + +static int pm8xxx_rtc_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + struct pm8xxx_rtc *rtc_dd; + int rc; + + match = of_match_node(pm8xxx_id_table, pdev->dev.of_node); + if (!match) + return -ENXIO; + + rtc_dd = devm_kzalloc(&pdev->dev, sizeof(*rtc_dd), GFP_KERNEL); + if (rtc_dd == NULL) + return -ENOMEM; + + rtc_dd->regs = match->data; + rtc_dd->dev = &pdev->dev; + + rtc_dd->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!rtc_dd->regmap) + return -ENXIO; + + if (!of_property_read_bool(pdev->dev.of_node, "qcom,no-alarm")) { + rtc_dd->alarm_irq = platform_get_irq(pdev, 0); + if (rtc_dd->alarm_irq < 0) + return -ENXIO; + } + + rtc_dd->allow_set_time = of_property_read_bool(pdev->dev.of_node, + "allow-set-time"); + if (!rtc_dd->allow_set_time) { + rc = pm8xxx_rtc_probe_offset(rtc_dd); + if (rc) + return rc; + } + + rc = pm8xxx_rtc_enable(rtc_dd); + if (rc) + return rc; + + platform_set_drvdata(pdev, rtc_dd); + + rtc_dd->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc_dd->rtc)) + return PTR_ERR(rtc_dd->rtc); + + rtc_dd->rtc->ops = &pm8xxx_rtc_ops; + rtc_dd->rtc->range_max = U32_MAX; + + if (rtc_dd->alarm_irq) { + rc = devm_request_any_context_irq(&pdev->dev, rtc_dd->alarm_irq, + pm8xxx_alarm_trigger, + IRQF_TRIGGER_RISING, + "pm8xxx_rtc_alarm", rtc_dd); + if (rc < 0) + return rc; + + rc = devm_pm_set_wake_irq(&pdev->dev, rtc_dd->alarm_irq); + if (rc) + return rc; + + devm_device_init_wakeup(&pdev->dev); + } else { + clear_bit(RTC_FEATURE_ALARM, rtc_dd->rtc->features); + } + + return devm_rtc_register_device(rtc_dd->rtc); +} + +static void pm8xxx_shutdown(struct platform_device *pdev) +{ + struct pm8xxx_rtc *rtc_dd = platform_get_drvdata(pdev); + + if (rtc_dd->offset_dirty) { + if (rtc_dd->nvmem_cell) + pm8xxx_rtc_write_nvmem_offset(rtc_dd, rtc_dd->offset); + else + pm8xxx_rtc_write_uefi_offset(rtc_dd, rtc_dd->offset); + } +} + +static struct platform_driver pm8xxx_rtc_driver = { + .probe = pm8xxx_rtc_probe, + .shutdown = pm8xxx_shutdown, + .driver = { + .name = "rtc-pm8xxx", + .of_match_table = pm8xxx_id_table, + }, +}; + +module_platform_driver(pm8xxx_rtc_driver); + +MODULE_DESCRIPTION("PMIC8xxx RTC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Anirudh Ghayal "); +MODULE_AUTHOR("Johan Hovold "); diff --git a/drivers/rtc/rtc-ps3.c b/drivers/rtc/rtc-ps3.c new file mode 100644 index 000000000..6b098734c --- /dev/null +++ b/drivers/rtc/rtc-ps3.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PS3 RTC Driver + * + * Copyright 2009 Sony Corporation + */ + +#include +#include +#include +#include + +#include +#include + + +static u64 read_rtc(void) +{ + int result; + u64 rtc_val; + u64 tb_val; + + result = lv1_get_rtc(&rtc_val, &tb_val); + BUG_ON(result); + + return rtc_val; +} + +static int ps3_get_time(struct device *dev, struct rtc_time *tm) +{ + rtc_time64_to_tm(read_rtc() + ps3_os_area_get_rtc_diff(), tm); + return 0; +} + +static int ps3_set_time(struct device *dev, struct rtc_time *tm) +{ + ps3_os_area_set_rtc_diff(rtc_tm_to_time64(tm) - read_rtc()); + return 0; +} + +static const struct rtc_class_ops ps3_rtc_ops = { + .read_time = ps3_get_time, + .set_time = ps3_set_time, +}; + +static int __init ps3_rtc_probe(struct platform_device *dev) +{ + struct rtc_device *rtc; + + rtc = devm_rtc_allocate_device(&dev->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + rtc->ops = &ps3_rtc_ops; + rtc->range_max = U64_MAX; + + platform_set_drvdata(dev, rtc); + + return devm_rtc_register_device(rtc); +} + +static struct platform_driver ps3_rtc_driver = { + .driver = { + .name = "rtc-ps3", + }, +}; + +module_platform_driver_probe(ps3_rtc_driver, ps3_rtc_probe); + +MODULE_AUTHOR("Sony Corporation"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ps3 RTC driver"); +MODULE_ALIAS("platform:rtc-ps3"); diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c new file mode 100644 index 000000000..62ee6b8f9 --- /dev/null +++ b/drivers/rtc/rtc-pxa.c @@ -0,0 +1,425 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Real Time Clock interface for XScale PXA27x and PXA3xx + * + * Copyright (C) 2008 Robert Jarzmik + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rtc-sa1100.h" + +#define RTC_DEF_DIVIDER (32768 - 1) +#define RTC_DEF_TRIM 0 +#define MAXFREQ_PERIODIC 1000 + +/* + * PXA Registers and bits definitions + */ +#define RTSR_PICE (1 << 15) /* Periodic interrupt count enable */ +#define RTSR_PIALE (1 << 14) /* Periodic interrupt Alarm enable */ +#define RTSR_PIAL (1 << 13) /* Periodic interrupt detected */ +#define RTSR_SWALE2 (1 << 11) /* RTC stopwatch alarm2 enable */ +#define RTSR_SWAL2 (1 << 10) /* RTC stopwatch alarm2 detected */ +#define RTSR_SWALE1 (1 << 9) /* RTC stopwatch alarm1 enable */ +#define RTSR_SWAL1 (1 << 8) /* RTC stopwatch alarm1 detected */ +#define RTSR_RDALE2 (1 << 7) /* RTC alarm2 enable */ +#define RTSR_RDAL2 (1 << 6) /* RTC alarm2 detected */ +#define RTSR_RDALE1 (1 << 5) /* RTC alarm1 enable */ +#define RTSR_RDAL1 (1 << 4) /* RTC alarm1 detected */ +#define RTSR_HZE (1 << 3) /* HZ interrupt enable */ +#define RTSR_ALE (1 << 2) /* RTC alarm interrupt enable */ +#define RTSR_HZ (1 << 1) /* HZ rising-edge detected */ +#define RTSR_AL (1 << 0) /* RTC alarm detected */ +#define RTSR_TRIG_MASK (RTSR_AL | RTSR_HZ | RTSR_RDAL1 | RTSR_RDAL2\ + | RTSR_SWAL1 | RTSR_SWAL2) +#define RYxR_YEAR_S 9 +#define RYxR_YEAR_MASK (0xfff << RYxR_YEAR_S) +#define RYxR_MONTH_S 5 +#define RYxR_MONTH_MASK (0xf << RYxR_MONTH_S) +#define RYxR_DAY_MASK 0x1f +#define RDxR_WOM_S 20 +#define RDxR_WOM_MASK (0x7 << RDxR_WOM_S) +#define RDxR_DOW_S 17 +#define RDxR_DOW_MASK (0x7 << RDxR_DOW_S) +#define RDxR_HOUR_S 12 +#define RDxR_HOUR_MASK (0x1f << RDxR_HOUR_S) +#define RDxR_MIN_S 6 +#define RDxR_MIN_MASK (0x3f << RDxR_MIN_S) +#define RDxR_SEC_MASK 0x3f + +#define RTSR 0x08 +#define RTTR 0x0c +#define RDCR 0x10 +#define RYCR 0x14 +#define RDAR1 0x18 +#define RYAR1 0x1c +#define RTCPICR 0x34 +#define PIAR 0x38 + +#define rtc_readl(pxa_rtc, reg) \ + __raw_readl((pxa_rtc)->base + (reg)) +#define rtc_writel(pxa_rtc, reg, value) \ + __raw_writel((value), (pxa_rtc)->base + (reg)) + +struct pxa_rtc { + struct sa1100_rtc sa1100_rtc; + struct resource *ress; + void __iomem *base; + struct rtc_device *rtc; + spinlock_t lock; /* Protects this structure */ +}; + + +static u32 ryxr_calc(struct rtc_time *tm) +{ + return ((tm->tm_year + 1900) << RYxR_YEAR_S) + | ((tm->tm_mon + 1) << RYxR_MONTH_S) + | tm->tm_mday; +} + +static u32 rdxr_calc(struct rtc_time *tm) +{ + return ((((tm->tm_mday + 6) / 7) << RDxR_WOM_S) & RDxR_WOM_MASK) + | (((tm->tm_wday + 1) << RDxR_DOW_S) & RDxR_DOW_MASK) + | (tm->tm_hour << RDxR_HOUR_S) + | (tm->tm_min << RDxR_MIN_S) + | tm->tm_sec; +} + +static void tm_calc(u32 rycr, u32 rdcr, struct rtc_time *tm) +{ + tm->tm_year = ((rycr & RYxR_YEAR_MASK) >> RYxR_YEAR_S) - 1900; + tm->tm_mon = (((rycr & RYxR_MONTH_MASK) >> RYxR_MONTH_S)) - 1; + tm->tm_mday = (rycr & RYxR_DAY_MASK); + tm->tm_wday = ((rycr & RDxR_DOW_MASK) >> RDxR_DOW_S) - 1; + tm->tm_hour = (rdcr & RDxR_HOUR_MASK) >> RDxR_HOUR_S; + tm->tm_min = (rdcr & RDxR_MIN_MASK) >> RDxR_MIN_S; + tm->tm_sec = rdcr & RDxR_SEC_MASK; +} + +static void rtsr_clear_bits(struct pxa_rtc *pxa_rtc, u32 mask) +{ + u32 rtsr; + + rtsr = rtc_readl(pxa_rtc, RTSR); + rtsr &= ~RTSR_TRIG_MASK; + rtsr &= ~mask; + rtc_writel(pxa_rtc, RTSR, rtsr); +} + +static void rtsr_set_bits(struct pxa_rtc *pxa_rtc, u32 mask) +{ + u32 rtsr; + + rtsr = rtc_readl(pxa_rtc, RTSR); + rtsr &= ~RTSR_TRIG_MASK; + rtsr |= mask; + rtc_writel(pxa_rtc, RTSR, rtsr); +} + +static irqreturn_t pxa_rtc_irq(int irq, void *dev_id) +{ + struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev_id); + u32 rtsr; + unsigned long events = 0; + + spin_lock(&pxa_rtc->lock); + + /* clear interrupt sources */ + rtsr = rtc_readl(pxa_rtc, RTSR); + rtc_writel(pxa_rtc, RTSR, rtsr); + + /* temporary disable rtc interrupts */ + rtsr_clear_bits(pxa_rtc, RTSR_RDALE1 | RTSR_PIALE | RTSR_HZE); + + /* clear alarm interrupt if it has occurred */ + if (rtsr & RTSR_RDAL1) + rtsr &= ~RTSR_RDALE1; + + /* update irq data & counter */ + if (rtsr & RTSR_RDAL1) + events |= RTC_AF | RTC_IRQF; + if (rtsr & RTSR_HZ) + events |= RTC_UF | RTC_IRQF; + if (rtsr & RTSR_PIAL) + events |= RTC_PF | RTC_IRQF; + + rtc_update_irq(pxa_rtc->rtc, 1, events); + + /* enable back rtc interrupts */ + rtc_writel(pxa_rtc, RTSR, rtsr & ~RTSR_TRIG_MASK); + + spin_unlock(&pxa_rtc->lock); + return IRQ_HANDLED; +} + +static int pxa_rtc_open(struct device *dev) +{ + struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev); + int ret; + + ret = request_irq(pxa_rtc->sa1100_rtc.irq_1hz, pxa_rtc_irq, 0, + "rtc 1Hz", dev); + if (ret < 0) { + dev_err(dev, "can't get irq %i, err %d\n", + pxa_rtc->sa1100_rtc.irq_1hz, ret); + goto err_irq_1Hz; + } + ret = request_irq(pxa_rtc->sa1100_rtc.irq_alarm, pxa_rtc_irq, 0, + "rtc Alrm", dev); + if (ret < 0) { + dev_err(dev, "can't get irq %i, err %d\n", + pxa_rtc->sa1100_rtc.irq_alarm, ret); + goto err_irq_Alrm; + } + + return 0; + +err_irq_Alrm: + free_irq(pxa_rtc->sa1100_rtc.irq_1hz, dev); +err_irq_1Hz: + return ret; +} + +static void pxa_rtc_release(struct device *dev) +{ + struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev); + + spin_lock_irq(&pxa_rtc->lock); + rtsr_clear_bits(pxa_rtc, RTSR_PIALE | RTSR_RDALE1 | RTSR_HZE); + spin_unlock_irq(&pxa_rtc->lock); + + free_irq(pxa_rtc->sa1100_rtc.irq_1hz, dev); + free_irq(pxa_rtc->sa1100_rtc.irq_alarm, dev); +} + +static int pxa_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev); + + spin_lock_irq(&pxa_rtc->lock); + + if (enabled) + rtsr_set_bits(pxa_rtc, RTSR_RDALE1); + else + rtsr_clear_bits(pxa_rtc, RTSR_RDALE1); + + spin_unlock_irq(&pxa_rtc->lock); + return 0; +} + +static int pxa_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev); + u32 rycr, rdcr; + + rycr = rtc_readl(pxa_rtc, RYCR); + rdcr = rtc_readl(pxa_rtc, RDCR); + + tm_calc(rycr, rdcr, tm); + return 0; +} + +static int pxa_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev); + + rtc_writel(pxa_rtc, RYCR, ryxr_calc(tm)); + rtc_writel(pxa_rtc, RDCR, rdxr_calc(tm)); + + return 0; +} + +static int pxa_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev); + u32 rtsr, ryar, rdar; + + ryar = rtc_readl(pxa_rtc, RYAR1); + rdar = rtc_readl(pxa_rtc, RDAR1); + tm_calc(ryar, rdar, &alrm->time); + + rtsr = rtc_readl(pxa_rtc, RTSR); + alrm->enabled = (rtsr & RTSR_RDALE1) ? 1 : 0; + alrm->pending = (rtsr & RTSR_RDAL1) ? 1 : 0; + return 0; +} + +static int pxa_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev); + u32 rtsr; + + spin_lock_irq(&pxa_rtc->lock); + + rtc_writel(pxa_rtc, RYAR1, ryxr_calc(&alrm->time)); + rtc_writel(pxa_rtc, RDAR1, rdxr_calc(&alrm->time)); + + rtsr = rtc_readl(pxa_rtc, RTSR); + if (alrm->enabled) + rtsr |= RTSR_RDALE1; + else + rtsr &= ~RTSR_RDALE1; + rtc_writel(pxa_rtc, RTSR, rtsr); + + spin_unlock_irq(&pxa_rtc->lock); + + return 0; +} + +static int pxa_rtc_proc(struct device *dev, struct seq_file *seq) +{ + struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev); + + seq_printf(seq, "trim/divider\t: 0x%08x\n", rtc_readl(pxa_rtc, RTTR)); + seq_printf(seq, "update_IRQ\t: %s\n", + (rtc_readl(pxa_rtc, RTSR) & RTSR_HZE) ? "yes" : "no"); + seq_printf(seq, "periodic_IRQ\t: %s\n", + (rtc_readl(pxa_rtc, RTSR) & RTSR_PIALE) ? "yes" : "no"); + seq_printf(seq, "periodic_freq\t: %u\n", rtc_readl(pxa_rtc, PIAR)); + + return 0; +} + +static const struct rtc_class_ops pxa_rtc_ops = { + .read_time = pxa_rtc_read_time, + .set_time = pxa_rtc_set_time, + .read_alarm = pxa_rtc_read_alarm, + .set_alarm = pxa_rtc_set_alarm, + .alarm_irq_enable = pxa_alarm_irq_enable, + .proc = pxa_rtc_proc, +}; + +static int __init pxa_rtc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pxa_rtc *pxa_rtc; + struct sa1100_rtc *sa1100_rtc; + int ret; + + pxa_rtc = devm_kzalloc(dev, sizeof(*pxa_rtc), GFP_KERNEL); + if (!pxa_rtc) + return -ENOMEM; + sa1100_rtc = &pxa_rtc->sa1100_rtc; + + spin_lock_init(&pxa_rtc->lock); + platform_set_drvdata(pdev, pxa_rtc); + + pxa_rtc->ress = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!pxa_rtc->ress) { + dev_err(dev, "No I/O memory resource defined\n"); + return -ENXIO; + } + + sa1100_rtc->irq_1hz = platform_get_irq(pdev, 0); + if (sa1100_rtc->irq_1hz < 0) + return -ENXIO; + sa1100_rtc->irq_alarm = platform_get_irq(pdev, 1); + if (sa1100_rtc->irq_alarm < 0) + return -ENXIO; + + sa1100_rtc->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(sa1100_rtc->rtc)) + return PTR_ERR(sa1100_rtc->rtc); + + pxa_rtc->base = devm_ioremap(dev, pxa_rtc->ress->start, + resource_size(pxa_rtc->ress)); + if (!pxa_rtc->base) { + dev_err(dev, "Unable to map pxa RTC I/O memory\n"); + return -ENOMEM; + } + + pxa_rtc_open(dev); + + sa1100_rtc->rcnr = pxa_rtc->base + 0x0; + sa1100_rtc->rtsr = pxa_rtc->base + 0x8; + sa1100_rtc->rtar = pxa_rtc->base + 0x4; + sa1100_rtc->rttr = pxa_rtc->base + 0xc; + ret = sa1100_rtc_init(pdev, sa1100_rtc); + if (ret) { + dev_err(dev, "Unable to init SA1100 RTC sub-device\n"); + return ret; + } + + rtsr_clear_bits(pxa_rtc, RTSR_PIALE | RTSR_RDALE1 | RTSR_HZE); + + pxa_rtc->rtc = devm_rtc_device_register(&pdev->dev, "pxa-rtc", + &pxa_rtc_ops, THIS_MODULE); + if (IS_ERR(pxa_rtc->rtc)) { + ret = PTR_ERR(pxa_rtc->rtc); + dev_err(dev, "Failed to register RTC device -> %d\n", ret); + return ret; + } + + device_init_wakeup(dev, true); + + return 0; +} + +static void __exit pxa_rtc_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + pxa_rtc_release(dev); +} + +#ifdef CONFIG_OF +static const struct of_device_id pxa_rtc_dt_ids[] = { + { .compatible = "marvell,pxa-rtc" }, + {} +}; +MODULE_DEVICE_TABLE(of, pxa_rtc_dt_ids); +#endif + +#ifdef CONFIG_PM_SLEEP +static int pxa_rtc_suspend(struct device *dev) +{ + struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(pxa_rtc->sa1100_rtc.irq_alarm); + return 0; +} + +static int pxa_rtc_resume(struct device *dev) +{ + struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(pxa_rtc->sa1100_rtc.irq_alarm); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(pxa_rtc_pm_ops, pxa_rtc_suspend, pxa_rtc_resume); + +/* + * pxa_rtc_remove() lives in .exit.text. For drivers registered via + * module_platform_driver_probe() this is ok because they cannot get unbound at + * runtime. So mark the driver struct with __refdata to prevent modpost + * triggering a section mismatch warning. + */ +static struct platform_driver pxa_rtc_driver __refdata = { + .remove = __exit_p(pxa_rtc_remove), + .driver = { + .name = "pxa-rtc", + .of_match_table = of_match_ptr(pxa_rtc_dt_ids), + .pm = &pxa_rtc_pm_ops, + }, +}; + +module_platform_driver_probe(pxa_rtc_driver, pxa_rtc_probe); + +MODULE_AUTHOR("Robert Jarzmik "); +MODULE_DESCRIPTION("PXA27x/PXA3xx Realtime Clock Driver (RTC)"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pxa-rtc"); diff --git a/drivers/rtc/rtc-r7301.c b/drivers/rtc/rtc-r7301.c new file mode 100644 index 000000000..6323b7771 --- /dev/null +++ b/drivers/rtc/rtc-r7301.c @@ -0,0 +1,479 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * EPSON TOYOCOM RTC-7301SF/DG Driver + * + * Copyright (c) 2016 Akinobu Mita + * + * Based on rtc-rp5c01.c + * + * Datasheet: http://www5.epsondevice.com/en/products/parallel/rtc7301sf.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "rtc-r7301" + +#define RTC7301_1_SEC 0x0 /* Bank 0 and Band 1 */ +#define RTC7301_10_SEC 0x1 /* Bank 0 and Band 1 */ +#define RTC7301_AE BIT(3) +#define RTC7301_1_MIN 0x2 /* Bank 0 and Band 1 */ +#define RTC7301_10_MIN 0x3 /* Bank 0 and Band 1 */ +#define RTC7301_1_HOUR 0x4 /* Bank 0 and Band 1 */ +#define RTC7301_10_HOUR 0x5 /* Bank 0 and Band 1 */ +#define RTC7301_DAY_OF_WEEK 0x6 /* Bank 0 and Band 1 */ +#define RTC7301_1_DAY 0x7 /* Bank 0 and Band 1 */ +#define RTC7301_10_DAY 0x8 /* Bank 0 and Band 1 */ +#define RTC7301_1_MONTH 0x9 /* Bank 0 */ +#define RTC7301_10_MONTH 0xa /* Bank 0 */ +#define RTC7301_1_YEAR 0xb /* Bank 0 */ +#define RTC7301_10_YEAR 0xc /* Bank 0 */ +#define RTC7301_100_YEAR 0xd /* Bank 0 */ +#define RTC7301_1000_YEAR 0xe /* Bank 0 */ +#define RTC7301_ALARM_CONTROL 0xe /* Bank 1 */ +#define RTC7301_ALARM_CONTROL_AIE BIT(0) +#define RTC7301_ALARM_CONTROL_AF BIT(1) +#define RTC7301_TIMER_CONTROL 0xe /* Bank 2 */ +#define RTC7301_TIMER_CONTROL_TIE BIT(0) +#define RTC7301_TIMER_CONTROL_TF BIT(1) +#define RTC7301_CONTROL 0xf /* All banks */ +#define RTC7301_CONTROL_BUSY BIT(0) +#define RTC7301_CONTROL_STOP BIT(1) +#define RTC7301_CONTROL_BANK_SEL_0 BIT(2) +#define RTC7301_CONTROL_BANK_SEL_1 BIT(3) + +struct rtc7301_priv { + struct regmap *regmap; + int irq; + spinlock_t lock; + u8 bank; +}; + +/* + * When the device is memory-mapped, some platforms pack the registers into + * 32-bit access using the lower 8 bits at each 4-byte stride, while others + * expose them as simply consecutive bytes. + */ +static const struct regmap_config rtc7301_regmap_32_config = { + .reg_bits = 32, + .val_bits = 8, + .reg_stride = 4, +}; + +static const struct regmap_config rtc7301_regmap_8_config = { + .reg_bits = 8, + .val_bits = 8, + .reg_stride = 1, +}; + +static u8 rtc7301_read(struct rtc7301_priv *priv, unsigned int reg) +{ + int reg_stride = regmap_get_reg_stride(priv->regmap); + unsigned int val; + + regmap_read(priv->regmap, reg_stride * reg, &val); + + return val & 0xf; +} + +static void rtc7301_write(struct rtc7301_priv *priv, u8 val, unsigned int reg) +{ + int reg_stride = regmap_get_reg_stride(priv->regmap); + + regmap_write(priv->regmap, reg_stride * reg, val); +} + +static void rtc7301_update_bits(struct rtc7301_priv *priv, unsigned int reg, + u8 mask, u8 val) +{ + int reg_stride = regmap_get_reg_stride(priv->regmap); + + regmap_update_bits(priv->regmap, reg_stride * reg, mask, val); +} + +static int rtc7301_wait_while_busy(struct rtc7301_priv *priv) +{ + int retries = 100; + + while (retries-- > 0) { + u8 val; + + val = rtc7301_read(priv, RTC7301_CONTROL); + if (!(val & RTC7301_CONTROL_BUSY)) + return 0; + + udelay(300); + } + + return -ETIMEDOUT; +} + +static void rtc7301_stop(struct rtc7301_priv *priv) +{ + rtc7301_update_bits(priv, RTC7301_CONTROL, RTC7301_CONTROL_STOP, + RTC7301_CONTROL_STOP); +} + +static void rtc7301_start(struct rtc7301_priv *priv) +{ + rtc7301_update_bits(priv, RTC7301_CONTROL, RTC7301_CONTROL_STOP, 0); +} + +static void rtc7301_select_bank(struct rtc7301_priv *priv, u8 bank) +{ + u8 val = 0; + + if (bank == priv->bank) + return; + + if (bank & BIT(0)) + val |= RTC7301_CONTROL_BANK_SEL_0; + if (bank & BIT(1)) + val |= RTC7301_CONTROL_BANK_SEL_1; + + rtc7301_update_bits(priv, RTC7301_CONTROL, + RTC7301_CONTROL_BANK_SEL_0 | + RTC7301_CONTROL_BANK_SEL_1, val); + + priv->bank = bank; +} + +static void rtc7301_get_time(struct rtc7301_priv *priv, struct rtc_time *tm, + bool alarm) +{ + int year; + + tm->tm_sec = rtc7301_read(priv, RTC7301_1_SEC); + tm->tm_sec += (rtc7301_read(priv, RTC7301_10_SEC) & ~RTC7301_AE) * 10; + tm->tm_min = rtc7301_read(priv, RTC7301_1_MIN); + tm->tm_min += (rtc7301_read(priv, RTC7301_10_MIN) & ~RTC7301_AE) * 10; + tm->tm_hour = rtc7301_read(priv, RTC7301_1_HOUR); + tm->tm_hour += (rtc7301_read(priv, RTC7301_10_HOUR) & ~RTC7301_AE) * 10; + tm->tm_mday = rtc7301_read(priv, RTC7301_1_DAY); + tm->tm_mday += (rtc7301_read(priv, RTC7301_10_DAY) & ~RTC7301_AE) * 10; + + if (alarm) { + tm->tm_wday = -1; + tm->tm_mon = -1; + tm->tm_year = -1; + tm->tm_yday = -1; + tm->tm_isdst = -1; + return; + } + + tm->tm_wday = (rtc7301_read(priv, RTC7301_DAY_OF_WEEK) & ~RTC7301_AE); + tm->tm_mon = rtc7301_read(priv, RTC7301_10_MONTH) * 10 + + rtc7301_read(priv, RTC7301_1_MONTH) - 1; + year = rtc7301_read(priv, RTC7301_1000_YEAR) * 1000 + + rtc7301_read(priv, RTC7301_100_YEAR) * 100 + + rtc7301_read(priv, RTC7301_10_YEAR) * 10 + + rtc7301_read(priv, RTC7301_1_YEAR); + + tm->tm_year = year - 1900; +} + +static void rtc7301_write_time(struct rtc7301_priv *priv, struct rtc_time *tm, + bool alarm) +{ + int year; + + rtc7301_write(priv, tm->tm_sec % 10, RTC7301_1_SEC); + rtc7301_write(priv, tm->tm_sec / 10, RTC7301_10_SEC); + + rtc7301_write(priv, tm->tm_min % 10, RTC7301_1_MIN); + rtc7301_write(priv, tm->tm_min / 10, RTC7301_10_MIN); + + rtc7301_write(priv, tm->tm_hour % 10, RTC7301_1_HOUR); + rtc7301_write(priv, tm->tm_hour / 10, RTC7301_10_HOUR); + + rtc7301_write(priv, tm->tm_mday % 10, RTC7301_1_DAY); + rtc7301_write(priv, tm->tm_mday / 10, RTC7301_10_DAY); + + /* Don't care for alarm register */ + rtc7301_write(priv, alarm ? RTC7301_AE : tm->tm_wday, + RTC7301_DAY_OF_WEEK); + + if (alarm) + return; + + rtc7301_write(priv, (tm->tm_mon + 1) % 10, RTC7301_1_MONTH); + rtc7301_write(priv, (tm->tm_mon + 1) / 10, RTC7301_10_MONTH); + + year = tm->tm_year + 1900; + + rtc7301_write(priv, year % 10, RTC7301_1_YEAR); + rtc7301_write(priv, (year / 10) % 10, RTC7301_10_YEAR); + rtc7301_write(priv, (year / 100) % 10, RTC7301_100_YEAR); + rtc7301_write(priv, year / 1000, RTC7301_1000_YEAR); +} + +static void rtc7301_alarm_irq(struct rtc7301_priv *priv, unsigned int enabled) +{ + rtc7301_update_bits(priv, RTC7301_ALARM_CONTROL, + RTC7301_ALARM_CONTROL_AF | + RTC7301_ALARM_CONTROL_AIE, + enabled ? RTC7301_ALARM_CONTROL_AIE : 0); +} + +static int rtc7301_read_time(struct device *dev, struct rtc_time *tm) +{ + struct rtc7301_priv *priv = dev_get_drvdata(dev); + unsigned long flags; + int err; + + spin_lock_irqsave(&priv->lock, flags); + + rtc7301_select_bank(priv, 0); + + err = rtc7301_wait_while_busy(priv); + if (!err) + rtc7301_get_time(priv, tm, false); + + spin_unlock_irqrestore(&priv->lock, flags); + + return err; +} + +static int rtc7301_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rtc7301_priv *priv = dev_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + + rtc7301_stop(priv); + udelay(300); + rtc7301_select_bank(priv, 0); + rtc7301_write_time(priv, tm, false); + rtc7301_start(priv); + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static int rtc7301_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct rtc7301_priv *priv = dev_get_drvdata(dev); + unsigned long flags; + u8 alrm_ctrl; + + if (priv->irq <= 0) + return -EINVAL; + + spin_lock_irqsave(&priv->lock, flags); + + rtc7301_select_bank(priv, 1); + rtc7301_get_time(priv, &alarm->time, true); + + alrm_ctrl = rtc7301_read(priv, RTC7301_ALARM_CONTROL); + + alarm->enabled = !!(alrm_ctrl & RTC7301_ALARM_CONTROL_AIE); + alarm->pending = !!(alrm_ctrl & RTC7301_ALARM_CONTROL_AF); + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static int rtc7301_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct rtc7301_priv *priv = dev_get_drvdata(dev); + unsigned long flags; + + if (priv->irq <= 0) + return -EINVAL; + + spin_lock_irqsave(&priv->lock, flags); + + rtc7301_select_bank(priv, 1); + rtc7301_write_time(priv, &alarm->time, true); + rtc7301_alarm_irq(priv, alarm->enabled); + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static int rtc7301_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct rtc7301_priv *priv = dev_get_drvdata(dev); + unsigned long flags; + + if (priv->irq <= 0) + return -EINVAL; + + spin_lock_irqsave(&priv->lock, flags); + + rtc7301_select_bank(priv, 1); + rtc7301_alarm_irq(priv, enabled); + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static const struct rtc_class_ops rtc7301_rtc_ops = { + .read_time = rtc7301_read_time, + .set_time = rtc7301_set_time, + .read_alarm = rtc7301_read_alarm, + .set_alarm = rtc7301_set_alarm, + .alarm_irq_enable = rtc7301_alarm_irq_enable, +}; + +static irqreturn_t rtc7301_irq_handler(int irq, void *dev_id) +{ + struct rtc_device *rtc = dev_id; + struct rtc7301_priv *priv = dev_get_drvdata(rtc->dev.parent); + irqreturn_t ret = IRQ_NONE; + u8 alrm_ctrl; + + spin_lock(&priv->lock); + + rtc7301_select_bank(priv, 1); + + alrm_ctrl = rtc7301_read(priv, RTC7301_ALARM_CONTROL); + if (alrm_ctrl & RTC7301_ALARM_CONTROL_AF) { + ret = IRQ_HANDLED; + rtc7301_alarm_irq(priv, false); + rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF); + } + + spin_unlock(&priv->lock); + + return ret; +} + +static void rtc7301_init(struct rtc7301_priv *priv) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + + rtc7301_select_bank(priv, 2); + rtc7301_write(priv, 0, RTC7301_TIMER_CONTROL); + + spin_unlock_irqrestore(&priv->lock, flags); +} + +static int __init rtc7301_rtc_probe(struct platform_device *dev) +{ + void __iomem *regs; + struct rtc7301_priv *priv; + struct rtc_device *rtc; + static const struct regmap_config *mapconf; + int ret; + u32 val; + + priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + regs = devm_platform_ioremap_resource(dev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + ret = device_property_read_u32(&dev->dev, "reg-io-width", &val); + if (ret) + /* Default to 32bit accesses */ + val = 4; + + switch (val) { + case 1: + mapconf = &rtc7301_regmap_8_config; + break; + case 4: + mapconf = &rtc7301_regmap_32_config; + break; + default: + dev_err(&dev->dev, "invalid reg-io-width %d\n", val); + return -EINVAL; + } + + priv->regmap = devm_regmap_init_mmio(&dev->dev, regs, + mapconf); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->irq = platform_get_irq(dev, 0); + + spin_lock_init(&priv->lock); + priv->bank = -1; + + rtc7301_init(priv); + + platform_set_drvdata(dev, priv); + + rtc = devm_rtc_device_register(&dev->dev, DRV_NAME, &rtc7301_rtc_ops, + THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + if (priv->irq > 0) { + ret = devm_request_irq(&dev->dev, priv->irq, + rtc7301_irq_handler, IRQF_SHARED, + dev_name(&dev->dev), rtc); + if (ret) { + priv->irq = 0; + dev_err(&dev->dev, "unable to request IRQ\n"); + } else { + device_set_wakeup_capable(&dev->dev, true); + } + } + + return 0; +} + +#ifdef CONFIG_PM_SLEEP + +static int rtc7301_suspend(struct device *dev) +{ + struct rtc7301_priv *priv = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(priv->irq); + + return 0; +} + +static int rtc7301_resume(struct device *dev) +{ + struct rtc7301_priv *priv = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(priv->irq); + + return 0; +} + +#endif + +static SIMPLE_DEV_PM_OPS(rtc7301_pm_ops, rtc7301_suspend, rtc7301_resume); + +static const struct of_device_id rtc7301_dt_match[] = { + { .compatible = "epson,rtc7301sf" }, + { .compatible = "epson,rtc7301dg" }, + {} +}; +MODULE_DEVICE_TABLE(of, rtc7301_dt_match); + +static struct platform_driver rtc7301_rtc_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = rtc7301_dt_match, + .pm = &rtc7301_pm_ops, + }, +}; + +module_platform_driver_probe(rtc7301_rtc_driver, rtc7301_rtc_probe); + +MODULE_AUTHOR("Akinobu Mita "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("EPSON TOYOCOM RTC-7301SF/DG Driver"); +MODULE_ALIAS("platform:rtc-r7301"); diff --git a/drivers/rtc/rtc-r9701.c b/drivers/rtc/rtc-r9701.c new file mode 100644 index 000000000..60a3c3d74 --- /dev/null +++ b/drivers/rtc/rtc-r9701.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Epson RTC-9701JE + * + * Copyright (C) 2008 Magnus Damm + * + * Based on rtc-max6902.c + * + * Copyright (C) 2006 8D Technologies inc. + * Copyright (C) 2004 Compulab Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RSECCNT 0x00 /* Second Counter */ +#define RMINCNT 0x01 /* Minute Counter */ +#define RHRCNT 0x02 /* Hour Counter */ +#define RWKCNT 0x03 /* Week Counter */ +#define RDAYCNT 0x04 /* Day Counter */ +#define RMONCNT 0x05 /* Month Counter */ +#define RYRCNT 0x06 /* Year Counter */ +#define R100CNT 0x07 /* Y100 Counter */ +#define RMINAR 0x08 /* Minute Alarm */ +#define RHRAR 0x09 /* Hour Alarm */ +#define RWKAR 0x0a /* Week/Day Alarm */ +#define RTIMCNT 0x0c /* Interval Timer */ +#define REXT 0x0d /* Extension Register */ +#define RFLAG 0x0e /* RTC Flag Register */ +#define RCR 0x0f /* RTC Control Register */ + +static int write_reg(struct device *dev, int address, unsigned char data) +{ + struct spi_device *spi = to_spi_device(dev); + unsigned char buf[2]; + + buf[0] = address & 0x7f; + buf[1] = data; + + return spi_write(spi, buf, ARRAY_SIZE(buf)); +} + +static int read_regs(struct device *dev, unsigned char *regs, int no_regs) +{ + struct spi_device *spi = to_spi_device(dev); + u8 txbuf[1], rxbuf[1]; + int k, ret; + + ret = 0; + + for (k = 0; ret == 0 && k < no_regs; k++) { + txbuf[0] = 0x80 | regs[k]; + ret = spi_write_then_read(spi, txbuf, 1, rxbuf, 1); + regs[k] = rxbuf[0]; + } + + return ret; +} + +static int r9701_get_datetime(struct device *dev, struct rtc_time *dt) +{ + int ret; + unsigned char buf[] = { RSECCNT, RMINCNT, RHRCNT, + RDAYCNT, RMONCNT, RYRCNT }; + + ret = read_regs(dev, buf, ARRAY_SIZE(buf)); + if (ret) + return ret; + + dt->tm_sec = bcd2bin(buf[0]); /* RSECCNT */ + dt->tm_min = bcd2bin(buf[1]); /* RMINCNT */ + dt->tm_hour = bcd2bin(buf[2]); /* RHRCNT */ + + dt->tm_mday = bcd2bin(buf[3]); /* RDAYCNT */ + dt->tm_mon = bcd2bin(buf[4]) - 1; /* RMONCNT */ + dt->tm_year = bcd2bin(buf[5]) + 100; /* RYRCNT */ + + return 0; +} + +static int r9701_set_datetime(struct device *dev, struct rtc_time *dt) +{ + int ret; + + ret = write_reg(dev, RHRCNT, bin2bcd(dt->tm_hour)); + ret = ret ? ret : write_reg(dev, RMINCNT, bin2bcd(dt->tm_min)); + ret = ret ? ret : write_reg(dev, RSECCNT, bin2bcd(dt->tm_sec)); + ret = ret ? ret : write_reg(dev, RDAYCNT, bin2bcd(dt->tm_mday)); + ret = ret ? ret : write_reg(dev, RMONCNT, bin2bcd(dt->tm_mon + 1)); + ret = ret ? ret : write_reg(dev, RYRCNT, bin2bcd(dt->tm_year - 100)); + + return ret; +} + +static const struct rtc_class_ops r9701_rtc_ops = { + .read_time = r9701_get_datetime, + .set_time = r9701_set_datetime, +}; + +static int r9701_probe(struct spi_device *spi) +{ + struct rtc_device *rtc; + unsigned char tmp; + int res; + + tmp = R100CNT; + res = read_regs(&spi->dev, &tmp, 1); + if (res || tmp != 0x20) { + dev_err(&spi->dev, "cannot read RTC register\n"); + return -ENODEV; + } + + rtc = devm_rtc_allocate_device(&spi->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + spi_set_drvdata(spi, rtc); + rtc->ops = &r9701_rtc_ops; + rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rtc->range_max = RTC_TIMESTAMP_END_2099; + + return devm_rtc_register_device(rtc); +} + +static struct spi_driver r9701_driver = { + .driver = { + .name = "rtc-r9701", + }, + .probe = r9701_probe, +}; + +module_spi_driver(r9701_driver); + +MODULE_DESCRIPTION("r9701 spi RTC driver"); +MODULE_AUTHOR("Magnus Damm "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:rtc-r9701"); diff --git a/drivers/rtc/rtc-rc5t583.c b/drivers/rtc/rtc-rc5t583.c new file mode 100644 index 000000000..8ba9cda74 --- /dev/null +++ b/drivers/rtc/rtc-rc5t583.c @@ -0,0 +1,312 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rtc-rc5t583.c -- RICOH RC5T583 Real Time Clock + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * Author: Venu Byravarasu + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct rc5t583_rtc { + struct rtc_device *rtc; + /* To store the list of enabled interrupts, during system suspend */ + u32 irqen; +}; + +/* Total number of RTC registers needed to set time*/ +#define NUM_TIME_REGS (RC5T583_RTC_YEAR - RC5T583_RTC_SEC + 1) + +/* Total number of RTC registers needed to set Y-Alarm*/ +#define NUM_YAL_REGS (RC5T583_RTC_AY_YEAR - RC5T583_RTC_AY_MIN + 1) + +/* Set Y-Alarm interrupt */ +#define SET_YAL BIT(5) + +/* Get Y-Alarm interrupt status*/ +#define GET_YAL_STATUS BIT(3) + +static int rc5t583_rtc_alarm_irq_enable(struct device *dev, unsigned enabled) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent); + u8 val; + + /* Set Y-Alarm, based on 'enabled' */ + val = enabled ? SET_YAL : 0; + + return regmap_update_bits(rc5t583->regmap, RC5T583_RTC_CTL1, SET_YAL, + val); +} + +/* + * Gets current rc5t583 RTC time and date parameters. + * + * The RTC's time/alarm representation is not what gmtime(3) requires + * Linux to use: + * + * - Months are 1..12 vs Linux 0-11 + * - Years are 0..99 vs Linux 1900..N (we assume 21st century) + */ +static int rc5t583_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent); + u8 rtc_data[NUM_TIME_REGS]; + int ret; + + ret = regmap_bulk_read(rc5t583->regmap, RC5T583_RTC_SEC, rtc_data, + NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "RTC read time failed with err:%d\n", ret); + return ret; + } + + tm->tm_sec = bcd2bin(rtc_data[0]); + tm->tm_min = bcd2bin(rtc_data[1]); + tm->tm_hour = bcd2bin(rtc_data[2]); + tm->tm_wday = bcd2bin(rtc_data[3]); + tm->tm_mday = bcd2bin(rtc_data[4]); + tm->tm_mon = bcd2bin(rtc_data[5]) - 1; + tm->tm_year = bcd2bin(rtc_data[6]) + 100; + + return ret; +} + +static int rc5t583_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent); + unsigned char rtc_data[NUM_TIME_REGS]; + int ret; + + rtc_data[0] = bin2bcd(tm->tm_sec); + rtc_data[1] = bin2bcd(tm->tm_min); + rtc_data[2] = bin2bcd(tm->tm_hour); + rtc_data[3] = bin2bcd(tm->tm_wday); + rtc_data[4] = bin2bcd(tm->tm_mday); + rtc_data[5] = bin2bcd(tm->tm_mon + 1); + rtc_data[6] = bin2bcd(tm->tm_year - 100); + + ret = regmap_bulk_write(rc5t583->regmap, RC5T583_RTC_SEC, rtc_data, + NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "RTC set time failed with error %d\n", ret); + return ret; + } + + return ret; +} + +static int rc5t583_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent); + unsigned char alarm_data[NUM_YAL_REGS]; + u32 interrupt_enable; + int ret; + + ret = regmap_bulk_read(rc5t583->regmap, RC5T583_RTC_AY_MIN, alarm_data, + NUM_YAL_REGS); + if (ret < 0) { + dev_err(dev, "rtc_read_alarm error %d\n", ret); + return ret; + } + + alm->time.tm_sec = 0; + alm->time.tm_min = bcd2bin(alarm_data[0]); + alm->time.tm_hour = bcd2bin(alarm_data[1]); + alm->time.tm_mday = bcd2bin(alarm_data[2]); + alm->time.tm_mon = bcd2bin(alarm_data[3]) - 1; + alm->time.tm_year = bcd2bin(alarm_data[4]) + 100; + + ret = regmap_read(rc5t583->regmap, RC5T583_RTC_CTL1, &interrupt_enable); + if (ret < 0) + return ret; + + /* check if YALE is set */ + if (interrupt_enable & SET_YAL) + alm->enabled = 1; + + return ret; +} + +static int rc5t583_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent); + unsigned char alarm_data[NUM_YAL_REGS]; + int ret; + + ret = rc5t583_rtc_alarm_irq_enable(dev, 0); + if (ret) + return ret; + + alarm_data[0] = bin2bcd(alm->time.tm_min); + alarm_data[1] = bin2bcd(alm->time.tm_hour); + alarm_data[2] = bin2bcd(alm->time.tm_mday); + alarm_data[3] = bin2bcd(alm->time.tm_mon + 1); + alarm_data[4] = bin2bcd(alm->time.tm_year - 100); + + ret = regmap_bulk_write(rc5t583->regmap, RC5T583_RTC_AY_MIN, alarm_data, + NUM_YAL_REGS); + if (ret) { + dev_err(dev, "rtc_set_alarm error %d\n", ret); + return ret; + } + + if (alm->enabled) + ret = rc5t583_rtc_alarm_irq_enable(dev, 1); + + return ret; +} + +static irqreturn_t rc5t583_rtc_interrupt(int irq, void *rtc) +{ + struct device *dev = rtc; + struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent); + struct rc5t583_rtc *rc5t583_rtc = dev_get_drvdata(dev); + unsigned long events = 0; + int ret; + u32 rtc_reg; + + ret = regmap_read(rc5t583->regmap, RC5T583_RTC_CTL2, &rtc_reg); + if (ret < 0) + return IRQ_NONE; + + if (rtc_reg & GET_YAL_STATUS) { + events = RTC_IRQF | RTC_AF; + /* clear pending Y-alarm interrupt bit */ + rtc_reg &= ~GET_YAL_STATUS; + } + + ret = regmap_write(rc5t583->regmap, RC5T583_RTC_CTL2, rtc_reg); + if (ret) + return IRQ_NONE; + + /* Notify RTC core on event */ + rtc_update_irq(rc5t583_rtc->rtc, 1, events); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops rc5t583_rtc_ops = { + .read_time = rc5t583_rtc_read_time, + .set_time = rc5t583_rtc_set_time, + .read_alarm = rc5t583_rtc_read_alarm, + .set_alarm = rc5t583_rtc_set_alarm, + .alarm_irq_enable = rc5t583_rtc_alarm_irq_enable, +}; + +static int rc5t583_rtc_probe(struct platform_device *pdev) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(pdev->dev.parent); + struct rc5t583_rtc *ricoh_rtc; + struct rc5t583_platform_data *pmic_plat_data; + int ret; + int irq; + + ricoh_rtc = devm_kzalloc(&pdev->dev, sizeof(struct rc5t583_rtc), + GFP_KERNEL); + if (!ricoh_rtc) + return -ENOMEM; + + platform_set_drvdata(pdev, ricoh_rtc); + + /* Clear pending interrupts */ + ret = regmap_write(rc5t583->regmap, RC5T583_RTC_CTL2, 0); + if (ret < 0) + return ret; + + /* clear RTC Adjust register */ + ret = regmap_write(rc5t583->regmap, RC5T583_RTC_ADJ, 0); + if (ret < 0) { + dev_err(&pdev->dev, "unable to program rtc_adjust reg\n"); + return -EBUSY; + } + + pmic_plat_data = dev_get_platdata(rc5t583->dev); + irq = pmic_plat_data->irq_base; + if (irq <= 0) { + dev_warn(&pdev->dev, "Wake up is not possible as irq = %d\n", + irq); + return ret; + } + + irq += RC5T583_IRQ_YALE; + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + rc5t583_rtc_interrupt, IRQF_TRIGGER_LOW, + "rtc-rc5t583", &pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "IRQ is not free.\n"); + return ret; + } + device_init_wakeup(&pdev->dev, true); + + ricoh_rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + &rc5t583_rtc_ops, THIS_MODULE); + if (IS_ERR(ricoh_rtc->rtc)) { + ret = PTR_ERR(ricoh_rtc->rtc); + dev_err(&pdev->dev, "RTC device register: err %d\n", ret); + return ret; + } + + return 0; +} + +/* + * Disable rc5t583 RTC interrupts. + * Sets status flag to free. + */ +static void rc5t583_rtc_remove(struct platform_device *pdev) +{ + struct rc5t583_rtc *rc5t583_rtc = platform_get_drvdata(pdev); + + rc5t583_rtc_alarm_irq_enable(&rc5t583_rtc->rtc->dev, 0); +} + +#ifdef CONFIG_PM_SLEEP +static int rc5t583_rtc_suspend(struct device *dev) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent); + struct rc5t583_rtc *rc5t583_rtc = dev_get_drvdata(dev); + int ret; + + /* Store current list of enabled interrupts*/ + ret = regmap_read(rc5t583->regmap, RC5T583_RTC_CTL1, + &rc5t583_rtc->irqen); + return ret; +} + +static int rc5t583_rtc_resume(struct device *dev) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent); + struct rc5t583_rtc *rc5t583_rtc = dev_get_drvdata(dev); + + /* Restore list of enabled interrupts before suspend */ + return regmap_write(rc5t583->regmap, RC5T583_RTC_CTL1, + rc5t583_rtc->irqen); +} +#endif + +static SIMPLE_DEV_PM_OPS(rc5t583_rtc_pm_ops, rc5t583_rtc_suspend, + rc5t583_rtc_resume); + +static struct platform_driver rc5t583_rtc_driver = { + .probe = rc5t583_rtc_probe, + .remove = rc5t583_rtc_remove, + .driver = { + .name = "rtc-rc5t583", + .pm = &rc5t583_rtc_pm_ops, + }, +}; + +module_platform_driver(rc5t583_rtc_driver); +MODULE_ALIAS("platform:rtc-rc5t583"); +MODULE_AUTHOR("Venu Byravarasu "); +MODULE_DESCRIPTION("RICOH 5T583 RTC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-rc5t619.c b/drivers/rtc/rtc-rc5t619.c new file mode 100644 index 000000000..74d169102 --- /dev/null +++ b/drivers/rtc/rtc-rc5t619.c @@ -0,0 +1,451 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * drivers/rtc/rtc-rc5t619.c + * + * Real time clock driver for RICOH RC5T619 power management chip. + * + * Copyright (C) 2019 Andreas Kemnade + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct rc5t619_rtc { + int irq; + struct rtc_device *rtc; + struct rn5t618 *rn5t618; +}; + +#define CTRL1_ALARM_ENABLED 0x40 +#define CTRL1_24HR 0x20 +#define CTRL1_PERIODIC_MASK 0xf + +#define CTRL2_PON 0x10 +#define CTRL2_ALARM_STATUS 0x80 +#define CTRL2_CTFG 0x4 +#define CTRL2_CTC 0x1 + +#define MONTH_CENTFLAG 0x80 +#define HOUR_PMFLAG 0x20 +#define MDAY_DAL_EXT 0x80 + +static uint8_t rtc5t619_12hour_bcd2bin(uint8_t hour) +{ + if (hour & HOUR_PMFLAG) { + hour = bcd2bin(hour & ~HOUR_PMFLAG); + return hour == 12 ? 12 : 12 + hour; + } + + hour = bcd2bin(hour); + return hour == 12 ? 0 : hour; +} + +static uint8_t rtc5t619_12hour_bin2bcd(uint8_t hour) +{ + if (!hour) + return 0x12; + + if (hour < 12) + return bin2bcd(hour); + + if (hour == 12) + return 0x12 | HOUR_PMFLAG; + + return bin2bcd(hour - 12) | HOUR_PMFLAG; +} + +static int rc5t619_rtc_periodic_disable(struct device *dev) +{ + struct rc5t619_rtc *rtc = dev_get_drvdata(dev); + int err; + + /* disable function */ + err = regmap_update_bits(rtc->rn5t618->regmap, + RN5T618_RTC_CTRL1, CTRL1_PERIODIC_MASK, 0); + if (err < 0) + return err; + + /* clear alarm flag and CTFG */ + err = regmap_update_bits(rtc->rn5t618->regmap, RN5T618_RTC_CTRL2, + CTRL2_ALARM_STATUS | CTRL2_CTFG | CTRL2_CTC, + 0); + if (err < 0) + return err; + + return 0; +} + +/* things to be done once after power on */ +static int rc5t619_rtc_pon_setup(struct device *dev) +{ + struct rc5t619_rtc *rtc = dev_get_drvdata(dev); + int err; + unsigned int reg_data; + + err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_CTRL2, ®_data); + if (err < 0) + return err; + + /* clear VDET PON */ + reg_data &= ~(CTRL2_PON | CTRL2_CTC | 0x4a); /* 0101-1011 */ + reg_data |= 0x20; /* 0010-0000 */ + err = regmap_write(rtc->rn5t618->regmap, RN5T618_RTC_CTRL2, reg_data); + if (err < 0) + return err; + + /* clearing RTC Adjust register */ + err = regmap_write(rtc->rn5t618->regmap, RN5T618_RTC_ADJUST, 0); + if (err) + return err; + + return regmap_update_bits(rtc->rn5t618->regmap, + RN5T618_RTC_CTRL1, + CTRL1_24HR, CTRL1_24HR); +} + +static int rc5t619_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct rc5t619_rtc *rtc = dev_get_drvdata(dev); + u8 buff[7]; + int err; + int cent_flag; + unsigned int ctrl1; + unsigned int ctrl2; + + err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_CTRL2, &ctrl2); + if (err < 0) + return err; + + if (ctrl2 & CTRL2_PON) + return -EINVAL; + + err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_CTRL1, &ctrl1); + if (err < 0) + return err; + + err = regmap_bulk_read(rtc->rn5t618->regmap, RN5T618_RTC_SECONDS, + buff, sizeof(buff)); + if (err < 0) + return err; + + if (buff[5] & MONTH_CENTFLAG) + cent_flag = 1; + else + cent_flag = 0; + + tm->tm_sec = bcd2bin(buff[0]); + tm->tm_min = bcd2bin(buff[1]); + + if (ctrl1 & CTRL1_24HR) + tm->tm_hour = bcd2bin(buff[2]); + else + tm->tm_hour = rtc5t619_12hour_bcd2bin(buff[2]); + + tm->tm_wday = bcd2bin(buff[3]); + tm->tm_mday = bcd2bin(buff[4]); + tm->tm_mon = bcd2bin(buff[5] & 0x1f) - 1; /* back to system 0-11 */ + tm->tm_year = bcd2bin(buff[6]) + 100 * cent_flag; + + return 0; +} + +static int rc5t619_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rc5t619_rtc *rtc = dev_get_drvdata(dev); + u8 buff[7]; + int err; + int cent_flag; + unsigned int ctrl1; + unsigned int ctrl2; + + err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_CTRL2, &ctrl2); + if (err < 0) + return err; + + if (ctrl2 & CTRL2_PON) + rc5t619_rtc_pon_setup(dev); + + err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_CTRL1, &ctrl1); + if (err < 0) + return err; + + if (tm->tm_year >= 100) + cent_flag = 1; + else + cent_flag = 0; + + buff[0] = bin2bcd(tm->tm_sec); + buff[1] = bin2bcd(tm->tm_min); + + if (ctrl1 & CTRL1_24HR) + buff[2] = bin2bcd(tm->tm_hour); + else + buff[2] = rtc5t619_12hour_bin2bcd(tm->tm_hour); + + buff[3] = bin2bcd(tm->tm_wday); + buff[4] = bin2bcd(tm->tm_mday); + buff[5] = bin2bcd(tm->tm_mon + 1); /* system set 0-11 */ + buff[6] = bin2bcd(tm->tm_year - cent_flag * 100); + + if (cent_flag) + buff[5] |= MONTH_CENTFLAG; + + err = regmap_bulk_write(rtc->rn5t618->regmap, RN5T618_RTC_SECONDS, + buff, sizeof(buff)); + if (err < 0) { + dev_err(dev, "failed to program new time: %d\n", err); + return err; + } + + return 0; +} + +/* 0-disable, 1-enable */ +static int rc5t619_rtc_alarm_enable(struct device *dev, unsigned int enabled) +{ + struct rc5t619_rtc *rtc = dev_get_drvdata(dev); + + return regmap_update_bits(rtc->rn5t618->regmap, + RN5T618_RTC_CTRL1, + CTRL1_ALARM_ENABLED, + enabled ? CTRL1_ALARM_ENABLED : 0); +} + +static int rc5t619_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rc5t619_rtc *rtc = dev_get_drvdata(dev); + u8 buff[6]; + unsigned int buff_cent; + int err; + int cent_flag; + unsigned int ctrl1; + + err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_CTRL1, &ctrl1); + if (err) + return err; + + err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_MONTH, &buff_cent); + if (err < 0) { + dev_err(dev, "failed to read time: %d\n", err); + return err; + } + + if (buff_cent & MONTH_CENTFLAG) + cent_flag = 1; + else + cent_flag = 0; + + err = regmap_bulk_read(rtc->rn5t618->regmap, RN5T618_RTC_ALARM_Y_SEC, + buff, sizeof(buff)); + if (err) + return err; + + buff[3] = buff[3] & 0x3f; + + alrm->time.tm_sec = bcd2bin(buff[0]); + alrm->time.tm_min = bcd2bin(buff[1]); + + if (ctrl1 & CTRL1_24HR) + alrm->time.tm_hour = bcd2bin(buff[2]); + else + alrm->time.tm_hour = rtc5t619_12hour_bcd2bin(buff[2]); + + alrm->time.tm_mday = bcd2bin(buff[3]); + alrm->time.tm_mon = bcd2bin(buff[4]) - 1; + alrm->time.tm_year = bcd2bin(buff[5]) + 100 * cent_flag; + alrm->enabled = !!(ctrl1 & CTRL1_ALARM_ENABLED); + dev_dbg(dev, "read alarm: %ptR\n", &alrm->time); + + return 0; +} + +static int rc5t619_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rc5t619_rtc *rtc = dev_get_drvdata(dev); + u8 buff[6]; + int err; + int cent_flag; + unsigned int ctrl1; + + err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_CTRL1, &ctrl1); + if (err) + return err; + + err = rc5t619_rtc_alarm_enable(dev, 0); + if (err < 0) + return err; + + if (rtc->irq == -1) + return -EINVAL; + + if (alrm->enabled == 0) + return 0; + + if (alrm->time.tm_year >= 100) + cent_flag = 1; + else + cent_flag = 0; + + alrm->time.tm_mon += 1; + buff[0] = bin2bcd(alrm->time.tm_sec); + buff[1] = bin2bcd(alrm->time.tm_min); + + if (ctrl1 & CTRL1_24HR) + buff[2] = bin2bcd(alrm->time.tm_hour); + else + buff[2] = rtc5t619_12hour_bin2bcd(alrm->time.tm_hour); + + buff[3] = bin2bcd(alrm->time.tm_mday); + buff[4] = bin2bcd(alrm->time.tm_mon); + buff[5] = bin2bcd(alrm->time.tm_year - 100 * cent_flag); + buff[3] |= MDAY_DAL_EXT; + + err = regmap_bulk_write(rtc->rn5t618->regmap, RN5T618_RTC_ALARM_Y_SEC, + buff, sizeof(buff)); + if (err < 0) + return err; + + return rc5t619_rtc_alarm_enable(dev, alrm->enabled); +} + +static const struct rtc_class_ops rc5t619_rtc_ops = { + .read_time = rc5t619_rtc_read_time, + .set_time = rc5t619_rtc_set_time, + .set_alarm = rc5t619_rtc_set_alarm, + .read_alarm = rc5t619_rtc_read_alarm, + .alarm_irq_enable = rc5t619_rtc_alarm_enable, +}; + +static int rc5t619_rtc_alarm_flag_clr(struct device *dev) +{ + struct rc5t619_rtc *rtc = dev_get_drvdata(dev); + + /* clear alarm-D status bits.*/ + return regmap_update_bits(rtc->rn5t618->regmap, + RN5T618_RTC_CTRL2, + CTRL2_ALARM_STATUS | CTRL2_CTC, 0); +} + +static irqreturn_t rc5t619_rtc_irq(int irq, void *data) +{ + struct device *dev = data; + struct rc5t619_rtc *rtc = dev_get_drvdata(dev); + + rc5t619_rtc_alarm_flag_clr(dev); + + rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF); + return IRQ_HANDLED; +} + +static int rc5t619_rtc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rn5t618 *rn5t618 = dev_get_drvdata(pdev->dev.parent); + struct rc5t619_rtc *rtc; + unsigned int ctrl2; + int err; + + rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->rn5t618 = rn5t618; + + dev_set_drvdata(dev, rtc); + rtc->irq = -1; + + if (rn5t618->irq_data) + rtc->irq = regmap_irq_get_virq(rn5t618->irq_data, + RN5T618_IRQ_RTC); + + if (rtc->irq < 0) + rtc->irq = -1; + + err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_CTRL2, &ctrl2); + if (err < 0) + return err; + + /* disable rtc periodic function */ + err = rc5t619_rtc_periodic_disable(&pdev->dev); + if (err) + return err; + + if (ctrl2 & CTRL2_PON) { + err = rc5t619_rtc_alarm_flag_clr(&pdev->dev); + if (err) + return err; + } + + rtc->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc->rtc)) { + err = PTR_ERR(rtc->rtc); + dev_err(dev, "RTC device register: err %d\n", err); + return err; + } + + rtc->rtc->ops = &rc5t619_rtc_ops; + rtc->rtc->range_min = RTC_TIMESTAMP_BEGIN_1900; + rtc->rtc->range_max = RTC_TIMESTAMP_END_2099; + + /* set interrupt and enable it */ + if (rtc->irq != -1) { + err = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL, + rc5t619_rtc_irq, + IRQF_ONESHOT, + "rtc-rc5t619", + &pdev->dev); + if (err < 0) { + dev_err(&pdev->dev, "request IRQ:%d fail\n", rtc->irq); + rtc->irq = -1; + + err = rc5t619_rtc_alarm_enable(&pdev->dev, 0); + if (err) + return err; + + } else { + /* enable wake */ + device_init_wakeup(&pdev->dev, true); + enable_irq_wake(rtc->irq); + } + } else { + /* system don't want to using alarm interrupt, so close it */ + err = rc5t619_rtc_alarm_enable(&pdev->dev, 0); + if (err) + return err; + + dev_warn(&pdev->dev, "rc5t619 interrupt is disabled\n"); + } + + return devm_rtc_register_device(rtc->rtc); +} + +static const struct platform_device_id rc5t619_rtc_id[] = { + { + .name = "rc5t619-rtc", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, rc5t619_rtc_id); + +static struct platform_driver rc5t619_rtc_driver = { + .driver = { + .name = "rc5t619-rtc", + }, + .probe = rc5t619_rtc_probe, + .id_table = rc5t619_rtc_id, +}; +module_platform_driver(rc5t619_rtc_driver); + +MODULE_DESCRIPTION("RICOH RC5T619 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-renesas-rtca3.c b/drivers/rtc/rtc-renesas-rtca3.c new file mode 100644 index 000000000..b3875d041 --- /dev/null +++ b/drivers/rtc/rtc-renesas-rtca3.c @@ -0,0 +1,900 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * On-Chip RTC Support available on RZ/G3S SoC + * + * Copyright (C) 2024 Renesas Electronics Corp. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Counter registers. */ +#define RTCA3_RSECCNT 0x2 +#define RTCA3_RSECCNT_SEC GENMASK(6, 0) +#define RTCA3_RMINCNT 0x4 +#define RTCA3_RMINCNT_MIN GENMASK(6, 0) +#define RTCA3_RHRCNT 0x6 +#define RTCA3_RHRCNT_HR GENMASK(5, 0) +#define RTCA3_RHRCNT_PM BIT(6) +#define RTCA3_RWKCNT 0x8 +#define RTCA3_RWKCNT_WK GENMASK(2, 0) +#define RTCA3_RDAYCNT 0xa +#define RTCA3_RDAYCNT_DAY GENMASK(5, 0) +#define RTCA3_RMONCNT 0xc +#define RTCA3_RMONCNT_MONTH GENMASK(4, 0) +#define RTCA3_RYRCNT 0xe +#define RTCA3_RYRCNT_YEAR GENMASK(7, 0) + +/* Alarm registers. */ +#define RTCA3_RSECAR 0x10 +#define RTCA3_RSECAR_SEC GENMASK(6, 0) +#define RTCA3_RMINAR 0x12 +#define RTCA3_RMINAR_MIN GENMASK(6, 0) +#define RTCA3_RHRAR 0x14 +#define RTCA3_RHRAR_HR GENMASK(5, 0) +#define RTCA3_RHRAR_PM BIT(6) +#define RTCA3_RWKAR 0x16 +#define RTCA3_RWKAR_DAYW GENMASK(2, 0) +#define RTCA3_RDAYAR 0x18 +#define RTCA3_RDAYAR_DATE GENMASK(5, 0) +#define RTCA3_RMONAR 0x1a +#define RTCA3_RMONAR_MON GENMASK(4, 0) +#define RTCA3_RYRAR 0x1c +#define RTCA3_RYRAR_YR GENMASK(7, 0) +#define RTCA3_RYRAREN 0x1e + +/* Alarm enable bit (for all alarm registers). */ +#define RTCA3_AR_ENB BIT(7) + +/* Control registers. */ +#define RTCA3_RCR1 0x22 +#define RTCA3_RCR1_AIE BIT(0) +#define RTCA3_RCR1_CIE BIT(1) +#define RTCA3_RCR1_PIE BIT(2) +#define RTCA3_RCR1_PES GENMASK(7, 4) +#define RTCA3_RCR1_PES_1_64_SEC 0x8 +#define RTCA3_RCR2 0x24 +#define RTCA3_RCR2_START BIT(0) +#define RTCA3_RCR2_RESET BIT(1) +#define RTCA3_RCR2_AADJE BIT(4) +#define RTCA3_RCR2_ADJP BIT(5) +#define RTCA3_RCR2_HR24 BIT(6) +#define RTCA3_RCR2_CNTMD BIT(7) +#define RTCA3_RSR 0x20 +#define RTCA3_RSR_AF BIT(0) +#define RTCA3_RSR_CF BIT(1) +#define RTCA3_RSR_PF BIT(2) +#define RTCA3_RADJ 0x2e +#define RTCA3_RADJ_ADJ GENMASK(5, 0) +#define RTCA3_RADJ_ADJ_MAX 0x3f +#define RTCA3_RADJ_PMADJ GENMASK(7, 6) +#define RTCA3_RADJ_PMADJ_NONE 0 +#define RTCA3_RADJ_PMADJ_ADD 1 +#define RTCA3_RADJ_PMADJ_SUB 2 + +/* Polling operation timeouts. */ +#define RTCA3_DEFAULT_TIMEOUT_US 150 +#define RTCA3_IRQSET_TIMEOUT_US 5000 +#define RTCA3_START_TIMEOUT_US 150000 +#define RTCA3_RESET_TIMEOUT_US 200000 + +/** + * enum rtca3_alrm_set_step - RTCA3 alarm set steps + * @RTCA3_ALRM_SSTEP_DONE: alarm setup done step + * @RTCA3_ALRM_SSTEP_IRQ: two 1/64 periodic IRQs were generated step + * @RTCA3_ALRM_SSTEP_INIT: alarm setup initialization step + */ +enum rtca3_alrm_set_step { + RTCA3_ALRM_SSTEP_DONE = 0, + RTCA3_ALRM_SSTEP_IRQ = 1, + RTCA3_ALRM_SSTEP_INIT = 3, +}; + +/** + * struct rtca3_ppb_per_cycle - PPB per cycle + * @ten_sec: PPB per cycle in 10 seconds adjustment mode + * @sixty_sec: PPB per cycle in 60 seconds adjustment mode + */ +struct rtca3_ppb_per_cycle { + int ten_sec; + int sixty_sec; +}; + +/** + * struct rtca3_priv - RTCA3 private data structure + * @base: base address + * @rtc_dev: RTC device + * @rstc: reset control + * @set_alarm_completion: alarm setup completion + * @alrm_sstep: alarm setup step (see enum rtca3_alrm_set_step) + * @lock: device lock + * @ppb: ppb per cycle for each the available adjustment modes + * @wakeup_irq: wakeup IRQ + */ +struct rtca3_priv { + void __iomem *base; + struct rtc_device *rtc_dev; + struct reset_control *rstc; + struct completion set_alarm_completion; + atomic_t alrm_sstep; + spinlock_t lock; + struct rtca3_ppb_per_cycle ppb; + int wakeup_irq; +}; + +static void rtca3_byte_update_bits(struct rtca3_priv *priv, u8 off, u8 mask, u8 val) +{ + u8 tmp; + + tmp = readb(priv->base + off); + tmp &= ~mask; + tmp |= (val & mask); + writeb(tmp, priv->base + off); +} + +static u8 rtca3_alarm_handler_helper(struct rtca3_priv *priv) +{ + u8 val, pending; + + val = readb(priv->base + RTCA3_RSR); + pending = val & RTCA3_RSR_AF; + writeb(val & ~pending, priv->base + RTCA3_RSR); + + if (pending) + rtc_update_irq(priv->rtc_dev, 1, RTC_AF | RTC_IRQF); + + return pending; +} + +static irqreturn_t rtca3_alarm_handler(int irq, void *dev_id) +{ + struct rtca3_priv *priv = dev_id; + u8 pending; + + guard(spinlock)(&priv->lock); + + pending = rtca3_alarm_handler_helper(priv); + + return IRQ_RETVAL(pending); +} + +static irqreturn_t rtca3_periodic_handler(int irq, void *dev_id) +{ + struct rtca3_priv *priv = dev_id; + u8 val, pending; + + guard(spinlock)(&priv->lock); + + val = readb(priv->base + RTCA3_RSR); + pending = val & RTCA3_RSR_PF; + + if (pending) { + writeb(val & ~pending, priv->base + RTCA3_RSR); + + if (atomic_read(&priv->alrm_sstep) > RTCA3_ALRM_SSTEP_IRQ) { + /* Alarm setup in progress. */ + atomic_dec(&priv->alrm_sstep); + + if (atomic_read(&priv->alrm_sstep) == RTCA3_ALRM_SSTEP_IRQ) { + /* + * We got 2 * 1/64 periodic interrupts. Disable + * interrupt and let alarm setup continue. + */ + rtca3_byte_update_bits(priv, RTCA3_RCR1, + RTCA3_RCR1_PIE, 0); + readb_poll_timeout_atomic(priv->base + RTCA3_RCR1, val, + !(val & RTCA3_RCR1_PIE), + 10, RTCA3_DEFAULT_TIMEOUT_US); + complete(&priv->set_alarm_completion); + } + } + } + + return IRQ_RETVAL(pending); +} + +static void rtca3_prepare_cntalrm_regs_for_read(struct rtca3_priv *priv, bool cnt) +{ + /* Offset b/w time and alarm registers. */ + u8 offset = cnt ? 0 : 0xe; + + /* + * According to HW manual (section 22.6.4. Notes on writing to and + * reading from registers) after writing to count registers, alarm + * registers, year alarm enable register, bits RCR2.AADJE, AADJP, + * and HR24 register, we need to do 3 empty reads before being + * able to fetch the registers content. + */ + for (u8 i = 0; i < 3; i++) { + readb(priv->base + RTCA3_RSECCNT + offset); + readb(priv->base + RTCA3_RMINCNT + offset); + readb(priv->base + RTCA3_RHRCNT + offset); + readb(priv->base + RTCA3_RWKCNT + offset); + readb(priv->base + RTCA3_RDAYCNT + offset); + readw(priv->base + RTCA3_RYRCNT + offset); + if (!cnt) + readb(priv->base + RTCA3_RYRAREN); + } +} + +static u32 rtca3_decode_year(u8 mask, u16 year) +{ + u8 y = FIELD_GET(mask, year); + u32 century = bcd2bin((y == 0x99) ? 0x19 : 0x20); + + return (century * 100 + bcd2bin(y)) - 1900; +} + +static int rtca3_read_time(struct device *dev, struct rtc_time *tm) +{ + struct rtca3_priv *priv = dev_get_drvdata(dev); + u8 sec, min, hour, wday, mday, month, tmp; + u8 trials = 0; + u16 year; + + guard(spinlock_irqsave)(&priv->lock); + + tmp = readb(priv->base + RTCA3_RCR2); + if (!(tmp & RTCA3_RCR2_START)) + return -EINVAL; + + do { + /* Clear carry interrupt. */ + rtca3_byte_update_bits(priv, RTCA3_RSR, RTCA3_RSR_CF, 0); + + /* Read counters. */ + sec = readb(priv->base + RTCA3_RSECCNT); + min = readb(priv->base + RTCA3_RMINCNT); + hour = readb(priv->base + RTCA3_RHRCNT); + wday = readb(priv->base + RTCA3_RWKCNT); + mday = readb(priv->base + RTCA3_RDAYCNT); + month = readb(priv->base + RTCA3_RMONCNT); + year = readw(priv->base + RTCA3_RYRCNT); + + tmp = readb(priv->base + RTCA3_RSR); + + /* + * We cannot generate carries due to reading 64Hz counter as + * the driver doesn't implement carry, thus, carries will be + * generated once per seconds. Add a timeout of 5 trials here + * to avoid infinite loop, if any. + */ + } while ((tmp & RTCA3_RSR_CF) && ++trials < 5); + + if (trials >= 5) + return -ETIMEDOUT; + + tm->tm_sec = bcd2bin(FIELD_GET(RTCA3_RSECCNT_SEC, sec)); + tm->tm_min = bcd2bin(FIELD_GET(RTCA3_RMINCNT_MIN, min)); + tm->tm_hour = bcd2bin(FIELD_GET(RTCA3_RHRCNT_HR, hour)); + tm->tm_wday = bcd2bin(FIELD_GET(RTCA3_RWKCNT_WK, wday)); + tm->tm_mday = bcd2bin(FIELD_GET(RTCA3_RDAYCNT_DAY, mday)); + tm->tm_mon = bcd2bin(FIELD_GET(RTCA3_RMONCNT_MONTH, month)) - 1; + tm->tm_year = rtca3_decode_year(RTCA3_RYRCNT_YEAR, year); + + return 0; +} + +static int rtca3_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rtca3_priv *priv = dev_get_drvdata(dev); + u8 rcr2, tmp; + int ret; + + guard(spinlock_irqsave)(&priv->lock); + + /* Stop the RTC. */ + rcr2 = readb(priv->base + RTCA3_RCR2); + writeb(rcr2 & ~RTCA3_RCR2_START, priv->base + RTCA3_RCR2); + ret = readb_poll_timeout_atomic(priv->base + RTCA3_RCR2, tmp, + !(tmp & RTCA3_RCR2_START), + 10, RTCA3_DEFAULT_TIMEOUT_US); + if (ret) + return ret; + + /* Update time. */ + writeb(bin2bcd(tm->tm_sec), priv->base + RTCA3_RSECCNT); + writeb(bin2bcd(tm->tm_min), priv->base + RTCA3_RMINCNT); + writeb(bin2bcd(tm->tm_hour), priv->base + RTCA3_RHRCNT); + writeb(bin2bcd(tm->tm_wday), priv->base + RTCA3_RWKCNT); + writeb(bin2bcd(tm->tm_mday), priv->base + RTCA3_RDAYCNT); + writeb(bin2bcd(tm->tm_mon + 1), priv->base + RTCA3_RMONCNT); + writew(bin2bcd(tm->tm_year % 100), priv->base + RTCA3_RYRCNT); + + /* Make sure we can read back the counters. */ + rtca3_prepare_cntalrm_regs_for_read(priv, true); + + /* Start RTC. */ + writeb(rcr2 | RTCA3_RCR2_START, priv->base + RTCA3_RCR2); + return readb_poll_timeout_atomic(priv->base + RTCA3_RCR2, tmp, + (tmp & RTCA3_RCR2_START), + 10, RTCA3_DEFAULT_TIMEOUT_US); +} + +static int rtca3_alarm_irq_set_helper(struct rtca3_priv *priv, + u8 interrupts, + unsigned int enabled) +{ + u8 tmp, val; + + if (enabled) { + /* + * AIE, CIE, PIE bit indexes in RSR corresponds with + * those on RCR1. Same interrupts mask can be used. + */ + rtca3_byte_update_bits(priv, RTCA3_RSR, interrupts, 0); + val = interrupts; + } else { + val = 0; + } + + rtca3_byte_update_bits(priv, RTCA3_RCR1, interrupts, val); + return readb_poll_timeout_atomic(priv->base + RTCA3_RCR1, tmp, + ((tmp & interrupts) == val), + 10, RTCA3_IRQSET_TIMEOUT_US); +} + +static int rtca3_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct rtca3_priv *priv = dev_get_drvdata(dev); + + guard(spinlock_irqsave)(&priv->lock); + + return rtca3_alarm_irq_set_helper(priv, RTCA3_RCR1_AIE, enabled); +} + +static int rtca3_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct rtca3_priv *priv = dev_get_drvdata(dev); + u8 sec, min, hour, wday, mday, month; + struct rtc_time *tm = &wkalrm->time; + u16 year; + + guard(spinlock_irqsave)(&priv->lock); + + sec = readb(priv->base + RTCA3_RSECAR); + min = readb(priv->base + RTCA3_RMINAR); + hour = readb(priv->base + RTCA3_RHRAR); + wday = readb(priv->base + RTCA3_RWKAR); + mday = readb(priv->base + RTCA3_RDAYAR); + month = readb(priv->base + RTCA3_RMONAR); + year = readw(priv->base + RTCA3_RYRAR); + + tm->tm_sec = bcd2bin(FIELD_GET(RTCA3_RSECAR_SEC, sec)); + tm->tm_min = bcd2bin(FIELD_GET(RTCA3_RMINAR_MIN, min)); + tm->tm_hour = bcd2bin(FIELD_GET(RTCA3_RHRAR_HR, hour)); + tm->tm_wday = bcd2bin(FIELD_GET(RTCA3_RWKAR_DAYW, wday)); + tm->tm_mday = bcd2bin(FIELD_GET(RTCA3_RDAYAR_DATE, mday)); + tm->tm_mon = bcd2bin(FIELD_GET(RTCA3_RMONAR_MON, month)) - 1; + tm->tm_year = rtca3_decode_year(RTCA3_RYRAR_YR, year); + + wkalrm->enabled = !!(readb(priv->base + RTCA3_RCR1) & RTCA3_RCR1_AIE); + + return 0; +} + +static int rtca3_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct rtca3_priv *priv = dev_get_drvdata(dev); + struct rtc_time *tm = &wkalrm->time; + u8 rcr1, tmp; + int ret; + + scoped_guard(spinlock_irqsave, &priv->lock) { + tmp = readb(priv->base + RTCA3_RCR2); + if (!(tmp & RTCA3_RCR2_START)) + return -EPERM; + + /* Disable AIE to prevent false interrupts. */ + rcr1 = readb(priv->base + RTCA3_RCR1); + rcr1 &= ~RTCA3_RCR1_AIE; + writeb(rcr1, priv->base + RTCA3_RCR1); + ret = readb_poll_timeout_atomic(priv->base + RTCA3_RCR1, tmp, + !(tmp & RTCA3_RCR1_AIE), + 10, RTCA3_DEFAULT_TIMEOUT_US); + if (ret) + return ret; + + /* Set the time and enable the alarm. */ + writeb(RTCA3_AR_ENB | bin2bcd(tm->tm_sec), priv->base + RTCA3_RSECAR); + writeb(RTCA3_AR_ENB | bin2bcd(tm->tm_min), priv->base + RTCA3_RMINAR); + writeb(RTCA3_AR_ENB | bin2bcd(tm->tm_hour), priv->base + RTCA3_RHRAR); + writeb(RTCA3_AR_ENB | bin2bcd(tm->tm_wday), priv->base + RTCA3_RWKAR); + writeb(RTCA3_AR_ENB | bin2bcd(tm->tm_mday), priv->base + RTCA3_RDAYAR); + writeb(RTCA3_AR_ENB | bin2bcd(tm->tm_mon + 1), priv->base + RTCA3_RMONAR); + + writew(bin2bcd(tm->tm_year % 100), priv->base + RTCA3_RYRAR); + writeb(RTCA3_AR_ENB, priv->base + RTCA3_RYRAREN); + + /* Make sure we can read back the counters. */ + rtca3_prepare_cntalrm_regs_for_read(priv, false); + + /* Need to wait for 2 * 1/64 periodic interrupts to be generated. */ + atomic_set(&priv->alrm_sstep, RTCA3_ALRM_SSTEP_INIT); + reinit_completion(&priv->set_alarm_completion); + + /* Enable periodic interrupt. */ + rcr1 |= RTCA3_RCR1_PIE; + writeb(rcr1, priv->base + RTCA3_RCR1); + ret = readb_poll_timeout_atomic(priv->base + RTCA3_RCR1, tmp, + (tmp & RTCA3_RCR1_PIE), + 10, RTCA3_IRQSET_TIMEOUT_US); + } + + if (ret) + goto setup_failed; + + /* Wait for the 2 * 1/64 periodic interrupts. */ + ret = wait_for_completion_interruptible_timeout(&priv->set_alarm_completion, + msecs_to_jiffies(500)); + if (ret <= 0) { + ret = -ETIMEDOUT; + goto setup_failed; + } + + scoped_guard(spinlock_irqsave, &priv->lock) { + ret = rtca3_alarm_irq_set_helper(priv, RTCA3_RCR1_AIE, wkalrm->enabled); + atomic_set(&priv->alrm_sstep, RTCA3_ALRM_SSTEP_DONE); + } + + return ret; + +setup_failed: + scoped_guard(spinlock_irqsave, &priv->lock) { + /* + * Disable PIE to avoid interrupt storm in case HW needed more than + * specified timeout for setup. + */ + writeb(rcr1 & ~RTCA3_RCR1_PIE, priv->base + RTCA3_RCR1); + readb_poll_timeout_atomic(priv->base + RTCA3_RCR1, tmp, !(tmp & RTCA3_RCR1_PIE), + 10, RTCA3_DEFAULT_TIMEOUT_US); + atomic_set(&priv->alrm_sstep, RTCA3_ALRM_SSTEP_DONE); + } + + return ret; +} + +static int rtca3_read_offset(struct device *dev, long *offset) +{ + struct rtca3_priv *priv = dev_get_drvdata(dev); + u8 val, radj, cycles; + u32 ppb_per_cycle; + + scoped_guard(spinlock_irqsave, &priv->lock) { + radj = readb(priv->base + RTCA3_RADJ); + val = readb(priv->base + RTCA3_RCR2); + } + + cycles = FIELD_GET(RTCA3_RADJ_ADJ, radj); + + if (!cycles) { + *offset = 0; + return 0; + } + + if (val & RTCA3_RCR2_ADJP) + ppb_per_cycle = priv->ppb.ten_sec; + else + ppb_per_cycle = priv->ppb.sixty_sec; + + *offset = cycles * ppb_per_cycle; + val = FIELD_GET(RTCA3_RADJ_PMADJ, radj); + if (val == RTCA3_RADJ_PMADJ_SUB) + *offset = -(*offset); + + return 0; +} + +static int rtca3_set_offset(struct device *dev, long offset) +{ + struct rtca3_priv *priv = dev_get_drvdata(dev); + int cycles, cycles10, cycles60; + u8 radj, adjp, tmp; + int ret; + + /* + * Automatic time error adjustment could be set at intervals of 10 + * or 60 seconds. + */ + cycles10 = DIV_ROUND_CLOSEST(offset, priv->ppb.ten_sec); + cycles60 = DIV_ROUND_CLOSEST(offset, priv->ppb.sixty_sec); + + /* We can set b/w 1 and 63 clock cycles. */ + if (cycles60 >= -RTCA3_RADJ_ADJ_MAX && + cycles60 <= RTCA3_RADJ_ADJ_MAX) { + cycles = cycles60; + adjp = 0; + } else if (cycles10 >= -RTCA3_RADJ_ADJ_MAX && + cycles10 <= RTCA3_RADJ_ADJ_MAX) { + cycles = cycles10; + adjp = RTCA3_RCR2_ADJP; + } else { + return -ERANGE; + } + + radj = FIELD_PREP(RTCA3_RADJ_ADJ, abs(cycles)); + if (!cycles) + radj |= FIELD_PREP(RTCA3_RADJ_PMADJ, RTCA3_RADJ_PMADJ_NONE); + else if (cycles > 0) + radj |= FIELD_PREP(RTCA3_RADJ_PMADJ, RTCA3_RADJ_PMADJ_ADD); + else + radj |= FIELD_PREP(RTCA3_RADJ_PMADJ, RTCA3_RADJ_PMADJ_SUB); + + guard(spinlock_irqsave)(&priv->lock); + + tmp = readb(priv->base + RTCA3_RCR2); + + if ((tmp & RTCA3_RCR2_ADJP) != adjp) { + /* RADJ.PMADJ need to be set to zero before setting RCR2.ADJP. */ + writeb(0, priv->base + RTCA3_RADJ); + ret = readb_poll_timeout_atomic(priv->base + RTCA3_RADJ, tmp, !tmp, + 10, RTCA3_DEFAULT_TIMEOUT_US); + if (ret) + return ret; + + rtca3_byte_update_bits(priv, RTCA3_RCR2, RTCA3_RCR2_ADJP, adjp); + ret = readb_poll_timeout_atomic(priv->base + RTCA3_RCR2, tmp, + ((tmp & RTCA3_RCR2_ADJP) == adjp), + 10, RTCA3_DEFAULT_TIMEOUT_US); + if (ret) + return ret; + } + + writeb(radj, priv->base + RTCA3_RADJ); + return readb_poll_timeout_atomic(priv->base + RTCA3_RADJ, tmp, (tmp == radj), + 10, RTCA3_DEFAULT_TIMEOUT_US); +} + +static const struct rtc_class_ops rtca3_ops = { + .read_time = rtca3_read_time, + .set_time = rtca3_set_time, + .read_alarm = rtca3_read_alarm, + .set_alarm = rtca3_set_alarm, + .alarm_irq_enable = rtca3_alarm_irq_enable, + .set_offset = rtca3_set_offset, + .read_offset = rtca3_read_offset, +}; + +static int rtca3_initial_setup(struct clk *clk, struct rtca3_priv *priv) +{ + unsigned long osc32k_rate; + u8 val, tmp, mask; + u32 sleep_us; + int ret; + + osc32k_rate = clk_get_rate(clk); + if (!osc32k_rate) + return -EINVAL; + + sleep_us = DIV_ROUND_UP_ULL(1000000ULL, osc32k_rate) * 6; + + priv->ppb.ten_sec = DIV_ROUND_CLOSEST_ULL(1000000000ULL, (osc32k_rate * 10)); + priv->ppb.sixty_sec = DIV_ROUND_CLOSEST_ULL(1000000000ULL, (osc32k_rate * 60)); + + /* + * According to HW manual (section 22.4.2. Clock and count mode setting procedure) + * we need to wait at least 6 cycles of the 32KHz clock after clock was enabled. + */ + usleep_range(sleep_us, sleep_us + 10); + + mask = RTCA3_RCR2_START | RTCA3_RCR2_HR24; + val = readb(priv->base + RTCA3_RCR2); + /* Only disable the interrupts if already started in 24 hours and calendar count mode. */ + if ((val & mask) == mask) { + /* Disable all interrupts. */ + mask = RTCA3_RCR1_AIE | RTCA3_RCR1_CIE | RTCA3_RCR1_PIE; + return rtca3_alarm_irq_set_helper(priv, mask, 0); + } + + /* Reconfigure the RTC in 24 hours and calendar count mode. */ + mask = RTCA3_RCR2_START | RTCA3_RCR2_CNTMD; + writeb(0, priv->base + RTCA3_RCR2); + ret = readb_poll_timeout(priv->base + RTCA3_RCR2, tmp, !(tmp & mask), + 10, RTCA3_DEFAULT_TIMEOUT_US); + if (ret) + return ret; + + /* + * Set 24 hours mode. According to HW manual (section 22.3.19. RTC Control + * Register 2) this needs to be done separate from stop operation. + */ + mask = RTCA3_RCR2_HR24; + val = RTCA3_RCR2_HR24; + writeb(val, priv->base + RTCA3_RCR2); + ret = readb_poll_timeout(priv->base + RTCA3_RCR2, tmp, (tmp & mask), + 10, RTCA3_DEFAULT_TIMEOUT_US); + if (ret) + return ret; + + /* Execute reset. */ + mask = RTCA3_RCR2_RESET; + writeb(val | RTCA3_RCR2_RESET, priv->base + RTCA3_RCR2); + ret = readb_poll_timeout(priv->base + RTCA3_RCR2, tmp, !(tmp & mask), + 10, RTCA3_RESET_TIMEOUT_US); + if (ret) + return ret; + + /* + * According to HW manual (section 22.6.3. Notes on writing to and reading + * from registers) after reset we need to wait 6 clock cycles before + * writing to RTC registers. + */ + usleep_range(sleep_us, sleep_us + 10); + + /* Set no adjustment. */ + writeb(0, priv->base + RTCA3_RADJ); + ret = readb_poll_timeout(priv->base + RTCA3_RADJ, tmp, !tmp, 10, + RTCA3_DEFAULT_TIMEOUT_US); + if (ret) + return ret; + + /* Start the RTC and enable automatic time error adjustment. */ + mask = RTCA3_RCR2_START | RTCA3_RCR2_AADJE; + val |= RTCA3_RCR2_START | RTCA3_RCR2_AADJE; + writeb(val, priv->base + RTCA3_RCR2); + ret = readb_poll_timeout(priv->base + RTCA3_RCR2, tmp, ((tmp & mask) == mask), + 10, RTCA3_START_TIMEOUT_US); + if (ret) + return ret; + + /* + * According to HW manual (section 22.6.4. Notes on writing to and reading + * from registers) we need to wait 1/128 seconds while the clock is operating + * (RCR2.START bit = 1) to be able to read the counters after a return from + * reset. + */ + usleep_range(8000, 9000); + + /* Set period interrupt to 1/64 seconds. It is necessary for alarm setup. */ + val = FIELD_PREP(RTCA3_RCR1_PES, RTCA3_RCR1_PES_1_64_SEC); + rtca3_byte_update_bits(priv, RTCA3_RCR1, RTCA3_RCR1_PES, val); + return readb_poll_timeout(priv->base + RTCA3_RCR1, tmp, ((tmp & RTCA3_RCR1_PES) == val), + 10, RTCA3_DEFAULT_TIMEOUT_US); +} + +static int rtca3_request_irqs(struct platform_device *pdev, struct rtca3_priv *priv) +{ + struct device *dev = &pdev->dev; + int ret, irq; + + irq = platform_get_irq_byname(pdev, "alarm"); + if (irq < 0) + return dev_err_probe(dev, irq, "Failed to get alarm IRQ!\n"); + + ret = devm_request_irq(dev, irq, rtca3_alarm_handler, 0, "rtca3-alarm", priv); + if (ret) + return dev_err_probe(dev, ret, "Failed to request alarm IRQ!\n"); + priv->wakeup_irq = irq; + + irq = platform_get_irq_byname(pdev, "period"); + if (irq < 0) + return dev_err_probe(dev, irq, "Failed to get period IRQ!\n"); + + ret = devm_request_irq(dev, irq, rtca3_periodic_handler, 0, "rtca3-period", priv); + if (ret) + return dev_err_probe(dev, ret, "Failed to request period IRQ!\n"); + + /* + * Driver doesn't implement carry handler. Just get the IRQ here + * for backward compatibility, in case carry support will be added later. + */ + irq = platform_get_irq_byname(pdev, "carry"); + if (irq < 0) + return dev_err_probe(dev, irq, "Failed to get carry IRQ!\n"); + + return 0; +} + +static void rtca3_action(void *data) +{ + struct device *dev = data; + struct rtca3_priv *priv = dev_get_drvdata(dev); + int ret; + + ret = reset_control_assert(priv->rstc); + if (ret) + dev_err(dev, "Failed to assert reset!"); + + ret = pm_runtime_put_sync(dev); + if (ret < 0) + dev_err(dev, "Failed to runtime suspend!"); +} + +static int rtca3_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rtca3_priv *priv; + struct clk *clk; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + priv->rstc = devm_reset_control_array_get_shared(dev); + if (IS_ERR(priv->rstc)) + return PTR_ERR(priv->rstc); + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + ret = reset_control_deassert(priv->rstc); + if (ret) { + pm_runtime_put_sync(dev); + return ret; + } + + dev_set_drvdata(dev, priv); + ret = devm_add_action_or_reset(dev, rtca3_action, dev); + if (ret) + return ret; + + /* + * This must be an always-on clock to keep the RTC running even after + * driver is unbinded. + */ + clk = devm_clk_get_enabled(dev, "counter"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + spin_lock_init(&priv->lock); + atomic_set(&priv->alrm_sstep, RTCA3_ALRM_SSTEP_DONE); + init_completion(&priv->set_alarm_completion); + + ret = rtca3_initial_setup(clk, priv); + if (ret) + return dev_err_probe(dev, ret, "Failed to setup the RTC!\n"); + + ret = rtca3_request_irqs(pdev, priv); + if (ret) + return ret; + + device_init_wakeup(&pdev->dev, true); + + priv->rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(priv->rtc_dev)) + return PTR_ERR(priv->rtc_dev); + + priv->rtc_dev->ops = &rtca3_ops; + priv->rtc_dev->range_min = RTC_TIMESTAMP_BEGIN_2000; + priv->rtc_dev->range_max = RTC_TIMESTAMP_END_2099; + + return devm_rtc_register_device(priv->rtc_dev); +} + +static void rtca3_remove(struct platform_device *pdev) +{ + struct rtca3_priv *priv = platform_get_drvdata(pdev); + + guard(spinlock_irqsave)(&priv->lock); + + /* + * Disable alarm, periodic interrupts. The RTC device cannot + * power up the system. + */ + rtca3_alarm_irq_set_helper(priv, RTCA3_RCR1_AIE | RTCA3_RCR1_PIE, 0); +} + +static int rtca3_suspend(struct device *dev) +{ + struct rtca3_priv *priv = dev_get_drvdata(dev); + + if (!device_may_wakeup(dev)) + return 0; + + /* Alarm setup in progress. */ + if (atomic_read(&priv->alrm_sstep) != RTCA3_ALRM_SSTEP_DONE) + return -EBUSY; + + enable_irq_wake(priv->wakeup_irq); + + return 0; +} + +static int rtca3_clean_alarm(struct rtca3_priv *priv) +{ + struct rtc_device *rtc_dev = priv->rtc_dev; + time64_t alarm_time, now; + struct rtc_wkalrm alarm; + struct rtc_time tm; + u8 pending; + int ret; + + ret = rtc_read_alarm(rtc_dev, &alarm); + if (ret) + return ret; + + if (!alarm.enabled) + return 0; + + ret = rtc_read_time(rtc_dev, &tm); + if (ret) + return ret; + + alarm_time = rtc_tm_to_time64(&alarm.time); + now = rtc_tm_to_time64(&tm); + if (alarm_time >= now) + return 0; + + /* + * Heuristically, it has been determined that when returning from deep + * sleep state the RTCA3_RSR.AF is zero even though the alarm expired. + * Call again the rtc_update_irq() if alarm helper detects this. + */ + + guard(spinlock_irqsave)(&priv->lock); + + pending = rtca3_alarm_handler_helper(priv); + if (!pending) + rtc_update_irq(priv->rtc_dev, 1, RTC_AF | RTC_IRQF); + + return 0; +} + +static int rtca3_resume(struct device *dev) +{ + struct rtca3_priv *priv = dev_get_drvdata(dev); + + if (!device_may_wakeup(dev)) + return 0; + + disable_irq_wake(priv->wakeup_irq); + + /* + * According to the HW manual (section 22.6.4 Notes on writing to + * and reading from registers) we need to wait 1/128 seconds while + * RCR2.START = 1 to be able to read the counters after a return from low + * power consumption state. + */ + mdelay(8); + + /* + * The alarm cannot wake the system from deep sleep states. In case + * we return from deep sleep states and the alarm expired we need + * to disable it to avoid failures when setting another alarm. + */ + return rtca3_clean_alarm(priv); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(rtca3_pm_ops, rtca3_suspend, rtca3_resume); + +static const struct of_device_id rtca3_of_match[] = { + { .compatible = "renesas,rz-rtca3", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rtca3_of_match); + +static struct platform_driver rtca3_platform_driver = { + .driver = { + .name = "rtc-rtca3", + .pm = pm_ptr(&rtca3_pm_ops), + .of_match_table = rtca3_of_match, + }, + .probe = rtca3_probe, + .remove = rtca3_remove, +}; +module_platform_driver(rtca3_platform_driver); + +MODULE_DESCRIPTION("Renesas RTCA-3 RTC driver"); +MODULE_AUTHOR("Claudiu Beznea "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-rk808.c b/drivers/rtc/rtc-rk808.c new file mode 100644 index 000000000..59b8e9a30 --- /dev/null +++ b/drivers/rtc/rtc-rk808.c @@ -0,0 +1,460 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * RTC driver for Rockchip RK808 + * + * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd + * + * Author: Chris Zhong + * Author: Zhang Qing + */ + +#include +#include +#include +#include +#include +#include + +/* RTC_CTRL_REG bitfields */ +#define BIT_RTC_CTRL_REG_STOP_RTC_M BIT(0) + +/* RK808 has a shadowed register for saving a "frozen" RTC time. + * When user setting "GET_TIME" to 1, the time will save in this shadowed + * register. If set "READSEL" to 1, user read rtc time register, actually + * get the time of that moment. If we need the real time, clr this bit. + */ +#define BIT_RTC_CTRL_REG_RTC_GET_TIME BIT(6) +#define BIT_RTC_CTRL_REG_RTC_READSEL_M BIT(7) +#define BIT_RTC_INTERRUPTS_REG_IT_ALARM_M BIT(3) +#define RTC_STATUS_MASK 0xFE + +#define SECONDS_REG_MSK 0x7F +#define MINUTES_REG_MAK 0x7F +#define HOURS_REG_MSK 0x3F +#define DAYS_REG_MSK 0x3F +#define MONTHS_REG_MSK 0x1F +#define YEARS_REG_MSK 0xFF +#define WEEKS_REG_MSK 0x7 + +/* REG_SECONDS_REG through REG_YEARS_REG is how many registers? */ + +#define NUM_TIME_REGS (RK808_WEEKS_REG - RK808_SECONDS_REG + 1) +#define NUM_ALARM_REGS (RK808_ALARM_YEARS_REG - RK808_ALARM_SECONDS_REG + 1) + +struct rk_rtc_compat_reg { + unsigned int ctrl_reg; + unsigned int status_reg; + unsigned int alarm_seconds_reg; + unsigned int int_reg; + unsigned int seconds_reg; +}; + +struct rk808_rtc { + struct regmap *regmap; + struct rtc_device *rtc; + struct rk_rtc_compat_reg *creg; + int irq; +}; + +/* + * The Rockchip calendar used by the RK808 counts November with 31 days. We use + * these translation functions to convert its dates to/from the Gregorian + * calendar used by the rest of the world. We arbitrarily define Jan 1st, 2016 + * as the day when both calendars were in sync, and treat all other dates + * relative to that. + * NOTE: Other system software (e.g. firmware) that reads the same hardware must + * implement this exact same conversion algorithm, with the same anchor date. + */ +static time64_t nov2dec_transitions(struct rtc_time *tm) +{ + return (tm->tm_year + 1900) - 2016 + (tm->tm_mon + 1 > 11 ? 1 : 0); +} + +static void rockchip_to_gregorian(struct rtc_time *tm) +{ + /* If it's Nov 31st, rtc_tm_to_time64() will count that like Dec 1st */ + time64_t time = rtc_tm_to_time64(tm); + rtc_time64_to_tm(time + nov2dec_transitions(tm) * 86400, tm); +} + +static void gregorian_to_rockchip(struct rtc_time *tm) +{ + time64_t extra_days = nov2dec_transitions(tm); + time64_t time = rtc_tm_to_time64(tm); + rtc_time64_to_tm(time - extra_days * 86400, tm); + + /* Compensate if we went back over Nov 31st (will work up to 2381) */ + if (nov2dec_transitions(tm) < extra_days) { + if (tm->tm_mon + 1 == 11) + tm->tm_mday++; /* This may result in 31! */ + else + rtc_time64_to_tm(time - (extra_days - 1) * 86400, tm); + } +} + +/* Read current time and date in RTC */ +static int rk808_rtc_readtime(struct device *dev, struct rtc_time *tm) +{ + struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev); + u8 rtc_data[NUM_TIME_REGS]; + int ret; + + /* Force an update of the shadowed registers right now */ + ret = regmap_update_bits(rk808_rtc->regmap, rk808_rtc->creg->ctrl_reg, + BIT_RTC_CTRL_REG_RTC_GET_TIME, + BIT_RTC_CTRL_REG_RTC_GET_TIME); + if (ret) { + dev_err(dev, "Failed to update bits rtc_ctrl: %d\n", ret); + return ret; + } + + /* + * After we set the GET_TIME bit, the rtc time can't be read + * immediately. So we should wait up to 31.25 us, about one cycle of + * 32khz. If we clear the GET_TIME bit here, the time of i2c transfer + * certainly more than 31.25us: 16 * 2.5us at 400kHz bus frequency. + */ + ret = regmap_update_bits(rk808_rtc->regmap, rk808_rtc->creg->ctrl_reg, + BIT_RTC_CTRL_REG_RTC_GET_TIME, + 0); + if (ret) { + dev_err(dev, "Failed to update bits rtc_ctrl: %d\n", ret); + return ret; + } + + ret = regmap_bulk_read(rk808_rtc->regmap, rk808_rtc->creg->seconds_reg, + rtc_data, NUM_TIME_REGS); + if (ret) { + dev_err(dev, "Failed to bulk read rtc_data: %d\n", ret); + return ret; + } + + tm->tm_sec = bcd2bin(rtc_data[0] & SECONDS_REG_MSK); + tm->tm_min = bcd2bin(rtc_data[1] & MINUTES_REG_MAK); + tm->tm_hour = bcd2bin(rtc_data[2] & HOURS_REG_MSK); + tm->tm_mday = bcd2bin(rtc_data[3] & DAYS_REG_MSK); + tm->tm_mon = (bcd2bin(rtc_data[4] & MONTHS_REG_MSK)) - 1; + tm->tm_year = (bcd2bin(rtc_data[5] & YEARS_REG_MSK)) + 100; + tm->tm_wday = bcd2bin(rtc_data[6] & WEEKS_REG_MSK); + rockchip_to_gregorian(tm); + dev_dbg(dev, "RTC date/time %ptRd(%d) %ptRt\n", tm, tm->tm_wday, tm); + + return ret; +} + +/* Set current time and date in RTC */ +static int rk808_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev); + u8 rtc_data[NUM_TIME_REGS]; + int ret; + + dev_dbg(dev, "set RTC date/time %ptRd(%d) %ptRt\n", tm, tm->tm_wday, tm); + gregorian_to_rockchip(tm); + rtc_data[0] = bin2bcd(tm->tm_sec); + rtc_data[1] = bin2bcd(tm->tm_min); + rtc_data[2] = bin2bcd(tm->tm_hour); + rtc_data[3] = bin2bcd(tm->tm_mday); + rtc_data[4] = bin2bcd(tm->tm_mon + 1); + rtc_data[5] = bin2bcd(tm->tm_year - 100); + rtc_data[6] = bin2bcd(tm->tm_wday); + + /* Stop RTC while updating the RTC registers */ + ret = regmap_update_bits(rk808_rtc->regmap, rk808_rtc->creg->ctrl_reg, + BIT_RTC_CTRL_REG_STOP_RTC_M, + BIT_RTC_CTRL_REG_STOP_RTC_M); + if (ret) { + dev_err(dev, "Failed to update RTC control: %d\n", ret); + return ret; + } + + ret = regmap_bulk_write(rk808_rtc->regmap, rk808_rtc->creg->seconds_reg, + rtc_data, NUM_TIME_REGS); + if (ret) { + dev_err(dev, "Failed to bull write rtc_data: %d\n", ret); + return ret; + } + /* Start RTC again */ + ret = regmap_update_bits(rk808_rtc->regmap, rk808_rtc->creg->ctrl_reg, + BIT_RTC_CTRL_REG_STOP_RTC_M, 0); + if (ret) { + dev_err(dev, "Failed to update RTC control: %d\n", ret); + return ret; + } + return 0; +} + +/* Read alarm time and date in RTC */ +static int rk808_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev); + u8 alrm_data[NUM_ALARM_REGS]; + uint32_t int_reg; + int ret; + + ret = regmap_bulk_read(rk808_rtc->regmap, + rk808_rtc->creg->alarm_seconds_reg, + alrm_data, NUM_ALARM_REGS); + if (ret) { + dev_err(dev, "Failed to read RTC alarm date REG: %d\n", ret); + return ret; + } + + alrm->time.tm_sec = bcd2bin(alrm_data[0] & SECONDS_REG_MSK); + alrm->time.tm_min = bcd2bin(alrm_data[1] & MINUTES_REG_MAK); + alrm->time.tm_hour = bcd2bin(alrm_data[2] & HOURS_REG_MSK); + alrm->time.tm_mday = bcd2bin(alrm_data[3] & DAYS_REG_MSK); + alrm->time.tm_mon = (bcd2bin(alrm_data[4] & MONTHS_REG_MSK)) - 1; + alrm->time.tm_year = (bcd2bin(alrm_data[5] & YEARS_REG_MSK)) + 100; + rockchip_to_gregorian(&alrm->time); + + ret = regmap_read(rk808_rtc->regmap, rk808_rtc->creg->int_reg, &int_reg); + if (ret) { + dev_err(dev, "Failed to read RTC INT REG: %d\n", ret); + return ret; + } + + dev_dbg(dev, "alrm read RTC date/time %ptRd(%d) %ptRt\n", + &alrm->time, alrm->time.tm_wday, &alrm->time); + + alrm->enabled = (int_reg & BIT_RTC_INTERRUPTS_REG_IT_ALARM_M) ? 1 : 0; + + return 0; +} + +static int rk808_rtc_stop_alarm(struct rk808_rtc *rk808_rtc) +{ + int ret; + + ret = regmap_update_bits(rk808_rtc->regmap, rk808_rtc->creg->int_reg, + BIT_RTC_INTERRUPTS_REG_IT_ALARM_M, 0); + + return ret; +} + +static int rk808_rtc_start_alarm(struct rk808_rtc *rk808_rtc) +{ + int ret; + + ret = regmap_update_bits(rk808_rtc->regmap, rk808_rtc->creg->int_reg, + BIT_RTC_INTERRUPTS_REG_IT_ALARM_M, + BIT_RTC_INTERRUPTS_REG_IT_ALARM_M); + + return ret; +} + +static int rk808_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev); + u8 alrm_data[NUM_ALARM_REGS]; + int ret; + + ret = rk808_rtc_stop_alarm(rk808_rtc); + if (ret) { + dev_err(dev, "Failed to stop alarm: %d\n", ret); + return ret; + } + dev_dbg(dev, "alrm set RTC date/time %ptRd(%d) %ptRt\n", + &alrm->time, alrm->time.tm_wday, &alrm->time); + + gregorian_to_rockchip(&alrm->time); + alrm_data[0] = bin2bcd(alrm->time.tm_sec); + alrm_data[1] = bin2bcd(alrm->time.tm_min); + alrm_data[2] = bin2bcd(alrm->time.tm_hour); + alrm_data[3] = bin2bcd(alrm->time.tm_mday); + alrm_data[4] = bin2bcd(alrm->time.tm_mon + 1); + alrm_data[5] = bin2bcd(alrm->time.tm_year - 100); + + ret = regmap_bulk_write(rk808_rtc->regmap, + rk808_rtc->creg->alarm_seconds_reg, + alrm_data, NUM_ALARM_REGS); + if (ret) { + dev_err(dev, "Failed to bulk write: %d\n", ret); + return ret; + } + if (alrm->enabled) { + ret = rk808_rtc_start_alarm(rk808_rtc); + if (ret) { + dev_err(dev, "Failed to start alarm: %d\n", ret); + return ret; + } + } + return 0; +} + +static int rk808_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev); + + if (enabled) + return rk808_rtc_start_alarm(rk808_rtc); + + return rk808_rtc_stop_alarm(rk808_rtc); +} + +/* + * We will just handle setting the frequency and make use the framework for + * reading the periodic interupts. + * + * @freq: Current periodic IRQ freq: + * bit 0: every second + * bit 1: every minute + * bit 2: every hour + * bit 3: every day + */ +static irqreturn_t rk808_alarm_irq(int irq, void *data) +{ + struct rk808_rtc *rk808_rtc = data; + int ret; + + ret = regmap_write(rk808_rtc->regmap, rk808_rtc->creg->status_reg, + RTC_STATUS_MASK); + if (ret) { + dev_err(&rk808_rtc->rtc->dev, + "%s:Failed to update RTC status: %d\n", __func__, ret); + return ret; + } + + rtc_update_irq(rk808_rtc->rtc, 1, RTC_IRQF | RTC_AF); + dev_dbg(&rk808_rtc->rtc->dev, + "%s:irq=%d\n", __func__, irq); + return IRQ_HANDLED; +} + +static const struct rtc_class_ops rk808_rtc_ops = { + .read_time = rk808_rtc_readtime, + .set_time = rk808_rtc_set_time, + .read_alarm = rk808_rtc_readalarm, + .set_alarm = rk808_rtc_setalarm, + .alarm_irq_enable = rk808_rtc_alarm_irq_enable, +}; + +#ifdef CONFIG_PM_SLEEP +/* Turn off the alarm if it should not be a wake source. */ +static int rk808_rtc_suspend(struct device *dev) +{ + struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(rk808_rtc->irq); + + return 0; +} + +/* Enable the alarm if it should be enabled (in case it was disabled to + * prevent use as a wake source). + */ +static int rk808_rtc_resume(struct device *dev) +{ + struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(rk808_rtc->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(rk808_rtc_pm_ops, + rk808_rtc_suspend, rk808_rtc_resume); + +static struct rk_rtc_compat_reg rk808_creg = { + .ctrl_reg = RK808_RTC_CTRL_REG, + .status_reg = RK808_RTC_STATUS_REG, + .alarm_seconds_reg = RK808_ALARM_SECONDS_REG, + .int_reg = RK808_RTC_INT_REG, + .seconds_reg = RK808_SECONDS_REG, +}; + +static struct rk_rtc_compat_reg rk817_creg = { + .ctrl_reg = RK817_RTC_CTRL_REG, + .status_reg = RK817_RTC_STATUS_REG, + .alarm_seconds_reg = RK817_ALARM_SECONDS_REG, + .int_reg = RK817_RTC_INT_REG, + .seconds_reg = RK817_SECONDS_REG, +}; + +static int rk808_rtc_probe(struct platform_device *pdev) +{ + struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent); + struct rk808_rtc *rk808_rtc; + int ret; + + rk808_rtc = devm_kzalloc(&pdev->dev, sizeof(*rk808_rtc), GFP_KERNEL); + if (rk808_rtc == NULL) + return -ENOMEM; + + switch (rk808->variant) { + case RK809_ID: + case RK817_ID: + rk808_rtc->creg = &rk817_creg; + break; + default: + rk808_rtc->creg = &rk808_creg; + break; + } + platform_set_drvdata(pdev, rk808_rtc); + rk808_rtc->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!rk808_rtc->regmap) + return -ENODEV; + + /* start rtc running by default, and use shadowed timer. */ + ret = regmap_update_bits(rk808_rtc->regmap, rk808_rtc->creg->ctrl_reg, + BIT_RTC_CTRL_REG_STOP_RTC_M | + BIT_RTC_CTRL_REG_RTC_READSEL_M, + BIT_RTC_CTRL_REG_RTC_READSEL_M); + if (ret) { + dev_err(&pdev->dev, + "Failed to update RTC control: %d\n", ret); + return ret; + } + + ret = regmap_write(rk808_rtc->regmap, rk808_rtc->creg->status_reg, + RTC_STATUS_MASK); + if (ret) { + dev_err(&pdev->dev, + "Failed to write RTC status: %d\n", ret); + return ret; + } + + device_init_wakeup(&pdev->dev, true); + + rk808_rtc->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rk808_rtc->rtc)) + return PTR_ERR(rk808_rtc->rtc); + + rk808_rtc->rtc->ops = &rk808_rtc_ops; + + rk808_rtc->irq = platform_get_irq(pdev, 0); + if (rk808_rtc->irq < 0) + return rk808_rtc->irq; + + /* request alarm irq of rk808 */ + ret = devm_request_threaded_irq(&pdev->dev, rk808_rtc->irq, NULL, + rk808_alarm_irq, 0, + "RTC alarm", rk808_rtc); + if (ret) { + dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n", + rk808_rtc->irq, ret); + return ret; + } + + return devm_rtc_register_device(rk808_rtc->rtc); +} + +static struct platform_driver rk808_rtc_driver = { + .probe = rk808_rtc_probe, + .driver = { + .name = "rk808-rtc", + .pm = &rk808_rtc_pm_ops, + }, +}; + +module_platform_driver(rk808_rtc_driver); + +MODULE_DESCRIPTION("RTC driver for the rk808 series PMICs"); +MODULE_AUTHOR("Chris Zhong "); +MODULE_AUTHOR("Zhang Qing "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rk808-rtc"); diff --git a/drivers/rtc/rtc-rp5c01.c b/drivers/rtc/rtc-rp5c01.c new file mode 100644 index 000000000..44afa6d99 --- /dev/null +++ b/drivers/rtc/rtc-rp5c01.c @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Ricoh RP5C01 RTC Driver + * + * Copyright 2009 Geert Uytterhoeven + * + * Based on the A3000 TOD code in arch/m68k/amiga/config.c + * Copyright (C) 1993 Hamish Macdonald + */ + +#include +#include +#include +#include +#include +#include + + +enum { + RP5C01_1_SECOND = 0x0, /* MODE 00 */ + RP5C01_10_SECOND = 0x1, /* MODE 00 */ + RP5C01_1_MINUTE = 0x2, /* MODE 00 and MODE 01 */ + RP5C01_10_MINUTE = 0x3, /* MODE 00 and MODE 01 */ + RP5C01_1_HOUR = 0x4, /* MODE 00 and MODE 01 */ + RP5C01_10_HOUR = 0x5, /* MODE 00 and MODE 01 */ + RP5C01_DAY_OF_WEEK = 0x6, /* MODE 00 and MODE 01 */ + RP5C01_1_DAY = 0x7, /* MODE 00 and MODE 01 */ + RP5C01_10_DAY = 0x8, /* MODE 00 and MODE 01 */ + RP5C01_1_MONTH = 0x9, /* MODE 00 */ + RP5C01_10_MONTH = 0xa, /* MODE 00 */ + RP5C01_1_YEAR = 0xb, /* MODE 00 */ + RP5C01_10_YEAR = 0xc, /* MODE 00 */ + + RP5C01_12_24_SELECT = 0xa, /* MODE 01 */ + RP5C01_LEAP_YEAR = 0xb, /* MODE 01 */ + + RP5C01_MODE = 0xd, /* all modes */ + RP5C01_TEST = 0xe, /* all modes */ + RP5C01_RESET = 0xf, /* all modes */ +}; + +#define RP5C01_12_24_SELECT_12 (0 << 0) +#define RP5C01_12_24_SELECT_24 (1 << 0) + +#define RP5C01_10_HOUR_AM (0 << 1) +#define RP5C01_10_HOUR_PM (1 << 1) + +#define RP5C01_MODE_TIMER_EN (1 << 3) /* timer enable */ +#define RP5C01_MODE_ALARM_EN (1 << 2) /* alarm enable */ + +#define RP5C01_MODE_MODE_MASK (3 << 0) +#define RP5C01_MODE_MODE00 (0 << 0) /* time */ +#define RP5C01_MODE_MODE01 (1 << 0) /* alarm, 12h/24h, leap year */ +#define RP5C01_MODE_RAM_BLOCK10 (2 << 0) /* RAM 4 bits x 13 */ +#define RP5C01_MODE_RAM_BLOCK11 (3 << 0) /* RAM 4 bits x 13 */ + +#define RP5C01_RESET_1HZ_PULSE (1 << 3) +#define RP5C01_RESET_16HZ_PULSE (1 << 2) +#define RP5C01_RESET_SECOND (1 << 1) /* reset divider stages for */ + /* seconds or smaller units */ +#define RP5C01_RESET_ALARM (1 << 0) /* reset all alarm registers */ + + +struct rp5c01_priv { + u32 __iomem *regs; + struct rtc_device *rtc; + spinlock_t lock; /* against concurrent RTC/NVRAM access */ +}; + +static inline unsigned int rp5c01_read(struct rp5c01_priv *priv, + unsigned int reg) +{ + return __raw_readl(&priv->regs[reg]) & 0xf; +} + +static inline void rp5c01_write(struct rp5c01_priv *priv, unsigned int val, + unsigned int reg) +{ + __raw_writel(val, &priv->regs[reg]); +} + +static void rp5c01_lock(struct rp5c01_priv *priv) +{ + rp5c01_write(priv, RP5C01_MODE_MODE00, RP5C01_MODE); +} + +static void rp5c01_unlock(struct rp5c01_priv *priv) +{ + rp5c01_write(priv, RP5C01_MODE_TIMER_EN | RP5C01_MODE_MODE01, + RP5C01_MODE); +} + +static int rp5c01_read_time(struct device *dev, struct rtc_time *tm) +{ + struct rp5c01_priv *priv = dev_get_drvdata(dev); + + spin_lock_irq(&priv->lock); + rp5c01_lock(priv); + + tm->tm_sec = rp5c01_read(priv, RP5C01_10_SECOND) * 10 + + rp5c01_read(priv, RP5C01_1_SECOND); + tm->tm_min = rp5c01_read(priv, RP5C01_10_MINUTE) * 10 + + rp5c01_read(priv, RP5C01_1_MINUTE); + tm->tm_hour = rp5c01_read(priv, RP5C01_10_HOUR) * 10 + + rp5c01_read(priv, RP5C01_1_HOUR); + tm->tm_mday = rp5c01_read(priv, RP5C01_10_DAY) * 10 + + rp5c01_read(priv, RP5C01_1_DAY); + tm->tm_wday = rp5c01_read(priv, RP5C01_DAY_OF_WEEK); + tm->tm_mon = rp5c01_read(priv, RP5C01_10_MONTH) * 10 + + rp5c01_read(priv, RP5C01_1_MONTH) - 1; + tm->tm_year = rp5c01_read(priv, RP5C01_10_YEAR) * 10 + + rp5c01_read(priv, RP5C01_1_YEAR); + if (tm->tm_year <= 69) + tm->tm_year += 100; + + rp5c01_unlock(priv); + spin_unlock_irq(&priv->lock); + + return 0; +} + +static int rp5c01_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rp5c01_priv *priv = dev_get_drvdata(dev); + + spin_lock_irq(&priv->lock); + rp5c01_lock(priv); + + rp5c01_write(priv, tm->tm_sec / 10, RP5C01_10_SECOND); + rp5c01_write(priv, tm->tm_sec % 10, RP5C01_1_SECOND); + rp5c01_write(priv, tm->tm_min / 10, RP5C01_10_MINUTE); + rp5c01_write(priv, tm->tm_min % 10, RP5C01_1_MINUTE); + rp5c01_write(priv, tm->tm_hour / 10, RP5C01_10_HOUR); + rp5c01_write(priv, tm->tm_hour % 10, RP5C01_1_HOUR); + rp5c01_write(priv, tm->tm_mday / 10, RP5C01_10_DAY); + rp5c01_write(priv, tm->tm_mday % 10, RP5C01_1_DAY); + if (tm->tm_wday != -1) + rp5c01_write(priv, tm->tm_wday, RP5C01_DAY_OF_WEEK); + rp5c01_write(priv, (tm->tm_mon + 1) / 10, RP5C01_10_MONTH); + rp5c01_write(priv, (tm->tm_mon + 1) % 10, RP5C01_1_MONTH); + if (tm->tm_year >= 100) + tm->tm_year -= 100; + rp5c01_write(priv, tm->tm_year / 10, RP5C01_10_YEAR); + rp5c01_write(priv, tm->tm_year % 10, RP5C01_1_YEAR); + + rp5c01_unlock(priv); + spin_unlock_irq(&priv->lock); + return 0; +} + +static const struct rtc_class_ops rp5c01_rtc_ops = { + .read_time = rp5c01_read_time, + .set_time = rp5c01_set_time, +}; + + +/* + * The NVRAM is organized as 2 blocks of 13 nibbles of 4 bits. + * We provide access to them like AmigaOS does: the high nibble of each 8-bit + * byte is stored in BLOCK10, the low nibble in BLOCK11. + */ + +static int rp5c01_nvram_read(void *_priv, unsigned int pos, void *val, + size_t bytes) +{ + struct rp5c01_priv *priv = _priv; + u8 *buf = val; + + spin_lock_irq(&priv->lock); + + for (; bytes; bytes--) { + u8 data; + + rp5c01_write(priv, + RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK10, + RP5C01_MODE); + data = rp5c01_read(priv, pos) << 4; + rp5c01_write(priv, + RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK11, + RP5C01_MODE); + data |= rp5c01_read(priv, pos++); + rp5c01_write(priv, RP5C01_MODE_TIMER_EN | RP5C01_MODE_MODE01, + RP5C01_MODE); + *buf++ = data; + } + + spin_unlock_irq(&priv->lock); + return 0; +} + +static int rp5c01_nvram_write(void *_priv, unsigned int pos, void *val, + size_t bytes) +{ + struct rp5c01_priv *priv = _priv; + u8 *buf = val; + + spin_lock_irq(&priv->lock); + + for (; bytes; bytes--) { + u8 data = *buf++; + + rp5c01_write(priv, + RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK10, + RP5C01_MODE); + rp5c01_write(priv, data >> 4, pos); + rp5c01_write(priv, + RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK11, + RP5C01_MODE); + rp5c01_write(priv, data & 0xf, pos++); + rp5c01_write(priv, RP5C01_MODE_TIMER_EN | RP5C01_MODE_MODE01, + RP5C01_MODE); + } + + spin_unlock_irq(&priv->lock); + return 0; +} + +static int __init rp5c01_rtc_probe(struct platform_device *dev) +{ + struct resource *res; + struct rp5c01_priv *priv; + struct rtc_device *rtc; + int error; + struct nvmem_config nvmem_cfg = { + .name = "rp5c01_nvram", + .word_size = 1, + .stride = 1, + .size = RP5C01_MODE, + .reg_read = rp5c01_nvram_read, + .reg_write = rp5c01_nvram_write, + }; + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->regs = devm_ioremap(&dev->dev, res->start, resource_size(res)); + if (!priv->regs) + return -ENOMEM; + + spin_lock_init(&priv->lock); + + platform_set_drvdata(dev, priv); + + rtc = devm_rtc_allocate_device(&dev->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + rtc->ops = &rp5c01_rtc_ops; + + priv->rtc = rtc; + + nvmem_cfg.priv = priv; + error = devm_rtc_nvmem_register(rtc, &nvmem_cfg); + if (error) + return error; + + return devm_rtc_register_device(rtc); +} + +static struct platform_driver rp5c01_rtc_driver = { + .driver = { + .name = "rtc-rp5c01", + }, +}; + +module_platform_driver_probe(rp5c01_rtc_driver, rp5c01_rtc_probe); + +MODULE_AUTHOR("Geert Uytterhoeven "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Ricoh RP5C01 RTC driver"); +MODULE_ALIAS("platform:rtc-rp5c01"); diff --git a/drivers/rtc/rtc-rs5c313.c b/drivers/rtc/rtc-rs5c313.c new file mode 100644 index 000000000..712a08e9e --- /dev/null +++ b/drivers/rtc/rtc-rs5c313.c @@ -0,0 +1,392 @@ +/* + * Ricoh RS5C313 RTC device/driver + * Copyright (C) 2007 Nobuhiro Iwamatsu + * + * 2005-09-19 modified by kogiidena + * + * Based on the old drivers/char/rs5c313_rtc.c by: + * Copyright (C) 2000 Philipp Rumpf + * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka + * + * Based on code written by Paul Gortmaker. + * Copyright (C) 1996 Paul Gortmaker + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Based on other minimal char device drivers, like Alan's + * watchdog, Ted's random, etc. etc. + * + * 1.07 Paul Gortmaker. + * 1.08 Miquel van Smoorenburg: disallow certain things on the + * DEC Alpha as the CMOS clock is also used for other things. + * 1.09 Nikita Schmidt: epoch support and some Alpha cleanup. + * 1.09a Pete Zaitcev: Sun SPARC + * 1.09b Jeff Garzik: Modularize, init cleanup + * 1.09c Jeff Garzik: SMP cleanup + * 1.10 Paul Barton-Davis: add support for async I/O + * 1.10a Andrea Arcangeli: Alpha updates + * 1.10b Andrew Morton: SMP lock fix + * 1.10c Cesar Barros: SMP locking fixes and cleanup + * 1.10d Paul Gortmaker: delete paranoia check in rtc_exit + * 1.10e Maciej W. Rozycki: Handle DECstation's year weirdness. + * 1.11 Takashi Iwai: Kernel access functions + * rtc_register/rtc_unregister/rtc_control + * 1.11a Daniele Bellucci: Audit create_proc_read_entry in rtc_init + * 1.12 Venkatesh Pallipadi: Hooks for emulating rtc on HPET base-timer + * CONFIG_HPET_EMULATE_RTC + * 1.13 Nobuhiro Iwamatsu: Update driver. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "rs5c313" + +#ifdef CONFIG_SH_LANDISK +/*****************************************************/ +/* LANDISK dependence part of RS5C313 */ +/*****************************************************/ + +#define SCSMR1 0xFFE00000 +#define SCSCR1 0xFFE00008 +#define SCSMR1_CA 0x80 +#define SCSCR1_CKE 0x03 +#define SCSPTR1 0xFFE0001C +#define SCSPTR1_EIO 0x80 +#define SCSPTR1_SPB1IO 0x08 +#define SCSPTR1_SPB1DT 0x04 +#define SCSPTR1_SPB0IO 0x02 +#define SCSPTR1_SPB0DT 0x01 + +#define SDA_OEN SCSPTR1_SPB1IO +#define SDA SCSPTR1_SPB1DT +#define SCL_OEN SCSPTR1_SPB0IO +#define SCL SCSPTR1_SPB0DT + +/* RICOH RS5C313 CE port */ +#define RS5C313_CE 0xB0000003 + +/* RICOH RS5C313 CE port bit */ +#define RS5C313_CE_RTCCE 0x02 + +/* SCSPTR1 data */ +unsigned char scsptr1_data; + +#define RS5C313_CEENABLE __raw_writeb(RS5C313_CE_RTCCE, RS5C313_CE); +#define RS5C313_CEDISABLE __raw_writeb(0x00, RS5C313_CE) +#define RS5C313_MISCOP __raw_writeb(0x02, 0xB0000008) + +static void rs5c313_init_port(void) +{ + /* Set SCK as I/O port and Initialize SCSPTR1 data & I/O port. */ + __raw_writeb(__raw_readb(SCSMR1) & ~SCSMR1_CA, SCSMR1); + __raw_writeb(__raw_readb(SCSCR1) & ~SCSCR1_CKE, SCSCR1); + + /* And Initialize SCL for RS5C313 clock */ + scsptr1_data = __raw_readb(SCSPTR1) | SCL; /* SCL:H */ + __raw_writeb(scsptr1_data, SCSPTR1); + scsptr1_data = __raw_readb(SCSPTR1) | SCL_OEN; /* SCL output enable */ + __raw_writeb(scsptr1_data, SCSPTR1); + RS5C313_CEDISABLE; /* CE:L */ +} + +static void rs5c313_write_data(unsigned char data) +{ + int i; + + for (i = 0; i < 8; i++) { + /* SDA:Write Data */ + scsptr1_data = (scsptr1_data & ~SDA) | + ((((0x80 >> i) & data) >> (7 - i)) << 2); + __raw_writeb(scsptr1_data, SCSPTR1); + if (i == 0) { + scsptr1_data |= SDA_OEN; /* SDA:output enable */ + __raw_writeb(scsptr1_data, SCSPTR1); + } + ndelay(700); + scsptr1_data &= ~SCL; /* SCL:L */ + __raw_writeb(scsptr1_data, SCSPTR1); + ndelay(700); + scsptr1_data |= SCL; /* SCL:H */ + __raw_writeb(scsptr1_data, SCSPTR1); + } + + scsptr1_data &= ~SDA_OEN; /* SDA:output disable */ + __raw_writeb(scsptr1_data, SCSPTR1); +} + +static unsigned char rs5c313_read_data(void) +{ + int i; + unsigned char data = 0; + + for (i = 0; i < 8; i++) { + ndelay(700); + /* SDA:Read Data */ + data |= ((__raw_readb(SCSPTR1) & SDA) >> 2) << (7 - i); + scsptr1_data &= ~SCL; /* SCL:L */ + __raw_writeb(scsptr1_data, SCSPTR1); + ndelay(700); + scsptr1_data |= SCL; /* SCL:H */ + __raw_writeb(scsptr1_data, SCSPTR1); + } + return data & 0x0F; +} + +#endif /* CONFIG_SH_LANDISK */ + +/*****************************************************/ +/* machine independence part of RS5C313 */ +/*****************************************************/ + +/* RICOH RS5C313 address */ +#define RS5C313_ADDR_SEC 0x00 +#define RS5C313_ADDR_SEC10 0x01 +#define RS5C313_ADDR_MIN 0x02 +#define RS5C313_ADDR_MIN10 0x03 +#define RS5C313_ADDR_HOUR 0x04 +#define RS5C313_ADDR_HOUR10 0x05 +#define RS5C313_ADDR_WEEK 0x06 +#define RS5C313_ADDR_INTINTVREG 0x07 +#define RS5C313_ADDR_DAY 0x08 +#define RS5C313_ADDR_DAY10 0x09 +#define RS5C313_ADDR_MON 0x0A +#define RS5C313_ADDR_MON10 0x0B +#define RS5C313_ADDR_YEAR 0x0C +#define RS5C313_ADDR_YEAR10 0x0D +#define RS5C313_ADDR_CNTREG 0x0E +#define RS5C313_ADDR_TESTREG 0x0F + +/* RICOH RS5C313 control register */ +#define RS5C313_CNTREG_ADJ_BSY 0x01 +#define RS5C313_CNTREG_WTEN_XSTP 0x02 +#define RS5C313_CNTREG_12_24 0x04 +#define RS5C313_CNTREG_CTFG 0x08 + +/* RICOH RS5C313 test register */ +#define RS5C313_TESTREG_TEST 0x01 + +/* RICOH RS5C313 control bit */ +#define RS5C313_CNTBIT_READ 0x40 +#define RS5C313_CNTBIT_AD 0x20 +#define RS5C313_CNTBIT_DT 0x10 + +static unsigned char rs5c313_read_reg(unsigned char addr) +{ + + rs5c313_write_data(addr | RS5C313_CNTBIT_READ | RS5C313_CNTBIT_AD); + return rs5c313_read_data(); +} + +static void rs5c313_write_reg(unsigned char addr, unsigned char data) +{ + data &= 0x0f; + rs5c313_write_data(addr | RS5C313_CNTBIT_AD); + rs5c313_write_data(data | RS5C313_CNTBIT_DT); + return; +} + +static inline unsigned char rs5c313_read_cntreg(void) +{ + return rs5c313_read_reg(RS5C313_ADDR_CNTREG); +} + +static inline void rs5c313_write_cntreg(unsigned char data) +{ + rs5c313_write_reg(RS5C313_ADDR_CNTREG, data); +} + +static inline void rs5c313_write_intintvreg(unsigned char data) +{ + rs5c313_write_reg(RS5C313_ADDR_INTINTVREG, data); +} + +static int rs5c313_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + int data; + int cnt; + + cnt = 0; + while (1) { + RS5C313_CEENABLE; /* CE:H */ + + /* Initialize control reg. 24 hour */ + rs5c313_write_cntreg(0x04); + + if (!(rs5c313_read_cntreg() & RS5C313_CNTREG_ADJ_BSY)) + break; + + RS5C313_CEDISABLE; + ndelay(700); /* CE:L */ + + if (cnt++ > 100) { + dev_err(dev, "%s: timeout error\n", __func__); + return -EIO; + } + } + + data = rs5c313_read_reg(RS5C313_ADDR_SEC); + data |= (rs5c313_read_reg(RS5C313_ADDR_SEC10) << 4); + tm->tm_sec = bcd2bin(data); + + data = rs5c313_read_reg(RS5C313_ADDR_MIN); + data |= (rs5c313_read_reg(RS5C313_ADDR_MIN10) << 4); + tm->tm_min = bcd2bin(data); + + data = rs5c313_read_reg(RS5C313_ADDR_HOUR); + data |= (rs5c313_read_reg(RS5C313_ADDR_HOUR10) << 4); + tm->tm_hour = bcd2bin(data); + + data = rs5c313_read_reg(RS5C313_ADDR_DAY); + data |= (rs5c313_read_reg(RS5C313_ADDR_DAY10) << 4); + tm->tm_mday = bcd2bin(data); + + data = rs5c313_read_reg(RS5C313_ADDR_MON); + data |= (rs5c313_read_reg(RS5C313_ADDR_MON10) << 4); + tm->tm_mon = bcd2bin(data) - 1; + + data = rs5c313_read_reg(RS5C313_ADDR_YEAR); + data |= (rs5c313_read_reg(RS5C313_ADDR_YEAR10) << 4); + tm->tm_year = bcd2bin(data); + + if (tm->tm_year < 70) + tm->tm_year += 100; + + data = rs5c313_read_reg(RS5C313_ADDR_WEEK); + tm->tm_wday = bcd2bin(data); + + RS5C313_CEDISABLE; + ndelay(700); /* CE:L */ + + return 0; +} + +static int rs5c313_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + int data; + int cnt; + + cnt = 0; + /* busy check. */ + while (1) { + RS5C313_CEENABLE; /* CE:H */ + + /* Initialize control reg. 24 hour */ + rs5c313_write_cntreg(0x04); + + if (!(rs5c313_read_cntreg() & RS5C313_CNTREG_ADJ_BSY)) + break; + RS5C313_MISCOP; + RS5C313_CEDISABLE; + ndelay(700); /* CE:L */ + + if (cnt++ > 100) { + dev_err(dev, "%s: timeout error\n", __func__); + return -EIO; + } + } + + data = bin2bcd(tm->tm_sec); + rs5c313_write_reg(RS5C313_ADDR_SEC, data); + rs5c313_write_reg(RS5C313_ADDR_SEC10, (data >> 4)); + + data = bin2bcd(tm->tm_min); + rs5c313_write_reg(RS5C313_ADDR_MIN, data); + rs5c313_write_reg(RS5C313_ADDR_MIN10, (data >> 4)); + + data = bin2bcd(tm->tm_hour); + rs5c313_write_reg(RS5C313_ADDR_HOUR, data); + rs5c313_write_reg(RS5C313_ADDR_HOUR10, (data >> 4)); + + data = bin2bcd(tm->tm_mday); + rs5c313_write_reg(RS5C313_ADDR_DAY, data); + rs5c313_write_reg(RS5C313_ADDR_DAY10, (data >> 4)); + + data = bin2bcd(tm->tm_mon + 1); + rs5c313_write_reg(RS5C313_ADDR_MON, data); + rs5c313_write_reg(RS5C313_ADDR_MON10, (data >> 4)); + + data = bin2bcd(tm->tm_year % 100); + rs5c313_write_reg(RS5C313_ADDR_YEAR, data); + rs5c313_write_reg(RS5C313_ADDR_YEAR10, (data >> 4)); + + data = bin2bcd(tm->tm_wday); + rs5c313_write_reg(RS5C313_ADDR_WEEK, data); + + RS5C313_CEDISABLE; /* CE:H */ + ndelay(700); + + return 0; +} + +static void rs5c313_check_xstp_bit(void) +{ + struct rtc_time tm; + int cnt; + + RS5C313_CEENABLE; /* CE:H */ + if (rs5c313_read_cntreg() & RS5C313_CNTREG_WTEN_XSTP) { + /* INT interval reg. OFF */ + rs5c313_write_intintvreg(0x00); + /* Initialize control reg. 24 hour & adjust */ + rs5c313_write_cntreg(0x07); + + /* busy check. */ + for (cnt = 0; cnt < 100; cnt++) { + if (!(rs5c313_read_cntreg() & RS5C313_CNTREG_ADJ_BSY)) + break; + RS5C313_MISCOP; + } + + memset(&tm, 0, sizeof(struct rtc_time)); + tm.tm_mday = 1; + tm.tm_mon = 1 - 1; + tm.tm_year = 2000 - 1900; + + rs5c313_rtc_set_time(NULL, &tm); + pr_err("invalid value, resetting to 1 Jan 2000\n"); + } + RS5C313_CEDISABLE; + ndelay(700); /* CE:L */ +} + +static const struct rtc_class_ops rs5c313_rtc_ops = { + .read_time = rs5c313_rtc_read_time, + .set_time = rs5c313_rtc_set_time, +}; + +static int rs5c313_rtc_probe(struct platform_device *pdev) +{ + struct rtc_device *rtc; + + rs5c313_init_port(); + rs5c313_check_xstp_bit(); + + rtc = devm_rtc_device_register(&pdev->dev, "rs5c313", &rs5c313_rtc_ops, + THIS_MODULE); + + return PTR_ERR_OR_ZERO(rtc); +} + +static struct platform_driver rs5c313_rtc_platform_driver = { + .driver = { + .name = DRV_NAME, + }, + .probe = rs5c313_rtc_probe, +}; + +module_platform_driver(rs5c313_rtc_platform_driver); + +MODULE_AUTHOR("kogiidena , Nobuhiro Iwamatsu "); +MODULE_DESCRIPTION("Ricoh RS5C313 RTC device driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/rtc/rtc-rs5c348.c b/drivers/rtc/rtc-rs5c348.c new file mode 100644 index 000000000..fec633f80 --- /dev/null +++ b/drivers/rtc/rtc-rs5c348.c @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * A SPI driver for the Ricoh RS5C348 RTC + * + * Copyright (C) 2006 Atsushi Nemoto + * + * The board specific init code should provide characteristics of this + * device: + * Mode 1 (High-Active, Shift-Then-Sample), High Avtive CS + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RS5C348_REG_SECS 0 +#define RS5C348_REG_MINS 1 +#define RS5C348_REG_HOURS 2 +#define RS5C348_REG_WDAY 3 +#define RS5C348_REG_DAY 4 +#define RS5C348_REG_MONTH 5 +#define RS5C348_REG_YEAR 6 +#define RS5C348_REG_CTL1 14 +#define RS5C348_REG_CTL2 15 + +#define RS5C348_SECS_MASK 0x7f +#define RS5C348_MINS_MASK 0x7f +#define RS5C348_HOURS_MASK 0x3f +#define RS5C348_WDAY_MASK 0x03 +#define RS5C348_DAY_MASK 0x3f +#define RS5C348_MONTH_MASK 0x1f + +#define RS5C348_BIT_PM 0x20 /* REG_HOURS */ +#define RS5C348_BIT_Y2K 0x80 /* REG_MONTH */ +#define RS5C348_BIT_24H 0x20 /* REG_CTL1 */ +#define RS5C348_BIT_XSTP 0x10 /* REG_CTL2 */ +#define RS5C348_BIT_VDET 0x40 /* REG_CTL2 */ + +#define RS5C348_CMD_W(addr) (((addr) << 4) | 0x08) /* single write */ +#define RS5C348_CMD_R(addr) (((addr) << 4) | 0x0c) /* single read */ +#define RS5C348_CMD_MW(addr) (((addr) << 4) | 0x00) /* burst write */ +#define RS5C348_CMD_MR(addr) (((addr) << 4) | 0x04) /* burst read */ + +struct rs5c348_plat_data { + struct rtc_device *rtc; + int rtc_24h; +}; + +static int +rs5c348_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct spi_device *spi = to_spi_device(dev); + struct rs5c348_plat_data *pdata = dev_get_platdata(&spi->dev); + u8 txbuf[5+7], *txp; + int ret; + + ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_CTL2)); + if (ret < 0) + return ret; + if (ret & RS5C348_BIT_XSTP) { + txbuf[0] = RS5C348_CMD_W(RS5C348_REG_CTL2); + txbuf[1] = 0; + ret = spi_write_then_read(spi, txbuf, 2, NULL, 0); + if (ret < 0) + return ret; + } + + /* Transfer 5 bytes before writing SEC. This gives 31us for carry. */ + txp = txbuf; + txbuf[0] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */ + txbuf[1] = 0; /* dummy */ + txbuf[2] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */ + txbuf[3] = 0; /* dummy */ + txbuf[4] = RS5C348_CMD_MW(RS5C348_REG_SECS); /* cmd, sec, ... */ + txp = &txbuf[5]; + txp[RS5C348_REG_SECS] = bin2bcd(tm->tm_sec); + txp[RS5C348_REG_MINS] = bin2bcd(tm->tm_min); + if (pdata->rtc_24h) { + txp[RS5C348_REG_HOURS] = bin2bcd(tm->tm_hour); + } else { + /* hour 0 is AM12, noon is PM12 */ + txp[RS5C348_REG_HOURS] = bin2bcd((tm->tm_hour + 11) % 12 + 1) | + (tm->tm_hour >= 12 ? RS5C348_BIT_PM : 0); + } + txp[RS5C348_REG_WDAY] = bin2bcd(tm->tm_wday); + txp[RS5C348_REG_DAY] = bin2bcd(tm->tm_mday); + txp[RS5C348_REG_MONTH] = bin2bcd(tm->tm_mon + 1) | + (tm->tm_year >= 100 ? RS5C348_BIT_Y2K : 0); + txp[RS5C348_REG_YEAR] = bin2bcd(tm->tm_year % 100); + /* write in one transfer to avoid data inconsistency */ + ret = spi_write_then_read(spi, txbuf, sizeof(txbuf), NULL, 0); + udelay(62); /* Tcsr 62us */ + return ret; +} + +static int +rs5c348_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct spi_device *spi = to_spi_device(dev); + struct rs5c348_plat_data *pdata = dev_get_platdata(&spi->dev); + u8 txbuf[5], rxbuf[7]; + int ret; + + ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_CTL2)); + if (ret < 0) + return ret; + if (ret & RS5C348_BIT_VDET) + dev_warn(&spi->dev, "voltage-low detected.\n"); + if (ret & RS5C348_BIT_XSTP) { + dev_warn(&spi->dev, "oscillator-stop detected.\n"); + return -EINVAL; + } + + /* Transfer 5 byte befores reading SEC. This gives 31us for carry. */ + txbuf[0] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */ + txbuf[1] = 0; /* dummy */ + txbuf[2] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */ + txbuf[3] = 0; /* dummy */ + txbuf[4] = RS5C348_CMD_MR(RS5C348_REG_SECS); /* cmd, sec, ... */ + + /* read in one transfer to avoid data inconsistency */ + ret = spi_write_then_read(spi, txbuf, sizeof(txbuf), + rxbuf, sizeof(rxbuf)); + udelay(62); /* Tcsr 62us */ + if (ret < 0) + return ret; + + tm->tm_sec = bcd2bin(rxbuf[RS5C348_REG_SECS] & RS5C348_SECS_MASK); + tm->tm_min = bcd2bin(rxbuf[RS5C348_REG_MINS] & RS5C348_MINS_MASK); + tm->tm_hour = bcd2bin(rxbuf[RS5C348_REG_HOURS] & RS5C348_HOURS_MASK); + if (!pdata->rtc_24h) { + if (rxbuf[RS5C348_REG_HOURS] & RS5C348_BIT_PM) { + tm->tm_hour -= 20; + tm->tm_hour %= 12; + tm->tm_hour += 12; + } else + tm->tm_hour %= 12; + } + tm->tm_wday = bcd2bin(rxbuf[RS5C348_REG_WDAY] & RS5C348_WDAY_MASK); + tm->tm_mday = bcd2bin(rxbuf[RS5C348_REG_DAY] & RS5C348_DAY_MASK); + tm->tm_mon = + bcd2bin(rxbuf[RS5C348_REG_MONTH] & RS5C348_MONTH_MASK) - 1; + /* year is 1900 + tm->tm_year */ + tm->tm_year = bcd2bin(rxbuf[RS5C348_REG_YEAR]) + + ((rxbuf[RS5C348_REG_MONTH] & RS5C348_BIT_Y2K) ? 100 : 0); + + return 0; +} + +static const struct rtc_class_ops rs5c348_rtc_ops = { + .read_time = rs5c348_rtc_read_time, + .set_time = rs5c348_rtc_set_time, +}; + +static int rs5c348_probe(struct spi_device *spi) +{ + int ret; + struct rtc_device *rtc; + struct rs5c348_plat_data *pdata; + + pdata = devm_kzalloc(&spi->dev, sizeof(struct rs5c348_plat_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + spi->dev.platform_data = pdata; + + /* Check D7 of SECOND register */ + ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_SECS)); + if (ret < 0 || (ret & 0x80)) { + dev_err(&spi->dev, "not found.\n"); + return ret; + } + + dev_info(&spi->dev, "spiclk %u KHz.\n", + (spi->max_speed_hz + 500) / 1000); + + ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_CTL1)); + if (ret < 0) + return ret; + if (ret & RS5C348_BIT_24H) + pdata->rtc_24h = 1; + + rtc = devm_rtc_allocate_device(&spi->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + pdata->rtc = rtc; + + rtc->ops = &rs5c348_rtc_ops; + + return devm_rtc_register_device(rtc); +} + +static struct spi_driver rs5c348_driver = { + .driver = { + .name = "rtc-rs5c348", + }, + .probe = rs5c348_probe, +}; + +module_spi_driver(rs5c348_driver); + +MODULE_AUTHOR("Atsushi Nemoto "); +MODULE_DESCRIPTION("Ricoh RS5C348 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:rtc-rs5c348"); diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c new file mode 100644 index 000000000..24bd795d9 --- /dev/null +++ b/drivers/rtc/rtc-rs5c372.c @@ -0,0 +1,930 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * An I2C driver for Ricoh RS5C372, R2025S/D and RV5C38[67] RTCs + * + * Copyright (C) 2005 Pavel Mironchik + * Copyright (C) 2006 Tower Technologies + * Copyright (C) 2008 Paul Mundt + */ + +#include +#include +#include +#include +#include +#include + +/* + * 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. + */ +#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 +# define RS5C372_TRIM_XSL 0x80 /* only if RS5C372[a|b] */ +# define RS5C372_TRIM_MASK 0x7F +# define R2221TL_TRIM_DEV (1 << 7) /* only if R2221TL */ +# define RS5C372_TRIM_DECR (1 << 6) + +#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) +# define RS5C_CTRL2_XSTP (1 << 4) /* only if !R2x2x */ +# 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 */ +# 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, + rtc_r2025sd, + rtc_r2221tl, + rtc_rs5c372a, + rtc_rs5c372b, + rtc_rv5c386, + rtc_rv5c387a, +}; + +static const struct i2c_device_id rs5c372_id[] = { + { .name = "r2025sd", .driver_data = rtc_r2025sd }, + { .name = "r2221tl", .driver_data = rtc_r2221tl }, + { .name = "rs5c372a", .driver_data = rtc_rs5c372a }, + { .name = "rs5c372b", .driver_data = rtc_rs5c372b }, + { .name = "rv5c386", .driver_data = rtc_rv5c386 }, + { .name = "rv5c387a", .driver_data = rtc_rv5c387a }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rs5c372_id); + +static const __maybe_unused 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); + +/* 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; + unsigned smbus:1; + char buf[17]; + char *regs; +}; + +static int rs5c_get_regs(struct rs5c372 *rs5c) +{ + struct i2c_client *client = rs5c->client; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = sizeof(rs5c->buf), + .buf = rs5c->buf + }, + }; + + /* 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. + * + * The third method on the other hand doesn't work for the SMBus-only + * configurations, so we use the first method there, stripping off + * the extra register in the process. + */ + 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\n"); + return -EIO; + } + } else { + if ((i2c_transfer(client->adapter, msgs, 1)) != 1) { + dev_warn(&client->dev, "can't read registers\n"); + return -EIO; + } + } + + dev_dbg(&client->dev, + "%3ph (%02x) %3ph (%02x), %3ph, %3ph; %02x %02x\n", + rs5c->regs + 0, rs5c->regs[3], + rs5c->regs + 4, rs5c->regs[7], + rs5c->regs + 8, rs5c->regs + 11, + rs5c->regs[14], rs5c->regs[15]); + + return 0; +} + +static unsigned rs5c_reg2hr(struct rs5c372 *rs5c, unsigned reg) +{ + unsigned hour; + + if (rs5c->time24) + return bcd2bin(reg & 0x3f); + + hour = bcd2bin(reg & 0x1f); + if (hour == 12) + hour = 0; + if (reg & 0x20) + hour += 12; + return hour; +} + +static unsigned rs5c_hr2reg(struct rs5c372 *rs5c, unsigned hour) +{ + if (rs5c->time24) + return bin2bcd(hour); + + if (hour > 12) + return 0x20 | bin2bcd(hour - 12); + if (hour == 12) + return 0x20 | bin2bcd(12); + if (hour == 0) + return bin2bcd(12); + return bin2bcd(hour); +} + +static int rs5c372_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rs5c372 *rs5c = i2c_get_clientdata(client); + int status = rs5c_get_regs(rs5c); + unsigned char ctrl2 = rs5c->regs[RS5C_REG_CTRL2]; + + if (status < 0) + return status; + + 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.\n"); + return -EINVAL; + } + break; + default: + if (ctrl2 & RS5C_CTRL2_XSTP) { + dev_warn(&client->dev, "rtc oscillator interruption detected. Please reset the rtc clock.\n"); + return -EINVAL; + } + } + + tm->tm_sec = bcd2bin(rs5c->regs[RS5C372_REG_SECS] & 0x7f); + tm->tm_min = bcd2bin(rs5c->regs[RS5C372_REG_MINS] & 0x7f); + tm->tm_hour = rs5c_reg2hr(rs5c, rs5c->regs[RS5C372_REG_HOURS]); + + tm->tm_wday = bcd2bin(rs5c->regs[RS5C372_REG_WDAY] & 0x07); + tm->tm_mday = bcd2bin(rs5c->regs[RS5C372_REG_DAY] & 0x3f); + + /* tm->tm_mon is zero-based */ + tm->tm_mon = bcd2bin(rs5c->regs[RS5C372_REG_MONTH] & 0x1f) - 1; + + /* year is 1900 + tm->tm_year */ + tm->tm_year = bcd2bin(rs5c->regs[RS5C372_REG_YEAR]) + 100; + + dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + return 0; +} + +static int rs5c372_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rs5c372 *rs5c = i2c_get_clientdata(client); + unsigned char buf[7]; + unsigned char ctrl2; + int addr; + + dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + addr = RS5C_ADDR(RS5C372_REG_SECS); + buf[0] = bin2bcd(tm->tm_sec); + buf[1] = bin2bcd(tm->tm_min); + buf[2] = rs5c_hr2reg(rs5c, tm->tm_hour); + 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); + + if (i2c_smbus_write_i2c_block_data(client, addr, sizeof(buf), buf) < 0) { + dev_dbg(&client->dev, "%s: write error in line %i\n", + __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\n", + __func__, __LINE__); + return -EIO; + } + + return 0; +} + +#if IS_ENABLED(CONFIG_RTC_INTF_PROC) +#define NEED_TRIM +#endif + +#if IS_ENABLED(CONFIG_RTC_INTF_SYSFS) +#define NEED_TRIM +#endif + +#ifdef NEED_TRIM +static int rs5c372_get_trim(struct i2c_client *client, int *osc, int *trim) +{ + struct rs5c372 *rs5c372 = i2c_get_clientdata(client); + u8 tmp = rs5c372->regs[RS5C372_REG_TRIM]; + + if (osc) { + if (rs5c372->type == rtc_rs5c372a || rs5c372->type == rtc_rs5c372b) + *osc = (tmp & RS5C372_TRIM_XSL) ? 32000 : 32768; + else + *osc = 32768; + } + + if (trim) { + dev_dbg(&client->dev, "%s: raw trim=%x\n", __func__, tmp); + 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; + } + + return 0; +} +#endif + +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) { + dev_warn(dev, "can't update alarm\n"); + status = -EIO; + } else + rs5c->regs[RS5C_REG_CTRL1] = buf; + + return status; +} + + +/* 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; + t->time.tm_min = bcd2bin(rs5c->regs[RS5C_REG_ALARM_A_MIN] & 0x7f); + t->time.tm_hour = rs5c_reg2hr(rs5c, rs5c->regs[RS5C_REG_ALARM_A_HOURS]); + + /* ... 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); + int status, addr, i; + unsigned char buf[3]; + + /* 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) { + 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) { + dev_dbg(dev, "can't disable alarm\n"); + return -EIO; + } + rs5c->regs[RS5C_REG_CTRL1] = buf[0]; + } + + /* set alarm */ + buf[0] = bin2bcd(t->time.tm_min); + 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) { + dev_dbg(dev, "can't set alarm time\n"); + return -EIO; + } + } + + /* ... and maybe enable its irq */ + if (t->enabled) { + 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) + dev_warn(dev, "can't enable alarm\n"); + rs5c->regs[RS5C_REG_CTRL1] = buf[0]; + } + + return 0; +} + +#if IS_ENABLED(CONFIG_RTC_INTF_PROC) + +static int rs5c372_rtc_proc(struct device *dev, struct seq_file *seq) +{ + int err, osc, trim; + + err = rs5c372_get_trim(to_i2c_client(dev), &osc, &trim); + if (err == 0) { + seq_printf(seq, "crystal\t\t: %d.%03d KHz\n", + osc / 1000, osc % 1000); + seq_printf(seq, "trim\t\t: %d\n", trim); + } + + return 0; +} + +#else +#define rs5c372_rtc_proc NULL +#endif + +#ifdef CONFIG_RTC_INTF_DEV +static int rs5c372_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct rs5c372 *rs5c = i2c_get_clientdata(to_i2c_client(dev)); + unsigned char ctrl2; + int addr; + unsigned int flags; + + dev_dbg(dev, "%s: cmd=%x\n", __func__, cmd); + + addr = RS5C_ADDR(RS5C_REG_CTRL2); + ctrl2 = i2c_smbus_read_byte_data(rs5c->client, addr); + + switch (cmd) { + case RTC_VL_READ: + flags = 0; + + 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))) { + flags |= RTC_VL_DATA_INVALID; + } + if (ctrl2 & R2x2x_CTRL2_VDET) + flags |= RTC_VL_BACKUP_LOW; + break; + default: + if (ctrl2 & RS5C_CTRL2_XSTP) + flags |= RTC_VL_DATA_INVALID; + break; + } + + return put_user(flags, (unsigned int __user *)arg); + case RTC_VL_CLR: + /* clear VDET bit */ + if (rs5c->type == rtc_r2025sd || rs5c->type == rtc_r2221tl) { + ctrl2 &= ~R2x2x_CTRL2_VDET; + if (i2c_smbus_write_byte_data(rs5c->client, addr, ctrl2) < 0) { + dev_dbg(&rs5c->client->dev, "%s: write error in line %i\n", + __func__, __LINE__); + return -EIO; + } + } + return 0; + default: + return -ENOIOCTLCMD; + } + return 0; +} +#else +#define rs5c372_ioctl NULL +#endif + +static int rs5c372_read_offset(struct device *dev, long *offset) +{ + struct rs5c372 *rs5c = i2c_get_clientdata(to_i2c_client(dev)); + u8 val = rs5c->regs[RS5C372_REG_TRIM]; + long ppb_per_step = 0; + bool decr = val & RS5C372_TRIM_DECR; + + switch (rs5c->type) { + case rtc_r2221tl: + ppb_per_step = val & R2221TL_TRIM_DEV ? 1017 : 3051; + break; + case rtc_rs5c372a: + case rtc_rs5c372b: + ppb_per_step = val & RS5C372_TRIM_XSL ? 3125 : 3051; + break; + default: + ppb_per_step = 3051; + break; + } + + /* Only bits[0:5] repsents the time counts */ + val &= 0x3F; + + /* If bits[1:5] are all 0, it means no increment or decrement */ + if (!(val & 0x3E)) { + *offset = 0; + } else { + if (decr) + *offset = -(((~val) & 0x3F) + 1) * ppb_per_step; + else + *offset = (val - 1) * ppb_per_step; + } + + return 0; +} + +static int rs5c372_set_offset(struct device *dev, long offset) +{ + struct rs5c372 *rs5c = i2c_get_clientdata(to_i2c_client(dev)); + int addr = RS5C_ADDR(RS5C372_REG_TRIM); + u8 val = 0; + u8 tmp = 0; + long ppb_per_step = 3051; + long steps = LONG_MIN; + + switch (rs5c->type) { + case rtc_rs5c372a: + case rtc_rs5c372b: + tmp = rs5c->regs[RS5C372_REG_TRIM]; + if (tmp & RS5C372_TRIM_XSL) { + ppb_per_step = 3125; + val |= RS5C372_TRIM_XSL; + } + break; + case rtc_r2221tl: + /* + * Check if it is possible to use high resolution mode (DEV=1). + * In this mode, the minimum resolution is 2 / (32768 * 20 * 3), + * which is about 1017 ppb. + */ + steps = DIV_ROUND_CLOSEST(offset, 1017); + if (steps >= -0x3E && steps <= 0x3E) { + ppb_per_step = 1017; + val |= R2221TL_TRIM_DEV; + } else { + /* + * offset is out of the range of high resolution mode. + * Try to use low resolution mode (DEV=0). In this mode, + * the minimum resolution is 2 / (32768 * 20), which is + * about 3051 ppb. + */ + steps = LONG_MIN; + } + break; + default: + break; + } + + if (steps == LONG_MIN) { + steps = DIV_ROUND_CLOSEST(offset, ppb_per_step); + if (steps > 0x3E || steps < -0x3E) + return -ERANGE; + } + + if (steps > 0) { + val |= steps + 1; + } else { + val |= RS5C372_TRIM_DECR; + val |= (~(-steps - 1)) & 0x3F; + } + + if (!steps || !(val & 0x3E)) { + /* + * if offset is too small, set oscillation adjustment register + * or time trimming register with its default value whic means + * no increment or decrement. But for rs5c372[a|b], the XSL bit + * should be kept unchanged. + */ + if (rs5c->type == rtc_rs5c372a || rs5c->type == rtc_rs5c372b) + val &= RS5C372_TRIM_XSL; + else + val = 0; + } + + dev_dbg(&rs5c->client->dev, "write 0x%x for offset %ld\n", val, offset); + + if (i2c_smbus_write_byte_data(rs5c->client, addr, val) < 0) { + dev_err(&rs5c->client->dev, "failed to write 0x%x to reg %d\n", val, addr); + return -EIO; + } + + rs5c->regs[RS5C372_REG_TRIM] = val; + + return 0; +} + +static const struct rtc_class_ops rs5c372_rtc_ops = { + .proc = rs5c372_rtc_proc, + .read_time = rs5c372_rtc_read_time, + .set_time = rs5c372_rtc_set_time, + .read_alarm = rs5c_read_alarm, + .set_alarm = rs5c_set_alarm, + .alarm_irq_enable = rs5c_rtc_alarm_irq_enable, + .ioctl = rs5c372_ioctl, + .read_offset = rs5c372_read_offset, + .set_offset = rs5c372_set_offset, +}; + +#if IS_ENABLED(CONFIG_RTC_INTF_SYSFS) + +static ssize_t rs5c372_sysfs_show_trim(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int err, trim; + + err = rs5c372_get_trim(to_i2c_client(dev), NULL, &trim); + if (err) + return err; + + return sprintf(buf, "%d\n", trim); +} +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) +{ + int err, osc; + + err = rs5c372_get_trim(to_i2c_client(dev), &osc, NULL); + if (err) + return err; + + return sprintf(buf, "%d.%03d KHz\n", osc / 1000, osc % 1000); +} +static DEVICE_ATTR(osc, S_IRUGO, rs5c372_sysfs_show_osc, NULL); + +static int rs5c_sysfs_register(struct device *dev) +{ + 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; +} + +static void rs5c_sysfs_unregister(struct device *dev) +{ + device_remove_file(dev, &dev_attr_trim); + device_remove_file(dev, &dev_attr_osc); +} + +#else +static int rs5c_sysfs_register(struct device *dev) +{ + return 0; +} + +static void rs5c_sysfs_unregister(struct device *dev) +{ + /* nothing */ +} +#endif /* SYSFS */ + +static struct i2c_driver rs5c372_driver; + +static int rs5c_oscillator_setup(struct rs5c372 *rs5c372) +{ + unsigned char buf[2]; + int addr, i, ret = 0; + + addr = RS5C_ADDR(RS5C_REG_CTRL1); + buf[0] = rs5c372->regs[RS5C_REG_CTRL1]; + buf[1] = rs5c372->regs[RS5C_REG_CTRL2]; + + switch (rs5c372->type) { + case rtc_r2025sd: + if (buf[1] & R2x2x_CTRL2_XSTP) + return ret; + break; + case rtc_r2221tl: + if (!(buf[1] & R2x2x_CTRL2_XSTP)) + return ret; + break; + default: + if (!(buf[1] & RS5C_CTRL2_XSTP)) + return ret; + break; + } + + /* use 24hr mode */ + switch (rs5c372->type) { + case rtc_rs5c372a: + case rtc_rs5c372b: + buf[1] |= RS5C372_CTRL2_24; + rs5c372->time24 = 1; + break; + case rtc_r2025sd: + case rtc_r2221tl: + 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; +} + +static int rs5c372_probe(struct i2c_client *client) +{ + int err = 0; + int smbus_mode = 0; + struct rs5c372 *rs5c372; + + dev_dbg(&client->dev, "%s\n", __func__); + + 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; + } + } + + rs5c372 = devm_kzalloc(&client->dev, sizeof(struct rs5c372), + GFP_KERNEL); + if (!rs5c372) { + err = -ENOMEM; + goto exit; + } + + rs5c372->client = client; + i2c_set_clientdata(client, rs5c372); + rs5c372->type = (uintptr_t)i2c_get_match_data(client); + + /* we read registers 0x0f then 0x00-0x0f; skip the first one */ + rs5c372->regs = &rs5c372->buf[1]; + rs5c372->smbus = smbus_mode; + + err = rs5c_get_regs(rs5c372); + if (err < 0) + goto exit; + + /* 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; + case rtc_r2025sd: + case rtc_r2221tl: + 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\n"); + goto exit; + } + + /* if the oscillator lost power and no other software (like + * the bootloader) set it up, do it here. + * + * The R2025S/D does this a little differently than the other + * parts, so we special case that.. + */ + err = rs5c_oscillator_setup(rs5c372); + if (unlikely(err < 0)) { + dev_err(&client->dev, "setup error\n"); + goto exit; + } + + dev_info(&client->dev, "%s found, %s\n", + ({ char *s; switch (rs5c372->type) { + case rtc_r2025sd: s = "r2025sd"; break; + case rtc_r2221tl: s = "r2221tl"; break; + 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" + ); + + /* REVISIT use client->irq to register alarm irq ... */ + rs5c372->rtc = devm_rtc_device_register(&client->dev, + rs5c372_driver.driver.name, + &rs5c372_rtc_ops, THIS_MODULE); + + if (IS_ERR(rs5c372->rtc)) { + err = PTR_ERR(rs5c372->rtc); + goto exit; + } + + err = rs5c_sysfs_register(&client->dev); + if (err) + goto exit; + + return 0; + +exit: + return err; +} + +static void rs5c372_remove(struct i2c_client *client) +{ + rs5c_sysfs_unregister(&client->dev); +} + +static struct i2c_driver rs5c372_driver = { + .driver = { + .name = "rtc-rs5c372", + .of_match_table = of_match_ptr(rs5c372_of_match), + }, + .probe = rs5c372_probe, + .remove = rs5c372_remove, + .id_table = rs5c372_id, +}; + +module_i2c_driver(rs5c372_driver); + +MODULE_AUTHOR( + "Pavel Mironchik , " + "Alessandro Zummo , " + "Paul Mundt "); +MODULE_DESCRIPTION("Ricoh RS5C372 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-rtd119x.c b/drivers/rtc/rtc-rtd119x.c new file mode 100644 index 000000000..85a138db8 --- /dev/null +++ b/drivers/rtc/rtc-rtd119x.c @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Realtek RTD129x RTC + * + * Copyright (c) 2017 Andreas Färber + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define RTD_RTCSEC 0x00 +#define RTD_RTCMIN 0x04 +#define RTD_RTCHR 0x08 +#define RTD_RTCDATE1 0x0c +#define RTD_RTCDATE2 0x10 +#define RTD_RTCACR 0x28 +#define RTD_RTCEN 0x2c +#define RTD_RTCCR 0x30 + +#define RTD_RTCSEC_RTCSEC_MASK 0x7f + +#define RTD_RTCMIN_RTCMIN_MASK 0x3f + +#define RTD_RTCHR_RTCHR_MASK 0x1f + +#define RTD_RTCDATE1_RTCDATE1_MASK 0xff + +#define RTD_RTCDATE2_RTCDATE2_MASK 0x7f + +#define RTD_RTCACR_RTCPWR BIT(7) + +#define RTD_RTCEN_RTCEN_MASK 0xff + +#define RTD_RTCCR_RTCRST BIT(6) + +struct rtd119x_rtc { + void __iomem *base; + struct clk *clk; + struct rtc_device *rtcdev; + unsigned int base_year; +}; + +static inline int rtd119x_rtc_days_in_year(int year) +{ + return 365 + (is_leap_year(year) ? 1 : 0); +} + +static void rtd119x_rtc_reset(struct device *dev) +{ + struct rtd119x_rtc *data = dev_get_drvdata(dev); + u32 val; + + val = readl_relaxed(data->base + RTD_RTCCR); + val |= RTD_RTCCR_RTCRST; + writel_relaxed(val, data->base + RTD_RTCCR); + + val &= ~RTD_RTCCR_RTCRST; + writel(val, data->base + RTD_RTCCR); +} + +static void rtd119x_rtc_set_enabled(struct device *dev, bool enable) +{ + struct rtd119x_rtc *data = dev_get_drvdata(dev); + u32 val; + + val = readl_relaxed(data->base + RTD_RTCEN); + if (enable) { + if ((val & RTD_RTCEN_RTCEN_MASK) == 0x5a) + return; + writel_relaxed(0x5a, data->base + RTD_RTCEN); + } else { + writel_relaxed(0, data->base + RTD_RTCEN); + } +} + +static int rtd119x_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct rtd119x_rtc *data = dev_get_drvdata(dev); + s32 day; + u32 sec; + unsigned int year; + int tries = 0; + + while (true) { + tm->tm_sec = (readl_relaxed(data->base + RTD_RTCSEC) & RTD_RTCSEC_RTCSEC_MASK) >> 1; + tm->tm_min = readl_relaxed(data->base + RTD_RTCMIN) & RTD_RTCMIN_RTCMIN_MASK; + tm->tm_hour = readl_relaxed(data->base + RTD_RTCHR) & RTD_RTCHR_RTCHR_MASK; + day = readl_relaxed(data->base + RTD_RTCDATE1) & RTD_RTCDATE1_RTCDATE1_MASK; + day |= (readl_relaxed(data->base + RTD_RTCDATE2) & RTD_RTCDATE2_RTCDATE2_MASK) << 8; + sec = (readl_relaxed(data->base + RTD_RTCSEC) & RTD_RTCSEC_RTCSEC_MASK) >> 1; + tries++; + + if (sec == tm->tm_sec) + break; + + if (tries >= 3) + return -EINVAL; + } + if (tries > 1) + dev_dbg(dev, "%s: needed %i tries\n", __func__, tries); + + year = data->base_year; + while (day >= rtd119x_rtc_days_in_year(year)) { + day -= rtd119x_rtc_days_in_year(year); + year++; + } + tm->tm_year = year - 1900; + tm->tm_yday = day; + + tm->tm_mon = 0; + while (day >= rtc_month_days(tm->tm_mon, year)) { + day -= rtc_month_days(tm->tm_mon, year); + tm->tm_mon++; + } + tm->tm_mday = day + 1; + + return 0; +} + +static int rtd119x_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rtd119x_rtc *data = dev_get_drvdata(dev); + unsigned int day; + int i; + + if (1900 + tm->tm_year < data->base_year) + return -EINVAL; + + day = 0; + for (i = data->base_year; i < 1900 + tm->tm_year; i++) + day += rtd119x_rtc_days_in_year(i); + + day += tm->tm_yday; + if (day > 0x7fff) + return -EINVAL; + + rtd119x_rtc_set_enabled(dev, false); + + writel_relaxed((tm->tm_sec << 1) & RTD_RTCSEC_RTCSEC_MASK, data->base + RTD_RTCSEC); + writel_relaxed(tm->tm_min & RTD_RTCMIN_RTCMIN_MASK, data->base + RTD_RTCMIN); + writel_relaxed(tm->tm_hour & RTD_RTCHR_RTCHR_MASK, data->base + RTD_RTCHR); + writel_relaxed(day & RTD_RTCDATE1_RTCDATE1_MASK, data->base + RTD_RTCDATE1); + writel_relaxed((day >> 8) & RTD_RTCDATE2_RTCDATE2_MASK, data->base + RTD_RTCDATE2); + + rtd119x_rtc_set_enabled(dev, true); + + return 0; +} + +static const struct rtc_class_ops rtd119x_rtc_ops = { + .read_time = rtd119x_rtc_read_time, + .set_time = rtd119x_rtc_set_time, +}; + +static const struct of_device_id rtd119x_rtc_dt_ids[] = { + { .compatible = "realtek,rtd1295-rtc" }, + { } +}; + +static int rtd119x_rtc_probe(struct platform_device *pdev) +{ + struct rtd119x_rtc *data; + u32 val; + int ret; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + platform_set_drvdata(pdev, data); + data->base_year = 2014; + + data->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(data->base)) + return PTR_ERR(data->base); + + data->clk = of_clk_get(pdev->dev.of_node, 0); + if (IS_ERR(data->clk)) + return PTR_ERR(data->clk); + + ret = clk_prepare_enable(data->clk); + if (ret) { + clk_put(data->clk); + return ret; + } + + val = readl_relaxed(data->base + RTD_RTCACR); + if (!(val & RTD_RTCACR_RTCPWR)) { + writel_relaxed(RTD_RTCACR_RTCPWR, data->base + RTD_RTCACR); + + rtd119x_rtc_reset(&pdev->dev); + + writel_relaxed(0, data->base + RTD_RTCMIN); + writel_relaxed(0, data->base + RTD_RTCHR); + writel_relaxed(0, data->base + RTD_RTCDATE1); + writel_relaxed(0, data->base + RTD_RTCDATE2); + } + + rtd119x_rtc_set_enabled(&pdev->dev, true); + + data->rtcdev = devm_rtc_device_register(&pdev->dev, "rtc", + &rtd119x_rtc_ops, THIS_MODULE); + if (IS_ERR(data->rtcdev)) { + dev_err(&pdev->dev, "failed to register rtc device"); + clk_disable_unprepare(data->clk); + clk_put(data->clk); + return PTR_ERR(data->rtcdev); + } + + return 0; +} + +static void rtd119x_rtc_remove(struct platform_device *pdev) +{ + struct rtd119x_rtc *data = platform_get_drvdata(pdev); + + rtd119x_rtc_set_enabled(&pdev->dev, false); + + clk_disable_unprepare(data->clk); + clk_put(data->clk); +} + +static struct platform_driver rtd119x_rtc_driver = { + .probe = rtd119x_rtc_probe, + .remove = rtd119x_rtc_remove, + .driver = { + .name = "rtd1295-rtc", + .of_match_table = rtd119x_rtc_dt_ids, + }, +}; +builtin_platform_driver(rtd119x_rtc_driver); diff --git a/drivers/rtc/rtc-rv3028.c b/drivers/rtc/rtc-rv3028.c new file mode 100644 index 000000000..d25e33d08 --- /dev/null +++ b/drivers/rtc/rtc-rv3028.c @@ -0,0 +1,1063 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RTC driver for the Micro Crystal RV3028 + * + * Copyright (C) 2019 Micro Crystal SA + * + * Alexandre Belloni + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RV3028_SEC 0x00 +#define RV3028_MIN 0x01 +#define RV3028_HOUR 0x02 +#define RV3028_WDAY 0x03 +#define RV3028_DAY 0x04 +#define RV3028_MONTH 0x05 +#define RV3028_YEAR 0x06 +#define RV3028_ALARM_MIN 0x07 +#define RV3028_ALARM_HOUR 0x08 +#define RV3028_ALARM_DAY 0x09 +#define RV3028_STATUS 0x0E +#define RV3028_CTRL1 0x0F +#define RV3028_CTRL2 0x10 +#define RV3028_EVT_CTRL 0x13 +#define RV3028_TS_COUNT 0x14 +#define RV3028_TS_SEC 0x15 +#define RV3028_RAM1 0x1F +#define RV3028_EEPROM_ADDR 0x25 +#define RV3028_EEPROM_DATA 0x26 +#define RV3028_EEPROM_CMD 0x27 +#define RV3028_CLKOUT 0x35 +#define RV3028_OFFSET 0x36 +#define RV3028_BACKUP 0x37 + +#define RV3028_STATUS_PORF BIT(0) +#define RV3028_STATUS_EVF BIT(1) +#define RV3028_STATUS_AF BIT(2) +#define RV3028_STATUS_TF BIT(3) +#define RV3028_STATUS_UF BIT(4) +#define RV3028_STATUS_BSF BIT(5) +#define RV3028_STATUS_CLKF BIT(6) +#define RV3028_STATUS_EEBUSY BIT(7) + +#define RV3028_CLKOUT_FD_MASK GENMASK(2, 0) +#define RV3028_CLKOUT_PORIE BIT(3) +#define RV3028_CLKOUT_CLKSY BIT(6) +#define RV3028_CLKOUT_CLKOE BIT(7) + +#define RV3028_CTRL1_EERD BIT(3) +#define RV3028_CTRL1_WADA BIT(5) + +#define RV3028_CTRL2_RESET BIT(0) +#define RV3028_CTRL2_12_24 BIT(1) +#define RV3028_CTRL2_EIE BIT(2) +#define RV3028_CTRL2_AIE BIT(3) +#define RV3028_CTRL2_TIE BIT(4) +#define RV3028_CTRL2_UIE BIT(5) +#define RV3028_CTRL2_TSE BIT(7) + +#define RV3028_EVT_CTRL_TSR BIT(2) + +#define RV3028_EEPROM_CMD_UPDATE 0x11 +#define RV3028_EEPROM_CMD_WRITE 0x21 +#define RV3028_EEPROM_CMD_READ 0x22 + +#define RV3028_EEBUSY_POLL 10000 +#define RV3028_EEBUSY_TIMEOUT 100000 + +#define RV3028_BACKUP_TCE BIT(5) +#define RV3028_BACKUP_TCR_MASK GENMASK(1,0) +#define RV3028_BACKUP_BSM GENMASK(3,2) + +#define RV3028_BACKUP_BSM_DSM 0x1 +#define RV3028_BACKUP_BSM_LSM 0x3 + +#define OFFSET_STEP_PPT 953674 + +enum rv3028_type { + rv_3028, +}; + +struct rv3028_data { + struct regmap *regmap; + struct rtc_device *rtc; + enum rv3028_type type; +#ifdef CONFIG_COMMON_CLK + struct clk_hw clkout_hw; +#endif +}; + +static u16 rv3028_trickle_resistors[] = {3000, 5000, 9000, 15000}; + +static ssize_t timestamp0_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev->parent); + + regmap_update_bits(rv3028->regmap, RV3028_EVT_CTRL, RV3028_EVT_CTRL_TSR, + RV3028_EVT_CTRL_TSR); + + return count; +}; + +static ssize_t timestamp0_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev->parent); + struct rtc_time tm; + unsigned int count; + u8 date[6]; + int ret; + + ret = regmap_read(rv3028->regmap, RV3028_TS_COUNT, &count); + if (ret) + return ret; + + if (!count) + return 0; + + ret = regmap_bulk_read(rv3028->regmap, RV3028_TS_SEC, date, + sizeof(date)); + if (ret) + return ret; + + tm.tm_sec = bcd2bin(date[0]); + tm.tm_min = bcd2bin(date[1]); + tm.tm_hour = bcd2bin(date[2]); + tm.tm_mday = bcd2bin(date[3]); + tm.tm_mon = bcd2bin(date[4]) - 1; + tm.tm_year = bcd2bin(date[5]) + 100; + + ret = rtc_valid_tm(&tm); + if (ret) + return ret; + + return sprintf(buf, "%llu\n", + (unsigned long long)rtc_tm_to_time64(&tm)); +}; + +static DEVICE_ATTR_RW(timestamp0); + +static ssize_t timestamp0_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev->parent); + unsigned int count; + int ret; + + ret = regmap_read(rv3028->regmap, RV3028_TS_COUNT, &count); + if (ret) + return ret; + + return sprintf(buf, "%u\n", count); +}; + +static DEVICE_ATTR_RO(timestamp0_count); + +static struct attribute *rv3028_attrs[] = { + &dev_attr_timestamp0.attr, + &dev_attr_timestamp0_count.attr, + NULL +}; + +static const struct attribute_group rv3028_attr_group = { + .attrs = rv3028_attrs, +}; + +static int rv3028_exit_eerd(struct rv3028_data *rv3028, u32 eerd) +{ + if (eerd) + return 0; + + return regmap_update_bits(rv3028->regmap, RV3028_CTRL1, RV3028_CTRL1_EERD, 0); +} + +static int rv3028_enter_eerd(struct rv3028_data *rv3028, u32 *eerd) +{ + u32 ctrl1, status; + int ret; + + ret = regmap_read(rv3028->regmap, RV3028_CTRL1, &ctrl1); + if (ret) + return ret; + + *eerd = ctrl1 & RV3028_CTRL1_EERD; + if (*eerd) + return 0; + + ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL1, + RV3028_CTRL1_EERD, RV3028_CTRL1_EERD); + if (ret) + return ret; + + ret = regmap_read_poll_timeout(rv3028->regmap, RV3028_STATUS, status, + !(status & RV3028_STATUS_EEBUSY), + RV3028_EEBUSY_POLL, RV3028_EEBUSY_TIMEOUT); + if (ret) { + rv3028_exit_eerd(rv3028, *eerd); + + return ret; + } + + return 0; +} + +static int rv3028_update_eeprom(struct rv3028_data *rv3028, u32 eerd) +{ + u32 status; + int ret; + + ret = regmap_write(rv3028->regmap, RV3028_EEPROM_CMD, 0x0); + if (ret) + goto exit_eerd; + + ret = regmap_write(rv3028->regmap, RV3028_EEPROM_CMD, RV3028_EEPROM_CMD_UPDATE); + if (ret) + goto exit_eerd; + + usleep_range(63000, RV3028_EEBUSY_TIMEOUT); + + ret = regmap_read_poll_timeout(rv3028->regmap, RV3028_STATUS, status, + !(status & RV3028_STATUS_EEBUSY), + RV3028_EEBUSY_POLL, RV3028_EEBUSY_TIMEOUT); + +exit_eerd: + rv3028_exit_eerd(rv3028, eerd); + + return ret; +} + +static int rv3028_update_cfg(struct rv3028_data *rv3028, unsigned int reg, + unsigned int mask, unsigned int val) +{ + u32 eerd; + int ret; + + ret = rv3028_enter_eerd(rv3028, &eerd); + if (ret) + return ret; + + ret = regmap_update_bits(rv3028->regmap, reg, mask, val); + if (ret) { + rv3028_exit_eerd(rv3028, eerd); + return ret; + } + + return rv3028_update_eeprom(rv3028, eerd); +} + +static irqreturn_t rv3028_handle_irq(int irq, void *dev_id) +{ + struct rv3028_data *rv3028 = dev_id; + unsigned long events = 0; + u32 status = 0, ctrl = 0; + + if (regmap_read(rv3028->regmap, RV3028_STATUS, &status) < 0 || + status == 0) { + return IRQ_NONE; + } + + status &= ~RV3028_STATUS_PORF; + + if (status & RV3028_STATUS_TF) { + status |= RV3028_STATUS_TF; + ctrl |= RV3028_CTRL2_TIE; + events |= RTC_PF; + } + + if (status & RV3028_STATUS_AF) { + status |= RV3028_STATUS_AF; + ctrl |= RV3028_CTRL2_AIE; + events |= RTC_AF; + } + + if (status & RV3028_STATUS_UF) { + status |= RV3028_STATUS_UF; + ctrl |= RV3028_CTRL2_UIE; + events |= RTC_UF; + } + + if (events) { + rtc_update_irq(rv3028->rtc, 1, events); + regmap_update_bits(rv3028->regmap, RV3028_STATUS, status, 0); + regmap_update_bits(rv3028->regmap, RV3028_CTRL2, ctrl, 0); + } + + if (status & RV3028_STATUS_EVF) { + sysfs_notify(&rv3028->rtc->dev.kobj, NULL, + dev_attr_timestamp0.attr.name); + dev_warn(&rv3028->rtc->dev, "event detected"); + } + + return IRQ_HANDLED; +} + +static int rv3028_get_time(struct device *dev, struct rtc_time *tm) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev); + u8 date[7]; + int ret, status; + + ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status); + if (ret < 0) + return ret; + + if (status & RV3028_STATUS_PORF) + return -EINVAL; + + ret = regmap_bulk_read(rv3028->regmap, RV3028_SEC, date, sizeof(date)); + if (ret) + return ret; + + tm->tm_sec = bcd2bin(date[RV3028_SEC] & 0x7f); + tm->tm_min = bcd2bin(date[RV3028_MIN] & 0x7f); + tm->tm_hour = bcd2bin(date[RV3028_HOUR] & 0x3f); + tm->tm_wday = date[RV3028_WDAY] & 0x7f; + tm->tm_mday = bcd2bin(date[RV3028_DAY] & 0x3f); + tm->tm_mon = bcd2bin(date[RV3028_MONTH] & 0x1f) - 1; + tm->tm_year = bcd2bin(date[RV3028_YEAR]) + 100; + + return 0; +} + +static int rv3028_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev); + u8 date[7]; + int ret; + + date[RV3028_SEC] = bin2bcd(tm->tm_sec); + date[RV3028_MIN] = bin2bcd(tm->tm_min); + date[RV3028_HOUR] = bin2bcd(tm->tm_hour); + date[RV3028_WDAY] = tm->tm_wday; + date[RV3028_DAY] = bin2bcd(tm->tm_mday); + date[RV3028_MONTH] = bin2bcd(tm->tm_mon + 1); + date[RV3028_YEAR] = bin2bcd(tm->tm_year - 100); + + /* + * Writing to the Seconds register has the same effect as setting RESET + * bit to 1 + */ + ret = regmap_bulk_write(rv3028->regmap, RV3028_SEC, date, + sizeof(date)); + if (ret) + return ret; + + ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS, + RV3028_STATUS_PORF, 0); + + return ret; +} + +static int rv3028_get_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev); + u8 alarmvals[3]; + int status, ctrl, ret; + + ret = regmap_bulk_read(rv3028->regmap, RV3028_ALARM_MIN, alarmvals, + sizeof(alarmvals)); + if (ret) + return ret; + + ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status); + if (ret < 0) + return ret; + + ret = regmap_read(rv3028->regmap, RV3028_CTRL2, &ctrl); + if (ret < 0) + return ret; + + alrm->time.tm_sec = 0; + alrm->time.tm_min = bcd2bin(alarmvals[0] & 0x7f); + alrm->time.tm_hour = bcd2bin(alarmvals[1] & 0x3f); + alrm->time.tm_mday = bcd2bin(alarmvals[2] & 0x3f); + + alrm->enabled = !!(ctrl & RV3028_CTRL2_AIE); + alrm->pending = (status & RV3028_STATUS_AF) && alrm->enabled; + + return 0; +} + +static int rv3028_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev); + u8 alarmvals[3]; + u8 ctrl = 0; + int ret; + + /* The alarm has no seconds, round up to nearest minute */ + if (alrm->time.tm_sec) { + time64_t alarm_time = rtc_tm_to_time64(&alrm->time); + + alarm_time += 60 - alrm->time.tm_sec; + rtc_time64_to_tm(alarm_time, &alrm->time); + } + + ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2, + RV3028_CTRL2_AIE | RV3028_CTRL2_UIE, 0); + if (ret) + return ret; + + alarmvals[0] = bin2bcd(alrm->time.tm_min); + alarmvals[1] = bin2bcd(alrm->time.tm_hour); + alarmvals[2] = bin2bcd(alrm->time.tm_mday); + + ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS, + RV3028_STATUS_AF, 0); + if (ret) + return ret; + + ret = regmap_bulk_write(rv3028->regmap, RV3028_ALARM_MIN, alarmvals, + sizeof(alarmvals)); + if (ret) + return ret; + + if (alrm->enabled) { + if (rv3028->rtc->uie_rtctimer.enabled) + ctrl |= RV3028_CTRL2_UIE; + if (rv3028->rtc->aie_timer.enabled) + ctrl |= RV3028_CTRL2_AIE; + } + + ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2, + RV3028_CTRL2_UIE | RV3028_CTRL2_AIE, ctrl); + + return ret; +} + +static int rv3028_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev); + int ctrl = 0, ret; + + if (enabled) { + if (rv3028->rtc->uie_rtctimer.enabled) + ctrl |= RV3028_CTRL2_UIE; + if (rv3028->rtc->aie_timer.enabled) + ctrl |= RV3028_CTRL2_AIE; + } + + ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS, + RV3028_STATUS_AF | RV3028_STATUS_UF, 0); + if (ret) + return ret; + + ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2, + RV3028_CTRL2_UIE | RV3028_CTRL2_AIE, ctrl); + if (ret) + return ret; + + return 0; +} + +static int rv3028_read_offset(struct device *dev, long *offset) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev); + int ret, value, steps; + + ret = regmap_read(rv3028->regmap, RV3028_OFFSET, &value); + if (ret < 0) + return ret; + + steps = sign_extend32(value << 1, 8); + + ret = regmap_read(rv3028->regmap, RV3028_BACKUP, &value); + if (ret < 0) + return ret; + + steps += value >> 7; + + *offset = DIV_ROUND_CLOSEST(steps * OFFSET_STEP_PPT, 1000); + + return 0; +} + +static int rv3028_set_offset(struct device *dev, long offset) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev); + u32 eerd; + int ret; + + offset = clamp(offset, -244141L, 243187L) * 1000; + offset = DIV_ROUND_CLOSEST(offset, OFFSET_STEP_PPT); + + ret = rv3028_enter_eerd(rv3028, &eerd); + if (ret) + return ret; + + ret = regmap_write(rv3028->regmap, RV3028_OFFSET, offset >> 1); + if (ret < 0) + goto exit_eerd; + + ret = regmap_update_bits(rv3028->regmap, RV3028_BACKUP, BIT(7), + offset << 7); + if (ret < 0) + goto exit_eerd; + + return rv3028_update_eeprom(rv3028, eerd); + +exit_eerd: + rv3028_exit_eerd(rv3028, eerd); + + return ret; + +} + +static int rv3028_param_get(struct device *dev, struct rtc_param *param) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev); + int ret; + u32 value; + + switch(param->param) { + case RTC_PARAM_BACKUP_SWITCH_MODE: + ret = regmap_read(rv3028->regmap, RV3028_BACKUP, &value); + if (ret < 0) + return ret; + + value = FIELD_GET(RV3028_BACKUP_BSM, value); + + switch(value) { + case RV3028_BACKUP_BSM_DSM: + param->uvalue = RTC_BSM_DIRECT; + break; + case RV3028_BACKUP_BSM_LSM: + param->uvalue = RTC_BSM_LEVEL; + break; + default: + param->uvalue = RTC_BSM_DISABLED; + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int rv3028_param_set(struct device *dev, struct rtc_param *param) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev); + u8 mode; + + switch(param->param) { + case RTC_PARAM_BACKUP_SWITCH_MODE: + switch (param->uvalue) { + case RTC_BSM_DISABLED: + mode = 0; + break; + case RTC_BSM_DIRECT: + mode = RV3028_BACKUP_BSM_DSM; + break; + case RTC_BSM_LEVEL: + mode = RV3028_BACKUP_BSM_LSM; + break; + default: + return -EINVAL; + } + + return rv3028_update_cfg(rv3028, RV3028_BACKUP, RV3028_BACKUP_BSM, + FIELD_PREP(RV3028_BACKUP_BSM, mode)); + + default: + return -EINVAL; + } + + return 0; +} + +static int rv3028_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct rv3028_data *rv3028 = dev_get_drvdata(dev); + int status, ret = 0; + + switch (cmd) { + case RTC_VL_READ: + ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status); + if (ret < 0) + return ret; + + status = status & RV3028_STATUS_PORF ? RTC_VL_DATA_INVALID : 0; + return put_user(status, (unsigned int __user *)arg); + + default: + return -ENOIOCTLCMD; + } +} + +static int rv3028_nvram_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + return regmap_bulk_write(priv, RV3028_RAM1 + offset, val, bytes); +} + +static int rv3028_nvram_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + return regmap_bulk_read(priv, RV3028_RAM1 + offset, val, bytes); +} + +static int rv3028_eeprom_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct rv3028_data *rv3028 = priv; + u32 status, eerd; + int i, ret; + u8 *buf = val; + + ret = rv3028_enter_eerd(rv3028, &eerd); + if (ret) + return ret; + + for (i = 0; i < bytes; i++) { + ret = regmap_write(rv3028->regmap, RV3028_EEPROM_ADDR, offset + i); + if (ret) + goto restore_eerd; + + ret = regmap_write(rv3028->regmap, RV3028_EEPROM_DATA, buf[i]); + if (ret) + goto restore_eerd; + + ret = regmap_write(rv3028->regmap, RV3028_EEPROM_CMD, 0x0); + if (ret) + goto restore_eerd; + + ret = regmap_write(rv3028->regmap, RV3028_EEPROM_CMD, + RV3028_EEPROM_CMD_WRITE); + if (ret) + goto restore_eerd; + + usleep_range(RV3028_EEBUSY_POLL, RV3028_EEBUSY_TIMEOUT); + + ret = regmap_read_poll_timeout(rv3028->regmap, RV3028_STATUS, status, + !(status & RV3028_STATUS_EEBUSY), + RV3028_EEBUSY_POLL, + RV3028_EEBUSY_TIMEOUT); + if (ret) + goto restore_eerd; + } + +restore_eerd: + rv3028_exit_eerd(rv3028, eerd); + + return ret; +} + +static int rv3028_eeprom_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct rv3028_data *rv3028 = priv; + u32 status, eerd, data; + int i, ret; + u8 *buf = val; + + ret = rv3028_enter_eerd(rv3028, &eerd); + if (ret) + return ret; + + for (i = 0; i < bytes; i++) { + ret = regmap_write(rv3028->regmap, RV3028_EEPROM_ADDR, offset + i); + if (ret) + goto restore_eerd; + + ret = regmap_write(rv3028->regmap, RV3028_EEPROM_CMD, 0x0); + if (ret) + goto restore_eerd; + + ret = regmap_write(rv3028->regmap, RV3028_EEPROM_CMD, + RV3028_EEPROM_CMD_READ); + if (ret) + goto restore_eerd; + + ret = regmap_read_poll_timeout(rv3028->regmap, RV3028_STATUS, status, + !(status & RV3028_STATUS_EEBUSY), + RV3028_EEBUSY_POLL, + RV3028_EEBUSY_TIMEOUT); + if (ret) + goto restore_eerd; + + ret = regmap_read(rv3028->regmap, RV3028_EEPROM_DATA, &data); + if (ret) + goto restore_eerd; + buf[i] = data; + } + +restore_eerd: + rv3028_exit_eerd(rv3028, eerd); + + return ret; +} + +#ifdef CONFIG_COMMON_CLK +#define clkout_hw_to_rv3028(hw) container_of(hw, struct rv3028_data, clkout_hw) + +static int clkout_rates[] = { + 32768, + 8192, + 1024, + 64, + 32, + 1, +}; + +static unsigned long rv3028_clkout_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + int clkout, ret; + struct rv3028_data *rv3028 = clkout_hw_to_rv3028(hw); + + ret = regmap_read(rv3028->regmap, RV3028_CLKOUT, &clkout); + if (ret < 0) + return 0; + + clkout &= RV3028_CLKOUT_FD_MASK; + return clkout_rates[clkout]; +} + +static int rv3028_clkout_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(clkout_rates); i++) + if (clkout_rates[i] <= req->rate) { + req->rate = clkout_rates[i]; + + return 0; + } + + req->rate = clkout_rates[0]; + + return 0; +} + +static int rv3028_clkout_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int i, ret; + u32 enabled; + struct rv3028_data *rv3028 = clkout_hw_to_rv3028(hw); + + ret = regmap_read(rv3028->regmap, RV3028_CLKOUT, &enabled); + if (ret < 0) + return ret; + + ret = regmap_write(rv3028->regmap, RV3028_CLKOUT, 0x0); + if (ret < 0) + return ret; + + enabled &= RV3028_CLKOUT_CLKOE; + + for (i = 0; i < ARRAY_SIZE(clkout_rates); i++) + if (clkout_rates[i] == rate) + return rv3028_update_cfg(rv3028, RV3028_CLKOUT, 0xff, + RV3028_CLKOUT_CLKSY | enabled | i); + + return -EINVAL; +} + +static int rv3028_clkout_prepare(struct clk_hw *hw) +{ + struct rv3028_data *rv3028 = clkout_hw_to_rv3028(hw); + + return regmap_write(rv3028->regmap, RV3028_CLKOUT, + RV3028_CLKOUT_CLKSY | RV3028_CLKOUT_CLKOE); +} + +static void rv3028_clkout_unprepare(struct clk_hw *hw) +{ + struct rv3028_data *rv3028 = clkout_hw_to_rv3028(hw); + + regmap_write(rv3028->regmap, RV3028_CLKOUT, 0x0); + regmap_update_bits(rv3028->regmap, RV3028_STATUS, + RV3028_STATUS_CLKF, 0); +} + +static int rv3028_clkout_is_prepared(struct clk_hw *hw) +{ + int clkout, ret; + struct rv3028_data *rv3028 = clkout_hw_to_rv3028(hw); + + ret = regmap_read(rv3028->regmap, RV3028_CLKOUT, &clkout); + if (ret < 0) + return ret; + + return !!(clkout & RV3028_CLKOUT_CLKOE); +} + +static const struct clk_ops rv3028_clkout_ops = { + .prepare = rv3028_clkout_prepare, + .unprepare = rv3028_clkout_unprepare, + .is_prepared = rv3028_clkout_is_prepared, + .recalc_rate = rv3028_clkout_recalc_rate, + .determine_rate = rv3028_clkout_determine_rate, + .set_rate = rv3028_clkout_set_rate, +}; + +static int rv3028_clkout_register_clk(struct rv3028_data *rv3028, + struct i2c_client *client) +{ + int ret; + struct clk *clk; + struct clk_init_data init = {}; + struct device_node *node = client->dev.of_node; + + ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS, + RV3028_STATUS_CLKF, 0); + if (ret < 0) + return ret; + + init.name = "rv3028-clkout"; + init.ops = &rv3028_clkout_ops; + init.flags = 0; + init.parent_names = NULL; + init.num_parents = 0; + rv3028->clkout_hw.init = &init; + + /* optional override of the clockname */ + of_property_read_string(node, "clock-output-names", &init.name); + + /* register the clock */ + clk = devm_clk_register(&client->dev, &rv3028->clkout_hw); + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); + + return 0; +} +#endif + +static const struct rtc_class_ops rv3028_rtc_ops = { + .read_time = rv3028_get_time, + .set_time = rv3028_set_time, + .read_alarm = rv3028_get_alarm, + .set_alarm = rv3028_set_alarm, + .alarm_irq_enable = rv3028_alarm_irq_enable, + .read_offset = rv3028_read_offset, + .set_offset = rv3028_set_offset, + .ioctl = rv3028_ioctl, + .param_get = rv3028_param_get, + .param_set = rv3028_param_set, +}; + +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x37, +}; + +static u8 rv3028_set_trickle_charger(struct rv3028_data *rv3028, + struct i2c_client *client) +{ + int ret, val_old, val; + u32 ohms, chargeable; + + ret = regmap_read(rv3028->regmap, RV3028_BACKUP, &val_old); + if (ret < 0) + return ret; + + /* mask out only trickle charger bits */ + val_old = val_old & (RV3028_BACKUP_TCE | RV3028_BACKUP_TCR_MASK); + val = val_old; + + /* setup trickle charger */ + if (!device_property_read_u32(&client->dev, "trickle-resistor-ohms", + &ohms)) { + int i; + + for (i = 0; i < ARRAY_SIZE(rv3028_trickle_resistors); i++) + if (ohms == rv3028_trickle_resistors[i]) + break; + + if (i < ARRAY_SIZE(rv3028_trickle_resistors)) { + /* enable trickle charger and its resistor */ + val = RV3028_BACKUP_TCE | i; + } else { + dev_warn(&client->dev, "invalid trickle resistor value\n"); + } + } + + if (!device_property_read_u32(&client->dev, "aux-voltage-chargeable", + &chargeable)) { + switch (chargeable) { + case 0: + val &= ~RV3028_BACKUP_TCE; + break; + case 1: + val |= RV3028_BACKUP_TCE; + break; + default: + dev_warn(&client->dev, + "unsupported aux-voltage-chargeable value\n"); + break; + } + } + + /* only update EEPROM if changes are necessary */ + if (val_old != val) { + ret = rv3028_update_cfg(rv3028, RV3028_BACKUP, RV3028_BACKUP_TCE | + RV3028_BACKUP_TCR_MASK, val); + if (ret) + return ret; + } + + return ret; +} + +static int rv3028_probe(struct i2c_client *client) +{ + struct rv3028_data *rv3028; + int ret, status; + struct nvmem_config nvmem_cfg = { + .name = "rv3028_nvram", + .word_size = 1, + .stride = 1, + .size = 2, + .type = NVMEM_TYPE_BATTERY_BACKED, + .reg_read = rv3028_nvram_read, + .reg_write = rv3028_nvram_write, + }; + struct nvmem_config eeprom_cfg = { + .name = "rv3028_eeprom", + .word_size = 1, + .stride = 1, + .size = 43, + .type = NVMEM_TYPE_EEPROM, + .reg_read = rv3028_eeprom_read, + .reg_write = rv3028_eeprom_write, + }; + + rv3028 = devm_kzalloc(&client->dev, sizeof(struct rv3028_data), + GFP_KERNEL); + if (!rv3028) + return -ENOMEM; + + rv3028->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(rv3028->regmap)) + return PTR_ERR(rv3028->regmap); + + i2c_set_clientdata(client, rv3028); + + ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status); + if (ret < 0) + return ret; + + if (status & RV3028_STATUS_AF) + dev_warn(&client->dev, "An alarm may have been missed.\n"); + + rv3028->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(rv3028->rtc)) + return PTR_ERR(rv3028->rtc); + + if (client->irq > 0) { + unsigned long flags; + + /* + * If flags = 0, devm_request_threaded_irq() will use IRQ flags + * obtained from device tree. + */ + if (dev_fwnode(&client->dev)) + flags = 0; + else + flags = IRQF_TRIGGER_LOW; + + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, rv3028_handle_irq, + flags | IRQF_ONESHOT, + "rv3028", rv3028); + if (ret) { + dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n"); + client->irq = 0; + } + } + if (!client->irq) + clear_bit(RTC_FEATURE_ALARM, rv3028->rtc->features); + + ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL1, + RV3028_CTRL1_WADA, RV3028_CTRL1_WADA); + if (ret) + return ret; + + /* setup timestamping */ + ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2, + RV3028_CTRL2_EIE | RV3028_CTRL2_TSE, + RV3028_CTRL2_EIE | RV3028_CTRL2_TSE); + if (ret) + return ret; + + ret = rv3028_set_trickle_charger(rv3028, client); + if (ret) + return ret; + + ret = rtc_add_group(rv3028->rtc, &rv3028_attr_group); + if (ret) + return ret; + + set_bit(RTC_FEATURE_BACKUP_SWITCH_MODE, rv3028->rtc->features); + + rv3028->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rv3028->rtc->range_max = RTC_TIMESTAMP_END_2099; + rv3028->rtc->ops = &rv3028_rtc_ops; + ret = devm_rtc_register_device(rv3028->rtc); + if (ret) + return ret; + + nvmem_cfg.priv = rv3028->regmap; + devm_rtc_nvmem_register(rv3028->rtc, &nvmem_cfg); + eeprom_cfg.priv = rv3028; + devm_rtc_nvmem_register(rv3028->rtc, &eeprom_cfg); + +#ifdef CONFIG_COMMON_CLK + rv3028_clkout_register_clk(rv3028, client); +#endif + return 0; +} + +static const struct acpi_device_id rv3028_i2c_acpi_match[] = { + { "MCRY3028" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, rv3028_i2c_acpi_match); + +static const __maybe_unused struct of_device_id rv3028_of_match[] = { + { .compatible = "microcrystal,rv3028", }, + { } +}; +MODULE_DEVICE_TABLE(of, rv3028_of_match); + +static const struct i2c_device_id rv3028_id_table[] = { + { .name = "rv3028", }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rv3028_id_table); + +static struct i2c_driver rv3028_driver = { + .driver = { + .name = "rtc-rv3028", + .acpi_match_table = rv3028_i2c_acpi_match, + .of_match_table = of_match_ptr(rv3028_of_match), + }, + .id_table = rv3028_id_table, + .probe = rv3028_probe, +}; +module_i2c_driver(rv3028_driver); + +MODULE_AUTHOR("Alexandre Belloni "); +MODULE_DESCRIPTION("Micro Crystal RV3028 RTC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-rv3029c2.c b/drivers/rtc/rtc-rv3029c2.c new file mode 100644 index 000000000..98953af2a --- /dev/null +++ b/drivers/rtc/rtc-rv3029c2.c @@ -0,0 +1,924 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Micro Crystal RV-3029 / RV-3049 rtc class driver + * + * Author: Gregory Hermant + * Michael Buesch + * + * based on previously existing rtc class drivers + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register map */ +/* control section */ +#define RV3029_ONOFF_CTRL 0x00 +#define RV3029_ONOFF_CTRL_WE BIT(0) +#define RV3029_ONOFF_CTRL_TE BIT(1) +#define RV3029_ONOFF_CTRL_TAR BIT(2) +#define RV3029_ONOFF_CTRL_EERE BIT(3) +#define RV3029_ONOFF_CTRL_SRON BIT(4) +#define RV3029_ONOFF_CTRL_TD0 BIT(5) +#define RV3029_ONOFF_CTRL_TD1 BIT(6) +#define RV3029_ONOFF_CTRL_CLKINT BIT(7) +#define RV3029_IRQ_CTRL 0x01 +#define RV3029_IRQ_CTRL_AIE BIT(0) +#define RV3029_IRQ_CTRL_TIE BIT(1) +#define RV3029_IRQ_CTRL_V1IE BIT(2) +#define RV3029_IRQ_CTRL_V2IE BIT(3) +#define RV3029_IRQ_CTRL_SRIE BIT(4) +#define RV3029_IRQ_FLAGS 0x02 +#define RV3029_IRQ_FLAGS_AF BIT(0) +#define RV3029_IRQ_FLAGS_TF BIT(1) +#define RV3029_IRQ_FLAGS_V1IF BIT(2) +#define RV3029_IRQ_FLAGS_V2IF BIT(3) +#define RV3029_IRQ_FLAGS_SRF BIT(4) +#define RV3029_STATUS 0x03 +#define RV3029_STATUS_VLOW1 BIT(2) +#define RV3029_STATUS_VLOW2 BIT(3) +#define RV3029_STATUS_SR BIT(4) +#define RV3029_STATUS_PON BIT(5) +#define RV3029_STATUS_EEBUSY BIT(7) +#define RV3029_RST_CTRL 0x04 +#define RV3029_RST_CTRL_SYSR BIT(4) +#define RV3029_CONTROL_SECTION_LEN 0x05 + +/* watch section */ +#define RV3029_W_SEC 0x08 +#define RV3029_W_MINUTES 0x09 +#define RV3029_W_HOURS 0x0A +#define RV3029_REG_HR_12_24 BIT(6) /* 24h/12h mode */ +#define RV3029_REG_HR_PM BIT(5) /* PM/AM bit in 12h mode */ +#define RV3029_W_DATE 0x0B +#define RV3029_W_DAYS 0x0C +#define RV3029_W_MONTHS 0x0D +#define RV3029_W_YEARS 0x0E +#define RV3029_WATCH_SECTION_LEN 0x07 + +/* alarm section */ +#define RV3029_A_SC 0x10 +#define RV3029_A_MN 0x11 +#define RV3029_A_HR 0x12 +#define RV3029_A_DT 0x13 +#define RV3029_A_DW 0x14 +#define RV3029_A_MO 0x15 +#define RV3029_A_YR 0x16 +#define RV3029_A_AE_X BIT(7) +#define RV3029_ALARM_SECTION_LEN 0x07 + +/* timer section */ +#define RV3029_TIMER_LOW 0x18 +#define RV3029_TIMER_HIGH 0x19 + +/* temperature section */ +#define RV3029_TEMP_PAGE 0x20 + +/* eeprom data section */ +#define RV3029_E2P_EEDATA1 0x28 +#define RV3029_E2P_EEDATA2 0x29 +#define RV3029_E2PDATA_SECTION_LEN 0x02 + +/* eeprom control section */ +#define RV3029_CONTROL_E2P_EECTRL 0x30 +#define RV3029_EECTRL_THP BIT(0) /* temp scan interval */ +#define RV3029_EECTRL_THE BIT(1) /* thermometer enable */ +#define RV3029_EECTRL_FD0 BIT(2) /* CLKOUT */ +#define RV3029_EECTRL_FD1 BIT(3) /* CLKOUT */ +#define RV3029_TRICKLE_1K BIT(4) /* 1.5K resistance */ +#define RV3029_TRICKLE_5K BIT(5) /* 5K resistance */ +#define RV3029_TRICKLE_20K BIT(6) /* 20K resistance */ +#define RV3029_TRICKLE_80K BIT(7) /* 80K resistance */ +#define RV3029_TRICKLE_MASK (RV3029_TRICKLE_1K |\ + RV3029_TRICKLE_5K |\ + RV3029_TRICKLE_20K |\ + RV3029_TRICKLE_80K) +#define RV3029_TRICKLE_SHIFT 4 +#define RV3029_CONTROL_E2P_XOFFS 0x31 /* XTAL offset */ +#define RV3029_CONTROL_E2P_XOFFS_SIGN BIT(7) /* Sign: 1->pos, 0->neg */ +#define RV3029_CONTROL_E2P_QCOEF 0x32 /* XTAL temp drift coef */ +#define RV3029_CONTROL_E2P_TURNOVER 0x33 /* XTAL turnover temp (in *C) */ +#define RV3029_CONTROL_E2P_TOV_MASK 0x3F /* XTAL turnover temp mask */ + +/* user ram section */ +#define RV3029_RAM_PAGE 0x38 +#define RV3029_RAM_SECTION_LEN 8 + +struct rv3029_data { + struct device *dev; + struct rtc_device *rtc; + struct regmap *regmap; + int irq; +}; + +static int rv3029_eeprom_busywait(struct rv3029_data *rv3029) +{ + unsigned int sr; + int i, ret; + + for (i = 100; i > 0; i--) { + ret = regmap_read(rv3029->regmap, RV3029_STATUS, &sr); + if (ret < 0) + break; + if (!(sr & RV3029_STATUS_EEBUSY)) + break; + usleep_range(1000, 10000); + } + if (i <= 0) { + dev_err(rv3029->dev, "EEPROM busy wait timeout.\n"); + return -ETIMEDOUT; + } + + return ret; +} + +static int rv3029_eeprom_exit(struct rv3029_data *rv3029) +{ + /* Re-enable eeprom refresh */ + return regmap_update_bits(rv3029->regmap, RV3029_ONOFF_CTRL, + RV3029_ONOFF_CTRL_EERE, + RV3029_ONOFF_CTRL_EERE); +} + +static int rv3029_eeprom_enter(struct rv3029_data *rv3029) +{ + unsigned int sr; + int ret; + + /* Check whether we are in the allowed voltage range. */ + ret = regmap_read(rv3029->regmap, RV3029_STATUS, &sr); + if (ret < 0) + return ret; + if (sr & RV3029_STATUS_VLOW2) + return -ENODEV; + if (sr & RV3029_STATUS_VLOW1) { + /* We clear the bits and retry once just in case + * we had a brown out in early startup. + */ + ret = regmap_update_bits(rv3029->regmap, RV3029_STATUS, + RV3029_STATUS_VLOW1, 0); + if (ret < 0) + return ret; + usleep_range(1000, 10000); + ret = regmap_read(rv3029->regmap, RV3029_STATUS, &sr); + if (ret < 0) + return ret; + if (sr & RV3029_STATUS_VLOW1) { + dev_err(rv3029->dev, + "Supply voltage is too low to safely access the EEPROM.\n"); + return -ENODEV; + } + } + + /* Disable eeprom refresh. */ + ret = regmap_update_bits(rv3029->regmap, RV3029_ONOFF_CTRL, + RV3029_ONOFF_CTRL_EERE, 0); + if (ret < 0) + return ret; + + /* Wait for any previous eeprom accesses to finish. */ + ret = rv3029_eeprom_busywait(rv3029); + if (ret < 0) + rv3029_eeprom_exit(rv3029); + + return ret; +} + +static int rv3029_eeprom_read(struct rv3029_data *rv3029, u8 reg, + u8 buf[], size_t len) +{ + int ret, err; + + err = rv3029_eeprom_enter(rv3029); + if (err < 0) + return err; + + ret = regmap_bulk_read(rv3029->regmap, reg, buf, len); + + err = rv3029_eeprom_exit(rv3029); + if (err < 0) + return err; + + return ret; +} + +static int rv3029_eeprom_write(struct rv3029_data *rv3029, u8 reg, + u8 const buf[], size_t len) +{ + unsigned int tmp; + int ret, err; + size_t i; + + err = rv3029_eeprom_enter(rv3029); + if (err < 0) + return err; + + for (i = 0; i < len; i++, reg++) { + ret = regmap_read(rv3029->regmap, reg, &tmp); + if (ret < 0) + break; + if (tmp != buf[i]) { + tmp = buf[i]; + ret = regmap_write(rv3029->regmap, reg, tmp); + if (ret < 0) + break; + } + ret = rv3029_eeprom_busywait(rv3029); + if (ret < 0) + break; + } + + err = rv3029_eeprom_exit(rv3029); + if (err < 0) + return err; + + return ret; +} + +static int rv3029_eeprom_update_bits(struct rv3029_data *rv3029, + u8 reg, u8 mask, u8 set) +{ + u8 buf; + int ret; + + ret = rv3029_eeprom_read(rv3029, reg, &buf, 1); + if (ret < 0) + return ret; + buf &= ~mask; + buf |= set & mask; + ret = rv3029_eeprom_write(rv3029, reg, &buf, 1); + if (ret < 0) + return ret; + + return 0; +} + +static irqreturn_t rv3029_handle_irq(int irq, void *dev_id) +{ + struct device *dev = dev_id; + struct rv3029_data *rv3029 = dev_get_drvdata(dev); + unsigned int flags, controls; + unsigned long events = 0; + int ret; + + rtc_lock(rv3029->rtc); + + ret = regmap_read(rv3029->regmap, RV3029_IRQ_CTRL, &controls); + if (ret) { + dev_warn(dev, "Read IRQ Control Register error %d\n", ret); + rtc_unlock(rv3029->rtc); + return IRQ_NONE; + } + + ret = regmap_read(rv3029->regmap, RV3029_IRQ_FLAGS, &flags); + if (ret) { + dev_warn(dev, "Read IRQ Flags Register error %d\n", ret); + rtc_unlock(rv3029->rtc); + return IRQ_NONE; + } + + if (flags & RV3029_IRQ_FLAGS_AF) { + flags &= ~RV3029_IRQ_FLAGS_AF; + controls &= ~RV3029_IRQ_CTRL_AIE; + events |= RTC_AF; + } + + if (events) { + rtc_update_irq(rv3029->rtc, 1, events); + regmap_write(rv3029->regmap, RV3029_IRQ_FLAGS, flags); + regmap_write(rv3029->regmap, RV3029_IRQ_CTRL, controls); + } + rtc_unlock(rv3029->rtc); + + return IRQ_HANDLED; +} + +static int rv3029_read_time(struct device *dev, struct rtc_time *tm) +{ + struct rv3029_data *rv3029 = dev_get_drvdata(dev); + unsigned int sr; + int ret; + u8 regs[RV3029_WATCH_SECTION_LEN] = { 0, }; + + ret = regmap_read(rv3029->regmap, RV3029_STATUS, &sr); + if (ret < 0) + return ret; + + if (sr & (RV3029_STATUS_VLOW2 | RV3029_STATUS_PON)) + return -EINVAL; + + ret = regmap_bulk_read(rv3029->regmap, RV3029_W_SEC, regs, + RV3029_WATCH_SECTION_LEN); + if (ret < 0) + return ret; + + tm->tm_sec = bcd2bin(regs[RV3029_W_SEC - RV3029_W_SEC]); + tm->tm_min = bcd2bin(regs[RV3029_W_MINUTES - RV3029_W_SEC]); + + /* HR field has a more complex interpretation */ + { + const u8 _hr = regs[RV3029_W_HOURS - RV3029_W_SEC]; + + if (_hr & RV3029_REG_HR_12_24) { + /* 12h format */ + tm->tm_hour = bcd2bin(_hr & 0x1f); + if (_hr & RV3029_REG_HR_PM) /* PM flag set */ + tm->tm_hour += 12; + } else /* 24h format */ + tm->tm_hour = bcd2bin(_hr & 0x3f); + } + + tm->tm_mday = bcd2bin(regs[RV3029_W_DATE - RV3029_W_SEC]); + tm->tm_mon = bcd2bin(regs[RV3029_W_MONTHS - RV3029_W_SEC]) - 1; + tm->tm_year = bcd2bin(regs[RV3029_W_YEARS - RV3029_W_SEC]) + 100; + tm->tm_wday = bcd2bin(regs[RV3029_W_DAYS - RV3029_W_SEC]) - 1; + + return 0; +} + +static int rv3029_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct rv3029_data *rv3029 = dev_get_drvdata(dev); + struct rtc_time *const tm = &alarm->time; + unsigned int controls, flags; + int ret; + u8 regs[8]; + + ret = regmap_bulk_read(rv3029->regmap, RV3029_A_SC, regs, + RV3029_ALARM_SECTION_LEN); + if (ret < 0) + return ret; + + ret = regmap_read(rv3029->regmap, RV3029_IRQ_CTRL, &controls); + if (ret) + return ret; + + ret = regmap_read(rv3029->regmap, RV3029_IRQ_FLAGS, &flags); + if (ret < 0) + return ret; + + tm->tm_sec = bcd2bin(regs[RV3029_A_SC - RV3029_A_SC] & 0x7f); + tm->tm_min = bcd2bin(regs[RV3029_A_MN - RV3029_A_SC] & 0x7f); + tm->tm_hour = bcd2bin(regs[RV3029_A_HR - RV3029_A_SC] & 0x3f); + tm->tm_mday = bcd2bin(regs[RV3029_A_DT - RV3029_A_SC] & 0x3f); + tm->tm_mon = bcd2bin(regs[RV3029_A_MO - RV3029_A_SC] & 0x1f) - 1; + tm->tm_year = bcd2bin(regs[RV3029_A_YR - RV3029_A_SC] & 0x7f) + 100; + tm->tm_wday = bcd2bin(regs[RV3029_A_DW - RV3029_A_SC] & 0x07) - 1; + + alarm->enabled = !!(controls & RV3029_IRQ_CTRL_AIE); + alarm->pending = (flags & RV3029_IRQ_FLAGS_AF) && alarm->enabled; + + return 0; +} + +static int rv3029_alarm_irq_enable(struct device *dev, unsigned int enable) +{ + struct rv3029_data *rv3029 = dev_get_drvdata(dev); + + return regmap_update_bits(rv3029->regmap, RV3029_IRQ_CTRL, + RV3029_IRQ_CTRL_AIE, + enable ? RV3029_IRQ_CTRL_AIE : 0); +} + +static int rv3029_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct rv3029_data *rv3029 = dev_get_drvdata(dev); + struct rtc_time *const tm = &alarm->time; + int ret; + u8 regs[8]; + + /* Activate all the alarms with AE_x bit */ + regs[RV3029_A_SC - RV3029_A_SC] = bin2bcd(tm->tm_sec) | RV3029_A_AE_X; + regs[RV3029_A_MN - RV3029_A_SC] = bin2bcd(tm->tm_min) | RV3029_A_AE_X; + regs[RV3029_A_HR - RV3029_A_SC] = (bin2bcd(tm->tm_hour) & 0x3f) + | RV3029_A_AE_X; + regs[RV3029_A_DT - RV3029_A_SC] = (bin2bcd(tm->tm_mday) & 0x3f) + | RV3029_A_AE_X; + regs[RV3029_A_MO - RV3029_A_SC] = (bin2bcd(tm->tm_mon + 1) & 0x1f) + | RV3029_A_AE_X; + regs[RV3029_A_DW - RV3029_A_SC] = (bin2bcd(tm->tm_wday + 1) & 0x7) + | RV3029_A_AE_X; + regs[RV3029_A_YR - RV3029_A_SC] = (bin2bcd(tm->tm_year - 100)) + | RV3029_A_AE_X; + + /* Write the alarm */ + ret = regmap_bulk_write(rv3029->regmap, RV3029_A_SC, regs, + RV3029_ALARM_SECTION_LEN); + if (ret < 0) + return ret; + + return rv3029_alarm_irq_enable(dev, alarm->enabled); +} + +static int rv3029_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rv3029_data *rv3029 = dev_get_drvdata(dev); + u8 regs[8]; + int ret; + + regs[RV3029_W_SEC - RV3029_W_SEC] = bin2bcd(tm->tm_sec); + regs[RV3029_W_MINUTES - RV3029_W_SEC] = bin2bcd(tm->tm_min); + regs[RV3029_W_HOURS - RV3029_W_SEC] = bin2bcd(tm->tm_hour); + regs[RV3029_W_DATE - RV3029_W_SEC] = bin2bcd(tm->tm_mday); + regs[RV3029_W_MONTHS - RV3029_W_SEC] = bin2bcd(tm->tm_mon + 1); + regs[RV3029_W_DAYS - RV3029_W_SEC] = bin2bcd(tm->tm_wday + 1) & 0x7; + regs[RV3029_W_YEARS - RV3029_W_SEC] = bin2bcd(tm->tm_year - 100); + + ret = regmap_bulk_write(rv3029->regmap, RV3029_W_SEC, regs, + RV3029_WATCH_SECTION_LEN); + if (ret < 0) + return ret; + + /* clear PON and VLOW2 bits */ + return regmap_update_bits(rv3029->regmap, RV3029_STATUS, + RV3029_STATUS_PON | RV3029_STATUS_VLOW2, 0); +} + +static int rv3029_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct rv3029_data *rv3029 = dev_get_drvdata(dev); + unsigned long vl = 0; + int sr, ret = 0; + + switch (cmd) { + case RTC_VL_READ: + ret = regmap_read(rv3029->regmap, RV3029_STATUS, &sr); + if (ret < 0) + return ret; + + if (sr & RV3029_STATUS_VLOW1) + vl = RTC_VL_ACCURACY_LOW; + + if (sr & (RV3029_STATUS_VLOW2 | RV3029_STATUS_PON)) + vl |= RTC_VL_DATA_INVALID; + + return put_user(vl, (unsigned int __user *)arg); + + case RTC_VL_CLR: + return regmap_update_bits(rv3029->regmap, RV3029_STATUS, + RV3029_STATUS_VLOW1, 0); + + default: + return -ENOIOCTLCMD; + } +} + +static int rv3029_nvram_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + return regmap_bulk_write(priv, RV3029_RAM_PAGE + offset, val, bytes); +} + +static int rv3029_nvram_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + return regmap_bulk_read(priv, RV3029_RAM_PAGE + offset, val, bytes); +} + +static const struct rv3029_trickle_tab_elem { + u32 r; /* resistance in ohms */ + u8 conf; /* trickle config bits */ +} rv3029_trickle_tab[] = { + { + .r = 1076, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_5K | + RV3029_TRICKLE_20K | RV3029_TRICKLE_80K, + }, { + .r = 1091, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_5K | + RV3029_TRICKLE_20K, + }, { + .r = 1137, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_5K | + RV3029_TRICKLE_80K, + }, { + .r = 1154, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_5K, + }, { + .r = 1371, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_20K | + RV3029_TRICKLE_80K, + }, { + .r = 1395, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_20K, + }, { + .r = 1472, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_80K, + }, { + .r = 1500, + .conf = RV3029_TRICKLE_1K, + }, { + .r = 3810, + .conf = RV3029_TRICKLE_5K | RV3029_TRICKLE_20K | + RV3029_TRICKLE_80K, + }, { + .r = 4000, + .conf = RV3029_TRICKLE_5K | RV3029_TRICKLE_20K, + }, { + .r = 4706, + .conf = RV3029_TRICKLE_5K | RV3029_TRICKLE_80K, + }, { + .r = 5000, + .conf = RV3029_TRICKLE_5K, + }, { + .r = 16000, + .conf = RV3029_TRICKLE_20K | RV3029_TRICKLE_80K, + }, { + .r = 20000, + .conf = RV3029_TRICKLE_20K, + }, { + .r = 80000, + .conf = RV3029_TRICKLE_80K, + }, +}; + +static void rv3029_trickle_config(struct device *dev) +{ + struct rv3029_data *rv3029 = dev_get_drvdata(dev); + struct device_node *of_node = dev->of_node; + const struct rv3029_trickle_tab_elem *elem; + int i, err; + u32 ohms; + u8 trickle_set_bits; + + if (!of_node) + return; + + /* Configure the trickle charger. */ + err = of_property_read_u32(of_node, "trickle-resistor-ohms", &ohms); + if (err) { + /* Disable trickle charger. */ + trickle_set_bits = 0; + } else { + /* Enable trickle charger. */ + for (i = 0; i < ARRAY_SIZE(rv3029_trickle_tab); i++) { + elem = &rv3029_trickle_tab[i]; + if (elem->r >= ohms) + break; + } + trickle_set_bits = elem->conf; + dev_info(dev, + "Trickle charger enabled at %d ohms resistance.\n", + elem->r); + } + err = rv3029_eeprom_update_bits(rv3029, RV3029_CONTROL_E2P_EECTRL, + RV3029_TRICKLE_MASK, + trickle_set_bits); + if (err < 0) + dev_err(dev, "Failed to update trickle charger config\n"); +} + +#ifdef CONFIG_RTC_DRV_RV3029_HWMON + +static int rv3029_read_temp(struct rv3029_data *rv3029, int *temp_mC) +{ + unsigned int temp; + int ret; + + ret = regmap_read(rv3029->regmap, RV3029_TEMP_PAGE, &temp); + if (ret < 0) + return ret; + + *temp_mC = ((int)temp - 60) * 1000; + + return 0; +} + +static ssize_t rv3029_hwmon_show_temp(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rv3029_data *rv3029 = dev_get_drvdata(dev); + int ret, temp_mC; + + ret = rv3029_read_temp(rv3029, &temp_mC); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", temp_mC); +} + +static ssize_t rv3029_hwmon_set_update_interval(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct rv3029_data *rv3029 = dev_get_drvdata(dev); + unsigned int th_set_bits = 0; + unsigned long interval_ms; + int ret; + + ret = kstrtoul(buf, 10, &interval_ms); + if (ret < 0) + return ret; + + if (interval_ms != 0) { + th_set_bits |= RV3029_EECTRL_THE; + if (interval_ms >= 16000) + th_set_bits |= RV3029_EECTRL_THP; + } + ret = rv3029_eeprom_update_bits(rv3029, RV3029_CONTROL_E2P_EECTRL, + RV3029_EECTRL_THE | RV3029_EECTRL_THP, + th_set_bits); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t rv3029_hwmon_show_update_interval(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rv3029_data *rv3029 = dev_get_drvdata(dev); + int ret, interval_ms; + u8 eectrl; + + ret = rv3029_eeprom_read(rv3029, RV3029_CONTROL_E2P_EECTRL, + &eectrl, 1); + if (ret < 0) + return ret; + + if (eectrl & RV3029_EECTRL_THE) { + if (eectrl & RV3029_EECTRL_THP) + interval_ms = 16000; + else + interval_ms = 1000; + } else { + interval_ms = 0; + } + + return sprintf(buf, "%d\n", interval_ms); +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, rv3029_hwmon_show_temp, + NULL, 0); +static SENSOR_DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, + rv3029_hwmon_show_update_interval, + rv3029_hwmon_set_update_interval, 0); + +static struct attribute *rv3029_hwmon_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_update_interval.dev_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(rv3029_hwmon); + +static void rv3029_hwmon_register(struct device *dev, const char *name) +{ + struct rv3029_data *rv3029 = dev_get_drvdata(dev); + struct device *hwmon_dev; + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, name, rv3029, + rv3029_hwmon_groups); + if (IS_ERR(hwmon_dev)) { + dev_warn(dev, "unable to register hwmon device %ld\n", + PTR_ERR(hwmon_dev)); + } +} + +#else /* CONFIG_RTC_DRV_RV3029_HWMON */ + +static void rv3029_hwmon_register(struct device *dev, const char *name) +{ +} + +#endif /* CONFIG_RTC_DRV_RV3029_HWMON */ + +static const struct rtc_class_ops rv3029_rtc_ops = { + .read_time = rv3029_read_time, + .set_time = rv3029_set_time, + .ioctl = rv3029_ioctl, + .read_alarm = rv3029_read_alarm, + .set_alarm = rv3029_set_alarm, + .alarm_irq_enable = rv3029_alarm_irq_enable, +}; + +static int rv3029_probe(struct device *dev, struct regmap *regmap, int irq, + const char *name) +{ + struct rv3029_data *rv3029; + struct nvmem_config nvmem_cfg = { + .name = "rv3029_nvram", + .word_size = 1, + .stride = 1, + .size = RV3029_RAM_SECTION_LEN, + .type = NVMEM_TYPE_BATTERY_BACKED, + .reg_read = rv3029_nvram_read, + .reg_write = rv3029_nvram_write, + }; + int rc = 0; + + rv3029 = devm_kzalloc(dev, sizeof(*rv3029), GFP_KERNEL); + if (!rv3029) + return -ENOMEM; + + rv3029->regmap = regmap; + rv3029->irq = irq; + rv3029->dev = dev; + dev_set_drvdata(dev, rv3029); + + rv3029_trickle_config(dev); + rv3029_hwmon_register(dev, name); + + rv3029->rtc = devm_rtc_allocate_device(dev); + if (IS_ERR(rv3029->rtc)) + return PTR_ERR(rv3029->rtc); + + if (rv3029->irq > 0) { + unsigned long irqflags = IRQF_TRIGGER_LOW; + + if (dev_fwnode(dev)) + irqflags = 0; + + rc = devm_request_threaded_irq(dev, rv3029->irq, + NULL, rv3029_handle_irq, + irqflags | IRQF_ONESHOT, + "rv3029", dev); + if (rc) { + dev_warn(dev, "unable to request IRQ, alarms disabled\n"); + rv3029->irq = 0; + } + } + if (!rv3029->irq) + clear_bit(RTC_FEATURE_ALARM, rv3029->rtc->features); + + rv3029->rtc->ops = &rv3029_rtc_ops; + rv3029->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rv3029->rtc->range_max = RTC_TIMESTAMP_END_2079; + + rc = devm_rtc_register_device(rv3029->rtc); + if (rc) + return rc; + + nvmem_cfg.priv = rv3029->regmap; + devm_rtc_nvmem_register(rv3029->rtc, &nvmem_cfg); + + return 0; +} + +static const struct regmap_range rv3029_holes_range[] = { + regmap_reg_range(0x05, 0x07), + regmap_reg_range(0x0f, 0x0f), + regmap_reg_range(0x17, 0x17), + regmap_reg_range(0x1a, 0x1f), + regmap_reg_range(0x21, 0x27), + regmap_reg_range(0x34, 0x37), +}; + +static const struct regmap_access_table rv3029_regs = { + .no_ranges = rv3029_holes_range, + .n_no_ranges = ARRAY_SIZE(rv3029_holes_range), +}; + +static const struct regmap_config config = { + .reg_bits = 8, + .val_bits = 8, + .rd_table = &rv3029_regs, + .wr_table = &rv3029_regs, + .max_register = 0x3f, +}; + +#if IS_ENABLED(CONFIG_I2C) + +static int rv3029_i2c_probe(struct i2c_client *client) +{ + struct regmap *regmap; + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK | + I2C_FUNC_SMBUS_BYTE)) { + dev_err(&client->dev, "Adapter does not support SMBUS_I2C_BLOCK or SMBUS_I2C_BYTE\n"); + return -ENODEV; + } + + regmap = devm_regmap_init_i2c(client, &config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rv3029_probe(&client->dev, regmap, client->irq, client->name); +} + +static const struct i2c_device_id rv3029_id[] = { + { .name = "rv3029" }, + { .name = "rv3029c2" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rv3029_id); + +static const __maybe_unused struct of_device_id rv3029_of_match[] = { + { .compatible = "microcrystal,rv3029" }, + { } +}; +MODULE_DEVICE_TABLE(of, rv3029_of_match); + +static struct i2c_driver rv3029_driver = { + .driver = { + .name = "rv3029", + .of_match_table = of_match_ptr(rv3029_of_match), + }, + .probe = rv3029_i2c_probe, + .id_table = rv3029_id, +}; + +static int __init rv3029_register_driver(void) +{ + return i2c_add_driver(&rv3029_driver); +} + +static void rv3029_unregister_driver(void) +{ + i2c_del_driver(&rv3029_driver); +} + +#else + +static int __init rv3029_register_driver(void) +{ + return 0; +} + +static void rv3029_unregister_driver(void) +{ +} + +#endif + +#if IS_ENABLED(CONFIG_SPI_MASTER) + +static int rv3049_probe(struct spi_device *spi) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_spi(spi, &config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rv3029_probe(&spi->dev, regmap, spi->irq, "rv3049"); +} + +static struct spi_driver rv3049_driver = { + .driver = { + .name = "rv3049", + }, + .probe = rv3049_probe, +}; + +static int __init rv3049_register_driver(void) +{ + return spi_register_driver(&rv3049_driver); +} + +static void __exit rv3049_unregister_driver(void) +{ + spi_unregister_driver(&rv3049_driver); +} + +#else + +static int __init rv3049_register_driver(void) +{ + return 0; +} + +static void __exit rv3049_unregister_driver(void) +{ +} + +#endif + +static int __init rv30x9_init(void) +{ + int ret; + + ret = rv3029_register_driver(); + if (ret) + return ret; + + ret = rv3049_register_driver(); + if (ret) + rv3029_unregister_driver(); + + return ret; +} +module_init(rv30x9_init) + +static void __exit rv30x9_exit(void) +{ + rv3049_unregister_driver(); + rv3029_unregister_driver(); +} +module_exit(rv30x9_exit) + +MODULE_AUTHOR("Gregory Hermant "); +MODULE_AUTHOR("Michael Buesch "); +MODULE_DESCRIPTION("Micro Crystal RV3029/RV3049 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:rv3049"); diff --git a/drivers/rtc/rtc-rv3032.c b/drivers/rtc/rtc-rv3032.c new file mode 100644 index 000000000..687faaef4 --- /dev/null +++ b/drivers/rtc/rtc-rv3032.c @@ -0,0 +1,1004 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RTC driver for the Micro Crystal RV3032 + * + * Copyright (C) 2020 Micro Crystal SA + * + * Alexandre Belloni + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RV3032_SEC 0x01 +#define RV3032_MIN 0x02 +#define RV3032_HOUR 0x03 +#define RV3032_WDAY 0x04 +#define RV3032_DAY 0x05 +#define RV3032_MONTH 0x06 +#define RV3032_YEAR 0x07 +#define RV3032_ALARM_MIN 0x08 +#define RV3032_ALARM_HOUR 0x09 +#define RV3032_ALARM_DAY 0x0A +#define RV3032_STATUS 0x0D +#define RV3032_TLSB 0x0E +#define RV3032_TMSB 0x0F +#define RV3032_CTRL1 0x10 +#define RV3032_CTRL2 0x11 +#define RV3032_CTRL3 0x12 +#define RV3032_TS_CTRL 0x13 +#define RV3032_CLK_IRQ 0x14 +#define RV3032_EEPROM_ADDR 0x3D +#define RV3032_EEPROM_DATA 0x3E +#define RV3032_EEPROM_CMD 0x3F +#define RV3032_RAM1 0x40 +#define RV3032_PMU 0xC0 +#define RV3032_OFFSET 0xC1 +#define RV3032_CLKOUT1 0xC2 +#define RV3032_CLKOUT2 0xC3 +#define RV3032_TREF0 0xC4 +#define RV3032_TREF1 0xC5 + +#define RV3032_STATUS_VLF BIT(0) +#define RV3032_STATUS_PORF BIT(1) +#define RV3032_STATUS_EVF BIT(2) +#define RV3032_STATUS_AF BIT(3) +#define RV3032_STATUS_TF BIT(4) +#define RV3032_STATUS_UF BIT(5) +#define RV3032_STATUS_TLF BIT(6) +#define RV3032_STATUS_THF BIT(7) + +#define RV3032_TLSB_CLKF BIT(1) +#define RV3032_TLSB_EEBUSY BIT(2) +#define RV3032_TLSB_TEMP GENMASK(7, 4) + +#define RV3032_CLKOUT2_HFD_MSK GENMASK(4, 0) +#define RV3032_CLKOUT2_FD_MSK GENMASK(6, 5) +#define RV3032_CLKOUT2_OS BIT(7) + +#define RV3032_CTRL1_EERD BIT(2) + +#define RV3032_CTRL2_STOP BIT(0) +#define RV3032_CTRL2_EIE BIT(2) +#define RV3032_CTRL2_AIE BIT(3) +#define RV3032_CTRL2_TIE BIT(4) +#define RV3032_CTRL2_UIE BIT(5) +#define RV3032_CTRL2_CLKIE BIT(6) +#define RV3032_CTRL2_TSE BIT(7) + +#define RV3032_PMU_TCM GENMASK(1, 0) +#define RV3032_PMU_TCR GENMASK(3, 2) +#define RV3032_PMU_BSM GENMASK(5, 4) +#define RV3032_PMU_NCLKE BIT(6) + +#define RV3032_PMU_BSM_DSM 1 +#define RV3032_PMU_BSM_LSM 2 + +#define RV3032_OFFSET_MSK GENMASK(5, 0) + +#define RV3032_EVT_CTRL_TSR BIT(2) + +#define RV3032_EEPROM_CMD_UPDATE 0x11 +#define RV3032_EEPROM_CMD_WRITE 0x21 +#define RV3032_EEPROM_CMD_READ 0x22 + +#define RV3032_EEPROM_USER 0xCB + +#define RV3032_EEBUSY_POLL 10000 +#define RV3032_EEBUSY_TIMEOUT 100000 + +#define OFFSET_STEP_PPT 238419 + +struct rv3032_data { + struct regmap *regmap; + struct rtc_device *rtc; + bool trickle_charger_set; +#ifdef CONFIG_COMMON_CLK + struct clk_hw clkout_hw; +#endif +}; + +static u16 rv3032_trickle_resistors[] = {1000, 2000, 7000, 11000}; +static u16 rv3032_trickle_voltages[] = {0, 1750, 3000, 4400}; + +static int rv3032_exit_eerd(struct rv3032_data *rv3032, u32 eerd) +{ + if (eerd) + return 0; + + return regmap_update_bits(rv3032->regmap, RV3032_CTRL1, RV3032_CTRL1_EERD, 0); +} + +static int rv3032_enter_eerd(struct rv3032_data *rv3032, u32 *eerd) +{ + u32 ctrl1, status; + int ret; + + ret = regmap_read(rv3032->regmap, RV3032_CTRL1, &ctrl1); + if (ret) + return ret; + + *eerd = ctrl1 & RV3032_CTRL1_EERD; + if (*eerd) + return 0; + + ret = regmap_update_bits(rv3032->regmap, RV3032_CTRL1, + RV3032_CTRL1_EERD, RV3032_CTRL1_EERD); + if (ret) + return ret; + + ret = regmap_read_poll_timeout(rv3032->regmap, RV3032_TLSB, status, + !(status & RV3032_TLSB_EEBUSY), + RV3032_EEBUSY_POLL, RV3032_EEBUSY_TIMEOUT); + if (ret) { + rv3032_exit_eerd(rv3032, *eerd); + + return ret; + } + + return 0; +} + +static int rv3032_update_cfg(struct rv3032_data *rv3032, unsigned int reg, + unsigned int mask, unsigned int val) +{ + u32 status, eerd; + int ret; + + ret = rv3032_enter_eerd(rv3032, &eerd); + if (ret) + return ret; + + ret = regmap_update_bits(rv3032->regmap, reg, mask, val); + if (ret) + goto exit_eerd; + + ret = regmap_write(rv3032->regmap, RV3032_EEPROM_CMD, RV3032_EEPROM_CMD_UPDATE); + if (ret) + goto exit_eerd; + + usleep_range(46000, RV3032_EEBUSY_TIMEOUT); + + ret = regmap_read_poll_timeout(rv3032->regmap, RV3032_TLSB, status, + !(status & RV3032_TLSB_EEBUSY), + RV3032_EEBUSY_POLL, RV3032_EEBUSY_TIMEOUT); + +exit_eerd: + rv3032_exit_eerd(rv3032, eerd); + + return ret; +} + +static irqreturn_t rv3032_handle_irq(int irq, void *dev_id) +{ + struct rv3032_data *rv3032 = dev_id; + unsigned long events = 0; + u32 status = 0, ctrl = 0; + + if (regmap_read(rv3032->regmap, RV3032_STATUS, &status) < 0 || + status == 0) { + return IRQ_NONE; + } + + if (status & RV3032_STATUS_TF) { + status |= RV3032_STATUS_TF; + ctrl |= RV3032_CTRL2_TIE; + events |= RTC_PF; + } + + if (status & RV3032_STATUS_AF) { + status |= RV3032_STATUS_AF; + ctrl |= RV3032_CTRL2_AIE; + events |= RTC_AF; + } + + if (status & RV3032_STATUS_UF) { + status |= RV3032_STATUS_UF; + ctrl |= RV3032_CTRL2_UIE; + events |= RTC_UF; + } + + if (events) { + rtc_update_irq(rv3032->rtc, 1, events); + regmap_update_bits(rv3032->regmap, RV3032_STATUS, status, 0); + regmap_update_bits(rv3032->regmap, RV3032_CTRL2, ctrl, 0); + } + + return IRQ_HANDLED; +} + +static int rv3032_get_time(struct device *dev, struct rtc_time *tm) +{ + struct rv3032_data *rv3032 = dev_get_drvdata(dev); + u8 date[7]; + int ret, status; + + ret = regmap_read(rv3032->regmap, RV3032_STATUS, &status); + if (ret < 0) + return ret; + + if (status & (RV3032_STATUS_PORF | RV3032_STATUS_VLF)) + return -EINVAL; + + ret = regmap_bulk_read(rv3032->regmap, RV3032_SEC, date, sizeof(date)); + if (ret) + return ret; + + tm->tm_sec = bcd2bin(date[0] & 0x7f); + tm->tm_min = bcd2bin(date[1] & 0x7f); + tm->tm_hour = bcd2bin(date[2] & 0x3f); + tm->tm_wday = date[3] & 0x7; + tm->tm_mday = bcd2bin(date[4] & 0x3f); + tm->tm_mon = bcd2bin(date[5] & 0x1f) - 1; + tm->tm_year = bcd2bin(date[6]) + 100; + + return 0; +} + +static int rv3032_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rv3032_data *rv3032 = dev_get_drvdata(dev); + u8 date[7]; + int ret; + + date[0] = bin2bcd(tm->tm_sec); + date[1] = bin2bcd(tm->tm_min); + date[2] = bin2bcd(tm->tm_hour); + date[3] = tm->tm_wday; + date[4] = bin2bcd(tm->tm_mday); + date[5] = bin2bcd(tm->tm_mon + 1); + date[6] = bin2bcd(tm->tm_year - 100); + + ret = regmap_bulk_write(rv3032->regmap, RV3032_SEC, date, + sizeof(date)); + if (ret) + return ret; + + ret = regmap_update_bits(rv3032->regmap, RV3032_STATUS, + RV3032_STATUS_PORF | RV3032_STATUS_VLF, 0); + + return ret; +} + +static int rv3032_get_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rv3032_data *rv3032 = dev_get_drvdata(dev); + u8 alarmvals[3]; + int status, ctrl, ret; + + ret = regmap_bulk_read(rv3032->regmap, RV3032_ALARM_MIN, alarmvals, + sizeof(alarmvals)); + if (ret) + return ret; + + ret = regmap_read(rv3032->regmap, RV3032_STATUS, &status); + if (ret < 0) + return ret; + + ret = regmap_read(rv3032->regmap, RV3032_CTRL2, &ctrl); + if (ret < 0) + return ret; + + alrm->time.tm_sec = 0; + alrm->time.tm_min = bcd2bin(alarmvals[0] & 0x7f); + alrm->time.tm_hour = bcd2bin(alarmvals[1] & 0x3f); + alrm->time.tm_mday = bcd2bin(alarmvals[2] & 0x3f); + + alrm->enabled = !!(ctrl & RV3032_CTRL2_AIE); + alrm->pending = (status & RV3032_STATUS_AF) && alrm->enabled; + + return 0; +} + +static int rv3032_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rv3032_data *rv3032 = dev_get_drvdata(dev); + u8 alarmvals[3]; + u8 ctrl = 0; + int ret; + + ret = regmap_update_bits(rv3032->regmap, RV3032_CTRL2, + RV3032_CTRL2_AIE | RV3032_CTRL2_UIE, 0); + if (ret) + return ret; + + alarmvals[0] = bin2bcd(alrm->time.tm_min); + alarmvals[1] = bin2bcd(alrm->time.tm_hour); + alarmvals[2] = bin2bcd(alrm->time.tm_mday); + + ret = regmap_update_bits(rv3032->regmap, RV3032_STATUS, + RV3032_STATUS_AF, 0); + if (ret) + return ret; + + ret = regmap_bulk_write(rv3032->regmap, RV3032_ALARM_MIN, alarmvals, + sizeof(alarmvals)); + if (ret) + return ret; + + if (alrm->enabled) { + if (rv3032->rtc->uie_rtctimer.enabled) + ctrl |= RV3032_CTRL2_UIE; + if (rv3032->rtc->aie_timer.enabled) + ctrl |= RV3032_CTRL2_AIE; + } + + ret = regmap_update_bits(rv3032->regmap, RV3032_CTRL2, + RV3032_CTRL2_UIE | RV3032_CTRL2_AIE, ctrl); + + return ret; +} + +static int rv3032_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct rv3032_data *rv3032 = dev_get_drvdata(dev); + int ctrl = 0, ret; + + if (enabled) { + if (rv3032->rtc->uie_rtctimer.enabled) + ctrl |= RV3032_CTRL2_UIE; + if (rv3032->rtc->aie_timer.enabled) + ctrl |= RV3032_CTRL2_AIE; + } + + ret = regmap_update_bits(rv3032->regmap, RV3032_STATUS, + RV3032_STATUS_AF | RV3032_STATUS_UF, 0); + if (ret) + return ret; + + ret = regmap_update_bits(rv3032->regmap, RV3032_CTRL2, + RV3032_CTRL2_UIE | RV3032_CTRL2_AIE, ctrl); + if (ret) + return ret; + + return 0; +} + +static int rv3032_read_offset(struct device *dev, long *offset) +{ + struct rv3032_data *rv3032 = dev_get_drvdata(dev); + int ret, value, steps; + + ret = regmap_read(rv3032->regmap, RV3032_OFFSET, &value); + if (ret < 0) + return ret; + + steps = FIELD_GET_SIGNED(RV3032_OFFSET_MSK, value); + + *offset = DIV_ROUND_CLOSEST(steps * OFFSET_STEP_PPT, 1000); + + return 0; +} + +static int rv3032_set_offset(struct device *dev, long offset) +{ + struct rv3032_data *rv3032 = dev_get_drvdata(dev); + + offset = clamp(offset, -7629L, 7391L) * 1000; + offset = DIV_ROUND_CLOSEST(offset, OFFSET_STEP_PPT); + + return rv3032_update_cfg(rv3032, RV3032_OFFSET, RV3032_OFFSET_MSK, + FIELD_PREP(RV3032_OFFSET_MSK, offset)); +} + +static int rv3032_param_get(struct device *dev, struct rtc_param *param) +{ + struct rv3032_data *rv3032 = dev_get_drvdata(dev); + int ret; + + switch(param->param) { + u32 value; + + case RTC_PARAM_BACKUP_SWITCH_MODE: + ret = regmap_read(rv3032->regmap, RV3032_PMU, &value); + if (ret < 0) + return ret; + + value = FIELD_GET(RV3032_PMU_BSM, value); + + switch(value) { + case RV3032_PMU_BSM_DSM: + param->uvalue = RTC_BSM_DIRECT; + break; + case RV3032_PMU_BSM_LSM: + param->uvalue = RTC_BSM_LEVEL; + break; + default: + param->uvalue = RTC_BSM_DISABLED; + } + + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int rv3032_param_set(struct device *dev, struct rtc_param *param) +{ + struct rv3032_data *rv3032 = dev_get_drvdata(dev); + + switch(param->param) { + u8 mode; + case RTC_PARAM_BACKUP_SWITCH_MODE: + if (rv3032->trickle_charger_set) + return -EINVAL; + + switch (param->uvalue) { + case RTC_BSM_DISABLED: + mode = 0; + break; + case RTC_BSM_DIRECT: + mode = RV3032_PMU_BSM_DSM; + break; + case RTC_BSM_LEVEL: + mode = RV3032_PMU_BSM_LSM; + break; + default: + return -EINVAL; + } + + return rv3032_update_cfg(rv3032, RV3032_PMU, RV3032_PMU_BSM, + FIELD_PREP(RV3032_PMU_BSM, mode)); + + default: + return -EINVAL; + } + + return 0; +} + +static int rv3032_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct rv3032_data *rv3032 = dev_get_drvdata(dev); + int status, val = 0, ret = 0; + + switch (cmd) { + case RTC_VL_READ: + ret = regmap_read(rv3032->regmap, RV3032_STATUS, &status); + if (ret < 0) + return ret; + + if (status & (RV3032_STATUS_PORF | RV3032_STATUS_VLF)) + val = RTC_VL_DATA_INVALID; + return put_user(val, (unsigned int __user *)arg); + + default: + return -ENOIOCTLCMD; + } +} + +static int rv3032_nvram_write(void *priv, unsigned int offset, void *val, size_t bytes) +{ + return regmap_bulk_write(priv, RV3032_RAM1 + offset, val, bytes); +} + +static int rv3032_nvram_read(void *priv, unsigned int offset, void *val, size_t bytes) +{ + return regmap_bulk_read(priv, RV3032_RAM1 + offset, val, bytes); +} + +static int rv3032_eeprom_write(void *priv, unsigned int offset, void *val, size_t bytes) +{ + struct rv3032_data *rv3032 = priv; + u32 status, eerd; + int i, ret; + u8 *buf = val; + + ret = rv3032_enter_eerd(rv3032, &eerd); + if (ret) + return ret; + + for (i = 0; i < bytes; i++) { + ret = regmap_write(rv3032->regmap, RV3032_EEPROM_ADDR, + RV3032_EEPROM_USER + offset + i); + if (ret) + goto exit_eerd; + + ret = regmap_write(rv3032->regmap, RV3032_EEPROM_DATA, buf[i]); + if (ret) + goto exit_eerd; + + ret = regmap_write(rv3032->regmap, RV3032_EEPROM_CMD, + RV3032_EEPROM_CMD_WRITE); + if (ret) + goto exit_eerd; + + usleep_range(RV3032_EEBUSY_POLL, RV3032_EEBUSY_TIMEOUT); + + ret = regmap_read_poll_timeout(rv3032->regmap, RV3032_TLSB, status, + !(status & RV3032_TLSB_EEBUSY), + RV3032_EEBUSY_POLL, RV3032_EEBUSY_TIMEOUT); + if (ret) + goto exit_eerd; + } + +exit_eerd: + rv3032_exit_eerd(rv3032, eerd); + + return ret; +} + +static int rv3032_eeprom_read(void *priv, unsigned int offset, void *val, size_t bytes) +{ + struct rv3032_data *rv3032 = priv; + u32 status, eerd, data; + int i, ret; + u8 *buf = val; + + ret = rv3032_enter_eerd(rv3032, &eerd); + if (ret) + return ret; + + for (i = 0; i < bytes; i++) { + ret = regmap_write(rv3032->regmap, RV3032_EEPROM_ADDR, + RV3032_EEPROM_USER + offset + i); + if (ret) + goto exit_eerd; + + ret = regmap_write(rv3032->regmap, RV3032_EEPROM_CMD, + RV3032_EEPROM_CMD_READ); + if (ret) + goto exit_eerd; + + ret = regmap_read_poll_timeout(rv3032->regmap, RV3032_TLSB, status, + !(status & RV3032_TLSB_EEBUSY), + RV3032_EEBUSY_POLL, RV3032_EEBUSY_TIMEOUT); + if (ret) + goto exit_eerd; + + ret = regmap_read(rv3032->regmap, RV3032_EEPROM_DATA, &data); + if (ret) + goto exit_eerd; + buf[i] = data; + } + +exit_eerd: + rv3032_exit_eerd(rv3032, eerd); + + return ret; +} + +static int rv3032_trickle_charger_setup(struct device *dev, struct rv3032_data *rv3032) +{ + u32 val, ohms, voltage; + int i; + + val = FIELD_PREP(RV3032_PMU_TCM, 1) | FIELD_PREP(RV3032_PMU_BSM, RV3032_PMU_BSM_DSM); + if (!device_property_read_u32(dev, "trickle-voltage-millivolt", &voltage)) { + for (i = 0; i < ARRAY_SIZE(rv3032_trickle_voltages); i++) + if (voltage == rv3032_trickle_voltages[i]) + break; + if (i < ARRAY_SIZE(rv3032_trickle_voltages)) + val = FIELD_PREP(RV3032_PMU_TCM, i) | + FIELD_PREP(RV3032_PMU_BSM, RV3032_PMU_BSM_LSM); + } + + if (device_property_read_u32(dev, "trickle-resistor-ohms", &ohms)) + return 0; + + for (i = 0; i < ARRAY_SIZE(rv3032_trickle_resistors); i++) + if (ohms == rv3032_trickle_resistors[i]) + break; + + if (i >= ARRAY_SIZE(rv3032_trickle_resistors)) { + dev_warn(dev, "invalid trickle resistor value\n"); + + return 0; + } + + rv3032->trickle_charger_set = true; + + return rv3032_update_cfg(rv3032, RV3032_PMU, + RV3032_PMU_TCR | RV3032_PMU_TCM | RV3032_PMU_BSM, + val | FIELD_PREP(RV3032_PMU_TCR, i)); +} + +#ifdef CONFIG_COMMON_CLK +#define clkout_hw_to_rv3032(hw) container_of(hw, struct rv3032_data, clkout_hw) + +static int clkout_xtal_rates[] = { + 32768, + 1024, + 64, + 1, +}; + +#define RV3032_HFD_STEP 8192 + +static unsigned long rv3032_clkout_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + int clkout, ret; + struct rv3032_data *rv3032 = clkout_hw_to_rv3032(hw); + + ret = regmap_read(rv3032->regmap, RV3032_CLKOUT2, &clkout); + if (ret < 0) + return 0; + + if (clkout & RV3032_CLKOUT2_OS) { + unsigned long rate = FIELD_GET(RV3032_CLKOUT2_HFD_MSK, clkout) << 8; + + ret = regmap_read(rv3032->regmap, RV3032_CLKOUT1, &clkout); + if (ret < 0) + return 0; + + rate += clkout + 1; + + return rate * RV3032_HFD_STEP; + } + + return clkout_xtal_rates[FIELD_GET(RV3032_CLKOUT2_FD_MSK, clkout)]; +} + +static int rv3032_clkout_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + int i, hfd; + + if (req->rate < RV3032_HFD_STEP) + for (i = 0; i < ARRAY_SIZE(clkout_xtal_rates); i++) + if (clkout_xtal_rates[i] <= req->rate) { + req->rate = clkout_xtal_rates[i]; + + return 0; + } + + hfd = DIV_ROUND_CLOSEST(req->rate, RV3032_HFD_STEP); + + req->rate = RV3032_HFD_STEP * clamp(hfd, 0, 8192); + + return 0; +} + +static int rv3032_clkout_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct rv3032_data *rv3032 = clkout_hw_to_rv3032(hw); + u32 status, eerd; + int i, hfd, ret; + + for (i = 0; i < ARRAY_SIZE(clkout_xtal_rates); i++) { + if (clkout_xtal_rates[i] == rate) { + return rv3032_update_cfg(rv3032, RV3032_CLKOUT2, 0xff, + FIELD_PREP(RV3032_CLKOUT2_FD_MSK, i)); + } + } + + hfd = DIV_ROUND_CLOSEST(rate, RV3032_HFD_STEP); + hfd = clamp(hfd, 1, 8192) - 1; + + ret = rv3032_enter_eerd(rv3032, &eerd); + if (ret) + return ret; + + ret = regmap_write(rv3032->regmap, RV3032_CLKOUT1, hfd & 0xff); + if (ret) + goto exit_eerd; + + ret = regmap_write(rv3032->regmap, RV3032_CLKOUT2, RV3032_CLKOUT2_OS | + FIELD_PREP(RV3032_CLKOUT2_HFD_MSK, hfd >> 8)); + if (ret) + goto exit_eerd; + + ret = regmap_write(rv3032->regmap, RV3032_EEPROM_CMD, RV3032_EEPROM_CMD_UPDATE); + if (ret) + goto exit_eerd; + + usleep_range(46000, RV3032_EEBUSY_TIMEOUT); + + ret = regmap_read_poll_timeout(rv3032->regmap, RV3032_TLSB, status, + !(status & RV3032_TLSB_EEBUSY), + RV3032_EEBUSY_POLL, RV3032_EEBUSY_TIMEOUT); + +exit_eerd: + rv3032_exit_eerd(rv3032, eerd); + + return ret; +} + +static int rv3032_clkout_prepare(struct clk_hw *hw) +{ + struct rv3032_data *rv3032 = clkout_hw_to_rv3032(hw); + + return rv3032_update_cfg(rv3032, RV3032_PMU, RV3032_PMU_NCLKE, 0); +} + +static void rv3032_clkout_unprepare(struct clk_hw *hw) +{ + struct rv3032_data *rv3032 = clkout_hw_to_rv3032(hw); + + rv3032_update_cfg(rv3032, RV3032_PMU, RV3032_PMU_NCLKE, RV3032_PMU_NCLKE); +} + +static int rv3032_clkout_is_prepared(struct clk_hw *hw) +{ + int val, ret; + struct rv3032_data *rv3032 = clkout_hw_to_rv3032(hw); + + ret = regmap_read(rv3032->regmap, RV3032_PMU, &val); + if (ret < 0) + return ret; + + return !(val & RV3032_PMU_NCLKE); +} + +static const struct clk_ops rv3032_clkout_ops = { + .prepare = rv3032_clkout_prepare, + .unprepare = rv3032_clkout_unprepare, + .is_prepared = rv3032_clkout_is_prepared, + .recalc_rate = rv3032_clkout_recalc_rate, + .determine_rate = rv3032_clkout_determine_rate, + .set_rate = rv3032_clkout_set_rate, +}; + +static int rv3032_clkout_register_clk(struct rv3032_data *rv3032, + struct i2c_client *client) +{ + int ret; + struct clk *clk; + struct clk_init_data init = {}; + struct device_node *node = client->dev.of_node; + + ret = regmap_update_bits(rv3032->regmap, RV3032_TLSB, RV3032_TLSB_CLKF, 0); + if (ret < 0) + return ret; + + ret = regmap_update_bits(rv3032->regmap, RV3032_CTRL2, RV3032_CTRL2_CLKIE, 0); + if (ret < 0) + return ret; + + ret = regmap_write(rv3032->regmap, RV3032_CLK_IRQ, 0); + if (ret < 0) + return ret; + + init.name = "rv3032-clkout"; + init.ops = &rv3032_clkout_ops; + init.flags = 0; + init.parent_names = NULL; + init.num_parents = 0; + rv3032->clkout_hw.init = &init; + + of_property_read_string(node, "clock-output-names", &init.name); + + clk = devm_clk_register(&client->dev, &rv3032->clkout_hw); + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); + + return 0; +} +#endif + +static int rv3032_hwmon_read_temp(struct device *dev, long *mC) +{ + struct rv3032_data *rv3032 = dev_get_drvdata(dev); + u8 buf[2]; + int temp, prev = 0; + int ret; + + ret = regmap_bulk_read(rv3032->regmap, RV3032_TLSB, buf, sizeof(buf)); + if (ret) + return ret; + + temp = sign_extend32(buf[1], 7) << 4; + temp |= FIELD_GET(RV3032_TLSB_TEMP, buf[0]); + + /* No blocking or shadowing on RV3032_TLSB and RV3032_TMSB */ + do { + prev = temp; + + ret = regmap_bulk_read(rv3032->regmap, RV3032_TLSB, buf, sizeof(buf)); + if (ret) + return ret; + + temp = sign_extend32(buf[1], 7) << 4; + temp |= FIELD_GET(RV3032_TLSB_TEMP, buf[0]); + } while (temp != prev); + + *mC = (temp * 1000) / 16; + + return 0; +} + +static umode_t rv3032_hwmon_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (type != hwmon_temp) + return 0; + + switch (attr) { + case hwmon_temp_input: + return 0444; + default: + return 0; + } +} + +static int rv3032_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *temp) +{ + int err; + + switch (attr) { + case hwmon_temp_input: + err = rv3032_hwmon_read_temp(dev, temp); + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +static const struct hwmon_channel_info * const rv3032_hwmon_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST), + NULL +}; + +static const struct hwmon_ops rv3032_hwmon_hwmon_ops = { + .is_visible = rv3032_hwmon_is_visible, + .read = rv3032_hwmon_read, +}; + +static const struct hwmon_chip_info rv3032_hwmon_chip_info = { + .ops = &rv3032_hwmon_hwmon_ops, + .info = rv3032_hwmon_info, +}; + +static void rv3032_hwmon_register(struct device *dev) +{ + struct rv3032_data *rv3032 = dev_get_drvdata(dev); + + if (!IS_REACHABLE(CONFIG_HWMON)) + return; + + devm_hwmon_device_register_with_info(dev, "rv3032", rv3032, &rv3032_hwmon_chip_info, NULL); +} + +static const struct rtc_class_ops rv3032_rtc_ops = { + .read_time = rv3032_get_time, + .set_time = rv3032_set_time, + .read_offset = rv3032_read_offset, + .set_offset = rv3032_set_offset, + .ioctl = rv3032_ioctl, + .read_alarm = rv3032_get_alarm, + .set_alarm = rv3032_set_alarm, + .alarm_irq_enable = rv3032_alarm_irq_enable, + .param_get = rv3032_param_get, + .param_set = rv3032_param_set, +}; + +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xCA, +}; + +static int rv3032_probe(struct i2c_client *client) +{ + struct rv3032_data *rv3032; + int ret, status; + struct nvmem_config nvmem_cfg = { + .name = "rv3032_nvram", + .word_size = 1, + .stride = 1, + .size = 16, + .type = NVMEM_TYPE_BATTERY_BACKED, + .reg_read = rv3032_nvram_read, + .reg_write = rv3032_nvram_write, + }; + struct nvmem_config eeprom_cfg = { + .name = "rv3032_eeprom", + .word_size = 1, + .stride = 1, + .size = 32, + .type = NVMEM_TYPE_EEPROM, + .reg_read = rv3032_eeprom_read, + .reg_write = rv3032_eeprom_write, + }; + + rv3032 = devm_kzalloc(&client->dev, sizeof(struct rv3032_data), + GFP_KERNEL); + if (!rv3032) + return -ENOMEM; + + rv3032->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(rv3032->regmap)) + return PTR_ERR(rv3032->regmap); + + i2c_set_clientdata(client, rv3032); + + ret = regmap_read(rv3032->regmap, RV3032_STATUS, &status); + if (ret < 0) + return ret; + + rv3032->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(rv3032->rtc)) + return PTR_ERR(rv3032->rtc); + + if (client->irq > 0) { + unsigned long irqflags = IRQF_TRIGGER_LOW; + + if (dev_fwnode(&client->dev)) + irqflags = 0; + + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, rv3032_handle_irq, + irqflags | IRQF_ONESHOT, + "rv3032", rv3032); + if (ret) { + dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n"); + client->irq = 0; + } + } + if (!client->irq) + clear_bit(RTC_FEATURE_ALARM, rv3032->rtc->features); + + rv3032_trickle_charger_setup(&client->dev, rv3032); + + set_bit(RTC_FEATURE_BACKUP_SWITCH_MODE, rv3032->rtc->features); + set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rv3032->rtc->features); + + rv3032->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rv3032->rtc->range_max = RTC_TIMESTAMP_END_2099; + rv3032->rtc->ops = &rv3032_rtc_ops; + ret = devm_rtc_register_device(rv3032->rtc); + if (ret) + return ret; + + nvmem_cfg.priv = rv3032->regmap; + devm_rtc_nvmem_register(rv3032->rtc, &nvmem_cfg); + eeprom_cfg.priv = rv3032; + devm_rtc_nvmem_register(rv3032->rtc, &eeprom_cfg); + +#ifdef CONFIG_COMMON_CLK + rv3032_clkout_register_clk(rv3032, client); +#endif + + rv3032_hwmon_register(&client->dev); + + return 0; +} + +static const struct acpi_device_id rv3032_i2c_acpi_match[] = { + { "MCRY3032" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, rv3032_i2c_acpi_match); + +static const __maybe_unused struct of_device_id rv3032_of_match[] = { + { .compatible = "microcrystal,rv3032", }, + { } +}; +MODULE_DEVICE_TABLE(of, rv3032_of_match); + +static struct i2c_driver rv3032_driver = { + .driver = { + .name = "rtc-rv3032", + .acpi_match_table = rv3032_i2c_acpi_match, + .of_match_table = of_match_ptr(rv3032_of_match), + }, + .probe = rv3032_probe, +}; +module_i2c_driver(rv3032_driver); + +MODULE_AUTHOR("Alexandre Belloni "); +MODULE_DESCRIPTION("Micro Crystal RV3032 RTC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-rv8803.c b/drivers/rtc/rtc-rv8803.c new file mode 100644 index 000000000..b9b5fee16 --- /dev/null +++ b/drivers/rtc/rtc-rv8803.c @@ -0,0 +1,772 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RTC driver for the Micro Crystal RV8803 + * + * Copyright (C) 2015 Micro Crystal SA + * Alexandre Belloni + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RV8803_I2C_TRY_COUNT 4 + +#define RV8803_SEC 0x00 +#define RV8803_MIN 0x01 +#define RV8803_HOUR 0x02 +#define RV8803_WEEK 0x03 +#define RV8803_DAY 0x04 +#define RV8803_MONTH 0x05 +#define RV8803_YEAR 0x06 +#define RV8803_RAM 0x07 +#define RV8803_ALARM_MIN 0x08 +#define RV8803_ALARM_HOUR 0x09 +#define RV8803_ALARM_WEEK_OR_DAY 0x0A +#define RV8803_EXT 0x0D +#define RV8803_FLAG 0x0E +#define RV8803_CTRL 0x0F +#define RV8803_OSC_OFFSET 0x2C + +#define RV8803_EXT_WADA BIT(6) + +#define RV8803_FLAG_V1F BIT(0) +#define RV8803_FLAG_V2F BIT(1) +#define RV8803_FLAG_AF BIT(3) +#define RV8803_FLAG_TF BIT(4) +#define RV8803_FLAG_UF BIT(5) + +#define RV8803_CTRL_RESET BIT(0) + +#define RV8803_CTRL_EIE BIT(2) +#define RV8803_CTRL_AIE BIT(3) +#define RV8803_CTRL_TIE BIT(4) +#define RV8803_CTRL_UIE BIT(5) + +#define RX8803_CTRL_CSEL GENMASK(7, 6) + +#define RX8900_BACKUP_CTRL 0x18 +#define RX8900_FLAG_SWOFF BIT(2) +#define RX8900_FLAG_VDETOFF BIT(3) + +enum rv8803_type { + rv_8803, + rx_8803, + rx_8804, + rx_8900 +}; + +struct rv8803_data { + struct i2c_client *client; + struct rtc_device *rtc; + struct mutex flags_lock; + u8 ctrl; + u8 backup; + u8 alarm_invalid:1; + enum rv8803_type type; +}; + +static int rv8803_read_reg(const struct i2c_client *client, u8 reg) +{ + int try = RV8803_I2C_TRY_COUNT; + s32 ret; + + /* + * There is a 61µs window during which the RTC does not acknowledge I2C + * transfers. In that case, ensure that there are multiple attempts. + */ + do + ret = i2c_smbus_read_byte_data(client, reg); + while ((ret == -ENXIO || ret == -EIO) && --try); + if (ret < 0) + dev_err(&client->dev, "Unable to read register 0x%02x\n", reg); + + return ret; +} + +static int rv8803_read_regs(const struct i2c_client *client, + u8 reg, u8 count, u8 *values) +{ + int try = RV8803_I2C_TRY_COUNT; + s32 ret; + + do + ret = i2c_smbus_read_i2c_block_data(client, reg, count, values); + while ((ret == -ENXIO || ret == -EIO) && --try); + if (ret != count) { + dev_err(&client->dev, + "Unable to read registers 0x%02x..0x%02x\n", + reg, reg + count - 1); + return ret < 0 ? ret : -EIO; + } + + return 0; +} + +static int rv8803_write_reg(const struct i2c_client *client, u8 reg, u8 value) +{ + int try = RV8803_I2C_TRY_COUNT; + s32 ret; + + do + ret = i2c_smbus_write_byte_data(client, reg, value); + while ((ret == -ENXIO || ret == -EIO) && --try); + if (ret) + dev_err(&client->dev, "Unable to write register 0x%02x\n", reg); + + return ret; +} + +static int rv8803_write_regs(const struct i2c_client *client, + u8 reg, u8 count, const u8 *values) +{ + int try = RV8803_I2C_TRY_COUNT; + s32 ret; + + do + ret = i2c_smbus_write_i2c_block_data(client, reg, count, + values); + while ((ret == -ENXIO || ret == -EIO) && --try); + if (ret) + dev_err(&client->dev, + "Unable to write registers 0x%02x..0x%02x\n", + reg, reg + count - 1); + + return ret; +} + +static int rv8803_regs_init(struct rv8803_data *rv8803) +{ + int ret; + + ret = rv8803_write_reg(rv8803->client, RV8803_OSC_OFFSET, 0x00); + if (ret) + return ret; + + ret = rv8803_write_reg(rv8803->client, RV8803_CTRL, + FIELD_PREP(RX8803_CTRL_CSEL, 1)); /* 2s */ + if (ret) + return ret; + + ret = rv8803_write_regs(rv8803->client, RV8803_ALARM_MIN, 3, + (u8[]){ 0, 0, 0 }); + if (ret) + return ret; + + return rv8803_write_reg(rv8803->client, RV8803_RAM, 0x00); +} + +static int rv8803_regs_configure(struct rv8803_data *rv8803); + +static int rv8803_regs_reset(struct rv8803_data *rv8803, bool full) +{ + /* + * The RV-8803 resets all registers to POR defaults after voltage-loss, + * the Epson RTCs don't, so we manually reset the remainder here. + */ + if (full || rv8803->type == rx_8803 || rv8803->type == rx_8900) { + int ret = rv8803_regs_init(rv8803); + if (ret) + return ret; + } + + return rv8803_regs_configure(rv8803); +} + +static irqreturn_t rv8803_handle_irq(int irq, void *dev_id) +{ + struct i2c_client *client = dev_id; + struct rv8803_data *rv8803 = i2c_get_clientdata(client); + unsigned long events = 0; + int flags; + + mutex_lock(&rv8803->flags_lock); + + flags = rv8803_read_reg(client, RV8803_FLAG); + if (flags <= 0) { + mutex_unlock(&rv8803->flags_lock); + return IRQ_NONE; + } + + if (flags & RV8803_FLAG_V1F) + dev_warn(&client->dev, "Voltage low, temperature compensation stopped.\n"); + + if (flags & RV8803_FLAG_V2F) + dev_warn(&client->dev, "Voltage low, data loss detected.\n"); + + if (flags & RV8803_FLAG_TF) { + flags &= ~RV8803_FLAG_TF; + rv8803->ctrl &= ~RV8803_CTRL_TIE; + events |= RTC_PF; + } + + if (flags & RV8803_FLAG_AF) { + flags &= ~RV8803_FLAG_AF; + rv8803->ctrl &= ~RV8803_CTRL_AIE; + events |= RTC_AF; + } + + if (flags & RV8803_FLAG_UF) { + flags &= ~RV8803_FLAG_UF; + rv8803->ctrl &= ~RV8803_CTRL_UIE; + events |= RTC_UF; + } + + if (events) { + rtc_update_irq(rv8803->rtc, 1, events); + rv8803_write_reg(client, RV8803_FLAG, flags); + rv8803_write_reg(rv8803->client, RV8803_CTRL, rv8803->ctrl); + } + + mutex_unlock(&rv8803->flags_lock); + + return IRQ_HANDLED; +} + +static int rv8803_get_time(struct device *dev, struct rtc_time *tm) +{ + struct rv8803_data *rv8803 = dev_get_drvdata(dev); + u8 date1[7]; + u8 date2[7]; + u8 *date = date1; + int ret, flags; + + if (rv8803->alarm_invalid) { + dev_warn(dev, "Corruption detected, data may be invalid.\n"); + return -EINVAL; + } + + flags = rv8803_read_reg(rv8803->client, RV8803_FLAG); + if (flags < 0) + return flags; + + if (flags & RV8803_FLAG_V2F) { + dev_warn(dev, "Voltage low, data is invalid.\n"); + return -EINVAL; + } + + ret = rv8803_read_regs(rv8803->client, RV8803_SEC, 7, date); + if (ret) + return ret; + + if ((date1[RV8803_SEC] & 0x7f) == bin2bcd(59)) { + ret = rv8803_read_regs(rv8803->client, RV8803_SEC, 7, date2); + if (ret) + return ret; + + if ((date2[RV8803_SEC] & 0x7f) != bin2bcd(59)) + date = date2; + } + + tm->tm_sec = bcd2bin(date[RV8803_SEC] & 0x7f); + tm->tm_min = bcd2bin(date[RV8803_MIN] & 0x7f); + tm->tm_hour = bcd2bin(date[RV8803_HOUR] & 0x3f); + tm->tm_wday = ilog2(date[RV8803_WEEK] & 0x7f); + tm->tm_mday = bcd2bin(date[RV8803_DAY] & 0x3f); + tm->tm_mon = bcd2bin(date[RV8803_MONTH] & 0x1f) - 1; + tm->tm_year = bcd2bin(date[RV8803_YEAR]) + 100; + + return 0; +} + +static int rv8803_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rv8803_data *rv8803 = dev_get_drvdata(dev); + u8 date[7]; + int ctrl, flags, ret; + + ctrl = rv8803_read_reg(rv8803->client, RV8803_CTRL); + if (ctrl < 0) + return ctrl; + + /* Stop the clock */ + ret = rv8803_write_reg(rv8803->client, RV8803_CTRL, + ctrl | RV8803_CTRL_RESET); + if (ret) + return ret; + + date[RV8803_SEC] = bin2bcd(tm->tm_sec); + date[RV8803_MIN] = bin2bcd(tm->tm_min); + date[RV8803_HOUR] = bin2bcd(tm->tm_hour); + date[RV8803_WEEK] = 1 << (tm->tm_wday); + date[RV8803_DAY] = bin2bcd(tm->tm_mday); + date[RV8803_MONTH] = bin2bcd(tm->tm_mon + 1); + date[RV8803_YEAR] = bin2bcd(tm->tm_year - 100); + + ret = rv8803_write_regs(rv8803->client, RV8803_SEC, 7, date); + if (ret) + return ret; + + /* Restart the clock */ + ret = rv8803_write_reg(rv8803->client, RV8803_CTRL, + ctrl & ~RV8803_CTRL_RESET); + if (ret) + return ret; + + mutex_lock(&rv8803->flags_lock); + + flags = rv8803_read_reg(rv8803->client, RV8803_FLAG); + if (flags < 0) { + mutex_unlock(&rv8803->flags_lock); + return flags; + } + + if ((flags & RV8803_FLAG_V2F) || rv8803->alarm_invalid) { + /* + * If we sense corruption in the alarm registers, but see no + * voltage loss flag, we can't rely on other registers having + * sensible values. Reset them fully. + */ + ret = rv8803_regs_reset(rv8803, rv8803->alarm_invalid); + if (ret) { + mutex_unlock(&rv8803->flags_lock); + return ret; + } + + rv8803->alarm_invalid = false; + } + + ret = rv8803_write_reg(rv8803->client, RV8803_FLAG, + flags & ~(RV8803_FLAG_V1F | RV8803_FLAG_V2F)); + + mutex_unlock(&rv8803->flags_lock); + + return ret; +} + +static int rv8803_get_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rv8803_data *rv8803 = dev_get_drvdata(dev); + struct i2c_client *client = rv8803->client; + u8 alarmvals[3]; + int flags, ret; + + ret = rv8803_read_regs(client, RV8803_ALARM_MIN, 3, alarmvals); + if (ret) + return ret; + + flags = rv8803_read_reg(client, RV8803_FLAG); + if (flags < 0) + return flags; + + alarmvals[0] &= 0x7f; + alarmvals[1] &= 0x3f; + alarmvals[2] &= 0x3f; + + if (!bcd_is_valid(alarmvals[0]) || + !bcd_is_valid(alarmvals[1]) || + !bcd_is_valid(alarmvals[2])) + goto err_invalid; + + alrm->time.tm_sec = 0; + alrm->time.tm_min = bcd2bin(alarmvals[0]); + alrm->time.tm_hour = bcd2bin(alarmvals[1]); + alrm->time.tm_mday = bcd2bin(alarmvals[2]); + + alrm->enabled = !!(rv8803->ctrl & RV8803_CTRL_AIE); + alrm->pending = (flags & RV8803_FLAG_AF) && alrm->enabled; + + if ((unsigned int)alrm->time.tm_mday > 31 || + (unsigned int)alrm->time.tm_hour >= 24 || + (unsigned int)alrm->time.tm_min >= 60) + goto err_invalid; + + return 0; + +err_invalid: + rv8803->alarm_invalid = true; + return -EINVAL; +} + +static int rv8803_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rv8803_data *rv8803 = dev_get_drvdata(dev); + u8 alarmvals[3]; + u8 ctrl[2]; + int ret, err; + + /* The alarm has no seconds, round up to nearest minute */ + if (alrm->time.tm_sec) { + time64_t alarm_time = rtc_tm_to_time64(&alrm->time); + + alarm_time += 60 - alrm->time.tm_sec; + rtc_time64_to_tm(alarm_time, &alrm->time); + } + + mutex_lock(&rv8803->flags_lock); + + ret = rv8803_read_regs(client, RV8803_FLAG, 2, ctrl); + if (ret) { + mutex_unlock(&rv8803->flags_lock); + return ret; + } + + alarmvals[0] = bin2bcd(alrm->time.tm_min); + alarmvals[1] = bin2bcd(alrm->time.tm_hour); + alarmvals[2] = bin2bcd(alrm->time.tm_mday); + + if (rv8803->ctrl & (RV8803_CTRL_AIE | RV8803_CTRL_UIE)) { + rv8803->ctrl &= ~(RV8803_CTRL_AIE | RV8803_CTRL_UIE); + err = rv8803_write_reg(rv8803->client, RV8803_CTRL, + rv8803->ctrl); + if (err) { + mutex_unlock(&rv8803->flags_lock); + return err; + } + } + + ctrl[0] &= ~RV8803_FLAG_AF; + err = rv8803_write_reg(rv8803->client, RV8803_FLAG, ctrl[0]); + mutex_unlock(&rv8803->flags_lock); + if (err) + return err; + + err = rv8803_write_regs(rv8803->client, RV8803_ALARM_MIN, 3, alarmvals); + if (err) + return err; + + if (alrm->enabled) { + if (rv8803->rtc->uie_rtctimer.enabled) + rv8803->ctrl |= RV8803_CTRL_UIE; + if (rv8803->rtc->aie_timer.enabled) + rv8803->ctrl |= RV8803_CTRL_AIE; + + err = rv8803_write_reg(rv8803->client, RV8803_CTRL, + rv8803->ctrl); + if (err) + return err; + } + + return 0; +} + +static int rv8803_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rv8803_data *rv8803 = dev_get_drvdata(dev); + int ctrl, flags, err; + + ctrl = rv8803->ctrl; + + if (enabled) { + if (rv8803->rtc->uie_rtctimer.enabled) + ctrl |= RV8803_CTRL_UIE; + if (rv8803->rtc->aie_timer.enabled) + ctrl |= RV8803_CTRL_AIE; + } else { + if (!rv8803->rtc->uie_rtctimer.enabled) + ctrl &= ~RV8803_CTRL_UIE; + if (!rv8803->rtc->aie_timer.enabled) + ctrl &= ~RV8803_CTRL_AIE; + } + + mutex_lock(&rv8803->flags_lock); + flags = rv8803_read_reg(client, RV8803_FLAG); + if (flags < 0) { + mutex_unlock(&rv8803->flags_lock); + return flags; + } + flags &= ~(RV8803_FLAG_AF | RV8803_FLAG_UF); + err = rv8803_write_reg(client, RV8803_FLAG, flags); + mutex_unlock(&rv8803->flags_lock); + if (err) + return err; + + if (ctrl != rv8803->ctrl) { + rv8803->ctrl = ctrl; + err = rv8803_write_reg(client, RV8803_CTRL, rv8803->ctrl); + if (err) + return err; + } + + return 0; +} + +static int rv8803_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rv8803_data *rv8803 = dev_get_drvdata(dev); + unsigned int vl = 0; + int flags, ret = 0; + + switch (cmd) { + case RTC_VL_READ: + flags = rv8803_read_reg(client, RV8803_FLAG); + if (flags < 0) + return flags; + + if (flags & RV8803_FLAG_V1F) { + dev_warn(&client->dev, "Voltage low, temperature compensation stopped.\n"); + vl = RTC_VL_ACCURACY_LOW; + } + + if (flags & RV8803_FLAG_V2F) + vl |= RTC_VL_DATA_INVALID; + + return put_user(vl, (unsigned int __user *)arg); + + case RTC_VL_CLR: + mutex_lock(&rv8803->flags_lock); + flags = rv8803_read_reg(client, RV8803_FLAG); + if (flags < 0) { + mutex_unlock(&rv8803->flags_lock); + return flags; + } + + flags &= ~RV8803_FLAG_V1F; + ret = rv8803_write_reg(client, RV8803_FLAG, flags); + mutex_unlock(&rv8803->flags_lock); + if (ret) + return ret; + + return 0; + + default: + return -ENOIOCTLCMD; + } +} + +static int rv8803_nvram_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + return rv8803_write_reg(priv, RV8803_RAM, *(u8 *)val); +} + +static int rv8803_nvram_read(void *priv, unsigned int offset, + void *val, size_t bytes) +{ + int ret; + + ret = rv8803_read_reg(priv, RV8803_RAM); + if (ret < 0) + return ret; + + *(u8 *)val = ret; + + return 0; +} + +static const struct rtc_class_ops rv8803_rtc_ops = { + .read_time = rv8803_get_time, + .set_time = rv8803_set_time, + .ioctl = rv8803_ioctl, + .read_alarm = rv8803_get_alarm, + .set_alarm = rv8803_set_alarm, + .alarm_irq_enable = rv8803_alarm_irq_enable, +}; + +static int rx8900_trickle_charger_init(struct rv8803_data *rv8803) +{ + struct i2c_client *client = rv8803->client; + struct device_node *node = client->dev.of_node; + int err; + u8 flags; + + if (!node) + return 0; + + if (rv8803->type != rx_8900) + return 0; + + err = i2c_smbus_read_byte_data(rv8803->client, RX8900_BACKUP_CTRL); + if (err < 0) + return err; + + flags = (u8)err; + flags &= ~(RX8900_FLAG_VDETOFF | RX8900_FLAG_SWOFF); + flags |= rv8803->backup; + + return i2c_smbus_write_byte_data(rv8803->client, RX8900_BACKUP_CTRL, + flags); +} + +/* configure registers with values different than the Power-On reset defaults */ +static int rv8803_regs_configure(struct rv8803_data *rv8803) +{ + int err; + + err = rv8803_write_reg(rv8803->client, RV8803_EXT, RV8803_EXT_WADA); + if (err) + return err; + + err = rx8900_trickle_charger_init(rv8803); + if (err) { + dev_err(&rv8803->client->dev, "failed to init charger\n"); + return err; + } + + return 0; +} + +static int rv8803_resume(struct device *dev) +{ + struct rv8803_data *rv8803 = dev_get_drvdata(dev); + + if (rv8803->client->irq > 0 && device_may_wakeup(dev)) + disable_irq_wake(rv8803->client->irq); + + return 0; +} + +static int rv8803_suspend(struct device *dev) +{ + struct rv8803_data *rv8803 = dev_get_drvdata(dev); + + if (rv8803->client->irq > 0 && device_may_wakeup(dev)) + enable_irq_wake(rv8803->client->irq); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(rv8803_pm_ops, rv8803_suspend, rv8803_resume); + +static const struct i2c_device_id rv8803_id[] = { + { .name = "rv8803", .driver_data = rv_8803 }, + { .name = "rv8804", .driver_data = rx_8804 }, + { .name = "rx8803", .driver_data = rx_8803 }, + { .name = "rx8900", .driver_data = rx_8900 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rv8803_id); + +static int rv8803_probe(struct i2c_client *client) +{ + struct i2c_adapter *adapter = client->adapter; + struct rv8803_data *rv8803; + int err, flags; + struct nvmem_config nvmem_cfg = { + .name = "rv8803_nvram", + .word_size = 1, + .stride = 1, + .size = 1, + .reg_read = rv8803_nvram_read, + .reg_write = rv8803_nvram_write, + .priv = client, + }; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK)) { + dev_err(&adapter->dev, "doesn't support I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_I2C_BLOCK\n"); + return -EIO; + } + + rv8803 = devm_kzalloc(&client->dev, sizeof(struct rv8803_data), + GFP_KERNEL); + if (!rv8803) + return -ENOMEM; + + mutex_init(&rv8803->flags_lock); + rv8803->client = client; + rv8803->type = (uintptr_t)i2c_get_match_data(client); + i2c_set_clientdata(client, rv8803); + + flags = rv8803_read_reg(client, RV8803_FLAG); + if (flags < 0) + return flags; + + if (flags & RV8803_FLAG_V1F) + dev_warn(&client->dev, "Voltage low, temperature compensation stopped.\n"); + + if (flags & RV8803_FLAG_V2F) + dev_warn(&client->dev, "Voltage low, data loss detected.\n"); + + if (flags & RV8803_FLAG_AF) + dev_warn(&client->dev, "An alarm maybe have been missed.\n"); + + rv8803->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(rv8803->rtc)) + return PTR_ERR(rv8803->rtc); + + if (client->irq > 0) { + unsigned long irqflags = IRQF_TRIGGER_LOW; + + if (dev_fwnode(&client->dev)) + irqflags = 0; + + err = devm_request_threaded_irq(&client->dev, client->irq, + NULL, rv8803_handle_irq, + irqflags | IRQF_ONESHOT, + "rv8803", client); + if (err) { + dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n"); + client->irq = 0; + } else { + device_init_wakeup(&client->dev, true); + err = dev_pm_set_wake_irq(&client->dev, client->irq); + if (err) + dev_err(&client->dev, "failed to set wake IRQ\n"); + } + } else { + if (device_property_read_bool(&client->dev, "wakeup-source")) + device_init_wakeup(&client->dev, true); + else + clear_bit(RTC_FEATURE_ALARM, rv8803->rtc->features); + } + + if (of_property_read_bool(client->dev.of_node, "epson,vdet-disable")) + rv8803->backup |= RX8900_FLAG_VDETOFF; + + if (of_property_read_bool(client->dev.of_node, "trickle-diode-disable")) + rv8803->backup |= RX8900_FLAG_SWOFF; + + err = rv8803_regs_configure(rv8803); + if (err) + return err; + + rv8803->rtc->ops = &rv8803_rtc_ops; + rv8803->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rv8803->rtc->range_max = RTC_TIMESTAMP_END_2099; + err = devm_rtc_register_device(rv8803->rtc); + if (err) + return err; + + devm_rtc_nvmem_register(rv8803->rtc, &nvmem_cfg); + + return 0; +} + +static const __maybe_unused struct of_device_id rv8803_of_match[] = { + { + .compatible = "microcrystal,rv8803", + .data = (void *)rv_8803 + }, + { + .compatible = "epson,rx8803", + .data = (void *)rx_8803 + }, + { + .compatible = "epson,rx8804", + .data = (void *)rx_8804 + }, + { + .compatible = "epson,rx8900", + .data = (void *)rx_8900 + }, + { } +}; +MODULE_DEVICE_TABLE(of, rv8803_of_match); + +static struct i2c_driver rv8803_driver = { + .driver = { + .name = "rtc-rv8803", + .of_match_table = of_match_ptr(rv8803_of_match), + .pm = &rv8803_pm_ops, + }, + .probe = rv8803_probe, + .id_table = rv8803_id, +}; +module_i2c_driver(rv8803_driver); + +MODULE_AUTHOR("Alexandre Belloni "); +MODULE_DESCRIPTION("Micro Crystal RV8803 RTC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-rx4581.c b/drivers/rtc/rtc-rx4581.c new file mode 100644 index 000000000..c092e0452 --- /dev/null +++ b/drivers/rtc/rtc-rx4581.c @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* drivers/rtc/rtc-rx4581.c + * + * written by Torben Hohn + * + * Based on: + * drivers/rtc/rtc-max6902.c + * + * Copyright (C) 2006 8D Technologies inc. + * Copyright (C) 2004 Compulab Ltd. + * + * Driver for MAX6902 spi RTC + * + * and based on: + * drivers/rtc/rtc-rx8581.c + * + * An I2C driver for the Epson RX8581 RTC + * + * Author: Martyn Welch + * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc. + * + * Based on: rtc-pcf8563.c (An I2C driver for the Philips PCF8563 RTC) + * Copyright 2005-06 Tower Technologies + */ + +#include +#include +#include +#include +#include +#include +#include + +#define RX4581_REG_SC 0x00 /* Second in BCD */ +#define RX4581_REG_MN 0x01 /* Minute in BCD */ +#define RX4581_REG_HR 0x02 /* Hour in BCD */ +#define RX4581_REG_DW 0x03 /* Day of Week */ +#define RX4581_REG_DM 0x04 /* Day of Month in BCD */ +#define RX4581_REG_MO 0x05 /* Month in BCD */ +#define RX4581_REG_YR 0x06 /* Year in BCD */ +#define RX4581_REG_RAM 0x07 /* RAM */ +#define RX4581_REG_AMN 0x08 /* Alarm Min in BCD*/ +#define RX4581_REG_AHR 0x09 /* Alarm Hour in BCD */ +#define RX4581_REG_ADM 0x0A +#define RX4581_REG_ADW 0x0A +#define RX4581_REG_TMR0 0x0B +#define RX4581_REG_TMR1 0x0C +#define RX4581_REG_EXT 0x0D /* Extension Register */ +#define RX4581_REG_FLAG 0x0E /* Flag Register */ +#define RX4581_REG_CTRL 0x0F /* Control Register */ + + +/* Flag Register bit definitions */ +#define RX4581_FLAG_UF 0x20 /* Update */ +#define RX4581_FLAG_TF 0x10 /* Timer */ +#define RX4581_FLAG_AF 0x08 /* Alarm */ +#define RX4581_FLAG_VLF 0x02 /* Voltage Low */ + +/* Control Register bit definitions */ +#define RX4581_CTRL_UIE 0x20 /* Update Interrupt Enable */ +#define RX4581_CTRL_TIE 0x10 /* Timer Interrupt Enable */ +#define RX4581_CTRL_AIE 0x08 /* Alarm Interrupt Enable */ +#define RX4581_CTRL_STOP 0x02 /* STOP bit */ +#define RX4581_CTRL_RESET 0x01 /* RESET bit */ + +static int rx4581_set_reg(struct device *dev, unsigned char address, + unsigned char data) +{ + struct spi_device *spi = to_spi_device(dev); + unsigned char buf[2]; + + /* high nibble must be '0' to write */ + buf[0] = address & 0x0f; + buf[1] = data; + + return spi_write_then_read(spi, buf, 2, NULL, 0); +} + +static int rx4581_get_reg(struct device *dev, unsigned char address, + unsigned char *data) +{ + struct spi_device *spi = to_spi_device(dev); + + /* Set MSB to indicate read */ + *data = address | 0x80; + + return spi_write_then_read(spi, data, 1, data, 1); +} + +/* + * In the routines that deal directly with the rx8581 hardware, we use + * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. + */ +static int rx4581_get_datetime(struct device *dev, struct rtc_time *tm) +{ + struct spi_device *spi = to_spi_device(dev); + unsigned char date[7]; + unsigned char data; + int err; + + /* First we ensure that the "update flag" is not set, we read the + * time and date then re-read the "update flag". If the update flag + * has been set, we know that the time has changed during the read so + * we repeat the whole process again. + */ + err = rx4581_get_reg(dev, RX4581_REG_FLAG, &data); + if (err != 0) { + dev_err(dev, "Unable to read device flags\n"); + return -EIO; + } + + do { + /* If update flag set, clear it */ + if (data & RX4581_FLAG_UF) { + err = rx4581_set_reg(dev, + RX4581_REG_FLAG, (data & ~RX4581_FLAG_UF)); + if (err != 0) { + dev_err(dev, "Unable to write device " + "flags\n"); + return -EIO; + } + } + + /* Now read time and date */ + date[0] = 0x80; + err = spi_write_then_read(spi, date, 1, date, 7); + if (err < 0) { + dev_err(dev, "Unable to read date\n"); + return -EIO; + } + + /* Check flag register */ + err = rx4581_get_reg(dev, RX4581_REG_FLAG, &data); + if (err != 0) { + dev_err(dev, "Unable to read device flags\n"); + return -EIO; + } + } while (data & RX4581_FLAG_UF); + + if (data & RX4581_FLAG_VLF) + dev_info(dev, + "low voltage detected, date/time is not reliable.\n"); + + dev_dbg(dev, + "%s: raw data is sec=%02x, min=%02x, hr=%02x, " + "wday=%02x, mday=%02x, mon=%02x, year=%02x\n", + __func__, + date[0], date[1], date[2], date[3], date[4], date[5], date[6]); + + tm->tm_sec = bcd2bin(date[RX4581_REG_SC] & 0x7F); + tm->tm_min = bcd2bin(date[RX4581_REG_MN] & 0x7F); + tm->tm_hour = bcd2bin(date[RX4581_REG_HR] & 0x3F); /* rtc hr 0-23 */ + tm->tm_wday = ilog2(date[RX4581_REG_DW] & 0x7F); + tm->tm_mday = bcd2bin(date[RX4581_REG_DM] & 0x3F); + tm->tm_mon = bcd2bin(date[RX4581_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */ + tm->tm_year = bcd2bin(date[RX4581_REG_YR]); + if (tm->tm_year < 70) + tm->tm_year += 100; /* assume we are in 1970...2069 */ + + + dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + return 0; +} + +static int rx4581_set_datetime(struct device *dev, struct rtc_time *tm) +{ + struct spi_device *spi = to_spi_device(dev); + int err; + unsigned char buf[8], data; + + dev_dbg(dev, "%s: secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + buf[0] = 0x00; + /* hours, minutes and seconds */ + buf[RX4581_REG_SC+1] = bin2bcd(tm->tm_sec); + buf[RX4581_REG_MN+1] = bin2bcd(tm->tm_min); + buf[RX4581_REG_HR+1] = bin2bcd(tm->tm_hour); + + buf[RX4581_REG_DM+1] = bin2bcd(tm->tm_mday); + + /* month, 1 - 12 */ + buf[RX4581_REG_MO+1] = bin2bcd(tm->tm_mon + 1); + + /* year and century */ + buf[RX4581_REG_YR+1] = bin2bcd(tm->tm_year % 100); + buf[RX4581_REG_DW+1] = (0x1 << tm->tm_wday); + + /* Stop the clock */ + err = rx4581_get_reg(dev, RX4581_REG_CTRL, &data); + if (err != 0) { + dev_err(dev, "Unable to read control register\n"); + return -EIO; + } + + err = rx4581_set_reg(dev, RX4581_REG_CTRL, + (data | RX4581_CTRL_STOP)); + if (err != 0) { + dev_err(dev, "Unable to write control register\n"); + return -EIO; + } + + /* write register's data */ + err = spi_write_then_read(spi, buf, 8, NULL, 0); + if (err != 0) { + dev_err(dev, "Unable to write to date registers\n"); + return -EIO; + } + + /* get VLF and clear it */ + err = rx4581_get_reg(dev, RX4581_REG_FLAG, &data); + if (err != 0) { + dev_err(dev, "Unable to read flag register\n"); + return -EIO; + } + + err = rx4581_set_reg(dev, RX4581_REG_FLAG, + (data & ~(RX4581_FLAG_VLF))); + if (err != 0) { + dev_err(dev, "Unable to write flag register\n"); + return -EIO; + } + + /* Restart the clock */ + err = rx4581_get_reg(dev, RX4581_REG_CTRL, &data); + if (err != 0) { + dev_err(dev, "Unable to read control register\n"); + return -EIO; + } + + err = rx4581_set_reg(dev, RX4581_REG_CTRL, + (data & ~(RX4581_CTRL_STOP))); + if (err != 0) { + dev_err(dev, "Unable to write control register\n"); + return -EIO; + } + + return 0; +} + +static const struct rtc_class_ops rx4581_rtc_ops = { + .read_time = rx4581_get_datetime, + .set_time = rx4581_set_datetime, +}; + +static int rx4581_probe(struct spi_device *spi) +{ + struct rtc_device *rtc; + unsigned char tmp; + int res; + + res = rx4581_get_reg(&spi->dev, RX4581_REG_SC, &tmp); + if (res != 0) + return res; + + rtc = devm_rtc_device_register(&spi->dev, "rx4581", + &rx4581_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + spi_set_drvdata(spi, rtc); + return 0; +} + +static const struct spi_device_id rx4581_id[] = { + { "rx4581", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, rx4581_id); + +static struct spi_driver rx4581_driver = { + .driver = { + .name = "rtc-rx4581", + }, + .probe = rx4581_probe, + .id_table = rx4581_id, +}; + +module_spi_driver(rx4581_driver); + +MODULE_DESCRIPTION("rx4581 spi RTC driver"); +MODULE_AUTHOR("Torben Hohn"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:rtc-rx4581"); diff --git a/drivers/rtc/rtc-rx6110.c b/drivers/rtc/rtc-rx6110.c new file mode 100644 index 000000000..1eacd470a --- /dev/null +++ b/drivers/rtc/rtc-rx6110.c @@ -0,0 +1,511 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for the Epson RTC module RX-6110 SA + * + * Copyright(C) 2015 Pengutronix, Steffen Trumtrar + * Copyright(C) SEIKO EPSON CORPORATION 2013. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* RX-6110 Register definitions */ +#define RX6110_REG_SEC 0x10 +#define RX6110_REG_MIN 0x11 +#define RX6110_REG_HOUR 0x12 +#define RX6110_REG_WDAY 0x13 +#define RX6110_REG_MDAY 0x14 +#define RX6110_REG_MONTH 0x15 +#define RX6110_REG_YEAR 0x16 +#define RX6110_REG_RES1 0x17 +#define RX6110_REG_ALMIN 0x18 +#define RX6110_REG_ALHOUR 0x19 +#define RX6110_REG_ALWDAY 0x1A +#define RX6110_REG_TCOUNT0 0x1B +#define RX6110_REG_TCOUNT1 0x1C +#define RX6110_REG_EXT 0x1D +#define RX6110_REG_FLAG 0x1E +#define RX6110_REG_CTRL 0x1F +#define RX6110_REG_USER0 0x20 +#define RX6110_REG_USER1 0x21 +#define RX6110_REG_USER2 0x22 +#define RX6110_REG_USER3 0x23 +#define RX6110_REG_USER4 0x24 +#define RX6110_REG_USER5 0x25 +#define RX6110_REG_USER6 0x26 +#define RX6110_REG_USER7 0x27 +#define RX6110_REG_USER8 0x28 +#define RX6110_REG_USER9 0x29 +#define RX6110_REG_USERA 0x2A +#define RX6110_REG_USERB 0x2B +#define RX6110_REG_USERC 0x2C +#define RX6110_REG_USERD 0x2D +#define RX6110_REG_USERE 0x2E +#define RX6110_REG_USERF 0x2F +#define RX6110_REG_RES2 0x30 +#define RX6110_REG_RES3 0x31 +#define RX6110_REG_IRQ 0x32 + +#define RX6110_BIT_ALARM_EN BIT(7) + +/* Extension Register (1Dh) bit positions */ +#define RX6110_BIT_EXT_TSEL0 BIT(0) +#define RX6110_BIT_EXT_TSEL1 BIT(1) +#define RX6110_BIT_EXT_TSEL2 BIT(2) +#define RX6110_BIT_EXT_WADA BIT(3) +#define RX6110_BIT_EXT_TE BIT(4) +#define RX6110_BIT_EXT_USEL BIT(5) +#define RX6110_BIT_EXT_FSEL0 BIT(6) +#define RX6110_BIT_EXT_FSEL1 BIT(7) + +/* Flag Register (1Eh) bit positions */ +#define RX6110_BIT_FLAG_VLF BIT(1) +#define RX6110_BIT_FLAG_AF BIT(3) +#define RX6110_BIT_FLAG_TF BIT(4) +#define RX6110_BIT_FLAG_UF BIT(5) + +/* Control Register (1Fh) bit positions */ +#define RX6110_BIT_CTRL_TBKE BIT(0) +#define RX6110_BIT_CTRL_TBKON BIT(1) +#define RX6110_BIT_CTRL_TSTP BIT(2) +#define RX6110_BIT_CTRL_AIE BIT(3) +#define RX6110_BIT_CTRL_TIE BIT(4) +#define RX6110_BIT_CTRL_UIE BIT(5) +#define RX6110_BIT_CTRL_STOP BIT(6) +#define RX6110_BIT_CTRL_TEST BIT(7) + +enum { + RTC_SEC = 0, + RTC_MIN, + RTC_HOUR, + RTC_WDAY, + RTC_MDAY, + RTC_MONTH, + RTC_YEAR, + RTC_NR_TIME +}; + +#define RX6110_DRIVER_NAME "rx6110" + +struct rx6110_data { + struct rtc_device *rtc; + struct regmap *regmap; +}; + +/** + * rx6110_rtc_tm_to_data - convert rtc_time to native time encoding + * + * @tm: holds date and time + * @data: holds the encoding in rx6110 native form + */ +static int rx6110_rtc_tm_to_data(struct rtc_time *tm, u8 *data) +{ + pr_debug("%s: date %ptRr\n", __func__, tm); + + /* + * The year in the RTC is a value between 0 and 99. + * Assume that this represents the current century + * and disregard all other values. + */ + if (tm->tm_year < 100 || tm->tm_year >= 200) + return -EINVAL; + + data[RTC_SEC] = bin2bcd(tm->tm_sec); + data[RTC_MIN] = bin2bcd(tm->tm_min); + data[RTC_HOUR] = bin2bcd(tm->tm_hour); + data[RTC_WDAY] = BIT(bin2bcd(tm->tm_wday)); + data[RTC_MDAY] = bin2bcd(tm->tm_mday); + data[RTC_MONTH] = bin2bcd(tm->tm_mon + 1); + data[RTC_YEAR] = bin2bcd(tm->tm_year % 100); + + return 0; +} + +/** + * rx6110_data_to_rtc_tm - convert native time encoding to rtc_time + * + * @data: holds the encoding in rx6110 native form + * @tm: holds date and time + */ +static int rx6110_data_to_rtc_tm(u8 *data, struct rtc_time *tm) +{ + tm->tm_sec = bcd2bin(data[RTC_SEC] & 0x7f); + tm->tm_min = bcd2bin(data[RTC_MIN] & 0x7f); + /* only 24-hour clock */ + tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x3f); + tm->tm_wday = ffs(data[RTC_WDAY] & 0x7f); + tm->tm_mday = bcd2bin(data[RTC_MDAY] & 0x3f); + tm->tm_mon = bcd2bin(data[RTC_MONTH] & 0x1f) - 1; + tm->tm_year = bcd2bin(data[RTC_YEAR]) + 100; + + pr_debug("%s: date %ptRr\n", __func__, tm); + + /* + * The year in the RTC is a value between 0 and 99. + * Assume that this represents the current century + * and disregard all other values. + */ + if (tm->tm_year < 100 || tm->tm_year >= 200) + return -EINVAL; + + return 0; +} + +/** + * rx6110_set_time - set the current time in the rx6110 registers + * + * @dev: the rtc device in use + * @tm: holds date and time + * + * BUG: The HW assumes every year that is a multiple of 4 to be a leap + * year. Next time this is wrong is 2100, which will not be a leap year + * + * Note: If STOP is not set/cleared, the clock will start when the seconds + * register is written + * + */ +static int rx6110_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rx6110_data *rx6110 = dev_get_drvdata(dev); + u8 data[RTC_NR_TIME]; + int ret; + + ret = rx6110_rtc_tm_to_data(tm, data); + if (ret < 0) + return ret; + + /* set STOP bit before changing clock/calendar */ + ret = regmap_update_bits(rx6110->regmap, RX6110_REG_CTRL, + RX6110_BIT_CTRL_STOP, RX6110_BIT_CTRL_STOP); + if (ret) + return ret; + + ret = regmap_bulk_write(rx6110->regmap, RX6110_REG_SEC, data, + RTC_NR_TIME); + if (ret) + return ret; + + /* The time in the RTC is valid. Be sure to have VLF cleared. */ + ret = regmap_update_bits(rx6110->regmap, RX6110_REG_FLAG, + RX6110_BIT_FLAG_VLF, 0); + if (ret) + return ret; + + /* clear STOP bit after changing clock/calendar */ + ret = regmap_update_bits(rx6110->regmap, RX6110_REG_CTRL, + RX6110_BIT_CTRL_STOP, 0); + + return ret; +} + +/** + * rx6110_get_time - get the current time from the rx6110 registers + * @dev: the rtc device in use + * @tm: holds date and time + */ +static int rx6110_get_time(struct device *dev, struct rtc_time *tm) +{ + struct rx6110_data *rx6110 = dev_get_drvdata(dev); + u8 data[RTC_NR_TIME]; + int flags; + int ret; + + ret = regmap_read(rx6110->regmap, RX6110_REG_FLAG, &flags); + if (ret) + return -EINVAL; + + /* check for VLF Flag (set at power-on) */ + if ((flags & RX6110_BIT_FLAG_VLF)) { + dev_warn(dev, "Voltage low, data is invalid.\n"); + return -EINVAL; + } + + /* read registers to date */ + ret = regmap_bulk_read(rx6110->regmap, RX6110_REG_SEC, data, + RTC_NR_TIME); + if (ret) + return ret; + + ret = rx6110_data_to_rtc_tm(data, tm); + if (ret) + return ret; + + dev_dbg(dev, "%s: date %ptRr\n", __func__, tm); + + return 0; +} + +static const struct reg_sequence rx6110_default_regs[] = { + { RX6110_REG_RES1, 0xB8 }, + { RX6110_REG_RES2, 0x00 }, + { RX6110_REG_RES3, 0x10 }, + { RX6110_REG_IRQ, 0x00 }, + { RX6110_REG_ALMIN, 0x00 }, + { RX6110_REG_ALHOUR, 0x00 }, + { RX6110_REG_ALWDAY, 0x00 }, +}; + +/** + * rx6110_init - initialize the rx6110 registers + * + * @rx6110: pointer to the rx6110 struct in use + * + */ +static int rx6110_init(struct rx6110_data *rx6110) +{ + struct rtc_device *rtc = rx6110->rtc; + int flags; + int ret; + + ret = regmap_update_bits(rx6110->regmap, RX6110_REG_EXT, + RX6110_BIT_EXT_TE, 0); + if (ret) + return ret; + + ret = regmap_register_patch(rx6110->regmap, rx6110_default_regs, + ARRAY_SIZE(rx6110_default_regs)); + if (ret) + return ret; + + ret = regmap_read(rx6110->regmap, RX6110_REG_FLAG, &flags); + if (ret) + return ret; + + /* check for VLF Flag (set at power-on) */ + if ((flags & RX6110_BIT_FLAG_VLF)) + dev_warn(&rtc->dev, "Voltage low, data loss detected.\n"); + + /* check for Alarm Flag */ + if (flags & RX6110_BIT_FLAG_AF) + dev_warn(&rtc->dev, "An alarm may have been missed.\n"); + + /* check for Periodic Timer Flag */ + if (flags & RX6110_BIT_FLAG_TF) + dev_warn(&rtc->dev, "Periodic timer was detected\n"); + + /* check for Update Timer Flag */ + if (flags & RX6110_BIT_FLAG_UF) + dev_warn(&rtc->dev, "Update timer was detected\n"); + + /* clear all flags BUT VLF */ + ret = regmap_update_bits(rx6110->regmap, RX6110_REG_FLAG, + RX6110_BIT_FLAG_AF | + RX6110_BIT_FLAG_UF | + RX6110_BIT_FLAG_TF, + 0); + + return ret; +} + +static const struct rtc_class_ops rx6110_rtc_ops = { + .read_time = rx6110_get_time, + .set_time = rx6110_set_time, +}; + +static int rx6110_probe(struct rx6110_data *rx6110, struct device *dev) +{ + int err; + + rx6110->rtc = devm_rtc_device_register(dev, + RX6110_DRIVER_NAME, + &rx6110_rtc_ops, THIS_MODULE); + + if (IS_ERR(rx6110->rtc)) + return PTR_ERR(rx6110->rtc); + + err = rx6110_init(rx6110); + if (err) + return err; + + return 0; +} + +#if IS_ENABLED(CONFIG_SPI_MASTER) +static const struct regmap_config regmap_spi_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = RX6110_REG_IRQ, + .read_flag_mask = 0x80, +}; + +/** + * rx6110_spi_probe - initialize rtc driver + * @spi: pointer to spi device + */ +static int rx6110_spi_probe(struct spi_device *spi) +{ + struct rx6110_data *rx6110; + + if ((spi->bits_per_word && spi->bits_per_word != 8) || + (spi->max_speed_hz > 2000000) || + (spi->mode != (SPI_CS_HIGH | SPI_CPOL | SPI_CPHA))) { + dev_warn(&spi->dev, "SPI settings: bits_per_word: %d, max_speed_hz: %d, mode: %xh\n", + spi->bits_per_word, spi->max_speed_hz, spi->mode); + dev_warn(&spi->dev, "driving device in an unsupported mode"); + } + + rx6110 = devm_kzalloc(&spi->dev, sizeof(*rx6110), GFP_KERNEL); + if (!rx6110) + return -ENOMEM; + + rx6110->regmap = devm_regmap_init_spi(spi, ®map_spi_config); + if (IS_ERR(rx6110->regmap)) { + dev_err(&spi->dev, "regmap init failed for rtc rx6110\n"); + return PTR_ERR(rx6110->regmap); + } + + spi_set_drvdata(spi, rx6110); + + return rx6110_probe(rx6110, &spi->dev); +} + +static const struct spi_device_id rx6110_spi_id[] = { + { "rx6110", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, rx6110_spi_id); + +static const __maybe_unused struct of_device_id rx6110_spi_of_match[] = { + { .compatible = "epson,rx6110" }, + { }, +}; +MODULE_DEVICE_TABLE(of, rx6110_spi_of_match); + +static struct spi_driver rx6110_spi_driver = { + .driver = { + .name = RX6110_DRIVER_NAME, + .of_match_table = of_match_ptr(rx6110_spi_of_match), + }, + .probe = rx6110_spi_probe, + .id_table = rx6110_spi_id, +}; + +static int rx6110_spi_register(void) +{ + return spi_register_driver(&rx6110_spi_driver); +} + +static void rx6110_spi_unregister(void) +{ + spi_unregister_driver(&rx6110_spi_driver); +} +#else +static int rx6110_spi_register(void) +{ + return 0; +} + +static void rx6110_spi_unregister(void) +{ +} +#endif /* CONFIG_SPI_MASTER */ + +#if IS_ENABLED(CONFIG_I2C) +static const struct regmap_config regmap_i2c_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = RX6110_REG_IRQ, + .read_flag_mask = 0x80, +}; + +static int rx6110_i2c_probe(struct i2c_client *client) +{ + struct i2c_adapter *adapter = client->adapter; + struct rx6110_data *rx6110; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA + | I2C_FUNC_SMBUS_I2C_BLOCK)) { + dev_err(&adapter->dev, + "doesn't support required functionality\n"); + return -EIO; + } + + rx6110 = devm_kzalloc(&client->dev, sizeof(*rx6110), GFP_KERNEL); + if (!rx6110) + return -ENOMEM; + + rx6110->regmap = devm_regmap_init_i2c(client, ®map_i2c_config); + if (IS_ERR(rx6110->regmap)) { + dev_err(&client->dev, "regmap init failed for rtc rx6110\n"); + return PTR_ERR(rx6110->regmap); + } + + i2c_set_clientdata(client, rx6110); + + return rx6110_probe(rx6110, &client->dev); +} + +static const struct acpi_device_id rx6110_i2c_acpi_match[] = { + { "SECC6110" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, rx6110_i2c_acpi_match); + +static const struct i2c_device_id rx6110_i2c_id[] = { + { .name = "rx6110" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rx6110_i2c_id); + +static struct i2c_driver rx6110_i2c_driver = { + .driver = { + .name = RX6110_DRIVER_NAME, + .acpi_match_table = rx6110_i2c_acpi_match, + }, + .probe = rx6110_i2c_probe, + .id_table = rx6110_i2c_id, +}; + +static int rx6110_i2c_register(void) +{ + return i2c_add_driver(&rx6110_i2c_driver); +} + +static void rx6110_i2c_unregister(void) +{ + i2c_del_driver(&rx6110_i2c_driver); +} +#else +static int rx6110_i2c_register(void) +{ + return 0; +} + +static void rx6110_i2c_unregister(void) +{ +} +#endif /* CONFIG_I2C */ + +static int __init rx6110_module_init(void) +{ + int ret; + + ret = rx6110_spi_register(); + if (ret) + return ret; + + ret = rx6110_i2c_register(); + if (ret) + rx6110_spi_unregister(); + + return ret; +} +module_init(rx6110_module_init); + +static void __exit rx6110_module_exit(void) +{ + rx6110_spi_unregister(); + rx6110_i2c_unregister(); +} +module_exit(rx6110_module_exit); + +MODULE_AUTHOR("Val Krutov "); +MODULE_DESCRIPTION("RX-6110 SA RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-rx8010.c b/drivers/rtc/rtc-rx8010.c new file mode 100644 index 000000000..ce3954132 --- /dev/null +++ b/drivers/rtc/rtc-rx8010.c @@ -0,0 +1,434 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for the Epson RTC module RX-8010 SJ + * + * Copyright(C) Timesys Corporation 2015 + * Copyright(C) General Electric Company 2015 + */ + +#include +#include +#include +#include +#include +#include +#include + +#define RX8010_SEC 0x10 +#define RX8010_MIN 0x11 +#define RX8010_HOUR 0x12 +#define RX8010_WDAY 0x13 +#define RX8010_MDAY 0x14 +#define RX8010_MONTH 0x15 +#define RX8010_YEAR 0x16 +#define RX8010_RESV17 0x17 +#define RX8010_ALMIN 0x18 +#define RX8010_ALHOUR 0x19 +#define RX8010_ALWDAY 0x1A +#define RX8010_TCOUNT0 0x1B +#define RX8010_TCOUNT1 0x1C +#define RX8010_EXT 0x1D +#define RX8010_FLAG 0x1E +#define RX8010_CTRL 0x1F +/* 0x20 to 0x2F are user registers */ +#define RX8010_RESV30 0x30 +#define RX8010_RESV31 0x31 +#define RX8010_IRQ 0x32 + +#define RX8010_EXT_WADA BIT(3) + +#define RX8010_FLAG_VLF BIT(1) +#define RX8010_FLAG_AF BIT(3) +#define RX8010_FLAG_TF BIT(4) +#define RX8010_FLAG_UF BIT(5) + +#define RX8010_CTRL_AIE BIT(3) +#define RX8010_CTRL_UIE BIT(5) +#define RX8010_CTRL_STOP BIT(6) +#define RX8010_CTRL_TEST BIT(7) + +#define RX8010_ALARM_AE BIT(7) + +static const struct i2c_device_id rx8010_id[] = { + { .name = "rx8010" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rx8010_id); + +static const __maybe_unused struct of_device_id rx8010_of_match[] = { + { .compatible = "epson,rx8010" }, + { } +}; +MODULE_DEVICE_TABLE(of, rx8010_of_match); + +struct rx8010_data { + struct regmap *regs; + struct rtc_device *rtc; + u8 ctrlreg; +}; + +static irqreturn_t rx8010_irq_1_handler(int irq, void *dev_id) +{ + struct i2c_client *client = dev_id; + struct rx8010_data *rx8010 = i2c_get_clientdata(client); + int flagreg, err; + + rtc_lock(rx8010->rtc); + + err = regmap_read(rx8010->regs, RX8010_FLAG, &flagreg); + if (err) { + rtc_unlock(rx8010->rtc); + return IRQ_NONE; + } + + if (flagreg & RX8010_FLAG_VLF) + dev_warn(&client->dev, "Frequency stop detected\n"); + + if (flagreg & RX8010_FLAG_TF) { + flagreg &= ~RX8010_FLAG_TF; + rtc_update_irq(rx8010->rtc, 1, RTC_PF | RTC_IRQF); + } + + if (flagreg & RX8010_FLAG_AF) { + flagreg &= ~RX8010_FLAG_AF; + rtc_update_irq(rx8010->rtc, 1, RTC_AF | RTC_IRQF); + } + + if (flagreg & RX8010_FLAG_UF) { + flagreg &= ~RX8010_FLAG_UF; + rtc_update_irq(rx8010->rtc, 1, RTC_UF | RTC_IRQF); + } + + err = regmap_write(rx8010->regs, RX8010_FLAG, flagreg); + rtc_unlock(rx8010->rtc); + return err ? IRQ_NONE : IRQ_HANDLED; +} + +static int rx8010_get_time(struct device *dev, struct rtc_time *dt) +{ + struct rx8010_data *rx8010 = dev_get_drvdata(dev); + u8 date[RX8010_YEAR - RX8010_SEC + 1]; + int flagreg, err; + + err = regmap_read(rx8010->regs, RX8010_FLAG, &flagreg); + if (err) + return err; + + if (flagreg & RX8010_FLAG_VLF) { + dev_warn(dev, "Frequency stop detected\n"); + return -EINVAL; + } + + err = regmap_bulk_read(rx8010->regs, RX8010_SEC, date, sizeof(date)); + if (err) + return err; + + dt->tm_sec = bcd2bin(date[RX8010_SEC - RX8010_SEC] & 0x7f); + dt->tm_min = bcd2bin(date[RX8010_MIN - RX8010_SEC] & 0x7f); + dt->tm_hour = bcd2bin(date[RX8010_HOUR - RX8010_SEC] & 0x3f); + dt->tm_mday = bcd2bin(date[RX8010_MDAY - RX8010_SEC] & 0x3f); + dt->tm_mon = bcd2bin(date[RX8010_MONTH - RX8010_SEC] & 0x1f) - 1; + dt->tm_year = bcd2bin(date[RX8010_YEAR - RX8010_SEC]) + 100; + dt->tm_wday = ffs(date[RX8010_WDAY - RX8010_SEC] & 0x7f); + + return 0; +} + +static int rx8010_set_time(struct device *dev, struct rtc_time *dt) +{ + struct rx8010_data *rx8010 = dev_get_drvdata(dev); + u8 date[RX8010_YEAR - RX8010_SEC + 1]; + int err; + + /* set STOP bit before changing clock/calendar */ + err = regmap_set_bits(rx8010->regs, RX8010_CTRL, RX8010_CTRL_STOP); + if (err) + return err; + + date[RX8010_SEC - RX8010_SEC] = bin2bcd(dt->tm_sec); + date[RX8010_MIN - RX8010_SEC] = bin2bcd(dt->tm_min); + date[RX8010_HOUR - RX8010_SEC] = bin2bcd(dt->tm_hour); + date[RX8010_MDAY - RX8010_SEC] = bin2bcd(dt->tm_mday); + date[RX8010_MONTH - RX8010_SEC] = bin2bcd(dt->tm_mon + 1); + date[RX8010_YEAR - RX8010_SEC] = bin2bcd(dt->tm_year - 100); + date[RX8010_WDAY - RX8010_SEC] = bin2bcd(1 << dt->tm_wday); + + err = regmap_bulk_write(rx8010->regs, RX8010_SEC, date, sizeof(date)); + if (err) + return err; + + /* clear STOP bit after changing clock/calendar */ + err = regmap_clear_bits(rx8010->regs, RX8010_CTRL, RX8010_CTRL_STOP); + if (err) + return err; + + err = regmap_clear_bits(rx8010->regs, RX8010_FLAG, RX8010_FLAG_VLF); + if (err) + return err; + + return 0; +} + +static int rx8010_init(struct device *dev) +{ + struct rx8010_data *rx8010 = dev_get_drvdata(dev); + u8 ctrl[2]; + int need_clear = 0, err; + + /* Initialize reserved registers as specified in datasheet */ + err = regmap_write(rx8010->regs, RX8010_RESV17, 0xD8); + if (err) + return err; + + err = regmap_write(rx8010->regs, RX8010_RESV30, 0x00); + if (err) + return err; + + err = regmap_write(rx8010->regs, RX8010_RESV31, 0x08); + if (err) + return err; + + err = regmap_write(rx8010->regs, RX8010_IRQ, 0x00); + if (err) + return err; + + err = regmap_bulk_read(rx8010->regs, RX8010_FLAG, ctrl, 2); + if (err) + return err; + + if (ctrl[0] & RX8010_FLAG_VLF) + dev_warn(dev, "Frequency stop was detected\n"); + + if (ctrl[0] & RX8010_FLAG_AF) { + dev_warn(dev, "Alarm was detected\n"); + need_clear = 1; + } + + if (ctrl[0] & RX8010_FLAG_TF) + need_clear = 1; + + if (ctrl[0] & RX8010_FLAG_UF) + need_clear = 1; + + if (need_clear) { + ctrl[0] &= ~(RX8010_FLAG_AF | RX8010_FLAG_TF | RX8010_FLAG_UF); + err = regmap_write(rx8010->regs, RX8010_FLAG, ctrl[0]); + if (err) + return err; + } + + rx8010->ctrlreg = (ctrl[1] & ~RX8010_CTRL_TEST); + + return 0; +} + +static int rx8010_read_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct rx8010_data *rx8010 = dev_get_drvdata(dev); + u8 alarmvals[3]; + int flagreg, err; + + err = regmap_bulk_read(rx8010->regs, RX8010_ALMIN, alarmvals, 3); + if (err) + return err; + + err = regmap_read(rx8010->regs, RX8010_FLAG, &flagreg); + if (err) + return err; + + t->time.tm_sec = 0; + t->time.tm_min = bcd2bin(alarmvals[0] & 0x7f); + t->time.tm_hour = bcd2bin(alarmvals[1] & 0x3f); + + if (!(alarmvals[2] & RX8010_ALARM_AE)) + t->time.tm_mday = bcd2bin(alarmvals[2] & 0x7f); + + t->enabled = !!(rx8010->ctrlreg & RX8010_CTRL_AIE); + t->pending = (flagreg & RX8010_FLAG_AF) && t->enabled; + + return 0; +} + +static int rx8010_set_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct rx8010_data *rx8010 = dev_get_drvdata(dev); + u8 alarmvals[3]; + int err; + + if (rx8010->ctrlreg & (RX8010_CTRL_AIE | RX8010_CTRL_UIE)) { + rx8010->ctrlreg &= ~(RX8010_CTRL_AIE | RX8010_CTRL_UIE); + err = regmap_write(rx8010->regs, RX8010_CTRL, rx8010->ctrlreg); + if (err) + return err; + } + + err = regmap_clear_bits(rx8010->regs, RX8010_FLAG, RX8010_FLAG_AF); + if (err) + return err; + + alarmvals[0] = bin2bcd(t->time.tm_min); + alarmvals[1] = bin2bcd(t->time.tm_hour); + alarmvals[2] = bin2bcd(t->time.tm_mday); + + err = regmap_bulk_write(rx8010->regs, RX8010_ALMIN, alarmvals, 2); + if (err) + return err; + + err = regmap_clear_bits(rx8010->regs, RX8010_EXT, RX8010_EXT_WADA); + if (err) + return err; + + if (alarmvals[2] == 0) + alarmvals[2] |= RX8010_ALARM_AE; + + err = regmap_write(rx8010->regs, RX8010_ALWDAY, alarmvals[2]); + if (err) + return err; + + if (t->enabled) { + if (rx8010->rtc->uie_rtctimer.enabled) + rx8010->ctrlreg |= RX8010_CTRL_UIE; + if (rx8010->rtc->aie_timer.enabled) + rx8010->ctrlreg |= + (RX8010_CTRL_AIE | RX8010_CTRL_UIE); + + err = regmap_write(rx8010->regs, RX8010_CTRL, rx8010->ctrlreg); + if (err) + return err; + } + + return 0; +} + +static int rx8010_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct rx8010_data *rx8010 = dev_get_drvdata(dev); + int err; + u8 ctrl; + + ctrl = rx8010->ctrlreg; + + if (enabled) { + if (rx8010->rtc->uie_rtctimer.enabled) + ctrl |= RX8010_CTRL_UIE; + if (rx8010->rtc->aie_timer.enabled) + ctrl |= (RX8010_CTRL_AIE | RX8010_CTRL_UIE); + } else { + if (!rx8010->rtc->uie_rtctimer.enabled) + ctrl &= ~RX8010_CTRL_UIE; + if (!rx8010->rtc->aie_timer.enabled) + ctrl &= ~RX8010_CTRL_AIE; + } + + err = regmap_clear_bits(rx8010->regs, RX8010_FLAG, RX8010_FLAG_AF); + if (err) + return err; + + if (ctrl != rx8010->ctrlreg) { + rx8010->ctrlreg = ctrl; + err = regmap_write(rx8010->regs, RX8010_CTRL, rx8010->ctrlreg); + if (err) + return err; + } + + return 0; +} + +static int rx8010_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct rx8010_data *rx8010 = dev_get_drvdata(dev); + int tmp, flagreg, err; + + switch (cmd) { + case RTC_VL_READ: + err = regmap_read(rx8010->regs, RX8010_FLAG, &flagreg); + if (err) + return err; + + tmp = flagreg & RX8010_FLAG_VLF ? RTC_VL_DATA_INVALID : 0; + return put_user(tmp, (unsigned int __user *)arg); + + default: + return -ENOIOCTLCMD; + } +} + +static const struct rtc_class_ops rx8010_rtc_ops = { + .read_time = rx8010_get_time, + .set_time = rx8010_set_time, + .ioctl = rx8010_ioctl, + .read_alarm = rx8010_read_alarm, + .set_alarm = rx8010_set_alarm, + .alarm_irq_enable = rx8010_alarm_irq_enable, +}; + +static const struct regmap_config rx8010_regmap_config = { + .name = "rx8010-rtc", + .reg_bits = 8, + .val_bits = 8, +}; + +static int rx8010_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct rx8010_data *rx8010; + int err = 0; + + rx8010 = devm_kzalloc(dev, sizeof(*rx8010), GFP_KERNEL); + if (!rx8010) + return -ENOMEM; + + i2c_set_clientdata(client, rx8010); + + rx8010->regs = devm_regmap_init_i2c(client, &rx8010_regmap_config); + if (IS_ERR(rx8010->regs)) + return PTR_ERR(rx8010->regs); + + err = rx8010_init(dev); + if (err) + return err; + + rx8010->rtc = devm_rtc_allocate_device(dev); + if (IS_ERR(rx8010->rtc)) + return PTR_ERR(rx8010->rtc); + + if (client->irq > 0) { + unsigned long irqflags = IRQF_TRIGGER_LOW; + + if (dev_fwnode(&client->dev)) + irqflags = 0; + + err = devm_request_threaded_irq(dev, client->irq, NULL, + rx8010_irq_1_handler, + irqflags | IRQF_ONESHOT, + "rx8010", client); + if (err) { + dev_err(dev, "unable to request IRQ\n"); + return err; + } + } else { + clear_bit(RTC_FEATURE_ALARM, rx8010->rtc->features); + } + + rx8010->rtc->ops = &rx8010_rtc_ops; + rx8010->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rx8010->rtc->range_max = RTC_TIMESTAMP_END_2099; + + return devm_rtc_register_device(rx8010->rtc); +} + +static struct i2c_driver rx8010_driver = { + .driver = { + .name = "rtc-rx8010", + .of_match_table = of_match_ptr(rx8010_of_match), + }, + .probe = rx8010_probe, + .id_table = rx8010_id, +}; + +module_i2c_driver(rx8010_driver); + +MODULE_AUTHOR("Akshay Bhat "); +MODULE_DESCRIPTION("Epson RX8010SJ RTC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-rx8025.c b/drivers/rtc/rtc-rx8025.c new file mode 100644 index 000000000..5eeaa929f --- /dev/null +++ b/drivers/rtc/rtc-rx8025.c @@ -0,0 +1,588 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Epson's RTC module RX-8025 SA/NB + * + * Copyright (C) 2009 Wolfgang Grandegger + * + * Copyright (C) 2005 by Digi International Inc. + * All rights reserved. + * + * Modified by fengjh at rising.com.cn + * + * 2006.11 + * + * Code cleanup by Sergei Poselenov, + * Converted to new style by Wolfgang Grandegger + * Alarm and periodic interrupt added by Dmitry Rakhchev + */ +#include +#include +#include +#include +#include +#include +#include + +/* Register definitions */ +#define RX8025_REG_SEC 0x00 +#define RX8025_REG_MIN 0x01 +#define RX8025_REG_HOUR 0x02 +#define RX8025_REG_WDAY 0x03 +#define RX8025_REG_MDAY 0x04 +#define RX8025_REG_MONTH 0x05 +#define RX8025_REG_YEAR 0x06 +#define RX8025_REG_DIGOFF 0x07 +#define RX8025_REG_ALWMIN 0x08 +#define RX8025_REG_ALWHOUR 0x09 +#define RX8025_REG_ALWWDAY 0x0a +#define RX8025_REG_ALDMIN 0x0b +#define RX8025_REG_ALDHOUR 0x0c +/* 0x0d is reserved */ +#define RX8025_REG_CTRL1 0x0e +#define RX8025_REG_CTRL2 0x0f + +#define RX8025_BIT_CTRL1_CT (7 << 0) +/* 1 Hz periodic level irq */ +#define RX8025_BIT_CTRL1_CT_1HZ 4 +#define RX8025_BIT_CTRL1_TEST BIT(3) +#define RX8025_BIT_CTRL1_1224 BIT(5) +#define RX8025_BIT_CTRL1_DALE BIT(6) +#define RX8025_BIT_CTRL1_WALE BIT(7) + +#define RX8025_BIT_CTRL2_DAFG BIT(0) +#define RX8025_BIT_CTRL2_WAFG BIT(1) +#define RX8025_BIT_CTRL2_CTFG BIT(2) +#define RX8025_BIT_CTRL2_PON BIT(4) +#define RX8025_BIT_CTRL2_XST BIT(5) +#define RX8025_BIT_CTRL2_VDET BIT(6) + +#define RX8035_BIT_HOUR_1224 BIT(7) + +/* Clock precision adjustment */ +#define RX8025_ADJ_RESOLUTION 3050 /* in ppb */ +#define RX8025_ADJ_DATA_MAX 62 +#define RX8025_ADJ_DATA_MIN -62 + +enum rx_model { + model_rx_unknown, + model_rx_8025, + model_rx_8035, + model_last +}; + +static const struct i2c_device_id rx8025_id[] = { + { .name = "rx8025", .driver_data = model_rx_8025 }, + { .name = "rx8035", .driver_data = model_rx_8035 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rx8025_id); + +struct rx8025_data { + struct rtc_device *rtc; + enum rx_model model; + u8 ctrl1; + int is_24; +}; + +static s32 rx8025_read_reg(const struct i2c_client *client, u8 number) +{ + return i2c_smbus_read_byte_data(client, number << 4); +} + +static int rx8025_read_regs(const struct i2c_client *client, + u8 number, u8 length, u8 *values) +{ + int ret = i2c_smbus_read_i2c_block_data(client, number << 4, length, + values); + if (ret != length) + return ret < 0 ? ret : -EIO; + + return 0; +} + +static s32 rx8025_write_reg(const struct i2c_client *client, u8 number, + u8 value) +{ + return i2c_smbus_write_byte_data(client, number << 4, value); +} + +static s32 rx8025_write_regs(const struct i2c_client *client, + u8 number, u8 length, const u8 *values) +{ + return i2c_smbus_write_i2c_block_data(client, number << 4, + length, values); +} + +static int rx8025_is_osc_stopped(enum rx_model model, int ctrl2) +{ + int xstp = ctrl2 & RX8025_BIT_CTRL2_XST; + /* XSTP bit has different polarity on RX-8025 vs RX-8035. + * RX-8025: 0 == oscillator stopped + * RX-8035: 1 == oscillator stopped + */ + + if (model == model_rx_8025) + xstp = !xstp; + + return xstp; +} + +static int rx8025_check_validity(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rx8025_data *drvdata = dev_get_drvdata(dev); + int ctrl2; + int xstp; + + ctrl2 = rx8025_read_reg(client, RX8025_REG_CTRL2); + if (ctrl2 < 0) + return ctrl2; + + if (ctrl2 & RX8025_BIT_CTRL2_VDET) + dev_warn(dev, "power voltage drop detected\n"); + + if (ctrl2 & RX8025_BIT_CTRL2_PON) { + dev_warn(dev, "power-on reset detected, date is invalid\n"); + return -EINVAL; + } + + xstp = rx8025_is_osc_stopped(drvdata->model, ctrl2); + if (xstp) { + dev_warn(dev, "crystal stopped, date is invalid\n"); + return -EINVAL; + } + + return 0; +} + +static int rx8025_reset_validity(struct i2c_client *client) +{ + struct rx8025_data *drvdata = i2c_get_clientdata(client); + int ctrl2 = rx8025_read_reg(client, RX8025_REG_CTRL2); + + if (ctrl2 < 0) + return ctrl2; + + ctrl2 &= ~(RX8025_BIT_CTRL2_PON | RX8025_BIT_CTRL2_VDET); + + if (drvdata->model == model_rx_8025) + ctrl2 |= RX8025_BIT_CTRL2_XST; + else + ctrl2 &= ~(RX8025_BIT_CTRL2_XST); + + return rx8025_write_reg(client, RX8025_REG_CTRL2, + ctrl2); +} + +static irqreturn_t rx8025_handle_irq(int irq, void *dev_id) +{ + struct i2c_client *client = dev_id; + struct rx8025_data *rx8025 = i2c_get_clientdata(client); + int status, xstp; + + rtc_lock(rx8025->rtc); + status = rx8025_read_reg(client, RX8025_REG_CTRL2); + if (status < 0) + goto out; + + xstp = rx8025_is_osc_stopped(rx8025->model, status); + if (xstp) + dev_warn(&client->dev, "Oscillation stop was detected," + "you may have to readjust the clock\n"); + + if (status & RX8025_BIT_CTRL2_CTFG) { + /* periodic */ + status &= ~RX8025_BIT_CTRL2_CTFG; + rtc_update_irq(rx8025->rtc, 1, RTC_PF | RTC_IRQF); + } + + if (status & RX8025_BIT_CTRL2_DAFG) { + /* alarm */ + status &= RX8025_BIT_CTRL2_DAFG; + if (rx8025_write_reg(client, RX8025_REG_CTRL1, + rx8025->ctrl1 & ~RX8025_BIT_CTRL1_DALE)) + goto out; + rtc_update_irq(rx8025->rtc, 1, RTC_AF | RTC_IRQF); + } + +out: + rtc_unlock(rx8025->rtc); + + return IRQ_HANDLED; +} + +static int rx8025_get_time(struct device *dev, struct rtc_time *dt) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rx8025_data *rx8025 = dev_get_drvdata(dev); + u8 date[7]; + int err; + + err = rx8025_check_validity(dev); + if (err) + return err; + + err = rx8025_read_regs(client, RX8025_REG_SEC, 7, date); + if (err) + return err; + + dev_dbg(dev, "%s: read %7ph\n", __func__, date); + + dt->tm_sec = bcd2bin(date[RX8025_REG_SEC] & 0x7f); + dt->tm_min = bcd2bin(date[RX8025_REG_MIN] & 0x7f); + if (rx8025->is_24) + dt->tm_hour = bcd2bin(date[RX8025_REG_HOUR] & 0x3f); + else + dt->tm_hour = bcd2bin(date[RX8025_REG_HOUR] & 0x1f) % 12 + + (date[RX8025_REG_HOUR] & 0x20 ? 12 : 0); + + dt->tm_mday = bcd2bin(date[RX8025_REG_MDAY] & 0x3f); + dt->tm_mon = bcd2bin(date[RX8025_REG_MONTH] & 0x1f) - 1; + dt->tm_year = bcd2bin(date[RX8025_REG_YEAR]) + 100; + + dev_dbg(dev, "%s: date %ptRr\n", __func__, dt); + + return 0; +} + +static int rx8025_set_time(struct device *dev, struct rtc_time *dt) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rx8025_data *rx8025 = dev_get_drvdata(dev); + u8 date[7]; + int ret; + + /* + * Here the read-only bits are written as "0". I'm not sure if that + * is sound. + */ + date[RX8025_REG_SEC] = bin2bcd(dt->tm_sec); + date[RX8025_REG_MIN] = bin2bcd(dt->tm_min); + if (rx8025->is_24) + date[RX8025_REG_HOUR] = bin2bcd(dt->tm_hour); + else + date[RX8025_REG_HOUR] = (dt->tm_hour >= 12 ? 0x20 : 0) + | bin2bcd((dt->tm_hour + 11) % 12 + 1); + + date[RX8025_REG_WDAY] = bin2bcd(dt->tm_wday); + date[RX8025_REG_MDAY] = bin2bcd(dt->tm_mday); + date[RX8025_REG_MONTH] = bin2bcd(dt->tm_mon + 1); + date[RX8025_REG_YEAR] = bin2bcd(dt->tm_year - 100); + + dev_dbg(dev, "%s: write %7ph\n", __func__, date); + + ret = rx8025_write_regs(client, RX8025_REG_SEC, 7, date); + if (ret < 0) + return ret; + + return rx8025_reset_validity(client); +} + +static int rx8025_init_client(struct i2c_client *client) +{ + struct rx8025_data *rx8025 = i2c_get_clientdata(client); + u8 ctrl[2], ctrl2; + int need_clear = 0; + int hour_reg; + int err; + + err = rx8025_read_regs(client, RX8025_REG_CTRL1, 2, ctrl); + if (err) + goto out; + + /* Keep test bit zero ! */ + rx8025->ctrl1 = ctrl[0] & ~RX8025_BIT_CTRL1_TEST; + + if (ctrl[1] & (RX8025_BIT_CTRL2_DAFG | RX8025_BIT_CTRL2_WAFG)) { + dev_warn(&client->dev, "Alarm was detected\n"); + need_clear = 1; + } + + if (ctrl[1] & RX8025_BIT_CTRL2_CTFG) + need_clear = 1; + + if (need_clear) { + ctrl2 = ctrl[1]; + ctrl2 &= ~(RX8025_BIT_CTRL2_CTFG | RX8025_BIT_CTRL2_WAFG | + RX8025_BIT_CTRL2_DAFG); + + err = rx8025_write_reg(client, RX8025_REG_CTRL2, ctrl2); + } + + if (rx8025->model == model_rx_8035) { + /* In RX-8035, 12/24 flag is in the hour register */ + hour_reg = rx8025_read_reg(client, RX8025_REG_HOUR); + if (hour_reg < 0) + return hour_reg; + rx8025->is_24 = (hour_reg & RX8035_BIT_HOUR_1224); + } else { + rx8025->is_24 = (ctrl[0] & RX8025_BIT_CTRL1_1224); + } +out: + return err; +} + +/* Alarm support */ +static int rx8025_read_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rx8025_data *rx8025 = dev_get_drvdata(dev); + u8 ald[2]; + int ctrl2, err; + + err = rx8025_read_regs(client, RX8025_REG_ALDMIN, 2, ald); + if (err) + return err; + + ctrl2 = rx8025_read_reg(client, RX8025_REG_CTRL2); + if (ctrl2 < 0) + return ctrl2; + + dev_dbg(dev, "%s: read alarm 0x%02x 0x%02x ctrl2 %02x\n", + __func__, ald[0], ald[1], ctrl2); + + /* Hardware alarms precision is 1 minute! */ + t->time.tm_sec = 0; + t->time.tm_min = bcd2bin(ald[0] & 0x7f); + if (rx8025->is_24) + t->time.tm_hour = bcd2bin(ald[1] & 0x3f); + else + t->time.tm_hour = bcd2bin(ald[1] & 0x1f) % 12 + + (ald[1] & 0x20 ? 12 : 0); + + dev_dbg(dev, "%s: date: %ptRr\n", __func__, &t->time); + t->enabled = !!(rx8025->ctrl1 & RX8025_BIT_CTRL1_DALE); + t->pending = (ctrl2 & RX8025_BIT_CTRL2_DAFG) && t->enabled; + + return err; +} + +static int rx8025_set_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rx8025_data *rx8025 = dev_get_drvdata(dev); + u8 ald[2]; + int err; + + ald[0] = bin2bcd(t->time.tm_min); + if (rx8025->is_24) + ald[1] = bin2bcd(t->time.tm_hour); + else + ald[1] = (t->time.tm_hour >= 12 ? 0x20 : 0) + | bin2bcd((t->time.tm_hour + 11) % 12 + 1); + + dev_dbg(dev, "%s: write 0x%02x 0x%02x\n", __func__, ald[0], ald[1]); + + if (rx8025->ctrl1 & RX8025_BIT_CTRL1_DALE) { + rx8025->ctrl1 &= ~RX8025_BIT_CTRL1_DALE; + err = rx8025_write_reg(client, RX8025_REG_CTRL1, + rx8025->ctrl1); + if (err) + return err; + } + err = rx8025_write_regs(client, RX8025_REG_ALDMIN, 2, ald); + if (err) + return err; + + if (t->enabled) { + rx8025->ctrl1 |= RX8025_BIT_CTRL1_DALE; + err = rx8025_write_reg(client, RX8025_REG_CTRL1, + rx8025->ctrl1); + if (err) + return err; + } + + return 0; +} + +static int rx8025_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rx8025_data *rx8025 = dev_get_drvdata(dev); + u8 ctrl1; + int err; + + ctrl1 = rx8025->ctrl1; + if (enabled) + ctrl1 |= RX8025_BIT_CTRL1_DALE; + else + ctrl1 &= ~RX8025_BIT_CTRL1_DALE; + + if (ctrl1 != rx8025->ctrl1) { + rx8025->ctrl1 = ctrl1; + err = rx8025_write_reg(client, RX8025_REG_CTRL1, + rx8025->ctrl1); + if (err) + return err; + } + return 0; +} + +/* + * According to the RX8025 SA/NB application manual the frequency and + * temperature characteristics can be approximated using the following + * equation: + * + * df = a * (ut - t)**2 + * + * df: Frequency deviation in any temperature + * a : Coefficient = (-35 +-5) * 10**-9 + * ut: Ultimate temperature in degree = +25 +-5 degree + * t : Any temperature in degree + */ +static int rx8025_read_offset(struct device *dev, long *offset) +{ + struct i2c_client *client = to_i2c_client(dev); + int digoff; + + digoff = rx8025_read_reg(client, RX8025_REG_DIGOFF); + if (digoff < 0) + return digoff; + + *offset = digoff >= 64 ? digoff - 128 : digoff; + if (*offset > 0) + (*offset)--; + *offset *= RX8025_ADJ_RESOLUTION; + + return 0; +} + +static int rx8025_set_offset(struct device *dev, long offset) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 digoff; + + offset /= RX8025_ADJ_RESOLUTION; + if (offset > RX8025_ADJ_DATA_MAX) + offset = RX8025_ADJ_DATA_MAX; + else if (offset < RX8025_ADJ_DATA_MIN) + offset = RX8025_ADJ_DATA_MIN; + else if (offset > 0) + offset++; + else if (offset < 0) + offset += 128; + digoff = offset; + + return rx8025_write_reg(client, RX8025_REG_DIGOFF, digoff); +} + +static const struct rtc_class_ops rx8025_rtc_ops = { + .read_time = rx8025_get_time, + .set_time = rx8025_set_time, + .read_alarm = rx8025_read_alarm, + .set_alarm = rx8025_set_alarm, + .alarm_irq_enable = rx8025_alarm_irq_enable, + .read_offset = rx8025_read_offset, + .set_offset = rx8025_set_offset, +}; + +static ssize_t rx8025_sysfs_show_clock_adjust(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + long adj; + int err; + + dev_warn_once(dev, "clock_adjust_ppb is deprecated, use offset\n"); + err = rx8025_read_offset(dev, &adj); + if (err) + return err; + + return sprintf(buf, "%ld\n", -adj); +} + +static ssize_t rx8025_sysfs_store_clock_adjust(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + long adj; + int err; + + dev_warn_once(dev, "clock_adjust_ppb is deprecated, use offset\n"); + if (kstrtol(buf, 10, &adj) != 0) + return -EINVAL; + + err = rx8025_set_offset(dev, -adj); + + return err ? err : count; +} + +static DEVICE_ATTR(clock_adjust_ppb, S_IRUGO | S_IWUSR, + rx8025_sysfs_show_clock_adjust, + rx8025_sysfs_store_clock_adjust); + +static struct attribute *rx8025_attrs[] = { + &dev_attr_clock_adjust_ppb.attr, + NULL +}; + +static const struct attribute_group rx8025_attr_group = { + .attrs = rx8025_attrs, +}; + +static int rx8025_probe(struct i2c_client *client) +{ + struct i2c_adapter *adapter = client->adapter; + struct rx8025_data *rx8025; + int err = 0; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA + | I2C_FUNC_SMBUS_I2C_BLOCK)) { + dev_err(&adapter->dev, + "doesn't support required functionality\n"); + return -EIO; + } + + rx8025 = devm_kzalloc(&client->dev, sizeof(*rx8025), GFP_KERNEL); + if (!rx8025) + return -ENOMEM; + + i2c_set_clientdata(client, rx8025); + + rx8025->model = (uintptr_t)i2c_get_match_data(client); + + err = rx8025_init_client(client); + if (err) + return err; + + rx8025->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(rx8025->rtc)) + return PTR_ERR(rx8025->rtc); + + rx8025->rtc->ops = &rx8025_rtc_ops; + rx8025->rtc->range_min = RTC_TIMESTAMP_BEGIN_1900; + rx8025->rtc->range_max = RTC_TIMESTAMP_END_2099; + + if (client->irq > 0) { + dev_info(&client->dev, "IRQ %d supplied\n", client->irq); + err = devm_request_threaded_irq(&client->dev, client->irq, NULL, + rx8025_handle_irq, + IRQF_ONESHOT, + "rx8025", client); + if (err) + clear_bit(RTC_FEATURE_ALARM, rx8025->rtc->features); + } + + set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rx8025->rtc->features); + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rx8025->rtc->features); + + err = rtc_add_group(rx8025->rtc, &rx8025_attr_group); + if (err) + return err; + + return devm_rtc_register_device(rx8025->rtc); +} + +static struct i2c_driver rx8025_driver = { + .driver = { + .name = "rtc-rx8025", + }, + .probe = rx8025_probe, + .id_table = rx8025_id, +}; + +module_i2c_driver(rx8025_driver); + +MODULE_AUTHOR("Wolfgang Grandegger "); +MODULE_DESCRIPTION("RX-8025 SA/NB RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-rx8111.c b/drivers/rtc/rtc-rx8111.c new file mode 100644 index 000000000..8450d9f0b --- /dev/null +++ b/drivers/rtc/rtc-rx8111.c @@ -0,0 +1,368 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Epson RX8111 RTC. + * + * Copyright (C) 2023 Axis Communications AB + */ + +#include +#include +#include +#include +#include + +#include + +#define RX8111_REG_SEC 0x10 /* Second counter. */ +#define RX8111_REG_MIN 0x11 /* Minute counter */ +#define RX8111_REG_HOUR 0x12 /* Hour counter. */ +#define RX8111_REG_WEEK 0x13 /* Week day counter. */ +#define RX8111_REG_DAY 0x14 /* Month day counter. */ +#define RX8111_REG_MONTH 0x15 /* Month counter. */ +#define RX8111_REG_YEAR 0x16 /* Year counter. */ + +#define RX8111_REG_ALARM_MIN 0x17 /* Alarm minute. */ +#define RX8111_REG_ALARM_HOUR 0x18 /* Alarm hour. */ +#define RX8111_REG_ALARM_WEEK_DAY 0x19 /* Alarm week or month day. */ + +#define RX8111_REG_TIMER_COUNTER0 0x1a /* Timer counter LSB. */ +#define RX8111_REG_TIMER_COUNTER1 0x1b /* Timer counter. */ +#define RX8111_REG_TIMER_COUNTER2 0x1c /* Timer counter MSB. */ + +#define RX8111_REG_EXT 0x1d /* Extension register. */ +#define RX8111_REG_FLAG 0x1e /* Flag register. */ +#define RX8111_REG_CTRL 0x1f /* Control register. */ + +#define RX8111_REG_TS_1_1000_SEC 0x20 /* Timestamp 256 or 512 Hz . */ +#define RX8111_REG_TS_1_100_SEC 0x21 /* Timestamp 1 - 128 Hz. */ +#define RX8111_REG_TS_SEC 0x22 /* Timestamp second. */ +#define RX8111_REG_TS_MIN 0x23 /* Timestamp minute. */ +#define RX8111_REG_TS_HOUR 0x24 /* Timestamp hour. */ +#define RX8111_REG_TS_WEEK 0x25 /* Timestamp week day. */ +#define RX8111_REG_TS_DAY 0x26 /* Timestamp month day. */ +#define RX8111_REG_TS_MONTH 0x27 /* Timestamp month. */ +#define RX8111_REG_TS_YEAR 0x28 /* Timestamp year. */ +#define RX8111_REG_TS_STATUS 0x29 /* Timestamp status. */ + +#define RX8111_REG_EVIN_SETTING 0x2b /* Timestamp trigger setting. */ +#define RX8111_REG_ALARM_SEC 0x2c /* Alarm second. */ +#define RX8111_REG_TIMER_CTRL 0x2d /* Timer control. */ +#define RX8111_REG_TS_CTRL0 0x2e /* Timestamp control 0. */ +#define RX8111_REG_CMD_TRIGGER 0x2f /* Timestamp trigger. */ +#define RX8111_REG_PWR_SWITCH_CTRL 0x32 /* Power switch control. */ +#define RX8111_REG_STATUS_MON 0x33 /* Status monitor. */ +#define RX8111_REG_TS_CTRL1 0x34 /* Timestamp control 1. */ +#define RX8111_REG_TS_CTRL2 0x35 /* Timestamp control 2. */ +#define RX8111_REG_TS_CTRL3 0x36 /* Timestamp control 3. */ + +#define RX8111_FLAG_XST_BIT BIT(0) +#define RX8111_FLAG_VLF_BIT BIT(1) + +#define RX8111_TIME_BUF_SZ (RX8111_REG_YEAR - RX8111_REG_SEC + 1) + +enum rx8111_regfield { + /* RX8111_REG_EXT. */ + RX8111_REGF_TSEL0, + RX8111_REGF_TSEL1, + RX8111_REGF_ETS, + RX8111_REGF_WADA, + RX8111_REGF_TE, + RX8111_REGF_USEL, + RX8111_REGF_FSEL0, + RX8111_REGF_FSEL1, + + /* RX8111_REG_FLAG. */ + RX8111_REGF_XST, + RX8111_REGF_VLF, + RX8111_REGF_EVF, + RX8111_REGF_AF, + RX8111_REGF_TF, + RX8111_REGF_UF, + RX8111_REGF_POR, + + /* RX8111_REG_CTRL. */ + RX8111_REGF_STOP, + RX8111_REGF_EIE, + RX8111_REGF_AIE, + RX8111_REGF_TIE, + RX8111_REGF_UIE, + + /* RX8111_REG_PWR_SWITCH_CTRL. */ + RX8111_REGF_SMPT0, + RX8111_REGF_SMPT1, + RX8111_REGF_SWSEL0, + RX8111_REGF_SWSEL1, + RX8111_REGF_INIEN, + RX8111_REGF_CHGEN, + + /* RX8111_REG_STATUS_MON. */ + RX8111_REGF_VLOW, + + /* Sentinel value. */ + RX8111_REGF_MAX +}; + +static const struct reg_field rx8111_regfields[] = { + [RX8111_REGF_TSEL0] = REG_FIELD(RX8111_REG_EXT, 0, 0), + [RX8111_REGF_TSEL1] = REG_FIELD(RX8111_REG_EXT, 1, 1), + [RX8111_REGF_ETS] = REG_FIELD(RX8111_REG_EXT, 2, 2), + [RX8111_REGF_WADA] = REG_FIELD(RX8111_REG_EXT, 3, 3), + [RX8111_REGF_TE] = REG_FIELD(RX8111_REG_EXT, 4, 4), + [RX8111_REGF_USEL] = REG_FIELD(RX8111_REG_EXT, 5, 5), + [RX8111_REGF_FSEL0] = REG_FIELD(RX8111_REG_EXT, 6, 6), + [RX8111_REGF_FSEL1] = REG_FIELD(RX8111_REG_EXT, 7, 7), + + [RX8111_REGF_XST] = REG_FIELD(RX8111_REG_FLAG, 0, 0), + [RX8111_REGF_VLF] = REG_FIELD(RX8111_REG_FLAG, 1, 1), + [RX8111_REGF_EVF] = REG_FIELD(RX8111_REG_FLAG, 2, 2), + [RX8111_REGF_AF] = REG_FIELD(RX8111_REG_FLAG, 3, 3), + [RX8111_REGF_TF] = REG_FIELD(RX8111_REG_FLAG, 4, 4), + [RX8111_REGF_UF] = REG_FIELD(RX8111_REG_FLAG, 5, 5), + [RX8111_REGF_POR] = REG_FIELD(RX8111_REG_FLAG, 7, 7), + + [RX8111_REGF_STOP] = REG_FIELD(RX8111_REG_CTRL, 0, 0), + [RX8111_REGF_EIE] = REG_FIELD(RX8111_REG_CTRL, 2, 2), + [RX8111_REGF_AIE] = REG_FIELD(RX8111_REG_CTRL, 3, 3), + [RX8111_REGF_TIE] = REG_FIELD(RX8111_REG_CTRL, 4, 4), + [RX8111_REGF_UIE] = REG_FIELD(RX8111_REG_CTRL, 5, 5), + + [RX8111_REGF_SMPT0] = REG_FIELD(RX8111_REG_PWR_SWITCH_CTRL, 0, 0), + [RX8111_REGF_SMPT1] = REG_FIELD(RX8111_REG_PWR_SWITCH_CTRL, 1, 1), + [RX8111_REGF_SWSEL0] = REG_FIELD(RX8111_REG_PWR_SWITCH_CTRL, 2, 2), + [RX8111_REGF_SWSEL1] = REG_FIELD(RX8111_REG_PWR_SWITCH_CTRL, 3, 3), + [RX8111_REGF_INIEN] = REG_FIELD(RX8111_REG_PWR_SWITCH_CTRL, 6, 6), + [RX8111_REGF_CHGEN] = REG_FIELD(RX8111_REG_PWR_SWITCH_CTRL, 7, 7), + + [RX8111_REGF_VLOW] = REG_FIELD(RX8111_REG_STATUS_MON, 1, 1), +}; + +static const struct regmap_config rx8111_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = RX8111_REG_TS_CTRL3, +}; + +struct rx8111_data { + struct regmap *regmap; + struct regmap_field *regfields[RX8111_REGF_MAX]; + struct device *dev; + struct rtc_device *rtc; +}; + +static int rx8111_read_vl_flag(struct rx8111_data *data, unsigned int *vlval) +{ + int ret; + + ret = regmap_field_read(data->regfields[RX8111_REGF_VLF], vlval); + if (ret) + dev_dbg(data->dev, "Could not read VL flag (%d)", ret); + + return ret; +} + +static int rx8111_read_time(struct device *dev, struct rtc_time *tm) +{ + struct rx8111_data *data = dev_get_drvdata(dev); + u8 buf[RX8111_TIME_BUF_SZ]; + unsigned int regval; + int ret; + + /* Check status. */ + ret = regmap_read(data->regmap, RX8111_REG_FLAG, ®val); + if (ret) { + dev_dbg(data->dev, "Could not read flag register (%d)\n", ret); + return ret; + } + + if (FIELD_GET(RX8111_FLAG_XST_BIT, regval)) { + dev_dbg(data->dev, + "Crystal oscillation stopped, time is not reliable\n"); + return -EINVAL; + } + + if (FIELD_GET(RX8111_FLAG_VLF_BIT, regval)) { + dev_dbg(data->dev, + "Low voltage detected, time is not reliable\n"); + return -EINVAL; + } + + ret = regmap_field_read(data->regfields[RX8111_REGF_STOP], ®val); + if (ret) { + dev_dbg(data->dev, "Could not read clock status (%d)\n", ret); + return ret; + } + + if (regval) { + dev_dbg(data->dev, "Clock stopped, time is not reliable\n"); + return -EINVAL; + } + + /* Read time. */ + ret = regmap_bulk_read(data->regmap, RX8111_REG_SEC, buf, + ARRAY_SIZE(buf)); + if (ret) { + dev_dbg(data->dev, "Could not bulk read time (%d)\n", ret); + return ret; + } + + tm->tm_sec = bcd2bin(buf[0]); + tm->tm_min = bcd2bin(buf[1]); + tm->tm_hour = bcd2bin(buf[2]); + tm->tm_wday = ffs(buf[3]) - 1; + tm->tm_mday = bcd2bin(buf[4]); + tm->tm_mon = bcd2bin(buf[5]) - 1; + tm->tm_year = bcd2bin(buf[6]) + 100; + + return 0; +} + +static int rx8111_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rx8111_data *data = dev_get_drvdata(dev); + u8 buf[RX8111_TIME_BUF_SZ]; + int ret; + + buf[0] = bin2bcd(tm->tm_sec); + buf[1] = bin2bcd(tm->tm_min); + buf[2] = bin2bcd(tm->tm_hour); + buf[3] = BIT(tm->tm_wday); + buf[4] = bin2bcd(tm->tm_mday); + buf[5] = bin2bcd(tm->tm_mon + 1); + buf[6] = bin2bcd(tm->tm_year - 100); + + ret = regmap_clear_bits(data->regmap, RX8111_REG_FLAG, + RX8111_FLAG_XST_BIT | RX8111_FLAG_VLF_BIT); + if (ret) + return ret; + + /* Stop the clock. */ + ret = regmap_field_write(data->regfields[RX8111_REGF_STOP], 1); + if (ret) { + dev_dbg(data->dev, "Could not stop the clock (%d)\n", ret); + return ret; + } + + /* Set the time. */ + ret = regmap_bulk_write(data->regmap, RX8111_REG_SEC, buf, + ARRAY_SIZE(buf)); + if (ret) { + dev_dbg(data->dev, "Could not bulk write time (%d)\n", ret); + + /* + * We don't bother with trying to start the clock again. We + * check for this in rx8111_read_time() (and thus force user to + * call rx8111_set_time() to try again). + */ + return ret; + } + + /* Start the clock. */ + ret = regmap_field_write(data->regfields[RX8111_REGF_STOP], 0); + if (ret) { + dev_dbg(data->dev, "Could not start the clock (%d)\n", ret); + return ret; + } + + return 0; +} + +static int rx8111_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct rx8111_data *data = dev_get_drvdata(dev); + unsigned int regval; + unsigned int vlval; + int ret; + + switch (cmd) { + case RTC_VL_READ: + ret = rx8111_read_vl_flag(data, ®val); + if (ret) + return ret; + + vlval = regval ? RTC_VL_DATA_INVALID : 0; + + ret = regmap_field_read(data->regfields[RX8111_REGF_VLOW], + ®val); + if (ret) + return ret; + + vlval |= regval ? RTC_VL_BACKUP_LOW : 0; + + return put_user(vlval, (typeof(vlval) __user *)arg); + default: + return -ENOIOCTLCMD; + } +} + +static const struct rtc_class_ops rx8111_rtc_ops = { + .read_time = rx8111_read_time, + .set_time = rx8111_set_time, + .ioctl = rx8111_ioctl, +}; + +static int rx8111_probe(struct i2c_client *client) +{ + struct rx8111_data *data; + struct rtc_device *rtc; + size_t i; + + data = devm_kmalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) { + dev_dbg(&client->dev, "Could not allocate device data\n"); + return -ENOMEM; + } + + data->dev = &client->dev; + dev_set_drvdata(data->dev, data); + + data->regmap = devm_regmap_init_i2c(client, &rx8111_regmap_config); + if (IS_ERR(data->regmap)) { + dev_dbg(data->dev, "Could not initialize regmap\n"); + return PTR_ERR(data->regmap); + } + + for (i = 0; i < RX8111_REGF_MAX; ++i) { + data->regfields[i] = devm_regmap_field_alloc( + data->dev, data->regmap, rx8111_regfields[i]); + if (IS_ERR(data->regfields[i])) { + dev_dbg(data->dev, + "Could not allocate register field %zu\n", i); + return PTR_ERR(data->regfields[i]); + } + } + + rtc = devm_rtc_allocate_device(data->dev); + if (IS_ERR(rtc)) { + dev_dbg(data->dev, "Could not allocate rtc device\n"); + return PTR_ERR(rtc); + } + + rtc->ops = &rx8111_rtc_ops; + rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rtc->range_max = RTC_TIMESTAMP_END_2099; + + clear_bit(RTC_FEATURE_ALARM, rtc->features); + + return devm_rtc_register_device(rtc); +} + +static const struct of_device_id rx8111_of_match[] = { + { + .compatible = "epson,rx8111", + }, + {} +}; +MODULE_DEVICE_TABLE(of, rx8111_of_match); + +static struct i2c_driver rx8111_driver = { + .driver = { + .name = "rtc-rx8111", + .of_match_table = rx8111_of_match, + }, + .probe = rx8111_probe, +}; +module_i2c_driver(rx8111_driver); + +MODULE_AUTHOR("Waqar Hameed "); +MODULE_DESCRIPTION("Epson RX8111 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-rx8581.c b/drivers/rtc/rtc-rx8581.c new file mode 100644 index 000000000..cf4dab94c --- /dev/null +++ b/drivers/rtc/rtc-rx8581.c @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * An I2C driver for the Epson RX8581 RTC + * + * Author: Martyn Welch + * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc. + * + * Based on: rtc-pcf8563.c (An I2C driver for the Philips PCF8563 RTC) + * Copyright 2005-06 Tower Technologies + */ + +#include +#include +#include +#include +#include +#include +#include + +#define RX8581_REG_SC 0x00 /* Second in BCD */ +#define RX8581_REG_MN 0x01 /* Minute in BCD */ +#define RX8581_REG_HR 0x02 /* Hour in BCD */ +#define RX8581_REG_DW 0x03 /* Day of Week */ +#define RX8581_REG_DM 0x04 /* Day of Month in BCD */ +#define RX8581_REG_MO 0x05 /* Month in BCD */ +#define RX8581_REG_YR 0x06 /* Year in BCD */ +#define RX8581_REG_RAM 0x07 /* RAM */ +#define RX8581_REG_AMN 0x08 /* Alarm Min in BCD*/ +#define RX8581_REG_AHR 0x09 /* Alarm Hour in BCD */ +#define RX8581_REG_ADM 0x0A +#define RX8581_REG_ADW 0x0A +#define RX8581_REG_TMR0 0x0B +#define RX8581_REG_TMR1 0x0C +#define RX8581_REG_EXT 0x0D /* Extension Register */ +#define RX8581_REG_FLAG 0x0E /* Flag Register */ +#define RX8581_REG_CTRL 0x0F /* Control Register */ + + +/* Flag Register bit definitions */ +#define RX8581_FLAG_UF 0x20 /* Update */ +#define RX8581_FLAG_TF 0x10 /* Timer */ +#define RX8581_FLAG_AF 0x08 /* Alarm */ +#define RX8581_FLAG_VLF 0x02 /* Voltage Low */ + +/* Control Register bit definitions */ +#define RX8581_CTRL_UIE 0x20 /* Update Interrupt Enable */ +#define RX8581_CTRL_TIE 0x10 /* Timer Interrupt Enable */ +#define RX8581_CTRL_AIE 0x08 /* Alarm Interrupt Enable */ +#define RX8581_CTRL_STOP 0x02 /* STOP bit */ +#define RX8581_CTRL_RESET 0x01 /* RESET bit */ + +#define RX8571_USER_RAM 0x10 +#define RX8571_NVRAM_SIZE 0x10 + +struct rx85x1_config { + struct regmap_config regmap; + unsigned int num_nvram; +}; + +/* + * In the routines that deal directly with the rx8581 hardware, we use + * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. + */ +static int rx8581_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned char date[7]; + unsigned int data; + int err; + struct regmap *regmap = i2c_get_clientdata(client); + + /* First we ensure that the "update flag" is not set, we read the + * time and date then re-read the "update flag". If the update flag + * has been set, we know that the time has changed during the read so + * we repeat the whole process again. + */ + err = regmap_read(regmap, RX8581_REG_FLAG, &data); + if (err < 0) + return err; + + if (data & RX8581_FLAG_VLF) { + dev_warn(dev, + "low voltage detected, date/time is not reliable.\n"); + return -EINVAL; + } + + do { + /* If update flag set, clear it */ + if (data & RX8581_FLAG_UF) { + err = regmap_write(regmap, RX8581_REG_FLAG, + data & ~RX8581_FLAG_UF); + if (err < 0) + return err; + } + + /* Now read time and date */ + err = regmap_bulk_read(regmap, RX8581_REG_SC, date, + sizeof(date)); + if (err < 0) + return err; + + /* Check flag register */ + err = regmap_read(regmap, RX8581_REG_FLAG, &data); + if (err < 0) + return err; + } while (data & RX8581_FLAG_UF); + + dev_dbg(dev, "%s: raw data is sec=%02x, min=%02x, hr=%02x, " + "wday=%02x, mday=%02x, mon=%02x, year=%02x\n", + __func__, + date[0], date[1], date[2], date[3], date[4], date[5], date[6]); + + tm->tm_sec = bcd2bin(date[RX8581_REG_SC] & 0x7F); + tm->tm_min = bcd2bin(date[RX8581_REG_MN] & 0x7F); + tm->tm_hour = bcd2bin(date[RX8581_REG_HR] & 0x3F); /* rtc hr 0-23 */ + tm->tm_wday = ilog2(date[RX8581_REG_DW] & 0x7F); + tm->tm_mday = bcd2bin(date[RX8581_REG_DM] & 0x3F); + tm->tm_mon = bcd2bin(date[RX8581_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */ + tm->tm_year = bcd2bin(date[RX8581_REG_YR]) + 100; + + dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + return 0; +} + +static int rx8581_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + int err; + unsigned char buf[7]; + struct regmap *regmap = i2c_get_clientdata(client); + + dev_dbg(dev, "%s: secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + /* hours, minutes and seconds */ + buf[RX8581_REG_SC] = bin2bcd(tm->tm_sec); + buf[RX8581_REG_MN] = bin2bcd(tm->tm_min); + buf[RX8581_REG_HR] = bin2bcd(tm->tm_hour); + + buf[RX8581_REG_DM] = bin2bcd(tm->tm_mday); + + /* month, 1 - 12 */ + buf[RX8581_REG_MO] = bin2bcd(tm->tm_mon + 1); + + /* year and century */ + buf[RX8581_REG_YR] = bin2bcd(tm->tm_year - 100); + buf[RX8581_REG_DW] = (0x1 << tm->tm_wday); + + /* Stop the clock */ + err = regmap_update_bits(regmap, RX8581_REG_CTRL, + RX8581_CTRL_STOP, RX8581_CTRL_STOP); + if (err < 0) + return err; + + /* write register's data */ + err = regmap_bulk_write(regmap, RX8581_REG_SC, buf, sizeof(buf)); + if (err < 0) + return err; + + /* get VLF and clear it */ + err = regmap_update_bits(regmap, RX8581_REG_FLAG, RX8581_FLAG_VLF, 0); + if (err < 0) + return err; + + /* Restart the clock */ + return regmap_update_bits(regmap, RX8581_REG_CTRL, + RX8581_CTRL_STOP, 0); +} + +static const struct rtc_class_ops rx8581_rtc_ops = { + .read_time = rx8581_rtc_read_time, + .set_time = rx8581_rtc_set_time, +}; + +static int rx8571_nvram_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct regmap *regmap = priv; + + return regmap_bulk_read(regmap, RX8571_USER_RAM + offset, val, bytes); +} + +static int rx8571_nvram_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct regmap *regmap = priv; + + return regmap_bulk_write(regmap, RX8571_USER_RAM + offset, val, bytes); +} + +static int rx85x1_nvram_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct regmap *regmap = priv; + unsigned int tmp_val; + int ret; + + ret = regmap_read(regmap, RX8581_REG_RAM, &tmp_val); + (*(unsigned char *)val) = (unsigned char) tmp_val; + + return ret; +} + +static int rx85x1_nvram_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct regmap *regmap = priv; + unsigned char tmp_val; + + tmp_val = *((unsigned char *)val); + return regmap_write(regmap, RX8581_REG_RAM, (unsigned int)tmp_val); +} + +static const struct rx85x1_config rx8581_config = { + .regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xf, + }, + .num_nvram = 1 +}; + +static const struct rx85x1_config rx8571_config = { + .regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x1f, + }, + .num_nvram = 2 +}; + +static int rx8581_probe(struct i2c_client *client) +{ + struct regmap *regmap; + const struct rx85x1_config *config = &rx8581_config; + const void *data = of_device_get_match_data(&client->dev); + struct rtc_device *rtc; + static struct nvmem_config nvmem_cfg[] = { + { + .name = "rx85x1-", + .word_size = 1, + .stride = 1, + .size = 1, + .reg_read = rx85x1_nvram_read, + .reg_write = rx85x1_nvram_write, + }, { + .name = "rx8571-", + .word_size = 1, + .stride = 1, + .size = RX8571_NVRAM_SIZE, + .reg_read = rx8571_nvram_read, + .reg_write = rx8571_nvram_write, + }, + }; + int ret, i; + + dev_dbg(&client->dev, "%s\n", __func__); + + if (data) + config = data; + + regmap = devm_regmap_init_i2c(client, &config->regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + i2c_set_clientdata(client, regmap); + + rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + rtc->ops = &rx8581_rtc_ops; + rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rtc->range_max = RTC_TIMESTAMP_END_2099; + rtc->start_secs = 0; + rtc->set_start_time = true; + + ret = devm_rtc_register_device(rtc); + + for (i = 0; i < config->num_nvram; i++) { + nvmem_cfg[i].priv = regmap; + devm_rtc_nvmem_register(rtc, &nvmem_cfg[i]); + } + + return ret; +} + +static const struct i2c_device_id rx8581_id[] = { + { .name = "rx8581" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rx8581_id); + +static const __maybe_unused struct of_device_id rx8581_of_match[] = { + { .compatible = "epson,rx8571", .data = &rx8571_config }, + { .compatible = "epson,rx8581", .data = &rx8581_config }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rx8581_of_match); + +static struct i2c_driver rx8581_driver = { + .driver = { + .name = "rtc-rx8581", + .of_match_table = of_match_ptr(rx8581_of_match), + }, + .probe = rx8581_probe, + .id_table = rx8581_id, +}; + +module_i2c_driver(rx8581_driver); + +MODULE_AUTHOR("Martyn Welch "); +MODULE_DESCRIPTION("Epson RX-8571/RX-8581 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-rzn1.c b/drivers/rtc/rtc-rzn1.c new file mode 100644 index 000000000..b9d69ebdd --- /dev/null +++ b/drivers/rtc/rtc-rzn1.c @@ -0,0 +1,552 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Renesas RZ/N1 Real Time Clock interface for Linux + * + * Copyright: + * - 2014 Renesas Electronics Europe Limited + * - 2022 Schneider Electric + * + * Authors: + * - Michel Pollet + * - Miquel Raynal + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RZN1_RTC_CTL0 0x00 +#define RZN1_RTC_CTL0_SLSB_SCMP BIT(4) +#define RZN1_RTC_CTL0_AMPM BIT(5) +#define RZN1_RTC_CTL0_CEST BIT(6) +#define RZN1_RTC_CTL0_CE BIT(7) + +#define RZN1_RTC_CTL1 0x04 +#define RZN1_RTC_CTL1_1SE BIT(3) +#define RZN1_RTC_CTL1_ALME BIT(4) + +#define RZN1_RTC_CTL2 0x08 +#define RZN1_RTC_CTL2_WAIT BIT(0) +#define RZN1_RTC_CTL2_WST BIT(1) +#define RZN1_RTC_CTL2_WUST BIT(5) +#define RZN1_RTC_CTL2_STOPPED (RZN1_RTC_CTL2_WAIT | RZN1_RTC_CTL2_WST) + +#define RZN1_RTC_TIME 0x30 +#define RZN1_RTC_TIME_SEC GENMASK(7, 0) +#define RZN1_RTC_TIME_MIN GENMASK(15, 8) +#define RZN1_RTC_TIME_HOUR GENMASK(23, 16) + +#define RZN1_RTC_CAL 0x34 +#define RZN1_RTC_CAL_WDAY GENMASK(7, 0) +#define RZN1_RTC_CAL_DAY GENMASK(15, 8) +#define RZN1_RTC_CAL_MON GENMASK(23, 16) +#define RZN1_RTC_CAL_YEAR GENMASK(31, 24) + +#define RZN1_RTC_SUBU 0x38 +#define RZN1_RTC_SUBU_RTCA0FX GENMASK(5, 0) +#define RZN1_RTC_SUBU_DEV BIT(7) +#define RZN1_RTC_SUBU_DECR BIT(6) + +#define RZN1_RTC_SCMP 0x3c + +#define RZN1_RTC_ALM 0x40 +#define RZN1_RTC_ALH 0x44 +#define RZN1_RTC_ALW 0x48 + +#define RZN1_RTC_SECC 0x4c +#define RZN1_RTC_TIMEC 0x68 +#define RZN1_RTC_CALC 0x6c + +struct rzn1_rtc_data { + bool has_subu; +}; + +struct rzn1_rtc { + struct rtc_device *rtcdev; + void __iomem *base; + /* + * Protects access to RZN1_RTC_CTL1 reg. rtc_lock with threaded_irqs + * would introduce race conditions when switching interrupts because + * of potential sleeps + */ + spinlock_t ctl1_access_lock; + struct rtc_time tm_alarm; + unsigned long sync_time; +}; + +static void rzn1_rtc_get_time_snapshot(struct rzn1_rtc *rtc, struct rtc_time *tm) +{ + u32 val; + + val = readl(rtc->base + RZN1_RTC_TIMEC); + tm->tm_sec = bcd2bin(FIELD_GET(RZN1_RTC_TIME_SEC, val)); + tm->tm_min = bcd2bin(FIELD_GET(RZN1_RTC_TIME_MIN, val)); + tm->tm_hour = bcd2bin(FIELD_GET(RZN1_RTC_TIME_HOUR, val)); + + val = readl(rtc->base + RZN1_RTC_CALC); + tm->tm_wday = FIELD_GET(RZN1_RTC_CAL_WDAY, val); + tm->tm_mday = bcd2bin(FIELD_GET(RZN1_RTC_CAL_DAY, val)); + tm->tm_mon = bcd2bin(FIELD_GET(RZN1_RTC_CAL_MON, val)) - 1; + tm->tm_year = bcd2bin(FIELD_GET(RZN1_RTC_CAL_YEAR, val)) + 100; +} + +static int rzn1_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct rzn1_rtc *rtc = dev_get_drvdata(dev); + u32 val, secs; + + /* + * The RTC was not started or is stopped and thus does not carry the + * proper time/date. + */ + val = readl(rtc->base + RZN1_RTC_CTL2); + if (val & RZN1_RTC_CTL2_STOPPED) + return -EINVAL; + + rzn1_rtc_get_time_snapshot(rtc, tm); + secs = readl(rtc->base + RZN1_RTC_SECC); + if (tm->tm_sec != bcd2bin(secs)) + rzn1_rtc_get_time_snapshot(rtc, tm); + + return 0; +} + +static int rzn1_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rzn1_rtc *rtc = dev_get_drvdata(dev); + u32 val; + int ret; + + val = readl(rtc->base + RZN1_RTC_CTL2); + if (!(val & RZN1_RTC_CTL2_STOPPED)) { + /* Hold the counter if it was counting up */ + writel(RZN1_RTC_CTL2_WAIT, rtc->base + RZN1_RTC_CTL2); + + /* Wait 2-4 RTC_PCLK clock cycles for the counter to stop */ + usleep_range(rtc->sync_time, rtc->sync_time * 2); + ret = readl_poll_timeout(rtc->base + RZN1_RTC_CTL2, val, + val & RZN1_RTC_CTL2_WST, 0, 100); + if (ret) + return ret; + } + + val = FIELD_PREP(RZN1_RTC_TIME_SEC, bin2bcd(tm->tm_sec)) | + FIELD_PREP(RZN1_RTC_TIME_MIN, bin2bcd(tm->tm_min)) | + FIELD_PREP(RZN1_RTC_TIME_HOUR, bin2bcd(tm->tm_hour)); + writel(val, rtc->base + RZN1_RTC_TIME); + + val = FIELD_PREP(RZN1_RTC_CAL_WDAY, tm->tm_wday) | + FIELD_PREP(RZN1_RTC_CAL_DAY, bin2bcd(tm->tm_mday)) | + FIELD_PREP(RZN1_RTC_CAL_MON, bin2bcd(tm->tm_mon + 1)) | + FIELD_PREP(RZN1_RTC_CAL_YEAR, bin2bcd(tm->tm_year - 100)); + writel(val, rtc->base + RZN1_RTC_CAL); + + writel(0, rtc->base + RZN1_RTC_CTL2); + + return 0; +} + +static irqreturn_t rzn1_rtc_alarm_irq(int irq, void *dev_id) +{ + struct rzn1_rtc *rtc = dev_id; + u32 ctl1, set_irq_bits = 0; + + if (rtc->tm_alarm.tm_sec == 0) + rtc_update_irq(rtc->rtcdev, 1, RTC_AF | RTC_IRQF); + else + /* Switch to 1s interrupts */ + set_irq_bits = RZN1_RTC_CTL1_1SE; + + guard(spinlock)(&rtc->ctl1_access_lock); + + ctl1 = readl(rtc->base + RZN1_RTC_CTL1); + ctl1 &= ~RZN1_RTC_CTL1_ALME; + ctl1 |= set_irq_bits; + writel(ctl1, rtc->base + RZN1_RTC_CTL1); + + return IRQ_HANDLED; +} + +static irqreturn_t rzn1_rtc_1s_irq(int irq, void *dev_id) +{ + struct rzn1_rtc *rtc = dev_id; + u32 ctl1; + + if (readl(rtc->base + RZN1_RTC_SECC) == bin2bcd(rtc->tm_alarm.tm_sec)) { + guard(spinlock)(&rtc->ctl1_access_lock); + + ctl1 = readl(rtc->base + RZN1_RTC_CTL1); + ctl1 &= ~RZN1_RTC_CTL1_1SE; + writel(ctl1, rtc->base + RZN1_RTC_CTL1); + + rtc_update_irq(rtc->rtcdev, 1, RTC_AF | RTC_IRQF); + } + + return IRQ_HANDLED; +} + +static int rzn1_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) +{ + struct rzn1_rtc *rtc = dev_get_drvdata(dev); + struct rtc_time *tm = &rtc->tm_alarm, tm_now; + u32 ctl1; + int ret; + + guard(spinlock_irqsave)(&rtc->ctl1_access_lock); + + ctl1 = readl(rtc->base + RZN1_RTC_CTL1); + + if (enable) { + /* + * Use alarm interrupt if alarm time is at least a minute away + * or less than a minute but in the next minute. Otherwise use + * 1 second interrupt to wait for the proper second + */ + do { + ctl1 &= ~(RZN1_RTC_CTL1_ALME | RZN1_RTC_CTL1_1SE); + + ret = rzn1_rtc_read_time(dev, &tm_now); + if (ret) + return ret; + + if (rtc_tm_sub(tm, &tm_now) > 59 || tm->tm_min != tm_now.tm_min) + ctl1 |= RZN1_RTC_CTL1_ALME; + else + ctl1 |= RZN1_RTC_CTL1_1SE; + + writel(ctl1, rtc->base + RZN1_RTC_CTL1); + } while (readl(rtc->base + RZN1_RTC_SECC) != bin2bcd(tm_now.tm_sec)); + } else { + ctl1 &= ~(RZN1_RTC_CTL1_ALME | RZN1_RTC_CTL1_1SE); + writel(ctl1, rtc->base + RZN1_RTC_CTL1); + } + + return 0; +} + +static int rzn1_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rzn1_rtc *rtc = dev_get_drvdata(dev); + struct rtc_time *tm = &alrm->time; + unsigned int min, hour, wday, delta_days; + time64_t alarm; + u32 ctl1; + int ret; + + ret = rzn1_rtc_read_time(dev, tm); + if (ret) + return ret; + + ctl1 = readl(rtc->base + RZN1_RTC_CTL1); + alrm->enabled = !!(ctl1 & (RZN1_RTC_CTL1_ALME | RZN1_RTC_CTL1_1SE)); + + min = readl(rtc->base + RZN1_RTC_ALM); + hour = readl(rtc->base + RZN1_RTC_ALH); + + tm->tm_sec = 0; + tm->tm_min = bcd2bin(min); + tm->tm_hour = bcd2bin(hour); + + /* + * If wday is zero, no bit is set in RZN1_RTC_ALW. This is the + * register's power-on reset value. + */ + wday = readl(rtc->base + RZN1_RTC_ALW); + if (!wday) + return 0; + + delta_days = ((fls(wday) - 1) - tm->tm_wday + 7) % 7; + tm->tm_wday = fls(wday) - 1; + + if (delta_days) { + alarm = rtc_tm_to_time64(tm) + (delta_days * 86400); + rtc_time64_to_tm(alarm, tm); + } + + return 0; +} + +static int rzn1_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rzn1_rtc *rtc = dev_get_drvdata(dev); + struct rtc_time *tm = &alrm->time, tm_now; + time64_t alarm, farest; + int ret; + + ret = rzn1_rtc_read_time(dev, &tm_now); + if (ret) + return ret; + + /* We cannot set alarms more than one week ahead */ + farest = rtc_tm_to_time64(&tm_now) + rtc->rtcdev->alarm_offset_max; + alarm = rtc_tm_to_time64(tm); + if (alarm > farest) + return -ERANGE; + + /* Disable alarm interrupts before reprogramming the alarm. */ + ret = rzn1_rtc_alarm_irq_enable(dev, 0); + if (ret) + return ret; + + writel(bin2bcd(tm->tm_min), rtc->base + RZN1_RTC_ALM); + writel(bin2bcd(tm->tm_hour), rtc->base + RZN1_RTC_ALH); + writel(BIT(tm->tm_wday), rtc->base + RZN1_RTC_ALW); + + rtc->tm_alarm = alrm->time; + + rzn1_rtc_alarm_irq_enable(dev, alrm->enabled); + + return 0; +} + +static int rzn1_rtc_read_offset(struct device *dev, long *offset) +{ + struct rzn1_rtc *rtc = dev_get_drvdata(dev); + unsigned int ppb_per_step; + bool subtract; + u32 val; + + val = readl(rtc->base + RZN1_RTC_SUBU); + ppb_per_step = val & RZN1_RTC_SUBU_DEV ? 1017 : 3051; + subtract = val & RZN1_RTC_SUBU_DECR; + val = FIELD_GET(RZN1_RTC_SUBU_RTCA0FX, val); + + if (!val) + *offset = 0; + else if (subtract) + *offset = -(((~val) & RZN1_RTC_SUBU_RTCA0FX) + 1) * ppb_per_step; + else + *offset = (val - 1) * ppb_per_step; + + return 0; +} + +static int rzn1_rtc_set_offset(struct device *dev, long offset) +{ + struct rzn1_rtc *rtc = dev_get_drvdata(dev); + int stepsh, stepsl, steps; + u32 subu = 0, ctl2; + int ret; + + /* + * Check which resolution mode (every 20 or 60s) can be used. + * Between 2 and 124 clock pulses can be added or substracted. + * + * In 20s mode, the minimum resolution is 2 / (32768 * 20) which is + * close to 3051 ppb. In 60s mode, the resolution is closer to 1017. + */ + stepsh = DIV_ROUND_CLOSEST(offset, 1017); + stepsl = DIV_ROUND_CLOSEST(offset, 3051); + + if (stepsh >= -0x3E && stepsh <= 0x3E) { + /* 1017 ppb per step */ + steps = stepsh; + subu |= RZN1_RTC_SUBU_DEV; + } else if (stepsl >= -0x3E && stepsl <= 0x3E) { + /* 3051 ppb per step */ + steps = stepsl; + } else { + return -ERANGE; + } + + if (!steps) + return 0; + + if (steps > 0) { + subu |= steps + 1; + } else { + subu |= RZN1_RTC_SUBU_DECR; + subu |= (~(-steps - 1)) & RZN1_RTC_SUBU_RTCA0FX; + } + + ret = readl_poll_timeout(rtc->base + RZN1_RTC_CTL2, ctl2, + !(ctl2 & RZN1_RTC_CTL2_WUST), 100, 2000000); + if (ret) + return ret; + + writel(subu, rtc->base + RZN1_RTC_SUBU); + + return 0; +} + +static const struct rtc_class_ops rzn1_rtc_ops_subu = { + .read_time = rzn1_rtc_read_time, + .set_time = rzn1_rtc_set_time, + .read_alarm = rzn1_rtc_read_alarm, + .set_alarm = rzn1_rtc_set_alarm, + .alarm_irq_enable = rzn1_rtc_alarm_irq_enable, + .read_offset = rzn1_rtc_read_offset, + .set_offset = rzn1_rtc_set_offset, +}; + +static const struct rtc_class_ops rzn1_rtc_ops_scmp = { + .read_time = rzn1_rtc_read_time, + .set_time = rzn1_rtc_set_time, + .read_alarm = rzn1_rtc_read_alarm, + .set_alarm = rzn1_rtc_set_alarm, + .alarm_irq_enable = rzn1_rtc_alarm_irq_enable, +}; + +static void rzn1_rtc_disable_hardware(void *data) +{ + struct device *dev = data; + struct rzn1_rtc *rtc = dev_get_drvdata(dev); + + /* Disable all interrupts */ + writel(0, rtc->base + RZN1_RTC_CTL1); + + pm_runtime_put_sync(dev); +} + +static int rzn1_rtc_probe(struct platform_device *pdev) +{ + const struct rzn1_rtc_data *data; + struct device *dev = &pdev->dev; + unsigned long rate = 32768; + struct rzn1_rtc *rtc; + u32 val, scmp_val = 0; + struct clk *xtal; + int irq, ret; + + data = of_device_get_match_data(dev); + if (!data) + return -ENODEV; + + rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + platform_set_drvdata(pdev, rtc); + + rtc->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rtc->base)) + return dev_err_probe(dev, PTR_ERR(rtc->base), "Missing reg\n"); + + irq = platform_get_irq_byname(pdev, "alarm"); + if (irq < 0) + return irq; + + rtc->rtcdev = devm_rtc_allocate_device(dev); + if (IS_ERR(rtc->rtcdev)) + return PTR_ERR(rtc->rtcdev); + + rtc->rtcdev->range_min = RTC_TIMESTAMP_BEGIN_2000; + rtc->rtcdev->range_max = RTC_TIMESTAMP_END_2099; + rtc->rtcdev->alarm_offset_max = 7 * 86400; + + ret = devm_pm_runtime_enable(dev); + if (ret < 0) + return ret; + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + ret = devm_add_action_or_reset(dev, rzn1_rtc_disable_hardware, dev); + if (ret) + return ret; + + /* Only switch to scmp if we have an xtal clock with a valid rate and != 32768 */ + xtal = devm_clk_get_optional(dev, "xtal"); + if (IS_ERR(xtal)) { + return PTR_ERR(xtal); + } else if (xtal) { + rate = clk_get_rate(xtal); + + if (rate < 32000 || rate > BIT(22)) + return -EOPNOTSUPP; + + if (rate != 32768 || !data->has_subu) + scmp_val = RZN1_RTC_CTL0_SLSB_SCMP; + } else if (!data->has_subu) { + /* xtal is NULL here */ + return dev_err_probe(dev, -EOPNOTSUPP, + "No valid XTAL provided and SUBU mode not supported\n"); + } + + /* Calculate the duration of two RTC_PCLK clock cycles */ + rtc->sync_time = DIV_ROUND_UP(2 * USEC_PER_SEC, rate); + + /* Disable controller during SUBU/SCMP setup */ + val = readl(rtc->base + RZN1_RTC_CTL0) & ~RZN1_RTC_CTL0_CE; + writel(val, rtc->base + RZN1_RTC_CTL0); + /* Wait 2-4 RTC_PCLK clock cycles for the disabled controller to stop */ + ret = readl_poll_timeout(rtc->base + RZN1_RTC_CTL0, val, + !(val & RZN1_RTC_CTL0_CEST), rtc->sync_time, + rtc->sync_time * 2); + if (ret) + return ret; + + /* Set desired modes leaving the controller disabled */ + writel(RZN1_RTC_CTL0_AMPM | scmp_val, rtc->base + RZN1_RTC_CTL0); + + if (scmp_val) { + writel(rate - 1, rtc->base + RZN1_RTC_SCMP); + rtc->rtcdev->ops = &rzn1_rtc_ops_scmp; + } else { + rtc->rtcdev->ops = &rzn1_rtc_ops_subu; + } + + /* Enable controller finally */ + writel(RZN1_RTC_CTL0_CE | RZN1_RTC_CTL0_AMPM | scmp_val, rtc->base + RZN1_RTC_CTL0); + + /* Disable all interrupts */ + writel(0, rtc->base + RZN1_RTC_CTL1); + + spin_lock_init(&rtc->ctl1_access_lock); + + ret = devm_request_irq(dev, irq, rzn1_rtc_alarm_irq, 0, "RZN1 RTC Alarm", rtc); + if (ret) + return dev_err_probe(dev, ret, "RTC alarm interrupt not available\n"); + + irq = platform_get_irq_byname_optional(pdev, "pps"); + if (irq == -EPROBE_DEFER) + return irq; + if (irq >= 0) + ret = devm_request_irq(dev, irq, rzn1_rtc_1s_irq, 0, "RZN1 RTC 1s", rtc); + + if (irq < 0 || ret) { + set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->rtcdev->features); + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->rtcdev->features); + dev_warn(dev, "RTC pps interrupt not available. Alarm has only minute accuracy\n"); + } + + return devm_rtc_register_device(rtc->rtcdev); +} + +static const struct rzn1_rtc_data rzn1_rtc_rzt2h_data = { + .has_subu = false, +}; + +static const struct rzn1_rtc_data rzn1_rtc_rzn1_data = { + .has_subu = true, +}; + +static const struct of_device_id rzn1_rtc_of_match[] = { + { .compatible = "renesas,r9a09g077-rtc", .data = &rzn1_rtc_rzt2h_data }, + { .compatible = "renesas,rzn1-rtc", .data = &rzn1_rtc_rzn1_data }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rzn1_rtc_of_match); + +static struct platform_driver rzn1_rtc_driver = { + .probe = rzn1_rtc_probe, + .driver = { + .name = "rzn1-rtc", + .of_match_table = rzn1_rtc_of_match, + }, +}; +module_platform_driver(rzn1_rtc_driver); + +MODULE_AUTHOR("Michel Pollet "); +MODULE_AUTHOR("Miquel Raynal "); +MODULE_DESCRIPTION("RZ/N1 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-s32g.c b/drivers/rtc/rtc-s32g.c new file mode 100644 index 000000000..86716192d --- /dev/null +++ b/drivers/rtc/rtc-s32g.c @@ -0,0 +1,386 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2025 NXP + */ + +#include +#include +#include +#include +#include +#include + +#define RTCC_OFFSET 0x4ul +#define RTCS_OFFSET 0x8ul +#define APIVAL_OFFSET 0x10ul + +/* RTCC fields */ +#define RTCC_CNTEN BIT(31) +#define RTCC_APIEN BIT(15) +#define RTCC_APIIE BIT(14) +#define RTCC_CLKSEL_MASK GENMASK(13, 12) +#define RTCC_DIV512EN BIT(11) +#define RTCC_DIV32EN BIT(10) + +/* RTCS fields */ +#define RTCS_INV_API BIT(17) +#define RTCS_APIF BIT(13) + +#define APIVAL_MAX_VAL GENMASK(31, 0) +#define RTC_SYNCH_TIMEOUT (100 * USEC_PER_MSEC) + +/* + * S32G2 and S32G3 SoCs have RTC clock source1 reserved and + * should not be used. + */ +#define RTC_CLK_SRC1_RESERVED BIT(1) + +/* + * S32G RTC module has a 512 value and a 32 value hardware frequency + * divisors (DIV512 and DIV32) which could be used to achieve higher + * counter ranges by lowering the RTC frequency. + */ +enum { + DIV1 = 1, + DIV32 = 32, + DIV512 = 512, + DIV512_32 = 16384 +}; + +static const char *const rtc_clk_src[] = { + "source0", + "source1", + "source2", + "source3" +}; + +struct rtc_priv { + struct rtc_device *rdev; + void __iomem *rtc_base; + struct clk *ipg; + struct clk *clk_src; + const struct rtc_soc_data *rtc_data; + u64 rtc_hz; + time64_t sleep_sec; + int irq; + u32 clk_src_idx; +}; + +struct rtc_soc_data { + u32 clk_div; + u32 reserved_clk_mask; +}; + +static const struct rtc_soc_data rtc_s32g2_data = { + .clk_div = DIV512_32, + .reserved_clk_mask = RTC_CLK_SRC1_RESERVED, +}; + +static irqreturn_t s32g_rtc_handler(int irq, void *dev) +{ + struct rtc_priv *priv = platform_get_drvdata(dev); + u32 status; + + status = readl(priv->rtc_base + RTCS_OFFSET); + + if (status & RTCS_APIF) { + writel(0x0, priv->rtc_base + APIVAL_OFFSET); + writel(status | RTCS_APIF, priv->rtc_base + RTCS_OFFSET); + } + + rtc_update_irq(priv->rdev, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +/* + * The function is not really getting time from the RTC since the S32G RTC + * has several limitations. Thus, to setup alarm use system time. + */ +static int s32g_rtc_read_time(struct device *dev, + struct rtc_time *tm) +{ + struct rtc_priv *priv = dev_get_drvdata(dev); + time64_t sec; + + if (check_add_overflow(ktime_get_real_seconds(), + priv->sleep_sec, &sec)) + return -ERANGE; + + rtc_time64_to_tm(sec, tm); + + return 0; +} + +static int s32g_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rtc_priv *priv = dev_get_drvdata(dev); + u32 rtcc, rtcs; + + rtcc = readl(priv->rtc_base + RTCC_OFFSET); + rtcs = readl(priv->rtc_base + RTCS_OFFSET); + + alrm->enabled = rtcc & RTCC_APIIE; + if (alrm->enabled) + alrm->pending = !(rtcs & RTCS_APIF); + + return 0; +} + +static int s32g_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct rtc_priv *priv = dev_get_drvdata(dev); + u32 rtcc; + + /* RTC API functionality is used both for triggering interrupts + * and as a wakeup event. Hence it should always be enabled. + */ + rtcc = readl(priv->rtc_base + RTCC_OFFSET); + rtcc |= RTCC_APIEN | RTCC_APIIE; + writel(rtcc, priv->rtc_base + RTCC_OFFSET); + + return 0; +} + +static int s32g_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rtc_priv *priv = dev_get_drvdata(dev); + unsigned long long cycles; + long long t_offset; + time64_t alrm_time; + u32 rtcs; + int ret; + + alrm_time = rtc_tm_to_time64(&alrm->time); + t_offset = alrm_time - ktime_get_real_seconds() - priv->sleep_sec; + if (t_offset < 0) + return -ERANGE; + + cycles = t_offset * priv->rtc_hz; + if (cycles > APIVAL_MAX_VAL) + return -ERANGE; + + /* APIVAL could have been reset from the IRQ handler. + * Hence, we wait in case there is a synchronization process. + */ + ret = read_poll_timeout(readl, rtcs, !(rtcs & RTCS_INV_API), + 0, RTC_SYNCH_TIMEOUT, false, priv->rtc_base + RTCS_OFFSET); + if (ret) + return ret; + + writel(cycles, priv->rtc_base + APIVAL_OFFSET); + + return read_poll_timeout(readl, rtcs, !(rtcs & RTCS_INV_API), + 0, RTC_SYNCH_TIMEOUT, false, priv->rtc_base + RTCS_OFFSET); +} + +/* + * Disable the 32-bit free running counter. + * This allows Clock Source and Divisors selection + * to be performed without causing synchronization issues. + */ +static void s32g_rtc_disable(struct rtc_priv *priv) +{ + u32 rtcc = readl(priv->rtc_base + RTCC_OFFSET); + + rtcc &= ~RTCC_CNTEN; + writel(rtcc, priv->rtc_base + RTCC_OFFSET); +} + +static void s32g_rtc_enable(struct rtc_priv *priv) +{ + u32 rtcc = readl(priv->rtc_base + RTCC_OFFSET); + + rtcc |= RTCC_CNTEN; + writel(rtcc, priv->rtc_base + RTCC_OFFSET); +} + +static int rtc_clk_src_setup(struct rtc_priv *priv) +{ + u32 rtcc; + + rtcc = FIELD_PREP(RTCC_CLKSEL_MASK, priv->clk_src_idx); + + switch (priv->rtc_data->clk_div) { + case DIV512_32: + rtcc |= RTCC_DIV512EN; + rtcc |= RTCC_DIV32EN; + break; + case DIV512: + rtcc |= RTCC_DIV512EN; + break; + case DIV32: + rtcc |= RTCC_DIV32EN; + break; + case DIV1: + break; + default: + return -EINVAL; + } + + rtcc |= RTCC_APIEN | RTCC_APIIE; + /* + * Make sure the CNTEN is 0 before we configure + * the clock source and dividers. + */ + s32g_rtc_disable(priv); + writel(rtcc, priv->rtc_base + RTCC_OFFSET); + s32g_rtc_enable(priv); + + return 0; +} + +static const struct rtc_class_ops rtc_ops = { + .read_time = s32g_rtc_read_time, + .read_alarm = s32g_rtc_read_alarm, + .set_alarm = s32g_rtc_set_alarm, + .alarm_irq_enable = s32g_rtc_alarm_irq_enable, +}; + +static int rtc_clk_dts_setup(struct rtc_priv *priv, + struct device *dev) +{ + u32 i; + + priv->ipg = devm_clk_get_enabled(dev, "ipg"); + if (IS_ERR(priv->ipg)) + return dev_err_probe(dev, PTR_ERR(priv->ipg), + "Failed to get 'ipg' clock\n"); + + for (i = 0; i < ARRAY_SIZE(rtc_clk_src); i++) { + if (priv->rtc_data->reserved_clk_mask & BIT(i)) + return -EOPNOTSUPP; + + priv->clk_src = devm_clk_get_enabled(dev, rtc_clk_src[i]); + if (!IS_ERR(priv->clk_src)) { + priv->clk_src_idx = i; + break; + } + } + + if (IS_ERR(priv->clk_src)) + return dev_err_probe(dev, PTR_ERR(priv->clk_src), + "Failed to get rtc module clock source\n"); + + return 0; +} + +static int s32g_rtc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rtc_priv *priv; + unsigned long rtc_hz; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->rtc_data = of_device_get_match_data(dev); + if (!priv->rtc_data) + return -ENODEV; + + priv->rtc_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->rtc_base)) + return PTR_ERR(priv->rtc_base); + + device_init_wakeup(dev, true); + + ret = rtc_clk_dts_setup(priv, dev); + if (ret) + return ret; + + priv->rdev = devm_rtc_allocate_device(dev); + if (IS_ERR(priv->rdev)) + return PTR_ERR(priv->rdev); + + ret = rtc_clk_src_setup(priv); + if (ret) + return ret; + + priv->irq = platform_get_irq(pdev, 0); + if (priv->irq < 0) { + ret = priv->irq; + goto disable_rtc; + } + + rtc_hz = clk_get_rate(priv->clk_src); + if (!rtc_hz) { + dev_err(dev, "Failed to get RTC frequency\n"); + ret = -EINVAL; + goto disable_rtc; + } + + priv->rtc_hz = DIV_ROUND_UP(rtc_hz, priv->rtc_data->clk_div); + + platform_set_drvdata(pdev, priv); + priv->rdev->ops = &rtc_ops; + + ret = devm_request_irq(dev, priv->irq, + s32g_rtc_handler, 0, dev_name(dev), pdev); + if (ret) { + dev_err(dev, "Request interrupt %d failed, error: %d\n", + priv->irq, ret); + goto disable_rtc; + } + + ret = devm_rtc_register_device(priv->rdev); + if (ret) + goto disable_rtc; + + return 0; + +disable_rtc: + s32g_rtc_disable(priv); + return ret; +} + +static int s32g_rtc_suspend(struct device *dev) +{ + struct rtc_priv *priv = dev_get_drvdata(dev); + u32 apival = readl(priv->rtc_base + APIVAL_OFFSET); + + if (check_add_overflow(priv->sleep_sec, div64_u64(apival, priv->rtc_hz), + &priv->sleep_sec)) { + dev_warn(dev, "Overflow on sleep cycles occurred. Resetting to 0.\n"); + priv->sleep_sec = 0; + } + + return 0; +} + +static int s32g_rtc_resume(struct device *dev) +{ + struct rtc_priv *priv = dev_get_drvdata(dev); + + /* The transition from resume to run is a reset event. + * This leads to the RTC registers being reset after resume from + * suspend. It is uncommon, but this behaviour has been observed + * on S32G RTC after issuing a Suspend to RAM operation. + * Thus, reconfigure RTC registers on the resume path. + */ + return rtc_clk_src_setup(priv); +} + +static const struct of_device_id rtc_dt_ids[] = { + { .compatible = "nxp,s32g2-rtc", .data = &rtc_s32g2_data }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, rtc_dt_ids); + +static DEFINE_SIMPLE_DEV_PM_OPS(s32g_rtc_pm_ops, + s32g_rtc_suspend, s32g_rtc_resume); + +static struct platform_driver s32g_rtc_driver = { + .driver = { + .name = "s32g-rtc", + .pm = pm_sleep_ptr(&s32g_rtc_pm_ops), + .of_match_table = rtc_dt_ids, + }, + .probe = s32g_rtc_probe, +}; +module_platform_driver(s32g_rtc_driver); + +MODULE_AUTHOR("NXP"); +MODULE_DESCRIPTION("NXP RTC driver for S32G2/S32G3"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-s35390a.c b/drivers/rtc/rtc-s35390a.c new file mode 100644 index 000000000..b72eef4fb --- /dev/null +++ b/drivers/rtc/rtc-s35390a.c @@ -0,0 +1,541 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Seiko Instruments S-35390A RTC Driver + * + * Copyright (c) 2007 Byron Bradley + */ + +#include +#include +#include +#include +#include +#include +#include + +#define S35390A_CMD_STATUS1 0 +#define S35390A_CMD_STATUS2 1 +#define S35390A_CMD_TIME1 2 +#define S35390A_CMD_TIME2 3 +#define S35390A_CMD_INT2_REG1 5 +#define S35390A_CMD_FREE_REG 7 + +#define S35390A_BYTE_YEAR 0 +#define S35390A_BYTE_MONTH 1 +#define S35390A_BYTE_DAY 2 +#define S35390A_BYTE_WDAY 3 +#define S35390A_BYTE_HOURS 4 +#define S35390A_BYTE_MINS 5 +#define S35390A_BYTE_SECS 6 + +#define S35390A_ALRM_BYTE_WDAY 0 +#define S35390A_ALRM_BYTE_HOURS 1 +#define S35390A_ALRM_BYTE_MINS 2 + +/* flags for STATUS1 */ +#define S35390A_FLAG_POC BIT(0) +#define S35390A_FLAG_BLD BIT(1) +#define S35390A_FLAG_INT2 BIT(2) +#define S35390A_FLAG_24H BIT(6) +#define S35390A_FLAG_RESET BIT(7) + +/* flag for STATUS2 */ +#define S35390A_FLAG_TEST BIT(0) + +/* INT2 pin output mode */ +#define S35390A_INT2_MODE_MASK 0x0E +#define S35390A_INT2_MODE_NOINTR 0x00 +#define S35390A_INT2_MODE_ALARM BIT(1) /* INT2AE */ +#define S35390A_INT2_MODE_PMIN_EDG BIT(2) /* INT2ME */ +#define S35390A_INT2_MODE_FREQ BIT(3) /* INT2FE */ +#define S35390A_INT2_MODE_PMIN (BIT(3) | BIT(2)) /* INT2FE | INT2ME */ + +static const struct i2c_device_id s35390a_id[] = { + { .name = "s35390a" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, s35390a_id); + +static const __maybe_unused struct of_device_id s35390a_of_match[] = { + { .compatible = "sii,s35390a" }, + { } +}; +MODULE_DEVICE_TABLE(of, s35390a_of_match); + +struct s35390a { + struct i2c_client *client[8]; + int twentyfourhour; +}; + +static int s35390a_set_reg(struct s35390a *s35390a, int reg, u8 *buf, int len) +{ + struct i2c_client *client = s35390a->client[reg]; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .len = len, + .buf = buf + }, + }; + + if ((i2c_transfer(client->adapter, msg, 1)) != 1) + return -EIO; + + return 0; +} + +static int s35390a_get_reg(struct s35390a *s35390a, int reg, u8 *buf, int len) +{ + struct i2c_client *client = s35390a->client[reg]; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = buf + }, + }; + + if ((i2c_transfer(client->adapter, msg, 1)) != 1) + return -EIO; + + return 0; +} + +static int s35390a_init(struct s35390a *s35390a) +{ + u8 buf; + int ret; + unsigned initcount = 0; + + /* + * At least one of POC and BLD are set, so reinitialise chip. Keeping + * this information in the hardware to know later that the time isn't + * valid is unfortunately not possible because POC and BLD are cleared + * on read. So the reset is best done now. + * + * The 24H bit is kept over reset, so set it already here. + */ +initialize: + buf = S35390A_FLAG_RESET | S35390A_FLAG_24H; + ret = s35390a_set_reg(s35390a, S35390A_CMD_STATUS1, &buf, 1); + + if (ret < 0) + return ret; + + ret = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, &buf, 1); + if (ret < 0) + return ret; + + if (buf & (S35390A_FLAG_POC | S35390A_FLAG_BLD)) { + /* Try up to five times to reset the chip */ + if (initcount < 5) { + ++initcount; + goto initialize; + } else + return -EIO; + } + + return 1; +} + +/* + * Returns <0 on error, 0 if rtc is setup fine and 1 if the chip was reset. + * To keep the information if an irq is pending, pass the value read from + * STATUS1 to the caller. + */ +static int s35390a_read_status(struct s35390a *s35390a, char *status1) +{ + int ret; + + ret = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, status1, 1); + if (ret < 0) + return ret; + + if (*status1 & S35390A_FLAG_POC) { + /* + * Do not communicate for 0.5 seconds since the power-on + * detection circuit is in operation. + */ + msleep(500); + return 1; + } else if (*status1 & S35390A_FLAG_BLD) + return 1; + /* + * If both POC and BLD are unset everything is fine. + */ + return 0; +} + +static int s35390a_disable_test_mode(struct s35390a *s35390a) +{ + u8 buf[1]; + + if (s35390a_get_reg(s35390a, S35390A_CMD_STATUS2, buf, sizeof(buf)) < 0) + return -EIO; + + if (!(buf[0] & S35390A_FLAG_TEST)) + return 0; + + buf[0] &= ~S35390A_FLAG_TEST; + return s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, buf, sizeof(buf)); +} + +static char s35390a_hr2reg(struct s35390a *s35390a, int hour) +{ + if (s35390a->twentyfourhour) + return bin2bcd(hour); + + if (hour < 12) + return bin2bcd(hour); + + return 0x40 | bin2bcd(hour - 12); +} + +static int s35390a_reg2hr(struct s35390a *s35390a, char reg) +{ + unsigned hour; + + if (s35390a->twentyfourhour) + return bcd2bin(reg & 0x3f); + + hour = bcd2bin(reg & 0x3f); + if (reg & 0x40) + hour += 12; + + return hour; +} + +static int s35390a_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + struct s35390a *s35390a = i2c_get_clientdata(client); + int i; + u8 buf[7], status; + + dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d mday=%d, " + "mon=%d, year=%d, wday=%d\n", __func__, tm->tm_sec, + tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year, + tm->tm_wday); + + if (s35390a_read_status(s35390a, &status) == 1) + s35390a_init(s35390a); + + buf[S35390A_BYTE_YEAR] = bin2bcd(tm->tm_year - 100); + buf[S35390A_BYTE_MONTH] = bin2bcd(tm->tm_mon + 1); + buf[S35390A_BYTE_DAY] = bin2bcd(tm->tm_mday); + buf[S35390A_BYTE_WDAY] = bin2bcd(tm->tm_wday); + buf[S35390A_BYTE_HOURS] = s35390a_hr2reg(s35390a, tm->tm_hour); + buf[S35390A_BYTE_MINS] = bin2bcd(tm->tm_min); + buf[S35390A_BYTE_SECS] = bin2bcd(tm->tm_sec); + + /* This chip expects the bits of each byte to be in reverse order */ + for (i = 0; i < 7; ++i) + buf[i] = bitrev8(buf[i]); + + return s35390a_set_reg(s35390a, S35390A_CMD_TIME1, buf, sizeof(buf)); +} + +static int s35390a_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + struct s35390a *s35390a = i2c_get_clientdata(client); + u8 buf[7], status; + int i, err; + + if (s35390a_read_status(s35390a, &status) == 1) + return -EINVAL; + + err = s35390a_get_reg(s35390a, S35390A_CMD_TIME1, buf, sizeof(buf)); + if (err < 0) + return err; + + /* This chip returns the bits of each byte in reverse order */ + for (i = 0; i < 7; ++i) + buf[i] = bitrev8(buf[i]); + + tm->tm_sec = bcd2bin(buf[S35390A_BYTE_SECS]); + tm->tm_min = bcd2bin(buf[S35390A_BYTE_MINS]); + tm->tm_hour = s35390a_reg2hr(s35390a, buf[S35390A_BYTE_HOURS]); + tm->tm_wday = bcd2bin(buf[S35390A_BYTE_WDAY]); + tm->tm_mday = bcd2bin(buf[S35390A_BYTE_DAY]); + tm->tm_mon = bcd2bin(buf[S35390A_BYTE_MONTH]) - 1; + tm->tm_year = bcd2bin(buf[S35390A_BYTE_YEAR]) + 100; + + dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, mday=%d, " + "mon=%d, year=%d, wday=%d\n", __func__, tm->tm_sec, + tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year, + tm->tm_wday); + + return 0; +} + +static int s35390a_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct i2c_client *client = to_i2c_client(dev); + struct s35390a *s35390a = i2c_get_clientdata(client); + u8 buf[3], sts = 0; + int err, i; + + dev_dbg(&client->dev, "%s: alm is secs=%d, mins=%d, hours=%d mday=%d, "\ + "mon=%d, year=%d, wday=%d\n", __func__, alm->time.tm_sec, + alm->time.tm_min, alm->time.tm_hour, alm->time.tm_mday, + alm->time.tm_mon, alm->time.tm_year, alm->time.tm_wday); + + /* disable interrupt (which deasserts the irq line) */ + err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts)); + if (err < 0) + return err; + + /* clear pending interrupt (in STATUS1 only), if any */ + err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, &sts, sizeof(sts)); + if (err < 0) + return err; + + if (alm->enabled) + sts = S35390A_INT2_MODE_ALARM; + else + sts = S35390A_INT2_MODE_NOINTR; + + /* set interrupt mode*/ + err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts)); + if (err < 0) + return err; + + if (alm->time.tm_wday != -1) + buf[S35390A_ALRM_BYTE_WDAY] = bin2bcd(alm->time.tm_wday) | 0x80; + else + buf[S35390A_ALRM_BYTE_WDAY] = 0; + + buf[S35390A_ALRM_BYTE_HOURS] = s35390a_hr2reg(s35390a, + alm->time.tm_hour) | 0x80; + buf[S35390A_ALRM_BYTE_MINS] = bin2bcd(alm->time.tm_min) | 0x80; + + if (alm->time.tm_hour >= 12) + buf[S35390A_ALRM_BYTE_HOURS] |= 0x40; + + for (i = 0; i < 3; ++i) + buf[i] = bitrev8(buf[i]); + + err = s35390a_set_reg(s35390a, S35390A_CMD_INT2_REG1, buf, + sizeof(buf)); + + return err; +} + +static int s35390a_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct i2c_client *client = to_i2c_client(dev); + struct s35390a *s35390a = i2c_get_clientdata(client); + u8 buf[3], sts; + int i, err; + + err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts)); + if (err < 0) + return err; + + if ((sts & S35390A_INT2_MODE_MASK) != S35390A_INT2_MODE_ALARM) { + /* + * When the alarm isn't enabled, the register to configure + * the alarm time isn't accessible. + */ + alm->enabled = 0; + return 0; + } else { + alm->enabled = 1; + } + + err = s35390a_get_reg(s35390a, S35390A_CMD_INT2_REG1, buf, sizeof(buf)); + if (err < 0) + return err; + + /* This chip returns the bits of each byte in reverse order */ + for (i = 0; i < 3; ++i) + buf[i] = bitrev8(buf[i]); + + /* + * B0 of the three matching registers is an enable flag. Iff it is set + * the configured value is used for matching. + */ + if (buf[S35390A_ALRM_BYTE_WDAY] & 0x80) + alm->time.tm_wday = + bcd2bin(buf[S35390A_ALRM_BYTE_WDAY] & ~0x80); + + if (buf[S35390A_ALRM_BYTE_HOURS] & 0x80) + alm->time.tm_hour = + s35390a_reg2hr(s35390a, + buf[S35390A_ALRM_BYTE_HOURS] & ~0x80); + + if (buf[S35390A_ALRM_BYTE_MINS] & 0x80) + alm->time.tm_min = bcd2bin(buf[S35390A_ALRM_BYTE_MINS] & ~0x80); + + /* alarm triggers always at s=0 */ + alm->time.tm_sec = 0; + + dev_dbg(&client->dev, "%s: alm is mins=%d, hours=%d, wday=%d\n", + __func__, alm->time.tm_min, alm->time.tm_hour, + alm->time.tm_wday); + + return 0; +} + +static int s35390a_rtc_ioctl(struct device *dev, unsigned int cmd, + unsigned long arg) +{ + struct i2c_client *client = to_i2c_client(dev); + struct s35390a *s35390a = i2c_get_clientdata(client); + u8 sts; + int err; + + switch (cmd) { + case RTC_VL_READ: + /* s35390a_reset set lowvoltage flag and init RTC if needed */ + err = s35390a_read_status(s35390a, &sts); + if (err < 0) + return err; + if (copy_to_user((void __user *)arg, &err, sizeof(int))) + return -EFAULT; + break; + case RTC_VL_CLR: + /* update flag and clear register */ + err = s35390a_init(s35390a); + if (err < 0) + return err; + break; + default: + return -ENOIOCTLCMD; + } + + return 0; +} + +static const struct rtc_class_ops s35390a_rtc_ops = { + .read_time = s35390a_rtc_read_time, + .set_time = s35390a_rtc_set_time, + .set_alarm = s35390a_rtc_set_alarm, + .read_alarm = s35390a_rtc_read_alarm, + .ioctl = s35390a_rtc_ioctl, +}; + +static int s35390a_nvmem_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct s35390a *s35390a = priv; + + /* The offset is ignored because the NVMEM region is only 1 byte */ + return s35390a_get_reg(s35390a, S35390A_CMD_FREE_REG, val, bytes); +} + +static int s35390a_nvmem_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct s35390a *s35390a = priv; + + return s35390a_set_reg(s35390a, S35390A_CMD_FREE_REG, val, bytes); +} + +static int s35390a_probe(struct i2c_client *client) +{ + int err, err_read; + unsigned int i; + struct s35390a *s35390a; + struct rtc_device *rtc; + u8 buf, status1; + struct device *dev = &client->dev; + struct nvmem_config nvmem_cfg = { + .name = "s35390a_nvram", + .type = NVMEM_TYPE_BATTERY_BACKED, + .word_size = 1, + .stride = 1, + .size = 1, + .reg_read = s35390a_nvmem_read, + .reg_write = s35390a_nvmem_write, + }; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + s35390a = devm_kzalloc(dev, sizeof(struct s35390a), GFP_KERNEL); + if (!s35390a) + return -ENOMEM; + + s35390a->client[0] = client; + i2c_set_clientdata(client, s35390a); + + /* This chip uses multiple addresses, use dummy devices for them */ + for (i = 1; i < 8; ++i) { + s35390a->client[i] = devm_i2c_new_dummy_device(dev, + client->adapter, + client->addr + i); + if (IS_ERR(s35390a->client[i])) { + dev_err(dev, "Address %02x unavailable\n", + client->addr + i); + return PTR_ERR(s35390a->client[i]); + } + } + + rtc = devm_rtc_allocate_device(dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + err_read = s35390a_read_status(s35390a, &status1); + if (err_read < 0) { + dev_err(dev, "error resetting chip\n"); + return err_read; + } + + if (status1 & S35390A_FLAG_24H) + s35390a->twentyfourhour = 1; + else + s35390a->twentyfourhour = 0; + + if (status1 & S35390A_FLAG_INT2) { + /* disable alarm (and maybe test mode) */ + buf = 0; + err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &buf, 1); + if (err < 0) { + dev_err(dev, "error disabling alarm"); + return err; + } + } else { + err = s35390a_disable_test_mode(s35390a); + if (err < 0) { + dev_err(dev, "error disabling test mode\n"); + return err; + } + } + + device_set_wakeup_capable(dev, 1); + + rtc->ops = &s35390a_rtc_ops; + rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rtc->range_max = RTC_TIMESTAMP_END_2099; + + set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->features); + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features); + + if (status1 & S35390A_FLAG_INT2) + rtc_update_irq(rtc, 1, RTC_AF); + + nvmem_cfg.priv = s35390a; + err = devm_rtc_nvmem_register(rtc, &nvmem_cfg); + if (err) + return err; + + return devm_rtc_register_device(rtc); +} + +static struct i2c_driver s35390a_driver = { + .driver = { + .name = "rtc-s35390a", + .of_match_table = of_match_ptr(s35390a_of_match), + }, + .probe = s35390a_probe, + .id_table = s35390a_id, +}; + +module_i2c_driver(s35390a_driver); + +MODULE_AUTHOR("Byron Bradley "); +MODULE_DESCRIPTION("S35390A RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c new file mode 100644 index 000000000..291c0ccb0 --- /dev/null +++ b/drivers/rtc/rtc-s3c.c @@ -0,0 +1,566 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* drivers/rtc/rtc-s3c.c + * + * Copyright (c) 2010 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Copyright (c) 2004,2006 Simtec Electronics + * Ben Dooks, + * http://armlinux.simtec.co.uk/ + * + * S3C2410/S3C2440/S3C24XX Internal RTC Driver +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "rtc-s3c.h" + +struct s3c_rtc { + struct device *dev; + struct rtc_device *rtc; + + void __iomem *base; + struct clk *rtc_clk; + struct clk *rtc_src_clk; + bool alarm_enabled; + + const struct s3c_rtc_data *data; + + int irq_alarm; + spinlock_t alarm_lock; + + bool wake_en; +}; + +struct s3c_rtc_data { + bool needs_src_clk; + + void (*irq_handler) (struct s3c_rtc *info, int mask); + void (*enable) (struct s3c_rtc *info); + void (*disable) (struct s3c_rtc *info); +}; + +static int s3c_rtc_enable_clk(struct s3c_rtc *info) +{ + int ret; + + ret = clk_enable(info->rtc_clk); + if (ret) + return ret; + + if (info->data->needs_src_clk) { + ret = clk_enable(info->rtc_src_clk); + if (ret) { + clk_disable(info->rtc_clk); + return ret; + } + } + return 0; +} + +static void s3c_rtc_disable_clk(struct s3c_rtc *info) +{ + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); + clk_disable(info->rtc_clk); +} + +/* IRQ Handler */ +static irqreturn_t s3c_rtc_alarmirq(int irq, void *id) +{ + struct s3c_rtc *info = (struct s3c_rtc *)id; + + if (info->data->irq_handler) + info->data->irq_handler(info, S3C2410_INTP_ALM); + + return IRQ_HANDLED; +} + +/* Update control registers */ +static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) +{ + struct s3c_rtc *info = dev_get_drvdata(dev); + unsigned long flags; + unsigned int tmp; + int ret; + + dev_dbg(info->dev, "%s: aie=%d\n", __func__, enabled); + + ret = s3c_rtc_enable_clk(info); + if (ret) + return ret; + + tmp = readb(info->base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN; + + if (enabled) + tmp |= S3C2410_RTCALM_ALMEN; + + writeb(tmp, info->base + S3C2410_RTCALM); + + spin_lock_irqsave(&info->alarm_lock, flags); + + if (info->alarm_enabled && !enabled) + s3c_rtc_disable_clk(info); + else if (!info->alarm_enabled && enabled) + ret = s3c_rtc_enable_clk(info); + + info->alarm_enabled = enabled; + spin_unlock_irqrestore(&info->alarm_lock, flags); + + s3c_rtc_disable_clk(info); + + return ret; +} + +/* Read time from RTC and convert it from BCD */ +static int s3c_rtc_read_time(struct s3c_rtc *info, struct rtc_time *tm) +{ + unsigned int have_retried = 0; + int ret; + + ret = s3c_rtc_enable_clk(info); + if (ret) + return ret; + +retry_get_time: + tm->tm_min = readb(info->base + S3C2410_RTCMIN); + tm->tm_hour = readb(info->base + S3C2410_RTCHOUR); + tm->tm_mday = readb(info->base + S3C2410_RTCDATE); + tm->tm_mon = readb(info->base + S3C2410_RTCMON); + tm->tm_year = readb(info->base + S3C2410_RTCYEAR); + tm->tm_sec = readb(info->base + S3C2410_RTCSEC); + + /* + * The only way to work out whether the system was mid-update + * when we read it is to check the second counter, and if it + * is zero, then we re-try the entire read + */ + if (tm->tm_sec == 0 && !have_retried) { + have_retried = 1; + goto retry_get_time; + } + + s3c_rtc_disable_clk(info); + + tm->tm_sec = bcd2bin(tm->tm_sec); + tm->tm_min = bcd2bin(tm->tm_min); + tm->tm_hour = bcd2bin(tm->tm_hour); + tm->tm_mday = bcd2bin(tm->tm_mday); + tm->tm_mon = bcd2bin(tm->tm_mon); + tm->tm_year = bcd2bin(tm->tm_year); + + return 0; +} + +/* Convert time to BCD and write it to RTC */ +static int s3c_rtc_write_time(struct s3c_rtc *info, const struct rtc_time *tm) +{ + int ret; + + ret = s3c_rtc_enable_clk(info); + if (ret) + return ret; + + writeb(bin2bcd(tm->tm_sec), info->base + S3C2410_RTCSEC); + writeb(bin2bcd(tm->tm_min), info->base + S3C2410_RTCMIN); + writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_RTCHOUR); + writeb(bin2bcd(tm->tm_mday), info->base + S3C2410_RTCDATE); + writeb(bin2bcd(tm->tm_mon), info->base + S3C2410_RTCMON); + writeb(bin2bcd(tm->tm_year), info->base + S3C2410_RTCYEAR); + + s3c_rtc_disable_clk(info); + + return 0; +} + +static int s3c_rtc_gettime(struct device *dev, struct rtc_time *tm) +{ + struct s3c_rtc *info = dev_get_drvdata(dev); + int ret; + + ret = s3c_rtc_read_time(info, tm); + if (ret) + return ret; + + /* Convert internal representation to actual date/time */ + tm->tm_year += 100; + tm->tm_mon -= 1; + + dev_dbg(dev, "read time %ptR\n", tm); + return 0; +} + +static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) +{ + struct s3c_rtc *info = dev_get_drvdata(dev); + struct rtc_time rtc_tm = *tm; + + dev_dbg(dev, "set time %ptR\n", tm); + + /* + * Convert actual date/time to internal representation. + * We get around Y2K by simply not supporting it. + */ + rtc_tm.tm_year -= 100; + rtc_tm.tm_mon += 1; + + return s3c_rtc_write_time(info, &rtc_tm); +} + +static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct s3c_rtc *info = dev_get_drvdata(dev); + struct rtc_time *alm_tm = &alrm->time; + unsigned int alm_en; + int ret; + + ret = s3c_rtc_enable_clk(info); + if (ret) + return ret; + + alm_tm->tm_sec = readb(info->base + S3C2410_ALMSEC); + alm_tm->tm_min = readb(info->base + S3C2410_ALMMIN); + alm_tm->tm_hour = readb(info->base + S3C2410_ALMHOUR); + alm_tm->tm_mon = readb(info->base + S3C2410_ALMMON); + alm_tm->tm_mday = readb(info->base + S3C2410_ALMDATE); + alm_tm->tm_year = readb(info->base + S3C2410_ALMYEAR); + + alm_en = readb(info->base + S3C2410_RTCALM); + + s3c_rtc_disable_clk(info); + + alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0; + + dev_dbg(dev, "read alarm %d, %ptR\n", alm_en, alm_tm); + + /* decode the alarm enable field */ + if (alm_en & S3C2410_RTCALM_SECEN) + alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec); + + if (alm_en & S3C2410_RTCALM_MINEN) + alm_tm->tm_min = bcd2bin(alm_tm->tm_min); + + if (alm_en & S3C2410_RTCALM_HOUREN) + alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour); + + if (alm_en & S3C2410_RTCALM_DAYEN) + alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday); + + if (alm_en & S3C2410_RTCALM_MONEN) { + alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon); + alm_tm->tm_mon -= 1; + } + + if (alm_en & S3C2410_RTCALM_YEAREN) + alm_tm->tm_year = bcd2bin(alm_tm->tm_year); + + return 0; +} + +static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct s3c_rtc *info = dev_get_drvdata(dev); + struct rtc_time *tm = &alrm->time; + unsigned int alrm_en; + int ret; + + dev_dbg(dev, "s3c_rtc_setalarm: %d, %ptR\n", alrm->enabled, tm); + + ret = s3c_rtc_enable_clk(info); + if (ret) + return ret; + + alrm_en = readb(info->base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN; + writeb(0x00, info->base + S3C2410_RTCALM); + + if (tm->tm_sec < 60 && tm->tm_sec >= 0) { + alrm_en |= S3C2410_RTCALM_SECEN; + writeb(bin2bcd(tm->tm_sec), info->base + S3C2410_ALMSEC); + } + + if (tm->tm_min < 60 && tm->tm_min >= 0) { + alrm_en |= S3C2410_RTCALM_MINEN; + writeb(bin2bcd(tm->tm_min), info->base + S3C2410_ALMMIN); + } + + if (tm->tm_hour < 24 && tm->tm_hour >= 0) { + alrm_en |= S3C2410_RTCALM_HOUREN; + writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_ALMHOUR); + } + + if (tm->tm_mon < 12 && tm->tm_mon >= 0) { + alrm_en |= S3C2410_RTCALM_MONEN; + writeb(bin2bcd(tm->tm_mon + 1), info->base + S3C2410_ALMMON); + } + + if (tm->tm_mday <= 31 && tm->tm_mday >= 1) { + alrm_en |= S3C2410_RTCALM_DAYEN; + writeb(bin2bcd(tm->tm_mday), info->base + S3C2410_ALMDATE); + } + + dev_dbg(dev, "setting S3C2410_RTCALM to %08x\n", alrm_en); + + writeb(alrm_en, info->base + S3C2410_RTCALM); + + s3c_rtc_setaie(dev, alrm->enabled); + + s3c_rtc_disable_clk(info); + + return 0; +} + +static const struct rtc_class_ops s3c_rtcops = { + .read_time = s3c_rtc_gettime, + .set_time = s3c_rtc_settime, + .read_alarm = s3c_rtc_getalarm, + .set_alarm = s3c_rtc_setalarm, + .alarm_irq_enable = s3c_rtc_setaie, +}; + +static void s3c6410_rtc_enable(struct s3c_rtc *info) +{ + unsigned int con, tmp; + + con = readw(info->base + S3C2410_RTCCON); + /* re-enable the device, and check it is ok */ + if ((con & S3C2410_RTCCON_RTCEN) == 0) { + dev_info(info->dev, "rtc disabled, re-enabling\n"); + + tmp = readw(info->base + S3C2410_RTCCON); + writew(tmp | S3C2410_RTCCON_RTCEN, info->base + S3C2410_RTCCON); + } + + if (con & S3C2410_RTCCON_CNTSEL) { + dev_info(info->dev, "removing RTCCON_CNTSEL\n"); + + tmp = readw(info->base + S3C2410_RTCCON); + writew(tmp & ~S3C2410_RTCCON_CNTSEL, + info->base + S3C2410_RTCCON); + } + + if (con & S3C2410_RTCCON_CLKRST) { + dev_info(info->dev, "removing RTCCON_CLKRST\n"); + + tmp = readw(info->base + S3C2410_RTCCON); + writew(tmp & ~S3C2410_RTCCON_CLKRST, + info->base + S3C2410_RTCCON); + } +} + +static void s3c6410_rtc_disable(struct s3c_rtc *info) +{ + unsigned int con; + + con = readw(info->base + S3C2410_RTCCON); + con &= ~S3C64XX_RTCCON_TICEN; + con &= ~S3C2410_RTCCON_RTCEN; + writew(con, info->base + S3C2410_RTCCON); +} + +static void s3c_rtc_remove(struct platform_device *pdev) +{ + struct s3c_rtc *info = platform_get_drvdata(pdev); + + s3c_rtc_setaie(info->dev, 0); + + if (info->data->needs_src_clk) + clk_unprepare(info->rtc_src_clk); + clk_unprepare(info->rtc_clk); +} + +static int s3c_rtc_probe(struct platform_device *pdev) +{ + struct s3c_rtc *info = NULL; + int ret; + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->dev = &pdev->dev; + info->data = of_device_get_match_data(&pdev->dev); + if (!info->data) { + dev_err(&pdev->dev, "failed getting s3c_rtc_data\n"); + return -EINVAL; + } + spin_lock_init(&info->alarm_lock); + + platform_set_drvdata(pdev, info); + + info->irq_alarm = platform_get_irq(pdev, 0); + if (info->irq_alarm < 0) + return info->irq_alarm; + + dev_dbg(&pdev->dev, "s3c2410_rtc: alarm irq %d\n", info->irq_alarm); + + /* get the memory region */ + info->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(info->base)) + return PTR_ERR(info->base); + + info->rtc_clk = devm_clk_get(&pdev->dev, "rtc"); + if (IS_ERR(info->rtc_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(info->rtc_clk), + "failed to find rtc clock\n"); + ret = clk_prepare_enable(info->rtc_clk); + if (ret) + return ret; + + if (info->data->needs_src_clk) { + info->rtc_src_clk = devm_clk_get(&pdev->dev, "rtc_src"); + if (IS_ERR(info->rtc_src_clk)) { + ret = dev_err_probe(&pdev->dev, PTR_ERR(info->rtc_src_clk), + "failed to find rtc source clock\n"); + goto err_src_clk; + } + ret = clk_prepare_enable(info->rtc_src_clk); + if (ret) + goto err_src_clk; + } + + /* disable RTC enable bits potentially set by the bootloader */ + if (info->data->disable) + info->data->disable(info); + + /* check to see if everything is setup correctly */ + if (info->data->enable) + info->data->enable(info); + + dev_dbg(&pdev->dev, "s3c2410_rtc: RTCCON=%02x\n", + readw(info->base + S3C2410_RTCCON)); + + device_init_wakeup(&pdev->dev, true); + + info->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(info->rtc)) { + ret = PTR_ERR(info->rtc); + goto err_nortc; + } + + info->rtc->ops = &s3c_rtcops; + info->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + info->rtc->range_max = RTC_TIMESTAMP_END_2099; + + ret = devm_rtc_register_device(info->rtc); + if (ret) + goto err_nortc; + + ret = devm_request_irq(&pdev->dev, info->irq_alarm, s3c_rtc_alarmirq, + 0, "s3c2410-rtc alarm", info); + if (ret) { + dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_alarm, ret); + goto err_nortc; + } + + s3c_rtc_disable_clk(info); + + return 0; + +err_nortc: + if (info->data->disable) + info->data->disable(info); + + if (info->data->needs_src_clk) + clk_disable_unprepare(info->rtc_src_clk); +err_src_clk: + clk_disable_unprepare(info->rtc_clk); + + return ret; +} + +#ifdef CONFIG_PM_SLEEP + +static int s3c_rtc_suspend(struct device *dev) +{ + struct s3c_rtc *info = dev_get_drvdata(dev); + int ret; + + ret = s3c_rtc_enable_clk(info); + if (ret) + return ret; + + if (info->data->disable) + info->data->disable(info); + + if (device_may_wakeup(dev) && !info->wake_en) { + if (enable_irq_wake(info->irq_alarm) == 0) + info->wake_en = true; + else + dev_err(dev, "enable_irq_wake failed\n"); + } + + return 0; +} + +static int s3c_rtc_resume(struct device *dev) +{ + struct s3c_rtc *info = dev_get_drvdata(dev); + + if (info->data->enable) + info->data->enable(info); + + s3c_rtc_disable_clk(info); + + if (device_may_wakeup(dev) && info->wake_en) { + disable_irq_wake(info->irq_alarm); + info->wake_en = false; + } + + return 0; +} +#endif +static SIMPLE_DEV_PM_OPS(s3c_rtc_pm_ops, s3c_rtc_suspend, s3c_rtc_resume); + +static void s3c6410_rtc_irq(struct s3c_rtc *info, int mask) +{ + rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF); + writeb(mask, info->base + S3C2410_INTP); +} + +static const struct s3c_rtc_data s3c6410_rtc_data = { + .needs_src_clk = true, + .irq_handler = s3c6410_rtc_irq, + .enable = s3c6410_rtc_enable, + .disable = s3c6410_rtc_disable, +}; + +static const __maybe_unused struct of_device_id s3c_rtc_dt_match[] = { + { + .compatible = "samsung,s3c6410-rtc", + .data = &s3c6410_rtc_data, + }, { + .compatible = "samsung,exynos3250-rtc", + .data = &s3c6410_rtc_data, + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, s3c_rtc_dt_match); + +static struct platform_driver s3c_rtc_driver = { + .probe = s3c_rtc_probe, + .remove = s3c_rtc_remove, + .driver = { + .name = "s3c-rtc", + .pm = &s3c_rtc_pm_ops, + .of_match_table = of_match_ptr(s3c_rtc_dt_match), + }, +}; +module_platform_driver(s3c_rtc_driver); + +MODULE_DESCRIPTION("Samsung S3C RTC Driver"); +MODULE_AUTHOR("Ben Dooks "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-s3c.h b/drivers/rtc/rtc-s3c.h new file mode 100644 index 000000000..11d7a1255 --- /dev/null +++ b/drivers/rtc/rtc-s3c.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2003 Simtec Electronics + * http://www.simtec.co.uk/products/SWLINUX/ + * + * S3C2410 Internal RTC register definition +*/ + +#ifndef __ASM_ARCH_REGS_RTC_H +#define __ASM_ARCH_REGS_RTC_H __FILE__ + +#define S3C2410_RTCREG(x) (x) +#define S3C2410_INTP S3C2410_RTCREG(0x30) +#define S3C2410_INTP_ALM (1 << 1) +#define S3C2410_INTP_TIC (1 << 0) + +#define S3C2410_RTCCON S3C2410_RTCREG(0x40) +#define S3C2410_RTCCON_RTCEN (1 << 0) +#define S3C2410_RTCCON_CNTSEL (1 << 2) +#define S3C2410_RTCCON_CLKRST (1 << 3) +#define S3C2443_RTCCON_TICSEL (1 << 4) +#define S3C64XX_RTCCON_TICEN (1 << 8) + +#define S3C2410_RTCALM S3C2410_RTCREG(0x50) +#define S3C2410_RTCALM_ALMEN (1 << 6) +#define S3C2410_RTCALM_YEAREN (1 << 5) +#define S3C2410_RTCALM_MONEN (1 << 4) +#define S3C2410_RTCALM_DAYEN (1 << 3) +#define S3C2410_RTCALM_HOUREN (1 << 2) +#define S3C2410_RTCALM_MINEN (1 << 1) +#define S3C2410_RTCALM_SECEN (1 << 0) + +#define S3C2410_ALMSEC S3C2410_RTCREG(0x54) +#define S3C2410_ALMMIN S3C2410_RTCREG(0x58) +#define S3C2410_ALMHOUR S3C2410_RTCREG(0x5c) + +#define S3C2410_ALMDATE S3C2410_RTCREG(0x60) +#define S3C2410_ALMMON S3C2410_RTCREG(0x64) +#define S3C2410_ALMYEAR S3C2410_RTCREG(0x68) + +#define S3C2410_RTCSEC S3C2410_RTCREG(0x70) +#define S3C2410_RTCMIN S3C2410_RTCREG(0x74) +#define S3C2410_RTCHOUR S3C2410_RTCREG(0x78) +#define S3C2410_RTCDATE S3C2410_RTCREG(0x7c) +#define S3C2410_RTCMON S3C2410_RTCREG(0x84) +#define S3C2410_RTCYEAR S3C2410_RTCREG(0x88) + +#endif /* __ASM_ARCH_REGS_RTC_H */ diff --git a/drivers/rtc/rtc-s5m.c b/drivers/rtc/rtc-s5m.c new file mode 100644 index 000000000..aa706074e --- /dev/null +++ b/drivers/rtc/rtc-s5m.c @@ -0,0 +1,833 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright (c) 2013-2014 Samsung Electronics Co., Ltd +// http://www.samsung.com +// +// Copyright (C) 2013 Google, Inc + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Maximum number of retries for checking changes in UDR field + * of S5M_RTC_UDR_CON register (to limit possible endless loop). + * + * After writing to RTC registers (setting time or alarm) read the UDR field + * in S5M_RTC_UDR_CON register. UDR is auto-cleared when data have + * been transferred. + */ +#define UDR_READ_RETRY_CNT 5 + +enum { + RTC_SEC = 0, + RTC_MIN, + RTC_HOUR, + RTC_WEEKDAY, + RTC_DATE, + RTC_MONTH, + RTC_YEAR1, + RTC_YEAR2, + /* Make sure this is always the last enum name. */ + RTC_MAX_NUM_TIME_REGS +}; + +/* + * Registers used by the driver which are different between chipsets. + * + * Operations like read time and write alarm/time require updating + * specific fields in UDR register. These fields usually are auto-cleared + * (with some exceptions). + * + * Table of operations per device: + * + * Device | Write time | Read time | Write alarm + * ================================================= + * S5M8767 | UDR + TIME | | UDR + * S2MPG10 | WUDR | RUDR | AUDR + * S2MPS11/14 | WUDR | RUDR | WUDR + RUDR + * S2MPS13 | WUDR | RUDR | WUDR + AUDR + * S2MPS15 | WUDR | RUDR | AUDR + */ +struct s5m_rtc_reg_config { + /* Number of registers used for setting time/alarm0/alarm1 */ + unsigned int regs_count; + /* First register for time, seconds */ + unsigned int time; + /* RTC control register */ + unsigned int ctrl; + /* First register for alarm 0, seconds */ + unsigned int alarm0; + /* First register for alarm 1, seconds */ + unsigned int alarm1; + /* + * Register for update flag (UDR). Typically setting UDR field to 1 + * will enable update of time or alarm register. Then it will be + * auto-cleared after successful update. + */ + unsigned int udr_update; + /* Auto-cleared mask in UDR field for writing time and alarm */ + unsigned int autoclear_udr_mask; + /* + * Masks in UDR field for time and alarm operations. + * The read time mask can be 0. Rest should not. + */ + unsigned int read_time_udr_mask; + unsigned int write_time_udr_mask; + unsigned int write_alarm_udr_mask; +}; + +/* Register map for S5M8767 */ +static const struct s5m_rtc_reg_config s5m_rtc_regs = { + .regs_count = 8, + .time = S5M_RTC_SEC, + .ctrl = S5M_ALARM1_CONF, + .alarm0 = S5M_ALARM0_SEC, + .alarm1 = S5M_ALARM1_SEC, + .udr_update = S5M_RTC_UDR_CON, + .autoclear_udr_mask = S5M_RTC_UDR_MASK, + .read_time_udr_mask = 0, /* Not needed */ + .write_time_udr_mask = S5M_RTC_UDR_MASK | S5M_RTC_TIME_EN_MASK, + .write_alarm_udr_mask = S5M_RTC_UDR_MASK, +}; + +/* Register map for S2MPG10 */ +static const struct s5m_rtc_reg_config s2mpg10_rtc_regs = { + .regs_count = 7, + .time = S2MPG10_RTC_SEC, + .ctrl = S2MPG10_RTC_CTRL, + .alarm0 = S2MPG10_RTC_A0SEC, + .alarm1 = S2MPG10_RTC_A1SEC, + .udr_update = S2MPG10_RTC_UPDATE, + .autoclear_udr_mask = S2MPS15_RTC_WUDR_MASK | S2MPS15_RTC_AUDR_MASK, + .read_time_udr_mask = S2MPS_RTC_RUDR_MASK, + .write_time_udr_mask = S2MPS15_RTC_WUDR_MASK, + .write_alarm_udr_mask = S2MPS15_RTC_AUDR_MASK, +}; + +/* Register map for S2MPS13 */ +static const struct s5m_rtc_reg_config s2mps13_rtc_regs = { + .regs_count = 7, + .time = S2MPS_RTC_SEC, + .ctrl = S2MPS_RTC_CTRL, + .alarm0 = S2MPS_ALARM0_SEC, + .alarm1 = S2MPS_ALARM1_SEC, + .udr_update = S2MPS_RTC_UDR_CON, + .autoclear_udr_mask = S2MPS_RTC_WUDR_MASK, + .read_time_udr_mask = S2MPS_RTC_RUDR_MASK, + .write_time_udr_mask = S2MPS_RTC_WUDR_MASK, + .write_alarm_udr_mask = S2MPS_RTC_WUDR_MASK | S2MPS13_RTC_AUDR_MASK, +}; + +/* Register map for S2MPS11/14 */ +static const struct s5m_rtc_reg_config s2mps14_rtc_regs = { + .regs_count = 7, + .time = S2MPS_RTC_SEC, + .ctrl = S2MPS_RTC_CTRL, + .alarm0 = S2MPS_ALARM0_SEC, + .alarm1 = S2MPS_ALARM1_SEC, + .udr_update = S2MPS_RTC_UDR_CON, + .autoclear_udr_mask = S2MPS_RTC_WUDR_MASK, + .read_time_udr_mask = S2MPS_RTC_RUDR_MASK, + .write_time_udr_mask = S2MPS_RTC_WUDR_MASK, + .write_alarm_udr_mask = S2MPS_RTC_WUDR_MASK | S2MPS_RTC_RUDR_MASK, +}; + +/* + * Register map for S2MPS15 - in comparison to S2MPS14 the WUDR and AUDR bits + * are swapped. + */ +static const struct s5m_rtc_reg_config s2mps15_rtc_regs = { + .regs_count = 7, + .time = S2MPS_RTC_SEC, + .ctrl = S2MPS_RTC_CTRL, + .alarm0 = S2MPS_ALARM0_SEC, + .alarm1 = S2MPS_ALARM1_SEC, + .udr_update = S2MPS_RTC_UDR_CON, + .autoclear_udr_mask = S2MPS_RTC_WUDR_MASK, + .read_time_udr_mask = S2MPS_RTC_RUDR_MASK, + .write_time_udr_mask = S2MPS15_RTC_WUDR_MASK, + .write_alarm_udr_mask = S2MPS15_RTC_AUDR_MASK, +}; + +struct s5m_rtc_info { + struct device *dev; + struct sec_pmic_dev *s5m87xx; + struct regmap *regmap; + struct rtc_device *rtc_dev; + int irq; + enum sec_device_type device_type; + int rtc_24hr_mode; + const struct s5m_rtc_reg_config *regs; +}; + +static const struct regmap_config s5m_rtc_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = S5M_RTC_REG_MAX, +}; + +static const struct regmap_config s2mps14_rtc_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = S2MPS_RTC_REG_MAX, +}; + +static void s5m8767_data_to_tm(u8 *data, struct rtc_time *tm, + int rtc_24hr_mode) +{ + tm->tm_sec = data[RTC_SEC] & 0x7f; + tm->tm_min = data[RTC_MIN] & 0x7f; + if (rtc_24hr_mode) { + tm->tm_hour = data[RTC_HOUR] & 0x1f; + } else { + tm->tm_hour = data[RTC_HOUR] & 0x0f; + if (data[RTC_HOUR] & HOUR_PM_MASK) + tm->tm_hour += 12; + } + + tm->tm_wday = ffs(data[RTC_WEEKDAY] & 0x7f); + tm->tm_mday = data[RTC_DATE] & 0x1f; + tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1; + tm->tm_year = (data[RTC_YEAR1] & 0x7f) + 100; + tm->tm_yday = 0; + tm->tm_isdst = 0; +} + +static int s5m8767_tm_to_data(struct rtc_time *tm, u8 *data) +{ + data[RTC_SEC] = tm->tm_sec; + data[RTC_MIN] = tm->tm_min; + + if (tm->tm_hour >= 12) + data[RTC_HOUR] = tm->tm_hour | HOUR_PM_MASK; + else + data[RTC_HOUR] = tm->tm_hour & ~HOUR_PM_MASK; + + data[RTC_WEEKDAY] = 1 << tm->tm_wday; + data[RTC_DATE] = tm->tm_mday; + data[RTC_MONTH] = tm->tm_mon + 1; + data[RTC_YEAR1] = tm->tm_year - 100; + + return 0; +} + +/* + * Read RTC_UDR_CON register and wait till UDR field is cleared. + * This indicates that time/alarm update ended. + */ +static int s5m8767_wait_for_udr_update(struct s5m_rtc_info *info) +{ + int ret, retry = UDR_READ_RETRY_CNT; + unsigned int data; + + do { + ret = regmap_read(info->regmap, info->regs->udr_update, &data); + } while (--retry && (data & info->regs->autoclear_udr_mask) && !ret); + + if (!retry) + dev_err(info->dev, "waiting for UDR update, reached max number of retries\n"); + + return ret; +} + +static int s5m_check_pending_alarm_interrupt(struct s5m_rtc_info *info, + struct rtc_wkalrm *alarm) +{ + int ret; + unsigned int val; + + switch (info->device_type) { + case S5M8767X: + ret = regmap_read(info->regmap, S5M_RTC_STATUS, &val); + val &= S5M_ALARM0_STATUS; + break; + case S2MPG10: + case S2MPS15X: + case S2MPS14X: + case S2MPS13X: + ret = regmap_read(info->s5m87xx->regmap_pmic, S2MPS14_REG_ST2, + &val); + val &= S2MPS_ALARM0_STATUS; + break; + default: + return -EINVAL; + } + if (ret < 0) + return ret; + + if (val) + alarm->pending = 1; + else + alarm->pending = 0; + + return 0; +} + +static int s5m8767_rtc_set_time_reg(struct s5m_rtc_info *info) +{ + int ret; + + ret = regmap_set_bits(info->regmap, info->regs->udr_update, + info->regs->write_time_udr_mask); + if (ret < 0) { + dev_err(info->dev, "failed to write update reg(%d)\n", ret); + return ret; + } + + ret = s5m8767_wait_for_udr_update(info); + + return ret; +} + +static int s5m8767_rtc_set_alarm_reg(struct s5m_rtc_info *info) +{ + int ret; + unsigned int udr_mask; + + udr_mask = info->regs->write_alarm_udr_mask; + switch (info->device_type) { + case S5M8767X: + udr_mask |= S5M_RTC_TIME_EN_MASK; + break; + case S2MPG10: + case S2MPS15X: + case S2MPS14X: + case S2MPS13X: + /* No exceptions needed */ + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(info->regmap, info->regs->udr_update, + udr_mask, info->regs->write_alarm_udr_mask); + if (ret < 0) { + dev_err(info->dev, "%s: fail to write update reg(%d)\n", + __func__, ret); + return ret; + } + + ret = s5m8767_wait_for_udr_update(info); + + /* On S2MPS13 the AUDR is not auto-cleared */ + if (info->device_type == S2MPS13X) + regmap_clear_bits(info->regmap, info->regs->udr_update, + S2MPS13_RTC_AUDR_MASK); + + return ret; +} + +static int s5m_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct s5m_rtc_info *info = dev_get_drvdata(dev); + u8 data[RTC_MAX_NUM_TIME_REGS]; + int ret; + + if (info->regs->read_time_udr_mask) { + ret = regmap_set_bits(info->regmap, info->regs->udr_update, + info->regs->read_time_udr_mask); + if (ret) { + dev_err(dev, + "Failed to prepare registers for time reading: %d\n", + ret); + return ret; + } + } + ret = regmap_bulk_read(info->regmap, info->regs->time, data, + info->regs->regs_count); + if (ret < 0) + return ret; + + switch (info->device_type) { + case S5M8767X: + case S2MPG10: + case S2MPS15X: + case S2MPS14X: + case S2MPS13X: + s5m8767_data_to_tm(data, tm, info->rtc_24hr_mode); + break; + + default: + return -EINVAL; + } + + dev_dbg(dev, "%s: %ptR(%d)\n", __func__, tm, tm->tm_wday); + + return 0; +} + +static int s5m_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct s5m_rtc_info *info = dev_get_drvdata(dev); + u8 data[RTC_MAX_NUM_TIME_REGS]; + int ret = 0; + + switch (info->device_type) { + case S5M8767X: + case S2MPG10: + case S2MPS15X: + case S2MPS14X: + case S2MPS13X: + ret = s5m8767_tm_to_data(tm, data); + break; + default: + return -EINVAL; + } + + if (ret < 0) + return ret; + + dev_dbg(dev, "%s: %ptR(%d)\n", __func__, tm, tm->tm_wday); + + ret = regmap_raw_write(info->regmap, info->regs->time, data, + info->regs->regs_count); + if (ret < 0) + return ret; + + ret = s5m8767_rtc_set_time_reg(info); + + return ret; +} + +static int s5m_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct s5m_rtc_info *info = dev_get_drvdata(dev); + u8 data[RTC_MAX_NUM_TIME_REGS]; + int ret, i; + + ret = regmap_bulk_read(info->regmap, info->regs->alarm0, data, + info->regs->regs_count); + if (ret < 0) + return ret; + + switch (info->device_type) { + case S5M8767X: + case S2MPG10: + case S2MPS15X: + case S2MPS14X: + case S2MPS13X: + s5m8767_data_to_tm(data, &alrm->time, info->rtc_24hr_mode); + alrm->enabled = 0; + for (i = 0; i < info->regs->regs_count; i++) { + if (data[i] & ALARM_ENABLE_MASK) { + alrm->enabled = 1; + break; + } + } + break; + + default: + return -EINVAL; + } + + dev_dbg(dev, "%s: %ptR(%d)\n", __func__, &alrm->time, alrm->time.tm_wday); + + return s5m_check_pending_alarm_interrupt(info, alrm); +} + +static int s5m_rtc_stop_alarm(struct s5m_rtc_info *info) +{ + u8 data[RTC_MAX_NUM_TIME_REGS]; + int ret, i; + struct rtc_time tm; + + ret = regmap_bulk_read(info->regmap, info->regs->alarm0, data, + info->regs->regs_count); + if (ret < 0) + return ret; + + s5m8767_data_to_tm(data, &tm, info->rtc_24hr_mode); + dev_dbg(info->dev, "%s: %ptR(%d)\n", __func__, &tm, tm.tm_wday); + + switch (info->device_type) { + case S5M8767X: + case S2MPG10: + case S2MPS15X: + case S2MPS14X: + case S2MPS13X: + for (i = 0; i < info->regs->regs_count; i++) + data[i] &= ~ALARM_ENABLE_MASK; + + ret = regmap_raw_write(info->regmap, info->regs->alarm0, data, + info->regs->regs_count); + if (ret < 0) + return ret; + + ret = s5m8767_rtc_set_alarm_reg(info); + + break; + + default: + return -EINVAL; + } + + return ret; +} + +static int s5m_rtc_start_alarm(struct s5m_rtc_info *info) +{ + int ret; + u8 data[RTC_MAX_NUM_TIME_REGS]; + struct rtc_time tm; + + ret = regmap_bulk_read(info->regmap, info->regs->alarm0, data, + info->regs->regs_count); + if (ret < 0) + return ret; + + s5m8767_data_to_tm(data, &tm, info->rtc_24hr_mode); + dev_dbg(info->dev, "%s: %ptR(%d)\n", __func__, &tm, tm.tm_wday); + + switch (info->device_type) { + case S5M8767X: + case S2MPG10: + case S2MPS15X: + case S2MPS14X: + case S2MPS13X: + data[RTC_SEC] |= ALARM_ENABLE_MASK; + data[RTC_MIN] |= ALARM_ENABLE_MASK; + data[RTC_HOUR] |= ALARM_ENABLE_MASK; + data[RTC_WEEKDAY] &= ~ALARM_ENABLE_MASK; + if (data[RTC_DATE] & 0x1f) + data[RTC_DATE] |= ALARM_ENABLE_MASK; + if (data[RTC_MONTH] & 0xf) + data[RTC_MONTH] |= ALARM_ENABLE_MASK; + if (data[RTC_YEAR1] & 0x7f) + data[RTC_YEAR1] |= ALARM_ENABLE_MASK; + + ret = regmap_raw_write(info->regmap, info->regs->alarm0, data, + info->regs->regs_count); + if (ret < 0) + return ret; + ret = s5m8767_rtc_set_alarm_reg(info); + + break; + + default: + return -EINVAL; + } + + return ret; +} + +static int s5m_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct s5m_rtc_info *info = dev_get_drvdata(dev); + u8 data[RTC_MAX_NUM_TIME_REGS]; + int ret; + + switch (info->device_type) { + case S5M8767X: + case S2MPG10: + case S2MPS15X: + case S2MPS14X: + case S2MPS13X: + s5m8767_tm_to_data(&alrm->time, data); + break; + + default: + return -EINVAL; + } + + dev_dbg(dev, "%s: %ptR(%d)\n", __func__, &alrm->time, alrm->time.tm_wday); + + ret = s5m_rtc_stop_alarm(info); + if (ret < 0) + return ret; + + ret = regmap_raw_write(info->regmap, info->regs->alarm0, data, + info->regs->regs_count); + if (ret < 0) + return ret; + + ret = s5m8767_rtc_set_alarm_reg(info); + if (ret < 0) + return ret; + + if (alrm->enabled) + ret = s5m_rtc_start_alarm(info); + + return ret; +} + +static int s5m_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct s5m_rtc_info *info = dev_get_drvdata(dev); + + if (enabled) + return s5m_rtc_start_alarm(info); + else + return s5m_rtc_stop_alarm(info); +} + +static irqreturn_t s5m_rtc_alarm_irq(int irq, void *data) +{ + struct s5m_rtc_info *info = data; + + rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops s5m_rtc_ops = { + .read_time = s5m_rtc_read_time, + .set_time = s5m_rtc_set_time, + .read_alarm = s5m_rtc_read_alarm, + .set_alarm = s5m_rtc_set_alarm, + .alarm_irq_enable = s5m_rtc_alarm_irq_enable, +}; + +static int s5m8767_rtc_init_reg(struct s5m_rtc_info *info) +{ + u8 data[2]; + int ret; + + switch (info->device_type) { + case S5M8767X: + /* UDR update time. Default of 7.32 ms is too long. */ + ret = regmap_update_bits(info->regmap, S5M_RTC_UDR_CON, + S5M_RTC_UDR_T_MASK, S5M_RTC_UDR_T_450_US); + if (ret < 0) + dev_err(info->dev, "%s: fail to change UDR time: %d\n", + __func__, ret); + + /* Set RTC control register : Binary mode, 24hour mode */ + data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); + data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); + + ret = regmap_raw_write(info->regmap, S5M_ALARM0_CONF, data, 2); + break; + + case S2MPG10: + case S2MPS15X: + case S2MPS14X: + case S2MPS13X: + data[0] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); + ret = regmap_write(info->regmap, info->regs->ctrl, data[0]); + if (ret < 0) + break; + + /* + * Should set WUDR & (RUDR or AUDR) bits to high after writing + * RTC_CTRL register like writing Alarm registers. We can't find + * the description from datasheet but vendor code does that + * really. + */ + ret = s5m8767_rtc_set_alarm_reg(info); + break; + + default: + return -EINVAL; + } + + info->rtc_24hr_mode = 1; + if (ret < 0) + return dev_err_probe(info->dev, ret, + "%s: fail to write controlm reg\n", + __func__); + + return ret; +} + +static int s5m_rtc_restart_s2mpg10(struct sys_off_data *data) +{ + struct s5m_rtc_info *info = data->cb_data; + int ret; + + if (data->mode != REBOOT_COLD && data->mode != REBOOT_HARD) + return NOTIFY_DONE; + + /* + * Arm watchdog with maximum timeout (2 seconds), and perform full reset + * on expiry. + */ + ret = regmap_set_bits(info->regmap, S2MPG10_RTC_WTSR, + (S2MPG10_WTSR_COLDTIMER | S2MPG10_WTSR_COLDRST + | S2MPG10_WTSR_WTSRT | S2MPG10_WTSR_WTSR_EN)); + + return ret ? NOTIFY_BAD : NOTIFY_DONE; +} + +static int s5m_rtc_probe(struct platform_device *pdev) +{ + struct sec_pmic_dev *s5m87xx = dev_get_drvdata(pdev->dev.parent); + enum sec_device_type device_type = + platform_get_device_id(pdev)->driver_data; + struct s5m_rtc_info *info; + int ret, alarm_irq; + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->regmap = dev_get_regmap(pdev->dev.parent, "rtc"); + if (!info->regmap) { + const struct regmap_config *regmap_cfg; + struct i2c_client *i2c; + + switch (device_type) { + case S2MPS15X: + regmap_cfg = &s2mps14_rtc_regmap_config; + info->regs = &s2mps15_rtc_regs; + break; + case S2MPS14X: + regmap_cfg = &s2mps14_rtc_regmap_config; + info->regs = &s2mps14_rtc_regs; + break; + case S2MPS13X: + regmap_cfg = &s2mps14_rtc_regmap_config; + info->regs = &s2mps13_rtc_regs; + break; + case S5M8767X: + regmap_cfg = &s5m_rtc_regmap_config; + info->regs = &s5m_rtc_regs; + break; + default: + return dev_err_probe(&pdev->dev, -ENODEV, + "Unsupported device type %d\n", + device_type); + } + + i2c = devm_i2c_new_dummy_device(&pdev->dev, + s5m87xx->i2c->adapter, + RTC_I2C_ADDR); + if (IS_ERR(i2c)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2c), + "Failed to allocate I2C\n"); + + info->regmap = devm_regmap_init_i2c(i2c, regmap_cfg); + if (IS_ERR(info->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(info->regmap), + "Failed to allocate regmap\n"); + } else if (device_type == S2MPG10) { + info->regs = &s2mpg10_rtc_regs; + } else { + return dev_err_probe(&pdev->dev, -ENODEV, + "Unsupported device type %d\n", + device_type); + } + + info->dev = &pdev->dev; + info->s5m87xx = s5m87xx; + info->device_type = device_type; + + alarm_irq = platform_get_irq_byname_optional(pdev, "alarm"); + if (alarm_irq > 0) + info->irq = alarm_irq; + else if (alarm_irq == -ENXIO) + info->irq = 0; + else + return dev_err_probe(&pdev->dev, alarm_irq ? : -EINVAL, + "IRQ 'alarm' not found\n"); + + platform_set_drvdata(pdev, info); + + ret = s5m8767_rtc_init_reg(info); + if (ret) + return ret; + + info->rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(info->rtc_dev)) + return PTR_ERR(info->rtc_dev); + + info->rtc_dev->ops = &s5m_rtc_ops; + + info->rtc_dev->range_min = RTC_TIMESTAMP_BEGIN_2000; + info->rtc_dev->range_max = RTC_TIMESTAMP_END_2099; + + if (!info->irq) { + clear_bit(RTC_FEATURE_ALARM, info->rtc_dev->features); + } else { + ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL, + s5m_rtc_alarm_irq, 0, "rtc-alarm0", + info); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Failed to request alarm IRQ %d\n", + info->irq); + + ret = devm_device_init_wakeup(&pdev->dev); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Failed to init wakeup\n"); + } + + if (of_device_is_system_power_controller(pdev->dev.parent->of_node) && + info->device_type == S2MPG10) { + ret = devm_register_sys_off_handler(&pdev->dev, + SYS_OFF_MODE_RESTART, + SYS_OFF_PRIO_HIGH + 1, + s5m_rtc_restart_s2mpg10, + info); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to register restart handler\n"); + } + + return devm_rtc_register_device(info->rtc_dev); +} + +#ifdef CONFIG_PM_SLEEP +static int s5m_rtc_resume(struct device *dev) +{ + struct s5m_rtc_info *info = dev_get_drvdata(dev); + int ret = 0; + + if (info->irq && device_may_wakeup(dev)) + ret = disable_irq_wake(info->irq); + + return ret; +} + +static int s5m_rtc_suspend(struct device *dev) +{ + struct s5m_rtc_info *info = dev_get_drvdata(dev); + int ret = 0; + + if (info->irq && device_may_wakeup(dev)) + ret = enable_irq_wake(info->irq); + + return ret; +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(s5m_rtc_pm_ops, s5m_rtc_suspend, s5m_rtc_resume); + +static const struct platform_device_id s5m_rtc_id[] = { + { .name = "s5m-rtc", .driver_data = S5M8767X }, + { .name = "s2mpg10-rtc", .driver_data = S2MPG10 }, + { .name = "s2mps13-rtc", .driver_data = S2MPS13X }, + { .name = "s2mps14-rtc", .driver_data = S2MPS14X }, + { .name = "s2mps15-rtc", .driver_data = S2MPS15X }, + { } +}; +MODULE_DEVICE_TABLE(platform, s5m_rtc_id); + +static struct platform_driver s5m_rtc_driver = { + .driver = { + .name = "s5m-rtc", + .pm = &s5m_rtc_pm_ops, + }, + .probe = s5m_rtc_probe, + .id_table = s5m_rtc_id, +}; + +module_platform_driver(s5m_rtc_driver); + +/* Module information */ +MODULE_AUTHOR("Sangbeom Kim "); +MODULE_DESCRIPTION("Samsung S5M/S2MPS14 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c new file mode 100644 index 000000000..26b2f4184 --- /dev/null +++ b/drivers/rtc/rtc-sa1100.c @@ -0,0 +1,354 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Real Time Clock interface for StrongARM SA1x00 and XScale PXA2xx + * + * Copyright (c) 2000 Nils Faerber + * + * Based on rtc.c by Paul Gortmaker + * + * Original Driver by Nils Faerber + * + * Modifications from: + * CIH + * Nicolas Pitre + * Andrew Christian + * + * Converted to the RTC subsystem and Driver Model + * by Richard Purdie + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RTSR_HZE BIT(3) /* HZ interrupt enable */ +#define RTSR_ALE BIT(2) /* RTC alarm interrupt enable */ +#define RTSR_HZ BIT(1) /* HZ rising-edge detected */ +#define RTSR_AL BIT(0) /* RTC alarm detected */ + +#include "rtc-sa1100.h" + +#define RTC_DEF_DIVIDER (32768 - 1) +#define RTC_DEF_TRIM 0 + +static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id) +{ + struct sa1100_rtc *info = dev_get_drvdata(dev_id); + struct rtc_device *rtc = info->rtc; + unsigned int rtsr; + unsigned long events = 0; + + spin_lock(&info->lock); + + rtsr = readl_relaxed(info->rtsr); + /* clear interrupt sources */ + writel_relaxed(0, info->rtsr); + /* Fix for a nasty initialization problem the in SA11xx RTSR register. + * See also the comments in sa1100_rtc_probe(). */ + if (rtsr & (RTSR_ALE | RTSR_HZE)) { + /* This is the original code, before there was the if test + * above. This code does not clear interrupts that were not + * enabled. */ + writel_relaxed((RTSR_AL | RTSR_HZ) & (rtsr >> 2), info->rtsr); + } else { + /* For some reason, it is possible to enter this routine + * without interruptions enabled, it has been tested with + * several units (Bug in SA11xx chip?). + * + * This situation leads to an infinite "loop" of interrupt + * routine calling and as a result the processor seems to + * lock on its first call to open(). */ + writel_relaxed(RTSR_AL | RTSR_HZ, info->rtsr); + } + + /* clear alarm interrupt if it has occurred */ + if (rtsr & RTSR_AL) + rtsr &= ~RTSR_ALE; + writel_relaxed(rtsr & (RTSR_ALE | RTSR_HZE), info->rtsr); + + /* update irq data & counter */ + if (rtsr & RTSR_AL) + events |= RTC_AF | RTC_IRQF; + if (rtsr & RTSR_HZ) + events |= RTC_UF | RTC_IRQF; + + rtc_update_irq(rtc, 1, events); + + spin_unlock(&info->lock); + + return IRQ_HANDLED; +} + +static int sa1100_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + u32 rtsr; + struct sa1100_rtc *info = dev_get_drvdata(dev); + + spin_lock_irq(&info->lock); + rtsr = readl_relaxed(info->rtsr); + if (enabled) + rtsr |= RTSR_ALE; + else + rtsr &= ~RTSR_ALE; + writel_relaxed(rtsr, info->rtsr); + spin_unlock_irq(&info->lock); + return 0; +} + +static int sa1100_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct sa1100_rtc *info = dev_get_drvdata(dev); + + rtc_time64_to_tm(readl_relaxed(info->rcnr), tm); + return 0; +} + +static int sa1100_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct sa1100_rtc *info = dev_get_drvdata(dev); + + writel_relaxed(rtc_tm_to_time64(tm), info->rcnr); + + return 0; +} + +static int sa1100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + u32 rtsr; + struct sa1100_rtc *info = dev_get_drvdata(dev); + + rtsr = readl_relaxed(info->rtsr); + alrm->enabled = (rtsr & RTSR_ALE) ? 1 : 0; + alrm->pending = (rtsr & RTSR_AL) ? 1 : 0; + return 0; +} + +static int sa1100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct sa1100_rtc *info = dev_get_drvdata(dev); + + spin_lock_irq(&info->lock); + writel_relaxed(readl_relaxed(info->rtsr) & + (RTSR_HZE | RTSR_ALE | RTSR_AL), info->rtsr); + writel_relaxed(rtc_tm_to_time64(&alrm->time), info->rtar); + if (alrm->enabled) + writel_relaxed(readl_relaxed(info->rtsr) | RTSR_ALE, info->rtsr); + else + writel_relaxed(readl_relaxed(info->rtsr) & ~RTSR_ALE, info->rtsr); + spin_unlock_irq(&info->lock); + + return 0; +} + +static int sa1100_rtc_proc(struct device *dev, struct seq_file *seq) +{ + struct sa1100_rtc *info = dev_get_drvdata(dev); + + seq_printf(seq, "trim/divider\t\t: 0x%08x\n", readl_relaxed(info->rttr)); + seq_printf(seq, "RTSR\t\t\t: 0x%08x\n", readl_relaxed(info->rtsr)); + + return 0; +} + +static const struct rtc_class_ops sa1100_rtc_ops = { + .read_time = sa1100_rtc_read_time, + .set_time = sa1100_rtc_set_time, + .read_alarm = sa1100_rtc_read_alarm, + .set_alarm = sa1100_rtc_set_alarm, + .proc = sa1100_rtc_proc, + .alarm_irq_enable = sa1100_rtc_alarm_irq_enable, +}; + +int sa1100_rtc_init(struct platform_device *pdev, struct sa1100_rtc *info) +{ + int ret; + + spin_lock_init(&info->lock); + + info->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(info->clk)) { + dev_err(&pdev->dev, "failed to find rtc clock source\n"); + return PTR_ERR(info->clk); + } + + ret = clk_prepare_enable(info->clk); + if (ret) + return ret; + /* + * According to the manual we should be able to let RTTR be zero + * and then a default diviser for a 32.768KHz clock is used. + * Apparently this doesn't work, at least for my SA1110 rev 5. + * If the clock divider is uninitialized then reset it to the + * default value to get the 1Hz clock. + */ + if (readl_relaxed(info->rttr) == 0) { + writel_relaxed(RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16), info->rttr); + dev_warn(&pdev->dev, "warning: " + "initializing default clock divider/trim value\n"); + /* The current RTC value probably doesn't make sense either */ + writel_relaxed(0, info->rcnr); + } + + info->rtc->ops = &sa1100_rtc_ops; + info->rtc->range_max = U32_MAX; + + ret = devm_rtc_register_device(info->rtc); + if (ret) { + clk_disable_unprepare(info->clk); + return ret; + } + + /* Fix for a nasty initialization problem the in SA11xx RTSR register. + * See also the comments in sa1100_rtc_interrupt(). + * + * Sometimes bit 1 of the RTSR (RTSR_HZ) will wake up 1, which means an + * interrupt pending, even though interrupts were never enabled. + * In this case, this bit it must be reset before enabling + * interruptions to avoid a nonexistent interrupt to occur. + * + * In principle, the same problem would apply to bit 0, although it has + * never been observed to happen. + * + * This issue is addressed both here and in sa1100_rtc_interrupt(). + * If the issue is not addressed here, in the times when the processor + * wakes up with the bit set there will be one spurious interrupt. + * + * The issue is also dealt with in sa1100_rtc_interrupt() to be on the + * safe side, once the condition that lead to this strange + * initialization is unknown and could in principle happen during + * normal processing. + * + * Notice that clearing bit 1 and 0 is accomplished by writting ONES to + * the corresponding bits in RTSR. */ + writel_relaxed(RTSR_AL | RTSR_HZ, info->rtsr); + + return 0; +} +EXPORT_SYMBOL_GPL(sa1100_rtc_init); + +static int sa1100_rtc_probe(struct platform_device *pdev) +{ + struct sa1100_rtc *info; + void __iomem *base; + int irq_1hz, irq_alarm; + int ret; + + irq_1hz = platform_get_irq_byname(pdev, "rtc 1Hz"); + irq_alarm = platform_get_irq_byname(pdev, "rtc alarm"); + if (irq_1hz < 0 || irq_alarm < 0) + return -ENODEV; + + info = devm_kzalloc(&pdev->dev, sizeof(struct sa1100_rtc), GFP_KERNEL); + if (!info) + return -ENOMEM; + info->irq_1hz = irq_1hz; + info->irq_alarm = irq_alarm; + + info->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(info->rtc)) + return PTR_ERR(info->rtc); + + ret = devm_request_irq(&pdev->dev, irq_1hz, sa1100_rtc_interrupt, 0, + "rtc 1Hz", &pdev->dev); + if (ret) { + dev_err(&pdev->dev, "IRQ %d already in use.\n", irq_1hz); + return ret; + } + ret = devm_request_irq(&pdev->dev, irq_alarm, sa1100_rtc_interrupt, 0, + "rtc Alrm", &pdev->dev); + if (ret) { + dev_err(&pdev->dev, "IRQ %d already in use.\n", irq_alarm); + return ret; + } + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + if (IS_ENABLED(CONFIG_ARCH_SA1100) || + of_device_is_compatible(pdev->dev.of_node, "mrvl,sa1100-rtc")) { + info->rcnr = base + 0x04; + info->rtsr = base + 0x10; + info->rtar = base + 0x00; + info->rttr = base + 0x08; + } else { + info->rcnr = base + 0x0; + info->rtsr = base + 0x8; + info->rtar = base + 0x4; + info->rttr = base + 0xc; + } + + platform_set_drvdata(pdev, info); + device_init_wakeup(&pdev->dev, true); + + return sa1100_rtc_init(pdev, info); +} + +static void sa1100_rtc_remove(struct platform_device *pdev) +{ + struct sa1100_rtc *info = platform_get_drvdata(pdev); + + if (info) { + spin_lock_irq(&info->lock); + writel_relaxed(0, info->rtsr); + spin_unlock_irq(&info->lock); + clk_disable_unprepare(info->clk); + } +} + +#ifdef CONFIG_PM_SLEEP +static int sa1100_rtc_suspend(struct device *dev) +{ + struct sa1100_rtc *info = dev_get_drvdata(dev); + if (device_may_wakeup(dev)) + enable_irq_wake(info->irq_alarm); + return 0; +} + +static int sa1100_rtc_resume(struct device *dev) +{ + struct sa1100_rtc *info = dev_get_drvdata(dev); + if (device_may_wakeup(dev)) + disable_irq_wake(info->irq_alarm); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(sa1100_rtc_pm_ops, sa1100_rtc_suspend, + sa1100_rtc_resume); + +#ifdef CONFIG_OF +static const struct of_device_id sa1100_rtc_dt_ids[] = { + { .compatible = "mrvl,sa1100-rtc", }, + { .compatible = "mrvl,mmp-rtc", }, + {} +}; +MODULE_DEVICE_TABLE(of, sa1100_rtc_dt_ids); +#endif + +static struct platform_driver sa1100_rtc_driver = { + .probe = sa1100_rtc_probe, + .remove = sa1100_rtc_remove, + .driver = { + .name = "sa1100-rtc", + .pm = &sa1100_rtc_pm_ops, + .of_match_table = of_match_ptr(sa1100_rtc_dt_ids), + }, +}; + +module_platform_driver(sa1100_rtc_driver); + +MODULE_AUTHOR("Richard Purdie "); +MODULE_DESCRIPTION("SA11x0/PXA2xx Realtime Clock Driver (RTC)"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:sa1100-rtc"); diff --git a/drivers/rtc/rtc-sa1100.h b/drivers/rtc/rtc-sa1100.h new file mode 100644 index 000000000..cc724f5b0 --- /dev/null +++ b/drivers/rtc/rtc-sa1100.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __RTC_SA1100_H__ +#define __RTC_SA1100_H__ + +#include + +struct clk; +struct platform_device; + +struct sa1100_rtc { + spinlock_t lock; + void __iomem *rcnr; + void __iomem *rtar; + void __iomem *rtsr; + void __iomem *rttr; + int irq_1hz; + int irq_alarm; + struct rtc_device *rtc; + struct clk *clk; +}; + +int sa1100_rtc_init(struct platform_device *pdev, struct sa1100_rtc *info); + +#endif diff --git a/drivers/rtc/rtc-sc27xx.c b/drivers/rtc/rtc-sc27xx.c new file mode 100644 index 000000000..2b83561d4 --- /dev/null +++ b/drivers/rtc/rtc-sc27xx.c @@ -0,0 +1,647 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2017 Spreadtrum Communications Inc. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SPRD_RTC_SEC_CNT_VALUE 0x0 +#define SPRD_RTC_MIN_CNT_VALUE 0x4 +#define SPRD_RTC_HOUR_CNT_VALUE 0x8 +#define SPRD_RTC_DAY_CNT_VALUE 0xc +#define SPRD_RTC_SEC_CNT_UPD 0x10 +#define SPRD_RTC_MIN_CNT_UPD 0x14 +#define SPRD_RTC_HOUR_CNT_UPD 0x18 +#define SPRD_RTC_DAY_CNT_UPD 0x1c +#define SPRD_RTC_SEC_ALM_UPD 0x20 +#define SPRD_RTC_MIN_ALM_UPD 0x24 +#define SPRD_RTC_HOUR_ALM_UPD 0x28 +#define SPRD_RTC_DAY_ALM_UPD 0x2c +#define SPRD_RTC_INT_EN 0x30 +#define SPRD_RTC_INT_RAW_STS 0x34 +#define SPRD_RTC_INT_CLR 0x38 +#define SPRD_RTC_INT_MASK_STS 0x3C +#define SPRD_RTC_SEC_ALM_VALUE 0x40 +#define SPRD_RTC_MIN_ALM_VALUE 0x44 +#define SPRD_RTC_HOUR_ALM_VALUE 0x48 +#define SPRD_RTC_DAY_ALM_VALUE 0x4c +#define SPRD_RTC_SPG_VALUE 0x50 +#define SPRD_RTC_SPG_UPD 0x54 +#define SPRD_RTC_PWR_CTRL 0x58 +#define SPRD_RTC_PWR_STS 0x5c +#define SPRD_RTC_SEC_AUXALM_UPD 0x60 +#define SPRD_RTC_MIN_AUXALM_UPD 0x64 +#define SPRD_RTC_HOUR_AUXALM_UPD 0x68 +#define SPRD_RTC_DAY_AUXALM_UPD 0x6c + +/* BIT & MASK definition for SPRD_RTC_INT_* registers */ +#define SPRD_RTC_SEC_EN BIT(0) +#define SPRD_RTC_MIN_EN BIT(1) +#define SPRD_RTC_HOUR_EN BIT(2) +#define SPRD_RTC_DAY_EN BIT(3) +#define SPRD_RTC_ALARM_EN BIT(4) +#define SPRD_RTC_HRS_FORMAT_EN BIT(5) +#define SPRD_RTC_AUXALM_EN BIT(6) +#define SPRD_RTC_SPG_UPD_EN BIT(7) +#define SPRD_RTC_SEC_UPD_EN BIT(8) +#define SPRD_RTC_MIN_UPD_EN BIT(9) +#define SPRD_RTC_HOUR_UPD_EN BIT(10) +#define SPRD_RTC_DAY_UPD_EN BIT(11) +#define SPRD_RTC_ALMSEC_UPD_EN BIT(12) +#define SPRD_RTC_ALMMIN_UPD_EN BIT(13) +#define SPRD_RTC_ALMHOUR_UPD_EN BIT(14) +#define SPRD_RTC_ALMDAY_UPD_EN BIT(15) +#define SPRD_RTC_INT_MASK GENMASK(15, 0) + +#define SPRD_RTC_TIME_INT_MASK \ + (SPRD_RTC_SEC_UPD_EN | SPRD_RTC_MIN_UPD_EN | \ + SPRD_RTC_HOUR_UPD_EN | SPRD_RTC_DAY_UPD_EN) + +#define SPRD_RTC_ALMTIME_INT_MASK \ + (SPRD_RTC_ALMSEC_UPD_EN | SPRD_RTC_ALMMIN_UPD_EN | \ + SPRD_RTC_ALMHOUR_UPD_EN | SPRD_RTC_ALMDAY_UPD_EN) + +#define SPRD_RTC_ALM_INT_MASK \ + (SPRD_RTC_SEC_EN | SPRD_RTC_MIN_EN | \ + SPRD_RTC_HOUR_EN | SPRD_RTC_DAY_EN | \ + SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN) + +/* second/minute/hour/day values mask definition */ +#define SPRD_RTC_SEC_MASK GENMASK(5, 0) +#define SPRD_RTC_MIN_MASK GENMASK(5, 0) +#define SPRD_RTC_HOUR_MASK GENMASK(4, 0) +#define SPRD_RTC_DAY_MASK GENMASK(15, 0) + +/* alarm lock definition for SPRD_RTC_SPG_UPD register */ +#define SPRD_RTC_ALMLOCK_MASK GENMASK(7, 0) +#define SPRD_RTC_ALM_UNLOCK 0xa5 +#define SPRD_RTC_ALM_LOCK (~SPRD_RTC_ALM_UNLOCK & \ + SPRD_RTC_ALMLOCK_MASK) + +/* SPG values definition for SPRD_RTC_SPG_UPD register */ +#define SPRD_RTC_POWEROFF_ALM_FLAG BIT(8) + +/* power control/status definition */ +#define SPRD_RTC_POWER_RESET_VALUE 0x96 +#define SPRD_RTC_POWER_STS_CLEAR GENMASK(7, 0) +#define SPRD_RTC_POWER_STS_SHIFT 8 +#define SPRD_RTC_POWER_STS_VALID \ + (~SPRD_RTC_POWER_RESET_VALUE << SPRD_RTC_POWER_STS_SHIFT) + +/* timeout of synchronizing time and alarm registers (us) */ +#define SPRD_RTC_POLL_TIMEOUT 200000 +#define SPRD_RTC_POLL_DELAY_US 20000 + +struct sprd_rtc { + struct rtc_device *rtc; + struct regmap *regmap; + struct device *dev; + u32 base; + int irq; + bool valid; +}; + +/* + * The Spreadtrum RTC controller has 3 groups registers, including time, normal + * alarm and auxiliary alarm. The time group registers are used to set RTC time, + * the normal alarm registers are used to set normal alarm, and the auxiliary + * alarm registers are used to set auxiliary alarm. Both alarm event and + * auxiliary alarm event can wake up system from deep sleep, but only alarm + * event can power up system from power down status. + */ +enum sprd_rtc_reg_types { + SPRD_RTC_TIME, + SPRD_RTC_ALARM, + SPRD_RTC_AUX_ALARM, +}; + +static int sprd_rtc_clear_alarm_ints(struct sprd_rtc *rtc) +{ + return regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR, + SPRD_RTC_ALM_INT_MASK); +} + +static int sprd_rtc_lock_alarm(struct sprd_rtc *rtc, bool lock) +{ + int ret; + u32 val; + + ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_SPG_VALUE, &val); + if (ret) + return ret; + + val &= ~SPRD_RTC_ALMLOCK_MASK; + if (lock) + val |= SPRD_RTC_ALM_LOCK; + else + val |= SPRD_RTC_ALM_UNLOCK | SPRD_RTC_POWEROFF_ALM_FLAG; + + ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_SPG_UPD, val); + if (ret) + return ret; + + /* wait until the SPG value is updated successfully */ + ret = regmap_read_poll_timeout(rtc->regmap, + rtc->base + SPRD_RTC_INT_RAW_STS, val, + (val & SPRD_RTC_SPG_UPD_EN), + SPRD_RTC_POLL_DELAY_US, + SPRD_RTC_POLL_TIMEOUT); + if (ret) { + dev_err(rtc->dev, "failed to update SPG value:%d\n", ret); + return ret; + } + + return regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR, + SPRD_RTC_SPG_UPD_EN); +} + +static int sprd_rtc_get_secs(struct sprd_rtc *rtc, enum sprd_rtc_reg_types type, + time64_t *secs) +{ + u32 sec_reg, min_reg, hour_reg, day_reg; + u32 val, sec, min, hour, day; + int ret; + + switch (type) { + case SPRD_RTC_TIME: + sec_reg = SPRD_RTC_SEC_CNT_VALUE; + min_reg = SPRD_RTC_MIN_CNT_VALUE; + hour_reg = SPRD_RTC_HOUR_CNT_VALUE; + day_reg = SPRD_RTC_DAY_CNT_VALUE; + break; + case SPRD_RTC_ALARM: + sec_reg = SPRD_RTC_SEC_ALM_VALUE; + min_reg = SPRD_RTC_MIN_ALM_VALUE; + hour_reg = SPRD_RTC_HOUR_ALM_VALUE; + day_reg = SPRD_RTC_DAY_ALM_VALUE; + break; + case SPRD_RTC_AUX_ALARM: + sec_reg = SPRD_RTC_SEC_AUXALM_UPD; + min_reg = SPRD_RTC_MIN_AUXALM_UPD; + hour_reg = SPRD_RTC_HOUR_AUXALM_UPD; + day_reg = SPRD_RTC_DAY_AUXALM_UPD; + break; + default: + return -EINVAL; + } + + ret = regmap_read(rtc->regmap, rtc->base + sec_reg, &val); + if (ret) + return ret; + + sec = val & SPRD_RTC_SEC_MASK; + + ret = regmap_read(rtc->regmap, rtc->base + min_reg, &val); + if (ret) + return ret; + + min = val & SPRD_RTC_MIN_MASK; + + ret = regmap_read(rtc->regmap, rtc->base + hour_reg, &val); + if (ret) + return ret; + + hour = val & SPRD_RTC_HOUR_MASK; + + ret = regmap_read(rtc->regmap, rtc->base + day_reg, &val); + if (ret) + return ret; + + day = val & SPRD_RTC_DAY_MASK; + *secs = (((time64_t)(day * 24) + hour) * 60 + min) * 60 + sec; + return 0; +} + +static int sprd_rtc_set_secs(struct sprd_rtc *rtc, enum sprd_rtc_reg_types type, + time64_t secs) +{ + u32 sec_reg, min_reg, hour_reg, day_reg, sts_mask; + u32 sec, min, hour, day, val; + int ret, rem; + + /* convert seconds to RTC time format */ + day = div_s64_rem(secs, 86400, &rem); + hour = rem / 3600; + rem -= hour * 3600; + min = rem / 60; + sec = rem - min * 60; + + switch (type) { + case SPRD_RTC_TIME: + sec_reg = SPRD_RTC_SEC_CNT_UPD; + min_reg = SPRD_RTC_MIN_CNT_UPD; + hour_reg = SPRD_RTC_HOUR_CNT_UPD; + day_reg = SPRD_RTC_DAY_CNT_UPD; + sts_mask = SPRD_RTC_TIME_INT_MASK; + break; + case SPRD_RTC_ALARM: + sec_reg = SPRD_RTC_SEC_ALM_UPD; + min_reg = SPRD_RTC_MIN_ALM_UPD; + hour_reg = SPRD_RTC_HOUR_ALM_UPD; + day_reg = SPRD_RTC_DAY_ALM_UPD; + sts_mask = SPRD_RTC_ALMTIME_INT_MASK; + break; + case SPRD_RTC_AUX_ALARM: + sec_reg = SPRD_RTC_SEC_AUXALM_UPD; + min_reg = SPRD_RTC_MIN_AUXALM_UPD; + hour_reg = SPRD_RTC_HOUR_AUXALM_UPD; + day_reg = SPRD_RTC_DAY_AUXALM_UPD; + sts_mask = 0; + break; + default: + return -EINVAL; + } + + ret = regmap_write(rtc->regmap, rtc->base + sec_reg, sec); + if (ret) + return ret; + + ret = regmap_write(rtc->regmap, rtc->base + min_reg, min); + if (ret) + return ret; + + ret = regmap_write(rtc->regmap, rtc->base + hour_reg, hour); + if (ret) + return ret; + + ret = regmap_write(rtc->regmap, rtc->base + day_reg, day); + if (ret) + return ret; + + if (type == SPRD_RTC_AUX_ALARM) + return 0; + + /* + * Since the time and normal alarm registers are put in always-power-on + * region supplied by VDDRTC, then these registers changing time will + * be very long, about 125ms. Thus here we should wait until all + * values are updated successfully. + */ + ret = regmap_read_poll_timeout(rtc->regmap, + rtc->base + SPRD_RTC_INT_RAW_STS, val, + ((val & sts_mask) == sts_mask), + SPRD_RTC_POLL_DELAY_US, + SPRD_RTC_POLL_TIMEOUT); + if (ret < 0) { + dev_err(rtc->dev, "set time/alarm values timeout\n"); + return ret; + } + + return regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR, + sts_mask); +} + +static int sprd_rtc_set_aux_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct sprd_rtc *rtc = dev_get_drvdata(dev); + time64_t secs = rtc_tm_to_time64(&alrm->time); + int ret; + + /* clear the auxiliary alarm interrupt status */ + ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR, + SPRD_RTC_AUXALM_EN); + if (ret) + return ret; + + ret = sprd_rtc_set_secs(rtc, SPRD_RTC_AUX_ALARM, secs); + if (ret) + return ret; + + if (alrm->enabled) { + ret = regmap_update_bits(rtc->regmap, + rtc->base + SPRD_RTC_INT_EN, + SPRD_RTC_AUXALM_EN, + SPRD_RTC_AUXALM_EN); + } else { + ret = regmap_update_bits(rtc->regmap, + rtc->base + SPRD_RTC_INT_EN, + SPRD_RTC_AUXALM_EN, 0); + } + + return ret; +} + +static int sprd_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct sprd_rtc *rtc = dev_get_drvdata(dev); + time64_t secs; + int ret; + + if (!rtc->valid) { + dev_warn(dev, "RTC values are invalid\n"); + return -EINVAL; + } + + ret = sprd_rtc_get_secs(rtc, SPRD_RTC_TIME, &secs); + if (ret) + return ret; + + rtc_time64_to_tm(secs, tm); + return 0; +} + +static int sprd_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct sprd_rtc *rtc = dev_get_drvdata(dev); + time64_t secs = rtc_tm_to_time64(tm); + int ret; + + ret = sprd_rtc_set_secs(rtc, SPRD_RTC_TIME, secs); + if (ret) + return ret; + + if (!rtc->valid) { + /* Clear RTC power status firstly */ + ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_PWR_CTRL, + SPRD_RTC_POWER_STS_CLEAR); + if (ret) + return ret; + + /* + * Set RTC power status to indicate now RTC has valid time + * values. + */ + ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_PWR_CTRL, + SPRD_RTC_POWER_STS_VALID); + if (ret) + return ret; + + rtc->valid = true; + } + + return 0; +} + +static int sprd_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct sprd_rtc *rtc = dev_get_drvdata(dev); + time64_t secs; + int ret; + u32 val; + + /* + * The RTC core checks to see if there is an alarm already set in RTC + * hardware, and we always read the normal alarm at this time. + */ + ret = sprd_rtc_get_secs(rtc, SPRD_RTC_ALARM, &secs); + if (ret) + return ret; + + rtc_time64_to_tm(secs, &alrm->time); + + ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_INT_EN, &val); + if (ret) + return ret; + + alrm->enabled = !!(val & SPRD_RTC_ALARM_EN); + + ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_INT_RAW_STS, &val); + if (ret) + return ret; + + alrm->pending = !!(val & SPRD_RTC_ALARM_EN); + return 0; +} + +static int sprd_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct sprd_rtc *rtc = dev_get_drvdata(dev); + time64_t secs = rtc_tm_to_time64(&alrm->time); + struct rtc_time aie_time = + rtc_ktime_to_tm(rtc->rtc->aie_timer.node.expires); + int ret; + + /* + * We have 2 groups alarms: normal alarm and auxiliary alarm. Since + * both normal alarm event and auxiliary alarm event can wake up system + * from deep sleep, but only alarm event can power up system from power + * down status. Moreover we do not need to poll about 125ms when + * updating auxiliary alarm registers. Thus we usually set auxiliary + * alarm when wake up system from deep sleep, and for other scenarios, + * we should set normal alarm with polling status. + * + * So here we check if the alarm time is set by aie_timer, if yes, we + * should set normal alarm, if not, we should set auxiliary alarm which + * means it is just a wake event. + */ + if (!rtc->rtc->aie_timer.enabled || rtc_tm_sub(&aie_time, &alrm->time)) + return sprd_rtc_set_aux_alarm(dev, alrm); + + /* clear the alarm interrupt status firstly */ + ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR, + SPRD_RTC_ALARM_EN); + if (ret) + return ret; + + ret = sprd_rtc_set_secs(rtc, SPRD_RTC_ALARM, secs); + if (ret) + return ret; + + if (alrm->enabled) { + ret = regmap_update_bits(rtc->regmap, + rtc->base + SPRD_RTC_INT_EN, + SPRD_RTC_ALARM_EN, + SPRD_RTC_ALARM_EN); + if (ret) + return ret; + + /* unlock the alarm to enable the alarm function. */ + ret = sprd_rtc_lock_alarm(rtc, false); + } else { + regmap_update_bits(rtc->regmap, + rtc->base + SPRD_RTC_INT_EN, + SPRD_RTC_ALARM_EN, 0); + + /* + * Lock the alarm function in case fake alarm event will power + * up systems. + */ + ret = sprd_rtc_lock_alarm(rtc, true); + } + + return ret; +} + +static int sprd_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct sprd_rtc *rtc = dev_get_drvdata(dev); + int ret; + + if (enabled) { + ret = regmap_update_bits(rtc->regmap, + rtc->base + SPRD_RTC_INT_EN, + SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN, + SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN); + if (ret) + return ret; + + ret = sprd_rtc_lock_alarm(rtc, false); + } else { + regmap_update_bits(rtc->regmap, rtc->base + SPRD_RTC_INT_EN, + SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN, 0); + + ret = sprd_rtc_lock_alarm(rtc, true); + } + + return ret; +} + +static const struct rtc_class_ops sprd_rtc_ops = { + .read_time = sprd_rtc_read_time, + .set_time = sprd_rtc_set_time, + .read_alarm = sprd_rtc_read_alarm, + .set_alarm = sprd_rtc_set_alarm, + .alarm_irq_enable = sprd_rtc_alarm_irq_enable, +}; + +static irqreturn_t sprd_rtc_handler(int irq, void *dev_id) +{ + struct sprd_rtc *rtc = dev_id; + int ret; + + ret = sprd_rtc_clear_alarm_ints(rtc); + if (ret) + return IRQ_RETVAL(ret); + + rtc_update_irq(rtc->rtc, 1, RTC_AF | RTC_IRQF); + return IRQ_HANDLED; +} + +static int sprd_rtc_check_power_down(struct sprd_rtc *rtc) +{ + u32 val; + int ret; + + ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_PWR_STS, &val); + if (ret) + return ret; + + /* + * If the RTC power status value is SPRD_RTC_POWER_RESET_VALUE, which + * means the RTC has been powered down, so the RTC time values are + * invalid. + */ + rtc->valid = val != SPRD_RTC_POWER_RESET_VALUE; + return 0; +} + +static int sprd_rtc_check_alarm_int(struct sprd_rtc *rtc) +{ + u32 val; + int ret; + + ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_SPG_VALUE, &val); + if (ret) + return ret; + + /* + * The SPRD_RTC_INT_EN register is not put in always-power-on region + * supplied by VDDRTC, so we should check if we need enable the alarm + * interrupt when system booting. + * + * If we have set SPRD_RTC_POWEROFF_ALM_FLAG which is saved in + * always-power-on region, that means we have set one alarm last time, + * so we should enable the alarm interrupt to help RTC core to see if + * there is an alarm already set in RTC hardware. + */ + if (!(val & SPRD_RTC_POWEROFF_ALM_FLAG)) + return 0; + + return regmap_update_bits(rtc->regmap, rtc->base + SPRD_RTC_INT_EN, + SPRD_RTC_ALARM_EN, SPRD_RTC_ALARM_EN); +} + +static int sprd_rtc_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct sprd_rtc *rtc; + int ret; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!rtc->regmap) + return -ENODEV; + + ret = of_property_read_u32(node, "reg", &rtc->base); + if (ret) { + dev_err(&pdev->dev, "failed to get RTC base address\n"); + return ret; + } + + rtc->irq = platform_get_irq(pdev, 0); + if (rtc->irq < 0) + return rtc->irq; + + rtc->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc->rtc)) + return PTR_ERR(rtc->rtc); + + rtc->dev = &pdev->dev; + platform_set_drvdata(pdev, rtc); + + /* check if we need set the alarm interrupt */ + ret = sprd_rtc_check_alarm_int(rtc); + if (ret) { + dev_err(&pdev->dev, "failed to check RTC alarm interrupt\n"); + return ret; + } + + /* check if RTC time values are valid */ + ret = sprd_rtc_check_power_down(rtc); + if (ret) { + dev_err(&pdev->dev, "failed to check RTC time values\n"); + return ret; + } + + ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL, + sprd_rtc_handler, + IRQF_ONESHOT | IRQF_EARLY_RESUME, + pdev->name, rtc); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request RTC irq\n"); + return ret; + } + + device_init_wakeup(&pdev->dev, true); + + rtc->rtc->ops = &sprd_rtc_ops; + rtc->rtc->range_min = 0; + rtc->rtc->range_max = 5662310399LL; + ret = devm_rtc_register_device(rtc->rtc); + if (ret) { + device_init_wakeup(&pdev->dev, false); + return ret; + } + + return 0; +} + +static const struct of_device_id sprd_rtc_of_match[] = { + { .compatible = "sprd,sc2731-rtc", }, + { }, +}; +MODULE_DEVICE_TABLE(of, sprd_rtc_of_match); + +static struct platform_driver sprd_rtc_driver = { + .driver = { + .name = "sprd-rtc", + .of_match_table = sprd_rtc_of_match, + }, + .probe = sprd_rtc_probe, +}; +module_platform_driver(sprd_rtc_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Spreadtrum RTC Device Driver"); +MODULE_AUTHOR("Baolin Wang "); diff --git a/drivers/rtc/rtc-sd2405al.c b/drivers/rtc/rtc-sd2405al.c new file mode 100644 index 000000000..7982a910a --- /dev/null +++ b/drivers/rtc/rtc-sd2405al.c @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * RTC driver for the SD2405AL Real-Time Clock + * + * Datasheet: + * https://image.dfrobot.com/image/data/TOY0021/SD2405AL%20datasheet%20(Angelo%20v0.1).pdf + * + * I2C slave address: 0x32 + * + * Copyright (C) 2024-2025 Tóth János + */ + +#include +#include +#include +#include + +/* Real time clock registers */ +#define SD2405AL_REG_T_SEC 0x00 +#define SD2405AL_REG_T_MIN 0x01 +#define SD2405AL_REG_T_HOUR 0x02 +# define SD2405AL_BIT_12H_PM BIT(5) +# define SD2405AL_BIT_24H BIT(7) +#define SD2405AL_REG_T_WEEK 0x03 +#define SD2405AL_REG_T_DAY 0x04 +#define SD2405AL_REG_T_MON 0x05 +#define SD2405AL_REG_T_YEAR 0x06 + +#define SD2405AL_NUM_T_REGS (SD2405AL_REG_T_YEAR - SD2405AL_REG_T_SEC + 1) + +/* Control registers */ +#define SD2405AL_REG_CTR1 0x0F +# define SD2405AL_BIT_WRTC2 BIT(2) +# define SD2405AL_BIT_WRTC3 BIT(7) +#define SD2405AL_REG_CTR2 0x10 +# define SD2405AL_BIT_WRTC1 BIT(7) +#define SD2405AL_REG_CTR3 0x11 +#define SD2405AL_REG_TTF 0x12 +#define SD2405AL_REG_CNTDWN 0x13 + +/* General RAM */ +#define SD2405AL_REG_M_START 0x14 +#define SD2405AL_REG_M_END 0x1F + +struct sd2405al { + struct device *dev; + struct regmap *regmap; +}; + +static int sd2405al_enable_reg_write(struct sd2405al *sd2405al) +{ + int ret; + + /* order of writes is important */ + ret = regmap_update_bits(sd2405al->regmap, SD2405AL_REG_CTR2, + SD2405AL_BIT_WRTC1, SD2405AL_BIT_WRTC1); + if (ret < 0) + return ret; + + ret = regmap_update_bits(sd2405al->regmap, SD2405AL_REG_CTR1, + SD2405AL_BIT_WRTC2 | SD2405AL_BIT_WRTC3, + SD2405AL_BIT_WRTC2 | SD2405AL_BIT_WRTC3); + if (ret < 0) + return ret; + + return 0; +} + +static int sd2405al_disable_reg_write(struct sd2405al *sd2405al) +{ + int ret; + + /* order of writes is important */ + ret = regmap_update_bits(sd2405al->regmap, SD2405AL_REG_CTR1, + SD2405AL_BIT_WRTC2 | SD2405AL_BIT_WRTC3, 0x00); + if (ret < 0) + return ret; + + ret = regmap_update_bits(sd2405al->regmap, SD2405AL_REG_CTR2, + SD2405AL_BIT_WRTC1, 0x00); + if (ret < 0) + return ret; + + return 0; +} + +static int sd2405al_read_time(struct device *dev, struct rtc_time *time) +{ + u8 data[SD2405AL_NUM_T_REGS] = { 0 }; + struct sd2405al *sd2405al = dev_get_drvdata(dev); + int ret; + + ret = regmap_bulk_read(sd2405al->regmap, SD2405AL_REG_T_SEC, data, + SD2405AL_NUM_T_REGS); + if (ret < 0) + return ret; + + time->tm_sec = bcd2bin(data[SD2405AL_REG_T_SEC] & 0x7F); + time->tm_min = bcd2bin(data[SD2405AL_REG_T_MIN] & 0x7F); + + if (data[SD2405AL_REG_T_HOUR] & SD2405AL_BIT_24H) + time->tm_hour = bcd2bin(data[SD2405AL_REG_T_HOUR] & 0x3F); + else + if (data[SD2405AL_REG_T_HOUR] & SD2405AL_BIT_12H_PM) + time->tm_hour = bcd2bin(data[SD2405AL_REG_T_HOUR] + & 0x1F) + 12; + else /* 12 hour mode, AM */ + time->tm_hour = bcd2bin(data[SD2405AL_REG_T_HOUR] + & 0x1F); + + time->tm_wday = bcd2bin(data[SD2405AL_REG_T_WEEK] & 0x07); + time->tm_mday = bcd2bin(data[SD2405AL_REG_T_DAY] & 0x3F); + time->tm_mon = bcd2bin(data[SD2405AL_REG_T_MON] & 0x1F) - 1; + time->tm_year = bcd2bin(data[SD2405AL_REG_T_YEAR]) + 100; + + dev_dbg(sd2405al->dev, "read time: %ptR (%d)\n", time, time->tm_wday); + + return 0; +} + +static int sd2405al_set_time(struct device *dev, struct rtc_time *time) +{ + u8 data[SD2405AL_NUM_T_REGS]; + struct sd2405al *sd2405al = dev_get_drvdata(dev); + int ret; + + data[SD2405AL_REG_T_SEC] = bin2bcd(time->tm_sec); + data[SD2405AL_REG_T_MIN] = bin2bcd(time->tm_min); + data[SD2405AL_REG_T_HOUR] = bin2bcd(time->tm_hour) | SD2405AL_BIT_24H; + data[SD2405AL_REG_T_DAY] = bin2bcd(time->tm_mday); + data[SD2405AL_REG_T_WEEK] = bin2bcd(time->tm_wday); + data[SD2405AL_REG_T_MON] = bin2bcd(time->tm_mon) + 1; + data[SD2405AL_REG_T_YEAR] = bin2bcd(time->tm_year - 100); + + ret = sd2405al_enable_reg_write(sd2405al); + if (ret < 0) + return ret; + + ret = regmap_bulk_write(sd2405al->regmap, SD2405AL_REG_T_SEC, data, + SD2405AL_NUM_T_REGS); + if (ret < 0) + return ret; + + ret = regmap_write(sd2405al->regmap, SD2405AL_REG_TTF, 0x00); + if (ret < 0) + return ret; + + ret = sd2405al_disable_reg_write(sd2405al); + if (ret < 0) + return ret; + + dev_dbg(sd2405al->dev, "set time: %ptR (%d)\n", time, time->tm_wday); + + return 0; +} + +static const struct rtc_class_ops sd2405al_rtc_ops = { + .read_time = sd2405al_read_time, + .set_time = sd2405al_set_time, +}; + +static const struct regmap_config sd2405al_regmap_conf = { + .reg_bits = 8, + .val_bits = 8, + .max_register = SD2405AL_REG_M_END, +}; + +static int sd2405al_probe(struct i2c_client *client) +{ + struct sd2405al *sd2405al; + struct rtc_device *rtc; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + sd2405al = devm_kzalloc(&client->dev, sizeof(*sd2405al), GFP_KERNEL); + if (!sd2405al) + return -ENOMEM; + + sd2405al->dev = &client->dev; + + sd2405al->regmap = devm_regmap_init_i2c(client, &sd2405al_regmap_conf); + if (IS_ERR(sd2405al->regmap)) + return PTR_ERR(sd2405al->regmap); + + rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + rtc->ops = &sd2405al_rtc_ops; + rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rtc->range_max = RTC_TIMESTAMP_END_2099; + + dev_set_drvdata(&client->dev, sd2405al); + + ret = devm_rtc_register_device(rtc); + if (ret < 0) + return ret; + + return 0; +} + +static const struct i2c_device_id sd2405al_id[] = { + { .name = "sd2405al" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, sd2405al_id); + +static const __maybe_unused struct of_device_id sd2405al_of_match[] = { + { .compatible = "dfrobot,sd2405al" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sd2405al_of_match); + +static struct i2c_driver sd2405al_driver = { + .driver = { + .name = "sd2405al", + .of_match_table = of_match_ptr(sd2405al_of_match), + }, + .probe = sd2405al_probe, + .id_table = sd2405al_id, +}; + +module_i2c_driver(sd2405al_driver); + +MODULE_AUTHOR("Tóth János "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SD2405AL RTC driver"); diff --git a/drivers/rtc/rtc-sd3078.c b/drivers/rtc/rtc-sd3078.c new file mode 100644 index 000000000..2c61f0e20 --- /dev/null +++ b/drivers/rtc/rtc-sd3078.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Real Time Clock (RTC) Driver for sd3078 + * Copyright (C) 2018 Zoro Li + */ + +#include +#include +#include +#include +#include +#include + +#define SD3078_REG_SC 0x00 +#define SD3078_REG_MN 0x01 +#define SD3078_REG_HR 0x02 +#define SD3078_REG_DW 0x03 +#define SD3078_REG_DM 0x04 +#define SD3078_REG_MO 0x05 +#define SD3078_REG_YR 0x06 + +#define SD3078_REG_CTRL1 0x0f +#define SD3078_REG_CTRL2 0x10 +#define SD3078_REG_CTRL3 0x11 + +#define KEY_WRITE1 0x80 +#define KEY_WRITE2 0x04 +#define KEY_WRITE3 0x80 + +#define NUM_TIME_REGS (SD3078_REG_YR - SD3078_REG_SC + 1) + +/* + * The sd3078 has write protection + * and we can choose whether or not to use it. + * Write protection is turned off by default. + */ +#define WRITE_PROTECT_EN 0 + +/* + * In order to prevent arbitrary modification of the time register, + * when modification of the register, + * the "write" bit needs to be written in a certain order. + * 1. set WRITE1 bit + * 2. set WRITE2 bit + * 3. set WRITE3 bit + */ +static void sd3078_enable_reg_write(struct regmap *regmap) +{ + regmap_update_bits(regmap, SD3078_REG_CTRL2, KEY_WRITE1, KEY_WRITE1); + regmap_update_bits(regmap, SD3078_REG_CTRL1, KEY_WRITE2, KEY_WRITE2); + regmap_update_bits(regmap, SD3078_REG_CTRL1, KEY_WRITE3, KEY_WRITE3); +} + +#if WRITE_PROTECT_EN +/* + * In order to prevent arbitrary modification of the time register, + * we should disable the write function. + * when disable write, + * the "write" bit needs to be clear in a certain order. + * 1. clear WRITE2 bit + * 2. clear WRITE3 bit + * 3. clear WRITE1 bit + */ +static void sd3078_disable_reg_write(struct regmap *regmap) +{ + regmap_update_bits(regmap, SD3078_REG_CTRL1, KEY_WRITE2, 0); + regmap_update_bits(regmap, SD3078_REG_CTRL1, KEY_WRITE3, 0); + regmap_update_bits(regmap, SD3078_REG_CTRL2, KEY_WRITE1, 0); +} +#endif + +static int sd3078_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char hour; + unsigned char rtc_data[NUM_TIME_REGS] = {0}; + struct i2c_client *client = to_i2c_client(dev); + struct regmap *regmap = i2c_get_clientdata(client); + int ret; + + ret = regmap_bulk_read(regmap, SD3078_REG_SC, rtc_data, NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "reading from RTC failed with err:%d\n", ret); + return ret; + } + + tm->tm_sec = bcd2bin(rtc_data[SD3078_REG_SC] & 0x7F); + tm->tm_min = bcd2bin(rtc_data[SD3078_REG_MN] & 0x7F); + + /* + * The sd3078 supports 12/24 hour mode. + * When getting time, + * we need to convert the 12 hour mode to the 24 hour mode. + */ + hour = rtc_data[SD3078_REG_HR]; + if (hour & 0x80) /* 24H MODE */ + tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x3F); + else if (hour & 0x20) /* 12H MODE PM */ + tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x1F) + 12; + else /* 12H MODE AM */ + tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x1F); + + tm->tm_mday = bcd2bin(rtc_data[SD3078_REG_DM] & 0x3F); + tm->tm_wday = rtc_data[SD3078_REG_DW] & 0x07; + tm->tm_mon = bcd2bin(rtc_data[SD3078_REG_MO] & 0x1F) - 1; + tm->tm_year = bcd2bin(rtc_data[SD3078_REG_YR]) + 100; + + return 0; +} + +static int sd3078_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char rtc_data[NUM_TIME_REGS]; + struct i2c_client *client = to_i2c_client(dev); + struct regmap *regmap = i2c_get_clientdata(client); + int ret; + + rtc_data[SD3078_REG_SC] = bin2bcd(tm->tm_sec); + rtc_data[SD3078_REG_MN] = bin2bcd(tm->tm_min); + rtc_data[SD3078_REG_HR] = bin2bcd(tm->tm_hour) | 0x80; + rtc_data[SD3078_REG_DM] = bin2bcd(tm->tm_mday); + rtc_data[SD3078_REG_DW] = tm->tm_wday & 0x07; + rtc_data[SD3078_REG_MO] = bin2bcd(tm->tm_mon) + 1; + rtc_data[SD3078_REG_YR] = bin2bcd(tm->tm_year - 100); + +#if WRITE_PROTECT_EN + sd3078_enable_reg_write(regmap); +#endif + + ret = regmap_bulk_write(regmap, SD3078_REG_SC, rtc_data, + NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "writing to RTC failed with err:%d\n", ret); + return ret; + } + +#if WRITE_PROTECT_EN + sd3078_disable_reg_write(regmap); +#endif + + return 0; +} + +static const struct rtc_class_ops sd3078_rtc_ops = { + .read_time = sd3078_rtc_read_time, + .set_time = sd3078_rtc_set_time, +}; + +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x11, +}; + +static int sd3078_probe(struct i2c_client *client) +{ + int ret; + struct regmap *regmap; + struct rtc_device *rtc; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "regmap allocation failed\n"); + return PTR_ERR(regmap); + } + + i2c_set_clientdata(client, regmap); + + rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + rtc->ops = &sd3078_rtc_ops; + rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rtc->range_max = RTC_TIMESTAMP_END_2099; + + ret = devm_rtc_register_device(rtc); + if (ret) + return ret; + + sd3078_enable_reg_write(regmap); + + return 0; +} + +static const struct i2c_device_id sd3078_id[] = { + { .name = "sd3078" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sd3078_id); + +static const __maybe_unused struct of_device_id rtc_dt_match[] = { + { .compatible = "whwave,sd3078" }, + {}, +}; +MODULE_DEVICE_TABLE(of, rtc_dt_match); + +static struct i2c_driver sd3078_driver = { + .driver = { + .name = "sd3078", + .of_match_table = of_match_ptr(rtc_dt_match), + }, + .probe = sd3078_probe, + .id_table = sd3078_id, +}; + +module_i2c_driver(sd3078_driver); + +MODULE_AUTHOR("Dianlong Li "); +MODULE_DESCRIPTION("SD3078 RTC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c new file mode 100644 index 000000000..7a5feceb9 --- /dev/null +++ b/drivers/rtc/rtc-sh.c @@ -0,0 +1,506 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SuperH On-Chip RTC Support + * + * Copyright (C) 2006 - 2009 Paul Mundt + * Copyright (C) 2006 Jamie Lenehan + * Copyright (C) 2008 Angelo Castello + * Copyright (C) 2025 Wolfram Sang, Renesas Electronics Corporation + * + * Based on the old arch/sh/kernel/cpu/rtc.c by: + * + * Copyright (C) 2000 Philipp Rumpf + * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_SUPERH +#include +#else +/* Default values for RZ/A RTC */ +#define rtc_reg_size sizeof(u16) +#define RTC_BIT_INVERTED 0 /* no chip bugs */ +#define RTC_CAP_4_DIGIT_YEAR BIT(0) +#define RTC_DEF_CAPABILITIES RTC_CAP_4_DIGIT_YEAR +#endif + +#define DRV_NAME "sh-rtc" + +#define RTC_REG(r) ((r) * rtc_reg_size) + +#define R64CNT RTC_REG(0) + +#define RSECCNT RTC_REG(1) /* RTC sec */ +#define RMINCNT RTC_REG(2) /* RTC min */ +#define RHRCNT RTC_REG(3) /* RTC hour */ +#define RWKCNT RTC_REG(4) /* RTC week */ +#define RDAYCNT RTC_REG(5) /* RTC day */ +#define RMONCNT RTC_REG(6) /* RTC month */ +#define RYRCNT RTC_REG(7) /* RTC year */ +#define RSECAR RTC_REG(8) /* ALARM sec */ +#define RMINAR RTC_REG(9) /* ALARM min */ +#define RHRAR RTC_REG(10) /* ALARM hour */ +#define RWKAR RTC_REG(11) /* ALARM week */ +#define RDAYAR RTC_REG(12) /* ALARM day */ +#define RMONAR RTC_REG(13) /* ALARM month */ +#define RCR1 RTC_REG(14) /* Control */ +#define RCR2 RTC_REG(15) /* Control */ + +/* + * Note on RYRAR and RCR3: Up until this point most of the register + * definitions are consistent across all of the available parts. However, + * the placement of the optional RYRAR and RCR3 (the RYRAR control + * register used to control RYRCNT/RYRAR compare) varies considerably + * across various parts, occasionally being mapped in to a completely + * unrelated address space. For proper RYRAR support a separate resource + * would have to be handed off, but as this is purely optional in + * practice, we simply opt not to support it, thereby keeping the code + * quite a bit more simplified. + */ + +/* ALARM Bits - or with BCD encoded value */ +#define AR_ENB BIT(7) /* Enable for alarm cmp */ + +/* RCR1 Bits */ +#define RCR1_CF BIT(7) /* Carry Flag */ +#define RCR1_CIE BIT(4) /* Carry Interrupt Enable */ +#define RCR1_AIE BIT(3) /* Alarm Interrupt Enable */ +#define RCR1_AF BIT(0) /* Alarm Flag */ + +/* RCR2 Bits */ +#define RCR2_RTCEN BIT(3) /* ENable RTC */ +#define RCR2_ADJ BIT(2) /* ADJustment (30-second) */ +#define RCR2_RESET BIT(1) /* Reset bit */ +#define RCR2_START BIT(0) /* Start bit */ + +struct sh_rtc { + void __iomem *regbase; + int alarm_irq; + struct clk *clk; + struct rtc_device *rtc_dev; + spinlock_t lock; /* protecting register access */ + unsigned long capabilities; /* See asm/rtc.h for cap bits */ +}; + +static irqreturn_t sh_rtc_alarm(int irq, void *dev_id) +{ + struct sh_rtc *rtc = dev_id; + unsigned int tmp, pending; + + spin_lock(&rtc->lock); + + tmp = readb(rtc->regbase + RCR1); + pending = tmp & RCR1_AF; + tmp &= ~(RCR1_AF | RCR1_AIE); + writeb(tmp, rtc->regbase + RCR1); + + if (pending) + rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF); + + spin_unlock(&rtc->lock); + + return IRQ_RETVAL(pending); +} + +static int sh_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) +{ + struct sh_rtc *rtc = dev_get_drvdata(dev); + unsigned int tmp; + + spin_lock_irq(&rtc->lock); + + tmp = readb(rtc->regbase + RCR1); + + if (enable) + tmp |= RCR1_AIE; + else + tmp &= ~RCR1_AIE; + + writeb(tmp, rtc->regbase + RCR1); + + spin_unlock_irq(&rtc->lock); + + return 0; +} + +static int sh_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct sh_rtc *rtc = dev_get_drvdata(dev); + unsigned int sec128, sec2, yr, yr100, cf_bit; + + if (!(readb(rtc->regbase + RCR2) & RCR2_RTCEN)) + return -EINVAL; + + do { + unsigned int tmp; + + spin_lock_irq(&rtc->lock); + + tmp = readb(rtc->regbase + RCR1); + tmp &= ~RCR1_CF; /* Clear CF-bit */ + tmp |= RCR1_CIE; + writeb(tmp, rtc->regbase + RCR1); + + sec128 = readb(rtc->regbase + R64CNT); + + tm->tm_sec = bcd2bin(readb(rtc->regbase + RSECCNT)); + tm->tm_min = bcd2bin(readb(rtc->regbase + RMINCNT)); + tm->tm_hour = bcd2bin(readb(rtc->regbase + RHRCNT)); + tm->tm_wday = bcd2bin(readb(rtc->regbase + RWKCNT)); + tm->tm_mday = bcd2bin(readb(rtc->regbase + RDAYCNT)); + tm->tm_mon = bcd2bin(readb(rtc->regbase + RMONCNT)) - 1; + + if (rtc->capabilities & RTC_CAP_4_DIGIT_YEAR) { + yr = readw(rtc->regbase + RYRCNT); + yr100 = bcd2bin(yr >> 8); + yr &= 0xff; + } else { + yr = readb(rtc->regbase + RYRCNT); + yr100 = bcd2bin((yr == 0x99) ? 0x19 : 0x20); + } + + tm->tm_year = (yr100 * 100 + bcd2bin(yr)) - 1900; + + sec2 = readb(rtc->regbase + R64CNT); + cf_bit = readb(rtc->regbase + RCR1) & RCR1_CF; + + spin_unlock_irq(&rtc->lock); + } while (cf_bit != 0 || ((sec128 ^ sec2) & RTC_BIT_INVERTED) != 0); + +#if RTC_BIT_INVERTED != 0 + if ((sec128 & RTC_BIT_INVERTED)) + tm->tm_sec--; +#endif + + dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon + 1, tm->tm_year, tm->tm_wday); + + return 0; +} + +static int sh_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct sh_rtc *rtc = dev_get_drvdata(dev); + unsigned int tmp; + int year; + + spin_lock_irq(&rtc->lock); + + /* Reset pre-scaler & stop RTC */ + tmp = readb(rtc->regbase + RCR2); + tmp |= RCR2_RESET; + tmp &= ~RCR2_START; + writeb(tmp, rtc->regbase + RCR2); + + writeb(bin2bcd(tm->tm_sec), rtc->regbase + RSECCNT); + writeb(bin2bcd(tm->tm_min), rtc->regbase + RMINCNT); + writeb(bin2bcd(tm->tm_hour), rtc->regbase + RHRCNT); + writeb(bin2bcd(tm->tm_wday), rtc->regbase + RWKCNT); + writeb(bin2bcd(tm->tm_mday), rtc->regbase + RDAYCNT); + writeb(bin2bcd(tm->tm_mon + 1), rtc->regbase + RMONCNT); + + if (rtc->capabilities & RTC_CAP_4_DIGIT_YEAR) { + year = (bin2bcd((tm->tm_year + 1900) / 100) << 8) | + bin2bcd(tm->tm_year % 100); + writew(year, rtc->regbase + RYRCNT); + } else { + year = tm->tm_year % 100; + writeb(bin2bcd(year), rtc->regbase + RYRCNT); + } + + /* Start RTC */ + tmp = readb(rtc->regbase + RCR2); + tmp &= ~RCR2_RESET; + tmp |= RCR2_RTCEN | RCR2_START; + writeb(tmp, rtc->regbase + RCR2); + + spin_unlock_irq(&rtc->lock); + + return 0; +} + +static inline int sh_rtc_read_alarm_value(struct sh_rtc *rtc, int reg_off) +{ + unsigned int byte; + int value = -1; /* return -1 for ignored values */ + + byte = readb(rtc->regbase + reg_off); + if (byte & AR_ENB) { + byte &= ~AR_ENB; /* strip the enable bit */ + value = bcd2bin(byte); + } + + return value; +} + +static int sh_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct sh_rtc *rtc = dev_get_drvdata(dev); + struct rtc_time *tm = &wkalrm->time; + + spin_lock_irq(&rtc->lock); + + tm->tm_sec = sh_rtc_read_alarm_value(rtc, RSECAR); + tm->tm_min = sh_rtc_read_alarm_value(rtc, RMINAR); + tm->tm_hour = sh_rtc_read_alarm_value(rtc, RHRAR); + tm->tm_wday = sh_rtc_read_alarm_value(rtc, RWKAR); + tm->tm_mday = sh_rtc_read_alarm_value(rtc, RDAYAR); + tm->tm_mon = sh_rtc_read_alarm_value(rtc, RMONAR); + if (tm->tm_mon > 0) + tm->tm_mon -= 1; /* RTC is 1-12, tm_mon is 0-11 */ + + wkalrm->enabled = (readb(rtc->regbase + RCR1) & RCR1_AIE) ? 1 : 0; + + spin_unlock_irq(&rtc->lock); + + return 0; +} + +static inline void sh_rtc_write_alarm_value(struct sh_rtc *rtc, + int value, int reg_off) +{ + /* < 0 for a value that is ignored */ + if (value < 0) + writeb(0, rtc->regbase + reg_off); + else + writeb(bin2bcd(value) | AR_ENB, rtc->regbase + reg_off); +} + +static int sh_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct sh_rtc *rtc = dev_get_drvdata(dev); + unsigned int rcr1; + struct rtc_time *tm = &wkalrm->time; + int mon; + + spin_lock_irq(&rtc->lock); + + /* disable alarm interrupt and clear the alarm flag */ + rcr1 = readb(rtc->regbase + RCR1); + rcr1 &= ~(RCR1_AF | RCR1_AIE); + writeb(rcr1, rtc->regbase + RCR1); + + /* set alarm time */ + sh_rtc_write_alarm_value(rtc, tm->tm_sec, RSECAR); + sh_rtc_write_alarm_value(rtc, tm->tm_min, RMINAR); + sh_rtc_write_alarm_value(rtc, tm->tm_hour, RHRAR); + sh_rtc_write_alarm_value(rtc, tm->tm_wday, RWKAR); + sh_rtc_write_alarm_value(rtc, tm->tm_mday, RDAYAR); + mon = tm->tm_mon; + if (mon >= 0) + mon += 1; + sh_rtc_write_alarm_value(rtc, mon, RMONAR); + + if (wkalrm->enabled) { + rcr1 |= RCR1_AIE; + writeb(rcr1, rtc->regbase + RCR1); + } + + spin_unlock_irq(&rtc->lock); + + return 0; +} + +static const struct rtc_class_ops sh_rtc_ops = { + .read_time = sh_rtc_read_time, + .set_time = sh_rtc_set_time, + .read_alarm = sh_rtc_read_alarm, + .set_alarm = sh_rtc_set_alarm, + .alarm_irq_enable = sh_rtc_alarm_irq_enable, +}; + +static int __init sh_rtc_probe(struct platform_device *pdev) +{ + struct sh_rtc *rtc; + struct resource *res, *req_res; + char clk_name[14]; + int clk_id, ret; + unsigned int tmp; + resource_size_t regsize; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (unlikely(!rtc)) + return -ENOMEM; + + spin_lock_init(&rtc->lock); + + ret = platform_get_irq(pdev, 0); + if (unlikely(ret <= 0)) { + dev_err(&pdev->dev, "No IRQ resource\n"); + return -ENOENT; + } + + if (!pdev->dev.of_node) + rtc->alarm_irq = platform_get_irq(pdev, 2); + else + rtc->alarm_irq = ret; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "No IO resource\n"); + return -ENOENT; + } + + regsize = resource_size(res); + req_res = devm_request_mem_region(&pdev->dev, res->start, regsize, pdev->name); + if (!req_res) + return -EBUSY; + + rtc->regbase = devm_ioremap(&pdev->dev, req_res->start, regsize); + if (!rtc->regbase) + return -EINVAL; + + if (!pdev->dev.of_node) { + clk_id = pdev->id; + /* With a single device, the clock id is still "rtc0" */ + if (clk_id < 0) + clk_id = 0; + + snprintf(clk_name, sizeof(clk_name), "rtc%d", clk_id); + } else { + snprintf(clk_name, sizeof(clk_name), "fck"); + } + + rtc->clk = devm_clk_get(&pdev->dev, clk_name); + if (IS_ERR(rtc->clk)) { + /* + * No error handling for rtc->clk intentionally, not all + * platforms will have a unique clock for the RTC, and + * the clk API can handle the struct clk pointer being + * NULL. + */ + rtc->clk = NULL; + } + + rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc->rtc_dev)) + return PTR_ERR(rtc->rtc_dev); + + clk_enable(rtc->clk); + + rtc->capabilities = RTC_DEF_CAPABILITIES; + +#ifdef CONFIG_SUPERH + if (dev_get_platdata(&pdev->dev)) { + struct sh_rtc_platform_info *pinfo = + dev_get_platdata(&pdev->dev); + + /* + * Some CPUs have special capabilities in addition to the + * default set. Add those in here. + */ + rtc->capabilities |= pinfo->capabilities; + } +#endif + + ret = devm_request_irq(&pdev->dev, rtc->alarm_irq, sh_rtc_alarm, 0, "sh-rtc", rtc); + if (ret) { + dev_err(&pdev->dev, "request alarm IRQ failed with %d, IRQ %d\n", + ret, rtc->alarm_irq); + goto err_unmap; + } + + platform_set_drvdata(pdev, rtc); + + /* everything disabled by default */ + tmp = readb(rtc->regbase + RCR1); + tmp &= ~(RCR1_CIE | RCR1_AIE); + writeb(tmp, rtc->regbase + RCR1); + + rtc->rtc_dev->ops = &sh_rtc_ops; + + if (rtc->capabilities & RTC_CAP_4_DIGIT_YEAR) { + rtc->rtc_dev->range_min = RTC_TIMESTAMP_BEGIN_1900; + rtc->rtc_dev->range_max = RTC_TIMESTAMP_END_9999; + } else { + rtc->rtc_dev->range_min = mktime64(1999, 1, 1, 0, 0, 0); + rtc->rtc_dev->range_max = mktime64(2098, 12, 31, 23, 59, 59); + } + + ret = devm_rtc_register_device(rtc->rtc_dev); + if (ret) + goto err_unmap; + + device_init_wakeup(&pdev->dev, true); + return 0; + +err_unmap: + clk_disable(rtc->clk); + + return ret; +} + +static void __exit sh_rtc_remove(struct platform_device *pdev) +{ + struct sh_rtc *rtc = platform_get_drvdata(pdev); + + sh_rtc_alarm_irq_enable(&pdev->dev, 0); + + clk_disable(rtc->clk); +} + +static int sh_rtc_suspend(struct device *dev) +{ + struct sh_rtc *rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + irq_set_irq_wake(rtc->alarm_irq, 1); + + return 0; +} + +static int sh_rtc_resume(struct device *dev) +{ + struct sh_rtc *rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + irq_set_irq_wake(rtc->alarm_irq, 0); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(sh_rtc_pm_ops, sh_rtc_suspend, sh_rtc_resume); + +static const struct of_device_id sh_rtc_of_match[] = { + { .compatible = "renesas,sh-rtc", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sh_rtc_of_match); + +/* + * sh_rtc_remove() lives in .exit.text. For drivers registered via + * module_platform_driver_probe() this is ok because they cannot get unbound at + * runtime. So mark the driver struct with __refdata to prevent modpost + * triggering a section mismatch warning. + */ +static struct platform_driver sh_rtc_platform_driver __refdata = { + .driver = { + .name = DRV_NAME, + .pm = pm_sleep_ptr(&sh_rtc_pm_ops), + .of_match_table = sh_rtc_of_match, + }, + .remove = __exit_p(sh_rtc_remove), +}; + +module_platform_driver_probe(sh_rtc_platform_driver, sh_rtc_probe); + +MODULE_DESCRIPTION("SuperH on-chip RTC driver"); +MODULE_AUTHOR("Paul Mundt "); +MODULE_AUTHOR("Jamie Lenehan "); +MODULE_AUTHOR("Angelo Castello "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c new file mode 100644 index 000000000..d82acf1af --- /dev/null +++ b/drivers/rtc/rtc-snvs.c @@ -0,0 +1,446 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright (C) 2011-2012 Freescale Semiconductor, Inc. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SNVS_LPREGISTER_OFFSET 0x34 + +/* These register offsets are relative to LP (Low Power) range */ +#define SNVS_LPCR 0x04 +#define SNVS_LPSR 0x18 +#define SNVS_LPSRTCMR 0x1c +#define SNVS_LPSRTCLR 0x20 +#define SNVS_LPTAR 0x24 +#define SNVS_LPPGDR 0x30 + +#define SNVS_LPCR_SRTC_ENV (1 << 0) +#define SNVS_LPCR_LPTA_EN (1 << 1) +#define SNVS_LPCR_LPWUI_EN (1 << 3) +#define SNVS_LPSR_LPTA (1 << 0) + +#define SNVS_LPPGDR_INIT 0x41736166 +#define CNTR_TO_SECS_SH 15 + +/* The maximum RTC clock cycles that are allowed to pass between two + * consecutive clock counter register reads. If the values are corrupted a + * bigger difference is expected. The RTC frequency is 32kHz. With 320 cycles + * we end at 10ms which should be enough for most cases. If it once takes + * longer than expected we do a retry. + */ +#define MAX_RTC_READ_DIFF_CYCLES 320 + +struct snvs_rtc_data { + struct rtc_device *rtc; + struct regmap *regmap; + int offset; + int irq; + struct clk *clk; +}; + +/* Read 64 bit timer register, which could be in inconsistent state */ +static u64 rtc_read_lpsrt(struct snvs_rtc_data *data) +{ + u32 msb, lsb; + + regmap_read(data->regmap, data->offset + SNVS_LPSRTCMR, &msb); + regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &lsb); + return (u64)msb << 32 | lsb; +} + +/* Read the secure real time counter, taking care to deal with the cases of the + * counter updating while being read. + */ +static u32 rtc_read_lp_counter(struct snvs_rtc_data *data) +{ + u64 read1, read2; + s64 diff; + unsigned int timeout = 100; + + /* As expected, the registers might update between the read of the LSB + * reg and the MSB reg. It's also possible that one register might be + * in partially modified state as well. + */ + read1 = rtc_read_lpsrt(data); + do { + read2 = read1; + read1 = rtc_read_lpsrt(data); + diff = read1 - read2; + } while (((diff < 0) || (diff > MAX_RTC_READ_DIFF_CYCLES)) && --timeout); + if (!timeout) + dev_err(&data->rtc->dev, "Timeout trying to get valid LPSRT Counter read\n"); + + /* Convert 47-bit counter to 32-bit raw second count */ + return (u32) (read1 >> CNTR_TO_SECS_SH); +} + +/* Just read the lsb from the counter, dealing with inconsistent state */ +static int rtc_read_lp_counter_lsb(struct snvs_rtc_data *data, u32 *lsb) +{ + u32 count1, count2; + s32 diff; + unsigned int timeout = 100; + + regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count1); + do { + count2 = count1; + regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count1); + diff = count1 - count2; + } while (((diff < 0) || (diff > MAX_RTC_READ_DIFF_CYCLES)) && --timeout); + if (!timeout) { + dev_err(&data->rtc->dev, "Timeout trying to get valid LPSRT Counter read\n"); + return -ETIMEDOUT; + } + + *lsb = count1; + return 0; +} + +static int rtc_write_sync_lp(struct snvs_rtc_data *data) +{ + u32 count1, count2; + u32 elapsed; + unsigned int timeout = 1000; + int ret; + + ret = rtc_read_lp_counter_lsb(data, &count1); + if (ret) + return ret; + + /* Wait for 3 CKIL cycles, about 61.0-91.5 µs */ + do { + ret = rtc_read_lp_counter_lsb(data, &count2); + if (ret) + return ret; + elapsed = count2 - count1; /* wrap around _is_ handled! */ + } while (elapsed < 3 && --timeout); + if (!timeout) { + dev_err(&data->rtc->dev, "Timeout waiting for LPSRT Counter to change\n"); + return -ETIMEDOUT; + } + return 0; +} + +static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable) +{ + int timeout = 1000; + u32 lpcr; + + regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, SNVS_LPCR_SRTC_ENV, + enable ? SNVS_LPCR_SRTC_ENV : 0); + + while (--timeout) { + regmap_read(data->regmap, data->offset + SNVS_LPCR, &lpcr); + + if (enable) { + if (lpcr & SNVS_LPCR_SRTC_ENV) + break; + } else { + if (!(lpcr & SNVS_LPCR_SRTC_ENV)) + break; + } + } + + if (!timeout) + return -ETIMEDOUT; + + return 0; +} + +static int snvs_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct snvs_rtc_data *data = dev_get_drvdata(dev); + unsigned long time; + int ret; + + ret = clk_enable(data->clk); + if (ret) + return ret; + + time = rtc_read_lp_counter(data); + rtc_time64_to_tm(time, tm); + + clk_disable(data->clk); + + return 0; +} + +static int snvs_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct snvs_rtc_data *data = dev_get_drvdata(dev); + unsigned long time = rtc_tm_to_time64(tm); + int ret; + + ret = clk_enable(data->clk); + if (ret) + return ret; + + /* Disable RTC first */ + ret = snvs_rtc_enable(data, false); + if (ret) + return ret; + + /* Write 32-bit time to 47-bit timer, leaving 15 LSBs blank */ + regmap_write(data->regmap, data->offset + SNVS_LPSRTCLR, time << CNTR_TO_SECS_SH); + regmap_write(data->regmap, data->offset + SNVS_LPSRTCMR, time >> (32 - CNTR_TO_SECS_SH)); + + /* Enable RTC again */ + ret = snvs_rtc_enable(data, true); + + clk_disable(data->clk); + + return ret; +} + +static int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct snvs_rtc_data *data = dev_get_drvdata(dev); + u32 lptar, lpsr; + int ret; + + ret = clk_enable(data->clk); + if (ret) + return ret; + + regmap_read(data->regmap, data->offset + SNVS_LPTAR, &lptar); + rtc_time64_to_tm(lptar, &alrm->time); + + regmap_read(data->regmap, data->offset + SNVS_LPSR, &lpsr); + alrm->pending = (lpsr & SNVS_LPSR_LPTA) ? 1 : 0; + + clk_disable(data->clk); + + return 0; +} + +static int snvs_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) +{ + struct snvs_rtc_data *data = dev_get_drvdata(dev); + int ret; + + ret = clk_enable(data->clk); + if (ret) + return ret; + + regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, + (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN), + enable ? (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN) : 0); + + ret = rtc_write_sync_lp(data); + + clk_disable(data->clk); + + return ret; +} + +static int snvs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct snvs_rtc_data *data = dev_get_drvdata(dev); + unsigned long time = rtc_tm_to_time64(&alrm->time); + int ret; + + ret = clk_enable(data->clk); + if (ret) + return ret; + + regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, SNVS_LPCR_LPTA_EN, 0); + ret = rtc_write_sync_lp(data); + if (ret) + return ret; + regmap_write(data->regmap, data->offset + SNVS_LPTAR, time); + + /* Clear alarm interrupt status bit */ + regmap_write(data->regmap, data->offset + SNVS_LPSR, SNVS_LPSR_LPTA); + + clk_disable(data->clk); + + return snvs_rtc_alarm_irq_enable(dev, alrm->enabled); +} + +static const struct rtc_class_ops snvs_rtc_ops = { + .read_time = snvs_rtc_read_time, + .set_time = snvs_rtc_set_time, + .read_alarm = snvs_rtc_read_alarm, + .set_alarm = snvs_rtc_set_alarm, + .alarm_irq_enable = snvs_rtc_alarm_irq_enable, +}; + +static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id) +{ + struct device *dev = dev_id; + struct snvs_rtc_data *data = dev_get_drvdata(dev); + u32 lpsr; + u32 events = 0; + + clk_enable(data->clk); + + regmap_read(data->regmap, data->offset + SNVS_LPSR, &lpsr); + + if (lpsr & SNVS_LPSR_LPTA) { + events |= (RTC_AF | RTC_IRQF); + + /* RTC alarm should be one-shot */ + snvs_rtc_alarm_irq_enable(dev, 0); + + rtc_update_irq(data->rtc, 1, events); + } + + /* clear interrupt status */ + regmap_write(data->regmap, data->offset + SNVS_LPSR, lpsr); + + clk_disable(data->clk); + + return events ? IRQ_HANDLED : IRQ_NONE; +} + +static const struct regmap_config snvs_rtc_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +static void snvs_rtc_action(void *data) +{ + clk_disable_unprepare(data); +} + +static int snvs_rtc_probe(struct platform_device *pdev) +{ + struct snvs_rtc_data *data; + int ret; + void __iomem *mmio; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(data->rtc)) + return PTR_ERR(data->rtc); + + data->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "regmap"); + + if (IS_ERR(data->regmap)) { + dev_warn(&pdev->dev, "snvs rtc: you use old dts file, please update it\n"); + + mmio = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mmio)) + return PTR_ERR(mmio); + + data->regmap = devm_regmap_init_mmio(&pdev->dev, mmio, &snvs_rtc_config); + } else { + data->offset = SNVS_LPREGISTER_OFFSET; + of_property_read_u32(pdev->dev.of_node, "offset", &data->offset); + } + + if (IS_ERR(data->regmap)) { + dev_err(&pdev->dev, "Can't find snvs syscon\n"); + return -ENODEV; + } + + data->irq = platform_get_irq(pdev, 0); + if (data->irq < 0) + return data->irq; + + data->clk = devm_clk_get(&pdev->dev, "snvs-rtc"); + if (IS_ERR(data->clk)) { + data->clk = NULL; + } else { + ret = clk_prepare_enable(data->clk); + if (ret) { + dev_err(&pdev->dev, + "Could not prepare or enable the snvs clock\n"); + return ret; + } + } + + ret = devm_add_action_or_reset(&pdev->dev, snvs_rtc_action, data->clk); + if (ret) + return ret; + + platform_set_drvdata(pdev, data); + + /* Initialize glitch detect */ + regmap_write(data->regmap, data->offset + SNVS_LPPGDR, SNVS_LPPGDR_INIT); + + /* Clear interrupt status */ + regmap_write(data->regmap, data->offset + SNVS_LPSR, 0xffffffff); + + /* Enable RTC */ + ret = snvs_rtc_enable(data, true); + if (ret) { + dev_err(&pdev->dev, "failed to enable rtc %d\n", ret); + return ret; + } + + device_init_wakeup(&pdev->dev, true); + ret = dev_pm_set_wake_irq(&pdev->dev, data->irq); + if (ret) + dev_err(&pdev->dev, "failed to enable irq wake\n"); + + ret = devm_request_irq(&pdev->dev, data->irq, snvs_rtc_irq_handler, + IRQF_SHARED, "rtc alarm", &pdev->dev); + if (ret) { + dev_err(&pdev->dev, "failed to request irq %d: %d\n", + data->irq, ret); + return ret; + } + + data->rtc->ops = &snvs_rtc_ops; + data->rtc->range_max = U32_MAX; + + return devm_rtc_register_device(data->rtc); +} + +static int __maybe_unused snvs_rtc_suspend_noirq(struct device *dev) +{ + struct snvs_rtc_data *data = dev_get_drvdata(dev); + + clk_disable(data->clk); + + return 0; +} + +static int __maybe_unused snvs_rtc_resume_noirq(struct device *dev) +{ + struct snvs_rtc_data *data = dev_get_drvdata(dev); + + if (data->clk) + return clk_enable(data->clk); + + return 0; +} + +static const struct dev_pm_ops snvs_rtc_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(snvs_rtc_suspend_noirq, snvs_rtc_resume_noirq) +}; + +static const struct of_device_id snvs_dt_ids[] = { + { .compatible = "fsl,sec-v4.0-mon-rtc-lp", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, snvs_dt_ids); + +static struct platform_driver snvs_rtc_driver = { + .driver = { + .name = "snvs_rtc", + .pm = &snvs_rtc_pm_ops, + .of_match_table = snvs_dt_ids, + }, + .probe = snvs_rtc_probe, +}; +module_platform_driver(snvs_rtc_driver); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("Freescale SNVS RTC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-spacemit-p1.c b/drivers/rtc/rtc-spacemit-p1.c new file mode 100644 index 000000000..1de7bd995 --- /dev/null +++ b/drivers/rtc/rtc-spacemit-p1.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the RTC found in the SpacemiT P1 PMIC + * + * Copyright (C) 2025 by RISCstar Solutions Corporation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include + +#define MOD_NAME "spacemit-p1-rtc" + +/* + * Six consecutive 1-byte registers hold the seconds, minutes, hours, + * day-of-month, month, and year (respectively). + * + * The range of values in these registers is: + * seconds 0-59 + * minutes 0-59 + * hours 0-59 + * day 0-30 (struct tm is 1-31) + * month 0-11 + * year years since 2000 (struct tm is since 1900) + * + * Note that the day and month must be converted after reading and + * before writing. + */ +#define RTC_TIME 0x0d /* Offset of the seconds register */ + +#define RTC_CTRL 0x1d +#define RTC_EN BIT(2) + +/* Number of attempts to read a consistent time stamp before giving up */ +#define RTC_READ_TRIES 20 /* At least 1 */ + +struct p1_rtc { + struct regmap *regmap; + struct rtc_device *rtc; +}; + +/* + * The P1 hardware documentation states that the register values are + * latched to ensure a consistent time snapshot within the registers, + * but these are in fact unstable due to a bug in the hardware design. + * So we loop until we get two identical readings. + */ +static int p1_rtc_read_time(struct device *dev, struct rtc_time *t) +{ + struct p1_rtc *p1 = dev_get_drvdata(dev); + struct regmap *regmap = p1->regmap; + u32 count = RTC_READ_TRIES; + u8 seconds; + u8 time[6]; + int ret; + + ret = regmap_test_bits(regmap, RTC_CTRL, RTC_EN); + if (ret <= 0) + return ret ?: -EINVAL; /* RTC is disabled or error */ + + ret = regmap_bulk_read(regmap, RTC_TIME, time, sizeof(time)); + if (ret) + return ret; + + do { + seconds = time[0]; + ret = regmap_bulk_read(regmap, RTC_TIME, time, sizeof(time)); + if (ret) + return ret; + } while (time[0] != seconds && --count); + + if (!count) + return -EIO; /* Unable to get a consistent result */ + + t->tm_sec = time[0] & GENMASK(5, 0); + t->tm_min = time[1] & GENMASK(5, 0); + t->tm_hour = time[2] & GENMASK(4, 0); + t->tm_mday = (time[3] & GENMASK(4, 0)) + 1; + t->tm_mon = time[4] & GENMASK(3, 0); + t->tm_year = (time[5] & GENMASK(5, 0)) + 100; + + return 0; +} + +/* + * The P1 hardware documentation states that values in the registers are + * latched so when written they represent a consistent time snapshot. + * Nevertheless, this is not guaranteed by the implementation, so we must + * disable the RTC while updating it. + */ +static int p1_rtc_set_time(struct device *dev, struct rtc_time *t) +{ + struct p1_rtc *p1 = dev_get_drvdata(dev); + struct regmap *regmap = p1->regmap; + u8 time[6]; + int ret; + + time[0] = t->tm_sec; + time[1] = t->tm_min; + time[2] = t->tm_hour; + time[3] = t->tm_mday - 1; + time[4] = t->tm_mon; + time[5] = t->tm_year - 100; + + /* Disable the RTC to update; re-enable again when done */ + ret = regmap_clear_bits(regmap, RTC_CTRL, RTC_EN); + if (ret) + return ret; + + /* If something goes wrong, leave the RTC disabled */ + ret = regmap_bulk_write(regmap, RTC_TIME, time, sizeof(time)); + if (ret) + return ret; + + return regmap_set_bits(regmap, RTC_CTRL, RTC_EN); +} + +static const struct rtc_class_ops p1_rtc_class_ops = { + .read_time = p1_rtc_read_time, + .set_time = p1_rtc_set_time, +}; + +static int p1_rtc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rtc_device *rtc; + struct p1_rtc *p1; + + p1 = devm_kzalloc(dev, sizeof(*p1), GFP_KERNEL); + if (!p1) + return -ENOMEM; + dev_set_drvdata(dev, p1); + + p1->regmap = dev_get_regmap(dev->parent, NULL); + if (!p1->regmap) + return dev_err_probe(dev, -ENODEV, "failed to get regmap\n"); + + rtc = devm_rtc_allocate_device(dev); + if (IS_ERR(rtc)) + return dev_err_probe(dev, PTR_ERR(rtc), + "error allocating device\n"); + p1->rtc = rtc; + + rtc->ops = &p1_rtc_class_ops; + rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rtc->range_max = RTC_TIMESTAMP_END_2063; + + clear_bit(RTC_FEATURE_ALARM, rtc->features); + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features); + + return devm_rtc_register_device(rtc); +} + +static struct platform_driver p1_rtc_driver = { + .probe = p1_rtc_probe, + .driver = { + .name = MOD_NAME, + }, +}; + +module_platform_driver(p1_rtc_driver); + +MODULE_DESCRIPTION("SpacemiT P1 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" MOD_NAME); diff --git a/drivers/rtc/rtc-spear.c b/drivers/rtc/rtc-spear.c new file mode 100644 index 000000000..959acff8f --- /dev/null +++ b/drivers/rtc/rtc-spear.c @@ -0,0 +1,492 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * drivers/rtc/rtc-spear.c + * + * Copyright (C) 2010 ST Microelectronics + * Rajeev Kumar + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* RTC registers */ +#define TIME_REG 0x00 +#define DATE_REG 0x04 +#define ALARM_TIME_REG 0x08 +#define ALARM_DATE_REG 0x0C +#define CTRL_REG 0x10 +#define STATUS_REG 0x14 + +/* TIME_REG & ALARM_TIME_REG */ +#define SECONDS_UNITS (0xf<<0) /* seconds units position */ +#define SECONDS_TENS (0x7<<4) /* seconds tens position */ +#define MINUTES_UNITS (0xf<<8) /* minutes units position */ +#define MINUTES_TENS (0x7<<12) /* minutes tens position */ +#define HOURS_UNITS (0xf<<16) /* hours units position */ +#define HOURS_TENS (0x3<<20) /* hours tens position */ + +/* DATE_REG & ALARM_DATE_REG */ +#define DAYS_UNITS (0xf<<0) /* days units position */ +#define DAYS_TENS (0x3<<4) /* days tens position */ +#define MONTHS_UNITS (0xf<<8) /* months units position */ +#define MONTHS_TENS (0x1<<12) /* months tens position */ +#define YEARS_UNITS (0xf<<16) /* years units position */ +#define YEARS_TENS (0xf<<20) /* years tens position */ +#define YEARS_HUNDREDS (0xf<<24) /* years hundereds position */ +#define YEARS_MILLENIUMS (0xf<<28) /* years millenium position */ + +/* MASK SHIFT TIME_REG & ALARM_TIME_REG*/ +#define SECOND_SHIFT 0x00 /* seconds units */ +#define MINUTE_SHIFT 0x08 /* minutes units position */ +#define HOUR_SHIFT 0x10 /* hours units position */ +#define MDAY_SHIFT 0x00 /* Month day shift */ +#define MONTH_SHIFT 0x08 /* Month shift */ +#define YEAR_SHIFT 0x10 /* Year shift */ + +#define SECOND_MASK 0x7F +#define MIN_MASK 0x7F +#define HOUR_MASK 0x3F +#define DAY_MASK 0x3F +#define MONTH_MASK 0x7F +#define YEAR_MASK 0xFFFF + +/* date reg equal to time reg, for debug only */ +#define TIME_BYP (1<<9) +#define INT_ENABLE (1<<31) /* interrupt enable */ + +/* STATUS_REG */ +#define CLK_UNCONNECTED (1<<0) +#define PEND_WR_TIME (1<<2) +#define PEND_WR_DATE (1<<3) +#define LOST_WR_TIME (1<<4) +#define LOST_WR_DATE (1<<5) +#define RTC_INT_MASK (1<<31) +#define STATUS_BUSY (PEND_WR_TIME | PEND_WR_DATE) +#define STATUS_FAIL (LOST_WR_TIME | LOST_WR_DATE) + +struct spear_rtc_config { + struct rtc_device *rtc; + struct clk *clk; + spinlock_t lock; + void __iomem *ioaddr; + unsigned int irq_wake; +}; + +static inline void spear_rtc_clear_interrupt(struct spear_rtc_config *config) +{ + unsigned int val; + unsigned long flags; + + spin_lock_irqsave(&config->lock, flags); + val = readl(config->ioaddr + STATUS_REG); + val |= RTC_INT_MASK; + writel(val, config->ioaddr + STATUS_REG); + spin_unlock_irqrestore(&config->lock, flags); +} + +static inline void spear_rtc_enable_interrupt(struct spear_rtc_config *config) +{ + unsigned int val; + + val = readl(config->ioaddr + CTRL_REG); + if (!(val & INT_ENABLE)) { + spear_rtc_clear_interrupt(config); + val |= INT_ENABLE; + writel(val, config->ioaddr + CTRL_REG); + } +} + +static inline void spear_rtc_disable_interrupt(struct spear_rtc_config *config) +{ + unsigned int val; + + val = readl(config->ioaddr + CTRL_REG); + if (val & INT_ENABLE) { + val &= ~INT_ENABLE; + writel(val, config->ioaddr + CTRL_REG); + } +} + +static inline int is_write_complete(struct spear_rtc_config *config) +{ + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&config->lock, flags); + if ((readl(config->ioaddr + STATUS_REG)) & STATUS_FAIL) + ret = -EIO; + spin_unlock_irqrestore(&config->lock, flags); + + return ret; +} + +static void rtc_wait_not_busy(struct spear_rtc_config *config) +{ + int status, count = 0; + unsigned long flags; + + /* Assuming BUSY may stay active for 80 msec) */ + for (count = 0; count < 80; count++) { + spin_lock_irqsave(&config->lock, flags); + status = readl(config->ioaddr + STATUS_REG); + spin_unlock_irqrestore(&config->lock, flags); + if ((status & STATUS_BUSY) == 0) + break; + /* check status busy, after each msec */ + msleep(1); + } +} + +static irqreturn_t spear_rtc_irq(int irq, void *dev_id) +{ + struct spear_rtc_config *config = dev_id; + unsigned long events = 0; + unsigned int irq_data; + + spin_lock(&config->lock); + irq_data = readl(config->ioaddr + STATUS_REG); + spin_unlock(&config->lock); + + if ((irq_data & RTC_INT_MASK)) { + spear_rtc_clear_interrupt(config); + events = RTC_IRQF | RTC_AF; + rtc_update_irq(config->rtc, 1, events); + return IRQ_HANDLED; + } else + return IRQ_NONE; + +} + +static void tm2bcd(struct rtc_time *tm) +{ + tm->tm_sec = bin2bcd(tm->tm_sec); + tm->tm_min = bin2bcd(tm->tm_min); + tm->tm_hour = bin2bcd(tm->tm_hour); + tm->tm_mday = bin2bcd(tm->tm_mday); + tm->tm_mon = bin2bcd(tm->tm_mon + 1); + tm->tm_year = bin2bcd(tm->tm_year); +} + +static void bcd2tm(struct rtc_time *tm) +{ + tm->tm_sec = bcd2bin(tm->tm_sec); + tm->tm_min = bcd2bin(tm->tm_min); + tm->tm_hour = bcd2bin(tm->tm_hour); + tm->tm_mday = bcd2bin(tm->tm_mday); + tm->tm_mon = bcd2bin(tm->tm_mon) - 1; + /* epoch == 1900 */ + tm->tm_year = bcd2bin(tm->tm_year); +} + +/* + * spear_rtc_read_time - set the time + * @dev: rtc device in use + * @tm: holds date and time + * + * This function read time and date. On success it will return 0 + * otherwise -ve error is returned. + */ +static int spear_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct spear_rtc_config *config = dev_get_drvdata(dev); + unsigned int time, date; + + /* we don't report wday/yday/isdst ... */ + rtc_wait_not_busy(config); + + do { + time = readl(config->ioaddr + TIME_REG); + date = readl(config->ioaddr + DATE_REG); + } while (time == readl(config->ioaddr + TIME_REG)); + tm->tm_sec = (time >> SECOND_SHIFT) & SECOND_MASK; + tm->tm_min = (time >> MINUTE_SHIFT) & MIN_MASK; + tm->tm_hour = (time >> HOUR_SHIFT) & HOUR_MASK; + tm->tm_mday = (date >> MDAY_SHIFT) & DAY_MASK; + tm->tm_mon = (date >> MONTH_SHIFT) & MONTH_MASK; + tm->tm_year = (date >> YEAR_SHIFT) & YEAR_MASK; + + bcd2tm(tm); + return 0; +} + +/* + * spear_rtc_set_time - set the time + * @dev: rtc device in use + * @tm: holds date and time + * + * This function set time and date. On success it will return 0 + * otherwise -ve error is returned. + */ +static int spear_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct spear_rtc_config *config = dev_get_drvdata(dev); + unsigned int time, date; + + tm2bcd(tm); + + rtc_wait_not_busy(config); + time = (tm->tm_sec << SECOND_SHIFT) | (tm->tm_min << MINUTE_SHIFT) | + (tm->tm_hour << HOUR_SHIFT); + date = (tm->tm_mday << MDAY_SHIFT) | (tm->tm_mon << MONTH_SHIFT) | + (tm->tm_year << YEAR_SHIFT); + writel(time, config->ioaddr + TIME_REG); + writel(date, config->ioaddr + DATE_REG); + + return is_write_complete(config); +} + +/* + * spear_rtc_read_alarm - read the alarm time + * @dev: rtc device in use + * @alm: holds alarm date and time + * + * This function read alarm time and date. On success it will return 0 + * otherwise -ve error is returned. + */ +static int spear_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct spear_rtc_config *config = dev_get_drvdata(dev); + unsigned int time, date; + + rtc_wait_not_busy(config); + + time = readl(config->ioaddr + ALARM_TIME_REG); + date = readl(config->ioaddr + ALARM_DATE_REG); + alm->time.tm_sec = (time >> SECOND_SHIFT) & SECOND_MASK; + alm->time.tm_min = (time >> MINUTE_SHIFT) & MIN_MASK; + alm->time.tm_hour = (time >> HOUR_SHIFT) & HOUR_MASK; + alm->time.tm_mday = (date >> MDAY_SHIFT) & DAY_MASK; + alm->time.tm_mon = (date >> MONTH_SHIFT) & MONTH_MASK; + alm->time.tm_year = (date >> YEAR_SHIFT) & YEAR_MASK; + + bcd2tm(&alm->time); + alm->enabled = readl(config->ioaddr + CTRL_REG) & INT_ENABLE; + + return 0; +} + +/* + * spear_rtc_set_alarm - set the alarm time + * @dev: rtc device in use + * @alm: holds alarm date and time + * + * This function set alarm time and date. On success it will return 0 + * otherwise -ve error is returned. + */ +static int spear_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct spear_rtc_config *config = dev_get_drvdata(dev); + unsigned int time, date; + int err; + + tm2bcd(&alm->time); + + rtc_wait_not_busy(config); + + time = (alm->time.tm_sec << SECOND_SHIFT) | (alm->time.tm_min << + MINUTE_SHIFT) | (alm->time.tm_hour << HOUR_SHIFT); + date = (alm->time.tm_mday << MDAY_SHIFT) | (alm->time.tm_mon << + MONTH_SHIFT) | (alm->time.tm_year << YEAR_SHIFT); + + writel(time, config->ioaddr + ALARM_TIME_REG); + writel(date, config->ioaddr + ALARM_DATE_REG); + err = is_write_complete(config); + if (err < 0) + return err; + + if (alm->enabled) + spear_rtc_enable_interrupt(config); + else + spear_rtc_disable_interrupt(config); + + return 0; +} + +static int spear_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct spear_rtc_config *config = dev_get_drvdata(dev); + int ret = 0; + + spear_rtc_clear_interrupt(config); + + switch (enabled) { + case 0: + /* alarm off */ + spear_rtc_disable_interrupt(config); + break; + case 1: + /* alarm on */ + spear_rtc_enable_interrupt(config); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct rtc_class_ops spear_rtc_ops = { + .read_time = spear_rtc_read_time, + .set_time = spear_rtc_set_time, + .read_alarm = spear_rtc_read_alarm, + .set_alarm = spear_rtc_set_alarm, + .alarm_irq_enable = spear_alarm_irq_enable, +}; + +static int spear_rtc_probe(struct platform_device *pdev) +{ + struct spear_rtc_config *config; + int status = 0; + int irq; + + config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL); + if (!config) + return -ENOMEM; + + config->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(config->rtc)) + return PTR_ERR(config->rtc); + + /* alarm irqs */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + status = devm_request_irq(&pdev->dev, irq, spear_rtc_irq, 0, pdev->name, + config); + if (status) { + dev_err(&pdev->dev, "Alarm interrupt IRQ%d already claimed\n", + irq); + return status; + } + + config->ioaddr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(config->ioaddr)) + return PTR_ERR(config->ioaddr); + + config->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(config->clk)) + return PTR_ERR(config->clk); + + status = clk_prepare_enable(config->clk); + if (status < 0) + return status; + + spin_lock_init(&config->lock); + platform_set_drvdata(pdev, config); + + config->rtc->ops = &spear_rtc_ops; + config->rtc->range_min = RTC_TIMESTAMP_BEGIN_0000; + config->rtc->range_max = RTC_TIMESTAMP_END_9999; + + status = devm_rtc_register_device(config->rtc); + if (status) + goto err_disable_clock; + + if (!device_can_wakeup(&pdev->dev)) + device_init_wakeup(&pdev->dev, true); + + return 0; + +err_disable_clock: + clk_disable_unprepare(config->clk); + + return status; +} + +static void spear_rtc_remove(struct platform_device *pdev) +{ + struct spear_rtc_config *config = platform_get_drvdata(pdev); + + spear_rtc_disable_interrupt(config); + clk_disable_unprepare(config->clk); + device_init_wakeup(&pdev->dev, false); +} + +#ifdef CONFIG_PM_SLEEP +static int spear_rtc_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct spear_rtc_config *config = platform_get_drvdata(pdev); + int irq; + + irq = platform_get_irq(pdev, 0); + if (device_may_wakeup(&pdev->dev)) { + if (!enable_irq_wake(irq)) + config->irq_wake = 1; + } else { + spear_rtc_disable_interrupt(config); + clk_disable(config->clk); + } + + return 0; +} + +static int spear_rtc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct spear_rtc_config *config = platform_get_drvdata(pdev); + int irq; + + irq = platform_get_irq(pdev, 0); + + if (device_may_wakeup(&pdev->dev)) { + if (config->irq_wake) { + disable_irq_wake(irq); + config->irq_wake = 0; + } + } else { + clk_enable(config->clk); + spear_rtc_enable_interrupt(config); + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(spear_rtc_pm_ops, spear_rtc_suspend, spear_rtc_resume); + +static void spear_rtc_shutdown(struct platform_device *pdev) +{ + struct spear_rtc_config *config = platform_get_drvdata(pdev); + + spear_rtc_disable_interrupt(config); + clk_disable(config->clk); +} + +#ifdef CONFIG_OF +static const struct of_device_id spear_rtc_id_table[] = { + { .compatible = "st,spear600-rtc" }, + {} +}; +MODULE_DEVICE_TABLE(of, spear_rtc_id_table); +#endif + +static struct platform_driver spear_rtc_driver = { + .probe = spear_rtc_probe, + .remove = spear_rtc_remove, + .shutdown = spear_rtc_shutdown, + .driver = { + .name = "rtc-spear", + .pm = &spear_rtc_pm_ops, + .of_match_table = of_match_ptr(spear_rtc_id_table), + }, +}; + +module_platform_driver(spear_rtc_driver); + +MODULE_ALIAS("platform:rtc-spear"); +MODULE_AUTHOR("Rajeev Kumar "); +MODULE_DESCRIPTION("ST SPEAr Realtime Clock Driver (RTC)"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-ssd202d.c b/drivers/rtc/rtc-ssd202d.c new file mode 100644 index 000000000..72aa02eb8 --- /dev/null +++ b/drivers/rtc/rtc-ssd202d.c @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Real time clocks driver for MStar/SigmaStar SSD202D SoCs. + * + * (C) 2021 Daniel Palmer + * (C) 2023 Romain Perier + */ + +#include +#include +#include +#include +#include +#include +#include + +#define REG_CTRL 0x0 +#define REG_CTRL1 0x4 +#define REG_ISO_CTRL 0xc +#define REG_WRDATA_L 0x10 +#define REG_WRDATA_H 0x14 +#define REG_ISOACK 0x20 +#define REG_RDDATA_L 0x24 +#define REG_RDDATA_H 0x28 +#define REG_RDCNT_L 0x30 +#define REG_RDCNT_H 0x34 +#define REG_CNT_TRIG 0x38 +#define REG_PWRCTRL 0x3c +#define REG_RTC_TEST 0x54 + +#define CNT_RD_TRIG_BIT BIT(0) +#define CNT_RD_BIT BIT(0) +#define BASE_WR_BIT BIT(1) +#define BASE_RD_BIT BIT(2) +#define CNT_RST_BIT BIT(3) +#define ISO_CTRL_ACK_MASK BIT(3) +#define ISO_CTRL_ACK_SHIFT 3 +#define SW0_WR_BIT BIT(5) +#define SW1_WR_BIT BIT(6) +#define SW0_RD_BIT BIT(7) +#define SW1_RD_BIT BIT(8) + +#define ISO_CTRL_MASK GENMASK(2, 0) + +struct ssd202d_rtc { + struct rtc_device *rtc_dev; + void __iomem *base; +}; + +static u8 read_iso_en(void __iomem *base) +{ + return readb(base + REG_RTC_TEST) & 0x1; +} + +static u8 read_iso_ctrl_ack(void __iomem *base) +{ + return (readb(base + REG_ISOACK) & ISO_CTRL_ACK_MASK) >> ISO_CTRL_ACK_SHIFT; +} + +static int ssd202d_rtc_isoctrl(struct ssd202d_rtc *priv) +{ + static const unsigned int sequence[] = { 0x0, 0x1, 0x3, 0x7, 0x5, 0x1, 0x0 }; + unsigned int val; + struct device *dev = &priv->rtc_dev->dev; + int i, ret; + + /* + * This gates iso_en by writing a special sequence of bytes to iso_ctrl + * and ensuring that it has been correctly applied by reading iso_ctrl_ack + */ + for (i = 0; i < ARRAY_SIZE(sequence); i++) { + writeb(sequence[i] & ISO_CTRL_MASK, priv->base + REG_ISO_CTRL); + + ret = read_poll_timeout(read_iso_ctrl_ack, val, val == (i % 2), 100, + 20 * 100, true, priv->base); + if (ret) { + dev_dbg(dev, "Timeout waiting for ack byte %i (%x) of sequence\n", i, + sequence[i]); + return ret; + } + } + + /* + * At this point iso_en should be raised for 1ms + */ + ret = read_poll_timeout(read_iso_en, val, val, 100, 22 * 100, true, priv->base); + if (ret) + dev_dbg(dev, "Timeout waiting for iso_en\n"); + mdelay(2); + return 0; +} + +static void ssd202d_rtc_read_reg(struct ssd202d_rtc *priv, unsigned int reg, + unsigned int field, unsigned int *base) +{ + unsigned int l, h; + u16 val; + + /* Ask for the content of an RTC value into RDDATA by gating iso_en, + * then iso_en is gated and the content of RDDATA can be read + */ + val = readw(priv->base + reg); + writew(val | field, priv->base + reg); + ssd202d_rtc_isoctrl(priv); + writew(val & ~field, priv->base + reg); + + l = readw(priv->base + REG_RDDATA_L); + h = readw(priv->base + REG_RDDATA_H); + + *base = (h << 16) | l; +} + +static void ssd202d_rtc_write_reg(struct ssd202d_rtc *priv, unsigned int reg, + unsigned int field, u32 base) +{ + u16 val; + + /* Set the content of an RTC value from WRDATA by gating iso_en */ + val = readw(priv->base + reg); + writew(val | field, priv->base + reg); + writew(base, priv->base + REG_WRDATA_L); + writew(base >> 16, priv->base + REG_WRDATA_H); + ssd202d_rtc_isoctrl(priv); + writew(val & ~field, priv->base + reg); +} + +static int ssd202d_rtc_read_counter(struct ssd202d_rtc *priv, unsigned int *counter) +{ + unsigned int l, h; + u16 val; + + val = readw(priv->base + REG_CTRL1); + writew(val | CNT_RD_BIT, priv->base + REG_CTRL1); + ssd202d_rtc_isoctrl(priv); + writew(val & ~CNT_RD_BIT, priv->base + REG_CTRL1); + + val = readw(priv->base + REG_CTRL1); + writew(val | CNT_RD_TRIG_BIT, priv->base + REG_CNT_TRIG); + writew(val & ~CNT_RD_TRIG_BIT, priv->base + REG_CNT_TRIG); + + l = readw(priv->base + REG_RDCNT_L); + h = readw(priv->base + REG_RDCNT_H); + + *counter = (h << 16) | l; + + return 0; +} + +static int ssd202d_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct ssd202d_rtc *priv = dev_get_drvdata(dev); + unsigned int sw0, base, counter; + u32 seconds; + int ret; + + /* Check that RTC is enabled by SW */ + ssd202d_rtc_read_reg(priv, REG_CTRL, SW0_RD_BIT, &sw0); + if (sw0 != 1) + return -EINVAL; + + /* Get RTC base value from RDDATA */ + ssd202d_rtc_read_reg(priv, REG_CTRL, BASE_RD_BIT, &base); + /* Get RTC counter value from RDDATA */ + ret = ssd202d_rtc_read_counter(priv, &counter); + if (ret) + return ret; + + seconds = base + counter; + + rtc_time64_to_tm(seconds, tm); + + return 0; +} + +static int ssd202d_rtc_reset_counter(struct ssd202d_rtc *priv) +{ + u16 val; + + val = readw(priv->base + REG_CTRL); + writew(val | CNT_RST_BIT, priv->base + REG_CTRL); + ssd202d_rtc_isoctrl(priv); + writew(val & ~CNT_RST_BIT, priv->base + REG_CTRL); + ssd202d_rtc_isoctrl(priv); + + return 0; +} + +static int ssd202d_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct ssd202d_rtc *priv = dev_get_drvdata(dev); + unsigned long seconds = rtc_tm_to_time64(tm); + + ssd202d_rtc_write_reg(priv, REG_CTRL, BASE_WR_BIT, seconds); + ssd202d_rtc_reset_counter(priv); + ssd202d_rtc_write_reg(priv, REG_CTRL, SW0_WR_BIT, 1); + + return 0; +} + +static const struct rtc_class_ops ssd202d_rtc_ops = { + .read_time = ssd202d_rtc_read_time, + .set_time = ssd202d_rtc_set_time, +}; + +static int ssd202d_rtc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ssd202d_rtc *priv; + + priv = devm_kzalloc(&pdev->dev, sizeof(struct ssd202d_rtc), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->rtc_dev = devm_rtc_allocate_device(dev); + if (IS_ERR(priv->rtc_dev)) + return PTR_ERR(priv->rtc_dev); + + priv->rtc_dev->ops = &ssd202d_rtc_ops; + priv->rtc_dev->range_max = U32_MAX; + + platform_set_drvdata(pdev, priv); + + return devm_rtc_register_device(priv->rtc_dev); +} + +static const struct of_device_id ssd202d_rtc_of_match_table[] = { + { .compatible = "mstar,ssd202d-rtc" }, + { } +}; +MODULE_DEVICE_TABLE(of, ssd202d_rtc_of_match_table); + +static struct platform_driver ssd202d_rtc_driver = { + .probe = ssd202d_rtc_probe, + .driver = { + .name = "ssd202d-rtc", + .of_match_table = ssd202d_rtc_of_match_table, + }, +}; +module_platform_driver(ssd202d_rtc_driver); + +MODULE_AUTHOR("Daniel Palmer "); +MODULE_AUTHOR("Romain Perier "); +MODULE_DESCRIPTION("MStar SSD202D RTC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-st-lpc.c b/drivers/rtc/rtc-st-lpc.c new file mode 100644 index 000000000..ae79ddf24 --- /dev/null +++ b/drivers/rtc/rtc-st-lpc.c @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * rtc-st-lpc.c - ST's LPC RTC, powered by the Low Power Timer + * + * Copyright (C) 2014 STMicroelectronics Limited + * + * Author: David Paris for STMicroelectronics + * Lee Jones for STMicroelectronics + * + * Based on the original driver written by Stuart Menefy. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Low Power Timer */ +#define LPC_LPT_LSB_OFF 0x400 +#define LPC_LPT_MSB_OFF 0x404 +#define LPC_LPT_START_OFF 0x408 + +/* Low Power Alarm */ +#define LPC_LPA_LSB_OFF 0x410 +#define LPC_LPA_MSB_OFF 0x414 +#define LPC_LPA_START_OFF 0x418 + +/* LPC as WDT */ +#define LPC_WDT_OFF 0x510 +#define LPC_WDT_FLAG_OFF 0x514 + +struct st_rtc { + struct rtc_device *rtc_dev; + struct rtc_wkalrm alarm; + struct clk *clk; + unsigned long clkrate; + void __iomem *ioaddr; + bool irq_enabled:1; + spinlock_t lock; + short irq; +}; + +static void st_rtc_set_hw_alarm(struct st_rtc *rtc, + unsigned long msb, unsigned long lsb) +{ + unsigned long flags; + + spin_lock_irqsave(&rtc->lock, flags); + + writel_relaxed(1, rtc->ioaddr + LPC_WDT_OFF); + + writel_relaxed(msb, rtc->ioaddr + LPC_LPA_MSB_OFF); + writel_relaxed(lsb, rtc->ioaddr + LPC_LPA_LSB_OFF); + writel_relaxed(1, rtc->ioaddr + LPC_LPA_START_OFF); + + writel_relaxed(0, rtc->ioaddr + LPC_WDT_OFF); + + spin_unlock_irqrestore(&rtc->lock, flags); +} + +static irqreturn_t st_rtc_handler(int this_irq, void *data) +{ + struct st_rtc *rtc = (struct st_rtc *)data; + + rtc_update_irq(rtc->rtc_dev, 1, RTC_AF); + + return IRQ_HANDLED; +} + +static int st_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct st_rtc *rtc = dev_get_drvdata(dev); + unsigned long lpt_lsb, lpt_msb; + unsigned long long lpt; + unsigned long flags; + + spin_lock_irqsave(&rtc->lock, flags); + + do { + lpt_msb = readl_relaxed(rtc->ioaddr + LPC_LPT_MSB_OFF); + lpt_lsb = readl_relaxed(rtc->ioaddr + LPC_LPT_LSB_OFF); + } while (readl_relaxed(rtc->ioaddr + LPC_LPT_MSB_OFF) != lpt_msb); + + spin_unlock_irqrestore(&rtc->lock, flags); + + lpt = ((unsigned long long)lpt_msb << 32) | lpt_lsb; + do_div(lpt, rtc->clkrate); + rtc_time64_to_tm(lpt, tm); + + return 0; +} + +static int st_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct st_rtc *rtc = dev_get_drvdata(dev); + unsigned long long lpt, secs; + unsigned long flags; + + secs = rtc_tm_to_time64(tm); + + lpt = (unsigned long long)secs * rtc->clkrate; + + spin_lock_irqsave(&rtc->lock, flags); + + writel_relaxed(lpt >> 32, rtc->ioaddr + LPC_LPT_MSB_OFF); + writel_relaxed(lpt, rtc->ioaddr + LPC_LPT_LSB_OFF); + writel_relaxed(1, rtc->ioaddr + LPC_LPT_START_OFF); + + spin_unlock_irqrestore(&rtc->lock, flags); + + return 0; +} + +static int st_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct st_rtc *rtc = dev_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&rtc->lock, flags); + + memcpy(wkalrm, &rtc->alarm, sizeof(struct rtc_wkalrm)); + + spin_unlock_irqrestore(&rtc->lock, flags); + + return 0; +} + +static int st_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct st_rtc *rtc = dev_get_drvdata(dev); + + if (enabled && !rtc->irq_enabled) { + enable_irq(rtc->irq); + rtc->irq_enabled = true; + } else if (!enabled && rtc->irq_enabled) { + disable_irq(rtc->irq); + rtc->irq_enabled = false; + } + + return 0; +} + +static int st_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct st_rtc *rtc = dev_get_drvdata(dev); + struct rtc_time now; + unsigned long long now_secs; + unsigned long long alarm_secs; + unsigned long long lpa; + + st_rtc_read_time(dev, &now); + now_secs = rtc_tm_to_time64(&now); + alarm_secs = rtc_tm_to_time64(&t->time); + + memcpy(&rtc->alarm, t, sizeof(struct rtc_wkalrm)); + + /* Now many secs to fire */ + alarm_secs -= now_secs; + lpa = (unsigned long long)alarm_secs * rtc->clkrate; + + st_rtc_set_hw_alarm(rtc, lpa >> 32, lpa); + st_rtc_alarm_irq_enable(dev, t->enabled); + + return 0; +} + +static const struct rtc_class_ops st_rtc_ops = { + .read_time = st_rtc_read_time, + .set_time = st_rtc_set_time, + .read_alarm = st_rtc_read_alarm, + .set_alarm = st_rtc_set_alarm, + .alarm_irq_enable = st_rtc_alarm_irq_enable, +}; + +static int st_rtc_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct st_rtc *rtc; + uint32_t mode; + int ret = 0; + + ret = of_property_read_u32(np, "st,lpc-mode", &mode); + if (ret) { + dev_err(&pdev->dev, "An LPC mode must be provided\n"); + return -EINVAL; + } + + /* LPC can either run as a Clocksource or in RTC or WDT mode */ + if (mode != ST_LPC_MODE_RTC) + return -ENODEV; + + rtc = devm_kzalloc(&pdev->dev, sizeof(struct st_rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc->rtc_dev)) + return PTR_ERR(rtc->rtc_dev); + + spin_lock_init(&rtc->lock); + + rtc->ioaddr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rtc->ioaddr)) + return PTR_ERR(rtc->ioaddr); + + rtc->irq = platform_get_irq(pdev, 0); + if (rtc->irq < 0) + return rtc->irq; + + ret = devm_request_irq(&pdev->dev, rtc->irq, st_rtc_handler, + IRQF_NO_AUTOEN, pdev->name, rtc); + if (ret) { + dev_err(&pdev->dev, "Failed to request irq %i\n", rtc->irq); + return ret; + } + + enable_irq_wake(rtc->irq); + + rtc->clk = devm_clk_get_enabled(&pdev->dev, NULL); + if (IS_ERR(rtc->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(rtc->clk), + "Unable to request clock\n"); + + rtc->clkrate = clk_get_rate(rtc->clk); + if (!rtc->clkrate) { + dev_err(&pdev->dev, "Unable to fetch clock rate\n"); + return -EINVAL; + } + + device_set_wakeup_capable(&pdev->dev, 1); + + platform_set_drvdata(pdev, rtc); + + rtc->rtc_dev->ops = &st_rtc_ops; + rtc->rtc_dev->range_max = U64_MAX; + do_div(rtc->rtc_dev->range_max, rtc->clkrate); + + ret = devm_rtc_register_device(rtc->rtc_dev); + if (ret) + return ret; + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int st_rtc_suspend(struct device *dev) +{ + struct st_rtc *rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + return 0; + + writel_relaxed(1, rtc->ioaddr + LPC_WDT_OFF); + writel_relaxed(0, rtc->ioaddr + LPC_LPA_START_OFF); + writel_relaxed(0, rtc->ioaddr + LPC_WDT_OFF); + + return 0; +} + +static int st_rtc_resume(struct device *dev) +{ + struct st_rtc *rtc = dev_get_drvdata(dev); + + rtc_alarm_irq_enable(rtc->rtc_dev, 0); + + /* + * clean 'rtc->alarm' to allow a new + * .set_alarm to the upper RTC layer + */ + memset(&rtc->alarm, 0, sizeof(struct rtc_wkalrm)); + + writel_relaxed(0, rtc->ioaddr + LPC_LPA_MSB_OFF); + writel_relaxed(0, rtc->ioaddr + LPC_LPA_LSB_OFF); + writel_relaxed(1, rtc->ioaddr + LPC_WDT_OFF); + writel_relaxed(1, rtc->ioaddr + LPC_LPA_START_OFF); + writel_relaxed(0, rtc->ioaddr + LPC_WDT_OFF); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(st_rtc_pm_ops, st_rtc_suspend, st_rtc_resume); + +static const struct of_device_id st_rtc_match[] = { + { .compatible = "st,stih407-lpc" }, + {} +}; +MODULE_DEVICE_TABLE(of, st_rtc_match); + +static struct platform_driver st_rtc_platform_driver = { + .driver = { + .name = "st-lpc-rtc", + .pm = &st_rtc_pm_ops, + .of_match_table = st_rtc_match, + }, + .probe = st_rtc_probe, +}; + +module_platform_driver(st_rtc_platform_driver); + +MODULE_DESCRIPTION("STMicroelectronics LPC RTC driver"); +MODULE_AUTHOR("David Paris "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-starfire.c b/drivers/rtc/rtc-starfire.c new file mode 100644 index 000000000..fbd1ed41c --- /dev/null +++ b/drivers/rtc/rtc-starfire.c @@ -0,0 +1,60 @@ +/* rtc-starfire.c: Starfire platform RTC driver. + * + * Author: David S. Miller + * License: GPL + * + * Copyright (C) 2008 David S. Miller + */ + +#include +#include +#include +#include + +#include + +static u32 starfire_get_time(void) +{ + static char obp_gettod[32]; + static u32 unix_tod; + + sprintf(obp_gettod, "h# %08x unix-gettod", + (unsigned int) (long) &unix_tod); + prom_feval(obp_gettod); + + return unix_tod; +} + +static int starfire_read_time(struct device *dev, struct rtc_time *tm) +{ + rtc_time64_to_tm(starfire_get_time(), tm); + return 0; +} + +static const struct rtc_class_ops starfire_rtc_ops = { + .read_time = starfire_read_time, +}; + +static int __init starfire_rtc_probe(struct platform_device *pdev) +{ + struct rtc_device *rtc; + + rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + rtc->ops = &starfire_rtc_ops; + rtc->range_max = U32_MAX; + + platform_set_drvdata(pdev, rtc); + + return devm_rtc_register_device(rtc); +} + +static struct platform_driver starfire_rtc_driver = { + .driver = { + .name = "rtc-starfire", + }, +}; + +builtin_platform_driver_probe(starfire_rtc_driver, starfire_rtc_probe); diff --git a/drivers/rtc/rtc-stk17ta8.c b/drivers/rtc/rtc-stk17ta8.c new file mode 100644 index 000000000..7cb6be1b7 --- /dev/null +++ b/drivers/rtc/rtc-stk17ta8.c @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * A RTC driver for the Simtek STK17TA8 + * + * By Thomas Hommel + * + * Based on the DS1553 driver from + * Atsushi Nemoto + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RTC_REG_SIZE 0x20000 +#define RTC_OFFSET 0x1fff0 + +#define RTC_FLAGS (RTC_OFFSET + 0) +#define RTC_CENTURY (RTC_OFFSET + 1) +#define RTC_SECONDS_ALARM (RTC_OFFSET + 2) +#define RTC_MINUTES_ALARM (RTC_OFFSET + 3) +#define RTC_HOURS_ALARM (RTC_OFFSET + 4) +#define RTC_DATE_ALARM (RTC_OFFSET + 5) +#define RTC_INTERRUPTS (RTC_OFFSET + 6) +#define RTC_WATCHDOG (RTC_OFFSET + 7) +#define RTC_CALIBRATION (RTC_OFFSET + 8) +#define RTC_SECONDS (RTC_OFFSET + 9) +#define RTC_MINUTES (RTC_OFFSET + 10) +#define RTC_HOURS (RTC_OFFSET + 11) +#define RTC_DAY (RTC_OFFSET + 12) +#define RTC_DATE (RTC_OFFSET + 13) +#define RTC_MONTH (RTC_OFFSET + 14) +#define RTC_YEAR (RTC_OFFSET + 15) + +#define RTC_SECONDS_MASK 0x7f +#define RTC_DAY_MASK 0x07 +#define RTC_CAL_MASK 0x3f + +/* Bits in the Calibration register */ +#define RTC_STOP 0x80 + +/* Bits in the Flags register */ +#define RTC_FLAGS_AF 0x40 +#define RTC_FLAGS_PF 0x20 +#define RTC_WRITE 0x02 +#define RTC_READ 0x01 + +/* Bits in the Interrupts register */ +#define RTC_INTS_AIE 0x40 + +struct rtc_plat_data { + struct rtc_device *rtc; + void __iomem *ioaddr; + unsigned long last_jiffies; + int irq; + unsigned int irqen; + int alrm_sec; + int alrm_min; + int alrm_hour; + int alrm_mday; + spinlock_t lock; +}; + +static int stk17ta8_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + void __iomem *ioaddr = pdata->ioaddr; + u8 flags; + + flags = readb(pdata->ioaddr + RTC_FLAGS); + writeb(flags | RTC_WRITE, pdata->ioaddr + RTC_FLAGS); + + writeb(bin2bcd(tm->tm_year % 100), ioaddr + RTC_YEAR); + writeb(bin2bcd(tm->tm_mon + 1), ioaddr + RTC_MONTH); + writeb(bin2bcd(tm->tm_wday) & RTC_DAY_MASK, ioaddr + RTC_DAY); + writeb(bin2bcd(tm->tm_mday), ioaddr + RTC_DATE); + writeb(bin2bcd(tm->tm_hour), ioaddr + RTC_HOURS); + writeb(bin2bcd(tm->tm_min), ioaddr + RTC_MINUTES); + writeb(bin2bcd(tm->tm_sec) & RTC_SECONDS_MASK, ioaddr + RTC_SECONDS); + writeb(bin2bcd((tm->tm_year + 1900) / 100), ioaddr + RTC_CENTURY); + + writeb(flags & ~RTC_WRITE, pdata->ioaddr + RTC_FLAGS); + return 0; +} + +static int stk17ta8_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + void __iomem *ioaddr = pdata->ioaddr; + unsigned int year, month, day, hour, minute, second, week; + unsigned int century; + u8 flags; + + /* give enough time to update RTC in case of continuous read */ + if (pdata->last_jiffies == jiffies) + msleep(1); + pdata->last_jiffies = jiffies; + + flags = readb(pdata->ioaddr + RTC_FLAGS); + writeb(flags | RTC_READ, ioaddr + RTC_FLAGS); + second = readb(ioaddr + RTC_SECONDS) & RTC_SECONDS_MASK; + minute = readb(ioaddr + RTC_MINUTES); + hour = readb(ioaddr + RTC_HOURS); + day = readb(ioaddr + RTC_DATE); + week = readb(ioaddr + RTC_DAY) & RTC_DAY_MASK; + month = readb(ioaddr + RTC_MONTH); + year = readb(ioaddr + RTC_YEAR); + century = readb(ioaddr + RTC_CENTURY); + writeb(flags & ~RTC_READ, ioaddr + RTC_FLAGS); + tm->tm_sec = bcd2bin(second); + tm->tm_min = bcd2bin(minute); + tm->tm_hour = bcd2bin(hour); + tm->tm_mday = bcd2bin(day); + tm->tm_wday = bcd2bin(week); + tm->tm_mon = bcd2bin(month) - 1; + /* year is 1900 + tm->tm_year */ + tm->tm_year = bcd2bin(year) + bcd2bin(century) * 100 - 1900; + + return 0; +} + +static void stk17ta8_rtc_update_alarm(struct rtc_plat_data *pdata) +{ + void __iomem *ioaddr = pdata->ioaddr; + unsigned long irqflags; + u8 flags; + + spin_lock_irqsave(&pdata->lock, irqflags); + + flags = readb(ioaddr + RTC_FLAGS); + writeb(flags | RTC_WRITE, ioaddr + RTC_FLAGS); + + writeb(pdata->alrm_mday < 0 || (pdata->irqen & RTC_UF) ? + 0x80 : bin2bcd(pdata->alrm_mday), + ioaddr + RTC_DATE_ALARM); + writeb(pdata->alrm_hour < 0 || (pdata->irqen & RTC_UF) ? + 0x80 : bin2bcd(pdata->alrm_hour), + ioaddr + RTC_HOURS_ALARM); + writeb(pdata->alrm_min < 0 || (pdata->irqen & RTC_UF) ? + 0x80 : bin2bcd(pdata->alrm_min), + ioaddr + RTC_MINUTES_ALARM); + writeb(pdata->alrm_sec < 0 || (pdata->irqen & RTC_UF) ? + 0x80 : bin2bcd(pdata->alrm_sec), + ioaddr + RTC_SECONDS_ALARM); + writeb(pdata->irqen ? RTC_INTS_AIE : 0, ioaddr + RTC_INTERRUPTS); + readb(ioaddr + RTC_FLAGS); /* clear interrupts */ + writeb(flags & ~RTC_WRITE, ioaddr + RTC_FLAGS); + spin_unlock_irqrestore(&pdata->lock, irqflags); +} + +static int stk17ta8_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + + if (pdata->irq <= 0) + return -EINVAL; + pdata->alrm_mday = alrm->time.tm_mday; + pdata->alrm_hour = alrm->time.tm_hour; + pdata->alrm_min = alrm->time.tm_min; + pdata->alrm_sec = alrm->time.tm_sec; + if (alrm->enabled) + pdata->irqen |= RTC_AF; + stk17ta8_rtc_update_alarm(pdata); + return 0; +} + +static int stk17ta8_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + + if (pdata->irq <= 0) + return -EINVAL; + alrm->time.tm_mday = pdata->alrm_mday < 0 ? 0 : pdata->alrm_mday; + alrm->time.tm_hour = pdata->alrm_hour < 0 ? 0 : pdata->alrm_hour; + alrm->time.tm_min = pdata->alrm_min < 0 ? 0 : pdata->alrm_min; + alrm->time.tm_sec = pdata->alrm_sec < 0 ? 0 : pdata->alrm_sec; + alrm->enabled = (pdata->irqen & RTC_AF) ? 1 : 0; + return 0; +} + +static irqreturn_t stk17ta8_rtc_interrupt(int irq, void *dev_id) +{ + struct platform_device *pdev = dev_id; + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + unsigned long events = 0; + + spin_lock(&pdata->lock); + /* read and clear interrupt */ + if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_AF) { + events = RTC_IRQF; + if (readb(ioaddr + RTC_SECONDS_ALARM) & 0x80) + events |= RTC_UF; + else + events |= RTC_AF; + rtc_update_irq(pdata->rtc, 1, events); + } + spin_unlock(&pdata->lock); + return events ? IRQ_HANDLED : IRQ_NONE; +} + +static int stk17ta8_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + + if (pdata->irq <= 0) + return -EINVAL; + if (enabled) + pdata->irqen |= RTC_AF; + else + pdata->irqen &= ~RTC_AF; + stk17ta8_rtc_update_alarm(pdata); + return 0; +} + +static const struct rtc_class_ops stk17ta8_rtc_ops = { + .read_time = stk17ta8_rtc_read_time, + .set_time = stk17ta8_rtc_set_time, + .read_alarm = stk17ta8_rtc_read_alarm, + .set_alarm = stk17ta8_rtc_set_alarm, + .alarm_irq_enable = stk17ta8_rtc_alarm_irq_enable, +}; + +static int stk17ta8_nvram_read(void *priv, unsigned int pos, void *val, + size_t bytes) +{ + struct rtc_plat_data *pdata = priv; + void __iomem *ioaddr = pdata->ioaddr; + u8 *buf = val; + + for (; bytes; bytes--) + *buf++ = readb(ioaddr + pos++); + return 0; +} + +static int stk17ta8_nvram_write(void *priv, unsigned int pos, void *val, + size_t bytes) +{ + struct rtc_plat_data *pdata = priv; + void __iomem *ioaddr = pdata->ioaddr; + u8 *buf = val; + + for (; bytes; bytes--) + writeb(*buf++, ioaddr + pos++); + return 0; +} + +static int stk17ta8_rtc_probe(struct platform_device *pdev) +{ + unsigned int cal; + unsigned int flags; + struct rtc_plat_data *pdata; + void __iomem *ioaddr; + int ret = 0; + struct nvmem_config nvmem_cfg = { + .name = "stk17ta8_nvram", + .word_size = 1, + .stride = 1, + .size = RTC_OFFSET, + .reg_read = stk17ta8_nvram_read, + .reg_write = stk17ta8_nvram_write, + }; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + ioaddr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ioaddr)) + return PTR_ERR(ioaddr); + pdata->ioaddr = ioaddr; + pdata->irq = platform_get_irq(pdev, 0); + + /* turn RTC on if it was not on */ + cal = readb(ioaddr + RTC_CALIBRATION); + if (cal & RTC_STOP) { + cal &= RTC_CAL_MASK; + flags = readb(ioaddr + RTC_FLAGS); + writeb(flags | RTC_WRITE, ioaddr + RTC_FLAGS); + writeb(cal, ioaddr + RTC_CALIBRATION); + writeb(flags & ~RTC_WRITE, ioaddr + RTC_FLAGS); + } + if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_PF) + dev_warn(&pdev->dev, "voltage-low detected.\n"); + + spin_lock_init(&pdata->lock); + pdata->last_jiffies = jiffies; + platform_set_drvdata(pdev, pdata); + if (pdata->irq > 0) { + writeb(0, ioaddr + RTC_INTERRUPTS); + if (devm_request_irq(&pdev->dev, pdata->irq, + stk17ta8_rtc_interrupt, + IRQF_SHARED, + pdev->name, pdev) < 0) { + dev_warn(&pdev->dev, "interrupt not available.\n"); + pdata->irq = 0; + } + } + + pdata->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(pdata->rtc)) + return PTR_ERR(pdata->rtc); + + pdata->rtc->ops = &stk17ta8_rtc_ops; + + nvmem_cfg.priv = pdata; + ret = devm_rtc_nvmem_register(pdata->rtc, &nvmem_cfg); + if (ret) + return ret; + + return devm_rtc_register_device(pdata->rtc); +} + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:stk17ta8"); + +static struct platform_driver stk17ta8_rtc_driver = { + .probe = stk17ta8_rtc_probe, + .driver = { + .name = "stk17ta8", + }, +}; + +module_platform_driver(stk17ta8_rtc_driver); + +MODULE_AUTHOR("Thomas Hommel "); +MODULE_DESCRIPTION("Simtek STK17TA8 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-stm32.c b/drivers/rtc/rtc-stm32.c new file mode 100644 index 000000000..d4ebf3eb5 --- /dev/null +++ b/drivers/rtc/rtc-stm32.c @@ -0,0 +1,1288 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) STMicroelectronics 2017 + * Author: Amelie Delaunay + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "stm32_rtc" + +/* STM32_RTC_TR bit fields */ +#define STM32_RTC_TR_SEC_SHIFT 0 +#define STM32_RTC_TR_SEC GENMASK(6, 0) +#define STM32_RTC_TR_MIN_SHIFT 8 +#define STM32_RTC_TR_MIN GENMASK(14, 8) +#define STM32_RTC_TR_HOUR_SHIFT 16 +#define STM32_RTC_TR_HOUR GENMASK(21, 16) + +/* STM32_RTC_DR bit fields */ +#define STM32_RTC_DR_DATE_SHIFT 0 +#define STM32_RTC_DR_DATE GENMASK(5, 0) +#define STM32_RTC_DR_MONTH_SHIFT 8 +#define STM32_RTC_DR_MONTH GENMASK(12, 8) +#define STM32_RTC_DR_WDAY_SHIFT 13 +#define STM32_RTC_DR_WDAY GENMASK(15, 13) +#define STM32_RTC_DR_YEAR_SHIFT 16 +#define STM32_RTC_DR_YEAR GENMASK(23, 16) + +/* STM32_RTC_CR bit fields */ +#define STM32_RTC_CR_FMT BIT(6) +#define STM32_RTC_CR_ALRAE BIT(8) +#define STM32_RTC_CR_ALRAIE BIT(12) +#define STM32_RTC_CR_OSEL GENMASK(22, 21) +#define STM32_RTC_CR_OSEL_ALARM_A FIELD_PREP(STM32_RTC_CR_OSEL, 0x01) +#define STM32_RTC_CR_COE BIT(23) +#define STM32_RTC_CR_TAMPOE BIT(26) +#define STM32_RTC_CR_TAMPALRM_TYPE BIT(30) +#define STM32_RTC_CR_OUT2EN BIT(31) + +/* STM32_RTC_ISR/STM32_RTC_ICSR bit fields */ +#define STM32_RTC_ISR_ALRAWF BIT(0) +#define STM32_RTC_ISR_INITS BIT(4) +#define STM32_RTC_ISR_RSF BIT(5) +#define STM32_RTC_ISR_INITF BIT(6) +#define STM32_RTC_ISR_INIT BIT(7) +#define STM32_RTC_ISR_ALRAF BIT(8) + +/* STM32_RTC_PRER bit fields */ +#define STM32_RTC_PRER_PRED_S_SHIFT 0 +#define STM32_RTC_PRER_PRED_S GENMASK(14, 0) +#define STM32_RTC_PRER_PRED_A_SHIFT 16 +#define STM32_RTC_PRER_PRED_A GENMASK(22, 16) + +/* STM32_RTC_ALRMAR and STM32_RTC_ALRMBR bit fields */ +#define STM32_RTC_ALRMXR_SEC_SHIFT 0 +#define STM32_RTC_ALRMXR_SEC GENMASK(6, 0) +#define STM32_RTC_ALRMXR_SEC_MASK BIT(7) +#define STM32_RTC_ALRMXR_MIN_SHIFT 8 +#define STM32_RTC_ALRMXR_MIN GENMASK(14, 8) +#define STM32_RTC_ALRMXR_MIN_MASK BIT(15) +#define STM32_RTC_ALRMXR_HOUR_SHIFT 16 +#define STM32_RTC_ALRMXR_HOUR GENMASK(21, 16) +#define STM32_RTC_ALRMXR_PM BIT(22) +#define STM32_RTC_ALRMXR_HOUR_MASK BIT(23) +#define STM32_RTC_ALRMXR_DATE_SHIFT 24 +#define STM32_RTC_ALRMXR_DATE GENMASK(29, 24) +#define STM32_RTC_ALRMXR_WDSEL BIT(30) +#define STM32_RTC_ALRMXR_WDAY_SHIFT 24 +#define STM32_RTC_ALRMXR_WDAY GENMASK(27, 24) +#define STM32_RTC_ALRMXR_DATE_MASK BIT(31) + +/* STM32_RTC_SR/_SCR bit fields */ +#define STM32_RTC_SR_ALRA BIT(0) + +/* STM32_RTC_CFGR bit fields */ +#define STM32_RTC_CFGR_OUT2_RMP BIT(0) +#define STM32_RTC_CFGR_LSCOEN GENMASK(2, 1) +#define STM32_RTC_CFGR_LSCOEN_OUT1 1 +#define STM32_RTC_CFGR_LSCOEN_OUT2_RMP 2 + +/* STM32_RTC_VERR bit fields */ +#define STM32_RTC_VERR_MINREV_SHIFT 0 +#define STM32_RTC_VERR_MINREV GENMASK(3, 0) +#define STM32_RTC_VERR_MAJREV_SHIFT 4 +#define STM32_RTC_VERR_MAJREV GENMASK(7, 4) + +/* STM32_RTC_SECCFGR bit fields */ +#define STM32_RTC_SECCFGR 0x20 +#define STM32_RTC_SECCFGR_ALRA_SEC BIT(0) +#define STM32_RTC_SECCFGR_INIT_SEC BIT(14) +#define STM32_RTC_SECCFGR_SEC BIT(15) + +/* STM32_RTC_RXCIDCFGR bit fields */ +#define STM32_RTC_RXCIDCFGR(x) (0x80 + 0x4 * (x)) +#define STM32_RTC_RXCIDCFGR_CFEN BIT(0) +#define STM32_RTC_RXCIDCFGR_CID GENMASK(6, 4) +#define STM32_RTC_RXCIDCFGR_CID1 1 + +/* STM32_RTC_WPR key constants */ +#define RTC_WPR_1ST_KEY 0xCA +#define RTC_WPR_2ND_KEY 0x53 +#define RTC_WPR_WRONG_KEY 0xFF + +/* Max STM32 RTC register offset is 0x3FC */ +#define UNDEF_REG 0xFFFF + +/* STM32 RTC driver time helpers */ +#define SEC_PER_DAY (24 * 60 * 60) + +/* STM32 RTC pinctrl helpers */ +#define STM32_RTC_PINMUX(_name, _action, ...) { \ + .name = (_name), \ + .action = (_action), \ + .groups = ((const char *[]){ __VA_ARGS__ }), \ + .num_groups = ARRAY_SIZE(((const char *[]){ __VA_ARGS__ })), \ +} + +struct stm32_rtc; + +struct stm32_rtc_registers { + u16 tr; + u16 dr; + u16 cr; + u16 isr; + u16 prer; + u16 alrmar; + u16 wpr; + u16 sr; + u16 scr; + u16 cfgr; + u16 verr; +}; + +struct stm32_rtc_events { + u32 alra; +}; + +struct stm32_rtc_data { + const struct stm32_rtc_registers regs; + const struct stm32_rtc_events events; + void (*clear_events)(struct stm32_rtc *rtc, unsigned int flags); + bool has_pclk; + bool need_dbp; + bool need_accuracy; + bool rif_protected; + bool has_lsco; + bool has_alarm_out; +}; + +struct stm32_rtc { + struct rtc_device *rtc_dev; + void __iomem *base; + struct regmap *dbp; + unsigned int dbp_reg; + unsigned int dbp_mask; + struct clk *pclk; + struct clk *rtc_ck; + const struct stm32_rtc_data *data; + int irq_alarm; + struct clk *clk_lsco; +}; + +struct stm32_rtc_rif_resource { + unsigned int num; + u32 bit; +}; + +static const struct stm32_rtc_rif_resource STM32_RTC_RES_ALRA = {0, STM32_RTC_SECCFGR_ALRA_SEC}; +static const struct stm32_rtc_rif_resource STM32_RTC_RES_INIT = {5, STM32_RTC_SECCFGR_INIT_SEC}; + +static void stm32_rtc_wpr_unlock(struct stm32_rtc *rtc) +{ + const struct stm32_rtc_registers *regs = &rtc->data->regs; + + writel_relaxed(RTC_WPR_1ST_KEY, rtc->base + regs->wpr); + writel_relaxed(RTC_WPR_2ND_KEY, rtc->base + regs->wpr); +} + +static void stm32_rtc_wpr_lock(struct stm32_rtc *rtc) +{ + const struct stm32_rtc_registers *regs = &rtc->data->regs; + + writel_relaxed(RTC_WPR_WRONG_KEY, rtc->base + regs->wpr); +} + +enum stm32_rtc_pin_name { + NONE, + OUT1, + OUT2, + OUT2_RMP +}; + +static const struct pinctrl_pin_desc stm32_rtc_pinctrl_pins[] = { + PINCTRL_PIN(OUT1, "out1"), + PINCTRL_PIN(OUT2, "out2"), + PINCTRL_PIN(OUT2_RMP, "out2_rmp"), +}; + +static int stm32_rtc_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(stm32_rtc_pinctrl_pins); +} + +static const char *stm32_rtc_pinctrl_get_group_name(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + return stm32_rtc_pinctrl_pins[selector].name; +} + +static int stm32_rtc_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int selector, + const unsigned int **pins, + unsigned int *num_pins) +{ + *pins = &stm32_rtc_pinctrl_pins[selector].number; + *num_pins = 1; + return 0; +} + +static const struct pinctrl_ops stm32_rtc_pinctrl_ops = { + .dt_node_to_map = pinconf_generic_dt_node_to_map_all, + .dt_free_map = pinconf_generic_dt_free_map, + .get_groups_count = stm32_rtc_pinctrl_get_groups_count, + .get_group_name = stm32_rtc_pinctrl_get_group_name, + .get_group_pins = stm32_rtc_pinctrl_get_group_pins, +}; + +struct stm32_rtc_pinmux_func { + const char *name; + const char * const *groups; + const unsigned int num_groups; + int (*action)(struct pinctrl_dev *pctl_dev, unsigned int pin); +}; + +static int stm32_rtc_pinmux_action_alarm(struct pinctrl_dev *pctldev, unsigned int pin) +{ + struct stm32_rtc *rtc = pinctrl_dev_get_drvdata(pctldev); + struct stm32_rtc_registers regs = rtc->data->regs; + unsigned int cr = readl_relaxed(rtc->base + regs.cr); + unsigned int cfgr = readl_relaxed(rtc->base + regs.cfgr); + + if (!rtc->data->has_alarm_out) + return -EPERM; + + cr &= ~STM32_RTC_CR_OSEL; + cr |= STM32_RTC_CR_OSEL_ALARM_A; + cr &= ~STM32_RTC_CR_TAMPOE; + cr &= ~STM32_RTC_CR_COE; + cr &= ~STM32_RTC_CR_TAMPALRM_TYPE; + + switch (pin) { + case OUT1: + cr &= ~STM32_RTC_CR_OUT2EN; + cfgr &= ~STM32_RTC_CFGR_OUT2_RMP; + break; + case OUT2: + cr |= STM32_RTC_CR_OUT2EN; + cfgr &= ~STM32_RTC_CFGR_OUT2_RMP; + break; + case OUT2_RMP: + cr |= STM32_RTC_CR_OUT2EN; + cfgr |= STM32_RTC_CFGR_OUT2_RMP; + break; + default: + return -EINVAL; + } + + stm32_rtc_wpr_unlock(rtc); + writel_relaxed(cr, rtc->base + regs.cr); + writel_relaxed(cfgr, rtc->base + regs.cfgr); + stm32_rtc_wpr_lock(rtc); + + return 0; +} + +static int stm32_rtc_pinmux_lsco_available(struct pinctrl_dev *pctldev, unsigned int pin) +{ + struct stm32_rtc *rtc = pinctrl_dev_get_drvdata(pctldev); + struct stm32_rtc_registers regs = rtc->data->regs; + unsigned int cr = readl_relaxed(rtc->base + regs.cr); + unsigned int cfgr = readl_relaxed(rtc->base + regs.cfgr); + unsigned int calib = STM32_RTC_CR_COE; + unsigned int tampalrm = STM32_RTC_CR_TAMPOE | STM32_RTC_CR_OSEL; + + switch (pin) { + case OUT1: + if ((!(cr & STM32_RTC_CR_OUT2EN) && + ((cr & calib) || cr & tampalrm)) || + ((cr & calib) && (cr & tampalrm))) + return -EBUSY; + break; + case OUT2_RMP: + if ((cr & STM32_RTC_CR_OUT2EN) && + (cfgr & STM32_RTC_CFGR_OUT2_RMP) && + ((cr & calib) || (cr & tampalrm))) + return -EBUSY; + break; + default: + return -EINVAL; + } + + if (clk_get_rate(rtc->rtc_ck) != 32768) + return -ERANGE; + + return 0; +} + +static int stm32_rtc_pinmux_action_lsco(struct pinctrl_dev *pctldev, unsigned int pin) +{ + struct stm32_rtc *rtc = pinctrl_dev_get_drvdata(pctldev); + struct stm32_rtc_registers regs = rtc->data->regs; + struct device *dev = rtc->rtc_dev->dev.parent; + u8 lscoen; + int ret; + + if (!rtc->data->has_lsco) + return -EPERM; + + ret = stm32_rtc_pinmux_lsco_available(pctldev, pin); + if (ret) + return ret; + + lscoen = (pin == OUT1) ? STM32_RTC_CFGR_LSCOEN_OUT1 : STM32_RTC_CFGR_LSCOEN_OUT2_RMP; + + rtc->clk_lsco = clk_register_gate(dev, "rtc_lsco", __clk_get_name(rtc->rtc_ck), + CLK_IGNORE_UNUSED | CLK_IS_CRITICAL, + rtc->base + regs.cfgr, lscoen, 0, NULL); + if (IS_ERR(rtc->clk_lsco)) + return PTR_ERR(rtc->clk_lsco); + + of_clk_add_provider(dev->of_node, of_clk_src_simple_get, rtc->clk_lsco); + + return 0; +} + +static const struct stm32_rtc_pinmux_func stm32_rtc_pinmux_functions[] = { + STM32_RTC_PINMUX("lsco", &stm32_rtc_pinmux_action_lsco, "out1", "out2_rmp"), + STM32_RTC_PINMUX("alarm-a", &stm32_rtc_pinmux_action_alarm, "out1", "out2", "out2_rmp"), +}; + +static int stm32_rtc_pinmux_get_functions_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(stm32_rtc_pinmux_functions); +} + +static const char *stm32_rtc_pinmux_get_fname(struct pinctrl_dev *pctldev, unsigned int selector) +{ + return stm32_rtc_pinmux_functions[selector].name; +} + +static int stm32_rtc_pinmux_get_groups(struct pinctrl_dev *pctldev, unsigned int selector, + const char * const **groups, unsigned int * const num_groups) +{ + *groups = stm32_rtc_pinmux_functions[selector].groups; + *num_groups = stm32_rtc_pinmux_functions[selector].num_groups; + return 0; +} + +static int stm32_rtc_pinmux_set_mux(struct pinctrl_dev *pctldev, unsigned int selector, + unsigned int group) +{ + struct stm32_rtc_pinmux_func selected_func = stm32_rtc_pinmux_functions[selector]; + struct pinctrl_pin_desc pin = stm32_rtc_pinctrl_pins[group]; + + /* Call action */ + if (selected_func.action) + return selected_func.action(pctldev, pin.number); + + return -EINVAL; +} + +static const struct pinmux_ops stm32_rtc_pinmux_ops = { + .get_functions_count = stm32_rtc_pinmux_get_functions_count, + .get_function_name = stm32_rtc_pinmux_get_fname, + .get_function_groups = stm32_rtc_pinmux_get_groups, + .set_mux = stm32_rtc_pinmux_set_mux, + .strict = true, +}; + +static const struct pinctrl_desc stm32_rtc_pdesc = { + .name = DRIVER_NAME, + .pins = stm32_rtc_pinctrl_pins, + .npins = ARRAY_SIZE(stm32_rtc_pinctrl_pins), + .owner = THIS_MODULE, + .pctlops = &stm32_rtc_pinctrl_ops, + .pmxops = &stm32_rtc_pinmux_ops, +}; + +static int stm32_rtc_enter_init_mode(struct stm32_rtc *rtc) +{ + const struct stm32_rtc_registers *regs = &rtc->data->regs; + unsigned int isr = readl_relaxed(rtc->base + regs->isr); + + if (!(isr & STM32_RTC_ISR_INITF)) { + isr |= STM32_RTC_ISR_INIT; + writel_relaxed(isr, rtc->base + regs->isr); + + /* + * It takes around 2 rtc_ck clock cycles to enter in + * initialization phase mode (and have INITF flag set). As + * slowest rtc_ck frequency may be 32kHz and highest should be + * 1MHz, we poll every 10 us with a timeout of 100ms. + */ + return readl_relaxed_poll_timeout_atomic(rtc->base + regs->isr, isr, + (isr & STM32_RTC_ISR_INITF), + 10, 100000); + } + + return 0; +} + +static void stm32_rtc_exit_init_mode(struct stm32_rtc *rtc) +{ + const struct stm32_rtc_registers *regs = &rtc->data->regs; + unsigned int isr = readl_relaxed(rtc->base + regs->isr); + + isr &= ~STM32_RTC_ISR_INIT; + writel_relaxed(isr, rtc->base + regs->isr); +} + +static int stm32_rtc_wait_sync(struct stm32_rtc *rtc) +{ + const struct stm32_rtc_registers *regs = &rtc->data->regs; + unsigned int isr = readl_relaxed(rtc->base + regs->isr); + + isr &= ~STM32_RTC_ISR_RSF; + writel_relaxed(isr, rtc->base + regs->isr); + + /* + * Wait for RSF to be set to ensure the calendar registers are + * synchronised, it takes around 2 rtc_ck clock cycles + */ + return readl_relaxed_poll_timeout_atomic(rtc->base + regs->isr, + isr, + (isr & STM32_RTC_ISR_RSF), + 10, 100000); +} + +static void stm32_rtc_clear_event_flags(struct stm32_rtc *rtc, + unsigned int flags) +{ + rtc->data->clear_events(rtc, flags); +} + +static irqreturn_t stm32_rtc_alarm_irq(int irq, void *dev_id) +{ + struct stm32_rtc *rtc = (struct stm32_rtc *)dev_id; + const struct stm32_rtc_registers *regs = &rtc->data->regs; + const struct stm32_rtc_events *evts = &rtc->data->events; + unsigned int status, cr; + + rtc_lock(rtc->rtc_dev); + + status = readl_relaxed(rtc->base + regs->sr); + cr = readl_relaxed(rtc->base + regs->cr); + + if ((status & evts->alra) && + (cr & STM32_RTC_CR_ALRAIE)) { + /* Alarm A flag - Alarm interrupt */ + dev_dbg(&rtc->rtc_dev->dev, "Alarm occurred\n"); + + /* Pass event to the kernel */ + rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF); + + /* Clear event flags, otherwise new events won't be received */ + stm32_rtc_clear_event_flags(rtc, evts->alra); + } + + rtc_unlock(rtc->rtc_dev); + + return IRQ_HANDLED; +} + +/* Convert rtc_time structure from bin to bcd format */ +static void tm2bcd(struct rtc_time *tm) +{ + tm->tm_sec = bin2bcd(tm->tm_sec); + tm->tm_min = bin2bcd(tm->tm_min); + tm->tm_hour = bin2bcd(tm->tm_hour); + + tm->tm_mday = bin2bcd(tm->tm_mday); + tm->tm_mon = bin2bcd(tm->tm_mon + 1); + tm->tm_year = bin2bcd(tm->tm_year - 100); + /* + * Number of days since Sunday + * - on kernel side, 0=Sunday...6=Saturday + * - on rtc side, 0=invalid,1=Monday...7=Sunday + */ + tm->tm_wday = (!tm->tm_wday) ? 7 : tm->tm_wday; +} + +/* Convert rtc_time structure from bcd to bin format */ +static void bcd2tm(struct rtc_time *tm) +{ + tm->tm_sec = bcd2bin(tm->tm_sec); + tm->tm_min = bcd2bin(tm->tm_min); + tm->tm_hour = bcd2bin(tm->tm_hour); + + tm->tm_mday = bcd2bin(tm->tm_mday); + tm->tm_mon = bcd2bin(tm->tm_mon) - 1; + tm->tm_year = bcd2bin(tm->tm_year) + 100; + /* + * Number of days since Sunday + * - on kernel side, 0=Sunday...6=Saturday + * - on rtc side, 0=invalid,1=Monday...7=Sunday + */ + tm->tm_wday %= 7; +} + +static int stm32_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct stm32_rtc *rtc = dev_get_drvdata(dev); + const struct stm32_rtc_registers *regs = &rtc->data->regs; + unsigned int tr, dr; + + /* Time and Date in BCD format */ + tr = readl_relaxed(rtc->base + regs->tr); + dr = readl_relaxed(rtc->base + regs->dr); + + tm->tm_sec = (tr & STM32_RTC_TR_SEC) >> STM32_RTC_TR_SEC_SHIFT; + tm->tm_min = (tr & STM32_RTC_TR_MIN) >> STM32_RTC_TR_MIN_SHIFT; + tm->tm_hour = (tr & STM32_RTC_TR_HOUR) >> STM32_RTC_TR_HOUR_SHIFT; + + tm->tm_mday = (dr & STM32_RTC_DR_DATE) >> STM32_RTC_DR_DATE_SHIFT; + tm->tm_mon = (dr & STM32_RTC_DR_MONTH) >> STM32_RTC_DR_MONTH_SHIFT; + tm->tm_year = (dr & STM32_RTC_DR_YEAR) >> STM32_RTC_DR_YEAR_SHIFT; + tm->tm_wday = (dr & STM32_RTC_DR_WDAY) >> STM32_RTC_DR_WDAY_SHIFT; + + /* We don't report tm_yday and tm_isdst */ + + bcd2tm(tm); + + return 0; +} + +static int stm32_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct stm32_rtc *rtc = dev_get_drvdata(dev); + const struct stm32_rtc_registers *regs = &rtc->data->regs; + unsigned int tr, dr; + int ret = 0; + + tm2bcd(tm); + + /* Time in BCD format */ + tr = ((tm->tm_sec << STM32_RTC_TR_SEC_SHIFT) & STM32_RTC_TR_SEC) | + ((tm->tm_min << STM32_RTC_TR_MIN_SHIFT) & STM32_RTC_TR_MIN) | + ((tm->tm_hour << STM32_RTC_TR_HOUR_SHIFT) & STM32_RTC_TR_HOUR); + + /* Date in BCD format */ + dr = ((tm->tm_mday << STM32_RTC_DR_DATE_SHIFT) & STM32_RTC_DR_DATE) | + ((tm->tm_mon << STM32_RTC_DR_MONTH_SHIFT) & STM32_RTC_DR_MONTH) | + ((tm->tm_year << STM32_RTC_DR_YEAR_SHIFT) & STM32_RTC_DR_YEAR) | + ((tm->tm_wday << STM32_RTC_DR_WDAY_SHIFT) & STM32_RTC_DR_WDAY); + + stm32_rtc_wpr_unlock(rtc); + + ret = stm32_rtc_enter_init_mode(rtc); + if (ret) { + dev_err(dev, "Can't enter in init mode. Set time aborted.\n"); + goto end; + } + + writel_relaxed(tr, rtc->base + regs->tr); + writel_relaxed(dr, rtc->base + regs->dr); + + stm32_rtc_exit_init_mode(rtc); + + ret = stm32_rtc_wait_sync(rtc); +end: + stm32_rtc_wpr_lock(rtc); + + return ret; +} + +static int stm32_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct stm32_rtc *rtc = dev_get_drvdata(dev); + const struct stm32_rtc_registers *regs = &rtc->data->regs; + const struct stm32_rtc_events *evts = &rtc->data->events; + struct rtc_time *tm = &alrm->time; + unsigned int alrmar, cr, status; + + alrmar = readl_relaxed(rtc->base + regs->alrmar); + cr = readl_relaxed(rtc->base + regs->cr); + status = readl_relaxed(rtc->base + regs->sr); + + if (alrmar & STM32_RTC_ALRMXR_DATE_MASK) { + /* + * Date/day doesn't matter in Alarm comparison so alarm + * triggers every day + */ + tm->tm_mday = -1; + tm->tm_wday = -1; + } else { + if (alrmar & STM32_RTC_ALRMXR_WDSEL) { + /* Alarm is set to a day of week */ + tm->tm_mday = -1; + tm->tm_wday = (alrmar & STM32_RTC_ALRMXR_WDAY) >> + STM32_RTC_ALRMXR_WDAY_SHIFT; + tm->tm_wday %= 7; + } else { + /* Alarm is set to a day of month */ + tm->tm_wday = -1; + tm->tm_mday = (alrmar & STM32_RTC_ALRMXR_DATE) >> + STM32_RTC_ALRMXR_DATE_SHIFT; + } + } + + if (alrmar & STM32_RTC_ALRMXR_HOUR_MASK) { + /* Hours don't matter in Alarm comparison */ + tm->tm_hour = -1; + } else { + tm->tm_hour = (alrmar & STM32_RTC_ALRMXR_HOUR) >> + STM32_RTC_ALRMXR_HOUR_SHIFT; + if (alrmar & STM32_RTC_ALRMXR_PM) + tm->tm_hour += 12; + } + + if (alrmar & STM32_RTC_ALRMXR_MIN_MASK) { + /* Minutes don't matter in Alarm comparison */ + tm->tm_min = -1; + } else { + tm->tm_min = (alrmar & STM32_RTC_ALRMXR_MIN) >> + STM32_RTC_ALRMXR_MIN_SHIFT; + } + + if (alrmar & STM32_RTC_ALRMXR_SEC_MASK) { + /* Seconds don't matter in Alarm comparison */ + tm->tm_sec = -1; + } else { + tm->tm_sec = (alrmar & STM32_RTC_ALRMXR_SEC) >> + STM32_RTC_ALRMXR_SEC_SHIFT; + } + + bcd2tm(tm); + + alrm->enabled = (cr & STM32_RTC_CR_ALRAE) ? 1 : 0; + alrm->pending = (status & evts->alra) ? 1 : 0; + + return 0; +} + +static int stm32_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct stm32_rtc *rtc = dev_get_drvdata(dev); + const struct stm32_rtc_registers *regs = &rtc->data->regs; + const struct stm32_rtc_events *evts = &rtc->data->events; + unsigned int cr; + + cr = readl_relaxed(rtc->base + regs->cr); + + stm32_rtc_wpr_unlock(rtc); + + /* We expose Alarm A to the kernel */ + if (enabled) + cr |= (STM32_RTC_CR_ALRAIE | STM32_RTC_CR_ALRAE); + else + cr &= ~(STM32_RTC_CR_ALRAIE | STM32_RTC_CR_ALRAE); + writel_relaxed(cr, rtc->base + regs->cr); + + /* Clear event flags, otherwise new events won't be received */ + stm32_rtc_clear_event_flags(rtc, evts->alra); + + stm32_rtc_wpr_lock(rtc); + + return 0; +} + +static int stm32_rtc_valid_alrm(struct device *dev, struct rtc_time *tm) +{ + static struct rtc_time now; + time64_t max_alarm_time64; + int max_day_forward; + int next_month; + int next_year; + + /* + * Assuming current date is M-D-Y H:M:S. + * RTC alarm can't be set on a specific month and year. + * So the valid alarm range is: + * M-D-Y H:M:S < alarm <= (M+1)-D-Y H:M:S + */ + stm32_rtc_read_time(dev, &now); + + /* + * Find the next month and the year of the next month. + * Note: tm_mon and next_month are from 0 to 11 + */ + next_month = now.tm_mon + 1; + if (next_month == 12) { + next_month = 0; + next_year = now.tm_year + 1; + } else { + next_year = now.tm_year; + } + + /* Find the maximum limit of alarm in days. */ + max_day_forward = rtc_month_days(now.tm_mon, now.tm_year) + - now.tm_mday + + min(rtc_month_days(next_month, next_year), now.tm_mday); + + /* Convert to timestamp and compare the alarm time and its upper limit */ + max_alarm_time64 = rtc_tm_to_time64(&now) + max_day_forward * SEC_PER_DAY; + return rtc_tm_to_time64(tm) <= max_alarm_time64 ? 0 : -EINVAL; +} + +static int stm32_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct stm32_rtc *rtc = dev_get_drvdata(dev); + const struct stm32_rtc_registers *regs = &rtc->data->regs; + struct rtc_time *tm = &alrm->time; + unsigned int cr, isr, alrmar; + int ret = 0; + + /* + * RTC alarm can't be set on a specific date, unless this date is + * up to the same day of month next month. + */ + if (stm32_rtc_valid_alrm(dev, tm) < 0) { + dev_err(dev, "Alarm can be set only on upcoming month.\n"); + return -EINVAL; + } + + tm2bcd(tm); + + alrmar = 0; + /* tm_year and tm_mon are not used because not supported by RTC */ + alrmar |= (tm->tm_mday << STM32_RTC_ALRMXR_DATE_SHIFT) & + STM32_RTC_ALRMXR_DATE; + /* 24-hour format */ + alrmar &= ~STM32_RTC_ALRMXR_PM; + alrmar |= (tm->tm_hour << STM32_RTC_ALRMXR_HOUR_SHIFT) & + STM32_RTC_ALRMXR_HOUR; + alrmar |= (tm->tm_min << STM32_RTC_ALRMXR_MIN_SHIFT) & + STM32_RTC_ALRMXR_MIN; + alrmar |= (tm->tm_sec << STM32_RTC_ALRMXR_SEC_SHIFT) & + STM32_RTC_ALRMXR_SEC; + + stm32_rtc_wpr_unlock(rtc); + + /* Disable Alarm */ + cr = readl_relaxed(rtc->base + regs->cr); + cr &= ~STM32_RTC_CR_ALRAE; + writel_relaxed(cr, rtc->base + regs->cr); + + /* + * Poll Alarm write flag to be sure that Alarm update is allowed: it + * takes around 2 rtc_ck clock cycles + */ + ret = readl_relaxed_poll_timeout_atomic(rtc->base + regs->isr, + isr, + (isr & STM32_RTC_ISR_ALRAWF), + 10, 100000); + + if (ret) { + dev_err(dev, "Alarm update not allowed\n"); + goto end; + } + + /* Write to Alarm register */ + writel_relaxed(alrmar, rtc->base + regs->alrmar); + + stm32_rtc_alarm_irq_enable(dev, alrm->enabled); +end: + stm32_rtc_wpr_lock(rtc); + + return ret; +} + +static const struct rtc_class_ops stm32_rtc_ops = { + .read_time = stm32_rtc_read_time, + .set_time = stm32_rtc_set_time, + .read_alarm = stm32_rtc_read_alarm, + .set_alarm = stm32_rtc_set_alarm, + .alarm_irq_enable = stm32_rtc_alarm_irq_enable, +}; + +static void stm32_rtc_clear_events(struct stm32_rtc *rtc, + unsigned int flags) +{ + const struct stm32_rtc_registers *regs = &rtc->data->regs; + + /* Flags are cleared by writing 0 in RTC_ISR */ + writel_relaxed(readl_relaxed(rtc->base + regs->isr) & ~flags, + rtc->base + regs->isr); +} + +static const struct stm32_rtc_data stm32_rtc_data = { + .has_pclk = false, + .need_dbp = true, + .need_accuracy = false, + .rif_protected = false, + .has_lsco = false, + .has_alarm_out = false, + .regs = { + .tr = 0x00, + .dr = 0x04, + .cr = 0x08, + .isr = 0x0C, + .prer = 0x10, + .alrmar = 0x1C, + .wpr = 0x24, + .sr = 0x0C, /* set to ISR offset to ease alarm management */ + .scr = UNDEF_REG, + .cfgr = UNDEF_REG, + .verr = UNDEF_REG, + }, + .events = { + .alra = STM32_RTC_ISR_ALRAF, + }, + .clear_events = stm32_rtc_clear_events, +}; + +static const struct stm32_rtc_data stm32h7_rtc_data = { + .has_pclk = true, + .need_dbp = true, + .need_accuracy = false, + .rif_protected = false, + .has_lsco = false, + .has_alarm_out = false, + .regs = { + .tr = 0x00, + .dr = 0x04, + .cr = 0x08, + .isr = 0x0C, + .prer = 0x10, + .alrmar = 0x1C, + .wpr = 0x24, + .sr = 0x0C, /* set to ISR offset to ease alarm management */ + .scr = UNDEF_REG, + .cfgr = UNDEF_REG, + .verr = UNDEF_REG, + }, + .events = { + .alra = STM32_RTC_ISR_ALRAF, + }, + .clear_events = stm32_rtc_clear_events, +}; + +static void stm32mp1_rtc_clear_events(struct stm32_rtc *rtc, + unsigned int flags) +{ + struct stm32_rtc_registers regs = rtc->data->regs; + + /* Flags are cleared by writing 1 in RTC_SCR */ + writel_relaxed(flags, rtc->base + regs.scr); +} + +static const struct stm32_rtc_data stm32mp1_data = { + .has_pclk = true, + .need_dbp = false, + .need_accuracy = true, + .rif_protected = false, + .has_lsco = true, + .has_alarm_out = true, + .regs = { + .tr = 0x00, + .dr = 0x04, + .cr = 0x18, + .isr = 0x0C, /* named RTC_ICSR on stm32mp1 */ + .prer = 0x10, + .alrmar = 0x40, + .wpr = 0x24, + .sr = 0x50, + .scr = 0x5C, + .cfgr = 0x60, + .verr = 0x3F4, + }, + .events = { + .alra = STM32_RTC_SR_ALRA, + }, + .clear_events = stm32mp1_rtc_clear_events, +}; + +static const struct stm32_rtc_data stm32mp25_data = { + .has_pclk = true, + .need_dbp = false, + .need_accuracy = true, + .rif_protected = true, + .has_lsco = true, + .has_alarm_out = true, + .regs = { + .tr = 0x00, + .dr = 0x04, + .cr = 0x18, + .isr = 0x0C, /* named RTC_ICSR on stm32mp25 */ + .prer = 0x10, + .alrmar = 0x40, + .wpr = 0x24, + .sr = 0x50, + .scr = 0x5C, + .cfgr = 0x60, + .verr = 0x3F4, + }, + .events = { + .alra = STM32_RTC_SR_ALRA, + }, + .clear_events = stm32mp1_rtc_clear_events, +}; + +static const struct of_device_id stm32_rtc_of_match[] = { + { .compatible = "st,stm32-rtc", .data = &stm32_rtc_data }, + { .compatible = "st,stm32h7-rtc", .data = &stm32h7_rtc_data }, + { .compatible = "st,stm32mp1-rtc", .data = &stm32mp1_data }, + { .compatible = "st,stm32mp25-rtc", .data = &stm32mp25_data }, + {} +}; +MODULE_DEVICE_TABLE(of, stm32_rtc_of_match); + +static void stm32_rtc_clean_outs(struct stm32_rtc *rtc) +{ + struct stm32_rtc_registers regs = rtc->data->regs; + unsigned int cr = readl_relaxed(rtc->base + regs.cr); + + cr &= ~STM32_RTC_CR_OSEL; + cr &= ~STM32_RTC_CR_TAMPOE; + cr &= ~STM32_RTC_CR_COE; + cr &= ~STM32_RTC_CR_TAMPALRM_TYPE; + cr &= ~STM32_RTC_CR_OUT2EN; + + stm32_rtc_wpr_unlock(rtc); + writel_relaxed(cr, rtc->base + regs.cr); + stm32_rtc_wpr_lock(rtc); + + if (regs.cfgr != UNDEF_REG) { + unsigned int cfgr = readl_relaxed(rtc->base + regs.cfgr); + + cfgr &= ~STM32_RTC_CFGR_LSCOEN; + cfgr &= ~STM32_RTC_CFGR_OUT2_RMP; + writel_relaxed(cfgr, rtc->base + regs.cfgr); + } +} + +static int stm32_rtc_check_rif(struct stm32_rtc *stm32_rtc, + struct stm32_rtc_rif_resource res) +{ + u32 rxcidcfgr = readl_relaxed(stm32_rtc->base + STM32_RTC_RXCIDCFGR(res.num)); + u32 seccfgr; + + /* Check if RTC available for our CID */ + if ((rxcidcfgr & STM32_RTC_RXCIDCFGR_CFEN) && + (FIELD_GET(STM32_RTC_RXCIDCFGR_CID, rxcidcfgr) != STM32_RTC_RXCIDCFGR_CID1)) + return -EACCES; + + /* Check if RTC available for non secure world */ + seccfgr = readl_relaxed(stm32_rtc->base + STM32_RTC_SECCFGR); + if ((seccfgr & STM32_RTC_SECCFGR_SEC) | (seccfgr & res.bit)) + return -EACCES; + + return 0; +} + +static int stm32_rtc_init(struct platform_device *pdev, + struct stm32_rtc *rtc) +{ + const struct stm32_rtc_registers *regs = &rtc->data->regs; + unsigned int prer, pred_a, pred_s, pred_a_max, pred_s_max, cr; + unsigned int rate; + int ret; + + rate = clk_get_rate(rtc->rtc_ck); + + /* Find prediv_a and prediv_s to obtain the 1Hz calendar clock */ + pred_a_max = STM32_RTC_PRER_PRED_A >> STM32_RTC_PRER_PRED_A_SHIFT; + pred_s_max = STM32_RTC_PRER_PRED_S >> STM32_RTC_PRER_PRED_S_SHIFT; + + if (rate > (pred_a_max + 1) * (pred_s_max + 1)) { + dev_err(&pdev->dev, "rtc_ck rate is too high: %dHz\n", rate); + return -EINVAL; + } + + if (rtc->data->need_accuracy) { + for (pred_a = 0; pred_a <= pred_a_max; pred_a++) { + pred_s = (rate / (pred_a + 1)) - 1; + + if (pred_s <= pred_s_max && ((pred_s + 1) * (pred_a + 1)) == rate) + break; + } + } else { + for (pred_a = pred_a_max; pred_a + 1 > 0; pred_a--) { + pred_s = (rate / (pred_a + 1)) - 1; + + if (((pred_s + 1) * (pred_a + 1)) == rate) + break; + } + } + + /* + * Can't find a 1Hz, so give priority to RTC power consumption + * by choosing the higher possible value for prediv_a + */ + if (pred_s > pred_s_max || pred_a > pred_a_max) { + pred_a = pred_a_max; + pred_s = (rate / (pred_a + 1)) - 1; + + dev_warn(&pdev->dev, "rtc_ck is %s\n", + (rate < ((pred_a + 1) * (pred_s + 1))) ? + "fast" : "slow"); + } + + cr = readl_relaxed(rtc->base + regs->cr); + + prer = readl_relaxed(rtc->base + regs->prer); + prer &= STM32_RTC_PRER_PRED_S | STM32_RTC_PRER_PRED_A; + + pred_s = (pred_s << STM32_RTC_PRER_PRED_S_SHIFT) & + STM32_RTC_PRER_PRED_S; + pred_a = (pred_a << STM32_RTC_PRER_PRED_A_SHIFT) & + STM32_RTC_PRER_PRED_A; + + /* quit if there is nothing to initialize */ + if ((cr & STM32_RTC_CR_FMT) == 0 && prer == (pred_s | pred_a)) + return 0; + + stm32_rtc_wpr_unlock(rtc); + + ret = stm32_rtc_enter_init_mode(rtc); + if (ret) { + dev_err(&pdev->dev, + "Can't enter in init mode. Prescaler config failed.\n"); + goto end; + } + + writel_relaxed(pred_s, rtc->base + regs->prer); + writel_relaxed(pred_a | pred_s, rtc->base + regs->prer); + + /* Force 24h time format */ + cr &= ~STM32_RTC_CR_FMT; + writel_relaxed(cr, rtc->base + regs->cr); + + stm32_rtc_exit_init_mode(rtc); + + ret = stm32_rtc_wait_sync(rtc); +end: + stm32_rtc_wpr_lock(rtc); + + return ret; +} + +static int stm32_rtc_probe(struct platform_device *pdev) +{ + struct stm32_rtc *rtc; + const struct stm32_rtc_registers *regs; + struct pinctrl_dev *pctl; + int ret; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rtc->base)) + return PTR_ERR(rtc->base); + + rtc->data = (struct stm32_rtc_data *) + of_device_get_match_data(&pdev->dev); + regs = &rtc->data->regs; + + if (rtc->data->need_dbp) { + unsigned int args[2]; + + rtc->dbp = syscon_regmap_lookup_by_phandle_args(pdev->dev.of_node, + "st,syscfg", + 2, args); + if (IS_ERR(rtc->dbp)) { + dev_err(&pdev->dev, "no st,syscfg\n"); + return PTR_ERR(rtc->dbp); + } + + rtc->dbp_reg = args[0]; + rtc->dbp_mask = args[1]; + } + + if (!rtc->data->has_pclk) { + rtc->pclk = NULL; + rtc->rtc_ck = devm_clk_get(&pdev->dev, NULL); + } else { + rtc->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(rtc->pclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(rtc->pclk), "no pclk clock"); + + rtc->rtc_ck = devm_clk_get(&pdev->dev, "rtc_ck"); + } + if (IS_ERR(rtc->rtc_ck)) + return dev_err_probe(&pdev->dev, PTR_ERR(rtc->rtc_ck), "no rtc_ck clock"); + + if (rtc->data->has_pclk) { + ret = clk_prepare_enable(rtc->pclk); + if (ret) + return ret; + } + + ret = clk_prepare_enable(rtc->rtc_ck); + if (ret) + goto err_no_rtc_ck; + + if (rtc->data->need_dbp) + regmap_update_bits(rtc->dbp, rtc->dbp_reg, + rtc->dbp_mask, rtc->dbp_mask); + + if (rtc->data->rif_protected) { + ret = stm32_rtc_check_rif(rtc, STM32_RTC_RES_INIT); + if (!ret) + ret = stm32_rtc_check_rif(rtc, STM32_RTC_RES_ALRA); + if (ret) { + dev_err(&pdev->dev, "Failed to probe RTC due to RIF configuration\n"); + goto err; + } + } + + /* + * After a system reset, RTC_ISR.INITS flag can be read to check if + * the calendar has been initialized or not. INITS flag is reset by a + * power-on reset (no vbat, no power-supply). It is not reset if + * rtc_ck parent clock has changed (so RTC prescalers need to be + * changed). That's why we cannot rely on this flag to know if RTC + * init has to be done. + */ + ret = stm32_rtc_init(pdev, rtc); + if (ret) + goto err; + + rtc->irq_alarm = platform_get_irq(pdev, 0); + if (rtc->irq_alarm <= 0) { + ret = rtc->irq_alarm; + goto err; + } + + ret = devm_device_init_wakeup(&pdev->dev); + if (ret) + goto err; + + ret = devm_pm_set_wake_irq(&pdev->dev, rtc->irq_alarm); + if (ret) + goto err; + + platform_set_drvdata(pdev, rtc); + + rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name, + &stm32_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc->rtc_dev)) { + ret = PTR_ERR(rtc->rtc_dev); + dev_err(&pdev->dev, "rtc device registration failed, err=%d\n", + ret); + goto err; + } + + /* Handle RTC alarm interrupts */ + ret = devm_request_threaded_irq(&pdev->dev, rtc->irq_alarm, NULL, + stm32_rtc_alarm_irq, IRQF_ONESHOT, + pdev->name, rtc); + if (ret) { + dev_err(&pdev->dev, "IRQ%d (alarm interrupt) already claimed\n", + rtc->irq_alarm); + goto err; + } + + stm32_rtc_clean_outs(rtc); + + ret = devm_pinctrl_register_and_init(&pdev->dev, &stm32_rtc_pdesc, rtc, &pctl); + if (ret) + return dev_err_probe(&pdev->dev, ret, "pinctrl register failed"); + + ret = pinctrl_enable(pctl); + if (ret) + return dev_err_probe(&pdev->dev, ret, "pinctrl enable failed"); + + /* + * If INITS flag is reset (calendar year field set to 0x00), calendar + * must be initialized + */ + if (!(readl_relaxed(rtc->base + regs->isr) & STM32_RTC_ISR_INITS)) + dev_warn(&pdev->dev, "Date/Time must be initialized\n"); + + if (regs->verr != UNDEF_REG) { + u32 ver = readl_relaxed(rtc->base + regs->verr); + + dev_info(&pdev->dev, "registered rev:%d.%d\n", + (ver >> STM32_RTC_VERR_MAJREV_SHIFT) & 0xF, + (ver >> STM32_RTC_VERR_MINREV_SHIFT) & 0xF); + } + + return 0; + +err: + clk_disable_unprepare(rtc->rtc_ck); +err_no_rtc_ck: + if (rtc->data->has_pclk) + clk_disable_unprepare(rtc->pclk); + + if (rtc->data->need_dbp) + regmap_update_bits(rtc->dbp, rtc->dbp_reg, rtc->dbp_mask, 0); + + return ret; +} + +static void stm32_rtc_remove(struct platform_device *pdev) +{ + struct stm32_rtc *rtc = platform_get_drvdata(pdev); + const struct stm32_rtc_registers *regs = &rtc->data->regs; + unsigned int cr; + + if (!IS_ERR_OR_NULL(rtc->clk_lsco)) + clk_unregister_gate(rtc->clk_lsco); + + /* Disable interrupts */ + stm32_rtc_wpr_unlock(rtc); + cr = readl_relaxed(rtc->base + regs->cr); + cr &= ~STM32_RTC_CR_ALRAIE; + writel_relaxed(cr, rtc->base + regs->cr); + stm32_rtc_wpr_lock(rtc); + + clk_disable_unprepare(rtc->rtc_ck); + if (rtc->data->has_pclk) + clk_disable_unprepare(rtc->pclk); + + /* Enable backup domain write protection if needed */ + if (rtc->data->need_dbp) + regmap_update_bits(rtc->dbp, rtc->dbp_reg, rtc->dbp_mask, 0); +} + +static int stm32_rtc_suspend(struct device *dev) +{ + struct stm32_rtc *rtc = dev_get_drvdata(dev); + + if (rtc->data->has_pclk) + clk_disable_unprepare(rtc->pclk); + + return 0; +} + +static int stm32_rtc_resume(struct device *dev) +{ + struct stm32_rtc *rtc = dev_get_drvdata(dev); + int ret = 0; + + if (rtc->data->has_pclk) { + ret = clk_prepare_enable(rtc->pclk); + if (ret) + return ret; + } + + ret = stm32_rtc_wait_sync(rtc); + if (ret < 0) { + if (rtc->data->has_pclk) + clk_disable_unprepare(rtc->pclk); + return ret; + } + + return ret; +} + +static const struct dev_pm_ops stm32_rtc_pm_ops = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(stm32_rtc_suspend, stm32_rtc_resume) +}; + +static struct platform_driver stm32_rtc_driver = { + .probe = stm32_rtc_probe, + .remove = stm32_rtc_remove, + .driver = { + .name = DRIVER_NAME, + .pm = &stm32_rtc_pm_ops, + .of_match_table = stm32_rtc_of_match, + }, +}; + +module_platform_driver(stm32_rtc_driver); + +MODULE_AUTHOR("Amelie Delaunay "); +MODULE_DESCRIPTION("STMicroelectronics STM32 Real Time Clock driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-stmp3xxx.c b/drivers/rtc/rtc-stmp3xxx.c new file mode 100644 index 000000000..7afcd14ae --- /dev/null +++ b/drivers/rtc/rtc-stmp3xxx.c @@ -0,0 +1,419 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Freescale STMP37XX/STMP378X Real Time Clock driver + * + * Copyright (c) 2007 Sigmatel, Inc. + * Peter Hartley, + * + * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + * Copyright 2011 Wolfram Sang, Pengutronix e.K. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STMP3XXX_RTC_CTRL 0x0 +#define STMP3XXX_RTC_CTRL_ALARM_IRQ_EN 0x00000001 +#define STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN 0x00000002 +#define STMP3XXX_RTC_CTRL_ALARM_IRQ 0x00000004 +#define STMP3XXX_RTC_CTRL_WATCHDOGEN 0x00000010 + +#define STMP3XXX_RTC_STAT 0x10 +#define STMP3XXX_RTC_STAT_STALE_SHIFT 16 +#define STMP3XXX_RTC_STAT_RTC_PRESENT 0x80000000 +#define STMP3XXX_RTC_STAT_XTAL32000_PRESENT 0x10000000 +#define STMP3XXX_RTC_STAT_XTAL32768_PRESENT 0x08000000 + +#define STMP3XXX_RTC_SECONDS 0x30 + +#define STMP3XXX_RTC_ALARM 0x40 + +#define STMP3XXX_RTC_WATCHDOG 0x50 + +#define STMP3XXX_RTC_PERSISTENT0 0x60 +#define STMP3XXX_RTC_PERSISTENT0_CLOCKSOURCE (1 << 0) +#define STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN (1 << 1) +#define STMP3XXX_RTC_PERSISTENT0_ALARM_EN (1 << 2) +#define STMP3XXX_RTC_PERSISTENT0_XTAL24MHZ_PWRUP (1 << 4) +#define STMP3XXX_RTC_PERSISTENT0_XTAL32KHZ_PWRUP (1 << 5) +#define STMP3XXX_RTC_PERSISTENT0_XTAL32_FREQ (1 << 6) +#define STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE (1 << 7) + +#define STMP3XXX_RTC_PERSISTENT1 0x70 +/* missing bitmask in headers */ +#define STMP3XXX_RTC_PERSISTENT1_FORCE_UPDATER 0x80000000 + +struct stmp3xxx_rtc_data { + struct rtc_device *rtc; + void __iomem *io; + int irq_alarm; +}; + +#if IS_ENABLED(CONFIG_STMP3XXX_RTC_WATCHDOG) +/** + * stmp3xxx_wdt_set_timeout - configure the watchdog inside the STMP3xxx RTC + * @dev: the parent device of the watchdog (= the RTC) + * @timeout: the desired value for the timeout register of the watchdog. + * 0 disables the watchdog + * + * The watchdog needs one register and two bits which are in the RTC domain. + * To handle the resource conflict, the RTC driver will create another + * platform_device for the watchdog driver as a child of the RTC device. + * The watchdog driver is passed the below accessor function via platform_data + * to configure the watchdog. Locking is not needed because accessing SET/CLR + * registers is atomic. + */ + +static void stmp3xxx_wdt_set_timeout(struct device *dev, u32 timeout) +{ + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + + if (timeout) { + writel(timeout, rtc_data->io + STMP3XXX_RTC_WATCHDOG); + writel(STMP3XXX_RTC_CTRL_WATCHDOGEN, + rtc_data->io + STMP3XXX_RTC_CTRL + STMP_OFFSET_REG_SET); + writel(STMP3XXX_RTC_PERSISTENT1_FORCE_UPDATER, + rtc_data->io + STMP3XXX_RTC_PERSISTENT1 + STMP_OFFSET_REG_SET); + } else { + writel(STMP3XXX_RTC_CTRL_WATCHDOGEN, + rtc_data->io + STMP3XXX_RTC_CTRL + STMP_OFFSET_REG_CLR); + writel(STMP3XXX_RTC_PERSISTENT1_FORCE_UPDATER, + rtc_data->io + STMP3XXX_RTC_PERSISTENT1 + STMP_OFFSET_REG_CLR); + } +} + +static struct stmp3xxx_wdt_pdata wdt_pdata = { + .wdt_set_timeout = stmp3xxx_wdt_set_timeout, +}; + +static void stmp3xxx_wdt_register(struct platform_device *rtc_pdev) +{ + int rc = -1; + struct platform_device *wdt_pdev = + platform_device_alloc("stmp3xxx_rtc_wdt", rtc_pdev->id); + + if (wdt_pdev) { + wdt_pdev->dev.parent = &rtc_pdev->dev; + wdt_pdev->dev.platform_data = &wdt_pdata; + rc = platform_device_add(wdt_pdev); + if (rc) + platform_device_put(wdt_pdev); + } + + if (rc) + dev_err(&rtc_pdev->dev, + "failed to register stmp3xxx_rtc_wdt\n"); +} +#else +static void stmp3xxx_wdt_register(struct platform_device *rtc_pdev) +{ +} +#endif /* CONFIG_STMP3XXX_RTC_WATCHDOG */ + +static int stmp3xxx_wait_time(struct stmp3xxx_rtc_data *rtc_data) +{ + int timeout = 5000; /* 3ms according to i.MX28 Ref Manual */ + /* + * The i.MX28 Applications Processor Reference Manual, Rev. 1, 2010 + * states: + * | The order in which registers are updated is + * | Persistent 0, 1, 2, 3, 4, 5, Alarm, Seconds. + * | (This list is in bitfield order, from LSB to MSB, as they would + * | appear in the STALE_REGS and NEW_REGS bitfields of the HW_RTC_STAT + * | register. For example, the Seconds register corresponds to + * | STALE_REGS or NEW_REGS containing 0x80.) + */ + do { + if (!(readl(rtc_data->io + STMP3XXX_RTC_STAT) & + (0x80 << STMP3XXX_RTC_STAT_STALE_SHIFT))) + return 0; + udelay(1); + } while (--timeout > 0); + return (readl(rtc_data->io + STMP3XXX_RTC_STAT) & + (0x80 << STMP3XXX_RTC_STAT_STALE_SHIFT)) ? -ETIME : 0; +} + +/* Time read/write */ +static int stmp3xxx_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) +{ + int ret; + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + + ret = stmp3xxx_wait_time(rtc_data); + if (ret) + return ret; + + rtc_time64_to_tm(readl(rtc_data->io + STMP3XXX_RTC_SECONDS), rtc_tm); + return 0; +} + +static int stmp3xxx_rtc_settime(struct device *dev, struct rtc_time *rtc_tm) +{ + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + + writel(rtc_tm_to_time64(rtc_tm), rtc_data->io + STMP3XXX_RTC_SECONDS); + return stmp3xxx_wait_time(rtc_data); +} + +/* interrupt(s) handler */ +static irqreturn_t stmp3xxx_rtc_interrupt(int irq, void *dev_id) +{ + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev_id); + u32 status = readl(rtc_data->io + STMP3XXX_RTC_CTRL); + + if (status & STMP3XXX_RTC_CTRL_ALARM_IRQ) { + writel(STMP3XXX_RTC_CTRL_ALARM_IRQ, + rtc_data->io + STMP3XXX_RTC_CTRL + STMP_OFFSET_REG_CLR); + rtc_update_irq(rtc_data->rtc, 1, RTC_AF | RTC_IRQF); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int stmp3xxx_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + + if (enabled) { + writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | + STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN, + rtc_data->io + STMP3XXX_RTC_PERSISTENT0 + + STMP_OFFSET_REG_SET); + writel(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, + rtc_data->io + STMP3XXX_RTC_CTRL + STMP_OFFSET_REG_SET); + } else { + writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | + STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN, + rtc_data->io + STMP3XXX_RTC_PERSISTENT0 + + STMP_OFFSET_REG_CLR); + writel(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, + rtc_data->io + STMP3XXX_RTC_CTRL + STMP_OFFSET_REG_CLR); + } + return 0; +} + +static int stmp3xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + + rtc_time64_to_tm(readl(rtc_data->io + STMP3XXX_RTC_ALARM), &alm->time); + return 0; +} + +static int stmp3xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + + writel(rtc_tm_to_time64(&alm->time), rtc_data->io + STMP3XXX_RTC_ALARM); + + stmp3xxx_alarm_irq_enable(dev, alm->enabled); + + return 0; +} + +static const struct rtc_class_ops stmp3xxx_rtc_ops = { + .alarm_irq_enable = + stmp3xxx_alarm_irq_enable, + .read_time = stmp3xxx_rtc_gettime, + .set_time = stmp3xxx_rtc_settime, + .read_alarm = stmp3xxx_rtc_read_alarm, + .set_alarm = stmp3xxx_rtc_set_alarm, +}; + +static void stmp3xxx_rtc_remove(struct platform_device *pdev) +{ + struct stmp3xxx_rtc_data *rtc_data = platform_get_drvdata(pdev); + + if (!rtc_data) + return; + + writel(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, + rtc_data->io + STMP3XXX_RTC_CTRL + STMP_OFFSET_REG_CLR); +} + +static int stmp3xxx_rtc_probe(struct platform_device *pdev) +{ + struct stmp3xxx_rtc_data *rtc_data; + struct resource *r; + u32 rtc_stat; + u32 pers0_set, pers0_clr; + u32 crystalfreq = 0; + int err; + + rtc_data = devm_kzalloc(&pdev->dev, sizeof(*rtc_data), GFP_KERNEL); + if (!rtc_data) + return -ENOMEM; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "failed to get resource\n"); + return -ENXIO; + } + + rtc_data->io = devm_ioremap(&pdev->dev, r->start, resource_size(r)); + if (!rtc_data->io) { + dev_err(&pdev->dev, "ioremap failed\n"); + return -EIO; + } + + rtc_data->irq_alarm = platform_get_irq(pdev, 0); + + rtc_stat = readl(rtc_data->io + STMP3XXX_RTC_STAT); + if (!(rtc_stat & STMP3XXX_RTC_STAT_RTC_PRESENT)) { + dev_err(&pdev->dev, "no device onboard\n"); + return -ENODEV; + } + + platform_set_drvdata(pdev, rtc_data); + + /* + * Resetting the rtc stops the watchdog timer that is potentially + * running. So (assuming it is running on purpose) don't reset if the + * watchdog is enabled. + */ + if (readl(rtc_data->io + STMP3XXX_RTC_CTRL) & + STMP3XXX_RTC_CTRL_WATCHDOGEN) { + dev_info(&pdev->dev, + "Watchdog is running, skip resetting rtc\n"); + } else { + err = stmp_reset_block(rtc_data->io); + if (err) { + dev_err(&pdev->dev, "stmp_reset_block failed: %d\n", + err); + return err; + } + } + + /* + * Obviously the rtc needs a clock input to be able to run. + * This clock can be provided by an external 32k crystal. If that one is + * missing XTAL must not be disabled in suspend which consumes a + * lot of power. Normally the presence and exact frequency (supported + * are 32000 Hz and 32768 Hz) is detectable from fuses, but as reality + * proves these fuses are not blown correctly on all machines, so the + * frequency can be overridden in the device tree. + */ + if (rtc_stat & STMP3XXX_RTC_STAT_XTAL32000_PRESENT) + crystalfreq = 32000; + else if (rtc_stat & STMP3XXX_RTC_STAT_XTAL32768_PRESENT) + crystalfreq = 32768; + + of_property_read_u32(pdev->dev.of_node, "stmp,crystal-freq", + &crystalfreq); + + switch (crystalfreq) { + case 32000: + /* keep 32kHz crystal running in low-power mode */ + pers0_set = STMP3XXX_RTC_PERSISTENT0_XTAL32_FREQ | + STMP3XXX_RTC_PERSISTENT0_XTAL32KHZ_PWRUP | + STMP3XXX_RTC_PERSISTENT0_CLOCKSOURCE; + pers0_clr = STMP3XXX_RTC_PERSISTENT0_XTAL24MHZ_PWRUP; + break; + case 32768: + /* keep 32.768kHz crystal running in low-power mode */ + pers0_set = STMP3XXX_RTC_PERSISTENT0_XTAL32KHZ_PWRUP | + STMP3XXX_RTC_PERSISTENT0_CLOCKSOURCE; + pers0_clr = STMP3XXX_RTC_PERSISTENT0_XTAL24MHZ_PWRUP | + STMP3XXX_RTC_PERSISTENT0_XTAL32_FREQ; + break; + default: + dev_warn(&pdev->dev, + "invalid crystal-freq specified in device-tree. Assuming no crystal\n"); + fallthrough; + case 0: + /* keep XTAL on in low-power mode */ + pers0_set = STMP3XXX_RTC_PERSISTENT0_XTAL24MHZ_PWRUP; + pers0_clr = STMP3XXX_RTC_PERSISTENT0_XTAL32KHZ_PWRUP | + STMP3XXX_RTC_PERSISTENT0_CLOCKSOURCE; + } + + writel(pers0_set, rtc_data->io + STMP3XXX_RTC_PERSISTENT0 + + STMP_OFFSET_REG_SET); + + writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | + STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN | + STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE | pers0_clr, + rtc_data->io + STMP3XXX_RTC_PERSISTENT0 + STMP_OFFSET_REG_CLR); + + writel(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN | + STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, + rtc_data->io + STMP3XXX_RTC_CTRL + STMP_OFFSET_REG_CLR); + + rtc_data->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc_data->rtc)) + return PTR_ERR(rtc_data->rtc); + + err = devm_request_irq(&pdev->dev, rtc_data->irq_alarm, + stmp3xxx_rtc_interrupt, 0, "RTC alarm", &pdev->dev); + if (err) { + dev_err(&pdev->dev, "Cannot claim IRQ%d\n", + rtc_data->irq_alarm); + return err; + } + + rtc_data->rtc->ops = &stmp3xxx_rtc_ops; + rtc_data->rtc->range_max = U32_MAX; + + err = devm_rtc_register_device(rtc_data->rtc); + if (err) + return err; + + stmp3xxx_wdt_register(pdev); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int stmp3xxx_rtc_suspend(struct device *dev) +{ + return 0; +} + +static int stmp3xxx_rtc_resume(struct device *dev) +{ + struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); + + stmp_reset_block(rtc_data->io); + writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | + STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN | + STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE, + rtc_data->io + STMP3XXX_RTC_PERSISTENT0 + STMP_OFFSET_REG_CLR); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(stmp3xxx_rtc_pm_ops, stmp3xxx_rtc_suspend, + stmp3xxx_rtc_resume); + +static const struct of_device_id rtc_dt_ids[] = { + { .compatible = "fsl,stmp3xxx-rtc", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rtc_dt_ids); + +static struct platform_driver stmp3xxx_rtcdrv = { + .probe = stmp3xxx_rtc_probe, + .remove = stmp3xxx_rtc_remove, + .driver = { + .name = "stmp3xxx-rtc", + .pm = &stmp3xxx_rtc_pm_ops, + .of_match_table = rtc_dt_ids, + }, +}; + +module_platform_driver(stmp3xxx_rtcdrv); + +MODULE_DESCRIPTION("STMP3xxx RTC Driver"); +MODULE_AUTHOR("dmitry pervushin and " + "Wolfram Sang "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-sun4v.c b/drivers/rtc/rtc-sun4v.c new file mode 100644 index 000000000..a86e27de8 --- /dev/null +++ b/drivers/rtc/rtc-sun4v.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0 +/* rtc-sun4v.c: Hypervisor based RTC for SUN4V systems. + * + * Author: David S. Miller + * + * Copyright (C) 2008 David S. Miller + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#include + +static unsigned long hypervisor_get_time(void) +{ + unsigned long ret, time; + int retries = 10000; + +retry: + ret = sun4v_tod_get(&time); + if (ret == HV_EOK) + return time; + if (ret == HV_EWOULDBLOCK) { + if (--retries > 0) { + udelay(100); + goto retry; + } + pr_warn("tod_get() timed out.\n"); + return 0; + } + pr_warn("tod_get() not supported.\n"); + return 0; +} + +static int sun4v_read_time(struct device *dev, struct rtc_time *tm) +{ + rtc_time64_to_tm(hypervisor_get_time(), tm); + return 0; +} + +static int hypervisor_set_time(unsigned long secs) +{ + unsigned long ret; + int retries = 10000; + +retry: + ret = sun4v_tod_set(secs); + if (ret == HV_EOK) + return 0; + if (ret == HV_EWOULDBLOCK) { + if (--retries > 0) { + udelay(100); + goto retry; + } + pr_warn("tod_set() timed out.\n"); + return -EAGAIN; + } + pr_warn("tod_set() not supported.\n"); + return -EOPNOTSUPP; +} + +static int sun4v_set_time(struct device *dev, struct rtc_time *tm) +{ + return hypervisor_set_time(rtc_tm_to_time64(tm)); +} + +static const struct rtc_class_ops sun4v_rtc_ops = { + .read_time = sun4v_read_time, + .set_time = sun4v_set_time, +}; + +static int __init sun4v_rtc_probe(struct platform_device *pdev) +{ + struct rtc_device *rtc; + + rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + rtc->ops = &sun4v_rtc_ops; + rtc->range_max = U64_MAX; + platform_set_drvdata(pdev, rtc); + + return devm_rtc_register_device(rtc); +} + +static struct platform_driver sun4v_rtc_driver = { + .driver = { + .name = "rtc-sun4v", + }, +}; + +builtin_platform_driver_probe(sun4v_rtc_driver, sun4v_rtc_probe); diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c new file mode 100644 index 000000000..7fc60880e --- /dev/null +++ b/drivers/rtc/rtc-sun6i.c @@ -0,0 +1,883 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * An RTC driver for Allwinner A31/A23 + * + * Copyright (c) 2014, Chen-Yu Tsai + * + * based on rtc-sunxi.c + * + * An RTC driver for Allwinner A10/A20 + * + * Copyright (c) 2013, Carlo Caione + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Control register */ +#define SUN6I_LOSC_CTRL 0x0000 +#define SUN6I_LOSC_CTRL_KEY (0x16aa << 16) +#define SUN6I_LOSC_CTRL_AUTO_SWT_BYPASS BIT(15) +#define SUN6I_LOSC_CTRL_ALM_DHMS_ACC BIT(9) +#define SUN6I_LOSC_CTRL_RTC_HMS_ACC BIT(8) +#define SUN6I_LOSC_CTRL_RTC_YMD_ACC BIT(7) +#define SUN6I_LOSC_CTRL_EXT_LOSC_EN BIT(4) +#define SUN6I_LOSC_CTRL_EXT_OSC BIT(0) +#define SUN6I_LOSC_CTRL_ACC_MASK GENMASK(9, 7) + +#define SUN6I_LOSC_CLK_PRESCAL 0x0008 + +/* RTC */ +#define SUN6I_RTC_YMD 0x0010 +#define SUN6I_RTC_HMS 0x0014 + +/* Alarm 0 (counter) */ +#define SUN6I_ALRM_COUNTER 0x0020 +/* This holds the remaining alarm seconds on older SoCs (current value) */ +#define SUN6I_ALRM_COUNTER_HMS 0x0024 +#define SUN6I_ALRM_EN 0x0028 +#define SUN6I_ALRM_EN_CNT_EN BIT(0) +#define SUN6I_ALRM_IRQ_EN 0x002c +#define SUN6I_ALRM_IRQ_EN_CNT_IRQ_EN BIT(0) +#define SUN6I_ALRM_IRQ_STA 0x0030 +#define SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND BIT(0) + +/* Alarm 1 (wall clock) */ +#define SUN6I_ALRM1_EN 0x0044 +#define SUN6I_ALRM1_IRQ_EN 0x0048 +#define SUN6I_ALRM1_IRQ_STA 0x004c +#define SUN6I_ALRM1_IRQ_STA_WEEK_IRQ_PEND BIT(0) + +/* Alarm config */ +#define SUN6I_ALARM_CONFIG 0x0050 +#define SUN6I_ALARM_CONFIG_WAKEUP BIT(0) + +#define SUN6I_LOSC_OUT_GATING 0x0060 +#define SUN6I_LOSC_OUT_GATING_EN_OFFSET 0 + +/* General-purpose data */ +#define SUN6I_GP_DATA 0x0100 +#define SUN6I_GP_DATA_SIZE 0x20 + +/* + * Get date values + */ +#define SUN6I_DATE_GET_DAY_VALUE(x) ((x) & 0x0000001f) +#define SUN6I_DATE_GET_MON_VALUE(x) (((x) & 0x00000f00) >> 8) +#define SUN6I_DATE_GET_YEAR_VALUE(x) (((x) & 0x003f0000) >> 16) +#define SUN6I_LEAP_GET_VALUE(x) (((x) & 0x00400000) >> 22) + +/* + * Get time values + */ +#define SUN6I_TIME_GET_SEC_VALUE(x) ((x) & 0x0000003f) +#define SUN6I_TIME_GET_MIN_VALUE(x) (((x) & 0x00003f00) >> 8) +#define SUN6I_TIME_GET_HOUR_VALUE(x) (((x) & 0x001f0000) >> 16) + +/* + * Set date values + */ +#define SUN6I_DATE_SET_DAY_VALUE(x) ((x) & 0x0000001f) +#define SUN6I_DATE_SET_MON_VALUE(x) ((x) << 8 & 0x00000f00) +#define SUN6I_DATE_SET_YEAR_VALUE(x) ((x) << 16 & 0x003f0000) +#define SUN6I_LEAP_SET_VALUE(x) ((x) << 22 & 0x00400000) + +/* + * Set time values + */ +#define SUN6I_TIME_SET_SEC_VALUE(x) ((x) & 0x0000003f) +#define SUN6I_TIME_SET_MIN_VALUE(x) ((x) << 8 & 0x00003f00) +#define SUN6I_TIME_SET_HOUR_VALUE(x) ((x) << 16 & 0x001f0000) + +/* + * The year parameter passed to the driver is usually an offset relative to + * the year 1900. This macro is used to convert this offset to another one + * relative to the minimum year allowed by the hardware. + * + * The year range is 1970 - 2033. This range is selected to match Allwinner's + * driver, even though it is somewhat limited. + */ +#define SUN6I_YEAR_MIN 1970 +#define SUN6I_YEAR_OFF (SUN6I_YEAR_MIN - 1900) + +#define SECS_PER_DAY (24 * 3600ULL) + +/* + * There are other differences between models, including: + * + * - number of GPIO pins that can be configured to hold a certain level + * - crypto-key related registers (H5, H6) + * - boot process related (super standby, secondary processor entry address) + * registers (R40, H6) + * - SYS power domain controls (R40) + * - DCXO controls (H6) + * - RC oscillator calibration (H6) + * + * These functions are not covered by this driver. + */ +struct sun6i_rtc_clk_data { + unsigned long rc_osc_rate; + unsigned int fixed_prescaler : 16; + unsigned int has_prescaler : 1; + unsigned int has_out_clk : 1; + unsigned int has_losc_en : 1; + unsigned int has_auto_swt : 1; +}; + +#define RTC_LINEAR_DAY BIT(0) + +struct sun6i_rtc_dev { + struct rtc_device *rtc; + const struct sun6i_rtc_clk_data *data; + void __iomem *base; + int irq; + time64_t alarm; + unsigned long flags; + + struct clk_hw hw; + struct clk_hw *int_osc; + struct clk *losc; + struct clk *ext_losc; + + spinlock_t lock; +}; + +static struct sun6i_rtc_dev *sun6i_rtc; + +static unsigned long sun6i_rtc_osc_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct sun6i_rtc_dev *rtc = container_of(hw, struct sun6i_rtc_dev, hw); + u32 val = 0; + + val = readl(rtc->base + SUN6I_LOSC_CTRL); + if (val & SUN6I_LOSC_CTRL_EXT_OSC) + return parent_rate; + + if (rtc->data->fixed_prescaler) + parent_rate /= rtc->data->fixed_prescaler; + + if (rtc->data->has_prescaler) { + val = readl(rtc->base + SUN6I_LOSC_CLK_PRESCAL); + val &= GENMASK(4, 0); + } + + return parent_rate / (val + 1); +} + +static u8 sun6i_rtc_osc_get_parent(struct clk_hw *hw) +{ + struct sun6i_rtc_dev *rtc = container_of(hw, struct sun6i_rtc_dev, hw); + + return readl(rtc->base + SUN6I_LOSC_CTRL) & SUN6I_LOSC_CTRL_EXT_OSC; +} + +static int sun6i_rtc_osc_set_parent(struct clk_hw *hw, u8 index) +{ + struct sun6i_rtc_dev *rtc = container_of(hw, struct sun6i_rtc_dev, hw); + unsigned long flags; + u32 val; + + if (index > 1) + return -EINVAL; + + spin_lock_irqsave(&rtc->lock, flags); + val = readl(rtc->base + SUN6I_LOSC_CTRL); + val &= ~SUN6I_LOSC_CTRL_EXT_OSC; + val |= SUN6I_LOSC_CTRL_KEY; + val |= index ? SUN6I_LOSC_CTRL_EXT_OSC : 0; + if (rtc->data->has_losc_en) { + val &= ~SUN6I_LOSC_CTRL_EXT_LOSC_EN; + val |= index ? SUN6I_LOSC_CTRL_EXT_LOSC_EN : 0; + } + writel(val, rtc->base + SUN6I_LOSC_CTRL); + spin_unlock_irqrestore(&rtc->lock, flags); + + return 0; +} + +static const struct clk_ops sun6i_rtc_osc_ops = { + .recalc_rate = sun6i_rtc_osc_recalc_rate, + .determine_rate = clk_hw_determine_rate_no_reparent, + + .get_parent = sun6i_rtc_osc_get_parent, + .set_parent = sun6i_rtc_osc_set_parent, +}; + +static void __init sun6i_rtc_clk_init(struct device_node *node, + const struct sun6i_rtc_clk_data *data) +{ + struct clk_hw_onecell_data *clk_data; + struct sun6i_rtc_dev *rtc; + struct clk_init_data init = { + .ops = &sun6i_rtc_osc_ops, + .name = "losc", + }; + const char *iosc_name = "rtc-int-osc"; + const char *clkout_name = "osc32k-out"; + const char *parents[2]; + u32 reg; + + rtc = kzalloc_obj(*rtc); + if (!rtc) + return; + + rtc->data = data; + clk_data = kzalloc_flex(*clk_data, hws, 3); + if (!clk_data) { + kfree(rtc); + return; + } + + spin_lock_init(&rtc->lock); + + rtc->base = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(rtc->base)) { + pr_crit("Can't map RTC registers"); + goto err; + } + + reg = SUN6I_LOSC_CTRL_KEY; + if (rtc->data->has_auto_swt) { + /* Bypass auto-switch to int osc, on ext losc failure */ + reg |= SUN6I_LOSC_CTRL_AUTO_SWT_BYPASS; + writel(reg, rtc->base + SUN6I_LOSC_CTRL); + } + + /* Switch to the external, more precise, oscillator, if present */ + if (of_property_present(node, "clocks")) { + reg |= SUN6I_LOSC_CTRL_EXT_OSC; + if (rtc->data->has_losc_en) + reg |= SUN6I_LOSC_CTRL_EXT_LOSC_EN; + } + writel(reg, rtc->base + SUN6I_LOSC_CTRL); + + /* Yes, I know, this is ugly. */ + sun6i_rtc = rtc; + + of_property_read_string_index(node, "clock-output-names", 2, + &iosc_name); + + rtc->int_osc = clk_hw_register_fixed_rate_with_accuracy(NULL, + iosc_name, + NULL, 0, + rtc->data->rc_osc_rate, + 300000000); + if (IS_ERR(rtc->int_osc)) { + pr_crit("Couldn't register the internal oscillator\n"); + goto err; + } + + parents[0] = clk_hw_get_name(rtc->int_osc); + /* If there is no external oscillator, this will be NULL and ... */ + parents[1] = of_clk_get_parent_name(node, 0); + + rtc->hw.init = &init; + + init.parent_names = parents; + /* ... number of clock parents will be 1. */ + init.num_parents = of_clk_get_parent_count(node) + 1; + of_property_read_string_index(node, "clock-output-names", 0, + &init.name); + + rtc->losc = clk_register(NULL, &rtc->hw); + if (IS_ERR(rtc->losc)) { + pr_crit("Couldn't register the LOSC clock\n"); + goto err_register; + } + + of_property_read_string_index(node, "clock-output-names", 1, + &clkout_name); + rtc->ext_losc = clk_register_gate(NULL, clkout_name, init.name, + 0, rtc->base + SUN6I_LOSC_OUT_GATING, + SUN6I_LOSC_OUT_GATING_EN_OFFSET, 0, + &rtc->lock); + if (IS_ERR(rtc->ext_losc)) { + pr_crit("Couldn't register the LOSC external gate\n"); + goto err_register; + } + + clk_data->num = 3; + clk_data->hws[0] = &rtc->hw; + clk_data->hws[1] = __clk_get_hw(rtc->ext_losc); + clk_data->hws[2] = rtc->int_osc; + of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data); + return; + +err_register: + clk_hw_unregister_fixed_rate(rtc->int_osc); +err: + kfree(clk_data); +} + +static const struct sun6i_rtc_clk_data sun6i_a31_rtc_data = { + .rc_osc_rate = 667000, /* datasheet says 600 ~ 700 KHz */ + .has_prescaler = 1, +}; + +static void __init sun6i_a31_rtc_clk_init(struct device_node *node) +{ + sun6i_rtc_clk_init(node, &sun6i_a31_rtc_data); +} +CLK_OF_DECLARE_DRIVER(sun6i_a31_rtc_clk, "allwinner,sun6i-a31-rtc", + sun6i_a31_rtc_clk_init); + +static const struct sun6i_rtc_clk_data sun8i_a23_rtc_data = { + .rc_osc_rate = 667000, /* datasheet says 600 ~ 700 KHz */ + .has_prescaler = 1, + .has_out_clk = 1, +}; + +static void __init sun8i_a23_rtc_clk_init(struct device_node *node) +{ + sun6i_rtc_clk_init(node, &sun8i_a23_rtc_data); +} +CLK_OF_DECLARE_DRIVER(sun8i_a23_rtc_clk, "allwinner,sun8i-a23-rtc", + sun8i_a23_rtc_clk_init); + +static const struct sun6i_rtc_clk_data sun8i_h3_rtc_data = { + .rc_osc_rate = 16000000, + .fixed_prescaler = 32, + .has_prescaler = 1, + .has_out_clk = 1, +}; + +static void __init sun8i_h3_rtc_clk_init(struct device_node *node) +{ + sun6i_rtc_clk_init(node, &sun8i_h3_rtc_data); +} +CLK_OF_DECLARE_DRIVER(sun8i_h3_rtc_clk, "allwinner,sun8i-h3-rtc", + sun8i_h3_rtc_clk_init); +/* As far as we are concerned, clocks for H5 are the same as H3 */ +CLK_OF_DECLARE_DRIVER(sun50i_h5_rtc_clk, "allwinner,sun50i-h5-rtc", + sun8i_h3_rtc_clk_init); + +static const struct sun6i_rtc_clk_data sun50i_h6_rtc_data = { + .rc_osc_rate = 16000000, + .fixed_prescaler = 32, + .has_prescaler = 1, + .has_out_clk = 1, + .has_losc_en = 1, + .has_auto_swt = 1, +}; + +static void __init sun50i_h6_rtc_clk_init(struct device_node *node) +{ + sun6i_rtc_clk_init(node, &sun50i_h6_rtc_data); +} +CLK_OF_DECLARE_DRIVER(sun50i_h6_rtc_clk, "allwinner,sun50i-h6-rtc", + sun50i_h6_rtc_clk_init); + +/* + * The R40 user manual is self-conflicting on whether the prescaler is + * fixed or configurable. The clock diagram shows it as fixed, but there + * is also a configurable divider in the RTC block. + */ +static const struct sun6i_rtc_clk_data sun8i_r40_rtc_data = { + .rc_osc_rate = 16000000, + .fixed_prescaler = 512, +}; +static void __init sun8i_r40_rtc_clk_init(struct device_node *node) +{ + sun6i_rtc_clk_init(node, &sun8i_r40_rtc_data); +} +CLK_OF_DECLARE_DRIVER(sun8i_r40_rtc_clk, "allwinner,sun8i-r40-rtc", + sun8i_r40_rtc_clk_init); + +static const struct sun6i_rtc_clk_data sun8i_v3_rtc_data = { + .rc_osc_rate = 32000, + .has_out_clk = 1, + .has_auto_swt = 1, +}; + +static void __init sun8i_v3_rtc_clk_init(struct device_node *node) +{ + sun6i_rtc_clk_init(node, &sun8i_v3_rtc_data); +} +CLK_OF_DECLARE_DRIVER(sun8i_v3_rtc_clk, "allwinner,sun8i-v3-rtc", + sun8i_v3_rtc_clk_init); + +static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id) +{ + struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id; + irqreturn_t ret = IRQ_NONE; + u32 val; + + spin_lock(&chip->lock); + val = readl(chip->base + SUN6I_ALRM_IRQ_STA); + + if (val & SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND) { + val |= SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND; + writel(val, chip->base + SUN6I_ALRM_IRQ_STA); + + rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF); + + ret = IRQ_HANDLED; + } + spin_unlock(&chip->lock); + + return ret; +} + +static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip) +{ + u32 alrm_val = 0; + u32 alrm_irq_val = 0; + u32 alrm_wake_val = 0; + unsigned long flags; + + if (to) { + alrm_val = SUN6I_ALRM_EN_CNT_EN; + alrm_irq_val = SUN6I_ALRM_IRQ_EN_CNT_IRQ_EN; + alrm_wake_val = SUN6I_ALARM_CONFIG_WAKEUP; + } else { + writel(SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND, + chip->base + SUN6I_ALRM_IRQ_STA); + } + + spin_lock_irqsave(&chip->lock, flags); + writel(alrm_val, chip->base + SUN6I_ALRM_EN); + writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN); + writel(alrm_wake_val, chip->base + SUN6I_ALARM_CONFIG); + spin_unlock_irqrestore(&chip->lock, flags); +} + +static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) +{ + struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); + u32 date, time; + + /* + * read again in case it changes + */ + do { + date = readl(chip->base + SUN6I_RTC_YMD); + time = readl(chip->base + SUN6I_RTC_HMS); + } while ((date != readl(chip->base + SUN6I_RTC_YMD)) || + (time != readl(chip->base + SUN6I_RTC_HMS))); + + if (chip->flags & RTC_LINEAR_DAY) { + /* + * Newer chips store a linear day number, the manual + * does not mandate any epoch base. The BSP driver uses + * the UNIX epoch, let's just copy that, as it's the + * easiest anyway. + */ + rtc_time64_to_tm((date & 0xffff) * SECS_PER_DAY, rtc_tm); + } else { + rtc_tm->tm_mday = SUN6I_DATE_GET_DAY_VALUE(date); + rtc_tm->tm_mon = SUN6I_DATE_GET_MON_VALUE(date) - 1; + rtc_tm->tm_year = SUN6I_DATE_GET_YEAR_VALUE(date); + + /* + * switch from (data_year->min)-relative offset to + * a (1900)-relative one + */ + rtc_tm->tm_year += SUN6I_YEAR_OFF; + } + + rtc_tm->tm_sec = SUN6I_TIME_GET_SEC_VALUE(time); + rtc_tm->tm_min = SUN6I_TIME_GET_MIN_VALUE(time); + rtc_tm->tm_hour = SUN6I_TIME_GET_HOUR_VALUE(time); + + return 0; +} + +static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); + unsigned long flags; + u32 alrm_st; + u32 alrm_en; + + spin_lock_irqsave(&chip->lock, flags); + alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN); + alrm_st = readl(chip->base + SUN6I_ALRM_IRQ_STA); + spin_unlock_irqrestore(&chip->lock, flags); + + wkalrm->enabled = !!(alrm_en & SUN6I_ALRM_EN_CNT_EN); + wkalrm->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN); + rtc_time64_to_tm(chip->alarm, &wkalrm->time); + + return 0; +} + +static int sun6i_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); + struct rtc_time *alrm_tm = &wkalrm->time; + struct rtc_time tm_now; + time64_t time_set; + u32 counter_val, counter_val_hms; + int ret; + + time_set = rtc_tm_to_time64(alrm_tm); + + if (chip->flags & RTC_LINEAR_DAY) { + /* + * The alarm registers hold the actual alarm time, encoded + * in the same way (linear day + HMS) as the current time. + */ + counter_val_hms = SUN6I_TIME_SET_SEC_VALUE(alrm_tm->tm_sec) | + SUN6I_TIME_SET_MIN_VALUE(alrm_tm->tm_min) | + SUN6I_TIME_SET_HOUR_VALUE(alrm_tm->tm_hour); + /* The division will cut off the H:M:S part of alrm_tm. */ + counter_val = div_u64(rtc_tm_to_time64(alrm_tm), SECS_PER_DAY); + } else { + /* The alarm register holds the number of seconds left. */ + time64_t time_now; + + ret = sun6i_rtc_gettime(dev, &tm_now); + if (ret < 0) { + dev_err(dev, "Error in getting time\n"); + return -EINVAL; + } + + time_now = rtc_tm_to_time64(&tm_now); + if (time_set <= time_now) { + dev_err(dev, "Date to set in the past\n"); + return -EINVAL; + } + if ((time_set - time_now) > U32_MAX) { + dev_err(dev, "Date too far in the future\n"); + return -EINVAL; + } + + counter_val = time_set - time_now; + } + + sun6i_rtc_setaie(0, chip); + writel(0, chip->base + SUN6I_ALRM_COUNTER); + if (chip->flags & RTC_LINEAR_DAY) + writel(0, chip->base + SUN6I_ALRM_COUNTER_HMS); + usleep_range(100, 300); + + writel(counter_val, chip->base + SUN6I_ALRM_COUNTER); + if (chip->flags & RTC_LINEAR_DAY) + writel(counter_val_hms, chip->base + SUN6I_ALRM_COUNTER_HMS); + chip->alarm = time_set; + + sun6i_rtc_setaie(wkalrm->enabled, chip); + + return 0; +} + +static int sun6i_rtc_wait(struct sun6i_rtc_dev *chip, int offset, + unsigned int mask, unsigned int ms_timeout) +{ + const unsigned long timeout = jiffies + msecs_to_jiffies(ms_timeout); + u32 reg; + + do { + reg = readl(chip->base + offset); + reg &= mask; + + if (!reg) + return 0; + + } while (time_before(jiffies, timeout)); + + return -ETIMEDOUT; +} + +static int sun6i_rtc_settime(struct device *dev, struct rtc_time *rtc_tm) +{ + struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); + u32 date = 0; + u32 time = 0; + + time = SUN6I_TIME_SET_SEC_VALUE(rtc_tm->tm_sec) | + SUN6I_TIME_SET_MIN_VALUE(rtc_tm->tm_min) | + SUN6I_TIME_SET_HOUR_VALUE(rtc_tm->tm_hour); + + if (chip->flags & RTC_LINEAR_DAY) { + /* The division will cut off the H:M:S part of rtc_tm. */ + date = div_u64(rtc_tm_to_time64(rtc_tm), SECS_PER_DAY); + } else { + rtc_tm->tm_year -= SUN6I_YEAR_OFF; + rtc_tm->tm_mon += 1; + + date = SUN6I_DATE_SET_DAY_VALUE(rtc_tm->tm_mday) | + SUN6I_DATE_SET_MON_VALUE(rtc_tm->tm_mon) | + SUN6I_DATE_SET_YEAR_VALUE(rtc_tm->tm_year); + + if (is_leap_year(rtc_tm->tm_year + SUN6I_YEAR_MIN)) + date |= SUN6I_LEAP_SET_VALUE(1); + } + + /* Check whether registers are writable */ + if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL, + SUN6I_LOSC_CTRL_ACC_MASK, 50)) { + dev_err(dev, "rtc is still busy.\n"); + return -EBUSY; + } + + writel(time, chip->base + SUN6I_RTC_HMS); + + /* + * After writing the RTC HH-MM-SS register, the + * SUN6I_LOSC_CTRL_RTC_HMS_ACC bit is set and it will not + * be cleared until the real writing operation is finished + */ + + if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL, + SUN6I_LOSC_CTRL_RTC_HMS_ACC, 50)) { + dev_err(dev, "Failed to set rtc time.\n"); + return -ETIMEDOUT; + } + + writel(date, chip->base + SUN6I_RTC_YMD); + + /* + * After writing the RTC YY-MM-DD register, the + * SUN6I_LOSC_CTRL_RTC_YMD_ACC bit is set and it will not + * be cleared until the real writing operation is finished + */ + + if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL, + SUN6I_LOSC_CTRL_RTC_YMD_ACC, 50)) { + dev_err(dev, "Failed to set rtc time.\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int sun6i_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); + + if (!enabled) + sun6i_rtc_setaie(enabled, chip); + + return 0; +} + +static const struct rtc_class_ops sun6i_rtc_ops = { + .read_time = sun6i_rtc_gettime, + .set_time = sun6i_rtc_settime, + .read_alarm = sun6i_rtc_getalarm, + .set_alarm = sun6i_rtc_setalarm, + .alarm_irq_enable = sun6i_rtc_alarm_irq_enable +}; + +static int sun6i_rtc_nvmem_read(void *priv, unsigned int offset, void *_val, size_t bytes) +{ + struct sun6i_rtc_dev *chip = priv; + u32 *val = _val; + int i; + + for (i = 0; i < bytes / 4; ++i) + val[i] = readl(chip->base + SUN6I_GP_DATA + offset + 4 * i); + + return 0; +} + +static int sun6i_rtc_nvmem_write(void *priv, unsigned int offset, void *_val, size_t bytes) +{ + struct sun6i_rtc_dev *chip = priv; + u32 *val = _val; + int i; + + for (i = 0; i < bytes / 4; ++i) + writel(val[i], chip->base + SUN6I_GP_DATA + offset + 4 * i); + + return 0; +} + +static struct nvmem_config sun6i_rtc_nvmem_cfg = { + .type = NVMEM_TYPE_BATTERY_BACKED, + .reg_read = sun6i_rtc_nvmem_read, + .reg_write = sun6i_rtc_nvmem_write, + .size = SUN6I_GP_DATA_SIZE, + .word_size = 4, + .stride = 4, +}; + +#ifdef CONFIG_PM_SLEEP +/* Enable IRQ wake on suspend, to wake up from RTC. */ +static int sun6i_rtc_suspend(struct device *dev) +{ + struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(chip->irq); + + return 0; +} + +/* Disable IRQ wake on resume. */ +static int sun6i_rtc_resume(struct device *dev) +{ + struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(chip->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(sun6i_rtc_pm_ops, + sun6i_rtc_suspend, sun6i_rtc_resume); + +static void sun6i_rtc_bus_clk_cleanup(void *data) +{ + struct clk *bus_clk = data; + + clk_disable_unprepare(bus_clk); +} + +static int sun6i_rtc_probe(struct platform_device *pdev) +{ + struct sun6i_rtc_dev *chip = sun6i_rtc; + struct device *dev = &pdev->dev; + struct clk *bus_clk; + int ret; + + bus_clk = devm_clk_get_optional(dev, "bus"); + if (IS_ERR(bus_clk)) + return PTR_ERR(bus_clk); + + if (bus_clk) { + ret = clk_prepare_enable(bus_clk); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, sun6i_rtc_bus_clk_cleanup, + bus_clk); + if (ret) + return ret; + } + + if (!chip) { + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + spin_lock_init(&chip->lock); + + chip->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(chip->base)) + return PTR_ERR(chip->base); + + if (IS_REACHABLE(CONFIG_SUN6I_RTC_CCU)) { + ret = sun6i_rtc_ccu_probe(dev, chip->base); + if (ret) + return ret; + } + } + + platform_set_drvdata(pdev, chip); + + chip->flags = (unsigned long)of_device_get_match_data(&pdev->dev); + + chip->irq = platform_get_irq(pdev, 0); + if (chip->irq < 0) + return chip->irq; + + ret = devm_request_irq(&pdev->dev, chip->irq, sun6i_rtc_alarmirq, + 0, dev_name(&pdev->dev), chip); + if (ret) { + dev_err(&pdev->dev, "Could not request IRQ\n"); + return ret; + } + + /* clear the alarm counter value */ + writel(0, chip->base + SUN6I_ALRM_COUNTER); + + /* disable counter alarm */ + writel(0, chip->base + SUN6I_ALRM_EN); + + /* disable counter alarm interrupt */ + writel(0, chip->base + SUN6I_ALRM_IRQ_EN); + + /* disable week alarm */ + writel(0, chip->base + SUN6I_ALRM1_EN); + + /* disable week alarm interrupt */ + writel(0, chip->base + SUN6I_ALRM1_IRQ_EN); + + /* clear counter alarm pending interrupts */ + writel(SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND, + chip->base + SUN6I_ALRM_IRQ_STA); + + /* clear week alarm pending interrupts */ + writel(SUN6I_ALRM1_IRQ_STA_WEEK_IRQ_PEND, + chip->base + SUN6I_ALRM1_IRQ_STA); + + /* disable alarm wakeup */ + writel(0, chip->base + SUN6I_ALARM_CONFIG); + + clk_prepare_enable(chip->losc); + + device_init_wakeup(&pdev->dev, true); + + chip->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(chip->rtc)) + return PTR_ERR(chip->rtc); + + chip->rtc->ops = &sun6i_rtc_ops; + if (chip->flags & RTC_LINEAR_DAY) + chip->rtc->range_max = (65536 * SECS_PER_DAY) - 1; + else + chip->rtc->range_max = 2019686399LL; /* 2033-12-31 23:59:59 */ + + ret = devm_rtc_register_device(chip->rtc); + if (ret) + return ret; + + sun6i_rtc_nvmem_cfg.priv = chip; + ret = devm_rtc_nvmem_register(chip->rtc, &sun6i_rtc_nvmem_cfg); + if (ret) + return ret; + + return 0; +} + +/* + * As far as RTC functionality goes, all models are the same. The + * datasheets claim that different models have different number of + * registers available for non-volatile storage, but experiments show + * that all SoCs have 16 registers available for this purpose. + */ +static const struct of_device_id sun6i_rtc_dt_ids[] = { + { .compatible = "allwinner,sun6i-a31-rtc" }, + { .compatible = "allwinner,sun8i-a23-rtc" }, + { .compatible = "allwinner,sun8i-h3-rtc" }, + { .compatible = "allwinner,sun8i-r40-rtc" }, + { .compatible = "allwinner,sun8i-v3-rtc" }, + { .compatible = "allwinner,sun50i-h5-rtc" }, + { .compatible = "allwinner,sun50i-h6-rtc" }, + { .compatible = "allwinner,sun50i-h616-rtc", + .data = (void *)RTC_LINEAR_DAY }, + { .compatible = "allwinner,sun50i-r329-rtc", + .data = (void *)RTC_LINEAR_DAY }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids); + +static struct platform_driver sun6i_rtc_driver = { + .probe = sun6i_rtc_probe, + .driver = { + .name = "sun6i-rtc", + .of_match_table = sun6i_rtc_dt_ids, + .pm = &sun6i_rtc_pm_ops, + }, +}; +builtin_platform_driver(sun6i_rtc_driver); diff --git a/drivers/rtc/rtc-sunplus.c b/drivers/rtc/rtc-sunplus.c new file mode 100644 index 000000000..519a06e72 --- /dev/null +++ b/drivers/rtc/rtc-sunplus.c @@ -0,0 +1,359 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * The RTC driver for Sunplus SP7021 + * + * Copyright (C) 2019 Sunplus Technology Inc., All rights reseerved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RTC_REG_NAME "rtc" + +#define RTC_CTRL 0x40 +#define TIMER_FREEZE_MASK_BIT BIT(5 + 16) +#define TIMER_FREEZE BIT(5) +#define DIS_SYS_RST_RTC_MASK_BIT BIT(4 + 16) +#define DIS_SYS_RST_RTC BIT(4) +#define RTC32K_MODE_RESET_MASK_BIT BIT(3 + 16) +#define RTC32K_MODE_RESET BIT(3) +#define ALARM_EN_OVERDUE_MASK_BIT BIT(2 + 16) +#define ALARM_EN_OVERDUE BIT(2) +#define ALARM_EN_PMC_MASK_BIT BIT(1 + 16) +#define ALARM_EN_PMC BIT(1) +#define ALARM_EN_MASK_BIT BIT(0 + 16) +#define ALARM_EN BIT(0) +#define RTC_TIMER_OUT 0x44 +#define RTC_DIVIDER 0x48 +#define RTC_TIMER_SET 0x4c +#define RTC_ALARM_SET 0x50 +#define RTC_USER_DATA 0x54 +#define RTC_RESET_RECORD 0x58 +#define RTC_BATT_CHARGE_CTRL 0x5c +#define BAT_CHARGE_RSEL_MASK_BIT GENMASK(3 + 16, 2 + 16) +#define BAT_CHARGE_RSEL_MASK GENMASK(3, 2) +#define BAT_CHARGE_RSEL_2K_OHM FIELD_PREP(BAT_CHARGE_RSEL_MASK, 0) +#define BAT_CHARGE_RSEL_250_OHM FIELD_PREP(BAT_CHARGE_RSEL_MASK, 1) +#define BAT_CHARGE_RSEL_50_OHM FIELD_PREP(BAT_CHARGE_RSEL_MASK, 2) +#define BAT_CHARGE_RSEL_0_OHM FIELD_PREP(BAT_CHARGE_RSEL_MASK, 3) +#define BAT_CHARGE_DSEL_MASK_BIT BIT(1 + 16) +#define BAT_CHARGE_DSEL_MASK GENMASK(1, 1) +#define BAT_CHARGE_DSEL_ON FIELD_PREP(BAT_CHARGE_DSEL_MASK, 0) +#define BAT_CHARGE_DSEL_OFF FIELD_PREP(BAT_CHARGE_DSEL_MASK, 1) +#define BAT_CHARGE_EN_MASK_BIT BIT(0 + 16) +#define BAT_CHARGE_EN BIT(0) +#define RTC_TRIM_CTRL 0x60 + +struct sunplus_rtc { + struct rtc_device *rtc; + struct resource *res; + struct clk *rtcclk; + struct reset_control *rstc; + void __iomem *reg_base; + int irq; +}; + +static void sp_get_seconds(struct device *dev, unsigned long *secs) +{ + struct sunplus_rtc *sp_rtc = dev_get_drvdata(dev); + + *secs = (unsigned long)readl(sp_rtc->reg_base + RTC_TIMER_OUT); +} + +static void sp_set_seconds(struct device *dev, unsigned long secs) +{ + struct sunplus_rtc *sp_rtc = dev_get_drvdata(dev); + + writel((u32)secs, sp_rtc->reg_base + RTC_TIMER_SET); +} + +static int sp_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + unsigned long secs; + + sp_get_seconds(dev, &secs); + rtc_time64_to_tm(secs, tm); + + return 0; +} + +static int sp_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + unsigned long secs; + + secs = rtc_tm_to_time64(tm); + dev_dbg(dev, "%s, secs = %lu\n", __func__, secs); + sp_set_seconds(dev, secs); + + return 0; +} + +static int sp_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct sunplus_rtc *sp_rtc = dev_get_drvdata(dev); + unsigned long alarm_time; + + alarm_time = rtc_tm_to_time64(&alrm->time); + dev_dbg(dev, "%s, alarm_time: %u\n", __func__, (u32)(alarm_time)); + writel((u32)alarm_time, sp_rtc->reg_base + RTC_ALARM_SET); + + return 0; +} + +static int sp_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct sunplus_rtc *sp_rtc = dev_get_drvdata(dev); + unsigned int alarm_time; + + alarm_time = readl(sp_rtc->reg_base + RTC_ALARM_SET); + dev_dbg(dev, "%s, alarm_time: %u\n", __func__, alarm_time); + + if (alarm_time == 0) + alrm->enabled = 0; + else + alrm->enabled = 1; + + rtc_time64_to_tm((unsigned long)(alarm_time), &alrm->time); + + return 0; +} + +static int sp_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct sunplus_rtc *sp_rtc = dev_get_drvdata(dev); + + if (enabled) + writel((TIMER_FREEZE_MASK_BIT | DIS_SYS_RST_RTC_MASK_BIT | + RTC32K_MODE_RESET_MASK_BIT | ALARM_EN_OVERDUE_MASK_BIT | + ALARM_EN_PMC_MASK_BIT | ALARM_EN_MASK_BIT) | + (DIS_SYS_RST_RTC | ALARM_EN_OVERDUE | ALARM_EN_PMC | ALARM_EN), + sp_rtc->reg_base + RTC_CTRL); + else + writel((ALARM_EN_OVERDUE_MASK_BIT | ALARM_EN_PMC_MASK_BIT | ALARM_EN_MASK_BIT) | + 0x0, sp_rtc->reg_base + RTC_CTRL); + + return 0; +} + +static const struct rtc_class_ops sp_rtc_ops = { + .read_time = sp_rtc_read_time, + .set_time = sp_rtc_set_time, + .set_alarm = sp_rtc_set_alarm, + .read_alarm = sp_rtc_read_alarm, + .alarm_irq_enable = sp_rtc_alarm_irq_enable, +}; + +static irqreturn_t sp_rtc_irq_handler(int irq, void *dev_id) +{ + struct platform_device *plat_dev = dev_id; + struct sunplus_rtc *sp_rtc = dev_get_drvdata(&plat_dev->dev); + + rtc_update_irq(sp_rtc->rtc, 1, RTC_IRQF | RTC_AF); + dev_dbg(&plat_dev->dev, "[RTC] ALARM INT\n"); + + return IRQ_HANDLED; +} + +/* + * ------------------------------------------------------------------------------------- + * bat_charge_rsel bat_charge_dsel bat_charge_en Remarks + * x x 0 Disable + * 0 0 1 0.86mA (2K Ohm with diode) + * 1 0 1 1.81mA (250 Ohm with diode) + * 2 0 1 2.07mA (50 Ohm with diode) + * 3 0 1 16.0mA (0 Ohm with diode) + * 0 1 1 1.36mA (2K Ohm without diode) + * 1 1 1 3.99mA (250 Ohm without diode) + * 2 1 1 4.41mA (50 Ohm without diode) + * 3 1 1 16.0mA (0 Ohm without diode) + * ------------------------------------------------------------------------------------- + */ +static void sp_rtc_set_trickle_charger(struct device dev) +{ + struct sunplus_rtc *sp_rtc = dev_get_drvdata(&dev); + u32 ohms, rsel; + u32 chargeable; + + if (of_property_read_u32(dev.of_node, "trickle-resistor-ohms", &ohms) || + of_property_read_u32(dev.of_node, "aux-voltage-chargeable", &chargeable)) { + dev_warn(&dev, "battery charger disabled\n"); + return; + } + + switch (ohms) { + case 2000: + rsel = BAT_CHARGE_RSEL_2K_OHM; + break; + case 250: + rsel = BAT_CHARGE_RSEL_250_OHM; + break; + case 50: + rsel = BAT_CHARGE_RSEL_50_OHM; + break; + case 0: + rsel = BAT_CHARGE_RSEL_0_OHM; + break; + default: + dev_err(&dev, "invalid charger resistor value (%d)\n", ohms); + return; + } + + writel(BAT_CHARGE_RSEL_MASK_BIT | rsel, sp_rtc->reg_base + RTC_BATT_CHARGE_CTRL); + + switch (chargeable) { + case 0: + writel(BAT_CHARGE_DSEL_MASK_BIT | BAT_CHARGE_DSEL_OFF, + sp_rtc->reg_base + RTC_BATT_CHARGE_CTRL); + break; + case 1: + writel(BAT_CHARGE_DSEL_MASK_BIT | BAT_CHARGE_DSEL_ON, + sp_rtc->reg_base + RTC_BATT_CHARGE_CTRL); + break; + default: + dev_err(&dev, "invalid aux-voltage-chargeable value (%d)\n", chargeable); + return; + } + + writel(BAT_CHARGE_EN_MASK_BIT | BAT_CHARGE_EN, sp_rtc->reg_base + RTC_BATT_CHARGE_CTRL); +} + +static int sp_rtc_probe(struct platform_device *plat_dev) +{ + struct sunplus_rtc *sp_rtc; + int ret; + + sp_rtc = devm_kzalloc(&plat_dev->dev, sizeof(*sp_rtc), GFP_KERNEL); + if (!sp_rtc) + return -ENOMEM; + + sp_rtc->reg_base = devm_platform_ioremap_resource_byname(plat_dev, RTC_REG_NAME); + if (IS_ERR(sp_rtc->reg_base)) + return dev_err_probe(&plat_dev->dev, PTR_ERR(sp_rtc->reg_base), + "%s devm_ioremap_resource fail\n", RTC_REG_NAME); + dev_dbg(&plat_dev->dev, "res = %pR, reg_base = %p\n", + sp_rtc->res, sp_rtc->reg_base); + + sp_rtc->irq = platform_get_irq(plat_dev, 0); + if (sp_rtc->irq < 0) + return sp_rtc->irq; + + ret = devm_request_irq(&plat_dev->dev, sp_rtc->irq, sp_rtc_irq_handler, + IRQF_TRIGGER_RISING, "rtc irq", plat_dev); + if (ret) + return dev_err_probe(&plat_dev->dev, ret, "devm_request_irq failed:\n"); + + sp_rtc->rtcclk = devm_clk_get(&plat_dev->dev, NULL); + if (IS_ERR(sp_rtc->rtcclk)) + return dev_err_probe(&plat_dev->dev, PTR_ERR(sp_rtc->rtcclk), + "devm_clk_get fail\n"); + + sp_rtc->rstc = devm_reset_control_get_exclusive(&plat_dev->dev, NULL); + if (IS_ERR(sp_rtc->rstc)) + return dev_err_probe(&plat_dev->dev, PTR_ERR(sp_rtc->rstc), + "failed to retrieve reset controller\n"); + + ret = clk_prepare_enable(sp_rtc->rtcclk); + if (ret) + goto free_clk; + + ret = reset_control_deassert(sp_rtc->rstc); + if (ret) + goto free_reset_assert; + + device_init_wakeup(&plat_dev->dev, true); + dev_set_drvdata(&plat_dev->dev, sp_rtc); + + sp_rtc->rtc = devm_rtc_allocate_device(&plat_dev->dev); + if (IS_ERR(sp_rtc->rtc)) { + ret = PTR_ERR(sp_rtc->rtc); + goto free_reset_assert; + } + + sp_rtc->rtc->range_max = U32_MAX; + sp_rtc->rtc->range_min = 0; + sp_rtc->rtc->ops = &sp_rtc_ops; + + ret = devm_rtc_register_device(sp_rtc->rtc); + if (ret) + goto free_reset_assert; + + /* Setup trickle charger */ + if (plat_dev->dev.of_node) + sp_rtc_set_trickle_charger(plat_dev->dev); + + /* Keep RTC from system reset */ + writel(DIS_SYS_RST_RTC_MASK_BIT | DIS_SYS_RST_RTC, sp_rtc->reg_base + RTC_CTRL); + + return 0; + +free_reset_assert: + reset_control_assert(sp_rtc->rstc); +free_clk: + clk_disable_unprepare(sp_rtc->rtcclk); + + return ret; +} + +static void sp_rtc_remove(struct platform_device *plat_dev) +{ + struct sunplus_rtc *sp_rtc = dev_get_drvdata(&plat_dev->dev); + + device_init_wakeup(&plat_dev->dev, false); + reset_control_assert(sp_rtc->rstc); + clk_disable_unprepare(sp_rtc->rtcclk); +} + +#ifdef CONFIG_PM_SLEEP +static int sp_rtc_suspend(struct device *dev) +{ + struct sunplus_rtc *sp_rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(sp_rtc->irq); + + return 0; +} + +static int sp_rtc_resume(struct device *dev) +{ + struct sunplus_rtc *sp_rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(sp_rtc->irq); + + return 0; +} +#endif + +static const struct of_device_id sp_rtc_of_match[] = { + { .compatible = "sunplus,sp7021-rtc" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sp_rtc_of_match); + +static SIMPLE_DEV_PM_OPS(sp_rtc_pm_ops, sp_rtc_suspend, sp_rtc_resume); + +static struct platform_driver sp_rtc_driver = { + .probe = sp_rtc_probe, + .remove = sp_rtc_remove, + .driver = { + .name = "sp7021-rtc", + .of_match_table = sp_rtc_of_match, + .pm = &sp_rtc_pm_ops, + }, +}; +module_platform_driver(sp_rtc_driver); + +MODULE_AUTHOR("Vincent Shih "); +MODULE_DESCRIPTION("Sunplus RTC driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/rtc/rtc-sunxi.c b/drivers/rtc/rtc-sunxi.c new file mode 100644 index 000000000..5cab9953c --- /dev/null +++ b/drivers/rtc/rtc-sunxi.c @@ -0,0 +1,486 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * An RTC driver for Allwinner A10/A20 + * + * Copyright (c) 2013, Carlo Caione + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SUNXI_LOSC_CTRL 0x0000 +#define SUNXI_LOSC_CTRL_RTC_HMS_ACC BIT(8) +#define SUNXI_LOSC_CTRL_RTC_YMD_ACC BIT(7) + +#define SUNXI_RTC_YMD 0x0004 + +#define SUNXI_RTC_HMS 0x0008 + +#define SUNXI_ALRM_DHMS 0x000c + +#define SUNXI_ALRM_EN 0x0014 +#define SUNXI_ALRM_EN_CNT_EN BIT(8) + +#define SUNXI_ALRM_IRQ_EN 0x0018 +#define SUNXI_ALRM_IRQ_EN_CNT_IRQ_EN BIT(0) + +#define SUNXI_ALRM_IRQ_STA 0x001c +#define SUNXI_ALRM_IRQ_STA_CNT_IRQ_PEND BIT(0) + +#define SUNXI_MASK_DH 0x0000001f +#define SUNXI_MASK_SM 0x0000003f +#define SUNXI_MASK_M 0x0000000f +#define SUNXI_MASK_LY 0x00000001 +#define SUNXI_MASK_D 0x00000ffe +#define SUNXI_MASK_M 0x0000000f + +#define SUNXI_GET(x, mask, shift) (((x) & ((mask) << (shift))) \ + >> (shift)) + +#define SUNXI_SET(x, mask, shift) (((x) & (mask)) << (shift)) + +/* + * Get date values + */ +#define SUNXI_DATE_GET_DAY_VALUE(x) SUNXI_GET(x, SUNXI_MASK_DH, 0) +#define SUNXI_DATE_GET_MON_VALUE(x) SUNXI_GET(x, SUNXI_MASK_M, 8) +#define SUNXI_DATE_GET_YEAR_VALUE(x, mask) SUNXI_GET(x, mask, 16) + +/* + * Get time values + */ +#define SUNXI_TIME_GET_SEC_VALUE(x) SUNXI_GET(x, SUNXI_MASK_SM, 0) +#define SUNXI_TIME_GET_MIN_VALUE(x) SUNXI_GET(x, SUNXI_MASK_SM, 8) +#define SUNXI_TIME_GET_HOUR_VALUE(x) SUNXI_GET(x, SUNXI_MASK_DH, 16) + +/* + * Get alarm values + */ +#define SUNXI_ALRM_GET_SEC_VALUE(x) SUNXI_GET(x, SUNXI_MASK_SM, 0) +#define SUNXI_ALRM_GET_MIN_VALUE(x) SUNXI_GET(x, SUNXI_MASK_SM, 8) +#define SUNXI_ALRM_GET_HOUR_VALUE(x) SUNXI_GET(x, SUNXI_MASK_DH, 16) + +/* + * Set date values + */ +#define SUNXI_DATE_SET_DAY_VALUE(x) SUNXI_DATE_GET_DAY_VALUE(x) +#define SUNXI_DATE_SET_MON_VALUE(x) SUNXI_SET(x, SUNXI_MASK_M, 8) +#define SUNXI_DATE_SET_YEAR_VALUE(x, mask) SUNXI_SET(x, mask, 16) +#define SUNXI_LEAP_SET_VALUE(x, shift) SUNXI_SET(x, SUNXI_MASK_LY, shift) + +/* + * Set time values + */ +#define SUNXI_TIME_SET_SEC_VALUE(x) SUNXI_TIME_GET_SEC_VALUE(x) +#define SUNXI_TIME_SET_MIN_VALUE(x) SUNXI_SET(x, SUNXI_MASK_SM, 8) +#define SUNXI_TIME_SET_HOUR_VALUE(x) SUNXI_SET(x, SUNXI_MASK_DH, 16) + +/* + * Set alarm values + */ +#define SUNXI_ALRM_SET_SEC_VALUE(x) SUNXI_ALRM_GET_SEC_VALUE(x) +#define SUNXI_ALRM_SET_MIN_VALUE(x) SUNXI_SET(x, SUNXI_MASK_SM, 8) +#define SUNXI_ALRM_SET_HOUR_VALUE(x) SUNXI_SET(x, SUNXI_MASK_DH, 16) +#define SUNXI_ALRM_SET_DAY_VALUE(x) SUNXI_SET(x, SUNXI_MASK_D, 21) + +/* + * Time unit conversions + */ +#define SEC_IN_MIN 60 +#define SEC_IN_HOUR (60 * SEC_IN_MIN) +#define SEC_IN_DAY (24 * SEC_IN_HOUR) + +/* + * The year parameter passed to the driver is usually an offset relative to + * the year 1900. This macro is used to convert this offset to another one + * relative to the minimum year allowed by the hardware. + */ +#define SUNXI_YEAR_OFF(x) ((x)->min - 1900) + +/* + * min and max year are arbitrary set considering the limited range of the + * hardware register field + */ +struct sunxi_rtc_data_year { + unsigned int min; /* min year allowed */ + unsigned int max; /* max year allowed */ + unsigned int mask; /* mask for the year field */ + unsigned char leap_shift; /* bit shift to get the leap year */ +}; + +static const struct sunxi_rtc_data_year data_year_param[] = { + [0] = { + .min = 2010, + .max = 2073, + .mask = 0x3f, + .leap_shift = 22, + }, + [1] = { + .min = 1970, + .max = 2225, + .mask = 0xff, + .leap_shift = 24, + }, +}; + +struct sunxi_rtc_dev { + struct rtc_device *rtc; + struct device *dev; + const struct sunxi_rtc_data_year *data_year; + void __iomem *base; + int irq; +}; + +static irqreturn_t sunxi_rtc_alarmirq(int irq, void *id) +{ + struct sunxi_rtc_dev *chip = (struct sunxi_rtc_dev *) id; + u32 val; + + val = readl(chip->base + SUNXI_ALRM_IRQ_STA); + + if (val & SUNXI_ALRM_IRQ_STA_CNT_IRQ_PEND) { + val |= SUNXI_ALRM_IRQ_STA_CNT_IRQ_PEND; + writel(val, chip->base + SUNXI_ALRM_IRQ_STA); + + rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static void sunxi_rtc_setaie(unsigned int to, struct sunxi_rtc_dev *chip) +{ + u32 alrm_val = 0; + u32 alrm_irq_val = 0; + + if (to) { + alrm_val = readl(chip->base + SUNXI_ALRM_EN); + alrm_val |= SUNXI_ALRM_EN_CNT_EN; + + alrm_irq_val = readl(chip->base + SUNXI_ALRM_IRQ_EN); + alrm_irq_val |= SUNXI_ALRM_IRQ_EN_CNT_IRQ_EN; + } else { + writel(SUNXI_ALRM_IRQ_STA_CNT_IRQ_PEND, + chip->base + SUNXI_ALRM_IRQ_STA); + } + + writel(alrm_val, chip->base + SUNXI_ALRM_EN); + writel(alrm_irq_val, chip->base + SUNXI_ALRM_IRQ_EN); +} + +static int sunxi_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct sunxi_rtc_dev *chip = dev_get_drvdata(dev); + struct rtc_time *alrm_tm = &wkalrm->time; + u32 alrm; + u32 alrm_en; + u32 date; + + alrm = readl(chip->base + SUNXI_ALRM_DHMS); + date = readl(chip->base + SUNXI_RTC_YMD); + + alrm_tm->tm_sec = SUNXI_ALRM_GET_SEC_VALUE(alrm); + alrm_tm->tm_min = SUNXI_ALRM_GET_MIN_VALUE(alrm); + alrm_tm->tm_hour = SUNXI_ALRM_GET_HOUR_VALUE(alrm); + + alrm_tm->tm_mday = SUNXI_DATE_GET_DAY_VALUE(date); + alrm_tm->tm_mon = SUNXI_DATE_GET_MON_VALUE(date); + alrm_tm->tm_year = SUNXI_DATE_GET_YEAR_VALUE(date, + chip->data_year->mask); + + alrm_tm->tm_mon -= 1; + + /* + * switch from (data_year->min)-relative offset to + * a (1900)-relative one + */ + alrm_tm->tm_year += SUNXI_YEAR_OFF(chip->data_year); + + alrm_en = readl(chip->base + SUNXI_ALRM_IRQ_EN); + if (alrm_en & SUNXI_ALRM_EN_CNT_EN) + wkalrm->enabled = 1; + + return 0; +} + +static int sunxi_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) +{ + struct sunxi_rtc_dev *chip = dev_get_drvdata(dev); + u32 date, time; + + /* + * read again in case it changes + */ + do { + date = readl(chip->base + SUNXI_RTC_YMD); + time = readl(chip->base + SUNXI_RTC_HMS); + } while ((date != readl(chip->base + SUNXI_RTC_YMD)) || + (time != readl(chip->base + SUNXI_RTC_HMS))); + + rtc_tm->tm_sec = SUNXI_TIME_GET_SEC_VALUE(time); + rtc_tm->tm_min = SUNXI_TIME_GET_MIN_VALUE(time); + rtc_tm->tm_hour = SUNXI_TIME_GET_HOUR_VALUE(time); + + rtc_tm->tm_mday = SUNXI_DATE_GET_DAY_VALUE(date); + rtc_tm->tm_mon = SUNXI_DATE_GET_MON_VALUE(date); + rtc_tm->tm_year = SUNXI_DATE_GET_YEAR_VALUE(date, + chip->data_year->mask); + + rtc_tm->tm_mon -= 1; + + /* + * switch from (data_year->min)-relative offset to + * a (1900)-relative one + */ + rtc_tm->tm_year += SUNXI_YEAR_OFF(chip->data_year); + + return 0; +} + +static int sunxi_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct sunxi_rtc_dev *chip = dev_get_drvdata(dev); + struct rtc_time *alrm_tm = &wkalrm->time; + struct rtc_time tm_now; + u32 alrm; + time64_t diff; + unsigned long time_gap; + unsigned long time_gap_day; + unsigned long time_gap_hour; + unsigned long time_gap_min; + int ret; + + ret = sunxi_rtc_gettime(dev, &tm_now); + if (ret < 0) { + dev_err(dev, "Error in getting time\n"); + return -EINVAL; + } + + diff = rtc_tm_sub(alrm_tm, &tm_now); + if (diff <= 0) { + dev_err(dev, "Date to set in the past\n"); + return -EINVAL; + } + + if (diff > 255 * SEC_IN_DAY) { + dev_err(dev, "Day must be in the range 0 - 255\n"); + return -EINVAL; + } + + time_gap = diff; + time_gap_day = time_gap / SEC_IN_DAY; + time_gap -= time_gap_day * SEC_IN_DAY; + time_gap_hour = time_gap / SEC_IN_HOUR; + time_gap -= time_gap_hour * SEC_IN_HOUR; + time_gap_min = time_gap / SEC_IN_MIN; + time_gap -= time_gap_min * SEC_IN_MIN; + + sunxi_rtc_setaie(0, chip); + writel(0, chip->base + SUNXI_ALRM_DHMS); + usleep_range(100, 300); + + alrm = SUNXI_ALRM_SET_SEC_VALUE(time_gap) | + SUNXI_ALRM_SET_MIN_VALUE(time_gap_min) | + SUNXI_ALRM_SET_HOUR_VALUE(time_gap_hour) | + SUNXI_ALRM_SET_DAY_VALUE(time_gap_day); + writel(alrm, chip->base + SUNXI_ALRM_DHMS); + + writel(0, chip->base + SUNXI_ALRM_IRQ_EN); + writel(SUNXI_ALRM_IRQ_EN_CNT_IRQ_EN, chip->base + SUNXI_ALRM_IRQ_EN); + + sunxi_rtc_setaie(wkalrm->enabled, chip); + + return 0; +} + +static int sunxi_rtc_wait(struct sunxi_rtc_dev *chip, int offset, + unsigned int mask, unsigned int ms_timeout) +{ + const unsigned long timeout = jiffies + msecs_to_jiffies(ms_timeout); + u32 reg; + + do { + reg = readl(chip->base + offset); + reg &= mask; + + if (reg == mask) + return 0; + + } while (time_before(jiffies, timeout)); + + return -ETIMEDOUT; +} + +static int sunxi_rtc_settime(struct device *dev, struct rtc_time *rtc_tm) +{ + struct sunxi_rtc_dev *chip = dev_get_drvdata(dev); + u32 date = 0; + u32 time = 0; + unsigned int year; + + /* + * the input rtc_tm->tm_year is the offset relative to 1900. We use + * the SUNXI_YEAR_OFF macro to rebase it with respect to the min year + * allowed by the hardware + */ + + year = rtc_tm->tm_year + 1900; + if (year < chip->data_year->min || year > chip->data_year->max) { + dev_err(dev, "rtc only supports year in range %u - %u\n", + chip->data_year->min, chip->data_year->max); + return -EINVAL; + } + + rtc_tm->tm_year -= SUNXI_YEAR_OFF(chip->data_year); + rtc_tm->tm_mon += 1; + + date = SUNXI_DATE_SET_DAY_VALUE(rtc_tm->tm_mday) | + SUNXI_DATE_SET_MON_VALUE(rtc_tm->tm_mon) | + SUNXI_DATE_SET_YEAR_VALUE(rtc_tm->tm_year, + chip->data_year->mask); + + if (is_leap_year(year)) + date |= SUNXI_LEAP_SET_VALUE(1, chip->data_year->leap_shift); + + time = SUNXI_TIME_SET_SEC_VALUE(rtc_tm->tm_sec) | + SUNXI_TIME_SET_MIN_VALUE(rtc_tm->tm_min) | + SUNXI_TIME_SET_HOUR_VALUE(rtc_tm->tm_hour); + + writel(0, chip->base + SUNXI_RTC_HMS); + writel(0, chip->base + SUNXI_RTC_YMD); + + writel(time, chip->base + SUNXI_RTC_HMS); + + /* + * After writing the RTC HH-MM-SS register, the + * SUNXI_LOSC_CTRL_RTC_HMS_ACC bit is set and it will not + * be cleared until the real writing operation is finished + */ + + if (sunxi_rtc_wait(chip, SUNXI_LOSC_CTRL, + SUNXI_LOSC_CTRL_RTC_HMS_ACC, 50)) { + dev_err(dev, "Failed to set rtc time.\n"); + return -1; + } + + writel(date, chip->base + SUNXI_RTC_YMD); + + /* + * After writing the RTC YY-MM-DD register, the + * SUNXI_LOSC_CTRL_RTC_YMD_ACC bit is set and it will not + * be cleared until the real writing operation is finished + */ + + if (sunxi_rtc_wait(chip, SUNXI_LOSC_CTRL, + SUNXI_LOSC_CTRL_RTC_YMD_ACC, 50)) { + dev_err(dev, "Failed to set rtc time.\n"); + return -1; + } + + return 0; +} + +static int sunxi_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct sunxi_rtc_dev *chip = dev_get_drvdata(dev); + + if (!enabled) + sunxi_rtc_setaie(enabled, chip); + + return 0; +} + +static const struct rtc_class_ops sunxi_rtc_ops = { + .read_time = sunxi_rtc_gettime, + .set_time = sunxi_rtc_settime, + .read_alarm = sunxi_rtc_getalarm, + .set_alarm = sunxi_rtc_setalarm, + .alarm_irq_enable = sunxi_rtc_alarm_irq_enable +}; + +static const struct of_device_id sunxi_rtc_dt_ids[] = { + { .compatible = "allwinner,sun4i-a10-rtc", .data = &data_year_param[0] }, + { .compatible = "allwinner,sun7i-a20-rtc", .data = &data_year_param[1] }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, sunxi_rtc_dt_ids); + +static int sunxi_rtc_probe(struct platform_device *pdev) +{ + struct sunxi_rtc_dev *chip; + int ret; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + platform_set_drvdata(pdev, chip); + chip->dev = &pdev->dev; + + chip->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(chip->rtc)) + return PTR_ERR(chip->rtc); + + chip->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(chip->base)) + return PTR_ERR(chip->base); + + chip->irq = platform_get_irq(pdev, 0); + if (chip->irq < 0) + return chip->irq; + ret = devm_request_irq(&pdev->dev, chip->irq, sunxi_rtc_alarmirq, + 0, dev_name(&pdev->dev), chip); + if (ret) { + dev_err(&pdev->dev, "Could not request IRQ\n"); + return ret; + } + + chip->data_year = of_device_get_match_data(&pdev->dev); + if (!chip->data_year) { + dev_err(&pdev->dev, "Unable to setup RTC data\n"); + return -ENODEV; + } + + /* clear the alarm count value */ + writel(0, chip->base + SUNXI_ALRM_DHMS); + + /* disable alarm, not generate irq pending */ + writel(0, chip->base + SUNXI_ALRM_EN); + + /* disable alarm week/cnt irq, unset to cpu */ + writel(0, chip->base + SUNXI_ALRM_IRQ_EN); + + /* clear alarm week/cnt irq pending */ + writel(SUNXI_ALRM_IRQ_STA_CNT_IRQ_PEND, chip->base + + SUNXI_ALRM_IRQ_STA); + + chip->rtc->ops = &sunxi_rtc_ops; + + return devm_rtc_register_device(chip->rtc); +} + +static struct platform_driver sunxi_rtc_driver = { + .probe = sunxi_rtc_probe, + .driver = { + .name = "sunxi-rtc", + .of_match_table = sunxi_rtc_dt_ids, + }, +}; + +module_platform_driver(sunxi_rtc_driver); + +MODULE_DESCRIPTION("sunxi RTC driver"); +MODULE_AUTHOR("Carlo Caione "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-tegra.c b/drivers/rtc/rtc-tegra.c new file mode 100644 index 000000000..f16405c39 --- /dev/null +++ b/drivers/rtc/rtc-tegra.c @@ -0,0 +1,402 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * An RTC driver for the NVIDIA Tegra 200 series internal RTC. + * + * Copyright (c) 2010-2019, NVIDIA Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Set to 1 = busy every eight 32 kHz clocks during copy of sec+msec to AHB. */ +#define TEGRA_RTC_REG_BUSY 0x004 +#define TEGRA_RTC_REG_SECONDS 0x008 +/* When msec is read, the seconds are buffered into shadow seconds. */ +#define TEGRA_RTC_REG_SHADOW_SECONDS 0x00c +#define TEGRA_RTC_REG_MILLI_SECONDS 0x010 +#define TEGRA_RTC_REG_SECONDS_ALARM0 0x014 +#define TEGRA_RTC_REG_SECONDS_ALARM1 0x018 +#define TEGRA_RTC_REG_MILLI_SECONDS_ALARM0 0x01c +#define TEGRA_RTC_REG_INTR_MASK 0x028 +/* write 1 bits to clear status bits */ +#define TEGRA_RTC_REG_INTR_STATUS 0x02c + +/* bits in INTR_MASK */ +#define TEGRA_RTC_INTR_MASK_MSEC_CDN_ALARM (1<<4) +#define TEGRA_RTC_INTR_MASK_SEC_CDN_ALARM (1<<3) +#define TEGRA_RTC_INTR_MASK_MSEC_ALARM (1<<2) +#define TEGRA_RTC_INTR_MASK_SEC_ALARM1 (1<<1) +#define TEGRA_RTC_INTR_MASK_SEC_ALARM0 (1<<0) + +/* bits in INTR_STATUS */ +#define TEGRA_RTC_INTR_STATUS_MSEC_CDN_ALARM (1<<4) +#define TEGRA_RTC_INTR_STATUS_SEC_CDN_ALARM (1<<3) +#define TEGRA_RTC_INTR_STATUS_MSEC_ALARM (1<<2) +#define TEGRA_RTC_INTR_STATUS_SEC_ALARM1 (1<<1) +#define TEGRA_RTC_INTR_STATUS_SEC_ALARM0 (1<<0) + +struct tegra_rtc_info { + struct platform_device *pdev; + struct rtc_device *rtc; + void __iomem *base; /* NULL if not initialized */ + struct clk *clk; + int irq; /* alarm and periodic IRQ */ + spinlock_t lock; +}; + +/* + * RTC hardware is busy when it is updating its values over AHB once every + * eight 32 kHz clocks (~250 us). Outside of these updates the CPU is free to + * write. CPU is always free to read. + */ +static inline u32 tegra_rtc_check_busy(struct tegra_rtc_info *info) +{ + return readl(info->base + TEGRA_RTC_REG_BUSY) & 1; +} + +/* + * Wait for hardware to be ready for writing. This function tries to maximize + * the amount of time before the next update. It does this by waiting for the + * RTC to become busy with its periodic update, then returning once the RTC + * first becomes not busy. + * + * This periodic update (where the seconds and milliseconds are copied to the + * AHB side) occurs every eight 32 kHz clocks (~250 us). The behavior of this + * function allows us to make some assumptions without introducing a race, + * because 250 us is plenty of time to read/write a value. + */ +static int tegra_rtc_wait_while_busy(struct device *dev) +{ + struct tegra_rtc_info *info = dev_get_drvdata(dev); + int retries = 500; /* ~490 us is the worst case, ~250 us is best */ + + /* + * First wait for the RTC to become busy. This is when it posts its + * updated seconds+msec registers to AHB side. + */ + while (tegra_rtc_check_busy(info)) { + if (!retries--) + goto retry_failed; + + udelay(1); + } + + /* now we have about 250 us to manipulate registers */ + return 0; + +retry_failed: + dev_err(dev, "write failed: retry count exceeded\n"); + return -ETIMEDOUT; +} + +static int tegra_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct tegra_rtc_info *info = dev_get_drvdata(dev); + unsigned long flags; + u32 sec; + + /* + * RTC hardware copies seconds to shadow seconds when a read of + * milliseconds occurs. use a lock to keep other threads out. + */ + spin_lock_irqsave(&info->lock, flags); + + readl(info->base + TEGRA_RTC_REG_MILLI_SECONDS); + sec = readl(info->base + TEGRA_RTC_REG_SHADOW_SECONDS); + + spin_unlock_irqrestore(&info->lock, flags); + + rtc_time64_to_tm(sec, tm); + + dev_vdbg(dev, "time read as %u, %ptR\n", sec, tm); + + return 0; +} + +static int tegra_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct tegra_rtc_info *info = dev_get_drvdata(dev); + u32 sec; + int ret; + + /* convert tm to seconds */ + sec = rtc_tm_to_time64(tm); + + dev_vdbg(dev, "time set to %u, %ptR\n", sec, tm); + + /* seconds only written if wait succeeded */ + ret = tegra_rtc_wait_while_busy(dev); + if (!ret) + writel(sec, info->base + TEGRA_RTC_REG_SECONDS); + + dev_vdbg(dev, "time read back as %d\n", + readl(info->base + TEGRA_RTC_REG_SECONDS)); + + return ret; +} + +static int tegra_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct tegra_rtc_info *info = dev_get_drvdata(dev); + u32 sec, value; + + sec = readl(info->base + TEGRA_RTC_REG_SECONDS_ALARM0); + + if (sec == 0) { + /* alarm is disabled */ + alarm->enabled = 0; + } else { + /* alarm is enabled */ + alarm->enabled = 1; + rtc_time64_to_tm(sec, &alarm->time); + } + + value = readl(info->base + TEGRA_RTC_REG_INTR_STATUS); + alarm->pending = (value & TEGRA_RTC_INTR_STATUS_SEC_ALARM0) != 0; + + return 0; +} + +static int tegra_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct tegra_rtc_info *info = dev_get_drvdata(dev); + unsigned long flags; + u32 status; + + tegra_rtc_wait_while_busy(dev); + spin_lock_irqsave(&info->lock, flags); + + /* read the original value, and OR in the flag */ + status = readl(info->base + TEGRA_RTC_REG_INTR_MASK); + if (enabled) + status |= TEGRA_RTC_INTR_MASK_SEC_ALARM0; /* set it */ + else + status &= ~TEGRA_RTC_INTR_MASK_SEC_ALARM0; /* clear it */ + + writel(status, info->base + TEGRA_RTC_REG_INTR_MASK); + + spin_unlock_irqrestore(&info->lock, flags); + + return 0; +} + +static int tegra_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct tegra_rtc_info *info = dev_get_drvdata(dev); + u32 sec; + + if (alarm->enabled) + sec = rtc_tm_to_time64(&alarm->time); + else + sec = 0; + + tegra_rtc_wait_while_busy(dev); + writel(sec, info->base + TEGRA_RTC_REG_SECONDS_ALARM0); + dev_vdbg(dev, "alarm read back as %d\n", + readl(info->base + TEGRA_RTC_REG_SECONDS_ALARM0)); + + /* if successfully written and alarm is enabled ... */ + if (sec) { + tegra_rtc_alarm_irq_enable(dev, 1); + dev_vdbg(dev, "alarm set as %u, %ptR\n", sec, &alarm->time); + } else { + /* disable alarm if 0 or write error */ + dev_vdbg(dev, "alarm disabled\n"); + tegra_rtc_alarm_irq_enable(dev, 0); + } + + return 0; +} + +static int tegra_rtc_proc(struct device *dev, struct seq_file *seq) +{ + if (!dev || !dev->driver) + return 0; + + seq_printf(seq, "name\t\t: %s\n", dev_name(dev)); + + return 0; +} + +static irqreturn_t tegra_rtc_irq_handler(int irq, void *data) +{ + struct device *dev = data; + struct tegra_rtc_info *info = dev_get_drvdata(dev); + unsigned long events = 0; + u32 status; + + status = readl(info->base + TEGRA_RTC_REG_INTR_STATUS); + if (status) { + /* clear the interrupt masks and status on any IRQ */ + tegra_rtc_wait_while_busy(dev); + + spin_lock(&info->lock); + writel(0, info->base + TEGRA_RTC_REG_INTR_MASK); + writel(status, info->base + TEGRA_RTC_REG_INTR_STATUS); + spin_unlock(&info->lock); + } + + /* check if alarm */ + if (status & TEGRA_RTC_INTR_STATUS_SEC_ALARM0) + events |= RTC_IRQF | RTC_AF; + + /* check if periodic */ + if (status & TEGRA_RTC_INTR_STATUS_SEC_CDN_ALARM) + events |= RTC_IRQF | RTC_PF; + + rtc_update_irq(info->rtc, 1, events); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops tegra_rtc_ops = { + .read_time = tegra_rtc_read_time, + .set_time = tegra_rtc_set_time, + .read_alarm = tegra_rtc_read_alarm, + .set_alarm = tegra_rtc_set_alarm, + .proc = tegra_rtc_proc, + .alarm_irq_enable = tegra_rtc_alarm_irq_enable, +}; + +static const struct of_device_id tegra_rtc_dt_match[] = { + { .compatible = "nvidia,tegra20-rtc", }, + {} +}; +MODULE_DEVICE_TABLE(of, tegra_rtc_dt_match); + +static const struct acpi_device_id tegra_rtc_acpi_match[] = { + { "NVDA0280" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, tegra_rtc_acpi_match); + +static int tegra_rtc_probe(struct platform_device *pdev) +{ + struct tegra_rtc_info *info; + int ret; + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(info->base)) + return PTR_ERR(info->base); + + ret = platform_get_irq(pdev, 0); + if (ret <= 0) + return ret; + + info->irq = ret; + + info->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(info->rtc)) + return PTR_ERR(info->rtc); + + info->rtc->ops = &tegra_rtc_ops; + info->rtc->range_max = U32_MAX; + + if (dev_of_node(&pdev->dev)) { + info->clk = devm_clk_get_enabled(&pdev->dev, NULL); + if (IS_ERR(info->clk)) + return PTR_ERR(info->clk); + } + + /* set context info */ + info->pdev = pdev; + spin_lock_init(&info->lock); + + platform_set_drvdata(pdev, info); + + /* clear out the hardware */ + writel(0, info->base + TEGRA_RTC_REG_SECONDS_ALARM0); + writel(0xffffffff, info->base + TEGRA_RTC_REG_INTR_STATUS); + writel(0, info->base + TEGRA_RTC_REG_INTR_MASK); + + device_init_wakeup(&pdev->dev, true); + + ret = devm_request_irq(&pdev->dev, info->irq, tegra_rtc_irq_handler, + IRQF_TRIGGER_HIGH, dev_name(&pdev->dev), + &pdev->dev); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to request interrupt\n"); + + ret = devm_rtc_register_device(info->rtc); + if (ret) + return ret; + + dev_notice(&pdev->dev, "Tegra internal Real Time Clock\n"); + + return 0; +} + +static int tegra_rtc_suspend(struct device *dev) +{ + struct tegra_rtc_info *info = dev_get_drvdata(dev); + + tegra_rtc_wait_while_busy(dev); + + /* only use ALARM0 as a wake source */ + writel(0xffffffff, info->base + TEGRA_RTC_REG_INTR_STATUS); + writel(TEGRA_RTC_INTR_STATUS_SEC_ALARM0, + info->base + TEGRA_RTC_REG_INTR_MASK); + + dev_vdbg(dev, "alarm sec = %d\n", + readl(info->base + TEGRA_RTC_REG_SECONDS_ALARM0)); + + dev_vdbg(dev, "Suspend (device_may_wakeup=%d) IRQ:%d\n", + device_may_wakeup(dev), info->irq); + + /* leave the alarms on as a wake source */ + if (device_may_wakeup(dev)) + enable_irq_wake(info->irq); + + return 0; +} + +static int tegra_rtc_resume(struct device *dev) +{ + struct tegra_rtc_info *info = dev_get_drvdata(dev); + + dev_vdbg(dev, "Resume (device_may_wakeup=%d)\n", + device_may_wakeup(dev)); + + /* alarms were left on as a wake source, turn them off */ + if (device_may_wakeup(dev)) + disable_irq_wake(info->irq); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(tegra_rtc_pm_ops, tegra_rtc_suspend, tegra_rtc_resume); + +static void tegra_rtc_shutdown(struct platform_device *pdev) +{ + dev_vdbg(&pdev->dev, "disabling interrupts\n"); + tegra_rtc_alarm_irq_enable(&pdev->dev, 0); +} + +static struct platform_driver tegra_rtc_driver = { + .probe = tegra_rtc_probe, + .shutdown = tegra_rtc_shutdown, + .driver = { + .name = "tegra_rtc", + .of_match_table = tegra_rtc_dt_match, + .acpi_match_table = tegra_rtc_acpi_match, + .pm = pm_sleep_ptr(&tegra_rtc_pm_ops), + }, +}; +module_platform_driver(tegra_rtc_driver); + +MODULE_AUTHOR("Jon Mayo "); +MODULE_DESCRIPTION("driver for Tegra internal RTC"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-test.c b/drivers/rtc/rtc-test.c new file mode 100644 index 000000000..94f995feb --- /dev/null +++ b/drivers/rtc/rtc-test.c @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * An RTC test device/driver + * Copyright (C) 2005 Tower Technologies + * Author: Alessandro Zummo + */ + +#include +#include +#include +#include + +#define MAX_RTC_TEST 3 + +struct rtc_test_data { + struct rtc_device *rtc; + time64_t offset; + struct timer_list alarm; + bool alarm_en; +}; + +static struct platform_device *pdev[MAX_RTC_TEST]; + +static int test_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rtc_test_data *rtd = dev_get_drvdata(dev); + time64_t alarm; + + alarm = (rtd->alarm.expires - jiffies) / HZ; + alarm += ktime_get_real_seconds() + rtd->offset; + + rtc_time64_to_tm(alarm, &alrm->time); + alrm->enabled = rtd->alarm_en; + + return 0; +} + +static int test_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rtc_test_data *rtd = dev_get_drvdata(dev); + ktime_t timeout; + u64 expires; + + timeout = rtc_tm_to_time64(&alrm->time) - ktime_get_real_seconds(); + timeout -= rtd->offset; + + timer_delete(&rtd->alarm); + + expires = jiffies + timeout * HZ; + if (expires > U32_MAX) + expires = U32_MAX; + + rtd->alarm.expires = expires; + + if (alrm->enabled) + add_timer(&rtd->alarm); + + rtd->alarm_en = alrm->enabled; + + return 0; +} + +static int test_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct rtc_test_data *rtd = dev_get_drvdata(dev); + + rtc_time64_to_tm(ktime_get_real_seconds() + rtd->offset, tm); + + return 0; +} + +static int test_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rtc_test_data *rtd = dev_get_drvdata(dev); + + rtd->offset = rtc_tm_to_time64(tm) - ktime_get_real_seconds(); + + return 0; +} + +static int test_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) +{ + struct rtc_test_data *rtd = dev_get_drvdata(dev); + + rtd->alarm_en = enable; + if (enable) + add_timer(&rtd->alarm); + else + timer_delete(&rtd->alarm); + + return 0; +} + +static const struct rtc_class_ops test_rtc_ops_noalm = { + .read_time = test_rtc_read_time, + .set_time = test_rtc_set_time, + .alarm_irq_enable = test_rtc_alarm_irq_enable, +}; + +static const struct rtc_class_ops test_rtc_ops = { + .read_time = test_rtc_read_time, + .set_time = test_rtc_set_time, + .read_alarm = test_rtc_read_alarm, + .set_alarm = test_rtc_set_alarm, + .alarm_irq_enable = test_rtc_alarm_irq_enable, +}; + +static void test_rtc_alarm_handler(struct timer_list *t) +{ + struct rtc_test_data *rtd = timer_container_of(rtd, t, alarm); + + rtc_update_irq(rtd->rtc, 1, RTC_AF | RTC_IRQF); +} + +static int test_probe(struct platform_device *plat_dev) +{ + struct rtc_test_data *rtd; + + rtd = devm_kzalloc(&plat_dev->dev, sizeof(*rtd), GFP_KERNEL); + if (!rtd) + return -ENOMEM; + + platform_set_drvdata(plat_dev, rtd); + + rtd->rtc = devm_rtc_allocate_device(&plat_dev->dev); + if (IS_ERR(rtd->rtc)) + return PTR_ERR(rtd->rtc); + + switch (plat_dev->id) { + case 0: + rtd->rtc->ops = &test_rtc_ops_noalm; + break; + default: + rtd->rtc->ops = &test_rtc_ops; + device_init_wakeup(&plat_dev->dev, true); + } + + timer_setup(&rtd->alarm, test_rtc_alarm_handler, 0); + rtd->alarm.expires = 0; + + return devm_rtc_register_device(rtd->rtc); +} + +static struct platform_driver test_driver = { + .probe = test_probe, + .driver = { + .name = "rtc-test", + }, +}; + +static int __init test_init(void) +{ + int i, err; + + err = platform_driver_register(&test_driver); + if (err) + return err; + + err = -ENOMEM; + for (i = 0; i < MAX_RTC_TEST; i++) { + pdev[i] = platform_device_alloc("rtc-test", i); + if (!pdev[i]) + goto exit_free_mem; + } + + for (i = 0; i < MAX_RTC_TEST; i++) { + err = platform_device_add(pdev[i]); + if (err) + goto exit_device_del; + } + + return 0; + +exit_device_del: + for (; i > 0; i--) + platform_device_del(pdev[i - 1]); + +exit_free_mem: + for (i = 0; i < MAX_RTC_TEST; i++) + platform_device_put(pdev[i]); + + platform_driver_unregister(&test_driver); + return err; +} + +static void __exit test_exit(void) +{ + int i; + + for (i = 0; i < MAX_RTC_TEST; i++) + platform_device_unregister(pdev[i]); + + platform_driver_unregister(&test_driver); +} + +MODULE_AUTHOR("Alessandro Zummo "); +MODULE_DESCRIPTION("RTC test driver/device"); +MODULE_LICENSE("GPL v2"); + +module_init(test_init); +module_exit(test_exit); diff --git a/drivers/rtc/rtc-ti-k3.c b/drivers/rtc/rtc-ti-k3.c new file mode 100644 index 000000000..8df00319c --- /dev/null +++ b/drivers/rtc/rtc-ti-k3.c @@ -0,0 +1,670 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Texas Instruments K3 RTC driver + * + * Copyright (C) 2021-2022 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Registers */ +#define REG_K3RTC_S_CNT_LSW 0x08 +#define REG_K3RTC_S_CNT_MSW 0x0c +#define REG_K3RTC_COMP 0x10 +#define REG_K3RTC_ON_OFF_S_CNT_LSW 0x20 +#define REG_K3RTC_ON_OFF_S_CNT_MSW 0x24 +#define REG_K3RTC_SCRATCH0 0x30 +#define REG_K3RTC_SCRATCH7 0x4c +#define REG_K3RTC_GENERAL_CTL 0x50 +#define REG_K3RTC_IRQSTATUS_RAW_SYS 0x54 +#define REG_K3RTC_IRQSTATUS_SYS 0x58 +#define REG_K3RTC_IRQENABLE_SET_SYS 0x5c +#define REG_K3RTC_IRQENABLE_CLR_SYS 0x60 +#define REG_K3RTC_SYNCPEND 0x68 +#define REG_K3RTC_KICK0 0x70 +#define REG_K3RTC_KICK1 0x74 + +/* Freeze when lsw is read and unfreeze when msw is read */ +#define K3RTC_CNT_FMODE_S_CNT_VALUE (0x2 << 24) + +/* Magic values for lock/unlock */ +#define K3RTC_KICK0_UNLOCK_VALUE 0x83e70b13 +#define K3RTC_KICK1_UNLOCK_VALUE 0x95a4f1e0 + +/* Multiplier for ppb conversions */ +#define K3RTC_PPB_MULT (1000000000LL) +/* Min and max values supported with 'offset' interface (swapped sign) */ +#define K3RTC_MIN_OFFSET (-277761) +#define K3RTC_MAX_OFFSET (277778) + +static const struct regmap_config ti_k3_rtc_regmap_config = { + .name = "peripheral-registers", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = REG_K3RTC_KICK1, +}; + +enum ti_k3_rtc_fields { + K3RTC_KICK0, + K3RTC_KICK1, + K3RTC_S_CNT_LSW, + K3RTC_S_CNT_MSW, + K3RTC_O32K_OSC_DEP_EN, + K3RTC_UNLOCK, + K3RTC_CNT_FMODE, + K3RTC_PEND, + K3RTC_RELOAD_FROM_BBD, + K3RTC_COMP, + + K3RTC_ALM_S_CNT_LSW, + K3RTC_ALM_S_CNT_MSW, + K3RTC_IRQ_STATUS_RAW, + K3RTC_IRQ_STATUS, + K3RTC_IRQ_ENABLE_SET, + K3RTC_IRQ_ENABLE_CLR, + + K3RTC_IRQ_STATUS_ALT, + K3RTC_IRQ_ENABLE_CLR_ALT, + + K3_RTC_MAX_FIELDS +}; + +static const struct reg_field ti_rtc_reg_fields[] = { + [K3RTC_KICK0] = REG_FIELD(REG_K3RTC_KICK0, 0, 31), + [K3RTC_KICK1] = REG_FIELD(REG_K3RTC_KICK1, 0, 31), + [K3RTC_S_CNT_LSW] = REG_FIELD(REG_K3RTC_S_CNT_LSW, 0, 31), + [K3RTC_S_CNT_MSW] = REG_FIELD(REG_K3RTC_S_CNT_MSW, 0, 15), + [K3RTC_O32K_OSC_DEP_EN] = REG_FIELD(REG_K3RTC_GENERAL_CTL, 21, 21), + [K3RTC_UNLOCK] = REG_FIELD(REG_K3RTC_GENERAL_CTL, 23, 23), + [K3RTC_CNT_FMODE] = REG_FIELD(REG_K3RTC_GENERAL_CTL, 24, 25), + [K3RTC_PEND] = REG_FIELD(REG_K3RTC_SYNCPEND, 0, 1), + [K3RTC_RELOAD_FROM_BBD] = REG_FIELD(REG_K3RTC_SYNCPEND, 31, 31), + [K3RTC_COMP] = REG_FIELD(REG_K3RTC_COMP, 0, 31), + + /* We use on to off as alarm trigger */ + [K3RTC_ALM_S_CNT_LSW] = REG_FIELD(REG_K3RTC_ON_OFF_S_CNT_LSW, 0, 31), + [K3RTC_ALM_S_CNT_MSW] = REG_FIELD(REG_K3RTC_ON_OFF_S_CNT_MSW, 0, 15), + [K3RTC_IRQ_STATUS_RAW] = REG_FIELD(REG_K3RTC_IRQSTATUS_RAW_SYS, 0, 0), + [K3RTC_IRQ_STATUS] = REG_FIELD(REG_K3RTC_IRQSTATUS_SYS, 0, 0), + [K3RTC_IRQ_ENABLE_SET] = REG_FIELD(REG_K3RTC_IRQENABLE_SET_SYS, 0, 0), + [K3RTC_IRQ_ENABLE_CLR] = REG_FIELD(REG_K3RTC_IRQENABLE_CLR_SYS, 0, 0), + /* Off to on is alternate */ + [K3RTC_IRQ_STATUS_ALT] = REG_FIELD(REG_K3RTC_IRQSTATUS_SYS, 1, 1), + [K3RTC_IRQ_ENABLE_CLR_ALT] = REG_FIELD(REG_K3RTC_IRQENABLE_CLR_SYS, 1, 1), +}; + +/** + * struct ti_k3_rtc - Private data for ti-k3-rtc + * @irq: IRQ + * @sync_timeout_us: data sync timeout period in uSec + * @rate_32k: 32k clock rate in Hz + * @rtc_dev: rtc device + * @regmap: rtc mmio regmap + * @r_fields: rtc register fields + */ +struct ti_k3_rtc { + unsigned int irq; + u32 sync_timeout_us; + unsigned long rate_32k; + struct rtc_device *rtc_dev; + struct regmap *regmap; + struct regmap_field *r_fields[K3_RTC_MAX_FIELDS]; +}; + +static int k3rtc_field_read(struct ti_k3_rtc *priv, enum ti_k3_rtc_fields f) +{ + int ret; + int val; + + ret = regmap_field_read(priv->r_fields[f], &val); + /* + * We shouldn't be seeing regmap fail on us for mmio reads + * This is possible if clock context fails, but that isn't the case for us + */ + if (WARN_ON_ONCE(ret)) + return ret; + return val; +} + +static void k3rtc_field_write(struct ti_k3_rtc *priv, enum ti_k3_rtc_fields f, u32 val) +{ + regmap_field_write(priv->r_fields[f], val); +} + +/** + * k3rtc_fence - Ensure a register sync took place between the two domains + * @priv: pointer to priv data + * + * Return: 0 if the sync took place, else returns -ETIMEDOUT + */ +static int k3rtc_fence(struct ti_k3_rtc *priv) +{ + int ret; + + ret = regmap_field_read_poll_timeout(priv->r_fields[K3RTC_PEND], ret, + !ret, 2, priv->sync_timeout_us); + + return ret; +} + +static inline int k3rtc_check_unlocked(struct ti_k3_rtc *priv) +{ + int ret; + + ret = k3rtc_field_read(priv, K3RTC_UNLOCK); + if (ret < 0) + return ret; + + return (ret) ? 0 : 1; +} + +static int k3rtc_unlock_rtc(struct ti_k3_rtc *priv) +{ + int ret; + + ret = k3rtc_check_unlocked(priv); + if (!ret) + return ret; + + k3rtc_field_write(priv, K3RTC_KICK0, K3RTC_KICK0_UNLOCK_VALUE); + k3rtc_field_write(priv, K3RTC_KICK1, K3RTC_KICK1_UNLOCK_VALUE); + + /* Skip fence since we are going to check the unlock bit as fence */ + ret = regmap_field_read_poll_timeout(priv->r_fields[K3RTC_UNLOCK], ret, + ret, 2, priv->sync_timeout_us); + + return ret; +} + +/* + * This is the list of SoCs affected by TI's i2327 errata causing the RTC + * state-machine to break if not unlocked fast enough during boot. These + * SoCs must have the bootloader unlock this device very early in the + * boot-flow before we (Linux) can use this device. + */ +static const struct soc_device_attribute has_erratum_i2327[] = { + { .family = "AM62X", .revision = "SR1.0" }, + { /* sentinel */ } +}; + +static int k3rtc_configure(struct device *dev) +{ + int ret; + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + + /* + * HWBUG: The compare state machine is broken if the RTC module + * is NOT unlocked in under one second of boot - which is pretty long + * time from the perspective of Linux driver (module load, u-boot + * shell all can take much longer than this. + * + * In such occurrence, it is assumed that the RTC module is unusable + */ + if (soc_device_match(has_erratum_i2327)) { + ret = k3rtc_check_unlocked(priv); + /* If there is an error OR if we are locked, return error */ + if (ret) { + dev_err(dev, + HW_ERR "Erratum i2327 unlock QUIRK! Cannot operate!!\n"); + return -EFAULT; + } + } else { + /* May need to explicitly unlock first time */ + ret = k3rtc_unlock_rtc(priv); + if (ret) { + dev_err(dev, "Failed to unlock(%d)!\n", ret); + return ret; + } + } + + /* Enable Shadow register sync on 32k clock boundary */ + k3rtc_field_write(priv, K3RTC_O32K_OSC_DEP_EN, 0x1); + + /* + * Wait at least clock sync time before proceeding further programming. + * This ensures that the 32k based sync is active. + */ + usleep_range(priv->sync_timeout_us, priv->sync_timeout_us + 5); + + /* We need to ensure fence here to make sure sync here */ + ret = k3rtc_fence(priv); + if (ret) { + dev_err(dev, + "Failed fence osc_dep enable(%d) - is 32k clk working?!\n", ret); + return ret; + } + + /* + * FMODE setting: Reading lower seconds will freeze value on higher + * seconds. This also implies that we must *ALWAYS* read lower seconds + * prior to reading higher seconds + */ + k3rtc_field_write(priv, K3RTC_CNT_FMODE, K3RTC_CNT_FMODE_S_CNT_VALUE); + + /* Clear any spurious IRQ sources if any */ + k3rtc_field_write(priv, K3RTC_IRQ_STATUS_ALT, 0x1); + k3rtc_field_write(priv, K3RTC_IRQ_STATUS, 0x1); + /* Disable all IRQs */ + k3rtc_field_write(priv, K3RTC_IRQ_ENABLE_CLR_ALT, 0x1); + k3rtc_field_write(priv, K3RTC_IRQ_ENABLE_CLR, 0x1); + + /* And.. Let us Sync the writes in */ + return k3rtc_fence(priv); +} + +static int ti_k3_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + u32 seconds_lo, seconds_hi; + + seconds_lo = k3rtc_field_read(priv, K3RTC_S_CNT_LSW); + seconds_hi = k3rtc_field_read(priv, K3RTC_S_CNT_MSW); + + rtc_time64_to_tm((((time64_t)seconds_hi) << 32) | (time64_t)seconds_lo, tm); + + return 0; +} + +static int ti_k3_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + time64_t seconds; + + seconds = rtc_tm_to_time64(tm); + + /* + * Read operation on LSW will freeze the RTC, so to update + * the time, we cannot use field operations. Just write since the + * reserved bits are ignored. + */ + regmap_write(priv->regmap, REG_K3RTC_S_CNT_LSW, seconds); + regmap_write(priv->regmap, REG_K3RTC_S_CNT_MSW, seconds >> 32); + + return k3rtc_fence(priv); +} + +static int ti_k3_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + u32 reg; + u32 offset = enabled ? K3RTC_IRQ_ENABLE_SET : K3RTC_IRQ_ENABLE_CLR; + + reg = k3rtc_field_read(priv, K3RTC_IRQ_ENABLE_SET); + if ((enabled && reg) || (!enabled && !reg)) + return 0; + + k3rtc_field_write(priv, offset, 0x1); + + /* + * Ensure the write sync is through - NOTE: it should be OK to have + * ISR to fire as we are checking sync (which should be done in a 32k + * cycle or so). + */ + return k3rtc_fence(priv); +} + +static int ti_k3_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + u32 seconds_lo, seconds_hi; + + seconds_lo = k3rtc_field_read(priv, K3RTC_ALM_S_CNT_LSW); + seconds_hi = k3rtc_field_read(priv, K3RTC_ALM_S_CNT_MSW); + + rtc_time64_to_tm((((time64_t)seconds_hi) << 32) | (time64_t)seconds_lo, &alarm->time); + + alarm->enabled = k3rtc_field_read(priv, K3RTC_IRQ_ENABLE_SET); + + return 0; +} + +static int ti_k3_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + time64_t seconds; + int ret; + + seconds = rtc_tm_to_time64(&alarm->time); + + k3rtc_field_write(priv, K3RTC_ALM_S_CNT_LSW, seconds); + k3rtc_field_write(priv, K3RTC_ALM_S_CNT_MSW, (seconds >> 32)); + + /* Make sure the alarm time is synced in */ + ret = k3rtc_fence(priv); + if (ret) { + dev_err(dev, "Failed to fence(%d)! Potential config issue?\n", ret); + return ret; + } + + /* Alarm IRQ enable will do a sync */ + return ti_k3_rtc_alarm_irq_enable(dev, alarm->enabled); +} + +static int ti_k3_rtc_read_offset(struct device *dev, long *offset) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + u32 ticks_per_hr = priv->rate_32k * 3600; + int comp; + s64 tmp; + + comp = k3rtc_field_read(priv, K3RTC_COMP); + + /* Convert from RTC calibration register format to ppb format */ + tmp = comp * (s64)K3RTC_PPB_MULT; + if (tmp < 0) + tmp -= ticks_per_hr / 2LL; + else + tmp += ticks_per_hr / 2LL; + tmp = div_s64(tmp, ticks_per_hr); + + /* Offset value operates in negative way, so swap sign */ + *offset = (long)-tmp; + + return 0; +} + +static int ti_k3_rtc_set_offset(struct device *dev, long offset) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + u32 ticks_per_hr = priv->rate_32k * 3600; + int comp; + s64 tmp; + + /* Make sure offset value is within supported range */ + if (offset < K3RTC_MIN_OFFSET || offset > K3RTC_MAX_OFFSET) + return -ERANGE; + + /* Convert from ppb format to RTC calibration register format */ + tmp = offset * (s64)ticks_per_hr; + if (tmp < 0) + tmp -= K3RTC_PPB_MULT / 2LL; + else + tmp += K3RTC_PPB_MULT / 2LL; + tmp = div_s64(tmp, K3RTC_PPB_MULT); + + /* Offset value operates in negative way, so swap sign */ + comp = (int)-tmp; + + k3rtc_field_write(priv, K3RTC_COMP, comp); + + return k3rtc_fence(priv); +} + +static irqreturn_t ti_k3_rtc_interrupt(s32 irq, void *dev_id) +{ + struct device *dev = dev_id; + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + u32 reg; + int ret; + + /* + * IRQ assertion can be very fast, however, the IRQ Status clear + * de-assert depends on 32k clock edge in the 32k domain + * If we clear the status prior to the first 32k clock edge, + * the status bit is cleared, but the IRQ stays re-asserted. + * + * To prevent this condition, we need to wait for clock sync time. + * We can either do that by polling the 32k observability signal for + * a toggle OR we could just sleep and let the processor do other + * stuff. + */ + usleep_range(priv->sync_timeout_us, priv->sync_timeout_us + 2); + + /* Lets make sure that this is a valid interrupt */ + reg = k3rtc_field_read(priv, K3RTC_IRQ_STATUS); + + if (!reg) { + u32 raw = k3rtc_field_read(priv, K3RTC_IRQ_STATUS_RAW); + + dev_err(dev, + HW_ERR + "Erratum i2327/IRQ trig: status: 0x%08x / 0x%08x\n", reg, raw); + return IRQ_NONE; + } + + /* + * Write 1 to clear status reg + * We cannot use a field operation here due to a potential race between + * 32k domain and vbus domain. + */ + regmap_write(priv->regmap, REG_K3RTC_IRQSTATUS_SYS, 0x1); + + /* Sync the write in */ + ret = k3rtc_fence(priv); + if (ret) { + dev_err(dev, "Failed to fence irq status clr(%d)!\n", ret); + return IRQ_NONE; + } + + /* + * Force the 32k status to be reloaded back in to ensure status is + * reflected back correctly. + */ + k3rtc_field_write(priv, K3RTC_RELOAD_FROM_BBD, 0x1); + + /* Ensure the write sync is through */ + ret = k3rtc_fence(priv); + if (ret) { + dev_err(dev, "Failed to fence reload from bbd(%d)!\n", ret); + return IRQ_NONE; + } + + /* Now we ensure that the status bit is cleared */ + ret = regmap_field_read_poll_timeout(priv->r_fields[K3RTC_IRQ_STATUS], + ret, !ret, 2, priv->sync_timeout_us); + if (ret) { + dev_err(dev, "Time out waiting for status clear\n"); + return IRQ_NONE; + } + + /* Notify RTC core on event */ + rtc_update_irq(priv->rtc_dev, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops ti_k3_rtc_ops = { + .read_time = ti_k3_rtc_read_time, + .set_time = ti_k3_rtc_set_time, + .read_alarm = ti_k3_rtc_read_alarm, + .set_alarm = ti_k3_rtc_set_alarm, + .read_offset = ti_k3_rtc_read_offset, + .set_offset = ti_k3_rtc_set_offset, + .alarm_irq_enable = ti_k3_rtc_alarm_irq_enable, +}; + +static int ti_k3_rtc_scratch_read(void *priv_data, unsigned int offset, + void *val, size_t bytes) +{ + struct ti_k3_rtc *priv = (struct ti_k3_rtc *)priv_data; + + return regmap_bulk_read(priv->regmap, REG_K3RTC_SCRATCH0 + offset, val, bytes / 4); +} + +static int ti_k3_rtc_scratch_write(void *priv_data, unsigned int offset, + void *val, size_t bytes) +{ + struct ti_k3_rtc *priv = (struct ti_k3_rtc *)priv_data; + int ret; + + ret = regmap_bulk_write(priv->regmap, REG_K3RTC_SCRATCH0 + offset, val, bytes / 4); + if (ret) + return ret; + + return k3rtc_fence(priv); +} + +static struct nvmem_config ti_k3_rtc_nvmem_config = { + .name = "ti_k3_rtc_scratch", + .word_size = 4, + .stride = 4, + .size = REG_K3RTC_SCRATCH7 - REG_K3RTC_SCRATCH0 + 4, + .reg_read = ti_k3_rtc_scratch_read, + .reg_write = ti_k3_rtc_scratch_write, +}; + +static int k3rtc_get_32kclk(struct device *dev, struct ti_k3_rtc *priv) +{ + struct clk *clk; + + clk = devm_clk_get_enabled(dev, "osc32k"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + priv->rate_32k = clk_get_rate(clk); + + /* Make sure we are exact 32k clock. Else, try to compensate delay */ + if (priv->rate_32k != 32768) + dev_warn(dev, "Clock rate %ld is not 32768! Could misbehave!\n", + priv->rate_32k); + + /* + * Sync timeout should be two 32k clk sync cycles = ~61uS. We double + * it to comprehend intermediate bus segment and cpu frequency + * deltas + */ + priv->sync_timeout_us = (u32)(DIV_ROUND_UP_ULL(1000000, priv->rate_32k) * 4); + + return 0; +} + +static int k3rtc_get_vbusclk(struct device *dev, struct ti_k3_rtc *priv) +{ + struct clk *clk; + + /* Note: VBUS isn't a context clock, it is needed for hardware operation */ + clk = devm_clk_get_enabled(dev, "vbus"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + return 0; +} + +static int ti_k3_rtc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ti_k3_rtc *priv; + void __iomem *rtc_base; + int ret; + + priv = devm_kzalloc(dev, sizeof(struct ti_k3_rtc), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + rtc_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rtc_base)) + return PTR_ERR(rtc_base); + + priv->regmap = devm_regmap_init_mmio(dev, rtc_base, &ti_k3_rtc_regmap_config); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + ret = devm_regmap_field_bulk_alloc(dev, priv->regmap, priv->r_fields, + ti_rtc_reg_fields, K3_RTC_MAX_FIELDS); + if (ret) + return ret; + + ret = k3rtc_get_32kclk(dev, priv); + if (ret) + return ret; + ret = k3rtc_get_vbusclk(dev, priv); + if (ret) + return ret; + + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; + priv->irq = (unsigned int)ret; + + priv->rtc_dev = devm_rtc_allocate_device(dev); + if (IS_ERR(priv->rtc_dev)) + return PTR_ERR(priv->rtc_dev); + + priv->rtc_dev->ops = &ti_k3_rtc_ops; + priv->rtc_dev->range_max = (1ULL << 48) - 1; /* 48Bit seconds */ + ti_k3_rtc_nvmem_config.priv = priv; + + ret = devm_request_threaded_irq(dev, priv->irq, NULL, + ti_k3_rtc_interrupt, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + dev_name(dev), dev); + if (ret) { + dev_err(dev, "Could not request IRQ: %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, priv); + + ret = k3rtc_configure(dev); + if (ret) + return ret; + + if (device_property_present(dev, "wakeup-source")) + device_init_wakeup(dev, true); + else + device_set_wakeup_capable(dev, true); + + ret = devm_rtc_register_device(priv->rtc_dev); + if (ret) + return ret; + + return devm_rtc_nvmem_register(priv->rtc_dev, &ti_k3_rtc_nvmem_config); +} + +static const struct of_device_id ti_k3_rtc_of_match_table[] = { + {.compatible = "ti,am62-rtc" }, + {} +}; +MODULE_DEVICE_TABLE(of, ti_k3_rtc_of_match_table); + +static int __maybe_unused ti_k3_rtc_suspend(struct device *dev) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + return enable_irq_wake(priv->irq); + + return 0; +} + +static int __maybe_unused ti_k3_rtc_resume(struct device *dev) +{ + struct ti_k3_rtc *priv = dev_get_drvdata(dev); + int ret = 0; + + if (k3rtc_check_unlocked(priv)) { + /* RTC locked implies low power mode exit where RTC loses context */ + ret = k3rtc_configure(dev); + if (ret) + return ret; + } + + if (device_may_wakeup(dev)) + disable_irq_wake(priv->irq); + return ret; +} + +static SIMPLE_DEV_PM_OPS(ti_k3_rtc_pm_ops, ti_k3_rtc_suspend, ti_k3_rtc_resume); + +static struct platform_driver ti_k3_rtc_driver = { + .probe = ti_k3_rtc_probe, + .driver = { + .name = "rtc-ti-k3", + .of_match_table = ti_k3_rtc_of_match_table, + .pm = &ti_k3_rtc_pm_ops, + }, +}; +module_platform_driver(ti_k3_rtc_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("TI K3 RTC driver"); +MODULE_AUTHOR("Nishanth Menon"); diff --git a/drivers/rtc/rtc-tps6586x.c b/drivers/rtc/rtc-tps6586x.c new file mode 100644 index 000000000..54c8429b1 --- /dev/null +++ b/drivers/rtc/rtc-tps6586x.c @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rtc-tps6586x.c: RTC driver for TI PMIC TPS6586X + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * Author: Laxman Dewangan + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RTC_CTRL 0xc0 +#define POR_RESET_N BIT(7) +#define OSC_SRC_SEL BIT(6) +#define RTC_ENABLE BIT(5) /* enables alarm */ +#define RTC_BUF_ENABLE BIT(4) /* 32 KHz buffer enable */ +#define PRE_BYPASS BIT(3) /* 0=1KHz or 1=32KHz updates */ +#define CL_SEL_MASK (BIT(2)|BIT(1)) +#define CL_SEL_POS 1 +#define RTC_ALARM1_HI 0xc1 +#define RTC_COUNT4 0xc6 + +/* start a PMU RTC access by reading the register prior to the RTC_COUNT4 */ +#define RTC_COUNT4_DUMMYREAD 0xc5 + +/*only 14-bits width in second*/ +#define ALM1_VALID_RANGE_IN_SEC 0x3FFF + +#define TPS6586X_RTC_CL_SEL_1_5PF 0x0 +#define TPS6586X_RTC_CL_SEL_6_5PF 0x1 +#define TPS6586X_RTC_CL_SEL_7_5PF 0x2 +#define TPS6586X_RTC_CL_SEL_12_5PF 0x3 + +struct tps6586x_rtc { + struct device *dev; + struct rtc_device *rtc; + int irq; + bool irq_en; +}; + +static inline struct device *to_tps6586x_dev(struct device *dev) +{ + return dev->parent; +} + +static int tps6586x_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct device *tps_dev = to_tps6586x_dev(dev); + unsigned long long ticks = 0; + time64_t seconds; + u8 buff[6]; + int ret; + int i; + + ret = tps6586x_reads(tps_dev, RTC_COUNT4_DUMMYREAD, sizeof(buff), buff); + if (ret < 0) { + dev_err(dev, "read counter failed with err %d\n", ret); + return ret; + } + + for (i = 1; i < sizeof(buff); i++) { + ticks <<= 8; + ticks |= buff[i]; + } + + seconds = ticks >> 10; + rtc_time64_to_tm(seconds, tm); + + return 0; +} + +static int tps6586x_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct device *tps_dev = to_tps6586x_dev(dev); + unsigned long long ticks; + time64_t seconds; + u8 buff[5]; + int ret; + + seconds = rtc_tm_to_time64(tm); + + ticks = (unsigned long long)seconds << 10; + buff[0] = (ticks >> 32) & 0xff; + buff[1] = (ticks >> 24) & 0xff; + buff[2] = (ticks >> 16) & 0xff; + buff[3] = (ticks >> 8) & 0xff; + buff[4] = ticks & 0xff; + + /* Disable RTC before changing time */ + ret = tps6586x_clr_bits(tps_dev, RTC_CTRL, RTC_ENABLE); + if (ret < 0) { + dev_err(dev, "failed to clear RTC_ENABLE\n"); + return ret; + } + + ret = tps6586x_writes(tps_dev, RTC_COUNT4, sizeof(buff), buff); + if (ret < 0) { + dev_err(dev, "failed to program new time\n"); + return ret; + } + + /* Enable RTC */ + ret = tps6586x_set_bits(tps_dev, RTC_CTRL, RTC_ENABLE); + if (ret < 0) { + dev_err(dev, "failed to set RTC_ENABLE\n"); + return ret; + } + return 0; +} + +static int tps6586x_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct tps6586x_rtc *rtc = dev_get_drvdata(dev); + + if (enabled && !rtc->irq_en) { + enable_irq(rtc->irq); + rtc->irq_en = true; + } else if (!enabled && rtc->irq_en) { + disable_irq(rtc->irq); + rtc->irq_en = false; + } + return 0; +} + +static int tps6586x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct device *tps_dev = to_tps6586x_dev(dev); + time64_t seconds; + unsigned long ticks; + unsigned long rtc_current_time; + unsigned long long rticks = 0; + u8 buff[3]; + u8 rbuff[6]; + int ret; + int i; + + seconds = rtc_tm_to_time64(&alrm->time); + + ret = tps6586x_rtc_alarm_irq_enable(dev, alrm->enabled); + if (ret < 0) { + dev_err(dev, "can't set alarm irq, err %d\n", ret); + return ret; + } + + ret = tps6586x_reads(tps_dev, RTC_COUNT4_DUMMYREAD, + sizeof(rbuff), rbuff); + if (ret < 0) { + dev_err(dev, "read counter failed with err %d\n", ret); + return ret; + } + + for (i = 1; i < sizeof(rbuff); i++) { + rticks <<= 8; + rticks |= rbuff[i]; + } + + rtc_current_time = rticks >> 10; + if ((seconds - rtc_current_time) > ALM1_VALID_RANGE_IN_SEC) + seconds = rtc_current_time - 1; + + ticks = (unsigned long long)seconds << 10; + buff[0] = (ticks >> 16) & 0xff; + buff[1] = (ticks >> 8) & 0xff; + buff[2] = ticks & 0xff; + + ret = tps6586x_writes(tps_dev, RTC_ALARM1_HI, sizeof(buff), buff); + if (ret) + dev_err(dev, "programming alarm failed with err %d\n", ret); + + return ret; +} + +static int tps6586x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct device *tps_dev = to_tps6586x_dev(dev); + unsigned long ticks; + time64_t seconds; + u8 buff[3]; + int ret; + + ret = tps6586x_reads(tps_dev, RTC_ALARM1_HI, sizeof(buff), buff); + if (ret) { + dev_err(dev, "read RTC_ALARM1_HI failed with err %d\n", ret); + return ret; + } + + ticks = (buff[0] << 16) | (buff[1] << 8) | buff[2]; + seconds = ticks >> 10; + + rtc_time64_to_tm(seconds, &alrm->time); + return 0; +} + +static const struct rtc_class_ops tps6586x_rtc_ops = { + .read_time = tps6586x_rtc_read_time, + .set_time = tps6586x_rtc_set_time, + .set_alarm = tps6586x_rtc_set_alarm, + .read_alarm = tps6586x_rtc_read_alarm, + .alarm_irq_enable = tps6586x_rtc_alarm_irq_enable, +}; + +static irqreturn_t tps6586x_rtc_irq(int irq, void *data) +{ + struct tps6586x_rtc *rtc = data; + + rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF); + return IRQ_HANDLED; +} + +static int tps6586x_rtc_probe(struct platform_device *pdev) +{ + struct device *tps_dev = to_tps6586x_dev(&pdev->dev); + struct tps6586x_rtc *rtc; + int ret; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->dev = &pdev->dev; + rtc->irq = platform_get_irq(pdev, 0); + + /* 1 kHz tick mode, enable tick counting */ + ret = tps6586x_update(tps_dev, RTC_CTRL, + RTC_ENABLE | OSC_SRC_SEL | + ((TPS6586X_RTC_CL_SEL_1_5PF << CL_SEL_POS) & CL_SEL_MASK), + RTC_ENABLE | OSC_SRC_SEL | PRE_BYPASS | CL_SEL_MASK); + if (ret < 0) { + dev_err(&pdev->dev, "unable to start counter\n"); + return ret; + } + + device_init_wakeup(&pdev->dev, true); + + platform_set_drvdata(pdev, rtc); + rtc->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc->rtc)) { + ret = PTR_ERR(rtc->rtc); + goto fail_rtc_register; + } + + rtc->rtc->ops = &tps6586x_rtc_ops; + rtc->rtc->range_max = (1ULL << 30) - 1; /* 30-bit seconds */ + rtc->rtc->alarm_offset_max = ALM1_VALID_RANGE_IN_SEC; + rtc->rtc->start_secs = mktime64(2009, 1, 1, 0, 0, 0); + rtc->rtc->set_start_time = true; + + irq_set_status_flags(rtc->irq, IRQ_NOAUTOEN); + + ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL, + tps6586x_rtc_irq, + IRQF_ONESHOT, + dev_name(&pdev->dev), rtc); + if (ret < 0) { + dev_err(&pdev->dev, "request IRQ(%d) failed with ret %d\n", + rtc->irq, ret); + goto fail_rtc_register; + } + + ret = devm_rtc_register_device(rtc->rtc); + if (ret) + goto fail_rtc_register; + + return 0; + +fail_rtc_register: + tps6586x_update(tps_dev, RTC_CTRL, 0, + RTC_ENABLE | OSC_SRC_SEL | PRE_BYPASS | CL_SEL_MASK); + return ret; +}; + +static void tps6586x_rtc_remove(struct platform_device *pdev) +{ + struct device *tps_dev = to_tps6586x_dev(&pdev->dev); + + tps6586x_update(tps_dev, RTC_CTRL, 0, + RTC_ENABLE | OSC_SRC_SEL | PRE_BYPASS | CL_SEL_MASK); +} + +#ifdef CONFIG_PM_SLEEP +static int tps6586x_rtc_suspend(struct device *dev) +{ + struct tps6586x_rtc *rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(rtc->irq); + return 0; +} + +static int tps6586x_rtc_resume(struct device *dev) +{ + struct tps6586x_rtc *rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(rtc->irq); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(tps6586x_pm_ops, tps6586x_rtc_suspend, + tps6586x_rtc_resume); + +static struct platform_driver tps6586x_rtc_driver = { + .driver = { + .name = "tps6586x-rtc", + .pm = &tps6586x_pm_ops, + }, + .probe = tps6586x_rtc_probe, + .remove = tps6586x_rtc_remove, +}; +module_platform_driver(tps6586x_rtc_driver); + +MODULE_ALIAS("platform:tps6586x-rtc"); +MODULE_DESCRIPTION("TI TPS6586x RTC driver"); +MODULE_AUTHOR("Laxman dewangan "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-tps65910.c b/drivers/rtc/rtc-tps65910.c new file mode 100644 index 000000000..284aa2f03 --- /dev/null +++ b/drivers/rtc/rtc-tps65910.c @@ -0,0 +1,470 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * rtc-tps65910.c -- TPS65910 Real Time Clock interface + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * Author: Venu Byravarasu + * + * Based on original TI driver rtc-twl.c + * Copyright (C) 2007 MontaVista Software, Inc + * Author: Alexandre Rusev + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct tps65910_rtc { + struct rtc_device *rtc; + int irq; +}; + +/* Total number of RTC registers needed to set time*/ +#define NUM_TIME_REGS (TPS65910_YEARS - TPS65910_SECONDS + 1) + +/* Total number of RTC registers needed to set compensation registers */ +#define NUM_COMP_REGS (TPS65910_RTC_COMP_MSB - TPS65910_RTC_COMP_LSB + 1) + +/* Min and max values supported with 'offset' interface (swapped sign) */ +#define MIN_OFFSET (-277761) +#define MAX_OFFSET (277778) + +/* Number of ticks per hour */ +#define TICKS_PER_HOUR (32768 * 3600) + +/* Multiplier for ppb conversions */ +#define PPB_MULT (1000000000LL) + +static int tps65910_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct tps65910 *tps = dev_get_drvdata(dev->parent); + u8 val = 0; + + if (enabled) + val = TPS65910_RTC_INTERRUPTS_IT_ALARM; + + return regmap_write(tps->regmap, TPS65910_RTC_INTERRUPTS, val); +} + +/* + * Gets current tps65910 RTC time and date parameters. + * + * The RTC's time/alarm representation is not what gmtime(3) requires + * Linux to use: + * + * - Months are 1..12 vs Linux 0-11 + * - Years are 0..99 vs Linux 1900..N (we assume 21st century) + */ +static int tps65910_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char rtc_data[NUM_TIME_REGS]; + struct tps65910 *tps = dev_get_drvdata(dev->parent); + int ret; + + /* Copy RTC counting registers to static registers or latches */ + ret = regmap_update_bits(tps->regmap, TPS65910_RTC_CTRL, + TPS65910_RTC_CTRL_GET_TIME, TPS65910_RTC_CTRL_GET_TIME); + if (ret < 0) { + dev_err(dev, "RTC CTRL reg update failed with err:%d\n", ret); + return ret; + } + + ret = regmap_bulk_read(tps->regmap, TPS65910_SECONDS, rtc_data, + NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "reading from RTC failed with err:%d\n", ret); + return ret; + } + + tm->tm_sec = bcd2bin(rtc_data[0]); + tm->tm_min = bcd2bin(rtc_data[1]); + tm->tm_hour = bcd2bin(rtc_data[2]); + tm->tm_mday = bcd2bin(rtc_data[3]); + tm->tm_mon = bcd2bin(rtc_data[4]) - 1; + tm->tm_year = bcd2bin(rtc_data[5]) + 100; + + return ret; +} + +static int tps65910_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char rtc_data[NUM_TIME_REGS]; + struct tps65910 *tps = dev_get_drvdata(dev->parent); + int ret; + + rtc_data[0] = bin2bcd(tm->tm_sec); + rtc_data[1] = bin2bcd(tm->tm_min); + rtc_data[2] = bin2bcd(tm->tm_hour); + rtc_data[3] = bin2bcd(tm->tm_mday); + rtc_data[4] = bin2bcd(tm->tm_mon + 1); + rtc_data[5] = bin2bcd(tm->tm_year - 100); + + /* Stop RTC while updating the RTC time registers */ + ret = regmap_update_bits(tps->regmap, TPS65910_RTC_CTRL, + TPS65910_RTC_CTRL_STOP_RTC, 0); + if (ret < 0) { + dev_err(dev, "RTC stop failed with err:%d\n", ret); + return ret; + } + + /* update all the time registers in one shot */ + ret = regmap_bulk_write(tps->regmap, TPS65910_SECONDS, rtc_data, + NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "rtc_set_time error %d\n", ret); + return ret; + } + + /* Start back RTC */ + ret = regmap_update_bits(tps->regmap, TPS65910_RTC_CTRL, + TPS65910_RTC_CTRL_STOP_RTC, 1); + if (ret < 0) + dev_err(dev, "RTC start failed with err:%d\n", ret); + + return ret; +} + +/* + * Gets current tps65910 RTC alarm time. + */ +static int tps65910_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + unsigned char alarm_data[NUM_TIME_REGS]; + u32 int_val; + struct tps65910 *tps = dev_get_drvdata(dev->parent); + int ret; + + ret = regmap_bulk_read(tps->regmap, TPS65910_ALARM_SECONDS, alarm_data, + NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "rtc_read_alarm error %d\n", ret); + return ret; + } + + alm->time.tm_sec = bcd2bin(alarm_data[0]); + alm->time.tm_min = bcd2bin(alarm_data[1]); + alm->time.tm_hour = bcd2bin(alarm_data[2]); + alm->time.tm_mday = bcd2bin(alarm_data[3]); + alm->time.tm_mon = bcd2bin(alarm_data[4]) - 1; + alm->time.tm_year = bcd2bin(alarm_data[5]) + 100; + + ret = regmap_read(tps->regmap, TPS65910_RTC_INTERRUPTS, &int_val); + if (ret < 0) + return ret; + + if (int_val & TPS65910_RTC_INTERRUPTS_IT_ALARM) + alm->enabled = 1; + + return ret; +} + +static int tps65910_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + unsigned char alarm_data[NUM_TIME_REGS]; + struct tps65910 *tps = dev_get_drvdata(dev->parent); + int ret; + + ret = tps65910_rtc_alarm_irq_enable(dev, 0); + if (ret) + return ret; + + alarm_data[0] = bin2bcd(alm->time.tm_sec); + alarm_data[1] = bin2bcd(alm->time.tm_min); + alarm_data[2] = bin2bcd(alm->time.tm_hour); + alarm_data[3] = bin2bcd(alm->time.tm_mday); + alarm_data[4] = bin2bcd(alm->time.tm_mon + 1); + alarm_data[5] = bin2bcd(alm->time.tm_year - 100); + + /* update all the alarm registers in one shot */ + ret = regmap_bulk_write(tps->regmap, TPS65910_ALARM_SECONDS, + alarm_data, NUM_TIME_REGS); + if (ret) { + dev_err(dev, "rtc_set_alarm error %d\n", ret); + return ret; + } + + if (alm->enabled) + ret = tps65910_rtc_alarm_irq_enable(dev, 1); + + return ret; +} + +static int tps65910_rtc_set_calibration(struct device *dev, int calibration) +{ + unsigned char comp_data[NUM_COMP_REGS]; + struct tps65910 *tps = dev_get_drvdata(dev->parent); + s16 value; + int ret; + + /* + * TPS65910 uses two's complement 16 bit value for compensation for RTC + * crystal inaccuracies. One time every hour when seconds counter + * increments from 0 to 1 compensation value will be added to internal + * RTC counter value. + * + * Compensation value 0x7FFF is prohibited value. + * + * Valid range for compensation value: [-32768 .. 32766] + */ + if ((calibration < -32768) || (calibration > 32766)) { + dev_err(dev, "RTC calibration value out of range: %d\n", + calibration); + return -EINVAL; + } + + value = (s16)calibration; + + comp_data[0] = (u16)value & 0xFF; + comp_data[1] = ((u16)value >> 8) & 0xFF; + + /* Update all the compensation registers in one shot */ + ret = regmap_bulk_write(tps->regmap, TPS65910_RTC_COMP_LSB, + comp_data, NUM_COMP_REGS); + if (ret < 0) { + dev_err(dev, "rtc_set_calibration error: %d\n", ret); + return ret; + } + + /* Enable automatic compensation */ + ret = regmap_update_bits(tps->regmap, TPS65910_RTC_CTRL, + TPS65910_RTC_CTRL_AUTO_COMP, TPS65910_RTC_CTRL_AUTO_COMP); + if (ret < 0) + dev_err(dev, "auto_comp enable failed with error: %d\n", ret); + + return ret; +} + +static int tps65910_rtc_get_calibration(struct device *dev, int *calibration) +{ + unsigned char comp_data[NUM_COMP_REGS]; + struct tps65910 *tps = dev_get_drvdata(dev->parent); + unsigned int ctrl; + u16 value; + int ret; + + ret = regmap_read(tps->regmap, TPS65910_RTC_CTRL, &ctrl); + if (ret < 0) + return ret; + + /* If automatic compensation is not enabled report back zero */ + if (!(ctrl & TPS65910_RTC_CTRL_AUTO_COMP)) { + *calibration = 0; + return 0; + } + + ret = regmap_bulk_read(tps->regmap, TPS65910_RTC_COMP_LSB, comp_data, + NUM_COMP_REGS); + if (ret < 0) { + dev_err(dev, "rtc_get_calibration error: %d\n", ret); + return ret; + } + + value = (u16)comp_data[0] | ((u16)comp_data[1] << 8); + + *calibration = (s16)value; + + return 0; +} + +static int tps65910_read_offset(struct device *dev, long *offset) +{ + int calibration; + s64 tmp; + int ret; + + ret = tps65910_rtc_get_calibration(dev, &calibration); + if (ret < 0) + return ret; + + /* Convert from RTC calibration register format to ppb format */ + tmp = calibration * (s64)PPB_MULT; + if (tmp < 0) + tmp -= TICKS_PER_HOUR / 2LL; + else + tmp += TICKS_PER_HOUR / 2LL; + tmp = div_s64(tmp, TICKS_PER_HOUR); + + /* Offset value operates in negative way, so swap sign */ + *offset = (long)-tmp; + + return 0; +} + +static int tps65910_set_offset(struct device *dev, long offset) +{ + int calibration; + s64 tmp; + int ret; + + /* Make sure offset value is within supported range */ + if (offset < MIN_OFFSET || offset > MAX_OFFSET) + return -ERANGE; + + /* Convert from ppb format to RTC calibration register format */ + tmp = offset * (s64)TICKS_PER_HOUR; + if (tmp < 0) + tmp -= PPB_MULT / 2LL; + else + tmp += PPB_MULT / 2LL; + tmp = div_s64(tmp, PPB_MULT); + + /* Offset value operates in negative way, so swap sign */ + calibration = (int)-tmp; + + ret = tps65910_rtc_set_calibration(dev, calibration); + + return ret; +} + +static irqreturn_t tps65910_rtc_interrupt(int irq, void *rtc) +{ + struct device *dev = rtc; + unsigned long events = 0; + struct tps65910 *tps = dev_get_drvdata(dev->parent); + struct tps65910_rtc *tps_rtc = dev_get_drvdata(dev); + int ret; + u32 rtc_reg; + + ret = regmap_read(tps->regmap, TPS65910_RTC_STATUS, &rtc_reg); + if (ret) + return IRQ_NONE; + + if (rtc_reg & TPS65910_RTC_STATUS_ALARM) + events = RTC_IRQF | RTC_AF; + + ret = regmap_write(tps->regmap, TPS65910_RTC_STATUS, rtc_reg); + if (ret) + return IRQ_NONE; + + /* Notify RTC core on event */ + rtc_update_irq(tps_rtc->rtc, 1, events); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops tps65910_rtc_ops = { + .read_time = tps65910_rtc_read_time, + .set_time = tps65910_rtc_set_time, + .read_alarm = tps65910_rtc_read_alarm, + .set_alarm = tps65910_rtc_set_alarm, + .alarm_irq_enable = tps65910_rtc_alarm_irq_enable, + .read_offset = tps65910_read_offset, + .set_offset = tps65910_set_offset, +}; + +static int tps65910_rtc_probe(struct platform_device *pdev) +{ + struct tps65910 *tps65910 = NULL; + struct tps65910_rtc *tps_rtc = NULL; + int ret; + int irq; + u32 rtc_reg; + + tps65910 = dev_get_drvdata(pdev->dev.parent); + + tps_rtc = devm_kzalloc(&pdev->dev, sizeof(struct tps65910_rtc), + GFP_KERNEL); + if (!tps_rtc) + return -ENOMEM; + + tps_rtc->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(tps_rtc->rtc)) + return PTR_ERR(tps_rtc->rtc); + + /* Clear pending interrupts */ + ret = regmap_read(tps65910->regmap, TPS65910_RTC_STATUS, &rtc_reg); + if (ret < 0) + return ret; + + ret = regmap_write(tps65910->regmap, TPS65910_RTC_STATUS, rtc_reg); + if (ret < 0) + return ret; + + dev_dbg(&pdev->dev, "Enabling rtc-tps65910.\n"); + + /* Enable RTC digital power domain */ + ret = regmap_update_bits(tps65910->regmap, TPS65910_DEVCTRL, + DEVCTRL_RTC_PWDN_MASK, 0 << DEVCTRL_RTC_PWDN_SHIFT); + if (ret < 0) + return ret; + + rtc_reg = TPS65910_RTC_CTRL_STOP_RTC; + ret = regmap_write(tps65910->regmap, TPS65910_RTC_CTRL, rtc_reg); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, tps_rtc); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + tps65910_rtc_interrupt, IRQF_TRIGGER_LOW, + dev_name(&pdev->dev), &pdev->dev); + if (ret < 0) + irq = -1; + + tps_rtc->irq = irq; + if (irq != -1) { + if (device_property_present(tps65910->dev, "wakeup-source")) + device_init_wakeup(&pdev->dev, true); + else + device_set_wakeup_capable(&pdev->dev, 1); + } else { + clear_bit(RTC_FEATURE_ALARM, tps_rtc->rtc->features); + } + + tps_rtc->rtc->ops = &tps65910_rtc_ops; + tps_rtc->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + tps_rtc->rtc->range_max = RTC_TIMESTAMP_END_2099; + + return devm_rtc_register_device(tps_rtc->rtc); +} + +#ifdef CONFIG_PM_SLEEP +static int tps65910_rtc_suspend(struct device *dev) +{ + struct tps65910_rtc *tps_rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(tps_rtc->irq); + return 0; +} + +static int tps65910_rtc_resume(struct device *dev) +{ + struct tps65910_rtc *tps_rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(tps_rtc->irq); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(tps65910_rtc_pm_ops, tps65910_rtc_suspend, + tps65910_rtc_resume); + +static struct platform_driver tps65910_rtc_driver = { + .probe = tps65910_rtc_probe, + .driver = { + .name = "tps65910-rtc", + .pm = &tps65910_rtc_pm_ops, + }, +}; + +module_platform_driver(tps65910_rtc_driver); +MODULE_ALIAS("platform:tps65910-rtc"); +MODULE_AUTHOR("Venu Byravarasu "); +MODULE_DESCRIPTION("TI TPS65910 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-tps6594.c b/drivers/rtc/rtc-tps6594.c new file mode 100644 index 000000000..f89fc3e59 --- /dev/null +++ b/drivers/rtc/rtc-tps6594.c @@ -0,0 +1,504 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RTC driver for tps6594 PMIC + * + * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// Total number of RTC registers needed to set time +#define NUM_TIME_REGS (TPS6594_REG_RTC_WEEKS - TPS6594_REG_RTC_SECONDS + 1) + +// Total number of RTC alarm registers +#define NUM_TIME_ALARM_REGS (NUM_TIME_REGS - 1) + +/* + * Min and max values supported by 'offset' interface (swapped sign). + * After conversion, the values do not exceed the range [-32767, 33767] + * which COMP_REG must conform to. + */ +#define MIN_OFFSET (-277774) +#define MAX_OFFSET (277774) + +// Number of ticks per hour +#define TICKS_PER_HOUR (32768 * 3600LL) + +// Multiplier for ppb conversions +#define PPB_MULT NANO + +struct tps6594_rtc { + struct rtc_device *rtc_dev; + int irq; +}; + +static int tps6594_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct tps6594 *tps = dev_get_drvdata(dev->parent); + u8 val; + + val = enabled ? TPS6594_BIT_IT_ALARM : 0; + + return regmap_update_bits(tps->regmap, TPS6594_REG_RTC_INTERRUPTS, + TPS6594_BIT_IT_ALARM, val); +} + +/* Pulse GET_TIME field of RTC_CTRL_1 to store a timestamp in shadow registers. */ +static int tps6594_rtc_shadow_timestamp(struct device *dev, struct tps6594 *tps) +{ + int ret; + + /* + * Set GET_TIME to 0. Next time we set GET_TIME to 1 we will be sure to store + * an up-to-date timestamp. + */ + ret = regmap_clear_bits(tps->regmap, TPS6594_REG_RTC_CTRL_1, + TPS6594_BIT_GET_TIME); + if (ret < 0) + return ret; + + /* + * Copy content of RTC registers to shadow registers or latches to read + * a coherent timestamp. + */ + return regmap_set_bits(tps->regmap, TPS6594_REG_RTC_CTRL_1, + TPS6594_BIT_GET_TIME); +} + +static int tps6594_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char rtc_data[NUM_TIME_REGS]; + struct tps6594 *tps = dev_get_drvdata(dev->parent); + int ret; + + // Check if RTC is running. + ret = regmap_test_bits(tps->regmap, TPS6594_REG_RTC_STATUS, + TPS6594_BIT_RUN); + if (ret < 0) + return ret; + if (ret == 0) + return -EINVAL; + + ret = tps6594_rtc_shadow_timestamp(dev, tps); + if (ret < 0) + return ret; + + // Read shadowed RTC registers. + ret = regmap_bulk_read(tps->regmap, TPS6594_REG_RTC_SECONDS, rtc_data, + NUM_TIME_REGS); + if (ret < 0) + return ret; + + tm->tm_sec = bcd2bin(rtc_data[0]); + tm->tm_min = bcd2bin(rtc_data[1]); + tm->tm_hour = bcd2bin(rtc_data[2]); + tm->tm_mday = bcd2bin(rtc_data[3]); + tm->tm_mon = bcd2bin(rtc_data[4]) - 1; + tm->tm_year = bcd2bin(rtc_data[5]) + 100; + tm->tm_wday = bcd2bin(rtc_data[6]); + + return 0; +} + +static int tps6594_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char rtc_data[NUM_TIME_REGS]; + struct tps6594 *tps = dev_get_drvdata(dev->parent); + int ret; + + rtc_data[0] = bin2bcd(tm->tm_sec); + rtc_data[1] = bin2bcd(tm->tm_min); + rtc_data[2] = bin2bcd(tm->tm_hour); + rtc_data[3] = bin2bcd(tm->tm_mday); + rtc_data[4] = bin2bcd(tm->tm_mon + 1); + rtc_data[5] = bin2bcd(tm->tm_year - 100); + rtc_data[6] = bin2bcd(tm->tm_wday); + + // Stop RTC while updating the RTC time registers. + ret = regmap_clear_bits(tps->regmap, TPS6594_REG_RTC_CTRL_1, + TPS6594_BIT_STOP_RTC); + if (ret < 0) + return ret; + + // Update all the time registers in one shot. + ret = regmap_bulk_write(tps->regmap, TPS6594_REG_RTC_SECONDS, rtc_data, + NUM_TIME_REGS); + if (ret < 0) + return ret; + + // Start back RTC. + return regmap_set_bits(tps->regmap, TPS6594_REG_RTC_CTRL_1, + TPS6594_BIT_STOP_RTC); +} + +static int tps6594_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + unsigned char alarm_data[NUM_TIME_ALARM_REGS]; + u32 int_val; + struct tps6594 *tps = dev_get_drvdata(dev->parent); + int ret; + + ret = regmap_bulk_read(tps->regmap, TPS6594_REG_ALARM_SECONDS, + alarm_data, NUM_TIME_ALARM_REGS); + if (ret < 0) + return ret; + + alm->time.tm_sec = bcd2bin(alarm_data[0]); + alm->time.tm_min = bcd2bin(alarm_data[1]); + alm->time.tm_hour = bcd2bin(alarm_data[2]); + alm->time.tm_mday = bcd2bin(alarm_data[3]); + alm->time.tm_mon = bcd2bin(alarm_data[4]) - 1; + alm->time.tm_year = bcd2bin(alarm_data[5]) + 100; + + ret = regmap_read(tps->regmap, TPS6594_REG_RTC_INTERRUPTS, &int_val); + if (ret < 0) + return ret; + + alm->enabled = int_val & TPS6594_BIT_IT_ALARM; + + return 0; +} + +static int tps6594_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + unsigned char alarm_data[NUM_TIME_ALARM_REGS]; + struct tps6594 *tps = dev_get_drvdata(dev->parent); + int ret; + + // Disable alarm irq before changing the alarm timestamp. + ret = tps6594_rtc_alarm_irq_enable(dev, 0); + if (ret) + return ret; + + alarm_data[0] = bin2bcd(alm->time.tm_sec); + alarm_data[1] = bin2bcd(alm->time.tm_min); + alarm_data[2] = bin2bcd(alm->time.tm_hour); + alarm_data[3] = bin2bcd(alm->time.tm_mday); + alarm_data[4] = bin2bcd(alm->time.tm_mon + 1); + alarm_data[5] = bin2bcd(alm->time.tm_year - 100); + + // Update all the alarm registers in one shot. + ret = regmap_bulk_write(tps->regmap, TPS6594_REG_ALARM_SECONDS, + alarm_data, NUM_TIME_ALARM_REGS); + if (ret < 0) + return ret; + + if (alm->enabled) + ret = tps6594_rtc_alarm_irq_enable(dev, 1); + + return ret; +} + +static int tps6594_rtc_set_calibration(struct device *dev, int calibration) +{ + struct tps6594 *tps = dev_get_drvdata(dev->parent); + __le16 value; + int ret; + + /* + * TPS6594 uses two's complement 16 bit value for compensation of RTC + * crystal inaccuracies. One time every hour when seconds counter + * increments from 0 to 1 compensation value will be added to internal + * RTC counter value. + * + * Valid range for compensation value: [-32767 .. 32767]. + */ + if (calibration < S16_MIN + 1 || calibration > S16_MAX) + return -ERANGE; + + value = cpu_to_le16(calibration); + + // Update all the compensation registers in one shot. + ret = regmap_bulk_write(tps->regmap, TPS6594_REG_RTC_COMP_LSB, &value, + sizeof(value)); + if (ret < 0) + return ret; + + // Enable automatic compensation. + return regmap_set_bits(tps->regmap, TPS6594_REG_RTC_CTRL_1, + TPS6594_BIT_AUTO_COMP); +} + +static int tps6594_rtc_get_calibration(struct device *dev, int *calibration) +{ + struct tps6594 *tps = dev_get_drvdata(dev->parent); + unsigned int ctrl; + __le16 value; + int ret; + + ret = regmap_read(tps->regmap, TPS6594_REG_RTC_CTRL_1, &ctrl); + if (ret < 0) + return ret; + + // If automatic compensation is not enabled report back zero. + if (!(ctrl & TPS6594_BIT_AUTO_COMP)) { + *calibration = 0; + return 0; + } + + ret = regmap_bulk_read(tps->regmap, TPS6594_REG_RTC_COMP_LSB, &value, + sizeof(value)); + if (ret < 0) + return ret; + + *calibration = le16_to_cpu(value); + + return 0; +} + +static int tps6594_rtc_read_offset(struct device *dev, long *offset) +{ + int calibration; + s64 tmp; + int ret; + + ret = tps6594_rtc_get_calibration(dev, &calibration); + if (ret < 0) + return ret; + + // Convert from RTC calibration register format to ppb format. + tmp = calibration * PPB_MULT; + + if (tmp < 0) + tmp -= TICKS_PER_HOUR / 2LL; + else + tmp += TICKS_PER_HOUR / 2LL; + tmp = div_s64(tmp, TICKS_PER_HOUR); + + /* + * SAFETY: + * Computatiion is the reverse operation of the one done in + * `tps6594_rtc_set_offset`. The safety remarks applie here too. + */ + + /* + * Offset value operates in negative way, so swap sign. + * See 8.3.10.5, (32768 - COMP_REG). + */ + *offset = (long)-tmp; + + return 0; +} + +static int tps6594_rtc_set_offset(struct device *dev, long offset) +{ + int calibration; + s64 tmp; + + // Make sure offset value is within supported range. + if (offset < MIN_OFFSET || offset > MAX_OFFSET) + return -ERANGE; + + // Convert from ppb format to RTC calibration register format. + + tmp = offset * TICKS_PER_HOUR; + if (tmp < 0) + tmp -= PPB_MULT / 2LL; + else + tmp += PPB_MULT / 2LL; + tmp = div_s64(tmp, PPB_MULT); + + /* + * SAFETY: + * - tmp = offset * TICK_PER_HOUR : + * `offset` can't be more than 277774, so `tmp` can't exceed 277774000000000 + * which is lower than the maximum value in an `s64` (2^63-1). No overflow here. + * + * - tmp += TICK_PER_HOUR / 2LL : + * tmp will have a maximum value of 277774117964800 which is still inferior to 2^63-1. + */ + + // Offset value operates in negative way, so swap sign. + calibration = (int)-tmp; + + return tps6594_rtc_set_calibration(dev, calibration); +} + +static irqreturn_t tps6594_rtc_interrupt(int irq, void *data) +{ + struct device *dev = data; + struct tps6594 *tps = dev_get_drvdata(dev->parent); + struct tps6594_rtc *rtc = dev_get_drvdata(dev); + int ret; + u32 rtc_reg; + + ret = regmap_read(tps->regmap, TPS6594_REG_RTC_STATUS, &rtc_reg); + if (ret) + return IRQ_NONE; + + rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops tps6594_rtc_ops = { + .read_time = tps6594_rtc_read_time, + .set_time = tps6594_rtc_set_time, + .read_alarm = tps6594_rtc_read_alarm, + .set_alarm = tps6594_rtc_set_alarm, + .alarm_irq_enable = tps6594_rtc_alarm_irq_enable, + .read_offset = tps6594_rtc_read_offset, + .set_offset = tps6594_rtc_set_offset, +}; + +static int tps6594_rtc_probe(struct platform_device *pdev) +{ + struct tps6594 *tps = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct tps6594_rtc *rtc; + int irq; + int ret; + + rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->rtc_dev = devm_rtc_allocate_device(dev); + if (IS_ERR(rtc->rtc_dev)) + return PTR_ERR(rtc->rtc_dev); + + // Enable crystal oscillator. + ret = regmap_set_bits(tps->regmap, TPS6594_REG_RTC_CTRL_2, + TPS6594_BIT_XTAL_EN); + if (ret < 0) + return ret; + + ret = regmap_test_bits(tps->regmap, TPS6594_REG_RTC_STATUS, + TPS6594_BIT_RUN); + if (ret < 0) + return ret; + // RTC not running. + if (ret == 0) { + ret = regmap_set_bits(tps->regmap, TPS6594_REG_RTC_CTRL_1, + TPS6594_BIT_STOP_RTC); + if (ret < 0) + return ret; + + /* + * On some boards, a 40 ms delay is needed before BIT_RUN is set. + * 80 ms should provide sufficient margin. + */ + mdelay(80); + + /* + * RTC should be running now. Check if this is the case. + * If not it might be a missing oscillator. + */ + ret = regmap_test_bits(tps->regmap, TPS6594_REG_RTC_STATUS, + TPS6594_BIT_RUN); + if (ret < 0) + return ret; + if (ret == 0) + return -ENODEV; + + // Stop RTC until first call to `tps6594_rtc_set_time`. + ret = regmap_clear_bits(tps->regmap, TPS6594_REG_RTC_CTRL_1, + TPS6594_BIT_STOP_RTC); + if (ret < 0) + return ret; + } + + platform_set_drvdata(pdev, rtc); + + irq = platform_get_irq_byname(pdev, TPS6594_IRQ_NAME_ALARM); + if (irq < 0) + return dev_err_probe(dev, irq, "Failed to get irq\n"); + + rtc->irq = irq; + + ret = devm_request_threaded_irq(dev, irq, NULL, tps6594_rtc_interrupt, + IRQF_ONESHOT, TPS6594_IRQ_NAME_ALARM, + dev); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to request_threaded_irq\n"); + + ret = device_init_wakeup(dev, true); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to init rtc as wakeup source\n"); + + rtc->rtc_dev->ops = &tps6594_rtc_ops; + rtc->rtc_dev->range_min = RTC_TIMESTAMP_BEGIN_2000; + rtc->rtc_dev->range_max = RTC_TIMESTAMP_END_2099; + + return devm_rtc_register_device(rtc->rtc_dev); +} + +static int tps6594_rtc_resume(struct device *dev) +{ + struct tps6594 *tps = dev_get_drvdata(dev->parent); + struct tps6594_rtc *rtc = dev_get_drvdata(dev); + int ret; + + ret = regmap_test_bits(tps->regmap, TPS6594_REG_INT_STARTUP, + TPS6594_BIT_RTC_INT); + if (ret < 0) { + dev_err(dev, "failed to read REG_INT_STARTUP: %d\n", ret); + goto out; + } + + if (ret > 0) { + /* + * If the alarm bit is set, it means that the IRQ has been + * fired. But, the kernel may not have woke up yet when it + * happened. So, we have to clear it. + */ + ret = regmap_write(tps->regmap, TPS6594_REG_RTC_STATUS, + TPS6594_BIT_ALARM); + if (ret < 0) + dev_err(dev, "error clearing alarm bit: %d", ret); + + rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF); + } +out: + disable_irq_wake(rtc->irq); + + return 0; +} + +static int tps6594_rtc_suspend(struct device *dev) +{ + struct tps6594_rtc *rtc = dev_get_drvdata(dev); + + enable_irq_wake(rtc->irq); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(tps6594_rtc_pm_ops, tps6594_rtc_suspend, tps6594_rtc_resume); + +static const struct platform_device_id tps6594_rtc_id_table[] = { + { .name = "tps6594-rtc" }, + { } +}; +MODULE_DEVICE_TABLE(platform, tps6594_rtc_id_table); + +static struct platform_driver tps6594_rtc_driver = { + .probe = tps6594_rtc_probe, + .driver = { + .name = "tps6594-rtc", + .pm = pm_sleep_ptr(&tps6594_rtc_pm_ops), + }, + .id_table = tps6594_rtc_id_table, +}; + +module_platform_driver(tps6594_rtc_driver); +MODULE_AUTHOR("Esteban Blanc "); +MODULE_DESCRIPTION("TPS6594 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c new file mode 100644 index 000000000..e6106e67e --- /dev/null +++ b/drivers/rtc/rtc-twl.c @@ -0,0 +1,689 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * rtc-twl.c -- TWL Real Time Clock interface + * + * Copyright (C) 2007 MontaVista Software, Inc + * Author: Alexandre Rusev + * + * Based on original TI driver twl4030-rtc.c + * Copyright (C) 2006 Texas Instruments, Inc. + * + * Based on rtc-omap.c + * Copyright (C) 2003 MontaVista Software, Inc. + * Author: George G. Davis or + * Copyright (C) 2006 David Brownell + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +enum twl_class { + TWL_4030 = 0, + TWL_6030, +}; + +/* + * RTC block register offsets (use TWL_MODULE_RTC) + */ +enum { + REG_SECONDS_REG = 0, + REG_MINUTES_REG, + REG_HOURS_REG, + REG_DAYS_REG, + REG_MONTHS_REG, + REG_YEARS_REG, + REG_WEEKS_REG, + + REG_ALARM_SECONDS_REG, + REG_ALARM_MINUTES_REG, + REG_ALARM_HOURS_REG, + REG_ALARM_DAYS_REG, + REG_ALARM_MONTHS_REG, + REG_ALARM_YEARS_REG, + + REG_RTC_CTRL_REG, + REG_RTC_STATUS_REG, + REG_RTC_INTERRUPTS_REG, + + REG_RTC_COMP_LSB_REG, + REG_RTC_COMP_MSB_REG, +}; +static const u8 twl4030_rtc_reg_map[] = { + [REG_SECONDS_REG] = 0x00, + [REG_MINUTES_REG] = 0x01, + [REG_HOURS_REG] = 0x02, + [REG_DAYS_REG] = 0x03, + [REG_MONTHS_REG] = 0x04, + [REG_YEARS_REG] = 0x05, + [REG_WEEKS_REG] = 0x06, + + [REG_ALARM_SECONDS_REG] = 0x07, + [REG_ALARM_MINUTES_REG] = 0x08, + [REG_ALARM_HOURS_REG] = 0x09, + [REG_ALARM_DAYS_REG] = 0x0A, + [REG_ALARM_MONTHS_REG] = 0x0B, + [REG_ALARM_YEARS_REG] = 0x0C, + + [REG_RTC_CTRL_REG] = 0x0D, + [REG_RTC_STATUS_REG] = 0x0E, + [REG_RTC_INTERRUPTS_REG] = 0x0F, + + [REG_RTC_COMP_LSB_REG] = 0x10, + [REG_RTC_COMP_MSB_REG] = 0x11, +}; +static const u8 twl6030_rtc_reg_map[] = { + [REG_SECONDS_REG] = 0x00, + [REG_MINUTES_REG] = 0x01, + [REG_HOURS_REG] = 0x02, + [REG_DAYS_REG] = 0x03, + [REG_MONTHS_REG] = 0x04, + [REG_YEARS_REG] = 0x05, + [REG_WEEKS_REG] = 0x06, + + [REG_ALARM_SECONDS_REG] = 0x08, + [REG_ALARM_MINUTES_REG] = 0x09, + [REG_ALARM_HOURS_REG] = 0x0A, + [REG_ALARM_DAYS_REG] = 0x0B, + [REG_ALARM_MONTHS_REG] = 0x0C, + [REG_ALARM_YEARS_REG] = 0x0D, + + [REG_RTC_CTRL_REG] = 0x10, + [REG_RTC_STATUS_REG] = 0x11, + [REG_RTC_INTERRUPTS_REG] = 0x12, + + [REG_RTC_COMP_LSB_REG] = 0x13, + [REG_RTC_COMP_MSB_REG] = 0x14, +}; + +/* RTC_CTRL_REG bitfields */ +#define BIT_RTC_CTRL_REG_STOP_RTC_M 0x01 +#define BIT_RTC_CTRL_REG_ROUND_30S_M 0x02 +#define BIT_RTC_CTRL_REG_AUTO_COMP_M 0x04 +#define BIT_RTC_CTRL_REG_MODE_12_24_M 0x08 +#define BIT_RTC_CTRL_REG_TEST_MODE_M 0x10 +#define BIT_RTC_CTRL_REG_SET_32_COUNTER_M 0x20 +#define BIT_RTC_CTRL_REG_GET_TIME_M 0x40 +#define BIT_RTC_CTRL_REG_RTC_V_OPT 0x80 + +/* RTC_STATUS_REG bitfields */ +#define BIT_RTC_STATUS_REG_RUN_M 0x02 +#define BIT_RTC_STATUS_REG_1S_EVENT_M 0x04 +#define BIT_RTC_STATUS_REG_1M_EVENT_M 0x08 +#define BIT_RTC_STATUS_REG_1H_EVENT_M 0x10 +#define BIT_RTC_STATUS_REG_1D_EVENT_M 0x20 +#define BIT_RTC_STATUS_REG_ALARM_M 0x40 +#define BIT_RTC_STATUS_REG_POWER_UP_M 0x80 + +/* RTC_INTERRUPTS_REG bitfields */ +#define BIT_RTC_INTERRUPTS_REG_EVERY_M 0x03 +#define BIT_RTC_INTERRUPTS_REG_IT_TIMER_M 0x04 +#define BIT_RTC_INTERRUPTS_REG_IT_ALARM_M 0x08 + + +/* REG_SECONDS_REG through REG_YEARS_REG is how many registers? */ +#define ALL_TIME_REGS 6 + +/*----------------------------------------------------------------------*/ +struct twl_rtc { + struct device *dev; + struct rtc_device *rtc; + u8 *reg_map; + /* + * Cache the value for timer/alarm interrupts register; this is + * only changed by callers holding rtc ops lock (or resume). + */ + unsigned char rtc_irq_bits; + bool wake_enabled; +#ifdef CONFIG_PM_SLEEP + unsigned char irqstat; +#endif + enum twl_class class; +}; + +/* + * Supports 1 byte read from TWL RTC register. + */ +static int twl_rtc_read_u8(struct twl_rtc *twl_rtc, u8 *data, u8 reg) +{ + int ret; + + ret = twl_i2c_read_u8(TWL_MODULE_RTC, data, (twl_rtc->reg_map[reg])); + if (ret < 0) + pr_err("Could not read TWL register %X - error %d\n", reg, ret); + return ret; +} + +/* + * Supports 1 byte write to TWL RTC registers. + */ +static int twl_rtc_write_u8(struct twl_rtc *twl_rtc, u8 data, u8 reg) +{ + int ret; + + ret = twl_i2c_write_u8(TWL_MODULE_RTC, data, (twl_rtc->reg_map[reg])); + if (ret < 0) + pr_err("Could not write TWL register %X - error %d\n", + reg, ret); + return ret; +} + +/* + * Enable 1/second update and/or alarm interrupts. + */ +static int set_rtc_irq_bit(struct twl_rtc *twl_rtc, unsigned char bit) +{ + unsigned char val; + int ret; + + /* if the bit is set, return from here */ + if (twl_rtc->rtc_irq_bits & bit) + return 0; + + val = twl_rtc->rtc_irq_bits | bit; + val &= ~BIT_RTC_INTERRUPTS_REG_EVERY_M; + ret = twl_rtc_write_u8(twl_rtc, val, REG_RTC_INTERRUPTS_REG); + if (ret == 0) + twl_rtc->rtc_irq_bits = val; + + return ret; +} + +/* + * Disable update and/or alarm interrupts. + */ +static int mask_rtc_irq_bit(struct twl_rtc *twl_rtc, unsigned char bit) +{ + unsigned char val; + int ret; + + /* if the bit is clear, return from here */ + if (!(twl_rtc->rtc_irq_bits & bit)) + return 0; + + val = twl_rtc->rtc_irq_bits & ~bit; + ret = twl_rtc_write_u8(twl_rtc, val, REG_RTC_INTERRUPTS_REG); + if (ret == 0) + twl_rtc->rtc_irq_bits = val; + + return ret; +} + +static int twl_rtc_alarm_irq_enable(struct device *dev, unsigned enabled) +{ + struct platform_device *pdev = to_platform_device(dev); + struct twl_rtc *twl_rtc = dev_get_drvdata(dev); + int irq = platform_get_irq(pdev, 0); + int ret; + + if (enabled) { + ret = set_rtc_irq_bit(twl_rtc, + BIT_RTC_INTERRUPTS_REG_IT_ALARM_M); + if (device_can_wakeup(dev) && !twl_rtc->wake_enabled) { + enable_irq_wake(irq); + twl_rtc->wake_enabled = true; + } + } else { + ret = mask_rtc_irq_bit(twl_rtc, + BIT_RTC_INTERRUPTS_REG_IT_ALARM_M); + if (twl_rtc->wake_enabled) { + disable_irq_wake(irq); + twl_rtc->wake_enabled = false; + } + } + + return ret; +} + +/* + * Gets current TWL RTC time and date parameters. + * + * The RTC's time/alarm representation is not what gmtime(3) requires + * Linux to use: + * + * - Months are 1..12 vs Linux 0-11 + * - Years are 0..99 vs Linux 1900..N (we assume 21st century) + */ +static int twl_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct twl_rtc *twl_rtc = dev_get_drvdata(dev); + unsigned char rtc_data[ALL_TIME_REGS]; + int ret; + u8 save_control; + u8 rtc_control; + + ret = twl_rtc_read_u8(twl_rtc, &save_control, REG_RTC_CTRL_REG); + if (ret < 0) { + dev_err(dev, "%s: reading CTRL_REG, error %d\n", __func__, ret); + return ret; + } + /* for twl6030/32 make sure BIT_RTC_CTRL_REG_GET_TIME_M is clear */ + if (twl_rtc->class == TWL_6030) { + if (save_control & BIT_RTC_CTRL_REG_GET_TIME_M) { + save_control &= ~BIT_RTC_CTRL_REG_GET_TIME_M; + ret = twl_rtc_write_u8(twl_rtc, save_control, + REG_RTC_CTRL_REG); + if (ret < 0) { + dev_err(dev, "%s clr GET_TIME, error %d\n", + __func__, ret); + return ret; + } + } + } + + /* Copy RTC counting registers to static registers or latches */ + rtc_control = save_control | BIT_RTC_CTRL_REG_GET_TIME_M; + + /* for twl6030/32 enable read access to static shadowed registers */ + if (twl_rtc->class == TWL_6030) + rtc_control |= BIT_RTC_CTRL_REG_RTC_V_OPT; + + ret = twl_rtc_write_u8(twl_rtc, rtc_control, REG_RTC_CTRL_REG); + if (ret < 0) { + dev_err(dev, "%s: writing CTRL_REG, error %d\n", __func__, ret); + return ret; + } + + ret = twl_i2c_read(TWL_MODULE_RTC, rtc_data, + (twl_rtc->reg_map[REG_SECONDS_REG]), ALL_TIME_REGS); + + if (ret < 0) { + dev_err(dev, "%s: reading data, error %d\n", __func__, ret); + return ret; + } + + /* for twl6030 restore original state of rtc control register */ + if (twl_rtc->class == TWL_6030) { + ret = twl_rtc_write_u8(twl_rtc, save_control, REG_RTC_CTRL_REG); + if (ret < 0) { + dev_err(dev, "%s: restore CTRL_REG, error %d\n", + __func__, ret); + return ret; + } + } + + tm->tm_sec = bcd2bin(rtc_data[0]); + tm->tm_min = bcd2bin(rtc_data[1]); + tm->tm_hour = bcd2bin(rtc_data[2]); + tm->tm_mday = bcd2bin(rtc_data[3]); + tm->tm_mon = bcd2bin(rtc_data[4]) - 1; + tm->tm_year = bcd2bin(rtc_data[5]) + 100; + + return ret; +} + +static int twl_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct twl_rtc *twl_rtc = dev_get_drvdata(dev); + unsigned char save_control; + unsigned char rtc_data[ALL_TIME_REGS]; + int ret; + + rtc_data[0] = bin2bcd(tm->tm_sec); + rtc_data[1] = bin2bcd(tm->tm_min); + rtc_data[2] = bin2bcd(tm->tm_hour); + rtc_data[3] = bin2bcd(tm->tm_mday); + rtc_data[4] = bin2bcd(tm->tm_mon + 1); + rtc_data[5] = bin2bcd(tm->tm_year - 100); + + /* Stop RTC while updating the TC registers */ + ret = twl_rtc_read_u8(twl_rtc, &save_control, REG_RTC_CTRL_REG); + if (ret < 0) + goto out; + + save_control &= ~BIT_RTC_CTRL_REG_STOP_RTC_M; + ret = twl_rtc_write_u8(twl_rtc, save_control, REG_RTC_CTRL_REG); + if (ret < 0) + goto out; + + /* update all the time registers in one shot */ + ret = twl_i2c_write(TWL_MODULE_RTC, rtc_data, + (twl_rtc->reg_map[REG_SECONDS_REG]), ALL_TIME_REGS); + if (ret < 0) { + dev_err(dev, "rtc_set_time error %d\n", ret); + goto out; + } + + /* Start back RTC */ + save_control |= BIT_RTC_CTRL_REG_STOP_RTC_M; + ret = twl_rtc_write_u8(twl_rtc, save_control, REG_RTC_CTRL_REG); + +out: + return ret; +} + +/* + * Gets current TWL RTC alarm time. + */ +static int twl_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct twl_rtc *twl_rtc = dev_get_drvdata(dev); + unsigned char rtc_data[ALL_TIME_REGS]; + int ret; + + ret = twl_i2c_read(TWL_MODULE_RTC, rtc_data, + twl_rtc->reg_map[REG_ALARM_SECONDS_REG], ALL_TIME_REGS); + if (ret < 0) { + dev_err(dev, "rtc_read_alarm error %d\n", ret); + return ret; + } + + /* some of these fields may be wildcard/"match all" */ + alm->time.tm_sec = bcd2bin(rtc_data[0]); + alm->time.tm_min = bcd2bin(rtc_data[1]); + alm->time.tm_hour = bcd2bin(rtc_data[2]); + alm->time.tm_mday = bcd2bin(rtc_data[3]); + alm->time.tm_mon = bcd2bin(rtc_data[4]) - 1; + alm->time.tm_year = bcd2bin(rtc_data[5]) + 100; + + /* report cached alarm enable state */ + if (twl_rtc->rtc_irq_bits & BIT_RTC_INTERRUPTS_REG_IT_ALARM_M) + alm->enabled = 1; + + return ret; +} + +static int twl_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct twl_rtc *twl_rtc = dev_get_drvdata(dev); + + unsigned char alarm_data[ALL_TIME_REGS]; + int ret; + + ret = twl_rtc_alarm_irq_enable(dev, 0); + if (ret) + goto out; + + alarm_data[0] = bin2bcd(alm->time.tm_sec); + alarm_data[1] = bin2bcd(alm->time.tm_min); + alarm_data[2] = bin2bcd(alm->time.tm_hour); + alarm_data[3] = bin2bcd(alm->time.tm_mday); + alarm_data[4] = bin2bcd(alm->time.tm_mon + 1); + alarm_data[5] = bin2bcd(alm->time.tm_year - 100); + + /* update all the alarm registers in one shot */ + ret = twl_i2c_write(TWL_MODULE_RTC, alarm_data, + twl_rtc->reg_map[REG_ALARM_SECONDS_REG], ALL_TIME_REGS); + if (ret) { + dev_err(dev, "rtc_set_alarm error %d\n", ret); + goto out; + } + + if (alm->enabled) + ret = twl_rtc_alarm_irq_enable(dev, 1); +out: + return ret; +} + +static irqreturn_t twl_rtc_interrupt(int irq, void *data) +{ + struct twl_rtc *twl_rtc = data; + unsigned long events; + int ret = IRQ_NONE; + int res; + u8 rd_reg; + + res = twl_rtc_read_u8(twl_rtc, &rd_reg, REG_RTC_STATUS_REG); + if (res) + goto out; + /* + * Figure out source of interrupt: ALARM or TIMER in RTC_STATUS_REG. + * only one (ALARM or RTC) interrupt source may be enabled + * at time, we also could check our results + * by reading RTS_INTERRUPTS_REGISTER[IT_TIMER,IT_ALARM] + */ + if (rd_reg & BIT_RTC_STATUS_REG_ALARM_M) + events = RTC_IRQF | RTC_AF; + else + events = RTC_IRQF | RTC_PF; + + res = twl_rtc_write_u8(twl_rtc, BIT_RTC_STATUS_REG_ALARM_M, + REG_RTC_STATUS_REG); + if (res) + goto out; + + if (twl_rtc->class == TWL_4030) { + /* Clear on Read enabled. RTC_IT bit of TWL4030_INT_PWR_ISR1 + * needs 2 reads to clear the interrupt. One read is done in + * do_twl_pwrirq(). Doing the second read, to clear + * the bit. + * + * FIXME the reason PWR_ISR1 needs an extra read is that + * RTC_IF retriggered until we cleared REG_ALARM_M above. + * But re-reading like this is a bad hack; by doing so we + * risk wrongly clearing status for some other IRQ (losing + * the interrupt). Be smarter about handling RTC_UF ... + */ + res = twl_i2c_read_u8(TWL4030_MODULE_INT, + &rd_reg, TWL4030_INT_PWR_ISR1); + if (res) + goto out; + } + + /* Notify RTC core on event */ + rtc_update_irq(twl_rtc->rtc, 1, events); + + ret = IRQ_HANDLED; +out: + return ret; +} + +static const struct rtc_class_ops twl_rtc_ops = { + .read_time = twl_rtc_read_time, + .set_time = twl_rtc_set_time, + .read_alarm = twl_rtc_read_alarm, + .set_alarm = twl_rtc_set_alarm, + .alarm_irq_enable = twl_rtc_alarm_irq_enable, +}; + +static int twl_nvram_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + return twl_i2c_read((long)priv, val, offset, bytes); +} + +static int twl_nvram_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + return twl_i2c_write((long)priv, val, offset, bytes); +} + +/*----------------------------------------------------------------------*/ + +static int twl_rtc_probe(struct platform_device *pdev) +{ + struct twl_rtc *twl_rtc; + struct nvmem_config nvmem_cfg; + struct device_node *np = pdev->dev.of_node; + int ret = -EINVAL; + int irq = platform_get_irq(pdev, 0); + u8 rd_reg; + + if (!np) { + dev_err(&pdev->dev, "no DT info\n"); + return -EINVAL; + } + + if (irq <= 0) + return ret; + + twl_rtc = devm_kzalloc(&pdev->dev, sizeof(*twl_rtc), GFP_KERNEL); + if (!twl_rtc) + return -ENOMEM; + + if (twl_class_is_4030()) { + twl_rtc->class = TWL_4030; + twl_rtc->reg_map = (u8 *)twl4030_rtc_reg_map; + } else if (twl_class_is_6030()) { + twl_rtc->class = TWL_6030; + twl_rtc->reg_map = (u8 *)twl6030_rtc_reg_map; + } else { + dev_err(&pdev->dev, "TWL Class not supported.\n"); + return -EINVAL; + } + + ret = twl_rtc_read_u8(twl_rtc, &rd_reg, REG_RTC_STATUS_REG); + if (ret < 0) + return ret; + + if (rd_reg & BIT_RTC_STATUS_REG_POWER_UP_M) + dev_warn(&pdev->dev, "Power up reset detected.\n"); + + if (rd_reg & BIT_RTC_STATUS_REG_ALARM_M) + dev_warn(&pdev->dev, "Pending Alarm interrupt detected.\n"); + + /* Clear RTC Power up reset and pending alarm interrupts */ + ret = twl_rtc_write_u8(twl_rtc, rd_reg, REG_RTC_STATUS_REG); + if (ret < 0) + return ret; + + if (twl_rtc->class == TWL_6030) { + twl6030_interrupt_unmask(TWL6030_RTC_INT_MASK, + REG_INT_MSK_LINE_A); + twl6030_interrupt_unmask(TWL6030_RTC_INT_MASK, + REG_INT_MSK_STS_A); + } + + ret = twl_rtc_write_u8(twl_rtc, BIT_RTC_CTRL_REG_STOP_RTC_M, + REG_RTC_CTRL_REG); + if (ret < 0) + return ret; + + /* ensure interrupts are disabled, bootloaders can be strange */ + ret = twl_rtc_write_u8(twl_rtc, 0, REG_RTC_INTERRUPTS_REG); + if (ret < 0) + dev_warn(&pdev->dev, "unable to disable interrupt\n"); + + /* init cached IRQ enable bits */ + ret = twl_rtc_read_u8(twl_rtc, &twl_rtc->rtc_irq_bits, + REG_RTC_INTERRUPTS_REG); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, twl_rtc); + device_init_wakeup(&pdev->dev, true); + + twl_rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + &twl_rtc_ops, THIS_MODULE); + if (IS_ERR(twl_rtc->rtc)) + return PTR_ERR(twl_rtc->rtc); + + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + twl_rtc_interrupt, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + dev_name(&twl_rtc->rtc->dev), twl_rtc); + if (ret < 0) { + dev_err(&pdev->dev, "IRQ is not free.\n"); + return ret; + } + + memset(&nvmem_cfg, 0, sizeof(nvmem_cfg)); + nvmem_cfg.name = "twl-secured-"; + nvmem_cfg.type = NVMEM_TYPE_BATTERY_BACKED; + nvmem_cfg.reg_read = twl_nvram_read; + nvmem_cfg.reg_write = twl_nvram_write; + nvmem_cfg.word_size = 1; + nvmem_cfg.stride = 1; + if (twl_class_is_4030()) { + /* 20 bytes SECURED_REG area */ + nvmem_cfg.size = 20; + nvmem_cfg.priv = (void *)TWL_MODULE_SECURED_REG; + devm_rtc_nvmem_register(twl_rtc->rtc, &nvmem_cfg); + /* 8 bytes BACKUP area */ + nvmem_cfg.name = "twl-backup-"; + nvmem_cfg.size = 8; + nvmem_cfg.priv = (void *)TWL4030_MODULE_BACKUP; + devm_rtc_nvmem_register(twl_rtc->rtc, &nvmem_cfg); + } else { + /* 8 bytes SECURED_REG area */ + nvmem_cfg.size = 8; + nvmem_cfg.priv = (void *)TWL_MODULE_SECURED_REG; + devm_rtc_nvmem_register(twl_rtc->rtc, &nvmem_cfg); + } + + return 0; +} + +/* + * Disable all TWL RTC module interrupts. + * Sets status flag to free. + */ +static void twl_rtc_remove(struct platform_device *pdev) +{ + struct twl_rtc *twl_rtc = platform_get_drvdata(pdev); + + /* leave rtc running, but disable irqs */ + mask_rtc_irq_bit(twl_rtc, BIT_RTC_INTERRUPTS_REG_IT_ALARM_M); + mask_rtc_irq_bit(twl_rtc, BIT_RTC_INTERRUPTS_REG_IT_TIMER_M); + if (twl_rtc->class == TWL_6030) { + twl6030_interrupt_mask(TWL6030_RTC_INT_MASK, + REG_INT_MSK_LINE_A); + twl6030_interrupt_mask(TWL6030_RTC_INT_MASK, + REG_INT_MSK_STS_A); + } +} + +static void twl_rtc_shutdown(struct platform_device *pdev) +{ + struct twl_rtc *twl_rtc = platform_get_drvdata(pdev); + + /* mask timer interrupts, but leave alarm interrupts on to enable + power-on when alarm is triggered */ + mask_rtc_irq_bit(twl_rtc, BIT_RTC_INTERRUPTS_REG_IT_TIMER_M); +} + +#ifdef CONFIG_PM_SLEEP +static int twl_rtc_suspend(struct device *dev) +{ + struct twl_rtc *twl_rtc = dev_get_drvdata(dev); + + twl_rtc->irqstat = twl_rtc->rtc_irq_bits; + + mask_rtc_irq_bit(twl_rtc, BIT_RTC_INTERRUPTS_REG_IT_TIMER_M); + return 0; +} + +static int twl_rtc_resume(struct device *dev) +{ + struct twl_rtc *twl_rtc = dev_get_drvdata(dev); + + set_rtc_irq_bit(twl_rtc, twl_rtc->irqstat); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(twl_rtc_pm_ops, twl_rtc_suspend, twl_rtc_resume); + +static const struct of_device_id twl_rtc_of_match[] = { + {.compatible = "ti,twl4030-rtc", }, + { }, +}; +MODULE_DEVICE_TABLE(of, twl_rtc_of_match); + +static struct platform_driver twl4030rtc_driver = { + .probe = twl_rtc_probe, + .remove = twl_rtc_remove, + .shutdown = twl_rtc_shutdown, + .driver = { + .name = "twl_rtc", + .pm = &twl_rtc_pm_ops, + .of_match_table = twl_rtc_of_match, + }, +}; + +module_platform_driver(twl4030rtc_driver); + +MODULE_AUTHOR("Texas Instruments, MontaVista Software"); +MODULE_DESCRIPTION("TI TWL4030/TWL5030/TWL6030/TPS659x0 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-vt8500.c b/drivers/rtc/rtc-vt8500.c new file mode 100644 index 000000000..c8b568498 --- /dev/null +++ b/drivers/rtc/rtc-vt8500.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * drivers/rtc/rtc-vt8500.c + * + * Copyright (C) 2010 Alexey Charkov + * + * Based on rtc-pxa.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Register definitions + */ +#define VT8500_RTC_TS 0x00 /* Time set */ +#define VT8500_RTC_DS 0x04 /* Date set */ +#define VT8500_RTC_AS 0x08 /* Alarm set */ +#define VT8500_RTC_CR 0x0c /* Control */ +#define VT8500_RTC_TR 0x10 /* Time read */ +#define VT8500_RTC_DR 0x14 /* Date read */ +#define VT8500_RTC_WS 0x18 /* Write status */ +#define VT8500_RTC_CL 0x20 /* Calibration */ +#define VT8500_RTC_IS 0x24 /* Interrupt status */ +#define VT8500_RTC_ST 0x28 /* Status */ + +#define INVALID_TIME_BIT (1 << 31) + +#define DATE_CENTURY_S 19 +#define DATE_YEAR_S 11 +#define DATE_YEAR_MASK (0xff << DATE_YEAR_S) +#define DATE_MONTH_S 6 +#define DATE_MONTH_MASK (0x1f << DATE_MONTH_S) +#define DATE_DAY_MASK 0x3f + +#define TIME_DOW_S 20 +#define TIME_DOW_MASK (0x07 << TIME_DOW_S) +#define TIME_HOUR_S 14 +#define TIME_HOUR_MASK (0x3f << TIME_HOUR_S) +#define TIME_MIN_S 7 +#define TIME_MIN_MASK (0x7f << TIME_MIN_S) +#define TIME_SEC_MASK 0x7f + +#define ALARM_DAY_S 20 +#define ALARM_DAY_MASK (0x3f << ALARM_DAY_S) + +#define ALARM_DAY_BIT (1 << 29) +#define ALARM_HOUR_BIT (1 << 28) +#define ALARM_MIN_BIT (1 << 27) +#define ALARM_SEC_BIT (1 << 26) + +#define ALARM_ENABLE_MASK (ALARM_DAY_BIT \ + | ALARM_HOUR_BIT \ + | ALARM_MIN_BIT \ + | ALARM_SEC_BIT) + +#define VT8500_RTC_CR_ENABLE (1 << 0) /* Enable RTC */ +#define VT8500_RTC_CR_12H (1 << 1) /* 12h time format */ +#define VT8500_RTC_CR_SM_ENABLE (1 << 2) /* Enable periodic irqs */ +#define VT8500_RTC_CR_SM_SEC (1 << 3) /* 0: 1Hz/60, 1: 1Hz */ +#define VT8500_RTC_CR_CALIB (1 << 4) /* Enable calibration */ + +#define VT8500_RTC_IS_ALARM (1 << 0) /* Alarm interrupt status */ + +struct vt8500_rtc { + void __iomem *regbase; + int irq_alarm; + struct rtc_device *rtc; + spinlock_t lock; /* Protects this structure */ +}; + +static irqreturn_t vt8500_rtc_irq(int irq, void *dev_id) +{ + struct vt8500_rtc *vt8500_rtc = dev_id; + u32 isr; + unsigned long events = 0; + + spin_lock(&vt8500_rtc->lock); + + /* clear interrupt sources */ + isr = readl(vt8500_rtc->regbase + VT8500_RTC_IS); + writel(isr, vt8500_rtc->regbase + VT8500_RTC_IS); + + spin_unlock(&vt8500_rtc->lock); + + if (isr & VT8500_RTC_IS_ALARM) + events |= RTC_AF | RTC_IRQF; + + rtc_update_irq(vt8500_rtc->rtc, 1, events); + + return IRQ_HANDLED; +} + +static int vt8500_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev); + u32 date, time; + + date = readl(vt8500_rtc->regbase + VT8500_RTC_DR); + time = readl(vt8500_rtc->regbase + VT8500_RTC_TR); + + tm->tm_sec = bcd2bin(time & TIME_SEC_MASK); + tm->tm_min = bcd2bin((time & TIME_MIN_MASK) >> TIME_MIN_S); + tm->tm_hour = bcd2bin((time & TIME_HOUR_MASK) >> TIME_HOUR_S); + tm->tm_mday = bcd2bin(date & DATE_DAY_MASK); + tm->tm_mon = bcd2bin((date & DATE_MONTH_MASK) >> DATE_MONTH_S) - 1; + tm->tm_year = bcd2bin((date & DATE_YEAR_MASK) >> DATE_YEAR_S) + + ((date >> DATE_CENTURY_S) & 1 ? 200 : 100); + tm->tm_wday = (time & TIME_DOW_MASK) >> TIME_DOW_S; + + return 0; +} + +static int vt8500_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev); + + writel((bin2bcd(tm->tm_year % 100) << DATE_YEAR_S) + | (bin2bcd(tm->tm_mon + 1) << DATE_MONTH_S) + | (bin2bcd(tm->tm_mday)) + | ((tm->tm_year >= 200) << DATE_CENTURY_S), + vt8500_rtc->regbase + VT8500_RTC_DS); + writel((bin2bcd(tm->tm_wday) << TIME_DOW_S) + | (bin2bcd(tm->tm_hour) << TIME_HOUR_S) + | (bin2bcd(tm->tm_min) << TIME_MIN_S) + | (bin2bcd(tm->tm_sec)), + vt8500_rtc->regbase + VT8500_RTC_TS); + + return 0; +} + +static int vt8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev); + u32 isr, alarm; + + alarm = readl(vt8500_rtc->regbase + VT8500_RTC_AS); + isr = readl(vt8500_rtc->regbase + VT8500_RTC_IS); + + alrm->time.tm_mday = bcd2bin((alarm & ALARM_DAY_MASK) >> ALARM_DAY_S); + alrm->time.tm_hour = bcd2bin((alarm & TIME_HOUR_MASK) >> TIME_HOUR_S); + alrm->time.tm_min = bcd2bin((alarm & TIME_MIN_MASK) >> TIME_MIN_S); + alrm->time.tm_sec = bcd2bin((alarm & TIME_SEC_MASK)); + + alrm->enabled = (alarm & ALARM_ENABLE_MASK) ? 1 : 0; + alrm->pending = (isr & VT8500_RTC_IS_ALARM) ? 1 : 0; + + return rtc_valid_tm(&alrm->time); +} + +static int vt8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev); + + writel((alrm->enabled ? ALARM_ENABLE_MASK : 0) + | (bin2bcd(alrm->time.tm_mday) << ALARM_DAY_S) + | (bin2bcd(alrm->time.tm_hour) << TIME_HOUR_S) + | (bin2bcd(alrm->time.tm_min) << TIME_MIN_S) + | (bin2bcd(alrm->time.tm_sec)), + vt8500_rtc->regbase + VT8500_RTC_AS); + + return 0; +} + +static int vt8500_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev); + unsigned long tmp = readl(vt8500_rtc->regbase + VT8500_RTC_AS); + + if (enabled) + tmp |= ALARM_ENABLE_MASK; + else + tmp &= ~ALARM_ENABLE_MASK; + + writel(tmp, vt8500_rtc->regbase + VT8500_RTC_AS); + return 0; +} + +static const struct rtc_class_ops vt8500_rtc_ops = { + .read_time = vt8500_rtc_read_time, + .set_time = vt8500_rtc_set_time, + .read_alarm = vt8500_rtc_read_alarm, + .set_alarm = vt8500_rtc_set_alarm, + .alarm_irq_enable = vt8500_alarm_irq_enable, +}; + +static int vt8500_rtc_probe(struct platform_device *pdev) +{ + struct vt8500_rtc *vt8500_rtc; + int ret; + + vt8500_rtc = devm_kzalloc(&pdev->dev, + sizeof(struct vt8500_rtc), GFP_KERNEL); + if (!vt8500_rtc) + return -ENOMEM; + + spin_lock_init(&vt8500_rtc->lock); + platform_set_drvdata(pdev, vt8500_rtc); + + vt8500_rtc->irq_alarm = platform_get_irq(pdev, 0); + if (vt8500_rtc->irq_alarm < 0) + return vt8500_rtc->irq_alarm; + + vt8500_rtc->regbase = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(vt8500_rtc->regbase)) + return PTR_ERR(vt8500_rtc->regbase); + + /* Enable RTC and set it to 24-hour mode */ + writel(VT8500_RTC_CR_ENABLE, + vt8500_rtc->regbase + VT8500_RTC_CR); + + vt8500_rtc->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(vt8500_rtc->rtc)) + return PTR_ERR(vt8500_rtc->rtc); + + vt8500_rtc->rtc->ops = &vt8500_rtc_ops; + vt8500_rtc->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + vt8500_rtc->rtc->range_max = RTC_TIMESTAMP_END_2199; + + ret = devm_request_irq(&pdev->dev, vt8500_rtc->irq_alarm, + vt8500_rtc_irq, 0, "rtc alarm", vt8500_rtc); + if (ret < 0) { + dev_err(&pdev->dev, "can't get irq %i, err %d\n", + vt8500_rtc->irq_alarm, ret); + return ret; + } + + return devm_rtc_register_device(vt8500_rtc->rtc); +} + +static void vt8500_rtc_remove(struct platform_device *pdev) +{ + struct vt8500_rtc *vt8500_rtc = platform_get_drvdata(pdev); + + /* Disable alarm matching */ + writel(0, vt8500_rtc->regbase + VT8500_RTC_IS); +} + +static const struct of_device_id wmt_dt_ids[] = { + { .compatible = "via,vt8500-rtc", }, + {} +}; +MODULE_DEVICE_TABLE(of, wmt_dt_ids); + +static struct platform_driver vt8500_rtc_driver = { + .probe = vt8500_rtc_probe, + .remove = vt8500_rtc_remove, + .driver = { + .name = "vt8500-rtc", + .of_match_table = wmt_dt_ids, + }, +}; + +module_platform_driver(vt8500_rtc_driver); + +MODULE_AUTHOR("Alexey Charkov "); +MODULE_DESCRIPTION("VIA VT8500 SoC Realtime Clock Driver (RTC)"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:vt8500-rtc"); diff --git a/drivers/rtc/rtc-wilco-ec.c b/drivers/rtc/rtc-wilco-ec.c new file mode 100644 index 000000000..2a205a646 --- /dev/null +++ b/drivers/rtc/rtc-wilco-ec.c @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RTC interface for Wilco Embedded Controller with R/W abilities + * + * Copyright 2018 Google LLC + * + * The corresponding platform device is typically registered in + * drivers/platform/chrome/wilco_ec/core.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define EC_COMMAND_CMOS 0x7c +#define EC_CMOS_TOD_WRITE 0x02 +#define EC_CMOS_TOD_READ 0x08 + +/* Message sent to the EC to request the current time. */ +struct ec_rtc_read_request { + u8 command; + u8 reserved; + u8 param; +} __packed; +static struct ec_rtc_read_request read_rq = { + .command = EC_COMMAND_CMOS, + .param = EC_CMOS_TOD_READ, +}; + +/** + * struct ec_rtc_read_response - Format of RTC returned by EC. + * @reserved: Unused byte + * @second: Second value (0..59) + * @minute: Minute value (0..59) + * @hour: Hour value (0..23) + * @day: Day value (1..31) + * @month: Month value (1..12) + * @year: Year value (full year % 100) + * @century: Century value (full year / 100) + * + * All values are presented in binary (not BCD). + */ +struct ec_rtc_read_response { + u8 reserved; + u8 second; + u8 minute; + u8 hour; + u8 day; + u8 month; + u8 year; + u8 century; +} __packed; + +/** + * struct ec_rtc_write_request - Format of RTC sent to the EC. + * @command: Always EC_COMMAND_CMOS + * @reserved: Unused byte + * @param: Always EC_CMOS_TOD_WRITE + * @century: Century value (full year / 100) + * @year: Year value (full year % 100) + * @month: Month value (1..12) + * @day: Day value (1..31) + * @hour: Hour value (0..23) + * @minute: Minute value (0..59) + * @second: Second value (0..59) + * @weekday: Day of the week (0=Saturday) + * + * All values are presented in BCD. + */ +struct ec_rtc_write_request { + u8 command; + u8 reserved; + u8 param; + u8 century; + u8 year; + u8 month; + u8 day; + u8 hour; + u8 minute; + u8 second; + u8 weekday; +} __packed; + +static int wilco_ec_rtc_read(struct device *dev, struct rtc_time *tm) +{ + struct wilco_ec_device *ec = dev_get_drvdata(dev->parent); + struct ec_rtc_read_response rtc; + struct wilco_ec_message msg; + int ret; + + memset(&msg, 0, sizeof(msg)); + msg.type = WILCO_EC_MSG_LEGACY; + msg.request_data = &read_rq; + msg.request_size = sizeof(read_rq); + msg.response_data = &rtc; + msg.response_size = sizeof(rtc); + + ret = wilco_ec_mailbox(ec, &msg); + if (ret < 0) + return ret; + + tm->tm_sec = rtc.second; + tm->tm_min = rtc.minute; + tm->tm_hour = rtc.hour; + tm->tm_mday = rtc.day; + tm->tm_mon = rtc.month - 1; + tm->tm_year = rtc.year + (rtc.century * 100) - 1900; + /* Ignore other tm fields, man rtc says userspace shouldn't use them. */ + + if (rtc_valid_tm(tm)) { + dev_err(dev, "Time from RTC is invalid: %ptRr\n", tm); + return -EIO; + } + + return 0; +} + +static int wilco_ec_rtc_write(struct device *dev, struct rtc_time *tm) +{ + struct wilco_ec_device *ec = dev_get_drvdata(dev->parent); + struct ec_rtc_write_request rtc; + struct wilco_ec_message msg; + int year = tm->tm_year + 1900; + /* + * Convert from 0=Sunday to 0=Saturday for the EC + * We DO need to set weekday because the EC controls battery charging + * schedules that depend on the day of the week. + */ + int wday = tm->tm_wday == 6 ? 0 : tm->tm_wday + 1; + int ret; + + rtc.command = EC_COMMAND_CMOS; + rtc.param = EC_CMOS_TOD_WRITE; + rtc.century = bin2bcd(year / 100); + rtc.year = bin2bcd(year % 100); + rtc.month = bin2bcd(tm->tm_mon + 1); + rtc.day = bin2bcd(tm->tm_mday); + rtc.hour = bin2bcd(tm->tm_hour); + rtc.minute = bin2bcd(tm->tm_min); + rtc.second = bin2bcd(tm->tm_sec); + rtc.weekday = bin2bcd(wday); + + memset(&msg, 0, sizeof(msg)); + msg.type = WILCO_EC_MSG_LEGACY; + msg.request_data = &rtc; + msg.request_size = sizeof(rtc); + + ret = wilco_ec_mailbox(ec, &msg); + if (ret < 0) + return ret; + + return 0; +} + +static const struct rtc_class_ops wilco_ec_rtc_ops = { + .read_time = wilco_ec_rtc_read, + .set_time = wilco_ec_rtc_write, +}; + +static int wilco_ec_rtc_probe(struct platform_device *pdev) +{ + struct rtc_device *rtc; + + rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + rtc->ops = &wilco_ec_rtc_ops; + /* EC only supports this century */ + rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rtc->range_max = RTC_TIMESTAMP_END_2099; + rtc->owner = THIS_MODULE; + + return devm_rtc_register_device(rtc); +} + +static struct platform_driver wilco_ec_rtc_driver = { + .driver = { + .name = "rtc-wilco-ec", + }, + .probe = wilco_ec_rtc_probe, +}; + +module_platform_driver(wilco_ec_rtc_driver); + +MODULE_ALIAS("platform:rtc-wilco-ec"); +MODULE_AUTHOR("Nick Crews "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Wilco EC RTC driver"); diff --git a/drivers/rtc/rtc-wm831x.c b/drivers/rtc/rtc-wm831x.c new file mode 100644 index 000000000..218316be9 --- /dev/null +++ b/drivers/rtc/rtc-wm831x.c @@ -0,0 +1,475 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Real Time Clock driver for Wolfson Microelectronics WM831x + * + * Copyright (C) 2009 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * R16416 (0x4020) - RTC Write Counter + */ +#define WM831X_RTC_WR_CNT_MASK 0xFFFF /* RTC_WR_CNT - [15:0] */ +#define WM831X_RTC_WR_CNT_SHIFT 0 /* RTC_WR_CNT - [15:0] */ +#define WM831X_RTC_WR_CNT_WIDTH 16 /* RTC_WR_CNT - [15:0] */ + +/* + * R16417 (0x4021) - RTC Time 1 + */ +#define WM831X_RTC_TIME_MASK 0xFFFF /* RTC_TIME - [15:0] */ +#define WM831X_RTC_TIME_SHIFT 0 /* RTC_TIME - [15:0] */ +#define WM831X_RTC_TIME_WIDTH 16 /* RTC_TIME - [15:0] */ + +/* + * R16418 (0x4022) - RTC Time 2 + */ +#define WM831X_RTC_TIME_MASK 0xFFFF /* RTC_TIME - [15:0] */ +#define WM831X_RTC_TIME_SHIFT 0 /* RTC_TIME - [15:0] */ +#define WM831X_RTC_TIME_WIDTH 16 /* RTC_TIME - [15:0] */ + +/* + * R16419 (0x4023) - RTC Alarm 1 + */ +#define WM831X_RTC_ALM_MASK 0xFFFF /* RTC_ALM - [15:0] */ +#define WM831X_RTC_ALM_SHIFT 0 /* RTC_ALM - [15:0] */ +#define WM831X_RTC_ALM_WIDTH 16 /* RTC_ALM - [15:0] */ + +/* + * R16420 (0x4024) - RTC Alarm 2 + */ +#define WM831X_RTC_ALM_MASK 0xFFFF /* RTC_ALM - [15:0] */ +#define WM831X_RTC_ALM_SHIFT 0 /* RTC_ALM - [15:0] */ +#define WM831X_RTC_ALM_WIDTH 16 /* RTC_ALM - [15:0] */ + +/* + * R16421 (0x4025) - RTC Control + */ +#define WM831X_RTC_VALID 0x8000 /* RTC_VALID */ +#define WM831X_RTC_VALID_MASK 0x8000 /* RTC_VALID */ +#define WM831X_RTC_VALID_SHIFT 15 /* RTC_VALID */ +#define WM831X_RTC_VALID_WIDTH 1 /* RTC_VALID */ +#define WM831X_RTC_SYNC_BUSY 0x4000 /* RTC_SYNC_BUSY */ +#define WM831X_RTC_SYNC_BUSY_MASK 0x4000 /* RTC_SYNC_BUSY */ +#define WM831X_RTC_SYNC_BUSY_SHIFT 14 /* RTC_SYNC_BUSY */ +#define WM831X_RTC_SYNC_BUSY_WIDTH 1 /* RTC_SYNC_BUSY */ +#define WM831X_RTC_ALM_ENA 0x0400 /* RTC_ALM_ENA */ +#define WM831X_RTC_ALM_ENA_MASK 0x0400 /* RTC_ALM_ENA */ +#define WM831X_RTC_ALM_ENA_SHIFT 10 /* RTC_ALM_ENA */ +#define WM831X_RTC_ALM_ENA_WIDTH 1 /* RTC_ALM_ENA */ +#define WM831X_RTC_PINT_FREQ_MASK 0x0070 /* RTC_PINT_FREQ - [6:4] */ +#define WM831X_RTC_PINT_FREQ_SHIFT 4 /* RTC_PINT_FREQ - [6:4] */ +#define WM831X_RTC_PINT_FREQ_WIDTH 3 /* RTC_PINT_FREQ - [6:4] */ + +/* + * R16422 (0x4026) - RTC Trim + */ +#define WM831X_RTC_TRIM_MASK 0x03FF /* RTC_TRIM - [9:0] */ +#define WM831X_RTC_TRIM_SHIFT 0 /* RTC_TRIM - [9:0] */ +#define WM831X_RTC_TRIM_WIDTH 10 /* RTC_TRIM - [9:0] */ + +#define WM831X_SET_TIME_RETRIES 5 +#define WM831X_GET_TIME_RETRIES 5 + +struct wm831x_rtc { + struct wm831x *wm831x; + struct rtc_device *rtc; + unsigned int alarm_enabled:1; +}; + +static void wm831x_rtc_add_randomness(struct wm831x *wm831x) +{ + int ret; + u16 reg; + + /* + * The write counter contains a pseudo-random number which is + * regenerated every time we set the RTC so it should be a + * useful per-system source of entropy. + */ + ret = wm831x_reg_read(wm831x, WM831X_RTC_WRITE_COUNTER); + if (ret >= 0) { + reg = ret; + add_device_randomness(®, sizeof(reg)); + } else { + dev_warn(wm831x->dev, "Failed to read RTC write counter: %d\n", + ret); + } +} + +/* + * Read current time and date in RTC + */ +static int wm831x_rtc_readtime(struct device *dev, struct rtc_time *tm) +{ + struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev); + struct wm831x *wm831x = wm831x_rtc->wm831x; + u16 time1[2], time2[2]; + int ret; + int count = 0; + + /* Has the RTC been programmed? */ + ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL); + if (ret < 0) { + dev_err(dev, "Failed to read RTC control: %d\n", ret); + return ret; + } + if (!(ret & WM831X_RTC_VALID)) { + dev_dbg(dev, "RTC not yet configured\n"); + return -EINVAL; + } + + /* Read twice to make sure we don't read a corrupt, partially + * incremented, value. + */ + do { + ret = wm831x_bulk_read(wm831x, WM831X_RTC_TIME_1, + 2, time1); + if (ret != 0) + continue; + + ret = wm831x_bulk_read(wm831x, WM831X_RTC_TIME_1, + 2, time2); + if (ret != 0) + continue; + + if (memcmp(time1, time2, sizeof(time1)) == 0) { + u32 time = (time1[0] << 16) | time1[1]; + + rtc_time64_to_tm(time, tm); + return 0; + } + + } while (++count < WM831X_GET_TIME_RETRIES); + + dev_err(dev, "Timed out reading current time\n"); + + return -EIO; +} + +/* + * Set current time and date in RTC + */ +static int wm831x_rtc_settime(struct device *dev, struct rtc_time *tm) +{ + struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev); + struct wm831x *wm831x = wm831x_rtc->wm831x; + struct rtc_time new_tm; + unsigned long time, new_time; + int ret; + int count = 0; + + time = rtc_tm_to_time64(tm); + + ret = wm831x_reg_write(wm831x, WM831X_RTC_TIME_1, + (time >> 16) & 0xffff); + if (ret < 0) { + dev_err(dev, "Failed to write TIME_1: %d\n", ret); + return ret; + } + + ret = wm831x_reg_write(wm831x, WM831X_RTC_TIME_2, time & 0xffff); + if (ret < 0) { + dev_err(dev, "Failed to write TIME_2: %d\n", ret); + return ret; + } + + /* Wait for the update to complete - should happen first time + * round but be conservative. + */ + do { + msleep(1); + + ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL); + if (ret < 0) + ret = WM831X_RTC_SYNC_BUSY; + } while (!(ret & WM831X_RTC_SYNC_BUSY) && + ++count < WM831X_SET_TIME_RETRIES); + + if (ret & WM831X_RTC_SYNC_BUSY) { + dev_err(dev, "Timed out writing RTC update\n"); + return -EIO; + } + + /* Check that the update was accepted; security features may + * have caused the update to be ignored. + */ + ret = wm831x_rtc_readtime(dev, &new_tm); + if (ret < 0) + return ret; + + new_time = rtc_tm_to_time64(&new_tm); + + /* Allow a second of change in case of tick */ + if (new_time - time > 1) { + dev_err(dev, "RTC update not permitted by hardware\n"); + return -EPERM; + } + + return 0; +} + +/* + * Read alarm time and date in RTC + */ +static int wm831x_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev); + int ret; + u16 data[2]; + u32 time; + + ret = wm831x_bulk_read(wm831x_rtc->wm831x, WM831X_RTC_ALARM_1, + 2, data); + if (ret != 0) { + dev_err(dev, "Failed to read alarm time: %d\n", ret); + return ret; + } + + time = (data[0] << 16) | data[1]; + + rtc_time64_to_tm(time, &alrm->time); + + ret = wm831x_reg_read(wm831x_rtc->wm831x, WM831X_RTC_CONTROL); + if (ret < 0) { + dev_err(dev, "Failed to read RTC control: %d\n", ret); + return ret; + } + + if (ret & WM831X_RTC_ALM_ENA) + alrm->enabled = 1; + else + alrm->enabled = 0; + + return 0; +} + +static int wm831x_rtc_stop_alarm(struct wm831x_rtc *wm831x_rtc) +{ + wm831x_rtc->alarm_enabled = 0; + + return wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL, + WM831X_RTC_ALM_ENA, 0); +} + +static int wm831x_rtc_start_alarm(struct wm831x_rtc *wm831x_rtc) +{ + wm831x_rtc->alarm_enabled = 1; + + return wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL, + WM831X_RTC_ALM_ENA, WM831X_RTC_ALM_ENA); +} + +static int wm831x_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev); + struct wm831x *wm831x = wm831x_rtc->wm831x; + int ret; + unsigned long time; + + time = rtc_tm_to_time64(&alrm->time); + + ret = wm831x_rtc_stop_alarm(wm831x_rtc); + if (ret < 0) { + dev_err(dev, "Failed to stop alarm: %d\n", ret); + return ret; + } + + ret = wm831x_reg_write(wm831x, WM831X_RTC_ALARM_1, + (time >> 16) & 0xffff); + if (ret < 0) { + dev_err(dev, "Failed to write ALARM_1: %d\n", ret); + return ret; + } + + ret = wm831x_reg_write(wm831x, WM831X_RTC_ALARM_2, time & 0xffff); + if (ret < 0) { + dev_err(dev, "Failed to write ALARM_2: %d\n", ret); + return ret; + } + + if (alrm->enabled) { + ret = wm831x_rtc_start_alarm(wm831x_rtc); + if (ret < 0) { + dev_err(dev, "Failed to start alarm: %d\n", ret); + return ret; + } + } + + return 0; +} + +static int wm831x_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev); + + if (enabled) + return wm831x_rtc_start_alarm(wm831x_rtc); + else + return wm831x_rtc_stop_alarm(wm831x_rtc); +} + +static irqreturn_t wm831x_alm_irq(int irq, void *data) +{ + struct wm831x_rtc *wm831x_rtc = data; + + rtc_update_irq(wm831x_rtc->rtc, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops wm831x_rtc_ops = { + .read_time = wm831x_rtc_readtime, + .set_time = wm831x_rtc_settime, + .read_alarm = wm831x_rtc_readalarm, + .set_alarm = wm831x_rtc_setalarm, + .alarm_irq_enable = wm831x_rtc_alarm_irq_enable, +}; + +#ifdef CONFIG_PM +/* Turn off the alarm if it should not be a wake source. */ +static int wm831x_rtc_suspend(struct device *dev) +{ + struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev); + int ret, enable; + + if (wm831x_rtc->alarm_enabled && device_may_wakeup(dev)) + enable = WM831X_RTC_ALM_ENA; + else + enable = 0; + + ret = wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL, + WM831X_RTC_ALM_ENA, enable); + if (ret != 0) + dev_err(dev, "Failed to update RTC alarm: %d\n", ret); + + return 0; +} + +/* Enable the alarm if it should be enabled (in case it was disabled to + * prevent use as a wake source). + */ +static int wm831x_rtc_resume(struct device *dev) +{ + struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev); + int ret; + + if (wm831x_rtc->alarm_enabled) { + ret = wm831x_rtc_start_alarm(wm831x_rtc); + if (ret != 0) + dev_err(dev, "Failed to restart RTC alarm: %d\n", ret); + } + + return 0; +} + +/* Unconditionally disable the alarm */ +static int wm831x_rtc_freeze(struct device *dev) +{ + struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev); + int ret; + + ret = wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL, + WM831X_RTC_ALM_ENA, 0); + if (ret != 0) + dev_err(dev, "Failed to stop RTC alarm: %d\n", ret); + + return 0; +} +#else +#define wm831x_rtc_suspend NULL +#define wm831x_rtc_resume NULL +#define wm831x_rtc_freeze NULL +#endif + +static int wm831x_rtc_probe(struct platform_device *pdev) +{ + struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); + struct wm831x_rtc *wm831x_rtc; + int alm_irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "ALM")); + int ret = 0; + + wm831x_rtc = devm_kzalloc(&pdev->dev, sizeof(*wm831x_rtc), GFP_KERNEL); + if (wm831x_rtc == NULL) + return -ENOMEM; + + platform_set_drvdata(pdev, wm831x_rtc); + wm831x_rtc->wm831x = wm831x; + + ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to read RTC control: %d\n", ret); + return ret; + } + if (ret & WM831X_RTC_ALM_ENA) + wm831x_rtc->alarm_enabled = 1; + + device_init_wakeup(&pdev->dev, true); + + wm831x_rtc->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(wm831x_rtc->rtc)) + return PTR_ERR(wm831x_rtc->rtc); + + wm831x_rtc->rtc->ops = &wm831x_rtc_ops; + wm831x_rtc->rtc->range_max = U32_MAX; + + ret = devm_rtc_register_device(wm831x_rtc->rtc); + if (ret) + return ret; + + ret = devm_request_threaded_irq(&pdev->dev, alm_irq, NULL, + wm831x_alm_irq, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "RTC alarm", + wm831x_rtc); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n", + alm_irq, ret); + } + + wm831x_rtc_add_randomness(wm831x); + + return 0; +} + +static const struct dev_pm_ops wm831x_rtc_pm_ops = { + .suspend = wm831x_rtc_suspend, + .resume = wm831x_rtc_resume, + + .freeze = wm831x_rtc_freeze, + .thaw = wm831x_rtc_resume, + .restore = wm831x_rtc_resume, + + .poweroff = wm831x_rtc_suspend, +}; + +static struct platform_driver wm831x_rtc_driver = { + .probe = wm831x_rtc_probe, + .driver = { + .name = "wm831x-rtc", + .pm = &wm831x_rtc_pm_ops, + }, +}; + +module_platform_driver(wm831x_rtc_driver); + +MODULE_AUTHOR("Mark Brown "); +MODULE_DESCRIPTION("RTC driver for the WM831x series PMICs"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm831x-rtc"); diff --git a/drivers/rtc/rtc-wm8350.c b/drivers/rtc/rtc-wm8350.c new file mode 100644 index 000000000..3bd60d067 --- /dev/null +++ b/drivers/rtc/rtc-wm8350.c @@ -0,0 +1,474 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Real Time Clock driver for Wolfson Microelectronics WM8350 + * + * Copyright (C) 2007, 2008 Wolfson Microelectronics PLC. + * + * Author: Liam Girdwood + * linux@wolfsonmicro.com + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WM8350_SET_ALM_RETRIES 5 +#define WM8350_SET_TIME_RETRIES 5 +#define WM8350_GET_TIME_RETRIES 5 + +/* + * Read current time and date in RTC + */ +static int wm8350_rtc_readtime(struct device *dev, struct rtc_time *tm) +{ + struct wm8350 *wm8350 = dev_get_drvdata(dev); + u16 time1[4], time2[4]; + int retries = WM8350_GET_TIME_RETRIES, ret; + + /* + * Read the time twice and compare. + * If time1 == time2, then time is valid else retry. + */ + do { + ret = wm8350_block_read(wm8350, WM8350_RTC_SECONDS_MINUTES, + 4, time1); + if (ret < 0) + return ret; + ret = wm8350_block_read(wm8350, WM8350_RTC_SECONDS_MINUTES, + 4, time2); + if (ret < 0) + return ret; + + if (memcmp(time1, time2, sizeof(time1)) == 0) { + tm->tm_sec = time1[0] & WM8350_RTC_SECS_MASK; + + tm->tm_min = (time1[0] & WM8350_RTC_MINS_MASK) + >> WM8350_RTC_MINS_SHIFT; + + tm->tm_hour = time1[1] & WM8350_RTC_HRS_MASK; + + tm->tm_wday = ((time1[1] >> WM8350_RTC_DAY_SHIFT) + & 0x7) - 1; + + tm->tm_mon = ((time1[2] & WM8350_RTC_MTH_MASK) + >> WM8350_RTC_MTH_SHIFT) - 1; + + tm->tm_mday = (time1[2] & WM8350_RTC_DATE_MASK); + + tm->tm_year = ((time1[3] & WM8350_RTC_YHUNDREDS_MASK) + >> WM8350_RTC_YHUNDREDS_SHIFT) * 100; + tm->tm_year += time1[3] & WM8350_RTC_YUNITS_MASK; + + tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, + tm->tm_year); + tm->tm_year -= 1900; + + dev_dbg(dev, "Read (%d left): %04x %04x %04x %04x\n", + retries, + time1[0], time1[1], time1[2], time1[3]); + + return 0; + } + } while (retries--); + + dev_err(dev, "timed out reading RTC time\n"); + return -EIO; +} + +/* + * Set current time and date in RTC + */ +static int wm8350_rtc_settime(struct device *dev, struct rtc_time *tm) +{ + struct wm8350 *wm8350 = dev_get_drvdata(dev); + u16 time[4]; + u16 rtc_ctrl; + int ret, retries = WM8350_SET_TIME_RETRIES; + + time[0] = tm->tm_sec; + time[0] |= tm->tm_min << WM8350_RTC_MINS_SHIFT; + time[1] = tm->tm_hour; + time[1] |= (tm->tm_wday + 1) << WM8350_RTC_DAY_SHIFT; + time[2] = tm->tm_mday; + time[2] |= (tm->tm_mon + 1) << WM8350_RTC_MTH_SHIFT; + time[3] = ((tm->tm_year + 1900) / 100) << WM8350_RTC_YHUNDREDS_SHIFT; + time[3] |= (tm->tm_year + 1900) % 100; + + dev_dbg(dev, "Setting: %04x %04x %04x %04x\n", + time[0], time[1], time[2], time[3]); + + /* Set RTC_SET to stop the clock */ + ret = wm8350_set_bits(wm8350, WM8350_RTC_TIME_CONTROL, WM8350_RTC_SET); + if (ret < 0) + return ret; + + /* Wait until confirmation of stopping */ + do { + rtc_ctrl = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL); + schedule_timeout_uninterruptible(msecs_to_jiffies(1)); + } while (--retries && !(rtc_ctrl & WM8350_RTC_STS)); + + if (!retries) { + dev_err(dev, "timed out on set confirmation\n"); + return -EIO; + } + + /* Write time to RTC */ + ret = wm8350_block_write(wm8350, WM8350_RTC_SECONDS_MINUTES, 4, time); + if (ret < 0) + return ret; + + /* Clear RTC_SET to start the clock */ + ret = wm8350_clear_bits(wm8350, WM8350_RTC_TIME_CONTROL, + WM8350_RTC_SET); + return ret; +} + +/* + * Read alarm time and date in RTC + */ +static int wm8350_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct wm8350 *wm8350 = dev_get_drvdata(dev); + struct rtc_time *tm = &alrm->time; + u16 time[4]; + int ret; + + ret = wm8350_block_read(wm8350, WM8350_ALARM_SECONDS_MINUTES, 4, time); + if (ret < 0) + return ret; + + tm->tm_sec = time[0] & WM8350_RTC_ALMSECS_MASK; + if (tm->tm_sec == WM8350_RTC_ALMSECS_MASK) + tm->tm_sec = -1; + + tm->tm_min = time[0] & WM8350_RTC_ALMMINS_MASK; + if (tm->tm_min == WM8350_RTC_ALMMINS_MASK) + tm->tm_min = -1; + else + tm->tm_min >>= WM8350_RTC_ALMMINS_SHIFT; + + tm->tm_hour = time[1] & WM8350_RTC_ALMHRS_MASK; + if (tm->tm_hour == WM8350_RTC_ALMHRS_MASK) + tm->tm_hour = -1; + + tm->tm_wday = ((time[1] >> WM8350_RTC_ALMDAY_SHIFT) & 0x7) - 1; + if (tm->tm_wday > 7) + tm->tm_wday = -1; + + tm->tm_mon = time[2] & WM8350_RTC_ALMMTH_MASK; + if (tm->tm_mon == WM8350_RTC_ALMMTH_MASK) + tm->tm_mon = -1; + else + tm->tm_mon = (tm->tm_mon >> WM8350_RTC_ALMMTH_SHIFT) - 1; + + tm->tm_mday = (time[2] & WM8350_RTC_ALMDATE_MASK); + if (tm->tm_mday == WM8350_RTC_ALMDATE_MASK) + tm->tm_mday = -1; + + tm->tm_year = -1; + + alrm->enabled = !(time[3] & WM8350_RTC_ALMSTS); + + return 0; +} + +static int wm8350_rtc_stop_alarm(struct wm8350 *wm8350) +{ + int retries = WM8350_SET_ALM_RETRIES; + u16 rtc_ctrl; + int ret; + + /* Set RTC_SET to stop the clock */ + ret = wm8350_set_bits(wm8350, WM8350_RTC_TIME_CONTROL, + WM8350_RTC_ALMSET); + if (ret < 0) + return ret; + + /* Wait until confirmation of stopping */ + do { + rtc_ctrl = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL); + schedule_timeout_uninterruptible(msecs_to_jiffies(1)); + } while (retries-- && !(rtc_ctrl & WM8350_RTC_ALMSTS)); + + if (!(rtc_ctrl & WM8350_RTC_ALMSTS)) + return -ETIMEDOUT; + + return 0; +} + +static int wm8350_rtc_start_alarm(struct wm8350 *wm8350) +{ + int ret; + int retries = WM8350_SET_ALM_RETRIES; + u16 rtc_ctrl; + + ret = wm8350_clear_bits(wm8350, WM8350_RTC_TIME_CONTROL, + WM8350_RTC_ALMSET); + if (ret < 0) + return ret; + + /* Wait until confirmation */ + do { + rtc_ctrl = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL); + schedule_timeout_uninterruptible(msecs_to_jiffies(1)); + } while (retries-- && rtc_ctrl & WM8350_RTC_ALMSTS); + + if (rtc_ctrl & WM8350_RTC_ALMSTS) + return -ETIMEDOUT; + + return 0; +} + +static int wm8350_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct wm8350 *wm8350 = dev_get_drvdata(dev); + + if (enabled) + return wm8350_rtc_start_alarm(wm8350); + else + return wm8350_rtc_stop_alarm(wm8350); +} + +static int wm8350_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct wm8350 *wm8350 = dev_get_drvdata(dev); + struct rtc_time *tm = &alrm->time; + u16 time[3]; + int ret; + + memset(time, 0, sizeof(time)); + + if (tm->tm_sec != -1) + time[0] |= tm->tm_sec; + else + time[0] |= WM8350_RTC_ALMSECS_MASK; + + if (tm->tm_min != -1) + time[0] |= tm->tm_min << WM8350_RTC_ALMMINS_SHIFT; + else + time[0] |= WM8350_RTC_ALMMINS_MASK; + + if (tm->tm_hour != -1) + time[1] |= tm->tm_hour; + else + time[1] |= WM8350_RTC_ALMHRS_MASK; + + if (tm->tm_wday != -1) + time[1] |= (tm->tm_wday + 1) << WM8350_RTC_ALMDAY_SHIFT; + else + time[1] |= WM8350_RTC_ALMDAY_MASK; + + if (tm->tm_mday != -1) + time[2] |= tm->tm_mday; + else + time[2] |= WM8350_RTC_ALMDATE_MASK; + + if (tm->tm_mon != -1) + time[2] |= (tm->tm_mon + 1) << WM8350_RTC_ALMMTH_SHIFT; + else + time[2] |= WM8350_RTC_ALMMTH_MASK; + + ret = wm8350_rtc_stop_alarm(wm8350); + if (ret < 0) + return ret; + + /* Write time to RTC */ + ret = wm8350_block_write(wm8350, WM8350_ALARM_SECONDS_MINUTES, + 3, time); + if (ret < 0) + return ret; + + if (alrm->enabled) + ret = wm8350_rtc_start_alarm(wm8350); + + return ret; +} + +static irqreturn_t wm8350_rtc_alarm_handler(int irq, void *data) +{ + struct wm8350 *wm8350 = data; + struct rtc_device *rtc = wm8350->rtc.rtc; + int ret; + + rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF); + + /* Make it one shot */ + ret = wm8350_set_bits(wm8350, WM8350_RTC_TIME_CONTROL, + WM8350_RTC_ALMSET); + if (ret != 0) { + dev_err(&(wm8350->rtc.pdev->dev), + "Failed to disable alarm: %d\n", ret); + } + + return IRQ_HANDLED; +} + +static irqreturn_t wm8350_rtc_update_handler(int irq, void *data) +{ + struct wm8350 *wm8350 = data; + struct rtc_device *rtc = wm8350->rtc.rtc; + + rtc_update_irq(rtc, 1, RTC_IRQF | RTC_UF); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops wm8350_rtc_ops = { + .read_time = wm8350_rtc_readtime, + .set_time = wm8350_rtc_settime, + .read_alarm = wm8350_rtc_readalarm, + .set_alarm = wm8350_rtc_setalarm, + .alarm_irq_enable = wm8350_rtc_alarm_irq_enable, +}; + +#ifdef CONFIG_PM_SLEEP +static int wm8350_rtc_suspend(struct device *dev) +{ + struct wm8350 *wm8350 = dev_get_drvdata(dev); + int ret = 0; + u16 reg; + + reg = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL); + + if (device_may_wakeup(&wm8350->rtc.pdev->dev) && + reg & WM8350_RTC_ALMSTS) { + ret = wm8350_rtc_stop_alarm(wm8350); + if (ret != 0) + dev_err(dev, "Failed to stop RTC alarm: %d\n", ret); + } + + return ret; +} + +static int wm8350_rtc_resume(struct device *dev) +{ + struct wm8350 *wm8350 = dev_get_drvdata(dev); + int ret; + + if (wm8350->rtc.alarm_enabled) { + ret = wm8350_rtc_start_alarm(wm8350); + if (ret != 0) + dev_err(dev, "Failed to restart RTC alarm: %d\n", ret); + } + + return 0; +} +#endif + +static int wm8350_rtc_probe(struct platform_device *pdev) +{ + struct wm8350 *wm8350 = platform_get_drvdata(pdev); + struct wm8350_rtc *wm_rtc = &wm8350->rtc; + int ret = 0; + u16 timectl, power5; + + timectl = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL); + if (timectl & WM8350_RTC_BCD) { + dev_err(&pdev->dev, "RTC BCD mode not supported\n"); + return -EINVAL; + } + if (timectl & WM8350_RTC_12HR) { + dev_err(&pdev->dev, "RTC 12 hour mode not supported\n"); + return -EINVAL; + } + + /* enable the RTC if it's not already enabled */ + power5 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_5); + if (!(power5 & WM8350_RTC_TICK_ENA)) { + wm8350_reg_unlock(wm8350); + + ret = wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, + WM8350_RTC_TICK_ENA); + if (ret < 0) { + dev_err(&pdev->dev, "failed to enable RTC: %d\n", ret); + return ret; + } + + wm8350_reg_lock(wm8350); + } + + if (timectl & WM8350_RTC_STS) { + int retries; + + ret = wm8350_clear_bits(wm8350, WM8350_RTC_TIME_CONTROL, + WM8350_RTC_SET); + if (ret < 0) { + dev_err(&pdev->dev, "failed to start: %d\n", ret); + return ret; + } + + retries = WM8350_SET_TIME_RETRIES; + do { + timectl = wm8350_reg_read(wm8350, + WM8350_RTC_TIME_CONTROL); + } while (timectl & WM8350_RTC_STS && --retries); + + if (retries == 0) { + dev_err(&pdev->dev, "failed to start: timeout\n"); + return -ENODEV; + } + } + + device_init_wakeup(&pdev->dev, true); + + wm_rtc->rtc = devm_rtc_device_register(&pdev->dev, "wm8350", + &wm8350_rtc_ops, THIS_MODULE); + if (IS_ERR(wm_rtc->rtc)) + return PTR_ERR(wm_rtc->rtc); + + ret = wm8350_register_irq(wm8350, WM8350_IRQ_RTC_SEC, + wm8350_rtc_update_handler, 0, + "RTC Seconds", wm8350); + if (ret) + return ret; + + wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC); + + ret = wm8350_register_irq(wm8350, WM8350_IRQ_RTC_ALM, + wm8350_rtc_alarm_handler, 0, + "RTC Alarm", wm8350); + if (ret) { + wm8350_free_irq(wm8350, WM8350_IRQ_RTC_SEC, wm8350); + return ret; + } + + return 0; +} + +static void wm8350_rtc_remove(struct platform_device *pdev) +{ + struct wm8350 *wm8350 = platform_get_drvdata(pdev); + + wm8350_free_irq(wm8350, WM8350_IRQ_RTC_SEC, wm8350); + wm8350_free_irq(wm8350, WM8350_IRQ_RTC_ALM, wm8350); +} + +static SIMPLE_DEV_PM_OPS(wm8350_rtc_pm_ops, wm8350_rtc_suspend, + wm8350_rtc_resume); + +static struct platform_driver wm8350_rtc_driver = { + .probe = wm8350_rtc_probe, + .remove = wm8350_rtc_remove, + .driver = { + .name = "wm8350-rtc", + .pm = &wm8350_rtc_pm_ops, + }, +}; + +module_platform_driver(wm8350_rtc_driver); + +MODULE_AUTHOR("Mark Brown "); +MODULE_DESCRIPTION("RTC driver for the WM8350"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm8350-rtc"); diff --git a/drivers/rtc/rtc-x1205.c b/drivers/rtc/rtc-x1205.c new file mode 100644 index 000000000..bb1a5c86f --- /dev/null +++ b/drivers/rtc/rtc-x1205.c @@ -0,0 +1,693 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * An i2c driver for the Xicor/Intersil X1205 RTC + * Copyright 2004 Karen Spearel + * Copyright 2005 Alessandro Zummo + * + * please send all reports to: + * Karen Spearel + * Alessandro Zummo + * + * based on a lot of other RTC drivers. + * + * Information and datasheet: + * http://www.intersil.com/cda/deviceinfo/0,1477,X1205,00.html + */ + +#include +#include +#include +#include +#include +#include + +/* offsets into CCR area */ + +#define CCR_SEC 0 +#define CCR_MIN 1 +#define CCR_HOUR 2 +#define CCR_MDAY 3 +#define CCR_MONTH 4 +#define CCR_YEAR 5 +#define CCR_WDAY 6 +#define CCR_Y2K 7 + +#define X1205_REG_SR 0x3F /* status register */ +#define X1205_REG_Y2K 0x37 +#define X1205_REG_DW 0x36 +#define X1205_REG_YR 0x35 +#define X1205_REG_MO 0x34 +#define X1205_REG_DT 0x33 +#define X1205_REG_HR 0x32 +#define X1205_REG_MN 0x31 +#define X1205_REG_SC 0x30 +#define X1205_REG_DTR 0x13 +#define X1205_REG_ATR 0x12 +#define X1205_REG_INT 0x11 +#define X1205_REG_0 0x10 +#define X1205_REG_Y2K1 0x0F +#define X1205_REG_DWA1 0x0E +#define X1205_REG_YRA1 0x0D +#define X1205_REG_MOA1 0x0C +#define X1205_REG_DTA1 0x0B +#define X1205_REG_HRA1 0x0A +#define X1205_REG_MNA1 0x09 +#define X1205_REG_SCA1 0x08 +#define X1205_REG_Y2K0 0x07 +#define X1205_REG_DWA0 0x06 +#define X1205_REG_YRA0 0x05 +#define X1205_REG_MOA0 0x04 +#define X1205_REG_DTA0 0x03 +#define X1205_REG_HRA0 0x02 +#define X1205_REG_MNA0 0x01 +#define X1205_REG_SCA0 0x00 + +#define X1205_CCR_BASE 0x30 /* Base address of CCR */ +#define X1205_ALM0_BASE 0x00 /* Base address of ALARM0 */ + +#define X1205_SR_RTCF 0x01 /* Clock failure */ +#define X1205_SR_WEL 0x02 /* Write Enable Latch */ +#define X1205_SR_RWEL 0x04 /* Register Write Enable */ +#define X1205_SR_AL0 0x20 /* Alarm 0 match */ + +#define X1205_DTR_DTR0 0x01 +#define X1205_DTR_DTR1 0x02 +#define X1205_DTR_DTR2 0x04 + +#define X1205_HR_MIL 0x80 /* Set in ccr.hour for 24 hr mode */ + +#define X1205_INT_AL0E 0x20 /* Alarm 0 enable */ + +static struct i2c_driver x1205_driver; + +/* + * In the routines that deal directly with the x1205 hardware, we use + * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch + * Epoch is initialized as 2000. Time is set to UTC. + */ +static int x1205_get_datetime(struct i2c_client *client, struct rtc_time *tm, + unsigned char reg_base) +{ + unsigned char dt_addr[2] = { 0, reg_base }; + unsigned char buf[8]; + int i; + + struct i2c_msg msgs[] = { + {/* setup read ptr */ + .addr = client->addr, + .len = 2, + .buf = dt_addr + }, + {/* read date */ + .addr = client->addr, + .flags = I2C_M_RD, + .len = 8, + .buf = buf + }, + }; + + /* read date registers */ + if (i2c_transfer(client->adapter, &msgs[0], 2) != 2) { + dev_err(&client->dev, "%s: read error\n", __func__); + return -EIO; + } + + dev_dbg(&client->dev, + "%s: raw read data - sec=%02x, min=%02x, hr=%02x, " + "mday=%02x, mon=%02x, year=%02x, wday=%02x, y2k=%02x\n", + __func__, + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); + + /* Mask out the enable bits if these are alarm registers */ + if (reg_base < X1205_CCR_BASE) + for (i = 0; i <= 4; i++) + buf[i] &= 0x7F; + + tm->tm_sec = bcd2bin(buf[CCR_SEC]); + tm->tm_min = bcd2bin(buf[CCR_MIN]); + tm->tm_hour = bcd2bin(buf[CCR_HOUR] & 0x3F); /* hr is 0-23 */ + tm->tm_mday = bcd2bin(buf[CCR_MDAY]); + tm->tm_mon = bcd2bin(buf[CCR_MONTH]) - 1; /* mon is 0-11 */ + tm->tm_year = bcd2bin(buf[CCR_YEAR]) + + (bcd2bin(buf[CCR_Y2K]) * 100) - 1900; + tm->tm_wday = buf[CCR_WDAY]; + + dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + return 0; +} + +static int x1205_get_status(struct i2c_client *client, unsigned char *sr) +{ + static unsigned char sr_addr[2] = { 0, X1205_REG_SR }; + + struct i2c_msg msgs[] = { + { /* setup read ptr */ + .addr = client->addr, + .len = 2, + .buf = sr_addr + }, + { /* read status */ + .addr = client->addr, + .flags = I2C_M_RD, + .len = 1, + .buf = sr + }, + }; + + /* read status register */ + if (i2c_transfer(client->adapter, &msgs[0], 2) != 2) { + dev_err(&client->dev, "%s: read error\n", __func__); + return -EIO; + } + + return 0; +} + +static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm, + u8 reg_base, unsigned char alm_enable) +{ + int i, xfer; + unsigned char rdata[10] = { 0, reg_base }; + unsigned char *buf = rdata + 2; + + static const unsigned char wel[3] = { 0, X1205_REG_SR, + X1205_SR_WEL }; + + static const unsigned char rwel[3] = { 0, X1205_REG_SR, + X1205_SR_WEL | X1205_SR_RWEL }; + + static const unsigned char diswe[3] = { 0, X1205_REG_SR, 0 }; + + dev_dbg(&client->dev, + "%s: sec=%d min=%d hour=%d mday=%d mon=%d year=%d wday=%d\n", + __func__, tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, + tm->tm_mon, tm->tm_year, tm->tm_wday); + + buf[CCR_SEC] = bin2bcd(tm->tm_sec); + buf[CCR_MIN] = bin2bcd(tm->tm_min); + + /* set hour and 24hr bit */ + buf[CCR_HOUR] = bin2bcd(tm->tm_hour) | X1205_HR_MIL; + + buf[CCR_MDAY] = bin2bcd(tm->tm_mday); + + /* month, 1 - 12 */ + buf[CCR_MONTH] = bin2bcd(tm->tm_mon + 1); + + /* year, since the rtc epoch*/ + buf[CCR_YEAR] = bin2bcd(tm->tm_year % 100); + buf[CCR_WDAY] = tm->tm_wday & 0x07; + buf[CCR_Y2K] = bin2bcd((tm->tm_year + 1900) / 100); + + /* If writing alarm registers, set compare bits on registers 0-4 */ + if (reg_base < X1205_CCR_BASE) + for (i = 0; i <= 4; i++) + buf[i] |= 0x80; + + /* this sequence is required to unlock the chip */ + xfer = i2c_master_send(client, wel, 3); + if (xfer != 3) { + dev_err(&client->dev, "%s: wel - %d\n", __func__, xfer); + return -EIO; + } + + xfer = i2c_master_send(client, rwel, 3); + if (xfer != 3) { + dev_err(&client->dev, "%s: rwel - %d\n", __func__, xfer); + return -EIO; + } + + xfer = i2c_master_send(client, rdata, sizeof(rdata)); + if (xfer != sizeof(rdata)) { + dev_err(&client->dev, + "%s: result=%d addr=%02x, data=%02x\n", + __func__, + xfer, rdata[1], rdata[2]); + return -EIO; + } + + /* If we wrote to the nonvolatile region, wait 10msec for write cycle*/ + if (reg_base < X1205_CCR_BASE) { + unsigned char al0e[3] = { 0, X1205_REG_INT, 0 }; + + msleep(10); + + /* ...and set or clear the AL0E bit in the INT register */ + + /* Need to set RWEL again as the write has cleared it */ + xfer = i2c_master_send(client, rwel, 3); + if (xfer != 3) { + dev_err(&client->dev, + "%s: aloe rwel - %d\n", + __func__, + xfer); + return -EIO; + } + + if (alm_enable) + al0e[2] = X1205_INT_AL0E; + + xfer = i2c_master_send(client, al0e, 3); + if (xfer != 3) { + dev_err(&client->dev, + "%s: al0e - %d\n", + __func__, + xfer); + return -EIO; + } + + /* and wait 10msec again for this write to complete */ + msleep(10); + } + + /* disable further writes */ + xfer = i2c_master_send(client, diswe, 3); + if (xfer != 3) { + dev_err(&client->dev, "%s: diswe - %d\n", __func__, xfer); + return -EIO; + } + + return 0; +} + +static int x1205_fix_osc(struct i2c_client *client) +{ + int err; + struct rtc_time tm; + + memset(&tm, 0, sizeof(tm)); + + err = x1205_set_datetime(client, &tm, X1205_CCR_BASE, 0); + if (err < 0) + dev_err(&client->dev, "unable to restart the oscillator\n"); + + return err; +} + +static int x1205_get_dtrim(struct i2c_client *client, int *trim) +{ + unsigned char dtr; + static unsigned char dtr_addr[2] = { 0, X1205_REG_DTR }; + + struct i2c_msg msgs[] = { + { /* setup read ptr */ + .addr = client->addr, + .len = 2, + .buf = dtr_addr + }, + { /* read dtr */ + .addr = client->addr, + .flags = I2C_M_RD, + .len = 1, + .buf = &dtr + }, + }; + + /* read dtr register */ + if (i2c_transfer(client->adapter, &msgs[0], 2) != 2) { + dev_err(&client->dev, "%s: read error\n", __func__); + return -EIO; + } + + dev_dbg(&client->dev, "%s: raw dtr=%x\n", __func__, dtr); + + *trim = 0; + + if (dtr & X1205_DTR_DTR0) + *trim += 20; + + if (dtr & X1205_DTR_DTR1) + *trim += 10; + + if (dtr & X1205_DTR_DTR2) + *trim = -*trim; + + return 0; +} + +static int x1205_get_atrim(struct i2c_client *client, int *trim) +{ + s8 atr; + static unsigned char atr_addr[2] = { 0, X1205_REG_ATR }; + + struct i2c_msg msgs[] = { + {/* setup read ptr */ + .addr = client->addr, + .len = 2, + .buf = atr_addr + }, + {/* read atr */ + .addr = client->addr, + .flags = I2C_M_RD, + .len = 1, + .buf = &atr + }, + }; + + /* read atr register */ + if (i2c_transfer(client->adapter, &msgs[0], 2) != 2) { + dev_err(&client->dev, "%s: read error\n", __func__); + return -EIO; + } + + dev_dbg(&client->dev, "%s: raw atr=%x\n", __func__, atr); + + /* atr is a two's complement value on 6 bits, + * perform sign extension. The formula is + * Catr = (atr * 0.25pF) + 11.00pF. + */ + atr = sign_extend32(atr, 5); + + dev_dbg(&client->dev, "%s: raw atr=%x (%d)\n", __func__, atr, atr); + + *trim = (atr * 250) + 11000; + + dev_dbg(&client->dev, "%s: real=%d\n", __func__, *trim); + + return 0; +} + +struct x1205_limit { + unsigned char reg, mask, min, max; +}; + +static int x1205_validate_client(struct i2c_client *client) +{ + int i, xfer; + + /* Probe array. We will read the register at the specified + * address and check if the given bits are zero. + */ + static const unsigned char probe_zero_pattern[] = { + /* register, mask */ + X1205_REG_SR, 0x18, + X1205_REG_DTR, 0xF8, + X1205_REG_ATR, 0xC0, + X1205_REG_INT, 0x18, + X1205_REG_0, 0xFF, + }; + + static const struct x1205_limit probe_limits_pattern[] = { + /* register, mask, min, max */ + { X1205_REG_Y2K, 0xFF, 19, 20 }, + { X1205_REG_DW, 0xFF, 0, 6 }, + { X1205_REG_YR, 0xFF, 0, 99 }, + { X1205_REG_MO, 0xFF, 0, 12 }, + { X1205_REG_DT, 0xFF, 0, 31 }, + { X1205_REG_HR, 0x7F, 0, 23 }, + { X1205_REG_MN, 0xFF, 0, 59 }, + { X1205_REG_SC, 0xFF, 0, 59 }, + { X1205_REG_Y2K1, 0xFF, 19, 20 }, + { X1205_REG_Y2K0, 0xFF, 19, 20 }, + }; + + /* check that registers have bits a 0 where expected */ + for (i = 0; i < ARRAY_SIZE(probe_zero_pattern); i += 2) { + unsigned char buf; + + unsigned char addr[2] = { 0, probe_zero_pattern[i] }; + + struct i2c_msg msgs[2] = { + { + .addr = client->addr, + .len = 2, + .buf = addr + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = 1, + .buf = &buf + }, + }; + + xfer = i2c_transfer(client->adapter, msgs, 2); + if (xfer != 2) { + dev_err(&client->dev, + "%s: could not read register %x\n", + __func__, probe_zero_pattern[i]); + + return -EIO; + } + + if ((buf & probe_zero_pattern[i+1]) != 0) { + dev_err(&client->dev, + "%s: register=%02x, zero pattern=%d, value=%x\n", + __func__, probe_zero_pattern[i], i, buf); + + return -ENODEV; + } + } + + /* check limits (only registers with bcd values) */ + for (i = 0; i < ARRAY_SIZE(probe_limits_pattern); i++) { + unsigned char reg, value; + + unsigned char addr[2] = { 0, probe_limits_pattern[i].reg }; + + struct i2c_msg msgs[2] = { + { + .addr = client->addr, + .len = 2, + .buf = addr + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = 1, + .buf = ® + }, + }; + + xfer = i2c_transfer(client->adapter, msgs, 2); + if (xfer != 2) { + dev_err(&client->dev, + "%s: could not read register %x\n", + __func__, probe_limits_pattern[i].reg); + + return -EIO; + } + + value = bcd2bin(reg & probe_limits_pattern[i].mask); + + if (value > probe_limits_pattern[i].max || + value < probe_limits_pattern[i].min) { + dev_dbg(&client->dev, + "%s: register=%x, lim pattern=%d, value=%d\n", + __func__, probe_limits_pattern[i].reg, + i, value); + + return -ENODEV; + } + } + + return 0; +} + +static int x1205_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + int err; + unsigned char intreg, status; + static unsigned char int_addr[2] = { 0, X1205_REG_INT }; + struct i2c_client *client = to_i2c_client(dev); + struct i2c_msg msgs[] = { + { /* setup read ptr */ + .addr = client->addr, + .len = 2, + .buf = int_addr + }, + {/* read INT register */ + + .addr = client->addr, + .flags = I2C_M_RD, + .len = 1, + .buf = &intreg + }, + }; + + /* read interrupt register and status register */ + if (i2c_transfer(client->adapter, &msgs[0], 2) != 2) { + dev_err(&client->dev, "%s: read error\n", __func__); + return -EIO; + } + err = x1205_get_status(client, &status); + if (err == 0) { + alrm->pending = (status & X1205_SR_AL0) ? 1 : 0; + alrm->enabled = (intreg & X1205_INT_AL0E) ? 1 : 0; + err = x1205_get_datetime(client, &alrm->time, X1205_ALM0_BASE); + } + return err; +} + +static int x1205_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + return x1205_set_datetime(to_i2c_client(dev), + &alrm->time, X1205_ALM0_BASE, alrm->enabled); +} + +static int x1205_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + return x1205_get_datetime(to_i2c_client(dev), + tm, X1205_CCR_BASE); +} + +static int x1205_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + return x1205_set_datetime(to_i2c_client(dev), + tm, X1205_CCR_BASE, 0); +} + +static int x1205_rtc_proc(struct device *dev, struct seq_file *seq) +{ + int err, dtrim, atrim; + + err = x1205_get_dtrim(to_i2c_client(dev), &dtrim); + if (!err) + seq_printf(seq, "digital_trim\t: %d ppm\n", dtrim); + + err = x1205_get_atrim(to_i2c_client(dev), &atrim); + if (!err) + seq_printf(seq, "analog_trim\t: %d.%02d pF\n", + atrim / 1000, atrim % 1000); + return 0; +} + +static const struct rtc_class_ops x1205_rtc_ops = { + .proc = x1205_rtc_proc, + .read_time = x1205_rtc_read_time, + .set_time = x1205_rtc_set_time, + .read_alarm = x1205_rtc_read_alarm, + .set_alarm = x1205_rtc_set_alarm, +}; + +static ssize_t x1205_sysfs_show_atrim(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int err, atrim; + + err = x1205_get_atrim(to_i2c_client(dev), &atrim); + if (err) + return err; + + return sprintf(buf, "%d.%02d pF\n", atrim / 1000, atrim % 1000); +} +static DEVICE_ATTR(atrim, S_IRUGO, x1205_sysfs_show_atrim, NULL); + +static ssize_t x1205_sysfs_show_dtrim(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int err, dtrim; + + err = x1205_get_dtrim(to_i2c_client(dev), &dtrim); + if (err) + return err; + + return sprintf(buf, "%d ppm\n", dtrim); +} +static DEVICE_ATTR(dtrim, S_IRUGO, x1205_sysfs_show_dtrim, NULL); + +static int x1205_sysfs_register(struct device *dev) +{ + int err; + + err = device_create_file(dev, &dev_attr_atrim); + if (err) + return err; + + err = device_create_file(dev, &dev_attr_dtrim); + if (err) + device_remove_file(dev, &dev_attr_atrim); + + return err; +} + +static void x1205_sysfs_unregister(struct device *dev) +{ + device_remove_file(dev, &dev_attr_atrim); + device_remove_file(dev, &dev_attr_dtrim); +} + + +static int x1205_probe(struct i2c_client *client) +{ + int err = 0; + unsigned char sr; + struct rtc_device *rtc; + + dev_dbg(&client->dev, "%s\n", __func__); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + if (x1205_validate_client(client) < 0) + return -ENODEV; + + rtc = devm_rtc_device_register(&client->dev, x1205_driver.driver.name, + &x1205_rtc_ops, THIS_MODULE); + + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + i2c_set_clientdata(client, rtc); + + /* Check for power failures and eventually enable the osc */ + err = x1205_get_status(client, &sr); + if (!err) { + if (sr & X1205_SR_RTCF) { + dev_err(&client->dev, + "power failure detected, " + "please set the clock\n"); + udelay(50); + x1205_fix_osc(client); + } + } else { + dev_err(&client->dev, "couldn't read status\n"); + } + + err = x1205_sysfs_register(&client->dev); + if (err) + dev_err(&client->dev, "Unable to create sysfs entries\n"); + + return 0; +} + +static void x1205_remove(struct i2c_client *client) +{ + x1205_sysfs_unregister(&client->dev); +} + +static const struct i2c_device_id x1205_id[] = { + { .name = "x1205" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, x1205_id); + +static const struct of_device_id x1205_dt_ids[] = { + { .compatible = "xicor,x1205", }, + {}, +}; +MODULE_DEVICE_TABLE(of, x1205_dt_ids); + +static struct i2c_driver x1205_driver = { + .driver = { + .name = "rtc-x1205", + .of_match_table = x1205_dt_ids, + }, + .probe = x1205_probe, + .remove = x1205_remove, + .id_table = x1205_id, +}; + +module_i2c_driver(x1205_driver); + +MODULE_AUTHOR( + "Karen Spearel , " + "Alessandro Zummo "); +MODULE_DESCRIPTION("Xicor/Intersil X1205 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-xgene.c b/drivers/rtc/rtc-xgene.c new file mode 100644 index 000000000..6660b664e --- /dev/null +++ b/drivers/rtc/rtc-xgene.c @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * APM X-Gene SoC Real Time Clock Driver + * + * Copyright (c) 2014, Applied Micro Circuits Corporation + * Author: Rameshwar Prasad Sahu + * Loc Ho + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* RTC CSR Registers */ +#define RTC_CCVR 0x00 +#define RTC_CMR 0x04 +#define RTC_CLR 0x08 +#define RTC_CCR 0x0C +#define RTC_CCR_IE BIT(0) +#define RTC_CCR_MASK BIT(1) +#define RTC_CCR_EN BIT(2) +#define RTC_CCR_WEN BIT(3) +#define RTC_STAT 0x10 +#define RTC_STAT_BIT BIT(0) +#define RTC_RSTAT 0x14 +#define RTC_EOI 0x18 +#define RTC_VER 0x1C + +struct xgene_rtc_dev { + struct rtc_device *rtc; + void __iomem *csr_base; + struct clk *clk; + unsigned int irq_wake; + unsigned int irq_enabled; +}; + +static int xgene_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct xgene_rtc_dev *pdata = dev_get_drvdata(dev); + + rtc_time64_to_tm(readl(pdata->csr_base + RTC_CCVR), tm); + return 0; +} + +static int xgene_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct xgene_rtc_dev *pdata = dev_get_drvdata(dev); + + /* + * NOTE: After the following write, the RTC_CCVR is only reflected + * after the update cycle of 1 seconds. + */ + writel((u32)rtc_tm_to_time64(tm), pdata->csr_base + RTC_CLR); + readl(pdata->csr_base + RTC_CLR); /* Force a barrier */ + + return 0; +} + +static int xgene_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct xgene_rtc_dev *pdata = dev_get_drvdata(dev); + + /* If possible, CMR should be read here */ + rtc_time64_to_tm(0, &alrm->time); + alrm->enabled = readl(pdata->csr_base + RTC_CCR) & RTC_CCR_IE; + + return 0; +} + +static int xgene_rtc_alarm_irq_enable(struct device *dev, u32 enabled) +{ + struct xgene_rtc_dev *pdata = dev_get_drvdata(dev); + u32 ccr; + + ccr = readl(pdata->csr_base + RTC_CCR); + if (enabled) { + ccr &= ~RTC_CCR_MASK; + ccr |= RTC_CCR_IE; + } else { + ccr &= ~RTC_CCR_IE; + ccr |= RTC_CCR_MASK; + } + writel(ccr, pdata->csr_base + RTC_CCR); + + return 0; +} + +static int xgene_rtc_alarm_irq_enabled(struct device *dev) +{ + struct xgene_rtc_dev *pdata = dev_get_drvdata(dev); + + return readl(pdata->csr_base + RTC_CCR) & RTC_CCR_IE ? 1 : 0; +} + +static int xgene_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct xgene_rtc_dev *pdata = dev_get_drvdata(dev); + + writel((u32)rtc_tm_to_time64(&alrm->time), pdata->csr_base + RTC_CMR); + + xgene_rtc_alarm_irq_enable(dev, alrm->enabled); + + return 0; +} + +static const struct rtc_class_ops xgene_rtc_ops = { + .read_time = xgene_rtc_read_time, + .set_time = xgene_rtc_set_time, + .read_alarm = xgene_rtc_read_alarm, + .set_alarm = xgene_rtc_set_alarm, + .alarm_irq_enable = xgene_rtc_alarm_irq_enable, +}; + +static irqreturn_t xgene_rtc_interrupt(int irq, void *id) +{ + struct xgene_rtc_dev *pdata = id; + + /* Check if interrupt asserted */ + if (!(readl(pdata->csr_base + RTC_STAT) & RTC_STAT_BIT)) + return IRQ_NONE; + + /* Clear interrupt */ + readl(pdata->csr_base + RTC_EOI); + + rtc_update_irq(pdata->rtc, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static int xgene_rtc_probe(struct platform_device *pdev) +{ + struct xgene_rtc_dev *pdata; + int ret; + int irq; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + platform_set_drvdata(pdev, pdata); + + pdata->csr_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pdata->csr_base)) + return PTR_ERR(pdata->csr_base); + + pdata->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(pdata->rtc)) + return PTR_ERR(pdata->rtc); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + ret = devm_request_irq(&pdev->dev, irq, xgene_rtc_interrupt, 0, + dev_name(&pdev->dev), pdata); + if (ret) { + dev_err(&pdev->dev, "Could not request IRQ\n"); + return ret; + } + + pdata->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pdata->clk)) { + dev_err(&pdev->dev, "Couldn't get the clock for RTC\n"); + return -ENODEV; + } + ret = clk_prepare_enable(pdata->clk); + if (ret) + return ret; + + /* Turn on the clock and the crystal */ + writel(RTC_CCR_EN, pdata->csr_base + RTC_CCR); + + ret = device_init_wakeup(&pdev->dev, true); + if (ret) { + clk_disable_unprepare(pdata->clk); + return ret; + } + + pdata->rtc->ops = &xgene_rtc_ops; + pdata->rtc->range_max = U32_MAX; + + ret = devm_rtc_register_device(pdata->rtc); + if (ret) { + clk_disable_unprepare(pdata->clk); + return ret; + } + + return 0; +} + +static void xgene_rtc_remove(struct platform_device *pdev) +{ + struct xgene_rtc_dev *pdata = platform_get_drvdata(pdev); + + xgene_rtc_alarm_irq_enable(&pdev->dev, 0); + device_init_wakeup(&pdev->dev, false); + clk_disable_unprepare(pdata->clk); +} + +static int __maybe_unused xgene_rtc_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct xgene_rtc_dev *pdata = platform_get_drvdata(pdev); + int irq; + + irq = platform_get_irq(pdev, 0); + + /* + * If this RTC alarm will be used for waking the system up, + * don't disable it of course. Else we just disable the alarm + * and await suspension. + */ + if (device_may_wakeup(&pdev->dev)) { + if (!enable_irq_wake(irq)) + pdata->irq_wake = 1; + } else { + pdata->irq_enabled = xgene_rtc_alarm_irq_enabled(dev); + xgene_rtc_alarm_irq_enable(dev, 0); + clk_disable_unprepare(pdata->clk); + } + return 0; +} + +static int __maybe_unused xgene_rtc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct xgene_rtc_dev *pdata = platform_get_drvdata(pdev); + int irq; + int rc; + + irq = platform_get_irq(pdev, 0); + + if (device_may_wakeup(&pdev->dev)) { + if (pdata->irq_wake) { + disable_irq_wake(irq); + pdata->irq_wake = 0; + } + } else { + rc = clk_prepare_enable(pdata->clk); + if (rc) { + dev_err(dev, "Unable to enable clock error %d\n", rc); + return rc; + } + xgene_rtc_alarm_irq_enable(dev, pdata->irq_enabled); + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(xgene_rtc_pm_ops, xgene_rtc_suspend, xgene_rtc_resume); + +#ifdef CONFIG_OF +static const struct of_device_id xgene_rtc_of_match[] = { + {.compatible = "apm,xgene-rtc" }, + { } +}; +MODULE_DEVICE_TABLE(of, xgene_rtc_of_match); +#endif + +static struct platform_driver xgene_rtc_driver = { + .probe = xgene_rtc_probe, + .remove = xgene_rtc_remove, + .driver = { + .name = "xgene-rtc", + .pm = &xgene_rtc_pm_ops, + .of_match_table = of_match_ptr(xgene_rtc_of_match), + }, +}; + +module_platform_driver(xgene_rtc_driver); + +MODULE_DESCRIPTION("APM X-Gene SoC RTC driver"); +MODULE_AUTHOR("Rameshwar Sahu "); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-zynqmp.c b/drivers/rtc/rtc-zynqmp.c new file mode 100644 index 000000000..5bcb7536e --- /dev/null +++ b/drivers/rtc/rtc-zynqmp.c @@ -0,0 +1,422 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx Zynq Ultrascale+ MPSoC Real Time Clock Driver + * + * Copyright (C) 2015 Xilinx, Inc. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* RTC Registers */ +#define RTC_SET_TM_WR 0x00 +#define RTC_SET_TM_RD 0x04 +#define RTC_CALIB_WR 0x08 +#define RTC_CALIB_RD 0x0C +#define RTC_CUR_TM 0x10 +#define RTC_CUR_TICK 0x14 +#define RTC_ALRM 0x18 +#define RTC_INT_STS 0x20 +#define RTC_INT_MASK 0x24 +#define RTC_INT_EN 0x28 +#define RTC_INT_DIS 0x2C +#define RTC_CTRL 0x40 + +#define RTC_FR_EN BIT(20) +#define RTC_FR_DATSHIFT 16 +#define RTC_TICK_MASK 0xFFFF +#define RTC_INT_SEC BIT(0) +#define RTC_INT_ALRM BIT(1) +#define RTC_OSC_EN BIT(24) +#define RTC_BATT_EN BIT(31) + +#define RTC_CALIB_DEF 0x7FFF +#define RTC_CALIB_MASK 0x1FFFFF +#define RTC_ALRM_MASK BIT(1) +#define RTC_MSEC 1000 +#define RTC_FR_MASK 0xF0000 +#define RTC_FR_MAX_TICKS 16 +#define RTC_PPB 1000000000 + +struct xlnx_rtc_dev { + struct rtc_device *rtc; + void __iomem *reg_base; + int alarm_irq; + int sec_irq; + struct clk *rtc_clk; + unsigned int freq; +}; + +static int xlnx_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); + unsigned long new_time; + + /* + * The value written will be updated after 1 sec into the + * seconds read register, so we need to program time +1 sec + * to get the correct time on read. + */ + new_time = rtc_tm_to_time64(tm) + 1; + + writel(new_time, xrtcdev->reg_base + RTC_SET_TM_WR); + + /* + * Clear the rtc interrupt status register after setting the + * time. During a read_time function, the code should read the + * RTC_INT_STATUS register and if bit 0 is still 0, it means + * that one second has not elapsed yet since RTC was set and + * the current time should be read from SET_TIME_READ register; + * otherwise, CURRENT_TIME register is read to report the time + */ + writel(RTC_INT_SEC, xrtcdev->reg_base + RTC_INT_STS); + + return 0; +} + +static int xlnx_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + u32 status; + unsigned long read_time; + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); + + status = readl(xrtcdev->reg_base + RTC_INT_STS); + + if (status & RTC_INT_SEC) { + /* + * RTC has updated the CURRENT_TIME with the time written into + * SET_TIME_WRITE register. + */ + read_time = readl(xrtcdev->reg_base + RTC_CUR_TM); + } else { + /* + * Time written in SET_TIME_WRITE has not yet updated into + * the seconds read register, so read the time from the + * SET_TIME_WRITE instead of CURRENT_TIME register. + * Since we add +1 sec while writing, we need to -1 sec while + * reading. + */ + read_time = readl(xrtcdev->reg_base + RTC_SET_TM_RD) - 1; + } + rtc_time64_to_tm(read_time, tm); + + return 0; +} + +static int xlnx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); + + rtc_time64_to_tm(readl(xrtcdev->reg_base + RTC_ALRM), &alrm->time); + alrm->enabled = readl(xrtcdev->reg_base + RTC_INT_MASK) & RTC_INT_ALRM; + + return 0; +} + +static int xlnx_rtc_alarm_irq_enable(struct device *dev, u32 enabled) +{ + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); + unsigned int status; + ulong timeout; + + timeout = jiffies + msecs_to_jiffies(RTC_MSEC); + + if (enabled) { + while (1) { + status = readl(xrtcdev->reg_base + RTC_INT_STS); + if (!((status & RTC_ALRM_MASK) == RTC_ALRM_MASK)) + break; + + if (time_after_eq(jiffies, timeout)) { + dev_err(dev, "Time out occur, while clearing alarm status bit\n"); + return -ETIMEDOUT; + } + writel(RTC_INT_ALRM, xrtcdev->reg_base + RTC_INT_STS); + } + + writel(RTC_INT_ALRM, xrtcdev->reg_base + RTC_INT_EN); + } else { + writel(RTC_INT_ALRM, xrtcdev->reg_base + RTC_INT_DIS); + } + + return 0; +} + +static int xlnx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); + unsigned long alarm_time; + + alarm_time = rtc_tm_to_time64(&alrm->time); + + writel((u32)alarm_time, (xrtcdev->reg_base + RTC_ALRM)); + + xlnx_rtc_alarm_irq_enable(dev, alrm->enabled); + + return 0; +} + +static void xlnx_init_rtc(struct xlnx_rtc_dev *xrtcdev) +{ + u32 rtc_ctrl; + + /* Enable RTC switch to battery when VCC_PSAUX is not available */ + rtc_ctrl = readl(xrtcdev->reg_base + RTC_CTRL); + rtc_ctrl |= RTC_BATT_EN; + writel(rtc_ctrl, xrtcdev->reg_base + RTC_CTRL); +} + +static int xlnx_rtc_read_offset(struct device *dev, long *offset) +{ + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); + unsigned int calibval, fract_data, fract_part; + int freq = xrtcdev->freq; + int max_tick, tick_mult; + long offset_val; + + /* Tick to offset multiplier */ + tick_mult = DIV_ROUND_CLOSEST(RTC_PPB, freq); + + calibval = readl(xrtcdev->reg_base + RTC_CALIB_RD); + /* Offset with seconds ticks */ + max_tick = calibval & RTC_TICK_MASK; + offset_val = max_tick - freq; + /* Convert to ppb */ + offset_val *= tick_mult; + + /* Offset with fractional ticks */ + if (calibval & RTC_FR_EN) { + fract_data = (calibval & RTC_FR_MASK) >> RTC_FR_DATSHIFT; + fract_part = DIV_ROUND_UP(tick_mult, RTC_FR_MAX_TICKS); + offset_val += (fract_part * fract_data); + } + + *offset = offset_val; + + return 0; +} + +static int xlnx_rtc_set_offset(struct device *dev, long offset) +{ + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); + int max_tick, tick_mult, fract_offset, fract_part; + int freq = xrtcdev->freq; + unsigned int calibval; + int fract_data = 0; + + /* Tick to offset multiplier */ + tick_mult = DIV_ROUND_CLOSEST(RTC_PPB, freq); + + /* Number ticks for given offset */ + max_tick = div_s64_rem(offset, tick_mult, &fract_offset); + + if (freq + max_tick > RTC_TICK_MASK || (freq + max_tick < 1)) + return -ERANGE; + + /* Number fractional ticks for given offset */ + if (fract_offset) { + fract_part = DIV_ROUND_UP(tick_mult, RTC_FR_MAX_TICKS); + fract_data = fract_offset / fract_part; + /* Subtract one from max_tick while adding fract_offset */ + if (fract_offset < 0 && fract_data) { + max_tick--; + fract_data += RTC_FR_MAX_TICKS; + } + } + + /* Zynqmp RTC uses second and fractional tick + * counters for compensation + */ + calibval = max_tick + freq; + + if (fract_data) + calibval |= (RTC_FR_EN | (fract_data << RTC_FR_DATSHIFT)); + + writel(calibval, (xrtcdev->reg_base + RTC_CALIB_WR)); + + return 0; +} + +static const struct rtc_class_ops xlnx_rtc_ops = { + .set_time = xlnx_rtc_set_time, + .read_time = xlnx_rtc_read_time, + .read_alarm = xlnx_rtc_read_alarm, + .set_alarm = xlnx_rtc_set_alarm, + .alarm_irq_enable = xlnx_rtc_alarm_irq_enable, + .read_offset = xlnx_rtc_read_offset, + .set_offset = xlnx_rtc_set_offset, +}; + +static irqreturn_t xlnx_rtc_interrupt(int irq, void *id) +{ + struct xlnx_rtc_dev *xrtcdev = (struct xlnx_rtc_dev *)id; + unsigned int status; + + status = readl(xrtcdev->reg_base + RTC_INT_STS); + /* Check if interrupt asserted */ + if (!(status & (RTC_INT_SEC | RTC_INT_ALRM))) + return IRQ_NONE; + + /* Disable RTC_INT_ALRM interrupt only */ + writel(RTC_INT_ALRM, xrtcdev->reg_base + RTC_INT_DIS); + + if (status & RTC_INT_ALRM) + rtc_update_irq(xrtcdev->rtc, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static int xlnx_rtc_probe(struct platform_device *pdev) +{ + struct xlnx_rtc_dev *xrtcdev; + bool is_alarm_set = false; + u32 pending_alrm_irq; + u32 current_time; + u32 alarm_time; + int ret; + + xrtcdev = devm_kzalloc(&pdev->dev, sizeof(*xrtcdev), GFP_KERNEL); + if (!xrtcdev) + return -ENOMEM; + + platform_set_drvdata(pdev, xrtcdev); + + xrtcdev->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(xrtcdev->rtc)) + return PTR_ERR(xrtcdev->rtc); + + xrtcdev->rtc->ops = &xlnx_rtc_ops; + xrtcdev->rtc->range_max = U32_MAX; + + xrtcdev->reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(xrtcdev->reg_base)) + return PTR_ERR(xrtcdev->reg_base); + + /* Clear any pending alarm interrupts from previous kernel/boot */ + pending_alrm_irq = readl(xrtcdev->reg_base + RTC_INT_STS) & RTC_INT_ALRM; + if (pending_alrm_irq) + writel(pending_alrm_irq, xrtcdev->reg_base + RTC_INT_STS); + + /* Check if a valid alarm is already set from previous kernel/boot */ + alarm_time = readl(xrtcdev->reg_base + RTC_ALRM); + current_time = readl(xrtcdev->reg_base + RTC_CUR_TM); + if (alarm_time > current_time && alarm_time != 0) + is_alarm_set = true; + + xrtcdev->alarm_irq = platform_get_irq_byname(pdev, "alarm"); + if (xrtcdev->alarm_irq < 0) + return xrtcdev->alarm_irq; + ret = devm_request_irq(&pdev->dev, xrtcdev->alarm_irq, + xlnx_rtc_interrupt, 0, + dev_name(&pdev->dev), xrtcdev); + if (ret) { + dev_err(&pdev->dev, "request irq failed\n"); + return ret; + } + + xrtcdev->sec_irq = platform_get_irq_byname(pdev, "sec"); + if (xrtcdev->sec_irq < 0) + return xrtcdev->sec_irq; + ret = devm_request_irq(&pdev->dev, xrtcdev->sec_irq, + xlnx_rtc_interrupt, 0, + dev_name(&pdev->dev), xrtcdev); + if (ret) { + dev_err(&pdev->dev, "request irq failed\n"); + return ret; + } + + /* Getting the rtc info */ + xrtcdev->rtc_clk = devm_clk_get_optional(&pdev->dev, "rtc"); + if (IS_ERR(xrtcdev->rtc_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(xrtcdev->rtc_clk), + "Failed to get rtc clock\n"); + xrtcdev->freq = clk_get_rate(xrtcdev->rtc_clk); + if (!xrtcdev->freq) { + ret = of_property_read_u32(pdev->dev.of_node, "calibration", + &xrtcdev->freq); + if (ret) + xrtcdev->freq = RTC_CALIB_DEF; + } else { + xrtcdev->freq--; + } + + if (xrtcdev->freq > RTC_TICK_MASK) { + dev_err(&pdev->dev, "Invalid RTC calibration value\n"); + return -EINVAL; + } + + ret = readl(xrtcdev->reg_base + RTC_CALIB_RD); + if (!ret) + writel(xrtcdev->freq, (xrtcdev->reg_base + RTC_CALIB_WR)); + + xlnx_init_rtc(xrtcdev); + + /* Re-enable alarm interrupt if a valid alarm was found */ + if (is_alarm_set) + writel(RTC_INT_ALRM, xrtcdev->reg_base + RTC_INT_EN); + + device_init_wakeup(&pdev->dev, true); + + return devm_rtc_register_device(xrtcdev->rtc); +} + +static void xlnx_rtc_remove(struct platform_device *pdev) +{ + xlnx_rtc_alarm_irq_enable(&pdev->dev, 0); + device_init_wakeup(&pdev->dev, false); +} + +static int __maybe_unused xlnx_rtc_suspend(struct device *dev) +{ + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(xrtcdev->alarm_irq); + else + xlnx_rtc_alarm_irq_enable(dev, 0); + + return 0; +} + +static int __maybe_unused xlnx_rtc_resume(struct device *dev) +{ + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(xrtcdev->alarm_irq); + else + xlnx_rtc_alarm_irq_enable(dev, 1); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(xlnx_rtc_pm_ops, xlnx_rtc_suspend, xlnx_rtc_resume); + +static const struct of_device_id xlnx_rtc_of_match[] = { + {.compatible = "xlnx,zynqmp-rtc" }, + { } +}; +MODULE_DEVICE_TABLE(of, xlnx_rtc_of_match); + +static struct platform_driver xlnx_rtc_driver = { + .probe = xlnx_rtc_probe, + .remove = xlnx_rtc_remove, + .driver = { + .name = KBUILD_MODNAME, + .pm = &xlnx_rtc_pm_ops, + .of_match_table = xlnx_rtc_of_match, + }, +}; + +module_platform_driver(xlnx_rtc_driver); + +MODULE_DESCRIPTION("Xilinx Zynq MPSoC RTC driver"); +MODULE_AUTHOR("Xilinx Inc."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/sysfs.c b/drivers/rtc/sysfs.c new file mode 100644 index 000000000..4ab05e105 --- /dev/null +++ b/drivers/rtc/sysfs.c @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RTC subsystem, sysfs interface + * + * Copyright (C) 2005 Tower Technologies + * Author: Alessandro Zummo + */ + +#include +#include +#include + +#include "rtc-core.h" + +/* device attributes */ + +/* + * NOTE: RTC times displayed in sysfs use the RTC's timezone. That's + * ideally UTC. However, PCs that also boot to MS-Windows normally use + * the local time and change to match daylight savings time. That affects + * attributes including date, time, since_epoch, and wakealarm. + */ + +static ssize_t +name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%s %s\n", dev_driver_string(dev->parent), + dev_name(dev->parent)); +} +static DEVICE_ATTR_RO(name); + +static ssize_t +date_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t retval; + struct rtc_time tm; + + retval = rtc_read_time(to_rtc_device(dev), &tm); + if (retval) + return retval; + + return sysfs_emit(buf, "%ptRd\n", &tm); +} +static DEVICE_ATTR_RO(date); + +static ssize_t +time_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t retval; + struct rtc_time tm; + + retval = rtc_read_time(to_rtc_device(dev), &tm); + if (retval) + return retval; + + return sysfs_emit(buf, "%ptRt\n", &tm); +} +static DEVICE_ATTR_RO(time); + +static ssize_t +since_epoch_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t retval; + struct rtc_time tm; + + retval = rtc_read_time(to_rtc_device(dev), &tm); + if (retval) + return retval; + + return sysfs_emit(buf, "%lld\n", rtc_tm_to_time64(&tm)); +} +static DEVICE_ATTR_RO(since_epoch); + +static ssize_t +max_user_freq_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%d\n", to_rtc_device(dev)->max_user_freq); +} + +static ssize_t +max_user_freq_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t n) +{ + struct rtc_device *rtc = to_rtc_device(dev); + unsigned long val; + int err; + + err = kstrtoul(buf, 0, &val); + if (err) + return err; + + if (val >= 4096 || val == 0) + return -EINVAL; + + rtc->max_user_freq = (int)val; + + return n; +} +static DEVICE_ATTR_RW(max_user_freq); + +/** + * hctosys_show - indicate if the given RTC set the system time + * @dev: The device that the attribute belongs to. + * @attr: The attribute being read. + * @buf: The result buffer. + * + * buf is "1" if the system clock was set by this RTC at the last + * boot or resume event. + */ +static ssize_t +hctosys_show(struct device *dev, struct device_attribute *attr, char *buf) +{ +#ifdef CONFIG_RTC_HCTOSYS_DEVICE + if (rtc_hctosys_ret == 0 && + strcmp(dev_name(&to_rtc_device(dev)->dev), + CONFIG_RTC_HCTOSYS_DEVICE) == 0) + return sysfs_emit(buf, "1\n"); +#endif + return sysfs_emit(buf, "0\n"); +} +static DEVICE_ATTR_RO(hctosys); + +static ssize_t +wakealarm_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t retval; + struct rtc_wkalrm alm; + + /* Don't show disabled alarms. For uniformity, RTC alarms are + * conceptually one-shot, even though some common RTCs (on PCs) + * don't actually work that way. + * + * NOTE: RTC implementations where the alarm doesn't match an + * exact YYYY-MM-DD HH:MM[:SS] date *must* disable their RTC + * alarms after they trigger, to ensure one-shot semantics. + */ + retval = rtc_read_alarm(to_rtc_device(dev), &alm); + if (retval) + return retval; + + if (alm.enabled) + return sysfs_emit(buf, "%lld\n", rtc_tm_to_time64(&alm.time)); + + return 0; +} + +static ssize_t +wakealarm_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t n) +{ + ssize_t retval; + time64_t now, alarm; + time64_t push = 0; + struct rtc_wkalrm alm; + struct rtc_device *rtc = to_rtc_device(dev); + const char *buf_ptr; + int adjust = 0; + + /* Only request alarms that trigger in the future. Disable them + * by writing another time, e.g. 0 meaning Jan 1 1970 UTC. + */ + retval = rtc_read_time(rtc, &alm.time); + if (retval < 0) + return retval; + now = rtc_tm_to_time64(&alm.time); + + buf_ptr = buf; + if (*buf_ptr == '+') { + buf_ptr++; + if (*buf_ptr == '=') { + buf_ptr++; + push = 1; + } else { + adjust = 1; + } + } + retval = kstrtos64(buf_ptr, 0, &alarm); + if (retval) + return retval; + if (adjust) + alarm += now; + if (alarm > now || push) { + /* Avoid accidentally clobbering active alarms; we can't + * entirely prevent that here, without even the minimal + * locking from the /dev/rtcN api. + */ + retval = rtc_read_alarm(rtc, &alm); + if (retval < 0) + return retval; + if (alm.enabled) { + if (push) { + push = rtc_tm_to_time64(&alm.time); + alarm += push; + } else + return -EBUSY; + } else if (push) + return -EINVAL; + alm.enabled = 1; + } else { + alm.enabled = 0; + + /* Provide a valid future alarm time. Linux isn't EFI, + * this time won't be ignored when disabling the alarm. + */ + alarm = now + 300; + } + rtc_time64_to_tm(alarm, &alm.time); + + retval = rtc_set_alarm(rtc, &alm); + return (retval < 0) ? retval : n; +} +static DEVICE_ATTR_RW(wakealarm); + +static ssize_t +offset_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t retval; + long offset; + + retval = rtc_read_offset(to_rtc_device(dev), &offset); + if (retval) + return retval; + + return sysfs_emit(buf, "%ld\n", offset); +} + +static ssize_t +offset_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t n) +{ + ssize_t retval; + long offset; + + retval = kstrtol(buf, 10, &offset); + if (retval == 0) + retval = rtc_set_offset(to_rtc_device(dev), offset); + + return (retval < 0) ? retval : n; +} +static DEVICE_ATTR_RW(offset); + +static ssize_t +range_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "[%lld,%llu]\n", to_rtc_device(dev)->range_min, + to_rtc_device(dev)->range_max); +} +static DEVICE_ATTR_RO(range); + +static struct attribute *rtc_attrs[] = { + &dev_attr_name.attr, + &dev_attr_date.attr, + &dev_attr_time.attr, + &dev_attr_since_epoch.attr, + &dev_attr_max_user_freq.attr, + &dev_attr_hctosys.attr, + &dev_attr_wakealarm.attr, + &dev_attr_offset.attr, + &dev_attr_range.attr, + NULL, +}; + +/* The reason to trigger an alarm with no process watching it (via sysfs) + * is its side effect: waking from a system state like suspend-to-RAM or + * suspend-to-disk. So: no attribute unless that side effect is possible. + * (Userspace may disable that mechanism later.) + */ +static bool rtc_does_wakealarm(struct rtc_device *rtc) +{ + if (!device_can_wakeup(rtc->dev.parent)) + return false; + + return !!test_bit(RTC_FEATURE_ALARM, rtc->features); +} + +static umode_t rtc_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = kobj_to_dev(kobj); + struct rtc_device *rtc = to_rtc_device(dev); + umode_t mode = attr->mode; + + if (attr == &dev_attr_wakealarm.attr) { + if (!rtc_does_wakealarm(rtc)) + mode = 0; + } else if (attr == &dev_attr_offset.attr) { + if (!rtc->ops->set_offset) + mode = 0; + } else if (attr == &dev_attr_range.attr) { + if (!(rtc->range_max - rtc->range_min)) + mode = 0; + } + + return mode; +} + +static struct attribute_group rtc_attr_group = { + .is_visible = rtc_attr_is_visible, + .attrs = rtc_attrs, +}; +__ATTRIBUTE_GROUPS(rtc_attr); + +const struct attribute_group **rtc_get_dev_attribute_groups(void) +{ + return rtc_attr_groups; +} + +int rtc_add_groups(struct rtc_device *rtc, const struct attribute_group **grps) +{ + size_t old_cnt = 0, add_cnt = 0, new_cnt; + const struct attribute_group **groups, **old; + + if (grps) { + for (groups = grps; *groups; groups++) + add_cnt++; + /* No need to modify current groups if nothing new is provided */ + if (add_cnt == 0) + return 0; + } else { + return -EINVAL; + } + + groups = rtc->dev.groups; + if (groups) + for (; *groups; groups++) + old_cnt++; + + new_cnt = old_cnt + add_cnt + 1; + groups = devm_kcalloc(&rtc->dev, new_cnt, sizeof(*groups), GFP_KERNEL); + if (!groups) + return -ENOMEM; + memcpy(groups, rtc->dev.groups, old_cnt * sizeof(*groups)); + memcpy(groups + old_cnt, grps, add_cnt * sizeof(*groups)); + groups[old_cnt + add_cnt] = NULL; + + old = rtc->dev.groups; + rtc->dev.groups = groups; + if (old && old != rtc_attr_groups) + devm_kfree(&rtc->dev, old); + + return 0; +} +EXPORT_SYMBOL(rtc_add_groups); + +int rtc_add_group(struct rtc_device *rtc, const struct attribute_group *grp) +{ + const struct attribute_group *groups[] = { grp, NULL }; + + return rtc_add_groups(rtc, groups); +} +EXPORT_SYMBOL(rtc_add_group); diff --git a/drivers/rtc/test_rtc_lib.c b/drivers/rtc/test_rtc_lib.c new file mode 100644 index 000000000..0eebad1fe --- /dev/null +++ b/drivers/rtc/test_rtc_lib.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: LGPL-2.1+ + +#include +#include + +/* + * Advance a date by one day. + */ +static void advance_date(int *year, int *month, int *mday, int *yday, int *wday) +{ + *wday = (*wday + 1) % 7; + + if (*mday != rtc_month_days(*month - 1, *year)) { + ++*mday; + ++*yday; + return; + } + + *mday = 1; + if (*month != 12) { + ++*month; + ++*yday; + return; + } + + *month = 1; + *yday = 1; + ++*year; +} + +/* + * Check every day in specified number of years interval starting on 1970-01-01 + * against the expected result. + */ +static void rtc_time64_to_tm_test_date_range(struct kunit *test, int years) +{ + /* + * years = (years / 400) * 400 years + * = (years / 400) * 146097 days + * = (years / 400) * 146097 * 86400 seconds + */ + time64_t total_secs = ((time64_t)years) / 400 * 146097 * 86400; + + int year = 1900; + int month = 1; + int mday = 1; + int yday = 1; + int wday = 1; /* Jan 1st 1900 was a Monday */ + + struct rtc_time result; + time64_t secs; + const time64_t sec_offset = RTC_TIMESTAMP_BEGIN_1900 + ((1 * 60) + 2) * 60 + 3; + + for (secs = 0; secs <= total_secs; secs += 86400) { + + rtc_time64_to_tm(secs + sec_offset, &result); + + #define FAIL_MSG "%d/%02d/%02d (%2d, %d) : %lld", \ + year, month, mday, yday, wday, secs + sec_offset + + KUNIT_ASSERT_EQ_MSG(test, year - 1900, result.tm_year, FAIL_MSG); + KUNIT_ASSERT_EQ_MSG(test, month - 1, result.tm_mon, FAIL_MSG); + KUNIT_ASSERT_EQ_MSG(test, mday, result.tm_mday, FAIL_MSG); + KUNIT_ASSERT_EQ_MSG(test, yday, result.tm_yday, FAIL_MSG); + KUNIT_ASSERT_EQ_MSG(test, 1, result.tm_hour, FAIL_MSG); + KUNIT_ASSERT_EQ_MSG(test, 2, result.tm_min, FAIL_MSG); + KUNIT_ASSERT_EQ_MSG(test, 3, result.tm_sec, FAIL_MSG); + KUNIT_ASSERT_EQ_MSG(test, wday, result.tm_wday, FAIL_MSG); + + advance_date(&year, &month, &mday, &yday, &wday); + } +} + +/* + * Checks every day in a 160000 years interval starting on 1900-01-01 + * against the expected result. + */ +static void rtc_time64_to_tm_test_date_range_160000(struct kunit *test) +{ + rtc_time64_to_tm_test_date_range(test, 160000); +} + +/* + * Checks every day in a 1000 years interval starting on 1900-01-01 + * against the expected result. + */ +static void rtc_time64_to_tm_test_date_range_1000(struct kunit *test) +{ + rtc_time64_to_tm_test_date_range(test, 1000); +} + +static struct kunit_case rtc_lib_test_cases[] = { + KUNIT_CASE(rtc_time64_to_tm_test_date_range_1000), + KUNIT_CASE_SLOW(rtc_time64_to_tm_test_date_range_160000), + {} +}; + +static struct kunit_suite rtc_lib_test_suite = { + .name = "rtc_lib_test_cases", + .test_cases = rtc_lib_test_cases, +}; + +kunit_test_suite(rtc_lib_test_suite); + +MODULE_DESCRIPTION("KUnit test for RTC lib functions"); +MODULE_LICENSE("GPL"); -- cgit v1.3.1