pcmcia: remove obsolete host controller drivers

PCMCIA is almost completely obsolete (the last computers supporting it
natively were from ~2009), and the general consensus [1] seems to be
that support for it should be gradually removed from the kernel.

In 2023, an initial step of removing all the PCMCIA char drivers was
taken in commit 9b12f050c7 ("char: pcmcia: remove all the drivers"),
and that has not been reverted, so it seems logical to continue this
process by removing more low-hanging fruit.

These host controller drivers have had no meaningful changes since
their status was discussed in 2022 [2], and are unlikely to have any
remaining users. Remove them and a couple references to them
in comments.

The i82365 and tcic drivers are for ISA-attached host controllers,
which are even less likely to be used nowadays than ones on other buses.

The i82092 driver has almost certainly not been used in over 20 years.
It was broken by a null pointer dereference since the dawn of Git
history (2.6.12-rc2 in 2005) until someone fixed it in 2021 in commit
e39cdacf2f ("pcmcia: i82092: fix a null pointer dereference bug").
From their dmesg log [3], it is clear they were testing in an emulated
environment and not on real hardware.

i82365.h is used by drivers other than i82365 and is therefore retained.

[1] https://lore.kernel.org/all/c5b39544-a4fb-4796-a046-0b9be9853787@app.fastmail.com/
[2] https://lore.kernel.org/all/Y07d7rMvd5++85BJ@owl.dominikbrodowski.net/
[3] https://lore.kernel.org/all/1624345891-4215-1-git-send-email-zheyuma97@gmail.com/

Signed-off-by: Ethan Nelson-Moore <enelsonmoore@gmail.com>
Acked-by: Dave Hansen <dave.hansen@linux.intel.com> # for x86
Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net>
This commit is contained in:
Ethan Nelson-Moore
2026-03-09 00:41:48 -07:00
committed by Dominik Brodowski
parent bfcde62405
commit b3c26ea81c
11 changed files with 2 additions and 3161 deletions

View File

@@ -15,7 +15,6 @@ CONFIG_PCI=y
CONFIG_PCCARD=m
CONFIG_YENTA=m
CONFIG_PD6729=m
CONFIG_I82092=m
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODVERSIONS=y

View File

@@ -72,8 +72,6 @@ CONFIG_PCI_MSI=y
CONFIG_PCCARD=y
CONFIG_YENTA=y
CONFIG_PD6729=m
CONFIG_I82092=m
CONFIG_I82365=m
CONFIG_ADVANCED_OPTIONS=y
CONFIG_NET=y
CONFIG_PACKET=y

View File

