Blame view

drivers/pcmcia/i82092.c 15.2 KB
09c434b8a   Thomas Gleixner   treewide: Add SPD...
1
  // SPDX-License-Identifier: GPL-2.0-only
4839879f8   Simon Geis   PCMCIA/i82092: ad...
2
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
4
5
6
7
8
   * Driver for Intel I82092AA PCI-PCMCIA bridge.
   *
   * (C) 2001 Red Hat, Inc.
   *
   * Author: Arjan Van De Ven <arjanv@redhat.com>
   * Loosly based on i82365.c from the pcmcia-cs package
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
10
11
   */
  
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
13
14
15
16
17
  #include <linux/module.h>
  #include <linux/pci.h>
  #include <linux/init.h>
  #include <linux/workqueue.h>
  #include <linux/interrupt.h>
  #include <linux/device.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
  #include <pcmcia/ss.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19

ac5af8772   Simon Geis   PCMCIA/i82092: in...
20
  #include <linux/io.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
22
23
24
25
26
27
  
  #include "i82092aa.h"
  #include "i82365.h"
  
  MODULE_LICENSE("GPL");
  
  /* PCI core routines */
0178a7a54   Greg Kroah-Hartman   pcmcia: remove DE...
28
  static const struct pci_device_id i82092aa_pci_ids[] = {
2b2c5d8c1   Axel Lin   pcmcia: Convert t...
29
30
  	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82092AA_0) },
  	{ }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
32
  };
  MODULE_DEVICE_TABLE(pci, i82092aa_pci_ids);
ba66ddfa6   Sam Ravnborg   pcmcia: silence s...
33
  static struct pci_driver i82092aa_pci_driver = {
4839879f8   Simon Geis   PCMCIA/i82092: ad...
34
35
36
37
  	.name		= "i82092aa",
  	.id_table	= i82092aa_pci_ids,
  	.probe		= i82092aa_pci_probe,
  	.remove	= i82092aa_pci_remove,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
39
40
41
42
  };
  
  
  /* the pccard structure and its functions */
  static struct pccard_operations i82092aa_operations = {
4839879f8   Simon Geis   PCMCIA/i82092: ad...
43
  	.init			= i82092aa_init,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
  	.get_status		= i82092aa_get_status,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
46
47
48
  	.set_socket		= i82092aa_set_socket,
  	.set_io_map		= i82092aa_set_io_map,
  	.set_mem_map		= i82092aa_set_mem_map,
  };
25985edce   Lucas De Marchi   Fix common misspe...
49
  /* The card can do up to 4 sockets, allocate a structure for each of them */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
50
51
52
  
  struct socket_info {
  	int	number;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
53
54
55
56
57
58
59
  	int	card_state;
  		/* 0 = no socket,
  		 * 1 = empty socket,
  		 * 2 = card but not initialized,
  		 * 3 = operational card
  		 */
  	unsigned int io_base;	/* base io address of the socket */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
61
62
63
64
65
  	struct pcmcia_socket socket;
  	struct pci_dev *dev;	/* The PCI device for the socket */
  };
  
  #define MAX_SOCKETS 4
  static struct socket_info sockets[MAX_SOCKETS];
4839879f8   Simon Geis   PCMCIA/i82092: ad...
66
  static int socket_count;	/* shortcut */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67

