162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * PCMCIA 16-bit resource management functions
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 * Copyright (C) 1999	     David A. Hinds
1062306a36Sopenharmony_ci * Copyright (C) 2004-2010   Dominik Brodowski
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/interrupt.h>
1662306a36Sopenharmony_ci#include <linux/delay.h>
1762306a36Sopenharmony_ci#include <linux/pci.h>
1862306a36Sopenharmony_ci#include <linux/device.h>
1962306a36Sopenharmony_ci#include <linux/netdevice.h>
2062306a36Sopenharmony_ci#include <linux/slab.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <asm/irq.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <pcmcia/ss.h>
2562306a36Sopenharmony_ci#include <pcmcia/cistpl.h>
2662306a36Sopenharmony_ci#include <pcmcia/cisreg.h>
2762306a36Sopenharmony_ci#include <pcmcia/ds.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include "cs_internal.h"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/* Access speed for IO windows */
3362306a36Sopenharmony_cistatic int io_speed;
3462306a36Sopenharmony_cimodule_param(io_speed, int, 0444);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ciint pcmcia_validate_mem(struct pcmcia_socket *s)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	if (s->resource_ops->validate_mem)
4062306a36Sopenharmony_ci		return s->resource_ops->validate_mem(s);
4162306a36Sopenharmony_ci	/* if there is no callback, we can assume that everything is OK */
4262306a36Sopenharmony_ci	return 0;
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistruct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align,
4662306a36Sopenharmony_ci				 int low, struct pcmcia_socket *s)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	if (s->resource_ops->find_mem)
4962306a36Sopenharmony_ci		return s->resource_ops->find_mem(base, num, align, low, s);
5062306a36Sopenharmony_ci	return NULL;
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/**
5562306a36Sopenharmony_ci * release_io_space() - release IO ports allocated with alloc_io_space()
5662306a36Sopenharmony_ci * @s: pcmcia socket
5762306a36Sopenharmony_ci * @res: resource to release
5862306a36Sopenharmony_ci *
5962306a36Sopenharmony_ci */
6062306a36Sopenharmony_cistatic void release_io_space(struct pcmcia_socket *s, struct resource *res)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	resource_size_t num = resource_size(res);
6362306a36Sopenharmony_ci	int i;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	dev_dbg(&s->dev, "release_io_space for %pR\n", res);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	for (i = 0; i < MAX_IO_WIN; i++) {
6862306a36Sopenharmony_ci		if (!s->io[i].res)
6962306a36Sopenharmony_ci			continue;
7062306a36Sopenharmony_ci		if ((s->io[i].res->start <= res->start) &&
7162306a36Sopenharmony_ci		    (s->io[i].res->end >= res->end)) {
7262306a36Sopenharmony_ci			s->io[i].InUse -= num;
7362306a36Sopenharmony_ci			if (res->parent)
7462306a36Sopenharmony_ci				release_resource(res);
7562306a36Sopenharmony_ci			res->start = res->end = 0;
7662306a36Sopenharmony_ci			res->flags = IORESOURCE_IO;
7762306a36Sopenharmony_ci			/* Free the window if no one else is using it */
7862306a36Sopenharmony_ci			if (s->io[i].InUse == 0) {
7962306a36Sopenharmony_ci				release_resource(s->io[i].res);
8062306a36Sopenharmony_ci				kfree(s->io[i].res);
8162306a36Sopenharmony_ci				s->io[i].res = NULL;
8262306a36Sopenharmony_ci			}
8362306a36Sopenharmony_ci		}
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/**
8962306a36Sopenharmony_ci * alloc_io_space() - allocate IO ports for use by a PCMCIA device
9062306a36Sopenharmony_ci * @s: pcmcia socket
9162306a36Sopenharmony_ci * @res: resource to allocate (begin: begin, end: size)
9262306a36Sopenharmony_ci * @lines: number of IO lines decoded by the PCMCIA card
9362306a36Sopenharmony_ci *
9462306a36Sopenharmony_ci * Special stuff for managing IO windows, because they are scarce
9562306a36Sopenharmony_ci */
9662306a36Sopenharmony_cistatic int alloc_io_space(struct pcmcia_socket *s, struct resource *res,
9762306a36Sopenharmony_ci			unsigned int lines)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	unsigned int align;
10062306a36Sopenharmony_ci	unsigned int base = res->start;
10162306a36Sopenharmony_ci	unsigned int num = res->end;
10262306a36Sopenharmony_ci	int ret;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	res->flags |= IORESOURCE_IO;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	dev_dbg(&s->dev, "alloc_io_space request for %pR, %d lines\n",
10762306a36Sopenharmony_ci		res, lines);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	align = base ? (lines ? 1<<lines : 0) : 1;
11062306a36Sopenharmony_ci	if (align && (align < num)) {
11162306a36Sopenharmony_ci		if (base) {
11262306a36Sopenharmony_ci			dev_dbg(&s->dev, "odd IO request\n");
11362306a36Sopenharmony_ci			align = 0;
11462306a36Sopenharmony_ci		} else
11562306a36Sopenharmony_ci			while (align && (align < num))
11662306a36Sopenharmony_ci				align <<= 1;
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci	if (base & ~(align-1)) {
11962306a36Sopenharmony_ci		dev_dbg(&s->dev, "odd IO request\n");
12062306a36Sopenharmony_ci		align = 0;
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	ret = s->resource_ops->find_io(s, res->flags, &base, num, align,
12462306a36Sopenharmony_ci				&res->parent);
12562306a36Sopenharmony_ci	if (ret) {
12662306a36Sopenharmony_ci		dev_dbg(&s->dev, "alloc_io_space request failed (%d)\n", ret);
12762306a36Sopenharmony_ci		return -EINVAL;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	res->start = base;
13162306a36Sopenharmony_ci	res->end = res->start + num - 1;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (res->parent) {
13462306a36Sopenharmony_ci		ret = request_resource(res->parent, res);
13562306a36Sopenharmony_ci		if (ret) {
13662306a36Sopenharmony_ci			dev_warn(&s->dev,
13762306a36Sopenharmony_ci				"request_resource %pR failed: %d\n", res, ret);
13862306a36Sopenharmony_ci			res->parent = NULL;
13962306a36Sopenharmony_ci			release_io_space(s, res);
14062306a36Sopenharmony_ci		}
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci	dev_dbg(&s->dev, "alloc_io_space request result %d: %pR\n", ret, res);
14362306a36Sopenharmony_ci	return ret;
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci/*
14862306a36Sopenharmony_ci * pcmcia_access_config() - read or write card configuration registers
14962306a36Sopenharmony_ci *
15062306a36Sopenharmony_ci * pcmcia_access_config() reads and writes configuration registers in
15162306a36Sopenharmony_ci * attribute memory.  Memory window 0 is reserved for this and the tuple
15262306a36Sopenharmony_ci * reading services. Drivers must use pcmcia_read_config_byte() or
15362306a36Sopenharmony_ci * pcmcia_write_config_byte().
15462306a36Sopenharmony_ci */
15562306a36Sopenharmony_cistatic int pcmcia_access_config(struct pcmcia_device *p_dev,
15662306a36Sopenharmony_ci				off_t where, u8 *val,
15762306a36Sopenharmony_ci				int (*accessf) (struct pcmcia_socket *s,
15862306a36Sopenharmony_ci						int attr, unsigned int addr,
15962306a36Sopenharmony_ci						unsigned int len, void *ptr))
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	struct pcmcia_socket *s;
16262306a36Sopenharmony_ci	config_t *c;
16362306a36Sopenharmony_ci	int addr;
16462306a36Sopenharmony_ci	int ret = 0;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	s = p_dev->socket;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	mutex_lock(&s->ops_mutex);
16962306a36Sopenharmony_ci	c = p_dev->function_config;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if (!(c->state & CONFIG_LOCKED)) {
17262306a36Sopenharmony_ci		dev_dbg(&p_dev->dev, "Configuration isn't locked\n");
17362306a36Sopenharmony_ci		mutex_unlock(&s->ops_mutex);
17462306a36Sopenharmony_ci		return -EACCES;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	addr = (p_dev->config_base + where) >> 1;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	ret = accessf(s, 1, addr, 1, val);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	return ret;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci/*
18862306a36Sopenharmony_ci * pcmcia_read_config_byte() - read a byte from a card configuration register
18962306a36Sopenharmony_ci *
19062306a36Sopenharmony_ci * pcmcia_read_config_byte() reads a byte from a configuration register in
19162306a36Sopenharmony_ci * attribute memory.
19262306a36Sopenharmony_ci */
19362306a36Sopenharmony_ciint pcmcia_read_config_byte(struct pcmcia_device *p_dev, off_t where, u8 *val)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	return pcmcia_access_config(p_dev, where, val, pcmcia_read_cis_mem);
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ciEXPORT_SYMBOL(pcmcia_read_config_byte);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci/*
20162306a36Sopenharmony_ci * pcmcia_write_config_byte() - write a byte to a card configuration register
20262306a36Sopenharmony_ci *
20362306a36Sopenharmony_ci * pcmcia_write_config_byte() writes a byte to a configuration register in
20462306a36Sopenharmony_ci * attribute memory.
20562306a36Sopenharmony_ci */
20662306a36Sopenharmony_ciint pcmcia_write_config_byte(struct pcmcia_device *p_dev, off_t where, u8 val)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	return pcmcia_access_config(p_dev, where, &val, pcmcia_write_cis_mem);
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ciEXPORT_SYMBOL(pcmcia_write_config_byte);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci/**
21462306a36Sopenharmony_ci * pcmcia_map_mem_page() - modify iomem window to point to a different offset
21562306a36Sopenharmony_ci * @p_dev: pcmcia device
21662306a36Sopenharmony_ci * @res: iomem resource already enabled by pcmcia_request_window()
21762306a36Sopenharmony_ci * @offset: card_offset to map
21862306a36Sopenharmony_ci *
21962306a36Sopenharmony_ci * pcmcia_map_mem_page() modifies what can be read and written by accessing
22062306a36Sopenharmony_ci * an iomem range previously enabled by pcmcia_request_window(), by setting
22162306a36Sopenharmony_ci * the card_offset value to @offset.
22262306a36Sopenharmony_ci */
22362306a36Sopenharmony_ciint pcmcia_map_mem_page(struct pcmcia_device *p_dev, struct resource *res,
22462306a36Sopenharmony_ci			unsigned int offset)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct pcmcia_socket *s = p_dev->socket;
22762306a36Sopenharmony_ci	unsigned int w;
22862306a36Sopenharmony_ci	int ret;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	w = ((res->flags & IORESOURCE_BITS & WIN_FLAGS_REQ) >> 2) - 1;
23162306a36Sopenharmony_ci	if (w >= MAX_WIN)
23262306a36Sopenharmony_ci		return -EINVAL;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	mutex_lock(&s->ops_mutex);
23562306a36Sopenharmony_ci	s->win[w].card_start = offset;
23662306a36Sopenharmony_ci	ret = s->ops->set_mem_map(s, &s->win[w]);
23762306a36Sopenharmony_ci	if (ret)
23862306a36Sopenharmony_ci		dev_warn(&p_dev->dev, "failed to set_mem_map\n");
23962306a36Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
24062306a36Sopenharmony_ci	return ret;
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ciEXPORT_SYMBOL(pcmcia_map_mem_page);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci/**
24662306a36Sopenharmony_ci * pcmcia_fixup_iowidth() - reduce io width to 8bit
24762306a36Sopenharmony_ci * @p_dev: pcmcia device
24862306a36Sopenharmony_ci *
24962306a36Sopenharmony_ci * pcmcia_fixup_iowidth() allows a PCMCIA device driver to reduce the
25062306a36Sopenharmony_ci * IO width to 8bit after having called pcmcia_enable_device()
25162306a36Sopenharmony_ci * previously.
25262306a36Sopenharmony_ci */
25362306a36Sopenharmony_ciint pcmcia_fixup_iowidth(struct pcmcia_device *p_dev)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	struct pcmcia_socket *s = p_dev->socket;
25662306a36Sopenharmony_ci	pccard_io_map io_off = { 0, 0, 0, 0, 1 };
25762306a36Sopenharmony_ci	pccard_io_map io_on;
25862306a36Sopenharmony_ci	int i, ret = 0;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	mutex_lock(&s->ops_mutex);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	dev_dbg(&p_dev->dev, "fixup iowidth to 8bit\n");
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	if (!(s->state & SOCKET_PRESENT) ||
26562306a36Sopenharmony_ci		!(p_dev->function_config->state & CONFIG_LOCKED)) {
26662306a36Sopenharmony_ci		dev_dbg(&p_dev->dev, "No card? Config not locked?\n");
26762306a36Sopenharmony_ci		ret = -EACCES;
26862306a36Sopenharmony_ci		goto unlock;
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	io_on.speed = io_speed;
27262306a36Sopenharmony_ci	for (i = 0; i < MAX_IO_WIN; i++) {
27362306a36Sopenharmony_ci		if (!s->io[i].res)
27462306a36Sopenharmony_ci			continue;
27562306a36Sopenharmony_ci		io_off.map = i;
27662306a36Sopenharmony_ci		io_on.map = i;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci		io_on.flags = MAP_ACTIVE | IO_DATA_PATH_WIDTH_8;
27962306a36Sopenharmony_ci		io_on.start = s->io[i].res->start;
28062306a36Sopenharmony_ci		io_on.stop = s->io[i].res->end;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci		s->ops->set_io_map(s, &io_off);
28362306a36Sopenharmony_ci		msleep(40);
28462306a36Sopenharmony_ci		s->ops->set_io_map(s, &io_on);
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ciunlock:
28762306a36Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	return ret;
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ciEXPORT_SYMBOL(pcmcia_fixup_iowidth);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci/**
29562306a36Sopenharmony_ci * pcmcia_fixup_vpp() - set Vpp to a new voltage level
29662306a36Sopenharmony_ci * @p_dev: pcmcia device
29762306a36Sopenharmony_ci * @new_vpp: new Vpp voltage
29862306a36Sopenharmony_ci *
29962306a36Sopenharmony_ci * pcmcia_fixup_vpp() allows a PCMCIA device driver to set Vpp to
30062306a36Sopenharmony_ci * a new voltage level between calls to pcmcia_enable_device()
30162306a36Sopenharmony_ci * and pcmcia_disable_device().
30262306a36Sopenharmony_ci */
30362306a36Sopenharmony_ciint pcmcia_fixup_vpp(struct pcmcia_device *p_dev, unsigned char new_vpp)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	struct pcmcia_socket *s = p_dev->socket;
30662306a36Sopenharmony_ci	int ret = 0;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	mutex_lock(&s->ops_mutex);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	dev_dbg(&p_dev->dev, "fixup Vpp to %d\n", new_vpp);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	if (!(s->state & SOCKET_PRESENT) ||
31362306a36Sopenharmony_ci		!(p_dev->function_config->state & CONFIG_LOCKED)) {
31462306a36Sopenharmony_ci		dev_dbg(&p_dev->dev, "No card? Config not locked?\n");
31562306a36Sopenharmony_ci		ret = -EACCES;
31662306a36Sopenharmony_ci		goto unlock;
31762306a36Sopenharmony_ci	}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	s->socket.Vpp = new_vpp;
32062306a36Sopenharmony_ci	if (s->ops->set_socket(s, &s->socket)) {
32162306a36Sopenharmony_ci		dev_warn(&p_dev->dev, "Unable to set VPP\n");
32262306a36Sopenharmony_ci		ret = -EIO;
32362306a36Sopenharmony_ci		goto unlock;
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci	p_dev->vpp = new_vpp;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ciunlock:
32862306a36Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	return ret;
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ciEXPORT_SYMBOL(pcmcia_fixup_vpp);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci/**
33662306a36Sopenharmony_ci * pcmcia_release_configuration() - physically disable a PCMCIA device
33762306a36Sopenharmony_ci * @p_dev: pcmcia device
33862306a36Sopenharmony_ci *
33962306a36Sopenharmony_ci * pcmcia_release_configuration() is the 1:1 counterpart to
34062306a36Sopenharmony_ci * pcmcia_enable_device(): If a PCMCIA device is no longer used by any
34162306a36Sopenharmony_ci * driver, the Vpp voltage is set to 0, IRQs will no longer be generated,
34262306a36Sopenharmony_ci * and I/O ranges will be disabled. As pcmcia_release_io() and
34362306a36Sopenharmony_ci * pcmcia_release_window() still need to be called, device drivers are
34462306a36Sopenharmony_ci * expected to call pcmcia_disable_device() instead.
34562306a36Sopenharmony_ci */
34662306a36Sopenharmony_ciint pcmcia_release_configuration(struct pcmcia_device *p_dev)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	pccard_io_map io = { 0, 0, 0, 0, 1 };
34962306a36Sopenharmony_ci	struct pcmcia_socket *s = p_dev->socket;
35062306a36Sopenharmony_ci	config_t *c;
35162306a36Sopenharmony_ci	int i;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	mutex_lock(&s->ops_mutex);
35462306a36Sopenharmony_ci	c = p_dev->function_config;
35562306a36Sopenharmony_ci	if (p_dev->_locked) {
35662306a36Sopenharmony_ci		p_dev->_locked = 0;
35762306a36Sopenharmony_ci		if (--(s->lock_count) == 0) {
35862306a36Sopenharmony_ci			s->socket.flags = SS_OUTPUT_ENA; /* Is this correct? */
35962306a36Sopenharmony_ci			s->socket.Vpp = 0;
36062306a36Sopenharmony_ci			s->socket.io_irq = 0;
36162306a36Sopenharmony_ci			s->ops->set_socket(s, &s->socket);
36262306a36Sopenharmony_ci		}
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci	if (c->state & CONFIG_LOCKED) {
36562306a36Sopenharmony_ci		c->state &= ~CONFIG_LOCKED;
36662306a36Sopenharmony_ci		if (c->state & CONFIG_IO_REQ)
36762306a36Sopenharmony_ci			for (i = 0; i < MAX_IO_WIN; i++) {
36862306a36Sopenharmony_ci				if (!s->io[i].res)
36962306a36Sopenharmony_ci					continue;
37062306a36Sopenharmony_ci				s->io[i].Config--;
37162306a36Sopenharmony_ci				if (s->io[i].Config != 0)
37262306a36Sopenharmony_ci					continue;
37362306a36Sopenharmony_ci				io.map = i;
37462306a36Sopenharmony_ci				s->ops->set_io_map(s, &io);
37562306a36Sopenharmony_ci			}
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	return 0;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci/**
38462306a36Sopenharmony_ci * pcmcia_release_io() - release I/O allocated by a PCMCIA device
38562306a36Sopenharmony_ci * @p_dev: pcmcia device
38662306a36Sopenharmony_ci *
38762306a36Sopenharmony_ci * pcmcia_release_io() releases the I/O ranges allocated by a PCMCIA
38862306a36Sopenharmony_ci * device.  This may be invoked some time after a card ejection has
38962306a36Sopenharmony_ci * already dumped the actual socket configuration, so if the client is
39062306a36Sopenharmony_ci * "stale", we don't bother checking the port ranges against the
39162306a36Sopenharmony_ci * current socket values.
39262306a36Sopenharmony_ci */
39362306a36Sopenharmony_cistatic void pcmcia_release_io(struct pcmcia_device *p_dev)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	struct pcmcia_socket *s = p_dev->socket;
39662306a36Sopenharmony_ci	config_t *c;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	mutex_lock(&s->ops_mutex);
39962306a36Sopenharmony_ci	if (!p_dev->_io)
40062306a36Sopenharmony_ci		goto out;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	c = p_dev->function_config;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	release_io_space(s, &c->io[0]);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	if (c->io[1].end)
40762306a36Sopenharmony_ci		release_io_space(s, &c->io[1]);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	p_dev->_io = 0;
41062306a36Sopenharmony_ci	c->state &= ~CONFIG_IO_REQ;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ciout:
41362306a36Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
41462306a36Sopenharmony_ci} /* pcmcia_release_io */
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci/**
41862306a36Sopenharmony_ci * pcmcia_release_window() - release reserved iomem for PCMCIA devices
41962306a36Sopenharmony_ci * @p_dev: pcmcia device
42062306a36Sopenharmony_ci * @res: iomem resource to release
42162306a36Sopenharmony_ci *
42262306a36Sopenharmony_ci * pcmcia_release_window() releases &struct resource *res which was
42362306a36Sopenharmony_ci * previously reserved by calling pcmcia_request_window().
42462306a36Sopenharmony_ci */
42562306a36Sopenharmony_ciint pcmcia_release_window(struct pcmcia_device *p_dev, struct resource *res)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	struct pcmcia_socket *s = p_dev->socket;
42862306a36Sopenharmony_ci	pccard_mem_map *win;
42962306a36Sopenharmony_ci	unsigned int w;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	dev_dbg(&p_dev->dev, "releasing window %pR\n", res);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	w = ((res->flags & IORESOURCE_BITS & WIN_FLAGS_REQ) >> 2) - 1;
43462306a36Sopenharmony_ci	if (w >= MAX_WIN)
43562306a36Sopenharmony_ci		return -EINVAL;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	mutex_lock(&s->ops_mutex);
43862306a36Sopenharmony_ci	win = &s->win[w];
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	if (!(p_dev->_win & CLIENT_WIN_REQ(w))) {
44162306a36Sopenharmony_ci		dev_dbg(&p_dev->dev, "not releasing unknown window\n");
44262306a36Sopenharmony_ci		mutex_unlock(&s->ops_mutex);
44362306a36Sopenharmony_ci		return -EINVAL;
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	/* Shut down memory window */
44762306a36Sopenharmony_ci	win->flags &= ~MAP_ACTIVE;
44862306a36Sopenharmony_ci	s->ops->set_mem_map(s, win);
44962306a36Sopenharmony_ci	s->state &= ~SOCKET_WIN_REQ(w);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	/* Release system memory */
45262306a36Sopenharmony_ci	if (win->res) {
45362306a36Sopenharmony_ci		release_resource(res);
45462306a36Sopenharmony_ci		release_resource(win->res);
45562306a36Sopenharmony_ci		kfree(win->res);
45662306a36Sopenharmony_ci		win->res = NULL;
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci	res->start = res->end = 0;
45962306a36Sopenharmony_ci	res->flags = IORESOURCE_MEM;
46062306a36Sopenharmony_ci	p_dev->_win &= ~CLIENT_WIN_REQ(w);
46162306a36Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	return 0;
46462306a36Sopenharmony_ci} /* pcmcia_release_window */
46562306a36Sopenharmony_ciEXPORT_SYMBOL(pcmcia_release_window);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci/**
46962306a36Sopenharmony_ci * pcmcia_enable_device() - set up and activate a PCMCIA device
47062306a36Sopenharmony_ci * @p_dev: the associated PCMCIA device
47162306a36Sopenharmony_ci *
47262306a36Sopenharmony_ci * pcmcia_enable_device() physically enables a PCMCIA device. It parses
47362306a36Sopenharmony_ci * the flags passed to in @flags and stored in @p_dev->flags and sets up
47462306a36Sopenharmony_ci * the Vpp voltage, enables the speaker line, I/O ports and store proper
47562306a36Sopenharmony_ci * values to configuration registers.
47662306a36Sopenharmony_ci */
47762306a36Sopenharmony_ciint pcmcia_enable_device(struct pcmcia_device *p_dev)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	int i;
48062306a36Sopenharmony_ci	unsigned int base;
48162306a36Sopenharmony_ci	struct pcmcia_socket *s = p_dev->socket;
48262306a36Sopenharmony_ci	config_t *c;
48362306a36Sopenharmony_ci	pccard_io_map iomap;
48462306a36Sopenharmony_ci	unsigned char status = 0;
48562306a36Sopenharmony_ci	unsigned char ext_status = 0;
48662306a36Sopenharmony_ci	unsigned char option = 0;
48762306a36Sopenharmony_ci	unsigned int flags = p_dev->config_flags;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	if (!(s->state & SOCKET_PRESENT))
49062306a36Sopenharmony_ci		return -ENODEV;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	mutex_lock(&s->ops_mutex);
49362306a36Sopenharmony_ci	c = p_dev->function_config;
49462306a36Sopenharmony_ci	if (c->state & CONFIG_LOCKED) {
49562306a36Sopenharmony_ci		mutex_unlock(&s->ops_mutex);
49662306a36Sopenharmony_ci		dev_dbg(&p_dev->dev, "Configuration is locked\n");
49762306a36Sopenharmony_ci		return -EACCES;
49862306a36Sopenharmony_ci	}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	/* Do power control.  We don't allow changes in Vcc. */
50162306a36Sopenharmony_ci	s->socket.Vpp = p_dev->vpp;
50262306a36Sopenharmony_ci	if (s->ops->set_socket(s, &s->socket)) {
50362306a36Sopenharmony_ci		mutex_unlock(&s->ops_mutex);
50462306a36Sopenharmony_ci		dev_warn(&p_dev->dev, "Unable to set socket state\n");
50562306a36Sopenharmony_ci		return -EINVAL;
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	/* Pick memory or I/O card, DMA mode, interrupt */
50962306a36Sopenharmony_ci	if (p_dev->_io || flags & CONF_ENABLE_IRQ)
51062306a36Sopenharmony_ci		flags |= CONF_ENABLE_IOCARD;
51162306a36Sopenharmony_ci	if (flags & CONF_ENABLE_IOCARD)
51262306a36Sopenharmony_ci		s->socket.flags |= SS_IOCARD;
51362306a36Sopenharmony_ci	if (flags & CONF_ENABLE_ZVCARD)
51462306a36Sopenharmony_ci		s->socket.flags |= SS_ZVCARD | SS_IOCARD;
51562306a36Sopenharmony_ci	if (flags & CONF_ENABLE_SPKR) {
51662306a36Sopenharmony_ci		s->socket.flags |= SS_SPKR_ENA;
51762306a36Sopenharmony_ci		status = CCSR_AUDIO_ENA;
51862306a36Sopenharmony_ci		if (!(p_dev->config_regs & PRESENT_STATUS))
51962306a36Sopenharmony_ci			dev_warn(&p_dev->dev, "speaker requested, but "
52062306a36Sopenharmony_ci					      "PRESENT_STATUS not set!\n");
52162306a36Sopenharmony_ci	}
52262306a36Sopenharmony_ci	if (flags & CONF_ENABLE_IRQ)
52362306a36Sopenharmony_ci		s->socket.io_irq = s->pcmcia_irq;
52462306a36Sopenharmony_ci	else
52562306a36Sopenharmony_ci		s->socket.io_irq = 0;
52662306a36Sopenharmony_ci	if (flags & CONF_ENABLE_ESR) {
52762306a36Sopenharmony_ci		p_dev->config_regs |= PRESENT_EXT_STATUS;
52862306a36Sopenharmony_ci		ext_status = ESR_REQ_ATTN_ENA;
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci	s->ops->set_socket(s, &s->socket);
53162306a36Sopenharmony_ci	s->lock_count++;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	dev_dbg(&p_dev->dev,
53462306a36Sopenharmony_ci		"enable_device: V %d, flags %x, base %x, regs %x, idx %x\n",
53562306a36Sopenharmony_ci		p_dev->vpp, flags, p_dev->config_base, p_dev->config_regs,
53662306a36Sopenharmony_ci		p_dev->config_index);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	/* Set up CIS configuration registers */
53962306a36Sopenharmony_ci	base = p_dev->config_base;
54062306a36Sopenharmony_ci	if (p_dev->config_regs & PRESENT_COPY) {
54162306a36Sopenharmony_ci		u16 tmp = 0;
54262306a36Sopenharmony_ci		dev_dbg(&p_dev->dev, "clearing CISREG_SCR\n");
54362306a36Sopenharmony_ci		pcmcia_write_cis_mem(s, 1, (base + CISREG_SCR)>>1, 1, &tmp);
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci	if (p_dev->config_regs & PRESENT_PIN_REPLACE) {
54662306a36Sopenharmony_ci		u16 tmp = 0;
54762306a36Sopenharmony_ci		dev_dbg(&p_dev->dev, "clearing CISREG_PRR\n");
54862306a36Sopenharmony_ci		pcmcia_write_cis_mem(s, 1, (base + CISREG_PRR)>>1, 1, &tmp);
54962306a36Sopenharmony_ci	}
55062306a36Sopenharmony_ci	if (p_dev->config_regs & PRESENT_OPTION) {
55162306a36Sopenharmony_ci		if (s->functions == 1) {
55262306a36Sopenharmony_ci			option = p_dev->config_index & COR_CONFIG_MASK;
55362306a36Sopenharmony_ci		} else {
55462306a36Sopenharmony_ci			option = p_dev->config_index & COR_MFC_CONFIG_MASK;
55562306a36Sopenharmony_ci			option |= COR_FUNC_ENA|COR_IREQ_ENA;
55662306a36Sopenharmony_ci			if (p_dev->config_regs & PRESENT_IOBASE_0)
55762306a36Sopenharmony_ci				option |= COR_ADDR_DECODE;
55862306a36Sopenharmony_ci		}
55962306a36Sopenharmony_ci		if ((flags & CONF_ENABLE_IRQ) &&
56062306a36Sopenharmony_ci			!(flags & CONF_ENABLE_PULSE_IRQ))
56162306a36Sopenharmony_ci			option |= COR_LEVEL_REQ;
56262306a36Sopenharmony_ci		pcmcia_write_cis_mem(s, 1, (base + CISREG_COR)>>1, 1, &option);
56362306a36Sopenharmony_ci		msleep(40);
56462306a36Sopenharmony_ci	}
56562306a36Sopenharmony_ci	if (p_dev->config_regs & PRESENT_STATUS)
56662306a36Sopenharmony_ci		pcmcia_write_cis_mem(s, 1, (base + CISREG_CCSR)>>1, 1, &status);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	if (p_dev->config_regs & PRESENT_EXT_STATUS)
56962306a36Sopenharmony_ci		pcmcia_write_cis_mem(s, 1, (base + CISREG_ESR)>>1, 1,
57062306a36Sopenharmony_ci					&ext_status);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	if (p_dev->config_regs & PRESENT_IOBASE_0) {
57362306a36Sopenharmony_ci		u8 b = c->io[0].start & 0xff;
57462306a36Sopenharmony_ci		pcmcia_write_cis_mem(s, 1, (base + CISREG_IOBASE_0)>>1, 1, &b);
57562306a36Sopenharmony_ci		b = (c->io[0].start >> 8) & 0xff;
57662306a36Sopenharmony_ci		pcmcia_write_cis_mem(s, 1, (base + CISREG_IOBASE_1)>>1, 1, &b);
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci	if (p_dev->config_regs & PRESENT_IOSIZE) {
57962306a36Sopenharmony_ci		u8 b = resource_size(&c->io[0]) + resource_size(&c->io[1]) - 1;
58062306a36Sopenharmony_ci		pcmcia_write_cis_mem(s, 1, (base + CISREG_IOSIZE)>>1, 1, &b);
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	/* Configure I/O windows */
58462306a36Sopenharmony_ci	if (c->state & CONFIG_IO_REQ) {
58562306a36Sopenharmony_ci		iomap.speed = io_speed;
58662306a36Sopenharmony_ci		for (i = 0; i < MAX_IO_WIN; i++)
58762306a36Sopenharmony_ci			if (s->io[i].res) {
58862306a36Sopenharmony_ci				iomap.map = i;
58962306a36Sopenharmony_ci				iomap.flags = MAP_ACTIVE;
59062306a36Sopenharmony_ci				switch (s->io[i].res->flags & IO_DATA_PATH_WIDTH) {
59162306a36Sopenharmony_ci				case IO_DATA_PATH_WIDTH_16:
59262306a36Sopenharmony_ci					iomap.flags |= MAP_16BIT; break;
59362306a36Sopenharmony_ci				case IO_DATA_PATH_WIDTH_AUTO:
59462306a36Sopenharmony_ci					iomap.flags |= MAP_AUTOSZ; break;
59562306a36Sopenharmony_ci				default:
59662306a36Sopenharmony_ci					break;
59762306a36Sopenharmony_ci				}
59862306a36Sopenharmony_ci				iomap.start = s->io[i].res->start;
59962306a36Sopenharmony_ci				iomap.stop = s->io[i].res->end;
60062306a36Sopenharmony_ci				s->ops->set_io_map(s, &iomap);
60162306a36Sopenharmony_ci				s->io[i].Config++;
60262306a36Sopenharmony_ci			}
60362306a36Sopenharmony_ci	}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	c->state |= CONFIG_LOCKED;
60662306a36Sopenharmony_ci	p_dev->_locked = 1;
60762306a36Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
60862306a36Sopenharmony_ci	return 0;
60962306a36Sopenharmony_ci} /* pcmcia_enable_device */
61062306a36Sopenharmony_ciEXPORT_SYMBOL(pcmcia_enable_device);
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci/**
61462306a36Sopenharmony_ci * pcmcia_request_io() - attempt to reserve port ranges for PCMCIA devices
61562306a36Sopenharmony_ci * @p_dev: the associated PCMCIA device
61662306a36Sopenharmony_ci *
61762306a36Sopenharmony_ci * pcmcia_request_io() attempts to reserve the IO port ranges specified in
61862306a36Sopenharmony_ci * &struct pcmcia_device @p_dev->resource[0] and @p_dev->resource[1]. The
61962306a36Sopenharmony_ci * "start" value is the requested start of the IO port resource; "end"
62062306a36Sopenharmony_ci * reflects the number of ports requested. The number of IO lines requested
62162306a36Sopenharmony_ci * is specified in &struct pcmcia_device @p_dev->io_lines.
62262306a36Sopenharmony_ci */
62362306a36Sopenharmony_ciint pcmcia_request_io(struct pcmcia_device *p_dev)
62462306a36Sopenharmony_ci{
62562306a36Sopenharmony_ci	struct pcmcia_socket *s = p_dev->socket;
62662306a36Sopenharmony_ci	config_t *c = p_dev->function_config;
62762306a36Sopenharmony_ci	int ret = -EINVAL;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	mutex_lock(&s->ops_mutex);
63062306a36Sopenharmony_ci	dev_dbg(&p_dev->dev, "pcmcia_request_io: %pR , %pR",
63162306a36Sopenharmony_ci		&c->io[0], &c->io[1]);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	if (!(s->state & SOCKET_PRESENT)) {
63462306a36Sopenharmony_ci		dev_dbg(&p_dev->dev, "pcmcia_request_io: No card present\n");
63562306a36Sopenharmony_ci		goto out;
63662306a36Sopenharmony_ci	}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	if (c->state & CONFIG_LOCKED) {
63962306a36Sopenharmony_ci		dev_dbg(&p_dev->dev, "Configuration is locked\n");
64062306a36Sopenharmony_ci		goto out;
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci	if (c->state & CONFIG_IO_REQ) {
64362306a36Sopenharmony_ci		dev_dbg(&p_dev->dev, "IO already configured\n");
64462306a36Sopenharmony_ci		goto out;
64562306a36Sopenharmony_ci	}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	ret = alloc_io_space(s, &c->io[0], p_dev->io_lines);
64862306a36Sopenharmony_ci	if (ret)
64962306a36Sopenharmony_ci		goto out;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	if (c->io[1].end) {
65262306a36Sopenharmony_ci		ret = alloc_io_space(s, &c->io[1], p_dev->io_lines);
65362306a36Sopenharmony_ci		if (ret) {
65462306a36Sopenharmony_ci			struct resource tmp = c->io[0];
65562306a36Sopenharmony_ci			/* release the previously allocated resource */
65662306a36Sopenharmony_ci			release_io_space(s, &c->io[0]);
65762306a36Sopenharmony_ci			/* but preserve the settings, for they worked... */
65862306a36Sopenharmony_ci			c->io[0].end = resource_size(&tmp);
65962306a36Sopenharmony_ci			c->io[0].start = tmp.start;
66062306a36Sopenharmony_ci			c->io[0].flags = tmp.flags;
66162306a36Sopenharmony_ci			goto out;
66262306a36Sopenharmony_ci		}
66362306a36Sopenharmony_ci	} else
66462306a36Sopenharmony_ci		c->io[1].start = 0;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	c->state |= CONFIG_IO_REQ;
66762306a36Sopenharmony_ci	p_dev->_io = 1;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	dev_dbg(&p_dev->dev, "pcmcia_request_io succeeded: %pR , %pR",
67062306a36Sopenharmony_ci		&c->io[0], &c->io[1]);
67162306a36Sopenharmony_ciout:
67262306a36Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	return ret;
67562306a36Sopenharmony_ci} /* pcmcia_request_io */
67662306a36Sopenharmony_ciEXPORT_SYMBOL(pcmcia_request_io);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci/**
68062306a36Sopenharmony_ci * pcmcia_request_irq() - attempt to request a IRQ for a PCMCIA device
68162306a36Sopenharmony_ci * @p_dev: the associated PCMCIA device
68262306a36Sopenharmony_ci * @handler: IRQ handler to register
68362306a36Sopenharmony_ci *
68462306a36Sopenharmony_ci * pcmcia_request_irq() is a wrapper around request_irq() which allows
68562306a36Sopenharmony_ci * the PCMCIA core to clean up the registration in pcmcia_disable_device().
68662306a36Sopenharmony_ci * Drivers are free to use request_irq() directly, but then they need to
68762306a36Sopenharmony_ci * call free_irq() themselfves, too. Also, only %IRQF_SHARED capable IRQ
68862306a36Sopenharmony_ci * handlers are allowed.
68962306a36Sopenharmony_ci */
69062306a36Sopenharmony_ciint __must_check pcmcia_request_irq(struct pcmcia_device *p_dev,
69162306a36Sopenharmony_ci				    irq_handler_t handler)
69262306a36Sopenharmony_ci{
69362306a36Sopenharmony_ci	int ret;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	if (!p_dev->irq)
69662306a36Sopenharmony_ci		return -EINVAL;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	ret = request_irq(p_dev->irq, handler, IRQF_SHARED,
69962306a36Sopenharmony_ci			p_dev->devname, p_dev->priv);
70062306a36Sopenharmony_ci	if (!ret)
70162306a36Sopenharmony_ci		p_dev->_irq = 1;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	return ret;
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ciEXPORT_SYMBOL(pcmcia_request_irq);
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci#ifdef CONFIG_PCMCIA_PROBE
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci/* mask of IRQs already reserved by other cards, we should avoid using them */
71162306a36Sopenharmony_cistatic u8 pcmcia_used_irq[32];
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_cistatic irqreturn_t test_action(int cpl, void *dev_id)
71462306a36Sopenharmony_ci{
71562306a36Sopenharmony_ci	return IRQ_NONE;
71662306a36Sopenharmony_ci}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci/**
71962306a36Sopenharmony_ci * pcmcia_setup_isa_irq() - determine whether an ISA IRQ can be used
72062306a36Sopenharmony_ci * @p_dev: the associated PCMCIA device
72162306a36Sopenharmony_ci * @type:  IRQ type (flags)
72262306a36Sopenharmony_ci *
72362306a36Sopenharmony_ci * locking note: must be called with ops_mutex locked.
72462306a36Sopenharmony_ci */
72562306a36Sopenharmony_cistatic int pcmcia_setup_isa_irq(struct pcmcia_device *p_dev, int type)
72662306a36Sopenharmony_ci{
72762306a36Sopenharmony_ci	struct pcmcia_socket *s = p_dev->socket;
72862306a36Sopenharmony_ci	unsigned int try, irq;
72962306a36Sopenharmony_ci	u32 mask = s->irq_mask;
73062306a36Sopenharmony_ci	int ret = -ENODEV;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	for (try = 0; try < 64; try++) {
73362306a36Sopenharmony_ci		irq = try % 32;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci		if (irq > NR_IRQS)
73662306a36Sopenharmony_ci			continue;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci		/* marked as available by driver, not blocked by userspace? */
73962306a36Sopenharmony_ci		if (!((mask >> irq) & 1))
74062306a36Sopenharmony_ci			continue;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci		/* avoid an IRQ which is already used by another PCMCIA card */
74362306a36Sopenharmony_ci		if ((try < 32) && pcmcia_used_irq[irq])
74462306a36Sopenharmony_ci			continue;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci		/* register the correct driver, if possible, to check whether
74762306a36Sopenharmony_ci		 * registering a dummy handle works, i.e. if the IRQ isn't
74862306a36Sopenharmony_ci		 * marked as used by the kernel resource management core */
74962306a36Sopenharmony_ci		ret = request_irq(irq, test_action, type, p_dev->devname,
75062306a36Sopenharmony_ci				  p_dev);
75162306a36Sopenharmony_ci		if (!ret) {
75262306a36Sopenharmony_ci			free_irq(irq, p_dev);
75362306a36Sopenharmony_ci			p_dev->irq = s->pcmcia_irq = irq;
75462306a36Sopenharmony_ci			pcmcia_used_irq[irq]++;
75562306a36Sopenharmony_ci			break;
75662306a36Sopenharmony_ci		}
75762306a36Sopenharmony_ci	}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	return ret;
76062306a36Sopenharmony_ci}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_civoid pcmcia_cleanup_irq(struct pcmcia_socket *s)
76362306a36Sopenharmony_ci{
76462306a36Sopenharmony_ci	pcmcia_used_irq[s->pcmcia_irq]--;
76562306a36Sopenharmony_ci	s->pcmcia_irq = 0;
76662306a36Sopenharmony_ci}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci#else /* CONFIG_PCMCIA_PROBE */
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_cistatic int pcmcia_setup_isa_irq(struct pcmcia_device *p_dev, int type)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	return -EINVAL;
77362306a36Sopenharmony_ci}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_civoid pcmcia_cleanup_irq(struct pcmcia_socket *s)
77662306a36Sopenharmony_ci{
77762306a36Sopenharmony_ci	s->pcmcia_irq = 0;
77862306a36Sopenharmony_ci	return;
77962306a36Sopenharmony_ci}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci#endif  /* CONFIG_PCMCIA_PROBE */
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci/**
78562306a36Sopenharmony_ci * pcmcia_setup_irq() - determine IRQ to be used for device
78662306a36Sopenharmony_ci * @p_dev: the associated PCMCIA device
78762306a36Sopenharmony_ci *
78862306a36Sopenharmony_ci * locking note: must be called with ops_mutex locked.
78962306a36Sopenharmony_ci */
79062306a36Sopenharmony_ciint pcmcia_setup_irq(struct pcmcia_device *p_dev)
79162306a36Sopenharmony_ci{
79262306a36Sopenharmony_ci	struct pcmcia_socket *s = p_dev->socket;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	if (p_dev->irq)
79562306a36Sopenharmony_ci		return 0;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	/* already assigned? */
79862306a36Sopenharmony_ci	if (s->pcmcia_irq) {
79962306a36Sopenharmony_ci		p_dev->irq = s->pcmcia_irq;
80062306a36Sopenharmony_ci		return 0;
80162306a36Sopenharmony_ci	}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	/* prefer an exclusive ISA irq */
80462306a36Sopenharmony_ci	if (!pcmcia_setup_isa_irq(p_dev, 0))
80562306a36Sopenharmony_ci		return 0;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	/* but accept a shared ISA irq */
80862306a36Sopenharmony_ci	if (!pcmcia_setup_isa_irq(p_dev, IRQF_SHARED))
80962306a36Sopenharmony_ci		return 0;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	/* but use the PCI irq otherwise */
81262306a36Sopenharmony_ci	if (s->pci_irq) {
81362306a36Sopenharmony_ci		p_dev->irq = s->pcmcia_irq = s->pci_irq;
81462306a36Sopenharmony_ci		return 0;
81562306a36Sopenharmony_ci	}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	return -EINVAL;
81862306a36Sopenharmony_ci}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci/**
82262306a36Sopenharmony_ci * pcmcia_request_window() - attempt to reserve iomem for PCMCIA devices
82362306a36Sopenharmony_ci * @p_dev: the associated PCMCIA device
82462306a36Sopenharmony_ci * @res: &struct resource pointing to p_dev->resource[2..5]
82562306a36Sopenharmony_ci * @speed: access speed
82662306a36Sopenharmony_ci *
82762306a36Sopenharmony_ci * pcmcia_request_window() attepts to reserve an iomem ranges specified in
82862306a36Sopenharmony_ci * &struct resource @res pointing to one of the entries in
82962306a36Sopenharmony_ci * &struct pcmcia_device @p_dev->resource[2..5]. The "start" value is the
83062306a36Sopenharmony_ci * requested start of the IO mem resource; "end" reflects the size
83162306a36Sopenharmony_ci * requested.
83262306a36Sopenharmony_ci */
83362306a36Sopenharmony_ciint pcmcia_request_window(struct pcmcia_device *p_dev, struct resource *res,
83462306a36Sopenharmony_ci			unsigned int speed)
83562306a36Sopenharmony_ci{
83662306a36Sopenharmony_ci	struct pcmcia_socket *s = p_dev->socket;
83762306a36Sopenharmony_ci	pccard_mem_map *win;
83862306a36Sopenharmony_ci	u_long align;
83962306a36Sopenharmony_ci	int w;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	dev_dbg(&p_dev->dev, "request_window %pR %d\n", res, speed);
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	if (!(s->state & SOCKET_PRESENT)) {
84462306a36Sopenharmony_ci		dev_dbg(&p_dev->dev, "No card present\n");
84562306a36Sopenharmony_ci		return -ENODEV;
84662306a36Sopenharmony_ci	}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	/* Window size defaults to smallest available */
84962306a36Sopenharmony_ci	if (res->end == 0)
85062306a36Sopenharmony_ci		res->end = s->map_size;
85162306a36Sopenharmony_ci	align = (s->features & SS_CAP_MEM_ALIGN) ? res->end : s->map_size;
85262306a36Sopenharmony_ci	if (res->end & (s->map_size-1)) {
85362306a36Sopenharmony_ci		dev_dbg(&p_dev->dev, "invalid map size\n");
85462306a36Sopenharmony_ci		return -EINVAL;
85562306a36Sopenharmony_ci	}
85662306a36Sopenharmony_ci	if ((res->start && (s->features & SS_CAP_STATIC_MAP)) ||
85762306a36Sopenharmony_ci	    (res->start & (align-1))) {
85862306a36Sopenharmony_ci		dev_dbg(&p_dev->dev, "invalid base address\n");
85962306a36Sopenharmony_ci		return -EINVAL;
86062306a36Sopenharmony_ci	}
86162306a36Sopenharmony_ci	if (res->start)
86262306a36Sopenharmony_ci		align = 0;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	/* Allocate system memory window */
86562306a36Sopenharmony_ci	mutex_lock(&s->ops_mutex);
86662306a36Sopenharmony_ci	for (w = 0; w < MAX_WIN; w++)
86762306a36Sopenharmony_ci		if (!(s->state & SOCKET_WIN_REQ(w)))
86862306a36Sopenharmony_ci			break;
86962306a36Sopenharmony_ci	if (w == MAX_WIN) {
87062306a36Sopenharmony_ci		dev_dbg(&p_dev->dev, "all windows are used already\n");
87162306a36Sopenharmony_ci		mutex_unlock(&s->ops_mutex);
87262306a36Sopenharmony_ci		return -EINVAL;
87362306a36Sopenharmony_ci	}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	win = &s->win[w];
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	if (!(s->features & SS_CAP_STATIC_MAP)) {
87862306a36Sopenharmony_ci		win->res = pcmcia_find_mem_region(res->start, res->end, align,
87962306a36Sopenharmony_ci						0, s);
88062306a36Sopenharmony_ci		if (!win->res) {
88162306a36Sopenharmony_ci			dev_dbg(&p_dev->dev, "allocating mem region failed\n");
88262306a36Sopenharmony_ci			mutex_unlock(&s->ops_mutex);
88362306a36Sopenharmony_ci			return -EINVAL;
88462306a36Sopenharmony_ci		}
88562306a36Sopenharmony_ci	}
88662306a36Sopenharmony_ci	p_dev->_win |= CLIENT_WIN_REQ(w);
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	/* Configure the socket controller */
88962306a36Sopenharmony_ci	win->map = w+1;
89062306a36Sopenharmony_ci	win->flags = res->flags & WIN_FLAGS_MAP;
89162306a36Sopenharmony_ci	win->speed = speed;
89262306a36Sopenharmony_ci	win->card_start = 0;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	if (s->ops->set_mem_map(s, win) != 0) {
89562306a36Sopenharmony_ci		dev_dbg(&p_dev->dev, "failed to set memory mapping\n");
89662306a36Sopenharmony_ci		mutex_unlock(&s->ops_mutex);
89762306a36Sopenharmony_ci		return -EIO;
89862306a36Sopenharmony_ci	}
89962306a36Sopenharmony_ci	s->state |= SOCKET_WIN_REQ(w);
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	/* Return window handle */
90262306a36Sopenharmony_ci	if (s->features & SS_CAP_STATIC_MAP)
90362306a36Sopenharmony_ci		res->start = win->static_start;
90462306a36Sopenharmony_ci	else
90562306a36Sopenharmony_ci		res->start = win->res->start;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	/* convert to new-style resources */
90862306a36Sopenharmony_ci	res->end += res->start - 1;
90962306a36Sopenharmony_ci	res->flags &= ~WIN_FLAGS_REQ;
91062306a36Sopenharmony_ci	res->flags |= (win->map << 2) | IORESOURCE_MEM;
91162306a36Sopenharmony_ci	res->parent = win->res;
91262306a36Sopenharmony_ci	if (win->res)
91362306a36Sopenharmony_ci		request_resource(&iomem_resource, res);
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	dev_dbg(&p_dev->dev, "request_window results in %pR\n", res);
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	return 0;
92062306a36Sopenharmony_ci} /* pcmcia_request_window */
92162306a36Sopenharmony_ciEXPORT_SYMBOL(pcmcia_request_window);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci/**
92562306a36Sopenharmony_ci * pcmcia_disable_device() - disable and clean up a PCMCIA device
92662306a36Sopenharmony_ci * @p_dev: the associated PCMCIA device
92762306a36Sopenharmony_ci *
92862306a36Sopenharmony_ci * pcmcia_disable_device() is the driver-callable counterpart to
92962306a36Sopenharmony_ci * pcmcia_enable_device(): If a PCMCIA device is no longer used,
93062306a36Sopenharmony_ci * drivers are expected to clean up and disable the device by calling
93162306a36Sopenharmony_ci * this function. Any I/O ranges (iomem and ioports) will be released,
93262306a36Sopenharmony_ci * the Vpp voltage will be set to 0, and IRQs will no longer be
93362306a36Sopenharmony_ci * generated -- at least if there is no other card function (of
93462306a36Sopenharmony_ci * multifunction devices) being used.
93562306a36Sopenharmony_ci */
93662306a36Sopenharmony_civoid pcmcia_disable_device(struct pcmcia_device *p_dev)
93762306a36Sopenharmony_ci{
93862306a36Sopenharmony_ci	int i;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	dev_dbg(&p_dev->dev, "disabling device\n");
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	for (i = 0; i < MAX_WIN; i++) {
94362306a36Sopenharmony_ci		struct resource *res = p_dev->resource[MAX_IO_WIN + i];
94462306a36Sopenharmony_ci		if (res->flags & WIN_FLAGS_REQ)
94562306a36Sopenharmony_ci			pcmcia_release_window(p_dev, res);
94662306a36Sopenharmony_ci	}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	pcmcia_release_configuration(p_dev);
94962306a36Sopenharmony_ci	pcmcia_release_io(p_dev);
95062306a36Sopenharmony_ci	if (p_dev->_irq) {
95162306a36Sopenharmony_ci		free_irq(p_dev->irq, p_dev->priv);
95262306a36Sopenharmony_ci		p_dev->_irq = 0;
95362306a36Sopenharmony_ci	}
95462306a36Sopenharmony_ci}
95562306a36Sopenharmony_ciEXPORT_SYMBOL(pcmcia_disable_device);
956