ide-cs.c 12 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/*======================================================================

    A driver for PCMCIA IDE/ATA disk cards

    The contents of this file are subject to the Mozilla Public
    License Version 1.1 (the "License"); you may not use this file
    except in compliance with the License. You may obtain a copy of
    the License at http://www.mozilla.org/MPL/

    Software distributed under the License is distributed on an "AS
    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
    implied. See the License for the specific language governing
    rights and limitations under the License.

    The initial developer of the original code is David A. Hinds
    <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.

    Alternatively, the contents of this file may be used under the
    terms of the GNU General Public License version 2 (the "GPL"), in
    which case the provisions of the GPL are applicable instead of the
    above.  If you wish to allow the use of your version of this file
    only under the terms of the GPL and not to allow others to use
    your version of this file under the MPL, indicate your decision
    by deleting the provisions above and replace them with the notice
    and other provisions required by the GPL.  If you do not delete
    the provisions above, a recipient may use your version of this
    file under either the MPL or the GPL.
29

Linus Torvalds's avatar
Linus Torvalds committed
30 31 32 33 34 35 36 37 38 39 40 41
======================================================================*/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/ioport.h>
#include <linux/ide.h>
#include <linux/major.h>
42
#include <linux/delay.h>
Linus Torvalds's avatar
Linus Torvalds committed
43 44 45 46 47 48 49
#include <asm/io.h>

#include <pcmcia/cistpl.h>
#include <pcmcia/ds.h>
#include <pcmcia/cisreg.h>
#include <pcmcia/ciscode.h>

50 51
#define DRV_NAME "ide-cs"

Linus Torvalds's avatar
Linus Torvalds committed
52 53 54 55 56 57 58 59 60 61 62
/*====================================================================*/

/* Module parameters */

MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
MODULE_DESCRIPTION("PCMCIA ATA/IDE card driver");
MODULE_LICENSE("Dual MPL/GPL");

/*====================================================================*/

typedef struct ide_info_t {
63
	struct pcmcia_device	*p_dev;
64
	struct ide_host		*host;
65
	int			ndev;
Linus Torvalds's avatar
Linus Torvalds committed
66 67
} ide_info_t;

68
static void ide_release(struct pcmcia_device *);
69
static int ide_config(struct pcmcia_device *);
Linus Torvalds's avatar
Linus Torvalds committed
70

71
static void ide_detach(struct pcmcia_device *p_dev);
Linus Torvalds's avatar
Linus Torvalds committed
72

73
static int ide_probe(struct pcmcia_device *link)
Linus Torvalds's avatar
Linus Torvalds committed
74 75
{
    ide_info_t *info;
76

77
    dev_dbg(&link->dev, "ide_attach()\n");
Linus Torvalds's avatar
Linus Torvalds committed
78 79

    /* Create new ide device */
80
    info = kzalloc(sizeof(*info), GFP_KERNEL);
81 82
    if (!info)
	return -ENOMEM;
83

84
    info->p_dev = link;
85
    link->priv = info;
Linus Torvalds's avatar
Linus Torvalds committed
86

87 88
    link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO |
	    CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC;
89

90
    return ide_config(link);
Linus Torvalds's avatar
Linus Torvalds committed
91 92
} /* ide_attach */

93
static void ide_detach(struct pcmcia_device *link)
Linus Torvalds's avatar
Linus Torvalds committed
94
{
95 96
    ide_info_t *info = link->priv;

97
    dev_dbg(&link->dev, "ide_detach(0x%p)\n", link);
Linus Torvalds's avatar
Linus Torvalds committed
98

99
    ide_release(link);
100

101
    kfree(info);
Linus Torvalds's avatar
Linus Torvalds committed
102 103
} /* ide_detach */

104 105 106 107
static const struct ide_port_ops idecs_port_ops = {
	.quirkproc		= ide_undecoded_slave,
};

108 109 110
static const struct ide_port_info idecs_port_info = {
	.port_ops		= &idecs_port_ops,
	.host_flags		= IDE_HFLAG_NO_DMA,
111
	.irq_flags		= IRQF_SHARED,
112
	.chipset		= ide_pci,
113 114
};

115
static struct ide_host *idecs_register(unsigned long io, unsigned long ctl,
116
				unsigned long irq, struct pcmcia_device *handle)
