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

Last change on this file since 209 was 209, checked in by David Azarewicz, 4 years ago

Debugging support changes.

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-2021 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.