| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * TPS6594 PFSM userspace example |
| * |
| * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/ |
| * |
| * This example shows how to use PFSMs from a userspace application, |
| * on TI j721s2 platform. The PMIC is armed to be triggered by a RTC |
| * alarm to execute state transition (RETENTION to ACTIVE). |
| */ |
| |
| #include <fcntl.h> |
| #include <stdio.h> |
| #include <sys/ioctl.h> |
| #include <unistd.h> |
| |
| #include <linux/rtc.h> |
| #include <linux/tps6594_pfsm.h> |
| |
| #define ALARM_DELTA_SEC 30 |
| |
| #define RTC_A "/dev/rtc0" |
| |
| #define PMIC_NB 3 |
| #define PMIC_A "/dev/pfsm-0-0x48" |
| #define PMIC_B "/dev/pfsm-0-0x4c" |
| #define PMIC_C "/dev/pfsm-2-0x58" |
| |
| static const char * const dev_pfsm[] = {PMIC_A, PMIC_B, PMIC_C}; |
| |
| int main(int argc, char *argv[]) |
| { |
| int i, ret, fd_rtc, fd_pfsm[PMIC_NB] = { 0 }; |
| struct rtc_time rtc_tm; |
| struct pmic_state_opt pmic_opt = { 0 }; |
| unsigned long data; |
| |
| fd_rtc = open(RTC_A, O_RDONLY); |
| if (fd_rtc < 0) { |
| perror("Failed to open RTC device."); |
| goto out; |
| } |
| |
| for (i = 0 ; i < PMIC_NB ; i++) { |
| fd_pfsm[i] = open(dev_pfsm[i], O_RDWR); |
| if (fd_pfsm[i] < 0) { |
| perror("Failed to open PFSM device."); |
| goto out; |
| } |
| } |
| |
| /* Read RTC date/time */ |
| ret = ioctl(fd_rtc, RTC_RD_TIME, &rtc_tm); |
| if (ret < 0) { |
| perror("Failed to read RTC date/time."); |
| goto out; |
| } |
| printf("Current RTC date/time is %d-%d-%d, %02d:%02d:%02d.\n", |
| rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900, |
| rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec); |
| |
| /* Set RTC alarm to ALARM_DELTA_SEC sec in the future, and check for rollover */ |
| rtc_tm.tm_sec += ALARM_DELTA_SEC; |
| if (rtc_tm.tm_sec >= 60) { |
| rtc_tm.tm_sec %= 60; |
| rtc_tm.tm_min++; |
| } |
| if (rtc_tm.tm_min == 60) { |
| rtc_tm.tm_min = 0; |
| rtc_tm.tm_hour++; |
| } |
| if (rtc_tm.tm_hour == 24) |
| rtc_tm.tm_hour = 0; |
| ret = ioctl(fd_rtc, RTC_ALM_SET, &rtc_tm); |
| if (ret < 0) { |
| perror("Failed to set RTC alarm."); |
| goto out; |
| } |
| |
| /* Enable alarm interrupts */ |
| ret = ioctl(fd_rtc, RTC_AIE_ON, 0); |
| if (ret < 0) { |
| perror("Failed to enable alarm interrupts."); |
| goto out; |
| } |
| printf("Waiting %d seconds for alarm...\n", ALARM_DELTA_SEC); |
| |
| /* |
| * Set RETENTION state with options for PMIC_C/B/A respectively. |
| * Since PMIC_A is master, it should be the last one to be configured. |
| */ |
| pmic_opt.ddr_retention = 1; |
| for (i = PMIC_NB - 1 ; i >= 0 ; i--) { |
| printf("Set RETENTION state for PMIC_%d.\n", i); |
| sleep(1); |
| ret = ioctl(fd_pfsm[i], PMIC_SET_RETENTION_STATE, &pmic_opt); |
| if (ret < 0) { |
| perror("Failed to set RETENTION state."); |
| goto out_reset; |
| } |
| } |
| |
| /* This blocks until the alarm ring causes an interrupt */ |
| ret = read(fd_rtc, &data, sizeof(unsigned long)); |
| if (ret < 0) |
| perror("Failed to get RTC alarm."); |
| else |
| puts("Alarm rang.\n"); |
| |
| out_reset: |
| ioctl(fd_rtc, RTC_AIE_OFF, 0); |
| |
| /* Set ACTIVE state for PMIC_A */ |
| ioctl(fd_pfsm[0], PMIC_SET_ACTIVE_STATE, 0); |
| |
| out: |
| for (i = 0 ; i < PMIC_NB ; i++) |
| if (fd_pfsm[i]) |
| close(fd_pfsm[i]); |
| |
| if (fd_rtc) |
| close(fd_rtc); |
| |
| return 0; |
| } |