developer | 555ed55 | 2019-08-21 22:49:49 +0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved. |
| 3 | * |
| 4 | * SPDX-License-Identifier: BSD-3-Clause |
| 5 | */ |
| 6 | |
| 7 | #include <common/debug.h> |
| 8 | #include <drivers/delay_timer.h> |
| 9 | #include <lib/mmio.h> |
| 10 | #include <sspm_reg.h> |
| 11 | #include <mtk_mcdi.h> |
| 12 | |
| 13 | static inline uint32_t mcdi_mbox_read(uint32_t id) |
| 14 | { |
| 15 | return mmio_read_32(SSPM_MBOX_3_BASE + (id << 2)); |
| 16 | } |
| 17 | |
| 18 | static inline void mcdi_mbox_write(uint32_t id, uint32_t val) |
| 19 | { |
| 20 | mmio_write_32(SSPM_MBOX_3_BASE + (id << 2), val); |
| 21 | } |
| 22 | |
| 23 | void sspm_set_bootaddr(uint32_t bootaddr) |
| 24 | { |
| 25 | mcdi_mbox_write(MCDI_MBOX_BOOTADDR, bootaddr); |
| 26 | } |
| 27 | |
| 28 | void sspm_cluster_pwr_off_notify(uint32_t cluster) |
| 29 | { |
| 30 | mcdi_mbox_write(MCDI_MBOX_CLUSTER_0_ATF_ACTION_DONE + cluster, 1); |
| 31 | } |
| 32 | |
| 33 | void sspm_cluster_pwr_on_notify(uint32_t cluster) |
| 34 | { |
| 35 | mcdi_mbox_write(MCDI_MBOX_CLUSTER_0_ATF_ACTION_DONE + cluster, 0); |
| 36 | } |
| 37 | |
| 38 | void sspm_standbywfi_irq_enable(uint32_t cpu_idx) |
| 39 | { |
| 40 | mmio_write_32(SSPM_CFGREG_ACAO_INT_SET, STANDBYWFI_EN(cpu_idx)); |
| 41 | } |
| 42 | |
| 43 | uint32_t mcdi_avail_cpu_mask_read(void) |
| 44 | { |
| 45 | return mcdi_mbox_read(MCDI_MBOX_AVAIL_CPU_MASK); |
| 46 | } |
| 47 | |
| 48 | uint32_t mcdi_avail_cpu_mask_write(uint32_t mask) |
| 49 | { |
| 50 | mcdi_mbox_write(MCDI_MBOX_AVAIL_CPU_MASK, mask); |
| 51 | |
| 52 | return mask; |
| 53 | } |
| 54 | |
| 55 | uint32_t mcdi_avail_cpu_mask_set(uint32_t mask) |
| 56 | { |
| 57 | uint32_t m; |
| 58 | |
| 59 | m = mcdi_mbox_read(MCDI_MBOX_AVAIL_CPU_MASK); |
| 60 | m |= mask; |
| 61 | mcdi_mbox_write(MCDI_MBOX_AVAIL_CPU_MASK, m); |
| 62 | |
| 63 | return m; |
| 64 | } |
| 65 | |
| 66 | uint32_t mcdi_avail_cpu_mask_clr(uint32_t mask) |
| 67 | { |
| 68 | uint32_t m; |
| 69 | |
| 70 | m = mcdi_mbox_read(MCDI_MBOX_AVAIL_CPU_MASK); |
| 71 | m &= ~mask; |
| 72 | mcdi_mbox_write(MCDI_MBOX_AVAIL_CPU_MASK, m); |
| 73 | |
| 74 | return m; |
| 75 | } |
| 76 | |
| 77 | uint32_t mcdi_cpu_cluster_pwr_stat_read(void) |
| 78 | { |
| 79 | return mcdi_mbox_read(MCDI_MBOX_CPU_CLUSTER_PWR_STAT); |
| 80 | } |
| 81 | |
| 82 | #define PAUSE_BIT 1 |
| 83 | #define CLUSTER_OFF_OFS 20 |
| 84 | #define CPU_OFF_OFS 24 |
| 85 | #define CLUSTER_ON_OFS 4 |
| 86 | #define CPU_ON_OFS 8 |
| 87 | |
| 88 | static uint32_t target_mask(int cluster, int cpu_idx, bool on) |
| 89 | { |
| 90 | uint32_t t = 0; |
| 91 | |
| 92 | if (on) { |
| 93 | if (cluster >= 0) |
| 94 | t |= BIT(cluster + CLUSTER_ON_OFS); |
| 95 | |
| 96 | if (cpu_idx >= 0) |
| 97 | t |= BIT(cpu_idx + CPU_ON_OFS); |
| 98 | } else { |
| 99 | if (cluster >= 0) |
| 100 | t |= BIT(cluster + CLUSTER_OFF_OFS); |
| 101 | |
| 102 | if (cpu_idx >= 0) |
| 103 | t |= BIT(cpu_idx + CPU_OFF_OFS); |
| 104 | } |
| 105 | |
| 106 | return t; |
| 107 | } |
| 108 | |
| 109 | void mcdi_pause_clr(int cluster, int cpu_idx, bool on) |
| 110 | { |
| 111 | uint32_t tgt = target_mask(cluster, cpu_idx, on); |
| 112 | uint32_t m = mcdi_mbox_read(MCDI_MBOX_PAUSE_ACTION); |
| 113 | |
| 114 | m &= ~tgt; |
| 115 | mcdi_mbox_write(MCDI_MBOX_PAUSE_ACTION, m); |
| 116 | } |
| 117 | |
| 118 | void mcdi_pause_set(int cluster, int cpu_idx, bool on) |
| 119 | { |
| 120 | uint32_t tgt = target_mask(cluster, cpu_idx, on); |
| 121 | uint32_t m = mcdi_mbox_read(MCDI_MBOX_PAUSE_ACTION); |
| 122 | uint32_t tgtn = target_mask(-1, cpu_idx, !on); |
| 123 | |
| 124 | /* request on and off at the same time to ensure it can be paused */ |
| 125 | m |= tgt | tgtn; |
| 126 | mcdi_mbox_write(MCDI_MBOX_PAUSE_ACTION, m); |
| 127 | |
| 128 | /* wait pause_ack */ |
| 129 | while (!mcdi_mbox_read(MCDI_MBOX_PAUSE_ACK)) |
| 130 | ; |
| 131 | |
| 132 | /* clear non-requested operation */ |
| 133 | m &= ~tgtn; |
| 134 | mcdi_mbox_write(MCDI_MBOX_PAUSE_ACTION, m); |
| 135 | } |
| 136 | |
| 137 | void mcdi_pause(void) |
| 138 | { |
| 139 | uint32_t m = mcdi_mbox_read(MCDI_MBOX_PAUSE_ACTION) | BIT(PAUSE_BIT); |
| 140 | |
| 141 | mcdi_mbox_write(MCDI_MBOX_PAUSE_ACTION, m); |
| 142 | |
| 143 | /* wait pause_ack */ |
| 144 | while (!mcdi_mbox_read(MCDI_MBOX_PAUSE_ACK)) |
| 145 | ; |
| 146 | } |
| 147 | |
| 148 | void mcdi_unpause(void) |
| 149 | { |
| 150 | uint32_t m = mcdi_mbox_read(MCDI_MBOX_PAUSE_ACTION) & ~BIT(PAUSE_BIT); |
| 151 | |
| 152 | mcdi_mbox_write(MCDI_MBOX_PAUSE_ACTION, m); |
| 153 | } |
| 154 | |
| 155 | void mcdi_hotplug_wait_ack(int cluster, int cpu_idx, bool on) |
| 156 | { |
| 157 | uint32_t tgt = target_mask(cluster, cpu_idx, on); |
| 158 | uint32_t ack = mcdi_mbox_read(MCDI_MBOX_HP_ACK); |
| 159 | |
| 160 | /* wait until ack */ |
| 161 | while (!(ack & tgt)) |
| 162 | ack = mcdi_mbox_read(MCDI_MBOX_HP_ACK); |
| 163 | } |
| 164 | |
| 165 | void mcdi_hotplug_clr(int cluster, int cpu_idx, bool on) |
| 166 | { |
| 167 | uint32_t tgt = target_mask(cluster, cpu_idx, on); |
| 168 | uint32_t tgt_cpu = target_mask(-1, cpu_idx, on); |
| 169 | uint32_t cmd = mcdi_mbox_read(MCDI_MBOX_HP_CMD); |
| 170 | uint32_t ack = mcdi_mbox_read(MCDI_MBOX_HP_ACK); |
| 171 | |
| 172 | if (!(cmd & tgt)) |
| 173 | return; |
| 174 | |
| 175 | /* wait until ack */ |
| 176 | while (!(ack & tgt_cpu)) |
| 177 | ack = mcdi_mbox_read(MCDI_MBOX_HP_ACK); |
| 178 | |
| 179 | cmd &= ~tgt; |
| 180 | mcdi_mbox_write(MCDI_MBOX_HP_CMD, cmd); |
| 181 | } |
| 182 | |
| 183 | void mcdi_hotplug_set(int cluster, int cpu_idx, bool on) |
| 184 | { |
| 185 | uint32_t tgt = target_mask(cluster, cpu_idx, on); |
| 186 | uint32_t tgt_cpu = target_mask(-1, cpu_idx, on); |
| 187 | uint32_t cmd = mcdi_mbox_read(MCDI_MBOX_HP_CMD); |
| 188 | uint32_t ack = mcdi_mbox_read(MCDI_MBOX_HP_ACK); |
| 189 | |
| 190 | if ((cmd & tgt) == tgt) |
| 191 | return; |
| 192 | |
| 193 | /* wait until ack clear */ |
| 194 | while (ack & tgt_cpu) |
| 195 | ack = mcdi_mbox_read(MCDI_MBOX_HP_ACK); |
| 196 | |
| 197 | cmd |= tgt; |
| 198 | mcdi_mbox_write(MCDI_MBOX_HP_CMD, cmd); |
| 199 | } |
| 200 | |
| 201 | bool check_mcdi_ctl_stat(void) |
| 202 | { |
| 203 | uint32_t clk_regs[] = {0x100010ac, 0x100010c8}; |
| 204 | uint32_t clk_mask[] = {0x00028000, 0x00000018}; |
| 205 | uint32_t tgt = target_mask(0, 0, true); |
| 206 | uint32_t m; |
| 207 | int i; |
| 208 | |
| 209 | /* check clk status */ |
| 210 | for (i = 0; i < ARRAY_SIZE(clk_regs); i++) { |
| 211 | if (mmio_read_32(clk_regs[i]) & clk_mask[i]) { |
| 212 | WARN("mcdi: clk check fail.\n"); |
| 213 | return false; |
| 214 | } |
| 215 | } |
| 216 | |
| 217 | /* check mcdi cmd handling */ |
| 218 | m = mcdi_mbox_read(MCDI_MBOX_PAUSE_ACTION) | BIT(PAUSE_BIT); |
| 219 | mcdi_mbox_write(MCDI_MBOX_PAUSE_ACTION, m); |
| 220 | |
| 221 | i = 500; |
| 222 | while (!mcdi_mbox_read(MCDI_MBOX_PAUSE_ACK) && --i > 0) |
| 223 | udelay(10); |
| 224 | |
| 225 | m = mcdi_mbox_read(MCDI_MBOX_PAUSE_ACTION) & ~BIT(PAUSE_BIT); |
| 226 | mcdi_mbox_write(MCDI_MBOX_PAUSE_ACTION, m); |
| 227 | |
| 228 | if (i == 0) { |
| 229 | WARN("mcdi: pause_action fail.\n"); |
| 230 | return false; |
| 231 | } |
| 232 | |
| 233 | /* check mcdi cmd handling */ |
| 234 | if (mcdi_mbox_read(MCDI_MBOX_HP_CMD) || |
| 235 | mcdi_mbox_read(MCDI_MBOX_HP_ACK)) { |
| 236 | WARN("mcdi: hp_cmd fail.\n"); |
| 237 | return false; |
| 238 | } |
| 239 | |
| 240 | mcdi_mbox_write(MCDI_MBOX_HP_CMD, tgt); |
| 241 | |
| 242 | i = 500; |
| 243 | while ((mcdi_mbox_read(MCDI_MBOX_HP_ACK) & tgt) != tgt && --i > 0) |
| 244 | udelay(10); |
| 245 | |
| 246 | mcdi_mbox_write(MCDI_MBOX_HP_CMD, 0); |
| 247 | |
| 248 | if (i == 0) { |
| 249 | WARN("mcdi: hp_ack fail.\n"); |
| 250 | return false; |
| 251 | } |
| 252 | |
| 253 | return true; |
| 254 | } |
| 255 | |
| 256 | void mcdi_init(void) |
| 257 | { |
| 258 | mcdi_avail_cpu_mask_write(0x01); /* cpu0 default on */ |
| 259 | } |