[PATCH] yenta TI: turn off interrupts during card power-on #2
authorDaniel Ritz <daniel.ritz@gmx.ch>
Thu, 23 Jun 2005 07:10:12 +0000 (00:10 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Thu, 23 Jun 2005 16:45:31 +0000 (09:45 -0700)
- make boot-up card recognition more reliable (ie.  redo interrogation
  always if there is no valid 'card inserted' state) (and yes, i saw it
  happening on an o2micro controller that both CB_CBARD and CB_16BITCARD
  bits were set at the same time)

- also redo interrogation before probing the ISA interrupts.  it's safer
  to do the probing with the socket in a clean state.

- make card insert detect more reliable.  yenta_get_status() now returns
  SS_PENDING as long as the card is not completley inserted and one of the
  voltage bits is set.  also !CB_CBARD doesn't mean CB_16BITCARD.  there is
  CB_NOTACARD as well, so make an explicit check for CB_16BITCARD.

- for TI bridges: disable IRQs during power-on.  in all-serial and tied
  interrupt mode the interrupts are always disabled for single-slot
  controllers.  for two-slot contollers the disabling is only done when the
  other slot is empty.  to force disabling there is a new module parameter
  now: pwr_irqs_off=Y (which is a regression for working setups.  that's
  why it's an option, only use when required)

- modparm to disable ISA interrupt probing (isa_probe, defaults to on)

- remove unneeded code/cleanups (ie.  merge yenta_events() into
  yenta_interrupts())

Signed-off-by: Daniel Ritz <daniel.ritz@gmx.ch>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
drivers/pcmcia/cs.c
drivers/pcmcia/ti113x.h
drivers/pcmcia/yenta_socket.c
include/pcmcia/ss.h

index 03fc885..d136b3c 100644 (file)
@@ -508,6 +508,10 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay)
                cs_err(skt, "unsupported voltage key.\n");
                return CS_BAD_TYPE;
        }
+
+       if (skt->power_hook)
+               skt->power_hook(skt, HOOK_POWER_PRE);
+
        skt->socket.flags = 0;
        skt->ops->set_socket(skt, &skt->socket);
 
@@ -522,7 +526,12 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay)
                return CS_BAD_TYPE;
        }
 
