123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449 |
- /*
- * ACPI PCI HotPlug PCI configuration space management
- *
- * Copyright (C) 1995,2001 Compaq Computer Corporation
- * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
- * Copyright (C) 2001,2002 IBM Corp.
- * Copyright (C) 2002 Takayoshi Kochi (t-kochi@bq.jp.nec.com)
- * Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com)
- * Copyright (C) 2002 NEC Corporation
- *
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at
- * your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
- * NON INFRINGEMENT. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Send feedback to <t-kochi@bq.jp.nec.com>
- *
- */
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/pci.h>
- #include <linux/acpi.h>
- #include "../pci.h"
- #include "pci_hotplug.h"
- #include "acpiphp.h"
- #define MY_NAME "acpiphp_pci"
- /* allocate mem/pmem/io resource to a new function */
- static int init_config_space (struct acpiphp_func *func)
- {
- u32 bar, len;
- u32 address[] = {
- PCI_BASE_ADDRESS_0,
- PCI_BASE_ADDRESS_1,
- PCI_BASE_ADDRESS_2,
- PCI_BASE_ADDRESS_3,
- PCI_BASE_ADDRESS_4,
- PCI_BASE_ADDRESS_5,
- 0
- };
- int count;
- struct acpiphp_bridge *bridge;
- struct pci_resource *res;
- struct pci_bus *pbus;
- int bus, device, function;
- unsigned int devfn;
- u16 tmp;
- bridge = func->slot->bridge;
- pbus = bridge->pci_bus;
- bus = bridge->bus;
- device = func->slot->device;
- function = func->function;
- devfn = PCI_DEVFN(device, function);
- for (count = 0; address[count]; count++) { /* for 6 BARs */
- pci_bus_write_config_dword(pbus, devfn,
- address[count], 0xFFFFFFFF);
- pci_bus_read_config_dword(pbus, devfn, address[count], &bar);
- if (!bar) /* This BAR is not implemented */
- continue;
- dbg("Device %02x.%02x BAR %d wants %x\n", device, function, count, bar);
- if (bar & PCI_BASE_ADDRESS_SPACE_IO) {
- /* This is IO */
- len = bar & (PCI_BASE_ADDRESS_IO_MASK & 0xFFFF);
- len = len & ~(len - 1);
- dbg("len in IO %x, BAR %d\n", len, count);
- spin_lock(&bridge->res_lock);
- res = acpiphp_get_io_resource(&bridge->io_head, len);
- spin_unlock(&bridge->res_lock);
- if (!res) {
- err("cannot allocate requested io for %02x:%02x.%d len %x\n",
- bus, device, function, len);
- return -1;
- }
- pci_bus_write_config_dword(pbus, devfn,
- address[count],
- (u32)res->base);
- res->next = func->io_head;
- func->io_head = res;
- } else {
- /* This is Memory */
- if (bar & PCI_BASE_ADDRESS_MEM_PREFETCH) {
- /* pfmem */
- len = bar & 0xFFFFFFF0;
- len = ~len + 1;
- dbg("len in PFMEM %x, BAR %d\n", len, count);
- spin_lock(&bridge->res_lock);
- res = acpiphp_get_resource(&bridge->p_mem_head, len);
- spin_unlock(&bridge->res_lock);
- if (!res) {
- err("cannot allocate requested pfmem for %02x:%02x.%d len %x\n",
- bus, device, function, len);
- return -1;
- }
- pci_bus_write_config_dword(pbus, devfn,
- address[count],
- (u32)res->base);
- if (bar & PCI_BASE_ADDRESS_MEM_TYPE_64) { /* takes up another dword */
- dbg("inside the pfmem 64 case, count %d\n", count);
- count += 1;
- pci_bus_write_config_dword(pbus, devfn,
- address[count],
- (u32)(res->base >> 32));
- }
- res->next = func->p_mem_head;
- func->p_mem_head = res;
- } else {
- /* regular memory */
- len = bar & 0xFFFFFFF0;
- len = ~len + 1;
- dbg("len in MEM %x, BAR %d\n", len, count);
- spin_lock(&bridge->res_lock);
- res = acpiphp_get_resource(&bridge->mem_head, len);
- spin_unlock(&bridge->res_lock);
- if (!res) {
- err("cannot allocate requested pfmem for %02x:%02x.%d len %x\n",
- bus, device, function, len);
- return -1;
- }
- pci_bus_write_config_dword(pbus, devfn,
- address[count],
- (u32)res->base);
- if (bar & PCI_BASE_ADDRESS_MEM_TYPE_64) {
- /* takes up another dword */
- dbg("inside mem 64 case, reg. mem, count %d\n", count);
- count += 1;
- pci_bus_write_config_dword(pbus, devfn,
- address[count],
- (u32)(res->base >> 32));
- }
- res->next = func->mem_head;
- func->mem_head = res;
- }
- }
- }
- /* disable expansion rom */
- pci_bus_write_config_dword(pbus, devfn, PCI_ROM_ADDRESS, 0x00000000);
- /* set PCI parameters from _HPP */
- pci_bus_write_config_byte(pbus, devfn, PCI_CACHE_LINE_SIZE,
- bridge->hpp.cache_line_size);
- pci_bus_write_config_byte(pbus, devfn, PCI_LATENCY_TIMER,
- bridge->hpp.latency_timer);
- pci_bus_read_config_word(pbus, devfn, PCI_COMMAND, &tmp);
- if (bridge->hpp.enable_SERR)
- tmp |= PCI_COMMAND_SERR;
- if (bridge->hpp.enable_PERR)
- tmp |= PCI_COMMAND_PARITY;
- pci_bus_write_config_word(pbus, devfn, PCI_COMMAND, tmp);
- return 0;
- }
- /* detect_used_resource - subtract resource under dev from bridge */
- static int detect_used_resource (struct acpiphp_bridge *bridge, struct pci_dev *dev)
- {
- int count;
- dbg("Device %s\n", pci_name(dev));
- for (count = 0; count < DEVICE_COUNT_RESOURCE; count++) {
- struct pci_resource *res;
- struct pci_resource **head;
- unsigned long base = dev->resource[count].start;
- unsigned long len = dev->resource[count].end - base + 1;
- unsigned long flags = dev->resource[count].flags;
- if (!flags)
- continue;
- dbg("BAR[%d] 0x%lx - 0x%lx (0x%lx)\n", count, base,
- base + len - 1, flags);
- if (flags & IORESOURCE_IO) {
- head = &bridge->io_head;
- } else if (flags & IORESOURCE_PREFETCH) {
- head = &bridge->p_mem_head;
- } else {
- head = &bridge->mem_head;
- }
- spin_lock(&bridge->res_lock);
- res = acpiphp_get_resource_with_base(head, base, len);
- spin_unlock(&bridge->res_lock);
- if (res)
- kfree(res);
- }
- return 0;
- }
- /**
- * acpiphp_detect_pci_resource - detect resources under bridge
- * @bridge: detect all resources already used under this bridge
- *
- * collect all resources already allocated for all devices under a bridge.
- */
- int acpiphp_detect_pci_resource (struct acpiphp_bridge *bridge)
- {
- struct list_head *l;
- struct pci_dev *dev;
- list_for_each (l, &bridge->pci_bus->devices) {
- dev = pci_dev_b(l);
- detect_used_resource(bridge, dev);
- }
- return 0;
- }
- /**
- * acpiphp_init_slot_resource - gather resource usage information of a slot
- * @slot: ACPI slot object to be checked, should have valid pci_dev member
- *
- * TBD: PCI-to-PCI bridge case
- * use pci_dev->resource[]
- */
- int acpiphp_init_func_resource (struct acpiphp_func *func)
- {
- u64 base;
- u32 bar, len;
- u32 address[] = {
- PCI_BASE_ADDRESS_0,
- PCI_BASE_ADDRESS_1,
- PCI_BASE_ADDRESS_2,
- PCI_BASE_ADDRESS_3,
- PCI_BASE_ADDRESS_4,
- PCI_BASE_ADDRESS_5,
- 0
- };
- int count;
- struct pci_resource *res;
- struct pci_dev *dev;
- dev = func->pci_dev;
- dbg("Hot-pluggable device %s\n", pci_name(dev));
- for (count = 0; address[count]; count++) { /* for 6 BARs */
- pci_read_config_dword(dev, address[count], &bar);
- if (!bar) /* This BAR is not implemented */
- continue;
- pci_write_config_dword(dev, address[count], 0xFFFFFFFF);
- pci_read_config_dword(dev, address[count], &len);
- if (len & PCI_BASE_ADDRESS_SPACE_IO) {
- /* This is IO */
- base = bar & 0xFFFFFFFC;
- len = len & (PCI_BASE_ADDRESS_IO_MASK & 0xFFFF);
- len = len & ~(len - 1);
- dbg("BAR[%d] %08x - %08x (IO)\n", count, (u32)base, (u32)base + len - 1);
- res = acpiphp_make_resource(base, len);
- if (!res)
- goto no_memory;
- res->next = func->io_head;
- func->io_head = res;
- } else {
- /* This is Memory */
- base = bar & 0xFFFFFFF0;
- if (len & PCI_BASE_ADDRESS_MEM_PREFETCH) {
- /* pfmem */
- len &= 0xFFFFFFF0;
- len = ~len + 1;
- if (len & PCI_BASE_ADDRESS_MEM_TYPE_64) { /* takes up another dword */
- dbg("prefetch mem 64\n");
- count += 1;
- }
- dbg("BAR[%d] %08x - %08x (PMEM)\n", count, (u32)base, (u32)base + len - 1);
- res = acpiphp_make_resource(base, len);
- if (!res)
- goto no_memory;
- res->next = func->p_mem_head;
- func->p_mem_head = res;
- } else {
- /* regular memory */
- len &= 0xFFFFFFF0;
- len = ~len + 1;
- if (len & PCI_BASE_ADDRESS_MEM_TYPE_64) {
- /* takes up another dword */
- dbg("mem 64\n");
- count += 1;
- }
- dbg("BAR[%d] %08x - %08x (MEM)\n", count, (u32)base, (u32)base + len - 1);
- res = acpiphp_make_resource(base, len);
- if (!res)
- goto no_memory;
- res->next = func->mem_head;
- func->mem_head = res;
- }
- }
- pci_write_config_dword(dev, address[count], bar);
- }
- #if 1
- acpiphp_dump_func_resource(func);
- #endif
- return 0;
- no_memory:
- err("out of memory\n");
- acpiphp_free_resource(&func->io_head);
- acpiphp_free_resource(&func->mem_head);
- acpiphp_free_resource(&func->p_mem_head);
- return -1;
- }
- /**
- * acpiphp_configure_slot - allocate PCI resources
- * @slot: slot to be configured
- *
- * initializes a PCI functions on a device inserted
- * into the slot
- *
- */
- int acpiphp_configure_slot (struct acpiphp_slot *slot)
- {
- struct acpiphp_func *func;
- struct list_head *l;
- u8 hdr;
- u32 dvid;
- int retval = 0;
- int is_multi = 0;
- pci_bus_read_config_byte(slot->bridge->pci_bus,
- PCI_DEVFN(slot->device, 0),
- PCI_HEADER_TYPE, &hdr);
- if (hdr & 0x80)
- is_multi = 1;
- list_for_each (l, &slot->funcs) {
- func = list_entry(l, struct acpiphp_func, sibling);
- if (is_multi || func->function == 0) {
- pci_bus_read_config_dword(slot->bridge->pci_bus,
- PCI_DEVFN(slot->device,
- func->function),
- PCI_VENDOR_ID, &dvid);
- if (dvid != 0xffffffff) {
- retval = init_config_space(func);
- if (retval)
- break;
- }
- }
- }
- return retval;
- }
- /**
- * acpiphp_configure_function - configure PCI function
- * @func: function to be configured
- *
- * initializes a PCI functions on a device inserted
- * into the slot
- *
- */
- int acpiphp_configure_function (struct acpiphp_func *func)
- {
- /* all handled by the pci core now */
- return 0;
- }
- /**
- * acpiphp_unconfigure_function - unconfigure PCI function
- * @func: function to be unconfigured
- *
- */
- void acpiphp_unconfigure_function (struct acpiphp_func *func)
- {
- struct acpiphp_bridge *bridge;
- /* if pci_dev is NULL, ignore it */
- if (!func->pci_dev)
- return;
- pci_remove_bus_device(func->pci_dev);
- /* free all resources */
- bridge = func->slot->bridge;
- spin_lock(&bridge->res_lock);
- acpiphp_move_resource(&func->io_head, &bridge->io_head);
- acpiphp_move_resource(&func->mem_head, &bridge->mem_head);
- acpiphp_move_resource(&func->p_mem_head, &bridge->p_mem_head);
- acpiphp_move_resource(&func->bus_head, &bridge->bus_head);
- spin_unlock(&bridge->res_lock);
- }
|