acpiphp_res.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:16k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

  1. /*
  2.  * ACPI PCI HotPlug Utility functions
  3.  *
  4.  * Copyright (c) 1995,2001 Compaq Computer Corporation
  5.  * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com)
  6.  * Copyright (c) 2001 IBM Corp.
  7.  * Copyright (c) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com)
  8.  * Copyright (c) 2002 Takayoshi Kochi (t-kouchi@cq.jp.nec.com)
  9.  * Copyright (c) 2002 NEC Corporation
  10.  *
  11.  * All rights reserved.
  12.  *
  13.  * This program is free software; you can redistribute it and/or modify
  14.  * it under the terms of the GNU General Public License as published by
  15.  * the Free Software Foundation; either version 2 of the License, or (at
  16.  * your option) any later version.
  17.  *
  18.  * This program is distributed in the hope that it will be useful, but
  19.  * WITHOUT ANY WARRANTY; without even the implied warranty of
  20.  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
  21.  * NON INFRINGEMENT.  See the GNU General Public License for more
  22.  * details.
  23.  *
  24.  * You should have received a copy of the GNU General Public License
  25.  * along with this program; if not, write to the Free Software
  26.  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  27.  *
  28.  * Send feedback to <gregkh@us.ibm.com>,<h-aono@ap.jp.nec.com>
  29.  *
  30.  */
  31. #include <linux/config.h>
  32. #include <linux/module.h>
  33. #include <linux/kernel.h>
  34. #include <linux/types.h>
  35. #include <linux/proc_fs.h>
  36. #include <linux/sysctl.h>
  37. #include <linux/pci.h>
  38. #include <linux/smp.h>
  39. #include <linux/smp_lock.h>
  40. #include <linux/init.h>
  41. #include <linux/string.h>
  42. #include <linux/mm.h>
  43. #include <linux/errno.h>
  44. #include <linux/ioport.h>
  45. #include <linux/slab.h>
  46. #include <linux/interrupt.h>
  47. #include <linux/timer.h>
  48. #include <linux/ioctl.h>
  49. #include <linux/fcntl.h>
  50. #include <linux/list.h>
  51. #include "pci_hotplug.h"
  52. #include "acpiphp.h"
  53. #define MY_NAME "acpiphp_res"
  54. /* local variables */
  55. static int debug = 0;
  56. /*
  57.  * sort_by_size - sort nodes by their length, smallest first
  58.  */
  59. static int sort_by_size(struct pci_resource **head)
  60. {
  61. struct pci_resource *current_res;
  62. struct pci_resource *next_res;
  63. int out_of_order = 1;
  64. if (!(*head))
  65. return 1;
  66. if (!((*head)->next))
  67. return 0;
  68. while (out_of_order) {
  69. out_of_order = 0;
  70. /* Special case for swapping list head */
  71. if (((*head)->next) &&
  72.     ((*head)->length > (*head)->next->length)) {
  73. out_of_order++;
  74. current_res = *head;
  75. *head = (*head)->next;
  76. current_res->next = (*head)->next;
  77. (*head)->next = current_res;
  78. }
  79. current_res = *head;
  80. while (current_res->next && current_res->next->next) {
  81. if (current_res->next->length > current_res->next->next->length) {
  82. out_of_order++;
  83. next_res = current_res->next;
  84. current_res->next = current_res->next->next;
  85. current_res = current_res->next;
  86. next_res->next = current_res->next;
  87. current_res->next = next_res;
  88. } else
  89. current_res = current_res->next;
  90. }
  91. }  /* End of out_of_order loop */
  92. return 0;
  93. }
  94. /*
  95.  * sort_by_max_size - sort nodes by their length, largest first
  96.  */
  97. static int sort_by_max_size(struct pci_resource **head)
  98. {
  99. struct pci_resource *current_res;
  100. struct pci_resource *next_res;
  101. int out_of_order = 1;
  102. if (!(*head))
  103. return 1;
  104. if (!((*head)->next))
  105. return 0;
  106. while (out_of_order) {
  107. out_of_order = 0;
  108. /* Special case for swapping list head */
  109. if (((*head)->next) &&
  110.     ((*head)->length < (*head)->next->length)) {
  111. out_of_order++;
  112. current_res = *head;
  113. *head = (*head)->next;
  114. current_res->next = (*head)->next;
  115. (*head)->next = current_res;
  116. }
  117. current_res = *head;
  118. while (current_res->next && current_res->next->next) {
  119. if (current_res->next->length < current_res->next->next->length) {
  120. out_of_order++;
  121. next_res = current_res->next;
  122. current_res->next = current_res->next->next;
  123. current_res = current_res->next;
  124. next_res->next = current_res->next;
  125. current_res->next = next_res;
  126. } else
  127. current_res = current_res->next;
  128. }
  129. }  /* End of out_of_order loop */
  130. return 0;
  131. }
  132. /**
  133.  * get_io_resource - get resource for I/O ports
  134.  *
  135.  * this function sorts the resource list by size and then
  136.  * returns the first node of "size" length that is not in the
  137.  * ISA aliasing window.  If it finds a node larger than "size"
  138.  * it will split it up.
  139.  *
  140.  * size must be a power of two.
  141.  *
  142.  * difference from get_resource is handling of ISA aliasing space.
  143.  *
  144.  */
  145. struct pci_resource *acpiphp_get_io_resource (struct pci_resource **head, u32 size)
  146. {
  147. struct pci_resource *prevnode;
  148. struct pci_resource *node;
  149. struct pci_resource *split_node;
  150. u64 temp_qword;
  151. if (!(*head))
  152. return NULL;
  153. if (acpiphp_resource_sort_and_combine(head))
  154. return NULL;
  155. if (sort_by_size(head))
  156. return NULL;
  157. for (node = *head; node; node = node->next) {
  158. if (node->length < size)
  159. continue;
  160. if (node->base & (size - 1)) {
  161. /* this one isn't base aligned properly
  162.    so we'll make a new entry and split it up */
  163. temp_qword = (node->base | (size-1)) + 1;
  164. /* Short circuit if adjusted size is too small */
  165. if ((node->length - (temp_qword - node->base)) < size)
  166. continue;
  167. split_node = acpiphp_make_resource(node->base, temp_qword - node->base);
  168. if (!split_node)
  169. return NULL;
  170. node->base = temp_qword;
  171. node->length -= split_node->length;
  172. /* Put it in the list */
  173. split_node->next = node->next;
  174. node->next = split_node;
  175. } /* End of non-aligned base */
  176. /* Don't need to check if too small since we already did */
  177. if (node->length > size) {
  178. /* this one is longer than we need
  179.    so we'll make a new entry and split it up */
  180. split_node = acpiphp_make_resource(node->base + size, node->length - size);
  181. if (!split_node)
  182. return NULL;
  183. node->length = size;
  184. /* Put it in the list */
  185. split_node->next = node->next;
  186. node->next = split_node;
  187. }  /* End of too big on top end */
  188. /* For IO make sure it's not in the ISA aliasing space */
  189. if (node->base & 0x300L)
  190. continue;
  191. /* If we got here, then it is the right size
  192.    Now take it out of the list */
  193. if (*head == node) {
  194. *head = node->next;
  195. } else {
  196. prevnode = *head;
  197. while (prevnode->next != node)
  198. prevnode = prevnode->next;
  199. prevnode->next = node->next;
  200. }
  201. node->next = NULL;
  202. /* Stop looping */
  203. break;
  204. }
  205. return node;
  206. }
  207. /**
  208.  * get_max_resource - get the largest resource
  209.  *
  210.  * Gets the largest node that is at least "size" big from the
  211.  * list pointed to by head.  It aligns the node on top and bottom
  212.  * to "size" alignment before returning it.
  213.  */
  214. struct pci_resource *acpiphp_get_max_resource (struct pci_resource **head, u32 size)
  215. {
  216. struct pci_resource *max;
  217. struct pci_resource *temp;
  218. struct pci_resource *split_node;
  219. u64 temp_qword;
  220. if (!(*head))
  221. return NULL;
  222. if (acpiphp_resource_sort_and_combine(head))
  223. return NULL;
  224. if (sort_by_max_size(head))
  225. return NULL;
  226. for (max = *head;max; max = max->next) {
  227. /* If not big enough we could probably just bail, 
  228.    instead we'll continue to the next. */
  229. if (max->length < size)
  230. continue;
  231. if (max->base & (size - 1)) {
  232. /* this one isn't base aligned properly
  233.    so we'll make a new entry and split it up */
  234. temp_qword = (max->base | (size-1)) + 1;
  235. /* Short circuit if adjusted size is too small */
  236. if ((max->length - (temp_qword - max->base)) < size)
  237. continue;
  238. split_node = acpiphp_make_resource(max->base, temp_qword - max->base);
  239. if (!split_node)
  240. return NULL;
  241. max->base = temp_qword;
  242. max->length -= split_node->length;
  243. /* Put it next in the list */
  244. split_node->next = max->next;
  245. max->next = split_node;
  246. }
  247. if ((max->base + max->length) & (size - 1)) {
  248. /* this one isn't end aligned properly at the top
  249.    so we'll make a new entry and split it up */
  250. temp_qword = ((max->base + max->length) & ~(size - 1));
  251. split_node = acpiphp_make_resource(temp_qword,
  252.    max->length + max->base - temp_qword);
  253. if (!split_node)
  254. return NULL;
  255. max->length -= split_node->length;
  256. /* Put it in the list */
  257. split_node->next = max->next;
  258. max->next = split_node;
  259. }
  260. /* Make sure it didn't shrink too much when we aligned it */
  261. if (max->length < size)
  262. continue;
  263. /* Now take it out of the list */
  264. temp = (struct pci_resource*) *head;
  265. if (temp == max) {
  266. *head = max->next;
  267. } else {
  268. while (temp && temp->next != max) {
  269. temp = temp->next;
  270. }
  271. temp->next = max->next;
  272. }
  273. max->next = NULL;
  274. return max;
  275. }
  276. /* If we get here, we couldn't find one */
  277. return NULL;
  278. }
  279. /**
  280.  * get_resource - get resource (mem, pfmem)
  281.  *
  282.  * this function sorts the resource list by size and then
  283.  * returns the first node of "size" length.  If it finds a node
  284.  * larger than "size" it will split it up.
  285.  *
  286.  * size must be a power of two.
  287.  *
  288.  */
  289. struct pci_resource *acpiphp_get_resource (struct pci_resource **head, u32 size)
  290. {
  291. struct pci_resource *prevnode;
  292. struct pci_resource *node;
  293. struct pci_resource *split_node;
  294. u64 temp_qword;
  295. if (!(*head))
  296. return NULL;
  297. if (acpiphp_resource_sort_and_combine(head))
  298. return NULL;
  299. if (sort_by_size(head))
  300. return NULL;
  301. for (node = *head; node; node = node->next) {
  302. dbg("%s: req_size =%x node=%p, base=%x, length=%x",
  303.     __FUNCTION__, size, node, (u32)node->base, node->length);
  304. if (node->length < size)
  305. continue;
  306. if (node->base & (size - 1)) {
  307. dbg("%s: not aligned", __FUNCTION__);
  308. /* this one isn't base aligned properly
  309.    so we'll make a new entry and split it up */
  310. temp_qword = (node->base | (size-1)) + 1;
  311. /* Short circuit if adjusted size is too small */
  312. if ((node->length - (temp_qword - node->base)) < size)
  313. continue;
  314. split_node = acpiphp_make_resource(node->base, temp_qword - node->base);
  315. if (!split_node)
  316. return NULL;
  317. node->base = temp_qword;
  318. node->length -= split_node->length;
  319. /* Put it in the list */
  320. split_node->next = node->next;
  321. node->next = split_node;
  322. } /* End of non-aligned base */
  323. /* Don't need to check if too small since we already did */
  324. if (node->length > size) {
  325. dbg("%s: too big", __FUNCTION__);
  326. /* this one is longer than we need
  327.    so we'll make a new entry and split it up */
  328. split_node = acpiphp_make_resource(node->base + size, node->length - size);
  329. if (!split_node)
  330. return NULL;
  331. node->length = size;
  332. /* Put it in the list */
  333. split_node->next = node->next;
  334. node->next = split_node;
  335. }  /* End of too big on top end */
  336. dbg("%s: got one!!!", __FUNCTION__);
  337. /* If we got here, then it is the right size
  338.    Now take it out of the list */
  339. if (*head == node) {
  340. *head = node->next;
  341. } else {
  342. prevnode = *head;
  343. while (prevnode->next != node)
  344. prevnode = prevnode->next;
  345. prevnode->next = node->next;
  346. }
  347. node->next = NULL;
  348. /* Stop looping */
  349. break;
  350. }
  351. return node;
  352. }
  353. /**
  354.  * get_resource_with_base - get resource with specific base address
  355.  *
  356.  * this function 
  357.  * returns the first node of "size" length located at specified base address.
  358.  * If it finds a node larger than "size" it will split it up.
  359.  *
  360.  * size must be a power of two.
  361.  *
  362.  */
  363. struct pci_resource *acpiphp_get_resource_with_base (struct pci_resource **head, u64 base, u32 size)
  364. {
  365. struct pci_resource *prevnode;
  366. struct pci_resource *node;
  367. struct pci_resource *split_node;
  368. u64 temp_qword;
  369. if (!(*head))
  370. return NULL;
  371. if (acpiphp_resource_sort_and_combine(head))
  372. return NULL;
  373. for (node = *head; node; node = node->next) {
  374. dbg(": 1st req_base=%x req_size =%x node=%p, base=%x, length=%x",
  375.     (u32)base, size, node, (u32)node->base, node->length);
  376. if (node->base > base)
  377. continue;
  378. if ((node->base + node->length) < (base + size))
  379. continue;
  380. if (node->base < base) {
  381. dbg(": split 1");
  382. /* this one isn't base aligned properly
  383.    so we'll make a new entry and split it up */
  384. temp_qword = base;
  385. /* Short circuit if adjusted size is too small */
  386. if ((node->length - (temp_qword - node->base)) < size)
  387. continue;
  388. split_node = acpiphp_make_resource(node->base, temp_qword - node->base);
  389. if (!split_node)
  390. return NULL;
  391. node->base = temp_qword;
  392. node->length -= split_node->length;
  393. /* Put it in the list */
  394. split_node->next = node->next;
  395. node->next = split_node;
  396. }
  397. dbg(": 2nd req_base=%x req_size =%x node=%p, base=%x, length=%x",
  398.     (u32)base, size, node, (u32)node->base, node->length);
  399. /* Don't need to check if too small since we already did */
  400. if (node->length > size) {
  401. dbg(": split 2");
  402. /* this one is longer than we need
  403.    so we'll make a new entry and split it up */
  404. split_node = acpiphp_make_resource(node->base + size, node->length - size);
  405. if (!split_node)
  406. return NULL;
  407. node->length = size;
  408. /* Put it in the list */
  409. split_node->next = node->next;
  410. node->next = split_node;
  411. }  /* End of too big on top end */
  412. dbg(": got one!!!");
  413. /* If we got here, then it is the right size
  414.    Now take it out of the list */
  415. if (*head == node) {
  416. *head = node->next;
  417. } else {
  418. prevnode = *head;
  419. while (prevnode->next != node)
  420. prevnode = prevnode->next;
  421. prevnode->next = node->next;
  422. }
  423. node->next = NULL;
  424. /* Stop looping */
  425. break;
  426. }
  427. return node;
  428. }
  429. /**
  430.  * acpiphp_resource_sort_and_combine
  431.  *
  432.  * Sorts all of the nodes in the list in ascending order by
  433.  * their base addresses.  Also does garbage collection by
  434.  * combining adjacent nodes.
  435.  *
  436.  * returns 0 if success
  437.  */
  438. int acpiphp_resource_sort_and_combine (struct pci_resource **head)
  439. {
  440. struct pci_resource *node1;
  441. struct pci_resource *node2;
  442. int out_of_order = 1;
  443. if (!(*head))
  444. return 1;
  445. dbg("*head->next = %p",(*head)->next);
  446. if (!(*head)->next)
  447. return 0; /* only one item on the list, already sorted! */
  448. dbg("*head->base = 0x%x",(u32)(*head)->base);
  449. dbg("*head->next->base = 0x%x", (u32)(*head)->next->base);
  450. while (out_of_order) {
  451. out_of_order = 0;
  452. /* Special case for swapping list head */
  453. if (((*head)->next) &&
  454.     ((*head)->base > (*head)->next->base)) {
  455. node1 = *head;
  456. (*head) = (*head)->next;
  457. node1->next = (*head)->next;
  458. (*head)->next = node1;
  459. out_of_order++;
  460. }
  461. node1 = (*head);
  462. while (node1->next && node1->next->next) {
  463. if (node1->next->base > node1->next->next->base) {
  464. out_of_order++;
  465. node2 = node1->next;
  466. node1->next = node1->next->next;
  467. node1 = node1->next;
  468. node2->next = node1->next;
  469. node1->next = node2;
  470. } else
  471. node1 = node1->next;
  472. }
  473. }  /* End of out_of_order loop */
  474. node1 = *head;
  475. while (node1 && node1->next) {
  476. if ((node1->base + node1->length) == node1->next->base) {
  477. /* Combine */
  478. dbg("8..");
  479. node1->length += node1->next->length;
  480. node2 = node1->next;
  481. node1->next = node1->next->next;
  482. kfree(node2);
  483. } else
  484. node1 = node1->next;
  485. }
  486. return 0;
  487. }
  488. /**
  489.  * acpiphp_make_resource - make resource structure
  490.  * @base: base address of a resource
  491.  * @length: length of a resource
  492.  */
  493. struct pci_resource *acpiphp_make_resource (u64 base, u32 length)
  494. {
  495. struct pci_resource *res;
  496. res = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
  497. if (res) {
  498. memset(res, 0, sizeof(struct pci_resource));
  499. res->base = base;
  500. res->length = length;
  501. }
  502. return res;
  503. }
  504. /**
  505.  * acpiphp_move_resource - move linked resources from one to another
  506.  * @from: head of linked resource list
  507.  * @to: head of linked resource list
  508.  */
  509. void acpiphp_move_resource (struct pci_resource **from, struct pci_resource **to)
  510. {
  511. struct pci_resource *tmp;
  512. while (*from) {
  513. tmp = (*from)->next;
  514. (*from)->next = *to;
  515. *to = *from;
  516. *from = tmp;
  517. }
  518. /* *from = NULL is guaranteed */
  519. }
  520. /**
  521.  * acpiphp_free_resource - free all linked resources
  522.  * @res: head of linked resource list
  523.  */
  524. void acpiphp_free_resource (struct pci_resource **res)
  525. {
  526. struct pci_resource *tmp;
  527. while (*res) {
  528. tmp = (*res)->next;
  529. kfree(*res);
  530. *res = tmp;
  531. }
  532. /* *res = NULL is guaranteed */
  533. }
  534. /* debug support functions;  will go away sometime :) */
  535. static void dump_resource(struct pci_resource *head)
  536. {
  537. struct pci_resource *p;
  538. int cnt;
  539. p = head;
  540. cnt = 0;
  541. while (p) {
  542. info("[%02d] %08x - %08x",
  543.      cnt++, (u32)p->base, (u32)p->base + p->length - 1);
  544. p = p->next;
  545. }
  546. }
  547. void acpiphp_dump_resource(struct acpiphp_bridge *bridge)
  548. {
  549. info("I/O resource:");
  550. dump_resource(bridge->io_head);
  551. info("MEM resource:");
  552. dump_resource(bridge->mem_head);
  553. info("PMEM resource:");
  554. dump_resource(bridge->p_mem_head);
  555. info("BUS resource:");
  556. dump_resource(bridge->bus_head);
  557. }
  558. void acpiphp_dump_func_resource(struct acpiphp_func *func)
  559. {
  560. info("I/O resource:");
  561. dump_resource(func->io_head);
  562. info("MEM resource:");
  563. dump_resource(func->mem_head);
  564. info("PMEM resource:");
  565. dump_resource(func->p_mem_head);
  566. info("BUS resource:");
  567. dump_resource(func->bus_head);
  568. }
  569. /*
  570. EXPORT_SYMBOL(acpiphp_get_io_resource);
  571. EXPORT_SYMBOL(acpiphp_get_max_resource);
  572. EXPORT_SYMBOL(acpiphp_get_resource);
  573. EXPORT_SYMBOL(acpiphp_resource_sort_and_combine);
  574. */