|
@@ -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);
|
|
|
}
|
|
|
|