1a881aebcSirving-ch-lin /*
2a881aebcSirving-ch-lin * Copyright (c) 2025, MediaTek Inc. All rights reserved.
3a881aebcSirving-ch-lin *
4a881aebcSirving-ch-lin * SPDX-License-Identifier: BSD-3-Clause
5a881aebcSirving-ch-lin */
6a881aebcSirving-ch-lin #include <stdint.h>
7a881aebcSirving-ch-lin
8a881aebcSirving-ch-lin #include <common/debug.h>
9a881aebcSirving-ch-lin #include <drivers/delay_timer.h>
10a881aebcSirving-ch-lin #include <lib/mmio.h>
11a881aebcSirving-ch-lin #include <lib/spinlock.h>
12a881aebcSirving-ch-lin #include <platform_def.h>
13a881aebcSirving-ch-lin
14a881aebcSirving-ch-lin #include <mtcmos.h>
15a881aebcSirving-ch-lin
16a881aebcSirving-ch-lin #define SPM_PROJECT_CODE 0xB16
17a881aebcSirving-ch-lin
18a881aebcSirving-ch-lin #define PWR_RST_B BIT(0)
19a881aebcSirving-ch-lin #define PWR_ISO BIT(1)
20a881aebcSirving-ch-lin #define PWR_ON BIT(2)
21a881aebcSirving-ch-lin #define PWR_ON_2ND BIT(3)
22a881aebcSirving-ch-lin #define PWR_CLK_DIS BIT(4)
23a881aebcSirving-ch-lin #define PWR_ACK BIT(30)
24a881aebcSirving-ch-lin #define PWR_ACK_2ND BIT(31)
25a881aebcSirving-ch-lin
26*41004253Sirving-ch-lin #define SRAM_PDN BIT(8)
27*41004253Sirving-ch-lin #define SRAM_PDN_ACK BIT(12)
28a881aebcSirving-ch-lin
29a881aebcSirving-ch-lin #define MTCMOS_TIMEOUT_US 500
30a881aebcSirving-ch-lin
31a881aebcSirving-ch-lin #define MTCMOS_ETIMEDOUT 25
32a881aebcSirving-ch-lin
33a881aebcSirving-ch-lin static spinlock_t mtcmos_ctrl_lock;
34a881aebcSirving-ch-lin
mtcmos_wait_for_state(uint32_t reg,uint32_t mask,bool is_set)35a881aebcSirving-ch-lin static int mtcmos_wait_for_state(uint32_t reg, uint32_t mask, bool is_set)
36a881aebcSirving-ch-lin {
37a881aebcSirving-ch-lin uint32_t retry = MTCMOS_TIMEOUT_US;
38a881aebcSirving-ch-lin uint32_t expect = is_set ? mask : 0;
39a881aebcSirving-ch-lin
40a881aebcSirving-ch-lin do {
41a881aebcSirving-ch-lin if ((mmio_read_32(reg) & mask) == expect)
42a881aebcSirving-ch-lin return 0;
43a881aebcSirving-ch-lin udelay(1);
44a881aebcSirving-ch-lin retry--;
45a881aebcSirving-ch-lin } while (retry);
46a881aebcSirving-ch-lin
47a881aebcSirving-ch-lin ERROR("%s(0x%x, 0x%x, %d) timeout, reg_val=0x%x\n",
48a881aebcSirving-ch-lin __func__, reg, mask, is_set, mmio_read_32(reg));
49a881aebcSirving-ch-lin
50a881aebcSirving-ch-lin return -MTCMOS_ETIMEDOUT;
51a881aebcSirving-ch-lin }
52a881aebcSirving-ch-lin
53a881aebcSirving-ch-lin
spm_mtcmos_ctrl_bus_prot(const struct bus_protect * bp_table,uint32_t bp_steps)54*41004253Sirving-ch-lin static int spm_mtcmos_ctrl_bus_prot(const struct bus_protect *bp_table, uint32_t bp_steps)
55a881aebcSirving-ch-lin {
56*41004253Sirving-ch-lin int i;
57a881aebcSirving-ch-lin
58*41004253Sirving-ch-lin for (i = 0; i < bp_steps; i++) {
59*41004253Sirving-ch-lin mmio_write_32(bp_table[i].en_addr, bp_table[i].mask);
60*41004253Sirving-ch-lin if (bp_table[i].rdy_addr) {
61*41004253Sirving-ch-lin if (mtcmos_wait_for_state(bp_table[i].rdy_addr, bp_table[i].mask, true))
62a881aebcSirving-ch-lin return -MTCMOS_ETIMEDOUT;
63a881aebcSirving-ch-lin }
64*41004253Sirving-ch-lin }
65a881aebcSirving-ch-lin return 0;
66a881aebcSirving-ch-lin }
67a881aebcSirving-ch-lin
68*41004253Sirving-ch-lin
spm_mtcmos_ctrl(enum mtcmos_state state,uintptr_t reg,bool has_sram,int * rtff_save_flag,const struct bus_protect * bp_table,uint32_t bp_steps)69*41004253Sirving-ch-lin static int spm_mtcmos_ctrl(enum mtcmos_state state, uintptr_t reg,
70*41004253Sirving-ch-lin bool has_sram, int *rtff_save_flag,
71*41004253Sirving-ch-lin const struct bus_protect *bp_table, uint32_t bp_steps)
72a881aebcSirving-ch-lin {
73a881aebcSirving-ch-lin int ret = 0;
74a881aebcSirving-ch-lin
75a881aebcSirving-ch-lin spin_lock(&mtcmos_ctrl_lock);
76a881aebcSirving-ch-lin
77a881aebcSirving-ch-lin mmio_write_32(POWERON_CONFIG_EN, (SPM_PROJECT_CODE << 16) | BIT(0));
78a881aebcSirving-ch-lin
79a881aebcSirving-ch-lin if (state == STA_POWER_DOWN) {
80*41004253Sirving-ch-lin ret = spm_mtcmos_ctrl_bus_prot(bp_table, bp_steps);
81a881aebcSirving-ch-lin if (ret)
82a881aebcSirving-ch-lin goto exit;
83a881aebcSirving-ch-lin
84*41004253Sirving-ch-lin if (has_sram) {
85*41004253Sirving-ch-lin mmio_setbits_32(reg, SRAM_PDN);
86*41004253Sirving-ch-lin ret = mtcmos_wait_for_state(reg, SRAM_PDN_ACK, true);
87a881aebcSirving-ch-lin if (ret)
88a881aebcSirving-ch-lin goto exit;
89a881aebcSirving-ch-lin }
90a881aebcSirving-ch-lin
91a881aebcSirving-ch-lin mmio_setbits_32(reg, RTFF_CLK_DIS);
92a881aebcSirving-ch-lin mmio_setbits_32(reg, RTFF_SAVE);
93a881aebcSirving-ch-lin mmio_clrbits_32(reg, RTFF_SAVE);
94a881aebcSirving-ch-lin mmio_clrbits_32(reg, RTFF_CLK_DIS);
95*41004253Sirving-ch-lin #ifdef RTFF_SAVE_FLAG
96a881aebcSirving-ch-lin mmio_setbits_32(reg, RTFF_SAVE_FLAG);
97*41004253Sirving-ch-lin #endif
98*41004253Sirving-ch-lin *rtff_save_flag = 1;
99*41004253Sirving-ch-lin
100a881aebcSirving-ch-lin
101a881aebcSirving-ch-lin mmio_setbits_32(reg, PWR_ISO);
102a881aebcSirving-ch-lin mmio_setbits_32(reg, PWR_CLK_DIS);
103a881aebcSirving-ch-lin mmio_clrbits_32(reg, PWR_RST_B);
104a881aebcSirving-ch-lin
105a881aebcSirving-ch-lin mmio_clrbits_32(reg, PWR_ON);
106a881aebcSirving-ch-lin ret = mtcmos_wait_for_state(reg, PWR_ACK, false);
107a881aebcSirving-ch-lin if (ret)
108a881aebcSirving-ch-lin goto exit;
109a881aebcSirving-ch-lin
110a881aebcSirving-ch-lin mmio_clrbits_32(reg, PWR_ON_2ND);
111a881aebcSirving-ch-lin ret = mtcmos_wait_for_state(reg, PWR_ACK_2ND, false);
112a881aebcSirving-ch-lin if (ret)
113a881aebcSirving-ch-lin goto exit;
114a881aebcSirving-ch-lin } else if (state == STA_POWER_ON) {
115a881aebcSirving-ch-lin mmio_setbits_32(reg, PWR_ON);
116a881aebcSirving-ch-lin ret = mtcmos_wait_for_state(reg, PWR_ACK, true);
117a881aebcSirving-ch-lin if (ret)
118a881aebcSirving-ch-lin goto exit;
119a881aebcSirving-ch-lin
120a881aebcSirving-ch-lin udelay(50);
121a881aebcSirving-ch-lin
122a881aebcSirving-ch-lin mmio_setbits_32(reg, PWR_ON_2ND);
123a881aebcSirving-ch-lin ret = mtcmos_wait_for_state(reg, PWR_ACK_2ND, true);
124a881aebcSirving-ch-lin if (ret)
125a881aebcSirving-ch-lin goto exit;
126a881aebcSirving-ch-lin
127a881aebcSirving-ch-lin mmio_clrbits_32(reg, PWR_CLK_DIS);
128a881aebcSirving-ch-lin mmio_clrbits_32(reg, PWR_ISO);
129a881aebcSirving-ch-lin udelay(10);
130a881aebcSirving-ch-lin mmio_setbits_32(reg, PWR_RST_B);
131a881aebcSirving-ch-lin
132*41004253Sirving-ch-lin
133*41004253Sirving-ch-lin if (*rtff_save_flag == 1) {
134a881aebcSirving-ch-lin mmio_setbits_32(reg, RTFF_CLK_DIS);
135a881aebcSirving-ch-lin mmio_clrbits_32(reg, RTFF_NRESTORE);
136a881aebcSirving-ch-lin mmio_setbits_32(reg, RTFF_NRESTORE);
137a881aebcSirving-ch-lin mmio_clrbits_32(reg, RTFF_CLK_DIS);
138*41004253Sirving-ch-lin #ifdef RTFF_SAVE_FLAG
139*41004253Sirving-ch-lin mmio_clrbits_32(reg, RTFF_SAVE_FLAG);
140*41004253Sirving-ch-lin #endif
141*41004253Sirving-ch-lin *rtff_save_flag = 0;
142a881aebcSirving-ch-lin }
143a881aebcSirving-ch-lin
144*41004253Sirving-ch-lin if (has_sram) {
145*41004253Sirving-ch-lin mmio_clrbits_32(reg, SRAM_PDN);
146*41004253Sirving-ch-lin ret = mtcmos_wait_for_state(reg, SRAM_PDN_ACK, false);
147a881aebcSirving-ch-lin if (ret)
148a881aebcSirving-ch-lin goto exit;
149a881aebcSirving-ch-lin }
150a881aebcSirving-ch-lin
151*41004253Sirving-ch-lin spm_mtcmos_ctrl_bus_prot(bp_table, bp_steps);
152a881aebcSirving-ch-lin }
153a881aebcSirving-ch-lin
154a881aebcSirving-ch-lin exit:
155a881aebcSirving-ch-lin spin_unlock(&mtcmos_ctrl_lock);
156a881aebcSirving-ch-lin return ret;
157a881aebcSirving-ch-lin }
158a881aebcSirving-ch-lin
spm_mtcmos_ctrl_ufs0(enum mtcmos_state state)159a881aebcSirving-ch-lin int spm_mtcmos_ctrl_ufs0(enum mtcmos_state state)
160a881aebcSirving-ch-lin {
161*41004253Sirving-ch-lin static int rtff_save_flag;
162*41004253Sirving-ch-lin const struct bus_protect *bp_table;
163*41004253Sirving-ch-lin uint32_t bp_steps;
164*41004253Sirving-ch-lin
165*41004253Sirving-ch-lin if (state == STA_POWER_DOWN) {
166*41004253Sirving-ch-lin bp_table = &ufs0_bus_prot_set_table[0];
167*41004253Sirving-ch-lin bp_steps = ARRAY_SIZE(ufs0_bus_prot_set_table);
168*41004253Sirving-ch-lin } else {
169*41004253Sirving-ch-lin bp_table = &ufs0_bus_prot_clr_table[0];
170*41004253Sirving-ch-lin bp_steps = ARRAY_SIZE(ufs0_bus_prot_clr_table);
171*41004253Sirving-ch-lin }
172*41004253Sirving-ch-lin
173*41004253Sirving-ch-lin return spm_mtcmos_ctrl(state, UFS0_PWR_CON, false, &rtff_save_flag,
174*41004253Sirving-ch-lin bp_table, bp_steps);
175a881aebcSirving-ch-lin }
176a881aebcSirving-ch-lin
spm_mtcmos_ctrl_ufs0_phy(enum mtcmos_state state)177a881aebcSirving-ch-lin int spm_mtcmos_ctrl_ufs0_phy(enum mtcmos_state state)
178a881aebcSirving-ch-lin {
179*41004253Sirving-ch-lin static int rtff_save_flag;
180*41004253Sirving-ch-lin const struct bus_protect *bp_table;
181*41004253Sirving-ch-lin uint32_t bp_steps;
182*41004253Sirving-ch-lin
183*41004253Sirving-ch-lin if (state == STA_POWER_DOWN) {
184*41004253Sirving-ch-lin bp_table = &ufs0_phy_bus_prot_set_table[0];
185*41004253Sirving-ch-lin bp_steps = ARRAY_SIZE(ufs0_phy_bus_prot_set_table);
186*41004253Sirving-ch-lin } else {
187*41004253Sirving-ch-lin bp_table = &ufs0_phy_bus_prot_clr_table[0];
188*41004253Sirving-ch-lin bp_steps = ARRAY_SIZE(ufs0_phy_bus_prot_clr_table);
189*41004253Sirving-ch-lin }
190*41004253Sirving-ch-lin
191*41004253Sirving-ch-lin return spm_mtcmos_ctrl(state, UFS0_PHY_PWR_CON, true, &rtff_save_flag,
192*41004253Sirving-ch-lin bp_table, bp_steps);
193a881aebcSirving-ch-lin }
194