lkml.org 
[lkml]   [2003]   [Apr]   [15]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Subject[PATCH] i82092 PCI-PCMCIA bridge driver fixes
From
Date
This device got very unhappy if it was put on a PCI bus with no ISA
bridge present and it didn't see any ISA transactions. Attached patch
makes it work with higher memory addresses and also makes it aware of
the I/O port restrictions that PCI busses may have in PeeCee-class
hardware.

Please apply.

===== drivers/pcmcia/i82092.c 1.4 vs edited =====
--- 1.4/drivers/pcmcia/i82092.c Sun Sep 29 20:32:03 2002
+++ edited/drivers/pcmcia/i82092.c Tue Apr 15 17:43:22 2003
@@ -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@redhat.com>
- * Loosly based on i82365.c from the pcmcia-cs package
+ * Author: Arjan Van De Ven <arjanv@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>
@@ -26,6 +26,11 @@
#include "i82365.h"

MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Red Hat, Inc. - Arjan Van De Ven <arjanv@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[] = {
@@ -73,9 +78,9 @@
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 */
socket_cap_t cap;
-
+
unsigned int pending_events; /* Pending events on this interface */

void (*handler)(void *info, u_int events);
@@ -87,19 +92,35 @@

#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;
+ struct pci_dev *parent;
int i;

enter("i82092aa_pci_probe");

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:
@@ -122,6 +143,53 @@
break;
}

+ 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 = (dev->resource[0].start & ~1);
@@ -129,10 +197,13 @@
request_region(sockets[i].io_base, 2, "i82092aa");


- sockets[i].cap.features |= SS_CAP_PCCARD;
+ sockets[i].cap.features |= SS_CAP_PCCARD | SS_CAP_PAGE_REGS;
sockets[i].cap.map_size = 0x1000;
sockets[i].cap.irq_mask = 0;
sockets[i].cap.pci_irq = dev->irq;
+
+ /* Trick the resource code into doing the right thing... */
+ sockets[i].cap.cb_dev = dev;

if (card_present(i)) {
sockets[i].card_state = 3;
@@ -177,7 +248,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);
@@ -191,7 +262,7 @@

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);
@@ -208,7 +279,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;
@@ -220,7 +291,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);
@@ -237,7 +308,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);
@@ -253,7 +324,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);
@@ -299,7 +370,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);
}
@@ -343,7 +414,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) {
@@ -419,7 +490,6 @@

enter("i82092aa_init");

- mem.sys_stop = 0x0fff;
i82092aa_set_socket(s, &dead_socket);
for (i = 0; i < 2; i++) {
io.map = i;
@@ -604,11 +674,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 */
}

@@ -616,11 +686,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;
}
@@ -628,18 +698,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->Vcc);
leave("i82092aa_set_socket");
return -EINVAL;
}
@@ -797,7 +867,7 @@
mem->card_start = ( (unsigned long)(i & 0x3fff)<12) + mem->sys_start;
mem->card_start &= 0x3ffffff;

- printk("Card %i is from %lx to %lx \n",sock,mem->sys_start,mem->sys_stop);
+ dprintk("Card %i is from %lx to %lx \n",sock,mem->sys_start,mem->sys_stop);

leave("i82092aa_get_mem_map");
return 0;
@@ -808,7 +878,7 @@
{
unsigned short base, i;
unsigned char map;
-
+
enter("i82092aa_set_mem_map");

map = mem->map;
@@ -817,17 +887,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); */
@@ -862,7 +937,7 @@

/* 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) {
@@ -872,10 +947,10 @@
/* printk("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;



--
dwmw2

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

\
 
 \ /
  Last update: 2005-03-22 13:34    [W:0.029 / U:0.356 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site