Blame view

drivers/net/arcnet/arc-rimi.c 10.5 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
  /*
   * Linux ARCnet driver - "RIM I" (entirely mem-mapped) cards
cb334648a   Joe Perches   arcnet: Use norma...
3
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
   * Written 1994-1999 by Avery Pennarun.
   * Written 1999-2000 by Martin Mares <mj@ucw.cz>.
   * Derived from skeleton.c by Donald Becker.
   *
   * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com)
   *  for sponsoring the further development of this driver.
   *
   * **********************
   *
   * The original copyright of skeleton.c was as follows:
   *
   * skeleton.c Written 1993 by Donald Becker.
   * Copyright 1993 United States Government as represented by the
   * Director, National Security Agency.  This software may only be used
   * and distributed according to the terms of the GNU General Public License as
   * modified by SRC, incorporated herein by reference.
   *
   * **********************
   *
   * For more details, see drivers/net/arcnet.c
   *
   * **********************
   */
05a24b234   Joe Perches   arcnet: Convert p...
27
28
  
  #define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
30
31
32
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/moduleparam.h>
  #include <linux/ioport.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
34
35
36
  #include <linux/delay.h>
  #include <linux/netdevice.h>
  #include <linux/bootmem.h>
  #include <linux/init.h>
a6b7a4078   Alexey Dobriyan   net: remove inter...
37
  #include <linux/interrupt.h>
5e7ef9134   Joe Perches   arcnet: Use inclu...
38
  #include <linux/io.h>
26c6d2816   Joe Perches   arcnet: Move file...
39
40
  
  #include "arcdevice.h"
8e0f295ea   Joe Perches   arcnet: Add com90...
41
  #include "com9026.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
42

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
44
45
46
47
48
49
50
51
52
  /* Internal function declarations */
  
  static int arcrimi_probe(struct net_device *dev);
  static int arcrimi_found(struct net_device *dev);
  static void arcrimi_command(struct net_device *dev, int command);
  static int arcrimi_status(struct net_device *dev);
  static void arcrimi_setmask(struct net_device *dev, int mask);
  static int arcrimi_reset(struct net_device *dev, int really_reset);
  static void arcrimi_copy_to_card(struct net_device *dev, int bufnum, int offset,
  				 void *buf, int count);
