1/* 2 * pmu.c, Power Management Unit routines for NEC VR4100 series. 3 * 4 * Copyright (C) 2003-2007 Yoichi Yuasa <yuasa@linux-mips.org> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 */ 20#include <linux/errno.h> 21#include <linux/init.h> 22#include <linux/ioport.h> 23#include <linux/kernel.h> 24#include <linux/pm.h> 25#include <linux/sched.h> 26#include <linux/types.h> 27 28#include <asm/cacheflush.h> 29#include <asm/cpu.h> 30#include <asm/io.h> 31#include <asm/processor.h> 32#include <asm/reboot.h> 33#include <asm/system.h> 34 35#define PMU_TYPE1_BASE 0x0b0000a0UL 36#define PMU_TYPE1_SIZE 0x0eUL 37 38#define PMU_TYPE2_BASE 0x0f0000c0UL 39#define PMU_TYPE2_SIZE 0x10UL 40 41#define PMUCNT2REG 0x06 42 #define SOFTRST 0x0010 43 44static void __iomem *pmu_base; 45 46#define pmu_read(offset) readw(pmu_base + (offset)) 47#define pmu_write(offset, value) writew((value), pmu_base + (offset)) 48 49static void vr41xx_cpu_wait(void) 50{ 51 local_irq_disable(); 52 if (!need_resched()) 53 /* 54 * "standby" sets IE bit of the CP0_STATUS to 1. 55 */ 56 __asm__("standby;\n"); 57 else 58 local_irq_enable(); 59} 60 61static inline void software_reset(void) 62{ 63 uint16_t pmucnt2; 64 65 switch (current_cpu_type()) { 66 case CPU_VR4122: 67 case CPU_VR4131: 68 case CPU_VR4133: 69 pmucnt2 = pmu_read(PMUCNT2REG); 70 pmucnt2 |= SOFTRST; 71 pmu_write(PMUCNT2REG, pmucnt2); 72 break; 73 default: 74 set_c0_status(ST0_BEV | ST0_ERL); 75 change_c0_config(CONF_CM_CMASK, CONF_CM_UNCACHED); 76 flush_cache_all(); 77 write_c0_wired(0); 78 __asm__("jr %0"::"r"(0xbfc00000)); 79 break; 80 } 81} 82 83static void vr41xx_restart(char *command) 84{ 85 local_irq_disable(); 86 software_reset(); 87 while (1) ; 88} 89 90static void vr41xx_halt(void) 91{ 92 local_irq_disable(); 93 printk(KERN_NOTICE "\nYou can turn off the power supply\n"); 94 __asm__("hibernate;\n"); 95} 96 97static int __init vr41xx_pmu_init(void) 98{ 99 unsigned long start, size; 100 101 switch (current_cpu_type()) { 102 case CPU_VR4111: 103 case CPU_VR4121: 104 start = PMU_TYPE1_BASE; 105 size = PMU_TYPE1_SIZE; 106 break; 107 case CPU_VR4122: 108 case CPU_VR4131: 109 case CPU_VR4133: 110 start = PMU_TYPE2_BASE; 111 size = PMU_TYPE2_SIZE; 112 break; 113 default: 114 printk("Unexpected CPU of NEC VR4100 series\n"); 115 return -ENODEV; 116 } 117 118 if (request_mem_region(start, size, "PMU") == NULL) 119 return -EBUSY; 120 121 pmu_base = ioremap(start, size); 122 if (pmu_base == NULL) { 123 release_mem_region(start, size); 124 return -EBUSY; 125 } 126 127 cpu_wait = vr41xx_cpu_wait; 128 _machine_restart = vr41xx_restart; 129 _machine_halt = vr41xx_halt; 130 pm_power_off = vr41xx_halt; 131 132 return 0; 133} 134 135core_initcall(vr41xx_pmu_init); 136