Blame view

drivers/ata/pata_cs5520.c 9.39 KB
669a5db41   Jeff Garzik   [libata] Add a bu...
1
2
3
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
40
41
42
43
  /*
   *	IDE tuning and bus mastering support for the CS5510/CS5520
   *	chipsets
   *
   *	The CS5510/CS5520 are slightly unusual devices. Unlike the
   *	typical IDE controllers they do bus mastering with the drive in
   *	PIO mode and smarter silicon.
   *
   *	The practical upshot of this is that we must always tune the
   *	drive for the right PIO mode. We must also ignore all the blacklists
   *	and the drive bus mastering DMA information. Also to confuse matters
   *	further we can do DMA on PIO only drives.
   *
   *	DMA on the 5510 also requires we disable_hlt() during DMA on early
   *	revisions.
   *
   *	*** This driver is strictly experimental ***
   *
   *	(c) Copyright Red Hat Inc 2002
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License as published by the
   * Free Software Foundation; either version 2, or (at your option) any
   * later version.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * General Public License for more details.
   *
   * Documentation:
   *	Not publically available.
   */
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/pci.h>
  #include <linux/init.h>
  #include <linux/blkdev.h>
  #include <linux/delay.h>
  #include <scsi/scsi_host.h>
  #include <linux/libata.h>
  
  #define DRV_NAME	"pata_cs5520"
2a3103ce4   Jeff Garzik   [libata] Bump dri...
44
  #define DRV_VERSION	"0.6.6"
669a5db41   Jeff Garzik   [libata] Add a bu...
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
  
  struct pio_clocks
  {
  	int address;
  	int assert;
  	int recovery;
  };
  
  static const struct pio_clocks cs5520_pio_clocks[]={
  	{3, 6, 11},
  	{2, 5, 6},
  	{1, 4, 3},
  	{1, 3, 2},
  	{1, 2, 1}
  };
  
  /**
   *	cs5520_set_timings	-	program PIO timings
   *	@ap: ATA port
   *	@adev: ATA device
   *
   *	Program the PIO mode timings for the controller according to the pio
   *	clocking table.
   */
  
  static void cs5520_set_timings(struct ata_port *ap, struct ata_device *adev, int pio)
  {
  	struct pci_dev *pdev = to_pci_dev(ap->host->dev);
  	int slave = adev->devno;
  
  	pio -= XFER_PIO_0;
  
  	/* Channel command timing */
  	pci_write_config_byte(pdev, 0x62 + ap->port_no,
  				(cs5520_pio_clocks[pio].recovery << 4) |
  				(cs5520_pio_clocks[pio].assert));
  	/* FIXME: should these use address ? */
  	/* Read command timing */
  	pci_write_config_byte(pdev, 0x64 +  4*ap->port_no + slave,
  				(cs5520_pio_clocks[pio].recovery << 4) |
  				(cs5520_pio_clocks[pio].assert));
  	/* Write command timing */
  	pci_write_config_byte(pdev, 0x66 +  4*ap->port_no + slave,
  				(cs5520_pio_clocks[pio].recovery << 4) |
  				(cs5520_pio_clocks[pio].assert));
  }
  
  /**
   *	cs5520_enable_dma	-	turn on DMA bits
   *
   *	Turn on the DMA bits for this disk. Needed because the BIOS probably
   *	has not done the work for us. Belongs in the core SATA code.
   */
  
  static void cs5520_enable_dma(struct ata_port *ap, struct ata_device *adev)
  {
  	/* Set the DMA enable/disable flag */
0d5ff5667   Tejun Heo   libata: convert t...
102
  	u8 reg = ioread8(ap->ioaddr.bmdma_addr + 0x02);
669a5db41   Jeff Garzik   [libata] Add a bu...
103
  	reg |= 1<<(adev->devno + 5);
0d5ff5667   Tejun Heo   libata: convert t...
104
  	iowrite8(reg, ap->ioaddr.bmdma_addr + 0x02);
669a5db41   Jeff Garzik   [libata] Add a bu...
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
  }
  
  /**
   *	cs5520_set_dmamode	-	program DMA timings
   *	@ap: ATA port
   *	@adev: ATA device
   *
   *	Program the DMA mode timings for the controller according to the pio
   *	clocking table. Note that this device sets the DMA timings to PIO
   *	mode values. This may seem bizarre but the 5520 architecture talks
   *	PIO mode to the disk and DMA mode to the controller so the underlying
   *	transfers are PIO timed.
   */
  
  static void cs5520_set_dmamode(struct ata_port *ap, struct ata_device *adev)
  {
  	static const int dma_xlate[3] = { XFER_PIO_0, XFER_PIO_3, XFER_PIO_4 };
  	cs5520_set_timings(ap, adev, dma_xlate[adev->dma_mode]);
  	cs5520_enable_dma(ap, adev);
  }
  
  /**
   *	cs5520_set_piomode	-	program PIO timings
   *	@ap: ATA port
   *	@adev: ATA device
   *
   *	Program the PIO mode timings for the controller according to the pio
   *	clocking table. We know pio_mode will equal dma_mode because of the
   *	CS5520 architecture. At least once we turned DMA on and wrote a
   *	mode setter.
   */
  
  static void cs5520_set_piomode(struct ata_port *ap, struct ata_device *adev)
  {
  	cs5520_set_timings(ap, adev, adev->pio_mode);
  }
