Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  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         .eh_device_reset_handler= qlogicfas408_device_reset,
 85         .eh_host_reset_handler  = qlogicfas408_host_reset,
 86         .bios_param             = qlogicfas408_biosparam,
 87         .can_queue              = 1,
 88         .this_id                = -1,
 89         .sg_tablesize           = SG_ALL,
 90         .cmd_per_lun            = 1,
 91         .use_clustering         = DISABLE_CLUSTERING,
 92 };
 93 
 94 /*====================================================================*/
 95 
 96 typedef struct scsi_info_t {
 97         dev_link_t link;
 98         dev_node_t node;
 99         struct Scsi_Host *host;
100         unsigned short manf_id;
101 } scsi_info_t;
102 
103 static void qlogic_release(dev_link_t *link);
104 static int qlogic_event(event_t event, int priority, event_callback_args_t * args);
105 
106 static dev_link_t *qlogic_attach(void);
107 static void qlogic_detach(dev_link_t *);
108 
109 
110 static dev_link_t *dev_list = NULL;
111 
112 static dev_info_t dev_info = "qlogic_cs";
113 
114 static struct Scsi_Host *qlogic_detect(Scsi_Host_Template *host,
115                                 dev_link_t *link, int qbase, int qlirq)
116 {
117         int qltyp;              /* type of chip */
118         int qinitid;
119         struct Scsi_Host *shost;        /* registered host structure */
120         struct qlogicfas408_priv *priv;
121 
122         qltyp = qlogicfas408_get_chip_type(qbase, INT_TYPE);
123         qinitid = host->this_id;
124         if (qinitid < 0)
125                 qinitid = 7;    /* if no ID, use 7 */
126 
127         qlogicfas408_setup(qbase, qinitid, INT_TYPE);
128 
129         host->name = qlogic_name;
130         shost = scsi_host_alloc(host, sizeof(struct qlogicfas408_priv));
131         if (!shost)
132                 goto err;
133         shost->io_port = qbase;
134         shost->n_io_port = 16;
135         shost->dma_channel = -1;
136         if (qlirq != -1)
137                 shost->irq = qlirq;
138 
139         priv = get_priv_by_host(shost);
140         priv->qlirq = qlirq;
141         priv->qbase = qbase;
142         priv->qinitid = qinitid;
143         priv->shost = shost;
144         priv->int_type = INT_TYPE;                                      
145 
146         if (request_irq(qlirq, qlogicfas408_ihandl, 0, qlogic_name, shost))
147                 goto free_scsi_host;
148 
149         sprintf(priv->qinfo,
150                 "Qlogicfas Driver version 0.46, chip %02X at %03X, IRQ %d, TPdma:%d",
151                 qltyp, qbase, qlirq, QL_TURBO_PDMA);
152 
153         if (scsi_add_host(shost, NULL))
154                 goto free_interrupt;
155 
156         scsi_scan_host(shost);
157 
158         return shost;
159 
160 free_interrupt:
161         free_irq(qlirq, shost);
162 
163 free_scsi_host:
164         scsi_host_put(shost);
165         
166 err:
167         return NULL;
168 }
169 static dev_link_t *qlogic_attach(void)
170 {
171         scsi_info_t *info;
172         client_reg_t client_reg;
173         dev_link_t *link;
174         int ret;
175 
176         DEBUG(0, "qlogic_attach()\n");
177 
178         /* Create new SCSI device */
179         info = kmalloc(sizeof(*info), GFP_KERNEL);
180         if (!info)
181                 return NULL;
182         memset(info, 0, sizeof(*info));
183         link = &info->link;
184         link->priv = info;
185         link->io.NumPorts1 = 16;
186         link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
187         link->io.IOAddrLines = 10;
188         link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
189         link->irq.IRQInfo1 = IRQ_LEVEL_ID;
190         link->conf.Attributes = CONF_ENABLE_IRQ;
191         link->conf.Vcc = 50;
192         link->conf.IntType = INT_MEMORY_AND_IO;
193         link->conf.Present = PRESENT_OPTION;
194 
195         /* Register with Card Services */
196         link->next = dev_list;
197         dev_list = link;
198         client_reg.dev_info = &dev_info;
199         client_reg.event_handler = &qlogic_event;
200         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;
201         client_reg.Version = 0x0210;
202         client_reg.event_callback_args.client_data = link;
203         ret = pcmcia_register_client(&link->handle, &client_reg);
204         if (ret != 0) {
205                 cs_error(link->handle, RegisterClient, ret);
206                 qlogic_detach(link);
207                 return NULL;
208         }
209 
210         return link;
211 }                               /* qlogic_attach */
212 
213 /*====================================================================*/
214 
215 static void qlogic_detach(dev_link_t * link)
216 {
217         dev_link_t **linkp;
218 
219         DEBUG(0, "qlogic_detach(0x%p)\n", link);
220 
221         /* Locate device structure */
222         for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
223                 if (*linkp == link)
224                         break;
225         if (*linkp == NULL)
226                 return;
227 
228         if (link->state & DEV_CONFIG)
229                 qlogic_release(link);
230 
231         if (link->handle)
232                 pcmcia_deregister_client(link->handle);
233 
234         /* Unlink device structure, free bits */
235         *linkp = link->next;
236         kfree(link->priv);
237 
238 }                               /* qlogic_detach */
239 
240 /*====================================================================*/
241 
242 #define CS_CHECK(fn, ret) \
243 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
244 
245 static void qlogic_config(dev_link_t * link)
246 {
247         client_handle_t handle = link->handle;
248         scsi_info_t *info = link->priv;
249         tuple_t tuple;
250         cisparse_t parse;
251         int i, last_ret, last_fn;
252         unsigned short tuple_data[32];
253         struct Scsi_Host *host;
254 
255         DEBUG(0, "qlogic_config(0x%p)\n", link);
256 
257         tuple.TupleData = (cisdata_t *) tuple_data;
258         tuple.TupleDataMax = 64;
259         tuple.TupleOffset = 0;
260         tuple.DesiredTuple = CISTPL_CONFIG;
261         CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
262         CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
263         CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
264         link->conf.ConfigBase = parse.config.base;
265 
266         tuple.DesiredTuple = CISTPL_MANFID;
267         if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) && (pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS))
268                 info->manf_id = le16_to_cpu(tuple.TupleData[0]);
269 
270         /* Configure card */
271         link->state |= DEV_CONFIG;
272 
273         tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
274         CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
275         while (1) {
276                 if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
277                                 pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
278                         goto next_entry;
279                 link->conf.ConfigIndex = parse.cftable_entry.index;
280                 link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
281                 link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
282                 if (link->io.BasePort1 != 0) {
283                         i = pcmcia_request_io(handle, &link->io);
284                         if (i == CS_SUCCESS)
285                                 break;
286                 }
287               next_entry:
288                 CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
289         }
290 
291         CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
292         CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
293 
294         if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) {
295                 /* set ATAcmd */
296                 outb(0xb4, link->io.BasePort1 + 0xd);
297                 outb(0x24, link->io.BasePort1 + 0x9);
298                 outb(0x04, link->io.BasePort1 + 0xd);
299         }
300 
301         /* The KXL-810AN has a bigger IO port window */
302         if (link->io.NumPorts1 == 32)
303                 host = qlogic_detect(&qlogicfas_driver_template, link,
304                         link->io.BasePort1 + 16, link->irq.AssignedIRQ);
305         else
306                 host = qlogic_detect(&qlogicfas_driver_template, link,
307                         link->io.BasePort1, link->irq.AssignedIRQ);
308         
309         if (!host) {
310                 printk(KERN_INFO "%s: no SCSI devices found\n", qlogic_name);
311                 goto out;
312         }
313 
314         sprintf(info->node.dev_name, "scsi%d", host->host_no);
315         link->dev = &info->node;
316         info->host = host;
317 
318 out:
319         link->state &= ~DEV_CONFIG_PENDING;
320         return;
321 
322 cs_failed:
323         cs_error(link->handle, last_fn, last_ret);
324         link->dev = NULL;
325         pcmcia_release_configuration(link->handle);
326         pcmcia_release_io(link->handle, &link->io);
327         pcmcia_release_irq(link->handle, &link->irq);
328         link->state &= ~DEV_CONFIG;
329         return;
330 
331 }                               /* qlogic_config */
332 
333 /*====================================================================*/
334 
335 static void qlogic_release(dev_link_t *link)
336 {
337         scsi_info_t *info = link->priv;
338 
339         DEBUG(0, "qlogic_release(0x%p)\n", link);
340 
341         scsi_remove_host(info->host);
342         link->dev = NULL;
343 
344         free_irq(link->irq.AssignedIRQ, info->host);
345 
346         pcmcia_release_configuration(link->handle);
347         pcmcia_release_io(link->handle, &link->io);
348         pcmcia_release_irq(link->handle, &link->irq);
349 
350         scsi_host_put(info->host);
351 
352         link->state &= ~DEV_CONFIG;
353 }
354 
355 /*====================================================================*/
356 
357 static int qlogic_event(event_t event, int priority, event_callback_args_t * args)
358 {
359         dev_link_t *link = args->client_data;
360 
361         DEBUG(1, "qlogic_event(0x%06x)\n", event);
362 
363         switch (event) {
364         case CS_EVENT_CARD_REMOVAL:
365                 link->state &= ~DEV_PRESENT;
366                 if (link->state & DEV_CONFIG)
367                         qlogic_release(link);
368                 break;
369         case CS_EVENT_CARD_INSERTION:
370                 link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
371                 qlogic_config(link);
372                 break;
373         case CS_EVENT_PM_SUSPEND:
374                 link->state |= DEV_SUSPEND;
375                 /* Fall through... */
376         case CS_EVENT_RESET_PHYSICAL:
377                 if (link->state & DEV_CONFIG)
378                         pcmcia_release_configuration(link->handle);
379                 break;
380         case CS_EVENT_PM_RESUME:
381                 link->state &= ~DEV_SUSPEND;
382                 /* Fall through... */
383         case CS_EVENT_CARD_RESET:
384                 if (link->state & DEV_CONFIG) {
385                         scsi_info_t *info = link->priv;
386                         pcmcia_request_configuration(link->handle, &link->conf);
387                         if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) {
388                                 outb(0x80, link->io.BasePort1 + 0xd);
389                                 outb(0x24, link->io.BasePort1 + 0x9);
390                                 outb(0x04, link->io.BasePort1 + 0xd);
391                         }
392                         /* Ugggglllyyyy!!! */
393                         qlogicfas408_bus_reset(NULL);
394                 }
395                 break;
396         }
397         return 0;
398 }                               /* qlogic_event */
399 
400 
401 static struct pcmcia_driver qlogic_cs_driver = {
402         .owner          = THIS_MODULE,
403         .drv            = {
404         .name           = "qlogic_cs",
405         },
406         .attach         = qlogic_attach,
407         .detach         = qlogic_detach,
408 };
409 
410 static int __init init_qlogic_cs(void)
411 {
412         return pcmcia_register_driver(&qlogic_cs_driver);
413 }
414 
415 static void __exit exit_qlogic_cs(void)
416 {
417         pcmcia_unregister_driver(&qlogic_cs_driver);
418         BUG_ON(dev_list != NULL);
419 }
420 
421 MODULE_AUTHOR("Tom Zerucha, Michael Griffith");
422 MODULE_DESCRIPTION("Driver for the PCMCIA Qlogic FAS SCSI controllers");
423 MODULE_LICENSE("GPL");
424 module_init(init_qlogic_cs);
425 module_exit(exit_qlogic_cs);
426 
  This page was automatically generated by the LXR engine.