blob: 120d5ad99296d154f1e1a8caa326bcb334415e0d [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>
Dominik Brodowski9fea84f2009-12-07 22:11:45 +010027#include <linux/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
29#include <asm/irq.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030
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 *
Kay Sievers25096982008-11-01 11:46:06 +010074make_resource(resource_size_t b, resource_size_t n, int flags, const 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{
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100147 struct resource_map *p, *q;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100149 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),
172 GFP_KERNEL);
173 if (!p) {
174 printk(KERN_WARNING "out of memory to update resources\n");
175 return -ENOMEM;
176 }
177 p->base = base+num;
178 p->num = q->base+q->num - p->base;
179 q->num = base - q->base;
180 p->next = q->next ; q->next = p;
181 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100184 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185}
186
187/*======================================================================
188
189 These routines examine a region of IO or memory addresses to
190 determine what ranges might be genuinely available.
191
192======================================================================*/
193
194#ifdef CONFIG_PCMCIA_PROBE
Olof Johansson906da802008-02-04 22:27:35 -0800195static void do_io_probe(struct pcmcia_socket *s, unsigned int base,
196 unsigned int num)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197{
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100198 struct resource *res;
199 struct socket_data *s_data = s->resource_data;
200 unsigned int i, j, bad;
201 int any;
202 u_char *b, hole, most;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100204 dev_printk(KERN_INFO, &s->dev, "cs: IO port probe %#x-%#x:",
205 base, base+num-1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100207 /* First, what does a floating port look like? */
208 b = kzalloc(256, GFP_KERNEL);
209 if (!b) {
210 printk("\n");
211 dev_printk(KERN_ERR, &s->dev,
212 "do_io_probe: unable to kmalloc 256 bytes");
213 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100215 for (i = base, most = 0; i < base+num; i += 8) {
216 res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
217 if (!res)
218 continue;
219 hole = inb(i);
220 for (j = 1; j < 8; j++)
221 if (inb(i+j) != hole)
222 break;
223 free_region(res);
224 if ((j == 8) && (++b[hole] > b[most]))
225 most = hole;
226 if (b[most] == 127)
227 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100229 kfree(b);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100231 bad = any = 0;
232 for (i = base; i < base+num; i += 8) {
233 res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
234 if (!res)
235 continue;
236 for (j = 0; j < 8; j++)
237 if (inb(i+j) != most)
238 break;
239 free_region(res);
240 if (j < 8) {
241 if (!any)
242 printk(" excluding");
243 if (!bad)
244 bad = any = i;
245 } else {
246 if (bad) {
247 sub_interval(&s_data->io_db, bad, i-bad);
248 printk(" %#x-%#x", bad, i-1);
249 bad = 0;
250 }
251 }
252 }
253 if (bad) {
254 if ((num > 16) && (bad == base) && (i == base+num)) {
255 printk(" nothing: probe failed.\n");
256 return;
257 } else {
258 sub_interval(&s_data->io_db, bad, i-bad);
259 printk(" %#x-%#x", bad, i-1);
260 }
261 }
262
263 printk(any ? "\n" : " clean.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264}
265#endif
266
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100267/*======================================================================*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100269/**
270 * readable() - iomem validation function for cards with a valid CIS
271 */
Dominik Brodowskic5081d52008-06-19 20:12:34 +0200272static int readable(struct pcmcia_socket *s, struct resource *res,
273 unsigned int *count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274{
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100275 int ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276
277 s->cis_mem.res = res;
278 s->cis_virt = ioremap(res->start, s->map_size);
279 if (s->cis_virt) {
Dominik Brodowski84897fc2009-10-18 23:51:09 +0200280 ret = pccard_validate_cis(s, count);
Dominik Brodowski904e3772010-01-02 12:28:04 +0100281 /* invalidate mapping */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 iounmap(s->cis_virt);
283 s->cis_virt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 }
285 s->cis_mem.res = NULL;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100286 if ((ret) || (*count == 0))
287 return -EINVAL;
288 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289}
290
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100291/**
292 * checksum() - iomem validation function for simple memory cards
293 */
294static int checksum(struct pcmcia_socket *s, struct resource *res,
295 unsigned int *value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296{
297 pccard_mem_map map;
298 int i, a = 0, b = -1, d;
299 void __iomem *virt;
300
301 virt = ioremap(res->start, s->map_size);
302 if (virt) {
303 map.map = 0;
304 map.flags = MAP_ACTIVE;
305 map.speed = 0;
306 map.res = res;
307 map.card_start = 0;
308 s->ops->set_mem_map(s, &map);
309
310 /* Don't bother checking every word... */
311 for (i = 0; i < s->map_size; i += 44) {
312 d = readl(virt+i);
313 a += d;
314 b &= d;
315 }
316
317 map.flags = 0;
318 s->ops->set_mem_map(s, &map);
319
320 iounmap(virt);
321 }
322
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100323 if (b == -1)
324 return -EINVAL;
325
326 *value = a;
327
328 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329}
330
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100331/**
332 * do_validate_mem() - low level validate a memory region for PCMCIA use
333 * @s: PCMCIA socket to validate
334 * @base: start address of resource to check
335 * @size: size of resource to check
336 * @validate: validation function to use
337 *
338 * do_validate_mem() splits up the memory region which is to be checked
339 * into two parts. Both are passed to the @validate() function. If
340 * @validate() returns non-zero, or the value parameter to @validate()
341 * is zero, or the value parameter is different between both calls,
342 * the check fails, and -EINVAL is returned. Else, 0 is returned.
343 */
344static int do_validate_mem(struct pcmcia_socket *s,
345 unsigned long base, unsigned long size,
346 int validate (struct pcmcia_socket *s,
347 struct resource *res,
348 unsigned int *value))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349{
350 struct resource *res1, *res2;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100351 unsigned int info1 = 1, info2 = 1;
352 int ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100354 res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "PCMCIA memprobe");
355 res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM,
356 "PCMCIA memprobe");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357
358 if (res1 && res2) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100359 ret = 0;
360 if (validate) {
361 ret = validate(s, res1, &info1);
362 ret += validate(s, res2, &info2);
363 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 }
365
366 free_region(res2);
367 free_region(res1);
368
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100369 dev_dbg(&s->dev, "cs: memory probe 0x%06lx-0x%06lx: %p %p %u %u %u",
370 base, base+size-1, res1, res2, ret, info1, info2);
371
372 if ((ret) || (info1 != info2) || (info1 == 0))
373 return -EINVAL;
374
375 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376}
377
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100379/**
380 * do_mem_probe() - validate a memory region for PCMCIA use
381 * @s: PCMCIA socket to validate
382 * @base: start address of resource to check
383 * @num: size of resource to check
384 * @validate: validation function to use
385 * @fallback: validation function to use if validate fails
386 *
387 * do_mem_probe() checks a memory region for use by the PCMCIA subsystem.
388 * To do so, the area is split up into sensible parts, and then passed
389 * into the @validate() function. Only if @validate() and @fallback() fail,
390 * the area is marked as unavaibale for use by the PCMCIA subsystem. The
391 * function returns the size of the usable memory area.
392 */
393static int do_mem_probe(struct pcmcia_socket *s, u_long base, u_long num,
394 int validate (struct pcmcia_socket *s,
395 struct resource *res,
396 unsigned int *value),
397 int fallback (struct pcmcia_socket *s,
398 struct resource *res,
399 unsigned int *value))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400{
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100401 struct socket_data *s_data = s->resource_data;
402 u_long i, j, bad, fail, step;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100404 dev_printk(KERN_INFO, &s->dev, "cs: memory probe 0x%06lx-0x%06lx:",
405 base, base+num-1);
406 bad = fail = 0;
407 step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff);
408 /* don't allow too large steps */
409 if (step > 0x800000)
410 step = 0x800000;
411 /* cis_readable wants to map 2x map_size */
412 if (step < 2 * s->map_size)
413 step = 2 * s->map_size;
414 for (i = j = base; i < base+num; i = j + step) {
415 if (!fail) {
416 for (j = i; j < base+num; j += step) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100417 if (!do_validate_mem(s, j, step, validate))
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100418 break;
419 }
420 fail = ((i == base) && (j == base+num));
421 }
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100422 if ((fail) && (fallback)) {
423 for (j = i; j < base+num; j += step)
424 if (!do_validate_mem(s, j, step, fallback))
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100425 break;
426 }
427 if (i != j) {
428 if (!bad)
429 printk(" excluding");
430 printk(" %#05lx-%#05lx", i, j-1);
431 sub_interval(&s_data->mem_db, i, j-i);
432 bad += j-i;
433 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100435 printk(bad ? "\n" : " clean.\n");
436 return num - bad;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437}
438
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100439
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440#ifdef CONFIG_PCMCIA_PROBE
441
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100442/**
443 * inv_probe() - top-to-bottom search for one usuable high memory area
444 * @s: PCMCIA socket to validate
445 * @m: resource_map to check
446 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447static u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s)
448{
Dominik Brodowskide759142005-09-28 19:41:56 +0200449 struct socket_data *s_data = s->resource_data;
450 u_long ok;
451 if (m == &s_data->mem_db)
452 return 0;
453 ok = inv_probe(m->next, s);
454 if (ok) {
455 if (m->base >= 0x100000)
456 sub_interval(&s_data->mem_db, m->base, m->num);
457 return ok;
458 }
459 if (m->base < 0x100000)
460 return 0;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100461 return do_mem_probe(s, m->base, m->num, readable, checksum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462}
463
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100464/**
465 * validate_mem() - memory probe function
466 * @s: PCMCIA socket to validate
467 * @probe_mask: MEM_PROBE_LOW | MEM_PROBE_HIGH
468 *
469 * The memory probe. If the memory list includes a 64K-aligned block
470 * below 1MB, we probe in 64K chunks, and as soon as we accumulate at
471 * least mem_limit free space, we quit. Returns 0 on usuable ports.
472 */
Dominik Brodowskide759142005-09-28 19:41:56 +0200473static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474{
Dominik Brodowskide759142005-09-28 19:41:56 +0200475 struct resource_map *m, mm;
476 static unsigned char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 };
477 unsigned long b, i, ok = 0;
478 struct socket_data *s_data = s->resource_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479
Dominik Brodowskide759142005-09-28 19:41:56 +0200480 /* We do up to four passes through the list */
481 if (probe_mask & MEM_PROBE_HIGH) {
482 if (inv_probe(s_data->mem_db.next, s) > 0)
483 return 0;
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200484 dev_printk(KERN_NOTICE, &s->dev,
485 "cs: warning: no high memory space available!\n");
Dominik Brodowskide759142005-09-28 19:41:56 +0200486 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487 }
Dominik Brodowskide759142005-09-28 19:41:56 +0200488
489 for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
490 mm = *m;
491 /* Only probe < 1 MB */
492 if (mm.base >= 0x100000)
493 continue;
494 if ((mm.base | mm.num) & 0xffff) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100495 ok += do_mem_probe(s, mm.base, mm.num, readable,
496 checksum);
Dominik Brodowskide759142005-09-28 19:41:56 +0200497 continue;
498 }
499 /* Special probe for 64K-aligned block */
500 for (i = 0; i < 4; i++) {
501 b = order[i] << 12;
502 if ((b >= mm.base) && (b+0x10000 <= mm.base+mm.num)) {
503 if (ok >= mem_limit)
504 sub_interval(&s_data->mem_db, b, 0x10000);
505 else
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100506 ok += do_mem_probe(s, b, 0x10000,
507 readable, checksum);
Dominik Brodowskide759142005-09-28 19:41:56 +0200508 }
509 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 }
Dominik Brodowskide759142005-09-28 19:41:56 +0200511
512 if (ok > 0)
513 return 0;
514
515 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516}
517
518#else /* CONFIG_PCMCIA_PROBE */
519
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100520/**
521 * validate_mem() - memory probe function
522 * @s: PCMCIA socket to validate
523 * @probe_mask: ignored
524 *
525 * Returns 0 on usuable ports.
526 */
Andrew Morton2cff9442005-11-16 21:29:26 -0800527static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528{
529 struct resource_map *m, mm;
530 struct socket_data *s_data = s->resource_data;
Andrew Morton2cff9442005-11-16 21:29:26 -0800531 unsigned long ok = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532
533 for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
534 mm = *m;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100535 ok += do_mem_probe(s, mm.base, mm.num, readable, checksum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 }
Andrew Morton2cff9442005-11-16 21:29:26 -0800537 if (ok > 0)
538 return 0;
539 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540}
541
542#endif /* CONFIG_PCMCIA_PROBE */
543
544
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100545/**
546 * pcmcia_nonstatic_validate_mem() - try to validate iomem for PCMCIA use
547 * @s: PCMCIA socket to validate
548 *
549 * This is tricky... when we set up CIS memory, we try to validate
550 * the memory window space allocations.
551 *
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100552 * Locking note: Must be called with skt_mutex held!
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 */
Dominik Brodowskide759142005-09-28 19:41:56 +0200554static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555{
556 struct socket_data *s_data = s->resource_data;
Dominik Brodowskide759142005-09-28 19:41:56 +0200557 unsigned int probe_mask = MEM_PROBE_LOW;
558 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559
Dominik Brodowskide759142005-09-28 19:41:56 +0200560 if (!probe_mem)
561 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100563 mutex_lock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564
Dominik Brodowskide759142005-09-28 19:41:56 +0200565 if (s->features & SS_CAP_PAGE_REGS)
566 probe_mask = MEM_PROBE_HIGH;
567
568 if (probe_mask & ~s_data->rsrc_mem_probe) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100569 if (s->state & SOCKET_PRESENT) {
Dominik Brodowskide759142005-09-28 19:41:56 +0200570 ret = validate_mem(s, probe_mask);
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100571 if (!ret)
572 s_data->rsrc_mem_probe |= probe_mask;
573 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 }
Dominik Brodowskide759142005-09-28 19:41:56 +0200575
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100576 mutex_unlock(&rsrc_mutex);
Dominik Brodowskide759142005-09-28 19:41:56 +0200577
578 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579}
580
581struct pcmcia_align_data {
582 unsigned long mask;
583 unsigned long offset;
584 struct resource_map *map;
585};
586
587static void
588pcmcia_common_align(void *align_data, struct resource *res,
Greg Kroah-Hartman2427ddd2006-06-12 17:07:52 -0700589 resource_size_t size, resource_size_t align)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590{
591 struct pcmcia_align_data *data = align_data;
Greg Kroah-Hartman2427ddd2006-06-12 17:07:52 -0700592 resource_size_t start;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 /*
594 * Ensure that we have the correct start address
595 */
596 start = (res->start & ~data->mask) + data->offset;
597 if (start < res->start)
598 start += data->mask + 1;
599 res->start = start;
600}
601
602static void
Greg Kroah-Hartman2427ddd2006-06-12 17:07:52 -0700603pcmcia_align(void *align_data, struct resource *res, resource_size_t size,
604 resource_size_t align)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605{
606 struct pcmcia_align_data *data = align_data;
607 struct resource_map *m;
608
609 pcmcia_common_align(data, res, size, align);
610
611 for (m = data->map->next; m != data->map; m = m->next) {
612 unsigned long start = m->base;
613 unsigned long end = m->base + m->num - 1;
614
615 /*
616 * If the lower resources are not available, try aligning
617 * to this entry of the resource database to see if it'll
618 * fit here.
619 */
620 if (res->start < start) {
621 res->start = start;
622 pcmcia_common_align(data, res, size, align);
623 }
624
625 /*
626 * If we're above the area which was passed in, there's
627 * no point proceeding.
628 */
629 if (res->start >= res->end)
630 break;
631
632 if ((res->start + size - 1) <= end)
633 break;
634 }
635
636 /*
637 * If we failed to find something suitable, ensure we fail.
638 */
639 if (m == data->map)
640 res->start = res->end;
641}
642
643/*
644 * Adjust an existing IO region allocation, but making sure that we don't
645 * encroach outside the resources which the user supplied.
646 */
647static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_start,
648 unsigned long r_end, struct pcmcia_socket *s)
649{
650 struct resource_map *m;
651 struct socket_data *s_data = s->resource_data;
652 int ret = -ENOMEM;
653
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100654 mutex_lock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 for (m = s_data->io_db.next; m != &s_data->io_db; m = m->next) {
656 unsigned long start = m->base;
657 unsigned long end = m->base + m->num - 1;
658
659 if (start > r_start || r_end > end)
660 continue;
661
662 ret = adjust_resource(res, r_start, r_end - r_start + 1);
663 break;
664 }
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100665 mutex_unlock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666
667 return ret;
668}
669
670/*======================================================================
671
672 These find ranges of I/O ports or memory addresses that are not
673 currently allocated by other devices.
674
675 The 'align' field should reflect the number of bits of address
676 that need to be preserved from the initial value of *base. It
677 should be a power of two, greater than or equal to 'num'. A value
678 of 0 means that all bits of *base are significant. *base should
679 also be strictly less than 'align'.
680
681======================================================================*/
682
Dominik Brodowskie94e15f2005-06-27 16:28:15 -0700683static struct resource *nonstatic_find_io_region(unsigned long base, int num,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 unsigned long align, struct pcmcia_socket *s)
685{
Kay Sievers25096982008-11-01 11:46:06 +0100686 struct resource *res = make_resource(0, num, IORESOURCE_IO, dev_name(&s->dev));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 struct socket_data *s_data = s->resource_data;
688 struct pcmcia_align_data data;
689 unsigned long min = base;
690 int ret;
691
692 if (align == 0)
693 align = 0x10000;
694
695 data.mask = align - 1;
696 data.offset = base & data.mask;
697 data.map = &s_data->io_db;
698
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100699 mutex_lock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700#ifdef CONFIG_PCI
701 if (s->cb_dev) {
702 ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1,
703 min, 0, pcmcia_align, &data);
704 } else
705#endif
706 ret = allocate_resource(&ioport_resource, res, num, min, ~0UL,
707 1, pcmcia_align, &data);
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100708 mutex_unlock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709
710 if (ret != 0) {
711 kfree(res);
712 res = NULL;
713 }
714 return res;
715}
716
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100717static struct resource *nonstatic_find_mem_region(u_long base, u_long num,
Dominik Brodowskie94e15f2005-06-27 16:28:15 -0700718 u_long align, int low, struct pcmcia_socket *s)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719{
Kay Sievers25096982008-11-01 11:46:06 +0100720 struct resource *res = make_resource(0, num, IORESOURCE_MEM, dev_name(&s->dev));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 struct socket_data *s_data = s->resource_data;
722 struct pcmcia_align_data data;
723 unsigned long min, max;
724 int ret, i;
725
726 low = low || !(s->features & SS_CAP_PAGE_REGS);
727
728 data.mask = align - 1;
729 data.offset = base & data.mask;
730 data.map = &s_data->mem_db;
731
732 for (i = 0; i < 2; i++) {
733 if (low) {
734 max = 0x100000UL;
735 min = base < max ? base : 0;
736 } else {
737 max = ~0UL;
738 min = 0x100000UL + base;
739 }
740
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100741 mutex_lock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742#ifdef CONFIG_PCI
743 if (s->cb_dev) {
744 ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num,
745 1, min, 0,
746 pcmcia_align, &data);
747 } else
748#endif
749 ret = allocate_resource(&iomem_resource, res, num, min,
750 max, 1, pcmcia_align, &data);
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100751 mutex_unlock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 if (ret == 0 || low)
753 break;
754 low = 1;
755 }
756
757 if (ret != 0) {
758 kfree(res);
759 res = NULL;
760 }
761 return res;
762}
763
764
Dominik Brodowski22916632005-06-27 16:28:46 -0700765static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 struct socket_data *data = s->resource_data;
Dominik Brodowski22916632005-06-27 16:28:46 -0700768 unsigned long size = end - start + 1;
769 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770
Dominik Brodowski1146bc72005-09-28 19:28:37 +0200771 if (end < start)
Dominik Brodowski22916632005-06-27 16:28:46 -0700772 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100774 mutex_lock(&rsrc_mutex);
Dominik Brodowski22916632005-06-27 16:28:46 -0700775 switch (action) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 case ADD_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700777 ret = add_interval(&data->mem_db, start, size);
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100778 if (!ret)
779 do_mem_probe(s, start, size, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 break;
781 case REMOVE_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700782 ret = sub_interval(&data->mem_db, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 break;
784 default:
Dominik Brodowski22916632005-06-27 16:28:46 -0700785 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 }
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100787 mutex_unlock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788
789 return ret;
790}
791
792
Dominik Brodowski22916632005-06-27 16:28:46 -0700793static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794{
795 struct socket_data *data = s->resource_data;
Dominik Brodowski22916632005-06-27 16:28:46 -0700796 unsigned long size = end - start + 1;
797 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798
Dominik Brodowski1146bc72005-09-28 19:28:37 +0200799 if (end < start)
Dominik Brodowski22916632005-06-27 16:28:46 -0700800 return -EINVAL;
801
802 if (end > IO_SPACE_LIMIT)
803 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100805 mutex_lock(&rsrc_mutex);
Dominik Brodowski22916632005-06-27 16:28:46 -0700806 switch (action) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 case ADD_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700808 if (add_interval(&data->io_db, start, size) != 0) {
809 ret = -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 break;
811 }
812#ifdef CONFIG_PCMCIA_PROBE
813 if (probe_io)
Dominik Brodowski22916632005-06-27 16:28:46 -0700814 do_io_probe(s, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815#endif
816 break;
817 case REMOVE_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700818 sub_interval(&data->io_db, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 break;
820 default:
Dominik Brodowski22916632005-06-27 16:28:46 -0700821 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 break;
823 }
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100824 mutex_unlock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825
826 return ret;
827}
828
829
Dominik Brodowski3c299762005-06-27 16:28:46 -0700830#ifdef CONFIG_PCI
831static int nonstatic_autoadd_resources(struct pcmcia_socket *s)
832{
833 struct resource *res;
834 int i, done = 0;
835
836 if (!s->cb_dev || !s->cb_dev->bus)
837 return -ENODEV;
838
Brian Gerst0d078f62005-10-30 14:59:20 -0800839#if defined(CONFIG_X86)
Dominik Brodowskib6d00f02005-06-27 16:29:02 -0700840 /* If this is the root bus, the risk of hitting
841 * some strange system devices which aren't protected
842 * by either ACPI resource tables or properly requested
843 * resources is too big. Therefore, don't do auto-adding
844 * of resources at the moment.
845 */
846 if (s->cb_dev->bus->number == 0)
847 return -EINVAL;
848#endif
849
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100850 for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
Dominik Brodowski3c299762005-06-27 16:28:46 -0700851 res = s->cb_dev->bus->resource[i];
852 if (!res)
853 continue;
854
855 if (res->flags & IORESOURCE_IO) {
856 if (res == &ioport_resource)
857 continue;
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200858 dev_printk(KERN_INFO, &s->cb_dev->dev,
859 "pcmcia: parent PCI bridge I/O "
860 "window: 0x%llx - 0x%llx\n",
861 (unsigned long long)res->start,
862 (unsigned long long)res->end);
Dominik Brodowski3c299762005-06-27 16:28:46 -0700863 if (!adjust_io(s, ADD_MANAGED_RESOURCE, res->start, res->end))
864 done |= IORESOURCE_IO;
865
866 }
867
868 if (res->flags & IORESOURCE_MEM) {
869 if (res == &iomem_resource)
870 continue;
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200871 dev_printk(KERN_INFO, &s->cb_dev->dev,
872 "pcmcia: parent PCI bridge Memory "
873 "window: 0x%llx - 0x%llx\n",
874 (unsigned long long)res->start,
875 (unsigned long long)res->end);
Dominik Brodowski3c299762005-06-27 16:28:46 -0700876 if (!adjust_memory(s, ADD_MANAGED_RESOURCE, res->start, res->end))
877 done |= IORESOURCE_MEM;
878 }
879 }
880
881 /* if we got at least one of IO, and one of MEM, we can be glad and
882 * activate the PCMCIA subsystem */
Dominik Brodowski54bb5672005-09-28 19:29:59 +0200883 if (done == (IORESOURCE_MEM | IORESOURCE_IO))
Dominik Brodowski3c299762005-06-27 16:28:46 -0700884 s->resource_setup_done = 1;
885
886 return 0;
887}
888
889#else
890
891static inline int nonstatic_autoadd_resources(struct pcmcia_socket *s)
892{
893 return -ENODEV;
894}
895
896#endif
897
898
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899static int nonstatic_init(struct pcmcia_socket *s)
900{
901 struct socket_data *data;
902
Dominik Brodowski8084b372005-12-11 21:18:26 +0100903 data = kzalloc(sizeof(struct socket_data), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904 if (!data)
905 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906
907 data->mem_db.next = &data->mem_db;
908 data->io_db.next = &data->io_db;
909
910 s->resource_data = (void *) data;
911
Dominik Brodowski3c299762005-06-27 16:28:46 -0700912 nonstatic_autoadd_resources(s);
913
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 return 0;
915}
916
917static void nonstatic_release_resource_db(struct pcmcia_socket *s)
918{
919 struct socket_data *data = s->resource_data;
920 struct resource_map *p, *q;
921
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100922 mutex_lock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923 for (p = data->mem_db.next; p != &data->mem_db; p = q) {
924 q = p->next;
925 kfree(p);
926 }
927 for (p = data->io_db.next; p != &data->io_db; p = q) {
928 q = p->next;
929 kfree(p);
930 }
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100931 mutex_unlock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932}
933
934
935struct pccard_resource_ops pccard_nonstatic_ops = {
936 .validate_mem = pcmcia_nonstatic_validate_mem,
937 .adjust_io_region = nonstatic_adjust_io_region,
938 .find_io = nonstatic_find_io_region,
939 .find_mem = nonstatic_find_mem_region,
Dominik Brodowskic5023802008-06-19 19:02:52 +0200940 .add_io = adjust_io,
941 .add_mem = adjust_memory,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 .init = nonstatic_init,
943 .exit = nonstatic_release_resource_db,
944};
945EXPORT_SYMBOL(pccard_nonstatic_ops);
946
947
948/* sysfs interface to the resource database */
949
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200950static ssize_t show_io_db(struct device *dev,
951 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200953 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954 struct socket_data *data;
955 struct resource_map *p;
956 ssize_t ret = 0;
957
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100958 mutex_lock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 data = s->resource_data;
960
961 for (p = data->io_db.next; p != &data->io_db; p = p->next) {
962 if (ret > (PAGE_SIZE - 10))
963 continue;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100964 ret += snprintf(&buf[ret], (PAGE_SIZE - ret - 1),
965 "0x%08lx - 0x%08lx\n",
966 ((unsigned long) p->base),
967 ((unsigned long) p->base + p->num - 1));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 }
969
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100970 mutex_unlock(&rsrc_mutex);
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100971 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972}
973
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200974static ssize_t store_io_db(struct device *dev,
975 struct device_attribute *attr,
976 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200978 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 unsigned long start_addr, end_addr;
Dominik Brodowski22916632005-06-27 16:28:46 -0700980 unsigned int add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 ssize_t ret = 0;
982
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100983 ret = sscanf(buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100985 ret = sscanf(buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -0700986 add = REMOVE_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100988 ret = sscanf(buf, "0x%lx - 0x%lx", &start_addr,
989 &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_io(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_io, 0600, show_io_db, store_io_db);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001006static ssize_t show_mem_db(struct device *dev,
1007 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001009 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 struct socket_data *data;
1011 struct resource_map *p;
1012 ssize_t ret = 0;
1013
Dominik Brodowski7fe908d2006-01-10 21:20:36 +01001014 mutex_lock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015 data = s->resource_data;
1016
1017 for (p = data->mem_db.next; p != &data->mem_db; p = p->next) {
1018 if (ret > (PAGE_SIZE - 10))
1019 continue;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001020 ret += snprintf(&buf[ret], (PAGE_SIZE - ret - 1),
1021 "0x%08lx - 0x%08lx\n",
1022 ((unsigned long) p->base),
1023 ((unsigned long) p->base + p->num - 1));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024 }
1025
Dominik Brodowski7fe908d2006-01-10 21:20:36 +01001026 mutex_unlock(&rsrc_mutex);
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001027 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028}
1029
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001030static ssize_t store_mem_db(struct device *dev,
1031 struct device_attribute *attr,
1032 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001034 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035 unsigned long start_addr, end_addr;
Dominik Brodowski22916632005-06-27 16:28:46 -07001036 unsigned int add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 ssize_t ret = 0;
1038
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001039 ret = sscanf(buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001041 ret = sscanf(buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -07001042 add = REMOVE_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001044 ret = sscanf(buf, "0x%lx - 0x%lx", &start_addr,
1045 &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -07001046 add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 if (ret != 2)
1048 return -EINVAL;
1049 }
1050 }
Dominik Brodowski1146bc72005-09-28 19:28:37 +02001051 if (end_addr < start_addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 return -EINVAL;
1053
Dominik Brodowski22916632005-06-27 16:28:46 -07001054 ret = adjust_memory(s, add, start_addr, end_addr);
Dominik Brodowski3c299762005-06-27 16:28:46 -07001055 if (!ret)
1056 s->resource_setup_new = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057
1058 return ret ? ret : count;
1059}
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001060static DEVICE_ATTR(available_resources_mem, 0600, show_mem_db, store_mem_db);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061
David Brownell7d578962008-06-12 12:13:55 -07001062static struct attribute *pccard_rsrc_attributes[] = {
1063 &dev_attr_available_resources_io.attr,
1064 &dev_attr_available_resources_mem.attr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 NULL,
1066};
1067
David Brownell7d578962008-06-12 12:13:55 -07001068static const struct attribute_group rsrc_attributes = {
1069 .attrs = pccard_rsrc_attributes,
1070};
1071
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001072static int __devinit pccard_sysfs_add_rsrc(struct device *dev,
Dmitry Torokhovd8539d82005-09-15 02:01:36 -05001073 struct class_interface *class_intf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001075 struct pcmcia_socket *s = dev_get_drvdata(dev);
David Brownell7d578962008-06-12 12:13:55 -07001076
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 if (s->resource_ops != &pccard_nonstatic_ops)
1078 return 0;
David Brownell7d578962008-06-12 12:13:55 -07001079 return sysfs_create_group(&dev->kobj, &rsrc_attributes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080}
1081
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001082static void __devexit pccard_sysfs_remove_rsrc(struct device *dev,
Dmitry Torokhovd8539d82005-09-15 02:01:36 -05001083 struct class_interface *class_intf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001085 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086
1087 if (s->resource_ops != &pccard_nonstatic_ops)
1088 return;
David Brownell7d578962008-06-12 12:13:55 -07001089 sysfs_remove_group(&dev->kobj, &rsrc_attributes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090}
1091
Sam Ravnborged49f5d2008-05-01 04:34:50 -07001092static struct class_interface pccard_rsrc_interface __refdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 .class = &pcmcia_socket_class,
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001094 .add_dev = &pccard_sysfs_add_rsrc,
1095 .remove_dev = __devexit_p(&pccard_sysfs_remove_rsrc),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096};
1097
1098static int __init nonstatic_sysfs_init(void)
1099{
1100 return class_interface_register(&pccard_rsrc_interface);
1101}
1102
1103static void __exit nonstatic_sysfs_exit(void)
1104{
1105 class_interface_unregister(&pccard_rsrc_interface);
1106}
1107
1108module_init(nonstatic_sysfs_init);
1109module_exit(nonstatic_sysfs_exit);