blob: 4745101fe29a112f6de6e43f17d503d92889eef0 [file] [log] [blame]
yuezonghe824eb0c2024-06-27 02:32:26 -07001/*
2 * linux/arch/arm/mach-zx297520v2/pcu.c
3 *
4 * Copyright (C) 2015 ZTE-TSP
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11#include <linux/errno.h>
12#include <linux/irq.h>
13#include <linux/module.h>
14
15#include <asm/io.h>
16
17#include <mach/board.h>
18#include <mach/iomap.h>
19#include <mach/pcu.h>
20#include <mach/pcu_def.h>
21#include <mach/irqs.h>
22#include <mach/spinlock.h>
23
24
25/**
26 * helper function
27 *
28 * get index of pcu_info struct.
29 */
30unsigned int pcu_get_index(PCU_INT_INDEX pcu_index)
31{
32 unsigned int i = 0;
33
34 for(i=0; i<ARRAY_SIZE(pcu_info); i++)
35 {
36 if(pcu_index == pcu_info[i].index)
37 return i;
38 }
39
40 return 0xff;
41}
42
43/*
44 * return external interrupt number from ex8-ex15,
45 * return value is 0-7
46 */
47unsigned int pcu_get_8in1_int_source(void)
48{
49 unsigned int vector_8in1 = 0;
50
51 vector_8in1 = zx_read_reg(EX_INT_VECTOR_REG);
52
53 return (vector_8in1&0x7);
54}
55
56
57/*external int 8-15 need extra clear*/
58static void pcu_int_clear_8in1(PCU_INT_INDEX index)
59{
60 unsigned int vector=0;
61
62 if ( (index >= PCU_EX8_INT)&&(index <= PCU_EX15_INT) )
63 {
64 /*
65 *in 7510 platform, 8in1 interrupt would be used by different cores.
66 *when any core installs a new 8in1 interrupt, another core may be
67 * responding another 8in1 interrupt, so 8in1 interrupt shouldn't be
68 *cleared. in this case, nothing to be done. but a new problem comes,
69 * the core install new 8in1 interrupt will receive a fake interrupt.
70 */
71 vector = pcu_get_8in1_int_source();
72 if (index != (vector + PCU_EX8_INT) )
73 return;
74
75 reg_spin_lock();
76 zx_write_reg(EX_INT_TOP_CLEAR_REG, 0x1);
77 reg_spin_unlock();
78 }
79}
80
81/*only pulse int need be clear*/
82void pcu_int_clear(PCU_INT_INDEX pcu_index)
83{
84 unsigned int index;
85
86 index = pcu_get_index(pcu_index);
87 if(index == 0xff)
88 return;
89
90 reg_spin_lock();
91 zx_set_reg(pcu_info[index].int_clr_reg.reg_addr,
92 (1<<pcu_info[index].int_clr_reg.reg_bit_offset));
93 reg_spin_unlock();
94
95 pcu_int_clear_8in1(pcu_index);
96}
97EXPORT_SYMBOL(pcu_int_clear);
98
99/**
100 * helper function
101 *
102 * get pcu level/pol configuration.
103 */
104static void pcu_split_int_type(unsigned int type, unsigned int *level, unsigned int *pol)
105{
106 switch (type) {
107 case IRQ_TYPE_LEVEL_HIGH:
108 *level = 1;
109 *pol = 1;
110 break;
111 case IRQ_TYPE_EDGE_RISING:
112 *level = 0;
113 *pol = 1;
114 break;
115 case IRQ_TYPE_LEVEL_LOW:
116 *level = 1;
117 *pol= 0;
118 break;
119 case IRQ_TYPE_EDGE_FALLING:
120 *level = 0;
121 *pol = 0;
122 break;
123 default:
124 BUG();
125 }
126}
127
128/*
129 * set pcu int type,
130 *
131 */
132void pcu_int_set_type(PCU_INT_INDEX pcu_index,unsigned int type)
133{
134 unsigned int level;
135 unsigned int pol;
136 unsigned int index;
137
138 index = pcu_get_index(pcu_index);
139 if(index == 0xff)
140 return;
141
142 pcu_split_int_type(type, &level, &pol);
143
144 reg_spin_lock();
145 /* set level*/
146 zx_wrie_bit(pcu_info[index].int_type_reg.reg_addr,
147 pcu_info[index].int_type_reg.reg_bit_offset,
148 level);
149
150 /* set polarity */
151 zx_wrie_bit(pcu_info[index].int_pol_reg.reg_addr,
152 pcu_info[index].int_pol_reg.reg_bit_offset,
153 pol);
154 reg_spin_unlock();
155
156 pcu_int_clear(pcu_index);
157}
158EXPORT_SYMBOL(pcu_int_set_type);
159
160/**
161 * init pcu before open irq when system start.
162 *
163 */
164void __init pcu_init(void)
165{
166 int i = 0;
167
168 pr_info("[PCU] PCU_INIT begin.\n");
169
170 /*set default int type/pol, and clear fake int*/
171 for(i=0; i<ARRAY_SIZE(pcu_info); i++)
172 {
173 if(!irq_type_valid(pcu_info[i].irq_type))
174 continue;
175
176 pcu_int_set_type(pcu_info[i].index, pcu_info[i].irq_type);
177 }
178
179 pm_pcu_init();
180
181 pr_info("[PCU] PCU_INIT end.\n");
182}
183
184/* low power function */
185extern unsigned int pm_get_wakesource(void);
186void pm_pcu_init(void)
187{
188 zx_clr_reg(ARM_AP_CONFIG_REG, PCU_MODE_MASK);
189 zx_set_reg(ARM_AP_CONFIG_REG, PCU_DPLL_SEL|PCU_L2_CLK_GATE|PCU_PLL_OFF);
190
191 zx_write_reg(AP_INT_WAKE_DIS_REG, ~(pm_get_wakesource()));
192}
193
194/**
195 * config pcu before poweroff
196 *
197 */
198void pm_set_pcu_poweroff(u32 sleep_time)
199{
200 zx_set_reg(ARM_AP_CONFIG_REG, PCU_POWEROFF_MODE);
201 zx_write_reg(ARM_AP_SLEEP_TIME_REG, sleep_time);
202}
203
204/**
205 * config pcu before sleep
206 *
207 */
208void pm_set_pcu_sleep(u32 sleep_time)
209{
210 zx_set_reg(ARM_AP_CONFIG_REG, PCU_SLEEP_MODE);
211 zx_write_reg(ARM_AP_SLEEP_TIME_REG, sleep_time);
212}
213
214/**
215 * clear pcu sleep mode.
216 *
217 */
218void pm_clear_pcu(void)
219{
220 zx_clr_reg(ARM_AP_CONFIG_REG, PCU_MODE_MASK);
221}
222
223
224/**
225 * get wakeup setting.
226 *
227 */
228unsigned int pcu_get_wakeup_setting(void)
229{
230 return zx_read_reg(AP_INT_WAKE_DIS_REG);
231}
232
233
234/**
235 * set wakeup enable.
236 *
237 *
238 */
239void pcu_irq_wakeup(PCU_INT_INDEX index, int enable)
240{
241 if ( (index < PCU_AP_TIMER1_INT) || (index > PCU_GMAC_INT) )
242 return;
243
244 if(enable)
245 {
246 zx_clr_reg(AP_INT_WAKE_DIS_REG, (1U << index));
247 }
248 else
249 {
250 zx_set_reg(AP_INT_WAKE_DIS_REG, (1U << index));
251 }
252}
253
254/**
255 * set wakeup enable by gic.
256 *
257 *
258 */
259int pcu_set_irq_wake(unsigned gic, unsigned on)
260{
261 PCU_INT_INDEX pcu_index = PCU_UNKNOWN_INT;
262
263 pcu_index = pcu_get_index_by_gic(gic);
264 if(pcu_index == PCU_UNKNOWN_INT)
265 return -EINVAL;
266
267 pcu_irq_wakeup(pcu_index, on);
268
269 return 0;
270}
271
272/**
273 * set pcu type by gic.
274 *
275 *
276 */
277int pcu_set_irq_type(unsigned gic, unsigned int type)
278{
279 PCU_INT_INDEX pcu_index = PCU_UNKNOWN_INT;
280
281 pcu_index = pcu_get_index_by_gic(gic);
282 if(pcu_index == PCU_UNKNOWN_INT)
283 return -EINVAL;
284
285 pcu_int_set_type(pcu_index, type);
286
287 return 0;
288}
289
290/**
291 * clr pcu int by gic.
292 *
293 *
294 */
295int pcu_clr_irq_pending(unsigned gic)
296{
297 PCU_INT_INDEX pcu_index = PCU_UNKNOWN_INT;
298
299 pcu_index = pcu_get_index_by_gic(gic);
300 if(pcu_index == PCU_UNKNOWN_INT)
301 return -EINVAL;
302
303 pcu_int_clear(pcu_index);
304
305 return 0;
306}
307EXPORT_SYMBOL(pcu_clr_irq_pending);
308
309#ifdef CONFIG_PM
310extern void pm_printk(const char *fmt, ...);
311#define pm_ram_log(fmt, args...) \
312{ \
313 printk(KERN_INFO "[SLP] " fmt, ##args); \
314 pm_printk("[SLP] " fmt, ##args); \
315}
316
317extern unsigned int pm_get_sleep_flag(void);
318
319void pm_get_wake_cause(void)
320{
321
322 if(pm_get_sleep_flag()!= true)
323 return;
324
325 unsigned int int_status[2];
326 int i = 0;
327 int index_found = 0xff;
328 unsigned int pcu_wake_setting;
329
330 /* when wake up, the level is high&the value is 0*/
331 int_status[0] = zx_read_reg(PCU_INT_READOUT_REG1);
332 int_status[1] = zx_read_reg(PCU_INT_READOUT_REG2);
333
334 pcu_wake_setting = pcu_get_wakeup_setting();
335
336 for(i=0; i<ARRAY_SIZE(pcu_info); i++)
337 {
338 if(pcu_wake_setting&(1<<pcu_info[i].index))
339 continue;
340
341 if(int_status[pcu_info[i].status_index/32]&(1<<(pcu_info[i].status_index%32)))
342 continue;
343
344 index_found = i;
345 break;
346 }
347
348 if(index_found != 0xff)
349 {
350 pm_ram_log(" wake: %d [%s]\n", pcu_info[index_found].gic_index, pcu_info[index_found].int_name);
351 pm_ram_log(" pcu int status:%x %x\n",int_status[0], int_status[1]);
352 if(pcu_info[index_found].gic_index == ICP_PS2AP_INT)
353 {
354 volatile unsigned int *low_word = ZX29_ICP_APPS_REG+0x8;
355 volatile unsigned int *high_word = ZX29_ICP_APPS_REG+0xC;
356 pm_ram_log(" icp status:%x %x\n",*low_word, *high_word);
357 }
358 }
359 else
360 {
361 pm_ram_log(" wake abnormal\n");
362 pm_ram_log(" pcu int status:%x %x\n",int_status[0], int_status[1]);
363 }
364}
365#endif
366