Blame view

drivers/ide/cs5530.c 8.14 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
   * Copyright (C) 2000			Andre Hedrick <andre@linux-ide.org>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
   * Copyright (C) 2000			Mark Lord <mlord@pobox.com>
5fd216bbb   Bartlomiej Zolnierkiewicz   cs5530/sc1200: ad...
4
5
   * Copyright (C) 2007			Bartlomiej Zolnierkiewicz
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
6
7
8
9
10
11
12
13
   * May be copied or modified under the terms of the GNU General Public License
   *
   * Development of this chipset driver was funded
   * by the nice folks at National Semiconductor.
   *
   * Documentation:
   *	CS5530 documentation available from National Semiconductor.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
15
16
  #include <linux/module.h>
  #include <linux/types.h>
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
  #include <linux/pci.h>
  #include <linux/init.h>
  #include <linux/ide.h>
78829dd92   Bartlomiej Zolnierkiewicz   ide: remove needl...
20

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
  #include <asm/io.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22

ced3ec8aa   Bartlomiej Zolnierkiewicz   ide: prefix messa...
23
  #define DRV_NAME "cs5530"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
  /*
   * Here are the standard PIO mode 0-4 timings for each "format".
   * Format-0 uses fast data reg timings, with slower command reg timings.
   * Format-1 uses fast timings for all registers, but won't work with all drives.
   */
  static unsigned int cs5530_pio_timings[2][5] = {
  	{0x00009172, 0x00012171, 0x00020080, 0x00032010, 0x00040010},
  	{0xd1329172, 0x71212171, 0x30200080, 0x20102010, 0x00100010}
  };
  
  /*
   * After chip reset, the PIO timings are set to 0x0000e132, which is not valid.
   */
  #define CS5530_BAD_PIO(timings) (((timings)&~0x80000000)==0x0000e132)
  #define CS5530_BASEREG(hwif)	(((hwif)->dma_base & ~0xf) + ((hwif)->channel ? 0x30 : 0x20))
  
  /**
88b2b32ba   Bartlomiej Zolnierkiewicz   ide: move ide_con...
41
   *	cs5530_set_pio_mode	-	set host controller for PIO mode
e085b3cae   Bartlomiej Zolnierkiewicz   ide: change ->set...
42
   *	@hwif: port
26bcb879c   Bartlomiej Zolnierkiewicz   ide: add ide_set{...
43
   *	@drive: drive
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
   *
88b2b32ba   Bartlomiej Zolnierkiewicz   ide: move ide_con...
45
   *	Handles setting of PIO mode for the chipset.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46
   *
26bcb879c   Bartlomiej Zolnierkiewicz   ide: add ide_set{...
47
   *	The init_hwif_cs5530() routine guarantees that all drives
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
49
   *	will have valid default PIO timings set up before we get here.
   */
