aec62xx.c 9.2 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
/*
 * Copyright (C) 1999-2002	Andre Hedrick <andre@linux-ide.org>
3
 * Copyright (C) 2007		MontaVista Software, Inc. <source@mvista.com>
Linus Torvalds's avatar
Linus Torvalds committed
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"

Linus Torvalds's avatar
Linus Torvalds committed
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 [] = {
Linus Torvalds's avatar
Linus Torvalds committed
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 [] = {
Linus Torvalds's avatar
Linus Torvalds committed
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_hwif_t *hwif, ide_drive_t *drive)
Linus Torvalds's avatar
Linus Torvalds committed
85
{
86
	struct pci_dev *dev	= to_pci_dev(hwif->dev);
87
88
	struct ide_host *host	= pci_get_drvdata(dev);
	struct chipset_bus_clock_list_entry *bus_clock = host->host_priv;
Linus Torvalds's avatar
Linus Torvalds committed
89
90
91
	u16 d_conf		= 0;
	u8 ultra = 0, ultra_conf = 0;
	u8 tmp0 = 0, tmp1 = 0, tmp2 = 0;
92
	const u8 speed = drive->dma_mode;
Linus Torvalds's avatar
Linus Torvalds committed
93
94
95
96
97
	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);
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
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_hwif_t *hwif, ide_drive_t *drive)
Linus Torvalds's avatar
Linus Torvalds committed
113
{
114
	struct pci_dev *dev	= to_pci_dev(hwif->dev);
115
116
	struct ide_host *host	= pci_get_drvdata(dev);
	struct chipset_bus_clock_list_entry *bus_clock = host->host_priv;
117
	u8 unit			= drive->dn & 1;
Linus Torvalds's avatar
Linus Torvalds committed
118
119
	u8 tmp1 = 0, tmp2 = 0;
	u8 ultra = 0, drive_conf = 0, ultra_conf = 0;
120
	const u8 speed = drive->dma_mode;
Linus Torvalds's avatar
Linus Torvalds committed
121
122
123
124
125
	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);
Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
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_hwif_t *hwif, ide_drive_t *drive)
Linus Torvalds's avatar
Linus Torvalds committed
138
{
139
	drive->dma_mode = drive->pio_mode;
140
	hwif->port_ops->set_dma_mode(hwif, drive);
Linus Torvalds's avatar
Linus Torvalds committed
141
142
}

143
static int init_chipset_aec62xx(struct pci_dev *dev)
Linus Torvalds's avatar
Linus Torvalds committed
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;
Linus Torvalds's avatar
Linus Torvalds committed
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,
};
Linus Torvalds's avatar
Linus Torvalds committed
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,
};
Linus Torvalds's avatar
Linus Torvalds committed
183

184
static const struct ide_port_info aec62xx_chipsets[] __devinitdata = {
185
186
	{	/* 0: AEC6210 */
		.name		= DRV_NAME,
Linus Torvalds's avatar
Linus Torvalds committed
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,
Bartlomiej Zolnierkiewicz's avatar
Bartlomiej Zolnierkiewicz committed
194
		.pio_mask	= ATA_PIO4,
195
196
		.mwdma_mask	= ATA_MWDMA2,
		.udma_mask	= ATA_UDMA2,
197
198
199
	},
	{	/* 1: AEC6260 */
		.name		= DRV_NAME,
Linus Torvalds's avatar
Linus Torvalds committed
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,
Bartlomiej Zolnierkiewicz's avatar
Bartlomiej Zolnierkiewicz committed
204
		.pio_mask	= ATA_PIO4,
205
206
		.mwdma_mask	= ATA_MWDMA2,
		.udma_mask	= ATA_UDMA4,
207
208
209
	},
	{	/* 2: AEC6260R */
		.name		= DRV_NAME,
Linus Torvalds's avatar
Linus Torvalds committed
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,
Bartlomiej Zolnierkiewicz's avatar
Bartlomiej Zolnierkiewicz committed
215
		.pio_mask	= ATA_PIO4,
216
217
		.mwdma_mask	= ATA_MWDMA2,
		.udma_mask	= ATA_UDMA4,
218
219
220
	},
	{	/* 3: AEC6280 */
		.name		= DRV_NAME,
Linus Torvalds's avatar
Linus Torvalds committed
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,
Bartlomiej Zolnierkiewicz's avatar
Bartlomiej Zolnierkiewicz committed
225
		.pio_mask	= ATA_PIO4,
226
227
		.mwdma_mask	= ATA_MWDMA2,
		.udma_mask	= ATA_UDMA5,
228
229
230
	},
	{	/* 4: AEC6280R */
		.name		= DRV_NAME,
Linus Torvalds's avatar
Linus Torvalds committed
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,
Bartlomiej Zolnierkiewicz's avatar
Bartlomiej Zolnierkiewicz committed
236
		.pio_mask	= ATA_PIO4,
237
238
		.mwdma_mask	= ATA_MWDMA2,
		.udma_mask	= ATA_UDMA5,
Linus Torvalds's avatar
Linus Torvalds committed
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.
Linus Torvalds's avatar
Linus Torvalds committed
252
 */
253

Linus Torvalds's avatar
Linus Torvalds committed
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;
		}
	}
Linus Torvalds's avatar
Linus Torvalds committed
282

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

	return err;
Linus Torvalds's avatar
Linus Torvalds committed
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 },
Linus Torvalds's avatar
Linus Torvalds committed
302
303
304
305
	{ 0, },
};
MODULE_DEVICE_TABLE(pci, aec62xx_pci_tbl);

306
static struct pci_driver aec62xx_pci_driver = {
Linus Torvalds's avatar
Linus Torvalds committed
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,
Linus Torvalds's avatar
Linus Torvalds committed
313
314
};

315
static int __init aec62xx_ide_init(void)
Linus Torvalds's avatar
Linus Torvalds committed
316
{
317
	return ide_pci_register_driver(&aec62xx_pci_driver);
Linus Torvalds's avatar
Linus Torvalds committed
318
319
}

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

Linus Torvalds's avatar
Linus Torvalds committed
325
module_init(aec62xx_ide_init);
326
module_exit(aec62xx_ide_exit);
Linus Torvalds's avatar
Linus Torvalds committed
327
328
329
330

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