18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * PCMCIA 16-bit resource management functions
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * The initial developer of the original code is David A. Hinds
68c2ecf20Sopenharmony_ci * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
78c2ecf20Sopenharmony_ci * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Copyright (C) 1999	     David A. Hinds
108c2ecf20Sopenharmony_ci * Copyright (C) 2004-2010   Dominik Brodowski
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
168c2ecf20Sopenharmony_ci#include <linux/delay.h>
178c2ecf20Sopenharmony_ci#include <linux/pci.h>
188c2ecf20Sopenharmony_ci#include <linux/device.h>
198c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <asm/irq.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include <pcmcia/ss.h>
258c2ecf20Sopenharmony_ci#include <pcmcia/cistpl.h>
268c2ecf20Sopenharmony_ci#include <pcmcia/cisreg.h>
278c2ecf20Sopenharmony_ci#include <pcmcia/ds.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#include "cs_internal.h"
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/* Access speed for IO windows */
338c2ecf20Sopenharmony_cistatic int io_speed;
348c2ecf20Sopenharmony_cimodule_param(io_speed, int, 0444);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ciint pcmcia_validate_mem(struct pcmcia_socket *s)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	if (s->resource_ops->validate_mem)
408c2ecf20Sopenharmony_ci		return s->resource_ops->validate_mem(s);
418c2ecf20Sopenharmony_ci	/* if there is no callback, we can assume that everything is OK */
428c2ecf20Sopenharmony_ci	return 0;
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistruct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align,
468c2ecf20Sopenharmony_ci				 int low, struct pcmcia_socket *s)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	if (s->resource_ops->find_mem)
498c2ecf20Sopenharmony_ci		return s->resource_ops->find_mem(base, num, align, low, s);
508c2ecf20Sopenharmony_ci	return NULL;
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/**
558c2ecf20Sopenharmony_ci * release_io_space() - release IO ports allocated with alloc_io_space()
568c2ecf20Sopenharmony_ci * @s: pcmcia socket
578c2ecf20Sopenharmony_ci * @res: resource to release
588c2ecf20Sopenharmony_ci *
598c2ecf20Sopenharmony_ci */
608c2ecf20Sopenharmony_cistatic void release_io_space(struct pcmcia_socket *s, struct resource *res)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	resource_size_t num = resource_size(res);
638c2ecf20Sopenharmony_ci	int i;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	dev_dbg(&s->dev, "release_io_space for %pR\n", res);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_IO_WIN; i++) {
688c2ecf20Sopenharmony_ci		if (!s->io[i].res)
698c2ecf20Sopenharmony_ci			continue;
708c2ecf20Sopenharmony_ci		if ((s->io[i].res->start <= res->start) &&
718c2ecf20Sopenharmony_ci		    (s->io[i].res->end >= res->end)) {
728c2ecf20Sopenharmony_ci			s->io[i].InUse -= num;
738c2ecf20Sopenharmony_ci			if (res->parent)
748c2ecf20Sopenharmony_ci				release_resource(res);
758c2ecf20Sopenharmony_ci			res->start = res->end = 0;
768c2ecf20Sopenharmony_ci			res->flags = IORESOURCE_IO;
778c2ecf20Sopenharmony_ci			/* Free the window if no one else is using it */
788c2ecf20Sopenharmony_ci			if (s->io[i].InUse == 0) {
798c2ecf20Sopenharmony_ci				release_resource(s->io[i].res);
808c2ecf20Sopenharmony_ci				kfree(s->io[i].res);
818c2ecf20Sopenharmony_ci				s->io[i].res = NULL;
828c2ecf20Sopenharmony_ci			}
838c2ecf20Sopenharmony_ci		}
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci/**
898c2ecf20Sopenharmony_ci * alloc_io_space() - allocate IO ports for use by a PCMCIA device
908c2ecf20Sopenharmony_ci * @s: pcmcia socket
918c2ecf20Sopenharmony_ci * @res: resource to allocate (begin: begin, end: size)
928c2ecf20Sopenharmony_ci * @lines: number of IO lines decoded by the PCMCIA card
938c2ecf20Sopenharmony_ci *
948c2ecf20Sopenharmony_ci * Special stuff for managing IO windows, because they are scarce
958c2ecf20Sopenharmony_ci */
968c2ecf20Sopenharmony_cistatic int alloc_io_space(struct pcmcia_socket *s, struct resource *res,
978c2ecf20Sopenharmony_ci			unsigned int lines)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	unsigned int align;
1008c2ecf20Sopenharmony_ci	unsigned int base = res->start;
1018c2ecf20Sopenharmony_ci	unsigned int num = res->end;
1028c2ecf20Sopenharmony_ci	int ret;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	res->flags |= IORESOURCE_IO;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	dev_dbg(&s->dev, "alloc_io_space request for %pR, %d lines\n",
1078c2ecf20Sopenharmony_ci		res, lines);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	align = base ? (lines ? 1<<lines : 0) : 1;
1108c2ecf20Sopenharmony_ci	if (align && (align < num)) {
1118c2ecf20Sopenharmony_ci		if (base) {
1128c2ecf20Sopenharmony_ci			dev_dbg(&s->dev, "odd IO request\n");
1138c2ecf20Sopenharmony_ci			align = 0;
1148c2ecf20Sopenharmony_ci		} else
1158c2ecf20Sopenharmony_ci			while (align && (align < num))
1168c2ecf20Sopenharmony_ci				align <<= 1;
1178c2ecf20Sopenharmony_ci	}
1188c2ecf20Sopenharmony_ci	if (base & ~(align-1)) {
1198c2ecf20Sopenharmony_ci		dev_dbg(&s->dev, "odd IO request\n");
1208c2ecf20Sopenharmony_ci		align = 0;
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	ret = s->resource_ops->find_io(s, res->flags, &base, num, align,
1248c2ecf20Sopenharmony_ci				&res->parent);
1258c2ecf20Sopenharmony_ci	if (ret) {
1268c2ecf20Sopenharmony_ci		dev_dbg(&s->dev, "alloc_io_space request failed (%d)\n", ret);
1278c2ecf20Sopenharmony_ci		return -EINVAL;
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	res->start = base;
1318c2ecf20Sopenharmony_ci	res->end = res->start + num - 1;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	if (res->parent) {
1348c2ecf20Sopenharmony_ci		ret = request_resource(res->parent, res);
1358c2ecf20Sopenharmony_ci		if (ret) {
1368c2ecf20Sopenharmony_ci			dev_warn(&s->dev,
1378c2ecf20Sopenharmony_ci				"request_resource %pR failed: %d\n", res, ret);
1388c2ecf20Sopenharmony_ci			res->parent = NULL;
1398c2ecf20Sopenharmony_ci			release_io_space(s, res);
1408c2ecf20Sopenharmony_ci		}
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci	dev_dbg(&s->dev, "alloc_io_space request result %d: %pR\n", ret, res);
1438c2ecf20Sopenharmony_ci	return ret;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci/**
1488c2ecf20Sopenharmony_ci * pcmcia_access_config() - read or write card configuration registers
1498c2ecf20Sopenharmony_ci *
1508c2ecf20Sopenharmony_ci * pcmcia_access_config() reads and writes configuration registers in
1518c2ecf20Sopenharmony_ci * attribute memory.  Memory window 0 is reserved for this and the tuple
1528c2ecf20Sopenharmony_ci * reading services. Drivers must use pcmcia_read_config_byte() or
1538c2ecf20Sopenharmony_ci * pcmcia_write_config_byte().
1548c2ecf20Sopenharmony_ci */
1558c2ecf20Sopenharmony_cistatic int pcmcia_access_config(struct pcmcia_device *p_dev,
1568c2ecf20Sopenharmony_ci				off_t where, u8 *val,
1578c2ecf20Sopenharmony_ci				int (*accessf) (struct pcmcia_socket *s,
1588c2ecf20Sopenharmony_ci						int attr, unsigned int addr,
1598c2ecf20Sopenharmony_ci						unsigned int len, void *ptr))
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	struct pcmcia_socket *s;
1628c2ecf20Sopenharmony_ci	config_t *c;
1638c2ecf20Sopenharmony_ci	int addr;
1648c2ecf20Sopenharmony_ci	int ret = 0;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	s = p_dev->socket;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	mutex_lock(&s->ops_mutex);
1698c2ecf20Sopenharmony_ci	c = p_dev->function_config;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	if (!(c->state & CONFIG_LOCKED)) {
1728c2ecf20Sopenharmony_ci		dev_dbg(&p_dev->dev, "Configuration isn't locked\n");
1738c2ecf20Sopenharmony_ci		mutex_unlock(&s->ops_mutex);
1748c2ecf20Sopenharmony_ci		return -EACCES;
1758c2ecf20Sopenharmony_ci	}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	addr = (p_dev->config_base + where) >> 1;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	ret = accessf(s, 1, addr, 1, val);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	return ret;
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci/**
1888c2ecf20Sopenharmony_ci * pcmcia_read_config_byte() - read a byte from a card configuration register
1898c2ecf20Sopenharmony_ci *
1908c2ecf20Sopenharmony_ci * pcmcia_read_config_byte() reads a byte from a configuration register in
1918c2ecf20Sopenharmony_ci * attribute memory.
1928c2ecf20Sopenharmony_ci */
1938c2ecf20Sopenharmony_ciint pcmcia_read_config_byte(struct pcmcia_device *p_dev, off_t where, u8 *val)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	return pcmcia_access_config(p_dev, where, val, pcmcia_read_cis_mem);
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcmcia_read_config_byte);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci/**
2018c2ecf20Sopenharmony_ci * pcmcia_write_config_byte() - write a byte to a card configuration register
2028c2ecf20Sopenharmony_ci *
2038c2ecf20Sopenharmony_ci * pcmcia_write_config_byte() writes a byte to a configuration register in
2048c2ecf20Sopenharmony_ci * attribute memory.
2058c2ecf20Sopenharmony_ci */
2068c2ecf20Sopenharmony_ciint pcmcia_write_config_byte(struct pcmcia_device *p_dev, off_t where, u8 val)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	return pcmcia_access_config(p_dev, where, &val, pcmcia_write_cis_mem);
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcmcia_write_config_byte);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci/**
2148c2ecf20Sopenharmony_ci * pcmcia_map_mem_page() - modify iomem window to point to a different offset
2158c2ecf20Sopenharmony_ci * @p_dev: pcmcia device
2168c2ecf20Sopenharmony_ci * @res: iomem resource already enabled by pcmcia_request_window()
2178c2ecf20Sopenharmony_ci * @offset: card_offset to map
2188c2ecf20Sopenharmony_ci *
2198c2ecf20Sopenharmony_ci * pcmcia_map_mem_page() modifies what can be read and written by accessing
2208c2ecf20Sopenharmony_ci * an iomem range previously enabled by pcmcia_request_window(), by setting
2218c2ecf20Sopenharmony_ci * the card_offset value to @offset.
2228c2ecf20Sopenharmony_ci */
2238c2ecf20Sopenharmony_ciint pcmcia_map_mem_page(struct pcmcia_device *p_dev, struct resource *res,
2248c2ecf20Sopenharmony_ci			unsigned int offset)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	struct pcmcia_socket *s = p_dev->socket;
2278c2ecf20Sopenharmony_ci	unsigned int w;
2288c2ecf20Sopenharmony_ci	int ret;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	w = ((res->flags & IORESOURCE_BITS & WIN_FLAGS_REQ) >> 2) - 1;
2318c2ecf20Sopenharmony_ci	if (w >= MAX_WIN)
2328c2ecf20Sopenharmony_ci		return -EINVAL;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	mutex_lock(&s->ops_mutex);
2358c2ecf20Sopenharmony_ci	s->win[w].card_start = offset;
2368c2ecf20Sopenharmony_ci	ret = s->ops->set_mem_map(s, &s->win[w]);
2378c2ecf20Sopenharmony_ci	if (ret)
2388c2ecf20Sopenharmony_ci		dev_warn(&p_dev->dev, "failed to set_mem_map\n");
2398c2ecf20Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
2408c2ecf20Sopenharmony_ci	return ret;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcmcia_map_mem_page);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci/**
2468c2ecf20Sopenharmony_ci * pcmcia_fixup_iowidth() - reduce io width to 8bit
2478c2ecf20Sopenharmony_ci * @p_dev: pcmcia device
2488c2ecf20Sopenharmony_ci *
2498c2ecf20Sopenharmony_ci * pcmcia_fixup_iowidth() allows a PCMCIA device driver to reduce the
2508c2ecf20Sopenharmony_ci * IO width to 8bit after having called pcmcia_enable_device()
2518c2ecf20Sopenharmony_ci * previously.
2528c2ecf20Sopenharmony_ci */
2538c2ecf20Sopenharmony_ciint pcmcia_fixup_iowidth(struct pcmcia_device *p_dev)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	struct pcmcia_socket *s = p_dev->socket;
2568c2ecf20Sopenharmony_ci	pccard_io_map io_off = { 0, 0, 0, 0, 1 };
2578c2ecf20Sopenharmony_ci	pccard_io_map io_on;
2588c2ecf20Sopenharmony_ci	int i, ret = 0;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	mutex_lock(&s->ops_mutex);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	dev_dbg(&p_dev->dev, "fixup iowidth to 8bit\n");
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	if (!(s->state & SOCKET_PRESENT) ||
2658c2ecf20Sopenharmony_ci		!(p_dev->function_config->state & CONFIG_LOCKED)) {
2668c2ecf20Sopenharmony_ci		dev_dbg(&p_dev->dev, "No card? Config not locked?\n");
2678c2ecf20Sopenharmony_ci		ret = -EACCES;
2688c2ecf20Sopenharmony_ci		goto unlock;
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	io_on.speed = io_speed;
2728c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_IO_WIN; i++) {
2738c2ecf20Sopenharmony_ci		if (!s->io[i].res)
2748c2ecf20Sopenharmony_ci			continue;
2758c2ecf20Sopenharmony_ci		io_off.map = i;
2768c2ecf20Sopenharmony_ci		io_on.map = i;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci		io_on.flags = MAP_ACTIVE | IO_DATA_PATH_WIDTH_8;
2798c2ecf20Sopenharmony_ci		io_on.start = s->io[i].res->start;
2808c2ecf20Sopenharmony_ci		io_on.stop = s->io[i].res->end;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci		s->ops->set_io_map(s, &io_off);
2838c2ecf20Sopenharmony_ci		msleep(40);
2848c2ecf20Sopenharmony_ci		s->ops->set_io_map(s, &io_on);
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ciunlock:
2878c2ecf20Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	return ret;
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcmcia_fixup_iowidth);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci/**
2958c2ecf20Sopenharmony_ci * pcmcia_fixup_vpp() - set Vpp to a new voltage level
2968c2ecf20Sopenharmony_ci * @p_dev: pcmcia device
2978c2ecf20Sopenharmony_ci * @new_vpp: new Vpp voltage
2988c2ecf20Sopenharmony_ci *
2998c2ecf20Sopenharmony_ci * pcmcia_fixup_vpp() allows a PCMCIA device driver to set Vpp to
3008c2ecf20Sopenharmony_ci * a new voltage level between calls to pcmcia_enable_device()
3018c2ecf20Sopenharmony_ci * and pcmcia_disable_device().
3028c2ecf20Sopenharmony_ci */
3038c2ecf20Sopenharmony_ciint pcmcia_fixup_vpp(struct pcmcia_device *p_dev, unsigned char new_vpp)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	struct pcmcia_socket *s = p_dev->socket;
3068c2ecf20Sopenharmony_ci	int ret = 0;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	mutex_lock(&s->ops_mutex);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	dev_dbg(&p_dev->dev, "fixup Vpp to %d\n", new_vpp);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	if (!(s->state & SOCKET_PRESENT) ||
3138c2ecf20Sopenharmony_ci		!(p_dev->function_config->state & CONFIG_LOCKED)) {
3148c2ecf20Sopenharmony_ci		dev_dbg(&p_dev->dev, "No card? Config not locked?\n");
3158c2ecf20Sopenharmony_ci		ret = -EACCES;
3168c2ecf20Sopenharmony_ci		goto unlock;
3178c2ecf20Sopenharmony_ci	}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	s->socket.Vpp = new_vpp;
3208c2ecf20Sopenharmony_ci	if (s->ops->set_socket(s, &s->socket)) {
3218c2ecf20Sopenharmony_ci		dev_warn(&p_dev->dev, "Unable to set VPP\n");
3228c2ecf20Sopenharmony_ci		ret = -EIO;
3238c2ecf20Sopenharmony_ci		goto unlock;
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci	p_dev->vpp = new_vpp;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ciunlock:
3288c2ecf20Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	return ret;
3318c2ecf20Sopenharmony_ci}
3328c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcmcia_fixup_vpp);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci/**
3368c2ecf20Sopenharmony_ci * pcmcia_release_configuration() - physically disable a PCMCIA device
3378c2ecf20Sopenharmony_ci * @p_dev: pcmcia device
3388c2ecf20Sopenharmony_ci *
3398c2ecf20Sopenharmony_ci * pcmcia_release_configuration() is the 1:1 counterpart to
3408c2ecf20Sopenharmony_ci * pcmcia_enable_device(): If a PCMCIA device is no longer used by any
3418c2ecf20Sopenharmony_ci * driver, the Vpp voltage is set to 0, IRQs will no longer be generated,
3428c2ecf20Sopenharmony_ci * and I/O ranges will be disabled. As pcmcia_release_io() and
3438c2ecf20Sopenharmony_ci * pcmcia_release_window() still need to be called, device drivers are
3448c2ecf20Sopenharmony_ci * expected to call pcmcia_disable_device() instead.
3458c2ecf20Sopenharmony_ci */
3468c2ecf20Sopenharmony_ciint pcmcia_release_configuration(struct pcmcia_device *p_dev)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	pccard_io_map io = { 0, 0, 0, 0, 1 };
3498c2ecf20Sopenharmony_ci	struct pcmcia_socket *s = p_dev->socket;
3508c2ecf20Sopenharmony_ci	config_t *c;
3518c2ecf20Sopenharmony_ci	int i;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	mutex_lock(&s->ops_mutex);
3548c2ecf20Sopenharmony_ci	c = p_dev->function_config;
3558c2ecf20Sopenharmony_ci	if (p_dev->_locked) {
3568c2ecf20Sopenharmony_ci		p_dev->_locked = 0;
3578c2ecf20Sopenharmony_ci		if (--(s->lock_count) == 0) {
3588c2ecf20Sopenharmony_ci			s->socket.flags = SS_OUTPUT_ENA; /* Is this correct? */
3598c2ecf20Sopenharmony_ci			s->socket.Vpp = 0;
3608c2ecf20Sopenharmony_ci			s->socket.io_irq = 0;
3618c2ecf20Sopenharmony_ci			s->ops->set_socket(s, &s->socket);
3628c2ecf20Sopenharmony_ci		}
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci	if (c->state & CONFIG_LOCKED) {
3658c2ecf20Sopenharmony_ci		c->state &= ~CONFIG_LOCKED;
3668c2ecf20Sopenharmony_ci		if (c->state & CONFIG_IO_REQ)
3678c2ecf20Sopenharmony_ci			for (i = 0; i < MAX_IO_WIN; i++) {
3688c2ecf20Sopenharmony_ci				if (!s->io[i].res)
3698c2ecf20Sopenharmony_ci					continue;
3708c2ecf20Sopenharmony_ci				s->io[i].Config--;
3718c2ecf20Sopenharmony_ci				if (s->io[i].Config != 0)
3728c2ecf20Sopenharmony_ci					continue;
3738c2ecf20Sopenharmony_ci				io.map = i;
3748c2ecf20Sopenharmony_ci				s->ops->set_io_map(s, &io);
3758c2ecf20Sopenharmony_ci			}
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	return 0;
3808c2ecf20Sopenharmony_ci}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci/**
3848c2ecf20Sopenharmony_ci * pcmcia_release_io() - release I/O allocated by a PCMCIA device
3858c2ecf20Sopenharmony_ci * @p_dev: pcmcia device
3868c2ecf20Sopenharmony_ci *
3878c2ecf20Sopenharmony_ci * pcmcia_release_io() releases the I/O ranges allocated by a PCMCIA
3888c2ecf20Sopenharmony_ci * device.  This may be invoked some time after a card ejection has
3898c2ecf20Sopenharmony_ci * already dumped the actual socket configuration, so if the client is
3908c2ecf20Sopenharmony_ci * "stale", we don't bother checking the port ranges against the
3918c2ecf20Sopenharmony_ci * current socket values.
3928c2ecf20Sopenharmony_ci */
3938c2ecf20Sopenharmony_cistatic int pcmcia_release_io(struct pcmcia_device *p_dev)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	struct pcmcia_socket *s = p_dev->socket;
3968c2ecf20Sopenharmony_ci	int ret = -EINVAL;
3978c2ecf20Sopenharmony_ci	config_t *c;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	mutex_lock(&s->ops_mutex);
4008c2ecf20Sopenharmony_ci	if (!p_dev->_io)
4018c2ecf20Sopenharmony_ci		goto out;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	c = p_dev->function_config;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	release_io_space(s, &c->io[0]);
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	if (c->io[1].end)
4088c2ecf20Sopenharmony_ci		release_io_space(s, &c->io[1]);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	p_dev->_io = 0;
4118c2ecf20Sopenharmony_ci	c->state &= ~CONFIG_IO_REQ;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ciout:
4148c2ecf20Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	return ret;
4178c2ecf20Sopenharmony_ci} /* pcmcia_release_io */
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci/**
4218c2ecf20Sopenharmony_ci * pcmcia_release_window() - release reserved iomem for PCMCIA devices
4228c2ecf20Sopenharmony_ci * @p_dev: pcmcia device
4238c2ecf20Sopenharmony_ci * @res: iomem resource to release
4248c2ecf20Sopenharmony_ci *
4258c2ecf20Sopenharmony_ci * pcmcia_release_window() releases &struct resource *res which was
4268c2ecf20Sopenharmony_ci * previously reserved by calling pcmcia_request_window().
4278c2ecf20Sopenharmony_ci */
4288c2ecf20Sopenharmony_ciint pcmcia_release_window(struct pcmcia_device *p_dev, struct resource *res)
4298c2ecf20Sopenharmony_ci{
4308c2ecf20Sopenharmony_ci	struct pcmcia_socket *s = p_dev->socket;
4318c2ecf20Sopenharmony_ci	pccard_mem_map *win;
4328c2ecf20Sopenharmony_ci	unsigned int w;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	dev_dbg(&p_dev->dev, "releasing window %pR\n", res);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	w = ((res->flags & IORESOURCE_BITS & WIN_FLAGS_REQ) >> 2) - 1;
4378c2ecf20Sopenharmony_ci	if (w >= MAX_WIN)
4388c2ecf20Sopenharmony_ci		return -EINVAL;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	mutex_lock(&s->ops_mutex);
4418c2ecf20Sopenharmony_ci	win = &s->win[w];
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	if (!(p_dev->_win & CLIENT_WIN_REQ(w))) {
4448c2ecf20Sopenharmony_ci		dev_dbg(&p_dev->dev, "not releasing unknown window\n");
4458c2ecf20Sopenharmony_ci		mutex_unlock(&s->ops_mutex);
4468c2ecf20Sopenharmony_ci		return -EINVAL;
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	/* Shut down memory window */
4508c2ecf20Sopenharmony_ci	win->flags &= ~MAP_ACTIVE;
4518c2ecf20Sopenharmony_ci	s->ops->set_mem_map(s, win);
4528c2ecf20Sopenharmony_ci	s->state &= ~SOCKET_WIN_REQ(w);
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	/* Release system memory */
4558c2ecf20Sopenharmony_ci	if (win->res) {
4568c2ecf20Sopenharmony_ci		release_resource(res);
4578c2ecf20Sopenharmony_ci		release_resource(win->res);
4588c2ecf20Sopenharmony_ci		kfree(win->res);
4598c2ecf20Sopenharmony_ci		win->res = NULL;
4608c2ecf20Sopenharmony_ci	}
4618c2ecf20Sopenharmony_ci	res->start = res->end = 0;
4628c2ecf20Sopenharmony_ci	res->flags = IORESOURCE_MEM;
4638c2ecf20Sopenharmony_ci	p_dev->_win &= ~CLIENT_WIN_REQ(w);
4648c2ecf20Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	return 0;
4678c2ecf20Sopenharmony_ci} /* pcmcia_release_window */
4688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcmcia_release_window);
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci/**
4728c2ecf20Sopenharmony_ci * pcmcia_enable_device() - set up and activate a PCMCIA device
4738c2ecf20Sopenharmony_ci * @p_dev: the associated PCMCIA device
4748c2ecf20Sopenharmony_ci *
4758c2ecf20Sopenharmony_ci * pcmcia_enable_device() physically enables a PCMCIA device. It parses
4768c2ecf20Sopenharmony_ci * the flags passed to in @flags and stored in @p_dev->flags and sets up
4778c2ecf20Sopenharmony_ci * the Vpp voltage, enables the speaker line, I/O ports and store proper
4788c2ecf20Sopenharmony_ci * values to configuration registers.
4798c2ecf20Sopenharmony_ci */
4808c2ecf20Sopenharmony_ciint pcmcia_enable_device(struct pcmcia_device *p_dev)
4818c2ecf20Sopenharmony_ci{
4828c2ecf20Sopenharmony_ci	int i;
4838c2ecf20Sopenharmony_ci	unsigned int base;
4848c2ecf20Sopenharmony_ci	struct pcmcia_socket *s = p_dev->socket;
4858c2ecf20Sopenharmony_ci	config_t *c;
4868c2ecf20Sopenharmony_ci	pccard_io_map iomap;
4878c2ecf20Sopenharmony_ci	unsigned char status = 0;
4888c2ecf20Sopenharmony_ci	unsigned char ext_status = 0;
4898c2ecf20Sopenharmony_ci	unsigned char option = 0;
4908c2ecf20Sopenharmony_ci	unsigned int flags = p_dev->config_flags;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	if (!(s->state & SOCKET_PRESENT))
4938c2ecf20Sopenharmony_ci		return -ENODEV;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	mutex_lock(&s->ops_mutex);
4968c2ecf20Sopenharmony_ci	c = p_dev->function_config;
4978c2ecf20Sopenharmony_ci	if (c->state & CONFIG_LOCKED) {
4988c2ecf20Sopenharmony_ci		mutex_unlock(&s->ops_mutex);
4998c2ecf20Sopenharmony_ci		dev_dbg(&p_dev->dev, "Configuration is locked\n");
5008c2ecf20Sopenharmony_ci		return -EACCES;
5018c2ecf20Sopenharmony_ci	}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	/* Do power control.  We don't allow changes in Vcc. */
5048c2ecf20Sopenharmony_ci	s->socket.Vpp = p_dev->vpp;
5058c2ecf20Sopenharmony_ci	if (s->ops->set_socket(s, &s->socket)) {
5068c2ecf20Sopenharmony_ci		mutex_unlock(&s->ops_mutex);
5078c2ecf20Sopenharmony_ci		dev_warn(&p_dev->dev, "Unable to set socket state\n");
5088c2ecf20Sopenharmony_ci		return -EINVAL;
5098c2ecf20Sopenharmony_ci	}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	/* Pick memory or I/O card, DMA mode, interrupt */
5128c2ecf20Sopenharmony_ci	if (p_dev->_io || flags & CONF_ENABLE_IRQ)
5138c2ecf20Sopenharmony_ci		flags |= CONF_ENABLE_IOCARD;
5148c2ecf20Sopenharmony_ci	if (flags & CONF_ENABLE_IOCARD)
5158c2ecf20Sopenharmony_ci		s->socket.flags |= SS_IOCARD;
5168c2ecf20Sopenharmony_ci	if (flags & CONF_ENABLE_ZVCARD)
5178c2ecf20Sopenharmony_ci		s->socket.flags |= SS_ZVCARD | SS_IOCARD;
5188c2ecf20Sopenharmony_ci	if (flags & CONF_ENABLE_SPKR) {
5198c2ecf20Sopenharmony_ci		s->socket.flags |= SS_SPKR_ENA;
5208c2ecf20Sopenharmony_ci		status = CCSR_AUDIO_ENA;
5218c2ecf20Sopenharmony_ci		if (!(p_dev->config_regs & PRESENT_STATUS))
5228c2ecf20Sopenharmony_ci			dev_warn(&p_dev->dev, "speaker requested, but "
5238c2ecf20Sopenharmony_ci					      "PRESENT_STATUS not set!\n");
5248c2ecf20Sopenharmony_ci	}
5258c2ecf20Sopenharmony_ci	if (flags & CONF_ENABLE_IRQ)
5268c2ecf20Sopenharmony_ci		s->socket.io_irq = s->pcmcia_irq;
5278c2ecf20Sopenharmony_ci	else
5288c2ecf20Sopenharmony_ci		s->socket.io_irq = 0;
5298c2ecf20Sopenharmony_ci	if (flags & CONF_ENABLE_ESR) {
5308c2ecf20Sopenharmony_ci		p_dev->config_regs |= PRESENT_EXT_STATUS;
5318c2ecf20Sopenharmony_ci		ext_status = ESR_REQ_ATTN_ENA;
5328c2ecf20Sopenharmony_ci	}
5338c2ecf20Sopenharmony_ci	s->ops->set_socket(s, &s->socket);
5348c2ecf20Sopenharmony_ci	s->lock_count++;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	dev_dbg(&p_dev->dev,
5378c2ecf20Sopenharmony_ci		"enable_device: V %d, flags %x, base %x, regs %x, idx %x\n",
5388c2ecf20Sopenharmony_ci		p_dev->vpp, flags, p_dev->config_base, p_dev->config_regs,
5398c2ecf20Sopenharmony_ci		p_dev->config_index);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	/* Set up CIS configuration registers */
5428c2ecf20Sopenharmony_ci	base = p_dev->config_base;
5438c2ecf20Sopenharmony_ci	if (p_dev->config_regs & PRESENT_COPY) {
5448c2ecf20Sopenharmony_ci		u16 tmp = 0;
5458c2ecf20Sopenharmony_ci		dev_dbg(&p_dev->dev, "clearing CISREG_SCR\n");
5468c2ecf20Sopenharmony_ci		pcmcia_write_cis_mem(s, 1, (base + CISREG_SCR)>>1, 1, &tmp);
5478c2ecf20Sopenharmony_ci	}
5488c2ecf20Sopenharmony_ci	if (p_dev->config_regs & PRESENT_PIN_REPLACE) {
5498c2ecf20Sopenharmony_ci		u16 tmp = 0;
5508c2ecf20Sopenharmony_ci		dev_dbg(&p_dev->dev, "clearing CISREG_PRR\n");
5518c2ecf20Sopenharmony_ci		pcmcia_write_cis_mem(s, 1, (base + CISREG_PRR)>>1, 1, &tmp);
5528c2ecf20Sopenharmony_ci	}
5538c2ecf20Sopenharmony_ci	if (p_dev->config_regs & PRESENT_OPTION) {
5548c2ecf20Sopenharmony_ci		if (s->functions == 1) {
5558c2ecf20Sopenharmony_ci			option = p_dev->config_index & COR_CONFIG_MASK;
5568c2ecf20Sopenharmony_ci		} else {
5578c2ecf20Sopenharmony_ci			option = p_dev->config_index & COR_MFC_CONFIG_MASK;
5588c2ecf20Sopenharmony_ci			option |= COR_FUNC_ENA|COR_IREQ_ENA;
5598c2ecf20Sopenharmony_ci			if (p_dev->config_regs & PRESENT_IOBASE_0)
5608c2ecf20Sopenharmony_ci				option |= COR_ADDR_DECODE;
5618c2ecf20Sopenharmony_ci		}
5628c2ecf20Sopenharmony_ci		if ((flags & CONF_ENABLE_IRQ) &&
5638c2ecf20Sopenharmony_ci			!(flags & CONF_ENABLE_PULSE_IRQ))
5648c2ecf20Sopenharmony_ci			option |= COR_LEVEL_REQ;
5658c2ecf20Sopenharmony_ci		pcmcia_write_cis_mem(s, 1, (base + CISREG_COR)>>1, 1, &option);
5668c2ecf20Sopenharmony_ci		msleep(40);
5678c2ecf20Sopenharmony_ci	}
5688c2ecf20Sopenharmony_ci	if (p_dev->config_regs & PRESENT_STATUS)
5698c2ecf20Sopenharmony_ci		pcmcia_write_cis_mem(s, 1, (base + CISREG_CCSR)>>1, 1, &status);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	if (p_dev->config_regs & PRESENT_EXT_STATUS)
5728c2ecf20Sopenharmony_ci		pcmcia_write_cis_mem(s, 1, (base + CISREG_ESR)>>1, 1,
5738c2ecf20Sopenharmony_ci					&ext_status);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	if (p_dev->config_regs & PRESENT_IOBASE_0) {
5768c2ecf20Sopenharmony_ci		u8 b = c->io[0].start & 0xff;
5778c2ecf20Sopenharmony_ci		pcmcia_write_cis_mem(s, 1, (base + CISREG_IOBASE_0)>>1, 1, &b);
5788c2ecf20Sopenharmony_ci		b = (c->io[0].start >> 8) & 0xff;
5798c2ecf20Sopenharmony_ci		pcmcia_write_cis_mem(s, 1, (base + CISREG_IOBASE_1)>>1, 1, &b);
5808c2ecf20Sopenharmony_ci	}
5818c2ecf20Sopenharmony_ci	if (p_dev->config_regs & PRESENT_IOSIZE) {
5828c2ecf20Sopenharmony_ci		u8 b = resource_size(&c->io[0]) + resource_size(&c->io[1]) - 1;
5838c2ecf20Sopenharmony_ci		pcmcia_write_cis_mem(s, 1, (base + CISREG_IOSIZE)>>1, 1, &b);
5848c2ecf20Sopenharmony_ci	}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	/* Configure I/O windows */
5878c2ecf20Sopenharmony_ci	if (c->state & CONFIG_IO_REQ) {
5888c2ecf20Sopenharmony_ci		iomap.speed = io_speed;
5898c2ecf20Sopenharmony_ci		for (i = 0; i < MAX_IO_WIN; i++)
5908c2ecf20Sopenharmony_ci			if (s->io[i].res) {
5918c2ecf20Sopenharmony_ci				iomap.map = i;
5928c2ecf20Sopenharmony_ci				iomap.flags = MAP_ACTIVE;
5938c2ecf20Sopenharmony_ci				switch (s->io[i].res->flags & IO_DATA_PATH_WIDTH) {
5948c2ecf20Sopenharmony_ci				case IO_DATA_PATH_WIDTH_16:
5958c2ecf20Sopenharmony_ci					iomap.flags |= MAP_16BIT; break;
5968c2ecf20Sopenharmony_ci				case IO_DATA_PATH_WIDTH_AUTO:
5978c2ecf20Sopenharmony_ci					iomap.flags |= MAP_AUTOSZ; break;
5988c2ecf20Sopenharmony_ci				default:
5998c2ecf20Sopenharmony_ci					break;
6008c2ecf20Sopenharmony_ci				}
6018c2ecf20Sopenharmony_ci				iomap.start = s->io[i].res->start;
6028c2ecf20Sopenharmony_ci				iomap.stop = s->io[i].res->end;
6038c2ecf20Sopenharmony_ci				s->ops->set_io_map(s, &iomap);
6048c2ecf20Sopenharmony_ci				s->io[i].Config++;
6058c2ecf20Sopenharmony_ci			}
6068c2ecf20Sopenharmony_ci	}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	c->state |= CONFIG_LOCKED;
6098c2ecf20Sopenharmony_ci	p_dev->_locked = 1;
6108c2ecf20Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
6118c2ecf20Sopenharmony_ci	return 0;
6128c2ecf20Sopenharmony_ci} /* pcmcia_enable_device */
6138c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcmcia_enable_device);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci/**
6178c2ecf20Sopenharmony_ci * pcmcia_request_io() - attempt to reserve port ranges for PCMCIA devices
6188c2ecf20Sopenharmony_ci * @p_dev: the associated PCMCIA device
6198c2ecf20Sopenharmony_ci *
6208c2ecf20Sopenharmony_ci * pcmcia_request_io() attempts to reserve the IO port ranges specified in
6218c2ecf20Sopenharmony_ci * &struct pcmcia_device @p_dev->resource[0] and @p_dev->resource[1]. The
6228c2ecf20Sopenharmony_ci * "start" value is the requested start of the IO port resource; "end"
6238c2ecf20Sopenharmony_ci * reflects the number of ports requested. The number of IO lines requested
6248c2ecf20Sopenharmony_ci * is specified in &struct pcmcia_device @p_dev->io_lines.
6258c2ecf20Sopenharmony_ci */
6268c2ecf20Sopenharmony_ciint pcmcia_request_io(struct pcmcia_device *p_dev)
6278c2ecf20Sopenharmony_ci{
6288c2ecf20Sopenharmony_ci	struct pcmcia_socket *s = p_dev->socket;
6298c2ecf20Sopenharmony_ci	config_t *c = p_dev->function_config;
6308c2ecf20Sopenharmony_ci	int ret = -EINVAL;
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	mutex_lock(&s->ops_mutex);
6338c2ecf20Sopenharmony_ci	dev_dbg(&p_dev->dev, "pcmcia_request_io: %pR , %pR",
6348c2ecf20Sopenharmony_ci		&c->io[0], &c->io[1]);
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	if (!(s->state & SOCKET_PRESENT)) {
6378c2ecf20Sopenharmony_ci		dev_dbg(&p_dev->dev, "pcmcia_request_io: No card present\n");
6388c2ecf20Sopenharmony_ci		goto out;
6398c2ecf20Sopenharmony_ci	}
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	if (c->state & CONFIG_LOCKED) {
6428c2ecf20Sopenharmony_ci		dev_dbg(&p_dev->dev, "Configuration is locked\n");
6438c2ecf20Sopenharmony_ci		goto out;
6448c2ecf20Sopenharmony_ci	}
6458c2ecf20Sopenharmony_ci	if (c->state & CONFIG_IO_REQ) {
6468c2ecf20Sopenharmony_ci		dev_dbg(&p_dev->dev, "IO already configured\n");
6478c2ecf20Sopenharmony_ci		goto out;
6488c2ecf20Sopenharmony_ci	}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	ret = alloc_io_space(s, &c->io[0], p_dev->io_lines);
6518c2ecf20Sopenharmony_ci	if (ret)
6528c2ecf20Sopenharmony_ci		goto out;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	if (c->io[1].end) {
6558c2ecf20Sopenharmony_ci		ret = alloc_io_space(s, &c->io[1], p_dev->io_lines);
6568c2ecf20Sopenharmony_ci		if (ret) {
6578c2ecf20Sopenharmony_ci			struct resource tmp = c->io[0];
6588c2ecf20Sopenharmony_ci			/* release the previously allocated resource */
6598c2ecf20Sopenharmony_ci			release_io_space(s, &c->io[0]);
6608c2ecf20Sopenharmony_ci			/* but preserve the settings, for they worked... */
6618c2ecf20Sopenharmony_ci			c->io[0].end = resource_size(&tmp);
6628c2ecf20Sopenharmony_ci			c->io[0].start = tmp.start;
6638c2ecf20Sopenharmony_ci			c->io[0].flags = tmp.flags;
6648c2ecf20Sopenharmony_ci			goto out;
6658c2ecf20Sopenharmony_ci		}
6668c2ecf20Sopenharmony_ci	} else
6678c2ecf20Sopenharmony_ci		c->io[1].start = 0;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	c->state |= CONFIG_IO_REQ;
6708c2ecf20Sopenharmony_ci	p_dev->_io = 1;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	dev_dbg(&p_dev->dev, "pcmcia_request_io succeeded: %pR , %pR",
6738c2ecf20Sopenharmony_ci		&c->io[0], &c->io[1]);
6748c2ecf20Sopenharmony_ciout:
6758c2ecf20Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	return ret;
6788c2ecf20Sopenharmony_ci} /* pcmcia_request_io */
6798c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcmcia_request_io);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci/**
6838c2ecf20Sopenharmony_ci * pcmcia_request_irq() - attempt to request a IRQ for a PCMCIA device
6848c2ecf20Sopenharmony_ci * @p_dev: the associated PCMCIA device
6858c2ecf20Sopenharmony_ci * @handler: IRQ handler to register
6868c2ecf20Sopenharmony_ci *
6878c2ecf20Sopenharmony_ci * pcmcia_request_irq() is a wrapper around request_irq() which allows
6888c2ecf20Sopenharmony_ci * the PCMCIA core to clean up the registration in pcmcia_disable_device().
6898c2ecf20Sopenharmony_ci * Drivers are free to use request_irq() directly, but then they need to
6908c2ecf20Sopenharmony_ci * call free_irq() themselfves, too. Also, only %IRQF_SHARED capable IRQ
6918c2ecf20Sopenharmony_ci * handlers are allowed.
6928c2ecf20Sopenharmony_ci */
6938c2ecf20Sopenharmony_ciint __must_check pcmcia_request_irq(struct pcmcia_device *p_dev,
6948c2ecf20Sopenharmony_ci				    irq_handler_t handler)
6958c2ecf20Sopenharmony_ci{
6968c2ecf20Sopenharmony_ci	int ret;
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	if (!p_dev->irq)
6998c2ecf20Sopenharmony_ci		return -EINVAL;
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	ret = request_irq(p_dev->irq, handler, IRQF_SHARED,
7028c2ecf20Sopenharmony_ci			p_dev->devname, p_dev->priv);
7038c2ecf20Sopenharmony_ci	if (!ret)
7048c2ecf20Sopenharmony_ci		p_dev->_irq = 1;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	return ret;
7078c2ecf20Sopenharmony_ci}
7088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcmcia_request_irq);
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci#ifdef CONFIG_PCMCIA_PROBE
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci/* mask of IRQs already reserved by other cards, we should avoid using them */
7148c2ecf20Sopenharmony_cistatic u8 pcmcia_used_irq[32];
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_cistatic irqreturn_t test_action(int cpl, void *dev_id)
7178c2ecf20Sopenharmony_ci{
7188c2ecf20Sopenharmony_ci	return IRQ_NONE;
7198c2ecf20Sopenharmony_ci}
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci/**
7228c2ecf20Sopenharmony_ci * pcmcia_setup_isa_irq() - determine whether an ISA IRQ can be used
7238c2ecf20Sopenharmony_ci * @p_dev - the associated PCMCIA device
7248c2ecf20Sopenharmony_ci *
7258c2ecf20Sopenharmony_ci * locking note: must be called with ops_mutex locked.
7268c2ecf20Sopenharmony_ci */
7278c2ecf20Sopenharmony_cistatic int pcmcia_setup_isa_irq(struct pcmcia_device *p_dev, int type)
7288c2ecf20Sopenharmony_ci{
7298c2ecf20Sopenharmony_ci	struct pcmcia_socket *s = p_dev->socket;
7308c2ecf20Sopenharmony_ci	unsigned int try, irq;
7318c2ecf20Sopenharmony_ci	u32 mask = s->irq_mask;
7328c2ecf20Sopenharmony_ci	int ret = -ENODEV;
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	for (try = 0; try < 64; try++) {
7358c2ecf20Sopenharmony_ci		irq = try % 32;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci		if (irq > NR_IRQS)
7388c2ecf20Sopenharmony_ci			continue;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci		/* marked as available by driver, not blocked by userspace? */
7418c2ecf20Sopenharmony_ci		if (!((mask >> irq) & 1))
7428c2ecf20Sopenharmony_ci			continue;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci		/* avoid an IRQ which is already used by another PCMCIA card */
7458c2ecf20Sopenharmony_ci		if ((try < 32) && pcmcia_used_irq[irq])
7468c2ecf20Sopenharmony_ci			continue;
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci		/* register the correct driver, if possible, to check whether
7498c2ecf20Sopenharmony_ci		 * registering a dummy handle works, i.e. if the IRQ isn't
7508c2ecf20Sopenharmony_ci		 * marked as used by the kernel resource management core */
7518c2ecf20Sopenharmony_ci		ret = request_irq(irq, test_action, type, p_dev->devname,
7528c2ecf20Sopenharmony_ci				  p_dev);
7538c2ecf20Sopenharmony_ci		if (!ret) {
7548c2ecf20Sopenharmony_ci			free_irq(irq, p_dev);
7558c2ecf20Sopenharmony_ci			p_dev->irq = s->pcmcia_irq = irq;
7568c2ecf20Sopenharmony_ci			pcmcia_used_irq[irq]++;
7578c2ecf20Sopenharmony_ci			break;
7588c2ecf20Sopenharmony_ci		}
7598c2ecf20Sopenharmony_ci	}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	return ret;
7628c2ecf20Sopenharmony_ci}
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_civoid pcmcia_cleanup_irq(struct pcmcia_socket *s)
7658c2ecf20Sopenharmony_ci{
7668c2ecf20Sopenharmony_ci	pcmcia_used_irq[s->pcmcia_irq]--;
7678c2ecf20Sopenharmony_ci	s->pcmcia_irq = 0;
7688c2ecf20Sopenharmony_ci}
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci#else /* CONFIG_PCMCIA_PROBE */
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_cistatic int pcmcia_setup_isa_irq(struct pcmcia_device *p_dev, int type)
7738c2ecf20Sopenharmony_ci{
7748c2ecf20Sopenharmony_ci	return -EINVAL;
7758c2ecf20Sopenharmony_ci}
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_civoid pcmcia_cleanup_irq(struct pcmcia_socket *s)
7788c2ecf20Sopenharmony_ci{
7798c2ecf20Sopenharmony_ci	s->pcmcia_irq = 0;
7808c2ecf20Sopenharmony_ci	return;
7818c2ecf20Sopenharmony_ci}
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci#endif  /* CONFIG_PCMCIA_PROBE */
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci/**
7878c2ecf20Sopenharmony_ci * pcmcia_setup_irq() - determine IRQ to be used for device
7888c2ecf20Sopenharmony_ci * @p_dev - the associated PCMCIA device
7898c2ecf20Sopenharmony_ci *
7908c2ecf20Sopenharmony_ci * locking note: must be called with ops_mutex locked.
7918c2ecf20Sopenharmony_ci */
7928c2ecf20Sopenharmony_ciint pcmcia_setup_irq(struct pcmcia_device *p_dev)
7938c2ecf20Sopenharmony_ci{
7948c2ecf20Sopenharmony_ci	struct pcmcia_socket *s = p_dev->socket;
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	if (p_dev->irq)
7978c2ecf20Sopenharmony_ci		return 0;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	/* already assigned? */
8008c2ecf20Sopenharmony_ci	if (s->pcmcia_irq) {
8018c2ecf20Sopenharmony_ci		p_dev->irq = s->pcmcia_irq;
8028c2ecf20Sopenharmony_ci		return 0;
8038c2ecf20Sopenharmony_ci	}
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	/* prefer an exclusive ISA irq */
8068c2ecf20Sopenharmony_ci	if (!pcmcia_setup_isa_irq(p_dev, 0))
8078c2ecf20Sopenharmony_ci		return 0;
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	/* but accept a shared ISA irq */
8108c2ecf20Sopenharmony_ci	if (!pcmcia_setup_isa_irq(p_dev, IRQF_SHARED))
8118c2ecf20Sopenharmony_ci		return 0;
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	/* but use the PCI irq otherwise */
8148c2ecf20Sopenharmony_ci	if (s->pci_irq) {
8158c2ecf20Sopenharmony_ci		p_dev->irq = s->pcmcia_irq = s->pci_irq;
8168c2ecf20Sopenharmony_ci		return 0;
8178c2ecf20Sopenharmony_ci	}
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	return -EINVAL;
8208c2ecf20Sopenharmony_ci}
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci/**
8248c2ecf20Sopenharmony_ci * pcmcia_request_window() - attempt to reserve iomem for PCMCIA devices
8258c2ecf20Sopenharmony_ci * @p_dev: the associated PCMCIA device
8268c2ecf20Sopenharmony_ci * @res: &struct resource pointing to p_dev->resource[2..5]
8278c2ecf20Sopenharmony_ci * @speed: access speed
8288c2ecf20Sopenharmony_ci *
8298c2ecf20Sopenharmony_ci * pcmcia_request_window() attepts to reserve an iomem ranges specified in
8308c2ecf20Sopenharmony_ci * &struct resource @res pointing to one of the entries in
8318c2ecf20Sopenharmony_ci * &struct pcmcia_device @p_dev->resource[2..5]. The "start" value is the
8328c2ecf20Sopenharmony_ci * requested start of the IO mem resource; "end" reflects the size
8338c2ecf20Sopenharmony_ci * requested.
8348c2ecf20Sopenharmony_ci */
8358c2ecf20Sopenharmony_ciint pcmcia_request_window(struct pcmcia_device *p_dev, struct resource *res,
8368c2ecf20Sopenharmony_ci			unsigned int speed)
8378c2ecf20Sopenharmony_ci{
8388c2ecf20Sopenharmony_ci	struct pcmcia_socket *s = p_dev->socket;
8398c2ecf20Sopenharmony_ci	pccard_mem_map *win;
8408c2ecf20Sopenharmony_ci	u_long align;
8418c2ecf20Sopenharmony_ci	int w;
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	dev_dbg(&p_dev->dev, "request_window %pR %d\n", res, speed);
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	if (!(s->state & SOCKET_PRESENT)) {
8468c2ecf20Sopenharmony_ci		dev_dbg(&p_dev->dev, "No card present\n");
8478c2ecf20Sopenharmony_ci		return -ENODEV;
8488c2ecf20Sopenharmony_ci	}
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	/* Window size defaults to smallest available */
8518c2ecf20Sopenharmony_ci	if (res->end == 0)
8528c2ecf20Sopenharmony_ci		res->end = s->map_size;
8538c2ecf20Sopenharmony_ci	align = (s->features & SS_CAP_MEM_ALIGN) ? res->end : s->map_size;
8548c2ecf20Sopenharmony_ci	if (res->end & (s->map_size-1)) {
8558c2ecf20Sopenharmony_ci		dev_dbg(&p_dev->dev, "invalid map size\n");
8568c2ecf20Sopenharmony_ci		return -EINVAL;
8578c2ecf20Sopenharmony_ci	}
8588c2ecf20Sopenharmony_ci	if ((res->start && (s->features & SS_CAP_STATIC_MAP)) ||
8598c2ecf20Sopenharmony_ci	    (res->start & (align-1))) {
8608c2ecf20Sopenharmony_ci		dev_dbg(&p_dev->dev, "invalid base address\n");
8618c2ecf20Sopenharmony_ci		return -EINVAL;
8628c2ecf20Sopenharmony_ci	}
8638c2ecf20Sopenharmony_ci	if (res->start)
8648c2ecf20Sopenharmony_ci		align = 0;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	/* Allocate system memory window */
8678c2ecf20Sopenharmony_ci	mutex_lock(&s->ops_mutex);
8688c2ecf20Sopenharmony_ci	for (w = 0; w < MAX_WIN; w++)
8698c2ecf20Sopenharmony_ci		if (!(s->state & SOCKET_WIN_REQ(w)))
8708c2ecf20Sopenharmony_ci			break;
8718c2ecf20Sopenharmony_ci	if (w == MAX_WIN) {
8728c2ecf20Sopenharmony_ci		dev_dbg(&p_dev->dev, "all windows are used already\n");
8738c2ecf20Sopenharmony_ci		mutex_unlock(&s->ops_mutex);
8748c2ecf20Sopenharmony_ci		return -EINVAL;
8758c2ecf20Sopenharmony_ci	}
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	win = &s->win[w];
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	if (!(s->features & SS_CAP_STATIC_MAP)) {
8808c2ecf20Sopenharmony_ci		win->res = pcmcia_find_mem_region(res->start, res->end, align,
8818c2ecf20Sopenharmony_ci						0, s);
8828c2ecf20Sopenharmony_ci		if (!win->res) {
8838c2ecf20Sopenharmony_ci			dev_dbg(&p_dev->dev, "allocating mem region failed\n");
8848c2ecf20Sopenharmony_ci			mutex_unlock(&s->ops_mutex);
8858c2ecf20Sopenharmony_ci			return -EINVAL;
8868c2ecf20Sopenharmony_ci		}
8878c2ecf20Sopenharmony_ci	}
8888c2ecf20Sopenharmony_ci	p_dev->_win |= CLIENT_WIN_REQ(w);
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	/* Configure the socket controller */
8918c2ecf20Sopenharmony_ci	win->map = w+1;
8928c2ecf20Sopenharmony_ci	win->flags = res->flags & WIN_FLAGS_MAP;
8938c2ecf20Sopenharmony_ci	win->speed = speed;
8948c2ecf20Sopenharmony_ci	win->card_start = 0;
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	if (s->ops->set_mem_map(s, win) != 0) {
8978c2ecf20Sopenharmony_ci		dev_dbg(&p_dev->dev, "failed to set memory mapping\n");
8988c2ecf20Sopenharmony_ci		mutex_unlock(&s->ops_mutex);
8998c2ecf20Sopenharmony_ci		return -EIO;
9008c2ecf20Sopenharmony_ci	}
9018c2ecf20Sopenharmony_ci	s->state |= SOCKET_WIN_REQ(w);
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	/* Return window handle */
9048c2ecf20Sopenharmony_ci	if (s->features & SS_CAP_STATIC_MAP)
9058c2ecf20Sopenharmony_ci		res->start = win->static_start;
9068c2ecf20Sopenharmony_ci	else
9078c2ecf20Sopenharmony_ci		res->start = win->res->start;
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	/* convert to new-style resources */
9108c2ecf20Sopenharmony_ci	res->end += res->start - 1;
9118c2ecf20Sopenharmony_ci	res->flags &= ~WIN_FLAGS_REQ;
9128c2ecf20Sopenharmony_ci	res->flags |= (win->map << 2) | IORESOURCE_MEM;
9138c2ecf20Sopenharmony_ci	res->parent = win->res;
9148c2ecf20Sopenharmony_ci	if (win->res)
9158c2ecf20Sopenharmony_ci		request_resource(&iomem_resource, res);
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	dev_dbg(&p_dev->dev, "request_window results in %pR\n", res);
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	mutex_unlock(&s->ops_mutex);
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	return 0;
9228c2ecf20Sopenharmony_ci} /* pcmcia_request_window */
9238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcmcia_request_window);
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci/**
9278c2ecf20Sopenharmony_ci * pcmcia_disable_device() - disable and clean up a PCMCIA device
9288c2ecf20Sopenharmony_ci * @p_dev: the associated PCMCIA device
9298c2ecf20Sopenharmony_ci *
9308c2ecf20Sopenharmony_ci * pcmcia_disable_device() is the driver-callable counterpart to
9318c2ecf20Sopenharmony_ci * pcmcia_enable_device(): If a PCMCIA device is no longer used,
9328c2ecf20Sopenharmony_ci * drivers are expected to clean up and disable the device by calling
9338c2ecf20Sopenharmony_ci * this function. Any I/O ranges (iomem and ioports) will be released,
9348c2ecf20Sopenharmony_ci * the Vpp voltage will be set to 0, and IRQs will no longer be
9358c2ecf20Sopenharmony_ci * generated -- at least if there is no other card function (of
9368c2ecf20Sopenharmony_ci * multifunction devices) being used.
9378c2ecf20Sopenharmony_ci */
9388c2ecf20Sopenharmony_civoid pcmcia_disable_device(struct pcmcia_device *p_dev)
9398c2ecf20Sopenharmony_ci{
9408c2ecf20Sopenharmony_ci	int i;
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	dev_dbg(&p_dev->dev, "disabling device\n");
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_WIN; i++) {
9458c2ecf20Sopenharmony_ci		struct resource *res = p_dev->resource[MAX_IO_WIN + i];
9468c2ecf20Sopenharmony_ci		if (res->flags & WIN_FLAGS_REQ)
9478c2ecf20Sopenharmony_ci			pcmcia_release_window(p_dev, res);
9488c2ecf20Sopenharmony_ci	}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	pcmcia_release_configuration(p_dev);
9518c2ecf20Sopenharmony_ci	pcmcia_release_io(p_dev);
9528c2ecf20Sopenharmony_ci	if (p_dev->_irq) {
9538c2ecf20Sopenharmony_ci		free_irq(p_dev->irq, p_dev->priv);
9548c2ecf20Sopenharmony_ci		p_dev->_irq = 0;
9558c2ecf20Sopenharmony_ci	}
9568c2ecf20Sopenharmony_ci}
9578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcmcia_disable_device);
958