All new accounts created on Gitlab now require administrator approval. If you invite any collaborators, please let Flux staff know so they can approve the accounts.

amd74xx.c 10.7 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5
/*
 * AMD 755/756/766/8111 and nVidia nForce/2/2s/3/3s/CK804/MCP04
 * IDE driver for Linux.
 *
 * Copyright (c) 2000-2002 Vojtech Pavlik
6
 * Copyright (c) 2007 Bartlomiej Zolnierkiewicz
Linus Torvalds's avatar
Linus Torvalds committed
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 *
 * Based on the work of:
 *      Andre Hedrick
 */

/*
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 */

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

#include "ide-timing.h"

26 27 28 29 30 31 32
enum {
	AMD_IDE_CONFIG		= 0x41,
	AMD_CABLE_DETECT	= 0x42,
	AMD_DRIVE_TIMING	= 0x48,
	AMD_8BIT_TIMING		= 0x4e,
	AMD_ADDRESS_SETUP	= 0x4c,
	AMD_UDMA_TIMING		= 0x50,
Linus Torvalds's avatar
Linus Torvalds committed
33 34 35 36 37
};

static unsigned int amd_80w;
static unsigned int amd_clock;

38
static char *amd_dma[] = { "16", "25", "33", "44", "66", "100", "133" };
Linus Torvalds's avatar
Linus Torvalds committed
39 40
static unsigned char amd_cyc2udma[] = { 6, 6, 5, 4, 0, 1, 1, 2, 2, 3, 3, 3, 3, 3, 3, 7 };

