aec62xx.c 9.2 KB
Newer Older
L
Linus Torvalds 已提交
1 2
/*
 * Copyright (C) 1999-2002	Andre Hedrick <andre@linux-ide.org>
3
 * Copyright (C) 2007		MontaVista Software, Inc. <source@mvista.com>
L
Linus Torvalds 已提交
4 5 6 7 8 9 10 11 12 13 14
 *
 */

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

#include <asm/io.h>

15 16
#define DRV_NAME "aec62xx"

L
Linus Torvalds 已提交
17 18 19 20 21 22
struct chipset_bus_clock_list_entry {
	u8 xfer_speed;
	u8 chipset_settings;
	u8 ultra_settings;
};

23
static const struct chipset_bus_clock_list_entry aec6xxx_33_base [] = {
L
Linus Torvalds 已提交
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
	{	XFER_UDMA_6,	0x31,	0x07	},
	{	XFER_UDMA_5,	0x31,	0x06	},
	{	XFER_UDMA_4,	0x31,	0x05	},
	{	XFER_UDMA_3,	0x31,	0x04	},
	{	XFER_UDMA_2,	0x31,	0x03	},
	{	XFER_UDMA_1,	0x31,	0x02	},
	{	XFER_UDMA_0,	0x31,	0x01	},

	{	XFER_MW_DMA_2,	0x31,	0x00	},
	{	XFER_MW_DMA_1,	0x31,	0x00	},
	{	XFER_MW_DMA_0,	0x0a,	0x00	},
	{	XFER_PIO_4,	0x31,	0x00	},
	{	XFER_PIO_3,	0x33,	0x00	},
	{	XFER_PIO_2,	0x08,	0x00	},
	{	XFER_PIO_1,	0x0a,	0x00	},
	{	XFER_PIO_0,	0x00,	0x00	},
	{	0,		0x00,	0x00	}
};

43
static const struct chipset_bus_clock_list_entry aec6xxx_34_base [] = {
L
Linus Torvalds 已提交
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
	{	XFER_UDMA_6,	0x41,	0x06	},
	{	XFER_UDMA_5,	0x41,	0x05	},
	{	XFER_UDMA_4,	0x41,	0x04	},
	{	XFER_UDMA_3,	0x41,	0x03	},
	{	XFER_UDMA_2,	0x41,	0x02	},
	{	XFER_UDMA_1,	0x41,	0x01	},
	{	XFER_UDMA_0,	0x41,	0x01	},

	{	XFER_MW_DMA_2,	0x41,	0x00	},
	{	XFER_MW_DMA_1,	0x42,	0x00	},
	{	XFER_MW_DMA_0,	0x7a,	0x00	},
	{	XFER_PIO_4,	0x41,	0x00	},
	{	XFER_PIO_3,	0x43,	0x00	},
	{	XFER_PIO_2,	0x78,	0x00	},
	{	XFER_PIO_1,	0x7a,	0x00	},
	{	XFER_PIO_0,	0x70,	0x00	},
	{	0,		0x00,	0x00	}
};

/*
 * TO DO: active tuning and correction of cards without a bios.
 */
static u8 pci_bus_clock_list (u8 speed, struct chipset_bus_clock_list_entry * chipset_table)
{
	for ( ; chipset_table->xfer_speed ; chipset_table++)
		if (chipset_table->xfer_speed == speed) {
			return chipset_table->chipset_settings;
		}
	return chipset_table->chipset_settings;
}

static u8 pci_bus_clock_list_ultra (u8 speed, struct chipset_bus_clock_list_entry * chipset_table)
{
	for ( ; chipset_table->xfer_speed ; chipset_table++)
		if (chipset_table->xfer_speed == speed) {
			return chipset_table->ultra_settings;
		}
	return chipset_table->ultra_settings;
}

