mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-23 17:30:02 +00:00
![Jens Künzer](/assets/img/avatar_default.png)
As the PCI irq pin of the ti1130 pcmcia bridge is not connected (at least on some old IBM Thinkpad 760ED notebooks), the Cardbus IRQ has to be routed to an ISA irq. Part 3 of a series to allow the ISA irq to be used for Cardbus devices if the socket's PCI irq is unusable. [linux@dominikbrodowski.net: split up the original patch, commit message, cleanup] Signed-off-by: Jens Kuenzer <Jens.Kuenzer@fpga.homeip.net> Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net>
984 lines
28 KiB
C
984 lines
28 KiB
C
/*
|
|
* ti113x.h 1.16 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_TI113X_H
|
|
#define _LINUX_TI113X_H
|
|
|
|
|
|
/* Register definitions for TI 113X PCI-to-CardBus bridges */
|
|
|
|
/* System Control Register */
|
|
#define TI113X_SYSTEM_CONTROL 0x0080 /* 32 bit */
|
|
#define TI113X_SCR_SMIROUTE 0x04000000
|
|
#define TI113X_SCR_SMISTATUS 0x02000000
|
|
#define TI113X_SCR_SMIENB 0x01000000
|
|
#define TI113X_SCR_VCCPROT 0x00200000
|
|
#define TI113X_SCR_REDUCEZV 0x00100000
|
|
#define TI113X_SCR_CDREQEN 0x00080000
|
|
#define TI113X_SCR_CDMACHAN 0x00070000
|
|
#define TI113X_SCR_SOCACTIVE 0x00002000
|
|
#define TI113X_SCR_PWRSTREAM 0x00000800
|
|
#define TI113X_SCR_DELAYUP 0x00000400
|
|
#define TI113X_SCR_DELAYDOWN 0x00000200
|
|
#define TI113X_SCR_INTERROGATE 0x00000100
|
|
#define TI113X_SCR_CLKRUN_SEL 0x00000080
|
|
#define TI113X_SCR_PWRSAVINGS 0x00000040
|
|
#define TI113X_SCR_SUBSYSRW 0x00000020
|
|
#define TI113X_SCR_CB_DPAR 0x00000010
|
|
#define TI113X_SCR_CDMA_EN 0x00000008
|
|
#define TI113X_SCR_ASYNC_IRQ 0x00000004
|
|
#define TI113X_SCR_KEEPCLK 0x00000002
|
|
#define TI113X_SCR_CLKRUN_ENA 0x00000001
|
|
|
|
#define TI122X_SCR_SER_STEP 0xc0000000
|
|
#define TI122X_SCR_INTRTIE 0x20000000
|
|
#define TIXX21_SCR_TIEALL 0x10000000
|
|
#define TI122X_SCR_CBRSVD 0x00400000
|
|
#define TI122X_SCR_MRBURSTDN 0x00008000
|
|
#define TI122X_SCR_MRBURSTUP 0x00004000
|
|
#define TI122X_SCR_RIMUX 0x00000001
|
|
|
|
/* Multimedia Control Register */
|
|
#define TI1250_MULTIMEDIA_CTL 0x0084 /* 8 bit */
|
|
#define TI1250_MMC_ZVOUTEN 0x80
|
|
#define TI1250_MMC_PORTSEL 0x40
|
|
#define TI1250_MMC_ZVEN1 0x02
|
|
#define TI1250_MMC_ZVEN0 0x01
|
|
|
|
#define TI1250_GENERAL_STATUS 0x0085 /* 8 bit */
|
|
#define TI1250_GPIO0_CONTROL 0x0088 /* 8 bit */
|
|
#define TI1250_GPIO1_CONTROL 0x0089 /* 8 bit */
|
|
#define TI1250_GPIO2_CONTROL 0x008a /* 8 bit */
|
|
#define TI1250_GPIO3_CONTROL 0x008b /* 8 bit */
|
|
#define TI1250_GPIO_MODE_MASK 0xc0
|
|
|
|
/* IRQMUX/MFUNC Register */
|
|
#define TI122X_MFUNC 0x008c /* 32 bit */
|
|
#define TI122X_MFUNC0_MASK 0x0000000f
|
|
#define TI122X_MFUNC1_MASK 0x000000f0
|
|
#define TI122X_MFUNC2_MASK 0x00000f00
|
|
#define TI122X_MFUNC3_MASK 0x0000f000
|
|
#define TI122X_MFUNC4_MASK 0x000f0000
|
|
#define TI122X_MFUNC5_MASK 0x00f00000
|
|
#define TI122X_MFUNC6_MASK 0x0f000000
|
|
|
|
#define TI122X_MFUNC0_INTA 0x00000002
|
|
#define TI125X_MFUNC0_INTB 0x00000001
|
|
#define TI122X_MFUNC1_INTB 0x00000020
|
|
#define TI122X_MFUNC3_IRQSER 0x00001000
|
|
|
|
|
|
/* Retry Status Register */
|
|
#define TI113X_RETRY_STATUS 0x0090 /* 8 bit */
|
|
#define TI113X_RSR_PCIRETRY 0x80
|
|
#define TI113X_RSR_CBRETRY 0x40
|
|
#define TI113X_RSR_TEXP_CBB 0x20
|
|
#define TI113X_RSR_MEXP_CBB 0x10
|
|
#define TI113X_RSR_TEXP_CBA 0x08
|
|
#define TI113X_RSR_MEXP_CBA 0x04
|
|
#define TI113X_RSR_TEXP_PCI 0x02
|
|
#define TI113X_RSR_MEXP_PCI 0x01
|
|
|
|
/* Card Control Register */
|
|
#define TI113X_CARD_CONTROL 0x0091 /* 8 bit */
|
|
#define TI113X_CCR_RIENB 0x80
|
|
#define TI113X_CCR_ZVENABLE 0x40
|
|
#define TI113X_CCR_PCI_IRQ_ENA 0x20
|
|
#define TI113X_CCR_PCI_IREQ 0x10
|
|
#define TI113X_CCR_PCI_CSC 0x08
|
|
#define TI113X_CCR_SPKROUTEN 0x02
|
|
#define TI113X_CCR_IFG 0x01
|
|
|
|
#define TI1220_CCR_PORT_SEL 0x20
|
|
#define TI122X_CCR_AUD2MUX 0x04
|
|
|
|
/* Device Control Register */
|
|
#define TI113X_DEVICE_CONTROL 0x0092 /* 8 bit */
|
|
#define TI113X_DCR_5V_FORCE 0x40
|
|
#define TI113X_DCR_3V_FORCE 0x20
|
|
#define TI113X_DCR_IMODE_MASK 0x06
|
|
#define TI113X_DCR_IMODE_ISA 0x02
|
|
#define TI113X_DCR_IMODE_SERIAL 0x04
|
|
|
|
#define TI12XX_DCR_IMODE_PCI_ONLY 0x00
|
|
#define TI12XX_DCR_IMODE_ALL_SERIAL 0x06
|
|
|
|
/* Buffer Control Register */
|
|
#define TI113X_BUFFER_CONTROL 0x0093 /* 8 bit */
|
|
#define TI113X_BCR_CB_READ_DEPTH 0x08
|
|
#define TI113X_BCR_CB_WRITE_DEPTH 0x04
|
|
#define TI113X_BCR_PCI_READ_DEPTH 0x02
|
|
#define TI113X_BCR_PCI_WRITE_DEPTH 0x01
|
|
|
|
/* Diagnostic Register */
|
|
#define TI1250_DIAGNOSTIC 0x0093 /* 8 bit */
|
|
#define TI1250_DIAG_TRUE_VALUE 0x80
|
|
#define TI1250_DIAG_PCI_IREQ 0x40
|
|
#define TI1250_DIAG_PCI_CSC 0x20
|
|
#define TI1250_DIAG_ASYNC_CSC 0x01
|
|
|
|
/* DMA Registers */
|
|
#define TI113X_DMA_0 0x0094 /* 32 bit */
|
|
#define TI113X_DMA_1 0x0098 /* 32 bit */
|
|
|
|
/* ExCA IO offset registers */
|
|
#define TI113X_IO_OFFSET(map) (0x36+((map)<<1))
|
|
|
|
/* EnE test register */
|
|
#define ENE_TEST_C9 0xc9 /* 8bit */
|
|
#define ENE_TEST_C9_TLTENABLE 0x02
|
|
#define ENE_TEST_C9_PFENABLE_F0 0x04
|
|
#define ENE_TEST_C9_PFENABLE_F1 0x08
|
|
#define ENE_TEST_C9_PFENABLE (ENE_TEST_C9_PFENABLE_F0 | ENE_TEST_C9_PFENABLE_F1)
|
|
#define ENE_TEST_C9_WPDISALBLE_F0 0x40
|
|
#define ENE_TEST_C9_WPDISALBLE_F1 0x80
|
|
#define ENE_TEST_C9_WPDISALBLE (ENE_TEST_C9_WPDISALBLE_F0 | ENE_TEST_C9_WPDISALBLE_F1)
|
|
|
|
/*
|
|
* Texas Instruments CardBus controller overrides.
|
|
*/
|
|
#define ti_sysctl(socket) ((socket)->private[0])
|
|
#define ti_cardctl(socket) ((socket)->private[1])
|
|
#define ti_devctl(socket) ((socket)->private[2])
|
|
#define ti_diag(socket) ((socket)->private[3])
|
|
#define ti_mfunc(socket) ((socket)->private[4])
|
|
#define ene_test_c9(socket) ((socket)->private[5])
|
|
|
|
/*
|
|
* These are the TI specific power management handlers.
|
|
*/
|
|
static void ti_save_state(struct yenta_socket *socket)
|
|
{
|
|
ti_sysctl(socket) = config_readl(socket, TI113X_SYSTEM_CONTROL);
|
|
ti_mfunc(socket) = config_readl(socket, TI122X_MFUNC);
|
|
ti_cardctl(socket) = config_readb(socket, TI113X_CARD_CONTROL);
|
|
ti_devctl(socket) = config_readb(socket, TI113X_DEVICE_CONTROL);
|
|
ti_diag(socket) = config_readb(socket, TI1250_DIAGNOSTIC);
|
|
|
|
if (socket->dev->vendor == PCI_VENDOR_ID_ENE)
|
|
ene_test_c9(socket) = config_readb(socket, ENE_TEST_C9);
|
|
}
|
|
|
|
static void ti_restore_state(struct yenta_socket *socket)
|
|
{
|
|
config_writel(socket, TI113X_SYSTEM_CONTROL, ti_sysctl(socket));
|
|
config_writel(socket, TI122X_MFUNC, ti_mfunc(socket));
|
|
config_writeb(socket, TI113X_CARD_CONTROL, ti_cardctl(socket));
|
|
config_writeb(socket, TI113X_DEVICE_CONTROL, ti_devctl(socket));
|
|
config_writeb(socket, TI1250_DIAGNOSTIC, ti_diag(socket));
|
|
|
|
if (socket->dev->vendor == PCI_VENDOR_ID_ENE)
|
|
config_writeb(socket, ENE_TEST_C9, ene_test_c9(socket));
|
|
}
|
|
|
|
/*
|
|
* Zoom video control for TI122x/113x chips
|
|
*/
|
|
|
|
static void ti_zoom_video(struct pcmcia_socket *sock, int onoff)
|
|
{
|
|
u8 reg;
|
|
struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket);
|
|
|
|
/* If we don't have a Zoom Video switch this is harmless,
|
|
we just tristate the unused (ZV) lines */
|
|
reg = config_readb(socket, TI113X_CARD_CONTROL);
|
|
if (onoff)
|
|
/* Zoom zoom, we will all go together, zoom zoom, zoom zoom */
|
|
reg |= TI113X_CCR_ZVENABLE;
|
|
else
|
|
reg &= ~TI113X_CCR_ZVENABLE;
|
|
config_writeb(socket, TI113X_CARD_CONTROL, reg);
|
|
}
|
|
|
|
/*
|
|
* The 145x series can also use this. They have an additional
|
|
* ZV autodetect mode we don't use but don't actually need.
|
|
* FIXME: manual says its in func0 and func1 but disagrees with
|
|
* itself about this - do we need to force func0, if so we need
|
|
* to know a lot more about socket pairings in pcmcia_socket than
|
|
* we do now.. uggh.
|
|
*/
|
|
|
|
static void ti1250_zoom_video(struct pcmcia_socket *sock, int onoff)
|
|
{
|
|
struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket);
|
|
int shift = 0;
|
|
u8 reg;
|
|
|
|
ti_zoom_video(sock, onoff);
|
|
|
|
reg = config_readb(socket, TI1250_MULTIMEDIA_CTL);
|
|
reg |= TI1250_MMC_ZVOUTEN; /* ZV bus enable */
|
|
|
|
if(PCI_FUNC(socket->dev->devfn)==1)
|
|
shift = 1;
|
|
|
|
if(onoff)
|
|
{
|
|
reg &= ~(1<<6); /* Clear select bit */
|
|
reg |= shift<<6; /* Favour our socket */
|
|
reg |= 1<<shift; /* Socket zoom video on */
|
|
}
|
|
else
|
|
{
|
|
reg &= ~(1<<6); /* Clear select bit */
|
|
reg |= (1^shift)<<6; /* Favour other socket */
|
|
reg &= ~(1<<shift); /* Socket zoon video off */
|
|
}
|
|
|
|
config_writeb(socket, TI1250_MULTIMEDIA_CTL, reg);
|
|
}
|
|
|
|
static void ti_set_zv(struct yenta_socket *socket)
|
|
{
|
|
if(socket->dev->vendor == PCI_VENDOR_ID_TI)
|
|
{
|
|
switch(socket->dev->device)
|
|
{
|
|
/* There may be more .. */
|
|
case PCI_DEVICE_ID_TI_1220:
|
|
case PCI_DEVICE_ID_TI_1221:
|
|
case PCI_DEVICE_ID_TI_1225:
|
|
case PCI_DEVICE_ID_TI_4510:
|
|
socket->socket.zoom_video = ti_zoom_video;
|
|
break;
|
|
case PCI_DEVICE_ID_TI_1250:
|
|
case PCI_DEVICE_ID_TI_1251A:
|
|
case PCI_DEVICE_ID_TI_1251B:
|
|
case PCI_DEVICE_ID_TI_1450:
|
|
socket->socket.zoom_video = ti1250_zoom_video;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Generic TI init - TI has an extension for the
|
|
* INTCTL register that sets the PCI CSC interrupt.
|
|
* Make sure we set it correctly at open and init
|
|
* time
|
|
* - override: disable the PCI CSC interrupt. This makes
|
|
* it possible to use the CSC interrupt to probe the
|
|
* ISA interrupts.
|
|
* - init: set the interrupt to match our PCI state.
|
|
* This makes us correctly get PCI CSC interrupt
|
|
* events.
|
|
*/
|
|
static int ti_init(struct yenta_socket *socket)
|
|
{
|
|
u8 new, reg = exca_readb(socket, I365_INTCTL);
|
|
|
|
new = reg & ~I365_INTR_ENA;
|
|
if (socket->dev->irq)
|
|
new |= I365_INTR_ENA;
|
|
if (new != reg)
|
|
exca_writeb(socket, I365_INTCTL, new);
|
|
return 0;
|
|
}
|
|
|
|
static int ti_override(struct yenta_socket *socket)
|
|
{
|
|
u8 new, reg = exca_readb(socket, I365_INTCTL);
|
|
|
|
new = reg & ~I365_INTR_ENA;
|
|
if (new != reg)
|
|
exca_writeb(socket, I365_INTCTL, new);
|
|
|
|
ti_set_zv(socket);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ti113x_use_isa_irq(struct yenta_socket *socket)
|
|
{
|
|
int isa_irq = -1;
|
|
u8 intctl;
|
|
u32 isa_irq_mask = 0;
|
|
|
|
if (!isa_probe)
|
|
return;
|
|
|
|
/* get a free isa int */
|
|
isa_irq_mask = yenta_probe_irq(socket, isa_interrupts);
|
|
if (!isa_irq_mask)
|
|
return; /* no useable isa irq found */
|
|
|
|
/* choose highest available */
|
|
for (; isa_irq_mask; isa_irq++)
|
|
isa_irq_mask >>= 1;
|
|
socket->cb_irq = isa_irq;
|
|
|
|
exca_writeb(socket, I365_CSCINT, (isa_irq << 4));
|
|
|
|
intctl = exca_readb(socket, I365_INTCTL);
|
|
intctl &= ~(I365_INTR_ENA | I365_IRQ_MASK); /* CSC Enable */
|
|
exca_writeb(socket, I365_INTCTL, intctl);
|
|
|
|
dev_info(&socket->dev->dev,
|
|
"Yenta TI113x: using isa irq %d for CardBus\n", isa_irq);
|
|
}
|
|
|
|
|
|
static int ti113x_override(struct yenta_socket *socket)
|
|
{
|
|
u8 cardctl;
|
|
|
|
cardctl = config_readb(socket, TI113X_CARD_CONTROL);
|
|
cardctl &= ~(TI113X_CCR_PCI_IRQ_ENA | TI113X_CCR_PCI_IREQ | TI113X_CCR_PCI_CSC);
|
|
if (socket->dev->irq)
|
|
cardctl |= TI113X_CCR_PCI_IRQ_ENA | TI113X_CCR_PCI_CSC | TI113X_CCR_PCI_IREQ;
|
|
else
|
|
ti113x_use_isa_irq(socket);
|
|
|
|
config_writeb(socket, TI113X_CARD_CONTROL, cardctl);
|
|
|
|
return ti_override(socket);
|
|
}
|
|
|
|
|
|
/* irqrouting for func0, probes PCI interrupt and ISA interrupts */
|
|
static void ti12xx_irqroute_func0(struct yenta_socket *socket)
|
|
{
|
|
u32 mfunc, mfunc_old, devctl;
|
|
u8 gpio3, gpio3_old;
|
|
int pci_irq_status;
|
|
|
|
mfunc = mfunc_old = config_readl(socket, TI122X_MFUNC);
|
|
devctl = config_readb(socket, TI113X_DEVICE_CONTROL);
|
|
dev_printk(KERN_INFO, &socket->dev->dev,
|
|
"TI: mfunc 0x%08x, devctl 0x%02x\n", mfunc, devctl);
|
|
|
|
/* make sure PCI interrupts are enabled before probing */
|
|
ti_init(socket);
|
|
|
|
/* test PCI interrupts first. only try fixing if return value is 0! */
|
|
pci_irq_status = yenta_probe_cb_irq(socket);
|
|
if (pci_irq_status)
|
|
goto out;
|
|
|
|
/*
|
|
* We're here which means PCI interrupts are _not_ delivered. try to
|
|
* find the right setting (all serial or parallel)
|
|
*/
|
|
dev_printk(KERN_INFO, &socket->dev->dev,
|
|
"TI: probing PCI interrupt failed, trying to fix\n");
|
|
|
|
/* for serial PCI make sure MFUNC3 is set to IRQSER */
|
|
if ((devctl & TI113X_DCR_IMODE_MASK) == TI12XX_DCR_IMODE_ALL_SERIAL) {
|
|
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:
|
|
mfunc = (mfunc & ~TI122X_MFUNC3_MASK) | TI122X_MFUNC3_IRQSER;
|
|
|
|
/* write down if changed, probe */
|
|
if (mfunc != mfunc_old) {
|
|
config_writel(socket, TI122X_MFUNC, mfunc);
|
|
|
|
pci_irq_status = yenta_probe_cb_irq(socket);
|
|
if (pci_irq_status == 1) {
|
|
dev_printk(KERN_INFO, &socket->dev->dev,
|
|
"TI: all-serial interrupts ok\n");
|
|
mfunc_old = mfunc;
|
|
goto out;
|
|
}
|
|
|
|
/* not working, back to old value */
|
|
mfunc = mfunc_old;
|
|
config_writel(socket, TI122X_MFUNC, mfunc);
|
|
|
|
if (pci_irq_status == -1)
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/* serial PCI interrupts not working fall back to parallel */
|
|
dev_printk(KERN_INFO, &socket->dev->dev,
|
|
"TI: falling back to parallel PCI interrupts\n");
|
|
devctl &= ~TI113X_DCR_IMODE_MASK;
|
|
devctl |= TI113X_DCR_IMODE_SERIAL; /* serial ISA could be right */
|
|
config_writeb(socket, TI113X_DEVICE_CONTROL, devctl);
|
|
}
|
|
|
|
/* parallel PCI interrupts: route INTA */
|
|
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:
|
|
/* make sure GPIO3 is set to INTA */
|
|
gpio3 = gpio3_old = config_readb(socket, TI1250_GPIO3_CONTROL);
|
|
gpio3 &= ~TI1250_GPIO_MODE_MASK;
|
|
if (gpio3 != gpio3_old)
|
|
config_writeb(socket, TI1250_GPIO3_CONTROL, gpio3);
|
|
break;
|
|
|
|
default:
|
|
gpio3 = gpio3_old = 0;
|
|
|
|
mfunc = (mfunc & ~TI122X_MFUNC0_MASK) | TI122X_MFUNC0_INTA;
|
|
if (mfunc != mfunc_old)
|
|
config_writel(socket, TI122X_MFUNC, mfunc);
|
|
}
|
|
|
|
/* time to probe again */
|
|
pci_irq_status = yenta_probe_cb_irq(socket);
|
|
if (pci_irq_status == 1) {
|
|
mfunc_old = mfunc;
|
|
dev_printk(KERN_INFO, &socket->dev->dev,
|
|
"TI: parallel PCI interrupts ok\n");
|
|
} else {
|
|
/* not working, back to old value */
|
|
mfunc = mfunc_old;
|
|
config_writel(socket, TI122X_MFUNC, mfunc);
|
|
if (gpio3 != gpio3_old)
|
|
config_writeb(socket, TI1250_GPIO3_CONTROL, gpio3_old);
|
|
}
|
|
|
|
out:
|
|
if (pci_irq_status < 1) {
|
|
socket->cb_irq = 0;
|
|
dev_printk(KERN_INFO, &socket->dev->dev,
|
|
"Yenta TI: no PCI interrupts. Fish. "
|
|
"Please report.\n");
|
|
}
|
|
}
|
|
|
|
|
|
/* changes the irq of func1 to match that of func0 */
|
|
static int ti12xx_align_irqs(struct yenta_socket *socket, int *old_irq)
|
|
{
|
|
struct pci_dev *func0;
|
|
|
|
/* find func0 device */
|
|
func0 = pci_get_slot(socket->dev->bus, socket->dev->devfn & ~0x07);
|
|
if (!func0)
|
|
return 0;
|
|
|
|
if (old_irq)
|
|
*old_irq = socket->cb_irq;
|
|
socket->cb_irq = socket->dev->irq = func0->irq;
|
|
|
|
pci_dev_put(func0);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* ties INTA and INTB together. also changes the devices irq to that of
|
|
* the function 0 device. call from func1 only.
|
|
* returns 1 if INTRTIE changed, 0 otherwise.
|
|
*/
|
|
static int ti12xx_tie_interrupts(struct yenta_socket *socket, int *old_irq)
|
|
{
|
|
u32 sysctl;
|
|
int ret;
|
|
|
|
sysctl = config_readl(socket, TI113X_SYSTEM_CONTROL);
|
|
if (sysctl & TI122X_SCR_INTRTIE)
|
|
return 0;
|
|
|
|
/* align */
|
|
ret = ti12xx_align_irqs(socket, old_irq);
|
|
if (!ret)
|
|
return 0;
|
|
|
|
/* tie */
|
|
sysctl |= TI122X_SCR_INTRTIE;
|
|
config_writel(socket, TI113X_SYSTEM_CONTROL, sysctl);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* undo what ti12xx_tie_interrupts() did */
|
|
static void ti12xx_untie_interrupts(struct yenta_socket *socket, int old_irq)
|
|
{
|
|
u32 sysctl = config_readl(socket, TI113X_SYSTEM_CONTROL);
|
|
sysctl &= ~TI122X_SCR_INTRTIE;
|
|
config_writel(socket, TI113X_SYSTEM_CONTROL, sysctl);
|
|
|
|
socket->cb_irq = socket->dev->irq = old_irq;
|
|
}
|
|
|
|
/*
|
|
* irqrouting for func1, plays with INTB routing
|
|
* only touches MFUNC for INTB routing. all other bits are taken
|
|
* care of in func0 already.
|
|
*/
|
|
static void ti12xx_irqroute_func1(struct yenta_socket *socket)
|
|
{
|
|
u32 mfunc, mfunc_old, devctl, sysctl;
|
|
int pci_irq_status;
|
|
|
|
mfunc = mfunc_old = config_readl(socket, TI122X_MFUNC);
|
|
devctl = config_readb(socket, TI113X_DEVICE_CONTROL);
|
|
dev_printk(KERN_INFO, &socket->dev->dev,
|
|
"TI: mfunc 0x%08x, devctl 0x%02x\n",
|
|
mfunc, devctl);
|
|
|
|
/* if IRQs are configured as tied, align irq of func1 with func0 */
|
|
sysctl = config_readl(socket, TI113X_SYSTEM_CONTROL);
|
|
if (sysctl & TI122X_SCR_INTRTIE)
|
|
ti12xx_align_irqs(socket, NULL);
|
|
|
|
/* make sure PCI interrupts are enabled before probing */
|
|
ti_init(socket);
|
|
|
|
/* test PCI interrupts first. only try fixing if return value is 0! */
|
|
pci_irq_status = yenta_probe_cb_irq(socket);
|
|
if (pci_irq_status)
|
|
goto out;
|
|
|
|
/*
|
|
* We're here which means PCI interrupts are _not_ delivered. try to
|
|
* find the right setting
|
|
*/
|
|
dev_printk(KERN_INFO, &socket->dev->dev,
|
|
"TI: probing PCI interrupt failed, trying to fix\n");
|
|
|
|
/* if all serial: set INTRTIE, probe again */
|
|
if ((devctl & TI113X_DCR_IMODE_MASK) == TI12XX_DCR_IMODE_ALL_SERIAL) {
|
|
int old_irq;
|
|
|
|
if (ti12xx_tie_interrupts(socket, &old_irq)) {
|
|
pci_irq_status = yenta_probe_cb_irq(socket);
|
|
if (pci_irq_status == 1) {
|
|
dev_printk(KERN_INFO, &socket->dev->dev,
|
|
"TI: all-serial interrupts, tied ok\n");
|
|
goto out;
|
|
}
|
|
|
|
ti12xx_untie_interrupts(socket, old_irq);
|
|
}
|
|
}
|
|
/* parallel PCI: route INTB, probe again */
|
|
else {
|
|
int old_irq;
|
|
|
|
switch (socket->dev->device) {
|
|
case PCI_DEVICE_ID_TI_1250:
|
|
/* the 1250 has one pin for IRQSER/INTB depending on devctl */
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_TI_1251A:
|
|
case PCI_DEVICE_ID_TI_1251B:
|
|
case PCI_DEVICE_ID_TI_1450:
|
|
/*
|
|
* those have a pin for IRQSER/INTB plus INTB in MFUNC0
|
|
* we alread probed the shared pin, now go for MFUNC0
|
|
*/
|
|
mfunc = (mfunc & ~TI122X_MFUNC0_MASK) | TI125X_MFUNC0_INTB;
|
|
break;
|
|
|
|
default:
|
|
mfunc = (mfunc & ~TI122X_MFUNC1_MASK) | TI122X_MFUNC1_INTB;
|
|
break;
|
|
}
|
|
|
|
/* write, probe */
|
|
if (mfunc != mfunc_old) {
|
|
config_writel(socket, TI122X_MFUNC, mfunc);
|
|
|
|
pci_irq_status = yenta_probe_cb_irq(socket);
|
|
if (pci_irq_status == 1) {
|
|
dev_printk(KERN_INFO, &socket->dev->dev,
|
|
"TI: parallel PCI interrupts ok\n");
|
|
goto out;
|
|
}
|
|
|
|
mfunc = mfunc_old;
|
|
config_writel(socket, TI122X_MFUNC, mfunc);
|
|
|
|
if (pci_irq_status == -1)
|
|
goto out;
|
|
}
|
|
|
|
/* still nothing: set INTRTIE */
|
|
if (ti12xx_tie_interrupts(socket, &old_irq)) {
|
|
pci_irq_status = yenta_probe_cb_irq(socket);
|
|
if (pci_irq_status == 1) {
|
|
dev_printk(KERN_INFO, &socket->dev->dev,
|
|
"TI: parallel PCI interrupts, tied ok\n");
|
|
goto out;
|
|
}
|
|
|
|
ti12xx_untie_interrupts(socket, old_irq);
|
|
}
|
|
}
|
|
|
|
out:
|
|
if (pci_irq_status < 1) {
|
|
socket->cb_irq = 0;
|
|
dev_printk(KERN_INFO, &socket->dev->dev,
|
|
"TI: no PCI interrupts. Fish. Please report.\n");
|
|
}
|
|
}
|
|
|
|
|
|
/* 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;
|
|
u32 sysctl;
|
|
|
|
/* 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;
|
|
|
|
case PCI_DEVICE_ID_TI_XX12:
|
|
case PCI_DEVICE_ID_TI_X515:
|
|
case PCI_DEVICE_ID_TI_X420:
|
|
case PCI_DEVICE_ID_TI_X620:
|
|
case PCI_DEVICE_ID_TI_XX21_XX11:
|
|
case PCI_DEVICE_ID_TI_7410:
|
|
case PCI_DEVICE_ID_TI_7610:
|
|
/*
|
|
* those are either single or dual slot CB with additional functions
|
|
* like 1394, smartcard reader, etc. check the TIEALL flag for them
|
|
* the TIEALL flag binds the IRQ of all functions toghether.
|
|
* we catch the single slot variants later.
|
|
*/
|
|
sysctl = config_readl(socket, TI113X_SYSTEM_CONTROL);
|
|
if (sysctl & TIXX21_SCR_TIEALL)
|
|
return 0;
|
|
|
|
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;
|
|
|
|
/*
|
|
* check that the device id of both slots match. this is needed for the
|
|
* XX21 and the XX11 controller that share the same device id for single
|
|
* and dual slot controllers. return '2nd slot empty'. we already checked
|
|
* if the interrupt is tied to another function.
|
|
*/
|
|
if (socket->dev->device != func->device)
|
|
goto out;
|
|
|
|
slot2 = pci_get_drvdata(func);
|
|
if (!slot2)
|
|
goto out;
|
|
|
|
/* check state */
|
|
yenta_get_status(&slot2->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;
|
|
|
|
/* make sure that memory burst is active */
|
|
val_orig = val = config_readl(socket, TI113X_SYSTEM_CONTROL);
|
|
if (disable_clkrun && PCI_FUNC(socket->dev->devfn) == 0) {
|
|
dev_printk(KERN_INFO, &socket->dev->dev,
|
|
"Disabling CLKRUN feature\n");
|
|
val |= TI113X_SCR_KEEPCLK;
|
|
}
|
|
if (!(val & TI122X_SCR_MRBURSTUP)) {
|
|
dev_printk(KERN_INFO, &socket->dev->dev,
|
|
"Enabling burst memory read transactions\n");
|
|
val |= TI122X_SCR_MRBURSTUP;
|
|
}
|
|
if (val_orig != val)
|
|
config_writel(socket, TI113X_SYSTEM_CONTROL, val);
|
|
|
|
/*
|
|
* Yenta expects controllers to use CSCINT to route
|
|
* CSC interrupts to PCI rather than INTVAL.
|
|
*/
|
|
val = config_readb(socket, TI1250_DIAGNOSTIC);
|
|
dev_printk(KERN_INFO, &socket->dev->dev,
|
|
"Using %s to route CSC interrupts to PCI\n",
|
|
(val & TI1250_DIAG_PCI_CSC) ? "CSCINT" : "INTVAL");
|
|
dev_printk(KERN_INFO, &socket->dev->dev,
|
|
"Routing CardBus interrupts to %s\n",
|
|
(val & TI1250_DIAG_PCI_IREQ) ? "PCI" : "ISA");
|
|
|
|
/* do irqrouting, depending on function */
|
|
if (PCI_FUNC(socket->dev->devfn) == 0)
|
|
ti12xx_irqroute_func0(socket);
|
|
else
|
|
ti12xx_irqroute_func1(socket);
|
|
|
|
/* install power hook */
|
|
socket->socket.power_hook = ti12xx_power_hook;
|
|
|
|
return ti_override(socket);
|
|
}
|
|
|
|
|
|
static int ti1250_override(struct yenta_socket *socket)
|
|
{
|
|
u8 old, diag;
|
|
|
|
old = config_readb(socket, TI1250_DIAGNOSTIC);
|
|
diag = old & ~(TI1250_DIAG_PCI_CSC | TI1250_DIAG_PCI_IREQ);
|
|
if (socket->cb_irq)
|
|
diag |= TI1250_DIAG_PCI_CSC | TI1250_DIAG_PCI_IREQ;
|
|
|
|
if (diag != old) {
|
|
dev_printk(KERN_INFO, &socket->dev->dev,
|
|
"adjusting diagnostic: %02x -> %02x\n",
|
|
old, diag);
|
|
config_writeb(socket, TI1250_DIAGNOSTIC, diag);
|
|
}
|
|
|
|
return ti12xx_override(socket);
|
|
}
|
|
|
|
|
|
/**
|
|
* EnE specific part. EnE bridges are register compatible with TI bridges but
|
|
* have their own test registers and more important their own little problems.
|
|
* Some fixup code to make everybody happy (TM).
|
|
*/
|
|
|
|
#ifdef CONFIG_YENTA_ENE_TUNE
|
|
/*
|
|
* set/clear various test bits:
|
|
* Defaults to clear the bit.
|
|
* - mask (u8) defines what bits to change
|
|
* - bits (u8) is the values to change them to
|
|
* -> it's
|
|
* current = (current & ~mask) | bits
|
|
*/
|
|
/* pci ids of devices that wants to have the bit set */
|
|
#define DEVID(_vend,_dev,_subvend,_subdev,mask,bits) { \
|
|
.vendor = _vend, \
|
|
.device = _dev, \
|
|
.subvendor = _subvend, \
|
|
.subdevice = _subdev, \
|
|
.driver_data = ((mask) << 8 | (bits)), \
|
|
}
|
|
static struct pci_device_id ene_tune_tbl[] = {
|
|
/* Echo Audio products based on motorola DSP56301 and DSP56361 */
|
|
DEVID(PCI_VENDOR_ID_MOTOROLA, 0x1801, 0xECC0, PCI_ANY_ID,
|
|
ENE_TEST_C9_TLTENABLE | ENE_TEST_C9_PFENABLE, ENE_TEST_C9_TLTENABLE),
|
|
DEVID(PCI_VENDOR_ID_MOTOROLA, 0x3410, 0xECC0, PCI_ANY_ID,
|
|
ENE_TEST_C9_TLTENABLE | ENE_TEST_C9_PFENABLE, ENE_TEST_C9_TLTENABLE),
|
|
|
|
{}
|
|
};
|
|
|
|
static void ene_tune_bridge(struct pcmcia_socket *sock, struct pci_bus *bus)
|
|
{
|
|
struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket);
|
|
struct pci_dev *dev;
|
|
struct pci_device_id *id = NULL;
|
|
u8 test_c9, old_c9, mask, bits;
|
|
|
|
list_for_each_entry(dev, &bus->devices, bus_list) {
|
|
id = (struct pci_device_id *) pci_match_id(ene_tune_tbl, dev);
|
|
if (id)
|
|
break;
|
|
}
|
|
|
|
test_c9 = old_c9 = config_readb(socket, ENE_TEST_C9);
|
|
if (id) {
|
|
mask = (id->driver_data >> 8) & 0xFF;
|
|
bits = id->driver_data & 0xFF;
|
|
|
|
test_c9 = (test_c9 & ~mask) | bits;
|
|
}
|
|
else
|
|
/* default to clear TLTEnable bit, old behaviour */
|
|
test_c9 &= ~ENE_TEST_C9_TLTENABLE;
|
|
|
|
dev_printk(KERN_INFO, &socket->dev->dev,
|
|
"EnE: chaning testregister 0xC9, %02x -> %02x\n",
|
|
old_c9, test_c9);
|
|
config_writeb(socket, ENE_TEST_C9, test_c9);
|
|
}
|
|
|
|
static int ene_override(struct yenta_socket *socket)
|
|
{
|
|
/* install tune_bridge() function */
|
|
socket->socket.tune_bridge = ene_tune_bridge;
|
|
|
|
return ti1250_override(socket);
|
|
}
|
|
#else
|
|
# define ene_override ti1250_override
|
|
#endif /* !CONFIG_YENTA_ENE_TUNE */
|
|
|
|
#endif /* _LINUX_TI113X_H */
|
|
|