41 42 43 44 45
static inline u8 amd_offset(struct pci_dev *dev)
{
	return (dev->vendor == PCI_VENDOR_ID_NVIDIA) ? 0x10 : 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
46 47 48 49
/*
 * amd_set_speed() writes timing values to the chipset registers
 */

50 51
static void amd_set_speed(struct pci_dev *dev, u8 dn, u8 udma_mask,
			  struct ide_timing *timing)
Linus Torvalds's avatar
Linus Torvalds committed
52
{
53
	u8 t = 0, offset = amd_offset(dev);
Linus Torvalds's avatar
Linus Torvalds committed
54

55
	pci_read_config_byte(dev, AMD_ADDRESS_SETUP + offset, &t);
Linus Torvalds's avatar
Linus Torvalds committed
56
	t = (t & ~(3 << ((3 - dn) << 1))) | ((FIT(timing->setup, 1, 4) - 1) << ((3 - dn) << 1));
57
	pci_write_config_byte(dev, AMD_ADDRESS_SETUP + offset, t);
Linus Torvalds's avatar
Linus Torvalds committed
58

59
	pci_write_config_byte(dev, AMD_8BIT_TIMING + offset + (1 - (dn >> 1)),
Linus Torvalds's avatar
Linus Torvalds committed
60 61
		((FIT(timing->act8b, 1, 16) - 1) << 4) | (FIT(timing->rec8b, 1, 16) - 1));

62
	pci_write_config_byte(dev, AMD_DRIVE_TIMING + offset + (3 - dn),
Linus Torvalds's avatar
Linus Torvalds committed
63 64
		((FIT(timing->active, 1, 16) - 1) << 4) | (FIT(timing->recover, 1, 16) - 1));

65
	switch (udma_mask) {
66 67 68 69 70
	case ATA_UDMA2: t = timing->udma ? (0xc0 | (FIT(timing->udma, 2, 5) - 2)) : 0x03; break;
	case ATA_UDMA4: t = timing->udma ? (0xc0 | amd_cyc2udma[FIT(timing->udma, 2, 10)]) : 0x03; break;
	case ATA_UDMA5: t = timing->udma ? (0xc0 | amd_cyc2udma[FIT(timing->udma, 1, 10)]) : 0x03; break;
	case ATA_UDMA6: t = timing->udma ? (0xc0 | amd_cyc2udma[FIT(timing->udma, 1, 15)]) : 0x03; break;
	default: return;
Linus Torvalds's avatar
Linus Torvalds committed
71 72
	}

73
	pci_write_config_byte(dev, AMD_UDMA_TIMING + offset + (3 - dn), t);
Linus Torvalds's avatar
Linus Torvalds committed
74 75 76
}

/*
77 78
 * amd_set_drive() computes timing values and configures the chipset
 * to a desired transfer mode.  It also can be called by upper layers.
Linus Torvalds's avatar
Linus Torvalds committed
79 80
 */

81
static void amd_set_drive(ide_drive_t *drive, const u8 speed)
Linus Torvalds's avatar
Linus Torvalds committed
82
{
83
	ide_hwif_t *hwif = drive->hwif;
84
	struct pci_dev *dev = to_pci_dev(hwif->dev);
85
	ide_drive_t *peer = hwif->drives + (~drive->dn & 1);
Linus Torvalds's avatar
Linus Torvalds committed
86 87
	struct ide_timing t, p;
	int T, UT;
88
	u8 udma_mask = hwif->ultra_mask;
Linus Torvalds's avatar
Linus Torvalds committed
89 90

	T = 1000000000 / amd_clock;
91
	UT = (udma_mask == ATA_UDMA2) ? T : (T / 2);
Linus Torvalds's avatar
Linus Torvalds committed
92 93 94 95 96 97 98 99 100 101 102

	ide_timing_compute(drive, speed, &t, T, UT);

	if (peer->present) {
		ide_timing_compute(peer, peer->current_speed, &p, T, UT);
		ide_timing_merge(&p, &t, &t, IDE_TIMING_8BIT);
	}

	if (speed == XFER_UDMA_5 && amd_clock <= 33333) t.udma = 1;
	if (speed == XFER_UDMA_6 && amd_clock <= 33333) t.udma = 15;

103
	amd_set_speed(dev, drive->dn, udma_mask, &t);
Linus Torvalds's avatar
Linus Torvalds committed
104 105 106
}

/*
107
 * amd_set_pio_mode() is a callback from upper layers for PIO-only tuning.
Linus Torvalds's avatar
Linus Torvalds committed
108 109
 */

110
static void amd_set_pio_mode(ide_drive_t *drive, const u8 pio)
Linus Torvalds's avatar
Linus Torvalds committed
111
{
112
	amd_set_drive(drive, XFER_PIO_0 + pio);
Linus Torvalds's avatar
Linus Torvalds committed
113 114
}

115 116 117 118 119 120
static void __devinit amd7409_cable_detect(struct pci_dev *dev,
					   const char *name)
{
	/* no host side cable detection */
	amd_80w = 0x03;
}
Linus Torvalds's avatar
Linus Torvalds committed
121

122 123
static void __devinit amd7411_cable_detect(struct pci_dev *dev,
					   const char *name)
Linus Torvalds's avatar
Linus Torvalds committed
124 125
{
	int i;
126 127 128 129 130 131 132 133 134 135 136 137 138 139
	u32 u = 0;
	u8 t = 0, offset = amd_offset(dev);

	pci_read_config_byte(dev, AMD_CABLE_DETECT + offset, &t);
	pci_read_config_dword(dev, AMD_UDMA_TIMING + offset, &u);
	amd_80w = ((t & 0x3) ? 1 : 0) | ((t & 0xc) ? 2 : 0);
	for (i = 24; i >= 0; i -= 8)
		if (((u >> i) & 4) && !(amd_80w & (1 << (1 - (i >> 4))))) {
			printk(KERN_WARNING "%s: BIOS didn't set cable bits "
					    "correctly. Enabling workaround.\n",
					    name);
			amd_80w |= (1 << (1 - (i >> 4)));
		}
}
Linus Torvalds's avatar
Linus Torvalds committed
140 141

/*
142
 * The initialization callback.  Initialize drive independent registers.
Linus Torvalds's avatar
Linus Torvalds committed
143 144
 */

145 146 147 148
static unsigned int __devinit init_chipset_amd74xx(struct pci_dev *dev,
						   const char *name)
{
	u8 t = 0, offset = amd_offset(dev);
Linus Torvalds's avatar
Linus Torvalds committed
149 150 151 152 153

/*
 * Check 80-wire cable presence.
 */

154 155 156 157 158 159 160 161
	if (dev->vendor == PCI_VENDOR_ID_AMD &&
	    dev->device == PCI_DEVICE_ID_AMD_COBRA_7401)
		; /* no UDMA > 2 */
	else if (dev->vendor == PCI_VENDOR_ID_AMD &&
		 dev->device == PCI_DEVICE_ID_AMD_VIPER_7409)
		amd7409_cable_detect(dev, name);
	else
		amd7411_cable_detect(dev, name);
Linus Torvalds's avatar
Linus Torvalds committed
162 163 164 165 166

/*
 * Take care of prefetch & postwrite.
 */

167 168 169 170 171 172 173 174 175 176
	pci_read_config_byte(dev, AMD_IDE_CONFIG + offset, &t);
	/*
	 * Check for broken FIFO support.
	 */
	if (dev->vendor == PCI_VENDOR_ID_AMD &&
	    dev->vendor == PCI_DEVICE_ID_AMD_VIPER_7411)
		t &= 0x0f;
	else
		t |= 0xf0;
	pci_write_config_byte(dev, AMD_IDE_CONFIG + offset, t);
Linus Torvalds's avatar
Linus Torvalds committed
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191

/*
 * Determine the system bus clock.
 */

	amd_clock = system_bus_clock() * 1000;

	switch (amd_clock) {
		case 33000: amd_clock = 33333; break;
		case 37000: amd_clock = 37500; break;
		case 41000: amd_clock = 41666; break;
	}

	if (amd_clock < 20000 || amd_clock > 50000) {
		printk(KERN_WARNING "%s: User given PCI clock speed impossible (%d), using 33 MHz instead.\n",
192
				    name, amd_clock);
Linus Torvalds's avatar
Linus Torvalds committed
193 194 195 196 197 198
		amd_clock = 33333;
	}

	return dev->irq;
}

199 200 201 202 203 204 205 206
static u8 __devinit amd_cable_detect(ide_hwif_t *hwif)
{
	if ((amd_80w >> hwif->channel) & 1)
		return ATA_CBL_PATA80;
	else
		return ATA_CBL_PATA40;
}

207
static void __devinit init_hwif_amd74xx(ide_hwif_t *hwif)
Linus Torvalds's avatar
Linus Torvalds committed
208
{
209 210
	struct pci_dev *dev = to_pci_dev(hwif->dev);

Linus Torvalds's avatar
Linus Torvalds committed
211
	if (hwif->irq == 0) /* 0 is bogus but will do for now */
212
		hwif->irq = pci_get_legacy_ide_irq(dev, hwif->channel);
Linus Torvalds's avatar
Linus Torvalds committed
213

214
	hwif->set_pio_mode = &amd_set_pio_mode;
215
	hwif->set_dma_mode = &amd_set_drive;
Linus Torvalds's avatar
Linus Torvalds committed
216

217
	hwif->cable_detect = amd_cable_detect;
Linus Torvalds's avatar
Linus Torvalds committed
218 219
}

220 221 222
#define IDE_HFLAGS_AMD \
	(IDE_HFLAG_PIO_NO_BLACKLIST | \
	 IDE_HFLAG_PIO_NO_DOWNGRADE | \
223
	 IDE_HFLAG_ABUSE_SET_DMA_MODE | \
224 225 226 227 228
	 IDE_HFLAG_POST_SET_MODE | \
	 IDE_HFLAG_IO_32BIT | \
	 IDE_HFLAG_UNMASK_IRQS | \
	 IDE_HFLAG_BOOTABLE)

229
#define DECLARE_AMD_DEV(name_str, swdma, udma)				\
Linus Torvalds's avatar
Linus Torvalds committed
230 231 232 233 234
	{								\
		.name		= name_str,				\
		.init_chipset	= init_chipset_amd74xx,			\
		.init_hwif	= init_hwif_amd74xx,			\
		.enablebits	= {{0x40,0x02,0x02}, {0x40,0x01,0x01}},	\
235
		.host_flags	= IDE_HFLAGS_AMD,			\
Bartlomiej Zolnierkiewicz's avatar
Bartlomiej Zolnierkiewicz committed
236
		.pio_mask	= ATA_PIO5,				\
237
		.swdma_mask	= swdma,				\
238
		.mwdma_mask	= ATA_MWDMA2,				\
239
		.udma_mask	= udma,					\
Linus Torvalds's avatar
Linus Torvalds committed
240 241
	}

242
#define DECLARE_NV_DEV(name_str, udma)					\
Linus Torvalds's avatar
Linus Torvalds committed
243 244 245 246 247
	{								\
		.name		= name_str,				\
		.init_chipset	= init_chipset_amd74xx,			\
		.init_hwif	= init_hwif_amd74xx,			\
		.enablebits	= {{0x50,0x02,0x02}, {0x50,0x01,0x01}},	\
248
		.host_flags	= IDE_HFLAGS_AMD,			\
Bartlomiej Zolnierkiewicz's avatar
Bartlomiej Zolnierkiewicz committed
249
		.pio_mask	= ATA_PIO5,				\
250 251
		.swdma_mask	= ATA_SWDMA2,				\
		.mwdma_mask	= ATA_MWDMA2,				\
252
		.udma_mask	= udma,					\
Linus Torvalds's avatar
Linus Torvalds committed
253 254
	}

255
static const struct ide_port_info amd74xx_chipsets[] __devinitdata = {
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
	/*  0 */ DECLARE_AMD_DEV("AMD7401",	  0x00, ATA_UDMA2),
	/*  1 */ DECLARE_AMD_DEV("AMD7409", ATA_SWDMA2, ATA_UDMA4),
	/*  2 */ DECLARE_AMD_DEV("AMD7411", ATA_SWDMA2, ATA_UDMA5),
	/*  3 */ DECLARE_AMD_DEV("AMD7441", ATA_SWDMA2, ATA_UDMA5),
	/*  4 */ DECLARE_AMD_DEV("AMD8111", ATA_SWDMA2, ATA_UDMA6),

	/*  5 */ DECLARE_NV_DEV("NFORCE",		ATA_UDMA5),
	/*  6 */ DECLARE_NV_DEV("NFORCE2",		ATA_UDMA6),
	/*  7 */ DECLARE_NV_DEV("NFORCE2-U400R",	ATA_UDMA6),
	/*  8 */ DECLARE_NV_DEV("NFORCE2-U400R-SATA",	ATA_UDMA6),
	/*  9 */ DECLARE_NV_DEV("NFORCE3-150",		ATA_UDMA6),
	/* 10 */ DECLARE_NV_DEV("NFORCE3-250",		ATA_UDMA6),
	/* 11 */ DECLARE_NV_DEV("NFORCE3-250-SATA",	ATA_UDMA6),
	/* 12 */ DECLARE_NV_DEV("NFORCE3-250-SATA2",	ATA_UDMA6),
	/* 13 */ DECLARE_NV_DEV("NFORCE-CK804",		ATA_UDMA6),
	/* 14 */ DECLARE_NV_DEV("NFORCE-MCP04",		ATA_UDMA6),
	/* 15 */ DECLARE_NV_DEV("NFORCE-MCP51",		ATA_UDMA6),
	/* 16 */ DECLARE_NV_DEV("NFORCE-MCP55",		ATA_UDMA6),
	/* 17 */ DECLARE_NV_DEV("NFORCE-MCP61",		ATA_UDMA6),
	/* 18 */ DECLARE_NV_DEV("NFORCE-MCP65",		ATA_UDMA6),
	/* 19 */ DECLARE_NV_DEV("NFORCE-MCP67",		ATA_UDMA6),
	/* 20 */ DECLARE_NV_DEV("NFORCE-MCP73",		ATA_UDMA6),
	/* 21 */ DECLARE_NV_DEV("NFORCE-MCP77",		ATA_UDMA6),

	/* 22 */ DECLARE_AMD_DEV("AMD5536", ATA_SWDMA2, ATA_UDMA5),
Linus Torvalds's avatar
Linus Torvalds committed
281 282 283 284
};

static int __devinit amd74xx_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
285 286 287 288 289 290 291 292 293 294 295
	struct ide_port_info d;
	u8 idx = id->driver_data;

	d = amd74xx_chipsets[idx];

	/*
	 * Check for bad SWDMA and incorrectly wired Serenade mainboards.
	 */
	if (idx == 1) {
		if (dev->revision <= 7)
			d.swdma_mask = 0;
296
		d.host_flags |= IDE_HFLAG_CLEAR_SIMPLEX;
297 298 299 300
	} else if (idx == 4) {
		if (dev->subsystem_vendor == PCI_VENDOR_ID_AMD &&
		    dev->subsystem_device == PCI_DEVICE_ID_AMD_SERENADE)
			d.udma_mask = ATA_UDMA5;
Linus Torvalds's avatar
Linus Torvalds committed
301
	}
302 303 304 305 306 307

	printk(KERN_INFO "%s: %s (rev %02x) UDMA%s controller\n",
			 d.name, pci_name(dev), dev->revision,
			 amd_dma[fls(d.udma_mask) - 1]);

	return ide_setup_pci_device(dev, &d);
Linus Torvalds's avatar
Linus Torvalds committed
308 309
}

