clean
[linux-2.4.21-pre4.git] / arch / ppc / kernel / ppc4xx_pm.c
1 /*
2  *      file: ppc4xx_pm.c
3  *
4  *      This an attempt to get Power Management going for the IBM 4xx processor.
5  *      This was derived from the ppc4xx._setup.c file
6  *
7  *      Armin Kuster akuster@mvista.com
8  *      Jan  2002
9  *
10  *
11  * Copyright 2002 MontaVista Softare Inc.
12  *
13  * This program is free software; you can redistribute  it and/or modify it
14  *  under  the terms of  the GNU General Public License as published by the
15  *  Free Software Foundation;  either version 2 of the  License, or (at your
16  *  option) any later version.
17  *
18  *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR   IMPLIED
19  *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
20  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
21  *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT,  INDIRECT,
22  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
24  *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25  *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
26  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  *  You should have received a copy of the  GNU General Public License along
30  *  with this program; if not, write  to the Free Software Foundation, Inc.,
31  *  675 Mass Ave, Cambridge, MA 02139, USA.
32  *
33  *      Version 1.0 (02/14/01) - A. Kuster
34  *      Initial version  - moved pm code from ppc4xx_setup.c
35  *
36  *      1.1 02/21/01 - A. Kuster
37  *              minor fixes, init value to 0 & += to &=
38  *              added stb03 ifdef for 2nd i2c device
39  *
40  *      1.2 02/23/01 - A. Kuster
41  *              Added 4xx version of apm support
42  *
43  *      1.3 05/25/02 - Armin
44  *         name change *_driver *_dev
45  *
46  *      1.4 06/18/02 - Armin
47  *      fix IIC config name
48  *
49  *      1.5 07/24/02 - Armin
50  *      fixed ppc4xx_pm_init so it inits now
51  *      and am using a default Power Managment bitmap
52  */
53
54 #include <linux/config.h>
55 #include <linux/init.h>
56 #include <linux/kernel.h>
57 #include <linux/list.h>
58 #include <linux/module.h>
59 #include <linux/pm.h>
60 #include <asm/ibm4xx.h>
61 #include <platforms/ibm_ocp.h>
62 #include <asm/system.h>
63
64 #undef DEBUG
65
66 #ifdef DEBUG
67 #define DBG(x...) printk(x)
68 #else
69 #define DBG(x...)
70 #endif
71
72 LIST_HEAD(ocp_devices);
73
74 #ifdef CONFIG_APM
75 /*
76  * OCP Power management..
77  *
78  * This needs to be done centralized, so that we power manage PCI
79  * devices in the right order: we should not shut down PCI bridges
80  * before we've shut down the devices behind them, and we should
81  * not wake up devices before we've woken up the bridge to the
82  * device.. Eh?
83  *
84  * We do not touch devices that don't have a driver that exports
85  * a suspend/resume function. That is just too dangerous. If the default
86  * PCI suspend/resume functions work for a device, the driver can
87  * easily implement them (ie just have a suspend function that calls
88  * the pci_set_power_state() function).
89  */
90
91 static int ocp_pm_save_state_device(struct ocp_dev *dev, u32 state)
92 {
93         int error = 0;
94         if (dev) {
95                 struct ocp_dev *driver = dev->driver;
96                 if (driver && driver->save_state)
97                         error = driver->save_state(dev,state);
98         }
99         return error;
100 }
101
102 static int ocp_pm_suspend_device(struct ocp_dev *dev, u32 state)
103 {
104         int error = 0;
105         if (dev) {
106                 struct ocp_dev *driver = dev->driver;
107                 if (driver && driver->suspend)
108                         error = driver->suspend(dev,state);
109         }
110         return error;
111 }
112
113 static int ocp_pm_resume_device(struct ocp_dev *dev)
114 {
115         int error = 0;
116         if (dev) {
117                 struct ocp_dev *driver = dev->driver;
118                 if (driver && driver->resume)
119                         error = driver->resume(dev);
120         }
121         return error;
122 }
123
124 static int
125 ocp_pm_callback(struct pm_dev *pm_device, pm_request_t rqst, void *data)
126 {
127         int error = 0;
128
129         switch (rqst) {
130         case PM_SAVE_STATE:
131                 error = ocp_pm_save_state_device((u32)data);
132                 break;
133         case PM_SUSPEND:
134                 error = ocp_pm_suspend_device((u32)data);
135                 break;
136         case PM_RESUME:
137                 error = ocp_pm_resume_device((u32)data);
138                 break;
139         default: break;
140         }
141         return error;
142 }
143 /**
144  * ocp_register_driver - register a new ocp driver
145  * @drv: the driver structure to register
146  *
147  * Adds the driver structure to the list of registered drivers
148  * Returns the number of ocp devices which were claimed by the driver
149  * during registration.  The driver remains registered even if the
150  * return value is zero.
151  */
152 int
153 ocp_register_driver(struct ocp_dev *drv)
154 {
155         struct ocp_dev *dev;
156         struct ocp_
157         list_add_tail(&drv->node, &ocp_devs);
158         return 0;
159 }
160
161 EXPORT_SYMBOL(ocp_register_driver);
162 #endif
163
164 /* When bits are "1" then the given clock is
165  * stopped therefore saving power 
166  *
167  * The objected is to turn off all unneccessary 
168  * clocks and have the drivers enable/disable
169  * them when in use.  We set the default
170  * in the <core>.h file
171  */
172
173 void __init
174 ppc4xx_pm_init(void)
175 {
176         
177         mtdcr(DCRN_CPMFR, 0);
178
179         /* turn off unused hardware to save power */
180
181         printk(KERN_INFO "OCP 4xx power management enabled\n");
182         mtdcr(DCRN_CPMFR, DFLT_IBM4xx_PM);
183
184 #ifdef CONFIG_APM
185         pm_gpio = pm_register(PM_SYS_DEV, 0, ocp_pm_callback);
186 #endif
187 }
188 __initcall(ppc4xx_pm_init);
189
190 /* Force/unforce power down for CPM Class 1 devices */
191
192 void
193 ppc4xx_cpm_fr(u32 bits, int val)
194 {
195         unsigned long flags;
196
197         save_flags(flags);
198         cli();
199
200         if (val)
201                 mtdcr(DCRN_CPMFR, mfdcr(DCRN_CPMFR) | bits);
202         else
203                 mtdcr(DCRN_CPMFR, mfdcr(DCRN_CPMFR) & ~bits);
204
205         restore_flags(flags);
206 }