/*
* 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-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
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/device.h>
#include <linux/netdevice.h>
#include <linux/slab.h>
#include <asm/irq.h>
#include <pcmcia/ss.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/cisreg.h>
#include <pcmcia/ds.h>
#include "cs_internal.h"
/* Access speed for IO windows */
static int io_speed;
module_param(io_speed, int, 0444);
int pcmcia_validate_mem(struct pcmcia_socket *s)
{
if (s->resource_ops->validate_mem)
return s->resource_ops->validate_mem(s);
/* if there is no callback, we can assume that everything is OK */
return 0;
}
struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align,
int low, struct pcmcia_socket *s)
{
if (s->resource_ops->find_mem)
return s->resource_ops->find_mem(base, num, align, low, s);
return NULL;
}
/**
* 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);
int i;
dev_dbg(&s->dev, "release_io_space for %pR\n", res);
for (i = 0; i < MAX_IO_WIN; i++) {
if (!s->io[i].res)
continue;
if ((s->io[i].res->start <= res->start) &&
(s->io[i].res->end >= res->end)) {
s->io[i].InUse -= num;
if (res->parent)
release_resource(res);
res->start = res->end = 0;
res->flags = IORESOURCE_IO;
/* Free the window if no one else is using it */
if (s->io[i].InUse == 0) {
release_resource(s->io[i].res);
kfree(s->io[i].res);
s->io[i].res = NULL;
}
}
}
}
/**
* 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
*/
static int alloc_io_space(struct pcmcia_socket *s, struct resource *res,
unsigned int lines)
{
unsigned int align;
unsigned int base = res->start;
unsigned int num = res->end;
int ret;
res->flags |= IORESOURCE_IO;
dev_dbg(&s->dev, "alloc_io_space request for %pR, %d lines\n",
res, lines);
align = base ? (lines ? 1<<lines : 0) : 1;
if (align && (align < num)) {
if (base) {
dev_dbg(&s->dev, "odd IO request\n");
align = 0;
} else
while (align && (align < num))
align <<= 1;
}
if (base & ~(align-1)) {
dev_dbg(&s->dev, "odd IO request\n");
align = 0;
}
ret = s->resource_ops->find_io(s, res->flags, &base, num, align,
&res->parent);
if (ret) {
dev_dbg(&s->dev, "alloc_io_space request failed (%d)\n", ret);
return -EINVAL;
}
res->start = base;
res->end = res->start + num - 1;
if (res->parent) {
ret = request_resource(res->parent, res);
if (ret) {
dev_warn(&s->dev,
"request_resource %pR failed: %d\n", res,