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

Last change on this file since 211 was 211, checked in by David Azarewicz, 2 years ago

Added workaround to help with VirtualBox issues.
Improved diagnostic messages.
Changed how timeouts are reset and how ctx hooks are triggered.
Added quirk for devices with issues executing some standard commands.
Changed to make /N the default.

File size: 7.9 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 * Copyright (c) 2013-2023 David Azarewicz <david@88watts.net>
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(0, DBG_PREFIX": 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(0, DBG_PREFIX": 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(DBG_FUNCBEG, DBG_PREFIX": 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(0, DBG_PREFIX": 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(DBG_FUNCBEG, DBG_PREFIX": 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 {
126 if (ai->ports[p].devs[d].present)
127 {
128 ahci_flush_cache(ai, p, d);
129 }
130 }
131 }
132
133 /* AHCI spec rev1.1 section 8.3.3:
134 * Software must disable interrupts prior to requesting a transition of the HBA to D3 state.
135 */
136 writel(ai->mmio + HOST_CTL, readl(ai->mmio + HOST_CTL) & ~HOST_IRQ_EN);
137 readl(ai->mmio + HOST_CTL); /* flush */
138
139 /* TODO: put the device into the D3 state */
140 }
141
142 /* reset init_complete so that we can process IORBs without interrupts */
143 init_complete = 0;
144
145 suspended = 1;
146 DPRINTF(DBG_FUNCEND, DBG_PREFIX": suspend() finished\n");
147}
148
149/******************************************************************************
150 * Resume handler. All ports are restarted with interrupts enabled using
151 * the same function as the IOCM_COMPLETE_INIT handler does.
152 */
153void resume(void)
154{
155 int a;
156
157 if (!suspended) return;
158 DPRINTF(DBG_FUNCBEG, DBG_PREFIX": resume()\n");
159
160 for (a = 0; a < ad_info_cnt; a++) {
161 AD_INFO *ai = ad_infos + a;
162
163 /* TODO: put the device into the D0 state */
164
165 //ahci_reset_controller(ai);
166
167 /* Complete initialization of this adapter; this will restart the ports
168 * with interrupts enabled and take care of whatever else needs to be
169 * done to get the adapter and its ports up and running.
170 */
171 ahci_complete_init(ai);
172 }
173
174 /* tell the driver we're again fully operational */
175 init_complete = 1;
176
177 /* unlock all adapters now that we have set the init_complete flag */
178 for (a = 0; a < ad_info_cnt; a++) {
179 AD_INFO *ai = ad_infos + a;
180 unlock_adapter(ai);
181 }
182
183 suspended = 0;
184
185 /* restart engine to resume IORB processing */
186 /* The resume_sleep_flag and probably rearming the ctx hook is a temporary hack
187 * to make resume kind of work when I/O operations are outstanding or started
188 * during the suspend operation. This behavior may change with future versions
189 * of the ACPI software which will make this hack unnecessary.
190 */
191 resume_sleep_flag = 5000;
192 KernArmHook(engine_ctxhook_h, 0, 0);
193
194 DPRINTF(DBG_FUNCEND, DBG_PREFIX": resume() finished\n");
195}
196
197/******************************************************************************
198 * This is the kernel exit handler for panics and traps.
199 * Assume the system is trashed and do the absolute minimum necessary
200 * to put the adapters into a state so that the BIOS can operate the
201 * adapters. We never need to recover from this as the system will be rebooted.
202 */
203void shutdown_driver(void)
204{
205 int a;
206 int p;
207 u16 i;
208 u32 tmp;
209 //int d;
210
211 DPRINTF(DBG_FUNCBEG, DBG_PREFIX": shutdown_driver() enter\n");
212
213 for (a = 0; a < ad_info_cnt; a++)
214 {
215 AD_INFO *ai = ad_infos + a;
216
217 /* Try to be nice. Wait 50ms for adapter to go not busy.
218 * If it doesn't go not busy in that time, too bad. Stop it anyway.
219 */
220 for (i=0; i<50000 && ai->busy; i++) udelay(1000);
221
222 for (p = 0; p <= ai->port_max; p++)
223 {
224 u8 *port_mmio = port_base(ai, p);
225
226 /* Wait up to 50ms for port to go not busy. Again stop it
227 * anyway if it doesn't go not busy in that time.
228 */
229 for (i=0; i<50000 && ahci_port_busy(ai, p); i++) udelay(1000);
230
231 /* stop port */
232 writel(port_mmio + PORT_IRQ_MASK, 0); /* disable port interrupts */
233 writel(port_mmio + PORT_CMD, readl(port_mmio + PORT_CMD) & ~PORT_CMD_FIS_RX); /* disable FIS reception */
234 while (readl(port_mmio + PORT_CMD) & PORT_CMD_FIS_ON); /* wait for it to stop */
235 writel(port_mmio + PORT_CMD, readl(port_mmio + PORT_CMD) & ~PORT_CMD_START); /* set port to idle */
236 while (readl(port_mmio + PORT_CMD) & PORT_CMD_LIST_ON); /* wait for it to stop */
237
238 /* clear any pending port IRQs */
239 tmp = readl(port_mmio + PORT_IRQ_STAT);
240 if (tmp) writel(port_mmio + PORT_IRQ_STAT, tmp);
241 writel(ai->mmio + HOST_IRQ_STAT, 1UL << p);
242
243 /* reset PxSACT register (tagged command queues, not reset by COMRESET) */
244 writel(port_mmio + PORT_SCR_ACT, 0);
245 readl(port_mmio + PORT_SCR_ACT); /* flush */
246 }
247 }
248
249 init_complete = 0;
250
251 /* restore BIOS configuration for each adapter */
252 for (a = 0; a < ad_info_cnt; a++)
253 {
254 ahci_restore_bios_config(ad_infos + a);
255 }
256
257 DPRINTF(DBG_FUNCEND, DBG_PREFIX": shutdown_driver() finished\n");
258}
259
Note: See TracBrowser for help on using the repository browser.