Linus Torvalds's avatar
Linus Torvalds committed
117
{
118
    struct ide_host *host;
119
    ide_hwif_t *hwif;
120
    int i, rc;
121
    struct ide_hw hw, *hws[] = { &hw };
122

123 124 125 126 127 128 129 130 131 132 133 134 135
    if (!request_region(io, 8, DRV_NAME)) {
	printk(KERN_ERR "%s: I/O resource 0x%lX-0x%lX not free.\n",
			DRV_NAME, io, io + 7);
	return NULL;
    }

    if (!request_region(ctl, 1, DRV_NAME)) {
	printk(KERN_ERR "%s: I/O resource 0x%lX not free.\n",
			DRV_NAME, ctl);
	release_region(io, 8);
	return NULL;
    }

Linus Torvalds's avatar
Linus Torvalds committed
136
    memset(&hw, 0, sizeof(hw));
137
    ide_std_init_ports(&hw, io, ctl);
Linus Torvalds's avatar
Linus Torvalds committed
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 151 152 153 154
    /* retry registration in case device is still spinning up */
    for (i = 0; i < 10; i++) {
	msleep(100);
	ide_port_scan(hwif);
	if (hwif->present)
155
	    return host;
156 157
    }

158
    return host;
159

160 161 162 163
out_release:
    release_region(ctl, 1);
    release_region(io, 8);
    return NULL;
Linus Torvalds's avatar
Linus Torvalds committed
164 165
}

166
static int pcmcia_check_one_config(struct pcmcia_device *pdev, void *priv_data)
167
{
168 169
	int *is_kme = priv_data;

170 171
	if ((pdev->resource[0]->flags & IO_DATA_PATH_WIDTH)
	    != IO_DATA_PATH_WIDTH_8) {
172 173 174 175 176 177 178 179 180 181 182
		pdev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
		pdev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
	}
	pdev->resource[1]->flags &= ~IO_DATA_PATH_WIDTH;
	pdev->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;

	if (pdev->resource[1]->end) {
		pdev->resource[0]->end = 8;
		pdev->resource[1]->end = (*is_kme) ? 2 : 1;
	} else {
		if (pdev->resource[0]->end < 16)
183
			return -ENODEV;
184
	}
185 186

	return pcmcia_request_io(pdev);
187 188
}

189
static int ide_config(struct pcmcia_device *link)
Linus Torvalds's avatar
Linus Torvalds committed
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;
Linus Torvalds's avatar
Linus Torvalds committed
195

196
    dev_dbg(&link->dev, "ide_config(0x%p)\n", link);
Linus Torvalds's avatar
Linus Torvalds committed
197

198 199 200
    is_kme = ((link->manf_id == MANFID_KME) &&
	      ((link->card_id == PRODID_KME_KXLC005_A) ||
	       (link->card_id == PRODID_KME_KXLC005_B)));
Linus Torvalds's avatar
Linus Torvalds committed
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 */
Linus Torvalds's avatar
Linus Torvalds committed
206
    }
207
    io_base = link->resource[0]->start;
208 209 210 211
    if (link->resource[1]->end)
	    ctl_base = link->resource[1]->start;
    else
	    ctl_base = link->resource[0]->start + 0x0e;
Linus Torvalds's avatar
Linus Torvalds committed
212

213
    if (!link->irq)
214
	    goto failed;
215 216

    ret = pcmcia_enable_device(link);
217 218
    if (ret)
	    goto failed;
Linus Torvalds's avatar
Linus Torvalds committed
219 220

    /* disable drive interrupts during IDE probe */
221
    outb(0x02, ctl_base);
Linus Torvalds's avatar
Linus Torvalds committed
222 223 224

    /* special setup for KXLC005 card */
    if (is_kme)
225
	outb(0x81, ctl_base+1);
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
232 233
    }

234
    if (host == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
235 236 237
	goto failed;

    info->ndev = 1;
238
    info->host = host;
239 240
    dev_info(&link->dev, "ide-cs: hd%c: Vpp = %d.%d\n",
	    'a' + host->ports[0]->index * 2,
241
	    link->vpp / 10, link->vpp % 10);
Linus Torvalds's avatar
Linus Torvalds committed
242

243
    return 0;
Linus Torvalds's avatar
Linus Torvalds committed
244 245 246

failed:
    ide_release(link);
247
    return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
248 249
} /* ide_config */

250
static void ide_release(struct pcmcia_device *link)
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
256

257 258 259 260 261 262 263
    if (info->ndev) {
	ide_hwif_t *hwif = host->ports[0];
	unsigned long data_addr, ctl_addr;

	data_addr = hwif->io_ports.data_addr;
	ctl_addr = hwif->io_ports.ctl_addr;

264
	ide_host_remove(host);
265
	info->ndev = 0;
266

267 268 269
	release_region(ctl_addr, 1);
	release_region(data_addr, 8);
    }
Linus Torvalds's avatar
Linus Torvalds committed
270

271
    pcmcia_disable_device(link);
Linus Torvalds's avatar
Linus Torvalds committed
272 273
} /* ide_release */

274

