Merge branch 'master'
[linux-2.6] / drivers / scsi / pcmcia / fdomain_stub.c
1 /*======================================================================
2
3     A driver for Future Domain-compatible PCMCIA SCSI cards
4
5     fdomain_cs.c 1.47 2001/10/13 00:08:52
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/ioport.h>
41 #include <scsi/scsi.h>
42 #include <linux/major.h>
43 #include <linux/blkdev.h>
44 #include <scsi/scsi_ioctl.h>
45
46 #include "scsi.h"
47 #include <scsi/scsi_host.h>
48 #include "fdomain.h"
49
50 #include <pcmcia/cs_types.h>
51 #include <pcmcia/cs.h>
52 #include <pcmcia/cistpl.h>
53 #include <pcmcia/ds.h>
54
55 /*====================================================================*/
56
57 /* Module parameters */
58
59 MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
60 MODULE_DESCRIPTION("Future Domain PCMCIA SCSI driver");
61 MODULE_LICENSE("Dual MPL/GPL");
62
63 #ifdef PCMCIA_DEBUG
64 static int pc_debug = PCMCIA_DEBUG;
65 module_param(pc_debug, int, 0);
66 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
67 static char *version =
68 "fdomain_cs.c 1.47 2001/10/13 00:08:52 (David Hinds)";
69 #else
70 #define DEBUG(n, args...)
71 #endif
72
73 /*====================================================================*/
74
75 typedef struct scsi_info_t {
76     dev_link_t          link;
77     dev_node_t          node;
78     struct Scsi_Host    *host;
79 } scsi_info_t;
80
81
82 static void fdomain_release(dev_link_t *link);
83 static void fdomain_detach(struct pcmcia_device *p_dev);
84 static void fdomain_config(dev_link_t *link);
85
86 static int fdomain_attach(struct pcmcia_device *p_dev)
87 {
88     scsi_info_t *info;
89     dev_link_t *link;
90
91     DEBUG(0, "fdomain_attach()\n");
92
93     /* Create new SCSI device */
94     info = kmalloc(sizeof(*info), GFP_KERNEL);
95     if (!info) return -ENOMEM;
96     memset(info, 0, sizeof(*info));
97     link = &info->link; link->priv = info;
98     link->io.NumPorts1 = 0x10;
99     link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
100     link->io.IOAddrLines = 10;
101     link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
102     link->irq.IRQInfo1 = IRQ_LEVEL_ID;
103     link->conf.Attributes = CONF_ENABLE_IRQ;
104     link->conf.Vcc = 50;
105     link->conf.IntType = INT_MEMORY_AND_IO;
106     link->conf.Present = PRESENT_OPTION;
107
108     link->handle = p_dev;
109     p_dev->instance = link;
110
111     link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
112     fdomain_config(link);
113
114     return 0;
115 } /* fdomain_attach */
116
117 /*====================================================================*/
118
119 static void fdomain_detach(struct pcmcia_device *p_dev)
120 {
121         dev_link_t *link = dev_to_instance(p_dev);
122
123         DEBUG(0, "fdomain_detach(0x%p)\n", link);
124
125         if (link->state & DEV_CONFIG)
126                 fdomain_release(link);
127
128         kfree(link->priv);
129 } /* fdomain_detach */
130
131 /*====================================================================*/
132
133 #define CS_CHECK(fn, ret) \
134 do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
135
136 static void fdomain_config(dev_link_t *link)
137 {
138     client_handle_t handle = link->handle;
139     scsi_info_t *info = link->priv;
140     tuple_t tuple;
141     cisparse_t parse;
142     int i, last_ret, last_fn;
143     u_char tuple_data[64];
144     char str[16];
145     struct Scsi_Host *host;
146
147     DEBUG(0, "fdomain_config(0x%p)\n", link);
148
149     tuple.DesiredTuple = CISTPL_CONFIG;
150     tuple.TupleData = tuple_data;
151     tuple.TupleDataMax = 64;
152     tuple.TupleOffset = 0;
153     CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
154     CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
155     CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
156     link->conf.ConfigBase = parse.config.base;
157
158     /* Configure card */
159     link->state |= DEV_CONFIG;
160     
161     tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
162     CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
163     while (1) {
164         if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
165                 pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
166             goto next_entry;
167         link->conf.ConfigIndex = parse.cftable_entry.index;
168         link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
169         i = pcmcia_request_io(handle, &link->io);
170         if (i == CS_SUCCESS) break;
171     next_entry:
172         CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
173     }
174
175     CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
176     CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
177     
178     /* A bad hack... */
179     release_region(link->io.BasePort1, link->io.NumPorts1);
180
181     /* Set configuration options for the fdomain driver */
182     sprintf(str, "%d,%d", link->io.BasePort1, link->irq.AssignedIRQ);
183     fdomain_setup(str);
184     
185     host = __fdomain_16x0_detect(&fdomain_driver_template);
186     if (!host) {
187         printk(KERN_INFO "fdomain_cs: no SCSI devices found\n");
188         goto cs_failed;
189     }
190  
191     scsi_add_host(host, NULL); /* XXX handle failure */
192     scsi_scan_host(host);
193
194     sprintf(info->node.dev_name, "scsi%d", host->host_no);
195     link->dev = &info->node;
196     info->host = host;
197     
198     link->state &= ~DEV_CONFIG_PENDING;
199     return;
200     
201 cs_failed:
202     cs_error(link->handle, last_fn, last_ret);
203     fdomain_release(link);
204     return;
205     
206 } /* fdomain_config */
207
208 /*====================================================================*/
209
210 static void fdomain_release(dev_link_t *link)
211 {
212     scsi_info_t *info = link->priv;
213
214     DEBUG(0, "fdomain_release(0x%p)\n", link);
215
216     scsi_remove_host(info->host);
217     link->dev = NULL;
218     
219     pcmcia_release_configuration(link->handle);
220     pcmcia_release_io(link->handle, &link->io);
221     pcmcia_release_irq(link->handle, &link->irq);
222
223     scsi_unregister(info->host);
224
225     link->state &= ~DEV_CONFIG;
226 }
227
228 /*====================================================================*/
229
230 static int fdomain_suspend(struct pcmcia_device *dev)
231 {
232         dev_link_t *link = dev_to_instance(dev);
233
234         link->state |= DEV_SUSPEND;
235         if (link->state & DEV_CONFIG)
236                 pcmcia_release_configuration(link->handle);
237
238         return 0;
239 }
240
241 static int fdomain_resume(struct pcmcia_device *dev)
242 {
243         dev_link_t *link = dev_to_instance(dev);
244
245         link->state &= ~DEV_SUSPEND;
246         if (link->state & DEV_CONFIG) {
247                 pcmcia_request_configuration(link->handle, &link->conf);
248                 fdomain_16x0_bus_reset(NULL);
249         }
250
251         return 0;
252 }
253
254 static struct pcmcia_device_id fdomain_ids[] = {
255         PCMCIA_DEVICE_PROD_ID12("IBM Corp.", "SCSI PCMCIA Card", 0xe3736c88, 0x859cad20),
256         PCMCIA_DEVICE_PROD_ID1("SCSI PCMCIA Adapter Card", 0x8dacb57e),
257         PCMCIA_DEVICE_PROD_ID12(" SIMPLE TECHNOLOGY Corporation", "SCSI PCMCIA Credit Card Controller", 0x182bdafe, 0xc80d106f),
258         PCMCIA_DEVICE_NULL,
259 };
260 MODULE_DEVICE_TABLE(pcmcia, fdomain_ids);
261
262 static struct pcmcia_driver fdomain_cs_driver = {
263         .owner          = THIS_MODULE,
264         .drv            = {
265                 .name   = "fdomain_cs",
266         },
267         .probe          = fdomain_attach,
268         .remove         = fdomain_detach,
269         .id_table       = fdomain_ids,
270         .suspend        = fdomain_suspend,
271         .resume         = fdomain_resume,
272 };
273
274 static int __init init_fdomain_cs(void)
275 {
276         return pcmcia_register_driver(&fdomain_cs_driver);
277 }
278
279 static void __exit exit_fdomain_cs(void)
280 {
281         pcmcia_unregister_driver(&fdomain_cs_driver);
282 }
283
284 module_init(init_fdomain_cs);
285 module_exit(exit_fdomain_cs);