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