summaryrefslogtreecommitdiffstats
path: root/package/acx-mac80211/patches/002-acx_vlynq.diff
blob: 69d13f9ead9abfec8233a774d1564cda16817682 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
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
342
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
376
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
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
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
diff -Nru 1/acx_struct.h 2/acx_struct.h
--- 1/acx_struct.h	2007-10-03 23:42:39.000000000 +0800
+++ 2/acx_struct.h	2007-10-06 01:15:32.000000000 +0800
@@ -1425,7 +1425,13 @@
 
 	const u16	*io;		/* points to ACX100 or ACX111 PCI I/O register address set */
 
+#ifdef CONFIG_PCI
 	struct pci_dev	*pdev;
+#endif
+#ifdef CONFIG_VLYNQ
+	struct vlynq_device	*vdev;
+#endif
+	struct device *bus_dev;
 
 	unsigned long	membase;
 	unsigned long	membase2;
diff -Nru 1/pci.c 2/pci.c
--- 1/pci.c	2007-10-03 23:42:39.000000000 +0800
+++ 2/pci.c	2007-10-06 01:15:32.000000000 +0800
@@ -33,11 +33,15 @@
 #include <linux/ethtool.h>
 #include <linux/dma-mapping.h>
 #include <linux/workqueue.h>
+#ifdef CONFIG_VLYNQ
+#include <linux/vlynq.h>
+#endif
 
 #include "acx.h"
 
 /***********************************************************************
 */
+#ifdef CONFIG_PCI
 #define PCI_TYPE		(PCI_USES_MEM | PCI_ADDR0 | PCI_NO_ACPI_WAKE)
 #define PCI_ACX100_REGION1		0x01
 #define PCI_ACX100_REGION1_SIZE		0x1000	/* Memory size - 4K bytes */
@@ -74,7 +78,7 @@
 #define PCI_UNKNOWN	5
 #define PCI_POWER_ERROR	-1
 #endif
-
+#endif /* CONFIG_PCI */
 /***********************************************************************
 */
 
@@ -87,12 +91,6 @@
 static void acxpci_s_up(struct ieee80211_hw *hw);
 static void acxpci_s_down(struct ieee80211_hw *hw);
 
-void acxpci_put_devname(acx_device_t *adev, struct ethtool_drvinfo *info)
-{
-
-        strncpy(info->bus_info,pci_name(adev->pdev), ETHTOOL_BUSINFO_LEN);
-}
-
 /***********************************************************************
 ** Register access
 **
@@ -605,12 +603,12 @@
 	snprintf(filename, sizeof(filename), "tiacx1%02dc%02X",
 		 IS_ACX111(adev) * 11, adev->radio_type);
 
-	fw_image = acx_s_read_fw(&adev->pdev->dev, filename, &file_size);
+	fw_image = acx_s_read_fw(adev->bus_dev, filename, &file_size);
 	if (!fw_image) {
 		adev->need_radio_fw = 1;
 		filename[sizeof("tiacx1NN") - 1] = '\0';
 		fw_image =
-		    acx_s_read_fw(&adev->pdev->dev, filename, &file_size);
+		    acx_s_read_fw(adev->bus_dev, filename, &file_size);
 		if (!fw_image) {
 			FN_EXIT1(NOT_OK);
 			return NOT_OK;
@@ -670,7 +668,7 @@
 
 	snprintf(filename, sizeof(filename), "tiacx1%02dr%02X",
 		 IS_ACX111(adev) * 11, adev->radio_type);
-	radio_image = acx_s_read_fw(&adev->pdev->dev, filename, &size);
+	radio_image = acx_s_read_fw(adev->bus_dev, filename, &size);
 	if (!radio_image) {
 		printk("acx: can't load radio module '%s'\n", filename);
 		goto fail;
@@ -886,7 +884,9 @@
 
 	acx_lock(adev, flags);
 
+#ifdef CONFIG_PCI
 	acxpci_l_reset_mac(adev);
+#endif
 
 	ecpu_ctrl = read_reg16(adev, IO_ACX_ECPU_CTRL) & 1;
 	if (!ecpu_ctrl) {
@@ -1426,6 +1426,7 @@
 };
 
 
+#ifdef CONFIG_PCI
 static int __devinit
 acxpci_e_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
@@ -1477,6 +1478,7 @@
 	/* acx_sem_lock(adev); */
 	adev->ieee = ieee;
 	adev->pdev = pdev;