669a5db41   Jeff Garzik   [libata] Add a bu...
141
  static struct scsi_host_template cs5520_sht = {
68d1d07b5   Tejun Heo   libata: implement...
142
  	ATA_BMDMA_SHT(DRV_NAME),
d26fc9551   Alan Cox   libata: Support c...
143
  	.sg_tablesize		= LIBATA_DUMB_MAX_PRD,
669a5db41   Jeff Garzik   [libata] Add a bu...
144
145
146
  };
  
  static struct ata_port_operations cs5520_port_ops = {
029cfd6b7   Tejun Heo   libata: implement...
147
  	.inherits		= &ata_bmdma_port_ops,
9363c3825   Tejun Heo   libata: rename SF...
148
  	.qc_prep		= ata_sff_dumb_qc_prep,
029cfd6b7   Tejun Heo   libata: implement...
149
  	.cable_detect		= ata_cable_40wire,
669a5db41   Jeff Garzik   [libata] Add a bu...
150
151
  	.set_piomode		= cs5520_set_piomode,
  	.set_dmamode		= cs5520_set_dmamode,
669a5db41   Jeff Garzik   [libata] Add a bu...
152
  };
5d728824e   Tejun Heo   libata: convert t...
153
  static int __devinit cs5520_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
669a5db41   Jeff Garzik   [libata] Add a bu...
154
  {
cbcdd8759   Tejun Heo   libata: implement...
155
156
  	static const unsigned int cmd_port[] = { 0x1F0, 0x170 };
  	static const unsigned int ctl_port[] = { 0x3F6, 0x376 };
5d728824e   Tejun Heo   libata: convert t...
157
158
  	struct ata_port_info pi = {
  		.flags		= ATA_FLAG_SLAVE_POSS,
14bdef982   Erik Inge Bolsø   [libata] convert ...
159
  		.pio_mask	= ATA_PIO4,
5d728824e   Tejun Heo   libata: convert t...
160
161
162
  		.port_ops	= &cs5520_port_ops,
  	};
  	const struct ata_port_info *ppi[2];
669a5db41   Jeff Garzik   [libata] Add a bu...
163
  	u8 pcicfg;
4ca4e4396   Al Viro   libata annotation...
164
  	void __iomem *iomap[5];
5d728824e   Tejun Heo   libata: convert t...
165
166
167
  	struct ata_host *host;
  	struct ata_ioports *ioaddr;
  	int i, rc;
669a5db41   Jeff Garzik   [libata] Add a bu...
168

f08048e94   Tejun Heo   libata: PCI devic...
169
170
171
  	rc = pcim_enable_device(pdev);
  	if (rc)
  		return rc;
669a5db41   Jeff Garzik   [libata] Add a bu...
172
  	/* IDE port enable bits */
5d728824e   Tejun Heo   libata: convert t...
173
  	pci_read_config_byte(pdev, 0x60, &pcicfg);
669a5db41   Jeff Garzik   [libata] Add a bu...
174
175
176
177
  
  	/* Check if the ATA ports are enabled */
  	if ((pcicfg & 3) == 0)
  		return -ENODEV;
5d728824e   Tejun Heo   libata: convert t...
178
179
180
181
182
  	ppi[0] = ppi[1] = &ata_dummy_port_info;
  	if (pcicfg & 1)
  		ppi[0] = &pi;
  	if (pcicfg & 2)
  		ppi[1] = &pi;
669a5db41   Jeff Garzik   [libata] Add a bu...
183
  	if ((pcicfg & 0x40) == 0) {
5d728824e   Tejun Heo   libata: convert t...
184
185
186
187
  		dev_printk(KERN_WARNING, &pdev->dev,
  			   "DMA mode disabled. Enabling.
  ");
  		pci_write_config_byte(pdev, 0x60, pcicfg | 0x40);
669a5db41   Jeff Garzik   [libata] Add a bu...
188
  	}
5d728824e   Tejun Heo   libata: convert t...
189
190
191
192
193
  	pi.mwdma_mask = id->driver_data;
  
  	host = ata_host_alloc_pinfo(&pdev->dev, ppi, 2);
  	if (!host)
  		return -ENOMEM;
669a5db41   Jeff Garzik   [libata] Add a bu...
194
  	/* Perform set up for DMA */
094839164   Benjamin Herrenschmidt   PCI: Remove users...
195
  	if (pci_enable_device_io(pdev)) {
669a5db41   Jeff Garzik   [libata] Add a bu...
196
197
198
199
  		printk(KERN_ERR DRV_NAME ": unable to configure BAR2.
  ");
  		return -ENODEV;
  	}
5d728824e   Tejun Heo   libata: convert t...
200
201
  
  	if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
669a5db41   Jeff Garzik   [libata] Add a bu...
202
203
204
205
  		printk(KERN_ERR DRV_NAME ": unable to configure DMA mask.
  ");
  		return -ENODEV;
  	}
5d728824e   Tejun Heo   libata: convert t...
206
  	if (pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK)) {
669a5db41   Jeff Garzik   [libata] Add a bu...
207
208
209
210
  		printk(KERN_ERR DRV_NAME ": unable to configure consistent DMA mask.
  ");
  		return -ENODEV;
  	}
5d728824e   Tejun Heo   libata: convert t...
211
  	/* Map IO ports and initialize host accordingly */
cbcdd8759   Tejun Heo   libata: implement...
212
213
214
215
  	iomap[0] = devm_ioport_map(&pdev->dev, cmd_port[0], 8);
  	iomap[1] = devm_ioport_map(&pdev->dev, ctl_port[0], 1);
  	iomap[2] = devm_ioport_map(&pdev->dev, cmd_port[1], 8);
  	iomap[3] = devm_ioport_map(&pdev->dev, ctl_port[1], 1);
5d728824e   Tejun Heo   libata: convert t...
216
  	iomap[4] = pcim_iomap(pdev, 2, 0);
0d5ff5667   Tejun Heo   libata: convert t...
217
218
219
  
  	if (!iomap[0] || !iomap[1] || !iomap[2] || !iomap[3] || !iomap[4])
  		return -ENOMEM;
5d728824e   Tejun Heo   libata: convert t...
220
221
222
223
224
  	ioaddr = &host->ports[0]->ioaddr;
  	ioaddr->cmd_addr = iomap[0];
  	ioaddr->ctl_addr = iomap[1];
  	ioaddr->altstatus_addr = iomap[1];
  	ioaddr->bmdma_addr = iomap[4];
9363c3825   Tejun Heo   libata: rename SF...
225
  	ata_sff_std_ports(ioaddr);
5d728824e   Tejun Heo   libata: convert t...
226

cbcdd8759   Tejun Heo   libata: implement...
227
228
229
  	ata_port_desc(host->ports[0],
  		      "cmd 0x%x ctl 0x%x", cmd_port[0], ctl_port[0]);
  	ata_port_pbar_desc(host->ports[0], 4, 0, "bmdma");
5d728824e   Tejun Heo   libata: convert t...
230
231
232
233
234
  	ioaddr = &host->ports[1]->ioaddr;
  	ioaddr->cmd_addr = iomap[2];
  	ioaddr->ctl_addr = iomap[3];
  	ioaddr->altstatus_addr = iomap[3];
  	ioaddr->bmdma_addr = iomap[4] + 8;
9363c3825   Tejun Heo   libata: rename SF...
235
  	ata_sff_std_ports(ioaddr);
5d728824e   Tejun Heo   libata: convert t...
236

cbcdd8759   Tejun Heo   libata: implement...
237
238
239
  	ata_port_desc(host->ports[1],
  		      "cmd 0x%x ctl 0x%x", cmd_port[1], ctl_port[1]);
  	ata_port_pbar_desc(host->ports[1], 4, 8, "bmdma");
5d728824e   Tejun Heo   libata: convert t...
240
241
242
243
244
245
246
247
  	/* activate the host */
  	pci_set_master(pdev);
  	rc = ata_host_start(host);
  	if (rc)
  		return rc;
  
  	for (i = 0; i < 2; i++) {
  		static const int irq[] = { 14, 15 };
8c6b065b7   Alan Cox   pata_cs5520: Fix ...
248
  		struct ata_port *ap = host->ports[i];
5d728824e   Tejun Heo   libata: convert t...
249
250
251
252
253
  
  		if (ata_port_is_dummy(ap))
  			continue;
  
  		rc = devm_request_irq(&pdev->dev, irq[ap->port_no],
9363c3825   Tejun Heo   libata: rename SF...
254
  				      ata_sff_interrupt, 0, DRV_NAME, host);
5d728824e   Tejun Heo   libata: convert t...
255
256
  		if (rc)
  			return rc;
4031826b3   Tejun Heo   libata: fix assig...
257

cbcdd8759   Tejun Heo   libata: implement...
258
  		ata_port_desc(ap, "irq %d", irq[i]);
5d728824e   Tejun Heo   libata: convert t...
259
260
261
  	}
  
  	return ata_host_register(host, &cs5520_sht);
669a5db41   Jeff Garzik   [libata] Add a bu...
262
  }
438ac6d5e   Tejun Heo   libata: add missi...
263
  #ifdef CONFIG_PM
8501120f1   Alan Cox   [PATCH] pata_cs55...
264
265
266
267
268
269
270
  /**
   *	cs5520_reinit_one	-	device resume
   *	@pdev: PCI device
   *
   *	Do any reconfiguration work needed by a resume from RAM. We need
   *	to restore DMA mode support on BIOSen which disabled it
   */
f20b16ff7   Jeff Garzik   [libata] trim tra...
271

8501120f1   Alan Cox   [PATCH] pata_cs55...
272
273
  static int cs5520_reinit_one(struct pci_dev *pdev)
  {
f08048e94   Tejun Heo   libata: PCI devic...
274
  	struct ata_host *host = dev_get_drvdata(&pdev->dev);
8501120f1   Alan Cox   [PATCH] pata_cs55...
275
  	u8 pcicfg;
f08048e94   Tejun Heo   libata: PCI devic...
276
277
278
279
280
  	int rc;
  
  	rc = ata_pci_device_do_resume(pdev);
  	if (rc)
  		return rc;
8501120f1   Alan Cox   [PATCH] pata_cs55...
281
282
283
  	pci_read_config_byte(pdev, 0x60, &pcicfg);
  	if ((pcicfg & 0x40) == 0)
  		pci_write_config_byte(pdev, 0x60, pcicfg | 0x40);
f08048e94   Tejun Heo   libata: PCI devic...
284
285
286
  
  	ata_host_resume(host);
  	return 0;
8501120f1   Alan Cox   [PATCH] pata_cs55...
287
  }
aa6de4942   Alan Cox   pata_cs5520: susp...
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
  
  /**
   *	cs5520_pci_device_suspend	-	device suspend
   *	@pdev: PCI device
   *
   *	We have to cut and waste bits from the standard method because
   *	the 5520 is a bit odd and not just a pure ATA device. As a result
   *	we must not disable it. The needed code is short and this avoids
   *	chip specific mess in the core code.
   */
  
  static int cs5520_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg)
  {
  	struct ata_host *host = dev_get_drvdata(&pdev->dev);
  	int rc = 0;
  
  	rc = ata_host_suspend(host, mesg);
  	if (rc)
  		return rc;
  
  	pci_save_state(pdev);
  	return 0;
  }
438ac6d5e   Tejun Heo   libata: add missi...
311
  #endif /* CONFIG_PM */
a84471fe2   Jeff Garzik   [libata] Trim tra...
312

669a5db41   Jeff Garzik   [libata] Add a bu...
313
314
  /* For now keep DMA off. We can set it for all but A rev CS5510 once the
     core ATA code can handle it */
2d2744fc8   Jeff Garzik   [libata] PCI ID t...
315
316
317
318
319
  static const struct pci_device_id pata_cs5520[] = {
  	{ PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5510), },
  	{ PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5520), },
  
  	{ },
