blob: 19cecb53975979574b290c7c6bf290d9782c3159 [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
Linus Torvalds1da177e2005-04-16 15:20:36 -070062#define MEM_PROBE_LOW (1 << 0)
63#define MEM_PROBE_HIGH (1 << 1)
64
65
66/*======================================================================
67
68 Linux resource management extensions
69
70======================================================================*/
71
72static struct resource *
Kay Sievers25096982008-11-01 11:46:06 +010073make_resource(resource_size_t b, resource_size_t n, int flags, const char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -070074{
Dominik Brodowski8084b372005-12-11 21:18:26 +010075 struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -070076
77 if (res) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070078 res->name = name;
79 res->start = b;
80 res->end = b + n - 1;
81 res->flags = flags;
82 }
83 return res;
84}
85
86static struct resource *
Greg Kroah-Hartman2427ddd2006-06-12 17:07:52 -070087claim_region(struct pcmcia_socket *s, resource_size_t base,
88 resource_size_t size, int type, char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -070089{
90 struct resource *res, *parent;
91
92 parent = type & IORESOURCE_MEM ? &iomem_resource : &ioport_resource;
93 res = make_resource(base, size, type | IORESOURCE_BUSY, name);
94
95 if (res) {
96#ifdef CONFIG_PCI
97 if (s && s->cb_dev)
98 parent = pci_find_parent_resource(s->cb_dev, res);
99#endif
100 if (!parent || request_resource(parent, res)) {
101 kfree(res);
102 res = NULL;
103 }
104 }
105 return res;
106}
107
108static void free_region(struct resource *res)
109{
110 if (res) {
111 release_resource(res);
112 kfree(res);
113 }
114}
115
116/*======================================================================
117
118 These manage the internal databases of available resources.
119
120======================================================================*/
121
122static int add_interval(struct resource_map *map, u_long base, u_long num)
123{
Dominik Brodowski11683862008-08-03 10:22:47 +0200124 struct resource_map *p, *q;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125
Dominik Brodowski11683862008-08-03 10:22:47 +0200126 for (p = map; ; p = p->next) {
127 if ((p != map) && (p->base+p->num-1 >= base))
128 return -1;
129 if ((p->next == map) || (p->next->base > base+num-1))
130 break;
131 }
132 q = kmalloc(sizeof(struct resource_map), GFP_KERNEL);
133 if (!q) {
134 printk(KERN_WARNING "out of memory to update resources\n");
135 return -ENOMEM;
136 }
137 q->base = base; q->num = num;
138 q->next = p->next; p->next = q;
139 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140}
141
142/*====================================================================*/
143
144static int sub_interval(struct resource_map *map, u_long base, u_long num)
145{
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100146 struct resource_map *p, *q;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100148 for (p = map; ; p = q) {
149 q = p->next;
150 if (q == map)
151 break;
152 if ((q->base+q->num > base) && (base+num > q->base)) {
153 if (q->base >= base) {
154 if (q->base+q->num <= base+num) {
155 /* Delete whole block */
156 p->next = q->next;
157 kfree(q);
158 /* don't advance the pointer yet */
159 q = p;
160 } else {
161 /* Cut off bit from the front */
162 q->num = q->base + q->num - base - num;
163 q->base = base + num;
164 }
165 } else if (q->base+q->num <= base+num) {
166 /* Cut off bit from the end */
167 q->num = base - q->base;
168 } else {
169 /* Split the block into two pieces */
170 p = kmalloc(sizeof(struct resource_map),
171 GFP_KERNEL);
172 if (!p) {
173 printk(KERN_WARNING "out of memory to update resources\n");
174 return -ENOMEM;
175 }
176 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 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100183 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{
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100197 struct resource *res;
198 struct socket_data *s_data = s->resource_data;
199 unsigned int i, j, bad;
200 int any;
201 u_char *b, hole, most;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100203 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
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100206 /* First, what does a floating port look like? */
207 b = kzalloc(256, GFP_KERNEL);
208 if (!b) {
209 printk("\n");
210 dev_printk(KERN_ERR, &s->dev,
211 "do_io_probe: unable to kmalloc 256 bytes");
212 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100214 for (i = base, most = 0; i < base+num; i += 8) {
215 res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
216 if (!res)
217 continue;
218 hole = inb(i);
219 for (j = 1; j < 8; j++)
220 if (inb(i+j) != hole)
221 break;
222 free_region(res);
223 if ((j == 8) && (++b[hole] > b[most]))
224 most = hole;
225 if (b[most] == 127)
226 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100228 kfree(b);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100230 bad = any = 0;
231 for (i = base; i < base+num; i += 8) {
232 res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
233 if (!res)
234 continue;
235 for (j = 0; j < 8; j++)
236 if (inb(i+j) != most)
237 break;
238 free_region(res);
239 if (j < 8) {
240 if (!any)
241 printk(" excluding");
242 if (!bad)
243 bad = any = i;
244 } else {
245 if (bad) {
246 sub_interval(&s_data->io_db, bad, i-bad);
247 printk(" %#x-%#x", bad, i-1);
248 bad = 0;
249 }
250 }
251 }
252 if (bad) {
253 if ((num > 16) && (bad == base) && (i == base+num)) {
254 printk(" nothing: probe failed.\n");
255 return;
256 } else {
257 sub_interval(&s_data->io_db, bad, i-bad);
258 printk(" %#x-%#x", bad, i-1);
259 }
260 }
261
262 printk(any ? "\n" : " clean.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263}
264#endif
265
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100266/*======================================================================*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100268/**
269 * readable() - iomem validation function for cards with a valid CIS
270 */
Dominik Brodowskic5081d52008-06-19 20:12:34 +0200271static int readable(struct pcmcia_socket *s, struct resource *res,
272 unsigned int *count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273{
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100274 int ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275
Dominik Brodowski7ab248552010-02-17 18:00:07 +0100276 if (s->fake_cis) {
277 dev_dbg(&s->dev, "fake CIS is being used: can't validate mem\n");
278 return 0;
279 }
280
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 s->cis_mem.res = res;
282 s->cis_virt = ioremap(res->start, s->map_size);
283 if (s->cis_virt) {
Dominik Brodowski6b8e0872010-01-12 21:42:51 +0100284 mutex_unlock(&s->ops_mutex);
Dominik Brodowski6e7b51a2010-01-06 13:57:43 +0100285 /* as we're only called from pcmcia.c, we're safe */
286 if (s->callback->validate)
287 ret = s->callback->validate(s, count);
Dominik Brodowski904e3772010-01-02 12:28:04 +0100288 /* invalidate mapping */
Dominik Brodowski6b8e0872010-01-12 21:42:51 +0100289 mutex_lock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 iounmap(s->cis_virt);
291 s->cis_virt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 }
293 s->cis_mem.res = NULL;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100294 if ((ret) || (*count == 0))
295 return -EINVAL;
296 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297}
298
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100299/**
300 * checksum() - iomem validation function for simple memory cards
301 */
302static int checksum(struct pcmcia_socket *s, struct resource *res,
303 unsigned int *value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304{
305 pccard_mem_map map;
306 int i, a = 0, b = -1, d;
307 void __iomem *virt;
308
309 virt = ioremap(res->start, s->map_size);
310 if (virt) {
311 map.map = 0;
312 map.flags = MAP_ACTIVE;
313 map.speed = 0;
314 map.res = res;
315 map.card_start = 0;
316 s->ops->set_mem_map(s, &map);
317
318 /* Don't bother checking every word... */
319 for (i = 0; i < s->map_size; i += 44) {
320 d = readl(virt+i);
321 a += d;
322 b &= d;
323 }
324
325 map.flags = 0;
326 s->ops->set_mem_map(s, &map);
327
328 iounmap(virt);
329 }
330
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100331 if (b == -1)
332 return -EINVAL;
333
334 *value = a;
335
336 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337}
338
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100339/**
340 * do_validate_mem() - low level validate a memory region for PCMCIA use
341 * @s: PCMCIA socket to validate
342 * @base: start address of resource to check
343 * @size: size of resource to check
344 * @validate: validation function to use
345 *
346 * do_validate_mem() splits up the memory region which is to be checked
347 * into two parts. Both are passed to the @validate() function. If
348 * @validate() returns non-zero, or the value parameter to @validate()
349 * is zero, or the value parameter is different between both calls,
350 * the check fails, and -EINVAL is returned. Else, 0 is returned.
351 */
352static int do_validate_mem(struct pcmcia_socket *s,
353 unsigned long base, unsigned long size,
354 int validate (struct pcmcia_socket *s,
355 struct resource *res,
356 unsigned int *value))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357{
358 struct resource *res1, *res2;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100359 unsigned int info1 = 1, info2 = 1;
360 int ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100362 res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "PCMCIA memprobe");
363 res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM,
364 "PCMCIA memprobe");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365
366 if (res1 && res2) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100367 ret = 0;
368 if (validate) {
369 ret = validate(s, res1, &info1);
370 ret += validate(s, res2, &info2);
371 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 }
373
374 free_region(res2);
375 free_region(res1);
376
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100377 dev_dbg(&s->dev, "cs: memory probe 0x%06lx-0x%06lx: %p %p %u %u %u",
378 base, base+size-1, res1, res2, ret, info1, info2);
379
380 if ((ret) || (info1 != info2) || (info1 == 0))
381 return -EINVAL;
382
383 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384}
385
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100387/**
388 * do_mem_probe() - validate a memory region for PCMCIA use
389 * @s: PCMCIA socket to validate
390 * @base: start address of resource to check
391 * @num: size of resource to check
392 * @validate: validation function to use
393 * @fallback: validation function to use if validate fails
394 *
395 * do_mem_probe() checks a memory region for use by the PCMCIA subsystem.
396 * To do so, the area is split up into sensible parts, and then passed
397 * into the @validate() function. Only if @validate() and @fallback() fail,
398 * the area is marked as unavaibale for use by the PCMCIA subsystem. The
399 * function returns the size of the usable memory area.
400 */
401static int do_mem_probe(struct pcmcia_socket *s, u_long base, u_long num,
402 int validate (struct pcmcia_socket *s,
403 struct resource *res,
404 unsigned int *value),
405 int fallback (struct pcmcia_socket *s,
406 struct resource *res,
407 unsigned int *value))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408{
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100409 struct socket_data *s_data = s->resource_data;
410 u_long i, j, bad, fail, step;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100412 dev_printk(KERN_INFO, &s->dev, "cs: memory probe 0x%06lx-0x%06lx:",
413 base, base+num-1);
414 bad = fail = 0;
415 step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff);
416 /* don't allow too large steps */
417 if (step > 0x800000)
418 step = 0x800000;
419 /* cis_readable wants to map 2x map_size */
420 if (step < 2 * s->map_size)
421 step = 2 * s->map_size;
422 for (i = j = base; i < base+num; i = j + step) {
423 if (!fail) {
424 for (j = i; j < base+num; j += step) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100425 if (!do_validate_mem(s, j, step, validate))
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100426 break;
427 }
428 fail = ((i == base) && (j == base+num));
429 }
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100430 if ((fail) && (fallback)) {
431 for (j = i; j < base+num; j += step)
432 if (!do_validate_mem(s, j, step, fallback))
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100433 break;
434 }
435 if (i != j) {
436 if (!bad)
437 printk(" excluding");
438 printk(" %#05lx-%#05lx", i, j-1);
439 sub_interval(&s_data->mem_db, i, j-i);
440 bad += j-i;
441 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100443 printk(bad ? "\n" : " clean.\n");
444 return num - bad;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445}
446
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100447
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448#ifdef CONFIG_PCMCIA_PROBE
449
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100450/**
451 * inv_probe() - top-to-bottom search for one usuable high memory area
452 * @s: PCMCIA socket to validate
453 * @m: resource_map to check
454 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455static u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s)
456{
Dominik Brodowskide759142005-09-28 19:41:56 +0200457 struct socket_data *s_data = s->resource_data;
458 u_long ok;
459 if (m == &s_data->mem_db)
460 return 0;
461 ok = inv_probe(m->next, s);
462 if (ok) {
463 if (m->base >= 0x100000)
464 sub_interval(&s_data->mem_db, m->base, m->num);
465 return ok;
466 }
467 if (m->base < 0x100000)
468 return 0;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100469 return do_mem_probe(s, m->base, m->num, readable, checksum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470}
471
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100472/**
473 * validate_mem() - memory probe function
474 * @s: PCMCIA socket to validate
475 * @probe_mask: MEM_PROBE_LOW | MEM_PROBE_HIGH
476 *
477 * The memory probe. If the memory list includes a 64K-aligned block
478 * below 1MB, we probe in 64K chunks, and as soon as we accumulate at
479 * least mem_limit free space, we quit. Returns 0 on usuable ports.
480 */
Dominik Brodowskide759142005-09-28 19:41:56 +0200481static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482{
Dominik Brodowskide759142005-09-28 19:41:56 +0200483 struct resource_map *m, mm;
484 static unsigned char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 };
485 unsigned long b, i, ok = 0;
486 struct socket_data *s_data = s->resource_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487
Dominik Brodowskide759142005-09-28 19:41:56 +0200488 /* We do up to four passes through the list */
489 if (probe_mask & MEM_PROBE_HIGH) {
490 if (inv_probe(s_data->mem_db.next, s) > 0)
491 return 0;
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200492 dev_printk(KERN_NOTICE, &s->dev,
493 "cs: warning: no high memory space available!\n");
Dominik Brodowskide759142005-09-28 19:41:56 +0200494 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 }
Dominik Brodowskide759142005-09-28 19:41:56 +0200496
497 for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
498 mm = *m;
499 /* Only probe < 1 MB */
500 if (mm.base >= 0x100000)
501 continue;
502 if ((mm.base | mm.num) & 0xffff) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100503 ok += do_mem_probe(s, mm.base, mm.num, readable,
504 checksum);
Dominik Brodowskide759142005-09-28 19:41:56 +0200505 continue;
506 }
507 /* Special probe for 64K-aligned block */
508 for (i = 0; i < 4; i++) {
509 b = order[i] << 12;
510 if ((b >= mm.base) && (b+0x10000 <= mm.base+mm.num)) {
511 if (ok >= mem_limit)
512 sub_interval(&s_data->mem_db, b, 0x10000);
513 else
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100514 ok += do_mem_probe(s, b, 0x10000,
515 readable, checksum);
Dominik Brodowskide759142005-09-28 19:41:56 +0200516 }
517 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 }
Dominik Brodowskide759142005-09-28 19:41:56 +0200519
520 if (ok > 0)
521 return 0;
522
523 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524}
525
526#else /* CONFIG_PCMCIA_PROBE */
527
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100528/**
529 * validate_mem() - memory probe function
530 * @s: PCMCIA socket to validate
531 * @probe_mask: ignored
532 *
533 * Returns 0 on usuable ports.
534 */
Andrew Morton2cff9442005-11-16 21:29:26 -0800535static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536{
537 struct resource_map *m, mm;
538 struct socket_data *s_data = s->resource_data;
Andrew Morton2cff9442005-11-16 21:29:26 -0800539 unsigned long ok = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540
541 for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
542 mm = *m;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100543 ok += do_mem_probe(s, mm.base, mm.num, readable, checksum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 }
Andrew Morton2cff9442005-11-16 21:29:26 -0800545 if (ok > 0)
546 return 0;
547 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548}
549
550#endif /* CONFIG_PCMCIA_PROBE */
551
552
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100553/**
554 * pcmcia_nonstatic_validate_mem() - try to validate iomem for PCMCIA use
555 * @s: PCMCIA socket to validate
556 *
557 * This is tricky... when we set up CIS memory, we try to validate
558 * the memory window space allocations.
559 *
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100560 * Locking note: Must be called with skt_mutex held!
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 */
Dominik Brodowskide759142005-09-28 19:41:56 +0200562static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563{
564 struct socket_data *s_data = s->resource_data;
Dominik Brodowskide759142005-09-28 19:41:56 +0200565 unsigned int probe_mask = MEM_PROBE_LOW;
566 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567
Dominik Brodowskide759142005-09-28 19:41:56 +0200568 if (!probe_mem)
569 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570
Dominik Brodowskide759142005-09-28 19:41:56 +0200571 if (s->features & SS_CAP_PAGE_REGS)
572 probe_mask = MEM_PROBE_HIGH;
573
574 if (probe_mask & ~s_data->rsrc_mem_probe) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100575 if (s->state & SOCKET_PRESENT) {
Dominik Brodowskide759142005-09-28 19:41:56 +0200576 ret = validate_mem(s, probe_mask);
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100577 if (!ret)
578 s_data->rsrc_mem_probe |= probe_mask;
579 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 }
Dominik Brodowskide759142005-09-28 19:41:56 +0200581
Dominik Brodowskide759142005-09-28 19:41:56 +0200582 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583}
584
585struct pcmcia_align_data {
586 unsigned long mask;
587 unsigned long offset;
588 struct resource_map *map;
589};
590
591static void
592pcmcia_common_align(void *align_data, struct resource *res,
Greg Kroah-Hartman2427ddd2006-06-12 17:07:52 -0700593 resource_size_t size, resource_size_t align)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594{
595 struct pcmcia_align_data *data = align_data;
Greg Kroah-Hartman2427ddd2006-06-12 17:07:52 -0700596 resource_size_t start;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 /*
598 * Ensure that we have the correct start address
599 */
600 start = (res->start & ~data->mask) + data->offset;
601 if (start < res->start)
602 start += data->mask + 1;
603 res->start = start;
604}
605
606static void
Greg Kroah-Hartman2427ddd2006-06-12 17:07:52 -0700607pcmcia_align(void *align_data, struct resource *res, resource_size_t size,
608 resource_size_t align)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609{
610 struct pcmcia_align_data *data = align_data;
611 struct resource_map *m;
612
613 pcmcia_common_align(data, res, size, align);
614
615 for (m = data->map->next; m != data->map; m = m->next) {
616 unsigned long start = m->base;
617 unsigned long end = m->base + m->num - 1;
618
619 /*
620 * If the lower resources are not available, try aligning
621 * to this entry of the resource database to see if it'll
622 * fit here.
623 */
624 if (res->start < start) {
625 res->start = start;
626 pcmcia_common_align(data, res, size, align);
627 }
628
629 /*
630 * If we're above the area which was passed in, there's
631 * no point proceeding.
632 */
633 if (res->start >= res->end)
634 break;
635
636 if ((res->start + size - 1) <= end)
637 break;
638 }
639
640 /*
641 * If we failed to find something suitable, ensure we fail.
642 */
643 if (m == data->map)
644 res->start = res->end;
645}
646
647/*
648 * Adjust an existing IO region allocation, but making sure that we don't
649 * encroach outside the resources which the user supplied.
650 */
651static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_start,
652 unsigned long r_end, struct pcmcia_socket *s)
653{
654 struct resource_map *m;
655 struct socket_data *s_data = s->resource_data;
656 int ret = -ENOMEM;
657
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 for (m = s_data->io_db.next; m != &s_data->io_db; m = m->next) {
659 unsigned long start = m->base;
660 unsigned long end = m->base + m->num - 1;
661
662 if (start > r_start || r_end > end)
663 continue;
664
665 ret = adjust_resource(res, r_start, r_end - r_start + 1);
666 break;
667 }
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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701#ifdef CONFIG_PCI
702 if (s->cb_dev) {
703 ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1,
704 min, 0, pcmcia_align, &data);
705 } else
706#endif
707 ret = allocate_resource(&ioport_resource, res, num, min, ~0UL,
708 1, pcmcia_align, &data);
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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741#ifdef CONFIG_PCI
742 if (s->cb_dev) {
743 ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num,
744 1, min, 0,
745 pcmcia_align, &data);
746 } else
747#endif
748 ret = allocate_resource(&iomem_resource, res, num, min,
749 max, 1, pcmcia_align, &data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 if (ret == 0 || low)
751 break;
752 low = 1;
753 }
754
755 if (ret != 0) {
756 kfree(res);
757 res = NULL;
758 }
759 return res;
760}
761
762
Dominik Brodowski22916632005-06-27 16:28:46 -0700763static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 struct socket_data *data = s->resource_data;
Dominik Brodowski22916632005-06-27 16:28:46 -0700766 unsigned long size = end - start + 1;
767 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768
Dominik Brodowski1146bc72005-09-28 19:28:37 +0200769 if (end < start)
Dominik Brodowski22916632005-06-27 16:28:46 -0700770 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771
Dominik Brodowski22916632005-06-27 16:28:46 -0700772 switch (action) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 case ADD_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700774 ret = add_interval(&data->mem_db, start, size);
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100775 if (!ret)
776 do_mem_probe(s, start, size, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 break;
778 case REMOVE_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700779 ret = sub_interval(&data->mem_db, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 break;
781 default:
Dominik Brodowski22916632005-06-27 16:28:46 -0700782 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784
785 return ret;
786}
787
788
Dominik Brodowski22916632005-06-27 16:28:46 -0700789static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790{
791 struct socket_data *data = s->resource_data;
Dominik Brodowski22916632005-06-27 16:28:46 -0700792 unsigned long size = end - start + 1;
793 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794
Dominik Brodowski1146bc72005-09-28 19:28:37 +0200795 if (end < start)
Dominik Brodowski22916632005-06-27 16:28:46 -0700796 return -EINVAL;
797
798 if (end > IO_SPACE_LIMIT)
799 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800
Dominik Brodowski22916632005-06-27 16:28:46 -0700801 switch (action) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 case ADD_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700803 if (add_interval(&data->io_db, start, size) != 0) {
804 ret = -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 break;
806 }
807#ifdef CONFIG_PCMCIA_PROBE
808 if (probe_io)
Dominik Brodowski22916632005-06-27 16:28:46 -0700809 do_io_probe(s, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810#endif
811 break;
812 case REMOVE_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700813 sub_interval(&data->io_db, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 break;
815 default:
Dominik Brodowski22916632005-06-27 16:28:46 -0700816 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 break;
818 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819
820 return ret;
821}
822
823
Dominik Brodowski3c299762005-06-27 16:28:46 -0700824#ifdef CONFIG_PCI
825static int nonstatic_autoadd_resources(struct pcmcia_socket *s)
826{
827 struct resource *res;
828 int i, done = 0;
829
830 if (!s->cb_dev || !s->cb_dev->bus)
831 return -ENODEV;
832
Brian Gerst0d078f62005-10-30 14:59:20 -0800833#if defined(CONFIG_X86)
Dominik Brodowskib6d00f02005-06-27 16:29:02 -0700834 /* If this is the root bus, the risk of hitting
835 * some strange system devices which aren't protected
836 * by either ACPI resource tables or properly requested
837 * resources is too big. Therefore, don't do auto-adding
838 * of resources at the moment.
839 */
840 if (s->cb_dev->bus->number == 0)
841 return -EINVAL;
842#endif
843
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100844 for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
Dominik Brodowski3c299762005-06-27 16:28:46 -0700845 res = s->cb_dev->bus->resource[i];
846 if (!res)
847 continue;
848
849 if (res->flags & IORESOURCE_IO) {
850 if (res == &ioport_resource)
851 continue;
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200852 dev_printk(KERN_INFO, &s->cb_dev->dev,
853 "pcmcia: parent PCI bridge I/O "
854 "window: 0x%llx - 0x%llx\n",
855 (unsigned long long)res->start,
856 (unsigned long long)res->end);
Dominik Brodowski3c299762005-06-27 16:28:46 -0700857 if (!adjust_io(s, ADD_MANAGED_RESOURCE, res->start, res->end))
858 done |= IORESOURCE_IO;
859
860 }
861
862 if (res->flags & IORESOURCE_MEM) {
863 if (res == &iomem_resource)
864 continue;
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200865 dev_printk(KERN_INFO, &s->cb_dev->dev,
866 "pcmcia: parent PCI bridge Memory "
867 "window: 0x%llx - 0x%llx\n",
868 (unsigned long long)res->start,
869 (unsigned long long)res->end);
Dominik Brodowski3c299762005-06-27 16:28:46 -0700870 if (!adjust_memory(s, ADD_MANAGED_RESOURCE, res->start, res->end))
871 done |= IORESOURCE_MEM;
872 }
873 }
874
875 /* if we got at least one of IO, and one of MEM, we can be glad and
876 * activate the PCMCIA subsystem */
Dominik Brodowski54bb5672005-09-28 19:29:59 +0200877 if (done == (IORESOURCE_MEM | IORESOURCE_IO))
Dominik Brodowski3c299762005-06-27 16:28:46 -0700878 s->resource_setup_done = 1;
879
880 return 0;
881}
882
883#else
884
885static inline int nonstatic_autoadd_resources(struct pcmcia_socket *s)
886{
887 return -ENODEV;
888}
889
890#endif
891
892
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893static int nonstatic_init(struct pcmcia_socket *s)
894{
895 struct socket_data *data;
896
Dominik Brodowski8084b372005-12-11 21:18:26 +0100897 data = kzalloc(sizeof(struct socket_data), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898 if (!data)
899 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900
901 data->mem_db.next = &data->mem_db;
902 data->io_db.next = &data->io_db;
903
904 s->resource_data = (void *) data;
905
Dominik Brodowski3c299762005-06-27 16:28:46 -0700906 nonstatic_autoadd_resources(s);
907
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 return 0;
909}
910
911static void nonstatic_release_resource_db(struct pcmcia_socket *s)
912{
913 struct socket_data *data = s->resource_data;
914 struct resource_map *p, *q;
915
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916 for (p = data->mem_db.next; p != &data->mem_db; p = q) {
917 q = p->next;
918 kfree(p);
919 }
920 for (p = data->io_db.next; p != &data->io_db; p = q) {
921 q = p->next;
922 kfree(p);
923 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924}
925
926
927struct pccard_resource_ops pccard_nonstatic_ops = {
928 .validate_mem = pcmcia_nonstatic_validate_mem,
929 .adjust_io_region = nonstatic_adjust_io_region,
930 .find_io = nonstatic_find_io_region,
931 .find_mem = nonstatic_find_mem_region,
Dominik Brodowskic5023802008-06-19 19:02:52 +0200932 .add_io = adjust_io,
933 .add_mem = adjust_memory,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 .init = nonstatic_init,
935 .exit = nonstatic_release_resource_db,
936};
937EXPORT_SYMBOL(pccard_nonstatic_ops);
938
939
940/* sysfs interface to the resource database */
941
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200942static ssize_t show_io_db(struct device *dev,
943 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200945 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946 struct socket_data *data;
947 struct resource_map *p;
948 ssize_t ret = 0;
949
Dominik Brodowskicfe5d802010-01-17 19:31:45 +0100950 mutex_lock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951 data = s->resource_data;
952
953 for (p = data->io_db.next; p != &data->io_db; p = p->next) {
954 if (ret > (PAGE_SIZE - 10))
955 continue;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100956 ret += snprintf(&buf[ret], (PAGE_SIZE - ret - 1),
957 "0x%08lx - 0x%08lx\n",
958 ((unsigned long) p->base),
959 ((unsigned long) p->base + p->num - 1));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 }
961
Dominik Brodowskicfe5d802010-01-17 19:31:45 +0100962 mutex_unlock(&s->ops_mutex);
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100963 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964}
965
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200966static ssize_t store_io_db(struct device *dev,
967 struct device_attribute *attr,
968 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200970 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 unsigned long start_addr, end_addr;
Dominik Brodowski22916632005-06-27 16:28:46 -0700972 unsigned int add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973 ssize_t ret = 0;
974
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100975 ret = sscanf(buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100977 ret = sscanf(buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -0700978 add = REMOVE_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100980 ret = sscanf(buf, "0x%lx - 0x%lx", &start_addr,
981 &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -0700982 add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983 if (ret != 2)
984 return -EINVAL;
985 }
986 }
Dominik Brodowski1146bc72005-09-28 19:28:37 +0200987 if (end_addr < start_addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988 return -EINVAL;
989
Dominik Brodowskicfe5d802010-01-17 19:31:45 +0100990 mutex_lock(&s->ops_mutex);
Dominik Brodowski22916632005-06-27 16:28:46 -0700991 ret = adjust_io(s, add, start_addr, end_addr);
Dominik Brodowski3c299762005-06-27 16:28:46 -0700992 if (!ret)
993 s->resource_setup_new = 1;
Dominik Brodowskicfe5d802010-01-17 19:31:45 +0100994 mutex_unlock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995
996 return ret ? ret : count;
997}
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200998static DEVICE_ATTR(available_resources_io, 0600, show_io_db, store_io_db);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001000static ssize_t show_mem_db(struct device *dev,
1001 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001003 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004 struct socket_data *data;
1005 struct resource_map *p;
1006 ssize_t ret = 0;
1007
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001008 mutex_lock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 data = s->resource_data;
1010
1011 for (p = data->mem_db.next; p != &data->mem_db; p = p->next) {
1012 if (ret > (PAGE_SIZE - 10))
1013 continue;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001014 ret += snprintf(&buf[ret], (PAGE_SIZE - ret - 1),
1015 "0x%08lx - 0x%08lx\n",
1016 ((unsigned long) p->base),
1017 ((unsigned long) p->base + p->num - 1));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018 }
1019
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001020 mutex_unlock(&s->ops_mutex);
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001021 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022}
1023
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001024static ssize_t store_mem_db(struct device *dev,
1025 struct device_attribute *attr,
1026 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001028 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029 unsigned long start_addr, end_addr;
Dominik Brodowski22916632005-06-27 16:28:46 -07001030 unsigned int add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 ssize_t ret = 0;
1032
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001033 ret = sscanf(buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001035 ret = sscanf(buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -07001036 add = REMOVE_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001038 ret = sscanf(buf, "0x%lx - 0x%lx", &start_addr,
1039 &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -07001040 add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041 if (ret != 2)
1042 return -EINVAL;
1043 }
1044 }
Dominik Brodowski1146bc72005-09-28 19:28:37 +02001045 if (end_addr < start_addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 return -EINVAL;
1047
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001048 mutex_lock(&s->ops_mutex);
Dominik Brodowski22916632005-06-27 16:28:46 -07001049 ret = adjust_memory(s, add, start_addr, end_addr);
Dominik Brodowski3c299762005-06-27 16:28:46 -07001050 if (!ret)
1051 s->resource_setup_new = 1;
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001052 mutex_unlock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053
1054 return ret ? ret : count;
1055}
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001056static DEVICE_ATTR(available_resources_mem, 0600, show_mem_db, store_mem_db);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057
David Brownell7d578962008-06-12 12:13:55 -07001058static struct attribute *pccard_rsrc_attributes[] = {
1059 &dev_attr_available_resources_io.attr,
1060 &dev_attr_available_resources_mem.attr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 NULL,
1062};
1063
David Brownell7d578962008-06-12 12:13:55 -07001064static const struct attribute_group rsrc_attributes = {
1065 .attrs = pccard_rsrc_attributes,
1066};
1067
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001068static int __devinit pccard_sysfs_add_rsrc(struct device *dev,
Dmitry Torokhovd8539d82005-09-15 02:01:36 -05001069 struct class_interface *class_intf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001071 struct pcmcia_socket *s = dev_get_drvdata(dev);
David Brownell7d578962008-06-12 12:13:55 -07001072
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 if (s->resource_ops != &pccard_nonstatic_ops)
1074 return 0;
David Brownell7d578962008-06-12 12:13:55 -07001075 return sysfs_create_group(&dev->kobj, &rsrc_attributes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076}
1077
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001078static void __devexit pccard_sysfs_remove_rsrc(struct device *dev,
Dmitry Torokhovd8539d82005-09-15 02:01:36 -05001079 struct class_interface *class_intf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001081 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082
1083 if (s->resource_ops != &pccard_nonstatic_ops)
1084 return;
David Brownell7d578962008-06-12 12:13:55 -07001085 sysfs_remove_group(&dev->kobj, &rsrc_attributes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086}
1087
Sam Ravnborged49f5d2008-05-01 04:34:50 -07001088static struct class_interface pccard_rsrc_interface __refdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 .class = &pcmcia_socket_class,
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001090 .add_dev = &pccard_sysfs_add_rsrc,
1091 .remove_dev = __devexit_p(&pccard_sysfs_remove_rsrc),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092};
1093
1094static int __init nonstatic_sysfs_init(void)
1095{
1096 return class_interface_register(&pccard_rsrc_interface);
1097}
1098
1099static void __exit nonstatic_sysfs_exit(void)
1100{
1101 class_interface_unregister(&pccard_rsrc_interface);
1102}
1103
1104module_init(nonstatic_sysfs_init);
1105module_exit(nonstatic_sysfs_exit);