Binary files acx-20071003/.pci.c.swp and acx-20071003.new/.pci.c.swp differ diff -urN acx-20071003/acx_struct.h acx-20071003.new/acx_struct.h --- acx-20071003/acx_struct.h 2007-10-03 17:42:18.000000000 +0200 +++ acx-20071003.new/acx_struct.h 2008-01-04 02:06:43.000000000 +0100 @@ -1440,7 +1440,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 -urN acx-20071003/pci.c acx-20071003.new/pci.c --- acx-20071003/pci.c 2008-01-04 02:05:00.000000000 +0100 +++ acx-20071003.new/pci.c 2008-01-04 03:10:42.000000000 +0100 @@ -59,12 +59,17 @@ #include #include #include +#ifdef CONFIG_VLYNQ +#include +#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 */ @@ -102,7 +107,7 @@ #define PCI_POWER_ERROR -1 #endif - +#endif /*********************************************************************** */ static void acxpci_i_tx_timeout(struct net_device *ndev); @@ -653,11 +658,11 @@ 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); + fw_image = acx_s_read_fw(adev->bus_dev, filename, &file_size); if (!fw_image) { FN_EXIT1(NOT_OK); return NOT_OK; @@ -716,7 +721,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; @@ -933,7 +938,9 @@ ecpu_ctrl = read_reg16(adev, IO_ACX_ECPU_CTRL) & 1; if (!ecpu_ctrl) { +#ifdef CONFIG_PCI acxpci_l_reset_mac(adev); +#endif ecpu_ctrl = read_reg16(adev, IO_ACX_ECPU_CTRL) & 1; } @@ -1473,6 +1480,7 @@ static void dummy_netdev_init(struct net_device *ndev) {} +#ifdef CONFIG_PCI static int __devinit acxpci_e_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -1606,6 +1614,7 @@ ** just _presume_ that we're under sem (instead of actually taking it): */ /* acx_sem_lock(adev); */ adev->pdev = pdev; + adev->bus_dev = &pdev->dev; adev->ndev = ndev; adev->dev_type = DEVTYPE_PCI; adev->chip_type = chip_type; @@ -1956,7 +1965,7 @@ return OK; } #endif /* CONFIG_PM */ - +#endif /* CONFIG_PCI */ /*********************************************************************** ** acxpci_s_up @@ -2051,7 +2060,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 @@ -3573,9 +3582,8 @@ { 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", msg, (int)size, ptr, (unsigned long long)*phy); @@ -4137,6 +4145,379 @@ } +#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; + acx_device_t *adev = NULL; + struct net_device *ndev = NULL; + acx111_ie_configoption_t co; + struct vlynq_mapping mapping[4] = { { 0, }, }; + struct vlynq_known *match = NULL; + int err; + + 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); + + ndev = alloc_netdev(sizeof(*adev), "wlan%d", dummy_netdev_init); + /* (NB: memsets to 0 entire area) */ + if (!ndev) { + printk("acx: no memory for netdevice struct\n"); + goto fail_alloc_netdev; + } + ether_setup(ndev); + ndev->open = &acxpci_e_open; + ndev->stop = &acxpci_e_close; + ndev->hard_start_xmit = &acx_i_start_xmit; + ndev->get_stats = &acx_e_get_stats; +#if IW_HANDLER_VERSION <= 5 + ndev->get_wireless_stats = &acx_e_get_wireless_stats; +#endif + ndev->wireless_handlers = (struct iw_handler_def *)&acx_ioctl_handler_def; + ndev->set_multicast_list = &acxpci_i_set_multicast_list; + ndev->tx_timeout = &acxpci_i_tx_timeout; + ndev->change_mtu = &acx_e_change_mtu; + ndev->watchdog_timeo = 4 * HZ; + + adev = ndev2adev(ndev); + + 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 */ + sema_init(&adev->sem, 1); + /* 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->ndev = ndev; + adev->vdev = vdev; + adev->bus_dev = &vdev->dev; + adev->dev_type = DEVTYPE_PCI; + +/** Finished with private interface **/ + + vlynq_set_drvdata(vdev, ndev); + 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; + ndev->irq = vlynq_virq_to_irq(vdev, match->irq); + ndev->base_addr = adev->iobase; + + 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, ndev->irq, + vdev->mem_start, adev->iobase); + log(L_ANY, "initial debug setting is 0x%04X\n", acx_debug); + + if (0 == ndev->irq) { + printk("acx: can't use IRQ 0\n"); + goto fail_irq; + } + + /* to find crashes due to weird driver access + * to unconfigured interface (ifup) */ + adev->mgmt_timer.function = (void (*)(unsigned long))0x0000dead; +#ifdef NONESSENTIAL_FEATURES + acx_show_card_eeprom_id(adev); +#endif /* NONESSENTIAL_FEATURES */ + +#ifdef SET_MODULE_OWNER + SET_MODULE_OWNER(ndev); +#endif + SET_NETDEV_DEV(ndev, adev->bus_dev); + + log(L_IRQ|L_INIT, "using IRQ %d\n", ndev->irq); + + + /* 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(ndev); + + /* 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 */ + acx_stop_queue(ndev, "on probe"); + acx_carrier_off(ndev, "on probe"); + + /* 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", + ndev->name, WIRELESS_EXT, UTS_RELEASE); + + log(L_IRQ | L_INIT, "using IRQ %d\n", ndev->irq); + +/** done with board specific setup **/ + err = register_netdev(ndev); + if (OK != err) { + printk("acx: register_netdev() FAILED: %d\n", err); + 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: + free_netdev(ndev); + + 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: + fail: + vlynq_disable_device(vdev); + done: + FN_EXIT1(result); + return result; +} + +static void vlynq_remove(struct vlynq_device *vdev) +{ + struct net_device *ndev = vlynq_get_drvdata(vdev);; + acx_device_t *adev; + unsigned long flags; + FN_ENTER; + + if (!ndev) { + log(L_DEBUG, "%s: card is unused. Skipping any release code\n", + __func__); + goto end; + } + + acx_lock(adev, flags); + adev = ndev2adev(ndev); + acx_unlock(adev, flags); + + /* If device wasn't hot unplugged... */ + if (adev_present(adev)) { + + acx_sem_lock(adev); + + /* disable both Tx and Rx to shut radio down properly */ + 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! */ + log(L_INIT, "removing device %s\n", ndev->name); + unregister_netdev(ndev); + + /* 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(ndev); + CLEAR_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP); + } + + acx_proc_unregister_entries(ndev); + + /* 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 */ + + 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...) */ + free_netdev(ndev); + + 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 + + +#ifdef CONFIG_PCI /*********************************************************************** ** Data for init_module/cleanup_module */ @@ -4192,7 +4573,7 @@ .resume = acxpci_e_resume #endif /* CONFIG_PM */ }; - +#endif /* CONFIG_PCI */ /*********************************************************************** ** acxpci_e_init_module @@ -4202,7 +4583,7 @@ int __init acxpci_e_init_module(void) { - int res; + int res = 0; FN_ENTER; @@ -4222,11 +4603,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; } @@ -4242,8 +4627,13 @@ acxpci_e_cleanup_module(void) { FN_ENTER; +#ifdef CONFIG_VLYNQ + vlynq_unregister_driver(&vlynq_acx); +#endif +#ifdef CONFIG_PCI pci_unregister_driver(&acxpci_drv_id); +#endif FN_EXIT0; }