Commit | Line | Data |
---|---|---|
9e66fb52 JP |
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("Failed to open RTC device."); | |
41 | goto out; | |
42 | } | |
43 | ||
44 | for (i = 0 ; i < PMIC_NB ; i++) { | |
45 | fd_pfsm[i] = open(dev_pfsm[i], O_RDWR); | |
46 | if (fd_pfsm[i] < 0) { | |
47 | perror("Failed to open PFSM device."); | |
48 | goto out; | |
49 | } | |
50 | } | |
51 | ||
52 | /* Read RTC date/time */ | |
53 | ret = ioctl(fd_rtc, RTC_RD_TIME, &rtc_tm); | |
54 | if (ret < 0) { | |
55 | perror("Failed to read RTC date/time."); | |
56 | goto out; | |
57 | } | |
58 | printf("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_rtc, RTC_ALM_SET, &rtc_tm); | |
75 | if (ret < 0) { | |
76 | perror("Failed to set RTC alarm."); | |
77 | goto out; | |
78 | } | |
79 | ||
80 | /* Enable alarm interrupts */ | |
81 | ret = ioctl(fd_rtc, RTC_AIE_ON, 0); | |
82 | if (ret < 0) { | |
83 | perror("Failed to enable alarm interrupts."); | |
84 | goto out; | |
85 | } | |
86 | printf("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("Set RETENTION state for PMIC_%d.\n", i); | |
95 | sleep(1); | |
96 | ret = ioctl(fd_pfsm[i], PMIC_SET_RETENTION_STATE, &pmic_opt); | |
97 | if (ret < 0) { | |
98 | perror("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_rtc, &data, sizeof(unsigned long)); | |
105 | if (ret < 0) | |
106 | perror("Failed to get RTC alarm."); | |
107 | else | |
108 | puts("Alarm rang.\n"); | |
109 | ||
110 | out_reset: | |
111 | ioctl(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_pfsm[i]); | |
120 | ||
121 | if (fd_rtc) | |
122 | close(fd_rtc); | |
123 | ||
124 | return 0; | |
125 | } |