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 49u8 cpci_get_attention_status(struct slot *slot) 50{ 51 int hs_cap; 52 u16 hs_csr; 53 54 hs_cap = pci_bus_find_capability(slot->bus, 55 slot->devfn, 56 PCI_CAP_ID_CHSWP); 57 if (!hs_cap) 58 return 0; 59 60 if (pci_bus_read_config_word(slot->bus, 61 slot->devfn, 62 hs_cap + 2, 63 &hs_csr)) 64 return 0; 65 66 return hs_csr & 0x0008 ? 1 : 0; 67} 68 69int cpci_set_attention_status(struct slot *slot, int status) 70{ 71 int hs_cap; 72 u16 hs_csr; 73 74 hs_cap = pci_bus_find_capability(slot->bus, 75 slot->devfn, 76 PCI_CAP_ID_CHSWP); 77 if (!hs_cap) 78 return 0; 79 if (pci_bus_read_config_word(slot->bus, 80 slot->devfn, 81 hs_cap + 2, 82 &hs_csr)) 83 return 0; 84 if (status) 85 hs_csr |= HS_CSR_LOO; 86 else 87 hs_csr &= ~HS_CSR_LOO; 88 if (pci_bus_write_config_word(slot->bus, 89 slot->devfn, 90 hs_cap + 2, 91 hs_csr)) 92 return 0; 93 return 1; 94} 95 96u16 cpci_get_hs_csr(struct slot *slot) 97{ 98 int hs_cap; 99 u16 hs_csr; 100 101 hs_cap = pci_bus_find_capability(slot->bus, 102 slot->devfn, 103 PCI_CAP_ID_CHSWP); 104 if (!hs_cap) 105 return 0xFFFF; 106 if (pci_bus_read_config_word(slot->bus, 107 slot->devfn, 108 hs_cap + 2, 109 &hs_csr)) 110 return 0xFFFF; 111 return hs_csr; 112} 113 114int cpci_check_and_clear_ins(struct slot *slot) 115{ 116 int hs_cap; 117 u16 hs_csr; 118 int ins = 0; 119 120 hs_cap = pci_bus_find_capability(slot->bus, 121 slot->devfn, 122 PCI_CAP_ID_CHSWP); 123 if (!hs_cap) 124 return 0; 125 if (pci_bus_read_config_word(slot->bus, 126 slot->devfn, 127 hs_cap + 2, 128 &hs_csr)) 129 return 0; 130 if (hs_csr & HS_CSR_INS) { 131 /* Clear INS (by setting it) */ 132 if (pci_bus_write_config_word(slot->bus, 133 slot->devfn, 134 hs_cap + 2, 135 hs_csr)) 136 ins = 0; 137 else 138 ins = 1; 139 } 140 return ins; 141} 142 143int cpci_check_ext(struct slot *slot) 144{ 145 int hs_cap; 146 u16 hs_csr; 147 int ext = 0; 148 149 hs_cap = pci_bus_find_capability(slot->bus, 150 slot->devfn, 151 PCI_CAP_ID_CHSWP); 152 if (!hs_cap) 153 return 0; 154 if (pci_bus_read_config_word(slot->bus, 155 slot->devfn, 156 hs_cap + 2, 157 &hs_csr)) 158 return 0; 159 if (hs_csr & HS_CSR_EXT) 160 ext = 1; 161 return ext; 162} 163 164int cpci_clear_ext(struct slot *slot) 165{ 166 int hs_cap; 167 u16 hs_csr; 168 169 hs_cap = pci_bus_find_capability(slot->bus, 170 slot->devfn, 171 PCI_CAP_ID_CHSWP); 172 if (!hs_cap) 173 return -ENODEV; 174 if (pci_bus_read_config_word(slot->bus, 175 slot->devfn, 176 hs_cap + 2, 177 &hs_csr)) 178 return -ENODEV; 179 if (hs_csr & HS_CSR_EXT) { 180 /* Clear EXT (by setting it) */ 181 if (pci_bus_write_config_word(slot->bus, 182 slot->devfn, 183 hs_cap + 2, 184 hs_csr)) 185 return -ENODEV; 186 } 187 return 0; 188} 189 190int cpci_led_on(struct slot *slot) 191{ 192 int hs_cap; 193 u16 hs_csr; 194 195 hs_cap = pci_bus_find_capability(slot->bus, 196 slot->devfn, 197 PCI_CAP_ID_CHSWP); 198 if (!hs_cap) 199 return -ENODEV; 200 if (pci_bus_read_config_word(slot->bus, 201 slot->devfn, 202 hs_cap + 2, 203 &hs_csr)) 204 return -ENODEV; 205 if ((hs_csr & HS_CSR_LOO) != HS_CSR_LOO) { 206 hs_csr |= HS_CSR_LOO; 207 if (pci_bus_write_config_word(slot->bus, 208 slot->devfn, 209 hs_cap + 2, 210 hs_csr)) { 211 err("Could not set LOO for slot %s", 212 hotplug_slot_name(slot->hotplug_slot)); 213 return -ENODEV; 214 } 215 } 216 return 0; 217} 218 219int cpci_led_off(struct slot *slot) 220{ 221 int hs_cap; 222 u16 hs_csr; 223 224 hs_cap = pci_bus_find_capability(slot->bus, 225 slot->devfn, 226 PCI_CAP_ID_CHSWP); 227 if (!hs_cap) 228 return -ENODEV; 229 if (pci_bus_read_config_word(slot->bus, 230 slot->devfn, 231 hs_cap + 2, 232 &hs_csr)) 233 return -ENODEV; 234 if (hs_csr & HS_CSR_LOO) { 235 hs_csr &= ~HS_CSR_LOO; 236 if (pci_bus_write_config_word(slot->bus, 237 slot->devfn, 238 hs_cap + 2, 239 hs_csr)) { 240 err("Could not clear LOO for slot %s", 241 hotplug_slot_name(slot->hotplug_slot)); 242 return -ENODEV; 243 } 244 } 245 return 0; 246} 247 248 249/* 250 * Device configuration functions 251 */ 252 253int cpci_configure_slot(struct slot *slot) 254{ 255 struct pci_dev *dev; 256 struct pci_bus *parent; 257 int ret = 0; 258 259 dbg("%s - enter", __func__); 260 261 pci_lock_rescan_remove(); 262 263 if (slot->dev == NULL) { 264 dbg("pci_dev null, finding %02x:%02x:%x", 265 slot->bus->number, PCI_SLOT(slot->devfn), PCI_FUNC(slot->devfn)); 266 slot->dev = pci_get_slot(slot->bus, slot->devfn); 267 } 268 269 /* Still NULL? Well then scan for it! */ 270 if (slot->dev == NULL) { 271 int n; 272 dbg("pci_dev still null"); 273 274 /* 275 * This will generate pci_dev structures for all functions, but 276 * we will only call this case when lookup fails. 277 */ 278 n = pci_scan_slot(slot->bus, slot->devfn); 279 dbg("%s: pci_scan_slot returned %d", __func__, n); 280 slot->dev = pci_get_slot(slot->bus, slot->devfn); 281 if (slot->dev == NULL) { 282 err("Could not find PCI device for slot %02x", slot->number); 283 ret = -ENODEV; 284 goto out; 285 } 286 } 287 parent = slot->dev->bus; 288 289 list_for_each_entry(dev, &parent->devices, bus_list) { 290 if (PCI_SLOT(dev->devfn) != PCI_SLOT(slot->devfn)) 291 continue; 292 if (pci_is_bridge(dev)) 293 pci_hp_add_bridge(dev); 294 } 295 296 297 pci_assign_unassigned_bridge_resources(parent->self); 298 299 pci_bus_add_devices(parent); 300 301 out: 302 pci_unlock_rescan_remove(); 303 dbg("%s - exit", __func__); 304 return ret; 305} 306 307int cpci_unconfigure_slot(struct slot *slot) 308{ 309 struct pci_dev *dev, *temp; 310 311 dbg("%s - enter", __func__); 312 if (!slot->dev) { 313 err("No device for slot %02x\n", slot->number); 314 return -ENODEV; 315 } 316 317 pci_lock_rescan_remove(); 318 319 list_for_each_entry_safe(dev, temp, &slot->bus->devices, bus_list) { 320 if (PCI_SLOT(dev->devfn) != PCI_SLOT(slot->devfn)) 321 continue; 322 pci_dev_get(dev); 323 pci_stop_and_remove_bus_device(dev); 324 pci_dev_put(dev); 325 } 326 pci_dev_put(slot->dev); 327 slot->dev = NULL; 328 329 pci_unlock_rescan_remove(); 330 331 dbg("%s - exit", __func__); 332 return 0; 333} 334