/*
* cistpl.c -- 16-bit PCMCIA Card Information Structure parser
*
* 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.
*
* 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.
*
* (C) 1999 David A. Hinds
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/major.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/pci.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include <asm/byteorder.h>
#include <asm/unaligned.h>
#include <pcmcia/cs_types.h>
#include <pcmcia/ss.h>
#include <pcmcia/cs.h>
#include <pcmcia/cisreg.h>
#include <pcmcia/cistpl.h>
#include "cs_internal.h"
static const u_char mantissa[] = {
10, 12, 13, 15, 20, 25, 30, 35,
40, 45, 50, 55, 60, 70, 80, 90
};
static const u_int exponent[] = {
1, 10, 100, 1000, 10000, 100000, 1000000, 10000000
};
/* Convert an extended speed byte to a time in nanoseconds */
#define SPEED_CVT(v) \
(mantissa[(((v)>>3)&15)-1] * exponent[(v)&7] / 10)
/* Convert a power byte to a current in 0.1 microamps */
#define POWER_CVT(v) \
(mantissa[((v)>>3)&15] * exponent[(v)&7] / 10)
#define POWER_SCALE(v) (exponent[(v)&7])
/* Upper limit on reasonable # of tuples */
#define MAX_TUPLES 200
/*====================================================================*/
/* Parameters that can be set with 'insmod' */
/* 16-bit CIS? */
static int cis_width;
module_param(cis_width, int, 0444);
void release_cis_mem(struct pcmcia_socket *s)
{
if (s->cis_mem.flags & MAP_ACTIVE) {
s->cis_mem.flags &= ~MAP_ACTIVE;
s->ops->set_mem_map(s, &s->cis_mem);
if (s->cis_mem.res) {
release_resource(s->cis_mem.res);
kfree(s->cis_mem.res);
s->cis_mem.res = NULL;
}
iounmap(s->cis_virt);
s->cis_virt = NULL;
}
}
EXPORT_SYMBOL(release_cis_mem);
/*
* Map the card memory at "card_offset" into virtual space.
* If flags & MAP_ATTRIB, map the attribute space, otherwise
* map the memory space.
*/
static void __iomem *
set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flags)
{
pccard_mem_map *mem = &s->cis_mem;
int ret;
if (!(s->features & SS_CAP_STATIC_MAP) && (mem->res == NULL)) {
mem->res = pcmcia_find_mem_region(0, s->map_size, s->map_size, 0, s);
if (mem->res == NULL) {
dev_printk(KERN_NOTICE, &s->dev,
"cs: unable to map card memory!\n");
return NULL;
}
s->cis_virt = NULL;
}
if (!(s->features & SS_CAP_STATIC_MAP) && (!s->cis_virt))
s->cis_virt = ioremap(mem->res->start, s->map_size);
mem->card_start = card_offset;
mem->flags = flags;
ret = s->ops->set_mem_map(s, mem);
if (ret) {
iounmap(s->cis_virt);
s->cis_virt = NULL;
return NULL;
}
if (s->features & SS_CAP_STATIC_MAP) {
if (s->cis_virt)
iounmap(s->cis_virt);
s->cis_virt = ioremap(mem->static_start, s->map_size);
}
return s->cis_virt;
}
/*======================================================================
Low-level functions to read and write CIS memory. I think the
write routine is only useful for writing one-byte registers.
======================================================================*/
/* Bits in attr field */
#define IS_ATTR 1
#define IS_INDIRECT 8
int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
u_int len, void *ptr)
{
void __iomem *sys, *end;
unsigned char *buf = ptr;
cs_dbg(s, 3, "pcmcia_read_cis_mem(%d, %#x, %u)\n", attr, addr, len);
if (attr & IS_INDIRECT) {
/* Indirect accesses use a bunch of special registers at fixed
locations in common memory */
u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN;
if (attr & IS_ATTR) {
addr *= 2;
flags = ICTRL0_AUTOINC;
}
sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0));
if (!sys) {
memset(ptr, 0xff, len);
return -1;
}
writeb(flags, sys+CISREG_ICTRL0);
writeb(addr & 0xff, sys+CISREG_IADDR0);
writeb((addr>>8) & 0xff, sys+CISREG_IADDR1);
writeb((addr>>16) & 0xff, sys+CISREG_IADDR2);
writeb((addr>>24) & 0xff, sys+CISREG_IADDR3);
for ( ; len > 0; len--, buf++)
*buf = readb(sys+CISREG_IDATA0);
} else {
u_int inc = 1, card_offset, flags;
flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0);
if (attr) {
flags |= MAP_ATTRIB;
inc++;
addr *= 2;
}
card_offset = addr & ~(s->map_size-1);
while (len) {
sys = set_cis_map(s, card_offset, flags);
if (!sys) {
memset(ptr, 0xff, len);
return -1;
}
end = sys + s->map_size;
sys = sys + (addr & (s->map_size-1));
for ( ; len > 0; len--, buf++, sys += inc) {
if (sys == end)
break;
*buf = readb(sys);
}
card_offset += s->map_size;
addr = 0;
}
}
cs_dbg(s, 3, " %#2.2x %#2.2x %#2.2x %#2.2x ...\n",
*(u_char *)(ptr+0), *(u_char *)(ptr+1),
*(u_char *)(ptr+2), *(u_char *)(ptr+3));
return 0;
}
EXPORT_SYMBOL(pcmcia_read_cis_mem);
void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
u_int len, void *ptr)
{
void __iomem *sys, *end;
unsigned char *buf = ptr;
cs_dbg(s, 3, "pcmcia_write_cis_mem(%d, %#x, %u)\n", attr, addr, len);
if (attr & IS_INDIRECT) {
/* Indirect accesses use a bunch of special registers at fixed
locations in common memory */
u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN;
if (attr & IS_ATTR) {
addr *= 2;
flags = ICTRL0_AUTOINC;
}
sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0));
if (!sys)
return; /* FIXME: Error */
writeb(flags, sys+CISREG_ICTRL0);
writeb(addr & 0xff, sys+CISREG_IADDR0);
writeb((addr>>8) & 0xff, sys+CISREG_IADDR1);
writeb((addr>>16) & 0xff, sys+CISREG_IADDR2);
writeb((addr>>24) & 0xff, sys+CISREG_IADDR3);
for ( ;
|