+	adev->bus_dev = &pdev->dev;
 	adev->dev_type = DEVTYPE_PCI;
 
 /** Finished with private interface **/
@@ -1916,7 +1918,7 @@
 	return OK;
 }
 #endif /* CONFIG_PM */
-
+#endif /* CONFIG_PCI */
 
 /***********************************************************************
 ** acxpci_s_up
@@ -1998,7 +2000,7 @@
 	/* then wait until interrupts have finished executing on other CPUs */
 	acx_lock(adev, flags);
 	disable_acx_irq(adev);
-        synchronize_irq(adev->pdev->irq);
+        synchronize_irq(adev->irq);
 	acx_unlock(adev, flags);
 
 	/* we really don't want to have an asynchronous tasklet disturb us
@@ -3449,8 +3451,7 @@
 {
 	void *ptr;
 
-	ptr = dma_alloc_coherent(adev->pdev ? &adev->pdev->dev : NULL,
-				 size, phy, GFP_KERNEL);
+	ptr = dma_alloc_coherent(adev->bus_dev, size, phy, GFP_KERNEL);
 
 	if (ptr) {
 		log(L_DEBUG, "%s sz=%d adr=0x%p phy=0x%08llx\n",
@@ -4016,10 +4017,371 @@
 	return OK;
 }
 
+#ifdef CONFIG_VLYNQ
+struct vlynq_reg_config {
+	u32 offset;
+	u32 value;
+};
+
+struct vlynq_known {
+	u32 chip_id;
+	char name[32];
+	struct vlynq_mapping rx_mapping[4];
+	int irq;
+	int irq_type;
+	int num_regs;
+	struct vlynq_reg_config regs[10];
+};
+
+#define CHIP_TNETW1130 0x00000009
+#define CHIP_TNETW1350 0x00000029
+
+static struct vlynq_known known_devices[] = {
+	{
+		.chip_id = CHIP_TNETW1130, .name = "TI TNETW1130",
+		.rx_mapping = {
+			{ .size = 0x22000, .offset = 0xf0000000 },
+			{ .size = 0x40000, .offset = 0xc0000000 },
+			{ .size = 0x0, .offset = 0x0 },
+			{ .size = 0x0, .offset = 0x0 },
+		},
+		.irq = 0,
+		.irq_type = IRQ_TYPE_EDGE_RISING,
+		.num_regs = 5,
+		.regs = {
+			{
+				.offset = 0x790,
+				.value = (0xd0000000 - PHYS_OFFSET)
+			},
+			{
+				.offset = 0x794,
+				.value = (0xd0000000 - PHYS_OFFSET)
+			},
+			{ .offset = 0x740, .value = 0 },
+			{ .offset = 0x744, .value = 0x00010000 },
+			{ .offset = 0x764, .value = 0x00010000 },
+		},
+	},
+	{
+		.chip_id = CHIP_TNETW1350, .name = "TI TNETW1350",
+		.rx_mapping = {
+			{ .size = 0x100000, .offset = 0x00300000 },
+			{ .size = 0x80000, .offset = 0x00000000 },
+			{ .size = 0x0, .offset = 0x0 },
+			{ .size = 0x0, .offset = 0x0 },
+		},
+		.irq = 0,
+		.irq_type = IRQ_TYPE_EDGE_RISING,
+		.num_regs = 5,
+		.regs = {
+			{
+				.offset = 0x790,
+				.value = (0x60000000 - PHYS_OFFSET)
+			},
+			{
+				.offset = 0x794,
+				.value = (0x60000000 - PHYS_OFFSET)
+			},
+			{ .offset = 0x740, .value = 0 },
+			{ .offset = 0x744, .value = 0x00010000 },
+			{ .offset = 0x764, .value = 0x00010000 },
+		},
+	},
+};
+
+static struct vlynq_device_id acx_vlynq_id[] = {
+	{ CHIP_TNETW1130, vlynq_div_auto, 0 },
+	{ CHIP_TNETW1350, vlynq_div_auto, 1 },
+	{ 0, 0, 0 },
+};
+
+static __devinit int vlynq_probe(struct vlynq_device *vdev,
+				 struct vlynq_device_id *id)
+{
+	int result = -EIO, i;
+	u32 addr;
+	struct ieee80211_hw *ieee;
+	acx_device_t *adev = NULL;
+	acx111_ie_configoption_t co;
+	struct vlynq_mapping mapping[4] = { { 0, }, };
+	struct vlynq_known *match = NULL;
+
+	FN_ENTER;
+	result = vlynq_enable_device(vdev);
+	if (result)
+		return result;
+
+	match = &known_devices[id->driver_data];
+
+	if (!match) {
+		result = -ENODEV;
+		goto fail;
+	}
+
+	mapping[0].offset = ARCH_PFN_OFFSET << PAGE_SHIFT;
+	mapping[0].size = 0x02000000;
+	vlynq_set_local_mapping(vdev, vdev->mem_start, mapping);
+	vlynq_set_remote_mapping(vdev, 0, match->rx_mapping);
+
+	set_irq_type(vlynq_virq_to_irq(vdev, match->irq), match->irq_type);
+
+	addr = (u32)ioremap(vdev->mem_start, 0x1000);
+	if (!addr) {
+		printk(KERN_ERR "%s: failed to remap io memory\n",
+		       vdev->dev.bus_id);
+		result = -ENXIO;
+		goto fail;
+	}
+
+	for (i = 0; i < match->num_regs; i++)
+		iowrite32(match->regs[i].value,
+			  (u32 *)(addr + match->regs[i].offset));
+
+	iounmap((void *)addr);
+
+	ieee = ieee80211_alloc_hw(sizeof(struct acx_device), &acxpci_hw_ops);
+	if (!ieee) {
+		printk("acx: could not allocate ieee80211 structure %s\n",
+		       vdev->dev.bus_id);
+		goto fail_alloc_netdev;
+	}
+	ieee->flags &=	 (~IEEE80211_HW_RX_INCLUDES_FCS &
+			  ~IEEE80211_HW_MONITOR_DURING_OPER) |
+			 IEEE80211_HW_WEP_INCLUDE_IV;
+	ieee->queues = 1;
+
+	adev = ieee2adev(ieee);
+
+	memset(adev, 0, sizeof(*adev));
+	/** Set up our private interface **/
+	spin_lock_init(&adev->lock);	/* initial state: unlocked */
+	/* We do not start with downed sem: we want PARANOID_LOCKING to work */
+	mutex_init(&adev->mutex);
+	/* since nobody can see new netdev yet, we can as well
+	 ** just _presume_ that we're under sem (instead of actually taking it): */
+	/* acx_sem_lock(adev); */
+	adev->ieee = ieee;
+	adev->vdev = vdev;
+	adev->bus_dev = &vdev->dev;
+	adev->dev_type = DEVTYPE_PCI;
+
+/** Finished with private interface **/
+
+	vlynq_set_drvdata(vdev, ieee);
+	if (!request_mem_region(vdev->mem_start, vdev->mem_end - vdev->mem_start, "acx")) {
+		printk("acx: cannot reserve VLYNQ memory region\n");
+		goto fail_request_mem_region;
+	}
+	adev->iobase = ioremap(vdev->mem_start, vdev->mem_end - vdev->mem_start);
+	if (!adev->iobase) {
+		printk("acx: ioremap() FAILED\n");
+		goto fail_ioremap;
+	}
+	adev->iobase2 = adev->iobase + match->rx_mapping[0].size;
+	adev->chip_type = CHIPTYPE_ACX111;
+	adev->chip_name = match->name;
+	adev->io = IO_ACX111;
+	adev->irq = vlynq_virq_to_irq(vdev, match->irq);
+
+	printk("acx: found %s-based wireless network card at %s, irq:%d, "
+	       "phymem:0x%x, mem:0x%p\n",
+	       match->name, vdev->dev.bus_id, adev->irq,
+	       vdev->mem_start, adev->iobase);
+	log(L_ANY, "initial debug setting is 0x%04X\n", acx_debug);
+
+	if (0 == adev->irq) {
+		printk("acx: can't use IRQ 0\n");
+		goto fail_irq;
+	}
+	SET_IEEE80211_DEV(ieee, &vdev->dev);
+
+
+	/* to find crashes due to weird driver access
+	 * to unconfigured interface (ifup) */
+	adev->mgmt_timer.function = (void (*)(unsigned long))0x0000dead;
+
+
+	/* ok, pci setup is finished, now start initializing the card */
+
+	/* NB: read_reg() reads may return bogus data before reset_dev(),
+	 * since the firmware which directly controls large parts of the I/O
+	 * registers isn't initialized yet.
+	 * acx100 seems to be more affected than acx111 */
+	if (OK != acxpci_s_reset_dev(adev))
+		goto fail_reset;
+
+	if (OK != acx_s_init_mac(adev))
+		goto fail_init_mac;
+
+	acx_s_interrogate(adev, &co, ACX111_IE_CONFIG_OPTIONS);
+/* TODO: merge them into one function, they are called just once and are the same for pci & usb */
+	if (OK != acxpci_read_eeprom_byte(adev, 0x05, &adev->eeprom_version))
+		goto fail_read_eeprom_version;
+
+	acx_s_parse_configoption(adev, &co);
+	acx_s_set_defaults(adev);
+	acx_s_get_firmware_version(adev);	/* needs to be after acx_s_init_mac() */
+	acx_display_hardware_details(adev);
+
+	/* Register the card, AFTER everything else has been set up,
+	 * since otherwise an ioctl could step on our feet due to
+	 * firmware operations happening in parallel or uninitialized data */
+
+
+	acx_proc_register_entries(ieee);
+
+	/* Now we have our device, so make sure the kernel doesn't try
+	 * to send packets even though we're not associated to a network yet */
+
+	/* after register_netdev() userspace may start working with dev
+	 * (in particular, on other CPUs), we only need to up the sem */
+	/* acx_sem_unlock(adev); */
+
+	printk("acx " ACX_RELEASE ": net device %s, driver compiled "
+	       "against wireless extensions %d and Linux %s\n",
+	       wiphy_name(adev->ieee->wiphy), WIRELESS_EXT, UTS_RELEASE);
+
+	MAC_COPY(adev->ieee->wiphy->perm_addr, adev->dev_addr);
+
+	log(L_IRQ | L_INIT, "using IRQ %d\n", adev->irq);
+
+/** done with board specific setup **/
+
+	acx_init_task_scheduler(adev);
+	result = ieee80211_register_hw(adev->ieee);
+	if (OK != result) {
+		printk("acx: ieee80211_register_hw() FAILED: %d\n", result);
+		goto fail_register_netdev;
+	}
+#if CMD_DISCOVERY
+	great_inquisitor(adev);
+#endif
+
+	result = OK;
+	goto done;
+
+	/* error paths: undo everything in reverse order... */
+
+
+	acxpci_s_delete_dma_regions(adev);
+
+      fail_init_mac:
+      fail_read_eeprom_version:
+      fail_reset:
+
+      fail_alloc_netdev:
+      fail_irq:
+
+	iounmap(adev->iobase);
+      fail_ioremap:
+
+	release_mem_region(vdev->mem_start, vdev->mem_end - vdev->mem_start);
+      fail_request_mem_region:
+      fail_register_netdev:
+	ieee80211_free_hw(ieee);
+      fail:
+	vlynq_disable_device(vdev);
+      done:
+	FN_EXIT1(result);
+	return result;
+}
+
+static void vlynq_remove(struct vlynq_device *vdev)
+{
+	struct ieee80211_hw *hw = vlynq_get_drvdata(vdev);
+	acx_device_t *adev = ieee2adev(hw);
+	unsigned long flags;
+	FN_ENTER;
+
+	if (!hw) {
+		log(L_DEBUG, "%s: card is unused. Skipping any release code\n",
+		    __func__);
+		goto end;
+	}
+
+
+	acx_lock(adev, flags);
+	acx_unlock(adev, flags);
+	adev->initialized = 0;
+
+	/* If device wasn't hot unplugged... */
+	if (adev_present(adev)) {
+
+		acx_sem_lock(adev);
+
+		/* disable both Tx and Rx to shut radio down properly */
+		if (adev->initialized) {
+			acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_TX, NULL, 0);
+			acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_RX, NULL, 0);
+		}
+		acx_lock(adev, flags);
+		/* disable power LED to save power :-) */
+		log(L_INIT, "switching off power LED to save power\n");
+		acxpci_l_power_led(adev, 0);
+		/* stop our eCPU */
+		acx_unlock(adev, flags);
+
+		acx_sem_unlock(adev);
+	}
+
+	/* unregister the device to not let the kernel
+	 * (e.g. ioctls) access a half-deconfigured device
+	 * NB: this will cause acxpci_e_close() to be called,
+	 * thus we shouldn't call it under sem!
+	 * Well, netdev did, but ieee80211 stack does not, so we
+	 * have to do so manually...
+	 */
+	acxpci_e_close(hw);
+	log(L_INIT, "removing device %s\n", wiphy_name(adev->ieee->wiphy));
+	ieee80211_unregister_hw(adev->ieee);
+
+	/* unregister_netdev ensures that no references to us left.
+	 * For paranoid reasons we continue to follow the rules */
+	acx_sem_lock(adev);
+
+	if (adev->dev_state_mask & ACX_STATE_IFACE_UP) {
+		acxpci_s_down(hw);
+		CLEAR_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP);
+	}
+
+	acx_proc_unregister_entries(adev->ieee);
+
+	/* finally, clean up PCI bus state */
+	acxpci_s_delete_dma_regions(adev);
+	if (adev->iobase)
+		iounmap(adev->iobase);
+	if (adev->iobase2)
+		iounmap(adev->iobase2);
+	release_mem_region(vdev->mem_start, vdev->mem_end - vdev->mem_start);
+
+	/* remove dev registration */
+
+	free_irq(adev->irq, adev);
+	acx_sem_unlock(adev);
+	vlynq_disable_device(vdev);
+
+	/* Free netdev (quite late,
+	 * since otherwise we might get caught off-guard
+	 * by a netdev timeout handler execution
+	 * expecting to see a working dev...) */
+	ieee80211_free_hw(adev->ieee);
+
+      end:
+	FN_EXIT0;
+}
+
+static struct vlynq_driver vlynq_acx = {
+	.name = "acx_vlynq",
+	.id_table = acx_vlynq_id,
+	.probe = vlynq_probe,
+	.remove = __devexit_p(vlynq_remove),
+};
+#endif
 
 /***********************************************************************
 ** Data for init_module/cleanup_module
 */
