cmd64x.c 12 KB
Newer Older
1
/*
Linus Torvalds's avatar
Linus Torvalds committed
2 3 4 5 6 7 8 9
 * cmd64x.c: Enable interrupts at initialization time on Ultra/PCI machines.
 *           Due to massive hardware bugs, UltraDMA is only supported
 *           on the 646U2 and not on the 646U.
 *
 * Copyright (C) 1998		Eddie C. Dost  (ecd@skynet.be)
 * Copyright (C) 1998		David S. Miller (davem@redhat.com)
 *
 * Copyright (C) 1999-2002	Andre Hedrick <andre@linux-ide.org>
10
 * Copyright (C) 2007-2010	Bartlomiej Zolnierkiewicz
11
 * Copyright (C) 2007,2009	MontaVista Software, Inc. <source@mvista.com>
Linus Torvalds's avatar
Linus Torvalds committed
12 13 14 15 16 17 18 19 20 21
 */

#include <linux/module.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/ide.h>
#include <linux/init.h>

#include <asm/io.h>

22 23
#define DRV_NAME "cmd64x"

Linus Torvalds's avatar
Linus Torvalds committed
24 25 26 27
/*
 * CMD64x specific registers definition.
 */
#define CFR		0x50
28
#define   CFR_INTR_CH0		0x04
Linus Torvalds's avatar
Linus Torvalds committed
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53

#define	CMDTIM		0x52
#define	ARTTIM0		0x53
#define	DRWTIM0		0x54
#define ARTTIM1 	0x55
#define DRWTIM1		0x56
#define ARTTIM23	0x57
#define   ARTTIM23_DIS_RA2	0x04
#define   ARTTIM23_DIS_RA3	0x08
#define   ARTTIM23_INTR_CH1	0x10
#define DRWTIM2		0x58
#define BRST		0x59
#define DRWTIM3		0x5b

#define BMIDECR0	0x70
#define MRDMODE		0x71
#define   MRDMODE_INTR_CH0	0x04
#define   MRDMODE_INTR_CH1	0x08
#define UDIDETCR0	0x73
#define DTPR0		0x74
#define BMIDECR1	0x78
#define BMIDECSR	0x79
#define UDIDETCR1	0x7B
#define DTPR1		0x7C

54
static void cmd64x_program_timings(ide_drive_t *drive, u8 mode)
Linus Torvalds's avatar
Linus Torvalds committed
55
{
56
	ide_hwif_t *hwif = drive->hwif;
57
	struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
58 59
	int bus_speed = ide_pci_clk ? ide_pci_clk : 33;
	const unsigned long T = 1000000 / bus_speed;
60
	static const u8 recovery_values[] =
Linus Torvalds's avatar
Linus Torvalds committed
61
		{15, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0};
62 63
	static const u8 setup_values[] = {0x40, 0x40, 0x40, 0x80, 0, 0xc0};
	static const u8 arttim_regs[4] = {ARTTIM0, ARTTIM1, ARTTIM23, ARTTIM23};
64
	static const u8 drwtim_regs[4] = {DRWTIM0, DRWTIM1, DRWTIM2, DRWTIM3};
65 66
	struct ide_timing t;
	u8 arttim = 0;
67

68
	ide_timing_compute(drive, mode, &t, T, 0);
69

Linus Torvalds's avatar
Linus Torvalds committed
70
	/*
71 72
	 * In case we've got too long recovery phase, try to lengthen
	 * the active phase
Linus Torvalds's avatar
Linus Torvalds committed
73
	 */
74 75 76
	if (t.recover > 16) {
		t.active += t.recover - 16;
		t.recover = 16;
Linus Torvalds's avatar
Linus Torvalds committed
77
	}
78 79
	if (t.active > 16)		/* shouldn't actually happen... */
		t.active = 16;
80

Linus Torvalds's avatar
Linus Torvalds committed
81 82 83
	/*
	 * Convert values to internal chipset representation
	 */
84 85
	t.recover = recovery_values[t.recover];
	t.active &= 0x0f;
Linus Torvalds's avatar
Linus Torvalds committed
86

87
	/* Program the active/recovery counts into the DRWTIM register */
88 89
	pci_write_config_byte(dev, drwtim_regs[drive->dn],
			      (t.active << 4) | t.recover);
Linus Torvalds's avatar
Linus Torvalds committed
90

91 92 93 94 95 96 97
	/*
	 * The primary channel has individual address setup timing registers
	 * for each drive and the hardware selects the slowest timing itself.
	 * The secondary channel has one common register and we have to select
	 * the slowest address setup timing ourselves.
	 */
	if (hwif->channel) {
98
		ide_drive_t *pair = ide_get_pair_dev(drive);
99

100 101 102 103 104 105 106 107 108 109 110
		if (pair) {
			struct ide_timing tp;

			ide_timing_compute(pair, pair->pio_mode, &tp, T, 0);
			ide_timing_merge(&t, &tp, &t, IDE_TIMING_SETUP);
			if (pair->dma_mode) {
				ide_timing_compute(pair, pair->dma_mode,
						&tp, T, 0);
				ide_timing_merge(&tp, &t, &t, IDE_TIMING_SETUP);
			}
		}
Linus Torvalds's avatar
Linus Torvalds committed
111 112
	}

113 114
	if (t.setup > 5)		/* shouldn't actually happen... */
		t.setup = 5;
Linus Torvalds's avatar
Linus Torvalds committed
115

116 117 118 119 120 121 122 123
	/*
	 * Program the address setup clocks into the ARTTIM registers.
	 * Avoid clearing the secondary channel's interrupt bit.
	 */
	(void) pci_read_config_byte (dev, arttim_regs[drive->dn], &arttim);
	if (hwif->channel)
		arttim &= ~ARTTIM23_INTR_CH1;
	arttim &= ~0xc0;
124
	arttim |= setup_values[t.setup];
125
	(void) pci_write_config_byte(dev, arttim_regs[drive->dn], arttim);
126 127 128 129
}

