pcmcia_resource.c
来自「linux 内核源代码」· C语言 代码 · 共 973 行 · 第 1/2 页
C
973 行
/* * PCMCIA 16-bit resource management functions * * The initial developer of the original code is David A. Hinds * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. * * Copyright (C) 1999 David A. Hinds * Copyright (C) 2004-2005 Dominik Brodowski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */#include <linux/module.h>#include <linux/kernel.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/pci.h>#include <linux/device.h>#define IN_CARD_SERVICES#include <pcmcia/cs_types.h>#include <pcmcia/ss.h>#include <pcmcia/cs.h>#include <pcmcia/bulkmem.h>#include <pcmcia/cistpl.h>#include <pcmcia/cisreg.h>#include <pcmcia/ds.h>#include "cs_internal.h"#include "ds_internal.h"/* Access speed for IO windows */static int io_speed = 0;module_param(io_speed, int, 0444);#ifdef CONFIG_PCMCIA_PROBE#include <asm/irq.h>/* mask of IRQs already reserved by other cards, we should avoid using them */static u8 pcmcia_used_irq[NR_IRQS];#endif#ifdef DEBUGextern int ds_pc_debug;#define ds_dbg(skt, lvl, fmt, arg...) do { \ if (ds_pc_debug >= lvl) \ printk(KERN_DEBUG "pcmcia_resource: %s: " fmt, \ cs_socket_name(skt) , ## arg); \} while (0)#else#define ds_dbg(lvl, fmt, arg...) do { } while (0)#endif/** alloc_io_space * * Special stuff for managing IO windows, because they are scarce */static int alloc_io_space(struct pcmcia_socket *s, u_int attr, ioaddr_t *base, ioaddr_t num, u_int lines){ int i; kio_addr_t try, align; align = (*base) ? (lines ? 1<<lines : 0) : 1; if (align && (align < num)) { if (*base) { ds_dbg(s, 0, "odd IO request: num %#x align %#lx\n", num, align); align = 0; } else while (align && (align < num)) align <<= 1; } if (*base & ~(align-1)) { ds_dbg(s, 0, "odd IO request: base %#x align %#lx\n", *base, align); align = 0; } if ((s->features & SS_CAP_STATIC_MAP) && s->io_offset) { *base = s->io_offset | (*base & 0x0fff); return 0; } /* Check for an already-allocated window that must conflict with * what was asked for. It is a hack because it does not catch all * potential conflicts, just the most obvious ones. */ for (i = 0; i < MAX_IO_WIN; i++) if ((s->io[i].res) && *base && ((s->io[i].res->start & (align-1)) == *base)) return 1; for (i = 0; i < MAX_IO_WIN; i++) { if (!s->io[i].res) { s->io[i].res = pcmcia_find_io_region(*base, num, align, s); if (s->io[i].res) { *base = s->io[i].res->start; s->io[i].res->flags = (s->io[i].res->flags & ~IORESOURCE_BITS) | (attr & IORESOURCE_BITS); s->io[i].InUse = num; break; } else return 1; } else if ((s->io[i].res->flags & IORESOURCE_BITS) != (attr & IORESOURCE_BITS)) continue; /* Try to extend top of window */ try = s->io[i].res->end + 1; if ((*base == 0) || (*base == try)) if (pcmcia_adjust_io_region(s->io[i].res, s->io[i].res->start, s->io[i].res->end + num, s) == 0) { *base = try; s->io[i].InUse += num; break; } /* Try to extend bottom of window */ try = s->io[i].res->start - num; if ((*base == 0) || (*base == try)) if (pcmcia_adjust_io_region(s->io[i].res, s->io[i].res->start - num, s->io[i].res->end, s) == 0) { *base = try; s->io[i].InUse += num; break; } } return (i == MAX_IO_WIN);} /* alloc_io_space */static void release_io_space(struct pcmcia_socket *s, ioaddr_t base, ioaddr_t num){ int i; for (i = 0; i < MAX_IO_WIN; i++) { if (!s->io[i].res) continue; if ((s->io[i].res->start <= base) && (s->io[i].res->end >= base+num-1)) { s->io[i].InUse -= num; /* Free the window if no one else is using it */ if (s->io[i].InUse == 0) { release_resource(s->io[i].res); kfree(s->io[i].res); s->io[i].res = NULL; } } }} /* release_io_space *//** pccard_access_configuration_register * * Access_configuration_register() reads and writes configuration * registers in attribute memory. Memory window 0 is reserved for * this and the tuple reading services. */int pcmcia_access_configuration_register(struct pcmcia_device *p_dev, conf_reg_t *reg){ struct pcmcia_socket *s; config_t *c; int addr; u_char val; if (!p_dev || !p_dev->function_config) return CS_NO_CARD; s = p_dev->socket; c = p_dev->function_config; if (!(c->state & CONFIG_LOCKED)) return CS_CONFIGURATION_LOCKED; addr = (c->ConfigBase + reg->Offset) >> 1; switch (reg->Action) { case CS_READ: pcmcia_read_cis_mem(s, 1, addr, 1, &val); reg->Value = val; break; case CS_WRITE: val = reg->Value; pcmcia_write_cis_mem(s, 1, addr, 1, &val); break; default: return CS_BAD_ARGS; break; } return CS_SUCCESS;} /* pcmcia_access_configuration_register */EXPORT_SYMBOL(pcmcia_access_configuration_register);int pccard_get_configuration_info(struct pcmcia_socket *s, struct pcmcia_device *p_dev, config_info_t *config){ config_t *c; if (!(s->state & SOCKET_PRESENT)) return CS_NO_CARD;#ifdef CONFIG_CARDBUS if (s->state & SOCKET_CARDBUS) { memset(config, 0, sizeof(config_info_t)); config->Vcc = s->socket.Vcc; config->Vpp1 = config->Vpp2 = s->socket.Vpp; config->Option = s->cb_dev->subordinate->number; if (s->state & SOCKET_CARDBUS_CONFIG) { config->Attributes = CONF_VALID_CLIENT; config->IntType = INT_CARDBUS; config->AssignedIRQ = s->irq.AssignedIRQ; if (config->AssignedIRQ) config->Attributes |= CONF_ENABLE_IRQ; if (s->io[0].res) { config->BasePort1 = s->io[0].res->start; config->NumPorts1 = s->io[0].res->end - config->BasePort1 + 1; } } return CS_SUCCESS; }#endif if (p_dev) { c = p_dev->function_config; config->Function = p_dev->func; } else { c = NULL; config->Function = 0; } if ((c == NULL) || !(c->state & CONFIG_LOCKED)) { config->Attributes = 0; config->Vcc = s->socket.Vcc; config->Vpp1 = config->Vpp2 = s->socket.Vpp; return CS_SUCCESS; } config->Attributes = c->Attributes | CONF_VALID_CLIENT; config->Vcc = s->socket.Vcc; config->Vpp1 = config->Vpp2 = s->socket.Vpp; config->IntType = c->IntType; config->ConfigBase = c->ConfigBase; config->Status = c->Status; config->Pin = c->Pin; config->Copy = c->Copy; config->Option = c->Option; config->ExtStatus = c->ExtStatus; config->Present = config->CardValues = c->CardValues; config->IRQAttributes = c->irq.Attributes; config->AssignedIRQ = s->irq.AssignedIRQ; config->BasePort1 = c->io.BasePort1; config->NumPorts1 = c->io.NumPorts1; config->Attributes1 = c->io.Attributes1; config->BasePort2 = c->io.BasePort2; config->NumPorts2 = c->io.NumPorts2; config->Attributes2 = c->io.Attributes2; config->IOAddrLines = c->io.IOAddrLines; return CS_SUCCESS;} /* pccard_get_configuration_info */int pcmcia_get_configuration_info(struct pcmcia_device *p_dev, config_info_t *config){ return pccard_get_configuration_info(p_dev->socket, p_dev, config);}EXPORT_SYMBOL(pcmcia_get_configuration_info);/** pcmcia_get_window */int pcmcia_get_window(struct pcmcia_socket *s, window_handle_t *handle, int idx, win_req_t *req){ window_t *win; int w; if (!s || !(s->state & SOCKET_PRESENT)) return CS_NO_CARD; for (w = idx; w < MAX_WIN; w++) if (s->state & SOCKET_WIN_REQ(w)) break; if (w == MAX_WIN) return CS_NO_MORE_ITEMS; win = &s->win[w]; req->Base = win->ctl.res->start; req->Size = win->ctl.res->end - win->ctl.res->start + 1; req->AccessSpeed = win->ctl.speed; req->Attributes = 0; if (win->ctl.flags & MAP_ATTRIB) req->Attributes |= WIN_MEMORY_TYPE_AM; if (win->ctl.flags & MAP_ACTIVE) req->Attributes |= WIN_ENABLE; if (win->ctl.flags & MAP_16BIT) req->Attributes |= WIN_DATA_WIDTH_16; if (win->ctl.flags & MAP_USE_WAIT) req->Attributes |= WIN_USE_WAIT; *handle = win; return CS_SUCCESS;} /* pcmcia_get_window */EXPORT_SYMBOL(pcmcia_get_window);/** pccard_get_status * * Get the current socket state bits. We don't support the latched * SocketState yet: I haven't seen any point for it. */int pccard_get_status(struct pcmcia_socket *s, struct pcmcia_device *p_dev, cs_status_t *status){ config_t *c; int val; s->ops->get_status(s, &val); status->CardState = status->SocketState = 0; status->CardState |= (val & SS_DETECT) ? CS_EVENT_CARD_DETECT : 0; status->CardState |= (val & SS_CARDBUS) ? CS_EVENT_CB_DETECT : 0; status->CardState |= (val & SS_3VCARD) ? CS_EVENT_3VCARD : 0; status->CardState |= (val & SS_XVCARD) ? CS_EVENT_XVCARD : 0; if (s->state & SOCKET_SUSPEND) status->CardState |= CS_EVENT_PM_SUSPEND; if (!(s->state & SOCKET_PRESENT)) return CS_NO_CARD; c = (p_dev) ? p_dev->function_config : NULL; if ((c != NULL) && (c->state & CONFIG_LOCKED) && (c->IntType & (INT_MEMORY_AND_IO | INT_ZOOMED_VIDEO))) { u_char reg; if (c->CardValues & PRESENT_PIN_REPLACE) { pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_PRR)>>1, 1, ®); status->CardState |= (reg & PRR_WP_STATUS) ? CS_EVENT_WRITE_PROTECT : 0; status->CardState |= (reg & PRR_READY_STATUS) ? CS_EVENT_READY_CHANGE : 0; status->CardState |= (reg & PRR_BVD2_STATUS) ? CS_EVENT_BATTERY_LOW : 0; status->CardState |= (reg & PRR_BVD1_STATUS) ? CS_EVENT_BATTERY_DEAD : 0; } else { /* No PRR? Then assume we're always ready */ status->CardState |= CS_EVENT_READY_CHANGE; } if (c->CardValues & PRESENT_EXT_STATUS) { pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_ESR)>>1, 1, ®); status->CardState |= (reg & ESR_REQ_ATTN) ? CS_EVENT_REQUEST_ATTENTION : 0; } return CS_SUCCESS; } status->CardState |= (val & SS_WRPROT) ? CS_EVENT_WRITE_PROTECT : 0; status->CardState |= (val & SS_BATDEAD) ? CS_EVENT_BATTERY_DEAD : 0; status->CardState |= (val & SS_BATWARN) ? CS_EVENT_BATTERY_LOW : 0; status->CardState |= (val & SS_READY) ? CS_EVENT_READY_CHANGE : 0; return CS_SUCCESS;} /* pccard_get_status */int pcmcia_get_status(struct pcmcia_device *p_dev, cs_status_t *status){ return pccard_get_status(p_dev->socket, p_dev, status);}EXPORT_SYMBOL(pcmcia_get_status);/** pcmcia_get_mem_page * * Change the card address of an already open memory window. */int pcmcia_get_mem_page(window_handle_t win, memreq_t *req){ if ((win == NULL) || (win->magic != WINDOW_MAGIC)) return CS_BAD_HANDLE; req->Page = 0; req->CardOffset = win->ctl.card_start; return CS_SUCCESS;} /* pcmcia_get_mem_page */EXPORT_SYMBOL(pcmcia_get_mem_page);int pcmcia_map_mem_page(window_handle_t win, memreq_t *req){ struct pcmcia_socket *s; if ((win == NULL) || (win->magic != WINDOW_MAGIC)) return CS_BAD_HANDLE; if (req->Page != 0) return CS_BAD_PAGE; s = win->sock; win->ctl.card_start = req->CardOffset; if (s->ops->set_mem_map(s, &win->ctl) != 0) return CS_BAD_OFFSET; return CS_SUCCESS;} /* pcmcia_map_mem_page */EXPORT_SYMBOL(pcmcia_map_mem_page);/** pcmcia_modify_configuration * * Modify a locked socket configuration */int pcmcia_modify_configuration(struct pcmcia_device *p_dev, modconf_t *mod){ struct pcmcia_socket *s; config_t *c; s = p_dev->socket; c = p_dev->function_config; if (!(s->state & SOCKET_PRESENT)) return CS_NO_CARD; if (!(c->state & CONFIG_LOCKED)) return CS_CONFIGURATION_LOCKED; if (mod->Attributes & CONF_IRQ_CHANGE_VALID) { if (mod->Attributes & CONF_ENABLE_IRQ) { c->Attributes |= CONF_ENABLE_IRQ; s->socket.io_irq = s->irq.AssignedIRQ; } else { c->Attributes &= ~CONF_ENABLE_IRQ; s->socket.io_irq = 0; } s->ops->set_socket(s, &s->socket); } if (mod->Attributes & CONF_VCC_CHANGE_VALID) return CS_BAD_VCC; /* We only allow changing Vpp1 and Vpp2 to the same value */ if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) && (mod->Attributes & CONF_VPP2_CHANGE_VALID)) { if (mod->Vpp1 != mod->Vpp2) return CS_BAD_VPP; s->socket.Vpp = mod->Vpp1; if (s->ops->set_socket(s, &s->socket)) return CS_BAD_VPP; } else if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) || (mod->Attributes & CONF_VPP2_CHANGE_VALID)) return CS_BAD_VPP; if (mod->Attributes & CONF_IO_CHANGE_WIDTH) { pccard_io_map io_off = { 0, 0, 0, 0, 1 }; pccard_io_map io_on; int i; io_on.speed = io_speed; for (i = 0; i < MAX_IO_WIN; i++) { if (!s->io[i].res) continue; io_off.map = i; io_on.map = i; io_on.flags = MAP_ACTIVE | IO_DATA_PATH_WIDTH_8; io_on.start = s->io[i].res->start; io_on.stop = s->io[i].res->end; s->ops->set_io_map(s, &io_off); mdelay(40); s->ops->set_io_map(s, &io_on); } } return CS_SUCCESS;} /* modify_configuration */EXPORT_SYMBOL(pcmcia_modify_configuration);int pcmcia_release_configuration(struct pcmcia_device *p_dev){ pccard_io_map io = { 0, 0, 0, 0, 1 }; struct pcmcia_socket *s = p_dev->socket;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?