+#ifdef CONFIG_PCI
 static const struct pci_device_id acxpci_id_tbl[] __devinitdata = {
 	{
 	 .vendor = PCI_VENDOR_ID_TI,
@@ -4071,7 +4433,7 @@
 	.resume = acxpci_e_resume
 #endif /* CONFIG_PM */
 };
-
+#endif /* CONFIG_PCI */
 
 /***********************************************************************
 ** acxpci_e_init_module
@@ -4080,7 +4442,7 @@
 */
 int __init acxpci_e_init_module(void)
 {
-	int res;
+	int res = 0;
 
 	FN_ENTER;
 
@@ -4100,10 +4462,15 @@
 #endif
 	log(L_INIT,
 	    "acx: " ENDIANNESS_STRING
-	    "acx: PCI module " ACX_RELEASE " initialized, "
+	    "acx: PCI/VLYNQ module " ACX_RELEASE " initialized, "
 	    "waiting for cards to probe...\n");
 
+#ifdef CONFIG_PCI
 	res = pci_register_driver(&acxpci_drv_id);
+#endif
+#ifdef CONFIG_VLYNQ
+	res = vlynq_register_driver(&vlynq_acx);
+#endif
 	FN_EXIT1(res);
 	return res;
 }
@@ -4119,7 +4486,12 @@
 {
 	FN_ENTER;
 
+#ifdef CONFIG_VLYNQ
+	vlynq_unregister_driver(&vlynq_acx);
+#endif
+#ifdef CONFIG_PCI
 	pci_unregister_driver(&acxpci_drv_id);
+#endif
 
 	FN_EXIT0;
 }