/*
 * Attempts to set drive's PIO mode.
130
 * Special cases are 8: prefetch off, 9: prefetch on (both never worked)
131
 */
132

133
static void cmd64x_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
134
{
135 136
	const u8 pio = drive->pio_mode - XFER_PIO_0;

137 138 139 140 141 142 143
	/*
	 * Filter out the prefetch control values
	 * to prevent PIO5 from being programmed
	 */
	if (pio == 8 || pio == 9)
		return;

144
	cmd64x_program_timings(drive, XFER_PIO_0 + pio);
Linus Torvalds's avatar
Linus Torvalds committed
145 146
}

147
static void cmd64x_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
Linus Torvalds's avatar
Linus Torvalds committed
148
{
149
	struct pci_dev *dev	= to_pci_dev(hwif->dev);
150 151
	u8 unit			= drive->dn & 0x01;
	u8 regU = 0, pciU	= hwif->channel ? UDIDETCR1 : UDIDETCR0;
152
	const u8 speed		= drive->dma_mode;
Linus Torvalds's avatar
Linus Torvalds committed
153

154 155
	pci_read_config_byte(dev, pciU, &regU);
	regU &= ~(unit ? 0xCA : 0x35);
Linus Torvalds's avatar
Linus Torvalds committed
156 157

	switch(speed) {
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
	case XFER_UDMA_5:
		regU |= unit ? 0x0A : 0x05;
		break;
	case XFER_UDMA_4:
		regU |= unit ? 0x4A : 0x15;
		break;
	case XFER_UDMA_3:
		regU |= unit ? 0x8A : 0x25;
		break;
	case XFER_UDMA_2:
		regU |= unit ? 0x42 : 0x11;
		break;
	case XFER_UDMA_1:
		regU |= unit ? 0x82 : 0x21;
		break;
	case XFER_UDMA_0:
		regU |= unit ? 0xC2 : 0x31;
		break;
	case XFER_MW_DMA_2:
	case XFER_MW_DMA_1:
	case XFER_MW_DMA_0:
179
		cmd64x_program_timings(drive, speed);
180
		break;
Linus Torvalds's avatar
Linus Torvalds committed
181 182
	}

183
	pci_write_config_byte(dev, pciU, regU);
Linus Torvalds's avatar
Linus Torvalds committed
184 185
}

186
static void cmd648_clear_irq(ide_drive_t *drive)
Linus Torvalds's avatar
Linus Torvalds committed
187
{
188
	ide_hwif_t *hwif	= drive->hwif;
189 190
	struct pci_dev *dev	= to_pci_dev(hwif->dev);
	unsigned long base	= pci_resource_start(dev, 4);
191 192
	u8  irq_mask		= hwif->channel ? MRDMODE_INTR_CH1 :
						  MRDMODE_INTR_CH0;
193
	u8  mrdmode		= inb(base + 1);
194 195

	/* clear the interrupt bit */
196
	outb((mrdmode & ~(MRDMODE_INTR_CH0 | MRDMODE_INTR_CH1)) | irq_mask,
197
	     base + 1);
Linus Torvalds's avatar
Linus Torvalds committed
198 199
}

