[CFT] pcmcia: i82092aa socket driver update [forward-port from 2.4]
Dominik Brodowski
linux at brodo.de
Fri Jun 20 10:22:25 BST 2003
Forward-port of Arjan's/David's updates for the i82092.c socket driver from
2.4.21. Could anyone with the right hardware please verify that it works?
Thanks in advance,
Dominik
i82092.c | 184 +++++++++++++++++++++++++++++++++++++++++++--------------------
1 files changed, 128 insertions(+), 56 deletions(-)
diff -ruN linux-original/drivers/pcmcia/i82092.c linux/drivers/pcmcia/i82092.c
--- linux-original/drivers/pcmcia/i82092.c 2003-06-20 08:48:26.000000000 +0200
+++ linux/drivers/pcmcia/i82092.c 2003-06-20 09:19:13.000000000 +0200
@@ -1,12 +1,12 @@
/*
* Driver for Intel I82092AA PCI-PCMCIA bridge.
*
- * (C) 2001 Red Hat, Inc.
+ * (C) 2001-2003 Red Hat, Inc.
*
- * Author: Arjan Van De Ven <arjanv at redhat.com>
- * Loosly based on i82365.c from the pcmcia-cs package
+ * Author: Arjan Van De Ven <arjanv at redhat.com>
+ * Loosely based on i82365.c from the pcmcia-cs package
*
- * $Id: i82092aa.c,v 1.2 2001/10/23 14:43:34 arjanv Exp $
+ * $Id: i82092.c,v 1.16 2003/04/15 16:36:42 dwmw2 Exp $
*/
#include <linux/kernel.h>
@@ -29,6 +29,11 @@
#include "i82365.h"
MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Red Hat, Inc. - Arjan Van De Ven <arjanv at redhat.com>");
+MODULE_DESCRIPTION("Socket driver for Intel i82092AA PCI-PCMCIA bridge");
+
+/* Extra i82092-specific register */
+#define I365_CPAGE 0x26
/* PCI core routines */
static struct pci_device_id i82092aa_pci_ids[] = {
@@ -83,7 +88,7 @@
1 = empty socket,
2 = card but not initialized,
3 = operational card */
- int io_base; /* base io address of the socket */
+ unsigned long io_base; /* base io address of the socket */
unsigned int pending_events; /* Pending events on this interface */
@@ -97,53 +102,120 @@
#define MAX_SOCKETS 4
static struct socket_info sockets[MAX_SOCKETS];
-static int socket_count; /* shortcut */
+static int socket_count; /* shortcut */
+
+int membase = -1;
+int isa_setup;
+MODULE_PARM(membase, "i");
+MODULE_PARM(isa_setup, "i");
static int __init i82092aa_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
unsigned char configbyte;
- int i, ret;
+ struct pci_dev *parent;
+ int ret, i;
enter("i82092aa_pci_probe");
- if ((ret = pci_enable_device(dev)))
- return ret;
-
+ if (pci_enable_device(dev))
+ return -EIO;
+
+ /* Since we have no memory BARs some firmware we may not
+ have had PCI_COMMAND_MEM enabled, yet the device needs
+ it. */
+ pci_read_config_byte(dev, PCI_COMMAND, &configbyte);
+ if (!(configbyte | PCI_COMMAND_MEMORY)) {
+ dprintk(KERN_DEBUG "Enabling PCI_COMMAND_MEMORY\n");
+ configbyte |= PCI_COMMAND_MEMORY;
+ pci_write_config_byte(dev, PCI_COMMAND, configbyte);
+ }
+
pci_read_config_byte(dev, 0x40, &configbyte); /* PCI Configuration Control */
switch(configbyte&6) {
case 0:
+ printk(KERN_INFO "i82092aa: configured as a 2 socket device.\n");
socket_count = 2;
break;
case 2:
+ printk(KERN_INFO "i82092aa: configured as a 1 socket device.\n");
socket_count = 1;
break;
case 4:
case 6:
+ printk(KERN_INFO "i82092aa: configured as a 4 socket device.\n");
socket_count = 4;
break;
default:
printk(KERN_ERR "i82092aa: Oops, you did something we didn't think of.\n");
- ret = -EIO;
- goto err_out_disable;
+ return -EIO;
+ break;
}
- printk(KERN_INFO "i82092aa: 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;
+
+ if (membase == -1) {
+ for (i = 0; i < 4; i++) {
+ if ((dev->bus->resource[i]->flags & (IORESOURCE_MEM|IORESOURCE_READONLY|IORESOURCE_CACHEABLE|IORESOURCE_SHADOWABLE))
+ == IORESOURCE_MEM) {
+ membase = dev->bus->resource[i]->start >> 24;
+ goto mem_ok;
+ }
+ }
+ printk(KERN_WARNING "No suitable memory range for i82092aa found\n");
+ return -ENOSPC;
}
+ mem_ok:
+ if (membase)
+ printk(KERN_NOTICE "i82092 memory base address set to 0x%02x000000\n", membase);
+
+ /* If we're down the end of the PCI bus chain where ISA cycles don't get sent, then
+ only 1/4 of the I/O address space is going to be usable, unless we make sure that
+ the NO_ISA bit in the Bridge Control register of all upstream busses is cleared.
+ Since some PCMCIA cards (most serial ports, for example) will decode 10 bits and
+ respond only to addresses where bits 8 and 9 are non-zero, we need to do this. */
+ for (parent = dev->bus->self;
+ parent && (parent->class>>8) == PCI_CLASS_BRIDGE_PCI;
+ parent = parent->bus->self) {
+ uint16_t brctl;
+
+ if (pci_read_config_word(parent, PCI_BRIDGE_CONTROL, &brctl)) {
+ printk(KERN_WARNING "Error reading bridge control word from device %s\n", parent->slot_name);
+ continue;
+ }
+
+ if (!(brctl & PCI_BRIDGE_CTL_NO_ISA))
+ continue;
+
+ if (isa_setup) {
+ printk(KERN_NOTICE "PCI bridge %s has NOISA bit set. Clearing to allow full PCMCIA operation\n",
+ parent->slot_name);
+ brctl &= ~PCI_BRIDGE_CTL_NO_ISA;
+ if (pci_write_config_word(parent, PCI_BRIDGE_CONTROL, brctl))
+ printk(KERN_WARNING "Error writing bridge control word from device %s\n", parent->slot_name);
+ } else {
+ printk(KERN_NOTICE "PCI bridge %s has NOISA bit set. Some I/O addresses for PCMCIA cards will not work.\n",
+ parent->slot_name);
+ printk(KERN_NOTICE "Perhaps use 'isa_setup=1' option to i82092.o?\n");
+ break;
+ }
+ }
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].socket.features |= SS_CAP_PCCARD;
+ sockets[i].io_base = (dev->resource[0].start & ~1);
+ if (sockets[i].io_base > 0)
+ request_region(sockets[i].io_base, 2, "i82092aa");
+
+
+ sockets[i].socket.features |= SS_CAP_PCCARD | SS_CAP_PAGE_REGS;
sockets[i].socket.map_size = 0x1000;
sockets[i].socket.irq_mask = 0;
sockets[i].socket.pci_irq = dev->irq;
sockets[i].number = i;
+
+ /* Trick the resource code into doing the right thing... */
+ sockets[i].socket.cb_dev = dev;
if (card_present(i)) {
sockets[i].card_state = 3;
@@ -187,7 +259,6 @@
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;
}
@@ -212,7 +283,7 @@
static unsigned char indirect_read(int socket, unsigned short reg)
{
- unsigned short int port;
+ unsigned long port;
unsigned char val;
unsigned long flags;
spin_lock_irqsave(&port_lock,flags);
@@ -227,7 +298,7 @@
#if 0
static unsigned short indirect_read16(int socket, unsigned short reg)
{
- unsigned short int port;
+ unsigned long port;
unsigned short tmp;
unsigned long flags;
spin_lock_irqsave(&port_lock,flags);
@@ -245,7 +316,7 @@
static void indirect_write(int socket, unsigned short reg, unsigned char value)
{
- unsigned short int port;
+ unsigned long port;
unsigned long flags;
spin_lock_irqsave(&port_lock,flags);
reg = reg + socket * 0x40;
@@ -257,7 +328,7 @@
static void indirect_setbit(int socket, unsigned short reg, unsigned char mask)
{
- unsigned short int port;
+ unsigned long port;
unsigned char val;
unsigned long flags;
spin_lock_irqsave(&port_lock,flags);
@@ -274,7 +345,7 @@
static void indirect_resetbit(int socket, unsigned short reg, unsigned char mask)
{
- unsigned short int port;
+ unsigned long port;
unsigned char val;
unsigned long flags;
spin_lock_irqsave(&port_lock,flags);
@@ -290,7 +361,7 @@
static void indirect_write16(int socket, unsigned short reg, unsigned short value)
{
- unsigned short int port;
+ unsigned long port;
unsigned char val;
unsigned long flags;
spin_lock_irqsave(&port_lock,flags);
@@ -320,7 +391,7 @@
else
return 0;
}
-
+
/* Interrupt handler functionality */
@@ -331,7 +402,7 @@
for (i=0; i < socket_count; i++) {
events = xchg(&(sockets[i].pending_events),0);
- printk("events = %x \n",events);
+ dprintk("events = %x \n",events);
if (sockets[i].handler)
sockets[i].handler(sockets[i].info, events);
}
@@ -375,7 +446,7 @@
if (csc & I365_CSC_DETECT) {
events |= SS_DETECT;
- printk("Card detected in socket %i!\n",i);
+ dprintk("Card detected in socket %i!\n",i);
}
if (indirect_read(i,I365_INTCTL) & I365_PC_IOCARD) {
@@ -412,7 +483,7 @@
unsigned int val;
enter("card_present");
- if ((socketno<0) || (socketno >= MAX_SOCKETS))
+ if ((socketno<0) || (socketno > MAX_SOCKETS))
return 0;
if (sockets[socketno].io_base == 0)
return 0;
@@ -451,7 +522,6 @@
enter("i82092aa_init");
- mem.sys_stop = 0x0fff;
i82092aa_set_socket(sock, &dead_socket);
for (i = 0; i < 2; i++) {
io.map = i;
@@ -626,11 +696,11 @@
reg = I365_PWR_NORESET; /* default: disable resetdrv on resume */
if (state->flags & SS_PWR_AUTO) {
- printk("Auto power\n");
+ dprintk("Auto power\n");
reg |= I365_PWR_AUTO; /* automatic power mngmnt */
}
if (state->flags & SS_OUTPUT_ENA) {
- printk("Power Enabled \n");
+ dprintk("Power Enabled \n");
reg |= I365_PWR_OUT; /* enable power */
}
@@ -638,11 +708,11 @@
case 0:
break;
case 50:
- printk("setting voltage to Vcc to 5V on socket %i\n",sock);
+ dprintk("setting voltage to Vcc to 5V on socket %i\n",sock);
reg |= I365_VCC_5V;
break;
default:
- printk("i82092aa: i82092aa_set_socket called with invalid VCC power value: %i ", state->Vcc);
+ printk(KERN_WARNING "i82092aa: i82092aa_set_socket called with invalid VCC power value: %i ", state->Vcc);
leave("i82092aa_set_socket");
return -EINVAL;
}
@@ -650,18 +720,18 @@
switch (state->Vpp) {
case 0:
- printk("not setting Vpp on socket %i\n",sock);
+ dprintk("not setting Vpp on socket %i\n",sock);
break;
case 50:
- printk("setting Vpp to 5.0 for socket %i\n",sock);
+ dprintk("setting Vpp to 5.0 for socket %i\n",sock);
reg |= I365_VPP1_5V | I365_VPP2_5V;
break;
case 120:
- printk("setting Vpp to 12.0\n");
+ dprintk("setting Vpp to 12.0\n");
reg |= I365_VPP1_12V | I365_VPP2_12V;
break;
default:
- printk("i82092aa: i82092aa_set_socket called with invalid VPP power value: %i ", state->Vcc);
+ printk(KERN_WARNING "i82092aa: i82092aa_set_socket called with invalid VPP power value: %i ", state->Vpp);
leave("i82092aa_set_socket");
return -EINVAL;
}
@@ -701,7 +771,7 @@
{
unsigned int sock = container_of(socket, struct socket_info, socket)->number;
unsigned char map, ioctl;
-
+
enter("i82092aa_set_io_map");
map = io->map;
@@ -720,7 +790,7 @@
if (indirect_read(sock, I365_ADDRWIN) & I365_ENA_IO(map))
indirect_resetbit(sock, I365_ADDRWIN, I365_ENA_IO(map));
-/* printk("set_io_map: Setting range to %x - %x \n",io->start,io->stop); */
+ dprintk("set_io_map: Setting range to %x - %x \n",io->start,io->stop);
/* write the new values */
indirect_write16(sock,I365_IO(map)+I365_W_START,io->start);
@@ -746,7 +816,7 @@
unsigned int sock = container_of(socket, struct socket_info, socket)->number;
unsigned short base, i;
unsigned char map;
-
+
enter("i82092aa_set_mem_map");
map = mem->map;
@@ -755,20 +825,22 @@
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));
+
+ if (!(mem->flags & MAP_ACTIVE))
+ return 0;
+
if ( (mem->card_start > 0x3ffffff) || (mem->sys_start > mem->sys_stop) ||
+ ((mem->sys_start >> 24) != membase) || ((mem->sys_stop >> 24) != membase) ||
(mem->speed > 1000) ) {
leave("i82092aa_set_mem_map: invalid address / speed");
- printk("invalid mem map for socket %i : %lx to %lx with a start of %x \n",sock,mem->sys_start, mem->sys_stop, mem->card_start);
+ printk(KERN_WARNING "invalid mem map for socket %i : %lx to %lx with a start of %x \n",sock,mem->sys_start, mem->sys_stop, 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));
-
-/* printk("set_mem_map: Setting map %i range to %x - %x on socket %i, speed is %i, active = %i \n",map, mem->sys_start,mem->sys_stop,sock,mem->speed,mem->flags & MAP_ACTIVE); */
+ dprintk("set_mem_map: Setting map %i range to %x - %x on socket %i, speed is %i, active = %i \n",map, mem->sys_start,mem->sys_stop,sock,mem->speed,mem->flags & MAP_ACTIVE);
/* write the start address */
base = I365_MEM(map);
@@ -800,20 +872,20 @@
/* card start */
- i = ((mem->card_start - mem->sys_start) >> 12) & 0x3fff;
+ i = (((mem->card_start - mem->sys_start) >> 12) - (membase << 12)) & 0x3fff;
if (mem->flags & MAP_WRPROT)
i |= I365_MEM_WRPROT;
if (mem->flags & MAP_ATTRIB) {
-/* printk("requesting attribute memory for socket %i\n",sock);*/
+ dprintk("requesting attribute memory for socket %i\n",sock);
i |= I365_MEM_REG;
} else {
-/* printk("requesting normal memory for socket %i\n",sock);*/
+ dprintk("requesting normal memory for socket %i\n",sock);
}
indirect_write16(sock,base+I365_W_OFF,i);
-
+ indirect_write(sock, I365_CPAGE, membase);
+
/* Enable the window if necessary */
- if (mem->flags & MAP_ACTIVE)
- indirect_setbit(sock, I365_ADDRWIN, I365_ENA_MEM(map));
+ indirect_setbit(sock, I365_ADDRWIN, I365_ENA_MEM(map));
leave("i82092aa_set_mem_map");
return 0;
More information about the linux-pcmcia
mailing list