@@ -62,7 +62,7 @@ void arch_remove_reservations(struct resource *avail)
/*
* Trim out BIOS area (high 2MB) and E820 regions. We do not remove
* the low 1MB unconditionally, as this area is needed for some ISA
* cards requiring a memory range, e.g. the i82365 PCMCIA controller.
* cards requiring a memory range.
*/
if (avail->flags & IORESOURCE_MEM) {
resource_clip(avail, BIOS_ROM_BASE, BIOS_ROM_END);

View File

@@ -1296,9 +1296,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C597_0, quirk_vt
/*
* CardBus controllers have a legacy base address that enables them to
* respond as i82365 pcmcia controllers. We don't want them to do this
* even if the Linux CardBus driver is not loaded, because the Linux i82365
* driver does not (and should not) handle CardBus.
* respond as i82365 PCMCIA controllers. We don't want them to do this.
*/
static void quirk_cardbus_legacy(struct pci_dev *dev)
{

View File

@@ -119,36 +119,6 @@ config PD6729
This provides support for the Cirrus PD6729 PCI-to-PCMCIA bridge
device, found in some older laptops and PCMCIA card readers.
config I82092
tristate "i82092 compatible bridge support"
depends on PCMCIA && PCI && HAS_IOPORT
select PCCARD_NONSTATIC
help
This provides support for the Intel I82092AA PCI-to-PCMCIA bridge device,
found in some older laptops and more commonly in evaluation boards for the
chip.
config I82365
tristate "i82365 compatible bridge support"
depends on PCMCIA && ISA
select PCCARD_NONSTATIC
help
Say Y here to include support for ISA-bus PCMCIA host bridges that
are register compatible with the Intel i82365. These are found on
older laptops and ISA-bus card readers for desktop systems. A
"bridge" is the hardware inside your computer that PCMCIA cards are
plugged into. If unsure, say N.
config TCIC
tristate "Databook TCIC host bridge support"
depends on PCMCIA && ISA
select PCCARD_NONSTATIC
help
Say Y here to include support for the Databook TCIC family of PCMCIA
host bridges. These are only found on a handful of old systems.
"Bridge" is the name used for the hardware inside your computer that
PCMCIA cards are plugged into. If unsure, say N.
config PCMCIA_ALCHEMY_DEVBOARD
tristate "Alchemy Db/Pb1xxx PCMCIA socket services"
depends on MIPS_DB1XXX && PCMCIA

View File

@@ -20,9 +20,6 @@ obj-$(CONFIG_PCCARD) += pcmcia_rsrc.o
obj-$(CONFIG_YENTA) += yenta_socket.o
obj-$(CONFIG_PD6729) += pd6729.o
obj-$(CONFIG_I82365) += i82365.o
obj-$(CONFIG_I82092) += i82092.o
obj-$(CONFIG_TCIC) += tcic.o
obj-$(CONFIG_PCMCIA_SOC_COMMON) += soc_common.o
obj-$(CONFIG_PCMCIA_SA11XX_BASE) += sa11xx_base.o
obj-$(CONFIG_PCMCIA_SA1100) += sa1100_cs.o

View File

@@ -1,679 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for Intel I82092AA PCI-PCMCIA bridge.
*
* (C) 2001 Red Hat, Inc.
*
* Author: Arjan Van De Ven <arjanv@redhat.com>
* Loosly based on i82365.c from the pcmcia-cs package
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <pcmcia/ss.h>
#include <linux/io.h>
#include "i82092aa.h"
#include "i82365.h"
MODULE_DESCRIPTION("Driver for Intel I82092AA PCI-PCMCIA bridge");
MODULE_LICENSE("GPL");
/* PCI core routines */
static const struct pci_device_id i82092aa_pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82092AA_0) },
{ }
};
MODULE_DEVICE_TABLE(pci, i82092aa_pci_ids);
static struct pci_driver i82092aa_pci_driver = {
.name = "i82092aa",
.id_table = i82092aa_pci_ids,
.probe = i82092aa_pci_probe,
.remove = i82092aa_pci_remove,
};
/* the pccard structure and its functions */
static struct pccard_operations i82092aa_operations = {
.init = i82092aa_init,
.get_status = i82092aa_get_status,
.set_socket = i82092aa_set_socket,
.set_io_map = i82092aa_set_io_map,
.set_mem_map = i82092aa_set_mem_map,
};
/* The card can do up to 4 sockets, allocate a structure for each of them */
struct socket_info {
int number;
int card_state;
/* 0 = no socket,
* 1 = empty socket,
* 2 = card but not initialized,
* 3 = operational card
*/
unsigned int io_base; /* base io address of the socket */
struct pcmcia_socket socket;
struct pci_dev *dev; /* The PCI device for the socket */
};
#define MAX_SOCKETS 4
static struct socket_info sockets[MAX_SOCKETS];
static int socket_count; /* shortcut */
static int i82092aa_pci_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
unsigned char configbyte;
int i, ret;
ret = pci_enable_device(dev);
if (ret)
return ret;
/* PCI Configuration Control */
pci_read_config_byte(dev, 0x40, &configbyte);
switch (configbyte&6) {
case 0:
socket_count = 2;
break;
case 2:
socket_count = 1;
break;
case 4:
case 6:
socket_count = 4;
break;
default:
dev_err(&dev->dev,
"Oops, you did something we didn't think of.\n");
ret = -EIO;
goto err_out_disable;
}
dev_info(&dev->dev, "configured as a %d socket device.\n",
socket_count);
if (!request_region(pci_resource_start(dev, 0), 2, "i82092aa")) {
ret = -EBUSY;
goto err_out_disable;
}
for (i = 0; i < socket_count; i++) {
sockets[i].card_state = 1; /* 1 = present but empty */
sockets[i].io_base = pci_resource_start(dev, 0);
sockets[i].dev = dev;
sockets[i].socket.features |= SS_CAP_PCCARD;
sockets[i].socket.map_size = 0x1000;
sockets[i].socket.irq_mask = 0;
sockets[i].socket.pci_irq = dev->irq;
sockets[i].socket.cb_dev = dev;
sockets[i].socket.owner = THIS_MODULE;
sockets[i].number = i;
if (card_present(i)) {
sockets[i].card_state = 3;
dev_dbg(&dev->dev, "slot %i is occupied\n", i);
} else {
dev_dbg(&dev->dev, "slot %i is vacant\n", i);
}
}
/* Now, specifiy that all interrupts are to be done as PCI interrupts
* bitmask, one bit per event, 1 = PCI interrupt, 0 = ISA interrupt
*/
configbyte = 0xFF;
/* PCI Interrupt Routing Register */
pci_write_config_byte(dev, 0x50, configbyte);
/* Register the interrupt handler */
dev_dbg(&dev->dev, "Requesting interrupt %i\n", dev->irq);
ret = request_irq(dev->irq, i82092aa_interrupt, IRQF_SHARED,
"i82092aa", i82092aa_interrupt);
if (ret) {
dev_err(&dev->dev, "Failed to register IRQ %d, aborting\n",
dev->irq);
goto err_out_free_res;
}
for (i = 0; i < socket_count; i++) {
sockets[i].socket.dev.parent = &dev->dev;
sockets[i].socket.ops = &i82092aa_operations;
sockets[i].socket.resource_ops = &pccard_nonstatic_ops;
ret = pcmcia_register_socket(&sockets[i].socket);
if (ret)
goto err_out_free_sockets;
}
return 0;
err_out_free_sockets:
if (i) {
for (i--; i >= 0; i--)
pcmcia_unregister_socket(&sockets[i].socket);
}
free_irq(dev->irq, i82092aa_interrupt);
err_out_free_res:
release_region(pci_resource_start(dev, 0), 2);
err_out_disable:
pci_disable_device(dev);
return ret;
}
static void i82092aa_pci_remove(struct pci_dev *dev)
{
int i;
free_irq(dev->irq, i82092aa_interrupt);
for (i = 0; i < socket_count; i++)
pcmcia_unregister_socket(&sockets[i].socket);
}
static DEFINE_SPINLOCK(port_lock);
/* basic value read/write functions */
static unsigned char indirect_read(int socket, unsigned short reg)
{
unsigned short int port;
unsigned char val;
unsigned long flags;
spin_lock_irqsave(&port_lock, flags);
reg += socket * 0x40;
port = sockets[socket].io_base;
outb(reg, port);
val = inb(port+1);
spin_unlock_irqrestore(&port_lock, flags);
return val;
}
static void indirect_write(int socket, unsigned short reg, unsigned char value)
{
unsigned short int port;
unsigned long flags;
spin_lock_irqsave(&port_lock, flags);
reg = reg + socket * 0x40;
port = sockets[socket].io_base;
outb(reg, port);
outb(value, port+1);
spin_unlock_irqrestore(&port_lock, flags);
}
static void indirect_setbit(int socket, unsigned short reg, unsigned char mask)
{
unsigned short int port;
unsigned char val;
unsigned long flags;
spin_lock_irqsave(&port_lock, flags);
reg = reg + socket * 0x40;
port = sockets[socket].io_base;
outb(reg, port);
val = inb(port+1);
val |= mask;
outb(reg, port);
outb(val, port+1);
spin_unlock_irqrestore(&port_lock, flags);
}
static void indirect_resetbit(int socket,
unsigned short reg, unsigned char mask)
{
unsigned short int port;
unsigned char val;
unsigned long flags;
spin_lock_irqsave(&port_lock, flags);
reg = reg + socket * 0x40;
port = sockets[socket].io_base;
outb(reg, port);
val = inb(port+1);
val &= ~mask;
outb(reg, port);
outb(val, port+1);
spin_unlock_irqrestore(&port_lock, flags);
}
static void indirect_write16(int socket,
unsigned short reg, unsigned short value)
{
unsigned short int port;
unsigned char val;
unsigned long flags;
spin_lock_irqsave(&port_lock, flags);
reg = reg + socket * 0x40;
port = sockets[socket].io_base;
outb(reg, port);
val = value & 255;
outb(val, port+1);
reg++;
outb(reg, port);
val = value>>8;
outb(val, port+1);
spin_unlock_irqrestore(&port_lock, flags);
}
/* simple helper functions */
/* External clock time, in nanoseconds. 120 ns = 8.33 MHz */
static int cycle_time = 120;
static int to_cycles(int ns)
{
if (cycle_time != 0)
return ns/cycle_time;
else
return 0;
}
/* Interrupt handler functionality */
static irqreturn_t i82092aa_interrupt(int irq, void *dev)
{
int i;
int loopcount = 0;
int handled = 0;
unsigned int events, active = 0;
while (1) {
loopcount++;
if (loopcount > 20) {
pr_err("i82092aa: infinite eventloop in interrupt\n");
break;
}
active = 0;
for (i = 0; i < socket_count; i++) {
int csc;
/* Inactive socket, should not happen */
if (sockets[i].card_state == 0)
continue;
/* card status change register */
csc = indirect_read(i, I365_CSC);
if (csc == 0) /* no events on this socket */
continue;
handled = 1;
events = 0;
if (csc & I365_CSC_DETECT) {
events |= SS_DETECT;
dev_info(&sockets[i].dev->dev,
"Card detected in socket %i!\n", i);
}
if (indirect_read(i, I365_INTCTL) & I365_PC_IOCARD) {
/* For IO/CARDS, bit 0 means "read the card" */
if (csc & I365_CSC_STSCHG)
events |= SS_STSCHG;
} else {
/* Check for battery/ready events */
if (csc & I365_CSC_BVD1)
events |= SS_BATDEAD;
if (csc & I365_CSC_BVD2)
events |= SS_BATWARN;
if (csc & I365_CSC_READY)
events |= SS_READY;
}
if (events)
pcmcia_parse_events(&sockets[i].socket, events);
active |= events;
}
if (active == 0) /* no more events to handle */
break;
}
return IRQ_RETVAL(handled);
}
/* socket functions */
static int card_present(int socketno)
{
unsigned int val;
if ((socketno < 0) || (socketno >= MAX_SOCKETS))
return 0;
if (sockets[socketno].io_base == 0)
return 0;
val = indirect_read(socketno, 1); /* Interface status register */
if ((val&12) == 12)
return 1;
return 0;
}
static void set_bridge_state(int sock)
{
indirect_write(sock, I365_GBLCTL, 0x00);
indirect_write(sock, I365_GENCTL, 0x00);
indirect_setbit(sock, I365_INTCTL, 0x08);
}
static int i82092aa_init(struct pcmcia_socket *sock)
{
int i;
struct resource res = { .start = 0, .end = 0x0fff };
pccard_io_map io = { 0, 0, 0, 0, 1 };
pccard_mem_map mem = { .res = &res, };
for (i = 0; i < 2; i++) {
io.map = i;
i82092aa_set_io_map(sock, &io);
}
for (i = 0; i < 5; i++) {
mem.map = i;
i82092aa_set_mem_map(sock, &mem);
}
return 0;
}
static int i82092aa_get_status(struct pcmcia_socket *socket, u_int *value)
{
unsigned int sock = container_of(socket,
struct socket_info, socket)->number;
unsigned int status;
/* Interface Status Register */
status = indirect_read(sock, I365_STATUS);
*value = 0;
if ((status & I365_CS_DETECT) == I365_CS_DETECT)
*value |= SS_DETECT;
/* IO cards have a different meaning of bits 0,1 */
/* Also notice the inverse-logic on the bits */
if (indirect_read(sock, I365_INTCTL) & I365_PC_IOCARD) {
/* IO card */
if (!(status & I365_CS_STSCHG))
*value |= SS_STSCHG;
} else { /* non I/O card */
if (!(status & I365_CS_BVD1))
*value |= SS_BATDEAD;
if (!(status & I365_CS_BVD2))
*value |= SS_BATWARN;
}
if (status & I365_CS_WRPROT)
(*value) |= SS_WRPROT; /* card is write protected */
if (status & I365_CS_READY)
(*value) |= SS_READY; /* card is not busy */
if (status & I365_CS_POWERON)
(*value) |= SS_POWERON; /* power is applied to the card */
return 0;
}
static int i82092aa_set_socket(struct pcmcia_socket *socket,
socket_state_t *state)
{
struct socket_info *sock_info = container_of(socket, struct socket_info,
socket);
unsigned int sock = sock_info->number;
unsigned char reg;
/* First, set the global controller options */
set_bridge_state(sock);
/* Values for the IGENC register */
reg = 0;
/* The reset bit has "inverse" logic */
if (!(state->flags & SS_RESET))
reg = reg | I365_PC_RESET;
if (state->flags & SS_IOCARD)
reg = reg | I365_PC_IOCARD;
/* IGENC, Interrupt and General Control Register */
indirect_write(sock, I365_INTCTL, reg);
/* Power registers */
reg = I365_PWR_NORESET; /* default: disable resetdrv on resume */
if (state->flags & SS_PWR_AUTO) {
dev_info(&sock_info->dev->dev, "Auto power\n");
reg |= I365_PWR_AUTO; /* automatic power mngmnt */
}
if (state->flags & SS_OUTPUT_ENA) {
dev_info(&sock_info->dev->dev, "Power Enabled\n");
reg |= I365_PWR_OUT; /* enable power */
}
switch (state->Vcc) {
case 0:
break;
case 50:
dev_info(&sock_info->dev->dev,
"setting voltage to Vcc to 5V on socket %i\n",
sock);
reg |= I365_VCC_5V;
break;
default:
dev_err(&sock_info->dev->dev,
"%s called with invalid VCC power value: %i",
__func__, state->Vcc);
return -EINVAL;
}
switch (state->Vpp) {
case 0:
dev_info(&sock_info->dev->dev,
"not setting Vpp on socket %i\n", sock);
break;
case 50:
dev_info(&sock_info->dev->dev,
"setting Vpp to 5.0 for socket %i\n", sock);
reg |= I365_VPP1_5V | I365_VPP2_5V;
break;
case 120:
dev_info(&sock_info->dev->dev, "setting Vpp to 12.0\n");
reg |= I365_VPP1_12V | I365_VPP2_12V;
break;
default:
dev_err(&sock_info->dev->dev,
"%s called with invalid VPP power value: %i",
__func__, state->Vcc);
return -EINVAL;
}
if (reg != indirect_read(sock, I365_POWER)) /* only write if changed */
indirect_write(sock, I365_POWER, reg);
/* Enable specific interrupt events */
reg = 0x00;
if (state->csc_mask & SS_DETECT)
reg |= I365_CSC_DETECT;
if (state->flags & SS_IOCARD) {
if (state->csc_mask & SS_STSCHG)
reg |= I365_CSC_STSCHG;
} else {
if (state->csc_mask & SS_BATDEAD)
reg |= I365_CSC_BVD1;
if (state->csc_mask & SS_BATWARN)
reg |= I365_CSC_BVD2;
if (state->csc_mask & SS_READY)
reg |= I365_CSC_READY;
}
/* now write the value and clear the (probably bogus) pending stuff
* by doing a dummy read
*/
indirect_write(sock, I365_CSCINT, reg);
(void)indirect_read(sock, I365_CSC);
return 0;
}
static int i82092aa_set_io_map(struct pcmcia_socket *socket,
struct pccard_io_map *io)
{
struct socket_info *sock_info = container_of(socket, struct socket_info,
socket);
unsigned int sock = sock_info->number;
unsigned char map, ioctl;
map = io->map;
/* Check error conditions */
if (map > 1)
return -EINVAL;
if ((io->start > 0xffff) || (io->stop > 0xffff)
|| (io->stop < io->start))
return -EINVAL;
/* Turn off the window before changing anything */
if (indirect_read(sock, I365_ADDRWIN) & I365_ENA_IO(map))
indirect_resetbit(sock, I365_ADDRWIN, I365_ENA_IO(map));
/* write the new values */
indirect_write16(sock, I365_IO(map)+I365_W_START, io->start);
indirect_write16(sock, I365_IO(map)+I365_W_STOP, io->stop);
ioctl = indirect_read(sock, I365_IOCTL) & ~I365_IOCTL_MASK(map);
if (io->flags & (MAP_16BIT|MAP_AUTOSZ))
ioctl |= I365_IOCTL_16BIT(map);
indirect_write(sock, I365_IOCTL, ioctl);
/* Turn the window back on if needed */
if (io->flags & MAP_ACTIVE)
indirect_setbit(sock, I365_ADDRWIN, I365_ENA_IO(map));
return 0;
}
static int i82092aa_set_mem_map(struct pcmcia_socket *socket,
struct pccard_mem_map *mem)
{
struct socket_info *sock_info = container_of(socket, struct socket_info,
socket);
unsigned int sock = sock_info->number;
struct pci_bus_region region;
unsigned short base, i;
unsigned char map;
pcibios_resource_to_bus(sock_info->dev->bus, &region, mem->res);
map = mem->map;
if (map > 4)
return -EINVAL;
if ((mem->card_start > 0x3ffffff) || (region.start > region.end) ||
(mem->speed > 1000)) {
dev_err(&sock_info->dev->dev,
"invalid mem map for socket %i: %llx to %llx with a start of %x\n",
sock,
(unsigned long long)region.start,
(unsigned long long)region.end,
mem->card_start);
return -EINVAL;
}
/* Turn off the window before changing anything */
if (indirect_read(sock, I365_ADDRWIN) & I365_ENA_MEM(map))
indirect_resetbit(sock, I365_ADDRWIN, I365_ENA_MEM(map));
/* write the start address */
base = I365_MEM(map);
i = (region.start >> 12) & 0x0fff;
if (mem->flags & MAP_16BIT)
i |= I365_MEM_16BIT;
if (mem->flags & MAP_0WS)
i |= I365_MEM_0WS;
indirect_write16(sock, base+I365_W_START, i);
/* write the stop address */
i = (region.end >> 12) & 0x0fff;
switch (to_cycles(mem->speed)) {
case 0:
break;
case 1:
i |= I365_MEM_WS0;
break;
case 2:
i |= I365_MEM_WS1;
break;
default:
i |= I365_MEM_WS1 | I365_MEM_WS0;
break;
}
indirect_write16(sock, base+I365_W_STOP, i);
/* card start */
i = ((mem->card_start - region.start) >> 12) & 0x3fff;
if (mem->flags & MAP_WRPROT)
i |= I365_MEM_WRPROT;
if (mem->flags & MAP_ATTRIB)
i |= I365_MEM_REG;
indirect_write16(sock, base+I365_W_OFF, i);
/* Enable the window if necessary */
if (mem->flags & MAP_ACTIVE)
indirect_setbit(sock, I365_ADDRWIN, I365_ENA_MEM(map));
return 0;
}
static int __init i82092aa_module_init(void)
{
return pci_register_driver(&i82092aa_pci_driver);
}
static void __exit i82092aa_module_exit(void)
{
pci_unregister_driver(&i82092aa_pci_driver);
if (sockets[0].io_base > 0)
release_region(sockets[0].io_base, 2);
}
module_init(i82092aa_module_init);
module_exit(i82092aa_module_exit);

