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