blob: c13424f7b479cb797ba3e7c5760a8cdb71026aea [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
276 s->cis_mem.res = res;
277 s->cis_virt = ioremap(res->start, s->map_size);
278 if (s->cis_virt) {
Dominik Brodowski6b8e0872010-01-12 21:42:51 +0100279 mutex_unlock(&s->ops_mutex);
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 */
Dominik Brodowski6b8e0872010-01-12 21:42:51 +0100284 mutex_lock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 iounmap(s->cis_virt);
286 s->cis_virt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 }
288 s->cis_mem.res = NULL;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100289 if ((ret) || (*count == 0))
290 return -EINVAL;
291 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292}
293
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100294/**
295 * checksum() - iomem validation function for simple memory cards
296 */
297static int checksum(struct pcmcia_socket *s, struct resource *res,
298 unsigned int *value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299{
300 pccard_mem_map map;
301 int i, a = 0, b = -1, d;
302 void __iomem *virt;
303
304 virt = ioremap(res->start, s->map_size);
305 if (virt) {
306 map.map = 0;
307 map.flags = MAP_ACTIVE;
308 map.speed = 0;
309 map.res = res;
310 map.card_start = 0;
311 s->ops->set_mem_map(s, &map);
312
313 /* Don't bother checking every word... */
314 for (i = 0; i < s->map_size; i += 44) {
315 d = readl(virt+i);
316 a += d;
317 b &= d;
318 }
319
320 map.flags = 0;
321 s->ops->set_mem_map(s, &map);
322
323 iounmap(virt);
324 }
325
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100326 if (b == -1)
327 return -EINVAL;
328
329 *value = a;
330
331 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332}
333
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100334/**
335 * do_validate_mem() - low level validate a memory region for PCMCIA use
336 * @s: PCMCIA socket to validate
337 * @base: start address of resource to check
338 * @size: size of resource to check
339 * @validate: validation function to use
340 *
341 * do_validate_mem() splits up the memory region which is to be checked
342 * into two parts. Both are passed to the @validate() function. If
343 * @validate() returns non-zero, or the value parameter to @validate()
344 * is zero, or the value parameter is different between both calls,
345 * the check fails, and -EINVAL is returned. Else, 0 is returned.
346 */
347static int do_validate_mem(struct pcmcia_socket *s,
348 unsigned long base, unsigned long size,
349 int validate (struct pcmcia_socket *s,
350 struct resource *res,
351 unsigned int *value))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352{
353 struct resource *res1, *res2;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100354 unsigned int info1 = 1, info2 = 1;
355 int ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100357 res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "PCMCIA memprobe");
358 res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM,
359 "PCMCIA memprobe");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360
361 if (res1 && res2) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100362 ret = 0;
363 if (validate) {
364 ret = validate(s, res1, &info1);
365 ret += validate(s, res2, &info2);
366 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 }
368
369 free_region(res2);
370 free_region(res1);
371
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100372 dev_dbg(&s->dev, "cs: memory probe 0x%06lx-0x%06lx: %p %p %u %u %u",
373 base, base+size-1, res1, res2, ret, info1, info2);
374
375 if ((ret) || (info1 != info2) || (info1 == 0))
376 return -EINVAL;
377
378 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379}
380
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100382/**
383 * do_mem_probe() - validate a memory region for PCMCIA use
384 * @s: PCMCIA socket to validate
385 * @base: start address of resource to check
386 * @num: size of resource to check
387 * @validate: validation function to use
388 * @fallback: validation function to use if validate fails
389 *
390 * do_mem_probe() checks a memory region for use by the PCMCIA subsystem.
391 * To do so, the area is split up into sensible parts, and then passed
392 * into the @validate() function. Only if @validate() and @fallback() fail,
393 * the area is marked as unavaibale for use by the PCMCIA subsystem. The
394 * function returns the size of the usable memory area.
395 */
396static int do_mem_probe(struct pcmcia_socket *s, u_long base, u_long num,
397 int validate (struct pcmcia_socket *s,
398 struct resource *res,
399 unsigned int *value),
400 int fallback (struct pcmcia_socket *s,
401 struct resource *res,
402 unsigned int *value))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403{
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100404 struct socket_data *s_data = s->resource_data;
405 u_long i, j, bad, fail, step;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100407 dev_printk(KERN_INFO, &s->dev, "cs: memory probe 0x%06lx-0x%06lx:",
408 base, base+num-1);
409 bad = fail = 0;
410 step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff);
411 /* don't allow too large steps */
412 if (step > 0x800000)
413 step = 0x800000;
414 /* cis_readable wants to map 2x map_size */
415 if (step < 2 * s->map_size)
416 step = 2 * s->map_size;
417 for (i = j = base; i < base+num; i = j + step) {
418 if (!fail) {
419 for (j = i; j < base+num; j += step) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100420 if (!do_validate_mem(s, j, step, validate))
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100421 break;
422 }
423 fail = ((i == base) && (j == base+num));
424 }
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100425 if ((fail) && (fallback)) {
426 for (j = i; j < base+num; j += step)
427 if (!do_validate_mem(s, j, step, fallback))
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100428 break;
429 }
430 if (i != j) {
431 if (!bad)
432 printk(" excluding");
433 printk(" %#05lx-%#05lx", i, j-1);
434 sub_interval(&s_data->mem_db, i, j-i);
435 bad += j-i;
436 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100438 printk(bad ? "\n" : " clean.\n");
439 return num - bad;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440}
441
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100442
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443#ifdef CONFIG_PCMCIA_PROBE
444
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100445/**
446 * inv_probe() - top-to-bottom search for one usuable high memory area
447 * @s: PCMCIA socket to validate
448 * @m: resource_map to check
449 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450static u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s)
451{
Dominik Brodowskide759142005-09-28 19:41:56 +0200452 struct socket_data *s_data = s->resource_data;
453 u_long ok;
454 if (m == &s_data->mem_db)
455 return 0;
456 ok = inv_probe(m->next, s);
457 if (ok) {
458 if (m->base >= 0x100000)
459 sub_interval(&s_data->mem_db, m->base, m->num);
460 return ok;
461 }
462 if (m->base < 0x100000)
463 return 0;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100464 return do_mem_probe(s, m->base, m->num, readable, checksum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465}
466
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100467/**
468 * validate_mem() - memory probe function
469 * @s: PCMCIA socket to validate
470 * @probe_mask: MEM_PROBE_LOW | MEM_PROBE_HIGH
471 *
472 * The memory probe. If the memory list includes a 64K-aligned block
473 * below 1MB, we probe in 64K chunks, and as soon as we accumulate at
474 * least mem_limit free space, we quit. Returns 0 on usuable ports.
475 */
Dominik Brodowskide759142005-09-28 19:41:56 +0200476static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477{
Dominik Brodowskide759142005-09-28 19:41:56 +0200478 struct resource_map *m, mm;
479 static unsigned char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 };
480 unsigned long b, i, ok = 0;
481 struct socket_data *s_data = s->resource_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482
Dominik Brodowskide759142005-09-28 19:41:56 +0200483 /* We do up to four passes through the list */
484 if (probe_mask & MEM_PROBE_HIGH) {
485 if (inv_probe(s_data->mem_db.next, s) > 0)
486 return 0;
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200487 dev_printk(KERN_NOTICE, &s->dev,
488 "cs: warning: no high memory space available!\n");
Dominik Brodowskide759142005-09-28 19:41:56 +0200489 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 }
Dominik Brodowskide759142005-09-28 19:41:56 +0200491
492 for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
493 mm = *m;
494 /* Only probe < 1 MB */
495 if (mm.base >= 0x100000)
496 continue;
497 if ((mm.base | mm.num) & 0xffff) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100498 ok += do_mem_probe(s, mm.base, mm.num, readable,
499 checksum);
Dominik Brodowskide759142005-09-28 19:41:56 +0200500 continue;
501 }
502 /* Special probe for 64K-aligned block */
503 for (i = 0; i < 4; i++) {
504 b = order[i] << 12;
505 if ((b >= mm.base) && (b+0x10000 <= mm.base+mm.num)) {
506 if (ok >= mem_limit)
507 sub_interval(&s_data->mem_db, b, 0x10000);
508 else
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100509 ok += do_mem_probe(s, b, 0x10000,
510 readable, checksum);
Dominik Brodowskide759142005-09-28 19:41:56 +0200511 }
512 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 }
Dominik Brodowskide759142005-09-28 19:41:56 +0200514
515 if (ok > 0)
516 return 0;
517
518 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519}
520
521#else /* CONFIG_PCMCIA_PROBE */
522
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100523/**
524 * validate_mem() - memory probe function
525 * @s: PCMCIA socket to validate
526 * @probe_mask: ignored
527 *
528 * Returns 0 on usuable ports.
529 */
Andrew Morton2cff9442005-11-16 21:29:26 -0800530static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531{
532 struct resource_map *m, mm;
533 struct socket_data *s_data = s->resource_data;
Andrew Morton2cff9442005-11-16 21:29:26 -0800534 unsigned long ok = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535
536 for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
537 mm = *m;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100538 ok += do_mem_probe(s, mm.base, mm.num, readable, checksum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 }
Andrew Morton2cff9442005-11-16 21:29:26 -0800540 if (ok > 0)
541 return 0;
542 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543}
544
545#endif /* CONFIG_PCMCIA_PROBE */
546
547
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100548/**
549 * pcmcia_nonstatic_validate_mem() - try to validate iomem for PCMCIA use
550 * @s: PCMCIA socket to validate
551 *
552 * This is tricky... when we set up CIS memory, we try to validate
553 * the memory window space allocations.
554 *
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100555 * Locking note: Must be called with skt_mutex held!
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 */
Dominik Brodowskide759142005-09-28 19:41:56 +0200557static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558{
559 struct socket_data *s_data = s->resource_data;
Dominik Brodowskide759142005-09-28 19:41:56 +0200560 unsigned int probe_mask = MEM_PROBE_LOW;
561 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562
Dominik Brodowskide759142005-09-28 19:41:56 +0200563 if (!probe_mem)
564 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565
Dominik Brodowskide759142005-09-28 19:41:56 +0200566 if (s->features & SS_CAP_PAGE_REGS)
567 probe_mask = MEM_PROBE_HIGH;
568
569 if (probe_mask & ~s_data->rsrc_mem_probe) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100570 if (s->state & SOCKET_PRESENT) {
Dominik Brodowskide759142005-09-28 19:41:56 +0200571 ret = validate_mem(s, probe_mask);
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100572 if (!ret)
573 s_data->rsrc_mem_probe |= probe_mask;
574 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 }
Dominik Brodowskide759142005-09-28 19:41:56 +0200576
Dominik Brodowskide759142005-09-28 19:41:56 +0200577 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578}
579
580struct pcmcia_align_data {
581 unsigned long mask;
582 unsigned long offset;
583 struct resource_map *map;
584};
585
586static void
587pcmcia_common_align(void *align_data, struct resource *res,
Greg Kroah-Hartman2427ddd2006-06-12 17:07:52 -0700588 resource_size_t size, resource_size_t align)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589{
590 struct pcmcia_align_data *data = align_data;
Greg Kroah-Hartman2427ddd2006-06-12 17:07:52 -0700591 resource_size_t start;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 /*
593 * Ensure that we have the correct start address
594 */
595 start = (res->start & ~data->mask) + data->offset;
596 if (start < res->start)
597 start += data->mask + 1;
598 res->start = start;
599}
600
601static void
Greg Kroah-Hartman2427ddd2006-06-12 17:07:52 -0700602pcmcia_align(void *align_data, struct resource *res, resource_size_t size,
603 resource_size_t align)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604{
605 struct pcmcia_align_data *data = align_data;
606 struct resource_map *m;
607
608 pcmcia_common_align(data, res, size, align);
609
610 for (m = data->map->next; m != data->map; m = m->next) {
611 unsigned long start = m->base;
612 unsigned long end = m->base + m->num - 1;
613
614 /*
615 * If the lower resources are not available, try aligning
616 * to this entry of the resource database to see if it'll
617 * fit here.
618 */
619 if (res->start < start) {
620 res->start = start;
621 pcmcia_common_align(data, res, size, align);
622 }
623
624 /*
625 * If we're above the area which was passed in, there's
626 * no point proceeding.
627 */
628 if (res->start >= res->end)
629 break;
630
631 if ((res->start + size - 1) <= end)
632 break;
633 }
634
635 /*
636 * If we failed to find something suitable, ensure we fail.
637 */
638 if (m == data->map)
639 res->start = res->end;
640}
641
642/*
643 * Adjust an existing IO region allocation, but making sure that we don't
644 * encroach outside the resources which the user supplied.
645 */
646static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_start,
647 unsigned long r_end, struct pcmcia_socket *s)
648{
649 struct resource_map *m;
650 struct socket_data *s_data = s->resource_data;
651 int ret = -ENOMEM;
652
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 for (m = s_data->io_db.next; m != &s_data->io_db; m = m->next) {
654 unsigned long start = m->base;
655 unsigned long end = m->base + m->num - 1;
656
657 if (start > r_start || r_end > end)
658 continue;
659
660 ret = adjust_resource(res, r_start, r_end - r_start + 1);
661 break;
662 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663
664 return ret;
665}
666
667/*======================================================================
668
669 These find ranges of I/O ports or memory addresses that are not
670 currently allocated by other devices.
671
672 The 'align' field should reflect the number of bits of address
673 that need to be preserved from the initial value of *base. It
674 should be a power of two, greater than or equal to 'num'. A value
675 of 0 means that all bits of *base are significant. *base should
676 also be strictly less than 'align'.
677
678======================================================================*/
679
Dominik Brodowskie94e15f2005-06-27 16:28:15 -0700680static struct resource *nonstatic_find_io_region(unsigned long base, int num,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 unsigned long align, struct pcmcia_socket *s)
682{
Kay Sievers25096982008-11-01 11:46:06 +0100683 struct resource *res = make_resource(0, num, IORESOURCE_IO, dev_name(&s->dev));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 struct socket_data *s_data = s->resource_data;
685 struct pcmcia_align_data data;
686 unsigned long min = base;
687 int ret;
688
689 if (align == 0)
690 align = 0x10000;
691
692 data.mask = align - 1;
693 data.offset = base & data.mask;
694 data.map = &s_data->io_db;
695
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696#ifdef CONFIG_PCI
697 if (s->cb_dev) {
698 ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1,
699 min, 0, pcmcia_align, &data);
700 } else
701#endif
702 ret = allocate_resource(&ioport_resource, res, num, min, ~0UL,
703 1, pcmcia_align, &data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704
705 if (ret != 0) {
706 kfree(res);
707 res = NULL;
708 }
709 return res;
710}
711
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100712static struct resource *nonstatic_find_mem_region(u_long base, u_long num,
Dominik Brodowskie94e15f2005-06-27 16:28:15 -0700713 u_long align, int low, struct pcmcia_socket *s)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714{
Kay Sievers25096982008-11-01 11:46:06 +0100715 struct resource *res = make_resource(0, num, IORESOURCE_MEM, dev_name(&s->dev));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 struct socket_data *s_data = s->resource_data;
717 struct pcmcia_align_data data;
718 unsigned long min, max;
719 int ret, i;
720
721 low = low || !(s->features & SS_CAP_PAGE_REGS);
722
723 data.mask = align - 1;
724 data.offset = base & data.mask;
725 data.map = &s_data->mem_db;
726
727 for (i = 0; i < 2; i++) {
728 if (low) {
729 max = 0x100000UL;
730 min = base < max ? base : 0;
731 } else {
732 max = ~0UL;
733 min = 0x100000UL + base;
734 }
735
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736#ifdef CONFIG_PCI
737 if (s->cb_dev) {
738 ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num,
739 1, min, 0,
740 pcmcia_align, &data);
741 } else
742#endif
743 ret = allocate_resource(&iomem_resource, res, num, min,
744 max, 1, pcmcia_align, &data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 if (ret == 0 || low)
746 break;
747 low = 1;
748 }
749
750 if (ret != 0) {
751 kfree(res);
752 res = NULL;
753 }
754 return res;
755}
756
757
Dominik Brodowski22916632005-06-27 16:28:46 -0700758static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 struct socket_data *data = s->resource_data;
Dominik Brodowski22916632005-06-27 16:28:46 -0700761 unsigned long size = end - start + 1;
762 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763
Dominik Brodowski1146bc72005-09-28 19:28:37 +0200764 if (end < start)
Dominik Brodowski22916632005-06-27 16:28:46 -0700765 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766
Dominik Brodowski22916632005-06-27 16:28:46 -0700767 switch (action) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 case ADD_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700769 ret = add_interval(&data->mem_db, start, size);
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100770 if (!ret)
771 do_mem_probe(s, start, size, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772 break;
773 case REMOVE_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700774 ret = sub_interval(&data->mem_db, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 break;
776 default:
Dominik Brodowski22916632005-06-27 16:28:46 -0700777 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779
780 return ret;
781}
782
783
Dominik Brodowski22916632005-06-27 16:28:46 -0700784static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785{
786 struct socket_data *data = s->resource_data;
Dominik Brodowski22916632005-06-27 16:28:46 -0700787 unsigned long size = end - start + 1;
788 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789
Dominik Brodowski1146bc72005-09-28 19:28:37 +0200790 if (end < start)
Dominik Brodowski22916632005-06-27 16:28:46 -0700791 return -EINVAL;
792
793 if (end > IO_SPACE_LIMIT)
794 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795
Dominik Brodowski22916632005-06-27 16:28:46 -0700796 switch (action) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 case ADD_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700798 if (add_interval(&data->io_db, start, size) != 0) {
799 ret = -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 break;
801 }
802#ifdef CONFIG_PCMCIA_PROBE
803 if (probe_io)
Dominik Brodowski22916632005-06-27 16:28:46 -0700804 do_io_probe(s, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805#endif
806 break;
807 case REMOVE_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700808 sub_interval(&data->io_db, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 break;
810 default:
Dominik Brodowski22916632005-06-27 16:28:46 -0700811 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 break;
813 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814
815 return ret;
816}
817
818
Dominik Brodowski3c299762005-06-27 16:28:46 -0700819#ifdef CONFIG_PCI
820static int nonstatic_autoadd_resources(struct pcmcia_socket *s)
821{
822 struct resource *res;
823 int i, done = 0;
824
825 if (!s->cb_dev || !s->cb_dev->bus)
826 return -ENODEV;
827
Brian Gerst0d078f62005-10-30 14:59:20 -0800828#if defined(CONFIG_X86)
Dominik Brodowskib6d00f02005-06-27 16:29:02 -0700829 /* If this is the root bus, the risk of hitting
830 * some strange system devices which aren't protected
831 * by either ACPI resource tables or properly requested
832 * resources is too big. Therefore, don't do auto-adding
833 * of resources at the moment.
834 */
835 if (s->cb_dev->bus->number == 0)
836 return -EINVAL;
837#endif
838
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100839 for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
Dominik Brodowski3c299762005-06-27 16:28:46 -0700840 res = s->cb_dev->bus->resource[i];
841 if (!res)
842 continue;
843
844 if (res->flags & IORESOURCE_IO) {
845 if (res == &ioport_resource)
846 continue;
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200847 dev_printk(KERN_INFO, &s->cb_dev->dev,
848 "pcmcia: parent PCI bridge I/O "
849 "window: 0x%llx - 0x%llx\n",
850 (unsigned long long)res->start,
851 (unsigned long long)res->end);
Dominik Brodowski3c299762005-06-27 16:28:46 -0700852 if (!adjust_io(s, ADD_MANAGED_RESOURCE, res->start, res->end))
853 done |= IORESOURCE_IO;
854
855 }
856
857 if (res->flags & IORESOURCE_MEM) {
858 if (res == &iomem_resource)
859 continue;
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200860 dev_printk(KERN_INFO, &s->cb_dev->dev,
861 "pcmcia: parent PCI bridge Memory "
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_memory(s, ADD_MANAGED_RESOURCE, res->start, res->end))
866 done |= IORESOURCE_MEM;
867 }
868 }
869
870 /* if we got at least one of IO, and one of MEM, we can be glad and
871 * activate the PCMCIA subsystem */
Dominik Brodowski54bb5672005-09-28 19:29:59 +0200872 if (done == (IORESOURCE_MEM | IORESOURCE_IO))
Dominik Brodowski3c299762005-06-27 16:28:46 -0700873 s->resource_setup_done = 1;
874
875 return 0;
876}
877
878#else
879
880static inline int nonstatic_autoadd_resources(struct pcmcia_socket *s)
881{
882 return -ENODEV;
883}
884
885#endif
886
887
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888static int nonstatic_init(struct pcmcia_socket *s)
889{
890 struct socket_data *data;
891
Dominik Brodowski8084b372005-12-11 21:18:26 +0100892 data = kzalloc(sizeof(struct socket_data), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893 if (!data)
894 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895
896 data->mem_db.next = &data->mem_db;
897 data->io_db.next = &data->io_db;
898
899 s->resource_data = (void *) data;
900
Dominik Brodowski3c299762005-06-27 16:28:46 -0700901 nonstatic_autoadd_resources(s);
902
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903 return 0;
904}
905
906static void nonstatic_release_resource_db(struct pcmcia_socket *s)
907{
908 struct socket_data *data = s->resource_data;
909 struct resource_map *p, *q;
910
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911 for (p = data->mem_db.next; p != &data->mem_db; p = q) {
912 q = p->next;
913 kfree(p);
914 }
915 for (p = data->io_db.next; p != &data->io_db; p = q) {
916 q = p->next;
917 kfree(p);
918 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919}
920
921
922struct pccard_resource_ops pccard_nonstatic_ops = {
923 .validate_mem = pcmcia_nonstatic_validate_mem,
924 .adjust_io_region = nonstatic_adjust_io_region,
925 .find_io = nonstatic_find_io_region,
926 .find_mem = nonstatic_find_mem_region,
Dominik Brodowskic5023802008-06-19 19:02:52 +0200927 .add_io = adjust_io,
928 .add_mem = adjust_memory,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929 .init = nonstatic_init,
930 .exit = nonstatic_release_resource_db,
931};
932EXPORT_SYMBOL(pccard_nonstatic_ops);
933
934
935/* sysfs interface to the resource database */
936
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200937static ssize_t show_io_db(struct device *dev,
938 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200940 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941 struct socket_data *data;
942 struct resource_map *p;
943 ssize_t ret = 0;
944
Dominik Brodowskicfe5d802010-01-17 19:31:45 +0100945 mutex_lock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946 data = s->resource_data;
947
948 for (p = data->io_db.next; p != &data->io_db; p = p->next) {
949 if (ret > (PAGE_SIZE - 10))
950 continue;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100951 ret += snprintf(&buf[ret], (PAGE_SIZE - ret - 1),
952 "0x%08lx - 0x%08lx\n",
953 ((unsigned long) p->base),
954 ((unsigned long) p->base + p->num - 1));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955 }
956
Dominik Brodowskicfe5d802010-01-17 19:31:45 +0100957 mutex_unlock(&s->ops_mutex);
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100958 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959}
960
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200961static ssize_t store_io_db(struct device *dev,
962 struct device_attribute *attr,
963 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200965 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 unsigned long start_addr, end_addr;
Dominik Brodowski22916632005-06-27 16:28:46 -0700967 unsigned int add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 ssize_t ret = 0;
969
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100970 ret = sscanf(buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100972 ret = sscanf(buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -0700973 add = REMOVE_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100975 ret = sscanf(buf, "0x%lx - 0x%lx", &start_addr,
976 &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -0700977 add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 if (ret != 2)
979 return -EINVAL;
980 }
981 }
Dominik Brodowski1146bc72005-09-28 19:28:37 +0200982 if (end_addr < start_addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983 return -EINVAL;
984
Dominik Brodowskicfe5d802010-01-17 19:31:45 +0100985 mutex_lock(&s->ops_mutex);
Dominik Brodowski22916632005-06-27 16:28:46 -0700986 ret = adjust_io(s, add, start_addr, end_addr);
Dominik Brodowski3c299762005-06-27 16:28:46 -0700987 if (!ret)
988 s->resource_setup_new = 1;
Dominik Brodowskicfe5d802010-01-17 19:31:45 +0100989 mutex_unlock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990
991 return ret ? ret : count;
992}
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200993static DEVICE_ATTR(available_resources_io, 0600, show_io_db, store_io_db);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200995static ssize_t show_mem_db(struct device *dev,
996 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200998 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 struct socket_data *data;
1000 struct resource_map *p;
1001 ssize_t ret = 0;
1002
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001003 mutex_lock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004 data = s->resource_data;
1005
1006 for (p = data->mem_db.next; p != &data->mem_db; p = p->next) {
1007 if (ret > (PAGE_SIZE - 10))
1008 continue;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001009 ret += snprintf(&buf[ret], (PAGE_SIZE - ret - 1),
1010 "0x%08lx - 0x%08lx\n",
1011 ((unsigned long) p->base),
1012 ((unsigned long) p->base + p->num - 1));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013 }
1014
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001015 mutex_unlock(&s->ops_mutex);
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001016 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017}
1018
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001019static ssize_t store_mem_db(struct device *dev,
1020 struct device_attribute *attr,
1021 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001023 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024 unsigned long start_addr, end_addr;
Dominik Brodowski22916632005-06-27 16:28:46 -07001025 unsigned int add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026 ssize_t ret = 0;
1027
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001028 ret = sscanf(buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001030 ret = sscanf(buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -07001031 add = REMOVE_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001033 ret = sscanf(buf, "0x%lx - 0x%lx", &start_addr,
1034 &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -07001035 add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 if (ret != 2)
1037 return -EINVAL;
1038 }
1039 }
Dominik Brodowski1146bc72005-09-28 19:28:37 +02001040 if (end_addr < start_addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041 return -EINVAL;
1042
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001043 mutex_lock(&s->ops_mutex);
Dominik Brodowski22916632005-06-27 16:28:46 -07001044 ret = adjust_memory(s, add, start_addr, end_addr);
Dominik Brodowski3c299762005-06-27 16:28:46 -07001045 if (!ret)
1046 s->resource_setup_new = 1;
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001047 mutex_unlock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048
1049 return ret ? ret : count;
1050}
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001051static DEVICE_ATTR(available_resources_mem, 0600, show_mem_db, store_mem_db);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052
David Brownell7d578962008-06-12 12:13:55 -07001053static struct attribute *pccard_rsrc_attributes[] = {
1054 &dev_attr_available_resources_io.attr,
1055 &dev_attr_available_resources_mem.attr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056 NULL,
1057};
1058
David Brownell7d578962008-06-12 12:13:55 -07001059static const struct attribute_group rsrc_attributes = {
1060 .attrs = pccard_rsrc_attributes,
1061};
1062
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001063static int __devinit pccard_sysfs_add_rsrc(struct device *dev,
Dmitry Torokhovd8539d82005-09-15 02:01:36 -05001064 struct class_interface *class_intf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001066 struct pcmcia_socket *s = dev_get_drvdata(dev);
David Brownell7d578962008-06-12 12:13:55 -07001067
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068 if (s->resource_ops != &pccard_nonstatic_ops)
1069 return 0;
David Brownell7d578962008-06-12 12:13:55 -07001070 return sysfs_create_group(&dev->kobj, &rsrc_attributes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071}
1072
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001073static void __devexit pccard_sysfs_remove_rsrc(struct device *dev,
Dmitry Torokhovd8539d82005-09-15 02:01:36 -05001074 struct class_interface *class_intf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001076 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077
1078 if (s->resource_ops != &pccard_nonstatic_ops)
1079 return;
David Brownell7d578962008-06-12 12:13:55 -07001080 sysfs_remove_group(&dev->kobj, &rsrc_attributes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081}
1082
Sam Ravnborged49f5d2008-05-01 04:34:50 -07001083static struct class_interface pccard_rsrc_interface __refdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 .class = &pcmcia_socket_class,
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001085 .add_dev = &pccard_sysfs_add_rsrc,
1086 .remove_dev = __devexit_p(&pccard_sysfs_remove_rsrc),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087};
1088
1089static int __init nonstatic_sysfs_init(void)
1090{
1091 return class_interface_register(&pccard_rsrc_interface);
1092}
1093
1094static void __exit nonstatic_sysfs_exit(void)
1095{
1096 class_interface_unregister(&pccard_rsrc_interface);
1097}
1098
1099module_init(nonstatic_sysfs_init);
1100module_exit(nonstatic_sysfs_exit);