1/* 2 * CompactPCI Hot Plug Driver PCI functions 3 * 4 * Copyright (C) 2002,2005 by SOMA Networks, Inc. 5 * 6 * All rights reserved. 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or (at 11 * your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, but 14 * WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 16 * NON INFRINGEMENT. See the GNU General Public License for more 17 * details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 22 * 23 * Send feedback to <scottm@somanetworks.com> 24 */ 25 26#include <linux/module.h> 27#include <linux/kernel.h> 28#include <linux/pci.h> 29#include <linux/pci_hotplug.h> 30#include <linux/proc_fs.h> 31#include "../pci.h" 32#include "cpci_hotplug.h" 33 34#define MY_NAME "cpci_hotplug" 35 36extern int cpci_debug; 37 38#define dbg(format, arg...) \ 39 do { \ 40 if (cpci_debug) \ 41 printk (KERN_DEBUG "%s: " format "\n", \ 42 MY_NAME , ## arg); \ 43 } while (0) 44#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg) 45#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg) 46#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg) 47 48#define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1)) 49 50 51u8 cpci_get_attention_status(struct slot* slot) 52{ 53 int hs_cap; 54 u16 hs_csr; 55 56 hs_cap = pci_bus_find_capability(slot->bus, 57 slot->devfn, 58 PCI_CAP_ID_CHSWP); 59 if (!hs_cap) 60 return 0; 61 62 if (pci_bus_read_config_word(slot->bus, 63 slot->devfn, 64 hs_cap + 2, 65 &hs_csr)) 66 return 0; 67 68 return hs_csr & 0x0008 ? 1 : 0; 69} 70 71int cpci_set_attention_status(struct slot* slot, int status) 72{ 73 int hs_cap; 74 u16 hs_csr; 75 76 hs_cap = pci_bus_find_capability(slot->bus, 77 slot->devfn, 78 PCI_CAP_ID_CHSWP); 79 if (!hs_cap) 80 return 0; 81 if (pci_bus_read_config_word(slot->bus, 82 slot->devfn, 83 hs_cap + 2, 84 &hs_csr)) 85 return 0; 86 if (status) 87 hs_csr |= HS_CSR_LOO; 88 else 89 hs_csr &= ~HS_CSR_LOO; 90 if (pci_bus_write_config_word(slot->bus, 91 slot->devfn, 92 hs_cap + 2, 93 hs_csr)) 94 return 0; 95 return 1; 96} 97 98u16 cpci_get_hs_csr(struct slot* slot) 99{ 100 int hs_cap; 101 u16 hs_csr; 102 103 hs_cap = pci_bus_find_capability(slot->bus, 104 slot->devfn, 105 PCI_CAP_ID_CHSWP); 106 if (!hs_cap) 107 return 0xFFFF; 108 if (pci_bus_read_config_word(slot->bus, 109 slot->devfn, 110 hs_cap + 2, 111 &hs_csr)) 112 return 0xFFFF; 113 return hs_csr; 114} 115 116int cpci_check_and_clear_ins(struct slot* slot) 117{ 118 int hs_cap; 119 u16 hs_csr; 120 int ins = 0; 121 122 hs_cap = pci_bus_find_capability(slot->bus, 123 slot->devfn, 124 PCI_CAP_ID_CHSWP); 125 if (!hs_cap) 126 return 0; 127 if (pci_bus_read_config_word(slot->bus, 128 slot->devfn, 129 hs_cap + 2, 130 &hs_csr)) 131 return 0; 132 if (hs_csr & HS_CSR_INS) { 133 /* Clear INS (by setting it) */ 134 if (pci_bus_write_config_word(slot->bus, 135 slot->devfn, 136 hs_cap + 2, 137 hs_csr)) 138 ins = 0; 139 else 140 ins = 1; 141 } 142 return ins; 143} 144 145int cpci_check_ext(struct slot* slot) 146{ 147 int hs_cap; 148 u16 hs_csr; 149 int ext = 0; 150 151 hs_cap = pci_bus_find_capability(slot->bus, 152 slot->devfn, 153 PCI_CAP_ID_CHSWP); 154 if (!hs_cap) 155 return 0; 156 if (pci_bus_read_config_word(slot->bus, 157 slot->devfn, 158 hs_cap + 2, 159 &hs_csr)) 160 return 0; 161 if (hs_csr & HS_CSR_EXT) 162 ext = 1; 163 return ext; 164} 165 166int cpci_clear_ext(struct slot* slot) 167{ 168 int hs_cap; 169 u16 hs_csr; 170 171 hs_cap = pci_bus_find_capability(slot->bus, 172 slot->devfn, 173 PCI_CAP_ID_CHSWP); 174 if (!hs_cap) 175 return -ENODEV; 176 if (pci_bus_read_config_word(slot->bus, 177 slot->devfn, 178 hs_cap + 2, 179 &hs_csr)) 180 return -ENODEV; 181 if (hs_csr & HS_CSR_EXT) { 182 /* Clear EXT (by setting it) */ 183 if (pci_bus_write_config_word(slot->bus, 184 slot->devfn, 185 hs_cap + 2, 186 hs_csr)) 187 return -ENODEV; 188 } 189 return 0; 190} 191 192int cpci_led_on(struct slot* slot) 193{ 194 int hs_cap; 195 u16 hs_csr; 196 197 hs_cap = pci_bus_find_capability(slot->bus, 198 slot->devfn, 199 PCI_CAP_ID_CHSWP); 200 if (!hs_cap) 201 return -ENODEV; 202 if (pci_bus_read_config_word(slot->bus, 203 slot->devfn, 204 hs_cap + 2, 205 &hs_csr)) 206 return -ENODEV; 207 if ((hs_csr & HS_CSR_LOO) != HS_CSR_LOO) { 208 hs_csr |= HS_CSR_LOO; 209 if (pci_bus_write_config_word(slot->bus, 210 slot->devfn, 211 hs_cap + 2, 212 hs_csr)) { 213 err("Could not set LOO for slot %s", 214 slot->hotplug_slot->name); 215 return -ENODEV; 216 } 217 } 218 return 0; 219} 220 221int cpci_led_off(struct slot* slot) 222{ 223 int hs_cap; 224 u16 hs_csr; 225 226 hs_cap = pci_bus_find_capability(slot->bus, 227 slot->devfn, 228 PCI_CAP_ID_CHSWP); 229 if (!hs_cap) 230 return -ENODEV; 231 if (pci_bus_read_config_word(slot->bus, 232 slot->devfn, 233 hs_cap + 2, 234 &hs_csr)) 235 return -ENODEV; 236 if (hs_csr & HS_CSR_LOO) { 237 hs_csr &= ~HS_CSR_LOO; 238 if (pci_bus_write_config_word(slot->bus, 239 slot->devfn, 240 hs_cap + 2, 241 hs_csr)) { 242 err("Could not clear LOO for slot %s", 243 slot->hotplug_slot->name); 244 return -ENODEV; 245 } 246 } 247 return 0; 248} 249 250 251/* 252 * Device configuration functions 253 */ 254 255int cpci_configure_slot(struct slot* slot) 256{ 257 struct pci_bus *parent; 258 int fn; 259 260 dbg("%s - enter", __FUNCTION__); 261 262 if (slot->dev == NULL) { 263 dbg("pci_dev null, finding %02x:%02x:%x", 264 slot->bus->number, PCI_SLOT(slot->devfn), PCI_FUNC(slot->devfn)); 265 slot->dev = pci_get_slot(slot->bus, slot->devfn); 266 } 267 268 /* Still NULL? Well then scan for it! */ 269 if (slot->dev == NULL) { 270 int n; 271 dbg("pci_dev still null"); 272 273 /* 274 * This will generate pci_dev structures for all functions, but 275 * we will only call this case when lookup fails. 276 */ 277 n = pci_scan_slot(slot->bus, slot->devfn); 278 dbg("%s: pci_scan_slot returned %d", __FUNCTION__, n); 279 slot->dev = pci_get_slot(slot->bus, slot->devfn); 280 if (slot->dev == NULL) { 281 err("Could not find PCI device for slot %02x", slot->number); 282 return -ENODEV; 283 } 284 } 285 parent = slot->dev->bus; 286 287 for (fn = 0; fn < 8; fn++) { 288 struct pci_dev *dev; 289 290 dev = pci_get_slot(parent, PCI_DEVFN(PCI_SLOT(slot->devfn), fn)); 291 if (!dev) 292 continue; 293 if ((dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) || 294 (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) { 295 /* Find an unused bus number for the new bridge */ 296 struct pci_bus *child; 297 unsigned char busnr, start = parent->secondary; 298 unsigned char end = parent->subordinate; 299 300 for (busnr = start; busnr <= end; busnr++) { 301 if (!pci_find_bus(pci_domain_nr(parent), 302 busnr)) 303 break; 304 } 305 if (busnr >= end) { 306 err("No free bus for hot-added bridge\n"); 307 pci_dev_put(dev); 308 continue; 309 } 310 child = pci_add_new_bus(parent, dev, busnr); 311 if (!child) { 312 err("Cannot add new bus for %s\n", 313 pci_name(dev)); 314 pci_dev_put(dev); 315 continue; 316 } 317 child->subordinate = pci_do_scan_bus(child); 318 pci_bus_size_bridges(child); 319 } 320 pci_dev_put(dev); 321 } 322 323 pci_bus_assign_resources(parent); 324 pci_bus_add_devices(parent); 325 pci_enable_bridges(parent); 326 327 dbg("%s - exit", __FUNCTION__); 328 return 0; 329} 330 331int cpci_unconfigure_slot(struct slot* slot) 332{ 333 int i; 334 struct pci_dev *dev; 335 336 dbg("%s - enter", __FUNCTION__); 337 if (!slot->dev) { 338 err("No device for slot %02x\n", slot->number); 339 return -ENODEV; 340 } 341 342 for (i = 0; i < 8; i++) { 343 dev = pci_get_slot(slot->bus, 344 PCI_DEVFN(PCI_SLOT(slot->devfn), i)); 345 if (dev) { 346 pci_remove_bus_device(dev); 347 pci_dev_put(dev); 348 } 349 } 350 pci_dev_put(slot->dev); 351 slot->dev = NULL; 352 353 dbg("%s - exit", __FUNCTION__); 354 return 0; 355} 356