-       return socket_reset(skt);
+       status = socket_reset(skt);
+
+       if (skt->power_hook)
+               skt->power_hook(skt, HOOK_POWER_POST);
+
+       return status;
 }
 
 /*
index a8a1d10..c7ba998 100644 (file)
@@ -611,6 +611,170 @@ out:
        }
 }
 
+
+/* Returns true value if the second slot of a two-slot controller is empty */
+static int ti12xx_2nd_slot_empty(struct yenta_socket *socket)
+{
+       struct pci_dev *func;
+       struct yenta_socket *slot2;
+       int devfn;
+       unsigned int state;
+       int ret = 1;
+
+       /* catch the two-slot controllers */
+       switch (socket->dev->device) {
+       case PCI_DEVICE_ID_TI_1220:
+       case PCI_DEVICE_ID_TI_1221:
+       case PCI_DEVICE_ID_TI_1225:
+       case PCI_DEVICE_ID_TI_1251A:
+       case PCI_DEVICE_ID_TI_1251B:
+       case PCI_DEVICE_ID_TI_1420:
+       case PCI_DEVICE_ID_TI_1450:
+       case PCI_DEVICE_ID_TI_1451A:
+       case PCI_DEVICE_ID_TI_1520:
+       case PCI_DEVICE_ID_TI_1620:
+       case PCI_DEVICE_ID_TI_4520:
+       case PCI_DEVICE_ID_TI_4450:
+       case PCI_DEVICE_ID_TI_4451:
+               /*
+                * there are way more, but they need to be added in yenta_socket.c
+                * and pci_ids.h first anyway.
+                */
+               break;
+
+       /* single-slot controllers have the 2nd slot empty always :) */
+       default:
+               return 1;
+       }
+
+       /* get other slot */
+       devfn = socket->dev->devfn & ~0x07;
+       func = pci_get_slot(socket->dev->bus,
+                           (socket->dev->devfn & 0x07) ? devfn : devfn | 0x01);
+       if (!func)
+               return 1;
+
+       slot2 = pci_get_drvdata(func);
+       if (!slot2)
+               goto out;
+
+       /* check state */
+       yenta_get_status(&socket->socket, &state);
+       if (state & SS_DETECT) {
+               ret = 0;
+               goto out;
+       }
+
+out:
+       pci_dev_put(func);
+       return ret;
+}
+
+/*
+ * TI specifiy parts for the power hook.
+ *
+ * some TI's with some CB's produces interrupt storm on power on. it has been
+ * seen with atheros wlan cards on TI1225 and TI1410. solution is simply to
+ * disable any CB interrupts during this time.
+ */
+static int ti12xx_power_hook(struct pcmcia_socket *sock, int operation)
+{
+       struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket);
+       u32 mfunc, devctl, sysctl;
+       u8 gpio3;
+
+       /* only POWER_PRE and POWER_POST are interesting */
+       if ((operation != HOOK_POWER_PRE) && (operation != HOOK_POWER_POST))
+               return 0;
+
+       devctl = config_readb(socket, TI113X_DEVICE_CONTROL);
+       sysctl = config_readl(socket, TI113X_SYSTEM_CONTROL);
+       mfunc = config_readl(socket, TI122X_MFUNC);
+
+       /*
+        * all serial/tied: only disable when modparm set. always doing it
+        * would mean a regression for working setups 'cos it disables the
+        * interrupts for both both slots on 2-slot controllers
+        * (and users of single slot controllers where it's save have to
+        * live with setting the modparm, most don't have to anyway)
+        */
+       if (((devctl & TI113X_DCR_IMODE_MASK) == TI12XX_DCR_IMODE_ALL_SERIAL) &&
+           (pwr_irqs_off || ti12xx_2nd_slot_empty(socket))) {
+               switch (socket->dev->device) {
+               case PCI_DEVICE_ID_TI_1250:
+               case PCI_DEVICE_ID_TI_1251A:
+               case PCI_DEVICE_ID_TI_1251B:
+               case PCI_DEVICE_ID_TI_1450:
+               case PCI_DEVICE_ID_TI_1451A:
+               case PCI_DEVICE_ID_TI_4450:
+               case PCI_DEVICE_ID_TI_4451:
+                       /* these chips have no IRQSER setting in MFUNC3  */
+                       break;
+
+               default:
+                       if (operation == HOOK_POWER_PRE)
+                               mfunc = (mfunc & ~TI122X_MFUNC3_MASK);
+                       else
+                               mfunc = (mfunc & ~TI122X_MFUNC3_MASK) | TI122X_MFUNC3_IRQSER;
+               }
+
+               return 0;
+       }
+
+       /* do the job differently for func0/1 */
+       if ((PCI_FUNC(socket->dev->devfn) == 0) ||
+           ((sysctl & TI122X_SCR_INTRTIE) &&
+            (pwr_irqs_off || ti12xx_2nd_slot_empty(socket)))) {
+               /* some bridges are different */
+               switch (socket->dev->device) {
+               case PCI_DEVICE_ID_TI_1250:
+               case PCI_DEVICE_ID_TI_1251A:
+               case PCI_DEVICE_ID_TI_1251B:
+               case PCI_DEVICE_ID_TI_1450:
+                       /* those oldies use gpio3 for INTA */
+                       gpio3 = config_readb(socket, TI1250_GPIO3_CONTROL);
+                       if (operation == HOOK_POWER_PRE)
+                               gpio3 = (gpio3 & ~TI1250_GPIO_MODE_MASK) | 0x40;
+                       else
+                               gpio3 &= ~TI1250_GPIO_MODE_MASK;
+                       config_writeb(socket, TI1250_GPIO3_CONTROL, gpio3);
+                       break;
+
+               default:
+                       /* all new bridges are the same */
+                       if (operation == HOOK_POWER_PRE)
+                               mfunc &= ~TI122X_MFUNC0_MASK;
+                       else
+                               mfunc |= TI122X_MFUNC0_INTA;
+                       config_writel(socket, TI122X_MFUNC, mfunc);
+               }
+       } else {
+               switch (socket->dev->device) {
+               case PCI_DEVICE_ID_TI_1251A:
+               case PCI_DEVICE_ID_TI_1251B:
+               case PCI_DEVICE_ID_TI_1450:
+                       /* those have INTA elsewhere and INTB in MFUNC0 */
+                       if (operation == HOOK_POWER_PRE)
+                               mfunc &= ~TI122X_MFUNC0_MASK;
+                       else
+                               mfunc |= TI125X_MFUNC0_INTB;
+                       config_writel(socket, TI122X_MFUNC, mfunc);
+
+                       break;
+
+               default:
+                       /* all new bridges are the same */
+                       if (operation == HOOK_POWER_PRE)
+                               mfunc &= ~TI122X_MFUNC1_MASK;
+                       else
+                               mfunc |= TI122X_MFUNC1_INTB;
+                       config_writel(socket, TI122X_MFUNC, mfunc);
+               }
+       }
+
+       return 0;
+}
+
 static int ti12xx_override(struct yenta_socket *socket)
 {
        u32 val, val_orig;
@@ -654,6 +818,9 @@ static int ti12xx_override(struct yenta_socket *socket)
        else
                ti12xx_irqroute_func1(socket);
 
+       /* install power hook */
+       socket->socket.power_hook = ti12xx_power_hook;
+
        return ti_override(socket);
 }
 
