1 /*
2 * Copyright (c) 2024, MediaTek Inc. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <errno.h>
8
9 #include <common/debug.h>
10 #include <drivers/delay_timer.h>
11 #include <lib/mmio.h>
12
13 #include "apusys_power.h"
14 #include "apusys_rv.h"
15 #include "apusys_rv_pwr_ctrl.h"
16
17 #define RPC_POWER_OFF_TIMEOUT_CNT (100000) /* 100ms */
18
wait_for_state_ready(uint32_t reg,uint32_t mask,uint32_t expect,uint32_t retry_times,uint32_t set_reg,uint32_t set_val)19 static int wait_for_state_ready(uint32_t reg, uint32_t mask, uint32_t expect,
20 uint32_t retry_times, uint32_t set_reg, uint32_t set_val)
21 {
22 uint32_t count = 0;
23
24 while ((mmio_read_32(reg) & mask) != expect) {
25 /*
26 * If retry_times == HW_SEM_NO_WAIT, it is just for checking if the hardware
27 * semaphore can be locked or not. The purpose is for SMMU to check NPU power
28 * status. Hence, just returning -EBUSY is okay. There is no need to show any
29 * ERROR message here.
30 */
31 if (retry_times == HW_SEM_NO_WAIT) {
32 return -EBUSY;
33 } else if (count > retry_times) {
34 ERROR("%s: timed out, reg = %x, mask = %x, expect = %x\n",
35 __func__, reg, mask, expect);
36 return -EBUSY;
37 }
38 count += 1;
39
40 if (set_reg)
41 mmio_write_32(set_reg, set_val);
42 udelay(1);
43 }
44
45 return 0;
46 }
47
apu_hw_sema_ctl_per_mbox(uint32_t sem_ctrl_addr,uint32_t sem_sta_addr,uint8_t usr_bit,enum apu_hw_sem_op ctl,uint32_t timeout,uint8_t bypass)48 int apu_hw_sema_ctl_per_mbox(uint32_t sem_ctrl_addr, uint32_t sem_sta_addr,
49 uint8_t usr_bit, enum apu_hw_sem_op ctl, uint32_t timeout,
50 uint8_t bypass)
51 {
52 int ret;
53 uint8_t ctl_bit = 0;
54
55 if (ctl == HW_SEM_GET)
56 ctl_bit = 0x1;
57 else if (ctl == HW_SEM_PUT)
58 ctl_bit = 0x2;
59 else
60 return -EINVAL;
61
62 /* return fail if semaphore is currently not held by this user */
63 if (ctl == HW_SEM_PUT && ((mmio_read_32(sem_sta_addr) & BIT(usr_bit)) == 0)
64 && !bypass) {
65 ERROR("%s release error: usr_bit:%d ctl:%d (sem_addr(0x%08x) = 0x%08x)\n",
66 __func__, usr_bit, ctl, sem_sta_addr, mmio_read_32(sem_sta_addr));
67 return -EINVAL;
68 }
69
70 mmio_write_32(sem_ctrl_addr, ctl_bit);
71
72 if (ctl == HW_SEM_PUT)
73 return 0;
74
75 ret = wait_for_state_ready(sem_sta_addr, BIT(usr_bit), BIT(usr_bit), timeout,
76 sem_ctrl_addr, ctl_bit);
77 if (ret)
78 return ret;
79
80 return 0;
81 }
82
apusys_rv_pwr_ctrl(enum APU_PWR_OP op)83 int apusys_rv_pwr_ctrl(enum APU_PWR_OP op)
84 {
85 int ret;
86 uint32_t global_ref_cnt;
87
88 ret = apu_hw_sema_ctl_per_mbox(APU_MBOX(APU_HW_SEM_SYS_APMCU) + APU_MBOX_SEMA0_CTRL,
89 APU_MBOX(APU_HW_SEM_SYS_APMCU) + APU_MBOX_SEMA0_STA,
90 APU_HW_SEM_SYS_APMCU, HW_SEM_GET, HW_SEM_TIMEOUT, 0);
91
92 if (ret) {
93 ERROR("%s(%d): sem acquire timeout\n", __func__, op);
94 return ret;
95 }
96
97 global_ref_cnt = mmio_read_32(APU_MBOX(APU_HW_SEM_SYS_APMCU) + APU_MBOX_DUMMY);
98
99 if (global_ref_cnt > 2) {
100 ERROR("%s: global_ref_cnt(%d) > 2\n", __func__, global_ref_cnt);
101 } else if (op == APU_PWR_OFF) {
102 global_ref_cnt--;
103 mmio_write_32(APU_MBOX(APU_HW_SEM_SYS_APMCU) + APU_MBOX_DUMMY, global_ref_cnt);
104 if (global_ref_cnt == 0)
105 mmio_write_32(APU_MBOX_WKUP_CFG(11), 0);
106 } else if (op == APU_PWR_ON) {
107 global_ref_cnt++;
108 mmio_write_32(APU_MBOX(APU_HW_SEM_SYS_APMCU) + APU_MBOX_DUMMY, global_ref_cnt);
109 if (global_ref_cnt == 1)
110 mmio_write_32(APU_MBOX_WKUP_CFG(11), 1);
111 }
112
113 ret = apu_hw_sema_ctl_per_mbox(APU_MBOX(APU_HW_SEM_SYS_APMCU) + APU_MBOX_SEMA0_CTRL,
114 APU_MBOX(APU_HW_SEM_SYS_APMCU) + APU_MBOX_SEMA0_STA,
115 APU_HW_SEM_SYS_APMCU, HW_SEM_PUT, HW_SEM_TIMEOUT, 0);
116
117 if (ret)
118 ERROR("%s(%d): sem release timeout\n", __func__, op);
119
120 return ret;
121 }
122
rv_iommu_hw_sem_trylock(void)123 int rv_iommu_hw_sem_trylock(void)
124 {
125 return apu_hw_sema_ctl_per_mbox(APU_MBOX(APU_HW_SEM_SYS_APMCU) + APU_MBOX_SEMA1_CTRL,
126 APU_MBOX(APU_HW_SEM_SYS_APMCU) + APU_MBOX_SEMA1_STA,
127 APU_HW_SEM_SYS_APMCU, HW_SEM_GET, HW_SEM_NO_WAIT, 0);
128 }
129
rv_iommu_hw_sem_unlock(void)130 int rv_iommu_hw_sem_unlock(void)
131 {
132 return apu_hw_sema_ctl_per_mbox(APU_MBOX(APU_HW_SEM_SYS_APMCU) + APU_MBOX_SEMA1_CTRL,
133 APU_MBOX(APU_HW_SEM_SYS_APMCU) + APU_MBOX_SEMA1_STA,
134 APU_HW_SEM_SYS_APMCU, HW_SEM_PUT, HW_SEM_NO_WAIT, 0);
135 }
136
apu_hw_sema_ctl(uint32_t sem_addr,uint8_t usr_bit,uint8_t ctl,uint32_t timeout,uint8_t bypass)137 int apu_hw_sema_ctl(uint32_t sem_addr, uint8_t usr_bit, uint8_t ctl, uint32_t timeout,
138 uint8_t bypass)
139 {
140 int ret;
141 uint8_t ctl_bit = 0;
142
143 if (ctl == HW_SEM_GET)
144 ctl_bit = usr_bit;
145 else if (ctl == HW_SEM_PUT)
146 ctl_bit = usr_bit + HW_SEM_PUT_BIT_SHIFT;
147 else
148 return -EINVAL;
149
150 if (ctl == HW_SEM_PUT && ((mmio_read_32(sem_addr) & BIT(ctl_bit)) == 0) && !bypass) {
151 ERROR("%s release error: usr_bit:%d ctl:%d (sem_addr(0x%08x) = 0x%08x)\n",
152 __func__, usr_bit, ctl, sem_addr, mmio_read_32(sem_addr));
153 return -EINVAL;
154 }
155
156 mmio_write_32(sem_addr, BIT(ctl_bit));
157
158 if (ctl == HW_SEM_PUT)
159 goto end;
160
161 ret = wait_for_state_ready(sem_addr, BIT(ctl_bit), BIT(ctl_bit), timeout,
162 sem_addr, BIT(ctl_bit));
163 if (ret)
164 return ret;
165
166 end:
167 VERBOSE("%s: sem_addr = 0x%x, usr_bit: %d, ctl: %d, sem_addr = 0x%08x\n",
168 __func__, sem_addr, usr_bit, ctl, mmio_read_32(sem_addr));
169
170 return 0;
171 }
172
apusys_infra_dcm_setup(void)173 int apusys_infra_dcm_setup(void)
174 {
175 mmio_write_32(APU_REG_AO_GLUE_CONFG,
176 mmio_read_32(APU_REG_AO_GLUE_CONFG) | BIT(24) | BIT(26));
177
178 return 0;
179 }
180