xref: /kernel/linux/linux-6.6/drivers/pcmcia/i82365.c (revision 62306a36)
162306a36Sopenharmony_ci/*======================================================================
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci    Device driver for Intel 82365 and compatible PC Card controllers.
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci    i82365.c 1.265 1999/11/10 18:36:21
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/kernel.h>
4162306a36Sopenharmony_ci#include <linux/errno.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/interrupt.h>
4762306a36Sopenharmony_ci#include <linux/platform_device.h>
4862306a36Sopenharmony_ci#include <linux/bitops.h>
4962306a36Sopenharmony_ci#include <asm/irq.h>
5062306a36Sopenharmony_ci#include <asm/io.h>
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#include <pcmcia/ss.h>
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#include <linux/isapnp.h>
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/* ISA-bus controllers */
5762306a36Sopenharmony_ci#include "i82365.h"
5862306a36Sopenharmony_ci#include "cirrus.h"
5962306a36Sopenharmony_ci#include "vg468.h"
6062306a36Sopenharmony_ci#include "ricoh.h"
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic irqreturn_t i365_count_irq(int, void *);
6462306a36Sopenharmony_cistatic inline int _check_irq(int irq, int flags)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci    if (request_irq(irq, i365_count_irq, flags, "x", i365_count_irq) != 0)
6762306a36Sopenharmony_ci	return -1;
6862306a36Sopenharmony_ci    free_irq(irq, i365_count_irq);
6962306a36Sopenharmony_ci    return 0;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/*====================================================================*/
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/* Parameters that can be set with 'insmod' */
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/* Default base address for i82365sl and other ISA chips */
7762306a36Sopenharmony_cistatic unsigned long i365_base = 0x3e0;
7862306a36Sopenharmony_ci/* Should we probe at 0x3e2 for an extra ISA controller? */
7962306a36Sopenharmony_cistatic int extra_sockets = 0;
8062306a36Sopenharmony_ci/* Specify a socket number to ignore */
8162306a36Sopenharmony_cistatic int ignore = -1;
8262306a36Sopenharmony_ci/* Bit map or list of interrupts to choose from */
8362306a36Sopenharmony_cistatic u_int irq_mask = 0xffff;
8462306a36Sopenharmony_cistatic int irq_list[16];
8562306a36Sopenharmony_cistatic unsigned int irq_list_count;
8662306a36Sopenharmony_ci/* The card status change interrupt -- 0 means autoselect */
8762306a36Sopenharmony_cistatic int cs_irq = 0;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/* Probe for safe interrupts? */
9062306a36Sopenharmony_cistatic int do_scan = 1;
9162306a36Sopenharmony_ci/* Poll status interval -- 0 means default to interrupt */
9262306a36Sopenharmony_cistatic int poll_interval = 0;
9362306a36Sopenharmony_ci/* External clock time, in nanoseconds.  120 ns = 8.33 MHz */
9462306a36Sopenharmony_cistatic int cycle_time = 120;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci/* Cirrus options */
9762306a36Sopenharmony_cistatic int has_dma = -1;
9862306a36Sopenharmony_cistatic int has_led = -1;
9962306a36Sopenharmony_cistatic int has_ring = -1;
10062306a36Sopenharmony_cistatic int dynamic_mode = 0;
10162306a36Sopenharmony_cistatic int freq_bypass = -1;
10262306a36Sopenharmony_cistatic int setup_time = -1;
10362306a36Sopenharmony_cistatic int cmd_time = -1;
10462306a36Sopenharmony_cistatic int recov_time = -1;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/* Vadem options */
10762306a36Sopenharmony_cistatic int async_clock = -1;
10862306a36Sopenharmony_cistatic int cable_mode = -1;
10962306a36Sopenharmony_cistatic int wakeup = 0;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cimodule_param_hw(i365_base, ulong, ioport, 0444);
11262306a36Sopenharmony_cimodule_param(ignore, int, 0444);
11362306a36Sopenharmony_cimodule_param(extra_sockets, int, 0444);
11462306a36Sopenharmony_cimodule_param_hw(irq_mask, int, other, 0444);
11562306a36Sopenharmony_cimodule_param_hw_array(irq_list, int, irq, &irq_list_count, 0444);
11662306a36Sopenharmony_cimodule_param_hw(cs_irq, int, irq, 0444);
11762306a36Sopenharmony_cimodule_param(async_clock, int, 0444);
11862306a36Sopenharmony_cimodule_param(cable_mode, int, 0444);
11962306a36Sopenharmony_cimodule_param(wakeup, int, 0444);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cimodule_param(do_scan, int, 0444);
12262306a36Sopenharmony_cimodule_param(poll_interval, int, 0444);
12362306a36Sopenharmony_cimodule_param(cycle_time, int, 0444);
12462306a36Sopenharmony_cimodule_param(has_dma, int, 0444);
12562306a36Sopenharmony_cimodule_param(has_led, int, 0444);
12662306a36Sopenharmony_cimodule_param(has_ring, int, 0444);
12762306a36Sopenharmony_cimodule_param(dynamic_mode, int, 0444);
12862306a36Sopenharmony_cimodule_param(freq_bypass, int, 0444);
12962306a36Sopenharmony_cimodule_param(setup_time, int, 0444);
13062306a36Sopenharmony_cimodule_param(cmd_time, int, 0444);
13162306a36Sopenharmony_cimodule_param(recov_time, int, 0444);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci/*====================================================================*/
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistruct cirrus_state {
13662306a36Sopenharmony_ci    u_char		misc1, misc2;
13762306a36Sopenharmony_ci    u_char		timer[6];
13862306a36Sopenharmony_ci};
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistruct vg46x_state {
14162306a36Sopenharmony_ci    u_char		ctl, ema;
14262306a36Sopenharmony_ci};
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistruct i82365_socket {
14562306a36Sopenharmony_ci    u_short		type, flags;
14662306a36Sopenharmony_ci    struct pcmcia_socket	socket;
14762306a36Sopenharmony_ci    unsigned int	number;
14862306a36Sopenharmony_ci    unsigned int	ioaddr;
14962306a36Sopenharmony_ci    u_short		psock;
15062306a36Sopenharmony_ci    u_char		cs_irq, intr;
15162306a36Sopenharmony_ci    union {
15262306a36Sopenharmony_ci	struct cirrus_state		cirrus;
15362306a36Sopenharmony_ci	struct vg46x_state		vg46x;
15462306a36Sopenharmony_ci    } state;
15562306a36Sopenharmony_ci};
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci/* Where we keep track of our sockets... */
15862306a36Sopenharmony_cistatic int sockets = 0;
15962306a36Sopenharmony_cistatic struct i82365_socket socket[8] = {
16062306a36Sopenharmony_ci    { 0, }, /* ... */
16162306a36Sopenharmony_ci};
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci/* Default ISA interrupt mask */
16462306a36Sopenharmony_ci#define I365_MASK	0xdeb8	/* irq 15,14,12,11,10,9,7,5,4,3 */
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic int grab_irq;
16762306a36Sopenharmony_cistatic DEFINE_SPINLOCK(isa_lock);
16862306a36Sopenharmony_ci#define ISA_LOCK(n, f) spin_lock_irqsave(&isa_lock, f)
16962306a36Sopenharmony_ci#define ISA_UNLOCK(n, f) spin_unlock_irqrestore(&isa_lock, f)
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic struct timer_list poll_timer;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci/*====================================================================*/
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci/* These definitions must match the pcic table! */
17662306a36Sopenharmony_cienum pcic_id {
17762306a36Sopenharmony_ci    IS_I82365A, IS_I82365B, IS_I82365DF,
17862306a36Sopenharmony_ci    IS_IBM, IS_RF5Cx96, IS_VLSI, IS_VG468, IS_VG469,
17962306a36Sopenharmony_ci    IS_PD6710, IS_PD672X, IS_VT83C469,
18062306a36Sopenharmony_ci};
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci/* Flags for classifying groups of controllers */
18362306a36Sopenharmony_ci#define IS_VADEM	0x0001
18462306a36Sopenharmony_ci#define IS_CIRRUS	0x0002
18562306a36Sopenharmony_ci#define IS_VIA		0x0010
18662306a36Sopenharmony_ci#define IS_UNKNOWN	0x0400
18762306a36Sopenharmony_ci#define IS_VG_PWR	0x0800
18862306a36Sopenharmony_ci#define IS_DF_PWR	0x1000
18962306a36Sopenharmony_ci#define IS_REGISTERED	0x2000
19062306a36Sopenharmony_ci#define IS_ALIVE	0x8000
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistruct pcic {
19362306a36Sopenharmony_ci    char		*name;
19462306a36Sopenharmony_ci    u_short		flags;
19562306a36Sopenharmony_ci};
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic struct pcic pcic[] = {
19862306a36Sopenharmony_ci    { "Intel i82365sl A step", 0 },
19962306a36Sopenharmony_ci    { "Intel i82365sl B step", 0 },
20062306a36Sopenharmony_ci    { "Intel i82365sl DF", IS_DF_PWR },
20162306a36Sopenharmony_ci    { "IBM Clone", 0 },
20262306a36Sopenharmony_ci    { "Ricoh RF5C296/396", 0 },
20362306a36Sopenharmony_ci    { "VLSI 82C146", 0 },
20462306a36Sopenharmony_ci    { "Vadem VG-468", IS_VADEM },
20562306a36Sopenharmony_ci    { "Vadem VG-469", IS_VADEM|IS_VG_PWR },
20662306a36Sopenharmony_ci    { "Cirrus PD6710", IS_CIRRUS },
20762306a36Sopenharmony_ci    { "Cirrus PD672x", IS_CIRRUS },
20862306a36Sopenharmony_ci    { "VIA VT83C469", IS_CIRRUS|IS_VIA },
20962306a36Sopenharmony_ci};
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci#define PCIC_COUNT	ARRAY_SIZE(pcic)
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci/*====================================================================*/
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic DEFINE_SPINLOCK(bus_lock);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic u_char i365_get(u_short sock, u_short reg)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci    unsigned long flags;
22062306a36Sopenharmony_ci    spin_lock_irqsave(&bus_lock,flags);
22162306a36Sopenharmony_ci    {
22262306a36Sopenharmony_ci	unsigned int port = socket[sock].ioaddr;
22362306a36Sopenharmony_ci	u_char val;
22462306a36Sopenharmony_ci	reg = I365_REG(socket[sock].psock, reg);
22562306a36Sopenharmony_ci	outb(reg, port); val = inb(port+1);
22662306a36Sopenharmony_ci	spin_unlock_irqrestore(&bus_lock,flags);
22762306a36Sopenharmony_ci	return val;
22862306a36Sopenharmony_ci    }
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic void i365_set(u_short sock, u_short reg, u_char data)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci    unsigned long flags;
23462306a36Sopenharmony_ci    spin_lock_irqsave(&bus_lock,flags);
23562306a36Sopenharmony_ci    {
23662306a36Sopenharmony_ci	unsigned int port = socket[sock].ioaddr;
23762306a36Sopenharmony_ci	u_char val = I365_REG(socket[sock].psock, reg);
23862306a36Sopenharmony_ci	outb(val, port); outb(data, port+1);
23962306a36Sopenharmony_ci	spin_unlock_irqrestore(&bus_lock,flags);
24062306a36Sopenharmony_ci    }
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic void i365_bset(u_short sock, u_short reg, u_char mask)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci    u_char d = i365_get(sock, reg);
24662306a36Sopenharmony_ci    d |= mask;
24762306a36Sopenharmony_ci    i365_set(sock, reg, d);
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic void i365_bclr(u_short sock, u_short reg, u_char mask)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci    u_char d = i365_get(sock, reg);
25362306a36Sopenharmony_ci    d &= ~mask;
25462306a36Sopenharmony_ci    i365_set(sock, reg, d);
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic void i365_bflip(u_short sock, u_short reg, u_char mask, int b)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci    u_char d = i365_get(sock, reg);
26062306a36Sopenharmony_ci    if (b)
26162306a36Sopenharmony_ci	d |= mask;
26262306a36Sopenharmony_ci    else
26362306a36Sopenharmony_ci	d &= ~mask;
26462306a36Sopenharmony_ci    i365_set(sock, reg, d);
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic u_short i365_get_pair(u_short sock, u_short reg)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci    u_short a, b;
27062306a36Sopenharmony_ci    a = i365_get(sock, reg);
27162306a36Sopenharmony_ci    b = i365_get(sock, reg+1);
27262306a36Sopenharmony_ci    return (a + (b<<8));
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic void i365_set_pair(u_short sock, u_short reg, u_short data)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci    i365_set(sock, reg, data & 0xff);
27862306a36Sopenharmony_ci    i365_set(sock, reg+1, data >> 8);
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci/*======================================================================
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci    Code to save and restore global state information for Cirrus
28462306a36Sopenharmony_ci    PD67xx controllers, and to set and report global configuration
28562306a36Sopenharmony_ci    options.
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci    The VIA controllers also use these routines, as they are mostly
28862306a36Sopenharmony_ci    Cirrus lookalikes, without the timing registers.
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci======================================================================*/
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci#define flip(v,b,f) (v = ((f)<0) ? v : ((f) ? ((v)|(b)) : ((v)&(~b))))
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic void cirrus_get_state(u_short s)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci    int i;
29762306a36Sopenharmony_ci    struct cirrus_state *p = &socket[s].state.cirrus;
29862306a36Sopenharmony_ci    p->misc1 = i365_get(s, PD67_MISC_CTL_1);
29962306a36Sopenharmony_ci    p->misc1 &= (PD67_MC1_MEDIA_ENA | PD67_MC1_INPACK_ENA);
30062306a36Sopenharmony_ci    p->misc2 = i365_get(s, PD67_MISC_CTL_2);
30162306a36Sopenharmony_ci    for (i = 0; i < 6; i++)
30262306a36Sopenharmony_ci	p->timer[i] = i365_get(s, PD67_TIME_SETUP(0)+i);
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic void cirrus_set_state(u_short s)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci    int i;
30862306a36Sopenharmony_ci    u_char misc;
30962306a36Sopenharmony_ci    struct cirrus_state *p = &socket[s].state.cirrus;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci    misc = i365_get(s, PD67_MISC_CTL_2);
31262306a36Sopenharmony_ci    i365_set(s, PD67_MISC_CTL_2, p->misc2);
31362306a36Sopenharmony_ci    if (misc & PD67_MC2_SUSPEND) mdelay(50);
31462306a36Sopenharmony_ci    misc = i365_get(s, PD67_MISC_CTL_1);
31562306a36Sopenharmony_ci    misc &= ~(PD67_MC1_MEDIA_ENA | PD67_MC1_INPACK_ENA);
31662306a36Sopenharmony_ci    i365_set(s, PD67_MISC_CTL_1, misc | p->misc1);
31762306a36Sopenharmony_ci    for (i = 0; i < 6; i++)
31862306a36Sopenharmony_ci	i365_set(s, PD67_TIME_SETUP(0)+i, p->timer[i]);
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic u_int __init cirrus_set_opts(u_short s, char *buf)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci    struct i82365_socket *t = &socket[s];
32462306a36Sopenharmony_ci    struct cirrus_state *p = &socket[s].state.cirrus;
32562306a36Sopenharmony_ci    u_int mask = 0xffff;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci    if (has_ring == -1) has_ring = 1;
32862306a36Sopenharmony_ci    flip(p->misc2, PD67_MC2_IRQ15_RI, has_ring);
32962306a36Sopenharmony_ci    flip(p->misc2, PD67_MC2_DYNAMIC_MODE, dynamic_mode);
33062306a36Sopenharmony_ci    flip(p->misc2, PD67_MC2_FREQ_BYPASS, freq_bypass);
33162306a36Sopenharmony_ci    if (p->misc2 & PD67_MC2_IRQ15_RI)
33262306a36Sopenharmony_ci	strcat(buf, " [ring]");
33362306a36Sopenharmony_ci    if (p->misc2 & PD67_MC2_DYNAMIC_MODE)
33462306a36Sopenharmony_ci	strcat(buf, " [dyn mode]");
33562306a36Sopenharmony_ci    if (p->misc2 & PD67_MC2_FREQ_BYPASS)
33662306a36Sopenharmony_ci	strcat(buf, " [freq bypass]");
33762306a36Sopenharmony_ci    if (p->misc1 & PD67_MC1_INPACK_ENA)
33862306a36Sopenharmony_ci	strcat(buf, " [inpack]");
33962306a36Sopenharmony_ci    if (p->misc2 & PD67_MC2_IRQ15_RI)
34062306a36Sopenharmony_ci	mask &= ~0x8000;
34162306a36Sopenharmony_ci    if (has_led > 0) {
34262306a36Sopenharmony_ci	strcat(buf, " [led]");
34362306a36Sopenharmony_ci	mask &= ~0x1000;
34462306a36Sopenharmony_ci    }
34562306a36Sopenharmony_ci    if (has_dma > 0) {
34662306a36Sopenharmony_ci	strcat(buf, " [dma]");
34762306a36Sopenharmony_ci	mask &= ~0x0600;
34862306a36Sopenharmony_ci    }
34962306a36Sopenharmony_ci    if (!(t->flags & IS_VIA)) {
35062306a36Sopenharmony_ci	if (setup_time >= 0)
35162306a36Sopenharmony_ci	    p->timer[0] = p->timer[3] = setup_time;
35262306a36Sopenharmony_ci	if (cmd_time > 0) {
35362306a36Sopenharmony_ci	    p->timer[1] = cmd_time;
35462306a36Sopenharmony_ci	    p->timer[4] = cmd_time*2+4;
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci	if (p->timer[1] == 0) {
35762306a36Sopenharmony_ci	    p->timer[1] = 6; p->timer[4] = 16;
35862306a36Sopenharmony_ci	    if (p->timer[0] == 0)
35962306a36Sopenharmony_ci		p->timer[0] = p->timer[3] = 1;
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci	if (recov_time >= 0)
36262306a36Sopenharmony_ci	    p->timer[2] = p->timer[5] = recov_time;
36362306a36Sopenharmony_ci	buf += strlen(buf);
36462306a36Sopenharmony_ci	sprintf(buf, " [%d/%d/%d] [%d/%d/%d]", p->timer[0], p->timer[1],
36562306a36Sopenharmony_ci		p->timer[2], p->timer[3], p->timer[4], p->timer[5]);
36662306a36Sopenharmony_ci    }
36762306a36Sopenharmony_ci    return mask;
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci/*======================================================================
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci    Code to save and restore global state information for Vadem VG468
37362306a36Sopenharmony_ci    and VG469 controllers, and to set and report global configuration
37462306a36Sopenharmony_ci    options.
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci======================================================================*/
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_cistatic void vg46x_get_state(u_short s)
37962306a36Sopenharmony_ci{
38062306a36Sopenharmony_ci    struct vg46x_state *p = &socket[s].state.vg46x;
38162306a36Sopenharmony_ci    p->ctl = i365_get(s, VG468_CTL);
38262306a36Sopenharmony_ci    if (socket[s].type == IS_VG469)
38362306a36Sopenharmony_ci	p->ema = i365_get(s, VG469_EXT_MODE);
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic void vg46x_set_state(u_short s)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci    struct vg46x_state *p = &socket[s].state.vg46x;
38962306a36Sopenharmony_ci    i365_set(s, VG468_CTL, p->ctl);
39062306a36Sopenharmony_ci    if (socket[s].type == IS_VG469)
39162306a36Sopenharmony_ci	i365_set(s, VG469_EXT_MODE, p->ema);
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cistatic u_int __init vg46x_set_opts(u_short s, char *buf)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci    struct vg46x_state *p = &socket[s].state.vg46x;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci    flip(p->ctl, VG468_CTL_ASYNC, async_clock);
39962306a36Sopenharmony_ci    flip(p->ema, VG469_MODE_CABLE, cable_mode);
40062306a36Sopenharmony_ci    if (p->ctl & VG468_CTL_ASYNC)
40162306a36Sopenharmony_ci	strcat(buf, " [async]");
40262306a36Sopenharmony_ci    if (p->ctl & VG468_CTL_INPACK)
40362306a36Sopenharmony_ci	strcat(buf, " [inpack]");
40462306a36Sopenharmony_ci    if (socket[s].type == IS_VG469) {
40562306a36Sopenharmony_ci	u_char vsel = i365_get(s, VG469_VSELECT);
40662306a36Sopenharmony_ci	if (vsel & VG469_VSEL_EXT_STAT) {
40762306a36Sopenharmony_ci	    strcat(buf, " [ext mode]");
40862306a36Sopenharmony_ci	    if (vsel & VG469_VSEL_EXT_BUS)
40962306a36Sopenharmony_ci		strcat(buf, " [isa buf]");
41062306a36Sopenharmony_ci	}
41162306a36Sopenharmony_ci	if (p->ema & VG469_MODE_CABLE)
41262306a36Sopenharmony_ci	    strcat(buf, " [cable]");
41362306a36Sopenharmony_ci	if (p->ema & VG469_MODE_COMPAT)
41462306a36Sopenharmony_ci	    strcat(buf, " [c step]");
41562306a36Sopenharmony_ci    }
41662306a36Sopenharmony_ci    return 0xffff;
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci/*======================================================================
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci    Generic routines to get and set controller options
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci======================================================================*/
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_cistatic void get_bridge_state(u_short s)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci    struct i82365_socket *t = &socket[s];
42862306a36Sopenharmony_ci    if (t->flags & IS_CIRRUS)
42962306a36Sopenharmony_ci	cirrus_get_state(s);
43062306a36Sopenharmony_ci    else if (t->flags & IS_VADEM)
43162306a36Sopenharmony_ci	vg46x_get_state(s);
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistatic void set_bridge_state(u_short s)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci    struct i82365_socket *t = &socket[s];
43762306a36Sopenharmony_ci    if (t->flags & IS_CIRRUS)
43862306a36Sopenharmony_ci	cirrus_set_state(s);
43962306a36Sopenharmony_ci    else {
44062306a36Sopenharmony_ci	i365_set(s, I365_GBLCTL, 0x00);
44162306a36Sopenharmony_ci	i365_set(s, I365_GENCTL, 0x00);
44262306a36Sopenharmony_ci    }
44362306a36Sopenharmony_ci    i365_bflip(s, I365_INTCTL, I365_INTR_ENA, t->intr);
44462306a36Sopenharmony_ci    if (t->flags & IS_VADEM)
44562306a36Sopenharmony_ci	vg46x_set_state(s);
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic u_int __init set_bridge_opts(u_short s, u_short ns)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci    u_short i;
45162306a36Sopenharmony_ci    u_int m = 0xffff;
45262306a36Sopenharmony_ci    char buf[128];
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci    for (i = s; i < s+ns; i++) {
45562306a36Sopenharmony_ci	if (socket[i].flags & IS_ALIVE) {
45662306a36Sopenharmony_ci	    printk(KERN_INFO "    host opts [%d]: already alive!\n", i);
45762306a36Sopenharmony_ci	    continue;
45862306a36Sopenharmony_ci	}
45962306a36Sopenharmony_ci	buf[0] = '\0';
46062306a36Sopenharmony_ci	get_bridge_state(i);
46162306a36Sopenharmony_ci	if (socket[i].flags & IS_CIRRUS)
46262306a36Sopenharmony_ci	    m = cirrus_set_opts(i, buf);
46362306a36Sopenharmony_ci	else if (socket[i].flags & IS_VADEM)
46462306a36Sopenharmony_ci	    m = vg46x_set_opts(i, buf);
46562306a36Sopenharmony_ci	set_bridge_state(i);
46662306a36Sopenharmony_ci	printk(KERN_INFO "    host opts [%d]:%s\n", i,
46762306a36Sopenharmony_ci	       (*buf) ? buf : " none");
46862306a36Sopenharmony_ci    }
46962306a36Sopenharmony_ci    return m;
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci/*======================================================================
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci    Interrupt testing code, for ISA and PCI interrupts
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci======================================================================*/
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_cistatic volatile u_int irq_hits;
47962306a36Sopenharmony_cistatic u_short irq_sock;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_cistatic irqreturn_t i365_count_irq(int irq, void *dev)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci    i365_get(irq_sock, I365_CSC);
48462306a36Sopenharmony_ci    irq_hits++;
48562306a36Sopenharmony_ci    pr_debug("i82365: -> hit on irq %d\n", irq);
48662306a36Sopenharmony_ci    return IRQ_HANDLED;
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic u_int __init test_irq(u_short sock, int irq)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci    pr_debug("i82365:  testing ISA irq %d\n", irq);
49262306a36Sopenharmony_ci    if (request_irq(irq, i365_count_irq, IRQF_PROBE_SHARED, "scan",
49362306a36Sopenharmony_ci			i365_count_irq) != 0)
49462306a36Sopenharmony_ci	return 1;
49562306a36Sopenharmony_ci    irq_hits = 0; irq_sock = sock;
49662306a36Sopenharmony_ci    msleep(10);
49762306a36Sopenharmony_ci    if (irq_hits) {
49862306a36Sopenharmony_ci	free_irq(irq, i365_count_irq);
49962306a36Sopenharmony_ci	pr_debug("i82365:    spurious hit!\n");
50062306a36Sopenharmony_ci	return 1;
50162306a36Sopenharmony_ci    }
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci    /* Generate one interrupt */
50462306a36Sopenharmony_ci    i365_set(sock, I365_CSCINT, I365_CSC_DETECT | (irq << 4));
50562306a36Sopenharmony_ci    i365_bset(sock, I365_GENCTL, I365_CTL_SW_IRQ);
50662306a36Sopenharmony_ci    udelay(1000);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci    free_irq(irq, i365_count_irq);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci    /* mask all interrupts */
51162306a36Sopenharmony_ci    i365_set(sock, I365_CSCINT, 0);
51262306a36Sopenharmony_ci    pr_debug("i82365:    hits = %d\n", irq_hits);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci    return (irq_hits != 1);
51562306a36Sopenharmony_ci}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_cistatic u_int __init isa_scan(u_short sock, u_int mask0)
51862306a36Sopenharmony_ci{
51962306a36Sopenharmony_ci    u_int mask1 = 0;
52062306a36Sopenharmony_ci    int i;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci#ifdef __alpha__
52362306a36Sopenharmony_ci#define PIC 0x4d0
52462306a36Sopenharmony_ci    /* Don't probe level-triggered interrupts -- reserved for PCI */
52562306a36Sopenharmony_ci    mask0 &= ~(inb(PIC) | (inb(PIC+1) << 8));
52662306a36Sopenharmony_ci#endif
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci    if (do_scan) {
52962306a36Sopenharmony_ci	set_bridge_state(sock);
53062306a36Sopenharmony_ci	i365_set(sock, I365_CSCINT, 0);
53162306a36Sopenharmony_ci	for (i = 0; i < 16; i++)
53262306a36Sopenharmony_ci	    if ((mask0 & (1 << i)) && (test_irq(sock, i) == 0))
53362306a36Sopenharmony_ci		mask1 |= (1 << i);
53462306a36Sopenharmony_ci	for (i = 0; i < 16; i++)
53562306a36Sopenharmony_ci	    if ((mask1 & (1 << i)) && (test_irq(sock, i) != 0))
53662306a36Sopenharmony_ci		mask1 ^= (1 << i);
53762306a36Sopenharmony_ci    }
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci    printk(KERN_INFO "    ISA irqs (");
54062306a36Sopenharmony_ci    if (mask1) {
54162306a36Sopenharmony_ci	printk("scanned");
54262306a36Sopenharmony_ci    } else {
54362306a36Sopenharmony_ci	/* Fallback: just find interrupts that aren't in use */
54462306a36Sopenharmony_ci	for (i = 0; i < 16; i++)
54562306a36Sopenharmony_ci	    if ((mask0 & (1 << i)) && (_check_irq(i, IRQF_PROBE_SHARED) == 0))
54662306a36Sopenharmony_ci		mask1 |= (1 << i);
54762306a36Sopenharmony_ci	printk("default");
54862306a36Sopenharmony_ci	/* If scan failed, default to polled status */
54962306a36Sopenharmony_ci	if (!cs_irq && (poll_interval == 0)) poll_interval = HZ;
55062306a36Sopenharmony_ci    }
55162306a36Sopenharmony_ci    printk(") = ");
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci    for (i = 0; i < 16; i++)
55462306a36Sopenharmony_ci	if (mask1 & (1<<i))
55562306a36Sopenharmony_ci	    printk("%s%d", ((mask1 & ((1<<i)-1)) ? "," : ""), i);
55662306a36Sopenharmony_ci    if (mask1 == 0) printk("none!");
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci    return mask1;
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci/*====================================================================*/
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci/* Time conversion functions */
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_cistatic int to_cycles(int ns)
56662306a36Sopenharmony_ci{
56762306a36Sopenharmony_ci    return ns/cycle_time;
56862306a36Sopenharmony_ci}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci/*====================================================================*/
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistatic int __init identify(unsigned int port, u_short sock)
57362306a36Sopenharmony_ci{
57462306a36Sopenharmony_ci    u_char val;
57562306a36Sopenharmony_ci    int type = -1;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci    /* Use the next free entry in the socket table */
57862306a36Sopenharmony_ci    socket[sockets].ioaddr = port;
57962306a36Sopenharmony_ci    socket[sockets].psock = sock;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci    /* Wake up a sleepy Cirrus controller */
58262306a36Sopenharmony_ci    if (wakeup) {
58362306a36Sopenharmony_ci	i365_bclr(sockets, PD67_MISC_CTL_2, PD67_MC2_SUSPEND);
58462306a36Sopenharmony_ci	/* Pause at least 50 ms */
58562306a36Sopenharmony_ci	mdelay(50);
58662306a36Sopenharmony_ci    }
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci    if ((val = i365_get(sockets, I365_IDENT)) & 0x70)
58962306a36Sopenharmony_ci	return -1;
59062306a36Sopenharmony_ci    switch (val) {
59162306a36Sopenharmony_ci    case 0x82:
59262306a36Sopenharmony_ci	type = IS_I82365A; break;
59362306a36Sopenharmony_ci    case 0x83:
59462306a36Sopenharmony_ci	type = IS_I82365B; break;
59562306a36Sopenharmony_ci    case 0x84:
59662306a36Sopenharmony_ci	type = IS_I82365DF; break;
59762306a36Sopenharmony_ci    case 0x88: case 0x89: case 0x8a:
59862306a36Sopenharmony_ci	type = IS_IBM; break;
59962306a36Sopenharmony_ci    }
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci    /* Check for Vadem VG-468 chips */
60262306a36Sopenharmony_ci    outb(0x0e, port);
60362306a36Sopenharmony_ci    outb(0x37, port);
60462306a36Sopenharmony_ci    i365_bset(sockets, VG468_MISC, VG468_MISC_VADEMREV);
60562306a36Sopenharmony_ci    val = i365_get(sockets, I365_IDENT);
60662306a36Sopenharmony_ci    if (val & I365_IDENT_VADEM) {
60762306a36Sopenharmony_ci	i365_bclr(sockets, VG468_MISC, VG468_MISC_VADEMREV);
60862306a36Sopenharmony_ci	type = ((val & 7) >= 4) ? IS_VG469 : IS_VG468;
60962306a36Sopenharmony_ci    }
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci    /* Check for Ricoh chips */
61262306a36Sopenharmony_ci    val = i365_get(sockets, RF5C_CHIP_ID);
61362306a36Sopenharmony_ci    if ((val == RF5C_CHIP_RF5C296) || (val == RF5C_CHIP_RF5C396))
61462306a36Sopenharmony_ci	type = IS_RF5Cx96;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci    /* Check for Cirrus CL-PD67xx chips */
61762306a36Sopenharmony_ci    i365_set(sockets, PD67_CHIP_INFO, 0);
61862306a36Sopenharmony_ci    val = i365_get(sockets, PD67_CHIP_INFO);
61962306a36Sopenharmony_ci    if ((val & PD67_INFO_CHIP_ID) == PD67_INFO_CHIP_ID) {
62062306a36Sopenharmony_ci	val = i365_get(sockets, PD67_CHIP_INFO);
62162306a36Sopenharmony_ci	if ((val & PD67_INFO_CHIP_ID) == 0) {
62262306a36Sopenharmony_ci	    type = (val & PD67_INFO_SLOTS) ? IS_PD672X : IS_PD6710;
62362306a36Sopenharmony_ci	    i365_set(sockets, PD67_EXT_INDEX, 0xe5);
62462306a36Sopenharmony_ci	    if (i365_get(sockets, PD67_EXT_INDEX) != 0xe5)
62562306a36Sopenharmony_ci		type = IS_VT83C469;
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci    }
62862306a36Sopenharmony_ci    return type;
62962306a36Sopenharmony_ci} /* identify */
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci/*======================================================================
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci    See if a card is present, powered up, in IO mode, and already
63462306a36Sopenharmony_ci    bound to a (non PC Card) Linux driver.  We leave these alone.
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci    We make an exception for cards that seem to be serial devices.
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci======================================================================*/
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_cistatic int __init is_alive(u_short sock)
64162306a36Sopenharmony_ci{
64262306a36Sopenharmony_ci    u_char stat;
64362306a36Sopenharmony_ci    unsigned int start, stop;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci    stat = i365_get(sock, I365_STATUS);
64662306a36Sopenharmony_ci    start = i365_get_pair(sock, I365_IO(0)+I365_W_START);
64762306a36Sopenharmony_ci    stop = i365_get_pair(sock, I365_IO(0)+I365_W_STOP);
64862306a36Sopenharmony_ci    if ((stat & I365_CS_DETECT) && (stat & I365_CS_POWERON) &&
64962306a36Sopenharmony_ci	(i365_get(sock, I365_INTCTL) & I365_PC_IOCARD) &&
65062306a36Sopenharmony_ci	(i365_get(sock, I365_ADDRWIN) & I365_ENA_IO(0)) &&
65162306a36Sopenharmony_ci	((start & 0xfeef) != 0x02e8)) {
65262306a36Sopenharmony_ci	if (!request_region(start, stop-start+1, "i82365"))
65362306a36Sopenharmony_ci	    return 1;
65462306a36Sopenharmony_ci	release_region(start, stop-start+1);
65562306a36Sopenharmony_ci    }
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci    return 0;
65862306a36Sopenharmony_ci}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci/*====================================================================*/
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_cistatic void __init add_socket(unsigned int port, int psock, int type)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci    socket[sockets].ioaddr = port;
66562306a36Sopenharmony_ci    socket[sockets].psock = psock;
66662306a36Sopenharmony_ci    socket[sockets].type = type;
66762306a36Sopenharmony_ci    socket[sockets].flags = pcic[type].flags;
66862306a36Sopenharmony_ci    if (is_alive(sockets))
66962306a36Sopenharmony_ci	socket[sockets].flags |= IS_ALIVE;
67062306a36Sopenharmony_ci    sockets++;
67162306a36Sopenharmony_ci}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_cistatic void __init add_pcic(int ns, int type)
67462306a36Sopenharmony_ci{
67562306a36Sopenharmony_ci    u_int mask = 0, i, base;
67662306a36Sopenharmony_ci    int isa_irq = 0;
67762306a36Sopenharmony_ci    struct i82365_socket *t = &socket[sockets-ns];
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci    base = sockets-ns;
68062306a36Sopenharmony_ci    if (base == 0) printk("\n");
68162306a36Sopenharmony_ci    printk(KERN_INFO "  %s", pcic[type].name);
68262306a36Sopenharmony_ci    printk(" ISA-to-PCMCIA at port %#x ofs 0x%02x",
68362306a36Sopenharmony_ci	       t->ioaddr, t->psock*0x40);
68462306a36Sopenharmony_ci    printk(", %d socket%s\n", ns, ((ns > 1) ? "s" : ""));
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci    /* Set host options, build basic interrupt mask */
68762306a36Sopenharmony_ci    if (irq_list_count == 0)
68862306a36Sopenharmony_ci	mask = irq_mask;
68962306a36Sopenharmony_ci    else
69062306a36Sopenharmony_ci	for (i = mask = 0; i < irq_list_count; i++)
69162306a36Sopenharmony_ci	    mask |= (1<<irq_list[i]);
69262306a36Sopenharmony_ci    mask &= I365_MASK & set_bridge_opts(base, ns);
69362306a36Sopenharmony_ci    /* Scan for ISA interrupts */
69462306a36Sopenharmony_ci    mask = isa_scan(base, mask);
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci    /* Poll if only two interrupts available */
69762306a36Sopenharmony_ci    if (!poll_interval) {
69862306a36Sopenharmony_ci	u_int tmp = (mask & 0xff20);
69962306a36Sopenharmony_ci	tmp = tmp & (tmp-1);
70062306a36Sopenharmony_ci	if ((tmp & (tmp-1)) == 0)
70162306a36Sopenharmony_ci	    poll_interval = HZ;
70262306a36Sopenharmony_ci    }
70362306a36Sopenharmony_ci    /* Only try an ISA cs_irq if this is the first controller */
70462306a36Sopenharmony_ci    if (!grab_irq && (cs_irq || !poll_interval)) {
70562306a36Sopenharmony_ci	/* Avoid irq 12 unless it is explicitly requested */
70662306a36Sopenharmony_ci	u_int cs_mask = mask & ((cs_irq) ? (1<<cs_irq) : ~(1<<12));
70762306a36Sopenharmony_ci	for (cs_irq = 15; cs_irq > 0; cs_irq--)
70862306a36Sopenharmony_ci	    if ((cs_mask & (1 << cs_irq)) &&
70962306a36Sopenharmony_ci		(_check_irq(cs_irq, IRQF_PROBE_SHARED) == 0))
71062306a36Sopenharmony_ci		break;
71162306a36Sopenharmony_ci	if (cs_irq) {
71262306a36Sopenharmony_ci	    grab_irq = 1;
71362306a36Sopenharmony_ci	    isa_irq = cs_irq;
71462306a36Sopenharmony_ci	    printk(" status change on irq %d\n", cs_irq);
71562306a36Sopenharmony_ci	}
71662306a36Sopenharmony_ci    }
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci    if (!isa_irq) {
71962306a36Sopenharmony_ci	if (poll_interval == 0)
72062306a36Sopenharmony_ci	    poll_interval = HZ;
72162306a36Sopenharmony_ci	printk(" polling interval = %d ms\n",
72262306a36Sopenharmony_ci	       poll_interval * 1000 / HZ);
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci    }
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci    /* Update socket interrupt information, capabilities */
72762306a36Sopenharmony_ci    for (i = 0; i < ns; i++) {
72862306a36Sopenharmony_ci	t[i].socket.features |= SS_CAP_PCCARD;
72962306a36Sopenharmony_ci	t[i].socket.map_size = 0x1000;
73062306a36Sopenharmony_ci	t[i].socket.irq_mask = mask;
73162306a36Sopenharmony_ci	t[i].cs_irq = isa_irq;
73262306a36Sopenharmony_ci    }
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci} /* add_pcic */
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci/*====================================================================*/
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci#ifdef CONFIG_PNP
73962306a36Sopenharmony_cistatic struct isapnp_device_id id_table[] __initdata = {
74062306a36Sopenharmony_ci	{ 	ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('P', 'N', 'P'),
74162306a36Sopenharmony_ci		ISAPNP_FUNCTION(0x0e00), (unsigned long) "Intel 82365-Compatible" },
74262306a36Sopenharmony_ci	{ 	ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('P', 'N', 'P'),
74362306a36Sopenharmony_ci		ISAPNP_FUNCTION(0x0e01), (unsigned long) "Cirrus Logic CL-PD6720" },
74462306a36Sopenharmony_ci	{ 	ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('P', 'N', 'P'),
74562306a36Sopenharmony_ci		ISAPNP_FUNCTION(0x0e02), (unsigned long) "VLSI VL82C146" },
74662306a36Sopenharmony_ci	{	0 }
74762306a36Sopenharmony_ci};
74862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(isapnp, id_table);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_cistatic struct pnp_dev *i82365_pnpdev;
75162306a36Sopenharmony_ci#endif
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_cistatic void __init isa_probe(void)
75462306a36Sopenharmony_ci{
75562306a36Sopenharmony_ci    int i, j, sock, k, ns, id;
75662306a36Sopenharmony_ci    unsigned int port;
75762306a36Sopenharmony_ci#ifdef CONFIG_PNP
75862306a36Sopenharmony_ci    struct isapnp_device_id *devid;
75962306a36Sopenharmony_ci    struct pnp_dev *dev;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci    for (devid = id_table; devid->vendor; devid++) {
76262306a36Sopenharmony_ci	if ((dev = pnp_find_dev(NULL, devid->vendor, devid->function, NULL))) {
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	    if (pnp_device_attach(dev) < 0)
76562306a36Sopenharmony_ci	    	continue;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	    if (pnp_activate_dev(dev) < 0) {
76862306a36Sopenharmony_ci		printk("activate failed\n");
76962306a36Sopenharmony_ci		pnp_device_detach(dev);
77062306a36Sopenharmony_ci		break;
77162306a36Sopenharmony_ci	    }
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	    if (!pnp_port_valid(dev, 0)) {
77462306a36Sopenharmony_ci		printk("invalid resources ?\n");
77562306a36Sopenharmony_ci		pnp_device_detach(dev);
77662306a36Sopenharmony_ci		break;
77762306a36Sopenharmony_ci	    }
77862306a36Sopenharmony_ci	    i365_base = pnp_port_start(dev, 0);
77962306a36Sopenharmony_ci	    i82365_pnpdev = dev;
78062306a36Sopenharmony_ci	    break;
78162306a36Sopenharmony_ci	}
78262306a36Sopenharmony_ci    }
78362306a36Sopenharmony_ci#endif
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci    if (!request_region(i365_base, 2, "i82365")) {
78662306a36Sopenharmony_ci	if (sockets == 0)
78762306a36Sopenharmony_ci	    printk("port conflict at %#lx\n", i365_base);
78862306a36Sopenharmony_ci	return;
78962306a36Sopenharmony_ci    }
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci    id = identify(i365_base, 0);
79262306a36Sopenharmony_ci    if ((id == IS_I82365DF) && (identify(i365_base, 1) != id)) {
79362306a36Sopenharmony_ci	for (i = 0; i < 4; i++) {
79462306a36Sopenharmony_ci	    if (i == ignore) continue;
79562306a36Sopenharmony_ci	    port = i365_base + ((i & 1) << 2) + ((i & 2) << 1);
79662306a36Sopenharmony_ci	    sock = (i & 1) << 1;
79762306a36Sopenharmony_ci	    if (identify(port, sock) == IS_I82365DF) {
79862306a36Sopenharmony_ci		add_socket(port, sock, IS_VLSI);
79962306a36Sopenharmony_ci		add_pcic(1, IS_VLSI);
80062306a36Sopenharmony_ci	    }
80162306a36Sopenharmony_ci	}
80262306a36Sopenharmony_ci    } else {
80362306a36Sopenharmony_ci	for (i = 0; i < 8; i += 2) {
80462306a36Sopenharmony_ci	    if (sockets && !extra_sockets && (i == 4))
80562306a36Sopenharmony_ci		break;
80662306a36Sopenharmony_ci	    port = i365_base + 2*(i>>2);
80762306a36Sopenharmony_ci	    sock = (i & 3);
80862306a36Sopenharmony_ci	    id = identify(port, sock);
80962306a36Sopenharmony_ci	    if (id < 0) continue;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	    for (j = ns = 0; j < 2; j++) {
81262306a36Sopenharmony_ci		/* Does the socket exist? */
81362306a36Sopenharmony_ci		if ((ignore == i+j) || (identify(port, sock+j) < 0))
81462306a36Sopenharmony_ci		    continue;
81562306a36Sopenharmony_ci		/* Check for bad socket decode */
81662306a36Sopenharmony_ci		for (k = 0; k <= sockets; k++)
81762306a36Sopenharmony_ci		    i365_set(k, I365_MEM(0)+I365_W_OFF, k);
81862306a36Sopenharmony_ci		for (k = 0; k <= sockets; k++)
81962306a36Sopenharmony_ci		    if (i365_get(k, I365_MEM(0)+I365_W_OFF) != k)
82062306a36Sopenharmony_ci			break;
82162306a36Sopenharmony_ci		if (k <= sockets) break;
82262306a36Sopenharmony_ci		add_socket(port, sock+j, id); ns++;
82362306a36Sopenharmony_ci	    }
82462306a36Sopenharmony_ci	    if (ns != 0) add_pcic(ns, id);
82562306a36Sopenharmony_ci	}
82662306a36Sopenharmony_ci    }
82762306a36Sopenharmony_ci}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci/*====================================================================*/
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_cistatic irqreturn_t pcic_interrupt(int irq, void *dev)
83262306a36Sopenharmony_ci{
83362306a36Sopenharmony_ci    int i, j, csc;
83462306a36Sopenharmony_ci    u_int events, active;
83562306a36Sopenharmony_ci    u_long flags = 0;
83662306a36Sopenharmony_ci    int handled = 0;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci    pr_debug("pcic_interrupt(%d)\n", irq);
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci    for (j = 0; j < 20; j++) {
84162306a36Sopenharmony_ci	active = 0;
84262306a36Sopenharmony_ci	for (i = 0; i < sockets; i++) {
84362306a36Sopenharmony_ci	    if (socket[i].cs_irq != irq)
84462306a36Sopenharmony_ci		continue;
84562306a36Sopenharmony_ci	    handled = 1;
84662306a36Sopenharmony_ci	    ISA_LOCK(i, flags);
84762306a36Sopenharmony_ci	    csc = i365_get(i, I365_CSC);
84862306a36Sopenharmony_ci	    if ((csc == 0) || (i365_get(i, I365_IDENT) & 0x70)) {
84962306a36Sopenharmony_ci		ISA_UNLOCK(i, flags);
85062306a36Sopenharmony_ci		continue;
85162306a36Sopenharmony_ci	    }
85262306a36Sopenharmony_ci	    events = (csc & I365_CSC_DETECT) ? SS_DETECT : 0;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	    if (i365_get(i, I365_INTCTL) & I365_PC_IOCARD)
85562306a36Sopenharmony_ci		events |= (csc & I365_CSC_STSCHG) ? SS_STSCHG : 0;
85662306a36Sopenharmony_ci	    else {
85762306a36Sopenharmony_ci		events |= (csc & I365_CSC_BVD1) ? SS_BATDEAD : 0;
85862306a36Sopenharmony_ci		events |= (csc & I365_CSC_BVD2) ? SS_BATWARN : 0;
85962306a36Sopenharmony_ci		events |= (csc & I365_CSC_READY) ? SS_READY : 0;
86062306a36Sopenharmony_ci	    }
86162306a36Sopenharmony_ci	    ISA_UNLOCK(i, flags);
86262306a36Sopenharmony_ci	    pr_debug("socket %d event 0x%02x\n", i, events);
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	    if (events)
86562306a36Sopenharmony_ci		pcmcia_parse_events(&socket[i].socket, events);
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	    active |= events;
86862306a36Sopenharmony_ci	}
86962306a36Sopenharmony_ci	if (!active) break;
87062306a36Sopenharmony_ci    }
87162306a36Sopenharmony_ci    if (j == 20)
87262306a36Sopenharmony_ci	printk(KERN_NOTICE "i82365: infinite loop in interrupt handler\n");
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci    pr_debug("pcic_interrupt done\n");
87562306a36Sopenharmony_ci    return IRQ_RETVAL(handled);
87662306a36Sopenharmony_ci} /* pcic_interrupt */
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_cistatic void pcic_interrupt_wrapper(struct timer_list *unused)
87962306a36Sopenharmony_ci{
88062306a36Sopenharmony_ci    pcic_interrupt(0, NULL);
88162306a36Sopenharmony_ci    poll_timer.expires = jiffies + poll_interval;
88262306a36Sopenharmony_ci    add_timer(&poll_timer);
88362306a36Sopenharmony_ci}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci/*====================================================================*/
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_cistatic int i365_get_status(u_short sock, u_int *value)
88862306a36Sopenharmony_ci{
88962306a36Sopenharmony_ci    u_int status;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci    status = i365_get(sock, I365_STATUS);
89262306a36Sopenharmony_ci    *value = ((status & I365_CS_DETECT) == I365_CS_DETECT)
89362306a36Sopenharmony_ci	? SS_DETECT : 0;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci    if (i365_get(sock, I365_INTCTL) & I365_PC_IOCARD)
89662306a36Sopenharmony_ci	*value |= (status & I365_CS_STSCHG) ? 0 : SS_STSCHG;
89762306a36Sopenharmony_ci    else {
89862306a36Sopenharmony_ci	*value |= (status & I365_CS_BVD1) ? 0 : SS_BATDEAD;
89962306a36Sopenharmony_ci	*value |= (status & I365_CS_BVD2) ? 0 : SS_BATWARN;
90062306a36Sopenharmony_ci    }
90162306a36Sopenharmony_ci    *value |= (status & I365_CS_WRPROT) ? SS_WRPROT : 0;
90262306a36Sopenharmony_ci    *value |= (status & I365_CS_READY) ? SS_READY : 0;
90362306a36Sopenharmony_ci    *value |= (status & I365_CS_POWERON) ? SS_POWERON : 0;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci    if (socket[sock].type == IS_VG469) {
90662306a36Sopenharmony_ci	status = i365_get(sock, VG469_VSENSE);
90762306a36Sopenharmony_ci	if (socket[sock].psock & 1) {
90862306a36Sopenharmony_ci	    *value |= (status & VG469_VSENSE_B_VS1) ? 0 : SS_3VCARD;
90962306a36Sopenharmony_ci	    *value |= (status & VG469_VSENSE_B_VS2) ? 0 : SS_XVCARD;
91062306a36Sopenharmony_ci	} else {
91162306a36Sopenharmony_ci	    *value |= (status & VG469_VSENSE_A_VS1) ? 0 : SS_3VCARD;
91262306a36Sopenharmony_ci	    *value |= (status & VG469_VSENSE_A_VS2) ? 0 : SS_XVCARD;
91362306a36Sopenharmony_ci	}
91462306a36Sopenharmony_ci    }
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci    pr_debug("GetStatus(%d) = %#4.4x\n", sock, *value);
91762306a36Sopenharmony_ci    return 0;
91862306a36Sopenharmony_ci} /* i365_get_status */
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci/*====================================================================*/
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_cistatic int i365_set_socket(u_short sock, socket_state_t *state)
92362306a36Sopenharmony_ci{
92462306a36Sopenharmony_ci    struct i82365_socket *t = &socket[sock];
92562306a36Sopenharmony_ci    u_char reg;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci    pr_debug("SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
92862306a36Sopenharmony_ci	  "io_irq %d, csc_mask %#2.2x)\n", sock, state->flags,
92962306a36Sopenharmony_ci	  state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci    /* First set global controller options */
93262306a36Sopenharmony_ci    set_bridge_state(sock);
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci    /* IO card, RESET flag, IO interrupt */
93562306a36Sopenharmony_ci    reg = t->intr;
93662306a36Sopenharmony_ci    reg |= state->io_irq;
93762306a36Sopenharmony_ci    reg |= (state->flags & SS_RESET) ? 0 : I365_PC_RESET;
93862306a36Sopenharmony_ci    reg |= (state->flags & SS_IOCARD) ? I365_PC_IOCARD : 0;
93962306a36Sopenharmony_ci    i365_set(sock, I365_INTCTL, reg);
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci    reg = I365_PWR_NORESET;
94262306a36Sopenharmony_ci    if (state->flags & SS_PWR_AUTO) reg |= I365_PWR_AUTO;
94362306a36Sopenharmony_ci    if (state->flags & SS_OUTPUT_ENA) reg |= I365_PWR_OUT;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci    if (t->flags & IS_CIRRUS) {
94662306a36Sopenharmony_ci	if (state->Vpp != 0) {
94762306a36Sopenharmony_ci	    if (state->Vpp == 120)
94862306a36Sopenharmony_ci		reg |= I365_VPP1_12V;
94962306a36Sopenharmony_ci	    else if (state->Vpp == state->Vcc)
95062306a36Sopenharmony_ci		reg |= I365_VPP1_5V;
95162306a36Sopenharmony_ci	    else return -EINVAL;
95262306a36Sopenharmony_ci	}
95362306a36Sopenharmony_ci	if (state->Vcc != 0) {
95462306a36Sopenharmony_ci	    reg |= I365_VCC_5V;
95562306a36Sopenharmony_ci	    if (state->Vcc == 33)
95662306a36Sopenharmony_ci		i365_bset(sock, PD67_MISC_CTL_1, PD67_MC1_VCC_3V);
95762306a36Sopenharmony_ci	    else if (state->Vcc == 50)
95862306a36Sopenharmony_ci		i365_bclr(sock, PD67_MISC_CTL_1, PD67_MC1_VCC_3V);
95962306a36Sopenharmony_ci	    else return -EINVAL;
96062306a36Sopenharmony_ci	}
96162306a36Sopenharmony_ci    } else if (t->flags & IS_VG_PWR) {
96262306a36Sopenharmony_ci	if (state->Vpp != 0) {
96362306a36Sopenharmony_ci	    if (state->Vpp == 120)
96462306a36Sopenharmony_ci		reg |= I365_VPP1_12V;
96562306a36Sopenharmony_ci	    else if (state->Vpp == state->Vcc)
96662306a36Sopenharmony_ci		reg |= I365_VPP1_5V;
96762306a36Sopenharmony_ci	    else return -EINVAL;
96862306a36Sopenharmony_ci	}
96962306a36Sopenharmony_ci	if (state->Vcc != 0) {
97062306a36Sopenharmony_ci	    reg |= I365_VCC_5V;
97162306a36Sopenharmony_ci	    if (state->Vcc == 33)
97262306a36Sopenharmony_ci		i365_bset(sock, VG469_VSELECT, VG469_VSEL_VCC);
97362306a36Sopenharmony_ci	    else if (state->Vcc == 50)
97462306a36Sopenharmony_ci		i365_bclr(sock, VG469_VSELECT, VG469_VSEL_VCC);
97562306a36Sopenharmony_ci	    else return -EINVAL;
97662306a36Sopenharmony_ci	}
97762306a36Sopenharmony_ci    } else if (t->flags & IS_DF_PWR) {
97862306a36Sopenharmony_ci	switch (state->Vcc) {
97962306a36Sopenharmony_ci	case 0:		break;
98062306a36Sopenharmony_ci	case 33:   	reg |= I365_VCC_3V; break;
98162306a36Sopenharmony_ci	case 50:	reg |= I365_VCC_5V; break;
98262306a36Sopenharmony_ci	default:	return -EINVAL;
98362306a36Sopenharmony_ci	}
98462306a36Sopenharmony_ci	switch (state->Vpp) {
98562306a36Sopenharmony_ci	case 0:		break;
98662306a36Sopenharmony_ci	case 50:   	reg |= I365_VPP1_5V; break;
98762306a36Sopenharmony_ci	case 120:	reg |= I365_VPP1_12V; break;
98862306a36Sopenharmony_ci	default:	return -EINVAL;
98962306a36Sopenharmony_ci	}
99062306a36Sopenharmony_ci    } else {
99162306a36Sopenharmony_ci	switch (state->Vcc) {
99262306a36Sopenharmony_ci	case 0:		break;
99362306a36Sopenharmony_ci	case 50:	reg |= I365_VCC_5V; break;
99462306a36Sopenharmony_ci	default:	return -EINVAL;
99562306a36Sopenharmony_ci	}
99662306a36Sopenharmony_ci	switch (state->Vpp) {
99762306a36Sopenharmony_ci	case 0:		break;
99862306a36Sopenharmony_ci	case 50:	reg |= I365_VPP1_5V | I365_VPP2_5V; break;
99962306a36Sopenharmony_ci	case 120:	reg |= I365_VPP1_12V | I365_VPP2_12V; break;
100062306a36Sopenharmony_ci	default:	return -EINVAL;
100162306a36Sopenharmony_ci	}
100262306a36Sopenharmony_ci    }
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci    if (reg != i365_get(sock, I365_POWER))
100562306a36Sopenharmony_ci	i365_set(sock, I365_POWER, reg);
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci    /* Chipset-specific functions */
100862306a36Sopenharmony_ci    if (t->flags & IS_CIRRUS) {
100962306a36Sopenharmony_ci	/* Speaker control */
101062306a36Sopenharmony_ci	i365_bflip(sock, PD67_MISC_CTL_1, PD67_MC1_SPKR_ENA,
101162306a36Sopenharmony_ci		   state->flags & SS_SPKR_ENA);
101262306a36Sopenharmony_ci    }
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci    /* Card status change interrupt mask */
101562306a36Sopenharmony_ci    reg = t->cs_irq << 4;
101662306a36Sopenharmony_ci    if (state->csc_mask & SS_DETECT) reg |= I365_CSC_DETECT;
101762306a36Sopenharmony_ci    if (state->flags & SS_IOCARD) {
101862306a36Sopenharmony_ci	if (state->csc_mask & SS_STSCHG) reg |= I365_CSC_STSCHG;
101962306a36Sopenharmony_ci    } else {
102062306a36Sopenharmony_ci	if (state->csc_mask & SS_BATDEAD) reg |= I365_CSC_BVD1;
102162306a36Sopenharmony_ci	if (state->csc_mask & SS_BATWARN) reg |= I365_CSC_BVD2;
102262306a36Sopenharmony_ci	if (state->csc_mask & SS_READY) reg |= I365_CSC_READY;
102362306a36Sopenharmony_ci    }
102462306a36Sopenharmony_ci    i365_set(sock, I365_CSCINT, reg);
102562306a36Sopenharmony_ci    i365_get(sock, I365_CSC);
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci    return 0;
102862306a36Sopenharmony_ci} /* i365_set_socket */
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci/*====================================================================*/
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_cistatic int i365_set_io_map(u_short sock, struct pccard_io_map *io)
103362306a36Sopenharmony_ci{
103462306a36Sopenharmony_ci    u_char map, ioctl;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci    pr_debug("SetIOMap(%d, %d, %#2.2x, %d ns, "
103762306a36Sopenharmony_ci	  "%#llx-%#llx)\n", sock, io->map, io->flags, io->speed,
103862306a36Sopenharmony_ci	  (unsigned long long)io->start, (unsigned long long)io->stop);
103962306a36Sopenharmony_ci    map = io->map;
104062306a36Sopenharmony_ci    if ((map > 1) || (io->start > 0xffff) || (io->stop > 0xffff) ||
104162306a36Sopenharmony_ci	(io->stop < io->start)) return -EINVAL;
104262306a36Sopenharmony_ci    /* Turn off the window before changing anything */
104362306a36Sopenharmony_ci    if (i365_get(sock, I365_ADDRWIN) & I365_ENA_IO(map))
104462306a36Sopenharmony_ci	i365_bclr(sock, I365_ADDRWIN, I365_ENA_IO(map));
104562306a36Sopenharmony_ci    i365_set_pair(sock, I365_IO(map)+I365_W_START, io->start);
104662306a36Sopenharmony_ci    i365_set_pair(sock, I365_IO(map)+I365_W_STOP, io->stop);
104762306a36Sopenharmony_ci    ioctl = i365_get(sock, I365_IOCTL) & ~I365_IOCTL_MASK(map);
104862306a36Sopenharmony_ci    if (io->speed) ioctl |= I365_IOCTL_WAIT(map);
104962306a36Sopenharmony_ci    if (io->flags & MAP_0WS) ioctl |= I365_IOCTL_0WS(map);
105062306a36Sopenharmony_ci    if (io->flags & MAP_16BIT) ioctl |= I365_IOCTL_16BIT(map);
105162306a36Sopenharmony_ci    if (io->flags & MAP_AUTOSZ) ioctl |= I365_IOCTL_IOCS16(map);
105262306a36Sopenharmony_ci    i365_set(sock, I365_IOCTL, ioctl);
105362306a36Sopenharmony_ci    /* Turn on the window if necessary */
105462306a36Sopenharmony_ci    if (io->flags & MAP_ACTIVE)
105562306a36Sopenharmony_ci	i365_bset(sock, I365_ADDRWIN, I365_ENA_IO(map));
105662306a36Sopenharmony_ci    return 0;
105762306a36Sopenharmony_ci} /* i365_set_io_map */
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci/*====================================================================*/
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_cistatic int i365_set_mem_map(u_short sock, struct pccard_mem_map *mem)
106262306a36Sopenharmony_ci{
106362306a36Sopenharmony_ci    u_short base, i;
106462306a36Sopenharmony_ci    u_char map;
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci    pr_debug("SetMemMap(%d, %d, %#2.2x, %d ns, %#llx-%#llx, "
106762306a36Sopenharmony_ci	  "%#x)\n", sock, mem->map, mem->flags, mem->speed,
106862306a36Sopenharmony_ci	  (unsigned long long)mem->res->start,
106962306a36Sopenharmony_ci	  (unsigned long long)mem->res->end, mem->card_start);
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci    map = mem->map;
107262306a36Sopenharmony_ci    if ((map > 4) || (mem->card_start > 0x3ffffff) ||
107362306a36Sopenharmony_ci	(mem->res->start > mem->res->end) || (mem->speed > 1000))
107462306a36Sopenharmony_ci	return -EINVAL;
107562306a36Sopenharmony_ci    if ((mem->res->start > 0xffffff) || (mem->res->end > 0xffffff))
107662306a36Sopenharmony_ci	return -EINVAL;
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci    /* Turn off the window before changing anything */
107962306a36Sopenharmony_ci    if (i365_get(sock, I365_ADDRWIN) & I365_ENA_MEM(map))
108062306a36Sopenharmony_ci	i365_bclr(sock, I365_ADDRWIN, I365_ENA_MEM(map));
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci    base = I365_MEM(map);
108362306a36Sopenharmony_ci    i = (mem->res->start >> 12) & 0x0fff;
108462306a36Sopenharmony_ci    if (mem->flags & MAP_16BIT) i |= I365_MEM_16BIT;
108562306a36Sopenharmony_ci    if (mem->flags & MAP_0WS) i |= I365_MEM_0WS;
108662306a36Sopenharmony_ci    i365_set_pair(sock, base+I365_W_START, i);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci    i = (mem->res->end >> 12) & 0x0fff;
108962306a36Sopenharmony_ci    switch (to_cycles(mem->speed)) {
109062306a36Sopenharmony_ci    case 0:	break;
109162306a36Sopenharmony_ci    case 1:	i |= I365_MEM_WS0; break;
109262306a36Sopenharmony_ci    case 2:	i |= I365_MEM_WS1; break;
109362306a36Sopenharmony_ci    default:	i |= I365_MEM_WS1 | I365_MEM_WS0; break;
109462306a36Sopenharmony_ci    }
109562306a36Sopenharmony_ci    i365_set_pair(sock, base+I365_W_STOP, i);
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci    i = ((mem->card_start - mem->res->start) >> 12) & 0x3fff;
109862306a36Sopenharmony_ci    if (mem->flags & MAP_WRPROT) i |= I365_MEM_WRPROT;
109962306a36Sopenharmony_ci    if (mem->flags & MAP_ATTRIB) i |= I365_MEM_REG;
110062306a36Sopenharmony_ci    i365_set_pair(sock, base+I365_W_OFF, i);
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci    /* Turn on the window if necessary */
110362306a36Sopenharmony_ci    if (mem->flags & MAP_ACTIVE)
110462306a36Sopenharmony_ci	i365_bset(sock, I365_ADDRWIN, I365_ENA_MEM(map));
110562306a36Sopenharmony_ci    return 0;
110662306a36Sopenharmony_ci} /* i365_set_mem_map */
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci#if 0 /* driver model ordering issue */
110962306a36Sopenharmony_ci/*======================================================================
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci    Routines for accessing socket information and register dumps via
111262306a36Sopenharmony_ci    /sys/class/pcmcia_socket/...
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci======================================================================*/
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_cistatic ssize_t show_info(struct class_device *class_dev, char *buf)
111762306a36Sopenharmony_ci{
111862306a36Sopenharmony_ci	struct i82365_socket *s = container_of(class_dev, struct i82365_socket, socket.dev);
111962306a36Sopenharmony_ci	return sprintf(buf, "type:     %s\npsock:    %d\n",
112062306a36Sopenharmony_ci		       pcic[s->type].name, s->psock);
112162306a36Sopenharmony_ci}
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_cistatic ssize_t show_exca(struct class_device *class_dev, char *buf)
112462306a36Sopenharmony_ci{
112562306a36Sopenharmony_ci	struct i82365_socket *s = container_of(class_dev, struct i82365_socket, socket.dev);
112662306a36Sopenharmony_ci	unsigned short sock;
112762306a36Sopenharmony_ci	int i;
112862306a36Sopenharmony_ci	ssize_t ret = 0;
112962306a36Sopenharmony_ci	unsigned long flags = 0;
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	sock = s->number;
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	ISA_LOCK(sock, flags);
113462306a36Sopenharmony_ci	for (i = 0; i < 0x40; i += 4) {
113562306a36Sopenharmony_ci		ret += sprintf(buf, "%02x %02x %02x %02x%s",
113662306a36Sopenharmony_ci			       i365_get(sock,i), i365_get(sock,i+1),
113762306a36Sopenharmony_ci			       i365_get(sock,i+2), i365_get(sock,i+3),
113862306a36Sopenharmony_ci			       ((i % 16) == 12) ? "\n" : " ");
113962306a36Sopenharmony_ci		buf += ret;
114062306a36Sopenharmony_ci	}
114162306a36Sopenharmony_ci	ISA_UNLOCK(sock, flags);
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	return ret;
114462306a36Sopenharmony_ci}
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_cistatic CLASS_DEVICE_ATTR(exca, S_IRUGO, show_exca, NULL);
114762306a36Sopenharmony_cistatic CLASS_DEVICE_ATTR(info, S_IRUGO, show_info, NULL);
114862306a36Sopenharmony_ci#endif
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci/*====================================================================*/
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci/* this is horribly ugly... proper locking needs to be done here at
115362306a36Sopenharmony_ci * some time... */
115462306a36Sopenharmony_ci#define LOCKED(x) do { \
115562306a36Sopenharmony_ci	int retval; \
115662306a36Sopenharmony_ci	unsigned long flags; \
115762306a36Sopenharmony_ci	spin_lock_irqsave(&isa_lock, flags); \
115862306a36Sopenharmony_ci	retval = x; \
115962306a36Sopenharmony_ci	spin_unlock_irqrestore(&isa_lock, flags); \
116062306a36Sopenharmony_ci	return retval; \
116162306a36Sopenharmony_ci} while (0)
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_cistatic int pcic_get_status(struct pcmcia_socket *s, u_int *value)
116562306a36Sopenharmony_ci{
116662306a36Sopenharmony_ci	unsigned int sock = container_of(s, struct i82365_socket, socket)->number;
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	if (socket[sock].flags & IS_ALIVE) {
116962306a36Sopenharmony_ci		*value = 0;
117062306a36Sopenharmony_ci		return -EINVAL;
117162306a36Sopenharmony_ci	}
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	LOCKED(i365_get_status(sock, value));
117462306a36Sopenharmony_ci}
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_cistatic int pcic_set_socket(struct pcmcia_socket *s, socket_state_t *state)
117762306a36Sopenharmony_ci{
117862306a36Sopenharmony_ci	unsigned int sock = container_of(s, struct i82365_socket, socket)->number;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	if (socket[sock].flags & IS_ALIVE)
118162306a36Sopenharmony_ci		return -EINVAL;
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	LOCKED(i365_set_socket(sock, state));
118462306a36Sopenharmony_ci}
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_cistatic int pcic_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
118762306a36Sopenharmony_ci{
118862306a36Sopenharmony_ci	unsigned int sock = container_of(s, struct i82365_socket, socket)->number;
118962306a36Sopenharmony_ci	if (socket[sock].flags & IS_ALIVE)
119062306a36Sopenharmony_ci		return -EINVAL;
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	LOCKED(i365_set_io_map(sock, io));
119362306a36Sopenharmony_ci}
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_cistatic int pcic_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *mem)
119662306a36Sopenharmony_ci{
119762306a36Sopenharmony_ci	unsigned int sock = container_of(s, struct i82365_socket, socket)->number;
119862306a36Sopenharmony_ci	if (socket[sock].flags & IS_ALIVE)
119962306a36Sopenharmony_ci		return -EINVAL;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	LOCKED(i365_set_mem_map(sock, mem));
120262306a36Sopenharmony_ci}
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_cistatic int pcic_init(struct pcmcia_socket *s)
120562306a36Sopenharmony_ci{
120662306a36Sopenharmony_ci	int i;
120762306a36Sopenharmony_ci	struct resource res = { .start = 0, .end = 0x1000 };
120862306a36Sopenharmony_ci	pccard_io_map io = { 0, 0, 0, 0, 1 };
120962306a36Sopenharmony_ci	pccard_mem_map mem = { .res = &res, };
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	for (i = 0; i < 2; i++) {
121262306a36Sopenharmony_ci		io.map = i;
121362306a36Sopenharmony_ci		pcic_set_io_map(s, &io);
121462306a36Sopenharmony_ci	}
121562306a36Sopenharmony_ci	for (i = 0; i < 5; i++) {
121662306a36Sopenharmony_ci		mem.map = i;
121762306a36Sopenharmony_ci		pcic_set_mem_map(s, &mem);
121862306a36Sopenharmony_ci	}
121962306a36Sopenharmony_ci	return 0;
122062306a36Sopenharmony_ci}
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_cistatic struct pccard_operations pcic_operations = {
122462306a36Sopenharmony_ci	.init			= pcic_init,
122562306a36Sopenharmony_ci	.get_status		= pcic_get_status,
122662306a36Sopenharmony_ci	.set_socket		= pcic_set_socket,
122762306a36Sopenharmony_ci	.set_io_map		= pcic_set_io_map,
122862306a36Sopenharmony_ci	.set_mem_map		= pcic_set_mem_map,
122962306a36Sopenharmony_ci};
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci/*====================================================================*/
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_cistatic struct platform_driver i82365_driver = {
123462306a36Sopenharmony_ci	.driver = {
123562306a36Sopenharmony_ci		.name = "i82365",
123662306a36Sopenharmony_ci	},
123762306a36Sopenharmony_ci};
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_cistatic struct platform_device *i82365_device;
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_cistatic int __init init_i82365(void)
124262306a36Sopenharmony_ci{
124362306a36Sopenharmony_ci    int i, ret;
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci    ret = platform_driver_register(&i82365_driver);
124662306a36Sopenharmony_ci    if (ret)
124762306a36Sopenharmony_ci	goto err_out;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci    i82365_device = platform_device_alloc("i82365", 0);
125062306a36Sopenharmony_ci    if (i82365_device) {
125162306a36Sopenharmony_ci	    ret = platform_device_add(i82365_device);
125262306a36Sopenharmony_ci	    if (ret)
125362306a36Sopenharmony_ci		    platform_device_put(i82365_device);
125462306a36Sopenharmony_ci    } else
125562306a36Sopenharmony_ci	    ret = -ENOMEM;
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci    if (ret)
125862306a36Sopenharmony_ci	goto err_driver_unregister;
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci    printk(KERN_INFO "Intel ISA PCIC probe: ");
126162306a36Sopenharmony_ci    sockets = 0;
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci    isa_probe();
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci    if (sockets == 0) {
126662306a36Sopenharmony_ci	printk("not found.\n");
126762306a36Sopenharmony_ci	ret = -ENODEV;
126862306a36Sopenharmony_ci	goto err_dev_unregister;
126962306a36Sopenharmony_ci    }
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci    /* Set up interrupt handler(s) */
127262306a36Sopenharmony_ci    if (grab_irq != 0)
127362306a36Sopenharmony_ci	ret = request_irq(cs_irq, pcic_interrupt, 0, "i82365", pcic_interrupt);
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci    if (ret)
127662306a36Sopenharmony_ci	goto err_socket_release;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci    /* register sockets with the pcmcia core */
127962306a36Sopenharmony_ci    for (i = 0; i < sockets; i++) {
128062306a36Sopenharmony_ci	    socket[i].socket.dev.parent = &i82365_device->dev;
128162306a36Sopenharmony_ci	    socket[i].socket.ops = &pcic_operations;
128262306a36Sopenharmony_ci	    socket[i].socket.resource_ops = &pccard_nonstatic_ops;
128362306a36Sopenharmony_ci	    socket[i].socket.owner = THIS_MODULE;
128462306a36Sopenharmony_ci	    socket[i].number = i;
128562306a36Sopenharmony_ci	    ret = pcmcia_register_socket(&socket[i].socket);
128662306a36Sopenharmony_ci	    if (!ret)
128762306a36Sopenharmony_ci		    socket[i].flags |= IS_REGISTERED;
128862306a36Sopenharmony_ci    }
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci    /* Finally, schedule a polling interrupt */
129162306a36Sopenharmony_ci    if (poll_interval != 0) {
129262306a36Sopenharmony_ci	timer_setup(&poll_timer, pcic_interrupt_wrapper, 0);
129362306a36Sopenharmony_ci    	poll_timer.expires = jiffies + poll_interval;
129462306a36Sopenharmony_ci	add_timer(&poll_timer);
129562306a36Sopenharmony_ci    }
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci    return 0;
129862306a36Sopenharmony_cierr_socket_release:
129962306a36Sopenharmony_ci    for (i = 0; i < sockets; i++) {
130062306a36Sopenharmony_ci	/* Turn off all interrupt sources! */
130162306a36Sopenharmony_ci	i365_set(i, I365_CSCINT, 0);
130262306a36Sopenharmony_ci	release_region(socket[i].ioaddr, 2);
130362306a36Sopenharmony_ci    }
130462306a36Sopenharmony_cierr_dev_unregister:
130562306a36Sopenharmony_ci    platform_device_unregister(i82365_device);
130662306a36Sopenharmony_ci    release_region(i365_base, 2);
130762306a36Sopenharmony_ci#ifdef CONFIG_PNP
130862306a36Sopenharmony_ci    if (i82365_pnpdev)
130962306a36Sopenharmony_ci	pnp_disable_dev(i82365_pnpdev);
131062306a36Sopenharmony_ci#endif
131162306a36Sopenharmony_cierr_driver_unregister:
131262306a36Sopenharmony_ci    platform_driver_unregister(&i82365_driver);
131362306a36Sopenharmony_cierr_out:
131462306a36Sopenharmony_ci    return ret;
131562306a36Sopenharmony_ci} /* init_i82365 */
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_cistatic void __exit exit_i82365(void)
131862306a36Sopenharmony_ci{
131962306a36Sopenharmony_ci    int i;
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci    for (i = 0; i < sockets; i++) {
132262306a36Sopenharmony_ci	    if (socket[i].flags & IS_REGISTERED)
132362306a36Sopenharmony_ci		    pcmcia_unregister_socket(&socket[i].socket);
132462306a36Sopenharmony_ci    }
132562306a36Sopenharmony_ci    platform_device_unregister(i82365_device);
132662306a36Sopenharmony_ci    if (poll_interval != 0)
132762306a36Sopenharmony_ci	del_timer_sync(&poll_timer);
132862306a36Sopenharmony_ci    if (grab_irq != 0)
132962306a36Sopenharmony_ci	free_irq(cs_irq, pcic_interrupt);
133062306a36Sopenharmony_ci    for (i = 0; i < sockets; i++) {
133162306a36Sopenharmony_ci	/* Turn off all interrupt sources! */
133262306a36Sopenharmony_ci	i365_set(i, I365_CSCINT, 0);
133362306a36Sopenharmony_ci	release_region(socket[i].ioaddr, 2);
133462306a36Sopenharmony_ci    }
133562306a36Sopenharmony_ci    release_region(i365_base, 2);
133662306a36Sopenharmony_ci#ifdef CONFIG_PNP
133762306a36Sopenharmony_ci    if (i82365_pnpdev)
133862306a36Sopenharmony_ci    		pnp_disable_dev(i82365_pnpdev);
133962306a36Sopenharmony_ci#endif
134062306a36Sopenharmony_ci    platform_driver_unregister(&i82365_driver);
134162306a36Sopenharmony_ci} /* exit_i82365 */
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_cimodule_init(init_i82365);
134462306a36Sopenharmony_cimodule_exit(exit_i82365);
134562306a36Sopenharmony_ciMODULE_LICENSE("Dual MPL/GPL");
134662306a36Sopenharmony_ci/*====================================================================*/
1347