310 311 312 313 314 315 316 317 318
static const struct pci_device_id amd74xx_pci_tbl[] = {
	{ PCI_VDEVICE(AMD,	PCI_DEVICE_ID_AMD_COBRA_7401),		 0 },
	{ PCI_VDEVICE(AMD,	PCI_DEVICE_ID_AMD_VIPER_7409),		 1 },
	{ PCI_VDEVICE(AMD,	PCI_DEVICE_ID_AMD_VIPER_7411),		 2 },
	{ PCI_VDEVICE(AMD,	PCI_DEVICE_ID_AMD_OPUS_7441),		 3 },
	{ PCI_VDEVICE(AMD,	PCI_DEVICE_ID_AMD_8111_IDE),		 4 },
	{ PCI_VDEVICE(NVIDIA,	PCI_DEVICE_ID_NVIDIA_NFORCE_IDE),	 5 },
	{ PCI_VDEVICE(NVIDIA,	PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE),	 6 },
	{ PCI_VDEVICE(NVIDIA,	PCI_DEVICE_ID_NVIDIA_NFORCE2S_IDE),	 7 },
Linus Torvalds's avatar
Linus Torvalds committed
319
#ifdef CONFIG_BLK_DEV_IDE_SATA
320
	{ PCI_VDEVICE(NVIDIA,	PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA),	 8 },
Linus Torvalds's avatar
Linus Torvalds committed
321
#endif
322 323
	{ PCI_VDEVICE(NVIDIA,	PCI_DEVICE_ID_NVIDIA_NFORCE3_IDE),	 9 },
	{ PCI_VDEVICE(NVIDIA,	PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE),	10 },
Linus Torvalds's avatar
Linus Torvalds committed
324
#ifdef CONFIG_BLK_DEV_IDE_SATA
325 326
	{ PCI_VDEVICE(NVIDIA,	PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA),	11 },
	{ PCI_VDEVICE(NVIDIA,	PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2),	12 },
Linus Torvalds's avatar
Linus Torvalds committed
327
#endif
328 329 330 331 332 333 334 335 336 337
	{ PCI_VDEVICE(NVIDIA,	PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE),	13 },
	{ PCI_VDEVICE(NVIDIA,	PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE),	14 },
	{ PCI_VDEVICE(NVIDIA,	PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE),	15 },
	{ PCI_VDEVICE(NVIDIA,	PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE),	16 },
	{ PCI_VDEVICE(NVIDIA,	PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_IDE),	17 },
	{ PCI_VDEVICE(NVIDIA,	PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_IDE),	18 },
	{ PCI_VDEVICE(NVIDIA,	PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_IDE),	19 },
	{ PCI_VDEVICE(NVIDIA,	PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_IDE),	20 },
	{ PCI_VDEVICE(NVIDIA,	PCI_DEVICE_ID_NVIDIA_NFORCE_MCP77_IDE),	21 },
	{ PCI_VDEVICE(AMD,	PCI_DEVICE_ID_AMD_CS5536_IDE),		22 },
Linus Torvalds's avatar
Linus Torvalds committed
338 339 340 341 342 343 344 345 346 347
	{ 0, },
};
MODULE_DEVICE_TABLE(pci, amd74xx_pci_tbl);

static struct pci_driver driver = {
	.name		= "AMD_IDE",
	.id_table	= amd74xx_pci_tbl,
	.probe		= amd74xx_probe,
};

348
static int __init amd74xx_ide_init(void)
Linus Torvalds's avatar
Linus Torvalds committed
349 350 351 352 353 354 355 356 357
{
	return ide_pci_register_driver(&driver);
}

module_init(amd74xx_ide_init);

MODULE_AUTHOR("Vojtech Pavlik");
MODULE_DESCRIPTION("AMD PCI IDE driver");
MODULE_LICENSE("GPL");