162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Driver for the Cirrus PD6729 PCI-PCMCIA bridge.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Based on the i82092.c driver.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * This software may be used and distributed according to the terms of
762306a36Sopenharmony_ci * the GNU General Public License, incorporated herein by reference.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/pci.h>
1462306a36Sopenharmony_ci#include <linux/init.h>
1562306a36Sopenharmony_ci#include <linux/workqueue.h>
1662306a36Sopenharmony_ci#include <linux/interrupt.h>
1762306a36Sopenharmony_ci#include <linux/device.h>
1862306a36Sopenharmony_ci#include <linux/io.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <pcmcia/ss.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include "pd6729.h"
2462306a36Sopenharmony_ci#include "i82365.h"
2562306a36Sopenharmony_ci#include "cirrus.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2862306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for the Cirrus PD6729 PCI-PCMCIA bridge");
2962306a36Sopenharmony_ciMODULE_AUTHOR("Jun Komuro <komurojun-mbn@nifty.com>");
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define MAX_SOCKETS 2
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/*
3462306a36Sopenharmony_ci * simple helper functions
3562306a36Sopenharmony_ci * External clock time, in nanoseconds.  120 ns = 8.33 MHz
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_ci#define to_cycles(ns)	((ns)/120)
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#ifndef NO_IRQ
4062306a36Sopenharmony_ci#define NO_IRQ	((unsigned int)(0))
4162306a36Sopenharmony_ci#endif
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/*
4462306a36Sopenharmony_ci * PARAMETERS
4562306a36Sopenharmony_ci *  irq_mode=n
4662306a36Sopenharmony_ci *     Specifies the interrupt delivery mode.  The default (1) is to use PCI
4762306a36Sopenharmony_ci *     interrupts; a value of 0 selects ISA interrupts. This must be set for
4862306a36Sopenharmony_ci *     correct operation of PCI card readers.
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic int irq_mode = 1; /* 0 = ISA interrupt, 1 = PCI interrupt */
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cimodule_param(irq_mode, int, 0444);
5462306a36Sopenharmony_ciMODULE_PARM_DESC(irq_mode,
5562306a36Sopenharmony_ci		"interrupt delivery mode. 0 = ISA, 1 = PCI. default is 1");
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic DEFINE_SPINLOCK(port_lock);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/* basic value read/write functions */
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic unsigned char indirect_read(struct pd6729_socket *socket,
6262306a36Sopenharmony_ci				   unsigned short reg)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	unsigned long port;
6562306a36Sopenharmony_ci	unsigned char val;
6662306a36Sopenharmony_ci	unsigned long flags;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	spin_lock_irqsave(&port_lock, flags);
6962306a36Sopenharmony_ci	reg += socket->number * 0x40;
7062306a36Sopenharmony_ci	port = socket->io_base;
7162306a36Sopenharmony_ci	outb(reg, port);
7262306a36Sopenharmony_ci	val = inb(port + 1);
7362306a36Sopenharmony_ci	spin_unlock_irqrestore(&port_lock, flags);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	return val;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic unsigned short indirect_read16(struct pd6729_socket *socket,
7962306a36Sopenharmony_ci				      unsigned short reg)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	unsigned long port;
8262306a36Sopenharmony_ci	unsigned short tmp;
8362306a36Sopenharmony_ci	unsigned long flags;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	spin_lock_irqsave(&port_lock, flags);
8662306a36Sopenharmony_ci	reg  = reg + socket->number * 0x40;
8762306a36Sopenharmony_ci	port = socket->io_base;
8862306a36Sopenharmony_ci	outb(reg, port);
8962306a36Sopenharmony_ci	tmp = inb(port + 1);
9062306a36Sopenharmony_ci	reg++;
9162306a36Sopenharmony_ci	outb(reg, port);
9262306a36Sopenharmony_ci	tmp = tmp | (inb(port + 1) << 8);
9362306a36Sopenharmony_ci	spin_unlock_irqrestore(&port_lock, flags);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return tmp;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic void indirect_write(struct pd6729_socket *socket, unsigned short reg,
9962306a36Sopenharmony_ci			   unsigned char value)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	unsigned long port;
10262306a36Sopenharmony_ci	unsigned long flags;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	spin_lock_irqsave(&port_lock, flags);
10562306a36Sopenharmony_ci	reg = reg + socket->number * 0x40;
10662306a36Sopenharmony_ci	port = socket->io_base;
10762306a36Sopenharmony_ci	outb(reg, port);
10862306a36Sopenharmony_ci	outb(value, port + 1);
10962306a36Sopenharmony_ci	spin_unlock_irqrestore(&port_lock, flags);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic void indirect_setbit(struct pd6729_socket *socket, unsigned short reg,
11362306a36Sopenharmony_ci			    unsigned char mask)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	unsigned long port;
11662306a36Sopenharmony_ci	unsigned char val;
11762306a36Sopenharmony_ci	unsigned long flags;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	spin_lock_irqsave(&port_lock, flags);
12062306a36Sopenharmony_ci	reg = reg + socket->number * 0x40;
12162306a36Sopenharmony_ci	port = socket->io_base;
12262306a36Sopenharmony_ci	outb(reg, port);
12362306a36Sopenharmony_ci	val = inb(port + 1);
12462306a36Sopenharmony_ci	val |= mask;
12562306a36Sopenharmony_ci	outb(reg, port);
12662306a36Sopenharmony_ci	outb(val, port + 1);
12762306a36Sopenharmony_ci	spin_unlock_irqrestore(&port_lock, flags);
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic void indirect_resetbit(struct pd6729_socket *socket, unsigned short reg,
13162306a36Sopenharmony_ci			      unsigned char mask)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	unsigned long port;
13462306a36Sopenharmony_ci	unsigned char val;
13562306a36Sopenharmony_ci	unsigned long flags;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	spin_lock_irqsave(&port_lock, flags);
13862306a36Sopenharmony_ci	reg = reg + socket->number * 0x40;
13962306a36Sopenharmony_ci	port = socket->io_base;
14062306a36Sopenharmony_ci	outb(reg, port);
14162306a36Sopenharmony_ci	val = inb(port + 1);
14262306a36Sopenharmony_ci	val &= ~mask;
14362306a36Sopenharmony_ci	outb(reg, port);
14462306a36Sopenharmony_ci	outb(val, port + 1);
14562306a36Sopenharmony_ci	spin_unlock_irqrestore(&port_lock, flags);
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic void indirect_write16(struct pd6729_socket *socket, unsigned short reg,
14962306a36Sopenharmony_ci			     unsigned short value)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	unsigned long port;
15262306a36Sopenharmony_ci	unsigned char val;
15362306a36Sopenharmony_ci	unsigned long flags;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	spin_lock_irqsave(&port_lock, flags);
15662306a36Sopenharmony_ci	reg = reg + socket->number * 0x40;
15762306a36Sopenharmony_ci	port = socket->io_base;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	outb(reg, port);
16062306a36Sopenharmony_ci	val = value & 255;
16162306a36Sopenharmony_ci	outb(val, port + 1);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	reg++;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	outb(reg, port);
16662306a36Sopenharmony_ci	val = value >> 8;
16762306a36Sopenharmony_ci	outb(val, port + 1);
16862306a36Sopenharmony_ci	spin_unlock_irqrestore(&port_lock, flags);
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci/* Interrupt handler functionality */
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic irqreturn_t pd6729_interrupt(int irq, void *dev)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	struct pd6729_socket *socket = (struct pd6729_socket *)dev;
17662306a36Sopenharmony_ci	int i;
17762306a36Sopenharmony_ci	int loopcount = 0;
17862306a36Sopenharmony_ci	int handled = 0;
17962306a36Sopenharmony_ci	unsigned int events, active = 0;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	while (1) {
18262306a36Sopenharmony_ci		loopcount++;
18362306a36Sopenharmony_ci		if (loopcount > 20) {
18462306a36Sopenharmony_ci			printk(KERN_ERR "pd6729: infinite eventloop "
18562306a36Sopenharmony_ci			       "in interrupt\n");
18662306a36Sopenharmony_ci			break;
18762306a36Sopenharmony_ci		}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci		active = 0;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci		for (i = 0; i < MAX_SOCKETS; i++) {
19262306a36Sopenharmony_ci			unsigned int csc;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci			/* card status change register */
19562306a36Sopenharmony_ci			csc = indirect_read(&socket[i], I365_CSC);
19662306a36Sopenharmony_ci			if (csc == 0)  /* no events on this socket */
19762306a36Sopenharmony_ci				continue;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci			handled = 1;
20062306a36Sopenharmony_ci			events = 0;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci			if (csc & I365_CSC_DETECT) {
20362306a36Sopenharmony_ci				events |= SS_DETECT;
20462306a36Sopenharmony_ci				dev_vdbg(&socket[i].socket.dev,
20562306a36Sopenharmony_ci					"Card detected in socket %i!\n", i);
20662306a36Sopenharmony_ci			}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci			if (indirect_read(&socket[i], I365_INTCTL)
20962306a36Sopenharmony_ci						& I365_PC_IOCARD) {
21062306a36Sopenharmony_ci				/* For IO/CARDS, bit 0 means "read the card" */
21162306a36Sopenharmony_ci				events |= (csc & I365_CSC_STSCHG)
21262306a36Sopenharmony_ci						? SS_STSCHG : 0;
21362306a36Sopenharmony_ci			} else {
21462306a36Sopenharmony_ci				/* Check for battery/ready events */
21562306a36Sopenharmony_ci				events |= (csc & I365_CSC_BVD1)
21662306a36Sopenharmony_ci						? SS_BATDEAD : 0;
21762306a36Sopenharmony_ci				events |= (csc & I365_CSC_BVD2)
21862306a36Sopenharmony_ci						? SS_BATWARN : 0;
21962306a36Sopenharmony_ci				events |= (csc & I365_CSC_READY)
22062306a36Sopenharmony_ci						? SS_READY : 0;
22162306a36Sopenharmony_ci			}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci			if (events)
22462306a36Sopenharmony_ci				pcmcia_parse_events(&socket[i].socket, events);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci			active |= events;
22762306a36Sopenharmony_ci		}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci		if (active == 0) /* no more events to handle */
23062306a36Sopenharmony_ci			break;
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci	return IRQ_RETVAL(handled);
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci/* socket functions */
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic void pd6729_interrupt_wrapper(struct timer_list *t)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct pd6729_socket *socket = from_timer(socket, t, poll_timer);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	pd6729_interrupt(0, (void *)socket);
24262306a36Sopenharmony_ci	mod_timer(&socket->poll_timer, jiffies + HZ);
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic int pd6729_get_status(struct pcmcia_socket *sock, u_int *value)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	struct pd6729_socket *socket
24862306a36Sopenharmony_ci			= container_of(sock, struct pd6729_socket, socket);
24962306a36Sopenharmony_ci	unsigned int status;
25062306a36Sopenharmony_ci	unsigned int data;
25162306a36Sopenharmony_ci	struct pd6729_socket *t;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	/* Interface Status Register */
25462306a36Sopenharmony_ci	status = indirect_read(socket, I365_STATUS);
25562306a36Sopenharmony_ci	*value = 0;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if ((status & I365_CS_DETECT) == I365_CS_DETECT)
25862306a36Sopenharmony_ci		*value |= SS_DETECT;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	/*
26162306a36Sopenharmony_ci	 * IO cards have a different meaning of bits 0,1
26262306a36Sopenharmony_ci	 * Also notice the inverse-logic on the bits
26362306a36Sopenharmony_ci	 */
26462306a36Sopenharmony_ci	if (indirect_read(socket, I365_INTCTL) & I365_PC_IOCARD) {
26562306a36Sopenharmony_ci		/* IO card */
26662306a36Sopenharmony_ci		if (!(status & I365_CS_STSCHG))
26762306a36Sopenharmony_ci			*value |= SS_STSCHG;
26862306a36Sopenharmony_ci	} else {
26962306a36Sopenharmony_ci		/* non I/O card */
27062306a36Sopenharmony_ci		if (!(status & I365_CS_BVD1))
27162306a36Sopenharmony_ci			*value |= SS_BATDEAD;
27262306a36Sopenharmony_ci		if (!(status & I365_CS_BVD2))
27362306a36Sopenharmony_ci			*value |= SS_BATWARN;
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	if (status & I365_CS_WRPROT)
27762306a36Sopenharmony_ci		*value |= SS_WRPROT;	/* card is write protected */
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if (status & I365_CS_READY)
28062306a36Sopenharmony_ci		*value |= SS_READY;	/* card is not busy */
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (status & I365_CS_POWERON)
28362306a36Sopenharmony_ci		*value |= SS_POWERON;	/* power is applied to the card */
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	t = (socket->number) ? socket : socket + 1;
28662306a36Sopenharmony_ci	indirect_write(t, PD67_EXT_INDEX, PD67_EXTERN_DATA);
28762306a36Sopenharmony_ci	data = indirect_read16(t, PD67_EXT_DATA);
28862306a36Sopenharmony_ci	*value |= (data & PD67_EXD_VS1(socket->number)) ? 0 : SS_3VCARD;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	return 0;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic int pd6729_set_socket(struct pcmcia_socket *sock, socket_state_t *state)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	struct pd6729_socket *socket
29762306a36Sopenharmony_ci			= container_of(sock, struct pd6729_socket, socket);
29862306a36Sopenharmony_ci	unsigned char reg, data;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	/* First, set the global controller options */
30162306a36Sopenharmony_ci	indirect_write(socket, I365_GBLCTL, 0x00);
30262306a36Sopenharmony_ci	indirect_write(socket, I365_GENCTL, 0x00);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	/* Values for the IGENC register */
30562306a36Sopenharmony_ci	socket->card_irq = state->io_irq;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	reg = 0;
30862306a36Sopenharmony_ci	/* The reset bit has "inverse" logic */
30962306a36Sopenharmony_ci	if (!(state->flags & SS_RESET))
31062306a36Sopenharmony_ci		reg |= I365_PC_RESET;
31162306a36Sopenharmony_ci	if (state->flags & SS_IOCARD)
31262306a36Sopenharmony_ci		reg |= I365_PC_IOCARD;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	/* IGENC, Interrupt and General Control Register */
31562306a36Sopenharmony_ci	indirect_write(socket, I365_INTCTL, reg);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	/* Power registers */
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	reg = I365_PWR_NORESET; /* default: disable resetdrv on resume */
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	if (state->flags & SS_PWR_AUTO) {
32262306a36Sopenharmony_ci		dev_dbg(&sock->dev, "Auto power\n");
32362306a36Sopenharmony_ci		reg |= I365_PWR_AUTO;	/* automatic power mngmnt */
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci	if (state->flags & SS_OUTPUT_ENA) {
32662306a36Sopenharmony_ci		dev_dbg(&sock->dev, "Power Enabled\n");
32762306a36Sopenharmony_ci		reg |= I365_PWR_OUT;	/* enable power */
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	switch (state->Vcc) {
33162306a36Sopenharmony_ci	case 0:
33262306a36Sopenharmony_ci		break;
33362306a36Sopenharmony_ci	case 33:
33462306a36Sopenharmony_ci		dev_dbg(&sock->dev,
33562306a36Sopenharmony_ci			"setting voltage to Vcc to 3.3V on socket %i\n",
33662306a36Sopenharmony_ci			socket->number);
33762306a36Sopenharmony_ci		reg |= I365_VCC_5V;
33862306a36Sopenharmony_ci		indirect_setbit(socket, PD67_MISC_CTL_1, PD67_MC1_VCC_3V);
33962306a36Sopenharmony_ci		break;
34062306a36Sopenharmony_ci	case 50:
34162306a36Sopenharmony_ci		dev_dbg(&sock->dev,
34262306a36Sopenharmony_ci			"setting voltage to Vcc to 5V on socket %i\n",
34362306a36Sopenharmony_ci			socket->number);
34462306a36Sopenharmony_ci		reg |= I365_VCC_5V;
34562306a36Sopenharmony_ci		indirect_resetbit(socket, PD67_MISC_CTL_1, PD67_MC1_VCC_3V);
34662306a36Sopenharmony_ci		break;
34762306a36Sopenharmony_ci	default:
34862306a36Sopenharmony_ci		dev_dbg(&sock->dev,
34962306a36Sopenharmony_ci			"pd6729_set_socket called with invalid VCC power "
35062306a36Sopenharmony_ci			"value: %i\n", state->Vcc);
35162306a36Sopenharmony_ci		return -EINVAL;
35262306a36Sopenharmony_ci	}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	switch (state->Vpp) {
35562306a36Sopenharmony_ci	case 0:
35662306a36Sopenharmony_ci		dev_dbg(&sock->dev, "not setting Vpp on socket %i\n",
35762306a36Sopenharmony_ci			socket->number);
35862306a36Sopenharmony_ci		break;
35962306a36Sopenharmony_ci	case 33:
36062306a36Sopenharmony_ci	case 50:
36162306a36Sopenharmony_ci		dev_dbg(&sock->dev, "setting Vpp to Vcc for socket %i\n",
36262306a36Sopenharmony_ci			socket->number);
36362306a36Sopenharmony_ci		reg |= I365_VPP1_5V;
36462306a36Sopenharmony_ci		break;
36562306a36Sopenharmony_ci	case 120:
36662306a36Sopenharmony_ci		dev_dbg(&sock->dev, "setting Vpp to 12.0\n");
36762306a36Sopenharmony_ci		reg |= I365_VPP1_12V;
36862306a36Sopenharmony_ci		break;
36962306a36Sopenharmony_ci	default:
37062306a36Sopenharmony_ci		dev_dbg(&sock->dev, "pd6729: pd6729_set_socket called with "
37162306a36Sopenharmony_ci			"invalid VPP power value: %i\n", state->Vpp);
37262306a36Sopenharmony_ci		return -EINVAL;
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	/* only write if changed */
37662306a36Sopenharmony_ci	if (reg != indirect_read(socket, I365_POWER))
37762306a36Sopenharmony_ci		indirect_write(socket, I365_POWER, reg);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	if (irq_mode == 1) {
38062306a36Sopenharmony_ci		/* all interrupts are to be done as PCI interrupts */
38162306a36Sopenharmony_ci		data = PD67_EC1_INV_MGMT_IRQ | PD67_EC1_INV_CARD_IRQ;
38262306a36Sopenharmony_ci	} else
38362306a36Sopenharmony_ci		data = 0;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	indirect_write(socket, PD67_EXT_INDEX, PD67_EXT_CTL_1);
38662306a36Sopenharmony_ci	indirect_write(socket, PD67_EXT_DATA, data);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	/* Enable specific interrupt events */
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	reg = 0x00;
39162306a36Sopenharmony_ci	if (state->csc_mask & SS_DETECT)
39262306a36Sopenharmony_ci		reg |= I365_CSC_DETECT;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	if (state->flags & SS_IOCARD) {
39562306a36Sopenharmony_ci		if (state->csc_mask & SS_STSCHG)
39662306a36Sopenharmony_ci			reg |= I365_CSC_STSCHG;
39762306a36Sopenharmony_ci	} else {
39862306a36Sopenharmony_ci		if (state->csc_mask & SS_BATDEAD)
39962306a36Sopenharmony_ci			reg |= I365_CSC_BVD1;
40062306a36Sopenharmony_ci		if (state->csc_mask & SS_BATWARN)
40162306a36Sopenharmony_ci			reg |= I365_CSC_BVD2;
40262306a36Sopenharmony_ci		if (state->csc_mask & SS_READY)
40362306a36Sopenharmony_ci			reg |= I365_CSC_READY;
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci	if (irq_mode == 1)
40662306a36Sopenharmony_ci		reg |= 0x30;	/* management IRQ: PCI INTA# = "irq 3" */
40762306a36Sopenharmony_ci	indirect_write(socket, I365_CSCINT, reg);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	reg = indirect_read(socket, I365_INTCTL);
41062306a36Sopenharmony_ci	if (irq_mode == 1)
41162306a36Sopenharmony_ci		reg |= 0x03;	/* card IRQ: PCI INTA# = "irq 3" */
41262306a36Sopenharmony_ci	else
41362306a36Sopenharmony_ci		reg |= socket->card_irq;
41462306a36Sopenharmony_ci	indirect_write(socket, I365_INTCTL, reg);
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	/* now clear the (probably bogus) pending stuff by doing a dummy read */
41762306a36Sopenharmony_ci	(void)indirect_read(socket, I365_CSC);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	return 0;
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic int pd6729_set_io_map(struct pcmcia_socket *sock,
42362306a36Sopenharmony_ci			     struct pccard_io_map *io)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	struct pd6729_socket *socket
42662306a36Sopenharmony_ci			= container_of(sock, struct pd6729_socket, socket);
42762306a36Sopenharmony_ci	unsigned char map, ioctl;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	map = io->map;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	/* Check error conditions */
43262306a36Sopenharmony_ci	if (map > 1) {
43362306a36Sopenharmony_ci		dev_dbg(&sock->dev, "pd6729_set_io_map with invalid map\n");
43462306a36Sopenharmony_ci		return -EINVAL;
43562306a36Sopenharmony_ci	}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	/* Turn off the window before changing anything */
43862306a36Sopenharmony_ci	if (indirect_read(socket, I365_ADDRWIN) & I365_ENA_IO(map))
43962306a36Sopenharmony_ci		indirect_resetbit(socket, I365_ADDRWIN, I365_ENA_IO(map));
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	/* dev_dbg(&sock->dev, "set_io_map: Setting range to %x - %x\n",
44262306a36Sopenharmony_ci	   io->start, io->stop);*/
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	/* write the new values */
44562306a36Sopenharmony_ci	indirect_write16(socket, I365_IO(map)+I365_W_START, io->start);
44662306a36Sopenharmony_ci	indirect_write16(socket, I365_IO(map)+I365_W_STOP, io->stop);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	ioctl = indirect_read(socket, I365_IOCTL) & ~I365_IOCTL_MASK(map);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	if (io->flags & MAP_0WS)
45162306a36Sopenharmony_ci		ioctl |= I365_IOCTL_0WS(map);
45262306a36Sopenharmony_ci	if (io->flags & MAP_16BIT)
45362306a36Sopenharmony_ci		ioctl |= I365_IOCTL_16BIT(map);
45462306a36Sopenharmony_ci	if (io->flags & MAP_AUTOSZ)
45562306a36Sopenharmony_ci		ioctl |= I365_IOCTL_IOCS16(map);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	indirect_write(socket, I365_IOCTL, ioctl);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	/* Turn the window back on if needed */
46062306a36Sopenharmony_ci	if (io->flags & MAP_ACTIVE)
46162306a36Sopenharmony_ci		indirect_setbit(socket, I365_ADDRWIN, I365_ENA_IO(map));
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	return 0;
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cistatic int pd6729_set_mem_map(struct pcmcia_socket *sock,
46762306a36Sopenharmony_ci			      struct pccard_mem_map *mem)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	struct pd6729_socket *socket
47062306a36Sopenharmony_ci			 = container_of(sock, struct pd6729_socket, socket);
47162306a36Sopenharmony_ci	unsigned short base, i;
47262306a36Sopenharmony_ci	unsigned char map;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	map = mem->map;
47562306a36Sopenharmony_ci	if (map > 4) {
47662306a36Sopenharmony_ci		dev_warn(&sock->dev, "invalid map requested\n");
47762306a36Sopenharmony_ci		return -EINVAL;
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	if ((mem->res->start > mem->res->end) || (mem->speed > 1000)) {
48162306a36Sopenharmony_ci		dev_warn(&sock->dev, "invalid invalid address / speed\n");
48262306a36Sopenharmony_ci		return -EINVAL;
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	/* Turn off the window before changing anything */
48662306a36Sopenharmony_ci	if (indirect_read(socket, I365_ADDRWIN) & I365_ENA_MEM(map))
48762306a36Sopenharmony_ci		indirect_resetbit(socket, I365_ADDRWIN, I365_ENA_MEM(map));
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	/* write the start address */
49062306a36Sopenharmony_ci	base = I365_MEM(map);
49162306a36Sopenharmony_ci	i = (mem->res->start >> 12) & 0x0fff;
49262306a36Sopenharmony_ci	if (mem->flags & MAP_16BIT)
49362306a36Sopenharmony_ci		i |= I365_MEM_16BIT;
49462306a36Sopenharmony_ci	if (mem->flags & MAP_0WS)
49562306a36Sopenharmony_ci		i |= I365_MEM_0WS;
49662306a36Sopenharmony_ci	indirect_write16(socket, base + I365_W_START, i);
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	/* write the stop address */
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	i = (mem->res->end >> 12) & 0x0fff;
50162306a36Sopenharmony_ci	switch (to_cycles(mem->speed)) {
50262306a36Sopenharmony_ci	case 0:
50362306a36Sopenharmony_ci		break;
50462306a36Sopenharmony_ci	case 1:
50562306a36Sopenharmony_ci		i |= I365_MEM_WS0;
50662306a36Sopenharmony_ci		break;
50762306a36Sopenharmony_ci	case 2:
50862306a36Sopenharmony_ci		i |= I365_MEM_WS1;
50962306a36Sopenharmony_ci		break;
51062306a36Sopenharmony_ci	default:
51162306a36Sopenharmony_ci		i |= I365_MEM_WS1 | I365_MEM_WS0;
51262306a36Sopenharmony_ci		break;
51362306a36Sopenharmony_ci	}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	indirect_write16(socket, base + I365_W_STOP, i);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	/* Take care of high byte */
51862306a36Sopenharmony_ci	indirect_write(socket, PD67_EXT_INDEX, PD67_MEM_PAGE(map));
51962306a36Sopenharmony_ci	indirect_write(socket, PD67_EXT_DATA, mem->res->start >> 24);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	/* card start */
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	i = ((mem->card_start - mem->res->start) >> 12) & 0x3fff;
52462306a36Sopenharmony_ci	if (mem->flags & MAP_WRPROT)
52562306a36Sopenharmony_ci		i |= I365_MEM_WRPROT;
52662306a36Sopenharmony_ci	if (mem->flags & MAP_ATTRIB) {
52762306a36Sopenharmony_ci		/* dev_dbg(&sock->dev, "requesting attribute memory for "
52862306a36Sopenharmony_ci		   "socket %i\n", socket->number);*/
52962306a36Sopenharmony_ci		i |= I365_MEM_REG;
53062306a36Sopenharmony_ci	} else {
53162306a36Sopenharmony_ci		/* dev_dbg(&sock->dev, "requesting normal memory for "
53262306a36Sopenharmony_ci		   "socket %i\n", socket->number);*/
53362306a36Sopenharmony_ci	}
53462306a36Sopenharmony_ci	indirect_write16(socket, base + I365_W_OFF, i);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	/* Enable the window if necessary */
53762306a36Sopenharmony_ci	if (mem->flags & MAP_ACTIVE)
53862306a36Sopenharmony_ci		indirect_setbit(socket, I365_ADDRWIN, I365_ENA_MEM(map));
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	return 0;
54162306a36Sopenharmony_ci}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_cistatic int pd6729_init(struct pcmcia_socket *sock)
54462306a36Sopenharmony_ci{
54562306a36Sopenharmony_ci	int i;
54662306a36Sopenharmony_ci	struct resource res = { .end = 0x0fff };
54762306a36Sopenharmony_ci	pccard_io_map io = { 0, 0, 0, 0, 1 };
54862306a36Sopenharmony_ci	pccard_mem_map mem = { .res = &res, };
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	pd6729_set_socket(sock, &dead_socket);
55162306a36Sopenharmony_ci	for (i = 0; i < 2; i++) {
55262306a36Sopenharmony_ci		io.map = i;
55362306a36Sopenharmony_ci		pd6729_set_io_map(sock, &io);
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci	for (i = 0; i < 5; i++) {
55662306a36Sopenharmony_ci		mem.map = i;
55762306a36Sopenharmony_ci		pd6729_set_mem_map(sock, &mem);
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	return 0;
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci/* the pccard structure and its functions */
56562306a36Sopenharmony_cistatic struct pccard_operations pd6729_operations = {
56662306a36Sopenharmony_ci	.init			= pd6729_init,
56762306a36Sopenharmony_ci	.get_status		= pd6729_get_status,
56862306a36Sopenharmony_ci	.set_socket		= pd6729_set_socket,
56962306a36Sopenharmony_ci	.set_io_map		= pd6729_set_io_map,
57062306a36Sopenharmony_ci	.set_mem_map		= pd6729_set_mem_map,
57162306a36Sopenharmony_ci};
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistatic irqreturn_t pd6729_test(int irq, void *dev)
57462306a36Sopenharmony_ci{
57562306a36Sopenharmony_ci	pr_devel("-> hit on irq %d\n", irq);
57662306a36Sopenharmony_ci	return IRQ_HANDLED;
57762306a36Sopenharmony_ci}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_cistatic int pd6729_check_irq(int irq)
58062306a36Sopenharmony_ci{
58162306a36Sopenharmony_ci	int ret;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	ret = request_irq(irq, pd6729_test, IRQF_PROBE_SHARED, "x",
58462306a36Sopenharmony_ci			  pd6729_test);
58562306a36Sopenharmony_ci	if (ret)
58662306a36Sopenharmony_ci		return -1;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	free_irq(irq, pd6729_test);
58962306a36Sopenharmony_ci	return 0;
59062306a36Sopenharmony_ci}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_cistatic u_int pd6729_isa_scan(void)
59362306a36Sopenharmony_ci{
59462306a36Sopenharmony_ci	u_int mask0, mask = 0;
59562306a36Sopenharmony_ci	int i;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	if (irq_mode == 1) {
59862306a36Sopenharmony_ci		printk(KERN_INFO "pd6729: PCI card interrupts, "
59962306a36Sopenharmony_ci		       "PCI status changes\n");
60062306a36Sopenharmony_ci		return 0;
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	mask0 = PD67_MASK;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	/* just find interrupts that aren't in use */
60662306a36Sopenharmony_ci	for (i = 0; i < 16; i++)
60762306a36Sopenharmony_ci		if ((mask0 & (1 << i)) && (pd6729_check_irq(i) == 0))
60862306a36Sopenharmony_ci			mask |= (1 << i);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	printk(KERN_INFO "pd6729: ISA irqs = ");
61162306a36Sopenharmony_ci	for (i = 0; i < 16; i++)
61262306a36Sopenharmony_ci		if (mask & (1<<i))
61362306a36Sopenharmony_ci			printk("%s%d", ((mask & ((1<<i)-1)) ? "," : ""), i);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	if (mask == 0)
61662306a36Sopenharmony_ci		printk("none!");
61762306a36Sopenharmony_ci	else
61862306a36Sopenharmony_ci		printk("  polling status changes.\n");
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	return mask;
62162306a36Sopenharmony_ci}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_cistatic int pd6729_pci_probe(struct pci_dev *dev,
62462306a36Sopenharmony_ci				      const struct pci_device_id *id)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	int i, j, ret;
62762306a36Sopenharmony_ci	u_int mask;
62862306a36Sopenharmony_ci	char configbyte;
62962306a36Sopenharmony_ci	struct pd6729_socket *socket;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	socket = kcalloc(MAX_SOCKETS, sizeof(struct pd6729_socket),
63262306a36Sopenharmony_ci			 GFP_KERNEL);
63362306a36Sopenharmony_ci	if (!socket) {
63462306a36Sopenharmony_ci		dev_warn(&dev->dev, "failed to kzalloc socket.\n");
63562306a36Sopenharmony_ci		return -ENOMEM;
63662306a36Sopenharmony_ci	}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	ret = pci_enable_device(dev);
63962306a36Sopenharmony_ci	if (ret) {
64062306a36Sopenharmony_ci		dev_warn(&dev->dev, "failed to enable pci_device.\n");
64162306a36Sopenharmony_ci		goto err_out_free_mem;
64262306a36Sopenharmony_ci	}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	if (!pci_resource_start(dev, 0)) {
64562306a36Sopenharmony_ci		dev_warn(&dev->dev, "refusing to load the driver as the "
64662306a36Sopenharmony_ci			"io_base is NULL.\n");
64762306a36Sopenharmony_ci		ret = -ENOMEM;
64862306a36Sopenharmony_ci		goto err_out_disable;
64962306a36Sopenharmony_ci	}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	dev_info(&dev->dev, "Cirrus PD6729 PCI to PCMCIA Bridge at 0x%llx "
65262306a36Sopenharmony_ci		"on irq %d\n",
65362306a36Sopenharmony_ci		(unsigned long long)pci_resource_start(dev, 0), dev->irq);
65462306a36Sopenharmony_ci	/*
65562306a36Sopenharmony_ci	 * Since we have no memory BARs some firmware may not
65662306a36Sopenharmony_ci	 * have had PCI_COMMAND_MEMORY enabled, yet the device needs it.
65762306a36Sopenharmony_ci	 */
65862306a36Sopenharmony_ci	pci_read_config_byte(dev, PCI_COMMAND, &configbyte);
65962306a36Sopenharmony_ci	if (!(configbyte & PCI_COMMAND_MEMORY)) {
66062306a36Sopenharmony_ci		dev_dbg(&dev->dev, "pd6729: Enabling PCI_COMMAND_MEMORY.\n");
66162306a36Sopenharmony_ci		configbyte |= PCI_COMMAND_MEMORY;
66262306a36Sopenharmony_ci		pci_write_config_byte(dev, PCI_COMMAND, configbyte);
66362306a36Sopenharmony_ci	}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	ret = pci_request_regions(dev, "pd6729");
66662306a36Sopenharmony_ci	if (ret) {
66762306a36Sopenharmony_ci		dev_warn(&dev->dev, "pci request region failed.\n");
66862306a36Sopenharmony_ci		goto err_out_disable;
66962306a36Sopenharmony_ci	}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	if (dev->irq == NO_IRQ)
67262306a36Sopenharmony_ci		irq_mode = 0;	/* fall back to ISA interrupt mode */
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	mask = pd6729_isa_scan();
67562306a36Sopenharmony_ci	if (irq_mode == 0 && mask == 0) {
67662306a36Sopenharmony_ci		dev_warn(&dev->dev, "no ISA interrupt is available.\n");
67762306a36Sopenharmony_ci		ret = -ENODEV;
67862306a36Sopenharmony_ci		goto err_out_free_res;
67962306a36Sopenharmony_ci	}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	for (i = 0; i < MAX_SOCKETS; i++) {
68262306a36Sopenharmony_ci		socket[i].io_base = pci_resource_start(dev, 0);
68362306a36Sopenharmony_ci		socket[i].socket.features |= SS_CAP_PAGE_REGS | SS_CAP_PCCARD;
68462306a36Sopenharmony_ci		socket[i].socket.map_size = 0x1000;
68562306a36Sopenharmony_ci		socket[i].socket.irq_mask = mask;
68662306a36Sopenharmony_ci		socket[i].socket.pci_irq  = dev->irq;
68762306a36Sopenharmony_ci		socket[i].socket.cb_dev = dev;
68862306a36Sopenharmony_ci		socket[i].socket.owner = THIS_MODULE;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci		socket[i].number = i;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci		socket[i].socket.ops = &pd6729_operations;
69362306a36Sopenharmony_ci		socket[i].socket.resource_ops = &pccard_nonstatic_ops;
69462306a36Sopenharmony_ci		socket[i].socket.dev.parent = &dev->dev;
69562306a36Sopenharmony_ci		socket[i].socket.driver_data = &socket[i];
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	pci_set_drvdata(dev, socket);
69962306a36Sopenharmony_ci	if (irq_mode == 1) {
70062306a36Sopenharmony_ci		/* Register the interrupt handler */
70162306a36Sopenharmony_ci		ret = request_irq(dev->irq, pd6729_interrupt, IRQF_SHARED,
70262306a36Sopenharmony_ci				  "pd6729", socket);
70362306a36Sopenharmony_ci		if (ret) {
70462306a36Sopenharmony_ci			dev_err(&dev->dev, "Failed to register irq %d\n",
70562306a36Sopenharmony_ci				dev->irq);
70662306a36Sopenharmony_ci			goto err_out_free_res;
70762306a36Sopenharmony_ci		}
70862306a36Sopenharmony_ci	} else {
70962306a36Sopenharmony_ci		/* poll Card status change */
71062306a36Sopenharmony_ci		timer_setup(&socket->poll_timer, pd6729_interrupt_wrapper, 0);
71162306a36Sopenharmony_ci		mod_timer(&socket->poll_timer, jiffies + HZ);
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	for (i = 0; i < MAX_SOCKETS; i++) {
71562306a36Sopenharmony_ci		ret = pcmcia_register_socket(&socket[i].socket);
71662306a36Sopenharmony_ci		if (ret) {
71762306a36Sopenharmony_ci			dev_warn(&dev->dev, "pcmcia_register_socket failed.\n");
71862306a36Sopenharmony_ci			for (j = 0; j < i ; j++)
71962306a36Sopenharmony_ci				pcmcia_unregister_socket(&socket[j].socket);
72062306a36Sopenharmony_ci			goto err_out_free_res2;
72162306a36Sopenharmony_ci		}
72262306a36Sopenharmony_ci	}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	return 0;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_cierr_out_free_res2:
72762306a36Sopenharmony_ci	if (irq_mode == 1)
72862306a36Sopenharmony_ci		free_irq(dev->irq, socket);
72962306a36Sopenharmony_ci	else
73062306a36Sopenharmony_ci		timer_shutdown_sync(&socket->poll_timer);
73162306a36Sopenharmony_cierr_out_free_res:
73262306a36Sopenharmony_ci	pci_release_regions(dev);
73362306a36Sopenharmony_cierr_out_disable:
73462306a36Sopenharmony_ci	pci_disable_device(dev);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_cierr_out_free_mem:
73762306a36Sopenharmony_ci	kfree(socket);
73862306a36Sopenharmony_ci	return ret;
73962306a36Sopenharmony_ci}
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_cistatic void pd6729_pci_remove(struct pci_dev *dev)
74262306a36Sopenharmony_ci{
74362306a36Sopenharmony_ci	int i;
74462306a36Sopenharmony_ci	struct pd6729_socket *socket = pci_get_drvdata(dev);
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	for (i = 0; i < MAX_SOCKETS; i++) {
74762306a36Sopenharmony_ci		/* Turn off all interrupt sources */
74862306a36Sopenharmony_ci		indirect_write(&socket[i], I365_CSCINT, 0);
74962306a36Sopenharmony_ci		indirect_write(&socket[i], I365_INTCTL, 0);
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci		pcmcia_unregister_socket(&socket[i].socket);
75262306a36Sopenharmony_ci	}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	if (irq_mode == 1)
75562306a36Sopenharmony_ci		free_irq(dev->irq, socket);
75662306a36Sopenharmony_ci	else
75762306a36Sopenharmony_ci		timer_shutdown_sync(&socket->poll_timer);
75862306a36Sopenharmony_ci	pci_release_regions(dev);
75962306a36Sopenharmony_ci	pci_disable_device(dev);
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	kfree(socket);
76262306a36Sopenharmony_ci}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_cistatic const struct pci_device_id pd6729_pci_ids[] = {
76562306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_6729) },
76662306a36Sopenharmony_ci	{ }
76762306a36Sopenharmony_ci};
76862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pd6729_pci_ids);
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_cistatic struct pci_driver pd6729_pci_driver = {
77162306a36Sopenharmony_ci	.name		= "pd6729",
77262306a36Sopenharmony_ci	.id_table	= pd6729_pci_ids,
77362306a36Sopenharmony_ci	.probe		= pd6729_pci_probe,
77462306a36Sopenharmony_ci	.remove		= pd6729_pci_remove,
77562306a36Sopenharmony_ci};
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_cimodule_pci_driver(pd6729_pci_driver);
778