162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * rsrc_nonstatic.c -- Resource management routines for !SS_CAP_STATIC_MAP sockets
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * The initial developer of the original code is David A. Hinds
662306a36Sopenharmony_ci * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
762306a36Sopenharmony_ci * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * (C) 1999		David A. Hinds
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/moduleparam.h>
1462306a36Sopenharmony_ci#include <linux/init.h>
1562306a36Sopenharmony_ci#include <linux/interrupt.h>
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/errno.h>
1862306a36Sopenharmony_ci#include <linux/types.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci#include <linux/ioport.h>
2162306a36Sopenharmony_ci#include <linux/timer.h>
2262306a36Sopenharmony_ci#include <linux/pci.h>
2362306a36Sopenharmony_ci#include <linux/device.h>
2462306a36Sopenharmony_ci#include <linux/io.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <asm/irq.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include <pcmcia/ss.h>
2962306a36Sopenharmony_ci#include <pcmcia/cistpl.h>
3062306a36Sopenharmony_ci#include "cs_internal.h"
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/* moved to rsrc_mgr.c
3362306a36Sopenharmony_ciMODULE_AUTHOR("David A. Hinds, Dominik Brodowski");
3462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
3562306a36Sopenharmony_ci*/
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/* Parameters that can be set with 'insmod' */
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0444)
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ciINT_MODULE_PARM(probe_mem,	1);		/* memory probe? */
4262306a36Sopenharmony_ci#ifdef CONFIG_PCMCIA_PROBE
4362306a36Sopenharmony_ciINT_MODULE_PARM(probe_io,	1);		/* IO port probe? */
4462306a36Sopenharmony_ciINT_MODULE_PARM(mem_limit,	0x10000);
4562306a36Sopenharmony_ci#endif
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/* for io_db and mem_db */
4862306a36Sopenharmony_cistruct resource_map {
4962306a36Sopenharmony_ci	u_long			base, num;
5062306a36Sopenharmony_ci	struct resource_map	*next;
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistruct socket_data {
5462306a36Sopenharmony_ci	struct resource_map		mem_db;
5562306a36Sopenharmony_ci	struct resource_map		mem_db_valid;
5662306a36Sopenharmony_ci	struct resource_map		io_db;
5762306a36Sopenharmony_ci};
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#define MEM_PROBE_LOW	(1 << 0)
6062306a36Sopenharmony_ci#define MEM_PROBE_HIGH	(1 << 1)
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/* Action field */
6362306a36Sopenharmony_ci#define REMOVE_MANAGED_RESOURCE		1
6462306a36Sopenharmony_ci#define ADD_MANAGED_RESOURCE		2
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/*======================================================================
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci    Linux resource management extensions
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci======================================================================*/
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic struct resource *
7362306a36Sopenharmony_ciclaim_region(struct pcmcia_socket *s, resource_size_t base,
7462306a36Sopenharmony_ci		resource_size_t size, int type, char *name)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	struct resource *res, *parent;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	parent = type & IORESOURCE_MEM ? &iomem_resource : &ioport_resource;
7962306a36Sopenharmony_ci	res = pcmcia_make_resource(base, size, type | IORESOURCE_BUSY, name);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	if (res) {
8262306a36Sopenharmony_ci#ifdef CONFIG_PCI
8362306a36Sopenharmony_ci		if (s && s->cb_dev)
8462306a36Sopenharmony_ci			parent = pci_find_parent_resource(s->cb_dev, res);
8562306a36Sopenharmony_ci#endif
8662306a36Sopenharmony_ci		if (!parent || request_resource(parent, res)) {
8762306a36Sopenharmony_ci			kfree(res);
8862306a36Sopenharmony_ci			res = NULL;
8962306a36Sopenharmony_ci		}
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci	return res;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic void free_region(struct resource *res)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	if (res) {
9762306a36Sopenharmony_ci		release_resource(res);
9862306a36Sopenharmony_ci		kfree(res);
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci/*======================================================================
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci    These manage the internal databases of available resources.
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci======================================================================*/
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic int add_interval(struct resource_map *map, u_long base, u_long num)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct resource_map *p, *q;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	for (p = map; ; p = p->next) {
11362306a36Sopenharmony_ci		if ((p != map) && (p->base+p->num >= base)) {
11462306a36Sopenharmony_ci			p->num = max(num + base - p->base, p->num);
11562306a36Sopenharmony_ci			return 0;
11662306a36Sopenharmony_ci		}
11762306a36Sopenharmony_ci		if ((p->next == map) || (p->next->base > base+num-1))
11862306a36Sopenharmony_ci			break;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci	q = kmalloc(sizeof(struct resource_map), GFP_KERNEL);
12162306a36Sopenharmony_ci	if (!q) {
12262306a36Sopenharmony_ci		printk(KERN_WARNING "out of memory to update resources\n");
12362306a36Sopenharmony_ci		return -ENOMEM;
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci	q->base = base; q->num = num;
12662306a36Sopenharmony_ci	q->next = p->next; p->next = q;
12762306a36Sopenharmony_ci	return 0;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci/*====================================================================*/
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic int sub_interval(struct resource_map *map, u_long base, u_long num)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct resource_map *p, *q;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	for (p = map; ; p = q) {
13762306a36Sopenharmony_ci		q = p->next;
13862306a36Sopenharmony_ci		if (q == map)
13962306a36Sopenharmony_ci			break;
14062306a36Sopenharmony_ci		if ((q->base+q->num > base) && (base+num > q->base)) {
14162306a36Sopenharmony_ci			if (q->base >= base) {
14262306a36Sopenharmony_ci				if (q->base+q->num <= base+num) {
14362306a36Sopenharmony_ci					/* Delete whole block */
14462306a36Sopenharmony_ci					p->next = q->next;
14562306a36Sopenharmony_ci					kfree(q);
14662306a36Sopenharmony_ci					/* don't advance the pointer yet */
14762306a36Sopenharmony_ci					q = p;
14862306a36Sopenharmony_ci				} else {
14962306a36Sopenharmony_ci					/* Cut off bit from the front */
15062306a36Sopenharmony_ci					q->num = q->base + q->num - base - num;
15162306a36Sopenharmony_ci					q->base = base + num;
15262306a36Sopenharmony_ci				}
15362306a36Sopenharmony_ci			} else if (q->base+q->num <= base+num) {
15462306a36Sopenharmony_ci				/* Cut off bit from the end */
15562306a36Sopenharmony_ci				q->num = base - q->base;
15662306a36Sopenharmony_ci			} else {
15762306a36Sopenharmony_ci				/* Split the block into two pieces */
15862306a36Sopenharmony_ci				p = kmalloc(sizeof(struct resource_map),
15962306a36Sopenharmony_ci					GFP_KERNEL);
16062306a36Sopenharmony_ci				if (!p) {
16162306a36Sopenharmony_ci					printk(KERN_WARNING "out of memory to update resources\n");
16262306a36Sopenharmony_ci					return -ENOMEM;
16362306a36Sopenharmony_ci				}
16462306a36Sopenharmony_ci				p->base = base+num;
16562306a36Sopenharmony_ci				p->num = q->base+q->num - p->base;
16662306a36Sopenharmony_ci				q->num = base - q->base;
16762306a36Sopenharmony_ci				p->next = q->next ; q->next = p;
16862306a36Sopenharmony_ci			}
16962306a36Sopenharmony_ci		}
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci	return 0;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci/*======================================================================
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci    These routines examine a region of IO or memory addresses to
17762306a36Sopenharmony_ci    determine what ranges might be genuinely available.
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci======================================================================*/
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci#ifdef CONFIG_PCMCIA_PROBE
18262306a36Sopenharmony_cistatic void do_io_probe(struct pcmcia_socket *s, unsigned int base,
18362306a36Sopenharmony_ci			unsigned int num)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	struct resource *res;
18662306a36Sopenharmony_ci	struct socket_data *s_data = s->resource_data;
18762306a36Sopenharmony_ci	unsigned int i, j, bad;
18862306a36Sopenharmony_ci	int any;
18962306a36Sopenharmony_ci	u_char *b, hole, most;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	dev_info(&s->dev, "cs: IO port probe %#x-%#x:", base, base+num-1);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	/* First, what does a floating port look like? */
19462306a36Sopenharmony_ci	b = kzalloc(256, GFP_KERNEL);
19562306a36Sopenharmony_ci	if (!b) {
19662306a36Sopenharmony_ci		pr_cont("\n");
19762306a36Sopenharmony_ci		dev_err(&s->dev, "do_io_probe: unable to kmalloc 256 bytes\n");
19862306a36Sopenharmony_ci		return;
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci	for (i = base, most = 0; i < base+num; i += 8) {
20162306a36Sopenharmony_ci		res = claim_region(s, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
20262306a36Sopenharmony_ci		if (!res)
20362306a36Sopenharmony_ci			continue;
20462306a36Sopenharmony_ci		hole = inb(i);
20562306a36Sopenharmony_ci		for (j = 1; j < 8; j++)
20662306a36Sopenharmony_ci			if (inb(i+j) != hole)
20762306a36Sopenharmony_ci				break;
20862306a36Sopenharmony_ci		free_region(res);
20962306a36Sopenharmony_ci		if ((j == 8) && (++b[hole] > b[most]))
21062306a36Sopenharmony_ci			most = hole;
21162306a36Sopenharmony_ci		if (b[most] == 127)
21262306a36Sopenharmony_ci			break;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci	kfree(b);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	bad = any = 0;
21762306a36Sopenharmony_ci	for (i = base; i < base+num; i += 8) {
21862306a36Sopenharmony_ci		res = claim_region(s, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
21962306a36Sopenharmony_ci		if (!res) {
22062306a36Sopenharmony_ci			if (!any)
22162306a36Sopenharmony_ci				pr_cont(" excluding");
22262306a36Sopenharmony_ci			if (!bad)
22362306a36Sopenharmony_ci				bad = any = i;
22462306a36Sopenharmony_ci			continue;
22562306a36Sopenharmony_ci		}
22662306a36Sopenharmony_ci		for (j = 0; j < 8; j++)
22762306a36Sopenharmony_ci			if (inb(i+j) != most)
22862306a36Sopenharmony_ci				break;
22962306a36Sopenharmony_ci		free_region(res);
23062306a36Sopenharmony_ci		if (j < 8) {
23162306a36Sopenharmony_ci			if (!any)
23262306a36Sopenharmony_ci				pr_cont(" excluding");
23362306a36Sopenharmony_ci			if (!bad)
23462306a36Sopenharmony_ci				bad = any = i;
23562306a36Sopenharmony_ci		} else {
23662306a36Sopenharmony_ci			if (bad) {
23762306a36Sopenharmony_ci				sub_interval(&s_data->io_db, bad, i-bad);
23862306a36Sopenharmony_ci				pr_cont(" %#x-%#x", bad, i-1);
23962306a36Sopenharmony_ci				bad = 0;
24062306a36Sopenharmony_ci			}
24162306a36Sopenharmony_ci		}
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci	if (bad) {
24462306a36Sopenharmony_ci		if ((num > 16) && (bad == base) && (i == base+num)) {
24562306a36Sopenharmony_ci			sub_interval(&s_data->io_db, bad, i-bad);
24662306a36Sopenharmony_ci			pr_cont(" nothing: probe failed.\n");
24762306a36Sopenharmony_ci			return;
24862306a36Sopenharmony_ci		} else {
24962306a36Sopenharmony_ci			sub_interval(&s_data->io_db, bad, i-bad);
25062306a36Sopenharmony_ci			pr_cont(" %#x-%#x", bad, i-1);
25162306a36Sopenharmony_ci		}
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	pr_cont("%s\n", !any ? " clean" : "");
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci#endif
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci/*======================================================================*/
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci/*
26162306a36Sopenharmony_ci * readable() - iomem validation function for cards with a valid CIS
26262306a36Sopenharmony_ci */
26362306a36Sopenharmony_cistatic int readable(struct pcmcia_socket *s, struct resource *res,
26462306a36Sopenharmony_ci		    unsigned int *count)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	int ret = -EINVAL;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	if (s->fake_cis) {
26962306a36Sopenharmony_ci		dev_dbg(&s->dev, "fake CIS is being used: can't validate mem\n");
27062306a36Sopenharmony_ci		return 0;
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	s->cis_mem.res = res;
27462306a36Sopenharmony_ci	s->cis_virt = ioremap(res->start, s->map_size);
27562306a36Sopenharmony_ci	if (s->cis_virt) {
27662306a36Sopenharmony_ci		mutex_unlock(&s->ops_mutex);
27762306a36Sopenharmony_ci		/* as we're only called from pcmcia.c, we're safe */
27862306a36Sopenharmony_ci		if (s->callback->validate)
27962306a36Sopenharmony_ci			ret = s->callback->validate(s, count);
28062306a36Sopenharmony_ci		/* invalidate mapping */
28162306a36Sopenharmony_ci		mutex_lock(&s->ops_mutex);
28262306a36Sopenharmony_ci		iounmap(s->cis_virt);
28362306a36Sopenharmony_ci		s->cis_virt = NULL;
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci	s->cis_mem.res = NULL;
28662306a36Sopenharmony_ci	if ((ret) || (*count == 0))
28762306a36Sopenharmony_ci		return -EINVAL;
28862306a36Sopenharmony_ci	return 0;
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci/*
29262306a36Sopenharmony_ci * checksum() - iomem validation function for simple memory cards
29362306a36Sopenharmony_ci */
29462306a36Sopenharmony_cistatic int checksum(struct pcmcia_socket *s, struct resource *res,
29562306a36Sopenharmony_ci		    unsigned int *value)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	pccard_mem_map map;
29862306a36Sopenharmony_ci	int i, a = 0, b = -1, d;
29962306a36Sopenharmony_ci	void __iomem *virt;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	virt = ioremap(res->start, s->map_size);
30262306a36Sopenharmony_ci	if (virt) {
30362306a36Sopenharmony_ci		map.map = 0;
30462306a36Sopenharmony_ci		map.flags = MAP_ACTIVE;
30562306a36Sopenharmony_ci		map.speed = 0;
30662306a36Sopenharmony_ci		map.res = res;
30762306a36Sopenharmony_ci		map.card_start = 0;
30862306a36Sopenharmony_ci		s->ops->set_mem_map(s, &map);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci		/* Don't bother checking every word... */
31162306a36Sopenharmony_ci		for (i = 0; i < s->map_size; i += 44) {
31262306a36Sopenharmony_ci			d = readl(virt+i);
31362306a36Sopenharmony_ci			a += d;
31462306a36Sopenharmony_ci			b &= d;
31562306a36Sopenharmony_ci		}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci		map.flags = 0;
31862306a36Sopenharmony_ci		s->ops->set_mem_map(s, &map);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci		iounmap(virt);
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	if (b == -1)
32462306a36Sopenharmony_ci		return -EINVAL;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	*value = a;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	return 0;
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci/**
33262306a36Sopenharmony_ci * do_validate_mem() - low level validate a memory region for PCMCIA use
33362306a36Sopenharmony_ci * @s:		PCMCIA socket to validate
33462306a36Sopenharmony_ci * @base:	start address of resource to check
33562306a36Sopenharmony_ci * @size:	size of resource to check
33662306a36Sopenharmony_ci * @validate:	validation function to use
33762306a36Sopenharmony_ci *
33862306a36Sopenharmony_ci * do_validate_mem() splits up the memory region which is to be checked
33962306a36Sopenharmony_ci * into two parts. Both are passed to the @validate() function. If
34062306a36Sopenharmony_ci * @validate() returns non-zero, or the value parameter to @validate()
34162306a36Sopenharmony_ci * is zero, or the value parameter is different between both calls,
34262306a36Sopenharmony_ci * the check fails, and -EINVAL is returned. Else, 0 is returned.
34362306a36Sopenharmony_ci */
34462306a36Sopenharmony_cistatic int do_validate_mem(struct pcmcia_socket *s,
34562306a36Sopenharmony_ci			   unsigned long base, unsigned long size,
34662306a36Sopenharmony_ci			   int (*validate)(struct pcmcia_socket *s,
34762306a36Sopenharmony_ci					   struct resource *res,
34862306a36Sopenharmony_ci					   unsigned int *value))
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	struct socket_data *s_data = s->resource_data;
35162306a36Sopenharmony_ci	struct resource *res1, *res2;
35262306a36Sopenharmony_ci	unsigned int info1 = 1, info2 = 1;
35362306a36Sopenharmony_ci	int ret = -EINVAL;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "PCMCIA memprobe");
35662306a36Sopenharmony_ci	res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM,
35762306a36Sopenharmony_ci			"PCMCIA memprobe");
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (res1 && res2) {
36062306a36Sopenharmony_ci		ret = 0;
36162306a36Sopenharmony_ci		if (validate) {
36262306a36Sopenharmony_ci			ret = validate(s, res1, &info1);
36362306a36Sopenharmony_ci			ret += validate(s, res2, &info2);
36462306a36Sopenharmony_ci		}
36562306a36Sopenharmony_ci	}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	dev_dbg(&s->dev, "cs: memory probe 0x%06lx-0x%06lx: %pr %pr %u %u %u",
36862306a36Sopenharmony_ci		base, base+size-1, res1, res2, ret, info1, info2);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	free_region(res2);
37162306a36Sopenharmony_ci	free_region(res1);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	if ((ret) || (info1 != info2) || (info1 == 0))
37462306a36Sopenharmony_ci		return -EINVAL;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	if (validate && !s->fake_cis) {
37762306a36Sopenharmony_ci		/* move it to the validated data set */
37862306a36Sopenharmony_ci		add_interval(&s_data->mem_db_valid, base, size);
37962306a36Sopenharmony_ci		sub_interval(&s_data->mem_db, base, size);
38062306a36Sopenharmony_ci	}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	return 0;
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci/**
38762306a36Sopenharmony_ci * do_mem_probe() - validate a memory region for PCMCIA use
38862306a36Sopenharmony_ci * @s:		PCMCIA socket to validate
38962306a36Sopenharmony_ci * @base:	start address of resource to check
39062306a36Sopenharmony_ci * @num:	size of resource to check
39162306a36Sopenharmony_ci * @validate:	validation function to use
39262306a36Sopenharmony_ci * @fallback:	validation function to use if validate fails
39362306a36Sopenharmony_ci *
39462306a36Sopenharmony_ci * do_mem_probe() checks a memory region for use by the PCMCIA subsystem.
39562306a36Sopenharmony_ci * To do so, the area is split up into sensible parts, and then passed
39662306a36Sopenharmony_ci * into the @validate() function. Only if @validate() and @fallback() fail,
39762306a36Sopenharmony_ci * the area is marked as unavailable for use by the PCMCIA subsystem. The
39862306a36Sopenharmony_ci * function returns the size of the usable memory area.
39962306a36Sopenharmony_ci */
40062306a36Sopenharmony_cistatic int do_mem_probe(struct pcmcia_socket *s, u_long base, u_long num,
40162306a36Sopenharmony_ci			int (*validate)(struct pcmcia_socket *s,
40262306a36Sopenharmony_ci					struct resource *res,
40362306a36Sopenharmony_ci					unsigned int *value),
40462306a36Sopenharmony_ci			int (*fallback)(struct pcmcia_socket *s,
40562306a36Sopenharmony_ci					struct resource *res,
40662306a36Sopenharmony_ci					unsigned int *value))
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	struct socket_data *s_data = s->resource_data;
40962306a36Sopenharmony_ci	u_long i, j, bad, fail, step;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	dev_info(&s->dev, "cs: memory probe 0x%06lx-0x%06lx:",
41262306a36Sopenharmony_ci		 base, base+num-1);
41362306a36Sopenharmony_ci	bad = fail = 0;
41462306a36Sopenharmony_ci	step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff);
41562306a36Sopenharmony_ci	/* don't allow too large steps */
41662306a36Sopenharmony_ci	if (step > 0x800000)
41762306a36Sopenharmony_ci		step = 0x800000;
41862306a36Sopenharmony_ci	/* cis_readable wants to map 2x map_size */
41962306a36Sopenharmony_ci	if (step < 2 * s->map_size)
42062306a36Sopenharmony_ci		step = 2 * s->map_size;
42162306a36Sopenharmony_ci	for (i = j = base; i < base+num; i = j + step) {
42262306a36Sopenharmony_ci		if (!fail) {
42362306a36Sopenharmony_ci			for (j = i; j < base+num; j += step) {
42462306a36Sopenharmony_ci				if (!do_validate_mem(s, j, step, validate))
42562306a36Sopenharmony_ci					break;
42662306a36Sopenharmony_ci			}
42762306a36Sopenharmony_ci			fail = ((i == base) && (j == base+num));
42862306a36Sopenharmony_ci		}
42962306a36Sopenharmony_ci		if ((fail) && (fallback)) {
43062306a36Sopenharmony_ci			for (j = i; j < base+num; j += step)
43162306a36Sopenharmony_ci				if (!do_validate_mem(s, j, step, fallback))
43262306a36Sopenharmony_ci					break;
43362306a36Sopenharmony_ci		}
43462306a36Sopenharmony_ci		if (i != j) {
43562306a36Sopenharmony_ci			if (!bad)
43662306a36Sopenharmony_ci				pr_cont(" excluding");
43762306a36Sopenharmony_ci			pr_cont(" %#05lx-%#05lx", i, j-1);
43862306a36Sopenharmony_ci			sub_interval(&s_data->mem_db, i, j-i);
43962306a36Sopenharmony_ci			bad += j-i;
44062306a36Sopenharmony_ci		}
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci	pr_cont("%s\n", !bad ? " clean" : "");
44362306a36Sopenharmony_ci	return num - bad;
44462306a36Sopenharmony_ci}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci#ifdef CONFIG_PCMCIA_PROBE
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci/**
45062306a36Sopenharmony_ci * inv_probe() - top-to-bottom search for one usuable high memory area
45162306a36Sopenharmony_ci * @s:		PCMCIA socket to validate
45262306a36Sopenharmony_ci * @m:		resource_map to check
45362306a36Sopenharmony_ci */
45462306a36Sopenharmony_cistatic u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	struct socket_data *s_data = s->resource_data;
45762306a36Sopenharmony_ci	u_long ok;
45862306a36Sopenharmony_ci	if (m == &s_data->mem_db)
45962306a36Sopenharmony_ci		return 0;
46062306a36Sopenharmony_ci	ok = inv_probe(m->next, s);
46162306a36Sopenharmony_ci	if (ok) {
46262306a36Sopenharmony_ci		if (m->base >= 0x100000)
46362306a36Sopenharmony_ci			sub_interval(&s_data->mem_db, m->base, m->num);
46462306a36Sopenharmony_ci		return ok;
46562306a36Sopenharmony_ci	}
46662306a36Sopenharmony_ci	if (m->base < 0x100000)
46762306a36Sopenharmony_ci		return 0;
46862306a36Sopenharmony_ci	return do_mem_probe(s, m->base, m->num, readable, checksum);
46962306a36Sopenharmony_ci}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci/**
47262306a36Sopenharmony_ci * validate_mem() - memory probe function
47362306a36Sopenharmony_ci * @s:		PCMCIA socket to validate
47462306a36Sopenharmony_ci * @probe_mask: MEM_PROBE_LOW | MEM_PROBE_HIGH
47562306a36Sopenharmony_ci *
47662306a36Sopenharmony_ci * The memory probe.  If the memory list includes a 64K-aligned block
47762306a36Sopenharmony_ci * below 1MB, we probe in 64K chunks, and as soon as we accumulate at
47862306a36Sopenharmony_ci * least mem_limit free space, we quit. Returns 0 on usuable ports.
47962306a36Sopenharmony_ci */
48062306a36Sopenharmony_cistatic int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
48162306a36Sopenharmony_ci{
48262306a36Sopenharmony_ci	struct resource_map *m, mm;
48362306a36Sopenharmony_ci	static unsigned char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 };
48462306a36Sopenharmony_ci	unsigned long b, i, ok = 0;
48562306a36Sopenharmony_ci	struct socket_data *s_data = s->resource_data;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	/* We do up to four passes through the list */
48862306a36Sopenharmony_ci	if (probe_mask & MEM_PROBE_HIGH) {
48962306a36Sopenharmony_ci		if (inv_probe(s_data->mem_db.next, s) > 0)
49062306a36Sopenharmony_ci			return 0;
49162306a36Sopenharmony_ci		if (s_data->mem_db_valid.next != &s_data->mem_db_valid)
49262306a36Sopenharmony_ci			return 0;
49362306a36Sopenharmony_ci		dev_notice(&s->dev,
49462306a36Sopenharmony_ci			   "cs: warning: no high memory space available!\n");
49562306a36Sopenharmony_ci		return -ENODEV;
49662306a36Sopenharmony_ci	}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
49962306a36Sopenharmony_ci		mm = *m;
50062306a36Sopenharmony_ci		/* Only probe < 1 MB */
50162306a36Sopenharmony_ci		if (mm.base >= 0x100000)
50262306a36Sopenharmony_ci			continue;
50362306a36Sopenharmony_ci		if ((mm.base | mm.num) & 0xffff) {
50462306a36Sopenharmony_ci			ok += do_mem_probe(s, mm.base, mm.num, readable,
50562306a36Sopenharmony_ci					   checksum);
50662306a36Sopenharmony_ci			continue;
50762306a36Sopenharmony_ci		}
50862306a36Sopenharmony_ci		/* Special probe for 64K-aligned block */
50962306a36Sopenharmony_ci		for (i = 0; i < 4; i++) {
51062306a36Sopenharmony_ci			b = order[i] << 12;
51162306a36Sopenharmony_ci			if ((b >= mm.base) && (b+0x10000 <= mm.base+mm.num)) {
51262306a36Sopenharmony_ci				if (ok >= mem_limit)
51362306a36Sopenharmony_ci					sub_interval(&s_data->mem_db, b, 0x10000);
51462306a36Sopenharmony_ci				else
51562306a36Sopenharmony_ci					ok += do_mem_probe(s, b, 0x10000,
51662306a36Sopenharmony_ci							   readable, checksum);
51762306a36Sopenharmony_ci			}
51862306a36Sopenharmony_ci		}
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	if (ok > 0)
52262306a36Sopenharmony_ci		return 0;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	return -ENODEV;
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci#else /* CONFIG_PCMCIA_PROBE */
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci/**
53062306a36Sopenharmony_ci * validate_mem() - memory probe function
53162306a36Sopenharmony_ci * @s:		PCMCIA socket to validate
53262306a36Sopenharmony_ci * @probe_mask: ignored
53362306a36Sopenharmony_ci *
53462306a36Sopenharmony_ci * Returns 0 on usuable ports.
53562306a36Sopenharmony_ci */
53662306a36Sopenharmony_cistatic int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	struct resource_map *m, mm;
53962306a36Sopenharmony_ci	struct socket_data *s_data = s->resource_data;
54062306a36Sopenharmony_ci	unsigned long ok = 0;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
54362306a36Sopenharmony_ci		mm = *m;
54462306a36Sopenharmony_ci		ok += do_mem_probe(s, mm.base, mm.num, readable, checksum);
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci	if (ok > 0)
54762306a36Sopenharmony_ci		return 0;
54862306a36Sopenharmony_ci	return -ENODEV;
54962306a36Sopenharmony_ci}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci#endif /* CONFIG_PCMCIA_PROBE */
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci/**
55562306a36Sopenharmony_ci * pcmcia_nonstatic_validate_mem() - try to validate iomem for PCMCIA use
55662306a36Sopenharmony_ci * @s:		PCMCIA socket to validate
55762306a36Sopenharmony_ci *
55862306a36Sopenharmony_ci * This is tricky... when we set up CIS memory, we try to validate
55962306a36Sopenharmony_ci * the memory window space allocations.
56062306a36Sopenharmony_ci *
56162306a36Sopenharmony_ci * Locking note: Must be called with skt_mutex held!
56262306a36Sopenharmony_ci */
56362306a36Sopenharmony_cistatic int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	struct socket_data *s_data = s->resource_data;
56662306a36Sopenharmony_ci	unsigned int probe_mask = MEM_PROBE_LOW;
56762306a36Sopenharmony_ci	int ret;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	if (!probe_mem || !(s->state & SOCKET_PRESENT))
57062306a36Sopenharmony_ci		return 0;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	if (s->features & SS_CAP_PAGE_REGS)
57362306a36Sopenharmony_ci		probe_mask = MEM_PROBE_HIGH;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	ret = validate_mem(s, probe_mask);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	if (s_data->mem_db_valid.next != &s_data->mem_db_valid)
57862306a36Sopenharmony_ci		return 0;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	return ret;
58162306a36Sopenharmony_ci}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_cistruct pcmcia_align_data {
58462306a36Sopenharmony_ci	unsigned long	mask;
58562306a36Sopenharmony_ci	unsigned long	offset;
58662306a36Sopenharmony_ci	struct resource_map	*map;
58762306a36Sopenharmony_ci};
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_cistatic resource_size_t pcmcia_common_align(struct pcmcia_align_data *align_data,
59062306a36Sopenharmony_ci					resource_size_t start)
59162306a36Sopenharmony_ci{
59262306a36Sopenharmony_ci	resource_size_t ret;
59362306a36Sopenharmony_ci	/*
59462306a36Sopenharmony_ci	 * Ensure that we have the correct start address
59562306a36Sopenharmony_ci	 */
59662306a36Sopenharmony_ci	ret = (start & ~align_data->mask) + align_data->offset;
59762306a36Sopenharmony_ci	if (ret < start)
59862306a36Sopenharmony_ci		ret += align_data->mask + 1;
59962306a36Sopenharmony_ci	return ret;
60062306a36Sopenharmony_ci}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_cistatic resource_size_t
60362306a36Sopenharmony_cipcmcia_align(void *align_data, const struct resource *res,
60462306a36Sopenharmony_ci	resource_size_t size, resource_size_t align)
60562306a36Sopenharmony_ci{
60662306a36Sopenharmony_ci	struct pcmcia_align_data *data = align_data;
60762306a36Sopenharmony_ci	struct resource_map *m;
60862306a36Sopenharmony_ci	resource_size_t start;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	start = pcmcia_common_align(data, res->start);
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	for (m = data->map->next; m != data->map; m = m->next) {
61362306a36Sopenharmony_ci		unsigned long map_start = m->base;
61462306a36Sopenharmony_ci		unsigned long map_end = m->base + m->num - 1;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci		/*
61762306a36Sopenharmony_ci		 * If the lower resources are not available, try aligning
61862306a36Sopenharmony_ci		 * to this entry of the resource database to see if it'll
61962306a36Sopenharmony_ci		 * fit here.
62062306a36Sopenharmony_ci		 */
62162306a36Sopenharmony_ci		if (start < map_start)
62262306a36Sopenharmony_ci			start = pcmcia_common_align(data, map_start);
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci		/*
62562306a36Sopenharmony_ci		 * If we're above the area which was passed in, there's
62662306a36Sopenharmony_ci		 * no point proceeding.
62762306a36Sopenharmony_ci		 */
62862306a36Sopenharmony_ci		if (start >= res->end)
62962306a36Sopenharmony_ci			break;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci		if ((start + size - 1) <= map_end)
63262306a36Sopenharmony_ci			break;
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	/*
63662306a36Sopenharmony_ci	 * If we failed to find something suitable, ensure we fail.
63762306a36Sopenharmony_ci	 */
63862306a36Sopenharmony_ci	if (m == data->map)
63962306a36Sopenharmony_ci		start = res->end;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	return start;
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci/*
64562306a36Sopenharmony_ci * Adjust an existing IO region allocation, but making sure that we don't
64662306a36Sopenharmony_ci * encroach outside the resources which the user supplied.
64762306a36Sopenharmony_ci */
64862306a36Sopenharmony_cistatic int __nonstatic_adjust_io_region(struct pcmcia_socket *s,
64962306a36Sopenharmony_ci					unsigned long r_start,
65062306a36Sopenharmony_ci					unsigned long r_end)
65162306a36Sopenharmony_ci{
65262306a36Sopenharmony_ci	struct resource_map *m;
65362306a36Sopenharmony_ci	struct socket_data *s_data = s->resource_data;
65462306a36Sopenharmony_ci	int ret = -ENOMEM;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	for (m = s_data->io_db.next; m != &s_data->io_db; m = m->next) {
65762306a36Sopenharmony_ci		unsigned long start = m->base;
65862306a36Sopenharmony_ci		unsigned long end = m->base + m->num - 1;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci		if (start > r_start || r_end > end)
66162306a36Sopenharmony_ci			continue;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci		ret = 0;
66462306a36Sopenharmony_ci	}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	return ret;
66762306a36Sopenharmony_ci}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci/*======================================================================
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci    These find ranges of I/O ports or memory addresses that are not
67262306a36Sopenharmony_ci    currently allocated by other devices.
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci    The 'align' field should reflect the number of bits of address
67562306a36Sopenharmony_ci    that need to be preserved from the initial value of *base.  It
67662306a36Sopenharmony_ci    should be a power of two, greater than or equal to 'num'.  A value
67762306a36Sopenharmony_ci    of 0 means that all bits of *base are significant.  *base should
67862306a36Sopenharmony_ci    also be strictly less than 'align'.
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci======================================================================*/
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cistatic struct resource *__nonstatic_find_io_region(struct pcmcia_socket *s,
68362306a36Sopenharmony_ci						unsigned long base, int num,
68462306a36Sopenharmony_ci						unsigned long align)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_IO,
68762306a36Sopenharmony_ci						dev_name(&s->dev));
68862306a36Sopenharmony_ci	struct socket_data *s_data = s->resource_data;
68962306a36Sopenharmony_ci	struct pcmcia_align_data data;
69062306a36Sopenharmony_ci	unsigned long min = base;
69162306a36Sopenharmony_ci	int ret;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	if (!res)
69462306a36Sopenharmony_ci		return NULL;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	data.mask = align - 1;
69762306a36Sopenharmony_ci	data.offset = base & data.mask;
69862306a36Sopenharmony_ci	data.map = &s_data->io_db;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci#ifdef CONFIG_PCI
70162306a36Sopenharmony_ci	if (s->cb_dev) {
70262306a36Sopenharmony_ci		ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1,
70362306a36Sopenharmony_ci					     min, 0, pcmcia_align, &data);
70462306a36Sopenharmony_ci	} else
70562306a36Sopenharmony_ci#endif
70662306a36Sopenharmony_ci		ret = allocate_resource(&ioport_resource, res, num, min, ~0UL,
70762306a36Sopenharmony_ci					1, pcmcia_align, &data);
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	if (ret != 0) {
71062306a36Sopenharmony_ci		kfree(res);
71162306a36Sopenharmony_ci		res = NULL;
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci	return res;
71462306a36Sopenharmony_ci}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_cistatic int nonstatic_find_io(struct pcmcia_socket *s, unsigned int attr,
71762306a36Sopenharmony_ci			unsigned int *base, unsigned int num,
71862306a36Sopenharmony_ci			unsigned int align, struct resource **parent)
71962306a36Sopenharmony_ci{
72062306a36Sopenharmony_ci	int i, ret = 0;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	/* Check for an already-allocated window that must conflict with
72362306a36Sopenharmony_ci	 * what was asked for.  It is a hack because it does not catch all
72462306a36Sopenharmony_ci	 * potential conflicts, just the most obvious ones.
72562306a36Sopenharmony_ci	 */
72662306a36Sopenharmony_ci	for (i = 0; i < MAX_IO_WIN; i++) {
72762306a36Sopenharmony_ci		if (!s->io[i].res)
72862306a36Sopenharmony_ci			continue;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci		if (!*base)
73162306a36Sopenharmony_ci			continue;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci		if ((s->io[i].res->start & (align-1)) == *base)
73462306a36Sopenharmony_ci			return -EBUSY;
73562306a36Sopenharmony_ci	}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	for (i = 0; i < MAX_IO_WIN; i++) {
73862306a36Sopenharmony_ci		struct resource *res = s->io[i].res;
73962306a36Sopenharmony_ci		unsigned int try;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci		if (res && (res->flags & IORESOURCE_BITS) !=
74262306a36Sopenharmony_ci			(attr & IORESOURCE_BITS))
74362306a36Sopenharmony_ci			continue;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci		if (!res) {
74662306a36Sopenharmony_ci			if (align == 0)
74762306a36Sopenharmony_ci				align = 0x10000;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci			res = s->io[i].res = __nonstatic_find_io_region(s,
75062306a36Sopenharmony_ci								*base, num,
75162306a36Sopenharmony_ci								align);
75262306a36Sopenharmony_ci			if (!res)
75362306a36Sopenharmony_ci				return -EINVAL;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci			*base = res->start;
75662306a36Sopenharmony_ci			s->io[i].res->flags =
75762306a36Sopenharmony_ci				((res->flags & ~IORESOURCE_BITS) |
75862306a36Sopenharmony_ci					(attr & IORESOURCE_BITS));
75962306a36Sopenharmony_ci			s->io[i].InUse = num;
76062306a36Sopenharmony_ci			*parent = res;
76162306a36Sopenharmony_ci			return 0;
76262306a36Sopenharmony_ci		}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci		/* Try to extend top of window */
76562306a36Sopenharmony_ci		try = res->end + 1;
76662306a36Sopenharmony_ci		if ((*base == 0) || (*base == try)) {
76762306a36Sopenharmony_ci			ret =  __nonstatic_adjust_io_region(s, res->start,
76862306a36Sopenharmony_ci							res->end + num);
76962306a36Sopenharmony_ci			if (!ret) {
77062306a36Sopenharmony_ci				ret = adjust_resource(s->io[i].res, res->start,
77162306a36Sopenharmony_ci						      resource_size(res) + num);
77262306a36Sopenharmony_ci				if (ret)
77362306a36Sopenharmony_ci					continue;
77462306a36Sopenharmony_ci				*base = try;
77562306a36Sopenharmony_ci				s->io[i].InUse += num;
77662306a36Sopenharmony_ci				*parent = res;
77762306a36Sopenharmony_ci				return 0;
77862306a36Sopenharmony_ci			}
77962306a36Sopenharmony_ci		}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci		/* Try to extend bottom of window */
78262306a36Sopenharmony_ci		try = res->start - num;
78362306a36Sopenharmony_ci		if ((*base == 0) || (*base == try)) {
78462306a36Sopenharmony_ci			ret =  __nonstatic_adjust_io_region(s,
78562306a36Sopenharmony_ci							res->start - num,
78662306a36Sopenharmony_ci							res->end);
78762306a36Sopenharmony_ci			if (!ret) {
78862306a36Sopenharmony_ci				ret = adjust_resource(s->io[i].res,
78962306a36Sopenharmony_ci						      res->start - num,
79062306a36Sopenharmony_ci						      resource_size(res) + num);
79162306a36Sopenharmony_ci				if (ret)
79262306a36Sopenharmony_ci					continue;
79362306a36Sopenharmony_ci				*base = try;
79462306a36Sopenharmony_ci				s->io[i].InUse += num;
79562306a36Sopenharmony_ci				*parent = res;
79662306a36Sopenharmony_ci				return 0;
79762306a36Sopenharmony_ci			}
79862306a36Sopenharmony_ci		}
79962306a36Sopenharmony_ci	}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	return -EINVAL;
80262306a36Sopenharmony_ci}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_cistatic struct resource *nonstatic_find_mem_region(u_long base, u_long num,
80662306a36Sopenharmony_ci		u_long align, int low, struct pcmcia_socket *s)
80762306a36Sopenharmony_ci{
80862306a36Sopenharmony_ci	struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_MEM,
80962306a36Sopenharmony_ci						dev_name(&s->dev));
81062306a36Sopenharmony_ci	struct socket_data *s_data = s->resource_data;
81162306a36Sopenharmony_ci	struct pcmcia_align_data data;
81262306a36Sopenharmony_ci	unsigned long min, max;
81362306a36Sopenharmony_ci	int ret, i, j;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	if (!res)
81662306a36Sopenharmony_ci		return NULL;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	low = low || !(s->features & SS_CAP_PAGE_REGS);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	data.mask = align - 1;
82162306a36Sopenharmony_ci	data.offset = base & data.mask;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	for (i = 0; i < 2; i++) {
82462306a36Sopenharmony_ci		data.map = &s_data->mem_db_valid;
82562306a36Sopenharmony_ci		if (low) {
82662306a36Sopenharmony_ci			max = 0x100000UL;
82762306a36Sopenharmony_ci			min = base < max ? base : 0;
82862306a36Sopenharmony_ci		} else {
82962306a36Sopenharmony_ci			max = ~0UL;
83062306a36Sopenharmony_ci			min = 0x100000UL + base;
83162306a36Sopenharmony_ci		}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci		for (j = 0; j < 2; j++) {
83462306a36Sopenharmony_ci#ifdef CONFIG_PCI
83562306a36Sopenharmony_ci			if (s->cb_dev) {
83662306a36Sopenharmony_ci				ret = pci_bus_alloc_resource(s->cb_dev->bus,
83762306a36Sopenharmony_ci							res, num, 1, min, 0,
83862306a36Sopenharmony_ci							pcmcia_align, &data);
83962306a36Sopenharmony_ci			} else
84062306a36Sopenharmony_ci#endif
84162306a36Sopenharmony_ci			{
84262306a36Sopenharmony_ci				ret = allocate_resource(&iomem_resource,
84362306a36Sopenharmony_ci							res, num, min, max, 1,
84462306a36Sopenharmony_ci							pcmcia_align, &data);
84562306a36Sopenharmony_ci			}
84662306a36Sopenharmony_ci			if (ret == 0)
84762306a36Sopenharmony_ci				break;
84862306a36Sopenharmony_ci			data.map = &s_data->mem_db;
84962306a36Sopenharmony_ci		}
85062306a36Sopenharmony_ci		if (ret == 0 || low)
85162306a36Sopenharmony_ci			break;
85262306a36Sopenharmony_ci		low = 1;
85362306a36Sopenharmony_ci	}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	if (ret != 0) {
85662306a36Sopenharmony_ci		kfree(res);
85762306a36Sopenharmony_ci		res = NULL;
85862306a36Sopenharmony_ci	}
85962306a36Sopenharmony_ci	return res;
86062306a36Sopenharmony_ci}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_cistatic int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end)
86462306a36Sopenharmony_ci{
86562306a36Sopenharmony_ci	struct socket_data *data = s->resource_data;
86662306a36Sopenharmony_ci	unsigned long size = end - start + 1;
86762306a36Sopenharmony_ci	int ret = 0;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	if (end < start)
87062306a36Sopenharmony_ci		return -EINVAL;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	switch (action) {
87362306a36Sopenharmony_ci	case ADD_MANAGED_RESOURCE:
87462306a36Sopenharmony_ci		ret = add_interval(&data->mem_db, start, size);
87562306a36Sopenharmony_ci		if (!ret)
87662306a36Sopenharmony_ci			do_mem_probe(s, start, size, NULL, NULL);
87762306a36Sopenharmony_ci		break;
87862306a36Sopenharmony_ci	case REMOVE_MANAGED_RESOURCE:
87962306a36Sopenharmony_ci		ret = sub_interval(&data->mem_db, start, size);
88062306a36Sopenharmony_ci		break;
88162306a36Sopenharmony_ci	default:
88262306a36Sopenharmony_ci		ret = -EINVAL;
88362306a36Sopenharmony_ci	}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	return ret;
88662306a36Sopenharmony_ci}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_cistatic int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end)
89062306a36Sopenharmony_ci{
89162306a36Sopenharmony_ci	struct socket_data *data = s->resource_data;
89262306a36Sopenharmony_ci	unsigned long size;
89362306a36Sopenharmony_ci	int ret = 0;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci#if defined(CONFIG_X86)
89662306a36Sopenharmony_ci	/* on x86, avoid anything < 0x100 for it is often used for
89762306a36Sopenharmony_ci	 * legacy platform devices */
89862306a36Sopenharmony_ci	if (start < 0x100)
89962306a36Sopenharmony_ci		start = 0x100;
90062306a36Sopenharmony_ci#endif
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	size = end - start + 1;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	if (end < start)
90562306a36Sopenharmony_ci		return -EINVAL;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	if (end > IO_SPACE_LIMIT)
90862306a36Sopenharmony_ci		return -EINVAL;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	switch (action) {
91162306a36Sopenharmony_ci	case ADD_MANAGED_RESOURCE:
91262306a36Sopenharmony_ci		if (add_interval(&data->io_db, start, size) != 0) {
91362306a36Sopenharmony_ci			ret = -EBUSY;
91462306a36Sopenharmony_ci			break;
91562306a36Sopenharmony_ci		}
91662306a36Sopenharmony_ci#ifdef CONFIG_PCMCIA_PROBE
91762306a36Sopenharmony_ci		if (probe_io)
91862306a36Sopenharmony_ci			do_io_probe(s, start, size);
91962306a36Sopenharmony_ci#endif
92062306a36Sopenharmony_ci		break;
92162306a36Sopenharmony_ci	case REMOVE_MANAGED_RESOURCE:
92262306a36Sopenharmony_ci		sub_interval(&data->io_db, start, size);
92362306a36Sopenharmony_ci		break;
92462306a36Sopenharmony_ci	default:
92562306a36Sopenharmony_ci		ret = -EINVAL;
92662306a36Sopenharmony_ci		break;
92762306a36Sopenharmony_ci	}
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	return ret;
93062306a36Sopenharmony_ci}
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci#ifdef CONFIG_PCI
93462306a36Sopenharmony_cistatic int nonstatic_autoadd_resources(struct pcmcia_socket *s)
93562306a36Sopenharmony_ci{
93662306a36Sopenharmony_ci	struct resource *res;
93762306a36Sopenharmony_ci	int i, done = 0;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	if (!s->cb_dev || !s->cb_dev->bus)
94062306a36Sopenharmony_ci		return -ENODEV;
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci#if defined(CONFIG_X86)
94362306a36Sopenharmony_ci	/* If this is the root bus, the risk of hitting some strange
94462306a36Sopenharmony_ci	 * system devices is too high: If a driver isn't loaded, the
94562306a36Sopenharmony_ci	 * resources are not claimed; even if a driver is loaded, it
94662306a36Sopenharmony_ci	 * may not request all resources or even the wrong one. We
94762306a36Sopenharmony_ci	 * can neither trust the rest of the kernel nor ACPI/PNP and
94862306a36Sopenharmony_ci	 * CRS parsing to get it right. Therefore, use several
94962306a36Sopenharmony_ci	 * safeguards:
95062306a36Sopenharmony_ci	 *
95162306a36Sopenharmony_ci	 * - Do not auto-add resources if the CardBus bridge is on
95262306a36Sopenharmony_ci	 *   the PCI root bus
95362306a36Sopenharmony_ci	 *
95462306a36Sopenharmony_ci	 * - Avoid any I/O ports < 0x100.
95562306a36Sopenharmony_ci	 *
95662306a36Sopenharmony_ci	 * - On PCI-PCI bridges, only use resources which are set up
95762306a36Sopenharmony_ci	 *   exclusively for the secondary PCI bus: the risk of hitting
95862306a36Sopenharmony_ci	 *   system devices is quite low, as they usually aren't
95962306a36Sopenharmony_ci	 *   connected to the secondary PCI bus.
96062306a36Sopenharmony_ci	 */
96162306a36Sopenharmony_ci	if (s->cb_dev->bus->number == 0)
96262306a36Sopenharmony_ci		return -EINVAL;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) {
96562306a36Sopenharmony_ci		res = s->cb_dev->bus->resource[i];
96662306a36Sopenharmony_ci#else
96762306a36Sopenharmony_ci	pci_bus_for_each_resource(s->cb_dev->bus, res, i) {
96862306a36Sopenharmony_ci#endif
96962306a36Sopenharmony_ci		if (!res)
97062306a36Sopenharmony_ci			continue;
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci		if (res->flags & IORESOURCE_IO) {
97362306a36Sopenharmony_ci			/* safeguard against the root resource, where the
97462306a36Sopenharmony_ci			 * risk of hitting any other device would be too
97562306a36Sopenharmony_ci			 * high */
97662306a36Sopenharmony_ci			if (res == &ioport_resource)
97762306a36Sopenharmony_ci				continue;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci			dev_info(&s->cb_dev->dev,
98062306a36Sopenharmony_ci				 "pcmcia: parent PCI bridge window: %pR\n",
98162306a36Sopenharmony_ci				 res);
98262306a36Sopenharmony_ci			if (!adjust_io(s, ADD_MANAGED_RESOURCE, res->start, res->end))
98362306a36Sopenharmony_ci				done |= IORESOURCE_IO;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci		}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci		if (res->flags & IORESOURCE_MEM) {
98862306a36Sopenharmony_ci			/* safeguard against the root resource, where the
98962306a36Sopenharmony_ci			 * risk of hitting any other device would be too
99062306a36Sopenharmony_ci			 * high */
99162306a36Sopenharmony_ci			if (res == &iomem_resource)
99262306a36Sopenharmony_ci				continue;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci			dev_info(&s->cb_dev->dev,
99562306a36Sopenharmony_ci				 "pcmcia: parent PCI bridge window: %pR\n",
99662306a36Sopenharmony_ci				 res);
99762306a36Sopenharmony_ci			if (!adjust_memory(s, ADD_MANAGED_RESOURCE, res->start, res->end))
99862306a36Sopenharmony_ci				done |= IORESOURCE_MEM;
99962306a36Sopenharmony_ci		}
100062306a36Sopenharmony_ci	}
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	/* if we got at least one of IO, and one of MEM, we can be glad and
100362306a36Sopenharmony_ci	 * activate the PCMCIA subsystem */
100462306a36Sopenharmony_ci	if (done == (IORESOURCE_MEM | IORESOURCE_IO))
100562306a36Sopenharmony_ci		s->resource_setup_done = 1;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	return 0;
100862306a36Sopenharmony_ci}
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci#else
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_cistatic inline int nonstatic_autoadd_resources(struct pcmcia_socket *s)
101362306a36Sopenharmony_ci{
101462306a36Sopenharmony_ci	return -ENODEV;
101562306a36Sopenharmony_ci}
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci#endif
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_cistatic int nonstatic_init(struct pcmcia_socket *s)
102162306a36Sopenharmony_ci{
102262306a36Sopenharmony_ci	struct socket_data *data;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	data = kzalloc(sizeof(struct socket_data), GFP_KERNEL);
102562306a36Sopenharmony_ci	if (!data)
102662306a36Sopenharmony_ci		return -ENOMEM;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	data->mem_db.next = &data->mem_db;
102962306a36Sopenharmony_ci	data->mem_db_valid.next = &data->mem_db_valid;
103062306a36Sopenharmony_ci	data->io_db.next = &data->io_db;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	s->resource_data = (void *) data;
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	nonstatic_autoadd_resources(s);
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	return 0;
103762306a36Sopenharmony_ci}
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_cistatic void nonstatic_release_resource_db(struct pcmcia_socket *s)
104062306a36Sopenharmony_ci{
104162306a36Sopenharmony_ci	struct socket_data *data = s->resource_data;
104262306a36Sopenharmony_ci	struct resource_map *p, *q;
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	for (p = data->mem_db_valid.next; p != &data->mem_db_valid; p = q) {
104562306a36Sopenharmony_ci		q = p->next;
104662306a36Sopenharmony_ci		kfree(p);
104762306a36Sopenharmony_ci	}
104862306a36Sopenharmony_ci	for (p = data->mem_db.next; p != &data->mem_db; p = q) {
104962306a36Sopenharmony_ci		q = p->next;
105062306a36Sopenharmony_ci		kfree(p);
105162306a36Sopenharmony_ci	}
105262306a36Sopenharmony_ci	for (p = data->io_db.next; p != &data->io_db; p = q) {
105362306a36Sopenharmony_ci		q = p->next;
105462306a36Sopenharmony_ci		kfree(p);
105562306a36Sopenharmony_ci	}
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	kfree(data);
105862306a36Sopenharmony_ci}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_cistruct pccard_resource_ops pccard_nonstatic_ops = {
106262306a36Sopenharmony_ci	.validate_mem = pcmcia_nonstatic_validate_mem,
106362306a36Sopenharmony_ci	.find_io = nonstatic_find_io,
106462306a36Sopenharmony_ci	.find_mem = nonstatic_find_mem_region,
106562306a36Sopenharmony_ci	.init = nonstatic_init,
106662306a36Sopenharmony_ci	.exit = nonstatic_release_resource_db,
106762306a36Sopenharmony_ci};
106862306a36Sopenharmony_ciEXPORT_SYMBOL(pccard_nonstatic_ops);
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci/* sysfs interface to the resource database */
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_cistatic ssize_t show_io_db(struct device *dev,
107462306a36Sopenharmony_ci			  struct device_attribute *attr, char *buf)
107562306a36Sopenharmony_ci{
107662306a36Sopenharmony_ci	struct pcmcia_socket *s = dev_get_drvdata(dev);
107762306a36Sopenharmony_ci	struct socket_data *data;
107862306a36Sopenharmony_ci	struct resource_map *p;
107962306a36Sopenharmony_ci	ssize_t ret = 0;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	mutex_lock(&s->ops_mutex);
108262306a36Sopenharmony_ci	data = s->resource_data;
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	for (p = data->io_db.next; p != &data->io_db; p = p->next) {
108562306a36Sopenharmony_ci		if (ret > (PAGE_SIZE - 10))
108662306a36Sopenharmony_ci			continue;
108762306a36Sopenharmony_ci		ret += sysfs_emit_at(buf, ret,
108862306a36Sopenharmony_ci				"0x%08lx - 0x%08lx\n",
108962306a36Sopenharmony_ci				((unsigned long) p->base),
109062306a36Sopenharmony_ci				((unsigned long) p->base + p->num - 1));
109162306a36Sopenharmony_ci	}
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
109462306a36Sopenharmony_ci	return ret;
109562306a36Sopenharmony_ci}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_cistatic ssize_t store_io_db(struct device *dev,
109862306a36Sopenharmony_ci			   struct device_attribute *attr,
109962306a36Sopenharmony_ci			   const char *buf, size_t count)
110062306a36Sopenharmony_ci{
110162306a36Sopenharmony_ci	struct pcmcia_socket *s = dev_get_drvdata(dev);
110262306a36Sopenharmony_ci	unsigned long start_addr, end_addr;
110362306a36Sopenharmony_ci	unsigned int add = ADD_MANAGED_RESOURCE;
110462306a36Sopenharmony_ci	ssize_t ret = 0;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	ret = sscanf(buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
110762306a36Sopenharmony_ci	if (ret != 2) {
110862306a36Sopenharmony_ci		ret = sscanf(buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
110962306a36Sopenharmony_ci		add = REMOVE_MANAGED_RESOURCE;
111062306a36Sopenharmony_ci		if (ret != 2) {
111162306a36Sopenharmony_ci			ret = sscanf(buf, "0x%lx - 0x%lx", &start_addr,
111262306a36Sopenharmony_ci				&end_addr);
111362306a36Sopenharmony_ci			add = ADD_MANAGED_RESOURCE;
111462306a36Sopenharmony_ci			if (ret != 2)
111562306a36Sopenharmony_ci				return -EINVAL;
111662306a36Sopenharmony_ci		}
111762306a36Sopenharmony_ci	}
111862306a36Sopenharmony_ci	if (end_addr < start_addr)
111962306a36Sopenharmony_ci		return -EINVAL;
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	mutex_lock(&s->ops_mutex);
112262306a36Sopenharmony_ci	ret = adjust_io(s, add, start_addr, end_addr);
112362306a36Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	return ret ? ret : count;
112662306a36Sopenharmony_ci}
112762306a36Sopenharmony_cistatic DEVICE_ATTR(available_resources_io, 0600, show_io_db, store_io_db);
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_cistatic ssize_t show_mem_db(struct device *dev,
113062306a36Sopenharmony_ci			   struct device_attribute *attr, char *buf)
113162306a36Sopenharmony_ci{
113262306a36Sopenharmony_ci	struct pcmcia_socket *s = dev_get_drvdata(dev);
113362306a36Sopenharmony_ci	struct socket_data *data;
113462306a36Sopenharmony_ci	struct resource_map *p;
113562306a36Sopenharmony_ci	ssize_t ret = 0;
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	mutex_lock(&s->ops_mutex);
113862306a36Sopenharmony_ci	data = s->resource_data;
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	for (p = data->mem_db_valid.next; p != &data->mem_db_valid;
114162306a36Sopenharmony_ci	     p = p->next) {
114262306a36Sopenharmony_ci		if (ret > (PAGE_SIZE - 10))
114362306a36Sopenharmony_ci			continue;
114462306a36Sopenharmony_ci		ret += sysfs_emit_at(buf, ret,
114562306a36Sopenharmony_ci				"0x%08lx - 0x%08lx\n",
114662306a36Sopenharmony_ci				((unsigned long) p->base),
114762306a36Sopenharmony_ci				((unsigned long) p->base + p->num - 1));
114862306a36Sopenharmony_ci	}
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	for (p = data->mem_db.next; p != &data->mem_db; p = p->next) {
115162306a36Sopenharmony_ci		if (ret > (PAGE_SIZE - 10))
115262306a36Sopenharmony_ci			continue;
115362306a36Sopenharmony_ci		ret += sysfs_emit_at(buf, ret,
115462306a36Sopenharmony_ci				"0x%08lx - 0x%08lx\n",
115562306a36Sopenharmony_ci				((unsigned long) p->base),
115662306a36Sopenharmony_ci				((unsigned long) p->base + p->num - 1));
115762306a36Sopenharmony_ci	}
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
116062306a36Sopenharmony_ci	return ret;
116162306a36Sopenharmony_ci}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_cistatic ssize_t store_mem_db(struct device *dev,
116462306a36Sopenharmony_ci			    struct device_attribute *attr,
116562306a36Sopenharmony_ci			    const char *buf, size_t count)
116662306a36Sopenharmony_ci{
116762306a36Sopenharmony_ci	struct pcmcia_socket *s = dev_get_drvdata(dev);
116862306a36Sopenharmony_ci	unsigned long start_addr, end_addr;
116962306a36Sopenharmony_ci	unsigned int add = ADD_MANAGED_RESOURCE;
117062306a36Sopenharmony_ci	ssize_t ret = 0;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	ret = sscanf(buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
117362306a36Sopenharmony_ci	if (ret != 2) {
117462306a36Sopenharmony_ci		ret = sscanf(buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
117562306a36Sopenharmony_ci		add = REMOVE_MANAGED_RESOURCE;
117662306a36Sopenharmony_ci		if (ret != 2) {
117762306a36Sopenharmony_ci			ret = sscanf(buf, "0x%lx - 0x%lx", &start_addr,
117862306a36Sopenharmony_ci				&end_addr);
117962306a36Sopenharmony_ci			add = ADD_MANAGED_RESOURCE;
118062306a36Sopenharmony_ci			if (ret != 2)
118162306a36Sopenharmony_ci				return -EINVAL;
118262306a36Sopenharmony_ci		}
118362306a36Sopenharmony_ci	}
118462306a36Sopenharmony_ci	if (end_addr < start_addr)
118562306a36Sopenharmony_ci		return -EINVAL;
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	mutex_lock(&s->ops_mutex);
118862306a36Sopenharmony_ci	ret = adjust_memory(s, add, start_addr, end_addr);
118962306a36Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	return ret ? ret : count;
119262306a36Sopenharmony_ci}
119362306a36Sopenharmony_cistatic DEVICE_ATTR(available_resources_mem, 0600, show_mem_db, store_mem_db);
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_cistatic struct attribute *pccard_rsrc_attributes[] = {
119662306a36Sopenharmony_ci	&dev_attr_available_resources_io.attr,
119762306a36Sopenharmony_ci	&dev_attr_available_resources_mem.attr,
119862306a36Sopenharmony_ci	NULL,
119962306a36Sopenharmony_ci};
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_cistatic const struct attribute_group rsrc_attributes = {
120262306a36Sopenharmony_ci	.attrs = pccard_rsrc_attributes,
120362306a36Sopenharmony_ci};
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_cistatic int pccard_sysfs_add_rsrc(struct device *dev)
120662306a36Sopenharmony_ci{
120762306a36Sopenharmony_ci	struct pcmcia_socket *s = dev_get_drvdata(dev);
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	if (s->resource_ops != &pccard_nonstatic_ops)
121062306a36Sopenharmony_ci		return 0;
121162306a36Sopenharmony_ci	return sysfs_create_group(&dev->kobj, &rsrc_attributes);
121262306a36Sopenharmony_ci}
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_cistatic void pccard_sysfs_remove_rsrc(struct device *dev)
121562306a36Sopenharmony_ci{
121662306a36Sopenharmony_ci	struct pcmcia_socket *s = dev_get_drvdata(dev);
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	if (s->resource_ops != &pccard_nonstatic_ops)
121962306a36Sopenharmony_ci		return;
122062306a36Sopenharmony_ci	sysfs_remove_group(&dev->kobj, &rsrc_attributes);
122162306a36Sopenharmony_ci}
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_cistatic struct class_interface pccard_rsrc_interface __refdata = {
122462306a36Sopenharmony_ci	.class = &pcmcia_socket_class,
122562306a36Sopenharmony_ci	.add_dev = &pccard_sysfs_add_rsrc,
122662306a36Sopenharmony_ci	.remove_dev = &pccard_sysfs_remove_rsrc,
122762306a36Sopenharmony_ci};
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_cistatic int __init nonstatic_sysfs_init(void)
123062306a36Sopenharmony_ci{
123162306a36Sopenharmony_ci	return class_interface_register(&pccard_rsrc_interface);
123262306a36Sopenharmony_ci}
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_cistatic void __exit nonstatic_sysfs_exit(void)
123562306a36Sopenharmony_ci{
123662306a36Sopenharmony_ci	class_interface_unregister(&pccard_rsrc_interface);
123762306a36Sopenharmony_ci}
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_cimodule_init(nonstatic_sysfs_init);
124062306a36Sopenharmony_cimodule_exit(nonstatic_sysfs_exit);
1241