/* * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include static inline uint32_t mcdi_mbox_read(uint32_t id) { return mmio_read_32(SSPM_MBOX_3_BASE + (id << 2)); } static inline void mcdi_mbox_write(uint32_t id, uint32_t val) { mmio_write_32(SSPM_MBOX_3_BASE + (id << 2), val); } void sspm_set_bootaddr(uint32_t bootaddr) { mcdi_mbox_write(MCDI_MBOX_BOOTADDR, bootaddr); } void sspm_cluster_pwr_off_notify(uint32_t cluster) { mcdi_mbox_write(MCDI_MBOX_CLUSTER_0_ATF_ACTION_DONE + cluster, 1); } void sspm_cluster_pwr_on_notify(uint32_t cluster) { mcdi_mbox_write(MCDI_MBOX_CLUSTER_0_ATF_ACTION_DONE + cluster, 0); } void sspm_standbywfi_irq_enable(uint32_t cpu_idx) { mmio_write_32(SSPM_CFGREG_ACAO_INT_SET, STANDBYWFI_EN(cpu_idx)); } uint32_t mcdi_avail_cpu_mask_read(void) { return mcdi_mbox_read(MCDI_MBOX_AVAIL_CPU_MASK); } uint32_t mcdi_avail_cpu_mask_write(uint32_t mask) { mcdi_mbox_write(MCDI_MBOX_AVAIL_CPU_MASK, mask); return mask; } uint32_t mcdi_avail_cpu_mask_set(uint32_t mask) { uint32_t m; m = mcdi_mbox_read(MCDI_MBOX_AVAIL_CPU_MASK); m |= mask; mcdi_mbox_write(MCDI_MBOX_AVAIL_CPU_MASK, m); return m; } uint32_t mcdi_avail_cpu_mask_clr(uint32_t mask) { uint32_t m; m = mcdi_mbox_read(MCDI_MBOX_AVAIL_CPU_MASK); m &= ~mask; mcdi_mbox_write(MCDI_MBOX_AVAIL_CPU_MASK, m); return m; } uint32_t mcdi_cpu_cluster_pwr_stat_read(void) { return mcdi_mbox_read(MCDI_MBOX_CPU_CLUSTER_PWR_STAT); } #define PAUSE_BIT 1 #define CLUSTER_OFF_OFS 20 #define CPU_OFF_OFS 24 #define CLUSTER_ON_OFS 4 #define CPU_ON_OFS 8 static uint32_t target_mask(int cluster, int cpu_idx, bool on) { uint32_t t = 0; if (on) { if (cluster >= 0) t |= BIT(cluster + CLUSTER_ON_OFS); if (cpu_idx >= 0) t |= BIT(cpu_idx + CPU_ON_OFS); } else { if (cluster >= 0) t |= BIT(cluster + CLUSTER_OFF_OFS); if (cpu_idx >= 0) t |= BIT(cpu_idx + CPU_OFF_OFS); } return t; } void mcdi_pause_clr(int cluster, int cpu_idx, bool on) { uint32_t tgt = target_mask(cluster, cpu_idx, on); uint32_t m = mcdi_mbox_read(MCDI_MBOX_PAUSE_ACTION); m &= ~tgt; mcdi_mbox_write(MCDI_MBOX_PAUSE_ACTION, m); } void mcdi_pause_set(int cluster, int cpu_idx, bool on) { uint32_t tgt = target_mask(cluster, cpu_idx, on); uint32_t m = mcdi_mbox_read(MCDI_MBOX_PAUSE_ACTION); uint32_t tgtn = target_mask(-1, cpu_idx, !on); /* request on and off at the same time to ensure it can be paused */ m |= tgt | tgtn; mcdi_mbox_write(MCDI_MBOX_PAUSE_ACTION, m); /* wait pause_ack */ while (!mcdi_mbox_read(MCDI_MBOX_PAUSE_ACK)) ; /* clear non-requested operation */ m &= ~tgtn; mcdi_mbox_write(MCDI_MBOX_PAUSE_ACTION, m); } void mcdi_pause(void) { uint32_t m = mcdi_mbox_read(MCDI_MBOX_PAUSE_ACTION) | BIT(PAUSE_BIT); mcdi_mbox_write(MCDI_MBOX_PAUSE_ACTION, m); /* wait pause_ack */ while (!mcdi_mbox_read(MCDI_MBOX_PAUSE_ACK)) ; } void mcdi_unpause(void) { uint32_t m = mcdi_mbox_read(MCDI_MBOX_PAUSE_ACTION) & ~BIT(PAUSE_BIT); mcdi_mbox_write(MCDI_MBOX_PAUSE_ACTION, m); } void mcdi_hotplug_wait_ack(int cluster, int cpu_idx, bool on) { uint32_t tgt = target_mask(cluster, cpu_idx, on); uint32_t ack = mcdi_mbox_read(MCDI_MBOX_HP_ACK); /* wait until ack */ while (!(ack & tgt)) ack = mcdi_mbox_read(MCDI_MBOX_HP_ACK); } void mcdi_hotplug_clr(int cluster, int cpu_idx, bool on) { uint32_t tgt = target_mask(cluster, cpu_idx, on); uint32_t tgt_cpu = target_mask(-1, cpu_idx, on); uint32_t cmd = mcdi_mbox_read(MCDI_MBOX_HP_CMD); uint32_t ack = mcdi_mbox_read(MCDI_MBOX_HP_ACK); if (!(cmd & tgt)) return; /* wait until ack */ while (!(ack & tgt_cpu)) ack = mcdi_mbox_read(MCDI_MBOX_HP_ACK); cmd &= ~tgt; mcdi_mbox_write(MCDI_MBOX_HP_CMD, cmd); } void mcdi_hotplug_set(int cluster, int cpu_idx, bool on) { uint32_t tgt = target_mask(cluster, cpu_idx, on); uint32_t tgt_cpu = target_mask(-1, cpu_idx, on); uint32_t cmd = mcdi_mbox_read(MCDI_MBOX_HP_CMD); uint32_t ack = mcdi_mbox_read(MCDI_MBOX_HP_ACK); if ((cmd & tgt) == tgt) return; /* wait until ack clear */ while (ack & tgt_cpu) ack = mcdi_mbox_read(MCDI_MBOX_HP_ACK); cmd |= tgt; mcdi_mbox_write(MCDI_MBOX_HP_CMD, cmd); } bool check_mcdi_ctl_stat(void) { uint32_t clk_regs[] = {0x100010ac, 0x100010c8}; uint32_t clk_mask[] = {0x00028000, 0x00000018}; uint32_t tgt = target_mask(0, 0, true); uint32_t m; int i; /* check clk status */ for (i = 0; i < ARRAY_SIZE(clk_regs); i++) { if (mmio_read_32(clk_regs[i]) & clk_mask[i]) { WARN("mcdi: clk check fail.\n"); return false; } } /* check mcdi cmd handling */ m = mcdi_mbox_read(MCDI_MBOX_PAUSE_ACTION) | BIT(PAUSE_BIT); mcdi_mbox_write(MCDI_MBOX_PAUSE_ACTION, m); i = 500; while (!mcdi_mbox_read(MCDI_MBOX_PAUSE_ACK) && --i > 0) udelay(10); m = mcdi_mbox_read(MCDI_MBOX_PAUSE_ACTION) & ~BIT(PAUSE_BIT); mcdi_mbox_write(MCDI_MBOX_PAUSE_ACTION, m); if (i == 0) { WARN("mcdi: pause_action fail.\n"); return false; } /* check mcdi cmd handling */ if (mcdi_mbox_read(MCDI_MBOX_HP_CMD) || mcdi_mbox_read(MCDI_MBOX_HP_ACK)) { WARN("mcdi: hp_cmd fail.\n"); return false; } mcdi_mbox_write(MCDI_MBOX_HP_CMD, tgt); i = 500; while ((mcdi_mbox_read(MCDI_MBOX_HP_ACK) & tgt) != tgt && --i > 0) udelay(10); mcdi_mbox_write(MCDI_MBOX_HP_CMD, 0); if (i == 0) { WARN("mcdi: hp_ack fail.\n"); return false; } return true; } void mcdi_init(void) { mcdi_avail_cpu_mask_write(0x01); /* cpu0 default on */ }