diff options
author | Chris Metcalf <cmetcalf@tilera.com> | 2011-05-02 15:09:42 -0400 |
---|---|---|
committer | Chris Metcalf <cmetcalf@tilera.com> | 2011-05-04 14:39:53 -0400 |
commit | 398fa5a9319797e43f67b215337afe62e39475ef (patch) | |
tree | 44d966997a6baa6b75a176526d981e3ffb32e471 /arch/tile/kernel | |
parent | 313ce674d3cbc2d48ed34a9462427920ac54f4ad (diff) |
arch/tile: improve support for PCI hotplug
Note that this is not complete hot-plug support; hot-unplug is not included.
Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>
Diffstat (limited to 'arch/tile/kernel')
-rw-r--r-- | arch/tile/kernel/pci.c | 207 |
1 files changed, 117 insertions, 90 deletions
diff --git a/arch/tile/kernel/pci.c b/arch/tile/kernel/pci.c index ea38f0c9ec7c..65add0270bb6 100644 --- a/arch/tile/kernel/pci.c +++ b/arch/tile/kernel/pci.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright 2010 Tilera Corporation. All Rights Reserved. | 2 | * Copyright 2011 Tilera Corporation. All Rights Reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or | 4 | * This program is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU General Public License | 5 | * modify it under the terms of the GNU General Public License |
@@ -59,6 +59,7 @@ int __write_once tile_plx_gen1; | |||
59 | 59 | ||
60 | static struct pci_controller controllers[TILE_NUM_PCIE]; | 60 | static struct pci_controller controllers[TILE_NUM_PCIE]; |
61 | static int num_controllers; | 61 | static int num_controllers; |
62 | static int pci_scan_flags[TILE_NUM_PCIE]; | ||
62 | 63 | ||
63 | static struct pci_ops tile_cfg_ops; | 64 | static struct pci_ops tile_cfg_ops; |
64 | 65 | ||
@@ -79,7 +80,7 @@ EXPORT_SYMBOL(pcibios_align_resource); | |||
79 | * controller_id is the controller number, config type is 0 or 1 for | 80 | * controller_id is the controller number, config type is 0 or 1 for |
80 | * config0 or config1 operations. | 81 | * config0 or config1 operations. |
81 | */ | 82 | */ |
82 | static int __init tile_pcie_open(int controller_id, int config_type) | 83 | static int __devinit tile_pcie_open(int controller_id, int config_type) |
83 | { | 84 | { |
84 | char filename[32]; | 85 | char filename[32]; |
85 | int fd; | 86 | int fd; |
@@ -95,7 +96,7 @@ static int __init tile_pcie_open(int controller_id, int config_type) | |||
95 | /* | 96 | /* |
96 | * Get the IRQ numbers from the HV and set up the handlers for them. | 97 | * Get the IRQ numbers from the HV and set up the handlers for them. |
97 | */ | 98 | */ |
98 | static int __init tile_init_irqs(int controller_id, | 99 | static int __devinit tile_init_irqs(int controller_id, |
99 | struct pci_controller *controller) | 100 | struct pci_controller *controller) |
100 | { | 101 | { |
101 | char filename[32]; | 102 | char filename[32]; |
@@ -139,71 +140,80 @@ static int __init tile_init_irqs(int controller_id, | |||
139 | * | 140 | * |
140 | * Returns the number of controllers discovered. | 141 | * Returns the number of controllers discovered. |
141 | */ | 142 | */ |
142 | int __init tile_pci_init(void) | 143 | int __devinit tile_pci_init(void) |
143 | { | 144 | { |
144 | int i; | 145 | int i; |
145 | 146 | ||
146 | pr_info("PCI: Searching for controllers...\n"); | 147 | pr_info("PCI: Searching for controllers...\n"); |
147 | 148 | ||
149 | /* Re-init number of PCIe controllers to support hot-plug feature. */ | ||
150 | num_controllers = 0; | ||
151 | |||
148 | /* Do any configuration we need before using the PCIe */ | 152 | /* Do any configuration we need before using the PCIe */ |
149 | 153 | ||
150 | for (i = 0; i < TILE_NUM_PCIE; i++) { | 154 | for (i = 0; i < TILE_NUM_PCIE; i++) { |
151 | int hv_cfg_fd0 = -1; | ||
152 | int hv_cfg_fd1 = -1; | ||
153 | int hv_mem_fd = -1; | ||
154 | char name[32]; | ||
155 | struct pci_controller *controller; | ||
156 | |||
157 | /* | 155 | /* |
158 | * Open the fd to the HV. If it fails then this | 156 | * To see whether we need a real config op based on |
159 | * device doesn't exist. | 157 | * the results of pcibios_init(), to support PCIe hot-plug. |
160 | */ | 158 | */ |
161 | hv_cfg_fd0 = tile_pcie_open(i, 0); | 159 | if (pci_scan_flags[i] == 0) { |
162 | if (hv_cfg_fd0 < 0) | 160 | int hv_cfg_fd0 = -1; |
163 | continue; | 161 | int hv_cfg_fd1 = -1; |
164 | hv_cfg_fd1 = tile_pcie_open(i, 1); | 162 | int hv_mem_fd = -1; |
165 | if (hv_cfg_fd1 < 0) { | 163 | char name[32]; |
166 | pr_err("PCI: Couldn't open config fd to HV " | 164 | struct pci_controller *controller; |
167 | "for controller %d\n", i); | 165 | |
168 | goto err_cont; | 166 | /* |
169 | } | 167 | * Open the fd to the HV. If it fails then this |
168 | * device doesn't exist. | ||
169 | */ | ||
170 | hv_cfg_fd0 = tile_pcie_open(i, 0); | ||
171 | if (hv_cfg_fd0 < 0) | ||
172 | continue; | ||
173 | hv_cfg_fd1 = tile_pcie_open(i, 1); | ||
174 | if (hv_cfg_fd1 < 0) { | ||
175 | pr_err("PCI: Couldn't open config fd to HV " | ||
176 | "for controller %d\n", i); | ||
177 | goto err_cont; | ||
178 | } | ||
170 | 179 | ||
171 | sprintf(name, "pcie/%d/mem", i); | 180 | sprintf(name, "pcie/%d/mem", i); |
172 | hv_mem_fd = hv_dev_open((HV_VirtAddr)name, 0); | 181 | hv_mem_fd = hv_dev_open((HV_VirtAddr)name, 0); |
173 | if (hv_mem_fd < 0) { | 182 | if (hv_mem_fd < 0) { |
174 | pr_err("PCI: Could not open mem fd to HV!\n"); | 183 | pr_err("PCI: Could not open mem fd to HV!\n"); |
175 | goto err_cont; | 184 | goto err_cont; |
176 | } | 185 | } |
177 | 186 | ||
178 | pr_info("PCI: Found PCI controller #%d\n", i); | 187 | pr_info("PCI: Found PCI controller #%d\n", i); |
179 | 188 | ||
180 | controller = &controllers[num_controllers]; | 189 | controller = &controllers[i]; |
181 | 190 | ||
182 | if (tile_init_irqs(i, controller)) { | 191 | if (tile_init_irqs(i, controller)) { |
183 | pr_err("PCI: Could not initialize " | 192 | pr_err("PCI: Could not initialize " |
184 | "IRQs, aborting.\n"); | 193 | "IRQs, aborting.\n"); |
185 | goto err_cont; | 194 | goto err_cont; |
186 | } | 195 | } |
187 | 196 | ||
188 | controller->index = num_controllers; | 197 | controller->index = i; |
189 | controller->hv_cfg_fd[0] = hv_cfg_fd0; | 198 | controller->hv_cfg_fd[0] = hv_cfg_fd0; |
190 | controller->hv_cfg_fd[1] = hv_cfg_fd1; | 199 | controller->hv_cfg_fd[1] = hv_cfg_fd1; |
191 | controller->hv_mem_fd = hv_mem_fd; | 200 | controller->hv_mem_fd = hv_mem_fd; |
192 | controller->first_busno = 0; | 201 | controller->first_busno = 0; |
193 | controller->last_busno = 0xff; | 202 | controller->last_busno = 0xff; |
194 | controller->ops = &tile_cfg_ops; | 203 | controller->ops = &tile_cfg_ops; |
195 | 204 | ||
196 | num_controllers++; | 205 | num_controllers++; |
197 | continue; | 206 | continue; |
198 | 207 | ||
199 | err_cont: | 208 | err_cont: |
200 | if (hv_cfg_fd0 >= 0) | 209 | if (hv_cfg_fd0 >= 0) |
201 | hv_dev_close(hv_cfg_fd0); | 210 | hv_dev_close(hv_cfg_fd0); |
202 | if (hv_cfg_fd1 >= 0) | 211 | if (hv_cfg_fd1 >= 0) |
203 | hv_dev_close(hv_cfg_fd1); | 212 | hv_dev_close(hv_cfg_fd1); |
204 | if (hv_mem_fd >= 0) | 213 | if (hv_mem_fd >= 0) |
205 | hv_dev_close(hv_mem_fd); | 214 | hv_dev_close(hv_mem_fd); |
206 | continue; | 215 | continue; |
216 | } | ||
207 | } | 217 | } |
208 | 218 | ||
209 | /* | 219 | /* |
@@ -232,7 +242,7 @@ static int tile_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | |||
232 | } | 242 | } |
233 | 243 | ||
234 | 244 | ||
235 | static void __init fixup_read_and_payload_sizes(void) | 245 | static void __devinit fixup_read_and_payload_sizes(void) |
236 | { | 246 | { |
237 | struct pci_dev *dev = NULL; | 247 | struct pci_dev *dev = NULL; |
238 | int smallest_max_payload = 0x1; /* Tile maxes out at 256 bytes. */ | 248 | int smallest_max_payload = 0x1; /* Tile maxes out at 256 bytes. */ |
@@ -282,7 +292,7 @@ static void __init fixup_read_and_payload_sizes(void) | |||
282 | * The controllers have been set up by the time we get here, by a call to | 292 | * The controllers have been set up by the time we get here, by a call to |
283 | * tile_pci_init. | 293 | * tile_pci_init. |
284 | */ | 294 | */ |
285 | static int __init pcibios_init(void) | 295 | int __devinit pcibios_init(void) |
286 | { | 296 | { |
287 | int i; | 297 | int i; |
288 | 298 | ||
@@ -296,25 +306,31 @@ static int __init pcibios_init(void) | |||
296 | mdelay(250); | 306 | mdelay(250); |
297 | 307 | ||
298 | /* Scan all of the recorded PCI controllers. */ | 308 | /* Scan all of the recorded PCI controllers. */ |
299 | for (i = 0; i < num_controllers; i++) { | 309 | for (i = 0; i < TILE_NUM_PCIE; i++) { |
300 | struct pci_controller *controller = &controllers[i]; | ||
301 | struct pci_bus *bus; | ||
302 | |||
303 | pr_info("PCI: initializing controller #%d\n", i); | ||
304 | |||
305 | /* | 310 | /* |
306 | * This comes from the generic Linux PCI driver. | 311 | * Do real pcibios init ops if the controller is initialized |
307 | * | 312 | * by tile_pci_init() successfully and not initialized by |
308 | * It reads the PCI tree for this bus into the Linux | 313 | * pcibios_init() yet to support PCIe hot-plug. |
309 | * data structures. | ||
310 | * | ||
311 | * This is inlined in linux/pci.h and calls into | ||
312 | * pci_scan_bus_parented() in probe.c. | ||
313 | */ | 314 | */ |
314 | bus = pci_scan_bus(0, controller->ops, controller); | 315 | if (pci_scan_flags[i] == 0 && controllers[i].ops != NULL) { |
315 | controller->root_bus = bus; | 316 | struct pci_controller *controller = &controllers[i]; |
316 | controller->last_busno = bus->subordinate; | 317 | struct pci_bus *bus; |
317 | 318 | ||
319 | pr_info("PCI: initializing controller #%d\n", i); | ||
320 | |||
321 | /* | ||
322 | * This comes from the generic Linux PCI driver. | ||
323 | * | ||
324 | * It reads the PCI tree for this bus into the Linux | ||
325 | * data structures. | ||
326 | * | ||
327 | * This is inlined in linux/pci.h and calls into | ||
328 | * pci_scan_bus_parented() in probe.c. | ||
329 | */ | ||
330 | bus = pci_scan_bus(0, controller->ops, controller); | ||
331 | controller->root_bus = bus; | ||
332 | controller->last_busno = bus->subordinate; | ||
333 | } | ||
318 | } | 334 | } |
319 | 335 | ||
320 | /* Do machine dependent PCI interrupt routing */ | 336 | /* Do machine dependent PCI interrupt routing */ |
@@ -326,34 +342,45 @@ static int __init pcibios_init(void) | |||
326 | * It allocates all of the resources (I/O memory, etc) | 342 | * It allocates all of the resources (I/O memory, etc) |
327 | * associated with the devices read in above. | 343 | * associated with the devices read in above. |
328 | */ | 344 | */ |
329 | |||
330 | pci_assign_unassigned_resources(); | 345 | pci_assign_unassigned_resources(); |
331 | 346 | ||
332 | /* Configure the max_read_size and max_payload_size values. */ | 347 | /* Configure the max_read_size and max_payload_size values. */ |
333 | fixup_read_and_payload_sizes(); | 348 | fixup_read_and_payload_sizes(); |
334 | 349 | ||
335 | /* Record the I/O resources in the PCI controller structure. */ | 350 | /* Record the I/O resources in the PCI controller structure. */ |
336 | for (i = 0; i < num_controllers; i++) { | 351 | for (i = 0; i < TILE_NUM_PCIE; i++) { |
337 | struct pci_bus *root_bus = controllers[i].root_bus; | 352 | /* |
338 | struct pci_bus *next_bus; | 353 | * Do real pcibios init ops if the controller is initialized |
339 | struct pci_dev *dev; | 354 | * by tile_pci_init() successfully and not initialized by |
340 | 355 | * pcibios_init() yet to support PCIe hot-plug. | |
341 | list_for_each_entry(dev, &root_bus->devices, bus_list) { | 356 | */ |
342 | /* Find the PCI host controller, ie. the 1st bridge. */ | 357 | if (pci_scan_flags[i] == 0 && controllers[i].ops != NULL) { |
343 | if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI && | 358 | struct pci_bus *root_bus = controllers[i].root_bus; |
344 | (PCI_SLOT(dev->devfn) == 0)) { | 359 | struct pci_bus *next_bus; |
345 | next_bus = dev->subordinate; | 360 | struct pci_dev *dev; |
346 | controllers[i].mem_resources[0] = | 361 | |
347 | *next_bus->resource[0]; | 362 | list_for_each_entry(dev, &root_bus->devices, bus_list) { |
348 | controllers[i].mem_resources[1] = | 363 | /* |
349 | *next_bus->resource[1]; | 364 | * Find the PCI host controller, ie. the 1st |
350 | controllers[i].mem_resources[2] = | 365 | * bridge. |
351 | *next_bus->resource[2]; | 366 | */ |
352 | 367 | if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI && | |
353 | break; | 368 | (PCI_SLOT(dev->devfn) == 0)) { |
369 | next_bus = dev->subordinate; | ||
370 | controllers[i].mem_resources[0] = | ||
371 | *next_bus->resource[0]; | ||
372 | controllers[i].mem_resources[1] = | ||
373 | *next_bus->resource[1]; | ||
374 | controllers[i].mem_resources[2] = | ||
375 | *next_bus->resource[2]; | ||
376 | |||
377 | /* Setup flags. */ | ||
378 | pci_scan_flags[i] = 1; | ||
379 | |||
380 | break; | ||
381 | } | ||
354 | } | 382 | } |
355 | } | 383 | } |
356 | |||
357 | } | 384 | } |
358 | 385 | ||
359 | return 0; | 386 | return 0; |
@@ -381,7 +408,7 @@ char __devinit *pcibios_setup(char *str) | |||
381 | /* | 408 | /* |
382 | * This is called from the generic Linux layer. | 409 | * This is called from the generic Linux layer. |
383 | */ | 410 | */ |
384 | void __init pcibios_update_irq(struct pci_dev *dev, int irq) | 411 | void __devinit pcibios_update_irq(struct pci_dev *dev, int irq) |
385 | { | 412 | { |
386 | pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); | 413 | pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); |
387 | } | 414 | } |