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