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)
171	    != IO_DATA_PATH_WIDTH_8) {
172		pdev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
173		pdev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
174	}
175	pdev->resource[1]->flags &= ~IO_DATA_PATH_WIDTH;
176	pdev->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
177
178	if (pdev->resource[1]->end) {
179		pdev->resource[0]->end = 8;
180		pdev->resource[1]->end = (*is_kme) ? 2 : 1;
181	} else {
182		if (pdev->resource[0]->end < 16)
183			return -ENODEV;
184	}
185
186	return pcmcia_request_io(pdev);
187}
188
189static int ide_config(struct pcmcia_device *link)
190{
191    ide_info_t *info = link->priv;
192    int ret = 0, is_kme = 0;
193    unsigned long io_base, ctl_base;
194    struct ide_host *host;
195
196    dev_dbg(&link->dev, "ide_config(0x%p)\n", link);
197
198    is_kme = ((link->manf_id == MANFID_KME) &&
199	      ((link->card_id == PRODID_KME_KXLC005_A) ||
200	       (link->card_id == PRODID_KME_KXLC005_B)));
201
202    if (pcmcia_loop_config(link, pcmcia_check_one_config, &is_kme)) {
203	    link->config_flags &= ~CONF_AUTO_CHECK_VCC;
204	    if (pcmcia_loop_config(link, pcmcia_check_one_config, &is_kme))
205		    goto failed; /* No suitable config found */
206    }
207    io_base = link->resource[0]->start;
208    if (link->resource[1]->end)
209	    ctl_base = link->resource[1]->start;
210    else
211	    ctl_base = link->resource[0]->start + 0x0e;
212
213    if (!link->irq)
214	    goto failed;
215
216    ret = pcmcia_enable_device(link);
217    if (ret)
218	    goto failed;
219
220    /* disable drive interrupts during IDE probe */
221    outb(0x02, ctl_base);
222
223    /* special setup for KXLC005 card */
224    if (is_kme)
225	outb(0x81, ctl_base+1);
226
227     host = idecs_register(io_base, ctl_base, link->irq, link);
228     if (host == NULL && resource_size(link->resource[0]) == 0x20) {
229	    outb(0x02, ctl_base + 0x10);
230	    host = idecs_register(io_base + 0x10, ctl_base + 0x10,
231				  link->irq, link);
232    }
233
234    if (host == NULL)
235	goto failed;
236
237    info->ndev = 1;
238    info->host = host;
239    dev_info(&link->dev, "ide-cs: hd%c: Vpp = %d.%d\n",
240	    'a' + host->ports[0]->index * 2,
241	    link->vpp / 10, link->vpp % 10);
242
243    return 0;
244
245failed:
246    ide_release(link);
247    return -ENODEV;
248} /* ide_config */
249
250static void ide_release(struct pcmcia_device *link)
251{
252    ide_info_t *info = link->priv;
253    struct ide_host *host = info->host;
254
255    dev_dbg(&link->dev, "ide_release(0x%p)\n", link);
256
257    if (info->ndev) {
258	ide_hwif_t *hwif = host->ports[0];
259	unsigned long data_addr, ctl_addr;
260
261	data_addr = hwif->io_ports.data_addr;
262	ctl_addr = hwif->io_ports.ctl_addr;
263
264	ide_host_remove(host);
265	info->ndev = 0;
266
267	release_region(ctl_addr, 1);
268	release_region(data_addr, 8);
269    }
270
271    pcmcia_disable_device(link);
272} /* ide_release */
273
274
275static const struct pcmcia_device_id ide_ids[] = {
276	PCMCIA_DEVICE_FUNC_ID(4),
277	PCMCIA_DEVICE_MANF_CARD(0x0000, 0x0000),	/* Corsair */
278	PCMCIA_DEVICE_MANF_CARD(0x0007, 0x0000),	/* Hitachi */
279	PCMCIA_DEVICE_MANF_CARD(0x000a, 0x0000),	/* I-O Data CFA */
280	PCMCIA_DEVICE_MANF_CARD(0x001c, 0x0001),	/* Mitsubishi CFA */
281	PCMCIA_DEVICE_MANF_CARD(0x0032, 0x0704),
282	PCMCIA_DEVICE_MANF_CARD(0x0032, 0x2904),
283	PCMCIA_DEVICE_MANF_CARD(0x0045, 0x0401),	/* SanDisk CFA */
284	PCMCIA_DEVICE_MANF_CARD(0x004f, 0x0000),	/* Kingston */
285	PCMCIA_DEVICE_MANF_CARD(0x0097, 0x1620), 	/* TI emulated */
286	PCMCIA_DEVICE_MANF_CARD(0x0098, 0x0000),	/* Toshiba */
287	PCMCIA_DEVICE_MANF_CARD(0x00a4, 0x002d),
288	PCMCIA_DEVICE_MANF_CARD(0x00ce, 0x0000),	/* Samsung */
289	PCMCIA_DEVICE_MANF_CARD(0x0319, 0x0000),	/* Hitachi */
290	PCMCIA_DEVICE_MANF_CARD(0x2080, 0x0001),
291	PCMCIA_DEVICE_MANF_CARD(0x4e01, 0x0100),	/* Viking CFA */
292	PCMCIA_DEVICE_MANF_CARD(0x4e01, 0x0200),	/* Lexar, Viking CFA */
293	PCMCIA_DEVICE_PROD_ID123("Caravelle", "PSC-IDE ", "PSC000", 0x8c36137c, 0xd0693ab8, 0x2768a9f0),
294	PCMCIA_DEVICE_PROD_ID123("CDROM", "IDE", "MCD-601p", 0x1b9179ca, 0xede88951, 0x0d902f74),
295	PCMCIA_DEVICE_PROD_ID123("PCMCIA", "IDE CARD", "F1", 0x281f1c5d, 0x1907960c, 0xf7fde8b9),
296	PCMCIA_DEVICE_PROD_ID12("ARGOSY", "CD-ROM", 0x78f308dc, 0x66536591),
297	PCMCIA_DEVICE_PROD_ID12("ARGOSY", "PnPIDE", 0x78f308dc, 0x0c694728),
298	PCMCIA_DEVICE_PROD_ID12("CNF   ", "CD-ROM", 0x46d7db81, 0x66536591),
299	PCMCIA_DEVICE_PROD_ID12("CNF CD-M", "CD-ROM", 0x7d93b852, 0x66536591),
300	PCMCIA_DEVICE_PROD_ID12("Creative Technology Ltd.", "PCMCIA CD-ROM Interface Card", 0xff8c8a45, 0xfe8020c4),
301	PCMCIA_DEVICE_PROD_ID12("Digital Equipment Corporation.", "Digital Mobile Media CD-ROM", 0x17692a66, 0xef1dcbde),
302	PCMCIA_DEVICE_PROD_ID12("EXP", "CD+GAME", 0x6f58c983, 0x63c13aaf),
303	PCMCIA_DEVICE_PROD_ID12("EXP   ", "CD-ROM", 0x0a5c52fd, 0x66536591),
304	PCMCIA_DEVICE_PROD_ID12("EXP   ", "PnPIDE", 0x0a5c52fd, 0x0c694728),
305	PCMCIA_DEVICE_PROD_ID12("FREECOM", "PCCARD-IDE", 0x5714cbf7, 0x48e0ab8e),
306	PCMCIA_DEVICE_PROD_ID12("HITACHI", "FLASH", 0xf4f43949, 0x9eb86aae),
307	PCMCIA_DEVICE_PROD_ID12("HITACHI", "microdrive", 0xf4f43949, 0xa6d76178),
308	PCMCIA_DEVICE_PROD_ID12("Hyperstone", "Model1", 0x3d5b9ef5, 0xca6ab420),
309	PCMCIA_DEVICE_PROD_ID12("IBM", "microdrive", 0xb569a6e5, 0xa6d76178),
310	PCMCIA_DEVICE_PROD_ID12("IBM", "IBM17JSSFP20", 0xb569a6e5, 0xf2508753),
311	PCMCIA_DEVICE_PROD_ID12("KINGSTON", "CF CARD 1GB", 0x2e6d1829, 0x55d5bffb),
312	PCMCIA_DEVICE_PROD_ID12("KINGSTON", "CF CARD 4GB", 0x2e6d1829, 0x531e7d10),
313	PCMCIA_DEVICE_PROD_ID12("KINGSTON", "CF8GB", 0x2e6d1829, 0xacbe682e),
314	PCMCIA_DEVICE_PROD_ID12("IO DATA", "CBIDE2      ", 0x547e66dc, 0x8671043b),
315	PCMCIA_DEVICE_PROD_ID12("IO DATA", "PCIDE", 0x547e66dc, 0x5c5ab149),
316	PCMCIA_DEVICE_PROD_ID12("IO DATA", "PCIDEII", 0x547e66dc, 0xb3662674),
317	PCMCIA_DEVICE_PROD_ID12("LOOKMEET", "CBIDE2      ", 0xe37be2b5, 0x8671043b),
318	PCMCIA_DEVICE_PROD_ID12("M-Systems", "CF300", 0x7ed2ad87, 0x7e9e78ee),
319	PCMCIA_DEVICE_PROD_ID12("M-Systems", "CF500", 0x7ed2ad87, 0x7a13045c),
320	PCMCIA_DEVICE_PROD_ID2("NinjaATA-", 0xebe0bd79),
321	PCMCIA_DEVICE_PROD_ID12("PCMCIA", "CD-ROM", 0x281f1c5d, 0x66536591),
322	PCMCIA_DEVICE_PROD_ID12("PCMCIA", "PnPIDE", 0x281f1c5d, 0x0c694728),
323	PCMCIA_DEVICE_PROD_ID12("SHUTTLE TECHNOLOGY LTD.", "PCCARD-IDE/ATAPI Adapter", 0x4a3f0ba0, 0x322560e1),
324	PCMCIA_DEVICE_PROD_ID12("SEAGATE", "ST1", 0x87c1b330, 0xe1f30883),
325	PCMCIA_DEVICE_PROD_ID12("SAMSUNG", "04/05/06", 0x43d74cb4, 0x6a22777d),
326	PCMCIA_DEVICE_PROD_ID12("SMI VENDOR", "SMI PRODUCT", 0x30896c92, 0x703cc5f6),
327	PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "MK2001MPL", 0xb4585a1a, 0x3489e003),
328	PCMCIA_DEVICE_PROD_ID1("TRANSCEND    512M   ", 0xd0909443),
329	PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS1GCF45", 0x709b1bf1, 0xf68b6f32),
330	PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS1GCF80", 0x709b1bf1, 0x2a54d4b1),
331	PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS2GCF120", 0x709b1bf1, 0x969aa4f2),
332	PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS4GCF120", 0x709b1bf1, 0xf54a91c8),
333	PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS4GCF133", 0x709b1bf1, 0x7558f133),
334	PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS8GCF133", 0x709b1bf1, 0xb2f89b47),
335	PCMCIA_DEVICE_PROD_ID12("WIT", "IDE16", 0x244e5994, 0x3e232852),
336	PCMCIA_DEVICE_PROD_ID12("WEIDA", "TWTTI", 0xcc7cf69c, 0x212bb918),
337	PCMCIA_DEVICE_PROD_ID1("STI Flash", 0xe4a13209),
338	PCMCIA_DEVICE_PROD_ID12("STI", "Flash 5.0", 0xbf2df18d, 0x8cb57a0e),
339	PCMCIA_MFC_DEVICE_PROD_ID12(1, "SanDisk", "ConnectPlus", 0x7a954bd9, 0x74be00c6),
340	PCMCIA_DEVICE_PROD_ID2("Flash Card", 0x5a362506),
341	PCMCIA_DEVICE_NULL,
342};
343MODULE_DEVICE_TABLE(pcmcia, ide_ids);
344
345static struct pcmcia_driver ide_cs_driver = {
346	.owner		= THIS_MODULE,
347	.name		= "ide-cs",
348	.probe		= ide_probe,
349	.remove		= ide_detach,
350	.id_table       = ide_ids,
351};
352
353static int __init init_ide_cs(void)
354{
355	return pcmcia_register_driver(&ide_cs_driver);
356}
357
358static void __exit exit_ide_cs(void)
359{
360	pcmcia_unregister_driver(&ide_cs_driver);
361}
362
363late_initcall(init_ide_cs);
364module_exit(exit_ide_cs);
365