Blame view

drivers/sn/ioc3.c 20 KB
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
1
2
3
4
5
6
7
8
  /*
   * SGI IOC3 master driver and IRQ demuxer
   *
   * Copyright (c) 2005 Stanislaw Skowronek <skylark@linux-mips.org>
   * Heavily based on similar work by:
   *   Brent Casavant <bcasavan@sgi.com> - IOC4 master driver
   *   Pat Gefre <pfg@sgi.com> - IOC3 serial port IRQ demuxer
   */
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
9
10
11
  #include <linux/errno.h>
  #include <linux/module.h>
  #include <linux/pci.h>
56b146d36   Tobias Klauser   [PATCH] Last DMA_...
12
  #include <linux/dma-mapping.h>
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
13
14
15
16
17
  #include <linux/interrupt.h>
  #include <linux/spinlock.h>
  #include <linux/delay.h>
  #include <linux/ioc3.h>
  #include <linux/rwsem.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
18
  #include <linux/slab.h>
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
19
20
21
22
23
24
25
26
27
  
  #define IOC3_PCI_SIZE 0x100000
  
  static LIST_HEAD(ioc3_devices);
  static int ioc3_counter;
  static DECLARE_RWSEM(ioc3_devices_rwsem);
  
  static struct ioc3_submodule *ioc3_submodules[IOC3_MAX_SUBMODULES];
  static struct ioc3_submodule *ioc3_ethernet;
34af946a2   Ingo Molnar   [PATCH] spin/rwlo...
28
  static DEFINE_RWLOCK(ioc3_submodules_lock);
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
29
30
31
32
33
34
35
36
37
38
39
40
  
  /* NIC probing code */
  
  #define GPCR_MLAN_EN    0x00200000      /* enable MCR to pin 8 */
  
  static inline unsigned mcr_pack(unsigned pulse, unsigned sample)
  {
  	return (pulse << 10) | (sample << 2);
  }
  
  static int nic_wait(struct ioc3_driver_data *idd)
  {
d65610100   Al Viro   [PATCH] sn3 iomem...
41
  	unsigned mcr;
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
42
43
  
          do {
d65610100   Al Viro   [PATCH] sn3 iomem...
44
                  mcr = readl(&idd->vma->mcr);
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
45
46
47
48
49
50
51
52
53
54
55
          } while (!(mcr & 2));
  
          return mcr & 1;
  }
  
  static int nic_reset(struct ioc3_driver_data *idd)
  {
          int presence;
  	unsigned long flags;
  
  	local_irq_save(flags);
d65610100   Al Viro   [PATCH] sn3 iomem...
56
  	writel(mcr_pack(500, 65), &idd->vma->mcr);
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
57
58
59
60
61
62
63
  	presence = nic_wait(idd);
  	local_irq_restore(flags);
  
  	udelay(500);
  
          return presence;
  }
15b370c95   Pat Gefre   [PATCH] Altix: mo...
64
  static int nic_read_bit(struct ioc3_driver_data *idd)
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
65
66
67
68
69
  {
  	int result;
  	unsigned long flags;
  
  	local_irq_save(flags);
d65610100   Al Viro   [PATCH] sn3 iomem...
70
  	writel(mcr_pack(6, 13), &idd->vma->mcr);
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
71
72
73
74
75
76
77
  	result = nic_wait(idd);
  	local_irq_restore(flags);
  
  	udelay(500);
  
  	return result;
  }
