1 /*======================================================================
3 Resource management routines
5 rsrc_mgr.c 1.79 2000/08/30 20:23:58
7 The contents of this file are subject to the Mozilla Public
8 License Version 1.1 (the "License"); you may not use this file
9 except in compliance with the License. You may obtain a copy of
10 the License at http://www.mozilla.org/MPL/
12 Software distributed under the License is distributed on an "AS
13 IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14 implied. See the License for the specific language governing
15 rights and limitations under the License.
17 The initial developer of the original code is David A. Hinds
18 <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
19 are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
21 Alternatively, the contents of this file may be used under the
22 terms of the GNU Public License version 2 (the "GPL"), in which
23 case the provisions of the GPL are applicable instead of the
24 above. If you wish to allow the use of your version of this file
25 only under the terms of the GPL and not to allow others to use
26 your version of this file under the MPL, indicate your decision
27 by deleting the provisions above and replace them with the notice
28 and other provisions required by the GPL. If you do not delete
29 the provisions above, a recipient may use your version of this
30 file under either the MPL or the GPL.
32 ======================================================================*/
34 #define __NO_VERSION__
36 #include <linux/config.h>
37 #include <linux/module.h>
38 #include <linux/init.h>
39 #include <linux/sched.h>
40 #include <linux/kernel.h>
41 #include <linux/errno.h>
42 #include <linux/types.h>
43 #include <linux/malloc.h>
44 #include <linux/ioport.h>
45 #include <linux/timer.h>
49 #include <pcmcia/cs_types.h>
50 #include <pcmcia/ss.h>
51 #include <pcmcia/cs.h>
52 #include <pcmcia/bulkmem.h>
53 #include <pcmcia/cistpl.h>
54 #include "cs_internal.h"
57 /*====================================================================*/
59 /* Parameters that can be set with 'insmod' */
61 #define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
63 INT_MODULE_PARM(probe_mem
, 1); /* memory probe? */
65 INT_MODULE_PARM(probe_io
, 1); /* IO port probe? */
66 INT_MODULE_PARM(mem_limit
, 0x10000);
69 /*======================================================================
71 The resource_map_t structures are used to track what resources are
72 available for allocation for PC Card devices.
74 ======================================================================*/
76 typedef struct resource_map_t
{
78 struct resource_map_t
*next
;
81 /* Memory resource database */
82 static resource_map_t mem_db
= { 0, 0, &mem_db
};
84 /* IO port resource database */
85 static resource_map_t io_db
= { 0, 0, &io_db
};
89 typedef struct irq_info_t
{
91 int time_share
, dyn_share
;
92 struct socket_info_t
*Socket
;
95 /* Table of IRQ assignments */
96 static irq_info_t irq_table
[NR_IRQS
] = { { 0, 0, 0 }, /* etc */ };
100 /*======================================================================
102 Linux resource management extensions
104 ======================================================================*/
106 #define check_io_resource(b,n) check_resource(&ioport_resource, (b), (n))
107 #define check_mem_resource(b,n) check_resource(&iomem_resource, (b), (n))
109 /*======================================================================
111 These manage the internal databases of available resources.
113 ======================================================================*/
115 static int add_interval(resource_map_t
*map
, u_long base
, u_long num
)
117 resource_map_t
*p
, *q
;
119 for (p
= map
; ; p
= p
->next
) {
120 if ((p
!= map
) && (p
->base
+p
->num
-1 >= base
))
122 if ((p
->next
== map
) || (p
->next
->base
> base
+num
-1))
125 q
= kmalloc(sizeof(resource_map_t
), GFP_KERNEL
);
126 if (!q
) return CS_OUT_OF_RESOURCE
;
127 q
->base
= base
; q
->num
= num
;
128 q
->next
= p
->next
; p
->next
= q
;
132 /*====================================================================*/
134 static int sub_interval(resource_map_t
*map
, u_long base
, u_long num
)
136 resource_map_t
*p
, *q
;
138 for (p
= map
; ; p
= q
) {
142 if ((q
->base
+q
->num
> base
) && (base
+num
> q
->base
)) {
143 if (q
->base
>= base
) {
144 if (q
->base
+q
->num
<= base
+num
) {
145 /* Delete whole block */
148 /* don't advance the pointer yet */
151 /* Cut off bit from the front */
152 q
->num
= q
->base
+ q
->num
- base
- num
;
153 q
->base
= base
+ num
;
155 } else if (q
->base
+q
->num
<= base
+num
) {
156 /* Cut off bit from the end */
157 q
->num
= base
- q
->base
;
159 /* Split the block into two pieces */
160 p
= kmalloc(sizeof(resource_map_t
), GFP_KERNEL
);
161 if (!p
) return CS_OUT_OF_RESOURCE
;
163 p
->num
= q
->base
+q
->num
- p
->base
;
164 q
->num
= base
- q
->base
;
165 p
->next
= q
->next
; q
->next
= p
;
172 /*======================================================================
174 These routines examine a region of IO or memory addresses to
175 determine what ranges might be genuinely available.
177 ======================================================================*/
180 static void do_io_probe(ioaddr_t base
, ioaddr_t num
)
183 ioaddr_t i
, j
, bad
, any
;
184 u_char
*b
, hole
, most
;
186 printk(KERN_INFO
"cs: IO port probe 0x%04x-0x%04x:",
189 /* First, what does a floating port look like? */
190 b
= kmalloc(256, GFP_KERNEL
);
192 for (i
= base
, most
= 0; i
< base
+num
; i
+= 8) {
193 if (check_io_resource(i
, 8))
196 for (j
= 1; j
< 8; j
++)
197 if (inb(i
+j
) != hole
) break;
198 if ((j
== 8) && (++b
[hole
] > b
[most
]))
200 if (b
[most
] == 127) break;
205 for (i
= base
; i
< base
+num
; i
+= 8) {
206 if (check_io_resource(i
, 8))
208 for (j
= 0; j
< 8; j
++)
209 if (inb(i
+j
) != most
) break;
212 printk(" excluding");
217 sub_interval(&io_db
, bad
, i
-bad
);
218 printk(" %#04x-%#04x", bad
, i
-1);
224 if ((num
> 16) && (bad
== base
) && (i
== base
+num
)) {
225 printk(" nothing: probe failed.\n");
228 sub_interval(&io_db
, bad
, i
-bad
);
229 printk(" %#04x-%#04x", bad
, i
-1);
233 printk(any
? "\n" : " clean.\n");
237 /*======================================================================
239 The memory probe. If the memory list includes a 64K-aligned block
240 below 1MB, we probe in 64K chunks, and as soon as we accumulate at
241 least mem_limit free space, we quit.
243 ======================================================================*/
245 static int do_mem_probe(u_long base
, u_long num
,
246 int (*is_valid
)(u_long
), int (*do_cksum
)(u_long
))
248 u_long i
, j
, bad
, fail
, step
;
250 printk(KERN_INFO
"cs: memory probe 0x%06lx-0x%06lx:",
253 step
= (num
< 0x20000) ? 0x2000 : ((num
>>4) & ~0x1fff);
254 for (i
= base
; i
< base
+num
; i
= j
+ step
) {
256 for (j
= i
; j
< base
+num
; j
+= step
)
257 if ((check_mem_resource(j
, step
) == 0) && is_valid(j
))
259 fail
= ((i
== base
) && (j
== base
+num
));
262 for (j
= i
; j
< base
+num
; j
+= 2*step
)
263 if ((check_mem_resource(j
, 2*step
) == 0) &&
264 do_cksum(j
) && do_cksum(j
+step
))
268 if (!bad
) printk(" excluding");
269 printk(" %#05lx-%#05lx", i
, j
-1);
270 sub_interval(&mem_db
, i
, j
-i
);
274 printk(bad
? "\n" : " clean.\n");
280 static u_long
inv_probe(int (*is_valid
)(u_long
),
281 int (*do_cksum
)(u_long
),
287 ok
= inv_probe(is_valid
, do_cksum
, m
->next
);
289 if (m
->base
>= 0x100000)
290 sub_interval(&mem_db
, m
->base
, m
->num
);
293 if (m
->base
< 0x100000)
295 return do_mem_probe(m
->base
, m
->num
, is_valid
, do_cksum
);
298 void validate_mem(int (*is_valid
)(u_long
), int (*do_cksum
)(u_long
),
301 resource_map_t
*m
, *n
;
302 static u_char order
[] = { 0xd0, 0xe0, 0xc0, 0xf0 };
303 static int hi
= 0, lo
= 0;
306 if (!probe_mem
) return;
307 /* We do up to four passes through the list */
309 if (hi
++ || (inv_probe(is_valid
, do_cksum
, mem_db
.next
) > 0))
311 printk(KERN_NOTICE
"cs: warning: no high memory space "
315 for (m
= mem_db
.next
; m
!= &mem_db
; m
= n
) {
317 /* Only probe < 1 MB */
318 if (m
->base
>= 0x100000) continue;
319 if ((m
->base
| m
->num
) & 0xffff) {
320 ok
+= do_mem_probe(m
->base
, m
->num
, is_valid
, do_cksum
);
323 /* Special probe for 64K-aligned block */
324 for (i
= 0; i
< 4; i
++) {
326 if ((b
>= m
->base
) && (b
+0x10000 <= m
->base
+m
->num
)) {
328 sub_interval(&mem_db
, b
, 0x10000);
330 ok
+= do_mem_probe(b
, 0x10000, is_valid
, do_cksum
);
336 #else /* CONFIG_ISA */
338 void validate_mem(int (*is_valid
)(u_long
), int (*do_cksum
)(u_long
),
344 if (!probe_mem
|| done
++)
346 for (m
= mem_db
.next
; m
!= &mem_db
; m
= m
->next
)
347 if (do_mem_probe(m
->base
, m
->num
, is_valid
, do_cksum
))
351 #endif /* CONFIG_ISA */
353 /*======================================================================
355 These find ranges of I/O ports or memory addresses that are not
356 currently allocated by other devices.
358 The 'align' field should reflect the number of bits of address
359 that need to be preserved from the initial value of *base. It
360 should be a power of two, greater than or equal to 'num'. A value
361 of 0 means that all bits of *base are significant. *base should
362 also be strictly less than 'align'.
364 ======================================================================*/
366 int find_io_region(ioaddr_t
*base
, ioaddr_t num
, ioaddr_t align
,
372 for (m
= io_db
.next
; m
!= &io_db
; m
= m
->next
) {
373 try = (m
->base
& ~(align
-1)) + *base
;
374 for (try = (try >= m
->base
) ? try : try+align
;
375 (try >= m
->base
) && (try+num
<= m
->base
+m
->num
);
377 if (check_io_resource(try, num
) == 0) {
379 request_region(try, num
, name
);
388 int find_mem_region(u_long
*base
, u_long num
, u_long align
,
389 int force_low
, char *name
)
395 for (m
= mem_db
.next
; m
!= &mem_db
; m
= m
->next
) {
396 /* first pass >1MB, second pass <1MB */
397 if ((force_low
!= 0) ^ (m
->base
< 0x100000)) continue;
398 try = (m
->base
& ~(align
-1)) + *base
;
399 for (try = (try >= m
->base
) ? try : try+align
;
400 (try >= m
->base
) && (try+num
<= m
->base
+m
->num
);
402 if (check_mem_resource(try, num
) == 0) {
403 request_mem_region(try, num
, name
);
410 if (force_low
) break;
416 /*======================================================================
418 This checks to see if an interrupt is available, with support
419 for interrupt sharing. We don't support reserving interrupts
420 yet. If the interrupt is available, we allocate it.
422 ======================================================================*/
426 static void fake_irq(int i
, void *d
, struct pt_regs
*r
) { }
427 static inline int check_irq(int irq
)
429 if (request_irq(irq
, fake_irq
, 0, "bogus", NULL
) != 0)
435 int try_irq(u_int Attributes
, int irq
, int specific
)
437 irq_info_t
*info
= &irq_table
[irq
];
438 if (info
->Attributes
& RES_ALLOCATED
) {
439 switch (Attributes
& IRQ_TYPE
) {
440 case IRQ_TYPE_EXCLUSIVE
:
443 if ((info
->Attributes
& RES_IRQ_TYPE
)
444 != RES_IRQ_TYPE_TIME
)
446 if (Attributes
& IRQ_FIRST_SHARED
)
447 return CS_BAD_ATTRIBUTE
;
448 info
->Attributes
|= RES_IRQ_TYPE_TIME
| RES_ALLOCATED
;
451 case IRQ_TYPE_DYNAMIC_SHARING
:
452 if ((info
->Attributes
& RES_IRQ_TYPE
)
453 != RES_IRQ_TYPE_DYNAMIC
)
455 if (Attributes
& IRQ_FIRST_SHARED
)
456 return CS_BAD_ATTRIBUTE
;
457 info
->Attributes
|= RES_IRQ_TYPE_DYNAMIC
| RES_ALLOCATED
;
462 if ((info
->Attributes
& RES_RESERVED
) && !specific
)
464 if (check_irq(irq
) != 0)
466 switch (Attributes
& IRQ_TYPE
) {
467 case IRQ_TYPE_EXCLUSIVE
:
468 info
->Attributes
|= RES_ALLOCATED
;
471 if (!(Attributes
& IRQ_FIRST_SHARED
))
472 return CS_BAD_ATTRIBUTE
;
473 info
->Attributes
|= RES_IRQ_TYPE_TIME
| RES_ALLOCATED
;
474 info
->time_share
= 1;
476 case IRQ_TYPE_DYNAMIC_SHARING
:
477 if (!(Attributes
& IRQ_FIRST_SHARED
))
478 return CS_BAD_ATTRIBUTE
;
479 info
->Attributes
|= RES_IRQ_TYPE_DYNAMIC
| RES_ALLOCATED
;
489 /*====================================================================*/
493 void undo_irq(u_int Attributes
, int irq
)
497 info
= &irq_table
[irq
];
498 switch (Attributes
& IRQ_TYPE
) {
499 case IRQ_TYPE_EXCLUSIVE
:
500 info
->Attributes
&= RES_RESERVED
;
504 if (info
->time_share
== 0)
505 info
->Attributes
&= RES_RESERVED
;
507 case IRQ_TYPE_DYNAMIC_SHARING
:
509 if (info
->dyn_share
== 0)
510 info
->Attributes
&= RES_RESERVED
;
517 /*======================================================================
519 The various adjust_* calls form the external interface to the
522 ======================================================================*/
524 static int adjust_memory(adjust_t
*adj
)
529 base
= adj
->resource
.memory
.Base
;
530 num
= adj
->resource
.memory
.Size
;
531 if ((num
== 0) || (base
+num
-1 < base
))
535 switch (adj
->Action
) {
536 case ADD_MANAGED_RESOURCE
:
537 ret
= add_interval(&mem_db
, base
, num
);
539 case REMOVE_MANAGED_RESOURCE
:
540 ret
= sub_interval(&mem_db
, base
, num
);
541 if (ret
== CS_SUCCESS
) {
542 for (i
= 0; i
< sockets
; i
++) {
543 release_cis_mem(socket_table
[i
]);
544 #ifdef CONFIG_CARDBUS
545 cb_release_cis_mem(socket_table
[i
]);
551 ret
= CS_UNSUPPORTED_FUNCTION
;
557 /*====================================================================*/
559 static int adjust_io(adjust_t
*adj
)
563 base
= adj
->resource
.io
.BasePort
;
564 num
= adj
->resource
.io
.NumPorts
;
565 if ((base
< 0) || (base
> 0xffff))
567 if ((num
<= 0) || (base
+num
> 0x10000) || (base
+num
<= base
))
570 switch (adj
->Action
) {
571 case ADD_MANAGED_RESOURCE
:
572 if (add_interval(&io_db
, base
, num
) != 0)
576 do_io_probe(base
, num
);
579 case REMOVE_MANAGED_RESOURCE
:
580 sub_interval(&io_db
, base
, num
);
583 return CS_UNSUPPORTED_FUNCTION
;
590 /*====================================================================*/
592 static int adjust_irq(adjust_t
*adj
)
598 irq
= adj
->resource
.irq
.IRQ
;
599 if ((irq
< 0) || (irq
> 15))
601 info
= &irq_table
[irq
];
603 switch (adj
->Action
) {
604 case ADD_MANAGED_RESOURCE
:
605 if (info
->Attributes
& RES_REMOVED
)
606 info
->Attributes
&= ~(RES_REMOVED
|RES_ALLOCATED
);
608 if (adj
->Attributes
& RES_ALLOCATED
)
610 if (adj
->Attributes
& RES_RESERVED
)
611 info
->Attributes
|= RES_RESERVED
;
613 info
->Attributes
&= ~RES_RESERVED
;
615 case REMOVE_MANAGED_RESOURCE
:
616 if (info
->Attributes
& RES_REMOVED
)
618 if (info
->Attributes
& RES_ALLOCATED
)
620 info
->Attributes
|= RES_ALLOCATED
|RES_REMOVED
;
621 info
->Attributes
&= ~RES_RESERVED
;
624 return CS_UNSUPPORTED_FUNCTION
;
631 /*====================================================================*/
633 int pcmcia_adjust_resource_info(client_handle_t handle
, adjust_t
*adj
)
635 if (CHECK_HANDLE(handle
))
636 return CS_BAD_HANDLE
;
638 switch (adj
->Resource
) {
639 case RES_MEMORY_RANGE
:
640 return adjust_memory(adj
);
643 return adjust_io(adj
);
646 return adjust_irq(adj
);
649 return CS_UNSUPPORTED_FUNCTION
;
652 /*====================================================================*/
654 void release_resource_db(void)
656 resource_map_t
*p
, *q
;
658 for (p
= mem_db
.next
; p
!= &mem_db
; p
= q
) {
662 for (p
= io_db
.next
; p
!= &io_db
; p
= q
) {