blob: a06881c3b962b1631c40e92bebc7dcbad5fb153e [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) {
Dominik Brodowskif309cb32010-02-17 14:35:33 +0100127 if ((p != map) && (p->base+p->num >= base)) {
128 p->num = max(num + base - p->base, p->num);
129 return 0;
130 }
Dominik Brodowski11683862008-08-03 10:22:47 +0200131 if ((p->next == map) || (p->next->base > base+num-1))
132 break;
133 }
134 q = kmalloc(sizeof(struct resource_map), GFP_KERNEL);
135 if (!q) {
136 printk(KERN_WARNING "out of memory to update resources\n");
137 return -ENOMEM;
138 }
139 q->base = base; q->num = num;
140 q->next = p->next; p->next = q;
141 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142}
143
144/*====================================================================*/
145
146static int sub_interval(struct resource_map *map, u_long base, u_long num)
147{
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100148 struct resource_map *p, *q;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100150 for (p = map; ; p = q) {
151 q = p->next;
152 if (q == map)
153 break;
154 if ((q->base+q->num > base) && (base+num > q->base)) {
155 if (q->base >= base) {
156 if (q->base+q->num <= base+num) {
157 /* Delete whole block */
158 p->next = q->next;
159 kfree(q);
160 /* don't advance the pointer yet */
161 q = p;
162 } else {
163 /* Cut off bit from the front */
164 q->num = q->base + q->num - base - num;
165 q->base = base + num;
166 }
167 } else if (q->base+q->num <= base+num) {
168 /* Cut off bit from the end */
169 q->num = base - q->base;
170 } else {
171 /* Split the block into two pieces */
172 p = kmalloc(sizeof(struct resource_map),
173 GFP_KERNEL);
174 if (!p) {
175 printk(KERN_WARNING "out of memory to update resources\n");
176 return -ENOMEM;
177 }
178 p->base = base+num;
179 p->num = q->base+q->num - p->base;
180 q->num = base - q->base;
181 p->next = q->next ; q->next = p;
182 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100185 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186}
187
188/*======================================================================
189
190 These routines examine a region of IO or memory addresses to
191 determine what ranges might be genuinely available.
192
193======================================================================*/
194
195#ifdef CONFIG_PCMCIA_PROBE
Olof Johansson906da802008-02-04 22:27:35 -0800196static void do_io_probe(struct pcmcia_socket *s, unsigned int base,
197 unsigned int num)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198{
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100199 struct resource *res;
200 struct socket_data *s_data = s->resource_data;
201 unsigned int i, j, bad;
202 int any;
203 u_char *b, hole, most;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100205 dev_printk(KERN_INFO, &s->dev, "cs: IO port probe %#x-%#x:",
206 base, base+num-1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100208 /* First, what does a floating port look like? */
209 b = kzalloc(256, GFP_KERNEL);
210 if (!b) {
211 printk("\n");
212 dev_printk(KERN_ERR, &s->dev,
213 "do_io_probe: unable to kmalloc 256 bytes");
214 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100216 for (i = base, most = 0; i < base+num; i += 8) {
217 res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
218 if (!res)
219 continue;
220 hole = inb(i);
221 for (j = 1; j < 8; j++)
222 if (inb(i+j) != hole)
223 break;
224 free_region(res);
225 if ((j == 8) && (++b[hole] > b[most]))
226 most = hole;
227 if (b[most] == 127)
228 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100230 kfree(b);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100232 bad = any = 0;
233 for (i = base; i < base+num; i += 8) {
234 res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
235 if (!res)
236 continue;
237 for (j = 0; j < 8; j++)
238 if (inb(i+j) != most)
239 break;
240 free_region(res);
241 if (j < 8) {
242 if (!any)
243 printk(" excluding");
244 if (!bad)
245 bad = any = i;
246 } else {
247 if (bad) {
248 sub_interval(&s_data->io_db, bad, i-bad);
249 printk(" %#x-%#x", bad, i-1);
250 bad = 0;
251 }
252 }
253 }
254 if (bad) {
255 if ((num > 16) && (bad == base) && (i == base+num)) {
256 printk(" nothing: probe failed.\n");
257 return;
258 } else {
259 sub_interval(&s_data->io_db, bad, i-bad);
260 printk(" %#x-%#x", bad, i-1);
261 }
262 }
263
264 printk(any ? "\n" : " clean.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265}
266#endif
267
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100268/*======================================================================*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100270/**
271 * readable() - iomem validation function for cards with a valid CIS
272 */
Dominik Brodowskic5081d52008-06-19 20:12:34 +0200273static int readable(struct pcmcia_socket *s, struct resource *res,
274 unsigned int *count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275{
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100276 int ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277
Dominik Brodowski7ab248552010-02-17 18:00:07 +0100278 if (s->fake_cis) {
279 dev_dbg(&s->dev, "fake CIS is being used: can't validate mem\n");
280 return 0;
281 }
282
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 s->cis_mem.res = res;
284 s->cis_virt = ioremap(res->start, s->map_size);
285 if (s->cis_virt) {
Dominik Brodowski6b8e0872010-01-12 21:42:51 +0100286 mutex_unlock(&s->ops_mutex);
Dominik Brodowski6e7b51a2010-01-06 13:57:43 +0100287 /* as we're only called from pcmcia.c, we're safe */
288 if (s->callback->validate)
289 ret = s->callback->validate(s, count);
Dominik Brodowski904e3772010-01-02 12:28:04 +0100290 /* invalidate mapping */
Dominik Brodowski6b8e0872010-01-12 21:42:51 +0100291 mutex_lock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 iounmap(s->cis_virt);
293 s->cis_virt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294 }
295 s->cis_mem.res = NULL;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100296 if ((ret) || (*count == 0))
297 return -EINVAL;
298 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299}
300
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100301/**
302 * checksum() - iomem validation function for simple memory cards
303 */
304static int checksum(struct pcmcia_socket *s, struct resource *res,
305 unsigned int *value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306{
307 pccard_mem_map map;
308 int i, a = 0, b = -1, d;
309 void __iomem *virt;
310
311 virt = ioremap(res->start, s->map_size);
312 if (virt) {
313 map.map = 0;
314 map.flags = MAP_ACTIVE;
315 map.speed = 0;
316 map.res = res;
317 map.card_start = 0;
318 s->ops->set_mem_map(s, &map);
319
320 /* Don't bother checking every word... */
321 for (i = 0; i < s->map_size; i += 44) {
322 d = readl(virt+i);
323 a += d;
324 b &= d;
325 }
326
327 map.flags = 0;
328 s->ops->set_mem_map(s, &map);
329
330 iounmap(virt);
331 }
332
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100333 if (b == -1)
334 return -EINVAL;
335
336 *value = a;
337
338 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339}
340
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100341/**
342 * do_validate_mem() - low level validate a memory region for PCMCIA use
343 * @s: PCMCIA socket to validate
344 * @base: start address of resource to check
345 * @size: size of resource to check
346 * @validate: validation function to use
347 *
348 * do_validate_mem() splits up the memory region which is to be checked
349 * into two parts. Both are passed to the @validate() function. If
350 * @validate() returns non-zero, or the value parameter to @validate()
351 * is zero, or the value parameter is different between both calls,
352 * the check fails, and -EINVAL is returned. Else, 0 is returned.
353 */
354static int do_validate_mem(struct pcmcia_socket *s,
355 unsigned long base, unsigned long size,
356 int validate (struct pcmcia_socket *s,
357 struct resource *res,
358 unsigned int *value))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359{
360 struct resource *res1, *res2;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100361 unsigned int info1 = 1, info2 = 1;
362 int ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100364 res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "PCMCIA memprobe");
365 res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM,
366 "PCMCIA memprobe");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367
368 if (res1 && res2) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100369 ret = 0;
370 if (validate) {
371 ret = validate(s, res1, &info1);
372 ret += validate(s, res2, &info2);
373 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 }
375
376 free_region(res2);
377 free_region(res1);
378
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100379 dev_dbg(&s->dev, "cs: memory probe 0x%06lx-0x%06lx: %p %p %u %u %u",
380 base, base+size-1, res1, res2, ret, info1, info2);
381
382 if ((ret) || (info1 != info2) || (info1 == 0))
383 return -EINVAL;
384
385 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386}
387
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100389/**
390 * do_mem_probe() - validate a memory region for PCMCIA use
391 * @s: PCMCIA socket to validate
392 * @base: start address of resource to check
393 * @num: size of resource to check
394 * @validate: validation function to use
395 * @fallback: validation function to use if validate fails
396 *
397 * do_mem_probe() checks a memory region for use by the PCMCIA subsystem.
398 * To do so, the area is split up into sensible parts, and then passed
399 * into the @validate() function. Only if @validate() and @fallback() fail,
400 * the area is marked as unavaibale for use by the PCMCIA subsystem. The
401 * function returns the size of the usable memory area.
402 */
403static int do_mem_probe(struct pcmcia_socket *s, u_long base, u_long num,
404 int validate (struct pcmcia_socket *s,
405 struct resource *res,
406 unsigned int *value),
407 int fallback (struct pcmcia_socket *s,
408 struct resource *res,
409 unsigned int *value))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410{
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100411 struct socket_data *s_data = s->resource_data;
412 u_long i, j, bad, fail, step;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100414 dev_printk(KERN_INFO, &s->dev, "cs: memory probe 0x%06lx-0x%06lx:",
415 base, base+num-1);
416 bad = fail = 0;
417 step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff);
418 /* don't allow too large steps */
419 if (step > 0x800000)
420 step = 0x800000;
421 /* cis_readable wants to map 2x map_size */
422 if (step < 2 * s->map_size)
423 step = 2 * s->map_size;
424 for (i = j = base; i < base+num; i = j + step) {
425 if (!fail) {
426 for (j = i; j < base+num; j += step) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100427 if (!do_validate_mem(s, j, step, validate))
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100428 break;
429 }
430 fail = ((i == base) && (j == base+num));
431 }
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100432 if ((fail) && (fallback)) {
433 for (j = i; j < base+num; j += step)
434 if (!do_validate_mem(s, j, step, fallback))
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100435 break;
436 }
437 if (i != j) {
438 if (!bad)
439 printk(" excluding");
440 printk(" %#05lx-%#05lx", i, j-1);
441 sub_interval(&s_data->mem_db, i, j-i);
442 bad += j-i;
443 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100445 printk(bad ? "\n" : " clean.\n");
446 return num - bad;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447}
448
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100449
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450#ifdef CONFIG_PCMCIA_PROBE
451
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100452/**
453 * inv_probe() - top-to-bottom search for one usuable high memory area
454 * @s: PCMCIA socket to validate
455 * @m: resource_map to check
456 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457static u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s)
458{
Dominik Brodowskide759142005-09-28 19:41:56 +0200459 struct socket_data *s_data = s->resource_data;
460 u_long ok;
461 if (m == &s_data->mem_db)
462 return 0;
463 ok = inv_probe(m->next, s);
464 if (ok) {
465 if (m->base >= 0x100000)
466 sub_interval(&s_data->mem_db, m->base, m->num);
467 return ok;
468 }
469 if (m->base < 0x100000)
470 return 0;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100471 return do_mem_probe(s, m->base, m->num, readable, checksum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472}
473
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100474/**
475 * validate_mem() - memory probe function
476 * @s: PCMCIA socket to validate
477 * @probe_mask: MEM_PROBE_LOW | MEM_PROBE_HIGH
478 *
479 * The memory probe. If the memory list includes a 64K-aligned block
480 * below 1MB, we probe in 64K chunks, and as soon as we accumulate at
481 * least mem_limit free space, we quit. Returns 0 on usuable ports.
482 */
Dominik Brodowskide759142005-09-28 19:41:56 +0200483static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484{
Dominik Brodowskide759142005-09-28 19:41:56 +0200485 struct resource_map *m, mm;
486 static unsigned char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 };
487 unsigned long b, i, ok = 0;
488 struct socket_data *s_data = s->resource_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489
Dominik Brodowskide759142005-09-28 19:41:56 +0200490 /* We do up to four passes through the list */
491 if (probe_mask & MEM_PROBE_HIGH) {
492 if (inv_probe(s_data->mem_db.next, s) > 0)
493 return 0;
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200494 dev_printk(KERN_NOTICE, &s->dev,
495 "cs: warning: no high memory space available!\n");
Dominik Brodowskide759142005-09-28 19:41:56 +0200496 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 }
Dominik Brodowskide759142005-09-28 19:41:56 +0200498
499 for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
500 mm = *m;
501 /* Only probe < 1 MB */
502 if (mm.base >= 0x100000)
503 continue;
504 if ((mm.base | mm.num) & 0xffff) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100505 ok += do_mem_probe(s, mm.base, mm.num, readable,
506 checksum);
Dominik Brodowskide759142005-09-28 19:41:56 +0200507 continue;
508 }
509 /* Special probe for 64K-aligned block */
510 for (i = 0; i < 4; i++) {
511 b = order[i] << 12;
512 if ((b >= mm.base) && (b+0x10000 <= mm.base+mm.num)) {
513 if (ok >= mem_limit)
514 sub_interval(&s_data->mem_db, b, 0x10000);
515 else
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100516 ok += do_mem_probe(s, b, 0x10000,
517 readable, checksum);
Dominik Brodowskide759142005-09-28 19:41:56 +0200518 }
519 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 }
Dominik Brodowskide759142005-09-28 19:41:56 +0200521
522 if (ok > 0)
523 return 0;
524
525 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526}
527
528#else /* CONFIG_PCMCIA_PROBE */
529
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100530/**
531 * validate_mem() - memory probe function
532 * @s: PCMCIA socket to validate
533 * @probe_mask: ignored
534 *
535 * Returns 0 on usuable ports.
536 */
Andrew Morton2cff9442005-11-16 21:29:26 -0800537static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538{
539 struct resource_map *m, mm;
540 struct socket_data *s_data = s->resource_data;
Andrew Morton2cff9442005-11-16 21:29:26 -0800541 unsigned long ok = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542
543 for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
544 mm = *m;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100545 ok += do_mem_probe(s, mm.base, mm.num, readable, checksum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 }
Andrew Morton2cff9442005-11-16 21:29:26 -0800547 if (ok > 0)
548 return 0;
549 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550}
551
552#endif /* CONFIG_PCMCIA_PROBE */
553
554
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100555/**
556 * pcmcia_nonstatic_validate_mem() - try to validate iomem for PCMCIA use
557 * @s: PCMCIA socket to validate
558 *
559 * This is tricky... when we set up CIS memory, we try to validate
560 * the memory window space allocations.
561 *
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100562 * Locking note: Must be called with skt_mutex held!
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 */
Dominik Brodowskide759142005-09-28 19:41:56 +0200564static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565{
566 struct socket_data *s_data = s->resource_data;
Dominik Brodowskide759142005-09-28 19:41:56 +0200567 unsigned int probe_mask = MEM_PROBE_LOW;
568 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569
Dominik Brodowskide759142005-09-28 19:41:56 +0200570 if (!probe_mem)
571 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572
Dominik Brodowskide759142005-09-28 19:41:56 +0200573 if (s->features & SS_CAP_PAGE_REGS)
574 probe_mask = MEM_PROBE_HIGH;
575
576 if (probe_mask & ~s_data->rsrc_mem_probe) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100577 if (s->state & SOCKET_PRESENT) {
Dominik Brodowskide759142005-09-28 19:41:56 +0200578 ret = validate_mem(s, probe_mask);
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100579 if (!ret)
580 s_data->rsrc_mem_probe |= probe_mask;
581 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 }
Dominik Brodowskide759142005-09-28 19:41:56 +0200583
Dominik Brodowskide759142005-09-28 19:41:56 +0200584 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585}
586
587struct pcmcia_align_data {
588 unsigned long mask;
589 unsigned long offset;
590 struct resource_map *map;
591};
592
593static void
594pcmcia_common_align(void *align_data, struct resource *res,
Greg Kroah-Hartman2427ddd2006-06-12 17:07:52 -0700595 resource_size_t size, resource_size_t align)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596{
597 struct pcmcia_align_data *data = align_data;
Greg Kroah-Hartman2427ddd2006-06-12 17:07:52 -0700598 resource_size_t start;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 /*
600 * Ensure that we have the correct start address
601 */
602 start = (res->start & ~data->mask) + data->offset;
603 if (start < res->start)
604 start += data->mask + 1;
605 res->start = start;
606}
607
608static void
Greg Kroah-Hartman2427ddd2006-06-12 17:07:52 -0700609pcmcia_align(void *align_data, struct resource *res, resource_size_t size,
610 resource_size_t align)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611{
612 struct pcmcia_align_data *data = align_data;
613 struct resource_map *m;
614
615 pcmcia_common_align(data, res, size, align);
616
617 for (m = data->map->next; m != data->map; m = m->next) {
618 unsigned long start = m->base;
619 unsigned long end = m->base + m->num - 1;
620
621 /*
622 * If the lower resources are not available, try aligning
623 * to this entry of the resource database to see if it'll
624 * fit here.
625 */
626 if (res->start < start) {
627 res->start = start;
628 pcmcia_common_align(data, res, size, align);
629 }
630
631 /*
632 * If we're above the area which was passed in, there's
633 * no point proceeding.
634 */
635 if (res->start >= res->end)
636 break;
637
638 if ((res->start + size - 1) <= end)
639 break;
640 }
641
642 /*
643 * If we failed to find something suitable, ensure we fail.
644 */
645 if (m == data->map)
646 res->start = res->end;
647}
648
649/*
650 * Adjust an existing IO region allocation, but making sure that we don't
651 * encroach outside the resources which the user supplied.
652 */
653static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_start,
654 unsigned long r_end, struct pcmcia_socket *s)
655{
656 struct resource_map *m;
657 struct socket_data *s_data = s->resource_data;
658 int ret = -ENOMEM;
659
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660 for (m = s_data->io_db.next; m != &s_data->io_db; m = m->next) {
661 unsigned long start = m->base;
662 unsigned long end = m->base + m->num - 1;
663
664 if (start > r_start || r_end > end)
665 continue;
666
667 ret = adjust_resource(res, r_start, r_end - r_start + 1);
668 break;
669 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670
671 return ret;
672}
673
674/*======================================================================
675
676 These find ranges of I/O ports or memory addresses that are not
677 currently allocated by other devices.
678
679 The 'align' field should reflect the number of bits of address
680 that need to be preserved from the initial value of *base. It
681 should be a power of two, greater than or equal to 'num'. A value
682 of 0 means that all bits of *base are significant. *base should
683 also be strictly less than 'align'.
684
685======================================================================*/
686
Dominik Brodowskie94e15f2005-06-27 16:28:15 -0700687static struct resource *nonstatic_find_io_region(unsigned long base, int num,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 unsigned long align, struct pcmcia_socket *s)
689{
Kay Sievers25096982008-11-01 11:46:06 +0100690 struct resource *res = make_resource(0, num, IORESOURCE_IO, dev_name(&s->dev));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 struct socket_data *s_data = s->resource_data;
692 struct pcmcia_align_data data;
693 unsigned long min = base;
694 int ret;
695
696 if (align == 0)
697 align = 0x10000;
698
699 data.mask = align - 1;
700 data.offset = base & data.mask;
701 data.map = &s_data->io_db;
702
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703#ifdef CONFIG_PCI
704 if (s->cb_dev) {
705 ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1,
706 min, 0, pcmcia_align, &data);
707 } else
708#endif
709 ret = allocate_resource(&ioport_resource, res, num, min, ~0UL,
710 1, pcmcia_align, &data);
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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743#ifdef CONFIG_PCI
744 if (s->cb_dev) {
745 ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num,
746 1, min, 0,
747 pcmcia_align, &data);
748 } else
749#endif
750 ret = allocate_resource(&iomem_resource, res, num, min,
751 max, 1, pcmcia_align, &data);
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 Brodowski22916632005-06-27 16:28:46 -0700774 switch (action) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 case ADD_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700776 ret = add_interval(&data->mem_db, start, size);
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100777 if (!ret)
778 do_mem_probe(s, start, size, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 break;
780 case REMOVE_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700781 ret = sub_interval(&data->mem_db, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 break;
783 default:
Dominik Brodowski22916632005-06-27 16:28:46 -0700784 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786
787 return ret;
788}
789
790
Dominik Brodowski22916632005-06-27 16:28:46 -0700791static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792{
793 struct socket_data *data = s->resource_data;
Dominik Brodowski22916632005-06-27 16:28:46 -0700794 unsigned long size = end - start + 1;
795 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796
Dominik Brodowski1146bc72005-09-28 19:28:37 +0200797 if (end < start)
Dominik Brodowski22916632005-06-27 16:28:46 -0700798 return -EINVAL;
799
800 if (end > IO_SPACE_LIMIT)
801 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802
Dominik Brodowski22916632005-06-27 16:28:46 -0700803 switch (action) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 case ADD_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700805 if (add_interval(&data->io_db, start, size) != 0) {
806 ret = -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 break;
808 }
809#ifdef CONFIG_PCMCIA_PROBE
810 if (probe_io)
Dominik Brodowski22916632005-06-27 16:28:46 -0700811 do_io_probe(s, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812#endif
813 break;
814 case REMOVE_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700815 sub_interval(&data->io_db, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 break;
817 default:
Dominik Brodowski22916632005-06-27 16:28:46 -0700818 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 break;
820 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821
822 return ret;
823}
824
825
Dominik Brodowski3c299762005-06-27 16:28:46 -0700826#ifdef CONFIG_PCI
827static int nonstatic_autoadd_resources(struct pcmcia_socket *s)
828{
829 struct resource *res;
830 int i, done = 0;
831
832 if (!s->cb_dev || !s->cb_dev->bus)
833 return -ENODEV;
834
Brian Gerst0d078f62005-10-30 14:59:20 -0800835#if defined(CONFIG_X86)
Dominik Brodowskib6d00f02005-06-27 16:29:02 -0700836 /* If this is the root bus, the risk of hitting
837 * some strange system devices which aren't protected
838 * by either ACPI resource tables or properly requested
839 * resources is too big. Therefore, don't do auto-adding
840 * of resources at the moment.
841 */
842 if (s->cb_dev->bus->number == 0)
843 return -EINVAL;
844#endif
845
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100846 for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
Dominik Brodowski3c299762005-06-27 16:28:46 -0700847 res = s->cb_dev->bus->resource[i];
848 if (!res)
849 continue;
850
851 if (res->flags & IORESOURCE_IO) {
852 if (res == &ioport_resource)
853 continue;
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200854 dev_printk(KERN_INFO, &s->cb_dev->dev,
855 "pcmcia: parent PCI bridge I/O "
856 "window: 0x%llx - 0x%llx\n",
857 (unsigned long long)res->start,
858 (unsigned long long)res->end);
Dominik Brodowski3c299762005-06-27 16:28:46 -0700859 if (!adjust_io(s, ADD_MANAGED_RESOURCE, res->start, res->end))
860 done |= IORESOURCE_IO;
861
862 }
863
864 if (res->flags & IORESOURCE_MEM) {
865 if (res == &iomem_resource)
866 continue;
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200867 dev_printk(KERN_INFO, &s->cb_dev->dev,
868 "pcmcia: parent PCI bridge Memory "
869 "window: 0x%llx - 0x%llx\n",
870 (unsigned long long)res->start,
871 (unsigned long long)res->end);
Dominik Brodowski3c299762005-06-27 16:28:46 -0700872 if (!adjust_memory(s, ADD_MANAGED_RESOURCE, res->start, res->end))
873 done |= IORESOURCE_MEM;
874 }
875 }
876
877 /* if we got at least one of IO, and one of MEM, we can be glad and
878 * activate the PCMCIA subsystem */
Dominik Brodowski54bb5672005-09-28 19:29:59 +0200879 if (done == (IORESOURCE_MEM | IORESOURCE_IO))
Dominik Brodowski3c299762005-06-27 16:28:46 -0700880 s->resource_setup_done = 1;
881
882 return 0;
883}
884
885#else
886
887static inline int nonstatic_autoadd_resources(struct pcmcia_socket *s)
888{
889 return -ENODEV;
890}
891
892#endif
893
894
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895static int nonstatic_init(struct pcmcia_socket *s)
896{
897 struct socket_data *data;
898
Dominik Brodowski8084b372005-12-11 21:18:26 +0100899 data = kzalloc(sizeof(struct socket_data), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900 if (!data)
901 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902
903 data->mem_db.next = &data->mem_db;
904 data->io_db.next = &data->io_db;
905
906 s->resource_data = (void *) data;
907
Dominik Brodowski3c299762005-06-27 16:28:46 -0700908 nonstatic_autoadd_resources(s);
909
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910 return 0;
911}
912
913static void nonstatic_release_resource_db(struct pcmcia_socket *s)
914{
915 struct socket_data *data = s->resource_data;
916 struct resource_map *p, *q;
917
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918 for (p = data->mem_db.next; p != &data->mem_db; p = q) {
919 q = p->next;
920 kfree(p);
921 }
922 for (p = data->io_db.next; p != &data->io_db; p = q) {
923 q = p->next;
924 kfree(p);
925 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926}
927
928
929struct pccard_resource_ops pccard_nonstatic_ops = {
930 .validate_mem = pcmcia_nonstatic_validate_mem,
931 .adjust_io_region = nonstatic_adjust_io_region,
932 .find_io = nonstatic_find_io_region,
933 .find_mem = nonstatic_find_mem_region,
Dominik Brodowskic5023802008-06-19 19:02:52 +0200934 .add_io = adjust_io,
935 .add_mem = adjust_memory,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 .init = nonstatic_init,
937 .exit = nonstatic_release_resource_db,
938};
939EXPORT_SYMBOL(pccard_nonstatic_ops);
940
941
942/* sysfs interface to the resource database */
943
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200944static ssize_t show_io_db(struct device *dev,
945 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200947 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948 struct socket_data *data;
949 struct resource_map *p;
950 ssize_t ret = 0;
951
Dominik Brodowskicfe5d802010-01-17 19:31:45 +0100952 mutex_lock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953 data = s->resource_data;
954
955 for (p = data->io_db.next; p != &data->io_db; p = p->next) {
956 if (ret > (PAGE_SIZE - 10))
957 continue;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100958 ret += snprintf(&buf[ret], (PAGE_SIZE - ret - 1),
959 "0x%08lx - 0x%08lx\n",
960 ((unsigned long) p->base),
961 ((unsigned long) p->base + p->num - 1));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962 }
963
Dominik Brodowskicfe5d802010-01-17 19:31:45 +0100964 mutex_unlock(&s->ops_mutex);
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100965 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966}
967
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200968static ssize_t store_io_db(struct device *dev,
969 struct device_attribute *attr,
970 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200972 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973 unsigned long start_addr, end_addr;
Dominik Brodowski22916632005-06-27 16:28:46 -0700974 unsigned int add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 ssize_t ret = 0;
976
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100977 ret = sscanf(buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100979 ret = sscanf(buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -0700980 add = REMOVE_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100982 ret = sscanf(buf, "0x%lx - 0x%lx", &start_addr,
983 &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -0700984 add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 if (ret != 2)
986 return -EINVAL;
987 }
988 }
Dominik Brodowski1146bc72005-09-28 19:28:37 +0200989 if (end_addr < start_addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 return -EINVAL;
991
Dominik Brodowskicfe5d802010-01-17 19:31:45 +0100992 mutex_lock(&s->ops_mutex);
Dominik Brodowski22916632005-06-27 16:28:46 -0700993 ret = adjust_io(s, add, start_addr, end_addr);
Dominik Brodowski3c299762005-06-27 16:28:46 -0700994 if (!ret)
995 s->resource_setup_new = 1;
Dominik Brodowskicfe5d802010-01-17 19:31:45 +0100996 mutex_unlock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997
998 return ret ? ret : count;
999}
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001000static DEVICE_ATTR(available_resources_io, 0600, show_io_db, store_io_db);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001002static ssize_t show_mem_db(struct device *dev,
1003 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001005 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006 struct socket_data *data;
1007 struct resource_map *p;
1008 ssize_t ret = 0;
1009
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001010 mutex_lock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011 data = s->resource_data;
1012
1013 for (p = data->mem_db.next; p != &data->mem_db; p = p->next) {
1014 if (ret > (PAGE_SIZE - 10))
1015 continue;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001016 ret += snprintf(&buf[ret], (PAGE_SIZE - ret - 1),
1017 "0x%08lx - 0x%08lx\n",
1018 ((unsigned long) p->base),
1019 ((unsigned long) p->base + p->num - 1));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020 }
1021
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001022 mutex_unlock(&s->ops_mutex);
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001023 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024}
1025
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001026static ssize_t store_mem_db(struct device *dev,
1027 struct device_attribute *attr,
1028 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001030 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 unsigned long start_addr, end_addr;
Dominik Brodowski22916632005-06-27 16:28:46 -07001032 unsigned int add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 ssize_t ret = 0;
1034
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001035 ret = sscanf(buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001037 ret = sscanf(buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -07001038 add = REMOVE_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001040 ret = sscanf(buf, "0x%lx - 0x%lx", &start_addr,
1041 &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -07001042 add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 if (ret != 2)
1044 return -EINVAL;
1045 }
1046 }
Dominik Brodowski1146bc72005-09-28 19:28:37 +02001047 if (end_addr < start_addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048 return -EINVAL;
1049
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001050 mutex_lock(&s->ops_mutex);
Dominik Brodowski22916632005-06-27 16:28:46 -07001051 ret = adjust_memory(s, add, start_addr, end_addr);
Dominik Brodowski3c299762005-06-27 16:28:46 -07001052 if (!ret)
1053 s->resource_setup_new = 1;
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001054 mutex_unlock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055
1056 return ret ? ret : count;
1057}
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001058static DEVICE_ATTR(available_resources_mem, 0600, show_mem_db, store_mem_db);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059
David Brownell7d578962008-06-12 12:13:55 -07001060static struct attribute *pccard_rsrc_attributes[] = {
1061 &dev_attr_available_resources_io.attr,
1062 &dev_attr_available_resources_mem.attr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 NULL,
1064};
1065
David Brownell7d578962008-06-12 12:13:55 -07001066static const struct attribute_group rsrc_attributes = {
1067 .attrs = pccard_rsrc_attributes,
1068};
1069
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001070static int __devinit pccard_sysfs_add_rsrc(struct device *dev,
Dmitry Torokhovd8539d82005-09-15 02:01:36 -05001071 struct class_interface *class_intf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001073 struct pcmcia_socket *s = dev_get_drvdata(dev);
David Brownell7d578962008-06-12 12:13:55 -07001074
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 if (s->resource_ops != &pccard_nonstatic_ops)
1076 return 0;
David Brownell7d578962008-06-12 12:13:55 -07001077 return sysfs_create_group(&dev->kobj, &rsrc_attributes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078}
1079
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001080static void __devexit pccard_sysfs_remove_rsrc(struct device *dev,
Dmitry Torokhovd8539d82005-09-15 02:01:36 -05001081 struct class_interface *class_intf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001083 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084
1085 if (s->resource_ops != &pccard_nonstatic_ops)
1086 return;
David Brownell7d578962008-06-12 12:13:55 -07001087 sysfs_remove_group(&dev->kobj, &rsrc_attributes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088}
1089
Sam Ravnborged49f5d2008-05-01 04:34:50 -07001090static struct class_interface pccard_rsrc_interface __refdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 .class = &pcmcia_socket_class,
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001092 .add_dev = &pccard_sysfs_add_rsrc,
1093 .remove_dev = __devexit_p(&pccard_sysfs_remove_rsrc),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094};
1095
1096static int __init nonstatic_sysfs_init(void)
1097{
1098 return class_interface_register(&pccard_rsrc_interface);
1099}
1100
1101static void __exit nonstatic_sysfs_exit(void)
1102{
1103 class_interface_unregister(&pccard_rsrc_interface);
1104}
1105
1106module_init(nonstatic_sysfs_init);
1107module_exit(nonstatic_sysfs_exit);