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/cpu.h>
21 #include <linux/errno.h>
22 #include <linux/init.h>
23 #include <linux/ioport.h>
24 #include <linux/kernel.h>
25 #include <linux/pm.h>
26 #include <linux/sched.h>
27 #include <linux/types.h>
28 
29 #include <asm/cacheflush.h>
30 #include <asm/cpu.h>
31 #include <asm/idle.h>
32 #include <asm/io.h>
33 #include <asm/processor.h>
34 #include <asm/reboot.h>
35 
36 #define PMU_TYPE1_BASE	0x0b0000a0UL
37 #define PMU_TYPE1_SIZE	0x0eUL
38 
39 #define PMU_TYPE2_BASE	0x0f0000c0UL
40 #define PMU_TYPE2_SIZE	0x10UL
41 
42 #define PMUCNT2REG	0x06
43  #define SOFTRST	0x0010
44 
45 static void __iomem *pmu_base;
46 
47 #define pmu_read(offset)		readw(pmu_base + (offset))
48 #define pmu_write(offset, value)	writew((value), pmu_base + (offset))
49 
vr41xx_cpu_wait(void)50 static void __cpuidle vr41xx_cpu_wait(void)
51 {
52 	local_irq_disable();
53 	if (!need_resched())
54 		/*
55 		 * "standby" sets IE bit of the CP0_STATUS to 1.
56 		 */
57 		__asm__("standby;\n");
58 	else
59 		local_irq_enable();
60 }
61 
software_reset(void)62 static inline void software_reset(void)
63 {
64 	uint16_t pmucnt2;
65 
66 	switch (current_cpu_type()) {
67 	case CPU_VR4122:
68 	case CPU_VR4131:
69 	case CPU_VR4133:
70 		pmucnt2 = pmu_read(PMUCNT2REG);
71 		pmucnt2 |= SOFTRST;
72 		pmu_write(PMUCNT2REG, pmucnt2);
73 		break;
74 	default:
75 		set_c0_status(ST0_BEV | ST0_ERL);
76 		change_c0_config(CONF_CM_CMASK, CONF_CM_UNCACHED);
77 		__flush_cache_all();
78 		write_c0_wired(0);
79 		__asm__("jr	%0"::"r"(0xbfc00000));
80 		break;
81 	}
82 }
83 
vr41xx_restart(char * command)84 static void vr41xx_restart(char *command)
85 {
86 	local_irq_disable();
87 	software_reset();
88 	while (1) ;
89 }
90 
vr41xx_halt(void)91 static void vr41xx_halt(void)
92 {
93 	local_irq_disable();
94 	printk(KERN_NOTICE "\nYou can turn off the power supply\n");
95 	__asm__("hibernate;\n");
96 }
97 
vr41xx_pmu_init(void)98 static int __init vr41xx_pmu_init(void)
99 {
100 	unsigned long start, size;
101 
102 	switch (current_cpu_type()) {
103 	case CPU_VR4111:
104 	case CPU_VR4121:
105 		start = PMU_TYPE1_BASE;
106 		size = PMU_TYPE1_SIZE;
107 		break;
108 	case CPU_VR4122:
109 	case CPU_VR4131:
110 	case CPU_VR4133:
111 		start = PMU_TYPE2_BASE;
112 		size = PMU_TYPE2_SIZE;
113 		break;
114 	default:
115 		printk("Unexpected CPU of NEC VR4100 series\n");
116 		return -ENODEV;
117 	}
118 
119 	if (request_mem_region(start, size, "PMU") == NULL)
120 		return -EBUSY;
121 
122 	pmu_base = ioremap(start, size);
123 	if (pmu_base == NULL) {
124 		release_mem_region(start, size);
125 		return -EBUSY;
126 	}
127 
128 	cpu_wait = vr41xx_cpu_wait;
129 	_machine_restart = vr41xx_restart;
130 	_machine_halt = vr41xx_halt;
131 	pm_power_off = vr41xx_halt;
132 
133 	return 0;
134 }
135 
136 core_initcall(vr41xx_pmu_init);
137