Commit 1a8d4663 authored by Dominik Brodowski's avatar Dominik Brodowski Committed by Linus Torvalds

[PATCH] pcmcia: move pcmcia resource handling out of cs.c

Move the 16-bit PCMICA resource handling from pcmcia_core.o to pcmcia.o.
Signed-off-by: default avatarDominik Brodowski <linux@dominikbrodowski.net>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent e6ea0b9e
...@@ -10,7 +10,7 @@ pcmcia_core-y += cs.o cistpl.o rsrc_mgr.o socket_sysfs.o ...@@ -10,7 +10,7 @@ pcmcia_core-y += cs.o cistpl.o rsrc_mgr.o socket_sysfs.o
pcmcia_core-$(CONFIG_CARDBUS) += cardbus.o pcmcia_core-$(CONFIG_CARDBUS) += cardbus.o
obj-$(CONFIG_PCCARD) += pcmcia_core.o obj-$(CONFIG_PCCARD) += pcmcia_core.o
pcmcia-y += ds.o pcmcia_compat.o pcmcia-y += ds.o pcmcia_compat.o pcmcia_resource.o
pcmcia-$(CONFIG_PCMCIA_IOCTL) += pcmcia_ioctl.o pcmcia-$(CONFIG_PCMCIA_IOCTL) += pcmcia_ioctl.o
obj-$(CONFIG_PCMCIA) += pcmcia.o obj-$(CONFIG_PCMCIA) += pcmcia.o
......
...@@ -182,6 +182,8 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, ...@@ -182,6 +182,8 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
*(u_char *)(ptr+2), *(u_char *)(ptr+3)); *(u_char *)(ptr+2), *(u_char *)(ptr+3));
return 0; return 0;
} }
EXPORT_SYMBOL(pcmcia_read_cis_mem);
void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
u_int len, void *ptr) u_int len, void *ptr)
...@@ -239,6 +241,8 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, ...@@ -239,6 +241,8 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
} }
} }
} }
EXPORT_SYMBOL(pcmcia_write_cis_mem);
/*====================================================================== /*======================================================================
......
...@@ -89,9 +89,6 @@ INT_MODULE_PARM(unreset_limit, 30); /* unreset_check's */ ...@@ -89,9 +89,6 @@ INT_MODULE_PARM(unreset_limit, 30); /* unreset_check's */
/* Access speed for attribute memory windows */ /* Access speed for attribute memory windows */
INT_MODULE_PARM(cis_speed, 300); /* ns */ INT_MODULE_PARM(cis_speed, 300); /* ns */
/* Access speed for IO windows */
INT_MODULE_PARM(io_speed, 0); /* ns */
#ifdef DEBUG #ifdef DEBUG
static int pc_debug; static int pc_debug;
...@@ -117,11 +114,6 @@ EXPORT_SYMBOL(pcmcia_socket_list); ...@@ -117,11 +114,6 @@ EXPORT_SYMBOL(pcmcia_socket_list);
EXPORT_SYMBOL(pcmcia_socket_list_rwsem); EXPORT_SYMBOL(pcmcia_socket_list_rwsem);
#ifdef CONFIG_PCMCIA_PROBE
/* mask ofIRQs already reserved by other cards, we should avoid using them */
static u8 pcmcia_used_irq[NR_IRQS];
#endif
/*==================================================================== /*====================================================================
Low-level PC Card interface drivers need to register with Card Low-level PC Card interface drivers need to register with Card
...@@ -748,260 +740,6 @@ void pcmcia_parse_events(struct pcmcia_socket *s, u_int events) ...@@ -748,260 +740,6 @@ void pcmcia_parse_events(struct pcmcia_socket *s, u_int events)
} /* pcmcia_parse_events */ } /* pcmcia_parse_events */
/*======================================================================
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) {
cs_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)) {
cs_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].NumPorts != 0) &&
((s->io[i].BasePort & (align-1)) == *base))
return 1;
for (i = 0; i < MAX_IO_WIN; i++) {
if (s->io[i].NumPorts == 0) {
s->io[i].res = pcmcia_find_io_region(*base, num, align, s);
if (s->io[i].res) {
s->io[i].Attributes = attr;
s->io[i].BasePort = *base = s->io[i].res->start;
s->io[i].NumPorts = s->io[i].InUse = num;
break;
} else
return 1;
} else if (s->io[i].Attributes != attr)
continue;
/* Try to extend top of window */
try = s->io[i].BasePort + s->io[i].NumPorts;
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].NumPorts += num;
s->io[i].InUse += num;
break;
}
/* Try to extend bottom of window */
try = s->io[i].BasePort - 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) {
s->io[i].BasePort = *base = try;
s->io[i].NumPorts += num;
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].BasePort <= base) &&
(s->io[i].BasePort+s->io[i].NumPorts >= base+num)) {
s->io[i].InUse -= num;
/* Free the window if no one else is using it */
if (s->io[i].InUse == 0) {
s->io[i].NumPorts = 0;
release_resource(s->io[i].res);
kfree(s->io[i].res);
s->io[i].res = NULL;
}
}
}
}
/*======================================================================
Access_configuration_register() reads and writes configuration
registers in attribute memory. Memory window 0 is reserved for
this and the tuple reading services.
======================================================================*/
int pccard_access_configuration_register(struct pcmcia_socket *s,
unsigned int function,
conf_reg_t *reg)
{
config_t *c;
int addr;
u_char val;
if (!s || !s->config)
return CS_NO_CARD;
c = &s->config[function];
if (c == NULL)
return CS_NO_CARD;
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;
} /* access_configuration_register */
EXPORT_SYMBOL(pccard_access_configuration_register);
/*====================================================================*/
int pccard_get_configuration_info(struct pcmcia_socket *s,
unsigned int function,
config_info_t *config)
{
config_t *c;
if (!(s->state & SOCKET_PRESENT))
return CS_NO_CARD;
config->Function = function;
#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;
config->BasePort1 = s->io[0].BasePort;
config->NumPorts1 = s->io[0].NumPorts;
}
return CS_SUCCESS;
}
#endif
c = (s->config != NULL) ? &s->config[function] : NULL;
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;
}
/* !!! This is a hack !!! */
memcpy(&config->Attributes, &c->Attributes, sizeof(config_t));
config->Attributes |= CONF_VALID_CLIENT;
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;
} /* get_configuration_info */
EXPORT_SYMBOL(pccard_get_configuration_info);
/*======================================================================
Return information about this version of Card Services.
======================================================================*/
int pcmcia_get_card_services_info(servinfo_t *info)
{
unsigned int socket_count = 0;
struct list_head *tmp;
info->Signature[0] = 'C';
info->Signature[1] = 'S';
down_read(&pcmcia_socket_list_rwsem);
list_for_each(tmp, &pcmcia_socket_list)
socket_count++;
up_read(&pcmcia_socket_list_rwsem);
info->Count = socket_count;
info->Revision = CS_RELEASE_CODE;
info->CSLevel = 0x0210;
info->VendorString = (char *)release;
return CS_SUCCESS;
} /* get_card_services_info */
/*====================================================================*/
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;
} /* get_window */
EXPORT_SYMBOL(pcmcia_get_window);
/*===================================================================== /*=====================================================================
Return the PCI device associated with a card.. Return the PCI device associated with a card..
...@@ -1022,143 +760,6 @@ EXPORT_SYMBOL(pcmcia_lookup_bus); ...@@ -1022,143 +760,6 @@ EXPORT_SYMBOL(pcmcia_lookup_bus);
#endif #endif
/*======================================================================
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, unsigned int function, 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 = (s->config != NULL) ? &s->config[function] : NULL;
if ((c != NULL) && (c->state & CONFIG_LOCKED) &&
(c->IntType & (INT_MEMORY_AND_IO | INT_ZOOMED_VIDEO))) {
u_char reg;
if (c->Present & PRESENT_PIN_REPLACE) {
pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_PRR)>>1, 1, &reg);
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->Present & PRESENT_EXT_STATUS) {
pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_ESR)>>1, 1, &reg);
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;
} /* get_status */
EXPORT_SYMBOL(pccard_get_status);
/*======================================================================
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;
} /* 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;
} /* map_mem_page */
/*======================================================================
Modify a locked socket configuration
======================================================================*/
int pcmcia_modify_configuration(client_handle_t handle,
modconf_t *mod)
{
struct pcmcia_socket *s;
config_t *c;
if (CHECK_HANDLE(handle))
return CS_BAD_HANDLE;
s = SOCKET(handle); c = CONFIG(handle);
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;
c->Vpp1 = c->Vpp2 = 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;
return CS_SUCCESS;
} /* modify_configuration */
/* register pcmcia_callback */ /* register pcmcia_callback */
int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c) int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c)
...@@ -1188,530 +789,6 @@ int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c) ...@@ -1188,530 +789,6 @@ int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c)
} }
EXPORT_SYMBOL(pccard_register_pcmcia); EXPORT_SYMBOL(pccard_register_pcmcia);
/*====================================================================*/
int pcmcia_release_configuration(client_handle_t handle)
{
pccard_io_map io = { 0, 0, 0, 0, 1 };
struct pcmcia_socket *s;
int i;
if (CHECK_HANDLE(handle) ||
!(handle->state & CLIENT_CONFIG_LOCKED))
return CS_BAD_HANDLE;
handle->state &= ~CLIENT_CONFIG_LOCKED;
s = SOCKET(handle);
#ifdef CONFIG_CARDBUS
if (handle->state & CLIENT_CARDBUS)
return CS_SUCCESS;
#endif
if (!(handle->state & CLIENT_STALE)) {
config_t *c = CONFIG(handle);
if (--(s->lock_count) == 0) {
s->socket.flags = SS_OUTPUT_ENA; /* Is this correct? */
s->socket.Vpp = 0;
s->socket.io_irq = 0;
s->ops->set_socket(s, &s->socket);
}
if (c->state & CONFIG_IO_REQ)
for (i = 0; i < MAX_IO_WIN; i++) {
if (s->io[i].NumPorts == 0)
continue;
s->io[i].Config--;
if (s->io[i].Config != 0)
continue;
io.map = i;
s->ops->set_io_map(s, &io);
}
c->state &= ~CONFIG_LOCKED;
}
return CS_SUCCESS;
} /* release_configuration */
/*======================================================================
Release_io() releases the I/O ranges allocated by a client. This
may be invoked some time after a card ejection has already dumped
the actual socket configuration, so if the client is "stale", we
don't bother checking the port ranges against the current socket
values.
======================================================================*/
int pcmcia_release_io(client_handle_t handle, io_req_t *req)
{
struct pcmcia_socket *s;
if (CHECK_HANDLE(handle) || !(handle->state & CLIENT_IO_REQ))
return CS_BAD_HANDLE;
handle->state &= ~CLIENT_IO_REQ;
s = SOCKET(handle);
#ifdef CONFIG_CARDBUS
if (handle->state & CLIENT_CARDBUS)
return CS_SUCCESS;
#endif
if (!(handle->state & CLIENT_STALE)) {
config_t *c = CONFIG(handle);
if (c->state & CONFIG_LOCKED)
return CS_CONFIGURATION_LOCKED;
if ((c->io.BasePort1 != req->BasePort1) ||
(c->io.NumPorts1 != req->NumPorts1) ||
(c->io.BasePort2 != req->BasePort2) ||
(c->io.NumPorts2 != req->NumPorts2))
return CS_BAD_ARGS;
c->state &= ~CONFIG_IO_REQ;
}
release_io_space(s, req->BasePort1, req->NumPorts1);
if (req->NumPorts2)
release_io_space(s, req->BasePort2, req->NumPorts2);
return CS_SUCCESS;
} /* release_io */
/*====================================================================*/
int pcmcia_release_irq(client_handle_t handle, irq_req_t *req)
{
struct pcmcia_socket *s;
if (CHECK_HANDLE(handle) || !(handle->state & CLIENT_IRQ_REQ))
return CS_BAD_HANDLE;
handle->state &= ~CLIENT_IRQ_REQ;
s = SOCKET(handle);
if (!(handle->state & CLIENT_STALE)) {
config_t *c = CONFIG(handle);
if (c->state & CONFIG_LOCKED)
return CS_CONFIGURATION_LOCKED;
if (c->irq.Attributes != req->Attributes)
return CS_BAD_ATTRIBUTE;
if (s->irq.AssignedIRQ != req->AssignedIRQ)
return CS_BAD_IRQ;
if (--s->irq.Config == 0) {
c->state &= ~CONFIG_IRQ_REQ;
s->irq.AssignedIRQ = 0;
}
}
if (req->Attributes & IRQ_HANDLE_PRESENT) {
free_irq(req->AssignedIRQ, req->Instance);
}
#ifdef CONFIG_PCMCIA_PROBE
pcmcia_used_irq[req->AssignedIRQ]--;
#endif
return CS_SUCCESS;
} /* cs_release_irq */
/*====================================================================*/
int pcmcia_release_window(window_handle_t win)
{
struct pcmcia_socket *s;
if ((win == NULL) || (win->magic != WINDOW_MAGIC))
return CS_BAD_HANDLE;
s = win->sock;
if (!(win->handle->state & CLIENT_WIN_REQ(win->index)))
return CS_BAD_HANDLE;
/* Shut down memory window */
win->ctl.flags &= ~MAP_ACTIVE;
s->ops->set_mem_map(s, &win->ctl);
s->state &= ~SOCKET_WIN_REQ(win->index);
/* Release system memory */
if (win->ctl.res) {
release_resource(win->ctl.res);
kfree(win->ctl.res);
win->ctl.res = NULL;
}
win->handle->state &= ~CLIENT_WIN_REQ(win->index);
win->magic = 0;
return CS_SUCCESS;
} /* release_window */
/*====================================================================*/
int pcmcia_request_configuration(client_handle_t handle,
config_req_t *req)
{
int i;
u_int base;
struct pcmcia_socket *s;
config_t *c;
pccard_io_map iomap;
if (CHECK_HANDLE(handle))
return CS_BAD_HANDLE;
s = SOCKET(handle);
if (!(s->state & SOCKET_PRESENT))
return CS_NO_CARD;
#ifdef CONFIG_CARDBUS
if (handle->state & CLIENT_CARDBUS)
return CS_UNSUPPORTED_MODE;
#endif
if (req->IntType & INT_CARDBUS)
return CS_UNSUPPORTED_MODE;
c = CONFIG(handle);
if (c->state & CONFIG_LOCKED)
return CS_CONFIGURATION_LOCKED;
/* Do power control. We don't allow changes in Vcc. */
if (s->socket.Vcc != req->Vcc)
return CS_BAD_VCC;
if (req->Vpp1 != req->Vpp2)
return CS_BAD_VPP;
s->socket.Vpp = req->Vpp1;
if (s->ops->set_socket(s, &s->socket))
return CS_BAD_VPP;
c->Vcc = req->Vcc; c->Vpp1 = c->Vpp2 = req->Vpp1;
/* Pick memory or I/O card, DMA mode, interrupt */
c->IntType = req->IntType;
c->Attributes = req->Attributes;
if (req->IntType & INT_MEMORY_AND_IO)
s->socket.flags |= SS_IOCARD;
if (req->IntType & INT_ZOOMED_VIDEO)
s->socket.flags |= SS_ZVCARD | SS_IOCARD;
if (req->Attributes & CONF_ENABLE_DMA)
s->socket.flags |= SS_DMA_MODE;
if (req->Attributes & CONF_ENABLE_SPKR)
s->socket.flags |= SS_SPKR_ENA;
if (req->Attributes & CONF_ENABLE_IRQ)
s->socket.io_irq = s->irq.AssignedIRQ;
else
s->socket.io_irq = 0;
s->ops->set_socket(s, &s->socket);
s->lock_count++;
/* Set up CIS configuration registers */
base = c->ConfigBase = req->ConfigBase;
c->Present = c->CardValues = req->Present;
if (req->Present & PRESENT_COPY) {
c->Copy = req->Copy;
pcmcia_write_cis_mem(s, 1, (base + CISREG_SCR)>>1, 1, &c->Copy);
}
if (req->Present & PRESENT_OPTION) {
if (s->functions == 1) {
c->Option = req->ConfigIndex & COR_CONFIG_MASK;
} else {
c->Option = req->ConfigIndex & COR_MFC_CONFIG_MASK;
c->Option |= COR_FUNC_ENA|COR_IREQ_ENA;
if (req->Present & PRESENT_IOBASE_0)
c->Option |= COR_ADDR_DECODE;
}
if (c->state & CONFIG_IRQ_REQ)
if (!(c->irq.Attributes & IRQ_FORCED_PULSE))
c->Option |= COR_LEVEL_REQ;
pcmcia_write_cis_mem(s, 1, (base + CISREG_COR)>>1, 1, &c->Option);
mdelay(40);
}
if (req->Present & PRESENT_STATUS) {
c->Status = req->Status;
pcmcia_write_cis_mem(s, 1, (base + CISREG_CCSR)>>1, 1, &c->Status);
}
if (req->Present & PRESENT_PIN_REPLACE) {
c->Pin = req->Pin;
pcmcia_write_cis_mem(s, 1, (base + CISREG_PRR)>>1, 1, &c->Pin);
}
if (req->Present & PRESENT_EXT_STATUS) {
c->ExtStatus = req->ExtStatus;
pcmcia_write_cis_mem(s, 1, (base + CISREG_ESR)>>1, 1, &c->ExtStatus);
}
if (req->Present & PRESENT_IOBASE_0) {
u_char b = c->io.BasePort1 & 0xff;
pcmcia_write_cis_mem(s, 1, (base + CISREG_IOBASE_0)>>1, 1, &b);
b = (c->io.BasePort1 >> 8) & 0xff;
pcmcia_write_cis_mem(s, 1, (base + CISREG_IOBASE_1)>>1, 1, &b);
}
if (req->Present & PRESENT_IOSIZE) {
u_char b = c->io.NumPorts1 + c->io.NumPorts2 - 1;
pcmcia_write_cis_mem(s, 1, (base + CISREG_IOSIZE)>>1, 1, &b);
}
/* Configure I/O windows */
if (c->state & CONFIG_IO_REQ) {
iomap.speed = io_speed;
for (i = 0; i < MAX_IO_WIN; i++)
if (s->io[i].NumPorts != 0) {
iomap.map = i;
iomap.flags = MAP_ACTIVE;
switch (s->io[i].Attributes & IO_DATA_PATH_WIDTH) {
case IO_DATA_PATH_WIDTH_16:
iomap.flags |= MAP_16BIT; break;
case IO_DATA_PATH_WIDTH_AUTO:
iomap.flags |= MAP_AUTOSZ; break;
default:
break;
}
iomap.start = s->io[i].BasePort;
iomap.stop = iomap.start + s->io[i].NumPorts - 1;
s->ops->set_io_map(s, &iomap);
s->io[i].Config++;
}
}
c->state |= CONFIG_LOCKED;
handle->state |= CLIENT_CONFIG_LOCKED;
return CS_SUCCESS;
} /* request_configuration */
/*======================================================================
Request_io() reserves ranges of port addresses for a socket.
I have not implemented range sharing or alias addressing.
======================================================================*/
int pcmcia_request_io(client_handle_t handle, io_req_t *req)
{
struct pcmcia_socket *s;
config_t *c;
if (CHECK_HANDLE(handle))
return CS_BAD_HANDLE;
s = SOCKET(handle);
if (!(s->state & SOCKET_PRESENT))
return CS_NO_CARD;
if (handle->state & CLIENT_CARDBUS) {
#ifdef CONFIG_CARDBUS
handle->state |= CLIENT_IO_REQ;
return CS_SUCCESS;
#else
return CS_UNSUPPORTED_FUNCTION;
#endif
}
if (!req)
return CS_UNSUPPORTED_MODE;
c = CONFIG(handle);
if (c->state & CONFIG_LOCKED)
return CS_CONFIGURATION_LOCKED;
if (c->state & CONFIG_IO_REQ)
return CS_IN_USE;
if (req->Attributes1 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS))
return CS_BAD_ATTRIBUTE;
if ((req->NumPorts2 > 0) &&
(req->Attributes2 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS)))
return CS_BAD_ATTRIBUTE;
if (alloc_io_space(s, req->Attributes1, &req->BasePort1,
req->NumPorts1, req->IOAddrLines))
return CS_IN_USE;
if (req->NumPorts2) {
if (alloc_io_space(s, req->Attributes2, &req->BasePort2,
req->NumPorts2, req->IOAddrLines)) {
release_io_space(s, req->BasePort1, req->NumPorts1);
return CS_IN_USE;
}
}
c->io = *req;
c->state |= CONFIG_IO_REQ;
handle->state |= CLIENT_IO_REQ;
return CS_SUCCESS;
} /* request_io */
/*======================================================================
Request_irq() reserves an irq for this client.
Also, since Linux only reserves irq's when they are actually
hooked, we don't guarantee that an irq will still be available
when the configuration is locked. Now that I think about it,
there might be a way to fix this using a dummy handler.
======================================================================*/
#ifdef CONFIG_PCMCIA_PROBE
static irqreturn_t test_action(int cpl, void *dev_id, struct pt_regs *regs)
{
return IRQ_NONE;
}
#endif
int pcmcia_request_irq(client_handle_t handle, irq_req_t *req)
{
struct pcmcia_socket *s;
config_t *c;
int ret = CS_IN_USE, irq = 0;
struct pcmcia_device *p_dev = handle_to_pdev(handle);
if (CHECK_HANDLE(handle))
return CS_BAD_HANDLE;
s = SOCKET(handle);
if (!(s->state & SOCKET_PRESENT))
return CS_NO_CARD;
c = CONFIG(handle);
if (c->state & CONFIG_LOCKED)
return CS_CONFIGURATION_LOCKED;
if (c->state & CONFIG_IRQ_REQ)
return CS_IN_USE;
#ifdef CONFIG_PCMCIA_PROBE
if (s->irq.AssignedIRQ != 0) {
/* If the interrupt is already assigned, it must be the same */
irq = s->irq.AssignedIRQ;
} else {
int try;
u32 mask = s->irq_mask;
void *data = NULL;
for (try = 0; try < 64; try++) {
irq = try % 32;
/* marked as available by driver, and not blocked by userspace? */
if (!((mask >> irq) & 1))
continue;
/* avoid an IRQ which is already used by a PCMCIA card */
if ((try < 32) && pcmcia_used_irq[irq])
continue;
/* register the correct driver, if possible, of check whether
* registering a dummy handle works, i.e. if the IRQ isn't
* marked as used by the kernel resource management core */
ret = request_irq(irq,
(req->Attributes & IRQ_HANDLE_PRESENT) ? req->Handler : test_action,
((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) ||
(s->functions > 1) ||
(irq == s->pci_irq)) ? SA_SHIRQ : 0,
p_dev->dev.bus_id,
(req->Attributes & IRQ_HANDLE_PRESENT) ? req->Instance : data);
if (!ret) {
if (!(req->Attributes & IRQ_HANDLE_PRESENT))
free_irq(irq, data);
break;
}
}
}
#endif
if (ret) {
if (!s->pci_irq)
return ret;
irq = s->pci_irq;
}
if (ret && req->Attributes & IRQ_HANDLE_PRESENT) {
if (request_irq(irq, req->Handler,
((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) ||
(s->functions > 1) ||
(irq == s->pci_irq)) ? SA_SHIRQ : 0,
p_dev->dev.bus_id, req->Instance))
return CS_IN_USE;
}
c->irq.Attributes = req->Attributes;
s->irq.AssignedIRQ = req->AssignedIRQ = irq;
s->irq.Config++;
c->state |= CONFIG_IRQ_REQ;
handle->state |= CLIENT_IRQ_REQ;
#ifdef CONFIG_PCMCIA_PROBE
pcmcia_used_irq[irq]++;
#endif
return CS_SUCCESS;
} /* pcmcia_request_irq */
/*======================================================================
Request_window() establishes a mapping between card memory space
and system memory space.
======================================================================*/
int pcmcia_request_window(client_handle_t *handle, win_req_t *req, window_handle_t *wh)
{
struct pcmcia_socket *s;
window_t *win;
u_long align;
int w;
if (CHECK_HANDLE(*handle))
return CS_BAD_HANDLE;
s = (*handle)->Socket;
if (!(s->state & SOCKET_PRESENT))
return CS_NO_CARD;
if (req->Attributes & (WIN_PAGED | WIN_SHARED))
return CS_BAD_ATTRIBUTE;
/* Window size defaults to smallest available */
if (req->Size == 0)
req->Size = s->map_size;
align = (((s->features & SS_CAP_MEM_ALIGN) ||
(req->Attributes & WIN_STRICT_ALIGN)) ?
req->Size : s->map_size);
if (req->Size & (s->map_size-1))
return CS_BAD_SIZE;
if ((req->Base && (s->features & SS_CAP_STATIC_MAP)) ||
(req->Base & (align-1)))
return CS_BAD_BASE;
if (req->Base)
align = 0;
/* Allocate system memory window */
for (w = 0; w < MAX_WIN; w++)
if (!(s->state & SOCKET_WIN_REQ(w))) break;
if (w == MAX_WIN)
return CS_OUT_OF_RESOURCE;
win = &s->win[w];
win->magic = WINDOW_MAGIC;
win->index = w;
win->handle = *handle;
win->sock = s;
if (!(s->features & SS_CAP_STATIC_MAP)) {
win->ctl.res = pcmcia_find_mem_region(req->Base, req->Size, align,
(req->Attributes & WIN_MAP_BELOW_1MB), s);
if (!win->ctl.res)
return CS_IN_USE;
}
(*handle)->state |= CLIENT_WIN_REQ(w);
/* Configure the socket controller */
win->ctl.map = w+1;
win->ctl.flags = 0;
win->ctl.speed = req->AccessSpeed;
if (req->Attributes & WIN_MEMORY_TYPE)
win->ctl.flags |= MAP_ATTRIB;
if (req->Attributes & WIN_ENABLE)
win->ctl.flags |= MAP_ACTIVE;
if (req->Attributes & WIN_DATA_WIDTH_16)
win->ctl.flags |= MAP_16BIT;
if (req->Attributes & WIN_USE_WAIT)
win->ctl.flags |= MAP_USE_WAIT;
win->ctl.card_start = 0;
if (s->ops->set_mem_map(s, &win->ctl) != 0)
return CS_BAD_ARGS;
s->state |= SOCKET_WIN_REQ(w);
/* Return window handle */
if (s->features & SS_CAP_STATIC_MAP) {
req->Base = win->ctl.static_start;
} else {
req->Base = win->ctl.res->start;
}
*wh = win;
return CS_SUCCESS;
} /* request_window */
/*====================================================================== /*======================================================================
...@@ -1888,20 +965,8 @@ static int pcmcia_socket_hotplug(struct class_device *dev, char **envp, ...@@ -1888,20 +965,8 @@ static int pcmcia_socket_hotplug(struct class_device *dev, char **envp,
======================================================================*/ ======================================================================*/
/* in alpha order */ /* in alpha order */
EXPORT_SYMBOL(pcmcia_eject_card); EXPORT_SYMBOL(pcmcia_eject_card);
EXPORT_SYMBOL(pcmcia_get_card_services_info);
EXPORT_SYMBOL(pcmcia_get_mem_page);
EXPORT_SYMBOL(pcmcia_insert_card); EXPORT_SYMBOL(pcmcia_insert_card);
EXPORT_SYMBOL(pcmcia_map_mem_page);
EXPORT_SYMBOL(pcmcia_modify_configuration);
EXPORT_SYMBOL(pcmcia_release_configuration);
EXPORT_SYMBOL(pcmcia_release_io);
EXPORT_SYMBOL(pcmcia_release_irq);
EXPORT_SYMBOL(pcmcia_release_window);
EXPORT_SYMBOL(pcmcia_replace_cis); EXPORT_SYMBOL(pcmcia_replace_cis);
EXPORT_SYMBOL(pcmcia_request_configuration);
EXPORT_SYMBOL(pcmcia_request_io);
EXPORT_SYMBOL(pcmcia_request_irq);
EXPORT_SYMBOL(pcmcia_request_window);
EXPORT_SYMBOL(pcmcia_resume_card); EXPORT_SYMBOL(pcmcia_resume_card);
EXPORT_SYMBOL(pcmcia_suspend_card); EXPORT_SYMBOL(pcmcia_suspend_card);
......
/*
* 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/config.h>
#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/version.h>
#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"
static const char *release = "Linux Kernel Card Services";
/* Access speed for IO windows */
static int io_speed = 0;
module_param(io_speed, int, 0444);
#ifdef CONFIG_PCMCIA_PROBE
/* mask of IRQs already reserved by other cards, we should avoid using them */
static u8 pcmcia_used_irq[NR_IRQS];
#endif
#ifdef DEBUG
extern int ds_pc_debug;
#define cs_socket_name(skt) ((skt)->dev.class_id)
#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].NumPorts != 0) &&
((s->io[i].BasePort & (align-1)) == *base))
return 1;
for (i = 0; i < MAX_IO_WIN; i++) {
if (s->io[i].NumPorts == 0) {
s->io[i].res = pcmcia_find_io_region(*base, num, align, s);
if (s->io[i].res) {
s->io[i].Attributes = attr;
s->io[i].BasePort = *base = s->io[i].res->start;
s->io[i].NumPorts = s->io[i].InUse = num;
break;
} else
return 1;
} else if (s->io[i].Attributes != attr)
continue;
/* Try to extend top of window */
try = s->io[i].BasePort + s->io[i].NumPorts;
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].NumPorts += num;
s->io[i].InUse += num;
break;
}
/* Try to extend bottom of window */
try = s->io[i].BasePort - 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) {
s->io[i].BasePort = *base = try;
s->io[i].NumPorts += num;
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].BasePort <= base) &&
(s->io[i].BasePort+s->io[i].NumPorts >= base+num)) {
s->io[i].InUse -= num;
/* Free the window if no one else is using it */
if (s->io[i].InUse == 0) {
s->io[i].NumPorts = 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 pccard_access_configuration_register(struct pcmcia_socket *s,
unsigned int function,
conf_reg_t *reg)
{
config_t *c;
int addr;
u_char val;
if (!s || !s->config)
return CS_NO_CARD;
c = &s->config[function];
if (c == NULL)
return CS_NO_CARD;
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;
} /* pccard_access_configuration_register */
EXPORT_SYMBOL(pccard_access_configuration_register);
int pccard_get_configuration_info(struct pcmcia_socket *s,
unsigned int function,
config_info_t *config)
{
config_t *c;
if (!(s->state & SOCKET_PRESENT))
return CS_NO_CARD;
config->Function = function;
#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;
config->BasePort1 = s->io[0].BasePort;
config->NumPorts1 = s->io[0].NumPorts;
}
return CS_SUCCESS;
}
#endif
c = (s->config != NULL) ? &s->config[function] : NULL;
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;
}
/* !!! This is a hack !!! */
memcpy(&config->Attributes, &c->Attributes, sizeof(config_t));
config->Attributes |= CONF_VALID_CLIENT;
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 */
EXPORT_SYMBOL(pccard_get_configuration_info);
/** pcmcia_get_card_services_info
*
* Return information about this version of Card Services
*/
int pcmcia_get_card_services_info(servinfo_t *info)
{
unsigned int socket_count = 0;
struct list_head *tmp;
info->Signature[0] = 'C';
info->Signature[1] = 'S';
down_read(&pcmcia_socket_list_rwsem);
list_for_each(tmp, &pcmcia_socket_list)
socket_count++;
up_read(&pcmcia_socket_list_rwsem);
info->Count = socket_count;
info->Revision = CS_RELEASE_CODE;
info->CSLevel = 0x0210;
info->VendorString = (char *)release;
return CS_SUCCESS;
} /* get_card_services_info */
EXPORT_SYMBOL(pcmcia_get_card_services_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, unsigned int function,
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 = (s->config != NULL) ? &s->config[function] : NULL;
if ((c != NULL) && (c->state & CONFIG_LOCKED) &&
(c->IntType & (INT_MEMORY_AND_IO | INT_ZOOMED_VIDEO))) {
u_char reg;
if (c->Present & PRESENT_PIN_REPLACE) {
pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_PRR)>>1, 1, &reg);
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->Present & PRESENT_EXT_STATUS) {
pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_ESR)>>1, 1, &reg);
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 */
EXPORT_SYMBOL(pccard_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(client_handle_t handle,
modconf_t *mod)
{
struct pcmcia_socket *s;
config_t *c;
if (CHECK_HANDLE(handle))
return CS_BAD_HANDLE;
s = SOCKET(handle);
c = CONFIG(handle);
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;
c->Vpp1 = c->Vpp2 = 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;
return CS_SUCCESS;
} /* modify_configuration */
EXPORT_SYMBOL(pcmcia_modify_configuration);
int pcmcia_release_configuration(client_handle_t handle)
{
pccard_io_map io = { 0, 0, 0, 0, 1 };
struct pcmcia_socket *s;
int i;
if (CHECK_HANDLE(handle) ||
!(handle->state & CLIENT_CONFIG_LOCKED))
return CS_BAD_HANDLE;
handle->state &= ~CLIENT_CONFIG_LOCKED;
s = SOCKET(handle);
#ifdef CONFIG_CARDBUS
if (handle->state & CLIENT_CARDBUS)
return CS_SUCCESS;
#endif
if (!(handle->state & CLIENT_STALE)) {
config_t *c = CONFIG(handle);
if (--(s->lock_count) == 0) {
s->socket.flags = SS_OUTPUT_ENA; /* Is this correct? */
s->socket.Vpp = 0;
s->socket.io_irq = 0;
s->ops->set_socket(s, &s->socket);
}
if (c->state & CONFIG_IO_REQ)
for (i = 0; i < MAX_IO_WIN; i++) {
if (s->io[i].NumPorts == 0)
continue;
s->io[i].Config--;
if (s->io[i].Config != 0)
continue;
io.map = i;
s->ops->set_io_map(s, &io);
}
c->state &= ~CONFIG_LOCKED;
}
return CS_SUCCESS;
} /* pcmcia_release_configuration */
EXPORT_SYMBOL(pcmcia_release_configuration);
/** pcmcia_release_io
*
* Release_io() releases the I/O ranges allocated by a client. This
* may be invoked some time after a card ejection has already dumped
* the actual socket configuration, so if the client is "stale", we
* don't bother checking the port ranges against the current socket
* values.
*/
int pcmcia_release_io(client_handle_t handle, io_req_t *req)
{
struct pcmcia_socket *s;
if (CHECK_HANDLE(handle) || !(handle->state & CLIENT_IO_REQ))
return CS_BAD_HANDLE;
handle->state &= ~CLIENT_IO_REQ;
s = SOCKET(handle);
#ifdef CONFIG_CARDBUS
if (handle->state & CLIENT_CARDBUS)
return CS_SUCCESS;
#endif
if (!(handle->state & CLIENT_STALE)) {
config_t *c = CONFIG(handle);
if (c->state & CONFIG_LOCKED)
return CS_CONFIGURATION_LOCKED;
if ((c->io.BasePort1 != req->BasePort1) ||
(c->io.NumPorts1 != req->NumPorts1) ||
(c->io.BasePort2 != req->BasePort2) ||
(c->io.NumPorts2 != req->NumPorts2))
return CS_BAD_ARGS;
c->state &= ~CONFIG_IO_REQ;
}
release_io_space(s, req->BasePort1, req->NumPorts1);
if (req->NumPorts2)
release_io_space(s, req->BasePort2, req->NumPorts2);
return CS_SUCCESS;
} /* pcmcia_release_io */
EXPORT_SYMBOL(pcmcia_release_io);
int pcmcia_release_irq(client_handle_t handle, irq_req_t *req)
{
struct pcmcia_socket *s;
if (CHECK_HANDLE(handle) || !(handle->state & CLIENT_IRQ_REQ))
return CS_BAD_HANDLE;
handle->state &= ~CLIENT_IRQ_REQ;
s = SOCKET(handle);
if (!(handle->state & CLIENT_STALE)) {
config_t *c = CONFIG(handle);
if (c->state & CONFIG_LOCKED)
return CS_CONFIGURATION_LOCKED;
if (c->irq.Attributes != req->Attributes)
return CS_BAD_ATTRIBUTE;
if (s->irq.AssignedIRQ != req->AssignedIRQ)
return CS_BAD_IRQ;
if (--s->irq.Config == 0) {
c->state &= ~CONFIG_IRQ_REQ;
s->irq.AssignedIRQ = 0;
}
}
if (req->Attributes & IRQ_HANDLE_PRESENT) {
free_irq(req->AssignedIRQ, req->Instance);
}
#ifdef CONFIG_PCMCIA_PROBE
pcmcia_used_irq[req->AssignedIRQ]--;
#endif
return CS_SUCCESS;
} /* pcmcia_release_irq */
EXPORT_SYMBOL(pcmcia_release_irq);
int pcmcia_release_window(window_handle_t win)
{
struct pcmcia_socket *s;
if ((win == NULL) || (win->magic != WINDOW_MAGIC))
return CS_BAD_HANDLE;
s = win->sock;
if (!(win->handle->state & CLIENT_WIN_REQ(win->index)))
return CS_BAD_HANDLE;
/* Shut down memory window */
win->ctl.flags &= ~MAP_ACTIVE;
s->ops->set_mem_map(s, &win->ctl);
s->state &= ~SOCKET_WIN_REQ(win->index);
/* Release system memory */
if (win->ctl.res) {
release_resource(win->ctl.res);
kfree(win->ctl.res);
win->ctl.res = NULL;
}
win->handle->state &= ~CLIENT_WIN_REQ(win->index);
win->magic = 0;
return CS_SUCCESS;
} /* pcmcia_release_window */
EXPORT_SYMBOL(pcmcia_release_window);
int pcmcia_request_configuration(client_handle_t handle,
config_req_t *req)
{
int i;
u_int base;
struct pcmcia_socket *s;
config_t *c;
pccard_io_map iomap;
if (CHECK_HANDLE(handle))
return CS_BAD_HANDLE;
s = SOCKET(handle);
if (!(s->state & SOCKET_PRESENT))
return CS_NO_CARD;
#ifdef CONFIG_CARDBUS
if (handle->state & CLIENT_CARDBUS)
return CS_UNSUPPORTED_MODE;
#endif
if (req->IntType & INT_CARDBUS)
return CS_UNSUPPORTED_MODE;
c = CONFIG(handle);
if (c->state & CONFIG_LOCKED)
return CS_CONFIGURATION_LOCKED;
/* Do power control. We don't allow changes in Vcc. */
if (s->socket.Vcc != req->Vcc)
return CS_BAD_VCC;
if (req->Vpp1 != req->Vpp2)
return CS_BAD_VPP;
s->socket.Vpp = req->Vpp1;
if (s->ops->set_socket(s, &s->socket))
return CS_BAD_VPP;
c->Vcc = req->Vcc; c->Vpp1 = c->Vpp2 = req->Vpp1;
/* Pick memory or I/O card, DMA mode, interrupt */
c->IntType = req->IntType;
c->Attributes = req->Attributes;
if (req->IntType & INT_MEMORY_AND_IO)
s->socket.flags |= SS_IOCARD;
if (req->IntType & INT_ZOOMED_VIDEO)
s->socket.flags |= SS_ZVCARD | SS_IOCARD;
if (req->Attributes & CONF_ENABLE_DMA)
s->socket.flags |= SS_DMA_MODE;
if (req->Attributes & CONF_ENABLE_SPKR)
s->socket.flags |= SS_SPKR_ENA;
if (req->Attributes & CONF_ENABLE_IRQ)
s->socket.io_irq = s->irq.AssignedIRQ;
else
s->socket.io_irq = 0;
s->ops->set_socket(s, &s->socket);
s->lock_count++;
/* Set up CIS configuration registers */
base = c->ConfigBase = req->ConfigBase;
c->Present = c->CardValues = req->Present;
if (req->Present & PRESENT_COPY) {
c->Copy = req->Copy;
pcmcia_write_cis_mem(s, 1, (base + CISREG_SCR)>>1, 1, &c->Copy);
}
if (req->Present & PRESENT_OPTION) {
if (s->functions == 1) {
c->Option = req->ConfigIndex & COR_CONFIG_MASK;
} else {
c->Option = req->ConfigIndex & COR_MFC_CONFIG_MASK;
c->Option |= COR_FUNC_ENA|COR_IREQ_ENA;
if (req->Present & PRESENT_IOBASE_0)
c->Option |= COR_ADDR_DECODE;
}
if (c->state & CONFIG_IRQ_REQ)
if (!(c->irq.Attributes & IRQ_FORCED_PULSE))
c->Option |= COR_LEVEL_REQ;
pcmcia_write_cis_mem(s, 1, (base + CISREG_COR)>>1, 1, &c->Option);
mdelay(40);
}
if (req->Present & PRESENT_STATUS) {
c->Status = req->Status;
pcmcia_write_cis_mem(s, 1, (base + CISREG_CCSR)>>1, 1, &c->Status);
}
if (req->Present & PRESENT_PIN_REPLACE) {
c->Pin = req->Pin;
pcmcia_write_cis_mem(s, 1, (base + CISREG_PRR)>>1, 1, &c->Pin);
}
if (req->Present & PRESENT_EXT_STATUS) {
c->ExtStatus = req->ExtStatus;
pcmcia_write_cis_mem(s, 1, (base + CISREG_ESR)>>1, 1, &c->ExtStatus);
}
if (req->Present & PRESENT_IOBASE_0) {
u_char b = c->io.BasePort1 & 0xff;
pcmcia_write_cis_mem(s, 1, (base + CISREG_IOBASE_0)>>1, 1, &b);
b = (c->io.BasePort1 >> 8) & 0xff;
pcmcia_write_cis_mem(s, 1, (base + CISREG_IOBASE_1)>>1, 1, &b);
}
if (req->Present & PRESENT_IOSIZE) {
u_char b = c->io.NumPorts1 + c->io.NumPorts2 - 1;
pcmcia_write_cis_mem(s, 1, (base + CISREG_IOSIZE)>>1, 1, &b);
}
/* Configure I/O windows */
if (c->state & CONFIG_IO_REQ) {
iomap.speed = io_speed;
for (i = 0; i < MAX_IO_WIN; i++)
if (s->io[i].NumPorts != 0) {
iomap.map = i;
iomap.flags = MAP_ACTIVE;
switch (s->io[i].Attributes & IO_DATA_PATH_WIDTH) {
case IO_DATA_PATH_WIDTH_16:
iomap.flags |= MAP_16BIT; break;
case IO_DATA_PATH_WIDTH_AUTO:
iomap.flags |= MAP_AUTOSZ; break;
default:
break;
}
iomap.start = s->io[i].BasePort;
iomap.stop = iomap.start + s->io[i].NumPorts - 1;
s->ops->set_io_map(s, &iomap);
s->io[i].Config++;
}
}
c->state |= CONFIG_LOCKED;
handle->state |= CLIENT_CONFIG_LOCKED;
return CS_SUCCESS;
} /* pcmcia_request_configuration */
EXPORT_SYMBOL(pcmcia_request_configuration);
/** pcmcia_request_io
*
* Request_io() reserves ranges of port addresses for a socket.
* I have not implemented range sharing or alias addressing.
*/
int pcmcia_request_io(client_handle_t handle, io_req_t *req)
{
struct pcmcia_socket *s;
config_t *c;
if (CHECK_HANDLE(handle))
return CS_BAD_HANDLE;
s = SOCKET(handle);
if (!(s->state & SOCKET_PRESENT))
return CS_NO_CARD;
if (handle->state & CLIENT_CARDBUS) {
#ifdef CONFIG_CARDBUS
handle->state |= CLIENT_IO_REQ;
return CS_SUCCESS;
#else
return CS_UNSUPPORTED_FUNCTION;
#endif
}
if (!req)
return CS_UNSUPPORTED_MODE;
c = CONFIG(handle);
if (c->state & CONFIG_LOCKED)
return CS_CONFIGURATION_LOCKED;
if (c->state & CONFIG_IO_REQ)
return CS_IN_USE;
if (req->Attributes1 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS))
return CS_BAD_ATTRIBUTE;
if ((req->NumPorts2 > 0) &&
(req->Attributes2 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS)))
return CS_BAD_ATTRIBUTE;
if (alloc_io_space(s, req->Attributes1, &req->BasePort1,
req->NumPorts1, req->IOAddrLines))
return CS_IN_USE;
if (req->NumPorts2) {
if (alloc_io_space(s, req->Attributes2, &req->BasePort2,
req->NumPorts2, req->IOAddrLines)) {
release_io_space(s, req->BasePort1, req->NumPorts1);
return CS_IN_USE;
}
}
c->io = *req;
c->state |= CONFIG_IO_REQ;
handle->state |= CLIENT_IO_REQ;
return CS_SUCCESS;
} /* pcmcia_request_io */
EXPORT_SYMBOL(pcmcia_request_io);
/** pcmcia_request_irq
*
* Request_irq() reserves an irq for this client.
*
* Also, since Linux only reserves irq's when they are actually
* hooked, we don't guarantee that an irq will still be available
* when the configuration is locked. Now that I think about it,
* there might be a way to fix this using a dummy handler.
*/
#ifdef CONFIG_PCMCIA_PROBE
static irqreturn_t test_action(int cpl, void *dev_id, struct pt_regs *regs)
{
return IRQ_NONE;
}
#endif
int pcmcia_request_irq(client_handle_t handle, irq_req_t *req)
{
struct pcmcia_socket *s;
config_t *c;
int ret = CS_IN_USE, irq = 0;
struct pcmcia_device *p_dev = handle_to_pdev(handle);
if (CHECK_HANDLE(handle))
return CS_BAD_HANDLE;
s = SOCKET(handle);
if (!(s->state & SOCKET_PRESENT))
return CS_NO_CARD;
c = CONFIG(handle);
if (c->state & CONFIG_LOCKED)
return CS_CONFIGURATION_LOCKED;
if (c->state & CONFIG_IRQ_REQ)
return CS_IN_USE;
#ifdef CONFIG_PCMCIA_PROBE
if (s->irq.AssignedIRQ != 0) {
/* If the interrupt is already assigned, it must be the same */
irq = s->irq.AssignedIRQ;
} else {
int try;
u32 mask = s->irq_mask;
void *data = NULL;
for (try = 0; try < 64; try++) {
irq = try % 32;
/* marked as available by driver, and not blocked by userspace? */
if (!((mask >> irq) & 1))
continue;
/* avoid an IRQ which is already used by a PCMCIA card */
if ((try < 32) && pcmcia_used_irq[irq])
continue;
/* register the correct driver, if possible, of check whether
* registering a dummy handle works, i.e. if the IRQ isn't
* marked as used by the kernel resource management core */
ret = request_irq(irq,
(req->Attributes & IRQ_HANDLE_PRESENT) ? req->Handler : test_action,
((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) ||
(s->functions > 1) ||
(irq == s->pci_irq)) ? SA_SHIRQ : 0,
p_dev->dev.bus_id,
(req->Attributes & IRQ_HANDLE_PRESENT) ? req->Instance : data);
if (!ret) {
if (!(req->Attributes & IRQ_HANDLE_PRESENT))
free_irq(irq, data);
break;
}
}
}
#endif
if (ret) {
if (!s->pci_irq)
return ret;
irq = s->pci_irq;
}
if (ret && req->Attributes & IRQ_HANDLE_PRESENT) {
if (request_irq(irq, req->Handler,
((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) ||
(s->functions > 1) ||
(irq == s->pci_irq)) ? SA_SHIRQ : 0,
p_dev->dev.bus_id, req->Instance))
return CS_IN_USE;
}
c->irq.Attributes = req->Attributes;
s->irq.AssignedIRQ = req->AssignedIRQ = irq;
s->irq.Config++;
c->state |= CONFIG_IRQ_REQ;
handle->state |= CLIENT_IRQ_REQ;
#ifdef CONFIG_PCMCIA_PROBE
pcmcia_used_irq[irq]++;
#endif
return CS_SUCCESS;
} /* pcmcia_request_irq */
EXPORT_SYMBOL(pcmcia_request_irq);
/** pcmcia_request_window
*
* Request_window() establishes a mapping between card memory space
* and system memory space.
*/
int pcmcia_request_window(client_handle_t *handle, win_req_t *req, window_handle_t *wh)
{
struct pcmcia_socket *s;
window_t *win;
u_long align;
int w;
if (CHECK_HANDLE(*handle))
return CS_BAD_HANDLE;
s = (*handle)->Socket;
if (!(s->state & SOCKET_PRESENT))
return CS_NO_CARD;
if (req->Attributes & (WIN_PAGED | WIN_SHARED))
return CS_BAD_ATTRIBUTE;
/* Window size defaults to smallest available */
if (req->Size == 0)
req->Size = s->map_size;
align = (((s->features & SS_CAP_MEM_ALIGN) ||
(req->Attributes & WIN_STRICT_ALIGN)) ?
req->Size : s->map_size);
if (req->Size & (s->map_size-1))
return CS_BAD_SIZE;
if ((req->Base && (s->features & SS_CAP_STATIC_MAP)) ||
(req->Base & (align-1)))
return CS_BAD_BASE;
if (req->Base)
align = 0;
/* Allocate system memory window */
for (w = 0; w < MAX_WIN; w++)
if (!(s->state & SOCKET_WIN_REQ(w))) break;
if (w == MAX_WIN)
return CS_OUT_OF_RESOURCE;
win = &s->win[w];
win->magic = WINDOW_MAGIC;
win->index = w;
win->handle = *handle;
win->sock = s;
if (!(s->features & SS_CAP_STATIC_MAP)) {
win->ctl.res = pcmcia_find_mem_region(req->Base, req->Size, align,
(req->Attributes & WIN_MAP_BELOW_1MB), s);
if (!win->ctl.res)
return CS_IN_USE;
}
(*handle)->state |= CLIENT_WIN_REQ(w);
/* Configure the socket controller */
win->ctl.map = w+1;
win->ctl.flags = 0;
win->ctl.speed = req->AccessSpeed;
if (req->Attributes & WIN_MEMORY_TYPE)
win->ctl.flags |= MAP_ATTRIB;
if (req->Attributes & WIN_ENABLE)
win->ctl.flags |= MAP_ACTIVE;
if (req->Attributes & WIN_DATA_WIDTH_16)
win->ctl.flags |= MAP_16BIT;
if (req->Attributes & WIN_USE_WAIT)
win->ctl.flags |= MAP_USE_WAIT;
win->ctl.card_start = 0;
if (s->ops->set_mem_map(s, &win->ctl) != 0)
return CS_BAD_ARGS;
s->state |= SOCKET_WIN_REQ(w);
/* Return window handle */
if (s->features & SS_CAP_STATIC_MAP) {
req->Base = win->ctl.static_start;
} else {
req->Base = win->ctl.res->start;
}
*wh = win;
return CS_SUCCESS;
} /* pcmcia_request_window */
EXPORT_SYMBOL(pcmcia_request_window);
...@@ -112,6 +112,7 @@ int pcmcia_adjust_io_region(struct resource *res, unsigned long r_start, ...@@ -112,6 +112,7 @@ int pcmcia_adjust_io_region(struct resource *res, unsigned long r_start,
return s->resource_ops->adjust_io_region(res, r_start, r_end, s); return s->resource_ops->adjust_io_region(res, r_start, r_end, s);
return -ENOMEM; return -ENOMEM;
} }
EXPORT_SYMBOL(pcmcia_adjust_io_region);
struct resource *pcmcia_find_io_region(unsigned long base, int num, struct resource *pcmcia_find_io_region(unsigned long base, int num,
unsigned long align, struct pcmcia_socket *s) unsigned long align, struct pcmcia_socket *s)
...@@ -120,6 +121,7 @@ struct resource *pcmcia_find_io_region(unsigned long base, int num, ...@@ -120,6 +121,7 @@ struct resource *pcmcia_find_io_region(unsigned long base, int num,
return s->resource_ops->find_io(base, num, align, s); return s->resource_ops->find_io(base, num, align, s);
return NULL; return NULL;
} }
EXPORT_SYMBOL(pcmcia_find_io_region);
struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align, struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align,
int low, struct pcmcia_socket *s) int low, struct pcmcia_socket *s)
...@@ -128,6 +130,7 @@ struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align, ...@@ -128,6 +130,7 @@ struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align,
return s->resource_ops->find_mem(base, num, align, low, s); return s->resource_ops->find_mem(base, num, align, low, s);
return NULL; return NULL;
} }
EXPORT_SYMBOL(pcmcia_find_mem_region);
void release_resource_db(struct pcmcia_socket *s) void release_resource_db(struct pcmcia_socket *s)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment