blob: c5ebc603b0584e139dd65feeba022d32e2b9c904 [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
Dominik Brodowski49b11532010-03-07 16:41:57 +010037/* moved to rsrc_mgr.c
Linus Torvalds1da177e2005-04-16 15:20:36 -070038MODULE_AUTHOR("David A. Hinds, Dominik Brodowski");
39MODULE_LICENSE("GPL");
Dominik Brodowski49b11532010-03-07 16:41:57 +010040*/
Linus Torvalds1da177e2005-04-16 15:20:36 -070041
42/* Parameters that can be set with 'insmod' */
43
44#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0444)
45
46INT_MODULE_PARM(probe_mem, 1); /* memory probe? */
47#ifdef CONFIG_PCMCIA_PROBE
48INT_MODULE_PARM(probe_io, 1); /* IO port probe? */
49INT_MODULE_PARM(mem_limit, 0x10000);
50#endif
51
52/* for io_db and mem_db */
53struct resource_map {
54 u_long base, num;
55 struct resource_map *next;
56};
57
58struct socket_data {
59 struct resource_map mem_db;
Dominik Brodowski7b4884c2010-02-17 16:25:53 +010060 struct resource_map mem_db_valid;
Linus Torvalds1da177e2005-04-16 15:20:36 -070061 struct resource_map io_db;
Linus Torvalds1da177e2005-04-16 15:20:36 -070062};
63
Linus Torvalds1da177e2005-04-16 15:20:36 -070064#define MEM_PROBE_LOW (1 << 0)
65#define MEM_PROBE_HIGH (1 << 1)
66
67
68/*======================================================================
69
70 Linux resource management extensions
71
72======================================================================*/
73
74static struct resource *
Greg Kroah-Hartman2427ddd2006-06-12 17:07:52 -070075claim_region(struct pcmcia_socket *s, resource_size_t base,
76 resource_size_t size, int type, char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -070077{
78 struct resource *res, *parent;
79
80 parent = type & IORESOURCE_MEM ? &iomem_resource : &ioport_resource;
Dominik Brodowski49b11532010-03-07 16:41:57 +010081 res = pcmcia_make_resource(base, size, type | IORESOURCE_BUSY, name);
Linus Torvalds1da177e2005-04-16 15:20:36 -070082
83 if (res) {
84#ifdef CONFIG_PCI
85 if (s && s->cb_dev)
86 parent = pci_find_parent_resource(s->cb_dev, res);
87#endif
88 if (!parent || request_resource(parent, res)) {
89 kfree(res);
90 res = NULL;
91 }
92 }
93 return res;
94}
95
96static void free_region(struct resource *res)
97{
98 if (res) {
99 release_resource(res);
100 kfree(res);
101 }
102}
103
104/*======================================================================
105
106 These manage the internal databases of available resources.
107
108======================================================================*/
109
110static int add_interval(struct resource_map *map, u_long base, u_long num)
111{
Dominik Brodowski11683862008-08-03 10:22:47 +0200112 struct resource_map *p, *q;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113
Dominik Brodowski11683862008-08-03 10:22:47 +0200114 for (p = map; ; p = p->next) {
Dominik Brodowskif309cb32010-02-17 14:35:33 +0100115 if ((p != map) && (p->base+p->num >= base)) {
116 p->num = max(num + base - p->base, p->num);
117 return 0;
118 }
Dominik Brodowski11683862008-08-03 10:22:47 +0200119 if ((p->next == map) || (p->next->base > base+num-1))
120 break;
121 }
122 q = kmalloc(sizeof(struct resource_map), GFP_KERNEL);
123 if (!q) {
124 printk(KERN_WARNING "out of memory to update resources\n");
125 return -ENOMEM;
126 }
127 q->base = base; q->num = num;
128 q->next = p->next; p->next = q;
129 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130}
131
132/*====================================================================*/
133
134static int sub_interval(struct resource_map *map, u_long base, u_long num)
135{
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100136 struct resource_map *p, *q;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100138 for (p = map; ; p = q) {
139 q = p->next;
140 if (q == map)
141 break;
142 if ((q->base+q->num > base) && (base+num > q->base)) {
143 if (q->base >= base) {
144 if (q->base+q->num <= base+num) {
145 /* Delete whole block */
146 p->next = q->next;
147 kfree(q);
148 /* don't advance the pointer yet */
149 q = p;
150 } else {
151 /* Cut off bit from the front */
152 q->num = q->base + q->num - base - num;
153 q->base = base + num;
154 }
155 } else if (q->base+q->num <= base+num) {
156 /* Cut off bit from the end */
157 q->num = base - q->base;
158 } else {
159 /* Split the block into two pieces */
160 p = kmalloc(sizeof(struct resource_map),
161 GFP_KERNEL);
162 if (!p) {
163 printk(KERN_WARNING "out of memory to update resources\n");
164 return -ENOMEM;
165 }
166 p->base = base+num;
167 p->num = q->base+q->num - p->base;
168 q->num = base - q->base;
169 p->next = q->next ; q->next = p;
170 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100173 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174}
175
176/*======================================================================
177
178 These routines examine a region of IO or memory addresses to
179 determine what ranges might be genuinely available.
180
181======================================================================*/
182
183#ifdef CONFIG_PCMCIA_PROBE
Olof Johansson906da802008-02-04 22:27:35 -0800184static void do_io_probe(struct pcmcia_socket *s, unsigned int base,
185 unsigned int num)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186{
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100187 struct resource *res;
188 struct socket_data *s_data = s->resource_data;
189 unsigned int i, j, bad;
190 int any;
191 u_char *b, hole, most;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100193 dev_printk(KERN_INFO, &s->dev, "cs: IO port probe %#x-%#x:",
194 base, base+num-1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100196 /* First, what does a floating port look like? */
197 b = kzalloc(256, GFP_KERNEL);
198 if (!b) {
199 printk("\n");
200 dev_printk(KERN_ERR, &s->dev,
201 "do_io_probe: unable to kmalloc 256 bytes");
202 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100204 for (i = base, most = 0; i < base+num; i += 8) {
Dominik Brodowski509b0862010-04-08 19:23:07 +0200205 res = claim_region(s, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100206 if (!res)
207 continue;
208 hole = inb(i);
209 for (j = 1; j < 8; j++)
210 if (inb(i+j) != hole)
211 break;
212 free_region(res);
213 if ((j == 8) && (++b[hole] > b[most]))
214 most = hole;
215 if (b[most] == 127)
216 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100218 kfree(b);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100220 bad = any = 0;
221 for (i = base; i < base+num; i += 8) {
Dominik Brodowski509b0862010-04-08 19:23:07 +0200222 res = claim_region(s, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
223 if (!res) {
224 if (!any)
225 printk(" excluding");
226 if (!bad)
227 bad = any = i;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100228 continue;
Dominik Brodowski509b0862010-04-08 19:23:07 +0200229 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100230 for (j = 0; j < 8; j++)
231 if (inb(i+j) != most)
232 break;
233 free_region(res);
234 if (j < 8) {
235 if (!any)
236 printk(" excluding");
237 if (!bad)
238 bad = any = i;
239 } else {
240 if (bad) {
241 sub_interval(&s_data->io_db, bad, i-bad);
242 printk(" %#x-%#x", bad, i-1);
243 bad = 0;
244 }
245 }
246 }
247 if (bad) {
248 if ((num > 16) && (bad == base) && (i == base+num)) {
Dominik Brodowski509b0862010-04-08 19:23:07 +0200249 sub_interval(&s_data->io_db, bad, i-bad);
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100250 printk(" nothing: probe failed.\n");
251 return;
252 } else {
253 sub_interval(&s_data->io_db, bad, i-bad);
254 printk(" %#x-%#x", bad, i-1);
255 }
256 }
257
258 printk(any ? "\n" : " clean.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259}
260#endif
261
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100262/*======================================================================*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100264/**
265 * readable() - iomem validation function for cards with a valid CIS
266 */
Dominik Brodowskic5081d52008-06-19 20:12:34 +0200267static int readable(struct pcmcia_socket *s, struct resource *res,
268 unsigned int *count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269{
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100270 int ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271
Dominik Brodowski7ab248552010-02-17 18:00:07 +0100272 if (s->fake_cis) {
273 dev_dbg(&s->dev, "fake CIS is being used: can't validate mem\n");
274 return 0;
275 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276
277 s->cis_mem.res = res;
278 s->cis_virt = ioremap(res->start, s->map_size);
279 if (s->cis_virt) {
Dominik Brodowski6b8e0872010-01-12 21:42:51 +0100280 mutex_unlock(&s->ops_mutex);
Dominik Brodowski6e7b51a2010-01-06 13:57:43 +0100281 /* as we're only called from pcmcia.c, we're safe */
282 if (s->callback->validate)
283 ret = s->callback->validate(s, count);
Dominik Brodowski904e3772010-01-02 12:28:04 +0100284 /* invalidate mapping */
Dominik Brodowski6b8e0872010-01-12 21:42:51 +0100285 mutex_lock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 iounmap(s->cis_virt);
287 s->cis_virt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 }
289 s->cis_mem.res = NULL;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100290 if ((ret) || (*count == 0))
291 return -EINVAL;
292 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293}
294
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100295/**
296 * checksum() - iomem validation function for simple memory cards
297 */
298static int checksum(struct pcmcia_socket *s, struct resource *res,
299 unsigned int *value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300{
301 pccard_mem_map map;
302 int i, a = 0, b = -1, d;
303 void __iomem *virt;
304
305 virt = ioremap(res->start, s->map_size);
306 if (virt) {
307 map.map = 0;
308 map.flags = MAP_ACTIVE;
309 map.speed = 0;
310 map.res = res;
311 map.card_start = 0;
312 s->ops->set_mem_map(s, &map);
313
314 /* Don't bother checking every word... */
315 for (i = 0; i < s->map_size; i += 44) {
316 d = readl(virt+i);
317 a += d;
318 b &= d;
319 }
320
321 map.flags = 0;
322 s->ops->set_mem_map(s, &map);
323
324 iounmap(virt);
325 }
326
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100327 if (b == -1)
328 return -EINVAL;
329
330 *value = a;
331
332 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333}
334
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100335/**
336 * do_validate_mem() - low level validate a memory region for PCMCIA use
337 * @s: PCMCIA socket to validate
338 * @base: start address of resource to check
339 * @size: size of resource to check
340 * @validate: validation function to use
341 *
342 * do_validate_mem() splits up the memory region which is to be checked
343 * into two parts. Both are passed to the @validate() function. If
344 * @validate() returns non-zero, or the value parameter to @validate()
345 * is zero, or the value parameter is different between both calls,
346 * the check fails, and -EINVAL is returned. Else, 0 is returned.
347 */
348static int do_validate_mem(struct pcmcia_socket *s,
349 unsigned long base, unsigned long size,
350 int validate (struct pcmcia_socket *s,
351 struct resource *res,
352 unsigned int *value))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353{
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100354 struct socket_data *s_data = s->resource_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 struct resource *res1, *res2;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100356 unsigned int info1 = 1, info2 = 1;
357 int ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100359 res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "PCMCIA memprobe");
360 res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM,
361 "PCMCIA memprobe");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362
363 if (res1 && res2) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100364 ret = 0;
365 if (validate) {
366 ret = validate(s, res1, &info1);
367 ret += validate(s, res2, &info2);
368 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369 }
370
371 free_region(res2);
372 free_region(res1);
373
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100374 dev_dbg(&s->dev, "cs: memory probe 0x%06lx-0x%06lx: %p %p %u %u %u",
375 base, base+size-1, res1, res2, ret, info1, info2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100377 if ((ret) || (info1 != info2) || (info1 == 0))
378 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100380 if (validate && !s->fake_cis) {
381 /* move it to the validated data set */
382 add_interval(&s_data->mem_db_valid, base, size);
383 sub_interval(&s_data->mem_db, base, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 }
385
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100386 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387}
388
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100390/**
391 * do_mem_probe() - validate a memory region for PCMCIA use
392 * @s: PCMCIA socket to validate
393 * @base: start address of resource to check
394 * @num: size of resource to check
395 * @validate: validation function to use
396 * @fallback: validation function to use if validate fails
397 *
398 * do_mem_probe() checks a memory region for use by the PCMCIA subsystem.
399 * To do so, the area is split up into sensible parts, and then passed
400 * into the @validate() function. Only if @validate() and @fallback() fail,
401 * the area is marked as unavaibale for use by the PCMCIA subsystem. The
402 * function returns the size of the usable memory area.
403 */
404static int do_mem_probe(struct pcmcia_socket *s, u_long base, u_long num,
405 int validate (struct pcmcia_socket *s,
406 struct resource *res,
407 unsigned int *value),
408 int fallback (struct pcmcia_socket *s,
409 struct resource *res,
410 unsigned int *value))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411{
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100412 struct socket_data *s_data = s->resource_data;
413 u_long i, j, bad, fail, step;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100415 dev_printk(KERN_INFO, &s->dev, "cs: memory probe 0x%06lx-0x%06lx:",
416 base, base+num-1);
417 bad = fail = 0;
418 step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff);
419 /* don't allow too large steps */
420 if (step > 0x800000)
421 step = 0x800000;
422 /* cis_readable wants to map 2x map_size */
423 if (step < 2 * s->map_size)
424 step = 2 * s->map_size;
425 for (i = j = base; i < base+num; i = j + step) {
426 if (!fail) {
427 for (j = i; j < base+num; j += step) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100428 if (!do_validate_mem(s, j, step, validate))
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100429 break;
430 }
431 fail = ((i == base) && (j == base+num));
432 }
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100433 if ((fail) && (fallback)) {
434 for (j = i; j < base+num; j += step)
435 if (!do_validate_mem(s, j, step, fallback))
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100436 break;
437 }
438 if (i != j) {
439 if (!bad)
440 printk(" excluding");
441 printk(" %#05lx-%#05lx", i, j-1);
442 sub_interval(&s_data->mem_db, i, j-i);
443 bad += j-i;
444 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100446 printk(bad ? "\n" : " clean.\n");
447 return num - bad;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448}
449
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100450
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451#ifdef CONFIG_PCMCIA_PROBE
452
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100453/**
454 * inv_probe() - top-to-bottom search for one usuable high memory area
455 * @s: PCMCIA socket to validate
456 * @m: resource_map to check
457 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458static u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s)
459{
Dominik Brodowskide759142005-09-28 19:41:56 +0200460 struct socket_data *s_data = s->resource_data;
461 u_long ok;
462 if (m == &s_data->mem_db)
463 return 0;
464 ok = inv_probe(m->next, s);
465 if (ok) {
466 if (m->base >= 0x100000)
467 sub_interval(&s_data->mem_db, m->base, m->num);
468 return ok;
469 }
470 if (m->base < 0x100000)
471 return 0;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100472 return do_mem_probe(s, m->base, m->num, readable, checksum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473}
474
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100475/**
476 * validate_mem() - memory probe function
477 * @s: PCMCIA socket to validate
478 * @probe_mask: MEM_PROBE_LOW | MEM_PROBE_HIGH
479 *
480 * The memory probe. If the memory list includes a 64K-aligned block
481 * below 1MB, we probe in 64K chunks, and as soon as we accumulate at
482 * least mem_limit free space, we quit. Returns 0 on usuable ports.
483 */
Dominik Brodowskide759142005-09-28 19:41:56 +0200484static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485{
Dominik Brodowskide759142005-09-28 19:41:56 +0200486 struct resource_map *m, mm;
487 static unsigned char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 };
488 unsigned long b, i, ok = 0;
489 struct socket_data *s_data = s->resource_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490
Dominik Brodowskide759142005-09-28 19:41:56 +0200491 /* We do up to four passes through the list */
492 if (probe_mask & MEM_PROBE_HIGH) {
493 if (inv_probe(s_data->mem_db.next, s) > 0)
494 return 0;
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100495 if (s_data->mem_db_valid.next != &s_data->mem_db_valid)
496 return 0;
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200497 dev_printk(KERN_NOTICE, &s->dev,
498 "cs: warning: no high memory space available!\n");
Dominik Brodowskide759142005-09-28 19:41:56 +0200499 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 }
Dominik Brodowskide759142005-09-28 19:41:56 +0200501
502 for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
503 mm = *m;
504 /* Only probe < 1 MB */
505 if (mm.base >= 0x100000)
506 continue;
507 if ((mm.base | mm.num) & 0xffff) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100508 ok += do_mem_probe(s, mm.base, mm.num, readable,
509 checksum);
Dominik Brodowskide759142005-09-28 19:41:56 +0200510 continue;
511 }
512 /* Special probe for 64K-aligned block */
513 for (i = 0; i < 4; i++) {
514 b = order[i] << 12;
515 if ((b >= mm.base) && (b+0x10000 <= mm.base+mm.num)) {
516 if (ok >= mem_limit)
517 sub_interval(&s_data->mem_db, b, 0x10000);
518 else
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100519 ok += do_mem_probe(s, b, 0x10000,
520 readable, checksum);
Dominik Brodowskide759142005-09-28 19:41:56 +0200521 }
522 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 }
Dominik Brodowskide759142005-09-28 19:41:56 +0200524
525 if (ok > 0)
526 return 0;
527
528 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529}
530
531#else /* CONFIG_PCMCIA_PROBE */
532
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100533/**
534 * validate_mem() - memory probe function
535 * @s: PCMCIA socket to validate
536 * @probe_mask: ignored
537 *
538 * Returns 0 on usuable ports.
539 */
Andrew Morton2cff9442005-11-16 21:29:26 -0800540static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541{
542 struct resource_map *m, mm;
543 struct socket_data *s_data = s->resource_data;
Andrew Morton2cff9442005-11-16 21:29:26 -0800544 unsigned long ok = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545
546 for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
547 mm = *m;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100548 ok += do_mem_probe(s, mm.base, mm.num, readable, checksum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 }
Andrew Morton2cff9442005-11-16 21:29:26 -0800550 if (ok > 0)
551 return 0;
552 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553}
554
555#endif /* CONFIG_PCMCIA_PROBE */
556
557
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100558/**
559 * pcmcia_nonstatic_validate_mem() - try to validate iomem for PCMCIA use
560 * @s: PCMCIA socket to validate
561 *
562 * This is tricky... when we set up CIS memory, we try to validate
563 * the memory window space allocations.
564 *
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100565 * Locking note: Must be called with skt_mutex held!
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 */
Dominik Brodowskide759142005-09-28 19:41:56 +0200567static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568{
569 struct socket_data *s_data = s->resource_data;
Dominik Brodowskide759142005-09-28 19:41:56 +0200570 unsigned int probe_mask = MEM_PROBE_LOW;
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100571 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100573 if (!probe_mem || !(s->state & SOCKET_PRESENT))
Dominik Brodowskide759142005-09-28 19:41:56 +0200574 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575
Dominik Brodowskide759142005-09-28 19:41:56 +0200576 if (s->features & SS_CAP_PAGE_REGS)
577 probe_mask = MEM_PROBE_HIGH;
578
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100579 ret = validate_mem(s, probe_mask);
Dominik Brodowskide759142005-09-28 19:41:56 +0200580
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100581 if (s_data->mem_db_valid.next != &s_data->mem_db_valid)
582 return 0;
Dominik Brodowskide759142005-09-28 19:41:56 +0200583
584 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585}
586
587struct pcmcia_align_data {
588 unsigned long mask;
589 unsigned long offset;
590 struct resource_map *map;
591};
592
Dominik Brodowski147a2742010-04-04 18:10:35 +0200593static resource_size_t pcmcia_common_align(struct pcmcia_align_data *align_data,
594 resource_size_t start)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595{
Dominik Brodowski147a2742010-04-04 18:10:35 +0200596 resource_size_t ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 /*
598 * Ensure that we have the correct start address
599 */
Dominik Brodowski147a2742010-04-04 18:10:35 +0200600 ret = (start & ~align_data->mask) + align_data->offset;
601 if (ret < start)
602 ret += align_data->mask + 1;
603 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604}
605
Dominik Brodowskib26b2d42010-01-01 17:40:49 +0100606static resource_size_t
Dominik Brodowski3b7a17f2010-01-01 17:40:50 +0100607pcmcia_align(void *align_data, const struct resource *res,
608 resource_size_t size, resource_size_t align)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609{
610 struct pcmcia_align_data *data = align_data;
611 struct resource_map *m;
Dominik Brodowskib26b2d42010-01-01 17:40:49 +0100612 resource_size_t start;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613
Dominik Brodowski147a2742010-04-04 18:10:35 +0200614 start = pcmcia_common_align(data, res->start);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615
616 for (m = data->map->next; m != data->map; m = m->next) {
Dominik Brodowski147a2742010-04-04 18:10:35 +0200617 unsigned long map_start = m->base;
618 unsigned long map_end = m->base + m->num - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619
620 /*
621 * If the lower resources are not available, try aligning
622 * to this entry of the resource database to see if it'll
623 * fit here.
624 */
Dominik Brodowski147a2742010-04-04 18:10:35 +0200625 if (start < map_start)
626 start = pcmcia_common_align(data, map_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627
628 /*
629 * If we're above the area which was passed in, there's
630 * no point proceeding.
631 */
Dominik Brodowski147a2742010-04-04 18:10:35 +0200632 if (start >= res->end)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 break;
634
Dominik Brodowski147a2742010-04-04 18:10:35 +0200635 if ((start + size - 1) <= map_end)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 break;
637 }
638
639 /*
640 * If we failed to find something suitable, ensure we fail.
641 */
642 if (m == data->map)
Dominik Brodowskib26b2d42010-01-01 17:40:49 +0100643 start = res->end;
644
645 return start;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646}
647
648/*
649 * Adjust an existing IO region allocation, but making sure that we don't
650 * encroach outside the resources which the user supplied.
651 */
652static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_start,
653 unsigned long r_end, struct pcmcia_socket *s)
654{
655 struct resource_map *m;
656 struct socket_data *s_data = s->resource_data;
657 int ret = -ENOMEM;
658
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 for (m = s_data->io_db.next; m != &s_data->io_db; m = m->next) {
660 unsigned long start = m->base;
661 unsigned long end = m->base + m->num - 1;
662
663 if (start > r_start || r_end > end)
664 continue;
665
666 ret = adjust_resource(res, r_start, r_end - r_start + 1);
667 break;
668 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669
670 return ret;
671}
672
673/*======================================================================
674
675 These find ranges of I/O ports or memory addresses that are not
676 currently allocated by other devices.
677
678 The 'align' field should reflect the number of bits of address
679 that need to be preserved from the initial value of *base. It
680 should be a power of two, greater than or equal to 'num'. A value
681 of 0 means that all bits of *base are significant. *base should
682 also be strictly less than 'align'.
683
684======================================================================*/
685
Dominik Brodowskie94e15f2005-06-27 16:28:15 -0700686static struct resource *nonstatic_find_io_region(unsigned long base, int num,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 unsigned long align, struct pcmcia_socket *s)
688{
Dominik Brodowski49b11532010-03-07 16:41:57 +0100689 struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_IO,
690 dev_name(&s->dev));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 struct socket_data *s_data = s->resource_data;
692 struct pcmcia_align_data data;
693 unsigned long min = base;
694 int ret;
695
696 if (align == 0)
697 align = 0x10000;
698
699 data.mask = align - 1;
700 data.offset = base & data.mask;
701 data.map = &s_data->io_db;
702
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703#ifdef CONFIG_PCI
704 if (s->cb_dev) {
705 ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1,
706 min, 0, pcmcia_align, &data);
707 } else
708#endif
709 ret = allocate_resource(&ioport_resource, res, num, min, ~0UL,
710 1, pcmcia_align, &data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711
712 if (ret != 0) {
713 kfree(res);
714 res = NULL;
715 }
716 return res;
717}
718
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100719static struct resource *nonstatic_find_mem_region(u_long base, u_long num,
Dominik Brodowskie94e15f2005-06-27 16:28:15 -0700720 u_long align, int low, struct pcmcia_socket *s)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721{
Dominik Brodowski49b11532010-03-07 16:41:57 +0100722 struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_MEM,
723 dev_name(&s->dev));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 struct socket_data *s_data = s->resource_data;
725 struct pcmcia_align_data data;
726 unsigned long min, max;
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100727 int ret, i, j;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728
729 low = low || !(s->features & SS_CAP_PAGE_REGS);
730
731 data.mask = align - 1;
732 data.offset = base & data.mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733
734 for (i = 0; i < 2; i++) {
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100735 data.map = &s_data->mem_db_valid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736 if (low) {
737 max = 0x100000UL;
738 min = base < max ? base : 0;
739 } else {
740 max = ~0UL;
741 min = 0x100000UL + base;
742 }
743
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100744 for (j = 0; j < 2; j++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745#ifdef CONFIG_PCI
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100746 if (s->cb_dev) {
747 ret = pci_bus_alloc_resource(s->cb_dev->bus,
748 res, num, 1, min, 0,
749 pcmcia_align, &data);
750 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751#endif
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100752 {
753 ret = allocate_resource(&iomem_resource,
754 res, num, min, max, 1,
755 pcmcia_align, &data);
756 }
757 if (ret == 0)
758 break;
759 data.map = &s_data->mem_db;
760 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 if (ret == 0 || low)
762 break;
763 low = 1;
764 }
765
766 if (ret != 0) {
767 kfree(res);
768 res = NULL;
769 }
770 return res;
771}
772
773
Dominik Brodowski22916632005-06-27 16:28:46 -0700774static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 struct socket_data *data = s->resource_data;
Dominik Brodowski22916632005-06-27 16:28:46 -0700777 unsigned long size = end - start + 1;
778 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779
Dominik Brodowski1146bc72005-09-28 19:28:37 +0200780 if (end < start)
Dominik Brodowski22916632005-06-27 16:28:46 -0700781 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782
Dominik Brodowski22916632005-06-27 16:28:46 -0700783 switch (action) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 case ADD_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700785 ret = add_interval(&data->mem_db, start, size);
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100786 if (!ret)
787 do_mem_probe(s, start, size, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 break;
789 case REMOVE_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700790 ret = sub_interval(&data->mem_db, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 break;
792 default:
Dominik Brodowski22916632005-06-27 16:28:46 -0700793 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795
796 return ret;
797}
798
799
Dominik Brodowski22916632005-06-27 16:28:46 -0700800static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801{
802 struct socket_data *data = s->resource_data;
Dominik Brodowski41b97ab2010-04-15 19:01:53 +0200803 unsigned long size;
Dominik Brodowski22916632005-06-27 16:28:46 -0700804 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805
Dominik Brodowski9713ab22010-03-23 16:05:00 +0100806#if defined(CONFIG_X86)
807 /* on x86, avoid anything < 0x100 for it is often used for
808 * legacy platform devices */
809 if (start < 0x100)
810 start = 0x100;
811#endif
812
Dominik Brodowski41b97ab2010-04-15 19:01:53 +0200813 size = end - start + 1;
814
Dominik Brodowski1146bc72005-09-28 19:28:37 +0200815 if (end < start)
Dominik Brodowski22916632005-06-27 16:28:46 -0700816 return -EINVAL;
817
818 if (end > IO_SPACE_LIMIT)
819 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820
Dominik Brodowski22916632005-06-27 16:28:46 -0700821 switch (action) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 case ADD_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700823 if (add_interval(&data->io_db, start, size) != 0) {
824 ret = -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 break;
826 }
827#ifdef CONFIG_PCMCIA_PROBE
828 if (probe_io)
Dominik Brodowski22916632005-06-27 16:28:46 -0700829 do_io_probe(s, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830#endif
831 break;
832 case REMOVE_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700833 sub_interval(&data->io_db, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834 break;
835 default:
Dominik Brodowski22916632005-06-27 16:28:46 -0700836 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 break;
838 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839
840 return ret;
841}
842
843
Dominik Brodowski3c299762005-06-27 16:28:46 -0700844#ifdef CONFIG_PCI
845static int nonstatic_autoadd_resources(struct pcmcia_socket *s)
846{
847 struct resource *res;
848 int i, done = 0;
849
850 if (!s->cb_dev || !s->cb_dev->bus)
851 return -ENODEV;
852
Brian Gerst0d078f62005-10-30 14:59:20 -0800853#if defined(CONFIG_X86)
Dominik Brodowskib6d00f02005-06-27 16:29:02 -0700854 /* If this is the root bus, the risk of hitting
855 * some strange system devices which aren't protected
856 * by either ACPI resource tables or properly requested
857 * resources is too big. Therefore, don't do auto-adding
858 * of resources at the moment.
859 */
860 if (s->cb_dev->bus->number == 0)
861 return -EINVAL;
862#endif
863
Bjorn Helgaas89a74ec2010-02-23 10:24:31 -0700864 pci_bus_for_each_resource(s->cb_dev->bus, res, i) {
Dominik Brodowski3c299762005-06-27 16:28:46 -0700865 if (!res)
866 continue;
867
868 if (res->flags & IORESOURCE_IO) {
869 if (res == &ioport_resource)
870 continue;
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200871 dev_printk(KERN_INFO, &s->cb_dev->dev,
Bjorn Helgaase1944c62010-03-16 15:53:08 -0600872 "pcmcia: parent PCI bridge window: %pR\n",
873 res);
Dominik Brodowski3c299762005-06-27 16:28:46 -0700874 if (!adjust_io(s, ADD_MANAGED_RESOURCE, res->start, res->end))
875 done |= IORESOURCE_IO;
876
877 }
878
879 if (res->flags & IORESOURCE_MEM) {
880 if (res == &iomem_resource)
881 continue;
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200882 dev_printk(KERN_INFO, &s->cb_dev->dev,
Bjorn Helgaase1944c62010-03-16 15:53:08 -0600883 "pcmcia: parent PCI bridge window: %pR\n",
884 res);
Dominik Brodowski3c299762005-06-27 16:28:46 -0700885 if (!adjust_memory(s, ADD_MANAGED_RESOURCE, res->start, res->end))
886 done |= IORESOURCE_MEM;
887 }
888 }
889
890 /* if we got at least one of IO, and one of MEM, we can be glad and
891 * activate the PCMCIA subsystem */
Dominik Brodowski54bb5672005-09-28 19:29:59 +0200892 if (done == (IORESOURCE_MEM | IORESOURCE_IO))
Dominik Brodowski3c299762005-06-27 16:28:46 -0700893 s->resource_setup_done = 1;
894
895 return 0;
896}
897
898#else
899
900static inline int nonstatic_autoadd_resources(struct pcmcia_socket *s)
901{
902 return -ENODEV;
903}
904
905#endif
906
907
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908static int nonstatic_init(struct pcmcia_socket *s)
909{
910 struct socket_data *data;
911
Dominik Brodowski8084b372005-12-11 21:18:26 +0100912 data = kzalloc(sizeof(struct socket_data), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 if (!data)
914 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915
916 data->mem_db.next = &data->mem_db;
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100917 data->mem_db_valid.next = &data->mem_db_valid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918 data->io_db.next = &data->io_db;
919
920 s->resource_data = (void *) data;
921
Dominik Brodowski3c299762005-06-27 16:28:46 -0700922 nonstatic_autoadd_resources(s);
923
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924 return 0;
925}
926
927static void nonstatic_release_resource_db(struct pcmcia_socket *s)
928{
929 struct socket_data *data = s->resource_data;
930 struct resource_map *p, *q;
931
Dominik Brodowski7b4884c2010-02-17 16:25:53 +0100932 for (p = data->mem_db_valid.next; p != &data->mem_db_valid; p = q) {
933 q = p->next;
934 kfree(p);
935 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 for (p = data->mem_db.next; p != &data->mem_db; p = q) {
937 q = p->next;
938 kfree(p);
939 }
940 for (p = data->io_db.next; p != &data->io_db; p = q) {
941 q = p->next;
942 kfree(p);
943 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944}
945
946
947struct pccard_resource_ops pccard_nonstatic_ops = {
948 .validate_mem = pcmcia_nonstatic_validate_mem,
949 .adjust_io_region = nonstatic_adjust_io_region,
950 .find_io = nonstatic_find_io_region,
951 .find_mem = nonstatic_find_mem_region,
Dominik Brodowskic5023802008-06-19 19:02:52 +0200952 .add_io = adjust_io,
953 .add_mem = adjust_memory,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954 .init = nonstatic_init,
955 .exit = nonstatic_release_resource_db,
956};
957EXPORT_SYMBOL(pccard_nonstatic_ops);
958
959
960/* sysfs interface to the resource database */
961
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200962static ssize_t show_io_db(struct device *dev,
963 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200965 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 struct socket_data *data;
967 struct resource_map *p;
968 ssize_t ret = 0;
969
Dominik Brodowskicfe5d802010-01-17 19:31:45 +0100970 mutex_lock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 data = s->resource_data;
972
973 for (p = data->io_db.next; p != &data->io_db; p = p->next) {
974 if (ret > (PAGE_SIZE - 10))
975 continue;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100976 ret += snprintf(&buf[ret], (PAGE_SIZE - ret - 1),
977 "0x%08lx - 0x%08lx\n",
978 ((unsigned long) p->base),
979 ((unsigned long) p->base + p->num - 1));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 }
981
Dominik Brodowskicfe5d802010-01-17 19:31:45 +0100982 mutex_unlock(&s->ops_mutex);
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100983 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984}
985
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200986static ssize_t store_io_db(struct device *dev,
987 struct device_attribute *attr,
988 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +0200990 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 unsigned long start_addr, end_addr;
Dominik Brodowski22916632005-06-27 16:28:46 -0700992 unsigned int add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 ssize_t ret = 0;
994
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100995 ret = sscanf(buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100997 ret = sscanf(buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -0700998 add = REMOVE_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001000 ret = sscanf(buf, "0x%lx - 0x%lx", &start_addr,
1001 &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -07001002 add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003 if (ret != 2)
1004 return -EINVAL;
1005 }
1006 }
Dominik Brodowski1146bc72005-09-28 19:28:37 +02001007 if (end_addr < start_addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008 return -EINVAL;
1009
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001010 mutex_lock(&s->ops_mutex);
Dominik Brodowski22916632005-06-27 16:28:46 -07001011 ret = adjust_io(s, add, start_addr, end_addr);
Dominik Brodowski3c299762005-06-27 16:28:46 -07001012 if (!ret)
1013 s->resource_setup_new = 1;
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001014 mutex_unlock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015
1016 return ret ? ret : count;
1017}
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001018static DEVICE_ATTR(available_resources_io, 0600, show_io_db, store_io_db);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001020static ssize_t show_mem_db(struct device *dev,
1021 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001023 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024 struct socket_data *data;
1025 struct resource_map *p;
1026 ssize_t ret = 0;
1027
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001028 mutex_lock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029 data = s->resource_data;
1030
Dominik Brodowski7b4884c2010-02-17 16:25:53 +01001031 for (p = data->mem_db_valid.next; p != &data->mem_db_valid;
1032 p = p->next) {
1033 if (ret > (PAGE_SIZE - 10))
1034 continue;
1035 ret += snprintf(&buf[ret], (PAGE_SIZE - ret - 1),
1036 "0x%08lx - 0x%08lx\n",
1037 ((unsigned long) p->base),
1038 ((unsigned long) p->base + p->num - 1));
1039 }
1040
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041 for (p = data->mem_db.next; p != &data->mem_db; p = p->next) {
1042 if (ret > (PAGE_SIZE - 10))
1043 continue;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001044 ret += snprintf(&buf[ret], (PAGE_SIZE - ret - 1),
1045 "0x%08lx - 0x%08lx\n",
1046 ((unsigned long) p->base),
1047 ((unsigned long) p->base + p->num - 1));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048 }
1049
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001050 mutex_unlock(&s->ops_mutex);
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001051 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052}
1053
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001054static ssize_t store_mem_db(struct device *dev,
1055 struct device_attribute *attr,
1056 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001058 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 unsigned long start_addr, end_addr;
Dominik Brodowski22916632005-06-27 16:28:46 -07001060 unsigned int add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 ssize_t ret = 0;
1062
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001063 ret = sscanf(buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001065 ret = sscanf(buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -07001066 add = REMOVE_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001068 ret = sscanf(buf, "0x%lx - 0x%lx", &start_addr,
1069 &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -07001070 add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 if (ret != 2)
1072 return -EINVAL;
1073 }
1074 }
Dominik Brodowski1146bc72005-09-28 19:28:37 +02001075 if (end_addr < start_addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 return -EINVAL;
1077
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001078 mutex_lock(&s->ops_mutex);
Dominik Brodowski22916632005-06-27 16:28:46 -07001079 ret = adjust_memory(s, add, start_addr, end_addr);
Dominik Brodowski3c299762005-06-27 16:28:46 -07001080 if (!ret)
1081 s->resource_setup_new = 1;
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001082 mutex_unlock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083
1084 return ret ? ret : count;
1085}
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001086static DEVICE_ATTR(available_resources_mem, 0600, show_mem_db, store_mem_db);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087
David Brownell7d578962008-06-12 12:13:55 -07001088static struct attribute *pccard_rsrc_attributes[] = {
1089 &dev_attr_available_resources_io.attr,
1090 &dev_attr_available_resources_mem.attr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 NULL,
1092};
1093
David Brownell7d578962008-06-12 12:13:55 -07001094static const struct attribute_group rsrc_attributes = {
1095 .attrs = pccard_rsrc_attributes,
1096};
1097
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001098static int __devinit pccard_sysfs_add_rsrc(struct device *dev,
Dmitry Torokhovd8539d82005-09-15 02:01:36 -05001099 struct class_interface *class_intf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001101 struct pcmcia_socket *s = dev_get_drvdata(dev);
David Brownell7d578962008-06-12 12:13:55 -07001102
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103 if (s->resource_ops != &pccard_nonstatic_ops)
1104 return 0;
David Brownell7d578962008-06-12 12:13:55 -07001105 return sysfs_create_group(&dev->kobj, &rsrc_attributes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106}
1107
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001108static void __devexit pccard_sysfs_remove_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);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112
1113 if (s->resource_ops != &pccard_nonstatic_ops)
1114 return;
David Brownell7d578962008-06-12 12:13:55 -07001115 sysfs_remove_group(&dev->kobj, &rsrc_attributes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116}
1117
Sam Ravnborged49f5d2008-05-01 04:34:50 -07001118static struct class_interface pccard_rsrc_interface __refdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 .class = &pcmcia_socket_class,
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001120 .add_dev = &pccard_sysfs_add_rsrc,
1121 .remove_dev = __devexit_p(&pccard_sysfs_remove_rsrc),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122};
1123
1124static int __init nonstatic_sysfs_init(void)
1125{
1126 return class_interface_register(&pccard_rsrc_interface);
1127}
1128
1129static void __exit nonstatic_sysfs_exit(void)
1130{
1131 class_interface_unregister(&pccard_rsrc_interface);
1132}
1133
1134module_init(nonstatic_sysfs_init);
1135module_exit(nonstatic_sysfs_exit);