152b4bb57   Simon Geis   PCMCIA/i82092: sh...
68
69
  static int i82092aa_pci_probe(struct pci_dev *dev,
  			      const struct pci_device_id *id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
71
72
  {
  	unsigned char configbyte;
  	int i, ret;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
73

908864641   Simon Geis   PCMCIA/i82092: mo...
74
75
  	ret = pci_enable_device(dev);
  	if (ret)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76
  		return ret;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
77

152b4bb57   Simon Geis   PCMCIA/i82092: sh...
78
79
  	/* PCI Configuration Control */
  	pci_read_config_byte(dev, 0x40, &configbyte);
4839879f8   Simon Geis   PCMCIA/i82092: ad...
80
  	switch (configbyte&6) {
6aaf8ff32   Simon Geis   PCMCIA/i82092: ch...
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
  	case 0:
  		socket_count = 2;
  		break;
  	case 2:
  		socket_count = 1;
  		break;
  	case 4:
  	case 6:
  		socket_count = 4;
  		break;
  
  	default:
  		dev_err(&dev->dev,
  			"Oops, you did something we didn't think of.
  ");
  		ret = -EIO;
  		goto err_out_disable;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
  	}
26a0a1041   Simon Geis   PCMCIA/i82092: us...
99
100
101
  	dev_info(&dev->dev, "configured as a %d socket device.
  ",
  		 socket_count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
103
104
105
106
  
  	if (!request_region(pci_resource_start(dev, 0), 2, "i82092aa")) {
  		ret = -EBUSY;
  		goto err_out_disable;
  	}
4839879f8   Simon Geis   PCMCIA/i82092: ad...
107
108
  
  	for (i = 0; i < socket_count; i++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
110
111
112
113
114
  		sockets[i].card_state = 1; /* 1 = present but empty */
  		sockets[i].io_base = pci_resource_start(dev, 0);
  		sockets[i].socket.features |= SS_CAP_PCCARD;
  		sockets[i].socket.map_size = 0x1000;
  		sockets[i].socket.irq_mask = 0;
  		sockets[i].socket.pci_irq  = dev->irq;
7a96e87d6   Dominik Brodowski   pcmcia: pd6729, i...
115
  		sockets[i].socket.cb_dev  = dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
117
118
  		sockets[i].socket.owner = THIS_MODULE;
  
  		sockets[i].number = i;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
119

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120
121
  		if (card_present(i)) {
  			sockets[i].card_state = 3;
26a0a1041   Simon Geis   PCMCIA/i82092: us...
122
123
  			dev_dbg(&dev->dev, "slot %i is occupied
  ", i);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
  		} else {
26a0a1041   Simon Geis   PCMCIA/i82092: us...
125
126
  			dev_dbg(&dev->dev, "slot %i is vacant
  ", i);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127
128
  		}
  	}
4839879f8   Simon Geis   PCMCIA/i82092: ad...
129

152b4bb57   Simon Geis   PCMCIA/i82092: sh...
130
131
132
133
134
135
136
  	/* Now, specifiy that all interrupts are to be done as PCI interrupts
  	 * bitmask, one bit per event, 1 = PCI interrupt, 0 = ISA interrupt
  	 */
  	configbyte = 0xFF;
  
  	/* PCI Interrupt Routing Register */
  	pci_write_config_byte(dev, 0x50, configbyte);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
138
  
  	/* Register the interrupt handler */
836e9494f   Adam Zerella   pcmcia/i82092: Re...
139
140
  	dev_dbg(&dev->dev, "Requesting interrupt %i
  ", dev->irq);
908864641   Simon Geis   PCMCIA/i82092: mo...
141
142
143
  	ret = request_irq(dev->irq, i82092aa_interrupt, IRQF_SHARED,
  			  "i82092aa", i82092aa_interrupt);
  	if (ret) {
26a0a1041   Simon Geis   PCMCIA/i82092: us...
144
145
146
  		dev_err(&dev->dev, "Failed to register IRQ %d, aborting
  ",
  			dev->irq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
147
148
  		goto err_out_free_res;
  	}
4839879f8   Simon Geis   PCMCIA/i82092: ad...
149
  	for (i = 0; i < socket_count; i++) {
873733188   Greg Kroah-Hartman   Driver core: conv...
150
  		sockets[i].socket.dev.parent = &dev->dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151
152
153
  		sockets[i].socket.ops = &i82092aa_operations;
  		sockets[i].socket.resource_ops = &pccard_nonstatic_ops;
  		ret = pcmcia_register_socket(&sockets[i].socket);
ae1f62c54   Simon Geis   PCMCIA/i82092: re...
154
  		if (ret)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
  			goto err_out_free_sockets;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
158
159
160
  	return 0;
  
  err_out_free_sockets:
  	if (i) {
ae1f62c54   Simon Geis   PCMCIA/i82092: re...
161
  		for (i--; i >= 0; i--)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
  			pcmcia_unregister_socket(&sockets[i].socket);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
164
165
166
167
168
  	}
  	free_irq(dev->irq, i82092aa_interrupt);
  err_out_free_res:
  	release_region(pci_resource_start(dev, 0), 2);
  err_out_disable:
  	pci_disable_device(dev);
4839879f8   Simon Geis   PCMCIA/i82092: ad...
169
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
170
  }
e765a02cb   Bill Pemberton   pcmcia: remove us...
171
  static void i82092aa_pci_remove(struct pci_dev *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
172
  {
36c286d5a   Dan Carpenter   pcmcia: i82092: f...
173
  	int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
  	free_irq(dev->irq, i82092aa_interrupt);
36c286d5a   Dan Carpenter   pcmcia: i82092: f...
176
177
  	for (i = 0; i < socket_count; i++)
  		pcmcia_unregister_socket(&sockets[i].socket);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178
179
180
181
182
183
184
185
186
187
188
  }
  
  static DEFINE_SPINLOCK(port_lock);
  
  /* basic value read/write functions */
  
  static unsigned char indirect_read(int socket, unsigned short reg)
  {
  	unsigned short int port;
  	unsigned char val;
  	unsigned long flags;
4ae66dd77   Simon Geis   PCMCIA/i82092: in...
189

4839879f8   Simon Geis   PCMCIA/i82092: ad...
190
  	spin_lock_irqsave(&port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
192
  	reg += socket * 0x40;
  	port = sockets[socket].io_base;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
193
  	outb(reg, port);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
  	val = inb(port+1);
4839879f8   Simon Geis   PCMCIA/i82092: ad...
195
  	spin_unlock_irqrestore(&port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
197
  	return val;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198
199
200
201
  static void indirect_write(int socket, unsigned short reg, unsigned char value)
  {
  	unsigned short int port;
  	unsigned long flags;
4ae66dd77   Simon Geis   PCMCIA/i82092: in...
202

4839879f8   Simon Geis   PCMCIA/i82092: ad...
203
  	spin_lock_irqsave(&port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
204
  	reg = reg + socket * 0x40;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
205
206
207
208
  	port = sockets[socket].io_base;
  	outb(reg, port);
  	outb(value, port+1);
  	spin_unlock_irqrestore(&port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
210
211
212
213
214
215
  }
  
  static void indirect_setbit(int socket, unsigned short reg, unsigned char mask)
  {
  	unsigned short int port;
  	unsigned char val;
  	unsigned long flags;
4ae66dd77   Simon Geis   PCMCIA/i82092: in...
216

4839879f8   Simon Geis   PCMCIA/i82092: ad...
217
  	spin_lock_irqsave(&port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218
  	reg = reg + socket * 0x40;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
219
220
  	port = sockets[socket].io_base;
  	outb(reg, port);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
221
222
  	val = inb(port+1);
  	val |= mask;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
223
224
225
  	outb(reg, port);
  	outb(val, port+1);
  	spin_unlock_irqrestore(&port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
  }
152b4bb57   Simon Geis   PCMCIA/i82092: sh...
227
228
  static void indirect_resetbit(int socket,
  			      unsigned short reg, unsigned char mask)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
230
231
232
  {
  	unsigned short int port;
  	unsigned char val;
  	unsigned long flags;
4ae66dd77   Simon Geis   PCMCIA/i82092: in...
233

4839879f8   Simon Geis   PCMCIA/i82092: ad...
234
  	spin_lock_irqsave(&port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
235
  	reg = reg + socket * 0x40;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
236
237
  	port = sockets[socket].io_base;
  	outb(reg, port);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238
239
  	val = inb(port+1);
  	val &= ~mask;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
240
241
242
  	outb(reg, port);
  	outb(val, port+1);
  	spin_unlock_irqrestore(&port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243
  }
152b4bb57   Simon Geis   PCMCIA/i82092: sh...
244
245
  static void indirect_write16(int socket,
  			     unsigned short reg, unsigned short value)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
247
248
249
  {
  	unsigned short int port;
  	unsigned char val;
  	unsigned long flags;
4ae66dd77   Simon Geis   PCMCIA/i82092: in...
250

4839879f8   Simon Geis   PCMCIA/i82092: ad...
251
  	spin_lock_irqsave(&port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
  	reg = reg + socket * 0x40;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
253
254
255
  	port = sockets[socket].io_base;
  
  	outb(reg, port);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
  	val = value & 255;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
257
  	outb(val, port+1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258
  	reg++;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
259
260
  
  	outb(reg, port);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
  	val = value>>8;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
262
263
  	outb(val, port+1);
  	spin_unlock_irqrestore(&port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
265
266
267
268
269
270
271
  }
  
  /* simple helper functions */
  /* External clock time, in nanoseconds.  120 ns = 8.33 MHz */
  static int cycle_time = 120;
  
  static int to_cycles(int ns)
  {
4839879f8   Simon Geis   PCMCIA/i82092: ad...
272
  	if (cycle_time != 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
274
275
276
  		return ns/cycle_time;
  	else
  		return 0;
  }
4839879f8   Simon Geis   PCMCIA/i82092: ad...
277

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278
279
  
  /* Interrupt handler functionality */
7d12e780e   David Howells   IRQ: Maintain reg...
280
  static irqreturn_t i82092aa_interrupt(int irq, void *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
282
283
284
  {
  	int i;
  	int loopcount = 0;
  	int handled = 0;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
285
  	unsigned int events, active = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286
287
  	while (1) {
  		loopcount++;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
288
  		if (loopcount > 20) {
26a0a1041   Simon Geis   PCMCIA/i82092: us...
289
290
  			pr_err("i82092aa: infinite eventloop in interrupt
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
291
292
  			break;
  		}
4839879f8   Simon Geis   PCMCIA/i82092: ad...
293

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
  		active = 0;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
295
296
  
  		for (i = 0; i < socket_count; i++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
297
  			int csc;
4ae66dd77   Simon Geis   PCMCIA/i82092: in...
298

152b4bb57   Simon Geis   PCMCIA/i82092: sh...
299
300
  			/* Inactive socket, should not happen */
  			if (sockets[i].card_state == 0)
4839879f8   Simon Geis   PCMCIA/i82092: ad...
301
  				continue;
152b4bb57   Simon Geis   PCMCIA/i82092: sh...
302
303
  			/* card status change register */
  			csc = indirect_read(i, I365_CSC);
4839879f8   Simon Geis   PCMCIA/i82092: ad...
304
305
  
  			if (csc == 0)  /* no events on this socket */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
  				continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307
308
  			handled = 1;
  			events = 0;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
309

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
310
311
  			if (csc & I365_CSC_DETECT) {
  				events |= SS_DETECT;
26a0a1041   Simon Geis   PCMCIA/i82092: us...
312
313
314
  				dev_info(&sockets[i].dev->dev,
  					 "Card detected in socket %i!
  ", i);
4839879f8   Simon Geis   PCMCIA/i82092: ad...
315
316
317
  			}
  
  			if (indirect_read(i, I365_INTCTL) & I365_PC_IOCARD) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318
  				/* For IO/CARDS, bit 0 means "read the card" */
152b4bb57   Simon Geis   PCMCIA/i82092: sh...
319
320
  				if (csc & I365_CSC_STSCHG)
  					events |= SS_STSCHG;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
321
322
  			} else {
  				/* Check for battery/ready events */
152b4bb57   Simon Geis   PCMCIA/i82092: sh...
323
324
325
326
327
328
  				if (csc & I365_CSC_BVD1)
  					events |= SS_BATDEAD;
  				if (csc & I365_CSC_BVD2)
  					events |= SS_BATWARN;
  				if (csc & I365_CSC_READY)
  					events |= SS_READY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
329
  			}
4839879f8   Simon Geis   PCMCIA/i82092: ad...
330

ae1f62c54   Simon Geis   PCMCIA/i82092: re...
331
  			if (events)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
332
  				pcmcia_parse_events(&sockets[i].socket, events);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333
334
  			active |= events;
  		}
4839879f8   Simon Geis   PCMCIA/i82092: ad...
335
336
337
  
  		if (active == 0) /* no more events to handle */
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338
339
  	}
  	return IRQ_RETVAL(handled);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
341
342
343
344
345
346
  }
  
  
  
  /* socket functions */
  
  static int card_present(int socketno)
4839879f8   Simon Geis   PCMCIA/i82092: ad...
347
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
348
  	unsigned int val;
4ae66dd77   Simon Geis   PCMCIA/i82092: in...
349

4839879f8   Simon Geis   PCMCIA/i82092: ad...
350
  	if ((socketno < 0) || (socketno >= MAX_SOCKETS))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
352
353
  		return 0;
  	if (sockets[socketno].io_base == 0)
  		return 0;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
354

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
355
  	val = indirect_read(socketno, 1); /* Interface status register */
52739f063   Simon Geis   PCMCIA/i82092: de...
356
  	if ((val&12) == 12)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
  		return 1;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
358

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359
360
361
362
363
  	return 0;
  }
  
  static void set_bridge_state(int sock)
  {
4839879f8   Simon Geis   PCMCIA/i82092: ad...
364
365
366
367
  	indirect_write(sock, I365_GBLCTL, 0x00);
  	indirect_write(sock, I365_GENCTL, 0x00);
  
  	indirect_setbit(sock, I365_INTCTL, 0x08);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
369
370
371
372
  static int i82092aa_init(struct pcmcia_socket *sock)
  {
  	int i;
  	struct resource res = { .start = 0, .end = 0x0fff };
4839879f8   Simon Geis   PCMCIA/i82092: ad...
373
  	pccard_io_map io = { 0, 0, 0, 0, 1 };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
374
  	pccard_mem_map mem = { .res = &res, };
4839879f8   Simon Geis   PCMCIA/i82092: ad...
375

4839879f8   Simon Geis   PCMCIA/i82092: ad...
376
377
378
  	for (i = 0; i < 2; i++) {
  		io.map = i;
  		i82092aa_set_io_map(sock, &io);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
  	}
4839879f8   Simon Geis   PCMCIA/i82092: ad...
380
381
382
  	for (i = 0; i < 5; i++) {
  		mem.map = i;
  		i82092aa_set_mem_map(sock, &mem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383
  	}
52739f063   Simon Geis   PCMCIA/i82092: de...
384

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
385
386
  	return 0;
  }
4839879f8   Simon Geis   PCMCIA/i82092: ad...
387

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
388
389
  static int i82092aa_get_status(struct pcmcia_socket *socket, u_int *value)
  {
152b4bb57   Simon Geis   PCMCIA/i82092: sh...
390
391
  	unsigned int sock = container_of(socket,
  				struct socket_info, socket)->number;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
392
  	unsigned int status;
152b4bb57   Simon Geis   PCMCIA/i82092: sh...
393
394
395
  
  	/* Interface Status Register */
  	status = indirect_read(sock, I365_STATUS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
396
  	*value = 0;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
397

ae1f62c54   Simon Geis   PCMCIA/i82092: re...
398
  	if ((status & I365_CS_DETECT) == I365_CS_DETECT)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
399
  		*value |= SS_DETECT;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
400

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
401
402
  	/* IO cards have a different meaning of bits 0,1 */
  	/* Also notice the inverse-logic on the bits */
4839879f8   Simon Geis   PCMCIA/i82092: ad...
403
  	if (indirect_read(sock, I365_INTCTL) & I365_PC_IOCARD) {
84182fc7c   Colin Ian King   pcmcia: clean an ...
404
405
406
407
408
409
410
411
412
  		/* IO card */
  		if (!(status & I365_CS_STSCHG))
  			*value |= SS_STSCHG;
  	} else { /* non I/O card */
  		if (!(status & I365_CS_BVD1))
  			*value |= SS_BATDEAD;
  		if (!(status & I365_CS_BVD2))
  			*value |= SS_BATWARN;
  	}
4839879f8   Simon Geis   PCMCIA/i82092: ad...
413

84182fc7c   Colin Ian King   pcmcia: clean an ...
414
415
  	if (status & I365_CS_WRPROT)
  		(*value) |= SS_WRPROT;	/* card is write protected */
4839879f8   Simon Geis   PCMCIA/i82092: ad...
416

84182fc7c   Colin Ian King   pcmcia: clean an ...
417
418
  	if (status & I365_CS_READY)
  		(*value) |= SS_READY;    /* card is not busy */
4839879f8   Simon Geis   PCMCIA/i82092: ad...
419

84182fc7c   Colin Ian King   pcmcia: clean an ...
420
421
  	if (status & I365_CS_POWERON)
  		(*value) |= SS_POWERON;  /* power is applied to the card */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
422

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
423
424
  	return 0;
  }
152b4bb57   Simon Geis   PCMCIA/i82092: sh...
425
426
  static int i82092aa_set_socket(struct pcmcia_socket *socket,
  			       socket_state_t *state)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
427
  {
26a0a1041   Simon Geis   PCMCIA/i82092: us...
428
429
430
  	struct socket_info *sock_info = container_of(socket, struct socket_info,
  						     socket);
  	unsigned int sock = sock_info->number;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
431
  	unsigned char reg;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
432

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
433
  	/* First, set the global controller options */
4839879f8   Simon Geis   PCMCIA/i82092: ad...
434

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
435
  	set_bridge_state(sock);
4839879f8   Simon Geis   PCMCIA/i82092: ad...
436

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
437
  	/* Values for the IGENC register */
4839879f8   Simon Geis   PCMCIA/i82092: ad...
438

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
439
  	reg = 0;
152b4bb57   Simon Geis   PCMCIA/i82092: sh...
440
441
442
  
  	/* The reset bit has "inverse" logic */
  	if (!(state->flags & SS_RESET))
4839879f8   Simon Geis   PCMCIA/i82092: ad...
443
444
  		reg = reg | I365_PC_RESET;
  	if (state->flags & SS_IOCARD)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
445
  		reg = reg | I365_PC_IOCARD;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
446

152b4bb57   Simon Geis   PCMCIA/i82092: sh...
447
448
  	/* IGENC, Interrupt and General Control Register */
  	indirect_write(sock, I365_INTCTL, reg);
4839879f8   Simon Geis   PCMCIA/i82092: ad...
449

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
450
  	/* Power registers */
4839879f8   Simon Geis   PCMCIA/i82092: ad...
451

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
452
  	reg = I365_PWR_NORESET; /* default: disable resetdrv on resume */
4839879f8   Simon Geis   PCMCIA/i82092: ad...
453

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454
  	if (state->flags & SS_PWR_AUTO) {
26a0a1041   Simon Geis   PCMCIA/i82092: us...
455
456
  		dev_info(&sock_info->dev->dev, "Auto power
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
457
458
459
  		reg |= I365_PWR_AUTO;	/* automatic power mngmnt */
  	}
  	if (state->flags & SS_OUTPUT_ENA) {
26a0a1041   Simon Geis   PCMCIA/i82092: us...
460
461
  		dev_info(&sock_info->dev->dev, "Power Enabled
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
462
463
  		reg |= I365_PWR_OUT;	/* enable power */
  	}
4839879f8   Simon Geis   PCMCIA/i82092: ad...
464

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
465
  	switch (state->Vcc) {
6aaf8ff32   Simon Geis   PCMCIA/i82092: ch...
466
467
468
469
470
471
472
473
474
475
476
477
478
  	case 0:
  		break;
  	case 50:
  		dev_info(&sock_info->dev->dev,
  			 "setting voltage to Vcc to 5V on socket %i
  ",
  			 sock);
  		reg |= I365_VCC_5V;
  		break;
  	default:
  		dev_err(&sock_info->dev->dev,
  			"%s called with invalid VCC power value: %i",
  			__func__, state->Vcc);
6aaf8ff32   Simon Geis   PCMCIA/i82092: ch...
479
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
480
  	}
4839879f8   Simon Geis   PCMCIA/i82092: ad...
481

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
482
  	switch (state->Vpp) {
6aaf8ff32   Simon Geis   PCMCIA/i82092: ch...
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
  	case 0:
  		dev_info(&sock_info->dev->dev,
  			 "not setting Vpp on socket %i
  ", sock);
  		break;
  	case 50:
  		dev_info(&sock_info->dev->dev,
  			 "setting Vpp to 5.0 for socket %i
  ", sock);
  		reg |= I365_VPP1_5V | I365_VPP2_5V;
  		break;
  	case 120:
  		dev_info(&sock_info->dev->dev, "setting Vpp to 12.0
  ");
  		reg |= I365_VPP1_12V | I365_VPP2_12V;
  		break;
  	default:
  		dev_err(&sock_info->dev->dev,
  			"%s called with invalid VPP power value: %i",
  			__func__, state->Vcc);
6aaf8ff32   Simon Geis   PCMCIA/i82092: ch...
503
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
504
  	}
4839879f8   Simon Geis   PCMCIA/i82092: ad...
505
506
507
  
  	if (reg != indirect_read(sock, I365_POWER)) /* only write if changed */
  		indirect_write(sock, I365_POWER, reg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
508
  	/* Enable specific interrupt events */
4839879f8   Simon Geis   PCMCIA/i82092: ad...
509

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
510
  	reg = 0x00;
ae1f62c54   Simon Geis   PCMCIA/i82092: re...
511
  	if (state->csc_mask & SS_DETECT)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
512
  		reg |= I365_CSC_DETECT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
513
514
515
516
  	if (state->flags & SS_IOCARD) {
  		if (state->csc_mask & SS_STSCHG)
  			reg |= I365_CSC_STSCHG;
  	} else {
4839879f8   Simon Geis   PCMCIA/i82092: ad...
517
  		if (state->csc_mask & SS_BATDEAD)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
518
  			reg |= I365_CSC_BVD1;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
519
  		if (state->csc_mask & SS_BATWARN)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
520
  			reg |= I365_CSC_BVD2;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
521
522
  		if (state->csc_mask & SS_READY)
  			reg |= I365_CSC_READY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
523
  	}
4839879f8   Simon Geis   PCMCIA/i82092: ad...
524

152b4bb57   Simon Geis   PCMCIA/i82092: sh...
525
526
527
  	/* now write the value and clear the (probably bogus) pending stuff
  	 * by doing a dummy read
  	 */
4839879f8   Simon Geis   PCMCIA/i82092: ad...
528
529
530
  
  	indirect_write(sock, I365_CSCINT, reg);
  	(void)indirect_read(sock, I365_CSC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
531

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
532
533
  	return 0;
  }
152b4bb57   Simon Geis   PCMCIA/i82092: sh...
534
535
  static int i82092aa_set_io_map(struct pcmcia_socket *socket,
  			       struct pccard_io_map *io)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
536
  {
26a0a1041   Simon Geis   PCMCIA/i82092: us...
537
538
539
  	struct socket_info *sock_info = container_of(socket, struct socket_info,
  						     socket);
  	unsigned int sock = sock_info->number;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
540
  	unsigned char map, ioctl;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
541

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
542
  	map = io->map;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
543
544
  
  	/* Check error conditions */
52739f063   Simon Geis   PCMCIA/i82092: de...
545
  	if (map > 1)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
546
  		return -EINVAL;
52739f063   Simon Geis   PCMCIA/i82092: de...
547

152b4bb57   Simon Geis   PCMCIA/i82092: sh...
548
  	if ((io->start > 0xffff) || (io->stop > 0xffff)
52739f063   Simon Geis   PCMCIA/i82092: de...
549
  				 || (io->stop < io->start))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
550
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
551

4839879f8   Simon Geis   PCMCIA/i82092: ad...
552
  	/* Turn off the window before changing anything */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553
554
  	if (indirect_read(sock, I365_ADDRWIN) & I365_ENA_IO(map))
  		indirect_resetbit(sock, I365_ADDRWIN, I365_ENA_IO(map));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
555
  	/* write the new values */
4839879f8   Simon Geis   PCMCIA/i82092: ad...
556
557
558
559
  	indirect_write16(sock, I365_IO(map)+I365_W_START, io->start);
  	indirect_write16(sock, I365_IO(map)+I365_W_STOP, io->stop);
  
  	ioctl = indirect_read(sock, I365_IOCTL) & ~I365_IOCTL_MASK(map);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
560
561
  	if (io->flags & (MAP_16BIT|MAP_AUTOSZ))
  		ioctl |= I365_IOCTL_16BIT(map);
4839879f8   Simon Geis   PCMCIA/i82092: ad...
562
563
  
  	indirect_write(sock, I365_IOCTL, ioctl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564
565
  	/* Turn the window back on if needed */
  	if (io->flags & MAP_ACTIVE)
4839879f8   Simon Geis   PCMCIA/i82092: ad...
566
  		indirect_setbit(sock, I365_ADDRWIN, I365_ENA_IO(map));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
567
568
  	return 0;
  }
152b4bb57   Simon Geis   PCMCIA/i82092: sh...
569
570
  static int i82092aa_set_mem_map(struct pcmcia_socket *socket,
  				struct pccard_mem_map *mem)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
571
  {
152b4bb57   Simon Geis   PCMCIA/i82092: sh...
572
573
  	struct socket_info *sock_info = container_of(socket, struct socket_info,
  						     socket);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
574
575
576
577
  	unsigned int sock = sock_info->number;
  	struct pci_bus_region region;
  	unsigned short base, i;
  	unsigned char map;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
578

fc2798502   Yinghai Lu   PCI: Convert pcib...
579
  	pcibios_resource_to_bus(sock_info->dev->bus, &region, mem->res);
4839879f8   Simon Geis   PCMCIA/i82092: ad...
580

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
581
  	map = mem->map;
52739f063   Simon Geis   PCMCIA/i82092: de...
582
  	if (map > 4)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
583
  		return -EINVAL;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
584
585
586
  
  	if ((mem->card_start > 0x3ffffff) || (region.start > region.end) ||
  	     (mem->speed > 1000)) {
26a0a1041   Simon Geis   PCMCIA/i82092: us...
587
  		dev_err(&sock_info->dev->dev,
152b4bb57   Simon Geis   PCMCIA/i82092: sh...
588
589
  			"invalid mem map for socket %i: %llx to %llx with a start of %x
  ",
f96ee7a41   Andrew Morton   PCI: drivers/pcmc...
590
591
592
593
  			sock,
  			(unsigned long long)region.start,
  			(unsigned long long)region.end,
  			mem->card_start);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
594
595
  		return -EINVAL;
  	}
4839879f8   Simon Geis   PCMCIA/i82092: ad...
596

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
597
598
  	/* Turn off the window before changing anything */
  	if (indirect_read(sock, I365_ADDRWIN) & I365_ENA_MEM(map))
4839879f8   Simon Geis   PCMCIA/i82092: ad...
599
  		indirect_resetbit(sock, I365_ADDRWIN, I365_ENA_MEM(map));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
600
601
602
  	/* write the start address */
  	base = I365_MEM(map);
  	i = (region.start >> 12) & 0x0fff;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
603
  	if (mem->flags & MAP_16BIT)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
604
605
  		i |= I365_MEM_16BIT;
  	if (mem->flags & MAP_0WS)
4839879f8   Simon Geis   PCMCIA/i82092: ad...
606
607
  		i |= I365_MEM_0WS;
  	indirect_write16(sock, base+I365_W_START, i);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
608
  	/* write the stop address */
4839879f8   Simon Geis   PCMCIA/i82092: ad...
609
610
  
  	i = (region.end >> 12) & 0x0fff;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
611
  	switch (to_cycles(mem->speed)) {
6aaf8ff32   Simon Geis   PCMCIA/i82092: ch...
612
613
614
615
616
617
618
619
620
621
622
  	case 0:
  		break;
  	case 1:
  		i |= I365_MEM_WS0;
  		break;
  	case 2:
  		i |= I365_MEM_WS1;
  		break;
  	default:
  		i |= I365_MEM_WS1 | I365_MEM_WS0;
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
623
  	}
4839879f8   Simon Geis   PCMCIA/i82092: ad...
624
625
  
  	indirect_write16(sock, base+I365_W_STOP, i);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
626
  	/* card start */
4839879f8   Simon Geis   PCMCIA/i82092: ad...
627

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
628
629
630
  	i = ((mem->card_start - region.start) >> 12) & 0x3fff;
  	if (mem->flags & MAP_WRPROT)
  		i |= I365_MEM_WRPROT;
26a0a1041   Simon Geis   PCMCIA/i82092: us...
631
  	if (mem->flags & MAP_ATTRIB)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
632
  		i |= I365_MEM_REG;
4839879f8   Simon Geis   PCMCIA/i82092: ad...
633
  	indirect_write16(sock, base+I365_W_OFF, i);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
634
635
636
  	/* Enable the window if necessary */
  	if (mem->flags & MAP_ACTIVE)
  		indirect_setbit(sock, I365_ADDRWIN, I365_ENA_MEM(map));
4839879f8   Simon Geis   PCMCIA/i82092: ad...
637

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
638
639
640
641
642
  	return 0;
  }
  
  static int i82092aa_module_init(void)
  {
ba66ddfa6   Sam Ravnborg   pcmcia: silence s...
643
  	return pci_register_driver(&i82092aa_pci_driver);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
644
645
646
647
  }
  
  static void i82092aa_module_exit(void)
  {
ba66ddfa6   Sam Ravnborg   pcmcia: silence s...
648
  	pci_unregister_driver(&i82092aa_pci_driver);
4839879f8   Simon Geis   PCMCIA/i82092: ad...
649
  	if (sockets[0].io_base > 0)
6aaf8ff32   Simon Geis   PCMCIA/i82092: ch...
650
  		release_region(sockets[0].io_base, 2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
651
652
653
654
  }
  
  module_init(i82092aa_module_init);
  module_exit(i82092aa_module_exit);