xref: /kernel/linux/linux-6.6/drivers/pcmcia/tcic.c (revision 62306a36)
162306a36Sopenharmony_ci/*======================================================================
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci    Device driver for Databook TCIC-2 PCMCIA controller
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci    tcic.c 1.111 2000/02/15 04:13:12
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci    The contents of this file are subject to the Mozilla Public
862306a36Sopenharmony_ci    License Version 1.1 (the "License"); you may not use this file
962306a36Sopenharmony_ci    except in compliance with the License. You may obtain a copy of
1062306a36Sopenharmony_ci    the License at http://www.mozilla.org/MPL/
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci    Software distributed under the License is distributed on an "AS
1362306a36Sopenharmony_ci    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
1462306a36Sopenharmony_ci    implied. See the License for the specific language governing
1562306a36Sopenharmony_ci    rights and limitations under the License.
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci    The initial developer of the original code is David A. Hinds
1862306a36Sopenharmony_ci    <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
1962306a36Sopenharmony_ci    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci    Alternatively, the contents of this file may be used under the
2262306a36Sopenharmony_ci    terms of the GNU General Public License version 2 (the "GPL"), in which
2362306a36Sopenharmony_ci    case the provisions of the GPL are applicable instead of the
2462306a36Sopenharmony_ci    above.  If you wish to allow the use of your version of this file
2562306a36Sopenharmony_ci    only under the terms of the GPL and not to allow others to use
2662306a36Sopenharmony_ci    your version of this file under the MPL, indicate your decision
2762306a36Sopenharmony_ci    by deleting the provisions above and replace them with the notice
2862306a36Sopenharmony_ci    and other provisions required by the GPL.  If you do not delete
2962306a36Sopenharmony_ci    the provisions above, a recipient may use your version of this
3062306a36Sopenharmony_ci    file under either the MPL or the GPL.
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci======================================================================*/
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include <linux/module.h>
3562306a36Sopenharmony_ci#include <linux/moduleparam.h>
3662306a36Sopenharmony_ci#include <linux/init.h>
3762306a36Sopenharmony_ci#include <linux/types.h>
3862306a36Sopenharmony_ci#include <linux/fcntl.h>
3962306a36Sopenharmony_ci#include <linux/string.h>
4062306a36Sopenharmony_ci#include <linux/errno.h>
4162306a36Sopenharmony_ci#include <linux/interrupt.h>
4262306a36Sopenharmony_ci#include <linux/timer.h>
4362306a36Sopenharmony_ci#include <linux/ioport.h>
4462306a36Sopenharmony_ci#include <linux/delay.h>
4562306a36Sopenharmony_ci#include <linux/workqueue.h>
4662306a36Sopenharmony_ci#include <linux/platform_device.h>
4762306a36Sopenharmony_ci#include <linux/bitops.h>
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#include <asm/io.h>
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#include <pcmcia/ss.h>
5262306a36Sopenharmony_ci#include "tcic.h"
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ciMODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
5562306a36Sopenharmony_ciMODULE_DESCRIPTION("Databook TCIC-2 PCMCIA socket driver");
5662306a36Sopenharmony_ciMODULE_LICENSE("Dual MPL/GPL");
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/*====================================================================*/
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci/* Parameters that can be set with 'insmod' */
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/* The base port address of the TCIC-2 chip */
6362306a36Sopenharmony_cistatic unsigned long tcic_base = TCIC_BASE;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/* Specify a socket number to ignore */
6662306a36Sopenharmony_cistatic int ignore = -1;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/* Probe for safe interrupts? */
6962306a36Sopenharmony_cistatic int do_scan = 1;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/* Bit map of interrupts to choose from */
7262306a36Sopenharmony_cistatic u_int irq_mask = 0xffff;
7362306a36Sopenharmony_cistatic int irq_list[16];
7462306a36Sopenharmony_cistatic unsigned int irq_list_count;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/* The card status change interrupt -- 0 means autoselect */
7762306a36Sopenharmony_cistatic int cs_irq;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/* Poll status interval -- 0 means default to interrupt */
8062306a36Sopenharmony_cistatic int poll_interval;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/* Delay for card status double-checking */
8362306a36Sopenharmony_cistatic int poll_quick = HZ/20;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/* CCLK external clock time, in nanoseconds.  70 ns = 14.31818 MHz */
8662306a36Sopenharmony_cistatic int cycle_time = 70;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cimodule_param_hw(tcic_base, ulong, ioport, 0444);
8962306a36Sopenharmony_cimodule_param(ignore, int, 0444);
9062306a36Sopenharmony_cimodule_param(do_scan, int, 0444);
9162306a36Sopenharmony_cimodule_param_hw(irq_mask, int, other, 0444);
9262306a36Sopenharmony_cimodule_param_hw_array(irq_list, int, irq, &irq_list_count, 0444);
9362306a36Sopenharmony_cimodule_param_hw(cs_irq, int, irq, 0444);
9462306a36Sopenharmony_cimodule_param(poll_interval, int, 0444);
9562306a36Sopenharmony_cimodule_param(poll_quick, int, 0444);
9662306a36Sopenharmony_cimodule_param(cycle_time, int, 0444);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/*====================================================================*/
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic irqreturn_t tcic_interrupt(int irq, void *dev);
10162306a36Sopenharmony_cistatic void tcic_timer(struct timer_list *unused);
10262306a36Sopenharmony_cistatic struct pccard_operations tcic_operations;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistruct tcic_socket {
10562306a36Sopenharmony_ci    u_short	psock;
10662306a36Sopenharmony_ci    u_char	last_sstat;
10762306a36Sopenharmony_ci    u_char	id;
10862306a36Sopenharmony_ci    struct pcmcia_socket	socket;
10962306a36Sopenharmony_ci};
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic struct timer_list poll_timer;
11262306a36Sopenharmony_cistatic int tcic_timer_pending;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic int sockets;
11562306a36Sopenharmony_cistatic struct tcic_socket socket_table[2];
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci/*====================================================================*/
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci/* Trick when selecting interrupts: the TCIC sktirq pin is supposed
12062306a36Sopenharmony_ci   to map to irq 11, but is coded as 0 or 1 in the irq registers. */
12162306a36Sopenharmony_ci#define TCIC_IRQ(x) ((x) ? (((x) == 11) ? 1 : (x)) : 15)
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci#ifdef DEBUG_X
12462306a36Sopenharmony_cistatic u_char tcic_getb(u_char reg)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci    u_char val = inb(tcic_base+reg);
12762306a36Sopenharmony_ci    printk(KERN_DEBUG "tcic_getb(%#lx) = %#x\n", tcic_base+reg, val);
12862306a36Sopenharmony_ci    return val;
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic u_short tcic_getw(u_char reg)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci    u_short val = inw(tcic_base+reg);
13462306a36Sopenharmony_ci    printk(KERN_DEBUG "tcic_getw(%#lx) = %#x\n", tcic_base+reg, val);
13562306a36Sopenharmony_ci    return val;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic void tcic_setb(u_char reg, u_char data)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci    printk(KERN_DEBUG "tcic_setb(%#lx, %#x)\n", tcic_base+reg, data);
14162306a36Sopenharmony_ci    outb(data, tcic_base+reg);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic void tcic_setw(u_char reg, u_short data)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci    printk(KERN_DEBUG "tcic_setw(%#lx, %#x)\n", tcic_base+reg, data);
14762306a36Sopenharmony_ci    outw(data, tcic_base+reg);
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci#else
15062306a36Sopenharmony_ci#define tcic_getb(reg) inb(tcic_base+reg)
15162306a36Sopenharmony_ci#define tcic_getw(reg) inw(tcic_base+reg)
15262306a36Sopenharmony_ci#define tcic_setb(reg, data) outb(data, tcic_base+reg)
15362306a36Sopenharmony_ci#define tcic_setw(reg, data) outw(data, tcic_base+reg)
15462306a36Sopenharmony_ci#endif
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic void tcic_setl(u_char reg, u_int data)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci#ifdef DEBUG_X
15962306a36Sopenharmony_ci    printk(KERN_DEBUG "tcic_setl(%#x, %#lx)\n", tcic_base+reg, data);
16062306a36Sopenharmony_ci#endif
16162306a36Sopenharmony_ci    outw(data & 0xffff, tcic_base+reg);
16262306a36Sopenharmony_ci    outw(data >> 16, tcic_base+reg+2);
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic void tcic_aux_setb(u_short reg, u_char data)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci    u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg;
16862306a36Sopenharmony_ci    tcic_setb(TCIC_MODE, mode);
16962306a36Sopenharmony_ci    tcic_setb(TCIC_AUX, data);
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic u_short tcic_aux_getw(u_short reg)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci    u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg;
17562306a36Sopenharmony_ci    tcic_setb(TCIC_MODE, mode);
17662306a36Sopenharmony_ci    return tcic_getw(TCIC_AUX);
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic void tcic_aux_setw(u_short reg, u_short data)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci    u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg;
18262306a36Sopenharmony_ci    tcic_setb(TCIC_MODE, mode);
18362306a36Sopenharmony_ci    tcic_setw(TCIC_AUX, data);
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci/*====================================================================*/
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci/* Time conversion functions */
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic int to_cycles(int ns)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci    if (ns < 14)
19362306a36Sopenharmony_ci	return 0;
19462306a36Sopenharmony_ci    else
19562306a36Sopenharmony_ci	return 2*(ns-14)/cycle_time;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci/*====================================================================*/
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic volatile u_int irq_hits;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic irqreturn_t __init tcic_irq_count(int irq, void *dev)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci    irq_hits++;
20562306a36Sopenharmony_ci    return IRQ_HANDLED;
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic u_int __init try_irq(int irq)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci    u_short cfg;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci    irq_hits = 0;
21362306a36Sopenharmony_ci    if (request_irq(irq, tcic_irq_count, 0, "irq scan", tcic_irq_count) != 0)
21462306a36Sopenharmony_ci	return -1;
21562306a36Sopenharmony_ci    mdelay(10);
21662306a36Sopenharmony_ci    if (irq_hits) {
21762306a36Sopenharmony_ci	free_irq(irq, tcic_irq_count);
21862306a36Sopenharmony_ci	return -1;
21962306a36Sopenharmony_ci    }
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci    /* Generate one interrupt */
22262306a36Sopenharmony_ci    cfg = TCIC_SYSCFG_AUTOBUSY | 0x0a00;
22362306a36Sopenharmony_ci    tcic_aux_setw(TCIC_AUX_SYSCFG, cfg | TCIC_IRQ(irq));
22462306a36Sopenharmony_ci    tcic_setb(TCIC_IENA, TCIC_IENA_ERR | TCIC_IENA_CFG_HIGH);
22562306a36Sopenharmony_ci    tcic_setb(TCIC_ICSR, TCIC_ICSR_ERR | TCIC_ICSR_JAM);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci    udelay(1000);
22862306a36Sopenharmony_ci    free_irq(irq, tcic_irq_count);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci    /* Turn off interrupts */
23162306a36Sopenharmony_ci    tcic_setb(TCIC_IENA, TCIC_IENA_CFG_OFF);
23262306a36Sopenharmony_ci    while (tcic_getb(TCIC_ICSR))
23362306a36Sopenharmony_ci	tcic_setb(TCIC_ICSR, TCIC_ICSR_JAM);
23462306a36Sopenharmony_ci    tcic_aux_setw(TCIC_AUX_SYSCFG, cfg);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci    return (irq_hits != 1);
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic u_int __init irq_scan(u_int mask0)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci    u_int mask1;
24262306a36Sopenharmony_ci    int i;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci#ifdef __alpha__
24562306a36Sopenharmony_ci#define PIC 0x4d0
24662306a36Sopenharmony_ci    /* Don't probe level-triggered interrupts -- reserved for PCI */
24762306a36Sopenharmony_ci    int level_mask = inb_p(PIC) | (inb_p(PIC+1) << 8);
24862306a36Sopenharmony_ci    if (level_mask)
24962306a36Sopenharmony_ci	mask0 &= ~level_mask;
25062306a36Sopenharmony_ci#endif
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci    mask1 = 0;
25362306a36Sopenharmony_ci    if (do_scan) {
25462306a36Sopenharmony_ci	for (i = 0; i < 16; i++)
25562306a36Sopenharmony_ci	    if ((mask0 & (1 << i)) && (try_irq(i) == 0))
25662306a36Sopenharmony_ci		mask1 |= (1 << i);
25762306a36Sopenharmony_ci	for (i = 0; i < 16; i++)
25862306a36Sopenharmony_ci	    if ((mask1 & (1 << i)) && (try_irq(i) != 0)) {
25962306a36Sopenharmony_ci		mask1 ^= (1 << i);
26062306a36Sopenharmony_ci	    }
26162306a36Sopenharmony_ci    }
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci    if (mask1) {
26462306a36Sopenharmony_ci	printk("scanned");
26562306a36Sopenharmony_ci    } else {
26662306a36Sopenharmony_ci	/* Fallback: just find interrupts that aren't in use */
26762306a36Sopenharmony_ci	for (i = 0; i < 16; i++)
26862306a36Sopenharmony_ci	    if ((mask0 & (1 << i)) &&
26962306a36Sopenharmony_ci		(request_irq(i, tcic_irq_count, 0, "x", tcic_irq_count) == 0)) {
27062306a36Sopenharmony_ci		mask1 |= (1 << i);
27162306a36Sopenharmony_ci		free_irq(i, tcic_irq_count);
27262306a36Sopenharmony_ci	    }
27362306a36Sopenharmony_ci	printk("default");
27462306a36Sopenharmony_ci    }
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci    printk(") = ");
27762306a36Sopenharmony_ci    for (i = 0; i < 16; i++)
27862306a36Sopenharmony_ci	if (mask1 & (1<<i))
27962306a36Sopenharmony_ci	    printk("%s%d", ((mask1 & ((1<<i)-1)) ? "," : ""), i);
28062306a36Sopenharmony_ci    printk(" ");
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci    return mask1;
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci/*======================================================================
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci    See if a card is present, powered up, in IO mode, and already
28862306a36Sopenharmony_ci    bound to a (non-PCMCIA) Linux driver.
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci    We make an exception for cards that look like serial devices.
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci======================================================================*/
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic int __init is_active(int s)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci    u_short scf1, ioctl, base, num;
29762306a36Sopenharmony_ci    u_char pwr, sstat;
29862306a36Sopenharmony_ci    u_int addr;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci    tcic_setl(TCIC_ADDR, (s << TCIC_ADDR_SS_SHFT)
30162306a36Sopenharmony_ci	      | TCIC_ADDR_INDREG | TCIC_SCF1(s));
30262306a36Sopenharmony_ci    scf1 = tcic_getw(TCIC_DATA);
30362306a36Sopenharmony_ci    pwr = tcic_getb(TCIC_PWR);
30462306a36Sopenharmony_ci    sstat = tcic_getb(TCIC_SSTAT);
30562306a36Sopenharmony_ci    addr = TCIC_IWIN(s, 0);
30662306a36Sopenharmony_ci    tcic_setw(TCIC_ADDR, addr + TCIC_IBASE_X);
30762306a36Sopenharmony_ci    base = tcic_getw(TCIC_DATA);
30862306a36Sopenharmony_ci    tcic_setw(TCIC_ADDR, addr + TCIC_ICTL_X);
30962306a36Sopenharmony_ci    ioctl = tcic_getw(TCIC_DATA);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci    if (ioctl & TCIC_ICTL_TINY)
31262306a36Sopenharmony_ci	num = 1;
31362306a36Sopenharmony_ci    else {
31462306a36Sopenharmony_ci	num = (base ^ (base-1));
31562306a36Sopenharmony_ci	base = base & (base-1);
31662306a36Sopenharmony_ci    }
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci    if ((sstat & TCIC_SSTAT_CD) && (pwr & TCIC_PWR_VCC(s)) &&
31962306a36Sopenharmony_ci	(scf1 & TCIC_SCF1_IOSTS) && (ioctl & TCIC_ICTL_ENA) &&
32062306a36Sopenharmony_ci	((base & 0xfeef) != 0x02e8)) {
32162306a36Sopenharmony_ci	struct resource *res = request_region(base, num, "tcic-2");
32262306a36Sopenharmony_ci	if (!res) /* region is busy */
32362306a36Sopenharmony_ci	    return 1;
32462306a36Sopenharmony_ci	release_region(base, num);
32562306a36Sopenharmony_ci    }
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci    return 0;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci/*======================================================================
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci    This returns the revision code for the specified socket.
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci======================================================================*/
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic int __init get_tcic_id(void)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci    u_short id;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci    tcic_aux_setw(TCIC_AUX_TEST, TCIC_TEST_DIAG);
34162306a36Sopenharmony_ci    id = tcic_aux_getw(TCIC_AUX_ILOCK);
34262306a36Sopenharmony_ci    id = (id & TCIC_ILOCKTEST_ID_MASK) >> TCIC_ILOCKTEST_ID_SH;
34362306a36Sopenharmony_ci    tcic_aux_setw(TCIC_AUX_TEST, 0);
34462306a36Sopenharmony_ci    return id;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci/*====================================================================*/
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic struct platform_driver tcic_driver = {
35062306a36Sopenharmony_ci	.driver = {
35162306a36Sopenharmony_ci		.name = "tcic-pcmcia",
35262306a36Sopenharmony_ci	},
35362306a36Sopenharmony_ci};
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic struct platform_device tcic_device = {
35662306a36Sopenharmony_ci	.name = "tcic-pcmcia",
35762306a36Sopenharmony_ci	.id = 0,
35862306a36Sopenharmony_ci};
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cistatic int __init init_tcic(void)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci    int i, sock, ret = 0;
36462306a36Sopenharmony_ci    u_int mask, scan;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci    if (platform_driver_register(&tcic_driver))
36762306a36Sopenharmony_ci	return -1;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci    printk(KERN_INFO "Databook TCIC-2 PCMCIA probe: ");
37062306a36Sopenharmony_ci    sock = 0;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci    if (!request_region(tcic_base, 16, "tcic-2")) {
37362306a36Sopenharmony_ci	printk("could not allocate ports,\n ");
37462306a36Sopenharmony_ci	platform_driver_unregister(&tcic_driver);
37562306a36Sopenharmony_ci	return -ENODEV;
37662306a36Sopenharmony_ci    }
37762306a36Sopenharmony_ci    else {
37862306a36Sopenharmony_ci	tcic_setw(TCIC_ADDR, 0);
37962306a36Sopenharmony_ci	if (tcic_getw(TCIC_ADDR) == 0) {
38062306a36Sopenharmony_ci	    tcic_setw(TCIC_ADDR, 0xc3a5);
38162306a36Sopenharmony_ci	    if (tcic_getw(TCIC_ADDR) == 0xc3a5) sock = 2;
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci	if (sock == 0) {
38462306a36Sopenharmony_ci	    /* See if resetting the controller does any good */
38562306a36Sopenharmony_ci	    tcic_setb(TCIC_SCTRL, TCIC_SCTRL_RESET);
38662306a36Sopenharmony_ci	    tcic_setb(TCIC_SCTRL, 0);
38762306a36Sopenharmony_ci	    tcic_setw(TCIC_ADDR, 0);
38862306a36Sopenharmony_ci	    if (tcic_getw(TCIC_ADDR) == 0) {
38962306a36Sopenharmony_ci		tcic_setw(TCIC_ADDR, 0xc3a5);
39062306a36Sopenharmony_ci		if (tcic_getw(TCIC_ADDR) == 0xc3a5) sock = 2;
39162306a36Sopenharmony_ci	    }
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci    }
39462306a36Sopenharmony_ci    if (sock == 0) {
39562306a36Sopenharmony_ci	printk("not found.\n");
39662306a36Sopenharmony_ci	release_region(tcic_base, 16);
39762306a36Sopenharmony_ci	platform_driver_unregister(&tcic_driver);
39862306a36Sopenharmony_ci	return -ENODEV;
39962306a36Sopenharmony_ci    }
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci    sockets = 0;
40262306a36Sopenharmony_ci    for (i = 0; i < sock; i++) {
40362306a36Sopenharmony_ci	if ((i == ignore) || is_active(i)) continue;
40462306a36Sopenharmony_ci	socket_table[sockets].psock = i;
40562306a36Sopenharmony_ci	socket_table[sockets].id = get_tcic_id();
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	socket_table[sockets].socket.owner = THIS_MODULE;
40862306a36Sopenharmony_ci	/* only 16-bit cards, memory windows must be size-aligned */
40962306a36Sopenharmony_ci	/* No PCI or CardBus support */
41062306a36Sopenharmony_ci	socket_table[sockets].socket.features = SS_CAP_PCCARD | SS_CAP_MEM_ALIGN;
41162306a36Sopenharmony_ci	/* irq 14, 11, 10, 7, 6, 5, 4, 3 */
41262306a36Sopenharmony_ci	socket_table[sockets].socket.irq_mask = 0x4cf8;
41362306a36Sopenharmony_ci	/* 4K minimum window size */
41462306a36Sopenharmony_ci	socket_table[sockets].socket.map_size = 0x1000;
41562306a36Sopenharmony_ci	sockets++;
41662306a36Sopenharmony_ci    }
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci    switch (socket_table[0].id) {
41962306a36Sopenharmony_ci    case TCIC_ID_DB86082:
42062306a36Sopenharmony_ci	printk("DB86082"); break;
42162306a36Sopenharmony_ci    case TCIC_ID_DB86082A:
42262306a36Sopenharmony_ci	printk("DB86082A"); break;
42362306a36Sopenharmony_ci    case TCIC_ID_DB86084:
42462306a36Sopenharmony_ci	printk("DB86084"); break;
42562306a36Sopenharmony_ci    case TCIC_ID_DB86084A:
42662306a36Sopenharmony_ci	printk("DB86084A"); break;
42762306a36Sopenharmony_ci    case TCIC_ID_DB86072:
42862306a36Sopenharmony_ci	printk("DB86072"); break;
42962306a36Sopenharmony_ci    case TCIC_ID_DB86184:
43062306a36Sopenharmony_ci	printk("DB86184"); break;
43162306a36Sopenharmony_ci    case TCIC_ID_DB86082B:
43262306a36Sopenharmony_ci	printk("DB86082B"); break;
43362306a36Sopenharmony_ci    default:
43462306a36Sopenharmony_ci	printk("Unknown ID 0x%02x", socket_table[0].id);
43562306a36Sopenharmony_ci    }
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci    /* Set up polling */
43862306a36Sopenharmony_ci    timer_setup(&poll_timer, &tcic_timer, 0);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci    /* Build interrupt mask */
44162306a36Sopenharmony_ci    printk(KERN_CONT ", %d sockets\n", sockets);
44262306a36Sopenharmony_ci    printk(KERN_INFO "  irq list (");
44362306a36Sopenharmony_ci    if (irq_list_count == 0)
44462306a36Sopenharmony_ci	mask = irq_mask;
44562306a36Sopenharmony_ci    else
44662306a36Sopenharmony_ci	for (i = mask = 0; i < irq_list_count; i++)
44762306a36Sopenharmony_ci	    mask |= (1<<irq_list[i]);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci    /* irq 14, 11, 10, 7, 6, 5, 4, 3 */
45062306a36Sopenharmony_ci    mask &= 0x4cf8;
45162306a36Sopenharmony_ci    /* Scan interrupts */
45262306a36Sopenharmony_ci    mask = irq_scan(mask);
45362306a36Sopenharmony_ci    for (i=0;i<sockets;i++)
45462306a36Sopenharmony_ci	    socket_table[i].socket.irq_mask = mask;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci    /* Check for only two interrupts available */
45762306a36Sopenharmony_ci    scan = (mask & (mask-1));
45862306a36Sopenharmony_ci    if (((scan & (scan-1)) == 0) && (poll_interval == 0))
45962306a36Sopenharmony_ci	poll_interval = HZ;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci    if (poll_interval == 0) {
46262306a36Sopenharmony_ci	/* Avoid irq 12 unless it is explicitly requested */
46362306a36Sopenharmony_ci	u_int cs_mask = mask & ((cs_irq) ? (1<<cs_irq) : ~(1<<12));
46462306a36Sopenharmony_ci	for (i = 15; i > 0; i--)
46562306a36Sopenharmony_ci	    if ((cs_mask & (1 << i)) &&
46662306a36Sopenharmony_ci		(request_irq(i, tcic_interrupt, 0, "tcic",
46762306a36Sopenharmony_ci			     tcic_interrupt) == 0))
46862306a36Sopenharmony_ci		break;
46962306a36Sopenharmony_ci	cs_irq = i;
47062306a36Sopenharmony_ci	if (cs_irq == 0) poll_interval = HZ;
47162306a36Sopenharmony_ci    }
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci    if (socket_table[0].socket.irq_mask & (1 << 11))
47462306a36Sopenharmony_ci	printk("sktirq is irq 11, ");
47562306a36Sopenharmony_ci    if (cs_irq != 0)
47662306a36Sopenharmony_ci	printk("status change on irq %d\n", cs_irq);
47762306a36Sopenharmony_ci    else
47862306a36Sopenharmony_ci	printk("polled status, interval = %d ms\n",
47962306a36Sopenharmony_ci	       poll_interval * 1000 / HZ);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci    for (i = 0; i < sockets; i++) {
48262306a36Sopenharmony_ci	tcic_setw(TCIC_ADDR+2, socket_table[i].psock << TCIC_SS_SHFT);
48362306a36Sopenharmony_ci	socket_table[i].last_sstat = tcic_getb(TCIC_SSTAT);
48462306a36Sopenharmony_ci    }
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci    /* jump start interrupt handler, if needed */
48762306a36Sopenharmony_ci    tcic_interrupt(0, NULL);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci    platform_device_register(&tcic_device);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci    for (i = 0; i < sockets; i++) {
49262306a36Sopenharmony_ci	    socket_table[i].socket.ops = &tcic_operations;
49362306a36Sopenharmony_ci	    socket_table[i].socket.resource_ops = &pccard_nonstatic_ops;
49462306a36Sopenharmony_ci	    socket_table[i].socket.dev.parent = &tcic_device.dev;
49562306a36Sopenharmony_ci	    ret = pcmcia_register_socket(&socket_table[i].socket);
49662306a36Sopenharmony_ci	    if (ret && i)
49762306a36Sopenharmony_ci		    pcmcia_unregister_socket(&socket_table[0].socket);
49862306a36Sopenharmony_ci    }
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci    return ret;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci    return 0;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci} /* init_tcic */
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci/*====================================================================*/
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_cistatic void __exit exit_tcic(void)
50962306a36Sopenharmony_ci{
51062306a36Sopenharmony_ci    int i;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci    del_timer_sync(&poll_timer);
51362306a36Sopenharmony_ci    if (cs_irq != 0) {
51462306a36Sopenharmony_ci	tcic_aux_setw(TCIC_AUX_SYSCFG, TCIC_SYSCFG_AUTOBUSY|0x0a00);
51562306a36Sopenharmony_ci	free_irq(cs_irq, tcic_interrupt);
51662306a36Sopenharmony_ci    }
51762306a36Sopenharmony_ci    release_region(tcic_base, 16);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci    for (i = 0; i < sockets; i++) {
52062306a36Sopenharmony_ci	    pcmcia_unregister_socket(&socket_table[i].socket);
52162306a36Sopenharmony_ci    }
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci    platform_device_unregister(&tcic_device);
52462306a36Sopenharmony_ci    platform_driver_unregister(&tcic_driver);
52562306a36Sopenharmony_ci} /* exit_tcic */
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci/*====================================================================*/
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic irqreturn_t tcic_interrupt(int irq, void *dev)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci    int i, quick = 0;
53262306a36Sopenharmony_ci    u_char latch, sstat;
53362306a36Sopenharmony_ci    u_short psock;
53462306a36Sopenharmony_ci    u_int events;
53562306a36Sopenharmony_ci    static volatile int active = 0;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci    if (active) {
53862306a36Sopenharmony_ci	printk(KERN_NOTICE "tcic: reentered interrupt handler!\n");
53962306a36Sopenharmony_ci	return IRQ_NONE;
54062306a36Sopenharmony_ci    } else
54162306a36Sopenharmony_ci	active = 1;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci    pr_debug("tcic_interrupt()\n");
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci    for (i = 0; i < sockets; i++) {
54662306a36Sopenharmony_ci	psock = socket_table[i].psock;
54762306a36Sopenharmony_ci	tcic_setl(TCIC_ADDR, (psock << TCIC_ADDR_SS_SHFT)
54862306a36Sopenharmony_ci		  | TCIC_ADDR_INDREG | TCIC_SCF1(psock));
54962306a36Sopenharmony_ci	sstat = tcic_getb(TCIC_SSTAT);
55062306a36Sopenharmony_ci	latch = sstat ^ socket_table[psock].last_sstat;
55162306a36Sopenharmony_ci	socket_table[i].last_sstat = sstat;
55262306a36Sopenharmony_ci	if (tcic_getb(TCIC_ICSR) & TCIC_ICSR_CDCHG) {
55362306a36Sopenharmony_ci	    tcic_setb(TCIC_ICSR, TCIC_ICSR_CLEAR);
55462306a36Sopenharmony_ci	    quick = 1;
55562306a36Sopenharmony_ci	}
55662306a36Sopenharmony_ci	if (latch == 0)
55762306a36Sopenharmony_ci	    continue;
55862306a36Sopenharmony_ci	events = (latch & TCIC_SSTAT_CD) ? SS_DETECT : 0;
55962306a36Sopenharmony_ci	events |= (latch & TCIC_SSTAT_WP) ? SS_WRPROT : 0;
56062306a36Sopenharmony_ci	if (tcic_getw(TCIC_DATA) & TCIC_SCF1_IOSTS) {
56162306a36Sopenharmony_ci	    events |= (latch & TCIC_SSTAT_LBAT1) ? SS_STSCHG : 0;
56262306a36Sopenharmony_ci	} else {
56362306a36Sopenharmony_ci	    events |= (latch & TCIC_SSTAT_RDY) ? SS_READY : 0;
56462306a36Sopenharmony_ci	    events |= (latch & TCIC_SSTAT_LBAT1) ? SS_BATDEAD : 0;
56562306a36Sopenharmony_ci	    events |= (latch & TCIC_SSTAT_LBAT2) ? SS_BATWARN : 0;
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci	if (events) {
56862306a36Sopenharmony_ci		pcmcia_parse_events(&socket_table[i].socket, events);
56962306a36Sopenharmony_ci	}
57062306a36Sopenharmony_ci    }
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci    /* Schedule next poll, if needed */
57362306a36Sopenharmony_ci    if (((cs_irq == 0) || quick) && (!tcic_timer_pending)) {
57462306a36Sopenharmony_ci	poll_timer.expires = jiffies + (quick ? poll_quick : poll_interval);
57562306a36Sopenharmony_ci	add_timer(&poll_timer);
57662306a36Sopenharmony_ci	tcic_timer_pending = 1;
57762306a36Sopenharmony_ci    }
57862306a36Sopenharmony_ci    active = 0;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci    pr_debug("interrupt done\n");
58162306a36Sopenharmony_ci    return IRQ_HANDLED;
58262306a36Sopenharmony_ci} /* tcic_interrupt */
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cistatic void tcic_timer(struct timer_list *unused)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci    pr_debug("tcic_timer()\n");
58762306a36Sopenharmony_ci    tcic_timer_pending = 0;
58862306a36Sopenharmony_ci    tcic_interrupt(0, NULL);
58962306a36Sopenharmony_ci} /* tcic_timer */
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci/*====================================================================*/
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_cistatic int tcic_get_status(struct pcmcia_socket *sock, u_int *value)
59462306a36Sopenharmony_ci{
59562306a36Sopenharmony_ci    u_short psock = container_of(sock, struct tcic_socket, socket)->psock;
59662306a36Sopenharmony_ci    u_char reg;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci    tcic_setl(TCIC_ADDR, (psock << TCIC_ADDR_SS_SHFT)
59962306a36Sopenharmony_ci	      | TCIC_ADDR_INDREG | TCIC_SCF1(psock));
60062306a36Sopenharmony_ci    reg = tcic_getb(TCIC_SSTAT);
60162306a36Sopenharmony_ci    *value  = (reg & TCIC_SSTAT_CD) ? SS_DETECT : 0;
60262306a36Sopenharmony_ci    *value |= (reg & TCIC_SSTAT_WP) ? SS_WRPROT : 0;
60362306a36Sopenharmony_ci    if (tcic_getw(TCIC_DATA) & TCIC_SCF1_IOSTS) {
60462306a36Sopenharmony_ci	*value |= (reg & TCIC_SSTAT_LBAT1) ? SS_STSCHG : 0;
60562306a36Sopenharmony_ci    } else {
60662306a36Sopenharmony_ci	*value |= (reg & TCIC_SSTAT_RDY) ? SS_READY : 0;
60762306a36Sopenharmony_ci	*value |= (reg & TCIC_SSTAT_LBAT1) ? SS_BATDEAD : 0;
60862306a36Sopenharmony_ci	*value |= (reg & TCIC_SSTAT_LBAT2) ? SS_BATWARN : 0;
60962306a36Sopenharmony_ci    }
61062306a36Sopenharmony_ci    reg = tcic_getb(TCIC_PWR);
61162306a36Sopenharmony_ci    if (reg & (TCIC_PWR_VCC(psock)|TCIC_PWR_VPP(psock)))
61262306a36Sopenharmony_ci	*value |= SS_POWERON;
61362306a36Sopenharmony_ci    dev_dbg(&sock->dev, "GetStatus(%d) = %#2.2x\n", psock, *value);
61462306a36Sopenharmony_ci    return 0;
61562306a36Sopenharmony_ci} /* tcic_get_status */
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci/*====================================================================*/
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_cistatic int tcic_set_socket(struct pcmcia_socket *sock, socket_state_t *state)
62062306a36Sopenharmony_ci{
62162306a36Sopenharmony_ci    u_short psock = container_of(sock, struct tcic_socket, socket)->psock;
62262306a36Sopenharmony_ci    u_char reg;
62362306a36Sopenharmony_ci    u_short scf1, scf2;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci    dev_dbg(&sock->dev, "SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
62662306a36Sopenharmony_ci	  "io_irq %d, csc_mask %#2.2x)\n", psock, state->flags,
62762306a36Sopenharmony_ci	  state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
62862306a36Sopenharmony_ci    tcic_setw(TCIC_ADDR+2, (psock << TCIC_SS_SHFT) | TCIC_ADR2_INDREG);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci    reg = tcic_getb(TCIC_PWR);
63162306a36Sopenharmony_ci    reg &= ~(TCIC_PWR_VCC(psock) | TCIC_PWR_VPP(psock));
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci    if (state->Vcc == 50) {
63462306a36Sopenharmony_ci	switch (state->Vpp) {
63562306a36Sopenharmony_ci	case 0:   reg |= TCIC_PWR_VCC(psock) | TCIC_PWR_VPP(psock); break;
63662306a36Sopenharmony_ci	case 50:  reg |= TCIC_PWR_VCC(psock); break;
63762306a36Sopenharmony_ci	case 120: reg |= TCIC_PWR_VPP(psock); break;
63862306a36Sopenharmony_ci	default:  return -EINVAL;
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci    } else if (state->Vcc != 0)
64162306a36Sopenharmony_ci	return -EINVAL;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci    if (reg != tcic_getb(TCIC_PWR))
64462306a36Sopenharmony_ci	tcic_setb(TCIC_PWR, reg);
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci    reg = TCIC_ILOCK_HOLD_CCLK | TCIC_ILOCK_CWAIT;
64762306a36Sopenharmony_ci    if (state->flags & SS_OUTPUT_ENA) {
64862306a36Sopenharmony_ci	tcic_setb(TCIC_SCTRL, TCIC_SCTRL_ENA);
64962306a36Sopenharmony_ci	reg |= TCIC_ILOCK_CRESENA;
65062306a36Sopenharmony_ci    } else
65162306a36Sopenharmony_ci	tcic_setb(TCIC_SCTRL, 0);
65262306a36Sopenharmony_ci    if (state->flags & SS_RESET)
65362306a36Sopenharmony_ci	reg |= TCIC_ILOCK_CRESET;
65462306a36Sopenharmony_ci    tcic_aux_setb(TCIC_AUX_ILOCK, reg);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci    tcic_setw(TCIC_ADDR, TCIC_SCF1(psock));
65762306a36Sopenharmony_ci    scf1 = TCIC_SCF1_FINPACK;
65862306a36Sopenharmony_ci    scf1 |= TCIC_IRQ(state->io_irq);
65962306a36Sopenharmony_ci    if (state->flags & SS_IOCARD) {
66062306a36Sopenharmony_ci	scf1 |= TCIC_SCF1_IOSTS;
66162306a36Sopenharmony_ci	if (state->flags & SS_SPKR_ENA)
66262306a36Sopenharmony_ci	    scf1 |= TCIC_SCF1_SPKR;
66362306a36Sopenharmony_ci	if (state->flags & SS_DMA_MODE)
66462306a36Sopenharmony_ci	    scf1 |= TCIC_SCF1_DREQ2 << TCIC_SCF1_DMA_SHIFT;
66562306a36Sopenharmony_ci    }
66662306a36Sopenharmony_ci    tcic_setw(TCIC_DATA, scf1);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci    /* Some general setup stuff, and configure status interrupt */
66962306a36Sopenharmony_ci    reg = TCIC_WAIT_ASYNC | TCIC_WAIT_SENSE | to_cycles(250);
67062306a36Sopenharmony_ci    tcic_aux_setb(TCIC_AUX_WCTL, reg);
67162306a36Sopenharmony_ci    tcic_aux_setw(TCIC_AUX_SYSCFG, TCIC_SYSCFG_AUTOBUSY|0x0a00|
67262306a36Sopenharmony_ci		  TCIC_IRQ(cs_irq));
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci    /* Card status change interrupt mask */
67562306a36Sopenharmony_ci    tcic_setw(TCIC_ADDR, TCIC_SCF2(psock));
67662306a36Sopenharmony_ci    scf2 = TCIC_SCF2_MALL;
67762306a36Sopenharmony_ci    if (state->csc_mask & SS_DETECT) scf2 &= ~TCIC_SCF2_MCD;
67862306a36Sopenharmony_ci    if (state->flags & SS_IOCARD) {
67962306a36Sopenharmony_ci	if (state->csc_mask & SS_STSCHG) reg &= ~TCIC_SCF2_MLBAT1;
68062306a36Sopenharmony_ci    } else {
68162306a36Sopenharmony_ci	if (state->csc_mask & SS_BATDEAD) reg &= ~TCIC_SCF2_MLBAT1;
68262306a36Sopenharmony_ci	if (state->csc_mask & SS_BATWARN) reg &= ~TCIC_SCF2_MLBAT2;
68362306a36Sopenharmony_ci	if (state->csc_mask & SS_READY) reg &= ~TCIC_SCF2_MRDY;
68462306a36Sopenharmony_ci    }
68562306a36Sopenharmony_ci    tcic_setw(TCIC_DATA, scf2);
68662306a36Sopenharmony_ci    /* For the ISA bus, the irq should be active-high totem-pole */
68762306a36Sopenharmony_ci    tcic_setb(TCIC_IENA, TCIC_IENA_CDCHG | TCIC_IENA_CFG_HIGH);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci    return 0;
69062306a36Sopenharmony_ci} /* tcic_set_socket */
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci/*====================================================================*/
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_cistatic int tcic_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io)
69562306a36Sopenharmony_ci{
69662306a36Sopenharmony_ci    u_short psock = container_of(sock, struct tcic_socket, socket)->psock;
69762306a36Sopenharmony_ci    u_int addr;
69862306a36Sopenharmony_ci    u_short base, len, ioctl;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci    dev_dbg(&sock->dev, "SetIOMap(%d, %d, %#2.2x, %d ns, "
70162306a36Sopenharmony_ci	  "%#llx-%#llx)\n", psock, io->map, io->flags, io->speed,
70262306a36Sopenharmony_ci	  (unsigned long long)io->start, (unsigned long long)io->stop);
70362306a36Sopenharmony_ci    if ((io->map > 1) || (io->start > 0xffff) || (io->stop > 0xffff) ||
70462306a36Sopenharmony_ci	(io->stop < io->start)) return -EINVAL;
70562306a36Sopenharmony_ci    tcic_setw(TCIC_ADDR+2, TCIC_ADR2_INDREG | (psock << TCIC_SS_SHFT));
70662306a36Sopenharmony_ci    addr = TCIC_IWIN(psock, io->map);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci    base = io->start; len = io->stop - io->start;
70962306a36Sopenharmony_ci    /* Check to see that len+1 is power of two, etc */
71062306a36Sopenharmony_ci    if ((len & (len+1)) || (base & len)) return -EINVAL;
71162306a36Sopenharmony_ci    base |= (len+1)>>1;
71262306a36Sopenharmony_ci    tcic_setw(TCIC_ADDR, addr + TCIC_IBASE_X);
71362306a36Sopenharmony_ci    tcic_setw(TCIC_DATA, base);
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci    ioctl  = (psock << TCIC_ICTL_SS_SHFT);
71662306a36Sopenharmony_ci    ioctl |= (len == 0) ? TCIC_ICTL_TINY : 0;
71762306a36Sopenharmony_ci    ioctl |= (io->flags & MAP_ACTIVE) ? TCIC_ICTL_ENA : 0;
71862306a36Sopenharmony_ci    ioctl |= to_cycles(io->speed) & TCIC_ICTL_WSCNT_MASK;
71962306a36Sopenharmony_ci    if (!(io->flags & MAP_AUTOSZ)) {
72062306a36Sopenharmony_ci	ioctl |= TCIC_ICTL_QUIET;
72162306a36Sopenharmony_ci	ioctl |= (io->flags & MAP_16BIT) ? TCIC_ICTL_BW_16 : TCIC_ICTL_BW_8;
72262306a36Sopenharmony_ci    }
72362306a36Sopenharmony_ci    tcic_setw(TCIC_ADDR, addr + TCIC_ICTL_X);
72462306a36Sopenharmony_ci    tcic_setw(TCIC_DATA, ioctl);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci    return 0;
72762306a36Sopenharmony_ci} /* tcic_set_io_map */
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci/*====================================================================*/
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_cistatic int tcic_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *mem)
73262306a36Sopenharmony_ci{
73362306a36Sopenharmony_ci    u_short psock = container_of(sock, struct tcic_socket, socket)->psock;
73462306a36Sopenharmony_ci    u_short addr, ctl;
73562306a36Sopenharmony_ci    u_long base, len, mmap;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci    dev_dbg(&sock->dev, "SetMemMap(%d, %d, %#2.2x, %d ns, "
73862306a36Sopenharmony_ci	  "%#llx-%#llx, %#x)\n", psock, mem->map, mem->flags,
73962306a36Sopenharmony_ci	  mem->speed, (unsigned long long)mem->res->start,
74062306a36Sopenharmony_ci	  (unsigned long long)mem->res->end, mem->card_start);
74162306a36Sopenharmony_ci    if ((mem->map > 3) || (mem->card_start > 0x3ffffff) ||
74262306a36Sopenharmony_ci	(mem->res->start > 0xffffff) || (mem->res->end > 0xffffff) ||
74362306a36Sopenharmony_ci	(mem->res->start > mem->res->end) || (mem->speed > 1000))
74462306a36Sopenharmony_ci	return -EINVAL;
74562306a36Sopenharmony_ci    tcic_setw(TCIC_ADDR+2, TCIC_ADR2_INDREG | (psock << TCIC_SS_SHFT));
74662306a36Sopenharmony_ci    addr = TCIC_MWIN(psock, mem->map);
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci    base = mem->res->start; len = mem->res->end - mem->res->start;
74962306a36Sopenharmony_ci    if ((len & (len+1)) || (base & len)) return -EINVAL;
75062306a36Sopenharmony_ci    if (len == 0x0fff)
75162306a36Sopenharmony_ci	base = (base >> TCIC_MBASE_HA_SHFT) | TCIC_MBASE_4K_BIT;
75262306a36Sopenharmony_ci    else
75362306a36Sopenharmony_ci	base = (base | (len+1)>>1) >> TCIC_MBASE_HA_SHFT;
75462306a36Sopenharmony_ci    tcic_setw(TCIC_ADDR, addr + TCIC_MBASE_X);
75562306a36Sopenharmony_ci    tcic_setw(TCIC_DATA, base);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci    mmap = mem->card_start - mem->res->start;
75862306a36Sopenharmony_ci    mmap = (mmap >> TCIC_MMAP_CA_SHFT) & TCIC_MMAP_CA_MASK;
75962306a36Sopenharmony_ci    if (mem->flags & MAP_ATTRIB) mmap |= TCIC_MMAP_REG;
76062306a36Sopenharmony_ci    tcic_setw(TCIC_ADDR, addr + TCIC_MMAP_X);
76162306a36Sopenharmony_ci    tcic_setw(TCIC_DATA, mmap);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci    ctl  = TCIC_MCTL_QUIET | (psock << TCIC_MCTL_SS_SHFT);
76462306a36Sopenharmony_ci    ctl |= to_cycles(mem->speed) & TCIC_MCTL_WSCNT_MASK;
76562306a36Sopenharmony_ci    ctl |= (mem->flags & MAP_16BIT) ? 0 : TCIC_MCTL_B8;
76662306a36Sopenharmony_ci    ctl |= (mem->flags & MAP_WRPROT) ? TCIC_MCTL_WP : 0;
76762306a36Sopenharmony_ci    ctl |= (mem->flags & MAP_ACTIVE) ? TCIC_MCTL_ENA : 0;
76862306a36Sopenharmony_ci    tcic_setw(TCIC_ADDR, addr + TCIC_MCTL_X);
76962306a36Sopenharmony_ci    tcic_setw(TCIC_DATA, ctl);
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci    return 0;
77262306a36Sopenharmony_ci} /* tcic_set_mem_map */
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci/*====================================================================*/
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_cistatic int tcic_init(struct pcmcia_socket *s)
77762306a36Sopenharmony_ci{
77862306a36Sopenharmony_ci	int i;
77962306a36Sopenharmony_ci	struct resource res = { .start = 0, .end = 0x1000 };
78062306a36Sopenharmony_ci	pccard_io_map io = { 0, 0, 0, 0, 1 };
78162306a36Sopenharmony_ci	pccard_mem_map mem = { .res = &res, };
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	for (i = 0; i < 2; i++) {
78462306a36Sopenharmony_ci		io.map = i;
78562306a36Sopenharmony_ci		tcic_set_io_map(s, &io);
78662306a36Sopenharmony_ci	}
78762306a36Sopenharmony_ci	for (i = 0; i < 5; i++) {
78862306a36Sopenharmony_ci		mem.map = i;
78962306a36Sopenharmony_ci		tcic_set_mem_map(s, &mem);
79062306a36Sopenharmony_ci	}
79162306a36Sopenharmony_ci	return 0;
79262306a36Sopenharmony_ci}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_cistatic struct pccard_operations tcic_operations = {
79562306a36Sopenharmony_ci	.init		   = tcic_init,
79662306a36Sopenharmony_ci	.get_status	   = tcic_get_status,
79762306a36Sopenharmony_ci	.set_socket	   = tcic_set_socket,
79862306a36Sopenharmony_ci	.set_io_map	   = tcic_set_io_map,
79962306a36Sopenharmony_ci	.set_mem_map	   = tcic_set_mem_map,
80062306a36Sopenharmony_ci};
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci/*====================================================================*/
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_cimodule_init(init_tcic);
80562306a36Sopenharmony_cimodule_exit(exit_tcic);
806