blob: 1178a823fbc64cfddecf12241cc81cff8751d043 [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;
Dominik Brodowski7b4884c2010-02-17 16:25:53 +010058 struct resource_map mem_db_valid;
Linus Torvalds1da177e2005-04-16 15:20:36 -070059 struct resource_map io_db;
Linus Torvalds1da177e2005-04-16 15:20:36 -070060};
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) {
Dominik Brodowski509b0862010-04-08 19:23:07 +0200217 res = claim_region(s, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100218 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) {
Dominik Brodowski509b0862010-04-08 19:23:07 +0200234 res = claim_region(s, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
235 if (!res) {
236 if (!any)
237 printk(" excluding");
238 if (!bad)
239 bad = any = i;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100240 continue;
Dominik Brodowski509b0862010-04-08 19:23:07 +0200241 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100242 for (j = 0; j < 8; j++)
243 if (inb(i+j) != most)
244 break;
245 free_region(res);
246 if (j < 8) {
247 if (!any)
248 printk(" excluding");
249 if (!bad)
250 bad = any = i;
251 } else {
252 if (bad) {
253 sub_interval(&s_data->io_db, bad, i-bad);
254 printk(" %#x-%#x", bad, i-1);
255 bad = 0;
256 }
257 }
258 }
259 if (bad) {
260 if ((num > 16) && (bad == base) && (i == base+num)) {
Dominik Brodowski509b0862010-04-08 19:23:07 +0200261 sub_interval(&s_data->io_db, bad, i-bad);
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100262 printk(" nothing: probe failed.\n");
263 return;
264 } else {
265 sub_interval(&s_data->io_db, bad, i-bad);
266 printk(" %#x-%#x", bad, i-1);
267 }
268 }
269
270 printk(any ? "\n" : " clean.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271}
272#endif
273
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100274/*======================================================================*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100276/**
277 * readable() - iomem validation function for cards with a valid CIS
278 */
Dominik Brodowskic5081d52008-06-19 20:12:34 +0200279static int readable(struct pcmcia_socket *s, struct resource *res,
280 unsigned int *count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281{
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100282 int ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283
Dominik Brodowski7ab248552010-02-17 18:00:07 +0100284 if (s->fake_cis) {
285 dev_dbg(&s->dev, "fake CIS is being used: can't validate mem\n");
286 return 0;
287 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288
289 s->cis_mem.res = res;
290 s->cis_virt = ioremap(res->start, s->map_size);
291 if (s->cis_virt) {
Dominik Brodowski6b8e0872010-01-12 21:42:51 +0100292 mutex_unlock(&s->ops_mutex);
Dominik Brodowski6e7b51a2010-01-06 13:57:43 +0100293 /* as we're only called from pcmcia.c, we're safe */
294 if (s->callback->validate)
295 ret = s->callback->validate(s, count);
Dominik Brodowski904e3772010-01-02 12:28:04 +0100296 /* invalidate mapping */
Dominik Brodowski6b8e0872010-01-12 21:42:51 +0100297 mutex_lock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 iounmap(s->cis_virt);
299 s->cis_virt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 }
301 s->cis_mem.res = NULL;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100302 if ((ret) || (*count == 0))
303 return -EINVAL;
304 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305}
306
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100307/**
308 * checksum() - iomem validation function for simple memory cards
309 */
310static int checksum(struct pcmcia_socket *s, struct resource *res,
311 unsigned int *value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312{
313 pccard_mem_map map;
314 int i, a = 0, b = -1, d;
315 void __iomem *virt;
316
317 virt = ioremap(res->start, s->map_size);
318 if (virt) {
319 map.map = 0;
320 map.flags = MAP_ACTIVE;
321 map.speed = 0;
322 map.res = res;
323 map.card_start = 0;
324 s->ops->set_mem_map(s, &map);
325
326 /* Don't bother checking every word... */
327 for (i = 0; i < s->map_size; i += 44) {
328 d = readl(virt+i);
329 a += d;
330 b &= d;
331 }
332
333 map.flags = 0;
334 s->ops->set_mem_map(s, &map);
335
336 iounmap(virt);
337 }
338
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100339 if (b == -1)
340 return -EINVAL;
341
342 *value = a;
343
344 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345}
346
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100347/**
348 * do_validate_mem() - low level validate a memory region for PCMCIA use
349 * @s: PCMCIA socket to validate
350 * @base: start address of resource to check
351 * @size: size of resource to check
352 * @validate: validation function to use
353 *
354 * do_validate_mem() splits up the memory region which is to be checked
355 * into two parts. Both are passed to the @validate() function. If
356 * @validate() returns non-zero, or the value parameter to @validate()
357 * is zero, or the value parameter is different between both calls,
358 * the check fails, and -EINVAL is returned. Else, 0 is returned.
359 */
360static int do_validate_mem(struct pcmcia_socket *s,
361 unsigned long base, unsigned long size,
362 int validate (struct pcmcia_socket *s,
363 struct resource *res,
364 unsigned int *value))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365{
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100366 struct socket_data *s_data = s->resource_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 struct resource *res1, *res2;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100368 unsigned int info1 = 1, info2 = 1;
369 int ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100371 res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "PCMCIA memprobe");
372 res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM,
373 "PCMCIA memprobe");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374
375 if (res1 && res2) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100376 ret = 0;
377 if (validate) {
378 ret = validate(s, res1, &info1);
379 ret += validate(s, res2, &info2);
380 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 }
382
383 free_region(res2);
384 free_region(res1);
385
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100386 dev_dbg(&s->dev, "cs: memory probe 0x%06lx-0x%06lx: %p %p %u %u %u",
387 base, base+size-1, res1, res2, ret, info1, info2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100389 if ((ret) || (info1 != info2) || (info1 == 0))
390 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100392 if (validate && !s->fake_cis) {
393 /* move it to the validated data set */
394 add_interval(&s_data->mem_db_valid, base, size);
395 sub_interval(&s_data->mem_db, base, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 }
397
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100398 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399}
400
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100402/**
403 * do_mem_probe() - validate a memory region for PCMCIA use
404 * @s: PCMCIA socket to validate
405 * @base: start address of resource to check
406 * @num: size of resource to check
407 * @validate: validation function to use
408 * @fallback: validation function to use if validate fails
409 *
410 * do_mem_probe() checks a memory region for use by the PCMCIA subsystem.
411 * To do so, the area is split up into sensible parts, and then passed
412 * into the @validate() function. Only if @validate() and @fallback() fail,
413 * the area is marked as unavaibale for use by the PCMCIA subsystem. The
414 * function returns the size of the usable memory area.
415 */
416static int do_mem_probe(struct pcmcia_socket *s, u_long base, u_long num,
417 int validate (struct pcmcia_socket *s,
418 struct resource *res,
419 unsigned int *value),
420 int fallback (struct pcmcia_socket *s,
421 struct resource *res,
422 unsigned int *value))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423{
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100424 struct socket_data *s_data = s->resource_data;
425 u_long i, j, bad, fail, step;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100427 dev_printk(KERN_INFO, &s->dev, "cs: memory probe 0x%06lx-0x%06lx:",
428 base, base+num-1);
429 bad = fail = 0;
430 step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff);
431 /* don't allow too large steps */
432 if (step > 0x800000)
433 step = 0x800000;
434 /* cis_readable wants to map 2x map_size */
435 if (step < 2 * s->map_size)
436 step = 2 * s->map_size;
437 for (i = j = base; i < base+num; i = j + step) {
438 if (!fail) {
439 for (j = i; j < base+num; j += step) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100440 if (!do_validate_mem(s, j, step, validate))
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100441 break;
442 }
443 fail = ((i == base) && (j == base+num));
444 }
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100445 if ((fail) && (fallback)) {
446 for (j = i; j < base+num; j += step)
447 if (!do_validate_mem(s, j, step, fallback))
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100448 break;
449 }
450 if (i != j) {
451 if (!bad)
452 printk(" excluding");
453 printk(" %#05lx-%#05lx", i, j-1);
454 sub_interval(&s_data->mem_db, i, j-i);
455 bad += j-i;
456 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100458 printk(bad ? "\n" : " clean.\n");
459 return num - bad;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460}
461
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100462
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463#ifdef CONFIG_PCMCIA_PROBE
464
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100465/**
466 * inv_probe() - top-to-bottom search for one usuable high memory area
467 * @s: PCMCIA socket to validate
468 * @m: resource_map to check
469 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470static u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s)
471{
Dominik Brodowskide759142005-09-28 19:41:56 +0200472 struct socket_data *s_data = s->resource_data;
473 u_long ok;
474 if (m == &s_data->mem_db)
475 return 0;
476 ok = inv_probe(m->next, s);
477 if (ok) {
478 if (m->base >= 0x100000)
479 sub_interval(&s_data->mem_db, m->base, m->num);
480 return ok;
481 }
482 if (m->base < 0x100000)
483 return 0;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100484 return do_mem_probe(s, m->base, m->num, readable, checksum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485}
486
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100487/**
488 * validate_mem() - memory probe function
489 * @s: PCMCIA socket to validate
490 * @probe_mask: MEM_PROBE_LOW | MEM_PROBE_HIGH
491 *
492 * The memory probe. If the memory list includes a 64K-aligned block
493 * below 1MB, we probe in 64K chunks, and as soon as we accumulate at
494 * least mem_limit free space, we quit. Returns 0 on usuable ports.
495 */
Dominik Brodowskide759142005-09-28 19:41:56 +0200496static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497{
Dominik Brodowskide759142005-09-28 19:41:56 +0200498 struct resource_map *m, mm;
499 static unsigned char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 };
500 unsigned long b, i, ok = 0;
501 struct socket_data *s_data = s->resource_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502
Dominik Brodowskide759142005-09-28 19:41:56 +0200503 /* We do up to four passes through the list */
504 if (probe_mask & MEM_PROBE_HIGH) {
505 if (inv_probe(s_data->mem_db.next, s) > 0)
506 return 0;
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100507 if (s_data->mem_db_valid.next != &s_data->mem_db_valid)
508 return 0;
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200509 dev_printk(KERN_NOTICE, &s->dev,
510 "cs: warning: no high memory space available!\n");
Dominik Brodowskide759142005-09-28 19:41:56 +0200511 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 }
Dominik Brodowskide759142005-09-28 19:41:56 +0200513
514 for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
515 mm = *m;
516 /* Only probe < 1 MB */
517 if (mm.base >= 0x100000)
518 continue;
519 if ((mm.base | mm.num) & 0xffff) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100520 ok += do_mem_probe(s, mm.base, mm.num, readable,
521 checksum);
Dominik Brodowskide759142005-09-28 19:41:56 +0200522 continue;
523 }
524 /* Special probe for 64K-aligned block */
525 for (i = 0; i < 4; i++) {
526 b = order[i] << 12;
527 if ((b >= mm.base) && (b+0x10000 <= mm.base+mm.num)) {
528 if (ok >= mem_limit)
529 sub_interval(&s_data->mem_db, b, 0x10000);
530 else
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100531 ok += do_mem_probe(s, b, 0x10000,
532 readable, checksum);
Dominik Brodowskide759142005-09-28 19:41:56 +0200533 }
534 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 }
Dominik Brodowskide759142005-09-28 19:41:56 +0200536
537 if (ok > 0)
538 return 0;
539
540 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541}
542
543#else /* CONFIG_PCMCIA_PROBE */
544
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100545/**
546 * validate_mem() - memory probe function
547 * @s: PCMCIA socket to validate
548 * @probe_mask: ignored
549 *
550 * Returns 0 on usuable ports.
551 */
Andrew Morton2cff9442005-11-16 21:29:26 -0800552static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553{
554 struct resource_map *m, mm;
555 struct socket_data *s_data = s->resource_data;
Andrew Morton2cff9442005-11-16 21:29:26 -0800556 unsigned long ok = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557
558 for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
559 mm = *m;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100560 ok += do_mem_probe(s, mm.base, mm.num, readable, checksum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 }
Andrew Morton2cff9442005-11-16 21:29:26 -0800562 if (ok > 0)
563 return 0;
564 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565}
566
567#endif /* CONFIG_PCMCIA_PROBE */
568
569
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100570/**
571 * pcmcia_nonstatic_validate_mem() - try to validate iomem for PCMCIA use
572 * @s: PCMCIA socket to validate
573 *
574 * This is tricky... when we set up CIS memory, we try to validate
575 * the memory window space allocations.
576 *
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100577 * Locking note: Must be called with skt_mutex held!
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 */
Dominik Brodowskide759142005-09-28 19:41:56 +0200579static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580{
581 struct socket_data *s_data = s->resource_data;
Dominik Brodowskide759142005-09-28 19:41:56 +0200582 unsigned int probe_mask = MEM_PROBE_LOW;
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100583 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100585 if (!probe_mem || !(s->state & SOCKET_PRESENT))
Dominik Brodowskide759142005-09-28 19:41:56 +0200586 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587
Dominik Brodowskide759142005-09-28 19:41:56 +0200588 if (s->features & SS_CAP_PAGE_REGS)
589 probe_mask = MEM_PROBE_HIGH;
590
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100591 ret = validate_mem(s, probe_mask);
Dominik Brodowskide759142005-09-28 19:41:56 +0200592
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100593 if (s_data->mem_db_valid.next != &s_data->mem_db_valid)
594 return 0;
Dominik Brodowskide759142005-09-28 19:41:56 +0200595
596 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597}
598
599struct pcmcia_align_data {
600 unsigned long mask;
601 unsigned long offset;
602 struct resource_map *map;
603};
604
Dominik Brodowski147a2742010-04-04 18:10:35 +0200605static resource_size_t pcmcia_common_align(struct pcmcia_align_data *align_data,
606 resource_size_t start)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607{
Dominik Brodowski147a2742010-04-04 18:10:35 +0200608 resource_size_t ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 /*
610 * Ensure that we have the correct start address
611 */
Dominik Brodowski147a2742010-04-04 18:10:35 +0200612 ret = (start & ~align_data->mask) + align_data->offset;
613 if (ret < start)
614 ret += align_data->mask + 1;
615 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616}
617
Dominik Brodowskib26b2d42010-01-01 17:40:49 +0100618static resource_size_t
Dominik Brodowski3b7a17f2010-01-01 17:40:50 +0100619pcmcia_align(void *align_data, const struct resource *res,
620 resource_size_t size, resource_size_t align)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621{
622 struct pcmcia_align_data *data = align_data;
623 struct resource_map *m;
Dominik Brodowskib26b2d42010-01-01 17:40:49 +0100624 resource_size_t start;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625
Dominik Brodowski147a2742010-04-04 18:10:35 +0200626 start = pcmcia_common_align(data, res->start);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627
628 for (m = data->map->next; m != data->map; m = m->next) {
Dominik Brodowski147a2742010-04-04 18:10:35 +0200629 unsigned long map_start = m->base;
630 unsigned long map_end = m->base + m->num - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631
632 /*
633 * If the lower resources are not available, try aligning
634 * to this entry of the resource database to see if it'll
635 * fit here.
636 */
Dominik Brodowski147a2742010-04-04 18:10:35 +0200637 if (start < map_start)
638 start = pcmcia_common_align(data, map_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639
640 /*
641 * If we're above the area which was passed in, there's
642 * no point proceeding.
643 */
Dominik Brodowski147a2742010-04-04 18:10:35 +0200644 if (start >= res->end)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 break;
646
Dominik Brodowski147a2742010-04-04 18:10:35 +0200647 if ((start + size - 1) <= map_end)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 break;
649 }
650
651 /*
652 * If we failed to find something suitable, ensure we fail.
653 */
654 if (m == data->map)
Dominik Brodowskib26b2d42010-01-01 17:40:49 +0100655 start = res->end;
656
657 return start;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658}
659
660/*
661 * Adjust an existing IO region allocation, but making sure that we don't
662 * encroach outside the resources which the user supplied.
663 */
664static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_start,
665 unsigned long r_end, struct pcmcia_socket *s)
666{
667 struct resource_map *m;
668 struct socket_data *s_data = s->resource_data;
669 int ret = -ENOMEM;
670
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 for (m = s_data->io_db.next; m != &s_data->io_db; m = m->next) {
672 unsigned long start = m->base;
673 unsigned long end = m->base + m->num - 1;
674
675 if (start > r_start || r_end > end)
676 continue;
677
678 ret = adjust_resource(res, r_start, r_end - r_start + 1);
679 break;
680 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681
682 return ret;
683}
684
685/*======================================================================
686
687 These find ranges of I/O ports or memory addresses that are not
688 currently allocated by other devices.
689
690 The 'align' field should reflect the number of bits of address
691 that need to be preserved from the initial value of *base. It
692 should be a power of two, greater than or equal to 'num'. A value
693 of 0 means that all bits of *base are significant. *base should
694 also be strictly less than 'align'.
695
696======================================================================*/
697
Dominik Brodowskie94e15f2005-06-27 16:28:15 -0700698static struct resource *nonstatic_find_io_region(unsigned long base, int num,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 unsigned long align, struct pcmcia_socket *s)
700{
Kay Sievers25096982008-11-01 11:46:06 +0100701 struct resource *res = make_resource(0, num, IORESOURCE_IO, dev_name(&s->dev));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 struct socket_data *s_data = s->resource_data;
703 struct pcmcia_align_data data;
704 unsigned long min = base;
705 int ret;
706
707 if (align == 0)
708 align = 0x10000;
709
710 data.mask = align - 1;
711 data.offset = base & data.mask;
712 data.map = &s_data->io_db;
713
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714#ifdef CONFIG_PCI
715 if (s->cb_dev) {
716 ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1,
717 min, 0, pcmcia_align, &data);
718 } else
719#endif
720 ret = allocate_resource(&ioport_resource, res, num, min, ~0UL,
721 1, pcmcia_align, &data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722
723 if (ret != 0) {
724 kfree(res);
725 res = NULL;
726 }
727 return res;
728}
729
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100730static struct resource *nonstatic_find_mem_region(u_long base, u_long num,
Dominik Brodowskie94e15f2005-06-27 16:28:15 -0700731 u_long align, int low, struct pcmcia_socket *s)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732{
Kay Sievers25096982008-11-01 11:46:06 +0100733 struct resource *res = make_resource(0, num, IORESOURCE_MEM, dev_name(&s->dev));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 struct socket_data *s_data = s->resource_data;
735 struct pcmcia_align_data data;
736 unsigned long min, max;
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100737 int ret, i, j;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738
739 low = low || !(s->features & SS_CAP_PAGE_REGS);
740
741 data.mask = align - 1;
742 data.offset = base & data.mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743
744 for (i = 0; i < 2; i++) {
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100745 data.map = &s_data->mem_db_valid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746 if (low) {
747 max = 0x100000UL;
748 min = base < max ? base : 0;
749 } else {
750 max = ~0UL;
751 min = 0x100000UL + base;
752 }
753
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100754 for (j = 0; j < 2; j++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755#ifdef CONFIG_PCI
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100756 if (s->cb_dev) {
757 ret = pci_bus_alloc_resource(s->cb_dev->bus,
758 res, num, 1, min, 0,
759 pcmcia_align, &data);
760 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761#endif
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100762 {
763 ret = allocate_resource(&iomem_resource,
764 res, num, min, max, 1,
765 pcmcia_align, &data);
766 }
767 if (ret == 0)
768 break;
769 data.map = &s_data->mem_db;
770 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 if (ret == 0 || low)
772 break;
773 low = 1;
774 }
775
776 if (ret != 0) {
777 kfree(res);
778 res = NULL;
779 }
780 return res;
781}
782
783
Dominik Brodowski22916632005-06-27 16:28:46 -0700784static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792
Dominik Brodowski22916632005-06-27 16:28:46 -0700793 switch (action) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 case ADD_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700795 ret = add_interval(&data->mem_db, start, size);
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100796 if (!ret)
797 do_mem_probe(s, start, size, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 break;
799 case REMOVE_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700800 ret = sub_interval(&data->mem_db, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 break;
802 default:
Dominik Brodowski22916632005-06-27 16:28:46 -0700803 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805
806 return ret;
807}
808
809
Dominik Brodowski22916632005-06-27 16:28:46 -0700810static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811{
812 struct socket_data *data = s->resource_data;
Dominik Brodowski22916632005-06-27 16:28:46 -0700813 unsigned long size = end - start + 1;
814 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815
Dominik Brodowski9713ab22010-03-23 16:05:00 +0100816#if defined(CONFIG_X86)
817 /* on x86, avoid anything < 0x100 for it is often used for
818 * legacy platform devices */
819 if (start < 0x100)
820 start = 0x100;
821#endif
822
Dominik Brodowski1146bc72005-09-28 19:28:37 +0200823 if (end < start)
Dominik Brodowski22916632005-06-27 16:28:46 -0700824 return -EINVAL;
825
826 if (end > IO_SPACE_LIMIT)
827 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828
Dominik Brodowski22916632005-06-27 16:28:46 -0700829 switch (action) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 case ADD_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700831 if (add_interval(&data->io_db, start, size) != 0) {
832 ret = -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 break;
834 }
835#ifdef CONFIG_PCMCIA_PROBE
836 if (probe_io)
Dominik Brodowski22916632005-06-27 16:28:46 -0700837 do_io_probe(s, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838#endif
839 break;
840 case REMOVE_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700841 sub_interval(&data->io_db, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 break;
843 default:
Dominik Brodowski22916632005-06-27 16:28:46 -0700844 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 break;
846 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847
848 return ret;
849}
850
851
Dominik Brodowski3c299762005-06-27 16:28:46 -0700852#ifdef CONFIG_PCI
853static int nonstatic_autoadd_resources(struct pcmcia_socket *s)
854{
855 struct resource *res;
856 int i, done = 0;
857
858 if (!s->cb_dev || !s->cb_dev->bus)
859 return -ENODEV;
860
Brian Gerst0d078f62005-10-30 14:59:20 -0800861#if defined(CONFIG_X86)
Dominik Brodowskib6d00f02005-06-27 16:29:02 -0700862 /* If this is the root bus, the risk of hitting
863 * some strange system devices which aren't protected
864 * by either ACPI resource tables or properly requested
865 * resources is too big. Therefore, don't do auto-adding
866 * of resources at the moment.
867 */
868 if (s->cb_dev->bus->number == 0)
869 return -EINVAL;
870#endif
871
Bjorn Helgaas89a74ec2010-02-23 10:24:31 -0700872 pci_bus_for_each_resource(s->cb_dev->bus, res, i) {
Dominik Brodowski3c299762005-06-27 16:28:46 -0700873 if (!res)
874 continue;
875
876 if (res->flags & IORESOURCE_IO) {
877 if (res == &ioport_resource)
878 continue;
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200879 dev_printk(KERN_INFO, &s->cb_dev->dev,
Bjorn Helgaase1944c62010-03-16 15:53:08 -0600880 "pcmcia: parent PCI bridge window: %pR\n",
881 res);
Dominik Brodowski3c299762005-06-27 16:28:46 -0700882 if (!adjust_io(s, ADD_MANAGED_RESOURCE, res->start, res->end))
883 done |= IORESOURCE_IO;
884
885 }
886
887 if (res->flags & IORESOURCE_MEM) {
888 if (res == &iomem_resource)
889 continue;
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200890 dev_printk(KERN_INFO, &s->cb_dev->dev,
Bjorn Helgaase1944c62010-03-16 15:53:08 -0600891 "pcmcia: parent PCI bridge window: %pR\n",
892 res);
Dominik Brodowski3c299762005-06-27 16:28:46 -0700893 if (!adjust_memory(s, ADD_MANAGED_RESOURCE, res->start, res->end))
894 done |= IORESOURCE_MEM;
895 }
896 }
897
898 /* if we got at least one of IO, and one of MEM, we can be glad and
899 * activate the PCMCIA subsystem */
Dominik Brodowski54bb5672005-09-28 19:29:59 +0200900 if (done == (IORESOURCE_MEM | IORESOURCE_IO))
Dominik Brodowski3c299762005-06-27 16:28:46 -0700901 s->resource_setup_done = 1;
902
903 return 0;
904}
905
906#else
907
908static inline int nonstatic_autoadd_resources(struct pcmcia_socket *s)
909{
910 return -ENODEV;
911}
912
913#endif
914
915
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916static int nonstatic_init(struct pcmcia_socket *s)
917{
918 struct socket_data *data;
919
Dominik Brodowski8084b372005-12-11 21:18:26 +0100920 data = kzalloc(sizeof(struct socket_data), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921 if (!data)
922 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923
924 data->mem_db.next = &data->mem_db;
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100925 data->mem_db_valid.next = &data->mem_db_valid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 data->io_db.next = &data->io_db;
927
928 s->resource_data = (void *) data;
929
Dominik Brodowski3c299762005-06-27 16:28:46 -0700930 nonstatic_autoadd_resources(s);
931
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932 return 0;
933}
934
935static void nonstatic_release_resource_db(struct pcmcia_socket *s)
936{
937 struct socket_data *data = s->resource_data;
938 struct resource_map *p, *q;
939
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100940 for (p = data->mem_db_valid.next; p != &data->mem_db_valid; p = q) {
941 q = p->next;
942 kfree(p);
943 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 for (p = data->mem_db.next; p != &data->mem_db; p = q) {
945 q = p->next;
946 kfree(p);
947 }
948 for (p = data->io_db.next; p != &data->io_db; p = q) {
949 q = p->next;
950 kfree(p);
951 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952}
953
954
955struct pccard_resource_ops pccard_nonstatic_ops = {
956 .validate_mem = pcmcia_nonstatic_validate_mem,
957 .adjust_io_region = nonstatic_adjust_io_region,
958 .find_io = nonstatic_find_io_region,
959 .find_mem = nonstatic_find_mem_region,
Dominik Brodowskic5023802008-06-19 19:02:52 +0200960 .add_io = adjust_io,
961 .add_mem = adjust_memory,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962 .init = nonstatic_init,
963 .exit = nonstatic_release_resource_db,
964};
965EXPORT_SYMBOL(pccard_nonstatic_ops);
966
967
968/* sysfs interface to the resource database */
969
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200970static ssize_t show_io_db(struct device *dev,
971 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200973 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 struct socket_data *data;
975 struct resource_map *p;
976 ssize_t ret = 0;
977
Dominik Brodowskicfe5d802010-01-17 19:31:45 +0100978 mutex_lock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 data = s->resource_data;
980
981 for (p = data->io_db.next; p != &data->io_db; p = p->next) {
982 if (ret > (PAGE_SIZE - 10))
983 continue;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100984 ret += snprintf(&buf[ret], (PAGE_SIZE - ret - 1),
985 "0x%08lx - 0x%08lx\n",
986 ((unsigned long) p->base),
987 ((unsigned long) p->base + p->num - 1));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988 }
989
Dominik Brodowskicfe5d802010-01-17 19:31:45 +0100990 mutex_unlock(&s->ops_mutex);
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100991 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992}
993
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200994static ssize_t store_io_db(struct device *dev,
995 struct device_attribute *attr,
996 const char *buf, size_t count)
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 unsigned long start_addr, end_addr;
Dominik Brodowski22916632005-06-27 16:28:46 -07001000 unsigned int add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001 ssize_t ret = 0;
1002
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001003 ret = sscanf(buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001005 ret = sscanf(buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -07001006 add = REMOVE_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001008 ret = sscanf(buf, "0x%lx - 0x%lx", &start_addr,
1009 &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -07001010 add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011 if (ret != 2)
1012 return -EINVAL;
1013 }
1014 }
Dominik Brodowski1146bc72005-09-28 19:28:37 +02001015 if (end_addr < start_addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016 return -EINVAL;
1017
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001018 mutex_lock(&s->ops_mutex);
Dominik Brodowski22916632005-06-27 16:28:46 -07001019 ret = adjust_io(s, add, start_addr, end_addr);
Dominik Brodowski3c299762005-06-27 16:28:46 -07001020 if (!ret)
1021 s->resource_setup_new = 1;
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001022 mutex_unlock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023
1024 return ret ? ret : count;
1025}
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001026static DEVICE_ATTR(available_resources_io, 0600, show_io_db, store_io_db);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001028static ssize_t show_mem_db(struct device *dev,
1029 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001031 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 struct socket_data *data;
1033 struct resource_map *p;
1034 ssize_t ret = 0;
1035
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001036 mutex_lock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 data = s->resource_data;
1038
Dominik Brodowski7b4884c2010-02-17 16:25:53 +01001039 for (p = data->mem_db_valid.next; p != &data->mem_db_valid;
1040 p = p->next) {
1041 if (ret > (PAGE_SIZE - 10))
1042 continue;
1043 ret += snprintf(&buf[ret], (PAGE_SIZE - ret - 1),
1044 "0x%08lx - 0x%08lx\n",
1045 ((unsigned long) p->base),
1046 ((unsigned long) p->base + p->num - 1));
1047 }
1048
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 for (p = data->mem_db.next; p != &data->mem_db; p = p->next) {
1050 if (ret > (PAGE_SIZE - 10))
1051 continue;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001052 ret += snprintf(&buf[ret], (PAGE_SIZE - ret - 1),
1053 "0x%08lx - 0x%08lx\n",
1054 ((unsigned long) p->base),
1055 ((unsigned long) p->base + p->num - 1));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056 }
1057
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001058 mutex_unlock(&s->ops_mutex);
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001059 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060}
1061
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001062static ssize_t store_mem_db(struct device *dev,
1063 struct device_attribute *attr,
1064 const char *buf, size_t count)
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);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 unsigned long start_addr, end_addr;
Dominik Brodowski22916632005-06-27 16:28:46 -07001068 unsigned int add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 ssize_t ret = 0;
1070
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001071 ret = sscanf(buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001073 ret = sscanf(buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -07001074 add = REMOVE_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001076 ret = sscanf(buf, "0x%lx - 0x%lx", &start_addr,
1077 &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -07001078 add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 if (ret != 2)
1080 return -EINVAL;
1081 }
1082 }
Dominik Brodowski1146bc72005-09-28 19:28:37 +02001083 if (end_addr < start_addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 return -EINVAL;
1085
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001086 mutex_lock(&s->ops_mutex);
Dominik Brodowski22916632005-06-27 16:28:46 -07001087 ret = adjust_memory(s, add, start_addr, end_addr);
Dominik Brodowski3c299762005-06-27 16:28:46 -07001088 if (!ret)
1089 s->resource_setup_new = 1;
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001090 mutex_unlock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091
1092 return ret ? ret : count;
1093}
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001094static DEVICE_ATTR(available_resources_mem, 0600, show_mem_db, store_mem_db);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095
David Brownell7d578962008-06-12 12:13:55 -07001096static struct attribute *pccard_rsrc_attributes[] = {
1097 &dev_attr_available_resources_io.attr,
1098 &dev_attr_available_resources_mem.attr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099 NULL,
1100};
1101
David Brownell7d578962008-06-12 12:13:55 -07001102static const struct attribute_group rsrc_attributes = {
1103 .attrs = pccard_rsrc_attributes,
1104};
1105
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001106static int __devinit pccard_sysfs_add_rsrc(struct device *dev,
Dmitry Torokhovd8539d82005-09-15 02:01:36 -05001107 struct class_interface *class_intf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001109 struct pcmcia_socket *s = dev_get_drvdata(dev);
David Brownell7d578962008-06-12 12:13:55 -07001110
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 if (s->resource_ops != &pccard_nonstatic_ops)
1112 return 0;
David Brownell7d578962008-06-12 12:13:55 -07001113 return sysfs_create_group(&dev->kobj, &rsrc_attributes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114}
1115
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001116static void __devexit pccard_sysfs_remove_rsrc(struct device *dev,
Dmitry Torokhovd8539d82005-09-15 02:01:36 -05001117 struct class_interface *class_intf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001119 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120
1121 if (s->resource_ops != &pccard_nonstatic_ops)
1122 return;
David Brownell7d578962008-06-12 12:13:55 -07001123 sysfs_remove_group(&dev->kobj, &rsrc_attributes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124}
1125
Sam Ravnborged49f5d2008-05-01 04:34:50 -07001126static struct class_interface pccard_rsrc_interface __refdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127 .class = &pcmcia_socket_class,
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001128 .add_dev = &pccard_sysfs_add_rsrc,
1129 .remove_dev = __devexit_p(&pccard_sysfs_remove_rsrc),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130};
1131
1132static int __init nonstatic_sysfs_init(void)
1133{
1134 return class_interface_register(&pccard_rsrc_interface);
1135}
1136
1137static void __exit nonstatic_sysfs_exit(void)
1138{
1139 class_interface_unregister(&pccard_rsrc_interface);
1140}
1141
1142module_init(nonstatic_sysfs_init);
1143module_exit(nonstatic_sysfs_exit);