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.

atiixp.c 5.6 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2
/*
 *  Copyright (C) 2003 ATI Inc. <hyu@ati.com>
3
 *  Copyright (C) 2004,2007 Bartlomiej Zolnierkiewicz
Linus Torvalds's avatar
Linus Torvalds committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
 */

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

#define ATIIXP_IDE_PIO_TIMING		0x40
#define ATIIXP_IDE_MDMA_TIMING		0x44
#define ATIIXP_IDE_PIO_CONTROL		0x48
#define ATIIXP_IDE_PIO_MODE		0x4a
#define ATIIXP_IDE_UDMA_CONTROL		0x54
#define ATIIXP_IDE_UDMA_MODE		0x56

typedef struct {
	u8 command_width;
	u8 recover_width;
} atiixp_ide_timing;

static atiixp_ide_timing pio_timing[] = {
	{ 0x05, 0x0d },
	{ 0x04, 0x07 },
	{ 0x03, 0x04 },
	{ 0x02, 0x02 },
	{ 0x02, 0x00 },
};

static atiixp_ide_timing mdma_timing[] = {
	{ 0x07, 0x07 },
	{ 0x02, 0x01 },
	{ 0x02, 0x00 },
};

40 41
static DEFINE_SPINLOCK(atiixp_lock);

Linus Torvalds's avatar
Linus Torvalds committed
42
/**
43 44 45
 *	atiixp_set_pio_mode	-	set host controller for PIO mode
 *	@drive: drive
 *	@pio: PIO mode number
Linus Torvalds's avatar
Linus Torvalds committed
46 47 48 49
 *
 *	Set the interface PIO mode.
 */