View File

@@ -1,24 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _INCLUDE_GUARD_i82092aa_H_
#define _INCLUDE_GUARD_i82092aa_H_
#include <linux/interrupt.h>
/* prototypes */
static int i82092aa_pci_probe(struct pci_dev *dev, const struct pci_device_id *id);
static void i82092aa_pci_remove(struct pci_dev *dev);
static int card_present(int socketno);
static irqreturn_t i82092aa_interrupt(int irq, void *dev);
static int i82092aa_get_status(struct pcmcia_socket *socket, u_int *value);
static int i82092aa_set_socket(struct pcmcia_socket *socket, socket_state_t *state);
static int i82092aa_set_io_map(struct pcmcia_socket *socket, struct pccard_io_map *io);
static int i82092aa_set_mem_map(struct pcmcia_socket *socket, struct pccard_mem_map *mem);
static int i82092aa_init(struct pcmcia_socket *socket);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,805 +0,0 @@
/*======================================================================
Device driver for Databook TCIC-2 PCMCIA controller
tcic.c 1.111 2000/02/15 04:13:12
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
<dahinds@users.sourceforge.net>. 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 General 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.
======================================================================*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
#include <linux/bitops.h>
#include <asm/io.h>
#include <pcmcia/ss.h>
#include "tcic.h"
MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
MODULE_DESCRIPTION("Databook TCIC-2 PCMCIA socket driver");
MODULE_LICENSE("Dual MPL/GPL");
/*====================================================================*/
/* Parameters that can be set with 'insmod' */
/* The base port address of the TCIC-2 chip */
static unsigned long tcic_base = TCIC_BASE;
/* Specify a socket number to ignore */
static int ignore = -1;
/* Probe for safe interrupts? */
static int do_scan = 1;
/* Bit map of interrupts to choose from */
static u_int irq_mask = 0xffff;
static int irq_list[16];
static unsigned int irq_list_count;
/* The card status change interrupt -- 0 means autoselect */
static int cs_irq;
/* Poll status interval -- 0 means default to interrupt */
static int poll_interval;
/* Delay for card status double-checking */
static int poll_quick = HZ/20;
/* CCLK external clock time, in nanoseconds. 70 ns = 14.31818 MHz */
static int cycle_time = 70;
module_param_hw(tcic_base, ulong, ioport, 0444);
module_param(ignore, int, 0444);
module_param(do_scan, int, 0444);
module_param_hw(irq_mask, int, other, 0444);
module_param_hw_array(irq_list, int, irq, &irq_list_count, 0444);
module_param_hw(cs_irq, int, irq, 0444);
module_param(poll_interval, int, 0444);
module_param(poll_quick, int, 0444);
module_param(cycle_time, int, 0444);
/*====================================================================*/
static irqreturn_t tcic_interrupt(int irq, void *dev);
static void tcic_timer(struct timer_list *unused);
static struct pccard_operations tcic_operations;
struct tcic_socket {
u_short psock;
u_char last_sstat;
u_char id;
struct pcmcia_socket socket;
};
static struct timer_list poll_timer;
static int tcic_timer_pending;
static int sockets;
static struct tcic_socket socket_table[2];
/*====================================================================*/
/* Trick when selecting interrupts: the TCIC sktirq pin is supposed
to map to irq 11, but is coded as 0 or 1 in the irq registers. */
#define TCIC_IRQ(x) ((x) ? (((x) == 11) ? 1 : (x)) : 15)
#ifdef DEBUG_X
static u_char tcic_getb(u_char reg)
{
u_char val = inb(tcic_base+reg);
printk(KERN_DEBUG "tcic_getb(%#lx) = %#x\n", tcic_base+reg, val);
return val;
}
static u_short tcic_getw(u_char reg)
{
u_short val = inw(tcic_base+reg);
printk(KERN_DEBUG "tcic_getw(%#lx) = %#x\n", tcic_base+reg, val);
return val;
}
static void tcic_setb(u_char reg, u_char data)
{
printk(KERN_DEBUG "tcic_setb(%#lx, %#x)\n", tcic_base+reg, data);
outb(data, tcic_base+reg);
}
static void tcic_setw(u_char reg, u_short data)
{
printk(KERN_DEBUG "tcic_setw(%#lx, %#x)\n", tcic_base+reg, data);
outw(data, tcic_base+reg);
}
#else
#define tcic_getb(reg) inb(tcic_base+reg)
#define tcic_getw(reg) inw(tcic_base+reg)
#define tcic_setb(reg, data) outb(data, tcic_base+reg)
#define tcic_setw(reg, data) outw(data, tcic_base+reg)
#endif
static void tcic_setl(u_char reg, u_int data)
{
#ifdef DEBUG_X
printk(KERN_DEBUG "tcic_setl(%#x, %#lx)\n", tcic_base+reg, data);
#endif
outw(data & 0xffff, tcic_base+reg);
outw(data >> 16, tcic_base+reg+2);
}
static void tcic_aux_setb(u_short reg, u_char data)
{
u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg;
tcic_setb(TCIC_MODE, mode);
tcic_setb(TCIC_AUX, data);
}
static u_short tcic_aux_getw(u_short reg)
{
u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg;
tcic_setb(TCIC_MODE, mode);
return tcic_getw(TCIC_AUX);
}
static void tcic_aux_setw(u_short reg, u_short data)
{
u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg;
tcic_setb(TCIC_MODE, mode);
tcic_setw(TCIC_AUX, data);
}
/*====================================================================*/
/* Time conversion functions */
static int to_cycles(int ns)
{
if (ns < 14)
return 0;
else
return 2*(ns-14)/cycle_time;
}
/*====================================================================*/
static volatile u_int irq_hits;
static irqreturn_t __init tcic_irq_count(int irq, void *dev)
{
irq_hits++;
return IRQ_HANDLED;
}
static u_int __init try_irq(int irq)
{
u_short cfg;
irq_hits = 0;
if (request_irq(irq, tcic_irq_count, 0, "irq scan", tcic_irq_count) != 0)
return -1;
mdelay(10);
if (irq_hits) {
free_irq(irq, tcic_irq_count);
return -1;
}
/* Generate one interrupt */
cfg = TCIC_SYSCFG_AUTOBUSY | 0x0a00;
tcic_aux_setw(TCIC_AUX_SYSCFG, cfg | TCIC_IRQ(irq));
tcic_setb(TCIC_IENA, TCIC_IENA_ERR | TCIC_IENA_CFG_HIGH);
tcic_setb(TCIC_ICSR, TCIC_ICSR_ERR | TCIC_ICSR_JAM);
udelay(1000);
free_irq(irq, tcic_irq_count);
/* Turn off interrupts */
tcic_setb(TCIC_IENA, TCIC_IENA_CFG_OFF);
while (tcic_getb(TCIC_ICSR))
tcic_setb(TCIC_ICSR, TCIC_ICSR_JAM);
tcic_aux_setw(TCIC_AUX_SYSCFG, cfg);
return (irq_hits != 1);
}
static u_int __init irq_scan(u_int mask0)
{
u_int mask1;
int i;
#ifdef __alpha__
#define PIC 0x4d0
/* Don't probe level-triggered interrupts -- reserved for PCI */
int level_mask = inb_p(PIC) | (inb_p(PIC+1) << 8);
if (level_mask)
mask0 &= ~level_mask;
#endif
mask1 = 0;
if (do_scan) {
for (i = 0; i < 16; i++)
if ((mask0 & (1 << i)) && (try_irq(i) == 0))
mask1 |= (1 << i);
for (i = 0; i < 16; i++)
if ((mask1 & (1 << i)) && (try_irq(i) != 0)) {
mask1 ^= (1 << i);
}
}
if (mask1) {
printk("scanned");
} else {
/* Fallback: just find interrupts that aren't in use */
for (i = 0; i < 16; i++)
if ((mask0 & (1 << i)) &&
(request_irq(i, tcic_irq_count, 0, "x", tcic_irq_count) == 0)) {
mask1 |= (1 << i);
free_irq(i, tcic_irq_count);
}
printk("default");
}
printk(") = ");
for (i = 0; i < 16; i++)
if (mask1 & (1<<i))
printk("%s%d", ((mask1 & ((1<<i)-1)) ? "," : ""), i);
printk(" ");
return mask1;
}
/*======================================================================
See if a card is present, powered up, in IO mode, and already
bound to a (non-PCMCIA) Linux driver.
We make an exception for cards that look like serial devices.
======================================================================*/
static int __init is_active(int s)
{
u_short scf1, ioctl, base, num;
u_char pwr, sstat;
u_int addr;
tcic_setl(TCIC_ADDR, (s << TCIC_ADDR_SS_SHFT)
| TCIC_ADDR_INDREG | TCIC_SCF1(s));
scf1 = tcic_getw(TCIC_DATA);
pwr = tcic_getb(TCIC_PWR);
sstat = tcic_getb(TCIC_SSTAT);
addr = TCIC_IWIN(s, 0);
tcic_setw(TCIC_ADDR, addr + TCIC_IBASE_X);
base = tcic_getw(TCIC_DATA);
tcic_setw(TCIC_ADDR, addr + TCIC_ICTL_X);
ioctl = tcic_getw(TCIC_DATA);
if (ioctl & TCIC_ICTL_TINY)
num = 1;
else {
num = (base ^ (base-1));
base = base & (base-1);
}
if ((sstat & TCIC_SSTAT_CD) && (pwr & TCIC_PWR_VCC(s)) &&
(scf1 & TCIC_SCF1_IOSTS) && (ioctl & TCIC_ICTL_ENA) &&
((base & 0xfeef) != 0x02e8)) {
struct resource *res = request_region(base, num, "tcic-2");
if (!res) /* region is busy */
return 1;
release_region(base, num);
}
return 0;
}
/*======================================================================
This returns the revision code for the specified socket.
======================================================================*/
static int __init get_tcic_id(void)
{
u_short id;
tcic_aux_setw(TCIC_AUX_TEST, TCIC_TEST_DIAG);
id = tcic_aux_getw(TCIC_AUX_ILOCK);
id = (id & TCIC_ILOCKTEST_ID_MASK) >> TCIC_ILOCKTEST_ID_SH;
tcic_aux_setw(TCIC_AUX_TEST, 0);
return id;
}
/*====================================================================*/
static struct platform_driver tcic_driver = {
.driver = {
.name = "tcic-pcmcia",
},
};
static struct platform_device tcic_device = {
.name = "tcic-pcmcia",
.id = 0,
};
static int __init init_tcic(void)
{
int i, sock, ret = 0;
u_int mask, scan;
if (platform_driver_register(&tcic_driver))
return -1;
printk(KERN_INFO "Databook TCIC-2 PCMCIA probe: ");
sock = 0;
if (!request_region(tcic_base, 16, "tcic-2")) {
printk("could not allocate ports,\n ");
platform_driver_unregister(&tcic_driver);
return -ENODEV;
}
else {
tcic_setw(TCIC_ADDR, 0);
if (tcic_getw(TCIC_ADDR) == 0) {
tcic_setw(TCIC_ADDR, 0xc3a5);
if (tcic_getw(TCIC_ADDR) == 0xc3a5) sock = 2;
}
if (sock == 0) {
/* See if resetting the controller does any good */
tcic_setb(TCIC_SCTRL, TCIC_SCTRL_RESET);
tcic_setb(TCIC_SCTRL, 0);
tcic_setw(TCIC_ADDR, 0);
if (tcic_getw(TCIC_ADDR) == 0) {
tcic_setw(TCIC_ADDR, 0xc3a5);
if (tcic_getw(TCIC_ADDR) == 0xc3a5) sock = 2;
}
}
}
if (sock == 0) {
printk("not found.\n");
release_region(tcic_base, 16);
platform_driver_unregister(&tcic_driver);
return -ENODEV;
}
sockets = 0;
for (i = 0; i < sock; i++) {
if ((i == ignore) || is_active(i)) continue;
socket_table[sockets].psock = i;
socket_table[sockets].id = get_tcic_id();
socket_table[sockets].socket.owner = THIS_MODULE;
/* only 16-bit cards, memory windows must be size-aligned */
/* No PCI or CardBus support */
socket_table[sockets].socket.features = SS_CAP_PCCARD | SS_CAP_MEM_ALIGN;
/* irq 14, 11, 10, 7, 6, 5, 4, 3 */
socket_table[sockets].socket.irq_mask = 0x4cf8;
/* 4K minimum window size */
socket_table[sockets].socket.map_size = 0x1000;
sockets++;
}
switch (socket_table[0].id) {
case TCIC_ID_DB86082:
printk("DB86082"); break;
case TCIC_ID_DB86082A:
printk("DB86082A"); break;
case TCIC_ID_DB86084:
printk("DB86084"); break;
case TCIC_ID_DB86084A:
printk("DB86084A"); break;
case TCIC_ID_DB86072:
printk("DB86072"); break;
case TCIC_ID_DB86184:
printk("DB86184"); break;
case TCIC_ID_DB86082B:
printk("DB86082B"); break;
default:
printk("Unknown ID 0x%02x", socket_table[0].id);
}
/* Set up polling */
timer_setup(&poll_timer, tcic_timer, 0);
/* Build interrupt mask */
printk(KERN_CONT ", %d sockets\n", sockets);
printk(KERN_INFO " irq list (");
if (irq_list_count == 0)
mask = irq_mask;
else
for (i = mask = 0; i < irq_list_count; i++)
mask |= (1<<irq_list[i]);
/* irq 14, 11, 10, 7, 6, 5, 4, 3 */
mask &= 0x4cf8;
/* Scan interrupts */
mask = irq_scan(mask);
for (i=0;i<sockets;i++)
socket_table[i].socket.irq_mask = mask;
/* Check for only two interrupts available */
scan = (mask & (mask-1));
if (((scan & (scan-1)) == 0) && (poll_interval == 0))
poll_interval = HZ;
if (poll_interval == 0) {
/* Avoid irq 12 unless it is explicitly requested */
u_int cs_mask = mask & ((cs_irq) ? (1<<cs_irq) : ~(1<<12));
for (i = 15; i > 0; i--)
if ((cs_mask & (1 << i)) &&
(request_irq(i, tcic_interrupt, 0, "tcic",
tcic_interrupt) == 0))
break;
cs_irq = i;
if (cs_irq == 0) poll_interval = HZ;
}
if (socket_table[0].socket.irq_mask & (1 << 11))
printk("sktirq is irq 11, ");
if (cs_irq != 0)
printk("status change on irq %d\n", cs_irq);
else
printk("polled status, interval = %d ms\n",
poll_interval * 1000 / HZ);
for (i = 0; i < sockets; i++) {
tcic_setw(TCIC_ADDR+2, socket_table[i].psock << TCIC_SS_SHFT);
socket_table[i].last_sstat = tcic_getb(TCIC_SSTAT);
}
/* jump start interrupt handler, if needed */
tcic_interrupt(0, NULL);
platform_device_register(&tcic_device);
for (i = 0; i < sockets; i++) {
socket_table[i].socket.ops = &tcic_operations;
socket_table[i].socket.resource_ops = &pccard_nonstatic_ops;
socket_table[i].socket.dev.parent = &tcic_device.dev;
ret = pcmcia_register_socket(&socket_table[i].socket);
if (ret && i)
pcmcia_unregister_socket(&socket_table[0].socket);
}
return ret;
return 0;
} /* init_tcic */
/*====================================================================*/
static void __exit exit_tcic(void)
{
int i;
timer_delete_sync(&poll_timer);
if (cs_irq != 0) {
tcic_aux_setw(TCIC_AUX_SYSCFG, TCIC_SYSCFG_AUTOBUSY|0x0a00);
free_irq(cs_irq, tcic_interrupt);
}
release_region(tcic_base, 16);
for (i = 0; i < sockets; i++) {
pcmcia_unregister_socket(&socket_table[i].socket);
}
platform_device_unregister(&tcic_device);
platform_driver_unregister(&tcic_driver);
} /* exit_tcic */
/*====================================================================*/
static irqreturn_t tcic_interrupt(int irq, void *dev)
{
int i, quick = 0;
u_char latch, sstat;
u_short psock;
u_int events;
static volatile int active = 0;
if (active) {
printk(KERN_NOTICE "tcic: reentered interrupt handler!\n");
return IRQ_NONE;
} else
active = 1;
pr_debug("tcic_interrupt()\n");
for (i = 0; i < sockets; i++) {
psock = socket_table[i].psock;
tcic_setl(TCIC_ADDR, (psock << TCIC_ADDR_SS_SHFT)
| TCIC_ADDR_INDREG | TCIC_SCF1(psock));
sstat = tcic_getb(TCIC_SSTAT);
latch = sstat ^ socket_table[psock].last_sstat;
socket_table[i].last_sstat = sstat;
if (tcic_getb(TCIC_ICSR) & TCIC_ICSR_CDCHG) {
tcic_setb(TCIC_ICSR, TCIC_ICSR_CLEAR);
quick = 1;
}
if (latch == 0)
continue;
events = (latch & TCIC_SSTAT_CD) ? SS_DETECT : 0;
events |= (latch & TCIC_SSTAT_WP) ? SS_WRPROT : 0;
if (tcic_getw(TCIC_DATA) & TCIC_SCF1_IOSTS) {
events |= (latch & TCIC_SSTAT_LBAT1) ? SS_STSCHG : 0;
} else {
events |= (latch & TCIC_SSTAT_RDY) ? SS_READY : 0;
events |= (latch & TCIC_SSTAT_LBAT1) ? SS_BATDEAD : 0;
events |= (latch & TCIC_SSTAT_LBAT2) ? SS_BATWARN : 0;
}
if (events) {
pcmcia_parse_events(&socket_table[i].socket, events);
}
}
/* Schedule next poll, if needed */
if (((cs_irq == 0) || quick) && (!tcic_timer_pending)) {
poll_timer.expires = jiffies + (quick ? poll_quick : poll_interval);
add_timer(&poll_timer);
tcic_timer_pending = 1;
}
active = 0;
pr_debug("interrupt done\n");
return IRQ_HANDLED;
} /* tcic_interrupt */
static void tcic_timer(struct timer_list *unused)
{
pr_debug("tcic_timer()\n");
tcic_timer_pending = 0;
tcic_interrupt(0, NULL);
} /* tcic_timer */
/*====================================================================*/
static int tcic_get_status(struct pcmcia_socket *sock, u_int *value)
{
u_short psock = container_of(sock, struct tcic_socket, socket)->psock;
u_char reg;
tcic_setl(TCIC_ADDR, (psock << TCIC_ADDR_SS_SHFT)
| TCIC_ADDR_INDREG | TCIC_SCF1(psock));
reg = tcic_getb(TCIC_SSTAT);
*value = (reg & TCIC_SSTAT_CD) ? SS_DETECT : 0;
*value |= (reg & TCIC_SSTAT_WP) ? SS_WRPROT : 0;
if (tcic_getw(TCIC_DATA) & TCIC_SCF1_IOSTS) {
*value |= (reg & TCIC_SSTAT_LBAT1) ? SS_STSCHG : 0;
} else {
*value |= (reg & TCIC_SSTAT_RDY) ? SS_READY : 0;
*value |= (reg & TCIC_SSTAT_LBAT1) ? SS_BATDEAD : 0;
*value |= (reg & TCIC_SSTAT_LBAT2) ? SS_BATWARN : 0;
}
reg = tcic_getb(TCIC_PWR);
if (reg & (TCIC_PWR_VCC(psock)|TCIC_PWR_VPP(psock)))
*value |= SS_POWERON;
dev_dbg(&sock->dev, "GetStatus(%d) = %#2.2x\n", psock, *value);
return 0;
} /* tcic_get_status */
/*====================================================================*/
static int tcic_set_socket(struct pcmcia_socket *sock, socket_state_t *state)
{
u_short psock = container_of(sock, struct tcic_socket, socket)->psock;
u_char reg;
u_short scf1, scf2;
dev_dbg(&sock->dev, "SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
"io_irq %d, csc_mask %#2.2x)\n", psock, state->flags,
state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
tcic_setw(TCIC_ADDR+2, (psock << TCIC_SS_SHFT) | TCIC_ADR2_INDREG);
reg = tcic_getb(TCIC_PWR);
reg &= ~(TCIC_PWR_VCC(psock) | TCIC_PWR_VPP(psock));
if (state->Vcc == 50) {
switch (state->Vpp) {
case 0: reg |= TCIC_PWR_VCC(psock) | TCIC_PWR_VPP(psock); break;
case 50: reg |= TCIC_PWR_VCC(psock); break;
case 120: reg |= TCIC_PWR_VPP(psock); break;
default: return -EINVAL;
}
} else if (state->Vcc != 0)
return -EINVAL;
if (reg != tcic_getb(TCIC_PWR))
tcic_setb(TCIC_PWR, reg);
reg = TCIC_ILOCK_HOLD_CCLK | TCIC_ILOCK_CWAIT;
if (state->flags & SS_OUTPUT_ENA) {
tcic_setb(TCIC_SCTRL, TCIC_SCTRL_ENA);
reg |= TCIC_ILOCK_CRESENA;
} else
tcic_setb(TCIC_SCTRL, 0);
if (state->flags & SS_RESET)
reg |= TCIC_ILOCK_CRESET;
tcic_aux_setb(TCIC_AUX_ILOCK, reg);
tcic_setw(TCIC_ADDR, TCIC_SCF1(psock));
scf1 = TCIC_SCF1_FINPACK;
scf1 |= TCIC_IRQ(state->io_irq);
if (state->flags & SS_IOCARD) {
scf1 |= TCIC_SCF1_IOSTS;
if (state->flags & SS_SPKR_ENA)
scf1 |= TCIC_SCF1_SPKR;
if (state->flags & SS_DMA_MODE)
scf1 |= TCIC_SCF1_DREQ2 << TCIC_SCF1_DMA_SHIFT;
}
tcic_setw(TCIC_DATA, scf1);
/* Some general setup stuff, and configure status interrupt */
reg = TCIC_WAIT_ASYNC | TCIC_WAIT_SENSE | to_cycles(250);
tcic_aux_setb(TCIC_AUX_WCTL, reg);
tcic_aux_setw(TCIC_AUX_SYSCFG, TCIC_SYSCFG_AUTOBUSY|0x0a00|
TCIC_IRQ(cs_irq));
/* Card status change interrupt mask */
tcic_setw(TCIC_ADDR, TCIC_SCF2(psock));
scf2 = TCIC_SCF2_MALL;
if (state->csc_mask & SS_DETECT) scf2 &= ~TCIC_SCF2_MCD;
if (state->flags & SS_IOCARD) {
if (state->csc_mask & SS_STSCHG) reg &= ~TCIC_SCF2_MLBAT1;
} else {
if (state->csc_mask & SS_BATDEAD) reg &= ~TCIC_SCF2_MLBAT1;
if (state->csc_mask & SS_BATWARN) reg &= ~TCIC_SCF2_MLBAT2;
if (state->csc_mask & SS_READY) reg &= ~TCIC_SCF2_MRDY;
}
tcic_setw(TCIC_DATA, scf2);
/* For the ISA bus, the irq should be active-high totem-pole */
tcic_setb(TCIC_IENA, TCIC_IENA_CDCHG | TCIC_IENA_CFG_HIGH);
return 0;
} /* tcic_set_socket */
/*====================================================================*/
static int tcic_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io)
{
u_short psock = container_of(sock, struct tcic_socket, socket)->psock;
u_int addr;
u_short base, len, ioctl;
dev_dbg(&sock->dev, "SetIOMap(%d, %d, %#2.2x, %d ns, "
"%#llx-%#llx)\n", psock, io->map, io->flags, io->speed,
(unsigned long long)io->start, (unsigned long long)io->stop);
if ((io->map > 1) || (io->start > 0xffff) || (io->stop > 0xffff) ||
(io->stop < io->start)) return -EINVAL;
tcic_setw(TCIC_ADDR+2, TCIC_ADR2_INDREG | (psock << TCIC_SS_SHFT));
addr = TCIC_IWIN(psock, io->map);
base = io->start; len = io->stop - io->start;
/* Check to see that len+1 is power of two, etc */
if ((len & (len+1)) || (base & len)) return -EINVAL;
base |= (len+1)>>1;
tcic_setw(TCIC_ADDR, addr + TCIC_IBASE_X);
tcic_setw(TCIC_DATA, base);
ioctl = (psock << TCIC_ICTL_SS_SHFT);
ioctl |= (len == 0) ? TCIC_ICTL_TINY : 0;
ioctl |= (io->flags & MAP_ACTIVE) ? TCIC_ICTL_ENA : 0;
ioctl |= to_cycles(io->speed) & TCIC_ICTL_WSCNT_MASK;
if (!(io->flags & MAP_AUTOSZ)) {
ioctl |= TCIC_ICTL_QUIET;
ioctl |= (io->flags & MAP_16BIT) ? TCIC_ICTL_BW_16 : TCIC_ICTL_BW_8;
}
tcic_setw(TCIC_ADDR, addr + TCIC_ICTL_X);
tcic_setw(TCIC_DATA, ioctl);
return 0;
} /* tcic_set_io_map */
/*====================================================================*/
static int tcic_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *mem)
{
u_short psock = container_of(sock, struct tcic_socket, socket)->psock;
u_short addr, ctl;
u_long base, len, mmap;
dev_dbg(&sock->dev, "SetMemMap(%d, %d, %#2.2x, %d ns, "
"%#llx-%#llx, %#x)\n", psock, mem->map, mem->flags,
mem->speed, (unsigned long long)mem->res->start,
(unsigned long long)mem->res->end, mem->card_start);
if ((mem->map > 3) || (mem->card_start > 0x3ffffff) ||
(mem->res->start > 0xffffff) || (mem->res->end > 0xffffff) ||
(mem->res->start > mem->res->end) || (mem->speed > 1000))
return -EINVAL;
tcic_setw(TCIC_ADDR+2, TCIC_ADR2_INDREG | (psock << TCIC_SS_SHFT));
addr = TCIC_MWIN(psock, mem->map);
base = mem->res->start; len = mem->res->end - mem->res->start;
if ((len & (len+1)) || (base & len)) return -EINVAL;
if (len == 0x0fff)
base = (base >> TCIC_MBASE_HA_SHFT) | TCIC_MBASE_4K_BIT;
else
base = (base | (len+1)>>1) >> TCIC_MBASE_HA_SHFT;
tcic_setw(TCIC_ADDR, addr + TCIC_MBASE_X);
tcic_setw(TCIC_DATA, base);
mmap = mem->card_start - mem->res->start;
mmap = (mmap >> TCIC_MMAP_CA_SHFT) & TCIC_MMAP_CA_MASK;
if (mem->flags & MAP_ATTRIB) mmap |= TCIC_MMAP_REG;
tcic_setw(TCIC_ADDR, addr + TCIC_MMAP_X);
tcic_setw(TCIC_DATA, mmap);
ctl = TCIC_MCTL_QUIET | (psock << TCIC_MCTL_SS_SHFT);
ctl |= to_cycles(mem->speed) & TCIC_MCTL_WSCNT_MASK;
ctl |= (mem->flags & MAP_16BIT) ? 0 : TCIC_MCTL_B8;
ctl |= (mem->flags & MAP_WRPROT) ? TCIC_MCTL_WP : 0;
ctl |= (mem->flags & MAP_ACTIVE) ? TCIC_MCTL_ENA : 0;
tcic_setw(TCIC_ADDR, addr + TCIC_MCTL_X);
tcic_setw(TCIC_DATA, ctl);
return 0;
} /* tcic_set_mem_map */
/*====================================================================*/
static int tcic_init(struct pcmcia_socket *s)
{
int i;
struct resource res = { .start = 0, .end = 0x1000 };
pccard_io_map io = { 0, 0, 0, 0, 1 };
pccard_mem_map mem = { .res = &res, };
for (i = 0; i < 2; i++) {
io.map = i;
tcic_set_io_map(s, &io);
}
for (i = 0; i < 5; i++) {
mem.map = i;
tcic_set_mem_map(s, &mem);
}
return 0;
}
static struct pccard_operations tcic_operations = {
.init = tcic_init,
.get_status = tcic_get_status,
.set_socket = tcic_set_socket,
.set_io_map = tcic_set_io_map,
.set_mem_map = tcic_set_mem_map,
};
/*====================================================================*/
module_init(init_tcic);
module_exit(exit_tcic);

