18c2ecf20Sopenharmony_ci/*======================================================================
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci    Device driver for Intel 82365 and compatible PC Card controllers.
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci    i82365.c 1.265 1999/11/10 18:36:21
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci    The contents of this file are subject to the Mozilla Public
88c2ecf20Sopenharmony_ci    License Version 1.1 (the "License"); you may not use this file
98c2ecf20Sopenharmony_ci    except in compliance with the License. You may obtain a copy of
108c2ecf20Sopenharmony_ci    the License at http://www.mozilla.org/MPL/
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci    Software distributed under the License is distributed on an "AS
138c2ecf20Sopenharmony_ci    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
148c2ecf20Sopenharmony_ci    implied. See the License for the specific language governing
158c2ecf20Sopenharmony_ci    rights and limitations under the License.
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci    The initial developer of the original code is David A. Hinds
188c2ecf20Sopenharmony_ci    <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
198c2ecf20Sopenharmony_ci    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci    Alternatively, the contents of this file may be used under the
228c2ecf20Sopenharmony_ci    terms of the GNU General Public License version 2 (the "GPL"), in which
238c2ecf20Sopenharmony_ci    case the provisions of the GPL are applicable instead of the
248c2ecf20Sopenharmony_ci    above.  If you wish to allow the use of your version of this file
258c2ecf20Sopenharmony_ci    only under the terms of the GPL and not to allow others to use
268c2ecf20Sopenharmony_ci    your version of this file under the MPL, indicate your decision
278c2ecf20Sopenharmony_ci    by deleting the provisions above and replace them with the notice
288c2ecf20Sopenharmony_ci    and other provisions required by the GPL.  If you do not delete
298c2ecf20Sopenharmony_ci    the provisions above, a recipient may use your version of this
308c2ecf20Sopenharmony_ci    file under either the MPL or the GPL.
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci======================================================================*/
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include <linux/module.h>
358c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
368c2ecf20Sopenharmony_ci#include <linux/init.h>
378c2ecf20Sopenharmony_ci#include <linux/types.h>
388c2ecf20Sopenharmony_ci#include <linux/fcntl.h>
398c2ecf20Sopenharmony_ci#include <linux/string.h>
408c2ecf20Sopenharmony_ci#include <linux/kernel.h>
418c2ecf20Sopenharmony_ci#include <linux/errno.h>
428c2ecf20Sopenharmony_ci#include <linux/timer.h>
438c2ecf20Sopenharmony_ci#include <linux/ioport.h>
448c2ecf20Sopenharmony_ci#include <linux/delay.h>
458c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
468c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
478c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
488c2ecf20Sopenharmony_ci#include <linux/bitops.h>
498c2ecf20Sopenharmony_ci#include <asm/irq.h>
508c2ecf20Sopenharmony_ci#include <asm/io.h>
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#include <pcmcia/ss.h>
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#include <linux/isapnp.h>
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/* ISA-bus controllers */
578c2ecf20Sopenharmony_ci#include "i82365.h"
588c2ecf20Sopenharmony_ci#include "cirrus.h"
598c2ecf20Sopenharmony_ci#include "vg468.h"
608c2ecf20Sopenharmony_ci#include "ricoh.h"
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic irqreturn_t i365_count_irq(int, void *);
648c2ecf20Sopenharmony_cistatic inline int _check_irq(int irq, int flags)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci    if (request_irq(irq, i365_count_irq, flags, "x", i365_count_irq) != 0)
678c2ecf20Sopenharmony_ci	return -1;
688c2ecf20Sopenharmony_ci    free_irq(irq, i365_count_irq);
698c2ecf20Sopenharmony_ci    return 0;
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/*====================================================================*/
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/* Parameters that can be set with 'insmod' */
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci/* Default base address for i82365sl and other ISA chips */
778c2ecf20Sopenharmony_cistatic unsigned long i365_base = 0x3e0;
788c2ecf20Sopenharmony_ci/* Should we probe at 0x3e2 for an extra ISA controller? */
798c2ecf20Sopenharmony_cistatic int extra_sockets = 0;
808c2ecf20Sopenharmony_ci/* Specify a socket number to ignore */
818c2ecf20Sopenharmony_cistatic int ignore = -1;
828c2ecf20Sopenharmony_ci/* Bit map or list of interrupts to choose from */
838c2ecf20Sopenharmony_cistatic u_int irq_mask = 0xffff;
848c2ecf20Sopenharmony_cistatic int irq_list[16];
858c2ecf20Sopenharmony_cistatic unsigned int irq_list_count;
868c2ecf20Sopenharmony_ci/* The card status change interrupt -- 0 means autoselect */
878c2ecf20Sopenharmony_cistatic int cs_irq = 0;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci/* Probe for safe interrupts? */
908c2ecf20Sopenharmony_cistatic int do_scan = 1;
918c2ecf20Sopenharmony_ci/* Poll status interval -- 0 means default to interrupt */
928c2ecf20Sopenharmony_cistatic int poll_interval = 0;
938c2ecf20Sopenharmony_ci/* External clock time, in nanoseconds.  120 ns = 8.33 MHz */
948c2ecf20Sopenharmony_cistatic int cycle_time = 120;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci/* Cirrus options */
978c2ecf20Sopenharmony_cistatic int has_dma = -1;
988c2ecf20Sopenharmony_cistatic int has_led = -1;
998c2ecf20Sopenharmony_cistatic int has_ring = -1;
1008c2ecf20Sopenharmony_cistatic int dynamic_mode = 0;
1018c2ecf20Sopenharmony_cistatic int freq_bypass = -1;
1028c2ecf20Sopenharmony_cistatic int setup_time = -1;
1038c2ecf20Sopenharmony_cistatic int cmd_time = -1;
1048c2ecf20Sopenharmony_cistatic int recov_time = -1;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci/* Vadem options */
1078c2ecf20Sopenharmony_cistatic int async_clock = -1;
1088c2ecf20Sopenharmony_cistatic int cable_mode = -1;
1098c2ecf20Sopenharmony_cistatic int wakeup = 0;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cimodule_param_hw(i365_base, ulong, ioport, 0444);
1128c2ecf20Sopenharmony_cimodule_param(ignore, int, 0444);
1138c2ecf20Sopenharmony_cimodule_param(extra_sockets, int, 0444);
1148c2ecf20Sopenharmony_cimodule_param_hw(irq_mask, int, other, 0444);
1158c2ecf20Sopenharmony_cimodule_param_hw_array(irq_list, int, irq, &irq_list_count, 0444);
1168c2ecf20Sopenharmony_cimodule_param_hw(cs_irq, int, irq, 0444);
1178c2ecf20Sopenharmony_cimodule_param(async_clock, int, 0444);
1188c2ecf20Sopenharmony_cimodule_param(cable_mode, int, 0444);
1198c2ecf20Sopenharmony_cimodule_param(wakeup, int, 0444);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cimodule_param(do_scan, int, 0444);
1228c2ecf20Sopenharmony_cimodule_param(poll_interval, int, 0444);
1238c2ecf20Sopenharmony_cimodule_param(cycle_time, int, 0444);
1248c2ecf20Sopenharmony_cimodule_param(has_dma, int, 0444);
1258c2ecf20Sopenharmony_cimodule_param(has_led, int, 0444);
1268c2ecf20Sopenharmony_cimodule_param(has_ring, int, 0444);
1278c2ecf20Sopenharmony_cimodule_param(dynamic_mode, int, 0444);
1288c2ecf20Sopenharmony_cimodule_param(freq_bypass, int, 0444);
1298c2ecf20Sopenharmony_cimodule_param(setup_time, int, 0444);
1308c2ecf20Sopenharmony_cimodule_param(cmd_time, int, 0444);
1318c2ecf20Sopenharmony_cimodule_param(recov_time, int, 0444);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci/*====================================================================*/
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistruct cirrus_state {
1368c2ecf20Sopenharmony_ci    u_char		misc1, misc2;
1378c2ecf20Sopenharmony_ci    u_char		timer[6];
1388c2ecf20Sopenharmony_ci};
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistruct vg46x_state {
1418c2ecf20Sopenharmony_ci    u_char		ctl, ema;
1428c2ecf20Sopenharmony_ci};
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistruct i82365_socket {
1458c2ecf20Sopenharmony_ci    u_short		type, flags;
1468c2ecf20Sopenharmony_ci    struct pcmcia_socket	socket;
1478c2ecf20Sopenharmony_ci    unsigned int	number;
1488c2ecf20Sopenharmony_ci    unsigned int	ioaddr;
1498c2ecf20Sopenharmony_ci    u_short		psock;
1508c2ecf20Sopenharmony_ci    u_char		cs_irq, intr;
1518c2ecf20Sopenharmony_ci    union {
1528c2ecf20Sopenharmony_ci	struct cirrus_state		cirrus;
1538c2ecf20Sopenharmony_ci	struct vg46x_state		vg46x;
1548c2ecf20Sopenharmony_ci    } state;
1558c2ecf20Sopenharmony_ci};
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci/* Where we keep track of our sockets... */
1588c2ecf20Sopenharmony_cistatic int sockets = 0;
1598c2ecf20Sopenharmony_cistatic struct i82365_socket socket[8] = {
1608c2ecf20Sopenharmony_ci    { 0, }, /* ... */
1618c2ecf20Sopenharmony_ci};
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci/* Default ISA interrupt mask */
1648c2ecf20Sopenharmony_ci#define I365_MASK	0xdeb8	/* irq 15,14,12,11,10,9,7,5,4,3 */
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic int grab_irq;
1678c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(isa_lock);
1688c2ecf20Sopenharmony_ci#define ISA_LOCK(n, f) spin_lock_irqsave(&isa_lock, f)
1698c2ecf20Sopenharmony_ci#define ISA_UNLOCK(n, f) spin_unlock_irqrestore(&isa_lock, f)
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic struct timer_list poll_timer;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci/*====================================================================*/
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci/* These definitions must match the pcic table! */
1768c2ecf20Sopenharmony_cienum pcic_id {
1778c2ecf20Sopenharmony_ci    IS_I82365A, IS_I82365B, IS_I82365DF,
1788c2ecf20Sopenharmony_ci    IS_IBM, IS_RF5Cx96, IS_VLSI, IS_VG468, IS_VG469,
1798c2ecf20Sopenharmony_ci    IS_PD6710, IS_PD672X, IS_VT83C469,
1808c2ecf20Sopenharmony_ci};
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci/* Flags for classifying groups of controllers */
1838c2ecf20Sopenharmony_ci#define IS_VADEM	0x0001
1848c2ecf20Sopenharmony_ci#define IS_CIRRUS	0x0002
1858c2ecf20Sopenharmony_ci#define IS_VIA		0x0010
1868c2ecf20Sopenharmony_ci#define IS_UNKNOWN	0x0400
1878c2ecf20Sopenharmony_ci#define IS_VG_PWR	0x0800
1888c2ecf20Sopenharmony_ci#define IS_DF_PWR	0x1000
1898c2ecf20Sopenharmony_ci#define IS_REGISTERED	0x2000
1908c2ecf20Sopenharmony_ci#define IS_ALIVE	0x8000
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistruct pcic {
1938c2ecf20Sopenharmony_ci    char		*name;
1948c2ecf20Sopenharmony_ci    u_short		flags;
1958c2ecf20Sopenharmony_ci};
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic struct pcic pcic[] = {
1988c2ecf20Sopenharmony_ci    { "Intel i82365sl A step", 0 },
1998c2ecf20Sopenharmony_ci    { "Intel i82365sl B step", 0 },
2008c2ecf20Sopenharmony_ci    { "Intel i82365sl DF", IS_DF_PWR },
2018c2ecf20Sopenharmony_ci    { "IBM Clone", 0 },
2028c2ecf20Sopenharmony_ci    { "Ricoh RF5C296/396", 0 },
2038c2ecf20Sopenharmony_ci    { "VLSI 82C146", 0 },
2048c2ecf20Sopenharmony_ci    { "Vadem VG-468", IS_VADEM },
2058c2ecf20Sopenharmony_ci    { "Vadem VG-469", IS_VADEM|IS_VG_PWR },
2068c2ecf20Sopenharmony_ci    { "Cirrus PD6710", IS_CIRRUS },
2078c2ecf20Sopenharmony_ci    { "Cirrus PD672x", IS_CIRRUS },
2088c2ecf20Sopenharmony_ci    { "VIA VT83C469", IS_CIRRUS|IS_VIA },
2098c2ecf20Sopenharmony_ci};
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci#define PCIC_COUNT	ARRAY_SIZE(pcic)
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci/*====================================================================*/
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(bus_lock);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic u_char i365_get(u_short sock, u_short reg)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci    unsigned long flags;
2208c2ecf20Sopenharmony_ci    spin_lock_irqsave(&bus_lock,flags);
2218c2ecf20Sopenharmony_ci    {
2228c2ecf20Sopenharmony_ci	unsigned int port = socket[sock].ioaddr;
2238c2ecf20Sopenharmony_ci	u_char val;
2248c2ecf20Sopenharmony_ci	reg = I365_REG(socket[sock].psock, reg);
2258c2ecf20Sopenharmony_ci	outb(reg, port); val = inb(port+1);
2268c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&bus_lock,flags);
2278c2ecf20Sopenharmony_ci	return val;
2288c2ecf20Sopenharmony_ci    }
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic void i365_set(u_short sock, u_short reg, u_char data)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci    unsigned long flags;
2348c2ecf20Sopenharmony_ci    spin_lock_irqsave(&bus_lock,flags);
2358c2ecf20Sopenharmony_ci    {
2368c2ecf20Sopenharmony_ci	unsigned int port = socket[sock].ioaddr;
2378c2ecf20Sopenharmony_ci	u_char val = I365_REG(socket[sock].psock, reg);
2388c2ecf20Sopenharmony_ci	outb(val, port); outb(data, port+1);
2398c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&bus_lock,flags);
2408c2ecf20Sopenharmony_ci    }
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic void i365_bset(u_short sock, u_short reg, u_char mask)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci    u_char d = i365_get(sock, reg);
2468c2ecf20Sopenharmony_ci    d |= mask;
2478c2ecf20Sopenharmony_ci    i365_set(sock, reg, d);
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic void i365_bclr(u_short sock, u_short reg, u_char mask)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci    u_char d = i365_get(sock, reg);
2538c2ecf20Sopenharmony_ci    d &= ~mask;
2548c2ecf20Sopenharmony_ci    i365_set(sock, reg, d);
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic void i365_bflip(u_short sock, u_short reg, u_char mask, int b)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci    u_char d = i365_get(sock, reg);
2608c2ecf20Sopenharmony_ci    if (b)
2618c2ecf20Sopenharmony_ci	d |= mask;
2628c2ecf20Sopenharmony_ci    else
2638c2ecf20Sopenharmony_ci	d &= ~mask;
2648c2ecf20Sopenharmony_ci    i365_set(sock, reg, d);
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic u_short i365_get_pair(u_short sock, u_short reg)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci    u_short a, b;
2708c2ecf20Sopenharmony_ci    a = i365_get(sock, reg);
2718c2ecf20Sopenharmony_ci    b = i365_get(sock, reg+1);
2728c2ecf20Sopenharmony_ci    return (a + (b<<8));
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cistatic void i365_set_pair(u_short sock, u_short reg, u_short data)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci    i365_set(sock, reg, data & 0xff);
2788c2ecf20Sopenharmony_ci    i365_set(sock, reg+1, data >> 8);
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci/*======================================================================
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci    Code to save and restore global state information for Cirrus
2848c2ecf20Sopenharmony_ci    PD67xx controllers, and to set and report global configuration
2858c2ecf20Sopenharmony_ci    options.
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci    The VIA controllers also use these routines, as they are mostly
2888c2ecf20Sopenharmony_ci    Cirrus lookalikes, without the timing registers.
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci======================================================================*/
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci#define flip(v,b,f) (v = ((f)<0) ? v : ((f) ? ((v)|(b)) : ((v)&(~b))))
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic void cirrus_get_state(u_short s)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci    int i;
2978c2ecf20Sopenharmony_ci    struct cirrus_state *p = &socket[s].state.cirrus;
2988c2ecf20Sopenharmony_ci    p->misc1 = i365_get(s, PD67_MISC_CTL_1);
2998c2ecf20Sopenharmony_ci    p->misc1 &= (PD67_MC1_MEDIA_ENA | PD67_MC1_INPACK_ENA);
3008c2ecf20Sopenharmony_ci    p->misc2 = i365_get(s, PD67_MISC_CTL_2);
3018c2ecf20Sopenharmony_ci    for (i = 0; i < 6; i++)
3028c2ecf20Sopenharmony_ci	p->timer[i] = i365_get(s, PD67_TIME_SETUP(0)+i);
3038c2ecf20Sopenharmony_ci}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cistatic void cirrus_set_state(u_short s)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci    int i;
3088c2ecf20Sopenharmony_ci    u_char misc;
3098c2ecf20Sopenharmony_ci    struct cirrus_state *p = &socket[s].state.cirrus;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci    misc = i365_get(s, PD67_MISC_CTL_2);
3128c2ecf20Sopenharmony_ci    i365_set(s, PD67_MISC_CTL_2, p->misc2);
3138c2ecf20Sopenharmony_ci    if (misc & PD67_MC2_SUSPEND) mdelay(50);
3148c2ecf20Sopenharmony_ci    misc = i365_get(s, PD67_MISC_CTL_1);
3158c2ecf20Sopenharmony_ci    misc &= ~(PD67_MC1_MEDIA_ENA | PD67_MC1_INPACK_ENA);
3168c2ecf20Sopenharmony_ci    i365_set(s, PD67_MISC_CTL_1, misc | p->misc1);
3178c2ecf20Sopenharmony_ci    for (i = 0; i < 6; i++)
3188c2ecf20Sopenharmony_ci	i365_set(s, PD67_TIME_SETUP(0)+i, p->timer[i]);
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic u_int __init cirrus_set_opts(u_short s, char *buf)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci    struct i82365_socket *t = &socket[s];
3248c2ecf20Sopenharmony_ci    struct cirrus_state *p = &socket[s].state.cirrus;
3258c2ecf20Sopenharmony_ci    u_int mask = 0xffff;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci    if (has_ring == -1) has_ring = 1;
3288c2ecf20Sopenharmony_ci    flip(p->misc2, PD67_MC2_IRQ15_RI, has_ring);
3298c2ecf20Sopenharmony_ci    flip(p->misc2, PD67_MC2_DYNAMIC_MODE, dynamic_mode);
3308c2ecf20Sopenharmony_ci    flip(p->misc2, PD67_MC2_FREQ_BYPASS, freq_bypass);
3318c2ecf20Sopenharmony_ci    if (p->misc2 & PD67_MC2_IRQ15_RI)
3328c2ecf20Sopenharmony_ci	strcat(buf, " [ring]");
3338c2ecf20Sopenharmony_ci    if (p->misc2 & PD67_MC2_DYNAMIC_MODE)
3348c2ecf20Sopenharmony_ci	strcat(buf, " [dyn mode]");
3358c2ecf20Sopenharmony_ci    if (p->misc2 & PD67_MC2_FREQ_BYPASS)
3368c2ecf20Sopenharmony_ci	strcat(buf, " [freq bypass]");
3378c2ecf20Sopenharmony_ci    if (p->misc1 & PD67_MC1_INPACK_ENA)
3388c2ecf20Sopenharmony_ci	strcat(buf, " [inpack]");
3398c2ecf20Sopenharmony_ci    if (p->misc2 & PD67_MC2_IRQ15_RI)
3408c2ecf20Sopenharmony_ci	mask &= ~0x8000;
3418c2ecf20Sopenharmony_ci    if (has_led > 0) {
3428c2ecf20Sopenharmony_ci	strcat(buf, " [led]");
3438c2ecf20Sopenharmony_ci	mask &= ~0x1000;
3448c2ecf20Sopenharmony_ci    }
3458c2ecf20Sopenharmony_ci    if (has_dma > 0) {
3468c2ecf20Sopenharmony_ci	strcat(buf, " [dma]");
3478c2ecf20Sopenharmony_ci	mask &= ~0x0600;
3488c2ecf20Sopenharmony_ci    }
3498c2ecf20Sopenharmony_ci    if (!(t->flags & IS_VIA)) {
3508c2ecf20Sopenharmony_ci	if (setup_time >= 0)
3518c2ecf20Sopenharmony_ci	    p->timer[0] = p->timer[3] = setup_time;
3528c2ecf20Sopenharmony_ci	if (cmd_time > 0) {
3538c2ecf20Sopenharmony_ci	    p->timer[1] = cmd_time;
3548c2ecf20Sopenharmony_ci	    p->timer[4] = cmd_time*2+4;
3558c2ecf20Sopenharmony_ci	}
3568c2ecf20Sopenharmony_ci	if (p->timer[1] == 0) {
3578c2ecf20Sopenharmony_ci	    p->timer[1] = 6; p->timer[4] = 16;
3588c2ecf20Sopenharmony_ci	    if (p->timer[0] == 0)
3598c2ecf20Sopenharmony_ci		p->timer[0] = p->timer[3] = 1;
3608c2ecf20Sopenharmony_ci	}
3618c2ecf20Sopenharmony_ci	if (recov_time >= 0)
3628c2ecf20Sopenharmony_ci	    p->timer[2] = p->timer[5] = recov_time;
3638c2ecf20Sopenharmony_ci	buf += strlen(buf);
3648c2ecf20Sopenharmony_ci	sprintf(buf, " [%d/%d/%d] [%d/%d/%d]", p->timer[0], p->timer[1],
3658c2ecf20Sopenharmony_ci		p->timer[2], p->timer[3], p->timer[4], p->timer[5]);
3668c2ecf20Sopenharmony_ci    }
3678c2ecf20Sopenharmony_ci    return mask;
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci/*======================================================================
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci    Code to save and restore global state information for Vadem VG468
3738c2ecf20Sopenharmony_ci    and VG469 controllers, and to set and report global configuration
3748c2ecf20Sopenharmony_ci    options.
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci======================================================================*/
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_cistatic void vg46x_get_state(u_short s)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci    struct vg46x_state *p = &socket[s].state.vg46x;
3818c2ecf20Sopenharmony_ci    p->ctl = i365_get(s, VG468_CTL);
3828c2ecf20Sopenharmony_ci    if (socket[s].type == IS_VG469)
3838c2ecf20Sopenharmony_ci	p->ema = i365_get(s, VG469_EXT_MODE);
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cistatic void vg46x_set_state(u_short s)
3878c2ecf20Sopenharmony_ci{
3888c2ecf20Sopenharmony_ci    struct vg46x_state *p = &socket[s].state.vg46x;
3898c2ecf20Sopenharmony_ci    i365_set(s, VG468_CTL, p->ctl);
3908c2ecf20Sopenharmony_ci    if (socket[s].type == IS_VG469)
3918c2ecf20Sopenharmony_ci	i365_set(s, VG469_EXT_MODE, p->ema);
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic u_int __init vg46x_set_opts(u_short s, char *buf)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci    struct vg46x_state *p = &socket[s].state.vg46x;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci    flip(p->ctl, VG468_CTL_ASYNC, async_clock);
3998c2ecf20Sopenharmony_ci    flip(p->ema, VG469_MODE_CABLE, cable_mode);
4008c2ecf20Sopenharmony_ci    if (p->ctl & VG468_CTL_ASYNC)
4018c2ecf20Sopenharmony_ci	strcat(buf, " [async]");
4028c2ecf20Sopenharmony_ci    if (p->ctl & VG468_CTL_INPACK)
4038c2ecf20Sopenharmony_ci	strcat(buf, " [inpack]");
4048c2ecf20Sopenharmony_ci    if (socket[s].type == IS_VG469) {
4058c2ecf20Sopenharmony_ci	u_char vsel = i365_get(s, VG469_VSELECT);
4068c2ecf20Sopenharmony_ci	if (vsel & VG469_VSEL_EXT_STAT) {
4078c2ecf20Sopenharmony_ci	    strcat(buf, " [ext mode]");
4088c2ecf20Sopenharmony_ci	    if (vsel & VG469_VSEL_EXT_BUS)
4098c2ecf20Sopenharmony_ci		strcat(buf, " [isa buf]");
4108c2ecf20Sopenharmony_ci	}
4118c2ecf20Sopenharmony_ci	if (p->ema & VG469_MODE_CABLE)
4128c2ecf20Sopenharmony_ci	    strcat(buf, " [cable]");
4138c2ecf20Sopenharmony_ci	if (p->ema & VG469_MODE_COMPAT)
4148c2ecf20Sopenharmony_ci	    strcat(buf, " [c step]");
4158c2ecf20Sopenharmony_ci    }
4168c2ecf20Sopenharmony_ci    return 0xffff;
4178c2ecf20Sopenharmony_ci}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci/*======================================================================
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci    Generic routines to get and set controller options
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci======================================================================*/
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cistatic void get_bridge_state(u_short s)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci    struct i82365_socket *t = &socket[s];
4288c2ecf20Sopenharmony_ci    if (t->flags & IS_CIRRUS)
4298c2ecf20Sopenharmony_ci	cirrus_get_state(s);
4308c2ecf20Sopenharmony_ci    else if (t->flags & IS_VADEM)
4318c2ecf20Sopenharmony_ci	vg46x_get_state(s);
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_cistatic void set_bridge_state(u_short s)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci    struct i82365_socket *t = &socket[s];
4378c2ecf20Sopenharmony_ci    if (t->flags & IS_CIRRUS)
4388c2ecf20Sopenharmony_ci	cirrus_set_state(s);
4398c2ecf20Sopenharmony_ci    else {
4408c2ecf20Sopenharmony_ci	i365_set(s, I365_GBLCTL, 0x00);
4418c2ecf20Sopenharmony_ci	i365_set(s, I365_GENCTL, 0x00);
4428c2ecf20Sopenharmony_ci    }
4438c2ecf20Sopenharmony_ci    i365_bflip(s, I365_INTCTL, I365_INTR_ENA, t->intr);
4448c2ecf20Sopenharmony_ci    if (t->flags & IS_VADEM)
4458c2ecf20Sopenharmony_ci	vg46x_set_state(s);
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_cistatic u_int __init set_bridge_opts(u_short s, u_short ns)
4498c2ecf20Sopenharmony_ci{
4508c2ecf20Sopenharmony_ci    u_short i;
4518c2ecf20Sopenharmony_ci    u_int m = 0xffff;
4528c2ecf20Sopenharmony_ci    char buf[128];
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci    for (i = s; i < s+ns; i++) {
4558c2ecf20Sopenharmony_ci	if (socket[i].flags & IS_ALIVE) {
4568c2ecf20Sopenharmony_ci	    printk(KERN_INFO "    host opts [%d]: already alive!\n", i);
4578c2ecf20Sopenharmony_ci	    continue;
4588c2ecf20Sopenharmony_ci	}
4598c2ecf20Sopenharmony_ci	buf[0] = '\0';
4608c2ecf20Sopenharmony_ci	get_bridge_state(i);
4618c2ecf20Sopenharmony_ci	if (socket[i].flags & IS_CIRRUS)
4628c2ecf20Sopenharmony_ci	    m = cirrus_set_opts(i, buf);
4638c2ecf20Sopenharmony_ci	else if (socket[i].flags & IS_VADEM)
4648c2ecf20Sopenharmony_ci	    m = vg46x_set_opts(i, buf);
4658c2ecf20Sopenharmony_ci	set_bridge_state(i);
4668c2ecf20Sopenharmony_ci	printk(KERN_INFO "    host opts [%d]:%s\n", i,
4678c2ecf20Sopenharmony_ci	       (*buf) ? buf : " none");
4688c2ecf20Sopenharmony_ci    }
4698c2ecf20Sopenharmony_ci    return m;
4708c2ecf20Sopenharmony_ci}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci/*======================================================================
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci    Interrupt testing code, for ISA and PCI interrupts
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci======================================================================*/
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_cistatic volatile u_int irq_hits;
4798c2ecf20Sopenharmony_cistatic u_short irq_sock;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_cistatic irqreturn_t i365_count_irq(int irq, void *dev)
4828c2ecf20Sopenharmony_ci{
4838c2ecf20Sopenharmony_ci    i365_get(irq_sock, I365_CSC);
4848c2ecf20Sopenharmony_ci    irq_hits++;
4858c2ecf20Sopenharmony_ci    pr_debug("i82365: -> hit on irq %d\n", irq);
4868c2ecf20Sopenharmony_ci    return IRQ_HANDLED;
4878c2ecf20Sopenharmony_ci}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_cistatic u_int __init test_irq(u_short sock, int irq)
4908c2ecf20Sopenharmony_ci{
4918c2ecf20Sopenharmony_ci    pr_debug("i82365:  testing ISA irq %d\n", irq);
4928c2ecf20Sopenharmony_ci    if (request_irq(irq, i365_count_irq, IRQF_PROBE_SHARED, "scan",
4938c2ecf20Sopenharmony_ci			i365_count_irq) != 0)
4948c2ecf20Sopenharmony_ci	return 1;
4958c2ecf20Sopenharmony_ci    irq_hits = 0; irq_sock = sock;
4968c2ecf20Sopenharmony_ci    msleep(10);
4978c2ecf20Sopenharmony_ci    if (irq_hits) {
4988c2ecf20Sopenharmony_ci	free_irq(irq, i365_count_irq);
4998c2ecf20Sopenharmony_ci	pr_debug("i82365:    spurious hit!\n");
5008c2ecf20Sopenharmony_ci	return 1;
5018c2ecf20Sopenharmony_ci    }
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci    /* Generate one interrupt */
5048c2ecf20Sopenharmony_ci    i365_set(sock, I365_CSCINT, I365_CSC_DETECT | (irq << 4));
5058c2ecf20Sopenharmony_ci    i365_bset(sock, I365_GENCTL, I365_CTL_SW_IRQ);
5068c2ecf20Sopenharmony_ci    udelay(1000);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci    free_irq(irq, i365_count_irq);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci    /* mask all interrupts */
5118c2ecf20Sopenharmony_ci    i365_set(sock, I365_CSCINT, 0);
5128c2ecf20Sopenharmony_ci    pr_debug("i82365:    hits = %d\n", irq_hits);
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci    return (irq_hits != 1);
5158c2ecf20Sopenharmony_ci}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_cistatic u_int __init isa_scan(u_short sock, u_int mask0)
5188c2ecf20Sopenharmony_ci{
5198c2ecf20Sopenharmony_ci    u_int mask1 = 0;
5208c2ecf20Sopenharmony_ci    int i;
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci#ifdef __alpha__
5238c2ecf20Sopenharmony_ci#define PIC 0x4d0
5248c2ecf20Sopenharmony_ci    /* Don't probe level-triggered interrupts -- reserved for PCI */
5258c2ecf20Sopenharmony_ci    mask0 &= ~(inb(PIC) | (inb(PIC+1) << 8));
5268c2ecf20Sopenharmony_ci#endif
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci    if (do_scan) {
5298c2ecf20Sopenharmony_ci	set_bridge_state(sock);
5308c2ecf20Sopenharmony_ci	i365_set(sock, I365_CSCINT, 0);
5318c2ecf20Sopenharmony_ci	for (i = 0; i < 16; i++)
5328c2ecf20Sopenharmony_ci	    if ((mask0 & (1 << i)) && (test_irq(sock, i) == 0))
5338c2ecf20Sopenharmony_ci		mask1 |= (1 << i);
5348c2ecf20Sopenharmony_ci	for (i = 0; i < 16; i++)
5358c2ecf20Sopenharmony_ci	    if ((mask1 & (1 << i)) && (test_irq(sock, i) != 0))
5368c2ecf20Sopenharmony_ci		mask1 ^= (1 << i);
5378c2ecf20Sopenharmony_ci    }
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci    printk(KERN_INFO "    ISA irqs (");
5408c2ecf20Sopenharmony_ci    if (mask1) {
5418c2ecf20Sopenharmony_ci	printk("scanned");
5428c2ecf20Sopenharmony_ci    } else {
5438c2ecf20Sopenharmony_ci	/* Fallback: just find interrupts that aren't in use */
5448c2ecf20Sopenharmony_ci	for (i = 0; i < 16; i++)
5458c2ecf20Sopenharmony_ci	    if ((mask0 & (1 << i)) && (_check_irq(i, IRQF_PROBE_SHARED) == 0))
5468c2ecf20Sopenharmony_ci		mask1 |= (1 << i);
5478c2ecf20Sopenharmony_ci	printk("default");
5488c2ecf20Sopenharmony_ci	/* If scan failed, default to polled status */
5498c2ecf20Sopenharmony_ci	if (!cs_irq && (poll_interval == 0)) poll_interval = HZ;
5508c2ecf20Sopenharmony_ci    }
5518c2ecf20Sopenharmony_ci    printk(") = ");
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci    for (i = 0; i < 16; i++)
5548c2ecf20Sopenharmony_ci	if (mask1 & (1<<i))
5558c2ecf20Sopenharmony_ci	    printk("%s%d", ((mask1 & ((1<<i)-1)) ? "," : ""), i);
5568c2ecf20Sopenharmony_ci    if (mask1 == 0) printk("none!");
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci    return mask1;
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci/*====================================================================*/
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci/* Time conversion functions */
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_cistatic int to_cycles(int ns)
5668c2ecf20Sopenharmony_ci{
5678c2ecf20Sopenharmony_ci    return ns/cycle_time;
5688c2ecf20Sopenharmony_ci}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci/*====================================================================*/
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_cistatic int __init identify(unsigned int port, u_short sock)
5738c2ecf20Sopenharmony_ci{
5748c2ecf20Sopenharmony_ci    u_char val;
5758c2ecf20Sopenharmony_ci    int type = -1;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci    /* Use the next free entry in the socket table */
5788c2ecf20Sopenharmony_ci    socket[sockets].ioaddr = port;
5798c2ecf20Sopenharmony_ci    socket[sockets].psock = sock;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci    /* Wake up a sleepy Cirrus controller */
5828c2ecf20Sopenharmony_ci    if (wakeup) {
5838c2ecf20Sopenharmony_ci	i365_bclr(sockets, PD67_MISC_CTL_2, PD67_MC2_SUSPEND);
5848c2ecf20Sopenharmony_ci	/* Pause at least 50 ms */
5858c2ecf20Sopenharmony_ci	mdelay(50);
5868c2ecf20Sopenharmony_ci    }
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci    if ((val = i365_get(sockets, I365_IDENT)) & 0x70)
5898c2ecf20Sopenharmony_ci	return -1;
5908c2ecf20Sopenharmony_ci    switch (val) {
5918c2ecf20Sopenharmony_ci    case 0x82:
5928c2ecf20Sopenharmony_ci	type = IS_I82365A; break;
5938c2ecf20Sopenharmony_ci    case 0x83:
5948c2ecf20Sopenharmony_ci	type = IS_I82365B; break;
5958c2ecf20Sopenharmony_ci    case 0x84:
5968c2ecf20Sopenharmony_ci	type = IS_I82365DF; break;
5978c2ecf20Sopenharmony_ci    case 0x88: case 0x89: case 0x8a:
5988c2ecf20Sopenharmony_ci	type = IS_IBM; break;
5998c2ecf20Sopenharmony_ci    }
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci    /* Check for Vadem VG-468 chips */
6028c2ecf20Sopenharmony_ci    outb(0x0e, port);
6038c2ecf20Sopenharmony_ci    outb(0x37, port);
6048c2ecf20Sopenharmony_ci    i365_bset(sockets, VG468_MISC, VG468_MISC_VADEMREV);
6058c2ecf20Sopenharmony_ci    val = i365_get(sockets, I365_IDENT);
6068c2ecf20Sopenharmony_ci    if (val & I365_IDENT_VADEM) {
6078c2ecf20Sopenharmony_ci	i365_bclr(sockets, VG468_MISC, VG468_MISC_VADEMREV);
6088c2ecf20Sopenharmony_ci	type = ((val & 7) >= 4) ? IS_VG469 : IS_VG468;
6098c2ecf20Sopenharmony_ci    }
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci    /* Check for Ricoh chips */
6128c2ecf20Sopenharmony_ci    val = i365_get(sockets, RF5C_CHIP_ID);
6138c2ecf20Sopenharmony_ci    if ((val == RF5C_CHIP_RF5C296) || (val == RF5C_CHIP_RF5C396))
6148c2ecf20Sopenharmony_ci	type = IS_RF5Cx96;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci    /* Check for Cirrus CL-PD67xx chips */
6178c2ecf20Sopenharmony_ci    i365_set(sockets, PD67_CHIP_INFO, 0);
6188c2ecf20Sopenharmony_ci    val = i365_get(sockets, PD67_CHIP_INFO);
6198c2ecf20Sopenharmony_ci    if ((val & PD67_INFO_CHIP_ID) == PD67_INFO_CHIP_ID) {
6208c2ecf20Sopenharmony_ci	val = i365_get(sockets, PD67_CHIP_INFO);
6218c2ecf20Sopenharmony_ci	if ((val & PD67_INFO_CHIP_ID) == 0) {
6228c2ecf20Sopenharmony_ci	    type = (val & PD67_INFO_SLOTS) ? IS_PD672X : IS_PD6710;
6238c2ecf20Sopenharmony_ci	    i365_set(sockets, PD67_EXT_INDEX, 0xe5);
6248c2ecf20Sopenharmony_ci	    if (i365_get(sockets, PD67_EXT_INDEX) != 0xe5)
6258c2ecf20Sopenharmony_ci		type = IS_VT83C469;
6268c2ecf20Sopenharmony_ci	}
6278c2ecf20Sopenharmony_ci    }
6288c2ecf20Sopenharmony_ci    return type;
6298c2ecf20Sopenharmony_ci} /* identify */
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci/*======================================================================
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci    See if a card is present, powered up, in IO mode, and already
6348c2ecf20Sopenharmony_ci    bound to a (non PC Card) Linux driver.  We leave these alone.
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci    We make an exception for cards that seem to be serial devices.
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci======================================================================*/
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_cistatic int __init is_alive(u_short sock)
6418c2ecf20Sopenharmony_ci{
6428c2ecf20Sopenharmony_ci    u_char stat;
6438c2ecf20Sopenharmony_ci    unsigned int start, stop;
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci    stat = i365_get(sock, I365_STATUS);
6468c2ecf20Sopenharmony_ci    start = i365_get_pair(sock, I365_IO(0)+I365_W_START);
6478c2ecf20Sopenharmony_ci    stop = i365_get_pair(sock, I365_IO(0)+I365_W_STOP);
6488c2ecf20Sopenharmony_ci    if ((stat & I365_CS_DETECT) && (stat & I365_CS_POWERON) &&
6498c2ecf20Sopenharmony_ci	(i365_get(sock, I365_INTCTL) & I365_PC_IOCARD) &&
6508c2ecf20Sopenharmony_ci	(i365_get(sock, I365_ADDRWIN) & I365_ENA_IO(0)) &&
6518c2ecf20Sopenharmony_ci	((start & 0xfeef) != 0x02e8)) {
6528c2ecf20Sopenharmony_ci	if (!request_region(start, stop-start+1, "i82365"))
6538c2ecf20Sopenharmony_ci	    return 1;
6548c2ecf20Sopenharmony_ci	release_region(start, stop-start+1);
6558c2ecf20Sopenharmony_ci    }
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci    return 0;
6588c2ecf20Sopenharmony_ci}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci/*====================================================================*/
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_cistatic void __init add_socket(unsigned int port, int psock, int type)
6638c2ecf20Sopenharmony_ci{
6648c2ecf20Sopenharmony_ci    socket[sockets].ioaddr = port;
6658c2ecf20Sopenharmony_ci    socket[sockets].psock = psock;
6668c2ecf20Sopenharmony_ci    socket[sockets].type = type;
6678c2ecf20Sopenharmony_ci    socket[sockets].flags = pcic[type].flags;
6688c2ecf20Sopenharmony_ci    if (is_alive(sockets))
6698c2ecf20Sopenharmony_ci	socket[sockets].flags |= IS_ALIVE;
6708c2ecf20Sopenharmony_ci    sockets++;
6718c2ecf20Sopenharmony_ci}
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_cistatic void __init add_pcic(int ns, int type)
6748c2ecf20Sopenharmony_ci{
6758c2ecf20Sopenharmony_ci    u_int mask = 0, i, base;
6768c2ecf20Sopenharmony_ci    int isa_irq = 0;
6778c2ecf20Sopenharmony_ci    struct i82365_socket *t = &socket[sockets-ns];
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci    base = sockets-ns;
6808c2ecf20Sopenharmony_ci    if (base == 0) printk("\n");
6818c2ecf20Sopenharmony_ci    printk(KERN_INFO "  %s", pcic[type].name);
6828c2ecf20Sopenharmony_ci    printk(" ISA-to-PCMCIA at port %#x ofs 0x%02x",
6838c2ecf20Sopenharmony_ci	       t->ioaddr, t->psock*0x40);
6848c2ecf20Sopenharmony_ci    printk(", %d socket%s\n", ns, ((ns > 1) ? "s" : ""));
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci    /* Set host options, build basic interrupt mask */
6878c2ecf20Sopenharmony_ci    if (irq_list_count == 0)
6888c2ecf20Sopenharmony_ci	mask = irq_mask;
6898c2ecf20Sopenharmony_ci    else
6908c2ecf20Sopenharmony_ci	for (i = mask = 0; i < irq_list_count; i++)
6918c2ecf20Sopenharmony_ci	    mask |= (1<<irq_list[i]);
6928c2ecf20Sopenharmony_ci    mask &= I365_MASK & set_bridge_opts(base, ns);
6938c2ecf20Sopenharmony_ci    /* Scan for ISA interrupts */
6948c2ecf20Sopenharmony_ci    mask = isa_scan(base, mask);
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci    /* Poll if only two interrupts available */
6978c2ecf20Sopenharmony_ci    if (!poll_interval) {
6988c2ecf20Sopenharmony_ci	u_int tmp = (mask & 0xff20);
6998c2ecf20Sopenharmony_ci	tmp = tmp & (tmp-1);
7008c2ecf20Sopenharmony_ci	if ((tmp & (tmp-1)) == 0)
7018c2ecf20Sopenharmony_ci	    poll_interval = HZ;
7028c2ecf20Sopenharmony_ci    }
7038c2ecf20Sopenharmony_ci    /* Only try an ISA cs_irq if this is the first controller */
7048c2ecf20Sopenharmony_ci    if (!grab_irq && (cs_irq || !poll_interval)) {
7058c2ecf20Sopenharmony_ci	/* Avoid irq 12 unless it is explicitly requested */
7068c2ecf20Sopenharmony_ci	u_int cs_mask = mask & ((cs_irq) ? (1<<cs_irq) : ~(1<<12));
7078c2ecf20Sopenharmony_ci	for (cs_irq = 15; cs_irq > 0; cs_irq--)
7088c2ecf20Sopenharmony_ci	    if ((cs_mask & (1 << cs_irq)) &&
7098c2ecf20Sopenharmony_ci		(_check_irq(cs_irq, IRQF_PROBE_SHARED) == 0))
7108c2ecf20Sopenharmony_ci		break;
7118c2ecf20Sopenharmony_ci	if (cs_irq) {
7128c2ecf20Sopenharmony_ci	    grab_irq = 1;
7138c2ecf20Sopenharmony_ci	    isa_irq = cs_irq;
7148c2ecf20Sopenharmony_ci	    printk(" status change on irq %d\n", cs_irq);
7158c2ecf20Sopenharmony_ci	}
7168c2ecf20Sopenharmony_ci    }
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci    if (!isa_irq) {
7198c2ecf20Sopenharmony_ci	if (poll_interval == 0)
7208c2ecf20Sopenharmony_ci	    poll_interval = HZ;
7218c2ecf20Sopenharmony_ci	printk(" polling interval = %d ms\n",
7228c2ecf20Sopenharmony_ci	       poll_interval * 1000 / HZ);
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci    }
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci    /* Update socket interrupt information, capabilities */
7278c2ecf20Sopenharmony_ci    for (i = 0; i < ns; i++) {
7288c2ecf20Sopenharmony_ci	t[i].socket.features |= SS_CAP_PCCARD;
7298c2ecf20Sopenharmony_ci	t[i].socket.map_size = 0x1000;
7308c2ecf20Sopenharmony_ci	t[i].socket.irq_mask = mask;
7318c2ecf20Sopenharmony_ci	t[i].cs_irq = isa_irq;
7328c2ecf20Sopenharmony_ci    }
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci} /* add_pcic */
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci/*====================================================================*/
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP
7398c2ecf20Sopenharmony_cistatic struct isapnp_device_id id_table[] __initdata = {
7408c2ecf20Sopenharmony_ci	{ 	ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('P', 'N', 'P'),
7418c2ecf20Sopenharmony_ci		ISAPNP_FUNCTION(0x0e00), (unsigned long) "Intel 82365-Compatible" },
7428c2ecf20Sopenharmony_ci	{ 	ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('P', 'N', 'P'),
7438c2ecf20Sopenharmony_ci		ISAPNP_FUNCTION(0x0e01), (unsigned long) "Cirrus Logic CL-PD6720" },
7448c2ecf20Sopenharmony_ci	{ 	ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('P', 'N', 'P'),
7458c2ecf20Sopenharmony_ci		ISAPNP_FUNCTION(0x0e02), (unsigned long) "VLSI VL82C146" },
7468c2ecf20Sopenharmony_ci	{	0 }
7478c2ecf20Sopenharmony_ci};
7488c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(isapnp, id_table);
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_cistatic struct pnp_dev *i82365_pnpdev;
7518c2ecf20Sopenharmony_ci#endif
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_cistatic void __init isa_probe(void)
7548c2ecf20Sopenharmony_ci{
7558c2ecf20Sopenharmony_ci    int i, j, sock, k, ns, id;
7568c2ecf20Sopenharmony_ci    unsigned int port;
7578c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP
7588c2ecf20Sopenharmony_ci    struct isapnp_device_id *devid;
7598c2ecf20Sopenharmony_ci    struct pnp_dev *dev;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci    for (devid = id_table; devid->vendor; devid++) {
7628c2ecf20Sopenharmony_ci	if ((dev = pnp_find_dev(NULL, devid->vendor, devid->function, NULL))) {
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	    if (pnp_device_attach(dev) < 0)
7658c2ecf20Sopenharmony_ci	    	continue;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	    if (pnp_activate_dev(dev) < 0) {
7688c2ecf20Sopenharmony_ci		printk("activate failed\n");
7698c2ecf20Sopenharmony_ci		pnp_device_detach(dev);
7708c2ecf20Sopenharmony_ci		break;
7718c2ecf20Sopenharmony_ci	    }
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	    if (!pnp_port_valid(dev, 0)) {
7748c2ecf20Sopenharmony_ci		printk("invalid resources ?\n");
7758c2ecf20Sopenharmony_ci		pnp_device_detach(dev);
7768c2ecf20Sopenharmony_ci		break;
7778c2ecf20Sopenharmony_ci	    }
7788c2ecf20Sopenharmony_ci	    i365_base = pnp_port_start(dev, 0);
7798c2ecf20Sopenharmony_ci	    i82365_pnpdev = dev;
7808c2ecf20Sopenharmony_ci	    break;
7818c2ecf20Sopenharmony_ci	}
7828c2ecf20Sopenharmony_ci    }
7838c2ecf20Sopenharmony_ci#endif
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci    if (!request_region(i365_base, 2, "i82365")) {
7868c2ecf20Sopenharmony_ci	if (sockets == 0)
7878c2ecf20Sopenharmony_ci	    printk("port conflict at %#lx\n", i365_base);
7888c2ecf20Sopenharmony_ci	return;
7898c2ecf20Sopenharmony_ci    }
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci    id = identify(i365_base, 0);
7928c2ecf20Sopenharmony_ci    if ((id == IS_I82365DF) && (identify(i365_base, 1) != id)) {
7938c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++) {
7948c2ecf20Sopenharmony_ci	    if (i == ignore) continue;
7958c2ecf20Sopenharmony_ci	    port = i365_base + ((i & 1) << 2) + ((i & 2) << 1);
7968c2ecf20Sopenharmony_ci	    sock = (i & 1) << 1;
7978c2ecf20Sopenharmony_ci	    if (identify(port, sock) == IS_I82365DF) {
7988c2ecf20Sopenharmony_ci		add_socket(port, sock, IS_VLSI);
7998c2ecf20Sopenharmony_ci		add_pcic(1, IS_VLSI);
8008c2ecf20Sopenharmony_ci	    }
8018c2ecf20Sopenharmony_ci	}
8028c2ecf20Sopenharmony_ci    } else {
8038c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i += 2) {
8048c2ecf20Sopenharmony_ci	    if (sockets && !extra_sockets && (i == 4))
8058c2ecf20Sopenharmony_ci		break;
8068c2ecf20Sopenharmony_ci	    port = i365_base + 2*(i>>2);
8078c2ecf20Sopenharmony_ci	    sock = (i & 3);
8088c2ecf20Sopenharmony_ci	    id = identify(port, sock);
8098c2ecf20Sopenharmony_ci	    if (id < 0) continue;
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	    for (j = ns = 0; j < 2; j++) {
8128c2ecf20Sopenharmony_ci		/* Does the socket exist? */
8138c2ecf20Sopenharmony_ci		if ((ignore == i+j) || (identify(port, sock+j) < 0))
8148c2ecf20Sopenharmony_ci		    continue;
8158c2ecf20Sopenharmony_ci		/* Check for bad socket decode */
8168c2ecf20Sopenharmony_ci		for (k = 0; k <= sockets; k++)
8178c2ecf20Sopenharmony_ci		    i365_set(k, I365_MEM(0)+I365_W_OFF, k);
8188c2ecf20Sopenharmony_ci		for (k = 0; k <= sockets; k++)
8198c2ecf20Sopenharmony_ci		    if (i365_get(k, I365_MEM(0)+I365_W_OFF) != k)
8208c2ecf20Sopenharmony_ci			break;
8218c2ecf20Sopenharmony_ci		if (k <= sockets) break;
8228c2ecf20Sopenharmony_ci		add_socket(port, sock+j, id); ns++;
8238c2ecf20Sopenharmony_ci	    }
8248c2ecf20Sopenharmony_ci	    if (ns != 0) add_pcic(ns, id);
8258c2ecf20Sopenharmony_ci	}
8268c2ecf20Sopenharmony_ci    }
8278c2ecf20Sopenharmony_ci}
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci/*====================================================================*/
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_cistatic irqreturn_t pcic_interrupt(int irq, void *dev)
8328c2ecf20Sopenharmony_ci{
8338c2ecf20Sopenharmony_ci    int i, j, csc;
8348c2ecf20Sopenharmony_ci    u_int events, active;
8358c2ecf20Sopenharmony_ci    u_long flags = 0;
8368c2ecf20Sopenharmony_ci    int handled = 0;
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci    pr_debug("pcic_interrupt(%d)\n", irq);
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci    for (j = 0; j < 20; j++) {
8418c2ecf20Sopenharmony_ci	active = 0;
8428c2ecf20Sopenharmony_ci	for (i = 0; i < sockets; i++) {
8438c2ecf20Sopenharmony_ci	    if (socket[i].cs_irq != irq)
8448c2ecf20Sopenharmony_ci		continue;
8458c2ecf20Sopenharmony_ci	    handled = 1;
8468c2ecf20Sopenharmony_ci	    ISA_LOCK(i, flags);
8478c2ecf20Sopenharmony_ci	    csc = i365_get(i, I365_CSC);
8488c2ecf20Sopenharmony_ci	    if ((csc == 0) || (i365_get(i, I365_IDENT) & 0x70)) {
8498c2ecf20Sopenharmony_ci		ISA_UNLOCK(i, flags);
8508c2ecf20Sopenharmony_ci		continue;
8518c2ecf20Sopenharmony_ci	    }
8528c2ecf20Sopenharmony_ci	    events = (csc & I365_CSC_DETECT) ? SS_DETECT : 0;
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	    if (i365_get(i, I365_INTCTL) & I365_PC_IOCARD)
8558c2ecf20Sopenharmony_ci		events |= (csc & I365_CSC_STSCHG) ? SS_STSCHG : 0;
8568c2ecf20Sopenharmony_ci	    else {
8578c2ecf20Sopenharmony_ci		events |= (csc & I365_CSC_BVD1) ? SS_BATDEAD : 0;
8588c2ecf20Sopenharmony_ci		events |= (csc & I365_CSC_BVD2) ? SS_BATWARN : 0;
8598c2ecf20Sopenharmony_ci		events |= (csc & I365_CSC_READY) ? SS_READY : 0;
8608c2ecf20Sopenharmony_ci	    }
8618c2ecf20Sopenharmony_ci	    ISA_UNLOCK(i, flags);
8628c2ecf20Sopenharmony_ci	    pr_debug("socket %d event 0x%02x\n", i, events);
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	    if (events)
8658c2ecf20Sopenharmony_ci		pcmcia_parse_events(&socket[i].socket, events);
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	    active |= events;
8688c2ecf20Sopenharmony_ci	}
8698c2ecf20Sopenharmony_ci	if (!active) break;
8708c2ecf20Sopenharmony_ci    }
8718c2ecf20Sopenharmony_ci    if (j == 20)
8728c2ecf20Sopenharmony_ci	printk(KERN_NOTICE "i82365: infinite loop in interrupt handler\n");
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci    pr_debug("pcic_interrupt done\n");
8758c2ecf20Sopenharmony_ci    return IRQ_RETVAL(handled);
8768c2ecf20Sopenharmony_ci} /* pcic_interrupt */
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_cistatic void pcic_interrupt_wrapper(struct timer_list *unused)
8798c2ecf20Sopenharmony_ci{
8808c2ecf20Sopenharmony_ci    pcic_interrupt(0, NULL);
8818c2ecf20Sopenharmony_ci    poll_timer.expires = jiffies + poll_interval;
8828c2ecf20Sopenharmony_ci    add_timer(&poll_timer);
8838c2ecf20Sopenharmony_ci}
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci/*====================================================================*/
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_cistatic int i365_get_status(u_short sock, u_int *value)
8888c2ecf20Sopenharmony_ci{
8898c2ecf20Sopenharmony_ci    u_int status;
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci    status = i365_get(sock, I365_STATUS);
8928c2ecf20Sopenharmony_ci    *value = ((status & I365_CS_DETECT) == I365_CS_DETECT)
8938c2ecf20Sopenharmony_ci	? SS_DETECT : 0;
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci    if (i365_get(sock, I365_INTCTL) & I365_PC_IOCARD)
8968c2ecf20Sopenharmony_ci	*value |= (status & I365_CS_STSCHG) ? 0 : SS_STSCHG;
8978c2ecf20Sopenharmony_ci    else {
8988c2ecf20Sopenharmony_ci	*value |= (status & I365_CS_BVD1) ? 0 : SS_BATDEAD;
8998c2ecf20Sopenharmony_ci	*value |= (status & I365_CS_BVD2) ? 0 : SS_BATWARN;
9008c2ecf20Sopenharmony_ci    }
9018c2ecf20Sopenharmony_ci    *value |= (status & I365_CS_WRPROT) ? SS_WRPROT : 0;
9028c2ecf20Sopenharmony_ci    *value |= (status & I365_CS_READY) ? SS_READY : 0;
9038c2ecf20Sopenharmony_ci    *value |= (status & I365_CS_POWERON) ? SS_POWERON : 0;
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci    if (socket[sock].type == IS_VG469) {
9068c2ecf20Sopenharmony_ci	status = i365_get(sock, VG469_VSENSE);
9078c2ecf20Sopenharmony_ci	if (socket[sock].psock & 1) {
9088c2ecf20Sopenharmony_ci	    *value |= (status & VG469_VSENSE_B_VS1) ? 0 : SS_3VCARD;
9098c2ecf20Sopenharmony_ci	    *value |= (status & VG469_VSENSE_B_VS2) ? 0 : SS_XVCARD;
9108c2ecf20Sopenharmony_ci	} else {
9118c2ecf20Sopenharmony_ci	    *value |= (status & VG469_VSENSE_A_VS1) ? 0 : SS_3VCARD;
9128c2ecf20Sopenharmony_ci	    *value |= (status & VG469_VSENSE_A_VS2) ? 0 : SS_XVCARD;
9138c2ecf20Sopenharmony_ci	}
9148c2ecf20Sopenharmony_ci    }
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci    pr_debug("GetStatus(%d) = %#4.4x\n", sock, *value);
9178c2ecf20Sopenharmony_ci    return 0;
9188c2ecf20Sopenharmony_ci} /* i365_get_status */
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci/*====================================================================*/
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_cistatic int i365_set_socket(u_short sock, socket_state_t *state)
9238c2ecf20Sopenharmony_ci{
9248c2ecf20Sopenharmony_ci    struct i82365_socket *t = &socket[sock];
9258c2ecf20Sopenharmony_ci    u_char reg;
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci    pr_debug("SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
9288c2ecf20Sopenharmony_ci	  "io_irq %d, csc_mask %#2.2x)\n", sock, state->flags,
9298c2ecf20Sopenharmony_ci	  state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci    /* First set global controller options */
9328c2ecf20Sopenharmony_ci    set_bridge_state(sock);
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci    /* IO card, RESET flag, IO interrupt */
9358c2ecf20Sopenharmony_ci    reg = t->intr;
9368c2ecf20Sopenharmony_ci    reg |= state->io_irq;
9378c2ecf20Sopenharmony_ci    reg |= (state->flags & SS_RESET) ? 0 : I365_PC_RESET;
9388c2ecf20Sopenharmony_ci    reg |= (state->flags & SS_IOCARD) ? I365_PC_IOCARD : 0;
9398c2ecf20Sopenharmony_ci    i365_set(sock, I365_INTCTL, reg);
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci    reg = I365_PWR_NORESET;
9428c2ecf20Sopenharmony_ci    if (state->flags & SS_PWR_AUTO) reg |= I365_PWR_AUTO;
9438c2ecf20Sopenharmony_ci    if (state->flags & SS_OUTPUT_ENA) reg |= I365_PWR_OUT;
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci    if (t->flags & IS_CIRRUS) {
9468c2ecf20Sopenharmony_ci	if (state->Vpp != 0) {
9478c2ecf20Sopenharmony_ci	    if (state->Vpp == 120)
9488c2ecf20Sopenharmony_ci		reg |= I365_VPP1_12V;
9498c2ecf20Sopenharmony_ci	    else if (state->Vpp == state->Vcc)
9508c2ecf20Sopenharmony_ci		reg |= I365_VPP1_5V;
9518c2ecf20Sopenharmony_ci	    else return -EINVAL;
9528c2ecf20Sopenharmony_ci	}
9538c2ecf20Sopenharmony_ci	if (state->Vcc != 0) {
9548c2ecf20Sopenharmony_ci	    reg |= I365_VCC_5V;
9558c2ecf20Sopenharmony_ci	    if (state->Vcc == 33)
9568c2ecf20Sopenharmony_ci		i365_bset(sock, PD67_MISC_CTL_1, PD67_MC1_VCC_3V);
9578c2ecf20Sopenharmony_ci	    else if (state->Vcc == 50)
9588c2ecf20Sopenharmony_ci		i365_bclr(sock, PD67_MISC_CTL_1, PD67_MC1_VCC_3V);
9598c2ecf20Sopenharmony_ci	    else return -EINVAL;
9608c2ecf20Sopenharmony_ci	}
9618c2ecf20Sopenharmony_ci    } else if (t->flags & IS_VG_PWR) {
9628c2ecf20Sopenharmony_ci	if (state->Vpp != 0) {
9638c2ecf20Sopenharmony_ci	    if (state->Vpp == 120)
9648c2ecf20Sopenharmony_ci		reg |= I365_VPP1_12V;
9658c2ecf20Sopenharmony_ci	    else if (state->Vpp == state->Vcc)
9668c2ecf20Sopenharmony_ci		reg |= I365_VPP1_5V;
9678c2ecf20Sopenharmony_ci	    else return -EINVAL;
9688c2ecf20Sopenharmony_ci	}
9698c2ecf20Sopenharmony_ci	if (state->Vcc != 0) {
9708c2ecf20Sopenharmony_ci	    reg |= I365_VCC_5V;
9718c2ecf20Sopenharmony_ci	    if (state->Vcc == 33)
9728c2ecf20Sopenharmony_ci		i365_bset(sock, VG469_VSELECT, VG469_VSEL_VCC);
9738c2ecf20Sopenharmony_ci	    else if (state->Vcc == 50)
9748c2ecf20Sopenharmony_ci		i365_bclr(sock, VG469_VSELECT, VG469_VSEL_VCC);
9758c2ecf20Sopenharmony_ci	    else return -EINVAL;
9768c2ecf20Sopenharmony_ci	}
9778c2ecf20Sopenharmony_ci    } else if (t->flags & IS_DF_PWR) {
9788c2ecf20Sopenharmony_ci	switch (state->Vcc) {
9798c2ecf20Sopenharmony_ci	case 0:		break;
9808c2ecf20Sopenharmony_ci	case 33:   	reg |= I365_VCC_3V; break;
9818c2ecf20Sopenharmony_ci	case 50:	reg |= I365_VCC_5V; break;
9828c2ecf20Sopenharmony_ci	default:	return -EINVAL;
9838c2ecf20Sopenharmony_ci	}
9848c2ecf20Sopenharmony_ci	switch (state->Vpp) {
9858c2ecf20Sopenharmony_ci	case 0:		break;
9868c2ecf20Sopenharmony_ci	case 50:   	reg |= I365_VPP1_5V; break;
9878c2ecf20Sopenharmony_ci	case 120:	reg |= I365_VPP1_12V; break;
9888c2ecf20Sopenharmony_ci	default:	return -EINVAL;
9898c2ecf20Sopenharmony_ci	}
9908c2ecf20Sopenharmony_ci    } else {
9918c2ecf20Sopenharmony_ci	switch (state->Vcc) {
9928c2ecf20Sopenharmony_ci	case 0:		break;
9938c2ecf20Sopenharmony_ci	case 50:	reg |= I365_VCC_5V; break;
9948c2ecf20Sopenharmony_ci	default:	return -EINVAL;
9958c2ecf20Sopenharmony_ci	}
9968c2ecf20Sopenharmony_ci	switch (state->Vpp) {
9978c2ecf20Sopenharmony_ci	case 0:		break;
9988c2ecf20Sopenharmony_ci	case 50:	reg |= I365_VPP1_5V | I365_VPP2_5V; break;
9998c2ecf20Sopenharmony_ci	case 120:	reg |= I365_VPP1_12V | I365_VPP2_12V; break;
10008c2ecf20Sopenharmony_ci	default:	return -EINVAL;
10018c2ecf20Sopenharmony_ci	}
10028c2ecf20Sopenharmony_ci    }
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci    if (reg != i365_get(sock, I365_POWER))
10058c2ecf20Sopenharmony_ci	i365_set(sock, I365_POWER, reg);
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci    /* Chipset-specific functions */
10088c2ecf20Sopenharmony_ci    if (t->flags & IS_CIRRUS) {
10098c2ecf20Sopenharmony_ci	/* Speaker control */
10108c2ecf20Sopenharmony_ci	i365_bflip(sock, PD67_MISC_CTL_1, PD67_MC1_SPKR_ENA,
10118c2ecf20Sopenharmony_ci		   state->flags & SS_SPKR_ENA);
10128c2ecf20Sopenharmony_ci    }
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci    /* Card status change interrupt mask */
10158c2ecf20Sopenharmony_ci    reg = t->cs_irq << 4;
10168c2ecf20Sopenharmony_ci    if (state->csc_mask & SS_DETECT) reg |= I365_CSC_DETECT;
10178c2ecf20Sopenharmony_ci    if (state->flags & SS_IOCARD) {
10188c2ecf20Sopenharmony_ci	if (state->csc_mask & SS_STSCHG) reg |= I365_CSC_STSCHG;
10198c2ecf20Sopenharmony_ci    } else {
10208c2ecf20Sopenharmony_ci	if (state->csc_mask & SS_BATDEAD) reg |= I365_CSC_BVD1;
10218c2ecf20Sopenharmony_ci	if (state->csc_mask & SS_BATWARN) reg |= I365_CSC_BVD2;
10228c2ecf20Sopenharmony_ci	if (state->csc_mask & SS_READY) reg |= I365_CSC_READY;
10238c2ecf20Sopenharmony_ci    }
10248c2ecf20Sopenharmony_ci    i365_set(sock, I365_CSCINT, reg);
10258c2ecf20Sopenharmony_ci    i365_get(sock, I365_CSC);
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci    return 0;
10288c2ecf20Sopenharmony_ci} /* i365_set_socket */
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci/*====================================================================*/
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_cistatic int i365_set_io_map(u_short sock, struct pccard_io_map *io)
10338c2ecf20Sopenharmony_ci{
10348c2ecf20Sopenharmony_ci    u_char map, ioctl;
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci    pr_debug("SetIOMap(%d, %d, %#2.2x, %d ns, "
10378c2ecf20Sopenharmony_ci	  "%#llx-%#llx)\n", sock, io->map, io->flags, io->speed,
10388c2ecf20Sopenharmony_ci	  (unsigned long long)io->start, (unsigned long long)io->stop);
10398c2ecf20Sopenharmony_ci    map = io->map;
10408c2ecf20Sopenharmony_ci    if ((map > 1) || (io->start > 0xffff) || (io->stop > 0xffff) ||
10418c2ecf20Sopenharmony_ci	(io->stop < io->start)) return -EINVAL;
10428c2ecf20Sopenharmony_ci    /* Turn off the window before changing anything */
10438c2ecf20Sopenharmony_ci    if (i365_get(sock, I365_ADDRWIN) & I365_ENA_IO(map))
10448c2ecf20Sopenharmony_ci	i365_bclr(sock, I365_ADDRWIN, I365_ENA_IO(map));
10458c2ecf20Sopenharmony_ci    i365_set_pair(sock, I365_IO(map)+I365_W_START, io->start);
10468c2ecf20Sopenharmony_ci    i365_set_pair(sock, I365_IO(map)+I365_W_STOP, io->stop);
10478c2ecf20Sopenharmony_ci    ioctl = i365_get(sock, I365_IOCTL) & ~I365_IOCTL_MASK(map);
10488c2ecf20Sopenharmony_ci    if (io->speed) ioctl |= I365_IOCTL_WAIT(map);
10498c2ecf20Sopenharmony_ci    if (io->flags & MAP_0WS) ioctl |= I365_IOCTL_0WS(map);
10508c2ecf20Sopenharmony_ci    if (io->flags & MAP_16BIT) ioctl |= I365_IOCTL_16BIT(map);
10518c2ecf20Sopenharmony_ci    if (io->flags & MAP_AUTOSZ) ioctl |= I365_IOCTL_IOCS16(map);
10528c2ecf20Sopenharmony_ci    i365_set(sock, I365_IOCTL, ioctl);
10538c2ecf20Sopenharmony_ci    /* Turn on the window if necessary */
10548c2ecf20Sopenharmony_ci    if (io->flags & MAP_ACTIVE)
10558c2ecf20Sopenharmony_ci	i365_bset(sock, I365_ADDRWIN, I365_ENA_IO(map));
10568c2ecf20Sopenharmony_ci    return 0;
10578c2ecf20Sopenharmony_ci} /* i365_set_io_map */
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci/*====================================================================*/
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_cistatic int i365_set_mem_map(u_short sock, struct pccard_mem_map *mem)
10628c2ecf20Sopenharmony_ci{
10638c2ecf20Sopenharmony_ci    u_short base, i;
10648c2ecf20Sopenharmony_ci    u_char map;
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci    pr_debug("SetMemMap(%d, %d, %#2.2x, %d ns, %#llx-%#llx, "
10678c2ecf20Sopenharmony_ci	  "%#x)\n", sock, mem->map, mem->flags, mem->speed,
10688c2ecf20Sopenharmony_ci	  (unsigned long long)mem->res->start,
10698c2ecf20Sopenharmony_ci	  (unsigned long long)mem->res->end, mem->card_start);
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci    map = mem->map;
10728c2ecf20Sopenharmony_ci    if ((map > 4) || (mem->card_start > 0x3ffffff) ||
10738c2ecf20Sopenharmony_ci	(mem->res->start > mem->res->end) || (mem->speed > 1000))
10748c2ecf20Sopenharmony_ci	return -EINVAL;
10758c2ecf20Sopenharmony_ci    if ((mem->res->start > 0xffffff) || (mem->res->end > 0xffffff))
10768c2ecf20Sopenharmony_ci	return -EINVAL;
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci    /* Turn off the window before changing anything */
10798c2ecf20Sopenharmony_ci    if (i365_get(sock, I365_ADDRWIN) & I365_ENA_MEM(map))
10808c2ecf20Sopenharmony_ci	i365_bclr(sock, I365_ADDRWIN, I365_ENA_MEM(map));
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci    base = I365_MEM(map);
10838c2ecf20Sopenharmony_ci    i = (mem->res->start >> 12) & 0x0fff;
10848c2ecf20Sopenharmony_ci    if (mem->flags & MAP_16BIT) i |= I365_MEM_16BIT;
10858c2ecf20Sopenharmony_ci    if (mem->flags & MAP_0WS) i |= I365_MEM_0WS;
10868c2ecf20Sopenharmony_ci    i365_set_pair(sock, base+I365_W_START, i);
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci    i = (mem->res->end >> 12) & 0x0fff;
10898c2ecf20Sopenharmony_ci    switch (to_cycles(mem->speed)) {
10908c2ecf20Sopenharmony_ci    case 0:	break;
10918c2ecf20Sopenharmony_ci    case 1:	i |= I365_MEM_WS0; break;
10928c2ecf20Sopenharmony_ci    case 2:	i |= I365_MEM_WS1; break;
10938c2ecf20Sopenharmony_ci    default:	i |= I365_MEM_WS1 | I365_MEM_WS0; break;
10948c2ecf20Sopenharmony_ci    }
10958c2ecf20Sopenharmony_ci    i365_set_pair(sock, base+I365_W_STOP, i);
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci    i = ((mem->card_start - mem->res->start) >> 12) & 0x3fff;
10988c2ecf20Sopenharmony_ci    if (mem->flags & MAP_WRPROT) i |= I365_MEM_WRPROT;
10998c2ecf20Sopenharmony_ci    if (mem->flags & MAP_ATTRIB) i |= I365_MEM_REG;
11008c2ecf20Sopenharmony_ci    i365_set_pair(sock, base+I365_W_OFF, i);
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci    /* Turn on the window if necessary */
11038c2ecf20Sopenharmony_ci    if (mem->flags & MAP_ACTIVE)
11048c2ecf20Sopenharmony_ci	i365_bset(sock, I365_ADDRWIN, I365_ENA_MEM(map));
11058c2ecf20Sopenharmony_ci    return 0;
11068c2ecf20Sopenharmony_ci} /* i365_set_mem_map */
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci#if 0 /* driver model ordering issue */
11098c2ecf20Sopenharmony_ci/*======================================================================
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci    Routines for accessing socket information and register dumps via
11128c2ecf20Sopenharmony_ci    /sys/class/pcmcia_socket/...
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci======================================================================*/
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_cistatic ssize_t show_info(struct class_device *class_dev, char *buf)
11178c2ecf20Sopenharmony_ci{
11188c2ecf20Sopenharmony_ci	struct i82365_socket *s = container_of(class_dev, struct i82365_socket, socket.dev);
11198c2ecf20Sopenharmony_ci	return sprintf(buf, "type:     %s\npsock:    %d\n",
11208c2ecf20Sopenharmony_ci		       pcic[s->type].name, s->psock);
11218c2ecf20Sopenharmony_ci}
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_cistatic ssize_t show_exca(struct class_device *class_dev, char *buf)
11248c2ecf20Sopenharmony_ci{
11258c2ecf20Sopenharmony_ci	struct i82365_socket *s = container_of(class_dev, struct i82365_socket, socket.dev);
11268c2ecf20Sopenharmony_ci	unsigned short sock;
11278c2ecf20Sopenharmony_ci	int i;
11288c2ecf20Sopenharmony_ci	ssize_t ret = 0;
11298c2ecf20Sopenharmony_ci	unsigned long flags = 0;
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci	sock = s->number;
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci	ISA_LOCK(sock, flags);
11348c2ecf20Sopenharmony_ci	for (i = 0; i < 0x40; i += 4) {
11358c2ecf20Sopenharmony_ci		ret += sprintf(buf, "%02x %02x %02x %02x%s",
11368c2ecf20Sopenharmony_ci			       i365_get(sock,i), i365_get(sock,i+1),
11378c2ecf20Sopenharmony_ci			       i365_get(sock,i+2), i365_get(sock,i+3),
11388c2ecf20Sopenharmony_ci			       ((i % 16) == 12) ? "\n" : " ");
11398c2ecf20Sopenharmony_ci		buf += ret;
11408c2ecf20Sopenharmony_ci	}
11418c2ecf20Sopenharmony_ci	ISA_UNLOCK(sock, flags);
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci	return ret;
11448c2ecf20Sopenharmony_ci}
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_cistatic CLASS_DEVICE_ATTR(exca, S_IRUGO, show_exca, NULL);
11478c2ecf20Sopenharmony_cistatic CLASS_DEVICE_ATTR(info, S_IRUGO, show_info, NULL);
11488c2ecf20Sopenharmony_ci#endif
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci/*====================================================================*/
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_ci/* this is horribly ugly... proper locking needs to be done here at
11538c2ecf20Sopenharmony_ci * some time... */
11548c2ecf20Sopenharmony_ci#define LOCKED(x) do { \
11558c2ecf20Sopenharmony_ci	int retval; \
11568c2ecf20Sopenharmony_ci	unsigned long flags; \
11578c2ecf20Sopenharmony_ci	spin_lock_irqsave(&isa_lock, flags); \
11588c2ecf20Sopenharmony_ci	retval = x; \
11598c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&isa_lock, flags); \
11608c2ecf20Sopenharmony_ci	return retval; \
11618c2ecf20Sopenharmony_ci} while (0)
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_cistatic int pcic_get_status(struct pcmcia_socket *s, u_int *value)
11658c2ecf20Sopenharmony_ci{
11668c2ecf20Sopenharmony_ci	unsigned int sock = container_of(s, struct i82365_socket, socket)->number;
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	if (socket[sock].flags & IS_ALIVE) {
11698c2ecf20Sopenharmony_ci		*value = 0;
11708c2ecf20Sopenharmony_ci		return -EINVAL;
11718c2ecf20Sopenharmony_ci	}
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci	LOCKED(i365_get_status(sock, value));
11748c2ecf20Sopenharmony_ci}
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_cistatic int pcic_set_socket(struct pcmcia_socket *s, socket_state_t *state)
11778c2ecf20Sopenharmony_ci{
11788c2ecf20Sopenharmony_ci	unsigned int sock = container_of(s, struct i82365_socket, socket)->number;
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	if (socket[sock].flags & IS_ALIVE)
11818c2ecf20Sopenharmony_ci		return -EINVAL;
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	LOCKED(i365_set_socket(sock, state));
11848c2ecf20Sopenharmony_ci}
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_cistatic int pcic_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
11878c2ecf20Sopenharmony_ci{
11888c2ecf20Sopenharmony_ci	unsigned int sock = container_of(s, struct i82365_socket, socket)->number;
11898c2ecf20Sopenharmony_ci	if (socket[sock].flags & IS_ALIVE)
11908c2ecf20Sopenharmony_ci		return -EINVAL;
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	LOCKED(i365_set_io_map(sock, io));
11938c2ecf20Sopenharmony_ci}
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_cistatic int pcic_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *mem)
11968c2ecf20Sopenharmony_ci{
11978c2ecf20Sopenharmony_ci	unsigned int sock = container_of(s, struct i82365_socket, socket)->number;
11988c2ecf20Sopenharmony_ci	if (socket[sock].flags & IS_ALIVE)
11998c2ecf20Sopenharmony_ci		return -EINVAL;
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	LOCKED(i365_set_mem_map(sock, mem));
12028c2ecf20Sopenharmony_ci}
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_cistatic int pcic_init(struct pcmcia_socket *s)
12058c2ecf20Sopenharmony_ci{
12068c2ecf20Sopenharmony_ci	int i;
12078c2ecf20Sopenharmony_ci	struct resource res = { .start = 0, .end = 0x1000 };
12088c2ecf20Sopenharmony_ci	pccard_io_map io = { 0, 0, 0, 0, 1 };
12098c2ecf20Sopenharmony_ci	pccard_mem_map mem = { .res = &res, };
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++) {
12128c2ecf20Sopenharmony_ci		io.map = i;
12138c2ecf20Sopenharmony_ci		pcic_set_io_map(s, &io);
12148c2ecf20Sopenharmony_ci	}
12158c2ecf20Sopenharmony_ci	for (i = 0; i < 5; i++) {
12168c2ecf20Sopenharmony_ci		mem.map = i;
12178c2ecf20Sopenharmony_ci		pcic_set_mem_map(s, &mem);
12188c2ecf20Sopenharmony_ci	}
12198c2ecf20Sopenharmony_ci	return 0;
12208c2ecf20Sopenharmony_ci}
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_cistatic struct pccard_operations pcic_operations = {
12248c2ecf20Sopenharmony_ci	.init			= pcic_init,
12258c2ecf20Sopenharmony_ci	.get_status		= pcic_get_status,
12268c2ecf20Sopenharmony_ci	.set_socket		= pcic_set_socket,
12278c2ecf20Sopenharmony_ci	.set_io_map		= pcic_set_io_map,
12288c2ecf20Sopenharmony_ci	.set_mem_map		= pcic_set_mem_map,
12298c2ecf20Sopenharmony_ci};
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci/*====================================================================*/
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_cistatic struct platform_driver i82365_driver = {
12348c2ecf20Sopenharmony_ci	.driver = {
12358c2ecf20Sopenharmony_ci		.name = "i82365",
12368c2ecf20Sopenharmony_ci	},
12378c2ecf20Sopenharmony_ci};
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_cistatic struct platform_device *i82365_device;
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_cistatic int __init init_i82365(void)
12428c2ecf20Sopenharmony_ci{
12438c2ecf20Sopenharmony_ci    int i, ret;
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci    ret = platform_driver_register(&i82365_driver);
12468c2ecf20Sopenharmony_ci    if (ret)
12478c2ecf20Sopenharmony_ci	goto err_out;
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci    i82365_device = platform_device_alloc("i82365", 0);
12508c2ecf20Sopenharmony_ci    if (i82365_device) {
12518c2ecf20Sopenharmony_ci	    ret = platform_device_add(i82365_device);
12528c2ecf20Sopenharmony_ci	    if (ret)
12538c2ecf20Sopenharmony_ci		    platform_device_put(i82365_device);
12548c2ecf20Sopenharmony_ci    } else
12558c2ecf20Sopenharmony_ci	    ret = -ENOMEM;
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci    if (ret)
12588c2ecf20Sopenharmony_ci	goto err_driver_unregister;
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci    printk(KERN_INFO "Intel ISA PCIC probe: ");
12618c2ecf20Sopenharmony_ci    sockets = 0;
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ci    isa_probe();
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci    if (sockets == 0) {
12668c2ecf20Sopenharmony_ci	printk("not found.\n");
12678c2ecf20Sopenharmony_ci	ret = -ENODEV;
12688c2ecf20Sopenharmony_ci	goto err_dev_unregister;
12698c2ecf20Sopenharmony_ci    }
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci    /* Set up interrupt handler(s) */
12728c2ecf20Sopenharmony_ci    if (grab_irq != 0)
12738c2ecf20Sopenharmony_ci	ret = request_irq(cs_irq, pcic_interrupt, 0, "i82365", pcic_interrupt);
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci    if (ret)
12768c2ecf20Sopenharmony_ci	goto err_socket_release;
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci    /* register sockets with the pcmcia core */
12798c2ecf20Sopenharmony_ci    for (i = 0; i < sockets; i++) {
12808c2ecf20Sopenharmony_ci	    socket[i].socket.dev.parent = &i82365_device->dev;
12818c2ecf20Sopenharmony_ci	    socket[i].socket.ops = &pcic_operations;
12828c2ecf20Sopenharmony_ci	    socket[i].socket.resource_ops = &pccard_nonstatic_ops;
12838c2ecf20Sopenharmony_ci	    socket[i].socket.owner = THIS_MODULE;
12848c2ecf20Sopenharmony_ci	    socket[i].number = i;
12858c2ecf20Sopenharmony_ci	    ret = pcmcia_register_socket(&socket[i].socket);
12868c2ecf20Sopenharmony_ci	    if (!ret)
12878c2ecf20Sopenharmony_ci		    socket[i].flags |= IS_REGISTERED;
12888c2ecf20Sopenharmony_ci    }
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci    /* Finally, schedule a polling interrupt */
12918c2ecf20Sopenharmony_ci    if (poll_interval != 0) {
12928c2ecf20Sopenharmony_ci	timer_setup(&poll_timer, pcic_interrupt_wrapper, 0);
12938c2ecf20Sopenharmony_ci    	poll_timer.expires = jiffies + poll_interval;
12948c2ecf20Sopenharmony_ci	add_timer(&poll_timer);
12958c2ecf20Sopenharmony_ci    }
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_ci    return 0;
12988c2ecf20Sopenharmony_cierr_socket_release:
12998c2ecf20Sopenharmony_ci    for (i = 0; i < sockets; i++) {
13008c2ecf20Sopenharmony_ci	/* Turn off all interrupt sources! */
13018c2ecf20Sopenharmony_ci	i365_set(i, I365_CSCINT, 0);
13028c2ecf20Sopenharmony_ci	release_region(socket[i].ioaddr, 2);
13038c2ecf20Sopenharmony_ci    }
13048c2ecf20Sopenharmony_cierr_dev_unregister:
13058c2ecf20Sopenharmony_ci    platform_device_unregister(i82365_device);
13068c2ecf20Sopenharmony_ci    release_region(i365_base, 2);
13078c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP
13088c2ecf20Sopenharmony_ci    if (i82365_pnpdev)
13098c2ecf20Sopenharmony_ci	pnp_disable_dev(i82365_pnpdev);
13108c2ecf20Sopenharmony_ci#endif
13118c2ecf20Sopenharmony_cierr_driver_unregister:
13128c2ecf20Sopenharmony_ci    platform_driver_unregister(&i82365_driver);
13138c2ecf20Sopenharmony_cierr_out:
13148c2ecf20Sopenharmony_ci    return ret;
13158c2ecf20Sopenharmony_ci} /* init_i82365 */
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_cistatic void __exit exit_i82365(void)
13188c2ecf20Sopenharmony_ci{
13198c2ecf20Sopenharmony_ci    int i;
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ci    for (i = 0; i < sockets; i++) {
13228c2ecf20Sopenharmony_ci	    if (socket[i].flags & IS_REGISTERED)
13238c2ecf20Sopenharmony_ci		    pcmcia_unregister_socket(&socket[i].socket);
13248c2ecf20Sopenharmony_ci    }
13258c2ecf20Sopenharmony_ci    platform_device_unregister(i82365_device);
13268c2ecf20Sopenharmony_ci    if (poll_interval != 0)
13278c2ecf20Sopenharmony_ci	del_timer_sync(&poll_timer);
13288c2ecf20Sopenharmony_ci    if (grab_irq != 0)
13298c2ecf20Sopenharmony_ci	free_irq(cs_irq, pcic_interrupt);
13308c2ecf20Sopenharmony_ci    for (i = 0; i < sockets; i++) {
13318c2ecf20Sopenharmony_ci	/* Turn off all interrupt sources! */
13328c2ecf20Sopenharmony_ci	i365_set(i, I365_CSCINT, 0);
13338c2ecf20Sopenharmony_ci	release_region(socket[i].ioaddr, 2);
13348c2ecf20Sopenharmony_ci    }
13358c2ecf20Sopenharmony_ci    release_region(i365_base, 2);
13368c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP
13378c2ecf20Sopenharmony_ci    if (i82365_pnpdev)
13388c2ecf20Sopenharmony_ci    		pnp_disable_dev(i82365_pnpdev);
13398c2ecf20Sopenharmony_ci#endif
13408c2ecf20Sopenharmony_ci    platform_driver_unregister(&i82365_driver);
13418c2ecf20Sopenharmony_ci} /* exit_i82365 */
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_cimodule_init(init_i82365);
13448c2ecf20Sopenharmony_cimodule_exit(exit_i82365);
13458c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual MPL/GPL");
13468c2ecf20Sopenharmony_ci/*====================================================================*/
1347