275
static 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 280
	PCMCIA_DEVICE_MANF_CARD(0x000a, 0x0000),	/* I-O Data CFA */
	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 292
	PCMCIA_DEVICE_MANF_CARD(0x4e01, 0x0100),	/* Viking CFA */
	PCMCIA_DEVICE_MANF_CARD(0x4e01, 0x0200),	/* Lexar, Viking CFA */
293 294 295 296 297
	PCMCIA_DEVICE_PROD_ID123("Caravelle", "PSC-IDE ", "PSC000", 0x8c36137c, 0xd0693ab8, 0x2768a9f0),
	PCMCIA_DEVICE_PROD_ID123("CDROM", "IDE", "MCD-601p", 0x1b9179ca, 0xede88951, 0x0d902f74),
	PCMCIA_DEVICE_PROD_ID123("PCMCIA", "IDE CARD", "F1", 0x281f1c5d, 0x1907960c, 0xf7fde8b9),
	PCMCIA_DEVICE_PROD_ID12("ARGOSY", "CD-ROM", 0x78f308dc, 0x66536591),
	PCMCIA_DEVICE_PROD_ID12("ARGOSY", "PnPIDE", 0x78f308dc, 0x0c694728),
298
	PCMCIA_DEVICE_PROD_ID12("CNF   ", "CD-ROM", 0x46d7db81, 0x66536591),
299 300 301
	PCMCIA_DEVICE_PROD_ID12("CNF CD-M", "CD-ROM", 0x7d93b852, 0x66536591),
	PCMCIA_DEVICE_PROD_ID12("Creative Technology Ltd.", "PCMCIA CD-ROM Interface Card", 0xff8c8a45, 0xfe8020c4),
	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 304 305
	PCMCIA_DEVICE_PROD_ID12("EXP   ", "CD-ROM", 0x0a5c52fd, 0x66536591),
	PCMCIA_DEVICE_PROD_ID12("EXP   ", "PnPIDE", 0x0a5c52fd, 0x0c694728),
	PCMCIA_DEVICE_PROD_ID12("FREECOM", "PCCARD-IDE", 0x5714cbf7, 0x48e0ab8e),
306 307
	PCMCIA_DEVICE_PROD_ID12("HITACHI", "FLASH", 0xf4f43949, 0x9eb86aae),
	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 315 316 317
	PCMCIA_DEVICE_PROD_ID12("IO DATA", "CBIDE2      ", 0x547e66dc, 0x8671043b),
	PCMCIA_DEVICE_PROD_ID12("IO DATA", "PCIDE", 0x547e66dc, 0x5c5ab149),
	PCMCIA_DEVICE_PROD_ID12("IO DATA", "PCIDEII", 0x547e66dc, 0xb3662674),
	PCMCIA_DEVICE_PROD_ID12("LOOKMEET", "CBIDE2      ", 0xe37be2b5, 0x8671043b),
Matt Reimer's avatar
Matt Reimer committed
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 322 323
	PCMCIA_DEVICE_PROD_ID12("PCMCIA", "CD-ROM", 0x281f1c5d, 0x66536591),
	PCMCIA_DEVICE_PROD_ID12("PCMCIA", "PnPIDE", 0x281f1c5d, 0x0c694728),
	PCMCIA_DEVICE_PROD_ID12("SHUTTLE TECHNOLOGY LTD.", "PCCARD-IDE/ATAPI Adapter", 0x4a3f0ba0, 0x322560e1),
324 325 326
	PCMCIA_DEVICE_PROD_ID12("SEAGATE", "ST1", 0x87c1b330, 0xe1f30883),
	PCMCIA_DEVICE_PROD_ID12("SAMSUNG", "04/05/06", 0x43d74cb4, 0x6a22777d),
	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 342 343 344
	PCMCIA_DEVICE_NULL,
};
MODULE_DEVICE_TABLE(pcmcia, ide_ids);

Linus Torvalds's avatar
Linus Torvalds committed
345 346
static struct pcmcia_driver ide_cs_driver = {
	.owner		= THIS_MODULE,
347
	.name		= "ide-cs",
348
	.probe		= ide_probe,
349
	.remove		= ide_detach,
350
	.id_table       = ide_ids,
Linus Torvalds's avatar
Linus Torvalds committed
351 352 353 354 355 356 357 358 359 360 361 362
};

static int __init init_ide_cs(void)
{
	return pcmcia_register_driver(&ide_cs_driver);
}

static void __exit exit_ide_cs(void)
{
	pcmcia_unregister_driver(&ide_cs_driver);
}

363
late_initcall(init_ide_cs);
Linus Torvalds's avatar
Linus Torvalds committed
364
module_exit(exit_ide_cs);