200
static void cmd64x_clear_irq(ide_drive_t *drive)
Linus Torvalds's avatar
Linus Torvalds committed
201
{
202
	ide_hwif_t *hwif	= drive->hwif;
203
	struct pci_dev *dev	= to_pci_dev(hwif->dev);
204 205 206 207
	int irq_reg		= hwif->channel ? ARTTIM23 : CFR;
	u8  irq_mask		= hwif->channel ? ARTTIM23_INTR_CH1 :
						  CFR_INTR_CH0;
	u8  irq_stat		= 0;
Linus Torvalds's avatar
Linus Torvalds committed
208

209 210 211 212 213
	(void) pci_read_config_byte(dev, irq_reg, &irq_stat);
	/* clear the interrupt bit */
	(void) pci_write_config_byte(dev, irq_reg, irq_stat | irq_mask);
}

214
static int cmd648_test_irq(ide_hwif_t *hwif)
215
{
216 217
	struct pci_dev *dev	= to_pci_dev(hwif->dev);
	unsigned long base	= pci_resource_start(dev, 4);
218 219
	u8 irq_mask		= hwif->channel ? MRDMODE_INTR_CH1 :
						  MRDMODE_INTR_CH0;
220
	u8 mrdmode		= inb(base + 1);
221

222 223
	pr_debug("%s: mrdmode: 0x%02x irq_mask: 0x%02x\n",
		 hwif->name, mrdmode, irq_mask);
224

225
	return (mrdmode & irq_mask) ? 1 : 0;
Linus Torvalds's avatar
Linus Torvalds committed
226 227
}

228
static int cmd64x_test_irq(ide_hwif_t *hwif)
Linus Torvalds's avatar
Linus Torvalds committed
229
{
230
	struct pci_dev *dev	= to_pci_dev(hwif->dev);
231 232 233 234
	int irq_reg		= hwif->channel ? ARTTIM23 : CFR;
	u8  irq_mask		= hwif->channel ? ARTTIM23_INTR_CH1 :
						  CFR_INTR_CH0;
	u8  irq_stat		= 0;
235 236

	(void) pci_read_config_byte(dev, irq_reg, &irq_stat);
Linus Torvalds's avatar
Linus Torvalds committed
237

238 239
	pr_debug("%s: irq_stat: 0x%02x irq_mask: 0x%02x\n",
		 hwif->name, irq_stat, irq_mask);
Linus Torvalds's avatar
Linus Torvalds committed
240

241
	return (irq_stat & irq_mask) ? 1 : 0;
Linus Torvalds's avatar
Linus Torvalds committed
242 243 244 245 246 247 248
}

/*
 * ASUS P55T2P4D with CMD646 chipset revision 0x01 requires the old
 * event order for DMA transfers.
 */

249
static int cmd646_1_dma_end(ide_drive_t *drive)
Linus Torvalds's avatar
Linus Torvalds committed
250
{
251
	ide_hwif_t *hwif = drive->hwif;
Linus Torvalds's avatar
Linus Torvalds committed
252 253 254
	u8 dma_stat = 0, dma_cmd = 0;

	/* get DMA status */
255
	dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS);
Linus Torvalds's avatar
Linus Torvalds committed
256
	/* read DMA command state */
257
	dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD);
Linus Torvalds's avatar
Linus Torvalds committed
258
	/* stop DMA */
259
	outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD);
Linus Torvalds's avatar
Linus Torvalds committed
260
	/* clear the INTR & ERROR bits */
261
	outb(dma_stat | 6, hwif->dma_base + ATA_DMA_STATUS);
Linus Torvalds's avatar
Linus Torvalds committed
262 263 264 265
	/* verify good DMA status */
	return (dma_stat & 7) != 4;
}

266
static int init_chipset_cmd64x(struct pci_dev *dev)
Linus Torvalds's avatar
Linus Torvalds committed
267 268 269 270 271 272 273
{
	u8 mrdmode = 0;

	/* Set a good latency timer and cache line size value. */
	(void) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
	/* FIXME: pci_set_master() to ensure a good latency timer value */

274 275 276 277 278 279
	/*
	 * Enable interrupts, select MEMORY READ LINE for reads.
	 *
	 * NOTE: although not mentioned in the PCI0646U specs,
	 * bits 0-1 are write only and won't be read back as
	 * set or not -- PCI0646U2 specs clarify this point.
Linus Torvalds's avatar
Linus Torvalds committed
280
	 */
281 282 283
	(void) pci_read_config_byte (dev, MRDMODE, &mrdmode);
	mrdmode &= ~0x30;
	(void) pci_write_config_byte(dev, MRDMODE, (mrdmode | 0x02));
Linus Torvalds's avatar
Linus Torvalds committed
284 285 286 287

	return 0;
}