e085b3cae   Bartlomiej Zolnierkiewicz   ide: change ->set...
50
  static void cs5530_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
  {
e085b3cae   Bartlomiej Zolnierkiewicz   ide: change ->set...
52
  	unsigned long basereg = CS5530_BASEREG(hwif);
88b2b32ba   Bartlomiej Zolnierkiewicz   ide: move ide_con...
53
  	unsigned int format = (inl(basereg + 4) >> 31) & 1;
e085b3cae   Bartlomiej Zolnierkiewicz   ide: change ->set...
54
  	const u8 pio = drive->pio_mode - XFER_PIO_0;
88b2b32ba   Bartlomiej Zolnierkiewicz   ide: move ide_con...
55
56
  
  	outl(cs5530_pio_timings[format][pio], basereg + ((drive->dn & 1)<<3));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
57
58
59
  }
  
  /**
5fd216bbb   Bartlomiej Zolnierkiewicz   cs5530/sc1200: ad...
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
   *	cs5530_udma_filter	-	UDMA filter
   *	@drive: drive
   *
   *	cs5530_udma_filter() does UDMA mask filtering for the given drive
   *	taking into the consideration capabilities of the mate device.
   *
   *	The CS5530 specifies that two drives sharing a cable cannot mix
   *	UDMA/MDMA.  It has to be one or the other, for the pair, though
   *	different timings can still be chosen for each drive.  We could
   *	set the appropriate timing bits on the fly, but that might be
   *	a bit confusing.  So, for now we statically handle this requirement
   *	by looking at our mate drive to see what it is capable of, before
   *	choosing a mode for our own drive.
   *
   *	Note: This relies on the fact we never fail from UDMA to MWDMA2
   *	but instead drop to PIO.
   */
  
  static u8 cs5530_udma_filter(ide_drive_t *drive)
  {
  	ide_hwif_t *hwif = drive->hwif;
7e59ea21a   Bartlomiej Zolnierkiewicz   ide: check drive-...
81
  	ide_drive_t *mate = ide_get_pair_dev(drive);
9ecab6e5b   Julia Lawall   drivers/ide/{cs55...
82
  	u16 *mateid;
5fd216bbb   Bartlomiej Zolnierkiewicz   cs5530/sc1200: ad...
83
  	u8 mask = hwif->ultra_mask;
7e59ea21a   Bartlomiej Zolnierkiewicz   ide: check drive-...
84
  	if (mate == NULL)
5fd216bbb   Bartlomiej Zolnierkiewicz   cs5530/sc1200: ad...
85
  		goto out;
9ecab6e5b   Julia Lawall   drivers/ide/{cs55...
86
  	mateid = mate->id;
5fd216bbb   Bartlomiej Zolnierkiewicz   cs5530/sc1200: ad...
87

48fb2688a   Bartlomiej Zolnierkiewicz   ide: remove drive...
88
  	if (ata_id_has_dma(mateid) && __ide_dma_bad_drive(mate) == 0) {
4dde4492d   Bartlomiej Zolnierkiewicz   ide: make drive->...
89
90
  		if ((mateid[ATA_ID_FIELD_VALID] & 4) &&
  		    (mateid[ATA_ID_UDMA_MODES] & 7))
5fd216bbb   Bartlomiej Zolnierkiewicz   cs5530/sc1200: ad...
91
  			goto out;
8d64fcd93   Sergei Shtylyov   ide: identify dat...
92
  		if (mateid[ATA_ID_MWDMA_MODES] & 7)
5fd216bbb   Bartlomiej Zolnierkiewicz   cs5530/sc1200: ad...
93
94
95
96
97
  			mask = 0;
  	}
  out:
  	return mask;
  }
8776168ca   Bartlomiej Zolnierkiewicz   ide: change ->set...
98
  static void cs5530_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
3c3f5d2c9   Bartlomiej Zolnierkiewicz   cs5530/sc1200: ad...
99
  {
5fd216bbb   Bartlomiej Zolnierkiewicz   cs5530/sc1200: ad...
100
  	unsigned long basereg;
3c3f5d2c9   Bartlomiej Zolnierkiewicz   cs5530/sc1200: ad...
101
  	unsigned int reg, timings = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102

8776168ca   Bartlomiej Zolnierkiewicz   ide: change ->set...
103
  	switch (drive->dma_mode) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104
105
106
107
108
109
  		case XFER_UDMA_0:	timings = 0x00921250; break;
  		case XFER_UDMA_1:	timings = 0x00911140; break;
  		case XFER_UDMA_2:	timings = 0x00911030; break;
  		case XFER_MW_DMA_0:	timings = 0x00077771; break;
  		case XFER_MW_DMA_1:	timings = 0x00012121; break;
  		case XFER_MW_DMA_2:	timings = 0x00002020; break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110
  	}
8776168ca   Bartlomiej Zolnierkiewicz   ide: change ->set...
111
  	basereg = CS5530_BASEREG(hwif);
0ecdca26e   Bartlomiej Zolnierkiewicz   ide: use PIO/MMIO...
112
  	reg = inl(basereg + 4);			/* get drive0 config register */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113
  	timings |= reg & 0x80000000;		/* preserve PIO format bit */
3c3f5d2c9   Bartlomiej Zolnierkiewicz   cs5530/sc1200: ad...
114
  	if ((drive-> dn & 1) == 0) {		/* are we configuring drive0? */
0ecdca26e   Bartlomiej Zolnierkiewicz   ide: use PIO/MMIO...
115
  		outl(timings, basereg + 4);	/* write drive0 config register */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
117
118
119
120
  	} else {
  		if (timings & 0x00100000)
  			reg |=  0x00100000;	/* enable UDMA timings for both drives */
  		else
  			reg &= ~0x00100000;	/* disable UDMA timings for both drives */
0ecdca26e   Bartlomiej Zolnierkiewicz   ide: use PIO/MMIO...
121
122
  		outl(reg, basereg + 4);		/* write drive0 config register */
  		outl(timings, basereg + 12);	/* write drive1 config register */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
125
126
127
128
  }
  
  /**
   *	init_chipset_5530	-	set up 5530 bridge
   *	@dev: PCI device
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
130
131
   *
   *	Initialize the cs5530 bridge for reliable IDE DMA operation.
   */
2ed0ef543   Bartlomiej Zolnierkiewicz   ide: fix ->init_c...
132
  static int init_chipset_cs5530(struct pci_dev *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
134
  {
  	struct pci_dev *master_0 = NULL, *cs5530_0 = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135

f7b0d2df2   Bartlomiej Zolnierkiewicz   cs5530: add missi...
136
137
  	if (pci_resource_start(dev, 4) == 0)
  		return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
  	dev = NULL;
652aa1629   Alan Cox   [PATCH] IDE: more...
139
  	while ((dev = pci_get_device(PCI_VENDOR_ID_CYRIX, PCI_ANY_ID, dev)) != NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
141
  		switch (dev->device) {
  			case PCI_DEVICE_ID_CYRIX_PCI_MASTER:
652aa1629   Alan Cox   [PATCH] IDE: more...
142
  				master_0 = pci_dev_get(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143
144
  				break;
  			case PCI_DEVICE_ID_CYRIX_5530_LEGACY:
652aa1629   Alan Cox   [PATCH] IDE: more...
145
  				cs5530_0 = pci_dev_get(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
147
148
149
  				break;
  		}
  	}
  	if (!master_0) {
a326b02b0   Bartlomiej Zolnierkiewicz   ide: drop 'name' ...
150
151
  		printk(KERN_ERR DRV_NAME ": unable to locate PCI MASTER function
  ");
652aa1629   Alan Cox   [PATCH] IDE: more...
152
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
153
154
  	}
  	if (!cs5530_0) {
a326b02b0   Bartlomiej Zolnierkiewicz   ide: drop 'name' ...
155
156
  		printk(KERN_ERR DRV_NAME ": unable to locate CS5530 LEGACY function
  ");
652aa1629   Alan Cox   [PATCH] IDE: more...
157
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
159
160
161
162
163
164
  	/*
  	 * Enable BusMaster and MemoryWriteAndInvalidate for the cs5530:
  	 * -->  OR 0x14 into 16-bit PCI COMMAND reg of function 0 of the cs5530
  	 */
  
  	pci_set_master(cs5530_0);
694625c0b   Randy Dunlap   PCI: add pci_try_...
165
  	pci_try_set_mwi(cs5530_0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
  
  	/*
  	 * Set PCI CacheLineSize to 16-bytes:
  	 * --> Write 0x04 into 8-bit PCI CACHELINESIZE reg of function 0 of the cs5530
  	 */
  
  	pci_write_config_byte(cs5530_0, PCI_CACHE_LINE_SIZE, 0x04);
  
  	/*
  	 * Disable trapping of UDMA register accesses (Win98 hack):
  	 * --> Write 0x5006 into 16-bit reg at offset 0xd0 of function 0 of the cs5530
  	 */
  
  	pci_write_config_word(cs5530_0, 0xd0, 0x5006);
  
  	/*
  	 * Bit-1 at 0x40 enables MemoryWriteAndInvalidate on internal X-bus:
  	 * The other settings are what is necessary to get the register
  	 * into a sane state for IDE DMA operation.
  	 */
  
  	pci_write_config_byte(master_0, 0x40, 0x1e);
  
  	/* 
  	 * Set max PCI burst size (16-bytes seems to work best):
  	 *	   16bytes: set bit-1 at 0x41 (reg value of 0x16)
  	 *	all others: clear bit-1 at 0x41, and do:
  	 *	  128bytes: OR 0x00 at 0x41
  	 *	  256bytes: OR 0x04 at 0x41
  	 *	  512bytes: OR 0x08 at 0x41
  	 *	 1024bytes: OR 0x0c at 0x41
  	 */
  
  	pci_write_config_byte(master_0, 0x41, 0x14);
  
  	/*
  	 * These settings are necessary to get the chip
  	 * into a sane state for IDE DMA operation.
  	 */
  
  	pci_write_config_byte(master_0, 0x42, 0x00);
  	pci_write_config_byte(master_0, 0x43, 0xc1);
652aa1629   Alan Cox   [PATCH] IDE: more...
208
209
210
  out:
  	pci_dev_put(master_0);
  	pci_dev_put(cs5530_0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
212
213
214
215
216
217
218
219
220
  	return 0;
  }
  
  /**
   *	init_hwif_cs5530	-	initialise an IDE channel
   *	@hwif: IDE to initialize
   *
   *	This gets invoked by the IDE driver once for each channel. It
   *	performs channel-specific pre-initialization before drive probing.
   */
fe31edc8a   Greg Kroah-Hartman   Drivers: ide: rem...
221
  static void init_hwif_cs5530 (ide_hwif_t *hwif)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
223
224
  {
  	unsigned long basereg;
  	u32 d0_timings;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
  	basereg = CS5530_BASEREG(hwif);
0ecdca26e   Bartlomiej Zolnierkiewicz   ide: use PIO/MMIO...
227
  	d0_timings = inl(basereg + 0);
93104654c   Bartlomiej Zolnierkiewicz   cs5530: always tu...
228
  	if (CS5530_BAD_PIO(d0_timings))
0ecdca26e   Bartlomiej Zolnierkiewicz   ide: use PIO/MMIO...
229
  		outl(cs5530_pio_timings[(d0_timings >> 31) & 1][0], basereg + 0);
93104654c   Bartlomiej Zolnierkiewicz   cs5530: always tu...
230
  	if (CS5530_BAD_PIO(inl(basereg + 8)))
0ecdca26e   Bartlomiej Zolnierkiewicz   ide: use PIO/MMIO...
231
  		outl(cs5530_pio_timings[(d0_timings >> 31) & 1][0], basereg + 8);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
  }
ac95beedf   Bartlomiej Zolnierkiewicz   ide: add struct i...
233
234
235
236
237
  static const struct ide_port_ops cs5530_port_ops = {
  	.set_pio_mode		= cs5530_set_pio_mode,
  	.set_dma_mode		= cs5530_set_dma_mode,
  	.udma_filter		= cs5530_udma_filter,
  };
fe31edc8a   Greg Kroah-Hartman   Drivers: ide: rem...
238
  static const struct ide_port_info cs5530_chipset = {
ced3ec8aa   Bartlomiej Zolnierkiewicz   ide: prefix messa...
239
  	.name		= DRV_NAME,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
241
  	.init_chipset	= init_chipset_cs5530,
  	.init_hwif	= init_hwif_cs5530,
ac95beedf   Bartlomiej Zolnierkiewicz   ide: add struct i...
242
  	.port_ops	= &cs5530_port_ops,
1c51361a9   Bartlomiej Zolnierkiewicz   ide: add IDE_HFLA...
243
  	.host_flags	= IDE_HFLAG_SERIALIZE |
5e71d9c5a   Bartlomiej Zolnierkiewicz   ide: IDE_HFLAG_BO...
244
  			  IDE_HFLAG_POST_SET_MODE,
4099d1432   Bartlomiej Zolnierkiewicz   ide: add PIO masks
245
  	.pio_mask	= ATA_PIO4,
5f8b6c348   Bartlomiej Zolnierkiewicz   ide: add ->mwdma_...
246
247
  	.mwdma_mask	= ATA_MWDMA2,
  	.udma_mask	= ATA_UDMA2,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
248
  };
fe31edc8a   Greg Kroah-Hartman   Drivers: ide: rem...
249
  static int cs5530_init_one(struct pci_dev *dev, const struct pci_device_id *id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
  {
6cdf6eb35   Bartlomiej Zolnierkiewicz   ide: add ->dev an...
251
  	return ide_pci_init_one(dev, &cs5530_chipset, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
  }
9cbcc5e3c   Bartlomiej Zolnierkiewicz   ide: use PCI_VDEV...
253
254
  static const struct pci_device_id cs5530_pci_tbl[] = {
  	{ PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5530_IDE), 0 },
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
256
257
  	{ 0, },
  };
  MODULE_DEVICE_TABLE(pci, cs5530_pci_tbl);
a9ab09e26   Bartlomiej Zolnierkiewicz   ide: use unique n...
258
  static struct pci_driver cs5530_pci_driver = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
260
261
  	.name		= "CS5530 IDE",
  	.id_table	= cs5530_pci_tbl,
  	.probe		= cs5530_init_one,
d16492a97   Bartlomiej Zolnierkiewicz   cs5530: add ->rem...
262
  	.remove		= ide_pci_remove,
feb22b7f8   Bartlomiej Zolnierkiewicz   ide: add proper P...
263
264
  	.suspend	= ide_pci_suspend,
  	.resume		= ide_pci_resume,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
  };
82ab1eece   Bartlomiej Zolnierkiewicz   ide: add missing ...
266
  static int __init cs5530_ide_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267
  {
a9ab09e26   Bartlomiej Zolnierkiewicz   ide: use unique n...
268
  	return ide_pci_register_driver(&cs5530_pci_driver);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
  }
d16492a97   Bartlomiej Zolnierkiewicz   cs5530: add ->rem...
270
271
  static void __exit cs5530_ide_exit(void)
  {
a9ab09e26   Bartlomiej Zolnierkiewicz   ide: use unique n...
272
  	pci_unregister_driver(&cs5530_pci_driver);
d16492a97   Bartlomiej Zolnierkiewicz   cs5530: add ->rem...
273
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274
  module_init(cs5530_ide_init);
d16492a97   Bartlomiej Zolnierkiewicz   cs5530: add ->rem...
275
  module_exit(cs5530_ide_exit);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
276
277
278
279
  
  MODULE_AUTHOR("Mark Lord");
  MODULE_DESCRIPTION("PCI driver module for Cyrix/NS 5530 IDE");
  MODULE_LICENSE("GPL");