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