blob: 0e4141bac7b1b099b3e47eae12576936f4805c45 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * rsrc_nonstatic.c -- Resource management routines for !SS_CAP_STATIC_MAP sockets
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * The initial developer of the original code is David A. Hinds
9 * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
10 * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
11 *
12 * (C) 1999 David A. Hinds
13 */
14
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include <linux/module.h>
16#include <linux/moduleparam.h>
17#include <linux/init.h>
18#include <linux/interrupt.h>
19#include <linux/kernel.h>
20#include <linux/errno.h>
21#include <linux/types.h>
22#include <linux/slab.h>
23#include <linux/ioport.h>
24#include <linux/timer.h>
25#include <linux/pci.h>
26#include <linux/device.h>
27
28#include <asm/irq.h>
29#include <asm/io.h>
30
31#include <pcmcia/cs_types.h>
32#include <pcmcia/ss.h>
33#include <pcmcia/cs.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070034#include <pcmcia/cistpl.h>
35#include "cs_internal.h"
36
37MODULE_AUTHOR("David A. Hinds, Dominik Brodowski");
38MODULE_LICENSE("GPL");
39
40/* Parameters that can be set with 'insmod' */
41
42#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0444)
43
44INT_MODULE_PARM(probe_mem, 1); /* memory probe? */
45#ifdef CONFIG_PCMCIA_PROBE
46INT_MODULE_PARM(probe_io, 1); /* IO port probe? */
47INT_MODULE_PARM(mem_limit, 0x10000);
48#endif
49
50/* for io_db and mem_db */
51struct resource_map {
52 u_long base, num;
53 struct resource_map *next;
54};
55
56struct socket_data {
57 struct resource_map mem_db;
58 struct resource_map io_db;
59 unsigned int rsrc_mem_probe;
60};
61
Dominik Brodowski7fe908d2006-01-10 21:20:36 +010062static DEFINE_MUTEX(rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -070063#define MEM_PROBE_LOW (1 << 0)
64#define MEM_PROBE_HIGH (1 << 1)
65
66
67/*======================================================================
68
69 Linux resource management extensions
70
71======================================================================*/
72
73static struct resource *
Greg Kroah-Hartman2427ddd2006-06-12 17:07:52 -070074make_resource(resource_size_t b, resource_size_t n, int flags, char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -070075{
Dominik Brodowski8084b372005-12-11 21:18:26 +010076 struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -070077
78 if (res) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070079 res->name = name;
80 res->start = b;
81 res->end = b + n - 1;
82 res->flags = flags;
83 }
84 return res;
85}
86
87static struct resource *
Greg Kroah-Hartman2427ddd2006-06-12 17:07:52 -070088claim_region(struct pcmcia_socket *s, resource_size_t base,
89 resource_size_t size, int type, char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -070090{
91 struct resource *res, *parent;
92
93 parent = type & IORESOURCE_MEM ? &iomem_resource : &ioport_resource;
94 res = make_resource(base, size, type | IORESOURCE_BUSY, name);
95
96 if (res) {
97#ifdef CONFIG_PCI
98 if (s && s->cb_dev)
99 parent = pci_find_parent_resource(s->cb_dev, res);
100#endif
101 if (!parent || request_resource(parent, res)) {
102 kfree(res);
103 res = NULL;
104 }
105 }
106 return res;
107}
108
109static void free_region(struct resource *res)
110{
111 if (res) {
112 release_resource(res);
113 kfree(res);
114 }
115}
116
117/*======================================================================
118
119 These manage the internal databases of available resources.
120
121======================================================================*/
122
123static int add_interval(struct resource_map *map, u_long base, u_long num)
124{
Dominik Brodowski11683862008-08-03 10:22:47 +0200125 struct resource_map *p, *q;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126
Dominik Brodowski11683862008-08-03 10:22:47 +0200127 for (p = map; ; p = p->next) {
128 if ((p != map) && (p->base+p->num-1 >= base))
129 return -1;
130 if ((p->next == map) || (p->next->base > base+num-1))
131 break;
132 }
133 q = kmalloc(sizeof(struct resource_map), GFP_KERNEL);
134 if (!q) {
135 printk(KERN_WARNING "out of memory to update resources\n");
136 return -ENOMEM;
137 }
138 q->base = base; q->num = num;
139 q->next = p->next; p->next = q;
140 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141}
142
143/*====================================================================*/
144
145static int sub_interval(struct resource_map *map, u_long base, u_long num)
146{
147 struct resource_map *p, *q;
148
149 for (p = map; ; p = q) {
150 q = p->next;
151 if (q == map)
152 break;
153 if ((q->base+q->num > base) && (base+num > q->base)) {
154 if (q->base >= base) {
155 if (q->base+q->num <= base+num) {
156 /* Delete whole block */
157 p->next = q->next;
158 kfree(q);
159 /* don't advance the pointer yet */
160 q = p;
161 } else {
162 /* Cut off bit from the front */
163 q->num = q->base + q->num - base - num;
164 q->base = base + num;
165 }
166 } else if (q->base+q->num <= base+num) {
167 /* Cut off bit from the end */
168 q->num = base - q->base;
169 } else {
170 /* Split the block into two pieces */
171 p = kmalloc(sizeof(struct resource_map), GFP_KERNEL);
Dominik Brodowski11683862008-08-03 10:22:47 +0200172 if (!p) {
173 printk(KERN_WARNING "out of memory to update resources\n");
174 return -ENOMEM;
175 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 p->base = base+num;
177 p->num = q->base+q->num - p->base;
178 q->num = base - q->base;
179 p->next = q->next ; q->next = p;
180 }
181 }
182 }
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200183 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184}
185
186/*======================================================================
187
188 These routines examine a region of IO or memory addresses to
189 determine what ranges might be genuinely available.
190
191======================================================================*/
192
193#ifdef CONFIG_PCMCIA_PROBE
Olof Johansson906da802008-02-04 22:27:35 -0800194static void do_io_probe(struct pcmcia_socket *s, unsigned int base,
195 unsigned int num)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196{
197 struct resource *res;
198 struct socket_data *s_data = s->resource_data;
Olof Johansson906da802008-02-04 22:27:35 -0800199 unsigned int i, j, bad;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 int any;
201 u_char *b, hole, most;
202
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200203 dev_printk(KERN_INFO, &s->dev, "cs: IO port probe %#x-%#x:",
204 base, base+num-1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205
206 /* First, what does a floating port look like? */
Dominik Brodowski8084b372005-12-11 21:18:26 +0100207 b = kzalloc(256, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208 if (!b) {
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200209 dev_printk(KERN_ERR, &s->dev,
210 "do_io_probe: unable to kmalloc 256 bytes");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 return;
212 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 for (i = base, most = 0; i < base+num; i += 8) {
214 res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA IO probe");
215 if (!res)
216 continue;
217 hole = inb(i);
218 for (j = 1; j < 8; j++)
219 if (inb(i+j) != hole) break;
220 free_region(res);
221 if ((j == 8) && (++b[hole] > b[most]))
222 most = hole;
223 if (b[most] == 127) break;
224 }
225 kfree(b);
226
227 bad = any = 0;
228 for (i = base; i < base+num; i += 8) {
229 res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA IO probe");
230 if (!res)
231 continue;
232 for (j = 0; j < 8; j++)
233 if (inb(i+j) != most) break;
234 free_region(res);
235 if (j < 8) {
236 if (!any)
237 printk(" excluding");
238 if (!bad)
239 bad = any = i;
240 } else {
241 if (bad) {
242 sub_interval(&s_data->io_db, bad, i-bad);
Olof Johansson906da802008-02-04 22:27:35 -0800243 printk(" %#x-%#x", bad, i-1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 bad = 0;
245 }
246 }
247 }
248 if (bad) {
249 if ((num > 16) && (bad == base) && (i == base+num)) {
250 printk(" nothing: probe failed.\n");
251 return;
252 } else {
253 sub_interval(&s_data->io_db, bad, i-bad);
Olof Johansson906da802008-02-04 22:27:35 -0800254 printk(" %#x-%#x", bad, i-1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255 }
256 }
257
258 printk(any ? "\n" : " clean.\n");
259}
260#endif
261
262/*======================================================================
263
264 This is tricky... when we set up CIS memory, we try to validate
265 the memory window space allocations.
266
267======================================================================*/
268
269/* Validation function for cards with a valid CIS */
Dominik Brodowskic5081d52008-06-19 20:12:34 +0200270static int readable(struct pcmcia_socket *s, struct resource *res,
271 unsigned int *count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272{
273 int ret = -1;
274
275 s->cis_mem.res = res;
276 s->cis_virt = ioremap(res->start, s->map_size);
277 if (s->cis_virt) {
Dominik Brodowskic5081d52008-06-19 20:12:34 +0200278 ret = pccard_validate_cis(s, BIND_FN_ALL, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 /* invalidate mapping and CIS cache */
280 iounmap(s->cis_virt);
281 s->cis_virt = NULL;
282 destroy_cis_cache(s);
283 }
284 s->cis_mem.res = NULL;
Dominik Brodowskic5081d52008-06-19 20:12:34 +0200285 if ((ret != 0) || (count == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 return 0;
287 return 1;
288}
289
290/* Validation function for simple memory cards */
291static int checksum(struct pcmcia_socket *s, struct resource *res)
292{
293 pccard_mem_map map;
294 int i, a = 0, b = -1, d;
295 void __iomem *virt;
296
297 virt = ioremap(res->start, s->map_size);
298 if (virt) {
299 map.map = 0;
300 map.flags = MAP_ACTIVE;
301 map.speed = 0;
302 map.res = res;
303 map.card_start = 0;
304 s->ops->set_mem_map(s, &map);
305
306 /* Don't bother checking every word... */
307 for (i = 0; i < s->map_size; i += 44) {
308 d = readl(virt+i);
309 a += d;
310 b &= d;
311 }
312
313 map.flags = 0;
314 s->ops->set_mem_map(s, &map);
315
316 iounmap(virt);
317 }
318
319 return (b == -1) ? -1 : (a>>1);
320}
321
322static int
323cis_readable(struct pcmcia_socket *s, unsigned long base, unsigned long size)
324{
325 struct resource *res1, *res2;
Dominik Brodowskic5081d52008-06-19 20:12:34 +0200326 unsigned int info1, info2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 int ret = 0;
328
329 res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "cs memory probe");
330 res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM, "cs memory probe");
331
332 if (res1 && res2) {
333 ret = readable(s, res1, &info1);
334 ret += readable(s, res2, &info2);
335 }
336
337 free_region(res2);
338 free_region(res1);
339
Dominik Brodowskic5081d52008-06-19 20:12:34 +0200340 return (ret == 2) && (info1 == info2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341}
342
343static int
344checksum_match(struct pcmcia_socket *s, unsigned long base, unsigned long size)
345{
346 struct resource *res1, *res2;
347 int a = -1, b = -1;
348
349 res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "cs memory probe");
350 res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM, "cs memory probe");
351
352 if (res1 && res2) {
353 a = checksum(s, res1);
354 b = checksum(s, res2);
355 }
356
357 free_region(res2);
358 free_region(res1);
359
360 return (a == b) && (a >= 0);
361}
362
363/*======================================================================
364
365 The memory probe. If the memory list includes a 64K-aligned block
366 below 1MB, we probe in 64K chunks, and as soon as we accumulate at
367 least mem_limit free space, we quit.
368
369======================================================================*/
370
371static int do_mem_probe(u_long base, u_long num, struct pcmcia_socket *s)
372{
373 struct socket_data *s_data = s->resource_data;
374 u_long i, j, bad, fail, step;
375
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200376 dev_printk(KERN_INFO, &s->dev, "cs: memory probe 0x%06lx-0x%06lx:",
377 base, base+num-1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 bad = fail = 0;
379 step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff);
Dominik Brodowski2ad0a0a2005-06-27 16:28:58 -0700380 /* don't allow too large steps */
381 if (step > 0x800000)
382 step = 0x800000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 /* cis_readable wants to map 2x map_size */
384 if (step < 2 * s->map_size)
385 step = 2 * s->map_size;
386 for (i = j = base; i < base+num; i = j + step) {
387 if (!fail) {
388 for (j = i; j < base+num; j += step) {
389 if (cis_readable(s, j, step))
390 break;
391 }
392 fail = ((i == base) && (j == base+num));
393 }
394 if (fail) {
395 for (j = i; j < base+num; j += 2*step)
396 if (checksum_match(s, j, step) &&
397 checksum_match(s, j + step, step))
398 break;
399 }
400 if (i != j) {
401 if (!bad) printk(" excluding");
402 printk(" %#05lx-%#05lx", i, j-1);
403 sub_interval(&s_data->mem_db, i, j-i);
404 bad += j-i;
405 }
406 }
407 printk(bad ? "\n" : " clean.\n");
408 return (num - bad);
409}
410
411#ifdef CONFIG_PCMCIA_PROBE
412
413static u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s)
414{
Dominik Brodowskide759142005-09-28 19:41:56 +0200415 struct socket_data *s_data = s->resource_data;
416 u_long ok;
417 if (m == &s_data->mem_db)
418 return 0;
419 ok = inv_probe(m->next, s);
420 if (ok) {
421 if (m->base >= 0x100000)
422 sub_interval(&s_data->mem_db, m->base, m->num);
423 return ok;
424 }
425 if (m->base < 0x100000)
426 return 0;
427 return do_mem_probe(m->base, m->num, s);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428}
429
Dominik Brodowskide759142005-09-28 19:41:56 +0200430static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431{
Dominik Brodowskide759142005-09-28 19:41:56 +0200432 struct resource_map *m, mm;
433 static unsigned char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 };
434 unsigned long b, i, ok = 0;
435 struct socket_data *s_data = s->resource_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436
Dominik Brodowskide759142005-09-28 19:41:56 +0200437 /* We do up to four passes through the list */
438 if (probe_mask & MEM_PROBE_HIGH) {
439 if (inv_probe(s_data->mem_db.next, s) > 0)
440 return 0;
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200441 dev_printk(KERN_NOTICE, &s->dev,
442 "cs: warning: no high memory space available!\n");
Dominik Brodowskide759142005-09-28 19:41:56 +0200443 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 }
Dominik Brodowskide759142005-09-28 19:41:56 +0200445
446 for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
447 mm = *m;
448 /* Only probe < 1 MB */
449 if (mm.base >= 0x100000)
450 continue;
451 if ((mm.base | mm.num) & 0xffff) {
452 ok += do_mem_probe(mm.base, mm.num, s);
453 continue;
454 }
455 /* Special probe for 64K-aligned block */
456 for (i = 0; i < 4; i++) {
457 b = order[i] << 12;
458 if ((b >= mm.base) && (b+0x10000 <= mm.base+mm.num)) {
459 if (ok >= mem_limit)
460 sub_interval(&s_data->mem_db, b, 0x10000);
461 else
462 ok += do_mem_probe(b, 0x10000, s);
463 }
464 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 }
Dominik Brodowskide759142005-09-28 19:41:56 +0200466
467 if (ok > 0)
468 return 0;
469
470 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471}
472
473#else /* CONFIG_PCMCIA_PROBE */
474
Andrew Morton2cff9442005-11-16 21:29:26 -0800475static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476{
477 struct resource_map *m, mm;
478 struct socket_data *s_data = s->resource_data;
Andrew Morton2cff9442005-11-16 21:29:26 -0800479 unsigned long ok = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480
481 for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
482 mm = *m;
Andrew Morton2cff9442005-11-16 21:29:26 -0800483 ok += do_mem_probe(mm.base, mm.num, s);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 }
Andrew Morton2cff9442005-11-16 21:29:26 -0800485 if (ok > 0)
486 return 0;
487 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488}
489
490#endif /* CONFIG_PCMCIA_PROBE */
491
492
493/*
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100494 * Locking note: Must be called with skt_mutex held!
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 */
Dominik Brodowskide759142005-09-28 19:41:56 +0200496static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497{
498 struct socket_data *s_data = s->resource_data;
Dominik Brodowskide759142005-09-28 19:41:56 +0200499 unsigned int probe_mask = MEM_PROBE_LOW;
500 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501
Dominik Brodowskide759142005-09-28 19:41:56 +0200502 if (!probe_mem)
503 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100505 mutex_lock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506
Dominik Brodowskide759142005-09-28 19:41:56 +0200507 if (s->features & SS_CAP_PAGE_REGS)
508 probe_mask = MEM_PROBE_HIGH;
509
510 if (probe_mask & ~s_data->rsrc_mem_probe) {
511 if (s->state & SOCKET_PRESENT)
512 ret = validate_mem(s, probe_mask);
513 if (!ret)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 s_data->rsrc_mem_probe |= probe_mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 }
Dominik Brodowskide759142005-09-28 19:41:56 +0200516
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100517 mutex_unlock(&rsrc_mutex);
Dominik Brodowskide759142005-09-28 19:41:56 +0200518
519 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520}
521
522struct pcmcia_align_data {
523 unsigned long mask;
524 unsigned long offset;
525 struct resource_map *map;
526};
527
528static void
529pcmcia_common_align(void *align_data, struct resource *res,
Greg Kroah-Hartman2427ddd2006-06-12 17:07:52 -0700530 resource_size_t size, resource_size_t align)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531{
532 struct pcmcia_align_data *data = align_data;
Greg Kroah-Hartman2427ddd2006-06-12 17:07:52 -0700533 resource_size_t start;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 /*
535 * Ensure that we have the correct start address
536 */
537 start = (res->start & ~data->mask) + data->offset;
538 if (start < res->start)
539 start += data->mask + 1;
540 res->start = start;
541}
542
543static void
Greg Kroah-Hartman2427ddd2006-06-12 17:07:52 -0700544pcmcia_align(void *align_data, struct resource *res, resource_size_t size,
545 resource_size_t align)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546{
547 struct pcmcia_align_data *data = align_data;
548 struct resource_map *m;
549
550 pcmcia_common_align(data, res, size, align);
551
552 for (m = data->map->next; m != data->map; m = m->next) {
553 unsigned long start = m->base;
554 unsigned long end = m->base + m->num - 1;
555
556 /*
557 * If the lower resources are not available, try aligning
558 * to this entry of the resource database to see if it'll
559 * fit here.
560 */
561 if (res->start < start) {
562 res->start = start;
563 pcmcia_common_align(data, res, size, align);
564 }
565
566 /*
567 * If we're above the area which was passed in, there's
568 * no point proceeding.
569 */
570 if (res->start >= res->end)
571 break;
572
573 if ((res->start + size - 1) <= end)
574 break;
575 }
576
577 /*
578 * If we failed to find something suitable, ensure we fail.
579 */
580 if (m == data->map)
581 res->start = res->end;
582}
583
584/*
585 * Adjust an existing IO region allocation, but making sure that we don't
586 * encroach outside the resources which the user supplied.
587 */
588static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_start,
589 unsigned long r_end, struct pcmcia_socket *s)
590{
591 struct resource_map *m;
592 struct socket_data *s_data = s->resource_data;
593 int ret = -ENOMEM;
594
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100595 mutex_lock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 for (m = s_data->io_db.next; m != &s_data->io_db; m = m->next) {
597 unsigned long start = m->base;
598 unsigned long end = m->base + m->num - 1;
599
600 if (start > r_start || r_end > end)
601 continue;
602
603 ret = adjust_resource(res, r_start, r_end - r_start + 1);
604 break;
605 }
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100606 mutex_unlock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607
608 return ret;
609}
610
611/*======================================================================
612
613 These find ranges of I/O ports or memory addresses that are not
614 currently allocated by other devices.
615
616 The 'align' field should reflect the number of bits of address
617 that need to be preserved from the initial value of *base. It
618 should be a power of two, greater than or equal to 'num'. A value
619 of 0 means that all bits of *base are significant. *base should
620 also be strictly less than 'align'.
621
622======================================================================*/
623
Dominik Brodowskie94e15f2005-06-27 16:28:15 -0700624static struct resource *nonstatic_find_io_region(unsigned long base, int num,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 unsigned long align, struct pcmcia_socket *s)
626{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200627 struct resource *res = make_resource(0, num, IORESOURCE_IO, s->dev.bus_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 struct socket_data *s_data = s->resource_data;
629 struct pcmcia_align_data data;
630 unsigned long min = base;
631 int ret;
632
633 if (align == 0)
634 align = 0x10000;
635
636 data.mask = align - 1;
637 data.offset = base & data.mask;
638 data.map = &s_data->io_db;
639
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100640 mutex_lock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641#ifdef CONFIG_PCI
642 if (s->cb_dev) {
643 ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1,
644 min, 0, pcmcia_align, &data);
645 } else
646#endif
647 ret = allocate_resource(&ioport_resource, res, num, min, ~0UL,
648 1, pcmcia_align, &data);
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100649 mutex_unlock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650
651 if (ret != 0) {
652 kfree(res);
653 res = NULL;
654 }
655 return res;
656}
657
Dominik Brodowskie94e15f2005-06-27 16:28:15 -0700658static struct resource * nonstatic_find_mem_region(u_long base, u_long num,
659 u_long align, int low, struct pcmcia_socket *s)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200661 struct resource *res = make_resource(0, num, IORESOURCE_MEM, s->dev.bus_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 struct socket_data *s_data = s->resource_data;
663 struct pcmcia_align_data data;
664 unsigned long min, max;
665 int ret, i;
666
667 low = low || !(s->features & SS_CAP_PAGE_REGS);
668
669 data.mask = align - 1;
670 data.offset = base & data.mask;
671 data.map = &s_data->mem_db;
672
673 for (i = 0; i < 2; i++) {
674 if (low) {
675 max = 0x100000UL;
676 min = base < max ? base : 0;
677 } else {
678 max = ~0UL;
679 min = 0x100000UL + base;
680 }
681
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100682 mutex_lock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683#ifdef CONFIG_PCI
684 if (s->cb_dev) {
685 ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num,
686 1, min, 0,
687 pcmcia_align, &data);
688 } else
689#endif
690 ret = allocate_resource(&iomem_resource, res, num, min,
691 max, 1, pcmcia_align, &data);
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100692 mutex_unlock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 if (ret == 0 || low)
694 break;
695 low = 1;
696 }
697
698 if (ret != 0) {
699 kfree(res);
700 res = NULL;
701 }
702 return res;
703}
704
705
Dominik Brodowski22916632005-06-27 16:28:46 -0700706static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 struct socket_data *data = s->resource_data;
Dominik Brodowski22916632005-06-27 16:28:46 -0700709 unsigned long size = end - start + 1;
710 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711
Dominik Brodowski1146bc72005-09-28 19:28:37 +0200712 if (end < start)
Dominik Brodowski22916632005-06-27 16:28:46 -0700713 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100715 mutex_lock(&rsrc_mutex);
Dominik Brodowski22916632005-06-27 16:28:46 -0700716 switch (action) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717 case ADD_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700718 ret = add_interval(&data->mem_db, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 break;
720 case REMOVE_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700721 ret = sub_interval(&data->mem_db, start, size);
722 if (!ret) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 struct pcmcia_socket *socket;
724 down_read(&pcmcia_socket_list_rwsem);
725 list_for_each_entry(socket, &pcmcia_socket_list, socket_list)
726 release_cis_mem(socket);
727 up_read(&pcmcia_socket_list_rwsem);
728 }
729 break;
730 default:
Dominik Brodowski22916632005-06-27 16:28:46 -0700731 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 }
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100733 mutex_unlock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734
735 return ret;
736}
737
738
Dominik Brodowski22916632005-06-27 16:28:46 -0700739static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740{
741 struct socket_data *data = s->resource_data;
Dominik Brodowski22916632005-06-27 16:28:46 -0700742 unsigned long size = end - start + 1;
743 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744
Dominik Brodowski1146bc72005-09-28 19:28:37 +0200745 if (end < start)
Dominik Brodowski22916632005-06-27 16:28:46 -0700746 return -EINVAL;
747
748 if (end > IO_SPACE_LIMIT)
749 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100751 mutex_lock(&rsrc_mutex);
Dominik Brodowski22916632005-06-27 16:28:46 -0700752 switch (action) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753 case ADD_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700754 if (add_interval(&data->io_db, start, size) != 0) {
755 ret = -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 break;
757 }
758#ifdef CONFIG_PCMCIA_PROBE
759 if (probe_io)
Dominik Brodowski22916632005-06-27 16:28:46 -0700760 do_io_probe(s, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761#endif
762 break;
763 case REMOVE_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700764 sub_interval(&data->io_db, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 break;
766 default:
Dominik Brodowski22916632005-06-27 16:28:46 -0700767 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 break;
769 }
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100770 mutex_unlock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771
772 return ret;
773}
774
775
Dominik Brodowski3c299762005-06-27 16:28:46 -0700776#ifdef CONFIG_PCI
777static int nonstatic_autoadd_resources(struct pcmcia_socket *s)
778{
779 struct resource *res;
780 int i, done = 0;
781
782 if (!s->cb_dev || !s->cb_dev->bus)
783 return -ENODEV;
784
Brian Gerst0d078f62005-10-30 14:59:20 -0800785#if defined(CONFIG_X86)
Dominik Brodowskib6d00f02005-06-27 16:29:02 -0700786 /* If this is the root bus, the risk of hitting
787 * some strange system devices which aren't protected
788 * by either ACPI resource tables or properly requested
789 * resources is too big. Therefore, don't do auto-adding
790 * of resources at the moment.
791 */
792 if (s->cb_dev->bus->number == 0)
793 return -EINVAL;
794#endif
795
Dominik Brodowski3c299762005-06-27 16:28:46 -0700796 for (i=0; i < PCI_BUS_NUM_RESOURCES; i++) {
797 res = s->cb_dev->bus->resource[i];
798 if (!res)
799 continue;
800
801 if (res->flags & IORESOURCE_IO) {
802 if (res == &ioport_resource)
803 continue;
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200804 dev_printk(KERN_INFO, &s->cb_dev->dev,
805 "pcmcia: parent PCI bridge I/O "
806 "window: 0x%llx - 0x%llx\n",
807 (unsigned long long)res->start,
808 (unsigned long long)res->end);
Dominik Brodowski3c299762005-06-27 16:28:46 -0700809 if (!adjust_io(s, ADD_MANAGED_RESOURCE, res->start, res->end))
810 done |= IORESOURCE_IO;
811
812 }
813
814 if (res->flags & IORESOURCE_MEM) {
815 if (res == &iomem_resource)
816 continue;
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200817 dev_printk(KERN_INFO, &s->cb_dev->dev,
818 "pcmcia: parent PCI bridge Memory "
819 "window: 0x%llx - 0x%llx\n",
820 (unsigned long long)res->start,
821 (unsigned long long)res->end);
Dominik Brodowski3c299762005-06-27 16:28:46 -0700822 if (!adjust_memory(s, ADD_MANAGED_RESOURCE, res->start, res->end))
823 done |= IORESOURCE_MEM;
824 }
825 }
826
827 /* if we got at least one of IO, and one of MEM, we can be glad and
828 * activate the PCMCIA subsystem */
Dominik Brodowski54bb5672005-09-28 19:29:59 +0200829 if (done == (IORESOURCE_MEM | IORESOURCE_IO))
Dominik Brodowski3c299762005-06-27 16:28:46 -0700830 s->resource_setup_done = 1;
831
832 return 0;
833}
834
835#else
836
837static inline int nonstatic_autoadd_resources(struct pcmcia_socket *s)
838{
839 return -ENODEV;
840}
841
842#endif
843
844
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845static int nonstatic_init(struct pcmcia_socket *s)
846{
847 struct socket_data *data;
848
Dominik Brodowski8084b372005-12-11 21:18:26 +0100849 data = kzalloc(sizeof(struct socket_data), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 if (!data)
851 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852
853 data->mem_db.next = &data->mem_db;
854 data->io_db.next = &data->io_db;
855
856 s->resource_data = (void *) data;
857
Dominik Brodowski3c299762005-06-27 16:28:46 -0700858 nonstatic_autoadd_resources(s);
859
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 return 0;
861}
862
863static void nonstatic_release_resource_db(struct pcmcia_socket *s)
864{
865 struct socket_data *data = s->resource_data;
866 struct resource_map *p, *q;
867
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100868 mutex_lock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869 for (p = data->mem_db.next; p != &data->mem_db; p = q) {
870 q = p->next;
871 kfree(p);
872 }
873 for (p = data->io_db.next; p != &data->io_db; p = q) {
874 q = p->next;
875 kfree(p);
876 }
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100877 mutex_unlock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878}
879
880
881struct pccard_resource_ops pccard_nonstatic_ops = {
882 .validate_mem = pcmcia_nonstatic_validate_mem,
883 .adjust_io_region = nonstatic_adjust_io_region,
884 .find_io = nonstatic_find_io_region,
885 .find_mem = nonstatic_find_mem_region,
Dominik Brodowskic5023802008-06-19 19:02:52 +0200886 .add_io = adjust_io,
887 .add_mem = adjust_memory,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 .init = nonstatic_init,
889 .exit = nonstatic_release_resource_db,
890};
891EXPORT_SYMBOL(pccard_nonstatic_ops);
892
893
894/* sysfs interface to the resource database */
895
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200896static ssize_t show_io_db(struct device *dev,
897 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200899 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900 struct socket_data *data;
901 struct resource_map *p;
902 ssize_t ret = 0;
903
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100904 mutex_lock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905 data = s->resource_data;
906
907 for (p = data->io_db.next; p != &data->io_db; p = p->next) {
908 if (ret > (PAGE_SIZE - 10))
909 continue;
910 ret += snprintf (&buf[ret], (PAGE_SIZE - ret - 1),
911 "0x%08lx - 0x%08lx\n",
912 ((unsigned long) p->base),
913 ((unsigned long) p->base + p->num - 1));
914 }
915
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100916 mutex_unlock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 return (ret);
918}
919
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200920static ssize_t store_io_db(struct device *dev,
921 struct device_attribute *attr,
922 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200924 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925 unsigned long start_addr, end_addr;
Dominik Brodowski22916632005-06-27 16:28:46 -0700926 unsigned int add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927 ssize_t ret = 0;
928
929 ret = sscanf (buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
930 if (ret != 2) {
931 ret = sscanf (buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -0700932 add = REMOVE_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933 if (ret != 2) {
934 ret = sscanf (buf, "0x%lx - 0x%lx", &start_addr, &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -0700935 add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 if (ret != 2)
937 return -EINVAL;
938 }
939 }
Dominik Brodowski1146bc72005-09-28 19:28:37 +0200940 if (end_addr < start_addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941 return -EINVAL;
942
Dominik Brodowski22916632005-06-27 16:28:46 -0700943 ret = adjust_io(s, add, start_addr, end_addr);
Dominik Brodowski3c299762005-06-27 16:28:46 -0700944 if (!ret)
945 s->resource_setup_new = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946
947 return ret ? ret : count;
948}
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200949static DEVICE_ATTR(available_resources_io, 0600, show_io_db, store_io_db);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200951static ssize_t show_mem_db(struct device *dev,
952 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200954 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955 struct socket_data *data;
956 struct resource_map *p;
957 ssize_t ret = 0;
958
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100959 mutex_lock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 data = s->resource_data;
961
962 for (p = data->mem_db.next; p != &data->mem_db; p = p->next) {
963 if (ret > (PAGE_SIZE - 10))
964 continue;
965 ret += snprintf (&buf[ret], (PAGE_SIZE - ret - 1),
966 "0x%08lx - 0x%08lx\n",
967 ((unsigned long) p->base),
968 ((unsigned long) p->base + p->num - 1));
969 }
970
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100971 mutex_unlock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972 return (ret);
973}
974
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200975static ssize_t store_mem_db(struct device *dev,
976 struct device_attribute *attr,
977 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200979 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 unsigned long start_addr, end_addr;
Dominik Brodowski22916632005-06-27 16:28:46 -0700981 unsigned int add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982 ssize_t ret = 0;
983
984 ret = sscanf (buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
985 if (ret != 2) {
986 ret = sscanf (buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -0700987 add = REMOVE_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988 if (ret != 2) {
989 ret = sscanf (buf, "0x%lx - 0x%lx", &start_addr, &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -0700990 add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 if (ret != 2)
992 return -EINVAL;
993 }
994 }
Dominik Brodowski1146bc72005-09-28 19:28:37 +0200995 if (end_addr < start_addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996 return -EINVAL;
997
Dominik Brodowski22916632005-06-27 16:28:46 -0700998 ret = adjust_memory(s, add, start_addr, end_addr);
Dominik Brodowski3c299762005-06-27 16:28:46 -0700999 if (!ret)
1000 s->resource_setup_new = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001
1002 return ret ? ret : count;
1003}
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001004static DEVICE_ATTR(available_resources_mem, 0600, show_mem_db, store_mem_db);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005
David Brownell7d578962008-06-12 12:13:55 -07001006static struct attribute *pccard_rsrc_attributes[] = {
1007 &dev_attr_available_resources_io.attr,
1008 &dev_attr_available_resources_mem.attr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 NULL,
1010};
1011
David Brownell7d578962008-06-12 12:13:55 -07001012static const struct attribute_group rsrc_attributes = {
1013 .attrs = pccard_rsrc_attributes,
1014};
1015
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001016static int __devinit pccard_sysfs_add_rsrc(struct device *dev,
Dmitry Torokhovd8539d82005-09-15 02:01:36 -05001017 struct class_interface *class_intf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001019 struct pcmcia_socket *s = dev_get_drvdata(dev);
David Brownell7d578962008-06-12 12:13:55 -07001020
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 if (s->resource_ops != &pccard_nonstatic_ops)
1022 return 0;
David Brownell7d578962008-06-12 12:13:55 -07001023 return sysfs_create_group(&dev->kobj, &rsrc_attributes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024}
1025
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001026static void __devexit pccard_sysfs_remove_rsrc(struct device *dev,
Dmitry Torokhovd8539d82005-09-15 02:01:36 -05001027 struct class_interface *class_intf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001029 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030
1031 if (s->resource_ops != &pccard_nonstatic_ops)
1032 return;
David Brownell7d578962008-06-12 12:13:55 -07001033 sysfs_remove_group(&dev->kobj, &rsrc_attributes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034}
1035
Sam Ravnborged49f5d2008-05-01 04:34:50 -07001036static struct class_interface pccard_rsrc_interface __refdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 .class = &pcmcia_socket_class,
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001038 .add_dev = &pccard_sysfs_add_rsrc,
1039 .remove_dev = __devexit_p(&pccard_sysfs_remove_rsrc),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040};
1041
1042static int __init nonstatic_sysfs_init(void)
1043{
1044 return class_interface_register(&pccard_rsrc_interface);
1045}
1046
1047static void __exit nonstatic_sysfs_exit(void)
1048{
1049 class_interface_unregister(&pccard_rsrc_interface);
1050}
1051
1052module_init(nonstatic_sysfs_init);
1053module_exit(nonstatic_sysfs_exit);