patch-2.3.17 linux/drivers/pcmcia/cardbus.c

Next file: linux/drivers/pcmcia/cb_enabler.c
Previous file: linux/drivers/pcmcia/bulkmem.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.16/linux/drivers/pcmcia/cardbus.c linux/drivers/pcmcia/cardbus.c
@@ -0,0 +1,638 @@
+/*======================================================================
+  
+    Cardbus device configuration
+    
+    cardbus.c 1.57 1999/09/07 15:19:32
+
+    The contents of this file are subject to the Mozilla Public
+    License Version 1.1 (the "License"); you may not use this file
+    except in compliance with the License. You may obtain a copy of
+    the License at http://www.mozilla.org/MPL/
+
+    Software distributed under the License is distributed on an "AS
+    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+    implied. See the License for the specific language governing
+    rights and limitations under the License.
+
+    The initial developer of the original code is David A. Hinds
+    <dhinds@hyper.stanford.edu>.  Portions created by David A. Hinds
+    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+
+    Alternatively, the contents of this file may be used under the
+    terms of the GNU Public License version 2 (the "GPL"), in which
+    case the provisions of the GPL are applicable instead of the
+    above.  If you wish to allow the use of your version of this file
+    only under the terms of the GPL and not to allow others to use
+    your version of this file under the MPL, indicate your decision
+    by deleting the provisions above and replace them with the notice
+    and other provisions required by the GPL.  If you do not delete
+    the provisions above, a recipient may use your version of this
+    file under either the MPL or the GPL.
+    
+    These routines handle allocating resources for Cardbus cards, as
+    well as setting up and shutting down Cardbus sockets.  They are
+    called from cs.c in response to Request/ReleaseConfiguration and
+    Request/ReleaseIO calls.
+    
+======================================================================*/
+
+#define __NO_VERSION__
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+
+#ifndef PCMCIA_DEBUG
+#define PCMCIA_DEBUG 1
+#endif
+static int pc_debug = PCMCIA_DEBUG;
+
+#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 "cs_internal.h"
+#include "rsrc_mgr.h"
+
+/*====================================================================*/
+
+#define FIND_FIRST_BIT(n)	((n) - ((n) & ((n)-1)))
+
+#define pci_readb		pcibios_read_config_byte
+#define pci_writeb		pcibios_write_config_byte
+#define pci_readw		pcibios_read_config_word
+#define pci_writew		pcibios_write_config_word
+#define pci_readl		pcibios_read_config_dword
+#define pci_writel		pcibios_write_config_dword
+
+#define CB_BAR(n)		(PCI_BASE_ADDRESS_0+(4*(n)))
+#define CB_ROM_BASE		0x0030
+
+/* Offsets in the Expansion ROM Image Header */
+#define ROM_SIGNATURE		0x0000	/* 2 bytes */
+#define ROM_DATA_PTR		0x0018	/* 2 bytes */
+
+/* Offsets in the CardBus PC Card Data Structure */
+#define PCDATA_SIGNATURE	0x0000	/* 4 bytes */
+#define PCDATA_VPD_PTR		0x0008	/* 2 bytes */
+#define PCDATA_LENGTH		0x000a	/* 2 bytes */
+#define PCDATA_REVISION		0x000c
+#define PCDATA_IMAGE_SZ		0x0010	/* 2 bytes */
+#define PCDATA_ROM_LEVEL	0x0012	/* 2 bytes */
+#define PCDATA_CODE_TYPE	0x0014
+#define PCDATA_INDICATOR	0x0015
+
+typedef struct cb_config_t {
+    u_int		size[7];
+    struct pci_dev	dev;
+} cb_config_t;
+
+#define BASE(dev,n)	((dev).resource[n].start)
+#define ROM(dev)	((dev).resource[6].start)
+
+/* There are three classes of bridge maps: IO ports,
+   non-prefetchable memory, and prefetchable memory */
+typedef enum { B_IO, B_M1, B_M2 } bridge_type;
+
+/*=====================================================================
+
+    Expansion ROM's have a special layout, and pointers specify an
+    image number and an offset within that image.  check_rom()
+    verifies that the expansion ROM exists and has the standard
+    layout.  xlate_rom_addr() converts an image/offset address to an
+    absolute offset from the ROM's base address.
+    
+=====================================================================*/
+
+static int check_rom(u_char *b, u_long len)
+{
+    u_int img = 0, ofs = 0, sz;
+    u_short data;
+    DEBUG(0, "ROM image dump:\n");
+    while ((readb(b) == 0x55) && (readb(b+1) == 0xaa)) {
+	data = readb(b+ROM_DATA_PTR) +
+	    (readb(b+ROM_DATA_PTR+1) << 8);
+	sz = 512 * (readb(b+data+PCDATA_IMAGE_SZ) +
+		    (readb(b+data+PCDATA_IMAGE_SZ+1) << 8));
+	DEBUG(0, "  image %d: 0x%06x-0x%06x, signature %c%c%c%c\n",
+	      img, ofs, ofs+sz-1,
+	      readb(b+data+PCDATA_SIGNATURE),
+	      readb(b+data+PCDATA_SIGNATURE+1),
+	      readb(b+data+PCDATA_SIGNATURE+2),
+	      readb(b+data+PCDATA_SIGNATURE+3));
+	ofs += sz; img++;
+	if ((readb(b+data+PCDATA_INDICATOR) & 0x80) ||
+	    (sz == 0) || (ofs >= len)) break;
+	b += sz;
+    }
+    return img;
+}
+
+static u_int xlate_rom_addr(u_char *b, u_int addr)
+{
+    u_int img = 0, ofs = 0, sz;
+    u_short data;
+    while ((readb(b) == 0x55) && (readb(b+1) == 0xaa)) {
+	if (img == (addr >> 28))
+	    return (addr & 0x0fffffff) + ofs;
+	data = readb(b+ROM_DATA_PTR) + (readb(b+ROM_DATA_PTR+1) << 8);
+	sz = 512 * (readb(b+data+PCDATA_IMAGE_SZ) +
+		    (readb(b+data+PCDATA_IMAGE_SZ+1) << 8));
+	if ((sz == 0) || (readb(b+data+PCDATA_INDICATOR) & 0x80))
+	    break;
+	b += sz; ofs += sz; img++;
+    }
+    return 0;
+}
+
+/*=====================================================================
+
+    These are similar to setup_cis_mem and release_cis_mem for 16-bit
+    cards.  The "result" that is used externally is the cb_cis_virt
+    pointer in the socket_info_t structure.
+    
+=====================================================================*/
+
+int cb_setup_cis_mem(socket_info_t *s, int space)
+{
+    cb_bridge_map *m = &s->cb_cis_map;
+    u_long base = 0;
+    u_int sz, br;
+
+    if (space == s->cb_cis_space)
+	return CS_SUCCESS;
+    else if (s->cb_cis_space != 0)
+	cb_release_cis_mem(s);
+    DEBUG(1, "cs: cb_setup_cis_mem(space %d)\n", space);
+    /* If socket is configured, then use existing memory mapping */
+    if (s->lock_count) {
+	s->cb_cis_virt =
+	    ioremap(BASE(s->cb_config[0].dev, space-1),
+		    s->cb_config[0].size[space-1] & ~3);
+	s->cb_cis_space = space;
+	return CS_SUCCESS;
+    }
+    
+    /* Not configured?  Then set up temporary map */
+    br = (space == 7) ? CB_ROM_BASE : CB_BAR(space-1);
+    pci_writel(s->cap.cardbus, 0, br, 0xffffffff);
+    pci_readl(s->cap.cardbus, 0, br, &sz);
+    sz &= PCI_BASE_ADDRESS_MEM_MASK;
+    sz = FIND_FIRST_BIT(sz);
+    if (sz < PAGE_SIZE) sz = PAGE_SIZE;
+    if (find_mem_region(&base, sz, "cb_enabler", sz, 0) != 0) {
+	printk(KERN_NOTICE "cs: could not allocate %dK memory for"
+	       " CardBus socket %d\n", sz/1024, s->sock);
+	return CS_OUT_OF_RESOURCE;
+    }
+    s->cb_cis_space = space;
+    s->cb_cis_virt = ioremap(base, sz);
+    DEBUG(1, "  phys 0x%08lx-0x%08lx, virt 0x%08lx\n",
+	  base, base+sz-1, (u_long)s->cb_cis_virt);
+    pci_writel(s->cap.cardbus, 0, br, base | 1);
+    pci_writeb(s->cap.cardbus, 0, PCI_COMMAND, PCI_COMMAND_MEMORY);
+    m->map = 0; m->flags = MAP_ACTIVE;
+    m->start = base; m->stop = base+sz-1;
+    s->ss_entry(s->sock, SS_SetBridge, m);
+    if ((space == 7) && (check_rom(s->cb_cis_virt, sz) == 0)) {
+	printk(KERN_NOTICE "cs: no valid ROM images found!\n");
+	return CS_READ_FAILURE;
+    }
+    return CS_SUCCESS;
+}
+
+void cb_release_cis_mem(socket_info_t *s)
+{
+    cb_bridge_map *m = &s->cb_cis_map;
+    u_int br;
+    if (s->cb_cis_virt) {
+	DEBUG(1, "cs: cb_release_cis_mem()\n");
+	iounmap(s->cb_cis_virt);
+	s->cb_cis_virt = NULL;
+	s->cb_cis_space = 0;
+    }
+    if (m->start) {
+	/* This is overkill: we probably only need to release the
+	   memory region, but the rest should be safe */
+	br = (s->cb_cis_space == 7) ?
+	    CB_ROM_BASE : CB_BAR(s->cb_cis_space-1);
+	m->map = 0; m->flags = 0;
+	s->ss_entry(s->sock, SS_SetBridge, m);
+	pci_writeb(s->cap.cardbus, 0, PCI_COMMAND, 0);
+	pci_writel(s->cap.cardbus, 0, br, 0);
+	release_mem_region(m->start, m->stop - m->start + 1);
+	m->start = 0;
+    }
+}
+
+/*=====================================================================
+
+    This is used by the CIS processing code to read CIS information
+    from a CardBus device.
+    
+=====================================================================*/
+
+void read_cb_mem(socket_info_t *s, u_char fn, int space,
+		 u_int addr, u_int len, void *ptr)
+{
+    DEBUG(3, "cs: read_cb_mem(%d, %#x, %u)\n", space, addr, len);
+    if (space == 0) {
+	if (addr+len > 0x100) goto fail;
+	for (; len; addr++, ptr++, len--)
+	    pci_readb(s->cap.cardbus, fn, addr, (u_char *)ptr);
+    } else {
+	if (cb_setup_cis_mem(s, space) != 0) goto fail;
+	if (space == 7) {
+	    addr = xlate_rom_addr(s->cb_cis_virt, addr);
+	    if (addr == 0) goto fail;
+	}
+	if (addr+len > s->cb_cis_map.stop - s->cb_cis_map.start)
+	    goto fail;
+	if (s->cb_cis_virt != NULL)
+	    for (; len; addr++, ptr++, len--)
+		*(u_char *)ptr = readb(s->cb_cis_virt+addr);
+    }
+    return;
+ fail:
+    memset(ptr, 0xff, len);
+    return;
+}
+
+/*=====================================================================
+
+    cb_alloc() and cb_free() allocate and free the kernel data
+    structures for a Cardbus device, and handle the lowest level PCI
+    device setup issues.
+    
+=====================================================================*/
+
+int cb_alloc(socket_info_t *s)
+{
+    struct pci_dev tmp;
+    u_short vend, v, dev;
+    u_char i, hdr, fn, bus = s->cap.cardbus;
+    cb_config_t *c;
+
+    tmp.bus = s->cap.cb_bus; tmp.devfn = 0;
+    tmp.next = pci_devices; pci_devices = &tmp;
+
+    pci_readw(bus, 0, PCI_VENDOR_ID, &vend);
+    pci_readw(bus, 0, PCI_DEVICE_ID, &dev);
+    printk(KERN_INFO "cs: cb_alloc(bus %d): vendor 0x%04x, "
+	   "device 0x%04x\n", bus, vend, dev);
+
+    pci_readb(bus, 0, PCI_HEADER_TYPE, &hdr);
+    if (hdr & 0x80) {
+	/* Count functions */
+	for (fn = 0; fn < 8; fn++) {
+	    tmp.devfn = fn;
+	    pci_readw(bus, fn, PCI_VENDOR_ID, &v);
+	    if (v != vend) break;
+	}
+    } else fn = 1;
+    s->functions = fn;
+    
+    c = kmalloc(fn * sizeof(struct cb_config_t), GFP_ATOMIC);
+    if (!c) return CS_OUT_OF_RESOURCE;
+    memset(c, 0, fn * sizeof(struct cb_config_t));
+    s->cb_config = c;
+
+    pci_devices = tmp.next;
+    for (i = 0; i < fn; i++) {
+	c[i].dev.bus = s->cap.cb_bus;
+	c[i].dev.devfn = i;
+	if (i < fn-1) {
+	    c[i].dev.sibling = c[i].dev.next = &c[i+1].dev;
+	}
+    }
+    s->cap.cb_bus->devices = &c[0].dev;
+    /* Link into PCI device chain */
+    c[s->functions-1].dev.next = pci_devices;
+    pci_devices = &c[0].dev;
+    for (i = 0; i < fn; i++) {
+	c[i].dev.vendor = vend;
+	pci_readw(bus, i, PCI_DEVICE_ID, &c[i].dev.device);
+	pci_readl(bus, i, PCI_CLASS_REVISION, &c[i].dev.class);
+	c[i].dev.class >>= 8;
+	c[i].dev.hdr_type = hdr;
+	pci_proc_attach_device(&c[i].dev);
+    }
+    
+    return CS_SUCCESS;
+}
+
+void cb_free(socket_info_t *s)
+{
+    struct pci_dev **p, *q;
+    cb_config_t *c = s->cb_config;
+
+    if (c) {
+	/* Unlink from PCI device chain */
+	for (p = &pci_devices; *p; p = &((*p)->next))
+	    if (*p == &c[0].dev) break;
+	for (q = *p; q; q = q->next) {
+	    if (q->bus != (*p)->bus) break;
+	    pci_proc_detach_device(q);
+	}
+	if (*p) *p = q;
+	s->cap.cb_bus->devices = NULL;
+    }
+    printk(KERN_INFO "cs: cb_free(bus %d)\n", s->cap.cardbus);
+    if (s->cb_config) {
+	kfree(s->cb_config);
+	s->cb_config = NULL;
+    }
+}
+
+/*=====================================================================
+
+    cb_config() has the job of allocating all system resources that
+    a Cardbus card requires.  Rather than using the CIS (which seems
+    to not always be present), it treats the card as an ordinary PCI
+    device, and probes the base address registers to determine each
+    function's IO and memory space needs.
+
+    It is called from the RequestIO card service.
+    
+======================================================================*/
+
+int cb_config(socket_info_t *s)
+{
+    cb_config_t *c = s->cb_config;
+    u_char fn = s->functions;
+    u_char i, j, bus = s->cap.cardbus, *name;
+    u_int sz, align, m, mask[3], num[3], base[3];
+    int irq, try, ret;
+
+    printk(KERN_INFO "cs: cb_config(bus %d)\n", s->cap.cardbus);
+
+    /* Determine IO and memory space needs */
+    num[B_IO] = num[B_M1] = num[B_M2] = 0;
+    mask[B_IO] = mask[B_M1] = mask[B_M2] = 0;
+    for (i = 0; i < fn; i++) {
+	for (j = 0; j < 6; j++) {
+	    pci_writel(bus, i, CB_BAR(j), 0xffffffff);
+	    pci_readl(bus, i, CB_BAR(j), &sz);
+	    if (sz == 0) continue;
+	    if (sz & PCI_BASE_ADDRESS_SPACE) {
+		m = B_IO;
+		sz &= PCI_BASE_ADDRESS_IO_MASK;
+	    } else {
+		m = (sz & PCI_BASE_ADDRESS_MEM_PREFETCH) ? B_M2 : B_M1;
+		sz &= PCI_BASE_ADDRESS_MEM_MASK;
+	    }
+	    sz = FIND_FIRST_BIT(sz);
+	    c[i].size[j] = sz | m;
+	    if (m && (sz < PAGE_SIZE)) sz = PAGE_SIZE;
+	    num[m] += sz; mask[m] |= sz;
+	}
+	pci_writel(bus, i, CB_ROM_BASE, 0xffffffff);
+	pci_readl(bus, i, CB_ROM_BASE, &sz);
+	if (sz != 0) {
+	    sz = FIND_FIRST_BIT(sz & ~0x00000001);
+	    c[i].size[6] = sz | B_M1;
+	    if (sz < PAGE_SIZE) sz = PAGE_SIZE;
+	    num[B_M1] += sz; mask[B_M1] |= sz;
+	}
+    }
+
+    /* Allocate system resources */
+    name = "cb_enabler";
+    s->io[0].NumPorts = num[B_IO];
+    s->io[0].BasePort = 0;
+    if (num[B_IO]) {
+	if (find_io_region(&s->io[0].BasePort, num[B_IO], name) != 0) {
+	    printk(KERN_NOTICE "cs: could not allocate %d IO ports for"
+		   " CardBus socket %d\n", num[B_IO], s->sock);
+	    goto failed;
+	}
+	base[B_IO] = s->io[0].BasePort + num[B_IO];
+    }
+    s->win[0].size = num[B_M1];
+    s->win[0].base = 0;
+    if (num[B_M1]) {
+	if (find_mem_region(&s->win[0].base, num[B_M1],
+			    name, num[B_M1], 0) != 0) {
+	    printk(KERN_NOTICE "cs: could not allocate %dK memory for"
+		   " CardBus socket %d\n", num[B_M1]/1024, s->sock);
+	    goto failed;
+	}
+	base[B_M1] = s->win[0].base + num[B_M1];
+    }
+    s->win[1].size = num[B_M2];
+    s->win[1].base = 0;
+    if (num[B_M2]) {
+	if (find_mem_region(&s->win[1].base, num[B_M2],
+			    name, num[B_M2], 0) != 0) {
+	    printk(KERN_NOTICE "cs: could not allocate %dK memory for"
+		   " CardBus socket %d\n", num[B_M2]/1024, s->sock);
+	    goto failed;
+	}
+	base[B_M2] = s->win[1].base + num[B_M2];
+    }
+    
+    /* Set up base address registers */
+    while (mask[B_IO] | mask[B_M1] | mask[B_M2]) {
+	num[B_IO] = FIND_FIRST_BIT(mask[B_IO]); mask[B_IO] -= num[B_IO];
+	num[B_M1] = FIND_FIRST_BIT(mask[B_M1]); mask[B_M1] -= num[B_M1];
+	num[B_M2] = FIND_FIRST_BIT(mask[B_M2]); mask[B_M2] -= num[B_M2];
+	for (i = 0; i < fn; i++) {
+	    for (j = 0; j < 7; j++) {
+		sz = c[i].size[j];
+		m = sz & 3; sz &= ~3;
+		align = (m && (sz < PAGE_SIZE)) ? PAGE_SIZE : sz;
+		if (sz && (align == num[m])) {
+		    base[m] -= align;
+		    if (j < 6)
+			printk(KERN_INFO "  fn %d bar %d: ", i, j+1);
+		    else
+			printk(KERN_INFO "  fn %d rom: ", i);
+		    printk("%s 0x%x-0x%x\n", (m) ? "mem" : "io",
+			   base[m], base[m]+sz-1);
+		    BASE(c[i].dev, j) = base[m];
+		}
+	    }
+	}
+    }
+    
+    /* Allocate interrupt if needed */
+    s->irq.AssignedIRQ = irq = 0; ret = -1;
+    for (i = 0; i < fn; i++) {
+	pci_readb(bus, i, PCI_INTERRUPT_PIN, &j);
+	if (j == 0) continue;
+	if (irq == 0) {
+	    if (s->cap.irq_mask & (1 << s->cap.pci_irq)) {
+		irq = s->cap.pci_irq;
+		ret = 0;
+	    }
+#ifdef CONFIG_ISA
+	    else
+		for (try = 0; try < 2; try++) {
+		    for (irq = 0; irq < 32; irq++)
+			if ((s->cap.irq_mask >> irq) & 1) {
+			    ret = try_irq(IRQ_TYPE_EXCLUSIVE, irq, try);
+			    if (ret == 0) break;
+			}
+		    if (ret == 0) break;
+		}
+	    if (ret != 0) {
+		printk(KERN_NOTICE "cs: could not allocate interrupt"
+		       " for CardBus socket %d\n", s->sock);
+		goto failed;
+	    }
+#endif
+	    s->irq.AssignedIRQ = irq;
+	}
+    }
+    c[0].dev.irq = irq;
+    
+    return CS_SUCCESS;
+
+failed:
+    cb_release(s);
+    return CS_OUT_OF_RESOURCE;
+}
+
+/*======================================================================
+
+    cb_release() releases all the system resources (IO and memory
+    space, and interrupt) committed for a Cardbus card by a prior call
+    to cb_config().
+
+    It is called from the ReleaseIO() service.
+    
+======================================================================*/
+
+void cb_release(socket_info_t *s)
+{
+    cb_config_t *c = s->cb_config;
+    
+    DEBUG(0, "cs: cb_release(bus %d)\n", s->cap.cardbus);
+    
+    if (s->win[0].size > 0)
+	release_mem_region(s->win[0].base, s->win[0].size);
+    if (s->win[1].size > 0)
+	release_mem_region(s->win[1].base, s->win[1].size);
+    if (s->io[0].NumPorts > 0)
+	release_region(s->io[0].BasePort, s->io[0].NumPorts);
+    s->io[0].NumPorts = 0;
+#ifdef CONFIG_ISA
+    if ((c[0].dev.irq != 0) && (c[0].dev.irq != s->cap.pci_irq))
+	undo_irq(IRQ_TYPE_EXCLUSIVE, c[0].dev.irq);
+#endif
+}
+
+/*=====================================================================
+
+    cb_enable() has the job of configuring a socket for a Cardbus
+    card, and initializing the card's PCI configuration registers.
+
+    It first sets up the Cardbus bridge windows, for IO and memory
+    accesses.  Then, it initializes each card function's base address
+    registers, interrupt line register, and command register.
+
+    It is called as part of the RequestConfiguration card service.
+    It should be called after a previous call to cb_config() (via the
+    RequestIO service).
+    
+======================================================================*/
+
+void cb_enable(socket_info_t *s)
+{
+    u_char i, j, bus = s->cap.cardbus;
+    cb_config_t *c = s->cb_config;
+    
+    DEBUG(0, "cs: cb_enable(bus %d)\n", bus);
+    
+    /* Configure bridge */
+    if (s->cb_cis_map.start)
+	cb_release_cis_mem(s);
+    for (i = 0; i < 3; i++) {
+	cb_bridge_map m;
+	switch (i) {
+	case B_IO:
+	    m.map = 0; m.flags = MAP_IOSPACE | MAP_ACTIVE;
+	    m.start = s->io[0].BasePort;
+	    m.stop = m.start + s->io[0].NumPorts - 1;
+	    break;
+	case B_M1:
+	    m.map = 0; m.flags = MAP_ACTIVE;
+	    m.start = s->win[0].base;
+	    m.stop = m.start + s->win[0].size - 1;
+	    break;
+	case B_M2:
+	    m.map = 1; m.flags = MAP_PREFETCH | MAP_ACTIVE;
+	    m.start = s->win[1].base;
+	    m.stop = m.start + s->win[1].size - 1;
+	    break;
+	}
+	if (m.start == 0) continue;
+	DEBUG(0, "  bridge %s map %d (flags 0x%x): 0x%x-0x%x\n",
+	      (m.flags & MAP_IOSPACE) ? "io" : "mem",
+	      m.map, m.flags, m.start, m.stop);
+	s->ss_entry(s->sock, SS_SetBridge, &m);
+    }
+
+    /* Set up base address registers */
+    for (i = 0; i < s->functions; i++) {
+	for (j = 0; j < 6; j++) {
+	    if (BASE(c[i].dev, j) != 0)
+		pci_writel(bus, i, CB_BAR(j), BASE(c[i].dev, j));
+	}
+	if (ROM(c[i].dev) != 0)
+	    pci_writel(bus, i, CB_ROM_BASE, ROM(c[i].dev) | 1);
+    }
+
+    /* Set up PCI interrupt and command registers */
+    for (i = 0; i < s->functions; i++) {
+	pci_writeb(bus, i, PCI_COMMAND, PCI_COMMAND_MASTER |
+		   PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+	pci_writeb(bus, i, PCI_CACHE_LINE_SIZE, 8);
+    }
+    
+    if (s->irq.AssignedIRQ) {
+	for (i = 0; i < s->functions; i++)
+	    pci_writeb(bus, i, PCI_INTERRUPT_LINE,
+		       s->irq.AssignedIRQ);
+	s->socket.io_irq = s->irq.AssignedIRQ;
+	s->ss_entry(s->sock, SS_SetSocket, &s->socket);
+    }
+}
+
+/*======================================================================
+
+    cb_disable() unconfigures a Cardbus card previously set up by
+    cb_enable().
+
+    It is called from the ReleaseConfiguration service.
+    
+======================================================================*/
+
+void cb_disable(socket_info_t *s)
+{
+    u_char i;
+    cb_bridge_map m = { 0, 0, 0, 0xffff };
+    
+    DEBUG(0, "cs: cb_disable(bus %d)\n", s->cap.cardbus);
+    
+    /* Turn off bridge windows */
+    if (s->cb_cis_map.start)
+	cb_release_cis_mem(s);
+    for (i = 0; i < 3; i++) {
+	switch (i) {
+	case B_IO: m.map = 0; m.flags = MAP_IOSPACE; break;
+	case B_M1: m.map = m.flags = 0; break;
+	case B_M2: m.map = 1; m.flags = 0; break;
+	}
+	s->ss_entry(s->sock, SS_SetBridge, &m);
+    }
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)