84
static void aec6210_set_mode(ide_drive_t *drive, const u8 speed)
L
Linus Torvalds 已提交
85
{
86
	ide_hwif_t *hwif	= drive->hwif;
87
	struct pci_dev *dev	= to_pci_dev(hwif->dev);
88 89
	struct ide_host *host	= pci_get_drvdata(dev);
	struct chipset_bus_clock_list_entry *bus_clock = host->host_priv;
L
Linus Torvalds 已提交
90 91 92 93 94 95 96 97
	u16 d_conf		= 0;
	u8 ultra = 0, ultra_conf = 0;
	u8 tmp0 = 0, tmp1 = 0, tmp2 = 0;
	unsigned long flags;

	local_irq_save(flags);
	/* 0x40|(2*drive->dn): Active, 0x41|(2*drive->dn): Recovery */
	pci_read_config_word(dev, 0x40|(2*drive->dn), &d_conf);
98
	tmp0 = pci_bus_clock_list(speed, bus_clock);
L
Linus Torvalds 已提交
99 100 101 102 103 104 105
	d_conf = ((tmp0 & 0xf0) << 4) | (tmp0 & 0xf);
	pci_write_config_word(dev, 0x40|(2*drive->dn), d_conf);

	tmp1 = 0x00;
	tmp2 = 0x00;
	pci_read_config_byte(dev, 0x54, &ultra);
	tmp1 = ((0x00 << (2*drive->dn)) | (ultra & ~(3 << (2*drive->dn))));
106
	ultra_conf = pci_bus_clock_list_ultra(speed, bus_clock);
L
Linus Torvalds 已提交
107 108 109 110 111
	tmp2 = ((ultra_conf << (2*drive->dn)) | (tmp1 & ~(3 << (2*drive->dn))));
	pci_write_config_byte(dev, 0x54, tmp2);
	local_irq_restore(flags);
}

112
static void aec6260_set_mode(ide_drive_t *drive, const u8 speed)
L
Linus Torvalds 已提交
113
{
114
	ide_hwif_t *hwif	= drive->hwif;
115
	struct pci_dev *dev	= to_pci_dev(hwif->dev);
116 117
	struct ide_host *host	= pci_get_drvdata(dev);
	struct chipset_bus_clock_list_entry *bus_clock = host->host_priv;
118
	u8 unit			= drive->dn & 1;
L
Linus Torvalds 已提交
119 120 121 122 123 124 125
	u8 tmp1 = 0, tmp2 = 0;
	u8 ultra = 0, drive_conf = 0, ultra_conf = 0;
	unsigned long flags;

	local_irq_save(flags);
	/* high 4-bits: Active, low 4-bits: Recovery */
	pci_read_config_byte(dev, 0x40|drive->dn, &drive_conf);
126
	drive_conf = pci_bus_clock_list(speed, bus_clock);
L
Linus Torvalds 已提交
127 128 129 130
	pci_write_config_byte(dev, 0x40|drive->dn, drive_conf);

	pci_read_config_byte(dev, (0x44|hwif->channel), &ultra);
	tmp1 = ((0x00 << (4*unit)) | (ultra & ~(7 << (4*unit))));
131
	ultra_conf = pci_bus_clock_list_ultra(speed, bus_clock);
L
Linus Torvalds 已提交
132 133 134 135 136
	tmp2 = ((ultra_conf << (4*unit)) | (tmp1 & ~(7 << (4*unit))));
	pci_write_config_byte(dev, (0x44|hwif->channel), tmp2);
	local_irq_restore(flags);
}

137
static void aec_set_pio_mode(ide_drive_t *drive, const u8 pio)
L
Linus Torvalds 已提交
138
{
139
	drive->dma_mode = pio + XFER_PIO_0;
140
	drive->hwif->port_ops->set_dma_mode(drive, pio + XFER_PIO_0);
L
Linus Torvalds 已提交
141 142
}

143
static int init_chipset_aec62xx(struct pci_dev *dev)
L
Linus Torvalds 已提交
144
{
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
	/* These are necessary to get AEC6280 Macintosh cards to work */
	if ((dev->device == PCI_DEVICE_ID_ARTOP_ATP865) ||
	    (dev->device == PCI_DEVICE_ID_ARTOP_ATP865R)) {
		u8 reg49h = 0, reg4ah = 0;
		/* Clear reset and test bits.  */
		pci_read_config_byte(dev, 0x49, &reg49h);
		pci_write_config_byte(dev, 0x49, reg49h & ~0x30);
		/* Enable chip interrupt output.  */
		pci_read_config_byte(dev, 0x4a, &reg4ah);
		pci_write_config_byte(dev, 0x4a, reg4ah & ~0x01);
		/* Enable burst mode. */
		pci_read_config_byte(dev, 0x4a, &reg4ah);
		pci_write_config_byte(dev, 0x4a, reg4ah | 0x80);
	}

160
	return 0;
L
Linus Torvalds 已提交
161 162
}