669a5db41   Jeff Garzik   [libata] Add a bu...
320
321
322
323
324
325
  };
  
  static struct pci_driver cs5520_pci_driver = {
  	.name 		= DRV_NAME,
  	.id_table	= pata_cs5520,
  	.probe 		= cs5520_init_one,
2855568b1   Jeff Garzik   [libata] struct p...
326
  	.remove		= ata_pci_remove_one,
438ac6d5e   Tejun Heo   libata: add missi...
327
  #ifdef CONFIG_PM
aa6de4942   Alan Cox   pata_cs5520: susp...
328
  	.suspend	= cs5520_pci_device_suspend,
8501120f1   Alan Cox   [PATCH] pata_cs55...
329
  	.resume		= cs5520_reinit_one,
438ac6d5e   Tejun Heo   libata: add missi...
330
  #endif
669a5db41   Jeff Garzik   [libata] Add a bu...
331
  };
669a5db41   Jeff Garzik   [libata] Add a bu...
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
  static int __init cs5520_init(void)
  {
  	return pci_register_driver(&cs5520_pci_driver);
  }
  
  static void __exit cs5520_exit(void)
  {
  	pci_unregister_driver(&cs5520_pci_driver);
  }
  
  MODULE_AUTHOR("Alan Cox");
  MODULE_DESCRIPTION("low-level driver for Cyrix CS5510/5520");
  MODULE_LICENSE("GPL");
  MODULE_DEVICE_TABLE(pci, pata_cs5520);
  MODULE_VERSION(DRV_VERSION);
  
  module_init(cs5520_init);
  module_exit(cs5520_exit);