xref: /OK3568_Linux_fs/kernel/drivers/mfd/db8500-prcmu.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * DB8500 PRCM Unit driver
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) STMicroelectronics 2009
6*4882a593Smuzhiyun  * Copyright (C) ST-Ericsson SA 2010
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  * Author: Kumar Sanghvi <kumar.sanghvi@stericsson.com>
9*4882a593Smuzhiyun  * Author: Sundar Iyer <sundar.iyer@stericsson.com>
10*4882a593Smuzhiyun  * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com>
11*4882a593Smuzhiyun  *
12*4882a593Smuzhiyun  * U8500 PRCM Unit interface driver
13*4882a593Smuzhiyun  */
14*4882a593Smuzhiyun #include <linux/init.h>
15*4882a593Smuzhiyun #include <linux/export.h>
16*4882a593Smuzhiyun #include <linux/kernel.h>
17*4882a593Smuzhiyun #include <linux/delay.h>
18*4882a593Smuzhiyun #include <linux/errno.h>
19*4882a593Smuzhiyun #include <linux/err.h>
20*4882a593Smuzhiyun #include <linux/spinlock.h>
21*4882a593Smuzhiyun #include <linux/io.h>
22*4882a593Smuzhiyun #include <linux/slab.h>
23*4882a593Smuzhiyun #include <linux/mutex.h>
24*4882a593Smuzhiyun #include <linux/completion.h>
25*4882a593Smuzhiyun #include <linux/irq.h>
26*4882a593Smuzhiyun #include <linux/jiffies.h>
27*4882a593Smuzhiyun #include <linux/bitops.h>
28*4882a593Smuzhiyun #include <linux/fs.h>
29*4882a593Smuzhiyun #include <linux/of.h>
30*4882a593Smuzhiyun #include <linux/of_address.h>
31*4882a593Smuzhiyun #include <linux/of_irq.h>
32*4882a593Smuzhiyun #include <linux/platform_device.h>
33*4882a593Smuzhiyun #include <linux/uaccess.h>
34*4882a593Smuzhiyun #include <linux/mfd/core.h>
35*4882a593Smuzhiyun #include <linux/mfd/dbx500-prcmu.h>
36*4882a593Smuzhiyun #include <linux/mfd/abx500/ab8500.h>
37*4882a593Smuzhiyun #include <linux/regulator/db8500-prcmu.h>
38*4882a593Smuzhiyun #include <linux/regulator/machine.h>
39*4882a593Smuzhiyun #include <linux/platform_data/ux500_wdt.h>
40*4882a593Smuzhiyun #include "dbx500-prcmu-regs.h"
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun /* Index of different voltages to be used when accessing AVSData */
43*4882a593Smuzhiyun #define PRCM_AVS_BASE		0x2FC
44*4882a593Smuzhiyun #define PRCM_AVS_VBB_RET	(PRCM_AVS_BASE + 0x0)
45*4882a593Smuzhiyun #define PRCM_AVS_VBB_MAX_OPP	(PRCM_AVS_BASE + 0x1)
46*4882a593Smuzhiyun #define PRCM_AVS_VBB_100_OPP	(PRCM_AVS_BASE + 0x2)
47*4882a593Smuzhiyun #define PRCM_AVS_VBB_50_OPP	(PRCM_AVS_BASE + 0x3)
48*4882a593Smuzhiyun #define PRCM_AVS_VARM_MAX_OPP	(PRCM_AVS_BASE + 0x4)
49*4882a593Smuzhiyun #define PRCM_AVS_VARM_100_OPP	(PRCM_AVS_BASE + 0x5)
50*4882a593Smuzhiyun #define PRCM_AVS_VARM_50_OPP	(PRCM_AVS_BASE + 0x6)
51*4882a593Smuzhiyun #define PRCM_AVS_VARM_RET	(PRCM_AVS_BASE + 0x7)
52*4882a593Smuzhiyun #define PRCM_AVS_VAPE_100_OPP	(PRCM_AVS_BASE + 0x8)
53*4882a593Smuzhiyun #define PRCM_AVS_VAPE_50_OPP	(PRCM_AVS_BASE + 0x9)
54*4882a593Smuzhiyun #define PRCM_AVS_VMOD_100_OPP	(PRCM_AVS_BASE + 0xA)
55*4882a593Smuzhiyun #define PRCM_AVS_VMOD_50_OPP	(PRCM_AVS_BASE + 0xB)
56*4882a593Smuzhiyun #define PRCM_AVS_VSAFE		(PRCM_AVS_BASE + 0xC)
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun #define PRCM_AVS_VOLTAGE		0
59*4882a593Smuzhiyun #define PRCM_AVS_VOLTAGE_MASK		0x3f
60*4882a593Smuzhiyun #define PRCM_AVS_ISSLOWSTARTUP		6
61*4882a593Smuzhiyun #define PRCM_AVS_ISSLOWSTARTUP_MASK	(1 << PRCM_AVS_ISSLOWSTARTUP)
62*4882a593Smuzhiyun #define PRCM_AVS_ISMODEENABLE		7
63*4882a593Smuzhiyun #define PRCM_AVS_ISMODEENABLE_MASK	(1 << PRCM_AVS_ISMODEENABLE)
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun #define PRCM_BOOT_STATUS	0xFFF
66*4882a593Smuzhiyun #define PRCM_ROMCODE_A2P	0xFFE
67*4882a593Smuzhiyun #define PRCM_ROMCODE_P2A	0xFFD
68*4882a593Smuzhiyun #define PRCM_XP70_CUR_PWR_STATE 0xFFC      /* 4 BYTES */
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun #define PRCM_SW_RST_REASON 0xFF8 /* 2 bytes */
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun #define _PRCM_MBOX_HEADER		0xFE8 /* 16 bytes */
73*4882a593Smuzhiyun #define PRCM_MBOX_HEADER_REQ_MB0	(_PRCM_MBOX_HEADER + 0x0)
74*4882a593Smuzhiyun #define PRCM_MBOX_HEADER_REQ_MB1	(_PRCM_MBOX_HEADER + 0x1)
75*4882a593Smuzhiyun #define PRCM_MBOX_HEADER_REQ_MB2	(_PRCM_MBOX_HEADER + 0x2)
76*4882a593Smuzhiyun #define PRCM_MBOX_HEADER_REQ_MB3	(_PRCM_MBOX_HEADER + 0x3)
77*4882a593Smuzhiyun #define PRCM_MBOX_HEADER_REQ_MB4	(_PRCM_MBOX_HEADER + 0x4)
78*4882a593Smuzhiyun #define PRCM_MBOX_HEADER_REQ_MB5	(_PRCM_MBOX_HEADER + 0x5)
79*4882a593Smuzhiyun #define PRCM_MBOX_HEADER_ACK_MB0	(_PRCM_MBOX_HEADER + 0x8)
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun /* Req Mailboxes */
82*4882a593Smuzhiyun #define PRCM_REQ_MB0 0xFDC /* 12 bytes  */
83*4882a593Smuzhiyun #define PRCM_REQ_MB1 0xFD0 /* 12 bytes  */
84*4882a593Smuzhiyun #define PRCM_REQ_MB2 0xFC0 /* 16 bytes  */
85*4882a593Smuzhiyun #define PRCM_REQ_MB3 0xE4C /* 372 bytes  */
86*4882a593Smuzhiyun #define PRCM_REQ_MB4 0xE48 /* 4 bytes  */
87*4882a593Smuzhiyun #define PRCM_REQ_MB5 0xE44 /* 4 bytes  */
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun /* Ack Mailboxes */
90*4882a593Smuzhiyun #define PRCM_ACK_MB0 0xE08 /* 52 bytes  */
91*4882a593Smuzhiyun #define PRCM_ACK_MB1 0xE04 /* 4 bytes */
92*4882a593Smuzhiyun #define PRCM_ACK_MB2 0xE00 /* 4 bytes */
93*4882a593Smuzhiyun #define PRCM_ACK_MB3 0xDFC /* 4 bytes */
94*4882a593Smuzhiyun #define PRCM_ACK_MB4 0xDF8 /* 4 bytes */
95*4882a593Smuzhiyun #define PRCM_ACK_MB5 0xDF4 /* 4 bytes */
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun /* Mailbox 0 headers */
98*4882a593Smuzhiyun #define MB0H_POWER_STATE_TRANS		0
99*4882a593Smuzhiyun #define MB0H_CONFIG_WAKEUPS_EXE		1
100*4882a593Smuzhiyun #define MB0H_READ_WAKEUP_ACK		3
101*4882a593Smuzhiyun #define MB0H_CONFIG_WAKEUPS_SLEEP	4
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun #define MB0H_WAKEUP_EXE 2
104*4882a593Smuzhiyun #define MB0H_WAKEUP_SLEEP 5
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun /* Mailbox 0 REQs */
107*4882a593Smuzhiyun #define PRCM_REQ_MB0_AP_POWER_STATE	(PRCM_REQ_MB0 + 0x0)
108*4882a593Smuzhiyun #define PRCM_REQ_MB0_AP_PLL_STATE	(PRCM_REQ_MB0 + 0x1)
109*4882a593Smuzhiyun #define PRCM_REQ_MB0_ULP_CLOCK_STATE	(PRCM_REQ_MB0 + 0x2)
110*4882a593Smuzhiyun #define PRCM_REQ_MB0_DO_NOT_WFI		(PRCM_REQ_MB0 + 0x3)
111*4882a593Smuzhiyun #define PRCM_REQ_MB0_WAKEUP_8500	(PRCM_REQ_MB0 + 0x4)
112*4882a593Smuzhiyun #define PRCM_REQ_MB0_WAKEUP_4500	(PRCM_REQ_MB0 + 0x8)
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun /* Mailbox 0 ACKs */
115*4882a593Smuzhiyun #define PRCM_ACK_MB0_AP_PWRSTTR_STATUS	(PRCM_ACK_MB0 + 0x0)
116*4882a593Smuzhiyun #define PRCM_ACK_MB0_READ_POINTER	(PRCM_ACK_MB0 + 0x1)
117*4882a593Smuzhiyun #define PRCM_ACK_MB0_WAKEUP_0_8500	(PRCM_ACK_MB0 + 0x4)
118*4882a593Smuzhiyun #define PRCM_ACK_MB0_WAKEUP_0_4500	(PRCM_ACK_MB0 + 0x8)
119*4882a593Smuzhiyun #define PRCM_ACK_MB0_WAKEUP_1_8500	(PRCM_ACK_MB0 + 0x1C)
120*4882a593Smuzhiyun #define PRCM_ACK_MB0_WAKEUP_1_4500	(PRCM_ACK_MB0 + 0x20)
121*4882a593Smuzhiyun #define PRCM_ACK_MB0_EVENT_4500_NUMBERS	20
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun /* Mailbox 1 headers */
124*4882a593Smuzhiyun #define MB1H_ARM_APE_OPP 0x0
125*4882a593Smuzhiyun #define MB1H_RESET_MODEM 0x2
126*4882a593Smuzhiyun #define MB1H_REQUEST_APE_OPP_100_VOLT 0x3
127*4882a593Smuzhiyun #define MB1H_RELEASE_APE_OPP_100_VOLT 0x4
128*4882a593Smuzhiyun #define MB1H_RELEASE_USB_WAKEUP 0x5
129*4882a593Smuzhiyun #define MB1H_PLL_ON_OFF 0x6
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun /* Mailbox 1 Requests */
132*4882a593Smuzhiyun #define PRCM_REQ_MB1_ARM_OPP			(PRCM_REQ_MB1 + 0x0)
133*4882a593Smuzhiyun #define PRCM_REQ_MB1_APE_OPP			(PRCM_REQ_MB1 + 0x1)
134*4882a593Smuzhiyun #define PRCM_REQ_MB1_PLL_ON_OFF			(PRCM_REQ_MB1 + 0x4)
135*4882a593Smuzhiyun #define PLL_SOC0_OFF	0x1
136*4882a593Smuzhiyun #define PLL_SOC0_ON	0x2
137*4882a593Smuzhiyun #define PLL_SOC1_OFF	0x4
138*4882a593Smuzhiyun #define PLL_SOC1_ON	0x8
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun /* Mailbox 1 ACKs */
141*4882a593Smuzhiyun #define PRCM_ACK_MB1_CURRENT_ARM_OPP	(PRCM_ACK_MB1 + 0x0)
142*4882a593Smuzhiyun #define PRCM_ACK_MB1_CURRENT_APE_OPP	(PRCM_ACK_MB1 + 0x1)
143*4882a593Smuzhiyun #define PRCM_ACK_MB1_APE_VOLTAGE_STATUS	(PRCM_ACK_MB1 + 0x2)
144*4882a593Smuzhiyun #define PRCM_ACK_MB1_DVFS_STATUS	(PRCM_ACK_MB1 + 0x3)
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun /* Mailbox 2 headers */
147*4882a593Smuzhiyun #define MB2H_DPS	0x0
148*4882a593Smuzhiyun #define MB2H_AUTO_PWR	0x1
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun /* Mailbox 2 REQs */
151*4882a593Smuzhiyun #define PRCM_REQ_MB2_SVA_MMDSP		(PRCM_REQ_MB2 + 0x0)
152*4882a593Smuzhiyun #define PRCM_REQ_MB2_SVA_PIPE		(PRCM_REQ_MB2 + 0x1)
153*4882a593Smuzhiyun #define PRCM_REQ_MB2_SIA_MMDSP		(PRCM_REQ_MB2 + 0x2)
154*4882a593Smuzhiyun #define PRCM_REQ_MB2_SIA_PIPE		(PRCM_REQ_MB2 + 0x3)
155*4882a593Smuzhiyun #define PRCM_REQ_MB2_SGA		(PRCM_REQ_MB2 + 0x4)
156*4882a593Smuzhiyun #define PRCM_REQ_MB2_B2R2_MCDE		(PRCM_REQ_MB2 + 0x5)
157*4882a593Smuzhiyun #define PRCM_REQ_MB2_ESRAM12		(PRCM_REQ_MB2 + 0x6)
158*4882a593Smuzhiyun #define PRCM_REQ_MB2_ESRAM34		(PRCM_REQ_MB2 + 0x7)
159*4882a593Smuzhiyun #define PRCM_REQ_MB2_AUTO_PM_SLEEP	(PRCM_REQ_MB2 + 0x8)
160*4882a593Smuzhiyun #define PRCM_REQ_MB2_AUTO_PM_IDLE	(PRCM_REQ_MB2 + 0xC)
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun /* Mailbox 2 ACKs */
163*4882a593Smuzhiyun #define PRCM_ACK_MB2_DPS_STATUS (PRCM_ACK_MB2 + 0x0)
164*4882a593Smuzhiyun #define HWACC_PWR_ST_OK 0xFE
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun /* Mailbox 3 headers */
167*4882a593Smuzhiyun #define MB3H_ANC	0x0
168*4882a593Smuzhiyun #define MB3H_SIDETONE	0x1
169*4882a593Smuzhiyun #define MB3H_SYSCLK	0xE
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun /* Mailbox 3 Requests */
172*4882a593Smuzhiyun #define PRCM_REQ_MB3_ANC_FIR_COEFF	(PRCM_REQ_MB3 + 0x0)
173*4882a593Smuzhiyun #define PRCM_REQ_MB3_ANC_IIR_COEFF	(PRCM_REQ_MB3 + 0x20)
174*4882a593Smuzhiyun #define PRCM_REQ_MB3_ANC_SHIFTER	(PRCM_REQ_MB3 + 0x60)
175*4882a593Smuzhiyun #define PRCM_REQ_MB3_ANC_WARP		(PRCM_REQ_MB3 + 0x64)
176*4882a593Smuzhiyun #define PRCM_REQ_MB3_SIDETONE_FIR_GAIN	(PRCM_REQ_MB3 + 0x68)
177*4882a593Smuzhiyun #define PRCM_REQ_MB3_SIDETONE_FIR_COEFF	(PRCM_REQ_MB3 + 0x6C)
178*4882a593Smuzhiyun #define PRCM_REQ_MB3_SYSCLK_MGT		(PRCM_REQ_MB3 + 0x16C)
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun /* Mailbox 4 headers */
181*4882a593Smuzhiyun #define MB4H_DDR_INIT	0x0
182*4882a593Smuzhiyun #define MB4H_MEM_ST	0x1
183*4882a593Smuzhiyun #define MB4H_HOTDOG	0x12
184*4882a593Smuzhiyun #define MB4H_HOTMON	0x13
185*4882a593Smuzhiyun #define MB4H_HOT_PERIOD	0x14
186*4882a593Smuzhiyun #define MB4H_A9WDOG_CONF 0x16
187*4882a593Smuzhiyun #define MB4H_A9WDOG_EN   0x17
188*4882a593Smuzhiyun #define MB4H_A9WDOG_DIS  0x18
189*4882a593Smuzhiyun #define MB4H_A9WDOG_LOAD 0x19
190*4882a593Smuzhiyun #define MB4H_A9WDOG_KICK 0x20
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun /* Mailbox 4 Requests */
193*4882a593Smuzhiyun #define PRCM_REQ_MB4_DDR_ST_AP_SLEEP_IDLE	(PRCM_REQ_MB4 + 0x0)
194*4882a593Smuzhiyun #define PRCM_REQ_MB4_DDR_ST_AP_DEEP_IDLE	(PRCM_REQ_MB4 + 0x1)
195*4882a593Smuzhiyun #define PRCM_REQ_MB4_ESRAM0_ST			(PRCM_REQ_MB4 + 0x3)
196*4882a593Smuzhiyun #define PRCM_REQ_MB4_HOTDOG_THRESHOLD		(PRCM_REQ_MB4 + 0x0)
197*4882a593Smuzhiyun #define PRCM_REQ_MB4_HOTMON_LOW			(PRCM_REQ_MB4 + 0x0)
198*4882a593Smuzhiyun #define PRCM_REQ_MB4_HOTMON_HIGH		(PRCM_REQ_MB4 + 0x1)
199*4882a593Smuzhiyun #define PRCM_REQ_MB4_HOTMON_CONFIG		(PRCM_REQ_MB4 + 0x2)
200*4882a593Smuzhiyun #define PRCM_REQ_MB4_HOT_PERIOD			(PRCM_REQ_MB4 + 0x0)
201*4882a593Smuzhiyun #define HOTMON_CONFIG_LOW			BIT(0)
202*4882a593Smuzhiyun #define HOTMON_CONFIG_HIGH			BIT(1)
203*4882a593Smuzhiyun #define PRCM_REQ_MB4_A9WDOG_0			(PRCM_REQ_MB4 + 0x0)
204*4882a593Smuzhiyun #define PRCM_REQ_MB4_A9WDOG_1			(PRCM_REQ_MB4 + 0x1)
205*4882a593Smuzhiyun #define PRCM_REQ_MB4_A9WDOG_2			(PRCM_REQ_MB4 + 0x2)
206*4882a593Smuzhiyun #define PRCM_REQ_MB4_A9WDOG_3			(PRCM_REQ_MB4 + 0x3)
207*4882a593Smuzhiyun #define A9WDOG_AUTO_OFF_EN			BIT(7)
208*4882a593Smuzhiyun #define A9WDOG_AUTO_OFF_DIS			0
209*4882a593Smuzhiyun #define A9WDOG_ID_MASK				0xf
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun /* Mailbox 5 Requests */
212*4882a593Smuzhiyun #define PRCM_REQ_MB5_I2C_SLAVE_OP	(PRCM_REQ_MB5 + 0x0)
213*4882a593Smuzhiyun #define PRCM_REQ_MB5_I2C_HW_BITS	(PRCM_REQ_MB5 + 0x1)
214*4882a593Smuzhiyun #define PRCM_REQ_MB5_I2C_REG		(PRCM_REQ_MB5 + 0x2)
215*4882a593Smuzhiyun #define PRCM_REQ_MB5_I2C_VAL		(PRCM_REQ_MB5 + 0x3)
216*4882a593Smuzhiyun #define PRCMU_I2C_WRITE(slave) (((slave) << 1) | BIT(6))
217*4882a593Smuzhiyun #define PRCMU_I2C_READ(slave) (((slave) << 1) | BIT(0) | BIT(6))
218*4882a593Smuzhiyun #define PRCMU_I2C_STOP_EN		BIT(3)
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun /* Mailbox 5 ACKs */
221*4882a593Smuzhiyun #define PRCM_ACK_MB5_I2C_STATUS	(PRCM_ACK_MB5 + 0x1)
222*4882a593Smuzhiyun #define PRCM_ACK_MB5_I2C_VAL	(PRCM_ACK_MB5 + 0x3)
223*4882a593Smuzhiyun #define I2C_WR_OK 0x1
224*4882a593Smuzhiyun #define I2C_RD_OK 0x2
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun #define NUM_MB 8
227*4882a593Smuzhiyun #define MBOX_BIT BIT
228*4882a593Smuzhiyun #define ALL_MBOX_BITS (MBOX_BIT(NUM_MB) - 1)
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun /*
231*4882a593Smuzhiyun  * Wakeups/IRQs
232*4882a593Smuzhiyun  */
233*4882a593Smuzhiyun 
234*4882a593Smuzhiyun #define WAKEUP_BIT_RTC BIT(0)
235*4882a593Smuzhiyun #define WAKEUP_BIT_RTT0 BIT(1)
236*4882a593Smuzhiyun #define WAKEUP_BIT_RTT1 BIT(2)
237*4882a593Smuzhiyun #define WAKEUP_BIT_HSI0 BIT(3)
238*4882a593Smuzhiyun #define WAKEUP_BIT_HSI1 BIT(4)
239*4882a593Smuzhiyun #define WAKEUP_BIT_CA_WAKE BIT(5)
240*4882a593Smuzhiyun #define WAKEUP_BIT_USB BIT(6)
241*4882a593Smuzhiyun #define WAKEUP_BIT_ABB BIT(7)
242*4882a593Smuzhiyun #define WAKEUP_BIT_ABB_FIFO BIT(8)
243*4882a593Smuzhiyun #define WAKEUP_BIT_SYSCLK_OK BIT(9)
244*4882a593Smuzhiyun #define WAKEUP_BIT_CA_SLEEP BIT(10)
245*4882a593Smuzhiyun #define WAKEUP_BIT_AC_WAKE_ACK BIT(11)
246*4882a593Smuzhiyun #define WAKEUP_BIT_SIDE_TONE_OK BIT(12)
247*4882a593Smuzhiyun #define WAKEUP_BIT_ANC_OK BIT(13)
248*4882a593Smuzhiyun #define WAKEUP_BIT_SW_ERROR BIT(14)
249*4882a593Smuzhiyun #define WAKEUP_BIT_AC_SLEEP_ACK BIT(15)
250*4882a593Smuzhiyun #define WAKEUP_BIT_ARM BIT(17)
251*4882a593Smuzhiyun #define WAKEUP_BIT_HOTMON_LOW BIT(18)
252*4882a593Smuzhiyun #define WAKEUP_BIT_HOTMON_HIGH BIT(19)
253*4882a593Smuzhiyun #define WAKEUP_BIT_MODEM_SW_RESET_REQ BIT(20)
254*4882a593Smuzhiyun #define WAKEUP_BIT_GPIO0 BIT(23)
255*4882a593Smuzhiyun #define WAKEUP_BIT_GPIO1 BIT(24)
256*4882a593Smuzhiyun #define WAKEUP_BIT_GPIO2 BIT(25)
257*4882a593Smuzhiyun #define WAKEUP_BIT_GPIO3 BIT(26)
258*4882a593Smuzhiyun #define WAKEUP_BIT_GPIO4 BIT(27)
259*4882a593Smuzhiyun #define WAKEUP_BIT_GPIO5 BIT(28)
260*4882a593Smuzhiyun #define WAKEUP_BIT_GPIO6 BIT(29)
261*4882a593Smuzhiyun #define WAKEUP_BIT_GPIO7 BIT(30)
262*4882a593Smuzhiyun #define WAKEUP_BIT_GPIO8 BIT(31)
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun static struct {
265*4882a593Smuzhiyun 	bool valid;
266*4882a593Smuzhiyun 	struct prcmu_fw_version version;
267*4882a593Smuzhiyun } fw_info;
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun static struct irq_domain *db8500_irq_domain;
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun /*
272*4882a593Smuzhiyun  * This vector maps irq numbers to the bits in the bit field used in
273*4882a593Smuzhiyun  * communication with the PRCMU firmware.
274*4882a593Smuzhiyun  *
275*4882a593Smuzhiyun  * The reason for having this is to keep the irq numbers contiguous even though
276*4882a593Smuzhiyun  * the bits in the bit field are not. (The bits also have a tendency to move
277*4882a593Smuzhiyun  * around, to further complicate matters.)
278*4882a593Smuzhiyun  */
279*4882a593Smuzhiyun #define IRQ_INDEX(_name) ((IRQ_PRCMU_##_name))
280*4882a593Smuzhiyun #define IRQ_ENTRY(_name)[IRQ_INDEX(_name)] = (WAKEUP_BIT_##_name)
281*4882a593Smuzhiyun 
282*4882a593Smuzhiyun #define IRQ_PRCMU_RTC 0
283*4882a593Smuzhiyun #define IRQ_PRCMU_RTT0 1
284*4882a593Smuzhiyun #define IRQ_PRCMU_RTT1 2
285*4882a593Smuzhiyun #define IRQ_PRCMU_HSI0 3
286*4882a593Smuzhiyun #define IRQ_PRCMU_HSI1 4
287*4882a593Smuzhiyun #define IRQ_PRCMU_CA_WAKE 5
288*4882a593Smuzhiyun #define IRQ_PRCMU_USB 6
289*4882a593Smuzhiyun #define IRQ_PRCMU_ABB 7
290*4882a593Smuzhiyun #define IRQ_PRCMU_ABB_FIFO 8
291*4882a593Smuzhiyun #define IRQ_PRCMU_ARM 9
292*4882a593Smuzhiyun #define IRQ_PRCMU_MODEM_SW_RESET_REQ 10
293*4882a593Smuzhiyun #define IRQ_PRCMU_GPIO0 11
294*4882a593Smuzhiyun #define IRQ_PRCMU_GPIO1 12
295*4882a593Smuzhiyun #define IRQ_PRCMU_GPIO2 13
296*4882a593Smuzhiyun #define IRQ_PRCMU_GPIO3 14
297*4882a593Smuzhiyun #define IRQ_PRCMU_GPIO4 15
298*4882a593Smuzhiyun #define IRQ_PRCMU_GPIO5 16
299*4882a593Smuzhiyun #define IRQ_PRCMU_GPIO6 17
300*4882a593Smuzhiyun #define IRQ_PRCMU_GPIO7 18
301*4882a593Smuzhiyun #define IRQ_PRCMU_GPIO8 19
302*4882a593Smuzhiyun #define IRQ_PRCMU_CA_SLEEP 20
303*4882a593Smuzhiyun #define IRQ_PRCMU_HOTMON_LOW 21
304*4882a593Smuzhiyun #define IRQ_PRCMU_HOTMON_HIGH 22
305*4882a593Smuzhiyun #define NUM_PRCMU_WAKEUPS 23
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun static u32 prcmu_irq_bit[NUM_PRCMU_WAKEUPS] = {
308*4882a593Smuzhiyun 	IRQ_ENTRY(RTC),
309*4882a593Smuzhiyun 	IRQ_ENTRY(RTT0),
310*4882a593Smuzhiyun 	IRQ_ENTRY(RTT1),
311*4882a593Smuzhiyun 	IRQ_ENTRY(HSI0),
312*4882a593Smuzhiyun 	IRQ_ENTRY(HSI1),
313*4882a593Smuzhiyun 	IRQ_ENTRY(CA_WAKE),
314*4882a593Smuzhiyun 	IRQ_ENTRY(USB),
315*4882a593Smuzhiyun 	IRQ_ENTRY(ABB),
316*4882a593Smuzhiyun 	IRQ_ENTRY(ABB_FIFO),
317*4882a593Smuzhiyun 	IRQ_ENTRY(CA_SLEEP),
318*4882a593Smuzhiyun 	IRQ_ENTRY(ARM),
319*4882a593Smuzhiyun 	IRQ_ENTRY(HOTMON_LOW),
320*4882a593Smuzhiyun 	IRQ_ENTRY(HOTMON_HIGH),
321*4882a593Smuzhiyun 	IRQ_ENTRY(MODEM_SW_RESET_REQ),
322*4882a593Smuzhiyun 	IRQ_ENTRY(GPIO0),
323*4882a593Smuzhiyun 	IRQ_ENTRY(GPIO1),
324*4882a593Smuzhiyun 	IRQ_ENTRY(GPIO2),
325*4882a593Smuzhiyun 	IRQ_ENTRY(GPIO3),
326*4882a593Smuzhiyun 	IRQ_ENTRY(GPIO4),
327*4882a593Smuzhiyun 	IRQ_ENTRY(GPIO5),
328*4882a593Smuzhiyun 	IRQ_ENTRY(GPIO6),
329*4882a593Smuzhiyun 	IRQ_ENTRY(GPIO7),
330*4882a593Smuzhiyun 	IRQ_ENTRY(GPIO8)
331*4882a593Smuzhiyun };
332*4882a593Smuzhiyun 
333*4882a593Smuzhiyun #define VALID_WAKEUPS (BIT(NUM_PRCMU_WAKEUP_INDICES) - 1)
334*4882a593Smuzhiyun #define WAKEUP_ENTRY(_name)[PRCMU_WAKEUP_INDEX_##_name] = (WAKEUP_BIT_##_name)
335*4882a593Smuzhiyun static u32 prcmu_wakeup_bit[NUM_PRCMU_WAKEUP_INDICES] = {
336*4882a593Smuzhiyun 	WAKEUP_ENTRY(RTC),
337*4882a593Smuzhiyun 	WAKEUP_ENTRY(RTT0),
338*4882a593Smuzhiyun 	WAKEUP_ENTRY(RTT1),
339*4882a593Smuzhiyun 	WAKEUP_ENTRY(HSI0),
340*4882a593Smuzhiyun 	WAKEUP_ENTRY(HSI1),
341*4882a593Smuzhiyun 	WAKEUP_ENTRY(USB),
342*4882a593Smuzhiyun 	WAKEUP_ENTRY(ABB),
343*4882a593Smuzhiyun 	WAKEUP_ENTRY(ABB_FIFO),
344*4882a593Smuzhiyun 	WAKEUP_ENTRY(ARM)
345*4882a593Smuzhiyun };
346*4882a593Smuzhiyun 
347*4882a593Smuzhiyun /*
348*4882a593Smuzhiyun  * mb0_transfer - state needed for mailbox 0 communication.
349*4882a593Smuzhiyun  * @lock:		The transaction lock.
350*4882a593Smuzhiyun  * @dbb_events_lock:	A lock used to handle concurrent access to (parts of)
351*4882a593Smuzhiyun  *			the request data.
352*4882a593Smuzhiyun  * @mask_work:		Work structure used for (un)masking wakeup interrupts.
353*4882a593Smuzhiyun  * @req:		Request data that need to persist between requests.
354*4882a593Smuzhiyun  */
355*4882a593Smuzhiyun static struct {
356*4882a593Smuzhiyun 	spinlock_t lock;
357*4882a593Smuzhiyun 	spinlock_t dbb_irqs_lock;
358*4882a593Smuzhiyun 	struct work_struct mask_work;
359*4882a593Smuzhiyun 	struct mutex ac_wake_lock;
360*4882a593Smuzhiyun 	struct completion ac_wake_work;
361*4882a593Smuzhiyun 	struct {
362*4882a593Smuzhiyun 		u32 dbb_irqs;
363*4882a593Smuzhiyun 		u32 dbb_wakeups;
364*4882a593Smuzhiyun 		u32 abb_events;
365*4882a593Smuzhiyun 	} req;
366*4882a593Smuzhiyun } mb0_transfer;
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun /*
369*4882a593Smuzhiyun  * mb1_transfer - state needed for mailbox 1 communication.
370*4882a593Smuzhiyun  * @lock:	The transaction lock.
371*4882a593Smuzhiyun  * @work:	The transaction completion structure.
372*4882a593Smuzhiyun  * @ape_opp:	The current APE OPP.
373*4882a593Smuzhiyun  * @ack:	Reply ("acknowledge") data.
374*4882a593Smuzhiyun  */
375*4882a593Smuzhiyun static struct {
376*4882a593Smuzhiyun 	struct mutex lock;
377*4882a593Smuzhiyun 	struct completion work;
378*4882a593Smuzhiyun 	u8 ape_opp;
379*4882a593Smuzhiyun 	struct {
380*4882a593Smuzhiyun 		u8 header;
381*4882a593Smuzhiyun 		u8 arm_opp;
382*4882a593Smuzhiyun 		u8 ape_opp;
383*4882a593Smuzhiyun 		u8 ape_voltage_status;
384*4882a593Smuzhiyun 	} ack;
385*4882a593Smuzhiyun } mb1_transfer;
386*4882a593Smuzhiyun 
387*4882a593Smuzhiyun /*
388*4882a593Smuzhiyun  * mb2_transfer - state needed for mailbox 2 communication.
389*4882a593Smuzhiyun  * @lock:            The transaction lock.
390*4882a593Smuzhiyun  * @work:            The transaction completion structure.
391*4882a593Smuzhiyun  * @auto_pm_lock:    The autonomous power management configuration lock.
392*4882a593Smuzhiyun  * @auto_pm_enabled: A flag indicating whether autonomous PM is enabled.
393*4882a593Smuzhiyun  * @req:             Request data that need to persist between requests.
394*4882a593Smuzhiyun  * @ack:             Reply ("acknowledge") data.
395*4882a593Smuzhiyun  */
396*4882a593Smuzhiyun static struct {
397*4882a593Smuzhiyun 	struct mutex lock;
398*4882a593Smuzhiyun 	struct completion work;
399*4882a593Smuzhiyun 	spinlock_t auto_pm_lock;
400*4882a593Smuzhiyun 	bool auto_pm_enabled;
401*4882a593Smuzhiyun 	struct {
402*4882a593Smuzhiyun 		u8 status;
403*4882a593Smuzhiyun 	} ack;
404*4882a593Smuzhiyun } mb2_transfer;
405*4882a593Smuzhiyun 
406*4882a593Smuzhiyun /*
407*4882a593Smuzhiyun  * mb3_transfer - state needed for mailbox 3 communication.
408*4882a593Smuzhiyun  * @lock:		The request lock.
409*4882a593Smuzhiyun  * @sysclk_lock:	A lock used to handle concurrent sysclk requests.
410*4882a593Smuzhiyun  * @sysclk_work:	Work structure used for sysclk requests.
411*4882a593Smuzhiyun  */
412*4882a593Smuzhiyun static struct {
413*4882a593Smuzhiyun 	spinlock_t lock;
414*4882a593Smuzhiyun 	struct mutex sysclk_lock;
415*4882a593Smuzhiyun 	struct completion sysclk_work;
416*4882a593Smuzhiyun } mb3_transfer;
417*4882a593Smuzhiyun 
418*4882a593Smuzhiyun /*
419*4882a593Smuzhiyun  * mb4_transfer - state needed for mailbox 4 communication.
420*4882a593Smuzhiyun  * @lock:	The transaction lock.
421*4882a593Smuzhiyun  * @work:	The transaction completion structure.
422*4882a593Smuzhiyun  */
423*4882a593Smuzhiyun static struct {
424*4882a593Smuzhiyun 	struct mutex lock;
425*4882a593Smuzhiyun 	struct completion work;
426*4882a593Smuzhiyun } mb4_transfer;
427*4882a593Smuzhiyun 
428*4882a593Smuzhiyun /*
429*4882a593Smuzhiyun  * mb5_transfer - state needed for mailbox 5 communication.
430*4882a593Smuzhiyun  * @lock:	The transaction lock.
431*4882a593Smuzhiyun  * @work:	The transaction completion structure.
432*4882a593Smuzhiyun  * @ack:	Reply ("acknowledge") data.
433*4882a593Smuzhiyun  */
434*4882a593Smuzhiyun static struct {
435*4882a593Smuzhiyun 	struct mutex lock;
436*4882a593Smuzhiyun 	struct completion work;
437*4882a593Smuzhiyun 	struct {
438*4882a593Smuzhiyun 		u8 status;
439*4882a593Smuzhiyun 		u8 value;
440*4882a593Smuzhiyun 	} ack;
441*4882a593Smuzhiyun } mb5_transfer;
442*4882a593Smuzhiyun 
443*4882a593Smuzhiyun static atomic_t ac_wake_req_state = ATOMIC_INIT(0);
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun /* Spinlocks */
446*4882a593Smuzhiyun static DEFINE_SPINLOCK(prcmu_lock);
447*4882a593Smuzhiyun static DEFINE_SPINLOCK(clkout_lock);
448*4882a593Smuzhiyun 
449*4882a593Smuzhiyun /* Global var to runtime determine TCDM base for v2 or v1 */
450*4882a593Smuzhiyun static __iomem void *tcdm_base;
451*4882a593Smuzhiyun static __iomem void *prcmu_base;
452*4882a593Smuzhiyun 
453*4882a593Smuzhiyun struct clk_mgt {
454*4882a593Smuzhiyun 	u32 offset;
455*4882a593Smuzhiyun 	u32 pllsw;
456*4882a593Smuzhiyun 	int branch;
457*4882a593Smuzhiyun 	bool clk38div;
458*4882a593Smuzhiyun };
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun enum {
461*4882a593Smuzhiyun 	PLL_RAW,
462*4882a593Smuzhiyun 	PLL_FIX,
463*4882a593Smuzhiyun 	PLL_DIV
464*4882a593Smuzhiyun };
465*4882a593Smuzhiyun 
466*4882a593Smuzhiyun static DEFINE_SPINLOCK(clk_mgt_lock);
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun #define CLK_MGT_ENTRY(_name, _branch, _clk38div)[PRCMU_##_name] = \
469*4882a593Smuzhiyun 	{ (PRCM_##_name##_MGT), 0 , _branch, _clk38div}
470*4882a593Smuzhiyun static struct clk_mgt clk_mgt[PRCMU_NUM_REG_CLOCKS] = {
471*4882a593Smuzhiyun 	CLK_MGT_ENTRY(SGACLK, PLL_DIV, false),
472*4882a593Smuzhiyun 	CLK_MGT_ENTRY(UARTCLK, PLL_FIX, true),
473*4882a593Smuzhiyun 	CLK_MGT_ENTRY(MSP02CLK, PLL_FIX, true),
474*4882a593Smuzhiyun 	CLK_MGT_ENTRY(MSP1CLK, PLL_FIX, true),
475*4882a593Smuzhiyun 	CLK_MGT_ENTRY(I2CCLK, PLL_FIX, true),
476*4882a593Smuzhiyun 	CLK_MGT_ENTRY(SDMMCCLK, PLL_DIV, true),
477*4882a593Smuzhiyun 	CLK_MGT_ENTRY(SLIMCLK, PLL_FIX, true),
478*4882a593Smuzhiyun 	CLK_MGT_ENTRY(PER1CLK, PLL_DIV, true),
479*4882a593Smuzhiyun 	CLK_MGT_ENTRY(PER2CLK, PLL_DIV, true),
480*4882a593Smuzhiyun 	CLK_MGT_ENTRY(PER3CLK, PLL_DIV, true),
481*4882a593Smuzhiyun 	CLK_MGT_ENTRY(PER5CLK, PLL_DIV, true),
482*4882a593Smuzhiyun 	CLK_MGT_ENTRY(PER6CLK, PLL_DIV, true),
483*4882a593Smuzhiyun 	CLK_MGT_ENTRY(PER7CLK, PLL_DIV, true),
484*4882a593Smuzhiyun 	CLK_MGT_ENTRY(LCDCLK, PLL_FIX, true),
485*4882a593Smuzhiyun 	CLK_MGT_ENTRY(BMLCLK, PLL_DIV, true),
486*4882a593Smuzhiyun 	CLK_MGT_ENTRY(HSITXCLK, PLL_DIV, true),
487*4882a593Smuzhiyun 	CLK_MGT_ENTRY(HSIRXCLK, PLL_DIV, true),
488*4882a593Smuzhiyun 	CLK_MGT_ENTRY(HDMICLK, PLL_FIX, false),
489*4882a593Smuzhiyun 	CLK_MGT_ENTRY(APEATCLK, PLL_DIV, true),
490*4882a593Smuzhiyun 	CLK_MGT_ENTRY(APETRACECLK, PLL_DIV, true),
491*4882a593Smuzhiyun 	CLK_MGT_ENTRY(MCDECLK, PLL_DIV, true),
492*4882a593Smuzhiyun 	CLK_MGT_ENTRY(IPI2CCLK, PLL_FIX, true),
493*4882a593Smuzhiyun 	CLK_MGT_ENTRY(DSIALTCLK, PLL_FIX, false),
494*4882a593Smuzhiyun 	CLK_MGT_ENTRY(DMACLK, PLL_DIV, true),
495*4882a593Smuzhiyun 	CLK_MGT_ENTRY(B2R2CLK, PLL_DIV, true),
496*4882a593Smuzhiyun 	CLK_MGT_ENTRY(TVCLK, PLL_FIX, true),
497*4882a593Smuzhiyun 	CLK_MGT_ENTRY(SSPCLK, PLL_FIX, true),
498*4882a593Smuzhiyun 	CLK_MGT_ENTRY(RNGCLK, PLL_FIX, true),
499*4882a593Smuzhiyun 	CLK_MGT_ENTRY(UICCCLK, PLL_FIX, false),
500*4882a593Smuzhiyun };
501*4882a593Smuzhiyun 
502*4882a593Smuzhiyun struct dsiclk {
503*4882a593Smuzhiyun 	u32 divsel_mask;
504*4882a593Smuzhiyun 	u32 divsel_shift;
505*4882a593Smuzhiyun 	u32 divsel;
506*4882a593Smuzhiyun };
507*4882a593Smuzhiyun 
508*4882a593Smuzhiyun static struct dsiclk dsiclk[2] = {
509*4882a593Smuzhiyun 	{
510*4882a593Smuzhiyun 		.divsel_mask = PRCM_DSI_PLLOUT_SEL_DSI0_PLLOUT_DIVSEL_MASK,
511*4882a593Smuzhiyun 		.divsel_shift = PRCM_DSI_PLLOUT_SEL_DSI0_PLLOUT_DIVSEL_SHIFT,
512*4882a593Smuzhiyun 		.divsel = PRCM_DSI_PLLOUT_SEL_PHI,
513*4882a593Smuzhiyun 	},
514*4882a593Smuzhiyun 	{
515*4882a593Smuzhiyun 		.divsel_mask = PRCM_DSI_PLLOUT_SEL_DSI1_PLLOUT_DIVSEL_MASK,
516*4882a593Smuzhiyun 		.divsel_shift = PRCM_DSI_PLLOUT_SEL_DSI1_PLLOUT_DIVSEL_SHIFT,
517*4882a593Smuzhiyun 		.divsel = PRCM_DSI_PLLOUT_SEL_PHI,
518*4882a593Smuzhiyun 	}
519*4882a593Smuzhiyun };
520*4882a593Smuzhiyun 
521*4882a593Smuzhiyun struct dsiescclk {
522*4882a593Smuzhiyun 	u32 en;
523*4882a593Smuzhiyun 	u32 div_mask;
524*4882a593Smuzhiyun 	u32 div_shift;
525*4882a593Smuzhiyun };
526*4882a593Smuzhiyun 
527*4882a593Smuzhiyun static struct dsiescclk dsiescclk[3] = {
528*4882a593Smuzhiyun 	{
529*4882a593Smuzhiyun 		.en = PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_EN,
530*4882a593Smuzhiyun 		.div_mask = PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_DIV_MASK,
531*4882a593Smuzhiyun 		.div_shift = PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_DIV_SHIFT,
532*4882a593Smuzhiyun 	},
533*4882a593Smuzhiyun 	{
534*4882a593Smuzhiyun 		.en = PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_EN,
535*4882a593Smuzhiyun 		.div_mask = PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_DIV_MASK,
536*4882a593Smuzhiyun 		.div_shift = PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_DIV_SHIFT,
537*4882a593Smuzhiyun 	},
538*4882a593Smuzhiyun 	{
539*4882a593Smuzhiyun 		.en = PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_EN,
540*4882a593Smuzhiyun 		.div_mask = PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_DIV_MASK,
541*4882a593Smuzhiyun 		.div_shift = PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_DIV_SHIFT,
542*4882a593Smuzhiyun 	}
543*4882a593Smuzhiyun };
544*4882a593Smuzhiyun 
db8500_prcmu_read(unsigned int reg)545*4882a593Smuzhiyun u32 db8500_prcmu_read(unsigned int reg)
546*4882a593Smuzhiyun {
547*4882a593Smuzhiyun 	return readl(prcmu_base + reg);
548*4882a593Smuzhiyun }
549*4882a593Smuzhiyun 
db8500_prcmu_write(unsigned int reg,u32 value)550*4882a593Smuzhiyun void db8500_prcmu_write(unsigned int reg, u32 value)
551*4882a593Smuzhiyun {
552*4882a593Smuzhiyun 	unsigned long flags;
553*4882a593Smuzhiyun 
554*4882a593Smuzhiyun 	spin_lock_irqsave(&prcmu_lock, flags);
555*4882a593Smuzhiyun 	writel(value, (prcmu_base + reg));
556*4882a593Smuzhiyun 	spin_unlock_irqrestore(&prcmu_lock, flags);
557*4882a593Smuzhiyun }
558*4882a593Smuzhiyun 
db8500_prcmu_write_masked(unsigned int reg,u32 mask,u32 value)559*4882a593Smuzhiyun void db8500_prcmu_write_masked(unsigned int reg, u32 mask, u32 value)
560*4882a593Smuzhiyun {
561*4882a593Smuzhiyun 	u32 val;
562*4882a593Smuzhiyun 	unsigned long flags;
563*4882a593Smuzhiyun 
564*4882a593Smuzhiyun 	spin_lock_irqsave(&prcmu_lock, flags);
565*4882a593Smuzhiyun 	val = readl(prcmu_base + reg);
566*4882a593Smuzhiyun 	val = ((val & ~mask) | (value & mask));
567*4882a593Smuzhiyun 	writel(val, (prcmu_base + reg));
568*4882a593Smuzhiyun 	spin_unlock_irqrestore(&prcmu_lock, flags);
569*4882a593Smuzhiyun }
570*4882a593Smuzhiyun 
prcmu_get_fw_version(void)571*4882a593Smuzhiyun struct prcmu_fw_version *prcmu_get_fw_version(void)
572*4882a593Smuzhiyun {
573*4882a593Smuzhiyun 	return fw_info.valid ? &fw_info.version : NULL;
574*4882a593Smuzhiyun }
575*4882a593Smuzhiyun 
prcmu_is_ulppll_disabled(void)576*4882a593Smuzhiyun static bool prcmu_is_ulppll_disabled(void)
577*4882a593Smuzhiyun {
578*4882a593Smuzhiyun 	struct prcmu_fw_version *ver;
579*4882a593Smuzhiyun 
580*4882a593Smuzhiyun 	ver = prcmu_get_fw_version();
581*4882a593Smuzhiyun 	return ver && ver->project == PRCMU_FW_PROJECT_U8420_SYSCLK;
582*4882a593Smuzhiyun }
583*4882a593Smuzhiyun 
prcmu_has_arm_maxopp(void)584*4882a593Smuzhiyun bool prcmu_has_arm_maxopp(void)
585*4882a593Smuzhiyun {
586*4882a593Smuzhiyun 	return (readb(tcdm_base + PRCM_AVS_VARM_MAX_OPP) &
587*4882a593Smuzhiyun 		PRCM_AVS_ISMODEENABLE_MASK) == PRCM_AVS_ISMODEENABLE_MASK;
588*4882a593Smuzhiyun }
589*4882a593Smuzhiyun 
590*4882a593Smuzhiyun /**
591*4882a593Smuzhiyun  * prcmu_set_rc_a2p - This function is used to run few power state sequences
592*4882a593Smuzhiyun  * @val: Value to be set, i.e. transition requested
593*4882a593Smuzhiyun  * Returns: 0 on success, -EINVAL on invalid argument
594*4882a593Smuzhiyun  *
595*4882a593Smuzhiyun  * This function is used to run the following power state sequences -
596*4882a593Smuzhiyun  * any state to ApReset,  ApDeepSleep to ApExecute, ApExecute to ApDeepSleep
597*4882a593Smuzhiyun  */
prcmu_set_rc_a2p(enum romcode_write val)598*4882a593Smuzhiyun int prcmu_set_rc_a2p(enum romcode_write val)
599*4882a593Smuzhiyun {
600*4882a593Smuzhiyun 	if (val < RDY_2_DS || val > RDY_2_XP70_RST)
601*4882a593Smuzhiyun 		return -EINVAL;
602*4882a593Smuzhiyun 	writeb(val, (tcdm_base + PRCM_ROMCODE_A2P));
603*4882a593Smuzhiyun 	return 0;
604*4882a593Smuzhiyun }
605*4882a593Smuzhiyun 
606*4882a593Smuzhiyun /**
607*4882a593Smuzhiyun  * prcmu_get_rc_p2a - This function is used to get power state sequences
608*4882a593Smuzhiyun  * Returns: the power transition that has last happened
609*4882a593Smuzhiyun  *
610*4882a593Smuzhiyun  * This function can return the following transitions-
611*4882a593Smuzhiyun  * any state to ApReset,  ApDeepSleep to ApExecute, ApExecute to ApDeepSleep
612*4882a593Smuzhiyun  */
prcmu_get_rc_p2a(void)613*4882a593Smuzhiyun enum romcode_read prcmu_get_rc_p2a(void)
614*4882a593Smuzhiyun {
615*4882a593Smuzhiyun 	return readb(tcdm_base + PRCM_ROMCODE_P2A);
616*4882a593Smuzhiyun }
617*4882a593Smuzhiyun 
618*4882a593Smuzhiyun /**
619*4882a593Smuzhiyun  * prcmu_get_current_mode - Return the current XP70 power mode
620*4882a593Smuzhiyun  * Returns: Returns the current AP(ARM) power mode: init,
621*4882a593Smuzhiyun  * apBoot, apExecute, apDeepSleep, apSleep, apIdle, apReset
622*4882a593Smuzhiyun  */
prcmu_get_xp70_current_state(void)623*4882a593Smuzhiyun enum ap_pwrst prcmu_get_xp70_current_state(void)
624*4882a593Smuzhiyun {
625*4882a593Smuzhiyun 	return readb(tcdm_base + PRCM_XP70_CUR_PWR_STATE);
626*4882a593Smuzhiyun }
627*4882a593Smuzhiyun 
628*4882a593Smuzhiyun /**
629*4882a593Smuzhiyun  * prcmu_config_clkout - Configure one of the programmable clock outputs.
630*4882a593Smuzhiyun  * @clkout:	The CLKOUT number (0 or 1).
631*4882a593Smuzhiyun  * @source:	The clock to be used (one of the PRCMU_CLKSRC_*).
632*4882a593Smuzhiyun  * @div:	The divider to be applied.
633*4882a593Smuzhiyun  *
634*4882a593Smuzhiyun  * Configures one of the programmable clock outputs (CLKOUTs).
635*4882a593Smuzhiyun  * @div should be in the range [1,63] to request a configuration, or 0 to
636*4882a593Smuzhiyun  * inform that the configuration is no longer requested.
637*4882a593Smuzhiyun  */
prcmu_config_clkout(u8 clkout,u8 source,u8 div)638*4882a593Smuzhiyun int prcmu_config_clkout(u8 clkout, u8 source, u8 div)
639*4882a593Smuzhiyun {
640*4882a593Smuzhiyun 	static int requests[2];
641*4882a593Smuzhiyun 	int r = 0;
642*4882a593Smuzhiyun 	unsigned long flags;
643*4882a593Smuzhiyun 	u32 val;
644*4882a593Smuzhiyun 	u32 bits;
645*4882a593Smuzhiyun 	u32 mask;
646*4882a593Smuzhiyun 	u32 div_mask;
647*4882a593Smuzhiyun 
648*4882a593Smuzhiyun 	BUG_ON(clkout > 1);
649*4882a593Smuzhiyun 	BUG_ON(div > 63);
650*4882a593Smuzhiyun 	BUG_ON((clkout == 0) && (source > PRCMU_CLKSRC_CLK009));
651*4882a593Smuzhiyun 
652*4882a593Smuzhiyun 	if (!div && !requests[clkout])
653*4882a593Smuzhiyun 		return -EINVAL;
654*4882a593Smuzhiyun 
655*4882a593Smuzhiyun 	if (clkout == 0) {
656*4882a593Smuzhiyun 		div_mask = PRCM_CLKOCR_CLKODIV0_MASK;
657*4882a593Smuzhiyun 		mask = (PRCM_CLKOCR_CLKODIV0_MASK | PRCM_CLKOCR_CLKOSEL0_MASK);
658*4882a593Smuzhiyun 		bits = ((source << PRCM_CLKOCR_CLKOSEL0_SHIFT) |
659*4882a593Smuzhiyun 			(div << PRCM_CLKOCR_CLKODIV0_SHIFT));
660*4882a593Smuzhiyun 	} else {
661*4882a593Smuzhiyun 		div_mask = PRCM_CLKOCR_CLKODIV1_MASK;
662*4882a593Smuzhiyun 		mask = (PRCM_CLKOCR_CLKODIV1_MASK | PRCM_CLKOCR_CLKOSEL1_MASK |
663*4882a593Smuzhiyun 			PRCM_CLKOCR_CLK1TYPE);
664*4882a593Smuzhiyun 		bits = ((source << PRCM_CLKOCR_CLKOSEL1_SHIFT) |
665*4882a593Smuzhiyun 			(div << PRCM_CLKOCR_CLKODIV1_SHIFT));
666*4882a593Smuzhiyun 	}
667*4882a593Smuzhiyun 	bits &= mask;
668*4882a593Smuzhiyun 
669*4882a593Smuzhiyun 	spin_lock_irqsave(&clkout_lock, flags);
670*4882a593Smuzhiyun 
671*4882a593Smuzhiyun 	val = readl(PRCM_CLKOCR);
672*4882a593Smuzhiyun 	if (val & div_mask) {
673*4882a593Smuzhiyun 		if (div) {
674*4882a593Smuzhiyun 			if ((val & mask) != bits) {
675*4882a593Smuzhiyun 				r = -EBUSY;
676*4882a593Smuzhiyun 				goto unlock_and_return;
677*4882a593Smuzhiyun 			}
678*4882a593Smuzhiyun 		} else {
679*4882a593Smuzhiyun 			if ((val & mask & ~div_mask) != bits) {
680*4882a593Smuzhiyun 				r = -EINVAL;
681*4882a593Smuzhiyun 				goto unlock_and_return;
682*4882a593Smuzhiyun 			}
683*4882a593Smuzhiyun 		}
684*4882a593Smuzhiyun 	}
685*4882a593Smuzhiyun 	writel((bits | (val & ~mask)), PRCM_CLKOCR);
686*4882a593Smuzhiyun 	requests[clkout] += (div ? 1 : -1);
687*4882a593Smuzhiyun 
688*4882a593Smuzhiyun unlock_and_return:
689*4882a593Smuzhiyun 	spin_unlock_irqrestore(&clkout_lock, flags);
690*4882a593Smuzhiyun 
691*4882a593Smuzhiyun 	return r;
692*4882a593Smuzhiyun }
693*4882a593Smuzhiyun 
db8500_prcmu_set_power_state(u8 state,bool keep_ulp_clk,bool keep_ap_pll)694*4882a593Smuzhiyun int db8500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll)
695*4882a593Smuzhiyun {
696*4882a593Smuzhiyun 	unsigned long flags;
697*4882a593Smuzhiyun 
698*4882a593Smuzhiyun 	BUG_ON((state < PRCMU_AP_SLEEP) || (PRCMU_AP_DEEP_IDLE < state));
699*4882a593Smuzhiyun 
700*4882a593Smuzhiyun 	spin_lock_irqsave(&mb0_transfer.lock, flags);
701*4882a593Smuzhiyun 
702*4882a593Smuzhiyun 	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
703*4882a593Smuzhiyun 		cpu_relax();
704*4882a593Smuzhiyun 
705*4882a593Smuzhiyun 	writeb(MB0H_POWER_STATE_TRANS, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB0));
706*4882a593Smuzhiyun 	writeb(state, (tcdm_base + PRCM_REQ_MB0_AP_POWER_STATE));
707*4882a593Smuzhiyun 	writeb((keep_ap_pll ? 1 : 0), (tcdm_base + PRCM_REQ_MB0_AP_PLL_STATE));
708*4882a593Smuzhiyun 	writeb((keep_ulp_clk ? 1 : 0),
709*4882a593Smuzhiyun 		(tcdm_base + PRCM_REQ_MB0_ULP_CLOCK_STATE));
710*4882a593Smuzhiyun 	writeb(0, (tcdm_base + PRCM_REQ_MB0_DO_NOT_WFI));
711*4882a593Smuzhiyun 	writel(MBOX_BIT(0), PRCM_MBOX_CPU_SET);
712*4882a593Smuzhiyun 
713*4882a593Smuzhiyun 	spin_unlock_irqrestore(&mb0_transfer.lock, flags);
714*4882a593Smuzhiyun 
715*4882a593Smuzhiyun 	return 0;
716*4882a593Smuzhiyun }
717*4882a593Smuzhiyun 
db8500_prcmu_get_power_state_result(void)718*4882a593Smuzhiyun u8 db8500_prcmu_get_power_state_result(void)
719*4882a593Smuzhiyun {
720*4882a593Smuzhiyun 	return readb(tcdm_base + PRCM_ACK_MB0_AP_PWRSTTR_STATUS);
721*4882a593Smuzhiyun }
722*4882a593Smuzhiyun 
723*4882a593Smuzhiyun /* This function should only be called while mb0_transfer.lock is held. */
config_wakeups(void)724*4882a593Smuzhiyun static void config_wakeups(void)
725*4882a593Smuzhiyun {
726*4882a593Smuzhiyun 	const u8 header[2] = {
727*4882a593Smuzhiyun 		MB0H_CONFIG_WAKEUPS_EXE,
728*4882a593Smuzhiyun 		MB0H_CONFIG_WAKEUPS_SLEEP
729*4882a593Smuzhiyun 	};
730*4882a593Smuzhiyun 	static u32 last_dbb_events;
731*4882a593Smuzhiyun 	static u32 last_abb_events;
732*4882a593Smuzhiyun 	u32 dbb_events;
733*4882a593Smuzhiyun 	u32 abb_events;
734*4882a593Smuzhiyun 	unsigned int i;
735*4882a593Smuzhiyun 
736*4882a593Smuzhiyun 	dbb_events = mb0_transfer.req.dbb_irqs | mb0_transfer.req.dbb_wakeups;
737*4882a593Smuzhiyun 	dbb_events |= (WAKEUP_BIT_AC_WAKE_ACK | WAKEUP_BIT_AC_SLEEP_ACK);
738*4882a593Smuzhiyun 
739*4882a593Smuzhiyun 	abb_events = mb0_transfer.req.abb_events;
740*4882a593Smuzhiyun 
741*4882a593Smuzhiyun 	if ((dbb_events == last_dbb_events) && (abb_events == last_abb_events))
742*4882a593Smuzhiyun 		return;
743*4882a593Smuzhiyun 
744*4882a593Smuzhiyun 	for (i = 0; i < 2; i++) {
745*4882a593Smuzhiyun 		while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
746*4882a593Smuzhiyun 			cpu_relax();
747*4882a593Smuzhiyun 		writel(dbb_events, (tcdm_base + PRCM_REQ_MB0_WAKEUP_8500));
748*4882a593Smuzhiyun 		writel(abb_events, (tcdm_base + PRCM_REQ_MB0_WAKEUP_4500));
749*4882a593Smuzhiyun 		writeb(header[i], (tcdm_base + PRCM_MBOX_HEADER_REQ_MB0));
750*4882a593Smuzhiyun 		writel(MBOX_BIT(0), PRCM_MBOX_CPU_SET);
751*4882a593Smuzhiyun 	}
752*4882a593Smuzhiyun 	last_dbb_events = dbb_events;
753*4882a593Smuzhiyun 	last_abb_events = abb_events;
754*4882a593Smuzhiyun }
755*4882a593Smuzhiyun 
db8500_prcmu_enable_wakeups(u32 wakeups)756*4882a593Smuzhiyun void db8500_prcmu_enable_wakeups(u32 wakeups)
757*4882a593Smuzhiyun {
758*4882a593Smuzhiyun 	unsigned long flags;
759*4882a593Smuzhiyun 	u32 bits;
760*4882a593Smuzhiyun 	int i;
761*4882a593Smuzhiyun 
762*4882a593Smuzhiyun 	BUG_ON(wakeups != (wakeups & VALID_WAKEUPS));
763*4882a593Smuzhiyun 
764*4882a593Smuzhiyun 	for (i = 0, bits = 0; i < NUM_PRCMU_WAKEUP_INDICES; i++) {
765*4882a593Smuzhiyun 		if (wakeups & BIT(i))
766*4882a593Smuzhiyun 			bits |= prcmu_wakeup_bit[i];
767*4882a593Smuzhiyun 	}
768*4882a593Smuzhiyun 
769*4882a593Smuzhiyun 	spin_lock_irqsave(&mb0_transfer.lock, flags);
770*4882a593Smuzhiyun 
771*4882a593Smuzhiyun 	mb0_transfer.req.dbb_wakeups = bits;
772*4882a593Smuzhiyun 	config_wakeups();
773*4882a593Smuzhiyun 
774*4882a593Smuzhiyun 	spin_unlock_irqrestore(&mb0_transfer.lock, flags);
775*4882a593Smuzhiyun }
776*4882a593Smuzhiyun 
db8500_prcmu_config_abb_event_readout(u32 abb_events)777*4882a593Smuzhiyun void db8500_prcmu_config_abb_event_readout(u32 abb_events)
778*4882a593Smuzhiyun {
779*4882a593Smuzhiyun 	unsigned long flags;
780*4882a593Smuzhiyun 
781*4882a593Smuzhiyun 	spin_lock_irqsave(&mb0_transfer.lock, flags);
782*4882a593Smuzhiyun 
783*4882a593Smuzhiyun 	mb0_transfer.req.abb_events = abb_events;
784*4882a593Smuzhiyun 	config_wakeups();
785*4882a593Smuzhiyun 
786*4882a593Smuzhiyun 	spin_unlock_irqrestore(&mb0_transfer.lock, flags);
787*4882a593Smuzhiyun }
788*4882a593Smuzhiyun 
db8500_prcmu_get_abb_event_buffer(void __iomem ** buf)789*4882a593Smuzhiyun void db8500_prcmu_get_abb_event_buffer(void __iomem **buf)
790*4882a593Smuzhiyun {
791*4882a593Smuzhiyun 	if (readb(tcdm_base + PRCM_ACK_MB0_READ_POINTER) & 1)
792*4882a593Smuzhiyun 		*buf = (tcdm_base + PRCM_ACK_MB0_WAKEUP_1_4500);
793*4882a593Smuzhiyun 	else
794*4882a593Smuzhiyun 		*buf = (tcdm_base + PRCM_ACK_MB0_WAKEUP_0_4500);
795*4882a593Smuzhiyun }
796*4882a593Smuzhiyun 
797*4882a593Smuzhiyun /**
798*4882a593Smuzhiyun  * db8500_prcmu_set_arm_opp - set the appropriate ARM OPP
799*4882a593Smuzhiyun  * @opp: The new ARM operating point to which transition is to be made
800*4882a593Smuzhiyun  * Returns: 0 on success, non-zero on failure
801*4882a593Smuzhiyun  *
802*4882a593Smuzhiyun  * This function sets the the operating point of the ARM.
803*4882a593Smuzhiyun  */
db8500_prcmu_set_arm_opp(u8 opp)804*4882a593Smuzhiyun int db8500_prcmu_set_arm_opp(u8 opp)
805*4882a593Smuzhiyun {
806*4882a593Smuzhiyun 	int r;
807*4882a593Smuzhiyun 
808*4882a593Smuzhiyun 	if (opp < ARM_NO_CHANGE || opp > ARM_EXTCLK)
809*4882a593Smuzhiyun 		return -EINVAL;
810*4882a593Smuzhiyun 
811*4882a593Smuzhiyun 	r = 0;
812*4882a593Smuzhiyun 
813*4882a593Smuzhiyun 	mutex_lock(&mb1_transfer.lock);
814*4882a593Smuzhiyun 
815*4882a593Smuzhiyun 	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
816*4882a593Smuzhiyun 		cpu_relax();
817*4882a593Smuzhiyun 
818*4882a593Smuzhiyun 	writeb(MB1H_ARM_APE_OPP, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
819*4882a593Smuzhiyun 	writeb(opp, (tcdm_base + PRCM_REQ_MB1_ARM_OPP));
820*4882a593Smuzhiyun 	writeb(APE_NO_CHANGE, (tcdm_base + PRCM_REQ_MB1_APE_OPP));
821*4882a593Smuzhiyun 
822*4882a593Smuzhiyun 	writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
823*4882a593Smuzhiyun 	wait_for_completion(&mb1_transfer.work);
824*4882a593Smuzhiyun 
825*4882a593Smuzhiyun 	if ((mb1_transfer.ack.header != MB1H_ARM_APE_OPP) ||
826*4882a593Smuzhiyun 		(mb1_transfer.ack.arm_opp != opp))
827*4882a593Smuzhiyun 		r = -EIO;
828*4882a593Smuzhiyun 
829*4882a593Smuzhiyun 	mutex_unlock(&mb1_transfer.lock);
830*4882a593Smuzhiyun 
831*4882a593Smuzhiyun 	return r;
832*4882a593Smuzhiyun }
833*4882a593Smuzhiyun 
834*4882a593Smuzhiyun /**
835*4882a593Smuzhiyun  * db8500_prcmu_get_arm_opp - get the current ARM OPP
836*4882a593Smuzhiyun  *
837*4882a593Smuzhiyun  * Returns: the current ARM OPP
838*4882a593Smuzhiyun  */
db8500_prcmu_get_arm_opp(void)839*4882a593Smuzhiyun int db8500_prcmu_get_arm_opp(void)
840*4882a593Smuzhiyun {
841*4882a593Smuzhiyun 	return readb(tcdm_base + PRCM_ACK_MB1_CURRENT_ARM_OPP);
842*4882a593Smuzhiyun }
843*4882a593Smuzhiyun 
844*4882a593Smuzhiyun /**
845*4882a593Smuzhiyun  * db8500_prcmu_get_ddr_opp - get the current DDR OPP
846*4882a593Smuzhiyun  *
847*4882a593Smuzhiyun  * Returns: the current DDR OPP
848*4882a593Smuzhiyun  */
db8500_prcmu_get_ddr_opp(void)849*4882a593Smuzhiyun int db8500_prcmu_get_ddr_opp(void)
850*4882a593Smuzhiyun {
851*4882a593Smuzhiyun 	return readb(PRCM_DDR_SUBSYS_APE_MINBW);
852*4882a593Smuzhiyun }
853*4882a593Smuzhiyun 
854*4882a593Smuzhiyun /* Divide the frequency of certain clocks by 2 for APE_50_PARTLY_25_OPP. */
request_even_slower_clocks(bool enable)855*4882a593Smuzhiyun static void request_even_slower_clocks(bool enable)
856*4882a593Smuzhiyun {
857*4882a593Smuzhiyun 	u32 clock_reg[] = {
858*4882a593Smuzhiyun 		PRCM_ACLK_MGT,
859*4882a593Smuzhiyun 		PRCM_DMACLK_MGT
860*4882a593Smuzhiyun 	};
861*4882a593Smuzhiyun 	unsigned long flags;
862*4882a593Smuzhiyun 	unsigned int i;
863*4882a593Smuzhiyun 
864*4882a593Smuzhiyun 	spin_lock_irqsave(&clk_mgt_lock, flags);
865*4882a593Smuzhiyun 
866*4882a593Smuzhiyun 	/* Grab the HW semaphore. */
867*4882a593Smuzhiyun 	while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
868*4882a593Smuzhiyun 		cpu_relax();
869*4882a593Smuzhiyun 
870*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(clock_reg); i++) {
871*4882a593Smuzhiyun 		u32 val;
872*4882a593Smuzhiyun 		u32 div;
873*4882a593Smuzhiyun 
874*4882a593Smuzhiyun 		val = readl(prcmu_base + clock_reg[i]);
875*4882a593Smuzhiyun 		div = (val & PRCM_CLK_MGT_CLKPLLDIV_MASK);
876*4882a593Smuzhiyun 		if (enable) {
877*4882a593Smuzhiyun 			if ((div <= 1) || (div > 15)) {
878*4882a593Smuzhiyun 				pr_err("prcmu: Bad clock divider %d in %s\n",
879*4882a593Smuzhiyun 					div, __func__);
880*4882a593Smuzhiyun 				goto unlock_and_return;
881*4882a593Smuzhiyun 			}
882*4882a593Smuzhiyun 			div <<= 1;
883*4882a593Smuzhiyun 		} else {
884*4882a593Smuzhiyun 			if (div <= 2)
885*4882a593Smuzhiyun 				goto unlock_and_return;
886*4882a593Smuzhiyun 			div >>= 1;
887*4882a593Smuzhiyun 		}
888*4882a593Smuzhiyun 		val = ((val & ~PRCM_CLK_MGT_CLKPLLDIV_MASK) |
889*4882a593Smuzhiyun 			(div & PRCM_CLK_MGT_CLKPLLDIV_MASK));
890*4882a593Smuzhiyun 		writel(val, prcmu_base + clock_reg[i]);
891*4882a593Smuzhiyun 	}
892*4882a593Smuzhiyun 
893*4882a593Smuzhiyun unlock_and_return:
894*4882a593Smuzhiyun 	/* Release the HW semaphore. */
895*4882a593Smuzhiyun 	writel(0, PRCM_SEM);
896*4882a593Smuzhiyun 
897*4882a593Smuzhiyun 	spin_unlock_irqrestore(&clk_mgt_lock, flags);
898*4882a593Smuzhiyun }
899*4882a593Smuzhiyun 
900*4882a593Smuzhiyun /**
901*4882a593Smuzhiyun  * db8500_set_ape_opp - set the appropriate APE OPP
902*4882a593Smuzhiyun  * @opp: The new APE operating point to which transition is to be made
903*4882a593Smuzhiyun  * Returns: 0 on success, non-zero on failure
904*4882a593Smuzhiyun  *
905*4882a593Smuzhiyun  * This function sets the operating point of the APE.
906*4882a593Smuzhiyun  */
db8500_prcmu_set_ape_opp(u8 opp)907*4882a593Smuzhiyun int db8500_prcmu_set_ape_opp(u8 opp)
908*4882a593Smuzhiyun {
909*4882a593Smuzhiyun 	int r = 0;
910*4882a593Smuzhiyun 
911*4882a593Smuzhiyun 	if (opp == mb1_transfer.ape_opp)
912*4882a593Smuzhiyun 		return 0;
913*4882a593Smuzhiyun 
914*4882a593Smuzhiyun 	mutex_lock(&mb1_transfer.lock);
915*4882a593Smuzhiyun 
916*4882a593Smuzhiyun 	if (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP)
917*4882a593Smuzhiyun 		request_even_slower_clocks(false);
918*4882a593Smuzhiyun 
919*4882a593Smuzhiyun 	if ((opp != APE_100_OPP) && (mb1_transfer.ape_opp != APE_100_OPP))
920*4882a593Smuzhiyun 		goto skip_message;
921*4882a593Smuzhiyun 
922*4882a593Smuzhiyun 	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
923*4882a593Smuzhiyun 		cpu_relax();
924*4882a593Smuzhiyun 
925*4882a593Smuzhiyun 	writeb(MB1H_ARM_APE_OPP, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
926*4882a593Smuzhiyun 	writeb(ARM_NO_CHANGE, (tcdm_base + PRCM_REQ_MB1_ARM_OPP));
927*4882a593Smuzhiyun 	writeb(((opp == APE_50_PARTLY_25_OPP) ? APE_50_OPP : opp),
928*4882a593Smuzhiyun 		(tcdm_base + PRCM_REQ_MB1_APE_OPP));
929*4882a593Smuzhiyun 
930*4882a593Smuzhiyun 	writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
931*4882a593Smuzhiyun 	wait_for_completion(&mb1_transfer.work);
932*4882a593Smuzhiyun 
933*4882a593Smuzhiyun 	if ((mb1_transfer.ack.header != MB1H_ARM_APE_OPP) ||
934*4882a593Smuzhiyun 		(mb1_transfer.ack.ape_opp != opp))
935*4882a593Smuzhiyun 		r = -EIO;
936*4882a593Smuzhiyun 
937*4882a593Smuzhiyun skip_message:
938*4882a593Smuzhiyun 	if ((!r && (opp == APE_50_PARTLY_25_OPP)) ||
939*4882a593Smuzhiyun 		(r && (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP)))
940*4882a593Smuzhiyun 		request_even_slower_clocks(true);
941*4882a593Smuzhiyun 	if (!r)
942*4882a593Smuzhiyun 		mb1_transfer.ape_opp = opp;
943*4882a593Smuzhiyun 
944*4882a593Smuzhiyun 	mutex_unlock(&mb1_transfer.lock);
945*4882a593Smuzhiyun 
946*4882a593Smuzhiyun 	return r;
947*4882a593Smuzhiyun }
948*4882a593Smuzhiyun 
949*4882a593Smuzhiyun /**
950*4882a593Smuzhiyun  * db8500_prcmu_get_ape_opp - get the current APE OPP
951*4882a593Smuzhiyun  *
952*4882a593Smuzhiyun  * Returns: the current APE OPP
953*4882a593Smuzhiyun  */
db8500_prcmu_get_ape_opp(void)954*4882a593Smuzhiyun int db8500_prcmu_get_ape_opp(void)
955*4882a593Smuzhiyun {
956*4882a593Smuzhiyun 	return readb(tcdm_base + PRCM_ACK_MB1_CURRENT_APE_OPP);
957*4882a593Smuzhiyun }
958*4882a593Smuzhiyun 
959*4882a593Smuzhiyun /**
960*4882a593Smuzhiyun  * db8500_prcmu_request_ape_opp_100_voltage - Request APE OPP 100% voltage
961*4882a593Smuzhiyun  * @enable: true to request the higher voltage, false to drop a request.
962*4882a593Smuzhiyun  *
963*4882a593Smuzhiyun  * Calls to this function to enable and disable requests must be balanced.
964*4882a593Smuzhiyun  */
db8500_prcmu_request_ape_opp_100_voltage(bool enable)965*4882a593Smuzhiyun int db8500_prcmu_request_ape_opp_100_voltage(bool enable)
966*4882a593Smuzhiyun {
967*4882a593Smuzhiyun 	int r = 0;
968*4882a593Smuzhiyun 	u8 header;
969*4882a593Smuzhiyun 	static unsigned int requests;
970*4882a593Smuzhiyun 
971*4882a593Smuzhiyun 	mutex_lock(&mb1_transfer.lock);
972*4882a593Smuzhiyun 
973*4882a593Smuzhiyun 	if (enable) {
974*4882a593Smuzhiyun 		if (0 != requests++)
975*4882a593Smuzhiyun 			goto unlock_and_return;
976*4882a593Smuzhiyun 		header = MB1H_REQUEST_APE_OPP_100_VOLT;
977*4882a593Smuzhiyun 	} else {
978*4882a593Smuzhiyun 		if (requests == 0) {
979*4882a593Smuzhiyun 			r = -EIO;
980*4882a593Smuzhiyun 			goto unlock_and_return;
981*4882a593Smuzhiyun 		} else if (1 != requests--) {
982*4882a593Smuzhiyun 			goto unlock_and_return;
983*4882a593Smuzhiyun 		}
984*4882a593Smuzhiyun 		header = MB1H_RELEASE_APE_OPP_100_VOLT;
985*4882a593Smuzhiyun 	}
986*4882a593Smuzhiyun 
987*4882a593Smuzhiyun 	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
988*4882a593Smuzhiyun 		cpu_relax();
989*4882a593Smuzhiyun 
990*4882a593Smuzhiyun 	writeb(header, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
991*4882a593Smuzhiyun 
992*4882a593Smuzhiyun 	writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
993*4882a593Smuzhiyun 	wait_for_completion(&mb1_transfer.work);
994*4882a593Smuzhiyun 
995*4882a593Smuzhiyun 	if ((mb1_transfer.ack.header != header) ||
996*4882a593Smuzhiyun 		((mb1_transfer.ack.ape_voltage_status & BIT(0)) != 0))
997*4882a593Smuzhiyun 		r = -EIO;
998*4882a593Smuzhiyun 
999*4882a593Smuzhiyun unlock_and_return:
1000*4882a593Smuzhiyun 	mutex_unlock(&mb1_transfer.lock);
1001*4882a593Smuzhiyun 
1002*4882a593Smuzhiyun 	return r;
1003*4882a593Smuzhiyun }
1004*4882a593Smuzhiyun 
1005*4882a593Smuzhiyun /**
1006*4882a593Smuzhiyun  * prcmu_release_usb_wakeup_state - release the state required by a USB wakeup
1007*4882a593Smuzhiyun  *
1008*4882a593Smuzhiyun  * This function releases the power state requirements of a USB wakeup.
1009*4882a593Smuzhiyun  */
prcmu_release_usb_wakeup_state(void)1010*4882a593Smuzhiyun int prcmu_release_usb_wakeup_state(void)
1011*4882a593Smuzhiyun {
1012*4882a593Smuzhiyun 	int r = 0;
1013*4882a593Smuzhiyun 
1014*4882a593Smuzhiyun 	mutex_lock(&mb1_transfer.lock);
1015*4882a593Smuzhiyun 
1016*4882a593Smuzhiyun 	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
1017*4882a593Smuzhiyun 		cpu_relax();
1018*4882a593Smuzhiyun 
1019*4882a593Smuzhiyun 	writeb(MB1H_RELEASE_USB_WAKEUP,
1020*4882a593Smuzhiyun 		(tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
1021*4882a593Smuzhiyun 
1022*4882a593Smuzhiyun 	writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
1023*4882a593Smuzhiyun 	wait_for_completion(&mb1_transfer.work);
1024*4882a593Smuzhiyun 
1025*4882a593Smuzhiyun 	if ((mb1_transfer.ack.header != MB1H_RELEASE_USB_WAKEUP) ||
1026*4882a593Smuzhiyun 		((mb1_transfer.ack.ape_voltage_status & BIT(0)) != 0))
1027*4882a593Smuzhiyun 		r = -EIO;
1028*4882a593Smuzhiyun 
1029*4882a593Smuzhiyun 	mutex_unlock(&mb1_transfer.lock);
1030*4882a593Smuzhiyun 
1031*4882a593Smuzhiyun 	return r;
1032*4882a593Smuzhiyun }
1033*4882a593Smuzhiyun 
request_pll(u8 clock,bool enable)1034*4882a593Smuzhiyun static int request_pll(u8 clock, bool enable)
1035*4882a593Smuzhiyun {
1036*4882a593Smuzhiyun 	int r = 0;
1037*4882a593Smuzhiyun 
1038*4882a593Smuzhiyun 	if (clock == PRCMU_PLLSOC0)
1039*4882a593Smuzhiyun 		clock = (enable ? PLL_SOC0_ON : PLL_SOC0_OFF);
1040*4882a593Smuzhiyun 	else if (clock == PRCMU_PLLSOC1)
1041*4882a593Smuzhiyun 		clock = (enable ? PLL_SOC1_ON : PLL_SOC1_OFF);
1042*4882a593Smuzhiyun 	else
1043*4882a593Smuzhiyun 		return -EINVAL;
1044*4882a593Smuzhiyun 
1045*4882a593Smuzhiyun 	mutex_lock(&mb1_transfer.lock);
1046*4882a593Smuzhiyun 
1047*4882a593Smuzhiyun 	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
1048*4882a593Smuzhiyun 		cpu_relax();
1049*4882a593Smuzhiyun 
1050*4882a593Smuzhiyun 	writeb(MB1H_PLL_ON_OFF, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
1051*4882a593Smuzhiyun 	writeb(clock, (tcdm_base + PRCM_REQ_MB1_PLL_ON_OFF));
1052*4882a593Smuzhiyun 
1053*4882a593Smuzhiyun 	writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
1054*4882a593Smuzhiyun 	wait_for_completion(&mb1_transfer.work);
1055*4882a593Smuzhiyun 
1056*4882a593Smuzhiyun 	if (mb1_transfer.ack.header != MB1H_PLL_ON_OFF)
1057*4882a593Smuzhiyun 		r = -EIO;
1058*4882a593Smuzhiyun 
1059*4882a593Smuzhiyun 	mutex_unlock(&mb1_transfer.lock);
1060*4882a593Smuzhiyun 
1061*4882a593Smuzhiyun 	return r;
1062*4882a593Smuzhiyun }
1063*4882a593Smuzhiyun 
1064*4882a593Smuzhiyun /**
1065*4882a593Smuzhiyun  * db8500_prcmu_set_epod - set the state of a EPOD (power domain)
1066*4882a593Smuzhiyun  * @epod_id: The EPOD to set
1067*4882a593Smuzhiyun  * @epod_state: The new EPOD state
1068*4882a593Smuzhiyun  *
1069*4882a593Smuzhiyun  * This function sets the state of a EPOD (power domain). It may not be called
1070*4882a593Smuzhiyun  * from interrupt context.
1071*4882a593Smuzhiyun  */
db8500_prcmu_set_epod(u16 epod_id,u8 epod_state)1072*4882a593Smuzhiyun int db8500_prcmu_set_epod(u16 epod_id, u8 epod_state)
1073*4882a593Smuzhiyun {
1074*4882a593Smuzhiyun 	int r = 0;
1075*4882a593Smuzhiyun 	bool ram_retention = false;
1076*4882a593Smuzhiyun 	int i;
1077*4882a593Smuzhiyun 
1078*4882a593Smuzhiyun 	/* check argument */
1079*4882a593Smuzhiyun 	BUG_ON(epod_id >= NUM_EPOD_ID);
1080*4882a593Smuzhiyun 
1081*4882a593Smuzhiyun 	/* set flag if retention is possible */
1082*4882a593Smuzhiyun 	switch (epod_id) {
1083*4882a593Smuzhiyun 	case EPOD_ID_SVAMMDSP:
1084*4882a593Smuzhiyun 	case EPOD_ID_SIAMMDSP:
1085*4882a593Smuzhiyun 	case EPOD_ID_ESRAM12:
1086*4882a593Smuzhiyun 	case EPOD_ID_ESRAM34:
1087*4882a593Smuzhiyun 		ram_retention = true;
1088*4882a593Smuzhiyun 		break;
1089*4882a593Smuzhiyun 	}
1090*4882a593Smuzhiyun 
1091*4882a593Smuzhiyun 	/* check argument */
1092*4882a593Smuzhiyun 	BUG_ON(epod_state > EPOD_STATE_ON);
1093*4882a593Smuzhiyun 	BUG_ON(epod_state == EPOD_STATE_RAMRET && !ram_retention);
1094*4882a593Smuzhiyun 
1095*4882a593Smuzhiyun 	/* get lock */
1096*4882a593Smuzhiyun 	mutex_lock(&mb2_transfer.lock);
1097*4882a593Smuzhiyun 
1098*4882a593Smuzhiyun 	/* wait for mailbox */
1099*4882a593Smuzhiyun 	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(2))
1100*4882a593Smuzhiyun 		cpu_relax();
1101*4882a593Smuzhiyun 
1102*4882a593Smuzhiyun 	/* fill in mailbox */
1103*4882a593Smuzhiyun 	for (i = 0; i < NUM_EPOD_ID; i++)
1104*4882a593Smuzhiyun 		writeb(EPOD_STATE_NO_CHANGE, (tcdm_base + PRCM_REQ_MB2 + i));
1105*4882a593Smuzhiyun 	writeb(epod_state, (tcdm_base + PRCM_REQ_MB2 + epod_id));
1106*4882a593Smuzhiyun 
1107*4882a593Smuzhiyun 	writeb(MB2H_DPS, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB2));
1108*4882a593Smuzhiyun 
1109*4882a593Smuzhiyun 	writel(MBOX_BIT(2), PRCM_MBOX_CPU_SET);
1110*4882a593Smuzhiyun 
1111*4882a593Smuzhiyun 	/*
1112*4882a593Smuzhiyun 	 * The current firmware version does not handle errors correctly,
1113*4882a593Smuzhiyun 	 * and we cannot recover if there is an error.
1114*4882a593Smuzhiyun 	 * This is expected to change when the firmware is updated.
1115*4882a593Smuzhiyun 	 */
1116*4882a593Smuzhiyun 	if (!wait_for_completion_timeout(&mb2_transfer.work,
1117*4882a593Smuzhiyun 			msecs_to_jiffies(20000))) {
1118*4882a593Smuzhiyun 		pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
1119*4882a593Smuzhiyun 			__func__);
1120*4882a593Smuzhiyun 		r = -EIO;
1121*4882a593Smuzhiyun 		goto unlock_and_return;
1122*4882a593Smuzhiyun 	}
1123*4882a593Smuzhiyun 
1124*4882a593Smuzhiyun 	if (mb2_transfer.ack.status != HWACC_PWR_ST_OK)
1125*4882a593Smuzhiyun 		r = -EIO;
1126*4882a593Smuzhiyun 
1127*4882a593Smuzhiyun unlock_and_return:
1128*4882a593Smuzhiyun 	mutex_unlock(&mb2_transfer.lock);
1129*4882a593Smuzhiyun 	return r;
1130*4882a593Smuzhiyun }
1131*4882a593Smuzhiyun 
1132*4882a593Smuzhiyun /**
1133*4882a593Smuzhiyun  * prcmu_configure_auto_pm - Configure autonomous power management.
1134*4882a593Smuzhiyun  * @sleep: Configuration for ApSleep.
1135*4882a593Smuzhiyun  * @idle:  Configuration for ApIdle.
1136*4882a593Smuzhiyun  */
prcmu_configure_auto_pm(struct prcmu_auto_pm_config * sleep,struct prcmu_auto_pm_config * idle)1137*4882a593Smuzhiyun void prcmu_configure_auto_pm(struct prcmu_auto_pm_config *sleep,
1138*4882a593Smuzhiyun 	struct prcmu_auto_pm_config *idle)
1139*4882a593Smuzhiyun {
1140*4882a593Smuzhiyun 	u32 sleep_cfg;
1141*4882a593Smuzhiyun 	u32 idle_cfg;
1142*4882a593Smuzhiyun 	unsigned long flags;
1143*4882a593Smuzhiyun 
1144*4882a593Smuzhiyun 	BUG_ON((sleep == NULL) || (idle == NULL));
1145*4882a593Smuzhiyun 
1146*4882a593Smuzhiyun 	sleep_cfg = (sleep->sva_auto_pm_enable & 0xF);
1147*4882a593Smuzhiyun 	sleep_cfg = ((sleep_cfg << 4) | (sleep->sia_auto_pm_enable & 0xF));
1148*4882a593Smuzhiyun 	sleep_cfg = ((sleep_cfg << 8) | (sleep->sva_power_on & 0xFF));
1149*4882a593Smuzhiyun 	sleep_cfg = ((sleep_cfg << 8) | (sleep->sia_power_on & 0xFF));
1150*4882a593Smuzhiyun 	sleep_cfg = ((sleep_cfg << 4) | (sleep->sva_policy & 0xF));
1151*4882a593Smuzhiyun 	sleep_cfg = ((sleep_cfg << 4) | (sleep->sia_policy & 0xF));
1152*4882a593Smuzhiyun 
1153*4882a593Smuzhiyun 	idle_cfg = (idle->sva_auto_pm_enable & 0xF);
1154*4882a593Smuzhiyun 	idle_cfg = ((idle_cfg << 4) | (idle->sia_auto_pm_enable & 0xF));
1155*4882a593Smuzhiyun 	idle_cfg = ((idle_cfg << 8) | (idle->sva_power_on & 0xFF));
1156*4882a593Smuzhiyun 	idle_cfg = ((idle_cfg << 8) | (idle->sia_power_on & 0xFF));
1157*4882a593Smuzhiyun 	idle_cfg = ((idle_cfg << 4) | (idle->sva_policy & 0xF));
1158*4882a593Smuzhiyun 	idle_cfg = ((idle_cfg << 4) | (idle->sia_policy & 0xF));
1159*4882a593Smuzhiyun 
1160*4882a593Smuzhiyun 	spin_lock_irqsave(&mb2_transfer.auto_pm_lock, flags);
1161*4882a593Smuzhiyun 
1162*4882a593Smuzhiyun 	/*
1163*4882a593Smuzhiyun 	 * The autonomous power management configuration is done through
1164*4882a593Smuzhiyun 	 * fields in mailbox 2, but these fields are only used as shared
1165*4882a593Smuzhiyun 	 * variables - i.e. there is no need to send a message.
1166*4882a593Smuzhiyun 	 */
1167*4882a593Smuzhiyun 	writel(sleep_cfg, (tcdm_base + PRCM_REQ_MB2_AUTO_PM_SLEEP));
1168*4882a593Smuzhiyun 	writel(idle_cfg, (tcdm_base + PRCM_REQ_MB2_AUTO_PM_IDLE));
1169*4882a593Smuzhiyun 
1170*4882a593Smuzhiyun 	mb2_transfer.auto_pm_enabled =
1171*4882a593Smuzhiyun 		((sleep->sva_auto_pm_enable == PRCMU_AUTO_PM_ON) ||
1172*4882a593Smuzhiyun 		 (sleep->sia_auto_pm_enable == PRCMU_AUTO_PM_ON) ||
1173*4882a593Smuzhiyun 		 (idle->sva_auto_pm_enable == PRCMU_AUTO_PM_ON) ||
1174*4882a593Smuzhiyun 		 (idle->sia_auto_pm_enable == PRCMU_AUTO_PM_ON));
1175*4882a593Smuzhiyun 
1176*4882a593Smuzhiyun 	spin_unlock_irqrestore(&mb2_transfer.auto_pm_lock, flags);
1177*4882a593Smuzhiyun }
1178*4882a593Smuzhiyun EXPORT_SYMBOL(prcmu_configure_auto_pm);
1179*4882a593Smuzhiyun 
prcmu_is_auto_pm_enabled(void)1180*4882a593Smuzhiyun bool prcmu_is_auto_pm_enabled(void)
1181*4882a593Smuzhiyun {
1182*4882a593Smuzhiyun 	return mb2_transfer.auto_pm_enabled;
1183*4882a593Smuzhiyun }
1184*4882a593Smuzhiyun 
request_sysclk(bool enable)1185*4882a593Smuzhiyun static int request_sysclk(bool enable)
1186*4882a593Smuzhiyun {
1187*4882a593Smuzhiyun 	int r;
1188*4882a593Smuzhiyun 	unsigned long flags;
1189*4882a593Smuzhiyun 
1190*4882a593Smuzhiyun 	r = 0;
1191*4882a593Smuzhiyun 
1192*4882a593Smuzhiyun 	mutex_lock(&mb3_transfer.sysclk_lock);
1193*4882a593Smuzhiyun 
1194*4882a593Smuzhiyun 	spin_lock_irqsave(&mb3_transfer.lock, flags);
1195*4882a593Smuzhiyun 
1196*4882a593Smuzhiyun 	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(3))
1197*4882a593Smuzhiyun 		cpu_relax();
1198*4882a593Smuzhiyun 
1199*4882a593Smuzhiyun 	writeb((enable ? ON : OFF), (tcdm_base + PRCM_REQ_MB3_SYSCLK_MGT));
1200*4882a593Smuzhiyun 
1201*4882a593Smuzhiyun 	writeb(MB3H_SYSCLK, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB3));
1202*4882a593Smuzhiyun 	writel(MBOX_BIT(3), PRCM_MBOX_CPU_SET);
1203*4882a593Smuzhiyun 
1204*4882a593Smuzhiyun 	spin_unlock_irqrestore(&mb3_transfer.lock, flags);
1205*4882a593Smuzhiyun 
1206*4882a593Smuzhiyun 	/*
1207*4882a593Smuzhiyun 	 * The firmware only sends an ACK if we want to enable the
1208*4882a593Smuzhiyun 	 * SysClk, and it succeeds.
1209*4882a593Smuzhiyun 	 */
1210*4882a593Smuzhiyun 	if (enable && !wait_for_completion_timeout(&mb3_transfer.sysclk_work,
1211*4882a593Smuzhiyun 			msecs_to_jiffies(20000))) {
1212*4882a593Smuzhiyun 		pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
1213*4882a593Smuzhiyun 			__func__);
1214*4882a593Smuzhiyun 		r = -EIO;
1215*4882a593Smuzhiyun 	}
1216*4882a593Smuzhiyun 
1217*4882a593Smuzhiyun 	mutex_unlock(&mb3_transfer.sysclk_lock);
1218*4882a593Smuzhiyun 
1219*4882a593Smuzhiyun 	return r;
1220*4882a593Smuzhiyun }
1221*4882a593Smuzhiyun 
request_timclk(bool enable)1222*4882a593Smuzhiyun static int request_timclk(bool enable)
1223*4882a593Smuzhiyun {
1224*4882a593Smuzhiyun 	u32 val;
1225*4882a593Smuzhiyun 
1226*4882a593Smuzhiyun 	/*
1227*4882a593Smuzhiyun 	 * On the U8420_CLKSEL firmware, the ULP (Ultra Low Power)
1228*4882a593Smuzhiyun 	 * PLL is disabled so we cannot use doze mode, this will
1229*4882a593Smuzhiyun 	 * stop the clock on this firmware.
1230*4882a593Smuzhiyun 	 */
1231*4882a593Smuzhiyun 	if (prcmu_is_ulppll_disabled())
1232*4882a593Smuzhiyun 		val = 0;
1233*4882a593Smuzhiyun 	else
1234*4882a593Smuzhiyun 		val = (PRCM_TCR_DOZE_MODE | PRCM_TCR_TENSEL_MASK);
1235*4882a593Smuzhiyun 
1236*4882a593Smuzhiyun 	if (!enable)
1237*4882a593Smuzhiyun 		val |= PRCM_TCR_STOP_TIMERS |
1238*4882a593Smuzhiyun 			PRCM_TCR_DOZE_MODE |
1239*4882a593Smuzhiyun 			PRCM_TCR_TENSEL_MASK;
1240*4882a593Smuzhiyun 
1241*4882a593Smuzhiyun 	writel(val, PRCM_TCR);
1242*4882a593Smuzhiyun 
1243*4882a593Smuzhiyun 	return 0;
1244*4882a593Smuzhiyun }
1245*4882a593Smuzhiyun 
request_clock(u8 clock,bool enable)1246*4882a593Smuzhiyun static int request_clock(u8 clock, bool enable)
1247*4882a593Smuzhiyun {
1248*4882a593Smuzhiyun 	u32 val;
1249*4882a593Smuzhiyun 	unsigned long flags;
1250*4882a593Smuzhiyun 
1251*4882a593Smuzhiyun 	spin_lock_irqsave(&clk_mgt_lock, flags);
1252*4882a593Smuzhiyun 
1253*4882a593Smuzhiyun 	/* Grab the HW semaphore. */
1254*4882a593Smuzhiyun 	while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
1255*4882a593Smuzhiyun 		cpu_relax();
1256*4882a593Smuzhiyun 
1257*4882a593Smuzhiyun 	val = readl(prcmu_base + clk_mgt[clock].offset);
1258*4882a593Smuzhiyun 	if (enable) {
1259*4882a593Smuzhiyun 		val |= (PRCM_CLK_MGT_CLKEN | clk_mgt[clock].pllsw);
1260*4882a593Smuzhiyun 	} else {
1261*4882a593Smuzhiyun 		clk_mgt[clock].pllsw = (val & PRCM_CLK_MGT_CLKPLLSW_MASK);
1262*4882a593Smuzhiyun 		val &= ~(PRCM_CLK_MGT_CLKEN | PRCM_CLK_MGT_CLKPLLSW_MASK);
1263*4882a593Smuzhiyun 	}
1264*4882a593Smuzhiyun 	writel(val, prcmu_base + clk_mgt[clock].offset);
1265*4882a593Smuzhiyun 
1266*4882a593Smuzhiyun 	/* Release the HW semaphore. */
1267*4882a593Smuzhiyun 	writel(0, PRCM_SEM);
1268*4882a593Smuzhiyun 
1269*4882a593Smuzhiyun 	spin_unlock_irqrestore(&clk_mgt_lock, flags);
1270*4882a593Smuzhiyun 
1271*4882a593Smuzhiyun 	return 0;
1272*4882a593Smuzhiyun }
1273*4882a593Smuzhiyun 
request_sga_clock(u8 clock,bool enable)1274*4882a593Smuzhiyun static int request_sga_clock(u8 clock, bool enable)
1275*4882a593Smuzhiyun {
1276*4882a593Smuzhiyun 	u32 val;
1277*4882a593Smuzhiyun 	int ret;
1278*4882a593Smuzhiyun 
1279*4882a593Smuzhiyun 	if (enable) {
1280*4882a593Smuzhiyun 		val = readl(PRCM_CGATING_BYPASS);
1281*4882a593Smuzhiyun 		writel(val | PRCM_CGATING_BYPASS_ICN2, PRCM_CGATING_BYPASS);
1282*4882a593Smuzhiyun 	}
1283*4882a593Smuzhiyun 
1284*4882a593Smuzhiyun 	ret = request_clock(clock, enable);
1285*4882a593Smuzhiyun 
1286*4882a593Smuzhiyun 	if (!ret && !enable) {
1287*4882a593Smuzhiyun 		val = readl(PRCM_CGATING_BYPASS);
1288*4882a593Smuzhiyun 		writel(val & ~PRCM_CGATING_BYPASS_ICN2, PRCM_CGATING_BYPASS);
1289*4882a593Smuzhiyun 	}
1290*4882a593Smuzhiyun 
1291*4882a593Smuzhiyun 	return ret;
1292*4882a593Smuzhiyun }
1293*4882a593Smuzhiyun 
plldsi_locked(void)1294*4882a593Smuzhiyun static inline bool plldsi_locked(void)
1295*4882a593Smuzhiyun {
1296*4882a593Smuzhiyun 	return (readl(PRCM_PLLDSI_LOCKP) &
1297*4882a593Smuzhiyun 		(PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP10 |
1298*4882a593Smuzhiyun 		 PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP3)) ==
1299*4882a593Smuzhiyun 		(PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP10 |
1300*4882a593Smuzhiyun 		 PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP3);
1301*4882a593Smuzhiyun }
1302*4882a593Smuzhiyun 
request_plldsi(bool enable)1303*4882a593Smuzhiyun static int request_plldsi(bool enable)
1304*4882a593Smuzhiyun {
1305*4882a593Smuzhiyun 	int r = 0;
1306*4882a593Smuzhiyun 	u32 val;
1307*4882a593Smuzhiyun 
1308*4882a593Smuzhiyun 	writel((PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMP |
1309*4882a593Smuzhiyun 		PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMPI), (enable ?
1310*4882a593Smuzhiyun 		PRCM_MMIP_LS_CLAMP_CLR : PRCM_MMIP_LS_CLAMP_SET));
1311*4882a593Smuzhiyun 
1312*4882a593Smuzhiyun 	val = readl(PRCM_PLLDSI_ENABLE);
1313*4882a593Smuzhiyun 	if (enable)
1314*4882a593Smuzhiyun 		val |= PRCM_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE;
1315*4882a593Smuzhiyun 	else
1316*4882a593Smuzhiyun 		val &= ~PRCM_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE;
1317*4882a593Smuzhiyun 	writel(val, PRCM_PLLDSI_ENABLE);
1318*4882a593Smuzhiyun 
1319*4882a593Smuzhiyun 	if (enable) {
1320*4882a593Smuzhiyun 		unsigned int i;
1321*4882a593Smuzhiyun 		bool locked = plldsi_locked();
1322*4882a593Smuzhiyun 
1323*4882a593Smuzhiyun 		for (i = 10; !locked && (i > 0); --i) {
1324*4882a593Smuzhiyun 			udelay(100);
1325*4882a593Smuzhiyun 			locked = plldsi_locked();
1326*4882a593Smuzhiyun 		}
1327*4882a593Smuzhiyun 		if (locked) {
1328*4882a593Smuzhiyun 			writel(PRCM_APE_RESETN_DSIPLL_RESETN,
1329*4882a593Smuzhiyun 				PRCM_APE_RESETN_SET);
1330*4882a593Smuzhiyun 		} else {
1331*4882a593Smuzhiyun 			writel((PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMP |
1332*4882a593Smuzhiyun 				PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMPI),
1333*4882a593Smuzhiyun 				PRCM_MMIP_LS_CLAMP_SET);
1334*4882a593Smuzhiyun 			val &= ~PRCM_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE;
1335*4882a593Smuzhiyun 			writel(val, PRCM_PLLDSI_ENABLE);
1336*4882a593Smuzhiyun 			r = -EAGAIN;
1337*4882a593Smuzhiyun 		}
1338*4882a593Smuzhiyun 	} else {
1339*4882a593Smuzhiyun 		writel(PRCM_APE_RESETN_DSIPLL_RESETN, PRCM_APE_RESETN_CLR);
1340*4882a593Smuzhiyun 	}
1341*4882a593Smuzhiyun 	return r;
1342*4882a593Smuzhiyun }
1343*4882a593Smuzhiyun 
request_dsiclk(u8 n,bool enable)1344*4882a593Smuzhiyun static int request_dsiclk(u8 n, bool enable)
1345*4882a593Smuzhiyun {
1346*4882a593Smuzhiyun 	u32 val;
1347*4882a593Smuzhiyun 
1348*4882a593Smuzhiyun 	val = readl(PRCM_DSI_PLLOUT_SEL);
1349*4882a593Smuzhiyun 	val &= ~dsiclk[n].divsel_mask;
1350*4882a593Smuzhiyun 	val |= ((enable ? dsiclk[n].divsel : PRCM_DSI_PLLOUT_SEL_OFF) <<
1351*4882a593Smuzhiyun 		dsiclk[n].divsel_shift);
1352*4882a593Smuzhiyun 	writel(val, PRCM_DSI_PLLOUT_SEL);
1353*4882a593Smuzhiyun 	return 0;
1354*4882a593Smuzhiyun }
1355*4882a593Smuzhiyun 
request_dsiescclk(u8 n,bool enable)1356*4882a593Smuzhiyun static int request_dsiescclk(u8 n, bool enable)
1357*4882a593Smuzhiyun {
1358*4882a593Smuzhiyun 	u32 val;
1359*4882a593Smuzhiyun 
1360*4882a593Smuzhiyun 	val = readl(PRCM_DSITVCLK_DIV);
1361*4882a593Smuzhiyun 	enable ? (val |= dsiescclk[n].en) : (val &= ~dsiescclk[n].en);
1362*4882a593Smuzhiyun 	writel(val, PRCM_DSITVCLK_DIV);
1363*4882a593Smuzhiyun 	return 0;
1364*4882a593Smuzhiyun }
1365*4882a593Smuzhiyun 
1366*4882a593Smuzhiyun /**
1367*4882a593Smuzhiyun  * db8500_prcmu_request_clock() - Request for a clock to be enabled or disabled.
1368*4882a593Smuzhiyun  * @clock:      The clock for which the request is made.
1369*4882a593Smuzhiyun  * @enable:     Whether the clock should be enabled (true) or disabled (false).
1370*4882a593Smuzhiyun  *
1371*4882a593Smuzhiyun  * This function should only be used by the clock implementation.
1372*4882a593Smuzhiyun  * Do not use it from any other place!
1373*4882a593Smuzhiyun  */
db8500_prcmu_request_clock(u8 clock,bool enable)1374*4882a593Smuzhiyun int db8500_prcmu_request_clock(u8 clock, bool enable)
1375*4882a593Smuzhiyun {
1376*4882a593Smuzhiyun 	if (clock == PRCMU_SGACLK)
1377*4882a593Smuzhiyun 		return request_sga_clock(clock, enable);
1378*4882a593Smuzhiyun 	else if (clock < PRCMU_NUM_REG_CLOCKS)
1379*4882a593Smuzhiyun 		return request_clock(clock, enable);
1380*4882a593Smuzhiyun 	else if (clock == PRCMU_TIMCLK)
1381*4882a593Smuzhiyun 		return request_timclk(enable);
1382*4882a593Smuzhiyun 	else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK))
1383*4882a593Smuzhiyun 		return request_dsiclk((clock - PRCMU_DSI0CLK), enable);
1384*4882a593Smuzhiyun 	else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK))
1385*4882a593Smuzhiyun 		return request_dsiescclk((clock - PRCMU_DSI0ESCCLK), enable);
1386*4882a593Smuzhiyun 	else if (clock == PRCMU_PLLDSI)
1387*4882a593Smuzhiyun 		return request_plldsi(enable);
1388*4882a593Smuzhiyun 	else if (clock == PRCMU_SYSCLK)
1389*4882a593Smuzhiyun 		return request_sysclk(enable);
1390*4882a593Smuzhiyun 	else if ((clock == PRCMU_PLLSOC0) || (clock == PRCMU_PLLSOC1))
1391*4882a593Smuzhiyun 		return request_pll(clock, enable);
1392*4882a593Smuzhiyun 	else
1393*4882a593Smuzhiyun 		return -EINVAL;
1394*4882a593Smuzhiyun }
1395*4882a593Smuzhiyun 
pll_rate(void __iomem * reg,unsigned long src_rate,int branch)1396*4882a593Smuzhiyun static unsigned long pll_rate(void __iomem *reg, unsigned long src_rate,
1397*4882a593Smuzhiyun 	int branch)
1398*4882a593Smuzhiyun {
1399*4882a593Smuzhiyun 	u64 rate;
1400*4882a593Smuzhiyun 	u32 val;
1401*4882a593Smuzhiyun 	u32 d;
1402*4882a593Smuzhiyun 	u32 div = 1;
1403*4882a593Smuzhiyun 
1404*4882a593Smuzhiyun 	val = readl(reg);
1405*4882a593Smuzhiyun 
1406*4882a593Smuzhiyun 	rate = src_rate;
1407*4882a593Smuzhiyun 	rate *= ((val & PRCM_PLL_FREQ_D_MASK) >> PRCM_PLL_FREQ_D_SHIFT);
1408*4882a593Smuzhiyun 
1409*4882a593Smuzhiyun 	d = ((val & PRCM_PLL_FREQ_N_MASK) >> PRCM_PLL_FREQ_N_SHIFT);
1410*4882a593Smuzhiyun 	if (d > 1)
1411*4882a593Smuzhiyun 		div *= d;
1412*4882a593Smuzhiyun 
1413*4882a593Smuzhiyun 	d = ((val & PRCM_PLL_FREQ_R_MASK) >> PRCM_PLL_FREQ_R_SHIFT);
1414*4882a593Smuzhiyun 	if (d > 1)
1415*4882a593Smuzhiyun 		div *= d;
1416*4882a593Smuzhiyun 
1417*4882a593Smuzhiyun 	if (val & PRCM_PLL_FREQ_SELDIV2)
1418*4882a593Smuzhiyun 		div *= 2;
1419*4882a593Smuzhiyun 
1420*4882a593Smuzhiyun 	if ((branch == PLL_FIX) || ((branch == PLL_DIV) &&
1421*4882a593Smuzhiyun 		(val & PRCM_PLL_FREQ_DIV2EN) &&
1422*4882a593Smuzhiyun 		((reg == PRCM_PLLSOC0_FREQ) ||
1423*4882a593Smuzhiyun 		 (reg == PRCM_PLLARM_FREQ) ||
1424*4882a593Smuzhiyun 		 (reg == PRCM_PLLDDR_FREQ))))
1425*4882a593Smuzhiyun 		div *= 2;
1426*4882a593Smuzhiyun 
1427*4882a593Smuzhiyun 	(void)do_div(rate, div);
1428*4882a593Smuzhiyun 
1429*4882a593Smuzhiyun 	return (unsigned long)rate;
1430*4882a593Smuzhiyun }
1431*4882a593Smuzhiyun 
1432*4882a593Smuzhiyun #define ROOT_CLOCK_RATE 38400000
1433*4882a593Smuzhiyun 
clock_rate(u8 clock)1434*4882a593Smuzhiyun static unsigned long clock_rate(u8 clock)
1435*4882a593Smuzhiyun {
1436*4882a593Smuzhiyun 	u32 val;
1437*4882a593Smuzhiyun 	u32 pllsw;
1438*4882a593Smuzhiyun 	unsigned long rate = ROOT_CLOCK_RATE;
1439*4882a593Smuzhiyun 
1440*4882a593Smuzhiyun 	val = readl(prcmu_base + clk_mgt[clock].offset);
1441*4882a593Smuzhiyun 
1442*4882a593Smuzhiyun 	if (val & PRCM_CLK_MGT_CLK38) {
1443*4882a593Smuzhiyun 		if (clk_mgt[clock].clk38div && (val & PRCM_CLK_MGT_CLK38DIV))
1444*4882a593Smuzhiyun 			rate /= 2;
1445*4882a593Smuzhiyun 		return rate;
1446*4882a593Smuzhiyun 	}
1447*4882a593Smuzhiyun 
1448*4882a593Smuzhiyun 	val |= clk_mgt[clock].pllsw;
1449*4882a593Smuzhiyun 	pllsw = (val & PRCM_CLK_MGT_CLKPLLSW_MASK);
1450*4882a593Smuzhiyun 
1451*4882a593Smuzhiyun 	if (pllsw == PRCM_CLK_MGT_CLKPLLSW_SOC0)
1452*4882a593Smuzhiyun 		rate = pll_rate(PRCM_PLLSOC0_FREQ, rate, clk_mgt[clock].branch);
1453*4882a593Smuzhiyun 	else if (pllsw == PRCM_CLK_MGT_CLKPLLSW_SOC1)
1454*4882a593Smuzhiyun 		rate = pll_rate(PRCM_PLLSOC1_FREQ, rate, clk_mgt[clock].branch);
1455*4882a593Smuzhiyun 	else if (pllsw == PRCM_CLK_MGT_CLKPLLSW_DDR)
1456*4882a593Smuzhiyun 		rate = pll_rate(PRCM_PLLDDR_FREQ, rate, clk_mgt[clock].branch);
1457*4882a593Smuzhiyun 	else
1458*4882a593Smuzhiyun 		return 0;
1459*4882a593Smuzhiyun 
1460*4882a593Smuzhiyun 	if ((clock == PRCMU_SGACLK) &&
1461*4882a593Smuzhiyun 		(val & PRCM_SGACLK_MGT_SGACLKDIV_BY_2_5_EN)) {
1462*4882a593Smuzhiyun 		u64 r = (rate * 10);
1463*4882a593Smuzhiyun 
1464*4882a593Smuzhiyun 		(void)do_div(r, 25);
1465*4882a593Smuzhiyun 		return (unsigned long)r;
1466*4882a593Smuzhiyun 	}
1467*4882a593Smuzhiyun 	val &= PRCM_CLK_MGT_CLKPLLDIV_MASK;
1468*4882a593Smuzhiyun 	if (val)
1469*4882a593Smuzhiyun 		return rate / val;
1470*4882a593Smuzhiyun 	else
1471*4882a593Smuzhiyun 		return 0;
1472*4882a593Smuzhiyun }
1473*4882a593Smuzhiyun 
armss_rate(void)1474*4882a593Smuzhiyun static unsigned long armss_rate(void)
1475*4882a593Smuzhiyun {
1476*4882a593Smuzhiyun 	u32 r;
1477*4882a593Smuzhiyun 	unsigned long rate;
1478*4882a593Smuzhiyun 
1479*4882a593Smuzhiyun 	r = readl(PRCM_ARM_CHGCLKREQ);
1480*4882a593Smuzhiyun 
1481*4882a593Smuzhiyun 	if (r & PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ) {
1482*4882a593Smuzhiyun 		/* External ARMCLKFIX clock */
1483*4882a593Smuzhiyun 
1484*4882a593Smuzhiyun 		rate = pll_rate(PRCM_PLLDDR_FREQ, ROOT_CLOCK_RATE, PLL_FIX);
1485*4882a593Smuzhiyun 
1486*4882a593Smuzhiyun 		/* Check PRCM_ARM_CHGCLKREQ divider */
1487*4882a593Smuzhiyun 		if (!(r & PRCM_ARM_CHGCLKREQ_PRCM_ARM_DIVSEL))
1488*4882a593Smuzhiyun 			rate /= 2;
1489*4882a593Smuzhiyun 
1490*4882a593Smuzhiyun 		/* Check PRCM_ARMCLKFIX_MGT divider */
1491*4882a593Smuzhiyun 		r = readl(PRCM_ARMCLKFIX_MGT);
1492*4882a593Smuzhiyun 		r &= PRCM_CLK_MGT_CLKPLLDIV_MASK;
1493*4882a593Smuzhiyun 		rate /= r;
1494*4882a593Smuzhiyun 
1495*4882a593Smuzhiyun 	} else {/* ARM PLL */
1496*4882a593Smuzhiyun 		rate = pll_rate(PRCM_PLLARM_FREQ, ROOT_CLOCK_RATE, PLL_DIV);
1497*4882a593Smuzhiyun 	}
1498*4882a593Smuzhiyun 
1499*4882a593Smuzhiyun 	return rate;
1500*4882a593Smuzhiyun }
1501*4882a593Smuzhiyun 
dsiclk_rate(u8 n)1502*4882a593Smuzhiyun static unsigned long dsiclk_rate(u8 n)
1503*4882a593Smuzhiyun {
1504*4882a593Smuzhiyun 	u32 divsel;
1505*4882a593Smuzhiyun 	u32 div = 1;
1506*4882a593Smuzhiyun 
1507*4882a593Smuzhiyun 	divsel = readl(PRCM_DSI_PLLOUT_SEL);
1508*4882a593Smuzhiyun 	divsel = ((divsel & dsiclk[n].divsel_mask) >> dsiclk[n].divsel_shift);
1509*4882a593Smuzhiyun 
1510*4882a593Smuzhiyun 	if (divsel == PRCM_DSI_PLLOUT_SEL_OFF)
1511*4882a593Smuzhiyun 		divsel = dsiclk[n].divsel;
1512*4882a593Smuzhiyun 	else
1513*4882a593Smuzhiyun 		dsiclk[n].divsel = divsel;
1514*4882a593Smuzhiyun 
1515*4882a593Smuzhiyun 	switch (divsel) {
1516*4882a593Smuzhiyun 	case PRCM_DSI_PLLOUT_SEL_PHI_4:
1517*4882a593Smuzhiyun 		div *= 2;
1518*4882a593Smuzhiyun 		fallthrough;
1519*4882a593Smuzhiyun 	case PRCM_DSI_PLLOUT_SEL_PHI_2:
1520*4882a593Smuzhiyun 		div *= 2;
1521*4882a593Smuzhiyun 		fallthrough;
1522*4882a593Smuzhiyun 	case PRCM_DSI_PLLOUT_SEL_PHI:
1523*4882a593Smuzhiyun 		return pll_rate(PRCM_PLLDSI_FREQ, clock_rate(PRCMU_HDMICLK),
1524*4882a593Smuzhiyun 			PLL_RAW) / div;
1525*4882a593Smuzhiyun 	default:
1526*4882a593Smuzhiyun 		return 0;
1527*4882a593Smuzhiyun 	}
1528*4882a593Smuzhiyun }
1529*4882a593Smuzhiyun 
dsiescclk_rate(u8 n)1530*4882a593Smuzhiyun static unsigned long dsiescclk_rate(u8 n)
1531*4882a593Smuzhiyun {
1532*4882a593Smuzhiyun 	u32 div;
1533*4882a593Smuzhiyun 
1534*4882a593Smuzhiyun 	div = readl(PRCM_DSITVCLK_DIV);
1535*4882a593Smuzhiyun 	div = ((div & dsiescclk[n].div_mask) >> (dsiescclk[n].div_shift));
1536*4882a593Smuzhiyun 	return clock_rate(PRCMU_TVCLK) / max((u32)1, div);
1537*4882a593Smuzhiyun }
1538*4882a593Smuzhiyun 
prcmu_clock_rate(u8 clock)1539*4882a593Smuzhiyun unsigned long prcmu_clock_rate(u8 clock)
1540*4882a593Smuzhiyun {
1541*4882a593Smuzhiyun 	if (clock < PRCMU_NUM_REG_CLOCKS)
1542*4882a593Smuzhiyun 		return clock_rate(clock);
1543*4882a593Smuzhiyun 	else if (clock == PRCMU_TIMCLK)
1544*4882a593Smuzhiyun 		return prcmu_is_ulppll_disabled() ?
1545*4882a593Smuzhiyun 			32768 : ROOT_CLOCK_RATE / 16;
1546*4882a593Smuzhiyun 	else if (clock == PRCMU_SYSCLK)
1547*4882a593Smuzhiyun 		return ROOT_CLOCK_RATE;
1548*4882a593Smuzhiyun 	else if (clock == PRCMU_PLLSOC0)
1549*4882a593Smuzhiyun 		return pll_rate(PRCM_PLLSOC0_FREQ, ROOT_CLOCK_RATE, PLL_RAW);
1550*4882a593Smuzhiyun 	else if (clock == PRCMU_PLLSOC1)
1551*4882a593Smuzhiyun 		return pll_rate(PRCM_PLLSOC1_FREQ, ROOT_CLOCK_RATE, PLL_RAW);
1552*4882a593Smuzhiyun 	else if (clock == PRCMU_ARMSS)
1553*4882a593Smuzhiyun 		return armss_rate();
1554*4882a593Smuzhiyun 	else if (clock == PRCMU_PLLDDR)
1555*4882a593Smuzhiyun 		return pll_rate(PRCM_PLLDDR_FREQ, ROOT_CLOCK_RATE, PLL_RAW);
1556*4882a593Smuzhiyun 	else if (clock == PRCMU_PLLDSI)
1557*4882a593Smuzhiyun 		return pll_rate(PRCM_PLLDSI_FREQ, clock_rate(PRCMU_HDMICLK),
1558*4882a593Smuzhiyun 			PLL_RAW);
1559*4882a593Smuzhiyun 	else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK))
1560*4882a593Smuzhiyun 		return dsiclk_rate(clock - PRCMU_DSI0CLK);
1561*4882a593Smuzhiyun 	else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK))
1562*4882a593Smuzhiyun 		return dsiescclk_rate(clock - PRCMU_DSI0ESCCLK);
1563*4882a593Smuzhiyun 	else
1564*4882a593Smuzhiyun 		return 0;
1565*4882a593Smuzhiyun }
1566*4882a593Smuzhiyun 
clock_source_rate(u32 clk_mgt_val,int branch)1567*4882a593Smuzhiyun static unsigned long clock_source_rate(u32 clk_mgt_val, int branch)
1568*4882a593Smuzhiyun {
1569*4882a593Smuzhiyun 	if (clk_mgt_val & PRCM_CLK_MGT_CLK38)
1570*4882a593Smuzhiyun 		return ROOT_CLOCK_RATE;
1571*4882a593Smuzhiyun 	clk_mgt_val &= PRCM_CLK_MGT_CLKPLLSW_MASK;
1572*4882a593Smuzhiyun 	if (clk_mgt_val == PRCM_CLK_MGT_CLKPLLSW_SOC0)
1573*4882a593Smuzhiyun 		return pll_rate(PRCM_PLLSOC0_FREQ, ROOT_CLOCK_RATE, branch);
1574*4882a593Smuzhiyun 	else if (clk_mgt_val == PRCM_CLK_MGT_CLKPLLSW_SOC1)
1575*4882a593Smuzhiyun 		return pll_rate(PRCM_PLLSOC1_FREQ, ROOT_CLOCK_RATE, branch);
1576*4882a593Smuzhiyun 	else if (clk_mgt_val == PRCM_CLK_MGT_CLKPLLSW_DDR)
1577*4882a593Smuzhiyun 		return pll_rate(PRCM_PLLDDR_FREQ, ROOT_CLOCK_RATE, branch);
1578*4882a593Smuzhiyun 	else
1579*4882a593Smuzhiyun 		return 0;
1580*4882a593Smuzhiyun }
1581*4882a593Smuzhiyun 
clock_divider(unsigned long src_rate,unsigned long rate)1582*4882a593Smuzhiyun static u32 clock_divider(unsigned long src_rate, unsigned long rate)
1583*4882a593Smuzhiyun {
1584*4882a593Smuzhiyun 	u32 div;
1585*4882a593Smuzhiyun 
1586*4882a593Smuzhiyun 	div = (src_rate / rate);
1587*4882a593Smuzhiyun 	if (div == 0)
1588*4882a593Smuzhiyun 		return 1;
1589*4882a593Smuzhiyun 	if (rate < (src_rate / div))
1590*4882a593Smuzhiyun 		div++;
1591*4882a593Smuzhiyun 	return div;
1592*4882a593Smuzhiyun }
1593*4882a593Smuzhiyun 
round_clock_rate(u8 clock,unsigned long rate)1594*4882a593Smuzhiyun static long round_clock_rate(u8 clock, unsigned long rate)
1595*4882a593Smuzhiyun {
1596*4882a593Smuzhiyun 	u32 val;
1597*4882a593Smuzhiyun 	u32 div;
1598*4882a593Smuzhiyun 	unsigned long src_rate;
1599*4882a593Smuzhiyun 	long rounded_rate;
1600*4882a593Smuzhiyun 
1601*4882a593Smuzhiyun 	val = readl(prcmu_base + clk_mgt[clock].offset);
1602*4882a593Smuzhiyun 	src_rate = clock_source_rate((val | clk_mgt[clock].pllsw),
1603*4882a593Smuzhiyun 		clk_mgt[clock].branch);
1604*4882a593Smuzhiyun 	div = clock_divider(src_rate, rate);
1605*4882a593Smuzhiyun 	if (val & PRCM_CLK_MGT_CLK38) {
1606*4882a593Smuzhiyun 		if (clk_mgt[clock].clk38div) {
1607*4882a593Smuzhiyun 			if (div > 2)
1608*4882a593Smuzhiyun 				div = 2;
1609*4882a593Smuzhiyun 		} else {
1610*4882a593Smuzhiyun 			div = 1;
1611*4882a593Smuzhiyun 		}
1612*4882a593Smuzhiyun 	} else if ((clock == PRCMU_SGACLK) && (div == 3)) {
1613*4882a593Smuzhiyun 		u64 r = (src_rate * 10);
1614*4882a593Smuzhiyun 
1615*4882a593Smuzhiyun 		(void)do_div(r, 25);
1616*4882a593Smuzhiyun 		if (r <= rate)
1617*4882a593Smuzhiyun 			return (unsigned long)r;
1618*4882a593Smuzhiyun 	}
1619*4882a593Smuzhiyun 	rounded_rate = (src_rate / min(div, (u32)31));
1620*4882a593Smuzhiyun 
1621*4882a593Smuzhiyun 	return rounded_rate;
1622*4882a593Smuzhiyun }
1623*4882a593Smuzhiyun 
1624*4882a593Smuzhiyun static const unsigned long db8500_armss_freqs[] = {
1625*4882a593Smuzhiyun 	199680000,
1626*4882a593Smuzhiyun 	399360000,
1627*4882a593Smuzhiyun 	798720000,
1628*4882a593Smuzhiyun 	998400000
1629*4882a593Smuzhiyun };
1630*4882a593Smuzhiyun 
1631*4882a593Smuzhiyun /* The DB8520 has slightly higher ARMSS max frequency */
1632*4882a593Smuzhiyun static const unsigned long db8520_armss_freqs[] = {
1633*4882a593Smuzhiyun 	199680000,
1634*4882a593Smuzhiyun 	399360000,
1635*4882a593Smuzhiyun 	798720000,
1636*4882a593Smuzhiyun 	1152000000
1637*4882a593Smuzhiyun };
1638*4882a593Smuzhiyun 
round_armss_rate(unsigned long rate)1639*4882a593Smuzhiyun static long round_armss_rate(unsigned long rate)
1640*4882a593Smuzhiyun {
1641*4882a593Smuzhiyun 	unsigned long freq = 0;
1642*4882a593Smuzhiyun 	const unsigned long *freqs;
1643*4882a593Smuzhiyun 	int nfreqs;
1644*4882a593Smuzhiyun 	int i;
1645*4882a593Smuzhiyun 
1646*4882a593Smuzhiyun 	if (fw_info.version.project == PRCMU_FW_PROJECT_U8520) {
1647*4882a593Smuzhiyun 		freqs = db8520_armss_freqs;
1648*4882a593Smuzhiyun 		nfreqs = ARRAY_SIZE(db8520_armss_freqs);
1649*4882a593Smuzhiyun 	} else {
1650*4882a593Smuzhiyun 		freqs = db8500_armss_freqs;
1651*4882a593Smuzhiyun 		nfreqs = ARRAY_SIZE(db8500_armss_freqs);
1652*4882a593Smuzhiyun 	}
1653*4882a593Smuzhiyun 
1654*4882a593Smuzhiyun 	/* Find the corresponding arm opp from the cpufreq table. */
1655*4882a593Smuzhiyun 	for (i = 0; i < nfreqs; i++) {
1656*4882a593Smuzhiyun 		freq = freqs[i];
1657*4882a593Smuzhiyun 		if (rate <= freq)
1658*4882a593Smuzhiyun 			break;
1659*4882a593Smuzhiyun 	}
1660*4882a593Smuzhiyun 
1661*4882a593Smuzhiyun 	/* Return the last valid value, even if a match was not found. */
1662*4882a593Smuzhiyun 	return freq;
1663*4882a593Smuzhiyun }
1664*4882a593Smuzhiyun 
1665*4882a593Smuzhiyun #define MIN_PLL_VCO_RATE 600000000ULL
1666*4882a593Smuzhiyun #define MAX_PLL_VCO_RATE 1680640000ULL
1667*4882a593Smuzhiyun 
round_plldsi_rate(unsigned long rate)1668*4882a593Smuzhiyun static long round_plldsi_rate(unsigned long rate)
1669*4882a593Smuzhiyun {
1670*4882a593Smuzhiyun 	long rounded_rate = 0;
1671*4882a593Smuzhiyun 	unsigned long src_rate;
1672*4882a593Smuzhiyun 	unsigned long rem;
1673*4882a593Smuzhiyun 	u32 r;
1674*4882a593Smuzhiyun 
1675*4882a593Smuzhiyun 	src_rate = clock_rate(PRCMU_HDMICLK);
1676*4882a593Smuzhiyun 	rem = rate;
1677*4882a593Smuzhiyun 
1678*4882a593Smuzhiyun 	for (r = 7; (rem > 0) && (r > 0); r--) {
1679*4882a593Smuzhiyun 		u64 d;
1680*4882a593Smuzhiyun 
1681*4882a593Smuzhiyun 		d = (r * rate);
1682*4882a593Smuzhiyun 		(void)do_div(d, src_rate);
1683*4882a593Smuzhiyun 		if (d < 6)
1684*4882a593Smuzhiyun 			d = 6;
1685*4882a593Smuzhiyun 		else if (d > 255)
1686*4882a593Smuzhiyun 			d = 255;
1687*4882a593Smuzhiyun 		d *= src_rate;
1688*4882a593Smuzhiyun 		if (((2 * d) < (r * MIN_PLL_VCO_RATE)) ||
1689*4882a593Smuzhiyun 			((r * MAX_PLL_VCO_RATE) < (2 * d)))
1690*4882a593Smuzhiyun 			continue;
1691*4882a593Smuzhiyun 		(void)do_div(d, r);
1692*4882a593Smuzhiyun 		if (rate < d) {
1693*4882a593Smuzhiyun 			if (rounded_rate == 0)
1694*4882a593Smuzhiyun 				rounded_rate = (long)d;
1695*4882a593Smuzhiyun 			break;
1696*4882a593Smuzhiyun 		}
1697*4882a593Smuzhiyun 		if ((rate - d) < rem) {
1698*4882a593Smuzhiyun 			rem = (rate - d);
1699*4882a593Smuzhiyun 			rounded_rate = (long)d;
1700*4882a593Smuzhiyun 		}
1701*4882a593Smuzhiyun 	}
1702*4882a593Smuzhiyun 	return rounded_rate;
1703*4882a593Smuzhiyun }
1704*4882a593Smuzhiyun 
round_dsiclk_rate(unsigned long rate)1705*4882a593Smuzhiyun static long round_dsiclk_rate(unsigned long rate)
1706*4882a593Smuzhiyun {
1707*4882a593Smuzhiyun 	u32 div;
1708*4882a593Smuzhiyun 	unsigned long src_rate;
1709*4882a593Smuzhiyun 	long rounded_rate;
1710*4882a593Smuzhiyun 
1711*4882a593Smuzhiyun 	src_rate = pll_rate(PRCM_PLLDSI_FREQ, clock_rate(PRCMU_HDMICLK),
1712*4882a593Smuzhiyun 		PLL_RAW);
1713*4882a593Smuzhiyun 	div = clock_divider(src_rate, rate);
1714*4882a593Smuzhiyun 	rounded_rate = (src_rate / ((div > 2) ? 4 : div));
1715*4882a593Smuzhiyun 
1716*4882a593Smuzhiyun 	return rounded_rate;
1717*4882a593Smuzhiyun }
1718*4882a593Smuzhiyun 
round_dsiescclk_rate(unsigned long rate)1719*4882a593Smuzhiyun static long round_dsiescclk_rate(unsigned long rate)
1720*4882a593Smuzhiyun {
1721*4882a593Smuzhiyun 	u32 div;
1722*4882a593Smuzhiyun 	unsigned long src_rate;
1723*4882a593Smuzhiyun 	long rounded_rate;
1724*4882a593Smuzhiyun 
1725*4882a593Smuzhiyun 	src_rate = clock_rate(PRCMU_TVCLK);
1726*4882a593Smuzhiyun 	div = clock_divider(src_rate, rate);
1727*4882a593Smuzhiyun 	rounded_rate = (src_rate / min(div, (u32)255));
1728*4882a593Smuzhiyun 
1729*4882a593Smuzhiyun 	return rounded_rate;
1730*4882a593Smuzhiyun }
1731*4882a593Smuzhiyun 
prcmu_round_clock_rate(u8 clock,unsigned long rate)1732*4882a593Smuzhiyun long prcmu_round_clock_rate(u8 clock, unsigned long rate)
1733*4882a593Smuzhiyun {
1734*4882a593Smuzhiyun 	if (clock < PRCMU_NUM_REG_CLOCKS)
1735*4882a593Smuzhiyun 		return round_clock_rate(clock, rate);
1736*4882a593Smuzhiyun 	else if (clock == PRCMU_ARMSS)
1737*4882a593Smuzhiyun 		return round_armss_rate(rate);
1738*4882a593Smuzhiyun 	else if (clock == PRCMU_PLLDSI)
1739*4882a593Smuzhiyun 		return round_plldsi_rate(rate);
1740*4882a593Smuzhiyun 	else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK))
1741*4882a593Smuzhiyun 		return round_dsiclk_rate(rate);
1742*4882a593Smuzhiyun 	else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK))
1743*4882a593Smuzhiyun 		return round_dsiescclk_rate(rate);
1744*4882a593Smuzhiyun 	else
1745*4882a593Smuzhiyun 		return (long)prcmu_clock_rate(clock);
1746*4882a593Smuzhiyun }
1747*4882a593Smuzhiyun 
set_clock_rate(u8 clock,unsigned long rate)1748*4882a593Smuzhiyun static void set_clock_rate(u8 clock, unsigned long rate)
1749*4882a593Smuzhiyun {
1750*4882a593Smuzhiyun 	u32 val;
1751*4882a593Smuzhiyun 	u32 div;
1752*4882a593Smuzhiyun 	unsigned long src_rate;
1753*4882a593Smuzhiyun 	unsigned long flags;
1754*4882a593Smuzhiyun 
1755*4882a593Smuzhiyun 	spin_lock_irqsave(&clk_mgt_lock, flags);
1756*4882a593Smuzhiyun 
1757*4882a593Smuzhiyun 	/* Grab the HW semaphore. */
1758*4882a593Smuzhiyun 	while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
1759*4882a593Smuzhiyun 		cpu_relax();
1760*4882a593Smuzhiyun 
1761*4882a593Smuzhiyun 	val = readl(prcmu_base + clk_mgt[clock].offset);
1762*4882a593Smuzhiyun 	src_rate = clock_source_rate((val | clk_mgt[clock].pllsw),
1763*4882a593Smuzhiyun 		clk_mgt[clock].branch);
1764*4882a593Smuzhiyun 	div = clock_divider(src_rate, rate);
1765*4882a593Smuzhiyun 	if (val & PRCM_CLK_MGT_CLK38) {
1766*4882a593Smuzhiyun 		if (clk_mgt[clock].clk38div) {
1767*4882a593Smuzhiyun 			if (div > 1)
1768*4882a593Smuzhiyun 				val |= PRCM_CLK_MGT_CLK38DIV;
1769*4882a593Smuzhiyun 			else
1770*4882a593Smuzhiyun 				val &= ~PRCM_CLK_MGT_CLK38DIV;
1771*4882a593Smuzhiyun 		}
1772*4882a593Smuzhiyun 	} else if (clock == PRCMU_SGACLK) {
1773*4882a593Smuzhiyun 		val &= ~(PRCM_CLK_MGT_CLKPLLDIV_MASK |
1774*4882a593Smuzhiyun 			PRCM_SGACLK_MGT_SGACLKDIV_BY_2_5_EN);
1775*4882a593Smuzhiyun 		if (div == 3) {
1776*4882a593Smuzhiyun 			u64 r = (src_rate * 10);
1777*4882a593Smuzhiyun 
1778*4882a593Smuzhiyun 			(void)do_div(r, 25);
1779*4882a593Smuzhiyun 			if (r <= rate) {
1780*4882a593Smuzhiyun 				val |= PRCM_SGACLK_MGT_SGACLKDIV_BY_2_5_EN;
1781*4882a593Smuzhiyun 				div = 0;
1782*4882a593Smuzhiyun 			}
1783*4882a593Smuzhiyun 		}
1784*4882a593Smuzhiyun 		val |= min(div, (u32)31);
1785*4882a593Smuzhiyun 	} else {
1786*4882a593Smuzhiyun 		val &= ~PRCM_CLK_MGT_CLKPLLDIV_MASK;
1787*4882a593Smuzhiyun 		val |= min(div, (u32)31);
1788*4882a593Smuzhiyun 	}
1789*4882a593Smuzhiyun 	writel(val, prcmu_base + clk_mgt[clock].offset);
1790*4882a593Smuzhiyun 
1791*4882a593Smuzhiyun 	/* Release the HW semaphore. */
1792*4882a593Smuzhiyun 	writel(0, PRCM_SEM);
1793*4882a593Smuzhiyun 
1794*4882a593Smuzhiyun 	spin_unlock_irqrestore(&clk_mgt_lock, flags);
1795*4882a593Smuzhiyun }
1796*4882a593Smuzhiyun 
set_armss_rate(unsigned long rate)1797*4882a593Smuzhiyun static int set_armss_rate(unsigned long rate)
1798*4882a593Smuzhiyun {
1799*4882a593Smuzhiyun 	unsigned long freq;
1800*4882a593Smuzhiyun 	u8 opps[] = { ARM_EXTCLK, ARM_50_OPP, ARM_100_OPP, ARM_MAX_OPP };
1801*4882a593Smuzhiyun 	const unsigned long *freqs;
1802*4882a593Smuzhiyun 	int nfreqs;
1803*4882a593Smuzhiyun 	int i;
1804*4882a593Smuzhiyun 
1805*4882a593Smuzhiyun 	if (fw_info.version.project == PRCMU_FW_PROJECT_U8520) {
1806*4882a593Smuzhiyun 		freqs = db8520_armss_freqs;
1807*4882a593Smuzhiyun 		nfreqs = ARRAY_SIZE(db8520_armss_freqs);
1808*4882a593Smuzhiyun 	} else {
1809*4882a593Smuzhiyun 		freqs = db8500_armss_freqs;
1810*4882a593Smuzhiyun 		nfreqs = ARRAY_SIZE(db8500_armss_freqs);
1811*4882a593Smuzhiyun 	}
1812*4882a593Smuzhiyun 
1813*4882a593Smuzhiyun 	/* Find the corresponding arm opp from the cpufreq table. */
1814*4882a593Smuzhiyun 	for (i = 0; i < nfreqs; i++) {
1815*4882a593Smuzhiyun 		freq = freqs[i];
1816*4882a593Smuzhiyun 		if (rate == freq)
1817*4882a593Smuzhiyun 			break;
1818*4882a593Smuzhiyun 	}
1819*4882a593Smuzhiyun 
1820*4882a593Smuzhiyun 	if (rate != freq)
1821*4882a593Smuzhiyun 		return -EINVAL;
1822*4882a593Smuzhiyun 
1823*4882a593Smuzhiyun 	/* Set the new arm opp. */
1824*4882a593Smuzhiyun 	pr_debug("SET ARM OPP 0x%02x\n", opps[i]);
1825*4882a593Smuzhiyun 	return db8500_prcmu_set_arm_opp(opps[i]);
1826*4882a593Smuzhiyun }
1827*4882a593Smuzhiyun 
set_plldsi_rate(unsigned long rate)1828*4882a593Smuzhiyun static int set_plldsi_rate(unsigned long rate)
1829*4882a593Smuzhiyun {
1830*4882a593Smuzhiyun 	unsigned long src_rate;
1831*4882a593Smuzhiyun 	unsigned long rem;
1832*4882a593Smuzhiyun 	u32 pll_freq = 0;
1833*4882a593Smuzhiyun 	u32 r;
1834*4882a593Smuzhiyun 
1835*4882a593Smuzhiyun 	src_rate = clock_rate(PRCMU_HDMICLK);
1836*4882a593Smuzhiyun 	rem = rate;
1837*4882a593Smuzhiyun 
1838*4882a593Smuzhiyun 	for (r = 7; (rem > 0) && (r > 0); r--) {
1839*4882a593Smuzhiyun 		u64 d;
1840*4882a593Smuzhiyun 		u64 hwrate;
1841*4882a593Smuzhiyun 
1842*4882a593Smuzhiyun 		d = (r * rate);
1843*4882a593Smuzhiyun 		(void)do_div(d, src_rate);
1844*4882a593Smuzhiyun 		if (d < 6)
1845*4882a593Smuzhiyun 			d = 6;
1846*4882a593Smuzhiyun 		else if (d > 255)
1847*4882a593Smuzhiyun 			d = 255;
1848*4882a593Smuzhiyun 		hwrate = (d * src_rate);
1849*4882a593Smuzhiyun 		if (((2 * hwrate) < (r * MIN_PLL_VCO_RATE)) ||
1850*4882a593Smuzhiyun 			((r * MAX_PLL_VCO_RATE) < (2 * hwrate)))
1851*4882a593Smuzhiyun 			continue;
1852*4882a593Smuzhiyun 		(void)do_div(hwrate, r);
1853*4882a593Smuzhiyun 		if (rate < hwrate) {
1854*4882a593Smuzhiyun 			if (pll_freq == 0)
1855*4882a593Smuzhiyun 				pll_freq = (((u32)d << PRCM_PLL_FREQ_D_SHIFT) |
1856*4882a593Smuzhiyun 					(r << PRCM_PLL_FREQ_R_SHIFT));
1857*4882a593Smuzhiyun 			break;
1858*4882a593Smuzhiyun 		}
1859*4882a593Smuzhiyun 		if ((rate - hwrate) < rem) {
1860*4882a593Smuzhiyun 			rem = (rate - hwrate);
1861*4882a593Smuzhiyun 			pll_freq = (((u32)d << PRCM_PLL_FREQ_D_SHIFT) |
1862*4882a593Smuzhiyun 				(r << PRCM_PLL_FREQ_R_SHIFT));
1863*4882a593Smuzhiyun 		}
1864*4882a593Smuzhiyun 	}
1865*4882a593Smuzhiyun 	if (pll_freq == 0)
1866*4882a593Smuzhiyun 		return -EINVAL;
1867*4882a593Smuzhiyun 
1868*4882a593Smuzhiyun 	pll_freq |= (1 << PRCM_PLL_FREQ_N_SHIFT);
1869*4882a593Smuzhiyun 	writel(pll_freq, PRCM_PLLDSI_FREQ);
1870*4882a593Smuzhiyun 
1871*4882a593Smuzhiyun 	return 0;
1872*4882a593Smuzhiyun }
1873*4882a593Smuzhiyun 
set_dsiclk_rate(u8 n,unsigned long rate)1874*4882a593Smuzhiyun static void set_dsiclk_rate(u8 n, unsigned long rate)
1875*4882a593Smuzhiyun {
1876*4882a593Smuzhiyun 	u32 val;
1877*4882a593Smuzhiyun 	u32 div;
1878*4882a593Smuzhiyun 
1879*4882a593Smuzhiyun 	div = clock_divider(pll_rate(PRCM_PLLDSI_FREQ,
1880*4882a593Smuzhiyun 			clock_rate(PRCMU_HDMICLK), PLL_RAW), rate);
1881*4882a593Smuzhiyun 
1882*4882a593Smuzhiyun 	dsiclk[n].divsel = (div == 1) ? PRCM_DSI_PLLOUT_SEL_PHI :
1883*4882a593Smuzhiyun 			   (div == 2) ? PRCM_DSI_PLLOUT_SEL_PHI_2 :
1884*4882a593Smuzhiyun 			   /* else */	PRCM_DSI_PLLOUT_SEL_PHI_4;
1885*4882a593Smuzhiyun 
1886*4882a593Smuzhiyun 	val = readl(PRCM_DSI_PLLOUT_SEL);
1887*4882a593Smuzhiyun 	val &= ~dsiclk[n].divsel_mask;
1888*4882a593Smuzhiyun 	val |= (dsiclk[n].divsel << dsiclk[n].divsel_shift);
1889*4882a593Smuzhiyun 	writel(val, PRCM_DSI_PLLOUT_SEL);
1890*4882a593Smuzhiyun }
1891*4882a593Smuzhiyun 
set_dsiescclk_rate(u8 n,unsigned long rate)1892*4882a593Smuzhiyun static void set_dsiescclk_rate(u8 n, unsigned long rate)
1893*4882a593Smuzhiyun {
1894*4882a593Smuzhiyun 	u32 val;
1895*4882a593Smuzhiyun 	u32 div;
1896*4882a593Smuzhiyun 
1897*4882a593Smuzhiyun 	div = clock_divider(clock_rate(PRCMU_TVCLK), rate);
1898*4882a593Smuzhiyun 	val = readl(PRCM_DSITVCLK_DIV);
1899*4882a593Smuzhiyun 	val &= ~dsiescclk[n].div_mask;
1900*4882a593Smuzhiyun 	val |= (min(div, (u32)255) << dsiescclk[n].div_shift);
1901*4882a593Smuzhiyun 	writel(val, PRCM_DSITVCLK_DIV);
1902*4882a593Smuzhiyun }
1903*4882a593Smuzhiyun 
prcmu_set_clock_rate(u8 clock,unsigned long rate)1904*4882a593Smuzhiyun int prcmu_set_clock_rate(u8 clock, unsigned long rate)
1905*4882a593Smuzhiyun {
1906*4882a593Smuzhiyun 	if (clock < PRCMU_NUM_REG_CLOCKS)
1907*4882a593Smuzhiyun 		set_clock_rate(clock, rate);
1908*4882a593Smuzhiyun 	else if (clock == PRCMU_ARMSS)
1909*4882a593Smuzhiyun 		return set_armss_rate(rate);
1910*4882a593Smuzhiyun 	else if (clock == PRCMU_PLLDSI)
1911*4882a593Smuzhiyun 		return set_plldsi_rate(rate);
1912*4882a593Smuzhiyun 	else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK))
1913*4882a593Smuzhiyun 		set_dsiclk_rate((clock - PRCMU_DSI0CLK), rate);
1914*4882a593Smuzhiyun 	else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK))
1915*4882a593Smuzhiyun 		set_dsiescclk_rate((clock - PRCMU_DSI0ESCCLK), rate);
1916*4882a593Smuzhiyun 	return 0;
1917*4882a593Smuzhiyun }
1918*4882a593Smuzhiyun 
db8500_prcmu_config_esram0_deep_sleep(u8 state)1919*4882a593Smuzhiyun int db8500_prcmu_config_esram0_deep_sleep(u8 state)
1920*4882a593Smuzhiyun {
1921*4882a593Smuzhiyun 	if ((state > ESRAM0_DEEP_SLEEP_STATE_RET) ||
1922*4882a593Smuzhiyun 	    (state < ESRAM0_DEEP_SLEEP_STATE_OFF))
1923*4882a593Smuzhiyun 		return -EINVAL;
1924*4882a593Smuzhiyun 
1925*4882a593Smuzhiyun 	mutex_lock(&mb4_transfer.lock);
1926*4882a593Smuzhiyun 
1927*4882a593Smuzhiyun 	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
1928*4882a593Smuzhiyun 		cpu_relax();
1929*4882a593Smuzhiyun 
1930*4882a593Smuzhiyun 	writeb(MB4H_MEM_ST, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4));
1931*4882a593Smuzhiyun 	writeb(((DDR_PWR_STATE_OFFHIGHLAT << 4) | DDR_PWR_STATE_ON),
1932*4882a593Smuzhiyun 	       (tcdm_base + PRCM_REQ_MB4_DDR_ST_AP_SLEEP_IDLE));
1933*4882a593Smuzhiyun 	writeb(DDR_PWR_STATE_ON,
1934*4882a593Smuzhiyun 	       (tcdm_base + PRCM_REQ_MB4_DDR_ST_AP_DEEP_IDLE));
1935*4882a593Smuzhiyun 	writeb(state, (tcdm_base + PRCM_REQ_MB4_ESRAM0_ST));
1936*4882a593Smuzhiyun 
1937*4882a593Smuzhiyun 	writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET);
1938*4882a593Smuzhiyun 	wait_for_completion(&mb4_transfer.work);
1939*4882a593Smuzhiyun 
1940*4882a593Smuzhiyun 	mutex_unlock(&mb4_transfer.lock);
1941*4882a593Smuzhiyun 
1942*4882a593Smuzhiyun 	return 0;
1943*4882a593Smuzhiyun }
1944*4882a593Smuzhiyun 
db8500_prcmu_config_hotdog(u8 threshold)1945*4882a593Smuzhiyun int db8500_prcmu_config_hotdog(u8 threshold)
1946*4882a593Smuzhiyun {
1947*4882a593Smuzhiyun 	mutex_lock(&mb4_transfer.lock);
1948*4882a593Smuzhiyun 
1949*4882a593Smuzhiyun 	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
1950*4882a593Smuzhiyun 		cpu_relax();
1951*4882a593Smuzhiyun 
1952*4882a593Smuzhiyun 	writeb(threshold, (tcdm_base + PRCM_REQ_MB4_HOTDOG_THRESHOLD));
1953*4882a593Smuzhiyun 	writeb(MB4H_HOTDOG, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4));
1954*4882a593Smuzhiyun 
1955*4882a593Smuzhiyun 	writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET);
1956*4882a593Smuzhiyun 	wait_for_completion(&mb4_transfer.work);
1957*4882a593Smuzhiyun 
1958*4882a593Smuzhiyun 	mutex_unlock(&mb4_transfer.lock);
1959*4882a593Smuzhiyun 
1960*4882a593Smuzhiyun 	return 0;
1961*4882a593Smuzhiyun }
1962*4882a593Smuzhiyun 
db8500_prcmu_config_hotmon(u8 low,u8 high)1963*4882a593Smuzhiyun int db8500_prcmu_config_hotmon(u8 low, u8 high)
1964*4882a593Smuzhiyun {
1965*4882a593Smuzhiyun 	mutex_lock(&mb4_transfer.lock);
1966*4882a593Smuzhiyun 
1967*4882a593Smuzhiyun 	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
1968*4882a593Smuzhiyun 		cpu_relax();
1969*4882a593Smuzhiyun 
1970*4882a593Smuzhiyun 	writeb(low, (tcdm_base + PRCM_REQ_MB4_HOTMON_LOW));
1971*4882a593Smuzhiyun 	writeb(high, (tcdm_base + PRCM_REQ_MB4_HOTMON_HIGH));
1972*4882a593Smuzhiyun 	writeb((HOTMON_CONFIG_LOW | HOTMON_CONFIG_HIGH),
1973*4882a593Smuzhiyun 		(tcdm_base + PRCM_REQ_MB4_HOTMON_CONFIG));
1974*4882a593Smuzhiyun 	writeb(MB4H_HOTMON, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4));
1975*4882a593Smuzhiyun 
1976*4882a593Smuzhiyun 	writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET);
1977*4882a593Smuzhiyun 	wait_for_completion(&mb4_transfer.work);
1978*4882a593Smuzhiyun 
1979*4882a593Smuzhiyun 	mutex_unlock(&mb4_transfer.lock);
1980*4882a593Smuzhiyun 
1981*4882a593Smuzhiyun 	return 0;
1982*4882a593Smuzhiyun }
1983*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(db8500_prcmu_config_hotmon);
1984*4882a593Smuzhiyun 
config_hot_period(u16 val)1985*4882a593Smuzhiyun static int config_hot_period(u16 val)
1986*4882a593Smuzhiyun {
1987*4882a593Smuzhiyun 	mutex_lock(&mb4_transfer.lock);
1988*4882a593Smuzhiyun 
1989*4882a593Smuzhiyun 	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
1990*4882a593Smuzhiyun 		cpu_relax();
1991*4882a593Smuzhiyun 
1992*4882a593Smuzhiyun 	writew(val, (tcdm_base + PRCM_REQ_MB4_HOT_PERIOD));
1993*4882a593Smuzhiyun 	writeb(MB4H_HOT_PERIOD, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4));
1994*4882a593Smuzhiyun 
1995*4882a593Smuzhiyun 	writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET);
1996*4882a593Smuzhiyun 	wait_for_completion(&mb4_transfer.work);
1997*4882a593Smuzhiyun 
1998*4882a593Smuzhiyun 	mutex_unlock(&mb4_transfer.lock);
1999*4882a593Smuzhiyun 
2000*4882a593Smuzhiyun 	return 0;
2001*4882a593Smuzhiyun }
2002*4882a593Smuzhiyun 
db8500_prcmu_start_temp_sense(u16 cycles32k)2003*4882a593Smuzhiyun int db8500_prcmu_start_temp_sense(u16 cycles32k)
2004*4882a593Smuzhiyun {
2005*4882a593Smuzhiyun 	if (cycles32k == 0xFFFF)
2006*4882a593Smuzhiyun 		return -EINVAL;
2007*4882a593Smuzhiyun 
2008*4882a593Smuzhiyun 	return config_hot_period(cycles32k);
2009*4882a593Smuzhiyun }
2010*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(db8500_prcmu_start_temp_sense);
2011*4882a593Smuzhiyun 
db8500_prcmu_stop_temp_sense(void)2012*4882a593Smuzhiyun int db8500_prcmu_stop_temp_sense(void)
2013*4882a593Smuzhiyun {
2014*4882a593Smuzhiyun 	return config_hot_period(0xFFFF);
2015*4882a593Smuzhiyun }
2016*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(db8500_prcmu_stop_temp_sense);
2017*4882a593Smuzhiyun 
prcmu_a9wdog(u8 cmd,u8 d0,u8 d1,u8 d2,u8 d3)2018*4882a593Smuzhiyun static int prcmu_a9wdog(u8 cmd, u8 d0, u8 d1, u8 d2, u8 d3)
2019*4882a593Smuzhiyun {
2020*4882a593Smuzhiyun 
2021*4882a593Smuzhiyun 	mutex_lock(&mb4_transfer.lock);
2022*4882a593Smuzhiyun 
2023*4882a593Smuzhiyun 	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
2024*4882a593Smuzhiyun 		cpu_relax();
2025*4882a593Smuzhiyun 
2026*4882a593Smuzhiyun 	writeb(d0, (tcdm_base + PRCM_REQ_MB4_A9WDOG_0));
2027*4882a593Smuzhiyun 	writeb(d1, (tcdm_base + PRCM_REQ_MB4_A9WDOG_1));
2028*4882a593Smuzhiyun 	writeb(d2, (tcdm_base + PRCM_REQ_MB4_A9WDOG_2));
2029*4882a593Smuzhiyun 	writeb(d3, (tcdm_base + PRCM_REQ_MB4_A9WDOG_3));
2030*4882a593Smuzhiyun 
2031*4882a593Smuzhiyun 	writeb(cmd, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4));
2032*4882a593Smuzhiyun 
2033*4882a593Smuzhiyun 	writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET);
2034*4882a593Smuzhiyun 	wait_for_completion(&mb4_transfer.work);
2035*4882a593Smuzhiyun 
2036*4882a593Smuzhiyun 	mutex_unlock(&mb4_transfer.lock);
2037*4882a593Smuzhiyun 
2038*4882a593Smuzhiyun 	return 0;
2039*4882a593Smuzhiyun 
2040*4882a593Smuzhiyun }
2041*4882a593Smuzhiyun 
db8500_prcmu_config_a9wdog(u8 num,bool sleep_auto_off)2042*4882a593Smuzhiyun int db8500_prcmu_config_a9wdog(u8 num, bool sleep_auto_off)
2043*4882a593Smuzhiyun {
2044*4882a593Smuzhiyun 	BUG_ON(num == 0 || num > 0xf);
2045*4882a593Smuzhiyun 	return prcmu_a9wdog(MB4H_A9WDOG_CONF, num, 0, 0,
2046*4882a593Smuzhiyun 			    sleep_auto_off ? A9WDOG_AUTO_OFF_EN :
2047*4882a593Smuzhiyun 			    A9WDOG_AUTO_OFF_DIS);
2048*4882a593Smuzhiyun }
2049*4882a593Smuzhiyun EXPORT_SYMBOL(db8500_prcmu_config_a9wdog);
2050*4882a593Smuzhiyun 
db8500_prcmu_enable_a9wdog(u8 id)2051*4882a593Smuzhiyun int db8500_prcmu_enable_a9wdog(u8 id)
2052*4882a593Smuzhiyun {
2053*4882a593Smuzhiyun 	return prcmu_a9wdog(MB4H_A9WDOG_EN, id, 0, 0, 0);
2054*4882a593Smuzhiyun }
2055*4882a593Smuzhiyun EXPORT_SYMBOL(db8500_prcmu_enable_a9wdog);
2056*4882a593Smuzhiyun 
db8500_prcmu_disable_a9wdog(u8 id)2057*4882a593Smuzhiyun int db8500_prcmu_disable_a9wdog(u8 id)
2058*4882a593Smuzhiyun {
2059*4882a593Smuzhiyun 	return prcmu_a9wdog(MB4H_A9WDOG_DIS, id, 0, 0, 0);
2060*4882a593Smuzhiyun }
2061*4882a593Smuzhiyun EXPORT_SYMBOL(db8500_prcmu_disable_a9wdog);
2062*4882a593Smuzhiyun 
db8500_prcmu_kick_a9wdog(u8 id)2063*4882a593Smuzhiyun int db8500_prcmu_kick_a9wdog(u8 id)
2064*4882a593Smuzhiyun {
2065*4882a593Smuzhiyun 	return prcmu_a9wdog(MB4H_A9WDOG_KICK, id, 0, 0, 0);
2066*4882a593Smuzhiyun }
2067*4882a593Smuzhiyun EXPORT_SYMBOL(db8500_prcmu_kick_a9wdog);
2068*4882a593Smuzhiyun 
2069*4882a593Smuzhiyun /*
2070*4882a593Smuzhiyun  * timeout is 28 bit, in ms.
2071*4882a593Smuzhiyun  */
db8500_prcmu_load_a9wdog(u8 id,u32 timeout)2072*4882a593Smuzhiyun int db8500_prcmu_load_a9wdog(u8 id, u32 timeout)
2073*4882a593Smuzhiyun {
2074*4882a593Smuzhiyun 	return prcmu_a9wdog(MB4H_A9WDOG_LOAD,
2075*4882a593Smuzhiyun 			    (id & A9WDOG_ID_MASK) |
2076*4882a593Smuzhiyun 			    /*
2077*4882a593Smuzhiyun 			     * Put the lowest 28 bits of timeout at
2078*4882a593Smuzhiyun 			     * offset 4. Four first bits are used for id.
2079*4882a593Smuzhiyun 			     */
2080*4882a593Smuzhiyun 			    (u8)((timeout << 4) & 0xf0),
2081*4882a593Smuzhiyun 			    (u8)((timeout >> 4) & 0xff),
2082*4882a593Smuzhiyun 			    (u8)((timeout >> 12) & 0xff),
2083*4882a593Smuzhiyun 			    (u8)((timeout >> 20) & 0xff));
2084*4882a593Smuzhiyun }
2085*4882a593Smuzhiyun EXPORT_SYMBOL(db8500_prcmu_load_a9wdog);
2086*4882a593Smuzhiyun 
2087*4882a593Smuzhiyun /**
2088*4882a593Smuzhiyun  * prcmu_abb_read() - Read register value(s) from the ABB.
2089*4882a593Smuzhiyun  * @slave:	The I2C slave address.
2090*4882a593Smuzhiyun  * @reg:	The (start) register address.
2091*4882a593Smuzhiyun  * @value:	The read out value(s).
2092*4882a593Smuzhiyun  * @size:	The number of registers to read.
2093*4882a593Smuzhiyun  *
2094*4882a593Smuzhiyun  * Reads register value(s) from the ABB.
2095*4882a593Smuzhiyun  * @size has to be 1 for the current firmware version.
2096*4882a593Smuzhiyun  */
prcmu_abb_read(u8 slave,u8 reg,u8 * value,u8 size)2097*4882a593Smuzhiyun int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size)
2098*4882a593Smuzhiyun {
2099*4882a593Smuzhiyun 	int r;
2100*4882a593Smuzhiyun 
2101*4882a593Smuzhiyun 	if (size != 1)
2102*4882a593Smuzhiyun 		return -EINVAL;
2103*4882a593Smuzhiyun 
2104*4882a593Smuzhiyun 	mutex_lock(&mb5_transfer.lock);
2105*4882a593Smuzhiyun 
2106*4882a593Smuzhiyun 	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
2107*4882a593Smuzhiyun 		cpu_relax();
2108*4882a593Smuzhiyun 
2109*4882a593Smuzhiyun 	writeb(0, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB5));
2110*4882a593Smuzhiyun 	writeb(PRCMU_I2C_READ(slave), (tcdm_base + PRCM_REQ_MB5_I2C_SLAVE_OP));
2111*4882a593Smuzhiyun 	writeb(PRCMU_I2C_STOP_EN, (tcdm_base + PRCM_REQ_MB5_I2C_HW_BITS));
2112*4882a593Smuzhiyun 	writeb(reg, (tcdm_base + PRCM_REQ_MB5_I2C_REG));
2113*4882a593Smuzhiyun 	writeb(0, (tcdm_base + PRCM_REQ_MB5_I2C_VAL));
2114*4882a593Smuzhiyun 
2115*4882a593Smuzhiyun 	writel(MBOX_BIT(5), PRCM_MBOX_CPU_SET);
2116*4882a593Smuzhiyun 
2117*4882a593Smuzhiyun 	if (!wait_for_completion_timeout(&mb5_transfer.work,
2118*4882a593Smuzhiyun 				msecs_to_jiffies(20000))) {
2119*4882a593Smuzhiyun 		pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
2120*4882a593Smuzhiyun 			__func__);
2121*4882a593Smuzhiyun 		r = -EIO;
2122*4882a593Smuzhiyun 	} else {
2123*4882a593Smuzhiyun 		r = ((mb5_transfer.ack.status == I2C_RD_OK) ? 0 : -EIO);
2124*4882a593Smuzhiyun 	}
2125*4882a593Smuzhiyun 
2126*4882a593Smuzhiyun 	if (!r)
2127*4882a593Smuzhiyun 		*value = mb5_transfer.ack.value;
2128*4882a593Smuzhiyun 
2129*4882a593Smuzhiyun 	mutex_unlock(&mb5_transfer.lock);
2130*4882a593Smuzhiyun 
2131*4882a593Smuzhiyun 	return r;
2132*4882a593Smuzhiyun }
2133*4882a593Smuzhiyun 
2134*4882a593Smuzhiyun /**
2135*4882a593Smuzhiyun  * prcmu_abb_write_masked() - Write masked register value(s) to the ABB.
2136*4882a593Smuzhiyun  * @slave:	The I2C slave address.
2137*4882a593Smuzhiyun  * @reg:	The (start) register address.
2138*4882a593Smuzhiyun  * @value:	The value(s) to write.
2139*4882a593Smuzhiyun  * @mask:	The mask(s) to use.
2140*4882a593Smuzhiyun  * @size:	The number of registers to write.
2141*4882a593Smuzhiyun  *
2142*4882a593Smuzhiyun  * Writes masked register value(s) to the ABB.
2143*4882a593Smuzhiyun  * For each @value, only the bits set to 1 in the corresponding @mask
2144*4882a593Smuzhiyun  * will be written. The other bits are not changed.
2145*4882a593Smuzhiyun  * @size has to be 1 for the current firmware version.
2146*4882a593Smuzhiyun  */
prcmu_abb_write_masked(u8 slave,u8 reg,u8 * value,u8 * mask,u8 size)2147*4882a593Smuzhiyun int prcmu_abb_write_masked(u8 slave, u8 reg, u8 *value, u8 *mask, u8 size)
2148*4882a593Smuzhiyun {
2149*4882a593Smuzhiyun 	int r;
2150*4882a593Smuzhiyun 
2151*4882a593Smuzhiyun 	if (size != 1)
2152*4882a593Smuzhiyun 		return -EINVAL;
2153*4882a593Smuzhiyun 
2154*4882a593Smuzhiyun 	mutex_lock(&mb5_transfer.lock);
2155*4882a593Smuzhiyun 
2156*4882a593Smuzhiyun 	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
2157*4882a593Smuzhiyun 		cpu_relax();
2158*4882a593Smuzhiyun 
2159*4882a593Smuzhiyun 	writeb(~*mask, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB5));
2160*4882a593Smuzhiyun 	writeb(PRCMU_I2C_WRITE(slave), (tcdm_base + PRCM_REQ_MB5_I2C_SLAVE_OP));
2161*4882a593Smuzhiyun 	writeb(PRCMU_I2C_STOP_EN, (tcdm_base + PRCM_REQ_MB5_I2C_HW_BITS));
2162*4882a593Smuzhiyun 	writeb(reg, (tcdm_base + PRCM_REQ_MB5_I2C_REG));
2163*4882a593Smuzhiyun 	writeb(*value, (tcdm_base + PRCM_REQ_MB5_I2C_VAL));
2164*4882a593Smuzhiyun 
2165*4882a593Smuzhiyun 	writel(MBOX_BIT(5), PRCM_MBOX_CPU_SET);
2166*4882a593Smuzhiyun 
2167*4882a593Smuzhiyun 	if (!wait_for_completion_timeout(&mb5_transfer.work,
2168*4882a593Smuzhiyun 				msecs_to_jiffies(20000))) {
2169*4882a593Smuzhiyun 		pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
2170*4882a593Smuzhiyun 			__func__);
2171*4882a593Smuzhiyun 		r = -EIO;
2172*4882a593Smuzhiyun 	} else {
2173*4882a593Smuzhiyun 		r = ((mb5_transfer.ack.status == I2C_WR_OK) ? 0 : -EIO);
2174*4882a593Smuzhiyun 	}
2175*4882a593Smuzhiyun 
2176*4882a593Smuzhiyun 	mutex_unlock(&mb5_transfer.lock);
2177*4882a593Smuzhiyun 
2178*4882a593Smuzhiyun 	return r;
2179*4882a593Smuzhiyun }
2180*4882a593Smuzhiyun 
2181*4882a593Smuzhiyun /**
2182*4882a593Smuzhiyun  * prcmu_abb_write() - Write register value(s) to the ABB.
2183*4882a593Smuzhiyun  * @slave:	The I2C slave address.
2184*4882a593Smuzhiyun  * @reg:	The (start) register address.
2185*4882a593Smuzhiyun  * @value:	The value(s) to write.
2186*4882a593Smuzhiyun  * @size:	The number of registers to write.
2187*4882a593Smuzhiyun  *
2188*4882a593Smuzhiyun  * Writes register value(s) to the ABB.
2189*4882a593Smuzhiyun  * @size has to be 1 for the current firmware version.
2190*4882a593Smuzhiyun  */
prcmu_abb_write(u8 slave,u8 reg,u8 * value,u8 size)2191*4882a593Smuzhiyun int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
2192*4882a593Smuzhiyun {
2193*4882a593Smuzhiyun 	u8 mask = ~0;
2194*4882a593Smuzhiyun 
2195*4882a593Smuzhiyun 	return prcmu_abb_write_masked(slave, reg, value, &mask, size);
2196*4882a593Smuzhiyun }
2197*4882a593Smuzhiyun 
2198*4882a593Smuzhiyun /**
2199*4882a593Smuzhiyun  * prcmu_ac_wake_req - should be called whenever ARM wants to wakeup Modem
2200*4882a593Smuzhiyun  */
prcmu_ac_wake_req(void)2201*4882a593Smuzhiyun int prcmu_ac_wake_req(void)
2202*4882a593Smuzhiyun {
2203*4882a593Smuzhiyun 	u32 val;
2204*4882a593Smuzhiyun 	int ret = 0;
2205*4882a593Smuzhiyun 
2206*4882a593Smuzhiyun 	mutex_lock(&mb0_transfer.ac_wake_lock);
2207*4882a593Smuzhiyun 
2208*4882a593Smuzhiyun 	val = readl(PRCM_HOSTACCESS_REQ);
2209*4882a593Smuzhiyun 	if (val & PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ)
2210*4882a593Smuzhiyun 		goto unlock_and_return;
2211*4882a593Smuzhiyun 
2212*4882a593Smuzhiyun 	atomic_set(&ac_wake_req_state, 1);
2213*4882a593Smuzhiyun 
2214*4882a593Smuzhiyun 	/*
2215*4882a593Smuzhiyun 	 * Force Modem Wake-up before hostaccess_req ping-pong.
2216*4882a593Smuzhiyun 	 * It prevents Modem to enter in Sleep while acking the hostaccess
2217*4882a593Smuzhiyun 	 * request. The 31us delay has been calculated by HWI.
2218*4882a593Smuzhiyun 	 */
2219*4882a593Smuzhiyun 	val |= PRCM_HOSTACCESS_REQ_WAKE_REQ;
2220*4882a593Smuzhiyun 	writel(val, PRCM_HOSTACCESS_REQ);
2221*4882a593Smuzhiyun 
2222*4882a593Smuzhiyun 	udelay(31);
2223*4882a593Smuzhiyun 
2224*4882a593Smuzhiyun 	val |= PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ;
2225*4882a593Smuzhiyun 	writel(val, PRCM_HOSTACCESS_REQ);
2226*4882a593Smuzhiyun 
2227*4882a593Smuzhiyun 	if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
2228*4882a593Smuzhiyun 			msecs_to_jiffies(5000))) {
2229*4882a593Smuzhiyun 		pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n",
2230*4882a593Smuzhiyun 			__func__);
2231*4882a593Smuzhiyun 		ret = -EFAULT;
2232*4882a593Smuzhiyun 	}
2233*4882a593Smuzhiyun 
2234*4882a593Smuzhiyun unlock_and_return:
2235*4882a593Smuzhiyun 	mutex_unlock(&mb0_transfer.ac_wake_lock);
2236*4882a593Smuzhiyun 	return ret;
2237*4882a593Smuzhiyun }
2238*4882a593Smuzhiyun 
2239*4882a593Smuzhiyun /**
2240*4882a593Smuzhiyun  * prcmu_ac_sleep_req - called when ARM no longer needs to talk to modem
2241*4882a593Smuzhiyun  */
prcmu_ac_sleep_req(void)2242*4882a593Smuzhiyun void prcmu_ac_sleep_req(void)
2243*4882a593Smuzhiyun {
2244*4882a593Smuzhiyun 	u32 val;
2245*4882a593Smuzhiyun 
2246*4882a593Smuzhiyun 	mutex_lock(&mb0_transfer.ac_wake_lock);
2247*4882a593Smuzhiyun 
2248*4882a593Smuzhiyun 	val = readl(PRCM_HOSTACCESS_REQ);
2249*4882a593Smuzhiyun 	if (!(val & PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ))
2250*4882a593Smuzhiyun 		goto unlock_and_return;
2251*4882a593Smuzhiyun 
2252*4882a593Smuzhiyun 	writel((val & ~PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ),
2253*4882a593Smuzhiyun 		PRCM_HOSTACCESS_REQ);
2254*4882a593Smuzhiyun 
2255*4882a593Smuzhiyun 	if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
2256*4882a593Smuzhiyun 			msecs_to_jiffies(5000))) {
2257*4882a593Smuzhiyun 		pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n",
2258*4882a593Smuzhiyun 			__func__);
2259*4882a593Smuzhiyun 	}
2260*4882a593Smuzhiyun 
2261*4882a593Smuzhiyun 	atomic_set(&ac_wake_req_state, 0);
2262*4882a593Smuzhiyun 
2263*4882a593Smuzhiyun unlock_and_return:
2264*4882a593Smuzhiyun 	mutex_unlock(&mb0_transfer.ac_wake_lock);
2265*4882a593Smuzhiyun }
2266*4882a593Smuzhiyun 
db8500_prcmu_is_ac_wake_requested(void)2267*4882a593Smuzhiyun bool db8500_prcmu_is_ac_wake_requested(void)
2268*4882a593Smuzhiyun {
2269*4882a593Smuzhiyun 	return (atomic_read(&ac_wake_req_state) != 0);
2270*4882a593Smuzhiyun }
2271*4882a593Smuzhiyun 
2272*4882a593Smuzhiyun /**
2273*4882a593Smuzhiyun  * db8500_prcmu_system_reset - System reset
2274*4882a593Smuzhiyun  *
2275*4882a593Smuzhiyun  * Saves the reset reason code and then sets the APE_SOFTRST register which
2276*4882a593Smuzhiyun  * fires interrupt to fw
2277*4882a593Smuzhiyun  *
2278*4882a593Smuzhiyun  * @reset_code: The reason for system reset
2279*4882a593Smuzhiyun  */
db8500_prcmu_system_reset(u16 reset_code)2280*4882a593Smuzhiyun void db8500_prcmu_system_reset(u16 reset_code)
2281*4882a593Smuzhiyun {
2282*4882a593Smuzhiyun 	writew(reset_code, (tcdm_base + PRCM_SW_RST_REASON));
2283*4882a593Smuzhiyun 	writel(1, PRCM_APE_SOFTRST);
2284*4882a593Smuzhiyun }
2285*4882a593Smuzhiyun 
2286*4882a593Smuzhiyun /**
2287*4882a593Smuzhiyun  * db8500_prcmu_get_reset_code - Retrieve SW reset reason code
2288*4882a593Smuzhiyun  *
2289*4882a593Smuzhiyun  * Retrieves the reset reason code stored by prcmu_system_reset() before
2290*4882a593Smuzhiyun  * last restart.
2291*4882a593Smuzhiyun  */
db8500_prcmu_get_reset_code(void)2292*4882a593Smuzhiyun u16 db8500_prcmu_get_reset_code(void)
2293*4882a593Smuzhiyun {
2294*4882a593Smuzhiyun 	return readw(tcdm_base + PRCM_SW_RST_REASON);
2295*4882a593Smuzhiyun }
2296*4882a593Smuzhiyun 
2297*4882a593Smuzhiyun /**
2298*4882a593Smuzhiyun  * db8500_prcmu_reset_modem - ask the PRCMU to reset modem
2299*4882a593Smuzhiyun  */
db8500_prcmu_modem_reset(void)2300*4882a593Smuzhiyun void db8500_prcmu_modem_reset(void)
2301*4882a593Smuzhiyun {
2302*4882a593Smuzhiyun 	mutex_lock(&mb1_transfer.lock);
2303*4882a593Smuzhiyun 
2304*4882a593Smuzhiyun 	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
2305*4882a593Smuzhiyun 		cpu_relax();
2306*4882a593Smuzhiyun 
2307*4882a593Smuzhiyun 	writeb(MB1H_RESET_MODEM, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
2308*4882a593Smuzhiyun 	writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
2309*4882a593Smuzhiyun 	wait_for_completion(&mb1_transfer.work);
2310*4882a593Smuzhiyun 
2311*4882a593Smuzhiyun 	/*
2312*4882a593Smuzhiyun 	 * No need to check return from PRCMU as modem should go in reset state
2313*4882a593Smuzhiyun 	 * This state is already managed by upper layer
2314*4882a593Smuzhiyun 	 */
2315*4882a593Smuzhiyun 
2316*4882a593Smuzhiyun 	mutex_unlock(&mb1_transfer.lock);
2317*4882a593Smuzhiyun }
2318*4882a593Smuzhiyun 
ack_dbb_wakeup(void)2319*4882a593Smuzhiyun static void ack_dbb_wakeup(void)
2320*4882a593Smuzhiyun {
2321*4882a593Smuzhiyun 	unsigned long flags;
2322*4882a593Smuzhiyun 
2323*4882a593Smuzhiyun 	spin_lock_irqsave(&mb0_transfer.lock, flags);
2324*4882a593Smuzhiyun 
2325*4882a593Smuzhiyun 	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
2326*4882a593Smuzhiyun 		cpu_relax();
2327*4882a593Smuzhiyun 
2328*4882a593Smuzhiyun 	writeb(MB0H_READ_WAKEUP_ACK, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB0));
2329*4882a593Smuzhiyun 	writel(MBOX_BIT(0), PRCM_MBOX_CPU_SET);
2330*4882a593Smuzhiyun 
2331*4882a593Smuzhiyun 	spin_unlock_irqrestore(&mb0_transfer.lock, flags);
2332*4882a593Smuzhiyun }
2333*4882a593Smuzhiyun 
print_unknown_header_warning(u8 n,u8 header)2334*4882a593Smuzhiyun static inline void print_unknown_header_warning(u8 n, u8 header)
2335*4882a593Smuzhiyun {
2336*4882a593Smuzhiyun 	pr_warn("prcmu: Unknown message header (%d) in mailbox %d\n",
2337*4882a593Smuzhiyun 		header, n);
2338*4882a593Smuzhiyun }
2339*4882a593Smuzhiyun 
read_mailbox_0(void)2340*4882a593Smuzhiyun static bool read_mailbox_0(void)
2341*4882a593Smuzhiyun {
2342*4882a593Smuzhiyun 	bool r;
2343*4882a593Smuzhiyun 	u32 ev;
2344*4882a593Smuzhiyun 	unsigned int n;
2345*4882a593Smuzhiyun 	u8 header;
2346*4882a593Smuzhiyun 
2347*4882a593Smuzhiyun 	header = readb(tcdm_base + PRCM_MBOX_HEADER_ACK_MB0);
2348*4882a593Smuzhiyun 	switch (header) {
2349*4882a593Smuzhiyun 	case MB0H_WAKEUP_EXE:
2350*4882a593Smuzhiyun 	case MB0H_WAKEUP_SLEEP:
2351*4882a593Smuzhiyun 		if (readb(tcdm_base + PRCM_ACK_MB0_READ_POINTER) & 1)
2352*4882a593Smuzhiyun 			ev = readl(tcdm_base + PRCM_ACK_MB0_WAKEUP_1_8500);
2353*4882a593Smuzhiyun 		else
2354*4882a593Smuzhiyun 			ev = readl(tcdm_base + PRCM_ACK_MB0_WAKEUP_0_8500);
2355*4882a593Smuzhiyun 
2356*4882a593Smuzhiyun 		if (ev & (WAKEUP_BIT_AC_WAKE_ACK | WAKEUP_BIT_AC_SLEEP_ACK))
2357*4882a593Smuzhiyun 			complete(&mb0_transfer.ac_wake_work);
2358*4882a593Smuzhiyun 		if (ev & WAKEUP_BIT_SYSCLK_OK)
2359*4882a593Smuzhiyun 			complete(&mb3_transfer.sysclk_work);
2360*4882a593Smuzhiyun 
2361*4882a593Smuzhiyun 		ev &= mb0_transfer.req.dbb_irqs;
2362*4882a593Smuzhiyun 
2363*4882a593Smuzhiyun 		for (n = 0; n < NUM_PRCMU_WAKEUPS; n++) {
2364*4882a593Smuzhiyun 			if (ev & prcmu_irq_bit[n])
2365*4882a593Smuzhiyun 				generic_handle_irq(irq_find_mapping(db8500_irq_domain, n));
2366*4882a593Smuzhiyun 		}
2367*4882a593Smuzhiyun 		r = true;
2368*4882a593Smuzhiyun 		break;
2369*4882a593Smuzhiyun 	default:
2370*4882a593Smuzhiyun 		print_unknown_header_warning(0, header);
2371*4882a593Smuzhiyun 		r = false;
2372*4882a593Smuzhiyun 		break;
2373*4882a593Smuzhiyun 	}
2374*4882a593Smuzhiyun 	writel(MBOX_BIT(0), PRCM_ARM_IT1_CLR);
2375*4882a593Smuzhiyun 	return r;
2376*4882a593Smuzhiyun }
2377*4882a593Smuzhiyun 
read_mailbox_1(void)2378*4882a593Smuzhiyun static bool read_mailbox_1(void)
2379*4882a593Smuzhiyun {
2380*4882a593Smuzhiyun 	mb1_transfer.ack.header = readb(tcdm_base + PRCM_MBOX_HEADER_REQ_MB1);
2381*4882a593Smuzhiyun 	mb1_transfer.ack.arm_opp = readb(tcdm_base +
2382*4882a593Smuzhiyun 		PRCM_ACK_MB1_CURRENT_ARM_OPP);
2383*4882a593Smuzhiyun 	mb1_transfer.ack.ape_opp = readb(tcdm_base +
2384*4882a593Smuzhiyun 		PRCM_ACK_MB1_CURRENT_APE_OPP);
2385*4882a593Smuzhiyun 	mb1_transfer.ack.ape_voltage_status = readb(tcdm_base +
2386*4882a593Smuzhiyun 		PRCM_ACK_MB1_APE_VOLTAGE_STATUS);
2387*4882a593Smuzhiyun 	writel(MBOX_BIT(1), PRCM_ARM_IT1_CLR);
2388*4882a593Smuzhiyun 	complete(&mb1_transfer.work);
2389*4882a593Smuzhiyun 	return false;
2390*4882a593Smuzhiyun }
2391*4882a593Smuzhiyun 
read_mailbox_2(void)2392*4882a593Smuzhiyun static bool read_mailbox_2(void)
2393*4882a593Smuzhiyun {
2394*4882a593Smuzhiyun 	mb2_transfer.ack.status = readb(tcdm_base + PRCM_ACK_MB2_DPS_STATUS);
2395*4882a593Smuzhiyun 	writel(MBOX_BIT(2), PRCM_ARM_IT1_CLR);
2396*4882a593Smuzhiyun 	complete(&mb2_transfer.work);
2397*4882a593Smuzhiyun 	return false;
2398*4882a593Smuzhiyun }
2399*4882a593Smuzhiyun 
read_mailbox_3(void)2400*4882a593Smuzhiyun static bool read_mailbox_3(void)
2401*4882a593Smuzhiyun {
2402*4882a593Smuzhiyun 	writel(MBOX_BIT(3), PRCM_ARM_IT1_CLR);
2403*4882a593Smuzhiyun 	return false;
2404*4882a593Smuzhiyun }
2405*4882a593Smuzhiyun 
read_mailbox_4(void)2406*4882a593Smuzhiyun static bool read_mailbox_4(void)
2407*4882a593Smuzhiyun {
2408*4882a593Smuzhiyun 	u8 header;
2409*4882a593Smuzhiyun 	bool do_complete = true;
2410*4882a593Smuzhiyun 
2411*4882a593Smuzhiyun 	header = readb(tcdm_base + PRCM_MBOX_HEADER_REQ_MB4);
2412*4882a593Smuzhiyun 	switch (header) {
2413*4882a593Smuzhiyun 	case MB4H_MEM_ST:
2414*4882a593Smuzhiyun 	case MB4H_HOTDOG:
2415*4882a593Smuzhiyun 	case MB4H_HOTMON:
2416*4882a593Smuzhiyun 	case MB4H_HOT_PERIOD:
2417*4882a593Smuzhiyun 	case MB4H_A9WDOG_CONF:
2418*4882a593Smuzhiyun 	case MB4H_A9WDOG_EN:
2419*4882a593Smuzhiyun 	case MB4H_A9WDOG_DIS:
2420*4882a593Smuzhiyun 	case MB4H_A9WDOG_LOAD:
2421*4882a593Smuzhiyun 	case MB4H_A9WDOG_KICK:
2422*4882a593Smuzhiyun 		break;
2423*4882a593Smuzhiyun 	default:
2424*4882a593Smuzhiyun 		print_unknown_header_warning(4, header);
2425*4882a593Smuzhiyun 		do_complete = false;
2426*4882a593Smuzhiyun 		break;
2427*4882a593Smuzhiyun 	}
2428*4882a593Smuzhiyun 
2429*4882a593Smuzhiyun 	writel(MBOX_BIT(4), PRCM_ARM_IT1_CLR);
2430*4882a593Smuzhiyun 
2431*4882a593Smuzhiyun 	if (do_complete)
2432*4882a593Smuzhiyun 		complete(&mb4_transfer.work);
2433*4882a593Smuzhiyun 
2434*4882a593Smuzhiyun 	return false;
2435*4882a593Smuzhiyun }
2436*4882a593Smuzhiyun 
read_mailbox_5(void)2437*4882a593Smuzhiyun static bool read_mailbox_5(void)
2438*4882a593Smuzhiyun {
2439*4882a593Smuzhiyun 	mb5_transfer.ack.status = readb(tcdm_base + PRCM_ACK_MB5_I2C_STATUS);
2440*4882a593Smuzhiyun 	mb5_transfer.ack.value = readb(tcdm_base + PRCM_ACK_MB5_I2C_VAL);
2441*4882a593Smuzhiyun 	writel(MBOX_BIT(5), PRCM_ARM_IT1_CLR);
2442*4882a593Smuzhiyun 	complete(&mb5_transfer.work);
2443*4882a593Smuzhiyun 	return false;
2444*4882a593Smuzhiyun }
2445*4882a593Smuzhiyun 
read_mailbox_6(void)2446*4882a593Smuzhiyun static bool read_mailbox_6(void)
2447*4882a593Smuzhiyun {
2448*4882a593Smuzhiyun 	writel(MBOX_BIT(6), PRCM_ARM_IT1_CLR);
2449*4882a593Smuzhiyun 	return false;
2450*4882a593Smuzhiyun }
2451*4882a593Smuzhiyun 
read_mailbox_7(void)2452*4882a593Smuzhiyun static bool read_mailbox_7(void)
2453*4882a593Smuzhiyun {
2454*4882a593Smuzhiyun 	writel(MBOX_BIT(7), PRCM_ARM_IT1_CLR);
2455*4882a593Smuzhiyun 	return false;
2456*4882a593Smuzhiyun }
2457*4882a593Smuzhiyun 
2458*4882a593Smuzhiyun static bool (* const read_mailbox[NUM_MB])(void) = {
2459*4882a593Smuzhiyun 	read_mailbox_0,
2460*4882a593Smuzhiyun 	read_mailbox_1,
2461*4882a593Smuzhiyun 	read_mailbox_2,
2462*4882a593Smuzhiyun 	read_mailbox_3,
2463*4882a593Smuzhiyun 	read_mailbox_4,
2464*4882a593Smuzhiyun 	read_mailbox_5,
2465*4882a593Smuzhiyun 	read_mailbox_6,
2466*4882a593Smuzhiyun 	read_mailbox_7
2467*4882a593Smuzhiyun };
2468*4882a593Smuzhiyun 
prcmu_irq_handler(int irq,void * data)2469*4882a593Smuzhiyun static irqreturn_t prcmu_irq_handler(int irq, void *data)
2470*4882a593Smuzhiyun {
2471*4882a593Smuzhiyun 	u32 bits;
2472*4882a593Smuzhiyun 	u8 n;
2473*4882a593Smuzhiyun 	irqreturn_t r;
2474*4882a593Smuzhiyun 
2475*4882a593Smuzhiyun 	bits = (readl(PRCM_ARM_IT1_VAL) & ALL_MBOX_BITS);
2476*4882a593Smuzhiyun 	if (unlikely(!bits))
2477*4882a593Smuzhiyun 		return IRQ_NONE;
2478*4882a593Smuzhiyun 
2479*4882a593Smuzhiyun 	r = IRQ_HANDLED;
2480*4882a593Smuzhiyun 	for (n = 0; bits; n++) {
2481*4882a593Smuzhiyun 		if (bits & MBOX_BIT(n)) {
2482*4882a593Smuzhiyun 			bits -= MBOX_BIT(n);
2483*4882a593Smuzhiyun 			if (read_mailbox[n]())
2484*4882a593Smuzhiyun 				r = IRQ_WAKE_THREAD;
2485*4882a593Smuzhiyun 		}
2486*4882a593Smuzhiyun 	}
2487*4882a593Smuzhiyun 	return r;
2488*4882a593Smuzhiyun }
2489*4882a593Smuzhiyun 
prcmu_irq_thread_fn(int irq,void * data)2490*4882a593Smuzhiyun static irqreturn_t prcmu_irq_thread_fn(int irq, void *data)
2491*4882a593Smuzhiyun {
2492*4882a593Smuzhiyun 	ack_dbb_wakeup();
2493*4882a593Smuzhiyun 	return IRQ_HANDLED;
2494*4882a593Smuzhiyun }
2495*4882a593Smuzhiyun 
prcmu_mask_work(struct work_struct * work)2496*4882a593Smuzhiyun static void prcmu_mask_work(struct work_struct *work)
2497*4882a593Smuzhiyun {
2498*4882a593Smuzhiyun 	unsigned long flags;
2499*4882a593Smuzhiyun 
2500*4882a593Smuzhiyun 	spin_lock_irqsave(&mb0_transfer.lock, flags);
2501*4882a593Smuzhiyun 
2502*4882a593Smuzhiyun 	config_wakeups();
2503*4882a593Smuzhiyun 
2504*4882a593Smuzhiyun 	spin_unlock_irqrestore(&mb0_transfer.lock, flags);
2505*4882a593Smuzhiyun }
2506*4882a593Smuzhiyun 
prcmu_irq_mask(struct irq_data * d)2507*4882a593Smuzhiyun static void prcmu_irq_mask(struct irq_data *d)
2508*4882a593Smuzhiyun {
2509*4882a593Smuzhiyun 	unsigned long flags;
2510*4882a593Smuzhiyun 
2511*4882a593Smuzhiyun 	spin_lock_irqsave(&mb0_transfer.dbb_irqs_lock, flags);
2512*4882a593Smuzhiyun 
2513*4882a593Smuzhiyun 	mb0_transfer.req.dbb_irqs &= ~prcmu_irq_bit[d->hwirq];
2514*4882a593Smuzhiyun 
2515*4882a593Smuzhiyun 	spin_unlock_irqrestore(&mb0_transfer.dbb_irqs_lock, flags);
2516*4882a593Smuzhiyun 
2517*4882a593Smuzhiyun 	if (d->irq != IRQ_PRCMU_CA_SLEEP)
2518*4882a593Smuzhiyun 		schedule_work(&mb0_transfer.mask_work);
2519*4882a593Smuzhiyun }
2520*4882a593Smuzhiyun 
prcmu_irq_unmask(struct irq_data * d)2521*4882a593Smuzhiyun static void prcmu_irq_unmask(struct irq_data *d)
2522*4882a593Smuzhiyun {
2523*4882a593Smuzhiyun 	unsigned long flags;
2524*4882a593Smuzhiyun 
2525*4882a593Smuzhiyun 	spin_lock_irqsave(&mb0_transfer.dbb_irqs_lock, flags);
2526*4882a593Smuzhiyun 
2527*4882a593Smuzhiyun 	mb0_transfer.req.dbb_irqs |= prcmu_irq_bit[d->hwirq];
2528*4882a593Smuzhiyun 
2529*4882a593Smuzhiyun 	spin_unlock_irqrestore(&mb0_transfer.dbb_irqs_lock, flags);
2530*4882a593Smuzhiyun 
2531*4882a593Smuzhiyun 	if (d->irq != IRQ_PRCMU_CA_SLEEP)
2532*4882a593Smuzhiyun 		schedule_work(&mb0_transfer.mask_work);
2533*4882a593Smuzhiyun }
2534*4882a593Smuzhiyun 
noop(struct irq_data * d)2535*4882a593Smuzhiyun static void noop(struct irq_data *d)
2536*4882a593Smuzhiyun {
2537*4882a593Smuzhiyun }
2538*4882a593Smuzhiyun 
2539*4882a593Smuzhiyun static struct irq_chip prcmu_irq_chip = {
2540*4882a593Smuzhiyun 	.name		= "prcmu",
2541*4882a593Smuzhiyun 	.irq_disable	= prcmu_irq_mask,
2542*4882a593Smuzhiyun 	.irq_ack	= noop,
2543*4882a593Smuzhiyun 	.irq_mask	= prcmu_irq_mask,
2544*4882a593Smuzhiyun 	.irq_unmask	= prcmu_irq_unmask,
2545*4882a593Smuzhiyun };
2546*4882a593Smuzhiyun 
fw_project_name(u32 project)2547*4882a593Smuzhiyun static char *fw_project_name(u32 project)
2548*4882a593Smuzhiyun {
2549*4882a593Smuzhiyun 	switch (project) {
2550*4882a593Smuzhiyun 	case PRCMU_FW_PROJECT_U8500:
2551*4882a593Smuzhiyun 		return "U8500";
2552*4882a593Smuzhiyun 	case PRCMU_FW_PROJECT_U8400:
2553*4882a593Smuzhiyun 		return "U8400";
2554*4882a593Smuzhiyun 	case PRCMU_FW_PROJECT_U9500:
2555*4882a593Smuzhiyun 		return "U9500";
2556*4882a593Smuzhiyun 	case PRCMU_FW_PROJECT_U8500_MBB:
2557*4882a593Smuzhiyun 		return "U8500 MBB";
2558*4882a593Smuzhiyun 	case PRCMU_FW_PROJECT_U8500_C1:
2559*4882a593Smuzhiyun 		return "U8500 C1";
2560*4882a593Smuzhiyun 	case PRCMU_FW_PROJECT_U8500_C2:
2561*4882a593Smuzhiyun 		return "U8500 C2";
2562*4882a593Smuzhiyun 	case PRCMU_FW_PROJECT_U8500_C3:
2563*4882a593Smuzhiyun 		return "U8500 C3";
2564*4882a593Smuzhiyun 	case PRCMU_FW_PROJECT_U8500_C4:
2565*4882a593Smuzhiyun 		return "U8500 C4";
2566*4882a593Smuzhiyun 	case PRCMU_FW_PROJECT_U9500_MBL:
2567*4882a593Smuzhiyun 		return "U9500 MBL";
2568*4882a593Smuzhiyun 	case PRCMU_FW_PROJECT_U8500_MBL:
2569*4882a593Smuzhiyun 		return "U8500 MBL";
2570*4882a593Smuzhiyun 	case PRCMU_FW_PROJECT_U8500_MBL2:
2571*4882a593Smuzhiyun 		return "U8500 MBL2";
2572*4882a593Smuzhiyun 	case PRCMU_FW_PROJECT_U8520:
2573*4882a593Smuzhiyun 		return "U8520 MBL";
2574*4882a593Smuzhiyun 	case PRCMU_FW_PROJECT_U8420:
2575*4882a593Smuzhiyun 		return "U8420";
2576*4882a593Smuzhiyun 	case PRCMU_FW_PROJECT_U8420_SYSCLK:
2577*4882a593Smuzhiyun 		return "U8420-sysclk";
2578*4882a593Smuzhiyun 	case PRCMU_FW_PROJECT_U9540:
2579*4882a593Smuzhiyun 		return "U9540";
2580*4882a593Smuzhiyun 	case PRCMU_FW_PROJECT_A9420:
2581*4882a593Smuzhiyun 		return "A9420";
2582*4882a593Smuzhiyun 	case PRCMU_FW_PROJECT_L8540:
2583*4882a593Smuzhiyun 		return "L8540";
2584*4882a593Smuzhiyun 	case PRCMU_FW_PROJECT_L8580:
2585*4882a593Smuzhiyun 		return "L8580";
2586*4882a593Smuzhiyun 	default:
2587*4882a593Smuzhiyun 		return "Unknown";
2588*4882a593Smuzhiyun 	}
2589*4882a593Smuzhiyun }
2590*4882a593Smuzhiyun 
db8500_irq_map(struct irq_domain * d,unsigned int virq,irq_hw_number_t hwirq)2591*4882a593Smuzhiyun static int db8500_irq_map(struct irq_domain *d, unsigned int virq,
2592*4882a593Smuzhiyun 				irq_hw_number_t hwirq)
2593*4882a593Smuzhiyun {
2594*4882a593Smuzhiyun 	irq_set_chip_and_handler(virq, &prcmu_irq_chip,
2595*4882a593Smuzhiyun 				handle_simple_irq);
2596*4882a593Smuzhiyun 
2597*4882a593Smuzhiyun 	return 0;
2598*4882a593Smuzhiyun }
2599*4882a593Smuzhiyun 
2600*4882a593Smuzhiyun static const struct irq_domain_ops db8500_irq_ops = {
2601*4882a593Smuzhiyun 	.map    = db8500_irq_map,
2602*4882a593Smuzhiyun 	.xlate  = irq_domain_xlate_twocell,
2603*4882a593Smuzhiyun };
2604*4882a593Smuzhiyun 
db8500_irq_init(struct device_node * np)2605*4882a593Smuzhiyun static int db8500_irq_init(struct device_node *np)
2606*4882a593Smuzhiyun {
2607*4882a593Smuzhiyun 	int i;
2608*4882a593Smuzhiyun 
2609*4882a593Smuzhiyun 	db8500_irq_domain = irq_domain_add_simple(
2610*4882a593Smuzhiyun 		np, NUM_PRCMU_WAKEUPS, 0,
2611*4882a593Smuzhiyun 		&db8500_irq_ops, NULL);
2612*4882a593Smuzhiyun 
2613*4882a593Smuzhiyun 	if (!db8500_irq_domain) {
2614*4882a593Smuzhiyun 		pr_err("Failed to create irqdomain\n");
2615*4882a593Smuzhiyun 		return -ENOSYS;
2616*4882a593Smuzhiyun 	}
2617*4882a593Smuzhiyun 
2618*4882a593Smuzhiyun 	/* All wakeups will be used, so create mappings for all */
2619*4882a593Smuzhiyun 	for (i = 0; i < NUM_PRCMU_WAKEUPS; i++)
2620*4882a593Smuzhiyun 		irq_create_mapping(db8500_irq_domain, i);
2621*4882a593Smuzhiyun 
2622*4882a593Smuzhiyun 	return 0;
2623*4882a593Smuzhiyun }
2624*4882a593Smuzhiyun 
dbx500_fw_version_init(struct device_node * np)2625*4882a593Smuzhiyun static void dbx500_fw_version_init(struct device_node *np)
2626*4882a593Smuzhiyun {
2627*4882a593Smuzhiyun 	void __iomem *tcpm_base;
2628*4882a593Smuzhiyun 	u32 version;
2629*4882a593Smuzhiyun 
2630*4882a593Smuzhiyun 	tcpm_base = of_iomap(np, 1);
2631*4882a593Smuzhiyun 	if (!tcpm_base) {
2632*4882a593Smuzhiyun 		pr_err("no prcmu tcpm mem region provided\n");
2633*4882a593Smuzhiyun 		return;
2634*4882a593Smuzhiyun 	}
2635*4882a593Smuzhiyun 
2636*4882a593Smuzhiyun 	version = readl(tcpm_base + DB8500_PRCMU_FW_VERSION_OFFSET);
2637*4882a593Smuzhiyun 	fw_info.version.project = (version & 0xFF);
2638*4882a593Smuzhiyun 	fw_info.version.api_version = (version >> 8) & 0xFF;
2639*4882a593Smuzhiyun 	fw_info.version.func_version = (version >> 16) & 0xFF;
2640*4882a593Smuzhiyun 	fw_info.version.errata = (version >> 24) & 0xFF;
2641*4882a593Smuzhiyun 	strncpy(fw_info.version.project_name,
2642*4882a593Smuzhiyun 		fw_project_name(fw_info.version.project),
2643*4882a593Smuzhiyun 		PRCMU_FW_PROJECT_NAME_LEN);
2644*4882a593Smuzhiyun 	fw_info.valid = true;
2645*4882a593Smuzhiyun 	pr_info("PRCMU firmware: %s(%d), version %d.%d.%d\n",
2646*4882a593Smuzhiyun 		fw_info.version.project_name,
2647*4882a593Smuzhiyun 		fw_info.version.project,
2648*4882a593Smuzhiyun 		fw_info.version.api_version,
2649*4882a593Smuzhiyun 		fw_info.version.func_version,
2650*4882a593Smuzhiyun 		fw_info.version.errata);
2651*4882a593Smuzhiyun 	iounmap(tcpm_base);
2652*4882a593Smuzhiyun }
2653*4882a593Smuzhiyun 
db8500_prcmu_early_init(void)2654*4882a593Smuzhiyun void __init db8500_prcmu_early_init(void)
2655*4882a593Smuzhiyun {
2656*4882a593Smuzhiyun 	/*
2657*4882a593Smuzhiyun 	 * This is a temporary remap to bring up the clocks. It is
2658*4882a593Smuzhiyun 	 * subsequently replaces with a real remap. After the merge of
2659*4882a593Smuzhiyun 	 * the mailbox subsystem all of this early code goes away, and the
2660*4882a593Smuzhiyun 	 * clock driver can probe independently. An early initcall will
2661*4882a593Smuzhiyun 	 * still be needed, but it can be diverted into drivers/clk/ux500.
2662*4882a593Smuzhiyun 	 */
2663*4882a593Smuzhiyun 	struct device_node *np;
2664*4882a593Smuzhiyun 
2665*4882a593Smuzhiyun 	np = of_find_compatible_node(NULL, NULL, "stericsson,db8500-prcmu");
2666*4882a593Smuzhiyun 	prcmu_base = of_iomap(np, 0);
2667*4882a593Smuzhiyun 	if (!prcmu_base) {
2668*4882a593Smuzhiyun 		of_node_put(np);
2669*4882a593Smuzhiyun 		pr_err("%s: ioremap() of prcmu registers failed!\n", __func__);
2670*4882a593Smuzhiyun 		return;
2671*4882a593Smuzhiyun 	}
2672*4882a593Smuzhiyun 	dbx500_fw_version_init(np);
2673*4882a593Smuzhiyun 	of_node_put(np);
2674*4882a593Smuzhiyun 
2675*4882a593Smuzhiyun 	spin_lock_init(&mb0_transfer.lock);
2676*4882a593Smuzhiyun 	spin_lock_init(&mb0_transfer.dbb_irqs_lock);
2677*4882a593Smuzhiyun 	mutex_init(&mb0_transfer.ac_wake_lock);
2678*4882a593Smuzhiyun 	init_completion(&mb0_transfer.ac_wake_work);
2679*4882a593Smuzhiyun 	mutex_init(&mb1_transfer.lock);
2680*4882a593Smuzhiyun 	init_completion(&mb1_transfer.work);
2681*4882a593Smuzhiyun 	mb1_transfer.ape_opp = APE_NO_CHANGE;
2682*4882a593Smuzhiyun 	mutex_init(&mb2_transfer.lock);
2683*4882a593Smuzhiyun 	init_completion(&mb2_transfer.work);
2684*4882a593Smuzhiyun 	spin_lock_init(&mb2_transfer.auto_pm_lock);
2685*4882a593Smuzhiyun 	spin_lock_init(&mb3_transfer.lock);
2686*4882a593Smuzhiyun 	mutex_init(&mb3_transfer.sysclk_lock);
2687*4882a593Smuzhiyun 	init_completion(&mb3_transfer.sysclk_work);
2688*4882a593Smuzhiyun 	mutex_init(&mb4_transfer.lock);
2689*4882a593Smuzhiyun 	init_completion(&mb4_transfer.work);
2690*4882a593Smuzhiyun 	mutex_init(&mb5_transfer.lock);
2691*4882a593Smuzhiyun 	init_completion(&mb5_transfer.work);
2692*4882a593Smuzhiyun 
2693*4882a593Smuzhiyun 	INIT_WORK(&mb0_transfer.mask_work, prcmu_mask_work);
2694*4882a593Smuzhiyun }
2695*4882a593Smuzhiyun 
init_prcm_registers(void)2696*4882a593Smuzhiyun static void init_prcm_registers(void)
2697*4882a593Smuzhiyun {
2698*4882a593Smuzhiyun 	u32 val;
2699*4882a593Smuzhiyun 
2700*4882a593Smuzhiyun 	val = readl(PRCM_A9PL_FORCE_CLKEN);
2701*4882a593Smuzhiyun 	val &= ~(PRCM_A9PL_FORCE_CLKEN_PRCM_A9PL_FORCE_CLKEN |
2702*4882a593Smuzhiyun 		PRCM_A9PL_FORCE_CLKEN_PRCM_A9AXI_FORCE_CLKEN);
2703*4882a593Smuzhiyun 	writel(val, (PRCM_A9PL_FORCE_CLKEN));
2704*4882a593Smuzhiyun }
2705*4882a593Smuzhiyun 
2706*4882a593Smuzhiyun /*
2707*4882a593Smuzhiyun  * Power domain switches (ePODs) modeled as regulators for the DB8500 SoC
2708*4882a593Smuzhiyun  */
2709*4882a593Smuzhiyun static struct regulator_consumer_supply db8500_vape_consumers[] = {
2710*4882a593Smuzhiyun 	REGULATOR_SUPPLY("v-ape", NULL),
2711*4882a593Smuzhiyun 	REGULATOR_SUPPLY("v-i2c", "nmk-i2c.0"),
2712*4882a593Smuzhiyun 	REGULATOR_SUPPLY("v-i2c", "nmk-i2c.1"),
2713*4882a593Smuzhiyun 	REGULATOR_SUPPLY("v-i2c", "nmk-i2c.2"),
2714*4882a593Smuzhiyun 	REGULATOR_SUPPLY("v-i2c", "nmk-i2c.3"),
2715*4882a593Smuzhiyun 	REGULATOR_SUPPLY("v-i2c", "nmk-i2c.4"),
2716*4882a593Smuzhiyun 	/* "v-mmc" changed to "vcore" in the mainline kernel */
2717*4882a593Smuzhiyun 	REGULATOR_SUPPLY("vcore", "sdi0"),
2718*4882a593Smuzhiyun 	REGULATOR_SUPPLY("vcore", "sdi1"),
2719*4882a593Smuzhiyun 	REGULATOR_SUPPLY("vcore", "sdi2"),
2720*4882a593Smuzhiyun 	REGULATOR_SUPPLY("vcore", "sdi3"),
2721*4882a593Smuzhiyun 	REGULATOR_SUPPLY("vcore", "sdi4"),
2722*4882a593Smuzhiyun 	REGULATOR_SUPPLY("v-dma", "dma40.0"),
2723*4882a593Smuzhiyun 	REGULATOR_SUPPLY("v-ape", "ab8500-usb.0"),
2724*4882a593Smuzhiyun 	/* "v-uart" changed to "vcore" in the mainline kernel */
2725*4882a593Smuzhiyun 	REGULATOR_SUPPLY("vcore", "uart0"),
2726*4882a593Smuzhiyun 	REGULATOR_SUPPLY("vcore", "uart1"),
2727*4882a593Smuzhiyun 	REGULATOR_SUPPLY("vcore", "uart2"),
2728*4882a593Smuzhiyun 	REGULATOR_SUPPLY("v-ape", "nmk-ske-keypad.0"),
2729*4882a593Smuzhiyun 	REGULATOR_SUPPLY("v-hsi", "ste_hsi.0"),
2730*4882a593Smuzhiyun 	REGULATOR_SUPPLY("vddvario", "smsc911x.0"),
2731*4882a593Smuzhiyun };
2732*4882a593Smuzhiyun 
2733*4882a593Smuzhiyun static struct regulator_consumer_supply db8500_vsmps2_consumers[] = {
2734*4882a593Smuzhiyun 	REGULATOR_SUPPLY("musb_1v8", "ab8500-usb.0"),
2735*4882a593Smuzhiyun 	/* AV8100 regulator */
2736*4882a593Smuzhiyun 	REGULATOR_SUPPLY("hdmi_1v8", "0-0070"),
2737*4882a593Smuzhiyun };
2738*4882a593Smuzhiyun 
2739*4882a593Smuzhiyun static struct regulator_consumer_supply db8500_b2r2_mcde_consumers[] = {
2740*4882a593Smuzhiyun 	REGULATOR_SUPPLY("vsupply", "b2r2_bus"),
2741*4882a593Smuzhiyun 	REGULATOR_SUPPLY("vsupply", "mcde"),
2742*4882a593Smuzhiyun };
2743*4882a593Smuzhiyun 
2744*4882a593Smuzhiyun /* SVA MMDSP regulator switch */
2745*4882a593Smuzhiyun static struct regulator_consumer_supply db8500_svammdsp_consumers[] = {
2746*4882a593Smuzhiyun 	REGULATOR_SUPPLY("sva-mmdsp", "cm_control"),
2747*4882a593Smuzhiyun };
2748*4882a593Smuzhiyun 
2749*4882a593Smuzhiyun /* SVA pipe regulator switch */
2750*4882a593Smuzhiyun static struct regulator_consumer_supply db8500_svapipe_consumers[] = {
2751*4882a593Smuzhiyun 	REGULATOR_SUPPLY("sva-pipe", "cm_control"),
2752*4882a593Smuzhiyun };
2753*4882a593Smuzhiyun 
2754*4882a593Smuzhiyun /* SIA MMDSP regulator switch */
2755*4882a593Smuzhiyun static struct regulator_consumer_supply db8500_siammdsp_consumers[] = {
2756*4882a593Smuzhiyun 	REGULATOR_SUPPLY("sia-mmdsp", "cm_control"),
2757*4882a593Smuzhiyun };
2758*4882a593Smuzhiyun 
2759*4882a593Smuzhiyun /* SIA pipe regulator switch */
2760*4882a593Smuzhiyun static struct regulator_consumer_supply db8500_siapipe_consumers[] = {
2761*4882a593Smuzhiyun 	REGULATOR_SUPPLY("sia-pipe", "cm_control"),
2762*4882a593Smuzhiyun };
2763*4882a593Smuzhiyun 
2764*4882a593Smuzhiyun static struct regulator_consumer_supply db8500_sga_consumers[] = {
2765*4882a593Smuzhiyun 	REGULATOR_SUPPLY("v-mali", NULL),
2766*4882a593Smuzhiyun };
2767*4882a593Smuzhiyun 
2768*4882a593Smuzhiyun /* ESRAM1 and 2 regulator switch */
2769*4882a593Smuzhiyun static struct regulator_consumer_supply db8500_esram12_consumers[] = {
2770*4882a593Smuzhiyun 	REGULATOR_SUPPLY("esram12", "cm_control"),
2771*4882a593Smuzhiyun };
2772*4882a593Smuzhiyun 
2773*4882a593Smuzhiyun /* ESRAM3 and 4 regulator switch */
2774*4882a593Smuzhiyun static struct regulator_consumer_supply db8500_esram34_consumers[] = {
2775*4882a593Smuzhiyun 	REGULATOR_SUPPLY("v-esram34", "mcde"),
2776*4882a593Smuzhiyun 	REGULATOR_SUPPLY("esram34", "cm_control"),
2777*4882a593Smuzhiyun 	REGULATOR_SUPPLY("lcla_esram", "dma40.0"),
2778*4882a593Smuzhiyun };
2779*4882a593Smuzhiyun 
2780*4882a593Smuzhiyun static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
2781*4882a593Smuzhiyun 	[DB8500_REGULATOR_VAPE] = {
2782*4882a593Smuzhiyun 		.constraints = {
2783*4882a593Smuzhiyun 			.name = "db8500-vape",
2784*4882a593Smuzhiyun 			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
2785*4882a593Smuzhiyun 			.always_on = true,
2786*4882a593Smuzhiyun 		},
2787*4882a593Smuzhiyun 		.consumer_supplies = db8500_vape_consumers,
2788*4882a593Smuzhiyun 		.num_consumer_supplies = ARRAY_SIZE(db8500_vape_consumers),
2789*4882a593Smuzhiyun 	},
2790*4882a593Smuzhiyun 	[DB8500_REGULATOR_VARM] = {
2791*4882a593Smuzhiyun 		.constraints = {
2792*4882a593Smuzhiyun 			.name = "db8500-varm",
2793*4882a593Smuzhiyun 			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
2794*4882a593Smuzhiyun 		},
2795*4882a593Smuzhiyun 	},
2796*4882a593Smuzhiyun 	[DB8500_REGULATOR_VMODEM] = {
2797*4882a593Smuzhiyun 		.constraints = {
2798*4882a593Smuzhiyun 			.name = "db8500-vmodem",
2799*4882a593Smuzhiyun 			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
2800*4882a593Smuzhiyun 		},
2801*4882a593Smuzhiyun 	},
2802*4882a593Smuzhiyun 	[DB8500_REGULATOR_VPLL] = {
2803*4882a593Smuzhiyun 		.constraints = {
2804*4882a593Smuzhiyun 			.name = "db8500-vpll",
2805*4882a593Smuzhiyun 			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
2806*4882a593Smuzhiyun 		},
2807*4882a593Smuzhiyun 	},
2808*4882a593Smuzhiyun 	[DB8500_REGULATOR_VSMPS1] = {
2809*4882a593Smuzhiyun 		.constraints = {
2810*4882a593Smuzhiyun 			.name = "db8500-vsmps1",
2811*4882a593Smuzhiyun 			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
2812*4882a593Smuzhiyun 		},
2813*4882a593Smuzhiyun 	},
2814*4882a593Smuzhiyun 	[DB8500_REGULATOR_VSMPS2] = {
2815*4882a593Smuzhiyun 		.constraints = {
2816*4882a593Smuzhiyun 			.name = "db8500-vsmps2",
2817*4882a593Smuzhiyun 			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
2818*4882a593Smuzhiyun 		},
2819*4882a593Smuzhiyun 		.consumer_supplies = db8500_vsmps2_consumers,
2820*4882a593Smuzhiyun 		.num_consumer_supplies = ARRAY_SIZE(db8500_vsmps2_consumers),
2821*4882a593Smuzhiyun 	},
2822*4882a593Smuzhiyun 	[DB8500_REGULATOR_VSMPS3] = {
2823*4882a593Smuzhiyun 		.constraints = {
2824*4882a593Smuzhiyun 			.name = "db8500-vsmps3",
2825*4882a593Smuzhiyun 			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
2826*4882a593Smuzhiyun 		},
2827*4882a593Smuzhiyun 	},
2828*4882a593Smuzhiyun 	[DB8500_REGULATOR_VRF1] = {
2829*4882a593Smuzhiyun 		.constraints = {
2830*4882a593Smuzhiyun 			.name = "db8500-vrf1",
2831*4882a593Smuzhiyun 			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
2832*4882a593Smuzhiyun 		},
2833*4882a593Smuzhiyun 	},
2834*4882a593Smuzhiyun 	[DB8500_REGULATOR_SWITCH_SVAMMDSP] = {
2835*4882a593Smuzhiyun 		/* dependency to u8500-vape is handled outside regulator framework */
2836*4882a593Smuzhiyun 		.constraints = {
2837*4882a593Smuzhiyun 			.name = "db8500-sva-mmdsp",
2838*4882a593Smuzhiyun 			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
2839*4882a593Smuzhiyun 		},
2840*4882a593Smuzhiyun 		.consumer_supplies = db8500_svammdsp_consumers,
2841*4882a593Smuzhiyun 		.num_consumer_supplies = ARRAY_SIZE(db8500_svammdsp_consumers),
2842*4882a593Smuzhiyun 	},
2843*4882a593Smuzhiyun 	[DB8500_REGULATOR_SWITCH_SVAMMDSPRET] = {
2844*4882a593Smuzhiyun 		.constraints = {
2845*4882a593Smuzhiyun 			/* "ret" means "retention" */
2846*4882a593Smuzhiyun 			.name = "db8500-sva-mmdsp-ret",
2847*4882a593Smuzhiyun 			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
2848*4882a593Smuzhiyun 		},
2849*4882a593Smuzhiyun 	},
2850*4882a593Smuzhiyun 	[DB8500_REGULATOR_SWITCH_SVAPIPE] = {
2851*4882a593Smuzhiyun 		/* dependency to u8500-vape is handled outside regulator framework */
2852*4882a593Smuzhiyun 		.constraints = {
2853*4882a593Smuzhiyun 			.name = "db8500-sva-pipe",
2854*4882a593Smuzhiyun 			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
2855*4882a593Smuzhiyun 		},
2856*4882a593Smuzhiyun 		.consumer_supplies = db8500_svapipe_consumers,
2857*4882a593Smuzhiyun 		.num_consumer_supplies = ARRAY_SIZE(db8500_svapipe_consumers),
2858*4882a593Smuzhiyun 	},
2859*4882a593Smuzhiyun 	[DB8500_REGULATOR_SWITCH_SIAMMDSP] = {
2860*4882a593Smuzhiyun 		/* dependency to u8500-vape is handled outside regulator framework */
2861*4882a593Smuzhiyun 		.constraints = {
2862*4882a593Smuzhiyun 			.name = "db8500-sia-mmdsp",
2863*4882a593Smuzhiyun 			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
2864*4882a593Smuzhiyun 		},
2865*4882a593Smuzhiyun 		.consumer_supplies = db8500_siammdsp_consumers,
2866*4882a593Smuzhiyun 		.num_consumer_supplies = ARRAY_SIZE(db8500_siammdsp_consumers),
2867*4882a593Smuzhiyun 	},
2868*4882a593Smuzhiyun 	[DB8500_REGULATOR_SWITCH_SIAMMDSPRET] = {
2869*4882a593Smuzhiyun 		.constraints = {
2870*4882a593Smuzhiyun 			.name = "db8500-sia-mmdsp-ret",
2871*4882a593Smuzhiyun 			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
2872*4882a593Smuzhiyun 		},
2873*4882a593Smuzhiyun 	},
2874*4882a593Smuzhiyun 	[DB8500_REGULATOR_SWITCH_SIAPIPE] = {
2875*4882a593Smuzhiyun 		/* dependency to u8500-vape is handled outside regulator framework */
2876*4882a593Smuzhiyun 		.constraints = {
2877*4882a593Smuzhiyun 			.name = "db8500-sia-pipe",
2878*4882a593Smuzhiyun 			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
2879*4882a593Smuzhiyun 		},
2880*4882a593Smuzhiyun 		.consumer_supplies = db8500_siapipe_consumers,
2881*4882a593Smuzhiyun 		.num_consumer_supplies = ARRAY_SIZE(db8500_siapipe_consumers),
2882*4882a593Smuzhiyun 	},
2883*4882a593Smuzhiyun 	[DB8500_REGULATOR_SWITCH_SGA] = {
2884*4882a593Smuzhiyun 		.supply_regulator = "db8500-vape",
2885*4882a593Smuzhiyun 		.constraints = {
2886*4882a593Smuzhiyun 			.name = "db8500-sga",
2887*4882a593Smuzhiyun 			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
2888*4882a593Smuzhiyun 		},
2889*4882a593Smuzhiyun 		.consumer_supplies = db8500_sga_consumers,
2890*4882a593Smuzhiyun 		.num_consumer_supplies = ARRAY_SIZE(db8500_sga_consumers),
2891*4882a593Smuzhiyun 
2892*4882a593Smuzhiyun 	},
2893*4882a593Smuzhiyun 	[DB8500_REGULATOR_SWITCH_B2R2_MCDE] = {
2894*4882a593Smuzhiyun 		.supply_regulator = "db8500-vape",
2895*4882a593Smuzhiyun 		.constraints = {
2896*4882a593Smuzhiyun 			.name = "db8500-b2r2-mcde",
2897*4882a593Smuzhiyun 			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
2898*4882a593Smuzhiyun 		},
2899*4882a593Smuzhiyun 		.consumer_supplies = db8500_b2r2_mcde_consumers,
2900*4882a593Smuzhiyun 		.num_consumer_supplies = ARRAY_SIZE(db8500_b2r2_mcde_consumers),
2901*4882a593Smuzhiyun 	},
2902*4882a593Smuzhiyun 	[DB8500_REGULATOR_SWITCH_ESRAM12] = {
2903*4882a593Smuzhiyun 		/*
2904*4882a593Smuzhiyun 		 * esram12 is set in retention and supplied by Vsafe when Vape is off,
2905*4882a593Smuzhiyun 		 * no need to hold Vape
2906*4882a593Smuzhiyun 		 */
2907*4882a593Smuzhiyun 		.constraints = {
2908*4882a593Smuzhiyun 			.name = "db8500-esram12",
2909*4882a593Smuzhiyun 			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
2910*4882a593Smuzhiyun 		},
2911*4882a593Smuzhiyun 		.consumer_supplies = db8500_esram12_consumers,
2912*4882a593Smuzhiyun 		.num_consumer_supplies = ARRAY_SIZE(db8500_esram12_consumers),
2913*4882a593Smuzhiyun 	},
2914*4882a593Smuzhiyun 	[DB8500_REGULATOR_SWITCH_ESRAM12RET] = {
2915*4882a593Smuzhiyun 		.constraints = {
2916*4882a593Smuzhiyun 			.name = "db8500-esram12-ret",
2917*4882a593Smuzhiyun 			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
2918*4882a593Smuzhiyun 		},
2919*4882a593Smuzhiyun 	},
2920*4882a593Smuzhiyun 	[DB8500_REGULATOR_SWITCH_ESRAM34] = {
2921*4882a593Smuzhiyun 		/*
2922*4882a593Smuzhiyun 		 * esram34 is set in retention and supplied by Vsafe when Vape is off,
2923*4882a593Smuzhiyun 		 * no need to hold Vape
2924*4882a593Smuzhiyun 		 */
2925*4882a593Smuzhiyun 		.constraints = {
2926*4882a593Smuzhiyun 			.name = "db8500-esram34",
2927*4882a593Smuzhiyun 			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
2928*4882a593Smuzhiyun 		},
2929*4882a593Smuzhiyun 		.consumer_supplies = db8500_esram34_consumers,
2930*4882a593Smuzhiyun 		.num_consumer_supplies = ARRAY_SIZE(db8500_esram34_consumers),
2931*4882a593Smuzhiyun 	},
2932*4882a593Smuzhiyun 	[DB8500_REGULATOR_SWITCH_ESRAM34RET] = {
2933*4882a593Smuzhiyun 		.constraints = {
2934*4882a593Smuzhiyun 			.name = "db8500-esram34-ret",
2935*4882a593Smuzhiyun 			.valid_ops_mask = REGULATOR_CHANGE_STATUS,
2936*4882a593Smuzhiyun 		},
2937*4882a593Smuzhiyun 	},
2938*4882a593Smuzhiyun };
2939*4882a593Smuzhiyun 
2940*4882a593Smuzhiyun static struct ux500_wdt_data db8500_wdt_pdata = {
2941*4882a593Smuzhiyun 	.timeout = 600, /* 10 minutes */
2942*4882a593Smuzhiyun 	.has_28_bits_resolution = true,
2943*4882a593Smuzhiyun };
2944*4882a593Smuzhiyun 
2945*4882a593Smuzhiyun static const struct mfd_cell common_prcmu_devs[] = {
2946*4882a593Smuzhiyun 	{
2947*4882a593Smuzhiyun 		.name = "ux500_wdt",
2948*4882a593Smuzhiyun 		.platform_data = &db8500_wdt_pdata,
2949*4882a593Smuzhiyun 		.pdata_size = sizeof(db8500_wdt_pdata),
2950*4882a593Smuzhiyun 		.id = -1,
2951*4882a593Smuzhiyun 	},
2952*4882a593Smuzhiyun };
2953*4882a593Smuzhiyun 
2954*4882a593Smuzhiyun static const struct mfd_cell db8500_prcmu_devs[] = {
2955*4882a593Smuzhiyun 	OF_MFD_CELL("db8500-prcmu-regulators", NULL,
2956*4882a593Smuzhiyun 		    &db8500_regulators, sizeof(db8500_regulators), 0,
2957*4882a593Smuzhiyun 		    "stericsson,db8500-prcmu-regulator"),
2958*4882a593Smuzhiyun 	OF_MFD_CELL("cpuidle-dbx500",
2959*4882a593Smuzhiyun 		    NULL, NULL, 0, 0, "stericsson,cpuidle-dbx500"),
2960*4882a593Smuzhiyun 	OF_MFD_CELL("db8500-thermal",
2961*4882a593Smuzhiyun 		    NULL, NULL, 0, 0, "stericsson,db8500-thermal"),
2962*4882a593Smuzhiyun };
2963*4882a593Smuzhiyun 
db8500_prcmu_register_ab8500(struct device * parent)2964*4882a593Smuzhiyun static int db8500_prcmu_register_ab8500(struct device *parent)
2965*4882a593Smuzhiyun {
2966*4882a593Smuzhiyun 	struct device_node *np;
2967*4882a593Smuzhiyun 	struct resource ab850x_resource;
2968*4882a593Smuzhiyun 	const struct mfd_cell ab8500_cell = {
2969*4882a593Smuzhiyun 		.name = "ab8500-core",
2970*4882a593Smuzhiyun 		.of_compatible = "stericsson,ab8500",
2971*4882a593Smuzhiyun 		.id = AB8500_VERSION_AB8500,
2972*4882a593Smuzhiyun 		.resources = &ab850x_resource,
2973*4882a593Smuzhiyun 		.num_resources = 1,
2974*4882a593Smuzhiyun 	};
2975*4882a593Smuzhiyun 	const struct mfd_cell ab8505_cell = {
2976*4882a593Smuzhiyun 		.name = "ab8505-core",
2977*4882a593Smuzhiyun 		.of_compatible = "stericsson,ab8505",
2978*4882a593Smuzhiyun 		.id = AB8500_VERSION_AB8505,
2979*4882a593Smuzhiyun 		.resources = &ab850x_resource,
2980*4882a593Smuzhiyun 		.num_resources = 1,
2981*4882a593Smuzhiyun 	};
2982*4882a593Smuzhiyun 	const struct mfd_cell *ab850x_cell;
2983*4882a593Smuzhiyun 
2984*4882a593Smuzhiyun 	if (!parent->of_node)
2985*4882a593Smuzhiyun 		return -ENODEV;
2986*4882a593Smuzhiyun 
2987*4882a593Smuzhiyun 	/* Look up the device node, sneak the IRQ out of it */
2988*4882a593Smuzhiyun 	for_each_child_of_node(parent->of_node, np) {
2989*4882a593Smuzhiyun 		if (of_device_is_compatible(np, ab8500_cell.of_compatible)) {
2990*4882a593Smuzhiyun 			ab850x_cell = &ab8500_cell;
2991*4882a593Smuzhiyun 			break;
2992*4882a593Smuzhiyun 		}
2993*4882a593Smuzhiyun 		if (of_device_is_compatible(np, ab8505_cell.of_compatible)) {
2994*4882a593Smuzhiyun 			ab850x_cell = &ab8505_cell;
2995*4882a593Smuzhiyun 			break;
2996*4882a593Smuzhiyun 		}
2997*4882a593Smuzhiyun 	}
2998*4882a593Smuzhiyun 	if (!np) {
2999*4882a593Smuzhiyun 		dev_info(parent, "could not find AB850X node in the device tree\n");
3000*4882a593Smuzhiyun 		return -ENODEV;
3001*4882a593Smuzhiyun 	}
3002*4882a593Smuzhiyun 	of_irq_to_resource_table(np, &ab850x_resource, 1);
3003*4882a593Smuzhiyun 
3004*4882a593Smuzhiyun 	return mfd_add_devices(parent, 0, ab850x_cell, 1, NULL, 0, NULL);
3005*4882a593Smuzhiyun }
3006*4882a593Smuzhiyun 
db8500_prcmu_probe(struct platform_device * pdev)3007*4882a593Smuzhiyun static int db8500_prcmu_probe(struct platform_device *pdev)
3008*4882a593Smuzhiyun {
3009*4882a593Smuzhiyun 	struct device_node *np = pdev->dev.of_node;
3010*4882a593Smuzhiyun 	int irq = 0, err = 0;
3011*4882a593Smuzhiyun 	struct resource *res;
3012*4882a593Smuzhiyun 
3013*4882a593Smuzhiyun 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcmu");
3014*4882a593Smuzhiyun 	if (!res) {
3015*4882a593Smuzhiyun 		dev_err(&pdev->dev, "no prcmu memory region provided\n");
3016*4882a593Smuzhiyun 		return -EINVAL;
3017*4882a593Smuzhiyun 	}
3018*4882a593Smuzhiyun 	prcmu_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
3019*4882a593Smuzhiyun 	if (!prcmu_base) {
3020*4882a593Smuzhiyun 		dev_err(&pdev->dev,
3021*4882a593Smuzhiyun 			"failed to ioremap prcmu register memory\n");
3022*4882a593Smuzhiyun 		return -ENOMEM;
3023*4882a593Smuzhiyun 	}
3024*4882a593Smuzhiyun 	init_prcm_registers();
3025*4882a593Smuzhiyun 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcmu-tcdm");
3026*4882a593Smuzhiyun 	if (!res) {
3027*4882a593Smuzhiyun 		dev_err(&pdev->dev, "no prcmu tcdm region provided\n");
3028*4882a593Smuzhiyun 		return -EINVAL;
3029*4882a593Smuzhiyun 	}
3030*4882a593Smuzhiyun 	tcdm_base = devm_ioremap(&pdev->dev, res->start,
3031*4882a593Smuzhiyun 			resource_size(res));
3032*4882a593Smuzhiyun 	if (!tcdm_base) {
3033*4882a593Smuzhiyun 		dev_err(&pdev->dev,
3034*4882a593Smuzhiyun 			"failed to ioremap prcmu-tcdm register memory\n");
3035*4882a593Smuzhiyun 		return -ENOMEM;
3036*4882a593Smuzhiyun 	}
3037*4882a593Smuzhiyun 
3038*4882a593Smuzhiyun 	/* Clean up the mailbox interrupts after pre-kernel code. */
3039*4882a593Smuzhiyun 	writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR);
3040*4882a593Smuzhiyun 
3041*4882a593Smuzhiyun 	irq = platform_get_irq(pdev, 0);
3042*4882a593Smuzhiyun 	if (irq <= 0)
3043*4882a593Smuzhiyun 		return irq;
3044*4882a593Smuzhiyun 
3045*4882a593Smuzhiyun 	err = request_threaded_irq(irq, prcmu_irq_handler,
3046*4882a593Smuzhiyun 	        prcmu_irq_thread_fn, IRQF_NO_SUSPEND, "prcmu", NULL);
3047*4882a593Smuzhiyun 	if (err < 0) {
3048*4882a593Smuzhiyun 		pr_err("prcmu: Failed to allocate IRQ_DB8500_PRCMU1.\n");
3049*4882a593Smuzhiyun 		return err;
3050*4882a593Smuzhiyun 	}
3051*4882a593Smuzhiyun 
3052*4882a593Smuzhiyun 	db8500_irq_init(np);
3053*4882a593Smuzhiyun 
3054*4882a593Smuzhiyun 	prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET);
3055*4882a593Smuzhiyun 
3056*4882a593Smuzhiyun 	err = mfd_add_devices(&pdev->dev, 0, common_prcmu_devs,
3057*4882a593Smuzhiyun 			      ARRAY_SIZE(common_prcmu_devs), NULL, 0, db8500_irq_domain);
3058*4882a593Smuzhiyun 	if (err) {
3059*4882a593Smuzhiyun 		pr_err("prcmu: Failed to add subdevices\n");
3060*4882a593Smuzhiyun 		return err;
3061*4882a593Smuzhiyun 	}
3062*4882a593Smuzhiyun 
3063*4882a593Smuzhiyun 	/* TODO: Remove restriction when clk definitions are available. */
3064*4882a593Smuzhiyun 	if (!of_machine_is_compatible("st-ericsson,u8540")) {
3065*4882a593Smuzhiyun 		err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs,
3066*4882a593Smuzhiyun 				      ARRAY_SIZE(db8500_prcmu_devs), NULL, 0,
3067*4882a593Smuzhiyun 				      db8500_irq_domain);
3068*4882a593Smuzhiyun 		if (err) {
3069*4882a593Smuzhiyun 			mfd_remove_devices(&pdev->dev);
3070*4882a593Smuzhiyun 			pr_err("prcmu: Failed to add subdevices\n");
3071*4882a593Smuzhiyun 			return err;
3072*4882a593Smuzhiyun 		}
3073*4882a593Smuzhiyun 	}
3074*4882a593Smuzhiyun 
3075*4882a593Smuzhiyun 	err = db8500_prcmu_register_ab8500(&pdev->dev);
3076*4882a593Smuzhiyun 	if (err) {
3077*4882a593Smuzhiyun 		mfd_remove_devices(&pdev->dev);
3078*4882a593Smuzhiyun 		pr_err("prcmu: Failed to add ab8500 subdevice\n");
3079*4882a593Smuzhiyun 		return err;
3080*4882a593Smuzhiyun 	}
3081*4882a593Smuzhiyun 
3082*4882a593Smuzhiyun 	pr_info("DB8500 PRCMU initialized\n");
3083*4882a593Smuzhiyun 	return err;
3084*4882a593Smuzhiyun }
3085*4882a593Smuzhiyun static const struct of_device_id db8500_prcmu_match[] = {
3086*4882a593Smuzhiyun 	{ .compatible = "stericsson,db8500-prcmu"},
3087*4882a593Smuzhiyun 	{ },
3088*4882a593Smuzhiyun };
3089*4882a593Smuzhiyun 
3090*4882a593Smuzhiyun static struct platform_driver db8500_prcmu_driver = {
3091*4882a593Smuzhiyun 	.driver = {
3092*4882a593Smuzhiyun 		.name = "db8500-prcmu",
3093*4882a593Smuzhiyun 		.of_match_table = db8500_prcmu_match,
3094*4882a593Smuzhiyun 	},
3095*4882a593Smuzhiyun 	.probe = db8500_prcmu_probe,
3096*4882a593Smuzhiyun };
3097*4882a593Smuzhiyun 
db8500_prcmu_init(void)3098*4882a593Smuzhiyun static int __init db8500_prcmu_init(void)
3099*4882a593Smuzhiyun {
3100*4882a593Smuzhiyun 	return platform_driver_register(&db8500_prcmu_driver);
3101*4882a593Smuzhiyun }
3102*4882a593Smuzhiyun core_initcall(db8500_prcmu_init);
3103