blob: bf9d070a44966d0a4206a702d94038e52c45b02a [file] [log] [blame]
Thomas Gleixnerd2912cb2019-06-04 10:11:33 +02001// SPDX-License-Identifier: GPL-2.0-only
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 * rsrc_nonstatic.c -- Resource management routines for !SS_CAP_STATIC_MAP sockets
4 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 * The initial developer of the original code is David A. Hinds
6 * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
7 * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
8 *
9 * (C) 1999 David A. Hinds
10 */
11
Linus Torvalds1da177e2005-04-16 15:20:36 -070012#include <linux/module.h>
13#include <linux/moduleparam.h>
14#include <linux/init.h>
15#include <linux/interrupt.h>
16#include <linux/kernel.h>
17#include <linux/errno.h>
18#include <linux/types.h>
19#include <linux/slab.h>
20#include <linux/ioport.h>
21#include <linux/timer.h>
22#include <linux/pci.h>
23#include <linux/device.h>
Dominik Brodowski9fea84f2009-12-07 22:11:45 +010024#include <linux/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070025
26#include <asm/irq.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070027
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include <pcmcia/ss.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070029#include <pcmcia/cistpl.h>
30#include "cs_internal.h"
31
Dominik Brodowski49b11532010-03-07 16:41:57 +010032/* moved to rsrc_mgr.c
Linus Torvalds1da177e2005-04-16 15:20:36 -070033MODULE_AUTHOR("David A. Hinds, Dominik Brodowski");
34MODULE_LICENSE("GPL");
Dominik Brodowski49b11532010-03-07 16:41:57 +010035*/
Linus Torvalds1da177e2005-04-16 15:20:36 -070036
37/* Parameters that can be set with 'insmod' */
38
39#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0444)
40
41INT_MODULE_PARM(probe_mem, 1); /* memory probe? */
42#ifdef CONFIG_PCMCIA_PROBE
43INT_MODULE_PARM(probe_io, 1); /* IO port probe? */
44INT_MODULE_PARM(mem_limit, 0x10000);
45#endif
46
47/* for io_db and mem_db */
48struct resource_map {
49 u_long base, num;
50 struct resource_map *next;
51};
52
53struct socket_data {
54 struct resource_map mem_db;
Dominik Brodowski7b4884ca2010-02-17 16:25:53 +010055 struct resource_map mem_db_valid;
Linus Torvalds1da177e2005-04-16 15:20:36 -070056 struct resource_map io_db;
Linus Torvalds1da177e2005-04-16 15:20:36 -070057};
58
Linus Torvalds1da177e2005-04-16 15:20:36 -070059#define MEM_PROBE_LOW (1 << 0)
60#define MEM_PROBE_HIGH (1 << 1)
61
Dominik Brodowski3dace8c2010-07-24 12:33:29 +020062/* Action field */
63#define REMOVE_MANAGED_RESOURCE 1
64#define ADD_MANAGED_RESOURCE 2
Linus Torvalds1da177e2005-04-16 15:20:36 -070065
66/*======================================================================
67
68 Linux resource management extensions
69
70======================================================================*/
71
72static struct resource *
Greg Kroah-Hartman2427ddd2006-06-12 17:07:52 -070073claim_region(struct pcmcia_socket *s, resource_size_t base,
74 resource_size_t size, int type, char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -070075{
76 struct resource *res, *parent;
77
78 parent = type & IORESOURCE_MEM ? &iomem_resource : &ioport_resource;
Dominik Brodowski49b11532010-03-07 16:41:57 +010079 res = pcmcia_make_resource(base, size, type | IORESOURCE_BUSY, name);
Linus Torvalds1da177e2005-04-16 15:20:36 -070080
81 if (res) {
82#ifdef CONFIG_PCI
83 if (s && s->cb_dev)
84 parent = pci_find_parent_resource(s->cb_dev, res);
85#endif
86 if (!parent || request_resource(parent, res)) {
87 kfree(res);
88 res = NULL;
89 }
90 }
91 return res;
92}
93
94static void free_region(struct resource *res)
95{
96 if (res) {
97 release_resource(res);
98 kfree(res);
99 }
100}
101
102/*======================================================================
103
104 These manage the internal databases of available resources.
105
106======================================================================*/
107
108static int add_interval(struct resource_map *map, u_long base, u_long num)
109{
Dominik Brodowski11683862008-08-03 10:22:47 +0200110 struct resource_map *p, *q;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111
Dominik Brodowski11683862008-08-03 10:22:47 +0200112 for (p = map; ; p = p->next) {
Dominik Brodowskif309cb32010-02-17 14:35:33 +0100113 if ((p != map) && (p->base+p->num >= base)) {
114 p->num = max(num + base - p->base, p->num);
115 return 0;
116 }
Dominik Brodowski11683862008-08-03 10:22:47 +0200117 if ((p->next == map) || (p->next->base > base+num-1))
118 break;
119 }
120 q = kmalloc(sizeof(struct resource_map), GFP_KERNEL);
121 if (!q) {
122 printk(KERN_WARNING "out of memory to update resources\n");
123 return -ENOMEM;
124 }
125 q->base = base; q->num = num;
126 q->next = p->next; p->next = q;
127 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128}
129
130/*====================================================================*/
131
132static int sub_interval(struct resource_map *map, u_long base, u_long num)
133{
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100134 struct resource_map *p, *q;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100136 for (p = map; ; p = q) {
137 q = p->next;
138 if (q == map)
139 break;
140 if ((q->base+q->num > base) && (base+num > q->base)) {
141 if (q->base >= base) {
142 if (q->base+q->num <= base+num) {
143 /* Delete whole block */
144 p->next = q->next;
145 kfree(q);
146 /* don't advance the pointer yet */
147 q = p;
148 } else {
149 /* Cut off bit from the front */
150 q->num = q->base + q->num - base - num;
151 q->base = base + num;
152 }
153 } else if (q->base+q->num <= base+num) {
154 /* Cut off bit from the end */
155 q->num = base - q->base;
156 } else {
157 /* Split the block into two pieces */
158 p = kmalloc(sizeof(struct resource_map),
159 GFP_KERNEL);
160 if (!p) {
161 printk(KERN_WARNING "out of memory to update resources\n");
162 return -ENOMEM;
163 }
164 p->base = base+num;
165 p->num = q->base+q->num - p->base;
166 q->num = base - q->base;
167 p->next = q->next ; q->next = p;
168 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100171 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172}
173
174/*======================================================================
175
176 These routines examine a region of IO or memory addresses to
177 determine what ranges might be genuinely available.
178
179======================================================================*/
180
181#ifdef CONFIG_PCMCIA_PROBE
Olof Johansson906da809c2008-02-04 22:27:35 -0800182static void do_io_probe(struct pcmcia_socket *s, unsigned int base,
183 unsigned int num)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184{
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100185 struct resource *res;
186 struct socket_data *s_data = s->resource_data;
187 unsigned int i, j, bad;
188 int any;
189 u_char *b, hole, most;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190
Joe Perchesf2e6cf72014-10-10 09:12:47 -0700191 dev_info(&s->dev, "cs: IO port probe %#x-%#x:", base, base+num-1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100193 /* First, what does a floating port look like? */
194 b = kzalloc(256, GFP_KERNEL);
195 if (!b) {
Joe Perchesf2e6cf72014-10-10 09:12:47 -0700196 pr_cont("\n");
197 dev_err(&s->dev, "do_io_probe: unable to kmalloc 256 bytes\n");
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100198 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100200 for (i = base, most = 0; i < base+num; i += 8) {
Dominik Brodowski509b0862010-04-08 19:23:07 +0200201 res = claim_region(s, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100202 if (!res)
203 continue;
204 hole = inb(i);
205 for (j = 1; j < 8; j++)
206 if (inb(i+j) != hole)
207 break;
208 free_region(res);
209 if ((j == 8) && (++b[hole] > b[most]))
210 most = hole;
211 if (b[most] == 127)
212 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100214 kfree(b);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100216 bad = any = 0;
217 for (i = base; i < base+num; i += 8) {
Dominik Brodowski509b0862010-04-08 19:23:07 +0200218 res = claim_region(s, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
219 if (!res) {
220 if (!any)
Joe Perchesf2e6cf72014-10-10 09:12:47 -0700221 pr_cont(" excluding");
Dominik Brodowski509b0862010-04-08 19:23:07 +0200222 if (!bad)
223 bad = any = i;
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100224 continue;
Dominik Brodowski509b0862010-04-08 19:23:07 +0200225 }
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100226 for (j = 0; j < 8; j++)
227 if (inb(i+j) != most)
228 break;
229 free_region(res);
230 if (j < 8) {
231 if (!any)
Joe Perchesf2e6cf72014-10-10 09:12:47 -0700232 pr_cont(" excluding");
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100233 if (!bad)
234 bad = any = i;
235 } else {
236 if (bad) {
237 sub_interval(&s_data->io_db, bad, i-bad);
Joe Perchesf2e6cf72014-10-10 09:12:47 -0700238 pr_cont(" %#x-%#x", bad, i-1);
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100239 bad = 0;
240 }
241 }
242 }
243 if (bad) {
244 if ((num > 16) && (bad == base) && (i == base+num)) {
Dominik Brodowski509b0862010-04-08 19:23:07 +0200245 sub_interval(&s_data->io_db, bad, i-bad);
Joe Perchesf2e6cf72014-10-10 09:12:47 -0700246 pr_cont(" nothing: probe failed.\n");
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100247 return;
248 } else {
249 sub_interval(&s_data->io_db, bad, i-bad);
Joe Perchesf2e6cf72014-10-10 09:12:47 -0700250 pr_cont(" %#x-%#x", bad, i-1);
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100251 }
252 }
253
Joe Perchesf2e6cf72014-10-10 09:12:47 -0700254 pr_cont("%s\n", !any ? " clean" : "");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255}
256#endif
257
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100258/*======================================================================*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259
Lee Jonesf20a7592021-03-12 11:02:34 +0000260/*
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100261 * readable() - iomem validation function for cards with a valid CIS
262 */
Dominik Brodowskic5081d52008-06-19 20:12:34 +0200263static int readable(struct pcmcia_socket *s, struct resource *res,
264 unsigned int *count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265{
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100266 int ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267
Dominik Brodowski7ab248552010-02-17 18:00:07 +0100268 if (s->fake_cis) {
269 dev_dbg(&s->dev, "fake CIS is being used: can't validate mem\n");
270 return 0;
271 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272
273 s->cis_mem.res = res;
274 s->cis_virt = ioremap(res->start, s->map_size);
275 if (s->cis_virt) {
Dominik Brodowski6b8e0872010-01-12 21:42:51 +0100276 mutex_unlock(&s->ops_mutex);
Dominik Brodowski6e7b51a2010-01-06 13:57:43 +0100277 /* as we're only called from pcmcia.c, we're safe */
278 if (s->callback->validate)
279 ret = s->callback->validate(s, count);
Dominik Brodowski904e3772010-01-02 12:28:04 +0100280 /* invalidate mapping */
Dominik Brodowski6b8e0872010-01-12 21:42:51 +0100281 mutex_lock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 iounmap(s->cis_virt);
283 s->cis_virt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 }
285 s->cis_mem.res = NULL;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100286 if ((ret) || (*count == 0))
287 return -EINVAL;
288 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289}
290
Lee Jonesf20a7592021-03-12 11:02:34 +0000291/*
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100292 * checksum() - iomem validation function for simple memory cards
293 */
294static int checksum(struct pcmcia_socket *s, struct resource *res,
295 unsigned int *value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296{
297 pccard_mem_map map;
298 int i, a = 0, b = -1, d;
299 void __iomem *virt;
300
301 virt = ioremap(res->start, s->map_size);
302 if (virt) {
303 map.map = 0;
304 map.flags = MAP_ACTIVE;
305 map.speed = 0;
306 map.res = res;
307 map.card_start = 0;
308 s->ops->set_mem_map(s, &map);
309
310 /* Don't bother checking every word... */
311 for (i = 0; i < s->map_size; i += 44) {
312 d = readl(virt+i);
313 a += d;
314 b &= d;
315 }
316
317 map.flags = 0;
318 s->ops->set_mem_map(s, &map);
319
320 iounmap(virt);
321 }
322
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100323 if (b == -1)
324 return -EINVAL;
325
326 *value = a;
327
328 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329}
330
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100331/**
332 * do_validate_mem() - low level validate a memory region for PCMCIA use
333 * @s: PCMCIA socket to validate
334 * @base: start address of resource to check
335 * @size: size of resource to check
336 * @validate: validation function to use
337 *
338 * do_validate_mem() splits up the memory region which is to be checked
339 * into two parts. Both are passed to the @validate() function. If
340 * @validate() returns non-zero, or the value parameter to @validate()
341 * is zero, or the value parameter is different between both calls,
342 * the check fails, and -EINVAL is returned. Else, 0 is returned.
343 */
344static int do_validate_mem(struct pcmcia_socket *s,
345 unsigned long base, unsigned long size,
Lee Jonese9d503f2021-03-12 11:02:39 +0000346 int (*validate)(struct pcmcia_socket *s,
347 struct resource *res,
348 unsigned int *value))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349{
Dominik Brodowski7b4884ca2010-02-17 16:25:53 +0100350 struct socket_data *s_data = s->resource_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 struct resource *res1, *res2;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100352 unsigned int info1 = 1, info2 = 1;
353 int ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100355 res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "PCMCIA memprobe");
356 res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM,
357 "PCMCIA memprobe");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358
359 if (res1 && res2) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100360 ret = 0;
361 if (validate) {
362 ret = validate(s, res1, &info1);
363 ret += validate(s, res2, &info2);
364 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 }
366
Dominik Brodowski3490a692018-01-22 21:36:27 +0100367 dev_dbg(&s->dev, "cs: memory probe 0x%06lx-0x%06lx: %pr %pr %u %u %u",
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100368 base, base+size-1, res1, res2, ret, info1, info2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369
Jesper Juhlbec0b2c2013-01-20 23:14:43 +0100370 free_region(res2);
371 free_region(res1);
372
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100373 if ((ret) || (info1 != info2) || (info1 == 0))
374 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375
Dominik Brodowski7b4884ca2010-02-17 16:25:53 +0100376 if (validate && !s->fake_cis) {
377 /* move it to the validated data set */
378 add_interval(&s_data->mem_db_valid, base, size);
379 sub_interval(&s_data->mem_db, base, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 }
381
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100382 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383}
384
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100386/**
387 * do_mem_probe() - validate a memory region for PCMCIA use
388 * @s: PCMCIA socket to validate
389 * @base: start address of resource to check
390 * @num: size of resource to check
391 * @validate: validation function to use
392 * @fallback: validation function to use if validate fails
393 *
394 * do_mem_probe() checks a memory region for use by the PCMCIA subsystem.
395 * To do so, the area is split up into sensible parts, and then passed
396 * into the @validate() function. Only if @validate() and @fallback() fail,
tangmeng6c00ef02022-01-27 15:15:09 +0800397 * the area is marked as unavailable for use by the PCMCIA subsystem. The
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100398 * function returns the size of the usable memory area.
399 */
400static int do_mem_probe(struct pcmcia_socket *s, u_long base, u_long num,
Lee Jonese9d503f2021-03-12 11:02:39 +0000401 int (*validate)(struct pcmcia_socket *s,
402 struct resource *res,
403 unsigned int *value),
404 int (*fallback)(struct pcmcia_socket *s,
405 struct resource *res,
406 unsigned int *value))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407{
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100408 struct socket_data *s_data = s->resource_data;
409 u_long i, j, bad, fail, step;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410
Joe Perchesf2e6cf72014-10-10 09:12:47 -0700411 dev_info(&s->dev, "cs: memory probe 0x%06lx-0x%06lx:",
412 base, base+num-1);
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100413 bad = fail = 0;
414 step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff);
415 /* don't allow too large steps */
416 if (step > 0x800000)
417 step = 0x800000;
418 /* cis_readable wants to map 2x map_size */
419 if (step < 2 * s->map_size)
420 step = 2 * s->map_size;
421 for (i = j = base; i < base+num; i = j + step) {
422 if (!fail) {
423 for (j = i; j < base+num; j += step) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100424 if (!do_validate_mem(s, j, step, validate))
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100425 break;
426 }
427 fail = ((i == base) && (j == base+num));
428 }
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100429 if ((fail) && (fallback)) {
430 for (j = i; j < base+num; j += step)
431 if (!do_validate_mem(s, j, step, fallback))
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100432 break;
433 }
434 if (i != j) {
435 if (!bad)
Joe Perchesf2e6cf72014-10-10 09:12:47 -0700436 pr_cont(" excluding");
437 pr_cont(" %#05lx-%#05lx", i, j-1);
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100438 sub_interval(&s_data->mem_db, i, j-i);
439 bad += j-i;
440 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 }
Joe Perchesf2e6cf72014-10-10 09:12:47 -0700442 pr_cont("%s\n", !bad ? " clean" : "");
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100443 return num - bad;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444}
445
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100446
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447#ifdef CONFIG_PCMCIA_PROBE
448
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100449/**
450 * inv_probe() - top-to-bottom search for one usuable high memory area
451 * @s: PCMCIA socket to validate
452 * @m: resource_map to check
453 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454static u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s)
455{
Dominik Brodowskide759142005-09-28 19:41:56 +0200456 struct socket_data *s_data = s->resource_data;
457 u_long ok;
458 if (m == &s_data->mem_db)
459 return 0;
460 ok = inv_probe(m->next, s);
461 if (ok) {
462 if (m->base >= 0x100000)
463 sub_interval(&s_data->mem_db, m->base, m->num);
464 return ok;
465 }
466 if (m->base < 0x100000)
467 return 0;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100468 return do_mem_probe(s, m->base, m->num, readable, checksum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469}
470
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100471/**
472 * validate_mem() - memory probe function
473 * @s: PCMCIA socket to validate
474 * @probe_mask: MEM_PROBE_LOW | MEM_PROBE_HIGH
475 *
476 * The memory probe. If the memory list includes a 64K-aligned block
477 * below 1MB, we probe in 64K chunks, and as soon as we accumulate at
478 * least mem_limit free space, we quit. Returns 0 on usuable ports.
479 */
Dominik Brodowskide759142005-09-28 19:41:56 +0200480static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481{
Dominik Brodowskide759142005-09-28 19:41:56 +0200482 struct resource_map *m, mm;
483 static unsigned char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 };
484 unsigned long b, i, ok = 0;
485 struct socket_data *s_data = s->resource_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486
Dominik Brodowskide759142005-09-28 19:41:56 +0200487 /* We do up to four passes through the list */
488 if (probe_mask & MEM_PROBE_HIGH) {
489 if (inv_probe(s_data->mem_db.next, s) > 0)
490 return 0;
Dominik Brodowski7b4884ca2010-02-17 16:25:53 +0100491 if (s_data->mem_db_valid.next != &s_data->mem_db_valid)
492 return 0;
Joe Perchesf2e6cf72014-10-10 09:12:47 -0700493 dev_notice(&s->dev,
Dominik Brodowskidbe4ea52008-08-02 21:36:19 +0200494 "cs: warning: no high memory space available!\n");
Dominik Brodowskide759142005-09-28 19:41:56 +0200495 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 }
Dominik Brodowskide759142005-09-28 19:41:56 +0200497
498 for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
499 mm = *m;
500 /* Only probe < 1 MB */
501 if (mm.base >= 0x100000)
502 continue;
503 if ((mm.base | mm.num) & 0xffff) {
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100504 ok += do_mem_probe(s, mm.base, mm.num, readable,
505 checksum);
Dominik Brodowskide759142005-09-28 19:41:56 +0200506 continue;
507 }
508 /* Special probe for 64K-aligned block */
509 for (i = 0; i < 4; i++) {
510 b = order[i] << 12;
511 if ((b >= mm.base) && (b+0x10000 <= mm.base+mm.num)) {
512 if (ok >= mem_limit)
513 sub_interval(&s_data->mem_db, b, 0x10000);
514 else
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100515 ok += do_mem_probe(s, b, 0x10000,
516 readable, checksum);
Dominik Brodowskide759142005-09-28 19:41:56 +0200517 }
518 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 }
Dominik Brodowskide759142005-09-28 19:41:56 +0200520
521 if (ok > 0)
522 return 0;
523
524 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525}
526
527#else /* CONFIG_PCMCIA_PROBE */
528
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100529/**
530 * validate_mem() - memory probe function
531 * @s: PCMCIA socket to validate
532 * @probe_mask: ignored
533 *
534 * Returns 0 on usuable ports.
535 */
Andrew Morton2cff9442005-11-16 21:29:26 -0800536static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537{
538 struct resource_map *m, mm;
539 struct socket_data *s_data = s->resource_data;
Andrew Morton2cff9442005-11-16 21:29:26 -0800540 unsigned long ok = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541
542 for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
543 mm = *m;
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100544 ok += do_mem_probe(s, mm.base, mm.num, readable, checksum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 }
Andrew Morton2cff9442005-11-16 21:29:26 -0800546 if (ok > 0)
547 return 0;
548 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549}
550
551#endif /* CONFIG_PCMCIA_PROBE */
552
553
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100554/**
555 * pcmcia_nonstatic_validate_mem() - try to validate iomem for PCMCIA use
556 * @s: PCMCIA socket to validate
557 *
558 * This is tricky... when we set up CIS memory, we try to validate
559 * the memory window space allocations.
560 *
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100561 * Locking note: Must be called with skt_mutex held!
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 */
Dominik Brodowskide759142005-09-28 19:41:56 +0200563static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564{
565 struct socket_data *s_data = s->resource_data;
Dominik Brodowskide759142005-09-28 19:41:56 +0200566 unsigned int probe_mask = MEM_PROBE_LOW;
Dominik Brodowski7b4884ca2010-02-17 16:25:53 +0100567 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568
Dominik Brodowski7b4884ca2010-02-17 16:25:53 +0100569 if (!probe_mem || !(s->state & SOCKET_PRESENT))
Dominik Brodowskide759142005-09-28 19:41:56 +0200570 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571
Dominik Brodowskide759142005-09-28 19:41:56 +0200572 if (s->features & SS_CAP_PAGE_REGS)
573 probe_mask = MEM_PROBE_HIGH;
574
Dominik Brodowski7b4884ca2010-02-17 16:25:53 +0100575 ret = validate_mem(s, probe_mask);
Dominik Brodowskide759142005-09-28 19:41:56 +0200576
Dominik Brodowski7b4884ca2010-02-17 16:25:53 +0100577 if (s_data->mem_db_valid.next != &s_data->mem_db_valid)
578 return 0;
Dominik Brodowskide759142005-09-28 19:41:56 +0200579
580 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581}
582
583struct pcmcia_align_data {
584 unsigned long mask;
585 unsigned long offset;
586 struct resource_map *map;
587};
588
Dominik Brodowski147a2742010-04-04 18:10:35 +0200589static resource_size_t pcmcia_common_align(struct pcmcia_align_data *align_data,
590 resource_size_t start)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591{
Dominik Brodowski147a2742010-04-04 18:10:35 +0200592 resource_size_t ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 /*
594 * Ensure that we have the correct start address
595 */
Dominik Brodowski147a2742010-04-04 18:10:35 +0200596 ret = (start & ~align_data->mask) + align_data->offset;
597 if (ret < start)
598 ret += align_data->mask + 1;
599 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600}
601
Dominik Brodowskib26b2d42010-01-01 17:40:49 +0100602static resource_size_t
Dominik Brodowski3b7a17f2010-01-01 17:40:50 +0100603pcmcia_align(void *align_data, const struct resource *res,
604 resource_size_t size, resource_size_t align)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605{
606 struct pcmcia_align_data *data = align_data;
607 struct resource_map *m;
Dominik Brodowskib26b2d42010-01-01 17:40:49 +0100608 resource_size_t start;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609
Dominik Brodowski147a2742010-04-04 18:10:35 +0200610 start = pcmcia_common_align(data, res->start);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611
612 for (m = data->map->next; m != data->map; m = m->next) {
Dominik Brodowski147a2742010-04-04 18:10:35 +0200613 unsigned long map_start = m->base;
614 unsigned long map_end = m->base + m->num - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615
616 /*
617 * If the lower resources are not available, try aligning
618 * to this entry of the resource database to see if it'll
619 * fit here.
620 */
Dominik Brodowski147a2742010-04-04 18:10:35 +0200621 if (start < map_start)
622 start = pcmcia_common_align(data, map_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623
624 /*
625 * If we're above the area which was passed in, there's
626 * no point proceeding.
627 */
Dominik Brodowski147a2742010-04-04 18:10:35 +0200628 if (start >= res->end)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 break;
630
Dominik Brodowski147a2742010-04-04 18:10:35 +0200631 if ((start + size - 1) <= map_end)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 break;
633 }
634
635 /*
636 * If we failed to find something suitable, ensure we fail.
637 */
638 if (m == data->map)
Dominik Brodowskib26b2d42010-01-01 17:40:49 +0100639 start = res->end;
640
641 return start;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642}
643
644/*
645 * Adjust an existing IO region allocation, but making sure that we don't
646 * encroach outside the resources which the user supplied.
647 */
Dominik Brodowskib19a7272010-03-20 13:10:47 +0100648static int __nonstatic_adjust_io_region(struct pcmcia_socket *s,
649 unsigned long r_start,
650 unsigned long r_end)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651{
652 struct resource_map *m;
653 struct socket_data *s_data = s->resource_data;
654 int ret = -ENOMEM;
655
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 for (m = s_data->io_db.next; m != &s_data->io_db; m = m->next) {
657 unsigned long start = m->base;
658 unsigned long end = m->base + m->num - 1;
659
660 if (start > r_start || r_end > end)
661 continue;
662
Dominik Brodowskib19a7272010-03-20 13:10:47 +0100663 ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665
666 return ret;
667}
668
669/*======================================================================
670
671 These find ranges of I/O ports or memory addresses that are not
672 currently allocated by other devices.
673
674 The 'align' field should reflect the number of bits of address
675 that need to be preserved from the initial value of *base. It
676 should be a power of two, greater than or equal to 'num'. A value
677 of 0 means that all bits of *base are significant. *base should
678 also be strictly less than 'align'.
679
680======================================================================*/
681
Dominik Brodowskib19a7272010-03-20 13:10:47 +0100682static struct resource *__nonstatic_find_io_region(struct pcmcia_socket *s,
683 unsigned long base, int num,
684 unsigned long align)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685{
Dominik Brodowski49b11532010-03-07 16:41:57 +0100686 struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_IO,
687 dev_name(&s->dev));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 struct socket_data *s_data = s->resource_data;
689 struct pcmcia_align_data data;
690 unsigned long min = base;
691 int ret;
692
Zhou Qingyangca0fe0d2021-12-01 00:59:23 +0800693 if (!res)
694 return NULL;
695
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 data.mask = align - 1;
697 data.offset = base & data.mask;
698 data.map = &s_data->io_db;
699
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700#ifdef CONFIG_PCI
701 if (s->cb_dev) {
702 ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1,
703 min, 0, pcmcia_align, &data);
704 } else
705#endif
706 ret = allocate_resource(&ioport_resource, res, num, min, ~0UL,
707 1, pcmcia_align, &data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708
709 if (ret != 0) {
710 kfree(res);
711 res = NULL;
712 }
713 return res;
714}
715
Dominik Brodowskib19a7272010-03-20 13:10:47 +0100716static int nonstatic_find_io(struct pcmcia_socket *s, unsigned int attr,
717 unsigned int *base, unsigned int num,
Dominik Brodowskiad0c7be2010-07-25 13:10:22 +0200718 unsigned int align, struct resource **parent)
Dominik Brodowskib19a7272010-03-20 13:10:47 +0100719{
720 int i, ret = 0;
721
722 /* Check for an already-allocated window that must conflict with
723 * what was asked for. It is a hack because it does not catch all
724 * potential conflicts, just the most obvious ones.
725 */
726 for (i = 0; i < MAX_IO_WIN; i++) {
727 if (!s->io[i].res)
728 continue;
729
730 if (!*base)
731 continue;
732
733 if ((s->io[i].res->start & (align-1)) == *base)
734 return -EBUSY;
735 }
736
737 for (i = 0; i < MAX_IO_WIN; i++) {
738 struct resource *res = s->io[i].res;
739 unsigned int try;
740
741 if (res && (res->flags & IORESOURCE_BITS) !=
742 (attr & IORESOURCE_BITS))
743 continue;
744
745 if (!res) {
746 if (align == 0)
747 align = 0x10000;
748
749 res = s->io[i].res = __nonstatic_find_io_region(s,
750 *base, num,
751 align);
752 if (!res)
753 return -EINVAL;
754
755 *base = res->start;
756 s->io[i].res->flags =
757 ((res->flags & ~IORESOURCE_BITS) |
758 (attr & IORESOURCE_BITS));
759 s->io[i].InUse = num;
Dominik Brodowskiad0c7be2010-07-25 13:10:22 +0200760 *parent = res;
Dominik Brodowskib19a7272010-03-20 13:10:47 +0100761 return 0;
762 }
763
764 /* Try to extend top of window */
765 try = res->end + 1;
766 if ((*base == 0) || (*base == try)) {
767 ret = __nonstatic_adjust_io_region(s, res->start,
768 res->end + num);
769 if (!ret) {
770 ret = adjust_resource(s->io[i].res, res->start,
Joe Perches28f65c112011-06-09 09:13:32 -0700771 resource_size(res) + num);
Dominik Brodowskib19a7272010-03-20 13:10:47 +0100772 if (ret)
773 continue;
774 *base = try;
775 s->io[i].InUse += num;
Dominik Brodowskiad0c7be2010-07-25 13:10:22 +0200776 *parent = res;
Dominik Brodowskib19a7272010-03-20 13:10:47 +0100777 return 0;
778 }
779 }
780
781 /* Try to extend bottom of window */
782 try = res->start - num;
783 if ((*base == 0) || (*base == try)) {
784 ret = __nonstatic_adjust_io_region(s,
785 res->start - num,
786 res->end);
787 if (!ret) {
788 ret = adjust_resource(s->io[i].res,
Joe Perches28f65c112011-06-09 09:13:32 -0700789 res->start - num,
790 resource_size(res) + num);
Dominik Brodowskib19a7272010-03-20 13:10:47 +0100791 if (ret)
792 continue;
793 *base = try;
794 s->io[i].InUse += num;
Dominik Brodowskiad0c7be2010-07-25 13:10:22 +0200795 *parent = res;
Dominik Brodowskib19a7272010-03-20 13:10:47 +0100796 return 0;
797 }
798 }
799 }
800
801 return -EINVAL;
802}
803
804
Dominik Brodowski9fea84f2009-12-07 22:11:45 +0100805static struct resource *nonstatic_find_mem_region(u_long base, u_long num,
Dominik Brodowskie94e15f2005-06-27 16:28:15 -0700806 u_long align, int low, struct pcmcia_socket *s)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807{
Dominik Brodowski49b11532010-03-07 16:41:57 +0100808 struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_MEM,
809 dev_name(&s->dev));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 struct socket_data *s_data = s->resource_data;
811 struct pcmcia_align_data data;
812 unsigned long min, max;
Dominik Brodowski7b4884ca2010-02-17 16:25:53 +0100813 int ret, i, j;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814
Zhou Qingyang977d2e72021-12-01 02:11:40 +0800815 if (!res)
816 return NULL;
817
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 low = low || !(s->features & SS_CAP_PAGE_REGS);
819
820 data.mask = align - 1;
821 data.offset = base & data.mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822
823 for (i = 0; i < 2; i++) {
Dominik Brodowski7b4884ca2010-02-17 16:25:53 +0100824 data.map = &s_data->mem_db_valid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 if (low) {
826 max = 0x100000UL;
827 min = base < max ? base : 0;
828 } else {
829 max = ~0UL;
830 min = 0x100000UL + base;
831 }
832
Dominik Brodowski7b4884ca2010-02-17 16:25:53 +0100833 for (j = 0; j < 2; j++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834#ifdef CONFIG_PCI
Dominik Brodowski7b4884ca2010-02-17 16:25:53 +0100835 if (s->cb_dev) {
836 ret = pci_bus_alloc_resource(s->cb_dev->bus,
837 res, num, 1, min, 0,
838 pcmcia_align, &data);
839 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840#endif
Dominik Brodowski7b4884ca2010-02-17 16:25:53 +0100841 {
842 ret = allocate_resource(&iomem_resource,
843 res, num, min, max, 1,
844 pcmcia_align, &data);
845 }
846 if (ret == 0)
847 break;
848 data.map = &s_data->mem_db;
849 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 if (ret == 0 || low)
851 break;
852 low = 1;
853 }
854
855 if (ret != 0) {
856 kfree(res);
857 res = NULL;
858 }
859 return res;
860}
861
862
Dominik Brodowski22916632005-06-27 16:28:46 -0700863static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 struct socket_data *data = s->resource_data;
Dominik Brodowski22916632005-06-27 16:28:46 -0700866 unsigned long size = end - start + 1;
867 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868
Dominik Brodowski1146bc72005-09-28 19:28:37 +0200869 if (end < start)
Dominik Brodowski22916632005-06-27 16:28:46 -0700870 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871
Dominik Brodowski22916632005-06-27 16:28:46 -0700872 switch (action) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 case ADD_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700874 ret = add_interval(&data->mem_db, start, size);
Dominik Brodowski3f32b3c2010-01-02 22:22:50 +0100875 if (!ret)
876 do_mem_probe(s, start, size, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877 break;
878 case REMOVE_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700879 ret = sub_interval(&data->mem_db, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 break;
881 default:
Dominik Brodowski22916632005-06-27 16:28:46 -0700882 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884
885 return ret;
886}
887
888
Dominik Brodowski22916632005-06-27 16:28:46 -0700889static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890{
891 struct socket_data *data = s->resource_data;
Dominik Brodowski41b97ab2010-04-15 19:01:53 +0200892 unsigned long size;
Dominik Brodowski22916632005-06-27 16:28:46 -0700893 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894
Dominik Brodowski9713ab22010-03-23 16:05:00 +0100895#if defined(CONFIG_X86)
896 /* on x86, avoid anything < 0x100 for it is often used for
897 * legacy platform devices */
898 if (start < 0x100)
899 start = 0x100;
900#endif
901
Dominik Brodowski41b97ab2010-04-15 19:01:53 +0200902 size = end - start + 1;
903
Dominik Brodowski1146bc72005-09-28 19:28:37 +0200904 if (end < start)
Dominik Brodowski22916632005-06-27 16:28:46 -0700905 return -EINVAL;
906
907 if (end > IO_SPACE_LIMIT)
908 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909
Dominik Brodowski22916632005-06-27 16:28:46 -0700910 switch (action) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911 case ADD_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700912 if (add_interval(&data->io_db, start, size) != 0) {
913 ret = -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 break;
915 }
916#ifdef CONFIG_PCMCIA_PROBE
917 if (probe_io)
Dominik Brodowski22916632005-06-27 16:28:46 -0700918 do_io_probe(s, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919#endif
920 break;
921 case REMOVE_MANAGED_RESOURCE:
Dominik Brodowski22916632005-06-27 16:28:46 -0700922 sub_interval(&data->io_db, start, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923 break;
924 default:
Dominik Brodowski22916632005-06-27 16:28:46 -0700925 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 break;
927 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928
929 return ret;
930}
931
932
Dominik Brodowski3c299762005-06-27 16:28:46 -0700933#ifdef CONFIG_PCI
934static int nonstatic_autoadd_resources(struct pcmcia_socket *s)
935{
936 struct resource *res;
937 int i, done = 0;
938
939 if (!s->cb_dev || !s->cb_dev->bus)
940 return -ENODEV;
941
Brian Gerst0d078f62005-10-30 14:59:20 -0800942#if defined(CONFIG_X86)
Dominik Brodowskicf26e8d2010-03-29 21:40:35 +0200943 /* If this is the root bus, the risk of hitting some strange
944 * system devices is too high: If a driver isn't loaded, the
945 * resources are not claimed; even if a driver is loaded, it
946 * may not request all resources or even the wrong one. We
947 * can neither trust the rest of the kernel nor ACPI/PNP and
948 * CRS parsing to get it right. Therefore, use several
949 * safeguards:
950 *
951 * - Do not auto-add resources if the CardBus bridge is on
952 * the PCI root bus
953 *
954 * - Avoid any I/O ports < 0x100.
955 *
956 * - On PCI-PCI bridges, only use resources which are set up
957 * exclusively for the secondary PCI bus: the risk of hitting
958 * system devices is quite low, as they usually aren't
959 * connected to the secondary PCI bus.
Dominik Brodowskib6d00f02005-06-27 16:29:02 -0700960 */
961 if (s->cb_dev->bus->number == 0)
962 return -EINVAL;
Dominik Brodowskib6d00f02005-06-27 16:29:02 -0700963
Dominik Brodowskicf26e8d2010-03-29 21:40:35 +0200964 for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) {
965 res = s->cb_dev->bus->resource[i];
966#else
Bjorn Helgaas89a74ec2010-02-23 10:24:31 -0700967 pci_bus_for_each_resource(s->cb_dev->bus, res, i) {
Dominik Brodowskicf26e8d2010-03-29 21:40:35 +0200968#endif
Dominik Brodowski3c299762005-06-27 16:28:46 -0700969 if (!res)
970 continue;
971
972 if (res->flags & IORESOURCE_IO) {
Dominik Brodowskicf26e8d2010-03-29 21:40:35 +0200973 /* safeguard against the root resource, where the
974 * risk of hitting any other device would be too
975 * high */
Dominik Brodowski3c299762005-06-27 16:28:46 -0700976 if (res == &ioport_resource)
977 continue;
Dominik Brodowskicf26e8d2010-03-29 21:40:35 +0200978
Joe Perchesf2e6cf72014-10-10 09:12:47 -0700979 dev_info(&s->cb_dev->dev,
980 "pcmcia: parent PCI bridge window: %pR\n",
981 res);
Dominik Brodowski3c299762005-06-27 16:28:46 -0700982 if (!adjust_io(s, ADD_MANAGED_RESOURCE, res->start, res->end))
983 done |= IORESOURCE_IO;
984
985 }
986
987 if (res->flags & IORESOURCE_MEM) {
Dominik Brodowskicf26e8d2010-03-29 21:40:35 +0200988 /* safeguard against the root resource, where the
989 * risk of hitting any other device would be too
990 * high */
Dominik Brodowski3c299762005-06-27 16:28:46 -0700991 if (res == &iomem_resource)
992 continue;
Dominik Brodowskicf26e8d2010-03-29 21:40:35 +0200993
Joe Perchesf2e6cf72014-10-10 09:12:47 -0700994 dev_info(&s->cb_dev->dev,
995 "pcmcia: parent PCI bridge window: %pR\n",
996 res);
Dominik Brodowski3c299762005-06-27 16:28:46 -0700997 if (!adjust_memory(s, ADD_MANAGED_RESOURCE, res->start, res->end))
998 done |= IORESOURCE_MEM;
999 }
1000 }
1001
1002 /* if we got at least one of IO, and one of MEM, we can be glad and
1003 * activate the PCMCIA subsystem */
Dominik Brodowski54bb5672005-09-28 19:29:59 +02001004 if (done == (IORESOURCE_MEM | IORESOURCE_IO))
Dominik Brodowski3c299762005-06-27 16:28:46 -07001005 s->resource_setup_done = 1;
1006
1007 return 0;
1008}
1009
1010#else
1011
1012static inline int nonstatic_autoadd_resources(struct pcmcia_socket *s)
1013{
1014 return -ENODEV;
1015}
1016
1017#endif
1018
1019
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020static int nonstatic_init(struct pcmcia_socket *s)
1021{
1022 struct socket_data *data;
1023
Dominik Brodowski8084b372005-12-11 21:18:26 +01001024 data = kzalloc(sizeof(struct socket_data), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025 if (!data)
1026 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027
1028 data->mem_db.next = &data->mem_db;
Dominik Brodowski7b4884ca2010-02-17 16:25:53 +01001029 data->mem_db_valid.next = &data->mem_db_valid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030 data->io_db.next = &data->io_db;
1031
1032 s->resource_data = (void *) data;
1033
Dominik Brodowski3c299762005-06-27 16:28:46 -07001034 nonstatic_autoadd_resources(s);
1035
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 return 0;
1037}
1038
1039static void nonstatic_release_resource_db(struct pcmcia_socket *s)
1040{
1041 struct socket_data *data = s->resource_data;
1042 struct resource_map *p, *q;
1043
Dominik Brodowski7b4884ca2010-02-17 16:25:53 +01001044 for (p = data->mem_db_valid.next; p != &data->mem_db_valid; p = q) {
1045 q = p->next;
1046 kfree(p);
1047 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048 for (p = data->mem_db.next; p != &data->mem_db; p = q) {
1049 q = p->next;
1050 kfree(p);
1051 }
1052 for (p = data->io_db.next; p != &data->io_db; p = q) {
1053 q = p->next;
1054 kfree(p);
1055 }
Armin Wolfc85fd942023-05-12 20:45:29 +02001056
1057 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058}
1059
1060
1061struct pccard_resource_ops pccard_nonstatic_ops = {
1062 .validate_mem = pcmcia_nonstatic_validate_mem,
Dominik Brodowskib19a7272010-03-20 13:10:47 +01001063 .find_io = nonstatic_find_io,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 .find_mem = nonstatic_find_mem_region,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 .init = nonstatic_init,
1066 .exit = nonstatic_release_resource_db,
1067};
1068EXPORT_SYMBOL(pccard_nonstatic_ops);
1069
1070
1071/* sysfs interface to the resource database */
1072
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001073static ssize_t show_io_db(struct device *dev,
1074 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001076 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 struct socket_data *data;
1078 struct resource_map *p;
1079 ssize_t ret = 0;
1080
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001081 mutex_lock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 data = s->resource_data;
1083
1084 for (p = data->io_db.next; p != &data->io_db; p = p->next) {
1085 if (ret > (PAGE_SIZE - 10))
1086 continue;
Dominik Brodowski78e01852021-10-15 09:13:23 +02001087 ret += sysfs_emit_at(buf, ret,
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001088 "0x%08lx - 0x%08lx\n",
1089 ((unsigned long) p->base),
1090 ((unsigned long) p->base + p->num - 1));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 }
1092
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001093 mutex_unlock(&s->ops_mutex);
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001094 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095}
1096
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001097static ssize_t store_io_db(struct device *dev,
1098 struct device_attribute *attr,
1099 const char *buf, size_t count)
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);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102 unsigned long start_addr, end_addr;
Dominik Brodowski22916632005-06-27 16:28:46 -07001103 unsigned int add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104 ssize_t ret = 0;
1105
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001106 ret = sscanf(buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001108 ret = sscanf(buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -07001109 add = REMOVE_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001111 ret = sscanf(buf, "0x%lx - 0x%lx", &start_addr,
1112 &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -07001113 add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114 if (ret != 2)
1115 return -EINVAL;
1116 }
1117 }
Dominik Brodowski1146bc72005-09-28 19:28:37 +02001118 if (end_addr < start_addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 return -EINVAL;
1120
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001121 mutex_lock(&s->ops_mutex);
Dominik Brodowski22916632005-06-27 16:28:46 -07001122 ret = adjust_io(s, add, start_addr, end_addr);
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001123 mutex_unlock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124
1125 return ret ? ret : count;
1126}
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001127static DEVICE_ATTR(available_resources_io, 0600, show_io_db, store_io_db);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001129static ssize_t show_mem_db(struct device *dev,
1130 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001132 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 struct socket_data *data;
1134 struct resource_map *p;
1135 ssize_t ret = 0;
1136
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001137 mutex_lock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 data = s->resource_data;
1139
Dominik Brodowski7b4884ca2010-02-17 16:25:53 +01001140 for (p = data->mem_db_valid.next; p != &data->mem_db_valid;
1141 p = p->next) {
1142 if (ret > (PAGE_SIZE - 10))
1143 continue;
Dominik Brodowski78e01852021-10-15 09:13:23 +02001144 ret += sysfs_emit_at(buf, ret,
Dominik Brodowski7b4884ca2010-02-17 16:25:53 +01001145 "0x%08lx - 0x%08lx\n",
1146 ((unsigned long) p->base),
1147 ((unsigned long) p->base + p->num - 1));
1148 }
1149
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 for (p = data->mem_db.next; p != &data->mem_db; p = p->next) {
1151 if (ret > (PAGE_SIZE - 10))
1152 continue;
Dominik Brodowski78e01852021-10-15 09:13:23 +02001153 ret += sysfs_emit_at(buf, ret,
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001154 "0x%08lx - 0x%08lx\n",
1155 ((unsigned long) p->base),
1156 ((unsigned long) p->base + p->num - 1));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157 }
1158
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001159 mutex_unlock(&s->ops_mutex);
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001160 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161}
1162
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001163static ssize_t store_mem_db(struct device *dev,
1164 struct device_attribute *attr,
1165 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001167 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 unsigned long start_addr, end_addr;
Dominik Brodowski22916632005-06-27 16:28:46 -07001169 unsigned int add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170 ssize_t ret = 0;
1171
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001172 ret = sscanf(buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001174 ret = sscanf(buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -07001175 add = REMOVE_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176 if (ret != 2) {
Dominik Brodowski9fea84f2009-12-07 22:11:45 +01001177 ret = sscanf(buf, "0x%lx - 0x%lx", &start_addr,
1178 &end_addr);
Dominik Brodowski22916632005-06-27 16:28:46 -07001179 add = ADD_MANAGED_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180 if (ret != 2)
1181 return -EINVAL;
1182 }
1183 }
Dominik Brodowski1146bc72005-09-28 19:28:37 +02001184 if (end_addr < start_addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185 return -EINVAL;
1186
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001187 mutex_lock(&s->ops_mutex);
Dominik Brodowski22916632005-06-27 16:28:46 -07001188 ret = adjust_memory(s, add, start_addr, end_addr);
Dominik Brodowskicfe5d802010-01-17 19:31:45 +01001189 mutex_unlock(&s->ops_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190
1191 return ret ? ret : count;
1192}
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001193static DEVICE_ATTR(available_resources_mem, 0600, show_mem_db, store_mem_db);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194
David Brownell7d578962008-06-12 12:13:55 -07001195static struct attribute *pccard_rsrc_attributes[] = {
1196 &dev_attr_available_resources_io.attr,
1197 &dev_attr_available_resources_mem.attr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 NULL,
1199};
1200
David Brownell7d578962008-06-12 12:13:55 -07001201static const struct attribute_group rsrc_attributes = {
1202 .attrs = pccard_rsrc_attributes,
1203};
1204
Greg Kroah-Hartman2243acd2023-04-02 19:58:49 +02001205static int pccard_sysfs_add_rsrc(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001207 struct pcmcia_socket *s = dev_get_drvdata(dev);
David Brownell7d578962008-06-12 12:13:55 -07001208
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209 if (s->resource_ops != &pccard_nonstatic_ops)
1210 return 0;
David Brownell7d578962008-06-12 12:13:55 -07001211 return sysfs_create_group(&dev->kobj, &rsrc_attributes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212}
1213
Greg Kroah-Hartman2243acd2023-04-02 19:58:49 +02001214static void pccard_sysfs_remove_rsrc(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215{
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001216 struct pcmcia_socket *s = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217
1218 if (s->resource_ops != &pccard_nonstatic_ops)
1219 return;
David Brownell7d578962008-06-12 12:13:55 -07001220 sysfs_remove_group(&dev->kobj, &rsrc_attributes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221}
1222
Sam Ravnborged49f5d2008-05-01 04:34:50 -07001223static struct class_interface pccard_rsrc_interface __refdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224 .class = &pcmcia_socket_class,
Greg Kroah-Hartman87373312006-09-12 17:00:10 +02001225 .add_dev = &pccard_sysfs_add_rsrc,
Bill Pemberton96364e32012-11-19 13:20:38 -05001226 .remove_dev = &pccard_sysfs_remove_rsrc,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227};
1228
1229static int __init nonstatic_sysfs_init(void)
1230{
1231 return class_interface_register(&pccard_rsrc_interface);
1232}
1233
1234static void __exit nonstatic_sysfs_exit(void)
1235{
1236 class_interface_unregister(&pccard_rsrc_interface);
1237}
1238
1239module_init(nonstatic_sysfs_init);
1240module_exit(nonstatic_sysfs_exit);