index 6404d97..bee0536 100644 (file)
@@ -32,6 +32,14 @@ static int disable_clkrun;
 module_param(disable_clkrun, bool, 0444);
 MODULE_PARM_DESC(disable_clkrun, "If PC card doesn't function properly, please try this option");
 
+static int isa_probe = 1;
+module_param(isa_probe, bool, 0444);
+MODULE_PARM_DESC(isa_probe, "If set ISA interrupts are probed (default). Set to N to disable probing");
+
+static int pwr_irqs_off;
+module_param(pwr_irqs_off, bool, 0644);
+MODULE_PARM_DESC(pwr_irqs_off, "Force IRQs off during power-on of slot. Use only when seeing IRQ storms!");
+
 #if 0
 #define debug(x,args...) printk(KERN_DEBUG "%s: " x, __func__ , ##args)
 #else
@@ -150,15 +158,16 @@ static int yenta_get_status(struct pcmcia_socket *sock, unsigned int *value)
 
        val  = (state & CB_3VCARD) ? SS_3VCARD : 0;
        val |= (state & CB_XVCARD) ? SS_XVCARD : 0;
-       val |= (state & (CB_CDETECT1 | CB_CDETECT2 | CB_5VCARD | CB_3VCARD
-                        | CB_XVCARD | CB_YVCARD)) ? 0 : SS_PENDING;
+       val |= (state & (CB_5VCARD | CB_3VCARD | CB_XVCARD | CB_YVCARD)) ? 0 : SS_PENDING;
+       val |= (state & (CB_CDETECT1 | CB_CDETECT2)) ? SS_PENDING : 0;
+
 
        if (state & CB_CBCARD) {
                val |= SS_CARDBUS;      
                val |= (state & CB_CARDSTS) ? SS_STSCHG : 0;
                val |= (state & (CB_CDETECT1 | CB_CDETECT2)) ? 0 : SS_DETECT;
                val |= (state & CB_PWRCYCLE) ? SS_POWERON | SS_READY : 0;
-       } else {
+       } else if (state & CB_16BITCARD) {
                u8 status = exca_readb(socket, I365_STATUS);
                val |= ((status & I365_CS_DETECT) == I365_CS_DETECT) ? SS_DETECT : 0;
                if (exca_readb(socket, I365_INTCTL) & I365_PC_IOCARD) {
@@ -405,11 +414,13 @@ static int yenta_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *
 }
 
 
-static unsigned int yenta_events(struct yenta_socket *socket)
+
+static irqreturn_t yenta_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
+       unsigned int events;
+       struct yenta_socket *socket = (struct yenta_socket *) dev_id;
        u8 csc;
        u32 cb_event;
-       unsigned int events;
 
        /* Clear interrupt status for the event */
        cb_event = cb_readl(socket, CB_SOCKET_EVENT);
@@ -426,20 +437,13 @@ static unsigned int yenta_events(struct yenta_socket *socket)
                events |= (csc & I365_CSC_BVD2) ? SS_BATWARN : 0;
                events |= (csc & I365_CSC_READY) ? SS_READY : 0;
        }
