123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700 |
- /*
- * ACPI PCI HotPlug Utility functions
- *
- * Copyright (C) 1995,2001 Compaq Computer Corporation
- * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
- * Copyright (C) 2001 IBM Corp.
- * Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com)
- * Copyright (C) 2002 Takayoshi Kochi (t-kochi@bq.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 <gregkh@us.ibm.com>, <t-kochi@bq.jp.nec.com>
- *
- */
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/types.h>
- #include <linux/proc_fs.h>
- #include <linux/sysctl.h>
- #include <linux/pci.h>
- #include <linux/smp.h>
- #include <linux/smp_lock.h>
- #include <linux/string.h>
- #include <linux/mm.h>
- #include <linux/errno.h>
- #include <linux/ioport.h>
- #include <linux/slab.h>
- #include <linux/interrupt.h>
- #include <linux/timer.h>
- #include <linux/ioctl.h>
- #include <linux/fcntl.h>
- #include <linux/list.h>
- #include "pci_hotplug.h"
- #include "acpiphp.h"
- #define MY_NAME "acpiphp_res"
- /*
- * sort_by_size - sort nodes by their length, smallest first
- */
- static int sort_by_size(struct pci_resource **head)
- {
- struct pci_resource *current_res;
- struct pci_resource *next_res;
- int out_of_order = 1;
- if (!(*head))
- return 1;
- if (!((*head)->next))
- return 0;
- while (out_of_order) {
- out_of_order = 0;
- /* Special case for swapping list head */
- if (((*head)->next) &&
- ((*head)->length > (*head)->next->length)) {
- out_of_order++;
- current_res = *head;
- *head = (*head)->next;
- current_res->next = (*head)->next;
- (*head)->next = current_res;
- }
- current_res = *head;
- while (current_res->next && current_res->next->next) {
- if (current_res->next->length > current_res->next->next->length) {
- out_of_order++;
- next_res = current_res->next;
- current_res->next = current_res->next->next;
- current_res = current_res->next;
- next_res->next = current_res->next;
- current_res->next = next_res;
- } else
- current_res = current_res->next;
- }
- } /* End of out_of_order loop */
- return 0;
- }
- #if 0
- /*
- * sort_by_max_size - sort nodes by their length, largest first
- */
- static int sort_by_max_size(struct pci_resource **head)
- {
- struct pci_resource *current_res;
- struct pci_resource *next_res;
- int out_of_order = 1;
- if (!(*head))
- return 1;
- if (!((*head)->next))
- return 0;
- while (out_of_order) {
- out_of_order = 0;
- /* Special case for swapping list head */
- if (((*head)->next) &&
- ((*head)->length < (*head)->next->length)) {
- out_of_order++;
- current_res = *head;
- *head = (*head)->next;
- current_res->next = (*head)->next;
- (*head)->next = current_res;
- }
- current_res = *head;
- while (current_res->next && current_res->next->next) {
- if (current_res->next->length < current_res->next->next->length) {
- out_of_order++;
- next_res = current_res->next;
- current_res->next = current_res->next->next;
- current_res = current_res->next;
- next_res->next = current_res->next;
- current_res->next = next_res;
- } else
- current_res = current_res->next;
- }
- } /* End of out_of_order loop */
- return 0;
- }
- #endif
- /**
- * get_io_resource - get resource for I/O ports
- *
- * this function sorts the resource list by size and then
- * returns the first node of "size" length that is not in the
- * ISA aliasing window. If it finds a node larger than "size"
- * it will split it up.
- *
- * size must be a power of two.
- *
- * difference from get_resource is handling of ISA aliasing space.
- *
- */
- struct pci_resource *acpiphp_get_io_resource (struct pci_resource **head, u32 size)
- {
- struct pci_resource *prevnode;
- struct pci_resource *node;
- struct pci_resource *split_node;
- u64 temp_qword;
- if (!(*head))
- return NULL;
- if (acpiphp_resource_sort_and_combine(head))
- return NULL;
- if (sort_by_size(head))
- return NULL;
- for (node = *head; node; node = node->next) {
- if (node->length < size)
- continue;
- if (node->base & (size - 1)) {
- /* this one isn't base aligned properly
- so we'll make a new entry and split it up */
- temp_qword = (node->base | (size-1)) + 1;
- /* Short circuit if adjusted size is too small */
- if ((node->length - (temp_qword - node->base)) < size)
- continue;
- split_node = acpiphp_make_resource(node->base, temp_qword - node->base);
- if (!split_node)
- return NULL;
- node->base = temp_qword;
- node->length -= split_node->length;
- /* Put it in the list */
- split_node->next = node->next;
- node->next = split_node;
- } /* End of non-aligned base */
- /* Don't need to check if too small since we already did */
- if (node->length > size) {
- /* this one is longer than we need
- so we'll make a new entry and split it up */
- split_node = acpiphp_make_resource(node->base + size, node->length - size);
- if (!split_node)
- return NULL;
- node->length = size;
- /* Put it in the list */
- split_node->next = node->next;
- node->next = split_node;
- } /* End of too big on top end */
- /* For IO make sure it's not in the ISA aliasing space */
- if ((node->base & 0x300L) && !(node->base & 0xfffff000))
- continue;
- /* If we got here, then it is the right size
- Now take it out of the list */
- if (*head == node) {
- *head = node->next;
- } else {
- prevnode = *head;
- while (prevnode->next != node)
- prevnode = prevnode->next;
- prevnode->next = node->next;
- }
- node->next = NULL;
- /* Stop looping */
- break;
- }
- return node;
- }
- #if 0
- /**
- * get_max_resource - get the largest resource
- *
- * Gets the largest node that is at least "size" big from the
- * list pointed to by head. It aligns the node on top and bottom
- * to "size" alignment before returning it.
- */
- static struct pci_resource *acpiphp_get_max_resource (struct pci_resource **head, u32 size)
- {
- struct pci_resource *max;
- struct pci_resource *temp;
- struct pci_resource *split_node;
- u64 temp_qword;
- if (!(*head))
- return NULL;
- if (acpiphp_resource_sort_and_combine(head))
- return NULL;
- if (sort_by_max_size(head))
- return NULL;
- for (max = *head;max; max = max->next) {
- /* If not big enough we could probably just bail,
- instead we'll continue to the next. */
- if (max->length < size)
- continue;
- if (max->base & (size - 1)) {
- /* this one isn't base aligned properly
- so we'll make a new entry and split it up */
- temp_qword = (max->base | (size-1)) + 1;
- /* Short circuit if adjusted size is too small */
- if ((max->length - (temp_qword - max->base)) < size)
- continue;
- split_node = acpiphp_make_resource(max->base, temp_qword - max->base);
- if (!split_node)
- return NULL;
- max->base = temp_qword;
- max->length -= split_node->length;
- /* Put it next in the list */
- split_node->next = max->next;
- max->next = split_node;
- }
- if ((max->base + max->length) & (size - 1)) {
- /* this one isn't end aligned properly at the top
- so we'll make a new entry and split it up */
- temp_qword = ((max->base + max->length) & ~(size - 1));
- split_node = acpiphp_make_resource(temp_qword,
- max->length + max->base - temp_qword);
- if (!split_node)
- return NULL;
- max->length -= split_node->length;
- /* Put it in the list */
- split_node->next = max->next;
- max->next = split_node;
- }
- /* Make sure it didn't shrink too much when we aligned it */
- if (max->length < size)
- continue;
- /* Now take it out of the list */
- temp = (struct pci_resource*) *head;
- if (temp == max) {
- *head = max->next;
- } else {
- while (temp && temp->next != max) {
- temp = temp->next;
- }
- temp->next = max->next;
- }
- max->next = NULL;
- return max;
- }
- /* If we get here, we couldn't find one */
- return NULL;
- }
- #endif
- /**
- * get_resource - get resource (mem, pfmem)
- *
- * this function sorts the resource list by size and then
- * returns the first node of "size" length. If it finds a node
- * larger than "size" it will split it up.
- *
- * size must be a power of two.
- *
- */
- struct pci_resource *acpiphp_get_resource (struct pci_resource **head, u32 size)
- {
- struct pci_resource *prevnode;
- struct pci_resource *node;
- struct pci_resource *split_node;
- u64 temp_qword;
- if (!(*head))
- return NULL;
- if (acpiphp_resource_sort_and_combine(head))
- return NULL;
- if (sort_by_size(head))
- return NULL;
- for (node = *head; node; node = node->next) {
- dbg("%s: req_size =%x node=%p, base=%x, length=%x\n",
- __FUNCTION__, size, node, (u32)node->base, node->length);
- if (node->length < size)
- continue;
- if (node->base & (size - 1)) {
- dbg("%s: not aligned\n", __FUNCTION__);
- /* this one isn't base aligned properly
- so we'll make a new entry and split it up */
- temp_qword = (node->base | (size-1)) + 1;
- /* Short circuit if adjusted size is too small */
- if ((node->length - (temp_qword - node->base)) < size)
- continue;
- split_node = acpiphp_make_resource(node->base, temp_qword - node->base);
- if (!split_node)
- return NULL;
- node->base = temp_qword;
- node->length -= split_node->length;
- /* Put it in the list */
- split_node->next = node->next;
- node->next = split_node;
- } /* End of non-aligned base */
- /* Don't need to check if too small since we already did */
- if (node->length > size) {
- dbg("%s: too big\n", __FUNCTION__);
- /* this one is longer than we need
- so we'll make a new entry and split it up */
- split_node = acpiphp_make_resource(node->base + size, node->length - size);
- if (!split_node)
- return NULL;
- node->length = size;
- /* Put it in the list */
- split_node->next = node->next;
- node->next = split_node;
- } /* End of too big on top end */
- dbg("%s: got one!!!\n", __FUNCTION__);
- /* If we got here, then it is the right size
- Now take it out of the list */
- if (*head == node) {
- *head = node->next;
- } else {
- prevnode = *head;
- while (prevnode->next != node)
- prevnode = prevnode->next;
- prevnode->next = node->next;
- }
- node->next = NULL;
- /* Stop looping */
- break;
- }
- return node;
- }
- /**
- * get_resource_with_base - get resource with specific base address
- *
- * this function
- * returns the first node of "size" length located at specified base address.
- * If it finds a node larger than "size" it will split it up.
- *
- * size must be a power of two.
- *
- */
- struct pci_resource *acpiphp_get_resource_with_base (struct pci_resource **head, u64 base, u32 size)
- {
- struct pci_resource *prevnode;
- struct pci_resource *node;
- struct pci_resource *split_node;
- u64 temp_qword;
- if (!(*head))
- return NULL;
- if (acpiphp_resource_sort_and_combine(head))
- return NULL;
- for (node = *head; node; node = node->next) {
- dbg(": 1st req_base=%x req_size =%x node=%p, base=%x, length=%x\n",
- (u32)base, size, node, (u32)node->base, node->length);
- if (node->base > base)
- continue;
- if ((node->base + node->length) < (base + size))
- continue;
- if (node->base < base) {
- dbg(": split 1\n");
- /* this one isn't base aligned properly
- so we'll make a new entry and split it up */
- temp_qword = base;
- /* Short circuit if adjusted size is too small */
- if ((node->length - (temp_qword - node->base)) < size)
- continue;
- split_node = acpiphp_make_resource(node->base, temp_qword - node->base);
- if (!split_node)
- return NULL;
- node->base = temp_qword;
- node->length -= split_node->length;
- /* Put it in the list */
- split_node->next = node->next;
- node->next = split_node;
- }
- dbg(": 2nd req_base=%x req_size =%x node=%p, base=%x, length=%x\n",
- (u32)base, size, node, (u32)node->base, node->length);
- /* Don't need to check if too small since we already did */
- if (node->length > size) {
- dbg(": split 2\n");
- /* this one is longer than we need
- so we'll make a new entry and split it up */
- split_node = acpiphp_make_resource(node->base + size, node->length - size);
- if (!split_node)
- return NULL;
- node->length = size;
- /* Put it in the list */
- split_node->next = node->next;
- node->next = split_node;
- } /* End of too big on top end */
- dbg(": got one!!!\n");
- /* If we got here, then it is the right size
- Now take it out of the list */
- if (*head == node) {
- *head = node->next;
- } else {
- prevnode = *head;
- while (prevnode->next != node)
- prevnode = prevnode->next;
- prevnode->next = node->next;
- }
- node->next = NULL;
- /* Stop looping */
- break;
- }
- return node;
- }
- /**
- * acpiphp_resource_sort_and_combine
- *
- * Sorts all of the nodes in the list in ascending order by
- * their base addresses. Also does garbage collection by
- * combining adjacent nodes.
- *
- * returns 0 if success
- */
- int acpiphp_resource_sort_and_combine (struct pci_resource **head)
- {
- struct pci_resource *node1;
- struct pci_resource *node2;
- int out_of_order = 1;
- if (!(*head))
- return 1;
- dbg("*head->next = %p\n",(*head)->next);
- if (!(*head)->next)
- return 0; /* only one item on the list, already sorted! */
- dbg("*head->base = 0x%x\n",(u32)(*head)->base);
- dbg("*head->next->base = 0x%x\n", (u32)(*head)->next->base);
- while (out_of_order) {
- out_of_order = 0;
- /* Special case for swapping list head */
- if (((*head)->next) &&
- ((*head)->base > (*head)->next->base)) {
- node1 = *head;
- (*head) = (*head)->next;
- node1->next = (*head)->next;
- (*head)->next = node1;
- out_of_order++;
- }
- node1 = (*head);
- while (node1->next && node1->next->next) {
- if (node1->next->base > node1->next->next->base) {
- out_of_order++;
- node2 = node1->next;
- node1->next = node1->next->next;
- node1 = node1->next;
- node2->next = node1->next;
- node1->next = node2;
- } else
- node1 = node1->next;
- }
- } /* End of out_of_order loop */
- node1 = *head;
- while (node1 && node1->next) {
- if ((node1->base + node1->length) == node1->next->base) {
- /* Combine */
- dbg("8..\n");
- node1->length += node1->next->length;
- node2 = node1->next;
- node1->next = node1->next->next;
- kfree(node2);
- } else
- node1 = node1->next;
- }
- return 0;
- }
- /**
- * acpiphp_make_resource - make resource structure
- * @base: base address of a resource
- * @length: length of a resource
- */
- struct pci_resource *acpiphp_make_resource (u64 base, u32 length)
- {
- struct pci_resource *res;
- res = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
- if (res) {
- memset(res, 0, sizeof(struct pci_resource));
- res->base = base;
- res->length = length;
- }
- return res;
- }
- /**
- * acpiphp_move_resource - move linked resources from one to another
- * @from: head of linked resource list
- * @to: head of linked resource list
- */
- void acpiphp_move_resource (struct pci_resource **from, struct pci_resource **to)
- {
- struct pci_resource *tmp;
- while (*from) {
- tmp = (*from)->next;
- (*from)->next = *to;
- *to = *from;
- *from = tmp;
- }
- /* *from = NULL is guaranteed */
- }
- /**
- * acpiphp_free_resource - free all linked resources
- * @res: head of linked resource list
- */
- void acpiphp_free_resource (struct pci_resource **res)
- {
- struct pci_resource *tmp;
- while (*res) {
- tmp = (*res)->next;
- kfree(*res);
- *res = tmp;
- }
- /* *res = NULL is guaranteed */
- }
- /* debug support functions; will go away sometime :) */
- static void dump_resource(struct pci_resource *head)
- {
- struct pci_resource *p;
- int cnt;
- p = head;
- cnt = 0;
- while (p) {
- dbg("[%02d] %08x - %08x\n",
- cnt++, (u32)p->base, (u32)p->base + p->length - 1);
- p = p->next;
- }
- }
- void acpiphp_dump_resource(struct acpiphp_bridge *bridge)
- {
- dbg("I/O resource:\n");
- dump_resource(bridge->io_head);
- dbg("MEM resource:\n");
- dump_resource(bridge->mem_head);
- dbg("PMEM resource:\n");
- dump_resource(bridge->p_mem_head);
- dbg("BUS resource:\n");
- dump_resource(bridge->bus_head);
- }
- void acpiphp_dump_func_resource(struct acpiphp_func *func)
- {
- dbg("I/O resource:\n");
- dump_resource(func->io_head);
- dbg("MEM resource:\n");
- dump_resource(func->mem_head);
- dbg("PMEM resource:\n");
- dump_resource(func->p_mem_head);
- dbg("BUS resource:\n");
- dump_resource(func->bus_head);
- }
|