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

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

driver info updates, misc cleanup, add comments
This is version 1.28

File size: 8.0 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 /* TODO: put the device into the D3 state */
137 }
138
139 /* reset init_complete so that we can process IORBs without interrupts */
140 init_complete = 0;
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 for (a = 0; a < ad_info_cnt; a++) {
158 AD_INFO *ai = ad_infos + a;
159
160 /* TODO: put the device into the D0 state */
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 /* The resume_sleep_flag and probably rearming the ctx hook is a temporary hack
184 * to make resume kind of work when I/O operations are outstanding or started
185 * during the suspend operation. This behavior may change with future versions
186 * of the ACPI software which will make this hack unnecessary.
187 */
188 resume_sleep_flag = 5000;
189 DevHelp_ArmCtxHook(0, engine_ctxhook_h);
190
191 dprintf("resume() finished\n");
192}
193
194/******************************************************************************
195 * This is the kernel exit handler for panics and traps.
196 * Assume the system is trashed and do the absolute minimum necessary
197 * to put the adapters into a state so that the BIOS can operate the
198 * adapters. We never need to recover from this as the system will be rebooted.
199 */
200void shutdown_driver(void)
201{
202 int a;
203 int p;
204 u16 i;
205 u32 tmp;
206 //int d;
207
208 dprintf("shutdown_driver() enter\n");
209
210 for (a = 0; a < ad_info_cnt; a++) {
211 AD_INFO *ai = ad_infos + a;
212
213 /* Try to be nice. Wait 50ms for adapter to go not busy.
214 * If it doesn't go not busy in that time, too bad. Stop it anyway.
215 */
216 for (i=0; i<50000 && ai->busy; i++) udelay(1000);
217
218 for (p = 0; p <= ai->port_max; p++) {
219 u8 _far *port_mmio = port_base(ai, p);
220
221 /* Wait up to 50ms for port to go not busy. Again stop it
222 * anyway if it doesn't go not busy in that time.
223 */
224 for (i=0; i<50000 && ahci_port_busy(ai, p); i++) udelay(1000);
225
226 /* stop port */
227 writel(port_mmio + PORT_IRQ_MASK, 0); /* disable port interrupts */
228 writel(port_mmio + PORT_CMD, readl(port_mmio + PORT_CMD) & ~PORT_CMD_FIS_RX); /* disable FIS reception */
229 while (readl(port_mmio + PORT_CMD) & PORT_CMD_FIS_ON); /* wait for it to stop */
230 writel(port_mmio + PORT_CMD, readl(port_mmio + PORT_CMD) & ~PORT_CMD_START); /* set port to idle */
231 while (readl(port_mmio + PORT_CMD) & PORT_CMD_LIST_ON); /* wait for it to stop */
232
233 /* clear any pending port IRQs */
234 tmp = readl(port_mmio + PORT_IRQ_STAT);
235 if (tmp) writel(port_mmio + PORT_IRQ_STAT, tmp);
236 writel(ai->mmio + HOST_IRQ_STAT, 1UL << p);
237
238 /* reset PxSACT register (tagged command queues, not reset by COMRESET) */
239 writel(port_mmio + PORT_SCR_ACT, 0);
240 readl(port_mmio + PORT_SCR_ACT); /* flush */
241
242 #if 0
243 ahci_start_port(ai, p, 0);
244
245 /* flush cache on all attached devices */
246 for (d = 0; d <= ai->ports[p].dev_max; d++) {
247 if (ai->ports[p].devs[d].present) {
248 ahci_flush_cache(ai, p, d);
249 }
250 }
251 #endif
252 }
253 }
254
255 init_complete = 0;
256
257 /* restore BIOS configuration for each adapter */
258 for (a = 0; a < ad_info_cnt; a++) {
259 ahci_restore_bios_config(ad_infos + a);
260 }
261
262 dprintf("shutdown_driver() finished\n");
263}
264
Note: See TracBrowser for help on using the repository browser.