Merge to Fedora kernel-2.6.7-1.441
[linux-2.6.git] / drivers / net / wireless / prism54 / isl_38xx.c
1 /*
2  *  
3  *  Copyright (C) 2002 Intersil Americas Inc.
4  *  Copyright (C) 2003-2004 Luis R. Rodriguez <mcgrof@ruslug.rutgers.edu>_
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
20
21 #define __KERNEL_SYSCALLS__
22
23 #include <linux/version.h>
24 #include <linux/module.h>
25 #include <linux/types.h>
26 #include <linux/delay.h>
27
28 #include <asm/uaccess.h>
29 #include <asm/io.h>
30
31 #include "prismcompat.h"
32 #include "isl_38xx.h"
33 #include "islpci_dev.h"
34 #include "islpci_mgt.h"
35
36 /******************************************************************************
37     Device Interface & Control functions
38 ******************************************************************************/
39
40 /**
41  * isl38xx_disable_interrupts - disable all interrupts
42  * @device: pci memory base address
43  *
44  *  Instructs the device to disable all interrupt reporting by asserting 
45  *  the IRQ line. New events may still show up in the interrupt identification
46  *  register located at offset %ISL38XX_INT_IDENT_REG.
47  */
48 void
49 isl38xx_disable_interrupts(void *device)
50 {
51         isl38xx_w32_flush(device, 0x00000000, ISL38XX_INT_EN_REG);
52         udelay(ISL38XX_WRITEIO_DELAY);
53 }
54
55 void
56 isl38xx_handle_sleep_request(isl38xx_control_block *control_block,
57                              int *powerstate, void *device_base)
58 {
59         /* device requests to go into sleep mode
60          * check whether the transmit queues for data and management are empty */
61         if (isl38xx_in_queue(control_block, ISL38XX_CB_TX_DATA_LQ))
62                 /* data tx queue not empty */
63                 return;
64
65         if (isl38xx_in_queue(control_block, ISL38XX_CB_TX_MGMTQ))
66                 /* management tx queue not empty */
67                 return;
68
69         /* check also whether received frames are pending */
70         if (isl38xx_in_queue(control_block, ISL38XX_CB_RX_DATA_LQ))
71                 /* data rx queue not empty */
72                 return;
73
74         if (isl38xx_in_queue(control_block, ISL38XX_CB_RX_MGMTQ))
75                 /* management rx queue not empty */
76                 return;
77
78 #if VERBOSE > SHOW_ERROR_MESSAGES
79         DEBUG(SHOW_TRACING, "Device going to sleep mode\n");
80 #endif
81
82         /* all queues are empty, allow the device to go into sleep mode */
83         *powerstate = ISL38XX_PSM_POWERSAVE_STATE;
84
85         /* assert the Sleep interrupt in the Device Interrupt Register */
86         isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_SLEEP,
87                           ISL38XX_DEV_INT_REG);
88         udelay(ISL38XX_WRITEIO_DELAY);
89 }
90
91 void
92 isl38xx_handle_wakeup(isl38xx_control_block *control_block,
93                       int *powerstate, void *device_base)
94 {
95         /* device is in active state, update the powerstate flag */
96         *powerstate = ISL38XX_PSM_ACTIVE_STATE;
97
98         /* now check whether there are frames pending for the card */
99         if (!isl38xx_in_queue(control_block, ISL38XX_CB_TX_DATA_LQ)
100             && !isl38xx_in_queue(control_block, ISL38XX_CB_TX_MGMTQ))
101                 return;
102
103 #if VERBOSE > SHOW_ERROR_MESSAGES
104         DEBUG(SHOW_ANYTHING, "Wake up handler trigger the device\n");
105 #endif
106
107         /* either data or management transmit queue has a frame pending
108          * trigger the device by setting the Update bit in the Device Int reg */
109         isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_UPDATE,
110                           ISL38XX_DEV_INT_REG);
111         udelay(ISL38XX_WRITEIO_DELAY);
112 }
113
114 void
115 isl38xx_trigger_device(int asleep, void *device_base)
116 {
117         struct timeval current_time;
118         u32 reg, counter = 0;
119
120 #if VERBOSE > SHOW_ERROR_MESSAGES
121         DEBUG(SHOW_FUNCTION_CALLS, "isl38xx trigger device\n");
122 #endif
123
124         /* check whether the device is in power save mode */
125         if (asleep) {
126                 /* device is in powersave, trigger the device for wakeup */
127 #if VERBOSE > SHOW_ERROR_MESSAGES
128                 do_gettimeofday(&current_time);
129                 DEBUG(SHOW_TRACING, "%08li.%08li Device wakeup triggered\n",
130                       current_time.tv_sec, current_time.tv_usec);
131 #endif
132
133                 DEBUG(SHOW_TRACING, "%08li.%08li Device register read %08x\n",
134                       current_time.tv_sec, current_time.tv_usec,
135                       readl(device_base + ISL38XX_CTRL_STAT_REG));
136                 udelay(ISL38XX_WRITEIO_DELAY);
137
138                 if (reg = readl(device_base + ISL38XX_INT_IDENT_REG),
139                     reg == 0xabadface) {
140 #if VERBOSE > SHOW_ERROR_MESSAGES
141                         do_gettimeofday(&current_time);
142                         DEBUG(SHOW_TRACING,
143                               "%08li.%08li Device register abadface\n",
144                               current_time.tv_sec, current_time.tv_usec);
145 #endif
146                         /* read the Device Status Register until Sleepmode bit is set */
147                         while (reg = readl(device_base + ISL38XX_CTRL_STAT_REG),
148                                (reg & ISL38XX_CTRL_STAT_SLEEPMODE) == 0) {
149                                 udelay(ISL38XX_WRITEIO_DELAY);
150                                 counter++;
151                         }
152
153                         DEBUG(SHOW_TRACING,
154                               "%08li.%08li Device register read %08x\n",
155                               current_time.tv_sec, current_time.tv_usec,
156                               readl(device_base + ISL38XX_CTRL_STAT_REG));
157                         udelay(ISL38XX_WRITEIO_DELAY);
158
159 #if VERBOSE > SHOW_ERROR_MESSAGES
160                         do_gettimeofday(&current_time);
161                         DEBUG(SHOW_TRACING,
162                               "%08li.%08li Device asleep counter %i\n",
163                               current_time.tv_sec, current_time.tv_usec,
164                               counter);
165 #endif
166                 }
167                 /* assert the Wakeup interrupt in the Device Interrupt Register */
168                 isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_WAKEUP,
169                                   ISL38XX_DEV_INT_REG);
170                 udelay(ISL38XX_WRITEIO_DELAY);
171
172                 /* perform another read on the Device Status Register */
173                 reg = readl(device_base + ISL38XX_CTRL_STAT_REG);
174                 udelay(ISL38XX_WRITEIO_DELAY);
175
176 #if VERBOSE > SHOW_ERROR_MESSAGES
177                 do_gettimeofday(&current_time);
178                 DEBUG(SHOW_TRACING, "%08li.%08li Device register read %08x\n",
179                       current_time.tv_sec, current_time.tv_usec, reg);
180 #endif
181         } else {
182                 /* device is (still) awake  */
183 #if VERBOSE > SHOW_ERROR_MESSAGES
184                 DEBUG(SHOW_TRACING, "Device is in active state\n");
185 #endif
186                 /* trigger the device by setting the Update bit in the Device Int reg */
187
188                 isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_UPDATE,
189                                   ISL38XX_DEV_INT_REG);
190                 udelay(ISL38XX_WRITEIO_DELAY);
191         }
192 }
193
194 void
195 isl38xx_interface_reset(void *device_base, dma_addr_t host_address)
196 {
197         u32 reg;
198
199 #if VERBOSE > SHOW_ERROR_MESSAGES
200         DEBUG(SHOW_FUNCTION_CALLS, "isl38xx_interface_reset \n");
201 #endif
202
203         /* load the address of the control block in the device */
204         isl38xx_w32_flush(device_base, host_address, ISL38XX_CTRL_BLK_BASE_REG);
205         udelay(ISL38XX_WRITEIO_DELAY);
206
207         /* set the reset bit in the Device Interrupt Register */
208         isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_RESET,
209                           ISL38XX_DEV_INT_REG);
210         udelay(ISL38XX_WRITEIO_DELAY);
211
212         /* enable the interrupt for detecting initialization */
213
214         /* Note: Do not enable other interrupts here. We want the
215          * device to have come up first 100% before allowing any other 
216          * interrupts. */
217         reg = ISL38XX_INT_IDENT_INIT;
218
219         isl38xx_w32_flush(device_base, reg, ISL38XX_INT_EN_REG);
220         udelay(ISL38XX_WRITEIO_DELAY);  /* allow complete full reset */
221 }
222
223 void
224 isl38xx_enable_common_interrupts(void *device_base) {
225         u32 reg;
226         reg = ( ISL38XX_INT_IDENT_UPDATE | 
227                         ISL38XX_INT_IDENT_SLEEP | ISL38XX_INT_IDENT_WAKEUP);
228         isl38xx_w32_flush(device_base, reg, ISL38XX_INT_EN_REG);
229         udelay(ISL38XX_WRITEIO_DELAY);
230 }
231
232 int
233 isl38xx_in_queue(isl38xx_control_block *cb, int queue)
234 {
235         const s32 delta = (le32_to_cpu(cb->driver_curr_frag[queue]) -
236                            le32_to_cpu(cb->device_curr_frag[queue]));
237
238         /* determine the amount of fragments in the queue depending on the type
239          * of the queue, either transmit or receive */
240
241         BUG_ON(delta < 0);      /* driver ptr must be ahead of device ptr */
242
243         switch (queue) {
244                 /* send queues */
245         case ISL38XX_CB_TX_MGMTQ:
246                 BUG_ON(delta > ISL38XX_CB_MGMT_QSIZE);
247         case ISL38XX_CB_TX_DATA_LQ:
248         case ISL38XX_CB_TX_DATA_HQ:
249                 BUG_ON(delta > ISL38XX_CB_TX_QSIZE);
250                 return delta;
251                 break;
252
253                 /* receive queues */
254         case ISL38XX_CB_RX_MGMTQ:
255                 BUG_ON(delta > ISL38XX_CB_MGMT_QSIZE);
256                 return ISL38XX_CB_MGMT_QSIZE - delta;
257                 break;
258
259         case ISL38XX_CB_RX_DATA_LQ:
260         case ISL38XX_CB_RX_DATA_HQ:
261                 BUG_ON(delta > ISL38XX_CB_RX_QSIZE);
262                 return ISL38XX_CB_RX_QSIZE - delta;
263                 break;
264         }
265         BUG();
266         return 0;
267 }