1*4882a593Smuzhiyun // SPDX-License-Identifier: (GPL-2.0+ OR MIT)
2*4882a593Smuzhiyun // Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
3*4882a593Smuzhiyun
4*4882a593Smuzhiyun #include <stdio.h>
5*4882a593Smuzhiyun #include <errno.h>
6*4882a593Smuzhiyun #include <string.h>
7*4882a593Smuzhiyun #include <sys/types.h>
8*4882a593Smuzhiyun #include <sys/stat.h>
9*4882a593Smuzhiyun #include <fcntl.h>
10*4882a593Smuzhiyun #include <unistd.h>
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun #define LOG_TAG "NPU_POWER"
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun #include <npu_powerctrl.h>
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun #define VERSION "V1.1"
17*4882a593Smuzhiyun #define FNAME_SIZE 50
18*4882a593Smuzhiyun #define GPIO_BASE_PATH "/sys/class/gpio"
19*4882a593Smuzhiyun #define GPIO_EXPORT_PATH GPIO_BASE_PATH "/export"
20*4882a593Smuzhiyun #define GPIO_UNEXPORT_PATH GPIO_BASE_PATH "/unexport"
21*4882a593Smuzhiyun #define CLKEN_24M_PATH "/sys/kernel/debug/clk/clk_wifi_pmu/clk_enable_count"
22*4882a593Smuzhiyun #define CLKEN_32k_PATH "/sys/kernel/debug/clk/rk808-clkout2/clk_enable_count"
23*4882a593Smuzhiyun #define PCIE_RESET_EP "sys/devices/platform/f8000000.pcie/pcie_reset_ep"
24*4882a593Smuzhiyun #define ACM_HIGHSPEED_ID "/sys/bus/platform/devices/fe380000.usb/usb*/*/idProduct"
25*4882a593Smuzhiyun #define ACM_FULLSPEED_ID "/sys/bus/platform/devices/fe3a0000.usb/usb*/*/idProduct"
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun #define NPU_VDD_0V8_GPIO "4" //GPIO0_PA4
29*4882a593Smuzhiyun #define NPU_VDD_LOG_GPIO "10" //GPIO0_PB2
30*4882a593Smuzhiyun #define NPU_VCC_1V8_GPIO "11" //GPIO0_PB3
31*4882a593Smuzhiyun #define NPU_VCC_DDR_GPIO NPU_VCC_1V8_GPIO
32*4882a593Smuzhiyun #define NPU_VDD_CPU_GPIO "54" //GPIO1_PC6
33*4882a593Smuzhiyun #define NPU_VCCIO_3V3_GPIO "55" //GPIO1_PC7
34*4882a593Smuzhiyun #define NPU_VDD_GPIO "56" //GPIO1_PD0
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun #define CPU_RESET_NPU_GPIO "32" //GPIO1_PA0
37*4882a593Smuzhiyun #define NPU_PMU_SLEEP_GPIO "35" //GPIO1_A3
38*4882a593Smuzhiyun #define CPU_INT_NPU_GPIO "36" //GPIO1_A4
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun static char gpio_list[][4] = {NPU_VDD_0V8_GPIO, NPU_VDD_LOG_GPIO, NPU_VCC_1V8_GPIO, \
41*4882a593Smuzhiyun NPU_VDD_CPU_GPIO, NPU_VCCIO_3V3_GPIO, NPU_VDD_GPIO, CPU_RESET_NPU_GPIO, \
42*4882a593Smuzhiyun NPU_PMU_SLEEP_GPIO, CPU_INT_NPU_GPIO};
43*4882a593Smuzhiyun
sysfs_write(char * path,char * val)44*4882a593Smuzhiyun static int sysfs_write(char *path, char *val) {
45*4882a593Smuzhiyun char buf[80];
46*4882a593Smuzhiyun int len;
47*4882a593Smuzhiyun int fd = open(path, O_WRONLY);
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun if (fd < 0) {
50*4882a593Smuzhiyun strerror_r(errno, buf, sizeof(buf));
51*4882a593Smuzhiyun printf("Error opening %s value=%s:%s\n", path, val, buf);
52*4882a593Smuzhiyun return -1;
53*4882a593Smuzhiyun }
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun len = write(fd, val, sizeof(val));
56*4882a593Smuzhiyun if (len < 0) {
57*4882a593Smuzhiyun strerror_r(errno, buf, sizeof(buf));
58*4882a593Smuzhiyun printf("Error writing to %s value=%s: %s\n", path, val, buf);
59*4882a593Smuzhiyun return -1;
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun close(fd);
63*4882a593Smuzhiyun return 0;
64*4882a593Smuzhiyun }
65*4882a593Smuzhiyun
sysfs_read(char * path,char * val)66*4882a593Smuzhiyun static void sysfs_read(char *path, char *val)
67*4882a593Smuzhiyun {
68*4882a593Smuzhiyun char buf[80];
69*4882a593Smuzhiyun int len;
70*4882a593Smuzhiyun int fd = open(path, O_RDONLY);
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun if (fd < 0) {
73*4882a593Smuzhiyun strerror_r(errno, buf, sizeof(buf));
74*4882a593Smuzhiyun printf("Error opening %s value=%s:%s\n", path, val, buf);
75*4882a593Smuzhiyun return;
76*4882a593Smuzhiyun }
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun len = read(fd, val, 1);
79*4882a593Smuzhiyun if (len < 0) {
80*4882a593Smuzhiyun strerror_r(errno, buf, sizeof(buf));
81*4882a593Smuzhiyun printf("Error reading to %s value=%s: %s\n", path, val, buf);
82*4882a593Smuzhiyun }
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun close(fd);
85*4882a593Smuzhiyun }
86*4882a593Smuzhiyun
clk_enable(char * enable)87*4882a593Smuzhiyun static int clk_enable(char *enable) {
88*4882a593Smuzhiyun char val;
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun sysfs_read(CLKEN_24M_PATH, &val);
91*4882a593Smuzhiyun if (*enable == val)
92*4882a593Smuzhiyun return 0;
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun printf("set clk_en %c to %s\n", val, enable);
95*4882a593Smuzhiyun sysfs_write(CLKEN_24M_PATH, enable);
96*4882a593Smuzhiyun return 0;
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun
request_gpio(char * gpio_num)99*4882a593Smuzhiyun static int request_gpio(char *gpio_num) {
100*4882a593Smuzhiyun int ret;
101*4882a593Smuzhiyun ret = sysfs_write(GPIO_EXPORT_PATH, gpio_num);
102*4882a593Smuzhiyun return ret;
103*4882a593Smuzhiyun }
104*4882a593Smuzhiyun
free_gpio(char * gpio_num)105*4882a593Smuzhiyun static void free_gpio(char *gpio_num) {
106*4882a593Smuzhiyun sysfs_write(GPIO_UNEXPORT_PATH, gpio_num);
107*4882a593Smuzhiyun }
108*4882a593Smuzhiyun
set_gpio_dir(char * gpio_num,char * dir)109*4882a593Smuzhiyun static void set_gpio_dir(char *gpio_num, char *dir) {
110*4882a593Smuzhiyun char gpio_dir_name[FNAME_SIZE];
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun snprintf(gpio_dir_name, sizeof(gpio_dir_name), "%s/gpio%s/direction",
113*4882a593Smuzhiyun GPIO_BASE_PATH, gpio_num);
114*4882a593Smuzhiyun sysfs_write(gpio_dir_name, dir);
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun
get_gpio(char * gpio_number)117*4882a593Smuzhiyun static int get_gpio(char *gpio_number) {
118*4882a593Smuzhiyun char gpio_name[FNAME_SIZE];
119*4882a593Smuzhiyun char value;
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun snprintf(gpio_name, sizeof(gpio_name), "%s/gpio%s/value",
122*4882a593Smuzhiyun GPIO_BASE_PATH, gpio_number);
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun sysfs_read(gpio_name, &value);
125*4882a593Smuzhiyun if (value == 48 || value == 49)
126*4882a593Smuzhiyun value -= 48;
127*4882a593Smuzhiyun else
128*4882a593Smuzhiyun value = -1;
129*4882a593Smuzhiyun return (int)value;
130*4882a593Smuzhiyun }
131*4882a593Smuzhiyun
set_gpio(char * gpio_number,char * val)132*4882a593Smuzhiyun static int set_gpio(char *gpio_number, char *val) {
133*4882a593Smuzhiyun char gpio_val_name[FNAME_SIZE];
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun snprintf(gpio_val_name, sizeof(gpio_val_name), "%s/gpio%s/value",
136*4882a593Smuzhiyun GPIO_BASE_PATH, gpio_number);
137*4882a593Smuzhiyun sysfs_write(gpio_val_name, val);
138*4882a593Smuzhiyun return 0;
139*4882a593Smuzhiyun }
140*4882a593Smuzhiyun
npu_power_gpio_init(void)141*4882a593Smuzhiyun void npu_power_gpio_init(void) {
142*4882a593Smuzhiyun int ret, index = 0, gpio_cnt = sizeof(gpio_list)/sizeof(int);
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun printf("version: %s\n", VERSION);
145*4882a593Smuzhiyun while (index != gpio_cnt) {
146*4882a593Smuzhiyun printf("init gpio: %s\n", gpio_list[index]);
147*4882a593Smuzhiyun ret = request_gpio(gpio_list[index]);
148*4882a593Smuzhiyun if (ret) {
149*4882a593Smuzhiyun return;
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun set_gpio_dir(gpio_list[index], "out");
152*4882a593Smuzhiyun index ++;
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun set_gpio_dir(NPU_PMU_SLEEP_GPIO, "in");
155*4882a593Smuzhiyun }
156*4882a593Smuzhiyun
npu_power_gpio_exit(void)157*4882a593Smuzhiyun void npu_power_gpio_exit(void) {
158*4882a593Smuzhiyun int index = 0, gpio_cnt = sizeof(gpio_list)/sizeof(int);
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun while (index != gpio_cnt) {
161*4882a593Smuzhiyun printf("init gpio: %s\n", gpio_list[index]);
162*4882a593Smuzhiyun free_gpio(gpio_list[index]);
163*4882a593Smuzhiyun index ++;
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun }
166*4882a593Smuzhiyun
npu_reset(void)167*4882a593Smuzhiyun void npu_reset(void) {
168*4882a593Smuzhiyun // sysfs_write("/sys/power/wake_lock", "npu_lock");
169*4882a593Smuzhiyun sysfs_write(CLKEN_32k_PATH, "1");
170*4882a593Smuzhiyun clk_enable("0");
171*4882a593Smuzhiyun set_gpio(NPU_VDD_GPIO, "0");
172*4882a593Smuzhiyun set_gpio(NPU_VCCIO_3V3_GPIO, "0");
173*4882a593Smuzhiyun set_gpio(NPU_VDD_CPU_GPIO, "0");
174*4882a593Smuzhiyun set_gpio(NPU_VCC_1V8_GPIO, "0");
175*4882a593Smuzhiyun set_gpio(NPU_VDD_0V8_GPIO, "0");
176*4882a593Smuzhiyun set_gpio(NPU_VDD_LOG_GPIO, "0");
177*4882a593Smuzhiyun set_gpio(CPU_INT_NPU_GPIO, "0");
178*4882a593Smuzhiyun set_gpio(CPU_RESET_NPU_GPIO, "0");
179*4882a593Smuzhiyun usleep(2000);
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun /*power en*/
182*4882a593Smuzhiyun set_gpio(NPU_VDD_0V8_GPIO, "1");
183*4882a593Smuzhiyun usleep(2000);
184*4882a593Smuzhiyun set_gpio(NPU_VDD_LOG_GPIO, "1");
185*4882a593Smuzhiyun usleep(2000);
186*4882a593Smuzhiyun set_gpio(NPU_VCC_1V8_GPIO, "1");
187*4882a593Smuzhiyun usleep(2000);
188*4882a593Smuzhiyun clk_enable("1");
189*4882a593Smuzhiyun set_gpio(NPU_VDD_CPU_GPIO, "1");
190*4882a593Smuzhiyun usleep(2000);
191*4882a593Smuzhiyun set_gpio(NPU_VCCIO_3V3_GPIO, "1");
192*4882a593Smuzhiyun usleep(2000);
193*4882a593Smuzhiyun set_gpio(NPU_VDD_GPIO, "1");
194*4882a593Smuzhiyun
195*4882a593Smuzhiyun usleep(25000);
196*4882a593Smuzhiyun set_gpio(CPU_RESET_NPU_GPIO, "1");
197*4882a593Smuzhiyun }
198*4882a593Smuzhiyun
npu_poweroff(void)199*4882a593Smuzhiyun void npu_poweroff(void) {
200*4882a593Smuzhiyun set_gpio(NPU_VDD_GPIO, "0");
201*4882a593Smuzhiyun set_gpio(NPU_VCCIO_3V3_GPIO, "0");
202*4882a593Smuzhiyun set_gpio(NPU_VDD_CPU_GPIO, "0");
203*4882a593Smuzhiyun set_gpio(NPU_VCC_1V8_GPIO, "0");
204*4882a593Smuzhiyun set_gpio(NPU_VDD_0V8_GPIO, "0");
205*4882a593Smuzhiyun set_gpio(NPU_VDD_LOG_GPIO, "0");
206*4882a593Smuzhiyun set_gpio(CPU_INT_NPU_GPIO, "0");
207*4882a593Smuzhiyun set_gpio(CPU_RESET_NPU_GPIO, "0");
208*4882a593Smuzhiyun clk_enable("0");
209*4882a593Smuzhiyun // sysfs_write("/sys/power/wake_unlock", "npu_lock");
210*4882a593Smuzhiyun }
211*4882a593Smuzhiyun
disconnect_usb_acm(void)212*4882a593Smuzhiyun int disconnect_usb_acm(void)
213*4882a593Smuzhiyun {
214*4882a593Smuzhiyun FILE *stream;
215*4882a593Smuzhiyun char buf[100];
216*4882a593Smuzhiyun int idx = 0, path_idx=0;
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun memset( buf,'\0',sizeof(buf) );
219*4882a593Smuzhiyun stream = popen( "cat " ACM_HIGHSPEED_ID, "r" );
220*4882a593Smuzhiyun if (!stream) {
221*4882a593Smuzhiyun path_idx=1;
222*4882a593Smuzhiyun stream = popen( "cat " ACM_FULLSPEED_ID, "r" );
223*4882a593Smuzhiyun if (!stream)
224*4882a593Smuzhiyun return -1;
225*4882a593Smuzhiyun }
226*4882a593Smuzhiyun fread( buf, sizeof(char), sizeof(buf), stream);
227*4882a593Smuzhiyun pclose( stream );
228*4882a593Smuzhiyun printf("ACM idProduct: %s", buf);
229*4882a593Smuzhiyun if (!strncmp(buf, "1005", 4)) {
230*4882a593Smuzhiyun if (path_idx)
231*4882a593Smuzhiyun stream = popen("find /sys/bus/platform/devices/fe3a0000.usb/usb*/*/ -name remove", "r" );
232*4882a593Smuzhiyun else
233*4882a593Smuzhiyun stream = popen("find /sys/bus/platform/devices/fe380000.usb/usb*/*/ -name remove", "r" );
234*4882a593Smuzhiyun fread( buf, sizeof(char), sizeof(buf), stream);
235*4882a593Smuzhiyun printf("usb remove patch is %s", buf);
236*4882a593Smuzhiyun /* The path string adds end character */
237*4882a593Smuzhiyun while(idx < 100) {
238*4882a593Smuzhiyun if (buf[idx] == '\n') {
239*4882a593Smuzhiyun buf[idx] = '\0';
240*4882a593Smuzhiyun break;
241*4882a593Smuzhiyun }
242*4882a593Smuzhiyun idx ++;
243*4882a593Smuzhiyun }
244*4882a593Smuzhiyun sysfs_write(buf, "0");
245*4882a593Smuzhiyun pclose( stream );
246*4882a593Smuzhiyun return 0;
247*4882a593Smuzhiyun }
248*4882a593Smuzhiyun return -1;
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun }
251*4882a593Smuzhiyun
npu_suspend(void)252*4882a593Smuzhiyun int npu_suspend(void) {
253*4882a593Smuzhiyun int retry=100;
254*4882a593Smuzhiyun int is_pcie;
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun if (get_gpio(NPU_PMU_SLEEP_GPIO)) {
257*4882a593Smuzhiyun printf("It is sleeping state, noting to do!\n");
258*4882a593Smuzhiyun return 0;
259*4882a593Smuzhiyun }
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun is_pcie = access(PCIE_RESET_EP, R_OK);
262*4882a593Smuzhiyun if (!is_pcie) {
263*4882a593Smuzhiyun sysfs_write(PCIE_RESET_EP, "2");
264*4882a593Smuzhiyun disconnect_usb_acm();
265*4882a593Smuzhiyun }
266*4882a593Smuzhiyun
267*4882a593Smuzhiyun set_gpio(CPU_INT_NPU_GPIO, "1");
268*4882a593Smuzhiyun usleep(20000);
269*4882a593Smuzhiyun set_gpio(CPU_INT_NPU_GPIO, "0");
270*4882a593Smuzhiyun
271*4882a593Smuzhiyun /*wait for npu enter sleep*/
272*4882a593Smuzhiyun while (--retry) {
273*4882a593Smuzhiyun if (get_gpio(NPU_PMU_SLEEP_GPIO)) {
274*4882a593Smuzhiyun usleep(10000);
275*4882a593Smuzhiyun set_gpio(NPU_VDD_CPU_GPIO, "0");
276*4882a593Smuzhiyun set_gpio(NPU_VDD_GPIO, "0");
277*4882a593Smuzhiyun clk_enable("0");
278*4882a593Smuzhiyun /* wait 1s for usb disconnect */
279*4882a593Smuzhiyun sleep(1);
280*4882a593Smuzhiyun // sysfs_write("/sys/power/wake_unlock", "npu_lock");
281*4882a593Smuzhiyun break;
282*4882a593Smuzhiyun }
283*4882a593Smuzhiyun usleep(10000);
284*4882a593Smuzhiyun }
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun if (!retry) {
287*4882a593Smuzhiyun printf("npu suspend timeout in one second\n");
288*4882a593Smuzhiyun return -1;
289*4882a593Smuzhiyun }
290*4882a593Smuzhiyun return 0;
291*4882a593Smuzhiyun }
292*4882a593Smuzhiyun
npu_resume(void)293*4882a593Smuzhiyun int npu_resume(void) {
294*4882a593Smuzhiyun int retry=100;
295*4882a593Smuzhiyun int is_pcie;
296*4882a593Smuzhiyun
297*4882a593Smuzhiyun if (!get_gpio(NPU_PMU_SLEEP_GPIO)) {
298*4882a593Smuzhiyun printf("It is awakening state, noting to do!\n");
299*4882a593Smuzhiyun return 0;
300*4882a593Smuzhiyun }
301*4882a593Smuzhiyun
302*4882a593Smuzhiyun clk_enable("1");
303*4882a593Smuzhiyun set_gpio(NPU_VDD_CPU_GPIO, "1");
304*4882a593Smuzhiyun set_gpio(NPU_VDD_GPIO, "1");
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun usleep(10000);
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun set_gpio(CPU_INT_NPU_GPIO, "1");
309*4882a593Smuzhiyun usleep(20000);
310*4882a593Smuzhiyun set_gpio(CPU_INT_NPU_GPIO, "0");
311*4882a593Smuzhiyun
312*4882a593Smuzhiyun /*wait for npu wakeup*/
313*4882a593Smuzhiyun while (--retry) {
314*4882a593Smuzhiyun if (!get_gpio(NPU_PMU_SLEEP_GPIO)) {
315*4882a593Smuzhiyun // sysfs_write("/sys/power/wake_lock", "npu_lock");
316*4882a593Smuzhiyun break;
317*4882a593Smuzhiyun }
318*4882a593Smuzhiyun usleep(10000);
319*4882a593Smuzhiyun }
320*4882a593Smuzhiyun
321*4882a593Smuzhiyun is_pcie = access(PCIE_RESET_EP, R_OK);
322*4882a593Smuzhiyun if (!is_pcie)
323*4882a593Smuzhiyun sysfs_write(PCIE_RESET_EP, "1");
324*4882a593Smuzhiyun
325*4882a593Smuzhiyun if (!retry) {
326*4882a593Smuzhiyun printf("npu resume timeout in one second\n");
327*4882a593Smuzhiyun return -1;
328*4882a593Smuzhiyun }
329*4882a593Smuzhiyun //waiting for userspase wakup
330*4882a593Smuzhiyun /*usleep(500000);*/
331*4882a593Smuzhiyun /*set_gpio(CPU_INT_NPU_GPIO, "0");*/
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun return 0;
334*4882a593Smuzhiyun }
335