d6d7d3ed5   Joe Perches   arcnet: Wrap some...
53
54
  static void arcrimi_copy_from_card(struct net_device *dev, int bufnum,
  				   int offset, void *buf, int count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
55
56
57
58
  
  /* Handy defines for ARCnet specific stuff */
  
  /* Amount of I/O memory used by the card */
cb334648a   Joe Perches   arcnet: Use norma...
59
60
  #define BUFFER_SIZE	(512)
  #define MIRROR_SIZE	(BUFFER_SIZE * 4)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
61

f2f0a16bf   Joe Perches   arcnet: Use netwo...
62
  /* We cannot probe for a RIM I card; one reason is I don't know how to reset
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
64
65
66
67
   * them.  In fact, we can't even get their node ID automatically.  So, we
   * need to be passed a specific shmem address, IRQ, and node ID.
   */
  static int __init arcrimi_probe(struct net_device *dev)
  {
72aeea484   Joe Perches   arcnet: Expand od...
68
  	if (BUGLVL(D_NORMAL)) {
05a24b234   Joe Perches   arcnet: Convert p...
69
70
71
72
73
74
75
  		pr_info("%s
  ", "RIM I (entirely mem-mapped) support");
  		pr_info("E-mail me if you actually test the RIM I driver, please!
  ");
  		pr_info("Given: node %02Xh, shmem %lXh, irq %d
  ",
  			dev->dev_addr[0], dev->mem_start, dev->irq);
72aeea484   Joe Perches   arcnet: Expand od...
76
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
78
  
  	if (dev->mem_start <= 0 || dev->irq <= 0) {
72aeea484   Joe Perches   arcnet: Expand od...
79
  		if (BUGLVL(D_NORMAL))
05a24b234   Joe Perches   arcnet: Convert p...
80
81
  			pr_err("No autoprobe for RIM I; you must specify the shmem and irq!
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
83
  		return -ENODEV;
  	}
d0f6ecad3   Al Viro   [PATCH] arcnet pr...
84
  	if (dev->dev_addr[0] == 0) {
72aeea484   Joe Perches   arcnet: Expand od...
85
  		if (BUGLVL(D_NORMAL))
05a24b234   Joe Perches   arcnet: Convert p...
86
87
  			pr_err("You need to specify your card's station ID!
  ");
d0f6ecad3   Al Viro   [PATCH] arcnet pr...
88
89
  		return -ENODEV;
  	}
f2f0a16bf   Joe Perches   arcnet: Use netwo...
90
  	/* Grab the memory region at mem_start for MIRROR_SIZE bytes.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
92
93
94
  	 * Later in arcrimi_found() the real size will be determined
  	 * and this reserve will be released and the correct size
  	 * will be taken.
  	 */
d0f6ecad3   Al Viro   [PATCH] arcnet pr...
95
  	if (!request_mem_region(dev->mem_start, MIRROR_SIZE, "arcnet (90xx)")) {
72aeea484   Joe Perches   arcnet: Expand od...
96
  		if (BUGLVL(D_NORMAL))
05a24b234   Joe Perches   arcnet: Convert p...
97
98
  			pr_notice("Card memory already allocated
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99
100
  		return -ENODEV;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101
102
  	return arcrimi_found(dev);
  }
d0f6ecad3   Al Viro   [PATCH] arcnet pr...
103
104
105
106
107
108
109
110
111
112
  static int check_mirror(unsigned long addr, size_t size)
  {
  	void __iomem *p;
  	int res = -1;
  
  	if (!request_mem_region(addr, size, "arcnet (90xx)"))
  		return -1;
  
  	p = ioremap(addr, size);
  	if (p) {
4e2f9f1b1   Joe Perches   arcnet: arc-rimi:...
113
  		if (arcnet_readb(p, COM9026_REG_R_STATUS) == TESTvalue)
d0f6ecad3   Al Viro   [PATCH] arcnet pr...
114
115
116
117
118
119
120
121
122
  			res = 1;
  		else
  			res = 0;
  		iounmap(p);
  	}
  
  	release_mem_region(addr, size);
  	return res;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123

f2f0a16bf   Joe Perches   arcnet: Use netwo...
124
125
  /* Set up the struct net_device associated with this card.
   * Called after probing succeeds.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126
127
128
129
130
   */
  static int __init arcrimi_found(struct net_device *dev)
  {
  	struct arcnet_local *lp;
  	unsigned long first_mirror, last_mirror, shmem;
d0f6ecad3   Al Viro   [PATCH] arcnet pr...
131
  	void __iomem *p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
133
  	int mirror_size;
  	int err;
d0f6ecad3   Al Viro   [PATCH] arcnet pr...
134
135
136
  	p = ioremap(dev->mem_start, MIRROR_SIZE);
  	if (!p) {
  		release_mem_region(dev->mem_start, MIRROR_SIZE);
a34c0932c   Joe Perches   arcnet: Convert B...
137
138
  		arc_printk(D_NORMAL, dev, "Can't ioremap
  ");
d0f6ecad3   Al Viro   [PATCH] arcnet pr...
139
140
  		return -ENODEV;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
  	/* reserve the irq */
a0607fd3a   Joe Perches   drivers/net: requ...
142
  	if (request_irq(dev->irq, arcnet_interrupt, 0, "arcnet (RIM I)", dev)) {
d0f6ecad3   Al Viro   [PATCH] arcnet pr...
143
144
  		iounmap(p);
  		release_mem_region(dev->mem_start, MIRROR_SIZE);
a34c0932c   Joe Perches   arcnet: Convert B...
145
146
  		arc_printk(D_NORMAL, dev, "Can't get IRQ %d!
  ", dev->irq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
147
148
149
150
  		return -ENODEV;
  	}
  
  	shmem = dev->mem_start;
4e2f9f1b1   Joe Perches   arcnet: arc-rimi:...
151
152
153
  	arcnet_writeb(TESTvalue, p, COM9026_REG_W_INTMASK);
  	arcnet_writeb(TESTvalue, p, COM9026_REG_W_COMMAND);
  					/* actually the station/node ID */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154
155
156
157
158
159
160
161
  
  	/* find the real shared memory start/end points, including mirrors */
  
  	/* guess the actual size of one "memory mirror" - the number of
  	 * bytes between copies of the shared memory.  On most cards, it's
  	 * 2k (or there are no mirrors at all) but on some, it's 4k.
  	 */
  	mirror_size = MIRROR_SIZE;
4e2f9f1b1   Joe Perches   arcnet: arc-rimi:...
162
  	if (arcnet_readb(p, COM9026_REG_R_STATUS) == TESTvalue &&
8e95a2026   Joe Perches   drivers/net: Move...
163
164
  	    check_mirror(shmem - MIRROR_SIZE, MIRROR_SIZE) == 0 &&
  	    check_mirror(shmem - 2 * MIRROR_SIZE, MIRROR_SIZE) == 1)
d0f6ecad3   Al Viro   [PATCH] arcnet pr...
165
  		mirror_size = 2 * MIRROR_SIZE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166

d0f6ecad3   Al Viro   [PATCH] arcnet pr...
167
168
  	first_mirror = shmem - mirror_size;
  	while (check_mirror(first_mirror, mirror_size) == 1)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169
170
  		first_mirror -= mirror_size;
  	first_mirror += mirror_size;
d0f6ecad3   Al Viro   [PATCH] arcnet pr...
171
172
  	last_mirror = shmem + mirror_size;
  	while (check_mirror(last_mirror, mirror_size) == 1)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173
174
175
176
177
178
179
  		last_mirror += mirror_size;
  	last_mirror -= mirror_size;
  
  	dev->mem_start = first_mirror;
  	dev->mem_end = last_mirror + MIRROR_SIZE - 1;
  
  	/* initialize the rest of the device structure. */
454d7c9b1   Wang Chen   netdevice: safe c...
180
  	lp = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
182
183
184
185
186
187
188
  	lp->card_name = "RIM I";
  	lp->hw.command = arcrimi_command;
  	lp->hw.status = arcrimi_status;
  	lp->hw.intmask = arcrimi_setmask;
  	lp->hw.reset = arcrimi_reset;
  	lp->hw.owner = THIS_MODULE;
  	lp->hw.copy_to_card = arcrimi_copy_to_card;
  	lp->hw.copy_from_card = arcrimi_copy_from_card;
f2f0a16bf   Joe Perches   arcnet: Use netwo...
189
  	/* re-reserve the memory region - arcrimi_probe() alloced this reqion
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
191
192
193
  	 * but didn't know the real size.  Free that region and then re-get
  	 * with the correct size.  There is a VERY slim chance this could
  	 * fail.
  	 */
d0f6ecad3   Al Viro   [PATCH] arcnet pr...
194
195
  	iounmap(p);
  	release_mem_region(shmem, MIRROR_SIZE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
197
198
  	if (!request_mem_region(dev->mem_start,
  				dev->mem_end - dev->mem_start + 1,
  				"arcnet (90xx)")) {
a34c0932c   Joe Perches   arcnet: Convert B...
199
200
  		arc_printk(D_NORMAL, dev, "Card memory already allocated
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
202
  		goto err_free_irq;
  	}
d6d7d3ed5   Joe Perches   arcnet: Wrap some...
203
204
  	lp->mem_start = ioremap(dev->mem_start,
  				dev->mem_end - dev->mem_start + 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
  	if (!lp->mem_start) {
a34c0932c   Joe Perches   arcnet: Convert B...
206
207
  		arc_printk(D_NORMAL, dev, "Can't remap device memory!
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
209
210
211
  		goto err_release_mem;
  	}
  
  	/* get and check the station ID from offset 1 in shmem */
4e2f9f1b1   Joe Perches   arcnet: arc-rimi:...
212
  	dev->dev_addr[0] = arcnet_readb(lp->mem_start, COM9026_REG_R_STATION);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213

a34c0932c   Joe Perches   arcnet: Convert B...
214
215
216
217
218
219
  	arc_printk(D_NORMAL, dev, "ARCnet RIM I: station %02Xh found at IRQ %d, ShMem %lXh (%ld*%d bytes)
  ",
  		   dev->dev_addr[0],
  		   dev->irq, dev->mem_start,
  		   (dev->mem_end - dev->mem_start + 1) / mirror_size,
  		   mirror_size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
  
  	err = register_netdev(dev);
  	if (err)
  		goto err_unmap;
  
  	return 0;
  
  err_unmap:
  	iounmap(lp->mem_start);
  err_release_mem:
  	release_mem_region(dev->mem_start, dev->mem_end - dev->mem_start + 1);
  err_free_irq:
  	free_irq(dev->irq, dev);
  	return -EIO;
  }
f2f0a16bf   Joe Perches   arcnet: Use netwo...
235
  /* Do a hardware reset on the card, and set up necessary registers.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
237
238
239
240
241
242
243
   *
   * This should be called as little as possible, because it disrupts the
   * token on the network (causes a RECON) and requires a significant delay.
   *
   * However, it does make sure the card is in a defined state.
   */
  static int arcrimi_reset(struct net_device *dev, int really_reset)
  {
454d7c9b1   Wang Chen   netdevice: safe c...
244
  	struct arcnet_local *lp = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
  	void __iomem *ioaddr = lp->mem_start + 0x800;
a34c0932c   Joe Perches   arcnet: Convert B...
246
247
  	arc_printk(D_INIT, dev, "Resetting %s (status=%02Xh)
  ",
4e2f9f1b1   Joe Perches   arcnet: arc-rimi:...
248
  		   dev->name, arcnet_readb(ioaddr, COM9026_REG_R_STATUS));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249
250
  
  	if (really_reset) {
4e2f9f1b1   Joe Perches   arcnet: arc-rimi:...
251
  		arcnet_writeb(TESTvalue, ioaddr, -0x800);	/* fake reset */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
253
  		return 0;
  	}
4e2f9f1b1   Joe Perches   arcnet: arc-rimi:...
254
255
256
  	/* clear flags & end reset */
  	arcnet_writeb(CFLAGScmd | RESETclear, ioaddr, COM9026_REG_W_COMMAND);
  	arcnet_writeb(CFLAGScmd | CONFIGclear, ioaddr, COM9026_REG_W_COMMAND);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
257
258
  
  	/* enable extended (512-byte) packets */
4e2f9f1b1   Joe Perches   arcnet: arc-rimi:...
259
  	arcnet_writeb(CONFIGcmd | EXTconf, ioaddr, COM9026_REG_W_COMMAND);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
261
262
263
264
265
266
  
  	/* done!  return success. */
  	return 0;
  }
  
  static void arcrimi_setmask(struct net_device *dev, int mask)
  {
454d7c9b1   Wang Chen   netdevice: safe c...
267
  	struct arcnet_local *lp = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
268
  	void __iomem *ioaddr = lp->mem_start + 0x800;
4e2f9f1b1   Joe Perches   arcnet: arc-rimi:...
269
  	arcnet_writeb(mask, ioaddr, COM9026_REG_W_INTMASK);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
270
271
272
273
  }
  
  static int arcrimi_status(struct net_device *dev)
  {
454d7c9b1   Wang Chen   netdevice: safe c...
274
  	struct arcnet_local *lp = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
  	void __iomem *ioaddr = lp->mem_start + 0x800;
4e2f9f1b1   Joe Perches   arcnet: arc-rimi:...
276
  	return arcnet_readb(ioaddr, COM9026_REG_R_STATUS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277
278
279
280
  }
  
  static void arcrimi_command(struct net_device *dev, int cmd)
  {
454d7c9b1   Wang Chen   netdevice: safe c...
281
  	struct arcnet_local *lp = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
  	void __iomem *ioaddr = lp->mem_start + 0x800;
4e2f9f1b1   Joe Perches   arcnet: arc-rimi:...
283
  	arcnet_writeb(cmd, ioaddr, COM9026_REG_W_COMMAND);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284
285
286
287
288
  }
  
  static void arcrimi_copy_to_card(struct net_device *dev, int bufnum, int offset,
  				 void *buf, int count)
  {
454d7c9b1   Wang Chen   netdevice: safe c...
289
  	struct arcnet_local *lp = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290
  	void __iomem *memaddr = lp->mem_start + 0x800 + bufnum * 512 + offset;
01a1d5ac4   Joe Perches   arcnet: Add and r...
291

a34c0932c   Joe Perches   arcnet: Convert B...
292
  	TIME(dev, "memcpy_toio", count, memcpy_toio(memaddr, buf, count));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293
  }
d6d7d3ed5   Joe Perches   arcnet: Wrap some...
294
295
  static void arcrimi_copy_from_card(struct net_device *dev, int bufnum,
  				   int offset, void *buf, int count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296
  {
454d7c9b1   Wang Chen   netdevice: safe c...
297
  	struct arcnet_local *lp = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
  	void __iomem *memaddr = lp->mem_start + 0x800 + bufnum * 512 + offset;
01a1d5ac4   Joe Perches   arcnet: Add and r...
299

a34c0932c   Joe Perches   arcnet: Convert B...
300
  	TIME(dev, "memcpy_fromio", count, memcpy_fromio(buf, memaddr, count));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
342
343
  }
  
  static int node;
  static int io;			/* use the insmod io= irq= node= options */
  static int irq;
  static char device[9];		/* use eg. device=arc1 to change name */
  
  module_param(node, int, 0);
  module_param(io, int, 0);
  module_param(irq, int, 0);
  module_param_string(device, device, sizeof(device), 0);
  MODULE_LICENSE("GPL");
  
  static struct net_device *my_dev;
  
  static int __init arc_rimi_init(void)
  {
  	struct net_device *dev;
  
  	dev = alloc_arcdev(device);
  	if (!dev)
  		return -ENOMEM;
  
  	if (node && node != 0xff)
  		dev->dev_addr[0] = node;
  
  	dev->mem_start = io;
  	dev->irq = irq;
  	if (dev->irq == 2)
  		dev->irq = 9;
  
  	if (arcrimi_probe(dev)) {
  		free_netdev(dev);
  		return -EIO;
  	}
  
  	my_dev = dev;
  	return 0;
  }
  
  static void __exit arc_rimi_exit(void)
  {
  	struct net_device *dev = my_dev;
454d7c9b1   Wang Chen   netdevice: safe c...
344
  	struct arcnet_local *lp = netdev_priv(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
345
346
347
348
349
350
351
352
353
354
355
356
  
  	unregister_netdev(dev);
  	iounmap(lp->mem_start);
  	release_mem_region(dev->mem_start, dev->mem_end - dev->mem_start + 1);
  	free_irq(dev->irq, dev);
  	free_netdev(dev);
  }
  
  #ifndef MODULE
  static int __init arcrimi_setup(char *s)
  {
  	int ints[8];
01a1d5ac4   Joe Perches   arcnet: Add and r...
357

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358
359
360
361
362
  	s = get_options(s, 8, ints);
  	if (!ints[0])
  		return 1;
  	switch (ints[0]) {
  	default:		/* ERROR */
05a24b234   Joe Perches   arcnet: Convert p...
363
364
  		pr_err("Too many arguments
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
  	case 3:		/* Node ID */
  		node = ints[3];
  	case 2:		/* IRQ */
  		irq = ints[2];
  	case 1:		/* IO address */
  		io = ints[1];
  	}
  	if (*s)
  		snprintf(device, sizeof(device), "%s", s);
  	return 1;
  }
  __setup("arcrimi=", arcrimi_setup);
  #endif				/* MODULE */
  
  module_init(arc_rimi_init)
  module_exit(arc_rimi_exit)