Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * cpcihp_zt5550.c | |
3 | * | |
4 | * Intel/Ziatech ZT5550 CompactPCI Host Controller driver | |
5 | * | |
6 | * Copyright 2002 SOMA Networks, Inc. | |
7 | * Copyright 2001 Intel San Luis Obispo | |
8 | * Copyright 2000,2001 MontaVista Software Inc. | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify it | |
11 | * under the terms of the GNU General Public License as published by the | |
12 | * Free Software Foundation; either version 2 of the License, or (at your | |
13 | * option) any later version. | |
14 | * | |
15 | * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | |
16 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY | |
17 | * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL | |
18 | * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
19 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
20 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
22 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
25 | * | |
26 | * You should have received a copy of the GNU General Public License along | |
27 | * with this program; if not, write to the Free Software Foundation, Inc., | |
28 | * 675 Mass Ave, Cambridge, MA 02139, USA. | |
29 | * | |
30 | * Send feedback to <scottm@somanetworks.com> | |
31 | */ | |
32 | ||
1da177e4 LT |
33 | #include <linux/module.h> |
34 | #include <linux/moduleparam.h> | |
35 | #include <linux/init.h> | |
36 | #include <linux/errno.h> | |
37 | #include <linux/pci.h> | |
6b4486e2 TG |
38 | #include <linux/interrupt.h> |
39 | #include <linux/signal.h> /* IRQF_SHARED */ | |
1da177e4 LT |
40 | #include "cpci_hotplug.h" |
41 | #include "cpcihp_zt5550.h" | |
42 | ||
43 | #define DRIVER_VERSION "0.2" | |
44 | #define DRIVER_AUTHOR "Scott Murray <scottm@somanetworks.com>" | |
45 | #define DRIVER_DESC "ZT5550 CompactPCI Hot Plug Driver" | |
46 | ||
47 | #define MY_NAME "cpcihp_zt5550" | |
48 | ||
49 | #define dbg(format, arg...) \ | |
50 | do { \ | |
51 | if(debug) \ | |
52 | printk (KERN_DEBUG "%s: " format "\n", \ | |
53 | MY_NAME , ## arg); \ | |
54 | } while(0) | |
55 | #define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg) | |
56 | #define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg) | |
57 | #define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg) | |
58 | ||
59 | /* local variables */ | |
60 | static int debug; | |
61 | static int poll; | |
62 | static struct cpci_hp_controller_ops zt5550_hpc_ops; | |
63 | static struct cpci_hp_controller zt5550_hpc; | |
64 | ||
65 | /* Primary cPCI bus bridge device */ | |
66 | static struct pci_dev *bus0_dev; | |
67 | static struct pci_bus *bus0; | |
68 | ||
69 | /* Host controller device */ | |
70 | static struct pci_dev *hc_dev; | |
71 | ||
72 | /* Host controller register addresses */ | |
73 | static void __iomem *hc_registers; | |
74 | static void __iomem *csr_hc_index; | |
75 | static void __iomem *csr_hc_data; | |
76 | static void __iomem *csr_int_status; | |
77 | static void __iomem *csr_int_mask; | |
78 | ||
79 | ||
80 | static int zt5550_hc_config(struct pci_dev *pdev) | |
81 | { | |
c8920f0c BH |
82 | int ret; |
83 | ||
1da177e4 LT |
84 | /* Since we know that no boards exist with two HC chips, treat it as an error */ |
85 | if(hc_dev) { | |
86 | err("too many host controller devices?"); | |
87 | return -EBUSY; | |
88 | } | |
c8920f0c BH |
89 | |
90 | ret = pci_enable_device(pdev); | |
91 | if(ret) { | |
92 | err("cannot enable %s\n", pci_name(pdev)); | |
93 | return ret; | |
94 | } | |
95 | ||
1da177e4 LT |
96 | hc_dev = pdev; |
97 | dbg("hc_dev = %p", hc_dev); | |
1396a8c3 GKH |
98 | dbg("pci resource start %llx", (unsigned long long)pci_resource_start(hc_dev, 1)); |
99 | dbg("pci resource len %llx", (unsigned long long)pci_resource_len(hc_dev, 1)); | |
1da177e4 LT |
100 | |
101 | if(!request_mem_region(pci_resource_start(hc_dev, 1), | |
102 | pci_resource_len(hc_dev, 1), MY_NAME)) { | |
103 | err("cannot reserve MMIO region"); | |
c8920f0c BH |
104 | ret = -ENOMEM; |
105 | goto exit_disable_device; | |
1da177e4 LT |
106 | } |
107 | ||
108 | hc_registers = | |
109 | ioremap(pci_resource_start(hc_dev, 1), pci_resource_len(hc_dev, 1)); | |
110 | if(!hc_registers) { | |
1396a8c3 GKH |
111 | err("cannot remap MMIO region %llx @ %llx", |
112 | (unsigned long long)pci_resource_len(hc_dev, 1), | |
113 | (unsigned long long)pci_resource_start(hc_dev, 1)); | |
c8920f0c BH |
114 | ret = -ENODEV; |
115 | goto exit_release_region; | |
1da177e4 LT |
116 | } |
117 | ||
118 | csr_hc_index = hc_registers + CSR_HCINDEX; | |
119 | csr_hc_data = hc_registers + CSR_HCDATA; | |
120 | csr_int_status = hc_registers + CSR_INTSTAT; | |
121 | csr_int_mask = hc_registers + CSR_INTMASK; | |
122 | ||
123 | /* | |
124 | * Disable host control, fault and serial interrupts | |
125 | */ | |
126 | dbg("disabling host control, fault and serial interrupts"); | |
127 | writeb((u8) HC_INT_MASK_REG, csr_hc_index); | |
128 | writeb((u8) ALL_INDEXED_INTS_MASK, csr_hc_data); | |
129 | dbg("disabled host control, fault and serial interrupts"); | |
130 | ||
131 | /* | |
132 | * Disable timer0, timer1 and ENUM interrupts | |
133 | */ | |
134 | dbg("disabling timer0, timer1 and ENUM interrupts"); | |
135 | writeb((u8) ALL_DIRECT_INTS_MASK, csr_int_mask); | |
136 | dbg("disabled timer0, timer1 and ENUM interrupts"); | |
137 | return 0; | |
c8920f0c BH |
138 | |
139 | exit_release_region: | |
140 | release_mem_region(pci_resource_start(hc_dev, 1), | |
141 | pci_resource_len(hc_dev, 1)); | |
142 | exit_disable_device: | |
143 | pci_disable_device(hc_dev); | |
144 | return ret; | |
1da177e4 LT |
145 | } |
146 | ||
147 | static int zt5550_hc_cleanup(void) | |
148 | { | |
149 | if(!hc_dev) | |
150 | return -ENODEV; | |
151 | ||
152 | iounmap(hc_registers); | |
153 | release_mem_region(pci_resource_start(hc_dev, 1), | |
154 | pci_resource_len(hc_dev, 1)); | |
c8920f0c | 155 | pci_disable_device(hc_dev); |
1da177e4 LT |
156 | return 0; |
157 | } | |
158 | ||
159 | static int zt5550_hc_query_enum(void) | |
160 | { | |
161 | u8 value; | |
162 | ||
163 | value = inb_p(ENUM_PORT); | |
164 | return ((value & ENUM_MASK) == ENUM_MASK); | |
165 | } | |
166 | ||
167 | static int zt5550_hc_check_irq(void *dev_id) | |
168 | { | |
169 | int ret; | |
170 | u8 reg; | |
171 | ||
172 | ret = 0; | |
173 | if(dev_id == zt5550_hpc.dev_id) { | |
174 | reg = readb(csr_int_status); | |
175 | if(reg) | |
176 | ret = 1; | |
177 | } | |
178 | return ret; | |
179 | } | |
180 | ||
181 | static int zt5550_hc_enable_irq(void) | |
182 | { | |
183 | u8 reg; | |
184 | ||
185 | if(hc_dev == NULL) { | |
186 | return -ENODEV; | |
187 | } | |
188 | reg = readb(csr_int_mask); | |
189 | reg = reg & ~ENUM_INT_MASK; | |
190 | writeb(reg, csr_int_mask); | |
191 | return 0; | |
192 | } | |
193 | ||
194 | static int zt5550_hc_disable_irq(void) | |
195 | { | |
196 | u8 reg; | |
197 | ||
198 | if(hc_dev == NULL) { | |
199 | return -ENODEV; | |
200 | } | |
201 | ||
202 | reg = readb(csr_int_mask); | |
203 | reg = reg | ENUM_INT_MASK; | |
204 | writeb(reg, csr_int_mask); | |
205 | return 0; | |
206 | } | |
207 | ||
208 | static int zt5550_hc_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) | |
209 | { | |
210 | int status; | |
211 | ||
212 | status = zt5550_hc_config(pdev); | |
213 | if(status != 0) { | |
214 | return status; | |
215 | } | |
216 | dbg("returned from zt5550_hc_config"); | |
217 | ||
218 | memset(&zt5550_hpc, 0, sizeof (struct cpci_hp_controller)); | |
219 | zt5550_hpc_ops.query_enum = zt5550_hc_query_enum; | |
220 | zt5550_hpc.ops = &zt5550_hpc_ops; | |
221 | if(!poll) { | |
222 | zt5550_hpc.irq = hc_dev->irq; | |
6b4486e2 | 223 | zt5550_hpc.irq_flags = IRQF_SHARED; |
1da177e4 LT |
224 | zt5550_hpc.dev_id = hc_dev; |
225 | ||
226 | zt5550_hpc_ops.enable_irq = zt5550_hc_enable_irq; | |
227 | zt5550_hpc_ops.disable_irq = zt5550_hc_disable_irq; | |
228 | zt5550_hpc_ops.check_irq = zt5550_hc_check_irq; | |
229 | } else { | |
230 | info("using ENUM# polling mode"); | |
231 | } | |
232 | ||
233 | status = cpci_hp_register_controller(&zt5550_hpc); | |
234 | if(status != 0) { | |
235 | err("could not register cPCI hotplug controller"); | |
236 | goto init_hc_error; | |
237 | } | |
238 | dbg("registered controller"); | |
239 | ||
240 | /* Look for first device matching cPCI bus's bridge vendor and device IDs */ | |
241 | if(!(bus0_dev = pci_get_device(PCI_VENDOR_ID_DEC, | |
242 | PCI_DEVICE_ID_DEC_21154, NULL))) { | |
243 | status = -ENODEV; | |
244 | goto init_register_error; | |
245 | } | |
246 | bus0 = bus0_dev->subordinate; | |
247 | pci_dev_put(bus0_dev); | |
248 | ||
249 | status = cpci_hp_register_bus(bus0, 0x0a, 0x0f); | |
250 | if(status != 0) { | |
251 | err("could not register cPCI hotplug bus"); | |
252 | goto init_register_error; | |
253 | } | |
254 | dbg("registered bus"); | |
255 | ||
256 | status = cpci_hp_start(); | |
257 | if(status != 0) { | |
258 | err("could not started cPCI hotplug system"); | |
259 | cpci_hp_unregister_bus(bus0); | |
260 | goto init_register_error; | |
261 | } | |
262 | dbg("started cpci hp system"); | |
263 | ||
264 | return 0; | |
265 | init_register_error: | |
266 | cpci_hp_unregister_controller(&zt5550_hpc); | |
267 | init_hc_error: | |
268 | err("status = %d", status); | |
269 | zt5550_hc_cleanup(); | |
270 | return status; | |
271 | ||
272 | } | |
273 | ||
274 | static void __devexit zt5550_hc_remove_one(struct pci_dev *pdev) | |
275 | { | |
276 | cpci_hp_stop(); | |
277 | cpci_hp_unregister_bus(bus0); | |
278 | cpci_hp_unregister_controller(&zt5550_hpc); | |
279 | zt5550_hc_cleanup(); | |
280 | } | |
281 | ||
282 | ||
283 | static struct pci_device_id zt5550_hc_pci_tbl[] = { | |
284 | { PCI_VENDOR_ID_ZIATECH, PCI_DEVICE_ID_ZIATECH_5550_HC, PCI_ANY_ID, PCI_ANY_ID, }, | |
285 | { 0, } | |
286 | }; | |
287 | MODULE_DEVICE_TABLE(pci, zt5550_hc_pci_tbl); | |
288 | ||
289 | static struct pci_driver zt5550_hc_driver = { | |
290 | .name = "zt5550_hc", | |
291 | .id_table = zt5550_hc_pci_tbl, | |
292 | .probe = zt5550_hc_init_one, | |
293 | .remove = __devexit_p(zt5550_hc_remove_one), | |
294 | }; | |
295 | ||
296 | static int __init zt5550_init(void) | |
297 | { | |
298 | struct resource* r; | |
03555d59 | 299 | int rc; |
1da177e4 LT |
300 | |
301 | info(DRIVER_DESC " version: " DRIVER_VERSION); | |
302 | r = request_region(ENUM_PORT, 1, "#ENUM hotswap signal register"); | |
303 | if(!r) | |
304 | return -EBUSY; | |
305 | ||
03555d59 SM |
306 | rc = pci_register_driver(&zt5550_hc_driver); |
307 | if(rc < 0) | |
308 | release_region(ENUM_PORT, 1); | |
309 | return rc; | |
1da177e4 LT |
310 | } |
311 | ||
312 | static void __exit | |
313 | zt5550_exit(void) | |
314 | { | |
315 | pci_unregister_driver(&zt5550_hc_driver); | |
316 | release_region(ENUM_PORT, 1); | |
317 | } | |
318 | ||
319 | module_init(zt5550_init); | |
320 | module_exit(zt5550_exit); | |
321 | ||
322 | MODULE_AUTHOR(DRIVER_AUTHOR); | |
323 | MODULE_DESCRIPTION(DRIVER_DESC); | |
324 | MODULE_LICENSE("GPL"); | |
325 | module_param(debug, bool, 0644); | |
326 | MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); | |
327 | module_param(poll, bool, 0644); | |
328 | MODULE_PARM_DESC(poll, "#ENUM polling mode enabled or not"); |