288
static u8 cmd64x_cable_detect(ide_hwif_t *hwif)
Linus Torvalds's avatar
Linus Torvalds committed
289
{
290
	struct pci_dev  *dev	= to_pci_dev(hwif->dev);
291
	u8 bmidecsr = 0, mask	= hwif->channel ? 0x02 : 0x01;
Linus Torvalds's avatar
Linus Torvalds committed
292

293 294 295 296
	switch (dev->device) {
	case PCI_DEVICE_ID_CMD_648:
	case PCI_DEVICE_ID_CMD_649:
 		pci_read_config_byte(dev, BMIDECSR, &bmidecsr);
297
		return (bmidecsr & mask) ? ATA_CBL_PATA80 : ATA_CBL_PATA40;
298
	default:
299
		return ATA_CBL_PATA40;
Linus Torvalds's avatar
Linus Torvalds committed
300 301 302
	}
}

303 304 305
static const struct ide_port_ops cmd64x_port_ops = {
	.set_pio_mode		= cmd64x_set_pio_mode,
	.set_dma_mode		= cmd64x_set_dma_mode,
306
	.clear_irq		= cmd64x_clear_irq,
307
	.test_irq		= cmd64x_test_irq,
308 309 310 311 312 313 314
	.cable_detect		= cmd64x_cable_detect,
};

static const struct ide_port_ops cmd648_port_ops = {
	.set_pio_mode		= cmd64x_set_pio_mode,
	.set_dma_mode		= cmd64x_set_dma_mode,
	.clear_irq		= cmd648_clear_irq,
315
	.test_irq		= cmd648_test_irq,
316 317 318
	.cable_detect		= cmd64x_cable_detect,
};

319 320 321 322
static const struct ide_dma_ops cmd646_rev1_dma_ops = {
	.dma_host_set		= ide_dma_host_set,
	.dma_setup		= ide_dma_setup,
	.dma_start		= ide_dma_start,
323
	.dma_end		= cmd646_1_dma_end,
324 325
	.dma_test_irq		= ide_dma_test_irq,
	.dma_lost_irq		= ide_dma_lost_irq,
326
	.dma_timer_expiry	= ide_dma_sff_timer_expiry,
327
	.dma_sff_read_status	= ide_dma_sff_read_status,
328 329
};

330
static const struct ide_port_info cmd64x_chipsets[] = {
331 332
	{	/* 0: CMD643 */
		.name		= DRV_NAME,
Linus Torvalds's avatar
Linus Torvalds committed
333
		.init_chipset	= init_chipset_cmd64x,
334
		.enablebits	= {{0x00,0x00,0x00}, {0x51,0x08,0x08}},
335
		.port_ops	= &cmd64x_port_ops,
336
		.host_flags	= IDE_HFLAG_CLEAR_SIMPLEX |
337 338
				  IDE_HFLAG_ABUSE_PREFETCH |
				  IDE_HFLAG_SERIALIZE,
339
		.pio_mask	= ATA_PIO5,
340
		.mwdma_mask	= ATA_MWDMA2,
341
		.udma_mask	= 0x00, /* no udma */
342 343 344
	},
	{	/* 1: CMD646 */
		.name		= DRV_NAME,
Linus Torvalds's avatar
Linus Torvalds committed
345
		.init_chipset	= init_chipset_cmd64x,
346
		.enablebits	= {{0x51,0x04,0x04}, {0x51,0x08,0x08}},
347
		.port_ops	= &cmd648_port_ops,
348 349
		.host_flags	= IDE_HFLAG_ABUSE_PREFETCH |
				  IDE_HFLAG_SERIALIZE,
350
		.pio_mask	= ATA_PIO5,
351 352
		.mwdma_mask	= ATA_MWDMA2,
		.udma_mask	= ATA_UDMA2,
353 354 355
	},
	{	/* 2: CMD648 */
		.name		= DRV_NAME,
Linus Torvalds's avatar
Linus Torvalds committed
356
		.init_chipset	= init_chipset_cmd64x,
357
		.enablebits	= {{0x51,0x04,0x04}, {0x51,0x08,0x08}},
358
		.port_ops	= &cmd648_port_ops,
359
		.host_flags	= IDE_HFLAG_ABUSE_PREFETCH,
360
		.pio_mask	= ATA_PIO5,
361 362
		.mwdma_mask	= ATA_MWDMA2,
		.udma_mask	= ATA_UDMA4,
363 364 365
	},
	{	/* 3: CMD649 */
		.name		= DRV_NAME,
Linus Torvalds's avatar
Linus Torvalds committed
366
		.init_chipset	= init_chipset_cmd64x,
367
		.enablebits	= {{0x51,0x04,0x04}, {0x51,0x08,0x08}},
368
		.port_ops	= &cmd648_port_ops,
369
		.host_flags	= IDE_HFLAG_ABUSE_PREFETCH,
370
		.pio_mask	= ATA_PIO5,
371 372
		.mwdma_mask	= ATA_MWDMA2,
		.udma_mask	= ATA_UDMA5,
Linus Torvalds's avatar
Linus Torvalds committed
373 374 375
	}
};

