1 /*====================================================================== 2 3 A driver for the Qlogic SCSI card 4 5 qlogic_cs.c 1.79 2000/06/12 21:27:26 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 which 23 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/ioport.h> 41 #include <asm/io.h> 42 #include <scsi/scsi.h> 43 #include <linux/major.h> 44 #include <linux/blkdev.h> 45 #include <scsi/scsi_ioctl.h> 46 #include <linux/interrupt.h> 47 48 #include "scsi.h" 49 #include <scsi/scsi_host.h> 50 #include "../qlogicfas408.h" 51 52 #include <pcmcia/version.h> 53 #include <pcmcia/cs_types.h> 54 #include <pcmcia/cs.h> 55 #include <pcmcia/cistpl.h> 56 #include <pcmcia/ds.h> 57 #include <pcmcia/ciscode.h> 58 59 /* Set the following to 2 to use normal interrupt (active high/totempole- 60 * tristate), otherwise use 0 (REQUIRED FOR PCMCIA) for active low, open 61 * drain 62 */ 63 #define INT_TYPE 0 64 65 static char qlogic_name[] = "qlogic_cs"; 66 67 #ifdef PCMCIA_DEBUG 68 static int pc_debug = PCMCIA_DEBUG; 69 module_param(pc_debug, int, 0644); 70 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) 71 static char *version = "qlogic_cs.c 1.79-ac 2002/10/26 (David Hinds)"; 72 #else 73 #define DEBUG(n, args...) 74 #endif 75 76 static Scsi_Host_Template qlogicfas_driver_template = { 77 .module = THIS_MODULE, 78 .name = qlogic_name, 79 .proc_name = qlogic_name, 80 .info = qlogicfas408_info, 81 .queuecommand = qlogicfas408_queuecommand, 82 .eh_abort_handler = qlogicfas408_abort, 83 .eh_bus_reset_handler = qlogicfas408_bus_reset, 84 .bios_param = qlogicfas408_biosparam, 85 .can_queue = 1, 86 .this_id = -1, 87 .sg_tablesize = SG_ALL, 88 .cmd_per_lun = 1, 89 .use_clustering = DISABLE_CLUSTERING, 90 }; 91 92 /*====================================================================*/ 93 94 typedef struct scsi_info_t { 95 dev_link_t link; 96 dev_node_t node; 97 struct Scsi_Host *host; 98 unsigned short manf_id; 99 } scsi_info_t; 100 101 static void qlogic_release(dev_link_t *link); 102 static int qlogic_event(event_t event, int priority, event_callback_args_t * args); 103 104 static dev_link_t *qlogic_attach(void); 105 static void qlogic_detach(dev_link_t *); 106 107 108 static dev_link_t *dev_list = NULL; 109 110 static dev_info_t dev_info = "qlogic_cs"; 111 112 static struct Scsi_Host *qlogic_detect(Scsi_Host_Template *host, 113 dev_link_t *link, int qbase, int qlirq) 114 { 115 int qltyp; /* type of chip */ 116 int qinitid; 117 struct Scsi_Host *shost; /* registered host structure */ 118 struct qlogicfas408_priv *priv; 119 120 qltyp = qlogicfas408_get_chip_type(qbase, INT_TYPE); 121 qinitid = host->this_id; 122 if (qinitid < 0) 123 qinitid = 7; /* if no ID, use 7 */ 124 125 qlogicfas408_setup(qbase, qinitid, INT_TYPE); 126 127 host->name = qlogic_name; 128 shost = scsi_host_alloc(host, sizeof(struct qlogicfas408_priv)); 129 if (!shost) 130 goto err; 131 shost->io_port = qbase; 132 shost->n_io_port = 16; 133 shost->dma_channel = -1; 134 if (qlirq != -1) 135 shost->irq = qlirq; 136 137 priv = get_priv_by_host(shost); 138 priv->qlirq = qlirq; 139 priv->qbase = qbase; 140 priv->qinitid = qinitid; 141 priv->shost = shost; 142 priv->int_type = INT_TYPE; 143 144 if (request_irq(qlirq, qlogicfas408_ihandl, 0, qlogic_name, shost)) 145 goto free_scsi_host; 146 147 sprintf(priv->qinfo, 148 "Qlogicfas Driver version 0.46, chip %02X at %03X, IRQ %d, TPdma:%d", 149 qltyp, qbase, qlirq, QL_TURBO_PDMA); 150 151 if (scsi_add_host(shost, NULL)) 152 goto free_interrupt; 153 154 scsi_scan_host(shost); 155 156 return shost; 157 158 free_interrupt: 159 free_irq(qlirq, shost); 160 161 free_scsi_host: 162 scsi_host_put(shost); 163 164 err: 165 return NULL; 166 } 167 static dev_link_t *qlogic_attach(void) 168 { 169 scsi_info_t *info; 170 client_reg_t client_reg; 171 dev_link_t *link; 172 int ret; 173 174 DEBUG(0, "qlogic_attach()\n"); 175 176 /* Create new SCSI device */ 177 info = kmalloc(sizeof(*info), GFP_KERNEL); 178 if (!info) 179 return NULL; 180 memset(info, 0, sizeof(*info)); 181 link = &info->link; 182 link->priv = info; 183 link->io.NumPorts1 = 16; 184 link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; 185 link->io.IOAddrLines = 10; 186 link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; 187 link->irq.IRQInfo1 = IRQ_LEVEL_ID; 188 link->conf.Attributes = CONF_ENABLE_IRQ; 189 link->conf.Vcc = 50; 190 link->conf.IntType = INT_MEMORY_AND_IO; 191 link->conf.Present = PRESENT_OPTION; 192 193 /* Register with Card Services */ 194 link->next = dev_list; 195 dev_list = link; 196 client_reg.dev_info = &dev_info; 197 client_reg.event_handler = &qlogic_event; 198 client_reg.EventMask = CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET | CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; 199 client_reg.Version = 0x0210; 200 client_reg.event_callback_args.client_data = link; 201 ret = pcmcia_register_client(&link->handle, &client_reg); 202 if (ret != 0) { 203 cs_error(link->handle, RegisterClient, ret); 204 qlogic_detach(link); 205 return NULL; 206 } 207 208 return link; 209 } /* qlogic_attach */ 210 211 /*====================================================================*/ 212 213 static void qlogic_detach(dev_link_t * link) 214 { 215 dev_link_t **linkp; 216 217 DEBUG(0, "qlogic_detach(0x%p)\n", link); 218 219 /* Locate device structure */ 220 for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) 221 if (*linkp == link) 222 break; 223 if (*linkp == NULL) 224 return; 225 226 if (link->state & DEV_CONFIG) 227 qlogic_release(link); 228 229 if (link->handle) 230 pcmcia_deregister_client(link->handle); 231 232 /* Unlink device structure, free bits */ 233 *linkp = link->next; 234 kfree(link->priv); 235 236 } /* qlogic_detach */ 237 238 /*====================================================================*/ 239 240 #define CS_CHECK(fn, ret) \ 241 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) 242 243 static void qlogic_config(dev_link_t * link) 244 { 245 client_handle_t handle = link->handle; 246 scsi_info_t *info = link->priv; 247 tuple_t tuple; 248 cisparse_t parse; 249 int i, last_ret, last_fn; 250 unsigned short tuple_data[32]; 251 struct Scsi_Host *host; 252 253 DEBUG(0, "qlogic_config(0x%p)\n", link); 254 255 tuple.TupleData = (cisdata_t *) tuple_data; 256 tuple.TupleDataMax = 64; 257 tuple.TupleOffset = 0; 258 tuple.DesiredTuple = CISTPL_CONFIG; 259 CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); 260 CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); 261 CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); 262 link->conf.ConfigBase = parse.config.base; 263 264 tuple.DesiredTuple = CISTPL_MANFID; 265 if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) && (pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS)) 266 info->manf_id = le16_to_cpu(tuple.TupleData[0]); 267 268 /* Configure card */ 269 link->state |= DEV_CONFIG; 270 271 tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; 272 CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); 273 while (1) { 274 if (pcmcia_get_tuple_data(handle, &tuple) != 0 || 275 pcmcia_parse_tuple(handle, &tuple, &parse) != 0) 276 goto next_entry; 277 link->conf.ConfigIndex = parse.cftable_entry.index; 278 link->io.BasePort1 = parse.cftable_entry.io.win[0].base; 279 link->io.NumPorts1 = parse.cftable_entry.io.win[0].len; 280 if (link->io.BasePort1 != 0) { 281 i = pcmcia_request_io(handle, &link->io); 282 if (i == CS_SUCCESS) 283 break; 284 } 285 next_entry: 286 CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple)); 287 } 288 289 CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq)); 290 CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf)); 291 292 if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) { 293 /* set ATAcmd */ 294 outb(0xb4, link->io.BasePort1 + 0xd); 295 outb(0x24, link->io.BasePort1 + 0x9); 296 outb(0x04, link->io.BasePort1 + 0xd); 297 } 298 299 /* The KXL-810AN has a bigger IO port window */ 300 if (link->io.NumPorts1 == 32) 301 host = qlogic_detect(&qlogicfas_driver_template, link, 302 link->io.BasePort1 + 16, link->irq.AssignedIRQ); 303 else 304 host = qlogic_detect(&qlogicfas_driver_template, link, 305 link->io.BasePort1, link->irq.AssignedIRQ); 306 307 if (!host) { 308 printk(KERN_INFO "%s: no SCSI devices found\n", qlogic_name); 309 goto out; 310 } 311 312 sprintf(info->node.dev_name, "scsi%d", host->host_no); 313 link->dev = &info->node; 314 info->host = host; 315 316 out: 317 link->state &= ~DEV_CONFIG_PENDING; 318 return; 319 320 cs_failed: 321 cs_error(link->handle, last_fn, last_ret); 322 link->dev = NULL; 323 pcmcia_release_configuration(link->handle); 324 pcmcia_release_io(link->handle, &link->io); 325 pcmcia_release_irq(link->handle, &link->irq); 326 link->state &= ~DEV_CONFIG; 327 return; 328 329 } /* qlogic_config */ 330 331 /*====================================================================*/ 332 333 static void qlogic_release(dev_link_t *link) 334 { 335 scsi_info_t *info = link->priv; 336 337 DEBUG(0, "qlogic_release(0x%p)\n", link); 338 339 scsi_remove_host(info->host); 340 link->dev = NULL; 341 342 free_irq(link->irq.AssignedIRQ, info->host); 343 344 pcmcia_release_configuration(link->handle); 345 pcmcia_release_io(link->handle, &link->io); 346 pcmcia_release_irq(link->handle, &link->irq); 347 348 scsi_host_put(info->host); 349 350 link->state &= ~DEV_CONFIG; 351 } 352 353 /*====================================================================*/ 354 355 static int qlogic_event(event_t event, int priority, event_callback_args_t * args) 356 { 357 dev_link_t *link = args->client_data; 358 359 DEBUG(1, "qlogic_event(0x%06x)\n", event); 360 361 switch (event) { 362 case CS_EVENT_CARD_REMOVAL: 363 link->state &= ~DEV_PRESENT; 364 if (link->state & DEV_CONFIG) 365 qlogic_release(link); 366 break; 367 case CS_EVENT_CARD_INSERTION: 368 link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; 369 qlogic_config(link); 370 break; 371 case CS_EVENT_PM_SUSPEND: 372 link->state |= DEV_SUSPEND; 373 /* Fall through... */ 374 case CS_EVENT_RESET_PHYSICAL: 375 if (link->state & DEV_CONFIG) 376 pcmcia_release_configuration(link->handle); 377 break; 378 case CS_EVENT_PM_RESUME: 379 link->state &= ~DEV_SUSPEND; 380 /* Fall through... */ 381 case CS_EVENT_CARD_RESET: 382 if (link->state & DEV_CONFIG) { 383 scsi_info_t *info = link->priv; 384 pcmcia_request_configuration(link->handle, &link->conf); 385 if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) { 386 outb(0x80, link->io.BasePort1 + 0xd); 387 outb(0x24, link->io.BasePort1 + 0x9); 388 outb(0x04, link->io.BasePort1 + 0xd); 389 } 390 /* Ugggglllyyyy!!! */ 391 qlogicfas408_bus_reset(NULL); 392 } 393 break; 394 } 395 return 0; 396 } /* qlogic_event */ 397 398 static struct pcmcia_device_id qlogic_ids[] = { 399 PCMCIA_DEVICE_PROD_ID12("EIger Labs", "PCMCIA-to-SCSI Adapter", 0x88395fa7, 0x33b7a5e6), 400 PCMCIA_DEVICE_PROD_ID12("EPSON", "SCSI-2 PC Card SC200", 0xd361772f, 0x299d1751), 401 PCMCIA_DEVICE_PROD_ID12("MACNICA", "MIRACLE SCSI-II mPS110", 0x20841b68, 0xab3c3b6d), 402 PCMCIA_DEVICE_PROD_ID12("MIDORI ELECTRONICS ", "CN-SC43", 0x6534382a, 0xd67eee79), 403 PCMCIA_DEVICE_PROD_ID12("NEC", "PC-9801N-J03R", 0x18df0ba0, 0x24662e8a), 404 PCMCIA_DEVICE_PROD_ID12("KME ", "KXLC003", 0x82375a27, 0xf68e5bf7), 405 PCMCIA_DEVICE_PROD_ID12("KME ", "KXLC004", 0x82375a27, 0x68eace54), 406 PCMCIA_DEVICE_PROD_ID12("KME", "KXLC101", 0x3faee676, 0x194250ec), 407 PCMCIA_DEVICE_PROD_ID12("QLOGIC CORPORATION", "pc05", 0xd77b2930, 0xa85b2735), 408 PCMCIA_DEVICE_PROD_ID12("QLOGIC CORPORATION", "pc05 rev 1.10", 0xd77b2930, 0x70f8b5f8), 409 PCMCIA_DEVICE_PROD_ID123("KME", "KXLC002", "00", 0x3faee676, 0x81896b61, 0xf99f065f), 410 PCMCIA_DEVICE_PROD_ID12("RATOC System Inc.", "SCSI2 CARD 37", 0x85c10e17, 0x1a2640c1), 411 PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "SCSC200A PC CARD SCSI", 0xb4585a1a, 0xa6f06ebe), 412 PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "SCSC200B PC CARD SCSI-10", 0xb4585a1a, 0x0a88dea0), 413 /* these conflict with other cards! */ 414 /* PCMCIA_DEVICE_PROD_ID123("MACNICA", "MIRACLE SCSI", "mPS100", 0x20841b68, 0xf8dedaeb, 0x89f7fafb), */ 415 /* PCMCIA_DEVICE_PROD_ID123("MACNICA", "MIRACLE SCSI", "mPS100", 0x20841b68, 0xf8dedaeb, 0x89f7fafb), */ 416 PCMCIA_DEVICE_NULL, 417 }; 418 MODULE_DEVICE_TABLE(pcmcia, qlogic_ids); 419 420 static struct pcmcia_driver qlogic_cs_driver = { 421 .owner = THIS_MODULE, 422 .drv = { 423 .name = "qlogic_cs", 424 }, 425 .attach = qlogic_attach, 426 .detach = qlogic_detach, 427 .id_table = qlogic_ids, 428 }; 429 430 static int __init init_qlogic_cs(void) 431 { 432 return pcmcia_register_driver(&qlogic_cs_driver); 433 } 434 435 static void __exit exit_qlogic_cs(void) 436 { 437 pcmcia_unregister_driver(&qlogic_cs_driver); 438 BUG_ON(dev_list != NULL); 439 } 440 441 MODULE_AUTHOR("Tom Zerucha, Michael Griffith"); 442 MODULE_DESCRIPTION("Driver for the PCMCIA Qlogic FAS SCSI controllers"); 443 MODULE_LICENSE("GPL"); 444 module_init(init_qlogic_cs); 445 module_exit(exit_qlogic_cs); 446