source: trunk/src/os2ahci/apm.c@ 161

Last change on this file since 161 was 161, checked in by David Azarewicz, 12 years ago

Suspend/resume changes

File size: 7.7 KB
Line 
1/******************************************************************************
2 * apm.c - Functions to interface with the legacy APM driver, and suspend / resume functions.
3 *
4 * Copyright (c) 2011 thi.guten Software Development
5 * Copyright (c) 2011 Mensys B.V.
6 *
7 * Authors: Christian Mueller, Markus Thielen
8 *
9 * Parts copied from/inspired by the Linux AHCI driver;
10 * those parts are (c) Linux AHCI/ATA maintainers
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26
27#include "os2ahci.h"
28
29/* Legacy APM support is not needed on eCS systems with ACPI and is more
30 * reliable without it enabled.
31 */
32#ifdef LEGACY_APM
33
34#include <apmcalls.h>
35USHORT _far _cdecl apm_event (APMEVENT _far *evt);
36
37/******************************************************************************
38 * Connect to APM driver and register for power state change events.
39 */
40void apm_init(void)
41{
42 USHORT rc;
43
44 /* connect to APM driver */
45 if ((rc = APMAttach()) != 0) {
46 dprintf("couldn't connect to APM driver (rc = %d)\n", rc);
47 return;
48 }
49
50 /* register for suspend/resume events */
51 if ((rc = APMRegister(apm_event, APM_NOTIFYSETPWR |
52 APM_NOTIFYNORMRESUME |
53 APM_NOTIFYCRITRESUME, 0)) != 0) {
54 dprintf("couldn't register for power event notificatins (rc = %d)\n", rc);
55 return;
56 }
57}
58
59/******************************************************************************
60 * APM event handler
61 */
62USHORT _far _cdecl apm_event(APMEVENT _far *evt)
63{
64 USHORT msg = (USHORT) evt->ulParm1;
65
66 dprintf("received APM event: 0x%lx/0x%lx\n");
67
68 switch (msg) {
69
70 case APM_SETPWRSTATE:
71 if (evt->ulParm2 >> 16 != APM_PWRSTATEREADY) {
72 /* we're suspending */
73 suspend();
74 }
75 break;
76
77 case APM_NORMRESUMEEVENT:
78 case APM_CRITRESUMEEVENT:
79 /* we're resuming */
80 resume();
81 break;
82
83 default:
84 dprintf("unknown APM event; ignoring...\n");
85 break;
86 }
87
88 return(0);
89}
90#endif /* LEGACY_APM */
91
92/******************************************************************************
93 * Suspend handler. In a nutshell, it'll turn off interrupts and flush all
94 * write caches.
95 */
96void suspend(void)
97{
98 int a;
99 int p;
100 int d;
101 TIMER Timer;
102
103 if (suspended) return;
104 dprintf("suspend()\n");
105
106 /* restart all ports with interrupts disabled */
107 for (a = 0; a < ad_info_cnt; a++) {
108 AD_INFO *ai = ad_infos + a;
109
110 lock_adapter(ai);
111 for (p = 0; p <= ai->port_max; p++) {
112 /* wait until all active commands have completed on this port */
113 timer_init(&Timer, 250);
114 while (ahci_port_busy(ai, p)) {
115 if (timer_check_and_block(&Timer)) break;
116 }
117
118 /* restart port with interrupts disabled */
119 ahci_stop_port(ai, p);
120 ahci_start_port(ai, p, 0);
121
122 /* flush cache on all attached devices */
123 for (d = 0; d <= ai->ports[p].dev_max; d++) {
124 if (ai->ports[p].devs[d].present) {
125 ahci_flush_cache(ai, p, d);
126 }
127 }
128 }
129
130 /* AHCI spec rev1.1 section 8.3.3:
131 * Software must disable interrupts prior to requesting a transition of the HBA to D3 state.
132 */
133 writel(ai->mmio + HOST_CTL, readl(ai->mmio + HOST_CTL) & ~HOST_IRQ_EN);
134 readl(ai->mmio + HOST_CTL); /* flush */
135 }
136
137 /* reset init_complete so that we can process IORBs without interrupts */
138 init_complete = 0;
139
140 /* TODO: put the device into the D3 state */
141
142 suspended = 1;
143 dprintf("suspend() finished\n");
144}
145
146/******************************************************************************
147 * Resume handler. All ports are restarted with interrupts enabled using
148 * the same function as the IOCM_COMPLETE_INIT handler does.
149 */
150void resume(void)
151{
152 int a;
153
154 if (!suspended) return;
155 dprintf("resume()\n");
156
157 /* TODO: put the device into the D0 state */
158
159 for (a = 0; a < ad_info_cnt; a++) {
160 AD_INFO *ai = ad_infos + a;
161
162 //ahci_reset_controller(ai);
163
164 /* Complete initialization of this adapter; this will restart the ports
165 * with interrupts enabled and take care of whatever else needs to be
166 * done to get the adapter and its ports up and running.
167 */
168 ahci_complete_init(ai);
169 }
170
171 /* tell the driver we're again fully operational */
172 init_complete = 1;
173
174 /* unlock all adapters now that we have set the init_complete flag */
175 for (a = 0; a < ad_info_cnt; a++) {
176 AD_INFO *ai = ad_infos + a;
177 unlock_adapter(ai);
178 }
179
180 suspended = 0;
181
182 /* restart engine to resume IORB processing */
183 resume_sleep_flag = 5000;
184 DevHelp_ArmCtxHook(0, engine_ctxhook_h);
185
186 dprintf("resume() finished\n");
187}
188
189/******************************************************************************
190 * This is the kernel exit handler for panics and traps.
191 * Assume the system is trashed and do the absolute minimum necessary
192 * to put the adapters into a state so that the BIOS can operate the
193 * adapters. We never need to recover from this as the system will be rebooted.
194 */
195void shutdown_driver(void)
196{
197 int a;
198 int p;
199 u16 i;
200 u32 tmp;
201 //int d;
202
203 dprintf("shutdown_driver() enter\n");
204
205 for (a = 0; a < ad_info_cnt; a++) {
206 AD_INFO *ai = ad_infos + a;
207
208 /* Try to be nice. Wait 50ms for adapter to go not busy.
209 * If it doesn't go not busy in that time, too bad. Stop it anyway.
210 */
211 for (i=0; i<50000 && ai->busy; i++) udelay(1000);
212
213 for (p = 0; p <= ai->port_max; p++) {
214 u8 _far *port_mmio = port_base(ai, p);
215
216 /* Wait up to 50ms for port to go not busy. Again stop it
217 * anyway if it doesn't go not busy in that time.
218 */
219 for (i=0; i<50000 && ahci_port_busy(ai, p); i++) udelay(1000);
220
221 /* stop port */
222 writel(port_mmio + PORT_IRQ_MASK, 0); /* disable port interrupts */
223 writel(port_mmio + PORT_CMD, readl(port_mmio + PORT_CMD) & ~PORT_CMD_FIS_RX); /* disable FIS reception */
224 while (readl(port_mmio + PORT_CMD) & PORT_CMD_FIS_ON); /* wait for it to stop */
225 writel(port_mmio + PORT_CMD, readl(port_mmio + PORT_CMD) & ~PORT_CMD_START); /* set port to idle */
226 while (readl(port_mmio + PORT_CMD) & PORT_CMD_LIST_ON); /* wait for it to stop */
227
228 /* clear any pending port IRQs */
229 tmp = readl(port_mmio + PORT_IRQ_STAT);
230 if (tmp) writel(port_mmio + PORT_IRQ_STAT, tmp);
231 writel(ai->mmio + HOST_IRQ_STAT, 1UL << p);
232
233 /* reset PxSACT register (tagged command queues, not reset by COMRESET) */
234 writel(port_mmio + PORT_SCR_ACT, 0);
235 readl(port_mmio + PORT_SCR_ACT); /* flush */
236
237 #if 0
238 ahci_start_port(ai, p, 0);
239
240 /* flush cache on all attached devices */
241 for (d = 0; d <= ai->ports[p].dev_max; d++) {
242 if (ai->ports[p].devs[d].present) {
243 ahci_flush_cache(ai, p, d);
244 }
245 }
246 #endif
247 }
248 }
249
250 init_complete = 0;
251
252 /* restore BIOS configuration for each adapter */
253 for (a = 0; a < ad_info_cnt; a++) {
254 ahci_restore_bios_config(ad_infos + a);
255 }
256
257 dprintf("shutdown_driver() finished\n");
258}
259
Note: See TracBrowser for help on using the repository browser.