163
static u8 atp86x_cable_detect(ide_hwif_t *hwif)
164 165 166 167 168 169 170 171 172
{
	struct pci_dev *dev = to_pci_dev(hwif->dev);
	u8 ata66 = 0, mask = hwif->channel ? 0x02 : 0x01;

	pci_read_config_byte(dev, 0x49, &ata66);

	return (ata66 & mask) ? ATA_CBL_PATA40 : ATA_CBL_PATA80;
}

173 174 175 176
static const struct ide_port_ops atp850_port_ops = {
	.set_pio_mode		= aec_set_pio_mode,
	.set_dma_mode		= aec6210_set_mode,
};
L
Linus Torvalds 已提交
177

178 179 180 181 182
static const struct ide_port_ops atp86x_port_ops = {
	.set_pio_mode		= aec_set_pio_mode,
	.set_dma_mode		= aec6260_set_mode,
	.cable_detect		= atp86x_cable_detect,
};
L
Linus Torvalds 已提交
183

184
static const struct ide_port_info aec62xx_chipsets[] __devinitdata = {
185 186
	{	/* 0: AEC6210 */
		.name		= DRV_NAME,
L
Linus Torvalds 已提交
187 188
		.init_chipset	= init_chipset_aec62xx,
		.enablebits	= {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}},
189
		.port_ops	= &atp850_port_ops,
190 191
		.host_flags	= IDE_HFLAG_SERIALIZE |
				  IDE_HFLAG_NO_ATAPI_DMA |
192
				  IDE_HFLAG_NO_DSC |
193
				  IDE_HFLAG_OFF_BOARD,
B
Bartlomiej Zolnierkiewicz 已提交
194
		.pio_mask	= ATA_PIO4,
195 196
		.mwdma_mask	= ATA_MWDMA2,
		.udma_mask	= ATA_UDMA2,
197 198 199
	},
	{	/* 1: AEC6260 */
		.name		= DRV_NAME,
L
Linus Torvalds 已提交
200
		.init_chipset	= init_chipset_aec62xx,
201
		.port_ops	= &atp86x_port_ops,
202 203
		.host_flags	= IDE_HFLAG_NO_ATAPI_DMA | IDE_HFLAG_NO_AUTODMA |
				  IDE_HFLAG_OFF_BOARD,
B
Bartlomiej Zolnierkiewicz 已提交
204
		.pio_mask	= ATA_PIO4,
205 206
		.mwdma_mask	= ATA_MWDMA2,
		.udma_mask	= ATA_UDMA4,
207 208 209
	},
	{	/* 2: AEC6260R */
		.name		= DRV_NAME,
L
Linus Torvalds 已提交
210 211
		.init_chipset	= init_chipset_aec62xx,
		.enablebits	= {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}},
212
		.port_ops	= &atp86x_port_ops,
213
		.host_flags	= IDE_HFLAG_NO_ATAPI_DMA |
214
				  IDE_HFLAG_NON_BOOTABLE,
B
Bartlomiej Zolnierkiewicz 已提交
215
		.pio_mask	= ATA_PIO4,
216 217
		.mwdma_mask	= ATA_MWDMA2,
		.udma_mask	= ATA_UDMA4,
218 219 220
	},
	{	/* 3: AEC6280 */
		.name		= DRV_NAME,
L
Linus Torvalds 已提交
221
		.init_chipset	= init_chipset_aec62xx,
222
		.port_ops	= &atp86x_port_ops,
223 224
		.host_flags	= IDE_HFLAG_NO_ATAPI_DMA |
				  IDE_HFLAG_OFF_BOARD,
B
Bartlomiej Zolnierkiewicz 已提交
225
		.pio_mask	= ATA_PIO4,
226 227
		.mwdma_mask	= ATA_MWDMA2,
		.udma_mask	= ATA_UDMA5,
228 229 230
	},
	{	/* 4: AEC6280R */
		.name		= DRV_NAME,
L
Linus Torvalds 已提交
231 232
		.init_chipset	= init_chipset_aec62xx,
		.enablebits	= {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}},
233
		.port_ops	= &atp86x_port_ops,
234 235
		.host_flags	= IDE_HFLAG_NO_ATAPI_DMA |
				  IDE_HFLAG_OFF_BOARD,
B
Bartlomiej Zolnierkiewicz 已提交
236
		.pio_mask	= ATA_PIO4,
