1/*====================================================================== 2 3 A driver for the Qlogic SCSI card 4 5 qlogic_cs.c 1.83 2001/10/13 00:08:53 6 7 The contents of this file are subject to the Mozilla Public 8 License Version 1.1 (the "License"); you may not use this file 9 except in compliance with the License. You may obtain a copy of 10 the License at http://www.mozilla.org/MPL/ 11 12 Software distributed under the License is distributed on an "AS 13 IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 14 implied. See the License for the specific language governing 15 rights and limitations under the License. 16 17 The initial developer of the original code is David A. Hinds 18 <dahinds@users.sourceforge.net>. Portions created by David A. Hinds 19 are Copyright (C) 1999 David A. Hinds. All Rights Reserved. 20 21 Alternatively, the contents of this file may be used under the 22 terms of the GNU General Public License version 2 (the "GPL"), in 23 which case the provisions of the GPL are applicable instead of the 24 above. If you wish to allow the use of your version of this file 25 only under the terms of the GPL and not to allow others to use 26 your version of this file under the MPL, indicate your decision 27 by deleting the provisions above and replace them with the notice 28 and other provisions required by the GPL. If you do not delete 29 the provisions above, a recipient may use your version of this 30 file under either the MPL or the GPL. 31 32======================================================================*/ 33 34#include <linux/module.h> 35#include <linux/init.h> 36#include <linux/kernel.h> 37#include <linux/sched.h> 38#include <linux/slab.h> 39#include <linux/string.h> 40#include <linux/timer.h> 41#include <linux/ioport.h> 42#include <asm/io.h> 43#include <asm/byteorder.h> 44#include <scsi/scsi.h> 45#include <linux/major.h> 46#include <linux/blk.h> 47 48#include <../drivers/scsi/scsi.h> 49#include <../drivers/scsi/hosts.h> 50#include <scsi/scsi_ioctl.h> 51 52#include <../drivers/scsi/qlogicfas.h> 53 54#define qlogic_reset(h) qlogicfas_reset(h, 0) 55 56#include <pcmcia/version.h> 57#include <pcmcia/cs_types.h> 58#include <pcmcia/cs.h> 59#include <pcmcia/cistpl.h> 60#include <pcmcia/ds.h> 61#include <pcmcia/ciscode.h> 62 63extern void qlogicfas_preset(int port, int irq); 64 65/*====================================================================*/ 66 67/* Module parameters */ 68 69MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>"); 70MODULE_DESCRIPTION("Qlogic PCMCIA SCSI driver"); 71MODULE_LICENSE("Dual MPL/GPL"); 72 73#define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i") 74 75/* Bit map of interrupts to choose from */ 76INT_MODULE_PARM(irq_mask, 0xdeb8); 77static int irq_list[4] = { -1 }; 78MODULE_PARM(irq_list, "1-4i"); 79 80#ifdef PCMCIA_DEBUG 81INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG); 82#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) 83static char *version = 84"qlogic_cs.c 1.83 2001/10/13 00:08:53 (David Hinds)"; 85#else 86#define DEBUG(n, args...) 87#endif 88 89/*====================================================================*/ 90 91typedef struct scsi_info_t { 92 dev_link_t link; 93 u_short manf_id; 94 int ndev; 95 dev_node_t node[8]; 96} scsi_info_t; 97 98static void qlogic_release(u_long arg); 99static int qlogic_event(event_t event, int priority, 100 event_callback_args_t *args); 101 102static dev_link_t *qlogic_attach(void); 103static void qlogic_detach(dev_link_t *); 104 105static Scsi_Host_Template driver_template = QLOGICFAS; 106 107static dev_link_t *dev_list = NULL; 108 109static dev_info_t dev_info = "qlogic_cs"; 110 111/*====================================================================*/ 112 113static void cs_error(client_handle_t handle, int func, int ret) 114{ 115 error_info_t err = { func, ret }; 116 CardServices(ReportError, handle, &err); 117} 118 119/*====================================================================*/ 120 121static dev_link_t *qlogic_attach(void) 122{ 123 scsi_info_t *info; 124 client_reg_t client_reg; 125 dev_link_t *link; 126 int i, ret; 127 128 DEBUG(0, "qlogic_attach()\n"); 129 130 /* Create new SCSI device */ 131 info = kmalloc(sizeof(*info), GFP_KERNEL); 132 if (!info) return NULL; 133 memset(info, 0, sizeof(*info)); 134 link = &info->link; link->priv = info; 135 link->release.function = &qlogic_release; 136 link->release.data = (u_long)link; 137 138 link->io.NumPorts1 = 16; 139 link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; 140 link->io.IOAddrLines = 10; 141 link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; 142 link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID; 143 if (irq_list[0] == -1) 144 link->irq.IRQInfo2 = irq_mask; 145 else 146 for (i = 0; i < 4; i++) 147 link->irq.IRQInfo2 |= 1 << irq_list[i]; 148 link->conf.Attributes = CONF_ENABLE_IRQ; 149 link->conf.Vcc = 50; 150 link->conf.IntType = INT_MEMORY_AND_IO; 151 link->conf.Present = PRESENT_OPTION; 152 153 /* Register with Card Services */ 154 link->next = dev_list; 155 dev_list = link; 156 client_reg.dev_info = &dev_info; 157 client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; 158 client_reg.event_handler = &qlogic_event; 159 client_reg.EventMask = 160 CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET | 161 CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | 162 CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; 163 client_reg.Version = 0x0210; 164 client_reg.event_callback_args.client_data = link; 165 ret = CardServices(RegisterClient, &link->handle, &client_reg); 166 if (ret != 0) { 167 cs_error(link->handle, RegisterClient, ret); 168 qlogic_detach(link); 169 return NULL; 170 } 171 172 return link; 173} /* qlogic_attach */ 174 175/*====================================================================*/ 176 177static void qlogic_detach(dev_link_t *link) 178{ 179 dev_link_t **linkp; 180 181 DEBUG(0, "qlogic_detach(0x%p)\n", link); 182 183 /* Locate device structure */ 184 for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) 185 if (*linkp == link) break; 186 if (*linkp == NULL) 187 return; 188 189 del_timer(&link->release); 190 if (link->state & DEV_CONFIG) { 191 qlogic_release((u_long)link); 192 if (link->state & DEV_STALE_CONFIG) { 193 link->state |= DEV_STALE_LINK; 194 return; 195 } 196 } 197 198 if (link->handle) 199 CardServices(DeregisterClient, link->handle); 200 201 /* Unlink device structure, free bits */ 202 *linkp = link->next; 203 kfree(link->priv); 204 205} /* qlogic_detach */ 206 207/*====================================================================*/ 208 209#define CS_CHECK(fn, args...) \ 210while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed 211 212#define CFG_CHECK(fn, args...) \ 213if (CardServices(fn, args) != 0) goto next_entry 214 215static void qlogic_config(dev_link_t *link) 216{ 217 client_handle_t handle = link->handle; 218 scsi_info_t *info = link->priv; 219 tuple_t tuple; 220 cisparse_t parse; 221 int i, last_ret, last_fn; 222 u_short tuple_data[32]; 223 Scsi_Device *dev; 224 dev_node_t **tail, *node; 225 struct Scsi_Host *host; 226 227 DEBUG(0, "qlogic_config(0x%p)\n", link); 228 229 tuple.TupleData = (cisdata_t *)tuple_data; 230 tuple.TupleDataMax = 64; 231 tuple.TupleOffset = 0; 232 tuple.DesiredTuple = CISTPL_CONFIG; 233 CS_CHECK(GetFirstTuple, handle, &tuple); 234 CS_CHECK(GetTupleData, handle, &tuple); 235 CS_CHECK(ParseTuple, handle, &tuple, &parse); 236 link->conf.ConfigBase = parse.config.base; 237 238 tuple.DesiredTuple = CISTPL_MANFID; 239 if ((CardServices(GetFirstTuple, handle, &tuple) == CS_SUCCESS) && 240 (CardServices(GetTupleData, handle, &tuple) == CS_SUCCESS)) 241 info->manf_id = le16_to_cpu(tuple.TupleData[0]); 242 243 /* Configure card */ 244 driver_template.module = &__this_module; 245 link->state |= DEV_CONFIG; 246 247 tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; 248 CS_CHECK(GetFirstTuple, handle, &tuple); 249 while (1) { 250 CFG_CHECK(GetTupleData, handle, &tuple); 251 CFG_CHECK(ParseTuple, handle, &tuple, &parse); 252 link->conf.ConfigIndex = parse.cftable_entry.index; 253 link->io.BasePort1 = parse.cftable_entry.io.win[0].base; 254 link->io.NumPorts1 = parse.cftable_entry.io.win[0].len; 255 if (link->io.BasePort1 != 0) { 256 i = CardServices(RequestIO, handle, &link->io); 257 if (i == CS_SUCCESS) break; 258 } 259 next_entry: 260 CS_CHECK(GetNextTuple, handle, &tuple); 261 } 262 263 CS_CHECK(RequestIRQ, handle, &link->irq); 264 CS_CHECK(RequestConfiguration, handle, &link->conf); 265 266 if ((info->manf_id == MANFID_MACNICA) || 267 (info->manf_id == MANFID_PIONEER) || 268 (info->manf_id == 0x0098)) { 269 /* set ATAcmd */ 270 outb(0xb4, link->io.BasePort1+0xd); 271 outb(0x24, link->io.BasePort1+0x9); 272 outb(0x04, link->io.BasePort1+0xd); 273 } 274 275 /* A bad hack... */ 276 release_region(link->io.BasePort1, link->io.NumPorts1); 277 278 /* The KXL-810AN has a bigger IO port window */ 279 if (link->io.NumPorts1 == 32) 280 qlogicfas_preset(link->io.BasePort1+16, link->irq.AssignedIRQ); 281 else 282 qlogicfas_preset(link->io.BasePort1, link->irq.AssignedIRQ); 283 284 scsi_register_module(MODULE_SCSI_HA, &driver_template); 285 286 tail = &link->dev; 287 info->ndev = 0; 288 for (host = scsi_hostlist; host; host = host->next) 289 if (host->hostt == &driver_template) 290 for (dev = host->host_queue; dev; dev = dev->next) { 291 u_long arg[2], id; 292 kernel_scsi_ioctl(dev, SCSI_IOCTL_GET_IDLUN, arg); 293 id = (arg[0]&0x0f) + ((arg[0]>>4)&0xf0) + 294 ((arg[0]>>8)&0xf00) + ((arg[0]>>12)&0xf000); 295 node = &info->node[info->ndev]; 296 node->minor = 0; 297 switch (dev->type) { 298 case TYPE_TAPE: 299 node->major = SCSI_TAPE_MAJOR; 300 sprintf(node->dev_name, "st#%04lx", id); 301 break; 302 case TYPE_DISK: 303 case TYPE_MOD: 304 node->major = SCSI_DISK0_MAJOR; 305 sprintf(node->dev_name, "sd#%04lx", id); 306 break; 307 case TYPE_ROM: 308 case TYPE_WORM: 309 node->major = SCSI_CDROM_MAJOR; 310 sprintf(node->dev_name, "sr#%04lx", id); 311 break; 312 default: 313 node->major = SCSI_GENERIC_MAJOR; 314 sprintf(node->dev_name, "sg#%04lx", id); 315 break; 316 } 317 *tail = node; tail = &node->next; 318 info->ndev++; 319 } 320 *tail = NULL; 321 if (info->ndev == 0) 322 printk(KERN_INFO "qlogic_cs: no SCSI devices found\n"); 323 324 link->state &= ~DEV_CONFIG_PENDING; 325 return; 326 327cs_failed: 328 cs_error(link->handle, last_fn, last_ret); 329 qlogic_release((u_long)link); 330 return; 331 332} /* qlogic_config */ 333 334/*====================================================================*/ 335 336static void qlogic_release(u_long arg) 337{ 338 dev_link_t *link = (dev_link_t *)arg; 339 340 DEBUG(0, "qlogic_release(0x%p)\n", link); 341 342 if (GET_USE_COUNT(&__this_module) != 0) { 343 DEBUG(0, "qlogic_cs: release postponed, device still open\n"); 344 link->state |= DEV_STALE_CONFIG; 345 return; 346 } 347 348 scsi_unregister_module(MODULE_SCSI_HA, &driver_template); 349 link->dev = NULL; 350 351 CardServices(ReleaseConfiguration, link->handle); 352 CardServices(ReleaseIO, link->handle, &link->io); 353 CardServices(ReleaseIRQ, link->handle, &link->irq); 354 355 link->state &= ~DEV_CONFIG; 356 if (link->state & DEV_STALE_LINK) 357 qlogic_detach(link); 358 359} /* qlogic_release */ 360 361/*====================================================================*/ 362 363static int qlogic_event(event_t event, int priority, 364 event_callback_args_t *args) 365{ 366 dev_link_t *link = args->client_data; 367 368 DEBUG(1, "qlogic_event(0x%06x)\n", event); 369 370 switch (event) { 371 case CS_EVENT_CARD_REMOVAL: 372 link->state &= ~DEV_PRESENT; 373 if (link->state & DEV_CONFIG) 374 mod_timer(&link->release, jiffies + HZ/20); 375 break; 376 case CS_EVENT_CARD_INSERTION: 377 link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; 378 qlogic_config(link); 379 break; 380 case CS_EVENT_PM_SUSPEND: 381 link->state |= DEV_SUSPEND; 382 /* Fall through... */ 383 case CS_EVENT_RESET_PHYSICAL: 384 if (link->state & DEV_CONFIG) 385 CardServices(ReleaseConfiguration, link->handle); 386 break; 387 case CS_EVENT_PM_RESUME: 388 link->state &= ~DEV_SUSPEND; 389 /* Fall through... */ 390 case CS_EVENT_CARD_RESET: 391 if (link->state & DEV_CONFIG) { 392 scsi_info_t *info = link->priv; 393 CardServices(RequestConfiguration, link->handle, &link->conf); 394 if ((info->manf_id == MANFID_MACNICA) || 395 (info->manf_id == MANFID_PIONEER) || 396 (info->manf_id == 0x0098)) { 397 outb( 0x80, link->io.BasePort1+0xd); 398 outb( 0x24, link->io.BasePort1+0x9); 399 outb( 0x04, link->io.BasePort1+0xd); 400 } 401 qlogic_reset(NULL); 402 } 403 break; 404 } 405 return 0; 406} /* qlogic_event */ 407 408/*====================================================================*/ 409 410static int __init init_qlogic_cs(void) { 411 servinfo_t serv; 412 DEBUG(0, "%s\n", version); 413 CardServices(GetCardServicesInfo, &serv); 414 if (serv.Revision != CS_RELEASE_CODE) { 415 printk(KERN_NOTICE "qlogic_cs: Card Services release " 416 "does not match!\n"); 417 return -1; 418 } 419 register_pccard_driver(&dev_info, &qlogic_attach, &qlogic_detach); 420 return 0; 421} 422 423static void __exit exit_qlogic_cs(void) { 424 DEBUG(0, "qlogic_cs: unloading\n"); 425 unregister_pccard_driver(&dev_info); 426 while (dev_list != NULL) 427 qlogic_detach(dev_list); 428} 429 430module_init(init_qlogic_cs); 431module_exit(exit_qlogic_cs); 432