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

Last change on this file since 196 was 196, checked in by David Azarewicz, 7 years ago

Changes to debug output for debug versions.
Removed interrupt requirement on init.

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