237 238
		.mwdma_mask	= ATA_MWDMA2,
		.udma_mask	= ATA_UDMA5,
L
Linus Torvalds 已提交
239 240 241 242 243 244 245 246 247 248
	}
};

/**
 *	aec62xx_init_one	-	called when a AEC is found
 *	@dev: the aec62xx device
 *	@id: the matching pci id
 *
 *	Called when the PCI registration layer (or the IDE initialization)
 *	finds a device matching our IDE device tables.
249 250
 *
 *	NOTE: since we're going to modify the 'name' field for AEC-6[26]80[R]
251
 *	chips, pass a local copy of 'struct ide_port_info' down the call chain.
L
Linus Torvalds 已提交
252
 */
253

L
Linus Torvalds 已提交
254 255
static int __devinit aec62xx_init_one(struct pci_dev *dev, const struct pci_device_id *id)
{
256
	const struct chipset_bus_clock_list_entry *bus_clock;
257
	struct ide_port_info d;
258
	u8 idx = id->driver_data;
259
	int bus_speed = ide_pci_clk ? ide_pci_clk : 33;
260 261
	int err;

262 263 264 265 266
	if (bus_speed <= 33)
		bus_clock = aec6xxx_33_base;
	else
		bus_clock = aec6xxx_34_base;

267 268 269
	err = pci_enable_device(dev);
	if (err)
		return err;
270 271 272 273 274 275 276

	d = aec62xx_chipsets[idx];

	if (idx == 3 || idx == 4) {
		unsigned long dma_base = pci_resource_start(dev, 4);

		if (inb(dma_base + 2) & 0x10) {
277 278
			printk(KERN_INFO DRV_NAME " %s: AEC6880%s card detected"
				"\n", pci_name(dev), (idx == 4) ? "R" : "");
279 280 281
			d.udma_mask = ATA_UDMA6;
		}
	}
L
Linus Torvalds 已提交
282

283
	err = ide_pci_init_one(dev, &d, (void *)bus_clock);
284 285 286 287
	if (err)
		pci_disable_device(dev);

	return err;
L
Linus Torvalds 已提交
288 289
}

290 291 292 293 294 295
static void __devexit aec62xx_remove(struct pci_dev *dev)
{
	ide_pci_remove(dev);
	pci_disable_device(dev);
}

296 297 298 299 300 301
static const struct pci_device_id aec62xx_pci_tbl[] = {
	{ PCI_VDEVICE(ARTOP, PCI_DEVICE_ID_ARTOP_ATP850UF), 0 },
	{ PCI_VDEVICE(ARTOP, PCI_DEVICE_ID_ARTOP_ATP860),   1 },
	{ PCI_VDEVICE(ARTOP, PCI_DEVICE_ID_ARTOP_ATP860R),  2 },
	{ PCI_VDEVICE(ARTOP, PCI_DEVICE_ID_ARTOP_ATP865),   3 },
	{ PCI_VDEVICE(ARTOP, PCI_DEVICE_ID_ARTOP_ATP865R),  4 },
L
Linus Torvalds 已提交
302 303 304 305
	{ 0, },
};
MODULE_DEVICE_TABLE(pci, aec62xx_pci_tbl);

306
static struct pci_driver aec62xx_pci_driver = {
L
Linus Torvalds 已提交
307 308 309
	.name		= "AEC62xx_IDE",
	.id_table	= aec62xx_pci_tbl,
	.probe		= aec62xx_init_one,
310
	.remove		= __devexit_p(aec62xx_remove),
311 312
	.suspend	= ide_pci_suspend,
	.resume		= ide_pci_resume,
L
Linus Torvalds 已提交
313 314
};

315
static int __init aec62xx_ide_init(void)
L
Linus Torvalds 已提交
316
{
317
	return ide_pci_register_driver(&aec62xx_pci_driver);
L
Linus Torvalds 已提交
318 319
}

320 321
static void __exit aec62xx_ide_exit(void)
{
322
	pci_unregister_driver(&aec62xx_pci_driver);
323 324
}

L
Linus Torvalds 已提交
325
module_init(aec62xx_ide_init);
326
module_exit(aec62xx_ide_exit);
L
Linus Torvalds 已提交
327 328 329 330

MODULE_AUTHOR("Andre Hedrick");
MODULE_DESCRIPTION("PCI driver module for ARTOP AEC62xx IDE");
MODULE_LICENSE("GPL");