|
@@ -6,7 +6,7 @@
|
|
|
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
|
|
|
*
|
|
|
* Copyright (C) 1999 David A. Hinds
|
|
|
- * Copyright (C) 2004-2005 Dominik Brodowski
|
|
|
+ * Copyright (C) 2004-2010 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
|
|
@@ -26,7 +26,6 @@
|
|
|
#include <asm/irq.h>
|
|
|
|
|
|
#include <pcmcia/ss.h>
|
|
|
-#include <pcmcia/cs.h>
|
|
|
#include <pcmcia/cistpl.h>
|
|
|
#include <pcmcia/cisreg.h>
|
|
|
#include <pcmcia/ds.h>
|
|
@@ -56,6 +55,12 @@ struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align,
|
|
|
}
|
|
|
|
|
|
|
|
|
+/**
|
|
|
+ * release_io_space() - release IO ports allocated with alloc_io_space()
|
|
|
+ * @s: pcmcia socket
|
|
|
+ * @res: resource to release
|
|
|
+ *
|
|
|
+ */
|
|
|
static void release_io_space(struct pcmcia_socket *s, struct resource *res)
|
|
|
{
|
|
|
resource_size_t num = resource_size(res);
|
|
@@ -81,9 +86,14 @@ static void release_io_space(struct pcmcia_socket *s, struct resource *res)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-} /* release_io_space */
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
-/** alloc_io_space
|
|
|
+/**
|
|
|
+ * alloc_io_space() - allocate IO ports for use by a PCMCIA device
|
|
|
+ * @s: pcmcia socket
|
|
|
+ * @res: resource to allocate (begin: begin, end: size)
|
|
|
+ * @lines: number of IO lines decoded by the PCMCIA card
|
|
|
*
|
|
|
* Special stuff for managing IO windows, because they are scarce
|
|
|
*/
|
|
@@ -135,7 +145,7 @@ static int alloc_io_space(struct pcmcia_socket *s, struct resource *res,
|
|
|
}
|
|
|
dev_dbg(&s->dev, "alloc_io_space request result %d: %pR\n", ret, res);
|
|
|
return ret;
|
|
|
-} /* alloc_io_space */
|
|
|
+}
|
|
|
|
|
|
|
|
|
/**
|
|
@@ -168,14 +178,14 @@ static int pcmcia_access_config(struct pcmcia_device *p_dev,
|
|
|
return -EACCES;
|
|
|
}
|
|
|
|
|
|
- addr = (c->ConfigBase + where) >> 1;
|
|
|
+ addr = (p_dev->config_base + where) >> 1;
|
|
|
|
|
|
ret = accessf(s, 1, addr, 1, val);
|
|
|
|
|
|
mutex_unlock(&s->ops_mutex);
|
|
|
|
|
|
return ret;
|
|
|
-} /* pcmcia_access_config */
|
|
|
+}
|
|
|
|
|
|
|
|
|
/**
|
|
@@ -204,11 +214,20 @@ int pcmcia_write_config_byte(struct pcmcia_device *p_dev, off_t where, u8 val)
|
|
|
EXPORT_SYMBOL(pcmcia_write_config_byte);
|
|
|
|
|
|
|
|
|
-int pcmcia_map_mem_page(struct pcmcia_device *p_dev, window_handle_t wh,
|
|
|
+/**
|
|
|
+ * pcmcia_map_mem_page() - modify iomem window to point to a different offset
|
|
|
+ * @p_dev: pcmcia device
|
|
|
+ * @res: iomem resource already enabled by pcmcia_request_window()
|
|
|
+ * @offset: card_offset to map
|
|
|
+ *
|
|
|
+ * pcmcia_map_mem_page() modifies what can be read and written by accessing
|
|
|
+ * an iomem range previously enabled by pcmcia_request_window(), by setting
|
|
|
+ * the card_offset value to @offset.
|
|
|
+ */
|
|
|
+int pcmcia_map_mem_page(struct pcmcia_device *p_dev, struct resource *res,
|
|
|
unsigned int offset)
|
|
|
{
|
|
|
struct pcmcia_socket *s = p_dev->socket;
|
|
|
- struct resource *res = wh;
|
|
|
unsigned int w;
|
|
|
int ret;
|
|
|
|
|
@@ -223,98 +242,111 @@ int pcmcia_map_mem_page(struct pcmcia_device *p_dev, window_handle_t wh,
|
|
|
dev_warn(&p_dev->dev, "failed to set_mem_map\n");
|
|
|
mutex_unlock(&s->ops_mutex);
|
|
|
return ret;
|
|
|
-} /* pcmcia_map_mem_page */
|
|
|
+}
|
|
|
EXPORT_SYMBOL(pcmcia_map_mem_page);
|
|
|
|
|
|
|
|
|
-/** pcmcia_modify_configuration
|
|
|
+/**
|
|
|
+ * pcmcia_fixup_iowidth() - reduce io width to 8bit
|
|
|
+ * @p_dev: pcmcia device
|
|
|
*
|
|
|
- * Modify a locked socket configuration
|
|
|
+ * pcmcia_fixup_iowidth() allows a PCMCIA device driver to reduce the
|
|
|
+ * IO width to 8bit after having called pcmcia_enable_device()
|
|
|
+ * previously.
|
|
|
*/
|
|
|
-int pcmcia_modify_configuration(struct pcmcia_device *p_dev,
|
|
|
- modconf_t *mod)
|
|
|
+int pcmcia_fixup_iowidth(struct pcmcia_device *p_dev)
|
|
|
{
|
|
|
- struct pcmcia_socket *s;
|
|
|
- config_t *c;
|
|
|
- int ret;
|
|
|
-
|
|
|
- s = p_dev->socket;
|
|
|
+ struct pcmcia_socket *s = p_dev->socket;
|
|
|
+ pccard_io_map io_off = { 0, 0, 0, 0, 1 };
|
|
|
+ pccard_io_map io_on;
|
|
|
+ int i, ret = 0;
|
|
|
|
|
|
mutex_lock(&s->ops_mutex);
|
|
|
- c = p_dev->function_config;
|
|
|
|
|
|
- if (!(s->state & SOCKET_PRESENT)) {
|
|
|
- dev_dbg(&p_dev->dev, "No card present\n");
|
|
|
- ret = -ENODEV;
|
|
|
- goto unlock;
|
|
|
- }
|
|
|
- if (!(c->state & CONFIG_LOCKED)) {
|
|
|
- dev_dbg(&p_dev->dev, "Configuration isnt't locked\n");
|
|
|
+ dev_dbg(&p_dev->dev, "fixup iowidth to 8bit\n");
|
|
|
+
|
|
|
+ if (!(s->state & SOCKET_PRESENT) ||
|
|
|
+ !(p_dev->function_config->state & CONFIG_LOCKED)) {
|
|
|
+ dev_dbg(&p_dev->dev, "No card? Config not locked?\n");
|
|
|
ret = -EACCES;
|
|
|
goto unlock;
|
|
|
}
|
|
|
|
|
|
- if (mod->Attributes & (CONF_IRQ_CHANGE_VALID | CONF_VCC_CHANGE_VALID)) {
|
|
|
- dev_dbg(&p_dev->dev,
|
|
|
- "changing Vcc or IRQ is not allowed at this time\n");
|
|
|
- ret = -EINVAL;
|
|
|
- goto unlock;
|
|
|
- }
|
|
|
+ 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;
|
|
|
|
|
|
- /* 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) {
|
|
|
- dev_dbg(&p_dev->dev,
|
|
|
- "Vpp1 and Vpp2 must be the same\n");
|
|
|
- ret = -EINVAL;
|
|
|
- goto unlock;
|
|
|
- }
|
|
|
- s->socket.Vpp = mod->Vpp1;
|
|
|
- if (s->ops->set_socket(s, &s->socket)) {
|
|
|
- dev_printk(KERN_WARNING, &p_dev->dev,
|
|
|
- "Unable to set VPP\n");
|
|
|
- ret = -EIO;
|
|
|
- goto unlock;
|
|
|
- }
|
|
|
- } else if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) ||
|
|
|
- (mod->Attributes & CONF_VPP2_CHANGE_VALID)) {
|
|
|
- dev_dbg(&p_dev->dev,
|
|
|
- "changing Vcc is not allowed at this time\n");
|
|
|
- ret = -EINVAL;
|
|
|
- goto unlock;
|
|
|
+ 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);
|
|
|
}
|
|
|
+unlock:
|
|
|
+ mutex_unlock(&s->ops_mutex);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(pcmcia_fixup_iowidth);
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * pcmcia_fixup_vpp() - set Vpp to a new voltage level
|
|
|
+ * @p_dev: pcmcia device
|
|
|
+ * @new_vpp: new Vpp voltage
|
|
|
+ *
|
|
|
+ * pcmcia_fixup_vpp() allows a PCMCIA device driver to set Vpp to
|
|
|
+ * a new voltage level between calls to pcmcia_enable_device()
|
|
|
+ * and pcmcia_disable_device().
|
|
|
+ */
|
|
|
+int pcmcia_fixup_vpp(struct pcmcia_device *p_dev, unsigned char new_vpp)
|
|
|
+{
|
|
|
+ struct pcmcia_socket *s = p_dev->socket;
|
|
|
+ int ret = 0;
|
|
|
|
|
|
- if (mod->Attributes & CONF_IO_CHANGE_WIDTH) {
|
|
|
- pccard_io_map io_off = { 0, 0, 0, 0, 1 };
|
|
|
- pccard_io_map io_on;
|
|
|
- int i;
|
|
|
+ mutex_lock(&s->ops_mutex);
|
|
|
|
|
|
- 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;
|
|
|
+ dev_dbg(&p_dev->dev, "fixup Vpp to %d\n", new_vpp);
|
|
|
|
|
|
- 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;
|
|
|
+ if (!(s->state & SOCKET_PRESENT) ||
|
|
|
+ !(p_dev->function_config->state & CONFIG_LOCKED)) {
|
|
|
+ dev_dbg(&p_dev->dev, "No card? Config not locked?\n");
|
|
|
+ ret = -EACCES;
|
|
|
+ goto unlock;
|
|
|
+ }
|
|
|
|
|
|
- s->ops->set_io_map(s, &io_off);
|
|
|
- mdelay(40);
|
|
|
- s->ops->set_io_map(s, &io_on);
|
|
|
- }
|
|
|
+ s->socket.Vpp = new_vpp;
|
|
|
+ if (s->ops->set_socket(s, &s->socket)) {
|
|
|
+ dev_warn(&p_dev->dev, "Unable to set VPP\n");
|
|
|
+ ret = -EIO;
|
|
|
+ goto unlock;
|
|
|
}
|
|
|
- ret = 0;
|
|
|
+ p_dev->vpp = new_vpp;
|
|
|
+
|
|
|
unlock:
|
|
|
mutex_unlock(&s->ops_mutex);
|
|
|
|
|
|
return ret;
|
|
|
-} /* modify_configuration */
|
|
|
-EXPORT_SYMBOL(pcmcia_modify_configuration);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(pcmcia_fixup_vpp);
|
|
|
|
|
|
|
|
|
+/**
|
|
|
+ * pcmcia_release_configuration() - physically disable a PCMCIA device
|
|
|
+ * @p_dev: pcmcia device
|
|
|
+ *
|
|
|
+ * pcmcia_release_configuration() is the 1:1 counterpart to
|
|
|
+ * pcmcia_enable_device(): If a PCMCIA device is no longer used by any
|
|
|
+ * driver, the Vpp voltage is set to 0, IRQs will no longer be generated,
|
|
|
+ * and I/O ranges will be disabled. As pcmcia_release_io() and
|
|
|
+ * pcmcia_release_window() still need to be called, device drivers are
|
|
|
+ * expected to call pcmcia_disable_device() instead.
|
|
|
+ */
|
|
|
int pcmcia_release_configuration(struct pcmcia_device *p_dev)
|
|
|
{
|
|
|
pccard_io_map io = { 0, 0, 0, 0, 1 };
|
|
@@ -327,7 +359,7 @@ int pcmcia_release_configuration(struct pcmcia_device *p_dev)
|
|
|
if (p_dev->_locked) {
|
|
|
p_dev->_locked = 0;
|
|
|
if (--(s->lock_count) == 0) {
|
|
|
- s->socket.flags = SS_OUTPUT_ENA; /* Is this correct? */
|
|
|
+ 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);
|
|
@@ -349,16 +381,18 @@ int pcmcia_release_configuration(struct pcmcia_device *p_dev)
|
|
|
mutex_unlock(&s->ops_mutex);
|
|
|
|
|
|
return 0;
|
|
|
-} /* pcmcia_release_configuration */
|
|
|
+}
|
|
|
|
|
|
|
|
|
-/** pcmcia_release_io
|
|
|
+/**
|
|
|
+ * pcmcia_release_io() - release I/O allocated by a PCMCIA device
|
|
|
+ * @p_dev: pcmcia device
|
|
|
*
|
|
|
- * 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.
|
|
|
+ * pcmcia_release_io() releases the I/O ranges allocated by a PCMCIA
|
|
|
+ * device. 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.
|
|
|
*/
|
|
|
static int pcmcia_release_io(struct pcmcia_device *p_dev)
|
|
|
{
|
|
@@ -387,6 +421,14 @@ out:
|
|
|
} /* pcmcia_release_io */
|
|
|
|
|
|
|
|
|
+/**
|
|
|
+ * pcmcia_release_window() - release reserved iomem for PCMCIA devices
|
|
|
+ * @p_dev: pcmcia device
|
|
|
+ * @res: iomem resource to release
|
|
|
+ *
|
|
|
+ * pcmcia_release_window() releases &struct resource *res which was
|
|
|
+ * previously reserved by calling pcmcia_request_window().
|
|
|
+ */
|
|
|
int pcmcia_release_window(struct pcmcia_device *p_dev, struct resource *res)
|
|
|
{
|
|
|
struct pcmcia_socket *s = p_dev->socket;
|
|
@@ -420,6 +462,8 @@ int pcmcia_release_window(struct pcmcia_device *p_dev, struct resource *res)
|
|
|
kfree(win->res);
|
|
|
win->res = NULL;
|
|
|
}
|
|
|
+ res->start = res->end = 0;
|
|
|
+ res->flags = IORESOURCE_MEM;
|
|
|
p_dev->_win &= ~CLIENT_WIN_REQ(w);
|
|
|
mutex_unlock(&s->ops_mutex);
|
|
|
|
|
@@ -428,23 +472,30 @@ int pcmcia_release_window(struct pcmcia_device *p_dev, struct resource *res)
|
|
|
EXPORT_SYMBOL(pcmcia_release_window);
|
|
|
|
|
|
|
|
|
-int pcmcia_request_configuration(struct pcmcia_device *p_dev,
|
|
|
- config_req_t *req)
|
|
|
+/**
|
|
|
+ * pcmcia_enable_device() - set up and activate a PCMCIA device
|
|
|
+ * @p_dev: the associated PCMCIA device
|
|
|
+ *
|
|
|
+ * pcmcia_enable_device() physically enables a PCMCIA device. It parses
|
|
|
+ * the flags passed to in @flags and stored in @p_dev->flags and sets up
|
|
|
+ * the Vpp voltage, enables the speaker line, I/O ports and store proper
|
|
|
+ * values to configuration registers.
|
|
|
+ */
|
|
|
+int pcmcia_enable_device(struct pcmcia_device *p_dev)
|
|
|
{
|
|
|
int i;
|
|
|
- u_int base;
|
|
|
+ unsigned int base;
|
|
|
struct pcmcia_socket *s = p_dev->socket;
|
|
|
config_t *c;
|
|
|
pccard_io_map iomap;
|
|
|
+ unsigned char status = 0;
|
|
|
+ unsigned char ext_status = 0;
|
|
|
+ unsigned char option = 0;
|
|
|
+ unsigned int flags = p_dev->config_flags;
|
|
|
|
|
|
if (!(s->state & SOCKET_PRESENT))
|
|
|
return -ENODEV;
|
|
|
|
|
|
- if (req->IntType & INT_CARDBUS) {
|
|
|
- dev_dbg(&p_dev->dev, "IntType may not be INT_CARDBUS\n");
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
-
|
|
|
mutex_lock(&s->ops_mutex);
|
|
|
c = p_dev->function_config;
|
|
|
if (c->state & CONFIG_LOCKED) {
|
|
@@ -454,7 +505,7 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
|
|
|
}
|
|
|
|
|
|
/* Do power control. We don't allow changes in Vcc. */
|
|
|
- s->socket.Vpp = req->Vpp;
|
|
|
+ s->socket.Vpp = p_dev->vpp;
|
|
|
if (s->ops->set_socket(s, &s->socket)) {
|
|
|
mutex_unlock(&s->ops_mutex);
|
|
|
dev_printk(KERN_WARNING, &p_dev->dev,
|
|
@@ -463,64 +514,72 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
|
|
|
}
|
|
|
|
|
|
/* Pick memory or I/O card, DMA mode, interrupt */
|
|
|
- c->IntType = req->IntType;
|
|
|
- c->Attributes = req->Attributes;
|
|
|
- if (req->IntType & INT_MEMORY_AND_IO)
|
|
|
+ if (p_dev->_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)
|
|
|
+ if (flags & CONF_ENABLE_SPKR) {
|
|
|
s->socket.flags |= SS_SPKR_ENA;
|
|
|
- if (req->Attributes & CONF_ENABLE_IRQ)
|
|
|
+ status = CCSR_AUDIO_ENA;
|
|
|
+ if (!(p_dev->config_regs & PRESENT_STATUS))
|
|
|
+ dev_warn(&p_dev->dev, "speaker requested, but "
|
|
|
+ "PRESENT_STATUS not set!\n");
|
|
|
+ }
|
|
|
+ if (flags & CONF_ENABLE_IRQ)
|
|
|
s->socket.io_irq = s->pcmcia_irq;
|
|
|
else
|
|
|
s->socket.io_irq = 0;
|
|
|
+ if (flags & CONF_ENABLE_ESR) {
|
|
|
+ p_dev->config_regs |= PRESENT_EXT_STATUS;
|
|
|
+ ext_status = ESR_REQ_ATTN_ENA;
|
|
|
+ }
|
|
|
s->ops->set_socket(s, &s->socket);
|
|
|
s->lock_count++;
|
|
|
|
|
|
+ dev_dbg(&p_dev->dev,
|
|
|
+ "enable_device: V %d, flags %x, base %x, regs %x, idx %x\n",
|
|
|
+ p_dev->vpp, flags, p_dev->config_base, p_dev->config_regs,
|
|
|
+ p_dev->config_index);
|
|
|
+
|
|
|
/* Set up CIS configuration registers */
|
|
|
- base = c->ConfigBase = req->ConfigBase;
|
|
|
- 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) {
|
|
|
+ base = p_dev->config_base;
|
|
|
+ if (p_dev->config_regs & PRESENT_COPY) {
|
|
|
+ u16 tmp = 0;
|
|
|
+ dev_dbg(&p_dev->dev, "clearing CISREG_SCR\n");
|
|
|
+ pcmcia_write_cis_mem(s, 1, (base + CISREG_SCR)>>1, 1, &tmp);
|
|
|
+ }
|
|
|
+ if (p_dev->config_regs & PRESENT_PIN_REPLACE) {
|
|
|
+ u16 tmp = 0;
|
|
|
+ dev_dbg(&p_dev->dev, "clearing CISREG_PRR\n");
|
|
|
+ pcmcia_write_cis_mem(s, 1, (base + CISREG_PRR)>>1, 1, &tmp);
|
|
|
+ }
|
|
|
+ if (p_dev->config_regs & PRESENT_OPTION) {
|
|
|
if (s->functions == 1) {
|
|
|
- c->Option = req->ConfigIndex & COR_CONFIG_MASK;
|
|
|
+ option = p_dev->config_index & 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;
|
|
|
+ option = p_dev->config_index & COR_MFC_CONFIG_MASK;
|
|
|
+ option |= COR_FUNC_ENA|COR_IREQ_ENA;
|
|
|
+ if (p_dev->config_regs & PRESENT_IOBASE_0)
|
|
|
+ option |= COR_ADDR_DECODE;
|
|
|
}
|
|
|
- if ((req->Attributes & CONF_ENABLE_IRQ) &&
|
|
|
- !(req->Attributes & CONF_ENABLE_PULSE_IRQ))
|
|
|
- c->Option |= COR_LEVEL_REQ;
|
|
|
- pcmcia_write_cis_mem(s, 1, (base + CISREG_COR)>>1, 1, &c->Option);
|
|
|
+ if ((flags & CONF_ENABLE_IRQ) &&
|
|
|
+ !(flags & CONF_ENABLE_PULSE_IRQ))
|
|
|
+ option |= COR_LEVEL_REQ;
|
|
|
+ pcmcia_write_cis_mem(s, 1, (base + CISREG_COR)>>1, 1, &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) {
|
|
|
+ if (p_dev->config_regs & PRESENT_STATUS)
|
|
|
+ pcmcia_write_cis_mem(s, 1, (base + CISREG_CCSR)>>1, 1, &status);
|
|
|
+
|
|
|
+ if (p_dev->config_regs & PRESENT_EXT_STATUS)
|
|
|
+ pcmcia_write_cis_mem(s, 1, (base + CISREG_ESR)>>1, 1,
|
|
|
+ &ext_status);
|
|
|
+
|
|
|
+ if (p_dev->config_regs & PRESENT_IOBASE_0) {
|
|
|
u8 b = c->io[0].start & 0xff;
|
|
|
pcmcia_write_cis_mem(s, 1, (base + CISREG_IOBASE_0)>>1, 1, &b);
|
|
|
b = (c->io[0].start >> 8) & 0xff;
|
|
|
pcmcia_write_cis_mem(s, 1, (base + CISREG_IOBASE_1)>>1, 1, &b);
|
|
|
}
|
|
|
- if (req->Present & PRESENT_IOSIZE) {
|
|
|
+ if (p_dev->config_regs & PRESENT_IOSIZE) {
|
|
|
u8 b = resource_size(&c->io[0]) + resource_size(&c->io[1]) - 1;
|
|
|
pcmcia_write_cis_mem(s, 1, (base + CISREG_IOSIZE)>>1, 1, &b);
|
|
|
}
|
|
@@ -551,14 +610,15 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
|
|
|
p_dev->_locked = 1;
|
|
|
mutex_unlock(&s->ops_mutex);
|
|
|
return 0;
|
|
|
-} /* pcmcia_request_configuration */
|
|
|
-EXPORT_SYMBOL(pcmcia_request_configuration);
|
|
|
+} /* pcmcia_enable_device */
|
|
|
+EXPORT_SYMBOL(pcmcia_enable_device);
|
|
|
|
|
|
|
|
|
/**
|
|
|
* pcmcia_request_io() - attempt to reserve port ranges for PCMCIA devices
|
|
|
+ * @p_dev: the associated PCMCIA device
|
|
|
*
|
|
|
- * pcmcia_request_io() attepts to reserve the IO port ranges specified in
|
|
|
+ * pcmcia_request_io() attempts to reserve the IO port ranges specified in
|
|
|
* &struct pcmcia_device @p_dev->resource[0] and @p_dev->resource[1]. The
|
|
|
* "start" value is the requested start of the IO port resource; "end"
|
|
|
* reflects the number of ports requested. The number of IO lines requested
|
|
@@ -622,11 +682,13 @@ EXPORT_SYMBOL(pcmcia_request_io);
|
|
|
|
|
|
/**
|
|
|
* pcmcia_request_irq() - attempt to request a IRQ for a PCMCIA device
|
|
|
+ * @p_dev: the associated PCMCIA device
|
|
|
+ * @handler: IRQ handler to register
|
|
|
*
|
|
|
- * pcmcia_request_irq() is a wrapper around request_irq which will allow
|
|
|
+ * pcmcia_request_irq() is a wrapper around request_irq() which allows
|
|
|
* the PCMCIA core to clean up the registration in pcmcia_disable_device().
|
|
|
* Drivers are free to use request_irq() directly, but then they need to
|
|
|
- * call free_irq themselfves, too. Also, only IRQF_SHARED capable IRQ
|
|
|
+ * call free_irq() themselfves, too. Also, only %IRQF_SHARED capable IRQ
|
|
|
* handlers are allowed.
|
|
|
*/
|
|
|
int __must_check pcmcia_request_irq(struct pcmcia_device *p_dev,
|
|
@@ -649,12 +711,14 @@ EXPORT_SYMBOL(pcmcia_request_irq);
|
|
|
|
|
|
/**
|
|
|
* pcmcia_request_exclusive_irq() - attempt to request an exclusive IRQ first
|
|
|
+ * @p_dev: the associated PCMCIA device
|
|
|
+ * @handler: IRQ handler to register
|
|
|
*
|
|
|
- * pcmcia_request_exclusive_irq() is a wrapper around request_irq which
|
|
|
+ * pcmcia_request_exclusive_irq() is a wrapper around request_irq() which
|
|
|
* attempts first to request an exclusive IRQ. If it fails, it also accepts
|
|
|
* a shared IRQ, but prints out a warning. PCMCIA drivers should allow for
|
|
|
* IRQ sharing and either use request_irq directly (then they need to call
|
|
|
- * free_irq themselves, too), or the pcmcia_request_irq() function.
|
|
|
+ * free_irq() themselves, too), or the pcmcia_request_irq() function.
|
|
|
*/
|
|
|
int __must_check
|
|
|
__pcmcia_request_exclusive_irq(struct pcmcia_device *p_dev,
|
|
@@ -795,38 +859,47 @@ int pcmcia_setup_irq(struct pcmcia_device *p_dev)
|
|
|
}
|
|
|
|
|
|
|
|
|
-/** pcmcia_request_window
|
|
|
+/**
|
|
|
+ * pcmcia_request_window() - attempt to reserve iomem for PCMCIA devices
|
|
|
+ * @p_dev: the associated PCMCIA device
|
|
|
+ * @res: &struct resource pointing to p_dev->resource[2..5]
|
|
|
+ * @speed: access speed
|
|
|
*
|
|
|
- * Request_window() establishes a mapping between card memory space
|
|
|
- * and system memory space.
|
|
|
+ * pcmcia_request_window() attepts to reserve an iomem ranges specified in
|
|
|
+ * &struct resource @res pointing to one of the entries in
|
|
|
+ * &struct pcmcia_device @p_dev->resource[2..5]. The "start" value is the
|
|
|
+ * requested start of the IO mem resource; "end" reflects the size
|
|
|
+ * requested.
|
|
|
*/
|
|
|
-int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_handle_t *wh)
|
|
|
+int pcmcia_request_window(struct pcmcia_device *p_dev, struct resource *res,
|
|
|
+ unsigned int speed)
|
|
|
{
|
|
|
struct pcmcia_socket *s = p_dev->socket;
|
|
|
pccard_mem_map *win;
|
|
|
u_long align;
|
|
|
- struct resource *res;
|
|
|
int w;
|
|
|
|
|
|
+ dev_dbg(&p_dev->dev, "request_window %pR %d\n", res, speed);
|
|
|
+
|
|
|
if (!(s->state & SOCKET_PRESENT)) {
|
|
|
dev_dbg(&p_dev->dev, "No card present\n");
|
|
|
return -ENODEV;
|
|
|
}
|
|
|
|
|
|
/* Window size defaults to smallest available */
|
|
|
- if (req->Size == 0)
|
|
|
- req->Size = s->map_size;
|
|
|
- align = (s->features & SS_CAP_MEM_ALIGN) ? req->Size : s->map_size;
|
|
|
- if (req->Size & (s->map_size-1)) {
|
|
|
+ if (res->end == 0)
|
|
|
+ res->end = s->map_size;
|
|
|
+ align = (s->features & SS_CAP_MEM_ALIGN) ? res->end : s->map_size;
|
|
|
+ if (res->end & (s->map_size-1)) {
|
|
|
dev_dbg(&p_dev->dev, "invalid map size\n");
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
- if ((req->Base && (s->features & SS_CAP_STATIC_MAP)) ||
|
|
|
- (req->Base & (align-1))) {
|
|
|
+ if ((res->start && (s->features & SS_CAP_STATIC_MAP)) ||
|
|
|
+ (res->start & (align-1))) {
|
|
|
dev_dbg(&p_dev->dev, "invalid base address\n");
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
- if (req->Base)
|
|
|
+ if (res->start)
|
|
|
align = 0;
|
|
|
|
|
|
/* Allocate system memory window */
|
|
@@ -843,7 +916,7 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha
|
|
|
win = &s->win[w];
|
|
|
|
|
|
if (!(s->features & SS_CAP_STATIC_MAP)) {
|
|
|
- win->res = pcmcia_find_mem_region(req->Base, req->Size, align,
|
|
|
+ win->res = pcmcia_find_mem_region(res->start, res->end, align,
|
|
|
0, s);
|
|
|
if (!win->res) {
|
|
|
dev_dbg(&p_dev->dev, "allocating mem region failed\n");
|
|
@@ -855,8 +928,8 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha
|
|
|
|
|
|
/* Configure the socket controller */
|
|
|
win->map = w+1;
|
|
|
- win->flags = req->Attributes;
|
|
|
- win->speed = req->AccessSpeed;
|
|
|
+ win->flags = res->flags & WIN_FLAGS_MAP;
|
|
|
+ win->speed = speed;
|
|
|
win->card_start = 0;
|
|
|
|
|
|
if (s->ops->set_mem_map(s, win) != 0) {
|
|
@@ -868,17 +941,14 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha
|
|
|
|
|
|
/* Return window handle */
|
|
|
if (s->features & SS_CAP_STATIC_MAP)
|
|
|
- req->Base = win->static_start;
|
|
|
+ res->start = win->static_start;
|
|
|
else
|
|
|
- req->Base = win->res->start;
|
|
|
+ res->start = win->res->start;
|
|
|
|
|
|
/* convert to new-style resources */
|
|
|
- res = p_dev->resource[w + MAX_IO_WIN];
|
|
|
- res->start = req->Base;
|
|
|
- res->end = req->Base + req->Size - 1;
|
|
|
- res->flags &= ~IORESOURCE_BITS;
|
|
|
- res->flags |= (req->Attributes & WIN_FLAGS_MAP) | (win->map << 2);
|
|
|
- res->flags |= IORESOURCE_MEM;
|
|
|
+ res->end += res->start - 1;
|
|
|
+ res->flags &= ~WIN_FLAGS_REQ;
|
|
|
+ res->flags |= (win->map << 2) | IORESOURCE_MEM;
|
|
|
res->parent = win->res;
|
|
|
if (win->res)
|
|
|
request_resource(&iomem_resource, res);
|
|
@@ -886,15 +956,30 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha
|
|
|
dev_dbg(&p_dev->dev, "request_window results in %pR\n", res);
|
|
|
|
|
|
mutex_unlock(&s->ops_mutex);
|
|
|
- *wh = res;
|
|
|
|
|
|
return 0;
|
|
|
} /* pcmcia_request_window */
|
|
|
EXPORT_SYMBOL(pcmcia_request_window);
|
|
|
|
|
|
+
|
|
|
+/**
|
|
|
+ * pcmcia_disable_device() - disable and clean up a PCMCIA device
|
|
|
+ * @p_dev: the associated PCMCIA device
|
|
|
+ *
|
|
|
+ * pcmcia_disable_device() is the driver-callable counterpart to
|
|
|
+ * pcmcia_enable_device(): If a PCMCIA device is no longer used,
|
|
|
+ * drivers are expected to clean up and disable the device by calling
|
|
|
+ * this function. Any I/O ranges (iomem and ioports) will be released,
|
|
|
+ * the Vpp voltage will be set to 0, and IRQs will no longer be
|
|
|
+ * generated -- at least if there is no other card function (of
|
|
|
+ * multifunction devices) being used.
|
|
|
+ */
|
|
|
void pcmcia_disable_device(struct pcmcia_device *p_dev)
|
|
|
{
|
|
|
int i;
|
|
|
+
|
|
|
+ dev_dbg(&p_dev->dev, "disabling device\n");
|
|
|
+
|
|
|
for (i = 0; i < MAX_WIN; i++) {
|
|
|
struct resource *res = p_dev->resource[MAX_IO_WIN + i];
|
|
|
if (res->flags & WIN_FLAGS_REQ)
|