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