View File

@@ -1,266 +0,0 @@
/*
* tcic.h 1.13 1999/10/25 20:03:34
*
* 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
* <dahinds@users.sourceforge.net>. 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 General 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.
*/
#ifndef _LINUX_TCIC_H
#define _LINUX_TCIC_H
#define TCIC_BASE 0x240
/* offsets of registers from TCIC_BASE */
#define TCIC_DATA 0x00
#define TCIC_ADDR 0x02
#define TCIC_SCTRL 0x06
#define TCIC_SSTAT 0x07
#define TCIC_MODE 0x08
#define TCIC_PWR 0x09
#define TCIC_EDC 0x0A
#define TCIC_ICSR 0x0C
#define TCIC_IENA 0x0D
#define TCIC_AUX 0x0E
#define TCIC_SS_SHFT 12
#define TCIC_SS_MASK 0x7000
/* Flags for TCIC_ADDR */
#define TCIC_ADR2_REG 0x8000
#define TCIC_ADR2_INDREG 0x0800
#define TCIC_ADDR_REG 0x80000000
#define TCIC_ADDR_SS_SHFT (TCIC_SS_SHFT+16)
#define TCIC_ADDR_SS_MASK (TCIC_SS_MASK<<16)
#define TCIC_ADDR_INDREG 0x08000000
#define TCIC_ADDR_IO 0x04000000
#define TCIC_ADDR_MASK 0x03ffffff
/* Flags for TCIC_SCTRL */
#define TCIC_SCTRL_ENA 0x01
#define TCIC_SCTRL_INCMODE 0x18
#define TCIC_SCTRL_INCMODE_HOLD 0x00
#define TCIC_SCTRL_INCMODE_WORD 0x08
#define TCIC_SCTRL_INCMODE_REG 0x10
#define TCIC_SCTRL_INCMODE_AUTO 0x18
#define TCIC_SCTRL_EDCSUM 0x20
#define TCIC_SCTRL_RESET 0x80
/* Flags for TCIC_SSTAT */
#define TCIC_SSTAT_6US 0x01
#define TCIC_SSTAT_10US 0x02
#define TCIC_SSTAT_PROGTIME 0x04
#define TCIC_SSTAT_LBAT1 0x08
#define TCIC_SSTAT_LBAT2 0x10
#define TCIC_SSTAT_RDY 0x20 /* Inverted */
#define TCIC_SSTAT_WP 0x40
#define TCIC_SSTAT_CD 0x80 /* Card detect */
/* Flags for TCIC_MODE */
#define TCIC_MODE_PGMMASK 0x1f
#define TCIC_MODE_NORMAL 0x00
#define TCIC_MODE_PGMWR 0x01
#define TCIC_MODE_PGMRD 0x02
#define TCIC_MODE_PGMCE 0x04
#define TCIC_MODE_PGMDBW 0x08
#define TCIC_MODE_PGMWORD 0x10
#define TCIC_MODE_AUXSEL_MASK 0xe0
/* Registers accessed through TCIC_AUX, by setting TCIC_MODE */
#define TCIC_AUX_TCTL (0<<5)
#define TCIC_AUX_PCTL (1<<5)
#define TCIC_AUX_WCTL (2<<5)
#define TCIC_AUX_EXTERN (3<<5)
#define TCIC_AUX_PDATA (4<<5)
#define TCIC_AUX_SYSCFG (5<<5)
#define TCIC_AUX_ILOCK (6<<5)
#define TCIC_AUX_TEST (7<<5)
/* Flags for TCIC_PWR */
#define TCIC_PWR_VCC(sock) (0x01<<(sock))
#define TCIC_PWR_VCC_MASK 0x03
#define TCIC_PWR_VPP(sock) (0x08<<(sock))
#define TCIC_PWR_VPP_MASK 0x18
#define TCIC_PWR_CLIMENA 0x40
#define TCIC_PWR_CLIMSTAT 0x80
/* Flags for TCIC_ICSR */
#define TCIC_ICSR_CLEAR 0x01
#define TCIC_ICSR_SET 0x02
#define TCIC_ICSR_JAM (TCIC_ICSR_CLEAR|TCIC_ICSR_SET)
#define TCIC_ICSR_STOPCPU 0x04
#define TCIC_ICSR_ILOCK 0x08
#define TCIC_ICSR_PROGTIME 0x10
#define TCIC_ICSR_ERR 0x20
#define TCIC_ICSR_CDCHG 0x40
#define TCIC_ICSR_IOCHK 0x80
/* Flags for TCIC_IENA */
#define TCIC_IENA_CFG_MASK 0x03
#define TCIC_IENA_CFG_OFF 0x00 /* disabled */
#define TCIC_IENA_CFG_OD 0x01 /* active low, open drain */
#define TCIC_IENA_CFG_LOW 0x02 /* active low, totem pole */
#define TCIC_IENA_CFG_HIGH 0x03 /* active high, totem pole */
#define TCIC_IENA_ILOCK 0x08
#define TCIC_IENA_PROGTIME 0x10
#define TCIC_IENA_ERR 0x20 /* overcurrent or iochk */
#define TCIC_IENA_CDCHG 0x40
/* Flags for TCIC_AUX_WCTL */
#define TCIC_WAIT_COUNT_MASK 0x001f
#define TCIC_WAIT_ASYNC 0x0020
#define TCIC_WAIT_SENSE 0x0040
#define TCIC_WAIT_SRC 0x0080
#define TCIC_WCTL_WR 0x0100
#define TCIC_WCTL_RD 0x0200
#define TCIC_WCTL_CE 0x0400
#define TCIC_WCTL_LLBAT1 0x0800
#define TCIC_WCTL_LLBAT2 0x1000
#define TCIC_WCTL_LRDY 0x2000
#define TCIC_WCTL_LWP 0x4000
#define TCIC_WCTL_LCD 0x8000
/* Flags for TCIC_AUX_SYSCFG */
#define TCIC_SYSCFG_IRQ_MASK 0x000f
#define TCIC_SYSCFG_MCSFULL 0x0010
#define TCIC_SYSCFG_IO1723 0x0020
#define TCIC_SYSCFG_MCSXB 0x0040
#define TCIC_SYSCFG_ICSXB 0x0080
#define TCIC_SYSCFG_NOPDN 0x0100
#define TCIC_SYSCFG_MPSEL_SHFT 9
#define TCIC_SYSCFG_MPSEL_MASK 0x0e00
#define TCIC_SYSCFG_MPSENSE 0x2000
#define TCIC_SYSCFG_AUTOBUSY 0x4000
#define TCIC_SYSCFG_ACC 0x8000
#define TCIC_ILOCK_OUT 0x01
#define TCIC_ILOCK_SENSE 0x02
#define TCIC_ILOCK_CRESET 0x04
#define TCIC_ILOCK_CRESENA 0x08
#define TCIC_ILOCK_CWAIT 0x10
#define TCIC_ILOCK_CWAITSNS 0x20
#define TCIC_ILOCK_HOLD_MASK 0xc0
#define TCIC_ILOCK_HOLD_CCLK 0xc0
#define TCIC_ILOCKTEST_ID_SH 8
#define TCIC_ILOCKTEST_ID_MASK 0x7f00
#define TCIC_ILOCKTEST_MCIC_1 0x8000
#define TCIC_ID_DB86082 0x02
#define TCIC_ID_DB86082A 0x03
#define TCIC_ID_DB86084 0x04
#define TCIC_ID_DB86084A 0x08
#define TCIC_ID_DB86072 0x15
#define TCIC_ID_DB86184 0x14
#define TCIC_ID_DB86082B 0x17
#define TCIC_TEST_DIAG 0x8000
/*
* Indirectly addressed registers
*/
#define TCIC_SCF1(sock) ((sock)<<3)
#define TCIC_SCF2(sock) (((sock)<<3)+2)
/* Flags for SCF1 */
#define TCIC_SCF1_IRQ_MASK 0x000f
#define TCIC_SCF1_IRQ_OFF 0x0000
#define TCIC_SCF1_IRQOC 0x0010
#define TCIC_SCF1_PCVT 0x0020
#define TCIC_SCF1_IRDY 0x0040
#define TCIC_SCF1_ATA 0x0080
#define TCIC_SCF1_DMA_SHIFT 8
#define TCIC_SCF1_DMA_MASK 0x0700
#define TCIC_SCF1_DMA_OFF 0
#define TCIC_SCF1_DREQ2 2
#define TCIC_SCF1_IOSTS 0x0800
#define TCIC_SCF1_SPKR 0x1000
#define TCIC_SCF1_FINPACK 0x2000
#define TCIC_SCF1_DELWR 0x4000
#define TCIC_SCF1_HD7IDE 0x8000
/* Flags for SCF2 */
#define TCIC_SCF2_RI 0x0001
#define TCIC_SCF2_IDBR 0x0002
#define TCIC_SCF2_MDBR 0x0004
#define TCIC_SCF2_MLBAT1 0x0008
#define TCIC_SCF2_MLBAT2 0x0010
#define TCIC_SCF2_MRDY 0x0020
#define TCIC_SCF2_MWP 0x0040
#define TCIC_SCF2_MCD 0x0080
#define TCIC_SCF2_MALL 0x00f8
/* Indirect addresses for memory window registers */
#define TCIC_MWIN(sock,map) (0x100+(((map)+((sock)<<2))<<3))
#define TCIC_MBASE_X 2
#define TCIC_MMAP_X 4
#define TCIC_MCTL_X 6
#define TCIC_MBASE_4K_BIT 0x4000
#define TCIC_MBASE_HA_SHFT 12
#define TCIC_MBASE_HA_MASK 0x0fff
#define TCIC_MMAP_REG 0x8000
#define TCIC_MMAP_CA_SHFT 12
#define TCIC_MMAP_CA_MASK 0x3fff
#define TCIC_MCTL_WSCNT_MASK 0x001f
#define TCIC_MCTL_WCLK 0x0020
#define TCIC_MCTL_WCLK_CCLK 0x0000
#define TCIC_MCTL_WCLK_BCLK 0x0020
#define TCIC_MCTL_QUIET 0x0040
#define TCIC_MCTL_WP 0x0080
#define TCIC_MCTL_ACC 0x0100
#define TCIC_MCTL_KE 0x0200
#define TCIC_MCTL_EDC 0x0400
#define TCIC_MCTL_B8 0x0800
#define TCIC_MCTL_SS_SHFT TCIC_SS_SHFT
#define TCIC_MCTL_SS_MASK TCIC_SS_MASK
#define TCIC_MCTL_ENA 0x8000
/* Indirect addresses for I/O window registers */
#define TCIC_IWIN(sock,map) (0x200+(((map)+((sock)<<1))<<2))
#define TCIC_IBASE_X 0
#define TCIC_ICTL_X 2
#define TCIC_ICTL_WSCNT_MASK TCIC_MCTL_WSCNT_MASK
#define TCIC_ICTL_QUIET TCIC_MCTL_QUIET
#define TCIC_ICTL_1K 0x0080
#define TCIC_ICTL_PASS16 0x0100
#define TCIC_ICTL_ACC TCIC_MCTL_ACC
#define TCIC_ICTL_TINY 0x0200
#define TCIC_ICTL_B16 0x0400
#define TCIC_ICTL_B8 TCIC_MCTL_B8
#define TCIC_ICTL_BW_MASK (TCIC_ICTL_B16|TCIC_ICTL_B8)
#define TCIC_ICTL_BW_DYN 0
#define TCIC_ICTL_BW_8 TCIC_ICTL_B8
#define TCIC_ICTL_BW_16 TCIC_ICTL_B16
#define TCIC_ICTL_BW_ATA (TCIC_ICTL_B16|TCIC_ICTL_B8)
#define TCIC_ICTL_SS_SHFT TCIC_SS_SHFT
#define TCIC_ICTL_SS_MASK TCIC_SS_MASK
#define TCIC_ICTL_ENA TCIC_MCTL_ENA
#endif /* _LINUX_TCIC_H */