| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- /*
- * Copyright (c) 2015 MediaTek Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #include <linux/errno.h>
- #include <asm/cacheflush.h>
- #include <mt-plat/sync_write.h>
- #include <mach/mt_spm_mtcmos.h>
- #include "mt-smp.h"
- #include "smp.h"
- #include "hotplug.h"
- atomic_t hotplug_cpu_count = ATOMIC_INIT(1);
- static inline void cpu_enter_lowpower(unsigned int cpu)
- {
- if (((cpu == 4) && (cpu_online(5) == 0) && (cpu_online(6) == 0)
- && (cpu_online(7) == 0)) || ((cpu == 5) && (cpu_online(4) == 0)
- && (cpu_online(6) == 0)
- && (cpu_online(7) == 0))
- || ((cpu == 6) && (cpu_online(4) == 0) && (cpu_online(5) == 0)
- && (cpu_online(7) == 0)) || ((cpu == 7) && (cpu_online(4) == 0)
- && (cpu_online(5) == 0)
- && (cpu_online(6) == 0))) {
- __disable_dcache__inner_flush_dcache_L1__inner_flush_dcache_L2
- ();
- /* Switch the processor from SMP mode to AMP mode by clearing
- the ACTLR SMP bit */
- __switch_to_amp();
- /* Execute an ISB instruction to ensure that all of the CP15
- register changes from the previous steps have been
- committed */
- isb();
- /* Execute a DSB instruction to ensure that all cache, TLB
- and branch predictor maintenance operations issued by any
- processor in the multiprocessor device before the SMP bit
- was cleared have completed */
- mb();
- /* Disable snoop requests and DVM message requests */
- REG_WRITE((void *)CCI400_SI3_SNOOP_CONTROL,
- readl((void *)CCI400_SI3_SNOOP_CONTROL) &
- ~(SNOOP_REQ | DVM_MSG_REQ));
- while (readl((void *)CCI400_STATUS) & CHANGE_PENDING)
- ;
- /* Disable CA15L snoop function */
- REG_WRITE((void *)MP1_AXI_CONFIG,
- readl((void *)MP1_AXI_CONFIG) | ACINACTM);
- } else {
- __disable_dcache__inner_flush_dcache_L1__inner_clean_dcache_L2
- ();
- /* Execute a CLREX instruction */
- __asm__ __volatile__("clrex");
- /* Switch the processor from SMP mode to AMP mode by
- clearing the ACTLR SMP bit */
- __switch_to_amp();
- }
- }
- static inline void cpu_leave_lowpower(unsigned int cpu)
- {
- if (((cpu == 4) && (cpu_online(5) == 0) && (cpu_online(6) == 0)
- && (cpu_online(7) == 0)) || ((cpu == 5) && (cpu_online(4) == 0)
- && (cpu_online(6) == 0)
- && (cpu_online(7) == 0))
- || ((cpu == 6) && (cpu_online(4) == 0) && (cpu_online(5) == 0)
- && (cpu_online(7) == 0)) || ((cpu == 7) && (cpu_online(4) == 0)
- && (cpu_online(5) == 0)
- && (cpu_online(6) == 0))) {
- /* Enable CA15L snoop function */
- REG_WRITE((void *)MP1_AXI_CONFIG,
- readl((void *)MP1_AXI_CONFIG) & ~ACINACTM);
- /* Enable snoop requests and DVM message requests */
- REG_WRITE((void *)CCI400_SI3_SNOOP_CONTROL,
- readl((void *)CCI400_SI3_SNOOP_CONTROL) |
- (SNOOP_REQ | DVM_MSG_REQ));
- while (readl((void *)CCI400_STATUS) & CHANGE_PENDING)
- ;
- }
- /* Set the ACTLR.SMP bit to 1 for SMP mode */
- __switch_to_smp();
- /* Enable dcache */
- __enable_dcache();
- }
- static inline void platform_do_lowpower(unsigned int cpu, int *spurious)
- {
- /* Just enter wfi for now. TODO: Properly shut off the cpu. */
- for (;;) {
- /* Execute an ISB instruction to ensure that all of the CP15
- register changes from the previous steps have been
- committed */
- isb();
- /* Execute a DSB instruction to ensure that all cache, TLB and
- branch predictor maintenance operations issued by any
- processor in the multiprocessor device before the SMP bit
- was cleared have completed */
- mb();
- /*
- * here's the WFI
- */
- __asm__ __volatile__("wfi");
- if (pen_release == cpu) {
- /*
- * OK, proper wakeup, we're done
- */
- break;
- }
- /*
- * Getting here, means that we have come out of WFI without
- * having been woken up - this shouldn't happen
- *
- * Just note it happening - when we're woken, we can report
- * its occurrence.
- */
- (*spurious)++;
- }
- }
- /*
- * mt_cpu_kill:
- * @cpu:
- * Return TBD.
- */
- int mt_cpu_kill(unsigned int cpu)
- {
- HOTPLUG_INFO("mt_cpu_kill, cpu: %d\n", cpu);
- #ifdef CONFIG_HOTPLUG_WITH_POWER_CTRL
- switch (cpu) {
- case 1:
- spm_mtcmos_ctrl_cpu1(STA_POWER_DOWN, 1);
- break;
- case 2:
- spm_mtcmos_ctrl_cpu2(STA_POWER_DOWN, 1);
- break;
- case 3:
- spm_mtcmos_ctrl_cpu3(STA_POWER_DOWN, 1);
- break;
- case 4:
- spm_mtcmos_ctrl_cpu4(STA_POWER_DOWN, 1);
- break;
- case 5:
- spm_mtcmos_ctrl_cpu5(STA_POWER_DOWN, 1);
- break;
- case 6:
- spm_mtcmos_ctrl_cpu6(STA_POWER_DOWN, 1);
- break;
- case 7:
- spm_mtcmos_ctrl_cpu7(STA_POWER_DOWN, 1);
- break;
- default:
- break;
- }
- #endif
- atomic_dec(&hotplug_cpu_count);
- return 1;
- }
- /*
- * mt_cpu_die: shutdown a CPU
- * @cpu:
- */
- void mt_cpu_die(unsigned int cpu)
- {
- int spurious = 0;
- HOTPLUG_INFO("mt_cpu_die, cpu: %d\n", cpu);
- /*
- * we're ready for shutdown now, so do it
- */
- cpu_enter_lowpower(cpu);
- platform_do_lowpower(cpu, &spurious);
- /*
- * bring this CPU back into the world of cache
- * coherency, and then restore interrupts
- */
- cpu_leave_lowpower(cpu);
- if (spurious)
- HOTPLUG_INFO(
- "spurious wakeup call, cpu: %d, spurious: %d\n",
- cpu, spurious);
- }
- /*
- * mt_cpu_disable:
- * @cpu:
- * Return error code.
- */
- int mt_cpu_disable(unsigned int cpu)
- {
- /*
- * we don't allow CPU 0 to be shutdown (it is still too special
- * e.g. clock tick interrupts)
- */
- HOTPLUG_INFO("mt_cpu_disable, cpu: %d\n", cpu);
- return cpu == 0 ? -EPERM : 0;
- }
|