blob: a6eb7b59ba9f805c68c13aacda0b7d886a6a9803 [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 Brodowski41b97ab2010-04-15 19:01:53 +0200813 unsigned long size;
Dominik Brodowski22916632005-06-27 16:28:46 -0700814 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 Brodowski41b97ab2010-04-15 19:01:53 +0200823 size = end - start + 1;
824
Dominik Brodowski1146bc72005-09-28 19:28:37 +0200825 if (end < start)
Dominik Brodowski22916632005-06-27 16:28:46 -0700826 return -EINVAL;
827
828 if (end > IO_SPACE_LIMIT)
829 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830
Dominik Brodowski22916632005-06-27 16:28:46 -0700831 switch (action) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 case ADD_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700833 if (add_interval(&data->io_db, start, size) != 0) {
834 ret = -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835 break;
836 }
837#ifdef CONFIG_PCMCIA_PROBE
838 if (probe_io)
Dominik Brodowski22916632005-06-27 16:28:46 -0700839 do_io_probe(s, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840#endif
841 break;
842 case REMOVE_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700843 sub_interval(&data->io_db, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 break;
845 default:
Dominik Brodowski22916632005-06-27 16:28:46 -0700846 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 break;
848 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849
850 return ret;
851}
852
853
Dominik Brodowski3c299762005-06-27 16:28:46 -0700854#ifdef CONFIG_PCI
855static int nonstatic_autoadd_resources(struct pcmcia_socket *s)
856{
857 struct resource *res;
858 int i, done = 0;
859
860 if (!s->cb_dev || !s->cb_dev->bus)
861 return -ENODEV;
862
Brian Gerst0d078f62005-10-30 14:59:20 -0800863#if defined(CONFIG_X86)
Dominik Brodowskib6d00f02005-06-27 16:29:02 -0700864 /* If this is the root bus, the risk of hitting
865 * some strange system devices which aren't protected
866 * by either ACPI resource tables or properly requested
867 * resources is too big. Therefore, don't do auto-adding
868 * of resources at the moment.
869 */
870 if (s->cb_dev->bus->number == 0)
871 return -EINVAL;
872#endif
873
Bjorn Helgaas89a74ec2010-02-23 10:24:31 -0700874 pci_bus_for_each_resource(s->cb_dev->bus, res, i) {
Dominik Brodowski3c299762005-06-27 16:28:46 -0700875 if (!res)
876 continue;
877
878 if (res->flags & IORESOURCE_IO) {
879 if (res == &ioport_resource)
880 continue;
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200881 dev_printk(KERN_INFO, &s->cb_dev->dev,
Bjorn Helgaase1944c62010-03-16 15:53:08 -0600882 "pcmcia: parent PCI bridge window: %pR\n",
883 res);
Dominik Brodowski3c299762005-06-27 16:28:46 -0700884 if (!adjust_io(s, ADD_MANAGED_RESOURCE, res->start, res->end))
885 done |= IORESOURCE_IO;
886
887 }
888
889 if (res->flags & IORESOURCE_MEM) {
890 if (res == &iomem_resource)
891 continue;
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200892 dev_printk(KERN_INFO, &s->cb_dev->dev,
Bjorn Helgaase1944c62010-03-16 15:53:08 -0600893 "pcmcia: parent PCI bridge window: %pR\n",
894 res);
Dominik Brodowski3c299762005-06-27 16:28:46 -0700895 if (!adjust_memory(s, ADD_MANAGED_RESOURCE, res->start, res->end))
896 done |= IORESOURCE_MEM;
897 }
898 }
899
900 /* if we got at least one of IO, and one of MEM, we can be glad and
901 * activate the PCMCIA subsystem */
Dominik Brodowski54bb5672005-09-28 19:29:59 +0200902 if (done == (IORESOURCE_MEM | IORESOURCE_IO))
Dominik Brodowski3c299762005-06-27 16:28:46 -0700903 s->resource_setup_done = 1;
904
905 return 0;
906}
907
908#else
909
910static inline int nonstatic_autoadd_resources(struct pcmcia_socket *s)
911{
912 return -ENODEV;
913}
914
915#endif
916
917
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918static int nonstatic_init(struct pcmcia_socket *s)
919{
920 struct socket_data *data;
921
Dominik Brodowski8084b372005-12-11 21:18:26 +0100922 data = kzalloc(sizeof(struct socket_data), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923 if (!data)
924 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925
926 data->mem_db.next = &data->mem_db;
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100927 data->mem_db_valid.next = &data->mem_db_valid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928 data->io_db.next = &data->io_db;
929
930 s->resource_data = (void *) data;
931
Dominik Brodowski3c299762005-06-27 16:28:46 -0700932 nonstatic_autoadd_resources(s);
933
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 return 0;
935}
936
937static void nonstatic_release_resource_db(struct pcmcia_socket *s)
938{
939 struct socket_data *data = s->resource_data;
940 struct resource_map *p, *q;
941
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100942 for (p = data->mem_db_valid.next; p != &data->mem_db_valid; p = q) {
943 q = p->next;
944 kfree(p);
945 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946 for (p = data->mem_db.next; p != &data->mem_db; p = q) {
947 q = p->next;
948 kfree(p);
949 }
950 for (p = data->io_db.next; p != &data->io_db; p = q) {
951 q = p->next;
952 kfree(p);
953 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954}
955
956
957struct pccard_resource_ops pccard_nonstatic_ops = {
958 .validate_mem = pcmcia_nonstatic_validate_mem,
959 .adjust_io_region = nonstatic_adjust_io_region,
960 .find_io = nonstatic_find_io_region,
961 .find_mem = nonstatic_find_mem_region,
Dominik Brodowskic5023802008-06-19 19:02:52 +0200962 .add_io = adjust_io,
963 .add_mem = adjust_memory,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964 .init = nonstatic_init,
965 .exit = nonstatic_release_resource_db,
966};
967EXPORT_SYMBOL(pccard_nonstatic_ops);
968
969
970/* sysfs interface to the resource database */
971
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200972static ssize_t show_io_db(struct device *dev,
973 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200975 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976 struct socket_data *data;
977 struct resource_map *p;
978 ssize_t ret = 0;
979
Dominik Brodowskicfe5d802010-01-17 19:31:45 +0100980 mutex_lock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 data = s->resource_data;
982
983 for (p = data->io_db.next; p != &data->io_db; p = p->next) {
984 if (ret > (PAGE_SIZE - 10))
985 continue;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100986 ret += snprintf(&buf[ret], (PAGE_SIZE - ret - 1),
987 "0x%08lx - 0x%08lx\n",
988 ((unsigned long) p->base),
989 ((unsigned long) p->base + p->num - 1));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 }
991
Dominik Brodowskicfe5d802010-01-17 19:31:45 +0100992 mutex_unlock(&s->ops_mutex);
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100993 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994}
995
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200996static ssize_t store_io_db(struct device *dev,
997 struct device_attribute *attr,
998 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001000 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001 unsigned long start_addr, end_addr;
Dominik Brodowski22916632005-06-27 16:28:46 -07001002 unsigned int add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003 ssize_t ret = 0;
1004
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001005 ret = sscanf(buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001007 ret = sscanf(buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -07001008 add = REMOVE_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001010 ret = sscanf(buf, "0x%lx - 0x%lx", &start_addr,
1011 &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -07001012 add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013 if (ret != 2)
1014 return -EINVAL;
1015 }
1016 }
Dominik Brodowski1146bc72005-09-28 19:28:37 +02001017 if (end_addr < start_addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018 return -EINVAL;
1019
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001020 mutex_lock(&s->ops_mutex);
Dominik Brodowski22916632005-06-27 16:28:46 -07001021 ret = adjust_io(s, add, start_addr, end_addr);
Dominik Brodowski3c299762005-06-27 16:28:46 -07001022 if (!ret)
1023 s->resource_setup_new = 1;
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001024 mutex_unlock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025
1026 return ret ? ret : count;
1027}
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001028static DEVICE_ATTR(available_resources_io, 0600, show_io_db, store_io_db);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001030static ssize_t show_mem_db(struct device *dev,
1031 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001033 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034 struct socket_data *data;
1035 struct resource_map *p;
1036 ssize_t ret = 0;
1037
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001038 mutex_lock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039 data = s->resource_data;
1040
Dominik Brodowski7b4884c2010-02-17 16:25:53 +01001041 for (p = data->mem_db_valid.next; p != &data->mem_db_valid;
1042 p = p->next) {
1043 if (ret > (PAGE_SIZE - 10))
1044 continue;
1045 ret += snprintf(&buf[ret], (PAGE_SIZE - ret - 1),
1046 "0x%08lx - 0x%08lx\n",
1047 ((unsigned long) p->base),
1048 ((unsigned long) p->base + p->num - 1));
1049 }
1050
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 for (p = data->mem_db.next; p != &data->mem_db; p = p->next) {
1052 if (ret > (PAGE_SIZE - 10))
1053 continue;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001054 ret += snprintf(&buf[ret], (PAGE_SIZE - ret - 1),
1055 "0x%08lx - 0x%08lx\n",
1056 ((unsigned long) p->base),
1057 ((unsigned long) p->base + p->num - 1));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 }
1059
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001060 mutex_unlock(&s->ops_mutex);
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001061 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062}
1063
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001064static ssize_t store_mem_db(struct device *dev,
1065 struct device_attribute *attr,
1066 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001068 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 unsigned long start_addr, end_addr;
Dominik Brodowski22916632005-06-27 16:28:46 -07001070 unsigned int add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 ssize_t ret = 0;
1072
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001073 ret = sscanf(buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001075 ret = sscanf(buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -07001076 add = REMOVE_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001078 ret = sscanf(buf, "0x%lx - 0x%lx", &start_addr,
1079 &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -07001080 add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 if (ret != 2)
1082 return -EINVAL;
1083 }
1084 }
Dominik Brodowski1146bc72005-09-28 19:28:37 +02001085 if (end_addr < start_addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086 return -EINVAL;
1087
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001088 mutex_lock(&s->ops_mutex);
Dominik Brodowski22916632005-06-27 16:28:46 -07001089 ret = adjust_memory(s, add, start_addr, end_addr);
Dominik Brodowski3c299762005-06-27 16:28:46 -07001090 if (!ret)
1091 s->resource_setup_new = 1;
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001092 mutex_unlock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093
1094 return ret ? ret : count;
1095}
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001096static DEVICE_ATTR(available_resources_mem, 0600, show_mem_db, store_mem_db);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097
David Brownell7d578962008-06-12 12:13:55 -07001098static struct attribute *pccard_rsrc_attributes[] = {
1099 &dev_attr_available_resources_io.attr,
1100 &dev_attr_available_resources_mem.attr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 NULL,
1102};
1103
David Brownell7d578962008-06-12 12:13:55 -07001104static const struct attribute_group rsrc_attributes = {
1105 .attrs = pccard_rsrc_attributes,
1106};
1107
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001108static int __devinit pccard_sysfs_add_rsrc(struct device *dev,
Dmitry Torokhovd8539d82005-09-15 02:01:36 -05001109 struct class_interface *class_intf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001111 struct pcmcia_socket *s = dev_get_drvdata(dev);
David Brownell7d578962008-06-12 12:13:55 -07001112
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 if (s->resource_ops != &pccard_nonstatic_ops)
1114 return 0;
David Brownell7d578962008-06-12 12:13:55 -07001115 return sysfs_create_group(&dev->kobj, &rsrc_attributes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116}
1117
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001118static void __devexit pccard_sysfs_remove_rsrc(struct device *dev,
Dmitry Torokhovd8539d82005-09-15 02:01:36 -05001119 struct class_interface *class_intf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001121 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122
1123 if (s->resource_ops != &pccard_nonstatic_ops)
1124 return;
David Brownell7d578962008-06-12 12:13:55 -07001125 sysfs_remove_group(&dev->kobj, &rsrc_attributes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126}
1127
Sam Ravnborged49f5d2008-05-01 04:34:50 -07001128static struct class_interface pccard_rsrc_interface __refdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 .class = &pcmcia_socket_class,
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001130 .add_dev = &pccard_sysfs_add_rsrc,
1131 .remove_dev = __devexit_p(&pccard_sysfs_remove_rsrc),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132};
1133
1134static int __init nonstatic_sysfs_init(void)
1135{
1136 return class_interface_register(&pccard_rsrc_interface);
1137}
1138
1139static void __exit nonstatic_sysfs_exit(void)
1140{
1141 class_interface_unregister(&pccard_rsrc_interface);
1142}
1143
1144module_init(nonstatic_sysfs_init);
1145module_exit(nonstatic_sysfs_exit);