1 // SPDX-License-Identifier: GPL-2.0-only
4 * (C) 2007 Martin K. Petersen <mkp@mkp.net>
5 * (C) 2009 Bartlomiej Zolnierkiewicz
8 * Available from AMD web site.
10 * The IDE timing registers for the CS5536 live in the Geode Machine
11 * Specific Register file and not PCI config space. Most BIOSes
12 * virtualize the PCI registers so the chip looks like a standard IDE
13 * controller. Unfortunately not all implementations get this right.
14 * In particular some have problems with unaligned accesses to the
15 * virtualized PCI registers. This driver always does full dword
16 * writes to work around the issue. Also, in case of a bad BIOS this
17 * driver can be loaded with the "msr=1" parameter which forces using
18 * the Machine Specific Registers to configure the device.
21 #include <linux/kernel.h>
22 #include <linux/module.h>
23 #include <linux/pci.h>
24 #include <linux/init.h>
25 #include <linux/ide.h>
28 #define DRV_NAME "cs5536"
31 MSR_IDE_CFG
= 0x51300010,
39 IDE_CFG_CHANEN
= (1 << 1),
40 IDE_CFG_CABLE
= (1 << 17) | (1 << 16),
46 IDE_CAST_D0_SHIFT
= 6,
47 IDE_CAST_D1_SHIFT
= 4,
48 IDE_CAST_DRV_MASK
= 0x3,
50 IDE_CAST_CMD_SHIFT
= 24,
51 IDE_CAST_CMD_MASK
= 0xff,
53 IDE_ETC_UDMA_MASK
= 0xc0,
58 static int cs5536_read(struct pci_dev
*pdev
, int reg
, u32
*val
)
60 if (unlikely(use_msr
)) {
63 rdmsr(MSR_IDE_CFG
+ reg
, *val
, dummy
);
67 return pci_read_config_dword(pdev
, PCI_IDE_CFG
+ reg
* 4, val
);
70 static int cs5536_write(struct pci_dev
*pdev
, int reg
, int val
)
72 if (unlikely(use_msr
)) {
73 wrmsr(MSR_IDE_CFG
+ reg
, val
, 0);
77 return pci_write_config_dword(pdev
, PCI_IDE_CFG
+ reg
* 4, val
);
80 static void cs5536_program_dtc(ide_drive_t
*drive
, u8 tim
)
82 struct pci_dev
*pdev
= to_pci_dev(drive
->hwif
->dev
);
83 int dshift
= (drive
->dn
& 1) ? IDE_D1_SHIFT
: IDE_D0_SHIFT
;
86 cs5536_read(pdev
, DTC
, &dtc
);
87 dtc
&= ~(IDE_DRV_MASK
<< dshift
);
89 cs5536_write(pdev
, DTC
, dtc
);
93 * cs5536_cable_detect - detect cable type
94 * @hwif: Port to detect on
96 * Perform cable detection for ATA66 capable cable.
98 * Returns a cable type.
101 static u8
cs5536_cable_detect(ide_hwif_t
*hwif
)
103 struct pci_dev
*pdev
= to_pci_dev(hwif
->dev
);
106 cs5536_read(pdev
, CFG
, &cfg
);
108 if (cfg
& IDE_CFG_CABLE
)
109 return ATA_CBL_PATA80
;
111 return ATA_CBL_PATA40
;
115 * cs5536_set_pio_mode - PIO timing setup
120 static void cs5536_set_pio_mode(ide_hwif_t
*hwif
, ide_drive_t
*drive
)
122 static const u8 drv_timings
[5] = {
123 0x98, 0x55, 0x32, 0x21, 0x20,
126 static const u8 addr_timings
[5] = {
127 0x2, 0x1, 0x0, 0x0, 0x0,
130 static const u8 cmd_timings
[5] = {
131 0x99, 0x92, 0x90, 0x22, 0x20,
134 struct pci_dev
*pdev
= to_pci_dev(hwif
->dev
);
135 ide_drive_t
*pair
= ide_get_pair_dev(drive
);
136 int cshift
= (drive
->dn
& 1) ? IDE_CAST_D1_SHIFT
: IDE_CAST_D0_SHIFT
;
137 unsigned long timings
= (unsigned long)ide_get_drivedata(drive
);
139 const u8 pio
= drive
->pio_mode
- XFER_PIO_0
;
143 cmd_pio
= min_t(u8
, pio
, pair
->pio_mode
- XFER_PIO_0
);
145 timings
&= (IDE_DRV_MASK
<< 8);
146 timings
|= drv_timings
[pio
];
147 ide_set_drivedata(drive
, (void *)timings
);
149 cs5536_program_dtc(drive
, drv_timings
[pio
]);
151 cs5536_read(pdev
, CAST
, &cast
);
153 cast
&= ~(IDE_CAST_DRV_MASK
<< cshift
);
154 cast
|= addr_timings
[pio
] << cshift
;
156 cast
&= ~(IDE_CAST_CMD_MASK
<< IDE_CAST_CMD_SHIFT
);
157 cast
|= cmd_timings
[cmd_pio
] << IDE_CAST_CMD_SHIFT
;
159 cs5536_write(pdev
, CAST
, cast
);
163 * cs5536_set_dma_mode - DMA timing setup
168 static void cs5536_set_dma_mode(ide_hwif_t
*hwif
, ide_drive_t
*drive
)
170 static const u8 udma_timings
[6] = {
171 0xc2, 0xc1, 0xc0, 0xc4, 0xc5, 0xc6,
174 static const u8 mwdma_timings
[3] = {
178 struct pci_dev
*pdev
= to_pci_dev(hwif
->dev
);
179 int dshift
= (drive
->dn
& 1) ? IDE_D1_SHIFT
: IDE_D0_SHIFT
;
180 unsigned long timings
= (unsigned long)ide_get_drivedata(drive
);
182 const u8 mode
= drive
->dma_mode
;
184 cs5536_read(pdev
, ETC
, &etc
);
186 if (mode
>= XFER_UDMA_0
) {
187 etc
&= ~(IDE_DRV_MASK
<< dshift
);
188 etc
|= udma_timings
[mode
- XFER_UDMA_0
] << dshift
;
190 etc
&= ~(IDE_ETC_UDMA_MASK
<< dshift
);
191 timings
&= IDE_DRV_MASK
;
192 timings
|= mwdma_timings
[mode
- XFER_MW_DMA_0
] << 8;
193 ide_set_drivedata(drive
, (void *)timings
);
196 cs5536_write(pdev
, ETC
, etc
);
199 static void cs5536_dma_start(ide_drive_t
*drive
)
201 unsigned long timings
= (unsigned long)ide_get_drivedata(drive
);
203 if (drive
->current_speed
< XFER_UDMA_0
&&
204 (timings
>> 8) != (timings
& IDE_DRV_MASK
))
205 cs5536_program_dtc(drive
, timings
>> 8);
207 ide_dma_start(drive
);
210 static int cs5536_dma_end(ide_drive_t
*drive
)
212 int ret
= ide_dma_end(drive
);
213 unsigned long timings
= (unsigned long)ide_get_drivedata(drive
);
215 if (drive
->current_speed
< XFER_UDMA_0
&&
216 (timings
>> 8) != (timings
& IDE_DRV_MASK
))
217 cs5536_program_dtc(drive
, timings
& IDE_DRV_MASK
);
222 static const struct ide_port_ops cs5536_port_ops
= {
223 .set_pio_mode
= cs5536_set_pio_mode
,
224 .set_dma_mode
= cs5536_set_dma_mode
,
225 .cable_detect
= cs5536_cable_detect
,
228 static const struct ide_dma_ops cs5536_dma_ops
= {
229 .dma_host_set
= ide_dma_host_set
,
230 .dma_setup
= ide_dma_setup
,
231 .dma_start
= cs5536_dma_start
,
232 .dma_end
= cs5536_dma_end
,
233 .dma_test_irq
= ide_dma_test_irq
,
234 .dma_lost_irq
= ide_dma_lost_irq
,
235 .dma_timer_expiry
= ide_dma_sff_timer_expiry
,
236 .dma_sff_read_status
= ide_dma_sff_read_status
,
239 static const struct ide_port_info cs5536_info
= {
241 .port_ops
= &cs5536_port_ops
,
242 .dma_ops
= &cs5536_dma_ops
,
243 .host_flags
= IDE_HFLAG_SINGLE
,
244 .pio_mask
= ATA_PIO4
,
245 .mwdma_mask
= ATA_MWDMA2
,
246 .udma_mask
= ATA_UDMA5
,
252 * @id: Entry in match table
255 static int cs5536_init_one(struct pci_dev
*dev
, const struct pci_device_id
*id
)
260 printk(KERN_INFO DRV_NAME
": Using MSR regs instead of PCI\n");
262 cs5536_read(dev
, CFG
, &cfg
);
264 if ((cfg
& IDE_CFG_CHANEN
) == 0) {
265 printk(KERN_ERR DRV_NAME
": disabled by BIOS\n");
269 return ide_pci_init_one(dev
, &cs5536_info
, NULL
);
272 static const struct pci_device_id cs5536_pci_tbl
[] = {
273 { PCI_VDEVICE(AMD
, PCI_DEVICE_ID_AMD_CS5536_IDE
), },
277 static struct pci_driver cs5536_pci_driver
= {
279 .id_table
= cs5536_pci_tbl
,
280 .probe
= cs5536_init_one
,
281 .remove
= ide_pci_remove
,
282 .suspend
= ide_pci_suspend
,
283 .resume
= ide_pci_resume
,
286 module_pci_driver(cs5536_pci_driver
);
288 MODULE_AUTHOR("Martin K. Petersen, Bartlomiej Zolnierkiewicz");
289 MODULE_DESCRIPTION("low-level driver for the CS5536 IDE controller");
290 MODULE_LICENSE("GPL");
291 MODULE_DEVICE_TABLE(pci
, cs5536_pci_tbl
);
293 module_param_named(msr
, use_msr
, int, 0644);
294 MODULE_PARM_DESC(msr
, "Force using MSR to configure IDE function (Default: 0)");