376
static int cmd64x_init_one(struct pci_dev *dev, const struct pci_device_id *id)
Linus Torvalds's avatar
Linus Torvalds committed
377
{
378
	struct ide_port_info d;
379 380 381 382
	u8 idx = id->driver_data;

	d = cmd64x_chipsets[idx];

383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
	if (idx == 1) {
		/*
		 * UltraDMA only supported on PCI646U and PCI646U2, which
		 * correspond to revisions 0x03, 0x05 and 0x07 respectively.
		 * Actually, although the CMD tech support people won't
		 * tell me the details, the 0x03 revision cannot support
		 * UDMA correctly without hardware modifications, and even
		 * then it only works with Quantum disks due to some
		 * hold time assumptions in the 646U part which are fixed
		 * in the 646U2.
		 *
		 * So we only do UltraDMA on revision 0x05 and 0x07 chipsets.
		 */
		if (dev->revision < 5) {
			d.udma_mask = 0x00;
			/*
			 * The original PCI0646 didn't have the primary
			 * channel enable bit, it appeared starting with
			 * PCI0646U (i.e. revision ID 3).
			 */
			if (dev->revision < 3) {
				d.enablebits[0].reg = 0;
405
				d.port_ops = &cmd64x_port_ops;
406 407 408 409 410
				if (dev->revision == 1)
					d.dma_ops = &cmd646_rev1_dma_ops;
			}
		}
	}
411

412
	return ide_pci_init_one(dev, &d, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
413 414
}

415 416 417 418 419
static const struct pci_device_id cmd64x_pci_tbl[] = {
	{ PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_643), 0 },
	{ PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_646), 1 },
	{ PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_648), 2 },
	{ PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_649), 3 },
Linus Torvalds's avatar
Linus Torvalds committed
420 421 422 423
	{ 0, },
};
MODULE_DEVICE_TABLE(pci, cmd64x_pci_tbl);

424
static struct pci_driver cmd64x_pci_driver = {
Linus Torvalds's avatar
Linus Torvalds committed
425 426 427
	.name		= "CMD64x_IDE",
	.id_table	= cmd64x_pci_tbl,
	.probe		= cmd64x_init_one,
428
	.remove		= ide_pci_remove,
429 430
	.suspend	= ide_pci_suspend,
	.resume		= ide_pci_resume,
Linus Torvalds's avatar
Linus Torvalds committed
431 432
};

433
static int __init cmd64x_ide_init(void)
Linus Torvalds's avatar
Linus Torvalds committed
434
{
435
	return ide_pci_register_driver(&cmd64x_pci_driver);
Linus Torvalds's avatar
Linus Torvalds committed
436 437
}

438 439
static void __exit cmd64x_ide_exit(void)
{
440
	pci_unregister_driver(&cmd64x_pci_driver);
441 442
}

Linus Torvalds's avatar
Linus Torvalds committed
443
module_init(cmd64x_ide_init);
444
module_exit(cmd64x_ide_exit);
Linus Torvalds's avatar
Linus Torvalds committed
445

446
MODULE_AUTHOR("Eddie Dost, David Miller, Andre Hedrick, Bartlomiej Zolnierkiewicz");
Linus Torvalds's avatar
Linus Torvalds committed
447 448
MODULE_DESCRIPTION("PCI driver module for CMD64x IDE");
MODULE_LICENSE("GPL");