50
static void atiixp_set_pio_mode(ide_drive_t *drive, const u8 pio)
Linus Torvalds's avatar
Linus Torvalds committed
51
{
52
	struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
Linus Torvalds's avatar
Linus Torvalds committed
53 54 55 56 57
	unsigned long flags;
	int timing_shift = (drive->dn & 2) ? 16 : 0 + (drive->dn & 1) ? 0 : 8;
	u32 pio_timing_data;
	u16 pio_mode_data;

58
	spin_lock_irqsave(&atiixp_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
59 60 61 62 63 64 65 66 67 68 69 70

	pci_read_config_word(dev, ATIIXP_IDE_PIO_MODE, &pio_mode_data);
	pio_mode_data &= ~(0x07 << (drive->dn * 4));
	pio_mode_data |= (pio << (drive->dn * 4));
	pci_write_config_word(dev, ATIIXP_IDE_PIO_MODE, pio_mode_data);

	pci_read_config_dword(dev, ATIIXP_IDE_PIO_TIMING, &pio_timing_data);
	pio_timing_data &= ~(0xff << timing_shift);
	pio_timing_data |= (pio_timing[pio].recover_width << timing_shift) |
		 (pio_timing[pio].command_width << (timing_shift + 4));
	pci_write_config_dword(dev, ATIIXP_IDE_PIO_TIMING, pio_timing_data);

71
	spin_unlock_irqrestore(&atiixp_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
72 73 74
}

/**
75 76 77
 *	atiixp_set_dma_mode	-	set host controller for DMA mode
 *	@drive: drive
 *	@speed: DMA mode
Linus Torvalds's avatar
Linus Torvalds committed
78
 *
79 80
 *	Set a ATIIXP host controller to the desired DMA mode.  This involves
 *	programming the right timing data into the PCI configuration space.
Linus Torvalds's avatar
Linus Torvalds committed
81 82
 */

83
static void atiixp_set_dma_mode(ide_drive_t *drive, const u8 speed)
Linus Torvalds's avatar
Linus Torvalds committed
84
{
85
	struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
Linus Torvalds's avatar
Linus Torvalds committed
86 87 88 89
	unsigned long flags;
	int timing_shift = (drive->dn & 2) ? 16 : 0 + (drive->dn & 1) ? 0 : 8;
	u32 tmp32;
	u16 tmp16;
90
	u16 udma_ctl = 0;
91

92
	spin_lock_irqsave(&atiixp_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
93

94 95
	pci_read_config_word(dev, ATIIXP_IDE_UDMA_CONTROL, &udma_ctl);

Linus Torvalds's avatar
Linus Torvalds committed
96 97 98 99 100
	if (speed >= XFER_UDMA_0) {
		pci_read_config_word(dev, ATIIXP_IDE_UDMA_MODE, &tmp16);
		tmp16 &= ~(0x07 << (drive->dn * 4));
		tmp16 |= ((speed & 0x07) << (drive->dn * 4));
		pci_write_config_word(dev, ATIIXP_IDE_UDMA_MODE, tmp16);
101 102 103 104 105 106 107 108 109 110 111 112

		udma_ctl |= (1 << drive->dn);
	} else if (speed >= XFER_MW_DMA_0) {
		u8 i = speed & 0x03;

		pci_read_config_dword(dev, ATIIXP_IDE_MDMA_TIMING, &tmp32);
		tmp32 &= ~(0xff << timing_shift);
		tmp32 |= (mdma_timing[i].recover_width << timing_shift) |
			 (mdma_timing[i].command_width << (timing_shift + 4));
		pci_write_config_dword(dev, ATIIXP_IDE_MDMA_TIMING, tmp32);

		udma_ctl &= ~(1 << drive->dn);
Linus Torvalds's avatar
Linus Torvalds committed
113 114
	}

115 116
	pci_write_config_word(dev, ATIIXP_IDE_UDMA_CONTROL, udma_ctl);

117
	spin_unlock_irqrestore(&atiixp_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
118 119
}

120 121 122 123 124 125 126 127 128 129 130 131 132
static u8 __devinit atiixp_cable_detect(ide_hwif_t *hwif)
{
	struct pci_dev *pdev = to_pci_dev(hwif->dev);
	u8 udma_mode = 0, ch = hwif->channel;

	pci_read_config_byte(pdev, ATIIXP_IDE_UDMA_MODE + ch, &udma_mode);

	if ((udma_mode & 0x07) >= 0x04 || (udma_mode & 0x70) >= 0x40)
		return ATA_CBL_PATA80;
	else
		return ATA_CBL_PATA40;
}

Linus Torvalds's avatar
Linus Torvalds committed
133 134 135 136 137 138 139 140 141 142
/**
 *	init_hwif_atiixp		-	fill in the hwif for the ATIIXP
 *	@hwif: IDE interface
 *
 *	Set up the ide_hwif_t for the ATIIXP interface according to the
 *	capabilities of the hardware.
 */

static void __devinit init_hwif_atiixp(ide_hwif_t *hwif)
{
143
	hwif->set_pio_mode = &atiixp_set_pio_mode;
144
	hwif->set_dma_mode = &atiixp_set_dma_mode;
Linus Torvalds's avatar
Linus Torvalds committed
145

146
	hwif->cable_detect = atiixp_cable_detect;
Linus Torvalds's avatar
Linus Torvalds committed
147 148
}

149
static const struct ide_port_info atiixp_pci_info[] __devinitdata = {
Linus Torvalds's avatar
Linus Torvalds committed
150 151 152 153
	{	/* 0 */
		.name		= "ATIIXP",
		.init_hwif	= init_hwif_atiixp,
		.enablebits	= {{0x48,0x01,0x00}, {0x48,0x08,0x00}},
154
		.host_flags	= IDE_HFLAG_LEGACY_IRQS | IDE_HFLAG_BOOTABLE,
Bartlomiej Zolnierkiewicz's avatar
Bartlomiej Zolnierkiewicz committed
155
		.pio_mask	= ATA_PIO4,
156 157
		.mwdma_mask	= ATA_MWDMA2,
		.udma_mask	= ATA_UDMA5,
158 159 160 161
	},{	/* 1 */
		.name		= "SB600_PATA",
		.init_hwif	= init_hwif_atiixp,
		.enablebits	= {{0x48,0x01,0x00}, {0x00,0x00,0x00}},
162 163
 		.host_flags	= IDE_HFLAG_SINGLE | IDE_HFLAG_LEGACY_IRQS |
				  IDE_HFLAG_BOOTABLE,
Bartlomiej Zolnierkiewicz's avatar
Bartlomiej Zolnierkiewicz committed
164
		.pio_mask	= ATA_PIO4,
165 166
		.mwdma_mask	= ATA_MWDMA2,
		.udma_mask	= ATA_UDMA5,
167
 	},
Linus Torvalds's avatar
Linus Torvalds committed
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
};

/**
 *	atiixp_init_one	-	called when a ATIIXP is found
 *	@dev: the atiixp device
 *	@id: the matching pci id
 *
 *	Called when the PCI registration layer (or the IDE initialization)
 *	finds a device matching our IDE device tables.
 */

static int __devinit atiixp_init_one(struct pci_dev *dev, const struct pci_device_id *id)
{
	return ide_setup_pci_device(dev, &atiixp_pci_info[id->driver_data]);
}

184 185 186 187 188 189
static const struct pci_device_id atiixp_pci_tbl[] = {
	{ PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_IXP200_IDE), 0 },
	{ PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_IXP300_IDE), 0 },
	{ PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_IXP400_IDE), 0 },
	{ PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_IXP600_IDE), 1 },
	{ PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_IXP700_IDE), 0 },
Linus Torvalds's avatar
Linus Torvalds committed
190 191 192 193 194 195 196 197 198 199
	{ 0, },
};
MODULE_DEVICE_TABLE(pci, atiixp_pci_tbl);

static struct pci_driver driver = {
	.name		= "ATIIXP_IDE",
	.id_table	= atiixp_pci_tbl,
	.probe		= atiixp_init_one,
};

200
static int __init atiixp_ide_init(void)
Linus Torvalds's avatar
Linus Torvalds committed
201 202 203 204 205 206 207 208 209
{
	return ide_pci_register_driver(&driver);
}

module_init(atiixp_ide_init);

MODULE_AUTHOR("HUI YU");
MODULE_DESCRIPTION("PCI driver module for ATI IXP IDE");
MODULE_LICENSE("GPL");