Blame view

drivers/pcmcia/rsrc_nonstatic.c 29.5 KB
d2912cb15   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
  /*
   * rsrc_nonstatic.c -- Resource management routines for !SS_CAP_STATIC_MAP sockets
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
6
7
8
9
10
   * The initial developer of the original code is David A. Hinds
   * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
   * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
   *
   * (C) 1999		David A. Hinds
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
12
13
14
15
16
17
18
19
20
21
22
  #include <linux/module.h>
  #include <linux/moduleparam.h>
  #include <linux/init.h>
  #include <linux/interrupt.h>
  #include <linux/kernel.h>
  #include <linux/errno.h>
  #include <linux/types.h>
  #include <linux/slab.h>
  #include <linux/ioport.h>
  #include <linux/timer.h>
  #include <linux/pci.h>
  #include <linux/device.h>
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
23
  #include <linux/io.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
25
  
  #include <asm/irq.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
  #include <pcmcia/ss.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
29
  #include <pcmcia/cistpl.h>
  #include "cs_internal.h"
49b1153ad   Dominik Brodowski   pcmcia: move all ...
30
  /* moved to rsrc_mgr.c
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
32
  MODULE_AUTHOR("David A. Hinds, Dominik Brodowski");
  MODULE_LICENSE("GPL");
49b1153ad   Dominik Brodowski   pcmcia: move all ...
33
  */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
  
  /* Parameters that can be set with 'insmod' */
  
  #define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0444)
  
  INT_MODULE_PARM(probe_mem,	1);		/* memory probe? */
  #ifdef CONFIG_PCMCIA_PROBE
  INT_MODULE_PARM(probe_io,	1);		/* IO port probe? */
  INT_MODULE_PARM(mem_limit,	0x10000);
  #endif
  
  /* for io_db and mem_db */
  struct resource_map {
  	u_long			base, num;
  	struct resource_map	*next;
  };
  
  struct socket_data {
  	struct resource_map		mem_db;
7b4884ca8   Dominik Brodowski   pcmcia: validate ...
53
  	struct resource_map		mem_db_valid;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54
  	struct resource_map		io_db;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
55
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
57
  #define MEM_PROBE_LOW	(1 << 0)
  #define MEM_PROBE_HIGH	(1 << 1)
3dace8cf1   Dominik Brodowski   pcmcia: clean up ...
58
59
60
  /* Action field */
  #define REMOVE_MANAGED_RESOURCE		1
  #define ADD_MANAGED_RESOURCE		2
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
61
62
63
64
65
66
67
68
  
  /*======================================================================
  
      Linux resource management extensions
  
  ======================================================================*/
  
  static struct resource *
2427ddd8f   Greg Kroah-Hartman   [PATCH] 64bit Res...
69
70
  claim_region(struct pcmcia_socket *s, resource_size_t base,
  		resource_size_t size, int type, char *name)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
72
73
74
  {
  	struct resource *res, *parent;
  
  	parent = type & IORESOURCE_MEM ? &iomem_resource : &ioport_resource;
49b1153ad   Dominik Brodowski   pcmcia: move all ...
75
  	res = pcmcia_make_resource(base, size, type | IORESOURCE_BUSY, name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
  
  	if (res) {
  #ifdef CONFIG_PCI
  		if (s && s->cb_dev)
  			parent = pci_find_parent_resource(s->cb_dev, res);
  #endif
  		if (!parent || request_resource(parent, res)) {
  			kfree(res);
  			res = NULL;
  		}
  	}
  	return res;
  }
  
  static void free_region(struct resource *res)
  {
  	if (res) {
  		release_resource(res);
  		kfree(res);
  	}
  }
  
  /*======================================================================
  
      These manage the internal databases of available resources.
  
  ======================================================================*/
  
  static int add_interval(struct resource_map *map, u_long base, u_long num)
  {
1168386aa   Dominik Brodowski   pcmcia: deprecate...
106
  	struct resource_map *p, *q;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107

1168386aa   Dominik Brodowski   pcmcia: deprecate...
108
  	for (p = map; ; p = p->next) {
f309cb3e4   Dominik Brodowski   pcmcia: allow for...
109
110
111
112
  		if ((p != map) && (p->base+p->num >= base)) {
  			p->num = max(num + base - p->base, p->num);
  			return 0;
  		}
1168386aa   Dominik Brodowski   pcmcia: deprecate...
113
114
115
116
117
118
119
120
121
122
123
124
  		if ((p->next == map) || (p->next->base > base+num-1))
  			break;
  	}
  	q = kmalloc(sizeof(struct resource_map), GFP_KERNEL);
  	if (!q) {
  		printk(KERN_WARNING "out of memory to update resources
  ");
  		return -ENOMEM;
  	}
  	q->base = base; q->num = num;
  	q->next = p->next; p->next = q;
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
126
127
128
129
130
  }
  
  /*====================================================================*/
  
  static int sub_interval(struct resource_map *map, u_long base, u_long num)
  {
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
  	struct resource_map *p, *q;
  
  	for (p = map; ; p = q) {
  		q = p->next;
  		if (q == map)
  			break;
  		if ((q->base+q->num > base) && (base+num > q->base)) {
  			if (q->base >= base) {
  				if (q->base+q->num <= base+num) {
  					/* Delete whole block */
  					p->next = q->next;
  					kfree(q);
  					/* don't advance the pointer yet */
  					q = p;
  				} else {
  					/* Cut off bit from the front */
  					q->num = q->base + q->num - base - num;
  					q->base = base + num;
  				}
  			} else if (q->base+q->num <= base+num) {
  				/* Cut off bit from the end */
  				q->num = base - q->base;
  			} else {
  				/* Split the block into two pieces */
  				p = kmalloc(sizeof(struct resource_map),
  					GFP_KERNEL);
  				if (!p) {
  					printk(KERN_WARNING "out of memory to update resources
  ");
  					return -ENOMEM;
  				}
  				p->base = base+num;
  				p->num = q->base+q->num - p->base;
  				q->num = base - q->base;
  				p->next = q->next ; q->next = p;
  			}
1168386aa   Dominik Brodowski   pcmcia: deprecate...
167
  		}
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
168
169
  	}
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
170
171
172
173
174
175
176
177
178
179
  }
  
  /*======================================================================
  
      These routines examine a region of IO or memory addresses to
      determine what ranges might be genuinely available.
  
  ======================================================================*/
  
  #ifdef CONFIG_PCMCIA_PROBE
906da809c   Olof Johansson   pcmcia: replace k...
180
181
  static void do_io_probe(struct pcmcia_socket *s, unsigned int base,
  			unsigned int num)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182
  {
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
183
184
185
186
187
  	struct resource *res;
  	struct socket_data *s_data = s->resource_data;
  	unsigned int i, j, bad;
  	int any;
  	u_char *b, hole, most;
f2e6cf767   Joe Perches   pcmcia: Convert d...
188
  	dev_info(&s->dev, "cs: IO port probe %#x-%#x:", base, base+num-1);
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
189
190
191
192
  
  	/* First, what does a floating port look like? */
  	b = kzalloc(256, GFP_KERNEL);
  	if (!b) {
f2e6cf767   Joe Perches   pcmcia: Convert d...
193
194
195
196
  		pr_cont("
  ");
  		dev_err(&s->dev, "do_io_probe: unable to kmalloc 256 bytes
  ");
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
197
198
199
  		return;
  	}
  	for (i = base, most = 0; i < base+num; i += 8) {
509b0865f   Dominik Brodowski   pcmcia: fix io_pr...
200
  		res = claim_region(s, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
  		if (!res)
  			continue;
  		hole = inb(i);
  		for (j = 1; j < 8; j++)
  			if (inb(i+j) != hole)
  				break;
  		free_region(res);
  		if ((j == 8) && (++b[hole] > b[most]))
  			most = hole;
  		if (b[most] == 127)
  			break;
  	}
  	kfree(b);
  
  	bad = any = 0;
  	for (i = base; i < base+num; i += 8) {
509b0865f   Dominik Brodowski   pcmcia: fix io_pr...
217
218
219
  		res = claim_region(s, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
  		if (!res) {
  			if (!any)
f2e6cf767   Joe Perches   pcmcia: Convert d...
220
  				pr_cont(" excluding");
509b0865f   Dominik Brodowski   pcmcia: fix io_pr...
221
222
  			if (!bad)
  				bad = any = i;
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
223
  			continue;
509b0865f   Dominik Brodowski   pcmcia: fix io_pr...
224
  		}
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
225
226
227
228
229
230
  		for (j = 0; j < 8; j++)
  			if (inb(i+j) != most)
  				break;
  		free_region(res);
  		if (j < 8) {
  			if (!any)
f2e6cf767   Joe Perches   pcmcia: Convert d...
231
  				pr_cont(" excluding");
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
232
233
234
235
236
  			if (!bad)
  				bad = any = i;
  		} else {
  			if (bad) {
  				sub_interval(&s_data->io_db, bad, i-bad);
f2e6cf767   Joe Perches   pcmcia: Convert d...
237
  				pr_cont(" %#x-%#x", bad, i-1);
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
238
239
240
241
242
243
  				bad = 0;
  			}
  		}
  	}
  	if (bad) {
  		if ((num > 16) && (bad == base) && (i == base+num)) {
509b0865f   Dominik Brodowski   pcmcia: fix io_pr...
244
  			sub_interval(&s_data->io_db, bad, i-bad);
f2e6cf767   Joe Perches   pcmcia: Convert d...
245
246
  			pr_cont(" nothing: probe failed.
  ");
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
247
248
249
  			return;
  		} else {
  			sub_interval(&s_data->io_db, bad, i-bad);
f2e6cf767   Joe Perches   pcmcia: Convert d...
250
  			pr_cont(" %#x-%#x", bad, i-1);
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
251
252
  		}
  	}
f2e6cf767   Joe Perches   pcmcia: Convert d...
253
254
  	pr_cont("%s
  ", !any ? " clean" : "");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
256
  }
  #endif
3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
257
  /*======================================================================*/
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258

3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
259
260
261
  /**
   * readable() - iomem validation function for cards with a valid CIS
   */
c5081d5f4   Dominik Brodowski   pcmcia: simplify ...
262
263
  static int readable(struct pcmcia_socket *s, struct resource *res,
  		    unsigned int *count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
  {
3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
265
  	int ret = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
266

7ab248554   Dominik Brodowski   pcmcia: add locki...
267
268
269
270
271
  	if (s->fake_cis) {
  		dev_dbg(&s->dev, "fake CIS is being used: can't validate mem
  ");
  		return 0;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
272
273
274
275
  
  	s->cis_mem.res = res;
  	s->cis_virt = ioremap(res->start, s->map_size);
  	if (s->cis_virt) {
6b8e087b8   Dominik Brodowski   pcmcia: add locki...
276
  		mutex_unlock(&s->ops_mutex);
6e7b51a73   Dominik Brodowski   pcmcia: move cist...
277
278
279
  		/* as we're only called from pcmcia.c, we're safe */
  		if (s->callback->validate)
  			ret = s->callback->validate(s, count);
904e37774   Dominik Brodowski   pcmcia: validate ...
280
  		/* invalidate mapping */
6b8e087b8   Dominik Brodowski   pcmcia: add locki...
281
  		mutex_lock(&s->ops_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
283
  		iounmap(s->cis_virt);
  		s->cis_virt = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284
285
  	}
  	s->cis_mem.res = NULL;
3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
286
287
288
  	if ((ret) || (*count == 0))
  		return -EINVAL;
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289
  }
3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
290
291
292
293
294
  /**
   * checksum() - iomem validation function for simple memory cards
   */
  static int checksum(struct pcmcia_socket *s, struct resource *res,
  		    unsigned int *value)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
  {
  	pccard_mem_map map;
  	int i, a = 0, b = -1, d;
  	void __iomem *virt;
  
  	virt = ioremap(res->start, s->map_size);
  	if (virt) {
  		map.map = 0;
  		map.flags = MAP_ACTIVE;
  		map.speed = 0;
  		map.res = res;
  		map.card_start = 0;
  		s->ops->set_mem_map(s, &map);
  
  		/* Don't bother checking every word... */
  		for (i = 0; i < s->map_size; i += 44) {
  			d = readl(virt+i);
  			a += d;
  			b &= d;
  		}
  
  		map.flags = 0;
  		s->ops->set_mem_map(s, &map);
  
  		iounmap(virt);
  	}
3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
321
322
  	if (b == -1)
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
323

3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
324
  	*value = a;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
325

3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
326
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
327
  }
3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
  /**
   * do_validate_mem() - low level validate a memory region for PCMCIA use
   * @s:		PCMCIA socket to validate
   * @base:	start address of resource to check
   * @size:	size of resource to check
   * @validate:	validation function to use
   *
   * do_validate_mem() splits up the memory region which is to be checked
   * into two parts. Both are passed to the @validate() function. If
   * @validate() returns non-zero, or the value parameter to @validate()
   * is zero, or the value parameter is different between both calls,
   * the check fails, and -EINVAL is returned. Else, 0 is returned.
   */
  static int do_validate_mem(struct pcmcia_socket *s,
  			   unsigned long base, unsigned long size,
  			   int validate (struct pcmcia_socket *s,
  					 struct resource *res,
  					 unsigned int *value))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
346
  {
7b4884ca8   Dominik Brodowski   pcmcia: validate ...
347
  	struct socket_data *s_data = s->resource_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
348
  	struct resource *res1, *res2;
3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
349
350
  	unsigned int info1 = 1, info2 = 1;
  	int ret = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351

9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
352
353
354
  	res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "PCMCIA memprobe");
  	res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM,
  			"PCMCIA memprobe");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
355
356
  
  	if (res1 && res2) {
3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
357
358
359
360
361
  		ret = 0;
  		if (validate) {
  			ret = validate(s, res1, &info1);
  			ret += validate(s, res2, &info2);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
362
  	}
3490a69bd   Dominik Brodowski   pcmcia: use prope...
363
  	dev_dbg(&s->dev, "cs: memory probe 0x%06lx-0x%06lx: %pr %pr %u %u %u",
3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
364
  		base, base+size-1, res1, res2, ret, info1, info2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365

bec0b2ce8   Jesper Juhl   pcmcia: avoid sta...
366
367
  	free_region(res2);
  	free_region(res1);
3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
368
369
  	if ((ret) || (info1 != info2) || (info1 == 0))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
370

7b4884ca8   Dominik Brodowski   pcmcia: validate ...
371
372
373
374
  	if (validate && !s->fake_cis) {
  		/* move it to the validated data set */
  		add_interval(&s_data->mem_db_valid, base, size);
  		sub_interval(&s_data->mem_db, base, size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
  	}
3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
376
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
378

3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
  /**
   * do_mem_probe() - validate a memory region for PCMCIA use
   * @s:		PCMCIA socket to validate
   * @base:	start address of resource to check
   * @num:	size of resource to check
   * @validate:	validation function to use
   * @fallback:	validation function to use if validate fails
   *
   * do_mem_probe() checks a memory region for use by the PCMCIA subsystem.
   * To do so, the area is split up into sensible parts, and then passed
   * into the @validate() function. Only if @validate() and @fallback() fail,
   * the area is marked as unavaibale for use by the PCMCIA subsystem. The
   * function returns the size of the usable memory area.
   */
  static int do_mem_probe(struct pcmcia_socket *s, u_long base, u_long num,
  			int validate (struct pcmcia_socket *s,
  				      struct resource *res,
  				      unsigned int *value),
  			int fallback (struct pcmcia_socket *s,
  				      struct resource *res,
  				      unsigned int *value))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
400
  {
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
401
402
  	struct socket_data *s_data = s->resource_data;
  	u_long i, j, bad, fail, step;
f2e6cf767   Joe Perches   pcmcia: Convert d...
403
404
  	dev_info(&s->dev, "cs: memory probe 0x%06lx-0x%06lx:",
  		 base, base+num-1);
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
405
406
407
408
409
410
411
412
413
414
415
  	bad = fail = 0;
  	step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff);
  	/* don't allow too large steps */
  	if (step > 0x800000)
  		step = 0x800000;
  	/* cis_readable wants to map 2x map_size */
  	if (step < 2 * s->map_size)
  		step = 2 * s->map_size;
  	for (i = j = base; i < base+num; i = j + step) {
  		if (!fail) {
  			for (j = i; j < base+num; j += step) {
3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
416
  				if (!do_validate_mem(s, j, step, validate))
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
417
418
419
420
  					break;
  			}
  			fail = ((i == base) && (j == base+num));
  		}
3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
421
422
423
  		if ((fail) && (fallback)) {
  			for (j = i; j < base+num; j += step)
  				if (!do_validate_mem(s, j, step, fallback))
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
424
425
426
427
  					break;
  		}
  		if (i != j) {
  			if (!bad)
f2e6cf767   Joe Perches   pcmcia: Convert d...
428
429
  				pr_cont(" excluding");
  			pr_cont(" %#05lx-%#05lx", i, j-1);
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
430
431
432
433
  			sub_interval(&s_data->mem_db, i, j-i);
  			bad += j-i;
  		}
  	}
f2e6cf767   Joe Perches   pcmcia: Convert d...
434
435
  	pr_cont("%s
  ", !bad ? " clean" : "");
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
436
  	return num - bad;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
437
  }
3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
438

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
439
  #ifdef CONFIG_PCMCIA_PROBE
3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
440
441
442
443
444
  /**
   * inv_probe() - top-to-bottom search for one usuable high memory area
   * @s:		PCMCIA socket to validate
   * @m:		resource_map to check
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
445
446
  static u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s)
  {
de75914ee   Dominik Brodowski   [PATCH] pcmcia: v...
447
448
449
450
451
452
453
454
455
456
457
458
  	struct socket_data *s_data = s->resource_data;
  	u_long ok;
  	if (m == &s_data->mem_db)
  		return 0;
  	ok = inv_probe(m->next, s);
  	if (ok) {
  		if (m->base >= 0x100000)
  			sub_interval(&s_data->mem_db, m->base, m->num);
  		return ok;
  	}
  	if (m->base < 0x100000)
  		return 0;
3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
459
  	return do_mem_probe(s, m->base, m->num, readable, checksum);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
460
  }
3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
461
462
463
464
465
466
467
468
469
  /**
   * validate_mem() - memory probe function
   * @s:		PCMCIA socket to validate
   * @probe_mask: MEM_PROBE_LOW | MEM_PROBE_HIGH
   *
   * The memory probe.  If the memory list includes a 64K-aligned block
   * below 1MB, we probe in 64K chunks, and as soon as we accumulate at
   * least mem_limit free space, we quit. Returns 0 on usuable ports.
   */
de75914ee   Dominik Brodowski   [PATCH] pcmcia: v...
470
  static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
471
  {
de75914ee   Dominik Brodowski   [PATCH] pcmcia: v...
472
473
474
475
  	struct resource_map *m, mm;
  	static unsigned char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 };
  	unsigned long b, i, ok = 0;
  	struct socket_data *s_data = s->resource_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
476

de75914ee   Dominik Brodowski   [PATCH] pcmcia: v...
477
478
479
480
  	/* We do up to four passes through the list */
  	if (probe_mask & MEM_PROBE_HIGH) {
  		if (inv_probe(s_data->mem_db.next, s) > 0)
  			return 0;
7b4884ca8   Dominik Brodowski   pcmcia: validate ...
481
482
  		if (s_data->mem_db_valid.next != &s_data->mem_db_valid)
  			return 0;
f2e6cf767   Joe Perches   pcmcia: Convert d...
483
  		dev_notice(&s->dev,
dbe4ea5fd   Dominik Brodowski   pcmcia: use dev_p...
484
485
  			   "cs: warning: no high memory space available!
  ");
de75914ee   Dominik Brodowski   [PATCH] pcmcia: v...
486
  		return -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
487
  	}
de75914ee   Dominik Brodowski   [PATCH] pcmcia: v...
488
489
490
491
492
493
494
  
  	for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
  		mm = *m;
  		/* Only probe < 1 MB */
  		if (mm.base >= 0x100000)
  			continue;
  		if ((mm.base | mm.num) & 0xffff) {
3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
495
496
  			ok += do_mem_probe(s, mm.base, mm.num, readable,
  					   checksum);
de75914ee   Dominik Brodowski   [PATCH] pcmcia: v...
497
498
499
500
501
502
503
504
505
  			continue;
  		}
  		/* Special probe for 64K-aligned block */
  		for (i = 0; i < 4; i++) {
  			b = order[i] << 12;
  			if ((b >= mm.base) && (b+0x10000 <= mm.base+mm.num)) {
  				if (ok >= mem_limit)
  					sub_interval(&s_data->mem_db, b, 0x10000);
  				else
3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
506
507
  					ok += do_mem_probe(s, b, 0x10000,
  							   readable, checksum);
de75914ee   Dominik Brodowski   [PATCH] pcmcia: v...
508
509
  			}
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
510
  	}
de75914ee   Dominik Brodowski   [PATCH] pcmcia: v...
511
512
513
514
515
  
  	if (ok > 0)
  		return 0;
  
  	return -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
516
517
518
  }
  
  #else /* CONFIG_PCMCIA_PROBE */
3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
519
520
521
522
523
524
525
  /**
   * validate_mem() - memory probe function
   * @s:		PCMCIA socket to validate
   * @probe_mask: ignored
   *
   * Returns 0 on usuable ports.
   */
2cff94472   Andrew Morton   [PATCH] pcmcia: v...
526
  static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
527
528
529
  {
  	struct resource_map *m, mm;
  	struct socket_data *s_data = s->resource_data;
2cff94472   Andrew Morton   [PATCH] pcmcia: v...
530
  	unsigned long ok = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
531
532
533
  
  	for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
  		mm = *m;
3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
534
  		ok += do_mem_probe(s, mm.base, mm.num, readable, checksum);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
535
  	}
2cff94472   Andrew Morton   [PATCH] pcmcia: v...
536
537
538
  	if (ok > 0)
  		return 0;
  	return -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
539
540
541
  }
  
  #endif /* CONFIG_PCMCIA_PROBE */
3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
542
543
544
545
546
547
548
  /**
   * pcmcia_nonstatic_validate_mem() - try to validate iomem for PCMCIA use
   * @s:		PCMCIA socket to validate
   *
   * This is tricky... when we set up CIS memory, we try to validate
   * the memory window space allocations.
   *
7fe908dd1   Dominik Brodowski   [PATCH] pcmcia: u...
549
   * Locking note: Must be called with skt_mutex held!
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
550
   */
de75914ee   Dominik Brodowski   [PATCH] pcmcia: v...
551
  static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
552
553
  {
  	struct socket_data *s_data = s->resource_data;
de75914ee   Dominik Brodowski   [PATCH] pcmcia: v...
554
  	unsigned int probe_mask = MEM_PROBE_LOW;
7b4884ca8   Dominik Brodowski   pcmcia: validate ...
555
  	int ret;
de75914ee   Dominik Brodowski   [PATCH] pcmcia: v...
556

7b4884ca8   Dominik Brodowski   pcmcia: validate ...
557
  	if (!probe_mem || !(s->state & SOCKET_PRESENT))
de75914ee   Dominik Brodowski   [PATCH] pcmcia: v...
558
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
559

de75914ee   Dominik Brodowski   [PATCH] pcmcia: v...
560
561
  	if (s->features & SS_CAP_PAGE_REGS)
  		probe_mask = MEM_PROBE_HIGH;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
562

7b4884ca8   Dominik Brodowski   pcmcia: validate ...
563
  	ret = validate_mem(s, probe_mask);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564

7b4884ca8   Dominik Brodowski   pcmcia: validate ...
565
566
  	if (s_data->mem_db_valid.next != &s_data->mem_db_valid)
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
567

de75914ee   Dominik Brodowski   [PATCH] pcmcia: v...
568
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
569
570
571
572
573
574
575
  }
  
  struct pcmcia_align_data {
  	unsigned long	mask;
  	unsigned long	offset;
  	struct resource_map	*map;
  };
147a27460   Dominik Brodowski   pcmcia: fix up al...
576
577
  static resource_size_t pcmcia_common_align(struct pcmcia_align_data *align_data,
  					resource_size_t start)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
578
  {
147a27460   Dominik Brodowski   pcmcia: fix up al...
579
  	resource_size_t ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
580
581
582
  	/*
  	 * Ensure that we have the correct start address
  	 */
147a27460   Dominik Brodowski   pcmcia: fix up al...
583
584
585
586
  	ret = (start & ~align_data->mask) + align_data->offset;
  	if (ret < start)
  		ret += align_data->mask + 1;
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
587
  }
b26b2d494   Dominik Brodowski   resource/PCI: ali...
588
  static resource_size_t
3b7a17fcd   Dominik Brodowski   resource/PCI: mar...
589
590
  pcmcia_align(void *align_data, const struct resource *res,
  	resource_size_t size, resource_size_t align)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
591
592
593
  {
  	struct pcmcia_align_data *data = align_data;
  	struct resource_map *m;
b26b2d494   Dominik Brodowski   resource/PCI: ali...
594
  	resource_size_t start;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
595

147a27460   Dominik Brodowski   pcmcia: fix up al...
596
  	start = pcmcia_common_align(data, res->start);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
597
598
  
  	for (m = data->map->next; m != data->map; m = m->next) {
147a27460   Dominik Brodowski   pcmcia: fix up al...
599
600
  		unsigned long map_start = m->base;
  		unsigned long map_end = m->base + m->num - 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
601
602
603
604
605
606
  
  		/*
  		 * If the lower resources are not available, try aligning
  		 * to this entry of the resource database to see if it'll
  		 * fit here.
  		 */
147a27460   Dominik Brodowski   pcmcia: fix up al...
607
608
  		if (start < map_start)
  			start = pcmcia_common_align(data, map_start);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
609
610
611
612
613
  
  		/*
  		 * If we're above the area which was passed in, there's
  		 * no point proceeding.
  		 */
147a27460   Dominik Brodowski   pcmcia: fix up al...
614
  		if (start >= res->end)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
615
  			break;
147a27460   Dominik Brodowski   pcmcia: fix up al...
616
  		if ((start + size - 1) <= map_end)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
617
618
619
620
621
622
623
  			break;
  	}
  
  	/*
  	 * If we failed to find something suitable, ensure we fail.
  	 */
  	if (m == data->map)
b26b2d494   Dominik Brodowski   resource/PCI: ali...
624
625
626
  		start = res->end;
  
  	return start;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
627
628
629
630
631
632
  }
  
  /*
   * Adjust an existing IO region allocation, but making sure that we don't
   * encroach outside the resources which the user supplied.
   */
b19a7275d   Dominik Brodowski   pcmcia: clarify a...
633
634
635
  static int __nonstatic_adjust_io_region(struct pcmcia_socket *s,
  					unsigned long r_start,
  					unsigned long r_end)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
636
637
638
639
  {
  	struct resource_map *m;
  	struct socket_data *s_data = s->resource_data;
  	int ret = -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
640
641
642
643
644
645
  	for (m = s_data->io_db.next; m != &s_data->io_db; m = m->next) {
  		unsigned long start = m->base;
  		unsigned long end = m->base + m->num - 1;
  
  		if (start > r_start || r_end > end)
  			continue;
b19a7275d   Dominik Brodowski   pcmcia: clarify a...
646
  		ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
647
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
  
  	return ret;
  }
  
  /*======================================================================
  
      These find ranges of I/O ports or memory addresses that are not
      currently allocated by other devices.
  
      The 'align' field should reflect the number of bits of address
      that need to be preserved from the initial value of *base.  It
      should be a power of two, greater than or equal to 'num'.  A value
      of 0 means that all bits of *base are significant.  *base should
      also be strictly less than 'align'.
  
  ======================================================================*/
b19a7275d   Dominik Brodowski   pcmcia: clarify a...
664
665
666
  static struct resource *__nonstatic_find_io_region(struct pcmcia_socket *s,
  						unsigned long base, int num,
  						unsigned long align)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
667
  {
49b1153ad   Dominik Brodowski   pcmcia: move all ...
668
669
  	struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_IO,
  						dev_name(&s->dev));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
670
671
672
673
  	struct socket_data *s_data = s->resource_data;
  	struct pcmcia_align_data data;
  	unsigned long min = base;
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
674
675
676
  	data.mask = align - 1;
  	data.offset = base & data.mask;
  	data.map = &s_data->io_db;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
677
678
679
680
681
682
683
684
  #ifdef CONFIG_PCI
  	if (s->cb_dev) {
  		ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1,
  					     min, 0, pcmcia_align, &data);
  	} else
  #endif
  		ret = allocate_resource(&ioport_resource, res, num, min, ~0UL,
  					1, pcmcia_align, &data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
685
686
687
688
689
690
691
  
  	if (ret != 0) {
  		kfree(res);
  		res = NULL;
  	}
  	return res;
  }
b19a7275d   Dominik Brodowski   pcmcia: clarify a...
692
693
  static int nonstatic_find_io(struct pcmcia_socket *s, unsigned int attr,
  			unsigned int *base, unsigned int num,
ad0c7be28   Dominik Brodowski   pcmcia: insert PC...
694
  			unsigned int align, struct resource **parent)
b19a7275d   Dominik Brodowski   pcmcia: clarify a...
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
  {
  	int i, ret = 0;
  
  	/* Check for an already-allocated window that must conflict with
  	 * what was asked for.  It is a hack because it does not catch all
  	 * potential conflicts, just the most obvious ones.
  	 */
  	for (i = 0; i < MAX_IO_WIN; i++) {
  		if (!s->io[i].res)
  			continue;
  
  		if (!*base)
  			continue;
  
  		if ((s->io[i].res->start & (align-1)) == *base)
  			return -EBUSY;
  	}
  
  	for (i = 0; i < MAX_IO_WIN; i++) {
  		struct resource *res = s->io[i].res;
  		unsigned int try;
  
  		if (res && (res->flags & IORESOURCE_BITS) !=
  			(attr & IORESOURCE_BITS))
  			continue;
  
  		if (!res) {
  			if (align == 0)
  				align = 0x10000;
  
  			res = s->io[i].res = __nonstatic_find_io_region(s,
  								*base, num,
  								align);
  			if (!res)
  				return -EINVAL;
  
  			*base = res->start;
  			s->io[i].res->flags =
  				((res->flags & ~IORESOURCE_BITS) |
  					(attr & IORESOURCE_BITS));
  			s->io[i].InUse = num;
ad0c7be28   Dominik Brodowski   pcmcia: insert PC...
736
  			*parent = res;
b19a7275d   Dominik Brodowski   pcmcia: clarify a...
737
738
739
740
741
742
743
744
745
746
  			return 0;
  		}
  
  		/* Try to extend top of window */
  		try = res->end + 1;
  		if ((*base == 0) || (*base == try)) {
  			ret =  __nonstatic_adjust_io_region(s, res->start,
  							res->end + num);
  			if (!ret) {
  				ret = adjust_resource(s->io[i].res, res->start,
28f65c11f   Joe Perches   treewide: Convert...
747
  						      resource_size(res) + num);
b19a7275d   Dominik Brodowski   pcmcia: clarify a...
748
749
750
751
  				if (ret)
  					continue;
  				*base = try;
  				s->io[i].InUse += num;
ad0c7be28   Dominik Brodowski   pcmcia: insert PC...
752
  				*parent = res;
b19a7275d   Dominik Brodowski   pcmcia: clarify a...
753
754
755
756
757
758
759
760
761
762
763
764
  				return 0;
  			}
  		}
  
  		/* Try to extend bottom of window */
  		try = res->start - num;
  		if ((*base == 0) || (*base == try)) {
  			ret =  __nonstatic_adjust_io_region(s,
  							res->start - num,
  							res->end);
  			if (!ret) {
  				ret = adjust_resource(s->io[i].res,
28f65c11f   Joe Perches   treewide: Convert...
765
766
  						      res->start - num,
  						      resource_size(res) + num);
b19a7275d   Dominik Brodowski   pcmcia: clarify a...
767
768
769
770
  				if (ret)
  					continue;
  				*base = try;
  				s->io[i].InUse += num;
ad0c7be28   Dominik Brodowski   pcmcia: insert PC...
771
  				*parent = res;
b19a7275d   Dominik Brodowski   pcmcia: clarify a...
772
773
774
775
776
777
778
  				return 0;
  			}
  		}
  	}
  
  	return -EINVAL;
  }
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
779
  static struct resource *nonstatic_find_mem_region(u_long base, u_long num,
e94e15f70   Dominik Brodowski   [PATCH] pcmcia: c...
780
  		u_long align, int low, struct pcmcia_socket *s)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
781
  {
49b1153ad   Dominik Brodowski   pcmcia: move all ...
782
783
  	struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_MEM,
  						dev_name(&s->dev));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
784
785
786
  	struct socket_data *s_data = s->resource_data;
  	struct pcmcia_align_data data;
  	unsigned long min, max;
7b4884ca8   Dominik Brodowski   pcmcia: validate ...
787
  	int ret, i, j;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
788
789
790
791
792
  
  	low = low || !(s->features & SS_CAP_PAGE_REGS);
  
  	data.mask = align - 1;
  	data.offset = base & data.mask;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
793
794
  
  	for (i = 0; i < 2; i++) {
7b4884ca8   Dominik Brodowski   pcmcia: validate ...
795
  		data.map = &s_data->mem_db_valid;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
796
797
798
799
800
801
802
  		if (low) {
  			max = 0x100000UL;
  			min = base < max ? base : 0;
  		} else {
  			max = ~0UL;
  			min = 0x100000UL + base;
  		}
7b4884ca8   Dominik Brodowski   pcmcia: validate ...
803
  		for (j = 0; j < 2; j++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
804
  #ifdef CONFIG_PCI
7b4884ca8   Dominik Brodowski   pcmcia: validate ...
805
806
807
808
809
  			if (s->cb_dev) {
  				ret = pci_bus_alloc_resource(s->cb_dev->bus,
  							res, num, 1, min, 0,
  							pcmcia_align, &data);
  			} else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
810
  #endif
7b4884ca8   Dominik Brodowski   pcmcia: validate ...
811
812
813
814
815
816
817
818
819
  			{
  				ret = allocate_resource(&iomem_resource,
  							res, num, min, max, 1,
  							pcmcia_align, &data);
  			}
  			if (ret == 0)
  				break;
  			data.map = &s_data->mem_db;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
820
821
822
823
824
825
826
827
828
829
830
  		if (ret == 0 || low)
  			break;
  		low = 1;
  	}
  
  	if (ret != 0) {
  		kfree(res);
  		res = NULL;
  	}
  	return res;
  }
22916638b   Dominik Brodowski   [PATCH] pcmcia: u...
831
  static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
832
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
833
  	struct socket_data *data = s->resource_data;
22916638b   Dominik Brodowski   [PATCH] pcmcia: u...
834
835
  	unsigned long size = end - start + 1;
  	int ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
836

1146bc743   Dominik Brodowski   [PATCH] pcmcia: a...
837
  	if (end < start)
22916638b   Dominik Brodowski   [PATCH] pcmcia: u...
838
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
839

22916638b   Dominik Brodowski   [PATCH] pcmcia: u...
840
  	switch (action) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
841
  	case ADD_MANAGED_RESOURCE:
22916638b   Dominik Brodowski   [PATCH] pcmcia: u...
842
  		ret = add_interval(&data->mem_db, start, size);
3f32b3c09   Dominik Brodowski   pcmcia: rsrc_nons...
843
844
  		if (!ret)
  			do_mem_probe(s, start, size, NULL, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
845
846
  		break;
  	case REMOVE_MANAGED_RESOURCE:
22916638b   Dominik Brodowski   [PATCH] pcmcia: u...
847
  		ret = sub_interval(&data->mem_db, start, size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
848
849
  		break;
  	default:
22916638b   Dominik Brodowski   [PATCH] pcmcia: u...
850
  		ret = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
851
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
852
853
854
  
  	return ret;
  }
22916638b   Dominik Brodowski   [PATCH] pcmcia: u...
855
  static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
856
857
  {
  	struct socket_data *data = s->resource_data;
41b97ab50   Dominik Brodowski   pcmcia: fix iopor...
858
  	unsigned long size;
22916638b   Dominik Brodowski   [PATCH] pcmcia: u...
859
  	int ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
860

9713ab28e   Dominik Brodowski   pcmcia: do not us...
861
862
863
864
865
866
  #if defined(CONFIG_X86)
  	/* on x86, avoid anything < 0x100 for it is often used for
  	 * legacy platform devices */
  	if (start < 0x100)
  		start = 0x100;
  #endif
41b97ab50   Dominik Brodowski   pcmcia: fix iopor...
867
  	size = end - start + 1;
1146bc743   Dominik Brodowski   [PATCH] pcmcia: a...
868
  	if (end < start)
22916638b   Dominik Brodowski   [PATCH] pcmcia: u...
869
870
871
872
  		return -EINVAL;
  
  	if (end > IO_SPACE_LIMIT)
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
873

22916638b   Dominik Brodowski   [PATCH] pcmcia: u...
874
  	switch (action) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
875
  	case ADD_MANAGED_RESOURCE:
22916638b   Dominik Brodowski   [PATCH] pcmcia: u...
876
877
  		if (add_interval(&data->io_db, start, size) != 0) {
  			ret = -EBUSY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
878
879
880
881
  			break;
  		}
  #ifdef CONFIG_PCMCIA_PROBE
  		if (probe_io)
22916638b   Dominik Brodowski   [PATCH] pcmcia: u...
882
  			do_io_probe(s, start, size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
883
884
885
  #endif
  		break;
  	case REMOVE_MANAGED_RESOURCE:
22916638b   Dominik Brodowski   [PATCH] pcmcia: u...
886
  		sub_interval(&data->io_db, start, size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
887
888
  		break;
  	default:
22916638b   Dominik Brodowski   [PATCH] pcmcia: u...
889
  		ret = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
890
891
  		break;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
892
893
894
  
  	return ret;
  }
3c29976a6   Dominik Brodowski   [PATCH] pcmcia: m...
895
896
897
898
899
900
901
902
  #ifdef CONFIG_PCI
  static int nonstatic_autoadd_resources(struct pcmcia_socket *s)
  {
  	struct resource *res;
  	int i, done = 0;
  
  	if (!s->cb_dev || !s->cb_dev->bus)
  		return -ENODEV;
0d078f6f9   Brian Gerst   [PATCH] CONFIG_IA32
903
  #if defined(CONFIG_X86)
cf26e8dc4   Dominik Brodowski   pcmcia: do not au...
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
  	/* If this is the root bus, the risk of hitting some strange
  	 * system devices is too high: If a driver isn't loaded, the
  	 * resources are not claimed; even if a driver is loaded, it
  	 * may not request all resources or even the wrong one. We
  	 * can neither trust the rest of the kernel nor ACPI/PNP and
  	 * CRS parsing to get it right. Therefore, use several
  	 * safeguards:
  	 *
  	 * - Do not auto-add resources if the CardBus bridge is on
  	 *   the PCI root bus
  	 *
  	 * - Avoid any I/O ports < 0x100.
  	 *
  	 * - On PCI-PCI bridges, only use resources which are set up
  	 *   exclusively for the secondary PCI bus: the risk of hitting
  	 *   system devices is quite low, as they usually aren't
  	 *   connected to the secondary PCI bus.
b6d00f0de   Dominik Brodowski   [PATCH] ACPI-base...
921
922
923
  	 */
  	if (s->cb_dev->bus->number == 0)
  		return -EINVAL;
b6d00f0de   Dominik Brodowski   [PATCH] ACPI-base...
924

cf26e8dc4   Dominik Brodowski   pcmcia: do not au...
925
926
927
  	for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) {
  		res = s->cb_dev->bus->resource[i];
  #else
89a74eccc   Bjorn Helgaas   PCI: add pci_bus_...
928
  	pci_bus_for_each_resource(s->cb_dev->bus, res, i) {
cf26e8dc4   Dominik Brodowski   pcmcia: do not au...
929
  #endif
3c29976a6   Dominik Brodowski   [PATCH] pcmcia: m...
930
931
932
933
  		if (!res)
  			continue;
  
  		if (res->flags & IORESOURCE_IO) {
cf26e8dc4   Dominik Brodowski   pcmcia: do not au...
934
935
936
  			/* safeguard against the root resource, where the
  			 * risk of hitting any other device would be too
  			 * high */
3c29976a6   Dominik Brodowski   [PATCH] pcmcia: m...
937
938
  			if (res == &ioport_resource)
  				continue;
cf26e8dc4   Dominik Brodowski   pcmcia: do not au...
939

f2e6cf767   Joe Perches   pcmcia: Convert d...
940
941
942
943
  			dev_info(&s->cb_dev->dev,
  				 "pcmcia: parent PCI bridge window: %pR
  ",
  				 res);
3c29976a6   Dominik Brodowski   [PATCH] pcmcia: m...
944
945
946
947
948
949
  			if (!adjust_io(s, ADD_MANAGED_RESOURCE, res->start, res->end))
  				done |= IORESOURCE_IO;
  
  		}
  
  		if (res->flags & IORESOURCE_MEM) {
cf26e8dc4   Dominik Brodowski   pcmcia: do not au...
950
951
952
  			/* safeguard against the root resource, where the
  			 * risk of hitting any other device would be too
  			 * high */
3c29976a6   Dominik Brodowski   [PATCH] pcmcia: m...
953
954
  			if (res == &iomem_resource)
  				continue;
cf26e8dc4   Dominik Brodowski   pcmcia: do not au...
955

f2e6cf767   Joe Perches   pcmcia: Convert d...
956
957
958
959
  			dev_info(&s->cb_dev->dev,
  				 "pcmcia: parent PCI bridge window: %pR
  ",
  				 res);
3c29976a6   Dominik Brodowski   [PATCH] pcmcia: m...
960
961
962
963
964
965
966
  			if (!adjust_memory(s, ADD_MANAGED_RESOURCE, res->start, res->end))
  				done |= IORESOURCE_MEM;
  		}
  	}
  
  	/* if we got at least one of IO, and one of MEM, we can be glad and
  	 * activate the PCMCIA subsystem */
54bb5675a   Dominik Brodowski   [PATCH] pcmcia: o...
967
  	if (done == (IORESOURCE_MEM | IORESOURCE_IO))
3c29976a6   Dominik Brodowski   [PATCH] pcmcia: m...
968
969
970
971
972
973
974
975
976
977
978
979
980
  		s->resource_setup_done = 1;
  
  	return 0;
  }
  
  #else
  
  static inline int nonstatic_autoadd_resources(struct pcmcia_socket *s)
  {
  	return -ENODEV;
  }
  
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
981
982
983
  static int nonstatic_init(struct pcmcia_socket *s)
  {
  	struct socket_data *data;
8084b372a   Dominik Brodowski   [PATCH] pcmcia: k...
984
  	data = kzalloc(sizeof(struct socket_data), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
985
986
  	if (!data)
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
987
988
  
  	data->mem_db.next = &data->mem_db;
7b4884ca8   Dominik Brodowski   pcmcia: validate ...
989
  	data->mem_db_valid.next = &data->mem_db_valid;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
990
991
992
  	data->io_db.next = &data->io_db;
  
  	s->resource_data = (void *) data;
3c29976a6   Dominik Brodowski   [PATCH] pcmcia: m...
993
  	nonstatic_autoadd_resources(s);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
994
995
996
997
998
999
1000
  	return 0;
  }
  
  static void nonstatic_release_resource_db(struct pcmcia_socket *s)
  {
  	struct socket_data *data = s->resource_data;
  	struct resource_map *p, *q;
7b4884ca8   Dominik Brodowski   pcmcia: validate ...
1001
1002
1003
1004
  	for (p = data->mem_db_valid.next; p != &data->mem_db_valid; p = q) {
  		q = p->next;
  		kfree(p);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1005
1006
1007
1008
1009
1010
1011
1012
  	for (p = data->mem_db.next; p != &data->mem_db; p = q) {
  		q = p->next;
  		kfree(p);
  	}
  	for (p = data->io_db.next; p != &data->io_db; p = q) {
  		q = p->next;
  		kfree(p);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1013
1014
1015
1016
1017
  }
  
  
  struct pccard_resource_ops pccard_nonstatic_ops = {
  	.validate_mem = pcmcia_nonstatic_validate_mem,
b19a7275d   Dominik Brodowski   pcmcia: clarify a...
1018
  	.find_io = nonstatic_find_io,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1019
  	.find_mem = nonstatic_find_mem_region,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1020
1021
1022
1023
1024
1025
1026
  	.init = nonstatic_init,
  	.exit = nonstatic_release_resource_db,
  };
  EXPORT_SYMBOL(pccard_nonstatic_ops);
  
  
  /* sysfs interface to the resource database */
873733188   Greg Kroah-Hartman   Driver core: conv...
1027
1028
  static ssize_t show_io_db(struct device *dev,
  			  struct device_attribute *attr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1029
  {
873733188   Greg Kroah-Hartman   Driver core: conv...
1030
  	struct pcmcia_socket *s = dev_get_drvdata(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1031
1032
1033
  	struct socket_data *data;
  	struct resource_map *p;
  	ssize_t ret = 0;
cfe5d8095   Dominik Brodowski   pcmcia: use ops_m...
1034
  	mutex_lock(&s->ops_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1035
1036
1037
1038
1039
  	data = s->resource_data;
  
  	for (p = data->io_db.next; p != &data->io_db; p = p->next) {
  		if (ret > (PAGE_SIZE - 10))
  			continue;
6d3fbe919   Takashi Iwai   pcmcia: Use scnpr...
1040
  		ret += scnprintf(&buf[ret], (PAGE_SIZE - ret - 1),
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
1041
1042
1043
1044
  				"0x%08lx - 0x%08lx
  ",
  				((unsigned long) p->base),
  				((unsigned long) p->base + p->num - 1));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1045
  	}
cfe5d8095   Dominik Brodowski   pcmcia: use ops_m...
1046
  	mutex_unlock(&s->ops_mutex);
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
1047
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1048
  }
873733188   Greg Kroah-Hartman   Driver core: conv...
1049
1050
1051
  static ssize_t store_io_db(struct device *dev,
  			   struct device_attribute *attr,
  			   const char *buf, size_t count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1052
  {
873733188   Greg Kroah-Hartman   Driver core: conv...
1053
  	struct pcmcia_socket *s = dev_get_drvdata(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1054
  	unsigned long start_addr, end_addr;
22916638b   Dominik Brodowski   [PATCH] pcmcia: u...
1055
  	unsigned int add = ADD_MANAGED_RESOURCE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1056
  	ssize_t ret = 0;
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
1057
  	ret = sscanf(buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1058
  	if (ret != 2) {
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
1059
  		ret = sscanf(buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
22916638b   Dominik Brodowski   [PATCH] pcmcia: u...
1060
  		add = REMOVE_MANAGED_RESOURCE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1061
  		if (ret != 2) {
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
1062
1063
  			ret = sscanf(buf, "0x%lx - 0x%lx", &start_addr,
  				&end_addr);
22916638b   Dominik Brodowski   [PATCH] pcmcia: u...
1064
  			add = ADD_MANAGED_RESOURCE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1065
1066
1067
1068
  			if (ret != 2)
  				return -EINVAL;
  		}
  	}
1146bc743   Dominik Brodowski   [PATCH] pcmcia: a...
1069
  	if (end_addr < start_addr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1070
  		return -EINVAL;
cfe5d8095   Dominik Brodowski   pcmcia: use ops_m...
1071
  	mutex_lock(&s->ops_mutex);
22916638b   Dominik Brodowski   [PATCH] pcmcia: u...
1072
  	ret = adjust_io(s, add, start_addr, end_addr);
cfe5d8095   Dominik Brodowski   pcmcia: use ops_m...
1073
  	mutex_unlock(&s->ops_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1074
1075
1076
  
  	return ret ? ret : count;
  }
873733188   Greg Kroah-Hartman   Driver core: conv...
1077
  static DEVICE_ATTR(available_resources_io, 0600, show_io_db, store_io_db);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1078

873733188   Greg Kroah-Hartman   Driver core: conv...
1079
1080
  static ssize_t show_mem_db(struct device *dev,
  			   struct device_attribute *attr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1081
  {
873733188   Greg Kroah-Hartman   Driver core: conv...
1082
  	struct pcmcia_socket *s = dev_get_drvdata(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1083
1084
1085
  	struct socket_data *data;
  	struct resource_map *p;
  	ssize_t ret = 0;
cfe5d8095   Dominik Brodowski   pcmcia: use ops_m...
1086
  	mutex_lock(&s->ops_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1087
  	data = s->resource_data;
7b4884ca8   Dominik Brodowski   pcmcia: validate ...
1088
1089
1090
1091
  	for (p = data->mem_db_valid.next; p != &data->mem_db_valid;
  	     p = p->next) {
  		if (ret > (PAGE_SIZE - 10))
  			continue;
6d3fbe919   Takashi Iwai   pcmcia: Use scnpr...
1092
  		ret += scnprintf(&buf[ret], (PAGE_SIZE - ret - 1),
7b4884ca8   Dominik Brodowski   pcmcia: validate ...
1093
1094
1095
1096
1097
  				"0x%08lx - 0x%08lx
  ",
  				((unsigned long) p->base),
  				((unsigned long) p->base + p->num - 1));
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1098
1099
1100
  	for (p = data->mem_db.next; p != &data->mem_db; p = p->next) {
  		if (ret > (PAGE_SIZE - 10))
  			continue;
6d3fbe919   Takashi Iwai   pcmcia: Use scnpr...
1101
  		ret += scnprintf(&buf[ret], (PAGE_SIZE - ret - 1),
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
1102
1103
1104
1105
  				"0x%08lx - 0x%08lx
  ",
  				((unsigned long) p->base),
  				((unsigned long) p->base + p->num - 1));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1106
  	}
cfe5d8095   Dominik Brodowski   pcmcia: use ops_m...
1107
  	mutex_unlock(&s->ops_mutex);
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
1108
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1109
  }
873733188   Greg Kroah-Hartman   Driver core: conv...
1110
1111
1112
  static ssize_t store_mem_db(struct device *dev,
  			    struct device_attribute *attr,
  			    const char *buf, size_t count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1113
  {
873733188   Greg Kroah-Hartman   Driver core: conv...
1114
  	struct pcmcia_socket *s = dev_get_drvdata(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1115
  	unsigned long start_addr, end_addr;
22916638b   Dominik Brodowski   [PATCH] pcmcia: u...
1116
  	unsigned int add = ADD_MANAGED_RESOURCE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1117
  	ssize_t ret = 0;
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
1118
  	ret = sscanf(buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1119
  	if (ret != 2) {
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
1120
  		ret = sscanf(buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
22916638b   Dominik Brodowski   [PATCH] pcmcia: u...
1121
  		add = REMOVE_MANAGED_RESOURCE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1122
  		if (ret != 2) {
9fea84f46   Dominik Brodowski   pcmcia: CodingSty...
1123
1124
  			ret = sscanf(buf, "0x%lx - 0x%lx", &start_addr,
  				&end_addr);
22916638b   Dominik Brodowski   [PATCH] pcmcia: u...
1125
  			add = ADD_MANAGED_RESOURCE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1126
1127
1128
1129
  			if (ret != 2)
  				return -EINVAL;
  		}
  	}
1146bc743   Dominik Brodowski   [PATCH] pcmcia: a...
1130
  	if (end_addr < start_addr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1131
  		return -EINVAL;
cfe5d8095   Dominik Brodowski   pcmcia: use ops_m...
1132
  	mutex_lock(&s->ops_mutex);
22916638b   Dominik Brodowski   [PATCH] pcmcia: u...
1133
  	ret = adjust_memory(s, add, start_addr, end_addr);
cfe5d8095   Dominik Brodowski   pcmcia: use ops_m...
1134
  	mutex_unlock(&s->ops_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1135
1136
1137
  
  	return ret ? ret : count;
  }
873733188   Greg Kroah-Hartman   Driver core: conv...
1138
  static DEVICE_ATTR(available_resources_mem, 0600, show_mem_db, store_mem_db);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1139

7d5789616   David Brownell   pcmcia: simplify ...
1140
1141
1142
  static struct attribute *pccard_rsrc_attributes[] = {
  	&dev_attr_available_resources_io.attr,
  	&dev_attr_available_resources_mem.attr,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1143
1144
  	NULL,
  };
7d5789616   David Brownell   pcmcia: simplify ...
1145
1146
1147
  static const struct attribute_group rsrc_attributes = {
  	.attrs = pccard_rsrc_attributes,
  };
34cdf25a1   Bill Pemberton   pcmcia: remove us...
1148
  static int pccard_sysfs_add_rsrc(struct device *dev,
d8539d81a   Dmitry Torokhov   [PATCH] Driver co...
1149
  					   struct class_interface *class_intf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1150
  {
873733188   Greg Kroah-Hartman   Driver core: conv...
1151
  	struct pcmcia_socket *s = dev_get_drvdata(dev);
7d5789616   David Brownell   pcmcia: simplify ...
1152

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1153
1154
  	if (s->resource_ops != &pccard_nonstatic_ops)
  		return 0;
7d5789616   David Brownell   pcmcia: simplify ...
1155
  	return sysfs_create_group(&dev->kobj, &rsrc_attributes);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1156
  }
e765a02cb   Bill Pemberton   pcmcia: remove us...
1157
  static void pccard_sysfs_remove_rsrc(struct device *dev,
d8539d81a   Dmitry Torokhov   [PATCH] Driver co...
1158
  					       struct class_interface *class_intf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1159
  {
873733188   Greg Kroah-Hartman   Driver core: conv...
1160
  	struct pcmcia_socket *s = dev_get_drvdata(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1161
1162
1163
  
  	if (s->resource_ops != &pccard_nonstatic_ops)
  		return;
7d5789616   David Brownell   pcmcia: simplify ...
1164
  	sysfs_remove_group(&dev->kobj, &rsrc_attributes);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1165
  }
ed49f5d00   Sam Ravnborg   pcmcia: silence s...
1166
  static struct class_interface pccard_rsrc_interface __refdata = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1167
  	.class = &pcmcia_socket_class,
873733188   Greg Kroah-Hartman   Driver core: conv...
1168
  	.add_dev = &pccard_sysfs_add_rsrc,
96364e3a5   Bill Pemberton   pcmcia: remove us...
1169
  	.remove_dev = &pccard_sysfs_remove_rsrc,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
  };
  
  static int __init nonstatic_sysfs_init(void)
  {
  	return class_interface_register(&pccard_rsrc_interface);
  }
  
  static void __exit nonstatic_sysfs_exit(void)
  {
  	class_interface_unregister(&pccard_rsrc_interface);
  }
  
  module_init(nonstatic_sysfs_init);
  module_exit(nonstatic_sysfs_exit);