1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * cpcihp_zt5550.c |
4 | * |
5 | * Intel/Ziatech ZT5550 CompactPCI Host Controller driver |
6 | * |
7 | * Copyright 2002 SOMA Networks, Inc. |
8 | * Copyright 2001 Intel San Luis Obispo |
9 | * Copyright 2000,2001 MontaVista Software Inc. |
10 | * |
11 | * Send feedback to <scottm@somanetworks.com> |
12 | */ |
13 | |
14 | #include <linux/module.h> |
15 | #include <linux/moduleparam.h> |
16 | #include <linux/init.h> |
17 | #include <linux/errno.h> |
18 | #include <linux/pci.h> |
19 | #include <linux/interrupt.h> |
20 | #include <linux/signal.h> /* IRQF_SHARED */ |
21 | #include "cpci_hotplug.h" |
22 | #include "cpcihp_zt5550.h" |
23 | |
24 | #define DRIVER_VERSION "0.2" |
25 | #define DRIVER_AUTHOR "Scott Murray <scottm@somanetworks.com>" |
26 | #define DRIVER_DESC "ZT5550 CompactPCI Hot Plug Driver" |
27 | |
28 | #define MY_NAME "cpcihp_zt5550" |
29 | |
30 | #define dbg(format, arg...) \ |
31 | do { \ |
32 | if (debug) \ |
33 | printk(KERN_DEBUG "%s: " format "\n", \ |
34 | MY_NAME, ## arg); \ |
35 | } while (0) |
36 | #define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME, ## arg) |
37 | #define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME, ## arg) |
38 | #define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME, ## arg) |
39 | |
40 | /* local variables */ |
41 | static bool debug; |
42 | static bool poll; |
43 | static struct cpci_hp_controller_ops zt5550_hpc_ops; |
44 | static struct cpci_hp_controller zt5550_hpc; |
45 | |
46 | /* Primary cPCI bus bridge device */ |
47 | static struct pci_dev *bus0_dev; |
48 | static struct pci_bus *bus0; |
49 | |
50 | /* Host controller device */ |
51 | static struct pci_dev *hc_dev; |
52 | |
53 | /* Host controller register addresses */ |
54 | static void __iomem *hc_registers; |
55 | static void __iomem *csr_hc_index; |
56 | static void __iomem *csr_hc_data; |
57 | static void __iomem *csr_int_status; |
58 | static void __iomem *csr_int_mask; |
59 | |
60 | |
61 | static int zt5550_hc_config(struct pci_dev *pdev) |
62 | { |
63 | int ret; |
64 | |
65 | /* Since we know that no boards exist with two HC chips, treat it as an error */ |
66 | if (hc_dev) { |
67 | err("too many host controller devices?" ); |
68 | return -EBUSY; |
69 | } |
70 | |
71 | ret = pci_enable_device(dev: pdev); |
72 | if (ret) { |
73 | err("cannot enable %s\n" , pci_name(pdev)); |
74 | return ret; |
75 | } |
76 | |
77 | hc_dev = pdev; |
78 | dbg("hc_dev = %p" , hc_dev); |
79 | dbg("pci resource start %llx" , (unsigned long long)pci_resource_start(hc_dev, 1)); |
80 | dbg("pci resource len %llx" , (unsigned long long)pci_resource_len(hc_dev, 1)); |
81 | |
82 | if (!request_mem_region(pci_resource_start(hc_dev, 1), |
83 | pci_resource_len(hc_dev, 1), MY_NAME)) { |
84 | err("cannot reserve MMIO region" ); |
85 | ret = -ENOMEM; |
86 | goto exit_disable_device; |
87 | } |
88 | |
89 | hc_registers = |
90 | ioremap(pci_resource_start(hc_dev, 1), pci_resource_len(hc_dev, 1)); |
91 | if (!hc_registers) { |
92 | err("cannot remap MMIO region %llx @ %llx" , |
93 | (unsigned long long)pci_resource_len(hc_dev, 1), |
94 | (unsigned long long)pci_resource_start(hc_dev, 1)); |
95 | ret = -ENODEV; |
96 | goto exit_release_region; |
97 | } |
98 | |
99 | csr_hc_index = hc_registers + CSR_HCINDEX; |
100 | csr_hc_data = hc_registers + CSR_HCDATA; |
101 | csr_int_status = hc_registers + CSR_INTSTAT; |
102 | csr_int_mask = hc_registers + CSR_INTMASK; |
103 | |
104 | /* |
105 | * Disable host control, fault and serial interrupts |
106 | */ |
107 | dbg("disabling host control, fault and serial interrupts" ); |
108 | writeb(val: (u8) HC_INT_MASK_REG, addr: csr_hc_index); |
109 | writeb(val: (u8) ALL_INDEXED_INTS_MASK, addr: csr_hc_data); |
110 | dbg("disabled host control, fault and serial interrupts" ); |
111 | |
112 | /* |
113 | * Disable timer0, timer1 and ENUM interrupts |
114 | */ |
115 | dbg("disabling timer0, timer1 and ENUM interrupts" ); |
116 | writeb(val: (u8) ALL_DIRECT_INTS_MASK, addr: csr_int_mask); |
117 | dbg("disabled timer0, timer1 and ENUM interrupts" ); |
118 | return 0; |
119 | |
120 | exit_release_region: |
121 | release_mem_region(pci_resource_start(hc_dev, 1), |
122 | pci_resource_len(hc_dev, 1)); |
123 | exit_disable_device: |
124 | pci_disable_device(dev: hc_dev); |
125 | return ret; |
126 | } |
127 | |
128 | static int zt5550_hc_cleanup(void) |
129 | { |
130 | if (!hc_dev) |
131 | return -ENODEV; |
132 | |
133 | iounmap(addr: hc_registers); |
134 | release_mem_region(pci_resource_start(hc_dev, 1), |
135 | pci_resource_len(hc_dev, 1)); |
136 | pci_disable_device(dev: hc_dev); |
137 | return 0; |
138 | } |
139 | |
140 | static int zt5550_hc_query_enum(void) |
141 | { |
142 | u8 value; |
143 | |
144 | value = inb_p(ENUM_PORT); |
145 | return ((value & ENUM_MASK) == ENUM_MASK); |
146 | } |
147 | |
148 | static int zt5550_hc_check_irq(void *dev_id) |
149 | { |
150 | int ret; |
151 | u8 reg; |
152 | |
153 | ret = 0; |
154 | if (dev_id == zt5550_hpc.dev_id) { |
155 | reg = readb(addr: csr_int_status); |
156 | if (reg) |
157 | ret = 1; |
158 | } |
159 | return ret; |
160 | } |
161 | |
162 | static int zt5550_hc_enable_irq(void) |
163 | { |
164 | u8 reg; |
165 | |
166 | if (hc_dev == NULL) |
167 | return -ENODEV; |
168 | |
169 | reg = readb(addr: csr_int_mask); |
170 | reg = reg & ~ENUM_INT_MASK; |
171 | writeb(val: reg, addr: csr_int_mask); |
172 | return 0; |
173 | } |
174 | |
175 | static int zt5550_hc_disable_irq(void) |
176 | { |
177 | u8 reg; |
178 | |
179 | if (hc_dev == NULL) |
180 | return -ENODEV; |
181 | |
182 | reg = readb(addr: csr_int_mask); |
183 | reg = reg | ENUM_INT_MASK; |
184 | writeb(val: reg, addr: csr_int_mask); |
185 | return 0; |
186 | } |
187 | |
188 | static int zt5550_hc_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) |
189 | { |
190 | int status; |
191 | |
192 | status = zt5550_hc_config(pdev); |
193 | if (status != 0) |
194 | return status; |
195 | |
196 | dbg("returned from zt5550_hc_config" ); |
197 | |
198 | memset(&zt5550_hpc, 0, sizeof(struct cpci_hp_controller)); |
199 | zt5550_hpc_ops.query_enum = zt5550_hc_query_enum; |
200 | zt5550_hpc.ops = &zt5550_hpc_ops; |
201 | if (!poll) { |
202 | zt5550_hpc.irq = hc_dev->irq; |
203 | zt5550_hpc.irq_flags = IRQF_SHARED; |
204 | zt5550_hpc.dev_id = hc_dev; |
205 | |
206 | zt5550_hpc_ops.enable_irq = zt5550_hc_enable_irq; |
207 | zt5550_hpc_ops.disable_irq = zt5550_hc_disable_irq; |
208 | zt5550_hpc_ops.check_irq = zt5550_hc_check_irq; |
209 | } else { |
210 | info("using ENUM# polling mode" ); |
211 | } |
212 | |
213 | status = cpci_hp_register_controller(controller: &zt5550_hpc); |
214 | if (status != 0) { |
215 | err("could not register cPCI hotplug controller" ); |
216 | goto init_hc_error; |
217 | } |
218 | dbg("registered controller" ); |
219 | |
220 | /* Look for first device matching cPCI bus's bridge vendor and device IDs */ |
221 | bus0_dev = pci_get_device(PCI_VENDOR_ID_DEC, |
222 | PCI_DEVICE_ID_DEC_21154, NULL); |
223 | if (!bus0_dev) { |
224 | status = -ENODEV; |
225 | goto init_register_error; |
226 | } |
227 | bus0 = bus0_dev->subordinate; |
228 | pci_dev_put(dev: bus0_dev); |
229 | |
230 | status = cpci_hp_register_bus(bus: bus0, first: 0x0a, last: 0x0f); |
231 | if (status != 0) { |
232 | err("could not register cPCI hotplug bus" ); |
233 | goto init_register_error; |
234 | } |
235 | dbg("registered bus" ); |
236 | |
237 | status = cpci_hp_start(); |
238 | if (status != 0) { |
239 | err("could not started cPCI hotplug system" ); |
240 | cpci_hp_unregister_bus(bus: bus0); |
241 | goto init_register_error; |
242 | } |
243 | dbg("started cpci hp system" ); |
244 | |
245 | return 0; |
246 | init_register_error: |
247 | cpci_hp_unregister_controller(controller: &zt5550_hpc); |
248 | init_hc_error: |
249 | err("status = %d" , status); |
250 | zt5550_hc_cleanup(); |
251 | return status; |
252 | |
253 | } |
254 | |
255 | static void zt5550_hc_remove_one(struct pci_dev *pdev) |
256 | { |
257 | cpci_hp_stop(); |
258 | cpci_hp_unregister_bus(bus: bus0); |
259 | cpci_hp_unregister_controller(controller: &zt5550_hpc); |
260 | zt5550_hc_cleanup(); |
261 | } |
262 | |
263 | |
264 | static const struct pci_device_id zt5550_hc_pci_tbl[] = { |
265 | { PCI_VENDOR_ID_ZIATECH, PCI_DEVICE_ID_ZIATECH_5550_HC, PCI_ANY_ID, PCI_ANY_ID, }, |
266 | { 0, } |
267 | }; |
268 | MODULE_DEVICE_TABLE(pci, zt5550_hc_pci_tbl); |
269 | |
270 | static struct pci_driver zt5550_hc_driver = { |
271 | .name = "zt5550_hc" , |
272 | .id_table = zt5550_hc_pci_tbl, |
273 | .probe = zt5550_hc_init_one, |
274 | .remove = zt5550_hc_remove_one, |
275 | }; |
276 | |
277 | static int __init zt5550_init(void) |
278 | { |
279 | struct resource *r; |
280 | int rc; |
281 | |
282 | info(DRIVER_DESC " version: " DRIVER_VERSION); |
283 | r = request_region(ENUM_PORT, 1, "#ENUM hotswap signal register" ); |
284 | if (!r) |
285 | return -EBUSY; |
286 | |
287 | rc = pci_register_driver(&zt5550_hc_driver); |
288 | if (rc < 0) |
289 | release_region(ENUM_PORT, 1); |
290 | return rc; |
291 | } |
292 | |
293 | static void __exit |
294 | zt5550_exit(void) |
295 | { |
296 | pci_unregister_driver(dev: &zt5550_hc_driver); |
297 | release_region(ENUM_PORT, 1); |
298 | } |
299 | |
300 | module_init(zt5550_init); |
301 | module_exit(zt5550_exit); |
302 | |
303 | MODULE_AUTHOR(DRIVER_AUTHOR); |
304 | MODULE_DESCRIPTION(DRIVER_DESC); |
305 | MODULE_LICENSE("GPL" ); |
306 | module_param(debug, bool, 0644); |
307 | MODULE_PARM_DESC(debug, "Debugging mode enabled or not" ); |
308 | module_param(poll, bool, 0644); |
309 | MODULE_PARM_DESC(poll, "#ENUM polling mode enabled or not" ); |
310 | |