cmd64x.c 12 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
Linus Torvalds's avatar
Linus Torvalds committed
3 4 5 6 7 8 9 10
 * 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>
11
 * Copyright (C) 2007-2010	Bartlomiej Zolnierkiewicz
12
 * Copyright (C) 2007,2009	MontaVista Software, Inc. <source@mvista.com>
Linus Torvalds's avatar
Linus Torvalds committed
13 14 15 16 17 18 19 20 21 22
 */

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

#include <asm/io.h>

23 24
#define DRV_NAME "cmd64x"

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

#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

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

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

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

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

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

92 93 94 95 96 97 98
	/*
	 * 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) {
99
		ide_drive_t *pair = ide_get_pair_dev(drive);
100

101 102 103 104 105 106 107 108 109 110 111
		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
112 113
	}

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

117 118 119 120 121 122 123 124
	/*
	 * 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;
125
	arttim |= setup_values[t.setup];
126
	(void) pci_write_config_byte(dev, arttim_regs[drive->dn], arttim);
127 128 129 130
}

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

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

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

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

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

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

	switch(speed) {
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
	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:
180
		cmd64x_program_timings(drive, speed);
181
		break;
Linus Torvalds's avatar
Linus Torvalds committed
182 183
	}

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

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

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

201
static void cmd64x_clear_irq(ide_drive_t *drive)
Linus Torvalds's avatar
Linus Torvalds committed
202
{
203
	ide_hwif_t *hwif	= drive->hwif;
204
	struct pci_dev *dev	= to_pci_dev(hwif->dev);
205 206 207 208
	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
209

210 211 212 213 214
	(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);
}

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

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

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

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

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

239 240
	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
241

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

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

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

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

267
static int init_chipset_cmd64x(struct pci_dev *dev)
Linus Torvalds's avatar
Linus Torvalds committed
268 269 270 271 272 273 274
{
	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 */

275 276 277 278 279 280
	/*
	 * 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
281
	 */
282 283 284
	(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
285 286 287 288

	return 0;
}

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

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

304 305 306
static const struct ide_port_ops cmd64x_port_ops = {
	.set_pio_mode		= cmd64x_set_pio_mode,
	.set_dma_mode		= cmd64x_set_dma_mode,
307
	.clear_irq		= cmd64x_clear_irq,
308
	.test_irq		= cmd64x_test_irq,
309 310 311 312 313 314 315
	.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,
316
	.test_irq		= cmd648_test_irq,
317 318 319
	.cable_detect		= cmd64x_cable_detect,
};

320 321 322 323
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,
324
	.dma_end		= cmd646_1_dma_end,
325 326
	.dma_test_irq		= ide_dma_test_irq,
	.dma_lost_irq		= ide_dma_lost_irq,
327
	.dma_timer_expiry	= ide_dma_sff_timer_expiry,
328
	.dma_sff_read_status	= ide_dma_sff_read_status,
329 330
};

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

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

	d = cmd64x_chipsets[idx];

384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
	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;
406
				d.port_ops = &cmd64x_port_ops;
407 408 409 410 411
				if (dev->revision == 1)
					d.dma_ops = &cmd646_rev1_dma_ops;
			}
		}
	}
412

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

416 417 418 419 420
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
421 422 423 424
	{ 0, },
};
MODULE_DEVICE_TABLE(pci, cmd64x_pci_tbl);

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

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

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

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

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