Jacky Bai | 4d93d1d | 2020-07-02 14:39:58 +0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2021-2024 NXP |
| 3 | * |
| 4 | * SPDX-License-Identifier: BSD-3-Clause |
| 5 | */ |
| 6 | #include <inttypes.h> |
| 7 | #include <lib/libc/errno.h> |
| 8 | #include <stdint.h> |
| 9 | #include <stdlib.h> |
| 10 | #include <string.h> |
| 11 | |
| 12 | #include <common/debug.h> |
| 13 | #include <drivers/scmi.h> |
| 14 | #include <lib/mmio.h> |
| 15 | #include <lib/utils_def.h> |
| 16 | #include <platform_def.h> |
| 17 | #include <scmi.h> |
| 18 | |
| 19 | #include <upower_api.h> |
| 20 | |
| 21 | #define POWER_STATE_ON (0 << 30) |
| 22 | #define POWER_STATE_OFF (1 << 30) |
| 23 | |
Ye Li | 234925b | 2021-12-22 14:46:01 +0800 | [diff] [blame] | 24 | extern bool is_lpav_owned_by_apd(void); |
| 25 | |
Jacky Bai | 4d93d1d | 2020-07-02 14:39:58 +0800 | [diff] [blame] | 26 | enum { |
| 27 | PS0 = 0, |
| 28 | PS1 = 1, |
| 29 | PS2 = 2, |
| 30 | PS3 = 3, |
| 31 | PS4 = 4, |
| 32 | PS5 = 5, |
| 33 | PS6 = 6, |
| 34 | PS7 = 7, |
| 35 | PS8 = 8, |
| 36 | PS9 = 9, |
| 37 | PS10 = 10, |
| 38 | PS11 = 11, |
| 39 | PS12 = 12, |
| 40 | PS13 = 13, |
| 41 | PS14 = 14, |
| 42 | PS15 = 15, |
| 43 | PS16 = 16, |
| 44 | PS17 = 17, |
| 45 | PS18 = 18, |
| 46 | PS19 = 19, |
| 47 | }; |
| 48 | |
| 49 | #define SRAM_DMA1 BIT(6) |
| 50 | #define SRAM_FLEXSPI2 BIT(7) |
| 51 | #define SRAM_USB0 BIT(10) |
| 52 | #define SRAM_USDHC0 BIT(11) |
| 53 | #define SRAM_USDHC1 BIT(12) |
| 54 | #define SRAM_USDHC2_USB1 BIT(13) |
| 55 | #define SRAM_DCNANO GENMASK_32(18, 17) |
| 56 | #define SRAM_EPDC GENMASK_32(20, 19) |
| 57 | #define SRAM_DMA2 BIT(21) |
| 58 | #define SRAM_GPU2D GENMASK_32(23, 22) |
| 59 | #define SRAM_GPU3D GENMASK_32(25, 24) |
| 60 | #define SRAM_HIFI4 BIT(26) |
| 61 | #define SRAM_ISI_BUFFER BIT(27) |
| 62 | #define SRAM_MIPI_CSI_FIFO BIT(28) |
| 63 | #define SRAM_MIPI_DSI_FIFO BIT(29) |
| 64 | #define SRAM_PXP BIT(30) |
| 65 | |
| 66 | #define SRAM_DMA0 BIT_64(33) |
| 67 | #define SRAM_FLEXCAN BIT_64(34) |
| 68 | #define SRAM_FLEXSPI0 BIT_64(35) |
| 69 | #define SRAM_FLEXSPI1 BIT_64(36) |
| 70 | |
| 71 | struct psw { |
| 72 | char *name; |
| 73 | uint32_t reg; |
| 74 | int power_state; |
| 75 | uint32_t count; |
| 76 | int flags; |
| 77 | }; |
| 78 | |
| 79 | #define ALWAYS_ON BIT(0) |
| 80 | |
| 81 | static struct psw imx8ulp_psw[] = { |
| 82 | [PS6] = { .name = "PS6", .reg = PS6, .flags = ALWAYS_ON, .power_state = POWER_STATE_ON }, |
| 83 | [PS7] = { .name = "PS7", .reg = PS7, .power_state = POWER_STATE_OFF }, |
| 84 | [PS8] = { .name = "PS8", .reg = PS8, .power_state = POWER_STATE_OFF }, |
| 85 | [PS13] = { .name = "PS13", .reg = PS13, .power_state = POWER_STATE_OFF }, |
| 86 | [PS14] = { .name = "PS14", .reg = PS14, .flags = ALWAYS_ON, .power_state = POWER_STATE_OFF }, |
| 87 | [PS15] = { .name = "PS15", .reg = PS15, .power_state = POWER_STATE_OFF }, |
| 88 | [PS16] = { .name = "PS16", .reg = PS16, .flags = ALWAYS_ON, .power_state = POWER_STATE_ON }, |
| 89 | }; |
| 90 | |
| 91 | struct power_domain { |
| 92 | char *name; |
| 93 | uint32_t reg; |
| 94 | uint32_t psw_parent; |
| 95 | uint32_t sram_parent; |
| 96 | uint64_t bits; |
| 97 | uint32_t power_state; |
Ye Li | 234925b | 2021-12-22 14:46:01 +0800 | [diff] [blame] | 98 | bool lpav; /* belong to lpav domain */ |
Jacky Bai | 4d93d1d | 2020-07-02 14:39:58 +0800 | [diff] [blame] | 99 | uint32_t sw_rst_reg; /* pcc sw reset reg offset */ |
| 100 | }; |
| 101 | |
| 102 | /* The Rich OS need flow the macro */ |
| 103 | #define IMX8ULP_PD_DMA1 0 |
| 104 | #define IMX8ULP_PD_FLEXSPI2 1 |
| 105 | #define IMX8ULP_PD_USB0 2 |
| 106 | #define IMX8ULP_PD_USDHC0 3 |
| 107 | #define IMX8ULP_PD_USDHC1 4 |
| 108 | #define IMX8ULP_PD_USDHC2_USB1 5 |
| 109 | #define IMX8ULP_PD_DCNANO 6 |
| 110 | #define IMX8ULP_PD_EPDC 7 |
| 111 | #define IMX8ULP_PD_DMA2 8 |
| 112 | #define IMX8ULP_PD_GPU2D 9 |
| 113 | #define IMX8ULP_PD_GPU3D 10 |
| 114 | #define IMX8ULP_PD_HIFI4 11 |
| 115 | #define IMX8ULP_PD_ISI 12 |
| 116 | #define IMX8ULP_PD_MIPI_CSI 13 |
| 117 | #define IMX8ULP_PD_MIPI_DSI 14 |
| 118 | #define IMX8ULP_PD_PXP 15 |
| 119 | |
| 120 | #define IMX8ULP_PD_PS6 16 |
| 121 | #define IMX8ULP_PD_PS7 17 |
| 122 | #define IMX8ULP_PD_PS8 18 |
| 123 | #define IMX8ULP_PD_PS13 19 |
| 124 | #define IMX8ULP_PD_PS14 20 |
| 125 | #define IMX8ULP_PD_PS15 21 |
| 126 | #define IMX8ULP_PD_PS16 22 |
| 127 | #define IMX8ULP_PD_MAX 23 |
| 128 | |
| 129 | /* LPAV peripheral PCC */ |
| 130 | #define PCC_GPU2D (IMX_PCC5_BASE + 0xf0) |
| 131 | #define PCC_GPU3D (IMX_PCC5_BASE + 0xf4) |
| 132 | #define PCC_EPDC (IMX_PCC5_BASE + 0xcc) |
| 133 | #define PCC_CSI (IMX_PCC5_BASE + 0xbc) |
| 134 | #define PCC_PXP (IMX_PCC5_BASE + 0xd0) |
| 135 | |
| 136 | #define PCC_SW_RST BIT(28) |
| 137 | |
| 138 | #define PWR_DOMAIN(_name, _reg, _psw_parent, _sram_parent, \ |
Ye Li | 234925b | 2021-12-22 14:46:01 +0800 | [diff] [blame] | 139 | _bits, _state, _lpav, _rst_reg) \ |
Jacky Bai | 4d93d1d | 2020-07-02 14:39:58 +0800 | [diff] [blame] | 140 | { \ |
| 141 | .name = _name, \ |
| 142 | .reg = _reg, \ |
| 143 | .psw_parent = _psw_parent, \ |
| 144 | .sram_parent = _sram_parent, \ |
| 145 | .bits = _bits, \ |
| 146 | .power_state = _state, \ |
Ye Li | 234925b | 2021-12-22 14:46:01 +0800 | [diff] [blame] | 147 | .lpav = _lpav, \ |
Jacky Bai | 4d93d1d | 2020-07-02 14:39:58 +0800 | [diff] [blame] | 148 | .sw_rst_reg = _rst_reg, \ |
| 149 | } |
| 150 | |
| 151 | static struct power_domain scmi_power_domains[] = { |
Ye Li | 234925b | 2021-12-22 14:46:01 +0800 | [diff] [blame] | 152 | PWR_DOMAIN("DMA1", IMX8ULP_PD_DMA1, PS6, PS6, SRAM_DMA1, POWER_STATE_OFF, false, 0U), |
| 153 | PWR_DOMAIN("FLEXSPI2", IMX8ULP_PD_FLEXSPI2, PS6, PS6, SRAM_FLEXSPI2, POWER_STATE_OFF, false, 0U), |
| 154 | PWR_DOMAIN("USB0", IMX8ULP_PD_USB0, PS6, PS6, SRAM_USB0, POWER_STATE_OFF, false, 0U), |
| 155 | PWR_DOMAIN("USDHC0", IMX8ULP_PD_USDHC0, PS6, PS6, SRAM_USDHC0, POWER_STATE_OFF, false, 0U), |
| 156 | PWR_DOMAIN("USDHC1", IMX8ULP_PD_USDHC1, PS6, PS6, SRAM_USDHC1, POWER_STATE_OFF, false, 0U), |
| 157 | PWR_DOMAIN("USDHC2_USB1", IMX8ULP_PD_USDHC2_USB1, PS6, PS6, SRAM_USDHC2_USB1, POWER_STATE_OFF, false, 0U), |
| 158 | PWR_DOMAIN("DCNano", IMX8ULP_PD_DCNANO, PS16, PS16, SRAM_DCNANO, POWER_STATE_OFF, true, 0U), |
| 159 | PWR_DOMAIN("EPDC", IMX8ULP_PD_EPDC, PS13, PS13, SRAM_EPDC, POWER_STATE_OFF, true, PCC_EPDC), |
| 160 | PWR_DOMAIN("DMA2", IMX8ULP_PD_DMA2, PS16, PS16, SRAM_DMA2, POWER_STATE_OFF, true, 0U), |
| 161 | PWR_DOMAIN("GPU2D", IMX8ULP_PD_GPU2D, PS16, PS16, SRAM_GPU2D, POWER_STATE_OFF, true, PCC_GPU2D), |
| 162 | PWR_DOMAIN("GPU3D", IMX8ULP_PD_GPU3D, PS7, PS7, SRAM_GPU3D, POWER_STATE_OFF, true, PCC_GPU3D), |
| 163 | PWR_DOMAIN("HIFI4", IMX8ULP_PD_HIFI4, PS8, PS8, SRAM_HIFI4, POWER_STATE_OFF, true, 0U), |
| 164 | PWR_DOMAIN("ISI", IMX8ULP_PD_ISI, PS16, PS16, SRAM_ISI_BUFFER, POWER_STATE_OFF, true, 0U), |
| 165 | PWR_DOMAIN("MIPI_CSI", IMX8ULP_PD_MIPI_CSI, PS15, PS16, SRAM_MIPI_CSI_FIFO, POWER_STATE_OFF, true, PCC_CSI), |
| 166 | PWR_DOMAIN("MIPI_DSI", IMX8ULP_PD_MIPI_DSI, PS14, PS16, SRAM_MIPI_DSI_FIFO, POWER_STATE_OFF, true, 0U), |
| 167 | PWR_DOMAIN("PXP", IMX8ULP_PD_PXP, PS13, PS13, SRAM_PXP | SRAM_EPDC, POWER_STATE_OFF, true, PCC_PXP) |
Jacky Bai | 4d93d1d | 2020-07-02 14:39:58 +0800 | [diff] [blame] | 168 | }; |
| 169 | |
| 170 | size_t plat_scmi_pd_count(unsigned int agent_id __unused) |
| 171 | { |
| 172 | return ARRAY_SIZE(scmi_power_domains); |
| 173 | } |
| 174 | |
| 175 | const char *plat_scmi_pd_get_name(unsigned int agent_id __unused, |
| 176 | unsigned int pd_id) |
| 177 | { |
| 178 | if (pd_id >= IMX8ULP_PD_PS6) { |
| 179 | return imx8ulp_psw[pd_id - IMX8ULP_PD_PS6].name; |
| 180 | } |
| 181 | |
| 182 | return scmi_power_domains[pd_id].name; |
| 183 | } |
| 184 | |
| 185 | unsigned int plat_scmi_pd_get_state(unsigned int agent_id __unused, |
| 186 | unsigned int pd_id __unused) |
| 187 | { |
| 188 | if (pd_id >= IMX8ULP_PD_PS6) { |
| 189 | return imx8ulp_psw[pd_id - IMX8ULP_PD_PS6].power_state; |
| 190 | } |
| 191 | |
| 192 | return scmi_power_domains[pd_id].power_state; |
| 193 | } |
| 194 | |
| 195 | extern void upower_wait_resp(void); |
| 196 | int upwr_pwm_power(const uint32_t swton[], const uint32_t memon[], bool on) |
| 197 | { |
| 198 | int ret_val; |
| 199 | int ret; |
| 200 | |
| 201 | if (on == true) { |
| 202 | ret = upwr_pwm_power_on(swton, memon, NULL); |
| 203 | } else { |
| 204 | ret = upwr_pwm_power_off(swton, memon, NULL); |
| 205 | } |
| 206 | |
| 207 | if (ret != 0U) { |
| 208 | WARN("%s failed: ret: %d, state: %x\n", __func__, ret, on); |
| 209 | return ret; |
| 210 | } |
| 211 | |
| 212 | upower_wait_resp(); |
| 213 | |
| 214 | ret = upwr_poll_req_status(UPWR_SG_PWRMGMT, NULL, NULL, &ret_val, 1000); |
| 215 | if (ret != UPWR_REQ_OK) { |
| 216 | WARN("Failure %d, %s\n", ret, __func__); |
| 217 | if (ret == UPWR_REQ_BUSY) { |
| 218 | return -EBUSY; |
| 219 | } else { |
| 220 | return -EINVAL; |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | return 0; |
| 225 | } |
| 226 | |
| 227 | int32_t plat_scmi_pd_psw(unsigned int index, unsigned int state) |
| 228 | { |
| 229 | uint32_t psw_parent = scmi_power_domains[index].psw_parent; |
| 230 | uint32_t sram_parent = scmi_power_domains[index].sram_parent; |
| 231 | uint64_t swt; |
| 232 | bool on; |
| 233 | int ret = 0; |
| 234 | |
| 235 | if ((imx8ulp_psw[psw_parent].flags & ALWAYS_ON) != 0U && |
| 236 | (imx8ulp_psw[sram_parent].flags & ALWAYS_ON) != 0U) { |
| 237 | return 0; |
| 238 | } |
| 239 | |
| 240 | on = (state == POWER_STATE_ON) ? true : false; |
| 241 | |
| 242 | if ((imx8ulp_psw[psw_parent].flags & ALWAYS_ON) == 0U) { |
| 243 | swt = 1 << imx8ulp_psw[psw_parent].reg; |
| 244 | if (imx8ulp_psw[psw_parent].count == 0U) { |
| 245 | if (on == false) { |
| 246 | WARN("off PSW[%d] that already in off state\n", psw_parent); |
| 247 | ret = -EACCES; |
| 248 | } else { |
| 249 | ret = upwr_pwm_power((const uint32_t *)&swt, NULL, on); |
| 250 | imx8ulp_psw[psw_parent].count++; |
| 251 | } |
| 252 | } else { |
| 253 | if (on == true) { |
| 254 | imx8ulp_psw[psw_parent].count++; |
| 255 | } else { |
| 256 | imx8ulp_psw[psw_parent].count--; |
| 257 | } |
| 258 | |
| 259 | if (imx8ulp_psw[psw_parent].count == 0U) { |
| 260 | ret = upwr_pwm_power((const uint32_t *)&swt, NULL, on); |
| 261 | } |
| 262 | } |
| 263 | } |
| 264 | |
| 265 | if (!(imx8ulp_psw[sram_parent].flags & ALWAYS_ON) && (psw_parent != sram_parent)) { |
| 266 | swt = 1 << imx8ulp_psw[sram_parent].reg; |
| 267 | if (imx8ulp_psw[sram_parent].count == 0U) { |
| 268 | if (on == false) { |
| 269 | WARN("off PSW[%d] that already in off state\n", sram_parent); |
| 270 | ret = -EACCES; |
| 271 | } else { |
| 272 | ret = upwr_pwm_power((const uint32_t *)&swt, NULL, on); |
| 273 | imx8ulp_psw[sram_parent].count++; |
| 274 | } |
| 275 | } else { |
| 276 | if (on == true) { |
| 277 | imx8ulp_psw[sram_parent].count++; |
| 278 | } else { |
| 279 | imx8ulp_psw[sram_parent].count--; |
| 280 | } |
| 281 | |
| 282 | if (imx8ulp_psw[sram_parent].count == 0U) { |
| 283 | ret = upwr_pwm_power((const uint32_t *)&swt, NULL, on); |
| 284 | } |
| 285 | } |
| 286 | } |
| 287 | |
| 288 | return ret; |
| 289 | } |
| 290 | |
Ye Li | 234925b | 2021-12-22 14:46:01 +0800 | [diff] [blame] | 291 | bool pd_allow_power_off(unsigned int pd_id) |
| 292 | { |
| 293 | if (scmi_power_domains[pd_id].lpav) { |
| 294 | if (!is_lpav_owned_by_apd()) { |
| 295 | return false; |
| 296 | } |
| 297 | } |
| 298 | |
| 299 | return true; |
| 300 | } |
| 301 | |
Jacky Bai | 4d93d1d | 2020-07-02 14:39:58 +0800 | [diff] [blame] | 302 | void assert_pcc_reset(unsigned int pcc) |
| 303 | { |
| 304 | /* if sw_rst_reg is valid, assert the pcc reset */ |
| 305 | if (pcc != 0U) { |
| 306 | mmio_clrbits_32(pcc, PCC_SW_RST); |
| 307 | } |
| 308 | } |
| 309 | |
| 310 | int32_t plat_scmi_pd_set_state(unsigned int agent_id __unused, |
| 311 | unsigned int flags, |
| 312 | unsigned int pd_id, |
| 313 | unsigned int state) |
| 314 | { |
| 315 | unsigned int ps_idx; |
| 316 | uint64_t mem; |
| 317 | bool on; |
| 318 | int ret; |
| 319 | |
| 320 | if (flags != 0U || pd_id >= IMX8ULP_PD_PS6) { |
| 321 | return SCMI_NOT_SUPPORTED; |
| 322 | } |
| 323 | |
| 324 | ps_idx = 0; |
| 325 | while (ps_idx < IMX8ULP_PD_PS6 && scmi_power_domains[ps_idx].reg != pd_id) { |
| 326 | ps_idx++; |
| 327 | } |
| 328 | |
| 329 | if (ps_idx == IMX8ULP_PD_PS6) { |
| 330 | return SCMI_NOT_FOUND; |
| 331 | } |
| 332 | |
| 333 | if (state == scmi_power_domains[ps_idx].power_state) { |
| 334 | return SCMI_SUCCESS; |
| 335 | } |
| 336 | |
| 337 | mem = scmi_power_domains[ps_idx].bits; |
| 338 | on = (state == POWER_STATE_ON ? true : false); |
| 339 | if (on == true) { |
| 340 | /* Assert pcc sw reset if necessary */ |
| 341 | assert_pcc_reset(scmi_power_domains[ps_idx].sw_rst_reg); |
| 342 | |
| 343 | ret = plat_scmi_pd_psw(ps_idx, state); |
| 344 | if (ret != 0U) { |
| 345 | return SCMI_DENIED; |
| 346 | } |
| 347 | |
| 348 | ret = upwr_pwm_power(NULL, (const uint32_t *)&mem, on); |
| 349 | if (ret != 0U) { |
| 350 | return SCMI_DENIED; |
| 351 | } |
| 352 | } else { |
Ye Li | 234925b | 2021-12-22 14:46:01 +0800 | [diff] [blame] | 353 | if (!pd_allow_power_off(ps_idx)) { |
| 354 | return SCMI_DENIED; |
| 355 | } |
| 356 | |
Jacky Bai | 4d93d1d | 2020-07-02 14:39:58 +0800 | [diff] [blame] | 357 | ret = upwr_pwm_power(NULL, (const uint32_t *)&mem, on); |
| 358 | if (ret != 0U) { |
| 359 | return SCMI_DENIED; |
| 360 | } |
| 361 | |
| 362 | ret = plat_scmi_pd_psw(ps_idx, state); |
| 363 | if (ret != 0U) { |
| 364 | return SCMI_DENIED; |
| 365 | } |
| 366 | } |
| 367 | |
| 368 | scmi_power_domains[pd_id].power_state = state; |
| 369 | |
| 370 | return SCMI_SUCCESS; |
| 371 | } |