]>
Commit | Line | Data |
---|---|---|
09c434b8 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
1da177e4 | 2 | /* |
1da177e4 LT |
3 | * Copyright (C) 1996-1998 Linus Torvalds & authors (see below) |
4 | */ | |
5 | ||
6 | /* | |
7 | * Authors: | |
8 | * Jaromir Koutek <miri@punknet.cz>, | |
9 | * Jan Harkes <jaharkes@cwi.nl>, | |
10 | * Mark Lord <mlord@pobox.com> | |
11 | * Some parts of code are from ali14xx.c and from rz1000.c. | |
1da177e4 LT |
12 | */ |
13 | ||
1da177e4 LT |
14 | #include <linux/types.h> |
15 | #include <linux/module.h> | |
16 | #include <linux/kernel.h> | |
1da177e4 | 17 | #include <linux/pci.h> |
1da177e4 LT |
18 | #include <linux/ide.h> |
19 | ||
20 | #include <asm/io.h> | |
21 | ||
ced3ec8a BZ |
22 | #define DRV_NAME "opti621" |
23 | ||
1da177e4 LT |
24 | #define READ_REG 0 /* index of Read cycle timing register */ |
25 | #define WRITE_REG 1 /* index of Write cycle timing register */ | |
26 | #define CNTRL_REG 3 /* index of Control register */ | |
27 | #define STRAP_REG 5 /* index of Strap register */ | |
28 | #define MISC_REG 6 /* index of Miscellaneous register */ | |
29 | ||
30 | static int reg_base; | |
31 | ||
e65dde71 BZ |
32 | static DEFINE_SPINLOCK(opti621_lock); |
33 | ||
1da177e4 LT |
34 | /* Write value to register reg, base of register |
35 | * is at reg_base (0x1f0 primary, 0x170 secondary, | |
36 | * if not changed by PCI configuration). | |
37 | * This is from setupvic.exe program. | |
38 | */ | |
0ecdca26 | 39 | static void write_reg(u8 value, int reg) |
1da177e4 | 40 | { |
0ecdca26 BZ |
41 | inw(reg_base + 1); |
42 | inw(reg_base + 1); | |
43 | outb(3, reg_base + 2); | |
44 | outb(value, reg_base + reg); | |
45 | outb(0x83, reg_base + 2); | |
1da177e4 LT |
46 | } |
47 | ||
1da177e4 LT |
48 | /* Read value from register reg, base of register |
49 | * is at reg_base (0x1f0 primary, 0x170 secondary, | |
50 | * if not changed by PCI configuration). | |
51 | * This is from setupvic.exe program. | |
52 | */ | |
0ecdca26 | 53 | static u8 read_reg(int reg) |
1da177e4 LT |
54 | { |
55 | u8 ret = 0; | |
56 | ||
0ecdca26 BZ |
57 | inw(reg_base + 1); |
58 | inw(reg_base + 1); | |
59 | outb(3, reg_base + 2); | |
60 | ret = inb(reg_base + reg); | |
61 | outb(0x83, reg_base + 2); | |
62 | ||
1da177e4 LT |
63 | return ret; |
64 | } | |
65 | ||
e085b3ca | 66 | static void opti621_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) |
1da177e4 | 67 | { |
7e59ea21 | 68 | ide_drive_t *pair = ide_get_pair_dev(drive); |
1da177e4 | 69 | unsigned long flags; |
e085b3ca BZ |
70 | unsigned long mode = drive->pio_mode, pair_mode; |
71 | const u8 pio = mode - XFER_PIO_0; | |
810253d4 BZ |
72 | u8 tim, misc, addr_pio = pio, clk; |
73 | ||
74 | /* DRDY is default 2 (by OPTi Databook) */ | |
80a65fc5 BZ |
75 | static const u8 addr_timings[2][5] = { |
76 | { 0x20, 0x10, 0x00, 0x00, 0x00 }, /* 33 MHz */ | |
77 | { 0x10, 0x10, 0x00, 0x00, 0x00 }, /* 25 MHz */ | |
810253d4 | 78 | }; |
80a65fc5 BZ |
79 | static const u8 data_rec_timings[2][5] = { |
80 | { 0x5b, 0x45, 0x32, 0x21, 0x20 }, /* 33 MHz */ | |
81 | { 0x48, 0x34, 0x21, 0x10, 0x10 } /* 25 MHz */ | |
810253d4 | 82 | }; |
1da177e4 | 83 | |
5bfb151f | 84 | ide_set_drivedata(drive, (void *)mode); |
6c987183 | 85 | |
7e59ea21 | 86 | if (pair) { |
5bfb151f JR |
87 | pair_mode = (unsigned long)ide_get_drivedata(pair); |
88 | if (pair_mode && pair_mode < mode) | |
89 | addr_pio = pair_mode - XFER_PIO_0; | |
6c987183 | 90 | } |
1da177e4 | 91 | |
21bd33a6 BZ |
92 | spin_lock_irqsave(&opti621_lock, flags); |
93 | ||
94 | reg_base = hwif->io_ports.data_addr; | |
95 | ||
96 | /* allow Register-B */ | |
97 | outb(0xc0, reg_base + CNTRL_REG); | |
98 | /* hmm, setupvic.exe does this ;-) */ | |
99 | outb(0xff, reg_base + 5); | |
100 | /* if reads 0xff, adapter not exist? */ | |
101 | (void)inb(reg_base + CNTRL_REG); | |
102 | /* if reads 0xc0, no interface exist? */ | |
103 | read_reg(CNTRL_REG); | |
104 | ||
105 | /* check CLK speed */ | |
106 | clk = read_reg(STRAP_REG) & 1; | |
107 | ||
108 | printk(KERN_INFO "%s: CLK = %d MHz\n", hwif->name, clk ? 25 : 33); | |
109 | ||
810253d4 BZ |
110 | tim = data_rec_timings[clk][pio]; |
111 | misc = addr_timings[clk][addr_pio]; | |
1da177e4 | 112 | |
6c987183 | 113 | /* select Index-0/1 for Register-A/B */ |
123995b9 | 114 | write_reg(drive->dn & 1, MISC_REG); |
0ecdca26 | 115 | /* set read cycle timings */ |
810253d4 | 116 | write_reg(tim, READ_REG); |
0ecdca26 | 117 | /* set write cycle timings */ |
810253d4 | 118 | write_reg(tim, WRITE_REG); |
1da177e4 | 119 | |
1da177e4 LT |
120 | /* use Register-A for drive 0 */ |
121 | /* use Register-B for drive 1 */ | |
0ecdca26 | 122 | write_reg(0x85, CNTRL_REG); |
1da177e4 LT |
123 | |
124 | /* set address setup, DRDY timings, */ | |
125 | /* and read prefetch for both drives */ | |
0ecdca26 | 126 | write_reg(misc, MISC_REG); |
1da177e4 | 127 | |
e65dde71 | 128 | spin_unlock_irqrestore(&opti621_lock, flags); |
1da177e4 LT |
129 | } |
130 | ||
ac95beed | 131 | static const struct ide_port_ops opti621_port_ops = { |
ac95beed BZ |
132 | .set_pio_mode = opti621_set_pio_mode, |
133 | }; | |
1da177e4 | 134 | |
fe31edc8 | 135 | static const struct ide_port_info opti621_chipset = { |
ced3ec8a | 136 | .name = DRV_NAME, |
80a65fc5 BZ |
137 | .enablebits = { {0x45, 0x80, 0x00}, {0x40, 0x08, 0x00} }, |
138 | .port_ops = &opti621_port_ops, | |
139 | .host_flags = IDE_HFLAG_NO_DMA, | |
140 | .pio_mask = ATA_PIO4, | |
1da177e4 LT |
141 | }; |
142 | ||
fe31edc8 | 143 | static int opti621_init_one(struct pci_dev *dev, const struct pci_device_id *id) |
1da177e4 | 144 | { |
6cdf6eb3 | 145 | return ide_pci_init_one(dev, &opti621_chipset, NULL); |
1da177e4 LT |
146 | } |
147 | ||
9cbcc5e3 BZ |
148 | static const struct pci_device_id opti621_pci_tbl[] = { |
149 | { PCI_VDEVICE(OPTI, PCI_DEVICE_ID_OPTI_82C621), 0 }, | |
80a65fc5 | 150 | { PCI_VDEVICE(OPTI, PCI_DEVICE_ID_OPTI_82C825), 0 }, |
1da177e4 LT |
151 | { 0, }, |
152 | }; | |
153 | MODULE_DEVICE_TABLE(pci, opti621_pci_tbl); | |
154 | ||
a9ab09e2 | 155 | static struct pci_driver opti621_pci_driver = { |
1da177e4 LT |
156 | .name = "Opti621_IDE", |
157 | .id_table = opti621_pci_tbl, | |
158 | .probe = opti621_init_one, | |
adc7f85a | 159 | .remove = ide_pci_remove, |
feb22b7f BZ |
160 | .suspend = ide_pci_suspend, |
161 | .resume = ide_pci_resume, | |
1da177e4 LT |
162 | }; |
163 | ||
82ab1eec | 164 | static int __init opti621_ide_init(void) |
1da177e4 | 165 | { |
a9ab09e2 | 166 | return ide_pci_register_driver(&opti621_pci_driver); |
1da177e4 LT |
167 | } |
168 | ||
adc7f85a BZ |
169 | static void __exit opti621_ide_exit(void) |
170 | { | |
a9ab09e2 | 171 | pci_unregister_driver(&opti621_pci_driver); |
adc7f85a BZ |
172 | } |
173 | ||
1da177e4 | 174 | module_init(opti621_ide_init); |
adc7f85a | 175 | module_exit(opti621_ide_exit); |
1da177e4 LT |
176 | |
177 | MODULE_AUTHOR("Jaromir Koutek, Jan Harkes, Mark Lord"); | |
178 | MODULE_DESCRIPTION("PCI driver module for Opti621 IDE"); | |
179 | MODULE_LICENSE("GPL"); |