blob: 91626c17f97b06efb76dfa5cfd4f493c7a70c901 [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 Brodowski6e7b51a2010-01-06 13:57:43 +0100280 /* as we're only called from pcmcia.c, we're safe */
281 if (s->callback->validate)
282 ret = s->callback->validate(s, count);
Dominik Brodowski904e3772010-01-02 12:28:04 +0100283 /* invalidate mapping */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 iounmap(s->cis_virt);
285 s->cis_virt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 }
287 s->cis_mem.res = NULL;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100288 if ((ret) || (*count == 0))
289 return -EINVAL;
290 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291}
292
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100293/**
294 * checksum() - iomem validation function for simple memory cards
295 */
296static int checksum(struct pcmcia_socket *s, struct resource *res,
297 unsigned int *value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298{
299 pccard_mem_map map;
300 int i, a = 0, b = -1, d;
301 void __iomem *virt;
302
303 virt = ioremap(res->start, s->map_size);
304 if (virt) {
305 map.map = 0;
306 map.flags = MAP_ACTIVE;
307 map.speed = 0;
308 map.res = res;
309 map.card_start = 0;
310 s->ops->set_mem_map(s, &map);
311
312 /* Don't bother checking every word... */
313 for (i = 0; i < s->map_size; i += 44) {
314 d = readl(virt+i);
315 a += d;
316 b &= d;
317 }
318
319 map.flags = 0;
320 s->ops->set_mem_map(s, &map);
321
322 iounmap(virt);
323 }
324
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100325 if (b == -1)
326 return -EINVAL;
327
328 *value = a;
329
330 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331}
332
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100333/**
334 * do_validate_mem() - low level validate a memory region for PCMCIA use
335 * @s: PCMCIA socket to validate
336 * @base: start address of resource to check
337 * @size: size of resource to check
338 * @validate: validation function to use
339 *
340 * do_validate_mem() splits up the memory region which is to be checked
341 * into two parts. Both are passed to the @validate() function. If
342 * @validate() returns non-zero, or the value parameter to @validate()
343 * is zero, or the value parameter is different between both calls,
344 * the check fails, and -EINVAL is returned. Else, 0 is returned.
345 */
346static int do_validate_mem(struct pcmcia_socket *s,
347 unsigned long base, unsigned long size,
348 int validate (struct pcmcia_socket *s,
349 struct resource *res,
350 unsigned int *value))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351{
352 struct resource *res1, *res2;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100353 unsigned int info1 = 1, info2 = 1;
354 int ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100356 res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "PCMCIA memprobe");
357 res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM,
358 "PCMCIA memprobe");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359
360 if (res1 && res2) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100361 ret = 0;
362 if (validate) {
363 ret = validate(s, res1, &info1);
364 ret += validate(s, res2, &info2);
365 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 }
367
368 free_region(res2);
369 free_region(res1);
370
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100371 dev_dbg(&s->dev, "cs: memory probe 0x%06lx-0x%06lx: %p %p %u %u %u",
372 base, base+size-1, res1, res2, ret, info1, info2);
373
374 if ((ret) || (info1 != info2) || (info1 == 0))
375 return -EINVAL;
376
377 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378}
379
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100381/**
382 * do_mem_probe() - validate a memory region for PCMCIA use
383 * @s: PCMCIA socket to validate
384 * @base: start address of resource to check
385 * @num: size of resource to check
386 * @validate: validation function to use
387 * @fallback: validation function to use if validate fails
388 *
389 * do_mem_probe() checks a memory region for use by the PCMCIA subsystem.
390 * To do so, the area is split up into sensible parts, and then passed
391 * into the @validate() function. Only if @validate() and @fallback() fail,
392 * the area is marked as unavaibale for use by the PCMCIA subsystem. The
393 * function returns the size of the usable memory area.
394 */
395static int do_mem_probe(struct pcmcia_socket *s, u_long base, u_long num,
396 int validate (struct pcmcia_socket *s,
397 struct resource *res,
398 unsigned int *value),
399 int fallback (struct pcmcia_socket *s,
400 struct resource *res,
401 unsigned int *value))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402{
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100403 struct socket_data *s_data = s->resource_data;
404 u_long i, j, bad, fail, step;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100406 dev_printk(KERN_INFO, &s->dev, "cs: memory probe 0x%06lx-0x%06lx:",
407 base, base+num-1);
408 bad = fail = 0;
409 step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff);
410 /* don't allow too large steps */
411 if (step > 0x800000)
412 step = 0x800000;
413 /* cis_readable wants to map 2x map_size */
414 if (step < 2 * s->map_size)
415 step = 2 * s->map_size;
416 for (i = j = base; i < base+num; i = j + step) {
417 if (!fail) {
418 for (j = i; j < base+num; j += step) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100419 if (!do_validate_mem(s, j, step, validate))
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100420 break;
421 }
422 fail = ((i == base) && (j == base+num));
423 }
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100424 if ((fail) && (fallback)) {
425 for (j = i; j < base+num; j += step)
426 if (!do_validate_mem(s, j, step, fallback))
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100427 break;
428 }
429 if (i != j) {
430 if (!bad)
431 printk(" excluding");
432 printk(" %#05lx-%#05lx", i, j-1);
433 sub_interval(&s_data->mem_db, i, j-i);
434 bad += j-i;
435 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100437 printk(bad ? "\n" : " clean.\n");
438 return num - bad;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439}
440
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100441
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442#ifdef CONFIG_PCMCIA_PROBE
443
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100444/**
445 * inv_probe() - top-to-bottom search for one usuable high memory area
446 * @s: PCMCIA socket to validate
447 * @m: resource_map to check
448 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449static u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s)
450{
Dominik Brodowskide759142005-09-28 19:41:56 +0200451 struct socket_data *s_data = s->resource_data;
452 u_long ok;
453 if (m == &s_data->mem_db)
454 return 0;
455 ok = inv_probe(m->next, s);
456 if (ok) {
457 if (m->base >= 0x100000)
458 sub_interval(&s_data->mem_db, m->base, m->num);
459 return ok;
460 }
461 if (m->base < 0x100000)
462 return 0;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100463 return do_mem_probe(s, m->base, m->num, readable, checksum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464}
465
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100466/**
467 * validate_mem() - memory probe function
468 * @s: PCMCIA socket to validate
469 * @probe_mask: MEM_PROBE_LOW | MEM_PROBE_HIGH
470 *
471 * The memory probe. If the memory list includes a 64K-aligned block
472 * below 1MB, we probe in 64K chunks, and as soon as we accumulate at
473 * least mem_limit free space, we quit. Returns 0 on usuable ports.
474 */
Dominik Brodowskide759142005-09-28 19:41:56 +0200475static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476{
Dominik Brodowskide759142005-09-28 19:41:56 +0200477 struct resource_map *m, mm;
478 static unsigned char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 };
479 unsigned long b, i, ok = 0;
480 struct socket_data *s_data = s->resource_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481
Dominik Brodowskide759142005-09-28 19:41:56 +0200482 /* We do up to four passes through the list */
483 if (probe_mask & MEM_PROBE_HIGH) {
484 if (inv_probe(s_data->mem_db.next, s) > 0)
485 return 0;
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200486 dev_printk(KERN_NOTICE, &s->dev,
487 "cs: warning: no high memory space available!\n");
Dominik Brodowskide759142005-09-28 19:41:56 +0200488 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 }
Dominik Brodowskide759142005-09-28 19:41:56 +0200490
491 for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
492 mm = *m;
493 /* Only probe < 1 MB */
494 if (mm.base >= 0x100000)
495 continue;
496 if ((mm.base | mm.num) & 0xffff) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100497 ok += do_mem_probe(s, mm.base, mm.num, readable,
498 checksum);
Dominik Brodowskide759142005-09-28 19:41:56 +0200499 continue;
500 }
501 /* Special probe for 64K-aligned block */
502 for (i = 0; i < 4; i++) {
503 b = order[i] << 12;
504 if ((b >= mm.base) && (b+0x10000 <= mm.base+mm.num)) {
505 if (ok >= mem_limit)
506 sub_interval(&s_data->mem_db, b, 0x10000);
507 else
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100508 ok += do_mem_probe(s, b, 0x10000,
509 readable, checksum);
Dominik Brodowskide759142005-09-28 19:41:56 +0200510 }
511 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 }
Dominik Brodowskide759142005-09-28 19:41:56 +0200513
514 if (ok > 0)
515 return 0;
516
517 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518}
519
520#else /* CONFIG_PCMCIA_PROBE */
521
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100522/**
523 * validate_mem() - memory probe function
524 * @s: PCMCIA socket to validate
525 * @probe_mask: ignored
526 *
527 * Returns 0 on usuable ports.
528 */
Andrew Morton2cff9442005-11-16 21:29:26 -0800529static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530{
531 struct resource_map *m, mm;
532 struct socket_data *s_data = s->resource_data;
Andrew Morton2cff9442005-11-16 21:29:26 -0800533 unsigned long ok = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534
535 for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
536 mm = *m;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100537 ok += do_mem_probe(s, mm.base, mm.num, readable, checksum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 }
Andrew Morton2cff9442005-11-16 21:29:26 -0800539 if (ok > 0)
540 return 0;
541 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542}
543
544#endif /* CONFIG_PCMCIA_PROBE */
545
546
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100547/**
548 * pcmcia_nonstatic_validate_mem() - try to validate iomem for PCMCIA use
549 * @s: PCMCIA socket to validate
550 *
551 * This is tricky... when we set up CIS memory, we try to validate
552 * the memory window space allocations.
553 *
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100554 * Locking note: Must be called with skt_mutex held!
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 */
Dominik Brodowskide759142005-09-28 19:41:56 +0200556static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557{
558 struct socket_data *s_data = s->resource_data;
Dominik Brodowskide759142005-09-28 19:41:56 +0200559 unsigned int probe_mask = MEM_PROBE_LOW;
560 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561
Dominik Brodowskide759142005-09-28 19:41:56 +0200562 if (!probe_mem)
563 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100565 mutex_lock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566
Dominik Brodowskide759142005-09-28 19:41:56 +0200567 if (s->features & SS_CAP_PAGE_REGS)
568 probe_mask = MEM_PROBE_HIGH;
569
570 if (probe_mask & ~s_data->rsrc_mem_probe) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100571 if (s->state & SOCKET_PRESENT) {
Dominik Brodowskide759142005-09-28 19:41:56 +0200572 ret = validate_mem(s, probe_mask);
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100573 if (!ret)
574 s_data->rsrc_mem_probe |= probe_mask;
575 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 }
Dominik Brodowskide759142005-09-28 19:41:56 +0200577
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100578 mutex_unlock(&rsrc_mutex);
Dominik Brodowskide759142005-09-28 19:41:56 +0200579
580 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581}
582
583struct pcmcia_align_data {
584 unsigned long mask;
585 unsigned long offset;
586 struct resource_map *map;
587};
588
589static void
590pcmcia_common_align(void *align_data, struct resource *res,
Greg Kroah-Hartman2427ddd2006-06-12 17:07:52 -0700591 resource_size_t size, resource_size_t align)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592{
593 struct pcmcia_align_data *data = align_data;
Greg Kroah-Hartman2427ddd2006-06-12 17:07:52 -0700594 resource_size_t start;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 /*
596 * Ensure that we have the correct start address
597 */
598 start = (res->start & ~data->mask) + data->offset;
599 if (start < res->start)
600 start += data->mask + 1;
601 res->start = start;
602}
603
604static void
Greg Kroah-Hartman2427ddd2006-06-12 17:07:52 -0700605pcmcia_align(void *align_data, struct resource *res, resource_size_t size,
606 resource_size_t align)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607{
608 struct pcmcia_align_data *data = align_data;
609 struct resource_map *m;
610
611 pcmcia_common_align(data, res, size, align);
612
613 for (m = data->map->next; m != data->map; m = m->next) {
614 unsigned long start = m->base;
615 unsigned long end = m->base + m->num - 1;
616
617 /*
618 * If the lower resources are not available, try aligning
619 * to this entry of the resource database to see if it'll
620 * fit here.
621 */
622 if (res->start < start) {
623 res->start = start;
624 pcmcia_common_align(data, res, size, align);
625 }
626
627 /*
628 * If we're above the area which was passed in, there's
629 * no point proceeding.
630 */
631 if (res->start >= res->end)
632 break;
633
634 if ((res->start + size - 1) <= end)
635 break;
636 }
637
638 /*
639 * If we failed to find something suitable, ensure we fail.
640 */
641 if (m == data->map)
642 res->start = res->end;
643}
644
645/*
646 * Adjust an existing IO region allocation, but making sure that we don't
647 * encroach outside the resources which the user supplied.
648 */
649static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_start,
650 unsigned long r_end, struct pcmcia_socket *s)
651{
652 struct resource_map *m;
653 struct socket_data *s_data = s->resource_data;
654 int ret = -ENOMEM;
655
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100656 mutex_lock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 for (m = s_data->io_db.next; m != &s_data->io_db; m = m->next) {
658 unsigned long start = m->base;
659 unsigned long end = m->base + m->num - 1;
660
661 if (start > r_start || r_end > end)
662 continue;
663
664 ret = adjust_resource(res, r_start, r_end - r_start + 1);
665 break;
666 }
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100667 mutex_unlock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668
669 return ret;
670}
671
672/*======================================================================
673
674 These find ranges of I/O ports or memory addresses that are not
675 currently allocated by other devices.
676
677 The 'align' field should reflect the number of bits of address
678 that need to be preserved from the initial value of *base. It
679 should be a power of two, greater than or equal to 'num'. A value
680 of 0 means that all bits of *base are significant. *base should
681 also be strictly less than 'align'.
682
683======================================================================*/
684
Dominik Brodowskie94e15f2005-06-27 16:28:15 -0700685static struct resource *nonstatic_find_io_region(unsigned long base, int num,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 unsigned long align, struct pcmcia_socket *s)
687{
Kay Sievers25096982008-11-01 11:46:06 +0100688 struct resource *res = make_resource(0, num, IORESOURCE_IO, dev_name(&s->dev));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689 struct socket_data *s_data = s->resource_data;
690 struct pcmcia_align_data data;
691 unsigned long min = base;
692 int ret;
693
694 if (align == 0)
695 align = 0x10000;
696
697 data.mask = align - 1;
698 data.offset = base & data.mask;
699 data.map = &s_data->io_db;
700
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100701 mutex_lock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702#ifdef CONFIG_PCI
703 if (s->cb_dev) {
704 ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1,
705 min, 0, pcmcia_align, &data);
706 } else
707#endif
708 ret = allocate_resource(&ioport_resource, res, num, min, ~0UL,
709 1, pcmcia_align, &data);
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100710 mutex_unlock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711
712 if (ret != 0) {
713 kfree(res);
714 res = NULL;
715 }
716 return res;
717}
718
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100719static struct resource *nonstatic_find_mem_region(u_long base, u_long num,
Dominik Brodowskie94e15f2005-06-27 16:28:15 -0700720 u_long align, int low, struct pcmcia_socket *s)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721{
Kay Sievers25096982008-11-01 11:46:06 +0100722 struct resource *res = make_resource(0, num, IORESOURCE_MEM, dev_name(&s->dev));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 struct socket_data *s_data = s->resource_data;
724 struct pcmcia_align_data data;
725 unsigned long min, max;
726 int ret, i;
727
728 low = low || !(s->features & SS_CAP_PAGE_REGS);
729
730 data.mask = align - 1;
731 data.offset = base & data.mask;
732 data.map = &s_data->mem_db;
733
734 for (i = 0; i < 2; i++) {
735 if (low) {
736 max = 0x100000UL;
737 min = base < max ? base : 0;
738 } else {
739 max = ~0UL;
740 min = 0x100000UL + base;
741 }
742
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100743 mutex_lock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744#ifdef CONFIG_PCI
745 if (s->cb_dev) {
746 ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num,
747 1, min, 0,
748 pcmcia_align, &data);
749 } else
750#endif
751 ret = allocate_resource(&iomem_resource, res, num, min,
752 max, 1, pcmcia_align, &data);
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100753 mutex_unlock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 if (ret == 0 || low)
755 break;
756 low = 1;
757 }
758
759 if (ret != 0) {
760 kfree(res);
761 res = NULL;
762 }
763 return res;
764}
765
766
Dominik Brodowski22916632005-06-27 16:28:46 -0700767static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 struct socket_data *data = s->resource_data;
Dominik Brodowski22916632005-06-27 16:28:46 -0700770 unsigned long size = end - start + 1;
771 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772
Dominik Brodowski1146bc72005-09-28 19:28:37 +0200773 if (end < start)
Dominik Brodowski22916632005-06-27 16:28:46 -0700774 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100776 mutex_lock(&rsrc_mutex);
Dominik Brodowski22916632005-06-27 16:28:46 -0700777 switch (action) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 case ADD_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700779 ret = add_interval(&data->mem_db, start, size);
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100780 if (!ret)
781 do_mem_probe(s, start, size, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 break;
783 case REMOVE_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700784 ret = sub_interval(&data->mem_db, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 break;
786 default:
Dominik Brodowski22916632005-06-27 16:28:46 -0700787 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 }
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100789 mutex_unlock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
791 return ret;
792}
793
794
Dominik Brodowski22916632005-06-27 16:28:46 -0700795static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796{
797 struct socket_data *data = s->resource_data;
Dominik Brodowski22916632005-06-27 16:28:46 -0700798 unsigned long size = end - start + 1;
799 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800
Dominik Brodowski1146bc72005-09-28 19:28:37 +0200801 if (end < start)
Dominik Brodowski22916632005-06-27 16:28:46 -0700802 return -EINVAL;
803
804 if (end > IO_SPACE_LIMIT)
805 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100807 mutex_lock(&rsrc_mutex);
Dominik Brodowski22916632005-06-27 16:28:46 -0700808 switch (action) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 case ADD_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700810 if (add_interval(&data->io_db, start, size) != 0) {
811 ret = -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 break;
813 }
814#ifdef CONFIG_PCMCIA_PROBE
815 if (probe_io)
Dominik Brodowski22916632005-06-27 16:28:46 -0700816 do_io_probe(s, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817#endif
818 break;
819 case REMOVE_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700820 sub_interval(&data->io_db, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 break;
822 default:
Dominik Brodowski22916632005-06-27 16:28:46 -0700823 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 break;
825 }
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100826 mutex_unlock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827
828 return ret;
829}
830
831
Dominik Brodowski3c299762005-06-27 16:28:46 -0700832#ifdef CONFIG_PCI
833static int nonstatic_autoadd_resources(struct pcmcia_socket *s)
834{
835 struct resource *res;
836 int i, done = 0;
837
838 if (!s->cb_dev || !s->cb_dev->bus)
839 return -ENODEV;
840
Brian Gerst0d078f62005-10-30 14:59:20 -0800841#if defined(CONFIG_X86)
Dominik Brodowskib6d00f02005-06-27 16:29:02 -0700842 /* If this is the root bus, the risk of hitting
843 * some strange system devices which aren't protected
844 * by either ACPI resource tables or properly requested
845 * resources is too big. Therefore, don't do auto-adding
846 * of resources at the moment.
847 */
848 if (s->cb_dev->bus->number == 0)
849 return -EINVAL;
850#endif
851
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100852 for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
Dominik Brodowski3c299762005-06-27 16:28:46 -0700853 res = s->cb_dev->bus->resource[i];
854 if (!res)
855 continue;
856
857 if (res->flags & IORESOURCE_IO) {
858 if (res == &ioport_resource)
859 continue;
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200860 dev_printk(KERN_INFO, &s->cb_dev->dev,
861 "pcmcia: parent PCI bridge I/O "
862 "window: 0x%llx - 0x%llx\n",
863 (unsigned long long)res->start,
864 (unsigned long long)res->end);
Dominik Brodowski3c299762005-06-27 16:28:46 -0700865 if (!adjust_io(s, ADD_MANAGED_RESOURCE, res->start, res->end))
866 done |= IORESOURCE_IO;
867
868 }
869
870 if (res->flags & IORESOURCE_MEM) {
871 if (res == &iomem_resource)
872 continue;
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200873 dev_printk(KERN_INFO, &s->cb_dev->dev,
874 "pcmcia: parent PCI bridge Memory "
875 "window: 0x%llx - 0x%llx\n",
876 (unsigned long long)res->start,
877 (unsigned long long)res->end);
Dominik Brodowski3c299762005-06-27 16:28:46 -0700878 if (!adjust_memory(s, ADD_MANAGED_RESOURCE, res->start, res->end))
879 done |= IORESOURCE_MEM;
880 }
881 }
882
883 /* if we got at least one of IO, and one of MEM, we can be glad and
884 * activate the PCMCIA subsystem */
Dominik Brodowski54bb5672005-09-28 19:29:59 +0200885 if (done == (IORESOURCE_MEM | IORESOURCE_IO))
Dominik Brodowski3c299762005-06-27 16:28:46 -0700886 s->resource_setup_done = 1;
887
888 return 0;
889}
890
891#else
892
893static inline int nonstatic_autoadd_resources(struct pcmcia_socket *s)
894{
895 return -ENODEV;
896}
897
898#endif
899
900
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901static int nonstatic_init(struct pcmcia_socket *s)
902{
903 struct socket_data *data;
904
Dominik Brodowski8084b372005-12-11 21:18:26 +0100905 data = kzalloc(sizeof(struct socket_data), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906 if (!data)
907 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908
909 data->mem_db.next = &data->mem_db;
910 data->io_db.next = &data->io_db;
911
912 s->resource_data = (void *) data;
913
Dominik Brodowski3c299762005-06-27 16:28:46 -0700914 nonstatic_autoadd_resources(s);
915
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916 return 0;
917}
918
919static void nonstatic_release_resource_db(struct pcmcia_socket *s)
920{
921 struct socket_data *data = s->resource_data;
922 struct resource_map *p, *q;
923
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100924 mutex_lock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925 for (p = data->mem_db.next; p != &data->mem_db; p = q) {
926 q = p->next;
927 kfree(p);
928 }
929 for (p = data->io_db.next; p != &data->io_db; p = q) {
930 q = p->next;
931 kfree(p);
932 }
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100933 mutex_unlock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934}
935
936
937struct pccard_resource_ops pccard_nonstatic_ops = {
938 .validate_mem = pcmcia_nonstatic_validate_mem,
939 .adjust_io_region = nonstatic_adjust_io_region,
940 .find_io = nonstatic_find_io_region,
941 .find_mem = nonstatic_find_mem_region,
Dominik Brodowskic5023802008-06-19 19:02:52 +0200942 .add_io = adjust_io,
943 .add_mem = adjust_memory,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 .init = nonstatic_init,
945 .exit = nonstatic_release_resource_db,
946};
947EXPORT_SYMBOL(pccard_nonstatic_ops);
948
949
950/* sysfs interface to the resource database */
951
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200952static ssize_t show_io_db(struct device *dev,
953 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200955 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 struct socket_data *data;
957 struct resource_map *p;
958 ssize_t ret = 0;
959
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100960 mutex_lock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961 data = s->resource_data;
962
963 for (p = data->io_db.next; p != &data->io_db; p = p->next) {
964 if (ret > (PAGE_SIZE - 10))
965 continue;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100966 ret += snprintf(&buf[ret], (PAGE_SIZE - ret - 1),
967 "0x%08lx - 0x%08lx\n",
968 ((unsigned long) p->base),
969 ((unsigned long) p->base + p->num - 1));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970 }
971
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100972 mutex_unlock(&rsrc_mutex);
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100973 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974}
975
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200976static ssize_t store_io_db(struct device *dev,
977 struct device_attribute *attr,
978 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200980 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 unsigned long start_addr, end_addr;
Dominik Brodowski22916632005-06-27 16:28:46 -0700982 unsigned int add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983 ssize_t ret = 0;
984
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100985 ret = sscanf(buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100987 ret = sscanf(buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -0700988 add = REMOVE_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100990 ret = sscanf(buf, "0x%lx - 0x%lx", &start_addr,
991 &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -0700992 add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 if (ret != 2)
994 return -EINVAL;
995 }
996 }
Dominik Brodowski1146bc72005-09-28 19:28:37 +0200997 if (end_addr < start_addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998 return -EINVAL;
999
Dominik Brodowski22916632005-06-27 16:28:46 -07001000 ret = adjust_io(s, add, start_addr, end_addr);
Dominik Brodowski3c299762005-06-27 16:28:46 -07001001 if (!ret)
1002 s->resource_setup_new = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003
1004 return ret ? ret : count;
1005}
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001006static DEVICE_ATTR(available_resources_io, 0600, show_io_db, store_io_db);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001008static ssize_t show_mem_db(struct device *dev,
1009 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001011 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012 struct socket_data *data;
1013 struct resource_map *p;
1014 ssize_t ret = 0;
1015
Dominik Brodowski7fe908d2006-01-10 21:20:36 +01001016 mutex_lock(&rsrc_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 data = s->resource_data;
1018
1019 for (p = data->mem_db.next; p != &data->mem_db; p = p->next) {
1020 if (ret > (PAGE_SIZE - 10))
1021 continue;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001022 ret += snprintf(&buf[ret], (PAGE_SIZE - ret - 1),
1023 "0x%08lx - 0x%08lx\n",
1024 ((unsigned long) p->base),
1025 ((unsigned long) p->base + p->num - 1));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026 }
1027
Dominik Brodowski7fe908d2006-01-10 21:20:36 +01001028 mutex_unlock(&rsrc_mutex);
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001029 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030}
1031
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001032static ssize_t store_mem_db(struct device *dev,
1033 struct device_attribute *attr,
1034 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001036 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 unsigned long start_addr, end_addr;
Dominik Brodowski22916632005-06-27 16:28:46 -07001038 unsigned int add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039 ssize_t ret = 0;
1040
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001041 ret = sscanf(buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001043 ret = sscanf(buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -07001044 add = REMOVE_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001046 ret = sscanf(buf, "0x%lx - 0x%lx", &start_addr,
1047 &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -07001048 add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 if (ret != 2)
1050 return -EINVAL;
1051 }
1052 }
Dominik Brodowski1146bc72005-09-28 19:28:37 +02001053 if (end_addr < start_addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054 return -EINVAL;
1055
Dominik Brodowski22916632005-06-27 16:28:46 -07001056 ret = adjust_memory(s, add, start_addr, end_addr);
Dominik Brodowski3c299762005-06-27 16:28:46 -07001057 if (!ret)
1058 s->resource_setup_new = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059
1060 return ret ? ret : count;
1061}
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001062static DEVICE_ATTR(available_resources_mem, 0600, show_mem_db, store_mem_db);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063
David Brownell7d578962008-06-12 12:13:55 -07001064static struct attribute *pccard_rsrc_attributes[] = {
1065 &dev_attr_available_resources_io.attr,
1066 &dev_attr_available_resources_mem.attr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 NULL,
1068};
1069
David Brownell7d578962008-06-12 12:13:55 -07001070static const struct attribute_group rsrc_attributes = {
1071 .attrs = pccard_rsrc_attributes,
1072};
1073
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001074static int __devinit pccard_sysfs_add_rsrc(struct device *dev,
Dmitry Torokhovd8539d82005-09-15 02:01:36 -05001075 struct class_interface *class_intf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001077 struct pcmcia_socket *s = dev_get_drvdata(dev);
David Brownell7d578962008-06-12 12:13:55 -07001078
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 if (s->resource_ops != &pccard_nonstatic_ops)
1080 return 0;
David Brownell7d578962008-06-12 12:13:55 -07001081 return sysfs_create_group(&dev->kobj, &rsrc_attributes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082}
1083
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001084static void __devexit pccard_sysfs_remove_rsrc(struct device *dev,
Dmitry Torokhovd8539d82005-09-15 02:01:36 -05001085 struct class_interface *class_intf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001087 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088
1089 if (s->resource_ops != &pccard_nonstatic_ops)
1090 return;
David Brownell7d578962008-06-12 12:13:55 -07001091 sysfs_remove_group(&dev->kobj, &rsrc_attributes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092}
1093
Sam Ravnborged49f5d2008-05-01 04:34:50 -07001094static struct class_interface pccard_rsrc_interface __refdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 .class = &pcmcia_socket_class,
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001096 .add_dev = &pccard_sysfs_add_rsrc,
1097 .remove_dev = __devexit_p(&pccard_sysfs_remove_rsrc),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098};
1099
1100static int __init nonstatic_sysfs_init(void)
1101{
1102 return class_interface_register(&pccard_rsrc_interface);
1103}
1104
1105static void __exit nonstatic_sysfs_exit(void)
1106{
1107 class_interface_unregister(&pccard_rsrc_interface);
1108}
1109
1110module_init(nonstatic_sysfs_init);
1111module_exit(nonstatic_sysfs_exit);