1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * TPS6594 PFSM userspace example |
4 | * |
5 | * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/ |
6 | * |
7 | * This example shows how to use PFSMs from a userspace application, |
8 | * on TI j721s2 platform. The PMIC is armed to be triggered by a RTC |
9 | * alarm to execute state transition (RETENTION to ACTIVE). |
10 | */ |
11 | |
12 | #include <fcntl.h> |
13 | #include <stdio.h> |
14 | #include <sys/ioctl.h> |
15 | #include <unistd.h> |
16 | |
17 | #include <linux/rtc.h> |
18 | #include <linux/tps6594_pfsm.h> |
19 | |
20 | #define ALARM_DELTA_SEC 30 |
21 | |
22 | #define RTC_A "/dev/rtc0" |
23 | |
24 | #define PMIC_NB 3 |
25 | #define PMIC_A "/dev/pfsm-0-0x48" |
26 | #define PMIC_B "/dev/pfsm-0-0x4c" |
27 | #define PMIC_C "/dev/pfsm-2-0x58" |
28 | |
29 | static const char * const dev_pfsm[] = {PMIC_A, PMIC_B, PMIC_C}; |
30 | |
31 | int main(int argc, char *argv[]) |
32 | { |
33 | int i, ret, fd_rtc, fd_pfsm[PMIC_NB] = { 0 }; |
34 | struct rtc_time rtc_tm; |
35 | struct pmic_state_opt pmic_opt = { 0 }; |
36 | unsigned long data; |
37 | |
38 | fd_rtc = open(RTC_A, O_RDONLY); |
39 | if (fd_rtc < 0) { |
40 | perror(s: "Failed to open RTC device." ); |
41 | goto out; |
42 | } |
43 | |
44 | for (i = 0 ; i < PMIC_NB ; i++) { |
45 | fd_pfsm[i] = open(file: dev_pfsm[i], O_RDWR); |
46 | if (fd_pfsm[i] < 0) { |
47 | perror(s: "Failed to open PFSM device." ); |
48 | goto out; |
49 | } |
50 | } |
51 | |
52 | /* Read RTC date/time */ |
53 | ret = ioctl(fd: fd_rtc, RTC_RD_TIME, &rtc_tm); |
54 | if (ret < 0) { |
55 | perror(s: "Failed to read RTC date/time." ); |
56 | goto out; |
57 | } |
58 | printf(format: "Current RTC date/time is %d-%d-%d, %02d:%02d:%02d.\n" , |
59 | rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900, |
60 | rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec); |
61 | |
62 | /* Set RTC alarm to ALARM_DELTA_SEC sec in the future, and check for rollover */ |
63 | rtc_tm.tm_sec += ALARM_DELTA_SEC; |
64 | if (rtc_tm.tm_sec >= 60) { |
65 | rtc_tm.tm_sec %= 60; |
66 | rtc_tm.tm_min++; |
67 | } |
68 | if (rtc_tm.tm_min == 60) { |
69 | rtc_tm.tm_min = 0; |
70 | rtc_tm.tm_hour++; |
71 | } |
72 | if (rtc_tm.tm_hour == 24) |
73 | rtc_tm.tm_hour = 0; |
74 | ret = ioctl(fd: fd_rtc, RTC_ALM_SET, &rtc_tm); |
75 | if (ret < 0) { |
76 | perror(s: "Failed to set RTC alarm." ); |
77 | goto out; |
78 | } |
79 | |
80 | /* Enable alarm interrupts */ |
81 | ret = ioctl(fd: fd_rtc, RTC_AIE_ON, 0); |
82 | if (ret < 0) { |
83 | perror(s: "Failed to enable alarm interrupts." ); |
84 | goto out; |
85 | } |
86 | printf(format: "Waiting %d seconds for alarm...\n" , ALARM_DELTA_SEC); |
87 | |
88 | /* |
89 | * Set RETENTION state with options for PMIC_C/B/A respectively. |
90 | * Since PMIC_A is master, it should be the last one to be configured. |
91 | */ |
92 | pmic_opt.ddr_retention = 1; |
93 | for (i = PMIC_NB - 1 ; i >= 0 ; i--) { |
94 | printf(format: "Set RETENTION state for PMIC_%d.\n" , i); |
95 | sleep(seconds: 1); |
96 | ret = ioctl(fd_pfsm[i], PMIC_SET_RETENTION_STATE, &pmic_opt); |
97 | if (ret < 0) { |
98 | perror(s: "Failed to set RETENTION state." ); |
99 | goto out_reset; |
100 | } |
101 | } |
102 | |
103 | /* This blocks until the alarm ring causes an interrupt */ |
104 | ret = read(fd: fd_rtc, buf: &data, nbytes: sizeof(unsigned long)); |
105 | if (ret < 0) |
106 | perror(s: "Failed to get RTC alarm." ); |
107 | else |
108 | puts(s: "Alarm rang.\n" ); |
109 | |
110 | out_reset: |
111 | ioctl(fd: fd_rtc, RTC_AIE_OFF, 0); |
112 | |
113 | /* Set ACTIVE state for PMIC_A */ |
114 | ioctl(fd_pfsm[0], PMIC_SET_ACTIVE_STATE, 0); |
115 | |
116 | out: |
117 | for (i = 0 ; i < PMIC_NB ; i++) |
118 | if (fd_pfsm[i]) |
119 | close(fd: fd_pfsm[i]); |
120 | |
121 | if (fd_rtc) |
122 | close(fd: fd_rtc); |
123 | |
124 | return 0; |
125 | } |
126 | |