xref: /linux/drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c (revision f08e7a4e8d6ac4de677727af352ea33c6ce9f444)
1*f08e7a4eSNicolas Frattaroli // SPDX-License-Identifier: GPL-2.0-only
2*f08e7a4eSNicolas Frattaroli /*
3*f08e7a4eSNicolas Frattaroli  * Driver for MediaTek MFlexGraphics Devices
4*f08e7a4eSNicolas Frattaroli  *
5*f08e7a4eSNicolas Frattaroli  * Copyright (C) 2025, Collabora Ltd.
6*f08e7a4eSNicolas Frattaroli  */
7*f08e7a4eSNicolas Frattaroli 
8*f08e7a4eSNicolas Frattaroli #include <linux/completion.h>
9*f08e7a4eSNicolas Frattaroli #include <linux/clk.h>
10*f08e7a4eSNicolas Frattaroli #include <linux/clk-provider.h>
11*f08e7a4eSNicolas Frattaroli #include <linux/container_of.h>
12*f08e7a4eSNicolas Frattaroli #include <linux/iopoll.h>
13*f08e7a4eSNicolas Frattaroli #include <linux/mailbox_client.h>
14*f08e7a4eSNicolas Frattaroli #include <linux/module.h>
15*f08e7a4eSNicolas Frattaroli #include <linux/of.h>
16*f08e7a4eSNicolas Frattaroli #include <linux/of_address.h>
17*f08e7a4eSNicolas Frattaroli #include <linux/of_platform.h>
18*f08e7a4eSNicolas Frattaroli #include <linux/of_reserved_mem.h>
19*f08e7a4eSNicolas Frattaroli #include <linux/overflow.h>
20*f08e7a4eSNicolas Frattaroli #include <linux/platform_device.h>
21*f08e7a4eSNicolas Frattaroli #include <linux/pm_domain.h>
22*f08e7a4eSNicolas Frattaroli #include <linux/pm_opp.h>
23*f08e7a4eSNicolas Frattaroli #include <linux/regulator/consumer.h>
24*f08e7a4eSNicolas Frattaroli #include <linux/units.h>
25*f08e7a4eSNicolas Frattaroli 
26*f08e7a4eSNicolas Frattaroli #define GPR_LP_STATE		0x0028
27*f08e7a4eSNicolas Frattaroli #define   EB_ON_SUSPEND		0x0
28*f08e7a4eSNicolas Frattaroli #define   EB_ON_RESUME		0x1
29*f08e7a4eSNicolas Frattaroli #define GPR_IPI_MAGIC		0x34
30*f08e7a4eSNicolas Frattaroli 
31*f08e7a4eSNicolas Frattaroli #define RPC_PWR_CON		0x0504
32*f08e7a4eSNicolas Frattaroli #define   PWR_ACK_M		GENMASK(31, 30)
33*f08e7a4eSNicolas Frattaroli #define RPC_DUMMY_REG_2		0x0658
34*f08e7a4eSNicolas Frattaroli #define RPC_GHPM_CFG0_CON	0x0800
35*f08e7a4eSNicolas Frattaroli #define   GHPM_ENABLE_M		BIT(0)
36*f08e7a4eSNicolas Frattaroli #define   GHPM_ON_SEQ_M		BIT(2)
37*f08e7a4eSNicolas Frattaroli #define RPC_GHPM_RO0_CON	0x09A4
38*f08e7a4eSNicolas Frattaroli #define   GHPM_STATE_M		GENMASK(7, 0)
39*f08e7a4eSNicolas Frattaroli #define   GHPM_PWR_STATE_M	BIT(16)
40*f08e7a4eSNicolas Frattaroli 
41*f08e7a4eSNicolas Frattaroli #define GF_REG_MAGIC			0x0000
42*f08e7a4eSNicolas Frattaroli #define GF_REG_GPU_OPP_IDX		0x0004
43*f08e7a4eSNicolas Frattaroli #define GF_REG_STK_OPP_IDX		0x0008
44*f08e7a4eSNicolas Frattaroli #define GF_REG_GPU_OPP_NUM		0x000c
45*f08e7a4eSNicolas Frattaroli #define GF_REG_STK_OPP_NUM		0x0010
46*f08e7a4eSNicolas Frattaroli #define GF_REG_GPU_OPP_SNUM		0x0014
47*f08e7a4eSNicolas Frattaroli #define GF_REG_STK_OPP_SNUM		0x0018
48*f08e7a4eSNicolas Frattaroli #define GF_REG_POWER_COUNT		0x001c
49*f08e7a4eSNicolas Frattaroli #define GF_REG_BUCK_COUNT		0x0020
50*f08e7a4eSNicolas Frattaroli #define GF_REG_MTCMOS_COUNT		0x0024
51*f08e7a4eSNicolas Frattaroli #define GF_REG_CG_COUNT			0x0028 /* CG = Clock Gate? */
52*f08e7a4eSNicolas Frattaroli #define GF_REG_ACTIVE_COUNT		0x002C
53*f08e7a4eSNicolas Frattaroli #define GF_REG_TEMP_RAW			0x0030
54*f08e7a4eSNicolas Frattaroli #define GF_REG_TEMP_NORM_GPU		0x0034
55*f08e7a4eSNicolas Frattaroli #define GF_REG_TEMP_HIGH_GPU		0x0038
56*f08e7a4eSNicolas Frattaroli #define GF_REG_TEMP_NORM_STK		0x003C
57*f08e7a4eSNicolas Frattaroli #define GF_REG_TEMP_HIGH_STK		0x0040
58*f08e7a4eSNicolas Frattaroli #define GF_REG_FREQ_CUR_GPU		0x0044
59*f08e7a4eSNicolas Frattaroli #define GF_REG_FREQ_CUR_STK		0x0048
60*f08e7a4eSNicolas Frattaroli #define GF_REG_FREQ_OUT_GPU		0x004C /* Guess: actual achieved freq */
61*f08e7a4eSNicolas Frattaroli #define GF_REG_FREQ_OUT_STK		0x0050 /* Guess: actual achieved freq */
62*f08e7a4eSNicolas Frattaroli #define GF_REG_FREQ_METER_GPU		0x0054 /* Seems unused, always 0 */
63*f08e7a4eSNicolas Frattaroli #define GF_REG_FREQ_METER_STK		0x0058 /* Seems unused, always 0 */
64*f08e7a4eSNicolas Frattaroli #define GF_REG_VOLT_CUR_GPU		0x005C /* in tens of microvolts */
65*f08e7a4eSNicolas Frattaroli #define GF_REG_VOLT_CUR_STK		0x0060 /* in tens of microvolts */
66*f08e7a4eSNicolas Frattaroli #define GF_REG_VOLT_CUR_GPU_SRAM	0x0064
67*f08e7a4eSNicolas Frattaroli #define GF_REG_VOLT_CUR_STK_SRAM	0x0068
68*f08e7a4eSNicolas Frattaroli #define GF_REG_VOLT_CUR_GPU_REG		0x006C /* Seems unused, always 0 */
69*f08e7a4eSNicolas Frattaroli #define GF_REG_VOLT_CUR_STK_REG		0x0070 /* Seems unused, always 0 */
70*f08e7a4eSNicolas Frattaroli #define GF_REG_VOLT_CUR_GPU_REG_SRAM	0x0074
71*f08e7a4eSNicolas Frattaroli #define GF_REG_VOLT_CUR_STK_REG_SRAM	0x0078
72*f08e7a4eSNicolas Frattaroli #define GF_REG_PWR_CUR_GPU		0x007C /* in milliwatts */
73*f08e7a4eSNicolas Frattaroli #define GF_REG_PWR_CUR_STK		0x0080 /* in milliwatts */
74*f08e7a4eSNicolas Frattaroli #define GF_REG_PWR_MAX_GPU		0x0084 /* in milliwatts */
75*f08e7a4eSNicolas Frattaroli #define GF_REG_PWR_MAX_STK		0x0088 /* in milliwatts */
76*f08e7a4eSNicolas Frattaroli #define GF_REG_PWR_MIN_GPU		0x008C /* in milliwatts */
77*f08e7a4eSNicolas Frattaroli #define GF_REG_PWR_MIN_STK		0x0090 /* in milliwatts */
78*f08e7a4eSNicolas Frattaroli #define GF_REG_LEAKAGE_RT_GPU		0x0094 /* Unknown */
79*f08e7a4eSNicolas Frattaroli #define GF_REG_LEAKAGE_RT_STK		0x0098 /* Unknown */
80*f08e7a4eSNicolas Frattaroli #define GF_REG_LEAKAGE_RT_SRAM		0x009C /* Unknown */
81*f08e7a4eSNicolas Frattaroli #define GF_REG_LEAKAGE_HT_GPU		0x00A0 /* Unknown */
82*f08e7a4eSNicolas Frattaroli #define GF_REG_LEAKAGE_HT_STK		0x00A4 /* Unknown */
83*f08e7a4eSNicolas Frattaroli #define GF_REG_LEAKAGE_HT_SRAM		0x00A8 /* Unknown */
84*f08e7a4eSNicolas Frattaroli #define GF_REG_VOLT_DAC_LOW_GPU		0x00AC /* Seems unused, always 0 */
85*f08e7a4eSNicolas Frattaroli #define GF_REG_VOLT_DAC_LOW_STK		0x00B0 /* Seems unused, always 0 */
86*f08e7a4eSNicolas Frattaroli #define GF_REG_OPP_CUR_CEIL		0x00B4
87*f08e7a4eSNicolas Frattaroli #define GF_REG_OPP_CUR_FLOOR		0x00B8
88*f08e7a4eSNicolas Frattaroli #define GF_REG_OPP_CUR_LIMITER_CEIL	0x00BC
89*f08e7a4eSNicolas Frattaroli #define GF_REG_OPP_CUR_LIMITER_FLOOR	0x00C0
90*f08e7a4eSNicolas Frattaroli #define GF_REG_OPP_PRIORITY_CEIL	0x00C4
91*f08e7a4eSNicolas Frattaroli #define GF_REG_OPP_PRIORITY_FLOOR	0x00C8
92*f08e7a4eSNicolas Frattaroli #define GF_REG_PWR_CTL			0x00CC
93*f08e7a4eSNicolas Frattaroli #define GF_REG_ACTIVE_SLEEP_CTL		0x00D0
94*f08e7a4eSNicolas Frattaroli #define GF_REG_DVFS_STATE		0x00D4
95*f08e7a4eSNicolas Frattaroli #define GF_REG_SHADER_PRESENT		0x00D8
96*f08e7a4eSNicolas Frattaroli #define GF_REG_ASENSOR_ENABLE		0x00DC
97*f08e7a4eSNicolas Frattaroli #define GF_REG_AGING_LOAD		0x00E0
98*f08e7a4eSNicolas Frattaroli #define GF_REG_AGING_MARGIN		0x00E4
99*f08e7a4eSNicolas Frattaroli #define GF_REG_AVS_ENABLE		0x00E8
100*f08e7a4eSNicolas Frattaroli #define GF_REG_AVS_MARGIN		0x00EC
101*f08e7a4eSNicolas Frattaroli #define GF_REG_CHIP_TYPE		0x00F0
102*f08e7a4eSNicolas Frattaroli #define GF_REG_SB_VERSION		0x00F4
103*f08e7a4eSNicolas Frattaroli #define GF_REG_PTP_VERSION		0x00F8
104*f08e7a4eSNicolas Frattaroli #define GF_REG_DBG_VERSION		0x00FC
105*f08e7a4eSNicolas Frattaroli #define GF_REG_KDBG_VERSION		0x0100
106*f08e7a4eSNicolas Frattaroli #define GF_REG_GPM1_MODE		0x0104
107*f08e7a4eSNicolas Frattaroli #define GF_REG_GPM3_MODE		0x0108
108*f08e7a4eSNicolas Frattaroli #define GF_REG_DFD_MODE			0x010C
109*f08e7a4eSNicolas Frattaroli #define GF_REG_DUAL_BUCK		0x0110
110*f08e7a4eSNicolas Frattaroli #define GF_REG_SEGMENT_ID		0x0114
111*f08e7a4eSNicolas Frattaroli #define GF_REG_POWER_TIME_H		0x0118
112*f08e7a4eSNicolas Frattaroli #define GF_REG_POWER_TIME_L		0x011C
113*f08e7a4eSNicolas Frattaroli #define GF_REG_PWR_STATUS		0x0120
114*f08e7a4eSNicolas Frattaroli #define GF_REG_STRESS_TEST		0x0124
115*f08e7a4eSNicolas Frattaroli #define GF_REG_TEST_MODE		0x0128
116*f08e7a4eSNicolas Frattaroli #define GF_REG_IPS_MODE			0x012C
117*f08e7a4eSNicolas Frattaroli #define GF_REG_TEMP_COMP_MODE		0x0130
118*f08e7a4eSNicolas Frattaroli #define GF_REG_HT_TEMP_COMP_MODE	0x0134
119*f08e7a4eSNicolas Frattaroli #define GF_REG_PWR_TRACKER_MODE		0x0138
120*f08e7a4eSNicolas Frattaroli #define GF_REG_OPP_TABLE_GPU		0x0314
121*f08e7a4eSNicolas Frattaroli #define GF_REG_OPP_TABLE_STK		0x09A4
122*f08e7a4eSNicolas Frattaroli #define GF_REG_OPP_TABLE_GPU_S		0x1034
123*f08e7a4eSNicolas Frattaroli #define GF_REG_OPP_TABLE_STK_S		0x16c4
124*f08e7a4eSNicolas Frattaroli #define GF_REG_LIMIT_TABLE		0x1d54
125*f08e7a4eSNicolas Frattaroli #define GF_REG_GPM3_TABLE		0x223C
126*f08e7a4eSNicolas Frattaroli 
127*f08e7a4eSNicolas Frattaroli #define MFG_MT8196_E2_ID		0x101
128*f08e7a4eSNicolas Frattaroli #define GPUEB_SLEEP_MAGIC		0x55667788UL
129*f08e7a4eSNicolas Frattaroli #define GPUEB_MEM_MAGIC			0xBABADADAUL
130*f08e7a4eSNicolas Frattaroli 
131*f08e7a4eSNicolas Frattaroli #define GPUEB_TIMEOUT_US		10000UL
132*f08e7a4eSNicolas Frattaroli #define GPUEB_POLL_US			50
133*f08e7a4eSNicolas Frattaroli 
134*f08e7a4eSNicolas Frattaroli #define MAX_OPP_NUM			70
135*f08e7a4eSNicolas Frattaroli 
136*f08e7a4eSNicolas Frattaroli #define GPUEB_MBOX_MAX_RX_SIZE		32 /* in bytes */
137*f08e7a4eSNicolas Frattaroli 
138*f08e7a4eSNicolas Frattaroli /*
139*f08e7a4eSNicolas Frattaroli  * This enum is part of the ABI of the GPUEB firmware. Don't change the
140*f08e7a4eSNicolas Frattaroli  * numbering, as you would wreak havoc.
141*f08e7a4eSNicolas Frattaroli  */
142*f08e7a4eSNicolas Frattaroli enum mtk_mfg_ipi_cmd {
143*f08e7a4eSNicolas Frattaroli 	CMD_INIT_SHARED_MEM		= 0,
144*f08e7a4eSNicolas Frattaroli 	CMD_GET_FREQ_BY_IDX		= 1,
145*f08e7a4eSNicolas Frattaroli 	CMD_GET_POWER_BY_IDX		= 2,
146*f08e7a4eSNicolas Frattaroli 	CMD_GET_OPPIDX_BY_FREQ		= 3,
147*f08e7a4eSNicolas Frattaroli 	CMD_GET_LEAKAGE_POWER		= 4,
148*f08e7a4eSNicolas Frattaroli 	CMD_SET_LIMIT			= 5,
149*f08e7a4eSNicolas Frattaroli 	CMD_POWER_CONTROL		= 6,
150*f08e7a4eSNicolas Frattaroli 	CMD_ACTIVE_SLEEP_CONTROL	= 7,
151*f08e7a4eSNicolas Frattaroli 	CMD_COMMIT			= 8,
152*f08e7a4eSNicolas Frattaroli 	CMD_DUAL_COMMIT			= 9,
153*f08e7a4eSNicolas Frattaroli 	CMD_PDCA_CONFIG			= 10,
154*f08e7a4eSNicolas Frattaroli 	CMD_UPDATE_DEBUG_OPP_INFO	= 11,
155*f08e7a4eSNicolas Frattaroli 	CMD_SWITCH_LIMIT		= 12,
156*f08e7a4eSNicolas Frattaroli 	CMD_FIX_TARGET_OPPIDX		= 13,
157*f08e7a4eSNicolas Frattaroli 	CMD_FIX_DUAL_TARGET_OPPIDX	= 14,
158*f08e7a4eSNicolas Frattaroli 	CMD_FIX_CUSTOM_FREQ_VOLT	= 15,
159*f08e7a4eSNicolas Frattaroli 	CMD_FIX_DUAL_CUSTOM_FREQ_VOLT	= 16,
160*f08e7a4eSNicolas Frattaroli 	CMD_SET_MFGSYS_CONFIG		= 17,
161*f08e7a4eSNicolas Frattaroli 	CMD_MSSV_COMMIT			= 18,
162*f08e7a4eSNicolas Frattaroli 	CMD_NUM				= 19,
163*f08e7a4eSNicolas Frattaroli };
164*f08e7a4eSNicolas Frattaroli 
165*f08e7a4eSNicolas Frattaroli /*
166*f08e7a4eSNicolas Frattaroli  * This struct is part of the ABI of the GPUEB firmware. Changing it, or
167*f08e7a4eSNicolas Frattaroli  * reordering fields in it, will break things, so don't do it. Thank you.
168*f08e7a4eSNicolas Frattaroli  */
169*f08e7a4eSNicolas Frattaroli struct __packed mtk_mfg_ipi_msg {
170*f08e7a4eSNicolas Frattaroli 	__le32 magic;
171*f08e7a4eSNicolas Frattaroli 	__le32 cmd;
172*f08e7a4eSNicolas Frattaroli 	__le32 target;
173*f08e7a4eSNicolas Frattaroli 	/*
174*f08e7a4eSNicolas Frattaroli 	 * Downstream relies on the compiler to implicitly add the following
175*f08e7a4eSNicolas Frattaroli 	 * padding, as it declares the struct as non-packed.
176*f08e7a4eSNicolas Frattaroli 	 */
177*f08e7a4eSNicolas Frattaroli 	__le32 reserved;
178*f08e7a4eSNicolas Frattaroli 	union {
179*f08e7a4eSNicolas Frattaroli 		s32 __bitwise oppidx;
180*f08e7a4eSNicolas Frattaroli 		s32 __bitwise return_value;
181*f08e7a4eSNicolas Frattaroli 		__le32 freq;
182*f08e7a4eSNicolas Frattaroli 		__le32 volt;
183*f08e7a4eSNicolas Frattaroli 		__le32 power;
184*f08e7a4eSNicolas Frattaroli 		__le32 power_state;
185*f08e7a4eSNicolas Frattaroli 		__le32 mode;
186*f08e7a4eSNicolas Frattaroli 		__le32 value;
187*f08e7a4eSNicolas Frattaroli 		struct {
188*f08e7a4eSNicolas Frattaroli 			__le64 base;
189*f08e7a4eSNicolas Frattaroli 			__le32 size;
190*f08e7a4eSNicolas Frattaroli 		} shared_mem;
191*f08e7a4eSNicolas Frattaroli 		struct {
192*f08e7a4eSNicolas Frattaroli 			__le32 freq;
193*f08e7a4eSNicolas Frattaroli 			__le32 volt;
194*f08e7a4eSNicolas Frattaroli 		} custom;
195*f08e7a4eSNicolas Frattaroli 		struct {
196*f08e7a4eSNicolas Frattaroli 			__le32 limiter;
197*f08e7a4eSNicolas Frattaroli 			s32 __bitwise ceiling_info;
198*f08e7a4eSNicolas Frattaroli 			s32 __bitwise floor_info;
199*f08e7a4eSNicolas Frattaroli 		} set_limit;
200*f08e7a4eSNicolas Frattaroli 		struct {
201*f08e7a4eSNicolas Frattaroli 			__le32 target;
202*f08e7a4eSNicolas Frattaroli 			__le32 val;
203*f08e7a4eSNicolas Frattaroli 		} mfg_cfg;
204*f08e7a4eSNicolas Frattaroli 		struct {
205*f08e7a4eSNicolas Frattaroli 			__le32 target;
206*f08e7a4eSNicolas Frattaroli 			__le32 val;
207*f08e7a4eSNicolas Frattaroli 		} mssv;
208*f08e7a4eSNicolas Frattaroli 		struct {
209*f08e7a4eSNicolas Frattaroli 			s32 __bitwise gpu_oppidx;
210*f08e7a4eSNicolas Frattaroli 			s32 __bitwise stack_oppidx;
211*f08e7a4eSNicolas Frattaroli 		} dual_commit;
212*f08e7a4eSNicolas Frattaroli 		struct {
213*f08e7a4eSNicolas Frattaroli 			__le32 fgpu;
214*f08e7a4eSNicolas Frattaroli 			__le32 vgpu;
215*f08e7a4eSNicolas Frattaroli 			__le32 fstack;
216*f08e7a4eSNicolas Frattaroli 			__le32 vstack;
217*f08e7a4eSNicolas Frattaroli 		} dual_custom;
218*f08e7a4eSNicolas Frattaroli 	} u;
219*f08e7a4eSNicolas Frattaroli };
220*f08e7a4eSNicolas Frattaroli 
221*f08e7a4eSNicolas Frattaroli struct __packed mtk_mfg_ipi_sleep_msg {
222*f08e7a4eSNicolas Frattaroli 	__le32 event;
223*f08e7a4eSNicolas Frattaroli 	__le32 state;
224*f08e7a4eSNicolas Frattaroli 	__le32 magic;
225*f08e7a4eSNicolas Frattaroli };
226*f08e7a4eSNicolas Frattaroli 
227*f08e7a4eSNicolas Frattaroli /**
228*f08e7a4eSNicolas Frattaroli  * struct mtk_mfg_opp_entry - OPP table entry from firmware
229*f08e7a4eSNicolas Frattaroli  * @freq_khz: The operating point's frequency in kilohertz
230*f08e7a4eSNicolas Frattaroli  * @voltage_core: The operating point's core voltage in tens of microvolts
231*f08e7a4eSNicolas Frattaroli  * @voltage_sram: The operating point's SRAM voltage in tens of microvolts
232*f08e7a4eSNicolas Frattaroli  * @posdiv: exponent of base 2 for PLL frequency divisor used for this OPP
233*f08e7a4eSNicolas Frattaroli  * @voltage_margin: Number of tens of microvolts the voltage can be undershot
234*f08e7a4eSNicolas Frattaroli  * @power_mw: estimate of power usage at this operating point, in milliwatts
235*f08e7a4eSNicolas Frattaroli  *
236*f08e7a4eSNicolas Frattaroli  * This struct is part of the ABI with the EB firmware. Do not change it.
237*f08e7a4eSNicolas Frattaroli  */
238*f08e7a4eSNicolas Frattaroli struct __packed mtk_mfg_opp_entry {
239*f08e7a4eSNicolas Frattaroli 	__le32 freq_khz;
240*f08e7a4eSNicolas Frattaroli 	__le32 voltage_core;
241*f08e7a4eSNicolas Frattaroli 	__le32 voltage_sram;
242*f08e7a4eSNicolas Frattaroli 	__le32 posdiv;
243*f08e7a4eSNicolas Frattaroli 	__le32 voltage_margin;
244*f08e7a4eSNicolas Frattaroli 	__le32 power_mw;
245*f08e7a4eSNicolas Frattaroli };
246*f08e7a4eSNicolas Frattaroli 
247*f08e7a4eSNicolas Frattaroli struct mtk_mfg_mbox {
248*f08e7a4eSNicolas Frattaroli 	struct mbox_client cl;
249*f08e7a4eSNicolas Frattaroli 	struct completion rx_done;
250*f08e7a4eSNicolas Frattaroli 	struct mtk_mfg *mfg;
251*f08e7a4eSNicolas Frattaroli 	struct mbox_chan *ch;
252*f08e7a4eSNicolas Frattaroli 	void *rx_data;
253*f08e7a4eSNicolas Frattaroli };
254*f08e7a4eSNicolas Frattaroli 
255*f08e7a4eSNicolas Frattaroli struct mtk_mfg {
256*f08e7a4eSNicolas Frattaroli 	struct generic_pm_domain pd;
257*f08e7a4eSNicolas Frattaroli 	struct platform_device *pdev;
258*f08e7a4eSNicolas Frattaroli 	struct clk *clk_eb;
259*f08e7a4eSNicolas Frattaroli 	struct clk_bulk_data *gpu_clks;
260*f08e7a4eSNicolas Frattaroli 	struct clk_hw clk_core_hw;
261*f08e7a4eSNicolas Frattaroli 	struct clk_hw clk_stack_hw;
262*f08e7a4eSNicolas Frattaroli 	struct regulator_bulk_data *gpu_regs;
263*f08e7a4eSNicolas Frattaroli 	void __iomem *rpc;
264*f08e7a4eSNicolas Frattaroli 	void __iomem *gpr;
265*f08e7a4eSNicolas Frattaroli 	void __iomem *shared_mem;
266*f08e7a4eSNicolas Frattaroli 	phys_addr_t shared_mem_phys;
267*f08e7a4eSNicolas Frattaroli 	unsigned int shared_mem_size;
268*f08e7a4eSNicolas Frattaroli 	u16 ghpm_en_reg;
269*f08e7a4eSNicolas Frattaroli 	u32 ipi_magic;
270*f08e7a4eSNicolas Frattaroli 	unsigned short num_gpu_opps;
271*f08e7a4eSNicolas Frattaroli 	unsigned short num_stack_opps;
272*f08e7a4eSNicolas Frattaroli 	struct dev_pm_opp_data *gpu_opps;
273*f08e7a4eSNicolas Frattaroli 	struct dev_pm_opp_data *stack_opps;
274*f08e7a4eSNicolas Frattaroli 	struct mtk_mfg_mbox *gf_mbox;
275*f08e7a4eSNicolas Frattaroli 	struct mtk_mfg_mbox *slp_mbox;
276*f08e7a4eSNicolas Frattaroli 	const struct mtk_mfg_variant *variant;
277*f08e7a4eSNicolas Frattaroli };
278*f08e7a4eSNicolas Frattaroli 
279*f08e7a4eSNicolas Frattaroli struct mtk_mfg_variant {
280*f08e7a4eSNicolas Frattaroli 	const char *const *clk_names;
281*f08e7a4eSNicolas Frattaroli 	unsigned int num_clks;
282*f08e7a4eSNicolas Frattaroli 	const char *const *regulator_names;
283*f08e7a4eSNicolas Frattaroli 	unsigned int num_regulators;
284*f08e7a4eSNicolas Frattaroli 	/** @turbo_below: opp indices below this value are considered turbo */
285*f08e7a4eSNicolas Frattaroli 	unsigned int turbo_below;
286*f08e7a4eSNicolas Frattaroli 	int (*init)(struct mtk_mfg *mfg);
287*f08e7a4eSNicolas Frattaroli };
288*f08e7a4eSNicolas Frattaroli 
289*f08e7a4eSNicolas Frattaroli static inline struct mtk_mfg *mtk_mfg_from_genpd(struct generic_pm_domain *pd)
290*f08e7a4eSNicolas Frattaroli {
291*f08e7a4eSNicolas Frattaroli 	return container_of(pd, struct mtk_mfg, pd);
292*f08e7a4eSNicolas Frattaroli }
293*f08e7a4eSNicolas Frattaroli 
294*f08e7a4eSNicolas Frattaroli static inline void mtk_mfg_update_reg_bits(void __iomem *addr, u32 mask, u32 val)
295*f08e7a4eSNicolas Frattaroli {
296*f08e7a4eSNicolas Frattaroli 	writel((readl(addr) & ~mask) | (val & mask), addr);
297*f08e7a4eSNicolas Frattaroli }
298*f08e7a4eSNicolas Frattaroli 
299*f08e7a4eSNicolas Frattaroli static inline bool mtk_mfg_is_powered_on(struct mtk_mfg *mfg)
300*f08e7a4eSNicolas Frattaroli {
301*f08e7a4eSNicolas Frattaroli 	return (readl(mfg->rpc + RPC_PWR_CON) & PWR_ACK_M) == PWR_ACK_M;
302*f08e7a4eSNicolas Frattaroli }
303*f08e7a4eSNicolas Frattaroli 
304*f08e7a4eSNicolas Frattaroli static unsigned long mtk_mfg_recalc_rate_gpu(struct clk_hw *hw,
305*f08e7a4eSNicolas Frattaroli 					     unsigned long parent_rate)
306*f08e7a4eSNicolas Frattaroli {
307*f08e7a4eSNicolas Frattaroli 	struct mtk_mfg *mfg = container_of(hw, struct mtk_mfg, clk_core_hw);
308*f08e7a4eSNicolas Frattaroli 
309*f08e7a4eSNicolas Frattaroli 	return readl(mfg->shared_mem + GF_REG_FREQ_OUT_GPU) * HZ_PER_KHZ;
310*f08e7a4eSNicolas Frattaroli }
311*f08e7a4eSNicolas Frattaroli 
312*f08e7a4eSNicolas Frattaroli static long mtk_mfg_round_rate(struct clk_hw *hw, unsigned long rate,
313*f08e7a4eSNicolas Frattaroli 			       unsigned long *parent_rate)
314*f08e7a4eSNicolas Frattaroli {
315*f08e7a4eSNicolas Frattaroli 	/*
316*f08e7a4eSNicolas Frattaroli 	 * The round_rate callback needs to be implemented to avoid returning
317*f08e7a4eSNicolas Frattaroli 	 * the current clock frequency, rather than something even remotely
318*f08e7a4eSNicolas Frattaroli 	 * close to the frequency that was asked for.
319*f08e7a4eSNicolas Frattaroli 	 *
320*f08e7a4eSNicolas Frattaroli 	 * Instead of writing considerable amounts of possibly slow code just to
321*f08e7a4eSNicolas Frattaroli 	 * somehow figure out which of the three PLLs to round for, or even to
322*f08e7a4eSNicolas Frattaroli 	 * do a search through one of two OPP tables in order to find the closest
323*f08e7a4eSNicolas Frattaroli 	 * OPP of a frequency, just return the rate as-is. This avoids devfreq
324*f08e7a4eSNicolas Frattaroli 	 * "rounding" a request for the lowest frequency to the possibly very
325*f08e7a4eSNicolas Frattaroli 	 * high current frequency, breaking the powersave governor in the process.
326*f08e7a4eSNicolas Frattaroli 	 */
327*f08e7a4eSNicolas Frattaroli 
328*f08e7a4eSNicolas Frattaroli 	return rate;
329*f08e7a4eSNicolas Frattaroli }
330*f08e7a4eSNicolas Frattaroli 
331*f08e7a4eSNicolas Frattaroli static unsigned long mtk_mfg_recalc_rate_stack(struct clk_hw *hw,
332*f08e7a4eSNicolas Frattaroli 					       unsigned long parent_rate)
333*f08e7a4eSNicolas Frattaroli {
334*f08e7a4eSNicolas Frattaroli 	struct mtk_mfg *mfg = container_of(hw, struct mtk_mfg, clk_stack_hw);
335*f08e7a4eSNicolas Frattaroli 
336*f08e7a4eSNicolas Frattaroli 	return readl(mfg->shared_mem + GF_REG_FREQ_OUT_STK) * HZ_PER_KHZ;
337*f08e7a4eSNicolas Frattaroli }
338*f08e7a4eSNicolas Frattaroli 
339*f08e7a4eSNicolas Frattaroli static const struct clk_ops mtk_mfg_clk_gpu_ops = {
340*f08e7a4eSNicolas Frattaroli 	.recalc_rate = mtk_mfg_recalc_rate_gpu,
341*f08e7a4eSNicolas Frattaroli 	.round_rate = mtk_mfg_round_rate,
342*f08e7a4eSNicolas Frattaroli };
343*f08e7a4eSNicolas Frattaroli 
344*f08e7a4eSNicolas Frattaroli static const struct clk_ops mtk_mfg_clk_stack_ops = {
345*f08e7a4eSNicolas Frattaroli 	.recalc_rate = mtk_mfg_recalc_rate_stack,
346*f08e7a4eSNicolas Frattaroli 	.round_rate = mtk_mfg_round_rate,
347*f08e7a4eSNicolas Frattaroli };
348*f08e7a4eSNicolas Frattaroli 
349*f08e7a4eSNicolas Frattaroli static const struct clk_init_data mtk_mfg_clk_gpu_init = {
350*f08e7a4eSNicolas Frattaroli 	.name = "gpu-core",
351*f08e7a4eSNicolas Frattaroli 	.ops = &mtk_mfg_clk_gpu_ops,
352*f08e7a4eSNicolas Frattaroli 	.flags = CLK_GET_RATE_NOCACHE,
353*f08e7a4eSNicolas Frattaroli };
354*f08e7a4eSNicolas Frattaroli 
355*f08e7a4eSNicolas Frattaroli static const struct clk_init_data mtk_mfg_clk_stack_init = {
356*f08e7a4eSNicolas Frattaroli 	.name = "gpu-stack",
357*f08e7a4eSNicolas Frattaroli 	.ops = &mtk_mfg_clk_stack_ops,
358*f08e7a4eSNicolas Frattaroli 	.flags = CLK_GET_RATE_NOCACHE,
359*f08e7a4eSNicolas Frattaroli };
360*f08e7a4eSNicolas Frattaroli 
361*f08e7a4eSNicolas Frattaroli static int mtk_mfg_eb_on(struct mtk_mfg *mfg)
362*f08e7a4eSNicolas Frattaroli {
363*f08e7a4eSNicolas Frattaroli 	struct device *dev = &mfg->pdev->dev;
364*f08e7a4eSNicolas Frattaroli 	u32 val;
365*f08e7a4eSNicolas Frattaroli 	int ret;
366*f08e7a4eSNicolas Frattaroli 
367*f08e7a4eSNicolas Frattaroli 	/*
368*f08e7a4eSNicolas Frattaroli 	 * If MFG is already on from e.g. the bootloader, skip doing the
369*f08e7a4eSNicolas Frattaroli 	 * power-on sequence, as it wouldn't work without powering it off first.
370*f08e7a4eSNicolas Frattaroli 	 */
371*f08e7a4eSNicolas Frattaroli 	if (mtk_mfg_is_powered_on(mfg))
372*f08e7a4eSNicolas Frattaroli 		return 0;
373*f08e7a4eSNicolas Frattaroli 
374*f08e7a4eSNicolas Frattaroli 	ret = readl_poll_timeout(mfg->rpc + RPC_GHPM_RO0_CON, val,
375*f08e7a4eSNicolas Frattaroli 				 !(val & (GHPM_PWR_STATE_M | GHPM_STATE_M)),
376*f08e7a4eSNicolas Frattaroli 				 GPUEB_POLL_US, GPUEB_TIMEOUT_US);
377*f08e7a4eSNicolas Frattaroli 	if (ret) {
378*f08e7a4eSNicolas Frattaroli 		dev_err(dev, "timed out waiting for EB to power on\n");
379*f08e7a4eSNicolas Frattaroli 		return ret;
380*f08e7a4eSNicolas Frattaroli 	}
381*f08e7a4eSNicolas Frattaroli 
382*f08e7a4eSNicolas Frattaroli 	mtk_mfg_update_reg_bits(mfg->rpc + mfg->ghpm_en_reg, GHPM_ENABLE_M,
383*f08e7a4eSNicolas Frattaroli 				GHPM_ENABLE_M);
384*f08e7a4eSNicolas Frattaroli 
385*f08e7a4eSNicolas Frattaroli 	mtk_mfg_update_reg_bits(mfg->rpc + RPC_GHPM_CFG0_CON, GHPM_ON_SEQ_M, 0);
386*f08e7a4eSNicolas Frattaroli 	mtk_mfg_update_reg_bits(mfg->rpc + RPC_GHPM_CFG0_CON, GHPM_ON_SEQ_M,
387*f08e7a4eSNicolas Frattaroli 				GHPM_ON_SEQ_M);
388*f08e7a4eSNicolas Frattaroli 
389*f08e7a4eSNicolas Frattaroli 	mtk_mfg_update_reg_bits(mfg->rpc + mfg->ghpm_en_reg, GHPM_ENABLE_M, 0);
390*f08e7a4eSNicolas Frattaroli 
391*f08e7a4eSNicolas Frattaroli 
392*f08e7a4eSNicolas Frattaroli 	ret = readl_poll_timeout(mfg->rpc + RPC_PWR_CON, val,
393*f08e7a4eSNicolas Frattaroli 				 (val & PWR_ACK_M) == PWR_ACK_M,
394*f08e7a4eSNicolas Frattaroli 				 GPUEB_POLL_US, GPUEB_TIMEOUT_US);
395*f08e7a4eSNicolas Frattaroli 	if (ret) {
396*f08e7a4eSNicolas Frattaroli 		dev_err(dev, "timed out waiting for EB power ack, val = 0x%X\n",
397*f08e7a4eSNicolas Frattaroli 			val);
398*f08e7a4eSNicolas Frattaroli 		return ret;
399*f08e7a4eSNicolas Frattaroli 	}
400*f08e7a4eSNicolas Frattaroli 
401*f08e7a4eSNicolas Frattaroli 	ret = readl_poll_timeout(mfg->gpr + GPR_LP_STATE, val,
402*f08e7a4eSNicolas Frattaroli 				 (val == EB_ON_RESUME),
403*f08e7a4eSNicolas Frattaroli 				 GPUEB_POLL_US, GPUEB_TIMEOUT_US);
404*f08e7a4eSNicolas Frattaroli 	if (ret) {
405*f08e7a4eSNicolas Frattaroli 		dev_err(dev, "timed out waiting for EB to resume, status = 0x%X\n", val);
406*f08e7a4eSNicolas Frattaroli 		return ret;
407*f08e7a4eSNicolas Frattaroli 	}
408*f08e7a4eSNicolas Frattaroli 
409*f08e7a4eSNicolas Frattaroli 	return 0;
410*f08e7a4eSNicolas Frattaroli }
411*f08e7a4eSNicolas Frattaroli 
412*f08e7a4eSNicolas Frattaroli static int mtk_mfg_eb_off(struct mtk_mfg *mfg)
413*f08e7a4eSNicolas Frattaroli {
414*f08e7a4eSNicolas Frattaroli 	struct device *dev = &mfg->pdev->dev;
415*f08e7a4eSNicolas Frattaroli 	struct mtk_mfg_ipi_sleep_msg msg = {
416*f08e7a4eSNicolas Frattaroli 		.event = 0,
417*f08e7a4eSNicolas Frattaroli 		.state = 0,
418*f08e7a4eSNicolas Frattaroli 		.magic = GPUEB_SLEEP_MAGIC
419*f08e7a4eSNicolas Frattaroli 	};
420*f08e7a4eSNicolas Frattaroli 	u32 val;
421*f08e7a4eSNicolas Frattaroli 	int ret;
422*f08e7a4eSNicolas Frattaroli 
423*f08e7a4eSNicolas Frattaroli 	ret = mbox_send_message(mfg->slp_mbox->ch, &msg);
424*f08e7a4eSNicolas Frattaroli 	if (ret < 0) {
425*f08e7a4eSNicolas Frattaroli 		dev_err(dev, "Cannot send sleep command: %pe\n", ERR_PTR(ret));
426*f08e7a4eSNicolas Frattaroli 		return ret;
427*f08e7a4eSNicolas Frattaroli 	}
428*f08e7a4eSNicolas Frattaroli 
429*f08e7a4eSNicolas Frattaroli 	ret = readl_poll_timeout(mfg->rpc + RPC_PWR_CON, val,
430*f08e7a4eSNicolas Frattaroli 				 !(val & PWR_ACK_M), GPUEB_POLL_US,
431*f08e7a4eSNicolas Frattaroli 				 GPUEB_TIMEOUT_US);
432*f08e7a4eSNicolas Frattaroli 
433*f08e7a4eSNicolas Frattaroli 	if (ret) {
434*f08e7a4eSNicolas Frattaroli 		dev_err(dev, "Timed out waiting for EB to power off, val=0x%08X\n", val);
435*f08e7a4eSNicolas Frattaroli 		return ret;
436*f08e7a4eSNicolas Frattaroli 	}
437*f08e7a4eSNicolas Frattaroli 
438*f08e7a4eSNicolas Frattaroli 	return 0;
439*f08e7a4eSNicolas Frattaroli }
440*f08e7a4eSNicolas Frattaroli 
441*f08e7a4eSNicolas Frattaroli /**
442*f08e7a4eSNicolas Frattaroli  * mtk_mfg_send_ipi - synchronously send an IPI message on the gpufreq channel
443*f08e7a4eSNicolas Frattaroli  * @mfg: pointer to this driver instance's private &struct mtk_mfg
444*f08e7a4eSNicolas Frattaroli  * @msg: pointer to a message to send; will have magic filled and response assigned
445*f08e7a4eSNicolas Frattaroli  *
446*f08e7a4eSNicolas Frattaroli  * Send an IPI message on the gpufreq channel, and wait for a response. Once a
447*f08e7a4eSNicolas Frattaroli  * response is received, assign a pointer to the response buffer (valid until
448*f08e7a4eSNicolas Frattaroli  * next response is received) to @msg.
449*f08e7a4eSNicolas Frattaroli  *
450*f08e7a4eSNicolas Frattaroli  * Returns 0 on success, negative errno on failure.
451*f08e7a4eSNicolas Frattaroli  */
452*f08e7a4eSNicolas Frattaroli static int mtk_mfg_send_ipi(struct mtk_mfg *mfg, struct mtk_mfg_ipi_msg *msg)
453*f08e7a4eSNicolas Frattaroli {
454*f08e7a4eSNicolas Frattaroli 	struct device *dev = &mfg->pdev->dev;
455*f08e7a4eSNicolas Frattaroli 	unsigned long wait;
456*f08e7a4eSNicolas Frattaroli 	int ret;
457*f08e7a4eSNicolas Frattaroli 
458*f08e7a4eSNicolas Frattaroli 	msg->magic = mfg->ipi_magic;
459*f08e7a4eSNicolas Frattaroli 
460*f08e7a4eSNicolas Frattaroli 	ret = mbox_send_message(mfg->gf_mbox->ch, msg);
461*f08e7a4eSNicolas Frattaroli 	if (ret < 0) {
462*f08e7a4eSNicolas Frattaroli 		dev_err(dev, "Cannot send GPUFreq IPI command: %pe\n", ERR_PTR(ret));
463*f08e7a4eSNicolas Frattaroli 		return ret;
464*f08e7a4eSNicolas Frattaroli 	}
465*f08e7a4eSNicolas Frattaroli 
466*f08e7a4eSNicolas Frattaroli 	wait = wait_for_completion_timeout(&mfg->gf_mbox->rx_done, msecs_to_jiffies(500));
467*f08e7a4eSNicolas Frattaroli 	if (!wait)
468*f08e7a4eSNicolas Frattaroli 		return -ETIMEDOUT;
469*f08e7a4eSNicolas Frattaroli 
470*f08e7a4eSNicolas Frattaroli 	msg = mfg->gf_mbox->rx_data;
471*f08e7a4eSNicolas Frattaroli 
472*f08e7a4eSNicolas Frattaroli 	if (msg->u.return_value < 0) {
473*f08e7a4eSNicolas Frattaroli 		dev_err(dev, "IPI return: %d\n", msg->u.return_value);
474*f08e7a4eSNicolas Frattaroli 		return -EPROTO;
475*f08e7a4eSNicolas Frattaroli 	}
476*f08e7a4eSNicolas Frattaroli 
477*f08e7a4eSNicolas Frattaroli 	return 0;
478*f08e7a4eSNicolas Frattaroli }
479*f08e7a4eSNicolas Frattaroli 
480*f08e7a4eSNicolas Frattaroli static int mtk_mfg_init_shared_mem(struct mtk_mfg *mfg)
481*f08e7a4eSNicolas Frattaroli {
482*f08e7a4eSNicolas Frattaroli 	struct device *dev = &mfg->pdev->dev;
483*f08e7a4eSNicolas Frattaroli 	struct mtk_mfg_ipi_msg msg = {};
484*f08e7a4eSNicolas Frattaroli 	int ret;
485*f08e7a4eSNicolas Frattaroli 
486*f08e7a4eSNicolas Frattaroli 	dev_dbg(dev, "clearing GPUEB shared memory, 0x%X bytes\n", mfg->shared_mem_size);
487*f08e7a4eSNicolas Frattaroli 	memset_io(mfg->shared_mem, 0, mfg->shared_mem_size);
488*f08e7a4eSNicolas Frattaroli 
489*f08e7a4eSNicolas Frattaroli 	msg.cmd = CMD_INIT_SHARED_MEM;
490*f08e7a4eSNicolas Frattaroli 	msg.u.shared_mem.base = mfg->shared_mem_phys;
491*f08e7a4eSNicolas Frattaroli 	msg.u.shared_mem.size = mfg->shared_mem_size;
492*f08e7a4eSNicolas Frattaroli 
493*f08e7a4eSNicolas Frattaroli 	ret = mtk_mfg_send_ipi(mfg, &msg);
494*f08e7a4eSNicolas Frattaroli 	if (ret)
495*f08e7a4eSNicolas Frattaroli 		return ret;
496*f08e7a4eSNicolas Frattaroli 
497*f08e7a4eSNicolas Frattaroli 	if (readl(mfg->shared_mem + GF_REG_MAGIC) != GPUEB_MEM_MAGIC) {
498*f08e7a4eSNicolas Frattaroli 		dev_err(dev, "EB did not initialise shared memory correctly\n");
499*f08e7a4eSNicolas Frattaroli 		return -EIO;
500*f08e7a4eSNicolas Frattaroli 	}
501*f08e7a4eSNicolas Frattaroli 
502*f08e7a4eSNicolas Frattaroli 	return 0;
503*f08e7a4eSNicolas Frattaroli }
504*f08e7a4eSNicolas Frattaroli 
505*f08e7a4eSNicolas Frattaroli static int mtk_mfg_power_control(struct mtk_mfg *mfg, bool enabled)
506*f08e7a4eSNicolas Frattaroli {
507*f08e7a4eSNicolas Frattaroli 	struct mtk_mfg_ipi_msg msg = {};
508*f08e7a4eSNicolas Frattaroli 
509*f08e7a4eSNicolas Frattaroli 	msg.cmd = CMD_POWER_CONTROL;
510*f08e7a4eSNicolas Frattaroli 	msg.u.power_state = enabled ? 1 : 0;
511*f08e7a4eSNicolas Frattaroli 
512*f08e7a4eSNicolas Frattaroli 	return mtk_mfg_send_ipi(mfg, &msg);
513*f08e7a4eSNicolas Frattaroli }
514*f08e7a4eSNicolas Frattaroli 
515*f08e7a4eSNicolas Frattaroli static int mtk_mfg_set_oppidx(struct mtk_mfg *mfg, unsigned int opp_idx)
516*f08e7a4eSNicolas Frattaroli {
517*f08e7a4eSNicolas Frattaroli 	struct mtk_mfg_ipi_msg msg = {};
518*f08e7a4eSNicolas Frattaroli 	int ret;
519*f08e7a4eSNicolas Frattaroli 
520*f08e7a4eSNicolas Frattaroli 	if (opp_idx >= mfg->num_gpu_opps)
521*f08e7a4eSNicolas Frattaroli 		return -EINVAL;
522*f08e7a4eSNicolas Frattaroli 
523*f08e7a4eSNicolas Frattaroli 	msg.cmd = CMD_FIX_DUAL_TARGET_OPPIDX;
524*f08e7a4eSNicolas Frattaroli 	msg.u.dual_commit.gpu_oppidx = opp_idx;
525*f08e7a4eSNicolas Frattaroli 	msg.u.dual_commit.stack_oppidx = opp_idx;
526*f08e7a4eSNicolas Frattaroli 
527*f08e7a4eSNicolas Frattaroli 	ret = mtk_mfg_send_ipi(mfg, &msg);
528*f08e7a4eSNicolas Frattaroli 	if (ret) {
529*f08e7a4eSNicolas Frattaroli 		dev_err(&mfg->pdev->dev, "Failed to set OPP %u: %pe\n",
530*f08e7a4eSNicolas Frattaroli 			opp_idx, ERR_PTR(ret));
531*f08e7a4eSNicolas Frattaroli 		return ret;
532*f08e7a4eSNicolas Frattaroli 	}
533*f08e7a4eSNicolas Frattaroli 
534*f08e7a4eSNicolas Frattaroli 	return 0;
535*f08e7a4eSNicolas Frattaroli }
536*f08e7a4eSNicolas Frattaroli 
537*f08e7a4eSNicolas Frattaroli static int mtk_mfg_read_opp_tables(struct mtk_mfg *mfg)
538*f08e7a4eSNicolas Frattaroli {
539*f08e7a4eSNicolas Frattaroli 	struct device *dev = &mfg->pdev->dev;
540*f08e7a4eSNicolas Frattaroli 	struct mtk_mfg_opp_entry e = {};
541*f08e7a4eSNicolas Frattaroli 	unsigned int i;
542*f08e7a4eSNicolas Frattaroli 
543*f08e7a4eSNicolas Frattaroli 	mfg->num_gpu_opps = readl(mfg->shared_mem + GF_REG_GPU_OPP_NUM);
544*f08e7a4eSNicolas Frattaroli 	mfg->num_stack_opps = readl(mfg->shared_mem + GF_REG_STK_OPP_NUM);
545*f08e7a4eSNicolas Frattaroli 
546*f08e7a4eSNicolas Frattaroli 	if (mfg->num_gpu_opps > MAX_OPP_NUM || mfg->num_gpu_opps == 0) {
547*f08e7a4eSNicolas Frattaroli 		dev_err(dev, "GPU OPP count (%u) out of range %u >= count > 0\n",
548*f08e7a4eSNicolas Frattaroli 			mfg->num_gpu_opps, MAX_OPP_NUM);
549*f08e7a4eSNicolas Frattaroli 		return -EINVAL;
550*f08e7a4eSNicolas Frattaroli 	}
551*f08e7a4eSNicolas Frattaroli 
552*f08e7a4eSNicolas Frattaroli 	if (mfg->num_stack_opps && mfg->num_stack_opps > MAX_OPP_NUM) {
553*f08e7a4eSNicolas Frattaroli 		dev_err(dev, "Stack OPP count (%u) out of range %u >= count >= 0\n",
554*f08e7a4eSNicolas Frattaroli 			mfg->num_stack_opps, MAX_OPP_NUM);
555*f08e7a4eSNicolas Frattaroli 		return -EINVAL;
556*f08e7a4eSNicolas Frattaroli 	}
557*f08e7a4eSNicolas Frattaroli 
558*f08e7a4eSNicolas Frattaroli 	mfg->gpu_opps = devm_kcalloc(dev, mfg->num_gpu_opps,
559*f08e7a4eSNicolas Frattaroli 				     sizeof(struct dev_pm_opp_data), GFP_KERNEL);
560*f08e7a4eSNicolas Frattaroli 	if (!mfg->gpu_opps)
561*f08e7a4eSNicolas Frattaroli 		return -ENOMEM;
562*f08e7a4eSNicolas Frattaroli 
563*f08e7a4eSNicolas Frattaroli 	if (mfg->num_stack_opps) {
564*f08e7a4eSNicolas Frattaroli 		mfg->stack_opps = devm_kcalloc(dev, mfg->num_stack_opps,
565*f08e7a4eSNicolas Frattaroli 					       sizeof(struct dev_pm_opp_data), GFP_KERNEL);
566*f08e7a4eSNicolas Frattaroli 		if (!mfg->stack_opps)
567*f08e7a4eSNicolas Frattaroli 			return -ENOMEM;
568*f08e7a4eSNicolas Frattaroli 	}
569*f08e7a4eSNicolas Frattaroli 
570*f08e7a4eSNicolas Frattaroli 	for (i = 0; i < mfg->num_gpu_opps; i++) {
571*f08e7a4eSNicolas Frattaroli 		memcpy_fromio(&e, mfg->shared_mem + GF_REG_OPP_TABLE_GPU + i * sizeof(e),
572*f08e7a4eSNicolas Frattaroli 			      sizeof(e));
573*f08e7a4eSNicolas Frattaroli 		if (mem_is_zero(&e, sizeof(e))) {
574*f08e7a4eSNicolas Frattaroli 			dev_err(dev, "ran into an empty GPU OPP at index %u\n",
575*f08e7a4eSNicolas Frattaroli 				i);
576*f08e7a4eSNicolas Frattaroli 			return -EINVAL;
577*f08e7a4eSNicolas Frattaroli 		}
578*f08e7a4eSNicolas Frattaroli 		mfg->gpu_opps[i].freq = e.freq_khz * HZ_PER_KHZ;
579*f08e7a4eSNicolas Frattaroli 		mfg->gpu_opps[i].u_volt = e.voltage_core * 10;
580*f08e7a4eSNicolas Frattaroli 		mfg->gpu_opps[i].level = i;
581*f08e7a4eSNicolas Frattaroli 		if (i < mfg->variant->turbo_below)
582*f08e7a4eSNicolas Frattaroli 			mfg->gpu_opps[i].turbo = true;
583*f08e7a4eSNicolas Frattaroli 	}
584*f08e7a4eSNicolas Frattaroli 
585*f08e7a4eSNicolas Frattaroli 	for (i = 0; i < mfg->num_stack_opps; i++) {
586*f08e7a4eSNicolas Frattaroli 		memcpy_fromio(&e, mfg->shared_mem + GF_REG_OPP_TABLE_STK + i * sizeof(e),
587*f08e7a4eSNicolas Frattaroli 			      sizeof(e));
588*f08e7a4eSNicolas Frattaroli 		if (mem_is_zero(&e, sizeof(e))) {
589*f08e7a4eSNicolas Frattaroli 			dev_err(dev, "ran into an empty Stack OPP at index %u\n",
590*f08e7a4eSNicolas Frattaroli 				i);
591*f08e7a4eSNicolas Frattaroli 			return -EINVAL;
592*f08e7a4eSNicolas Frattaroli 		}
593*f08e7a4eSNicolas Frattaroli 		mfg->stack_opps[i].freq = e.freq_khz * HZ_PER_KHZ;
594*f08e7a4eSNicolas Frattaroli 		mfg->stack_opps[i].u_volt = e.voltage_core * 10;
595*f08e7a4eSNicolas Frattaroli 		mfg->stack_opps[i].level = i;
596*f08e7a4eSNicolas Frattaroli 		if (i < mfg->variant->turbo_below)
597*f08e7a4eSNicolas Frattaroli 			mfg->stack_opps[i].turbo = true;
598*f08e7a4eSNicolas Frattaroli 	}
599*f08e7a4eSNicolas Frattaroli 
600*f08e7a4eSNicolas Frattaroli 	return 0;
601*f08e7a4eSNicolas Frattaroli }
602*f08e7a4eSNicolas Frattaroli 
603*f08e7a4eSNicolas Frattaroli static const char *const mtk_mfg_mt8196_clk_names[] = {
604*f08e7a4eSNicolas Frattaroli 	"core",
605*f08e7a4eSNicolas Frattaroli 	"stack0",
606*f08e7a4eSNicolas Frattaroli 	"stack1",
607*f08e7a4eSNicolas Frattaroli };
608*f08e7a4eSNicolas Frattaroli 
609*f08e7a4eSNicolas Frattaroli static const char *const mtk_mfg_mt8196_regulators[] = {
610*f08e7a4eSNicolas Frattaroli 	"core",
611*f08e7a4eSNicolas Frattaroli 	"stack",
612*f08e7a4eSNicolas Frattaroli 	"sram",
613*f08e7a4eSNicolas Frattaroli };
614*f08e7a4eSNicolas Frattaroli 
615*f08e7a4eSNicolas Frattaroli static int mtk_mfg_mt8196_init(struct mtk_mfg *mfg)
616*f08e7a4eSNicolas Frattaroli {
617*f08e7a4eSNicolas Frattaroli 	void __iomem *e2_base;
618*f08e7a4eSNicolas Frattaroli 
619*f08e7a4eSNicolas Frattaroli 	e2_base = devm_platform_ioremap_resource_byname(mfg->pdev, "hw-revision");
620*f08e7a4eSNicolas Frattaroli 	if (IS_ERR(e2_base))
621*f08e7a4eSNicolas Frattaroli 		return dev_err_probe(&mfg->pdev->dev, PTR_ERR(e2_base),
622*f08e7a4eSNicolas Frattaroli 				     "Couldn't get hw-revision register\n");
623*f08e7a4eSNicolas Frattaroli 
624*f08e7a4eSNicolas Frattaroli 	clk_prepare_enable(mfg->clk_eb);
625*f08e7a4eSNicolas Frattaroli 
626*f08e7a4eSNicolas Frattaroli 	if (readl(e2_base) == MFG_MT8196_E2_ID)
627*f08e7a4eSNicolas Frattaroli 		mfg->ghpm_en_reg = RPC_DUMMY_REG_2;
628*f08e7a4eSNicolas Frattaroli 	else
629*f08e7a4eSNicolas Frattaroli 		mfg->ghpm_en_reg = RPC_GHPM_CFG0_CON;
630*f08e7a4eSNicolas Frattaroli 
631*f08e7a4eSNicolas Frattaroli 	clk_disable_unprepare(mfg->clk_eb);
632*f08e7a4eSNicolas Frattaroli 
633*f08e7a4eSNicolas Frattaroli 	return 0;
634*f08e7a4eSNicolas Frattaroli }
635*f08e7a4eSNicolas Frattaroli 
636*f08e7a4eSNicolas Frattaroli static const struct mtk_mfg_variant mtk_mfg_mt8196_variant = {
637*f08e7a4eSNicolas Frattaroli 	.clk_names = mtk_mfg_mt8196_clk_names,
638*f08e7a4eSNicolas Frattaroli 	.num_clks = ARRAY_SIZE(mtk_mfg_mt8196_clk_names),
639*f08e7a4eSNicolas Frattaroli 	.regulator_names = mtk_mfg_mt8196_regulators,
640*f08e7a4eSNicolas Frattaroli 	.num_regulators = ARRAY_SIZE(mtk_mfg_mt8196_regulators),
641*f08e7a4eSNicolas Frattaroli 	.turbo_below = 7,
642*f08e7a4eSNicolas Frattaroli 	.init = mtk_mfg_mt8196_init,
643*f08e7a4eSNicolas Frattaroli };
644*f08e7a4eSNicolas Frattaroli 
645*f08e7a4eSNicolas Frattaroli static void mtk_mfg_mbox_rx_callback(struct mbox_client *cl, void *mssg)
646*f08e7a4eSNicolas Frattaroli {
647*f08e7a4eSNicolas Frattaroli 	struct mtk_mfg_mbox *mb = container_of(cl, struct mtk_mfg_mbox, cl);
648*f08e7a4eSNicolas Frattaroli 
649*f08e7a4eSNicolas Frattaroli 	if (mb->rx_data)
650*f08e7a4eSNicolas Frattaroli 		mb->rx_data = memcpy(mb->rx_data, mssg, GPUEB_MBOX_MAX_RX_SIZE);
651*f08e7a4eSNicolas Frattaroli 	complete(&mb->rx_done);
652*f08e7a4eSNicolas Frattaroli }
653*f08e7a4eSNicolas Frattaroli 
654*f08e7a4eSNicolas Frattaroli static int mtk_mfg_attach_dev(struct generic_pm_domain *pd, struct device *dev)
655*f08e7a4eSNicolas Frattaroli {
656*f08e7a4eSNicolas Frattaroli 	struct mtk_mfg *mfg = mtk_mfg_from_genpd(pd);
657*f08e7a4eSNicolas Frattaroli 	struct dev_pm_opp_data *so = mfg->stack_opps;
658*f08e7a4eSNicolas Frattaroli 	struct dev_pm_opp_data *go = mfg->gpu_opps;
659*f08e7a4eSNicolas Frattaroli 	struct dev_pm_opp_data *prev_o;
660*f08e7a4eSNicolas Frattaroli 	struct dev_pm_opp_data *o;
661*f08e7a4eSNicolas Frattaroli 	int i, ret;
662*f08e7a4eSNicolas Frattaroli 
663*f08e7a4eSNicolas Frattaroli 	for (i = mfg->num_gpu_opps - 1; i >= 0; i--) {
664*f08e7a4eSNicolas Frattaroli 		/*
665*f08e7a4eSNicolas Frattaroli 		 * Adding the lower of the two OPPs avoids gaps of indices in
666*f08e7a4eSNicolas Frattaroli 		 * situations where the GPU OPPs are duplicated a couple of
667*f08e7a4eSNicolas Frattaroli 		 * times when only the Stack OPP is being lowered at that index.
668*f08e7a4eSNicolas Frattaroli 		 */
669*f08e7a4eSNicolas Frattaroli 		if (i >= mfg->num_stack_opps || go[i].freq < so[i].freq)
670*f08e7a4eSNicolas Frattaroli 			o = &go[i];
671*f08e7a4eSNicolas Frattaroli 		else
672*f08e7a4eSNicolas Frattaroli 			o = &so[i];
673*f08e7a4eSNicolas Frattaroli 
674*f08e7a4eSNicolas Frattaroli 		/*
675*f08e7a4eSNicolas Frattaroli 		 * Skip indices where both GPU and Stack OPPs are equal. Nominally,
676*f08e7a4eSNicolas Frattaroli 		 * OPP core shouldn't care about dupes, but not doing so will cause
677*f08e7a4eSNicolas Frattaroli 		 * dev_pm_opp_find_freq_ceil_indexed to -ERANGE later down the line.
678*f08e7a4eSNicolas Frattaroli 		 */
679*f08e7a4eSNicolas Frattaroli 		if (prev_o && prev_o->freq == o->freq)
680*f08e7a4eSNicolas Frattaroli 			continue;
681*f08e7a4eSNicolas Frattaroli 
682*f08e7a4eSNicolas Frattaroli 		ret = dev_pm_opp_add_dynamic(dev, o);
683*f08e7a4eSNicolas Frattaroli 		if (ret) {
684*f08e7a4eSNicolas Frattaroli 			dev_err(dev, "Failed to add OPP level %u from PD %s: %pe\n",
685*f08e7a4eSNicolas Frattaroli 				o->level, pd->name, ERR_PTR(ret));
686*f08e7a4eSNicolas Frattaroli 			dev_pm_opp_remove_all_dynamic(dev);
687*f08e7a4eSNicolas Frattaroli 			return ret;
688*f08e7a4eSNicolas Frattaroli 		}
689*f08e7a4eSNicolas Frattaroli 		prev_o = o;
690*f08e7a4eSNicolas Frattaroli 	}
691*f08e7a4eSNicolas Frattaroli 
692*f08e7a4eSNicolas Frattaroli 	return 0;
693*f08e7a4eSNicolas Frattaroli }
694*f08e7a4eSNicolas Frattaroli 
695*f08e7a4eSNicolas Frattaroli static void mtk_mfg_detach_dev(struct generic_pm_domain *pd, struct device *dev)
696*f08e7a4eSNicolas Frattaroli {
697*f08e7a4eSNicolas Frattaroli 	dev_pm_opp_remove_all_dynamic(dev);
698*f08e7a4eSNicolas Frattaroli }
699*f08e7a4eSNicolas Frattaroli 
700*f08e7a4eSNicolas Frattaroli static int mtk_mfg_set_performance(struct generic_pm_domain *pd,
701*f08e7a4eSNicolas Frattaroli 				   unsigned int state)
702*f08e7a4eSNicolas Frattaroli {
703*f08e7a4eSNicolas Frattaroli 	struct mtk_mfg *mfg = mtk_mfg_from_genpd(pd);
704*f08e7a4eSNicolas Frattaroli 
705*f08e7a4eSNicolas Frattaroli 	/*
706*f08e7a4eSNicolas Frattaroli 	 * pmdomain core intentionally sets a performance state before turning
707*f08e7a4eSNicolas Frattaroli 	 * a domain on, and after turning it off. For the GPUEB however, it's
708*f08e7a4eSNicolas Frattaroli 	 * only possible to act on performance requests when the GPUEB is
709*f08e7a4eSNicolas Frattaroli 	 * powered on. To do this, return cleanly without taking action, and
710*f08e7a4eSNicolas Frattaroli 	 * defer setting what pmdomain core set in mtk_mfg_power_on.
711*f08e7a4eSNicolas Frattaroli 	 */
712*f08e7a4eSNicolas Frattaroli 	if (mfg->pd.status != GENPD_STATE_ON)
713*f08e7a4eSNicolas Frattaroli 		return 0;
714*f08e7a4eSNicolas Frattaroli 
715*f08e7a4eSNicolas Frattaroli 	return mtk_mfg_set_oppidx(mfg, state);
716*f08e7a4eSNicolas Frattaroli }
717*f08e7a4eSNicolas Frattaroli 
718*f08e7a4eSNicolas Frattaroli static int mtk_mfg_power_on(struct generic_pm_domain *pd)
719*f08e7a4eSNicolas Frattaroli {
720*f08e7a4eSNicolas Frattaroli 	struct mtk_mfg *mfg = mtk_mfg_from_genpd(pd);
721*f08e7a4eSNicolas Frattaroli 	int ret;
722*f08e7a4eSNicolas Frattaroli 
723*f08e7a4eSNicolas Frattaroli 	ret = regulator_bulk_enable(mfg->variant->num_regulators,
724*f08e7a4eSNicolas Frattaroli 				    mfg->gpu_regs);
725*f08e7a4eSNicolas Frattaroli 	if (ret)
726*f08e7a4eSNicolas Frattaroli 		return ret;
727*f08e7a4eSNicolas Frattaroli 
728*f08e7a4eSNicolas Frattaroli 	ret = clk_prepare_enable(mfg->clk_eb);
729*f08e7a4eSNicolas Frattaroli 	if (ret)
730*f08e7a4eSNicolas Frattaroli 		goto err_disable_regulators;
731*f08e7a4eSNicolas Frattaroli 
732*f08e7a4eSNicolas Frattaroli 	ret = clk_bulk_prepare_enable(mfg->variant->num_clks, mfg->gpu_clks);
733*f08e7a4eSNicolas Frattaroli 	if (ret)
734*f08e7a4eSNicolas Frattaroli 		goto err_disable_eb_clk;
735*f08e7a4eSNicolas Frattaroli 
736*f08e7a4eSNicolas Frattaroli 	ret = mtk_mfg_eb_on(mfg);
737*f08e7a4eSNicolas Frattaroli 	if (ret)
738*f08e7a4eSNicolas Frattaroli 		goto err_disable_clks;
739*f08e7a4eSNicolas Frattaroli 
740*f08e7a4eSNicolas Frattaroli 	mfg->ipi_magic = readl(mfg->gpr + GPR_IPI_MAGIC);
741*f08e7a4eSNicolas Frattaroli 
742*f08e7a4eSNicolas Frattaroli 	ret = mtk_mfg_power_control(mfg, true);
743*f08e7a4eSNicolas Frattaroli 	if (ret)
744*f08e7a4eSNicolas Frattaroli 		goto err_eb_off;
745*f08e7a4eSNicolas Frattaroli 
746*f08e7a4eSNicolas Frattaroli 	/* Don't try to set a OPP in probe before OPPs have been read from EB */
747*f08e7a4eSNicolas Frattaroli 	if (mfg->gpu_opps) {
748*f08e7a4eSNicolas Frattaroli 		/* The aforementioned deferred setting of pmdomain's state */
749*f08e7a4eSNicolas Frattaroli 		ret = mtk_mfg_set_oppidx(mfg, pd->performance_state);
750*f08e7a4eSNicolas Frattaroli 		if (ret)
751*f08e7a4eSNicolas Frattaroli 			dev_warn(&mfg->pdev->dev, "Failed to set oppidx in %s\n", __func__);
752*f08e7a4eSNicolas Frattaroli 	}
753*f08e7a4eSNicolas Frattaroli 
754*f08e7a4eSNicolas Frattaroli 	return 0;
755*f08e7a4eSNicolas Frattaroli 
756*f08e7a4eSNicolas Frattaroli err_eb_off:
757*f08e7a4eSNicolas Frattaroli 	mtk_mfg_eb_off(mfg);
758*f08e7a4eSNicolas Frattaroli err_disable_clks:
759*f08e7a4eSNicolas Frattaroli 	clk_bulk_disable_unprepare(mfg->variant->num_clks, mfg->gpu_clks);
760*f08e7a4eSNicolas Frattaroli err_disable_eb_clk:
761*f08e7a4eSNicolas Frattaroli 	clk_disable_unprepare(mfg->clk_eb);
762*f08e7a4eSNicolas Frattaroli err_disable_regulators:
763*f08e7a4eSNicolas Frattaroli 	regulator_bulk_disable(mfg->variant->num_regulators, mfg->gpu_regs);
764*f08e7a4eSNicolas Frattaroli 
765*f08e7a4eSNicolas Frattaroli 	return ret;
766*f08e7a4eSNicolas Frattaroli }
767*f08e7a4eSNicolas Frattaroli 
768*f08e7a4eSNicolas Frattaroli static int mtk_mfg_power_off(struct generic_pm_domain *pd)
769*f08e7a4eSNicolas Frattaroli {
770*f08e7a4eSNicolas Frattaroli 	struct mtk_mfg *mfg = mtk_mfg_from_genpd(pd);
771*f08e7a4eSNicolas Frattaroli 	struct device *dev = &mfg->pdev->dev;
772*f08e7a4eSNicolas Frattaroli 	int ret;
773*f08e7a4eSNicolas Frattaroli 
774*f08e7a4eSNicolas Frattaroli 	ret = mtk_mfg_power_control(mfg, false);
775*f08e7a4eSNicolas Frattaroli 	if (ret) {
776*f08e7a4eSNicolas Frattaroli 		dev_err(dev, "power_control failed: %pe\n", ERR_PTR(ret));
777*f08e7a4eSNicolas Frattaroli 		return ret;
778*f08e7a4eSNicolas Frattaroli 	}
779*f08e7a4eSNicolas Frattaroli 
780*f08e7a4eSNicolas Frattaroli 	ret = mtk_mfg_eb_off(mfg);
781*f08e7a4eSNicolas Frattaroli 	if (ret) {
782*f08e7a4eSNicolas Frattaroli 		dev_err(dev, "eb_off failed: %pe\n", ERR_PTR(ret));
783*f08e7a4eSNicolas Frattaroli 		return ret;
784*f08e7a4eSNicolas Frattaroli 	}
785*f08e7a4eSNicolas Frattaroli 
786*f08e7a4eSNicolas Frattaroli 	clk_bulk_disable_unprepare(mfg->variant->num_clks, mfg->gpu_clks);
787*f08e7a4eSNicolas Frattaroli 	clk_disable_unprepare(mfg->clk_eb);
788*f08e7a4eSNicolas Frattaroli 	ret = regulator_bulk_disable(mfg->variant->num_regulators, mfg->gpu_regs);
789*f08e7a4eSNicolas Frattaroli 	if (ret) {
790*f08e7a4eSNicolas Frattaroli 		dev_err(dev, "Disabling regulators failed: %pe\n", ERR_PTR(ret));
791*f08e7a4eSNicolas Frattaroli 		return ret;
792*f08e7a4eSNicolas Frattaroli 	}
793*f08e7a4eSNicolas Frattaroli 
794*f08e7a4eSNicolas Frattaroli 	return 0;
795*f08e7a4eSNicolas Frattaroli }
796*f08e7a4eSNicolas Frattaroli 
797*f08e7a4eSNicolas Frattaroli static int mtk_mfg_init_mbox(struct mtk_mfg *mfg)
798*f08e7a4eSNicolas Frattaroli {
799*f08e7a4eSNicolas Frattaroli 	struct device *dev = &mfg->pdev->dev;
800*f08e7a4eSNicolas Frattaroli 	struct mtk_mfg_mbox *gf;
801*f08e7a4eSNicolas Frattaroli 	struct mtk_mfg_mbox *slp;
802*f08e7a4eSNicolas Frattaroli 
803*f08e7a4eSNicolas Frattaroli 	gf = devm_kzalloc(dev, sizeof(*gf), GFP_KERNEL);
804*f08e7a4eSNicolas Frattaroli 	if (!gf)
805*f08e7a4eSNicolas Frattaroli 		return -ENOMEM;
806*f08e7a4eSNicolas Frattaroli 
807*f08e7a4eSNicolas Frattaroli 	gf->rx_data = devm_kzalloc(dev, GPUEB_MBOX_MAX_RX_SIZE, GFP_KERNEL);
808*f08e7a4eSNicolas Frattaroli 	if (!gf->rx_data)
809*f08e7a4eSNicolas Frattaroli 		return -ENOMEM;
810*f08e7a4eSNicolas Frattaroli 
811*f08e7a4eSNicolas Frattaroli 	gf->mfg = mfg;
812*f08e7a4eSNicolas Frattaroli 	init_completion(&gf->rx_done);
813*f08e7a4eSNicolas Frattaroli 	gf->cl.dev = dev;
814*f08e7a4eSNicolas Frattaroli 	gf->cl.rx_callback = mtk_mfg_mbox_rx_callback;
815*f08e7a4eSNicolas Frattaroli 	gf->cl.tx_tout = GPUEB_TIMEOUT_US / USEC_PER_MSEC;
816*f08e7a4eSNicolas Frattaroli 	gf->ch = mbox_request_channel_byname(&gf->cl, "gpufreq");
817*f08e7a4eSNicolas Frattaroli 	if (IS_ERR(gf->ch))
818*f08e7a4eSNicolas Frattaroli 		return PTR_ERR(gf->ch);
819*f08e7a4eSNicolas Frattaroli 
820*f08e7a4eSNicolas Frattaroli 	mfg->gf_mbox = gf;
821*f08e7a4eSNicolas Frattaroli 
822*f08e7a4eSNicolas Frattaroli 	slp = devm_kzalloc(dev, sizeof(*slp), GFP_KERNEL);
823*f08e7a4eSNicolas Frattaroli 	if (!slp)
824*f08e7a4eSNicolas Frattaroli 		return -ENOMEM;
825*f08e7a4eSNicolas Frattaroli 
826*f08e7a4eSNicolas Frattaroli 	slp->mfg = mfg;
827*f08e7a4eSNicolas Frattaroli 	init_completion(&slp->rx_done);
828*f08e7a4eSNicolas Frattaroli 	slp->cl.dev = dev;
829*f08e7a4eSNicolas Frattaroli 	slp->cl.tx_tout = GPUEB_TIMEOUT_US / USEC_PER_MSEC;
830*f08e7a4eSNicolas Frattaroli 	slp->cl.tx_block = true;
831*f08e7a4eSNicolas Frattaroli 	slp->ch = mbox_request_channel_byname(&slp->cl, "sleep");
832*f08e7a4eSNicolas Frattaroli 	if (IS_ERR(slp->ch)) {
833*f08e7a4eSNicolas Frattaroli 		mbox_free_channel(gf->ch);
834*f08e7a4eSNicolas Frattaroli 		return PTR_ERR(slp->ch);
835*f08e7a4eSNicolas Frattaroli 	}
836*f08e7a4eSNicolas Frattaroli 
837*f08e7a4eSNicolas Frattaroli 	mfg->slp_mbox = slp;
838*f08e7a4eSNicolas Frattaroli 
839*f08e7a4eSNicolas Frattaroli 	return 0;
840*f08e7a4eSNicolas Frattaroli }
841*f08e7a4eSNicolas Frattaroli 
842*f08e7a4eSNicolas Frattaroli static int mtk_mfg_init_clk_provider(struct mtk_mfg *mfg)
843*f08e7a4eSNicolas Frattaroli {
844*f08e7a4eSNicolas Frattaroli 	struct device *dev = &mfg->pdev->dev;
845*f08e7a4eSNicolas Frattaroli 	struct clk_hw_onecell_data *clk_data;
846*f08e7a4eSNicolas Frattaroli 	int ret;
847*f08e7a4eSNicolas Frattaroli 
848*f08e7a4eSNicolas Frattaroli 	clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, 2), GFP_KERNEL);
849*f08e7a4eSNicolas Frattaroli 	if (!clk_data)
850*f08e7a4eSNicolas Frattaroli 		return -ENOMEM;
851*f08e7a4eSNicolas Frattaroli 
852*f08e7a4eSNicolas Frattaroli 	clk_data->num = 2;
853*f08e7a4eSNicolas Frattaroli 
854*f08e7a4eSNicolas Frattaroli 	mfg->clk_core_hw.init = &mtk_mfg_clk_gpu_init;
855*f08e7a4eSNicolas Frattaroli 	mfg->clk_stack_hw.init = &mtk_mfg_clk_stack_init;
856*f08e7a4eSNicolas Frattaroli 
857*f08e7a4eSNicolas Frattaroli 	ret = devm_clk_hw_register(dev, &mfg->clk_core_hw);
858*f08e7a4eSNicolas Frattaroli 	if (ret)
859*f08e7a4eSNicolas Frattaroli 		return dev_err_probe(dev, ret, "Couldn't register GPU core clock\n");
860*f08e7a4eSNicolas Frattaroli 
861*f08e7a4eSNicolas Frattaroli 	ret = devm_clk_hw_register(dev, &mfg->clk_stack_hw);
862*f08e7a4eSNicolas Frattaroli 	if (ret)
863*f08e7a4eSNicolas Frattaroli 		return dev_err_probe(dev, ret, "Couldn't register GPU stack clock\n");
864*f08e7a4eSNicolas Frattaroli 
865*f08e7a4eSNicolas Frattaroli 	clk_data->hws[0] = &mfg->clk_core_hw;
866*f08e7a4eSNicolas Frattaroli 	clk_data->hws[1] = &mfg->clk_stack_hw;
867*f08e7a4eSNicolas Frattaroli 
868*f08e7a4eSNicolas Frattaroli 	ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data);
869*f08e7a4eSNicolas Frattaroli 	if (ret)
870*f08e7a4eSNicolas Frattaroli 		return dev_err_probe(dev, ret, "Couldn't register clock provider\n");
871*f08e7a4eSNicolas Frattaroli 
872*f08e7a4eSNicolas Frattaroli 	return 0;
873*f08e7a4eSNicolas Frattaroli }
874*f08e7a4eSNicolas Frattaroli 
875*f08e7a4eSNicolas Frattaroli static int mtk_mfg_probe(struct platform_device *pdev)
876*f08e7a4eSNicolas Frattaroli {
877*f08e7a4eSNicolas Frattaroli 	struct mtk_mfg *mfg;
878*f08e7a4eSNicolas Frattaroli 	struct device *dev = &pdev->dev;
879*f08e7a4eSNicolas Frattaroli 	const struct mtk_mfg_variant *data = of_device_get_match_data(dev);
880*f08e7a4eSNicolas Frattaroli 	struct resource res;
881*f08e7a4eSNicolas Frattaroli 	int ret, i;
882*f08e7a4eSNicolas Frattaroli 
883*f08e7a4eSNicolas Frattaroli 	mfg = devm_kzalloc(dev, sizeof(*mfg), GFP_KERNEL);
884*f08e7a4eSNicolas Frattaroli 	if (!mfg)
885*f08e7a4eSNicolas Frattaroli 		return -ENOMEM;
886*f08e7a4eSNicolas Frattaroli 
887*f08e7a4eSNicolas Frattaroli 	mfg->pdev = pdev;
888*f08e7a4eSNicolas Frattaroli 	mfg->variant = data;
889*f08e7a4eSNicolas Frattaroli 
890*f08e7a4eSNicolas Frattaroli 	dev_set_drvdata(dev, mfg);
891*f08e7a4eSNicolas Frattaroli 
892*f08e7a4eSNicolas Frattaroli 	mfg->gpr = devm_platform_ioremap_resource(pdev, 0);
893*f08e7a4eSNicolas Frattaroli 	if (IS_ERR(mfg->gpr))
894*f08e7a4eSNicolas Frattaroli 		return dev_err_probe(dev, PTR_ERR(mfg->gpr),
895*f08e7a4eSNicolas Frattaroli 				     "Couldn't retrieve GPR MMIO registers\n");
896*f08e7a4eSNicolas Frattaroli 
897*f08e7a4eSNicolas Frattaroli 	mfg->rpc = devm_platform_ioremap_resource(pdev, 1);
898*f08e7a4eSNicolas Frattaroli 	if (IS_ERR(mfg->rpc))
899*f08e7a4eSNicolas Frattaroli 		return dev_err_probe(dev, PTR_ERR(mfg->rpc),
900*f08e7a4eSNicolas Frattaroli 				     "Couldn't retrieve RPC MMIO registers\n");
901*f08e7a4eSNicolas Frattaroli 
902*f08e7a4eSNicolas Frattaroli 	mfg->clk_eb = devm_clk_get(dev, "eb");
903*f08e7a4eSNicolas Frattaroli 	if (IS_ERR(mfg->clk_eb))
904*f08e7a4eSNicolas Frattaroli 		return dev_err_probe(dev, PTR_ERR(mfg->clk_eb),
905*f08e7a4eSNicolas Frattaroli 				     "Couldn't get 'eb' clock\n");
906*f08e7a4eSNicolas Frattaroli 
907*f08e7a4eSNicolas Frattaroli 	mfg->gpu_clks = devm_kcalloc(dev, data->num_clks, sizeof(*mfg->gpu_clks),
908*f08e7a4eSNicolas Frattaroli 				     GFP_KERNEL);
909*f08e7a4eSNicolas Frattaroli 	if (!mfg->gpu_clks)
910*f08e7a4eSNicolas Frattaroli 		return -ENOMEM;
911*f08e7a4eSNicolas Frattaroli 
912*f08e7a4eSNicolas Frattaroli 	for (i = 0; i < data->num_clks; i++)
913*f08e7a4eSNicolas Frattaroli 		mfg->gpu_clks[i].id = data->clk_names[i];
914*f08e7a4eSNicolas Frattaroli 
915*f08e7a4eSNicolas Frattaroli 	ret = devm_clk_bulk_get(dev, data->num_clks, mfg->gpu_clks);
916*f08e7a4eSNicolas Frattaroli 	if (ret)
917*f08e7a4eSNicolas Frattaroli 		return dev_err_probe(dev, ret, "Couldn't get GPU clocks\n");
918*f08e7a4eSNicolas Frattaroli 
919*f08e7a4eSNicolas Frattaroli 	mfg->gpu_regs = devm_kcalloc(dev, data->num_regulators,
920*f08e7a4eSNicolas Frattaroli 				     sizeof(*mfg->gpu_regs), GFP_KERNEL);
921*f08e7a4eSNicolas Frattaroli 	if (!mfg->gpu_regs)
922*f08e7a4eSNicolas Frattaroli 		return -ENOMEM;
923*f08e7a4eSNicolas Frattaroli 
924*f08e7a4eSNicolas Frattaroli 	for (i = 0; i < data->num_regulators; i++)
925*f08e7a4eSNicolas Frattaroli 		mfg->gpu_regs[i].supply = data->regulator_names[i];
926*f08e7a4eSNicolas Frattaroli 
927*f08e7a4eSNicolas Frattaroli 	ret = devm_regulator_bulk_get(dev, data->num_regulators, mfg->gpu_regs);
928*f08e7a4eSNicolas Frattaroli 	if (ret)
929*f08e7a4eSNicolas Frattaroli 		return dev_err_probe(dev, ret, "Couldn't get GPU regulators\n");
930*f08e7a4eSNicolas Frattaroli 
931*f08e7a4eSNicolas Frattaroli 	ret = of_reserved_mem_region_to_resource(dev->of_node, 0, &res);
932*f08e7a4eSNicolas Frattaroli 	if (ret)
933*f08e7a4eSNicolas Frattaroli 		return dev_err_probe(dev, ret, "Couldn't get GPUEB shared memory\n");
934*f08e7a4eSNicolas Frattaroli 
935*f08e7a4eSNicolas Frattaroli 	mfg->shared_mem = devm_ioremap(dev, res.start, resource_size(&res));
936*f08e7a4eSNicolas Frattaroli 	if (!mfg->shared_mem)
937*f08e7a4eSNicolas Frattaroli 		return dev_err_probe(dev, -ENOMEM, "Can't ioremap GPUEB shared memory\n");
938*f08e7a4eSNicolas Frattaroli 	mfg->shared_mem_size = resource_size(&res);
939*f08e7a4eSNicolas Frattaroli 	mfg->shared_mem_phys = res.start;
940*f08e7a4eSNicolas Frattaroli 
941*f08e7a4eSNicolas Frattaroli 	if (data->init) {
942*f08e7a4eSNicolas Frattaroli 		ret = data->init(mfg);
943*f08e7a4eSNicolas Frattaroli 		if (ret)
944*f08e7a4eSNicolas Frattaroli 			return dev_err_probe(dev, ret, "Variant init failed\n");
945*f08e7a4eSNicolas Frattaroli 	}
946*f08e7a4eSNicolas Frattaroli 
947*f08e7a4eSNicolas Frattaroli 	mfg->pd.name = dev_name(dev);
948*f08e7a4eSNicolas Frattaroli 	mfg->pd.attach_dev = mtk_mfg_attach_dev;
949*f08e7a4eSNicolas Frattaroli 	mfg->pd.detach_dev = mtk_mfg_detach_dev;
950*f08e7a4eSNicolas Frattaroli 	mfg->pd.power_off = mtk_mfg_power_off;
951*f08e7a4eSNicolas Frattaroli 	mfg->pd.power_on = mtk_mfg_power_on;
952*f08e7a4eSNicolas Frattaroli 	mfg->pd.set_performance_state = mtk_mfg_set_performance;
953*f08e7a4eSNicolas Frattaroli 	mfg->pd.flags = GENPD_FLAG_OPP_TABLE_FW;
954*f08e7a4eSNicolas Frattaroli 
955*f08e7a4eSNicolas Frattaroli 	ret = pm_genpd_init(&mfg->pd, NULL, false);
956*f08e7a4eSNicolas Frattaroli 	if (ret)
957*f08e7a4eSNicolas Frattaroli 		return dev_err_probe(dev, ret, "Failed to initialise power domain\n");
958*f08e7a4eSNicolas Frattaroli 
959*f08e7a4eSNicolas Frattaroli 	ret = mtk_mfg_init_mbox(mfg);
960*f08e7a4eSNicolas Frattaroli 	if (ret) {
961*f08e7a4eSNicolas Frattaroli 		dev_err_probe(dev, ret, "Couldn't initialise mailbox\n");
962*f08e7a4eSNicolas Frattaroli 		goto err_remove_genpd;
963*f08e7a4eSNicolas Frattaroli 	}
964*f08e7a4eSNicolas Frattaroli 
965*f08e7a4eSNicolas Frattaroli 	ret = mtk_mfg_power_on(&mfg->pd);
966*f08e7a4eSNicolas Frattaroli 	if (ret) {
967*f08e7a4eSNicolas Frattaroli 		dev_err_probe(dev, ret, "Failed to power on MFG\n");
968*f08e7a4eSNicolas Frattaroli 		goto err_free_mbox;
969*f08e7a4eSNicolas Frattaroli 	}
970*f08e7a4eSNicolas Frattaroli 
971*f08e7a4eSNicolas Frattaroli 	ret = mtk_mfg_init_shared_mem(mfg);
972*f08e7a4eSNicolas Frattaroli 	if (ret) {
973*f08e7a4eSNicolas Frattaroli 		dev_err_probe(dev, ret, "Couldn't initialize EB shared memory\n");
974*f08e7a4eSNicolas Frattaroli 		goto err_power_off;
975*f08e7a4eSNicolas Frattaroli 	}
976*f08e7a4eSNicolas Frattaroli 
977*f08e7a4eSNicolas Frattaroli 	ret = mtk_mfg_read_opp_tables(mfg);
978*f08e7a4eSNicolas Frattaroli 	if (ret) {
979*f08e7a4eSNicolas Frattaroli 		dev_err_probe(dev, ret, "Error reading OPP tables from EB\n");
980*f08e7a4eSNicolas Frattaroli 		goto err_power_off;
981*f08e7a4eSNicolas Frattaroli 	}
982*f08e7a4eSNicolas Frattaroli 
983*f08e7a4eSNicolas Frattaroli 	ret = mtk_mfg_init_clk_provider(mfg);
984*f08e7a4eSNicolas Frattaroli 	if (ret)
985*f08e7a4eSNicolas Frattaroli 		goto err_power_off;
986*f08e7a4eSNicolas Frattaroli 
987*f08e7a4eSNicolas Frattaroli 	ret = of_genpd_add_provider_simple(dev->of_node, &mfg->pd);
988*f08e7a4eSNicolas Frattaroli 	if (ret) {
989*f08e7a4eSNicolas Frattaroli 		dev_err_probe(dev, ret, "Failed to add pmdomain provider\n");
990*f08e7a4eSNicolas Frattaroli 		goto err_power_off;
991*f08e7a4eSNicolas Frattaroli 	}
992*f08e7a4eSNicolas Frattaroli 
993*f08e7a4eSNicolas Frattaroli 	return 0;
994*f08e7a4eSNicolas Frattaroli 
995*f08e7a4eSNicolas Frattaroli err_power_off:
996*f08e7a4eSNicolas Frattaroli 	mtk_mfg_power_off(&mfg->pd);
997*f08e7a4eSNicolas Frattaroli err_free_mbox:
998*f08e7a4eSNicolas Frattaroli 	mbox_free_channel(mfg->slp_mbox->ch);
999*f08e7a4eSNicolas Frattaroli 	mfg->slp_mbox->ch = NULL;
1000*f08e7a4eSNicolas Frattaroli 	mbox_free_channel(mfg->gf_mbox->ch);
1001*f08e7a4eSNicolas Frattaroli 	mfg->gf_mbox->ch = NULL;
1002*f08e7a4eSNicolas Frattaroli err_remove_genpd:
1003*f08e7a4eSNicolas Frattaroli 	pm_genpd_remove(&mfg->pd);
1004*f08e7a4eSNicolas Frattaroli 
1005*f08e7a4eSNicolas Frattaroli 	return ret;
1006*f08e7a4eSNicolas Frattaroli }
1007*f08e7a4eSNicolas Frattaroli 
1008*f08e7a4eSNicolas Frattaroli static const struct of_device_id mtk_mfg_of_match[] = {
1009*f08e7a4eSNicolas Frattaroli 	{ .compatible = "mediatek,mt8196-gpufreq", .data = &mtk_mfg_mt8196_variant },
1010*f08e7a4eSNicolas Frattaroli 	{}
1011*f08e7a4eSNicolas Frattaroli };
1012*f08e7a4eSNicolas Frattaroli MODULE_DEVICE_TABLE(of, mtk_mfg_of_match);
1013*f08e7a4eSNicolas Frattaroli 
1014*f08e7a4eSNicolas Frattaroli static void mtk_mfg_remove(struct platform_device *pdev)
1015*f08e7a4eSNicolas Frattaroli {
1016*f08e7a4eSNicolas Frattaroli 	struct mtk_mfg *mfg = dev_get_drvdata(&pdev->dev);
1017*f08e7a4eSNicolas Frattaroli 
1018*f08e7a4eSNicolas Frattaroli 	if (mtk_mfg_is_powered_on(mfg))
1019*f08e7a4eSNicolas Frattaroli 		mtk_mfg_power_off(&mfg->pd);
1020*f08e7a4eSNicolas Frattaroli 
1021*f08e7a4eSNicolas Frattaroli 	of_genpd_del_provider(pdev->dev.of_node);
1022*f08e7a4eSNicolas Frattaroli 	pm_genpd_remove(&mfg->pd);
1023*f08e7a4eSNicolas Frattaroli 
1024*f08e7a4eSNicolas Frattaroli 	mbox_free_channel(mfg->gf_mbox->ch);
1025*f08e7a4eSNicolas Frattaroli 	mfg->gf_mbox->ch = NULL;
1026*f08e7a4eSNicolas Frattaroli 
1027*f08e7a4eSNicolas Frattaroli 	mbox_free_channel(mfg->slp_mbox->ch);
1028*f08e7a4eSNicolas Frattaroli 	mfg->slp_mbox->ch = NULL;
1029*f08e7a4eSNicolas Frattaroli }
1030*f08e7a4eSNicolas Frattaroli 
1031*f08e7a4eSNicolas Frattaroli static struct platform_driver mtk_mfg_driver = {
1032*f08e7a4eSNicolas Frattaroli 	.driver = {
1033*f08e7a4eSNicolas Frattaroli 		.name = "mtk-mfg-pmdomain",
1034*f08e7a4eSNicolas Frattaroli 		.of_match_table = mtk_mfg_of_match,
1035*f08e7a4eSNicolas Frattaroli 		.suppress_bind_attrs = true,
1036*f08e7a4eSNicolas Frattaroli 	},
1037*f08e7a4eSNicolas Frattaroli 	.probe = mtk_mfg_probe,
1038*f08e7a4eSNicolas Frattaroli 	.remove = mtk_mfg_remove,
1039*f08e7a4eSNicolas Frattaroli };
1040*f08e7a4eSNicolas Frattaroli module_platform_driver(mtk_mfg_driver);
1041*f08e7a4eSNicolas Frattaroli 
1042*f08e7a4eSNicolas Frattaroli MODULE_AUTHOR("Nicolas Frattaroli <nicolas.frattaroli@collabora.com>");
1043*f08e7a4eSNicolas Frattaroli MODULE_DESCRIPTION("MediaTek MFlexGraphics Power Domain Driver");
1044*f08e7a4eSNicolas Frattaroli MODULE_LICENSE("GPL");
1045