1/*======================================================================
2
3    A driver for PCMCIA IDE/ATA disk cards
4
5    The contents of this file are subject to the Mozilla Public
6    License Version 1.1 (the "License"); you may not use this file
7    except in compliance with the License. You may obtain a copy of
8    the License at http://www.mozilla.org/MPL/
9
10    Software distributed under the License is distributed on an "AS
11    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
12    implied. See the License for the specific language governing
13    rights and limitations under the License.
14
15    The initial developer of the original code is David A. Hinds
16    <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
17    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
18
19    Alternatively, the contents of this file may be used under the
20    terms of the GNU General Public License version 2 (the "GPL"), in
21    which case the provisions of the GPL are applicable instead of the
22    above.  If you wish to allow the use of your version of this file
23    only under the terms of the GPL and not to allow others to use
24    your version of this file under the MPL, indicate your decision
25    by deleting the provisions above and replace them with the notice
26    and other provisions required by the GPL.  If you do not delete
27    the provisions above, a recipient may use your version of this
28    file under either the MPL or the GPL.
29
30======================================================================*/
31
32#include <linux/module.h>
33#include <linux/kernel.h>
34#include <linux/init.h>
35#include <linux/ptrace.h>
36#include <linux/slab.h>
37#include <linux/string.h>
38#include <linux/timer.h>
39#include <linux/ioport.h>
40#include <linux/ide.h>
41#include <linux/major.h>
42#include <linux/delay.h>
43#include <asm/io.h>
44
45#include <pcmcia/cistpl.h>
46#include <pcmcia/ds.h>
47#include <pcmcia/cisreg.h>
48#include <pcmcia/ciscode.h>
49
50#define DRV_NAME "ide-cs"
51
52/*====================================================================*/
53
54/* Module parameters */
55
56MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
57MODULE_DESCRIPTION("PCMCIA ATA/IDE card driver");
58MODULE_LICENSE("Dual MPL/GPL");
59
60/*====================================================================*/
61
62typedef struct ide_info_t {
63	struct pcmcia_device	*p_dev;
64	struct ide_host		*host;
65	int			ndev;
66} ide_info_t;
67
68static void ide_release(struct pcmcia_device *);
69static int ide_config(struct pcmcia_device *);
70
71static void ide_detach(struct pcmcia_device *p_dev);
72
73static int ide_probe(struct pcmcia_device *link)
74{
75    ide_info_t *info;
76
77    dev_dbg(&link->dev, "ide_attach()\n");
78
79    /* Create new ide device */
80    info = kzalloc(sizeof(*info), GFP_KERNEL);
81    if (!info)
82	return -ENOMEM;
83
84    info->p_dev = link;
85    link->priv = info;
86
87    link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO |
88	    CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC;
89
90    return ide_config(link);
91} /* ide_attach */
92
93static void ide_detach(struct pcmcia_device *link)
94{
95    ide_info_t *info = link->priv;
96
97    dev_dbg(&link->dev, "ide_detach(0x%p)\n", link);
98
99    ide_release(link);
100
101    kfree(info);
102} /* ide_detach */
103
104static const struct ide_port_ops idecs_port_ops = {
105	.quirkproc		= ide_undecoded_slave,
106};
107
108static const struct ide_port_info idecs_port_info = {
109	.port_ops		= &idecs_port_ops,
110	.host_flags		= IDE_HFLAG_NO_DMA,
111	.irq_flags		= IRQF_SHARED,
112	.chipset		= ide_pci,
113};
114
115static struct ide_host *idecs_register(unsigned long io, unsigned long ctl,
116				unsigned long irq, struct pcmcia_device *handle)
117{
118    struct ide_host *host;
119    ide_hwif_t *hwif;
120    int i, rc;
121    struct ide_hw hw, *hws[] = { &hw };
122
123    if (!request_region(io, 8, DRV_NAME)) {
124	printk(KERN_ERR "%s: I/O resource 0x%lX-0x%lX not free.\n",
125			DRV_NAME, io, io + 7);
126	return NULL;
127    }
128
129    if (!request_region(ctl, 1, DRV_NAME)) {
130	printk(KERN_ERR "%s: I/O resource 0x%lX not free.\n",
131			DRV_NAME, ctl);
132	release_region(io, 8);
133	return NULL;
134    }
135
136    memset(&hw, 0, sizeof(hw));
137    ide_std_init_ports(&hw, io, ctl);
138    hw.irq = irq;
139    hw.dev = &handle->dev;
140
141    rc = ide_host_add(&idecs_port_info, hws, 1, &host);
142    if (rc)
143	goto out_release;
144
145    hwif = host->ports[0];
146
147    if (hwif->present)
148	return host;
149
150    /* retry registration in case device is still spinning up */
151    for (i = 0; i < 10; i++) {
152	msleep(100);
153	ide_port_scan(hwif);
154	if (hwif->present)
155	    return host;
156    }
157
158    return host;
159
160out_release:
161    release_region(ctl, 1);
162    release_region(io, 8);
163    return NULL;
164}
165
166static int pcmcia_check_one_config(struct pcmcia_device *pdev, void *priv_data)
167{
168	int *is_kme = priv_data;
169
170	if (!(pdev->resource[0]->flags & IO_DATA_PATH_WIDTH_8)) {
171		pdev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
172		pdev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
173	}
174	pdev->resource[1]->flags &= ~IO_DATA_PATH_WIDTH;
175	pdev->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
176
177	if (pdev->resource[1]->end) {
178		pdev->resource[0]->end = 8;
179		pdev->resource[1]->end = (*is_kme) ? 2 : 1;
180	} else {
181		if (pdev->resource[0]->end < 16)
182			return -ENODEV;
183	}
184
185	return pcmcia_request_io(pdev);
186}
187
188static int ide_config(struct pcmcia_device *link)
189{
190    ide_info_t *info = link->priv;
191    int ret = 0, is_kme = 0;
192    unsigned long io_base, ctl_base;
193    struct ide_host *host;
194
195    dev_dbg(&link->dev, "ide_config(0x%p)\n", link);
196
197    is_kme = ((link->manf_id == MANFID_KME) &&
198	      ((link->card_id == PRODID_KME_KXLC005_A) ||
199	       (link->card_id == PRODID_KME_KXLC005_B)));
200
201    if (pcmcia_loop_config(link, pcmcia_check_one_config, &is_kme)) {
202	    link->config_flags &= ~CONF_AUTO_CHECK_VCC;
203	    if (pcmcia_loop_config(link, pcmcia_check_one_config, &is_kme))
204		    goto failed; /* No suitable config found */
205    }
206    io_base = link->resource[0]->start;
207    if (link->resource[1]->end)
208	    ctl_base = link->resource[1]->start;
209    else
210	    ctl_base = link->resource[0]->start + 0x0e;
211
212    if (!link->irq)
213	    goto failed;
214
215    ret = pcmcia_enable_device(link);
216    if (ret)
217	    goto failed;
218
219    /* disable drive interrupts during IDE probe */
220    outb(0x02, ctl_base);
221
222    /* special setup for KXLC005 card */
223    if (is_kme)
224	outb(0x81, ctl_base+1);
225
226     host = idecs_register(io_base, ctl_base, link->irq, link);
227     if (host == NULL && resource_size(link->resource[0]) == 0x20) {
228	    outb(0x02, ctl_base + 0x10);
229	    host = idecs_register(io_base + 0x10, ctl_base + 0x10,
230				  link->irq, link);
231    }
232
233    if (host == NULL)
234	goto failed;
235
236    info->ndev = 1;
237    info->host = host;
238    dev_info(&link->dev, "ide-cs: hd%c: Vpp = %d.%d\n",
239	    'a' + host->ports[0]->index * 2,
240	    link->vpp / 10, link->vpp % 10);
241
242    return 0;
243
244failed:
245    ide_release(link);
246    return -ENODEV;
247} /* ide_config */
248
249static void ide_release(struct pcmcia_device *link)
250{
251    ide_info_t *info = link->priv;
252    struct ide_host *host = info->host;
253
254    dev_dbg(&link->dev, "ide_release(0x%p)\n", link);
255
256    if (info->ndev) {
257	ide_hwif_t *hwif = host->ports[0];
258	unsigned long data_addr, ctl_addr;
259
260	data_addr = hwif->io_ports.data_addr;
261	ctl_addr = hwif->io_ports.ctl_addr;
262
263	ide_host_remove(host);
264	info->ndev = 0;
265
266	release_region(ctl_addr, 1);
267	release_region(data_addr, 8);
268    }
269
270    pcmcia_disable_device(link);
271} /* ide_release */
272
273
274static const struct pcmcia_device_id ide_ids[] = {
275	PCMCIA_DEVICE_FUNC_ID(4),
276	PCMCIA_DEVICE_MANF_CARD(0x0000, 0x0000),	/* Corsair */
277	PCMCIA_DEVICE_MANF_CARD(0x0007, 0x0000),	/* Hitachi */
278	PCMCIA_DEVICE_MANF_CARD(0x000a, 0x0000),	/* I-O Data CFA */
279	PCMCIA_DEVICE_MANF_CARD(0x001c, 0x0001),	/* Mitsubishi CFA */
280	PCMCIA_DEVICE_MANF_CARD(0x0032, 0x0704),
281	PCMCIA_DEVICE_MANF_CARD(0x0032, 0x2904),
282	PCMCIA_DEVICE_MANF_CARD(0x0045, 0x0401),	/* SanDisk CFA */
283	PCMCIA_DEVICE_MANF_CARD(0x004f, 0x0000),	/* Kingston */
284	PCMCIA_DEVICE_MANF_CARD(0x0097, 0x1620), 	/* TI emulated */
285	PCMCIA_DEVICE_MANF_CARD(0x0098, 0x0000),	/* Toshiba */
286	PCMCIA_DEVICE_MANF_CARD(0x00a4, 0x002d),
287	PCMCIA_DEVICE_MANF_CARD(0x00ce, 0x0000),	/* Samsung */
288	PCMCIA_DEVICE_MANF_CARD(0x0319, 0x0000),	/* Hitachi */
289	PCMCIA_DEVICE_MANF_CARD(0x2080, 0x0001),
290	PCMCIA_DEVICE_MANF_CARD(0x4e01, 0x0100),	/* Viking CFA */
291	PCMCIA_DEVICE_MANF_CARD(0x4e01, 0x0200),	/* Lexar, Viking CFA */
292	PCMCIA_DEVICE_PROD_ID123("Caravelle", "PSC-IDE ", "PSC000", 0x8c36137c, 0xd0693ab8, 0x2768a9f0),
293	PCMCIA_DEVICE_PROD_ID123("CDROM", "IDE", "MCD-601p", 0x1b9179ca, 0xede88951, 0x0d902f74),
294	PCMCIA_DEVICE_PROD_ID123("PCMCIA", "IDE CARD", "F1", 0x281f1c5d, 0x1907960c, 0xf7fde8b9),
295	PCMCIA_DEVICE_PROD_ID12("ARGOSY", "CD-ROM", 0x78f308dc, 0x66536591),
296	PCMCIA_DEVICE_PROD_ID12("ARGOSY", "PnPIDE", 0x78f308dc, 0x0c694728),
297	PCMCIA_DEVICE_PROD_ID12("CNF   ", "CD-ROM", 0x46d7db81, 0x66536591),
298	PCMCIA_DEVICE_PROD_ID12("CNF CD-M", "CD-ROM", 0x7d93b852, 0x66536591),
299	PCMCIA_DEVICE_PROD_ID12("Creative Technology Ltd.", "PCMCIA CD-ROM Interface Card", 0xff8c8a45, 0xfe8020c4),
300	PCMCIA_DEVICE_PROD_ID12("Digital Equipment Corporation.", "Digital Mobile Media CD-ROM", 0x17692a66, 0xef1dcbde),
301	PCMCIA_DEVICE_PROD_ID12("EXP", "CD+GAME", 0x6f58c983, 0x63c13aaf),
302	PCMCIA_DEVICE_PROD_ID12("EXP   ", "CD-ROM", 0x0a5c52fd, 0x66536591),
303	PCMCIA_DEVICE_PROD_ID12("EXP   ", "PnPIDE", 0x0a5c52fd, 0x0c694728),
304	PCMCIA_DEVICE_PROD_ID12("FREECOM", "PCCARD-IDE", 0x5714cbf7, 0x48e0ab8e),
305	PCMCIA_DEVICE_PROD_ID12("HITACHI", "FLASH", 0xf4f43949, 0x9eb86aae),
306	PCMCIA_DEVICE_PROD_ID12("HITACHI", "microdrive", 0xf4f43949, 0xa6d76178),
307	PCMCIA_DEVICE_PROD_ID12("Hyperstone", "Model1", 0x3d5b9ef5, 0xca6ab420),
308	PCMCIA_DEVICE_PROD_ID12("IBM", "microdrive", 0xb569a6e5, 0xa6d76178),
309	PCMCIA_DEVICE_PROD_ID12("IBM", "IBM17JSSFP20", 0xb569a6e5, 0xf2508753),
310	PCMCIA_DEVICE_PROD_ID12("KINGSTON", "CF CARD 1GB", 0x2e6d1829, 0x55d5bffb),
311	PCMCIA_DEVICE_PROD_ID12("KINGSTON", "CF CARD 4GB", 0x2e6d1829, 0x531e7d10),
312	PCMCIA_DEVICE_PROD_ID12("KINGSTON", "CF8GB", 0x2e6d1829, 0xacbe682e),
313	PCMCIA_DEVICE_PROD_ID12("IO DATA", "CBIDE2      ", 0x547e66dc, 0x8671043b),
314	PCMCIA_DEVICE_PROD_ID12("IO DATA", "PCIDE", 0x547e66dc, 0x5c5ab149),
315	PCMCIA_DEVICE_PROD_ID12("IO DATA", "PCIDEII", 0x547e66dc, 0xb3662674),
316	PCMCIA_DEVICE_PROD_ID12("LOOKMEET", "CBIDE2      ", 0xe37be2b5, 0x8671043b),
317	PCMCIA_DEVICE_PROD_ID12("M-Systems", "CF300", 0x7ed2ad87, 0x7e9e78ee),
318	PCMCIA_DEVICE_PROD_ID12("M-Systems", "CF500", 0x7ed2ad87, 0x7a13045c),
319	PCMCIA_DEVICE_PROD_ID2("NinjaATA-", 0xebe0bd79),
320	PCMCIA_DEVICE_PROD_ID12("PCMCIA", "CD-ROM", 0x281f1c5d, 0x66536591),
321	PCMCIA_DEVICE_PROD_ID12("PCMCIA", "PnPIDE", 0x281f1c5d, 0x0c694728),
322	PCMCIA_DEVICE_PROD_ID12("SHUTTLE TECHNOLOGY LTD.", "PCCARD-IDE/ATAPI Adapter", 0x4a3f0ba0, 0x322560e1),
323	PCMCIA_DEVICE_PROD_ID12("SEAGATE", "ST1", 0x87c1b330, 0xe1f30883),
324	PCMCIA_DEVICE_PROD_ID12("SAMSUNG", "04/05/06", 0x43d74cb4, 0x6a22777d),
325	PCMCIA_DEVICE_PROD_ID12("SMI VENDOR", "SMI PRODUCT", 0x30896c92, 0x703cc5f6),
326	PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "MK2001MPL", 0xb4585a1a, 0x3489e003),
327	PCMCIA_DEVICE_PROD_ID1("TRANSCEND    512M   ", 0xd0909443),
328	PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS1GCF45", 0x709b1bf1, 0xf68b6f32),
329	PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS1GCF80", 0x709b1bf1, 0x2a54d4b1),
330	PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS2GCF120", 0x709b1bf1, 0x969aa4f2),
331	PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS4GCF120", 0x709b1bf1, 0xf54a91c8),
332	PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS4GCF133", 0x709b1bf1, 0x7558f133),
333	PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS8GCF133", 0x709b1bf1, 0xb2f89b47),
334	PCMCIA_DEVICE_PROD_ID12("WIT", "IDE16", 0x244e5994, 0x3e232852),
335	PCMCIA_DEVICE_PROD_ID12("WEIDA", "TWTTI", 0xcc7cf69c, 0x212bb918),
336	PCMCIA_DEVICE_PROD_ID1("STI Flash", 0xe4a13209),
337	PCMCIA_DEVICE_PROD_ID12("STI", "Flash 5.0", 0xbf2df18d, 0x8cb57a0e),
338	PCMCIA_MFC_DEVICE_PROD_ID12(1, "SanDisk", "ConnectPlus", 0x7a954bd9, 0x74be00c6),
339	PCMCIA_DEVICE_PROD_ID2("Flash Card", 0x5a362506),
340	PCMCIA_DEVICE_NULL,
341};
342MODULE_DEVICE_TABLE(pcmcia, ide_ids);
343
344static struct pcmcia_driver ide_cs_driver = {
345	.owner		= THIS_MODULE,
346	.name		= "ide-cs",
347	.probe		= ide_probe,
348	.remove		= ide_detach,
349	.id_table       = ide_ids,
350};
351
352static int __init init_ide_cs(void)
353{
354	return pcmcia_register_driver(&ide_cs_driver);
355}
356
357static void __exit exit_ide_cs(void)
358{
359	pcmcia_unregister_driver(&ide_cs_driver);
360}
361
362late_initcall(init_ide_cs);
363module_exit(exit_ide_cs);
364