diff options
Diffstat (limited to 'drivers/net/wireless/prism54/isl_38xx.c')
-rw-r--r-- | drivers/net/wireless/prism54/isl_38xx.c | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/drivers/net/wireless/prism54/isl_38xx.c b/drivers/net/wireless/prism54/isl_38xx.c new file mode 100644 index 000000000000..4481ec18c5a0 --- /dev/null +++ b/drivers/net/wireless/prism54/isl_38xx.c | |||
@@ -0,0 +1,260 @@ | |||
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 | #include <linux/version.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/types.h> | ||
24 | #include <linux/delay.h> | ||
25 | |||
26 | #include <asm/uaccess.h> | ||
27 | #include <asm/io.h> | ||
28 | |||
29 | #include "prismcompat.h" | ||
30 | #include "isl_38xx.h" | ||
31 | #include "islpci_dev.h" | ||
32 | #include "islpci_mgt.h" | ||
33 | |||
34 | /****************************************************************************** | ||
35 | Device Interface & Control functions | ||
36 | ******************************************************************************/ | ||
37 | |||
38 | /** | ||
39 | * isl38xx_disable_interrupts - disable all interrupts | ||
40 | * @device: pci memory base address | ||
41 | * | ||
42 | * Instructs the device to disable all interrupt reporting by asserting | ||
43 | * the IRQ line. New events may still show up in the interrupt identification | ||
44 | * register located at offset %ISL38XX_INT_IDENT_REG. | ||
45 | */ | ||
46 | void | ||
47 | isl38xx_disable_interrupts(void __iomem *device) | ||
48 | { | ||
49 | isl38xx_w32_flush(device, 0x00000000, ISL38XX_INT_EN_REG); | ||
50 | udelay(ISL38XX_WRITEIO_DELAY); | ||
51 | } | ||
52 | |||
53 | void | ||
54 | isl38xx_handle_sleep_request(isl38xx_control_block *control_block, | ||
55 | int *powerstate, void __iomem *device_base) | ||
56 | { | ||
57 | /* device requests to go into sleep mode | ||
58 | * check whether the transmit queues for data and management are empty */ | ||
59 | if (isl38xx_in_queue(control_block, ISL38XX_CB_TX_DATA_LQ)) | ||
60 | /* data tx queue not empty */ | ||
61 | return; | ||
62 | |||
63 | if (isl38xx_in_queue(control_block, ISL38XX_CB_TX_MGMTQ)) | ||
64 | /* management tx queue not empty */ | ||
65 | return; | ||
66 | |||
67 | /* check also whether received frames are pending */ | ||
68 | if (isl38xx_in_queue(control_block, ISL38XX_CB_RX_DATA_LQ)) | ||
69 | /* data rx queue not empty */ | ||
70 | return; | ||
71 | |||
72 | if (isl38xx_in_queue(control_block, ISL38XX_CB_RX_MGMTQ)) | ||
73 | /* management rx queue not empty */ | ||
74 | return; | ||
75 | |||
76 | #if VERBOSE > SHOW_ERROR_MESSAGES | ||
77 | DEBUG(SHOW_TRACING, "Device going to sleep mode\n"); | ||
78 | #endif | ||
79 | |||
80 | /* all queues are empty, allow the device to go into sleep mode */ | ||
81 | *powerstate = ISL38XX_PSM_POWERSAVE_STATE; | ||
82 | |||
83 | /* assert the Sleep interrupt in the Device Interrupt Register */ | ||
84 | isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_SLEEP, | ||
85 | ISL38XX_DEV_INT_REG); | ||
86 | udelay(ISL38XX_WRITEIO_DELAY); | ||
87 | } | ||
88 | |||
89 | void | ||
90 | isl38xx_handle_wakeup(isl38xx_control_block *control_block, | ||
91 | int *powerstate, void __iomem *device_base) | ||
92 | { | ||
93 | /* device is in active state, update the powerstate flag */ | ||
94 | *powerstate = ISL38XX_PSM_ACTIVE_STATE; | ||
95 | |||
96 | /* now check whether there are frames pending for the card */ | ||
97 | if (!isl38xx_in_queue(control_block, ISL38XX_CB_TX_DATA_LQ) | ||
98 | && !isl38xx_in_queue(control_block, ISL38XX_CB_TX_MGMTQ)) | ||
99 | return; | ||
100 | |||
101 | #if VERBOSE > SHOW_ERROR_MESSAGES | ||
102 | DEBUG(SHOW_ANYTHING, "Wake up handler trigger the device\n"); | ||
103 | #endif | ||
104 | |||
105 | /* either data or management transmit queue has a frame pending | ||
106 | * trigger the device by setting the Update bit in the Device Int reg */ | ||
107 | isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_UPDATE, | ||
108 | ISL38XX_DEV_INT_REG); | ||
109 | udelay(ISL38XX_WRITEIO_DELAY); | ||
110 | } | ||
111 | |||
112 | void | ||
113 | isl38xx_trigger_device(int asleep, void __iomem *device_base) | ||
114 | { | ||
115 | struct timeval current_time; | ||
116 | u32 reg, counter = 0; | ||
117 | |||
118 | #if VERBOSE > SHOW_ERROR_MESSAGES | ||
119 | DEBUG(SHOW_FUNCTION_CALLS, "isl38xx trigger device\n"); | ||
120 | #endif | ||
121 | |||
122 | /* check whether the device is in power save mode */ | ||
123 | if (asleep) { | ||
124 | /* device is in powersave, trigger the device for wakeup */ | ||
125 | #if VERBOSE > SHOW_ERROR_MESSAGES | ||
126 | do_gettimeofday(¤t_time); | ||
127 | DEBUG(SHOW_TRACING, "%08li.%08li Device wakeup triggered\n", | ||
128 | current_time.tv_sec, (long)current_time.tv_usec); | ||
129 | #endif | ||
130 | |||
131 | DEBUG(SHOW_TRACING, "%08li.%08li Device register read %08x\n", | ||
132 | current_time.tv_sec, (long)current_time.tv_usec, | ||
133 | readl(device_base + ISL38XX_CTRL_STAT_REG)); | ||
134 | udelay(ISL38XX_WRITEIO_DELAY); | ||
135 | |||
136 | reg = readl(device_base + ISL38XX_INT_IDENT_REG); | ||
137 | if (reg == 0xabadface) { | ||
138 | #if VERBOSE > SHOW_ERROR_MESSAGES | ||
139 | do_gettimeofday(¤t_time); | ||
140 | DEBUG(SHOW_TRACING, | ||
141 | "%08li.%08li Device register abadface\n", | ||
142 | current_time.tv_sec, (long)current_time.tv_usec); | ||
143 | #endif | ||
144 | /* read the Device Status Register until Sleepmode bit is set */ | ||
145 | while (reg = readl(device_base + ISL38XX_CTRL_STAT_REG), | ||
146 | (reg & ISL38XX_CTRL_STAT_SLEEPMODE) == 0) { | ||
147 | udelay(ISL38XX_WRITEIO_DELAY); | ||
148 | counter++; | ||
149 | } | ||
150 | |||
151 | DEBUG(SHOW_TRACING, | ||
152 | "%08li.%08li Device register read %08x\n", | ||
153 | current_time.tv_sec, (long)current_time.tv_usec, | ||
154 | readl(device_base + ISL38XX_CTRL_STAT_REG)); | ||
155 | udelay(ISL38XX_WRITEIO_DELAY); | ||
156 | |||
157 | #if VERBOSE > SHOW_ERROR_MESSAGES | ||
158 | do_gettimeofday(¤t_time); | ||
159 | DEBUG(SHOW_TRACING, | ||
160 | "%08li.%08li Device asleep counter %i\n", | ||
161 | current_time.tv_sec, (long)current_time.tv_usec, | ||
162 | counter); | ||
163 | #endif | ||
164 | } | ||
165 | /* assert the Wakeup interrupt in the Device Interrupt Register */ | ||
166 | isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_WAKEUP, | ||
167 | ISL38XX_DEV_INT_REG); | ||
168 | udelay(ISL38XX_WRITEIO_DELAY); | ||
169 | |||
170 | /* perform another read on the Device Status Register */ | ||
171 | reg = readl(device_base + ISL38XX_CTRL_STAT_REG); | ||
172 | udelay(ISL38XX_WRITEIO_DELAY); | ||
173 | |||
174 | #if VERBOSE > SHOW_ERROR_MESSAGES | ||
175 | do_gettimeofday(¤t_time); | ||
176 | DEBUG(SHOW_TRACING, "%08li.%08li Device register read %08x\n", | ||
177 | current_time.tv_sec, (long)current_time.tv_usec, reg); | ||
178 | #endif | ||
179 | } else { | ||
180 | /* device is (still) awake */ | ||
181 | #if VERBOSE > SHOW_ERROR_MESSAGES | ||
182 | DEBUG(SHOW_TRACING, "Device is in active state\n"); | ||
183 | #endif | ||
184 | /* trigger the device by setting the Update bit in the Device Int reg */ | ||
185 | |||
186 | isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_UPDATE, | ||
187 | ISL38XX_DEV_INT_REG); | ||
188 | udelay(ISL38XX_WRITEIO_DELAY); | ||
189 | } | ||
190 | } | ||
191 | |||
192 | void | ||
193 | isl38xx_interface_reset(void __iomem *device_base, dma_addr_t host_address) | ||
194 | { | ||
195 | #if VERBOSE > SHOW_ERROR_MESSAGES | ||
196 | DEBUG(SHOW_FUNCTION_CALLS, "isl38xx_interface_reset\n"); | ||
197 | #endif | ||
198 | |||
199 | /* load the address of the control block in the device */ | ||
200 | isl38xx_w32_flush(device_base, host_address, ISL38XX_CTRL_BLK_BASE_REG); | ||
201 | udelay(ISL38XX_WRITEIO_DELAY); | ||
202 | |||
203 | /* set the reset bit in the Device Interrupt Register */ | ||
204 | isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_RESET, ISL38XX_DEV_INT_REG); | ||
205 | udelay(ISL38XX_WRITEIO_DELAY); | ||
206 | |||
207 | /* enable the interrupt for detecting initialization */ | ||
208 | |||
209 | /* Note: Do not enable other interrupts here. We want the | ||
210 | * device to have come up first 100% before allowing any other | ||
211 | * interrupts. */ | ||
212 | isl38xx_w32_flush(device_base, ISL38XX_INT_IDENT_INIT, ISL38XX_INT_EN_REG); | ||
213 | udelay(ISL38XX_WRITEIO_DELAY); /* allow complete full reset */ | ||
214 | } | ||
215 | |||
216 | void | ||
217 | isl38xx_enable_common_interrupts(void __iomem *device_base) { | ||
218 | u32 reg; | ||
219 | reg = ( ISL38XX_INT_IDENT_UPDATE | | ||
220 | ISL38XX_INT_IDENT_SLEEP | ISL38XX_INT_IDENT_WAKEUP); | ||
221 | isl38xx_w32_flush(device_base, reg, ISL38XX_INT_EN_REG); | ||
222 | udelay(ISL38XX_WRITEIO_DELAY); | ||
223 | } | ||
224 | |||
225 | int | ||
226 | isl38xx_in_queue(isl38xx_control_block *cb, int queue) | ||
227 | { | ||
228 | const s32 delta = (le32_to_cpu(cb->driver_curr_frag[queue]) - | ||
229 | le32_to_cpu(cb->device_curr_frag[queue])); | ||
230 | |||
231 | /* determine the amount of fragments in the queue depending on the type | ||
232 | * of the queue, either transmit or receive */ | ||
233 | |||
234 | BUG_ON(delta < 0); /* driver ptr must be ahead of device ptr */ | ||
235 | |||
236 | switch (queue) { | ||
237 | /* send queues */ | ||
238 | case ISL38XX_CB_TX_MGMTQ: | ||
239 | BUG_ON(delta > ISL38XX_CB_MGMT_QSIZE); | ||
240 | case ISL38XX_CB_TX_DATA_LQ: | ||
241 | case ISL38XX_CB_TX_DATA_HQ: | ||
242 | BUG_ON(delta > ISL38XX_CB_TX_QSIZE); | ||
243 | return delta; | ||
244 | break; | ||
245 | |||
246 | /* receive queues */ | ||
247 | case ISL38XX_CB_RX_MGMTQ: | ||
248 | BUG_ON(delta > ISL38XX_CB_MGMT_QSIZE); | ||
249 | return ISL38XX_CB_MGMT_QSIZE - delta; | ||
250 | break; | ||
251 | |||
252 | case ISL38XX_CB_RX_DATA_LQ: | ||
253 | case ISL38XX_CB_RX_DATA_HQ: | ||
254 | BUG_ON(delta > ISL38XX_CB_RX_QSIZE); | ||
255 | return ISL38XX_CB_RX_QSIZE - delta; | ||
256 | break; | ||
257 | } | ||
258 | BUG(); | ||
259 | return 0; | ||
260 | } | ||