-       return events;
-}
-
-
-static irqreturn_t yenta_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-       unsigned int events;
-       struct yenta_socket *socket = (struct yenta_socket *) dev_id;
 
-       events = yenta_events(socket);
-       if (events) {
+       if (events)
                pcmcia_parse_events(&socket->socket, events);
+
+       if (cb_event || csc)
                return IRQ_HANDLED;
-       }
+
        return IRQ_NONE;
 }
 
@@ -470,11 +474,22 @@ static void yenta_clear_maps(struct yenta_socket *socket)
        }
 }
 
+/* redoes voltage interrogation if required */
+static void yenta_interrogate(struct yenta_socket *socket)
+{
+       u32 state;
+
+       state = cb_readl(socket, CB_SOCKET_STATE);
+       if (!(state & (CB_5VCARD | CB_3VCARD | CB_XVCARD | CB_YVCARD)) ||
+           (state & (CB_CDETECT1 | CB_CDETECT2 | CB_NOTACARD | CB_BADVCCREQ)) ||
+           ((state & (CB_16BITCARD | CB_CBCARD)) == (CB_16BITCARD | CB_CBCARD)))
+               cb_writel(socket, CB_SOCKET_FORCE, CB_CVSTEST);
+}
+
 /* Called at resume and initialization events */
 static int yenta_sock_init(struct pcmcia_socket *sock)
 {
        struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket);
-       u32 state;
        u16 bridge;
 
        bridge = config_readw(socket, CB_BRIDGE_CONTROL) & ~CB_BRIDGE_INTR;
@@ -486,10 +501,7 @@ static int yenta_sock_init(struct pcmcia_socket *sock)
        exca_writeb(socket, I365_GENCTL, 0x00);
 
        /* Redo card voltage interrogation */
-       state = cb_readl(socket, CB_SOCKET_STATE);
-       if (!(state & (CB_CDETECT1 | CB_CDETECT2 | CB_5VCARD |
-                      CB_3VCARD | CB_XVCARD | CB_YVCARD)))
-               cb_writel(socket, CB_SOCKET_FORCE, CB_CVSTEST);
+       yenta_interrogate(socket);
 
        yenta_clear_maps(socket);
 
@@ -856,7 +868,10 @@ static void yenta_get_socket_capabilities(struct yenta_socket *socket, u32 isa_i
        socket->socket.features |= SS_CAP_PAGE_REGS | SS_CAP_PCCARD | SS_CAP_CARDBUS;
        socket->socket.map_size = 0x1000;
        socket->socket.pci_irq = socket->cb_irq;
-       socket->socket.irq_mask = yenta_probe_irq(socket, isa_irq_mask);
+       if (isa_probe)
+               socket->socket.irq_mask = yenta_probe_irq(socket, isa_irq_mask);
+       else
+               socket->socket.irq_mask = 0;
        socket->socket.cb_dev = socket->dev;
 
        printk(KERN_INFO "Yenta: ISA IRQ mask 0x%04x, PCI irq %d\n",
@@ -996,6 +1011,7 @@ static int __devinit yenta_probe (struct pci_dev *dev, const struct pci_device_i
        }
 
        /* Figure out what the dang thing can do for the PCMCIA layer... */
+       yenta_interrogate(socket);
        yenta_get_socket_capabilities(socket, isa_interrupts);
        printk(KERN_INFO "Socket status: %08x\n", cb_readl(socket, CB_SOCKET_STATE));
 
index 6d3413a..67b867f 100644 (file)
@@ -77,6 +77,11 @@ extern socket_state_t dead_socket;
 /* Use this just for bridge windows */
 #define MAP_IOSPACE    0x20
 
+/* power hook operations */
+#define HOOK_POWER_PRE 0x01
+#define HOOK_POWER_POST        0x02
+
+
 typedef struct pccard_io_map {
     u_char     map;
     u_char     flags;
@@ -222,6 +227,9 @@ struct pcmcia_socket {
        /* Zoom video behaviour is so chip specific its not worth adding
           this to _ops */
        void                            (*zoom_video)(struct pcmcia_socket *, int);
+
+       /* so is power hook */
+       int (*power_hook)(struct pcmcia_socket *sock, int operation);
                            
        /* state thread */
        struct semaphore                skt_sem;        /* protects socket h/w state */