/*
* 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/cs_types.h>
#include <pcmcia/ss.h>
#include <pcmcia/cs.h>
#include <pcmcia/bulkmem.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/cisreg.h>
#include <pcmcia/ds.h>
#include "cs_internal.h"
#include "ds_internal.h"
/* Access speed for IO windows */
static int io_speed = 0;
module_param(io_speed, int, 0444);
#ifdef CONFIG_PCMCIA_PROBE
#include <asm/irq.h>
/* mask of IRQs already reserved by other cards, we should avoid using them */
static u8 pcmcia_used_irq[NR_IRQS];
#endif
#ifdef 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);
s->io[0].Attributes = attr;
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;
}
}