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