aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/ata/pata_cs5536.c
diff options
context:
space:
mode:
authorMartin K. Petersen <martin.petersen@oracle.com>2007-10-11 03:38:19 -0400
committerJeff Garzik <jeff@garzik.org>2007-10-15 15:44:18 -0400
commit3957df6160c90955979229b230cb5202e6a5ee2b (patch)
tree2d58fdddf114f52807f35cb6ed409c93d62975df /drivers/ata/pata_cs5536.c
parent135879600834124881ee37eabaaa56818c44bca3 (diff)
pata_cs5536: ATA driver for Geode companion chip
This is a driver for the ATA controller on the Geode CS5536 companion chip. The PCI device ID for this device was previously claimed by pata_amd.c but the PIO timings were not correct. This driver also works around a bug in some BIOSes that handle unaligned access to the PCI config registers poorly. Finally, the driver allows fallback to using MSR registers for configuration on BIOSes that are truly broken. Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/ata/pata_cs5536.c')
-rw-r--r--drivers/ata/pata_cs5536.c346
1 files changed, 346 insertions, 0 deletions
diff --git a/drivers/ata/pata_cs5536.c b/drivers/ata/pata_cs5536.c
new file mode 100644
index 000000000000..21405bf14837
--- /dev/null
+++ b/drivers/ata/pata_cs5536.c
@@ -0,0 +1,346 @@
1/*
2 * pata_cs5536.c - CS5536 PATA for new ATA layer
3 * (C) 2007 Martin K. Petersen <mkp@mkp.net>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 * Documentation:
19 * Available from AMD web site.
20 *
21 * The IDE timing registers for the CS5536 live in the Geode Machine
22 * Specific Register file and not PCI config space. Most BIOSes
23 * virtualize the PCI registers so the chip looks like a standard IDE
24 * controller. Unfortunately not all implementations get this right.
25 * In particular some have problems with unaligned accesses to the
26 * virtualized PCI registers. This driver always does full dword
27 * writes to work around the issue. Also, in case of a bad BIOS this
28 * driver can be loaded with the "msr=1" parameter which forces using
29 * the Machine Specific Registers to configure the device.
30 */
31
32#include <linux/kernel.h>
33#include <linux/module.h>
34#include <linux/pci.h>
35#include <linux/init.h>
36#include <linux/blkdev.h>
37#include <linux/delay.h>
38#include <linux/libata.h>
39#include <scsi/scsi_host.h>
40#include <asm/msr.h>
41
42#define DRV_NAME "pata_cs5536"
43#define DRV_VERSION "0.0.5"
44
45enum {
46 CFG = 0,
47 DTC = 1,
48 CAST = 2,
49 ETC = 3,
50
51 MSR_IDE_BASE = 0x51300000,
52 MSR_IDE_CFG = (MSR_IDE_BASE + 0x10),
53 MSR_IDE_DTC = (MSR_IDE_BASE + 0x12),
54 MSR_IDE_CAST = (MSR_IDE_BASE + 0x13),
55 MSR_IDE_ETC = (MSR_IDE_BASE + 0x14),
56
57 PCI_IDE_CFG = 0x40,
58 PCI_IDE_DTC = 0x48,
59 PCI_IDE_CAST = 0x4c,
60 PCI_IDE_ETC = 0x50,
61
62 IDE_CFG_CHANEN = 0x2,
63 IDE_CFG_CABLE = 0x10000,
64
65 IDE_D0_SHIFT = 24,
66 IDE_D1_SHIFT = 16,
67 IDE_DRV_MASK = 0xff,
68
69 IDE_CAST_D0_SHIFT = 6,
70 IDE_CAST_D1_SHIFT = 4,
71 IDE_CAST_DRV_MASK = 0x3,
72 IDE_CAST_CMD_MASK = 0xff,
73 IDE_CAST_CMD_SHIFT = 24,
74
75 IDE_ETC_NODMA = 0x03,
76};
77
78static int use_msr;
79
80static const u32 msr_reg[4] = {
81 MSR_IDE_CFG, MSR_IDE_DTC, MSR_IDE_CAST, MSR_IDE_ETC,
82};
83
84static const u8 pci_reg[4] = {
85 PCI_IDE_CFG, PCI_IDE_DTC, PCI_IDE_CAST, PCI_IDE_ETC,
86};
87
88static inline int cs5536_read(struct pci_dev *pdev, int reg, int *val)
89{
90 if (unlikely(use_msr)) {
91 u32 dummy;
92
93 rdmsr(msr_reg[reg], *val, dummy);
94 return 0;
95 }
96
97 return pci_read_config_dword(pdev, pci_reg[reg], val);
98}
99
100static inline int cs5536_write(struct pci_dev *pdev, int reg, int val)
101{
102 if (unlikely(use_msr)) {
103 wrmsr(msr_reg[reg], val, 0);
104 return 0;
105 }
106
107 return pci_write_config_dword(pdev, pci_reg[reg], val);
108}
109
110/**
111 * cs5536_cable_detect - detect cable type
112 * @ap: Port to detect on
113 * @deadline: deadline jiffies for the operation
114 *
115 * Perform cable detection for ATA66 capable cable. Return a libata
116 * cable type.
117 */
118
119static int cs5536_cable_detect(struct ata_port *ap)
120{
121 struct pci_dev *pdev = to_pci_dev(ap->host->dev);
122 u32 cfg;
123
124 cs5536_read(pdev, CFG, &cfg);
125
126 if (cfg & (IDE_CFG_CABLE << ap->port_no))
127 return ATA_CBL_PATA80;
128 else
129 return ATA_CBL_PATA40;
130}
131
132/**
133 * cs5536_set_piomode - PIO setup
134 * @ap: ATA interface
135 * @adev: device on the interface
136 */
137
138static void cs5536_set_piomode(struct ata_port *ap, struct ata_device *adev)
139{
140 static const u8 drv_timings[5] = {
141 0x98, 0x55, 0x32, 0x21, 0x20,
142 };
143
144 static const u8 addr_timings[5] = {
145 0x2, 0x1, 0x0, 0x0, 0x0,
146 };
147
148 static const u8 cmd_timings[5] = {
149 0x99, 0x92, 0x90, 0x22, 0x20,
150 };
151
152 struct pci_dev *pdev = to_pci_dev(ap->host->dev);
153 struct ata_device *pair = ata_dev_pair(adev);
154 int mode = adev->pio_mode - XFER_PIO_0;
155 int cmdmode = mode;
156 int dshift = ap->port_no ? IDE_D1_SHIFT : IDE_D0_SHIFT;
157 int cshift = ap->port_no ? IDE_CAST_D1_SHIFT : IDE_CAST_D0_SHIFT;
158 u32 dtc, cast, etc;
159
160 if (pair)
161 cmdmode = min(mode, pair->pio_mode - XFER_PIO_0);
162
163 cs5536_read(pdev, DTC, &dtc);
164 cs5536_read(pdev, CAST, &cast);
165 cs5536_read(pdev, ETC, &etc);
166
167 dtc &= ~(IDE_DRV_MASK << dshift);
168 dtc |= drv_timings[mode] << dshift;
169
170 cast &= ~(IDE_CAST_DRV_MASK << cshift);
171 cast |= addr_timings[mode] << cshift;
172
173 cast &= ~(IDE_CAST_CMD_MASK << IDE_CAST_CMD_SHIFT);
174 cast |= cmd_timings[cmdmode] << IDE_CAST_CMD_SHIFT;
175
176 etc &= ~(IDE_DRV_MASK << dshift);
177 etc |= IDE_ETC_NODMA << dshift;
178
179 cs5536_write(pdev, DTC, dtc);
180 cs5536_write(pdev, CAST, cast);
181 cs5536_write(pdev, ETC, etc);
182}
183
184/**
185 * cs5536_set_dmamode - DMA timing setup
186 * @ap: ATA interface
187 * @adev: Device being configured
188 *
189 */
190
191static void cs5536_set_dmamode(struct ata_port *ap, struct ata_device *adev)
192{
193 static const u8 udma_timings[6] = {
194 0xc2, 0xc1, 0xc0, 0xc4, 0xc5, 0xc6,
195 };
196
197 static const u8 mwdma_timings[3] = {
198 0x67, 0x21, 0x20,
199 };
200
201 struct pci_dev *pdev = to_pci_dev(ap->host->dev);
202 u32 dtc, etc;
203 int mode = adev->dma_mode;
204 int dshift = ap->port_no ? IDE_D1_SHIFT : IDE_D0_SHIFT;
205
206 if (mode >= XFER_UDMA_0) {
207 cs5536_read(pdev, ETC, &etc);
208
209 etc &= ~(IDE_DRV_MASK << dshift);
210 etc |= udma_timings[mode - XFER_UDMA_0] << dshift;
211
212 cs5536_write(pdev, ETC, etc);
213 } else { /* MWDMA */
214 cs5536_read(pdev, DTC, &dtc);
215
216 dtc &= ~(IDE_DRV_MASK << dshift);
217 dtc |= mwdma_timings[mode] << dshift;
218
219 cs5536_write(pdev, DTC, dtc);
220 }
221}
222
223static struct scsi_host_template cs5536_sht = {
224 .module = THIS_MODULE,
225 .name = DRV_NAME,
226 .ioctl = ata_scsi_ioctl,
227 .queuecommand = ata_scsi_queuecmd,
228 .can_queue = ATA_DEF_QUEUE,
229 .this_id = ATA_SHT_THIS_ID,
230 .sg_tablesize = LIBATA_MAX_PRD,
231 .cmd_per_lun = ATA_SHT_CMD_PER_LUN,
232 .emulated = ATA_SHT_EMULATED,
233 .use_clustering = ATA_SHT_USE_CLUSTERING,
234 .proc_name = DRV_NAME,
235 .dma_boundary = ATA_DMA_BOUNDARY,
236 .slave_configure = ata_scsi_slave_config,
237 .slave_destroy = ata_scsi_slave_destroy,
238 .bios_param = ata_std_bios_param,
239};
240
241static struct ata_port_operations cs5536_port_ops = {
242 .port_disable = ata_port_disable,
243 .set_piomode = cs5536_set_piomode,
244 .set_dmamode = cs5536_set_dmamode,
245 .mode_filter = ata_pci_default_filter,
246
247 .tf_load = ata_tf_load,
248 .tf_read = ata_tf_read,
249 .check_status = ata_check_status,
250 .exec_command = ata_exec_command,
251 .dev_select = ata_std_dev_select,
252
253 .freeze = ata_bmdma_freeze,
254 .thaw = ata_bmdma_thaw,
255 .error_handler = ata_bmdma_error_handler,
256 .post_internal_cmd = ata_bmdma_post_internal_cmd,
257 .cable_detect = cs5536_cable_detect,
258
259 .bmdma_setup = ata_bmdma_setup,
260 .bmdma_start = ata_bmdma_start,
261 .bmdma_stop = ata_bmdma_stop,
262 .bmdma_status = ata_bmdma_status,
263
264 .qc_prep = ata_qc_prep,
265 .qc_issue = ata_qc_issue_prot,
266
267 .data_xfer = ata_data_xfer,
268
269 .irq_handler = ata_interrupt,
270 .irq_clear = ata_bmdma_irq_clear,
271 .irq_on = ata_irq_on,
272 .irq_ack = ata_irq_ack,
273
274 .port_start = ata_port_start,
275};
276
277/**
278 * cs5536_init_one
279 * @dev: PCI device
280 * @id: Entry in match table
281 *
282 */
283
284static int cs5536_init_one(struct pci_dev *dev, const struct pci_device_id *id)
285{
286 static const struct ata_port_info info = {
287 .sht = &cs5536_sht,
288 .flags = ATA_FLAG_SLAVE_POSS,
289 .pio_mask = 0x1f,
290 .mwdma_mask = 0x07,
291 .udma_mask = ATA_UDMA5,
292 .port_ops = &cs5536_port_ops,
293 };
294
295 const struct ata_port_info *ppi[] = { &info, &ata_dummy_port_info };
296 u32 cfg;
297
298 if (use_msr)
299 printk(KERN_ERR DRV_NAME ": Using MSR regs instead of PCI\n");
300
301 cs5536_read(dev, CFG, &cfg);
302
303 if ((cfg & IDE_CFG_CHANEN) == 0) {
304 printk(KERN_ERR DRV_NAME ": disabled by BIOS\n");
305 return -ENODEV;
306 }
307
308 return ata_pci_init_one(dev, ppi);
309}
310
311static const struct pci_device_id cs5536[] = {
312 { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CS5536_IDE), },
313 { },
314};
315
316static struct pci_driver cs5536_pci_driver = {
317 .name = DRV_NAME,
318 .id_table = cs5536,
319 .probe = cs5536_init_one,
320 .remove = ata_pci_remove_one,
321#ifdef CONFIG_PM
322 .suspend = ata_pci_device_suspend,
323 .resume = ata_pci_device_resume,
324#endif
325};
326
327static int __init cs5536_init(void)
328{
329 return pci_register_driver(&cs5536_pci_driver);
330}
331
332static void __exit cs5536_exit(void)
333{
334 pci_unregister_driver(&cs5536_pci_driver);
335}
336
337MODULE_AUTHOR("Martin K. Petersen");
338MODULE_DESCRIPTION("low-level driver for the CS5536 IDE controller");
339MODULE_LICENSE("GPL");
340MODULE_DEVICE_TABLE(pci, cs5536);
341MODULE_VERSION(DRV_VERSION);
342module_param_named(msr, use_msr, int, 0644);
343MODULE_PARM_DESC(msr, "Force using MSR to configure IDE function (Default: 0)");
344
345module_init(cs5536_init);
346module_exit(cs5536_exit);