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

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

fixed trap dump kernel exit, some work on suspend/resume routines

File size: 7.9 KB
Line 
1/******************************************************************************
2 * apm.c - Functions to interface with the APM driver.
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#include <apmcalls.h>
30
31/* -------------------------- macros and constants ------------------------- */
32
33/* ------------------------ typedefs and structures ------------------------ */
34
35/* -------------------------- function prototypes -------------------------- */
36
37/* ------------------------ global/static variables ------------------------ */
38
39USHORT _far _cdecl apm_event (APMEVENT _far *evt);
40
41/* ----------------------------- start of code ----------------------------- */
42
43/******************************************************************************
44 * Connect to APM driver and register for power state change events.
45 */
46void apm_init(void)
47{
48 USHORT rc;
49
50 /* connect to APM driver */
51 if ((rc = APMAttach()) != 0) {
52 dprintf("couldn't connect to APM driver (rc = %d)\n", rc);
53 return;
54 }
55
56 /* register for suspend/resume events */
57 if ((rc = APMRegister(apm_event, APM_NOTIFYSETPWR |
58 APM_NOTIFYNORMRESUME |
59 APM_NOTIFYCRITRESUME, 0)) != 0) {
60 dprintf("couldn't register for power event notificatins (rc = %d)\n", rc);
61 return;
62 }
63}
64
65/******************************************************************************
66 * APM event handler
67 */
68USHORT _far _cdecl apm_event(APMEVENT _far *evt)
69{
70 USHORT msg = (USHORT) evt->ulParm1;
71
72 dprintf("received APM event: 0x%lx/0x%lx\n");
73
74 switch (msg) {
75
76 case APM_SETPWRSTATE:
77 if (evt->ulParm2 >> 16 != APM_PWRSTATEREADY) {
78 /* we're suspending */
79 suspend();
80 }
81 break;
82
83 case APM_NORMRESUMEEVENT:
84 case APM_CRITRESUMEEVENT:
85 /* we're resuming */
86 resume();
87 break;
88
89 default:
90 dprintf("unknown APM event; ignoring...\n");
91 break;
92 }
93
94 return(0);
95}
96
97/******************************************************************************
98 * Suspend handler. In a nutshell, it'll turn off interrupts and flush all
99 * write caches.
100 */
101void suspend(void)
102{
103 int a;
104 int p;
105 int d;
106 TIMER Timer;
107
108 if (suspended) return;
109 dprintf("suspend()\n");
110
111 /* restart all ports with interrupts disabled */
112 for (a = 0; a < ad_info_cnt; a++) {
113 AD_INFO *ai = ad_infos + a;
114
115 lock_adapter(ai);
116 for (p = 0; p <= ai->port_max; p++) {
117 /* wait until all active commands have completed on this port */
118 timer_init(&Timer, 250);
119 while (ahci_port_busy(ai, p)) {
120 if (timer_check_and_block(&Timer)) break;
121 }
122
123 /* restart port with interrupts disabled */
124 ahci_stop_port(ai, p);
125 ahci_start_port(ai, p, 0);
126
127 /* flush cache on all attached devices */
128 for (d = 0; d <= ai->ports[p].dev_max; d++) {
129 if (ai->ports[p].devs[d].present) {
130 ahci_flush_cache(ai, p, d);
131 }
132 }
133 }
134
135 /* AHCI spec rev1.1 section 8.3.3:
136 * Software must disable interrupts prior to requesting a transition of the HBA to D3 state.
137 */
138 writel(ai->mmio + HOST_CTL, readl(ai->mmio + HOST_CTL) & ~HOST_IRQ_EN);
139 readl(ai->mmio + HOST_CTL); /* flush */
140 }
141
142 /* reset init_complete so that we can process IORBs without interrupts */
143 init_complete = 0;
144
145#if 0
146 /* restore BIOS configuration for each adapter and release the adapter */
147 for (a = 0; a < ad_info_cnt; a++) {
148 ahci_restore_bios_config(ad_infos + a);
149 }
150#endif
151
152 /* TODO: put the device into the D3 state */
153
154 suspended = 1;
155 dprintf("suspend() finished\n");
156}
157
158/******************************************************************************
159 * Resume handler. All ports are restarted with interrupts enabled using
160 * the same function as the IOCM_COMPLETE_INIT handler does.
161 */
162void resume(void)
163{
164 int a;
165
166 if (!suspended) return;
167 dprintf("resume()\n");
168
169 /* TODO: put the device into the D0 state */
170
171 for (a = 0; a < ad_info_cnt; a++) {
172 AD_INFO *ai = ad_infos + a;
173
174 /* Complete initialization of this adapter; this will restart the ports
175 * with interrupts enabled and take care of whatever else needs to be
176 * done to get the adapter and its ports up and running.
177 */
178 ahci_complete_init(ai);
179 }
180
181 /* tell the driver we're again fully operational */
182 init_complete = 1;
183
184 /* unlock all adapters now that we have set the init_complete flag */
185 for (a = 0; a < ad_info_cnt; a++) {
186 unlock_adapter(ad_infos + a);
187 }
188
189 suspended = 0;
190 dprintf("resume() finished\n");
191}
192
193/******************************************************************************
194 * This is the kernel exit handler for panics and traps.
195 * Assume the system is trashed and do the absolute minimum necessary
196 * to put the adapters into a state so that the BIOS can operate the
197 * adapters. We never need to recover from this as the system will be rebooted.
198 */
199void shutdown_driver(void)
200{
201 int a;
202 int p;
203 u16 i;
204 u32 tmp;
205 //int d;
206
207 dprintf("shutdown_driver() enter\n");
208
209 for (a = 0; a < ad_info_cnt; a++) {
210 AD_INFO *ai = ad_infos + a;
211
212 /* Try to be nice. Wait 50ms for adapter to go not busy.
213 * If it doesn't go not busy in that time, too bad. Stop it anyway.
214 */
215 for (i=0; i<50000 && ai->busy; i++) udelay(1000);
216
217 for (p = 0; p <= ai->port_max; p++) {
218 u8 _far *port_mmio = port_base(ai, p);
219
220 /* Wait up to 50ms for port to go not busy. Again stop it
221 * anyway if it doesn't go not busy in that time.
222 */
223 for (i=0; i<50000 && ahci_port_busy(ai, p); i++) udelay(1000);
224
225 /* stop port */
226 writel(port_mmio + PORT_IRQ_MASK, 0); /* disable port interrupts */
227 writel(port_mmio + PORT_CMD, readl(port_mmio + PORT_CMD) & ~PORT_CMD_FIS_RX); /* disable FIS reception */
228 while (readl(port_mmio + PORT_CMD) & PORT_CMD_FIS_ON); /* wait for it to stop */
229 writel(port_mmio + PORT_CMD, readl(port_mmio + PORT_CMD) & ~PORT_CMD_START); /* set port to idle */
230 while (readl(port_mmio + PORT_CMD) & PORT_CMD_LIST_ON); /* wait for it to stop */
231
232 /* clear any pending port IRQs */
233 tmp = readl(port_mmio + PORT_IRQ_STAT);
234 if (tmp) writel(port_mmio + PORT_IRQ_STAT, tmp);
235 writel(ai->mmio + HOST_IRQ_STAT, 1UL << p);
236
237 /* reset PxSACT register (tagged command queues, not reset by COMRESET) */
238 writel(port_mmio + PORT_SCR_ACT, 0);
239 readl(port_mmio + PORT_SCR_ACT); /* flush */
240
241 #if 0
242 ahci_start_port(ai, p, 0);
243
244 /* flush cache on all attached devices */
245 for (d = 0; d <= ai->ports[p].dev_max; d++) {
246 if (ai->ports[p].devs[d].present) {
247 ahci_flush_cache(ai, p, d);
248 }
249 }
250 #endif
251 }
252 }
253
254 init_complete = 0;
255
256 /* restore BIOS configuration for each adapter */
257 for (a = 0; a < ad_info_cnt; a++) {
258 ahci_restore_bios_config(ad_infos + a);
259 }
260
261 dprintf("shutdown_driver() finished\n");
262}
263
Note: See TracBrowser for help on using the repository browser.