15b370c95   Pat Gefre   [PATCH] Altix: mo...
78
  static void nic_write_bit(struct ioc3_driver_data *idd, int bit)
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
79
80
  {
  	if (bit)
d65610100   Al Viro   [PATCH] sn3 iomem...
81
  		writel(mcr_pack(6, 110), &idd->vma->mcr);
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
82
  	else
d65610100   Al Viro   [PATCH] sn3 iomem...
83
  		writel(mcr_pack(80, 30), &idd->vma->mcr);
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
  
  	nic_wait(idd);
  }
  
  static unsigned nic_read_byte(struct ioc3_driver_data *idd)
  {
  	unsigned result = 0;
  	int i;
  
  	for (i = 0; i < 8; i++)
  		result = (result >> 1) | (nic_read_bit(idd) << 7);
  
  	return result;
  }
  
  static void nic_write_byte(struct ioc3_driver_data *idd, int byte)
  {
  	int i, bit;
  
  	for (i = 8; i; i--) {
  		bit = byte & 1;
  		byte >>= 1;
  
  		nic_write_bit(idd, bit);
  	}
  }
  
  static unsigned long
  nic_find(struct ioc3_driver_data *idd, int *last, unsigned long addr)
  {
  	int a, b, index, disc;
  
  	nic_reset(idd);
  
  	/* Search ROM.  */
  	nic_write_byte(idd, 0xF0);
  
  	/* Algorithm from ``Book of iButton Standards''.  */
  	for (index = 0, disc = 0; index < 64; index++) {
  		a = nic_read_bit(idd);
  		b = nic_read_bit(idd);
  
  		if (a && b) {
  			printk(KERN_WARNING "IOC3 NIC search failed.
  ");
  			*last = 0;
  			return 0;
  		}
  
  		if (!a && !b) {
  			if (index == *last) {
  				addr |= 1UL << index;
  			} else if (index > *last) {
  				addr &= ~(1UL << index);
  				disc = index;
  			} else if ((addr & (1UL << index)) == 0)
  				disc = index;
  			nic_write_bit(idd, (addr>>index)&1);
  			continue;
  		} else {
  			if (a)
  				addr |= 1UL << index;
  			else
  				addr &= ~(1UL << index);
  			nic_write_bit(idd, a);
  			continue;
  		}
  	}
  	*last = disc;
  	return addr;
  }
  
  static void nic_addr(struct ioc3_driver_data *idd, unsigned long addr)
  {
  	int index;
  
  	nic_reset(idd);
  	nic_write_byte(idd, 0xF0);
  	for (index = 0; index < 64; index++) {
  		nic_read_bit(idd);
  		nic_read_bit(idd);
  		nic_write_bit(idd, (addr>>index)&1);
  	}
  }
  
  static void crc16_byte(unsigned int *crc, unsigned char db)
  {
  	int i;
  
  	for(i=0;i<8;i++) {
  		*crc <<= 1;
  		if((db^(*crc>>16)) & 1)
  			*crc ^= 0x8005;
  		db >>= 1;
  	}
  	*crc &= 0xFFFF;
  }
  
  static unsigned int crc16_area(unsigned char *dbs, int size, unsigned int crc)
  {
  	while(size--)
  		crc16_byte(&crc, *(dbs++));
  	return crc;
  }
  
  static void crc8_byte(unsigned int *crc, unsigned char db)
  {
  	int i,f;
  
  	for(i=0;i<8;i++) {
  		f = (*crc ^ db) & 1;
  		*crc >>= 1;
  		db >>= 1;
  		if(f)
  			*crc ^= 0x8c;
  	}
  	*crc &= 0xff;
  }
  
  static unsigned int crc8_addr(unsigned long addr)
  {
  	int i;
  	unsigned int crc = 0x00;
  
  	for(i=0;i<8;i++)
  		crc8_byte(&crc, addr>>(i<<3));
  	return crc;
  }
  
  static void
  read_redir_page(struct ioc3_driver_data *idd, unsigned long addr, int page,
  			unsigned char *redir, unsigned char *data)
  {
  	int loops = 16, i;
  
  	while(redir[page] != 0xFF) {
  		page = redir[page]^0xFF;
  		loops--;
  		if(loops<0) {
  			printk(KERN_ERR "IOC3: NIC circular redirection
  ");
  			return;
  		}
  	}
  	loops = 3;
  	while(loops>0) {
  		nic_addr(idd, addr);
  		nic_write_byte(idd, 0xF0);
  		nic_write_byte(idd, (page << 5) & 0xE0);
  		nic_write_byte(idd, (page >> 3) & 0x1F);
  		for(i=0;i<0x20;i++)
  			data[i] = nic_read_byte(idd);
  		if(crc16_area(data, 0x20, 0x0000) == 0x800d)
  			return;
  		loops--;
  	}
  	printk(KERN_ERR "IOC3: CRC error in data page
  ");
  	for(i=0;i<0x20;i++)
  		data[i] = 0x00;
  }
  
  static void
  read_redir_map(struct ioc3_driver_data *idd, unsigned long addr,
  					 unsigned char *redir)
  {
  	int i,j,loops = 3,crc_ok;
  	unsigned int crc;
  
  	while(loops>0) {
  		crc_ok = 1;
  		nic_addr(idd, addr);
  		nic_write_byte(idd, 0xAA);
  		nic_write_byte(idd, 0x00);
  		nic_write_byte(idd, 0x01);
  		for(i=0;i<64;i+=8) {
  			for(j=0;j<8;j++)
  				redir[i+j] = nic_read_byte(idd);
  			crc = crc16_area(redir+i, 8, (i==0)?0x8707:0x0000);
  			crc16_byte(&crc, nic_read_byte(idd));
  			crc16_byte(&crc, nic_read_byte(idd));
  			if(crc != 0x800d)
  				crc_ok = 0;
  		}
  		if(crc_ok)
  			return;
  		loops--;
  	}
  	printk(KERN_ERR "IOC3: CRC error in redirection page
  ");
  	for(i=0;i<64;i++)
  		redir[i] = 0xFF;
  }
  
  static void read_nic(struct ioc3_driver_data *idd, unsigned long addr)
  {
  	unsigned char redir[64];
  	unsigned char data[64],part[32];
  	int i,j;
  
  	/* read redirections */
  	read_redir_map(idd, addr, redir);
  	/* read data pages */
  	read_redir_page(idd, addr, 0, redir, data);
  	read_redir_page(idd, addr, 1, redir, data+32);
  	/* assemble the part # */
  	j=0;
  	for(i=0;i<19;i++)
  		if(data[i+11] != ' ')
  			part[j++] = data[i+11];
  	for(i=0;i<6;i++)
  		if(data[i+32] != ' ')
  			part[j++] = data[i+32];
  	part[j] = 0;
  	/* skip Octane power supplies */
  	if(!strncmp(part, "060-0035-", 9))
  		return;
  	if(!strncmp(part, "060-0038-", 9))
  		return;
  	strcpy(idd->nic_part, part);
  	/* assemble the serial # */
  	j=0;
  	for(i=0;i<10;i++)
  		if(data[i+1] != ' ')
  			idd->nic_serial[j++] = data[i+1];
  	idd->nic_serial[j] = 0;
  }
  
  static void read_mac(struct ioc3_driver_data *idd, unsigned long addr)
  {
  	int i, loops = 3;
  	unsigned char data[13];
  
  	while(loops>0) {
  		nic_addr(idd, addr);
  		nic_write_byte(idd, 0xF0);
  		nic_write_byte(idd, 0x00);
  		nic_write_byte(idd, 0x00);
  		nic_read_byte(idd);
  		for(i=0;i<13;i++)
  			data[i] = nic_read_byte(idd);
  		if(crc16_area(data, 13, 0x0000) == 0x800d) {
  			for(i=10;i>4;i--)
  				idd->nic_mac[10-i] = data[i];
  			return;
  		}
  		loops--;
  	}
  	printk(KERN_ERR "IOC3: CRC error in MAC address
  ");
  	for(i=0;i<6;i++)
  		idd->nic_mac[i] = 0x00;
  }
  
  static void probe_nic(struct ioc3_driver_data *idd)
  {
          int save = 0, loops = 3;
          unsigned long first, addr;
d65610100   Al Viro   [PATCH] sn3 iomem...
342
          writel(GPCR_MLAN_EN, &idd->vma->gpcr_s);
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
  
          while(loops>0) {
                  idd->nic_part[0] = 0;
                  idd->nic_serial[0] = 0;
                  addr = first = nic_find(idd, &save, 0);
                  if(!first)
                          return;
                  while(1) {
                          if(crc8_addr(addr))
                                  break;
                          else {
                                  switch(addr & 0xFF) {
                                  case 0x0B:
                                          read_nic(idd, addr);
                                          break;
                                  case 0x09:
                                  case 0x89:
                                  case 0x91:
                                          read_mac(idd, addr);
                                          break;
                                  }
                          }
                          addr = nic_find(idd, &save, addr);
                          if(addr == first)
                                  return;
                  }
                  loops--;
          }
          printk(KERN_ERR "IOC3: CRC error in NIC address
  ");
  }
  
  /* Interrupts */
15b370c95   Pat Gefre   [PATCH] Altix: mo...
376
  static void write_ireg(struct ioc3_driver_data *idd, uint32_t val, int which)
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
  {
  	unsigned long flags;
  
  	spin_lock_irqsave(&idd->ir_lock, flags);
  	switch (which) {
  	case IOC3_W_IES:
  		writel(val, &idd->vma->sio_ies);
  		break;
  	case IOC3_W_IEC:
  		writel(val, &idd->vma->sio_iec);
  		break;
  	}
  	spin_unlock_irqrestore(&idd->ir_lock, flags);
  }
  static inline uint32_t get_pending_intrs(struct ioc3_driver_data *idd)
  {
  	unsigned long flag;
  	uint32_t intrs = 0;
  
  	spin_lock_irqsave(&idd->ir_lock, flag);
  	intrs = readl(&idd->vma->sio_ir);
  	intrs &= readl(&idd->vma->sio_ies);
  	spin_unlock_irqrestore(&idd->ir_lock, flag);
  	return intrs;
  }
7d12e780e   David Howells   IRQ: Maintain reg...
402
  static irqreturn_t ioc3_intr_io(int irq, void *arg)
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
403
404
  {
  	unsigned long flags;
c7bec5aba   Jeff Garzik   Various drivers' ...
405
  	struct ioc3_driver_data *idd = arg;
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
406
407
408
409
  	int handled = 1, id;
  	unsigned int pending;
  
  	read_lock_irqsave(&ioc3_submodules_lock, flags);
d65610100   Al Viro   [PATCH] sn3 iomem...
410
  	if(idd->dual_irq && readb(&idd->vma->eisr)) {
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
411
412
413
414
  		/* send Ethernet IRQ to the driver */
  		if(ioc3_ethernet && idd->active[ioc3_ethernet->id] &&
  						ioc3_ethernet->intr) {
  			handled = handled && !ioc3_ethernet->intr(ioc3_ethernet,
7d12e780e   David Howells   IRQ: Maintain reg...
415
  							idd, 0);
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
416
417
418
419
420
421
422
423
424
425
426
  		}
  	}
  	pending = get_pending_intrs(idd);	/* look at the IO IRQs */
  
  	for(id=0;id<IOC3_MAX_SUBMODULES;id++) {
  		if(idd->active[id] && ioc3_submodules[id]
  				&& (pending & ioc3_submodules[id]->irq_mask)
  				&& ioc3_submodules[id]->intr) {
  			write_ireg(idd, ioc3_submodules[id]->irq_mask,
  							IOC3_W_IEC);
  			if(!ioc3_submodules[id]->intr(ioc3_submodules[id],
7d12e780e   David Howells   IRQ: Maintain reg...
427
  				   idd, pending & ioc3_submodules[id]->irq_mask))
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
  				pending &= ~ioc3_submodules[id]->irq_mask;
  			if (ioc3_submodules[id]->reset_mask)
  				write_ireg(idd, ioc3_submodules[id]->irq_mask,
  							IOC3_W_IES);
  		}
  	}
  	read_unlock_irqrestore(&ioc3_submodules_lock, flags);
  	if(pending) {
  		printk(KERN_WARNING
  		  "IOC3: Pending IRQs 0x%08x discarded and disabled
  ",pending);
  		write_ireg(idd, pending, IOC3_W_IEC);
  		handled = 1;
  	}
  	return handled?IRQ_HANDLED:IRQ_NONE;
  }
7d12e780e   David Howells   IRQ: Maintain reg...
444
  static irqreturn_t ioc3_intr_eth(int irq, void *arg)
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
445
446
447
448
449
450
451
452
453
454
  {
  	unsigned long flags;
  	struct ioc3_driver_data *idd = (struct ioc3_driver_data *)arg;
  	int handled = 1;
  
  	if(!idd->dual_irq)
  		return IRQ_NONE;
  	read_lock_irqsave(&ioc3_submodules_lock, flags);
  	if(ioc3_ethernet && idd->active[ioc3_ethernet->id]
  				&& ioc3_ethernet->intr)
7d12e780e   David Howells   IRQ: Maintain reg...
455
  		handled = handled && !ioc3_ethernet->intr(ioc3_ethernet, idd, 0);
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
  	read_unlock_irqrestore(&ioc3_submodules_lock, flags);
  	return handled?IRQ_HANDLED:IRQ_NONE;
  }
  
  void ioc3_enable(struct ioc3_submodule *is,
  				struct ioc3_driver_data *idd, unsigned int irqs)
  {
  	write_ireg(idd, irqs & is->irq_mask, IOC3_W_IES);
  }
  
  void ioc3_ack(struct ioc3_submodule *is, struct ioc3_driver_data *idd,
  				unsigned int irqs)
  {
  	writel(irqs & is->irq_mask, &idd->vma->sio_ir);
  }
  
  void ioc3_disable(struct ioc3_submodule *is,
  				struct ioc3_driver_data *idd, unsigned int irqs)
  {
  	write_ireg(idd, irqs & is->irq_mask, IOC3_W_IEC);
  }
  
  void ioc3_gpcr_set(struct ioc3_driver_data *idd, unsigned int val)
  {
  	unsigned long flags;
  	spin_lock_irqsave(&idd->gpio_lock, flags);
  	writel(val, &idd->vma->gpcr_s);
  	spin_unlock_irqrestore(&idd->gpio_lock, flags);
  }
  
  /* Keep it simple, stupid! */
  static int find_slot(void **tab, int max)
  {
  	int i;
  	for(i=0;i<max;i++)
  		if(!(tab[i]))
  			return i;
  	return -1;
  }
  
  /* Register an IOC3 submodule */
  int ioc3_register_submodule(struct ioc3_submodule *is)
  {
  	struct ioc3_driver_data *idd;
  	int alloc_id;
  	unsigned long flags;
  
  	write_lock_irqsave(&ioc3_submodules_lock, flags);
  	alloc_id = find_slot((void **)ioc3_submodules, IOC3_MAX_SUBMODULES);
  	if(alloc_id != -1) {
  		ioc3_submodules[alloc_id] = is;
  		if(is->ethernet) {
  			if(ioc3_ethernet==NULL)
  				ioc3_ethernet=is;
  			else
  				printk(KERN_WARNING
  				  "IOC3 Ethernet module already registered!
  ");
  		}
  	}
  	write_unlock_irqrestore(&ioc3_submodules_lock, flags);
  
  	if(alloc_id == -1) {
  		printk(KERN_WARNING "Increase IOC3_MAX_SUBMODULES!
  ");
  		return -ENOMEM;
  	}
  
  	is->id=alloc_id;
  
  	/* Initialize submodule for each IOC3 */
  	if (!is->probe)
  		return 0;
  
  	down_read(&ioc3_devices_rwsem);
  	list_for_each_entry(idd, &ioc3_devices, list) {
  		/* set to 1 for IRQs in probe */
  		idd->active[alloc_id] = 1;
  		idd->active[alloc_id] = !is->probe(is, idd);
  	}
  	up_read(&ioc3_devices_rwsem);
  
  	return 0;
  }
  
  /* Unregister an IOC3 submodule */
  void ioc3_unregister_submodule(struct ioc3_submodule *is)
  {
  	struct ioc3_driver_data *idd;
  	unsigned long flags;
  
  	write_lock_irqsave(&ioc3_submodules_lock, flags);
  	if(ioc3_submodules[is->id]==is)
  		ioc3_submodules[is->id]=NULL;
  	else
  		printk(KERN_WARNING
  			"IOC3 submodule %s has wrong ID.
  ",is->name);
  	if(ioc3_ethernet==is)
  		ioc3_ethernet = NULL;
  	write_unlock_irqrestore(&ioc3_submodules_lock, flags);
  
  	/* Remove submodule for each IOC3 */
  	down_read(&ioc3_devices_rwsem);
  	list_for_each_entry(idd, &ioc3_devices, list)
  		if(idd->active[is->id]) {
  			if(is->remove)
  				if(is->remove(is, idd))
  					printk(KERN_WARNING
  					       "%s: IOC3 submodule %s remove failed "
  					       "for pci_dev %s.
  ",
9150c979c   Harvey Harrison   ioc3.c: replace r...
568
  					       __func__, module_name(is->owner),
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
569
570
571
572
573
574
575
576
577
578
579
  					       pci_name(idd->pdev));
  			idd->active[is->id] = 0;
  			if(is->irq_mask)
  				write_ireg(idd, is->irq_mask, IOC3_W_IEC);
  		}
  	up_read(&ioc3_devices_rwsem);
  }
  
  /*********************
   * Device management *
   *********************/
2ea5d35a4   Jean Delvare   ioc3/ioc4: variou...
580
  static char * __devinitdata
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
581
582
  ioc3_class_names[]={"unknown", "IP27 BaseIO", "IP30 system", "MENET 1/2/3",
  			"MENET 4", "CADduo", "Altix Serial"};
2ea5d35a4   Jean Delvare   ioc3/ioc4: variou...
583
  static int __devinit ioc3_class(struct ioc3_driver_data *idd)
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
  {
  	int res = IOC3_CLASS_NONE;
  	/* NIC-based logic */
  	if(!strncmp(idd->nic_part, "030-0891-", 9))
  		res = IOC3_CLASS_BASE_IP30;
  	if(!strncmp(idd->nic_part, "030-1155-", 9))
  		res = IOC3_CLASS_CADDUO;
  	if(!strncmp(idd->nic_part, "030-1657-", 9))
  		res = IOC3_CLASS_SERIAL;
  	if(!strncmp(idd->nic_part, "030-1664-", 9))
  		res = IOC3_CLASS_SERIAL;
  	/* total random heuristics */
  #ifdef CONFIG_SGI_IP27
  	if(!idd->nic_part[0])
  		res = IOC3_CLASS_BASE_IP27;
  #endif
  	/* print educational message */
  	printk(KERN_INFO "IOC3 part: [%s], serial: [%s] => class %s
  ",
  			idd->nic_part, idd->nic_serial, ioc3_class_names[res]);
  	return res;
  }
  /* Adds a new instance of an IOC3 card */
2ea5d35a4   Jean Delvare   ioc3/ioc4: variou...
607
608
  static int __devinit
  ioc3_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
609
610
611
612
613
614
615
616
617
618
  {
  	struct ioc3_driver_data *idd;
  	uint32_t pcmd;
  	int ret, id;
  
  	/* Enable IOC3 and take ownership of it */
  	if ((ret = pci_enable_device(pdev))) {
  		printk(KERN_WARNING
  		       "%s: Failed to enable IOC3 device for pci_dev %s.
  ",
9150c979c   Harvey Harrison   ioc3.c: replace r...
619
  		       __func__, pci_name(pdev));
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
620
621
622
623
624
  		goto out;
  	}
  	pci_set_master(pdev);
  
  #ifdef USE_64BIT_DMA
6a35528a8   Yang Hongyang   dma-mapping: repl...
625
          ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
626
          if (!ret) {
6a35528a8   Yang Hongyang   dma-mapping: repl...
627
                  ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
628
629
630
631
                  if (ret < 0) {
                          printk(KERN_WARNING "%s: Unable to obtain 64 bit DMA "
                                 "for consistent allocations
  ",
9150c979c   Harvey Harrison   ioc3.c: replace r...
632
  				__func__);
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
633
634
635
636
637
                  }
  	}
  #endif
  
  	/* Set up per-IOC3 data */
dd00cc486   Yoann Padioleau   some kmalloc/mems...
638
  	idd = kzalloc(sizeof(struct ioc3_driver_data), GFP_KERNEL);
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
639
640
641
642
  	if (!idd) {
  		printk(KERN_WARNING
  		       "%s: Failed to allocate IOC3 data for pci_dev %s.
  ",
9150c979c   Harvey Harrison   ioc3.c: replace r...
643
  		       __func__, pci_name(pdev));
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
644
645
646
  		ret = -ENODEV;
  		goto out_idd;
  	}
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
647
648
649
650
651
652
653
654
655
656
657
658
659
  	spin_lock_init(&idd->ir_lock);
  	spin_lock_init(&idd->gpio_lock);
  	idd->pdev = pdev;
  
  	/* Map all IOC3 registers.  These are shared between subdevices
  	 * so the main IOC3 module manages them.
  	 */
  	idd->pma = pci_resource_start(pdev, 0);
  	if (!idd->pma) {
  		printk(KERN_WARNING
  		       "%s: Unable to find IOC3 resource "
  		       "for pci_dev %s.
  ",
9150c979c   Harvey Harrison   ioc3.c: replace r...
660
  		       __func__, pci_name(pdev));
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
661
662
663
  		ret = -ENODEV;
  		goto out_pci;
  	}
52c9ae0ac   Brent Casavant   [PATCH] IOC3/IOC4...
664
  	if (!request_mem_region(idd->pma, IOC3_PCI_SIZE, "ioc3")) {
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
665
666
667
668
  		printk(KERN_WARNING
  		       "%s: Unable to request IOC3 region "
  		       "for pci_dev %s.
  ",
9150c979c   Harvey Harrison   ioc3.c: replace r...
669
  		       __func__, pci_name(pdev));
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
670
671
672
673
674
675
676
677
678
  		ret = -ENODEV;
  		goto out_pci;
  	}
  	idd->vma = ioremap(idd->pma, IOC3_PCI_SIZE);
  	if (!idd->vma) {
  		printk(KERN_WARNING
  		       "%s: Unable to remap IOC3 region "
  		       "for pci_dev %s.
  ",
9150c979c   Harvey Harrison   ioc3.c: replace r...
679
  		       __func__, pci_name(pdev));
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
680
681
682
683
684
685
686
  		ret = -ENODEV;
  		goto out_misc_region;
  	}
  
  	/* Track PCI-device specific data */
  	pci_set_drvdata(pdev, idd);
  	down_write(&ioc3_devices_rwsem);
2c43630fb   Pat Gefre   [PATCH] Altix: co...
687
  	list_add_tail(&idd->list, &ioc3_devices);
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
688
689
  	idd->id = ioc3_counter++;
  	up_write(&ioc3_devices_rwsem);
d65610100   Al Viro   [PATCH] sn3 iomem...
690
  	idd->gpdr_shadow = readl(&idd->vma->gpdr);
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
  
  	/* Read IOC3 NIC contents */
  	probe_nic(idd);
  
  	/* Detect IOC3 class */
  	idd->class = ioc3_class(idd);
  
  	/* Initialize IOC3 */
         pci_read_config_dword(pdev, PCI_COMMAND, &pcmd);
         pci_write_config_dword(pdev, PCI_COMMAND,
                                 pcmd | PCI_COMMAND_MEMORY |
                                 PCI_COMMAND_PARITY | PCI_COMMAND_SERR |
                                 PCI_SCR_DROP_MODE_EN);
  
  	write_ireg(idd, ~0, IOC3_W_IEC);
  	writel(~0, &idd->vma->sio_ir);
  
  	/* Set up IRQs */
  	if(idd->class == IOC3_CLASS_BASE_IP30
  				|| idd->class == IOC3_CLASS_BASE_IP27) {
  		writel(0, &idd->vma->eier);
  		writel(~0, &idd->vma->eisr);
  
  		idd->dual_irq = 1;
dace14537   Thomas Gleixner   [PATCH] irq-flags...
715
  		if (!request_irq(pdev->irq, ioc3_intr_eth, IRQF_SHARED,
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
716
717
718
719
720
721
  				 "ioc3-eth", (void *)idd)) {
  			idd->irq_eth = pdev->irq;
  		} else {
  			printk(KERN_WARNING
  			       "%s : request_irq fails for IRQ 0x%x
   ",
9150c979c   Harvey Harrison   ioc3.c: replace r...
722
  			       __func__, pdev->irq);
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
723
  		}
dace14537   Thomas Gleixner   [PATCH] irq-flags...
724
  		if (!request_irq(pdev->irq+2, ioc3_intr_io, IRQF_SHARED,
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
725
726
727
728
729
730
  				 "ioc3-io", (void *)idd)) {
  			idd->irq_io = pdev->irq+2;
  		} else {
  			printk(KERN_WARNING
  			       "%s : request_irq fails for IRQ 0x%x
   ",
9150c979c   Harvey Harrison   ioc3.c: replace r...
731
  			       __func__, pdev->irq+2);
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
732
733
  		}
  	} else {
dace14537   Thomas Gleixner   [PATCH] irq-flags...
734
  		if (!request_irq(pdev->irq, ioc3_intr_io, IRQF_SHARED,
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
735
736
737
738
739
740
  				 "ioc3", (void *)idd)) {
  			idd->irq_io = pdev->irq;
  		} else {
  			printk(KERN_WARNING
  			       "%s : request_irq fails for IRQ 0x%x
   ",
9150c979c   Harvey Harrison   ioc3.c: replace r...
741
  			       __func__, pdev->irq);
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
742
743
744
745
  		}
  	}
  
  	/* Add this IOC3 to all submodules */
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
746
747
748
749
750
751
  	for(id=0;id<IOC3_MAX_SUBMODULES;id++)
  		if(ioc3_submodules[id] && ioc3_submodules[id]->probe) {
  			idd->active[id] = 1;
  			idd->active[id] = !ioc3_submodules[id]->probe
  						(ioc3_submodules[id], idd);
  		}
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
752
753
754
755
756
757
758
  
  	printk(KERN_INFO "IOC3 Master Driver loaded for %s
  ", pci_name(pdev));
  
  	return 0;
  
  out_misc_region:
52c9ae0ac   Brent Casavant   [PATCH] IOC3/IOC4...
759
  	release_mem_region(idd->pma, IOC3_PCI_SIZE);
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
760
761
762
763
764
765
766
767
768
  out_pci:
  	kfree(idd);
  out_idd:
  	pci_disable_device(pdev);
  out:
  	return ret;
  }
  
  /* Removes a particular instance of an IOC3 card. */
2ea5d35a4   Jean Delvare   ioc3/ioc4: variou...
769
  static void __devexit ioc3_remove(struct pci_dev *pdev)
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
770
771
772
773
774
775
776
  {
  	int id;
  	struct ioc3_driver_data *idd;
  
  	idd = pci_get_drvdata(pdev);
  
  	/* Remove this IOC3 from all submodules */
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
777
778
779
780
781
782
783
784
785
  	for(id=0;id<IOC3_MAX_SUBMODULES;id++)
  		if(idd->active[id]) {
  			if(ioc3_submodules[id] && ioc3_submodules[id]->remove)
  				if(ioc3_submodules[id]->remove(ioc3_submodules[id],
  								idd))
  					printk(KERN_WARNING
  					       "%s: IOC3 submodule 0x%s remove failed "
  					       "for pci_dev %s.
  ",
9150c979c   Harvey Harrison   ioc3.c: replace r...
786
  						__func__,
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
787
788
789
790
  						module_name(ioc3_submodules[id]->owner),
  					        pci_name(pdev));
  			idd->active[id] = 0;
  		}
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
791
792
793
794
795
796
797
798
799
800
  
  	/* Clear and disable all IRQs */
  	write_ireg(idd, ~0, IOC3_W_IEC);
  	writel(~0, &idd->vma->sio_ir);
  
  	/* Release resources */
  	free_irq(idd->irq_io, (void *)idd);
  	if(idd->dual_irq)
  		free_irq(idd->irq_eth, (void *)idd);
  	iounmap(idd->vma);
52c9ae0ac   Brent Casavant   [PATCH] IOC3/IOC4...
801
  	release_mem_region(idd->pma, IOC3_PCI_SIZE);
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
  
  	/* Disable IOC3 and relinquish */
  	pci_disable_device(pdev);
  
  	/* Remove and free driver data */
  	down_write(&ioc3_devices_rwsem);
  	list_del(&idd->list);
  	up_write(&ioc3_devices_rwsem);
  	kfree(idd);
  }
  
  static struct pci_device_id ioc3_id_table[] = {
  	{PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, PCI_ANY_ID, PCI_ANY_ID},
  	{0}
  };
  
  static struct pci_driver ioc3_driver = {
  	.name = "IOC3",
  	.id_table = ioc3_id_table,
  	.probe = ioc3_probe,
2ea5d35a4   Jean Delvare   ioc3/ioc4: variou...
822
  	.remove = __devexit_p(ioc3_remove),
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
823
824
825
826
827
828
829
830
831
  };
  
  MODULE_DEVICE_TABLE(pci, ioc3_id_table);
  
  /*********************
   * Module management *
   *********************/
  
  /* Module load */
2ea5d35a4   Jean Delvare   ioc3/ioc4: variou...
832
  static int __init ioc3_init(void)
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
833
834
835
  {
  	if (ia64_platform_is("sn2"))
  		return pci_register_driver(&ioc3_driver);
9385565e2   Jean Delvare   ioc3/ioc4: fix er...
836
  	return -ENODEV;
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
837
838
839
  }
  
  /* Module unload */
2ea5d35a4   Jean Delvare   ioc3/ioc4: variou...
840
  static void __exit ioc3_exit(void)
2d0cfb527   Patrick Gefre   [PATCH] Altix: io...
841
842
843
844
845
846
847
848
849
850
  {
  	pci_unregister_driver(&ioc3_driver);
  }
  
  module_init(ioc3_init);
  module_exit(ioc3_exit);
  
  MODULE_AUTHOR("Stanislaw Skowronek <skylark@linux-mips.org>");
  MODULE_DESCRIPTION("PCI driver for SGI IOC3");
  MODULE_LICENSE("GPL");
53d8be5c1   Pat Gefre   [PATCH] Altix ioc...
851
852
853
854
855
856
  EXPORT_SYMBOL_GPL(ioc3_register_submodule);
  EXPORT_SYMBOL_GPL(ioc3_unregister_submodule);
  EXPORT_SYMBOL_GPL(ioc3_ack);
  EXPORT_SYMBOL_GPL(ioc3_gpcr_set);
  EXPORT_SYMBOL_GPL(ioc3_disable);
  EXPORT_SYMBOL_GPL(ioc3_enable);