diff options
Diffstat (limited to 'drivers')
353 files changed, 9789 insertions, 4900 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 4c35f0822d06..23dac3babfe3 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4204,9 +4204,18 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { { "PIONEER DVD-RW DVR-216D", NULL, ATA_HORKAGE_NOSETXFER }, /* devices that don't properly handle queued TRIM commands */ - { "Micron_M[56]*", NULL, ATA_HORKAGE_NO_NCQ_TRIM | + { "Micron_M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM | + ATA_HORKAGE_ZERO_AFTER_TRIM, }, + { "Crucial_CT*M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM | + ATA_HORKAGE_ZERO_AFTER_TRIM, }, + { "Micron_M5[15]0*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM | + ATA_HORKAGE_ZERO_AFTER_TRIM, }, + { "Crucial_CT*M550*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM | + ATA_HORKAGE_ZERO_AFTER_TRIM, }, + { "Crucial_CT*MX100*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM | + ATA_HORKAGE_ZERO_AFTER_TRIM, }, + { "Samsung SSD 850 PRO*", NULL, ATA_HORKAGE_NO_NCQ_TRIM | ATA_HORKAGE_ZERO_AFTER_TRIM, }, - { "Crucial_CT*SSD*", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, /* * As defined, the DRAT (Deterministic Read After Trim) and RZAT @@ -4226,6 +4235,8 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { */ { "INTEL*SSDSC2MH*", NULL, 0, }, + { "Micron*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, }, + { "Crucial*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, }, { "INTEL*SSD*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, }, { "SSD*INTEL*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, }, { "Samsung*SSD*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, }, @@ -4737,7 +4748,7 @@ struct ata_queued_cmd *ata_qc_new_init(struct ata_device *dev, int tag) return NULL; /* libsas case */ - if (!ap->scsi_host) { + if (ap->flags & ATA_FLAG_SAS_HOST) { tag = ata_sas_allocate_tag(ap); if (tag < 0) return NULL; @@ -4776,7 +4787,7 @@ void ata_qc_free(struct ata_queued_cmd *qc) tag = qc->tag; if (likely(ata_tag_valid(tag))) { qc->tag = ATA_TAG_POISON; - if (!ap->scsi_host) + if (ap->flags & ATA_FLAG_SAS_HOST) ata_sas_free_tag(tag, ap); } } diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index beb8b27d4621..a13587b5c2be 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -243,4 +243,12 @@ extern struct regcache_ops regcache_rbtree_ops; extern struct regcache_ops regcache_lzo_ops; extern struct regcache_ops regcache_flat_ops; +static inline const char *regmap_name(const struct regmap *map) +{ + if (map->dev) + return dev_name(map->dev); + + return map->name; +} + #endif diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index da84f544c544..87db9893b463 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -218,7 +218,7 @@ int regcache_read(struct regmap *map, ret = map->cache_ops->read(map, reg, value); if (ret == 0) - trace_regmap_reg_read_cache(map->dev, reg, *value); + trace_regmap_reg_read_cache(map, reg, *value); return ret; } @@ -311,7 +311,7 @@ int regcache_sync(struct regmap *map) dev_dbg(map->dev, "Syncing %s cache\n", map->cache_ops->name); name = map->cache_ops->name; - trace_regcache_sync(map->dev, name, "start"); + trace_regcache_sync(map, name, "start"); if (!map->cache_dirty) goto out; @@ -346,7 +346,7 @@ out: regmap_async_complete(map); - trace_regcache_sync(map->dev, name, "stop"); + trace_regcache_sync(map, name, "stop"); return ret; } @@ -381,7 +381,7 @@ int regcache_sync_region(struct regmap *map, unsigned int min, name = map->cache_ops->name; dev_dbg(map->dev, "Syncing %s cache from %d-%d\n", name, min, max); - trace_regcache_sync(map->dev, name, "start region"); + trace_regcache_sync(map, name, "start region"); if (!map->cache_dirty) goto out; @@ -401,7 +401,7 @@ out: regmap_async_complete(map); - trace_regcache_sync(map->dev, name, "stop region"); + trace_regcache_sync(map, name, "stop region"); return ret; } @@ -428,7 +428,7 @@ int regcache_drop_region(struct regmap *map, unsigned int min, map->lock(map->lock_arg); - trace_regcache_drop_region(map->dev, min, max); + trace_regcache_drop_region(map, min, max); ret = map->cache_ops->drop(map, min, max); @@ -455,7 +455,7 @@ void regcache_cache_only(struct regmap *map, bool enable) map->lock(map->lock_arg); WARN_ON(map->cache_bypass && enable); map->cache_only = enable; - trace_regmap_cache_only(map->dev, enable); + trace_regmap_cache_only(map, enable); map->unlock(map->lock_arg); } EXPORT_SYMBOL_GPL(regcache_cache_only); @@ -493,7 +493,7 @@ void regcache_cache_bypass(struct regmap *map, bool enable) map->lock(map->lock_arg); WARN_ON(map->cache_only && enable); map->cache_bypass = enable; - trace_regmap_cache_bypass(map->dev, enable); + trace_regmap_cache_bypass(map, enable); map->unlock(map->lock_arg); } EXPORT_SYMBOL_GPL(regcache_cache_bypass); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index f99b098ddabf..dbfe6a69c3da 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1281,7 +1281,7 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, if (map->async && map->bus->async_write) { struct regmap_async *async; - trace_regmap_async_write_start(map->dev, reg, val_len); + trace_regmap_async_write_start(map, reg, val_len); spin_lock_irqsave(&map->async_lock, flags); async = list_first_entry_or_null(&map->async_free, @@ -1339,8 +1339,7 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, return ret; } - trace_regmap_hw_write_start(map->dev, reg, - val_len / map->format.val_bytes); + trace_regmap_hw_write_start(map, reg, val_len / map->format.val_bytes); /* If we're doing a single register write we can probably just * send the work_buf directly, otherwise try to do a gather @@ -1372,8 +1371,7 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, kfree(buf); } - trace_regmap_hw_write_done(map->dev, reg, - val_len / map->format.val_bytes); + trace_regmap_hw_write_done(map, reg, val_len / map->format.val_bytes); return ret; } @@ -1407,12 +1405,12 @@ static int _regmap_bus_formatted_write(void *context, unsigned int reg, map->format.format_write(map, reg, val); - trace_regmap_hw_write_start(map->dev, reg, 1); + trace_regmap_hw_write_start(map, reg, 1); ret = map->bus->write(map->bus_context, map->work_buf, map->format.buf_size); - trace_regmap_hw_write_done(map->dev, reg, 1); + trace_regmap_hw_write_done(map, reg, 1); return ret; } @@ -1470,7 +1468,7 @@ int _regmap_write(struct regmap *map, unsigned int reg, dev_info(map->dev, "%x <= %x\n", reg, val); #endif - trace_regmap_reg_write(map->dev, reg, val); + trace_regmap_reg_write(map, reg, val); return map->reg_write(context, reg, val); } @@ -1773,7 +1771,7 @@ static int _regmap_raw_multi_reg_write(struct regmap *map, for (i = 0; i < num_regs; i++) { int reg = regs[i].reg; int val = regs[i].def; - trace_regmap_hw_write_start(map->dev, reg, 1); + trace_regmap_hw_write_start(map, reg, 1); map->format.format_reg(u8, reg, map->reg_shift); u8 += reg_bytes + pad_bytes; map->format.format_val(u8, val, 0); @@ -1788,7 +1786,7 @@ static int _regmap_raw_multi_reg_write(struct regmap *map, for (i = 0; i < num_regs; i++) { int reg = regs[i].reg; - trace_regmap_hw_write_done(map->dev, reg, 1); + trace_regmap_hw_write_done(map, reg, 1); } return ret; } @@ -2059,15 +2057,13 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, */ u8[0] |= map->read_flag_mask; - trace_regmap_hw_read_start(map->dev, reg, - val_len / map->format.val_bytes); + trace_regmap_hw_read_start(map, reg, val_len / map->format.val_bytes); ret = map->bus->read(map->bus_context, map->work_buf, map->format.reg_bytes + map->format.pad_bytes, val, val_len); - trace_regmap_hw_read_done(map->dev, reg, - val_len / map->format.val_bytes); + trace_regmap_hw_read_done(map, reg, val_len / map->format.val_bytes); return ret; } @@ -2123,7 +2119,7 @@ static int _regmap_read(struct regmap *map, unsigned int reg, dev_info(map->dev, "%x => %x\n", reg, *val); #endif - trace_regmap_reg_read(map->dev, reg, *val); + trace_regmap_reg_read(map, reg, *val); if (!map->cache_bypass) regcache_write(map, reg, *val); @@ -2480,7 +2476,7 @@ void regmap_async_complete_cb(struct regmap_async *async, int ret) struct regmap *map = async->map; bool wake; - trace_regmap_async_io_complete(map->dev); + trace_regmap_async_io_complete(map); spin_lock(&map->async_lock); list_move(&async->list, &map->async_free); @@ -2525,7 +2521,7 @@ int regmap_async_complete(struct regmap *map) if (!map->bus || !map->bus->async_write) return 0; - trace_regmap_async_complete_start(map->dev); + trace_regmap_async_complete_start(map); wait_event(map->async_waitq, regmap_async_is_done(map)); @@ -2534,7 +2530,7 @@ int regmap_async_complete(struct regmap *map) map->async_ret = 0; spin_unlock_irqrestore(&map->async_lock, flags); - trace_regmap_async_complete_done(map->dev); + trace_regmap_async_complete_done(map); return ret; } diff --git a/drivers/bcma/Kconfig b/drivers/bcma/Kconfig index 9be17d3431bb..fc6ffcfa8061 100644 --- a/drivers/bcma/Kconfig +++ b/drivers/bcma/Kconfig @@ -1,6 +1,6 @@ config BCMA_POSSIBLE bool - depends on HAS_IOMEM && HAS_DMA && PCI + depends on HAS_IOMEM && HAS_DMA default y menu "Broadcom specific AMBA" @@ -45,9 +45,9 @@ config BCMA_HOST_SOC If unsure, say N -# TODO: make it depend on PCI when ready config BCMA_DRIVER_PCI - bool + bool "BCMA Broadcom PCI core driver" + depends on BCMA && PCI default y help BCMA bus may have many versions of PCIe core. This driver diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h index 5a1d22489afc..15f2b2e242ea 100644 --- a/drivers/bcma/bcma_private.h +++ b/drivers/bcma/bcma_private.h @@ -106,15 +106,35 @@ static inline void __exit bcma_host_soc_unregister_driver(void) #endif /* CONFIG_BCMA_HOST_SOC && CONFIG_OF */ /* driver_pci.c */ +#ifdef CONFIG_BCMA_DRIVER_PCI u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address); void bcma_core_pci_early_init(struct bcma_drv_pci *pc); void bcma_core_pci_init(struct bcma_drv_pci *pc); void bcma_core_pci_up(struct bcma_drv_pci *pc); void bcma_core_pci_down(struct bcma_drv_pci *pc); +#else +static inline void bcma_core_pci_early_init(struct bcma_drv_pci *pc) +{ + WARN_ON(pc->core->bus->hosttype == BCMA_HOSTTYPE_PCI); +} +static inline void bcma_core_pci_init(struct bcma_drv_pci *pc) +{ + /* Initialization is required for PCI hosted bus */ + WARN_ON(pc->core->bus->hosttype == BCMA_HOSTTYPE_PCI); +} +#endif /* driver_pcie2.c */ +#ifdef CONFIG_BCMA_DRIVER_PCI void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2); void bcma_core_pcie2_up(struct bcma_drv_pcie2 *pcie2); +#else +static inline void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2) +{ + /* Initialization is required for PCI hosted bus */ + WARN_ON(pcie2->core->bus->hosttype == BCMA_HOSTTYPE_PCI); +} +#endif extern int bcma_chipco_watchdog_register(struct bcma_drv_cc *cc); diff --git a/drivers/bcma/driver_gpio.c b/drivers/bcma/driver_gpio.c index dce34fb52e27..74ccb02e0f10 100644 --- a/drivers/bcma/driver_gpio.c +++ b/drivers/bcma/driver_gpio.c @@ -17,6 +17,8 @@ #include "bcma_private.h" +#define BCMA_GPIO_MAX_PINS 32 + static inline struct bcma_drv_cc *bcma_gpio_get_cc(struct gpio_chip *chip) { return container_of(chip, struct bcma_drv_cc, gpio); @@ -204,6 +206,7 @@ static void bcma_gpio_irq_domain_exit(struct bcma_drv_cc *cc) int bcma_gpio_init(struct bcma_drv_cc *cc) { + struct bcma_bus *bus = cc->core->bus; struct gpio_chip *chip = &cc->gpio; int err; @@ -222,7 +225,7 @@ int bcma_gpio_init(struct bcma_drv_cc *cc) if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC) chip->of_node = cc->core->dev.of_node; #endif - switch (cc->core->bus->chipinfo.id) { + switch (bus->chipinfo.id) { case BCMA_CHIP_ID_BCM5357: case BCMA_CHIP_ID_BCM53572: chip->ngpio = 32; @@ -231,13 +234,17 @@ int bcma_gpio_init(struct bcma_drv_cc *cc) chip->ngpio = 16; } - /* There is just one SoC in one device and its GPIO addresses should be - * deterministic to address them more easily. The other buses could get - * a random base number. */ - if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC) - chip->base = 0; - else - chip->base = -1; + /* + * On MIPS we register GPIO devices (LEDs, buttons) using absolute GPIO + * pin numbers. We don't have Device Tree there and we can't really use + * relative (per chip) numbers. + * So let's use predictable base for BCM47XX and "random" for all other. + */ +#if IS_BUILTIN(CONFIG_BCM47XX) + chip->base = bus->num * BCMA_GPIO_MAX_PINS; +#else + chip->base = -1; +#endif err = bcma_gpio_irq_domain_init(cc); if (err) diff --git a/drivers/bcma/driver_pci.c b/drivers/bcma/driver_pci.c index cfd35bc1c5a3..f499a469e66d 100644 --- a/drivers/bcma/driver_pci.c +++ b/drivers/bcma/driver_pci.c @@ -282,39 +282,6 @@ void bcma_core_pci_power_save(struct bcma_bus *bus, bool up) } EXPORT_SYMBOL_GPL(bcma_core_pci_power_save); -int bcma_core_pci_irq_ctl(struct bcma_bus *bus, struct bcma_device *core, - bool enable) -{ - struct pci_dev *pdev; - u32 coremask, tmp; - int err = 0; - - if (bus->hosttype != BCMA_HOSTTYPE_PCI) { - /* This bcma device is not on a PCI host-bus. So the IRQs are - * not routed through the PCI core. - * So we must not enable routing through the PCI core. */ - goto out; - } - - pdev = bus->host_pci; - - err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp); - if (err) - goto out; - - coremask = BIT(core->core_index) << 8; - if (enable) - tmp |= coremask; - else - tmp &= ~coremask; - - err = pci_write_config_dword(pdev, BCMA_PCI_IRQMASK, tmp); - -out: - return err; -} -EXPORT_SYMBOL_GPL(bcma_core_pci_irq_ctl); - static void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend) { u32 w; diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c index a62a2f9091f5..0856189c065f 100644 --- a/drivers/bcma/host_pci.c +++ b/drivers/bcma/host_pci.c @@ -351,3 +351,37 @@ void bcma_host_pci_down(struct bcma_bus *bus) bcma_core_pci_down(&bus->drv_pci[0]); } EXPORT_SYMBOL_GPL(bcma_host_pci_down); + +/* See also si_pci_setup */ +int bcma_host_pci_irq_ctl(struct bcma_bus *bus, struct bcma_device *core, + bool enable) +{ + struct pci_dev *pdev; + u32 coremask, tmp; + int err = 0; + + if (bus->hosttype != BCMA_HOSTTYPE_PCI) { + /* This bcma device is not on a PCI host-bus. So the IRQs are + * not routed through the PCI core. + * So we must not enable routing through the PCI core. */ + goto out; + } + + pdev = bus->host_pci; + + err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp); + if (err) + goto out; + + coremask = BIT(core->core_index) << 8; + if (enable) + tmp |= coremask; + else + tmp &= ~coremask; + + err = pci_write_config_dword(pdev, BCMA_PCI_IRQMASK, tmp); + +out: + return err; +} +EXPORT_SYMBOL_GPL(bcma_host_pci_irq_ctl); diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 4bc2a5cb9935..a98c41f72c63 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -803,10 +803,6 @@ static int __init nbd_init(void) return -EINVAL; } - nbd_dev = kcalloc(nbds_max, sizeof(*nbd_dev), GFP_KERNEL); - if (!nbd_dev) - return -ENOMEM; - part_shift = 0; if (max_part > 0) { part_shift = fls(max_part); @@ -828,6 +824,10 @@ static int __init nbd_init(void) if (nbds_max > 1UL << (MINORBITS - part_shift)) return -EINVAL; + nbd_dev = kcalloc(nbds_max, sizeof(*nbd_dev), GFP_KERNEL); + if (!nbd_dev) + return -ENOMEM; + for (i = 0; i < nbds_max; i++) { struct gendisk *disk = alloc_disk(1 << part_shift); if (!disk) diff --git a/drivers/block/nvme-core.c b/drivers/block/nvme-core.c index ceb32dd52a6c..e23be20a3417 100644 --- a/drivers/block/nvme-core.c +++ b/drivers/block/nvme-core.c @@ -3003,6 +3003,7 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) } get_device(dev->device); + INIT_LIST_HEAD(&dev->node); INIT_WORK(&dev->probe_work, nvme_async_probe); schedule_work(&dev->probe_work); return 0; diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 9bf4d6ae6c6b..6e4ff16e487b 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -111,13 +111,7 @@ static const struct usb_device_id btusb_table[] = { { USB_DEVICE(0x0c10, 0x0000) }, /* Broadcom BCM20702A0 */ - { USB_DEVICE(0x0489, 0xe042) }, - { USB_DEVICE(0x04ca, 0x2003) }, - { USB_DEVICE(0x0b05, 0x17b5) }, - { USB_DEVICE(0x0b05, 0x17cb) }, { USB_DEVICE(0x413c, 0x8197) }, - { USB_DEVICE(0x13d3, 0x3404), - .driver_info = BTUSB_BCM_PATCHRAM }, /* Broadcom BCM20702B0 (Dynex/Insignia) */ { USB_DEVICE(0x19ff, 0x0239), .driver_info = BTUSB_BCM_PATCHRAM }, @@ -139,10 +133,12 @@ static const struct usb_device_id btusb_table[] = { .driver_info = BTUSB_BCM_PATCHRAM }, /* Belkin F8065bf - Broadcom based */ - { USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01) }, + { USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01), + .driver_info = BTUSB_BCM_PATCHRAM }, /* IMC Networks - Broadcom based */ - { USB_VENDOR_AND_INTERFACE_INFO(0x13d3, 0xff, 0x01, 0x01) }, + { USB_VENDOR_AND_INTERFACE_INFO(0x13d3, 0xff, 0x01, 0x01), + .driver_info = BTUSB_BCM_PATCHRAM }, /* Intel Bluetooth USB Bootloader (RAM module) */ { USB_DEVICE(0x8087, 0x0a5a), diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 48a0c250d5b8..1363dc616ace 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -499,7 +499,7 @@ static int hci_uart_set_flags(struct hci_uart *hu, unsigned long flags) BIT(HCI_UART_INIT_PENDING) | BIT(HCI_UART_EXT_CONFIG); - if ((flags & ~valid_flags)) + if (flags & ~valid_flags) return -EINVAL; hu->hdev_flags = flags; diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 68161f7a07d6..a0b036ccb118 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -192,6 +192,7 @@ config SYS_SUPPORTS_EM_STI config SH_TIMER_CMT bool "Renesas CMT timer driver" if COMPILE_TEST depends on GENERIC_CLOCKEVENTS + depends on HAS_IOMEM default SYS_SUPPORTS_SH_CMT help This enables build of a clocksource and clockevent driver for @@ -201,6 +202,7 @@ config SH_TIMER_CMT config SH_TIMER_MTU2 bool "Renesas MTU2 timer driver" if COMPILE_TEST depends on GENERIC_CLOCKEVENTS + depends on HAS_IOMEM default SYS_SUPPORTS_SH_MTU2 help This enables build of a clockevent driver for the Multi-Function @@ -210,6 +212,7 @@ config SH_TIMER_MTU2 config SH_TIMER_TMU bool "Renesas TMU timer driver" if COMPILE_TEST depends on GENERIC_CLOCKEVENTS + depends on HAS_IOMEM default SYS_SUPPORTS_SH_TMU help This enables build of a clocksource and clockevent driver for diff --git a/drivers/clocksource/timer-sun5i.c b/drivers/clocksource/timer-sun5i.c index 5dcbf90b8015..58597fbcc046 100644 --- a/drivers/clocksource/timer-sun5i.c +++ b/drivers/clocksource/timer-sun5i.c @@ -17,7 +17,6 @@ #include <linux/irq.h> #include <linux/irqreturn.h> #include <linux/reset.h> -#include <linux/sched_clock.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> @@ -137,11 +136,6 @@ static struct irqaction sun5i_timer_irq = { .dev_id = &sun5i_clockevent, }; -static u64 sun5i_timer_sched_read(void) -{ - return ~readl(timer_base + TIMER_CNTVAL_LO_REG(1)); -} - static void __init sun5i_timer_init(struct device_node *node) { struct reset_control *rstc; @@ -172,7 +166,6 @@ static void __init sun5i_timer_init(struct device_node *node) writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, timer_base + TIMER_CTL_REG(1)); - sched_clock_register(sun5i_timer_sched_read, 32, rate); clocksource_mmio_init(timer_base + TIMER_CNTVAL_LO_REG(1), node->name, rate, 340, 32, clocksource_mmio_readl_down); diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c index 0723096fb50a..c92d6a70ccf3 100644 --- a/drivers/dma/bcm2835-dma.c +++ b/drivers/dma/bcm2835-dma.c @@ -475,6 +475,7 @@ static int bcm2835_dma_terminate_all(struct dma_chan *chan) * c->desc is NULL and exit.) */ if (c->desc) { + bcm2835_dma_desc_free(&c->desc->vd); c->desc = NULL; bcm2835_dma_abort(c->chan_base); diff --git a/drivers/dma/dma-jz4740.c b/drivers/dma/dma-jz4740.c index 4527a3ebeac4..84884418fd30 100644 --- a/drivers/dma/dma-jz4740.c +++ b/drivers/dma/dma-jz4740.c @@ -511,6 +511,9 @@ static void jz4740_dma_desc_free(struct virt_dma_desc *vdesc) kfree(container_of(vdesc, struct jz4740_dma_desc, vdesc)); } +#define JZ4740_DMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | BIT(DMA_SLAVE_BUSWIDTH_4_BYTES)) + static int jz4740_dma_probe(struct platform_device *pdev) { struct jz4740_dmaengine_chan *chan; @@ -548,6 +551,10 @@ static int jz4740_dma_probe(struct platform_device *pdev) dd->device_prep_dma_cyclic = jz4740_dma_prep_dma_cyclic; dd->device_config = jz4740_dma_slave_config; dd->device_terminate_all = jz4740_dma_terminate_all; + dd->src_addr_widths = JZ4740_DMA_BUSWIDTHS; + dd->dst_addr_widths = JZ4740_DMA_BUSWIDTHS; + dd->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + dd->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; dd->dev = &pdev->dev; INIT_LIST_HEAD(&dd->channels); diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 276157f22612..53dbd3b3384c 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -260,6 +260,13 @@ static int edma_terminate_all(struct dma_chan *chan) */ if (echan->edesc) { int cyclic = echan->edesc->cyclic; + + /* + * free the running request descriptor + * since it is not in any of the vdesc lists + */ + edma_desc_free(&echan->edesc->vdesc); + echan->edesc = NULL; edma_stop(echan->ch_num); /* Move the cyclic channel back to default queue */ diff --git a/drivers/dma/moxart-dma.c b/drivers/dma/moxart-dma.c index 15cab7d79525..b4634109e010 100644 --- a/drivers/dma/moxart-dma.c +++ b/drivers/dma/moxart-dma.c @@ -193,8 +193,10 @@ static int moxart_terminate_all(struct dma_chan *chan) spin_lock_irqsave(&ch->vc.lock, flags); - if (ch->desc) + if (ch->desc) { + moxart_dma_desc_free(&ch->desc->vd); ch->desc = NULL; + } ctrl = readl(ch->base + REG_OFF_CTRL); ctrl &= ~(APB_DMA_ENABLE | APB_DMA_FIN_INT_EN | APB_DMA_ERR_INT_EN); diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c index 7dd6dd121681..167dbaf65742 100644 --- a/drivers/dma/omap-dma.c +++ b/drivers/dma/omap-dma.c @@ -981,6 +981,7 @@ static int omap_dma_terminate_all(struct dma_chan *chan) * c->desc is NULL and exit.) */ if (c->desc) { + omap_dma_desc_free(&c->desc->vd); c->desc = NULL; /* Avoid stopping the dma twice */ if (!c->paused) diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index 69fac068669f..2eebd28b4c40 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -86,10 +86,13 @@ static void dmi_table(u8 *buf, u32 len, int num, int i = 0; /* - * Stop when we see all the items the table claimed to have - * OR we run off the end of the table (also happens) + * Stop when we have seen all the items the table claimed to have + * (SMBIOS < 3.0 only) OR we reach an end-of-table marker OR we run + * off the end of the table (should never happen but sometimes does + * on bogus implementations.) */ - while ((i < num) && (data - buf + sizeof(struct dmi_header)) <= len) { + while ((!num || i < num) && + (data - buf + sizeof(struct dmi_header)) <= len) { const struct dmi_header *dm = (const struct dmi_header *)data; /* @@ -529,21 +532,10 @@ static int __init dmi_smbios3_present(const u8 *buf) if (memcmp(buf, "_SM3_", 5) == 0 && buf[6] < 32 && dmi_checksum(buf, buf[6])) { dmi_ver = get_unaligned_be16(buf + 7); + dmi_num = 0; /* No longer specified */ dmi_len = get_unaligned_le32(buf + 12); dmi_base = get_unaligned_le64(buf + 16); - /* - * The 64-bit SMBIOS 3.0 entry point no longer has a field - * containing the number of structures present in the table. - * Instead, it defines the table size as a maximum size, and - * relies on the end-of-table structure type (#127) to be used - * to signal the end of the table. - * So let's define dmi_num as an upper bound as well: each - * structure has a 4 byte header, so dmi_len / 4 is an upper - * bound for the number of structures in the table. - */ - dmi_num = dmi_len / 4; - if (dmi_walk_early(dmi_decode) == 0) { pr_info("SMBIOS %d.%d present.\n", dmi_ver >> 8, dmi_ver & 0xFF); diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index a6952ba343a8..a65b75161aa4 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -334,7 +334,7 @@ static struct irq_domain_ops mpc8xxx_gpio_irq_ops = { .xlate = irq_domain_xlate_twocell, }; -static struct of_device_id mpc8xxx_gpio_ids[] __initdata = { +static struct of_device_id mpc8xxx_gpio_ids[] = { { .compatible = "fsl,mpc8349-gpio", }, { .compatible = "fsl,mpc8572-gpio", }, { .compatible = "fsl,mpc8610-gpio", }, diff --git a/drivers/gpio/gpio-syscon.c b/drivers/gpio/gpio-syscon.c index 257e2989215c..045a952576c7 100644 --- a/drivers/gpio/gpio-syscon.c +++ b/drivers/gpio/gpio-syscon.c @@ -219,7 +219,7 @@ static int syscon_gpio_probe(struct platform_device *pdev) ret = of_property_read_u32_index(np, "gpio,syscon-dev", 2, &priv->dir_reg_offset); if (ret) - dev_err(dev, "can't read the dir register offset!\n"); + dev_dbg(dev, "can't read the dir register offset!\n"); priv->dir_reg_offset <<= 3; } diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index c0929d938ced..df990f29757a 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -201,6 +201,10 @@ static acpi_status acpi_gpiochip_request_interrupt(struct acpi_resource *ares, if (!handler) return AE_BAD_PARAMETER; + pin = acpi_gpiochip_pin_to_gpio_offset(chip, pin); + if (pin < 0) + return AE_BAD_PARAMETER; + desc = gpiochip_request_own_desc(chip, pin, "ACPI:Event"); if (IS_ERR(desc)) { dev_err(chip->dev, "Failed to request GPIO\n"); @@ -551,6 +555,12 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, struct gpio_desc *desc; bool found; + pin = acpi_gpiochip_pin_to_gpio_offset(chip, pin); + if (pin < 0) { + status = AE_BAD_PARAMETER; + goto out; + } + mutex_lock(&achip->conn_lock); found = false; diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index f6d04c7b5115..679b10e34fb5 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -525,17 +525,6 @@ void drm_framebuffer_reference(struct drm_framebuffer *fb) } EXPORT_SYMBOL(drm_framebuffer_reference); -static void drm_framebuffer_free_bug(struct kref *kref) -{ - BUG(); -} - -static void __drm_framebuffer_unreference(struct drm_framebuffer *fb) -{ - DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount)); - kref_put(&fb->refcount, drm_framebuffer_free_bug); -} - /** * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr * @fb: fb to unregister @@ -1320,7 +1309,7 @@ void drm_plane_force_disable(struct drm_plane *plane) return; } /* disconnect the plane from the fb and crtc: */ - __drm_framebuffer_unreference(plane->old_fb); + drm_framebuffer_unreference(plane->old_fb); plane->old_fb = NULL; plane->fb = NULL; plane->crtc = NULL; diff --git a/drivers/gpu/drm/drm_edid_load.c b/drivers/gpu/drm/drm_edid_load.c index 732cb6f8e653..4c0aa97aaf03 100644 --- a/drivers/gpu/drm/drm_edid_load.c +++ b/drivers/gpu/drm/drm_edid_load.c @@ -287,6 +287,7 @@ int drm_load_edid_firmware(struct drm_connector *connector) drm_mode_connector_update_edid_property(connector, edid); ret = drm_add_edid_modes(connector, edid); + drm_edid_to_eld(connector, edid); kfree(edid); return ret; diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 6591d48c1b9d..3fee587bc284 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -174,6 +174,7 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect struct edid *edid = (struct edid *) connector->edid_blob_ptr->data; count = drm_add_edid_modes(connector, edid); + drm_edid_to_eld(connector, edid); } else count = (*connector_funcs->get_modes)(connector); } diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index c300e22da8ac..33a10ce967ea 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -147,6 +147,7 @@ struct fimd_win_data { unsigned int ovl_height; unsigned int fb_width; unsigned int fb_height; + unsigned int fb_pitch; unsigned int bpp; unsigned int pixel_format; dma_addr_t dma_addr; @@ -532,13 +533,14 @@ static void fimd_win_mode_set(struct exynos_drm_crtc *crtc, win_data->offset_y = plane->crtc_y; win_data->ovl_width = plane->crtc_width; win_data->ovl_height = plane->crtc_height; + win_data->fb_pitch = plane->pitch; win_data->fb_width = plane->fb_width; win_data->fb_height = plane->fb_height; win_data->dma_addr = plane->dma_addr[0] + offset; win_data->bpp = plane->bpp; win_data->pixel_format = plane->pixel_format; - win_data->buf_offsize = (plane->fb_width - plane->crtc_width) * - (plane->bpp >> 3); + win_data->buf_offsize = + plane->pitch - (plane->crtc_width * (plane->bpp >> 3)); win_data->line_size = plane->crtc_width * (plane->bpp >> 3); DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n", @@ -704,7 +706,7 @@ static void fimd_win_commit(struct exynos_drm_crtc *crtc, int zpos) writel(val, ctx->regs + VIDWx_BUF_START(win, 0)); /* buffer end address */ - size = win_data->fb_width * win_data->ovl_height * (win_data->bpp >> 3); + size = win_data->fb_pitch * win_data->ovl_height * (win_data->bpp >> 3); val = (unsigned long)(win_data->dma_addr + size); writel(val, ctx->regs + VIDWx_BUF_END(win, 0)); diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index 3518bc4654c5..2e3bc57ea50e 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -55,6 +55,7 @@ struct hdmi_win_data { unsigned int fb_x; unsigned int fb_y; unsigned int fb_width; + unsigned int fb_pitch; unsigned int fb_height; unsigned int src_width; unsigned int src_height; @@ -438,7 +439,7 @@ static void vp_video_buffer(struct mixer_context *ctx, int win) } else { luma_addr[0] = win_data->dma_addr; chroma_addr[0] = win_data->dma_addr - + (win_data->fb_width * win_data->fb_height); + + (win_data->fb_pitch * win_data->fb_height); } if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE) { @@ -447,8 +448,8 @@ static void vp_video_buffer(struct mixer_context *ctx, int win) luma_addr[1] = luma_addr[0] + 0x40; chroma_addr[1] = chroma_addr[0] + 0x40; } else { - luma_addr[1] = luma_addr[0] + win_data->fb_width; - chroma_addr[1] = chroma_addr[0] + win_data->fb_width; + luma_addr[1] = luma_addr[0] + win_data->fb_pitch; + chroma_addr[1] = chroma_addr[0] + win_data->fb_pitch; } } else { ctx->interlace = false; @@ -469,10 +470,10 @@ static void vp_video_buffer(struct mixer_context *ctx, int win) vp_reg_writemask(res, VP_MODE, val, VP_MODE_FMT_MASK); /* setting size of input image */ - vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(win_data->fb_width) | + vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(win_data->fb_pitch) | VP_IMG_VSIZE(win_data->fb_height)); /* chroma height has to reduced by 2 to avoid chroma distorions */ - vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(win_data->fb_width) | + vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(win_data->fb_pitch) | VP_IMG_VSIZE(win_data->fb_height / 2)); vp_reg_write(res, VP_SRC_WIDTH, win_data->src_width); @@ -559,7 +560,7 @@ static void mixer_graph_buffer(struct mixer_context *ctx, int win) /* converting dma address base and source offset */ dma_addr = win_data->dma_addr + (win_data->fb_x * win_data->bpp >> 3) - + (win_data->fb_y * win_data->fb_width * win_data->bpp >> 3); + + (win_data->fb_y * win_data->fb_pitch); src_x_offset = 0; src_y_offset = 0; @@ -576,7 +577,8 @@ static void mixer_graph_buffer(struct mixer_context *ctx, int win) MXR_GRP_CFG_FORMAT_VAL(fmt), MXR_GRP_CFG_FORMAT_MASK); /* setup geometry */ - mixer_reg_write(res, MXR_GRAPHIC_SPAN(win), win_data->fb_width); + mixer_reg_write(res, MXR_GRAPHIC_SPAN(win), + win_data->fb_pitch / (win_data->bpp >> 3)); /* setup display size */ if (ctx->mxr_ver == MXR_VER_128_0_0_184 && @@ -961,6 +963,7 @@ static void mixer_win_mode_set(struct exynos_drm_crtc *crtc, win_data->fb_y = plane->fb_y; win_data->fb_width = plane->fb_width; win_data->fb_height = plane->fb_height; + win_data->fb_pitch = plane->pitch; win_data->src_width = plane->src_width; win_data->src_height = plane->src_height; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 5b205863b659..27ea6bdebce7 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2737,24 +2737,11 @@ i915_gem_retire_requests_ring(struct intel_engine_cs *ring) WARN_ON(i915_verify_lists(ring->dev)); - /* Move any buffers on the active list that are no longer referenced - * by the ringbuffer to the flushing/inactive lists as appropriate, - * before we free the context associated with the requests. + /* Retire requests first as we use it above for the early return. + * If we retire requests last, we may use a later seqno and so clear + * the requests lists without clearing the active list, leading to + * confusion. */ - while (!list_empty(&ring->active_list)) { - struct drm_i915_gem_object *obj; - - obj = list_first_entry(&ring->active_list, - struct drm_i915_gem_object, - ring_list); - - if (!i915_gem_request_completed(obj->last_read_req, true)) - break; - - i915_gem_object_move_to_inactive(obj); - } - - while (!list_empty(&ring->request_list)) { struct drm_i915_gem_request *request; struct intel_ringbuffer *ringbuf; @@ -2789,6 +2776,23 @@ i915_gem_retire_requests_ring(struct intel_engine_cs *ring) i915_gem_free_request(request); } + /* Move any buffers on the active list that are no longer referenced + * by the ringbuffer to the flushing/inactive lists as appropriate, + * before we free the context associated with the requests. + */ + while (!list_empty(&ring->active_list)) { + struct drm_i915_gem_object *obj; + + obj = list_first_entry(&ring->active_list, + struct drm_i915_gem_object, + ring_list); + + if (!i915_gem_request_completed(obj->last_read_req, true)) + break; + + i915_gem_object_move_to_inactive(obj); + } + if (unlikely(ring->trace_irq_req && i915_gem_request_completed(ring->trace_irq_req, true))) { ring->irq_put(ring); diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index b773368fc62c..38a742532c4f 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -1487,7 +1487,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, goto err; } - if (i915_needs_cmd_parser(ring)) { + if (i915_needs_cmd_parser(ring) && args->batch_len) { batch_obj = i915_gem_execbuffer_parse(ring, &shadow_exec_entry, eb, diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 6d22128d97b1..f75173c20f47 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2438,8 +2438,15 @@ intel_find_plane_obj(struct intel_crtc *intel_crtc, if (!intel_crtc->base.primary->fb) return; - if (intel_alloc_plane_obj(intel_crtc, plane_config)) + if (intel_alloc_plane_obj(intel_crtc, plane_config)) { + struct drm_plane *primary = intel_crtc->base.primary; + + primary->state->crtc = &intel_crtc->base; + primary->crtc = &intel_crtc->base; + update_state_fb(primary); + return; + } kfree(intel_crtc->base.primary->fb); intel_crtc->base.primary->fb = NULL; @@ -2462,11 +2469,15 @@ intel_find_plane_obj(struct intel_crtc *intel_crtc, continue; if (i915_gem_obj_ggtt_offset(obj) == plane_config->base) { + struct drm_plane *primary = intel_crtc->base.primary; + if (obj->tiling_mode != I915_TILING_NONE) dev_priv->preserve_bios_swizzle = true; drm_framebuffer_reference(c->primary->fb); - intel_crtc->base.primary->fb = c->primary->fb; + primary->fb = c->primary->fb; + primary->state->crtc = &intel_crtc->base; + primary->crtc = &intel_crtc->base; obj->frontbuffer_bits |= INTEL_FRONTBUFFER_PRIMARY(intel_crtc->pipe); break; } @@ -6663,7 +6674,6 @@ i9xx_get_initial_plane_config(struct intel_crtc *crtc, plane_config->size); crtc->base.primary->fb = fb; - update_state_fb(crtc->base.primary); } static void chv_crtc_clock_get(struct intel_crtc *crtc, @@ -7704,7 +7714,6 @@ skylake_get_initial_plane_config(struct intel_crtc *crtc, plane_config->size); crtc->base.primary->fb = fb; - update_state_fb(crtc->base.primary); return; error: @@ -7798,7 +7807,6 @@ ironlake_get_initial_plane_config(struct intel_crtc *crtc, plane_config->size); crtc->base.primary->fb = fb; - update_state_fb(crtc->base.primary); } static bool ironlake_get_pipe_config(struct intel_crtc *crtc, diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 0a52c44ad03d..9c5451c97942 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -1322,7 +1322,7 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data, drm_modeset_lock_all(dev); plane = drm_plane_find(dev, set->plane_id); - if (!plane) { + if (!plane || plane->type != DRM_PLANE_TYPE_OVERLAY) { ret = -ENOENT; goto out_unlock; } @@ -1349,7 +1349,7 @@ int intel_sprite_get_colorkey(struct drm_device *dev, void *data, drm_modeset_lock_all(dev); plane = drm_plane_find(dev, get->plane_id); - if (!plane) { + if (!plane || plane->type != DRM_PLANE_TYPE_OVERLAY) { ret = -ENOENT; goto out_unlock; } diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h index c648e1996dab..243a36c93b8f 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -2129,6 +2129,7 @@ #define VCE_UENC_REG_CLOCK_GATING 0x207c0 #define VCE_SYS_INT_EN 0x21300 # define VCE_SYS_INT_TRAP_INTERRUPT_EN (1 << 3) +#define VCE_LMI_VCPU_CACHE_40BIT_BAR 0x2145c #define VCE_LMI_CTRL2 0x21474 #define VCE_LMI_CTRL 0x21498 #define VCE_LMI_VM_CTRL 0x214a0 diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 5587603b4a89..33d5a4f4eebd 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1565,6 +1565,7 @@ struct radeon_dpm { int new_active_crtc_count; u32 current_active_crtcs; int current_active_crtc_count; + bool single_display; struct radeon_dpm_dynamic_state dyn_state; struct radeon_dpm_fan fan; u32 tdp_limit; diff --git a/drivers/gpu/drm/radeon/radeon_bios.c b/drivers/gpu/drm/radeon/radeon_bios.c index 63ccb8fa799c..d27e4ccb848c 100644 --- a/drivers/gpu/drm/radeon/radeon_bios.c +++ b/drivers/gpu/drm/radeon/radeon_bios.c @@ -76,7 +76,7 @@ static bool igp_read_bios_from_vram(struct radeon_device *rdev) static bool radeon_read_bios(struct radeon_device *rdev) { - uint8_t __iomem *bios; + uint8_t __iomem *bios, val1, val2; size_t size; rdev->bios = NULL; @@ -86,15 +86,19 @@ static bool radeon_read_bios(struct radeon_device *rdev) return false; } - if (size == 0 || bios[0] != 0x55 || bios[1] != 0xaa) { + val1 = readb(&bios[0]); + val2 = readb(&bios[1]); + + if (size == 0 || val1 != 0x55 || val2 != 0xaa) { pci_unmap_rom(rdev->pdev, bios); return false; } - rdev->bios = kmemdup(bios, size, GFP_KERNEL); + rdev->bios = kzalloc(size, GFP_KERNEL); if (rdev->bios == NULL) { pci_unmap_rom(rdev->pdev, bios); return false; } + memcpy_fromio(rdev->bios, bios, size); pci_unmap_rom(rdev->pdev, bios); return true; } diff --git a/drivers/gpu/drm/radeon/radeon_mn.c b/drivers/gpu/drm/radeon/radeon_mn.c index a69bd441dd2d..572b4dbec186 100644 --- a/drivers/gpu/drm/radeon/radeon_mn.c +++ b/drivers/gpu/drm/radeon/radeon_mn.c @@ -122,7 +122,6 @@ static void radeon_mn_invalidate_range_start(struct mmu_notifier *mn, it = interval_tree_iter_first(&rmn->objects, start, end); while (it) { struct radeon_bo *bo; - struct fence *fence; int r; bo = container_of(it, struct radeon_bo, mn_it); @@ -134,12 +133,10 @@ static void radeon_mn_invalidate_range_start(struct mmu_notifier *mn, continue; } - fence = reservation_object_get_excl(bo->tbo.resv); - if (fence) { - r = radeon_fence_wait((struct radeon_fence *)fence, false); - if (r) - DRM_ERROR("(%d) failed to wait for user bo\n", r); - } + r = reservation_object_wait_timeout_rcu(bo->tbo.resv, true, + false, MAX_SCHEDULE_TIMEOUT); + if (r) + DRM_ERROR("(%d) failed to wait for user bo\n", r); radeon_ttm_placement_from_domain(bo, RADEON_GEM_DOMAIN_CPU); r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false); diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 33cf4108386d..c1ba83a8dd8c 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -837,12 +837,8 @@ static void radeon_dpm_thermal_work_handler(struct work_struct *work) radeon_pm_compute_clocks(rdev); } -static struct radeon_ps *radeon_dpm_pick_power_state(struct radeon_device *rdev, - enum radeon_pm_state_type dpm_state) +static bool radeon_dpm_single_display(struct radeon_device *rdev) { - int i; - struct radeon_ps *ps; - u32 ui_class; bool single_display = (rdev->pm.dpm.new_active_crtc_count < 2) ? true : false; @@ -858,6 +854,17 @@ static struct radeon_ps *radeon_dpm_pick_power_state(struct radeon_device *rdev, if (single_display && (r600_dpm_get_vrefresh(rdev) >= 120)) single_display = false; + return single_display; +} + +static struct radeon_ps *radeon_dpm_pick_power_state(struct radeon_device *rdev, + enum radeon_pm_state_type dpm_state) +{ + int i; + struct radeon_ps *ps; + u32 ui_class; + bool single_display = radeon_dpm_single_display(rdev); + /* certain older asics have a separare 3D performance state, * so try that first if the user selected performance */ @@ -983,6 +990,7 @@ static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev) struct radeon_ps *ps; enum radeon_pm_state_type dpm_state; int ret; + bool single_display = radeon_dpm_single_display(rdev); /* if dpm init failed */ if (!rdev->pm.dpm_enabled) @@ -1007,6 +1015,9 @@ static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev) /* vce just modifies an existing state so force a change */ if (ps->vce_active != rdev->pm.dpm.vce_active) goto force; + /* user has made a display change (such as timing) */ + if (rdev->pm.dpm.single_display != single_display) + goto force; if ((rdev->family < CHIP_BARTS) || (rdev->flags & RADEON_IS_IGP)) { /* for pre-BTC and APUs if the num crtcs changed but state is the same, * all we need to do is update the display configuration. @@ -1069,6 +1080,7 @@ force: rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs; rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count; + rdev->pm.dpm.single_display = single_display; /* wait for the rings to drain */ for (i = 0; i < RADEON_NUM_RINGS; i++) { diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c index 2456f69efd23..8c7872339c2a 100644 --- a/drivers/gpu/drm/radeon/radeon_ring.c +++ b/drivers/gpu/drm/radeon/radeon_ring.c @@ -495,7 +495,7 @@ static int radeon_debugfs_ring_info(struct seq_file *m, void *data) seq_printf(m, "%u free dwords in ring\n", ring->ring_free_dw); seq_printf(m, "%u dwords in ring\n", count); - if (!ring->ready) + if (!ring->ring) return 0; /* print 8 dw before current rptr as often it's the last executed diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index d02aa1d0f588..b292aca0f342 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -598,6 +598,10 @@ static void radeon_ttm_tt_unpin_userptr(struct ttm_tt *ttm) enum dma_data_direction direction = write ? DMA_BIDIRECTIONAL : DMA_TO_DEVICE; + /* double check that we don't free the table twice */ + if (!ttm->sg->sgl) + return; + /* free the sg table and pages again */ dma_unmap_sg(rdev->dev, ttm->sg->sgl, ttm->sg->nents, direction); diff --git a/drivers/gpu/drm/radeon/vce_v2_0.c b/drivers/gpu/drm/radeon/vce_v2_0.c index 1ac7bb825a1b..fbbe78fbd087 100644 --- a/drivers/gpu/drm/radeon/vce_v2_0.c +++ b/drivers/gpu/drm/radeon/vce_v2_0.c @@ -156,6 +156,9 @@ int vce_v2_0_resume(struct radeon_device *rdev) WREG32(VCE_LMI_SWAP_CNTL1, 0); WREG32(VCE_LMI_VM_CTRL, 0); + WREG32(VCE_LMI_VCPU_CACHE_40BIT_BAR, addr >> 8); + + addr &= 0xff; size = RADEON_GPU_PAGE_ALIGN(rdev->vce_fw->size); WREG32(VCE_VCPU_CACHE_OFFSET0, addr & 0x7fffffff); WREG32(VCE_VCPU_CACHE_SIZE0, size); diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index 1096da327130..75c6d2103e07 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -659,7 +659,7 @@ static irqreturn_t bma180_trigger_handler(int irq, void *p) mutex_lock(&data->mutex); - for_each_set_bit(bit, indio_dev->buffer->scan_mask, + for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { ret = bma180_get_data_reg(data, bit); if (ret < 0) { diff --git a/drivers/iio/accel/bmc150-accel.c b/drivers/iio/accel/bmc150-accel.c index 066d0c04072c..75567fd457dc 100644 --- a/drivers/iio/accel/bmc150-accel.c +++ b/drivers/iio/accel/bmc150-accel.c @@ -168,14 +168,14 @@ static const struct { int val; int val2; u8 bw_bits; -} bmc150_accel_samp_freq_table[] = { {7, 810000, 0x08}, - {15, 630000, 0x09}, - {31, 250000, 0x0A}, - {62, 500000, 0x0B}, - {125, 0, 0x0C}, - {250, 0, 0x0D}, - {500, 0, 0x0E}, - {1000, 0, 0x0F} }; +} bmc150_accel_samp_freq_table[] = { {15, 620000, 0x08}, + {31, 260000, 0x09}, + {62, 500000, 0x0A}, + {125, 0, 0x0B}, + {250, 0, 0x0C}, + {500, 0, 0x0D}, + {1000, 0, 0x0E}, + {2000, 0, 0x0F} }; static const struct { int bw_bits; @@ -840,7 +840,7 @@ static int bmc150_accel_validate_trigger(struct iio_dev *indio_dev, } static IIO_CONST_ATTR_SAMP_FREQ_AVAIL( - "7.810000 15.630000 31.250000 62.500000 125 250 500 1000"); + "15.620000 31.260000 62.50000 125 250 500 1000 2000"); static struct attribute *bmc150_accel_attributes[] = { &iio_const_attr_sampling_frequency_available.dev_attr.attr, @@ -986,7 +986,7 @@ static irqreturn_t bmc150_accel_trigger_handler(int irq, void *p) int bit, ret, i = 0; mutex_lock(&data->mutex); - for_each_set_bit(bit, indio_dev->buffer->scan_mask, + for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { ret = i2c_smbus_read_word_data(data->client, BMC150_ACCEL_AXIS_TO_REG(bit)); diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index 567de269cc00..1a6379525fa4 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -956,7 +956,7 @@ static irqreturn_t kxcjk1013_trigger_handler(int irq, void *p) mutex_lock(&data->mutex); - for_each_set_bit(bit, indio_dev->buffer->scan_mask, + for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { ret = kxcjk1013_get_acc_reg(data, bit); if (ret < 0) { diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 202daf889be2..46379b1fb25b 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -137,7 +137,8 @@ config AXP288_ADC config CC10001_ADC tristate "Cosmic Circuits 10001 ADC driver" - depends on HAS_IOMEM || HAVE_CLK || REGULATOR + depends on HAVE_CLK || REGULATOR + depends on HAS_IOMEM select IIO_BUFFER select IIO_TRIGGERED_BUFFER help diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index ff61ae55dd3f..8a0eb4a04fb5 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -544,7 +544,6 @@ static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state) { struct iio_dev *idev = iio_trigger_get_drvdata(trig); struct at91_adc_state *st = iio_priv(idev); - struct iio_buffer *buffer = idev->buffer; struct at91_adc_reg_desc *reg = st->registers; u32 status = at91_adc_readl(st, reg->trigger_register); int value; @@ -564,7 +563,7 @@ static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state) at91_adc_writel(st, reg->trigger_register, status | value); - for_each_set_bit(bit, buffer->scan_mask, + for_each_set_bit(bit, idev->active_scan_mask, st->num_channels) { struct iio_chan_spec const *chan = idev->channels + bit; at91_adc_writel(st, AT91_ADC_CHER, @@ -579,7 +578,7 @@ static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state) at91_adc_writel(st, reg->trigger_register, status & ~value); - for_each_set_bit(bit, buffer->scan_mask, + for_each_set_bit(bit, idev->active_scan_mask, st->num_channels) { struct iio_chan_spec const *chan = idev->channels + bit; at91_adc_writel(st, AT91_ADC_CHDR, diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index 2e5cc4409f78..a0e7161f040c 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -188,12 +188,11 @@ static int tiadc_buffer_preenable(struct iio_dev *indio_dev) static int tiadc_buffer_postenable(struct iio_dev *indio_dev) { struct tiadc_device *adc_dev = iio_priv(indio_dev); - struct iio_buffer *buffer = indio_dev->buffer; unsigned int enb = 0; u8 bit; tiadc_step_config(indio_dev); - for_each_set_bit(bit, buffer->scan_mask, adc_dev->channels) + for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels) enb |= (get_adc_step_bit(adc_dev, bit) << 1); adc_dev->buffer_en_ch_steps = enb; diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c index 8ec353c01d98..e63b8e76d4c3 100644 --- a/drivers/iio/adc/vf610_adc.c +++ b/drivers/iio/adc/vf610_adc.c @@ -141,9 +141,13 @@ struct vf610_adc { struct regulator *vref; struct vf610_adc_feature adc_feature; + u32 sample_freq_avail[5]; + struct completion completion; }; +static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 }; + #define VF610_ADC_CHAN(_idx, _chan_type) { \ .type = (_chan_type), \ .indexed = 1, \ @@ -180,35 +184,47 @@ static const struct iio_chan_spec vf610_adc_iio_channels[] = { /* sentinel */ }; -/* - * ADC sample frequency, unit is ADCK cycles. - * ADC clk source is ipg clock, which is the same as bus clock. - * - * ADC conversion time = SFCAdder + AverageNum x (BCT + LSTAdder) - * SFCAdder: fixed to 6 ADCK cycles - * AverageNum: 1, 4, 8, 16, 32 samples for hardware average. - * BCT (Base Conversion Time): fixed to 25 ADCK cycles for 12 bit mode - * LSTAdder(Long Sample Time): fixed to 3 ADCK cycles - * - * By default, enable 12 bit resolution mode, clock source - * set to ipg clock, So get below frequency group: - */ -static const u32 vf610_sample_freq_avail[5] = -{1941176, 559332, 286957, 145374, 73171}; +static inline void vf610_adc_calculate_rates(struct vf610_adc *info) +{ + unsigned long adck_rate, ipg_rate = clk_get_rate(info->clk); + int i; + + /* + * Calculate ADC sample frequencies + * Sample time unit is ADCK cycles. ADCK clk source is ipg clock, + * which is the same as bus clock. + * + * ADC conversion time = SFCAdder + AverageNum x (BCT + LSTAdder) + * SFCAdder: fixed to 6 ADCK cycles + * AverageNum: 1, 4, 8, 16, 32 samples for hardware average. + * BCT (Base Conversion Time): fixed to 25 ADCK cycles for 12 bit mode + * LSTAdder(Long Sample Time): fixed to 3 ADCK cycles + */ + adck_rate = ipg_rate / info->adc_feature.clk_div; + for (i = 0; i < ARRAY_SIZE(vf610_hw_avgs); i++) + info->sample_freq_avail[i] = + adck_rate / (6 + vf610_hw_avgs[i] * (25 + 3)); +} static inline void vf610_adc_cfg_init(struct vf610_adc *info) { + struct vf610_adc_feature *adc_feature = &info->adc_feature; + /* set default Configuration for ADC controller */ - info->adc_feature.clk_sel = VF610_ADCIOC_BUSCLK_SET; - info->adc_feature.vol_ref = VF610_ADCIOC_VR_VREF_SET; + adc_feature->clk_sel = VF610_ADCIOC_BUSCLK_SET; + adc_feature->vol_ref = VF610_ADCIOC_VR_VREF_SET; + + adc_feature->calibration = true; + adc_feature->ovwren = true; + + adc_feature->res_mode = 12; + adc_feature->sample_rate = 1; + adc_feature->lpm = true; - info->adc_feature.calibration = true; - info->adc_feature.ovwren = true; + /* Use a save ADCK which is below 20MHz on all devices */ + adc_feature->clk_div = 8; - info->adc_feature.clk_div = 1; - info->adc_feature.res_mode = 12; - info->adc_feature.sample_rate = 1; - info->adc_feature.lpm = true; + vf610_adc_calculate_rates(info); } static void vf610_adc_cfg_post_set(struct vf610_adc *info) @@ -290,12 +306,10 @@ static void vf610_adc_cfg_set(struct vf610_adc *info) cfg_data = readl(info->regs + VF610_REG_ADC_CFG); - /* low power configuration */ cfg_data &= ~VF610_ADC_ADLPC_EN; if (adc_feature->lpm) cfg_data |= VF610_ADC_ADLPC_EN; - /* disable high speed */ cfg_data &= ~VF610_ADC_ADHSC_EN; writel(cfg_data, info->regs + VF610_REG_ADC_CFG); @@ -435,10 +449,27 @@ static irqreturn_t vf610_adc_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1941176, 559332, 286957, 145374, 73171"); +static ssize_t vf610_show_samp_freq_avail(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct vf610_adc *info = iio_priv(dev_to_iio_dev(dev)); + size_t len = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(info->sample_freq_avail); i++) + len += scnprintf(buf + len, PAGE_SIZE - len, + "%u ", info->sample_freq_avail[i]); + + /* replace trailing space by newline */ + buf[len - 1] = '\n'; + + return len; +} + +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(vf610_show_samp_freq_avail); static struct attribute *vf610_attributes[] = { - &iio_const_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, NULL }; @@ -502,7 +533,7 @@ static int vf610_read_raw(struct iio_dev *indio_dev, return IIO_VAL_FRACTIONAL_LOG2; case IIO_CHAN_INFO_SAMP_FREQ: - *val = vf610_sample_freq_avail[info->adc_feature.sample_rate]; + *val = info->sample_freq_avail[info->adc_feature.sample_rate]; *val2 = 0; return IIO_VAL_INT; @@ -525,9 +556,9 @@ static int vf610_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: for (i = 0; - i < ARRAY_SIZE(vf610_sample_freq_avail); + i < ARRAY_SIZE(info->sample_freq_avail); i++) - if (val == vf610_sample_freq_avail[i]) { + if (val == info->sample_freq_avail[i]) { info->adc_feature.sample_rate = i; vf610_adc_sample_set(info); return 0; diff --git a/drivers/iio/gyro/bmg160.c b/drivers/iio/gyro/bmg160.c index 60451b328242..ccf3ea7e1afa 100644 --- a/drivers/iio/gyro/bmg160.c +++ b/drivers/iio/gyro/bmg160.c @@ -822,7 +822,7 @@ static irqreturn_t bmg160_trigger_handler(int irq, void *p) int bit, ret, i = 0; mutex_lock(&data->mutex); - for_each_set_bit(bit, indio_dev->buffer->scan_mask, + for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { ret = i2c_smbus_read_word_data(data->client, BMG160_AXIS_TO_REG(bit)); diff --git a/drivers/iio/imu/adis_trigger.c b/drivers/iio/imu/adis_trigger.c index e0017c22bb9c..f53e9a803a0e 100644 --- a/drivers/iio/imu/adis_trigger.c +++ b/drivers/iio/imu/adis_trigger.c @@ -60,7 +60,7 @@ int adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev) iio_trigger_set_drvdata(adis->trig, adis); ret = iio_trigger_register(adis->trig); - indio_dev->trig = adis->trig; + indio_dev->trig = iio_trigger_get(adis->trig); if (ret) goto error_free_irq; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index d8d5bed65e07..ef76afe2643c 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -410,42 +410,46 @@ error_read_raw: } } -static int inv_mpu6050_write_fsr(struct inv_mpu6050_state *st, int fsr) +static int inv_mpu6050_write_gyro_scale(struct inv_mpu6050_state *st, int val) { - int result; + int result, i; u8 d; - if (fsr < 0 || fsr > INV_MPU6050_MAX_GYRO_FS_PARAM) - return -EINVAL; - if (fsr == st->chip_config.fsr) - return 0; + for (i = 0; i < ARRAY_SIZE(gyro_scale_6050); ++i) { + if (gyro_scale_6050[i] == val) { + d = (i << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT); + result = inv_mpu6050_write_reg(st, + st->reg->gyro_config, d); + if (result) + return result; - d = (fsr << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT); - result = inv_mpu6050_write_reg(st, st->reg->gyro_config, d); - if (result) - return result; - st->chip_config.fsr = fsr; + st->chip_config.fsr = i; + return 0; + } + } - return 0; + return -EINVAL; } -static int inv_mpu6050_write_accel_fs(struct inv_mpu6050_state *st, int fs) +static int inv_mpu6050_write_accel_scale(struct inv_mpu6050_state *st, int val) { - int result; + int result, i; u8 d; - if (fs < 0 || fs > INV_MPU6050_MAX_ACCL_FS_PARAM) - return -EINVAL; - if (fs == st->chip_config.accl_fs) - return 0; + for (i = 0; i < ARRAY_SIZE(accel_scale); ++i) { + if (accel_scale[i] == val) { + d = (i << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT); + result = inv_mpu6050_write_reg(st, + st->reg->accl_config, d); + if (result) + return result; - d = (fs << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT); - result = inv_mpu6050_write_reg(st, st->reg->accl_config, d); - if (result) - return result; - st->chip_config.accl_fs = fs; + st->chip_config.accl_fs = i; + return 0; + } + } - return 0; + return -EINVAL; } static int inv_mpu6050_write_raw(struct iio_dev *indio_dev, @@ -471,10 +475,10 @@ static int inv_mpu6050_write_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_ANGL_VEL: - result = inv_mpu6050_write_fsr(st, val); + result = inv_mpu6050_write_gyro_scale(st, val2); break; case IIO_ACCEL: - result = inv_mpu6050_write_accel_fs(st, val); + result = inv_mpu6050_write_accel_scale(st, val2); break; default: result = -EINVAL; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c index 0cd306a72a6e..ba27e277511f 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c @@ -24,6 +24,16 @@ #include <linux/poll.h> #include "inv_mpu_iio.h" +static void inv_clear_kfifo(struct inv_mpu6050_state *st) +{ + unsigned long flags; + + /* take the spin lock sem to avoid interrupt kick in */ + spin_lock_irqsave(&st->time_stamp_lock, flags); + kfifo_reset(&st->timestamps); + spin_unlock_irqrestore(&st->time_stamp_lock, flags); +} + int inv_reset_fifo(struct iio_dev *indio_dev) { int result; @@ -50,6 +60,10 @@ int inv_reset_fifo(struct iio_dev *indio_dev) INV_MPU6050_BIT_FIFO_RST); if (result) goto reset_fifo_fail; + + /* clear timestamps fifo */ + inv_clear_kfifo(st); + /* enable interrupt */ if (st->chip_config.accl_fifo_enable || st->chip_config.gyro_fifo_enable) { @@ -83,16 +97,6 @@ reset_fifo_fail: return result; } -static void inv_clear_kfifo(struct inv_mpu6050_state *st) -{ - unsigned long flags; - - /* take the spin lock sem to avoid interrupt kick in */ - spin_lock_irqsave(&st->time_stamp_lock, flags); - kfifo_reset(&st->timestamps); - spin_unlock_irqrestore(&st->time_stamp_lock, flags); -} - /** * inv_mpu6050_irq_handler() - Cache a timestamp at each data ready interrupt. */ @@ -184,7 +188,6 @@ end_session: flush_fifo: /* Flush HW and SW FIFOs. */ inv_reset_fifo(indio_dev); - inv_clear_kfifo(st); mutex_unlock(&indio_dev->mlock); iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/imu/kmx61.c b/drivers/iio/imu/kmx61.c index 5cc3692acf37..b3a36376c719 100644 --- a/drivers/iio/imu/kmx61.c +++ b/drivers/iio/imu/kmx61.c @@ -1227,7 +1227,7 @@ static irqreturn_t kmx61_trigger_handler(int irq, void *p) base = KMX61_MAG_XOUT_L; mutex_lock(&data->lock); - for_each_set_bit(bit, indio_dev->buffer->scan_mask, + for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { ret = kmx61_read_measurement(data, base, bit); if (ret < 0) { diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index aaba9d3d980e..4df97f650e44 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -847,8 +847,7 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev, * @attr_list: List of IIO device attributes * * This function frees the memory allocated for each of the IIO device - * attributes in the list. Note: if you want to reuse the list after calling - * this function you have to reinitialize it using INIT_LIST_HEAD(). + * attributes in the list. */ void iio_free_chan_devattr_list(struct list_head *attr_list) { @@ -856,6 +855,7 @@ void iio_free_chan_devattr_list(struct list_head *attr_list) list_for_each_entry_safe(p, n, attr_list, l) { kfree(p->dev_attr.attr.name); + list_del(&p->l); kfree(p); } } @@ -936,6 +936,7 @@ static void iio_device_unregister_sysfs(struct iio_dev *indio_dev) iio_free_chan_devattr_list(&indio_dev->channel_attr_list); kfree(indio_dev->chan_attr_group.attrs); + indio_dev->chan_attr_group.attrs = NULL; } static void iio_dev_release(struct device *device) diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index a4b397048f71..a99692ba91bc 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -500,6 +500,7 @@ int iio_device_register_eventset(struct iio_dev *indio_dev) error_free_setup_event_lines: iio_free_chan_devattr_list(&indio_dev->event_interface->dev_attr_list); kfree(indio_dev->event_interface); + indio_dev->event_interface = NULL; return ret; } diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index 74dff4e4a11a..89fca3a70750 100644 --- a/drivers/iio/proximity/sx9500.c +++ b/drivers/iio/proximity/sx9500.c @@ -494,7 +494,7 @@ static irqreturn_t sx9500_trigger_handler(int irq, void *private) mutex_lock(&data->mutex); - for_each_set_bit(bit, indio_dev->buffer->scan_mask, + for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { ret = sx9500_read_proximity(data, &indio_dev->channels[bit], &val); diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c index aec7a6aa2951..8c014b5dab4c 100644 --- a/drivers/infiniband/core/umem.c +++ b/drivers/infiniband/core/umem.c @@ -99,6 +99,14 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr, if (dmasync) dma_set_attr(DMA_ATTR_WRITE_BARRIER, &attrs); + /* + * If the combination of the addr and size requested for this memory + * region causes an integer overflow, return error. + */ + if ((PAGE_ALIGN(addr + size) <= size) || + (PAGE_ALIGN(addr + size) <= addr)) + return ERR_PTR(-EINVAL); + if (!can_do_mlock()) return ERR_PTR(-EPERM); diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index b972c0b41799..976bea794b5f 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -587,8 +587,9 @@ static int mlx4_ib_SET_PORT(struct mlx4_ib_dev *dev, u8 port, int reset_qkey_vio ((__be32 *) mailbox->buf)[1] = cpu_to_be32(cap_mask); } - err = mlx4_cmd(dev->dev, mailbox->dma, port, 0, MLX4_CMD_SET_PORT, - MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED); + err = mlx4_cmd(dev->dev, mailbox->dma, port, MLX4_SET_PORT_IB_OPCODE, + MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, + MLX4_CMD_WRAPPED); mlx4_free_cmd_mailbox(dev->dev, mailbox); return err; @@ -1525,8 +1526,8 @@ static void update_gids_task(struct work_struct *work) memcpy(gids, gw->gids, sizeof gw->gids); err = mlx4_cmd(dev, mailbox->dma, MLX4_SET_PORT_GID_TABLE << 8 | gw->port, - 1, MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, - MLX4_CMD_WRAPPED); + MLX4_SET_PORT_ETH_OPCODE, MLX4_CMD_SET_PORT, + MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED); if (err) pr_warn("set port command failed\n"); else @@ -1564,7 +1565,7 @@ static void reset_gids_task(struct work_struct *work) IB_LINK_LAYER_ETHERNET) { err = mlx4_cmd(dev, mailbox->dma, MLX4_SET_PORT_GID_TABLE << 8 | gw->port, - 1, MLX4_CMD_SET_PORT, + MLX4_SET_PORT_ETH_OPCODE, MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED); if (err) diff --git a/drivers/infiniband/hw/mlx5/ah.c b/drivers/infiniband/hw/mlx5/ah.c index 39ab0caefdf9..66080580e24d 100644 --- a/drivers/infiniband/hw/mlx5/ah.c +++ b/drivers/infiniband/hw/mlx5/ah.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/mlx5/cq.c b/drivers/infiniband/hw/mlx5/cq.c index c463e7bba5f4..2ee6b1051975 100644 --- a/drivers/infiniband/hw/mlx5/cq.c +++ b/drivers/infiniband/hw/mlx5/cq.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -572,11 +572,15 @@ int mlx5_ib_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc) int mlx5_ib_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags) { + struct mlx5_core_dev *mdev = to_mdev(ibcq->device)->mdev; + void __iomem *uar_page = mdev->priv.uuari.uars[0].map; + mlx5_cq_arm(&to_mcq(ibcq)->mcq, (flags & IB_CQ_SOLICITED_MASK) == IB_CQ_SOLICITED ? MLX5_CQ_DB_REQ_NOT_SOL : MLX5_CQ_DB_REQ_NOT, - to_mdev(ibcq->device)->mdev->priv.uuari.uars[0].map, - MLX5_GET_DOORBELL_LOCK(&to_mdev(ibcq->device)->mdev->priv.cq_uar_lock)); + uar_page, + MLX5_GET_DOORBELL_LOCK(&mdev->priv.cq_uar_lock), + to_mcq(ibcq)->mcq.cons_index); return 0; } @@ -697,8 +701,6 @@ static int create_cq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_cq *cq, cq->mcq.set_ci_db = cq->db.db; cq->mcq.arm_db = cq->db.db + 1; - *cq->mcq.set_ci_db = 0; - *cq->mcq.arm_db = 0; cq->mcq.cqe_sz = cqe_size; err = alloc_cq_buf(dev, &cq->buf, entries, cqe_size); @@ -782,7 +784,7 @@ struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev, int entries, cq->cqe_size = cqe_size; cqb->ctx.cqe_sz_flags = cqe_sz_to_mlx_sz(cqe_size) << 5; cqb->ctx.log_sz_usr_page = cpu_to_be32((ilog2(entries) << 24) | index); - err = mlx5_vector2eqn(dev, vector, &eqn, &irqn); + err = mlx5_vector2eqn(dev->mdev, vector, &eqn, &irqn); if (err) goto err_cqb; diff --git a/drivers/infiniband/hw/mlx5/doorbell.c b/drivers/infiniband/hw/mlx5/doorbell.c index ece028fc47d6..a0e4e6ddb71a 100644 --- a/drivers/infiniband/hw/mlx5/doorbell.c +++ b/drivers/infiniband/hw/mlx5/doorbell.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/mlx5/mad.c b/drivers/infiniband/hw/mlx5/mad.c index 657af9a1167c..9cf9a37bb5ff 100644 --- a/drivers/infiniband/hw/mlx5/mad.c +++ b/drivers/infiniband/hw/mlx5/mad.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index cc4ac1e583b2..57c9809e8b87 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -62,95 +62,6 @@ static char mlx5_version[] = DRIVER_NAME ": Mellanox Connect-IB Infiniband driver v" DRIVER_VERSION " (" DRIVER_RELDATE ")\n"; -int mlx5_vector2eqn(struct mlx5_ib_dev *dev, int vector, int *eqn, int *irqn) -{ - struct mlx5_eq_table *table = &dev->mdev->priv.eq_table; - struct mlx5_eq *eq, *n; - int err = -ENOENT; - - spin_lock(&table->lock); - list_for_each_entry_safe(eq, n, &dev->eqs_list, list) { - if (eq->index == vector) { - *eqn = eq->eqn; - *irqn = eq->irqn; - err = 0; - break; - } - } - spin_unlock(&table->lock); - - return err; -} - -static int alloc_comp_eqs(struct mlx5_ib_dev *dev) -{ - struct mlx5_eq_table *table = &dev->mdev->priv.eq_table; - char name[MLX5_MAX_EQ_NAME]; - struct mlx5_eq *eq, *n; - int ncomp_vec; - int nent; - int err; - int i; - - INIT_LIST_HEAD(&dev->eqs_list); - ncomp_vec = table->num_comp_vectors; - nent = MLX5_COMP_EQ_SIZE; - for (i = 0; i < ncomp_vec; i++) { - eq = kzalloc(sizeof(*eq), GFP_KERNEL); - if (!eq) { - err = -ENOMEM; - goto clean; - } - - snprintf(name, MLX5_MAX_EQ_NAME, "mlx5_comp%d", i); - err = mlx5_create_map_eq(dev->mdev, eq, - i + MLX5_EQ_VEC_COMP_BASE, nent, 0, - name, &dev->mdev->priv.uuari.uars[0]); - if (err) { - kfree(eq); - goto clean; - } - mlx5_ib_dbg(dev, "allocated completion EQN %d\n", eq->eqn); - eq->index = i; - spin_lock(&table->lock); - list_add_tail(&eq->list, &dev->eqs_list); - spin_unlock(&table->lock); - } - - dev->num_comp_vectors = ncomp_vec; - return 0; - -clean: - spin_lock(&table->lock); - list_for_each_entry_safe(eq, n, &dev->eqs_list, list) { - list_del(&eq->list); - spin_unlock(&table->lock); - if (mlx5_destroy_unmap_eq(dev->mdev, eq)) - mlx5_ib_warn(dev, "failed to destroy EQ 0x%x\n", eq->eqn); - kfree(eq); - spin_lock(&table->lock); - } - spin_unlock(&table->lock); - return err; -} - -static void free_comp_eqs(struct mlx5_ib_dev *dev) -{ - struct mlx5_eq_table *table = &dev->mdev->priv.eq_table; - struct mlx5_eq *eq, *n; - - spin_lock(&table->lock); - list_for_each_entry_safe(eq, n, &dev->eqs_list, list) { - list_del(&eq->list); - spin_unlock(&table->lock); - if (mlx5_destroy_unmap_eq(dev->mdev, eq)) - mlx5_ib_warn(dev, "failed to destroy EQ 0x%x\n", eq->eqn); - kfree(eq); - spin_lock(&table->lock); - } - spin_unlock(&table->lock); -} - static int mlx5_ib_query_device(struct ib_device *ibdev, struct ib_device_attr *props) { @@ -1291,10 +1202,6 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev) get_ext_port_caps(dev); - err = alloc_comp_eqs(dev); - if (err) - goto err_dealloc; - MLX5_INIT_DOORBELL_LOCK(&dev->uar_lock); strlcpy(dev->ib_dev.name, "mlx5_%d", IB_DEVICE_NAME_MAX); @@ -1303,7 +1210,8 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev) dev->ib_dev.local_dma_lkey = mdev->caps.gen.reserved_lkey; dev->num_ports = mdev->caps.gen.num_ports; dev->ib_dev.phys_port_cnt = dev->num_ports; - dev->ib_dev.num_comp_vectors = dev->num_comp_vectors; + dev->ib_dev.num_comp_vectors = + dev->mdev->priv.eq_table.num_comp_vectors; dev->ib_dev.dma_device = &mdev->pdev->dev; dev->ib_dev.uverbs_abi_ver = MLX5_IB_UVERBS_ABI_VERSION; @@ -1390,13 +1298,13 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev) err = init_node_data(dev); if (err) - goto err_eqs; + goto err_dealloc; mutex_init(&dev->cap_mask_mutex); err = create_dev_resources(&dev->devr); if (err) - goto err_eqs; + goto err_dealloc; err = mlx5_ib_odp_init_one(dev); if (err) @@ -1433,9 +1341,6 @@ err_odp: err_rsrc: destroy_dev_resources(&dev->devr); -err_eqs: - free_comp_eqs(dev); - err_dealloc: ib_dealloc_device((struct ib_device *)dev); @@ -1450,7 +1355,6 @@ static void mlx5_ib_remove(struct mlx5_core_dev *mdev, void *context) destroy_umrc_res(dev); mlx5_ib_odp_remove_one(dev); destroy_dev_resources(&dev->devr); - free_comp_eqs(dev); ib_dealloc_device(&dev->ib_dev); } @@ -1458,6 +1362,7 @@ static struct mlx5_interface mlx5_ib_interface = { .add = mlx5_ib_add, .remove = mlx5_ib_remove, .event = mlx5_ib_event, + .protocol = MLX5_INTERFACE_PROTOCOL_IB, }; static int __init mlx5_ib_init(void) diff --git a/drivers/infiniband/hw/mlx5/mem.c b/drivers/infiniband/hw/mlx5/mem.c index 611a9fdf2f38..40df2cca0609 100644 --- a/drivers/infiniband/hw/mlx5/mem.c +++ b/drivers/infiniband/hw/mlx5/mem.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index 83f22fe297c8..dff1cfcdf476 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -421,9 +421,7 @@ struct mlx5_ib_dev { struct ib_device ib_dev; struct mlx5_core_dev *mdev; MLX5_DECLARE_DOORBELL_LOCK(uar_lock); - struct list_head eqs_list; int num_ports; - int num_comp_vectors; /* serialize update of capability mask */ struct mutex cap_mask_mutex; @@ -594,7 +592,6 @@ struct ib_xrcd *mlx5_ib_alloc_xrcd(struct ib_device *ibdev, struct ib_ucontext *context, struct ib_udata *udata); int mlx5_ib_dealloc_xrcd(struct ib_xrcd *xrcd); -int mlx5_vector2eqn(struct mlx5_ib_dev *dev, int vector, int *eqn, int *irqn); int mlx5_ib_get_buf_offset(u64 addr, int page_shift, u32 *offset); int mlx5_query_ext_port_caps(struct mlx5_ib_dev *dev, u8 port); int mlx5_ib_query_port(struct ib_device *ibdev, u8 port, diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index cd9822eeacae..71c593583864 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index a2c541c4809a..5099db08afd2 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Mellanox Technologies. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index be0cd358b080..4d7024b899cb 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -796,9 +796,6 @@ static int create_kernel_qp(struct mlx5_ib_dev *dev, goto err_free; } - qp->db.db[0] = 0; - qp->db.db[1] = 0; - qp->sq.wrid = kmalloc(qp->sq.wqe_cnt * sizeof(*qp->sq.wrid), GFP_KERNEL); qp->sq.wr_data = kmalloc(qp->sq.wqe_cnt * sizeof(*qp->sq.wr_data), GFP_KERNEL); qp->rq.wrid = kmalloc(qp->rq.wqe_cnt * sizeof(*qp->rq.wrid), GFP_KERNEL); @@ -1162,10 +1159,11 @@ static void destroy_qp_common(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp) in = kzalloc(sizeof(*in), GFP_KERNEL); if (!in) return; + if (qp->state != IB_QPS_RESET) { mlx5_ib_qp_disable_pagefaults(qp); if (mlx5_core_qp_modify(dev->mdev, to_mlx5_state(qp->state), - MLX5_QP_STATE_RST, in, sizeof(*in), &qp->mqp)) + MLX5_QP_STATE_RST, in, 0, &qp->mqp)) mlx5_ib_warn(dev, "mlx5_ib: modify QP %06x to RESET failed\n", qp->mqp.qpn); } diff --git a/drivers/infiniband/hw/mlx5/srq.c b/drivers/infiniband/hw/mlx5/srq.c index 41fec66217dd..02d77a29764d 100644 --- a/drivers/infiniband/hw/mlx5/srq.c +++ b/drivers/infiniband/hw/mlx5/srq.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -165,8 +165,6 @@ static int create_srq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_srq *srq, return err; } - *srq->db.db = 0; - if (mlx5_buf_alloc(dev->mdev, buf_size, PAGE_SIZE * 2, &srq->buf)) { mlx5_ib_dbg(dev, "buf alloc failed\n"); err = -ENOMEM; diff --git a/drivers/infiniband/hw/mlx5/user.h b/drivers/infiniband/hw/mlx5/user.h index d0ba264ac1ed..76fb7b927d37 100644 --- a/drivers/infiniband/hw/mlx5/user.h +++ b/drivers/infiniband/hw/mlx5/user.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 58b5aa3b6f2d..657b89b1d291 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -842,6 +842,13 @@ static void ipoib_set_mcast_list(struct net_device *dev) queue_work(ipoib_workqueue, &priv->restart_task); } +static int ipoib_get_iflink(const struct net_device *dev) +{ + struct ipoib_dev_priv *priv = netdev_priv(dev); + + return priv->parent->ifindex; +} + static u32 ipoib_addr_hash(struct ipoib_neigh_hash *htbl, u8 *daddr) { /* @@ -1341,6 +1348,7 @@ static const struct net_device_ops ipoib_netdev_ops = { .ndo_start_xmit = ipoib_start_xmit, .ndo_tx_timeout = ipoib_timeout, .ndo_set_rx_mode = ipoib_set_mcast_list, + .ndo_get_iflink = ipoib_get_iflink, }; void ipoib_setup(struct net_device *dev) diff --git a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c index 9fad7b5ac8b9..4dd1313056a4 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c @@ -102,7 +102,6 @@ int __ipoib_vlan_add(struct ipoib_dev_priv *ppriv, struct ipoib_dev_priv *priv, } priv->child_type = type; - priv->dev->iflink = ppriv->dev->ifindex; list_add_tail(&priv->list, &ppriv->child_intfs); return 0; diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 1bd15ebc01f2..27bcdbc950c9 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -1154,10 +1154,28 @@ out: mutex_unlock(&alps_mutex); } -static void alps_report_bare_ps2_packet(struct input_dev *dev, +static void alps_report_bare_ps2_packet(struct psmouse *psmouse, unsigned char packet[], bool report_buttons) { + struct alps_data *priv = psmouse->private; + struct input_dev *dev; + + /* Figure out which device to use to report the bare packet */ + if (priv->proto_version == ALPS_PROTO_V2 && + (priv->flags & ALPS_DUALPOINT)) { + /* On V2 devices the DualPoint Stick reports bare packets */ + dev = priv->dev2; + } else if (unlikely(IS_ERR_OR_NULL(priv->dev3))) { + /* Register dev3 mouse if we received PS/2 packet first time */ + if (!IS_ERR(priv->dev3)) + psmouse_queue_work(psmouse, &priv->dev3_register_work, + 0); + return; + } else { + dev = priv->dev3; + } + if (report_buttons) alps_report_buttons(dev, NULL, packet[0] & 1, packet[0] & 2, packet[0] & 4); @@ -1232,8 +1250,8 @@ static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse) * de-synchronization. */ - alps_report_bare_ps2_packet(priv->dev2, - &psmouse->packet[3], false); + alps_report_bare_ps2_packet(psmouse, &psmouse->packet[3], + false); /* * Continue with the standard ALPS protocol handling, @@ -1289,18 +1307,9 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) * properly we only do this if the device is fully synchronized. */ if (!psmouse->out_of_sync_cnt && (psmouse->packet[0] & 0xc8) == 0x08) { - - /* Register dev3 mouse if we received PS/2 packet first time */ - if (unlikely(!priv->dev3)) - psmouse_queue_work(psmouse, - &priv->dev3_register_work, 0); - if (psmouse->pktcnt == 3) { - /* Once dev3 mouse device is registered report data */ - if (likely(!IS_ERR_OR_NULL(priv->dev3))) - alps_report_bare_ps2_packet(priv->dev3, - psmouse->packet, - true); + alps_report_bare_ps2_packet(psmouse, psmouse->packet, + true); return PSMOUSE_FULL_PACKET; } return PSMOUSE_GOOD_DATA; @@ -2281,10 +2290,12 @@ static int alps_set_protocol(struct psmouse *psmouse, priv->set_abs_params = alps_set_abs_params_mt; priv->nibble_commands = alps_v3_nibble_commands; priv->addr_command = PSMOUSE_CMD_RESET_WRAP; - priv->x_max = 1360; - priv->y_max = 660; priv->x_bits = 23; priv->y_bits = 12; + + if (alps_dolphin_get_device_area(psmouse, priv)) + return -EIO; + break; case ALPS_PROTO_V6: @@ -2303,9 +2314,8 @@ static int alps_set_protocol(struct psmouse *psmouse, priv->set_abs_params = alps_set_abs_params_mt; priv->nibble_commands = alps_v3_nibble_commands; priv->addr_command = PSMOUSE_CMD_RESET_WRAP; - - if (alps_dolphin_get_device_area(psmouse, priv)) - return -EIO; + priv->x_max = 0xfff; + priv->y_max = 0x7ff; if (priv->fw_ver[1] != 0xba) priv->flags |= ALPS_BUTTONPAD; diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index dda605836546..3b06c8a360b6 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -154,6 +154,11 @@ static const struct min_max_quirk min_max_pnpid_table[] = { }, { (const char * const []){"LEN2006", NULL}, + {2691, 2691}, + 1024, 5045, 2457, 4832 + }, + { + (const char * const []){"LEN2006", NULL}, {ANY_BOARD_ID, ANY_BOARD_ID}, 1264, 5675, 1171, 4688 }, @@ -189,7 +194,7 @@ static const char * const topbuttonpad_pnp_ids[] = { "LEN2003", "LEN2004", /* L440 */ "LEN2005", - "LEN2006", + "LEN2006", /* Edge E440/E540 */ "LEN2007", "LEN2008", "LEN2009", diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index fc13dd56953e..a3adde6519f0 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -1288,10 +1288,13 @@ static phys_addr_t arm_smmu_iova_to_phys(struct iommu_domain *domain, return 0; spin_lock_irqsave(&smmu_domain->pgtbl_lock, flags); - if (smmu_domain->smmu->features & ARM_SMMU_FEAT_TRANS_OPS) + if (smmu_domain->smmu->features & ARM_SMMU_FEAT_TRANS_OPS && + smmu_domain->stage == ARM_SMMU_DOMAIN_S1) { ret = arm_smmu_iova_to_phys_hard(domain, iova); - else + } else { ret = ops->iova_to_phys(ops, iova); + } + spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags); return ret; @@ -1556,7 +1559,7 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) return -ENODEV; } - if (smmu->version == 1 || (!(id & ID0_ATOSNS) && (id & ID0_S1TS))) { + if ((id & ID0_S1TS) && ((smmu->version == 1) || (id & ID0_ATOSNS))) { smmu->features |= ARM_SMMU_FEAT_TRANS_OPS; dev_notice(smmu->dev, "\taddress translation ops\n"); } diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index ae4c1a854e57..2d1e05bdbb53 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -1742,9 +1742,8 @@ static int domain_init(struct dmar_domain *domain, int guest_width) static void domain_exit(struct dmar_domain *domain) { - struct dmar_drhd_unit *drhd; - struct intel_iommu *iommu; struct page *freelist = NULL; + int i; /* Domain 0 is reserved, so dont process it */ if (!domain) @@ -1764,8 +1763,8 @@ static void domain_exit(struct dmar_domain *domain) /* clear attached or cached domains */ rcu_read_lock(); - for_each_active_iommu(iommu, drhd) - iommu_detach_domain(domain, iommu); + for_each_set_bit(i, domain->iommu_bmp, g_num_of_iommus) + iommu_detach_domain(domain, g_iommus[i]); rcu_read_unlock(); dma_free_pagelist(freelist); diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c index 10186cac7716..bc39bdf7b99b 100644 --- a/drivers/iommu/ipmmu-vmsa.c +++ b/drivers/iommu/ipmmu-vmsa.c @@ -851,6 +851,7 @@ static int ipmmu_remove(struct platform_device *pdev) static const struct of_device_id ipmmu_of_ids[] = { { .compatible = "renesas,ipmmu-vmsa", }, + { } }; static struct platform_driver ipmmu_driver = { diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 596b0a9eee99..9687f8afebff 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -169,7 +169,7 @@ static void its_encode_cmd(struct its_cmd_block *cmd, u8 cmd_nr) static void its_encode_devid(struct its_cmd_block *cmd, u32 devid) { - cmd->raw_cmd[0] &= ~(0xffffUL << 32); + cmd->raw_cmd[0] &= BIT_ULL(32) - 1; cmd->raw_cmd[0] |= ((u64)devid) << 32; } @@ -802,6 +802,7 @@ static int its_alloc_tables(struct its_node *its) int i; int psz = SZ_64K; u64 shr = GITS_BASER_InnerShareable; + u64 cache = GITS_BASER_WaWb; for (i = 0; i < GITS_BASER_NR_REGS; i++) { u64 val = readq_relaxed(its->base + GITS_BASER + i * 8); @@ -848,7 +849,7 @@ retry_baser: val = (virt_to_phys(base) | (type << GITS_BASER_TYPE_SHIFT) | ((entry_size - 1) << GITS_BASER_ENTRY_SIZE_SHIFT) | - GITS_BASER_WaWb | + cache | shr | GITS_BASER_VALID); @@ -874,9 +875,12 @@ retry_baser: * Shareability didn't stick. Just use * whatever the read reported, which is likely * to be the only thing this redistributor - * supports. + * supports. If that's zero, make it + * non-cacheable as well. */ shr = tmp & GITS_BASER_SHAREABILITY_MASK; + if (!shr) + cache = GITS_BASER_nC; goto retry_baser; } @@ -980,16 +984,39 @@ static void its_cpu_init_lpis(void) tmp = readq_relaxed(rbase + GICR_PROPBASER); if ((tmp ^ val) & GICR_PROPBASER_SHAREABILITY_MASK) { + if (!(tmp & GICR_PROPBASER_SHAREABILITY_MASK)) { + /* + * The HW reports non-shareable, we must + * remove the cacheability attributes as + * well. + */ + val &= ~(GICR_PROPBASER_SHAREABILITY_MASK | + GICR_PROPBASER_CACHEABILITY_MASK); + val |= GICR_PROPBASER_nC; + writeq_relaxed(val, rbase + GICR_PROPBASER); + } pr_info_once("GIC: using cache flushing for LPI property table\n"); gic_rdists->flags |= RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING; } /* set PENDBASE */ val = (page_to_phys(pend_page) | - GICR_PROPBASER_InnerShareable | - GICR_PROPBASER_WaWb); + GICR_PENDBASER_InnerShareable | + GICR_PENDBASER_WaWb); writeq_relaxed(val, rbase + GICR_PENDBASER); + tmp = readq_relaxed(rbase + GICR_PENDBASER); + + if (!(tmp & GICR_PENDBASER_SHAREABILITY_MASK)) { + /* + * The HW reports non-shareable, we must remove the + * cacheability attributes as well. + */ + val &= ~(GICR_PENDBASER_SHAREABILITY_MASK | + GICR_PENDBASER_CACHEABILITY_MASK); + val |= GICR_PENDBASER_nC; + writeq_relaxed(val, rbase + GICR_PENDBASER); + } /* Enable LPIs */ val = readl_relaxed(rbase + GICR_CTLR); @@ -1026,7 +1053,7 @@ static void its_cpu_init_collection(void) * This ITS wants a linear CPU number. */ target = readq_relaxed(gic_data_rdist_rd_base() + GICR_TYPER); - target = GICR_TYPER_CPU_NUMBER(target); + target = GICR_TYPER_CPU_NUMBER(target) << 16; } /* Perform collection mapping */ @@ -1422,14 +1449,26 @@ static int its_probe(struct device_node *node, struct irq_domain *parent) writeq_relaxed(baser, its->base + GITS_CBASER); tmp = readq_relaxed(its->base + GITS_CBASER); - writeq_relaxed(0, its->base + GITS_CWRITER); - writel_relaxed(GITS_CTLR_ENABLE, its->base + GITS_CTLR); - if ((tmp ^ baser) & GITS_BASER_SHAREABILITY_MASK) { + if ((tmp ^ baser) & GITS_CBASER_SHAREABILITY_MASK) { + if (!(tmp & GITS_CBASER_SHAREABILITY_MASK)) { + /* + * The HW reports non-shareable, we must + * remove the cacheability attributes as + * well. + */ + baser &= ~(GITS_CBASER_SHAREABILITY_MASK | + GITS_CBASER_CACHEABILITY_MASK); + baser |= GITS_CBASER_nC; + writeq_relaxed(baser, its->base + GITS_CBASER); + } pr_info("ITS: using cache flushing for cmd queue\n"); its->flags |= ITS_FLAGS_CMDQ_NEEDS_FLUSHING; } + writeq_relaxed(0, its->base + GITS_CWRITER); + writel_relaxed(GITS_CTLR_ENABLE, its->base + GITS_CTLR); + if (of_property_read_bool(its->msi_chip.of_node, "msi-controller")) { its->domain = irq_domain_add_tree(NULL, &its_domain_ops, its); if (!its->domain) { diff --git a/drivers/lguest/Kconfig b/drivers/lguest/Kconfig index ee035ec4526b..169172d2ba05 100644 --- a/drivers/lguest/Kconfig +++ b/drivers/lguest/Kconfig @@ -1,6 +1,6 @@ config LGUEST tristate "Linux hypervisor example code" - depends on X86_32 && EVENTFD && TTY + depends on X86_32 && EVENTFD && TTY && PCI_DIRECT select HVC_DRIVER ---help--- This is a very simple module which allows you to run diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 9b641b38b857..8001fe9e3434 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -433,7 +433,6 @@ static int dm_blk_open(struct block_device *bdev, fmode_t mode) dm_get(md); atomic_inc(&md->open_count); - out: spin_unlock(&_minor_lock); @@ -442,16 +441,20 @@ out: static void dm_blk_close(struct gendisk *disk, fmode_t mode) { - struct mapped_device *md = disk->private_data; + struct mapped_device *md; spin_lock(&_minor_lock); + md = disk->private_data; + if (WARN_ON(!md)) + goto out; + if (atomic_dec_and_test(&md->open_count) && (test_bit(DMF_DEFERRED_REMOVE, &md->flags))) queue_work(deferred_remove_workqueue, &deferred_remove_work); dm_put(md); - +out: spin_unlock(&_minor_lock); } @@ -2241,7 +2244,6 @@ static void free_dev(struct mapped_device *md) int minor = MINOR(disk_devt(md->disk)); unlock_fs(md); - bdput(md->bdev); destroy_workqueue(md->wq); if (md->kworker_task) @@ -2252,19 +2254,22 @@ static void free_dev(struct mapped_device *md) mempool_destroy(md->rq_pool); if (md->bs) bioset_free(md->bs); - blk_integrity_unregister(md->disk); - del_gendisk(md->disk); + cleanup_srcu_struct(&md->io_barrier); free_table_devices(&md->table_devices); - free_minor(minor); + dm_stats_cleanup(&md->stats); spin_lock(&_minor_lock); md->disk->private_data = NULL; spin_unlock(&_minor_lock); - + if (blk_get_integrity(md->disk)) + blk_integrity_unregister(md->disk); + del_gendisk(md->disk); put_disk(md->disk); blk_cleanup_queue(md->queue); - dm_stats_cleanup(&md->stats); + bdput(md->bdev); + free_minor(minor); + module_put(THIS_MODULE); kfree(md); } @@ -2642,8 +2647,9 @@ static void __dm_destroy(struct mapped_device *md, bool wait) might_sleep(); - spin_lock(&_minor_lock); map = dm_get_live_table(md, &srcu_idx); + + spin_lock(&_minor_lock); idr_replace(&_minor_idr, MINOR_ALLOCED, MINOR(disk_devt(dm_disk(md)))); set_bit(DMF_FREEING, &md->flags); spin_unlock(&_minor_lock); diff --git a/drivers/mfd/kempld-core.c b/drivers/mfd/kempld-core.c index f38ec424872e..5615522f8d62 100644 --- a/drivers/mfd/kempld-core.c +++ b/drivers/mfd/kempld-core.c @@ -739,7 +739,7 @@ static int __init kempld_init(void) for (id = kempld_dmi_table; id->matches[0].slot != DMI_NONE; id++) if (strstr(id->ident, force_device_id)) - if (id->callback && id->callback(id)) + if (id->callback && !id->callback(id)) break; if (id->matches[0].slot == DMI_NONE) return -ENODEV; diff --git a/drivers/mfd/rtsx_usb.c b/drivers/mfd/rtsx_usb.c index ede50244f265..dbd907d7170e 100644 --- a/drivers/mfd/rtsx_usb.c +++ b/drivers/mfd/rtsx_usb.c @@ -196,18 +196,27 @@ EXPORT_SYMBOL_GPL(rtsx_usb_ep0_write_register); int rtsx_usb_ep0_read_register(struct rtsx_ucr *ucr, u16 addr, u8 *data) { u16 value; + u8 *buf; + int ret; if (!data) return -EINVAL; - *data = 0; + + buf = kzalloc(sizeof(u8), GFP_KERNEL); + if (!buf) + return -ENOMEM; addr |= EP0_READ_REG_CMD << EP0_OP_SHIFT; value = swab16(addr); - return usb_control_msg(ucr->pusb_dev, + ret = usb_control_msg(ucr->pusb_dev, usb_rcvctrlpipe(ucr->pusb_dev, 0), RTSX_USB_REQ_REG_OP, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, 0, data, 1, 100); + value, 0, buf, 1, 100); + *data = *buf; + + kfree(buf); + return ret; } EXPORT_SYMBOL_GPL(rtsx_usb_ep0_read_register); @@ -288,18 +297,27 @@ static int rtsx_usb_get_status_with_bulk(struct rtsx_ucr *ucr, u16 *status) int rtsx_usb_get_card_status(struct rtsx_ucr *ucr, u16 *status) { int ret; + u16 *buf; if (!status) return -EINVAL; - if (polling_pipe == 0) + if (polling_pipe == 0) { + buf = kzalloc(sizeof(u16), GFP_KERNEL); + if (!buf) + return -ENOMEM; + ret = usb_control_msg(ucr->pusb_dev, usb_rcvctrlpipe(ucr->pusb_dev, 0), RTSX_USB_REQ_POLL, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, 0, status, 2, 100); - else + 0, 0, buf, 2, 100); + *status = *buf; + + kfree(buf); + } else { ret = rtsx_usb_get_status_with_bulk(ucr, status); + } /* usb_control_msg may return positive when success */ if (ret < 0) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 7b4684ccdb3f..78dde56ae6e6 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3881,7 +3881,8 @@ static inline int bond_slave_override(struct bonding *bond, /* Find out if any slaves have the same mapping as this skb. */ bond_for_each_slave_rcu(bond, slave, iter) { if (slave->queue_id == skb->queue_mapping) { - if (bond_slave_can_tx(slave)) { + if (bond_slave_is_up(slave) && + slave->link == BOND_LINK_UP) { bond_dev_queue_xmit(bond, skb, slave->dev); return 0; } diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c index 27bbc56de15f..9da06537237f 100644 --- a/drivers/net/caif/caif_serial.c +++ b/drivers/net/caif/caif_serial.c @@ -70,7 +70,6 @@ struct ser_device { struct tty_struct *tty; bool tx_started; unsigned long state; - char *tty_name; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_tty_dir; struct debugfs_blob_wrapper tx_blob; diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index 80c46ad4cee4..ad0a7e8c2c2b 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -592,13 +592,12 @@ static int flexcan_poll_state(struct net_device *dev, u32 reg_esr) rx_state = unlikely(reg_esr & FLEXCAN_ESR_RX_WRN) ? CAN_STATE_ERROR_WARNING : CAN_STATE_ERROR_ACTIVE; new_state = max(tx_state, rx_state); - } else if (unlikely(flt == FLEXCAN_ESR_FLT_CONF_PASSIVE)) { + } else { __flexcan_get_berr_counter(dev, &bec); - new_state = CAN_STATE_ERROR_PASSIVE; + new_state = flt == FLEXCAN_ESR_FLT_CONF_PASSIVE ? + CAN_STATE_ERROR_PASSIVE : CAN_STATE_BUS_OFF; rx_state = bec.rxerr >= bec.txerr ? new_state : 0; tx_state = bec.rxerr <= bec.txerr ? new_state : 0; - } else { - new_state = CAN_STATE_BUS_OFF; } /* state hasn't changed */ @@ -1158,12 +1157,19 @@ static int flexcan_probe(struct platform_device *pdev) const struct flexcan_devtype_data *devtype_data; struct net_device *dev; struct flexcan_priv *priv; + struct regulator *reg_xceiver; struct resource *mem; struct clk *clk_ipg = NULL, *clk_per = NULL; void __iomem *base; int err, irq; u32 clock_freq = 0; + reg_xceiver = devm_regulator_get(&pdev->dev, "xceiver"); + if (PTR_ERR(reg_xceiver) == -EPROBE_DEFER) + return -EPROBE_DEFER; + else if (IS_ERR(reg_xceiver)) + reg_xceiver = NULL; + if (pdev->dev.of_node) of_property_read_u32(pdev->dev.of_node, "clock-frequency", &clock_freq); @@ -1224,9 +1230,7 @@ static int flexcan_probe(struct platform_device *pdev) priv->pdata = dev_get_platdata(&pdev->dev); priv->devtype_data = devtype_data; - priv->reg_xceiver = devm_regulator_get(&pdev->dev, "xceiver"); - if (IS_ERR(priv->reg_xceiver)) - priv->reg_xceiver = NULL; + priv->reg_xceiver = reg_xceiver; netif_napi_add(dev, &priv->napi, flexcan_poll, FLEXCAN_NAPI_WEIGHT); diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c index 9376f5e5b94e..866bac0ae7e9 100644 --- a/drivers/net/can/usb/ems_usb.c +++ b/drivers/net/can/usb/ems_usb.c @@ -123,7 +123,7 @@ MODULE_LICENSE("GPL v2"); * CPC_MSG_TYPE_EXT_CAN_FRAME or CPC_MSG_TYPE_EXT_RTR_FRAME. */ struct cpc_can_msg { - u32 id; + __le32 id; u8 length; u8 msg[8]; }; @@ -200,8 +200,8 @@ struct __packed ems_cpc_msg { u8 type; /* type of message */ u8 length; /* length of data within union 'msg' */ u8 msgid; /* confirmation handle */ - u32 ts_sec; /* timestamp in seconds */ - u32 ts_nsec; /* timestamp in nano seconds */ + __le32 ts_sec; /* timestamp in seconds */ + __le32 ts_nsec; /* timestamp in nano seconds */ union { u8 generic[64]; @@ -765,7 +765,7 @@ static netdev_tx_t ems_usb_start_xmit(struct sk_buff *skb, struct net_device *ne msg = (struct ems_cpc_msg *)&buf[CPC_HEADER_SIZE]; - msg->msg.can_msg.id = cf->can_id & CAN_ERR_MASK; + msg->msg.can_msg.id = cpu_to_le32(cf->can_id & CAN_ERR_MASK); msg->msg.can_msg.length = cf->can_dlc; if (cf->can_id & CAN_RTR_FLAG) { @@ -783,9 +783,6 @@ static netdev_tx_t ems_usb_start_xmit(struct sk_buff *skb, struct net_device *ne msg->length = CPC_CAN_MSG_MIN_SIZE + cf->can_dlc; } - /* Respect byte order */ - msg->msg.can_msg.id = cpu_to_le32(msg->msg.can_msg.id); - for (i = 0; i < MAX_TX_URBS; i++) { if (dev->tx_contexts[i].echo_index == MAX_TX_URBS) { context = &dev->tx_contexts[i]; diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c index 009acc8641fc..8b4d3e6875eb 100644 --- a/drivers/net/can/usb/gs_usb.c +++ b/drivers/net/can/usb/gs_usb.c @@ -901,6 +901,8 @@ static int gs_usb_probe(struct usb_interface *intf, const struct usb_device_id * } dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; init_usb_anchor(&dev->rx_submitted); atomic_set(&dev->active_channels, 0); diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c index d269ae0b072a..4643914859b2 100644 --- a/drivers/net/can/usb/kvaser_usb.c +++ b/drivers/net/can/usb/kvaser_usb.c @@ -25,7 +25,6 @@ #include <linux/can/dev.h> #include <linux/can/error.h> -#define MAX_TX_URBS 16 #define MAX_RX_URBS 4 #define START_TIMEOUT 1000 /* msecs */ #define STOP_TIMEOUT 1000 /* msecs */ @@ -443,6 +442,7 @@ struct kvaser_usb_error_summary { }; }; +/* Context for an outstanding, not yet ACKed, transmission */ struct kvaser_usb_tx_urb_context { struct kvaser_usb_net_priv *priv; u32 echo_index; @@ -456,8 +456,13 @@ struct kvaser_usb { struct usb_endpoint_descriptor *bulk_in, *bulk_out; struct usb_anchor rx_submitted; + /* @max_tx_urbs: Firmware-reported maximum number of oustanding, + * not yet ACKed, transmissions on this device. This value is + * also used as a sentinel for marking free tx contexts. + */ u32 fw_version; unsigned int nchannels; + unsigned int max_tx_urbs; enum kvaser_usb_family family; bool rxinitdone; @@ -467,19 +472,18 @@ struct kvaser_usb { struct kvaser_usb_net_priv { struct can_priv can; - - spinlock_t tx_contexts_lock; - int active_tx_contexts; - struct kvaser_usb_tx_urb_context tx_contexts[MAX_TX_URBS]; - - struct usb_anchor tx_submitted; - struct completion start_comp, stop_comp; + struct can_berr_counter bec; struct kvaser_usb *dev; struct net_device *netdev; int channel; - struct can_berr_counter bec; + struct completion start_comp, stop_comp; + struct usb_anchor tx_submitted; + + spinlock_t tx_contexts_lock; + int active_tx_contexts; + struct kvaser_usb_tx_urb_context tx_contexts[]; }; static const struct usb_device_id kvaser_usb_table[] = { @@ -592,8 +596,8 @@ static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id, * for further details. */ if (tmp->len == 0) { - pos = round_up(pos, - dev->bulk_in->wMaxPacketSize); + pos = round_up(pos, le16_to_cpu(dev->bulk_in-> + wMaxPacketSize)); continue; } @@ -657,9 +661,13 @@ static int kvaser_usb_get_software_info(struct kvaser_usb *dev) switch (dev->family) { case KVASER_LEAF: dev->fw_version = le32_to_cpu(msg.u.leaf.softinfo.fw_version); + dev->max_tx_urbs = + le16_to_cpu(msg.u.leaf.softinfo.max_outstanding_tx); break; case KVASER_USBCAN: dev->fw_version = le32_to_cpu(msg.u.usbcan.softinfo.fw_version); + dev->max_tx_urbs = + le16_to_cpu(msg.u.usbcan.softinfo.max_outstanding_tx); break; } @@ -715,7 +723,7 @@ static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev, stats = &priv->netdev->stats; - context = &priv->tx_contexts[tid % MAX_TX_URBS]; + context = &priv->tx_contexts[tid % dev->max_tx_urbs]; /* Sometimes the state change doesn't come after a bus-off event */ if (priv->can.restart_ms && @@ -744,7 +752,7 @@ static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev, spin_lock_irqsave(&priv->tx_contexts_lock, flags); can_get_echo_skb(priv->netdev, context->echo_index); - context->echo_index = MAX_TX_URBS; + context->echo_index = dev->max_tx_urbs; --priv->active_tx_contexts; netif_wake_queue(priv->netdev); @@ -1329,7 +1337,8 @@ static void kvaser_usb_read_bulk_callback(struct urb *urb) * number of events in case of a heavy rx load on the bus. */ if (msg->len == 0) { - pos = round_up(pos, dev->bulk_in->wMaxPacketSize); + pos = round_up(pos, le16_to_cpu(dev->bulk_in-> + wMaxPacketSize)); continue; } @@ -1512,11 +1521,13 @@ error: static void kvaser_usb_reset_tx_urb_contexts(struct kvaser_usb_net_priv *priv) { - int i; + int i, max_tx_urbs; + + max_tx_urbs = priv->dev->max_tx_urbs; priv->active_tx_contexts = 0; - for (i = 0; i < MAX_TX_URBS; i++) - priv->tx_contexts[i].echo_index = MAX_TX_URBS; + for (i = 0; i < max_tx_urbs; i++) + priv->tx_contexts[i].echo_index = max_tx_urbs; } /* This method might sleep. Do not call it in the atomic context @@ -1702,14 +1713,14 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, *msg_tx_can_flags |= MSG_FLAG_REMOTE_FRAME; spin_lock_irqsave(&priv->tx_contexts_lock, flags); - for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) { - if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) { + for (i = 0; i < dev->max_tx_urbs; i++) { + if (priv->tx_contexts[i].echo_index == dev->max_tx_urbs) { context = &priv->tx_contexts[i]; context->echo_index = i; can_put_echo_skb(skb, netdev, context->echo_index); ++priv->active_tx_contexts; - if (priv->active_tx_contexts >= MAX_TX_URBS) + if (priv->active_tx_contexts >= dev->max_tx_urbs) netif_stop_queue(netdev); break; @@ -1743,7 +1754,7 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, spin_lock_irqsave(&priv->tx_contexts_lock, flags); can_free_echo_skb(netdev, context->echo_index); - context->echo_index = MAX_TX_URBS; + context->echo_index = dev->max_tx_urbs; --priv->active_tx_contexts; netif_wake_queue(netdev); @@ -1881,7 +1892,9 @@ static int kvaser_usb_init_one(struct usb_interface *intf, if (err) return err; - netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS); + netdev = alloc_candev(sizeof(*priv) + + dev->max_tx_urbs * sizeof(*priv->tx_contexts), + dev->max_tx_urbs); if (!netdev) { dev_err(&intf->dev, "Cannot alloc candev\n"); return -ENOMEM; @@ -2009,6 +2022,13 @@ static int kvaser_usb_probe(struct usb_interface *intf, return err; } + dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n", + ((dev->fw_version >> 24) & 0xff), + ((dev->fw_version >> 16) & 0xff), + (dev->fw_version & 0xffff)); + + dev_dbg(&intf->dev, "Max oustanding tx = %d URBs\n", dev->max_tx_urbs); + err = kvaser_usb_get_card_info(dev); if (err) { dev_err(&intf->dev, @@ -2016,11 +2036,6 @@ static int kvaser_usb_probe(struct usb_interface *intf, return err; } - dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n", - ((dev->fw_version >> 24) & 0xff), - ((dev->fw_version >> 16) & 0xff), - (dev->fw_version & 0xffff)); - for (i = 0; i < dev->nchannels; i++) { err = kvaser_usb_init_one(intf, id, i); if (err) { diff --git a/drivers/net/can/usb/peak_usb/pcan_ucan.h b/drivers/net/can/usb/peak_usb/pcan_ucan.h index 1ba7c25002e1..e8fc4952c6b0 100644 --- a/drivers/net/can/usb/peak_usb/pcan_ucan.h +++ b/drivers/net/can/usb/peak_usb/pcan_ucan.h @@ -26,8 +26,8 @@ #define PUCAN_CMD_FILTER_STD 0x008 #define PUCAN_CMD_TX_ABORT 0x009 #define PUCAN_CMD_WR_ERR_CNT 0x00a -#define PUCAN_CMD_RX_FRAME_ENABLE 0x00b -#define PUCAN_CMD_RX_FRAME_DISABLE 0x00c +#define PUCAN_CMD_SET_EN_OPTION 0x00b +#define PUCAN_CMD_CLR_DIS_OPTION 0x00c #define PUCAN_CMD_END_OF_COLLECTION 0x3ff /* uCAN received messages list */ @@ -101,14 +101,15 @@ struct __packed pucan_wr_err_cnt { u16 unused; }; -/* uCAN RX_FRAME_ENABLE command fields */ -#define PUCAN_FLTEXT_ERROR 0x0001 -#define PUCAN_FLTEXT_BUSLOAD 0x0002 +/* uCAN SET_EN/CLR_DIS _OPTION command fields */ +#define PUCAN_OPTION_ERROR 0x0001 +#define PUCAN_OPTION_BUSLOAD 0x0002 +#define PUCAN_OPTION_CANDFDISO 0x0004 -struct __packed pucan_filter_ext { +struct __packed pucan_options { __le16 opcode_channel; - __le16 ext_mask; + __le16 options; u32 unused; }; diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c index 0bac0f14edc3..09d14e70abd7 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c @@ -110,13 +110,13 @@ struct __packed pcan_ufd_led { u8 unused[5]; }; -/* Extended usage of uCAN commands CMD_RX_FRAME_xxxABLE for PCAN-USB Pro FD */ +/* Extended usage of uCAN commands CMD_xxx_xx_OPTION for PCAN-USB Pro FD */ #define PCAN_UFD_FLTEXT_CALIBRATION 0x8000 -struct __packed pcan_ufd_filter_ext { +struct __packed pcan_ufd_options { __le16 opcode_channel; - __le16 ext_mask; + __le16 ucan_mask; u16 unused; __le16 usb_mask; }; @@ -182,7 +182,7 @@ static inline void *pcan_usb_fd_cmd_buffer(struct peak_usb_device *dev) static int pcan_usb_fd_send_cmd(struct peak_usb_device *dev, void *cmd_tail) { void *cmd_head = pcan_usb_fd_cmd_buffer(dev); - int err; + int err = 0; u8 *packet_ptr; int i, n = 1, packet_len; ptrdiff_t cmd_len; @@ -251,6 +251,27 @@ static int pcan_usb_fd_build_restart_cmd(struct peak_usb_device *dev, u8 *buf) /* moves the pointer forward */ pc += sizeof(struct pucan_wr_err_cnt); + /* add command to switch from ISO to non-ISO mode, if fw allows it */ + if (dev->can.ctrlmode_supported & CAN_CTRLMODE_FD_NON_ISO) { + struct pucan_options *puo = (struct pucan_options *)pc; + + puo->opcode_channel = + (dev->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) ? + pucan_cmd_opcode_channel(dev, + PUCAN_CMD_CLR_DIS_OPTION) : + pucan_cmd_opcode_channel(dev, PUCAN_CMD_SET_EN_OPTION); + + puo->options = cpu_to_le16(PUCAN_OPTION_CANDFDISO); + + /* to be sure that no other extended bits will be taken into + * account + */ + puo->unused = 0; + + /* moves the pointer forward */ + pc += sizeof(struct pucan_options); + } + /* next, go back to operational mode */ cmd = (struct pucan_command *)pc; cmd->opcode_channel = pucan_cmd_opcode_channel(dev, @@ -321,21 +342,21 @@ static int pcan_usb_fd_set_filter_std(struct peak_usb_device *dev, int idx, return pcan_usb_fd_send_cmd(dev, cmd); } -/* set/unset notifications filter: +/* set/unset options * - * onoff sets(1)/unset(0) notifications - * mask each bit defines a kind of notification to set/unset + * onoff set(1)/unset(0) options + * mask each bit defines a kind of options to set/unset */ -static int pcan_usb_fd_set_filter_ext(struct peak_usb_device *dev, - bool onoff, u16 ext_mask, u16 usb_mask) +static int pcan_usb_fd_set_options(struct peak_usb_device *dev, + bool onoff, u16 ucan_mask, u16 usb_mask) { - struct pcan_ufd_filter_ext *cmd = pcan_usb_fd_cmd_buffer(dev); + struct pcan_ufd_options *cmd = pcan_usb_fd_cmd_buffer(dev); cmd->opcode_channel = pucan_cmd_opcode_channel(dev, - (onoff) ? PUCAN_CMD_RX_FRAME_ENABLE : - PUCAN_CMD_RX_FRAME_DISABLE); + (onoff) ? PUCAN_CMD_SET_EN_OPTION : + PUCAN_CMD_CLR_DIS_OPTION); - cmd->ext_mask = cpu_to_le16(ext_mask); + cmd->ucan_mask = cpu_to_le16(ucan_mask); cmd->usb_mask = cpu_to_le16(usb_mask); /* send the command */ @@ -770,9 +791,9 @@ static int pcan_usb_fd_start(struct peak_usb_device *dev) &pcan_usb_pro_fd); /* enable USB calibration messages */ - err = pcan_usb_fd_set_filter_ext(dev, 1, - PUCAN_FLTEXT_ERROR, - PCAN_UFD_FLTEXT_CALIBRATION); + err = pcan_usb_fd_set_options(dev, 1, + PUCAN_OPTION_ERROR, + PCAN_UFD_FLTEXT_CALIBRATION); } pdev->usb_if->dev_opened_count++; @@ -806,9 +827,9 @@ static int pcan_usb_fd_stop(struct peak_usb_device *dev) /* turn off special msgs for that interface if no other dev opened */ if (pdev->usb_if->dev_opened_count == 1) - pcan_usb_fd_set_filter_ext(dev, 0, - PUCAN_FLTEXT_ERROR, - PCAN_UFD_FLTEXT_CALIBRATION); + pcan_usb_fd_set_options(dev, 0, + PUCAN_OPTION_ERROR, + PCAN_UFD_FLTEXT_CALIBRATION); pdev->usb_if->dev_opened_count--; return 0; @@ -860,8 +881,14 @@ static int pcan_usb_fd_init(struct peak_usb_device *dev) pdev->usb_if->fw_info.fw_version[2], dev->adapter->ctrl_count); - /* the currently supported hw is non-ISO */ - dev->can.ctrlmode = CAN_CTRLMODE_FD_NON_ISO; + /* check for ability to switch between ISO/non-ISO modes */ + if (pdev->usb_if->fw_info.fw_version[0] >= 2) { + /* firmware >= 2.x supports ISO/non-ISO switching */ + dev->can.ctrlmode_supported |= CAN_CTRLMODE_FD_NON_ISO; + } else { + /* firmware < 2.x only supports fixed(!) non-ISO */ + dev->can.ctrlmode |= CAN_CTRLMODE_FD_NON_ISO; + } /* tell the hardware the can driver is running */ err = pcan_usb_fd_drv_loaded(dev, 1); @@ -937,9 +964,9 @@ static void pcan_usb_fd_exit(struct peak_usb_device *dev) if (dev->ctrl_idx == 0) { /* turn off calibration message if any device were opened */ if (pdev->usb_if->dev_opened_count > 0) - pcan_usb_fd_set_filter_ext(dev, 0, - PUCAN_FLTEXT_ERROR, - PCAN_UFD_FLTEXT_CALIBRATION); + pcan_usb_fd_set_options(dev, 0, + PUCAN_OPTION_ERROR, + PCAN_UFD_FLTEXT_CALIBRATION); /* tell USB adapter that the driver is being unloaded */ pcan_usb_fd_drv_loaded(dev, 0); diff --git a/drivers/net/dsa/mv88e6123_61_65.c b/drivers/net/dsa/mv88e6123_61_65.c index 2d7e1ffe9fdc..b4af6d5aff7c 100644 --- a/drivers/net/dsa/mv88e6123_61_65.c +++ b/drivers/net/dsa/mv88e6123_61_65.c @@ -25,66 +25,33 @@ static char *mv88e6123_61_65_probe(struct device *host_dev, int sw_addr) if (bus == NULL) return NULL; - ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03); + ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID); if (ret >= 0) { - if (ret == 0x1212) + if (ret == PORT_SWITCH_ID_6123_A1) return "Marvell 88E6123 (A1)"; - if (ret == 0x1213) + if (ret == PORT_SWITCH_ID_6123_A2) return "Marvell 88E6123 (A2)"; - if ((ret & 0xfff0) == 0x1210) + if ((ret & 0xfff0) == PORT_SWITCH_ID_6123) return "Marvell 88E6123"; - if (ret == 0x1612) + if (ret == PORT_SWITCH_ID_6161_A1) return "Marvell 88E6161 (A1)"; - if (ret == 0x1613) + if (ret == PORT_SWITCH_ID_6161_A2) return "Marvell 88E6161 (A2)"; - if ((ret & 0xfff0) == 0x1610) + if ((ret & 0xfff0) == PORT_SWITCH_ID_6161) return "Marvell 88E6161"; - if (ret == 0x1652) + if (ret == PORT_SWITCH_ID_6165_A1) return "Marvell 88E6165 (A1)"; - if (ret == 0x1653) + if (ret == PORT_SWITCH_ID_6165_A2) return "Marvell 88e6165 (A2)"; - if ((ret & 0xfff0) == 0x1650) + if ((ret & 0xfff0) == PORT_SWITCH_ID_6165) return "Marvell 88E6165"; } return NULL; } -static int mv88e6123_61_65_switch_reset(struct dsa_switch *ds) -{ - int i; - int ret; - unsigned long timeout; - - /* Set all ports to the disabled state. */ - for (i = 0; i < 8; i++) { - ret = REG_READ(REG_PORT(i), 0x04); - REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc); - } - - /* Wait for transmit queues to drain. */ - usleep_range(2000, 4000); - - /* Reset the switch. */ - REG_WRITE(REG_GLOBAL, 0x04, 0xc400); - - /* Wait up to one second for reset to complete. */ - timeout = jiffies + 1 * HZ; - while (time_before(jiffies, timeout)) { - ret = REG_READ(REG_GLOBAL, 0x00); - if ((ret & 0xc800) == 0xc800) - break; - - usleep_range(1000, 2000); - } - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - - return 0; -} - static int mv88e6123_61_65_setup_global(struct dsa_switch *ds) { int ret; @@ -271,6 +238,7 @@ static int mv88e6123_61_65_setup_port(struct dsa_switch *ds, int p) static int mv88e6123_61_65_setup(struct dsa_switch *ds) { + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int i; int ret; @@ -278,7 +246,19 @@ static int mv88e6123_61_65_setup(struct dsa_switch *ds) if (ret < 0) return ret; - ret = mv88e6123_61_65_switch_reset(ds); + switch (ps->id) { + case PORT_SWITCH_ID_6123: + ps->num_ports = 3; + break; + case PORT_SWITCH_ID_6161: + case PORT_SWITCH_ID_6165: + ps->num_ports = 6; + break; + default: + return -ENODEV; + } + + ret = mv88e6xxx_switch_reset(ds, false); if (ret < 0) return ret; @@ -288,7 +268,7 @@ static int mv88e6123_61_65_setup(struct dsa_switch *ds) if (ret < 0) return ret; - for (i = 0; i < 6; i++) { + for (i = 0; i < ps->num_ports; i++) { ret = mv88e6123_61_65_setup_port(ds, i); if (ret < 0) return ret; @@ -297,108 +277,18 @@ static int mv88e6123_61_65_setup(struct dsa_switch *ds) return 0; } -static int mv88e6123_61_65_port_to_phy_addr(int port) -{ - if (port >= 0 && port <= 4) - return port; - return -1; -} - -static int -mv88e6123_61_65_phy_read(struct dsa_switch *ds, int port, int regnum) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int addr = mv88e6123_61_65_port_to_phy_addr(port); - int ret; - - mutex_lock(&ps->phy_mutex); - ret = mv88e6xxx_phy_read(ds, addr, regnum); - mutex_unlock(&ps->phy_mutex); - return ret; -} - -static int -mv88e6123_61_65_phy_write(struct dsa_switch *ds, - int port, int regnum, u16 val) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int addr = mv88e6123_61_65_port_to_phy_addr(port); - int ret; - - mutex_lock(&ps->phy_mutex); - ret = mv88e6xxx_phy_write(ds, addr, regnum, val); - mutex_unlock(&ps->phy_mutex); - return ret; -} - -static struct mv88e6xxx_hw_stat mv88e6123_61_65_hw_stats[] = { - { "in_good_octets", 8, 0x00, }, - { "in_bad_octets", 4, 0x02, }, - { "in_unicast", 4, 0x04, }, - { "in_broadcasts", 4, 0x06, }, - { "in_multicasts", 4, 0x07, }, - { "in_pause", 4, 0x16, }, - { "in_undersize", 4, 0x18, }, - { "in_fragments", 4, 0x19, }, - { "in_oversize", 4, 0x1a, }, - { "in_jabber", 4, 0x1b, }, - { "in_rx_error", 4, 0x1c, }, - { "in_fcs_error", 4, 0x1d, }, - { "out_octets", 8, 0x0e, }, - { "out_unicast", 4, 0x10, }, - { "out_broadcasts", 4, 0x13, }, - { "out_multicasts", 4, 0x12, }, - { "out_pause", 4, 0x15, }, - { "excessive", 4, 0x11, }, - { "collisions", 4, 0x1e, }, - { "deferred", 4, 0x05, }, - { "single", 4, 0x14, }, - { "multiple", 4, 0x17, }, - { "out_fcs_error", 4, 0x03, }, - { "late", 4, 0x1f, }, - { "hist_64bytes", 4, 0x08, }, - { "hist_65_127bytes", 4, 0x09, }, - { "hist_128_255bytes", 4, 0x0a, }, - { "hist_256_511bytes", 4, 0x0b, }, - { "hist_512_1023bytes", 4, 0x0c, }, - { "hist_1024_max_bytes", 4, 0x0d, }, - { "sw_in_discards", 4, 0x110, }, - { "sw_in_filtered", 2, 0x112, }, - { "sw_out_filtered", 2, 0x113, }, -}; - -static void -mv88e6123_61_65_get_strings(struct dsa_switch *ds, int port, uint8_t *data) -{ - mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6123_61_65_hw_stats), - mv88e6123_61_65_hw_stats, port, data); -} - -static void -mv88e6123_61_65_get_ethtool_stats(struct dsa_switch *ds, - int port, uint64_t *data) -{ - mv88e6xxx_get_ethtool_stats(ds, ARRAY_SIZE(mv88e6123_61_65_hw_stats), - mv88e6123_61_65_hw_stats, port, data); -} - -static int mv88e6123_61_65_get_sset_count(struct dsa_switch *ds) -{ - return ARRAY_SIZE(mv88e6123_61_65_hw_stats); -} - struct dsa_switch_driver mv88e6123_61_65_switch_driver = { .tag_protocol = DSA_TAG_PROTO_EDSA, .priv_size = sizeof(struct mv88e6xxx_priv_state), .probe = mv88e6123_61_65_probe, .setup = mv88e6123_61_65_setup, .set_addr = mv88e6xxx_set_addr_indirect, - .phy_read = mv88e6123_61_65_phy_read, - .phy_write = mv88e6123_61_65_phy_write, + .phy_read = mv88e6xxx_phy_read, + .phy_write = mv88e6xxx_phy_write, .poll_link = mv88e6xxx_poll_link, - .get_strings = mv88e6123_61_65_get_strings, - .get_ethtool_stats = mv88e6123_61_65_get_ethtool_stats, - .get_sset_count = mv88e6123_61_65_get_sset_count, + .get_strings = mv88e6xxx_get_strings, + .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, + .get_sset_count = mv88e6xxx_get_sset_count, #ifdef CONFIG_NET_DSA_HWMON .get_temp = mv88e6xxx_get_temp, #endif diff --git a/drivers/net/dsa/mv88e6131.c b/drivers/net/dsa/mv88e6131.c index 2540ef0142af..e54824fa0d95 100644 --- a/drivers/net/dsa/mv88e6131.c +++ b/drivers/net/dsa/mv88e6131.c @@ -17,12 +17,6 @@ #include <net/dsa.h> #include "mv88e6xxx.h" -/* Switch product IDs */ -#define ID_6085 0x04a0 -#define ID_6095 0x0950 -#define ID_6131 0x1060 -#define ID_6131_B2 0x1066 - static char *mv88e6131_probe(struct device *host_dev, int sw_addr) { struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev); @@ -31,56 +25,23 @@ static char *mv88e6131_probe(struct device *host_dev, int sw_addr) if (bus == NULL) return NULL; - ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03); + ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID); if (ret >= 0) { int ret_masked = ret & 0xfff0; - if (ret_masked == ID_6085) + if (ret_masked == PORT_SWITCH_ID_6085) return "Marvell 88E6085"; - if (ret_masked == ID_6095) + if (ret_masked == PORT_SWITCH_ID_6095) return "Marvell 88E6095/88E6095F"; - if (ret == ID_6131_B2) + if (ret == PORT_SWITCH_ID_6131_B2) return "Marvell 88E6131 (B2)"; - if (ret_masked == ID_6131) + if (ret_masked == PORT_SWITCH_ID_6131) return "Marvell 88E6131"; } return NULL; } -static int mv88e6131_switch_reset(struct dsa_switch *ds) -{ - int i; - int ret; - unsigned long timeout; - - /* Set all ports to the disabled state. */ - for (i = 0; i < 11; i++) { - ret = REG_READ(REG_PORT(i), 0x04); - REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc); - } - - /* Wait for transmit queues to drain. */ - usleep_range(2000, 4000); - - /* Reset the switch. */ - REG_WRITE(REG_GLOBAL, 0x04, 0xc400); - - /* Wait up to one second for reset to complete. */ - timeout = jiffies + 1 * HZ; - while (time_before(jiffies, timeout)) { - ret = REG_READ(REG_GLOBAL, 0x00); - if ((ret & 0xc800) == 0xc800) - break; - - usleep_range(1000, 2000); - } - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - - return 0; -} - static int mv88e6131_setup_global(struct dsa_switch *ds) { int ret; @@ -174,7 +135,7 @@ static int mv88e6131_setup_port(struct dsa_switch *ds, int p) * (100 Mb/s on 6085) full duplex. */ if (dsa_is_cpu_port(ds, p) || ds->dsa_port_mask & (1 << p)) - if (ps->id == ID_6085) + if (ps->id == PORT_SWITCH_ID_6085) REG_WRITE(addr, 0x01, 0x003d); /* 100 Mb/s */ else REG_WRITE(addr, 0x01, 0x003e); /* 1000 Mb/s */ @@ -201,35 +162,13 @@ static int mv88e6131_setup_port(struct dsa_switch *ds, int p) /* On 6085, unknown multicast forward is controlled * here rather than in Port Control 2 register. */ - if (ps->id == ID_6085) + if (ps->id == PORT_SWITCH_ID_6085) val |= 0x0008; } if (ds->dsa_port_mask & (1 << p)) val |= 0x0100; REG_WRITE(addr, 0x04, val); - /* Port Control 1: disable trunking. Also, if this is the - * CPU port, enable learn messages to be sent to this port. - */ - REG_WRITE(addr, 0x05, dsa_is_cpu_port(ds, p) ? 0x8000 : 0x0000); - - /* Port based VLAN map: give each port its own address - * database, allow the CPU port to talk to each of the 'real' - * ports, and allow each of the 'real' ports to only talk to - * the upstream port. - */ - val = (p & 0xf) << 12; - if (dsa_is_cpu_port(ds, p)) - val |= ds->phys_port_mask; - else - val |= 1 << dsa_upstream_port(ds); - REG_WRITE(addr, 0x06, val); - - /* Default VLAN ID and priority: don't set a default VLAN - * ID, and set the default packet priority to zero. - */ - REG_WRITE(addr, 0x07, 0x0000); - /* Port Control 2: don't force a good FCS, don't use * VLAN-based, source address-based or destination * address-based priority overrides, don't let the switch @@ -242,7 +181,7 @@ static int mv88e6131_setup_port(struct dsa_switch *ds, int p) * If this is the upstream port for this switch, enable * forwarding of unknown multicast addresses. */ - if (ps->id == ID_6085) + if (ps->id == PORT_SWITCH_ID_6085) /* on 6085, bits 3:0 are reserved, bit 6 control ARP * mirroring, and multicast forward is handled in * Port Control register. @@ -278,7 +217,7 @@ static int mv88e6131_setup_port(struct dsa_switch *ds, int p) */ REG_WRITE(addr, 0x19, 0x7654); - return 0; + return mv88e6xxx_setup_port_common(ds, p); } static int mv88e6131_setup(struct dsa_switch *ds) @@ -287,13 +226,28 @@ static int mv88e6131_setup(struct dsa_switch *ds) int i; int ret; - mutex_init(&ps->smi_mutex); + ret = mv88e6xxx_setup_common(ds); + if (ret < 0) + return ret; + mv88e6xxx_ppu_state_init(ds); - mutex_init(&ps->stats_mutex); - ps->id = REG_READ(REG_PORT(0), 0x03) & 0xfff0; + switch (ps->id) { + case PORT_SWITCH_ID_6085: + ps->num_ports = 10; + break; + case PORT_SWITCH_ID_6095: + ps->num_ports = 11; + break; + case PORT_SWITCH_ID_6131: + case PORT_SWITCH_ID_6131_B2: + ps->num_ports = 8; + break; + default: + return -ENODEV; + } - ret = mv88e6131_switch_reset(ds); + ret = mv88e6xxx_switch_reset(ds, false); if (ret < 0) return ret; @@ -303,7 +257,7 @@ static int mv88e6131_setup(struct dsa_switch *ds) if (ret < 0) return ret; - for (i = 0; i < 11; i++) { + for (i = 0; i < ps->num_ports; i++) { ret = mv88e6131_setup_port(ds, i); if (ret < 0) return ret; @@ -312,17 +266,24 @@ static int mv88e6131_setup(struct dsa_switch *ds) return 0; } -static int mv88e6131_port_to_phy_addr(int port) +static int mv88e6131_port_to_phy_addr(struct dsa_switch *ds, int port) { - if (port >= 0 && port <= 11) + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + + if (port >= 0 && port < ps->num_ports) return port; - return -1; + + return -EINVAL; } static int mv88e6131_phy_read(struct dsa_switch *ds, int port, int regnum) { - int addr = mv88e6131_port_to_phy_addr(port); + int addr = mv88e6131_port_to_phy_addr(ds, port); + + if (addr < 0) + return addr; + return mv88e6xxx_phy_read_ppu(ds, addr, regnum); } @@ -330,61 +291,12 @@ static int mv88e6131_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val) { - int addr = mv88e6131_port_to_phy_addr(port); - return mv88e6xxx_phy_write_ppu(ds, addr, regnum, val); -} - -static struct mv88e6xxx_hw_stat mv88e6131_hw_stats[] = { - { "in_good_octets", 8, 0x00, }, - { "in_bad_octets", 4, 0x02, }, - { "in_unicast", 4, 0x04, }, - { "in_broadcasts", 4, 0x06, }, - { "in_multicasts", 4, 0x07, }, - { "in_pause", 4, 0x16, }, - { "in_undersize", 4, 0x18, }, - { "in_fragments", 4, 0x19, }, - { "in_oversize", 4, 0x1a, }, - { "in_jabber", 4, 0x1b, }, - { "in_rx_error", 4, 0x1c, }, - { "in_fcs_error", 4, 0x1d, }, - { "out_octets", 8, 0x0e, }, - { "out_unicast", 4, 0x10, }, - { "out_broadcasts", 4, 0x13, }, - { "out_multicasts", 4, 0x12, }, - { "out_pause", 4, 0x15, }, - { "excessive", 4, 0x11, }, - { "collisions", 4, 0x1e, }, - { "deferred", 4, 0x05, }, - { "single", 4, 0x14, }, - { "multiple", 4, 0x17, }, - { "out_fcs_error", 4, 0x03, }, - { "late", 4, 0x1f, }, - { "hist_64bytes", 4, 0x08, }, - { "hist_65_127bytes", 4, 0x09, }, - { "hist_128_255bytes", 4, 0x0a, }, - { "hist_256_511bytes", 4, 0x0b, }, - { "hist_512_1023bytes", 4, 0x0c, }, - { "hist_1024_max_bytes", 4, 0x0d, }, -}; - -static void -mv88e6131_get_strings(struct dsa_switch *ds, int port, uint8_t *data) -{ - mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6131_hw_stats), - mv88e6131_hw_stats, port, data); -} + int addr = mv88e6131_port_to_phy_addr(ds, port); -static void -mv88e6131_get_ethtool_stats(struct dsa_switch *ds, - int port, uint64_t *data) -{ - mv88e6xxx_get_ethtool_stats(ds, ARRAY_SIZE(mv88e6131_hw_stats), - mv88e6131_hw_stats, port, data); -} + if (addr < 0) + return addr; -static int mv88e6131_get_sset_count(struct dsa_switch *ds) -{ - return ARRAY_SIZE(mv88e6131_hw_stats); + return mv88e6xxx_phy_write_ppu(ds, addr, regnum, val); } struct dsa_switch_driver mv88e6131_switch_driver = { @@ -396,9 +308,9 @@ struct dsa_switch_driver mv88e6131_switch_driver = { .phy_read = mv88e6131_phy_read, .phy_write = mv88e6131_phy_write, .poll_link = mv88e6xxx_poll_link, - .get_strings = mv88e6131_get_strings, - .get_ethtool_stats = mv88e6131_get_ethtool_stats, - .get_sset_count = mv88e6131_get_sset_count, + .get_strings = mv88e6xxx_get_strings, + .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, + .get_sset_count = mv88e6xxx_get_sset_count, }; MODULE_ALIAS("platform:mv88e6085"); diff --git a/drivers/net/dsa/mv88e6171.c b/drivers/net/dsa/mv88e6171.c index 18cfead83dc9..9104efea0e3e 100644 --- a/drivers/net/dsa/mv88e6171.c +++ b/drivers/net/dsa/mv88e6171.c @@ -17,10 +17,6 @@ #include <net/dsa.h> #include "mv88e6xxx.h" -/* Switch product IDs */ -#define ID_6171 0x1710 -#define ID_6172 0x1720 - static char *mv88e6171_probe(struct device *host_dev, int sw_addr) { struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev); @@ -29,64 +25,20 @@ static char *mv88e6171_probe(struct device *host_dev, int sw_addr) if (bus == NULL) return NULL; - ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03); + ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID); if (ret >= 0) { - if ((ret & 0xfff0) == ID_6171) + if ((ret & 0xfff0) == PORT_SWITCH_ID_6171) return "Marvell 88E6171"; - if ((ret & 0xfff0) == ID_6172) + if ((ret & 0xfff0) == PORT_SWITCH_ID_6172) return "Marvell 88E6172"; } return NULL; } -static int mv88e6171_switch_reset(struct dsa_switch *ds) -{ - int i; - int ret; - unsigned long timeout; - - /* Set all ports to the disabled state. */ - for (i = 0; i < 8; i++) { - ret = REG_READ(REG_PORT(i), 0x04); - REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc); - } - - /* Wait for transmit queues to drain. */ - usleep_range(2000, 4000); - - /* Reset the switch. Keep PPU active. The PPU needs to be - * active to support indirect phy register accesses through - * global registers 0x18 and 0x19. - */ - REG_WRITE(REG_GLOBAL, 0x04, 0xc000); - - /* Wait up to one second for reset to complete. */ - timeout = jiffies + 1 * HZ; - while (time_before(jiffies, timeout)) { - ret = REG_READ(REG_GLOBAL, 0x00); - if ((ret & 0xc800) == 0xc800) - break; - - usleep_range(1000, 2000); - } - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - - /* Enable ports not under DSA, e.g. WAN port */ - for (i = 0; i < 8; i++) { - if (dsa_is_cpu_port(ds, i) || ds->phys_port_mask & (1 << i)) - continue; - - ret = REG_READ(REG_PORT(i), 0x04); - REG_WRITE(REG_PORT(i), 0x04, ret | 0x03); - } - - return 0; -} - static int mv88e6171_setup_global(struct dsa_switch *ds) { + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int ret; int i; @@ -151,7 +103,7 @@ static int mv88e6171_setup_global(struct dsa_switch *ds) } /* Clear all trunk masks. */ - for (i = 0; i < 8; i++) + for (i = 0; i < ps->num_ports; i++) REG_WRITE(REG_GLOBAL2, 0x07, 0x8000 | (i << 12) | 0xff); /* Clear all trunk mappings. */ @@ -274,6 +226,7 @@ static int mv88e6171_setup_port(struct dsa_switch *ds, int p) static int mv88e6171_setup(struct dsa_switch *ds) { + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int i; int ret; @@ -281,7 +234,9 @@ static int mv88e6171_setup(struct dsa_switch *ds) if (ret < 0) return ret; - ret = mv88e6171_switch_reset(ds); + ps->num_ports = 7; + + ret = mv88e6xxx_switch_reset(ds, true); if (ret < 0) return ret; @@ -291,7 +246,7 @@ static int mv88e6171_setup(struct dsa_switch *ds) if (ret < 0) return ret; - for (i = 0; i < 8; i++) { + for (i = 0; i < ps->num_ports; i++) { if (!(dsa_is_cpu_port(ds, i) || ds->phys_port_mask & (1 << i))) continue; @@ -303,99 +258,12 @@ static int mv88e6171_setup(struct dsa_switch *ds) return 0; } -static int mv88e6171_port_to_phy_addr(int port) -{ - if (port >= 0 && port <= 4) - return port; - return -1; -} - -static int -mv88e6171_phy_read(struct dsa_switch *ds, int port, int regnum) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int addr = mv88e6171_port_to_phy_addr(port); - int ret; - - mutex_lock(&ps->phy_mutex); - ret = mv88e6xxx_phy_read_indirect(ds, addr, regnum); - mutex_unlock(&ps->phy_mutex); - return ret; -} - -static int -mv88e6171_phy_write(struct dsa_switch *ds, - int port, int regnum, u16 val) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int addr = mv88e6171_port_to_phy_addr(port); - int ret; - - mutex_lock(&ps->phy_mutex); - ret = mv88e6xxx_phy_write_indirect(ds, addr, regnum, val); - mutex_unlock(&ps->phy_mutex); - return ret; -} - -static struct mv88e6xxx_hw_stat mv88e6171_hw_stats[] = { - { "in_good_octets", 8, 0x00, }, - { "in_bad_octets", 4, 0x02, }, - { "in_unicast", 4, 0x04, }, - { "in_broadcasts", 4, 0x06, }, - { "in_multicasts", 4, 0x07, }, - { "in_pause", 4, 0x16, }, - { "in_undersize", 4, 0x18, }, - { "in_fragments", 4, 0x19, }, - { "in_oversize", 4, 0x1a, }, - { "in_jabber", 4, 0x1b, }, - { "in_rx_error", 4, 0x1c, }, - { "in_fcs_error", 4, 0x1d, }, - { "out_octets", 8, 0x0e, }, - { "out_unicast", 4, 0x10, }, - { "out_broadcasts", 4, 0x13, }, - { "out_multicasts", 4, 0x12, }, - { "out_pause", 4, 0x15, }, - { "excessive", 4, 0x11, }, - { "collisions", 4, 0x1e, }, - { "deferred", 4, 0x05, }, - { "single", 4, 0x14, }, - { "multiple", 4, 0x17, }, - { "out_fcs_error", 4, 0x03, }, - { "late", 4, 0x1f, }, - { "hist_64bytes", 4, 0x08, }, - { "hist_65_127bytes", 4, 0x09, }, - { "hist_128_255bytes", 4, 0x0a, }, - { "hist_256_511bytes", 4, 0x0b, }, - { "hist_512_1023bytes", 4, 0x0c, }, - { "hist_1024_max_bytes", 4, 0x0d, }, -}; - -static void -mv88e6171_get_strings(struct dsa_switch *ds, int port, uint8_t *data) -{ - mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6171_hw_stats), - mv88e6171_hw_stats, port, data); -} - -static void -mv88e6171_get_ethtool_stats(struct dsa_switch *ds, - int port, uint64_t *data) -{ - mv88e6xxx_get_ethtool_stats(ds, ARRAY_SIZE(mv88e6171_hw_stats), - mv88e6171_hw_stats, port, data); -} - -static int mv88e6171_get_sset_count(struct dsa_switch *ds) -{ - return ARRAY_SIZE(mv88e6171_hw_stats); -} - static int mv88e6171_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - if (ps->id == ID_6172) + if (ps->id == PORT_SWITCH_ID_6172) return mv88e6xxx_get_eee(ds, port, e); return -EOPNOTSUPP; @@ -406,7 +274,7 @@ static int mv88e6171_set_eee(struct dsa_switch *ds, int port, { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - if (ps->id == ID_6172) + if (ps->id == PORT_SWITCH_ID_6172) return mv88e6xxx_set_eee(ds, port, phydev, e); return -EOPNOTSUPP; @@ -418,12 +286,12 @@ struct dsa_switch_driver mv88e6171_switch_driver = { .probe = mv88e6171_probe, .setup = mv88e6171_setup, .set_addr = mv88e6xxx_set_addr_indirect, - .phy_read = mv88e6171_phy_read, - .phy_write = mv88e6171_phy_write, + .phy_read = mv88e6xxx_phy_read_indirect, + .phy_write = mv88e6xxx_phy_write_indirect, .poll_link = mv88e6xxx_poll_link, - .get_strings = mv88e6171_get_strings, - .get_ethtool_stats = mv88e6171_get_ethtool_stats, - .get_sset_count = mv88e6171_get_sset_count, + .get_strings = mv88e6xxx_get_strings, + .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, + .get_sset_count = mv88e6xxx_get_sset_count, .set_eee = mv88e6171_set_eee, .get_eee = mv88e6171_get_eee, #ifdef CONFIG_NET_DSA_HWMON diff --git a/drivers/net/dsa/mv88e6352.c b/drivers/net/dsa/mv88e6352.c index 41fe3a6a72d1..126c11b81e75 100644 --- a/drivers/net/dsa/mv88e6352.c +++ b/drivers/net/dsa/mv88e6352.c @@ -30,58 +30,24 @@ static char *mv88e6352_probe(struct device *host_dev, int sw_addr) if (bus == NULL) return NULL; - ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03); + ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID); if (ret >= 0) { - if ((ret & 0xfff0) == 0x1760) + if ((ret & 0xfff0) == PORT_SWITCH_ID_6176) return "Marvell 88E6176"; - if (ret == 0x3521) + if (ret == PORT_SWITCH_ID_6352_A0) return "Marvell 88E6352 (A0)"; - if (ret == 0x3522) + if (ret == PORT_SWITCH_ID_6352_A1) return "Marvell 88E6352 (A1)"; - if ((ret & 0xfff0) == 0x3520) + if ((ret & 0xfff0) == PORT_SWITCH_ID_6352) return "Marvell 88E6352"; } return NULL; } -static int mv88e6352_switch_reset(struct dsa_switch *ds) -{ - unsigned long timeout; - int ret; - int i; - - /* Set all ports to the disabled state. */ - for (i = 0; i < 7; i++) { - ret = REG_READ(REG_PORT(i), 0x04); - REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc); - } - - /* Wait for transmit queues to drain. */ - usleep_range(2000, 4000); - - /* Reset the switch. Keep PPU active (bit 14, undocumented). - * The PPU needs to be active to support indirect phy register - * accesses through global registers 0x18 and 0x19. - */ - REG_WRITE(REG_GLOBAL, 0x04, 0xc000); - - /* Wait up to one second for reset to complete. */ - timeout = jiffies + 1 * HZ; - while (time_before(jiffies, timeout)) { - ret = REG_READ(REG_GLOBAL, 0x00); - if ((ret & 0x8800) == 0x8800) - break; - usleep_range(1000, 2000); - } - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - - return 0; -} - static int mv88e6352_setup_global(struct dsa_switch *ds) { + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int ret; int i; @@ -152,7 +118,7 @@ static int mv88e6352_setup_global(struct dsa_switch *ds) /* Disable ingress rate limiting by resetting all ingress * rate limit registers to their initial state. */ - for (i = 0; i < 7; i++) + for (i = 0; i < ps->num_ports; i++) REG_WRITE(REG_GLOBAL2, 0x09, 0x9000 | (i << 8)); /* Initialise cross-chip port VLAN table to reset defaults. */ @@ -264,48 +230,13 @@ static int mv88e6352_setup_port(struct dsa_switch *ds, int p) #ifdef CONFIG_NET_DSA_HWMON -static int mv88e6352_phy_page_read(struct dsa_switch *ds, - int port, int page, int reg) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int ret; - - mutex_lock(&ps->phy_mutex); - ret = mv88e6xxx_phy_write_indirect(ds, port, 0x16, page); - if (ret < 0) - goto error; - ret = mv88e6xxx_phy_read_indirect(ds, port, reg); -error: - mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0); - mutex_unlock(&ps->phy_mutex); - return ret; -} - -static int mv88e6352_phy_page_write(struct dsa_switch *ds, - int port, int page, int reg, int val) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int ret; - - mutex_lock(&ps->phy_mutex); - ret = mv88e6xxx_phy_write_indirect(ds, port, 0x16, page); - if (ret < 0) - goto error; - - ret = mv88e6xxx_phy_write_indirect(ds, port, reg, val); -error: - mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0); - mutex_unlock(&ps->phy_mutex); - return ret; -} - static int mv88e6352_get_temp(struct dsa_switch *ds, int *temp) { int ret; *temp = 0; - ret = mv88e6352_phy_page_read(ds, 0, 6, 27); + ret = mv88e6xxx_phy_page_read(ds, 0, 6, 27); if (ret < 0) return ret; @@ -320,7 +251,7 @@ static int mv88e6352_get_temp_limit(struct dsa_switch *ds, int *temp) *temp = 0; - ret = mv88e6352_phy_page_read(ds, 0, 6, 26); + ret = mv88e6xxx_phy_page_read(ds, 0, 6, 26); if (ret < 0) return ret; @@ -333,11 +264,11 @@ static int mv88e6352_set_temp_limit(struct dsa_switch *ds, int temp) { int ret; - ret = mv88e6352_phy_page_read(ds, 0, 6, 26); + ret = mv88e6xxx_phy_page_read(ds, 0, 6, 26); if (ret < 0) return ret; temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f); - return mv88e6352_phy_page_write(ds, 0, 6, 26, + return mv88e6xxx_phy_page_write(ds, 0, 6, 26, (ret & 0xe0ff) | (temp << 8)); } @@ -347,7 +278,7 @@ static int mv88e6352_get_temp_alarm(struct dsa_switch *ds, bool *alarm) *alarm = false; - ret = mv88e6352_phy_page_read(ds, 0, 6, 26); + ret = mv88e6xxx_phy_page_read(ds, 0, 6, 26); if (ret < 0) return ret; @@ -367,9 +298,11 @@ static int mv88e6352_setup(struct dsa_switch *ds) if (ret < 0) return ret; + ps->num_ports = 7; + mutex_init(&ps->eeprom_mutex); - ret = mv88e6352_switch_reset(ds); + ret = mv88e6xxx_switch_reset(ds, true); if (ret < 0) return ret; @@ -379,7 +312,7 @@ static int mv88e6352_setup(struct dsa_switch *ds) if (ret < 0) return ret; - for (i = 0; i < 7; i++) { + for (i = 0; i < ps->num_ports; i++) { ret = mv88e6352_setup_port(ds, i); if (ret < 0) return ret; @@ -388,83 +321,6 @@ static int mv88e6352_setup(struct dsa_switch *ds) return 0; } -static int mv88e6352_port_to_phy_addr(int port) -{ - if (port >= 0 && port <= 4) - return port; - return -EINVAL; -} - -static int -mv88e6352_phy_read(struct dsa_switch *ds, int port, int regnum) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int addr = mv88e6352_port_to_phy_addr(port); - int ret; - - if (addr < 0) - return addr; - - mutex_lock(&ps->phy_mutex); - ret = mv88e6xxx_phy_read_indirect(ds, addr, regnum); - mutex_unlock(&ps->phy_mutex); - - return ret; -} - -static int -mv88e6352_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int addr = mv88e6352_port_to_phy_addr(port); - int ret; - - if (addr < 0) - return addr; - - mutex_lock(&ps->phy_mutex); - ret = mv88e6xxx_phy_write_indirect(ds, addr, regnum, val); - mutex_unlock(&ps->phy_mutex); - - return ret; -} - -static struct mv88e6xxx_hw_stat mv88e6352_hw_stats[] = { - { "in_good_octets", 8, 0x00, }, - { "in_bad_octets", 4, 0x02, }, - { "in_unicast", 4, 0x04, }, - { "in_broadcasts", 4, 0x06, }, - { "in_multicasts", 4, 0x07, }, - { "in_pause", 4, 0x16, }, - { "in_undersize", 4, 0x18, }, - { "in_fragments", 4, 0x19, }, - { "in_oversize", 4, 0x1a, }, - { "in_jabber", 4, 0x1b, }, - { "in_rx_error", 4, 0x1c, }, - { "in_fcs_error", 4, 0x1d, }, - { "out_octets", 8, 0x0e, }, - { "out_unicast", 4, 0x10, }, - { "out_broadcasts", 4, 0x13, }, - { "out_multicasts", 4, 0x12, }, - { "out_pause", 4, 0x15, }, - { "excessive", 4, 0x11, }, - { "collisions", 4, 0x1e, }, - { "deferred", 4, 0x05, }, - { "single", 4, 0x14, }, - { "multiple", 4, 0x17, }, - { "out_fcs_error", 4, 0x03, }, - { "late", 4, 0x1f, }, - { "hist_64bytes", 4, 0x08, }, - { "hist_65_127bytes", 4, 0x09, }, - { "hist_128_255bytes", 4, 0x0a, }, - { "hist_256_511bytes", 4, 0x0b, }, - { "hist_512_1023bytes", 4, 0x0c, }, - { "hist_1024_max_bytes", 4, 0x0d, }, - { "sw_in_discards", 4, 0x110, }, - { "sw_in_filtered", 2, 0x112, }, - { "sw_out_filtered", 2, 0x113, }, -}; - static int mv88e6352_read_eeprom_word(struct dsa_switch *ds, int addr) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); @@ -663,37 +519,18 @@ static int mv88e6352_set_eeprom(struct dsa_switch *ds, return 0; } -static void -mv88e6352_get_strings(struct dsa_switch *ds, int port, uint8_t *data) -{ - mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6352_hw_stats), - mv88e6352_hw_stats, port, data); -} - -static void -mv88e6352_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data) -{ - mv88e6xxx_get_ethtool_stats(ds, ARRAY_SIZE(mv88e6352_hw_stats), - mv88e6352_hw_stats, port, data); -} - -static int mv88e6352_get_sset_count(struct dsa_switch *ds) -{ - return ARRAY_SIZE(mv88e6352_hw_stats); -} - struct dsa_switch_driver mv88e6352_switch_driver = { .tag_protocol = DSA_TAG_PROTO_EDSA, .priv_size = sizeof(struct mv88e6xxx_priv_state), .probe = mv88e6352_probe, .setup = mv88e6352_setup, .set_addr = mv88e6xxx_set_addr_indirect, - .phy_read = mv88e6352_phy_read, - .phy_write = mv88e6352_phy_write, + .phy_read = mv88e6xxx_phy_read_indirect, + .phy_write = mv88e6xxx_phy_write_indirect, .poll_link = mv88e6xxx_poll_link, - .get_strings = mv88e6352_get_strings, - .get_ethtool_stats = mv88e6352_get_ethtool_stats, - .get_sset_count = mv88e6352_get_sset_count, + .get_strings = mv88e6xxx_get_strings, + .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, + .get_sset_count = mv88e6xxx_get_sset_count, .set_eee = mv88e6xxx_set_eee, .get_eee = mv88e6xxx_get_eee, #ifdef CONFIG_NET_DSA_HWMON diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index 13572cc24c6d..fc8d3b6ffe8e 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -33,11 +33,11 @@ static int mv88e6xxx_reg_wait_ready(struct mii_bus *bus, int sw_addr) int i; for (i = 0; i < 16; i++) { - ret = mdiobus_read(bus, sw_addr, 0); + ret = mdiobus_read(bus, sw_addr, SMI_CMD); if (ret < 0) return ret; - if ((ret & 0x8000) == 0) + if ((ret & SMI_CMD_BUSY) == 0) return 0; } @@ -57,7 +57,8 @@ int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg) return ret; /* Transmit the read command. */ - ret = mdiobus_write(bus, sw_addr, 0, 0x9800 | (addr << 5) | reg); + ret = mdiobus_write(bus, sw_addr, SMI_CMD, + SMI_CMD_OP_22_READ | (addr << 5) | reg); if (ret < 0) return ret; @@ -67,7 +68,7 @@ int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg) return ret; /* Read the data. */ - ret = mdiobus_read(bus, sw_addr, 1); + ret = mdiobus_read(bus, sw_addr, SMI_DATA); if (ret < 0) return ret; @@ -119,12 +120,13 @@ int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr, return ret; /* Transmit the data to write. */ - ret = mdiobus_write(bus, sw_addr, 1, val); + ret = mdiobus_write(bus, sw_addr, SMI_DATA, val); if (ret < 0) return ret; /* Transmit the write command. */ - ret = mdiobus_write(bus, sw_addr, 0, 0x9400 | (addr << 5) | reg); + ret = mdiobus_write(bus, sw_addr, SMI_CMD, + SMI_CMD_OP_22_WRITE | (addr << 5) | reg); if (ret < 0) return ret; @@ -166,26 +168,26 @@ int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val) int mv88e6xxx_config_prio(struct dsa_switch *ds) { /* Configure the IP ToS mapping registers. */ - REG_WRITE(REG_GLOBAL, 0x10, 0x0000); - REG_WRITE(REG_GLOBAL, 0x11, 0x0000); - REG_WRITE(REG_GLOBAL, 0x12, 0x5555); - REG_WRITE(REG_GLOBAL, 0x13, 0x5555); - REG_WRITE(REG_GLOBAL, 0x14, 0xaaaa); - REG_WRITE(REG_GLOBAL, 0x15, 0xaaaa); - REG_WRITE(REG_GLOBAL, 0x16, 0xffff); - REG_WRITE(REG_GLOBAL, 0x17, 0xffff); + REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_0, 0x0000); + REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_1, 0x0000); + REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_2, 0x5555); + REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_3, 0x5555); + REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_4, 0xaaaa); + REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_5, 0xaaaa); + REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_6, 0xffff); + REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_7, 0xffff); /* Configure the IEEE 802.1p priority mapping register. */ - REG_WRITE(REG_GLOBAL, 0x18, 0xfa41); + REG_WRITE(REG_GLOBAL, GLOBAL_IEEE_PRI, 0xfa41); return 0; } int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr) { - REG_WRITE(REG_GLOBAL, 0x01, (addr[0] << 8) | addr[1]); - REG_WRITE(REG_GLOBAL, 0x02, (addr[2] << 8) | addr[3]); - REG_WRITE(REG_GLOBAL, 0x03, (addr[4] << 8) | addr[5]); + REG_WRITE(REG_GLOBAL, GLOBAL_MAC_01, (addr[0] << 8) | addr[1]); + REG_WRITE(REG_GLOBAL, GLOBAL_MAC_23, (addr[2] << 8) | addr[3]); + REG_WRITE(REG_GLOBAL, GLOBAL_MAC_45, (addr[4] << 8) | addr[5]); return 0; } @@ -199,12 +201,13 @@ int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr) int j; /* Write the MAC address byte. */ - REG_WRITE(REG_GLOBAL2, 0x0d, 0x8000 | (i << 8) | addr[i]); + REG_WRITE(REG_GLOBAL2, GLOBAL2_SWITCH_MAC, + GLOBAL2_SWITCH_MAC_BUSY | (i << 8) | addr[i]); /* Wait for the write to complete. */ for (j = 0; j < 16; j++) { - ret = REG_READ(REG_GLOBAL2, 0x0d); - if ((ret & 0x8000) == 0) + ret = REG_READ(REG_GLOBAL2, GLOBAL2_SWITCH_MAC); + if ((ret & GLOBAL2_SWITCH_MAC_BUSY) == 0) break; } if (j == 16) @@ -214,14 +217,17 @@ int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr) return 0; } -int mv88e6xxx_phy_read(struct dsa_switch *ds, int addr, int regnum) +/* Must be called with phy mutex held */ +static int _mv88e6xxx_phy_read(struct dsa_switch *ds, int addr, int regnum) { if (addr >= 0) return mv88e6xxx_reg_read(ds, addr, regnum); return 0xffff; } -int mv88e6xxx_phy_write(struct dsa_switch *ds, int addr, int regnum, u16 val) +/* Must be called with phy mutex held */ +static int _mv88e6xxx_phy_write(struct dsa_switch *ds, int addr, int regnum, + u16 val) { if (addr >= 0) return mv88e6xxx_reg_write(ds, addr, regnum, val); @@ -234,14 +240,16 @@ static int mv88e6xxx_ppu_disable(struct dsa_switch *ds) int ret; unsigned long timeout; - ret = REG_READ(REG_GLOBAL, 0x04); - REG_WRITE(REG_GLOBAL, 0x04, ret & ~0x4000); + ret = REG_READ(REG_GLOBAL, GLOBAL_CONTROL); + REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL, + ret & ~GLOBAL_CONTROL_PPU_ENABLE); timeout = jiffies + 1 * HZ; while (time_before(jiffies, timeout)) { - ret = REG_READ(REG_GLOBAL, 0x00); + ret = REG_READ(REG_GLOBAL, GLOBAL_STATUS); usleep_range(1000, 2000); - if ((ret & 0xc000) != 0xc000) + if ((ret & GLOBAL_STATUS_PPU_MASK) != + GLOBAL_STATUS_PPU_POLLING) return 0; } @@ -253,14 +261,15 @@ static int mv88e6xxx_ppu_enable(struct dsa_switch *ds) int ret; unsigned long timeout; - ret = REG_READ(REG_GLOBAL, 0x04); - REG_WRITE(REG_GLOBAL, 0x04, ret | 0x4000); + ret = REG_READ(REG_GLOBAL, GLOBAL_CONTROL); + REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL, ret | GLOBAL_CONTROL_PPU_ENABLE); timeout = jiffies + 1 * HZ; while (time_before(jiffies, timeout)) { - ret = REG_READ(REG_GLOBAL, 0x00); + ret = REG_READ(REG_GLOBAL, GLOBAL_STATUS); usleep_range(1000, 2000); - if ((ret & 0xc000) == 0xc000) + if ((ret & GLOBAL_STATUS_PPU_MASK) == + GLOBAL_STATUS_PPU_POLLING) return 0; } @@ -381,11 +390,12 @@ void mv88e6xxx_poll_link(struct dsa_switch *ds) link = 0; if (dev->flags & IFF_UP) { - port_status = mv88e6xxx_reg_read(ds, REG_PORT(i), 0x00); + port_status = mv88e6xxx_reg_read(ds, REG_PORT(i), + PORT_STATUS); if (port_status < 0) continue; - link = !!(port_status & 0x0800); + link = !!(port_status & PORT_STATUS_LINK); } if (!link) { @@ -396,22 +406,22 @@ void mv88e6xxx_poll_link(struct dsa_switch *ds) continue; } - switch (port_status & 0x0300) { - case 0x0000: + switch (port_status & PORT_STATUS_SPEED_MASK) { + case PORT_STATUS_SPEED_10: speed = 10; break; - case 0x0100: + case PORT_STATUS_SPEED_100: speed = 100; break; - case 0x0200: + case PORT_STATUS_SPEED_1000: speed = 1000; break; default: speed = -1; break; } - duplex = (port_status & 0x0400) ? 1 : 0; - fc = (port_status & 0x8000) ? 1 : 0; + duplex = (port_status & PORT_STATUS_DUPLEX) ? 1 : 0; + fc = (port_status & PORT_STATUS_PAUSE_EN) ? 1 : 0; if (!netif_carrier_ok(dev)) { netdev_info(dev, @@ -424,14 +434,27 @@ void mv88e6xxx_poll_link(struct dsa_switch *ds) } } +static bool mv88e6xxx_6352_family(struct dsa_switch *ds) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + + switch (ps->id) { + case PORT_SWITCH_ID_6352: + case PORT_SWITCH_ID_6172: + case PORT_SWITCH_ID_6176: + return true; + } + return false; +} + static int mv88e6xxx_stats_wait(struct dsa_switch *ds) { int ret; int i; for (i = 0; i < 10; i++) { - ret = REG_READ(REG_GLOBAL, 0x1d); - if ((ret & 0x8000) == 0) + ret = REG_READ(REG_GLOBAL, GLOBAL_STATS_OP); + if ((ret & GLOBAL_STATS_OP_BUSY) == 0) return 0; } @@ -442,8 +465,13 @@ static int mv88e6xxx_stats_snapshot(struct dsa_switch *ds, int port) { int ret; + if (mv88e6xxx_6352_family(ds)) + port = (port + 1) << 5; + /* Snapshot the hardware statistics counters for this port. */ - REG_WRITE(REG_GLOBAL, 0x1d, 0xdc00 | port); + REG_WRITE(REG_GLOBAL, GLOBAL_STATS_OP, + GLOBAL_STATS_OP_CAPTURE_PORT | + GLOBAL_STATS_OP_HIST_RX_TX | port); /* Wait for the snapshotting to complete. */ ret = mv88e6xxx_stats_wait(ds); @@ -460,7 +488,9 @@ static void mv88e6xxx_stats_read(struct dsa_switch *ds, int stat, u32 *val) *val = 0; - ret = mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x1d, 0xcc00 | stat); + ret = mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_STATS_OP, + GLOBAL_STATS_OP_READ_CAPTURED | + GLOBAL_STATS_OP_HIST_RX_TX | stat); if (ret < 0) return; @@ -468,22 +498,77 @@ static void mv88e6xxx_stats_read(struct dsa_switch *ds, int stat, u32 *val) if (ret < 0) return; - ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x1e); + ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_COUNTER_32); if (ret < 0) return; _val = ret << 16; - ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x1f); + ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_COUNTER_01); if (ret < 0) return; *val = _val | ret; } -void mv88e6xxx_get_strings(struct dsa_switch *ds, - int nr_stats, struct mv88e6xxx_hw_stat *stats, - int port, uint8_t *data) +static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = { + { "in_good_octets", 8, 0x00, }, + { "in_bad_octets", 4, 0x02, }, + { "in_unicast", 4, 0x04, }, + { "in_broadcasts", 4, 0x06, }, + { "in_multicasts", 4, 0x07, }, + { "in_pause", 4, 0x16, }, + { "in_undersize", 4, 0x18, }, + { "in_fragments", 4, 0x19, }, + { "in_oversize", 4, 0x1a, }, + { "in_jabber", 4, 0x1b, }, + { "in_rx_error", 4, 0x1c, }, + { "in_fcs_error", 4, 0x1d, }, + { "out_octets", 8, 0x0e, }, + { "out_unicast", 4, 0x10, }, + { "out_broadcasts", 4, 0x13, }, + { "out_multicasts", 4, 0x12, }, + { "out_pause", 4, 0x15, }, + { "excessive", 4, 0x11, }, + { "collisions", 4, 0x1e, }, + { "deferred", 4, 0x05, }, + { "single", 4, 0x14, }, + { "multiple", 4, 0x17, }, + { "out_fcs_error", 4, 0x03, }, + { "late", 4, 0x1f, }, + { "hist_64bytes", 4, 0x08, }, + { "hist_65_127bytes", 4, 0x09, }, + { "hist_128_255bytes", 4, 0x0a, }, + { "hist_256_511bytes", 4, 0x0b, }, + { "hist_512_1023bytes", 4, 0x0c, }, + { "hist_1024_max_bytes", 4, 0x0d, }, + /* Not all devices have the following counters */ + { "sw_in_discards", 4, 0x110, }, + { "sw_in_filtered", 2, 0x112, }, + { "sw_out_filtered", 2, 0x113, }, + +}; + +static bool have_sw_in_discards(struct dsa_switch *ds) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + + switch (ps->id) { + case PORT_SWITCH_ID_6095: case PORT_SWITCH_ID_6161: + case PORT_SWITCH_ID_6165: case PORT_SWITCH_ID_6171: + case PORT_SWITCH_ID_6172: case PORT_SWITCH_ID_6176: + case PORT_SWITCH_ID_6182: case PORT_SWITCH_ID_6185: + case PORT_SWITCH_ID_6352: + return true; + default: + return false; + } +} + +static void _mv88e6xxx_get_strings(struct dsa_switch *ds, + int nr_stats, + struct mv88e6xxx_hw_stat *stats, + int port, uint8_t *data) { int i; @@ -493,9 +578,10 @@ void mv88e6xxx_get_strings(struct dsa_switch *ds, } } -void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, - int nr_stats, struct mv88e6xxx_hw_stat *stats, - int port, uint64_t *data) +static void _mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, + int nr_stats, + struct mv88e6xxx_hw_stat *stats, + int port, uint64_t *data) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int ret; @@ -543,6 +629,39 @@ error: mutex_unlock(&ps->stats_mutex); } +/* All the statistics in the table */ +void +mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data) +{ + if (have_sw_in_discards(ds)) + _mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6xxx_hw_stats), + mv88e6xxx_hw_stats, port, data); + else + _mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6xxx_hw_stats) - 3, + mv88e6xxx_hw_stats, port, data); +} + +int mv88e6xxx_get_sset_count(struct dsa_switch *ds) +{ + if (have_sw_in_discards(ds)) + return ARRAY_SIZE(mv88e6xxx_hw_stats); + return ARRAY_SIZE(mv88e6xxx_hw_stats) - 3; +} + +void +mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, + int port, uint64_t *data) +{ + if (have_sw_in_discards(ds)) + _mv88e6xxx_get_ethtool_stats( + ds, ARRAY_SIZE(mv88e6xxx_hw_stats), + mv88e6xxx_hw_stats, port, data); + else + _mv88e6xxx_get_ethtool_stats( + ds, ARRAY_SIZE(mv88e6xxx_hw_stats) - 3, + mv88e6xxx_hw_stats, port, data); +} + int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port) { return 32 * sizeof(u16); @@ -579,37 +698,37 @@ int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp) mutex_lock(&ps->phy_mutex); - ret = mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x6); + ret = _mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x6); if (ret < 0) goto error; /* Enable temperature sensor */ - ret = mv88e6xxx_phy_read(ds, 0x0, 0x1a); + ret = _mv88e6xxx_phy_read(ds, 0x0, 0x1a); if (ret < 0) goto error; - ret = mv88e6xxx_phy_write(ds, 0x0, 0x1a, ret | (1 << 5)); + ret = _mv88e6xxx_phy_write(ds, 0x0, 0x1a, ret | (1 << 5)); if (ret < 0) goto error; /* Wait for temperature to stabilize */ usleep_range(10000, 12000); - val = mv88e6xxx_phy_read(ds, 0x0, 0x1a); + val = _mv88e6xxx_phy_read(ds, 0x0, 0x1a); if (val < 0) { ret = val; goto error; } /* Disable temperature sensor */ - ret = mv88e6xxx_phy_write(ds, 0x0, 0x1a, ret & ~(1 << 5)); + ret = _mv88e6xxx_phy_write(ds, 0x0, 0x1a, ret & ~(1 << 5)); if (ret < 0) goto error; *temp = ((val & 0x1f) - 5) * 5; error: - mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x0); + _mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x0); mutex_unlock(&ps->phy_mutex); return ret; } @@ -633,17 +752,20 @@ static int mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset, u16 mask) int mv88e6xxx_phy_wait(struct dsa_switch *ds) { - return mv88e6xxx_wait(ds, REG_GLOBAL2, 0x18, 0x8000); + return mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_SMI_OP, + GLOBAL2_SMI_OP_BUSY); } int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds) { - return mv88e6xxx_wait(ds, REG_GLOBAL2, 0x14, 0x0800); + return mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP, + GLOBAL2_EEPROM_OP_LOAD); } int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds) { - return mv88e6xxx_wait(ds, REG_GLOBAL2, 0x14, 0x8000); + return mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP, + GLOBAL2_EEPROM_OP_BUSY); } /* Must be called with SMI lock held */ @@ -668,80 +790,87 @@ static int _mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset, u16 mask) /* Must be called with SMI lock held */ static int _mv88e6xxx_atu_wait(struct dsa_switch *ds) { - return _mv88e6xxx_wait(ds, REG_GLOBAL, 0x0b, ATU_BUSY); + return _mv88e6xxx_wait(ds, REG_GLOBAL, GLOBAL_ATU_OP, + GLOBAL_ATU_OP_BUSY); } -int mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int addr, int regnum) +/* Must be called with phy mutex held */ +static int _mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int addr, + int regnum) { int ret; - REG_WRITE(REG_GLOBAL2, 0x18, 0x9800 | (addr << 5) | regnum); + REG_WRITE(REG_GLOBAL2, GLOBAL2_SMI_OP, + GLOBAL2_SMI_OP_22_READ | (addr << 5) | regnum); ret = mv88e6xxx_phy_wait(ds); if (ret < 0) return ret; - return REG_READ(REG_GLOBAL2, 0x19); + return REG_READ(REG_GLOBAL2, GLOBAL2_SMI_DATA); } -int mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr, int regnum, - u16 val) +/* Must be called with phy mutex held */ +static int _mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr, + int regnum, u16 val) { - REG_WRITE(REG_GLOBAL2, 0x19, val); - REG_WRITE(REG_GLOBAL2, 0x18, 0x9400 | (addr << 5) | regnum); + REG_WRITE(REG_GLOBAL2, GLOBAL2_SMI_DATA, val); + REG_WRITE(REG_GLOBAL2, GLOBAL2_SMI_OP, + GLOBAL2_SMI_OP_22_WRITE | (addr << 5) | regnum); return mv88e6xxx_phy_wait(ds); } int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) { + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int reg; - reg = mv88e6xxx_phy_read_indirect(ds, port, 16); + mutex_lock(&ps->phy_mutex); + + reg = _mv88e6xxx_phy_read_indirect(ds, port, 16); if (reg < 0) - return -EOPNOTSUPP; + goto out; e->eee_enabled = !!(reg & 0x0200); e->tx_lpi_enabled = !!(reg & 0x0100); - reg = REG_READ(REG_PORT(port), 0); - e->eee_active = !!(reg & 0x0040); - - return 0; -} - -static int mv88e6xxx_eee_enable_set(struct dsa_switch *ds, int port, - bool eee_enabled, bool tx_lpi_enabled) -{ - int reg, nreg; - - reg = mv88e6xxx_phy_read_indirect(ds, port, 16); + reg = mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_STATUS); if (reg < 0) - return reg; - - nreg = reg & ~0x0300; - if (eee_enabled) - nreg |= 0x0200; - if (tx_lpi_enabled) - nreg |= 0x0100; + goto out; - if (nreg != reg) - return mv88e6xxx_phy_write_indirect(ds, port, 16, nreg); + e->eee_active = !!(reg & PORT_STATUS_EEE); + reg = 0; - return 0; +out: + mutex_unlock(&ps->phy_mutex); + return reg; } int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, struct phy_device *phydev, struct ethtool_eee *e) { + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int reg; int ret; - ret = mv88e6xxx_eee_enable_set(ds, port, e->eee_enabled, - e->tx_lpi_enabled); - if (ret) - return -EOPNOTSUPP; + mutex_lock(&ps->phy_mutex); - return 0; + ret = _mv88e6xxx_phy_read_indirect(ds, port, 16); + if (ret < 0) + goto out; + + reg = ret & ~0x0300; + if (e->eee_enabled) + reg |= 0x0200; + if (e->tx_lpi_enabled) + reg |= 0x0100; + + ret = _mv88e6xxx_phy_write_indirect(ds, port, 16, reg); +out: + mutex_unlock(&ps->phy_mutex); + + return ret; } static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, int fid, u16 cmd) @@ -752,7 +881,7 @@ static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, int fid, u16 cmd) if (ret < 0) return ret; - ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x0b, cmd); + ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_OP, cmd); if (ret < 0) return ret; @@ -767,7 +896,7 @@ static int _mv88e6xxx_flush_fid(struct dsa_switch *ds, int fid) if (ret < 0) return ret; - return _mv88e6xxx_atu_cmd(ds, fid, ATU_CMD_FLUSH_NONSTATIC_FID); + return _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_FLUSH_NON_STATIC_DB); } static int mv88e6xxx_set_port_state(struct dsa_switch *ds, int port, u8 state) @@ -778,23 +907,25 @@ static int mv88e6xxx_set_port_state(struct dsa_switch *ds, int port, u8 state) mutex_lock(&ps->smi_mutex); - reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), 0x04); + reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_CONTROL); if (reg < 0) goto abort; - oldstate = reg & PSTATE_MASK; + oldstate = reg & PORT_CONTROL_STATE_MASK; if (oldstate != state) { /* Flush forwarding database if we're moving a port * from Learning or Forwarding state to Disabled or * Blocking or Listening state. */ - if (oldstate >= PSTATE_LEARNING && state <= PSTATE_BLOCKING) { + if (oldstate >= PORT_CONTROL_STATE_LEARNING && + state <= PORT_CONTROL_STATE_BLOCKING) { ret = _mv88e6xxx_flush_fid(ds, ps->fid[port]); if (ret) goto abort; } - reg = (reg & ~PSTATE_MASK) | state; - ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), 0x04, reg); + reg = (reg & ~PORT_CONTROL_STATE_MASK) | state; + ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL, + reg); } abort: @@ -815,7 +946,7 @@ static int _mv88e6xxx_update_port_config(struct dsa_switch *ds, int port) reg |= (ps->bridge_mask[fid] | (1 << dsa_upstream_port(ds))) & ~(1 << port); - return _mv88e6xxx_reg_write(ds, REG_PORT(port), 0x06, reg); + return _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_BASE_VLAN, reg); } /* Must be called with smi lock held */ @@ -927,18 +1058,18 @@ int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state) switch (state) { case BR_STATE_DISABLED: - stp_state = PSTATE_DISABLED; + stp_state = PORT_CONTROL_STATE_DISABLED; break; case BR_STATE_BLOCKING: case BR_STATE_LISTENING: - stp_state = PSTATE_BLOCKING; + stp_state = PORT_CONTROL_STATE_BLOCKING; break; case BR_STATE_LEARNING: - stp_state = PSTATE_LEARNING; + stp_state = PORT_CONTROL_STATE_LEARNING; break; case BR_STATE_FORWARDING: default: - stp_state = PSTATE_FORWARDING; + stp_state = PORT_CONTROL_STATE_FORWARDING; break; } @@ -960,8 +1091,9 @@ static int __mv88e6xxx_write_addr(struct dsa_switch *ds, int i, ret; for (i = 0; i < 3; i++) { - ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x0d + i, - (addr[i * 2] << 8) | addr[i * 2 + 1]); + ret = _mv88e6xxx_reg_write( + ds, REG_GLOBAL, GLOBAL_ATU_MAC_01 + i, + (addr[i * 2] << 8) | addr[i * 2 + 1]); if (ret < 0) return ret; } @@ -974,7 +1106,8 @@ static int __mv88e6xxx_read_addr(struct dsa_switch *ds, unsigned char *addr) int i, ret; for (i = 0; i < 3; i++) { - ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x0d + i); + ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, + GLOBAL_ATU_MAC_01 + i); if (ret < 0) return ret; addr[i * 2] = ret >> 8; @@ -999,12 +1132,12 @@ static int __mv88e6xxx_port_fdb_cmd(struct dsa_switch *ds, int port, if (ret < 0) return ret; - ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x0c, + ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_DATA, (0x10 << port) | state); if (ret) return ret; - ret = _mv88e6xxx_atu_cmd(ds, fid, ATU_CMD_LOAD_FID); + ret = _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_LOAD_DB); return ret; } @@ -1013,7 +1146,8 @@ int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid) { int state = is_multicast_ether_addr(addr) ? - FDB_STATE_MC_STATIC : FDB_STATE_STATIC; + GLOBAL_ATU_DATA_STATE_MC_STATIC : + GLOBAL_ATU_DATA_STATE_UC_STATIC; struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int ret; @@ -1031,7 +1165,8 @@ int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, int ret; mutex_lock(&ps->smi_mutex); - ret = __mv88e6xxx_port_fdb_cmd(ds, port, addr, FDB_STATE_UNUSED); + ret = __mv88e6xxx_port_fdb_cmd(ds, port, addr, + GLOBAL_ATU_DATA_STATE_UNUSED); mutex_unlock(&ps->smi_mutex); return ret; @@ -1053,15 +1188,15 @@ static int __mv88e6xxx_port_getnext(struct dsa_switch *ds, int port, return ret; do { - ret = _mv88e6xxx_atu_cmd(ds, fid, ATU_CMD_GETNEXT_FID); + ret = _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_GET_NEXT_DB); if (ret < 0) return ret; - ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x0c); + ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_ATU_DATA); if (ret < 0) return ret; - state = ret & FDB_STATE_MASK; - if (state == FDB_STATE_UNUSED) + state = ret & GLOBAL_ATU_DATA_STATE_MASK; + if (state == GLOBAL_ATU_DATA_STATE_UNUSED) return -ENOENT; } while (!(((ret >> 4) & 0xff) & (1 << port))); @@ -1070,7 +1205,8 @@ static int __mv88e6xxx_port_getnext(struct dsa_switch *ds, int port, return ret; *is_static = state == (is_multicast_ether_addr(addr) ? - FDB_STATE_MC_STATIC : FDB_STATE_STATIC); + GLOBAL_ATU_DATA_STATE_MC_STATIC : + GLOBAL_ATU_DATA_STATE_UC_STATIC); return 0; } @@ -1115,7 +1251,8 @@ int mv88e6xxx_setup_port_common(struct dsa_switch *ds, int port) /* Port Control 1: disable trunking, disable sending * learning messages to this port. */ - ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), 0x05, 0x0000); + ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_DEFAULT_VLAN, + 0x0000); if (ret) goto abort; @@ -1152,7 +1289,7 @@ int mv88e6xxx_setup_common(struct dsa_switch *ds) mutex_init(&ps->stats_mutex); mutex_init(&ps->phy_mutex); - ps->id = REG_READ(REG_PORT(0), 0x03) & 0xfff0; + ps->id = REG_READ(REG_PORT(0), PORT_SWITCH_ID) & 0xfff0; ps->fid_mask = (1 << DSA_MAX_PORTS) - 1; @@ -1161,6 +1298,154 @@ int mv88e6xxx_setup_common(struct dsa_switch *ds) return 0; } +int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + u16 is_reset = (ppu_active ? 0x8800 : 0xc800); + unsigned long timeout; + int ret; + int i; + + /* Set all ports to the disabled state. */ + for (i = 0; i < ps->num_ports; i++) { + ret = REG_READ(REG_PORT(i), PORT_CONTROL); + REG_WRITE(REG_PORT(i), PORT_CONTROL, ret & 0xfffc); + } + + /* Wait for transmit queues to drain. */ + usleep_range(2000, 4000); + + /* Reset the switch. Keep the PPU active if requested. The PPU + * needs to be active to support indirect phy register access + * through global registers 0x18 and 0x19. + */ + if (ppu_active) + REG_WRITE(REG_GLOBAL, 0x04, 0xc000); + else + REG_WRITE(REG_GLOBAL, 0x04, 0xc400); + + /* Wait up to one second for reset to complete. */ + timeout = jiffies + 1 * HZ; + while (time_before(jiffies, timeout)) { + ret = REG_READ(REG_GLOBAL, 0x00); + if ((ret & is_reset) == is_reset) + break; + usleep_range(1000, 2000); + } + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + + return 0; +} + +int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int ret; + + mutex_lock(&ps->phy_mutex); + ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page); + if (ret < 0) + goto error; + ret = _mv88e6xxx_phy_read_indirect(ds, port, reg); +error: + _mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0); + mutex_unlock(&ps->phy_mutex); + return ret; +} + +int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page, + int reg, int val) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int ret; + + mutex_lock(&ps->phy_mutex); + ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page); + if (ret < 0) + goto error; + + ret = _mv88e6xxx_phy_write_indirect(ds, port, reg, val); +error: + _mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0); + mutex_unlock(&ps->phy_mutex); + return ret; +} + +static int mv88e6xxx_port_to_phy_addr(struct dsa_switch *ds, int port) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + + if (port >= 0 && port < ps->num_ports) + return port; + return -EINVAL; +} + +int +mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int addr = mv88e6xxx_port_to_phy_addr(ds, port); + int ret; + + if (addr < 0) + return addr; + + mutex_lock(&ps->phy_mutex); + ret = _mv88e6xxx_phy_read(ds, addr, regnum); + mutex_unlock(&ps->phy_mutex); + return ret; +} + +int +mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int addr = mv88e6xxx_port_to_phy_addr(ds, port); + int ret; + + if (addr < 0) + return addr; + + mutex_lock(&ps->phy_mutex); + ret = _mv88e6xxx_phy_write(ds, addr, regnum, val); + mutex_unlock(&ps->phy_mutex); + return ret; +} + +int +mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int port, int regnum) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int addr = mv88e6xxx_port_to_phy_addr(ds, port); + int ret; + + if (addr < 0) + return addr; + + mutex_lock(&ps->phy_mutex); + ret = _mv88e6xxx_phy_read_indirect(ds, addr, regnum); + mutex_unlock(&ps->phy_mutex); + return ret; +} + +int +mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int port, int regnum, + u16 val) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int addr = mv88e6xxx_port_to_phy_addr(ds, port); + int ret; + + if (addr < 0) + return addr; + + mutex_lock(&ps->phy_mutex); + ret = _mv88e6xxx_phy_write_indirect(ds, addr, regnum, val); + mutex_unlock(&ps->phy_mutex); + return ret; +} + static int __init mv88e6xxx_init(void) { #if IS_ENABLED(CONFIG_NET_DSA_MV88E6131) diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h index aaf239aba726..e045154f3364 100644 --- a/drivers/net/dsa/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx.h @@ -11,33 +11,199 @@ #ifndef __MV88E6XXX_H #define __MV88E6XXX_H -#define REG_PORT(p) (0x10 + (p)) -#define REG_GLOBAL 0x1b -#define REG_GLOBAL2 0x1c - -/* ATU commands */ - -#define ATU_BUSY 0x8000 - -#define ATU_CMD_LOAD_FID (ATU_BUSY | 0x3000) -#define ATU_CMD_GETNEXT_FID (ATU_BUSY | 0x4000) -#define ATU_CMD_FLUSH_NONSTATIC_FID (ATU_BUSY | 0x6000) - -/* port states */ +#define SMI_CMD 0x00 +#define SMI_CMD_BUSY BIT(15) +#define SMI_CMD_CLAUSE_22 BIT(12) +#define SMI_CMD_OP_22_WRITE ((1 << 10) | SMI_CMD_BUSY | SMI_CMD_CLAUSE_22) +#define SMI_CMD_OP_22_READ ((2 << 10) | SMI_CMD_BUSY | SMI_CMD_CLAUSE_22) +#define SMI_CMD_OP_45_WRITE_ADDR ((0 << 10) | SMI_CMD_BUSY) +#define SMI_CMD_OP_45_WRITE_DATA ((1 << 10) | SMI_CMD_BUSY) +#define SMI_CMD_OP_45_READ_DATA ((2 << 10) | SMI_CMD_BUSY) +#define SMI_CMD_OP_45_READ_DATA_INC ((3 << 10) | SMI_CMD_BUSY) +#define SMI_DATA 0x01 -#define PSTATE_MASK 0x03 -#define PSTATE_DISABLED 0x00 -#define PSTATE_BLOCKING 0x01 -#define PSTATE_LEARNING 0x02 -#define PSTATE_FORWARDING 0x03 - -/* FDB states */ +#define REG_PORT(p) (0x10 + (p)) +#define PORT_STATUS 0x00 +#define PORT_STATUS_PAUSE_EN BIT(15) +#define PORT_STATUS_MY_PAUSE BIT(14) +#define PORT_STATUS_HD_FLOW BIT(13) +#define PORT_STATUS_PHY_DETECT BIT(12) +#define PORT_STATUS_LINK BIT(11) +#define PORT_STATUS_DUPLEX BIT(10) +#define PORT_STATUS_SPEED_MASK 0x0300 +#define PORT_STATUS_SPEED_10 0x0000 +#define PORT_STATUS_SPEED_100 0x0100 +#define PORT_STATUS_SPEED_1000 0x0200 +#define PORT_STATUS_EEE BIT(6) /* 6352 */ +#define PORT_STATUS_AM_DIS BIT(6) /* 6165 */ +#define PORT_STATUS_MGMII BIT(6) /* 6185 */ +#define PORT_STATUS_TX_PAUSED BIT(5) +#define PORT_STATUS_FLOW_CTRL BIT(4) +#define PORT_PCS_CTRL 0x01 +#define PORT_SWITCH_ID 0x03 +#define PORT_SWITCH_ID_6085 0x04a0 +#define PORT_SWITCH_ID_6095 0x0950 +#define PORT_SWITCH_ID_6123 0x1210 +#define PORT_SWITCH_ID_6123_A1 0x1212 +#define PORT_SWITCH_ID_6123_A2 0x1213 +#define PORT_SWITCH_ID_6131 0x1060 +#define PORT_SWITCH_ID_6131_B2 0x1066 +#define PORT_SWITCH_ID_6152 0x1a40 +#define PORT_SWITCH_ID_6155 0x1a50 +#define PORT_SWITCH_ID_6161 0x1610 +#define PORT_SWITCH_ID_6161_A1 0x1612 +#define PORT_SWITCH_ID_6161_A2 0x1613 +#define PORT_SWITCH_ID_6165 0x1650 +#define PORT_SWITCH_ID_6165_A1 0x1652 +#define PORT_SWITCH_ID_6165_A2 0x1653 +#define PORT_SWITCH_ID_6171 0x1710 +#define PORT_SWITCH_ID_6172 0x1720 +#define PORT_SWITCH_ID_6176 0x1760 +#define PORT_SWITCH_ID_6182 0x1a60 +#define PORT_SWITCH_ID_6185 0x1a70 +#define PORT_SWITCH_ID_6352 0x3520 +#define PORT_SWITCH_ID_6352_A0 0x3521 +#define PORT_SWITCH_ID_6352_A1 0x3522 +#define PORT_CONTROL 0x04 +#define PORT_CONTROL_STATE_MASK 0x03 +#define PORT_CONTROL_STATE_DISABLED 0x00 +#define PORT_CONTROL_STATE_BLOCKING 0x01 +#define PORT_CONTROL_STATE_LEARNING 0x02 +#define PORT_CONTROL_STATE_FORWARDING 0x03 +#define PORT_CONTROL_1 0x05 +#define PORT_BASE_VLAN 0x06 +#define PORT_DEFAULT_VLAN 0x07 +#define PORT_CONTROL_2 0x08 +#define PORT_RATE_CONTROL 0x09 +#define PORT_RATE_CONTROL_2 0x0a +#define PORT_ASSOC_VECTOR 0x0b +#define PORT_IN_DISCARD_LO 0x10 +#define PORT_IN_DISCARD_HI 0x11 +#define PORT_IN_FILTERED 0x12 +#define PORT_OUT_FILTERED 0x13 +#define PORT_TAG_REGMAP_0123 0x19 +#define PORT_TAG_REGMAP_4567 0x1a -#define FDB_STATE_MASK 0x0f +#define REG_GLOBAL 0x1b +#define GLOBAL_STATUS 0x00 +#define GLOBAL_STATUS_PPU_STATE BIT(15) /* 6351 and 6171 */ +/* Two bits for 6165, 6185 etc */ +#define GLOBAL_STATUS_PPU_MASK (0x3 << 14) +#define GLOBAL_STATUS_PPU_DISABLED_RST (0x0 << 14) +#define GLOBAL_STATUS_PPU_INITIALIZING (0x1 << 14) +#define GLOBAL_STATUS_PPU_DISABLED (0x2 << 14) +#define GLOBAL_STATUS_PPU_POLLING (0x3 << 14) +#define GLOBAL_MAC_01 0x01 +#define GLOBAL_MAC_23 0x02 +#define GLOBAL_MAC_45 0x03 +#define GLOBAL_CONTROL 0x04 +#define GLOBAL_CONTROL_SW_RESET BIT(15) +#define GLOBAL_CONTROL_PPU_ENABLE BIT(14) +#define GLOBAL_CONTROL_DISCARD_EXCESS BIT(13) /* 6352 */ +#define GLOBAL_CONTROL_SCHED_PRIO BIT(11) /* 6152 */ +#define GLOBAL_CONTROL_MAX_FRAME_1632 BIT(10) /* 6152 */ +#define GLOBAL_CONTROL_RELOAD_EEPROM BIT(9) /* 6152 */ +#define GLOBAL_CONTROL_DEVICE_EN BIT(7) +#define GLOBAL_CONTROL_STATS_DONE_EN BIT(6) +#define GLOBAL_CONTROL_VTU_PROBLEM_EN BIT(5) +#define GLOBAL_CONTROL_VTU_DONE_EN BIT(4) +#define GLOBAL_CONTROL_ATU_PROBLEM_EN BIT(3) +#define GLOBAL_CONTROL_ATU_DONE_EN BIT(2) +#define GLOBAL_CONTROL_TCAM_EN BIT(1) +#define GLOBAL_CONTROL_EEPROM_DONE_EN BIT(0) +#define GLOBAL_VTU_OP 0x05 +#define GLOBAL_VTU_VID 0x06 +#define GLOBAL_VTU_DATA_0_3 0x07 +#define GLOBAL_VTU_DATA_4_7 0x08 +#define GLOBAL_VTU_DATA_8_11 0x09 +#define GLOBAL_ATU_CONTROL 0x0a +#define GLOBAL_ATU_OP 0x0b +#define GLOBAL_ATU_OP_BUSY BIT(15) +#define GLOBAL_ATU_OP_NOP (0 << 12) +#define GLOBAL_ATU_OP_FLUSH_ALL ((1 << 12) | GLOBAL_ATU_OP_BUSY) +#define GLOBAL_ATU_OP_FLUSH_NON_STATIC ((2 << 12) | GLOBAL_ATU_OP_BUSY) +#define GLOBAL_ATU_OP_LOAD_DB ((3 << 12) | GLOBAL_ATU_OP_BUSY) +#define GLOBAL_ATU_OP_GET_NEXT_DB ((4 << 12) | GLOBAL_ATU_OP_BUSY) +#define GLOBAL_ATU_OP_FLUSH_DB ((5 << 12) | GLOBAL_ATU_OP_BUSY) +#define GLOBAL_ATU_OP_FLUSH_NON_STATIC_DB ((6 << 12) | GLOBAL_ATU_OP_BUSY) +#define GLOBAL_ATU_OP_GET_CLR_VIOLATION ((7 << 12) | GLOBAL_ATU_OP_BUSY) +#define GLOBAL_ATU_DATA 0x0c +#define GLOBAL_ATU_DATA_STATE_MASK 0x0f +#define GLOBAL_ATU_DATA_STATE_UNUSED 0x00 +#define GLOBAL_ATU_DATA_STATE_UC_MGMT 0x0d +#define GLOBAL_ATU_DATA_STATE_UC_STATIC 0x0e +#define GLOBAL_ATU_DATA_STATE_UC_PRIO_OVER 0x0f +#define GLOBAL_ATU_DATA_STATE_MC_NONE_RATE 0x05 +#define GLOBAL_ATU_DATA_STATE_MC_STATIC 0x07 +#define GLOBAL_ATU_DATA_STATE_MC_MGMT 0x0e +#define GLOBAL_ATU_DATA_STATE_MC_PRIO_OVER 0x0f +#define GLOBAL_ATU_MAC_01 0x0d +#define GLOBAL_ATU_MAC_23 0x0e +#define GLOBAL_ATU_MAC_45 0x0f +#define GLOBAL_IP_PRI_0 0x10 +#define GLOBAL_IP_PRI_1 0x11 +#define GLOBAL_IP_PRI_2 0x12 +#define GLOBAL_IP_PRI_3 0x13 +#define GLOBAL_IP_PRI_4 0x14 +#define GLOBAL_IP_PRI_5 0x15 +#define GLOBAL_IP_PRI_6 0x16 +#define GLOBAL_IP_PRI_7 0x17 +#define GLOBAL_IEEE_PRI 0x18 +#define GLOBAL_CORE_TAG_TYPE 0x19 +#define GLOBAL_MONITOR_CONTROL 0x1a +#define GLOBAL_CONTROL_2 0x1c +#define GLOBAL_STATS_OP 0x1d +#define GLOBAL_STATS_OP_BUSY BIT(15) +#define GLOBAL_STATS_OP_NOP (0 << 12) +#define GLOBAL_STATS_OP_FLUSH_ALL ((1 << 12) | GLOBAL_STATS_OP_BUSY) +#define GLOBAL_STATS_OP_FLUSH_PORT ((2 << 12) | GLOBAL_STATS_OP_BUSY) +#define GLOBAL_STATS_OP_READ_CAPTURED ((4 << 12) | GLOBAL_STATS_OP_BUSY) +#define GLOBAL_STATS_OP_CAPTURE_PORT ((5 << 12) | GLOBAL_STATS_OP_BUSY) +#define GLOBAL_STATS_OP_HIST_RX ((1 << 10) | GLOBAL_STATS_OP_BUSY) +#define GLOBAL_STATS_OP_HIST_TX ((2 << 10) | GLOBAL_STATS_OP_BUSY) +#define GLOBAL_STATS_OP_HIST_RX_TX ((3 << 10) | GLOBAL_STATS_OP_BUSY) +#define GLOBAL_STATS_COUNTER_32 0x1e +#define GLOBAL_STATS_COUNTER_01 0x1f -#define FDB_STATE_UNUSED 0x00 -#define FDB_STATE_MC_STATIC 0x07 /* static multicast */ -#define FDB_STATE_STATIC 0x0e /* static unicast */ +#define REG_GLOBAL2 0x1c +#define GLOBAL2_INT_SOURCE 0x00 +#define GLOBAL2_INT_MASK 0x01 +#define GLOBAL2_MGMT_EN_2X 0x02 +#define GLOBAL2_MGMT_EN_0X 0x03 +#define GLOBAL2_FLOW_CONTROL 0x04 +#define GLOBAL2_SWITCH_MGMT 0x05 +#define GLOBAL2_DEVICE_MAPPING 0x06 +#define GLOBAL2_TRUNK_MASK 0x07 +#define GLOBAL2_TRUNK_MAPPING 0x08 +#define GLOBAL2_INGRESS_OP 0x09 +#define GLOBAL2_INGRESS_DATA 0x0a +#define GLOBAL2_PVT_ADDR 0x0b +#define GLOBAL2_PVT_DATA 0x0c +#define GLOBAL2_SWITCH_MAC 0x0d +#define GLOBAL2_SWITCH_MAC_BUSY BIT(15) +#define GLOBAL2_ATU_STATS 0x0e +#define GLOBAL2_PRIO_OVERRIDE 0x0f +#define GLOBAL2_EEPROM_OP 0x14 +#define GLOBAL2_EEPROM_OP_BUSY BIT(15) +#define GLOBAL2_EEPROM_OP_LOAD BIT(11) +#define GLOBAL2_EEPROM_DATA 0x15 +#define GLOBAL2_PTP_AVB_OP 0x16 +#define GLOBAL2_PTP_AVB_DATA 0x17 +#define GLOBAL2_SMI_OP 0x18 +#define GLOBAL2_SMI_OP_BUSY BIT(15) +#define GLOBAL2_SMI_OP_CLAUSE_22 BIT(12) +#define GLOBAL2_SMI_OP_22_WRITE ((1 << 10) | GLOBAL2_SMI_OP_BUSY | \ + GLOBAL2_SMI_OP_CLAUSE_22) +#define GLOBAL2_SMI_OP_22_READ ((2 << 10) | GLOBAL2_SMI_OP_BUSY | \ + GLOBAL2_SMI_OP_CLAUSE_22) +#define GLOBAL2_SMI_OP_45_WRITE_ADDR ((0 << 10) | GLOBAL2_SMI_OP_BUSY) +#define GLOBAL2_SMI_OP_45_WRITE_DATA ((1 << 10) | GLOBAL2_SMI_OP_BUSY) +#define GLOBAL2_SMI_OP_45_READ_DATA ((2 << 10) | GLOBAL2_SMI_OP_BUSY) +#define GLOBAL2_SMI_DATA 0x19 +#define GLOBAL2_SCRATCH_MISC 0x1a +#define GLOBAL2_WDOG_CONTROL 0x1b +#define GLOBAL2_QOS_WEIGHT 0x1c +#define GLOBAL2_MISC 0x1d struct mv88e6xxx_priv_state { /* When using multi-chip addressing, this mutex protects @@ -73,6 +239,7 @@ struct mv88e6xxx_priv_state { struct mutex eeprom_mutex; int id; /* switch product id */ + int num_ports; /* number of switch ports */ /* hw bridging */ @@ -92,6 +259,7 @@ struct mv88e6xxx_hw_stat { int reg; }; +int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active); int mv88e6xxx_setup_port_common(struct dsa_switch *ds, int port); int mv88e6xxx_setup_common(struct dsa_switch *ds); int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg); @@ -102,19 +270,21 @@ int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val); int mv88e6xxx_config_prio(struct dsa_switch *ds); int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr); int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr); -int mv88e6xxx_phy_read(struct dsa_switch *ds, int addr, int regnum); -int mv88e6xxx_phy_write(struct dsa_switch *ds, int addr, int regnum, u16 val); +int mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum); +int mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val); +int mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int port, int regnum); +int mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int port, int regnum, + u16 val); void mv88e6xxx_ppu_state_init(struct dsa_switch *ds); int mv88e6xxx_phy_read_ppu(struct dsa_switch *ds, int addr, int regnum); int mv88e6xxx_phy_write_ppu(struct dsa_switch *ds, int addr, int regnum, u16 val); void mv88e6xxx_poll_link(struct dsa_switch *ds); -void mv88e6xxx_get_strings(struct dsa_switch *ds, - int nr_stats, struct mv88e6xxx_hw_stat *stats, - int port, uint8_t *data); -void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, - int nr_stats, struct mv88e6xxx_hw_stat *stats, - int port, uint64_t *data); +void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data); +void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port, + uint64_t *data); +int mv88e6xxx_get_sset_count(struct dsa_switch *ds); +int mv88e6xxx_get_sset_count_basic(struct dsa_switch *ds); int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port); void mv88e6xxx_get_regs(struct dsa_switch *ds, int port, struct ethtool_regs *regs, void *_p); @@ -137,7 +307,9 @@ int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid); int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port, unsigned char *addr, bool *is_static); - +int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg); +int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page, + int reg, int val); extern struct dsa_switch_driver mv88e6131_switch_driver; extern struct dsa_switch_driver mv88e6123_61_65_switch_driver; extern struct dsa_switch_driver mv88e6352_switch_driver; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index 756053c028be..4085c4b31047 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -1811,7 +1811,7 @@ struct bnx2x { int stats_state; /* used for synchronization of concurrent threads statistics handling */ - spinlock_t stats_lock; + struct mutex stats_lock; /* used by dmae command loader */ struct dmae_command stats_dmae; @@ -1935,8 +1935,6 @@ struct bnx2x { int fp_array_size; u32 dump_preset_idx; - bool stats_started; - struct semaphore stats_sema; u8 phys_port_id[ETH_ALEN]; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 177cb0e722e7..b9f85fccb419 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -129,8 +129,8 @@ struct bnx2x_mac_vals { u32 xmac_val; u32 emac_addr; u32 emac_val; - u32 umac_addr; - u32 umac_val; + u32 umac_addr[2]; + u32 umac_val[2]; u32 bmac_addr; u32 bmac_val[2]; }; @@ -7866,6 +7866,20 @@ int bnx2x_init_hw_func_cnic(struct bnx2x *bp) return 0; } +/* previous driver DMAE transaction may have occurred when pre-boot stage ended + * and boot began, or when kdump kernel was loaded. Either case would invalidate + * the addresses of the transaction, resulting in was-error bit set in the pci + * causing all hw-to-host pcie transactions to timeout. If this happened we want + * to clear the interrupt which detected this from the pglueb and the was done + * bit + */ +static void bnx2x_clean_pglue_errors(struct bnx2x *bp) +{ + if (!CHIP_IS_E1x(bp)) + REG_WR(bp, PGLUE_B_REG_WAS_ERROR_PF_7_0_CLR, + 1 << BP_ABS_FUNC(bp)); +} + static int bnx2x_init_hw_func(struct bnx2x *bp) { int port = BP_PORT(bp); @@ -7958,8 +7972,7 @@ static int bnx2x_init_hw_func(struct bnx2x *bp) bnx2x_init_block(bp, BLOCK_PGLUE_B, init_phase); - if (!CHIP_IS_E1x(bp)) - REG_WR(bp, PGLUE_B_REG_WAS_ERROR_PF_7_0_CLR, func); + bnx2x_clean_pglue_errors(bp); bnx2x_init_block(bp, BLOCK_ATC, init_phase); bnx2x_init_block(bp, BLOCK_DMAE, init_phase); @@ -10141,6 +10154,25 @@ static u32 bnx2x_get_pretend_reg(struct bnx2x *bp) return base + (BP_ABS_FUNC(bp)) * stride; } +static bool bnx2x_prev_unload_close_umac(struct bnx2x *bp, + u8 port, u32 reset_reg, + struct bnx2x_mac_vals *vals) +{ + u32 mask = MISC_REGISTERS_RESET_REG_2_UMAC0 << port; + u32 base_addr; + + if (!(mask & reset_reg)) + return false; + + BNX2X_DEV_INFO("Disable umac Rx %02x\n", port); + base_addr = port ? GRCBASE_UMAC1 : GRCBASE_UMAC0; + vals->umac_addr[port] = base_addr + UMAC_REG_COMMAND_CONFIG; + vals->umac_val[port] = REG_RD(bp, vals->umac_addr[port]); + REG_WR(bp, vals->umac_addr[port], 0); + + return true; +} + static void bnx2x_prev_unload_close_mac(struct bnx2x *bp, struct bnx2x_mac_vals *vals) { @@ -10149,10 +10181,7 @@ static void bnx2x_prev_unload_close_mac(struct bnx2x *bp, u8 port = BP_PORT(bp); /* reset addresses as they also mark which values were changed */ - vals->bmac_addr = 0; - vals->umac_addr = 0; - vals->xmac_addr = 0; - vals->emac_addr = 0; + memset(vals, 0, sizeof(*vals)); reset_reg = REG_RD(bp, MISC_REG_RESET_REG_2); @@ -10201,15 +10230,11 @@ static void bnx2x_prev_unload_close_mac(struct bnx2x *bp, REG_WR(bp, vals->xmac_addr, 0); mac_stopped = true; } - mask = MISC_REGISTERS_RESET_REG_2_UMAC0 << port; - if (mask & reset_reg) { - BNX2X_DEV_INFO("Disable umac Rx\n"); - base_addr = BP_PORT(bp) ? GRCBASE_UMAC1 : GRCBASE_UMAC0; - vals->umac_addr = base_addr + UMAC_REG_COMMAND_CONFIG; - vals->umac_val = REG_RD(bp, vals->umac_addr); - REG_WR(bp, vals->umac_addr, 0); - mac_stopped = true; - } + + mac_stopped |= bnx2x_prev_unload_close_umac(bp, 0, + reset_reg, vals); + mac_stopped |= bnx2x_prev_unload_close_umac(bp, 1, + reset_reg, vals); } if (mac_stopped) @@ -10505,8 +10530,11 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp) /* Close the MAC Rx to prevent BRB from filling up */ bnx2x_prev_unload_close_mac(bp, &mac_vals); - /* close LLH filters towards the BRB */ + /* close LLH filters for both ports towards the BRB */ bnx2x_set_rx_filter(&bp->link_params, 0); + bp->link_params.port ^= 1; + bnx2x_set_rx_filter(&bp->link_params, 0); + bp->link_params.port ^= 1; /* Check if the UNDI driver was previously loaded */ if (bnx2x_prev_is_after_undi(bp)) { @@ -10553,8 +10581,10 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp) if (mac_vals.xmac_addr) REG_WR(bp, mac_vals.xmac_addr, mac_vals.xmac_val); - if (mac_vals.umac_addr) - REG_WR(bp, mac_vals.umac_addr, mac_vals.umac_val); + if (mac_vals.umac_addr[0]) + REG_WR(bp, mac_vals.umac_addr[0], mac_vals.umac_val[0]); + if (mac_vals.umac_addr[1]) + REG_WR(bp, mac_vals.umac_addr[1], mac_vals.umac_val[1]); if (mac_vals.emac_addr) REG_WR(bp, mac_vals.emac_addr, mac_vals.emac_val); if (mac_vals.bmac_addr) { @@ -10571,26 +10601,6 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp) return bnx2x_prev_mcp_done(bp); } -/* previous driver DMAE transaction may have occurred when pre-boot stage ended - * and boot began, or when kdump kernel was loaded. Either case would invalidate - * the addresses of the transaction, resulting in was-error bit set in the pci - * causing all hw-to-host pcie transactions to timeout. If this happened we want - * to clear the interrupt which detected this from the pglueb and the was done - * bit - */ -static void bnx2x_prev_interrupted_dmae(struct bnx2x *bp) -{ - if (!CHIP_IS_E1x(bp)) { - u32 val = REG_RD(bp, PGLUE_B_REG_PGLUE_B_INT_STS); - if (val & PGLUE_B_PGLUE_B_INT_STS_REG_WAS_ERROR_ATTN) { - DP(BNX2X_MSG_SP, - "'was error' bit was found to be set in pglueb upon startup. Clearing\n"); - REG_WR(bp, PGLUE_B_REG_WAS_ERROR_PF_7_0_CLR, - 1 << BP_FUNC(bp)); - } - } -} - static int bnx2x_prev_unload(struct bnx2x *bp) { int time_counter = 10; @@ -10600,7 +10610,7 @@ static int bnx2x_prev_unload(struct bnx2x *bp) /* clear hw from errors which may have resulted from an interrupted * dmae transaction. */ - bnx2x_prev_interrupted_dmae(bp); + bnx2x_clean_pglue_errors(bp); /* Release previously held locks */ hw_lock_reg = (BP_FUNC(bp) <= 5) ? @@ -12044,9 +12054,8 @@ static int bnx2x_init_bp(struct bnx2x *bp) mutex_init(&bp->port.phy_mutex); mutex_init(&bp->fw_mb_mutex); mutex_init(&bp->drv_info_mutex); + mutex_init(&bp->stats_lock); bp->drv_info_mng_owner = false; - spin_lock_init(&bp->stats_lock); - sema_init(&bp->stats_sema, 1); INIT_DELAYED_WORK(&bp->sp_task, bnx2x_sp_task); INIT_DELAYED_WORK(&bp->sp_rtnl_task, bnx2x_sp_rtnl_task); @@ -13673,9 +13682,9 @@ static int bnx2x_eeh_nic_unload(struct bnx2x *bp) cancel_delayed_work_sync(&bp->sp_task); cancel_delayed_work_sync(&bp->period_task); - spin_lock_bh(&bp->stats_lock); + mutex_lock(&bp->stats_lock); bp->stats_state = STATS_STATE_DISABLED; - spin_unlock_bh(&bp->stats_lock); + mutex_unlock(&bp->stats_lock); bnx2x_save_statistics(bp); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c index 8638d6c97caa..d95f7b4e19e1 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c @@ -2238,7 +2238,9 @@ int bnx2x_vf_close(struct bnx2x *bp, struct bnx2x_virtf *vf) cookie.vf = vf; cookie.state = VF_ACQUIRED; - bnx2x_stats_safe_exec(bp, bnx2x_set_vf_state, &cookie); + rc = bnx2x_stats_safe_exec(bp, bnx2x_set_vf_state, &cookie); + if (rc) + goto op_err; } DP(BNX2X_MSG_IOV, "set state to acquired\n"); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c index 612cafb5df53..266b055c2360 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c @@ -123,36 +123,28 @@ static void bnx2x_dp_stats(struct bnx2x *bp) */ static void bnx2x_storm_stats_post(struct bnx2x *bp) { - if (!bp->stats_pending) { - int rc; + int rc; - spin_lock_bh(&bp->stats_lock); - - if (bp->stats_pending) { - spin_unlock_bh(&bp->stats_lock); - return; - } - - bp->fw_stats_req->hdr.drv_stats_counter = - cpu_to_le16(bp->stats_counter++); + if (bp->stats_pending) + return; - DP(BNX2X_MSG_STATS, "Sending statistics ramrod %d\n", - le16_to_cpu(bp->fw_stats_req->hdr.drv_stats_counter)); + bp->fw_stats_req->hdr.drv_stats_counter = + cpu_to_le16(bp->stats_counter++); - /* adjust the ramrod to include VF queues statistics */ - bnx2x_iov_adjust_stats_req(bp); - bnx2x_dp_stats(bp); + DP(BNX2X_MSG_STATS, "Sending statistics ramrod %d\n", + le16_to_cpu(bp->fw_stats_req->hdr.drv_stats_counter)); - /* send FW stats ramrod */ - rc = bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_STAT_QUERY, 0, - U64_HI(bp->fw_stats_req_mapping), - U64_LO(bp->fw_stats_req_mapping), - NONE_CONNECTION_TYPE); - if (rc == 0) - bp->stats_pending = 1; + /* adjust the ramrod to include VF queues statistics */ + bnx2x_iov_adjust_stats_req(bp); + bnx2x_dp_stats(bp); - spin_unlock_bh(&bp->stats_lock); - } + /* send FW stats ramrod */ + rc = bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_STAT_QUERY, 0, + U64_HI(bp->fw_stats_req_mapping), + U64_LO(bp->fw_stats_req_mapping), + NONE_CONNECTION_TYPE); + if (rc == 0) + bp->stats_pending = 1; } static void bnx2x_hw_stats_post(struct bnx2x *bp) @@ -221,7 +213,7 @@ static void bnx2x_stats_comp(struct bnx2x *bp) */ /* should be called under stats_sema */ -static void __bnx2x_stats_pmf_update(struct bnx2x *bp) +static void bnx2x_stats_pmf_update(struct bnx2x *bp) { struct dmae_command *dmae; u32 opcode; @@ -519,7 +511,7 @@ static void bnx2x_func_stats_init(struct bnx2x *bp) } /* should be called under stats_sema */ -static void __bnx2x_stats_start(struct bnx2x *bp) +static void bnx2x_stats_start(struct bnx2x *bp) { if (IS_PF(bp)) { if (bp->port.pmf) @@ -531,34 +523,13 @@ static void __bnx2x_stats_start(struct bnx2x *bp) bnx2x_hw_stats_post(bp); bnx2x_storm_stats_post(bp); } - - bp->stats_started = true; -} - -static void bnx2x_stats_start(struct bnx2x *bp) -{ - if (down_timeout(&bp->stats_sema, HZ/10)) - BNX2X_ERR("Unable to acquire stats lock\n"); - __bnx2x_stats_start(bp); - up(&bp->stats_sema); } static void bnx2x_stats_pmf_start(struct bnx2x *bp) { - if (down_timeout(&bp->stats_sema, HZ/10)) - BNX2X_ERR("Unable to acquire stats lock\n"); bnx2x_stats_comp(bp); - __bnx2x_stats_pmf_update(bp); - __bnx2x_stats_start(bp); - up(&bp->stats_sema); -} - -static void bnx2x_stats_pmf_update(struct bnx2x *bp) -{ - if (down_timeout(&bp->stats_sema, HZ/10)) - BNX2X_ERR("Unable to acquire stats lock\n"); - __bnx2x_stats_pmf_update(bp); - up(&bp->stats_sema); + bnx2x_stats_pmf_update(bp); + bnx2x_stats_start(bp); } static void bnx2x_stats_restart(struct bnx2x *bp) @@ -568,11 +539,9 @@ static void bnx2x_stats_restart(struct bnx2x *bp) */ if (IS_VF(bp)) return; - if (down_timeout(&bp->stats_sema, HZ/10)) - BNX2X_ERR("Unable to acquire stats lock\n"); + bnx2x_stats_comp(bp); - __bnx2x_stats_start(bp); - up(&bp->stats_sema); + bnx2x_stats_start(bp); } static void bnx2x_bmac_stats_update(struct bnx2x *bp) @@ -1246,18 +1215,12 @@ static void bnx2x_stats_update(struct bnx2x *bp) { u32 *stats_comp = bnx2x_sp(bp, stats_comp); - /* we run update from timer context, so give up - * if somebody is in the middle of transition - */ - if (down_trylock(&bp->stats_sema)) + if (bnx2x_edebug_stats_stopped(bp)) return; - if (bnx2x_edebug_stats_stopped(bp) || !bp->stats_started) - goto out; - if (IS_PF(bp)) { if (*stats_comp != DMAE_COMP_VAL) - goto out; + return; if (bp->port.pmf) bnx2x_hw_stats_update(bp); @@ -1267,7 +1230,7 @@ static void bnx2x_stats_update(struct bnx2x *bp) BNX2X_ERR("storm stats were not updated for 3 times\n"); bnx2x_panic(); } - goto out; + return; } } else { /* vf doesn't collect HW statistics, and doesn't get completions @@ -1281,7 +1244,7 @@ static void bnx2x_stats_update(struct bnx2x *bp) /* vf is done */ if (IS_VF(bp)) - goto out; + return; if (netif_msg_timer(bp)) { struct bnx2x_eth_stats *estats = &bp->eth_stats; @@ -1292,9 +1255,6 @@ static void bnx2x_stats_update(struct bnx2x *bp) bnx2x_hw_stats_post(bp); bnx2x_storm_stats_post(bp); - -out: - up(&bp->stats_sema); } static void bnx2x_port_stats_stop(struct bnx2x *bp) @@ -1358,12 +1318,7 @@ static void bnx2x_port_stats_stop(struct bnx2x *bp) static void bnx2x_stats_stop(struct bnx2x *bp) { - int update = 0; - - if (down_timeout(&bp->stats_sema, HZ/10)) - BNX2X_ERR("Unable to acquire stats lock\n"); - - bp->stats_started = false; + bool update = false; bnx2x_stats_comp(bp); @@ -1381,8 +1336,6 @@ static void bnx2x_stats_stop(struct bnx2x *bp) bnx2x_hw_stats_post(bp); bnx2x_stats_comp(bp); } - - up(&bp->stats_sema); } static void bnx2x_stats_do_nothing(struct bnx2x *bp) @@ -1410,18 +1363,28 @@ static const struct { void bnx2x_stats_handle(struct bnx2x *bp, enum bnx2x_stats_event event) { - enum bnx2x_stats_state state; - void (*action)(struct bnx2x *bp); + enum bnx2x_stats_state state = bp->stats_state; + if (unlikely(bp->panic)) return; - spin_lock_bh(&bp->stats_lock); - state = bp->stats_state; + /* Statistics update run from timer context, and we don't want to stop + * that context in case someone is in the middle of a transition. + * For other events, wait a bit until lock is taken. + */ + if (!mutex_trylock(&bp->stats_lock)) { + if (event == STATS_EVENT_UPDATE) + return; + + DP(BNX2X_MSG_STATS, + "Unlikely stats' lock contention [event %d]\n", event); + mutex_lock(&bp->stats_lock); + } + + bnx2x_stats_stm[state][event].action(bp); bp->stats_state = bnx2x_stats_stm[state][event].next_state; - action = bnx2x_stats_stm[state][event].action; - spin_unlock_bh(&bp->stats_lock); - action(bp); + mutex_unlock(&bp->stats_lock); if ((event != STATS_EVENT_UPDATE) || netif_msg_timer(bp)) DP(BNX2X_MSG_STATS, "state %d -> event %d -> state %d\n", @@ -1998,13 +1961,34 @@ void bnx2x_afex_collect_stats(struct bnx2x *bp, void *void_afex_stats, } } -void bnx2x_stats_safe_exec(struct bnx2x *bp, - void (func_to_exec)(void *cookie), - void *cookie){ - if (down_timeout(&bp->stats_sema, HZ/10)) - BNX2X_ERR("Unable to acquire stats lock\n"); +int bnx2x_stats_safe_exec(struct bnx2x *bp, + void (func_to_exec)(void *cookie), + void *cookie) +{ + int cnt = 10, rc = 0; + + /* Wait for statistics to end [while blocking further requests], + * then run supplied function 'safely'. + */ + mutex_lock(&bp->stats_lock); + bnx2x_stats_comp(bp); + while (bp->stats_pending && cnt--) + if (bnx2x_storm_stats_update(bp)) + usleep_range(1000, 2000); + if (bp->stats_pending) { + BNX2X_ERR("Failed to wait for stats pending to clear [possibly FW is stuck]\n"); + rc = -EBUSY; + goto out; + } + func_to_exec(cookie); - __bnx2x_stats_start(bp); - up(&bp->stats_sema); + +out: + /* No need to restart statistics - if they're enabled, the timer + * will restart the statistics. + */ + mutex_unlock(&bp->stats_lock); + + return rc; } diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h index 2beceaefdeea..965539a9dabe 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h @@ -539,9 +539,9 @@ struct bnx2x; void bnx2x_memset_stats(struct bnx2x *bp); void bnx2x_stats_init(struct bnx2x *bp); void bnx2x_stats_handle(struct bnx2x *bp, enum bnx2x_stats_event event); -void bnx2x_stats_safe_exec(struct bnx2x *bp, - void (func_to_exec)(void *cookie), - void *cookie); +int bnx2x_stats_safe_exec(struct bnx2x *bp, + void (func_to_exec)(void *cookie), + void *cookie); /** * bnx2x_save_statistics - save statistics when unloading. diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index f7855a61e7ad..6043734ea613 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -1734,6 +1734,9 @@ static int init_umac(struct bcmgenet_priv *priv) } else if (priv->ext_phy) { int0_enable |= UMAC_IRQ_LINK_EVENT; } else if (priv->phy_interface == PHY_INTERFACE_MODE_MOCA) { + if (priv->hw_params->flags & GENET_HAS_MOCA_LINK_DET) + int0_enable |= UMAC_IRQ_LINK_EVENT; + reg = bcmgenet_bp_mc_get(priv); reg |= BIT(priv->hw_params->bp_in_en_shift); @@ -2926,7 +2929,8 @@ static struct bcmgenet_hw_params bcmgenet_hw_params[] = { .rdma_offset = 0x10000, .tdma_offset = 0x11000, .words_per_bd = 2, - .flags = GENET_HAS_EXT | GENET_HAS_MDIO_INTR, + .flags = GENET_HAS_EXT | GENET_HAS_MDIO_INTR | + GENET_HAS_MOCA_LINK_DET, }, [GENET_V4] = { .tx_queues = 4, @@ -2944,7 +2948,8 @@ static struct bcmgenet_hw_params bcmgenet_hw_params[] = { .rdma_offset = 0x2000, .tdma_offset = 0x4000, .words_per_bd = 3, - .flags = GENET_HAS_40BITS | GENET_HAS_EXT | GENET_HAS_MDIO_INTR, + .flags = GENET_HAS_40BITS | GENET_HAS_EXT | + GENET_HAS_MDIO_INTR | GENET_HAS_MOCA_LINK_DET, }, }; diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h index ddaa40cb0f21..6f2887a5e0be 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h @@ -508,6 +508,7 @@ enum bcmgenet_version { #define GENET_HAS_40BITS (1 << 0) #define GENET_HAS_EXT (1 << 1) #define GENET_HAS_MDIO_INTR (1 << 2) +#define GENET_HAS_MOCA_LINK_DET (1 << 3) /* BCMGENET hardware parameters, keep this structure nicely aligned * since it is going to be used in hot paths diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c index 6d3b66a103cc..e7651b3c6c57 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmmii.c +++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c @@ -462,6 +462,15 @@ static int bcmgenet_mii_of_init(struct bcmgenet_priv *priv) return 0; } +static int bcmgenet_fixed_phy_link_update(struct net_device *dev, + struct fixed_phy_status *status) +{ + if (dev && dev->phydev && status) + status->link = dev->phydev->link; + + return 0; +} + static int bcmgenet_mii_pd_init(struct bcmgenet_priv *priv) { struct device *kdev = &priv->pdev->dev; @@ -513,6 +522,13 @@ static int bcmgenet_mii_pd_init(struct bcmgenet_priv *priv) dev_err(kdev, "failed to register fixed PHY device\n"); return -ENODEV; } + + if (priv->hw_params->flags & GENET_HAS_MOCA_LINK_DET) { + ret = fixed_phy_set_link_update( + phydev, bcmgenet_fixed_phy_link_update); + if (!ret) + phydev->link = 0; + } } priv->phydev = phydev; diff --git a/drivers/net/ethernet/chelsio/cxgb/cxgb2.c b/drivers/net/ethernet/chelsio/cxgb/cxgb2.c index 186566bfdbc8..f5f1b0b51ebd 100644 --- a/drivers/net/ethernet/chelsio/cxgb/cxgb2.c +++ b/drivers/net/ethernet/chelsio/cxgb/cxgb2.c @@ -354,7 +354,7 @@ static void set_msglevel(struct net_device *dev, u32 val) adapter->msg_enable = val; } -static char stats_strings[][ETH_GSTRING_LEN] = { +static const char stats_strings[][ETH_GSTRING_LEN] = { "TxOctetsOK", "TxOctetsBad", "TxUnicastFramesOK", diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c index db76f7040455..b96e4bfcac41 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c @@ -1537,7 +1537,7 @@ static void set_msglevel(struct net_device *dev, u32 val) adapter->msg_enable = val; } -static char stats_strings[][ETH_GSTRING_LEN] = { +static const char stats_strings[][ETH_GSTRING_LEN] = { "TxOctetsOK ", "TxFramesOK ", "TxMulticastFramesOK", diff --git a/drivers/net/ethernet/chelsio/cxgb4/Makefile b/drivers/net/ethernet/chelsio/cxgb4/Makefile index 07d9b68a4da2..ace0ab98d0f1 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/Makefile +++ b/drivers/net/ethernet/chelsio/cxgb4/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_CHELSIO_T4) += cxgb4.o -cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o clip_tbl.o +cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o clip_tbl.o cxgb4_ethtool.o cxgb4-$(CONFIG_CHELSIO_T4_DCB) += cxgb4_dcb.o cxgb4-$(CONFIG_CHELSIO_T4_FCOE) += cxgb4_fcoe.o cxgb4-$(CONFIG_DEBUG_FS) += cxgb4_debugfs.o diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index bf46ca935e2a..524d11098c56 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -60,6 +60,11 @@ enum { }; enum { + T4_REGMAP_SIZE = (160 * 1024), + T5_REGMAP_SIZE = (332 * 1024), +}; + +enum { MEM_EDC0, MEM_EDC1, MEM_MC, @@ -374,10 +379,19 @@ enum { }; enum { + MAX_TXQ_ENTRIES = 16384, + MAX_CTRL_TXQ_ENTRIES = 1024, + MAX_RSPQ_ENTRIES = 16384, + MAX_RX_BUFFERS = 16384, + MIN_TXQ_ENTRIES = 32, + MIN_CTRL_TXQ_ENTRIES = 32, + MIN_RSPQ_ENTRIES = 128, + MIN_FL_ENTRIES = 16 +}; + +enum { INGQ_EXTRAS = 2, /* firmware event queue and */ /* forwarded interrupts */ - MAX_EGRQ = MAX_ETH_QSETS*2 + MAX_OFLD_QSETS*2 - + MAX_CTRL_QUEUES + MAX_RDMA_QUEUES + MAX_ISCSI_QUEUES, MAX_INGQ = MAX_ETH_QSETS + MAX_OFLD_QSETS + MAX_RDMA_QUEUES + MAX_RDMA_CIQS + MAX_ISCSI_QUEUES + INGQ_EXTRAS, }; @@ -623,11 +637,13 @@ struct sge { unsigned int idma_qid[2]; /* SGE IDMA Hung Ingress Queue ID */ unsigned int egr_start; + unsigned int egr_sz; unsigned int ingr_start; - void *egr_map[MAX_EGRQ]; /* qid->queue egress queue map */ - struct sge_rspq *ingr_map[MAX_INGQ]; /* qid->queue ingress queue map */ - DECLARE_BITMAP(starving_fl, MAX_EGRQ); - DECLARE_BITMAP(txq_maperr, MAX_EGRQ); + unsigned int ingr_sz; + void **egr_map; /* qid->queue egress queue map */ + struct sge_rspq **ingr_map; /* qid->queue ingress queue map */ + unsigned long *starving_fl; + unsigned long *txq_maperr; struct timer_list rx_timer; /* refills starving FLs */ struct timer_list tx_timer; /* checks Tx queues */ }; @@ -1000,6 +1016,30 @@ static inline bool cxgb_poll_busy_polling(struct sge_rspq *q) } #endif /* CONFIG_NET_RX_BUSY_POLL */ +/* Return a version number to identify the type of adapter. The scheme is: + * - bits 0..9: chip version + * - bits 10..15: chip revision + * - bits 16..23: register dump version + */ +static inline unsigned int mk_adap_vers(struct adapter *ap) +{ + return CHELSIO_CHIP_VERSION(ap->params.chip) | + (CHELSIO_CHIP_RELEASE(ap->params.chip) << 10) | (1 << 16); +} + +/* Return a queue's interrupt hold-off time in us. 0 means no timer. */ +static inline unsigned int qtimer_val(const struct adapter *adap, + const struct sge_rspq *q) +{ + unsigned int idx = q->intr_params >> 1; + + return idx < SGE_NTIMERS ? adap->sge.timer_val[idx] : 0; +} + +/* driver version & name used for ethtool_drvinfo */ +extern char cxgb4_driver_name[]; +extern const char cxgb4_driver_version[]; + void t4_os_portmod_changed(const struct adapter *adap, int port_id); void t4_os_link_changed(struct adapter *adap, int port_id, int link_stat); @@ -1029,6 +1069,10 @@ int t4_sge_init(struct adapter *adap); void t4_sge_start(struct adapter *adap); void t4_sge_stop(struct adapter *adap); int cxgb_busy_poll(struct napi_struct *napi); +int cxgb4_set_rspq_intr_params(struct sge_rspq *q, unsigned int us, + unsigned int cnt); +void cxgb4_set_ethtool_ops(struct net_device *netdev); +int cxgb4_write_rss(const struct port_info *pi, const u16 *queues); extern int dbfifo_int_thresh; #define for_each_port(adapter, iter) \ @@ -1117,6 +1161,9 @@ static inline int t4_memory_write(struct adapter *adap, int mtype, u32 addr, return t4_memory_rw(adap, 0, mtype, addr, len, buf, 0); } +unsigned int t4_get_regs_len(struct adapter *adapter); +void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size); + int t4_seeprom_wp(struct adapter *adapter, bool enable); int get_vpd_params(struct adapter *adapter, struct vpd_params *p); int t4_read_flash(struct adapter *adapter, unsigned int addr, @@ -1143,6 +1190,8 @@ int cxgb4_t4_bar2_sge_qregs(struct adapter *adapter, unsigned int qtimer_val(const struct adapter *adap, const struct sge_rspq *q); + +int t4_init_devlog_params(struct adapter *adapter); int t4_init_sge_params(struct adapter *adapter); int t4_init_tp_params(struct adapter *adap); int t4_filter_field_shift(const struct adapter *adap, int filter_sel); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c index 0918c16bb154..f0285bcbe598 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c @@ -670,9 +670,13 @@ static int cctrl_tbl_show(struct seq_file *seq, void *v) "0.9375" }; int i; - u16 incr[NMTUS][NCCTRL_WIN]; + u16 (*incr)[NCCTRL_WIN]; struct adapter *adap = seq->private; + incr = kmalloc(sizeof(*incr) * NMTUS, GFP_KERNEL); + if (!incr) + return -ENOMEM; + t4_read_cong_tbl(adap, incr); for (i = 0; i < NCCTRL_WIN; ++i) { @@ -685,6 +689,8 @@ static int cctrl_tbl_show(struct seq_file *seq, void *v) adap->params.a_wnd[i], dec_fac[adap->params.b_wnd[i]]); } + + kfree(incr); return 0; } diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c new file mode 100644 index 000000000000..10d82b51d7ef --- /dev/null +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c @@ -0,0 +1,915 @@ +/* + * Copyright (C) 2013-2015 Chelsio Communications. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + */ + +#include <linux/firmware.h> +#include <linux/mdio.h> + +#include "cxgb4.h" +#include "t4_regs.h" +#include "t4fw_api.h" + +#define EEPROM_MAGIC 0x38E2F10C + +static u32 get_msglevel(struct net_device *dev) +{ + return netdev2adap(dev)->msg_enable; +} + +static void set_msglevel(struct net_device *dev, u32 val) +{ + netdev2adap(dev)->msg_enable = val; +} + +static const char stats_strings[][ETH_GSTRING_LEN] = { + "TxOctetsOK ", + "TxFramesOK ", + "TxBroadcastFrames ", + "TxMulticastFrames ", + "TxUnicastFrames ", + "TxErrorFrames ", + + "TxFrames64 ", + "TxFrames65To127 ", + "TxFrames128To255 ", + "TxFrames256To511 ", + "TxFrames512To1023 ", + "TxFrames1024To1518 ", + "TxFrames1519ToMax ", + + "TxFramesDropped ", + "TxPauseFrames ", + "TxPPP0Frames ", + "TxPPP1Frames ", + "TxPPP2Frames ", + "TxPPP3Frames ", + "TxPPP4Frames ", + "TxPPP5Frames ", + "TxPPP6Frames ", + "TxPPP7Frames ", + + "RxOctetsOK ", + "RxFramesOK ", + "RxBroadcastFrames ", + "RxMulticastFrames ", + "RxUnicastFrames ", + + "RxFramesTooLong ", + "RxJabberErrors ", + "RxFCSErrors ", + "RxLengthErrors ", + "RxSymbolErrors ", + "RxRuntFrames ", + + "RxFrames64 ", + "RxFrames65To127 ", + "RxFrames128To255 ", + "RxFrames256To511 ", + "RxFrames512To1023 ", + "RxFrames1024To1518 ", + "RxFrames1519ToMax ", + + "RxPauseFrames ", + "RxPPP0Frames ", + "RxPPP1Frames ", + "RxPPP2Frames ", + "RxPPP3Frames ", + "RxPPP4Frames ", + "RxPPP5Frames ", + "RxPPP6Frames ", + "RxPPP7Frames ", + + "RxBG0FramesDropped ", + "RxBG1FramesDropped ", + "RxBG2FramesDropped ", + "RxBG3FramesDropped ", + "RxBG0FramesTrunc ", + "RxBG1FramesTrunc ", + "RxBG2FramesTrunc ", + "RxBG3FramesTrunc ", + + "TSO ", + "TxCsumOffload ", + "RxCsumGood ", + "VLANextractions ", + "VLANinsertions ", + "GROpackets ", + "GROmerged ", + "WriteCoalSuccess ", + "WriteCoalFail ", +}; + +static int get_sset_count(struct net_device *dev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return ARRAY_SIZE(stats_strings); + default: + return -EOPNOTSUPP; + } +} + +static int get_regs_len(struct net_device *dev) +{ + struct adapter *adap = netdev2adap(dev); + + return t4_get_regs_len(adap); +} + +static int get_eeprom_len(struct net_device *dev) +{ + return EEPROMSIZE; +} + +static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + struct adapter *adapter = netdev2adap(dev); + u32 exprom_vers; + + strlcpy(info->driver, cxgb4_driver_name, sizeof(info->driver)); + strlcpy(info->version, cxgb4_driver_version, + sizeof(info->version)); + strlcpy(info->bus_info, pci_name(adapter->pdev), + sizeof(info->bus_info)); + + if (adapter->params.fw_vers) + snprintf(info->fw_version, sizeof(info->fw_version), + "%u.%u.%u.%u, TP %u.%u.%u.%u", + FW_HDR_FW_VER_MAJOR_G(adapter->params.fw_vers), + FW_HDR_FW_VER_MINOR_G(adapter->params.fw_vers), + FW_HDR_FW_VER_MICRO_G(adapter->params.fw_vers), + FW_HDR_FW_VER_BUILD_G(adapter->params.fw_vers), + FW_HDR_FW_VER_MAJOR_G(adapter->params.tp_vers), + FW_HDR_FW_VER_MINOR_G(adapter->params.tp_vers), + FW_HDR_FW_VER_MICRO_G(adapter->params.tp_vers), + FW_HDR_FW_VER_BUILD_G(adapter->params.tp_vers)); + + if (!t4_get_exprom_version(adapter, &exprom_vers)) + snprintf(info->erom_version, sizeof(info->erom_version), + "%u.%u.%u.%u", + FW_HDR_FW_VER_MAJOR_G(exprom_vers), + FW_HDR_FW_VER_MINOR_G(exprom_vers), + FW_HDR_FW_VER_MICRO_G(exprom_vers), + FW_HDR_FW_VER_BUILD_G(exprom_vers)); +} + +static void get_strings(struct net_device *dev, u32 stringset, u8 *data) +{ + if (stringset == ETH_SS_STATS) + memcpy(data, stats_strings, sizeof(stats_strings)); +} + +/* port stats maintained per queue of the port. They should be in the same + * order as in stats_strings above. + */ +struct queue_port_stats { + u64 tso; + u64 tx_csum; + u64 rx_csum; + u64 vlan_ex; + u64 vlan_ins; + u64 gro_pkts; + u64 gro_merged; +}; + +static void collect_sge_port_stats(const struct adapter *adap, + const struct port_info *p, + struct queue_port_stats *s) +{ + int i; + const struct sge_eth_txq *tx = &adap->sge.ethtxq[p->first_qset]; + const struct sge_eth_rxq *rx = &adap->sge.ethrxq[p->first_qset]; + + memset(s, 0, sizeof(*s)); + for (i = 0; i < p->nqsets; i++, rx++, tx++) { + s->tso += tx->tso; + s->tx_csum += tx->tx_cso; + s->rx_csum += rx->stats.rx_cso; + s->vlan_ex += rx->stats.vlan_ex; + s->vlan_ins += tx->vlan_ins; + s->gro_pkts += rx->stats.lro_pkts; + s->gro_merged += rx->stats.lro_merged; + } +} + +static void get_stats(struct net_device *dev, struct ethtool_stats *stats, + u64 *data) +{ + struct port_info *pi = netdev_priv(dev); + struct adapter *adapter = pi->adapter; + u32 val1, val2; + + t4_get_port_stats(adapter, pi->tx_chan, (struct port_stats *)data); + + data += sizeof(struct port_stats) / sizeof(u64); + collect_sge_port_stats(adapter, pi, (struct queue_port_stats *)data); + data += sizeof(struct queue_port_stats) / sizeof(u64); + if (!is_t4(adapter->params.chip)) { + t4_write_reg(adapter, SGE_STAT_CFG_A, STATSOURCE_T5_V(7)); + val1 = t4_read_reg(adapter, SGE_STAT_TOTAL_A); + val2 = t4_read_reg(adapter, SGE_STAT_MATCH_A); + *data = val1 - val2; + data++; + *data = val2; + data++; + } else { + memset(data, 0, 2 * sizeof(u64)); + *data += 2; + } +} + +static void get_regs(struct net_device *dev, struct ethtool_regs *regs, + void *buf) +{ + struct adapter *adap = netdev2adap(dev); + size_t buf_size; + + buf_size = t4_get_regs_len(adap); + regs->version = mk_adap_vers(adap); + t4_get_regs(adap, buf, buf_size); +} + +static int restart_autoneg(struct net_device *dev) +{ + struct port_info *p = netdev_priv(dev); + + if (!netif_running(dev)) + return -EAGAIN; + if (p->link_cfg.autoneg != AUTONEG_ENABLE) + return -EINVAL; + t4_restart_aneg(p->adapter, p->adapter->fn, p->tx_chan); + return 0; +} + +static int identify_port(struct net_device *dev, + enum ethtool_phys_id_state state) +{ + unsigned int val; + struct adapter *adap = netdev2adap(dev); + + if (state == ETHTOOL_ID_ACTIVE) + val = 0xffff; + else if (state == ETHTOOL_ID_INACTIVE) + val = 0; + else + return -EINVAL; + + return t4_identify_port(adap, adap->fn, netdev2pinfo(dev)->viid, val); +} + +static unsigned int from_fw_linkcaps(enum fw_port_type type, unsigned int caps) +{ + unsigned int v = 0; + + if (type == FW_PORT_TYPE_BT_SGMII || type == FW_PORT_TYPE_BT_XFI || + type == FW_PORT_TYPE_BT_XAUI) { + v |= SUPPORTED_TP; + if (caps & FW_PORT_CAP_SPEED_100M) + v |= SUPPORTED_100baseT_Full; + if (caps & FW_PORT_CAP_SPEED_1G) + v |= SUPPORTED_1000baseT_Full; + if (caps & FW_PORT_CAP_SPEED_10G) + v |= SUPPORTED_10000baseT_Full; + } else if (type == FW_PORT_TYPE_KX4 || type == FW_PORT_TYPE_KX) { + v |= SUPPORTED_Backplane; + if (caps & FW_PORT_CAP_SPEED_1G) + v |= SUPPORTED_1000baseKX_Full; + if (caps & FW_PORT_CAP_SPEED_10G) + v |= SUPPORTED_10000baseKX4_Full; + } else if (type == FW_PORT_TYPE_KR) { + v |= SUPPORTED_Backplane | SUPPORTED_10000baseKR_Full; + } else if (type == FW_PORT_TYPE_BP_AP) { + v |= SUPPORTED_Backplane | SUPPORTED_10000baseR_FEC | + SUPPORTED_10000baseKR_Full | SUPPORTED_1000baseKX_Full; + } else if (type == FW_PORT_TYPE_BP4_AP) { + v |= SUPPORTED_Backplane | SUPPORTED_10000baseR_FEC | + SUPPORTED_10000baseKR_Full | SUPPORTED_1000baseKX_Full | + SUPPORTED_10000baseKX4_Full; + } else if (type == FW_PORT_TYPE_FIBER_XFI || + type == FW_PORT_TYPE_FIBER_XAUI || + type == FW_PORT_TYPE_SFP || + type == FW_PORT_TYPE_QSFP_10G || + type == FW_PORT_TYPE_QSA) { + v |= SUPPORTED_FIBRE; + if (caps & FW_PORT_CAP_SPEED_1G) + v |= SUPPORTED_1000baseT_Full; + if (caps & FW_PORT_CAP_SPEED_10G) + v |= SUPPORTED_10000baseT_Full; + } else if (type == FW_PORT_TYPE_BP40_BA || + type == FW_PORT_TYPE_QSFP) { + v |= SUPPORTED_40000baseSR4_Full; + v |= SUPPORTED_FIBRE; + } + + if (caps & FW_PORT_CAP_ANEG) + v |= SUPPORTED_Autoneg; + return v; +} + +static unsigned int to_fw_linkcaps(unsigned int caps) +{ + unsigned int v = 0; + + if (caps & ADVERTISED_100baseT_Full) + v |= FW_PORT_CAP_SPEED_100M; + if (caps & ADVERTISED_1000baseT_Full) + v |= FW_PORT_CAP_SPEED_1G; + if (caps & ADVERTISED_10000baseT_Full) + v |= FW_PORT_CAP_SPEED_10G; + if (caps & ADVERTISED_40000baseSR4_Full) + v |= FW_PORT_CAP_SPEED_40G; + return v; +} + +static int get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + const struct port_info *p = netdev_priv(dev); + + if (p->port_type == FW_PORT_TYPE_BT_SGMII || + p->port_type == FW_PORT_TYPE_BT_XFI || + p->port_type == FW_PORT_TYPE_BT_XAUI) { + cmd->port = PORT_TP; + } else if (p->port_type == FW_PORT_TYPE_FIBER_XFI || + p->port_type == FW_PORT_TYPE_FIBER_XAUI) { + cmd->port = PORT_FIBRE; + } else if (p->port_type == FW_PORT_TYPE_SFP || + p->port_type == FW_PORT_TYPE_QSFP_10G || + p->port_type == FW_PORT_TYPE_QSA || + p->port_type == FW_PORT_TYPE_QSFP) { + if (p->mod_type == FW_PORT_MOD_TYPE_LR || + p->mod_type == FW_PORT_MOD_TYPE_SR || + p->mod_type == FW_PORT_MOD_TYPE_ER || + p->mod_type == FW_PORT_MOD_TYPE_LRM) + cmd->port = PORT_FIBRE; + else if (p->mod_type == FW_PORT_MOD_TYPE_TWINAX_PASSIVE || + p->mod_type == FW_PORT_MOD_TYPE_TWINAX_ACTIVE) + cmd->port = PORT_DA; + else + cmd->port = PORT_OTHER; + } else { + cmd->port = PORT_OTHER; + } + + if (p->mdio_addr >= 0) { + cmd->phy_address = p->mdio_addr; + cmd->transceiver = XCVR_EXTERNAL; + cmd->mdio_support = p->port_type == FW_PORT_TYPE_BT_SGMII ? + MDIO_SUPPORTS_C22 : MDIO_SUPPORTS_C45; + } else { + cmd->phy_address = 0; /* not really, but no better option */ + cmd->transceiver = XCVR_INTERNAL; + cmd->mdio_support = 0; + } + + cmd->supported = from_fw_linkcaps(p->port_type, p->link_cfg.supported); + cmd->advertising = from_fw_linkcaps(p->port_type, + p->link_cfg.advertising); + ethtool_cmd_speed_set(cmd, + netif_carrier_ok(dev) ? p->link_cfg.speed : 0); + cmd->duplex = DUPLEX_FULL; + cmd->autoneg = p->link_cfg.autoneg; + cmd->maxtxpkt = 0; + cmd->maxrxpkt = 0; + return 0; +} + +static unsigned int speed_to_caps(int speed) +{ + if (speed == 100) + return FW_PORT_CAP_SPEED_100M; + if (speed == 1000) + return FW_PORT_CAP_SPEED_1G; + if (speed == 10000) + return FW_PORT_CAP_SPEED_10G; + if (speed == 40000) + return FW_PORT_CAP_SPEED_40G; + return 0; +} + +static int set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + unsigned int cap; + struct port_info *p = netdev_priv(dev); + struct link_config *lc = &p->link_cfg; + u32 speed = ethtool_cmd_speed(cmd); + + if (cmd->duplex != DUPLEX_FULL) /* only full-duplex supported */ + return -EINVAL; + + if (!(lc->supported & FW_PORT_CAP_ANEG)) { + /* PHY offers a single speed. See if that's what's + * being requested. + */ + if (cmd->autoneg == AUTONEG_DISABLE && + (lc->supported & speed_to_caps(speed))) + return 0; + return -EINVAL; + } + + if (cmd->autoneg == AUTONEG_DISABLE) { + cap = speed_to_caps(speed); + + if (!(lc->supported & cap) || + (speed == 1000) || + (speed == 10000) || + (speed == 40000)) + return -EINVAL; + lc->requested_speed = cap; + lc->advertising = 0; + } else { + cap = to_fw_linkcaps(cmd->advertising); + if (!(lc->supported & cap)) + return -EINVAL; + lc->requested_speed = 0; + lc->advertising = cap | FW_PORT_CAP_ANEG; + } + lc->autoneg = cmd->autoneg; + + if (netif_running(dev)) + return t4_link_start(p->adapter, p->adapter->fn, p->tx_chan, + lc); + return 0; +} + +static void get_pauseparam(struct net_device *dev, + struct ethtool_pauseparam *epause) +{ + struct port_info *p = netdev_priv(dev); + + epause->autoneg = (p->link_cfg.requested_fc & PAUSE_AUTONEG) != 0; + epause->rx_pause = (p->link_cfg.fc & PAUSE_RX) != 0; + epause->tx_pause = (p->link_cfg.fc & PAUSE_TX) != 0; +} + +static int set_pauseparam(struct net_device *dev, + struct ethtool_pauseparam *epause) +{ + struct port_info *p = netdev_priv(dev); + struct link_config *lc = &p->link_cfg; + + if (epause->autoneg == AUTONEG_DISABLE) + lc->requested_fc = 0; + else if (lc->supported & FW_PORT_CAP_ANEG) + lc->requested_fc = PAUSE_AUTONEG; + else + return -EINVAL; + + if (epause->rx_pause) + lc->requested_fc |= PAUSE_RX; + if (epause->tx_pause) + lc->requested_fc |= PAUSE_TX; + if (netif_running(dev)) + return t4_link_start(p->adapter, p->adapter->fn, p->tx_chan, + lc); + return 0; +} + +static void get_sge_param(struct net_device *dev, struct ethtool_ringparam *e) +{ + const struct port_info *pi = netdev_priv(dev); + const struct sge *s = &pi->adapter->sge; + + e->rx_max_pending = MAX_RX_BUFFERS; + e->rx_mini_max_pending = MAX_RSPQ_ENTRIES; + e->rx_jumbo_max_pending = 0; + e->tx_max_pending = MAX_TXQ_ENTRIES; + + e->rx_pending = s->ethrxq[pi->first_qset].fl.size - 8; + e->rx_mini_pending = s->ethrxq[pi->first_qset].rspq.size; + e->rx_jumbo_pending = 0; + e->tx_pending = s->ethtxq[pi->first_qset].q.size; +} + +static int set_sge_param(struct net_device *dev, struct ethtool_ringparam *e) +{ + int i; + const struct port_info *pi = netdev_priv(dev); + struct adapter *adapter = pi->adapter; + struct sge *s = &adapter->sge; + + if (e->rx_pending > MAX_RX_BUFFERS || e->rx_jumbo_pending || + e->tx_pending > MAX_TXQ_ENTRIES || + e->rx_mini_pending > MAX_RSPQ_ENTRIES || + e->rx_mini_pending < MIN_RSPQ_ENTRIES || + e->rx_pending < MIN_FL_ENTRIES || e->tx_pending < MIN_TXQ_ENTRIES) + return -EINVAL; + + if (adapter->flags & FULL_INIT_DONE) + return -EBUSY; + + for (i = 0; i < pi->nqsets; ++i) { + s->ethtxq[pi->first_qset + i].q.size = e->tx_pending; + s->ethrxq[pi->first_qset + i].fl.size = e->rx_pending + 8; + s->ethrxq[pi->first_qset + i].rspq.size = e->rx_mini_pending; + } + return 0; +} + +/** + * set_rx_intr_params - set a net devices's RX interrupt holdoff paramete! + * @dev: the network device + * @us: the hold-off time in us, or 0 to disable timer + * @cnt: the hold-off packet count, or 0 to disable counter + * + * Set the RX interrupt hold-off parameters for a network device. + */ +static int set_rx_intr_params(struct net_device *dev, + unsigned int us, unsigned int cnt) +{ + int i, err; + struct port_info *pi = netdev_priv(dev); + struct adapter *adap = pi->adapter; + struct sge_eth_rxq *q = &adap->sge.ethrxq[pi->first_qset]; + + for (i = 0; i < pi->nqsets; i++, q++) { + err = cxgb4_set_rspq_intr_params(&q->rspq, us, cnt); + if (err) + return err; + } + return 0; +} + +static int set_adaptive_rx_setting(struct net_device *dev, int adaptive_rx) +{ + int i; + struct port_info *pi = netdev_priv(dev); + struct adapter *adap = pi->adapter; + struct sge_eth_rxq *q = &adap->sge.ethrxq[pi->first_qset]; + + for (i = 0; i < pi->nqsets; i++, q++) + q->rspq.adaptive_rx = adaptive_rx; + + return 0; +} + +static int get_adaptive_rx_setting(struct net_device *dev) +{ + struct port_info *pi = netdev_priv(dev); + struct adapter *adap = pi->adapter; + struct sge_eth_rxq *q = &adap->sge.ethrxq[pi->first_qset]; + + return q->rspq.adaptive_rx; +} + +static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c) +{ + set_adaptive_rx_setting(dev, c->use_adaptive_rx_coalesce); + return set_rx_intr_params(dev, c->rx_coalesce_usecs, + c->rx_max_coalesced_frames); +} + +static int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c) +{ + const struct port_info *pi = netdev_priv(dev); + const struct adapter *adap = pi->adapter; + const struct sge_rspq *rq = &adap->sge.ethrxq[pi->first_qset].rspq; + + c->rx_coalesce_usecs = qtimer_val(adap, rq); + c->rx_max_coalesced_frames = (rq->intr_params & QINTR_CNT_EN) ? + adap->sge.counter_val[rq->pktcnt_idx] : 0; + c->use_adaptive_rx_coalesce = get_adaptive_rx_setting(dev); + return 0; +} + +/** + * eeprom_ptov - translate a physical EEPROM address to virtual + * @phys_addr: the physical EEPROM address + * @fn: the PCI function number + * @sz: size of function-specific area + * + * Translate a physical EEPROM address to virtual. The first 1K is + * accessed through virtual addresses starting at 31K, the rest is + * accessed through virtual addresses starting at 0. + * + * The mapping is as follows: + * [0..1K) -> [31K..32K) + * [1K..1K+A) -> [31K-A..31K) + * [1K+A..ES) -> [0..ES-A-1K) + * + * where A = @fn * @sz, and ES = EEPROM size. + */ +static int eeprom_ptov(unsigned int phys_addr, unsigned int fn, unsigned int sz) +{ + fn *= sz; + if (phys_addr < 1024) + return phys_addr + (31 << 10); + if (phys_addr < 1024 + fn) + return 31744 - fn + phys_addr - 1024; + if (phys_addr < EEPROMSIZE) + return phys_addr - 1024 - fn; + return -EINVAL; +} + +/* The next two routines implement eeprom read/write from physical addresses. + */ +static int eeprom_rd_phys(struct adapter *adap, unsigned int phys_addr, u32 *v) +{ + int vaddr = eeprom_ptov(phys_addr, adap->fn, EEPROMPFSIZE); + + if (vaddr >= 0) + vaddr = pci_read_vpd(adap->pdev, vaddr, sizeof(u32), v); + return vaddr < 0 ? vaddr : 0; +} + +static int eeprom_wr_phys(struct adapter *adap, unsigned int phys_addr, u32 v) +{ + int vaddr = eeprom_ptov(phys_addr, adap->fn, EEPROMPFSIZE); + + if (vaddr >= 0) + vaddr = pci_write_vpd(adap->pdev, vaddr, sizeof(u32), &v); + return vaddr < 0 ? vaddr : 0; +} + +#define EEPROM_MAGIC 0x38E2F10C + +static int get_eeprom(struct net_device *dev, struct ethtool_eeprom *e, + u8 *data) +{ + int i, err = 0; + struct adapter *adapter = netdev2adap(dev); + u8 *buf = kmalloc(EEPROMSIZE, GFP_KERNEL); + + if (!buf) + return -ENOMEM; + + e->magic = EEPROM_MAGIC; + for (i = e->offset & ~3; !err && i < e->offset + e->len; i += 4) + err = eeprom_rd_phys(adapter, i, (u32 *)&buf[i]); + + if (!err) + memcpy(data, buf + e->offset, e->len); + kfree(buf); + return err; +} + +static int set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, + u8 *data) +{ + u8 *buf; + int err = 0; + u32 aligned_offset, aligned_len, *p; + struct adapter *adapter = netdev2adap(dev); + + if (eeprom->magic != EEPROM_MAGIC) + return -EINVAL; + + aligned_offset = eeprom->offset & ~3; + aligned_len = (eeprom->len + (eeprom->offset & 3) + 3) & ~3; + + if (adapter->fn > 0) { + u32 start = 1024 + adapter->fn * EEPROMPFSIZE; + + if (aligned_offset < start || + aligned_offset + aligned_len > start + EEPROMPFSIZE) + return -EPERM; + } + + if (aligned_offset != eeprom->offset || aligned_len != eeprom->len) { + /* RMW possibly needed for first or last words. + */ + buf = kmalloc(aligned_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + err = eeprom_rd_phys(adapter, aligned_offset, (u32 *)buf); + if (!err && aligned_len > 4) + err = eeprom_rd_phys(adapter, + aligned_offset + aligned_len - 4, + (u32 *)&buf[aligned_len - 4]); + if (err) + goto out; + memcpy(buf + (eeprom->offset & 3), data, eeprom->len); + } else { + buf = data; + } + + err = t4_seeprom_wp(adapter, false); + if (err) + goto out; + + for (p = (u32 *)buf; !err && aligned_len; aligned_len -= 4, p++) { + err = eeprom_wr_phys(adapter, aligned_offset, *p); + aligned_offset += 4; + } + + if (!err) + err = t4_seeprom_wp(adapter, true); +out: + if (buf != data) + kfree(buf); + return err; +} + +static int set_flash(struct net_device *netdev, struct ethtool_flash *ef) +{ + int ret; + const struct firmware *fw; + struct adapter *adap = netdev2adap(netdev); + unsigned int mbox = PCIE_FW_MASTER_M + 1; + + ef->data[sizeof(ef->data) - 1] = '\0'; + ret = request_firmware(&fw, ef->data, adap->pdev_dev); + if (ret < 0) + return ret; + + /* If the adapter has been fully initialized then we'll go ahead and + * try to get the firmware's cooperation in upgrading to the new + * firmware image otherwise we'll try to do the entire job from the + * host ... and we always "force" the operation in this path. + */ + if (adap->flags & FULL_INIT_DONE) + mbox = adap->mbox; + + ret = t4_fw_upgrade(adap, mbox, fw->data, fw->size, 1); + release_firmware(fw); + if (!ret) + dev_info(adap->pdev_dev, + "loaded firmware %s, reload cxgb4 driver\n", ef->data); + return ret; +} + +#define WOL_SUPPORTED (WAKE_BCAST | WAKE_MAGIC) +#define BCAST_CRC 0xa0ccc1a6 + +static void get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + wol->supported = WAKE_BCAST | WAKE_MAGIC; + wol->wolopts = netdev2adap(dev)->wol; + memset(&wol->sopass, 0, sizeof(wol->sopass)); +} + +static int set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + int err = 0; + struct port_info *pi = netdev_priv(dev); + + if (wol->wolopts & ~WOL_SUPPORTED) + return -EINVAL; + t4_wol_magic_enable(pi->adapter, pi->tx_chan, + (wol->wolopts & WAKE_MAGIC) ? dev->dev_addr : NULL); + if (wol->wolopts & WAKE_BCAST) { + err = t4_wol_pat_enable(pi->adapter, pi->tx_chan, 0xfe, ~0ULL, + ~0ULL, 0, false); + if (!err) + err = t4_wol_pat_enable(pi->adapter, pi->tx_chan, 1, + ~6ULL, ~0ULL, BCAST_CRC, true); + } else { + t4_wol_pat_enable(pi->adapter, pi->tx_chan, 0, 0, 0, 0, false); + } + return err; +} + +static u32 get_rss_table_size(struct net_device *dev) +{ + const struct port_info *pi = netdev_priv(dev); + + return pi->rss_size; +} + +static int get_rss_table(struct net_device *dev, u32 *p, u8 *key, u8 *hfunc) +{ + const struct port_info *pi = netdev_priv(dev); + unsigned int n = pi->rss_size; + + if (hfunc) + *hfunc = ETH_RSS_HASH_TOP; + if (!p) + return 0; + while (n--) + p[n] = pi->rss[n]; + return 0; +} + +static int set_rss_table(struct net_device *dev, const u32 *p, const u8 *key, + const u8 hfunc) +{ + unsigned int i; + struct port_info *pi = netdev_priv(dev); + + /* We require at least one supported parameter to be changed and no + * change in any of the unsupported parameters + */ + if (key || + (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)) + return -EOPNOTSUPP; + if (!p) + return 0; + + for (i = 0; i < pi->rss_size; i++) + pi->rss[i] = p[i]; + if (pi->adapter->flags & FULL_INIT_DONE) + return cxgb4_write_rss(pi, pi->rss); + return 0; +} + +static int get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info, + u32 *rules) +{ + const struct port_info *pi = netdev_priv(dev); + + switch (info->cmd) { + case ETHTOOL_GRXFH: { + unsigned int v = pi->rss_mode; + + info->data = 0; + switch (info->flow_type) { + case TCP_V4_FLOW: + if (v & FW_RSS_VI_CONFIG_CMD_IP4FOURTUPEN_F) + info->data = RXH_IP_SRC | RXH_IP_DST | + RXH_L4_B_0_1 | RXH_L4_B_2_3; + else if (v & FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN_F) + info->data = RXH_IP_SRC | RXH_IP_DST; + break; + case UDP_V4_FLOW: + if ((v & FW_RSS_VI_CONFIG_CMD_IP4FOURTUPEN_F) && + (v & FW_RSS_VI_CONFIG_CMD_UDPEN_F)) + info->data = RXH_IP_SRC | RXH_IP_DST | + RXH_L4_B_0_1 | RXH_L4_B_2_3; + else if (v & FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN_F) + info->data = RXH_IP_SRC | RXH_IP_DST; + break; + case SCTP_V4_FLOW: + case AH_ESP_V4_FLOW: + case IPV4_FLOW: + if (v & FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN_F) + info->data = RXH_IP_SRC | RXH_IP_DST; + break; + case TCP_V6_FLOW: + if (v & FW_RSS_VI_CONFIG_CMD_IP6FOURTUPEN_F) + info->data = RXH_IP_SRC | RXH_IP_DST | + RXH_L4_B_0_1 | RXH_L4_B_2_3; + else if (v & FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN_F) + info->data = RXH_IP_SRC | RXH_IP_DST; + break; + case UDP_V6_FLOW: + if ((v & FW_RSS_VI_CONFIG_CMD_IP6FOURTUPEN_F) && + (v & FW_RSS_VI_CONFIG_CMD_UDPEN_F)) + info->data = RXH_IP_SRC | RXH_IP_DST | + RXH_L4_B_0_1 | RXH_L4_B_2_3; + else if (v & FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN_F) + info->data = RXH_IP_SRC | RXH_IP_DST; + break; + case SCTP_V6_FLOW: + case AH_ESP_V6_FLOW: + case IPV6_FLOW: + if (v & FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN_F) + info->data = RXH_IP_SRC | RXH_IP_DST; + break; + } + return 0; + } + case ETHTOOL_GRXRINGS: + info->data = pi->nqsets; + return 0; + } + return -EOPNOTSUPP; +} + +static const struct ethtool_ops cxgb_ethtool_ops = { + .get_settings = get_settings, + .set_settings = set_settings, + .get_drvinfo = get_drvinfo, + .get_msglevel = get_msglevel, + .set_msglevel = set_msglevel, + .get_ringparam = get_sge_param, + .set_ringparam = set_sge_param, + .get_coalesce = get_coalesce, + .set_coalesce = set_coalesce, + .get_eeprom_len = get_eeprom_len, + .get_eeprom = get_eeprom, + .set_eeprom = set_eeprom, + .get_pauseparam = get_pauseparam, + .set_pauseparam = set_pauseparam, + .get_link = ethtool_op_get_link, + .get_strings = get_strings, + .set_phys_id = identify_port, + .nway_reset = restart_autoneg, + .get_sset_count = get_sset_count, + .get_ethtool_stats = get_stats, + .get_regs_len = get_regs_len, + .get_regs = get_regs, + .get_wol = get_wol, + .set_wol = set_wol, + .get_rxnfc = get_rxnfc, + .get_rxfh_indir_size = get_rss_table_size, + .get_rxfh = get_rss_table, + .set_rxfh = set_rss_table, + .flash_device = set_flash, +}; + +void cxgb4_set_ethtool_ops(struct net_device *netdev) +{ + netdev->ethtool_ops = &cxgb_ethtool_ops; +} diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index e40e283ff36c..24e10ea3d5ef 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -76,23 +76,15 @@ #include "clip_tbl.h" #include "l2t.h" +char cxgb4_driver_name[] = KBUILD_MODNAME; + #ifdef DRV_VERSION #undef DRV_VERSION #endif #define DRV_VERSION "2.0.0-ko" +const char cxgb4_driver_version[] = DRV_VERSION; #define DRV_DESC "Chelsio T4/T5 Network Driver" -enum { - MAX_TXQ_ENTRIES = 16384, - MAX_CTRL_TXQ_ENTRIES = 1024, - MAX_RSPQ_ENTRIES = 16384, - MAX_RX_BUFFERS = 16384, - MIN_TXQ_ENTRIES = 32, - MIN_CTRL_TXQ_ENTRIES = 32, - MIN_RSPQ_ENTRIES = 128, - MIN_FL_ENTRIES = 16 -}; - /* Host shadow copy of ingress filter entry. This is in host native format * and doesn't match the ordering or bit order, etc. of the hardware of the * firmware command. The use of bit-field structure elements is purely to @@ -857,14 +849,14 @@ static void free_msix_queue_irqs(struct adapter *adap) } /** - * write_rss - write the RSS table for a given port + * cxgb4_write_rss - write the RSS table for a given port * @pi: the port * @queues: array of queue indices for RSS * * Sets up the portion of the HW RSS table for the port's VI to distribute * packets to the Rx queues in @queues. */ -static int write_rss(const struct port_info *pi, const u16 *queues) +int cxgb4_write_rss(const struct port_info *pi, const u16 *queues) { u16 *rss; int i, err; @@ -897,7 +889,7 @@ static int setup_rss(struct adapter *adap) for_each_port(adap, i) { const struct port_info *pi = adap2pinfo(adap, i); - err = write_rss(pi, pi->rss); + err = cxgb4_write_rss(pi, pi->rss); if (err) return err; } @@ -920,7 +912,7 @@ static void quiesce_rx(struct adapter *adap) { int i; - for (i = 0; i < ARRAY_SIZE(adap->sge.ingr_map); i++) { + for (i = 0; i < adap->sge.ingr_sz; i++) { struct sge_rspq *q = adap->sge.ingr_map[i]; if (q && q->handler) { @@ -934,6 +926,21 @@ static void quiesce_rx(struct adapter *adap) } } +/* Disable interrupt and napi handler */ +static void disable_interrupts(struct adapter *adap) +{ + if (adap->flags & FULL_INIT_DONE) { + t4_intr_disable(adap); + if (adap->flags & USING_MSIX) { + free_msix_queue_irqs(adap); + free_irq(adap->msix_info[0].vec, adap); + } else { + free_irq(adap->pdev->irq, adap); + } + quiesce_rx(adap); + } +} + /* * Enable NAPI scheduling and interrupt generation for all Rx queues. */ @@ -941,7 +948,7 @@ static void enable_rx(struct adapter *adap) { int i; - for (i = 0; i < ARRAY_SIZE(adap->sge.ingr_map); i++) { + for (i = 0; i < adap->sge.ingr_sz; i++) { struct sge_rspq *q = adap->sge.ingr_map[i]; if (!q) @@ -992,8 +999,8 @@ static int setup_sge_queues(struct adapter *adap) int err, msi_idx, i, j; struct sge *s = &adap->sge; - bitmap_zero(s->starving_fl, MAX_EGRQ); - bitmap_zero(s->txq_maperr, MAX_EGRQ); + bitmap_zero(s->starving_fl, s->egr_sz); + bitmap_zero(s->txq_maperr, s->egr_sz); if (adap->flags & USING_MSIX) msi_idx = 1; /* vector 0 is for non-queue interrupts */ @@ -1005,6 +1012,19 @@ static int setup_sge_queues(struct adapter *adap) msi_idx = -((int)s->intrq.abs_id + 1); } + /* NOTE: If you add/delete any Ingress/Egress Queue allocations in here, + * don't forget to update the following which need to be + * synchronized to and changes here. + * + * 1. The calculations of MAX_INGQ in cxgb4.h. + * + * 2. Update enable_msix/name_msix_vecs/request_msix_queue_irqs + * to accommodate any new/deleted Ingress Queues + * which need MSI-X Vectors. + * + * 3. Update sge_qinfo_show() to include information on the + * new/deleted queues. + */ err = t4_sge_alloc_rxq(adap, &s->fw_evtq, true, adap->port[0], msi_idx, NULL, fwevtq_handler); if (err) { @@ -1299,1192 +1319,6 @@ static inline int is_offload(const struct adapter *adap) return adap->params.offload; } -/* - * Implementation of ethtool operations. - */ - -static u32 get_msglevel(struct net_device *dev) -{ - return netdev2adap(dev)->msg_enable; -} - -static void set_msglevel(struct net_device *dev, u32 val) -{ - netdev2adap(dev)->msg_enable = val; -} - -static char stats_strings[][ETH_GSTRING_LEN] = { - "TxOctetsOK ", - "TxFramesOK ", - "TxBroadcastFrames ", - "TxMulticastFrames ", - "TxUnicastFrames ", - "TxErrorFrames ", - - "TxFrames64 ", - "TxFrames65To127 ", - "TxFrames128To255 ", - "TxFrames256To511 ", - "TxFrames512To1023 ", - "TxFrames1024To1518 ", - "TxFrames1519ToMax ", - - "TxFramesDropped ", - "TxPauseFrames ", - "TxPPP0Frames ", - "TxPPP1Frames ", - "TxPPP2Frames ", - "TxPPP3Frames ", - "TxPPP4Frames ", - "TxPPP5Frames ", - "TxPPP6Frames ", - "TxPPP7Frames ", - - "RxOctetsOK ", - "RxFramesOK ", - "RxBroadcastFrames ", - "RxMulticastFrames ", - "RxUnicastFrames ", - - "RxFramesTooLong ", - "RxJabberErrors ", - "RxFCSErrors ", - "RxLengthErrors ", - "RxSymbolErrors ", - "RxRuntFrames ", - - "RxFrames64 ", - "RxFrames65To127 ", - "RxFrames128To255 ", - "RxFrames256To511 ", - "RxFrames512To1023 ", - "RxFrames1024To1518 ", - "RxFrames1519ToMax ", - - "RxPauseFrames ", - "RxPPP0Frames ", - "RxPPP1Frames ", - "RxPPP2Frames ", - "RxPPP3Frames ", - "RxPPP4Frames ", - "RxPPP5Frames ", - "RxPPP6Frames ", - "RxPPP7Frames ", - - "RxBG0FramesDropped ", - "RxBG1FramesDropped ", - "RxBG2FramesDropped ", - "RxBG3FramesDropped ", - "RxBG0FramesTrunc ", - "RxBG1FramesTrunc ", - "RxBG2FramesTrunc ", - "RxBG3FramesTrunc ", - - "TSO ", - "TxCsumOffload ", - "RxCsumGood ", - "VLANextractions ", - "VLANinsertions ", - "GROpackets ", - "GROmerged ", - "WriteCoalSuccess ", - "WriteCoalFail ", -}; - -static int get_sset_count(struct net_device *dev, int sset) -{ - switch (sset) { - case ETH_SS_STATS: - return ARRAY_SIZE(stats_strings); - default: - return -EOPNOTSUPP; - } -} - -#define T4_REGMAP_SIZE (160 * 1024) -#define T5_REGMAP_SIZE (332 * 1024) - -static int get_regs_len(struct net_device *dev) -{ - struct adapter *adap = netdev2adap(dev); - if (is_t4(adap->params.chip)) - return T4_REGMAP_SIZE; - else - return T5_REGMAP_SIZE; -} - -static int get_eeprom_len(struct net_device *dev) -{ - return EEPROMSIZE; -} - -static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) -{ - struct adapter *adapter = netdev2adap(dev); - u32 exprom_vers; - - strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, pci_name(adapter->pdev), - sizeof(info->bus_info)); - - if (adapter->params.fw_vers) - snprintf(info->fw_version, sizeof(info->fw_version), - "%u.%u.%u.%u, TP %u.%u.%u.%u", - FW_HDR_FW_VER_MAJOR_G(adapter->params.fw_vers), - FW_HDR_FW_VER_MINOR_G(adapter->params.fw_vers), - FW_HDR_FW_VER_MICRO_G(adapter->params.fw_vers), - FW_HDR_FW_VER_BUILD_G(adapter->params.fw_vers), - FW_HDR_FW_VER_MAJOR_G(adapter->params.tp_vers), - FW_HDR_FW_VER_MINOR_G(adapter->params.tp_vers), - FW_HDR_FW_VER_MICRO_G(adapter->params.tp_vers), - FW_HDR_FW_VER_BUILD_G(adapter->params.tp_vers)); - - if (!t4_get_exprom_version(adapter, &exprom_vers)) - snprintf(info->erom_version, sizeof(info->erom_version), - "%u.%u.%u.%u", - FW_HDR_FW_VER_MAJOR_G(exprom_vers), - FW_HDR_FW_VER_MINOR_G(exprom_vers), - FW_HDR_FW_VER_MICRO_G(exprom_vers), - FW_HDR_FW_VER_BUILD_G(exprom_vers)); -} - -static void get_strings(struct net_device *dev, u32 stringset, u8 *data) -{ - if (stringset == ETH_SS_STATS) - memcpy(data, stats_strings, sizeof(stats_strings)); -} - -/* - * port stats maintained per queue of the port. They should be in the same - * order as in stats_strings above. - */ -struct queue_port_stats { - u64 tso; - u64 tx_csum; - u64 rx_csum; - u64 vlan_ex; - u64 vlan_ins; - u64 gro_pkts; - u64 gro_merged; -}; - -static void collect_sge_port_stats(const struct adapter *adap, - const struct port_info *p, struct queue_port_stats *s) -{ - int i; - const struct sge_eth_txq *tx = &adap->sge.ethtxq[p->first_qset]; - const struct sge_eth_rxq *rx = &adap->sge.ethrxq[p->first_qset]; - - memset(s, 0, sizeof(*s)); - for (i = 0; i < p->nqsets; i++, rx++, tx++) { - s->tso += tx->tso; - s->tx_csum += tx->tx_cso; - s->rx_csum += rx->stats.rx_cso; - s->vlan_ex += rx->stats.vlan_ex; - s->vlan_ins += tx->vlan_ins; - s->gro_pkts += rx->stats.lro_pkts; - s->gro_merged += rx->stats.lro_merged; - } -} - -static void get_stats(struct net_device *dev, struct ethtool_stats *stats, - u64 *data) -{ - struct port_info *pi = netdev_priv(dev); - struct adapter *adapter = pi->adapter; - u32 val1, val2; - - t4_get_port_stats(adapter, pi->tx_chan, (struct port_stats *)data); - - data += sizeof(struct port_stats) / sizeof(u64); - collect_sge_port_stats(adapter, pi, (struct queue_port_stats *)data); - data += sizeof(struct queue_port_stats) / sizeof(u64); - if (!is_t4(adapter->params.chip)) { - t4_write_reg(adapter, SGE_STAT_CFG_A, STATSOURCE_T5_V(7)); - val1 = t4_read_reg(adapter, SGE_STAT_TOTAL_A); - val2 = t4_read_reg(adapter, SGE_STAT_MATCH_A); - *data = val1 - val2; - data++; - *data = val2; - data++; - } else { - memset(data, 0, 2 * sizeof(u64)); - *data += 2; - } -} - -/* - * Return a version number to identify the type of adapter. The scheme is: - * - bits 0..9: chip version - * - bits 10..15: chip revision - * - bits 16..23: register dump version - */ -static inline unsigned int mk_adap_vers(const struct adapter *ap) -{ - return CHELSIO_CHIP_VERSION(ap->params.chip) | - (CHELSIO_CHIP_RELEASE(ap->params.chip) << 10) | (1 << 16); -} - -static void reg_block_dump(struct adapter *ap, void *buf, unsigned int start, - unsigned int end) -{ - u32 *p = buf + start; - - for ( ; start <= end; start += sizeof(u32)) - *p++ = t4_read_reg(ap, start); -} - -static void get_regs(struct net_device *dev, struct ethtool_regs *regs, - void *buf) -{ - static const unsigned int t4_reg_ranges[] = { - 0x1008, 0x1108, - 0x1180, 0x11b4, - 0x11fc, 0x123c, - 0x1300, 0x173c, - 0x1800, 0x18fc, - 0x3000, 0x30d8, - 0x30e0, 0x5924, - 0x5960, 0x59d4, - 0x5a00, 0x5af8, - 0x6000, 0x6098, - 0x6100, 0x6150, - 0x6200, 0x6208, - 0x6240, 0x6248, - 0x6280, 0x6338, - 0x6370, 0x638c, - 0x6400, 0x643c, - 0x6500, 0x6524, - 0x6a00, 0x6a38, - 0x6a60, 0x6a78, - 0x6b00, 0x6b84, - 0x6bf0, 0x6c84, - 0x6cf0, 0x6d84, - 0x6df0, 0x6e84, - 0x6ef0, 0x6f84, - 0x6ff0, 0x7084, - 0x70f0, 0x7184, - 0x71f0, 0x7284, - 0x72f0, 0x7384, - 0x73f0, 0x7450, - 0x7500, 0x7530, - 0x7600, 0x761c, - 0x7680, 0x76cc, - 0x7700, 0x7798, - 0x77c0, 0x77fc, - 0x7900, 0x79fc, - 0x7b00, 0x7c38, - 0x7d00, 0x7efc, - 0x8dc0, 0x8e1c, - 0x8e30, 0x8e78, - 0x8ea0, 0x8f6c, - 0x8fc0, 0x9074, - 0x90fc, 0x90fc, - 0x9400, 0x9458, - 0x9600, 0x96bc, - 0x9800, 0x9808, - 0x9820, 0x983c, - 0x9850, 0x9864, - 0x9c00, 0x9c6c, - 0x9c80, 0x9cec, - 0x9d00, 0x9d6c, - 0x9d80, 0x9dec, - 0x9e00, 0x9e6c, - 0x9e80, 0x9eec, - 0x9f00, 0x9f6c, - 0x9f80, 0x9fec, - 0xd004, 0xd03c, - 0xdfc0, 0xdfe0, - 0xe000, 0xea7c, - 0xf000, 0x11110, - 0x11118, 0x11190, - 0x19040, 0x1906c, - 0x19078, 0x19080, - 0x1908c, 0x19124, - 0x19150, 0x191b0, - 0x191d0, 0x191e8, - 0x19238, 0x1924c, - 0x193f8, 0x19474, - 0x19490, 0x194f8, - 0x19800, 0x19f30, - 0x1a000, 0x1a06c, - 0x1a0b0, 0x1a120, - 0x1a128, 0x1a138, - 0x1a190, 0x1a1c4, - 0x1a1fc, 0x1a1fc, - 0x1e040, 0x1e04c, - 0x1e284, 0x1e28c, - 0x1e2c0, 0x1e2c0, - 0x1e2e0, 0x1e2e0, - 0x1e300, 0x1e384, - 0x1e3c0, 0x1e3c8, - 0x1e440, 0x1e44c, - 0x1e684, 0x1e68c, - 0x1e6c0, 0x1e6c0, - 0x1e6e0, 0x1e6e0, - 0x1e700, 0x1e784, - 0x1e7c0, 0x1e7c8, - 0x1e840, 0x1e84c, - 0x1ea84, 0x1ea8c, - 0x1eac0, 0x1eac0, - 0x1eae0, 0x1eae0, - 0x1eb00, 0x1eb84, - 0x1ebc0, 0x1ebc8, - 0x1ec40, 0x1ec4c, - 0x1ee84, 0x1ee8c, - 0x1eec0, 0x1eec0, - 0x1eee0, 0x1eee0, - 0x1ef00, 0x1ef84, - 0x1efc0, 0x1efc8, - 0x1f040, 0x1f04c, - 0x1f284, 0x1f28c, - 0x1f2c0, 0x1f2c0, - 0x1f2e0, 0x1f2e0, - 0x1f300, 0x1f384, - 0x1f3c0, 0x1f3c8, - 0x1f440, 0x1f44c, - 0x1f684, 0x1f68c, - 0x1f6c0, 0x1f6c0, - 0x1f6e0, 0x1f6e0, - 0x1f700, 0x1f784, - 0x1f7c0, 0x1f7c8, - 0x1f840, 0x1f84c, - 0x1fa84, 0x1fa8c, - 0x1fac0, 0x1fac0, - 0x1fae0, 0x1fae0, - 0x1fb00, 0x1fb84, - 0x1fbc0, 0x1fbc8, - 0x1fc40, 0x1fc4c, - 0x1fe84, 0x1fe8c, - 0x1fec0, 0x1fec0, - 0x1fee0, 0x1fee0, - 0x1ff00, 0x1ff84, - 0x1ffc0, 0x1ffc8, - 0x20000, 0x2002c, - 0x20100, 0x2013c, - 0x20190, 0x201c8, - 0x20200, 0x20318, - 0x20400, 0x20528, - 0x20540, 0x20614, - 0x21000, 0x21040, - 0x2104c, 0x21060, - 0x210c0, 0x210ec, - 0x21200, 0x21268, - 0x21270, 0x21284, - 0x212fc, 0x21388, - 0x21400, 0x21404, - 0x21500, 0x21518, - 0x2152c, 0x2153c, - 0x21550, 0x21554, - 0x21600, 0x21600, - 0x21608, 0x21628, - 0x21630, 0x2163c, - 0x21700, 0x2171c, - 0x21780, 0x2178c, - 0x21800, 0x21c38, - 0x21c80, 0x21d7c, - 0x21e00, 0x21e04, - 0x22000, 0x2202c, - 0x22100, 0x2213c, - 0x22190, 0x221c8, - 0x22200, 0x22318, - 0x22400, 0x22528, - 0x22540, 0x22614, - 0x23000, 0x23040, - 0x2304c, 0x23060, - 0x230c0, 0x230ec, - 0x23200, 0x23268, - 0x23270, 0x23284, - 0x232fc, 0x23388, - 0x23400, 0x23404, - 0x23500, 0x23518, - 0x2352c, 0x2353c, - 0x23550, 0x23554, - 0x23600, 0x23600, - 0x23608, 0x23628, - 0x23630, 0x2363c, - 0x23700, 0x2371c, - 0x23780, 0x2378c, - 0x23800, 0x23c38, - 0x23c80, 0x23d7c, - 0x23e00, 0x23e04, - 0x24000, 0x2402c, - 0x24100, 0x2413c, - 0x24190, 0x241c8, - 0x24200, 0x24318, - 0x24400, 0x24528, - 0x24540, 0x24614, - 0x25000, 0x25040, - 0x2504c, 0x25060, - 0x250c0, 0x250ec, - 0x25200, 0x25268, - 0x25270, 0x25284, - 0x252fc, 0x25388, - 0x25400, 0x25404, - 0x25500, 0x25518, - 0x2552c, 0x2553c, - 0x25550, 0x25554, - 0x25600, 0x25600, - 0x25608, 0x25628, - 0x25630, 0x2563c, - 0x25700, 0x2571c, - 0x25780, 0x2578c, - 0x25800, 0x25c38, - 0x25c80, 0x25d7c, - 0x25e00, 0x25e04, - 0x26000, 0x2602c, - 0x26100, 0x2613c, - 0x26190, 0x261c8, - 0x26200, 0x26318, - 0x26400, 0x26528, - 0x26540, 0x26614, - 0x27000, 0x27040, - 0x2704c, 0x27060, - 0x270c0, 0x270ec, - 0x27200, 0x27268, - 0x27270, 0x27284, - 0x272fc, 0x27388, - 0x27400, 0x27404, - 0x27500, 0x27518, - 0x2752c, 0x2753c, - 0x27550, 0x27554, - 0x27600, 0x27600, - 0x27608, 0x27628, - 0x27630, 0x2763c, - 0x27700, 0x2771c, - 0x27780, 0x2778c, - 0x27800, 0x27c38, - 0x27c80, 0x27d7c, - 0x27e00, 0x27e04 - }; - - static const unsigned int t5_reg_ranges[] = { - 0x1008, 0x1148, - 0x1180, 0x11b4, - 0x11fc, 0x123c, - 0x1280, 0x173c, - 0x1800, 0x18fc, - 0x3000, 0x3028, - 0x3060, 0x30d8, - 0x30e0, 0x30fc, - 0x3140, 0x357c, - 0x35a8, 0x35cc, - 0x35ec, 0x35ec, - 0x3600, 0x5624, - 0x56cc, 0x575c, - 0x580c, 0x5814, - 0x5890, 0x58bc, - 0x5940, 0x59dc, - 0x59fc, 0x5a18, - 0x5a60, 0x5a9c, - 0x5b9c, 0x5bfc, - 0x6000, 0x6040, - 0x6058, 0x614c, - 0x7700, 0x7798, - 0x77c0, 0x78fc, - 0x7b00, 0x7c54, - 0x7d00, 0x7efc, - 0x8dc0, 0x8de0, - 0x8df8, 0x8e84, - 0x8ea0, 0x8f84, - 0x8fc0, 0x90f8, - 0x9400, 0x9470, - 0x9600, 0x96f4, - 0x9800, 0x9808, - 0x9820, 0x983c, - 0x9850, 0x9864, - 0x9c00, 0x9c6c, - 0x9c80, 0x9cec, - 0x9d00, 0x9d6c, - 0x9d80, 0x9dec, - 0x9e00, 0x9e6c, - 0x9e80, 0x9eec, - 0x9f00, 0x9f6c, - 0x9f80, 0xa020, - 0xd004, 0xd03c, - 0xdfc0, 0xdfe0, - 0xe000, 0x11088, - 0x1109c, 0x11110, - 0x11118, 0x1117c, - 0x11190, 0x11204, - 0x19040, 0x1906c, - 0x19078, 0x19080, - 0x1908c, 0x19124, - 0x19150, 0x191b0, - 0x191d0, 0x191e8, - 0x19238, 0x19290, - 0x193f8, 0x19474, - 0x19490, 0x194cc, - 0x194f0, 0x194f8, - 0x19c00, 0x19c60, - 0x19c94, 0x19e10, - 0x19e50, 0x19f34, - 0x19f40, 0x19f50, - 0x19f90, 0x19fe4, - 0x1a000, 0x1a06c, - 0x1a0b0, 0x1a120, - 0x1a128, 0x1a138, - 0x1a190, 0x1a1c4, - 0x1a1fc, 0x1a1fc, - 0x1e008, 0x1e00c, - 0x1e040, 0x1e04c, - 0x1e284, 0x1e290, - 0x1e2c0, 0x1e2c0, - 0x1e2e0, 0x1e2e0, - 0x1e300, 0x1e384, - 0x1e3c0, 0x1e3c8, - 0x1e408, 0x1e40c, - 0x1e440, 0x1e44c, - 0x1e684, 0x1e690, - 0x1e6c0, 0x1e6c0, - 0x1e6e0, 0x1e6e0, - 0x1e700, 0x1e784, - 0x1e7c0, 0x1e7c8, - 0x1e808, 0x1e80c, - 0x1e840, 0x1e84c, - 0x1ea84, 0x1ea90, - 0x1eac0, 0x1eac0, - 0x1eae0, 0x1eae0, - 0x1eb00, 0x1eb84, - 0x1ebc0, 0x1ebc8, - 0x1ec08, 0x1ec0c, - 0x1ec40, 0x1ec4c, - 0x1ee84, 0x1ee90, - 0x1eec0, 0x1eec0, - 0x1eee0, 0x1eee0, - 0x1ef00, 0x1ef84, - 0x1efc0, 0x1efc8, - 0x1f008, 0x1f00c, - 0x1f040, 0x1f04c, - 0x1f284, 0x1f290, - 0x1f2c0, 0x1f2c0, - 0x1f2e0, 0x1f2e0, - 0x1f300, 0x1f384, - 0x1f3c0, 0x1f3c8, - 0x1f408, 0x1f40c, - 0x1f440, 0x1f44c, - 0x1f684, 0x1f690, - 0x1f6c0, 0x1f6c0, - 0x1f6e0, 0x1f6e0, - 0x1f700, 0x1f784, - 0x1f7c0, 0x1f7c8, - 0x1f808, 0x1f80c, - 0x1f840, 0x1f84c, - 0x1fa84, 0x1fa90, - 0x1fac0, 0x1fac0, - 0x1fae0, 0x1fae0, - 0x1fb00, 0x1fb84, - 0x1fbc0, 0x1fbc8, - 0x1fc08, 0x1fc0c, - 0x1fc40, 0x1fc4c, - 0x1fe84, 0x1fe90, - 0x1fec0, 0x1fec0, - 0x1fee0, 0x1fee0, - 0x1ff00, 0x1ff84, - 0x1ffc0, 0x1ffc8, - 0x30000, 0x30030, - 0x30100, 0x30144, - 0x30190, 0x301d0, - 0x30200, 0x30318, - 0x30400, 0x3052c, - 0x30540, 0x3061c, - 0x30800, 0x30834, - 0x308c0, 0x30908, - 0x30910, 0x309ac, - 0x30a00, 0x30a04, - 0x30a0c, 0x30a2c, - 0x30a44, 0x30a50, - 0x30a74, 0x30c24, - 0x30d08, 0x30d14, - 0x30d1c, 0x30d20, - 0x30d3c, 0x30d50, - 0x31200, 0x3120c, - 0x31220, 0x31220, - 0x31240, 0x31240, - 0x31600, 0x31600, - 0x31608, 0x3160c, - 0x31a00, 0x31a1c, - 0x31e04, 0x31e20, - 0x31e38, 0x31e3c, - 0x31e80, 0x31e80, - 0x31e88, 0x31ea8, - 0x31eb0, 0x31eb4, - 0x31ec8, 0x31ed4, - 0x31fb8, 0x32004, - 0x32208, 0x3223c, - 0x32600, 0x32630, - 0x32a00, 0x32abc, - 0x32b00, 0x32b70, - 0x33000, 0x33048, - 0x33060, 0x3309c, - 0x330f0, 0x33148, - 0x33160, 0x3319c, - 0x331f0, 0x332e4, - 0x332f8, 0x333e4, - 0x333f8, 0x33448, - 0x33460, 0x3349c, - 0x334f0, 0x33548, - 0x33560, 0x3359c, - 0x335f0, 0x336e4, - 0x336f8, 0x337e4, - 0x337f8, 0x337fc, - 0x33814, 0x33814, - 0x3382c, 0x3382c, - 0x33880, 0x3388c, - 0x338e8, 0x338ec, - 0x33900, 0x33948, - 0x33960, 0x3399c, - 0x339f0, 0x33ae4, - 0x33af8, 0x33b10, - 0x33b28, 0x33b28, - 0x33b3c, 0x33b50, - 0x33bf0, 0x33c10, - 0x33c28, 0x33c28, - 0x33c3c, 0x33c50, - 0x33cf0, 0x33cfc, - 0x34000, 0x34030, - 0x34100, 0x34144, - 0x34190, 0x341d0, - 0x34200, 0x34318, - 0x34400, 0x3452c, - 0x34540, 0x3461c, - 0x34800, 0x34834, - 0x348c0, 0x34908, - 0x34910, 0x349ac, - 0x34a00, 0x34a04, - 0x34a0c, 0x34a2c, - 0x34a44, 0x34a50, - 0x34a74, 0x34c24, - 0x34d08, 0x34d14, - 0x34d1c, 0x34d20, - 0x34d3c, 0x34d50, - 0x35200, 0x3520c, - 0x35220, 0x35220, - 0x35240, 0x35240, - 0x35600, 0x35600, - 0x35608, 0x3560c, - 0x35a00, 0x35a1c, - 0x35e04, 0x35e20, - 0x35e38, 0x35e3c, - 0x35e80, 0x35e80, - 0x35e88, 0x35ea8, - 0x35eb0, 0x35eb4, - 0x35ec8, 0x35ed4, - 0x35fb8, 0x36004, - 0x36208, 0x3623c, - 0x36600, 0x36630, - 0x36a00, 0x36abc, - 0x36b00, 0x36b70, - 0x37000, 0x37048, - 0x37060, 0x3709c, - 0x370f0, 0x37148, - 0x37160, 0x3719c, - 0x371f0, 0x372e4, - 0x372f8, 0x373e4, - 0x373f8, 0x37448, - 0x37460, 0x3749c, - 0x374f0, 0x37548, - 0x37560, 0x3759c, - 0x375f0, 0x376e4, - 0x376f8, 0x377e4, - 0x377f8, 0x377fc, - 0x37814, 0x37814, - 0x3782c, 0x3782c, - 0x37880, 0x3788c, - 0x378e8, 0x378ec, - 0x37900, 0x37948, - 0x37960, 0x3799c, - 0x379f0, 0x37ae4, - 0x37af8, 0x37b10, - 0x37b28, 0x37b28, - 0x37b3c, 0x37b50, - 0x37bf0, 0x37c10, - 0x37c28, 0x37c28, - 0x37c3c, 0x37c50, - 0x37cf0, 0x37cfc, - 0x38000, 0x38030, - 0x38100, 0x38144, - 0x38190, 0x381d0, - 0x38200, 0x38318, - 0x38400, 0x3852c, - 0x38540, 0x3861c, - 0x38800, 0x38834, - 0x388c0, 0x38908, - 0x38910, 0x389ac, - 0x38a00, 0x38a04, - 0x38a0c, 0x38a2c, - 0x38a44, 0x38a50, - 0x38a74, 0x38c24, - 0x38d08, 0x38d14, - 0x38d1c, 0x38d20, - 0x38d3c, 0x38d50, - 0x39200, 0x3920c, - 0x39220, 0x39220, - 0x39240, 0x39240, - 0x39600, 0x39600, - 0x39608, 0x3960c, - 0x39a00, 0x39a1c, - 0x39e04, 0x39e20, - 0x39e38, 0x39e3c, - 0x39e80, 0x39e80, - 0x39e88, 0x39ea8, - 0x39eb0, 0x39eb4, - 0x39ec8, 0x39ed4, - 0x39fb8, 0x3a004, - 0x3a208, 0x3a23c, - 0x3a600, 0x3a630, - 0x3aa00, 0x3aabc, - 0x3ab00, 0x3ab70, - 0x3b000, 0x3b048, - 0x3b060, 0x3b09c, - 0x3b0f0, 0x3b148, - 0x3b160, 0x3b19c, - 0x3b1f0, 0x3b2e4, - 0x3b2f8, 0x3b3e4, - 0x3b3f8, 0x3b448, - 0x3b460, 0x3b49c, - 0x3b4f0, 0x3b548, - 0x3b560, 0x3b59c, - 0x3b5f0, 0x3b6e4, - 0x3b6f8, 0x3b7e4, - 0x3b7f8, 0x3b7fc, - 0x3b814, 0x3b814, - 0x3b82c, 0x3b82c, - 0x3b880, 0x3b88c, - 0x3b8e8, 0x3b8ec, - 0x3b900, 0x3b948, - 0x3b960, 0x3b99c, - 0x3b9f0, 0x3bae4, - 0x3baf8, 0x3bb10, - 0x3bb28, 0x3bb28, - 0x3bb3c, 0x3bb50, - 0x3bbf0, 0x3bc10, - 0x3bc28, 0x3bc28, - 0x3bc3c, 0x3bc50, - 0x3bcf0, 0x3bcfc, - 0x3c000, 0x3c030, - 0x3c100, 0x3c144, - 0x3c190, 0x3c1d0, - 0x3c200, 0x3c318, - 0x3c400, 0x3c52c, - 0x3c540, 0x3c61c, - 0x3c800, 0x3c834, - 0x3c8c0, 0x3c908, - 0x3c910, 0x3c9ac, - 0x3ca00, 0x3ca04, - 0x3ca0c, 0x3ca2c, - 0x3ca44, 0x3ca50, - 0x3ca74, 0x3cc24, - 0x3cd08, 0x3cd14, - 0x3cd1c, 0x3cd20, - 0x3cd3c, 0x3cd50, - 0x3d200, 0x3d20c, - 0x3d220, 0x3d220, - 0x3d240, 0x3d240, - 0x3d600, 0x3d600, - 0x3d608, 0x3d60c, - 0x3da00, 0x3da1c, - 0x3de04, 0x3de20, - 0x3de38, 0x3de3c, - 0x3de80, 0x3de80, - 0x3de88, 0x3dea8, - 0x3deb0, 0x3deb4, - 0x3dec8, 0x3ded4, - 0x3dfb8, 0x3e004, - 0x3e208, 0x3e23c, - 0x3e600, 0x3e630, - 0x3ea00, 0x3eabc, - 0x3eb00, 0x3eb70, - 0x3f000, 0x3f048, - 0x3f060, 0x3f09c, - 0x3f0f0, 0x3f148, - 0x3f160, 0x3f19c, - 0x3f1f0, 0x3f2e4, - 0x3f2f8, 0x3f3e4, - 0x3f3f8, 0x3f448, - 0x3f460, 0x3f49c, - 0x3f4f0, 0x3f548, - 0x3f560, 0x3f59c, - 0x3f5f0, 0x3f6e4, - 0x3f6f8, 0x3f7e4, - 0x3f7f8, 0x3f7fc, - 0x3f814, 0x3f814, - 0x3f82c, 0x3f82c, - 0x3f880, 0x3f88c, - 0x3f8e8, 0x3f8ec, - 0x3f900, 0x3f948, - 0x3f960, 0x3f99c, - 0x3f9f0, 0x3fae4, - 0x3faf8, 0x3fb10, - 0x3fb28, 0x3fb28, - 0x3fb3c, 0x3fb50, - 0x3fbf0, 0x3fc10, - 0x3fc28, 0x3fc28, - 0x3fc3c, 0x3fc50, - 0x3fcf0, 0x3fcfc, - 0x40000, 0x4000c, - 0x40040, 0x40068, - 0x40080, 0x40144, - 0x40180, 0x4018c, - 0x40200, 0x40298, - 0x402ac, 0x4033c, - 0x403f8, 0x403fc, - 0x41304, 0x413c4, - 0x41400, 0x4141c, - 0x41480, 0x414d0, - 0x44000, 0x44078, - 0x440c0, 0x44278, - 0x442c0, 0x44478, - 0x444c0, 0x44678, - 0x446c0, 0x44878, - 0x448c0, 0x449fc, - 0x45000, 0x45068, - 0x45080, 0x45084, - 0x450a0, 0x450b0, - 0x45200, 0x45268, - 0x45280, 0x45284, - 0x452a0, 0x452b0, - 0x460c0, 0x460e4, - 0x47000, 0x4708c, - 0x47200, 0x47250, - 0x47400, 0x47420, - 0x47600, 0x47618, - 0x47800, 0x47814, - 0x48000, 0x4800c, - 0x48040, 0x48068, - 0x48080, 0x48144, - 0x48180, 0x4818c, - 0x48200, 0x48298, - 0x482ac, 0x4833c, - 0x483f8, 0x483fc, - 0x49304, 0x493c4, - 0x49400, 0x4941c, - 0x49480, 0x494d0, - 0x4c000, 0x4c078, - 0x4c0c0, 0x4c278, - 0x4c2c0, 0x4c478, - 0x4c4c0, 0x4c678, - 0x4c6c0, 0x4c878, - 0x4c8c0, 0x4c9fc, - 0x4d000, 0x4d068, - 0x4d080, 0x4d084, - 0x4d0a0, 0x4d0b0, - 0x4d200, 0x4d268, - 0x4d280, 0x4d284, - 0x4d2a0, 0x4d2b0, - 0x4e0c0, 0x4e0e4, - 0x4f000, 0x4f08c, - 0x4f200, 0x4f250, - 0x4f400, 0x4f420, - 0x4f600, 0x4f618, - 0x4f800, 0x4f814, - 0x50000, 0x500cc, - 0x50400, 0x50400, - 0x50800, 0x508cc, - 0x50c00, 0x50c00, - 0x51000, 0x5101c, - 0x51300, 0x51308, - }; - - int i; - struct adapter *ap = netdev2adap(dev); - static const unsigned int *reg_ranges; - int arr_size = 0, buf_size = 0; - - if (is_t4(ap->params.chip)) { - reg_ranges = &t4_reg_ranges[0]; - arr_size = ARRAY_SIZE(t4_reg_ranges); - buf_size = T4_REGMAP_SIZE; - } else { - reg_ranges = &t5_reg_ranges[0]; - arr_size = ARRAY_SIZE(t5_reg_ranges); - buf_size = T5_REGMAP_SIZE; - } - - regs->version = mk_adap_vers(ap); - - memset(buf, 0, buf_size); - for (i = 0; i < arr_size; i += 2) - reg_block_dump(ap, buf, reg_ranges[i], reg_ranges[i + 1]); -} - -static int restart_autoneg(struct net_device *dev) -{ - struct port_info *p = netdev_priv(dev); - - if (!netif_running(dev)) - return -EAGAIN; - if (p->link_cfg.autoneg != AUTONEG_ENABLE) - return -EINVAL; - t4_restart_aneg(p->adapter, p->adapter->fn, p->tx_chan); - return 0; -} - -static int identify_port(struct net_device *dev, - enum ethtool_phys_id_state state) -{ - unsigned int val; - struct adapter *adap = netdev2adap(dev); - - if (state == ETHTOOL_ID_ACTIVE) - val = 0xffff; - else if (state == ETHTOOL_ID_INACTIVE) - val = 0; - else - return -EINVAL; - - return t4_identify_port(adap, adap->fn, netdev2pinfo(dev)->viid, val); -} - -static unsigned int from_fw_linkcaps(enum fw_port_type type, unsigned int caps) -{ - unsigned int v = 0; - - if (type == FW_PORT_TYPE_BT_SGMII || type == FW_PORT_TYPE_BT_XFI || - type == FW_PORT_TYPE_BT_XAUI) { - v |= SUPPORTED_TP; - if (caps & FW_PORT_CAP_SPEED_100M) - v |= SUPPORTED_100baseT_Full; - if (caps & FW_PORT_CAP_SPEED_1G) - v |= SUPPORTED_1000baseT_Full; - if (caps & FW_PORT_CAP_SPEED_10G) - v |= SUPPORTED_10000baseT_Full; - } else if (type == FW_PORT_TYPE_KX4 || type == FW_PORT_TYPE_KX) { - v |= SUPPORTED_Backplane; - if (caps & FW_PORT_CAP_SPEED_1G) - v |= SUPPORTED_1000baseKX_Full; - if (caps & FW_PORT_CAP_SPEED_10G) - v |= SUPPORTED_10000baseKX4_Full; - } else if (type == FW_PORT_TYPE_KR) - v |= SUPPORTED_Backplane | SUPPORTED_10000baseKR_Full; - else if (type == FW_PORT_TYPE_BP_AP) - v |= SUPPORTED_Backplane | SUPPORTED_10000baseR_FEC | - SUPPORTED_10000baseKR_Full | SUPPORTED_1000baseKX_Full; - else if (type == FW_PORT_TYPE_BP4_AP) - v |= SUPPORTED_Backplane | SUPPORTED_10000baseR_FEC | - SUPPORTED_10000baseKR_Full | SUPPORTED_1000baseKX_Full | - SUPPORTED_10000baseKX4_Full; - else if (type == FW_PORT_TYPE_FIBER_XFI || - type == FW_PORT_TYPE_FIBER_XAUI || - type == FW_PORT_TYPE_SFP || - type == FW_PORT_TYPE_QSFP_10G || - type == FW_PORT_TYPE_QSA) { - v |= SUPPORTED_FIBRE; - if (caps & FW_PORT_CAP_SPEED_1G) - v |= SUPPORTED_1000baseT_Full; - if (caps & FW_PORT_CAP_SPEED_10G) - v |= SUPPORTED_10000baseT_Full; - } else if (type == FW_PORT_TYPE_BP40_BA || - type == FW_PORT_TYPE_QSFP) { - v |= SUPPORTED_40000baseSR4_Full; - v |= SUPPORTED_FIBRE; - } - - if (caps & FW_PORT_CAP_ANEG) - v |= SUPPORTED_Autoneg; - return v; -} - -static unsigned int to_fw_linkcaps(unsigned int caps) -{ - unsigned int v = 0; - - if (caps & ADVERTISED_100baseT_Full) - v |= FW_PORT_CAP_SPEED_100M; - if (caps & ADVERTISED_1000baseT_Full) - v |= FW_PORT_CAP_SPEED_1G; - if (caps & ADVERTISED_10000baseT_Full) - v |= FW_PORT_CAP_SPEED_10G; - if (caps & ADVERTISED_40000baseSR4_Full) - v |= FW_PORT_CAP_SPEED_40G; - return v; -} - -static int get_settings(struct net_device *dev, struct ethtool_cmd *cmd) -{ - const struct port_info *p = netdev_priv(dev); - - if (p->port_type == FW_PORT_TYPE_BT_SGMII || - p->port_type == FW_PORT_TYPE_BT_XFI || - p->port_type == FW_PORT_TYPE_BT_XAUI) - cmd->port = PORT_TP; - else if (p->port_type == FW_PORT_TYPE_FIBER_XFI || - p->port_type == FW_PORT_TYPE_FIBER_XAUI) - cmd->port = PORT_FIBRE; - else if (p->port_type == FW_PORT_TYPE_SFP || - p->port_type == FW_PORT_TYPE_QSFP_10G || - p->port_type == FW_PORT_TYPE_QSA || - p->port_type == FW_PORT_TYPE_QSFP) { - if (p->mod_type == FW_PORT_MOD_TYPE_LR || - p->mod_type == FW_PORT_MOD_TYPE_SR || - p->mod_type == FW_PORT_MOD_TYPE_ER || - p->mod_type == FW_PORT_MOD_TYPE_LRM) - cmd->port = PORT_FIBRE; - else if (p->mod_type == FW_PORT_MOD_TYPE_TWINAX_PASSIVE || - p->mod_type == FW_PORT_MOD_TYPE_TWINAX_ACTIVE) - cmd->port = PORT_DA; - else - cmd->port = PORT_OTHER; - } else - cmd->port = PORT_OTHER; - - if (p->mdio_addr >= 0) { - cmd->phy_address = p->mdio_addr; - cmd->transceiver = XCVR_EXTERNAL; - cmd->mdio_support = p->port_type == FW_PORT_TYPE_BT_SGMII ? - MDIO_SUPPORTS_C22 : MDIO_SUPPORTS_C45; - } else { - cmd->phy_address = 0; /* not really, but no better option */ - cmd->transceiver = XCVR_INTERNAL; - cmd->mdio_support = 0; - } - - cmd->supported = from_fw_linkcaps(p->port_type, p->link_cfg.supported); - cmd->advertising = from_fw_linkcaps(p->port_type, - p->link_cfg.advertising); - ethtool_cmd_speed_set(cmd, - netif_carrier_ok(dev) ? p->link_cfg.speed : 0); - cmd->duplex = DUPLEX_FULL; - cmd->autoneg = p->link_cfg.autoneg; - cmd->maxtxpkt = 0; - cmd->maxrxpkt = 0; - return 0; -} - -static unsigned int speed_to_caps(int speed) -{ - if (speed == 100) - return FW_PORT_CAP_SPEED_100M; - if (speed == 1000) - return FW_PORT_CAP_SPEED_1G; - if (speed == 10000) - return FW_PORT_CAP_SPEED_10G; - if (speed == 40000) - return FW_PORT_CAP_SPEED_40G; - return 0; -} - -static int set_settings(struct net_device *dev, struct ethtool_cmd *cmd) -{ - unsigned int cap; - struct port_info *p = netdev_priv(dev); - struct link_config *lc = &p->link_cfg; - u32 speed = ethtool_cmd_speed(cmd); - - if (cmd->duplex != DUPLEX_FULL) /* only full-duplex supported */ - return -EINVAL; - - if (!(lc->supported & FW_PORT_CAP_ANEG)) { - /* - * PHY offers a single speed. See if that's what's - * being requested. - */ - if (cmd->autoneg == AUTONEG_DISABLE && - (lc->supported & speed_to_caps(speed))) - return 0; - return -EINVAL; - } - - if (cmd->autoneg == AUTONEG_DISABLE) { - cap = speed_to_caps(speed); - - if (!(lc->supported & cap) || - (speed == 1000) || - (speed == 10000) || - (speed == 40000)) - return -EINVAL; - lc->requested_speed = cap; - lc->advertising = 0; - } else { - cap = to_fw_linkcaps(cmd->advertising); - if (!(lc->supported & cap)) - return -EINVAL; - lc->requested_speed = 0; - lc->advertising = cap | FW_PORT_CAP_ANEG; - } - lc->autoneg = cmd->autoneg; - - if (netif_running(dev)) - return t4_link_start(p->adapter, p->adapter->fn, p->tx_chan, - lc); - return 0; -} - -static void get_pauseparam(struct net_device *dev, - struct ethtool_pauseparam *epause) -{ - struct port_info *p = netdev_priv(dev); - - epause->autoneg = (p->link_cfg.requested_fc & PAUSE_AUTONEG) != 0; - epause->rx_pause = (p->link_cfg.fc & PAUSE_RX) != 0; - epause->tx_pause = (p->link_cfg.fc & PAUSE_TX) != 0; -} - -static int set_pauseparam(struct net_device *dev, - struct ethtool_pauseparam *epause) -{ - struct port_info *p = netdev_priv(dev); - struct link_config *lc = &p->link_cfg; - - if (epause->autoneg == AUTONEG_DISABLE) - lc->requested_fc = 0; - else if (lc->supported & FW_PORT_CAP_ANEG) - lc->requested_fc = PAUSE_AUTONEG; - else - return -EINVAL; - - if (epause->rx_pause) - lc->requested_fc |= PAUSE_RX; - if (epause->tx_pause) - lc->requested_fc |= PAUSE_TX; - if (netif_running(dev)) - return t4_link_start(p->adapter, p->adapter->fn, p->tx_chan, - lc); - return 0; -} - -static void get_sge_param(struct net_device *dev, struct ethtool_ringparam *e) -{ - const struct port_info *pi = netdev_priv(dev); - const struct sge *s = &pi->adapter->sge; - - e->rx_max_pending = MAX_RX_BUFFERS; - e->rx_mini_max_pending = MAX_RSPQ_ENTRIES; - e->rx_jumbo_max_pending = 0; - e->tx_max_pending = MAX_TXQ_ENTRIES; - - e->rx_pending = s->ethrxq[pi->first_qset].fl.size - 8; - e->rx_mini_pending = s->ethrxq[pi->first_qset].rspq.size; - e->rx_jumbo_pending = 0; - e->tx_pending = s->ethtxq[pi->first_qset].q.size; -} - -static int set_sge_param(struct net_device *dev, struct ethtool_ringparam *e) -{ - int i; - const struct port_info *pi = netdev_priv(dev); - struct adapter *adapter = pi->adapter; - struct sge *s = &adapter->sge; - - if (e->rx_pending > MAX_RX_BUFFERS || e->rx_jumbo_pending || - e->tx_pending > MAX_TXQ_ENTRIES || - e->rx_mini_pending > MAX_RSPQ_ENTRIES || - e->rx_mini_pending < MIN_RSPQ_ENTRIES || - e->rx_pending < MIN_FL_ENTRIES || e->tx_pending < MIN_TXQ_ENTRIES) - return -EINVAL; - - if (adapter->flags & FULL_INIT_DONE) - return -EBUSY; - - for (i = 0; i < pi->nqsets; ++i) { - s->ethtxq[pi->first_qset + i].q.size = e->tx_pending; - s->ethrxq[pi->first_qset + i].fl.size = e->rx_pending + 8; - s->ethrxq[pi->first_qset + i].rspq.size = e->rx_mini_pending; - } - return 0; -} - static int closest_timer(const struct sge *s, int time) { int i, delta, match = 0, min_delta = INT_MAX; @@ -2517,19 +1351,8 @@ static int closest_thres(const struct sge *s, int thres) return match; } -/* - * Return a queue's interrupt hold-off time in us. 0 means no timer. - */ -unsigned int qtimer_val(const struct adapter *adap, - const struct sge_rspq *q) -{ - unsigned int idx = q->intr_params >> 1; - - return idx < SGE_NTIMERS ? adap->sge.timer_val[idx] : 0; -} - /** - * set_rspq_intr_params - set a queue's interrupt holdoff parameters + * cxgb4_set_rspq_intr_params - set a queue's interrupt holdoff parameters * @q: the Rx queue * @us: the hold-off time in us, or 0 to disable timer * @cnt: the hold-off packet count, or 0 to disable counter @@ -2537,8 +1360,8 @@ unsigned int qtimer_val(const struct adapter *adap, * Sets an Rx queue's interrupt hold-off time and packet count. At least * one of the two needs to be enabled for the queue to generate interrupts. */ -static int set_rspq_intr_params(struct sge_rspq *q, - unsigned int us, unsigned int cnt) +int cxgb4_set_rspq_intr_params(struct sge_rspq *q, + unsigned int us, unsigned int cnt) { struct adapter *adap = q->adap; @@ -2569,259 +1392,6 @@ static int set_rspq_intr_params(struct sge_rspq *q, return 0; } -/** - * set_rx_intr_params - set a net devices's RX interrupt holdoff paramete! - * @dev: the network device - * @us: the hold-off time in us, or 0 to disable timer - * @cnt: the hold-off packet count, or 0 to disable counter - * - * Set the RX interrupt hold-off parameters for a network device. - */ -static int set_rx_intr_params(struct net_device *dev, - unsigned int us, unsigned int cnt) -{ - int i, err; - struct port_info *pi = netdev_priv(dev); - struct adapter *adap = pi->adapter; - struct sge_eth_rxq *q = &adap->sge.ethrxq[pi->first_qset]; - - for (i = 0; i < pi->nqsets; i++, q++) { - err = set_rspq_intr_params(&q->rspq, us, cnt); - if (err) - return err; - } - return 0; -} - -static int set_adaptive_rx_setting(struct net_device *dev, int adaptive_rx) -{ - int i; - struct port_info *pi = netdev_priv(dev); - struct adapter *adap = pi->adapter; - struct sge_eth_rxq *q = &adap->sge.ethrxq[pi->first_qset]; - - for (i = 0; i < pi->nqsets; i++, q++) - q->rspq.adaptive_rx = adaptive_rx; - - return 0; -} - -static int get_adaptive_rx_setting(struct net_device *dev) -{ - struct port_info *pi = netdev_priv(dev); - struct adapter *adap = pi->adapter; - struct sge_eth_rxq *q = &adap->sge.ethrxq[pi->first_qset]; - - return q->rspq.adaptive_rx; -} - -static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c) -{ - set_adaptive_rx_setting(dev, c->use_adaptive_rx_coalesce); - return set_rx_intr_params(dev, c->rx_coalesce_usecs, - c->rx_max_coalesced_frames); -} - -static int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c) -{ - const struct port_info *pi = netdev_priv(dev); - const struct adapter *adap = pi->adapter; - const struct sge_rspq *rq = &adap->sge.ethrxq[pi->first_qset].rspq; - - c->rx_coalesce_usecs = qtimer_val(adap, rq); - c->rx_max_coalesced_frames = (rq->intr_params & QINTR_CNT_EN) ? - adap->sge.counter_val[rq->pktcnt_idx] : 0; - c->use_adaptive_rx_coalesce = get_adaptive_rx_setting(dev); - return 0; -} - -/** - * eeprom_ptov - translate a physical EEPROM address to virtual - * @phys_addr: the physical EEPROM address - * @fn: the PCI function number - * @sz: size of function-specific area - * - * Translate a physical EEPROM address to virtual. The first 1K is - * accessed through virtual addresses starting at 31K, the rest is - * accessed through virtual addresses starting at 0. - * - * The mapping is as follows: - * [0..1K) -> [31K..32K) - * [1K..1K+A) -> [31K-A..31K) - * [1K+A..ES) -> [0..ES-A-1K) - * - * where A = @fn * @sz, and ES = EEPROM size. - */ -static int eeprom_ptov(unsigned int phys_addr, unsigned int fn, unsigned int sz) -{ - fn *= sz; - if (phys_addr < 1024) - return phys_addr + (31 << 10); - if (phys_addr < 1024 + fn) - return 31744 - fn + phys_addr - 1024; - if (phys_addr < EEPROMSIZE) - return phys_addr - 1024 - fn; - return -EINVAL; -} - -/* - * The next two routines implement eeprom read/write from physical addresses. - */ -static int eeprom_rd_phys(struct adapter *adap, unsigned int phys_addr, u32 *v) -{ - int vaddr = eeprom_ptov(phys_addr, adap->fn, EEPROMPFSIZE); - - if (vaddr >= 0) - vaddr = pci_read_vpd(adap->pdev, vaddr, sizeof(u32), v); - return vaddr < 0 ? vaddr : 0; -} - -static int eeprom_wr_phys(struct adapter *adap, unsigned int phys_addr, u32 v) -{ - int vaddr = eeprom_ptov(phys_addr, adap->fn, EEPROMPFSIZE); - - if (vaddr >= 0) - vaddr = pci_write_vpd(adap->pdev, vaddr, sizeof(u32), &v); - return vaddr < 0 ? vaddr : 0; -} - -#define EEPROM_MAGIC 0x38E2F10C - -static int get_eeprom(struct net_device *dev, struct ethtool_eeprom *e, - u8 *data) -{ - int i, err = 0; - struct adapter *adapter = netdev2adap(dev); - - u8 *buf = kmalloc(EEPROMSIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - e->magic = EEPROM_MAGIC; - for (i = e->offset & ~3; !err && i < e->offset + e->len; i += 4) - err = eeprom_rd_phys(adapter, i, (u32 *)&buf[i]); - - if (!err) - memcpy(data, buf + e->offset, e->len); - kfree(buf); - return err; -} - -static int set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, - u8 *data) -{ - u8 *buf; - int err = 0; - u32 aligned_offset, aligned_len, *p; - struct adapter *adapter = netdev2adap(dev); - - if (eeprom->magic != EEPROM_MAGIC) - return -EINVAL; - - aligned_offset = eeprom->offset & ~3; - aligned_len = (eeprom->len + (eeprom->offset & 3) + 3) & ~3; - - if (adapter->fn > 0) { - u32 start = 1024 + adapter->fn * EEPROMPFSIZE; - - if (aligned_offset < start || - aligned_offset + aligned_len > start + EEPROMPFSIZE) - return -EPERM; - } - - if (aligned_offset != eeprom->offset || aligned_len != eeprom->len) { - /* - * RMW possibly needed for first or last words. - */ - buf = kmalloc(aligned_len, GFP_KERNEL); - if (!buf) - return -ENOMEM; - err = eeprom_rd_phys(adapter, aligned_offset, (u32 *)buf); - if (!err && aligned_len > 4) - err = eeprom_rd_phys(adapter, - aligned_offset + aligned_len - 4, - (u32 *)&buf[aligned_len - 4]); - if (err) - goto out; - memcpy(buf + (eeprom->offset & 3), data, eeprom->len); - } else - buf = data; - - err = t4_seeprom_wp(adapter, false); - if (err) - goto out; - - for (p = (u32 *)buf; !err && aligned_len; aligned_len -= 4, p++) { - err = eeprom_wr_phys(adapter, aligned_offset, *p); - aligned_offset += 4; - } - - if (!err) - err = t4_seeprom_wp(adapter, true); -out: - if (buf != data) - kfree(buf); - return err; -} - -static int set_flash(struct net_device *netdev, struct ethtool_flash *ef) -{ - int ret; - const struct firmware *fw; - struct adapter *adap = netdev2adap(netdev); - unsigned int mbox = PCIE_FW_MASTER_M + 1; - - ef->data[sizeof(ef->data) - 1] = '\0'; - ret = request_firmware(&fw, ef->data, adap->pdev_dev); - if (ret < 0) - return ret; - - /* If the adapter has been fully initialized then we'll go ahead and - * try to get the firmware's cooperation in upgrading to the new - * firmware image otherwise we'll try to do the entire job from the - * host ... and we always "force" the operation in this path. - */ - if (adap->flags & FULL_INIT_DONE) - mbox = adap->mbox; - - ret = t4_fw_upgrade(adap, mbox, fw->data, fw->size, 1); - release_firmware(fw); - if (!ret) - dev_info(adap->pdev_dev, "loaded firmware %s," - " reload cxgb4 driver\n", ef->data); - return ret; -} - -#define WOL_SUPPORTED (WAKE_BCAST | WAKE_MAGIC) -#define BCAST_CRC 0xa0ccc1a6 - -static void get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) -{ - wol->supported = WAKE_BCAST | WAKE_MAGIC; - wol->wolopts = netdev2adap(dev)->wol; - memset(&wol->sopass, 0, sizeof(wol->sopass)); -} - -static int set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) -{ - int err = 0; - struct port_info *pi = netdev_priv(dev); - - if (wol->wolopts & ~WOL_SUPPORTED) - return -EINVAL; - t4_wol_magic_enable(pi->adapter, pi->tx_chan, - (wol->wolopts & WAKE_MAGIC) ? dev->dev_addr : NULL); - if (wol->wolopts & WAKE_BCAST) { - err = t4_wol_pat_enable(pi->adapter, pi->tx_chan, 0xfe, ~0ULL, - ~0ULL, 0, false); - if (!err) - err = t4_wol_pat_enable(pi->adapter, pi->tx_chan, 1, - ~6ULL, ~0ULL, BCAST_CRC, true); - } else - t4_wol_pat_enable(pi->adapter, pi->tx_chan, 0, 0, 0, 0, false); - return err; -} - static int cxgb_set_features(struct net_device *dev, netdev_features_t features) { const struct port_info *pi = netdev_priv(dev); @@ -2839,144 +1409,6 @@ static int cxgb_set_features(struct net_device *dev, netdev_features_t features) return err; } -static u32 get_rss_table_size(struct net_device *dev) -{ - const struct port_info *pi = netdev_priv(dev); - - return pi->rss_size; -} - -static int get_rss_table(struct net_device *dev, u32 *p, u8 *key, u8 *hfunc) -{ - const struct port_info *pi = netdev_priv(dev); - unsigned int n = pi->rss_size; - - if (hfunc) - *hfunc = ETH_RSS_HASH_TOP; - if (!p) - return 0; - while (n--) - p[n] = pi->rss[n]; - return 0; -} - -static int set_rss_table(struct net_device *dev, const u32 *p, const u8 *key, - const u8 hfunc) -{ - unsigned int i; - struct port_info *pi = netdev_priv(dev); - - /* We require at least one supported parameter to be changed and no - * change in any of the unsupported parameters - */ - if (key || - (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)) - return -EOPNOTSUPP; - if (!p) - return 0; - - for (i = 0; i < pi->rss_size; i++) - pi->rss[i] = p[i]; - if (pi->adapter->flags & FULL_INIT_DONE) - return write_rss(pi, pi->rss); - return 0; -} - -static int get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info, - u32 *rules) -{ - const struct port_info *pi = netdev_priv(dev); - - switch (info->cmd) { - case ETHTOOL_GRXFH: { - unsigned int v = pi->rss_mode; - - info->data = 0; - switch (info->flow_type) { - case TCP_V4_FLOW: - if (v & FW_RSS_VI_CONFIG_CMD_IP4FOURTUPEN_F) - info->data = RXH_IP_SRC | RXH_IP_DST | - RXH_L4_B_0_1 | RXH_L4_B_2_3; - else if (v & FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN_F) - info->data = RXH_IP_SRC | RXH_IP_DST; - break; - case UDP_V4_FLOW: - if ((v & FW_RSS_VI_CONFIG_CMD_IP4FOURTUPEN_F) && - (v & FW_RSS_VI_CONFIG_CMD_UDPEN_F)) - info->data = RXH_IP_SRC | RXH_IP_DST | - RXH_L4_B_0_1 | RXH_L4_B_2_3; - else if (v & FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN_F) - info->data = RXH_IP_SRC | RXH_IP_DST; - break; - case SCTP_V4_FLOW: - case AH_ESP_V4_FLOW: - case IPV4_FLOW: - if (v & FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN_F) - info->data = RXH_IP_SRC | RXH_IP_DST; - break; - case TCP_V6_FLOW: - if (v & FW_RSS_VI_CONFIG_CMD_IP6FOURTUPEN_F) - info->data = RXH_IP_SRC | RXH_IP_DST | - RXH_L4_B_0_1 | RXH_L4_B_2_3; - else if (v & FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN_F) - info->data = RXH_IP_SRC | RXH_IP_DST; - break; - case UDP_V6_FLOW: - if ((v & FW_RSS_VI_CONFIG_CMD_IP6FOURTUPEN_F) && - (v & FW_RSS_VI_CONFIG_CMD_UDPEN_F)) - info->data = RXH_IP_SRC | RXH_IP_DST | - RXH_L4_B_0_1 | RXH_L4_B_2_3; - else if (v & FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN_F) - info->data = RXH_IP_SRC | RXH_IP_DST; - break; - case SCTP_V6_FLOW: - case AH_ESP_V6_FLOW: - case IPV6_FLOW: - if (v & FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN_F) - info->data = RXH_IP_SRC | RXH_IP_DST; - break; - } - return 0; - } - case ETHTOOL_GRXRINGS: - info->data = pi->nqsets; - return 0; - } - return -EOPNOTSUPP; -} - -static const struct ethtool_ops cxgb_ethtool_ops = { - .get_settings = get_settings, - .set_settings = set_settings, - .get_drvinfo = get_drvinfo, - .get_msglevel = get_msglevel, - .set_msglevel = set_msglevel, - .get_ringparam = get_sge_param, - .set_ringparam = set_sge_param, - .get_coalesce = get_coalesce, - .set_coalesce = set_coalesce, - .get_eeprom_len = get_eeprom_len, - .get_eeprom = get_eeprom, - .set_eeprom = set_eeprom, - .get_pauseparam = get_pauseparam, - .set_pauseparam = set_pauseparam, - .get_link = ethtool_op_get_link, - .get_strings = get_strings, - .set_phys_id = identify_port, - .nway_reset = restart_autoneg, - .get_sset_count = get_sset_count, - .get_ethtool_stats = get_stats, - .get_regs_len = get_regs_len, - .get_regs = get_regs, - .get_wol = get_wol, - .set_wol = set_wol, - .get_rxnfc = get_rxnfc, - .get_rxfh_indir_size = get_rss_table_size, - .get_rxfh = get_rss_table, - .set_rxfh = set_rss_table, - .flash_device = set_flash, -}; - static int setup_debugfs(struct adapter *adap) { if (IS_ERR_OR_NULL(adap->debugfs_root)) @@ -4246,19 +2678,12 @@ static int cxgb_up(struct adapter *adap) static void cxgb_down(struct adapter *adapter) { - t4_intr_disable(adapter); cancel_work_sync(&adapter->tid_release_task); cancel_work_sync(&adapter->db_full_task); cancel_work_sync(&adapter->db_drop_task); adapter->tid_release_task_busy = false; adapter->tid_release_head = NULL; - if (adapter->flags & USING_MSIX) { - free_msix_queue_irqs(adapter); - free_irq(adapter->msix_info[0].vec, adapter); - } else - free_irq(adapter->pdev->irq, adapter); - quiesce_rx(adapter); t4_sge_stop(adapter); t4_free_sge_resources(adapter); adapter->flags &= ~FULL_INIT_DONE; @@ -4739,8 +3164,9 @@ static int adap_init1(struct adapter *adap, struct fw_caps_config_cmd *c) if (ret < 0) return ret; - ret = t4_cfg_pfvf(adap, adap->fn, adap->fn, 0, MAX_EGRQ, 64, MAX_INGQ, - 0, 0, 4, 0xf, 0xf, 16, FW_CMD_CAP_PF, FW_CMD_CAP_PF); + ret = t4_cfg_pfvf(adap, adap->fn, adap->fn, 0, adap->sge.egr_sz, 64, + MAX_INGQ, 0, 0, 4, 0xf, 0xf, 16, FW_CMD_CAP_PF, + FW_CMD_CAP_PF); if (ret < 0) return ret; @@ -5094,10 +3520,15 @@ static int adap_init0(struct adapter *adap) enum dev_state state; u32 params[7], val[7]; struct fw_caps_config_cmd caps_cmd; - struct fw_devlog_cmd devlog_cmd; - u32 devlog_meminfo; int reset = 1; + /* Grab Firmware Device Log parameters as early as possible so we have + * access to it for debugging, etc. + */ + ret = t4_init_devlog_params(adap); + if (ret < 0) + return ret; + /* Contact FW, advertising Master capability */ ret = t4_fw_hello(adap, adap->mbox, adap->mbox, MASTER_MAY, &state); if (ret < 0) { @@ -5175,30 +3606,6 @@ static int adap_init0(struct adapter *adap) if (ret < 0) goto bye; - /* Read firmware device log parameters. We really need to find a way - * to get these parameters initialized with some default values (which - * are likely to be correct) for the case where we either don't - * attache to the firmware or it's crashed when we probe the adapter. - * That way we'll still be able to perform early firmware startup - * debugging ... If the request to get the Firmware's Device Log - * parameters fails, we'll live so we don't make that a fatal error. - */ - memset(&devlog_cmd, 0, sizeof(devlog_cmd)); - devlog_cmd.op_to_write = htonl(FW_CMD_OP_V(FW_DEVLOG_CMD) | - FW_CMD_REQUEST_F | FW_CMD_READ_F); - devlog_cmd.retval_len16 = htonl(FW_LEN16(devlog_cmd)); - ret = t4_wr_mbox(adap, adap->mbox, &devlog_cmd, sizeof(devlog_cmd), - &devlog_cmd); - if (ret == 0) { - devlog_meminfo = - ntohl(devlog_cmd.memtype_devlog_memaddr16_devlog); - adap->params.devlog.memtype = - FW_DEVLOG_CMD_MEMTYPE_DEVLOG_G(devlog_meminfo); - adap->params.devlog.start = - FW_DEVLOG_CMD_MEMADDR16_DEVLOG_G(devlog_meminfo) << 4; - adap->params.devlog.size = ntohl(devlog_cmd.memsize_devlog); - } - /* * Find out what ports are available to us. Note that we need to do * this before calling adap_init0_no_config() since it needs nports @@ -5299,6 +3706,51 @@ static int adap_init0(struct adapter *adap) adap->tids.nftids = val[4] - val[3] + 1; adap->sge.ingr_start = val[5]; + /* qids (ingress/egress) returned from firmware can be anywhere + * in the range from EQ(IQFLINT)_START to EQ(IQFLINT)_END. + * Hence driver needs to allocate memory for this range to + * store the queue info. Get the highest IQFLINT/EQ index returned + * in FW_EQ_*_CMD.alloc command. + */ + params[0] = FW_PARAM_PFVF(EQ_END); + params[1] = FW_PARAM_PFVF(IQFLINT_END); + ret = t4_query_params(adap, adap->mbox, adap->fn, 0, 2, params, val); + if (ret < 0) + goto bye; + adap->sge.egr_sz = val[0] - adap->sge.egr_start + 1; + adap->sge.ingr_sz = val[1] - adap->sge.ingr_start + 1; + + adap->sge.egr_map = kcalloc(adap->sge.egr_sz, + sizeof(*adap->sge.egr_map), GFP_KERNEL); + if (!adap->sge.egr_map) { + ret = -ENOMEM; + goto bye; + } + + adap->sge.ingr_map = kcalloc(adap->sge.ingr_sz, + sizeof(*adap->sge.ingr_map), GFP_KERNEL); + if (!adap->sge.ingr_map) { + ret = -ENOMEM; + goto bye; + } + + /* Allocate the memory for the vaious egress queue bitmaps + * ie starving_fl and txq_maperr. + */ + adap->sge.starving_fl = kcalloc(BITS_TO_LONGS(adap->sge.egr_sz), + sizeof(long), GFP_KERNEL); + if (!adap->sge.starving_fl) { + ret = -ENOMEM; + goto bye; + } + + adap->sge.txq_maperr = kcalloc(BITS_TO_LONGS(adap->sge.egr_sz), + sizeof(long), GFP_KERNEL); + if (!adap->sge.txq_maperr) { + ret = -ENOMEM; + goto bye; + } + params[0] = FW_PARAM_PFVF(CLIP_START); params[1] = FW_PARAM_PFVF(CLIP_END); ret = t4_query_params(adap, adap->mbox, adap->fn, 0, 2, params, val); @@ -5507,6 +3959,10 @@ static int adap_init0(struct adapter *adap) * happened to HW/FW, stop issuing commands. */ bye: + kfree(adap->sge.egr_map); + kfree(adap->sge.ingr_map); + kfree(adap->sge.starving_fl); + kfree(adap->sge.txq_maperr); if (ret != -ETIMEDOUT && ret != -EIO) t4_fw_bye(adap, adap->mbox); return ret; @@ -5534,6 +3990,7 @@ static pci_ers_result_t eeh_err_detected(struct pci_dev *pdev, netif_carrier_off(dev); } spin_unlock(&adap->stats_lock); + disable_interrupts(adap); if (adap->flags & FULL_INIT_DONE) cxgb_down(adap); rtnl_unlock(); @@ -5636,7 +4093,7 @@ static inline void init_rspq(struct adapter *adap, struct sge_rspq *q, unsigned int size, unsigned int iqe_size) { q->adap = adap; - set_rspq_intr_params(q, us, cnt); + cxgb4_set_rspq_intr_params(q, us, cnt); q->iqe_len = iqe_size; q->size = size; } @@ -5942,6 +4399,10 @@ static void free_some_resources(struct adapter *adapter) t4_free_mem(adapter->l2t); t4_free_mem(adapter->tids.tid_tab); + kfree(adapter->sge.egr_map); + kfree(adapter->sge.ingr_map); + kfree(adapter->sge.starving_fl); + kfree(adapter->sge.txq_maperr); disable_msi(adapter); for_each_port(adapter, i) @@ -6127,7 +4588,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->dcbnl_ops = &cxgb4_dcb_ops; cxgb4_dcb_state_init(netdev); #endif - netdev->ethtool_ops = &cxgb_ethtool_ops; + cxgb4_set_ethtool_ops(netdev); } pci_set_drvdata(pdev, adapter); @@ -6267,6 +4728,8 @@ static void remove_one(struct pci_dev *pdev) if (is_offload(adapter)) detach_ulds(adapter); + disable_interrupts(adapter); + for_each_port(adapter, i) if (adapter->port[i]->reg_state == NETREG_REGISTERED) unregister_netdev(adapter->port[i]); diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index c46e7a938317..c438f3895c40 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -2239,7 +2239,7 @@ static void sge_rx_timer_cb(unsigned long data) struct adapter *adap = (struct adapter *)data; struct sge *s = &adap->sge; - for (i = 0; i < ARRAY_SIZE(s->starving_fl); i++) + for (i = 0; i < BITS_TO_LONGS(s->egr_sz); i++) for (m = s->starving_fl[i]; m; m &= m - 1) { struct sge_eth_rxq *rxq; unsigned int id = __ffs(m) + i * BITS_PER_LONG; @@ -2327,7 +2327,7 @@ static void sge_tx_timer_cb(unsigned long data) struct adapter *adap = (struct adapter *)data; struct sge *s = &adap->sge; - for (i = 0; i < ARRAY_SIZE(s->txq_maperr); i++) + for (i = 0; i < BITS_TO_LONGS(s->egr_sz); i++) for (m = s->txq_maperr[i]; m; m &= m - 1) { unsigned long id = __ffs(m) + i * BITS_PER_LONG; struct sge_ofld_txq *txq = s->egr_map[id]; @@ -2809,7 +2809,8 @@ void t4_free_sge_resources(struct adapter *adap) free_rspq_fl(adap, &adap->sge.intrq, NULL); /* clear the reverse egress queue map */ - memset(adap->sge.egr_map, 0, sizeof(adap->sge.egr_map)); + memset(adap->sge.egr_map, 0, + adap->sge.egr_sz * sizeof(*adap->sge.egr_map)); } void t4_sge_start(struct adapter *adap) diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index afbe1682ff48..5959e3ae72da 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -625,6 +625,734 @@ int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr, return 0; } +/** + * t4_get_regs_len - return the size of the chips register set + * @adapter: the adapter + * + * Returns the size of the chip's BAR0 register space. + */ +unsigned int t4_get_regs_len(struct adapter *adapter) +{ + unsigned int chip_version = CHELSIO_CHIP_VERSION(adapter->params.chip); + + switch (chip_version) { + case CHELSIO_T4: + return T4_REGMAP_SIZE; + + case CHELSIO_T5: + return T5_REGMAP_SIZE; + } + + dev_err(adapter->pdev_dev, + "Unsupported chip version %d\n", chip_version); + return 0; +} + +/** + * t4_get_regs - read chip registers into provided buffer + * @adap: the adapter + * @buf: register buffer + * @buf_size: size (in bytes) of register buffer + * + * If the provided register buffer isn't large enough for the chip's + * full register range, the register dump will be truncated to the + * register buffer's size. + */ +void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) +{ + static const unsigned int t4_reg_ranges[] = { + 0x1008, 0x1108, + 0x1180, 0x11b4, + 0x11fc, 0x123c, + 0x1300, 0x173c, + 0x1800, 0x18fc, + 0x3000, 0x30d8, + 0x30e0, 0x5924, + 0x5960, 0x59d4, + 0x5a00, 0x5af8, + 0x6000, 0x6098, + 0x6100, 0x6150, + 0x6200, 0x6208, + 0x6240, 0x6248, + 0x6280, 0x6338, + 0x6370, 0x638c, + 0x6400, 0x643c, + 0x6500, 0x6524, + 0x6a00, 0x6a38, + 0x6a60, 0x6a78, + 0x6b00, 0x6b84, + 0x6bf0, 0x6c84, + 0x6cf0, 0x6d84, + 0x6df0, 0x6e84, + 0x6ef0, 0x6f84, + 0x6ff0, 0x7084, + 0x70f0, 0x7184, + 0x71f0, 0x7284, + 0x72f0, 0x7384, + 0x73f0, 0x7450, + 0x7500, 0x7530, + 0x7600, 0x761c, + 0x7680, 0x76cc, + 0x7700, 0x7798, + 0x77c0, 0x77fc, + 0x7900, 0x79fc, + 0x7b00, 0x7c38, + 0x7d00, 0x7efc, + 0x8dc0, 0x8e1c, + 0x8e30, 0x8e78, + 0x8ea0, 0x8f6c, + 0x8fc0, 0x9074, + 0x90fc, 0x90fc, + 0x9400, 0x9458, + 0x9600, 0x96bc, + 0x9800, 0x9808, + 0x9820, 0x983c, + 0x9850, 0x9864, + 0x9c00, 0x9c6c, + 0x9c80, 0x9cec, + 0x9d00, 0x9d6c, + 0x9d80, 0x9dec, + 0x9e00, 0x9e6c, + 0x9e80, 0x9eec, + 0x9f00, 0x9f6c, + 0x9f80, 0x9fec, + 0xd004, 0xd03c, + 0xdfc0, 0xdfe0, + 0xe000, 0xea7c, + 0xf000, 0x11110, + 0x11118, 0x11190, + 0x19040, 0x1906c, + 0x19078, 0x19080, + 0x1908c, 0x19124, + 0x19150, 0x191b0, + 0x191d0, 0x191e8, + 0x19238, 0x1924c, + 0x193f8, 0x19474, + 0x19490, 0x194f8, + 0x19800, 0x19f30, + 0x1a000, 0x1a06c, + 0x1a0b0, 0x1a120, + 0x1a128, 0x1a138, + 0x1a190, 0x1a1c4, + 0x1a1fc, 0x1a1fc, + 0x1e040, 0x1e04c, + 0x1e284, 0x1e28c, + 0x1e2c0, 0x1e2c0, + 0x1e2e0, 0x1e2e0, + 0x1e300, 0x1e384, + 0x1e3c0, 0x1e3c8, + 0x1e440, 0x1e44c, + 0x1e684, 0x1e68c, + 0x1e6c0, 0x1e6c0, + 0x1e6e0, 0x1e6e0, + 0x1e700, 0x1e784, + 0x1e7c0, 0x1e7c8, + 0x1e840, 0x1e84c, + 0x1ea84, 0x1ea8c, + 0x1eac0, 0x1eac0, + 0x1eae0, 0x1eae0, + 0x1eb00, 0x1eb84, + 0x1ebc0, 0x1ebc8, + 0x1ec40, 0x1ec4c, + 0x1ee84, 0x1ee8c, + 0x1eec0, 0x1eec0, + 0x1eee0, 0x1eee0, + 0x1ef00, 0x1ef84, + 0x1efc0, 0x1efc8, + 0x1f040, 0x1f04c, + 0x1f284, 0x1f28c, + 0x1f2c0, 0x1f2c0, + 0x1f2e0, 0x1f2e0, + 0x1f300, 0x1f384, + 0x1f3c0, 0x1f3c8, + 0x1f440, 0x1f44c, + 0x1f684, 0x1f68c, + 0x1f6c0, 0x1f6c0, + 0x1f6e0, 0x1f6e0, + 0x1f700, 0x1f784, + 0x1f7c0, 0x1f7c8, + 0x1f840, 0x1f84c, + 0x1fa84, 0x1fa8c, + 0x1fac0, 0x1fac0, + 0x1fae0, 0x1fae0, + 0x1fb00, 0x1fb84, + 0x1fbc0, 0x1fbc8, + 0x1fc40, 0x1fc4c, + 0x1fe84, 0x1fe8c, + 0x1fec0, 0x1fec0, + 0x1fee0, 0x1fee0, + 0x1ff00, 0x1ff84, + 0x1ffc0, 0x1ffc8, + 0x20000, 0x2002c, + 0x20100, 0x2013c, + 0x20190, 0x201c8, + 0x20200, 0x20318, + 0x20400, 0x20528, + 0x20540, 0x20614, + 0x21000, 0x21040, + 0x2104c, 0x21060, + 0x210c0, 0x210ec, + 0x21200, 0x21268, + 0x21270, 0x21284, + 0x212fc, 0x21388, + 0x21400, 0x21404, + 0x21500, 0x21518, + 0x2152c, 0x2153c, + 0x21550, 0x21554, + 0x21600, 0x21600, + 0x21608, 0x21628, + 0x21630, 0x2163c, + 0x21700, 0x2171c, + 0x21780, 0x2178c, + 0x21800, 0x21c38, + 0x21c80, 0x21d7c, + 0x21e00, 0x21e04, + 0x22000, 0x2202c, + 0x22100, 0x2213c, + 0x22190, 0x221c8, + 0x22200, 0x22318, + 0x22400, 0x22528, + 0x22540, 0x22614, + 0x23000, 0x23040, + 0x2304c, 0x23060, + 0x230c0, 0x230ec, + 0x23200, 0x23268, + 0x23270, 0x23284, + 0x232fc, 0x23388, + 0x23400, 0x23404, + 0x23500, 0x23518, + 0x2352c, 0x2353c, + 0x23550, 0x23554, + 0x23600, 0x23600, + 0x23608, 0x23628, + 0x23630, 0x2363c, + 0x23700, 0x2371c, + 0x23780, 0x2378c, + 0x23800, 0x23c38, + 0x23c80, 0x23d7c, + 0x23e00, 0x23e04, + 0x24000, 0x2402c, + 0x24100, 0x2413c, + 0x24190, 0x241c8, + 0x24200, 0x24318, + 0x24400, 0x24528, + 0x24540, 0x24614, + 0x25000, 0x25040, + 0x2504c, 0x25060, + 0x250c0, 0x250ec, + 0x25200, 0x25268, + 0x25270, 0x25284, + 0x252fc, 0x25388, + 0x25400, 0x25404, + 0x25500, 0x25518, + 0x2552c, 0x2553c, + 0x25550, 0x25554, + 0x25600, 0x25600, + 0x25608, 0x25628, + 0x25630, 0x2563c, + 0x25700, 0x2571c, + 0x25780, 0x2578c, + 0x25800, 0x25c38, + 0x25c80, 0x25d7c, + 0x25e00, 0x25e04, + 0x26000, 0x2602c, + 0x26100, 0x2613c, + 0x26190, 0x261c8, + 0x26200, 0x26318, + 0x26400, 0x26528, + 0x26540, 0x26614, + 0x27000, 0x27040, + 0x2704c, 0x27060, + 0x270c0, 0x270ec, + 0x27200, 0x27268, + 0x27270, 0x27284, + 0x272fc, 0x27388, + 0x27400, 0x27404, + 0x27500, 0x27518, + 0x2752c, 0x2753c, + 0x27550, 0x27554, + 0x27600, 0x27600, + 0x27608, 0x27628, + 0x27630, 0x2763c, + 0x27700, 0x2771c, + 0x27780, 0x2778c, + 0x27800, 0x27c38, + 0x27c80, 0x27d7c, + 0x27e00, 0x27e04 + }; + + static const unsigned int t5_reg_ranges[] = { + 0x1008, 0x1148, + 0x1180, 0x11b4, + 0x11fc, 0x123c, + 0x1280, 0x173c, + 0x1800, 0x18fc, + 0x3000, 0x3028, + 0x3060, 0x30d8, + 0x30e0, 0x30fc, + 0x3140, 0x357c, + 0x35a8, 0x35cc, + 0x35ec, 0x35ec, + 0x3600, 0x5624, + 0x56cc, 0x575c, + 0x580c, 0x5814, + 0x5890, 0x58bc, + 0x5940, 0x59dc, + 0x59fc, 0x5a18, + 0x5a60, 0x5a9c, + 0x5b9c, 0x5bfc, + 0x6000, 0x6040, + 0x6058, 0x614c, + 0x7700, 0x7798, + 0x77c0, 0x78fc, + 0x7b00, 0x7c54, + 0x7d00, 0x7efc, + 0x8dc0, 0x8de0, + 0x8df8, 0x8e84, + 0x8ea0, 0x8f84, + 0x8fc0, 0x90f8, + 0x9400, 0x9470, + 0x9600, 0x96f4, + 0x9800, 0x9808, + 0x9820, 0x983c, + 0x9850, 0x9864, + 0x9c00, 0x9c6c, + 0x9c80, 0x9cec, + 0x9d00, 0x9d6c, + 0x9d80, 0x9dec, + 0x9e00, 0x9e6c, + 0x9e80, 0x9eec, + 0x9f00, 0x9f6c, + 0x9f80, 0xa020, + 0xd004, 0xd03c, + 0xdfc0, 0xdfe0, + 0xe000, 0x11088, + 0x1109c, 0x11110, + 0x11118, 0x1117c, + 0x11190, 0x11204, + 0x19040, 0x1906c, + 0x19078, 0x19080, + 0x1908c, 0x19124, + 0x19150, 0x191b0, + 0x191d0, 0x191e8, + 0x19238, 0x19290, + 0x193f8, 0x19474, + 0x19490, 0x194cc, + 0x194f0, 0x194f8, + 0x19c00, 0x19c60, + 0x19c94, 0x19e10, + 0x19e50, 0x19f34, + 0x19f40, 0x19f50, + 0x19f90, 0x19fe4, + 0x1a000, 0x1a06c, + 0x1a0b0, 0x1a120, + 0x1a128, 0x1a138, + 0x1a190, 0x1a1c4, + 0x1a1fc, 0x1a1fc, + 0x1e008, 0x1e00c, + 0x1e040, 0x1e04c, + 0x1e284, 0x1e290, + 0x1e2c0, 0x1e2c0, + 0x1e2e0, 0x1e2e0, + 0x1e300, 0x1e384, + 0x1e3c0, 0x1e3c8, + 0x1e408, 0x1e40c, + 0x1e440, 0x1e44c, + 0x1e684, 0x1e690, + 0x1e6c0, 0x1e6c0, + 0x1e6e0, 0x1e6e0, + 0x1e700, 0x1e784, + 0x1e7c0, 0x1e7c8, + 0x1e808, 0x1e80c, + 0x1e840, 0x1e84c, + 0x1ea84, 0x1ea90, + 0x1eac0, 0x1eac0, + 0x1eae0, 0x1eae0, + 0x1eb00, 0x1eb84, + 0x1ebc0, 0x1ebc8, + 0x1ec08, 0x1ec0c, + 0x1ec40, 0x1ec4c, + 0x1ee84, 0x1ee90, + 0x1eec0, 0x1eec0, + 0x1eee0, 0x1eee0, + 0x1ef00, 0x1ef84, + 0x1efc0, 0x1efc8, + 0x1f008, 0x1f00c, + 0x1f040, 0x1f04c, + 0x1f284, 0x1f290, + 0x1f2c0, 0x1f2c0, + 0x1f2e0, 0x1f2e0, + 0x1f300, 0x1f384, + 0x1f3c0, 0x1f3c8, + 0x1f408, 0x1f40c, + 0x1f440, 0x1f44c, + 0x1f684, 0x1f690, + 0x1f6c0, 0x1f6c0, + 0x1f6e0, 0x1f6e0, + 0x1f700, 0x1f784, + 0x1f7c0, 0x1f7c8, + 0x1f808, 0x1f80c, + 0x1f840, 0x1f84c, + 0x1fa84, 0x1fa90, + 0x1fac0, 0x1fac0, + 0x1fae0, 0x1fae0, + 0x1fb00, 0x1fb84, + 0x1fbc0, 0x1fbc8, + 0x1fc08, 0x1fc0c, + 0x1fc40, 0x1fc4c, + 0x1fe84, 0x1fe90, + 0x1fec0, 0x1fec0, + 0x1fee0, 0x1fee0, + 0x1ff00, 0x1ff84, + 0x1ffc0, 0x1ffc8, + 0x30000, 0x30030, + 0x30100, 0x30144, + 0x30190, 0x301d0, + 0x30200, 0x30318, + 0x30400, 0x3052c, + 0x30540, 0x3061c, + 0x30800, 0x30834, + 0x308c0, 0x30908, + 0x30910, 0x309ac, + 0x30a00, 0x30a04, + 0x30a0c, 0x30a2c, + 0x30a44, 0x30a50, + 0x30a74, 0x30c24, + 0x30d08, 0x30d14, + 0x30d1c, 0x30d20, + 0x30d3c, 0x30d50, + 0x31200, 0x3120c, + 0x31220, 0x31220, + 0x31240, 0x31240, + 0x31600, 0x31600, + 0x31608, 0x3160c, + 0x31a00, 0x31a1c, + 0x31e04, 0x31e20, + 0x31e38, 0x31e3c, + 0x31e80, 0x31e80, + 0x31e88, 0x31ea8, + 0x31eb0, 0x31eb4, + 0x31ec8, 0x31ed4, + 0x31fb8, 0x32004, + 0x32208, 0x3223c, + 0x32600, 0x32630, + 0x32a00, 0x32abc, + 0x32b00, 0x32b70, + 0x33000, 0x33048, + 0x33060, 0x3309c, + 0x330f0, 0x33148, + 0x33160, 0x3319c, + 0x331f0, 0x332e4, + 0x332f8, 0x333e4, + 0x333f8, 0x33448, + 0x33460, 0x3349c, + 0x334f0, 0x33548, + 0x33560, 0x3359c, + 0x335f0, 0x336e4, + 0x336f8, 0x337e4, + 0x337f8, 0x337fc, + 0x33814, 0x33814, + 0x3382c, 0x3382c, + 0x33880, 0x3388c, + 0x338e8, 0x338ec, + 0x33900, 0x33948, + 0x33960, 0x3399c, + 0x339f0, 0x33ae4, + 0x33af8, 0x33b10, + 0x33b28, 0x33b28, + 0x33b3c, 0x33b50, + 0x33bf0, 0x33c10, + 0x33c28, 0x33c28, + 0x33c3c, 0x33c50, + 0x33cf0, 0x33cfc, + 0x34000, 0x34030, + 0x34100, 0x34144, + 0x34190, 0x341d0, + 0x34200, 0x34318, + 0x34400, 0x3452c, + 0x34540, 0x3461c, + 0x34800, 0x34834, + 0x348c0, 0x34908, + 0x34910, 0x349ac, + 0x34a00, 0x34a04, + 0x34a0c, 0x34a2c, + 0x34a44, 0x34a50, + 0x34a74, 0x34c24, + 0x34d08, 0x34d14, + 0x34d1c, 0x34d20, + 0x34d3c, 0x34d50, + 0x35200, 0x3520c, + 0x35220, 0x35220, + 0x35240, 0x35240, + 0x35600, 0x35600, + 0x35608, 0x3560c, + 0x35a00, 0x35a1c, + 0x35e04, 0x35e20, + 0x35e38, 0x35e3c, + 0x35e80, 0x35e80, + 0x35e88, 0x35ea8, + 0x35eb0, 0x35eb4, + 0x35ec8, 0x35ed4, + 0x35fb8, 0x36004, + 0x36208, 0x3623c, + 0x36600, 0x36630, + 0x36a00, 0x36abc, + 0x36b00, 0x36b70, + 0x37000, 0x37048, + 0x37060, 0x3709c, + 0x370f0, 0x37148, + 0x37160, 0x3719c, + 0x371f0, 0x372e4, + 0x372f8, 0x373e4, + 0x373f8, 0x37448, + 0x37460, 0x3749c, + 0x374f0, 0x37548, + 0x37560, 0x3759c, + 0x375f0, 0x376e4, + 0x376f8, 0x377e4, + 0x377f8, 0x377fc, + 0x37814, 0x37814, + 0x3782c, 0x3782c, + 0x37880, 0x3788c, + 0x378e8, 0x378ec, + 0x37900, 0x37948, + 0x37960, 0x3799c, + 0x379f0, 0x37ae4, + 0x37af8, 0x37b10, + 0x37b28, 0x37b28, + 0x37b3c, 0x37b50, + 0x37bf0, 0x37c10, + 0x37c28, 0x37c28, + 0x37c3c, 0x37c50, + 0x37cf0, 0x37cfc, + 0x38000, 0x38030, + 0x38100, 0x38144, + 0x38190, 0x381d0, + 0x38200, 0x38318, + 0x38400, 0x3852c, + 0x38540, 0x3861c, + 0x38800, 0x38834, + 0x388c0, 0x38908, + 0x38910, 0x389ac, + 0x38a00, 0x38a04, + 0x38a0c, 0x38a2c, + 0x38a44, 0x38a50, + 0x38a74, 0x38c24, + 0x38d08, 0x38d14, + 0x38d1c, 0x38d20, + 0x38d3c, 0x38d50, + 0x39200, 0x3920c, + 0x39220, 0x39220, + 0x39240, 0x39240, + 0x39600, 0x39600, + 0x39608, 0x3960c, + 0x39a00, 0x39a1c, + 0x39e04, 0x39e20, + 0x39e38, 0x39e3c, + 0x39e80, 0x39e80, + 0x39e88, 0x39ea8, + 0x39eb0, 0x39eb4, + 0x39ec8, 0x39ed4, + 0x39fb8, 0x3a004, + 0x3a208, 0x3a23c, + 0x3a600, 0x3a630, + 0x3aa00, 0x3aabc, + 0x3ab00, 0x3ab70, + 0x3b000, 0x3b048, + 0x3b060, 0x3b09c, + 0x3b0f0, 0x3b148, + 0x3b160, 0x3b19c, + 0x3b1f0, 0x3b2e4, + 0x3b2f8, 0x3b3e4, + 0x3b3f8, 0x3b448, + 0x3b460, 0x3b49c, + 0x3b4f0, 0x3b548, + 0x3b560, 0x3b59c, + 0x3b5f0, 0x3b6e4, + 0x3b6f8, 0x3b7e4, + 0x3b7f8, 0x3b7fc, + 0x3b814, 0x3b814, + 0x3b82c, 0x3b82c, + 0x3b880, 0x3b88c, + 0x3b8e8, 0x3b8ec, + 0x3b900, 0x3b948, + 0x3b960, 0x3b99c, + 0x3b9f0, 0x3bae4, + 0x3baf8, 0x3bb10, + 0x3bb28, 0x3bb28, + 0x3bb3c, 0x3bb50, + 0x3bbf0, 0x3bc10, + 0x3bc28, 0x3bc28, + 0x3bc3c, 0x3bc50, + 0x3bcf0, 0x3bcfc, + 0x3c000, 0x3c030, + 0x3c100, 0x3c144, + 0x3c190, 0x3c1d0, + 0x3c200, 0x3c318, + 0x3c400, 0x3c52c, + 0x3c540, 0x3c61c, + 0x3c800, 0x3c834, + 0x3c8c0, 0x3c908, + 0x3c910, 0x3c9ac, + 0x3ca00, 0x3ca04, + 0x3ca0c, 0x3ca2c, + 0x3ca44, 0x3ca50, + 0x3ca74, 0x3cc24, + 0x3cd08, 0x3cd14, + 0x3cd1c, 0x3cd20, + 0x3cd3c, 0x3cd50, + 0x3d200, 0x3d20c, + 0x3d220, 0x3d220, + 0x3d240, 0x3d240, + 0x3d600, 0x3d600, + 0x3d608, 0x3d60c, + 0x3da00, 0x3da1c, + 0x3de04, 0x3de20, + 0x3de38, 0x3de3c, + 0x3de80, 0x3de80, + 0x3de88, 0x3dea8, + 0x3deb0, 0x3deb4, + 0x3dec8, 0x3ded4, + 0x3dfb8, 0x3e004, + 0x3e208, 0x3e23c, + 0x3e600, 0x3e630, + 0x3ea00, 0x3eabc, + 0x3eb00, 0x3eb70, + 0x3f000, 0x3f048, + 0x3f060, 0x3f09c, + 0x3f0f0, 0x3f148, + 0x3f160, 0x3f19c, + 0x3f1f0, 0x3f2e4, + 0x3f2f8, 0x3f3e4, + 0x3f3f8, 0x3f448, + 0x3f460, 0x3f49c, + 0x3f4f0, 0x3f548, + 0x3f560, 0x3f59c, + 0x3f5f0, 0x3f6e4, + 0x3f6f8, 0x3f7e4, + 0x3f7f8, 0x3f7fc, + 0x3f814, 0x3f814, + 0x3f82c, 0x3f82c, + 0x3f880, 0x3f88c, + 0x3f8e8, 0x3f8ec, + 0x3f900, 0x3f948, + 0x3f960, 0x3f99c, + 0x3f9f0, 0x3fae4, + 0x3faf8, 0x3fb10, + 0x3fb28, 0x3fb28, + 0x3fb3c, 0x3fb50, + 0x3fbf0, 0x3fc10, + 0x3fc28, 0x3fc28, + 0x3fc3c, 0x3fc50, + 0x3fcf0, 0x3fcfc, + 0x40000, 0x4000c, + 0x40040, 0x40068, + 0x40080, 0x40144, + 0x40180, 0x4018c, + 0x40200, 0x40298, + 0x402ac, 0x4033c, + 0x403f8, 0x403fc, + 0x41304, 0x413c4, + 0x41400, 0x4141c, + 0x41480, 0x414d0, + 0x44000, 0x44078, + 0x440c0, 0x44278, + 0x442c0, 0x44478, + 0x444c0, 0x44678, + 0x446c0, 0x44878, + 0x448c0, 0x449fc, + 0x45000, 0x45068, + 0x45080, 0x45084, + 0x450a0, 0x450b0, + 0x45200, 0x45268, + 0x45280, 0x45284, + 0x452a0, 0x452b0, + 0x460c0, 0x460e4, + 0x47000, 0x4708c, + 0x47200, 0x47250, + 0x47400, 0x47420, + 0x47600, 0x47618, + 0x47800, 0x47814, + 0x48000, 0x4800c, + 0x48040, 0x48068, + 0x48080, 0x48144, + 0x48180, 0x4818c, + 0x48200, 0x48298, + 0x482ac, 0x4833c, + 0x483f8, 0x483fc, + 0x49304, 0x493c4, + 0x49400, 0x4941c, + 0x49480, 0x494d0, + 0x4c000, 0x4c078, + 0x4c0c0, 0x4c278, + 0x4c2c0, 0x4c478, + 0x4c4c0, 0x4c678, + 0x4c6c0, 0x4c878, + 0x4c8c0, 0x4c9fc, + 0x4d000, 0x4d068, + 0x4d080, 0x4d084, + 0x4d0a0, 0x4d0b0, + 0x4d200, 0x4d268, + 0x4d280, 0x4d284, + 0x4d2a0, 0x4d2b0, + 0x4e0c0, 0x4e0e4, + 0x4f000, 0x4f08c, + 0x4f200, 0x4f250, + 0x4f400, 0x4f420, + 0x4f600, 0x4f618, + 0x4f800, 0x4f814, + 0x50000, 0x500cc, + 0x50400, 0x50400, + 0x50800, 0x508cc, + 0x50c00, 0x50c00, + 0x51000, 0x5101c, + 0x51300, 0x51308, + }; + + u32 *buf_end = (u32 *)((char *)buf + buf_size); + const unsigned int *reg_ranges; + int reg_ranges_size, range; + unsigned int chip_version = CHELSIO_CHIP_VERSION(adap->params.chip); + + /* Select the right set of register ranges to dump depending on the + * adapter chip type. + */ + switch (chip_version) { + case CHELSIO_T4: + reg_ranges = t4_reg_ranges; + reg_ranges_size = ARRAY_SIZE(t4_reg_ranges); + break; + + case CHELSIO_T5: + reg_ranges = t5_reg_ranges; + reg_ranges_size = ARRAY_SIZE(t5_reg_ranges); + break; + + default: + dev_err(adap->pdev_dev, + "Unsupported chip version %d\n", chip_version); + return; + } + + /* Clear the register buffer and insert the appropriate register + * values selected by the above register ranges. + */ + memset(buf, 0, buf_size); + for (range = 0; range < reg_ranges_size; range += 2) { + unsigned int reg = reg_ranges[range]; + unsigned int last_reg = reg_ranges[range + 1]; + u32 *bufp = (u32 *)((char *)buf + reg); + + /* Iterate across the register range filling in the register + * buffer but don't write past the end of the register buffer. + */ + while (reg <= last_reg && bufp < buf_end) { + *bufp++ = t4_read_reg(adap, reg); + reg += sizeof(u32); + } + } +} + #define EEPROM_STAT_ADDR 0x7bfc #define VPD_BASE 0x400 #define VPD_BASE_OLD 0 @@ -4459,6 +5187,59 @@ int cxgb4_t4_bar2_sge_qregs(struct adapter *adapter, } /** + * t4_init_devlog_params - initialize adapter->params.devlog + * @adap: the adapter + * + * Initialize various fields of the adapter's Firmware Device Log + * Parameters structure. + */ +int t4_init_devlog_params(struct adapter *adap) +{ + struct devlog_params *dparams = &adap->params.devlog; + u32 pf_dparams; + unsigned int devlog_meminfo; + struct fw_devlog_cmd devlog_cmd; + int ret; + + /* If we're dealing with newer firmware, the Device Log Paramerters + * are stored in a designated register which allows us to access the + * Device Log even if we can't talk to the firmware. + */ + pf_dparams = + t4_read_reg(adap, PCIE_FW_REG(PCIE_FW_PF_A, PCIE_FW_PF_DEVLOG)); + if (pf_dparams) { + unsigned int nentries, nentries128; + + dparams->memtype = PCIE_FW_PF_DEVLOG_MEMTYPE_G(pf_dparams); + dparams->start = PCIE_FW_PF_DEVLOG_ADDR16_G(pf_dparams) << 4; + + nentries128 = PCIE_FW_PF_DEVLOG_NENTRIES128_G(pf_dparams); + nentries = (nentries128 + 1) * 128; + dparams->size = nentries * sizeof(struct fw_devlog_e); + + return 0; + } + + /* Otherwise, ask the firmware for it's Device Log Parameters. + */ + memset(&devlog_cmd, 0, sizeof(devlog_cmd)); + devlog_cmd.op_to_write = htonl(FW_CMD_OP_V(FW_DEVLOG_CMD) | + FW_CMD_REQUEST_F | FW_CMD_READ_F); + devlog_cmd.retval_len16 = htonl(FW_LEN16(devlog_cmd)); + ret = t4_wr_mbox(adap, adap->mbox, &devlog_cmd, sizeof(devlog_cmd), + &devlog_cmd); + if (ret) + return ret; + + devlog_meminfo = ntohl(devlog_cmd.memtype_devlog_memaddr16_devlog); + dparams->memtype = FW_DEVLOG_CMD_MEMTYPE_DEVLOG_G(devlog_meminfo); + dparams->start = FW_DEVLOG_CMD_MEMADDR16_DEVLOG_G(devlog_meminfo) << 4; + dparams->size = ntohl(devlog_cmd.memsize_devlog); + + return 0; +} + +/** * t4_init_sge_params - initialize adap->params.sge * @adapter: the adapter * diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h index 231a725f6d5d..326674b19983 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h @@ -63,6 +63,8 @@ #define MC_BIST_STATUS_REG(reg_addr, idx) ((reg_addr) + (idx) * 4) #define EDC_BIST_STATUS_REG(reg_addr, idx) ((reg_addr) + (idx) * 4) +#define PCIE_FW_REG(reg_addr, idx) ((reg_addr) + (idx) * 4) + #define SGE_PF_KDOORBELL_A 0x0 #define QID_S 15 @@ -707,6 +709,7 @@ #define PFNUM_V(x) ((x) << PFNUM_S) #define PCIE_FW_A 0x30b8 +#define PCIE_FW_PF_A 0x30bc #define PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS_A 0x5908 diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h index d136ca6a0c8a..03fbfd1fb3df 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h @@ -101,7 +101,7 @@ enum fw_wr_opcodes { FW_RI_BIND_MW_WR = 0x18, FW_RI_FR_NSMR_WR = 0x19, FW_RI_INV_LSTAG_WR = 0x1a, - FW_LASTC2E_WR = 0x40 + FW_LASTC2E_WR = 0x70 }; struct fw_wr_hdr { @@ -993,6 +993,7 @@ enum fw_memtype_cf { FW_MEMTYPE_CF_EXTMEM = 0x2, FW_MEMTYPE_CF_FLASH = 0x4, FW_MEMTYPE_CF_INTERNAL = 0x5, + FW_MEMTYPE_CF_EXTMEM1 = 0x6, }; struct fw_caps_config_cmd { @@ -1035,6 +1036,7 @@ enum fw_params_mnem { FW_PARAMS_MNEM_PFVF = 2, /* function params */ FW_PARAMS_MNEM_REG = 3, /* limited register access */ FW_PARAMS_MNEM_DMAQ = 4, /* dma queue params */ + FW_PARAMS_MNEM_CHNET = 5, /* chnet params */ FW_PARAMS_MNEM_LAST }; @@ -3102,7 +3104,8 @@ enum fw_devlog_facility { FW_DEVLOG_FACILITY_FCOE = 0x2E, FW_DEVLOG_FACILITY_FOISCSI = 0x30, FW_DEVLOG_FACILITY_FOFCOE = 0x32, - FW_DEVLOG_FACILITY_MAX = 0x32, + FW_DEVLOG_FACILITY_CHNET = 0x34, + FW_DEVLOG_FACILITY_MAX = 0x34, }; /* log message format */ @@ -3139,4 +3142,36 @@ struct fw_devlog_cmd { (((x) >> FW_DEVLOG_CMD_MEMADDR16_DEVLOG_S) & \ FW_DEVLOG_CMD_MEMADDR16_DEVLOG_M) +/* P C I E F W P F 7 R E G I S T E R */ + +/* PF7 stores the Firmware Device Log parameters which allows Host Drivers to + * access the "devlog" which needing to contact firmware. The encoding is + * mostly the same as that returned by the DEVLOG command except for the size + * which is encoded as the number of entries in multiples-1 of 128 here rather + * than the memory size as is done in the DEVLOG command. Thus, 0 means 128 + * and 15 means 2048. This of course in turn constrains the allowed values + * for the devlog size ... + */ +#define PCIE_FW_PF_DEVLOG 7 + +#define PCIE_FW_PF_DEVLOG_NENTRIES128_S 28 +#define PCIE_FW_PF_DEVLOG_NENTRIES128_M 0xf +#define PCIE_FW_PF_DEVLOG_NENTRIES128_V(x) \ + ((x) << PCIE_FW_PF_DEVLOG_NENTRIES128_S) +#define PCIE_FW_PF_DEVLOG_NENTRIES128_G(x) \ + (((x) >> PCIE_FW_PF_DEVLOG_NENTRIES128_S) & \ + PCIE_FW_PF_DEVLOG_NENTRIES128_M) + +#define PCIE_FW_PF_DEVLOG_ADDR16_S 4 +#define PCIE_FW_PF_DEVLOG_ADDR16_M 0xffffff +#define PCIE_FW_PF_DEVLOG_ADDR16_V(x) ((x) << PCIE_FW_PF_DEVLOG_ADDR16_S) +#define PCIE_FW_PF_DEVLOG_ADDR16_G(x) \ + (((x) >> PCIE_FW_PF_DEVLOG_ADDR16_S) & PCIE_FW_PF_DEVLOG_ADDR16_M) + +#define PCIE_FW_PF_DEVLOG_MEMTYPE_S 0 +#define PCIE_FW_PF_DEVLOG_MEMTYPE_M 0xf +#define PCIE_FW_PF_DEVLOG_MEMTYPE_V(x) ((x) << PCIE_FW_PF_DEVLOG_MEMTYPE_S) +#define PCIE_FW_PF_DEVLOG_MEMTYPE_G(x) \ + (((x) >> PCIE_FW_PF_DEVLOG_MEMTYPE_S) & PCIE_FW_PF_DEVLOG_MEMTYPE_M) + #endif /* _T4FW_INTERFACE_H_ */ diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4fw_version.h b/drivers/net/ethernet/chelsio/cxgb4/t4fw_version.h index e2bd3f747858..b9d1cbac0eee 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4fw_version.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4fw_version.h @@ -36,13 +36,13 @@ #define __T4FW_VERSION_H__ #define T4FW_VERSION_MAJOR 0x01 -#define T4FW_VERSION_MINOR 0x0C -#define T4FW_VERSION_MICRO 0x19 +#define T4FW_VERSION_MINOR 0x0D +#define T4FW_VERSION_MICRO 0x20 #define T4FW_VERSION_BUILD 0x00 #define T5FW_VERSION_MAJOR 0x01 -#define T5FW_VERSION_MINOR 0x0C -#define T5FW_VERSION_MICRO 0x19 +#define T5FW_VERSION_MINOR 0x0D +#define T5FW_VERSION_MICRO 0x20 #define T5FW_VERSION_BUILD 0x00 #endif diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c index 5ba14b32c370..7715982230e5 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c @@ -1004,7 +1004,7 @@ static inline void ring_tx_db(struct adapter *adapter, struct sge_txq *tq, ? (tq->pidx - 1) : (tq->size - 1)); __be64 *src = (__be64 *)&tq->desc[index]; - __be64 __iomem *dst = (__be64 *)(tq->bar2_addr + + __be64 __iomem *dst = (__be64 __iomem *)(tq->bar2_addr + SGE_UDB_WCDOORBELL); unsigned int count = EQ_UNIT / sizeof(__be64); @@ -1018,7 +1018,11 @@ static inline void ring_tx_db(struct adapter *adapter, struct sge_txq *tq, * DMA. */ while (count) { - writeq(*src, dst); + /* the (__force u64) is because the compiler + * doesn't understand the endian swizzling + * going on + */ + writeq((__force u64)*src, dst); src++; dst++; count--; @@ -1252,8 +1256,8 @@ int t4vf_eth_xmit(struct sk_buff *skb, struct net_device *dev) BUG_ON(DIV_ROUND_UP(ETHTXQ_MAX_HDR, TXD_PER_EQ_UNIT) > 1); wr = (void *)&txq->q.desc[txq->q.pidx]; wr->equiq_to_len16 = cpu_to_be32(wr_mid); - wr->r3[0] = cpu_to_be64(0); - wr->r3[1] = cpu_to_be64(0); + wr->r3[0] = cpu_to_be32(0); + wr->r3[1] = cpu_to_be32(0); skb_copy_from_linear_data(skb, (void *)wr->ethmacdst, fw_hdr_copy_len); end = (u64 *)wr + flits; diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c index c21e2e954ad8..966ee900ed00 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c @@ -210,10 +210,10 @@ int t4vf_wr_mbox_core(struct adapter *adapter, const void *cmd, int size, if (rpl) { /* request bit in high-order BE word */ - WARN_ON((be32_to_cpu(*(const u32 *)cmd) + WARN_ON((be32_to_cpu(*(const __be32 *)cmd) & FW_CMD_REQUEST_F) == 0); get_mbox_rpl(adapter, rpl, size, mbox_data); - WARN_ON((be32_to_cpu(*(u32 *)rpl) + WARN_ON((be32_to_cpu(*(__be32 *)rpl) & FW_CMD_REQUEST_F) != 0); } t4_write_reg(adapter, mbox_ctl, @@ -484,7 +484,7 @@ int t4_bar2_sge_qregs(struct adapter *adapter, * o The BAR2 Queue ID. * o The BAR2 Queue ID Offset into the BAR2 page. */ - bar2_page_offset = ((qid >> qpp_shift) << page_shift); + bar2_page_offset = ((u64)(qid >> qpp_shift) << page_shift); bar2_qid = qid & qpp_mask; bar2_qid_offset = bar2_qid * SGE_UDB_SIZE; diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 78e1ce09b1ab..f6a3a7abd468 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -1954,6 +1954,7 @@ static int fec_enet_mii_init(struct platform_device *pdev) struct fec_enet_private *fep = netdev_priv(ndev); struct device_node *node; int err = -ENXIO, i; + u32 mii_speed, holdtime; /* * The i.MX28 dual fec interfaces are not equal. @@ -1991,10 +1992,33 @@ static int fec_enet_mii_init(struct platform_device *pdev) * Reference Manual has an error on this, and gets fixed on i.MX6Q * document. */ - fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 5000000); + mii_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 5000000); if (fep->quirks & FEC_QUIRK_ENET_MAC) - fep->phy_speed--; - fep->phy_speed <<= 1; + mii_speed--; + if (mii_speed > 63) { + dev_err(&pdev->dev, + "fec clock (%lu) to fast to get right mii speed\n", + clk_get_rate(fep->clk_ipg)); + err = -EINVAL; + goto err_out; + } + + /* + * The i.MX28 and i.MX6 types have another filed in the MSCR (aka + * MII_SPEED) register that defines the MDIO output hold time. Earlier + * versions are RAZ there, so just ignore the difference and write the + * register always. + * The minimal hold time according to IEE802.3 (clause 22) is 10 ns. + * HOLDTIME + 1 is the number of clk cycles the fec is holding the + * output. + * The HOLDTIME bitfield takes values between 0 and 7 (inclusive). + * Given that ceil(clkrate / 5000000) <= 64, the calculation for + * holdtime cannot result in a value greater than 3. + */ + holdtime = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 100000000) - 1; + + fep->phy_speed = mii_speed << 1 | holdtime << 8; + writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); fep->mii_bus = mdiobus_alloc(); diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c index bfdccbd58be0..4dd40e057f40 100644 --- a/drivers/net/ethernet/freescale/ucc_geth.c +++ b/drivers/net/ethernet/freescale/ucc_geth.c @@ -3893,6 +3893,9 @@ static int ucc_geth_probe(struct platform_device* ofdev) ugeth->phy_interface = phy_interface; ugeth->max_speed = max_speed; + /* Carrier starts down, phylib will bring it up */ + netif_carrier_off(dev); + err = register_netdev(dev); if (err) { if (netif_msg_probe(ugeth)) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 1c8bd7c152c2..33c35d3b7420 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -628,6 +628,7 @@ extern const char i40e_driver_name[]; extern const char i40e_driver_version_str[]; void i40e_do_reset_safe(struct i40e_pf *pf, u32 reset_flags); void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags); +struct i40e_vsi *i40e_find_vsi_from_id(struct i40e_pf *pf, u16 id); void i40e_update_stats(struct i40e_vsi *vsi); void i40e_update_eth_stats(struct i40e_vsi *vsi); struct rtnl_link_stats64 *i40e_get_vsi_stats_struct(struct i40e_vsi *vsi); diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c b/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c index 400fb28db576..bd5079d5c1b6 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c @@ -178,6 +178,10 @@ void i40e_dcbnl_set_all(struct i40e_vsi *vsi) if (!(pf->flags & I40E_FLAG_DCB_ENABLED)) return; + /* MFP mode but not an iSCSI PF so return */ + if ((pf->flags & I40E_FLAG_MFP_ENABLED) && !(pf->hw.func_caps.iscsi)) + return; + dcbxcfg = &hw->local_dcbx_config; /* Set up all the App TLVs if DCBx is negotiated */ @@ -282,6 +286,10 @@ void i40e_dcbnl_flush_apps(struct i40e_pf *pf, struct i40e_dcb_app_priority_table app; int i; + /* MFP mode but not an iSCSI PF so return */ + if ((pf->flags & I40E_FLAG_MFP_ENABLED) && !(pf->hw.func_caps.iscsi)) + return; + for (i = 0; i < old_cfg->numapps; i++) { app = old_cfg->app[i]; /* The APP is not available anymore delete it */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_fcoe.c b/drivers/net/ethernet/intel/i40e/i40e_fcoe.c index 1ca48458e668..1803afeef23e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_fcoe.c +++ b/drivers/net/ethernet/intel/i40e/i40e_fcoe.c @@ -1306,8 +1306,7 @@ static void i40e_fcoe_tx_map(struct i40e_ring *tx_ring, /* MACLEN is ether header length in words not bytes */ td_offset |= (maclen >> 1) << I40E_TX_DESC_LENGTH_MACLEN_SHIFT; - return i40e_tx_map(tx_ring, skb, first, tx_flags, hdr_len, - td_cmd, td_offset); + i40e_tx_map(tx_ring, skb, first, tx_flags, hdr_len, td_cmd, td_offset); } /** diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 845bceeda645..63de3f4b7a94 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -38,8 +38,8 @@ static const char i40e_driver_string[] = #define DRV_KERN "-k" #define DRV_VERSION_MAJOR 1 -#define DRV_VERSION_MINOR 2 -#define DRV_VERSION_BUILD 43 +#define DRV_VERSION_MINOR 3 +#define DRV_VERSION_BUILD 1 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \ __stringify(DRV_VERSION_MINOR) "." \ __stringify(DRV_VERSION_BUILD) DRV_KERN @@ -250,6 +250,22 @@ static int i40e_put_lump(struct i40e_lump_tracking *pile, u16 index, u16 id) } /** + * i40e_find_vsi_from_id - searches for the vsi with the given id + * @pf - the pf structure to search for the vsi + * @id - id of the vsi it is searching for + **/ +struct i40e_vsi *i40e_find_vsi_from_id(struct i40e_pf *pf, u16 id) +{ + int i; + + for (i = 0; i < pf->num_alloc_vsi; i++) + if (pf->vsi[i] && (pf->vsi[i]->id == id)) + return pf->vsi[i]; + + return NULL; +} + +/** * i40e_service_event_schedule - Schedule the service task to wake up * @pf: board private structure * @@ -1969,7 +1985,7 @@ void i40e_vlan_stripping_enable(struct i40e_vsi *vsi) I40E_AQ_VSI_PVLAN_EMOD_STR_BOTH; ctxt.seid = vsi->seid; - memcpy(&ctxt.info, &vsi->info, sizeof(vsi->info)); + ctxt.info = vsi->info; ret = i40e_aq_update_vsi_params(&vsi->back->hw, &ctxt, NULL); if (ret) { dev_info(&vsi->back->pdev->dev, @@ -1998,7 +2014,7 @@ void i40e_vlan_stripping_disable(struct i40e_vsi *vsi) I40E_AQ_VSI_PVLAN_EMOD_NOTHING; ctxt.seid = vsi->seid; - memcpy(&ctxt.info, &vsi->info, sizeof(vsi->info)); + ctxt.info = vsi->info; ret = i40e_aq_update_vsi_params(&vsi->back->hw, &ctxt, NULL); if (ret) { dev_info(&vsi->back->pdev->dev, @@ -2282,7 +2298,7 @@ int i40e_vsi_add_pvid(struct i40e_vsi *vsi, u16 vid) I40E_AQ_VSI_PVLAN_EMOD_STR; ctxt.seid = vsi->seid; - memcpy(&ctxt.info, &vsi->info, sizeof(vsi->info)); + ctxt.info = vsi->info; aq_ret = i40e_aq_update_vsi_params(&vsi->back->hw, &ctxt, NULL); if (aq_ret) { dev_info(&vsi->back->pdev->dev, @@ -3197,6 +3213,9 @@ static irqreturn_t i40e_intr(int irq, void *data) if (icr0 & I40E_PFINT_ICR0_HMC_ERR_MASK) { icr0 &= ~I40E_PFINT_ICR0_HMC_ERR_MASK; dev_info(&pf->pdev->dev, "HMC error interrupt\n"); + dev_info(&pf->pdev->dev, "HMC error info 0x%x, HMC error data 0x%x\n", + rd32(hw, I40E_PFHMC_ERRORINFO), + rd32(hw, I40E_PFHMC_ERRORDATA)); } if (icr0 & I40E_PFINT_ICR0_TIMESYNC_MASK) { @@ -4392,7 +4411,7 @@ static int i40e_vsi_config_tc(struct i40e_vsi *vsi, u8 enabled_tc) ctxt.pf_num = vsi->back->hw.pf_id; ctxt.vf_num = 0; ctxt.uplink_seid = vsi->uplink_seid; - memcpy(&ctxt.info, &vsi->info, sizeof(vsi->info)); + ctxt.info = vsi->info; i40e_vsi_setup_queue_map(vsi, &ctxt, enabled_tc, false); /* Update the VSI after updating the VSI queue-mapping information */ @@ -5220,9 +5239,8 @@ static int i40e_handle_lldp_event(struct i40e_pf *pf, goto exit; } - memset(&tmp_dcbx_cfg, 0, sizeof(tmp_dcbx_cfg)); /* Store the old configuration */ - memcpy(&tmp_dcbx_cfg, &hw->local_dcbx_config, sizeof(tmp_dcbx_cfg)); + tmp_dcbx_cfg = hw->local_dcbx_config; /* Reset the old DCBx configuration data */ memset(&hw->local_dcbx_config, 0, sizeof(hw->local_dcbx_config)); @@ -5782,11 +5800,9 @@ static void i40e_handle_link_event(struct i40e_pf *pf, struct i40e_hw *hw = &pf->hw; struct i40e_aqc_get_link_status *status = (struct i40e_aqc_get_link_status *)&e->desc.params.raw; - struct i40e_link_status *hw_link_info = &hw->phy.link_info; /* save off old link status information */ - memcpy(&pf->hw.phy.link_info_old, hw_link_info, - sizeof(pf->hw.phy.link_info_old)); + hw->phy.link_info_old = hw->phy.link_info; /* Do a new status request to re-enable LSE reporting * and load new status information into the hw struct @@ -6608,7 +6624,6 @@ static void i40e_sync_vxlan_filters_subtask(struct i40e_pf *pf) { struct i40e_hw *hw = &pf->hw; i40e_status ret; - u8 filter_index; __be16 port; int i; @@ -6621,22 +6636,20 @@ static void i40e_sync_vxlan_filters_subtask(struct i40e_pf *pf) if (pf->pending_vxlan_bitmap & (1 << i)) { pf->pending_vxlan_bitmap &= ~(1 << i); port = pf->vxlan_ports[i]; - ret = port ? - i40e_aq_add_udp_tunnel(hw, ntohs(port), + if (port) + ret = i40e_aq_add_udp_tunnel(hw, ntohs(port), I40E_AQC_TUNNEL_TYPE_VXLAN, - &filter_index, NULL) - : i40e_aq_del_udp_tunnel(hw, i, NULL); + NULL, NULL); + else + ret = i40e_aq_del_udp_tunnel(hw, i, NULL); if (ret) { - dev_info(&pf->pdev->dev, "Failed to execute AQ command for %s port %d with index %d\n", - port ? "adding" : "deleting", - ntohs(port), port ? i : i); - + dev_info(&pf->pdev->dev, + "%s vxlan port %d, index %d failed, err %d, aq_err %d\n", + port ? "add" : "delete", + ntohs(port), i, ret, + pf->hw.aq.asq_last_status); pf->vxlan_ports[i] = 0; - } else { - dev_info(&pf->pdev->dev, "%s port %d with AQ command with index %d\n", - port ? "Added" : "Deleted", - ntohs(port), port ? i : filter_index); } } } @@ -7829,7 +7842,8 @@ static void i40e_add_vxlan_port(struct net_device *netdev, /* Check if port already exists */ if (idx < I40E_MAX_PF_UDP_OFFLOAD_PORTS) { - netdev_info(netdev, "Port %d already offloaded\n", ntohs(port)); + netdev_info(netdev, "vxlan port %d already offloaded\n", + ntohs(port)); return; } @@ -7837,7 +7851,7 @@ static void i40e_add_vxlan_port(struct net_device *netdev, next_idx = i40e_get_vxlan_port_idx(pf, 0); if (next_idx == I40E_MAX_PF_UDP_OFFLOAD_PORTS) { - netdev_info(netdev, "Maximum number of UDP ports reached, not adding port %d\n", + netdev_info(netdev, "maximum number of vxlan UDP ports reached, not adding port %d\n", ntohs(port)); return; } @@ -7845,8 +7859,9 @@ static void i40e_add_vxlan_port(struct net_device *netdev, /* New port: add it and mark its index in the bitmap */ pf->vxlan_ports[next_idx] = port; pf->pending_vxlan_bitmap |= (1 << next_idx); - pf->flags |= I40E_FLAG_VXLAN_FILTER_SYNC; + + dev_info(&pf->pdev->dev, "adding vxlan port %d\n", ntohs(port)); } /** @@ -7874,12 +7889,13 @@ static void i40e_del_vxlan_port(struct net_device *netdev, * and make it pending */ pf->vxlan_ports[idx] = 0; - pf->pending_vxlan_bitmap |= (1 << idx); - pf->flags |= I40E_FLAG_VXLAN_FILTER_SYNC; + + dev_info(&pf->pdev->dev, "deleting vxlan port %d\n", + ntohs(port)); } else { - netdev_warn(netdev, "Port %d was not found, not deleting\n", + netdev_warn(netdev, "vxlan port %d was not found, not deleting\n", ntohs(port)); } } @@ -8269,7 +8285,7 @@ static int i40e_add_vsi(struct i40e_vsi *vsi) ret, pf->hw.aq.asq_last_status); return -ENOENT; } - memcpy(&vsi->info, &ctxt.info, sizeof(ctxt.info)); + vsi->info = ctxt.info; vsi->info.valid_sections = 0; vsi->seid = ctxt.seid; @@ -8403,7 +8419,7 @@ static int i40e_add_vsi(struct i40e_vsi *vsi) ret = -ENOENT; goto err; } - memcpy(&vsi->info, &ctxt.info, sizeof(ctxt.info)); + vsi->info = ctxt.info; vsi->info.valid_sections = 0; vsi->seid = ctxt.seid; vsi->id = ctxt.vsi_number; @@ -10210,6 +10226,8 @@ static int i40e_suspend(struct pci_dev *pdev, pm_message_t state) set_bit(__I40E_DOWN, &pf->state); del_timer_sync(&pf->service_timer); cancel_work_sync(&pf->service_task); + i40e_fdir_teardown(pf); + rtnl_lock(); i40e_prep_for_reset(pf); rtnl_unlock(); diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 9b11f2e7e361..d8989f9d1798 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1565,8 +1565,11 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget) if (likely(!skb)) { skb = netdev_alloc_skb_ip_align(rx_ring->netdev, rx_ring->rx_hdr_len); - if (!skb) + if (!skb) { rx_ring->rx_stats.alloc_buff_failed++; + break; + } + /* initialize queue mapping */ skb_record_rx_queue(skb, rx_ring->queue_index); /* we are reusing so sync this buffer for CPU use */ @@ -2054,6 +2057,19 @@ static int i40e_tx_prepare_vlan_flags(struct sk_buff *skb, __be16 protocol = skb->protocol; u32 tx_flags = 0; + if (protocol == htons(ETH_P_8021Q) && + !(tx_ring->netdev->features & NETIF_F_HW_VLAN_CTAG_TX)) { + /* When HW VLAN acceleration is turned off by the user the + * stack sets the protocol to 8021q so that the driver + * can take any steps required to support the SW only + * VLAN handling. In our case the driver doesn't need + * to take any further steps so just set the protocol + * to the encapsulated ethertype. + */ + skb->protocol = vlan_get_protocol(skb); + goto out; + } + /* if we have a HW VLAN tag being added, default to the HW one */ if (skb_vlan_tag_present(skb)) { tx_flags |= skb_vlan_tag_get(skb) << I40E_TX_FLAGS_VLAN_SHIFT; diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 0a93684130b9..4d69e1f04901 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -53,11 +53,12 @@ static inline void i40e_vc_disable_vf(struct i40e_pf *pf, struct i40e_vf *vf) * * check for the valid VSI id **/ -static inline bool i40e_vc_isvalid_vsi_id(struct i40e_vf *vf, u8 vsi_id) +static inline bool i40e_vc_isvalid_vsi_id(struct i40e_vf *vf, u16 vsi_id) { struct i40e_pf *pf = vf->pf; + struct i40e_vsi *vsi = i40e_find_vsi_from_id(pf, vsi_id); - return pf->vsi[vsi_id]->vf_id == vf->vf_id; + return (vsi && (vsi->vf_id == vf->vf_id)); } /** @@ -68,12 +69,13 @@ static inline bool i40e_vc_isvalid_vsi_id(struct i40e_vf *vf, u8 vsi_id) * * check for the valid queue id **/ -static inline bool i40e_vc_isvalid_queue_id(struct i40e_vf *vf, u8 vsi_id, +static inline bool i40e_vc_isvalid_queue_id(struct i40e_vf *vf, u16 vsi_id, u8 qid) { struct i40e_pf *pf = vf->pf; + struct i40e_vsi *vsi = i40e_find_vsi_from_id(pf, vsi_id); - return qid < pf->vsi[vsi_id]->alloc_queue_pairs; + return (vsi && (qid < vsi->alloc_queue_pairs)); } /** @@ -95,18 +97,21 @@ static inline bool i40e_vc_isvalid_vector_id(struct i40e_vf *vf, u8 vector_id) /** * i40e_vc_get_pf_queue_id * @vf: pointer to the VF info - * @vsi_idx: index of VSI in PF struct + * @vsi_id: id of VSI as provided by the FW * @vsi_queue_id: vsi relative queue id * * return PF relative queue id **/ -static u16 i40e_vc_get_pf_queue_id(struct i40e_vf *vf, u8 vsi_idx, +static u16 i40e_vc_get_pf_queue_id(struct i40e_vf *vf, u16 vsi_id, u8 vsi_queue_id) { struct i40e_pf *pf = vf->pf; - struct i40e_vsi *vsi = pf->vsi[vsi_idx]; + struct i40e_vsi *vsi = i40e_find_vsi_from_id(pf, vsi_id); u16 pf_queue_id = I40E_QUEUE_END_OF_LIST; + if (!vsi) + return pf_queue_id; + if (le16_to_cpu(vsi->info.mapping_flags) & I40E_AQ_VSI_QUE_MAP_NONCONTIG) pf_queue_id = @@ -121,12 +126,12 @@ static u16 i40e_vc_get_pf_queue_id(struct i40e_vf *vf, u8 vsi_idx, /** * i40e_config_irq_link_list * @vf: pointer to the VF info - * @vsi_idx: index of VSI in PF struct + * @vsi_id: id of VSI as given by the FW * @vecmap: irq map info * * configure irq link list from the map **/ -static void i40e_config_irq_link_list(struct i40e_vf *vf, u16 vsi_idx, +static void i40e_config_irq_link_list(struct i40e_vf *vf, u16 vsi_id, struct i40e_virtchnl_vector_map *vecmap) { unsigned long linklistmap = 0, tempmap; @@ -171,7 +176,7 @@ static void i40e_config_irq_link_list(struct i40e_vf *vf, u16 vsi_idx, I40E_VIRTCHNL_SUPPORTED_QTYPES)); vsi_queue_id = next_q/I40E_VIRTCHNL_SUPPORTED_QTYPES; qtype = next_q%I40E_VIRTCHNL_SUPPORTED_QTYPES; - pf_queue_id = i40e_vc_get_pf_queue_id(vf, vsi_idx, vsi_queue_id); + pf_queue_id = i40e_vc_get_pf_queue_id(vf, vsi_id, vsi_queue_id); reg = ((qtype << I40E_VPINT_LNKLSTN_FIRSTQ_TYPE_SHIFT) | pf_queue_id); wr32(hw, reg_idx, reg); @@ -198,7 +203,7 @@ static void i40e_config_irq_link_list(struct i40e_vf *vf, u16 vsi_idx, (I40E_MAX_VSI_QP * I40E_VIRTCHNL_SUPPORTED_QTYPES)) { vsi_queue_id = next_q / I40E_VIRTCHNL_SUPPORTED_QTYPES; qtype = next_q % I40E_VIRTCHNL_SUPPORTED_QTYPES; - pf_queue_id = i40e_vc_get_pf_queue_id(vf, vsi_idx, + pf_queue_id = i40e_vc_get_pf_queue_id(vf, vsi_id, vsi_queue_id); } else { pf_queue_id = I40E_QUEUE_END_OF_LIST; @@ -221,24 +226,26 @@ irq_list_done: /** * i40e_config_vsi_tx_queue * @vf: pointer to the VF info - * @vsi_idx: index of VSI in PF struct + * @vsi_id: id of VSI as provided by the FW * @vsi_queue_id: vsi relative queue index * @info: config. info * * configure tx queue **/ -static int i40e_config_vsi_tx_queue(struct i40e_vf *vf, u16 vsi_idx, +static int i40e_config_vsi_tx_queue(struct i40e_vf *vf, u16 vsi_id, u16 vsi_queue_id, struct i40e_virtchnl_txq_info *info) { struct i40e_pf *pf = vf->pf; struct i40e_hw *hw = &pf->hw; struct i40e_hmc_obj_txq tx_ctx; + struct i40e_vsi *vsi; u16 pf_queue_id; u32 qtx_ctl; int ret = 0; - pf_queue_id = i40e_vc_get_pf_queue_id(vf, vsi_idx, vsi_queue_id); + pf_queue_id = i40e_vc_get_pf_queue_id(vf, vsi_id, vsi_queue_id); + vsi = i40e_find_vsi_from_id(pf, vsi_id); /* clear the context structure first */ memset(&tx_ctx, 0, sizeof(struct i40e_hmc_obj_txq)); @@ -246,7 +253,7 @@ static int i40e_config_vsi_tx_queue(struct i40e_vf *vf, u16 vsi_idx, /* only set the required fields */ tx_ctx.base = info->dma_ring_addr / 128; tx_ctx.qlen = info->ring_len; - tx_ctx.rdylist = le16_to_cpu(pf->vsi[vsi_idx]->info.qs_handle[0]); + tx_ctx.rdylist = le16_to_cpu(vsi->info.qs_handle[0]); tx_ctx.rdylist_act = 0; tx_ctx.head_wb_ena = info->headwb_enabled; tx_ctx.head_wb_addr = info->dma_headwb_addr; @@ -288,13 +295,13 @@ error_context: /** * i40e_config_vsi_rx_queue * @vf: pointer to the VF info - * @vsi_idx: index of VSI in PF struct + * @vsi_id: id of VSI as provided by the FW * @vsi_queue_id: vsi relative queue index * @info: config. info * * configure rx queue **/ -static int i40e_config_vsi_rx_queue(struct i40e_vf *vf, u16 vsi_idx, +static int i40e_config_vsi_rx_queue(struct i40e_vf *vf, u16 vsi_id, u16 vsi_queue_id, struct i40e_virtchnl_rxq_info *info) { @@ -304,7 +311,7 @@ static int i40e_config_vsi_rx_queue(struct i40e_vf *vf, u16 vsi_idx, u16 pf_queue_id; int ret = 0; - pf_queue_id = i40e_vc_get_pf_queue_id(vf, vsi_idx, vsi_queue_id); + pf_queue_id = i40e_vc_get_pf_queue_id(vf, vsi_id, vsi_queue_id); /* clear the context structure first */ memset(&rx_ctx, 0, sizeof(struct i40e_hmc_obj_rxq)); @@ -401,7 +408,7 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type) } if (type == I40E_VSI_SRIOV) { u8 brdcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - vf->lan_vsi_index = vsi->idx; + vf->lan_vsi_idx = vsi->idx; vf->lan_vsi_id = vsi->id; /* If the port VLAN has been configured and then the * VF driver was removed then the VSI port VLAN @@ -466,8 +473,8 @@ static void i40e_enable_vf_mappings(struct i40e_vf *vf) wr32(hw, I40E_VPLAN_MAPENA(vf->vf_id), reg); /* map PF queues to VF queues */ - for (j = 0; j < pf->vsi[vf->lan_vsi_index]->alloc_queue_pairs; j++) { - u16 qid = i40e_vc_get_pf_queue_id(vf, vf->lan_vsi_index, j); + for (j = 0; j < pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs; j++) { + u16 qid = i40e_vc_get_pf_queue_id(vf, vf->lan_vsi_id, j); reg = (qid & I40E_VPLAN_QTABLE_QINDEX_MASK); wr32(hw, I40E_VPLAN_QTABLE(total_queue_pairs, vf->vf_id), reg); total_queue_pairs++; @@ -475,13 +482,13 @@ static void i40e_enable_vf_mappings(struct i40e_vf *vf) /* map PF queues to VSI */ for (j = 0; j < 7; j++) { - if (j * 2 >= pf->vsi[vf->lan_vsi_index]->alloc_queue_pairs) { + if (j * 2 >= pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs) { reg = 0x07FF07FF; /* unused */ } else { - u16 qid = i40e_vc_get_pf_queue_id(vf, vf->lan_vsi_index, + u16 qid = i40e_vc_get_pf_queue_id(vf, vf->lan_vsi_id, j * 2); reg = qid; - qid = i40e_vc_get_pf_queue_id(vf, vf->lan_vsi_index, + qid = i40e_vc_get_pf_queue_id(vf, vf->lan_vsi_id, (j * 2) + 1); reg |= qid << 16; } @@ -525,9 +532,9 @@ static void i40e_free_vf_res(struct i40e_vf *vf) int i, msix_vf; /* free vsi & disconnect it from the parent uplink */ - if (vf->lan_vsi_index) { - i40e_vsi_release(pf->vsi[vf->lan_vsi_index]); - vf->lan_vsi_index = 0; + if (vf->lan_vsi_idx) { + i40e_vsi_release(pf->vsi[vf->lan_vsi_idx]); + vf->lan_vsi_idx = 0; vf->lan_vsi_id = 0; } msix_vf = pf->hw.func_caps.num_msix_vectors_vf; @@ -582,7 +589,7 @@ static int i40e_alloc_vf_res(struct i40e_vf *vf) ret = i40e_alloc_vsi_res(vf, I40E_VSI_SRIOV); if (ret) goto error_alloc; - total_queue_pairs += pf->vsi[vf->lan_vsi_index]->alloc_queue_pairs; + total_queue_pairs += pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs; set_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps); /* store the total qps number for the runtime @@ -692,10 +699,10 @@ void i40e_reset_vf(struct i40e_vf *vf, bool flr) wr32(hw, I40E_VPGEN_VFRTRIG(vf->vf_id), reg); /* On initial reset, we won't have any queues */ - if (vf->lan_vsi_index == 0) + if (vf->lan_vsi_idx == 0) goto complete_reset; - i40e_vsi_control_rings(pf->vsi[vf->lan_vsi_index], false); + i40e_vsi_control_rings(pf->vsi[vf->lan_vsi_idx], false); complete_reset: /* reallocate VF resources to reset the VSI state */ i40e_free_vf_res(vf); @@ -732,6 +739,8 @@ void i40e_free_vfs(struct i40e_pf *pf) */ if (!pci_vfs_assigned(pf->pdev)) pci_disable_sriov(pf->pdev); + else + dev_warn(&pf->pdev->dev, "VFs are assigned - not disabling SR-IOV\n"); msleep(20); /* let any messages in transit get finished up */ @@ -761,9 +770,6 @@ void i40e_free_vfs(struct i40e_pf *pf) bit_idx = (hw->func_caps.vf_base_id + vf_id) % 32; wr32(hw, I40E_GLGEN_VFLRSTAT(reg_idx), (1 << bit_idx)); } - } else { - dev_warn(&pf->pdev->dev, - "unable to disable SR-IOV because VFs are assigned.\n"); } clear_bit(__I40E_VF_DISABLE, &pf->state); } @@ -1017,18 +1023,18 @@ static int i40e_vc_get_vf_resources_msg(struct i40e_vf *vf) } vfres->vf_offload_flags = I40E_VIRTCHNL_VF_OFFLOAD_L2; - vsi = pf->vsi[vf->lan_vsi_index]; + vsi = pf->vsi[vf->lan_vsi_idx]; if (!vsi->info.pvid) vfres->vf_offload_flags |= I40E_VIRTCHNL_VF_OFFLOAD_VLAN; vfres->num_vsis = num_vsis; vfres->num_queue_pairs = vf->num_queue_pairs; vfres->max_vectors = pf->hw.func_caps.num_msix_vectors_vf; - if (vf->lan_vsi_index) { - vfres->vsi_res[i].vsi_id = vf->lan_vsi_index; + if (vf->lan_vsi_idx) { + vfres->vsi_res[i].vsi_id = vf->lan_vsi_id; vfres->vsi_res[i].vsi_type = I40E_VSI_SRIOV; vfres->vsi_res[i].num_queue_pairs = - pf->vsi[vf->lan_vsi_index]->alloc_queue_pairs; + pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs; memcpy(vfres->vsi_res[i].default_mac_addr, vf->default_lan_addr.addr, ETH_ALEN); i++; @@ -1080,14 +1086,14 @@ static int i40e_vc_config_promiscuous_mode_msg(struct i40e_vf *vf, bool allmulti = false; i40e_status aq_ret; + vsi = i40e_find_vsi_from_id(pf, info->vsi_id); if (!test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states) || !test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps) || !i40e_vc_isvalid_vsi_id(vf, info->vsi_id) || - (pf->vsi[info->vsi_id]->type != I40E_VSI_FCOE)) { + (vsi->type != I40E_VSI_FCOE)) { aq_ret = I40E_ERR_PARAM; goto error_param; } - vsi = pf->vsi[info->vsi_id]; if (info->flags & I40E_FLAG_VF_MULTICAST_PROMISC) allmulti = true; aq_ret = i40e_aq_set_vsi_multicast_promiscuous(hw, vsi->seid, @@ -1149,7 +1155,7 @@ static int i40e_vc_config_queues_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) } } /* set vsi num_queue_pairs in use to num configured by VF */ - pf->vsi[vf->lan_vsi_index]->num_queue_pairs = qci->num_queue_pairs; + pf->vsi[vf->lan_vsi_idx]->num_queue_pairs = qci->num_queue_pairs; error_param: /* send the response to the VF */ @@ -1250,7 +1256,8 @@ static int i40e_vc_enable_queues_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) aq_ret = I40E_ERR_PARAM; goto error_param; } - if (i40e_vsi_control_rings(pf->vsi[vsi_id], true)) + + if (i40e_vsi_control_rings(pf->vsi[vf->lan_vsi_idx], true)) aq_ret = I40E_ERR_TIMEOUT; error_param: /* send the response to the VF */ @@ -1272,7 +1279,6 @@ static int i40e_vc_disable_queues_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) struct i40e_virtchnl_queue_select *vqs = (struct i40e_virtchnl_queue_select *)msg; struct i40e_pf *pf = vf->pf; - u16 vsi_id = vqs->vsi_id; i40e_status aq_ret = 0; if (!test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states)) { @@ -1289,7 +1295,8 @@ static int i40e_vc_disable_queues_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) aq_ret = I40E_ERR_PARAM; goto error_param; } - if (i40e_vsi_control_rings(pf->vsi[vsi_id], false)) + + if (i40e_vsi_control_rings(pf->vsi[vf->lan_vsi_idx], false)) aq_ret = I40E_ERR_TIMEOUT; error_param: @@ -1327,7 +1334,7 @@ static int i40e_vc_get_stats_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) goto error_param; } - vsi = pf->vsi[vqs->vsi_id]; + vsi = pf->vsi[vf->lan_vsi_idx]; if (!vsi) { aq_ret = I40E_ERR_PARAM; goto error_param; @@ -1405,7 +1412,7 @@ static int i40e_vc_add_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) if (ret) goto error_param; } - vsi = pf->vsi[vsi_id]; + vsi = pf->vsi[vf->lan_vsi_idx]; /* add new addresses to the list */ for (i = 0; i < al->num_elements; i++) { @@ -1473,7 +1480,7 @@ static int i40e_vc_del_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) goto error_param; } } - vsi = pf->vsi[vsi_id]; + vsi = pf->vsi[vf->lan_vsi_idx]; /* delete addresses from the list */ for (i = 0; i < al->num_elements; i++) @@ -1523,7 +1530,7 @@ static int i40e_vc_add_vlan_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) goto error_param; } } - vsi = pf->vsi[vsi_id]; + vsi = pf->vsi[vf->lan_vsi_idx]; if (vsi->info.pvid) { aq_ret = I40E_ERR_PARAM; goto error_param; @@ -1576,7 +1583,7 @@ static int i40e_vc_remove_vlan_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) } } - vsi = pf->vsi[vsi_id]; + vsi = pf->vsi[vf->lan_vsi_idx]; if (vsi->info.pvid) { aq_ret = I40E_ERR_PARAM; goto error_param; @@ -1965,7 +1972,7 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac) } vf = &(pf->vf[vf_id]); - vsi = pf->vsi[vf->lan_vsi_index]; + vsi = pf->vsi[vf->lan_vsi_idx]; if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) { dev_err(&pf->pdev->dev, "Uninitialized VF %d\n", vf_id); @@ -2039,7 +2046,7 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, } vf = &(pf->vf[vf_id]); - vsi = pf->vsi[vf->lan_vsi_index]; + vsi = pf->vsi[vf->lan_vsi_idx]; if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) { dev_err(&pf->pdev->dev, "Uninitialized VF %d\n", vf_id); ret = -EINVAL; @@ -2152,7 +2159,7 @@ int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate, } vf = &(pf->vf[vf_id]); - vsi = pf->vsi[vf->lan_vsi_index]; + vsi = pf->vsi[vf->lan_vsi_idx]; if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) { dev_err(&pf->pdev->dev, "Uninitialized VF %d.\n", vf_id); ret = -EINVAL; @@ -2226,7 +2233,7 @@ int i40e_ndo_get_vf_config(struct net_device *netdev, vf = &(pf->vf[vf_id]); /* first vsi is always the LAN vsi */ - vsi = pf->vsi[vf->lan_vsi_index]; + vsi = pf->vsi[vf->lan_vsi_idx]; if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) { dev_err(&pf->pdev->dev, "Uninitialized VF %d\n", vf_id); ret = -EINVAL; @@ -2350,7 +2357,7 @@ int i40e_ndo_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool enable) vf->spoofchk = enable; memset(&ctxt, 0, sizeof(ctxt)); - ctxt.seid = pf->vsi[vf->lan_vsi_index]->seid; + ctxt.seid = pf->vsi[vf->lan_vsi_idx]->seid; ctxt.pf_num = pf->hw.pf_id; ctxt.info.valid_sections = cpu_to_le16(I40E_AQ_VSI_PROP_SECURITY_VALID); if (enable) diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h index 9c3a41040835..09043c1aae54 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h @@ -88,7 +88,7 @@ struct i40e_vf { * When assigned, these will be non-zero, because VSI 0 is always * the main LAN VSI for the PF. */ - u8 lan_vsi_index; /* index into PF struct */ + u8 lan_vsi_idx; /* index into PF struct */ u8 lan_vsi_id; /* ID as used by firmware */ u8 num_queue_pairs; /* num of qps assigned to VF vsis */ diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index f41da5d8047b..e2ddb30e96f5 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -915,9 +915,7 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, * so the total length of IPv4 header is IHL*4 bytes * The UDP_0 bit *may* bet set if the *inner* header is UDP */ - if (ipv4_tunnel && - (decoded.inner_prot != I40E_RX_PTYPE_INNER_PROT_UDP) && - !(rx_status & (1 << I40E_RX_DESC_STATUS_UDP_0_SHIFT))) { + if (ipv4_tunnel) { skb->transport_header = skb->mac_header + sizeof(struct ethhdr) + (ip_hdr(skb)->ihl * 4); @@ -927,15 +925,19 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, skb->protocol == htons(ETH_P_8021AD)) ? VLAN_HLEN : 0; - rx_udp_csum = udp_csum(skb); - iph = ip_hdr(skb); - csum = csum_tcpudp_magic( - iph->saddr, iph->daddr, - (skb->len - skb_transport_offset(skb)), - IPPROTO_UDP, rx_udp_csum); + if ((ip_hdr(skb)->protocol == IPPROTO_UDP) && + (udp_hdr(skb)->check != 0)) { + rx_udp_csum = udp_csum(skb); + iph = ip_hdr(skb); + csum = csum_tcpudp_magic(iph->saddr, iph->daddr, + (skb->len - + skb_transport_offset(skb)), + IPPROTO_UDP, rx_udp_csum); - if (udp_hdr(skb)->check != csum) - goto checksum_fail; + if (udp_hdr(skb)->check != csum) + goto checksum_fail; + + } /* else its GRE and so no outer UDP header */ } skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -1038,8 +1040,11 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget) if (likely(!skb)) { skb = netdev_alloc_skb_ip_align(rx_ring->netdev, rx_ring->rx_hdr_len); - if (!skb) + if (!skb) { rx_ring->rx_stats.alloc_buff_failed++; + break; + } + /* initialize queue mapping */ skb_record_rx_queue(skb, rx_ring->queue_index); /* we are reusing so sync this buffer for CPU use */ @@ -1365,6 +1370,19 @@ static int i40e_tx_prepare_vlan_flags(struct sk_buff *skb, __be16 protocol = skb->protocol; u32 tx_flags = 0; + if (protocol == htons(ETH_P_8021Q) && + !(tx_ring->netdev->features & NETIF_F_HW_VLAN_CTAG_TX)) { + /* When HW VLAN acceleration is turned off by the user the + * stack sets the protocol to 8021q so that the driver + * can take any steps required to support the SW only + * VLAN handling. In our case the driver doesn't need + * to take any further steps so just set the protocol + * to the encapsulated ethertype. + */ + skb->protocol = vlan_get_protocol(skb); + goto out; + } + /* if we have a HW VLAN tag being added, default to the HW one */ if (skb_vlan_tag_present(skb)) { tx_flags |= skb_vlan_tag_get(skb) << I40E_TX_FLAGS_VLAN_SHIFT; @@ -1381,6 +1399,7 @@ static int i40e_tx_prepare_vlan_flags(struct sk_buff *skb, tx_flags |= I40E_TX_FLAGS_SW_VLAN; } +out: *flags = tx_flags; return 0; } diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index b08efafee1ae..6d5f3b21c68a 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -664,13 +664,21 @@ i40evf_vlan_filter *i40evf_find_vlan(struct i40evf_adapter *adapter, u16 vlan) static struct i40evf_vlan_filter *i40evf_add_vlan(struct i40evf_adapter *adapter, u16 vlan) { - struct i40evf_vlan_filter *f; + struct i40evf_vlan_filter *f = NULL; + int count = 50; + + while (test_and_set_bit(__I40EVF_IN_CRITICAL_TASK, + &adapter->crit_section)) { + udelay(1); + if (--count == 0) + goto out; + } f = i40evf_find_vlan(adapter, vlan); if (!f) { f = kzalloc(sizeof(*f), GFP_ATOMIC); if (!f) - return NULL; + goto clearout; f->vlan = vlan; @@ -680,6 +688,9 @@ i40evf_vlan_filter *i40evf_add_vlan(struct i40evf_adapter *adapter, u16 vlan) adapter->aq_required |= I40EVF_FLAG_AQ_ADD_VLAN_FILTER; } +clearout: + clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section); +out: return f; } @@ -691,12 +702,21 @@ i40evf_vlan_filter *i40evf_add_vlan(struct i40evf_adapter *adapter, u16 vlan) static void i40evf_del_vlan(struct i40evf_adapter *adapter, u16 vlan) { struct i40evf_vlan_filter *f; + int count = 50; + + while (test_and_set_bit(__I40EVF_IN_CRITICAL_TASK, + &adapter->crit_section)) { + udelay(1); + if (--count == 0) + return; + } f = i40evf_find_vlan(adapter, vlan); if (f) { f->remove = true; adapter->aq_required |= I40EVF_FLAG_AQ_DEL_VLAN_FILTER; } + clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section); } /** @@ -1415,41 +1435,22 @@ restart_watchdog: } /** - * next_queue - increment to next available tx queue - * @adapter: board private structure - * @j: queue counter - * - * Helper function for RSS programming to increment through available - * queus. Returns the next queue value. - **/ -static int next_queue(struct i40evf_adapter *adapter, int j) -{ - j += 1; - - return j >= adapter->num_active_queues ? 0 : j; -} - -/** - * i40evf_configure_rss - Prepare for RSS if used + * i40evf_configure_rss - Prepare for RSS * @adapter: board private structure **/ static void i40evf_configure_rss(struct i40evf_adapter *adapter) { u32 rss_key[I40E_VFQF_HKEY_MAX_INDEX + 1]; struct i40e_hw *hw = &adapter->hw; + u32 cqueue = 0; u32 lut = 0; int i, j; u64 hena; - /* No RSS for single queue. */ - if (adapter->num_active_queues == 1) { - wr32(hw, I40E_VFQF_HENA(0), 0); - wr32(hw, I40E_VFQF_HENA(1), 0); - return; - } - /* Hash type is configured by the PF - we just supply the key */ netdev_rss_key_fill(rss_key, sizeof(rss_key)); + + /* Fill out hash function seed */ for (i = 0; i <= I40E_VFQF_HKEY_MAX_INDEX; i++) wr32(hw, I40E_VFQF_HKEY(i), rss_key[i]); @@ -1459,16 +1460,14 @@ static void i40evf_configure_rss(struct i40evf_adapter *adapter) wr32(hw, I40E_VFQF_HENA(1), (u32)(hena >> 32)); /* Populate the LUT with max no. of queues in round robin fashion */ - j = adapter->num_active_queues; for (i = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++) { - j = next_queue(adapter, j); - lut = j; - j = next_queue(adapter, j); - lut |= j << 8; - j = next_queue(adapter, j); - lut |= j << 16; - j = next_queue(adapter, j); - lut |= j << 24; + lut = 0; + for (j = 0; j < 4; j++) { + if (cqueue == adapter->vsi_res->num_queue_pairs) + cqueue = 0; + lut |= ((cqueue) << (8 * j)); + cqueue++; + } wr32(hw, I40E_VFQF_HLUT(i), lut); } i40e_flush(hw); diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 96208f17bb53..ce5f7f9cff06 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -100,6 +100,8 @@ #define MVNETA_TXQ_CMD 0x2448 #define MVNETA_TXQ_DISABLE_SHIFT 8 #define MVNETA_TXQ_ENABLE_MASK 0x000000ff +#define MVNETA_GMAC_CLOCK_DIVIDER 0x24f4 +#define MVNETA_GMAC_1MS_CLOCK_ENABLE BIT(31) #define MVNETA_ACC_MODE 0x2500 #define MVNETA_CPU_MAP(cpu) (0x2540 + ((cpu) << 2)) #define MVNETA_CPU_RXQ_ACCESS_ALL_MASK 0x000000ff @@ -122,6 +124,7 @@ #define MVNETA_TX_INTR_MASK_ALL (0xff << 0) #define MVNETA_RX_INTR_MASK(nr_rxqs) (((1 << nr_rxqs) - 1) << 8) #define MVNETA_RX_INTR_MASK_ALL (0xff << 8) +#define MVNETA_MISCINTR_INTR_MASK BIT(31) #define MVNETA_INTR_OLD_CAUSE 0x25a8 #define MVNETA_INTR_OLD_MASK 0x25ac @@ -165,6 +168,7 @@ #define MVNETA_GMAC_MAX_RX_SIZE_MASK 0x7ffc #define MVNETA_GMAC0_PORT_ENABLE BIT(0) #define MVNETA_GMAC_CTRL_2 0x2c08 +#define MVNETA_GMAC2_INBAND_AN_ENABLE BIT(0) #define MVNETA_GMAC2_PCS_ENABLE BIT(3) #define MVNETA_GMAC2_PORT_RGMII BIT(4) #define MVNETA_GMAC2_PORT_RESET BIT(6) @@ -180,9 +184,11 @@ #define MVNETA_GMAC_AUTONEG_CONFIG 0x2c0c #define MVNETA_GMAC_FORCE_LINK_DOWN BIT(0) #define MVNETA_GMAC_FORCE_LINK_PASS BIT(1) +#define MVNETA_GMAC_INBAND_AN_ENABLE BIT(2) #define MVNETA_GMAC_CONFIG_MII_SPEED BIT(5) #define MVNETA_GMAC_CONFIG_GMII_SPEED BIT(6) #define MVNETA_GMAC_AN_SPEED_EN BIT(7) +#define MVNETA_GMAC_AN_FLOW_CTRL_EN BIT(11) #define MVNETA_GMAC_CONFIG_FULL_DUPLEX BIT(12) #define MVNETA_GMAC_AN_DUPLEX_EN BIT(13) #define MVNETA_MIB_COUNTERS_BASE 0x3080 @@ -304,6 +310,7 @@ struct mvneta_port { unsigned int link; unsigned int duplex; unsigned int speed; + int use_inband_status:1; }; /* The mvneta_tx_desc and mvneta_rx_desc structures describe the @@ -994,6 +1001,20 @@ static void mvneta_defaults_set(struct mvneta_port *pp) val &= ~MVNETA_PHY_POLLING_ENABLE; mvreg_write(pp, MVNETA_UNIT_CONTROL, val); + if (pp->use_inband_status) { + val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); + val &= ~(MVNETA_GMAC_FORCE_LINK_PASS | + MVNETA_GMAC_FORCE_LINK_DOWN | + MVNETA_GMAC_AN_FLOW_CTRL_EN); + val |= MVNETA_GMAC_INBAND_AN_ENABLE | + MVNETA_GMAC_AN_SPEED_EN | + MVNETA_GMAC_AN_DUPLEX_EN; + mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); + val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER); + val |= MVNETA_GMAC_1MS_CLOCK_ENABLE; + mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val); + } + mvneta_set_ucast_table(pp, -1); mvneta_set_special_mcast_table(pp, -1); mvneta_set_other_mcast_table(pp, -1); @@ -2043,6 +2064,28 @@ static irqreturn_t mvneta_isr(int irq, void *dev_id) return IRQ_HANDLED; } +static int mvneta_fixed_link_update(struct mvneta_port *pp, + struct phy_device *phy) +{ + struct fixed_phy_status status; + struct fixed_phy_status changed = {}; + u32 gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS); + + status.link = !!(gmac_stat & MVNETA_GMAC_LINK_UP); + if (gmac_stat & MVNETA_GMAC_SPEED_1000) + status.speed = SPEED_1000; + else if (gmac_stat & MVNETA_GMAC_SPEED_100) + status.speed = SPEED_100; + else + status.speed = SPEED_10; + status.duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX); + changed.link = 1; + changed.speed = 1; + changed.duplex = 1; + fixed_phy_update_state(phy, &status, &changed); + return 0; +} + /* NAPI handler * Bits 0 - 7 of the causeRxTx register indicate that are transmitted * packets on the corresponding TXQ (Bit 0 is for TX queue 1). @@ -2063,8 +2106,18 @@ static int mvneta_poll(struct napi_struct *napi, int budget) } /* Read cause register */ - cause_rx_tx = mvreg_read(pp, MVNETA_INTR_NEW_CAUSE) & - (MVNETA_RX_INTR_MASK(rxq_number) | MVNETA_TX_INTR_MASK(txq_number)); + cause_rx_tx = mvreg_read(pp, MVNETA_INTR_NEW_CAUSE); + if (cause_rx_tx & MVNETA_MISCINTR_INTR_MASK) { + u32 cause_misc = mvreg_read(pp, MVNETA_INTR_MISC_CAUSE); + + mvreg_write(pp, MVNETA_INTR_MISC_CAUSE, 0); + if (pp->use_inband_status && (cause_misc & + (MVNETA_CAUSE_PHY_STATUS_CHANGE | + MVNETA_CAUSE_LINK_CHANGE | + MVNETA_CAUSE_PSC_SYNC_CHANGE))) { + mvneta_fixed_link_update(pp, pp->phy_dev); + } + } /* Release Tx descriptors */ if (cause_rx_tx & MVNETA_TX_INTR_MASK_ALL) { @@ -2109,7 +2162,9 @@ static int mvneta_poll(struct napi_struct *napi, int budget) napi_complete(napi); local_irq_save(flags); mvreg_write(pp, MVNETA_INTR_NEW_MASK, - MVNETA_RX_INTR_MASK(rxq_number) | MVNETA_TX_INTR_MASK(txq_number)); + MVNETA_RX_INTR_MASK(rxq_number) | + MVNETA_TX_INTR_MASK(txq_number) | + MVNETA_MISCINTR_INTR_MASK); local_irq_restore(flags); } @@ -2373,7 +2428,13 @@ static void mvneta_start_dev(struct mvneta_port *pp) /* Unmask interrupts */ mvreg_write(pp, MVNETA_INTR_NEW_MASK, - MVNETA_RX_INTR_MASK(rxq_number) | MVNETA_TX_INTR_MASK(txq_number)); + MVNETA_RX_INTR_MASK(rxq_number) | + MVNETA_TX_INTR_MASK(txq_number) | + MVNETA_MISCINTR_INTR_MASK); + mvreg_write(pp, MVNETA_INTR_MISC_MASK, + MVNETA_CAUSE_PHY_STATUS_CHANGE | + MVNETA_CAUSE_LINK_CHANGE | + MVNETA_CAUSE_PSC_SYNC_CHANGE); phy_start(pp->phy_dev); netif_tx_start_all_queues(pp->dev); @@ -2523,9 +2584,7 @@ static void mvneta_adjust_link(struct net_device *ndev) val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); val &= ~(MVNETA_GMAC_CONFIG_MII_SPEED | MVNETA_GMAC_CONFIG_GMII_SPEED | - MVNETA_GMAC_CONFIG_FULL_DUPLEX | - MVNETA_GMAC_AN_SPEED_EN | - MVNETA_GMAC_AN_DUPLEX_EN); + MVNETA_GMAC_CONFIG_FULL_DUPLEX); if (phydev->duplex) val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX; @@ -2554,12 +2613,24 @@ static void mvneta_adjust_link(struct net_device *ndev) if (status_change) { if (phydev->link) { - u32 val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - val |= (MVNETA_GMAC_FORCE_LINK_PASS | - MVNETA_GMAC_FORCE_LINK_DOWN); - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); + if (!pp->use_inband_status) { + u32 val = mvreg_read(pp, + MVNETA_GMAC_AUTONEG_CONFIG); + val &= ~MVNETA_GMAC_FORCE_LINK_DOWN; + val |= MVNETA_GMAC_FORCE_LINK_PASS; + mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, + val); + } mvneta_port_up(pp); } else { + if (!pp->use_inband_status) { + u32 val = mvreg_read(pp, + MVNETA_GMAC_AUTONEG_CONFIG); + val &= ~MVNETA_GMAC_FORCE_LINK_PASS; + val |= MVNETA_GMAC_FORCE_LINK_DOWN; + mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, + val); + } mvneta_port_down(pp); } phy_print_status(phydev); @@ -2658,16 +2729,11 @@ static int mvneta_stop(struct net_device *dev) static int mvneta_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { struct mvneta_port *pp = netdev_priv(dev); - int ret; if (!pp->phy_dev) return -ENOTSUPP; - ret = phy_mii_ioctl(pp->phy_dev, ifr, cmd); - if (!ret) - mvneta_adjust_link(dev); - - return ret; + return phy_mii_ioctl(pp->phy_dev, ifr, cmd); } /* Ethtool methods */ @@ -2910,6 +2976,9 @@ static int mvneta_port_power_up(struct mvneta_port *pp, int phy_mode) return -EINVAL; } + if (pp->use_inband_status) + ctrl |= MVNETA_GMAC2_INBAND_AN_ENABLE; + /* Cancel Port Reset */ ctrl &= ~MVNETA_GMAC2_PORT_RESET; mvreg_write(pp, MVNETA_GMAC_CTRL_2, ctrl); @@ -2934,6 +3003,7 @@ static int mvneta_probe(struct platform_device *pdev) char hw_mac_addr[ETH_ALEN]; const char *mac_from; int phy_mode; + int fixed_phy = 0; int err; /* Our multiqueue support is not complete, so for now, only @@ -2967,6 +3037,7 @@ static int mvneta_probe(struct platform_device *pdev) dev_err(&pdev->dev, "cannot register fixed PHY\n"); goto err_free_irq; } + fixed_phy = 1; /* In the case of a fixed PHY, the DT node associated * to the PHY is the Ethernet MAC DT node. @@ -2990,6 +3061,8 @@ static int mvneta_probe(struct platform_device *pdev) pp = netdev_priv(dev); pp->phy_node = phy_node; pp->phy_interface = phy_mode; + pp->use_inband_status = (phy_mode == PHY_INTERFACE_MODE_SGMII) && + fixed_phy; pp->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(pp->clk)) { @@ -3067,6 +3140,12 @@ static int mvneta_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pp->dev); + if (pp->use_inband_status) { + struct phy_device *phy = of_phy_find_device(dn); + + mvneta_fixed_link_update(pp, phy); + } + return 0; err_free_stats: diff --git a/drivers/net/ethernet/mellanox/mlx4/Makefile b/drivers/net/ethernet/mellanox/mlx4/Makefile index 3e9c70f15b42..c82217e0d22d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/Makefile +++ b/drivers/net/ethernet/mellanox/mlx4/Makefile @@ -1,7 +1,8 @@ obj-$(CONFIG_MLX4_CORE) += mlx4_core.o -mlx4_core-y := alloc.o catas.o cmd.o cq.o eq.o fw.o icm.o intf.o main.o mcg.o \ - mr.o pd.o port.o profile.o qp.o reset.o sense.o srq.o resource_tracker.o +mlx4_core-y := alloc.o catas.o cmd.o cq.o eq.o fw.o fw_qos.o icm.o intf.o \ + main.o mcg.o mr.o pd.o port.o profile.o qp.o reset.o sense.o \ + srq.o resource_tracker.o obj-$(CONFIG_MLX4_EN) += mlx4_en.o diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index 20b3c7b21e63..f0fbb4ade85d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -48,6 +48,7 @@ #include "mlx4.h" #include "fw.h" +#include "fw_qos.h" #define CMD_POLL_TOKEN 0xffff #define INBOX_MASK 0xffffffffffffff00ULL @@ -724,8 +725,10 @@ static int mlx4_cmd_wait(struct mlx4_dev *dev, u64 in_param, u64 *out_param, * on the host, we deprecate the error message for this * specific command/input_mod/opcode_mod/fw-status to be debug. */ - if (op == MLX4_CMD_SET_PORT && in_modifier == 1 && - op_modifier == 0 && context->fw_status == CMD_STAT_BAD_SIZE) + if (op == MLX4_CMD_SET_PORT && + (in_modifier == 1 || in_modifier == 2) && + op_modifier == MLX4_SET_PORT_IB_OPCODE && + context->fw_status == CMD_STAT_BAD_SIZE) mlx4_dbg(dev, "command 0x%x failed: fw status = 0x%x\n", op, context->fw_status); else @@ -1455,6 +1458,24 @@ static struct mlx4_cmd_info cmd_info[] = { .wrapper = mlx4_CMD_EPERM_wrapper, }, { + .opcode = MLX4_CMD_ALLOCATE_VPP, + .has_inbox = false, + .has_outbox = true, + .out_is_imm = false, + .encode_slave_id = false, + .verify = NULL, + .wrapper = mlx4_CMD_EPERM_wrapper, + }, + { + .opcode = MLX4_CMD_SET_VPORT_QOS, + .has_inbox = false, + .has_outbox = true, + .out_is_imm = false, + .encode_slave_id = false, + .verify = NULL, + .wrapper = mlx4_CMD_EPERM_wrapper, + }, + { .opcode = MLX4_CMD_CONF_SPECIAL_QP, .has_inbox = false, .has_outbox = false, @@ -1790,7 +1811,8 @@ static int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv, if (vp_oper->state.default_vlan == vp_admin->default_vlan && vp_oper->state.default_qos == vp_admin->default_qos && - vp_oper->state.link_state == vp_admin->link_state) + vp_oper->state.link_state == vp_admin->link_state && + vp_oper->state.qos_vport == vp_admin->qos_vport) return 0; if (!(priv->mfunc.master.slave_state[slave].active && @@ -1848,6 +1870,7 @@ static int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv, vp_oper->state.default_vlan = vp_admin->default_vlan; vp_oper->state.default_qos = vp_admin->default_qos; vp_oper->state.link_state = vp_admin->link_state; + vp_oper->state.qos_vport = vp_admin->qos_vport; if (vp_admin->link_state == IFLA_VF_LINK_STATE_DISABLE) work->flags |= MLX4_VF_IMMED_VLAN_FLAG_LINK_DISABLE; @@ -1856,6 +1879,7 @@ static int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv, work->port = port; work->slave = slave; work->qos = vp_oper->state.default_qos; + work->qos_vport = vp_oper->state.qos_vport; work->vlan_id = vp_oper->state.default_vlan; work->vlan_ix = vp_oper->vlan_idx; work->priv = priv; @@ -1865,6 +1889,63 @@ static int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv, return 0; } +static void mlx4_set_default_port_qos(struct mlx4_dev *dev, int port) +{ + struct mlx4_qos_manager *port_qos_ctl; + struct mlx4_priv *priv = mlx4_priv(dev); + + port_qos_ctl = &priv->mfunc.master.qos_ctl[port]; + bitmap_zero(port_qos_ctl->priority_bm, MLX4_NUM_UP); + + /* Enable only default prio at PF init routine */ + set_bit(MLX4_DEFAULT_QOS_PRIO, port_qos_ctl->priority_bm); +} + +static void mlx4_allocate_port_vpps(struct mlx4_dev *dev, int port) +{ + int i; + int err; + int num_vfs; + u16 availible_vpp; + u8 vpp_param[MLX4_NUM_UP]; + struct mlx4_qos_manager *port_qos; + struct mlx4_priv *priv = mlx4_priv(dev); + + err = mlx4_ALLOCATE_VPP_get(dev, port, &availible_vpp, vpp_param); + if (err) { + mlx4_info(dev, "Failed query availible VPPs\n"); + return; + } + + port_qos = &priv->mfunc.master.qos_ctl[port]; + num_vfs = (availible_vpp / + bitmap_weight(port_qos->priority_bm, MLX4_NUM_UP)); + + for (i = 0; i < MLX4_NUM_UP; i++) { + if (test_bit(i, port_qos->priority_bm)) + vpp_param[i] = num_vfs; + } + + err = mlx4_ALLOCATE_VPP_set(dev, port, vpp_param); + if (err) { + mlx4_info(dev, "Failed allocating VPPs\n"); + return; + } + + /* Query actual allocated VPP, just to make sure */ + err = mlx4_ALLOCATE_VPP_get(dev, port, &availible_vpp, vpp_param); + if (err) { + mlx4_info(dev, "Failed query availible VPPs\n"); + return; + } + + port_qos->num_of_qos_vfs = num_vfs; + mlx4_dbg(dev, "Port %d Availible VPPs %d\n", port, availible_vpp); + + for (i = 0; i < MLX4_NUM_UP; i++) + mlx4_dbg(dev, "Port %d UP %d Allocated %d VPPs\n", port, i, + vpp_param[i]); +} static int mlx4_master_activate_admin_state(struct mlx4_priv *priv, int slave) { @@ -2002,7 +2083,6 @@ static void mlx4_master_do_cmd(struct mlx4_dev *dev, int slave, u8 cmd, goto reset_slave; slave_state[slave].vhcr_dma = ((u64) param) << 48; priv->mfunc.master.slave_state[slave].cookie = 0; - mutex_init(&priv->mfunc.master.gen_eqe_mutex[slave]); break; case MLX4_COMM_CMD_VHCR1: if (slave_state[slave].last_cmd != MLX4_COMM_CMD_VHCR0) @@ -2213,6 +2293,9 @@ int mlx4_multi_func_init(struct mlx4_dev *dev) } if (mlx4_is_master(dev)) { + struct mlx4_vf_oper_state *vf_oper; + struct mlx4_vf_admin_state *vf_admin; + priv->mfunc.master.slave_state = kzalloc(dev->num_slaves * sizeof(struct mlx4_slave_state), GFP_KERNEL); @@ -2232,8 +2315,11 @@ int mlx4_multi_func_init(struct mlx4_dev *dev) goto err_comm_oper; for (i = 0; i < dev->num_slaves; ++i) { + vf_admin = &priv->mfunc.master.vf_admin[i]; + vf_oper = &priv->mfunc.master.vf_oper[i]; s_state = &priv->mfunc.master.slave_state[i]; s_state->last_cmd = MLX4_COMM_CMD_RESET; + mutex_init(&priv->mfunc.master.gen_eqe_mutex[i]); for (j = 0; j < MLX4_EVENT_TYPES_NUM; ++j) s_state->event_eq[j].eqn = -1; __raw_writel((__force u32) 0, @@ -2242,6 +2328,9 @@ int mlx4_multi_func_init(struct mlx4_dev *dev) &priv->mfunc.comm[i].slave_read); mmiowb(); for (port = 1; port <= MLX4_MAX_PORTS; port++) { + struct mlx4_vport_state *admin_vport; + struct mlx4_vport_state *oper_vport; + s_state->vlan_filter[port] = kzalloc(sizeof(struct mlx4_vlan_fltr), GFP_KERNEL); @@ -2250,15 +2339,30 @@ int mlx4_multi_func_init(struct mlx4_dev *dev) kfree(s_state->vlan_filter[port]); goto err_slaves; } + + admin_vport = &vf_admin->vport[port]; + oper_vport = &vf_oper->vport[port].state; INIT_LIST_HEAD(&s_state->mcast_filters[port]); - priv->mfunc.master.vf_admin[i].vport[port].default_vlan = MLX4_VGT; - priv->mfunc.master.vf_oper[i].vport[port].state.default_vlan = MLX4_VGT; - priv->mfunc.master.vf_oper[i].vport[port].vlan_idx = NO_INDX; - priv->mfunc.master.vf_oper[i].vport[port].mac_idx = NO_INDX; + admin_vport->default_vlan = MLX4_VGT; + oper_vport->default_vlan = MLX4_VGT; + admin_vport->qos_vport = + MLX4_VPP_DEFAULT_VPORT; + oper_vport->qos_vport = MLX4_VPP_DEFAULT_VPORT; + vf_oper->vport[port].vlan_idx = NO_INDX; + vf_oper->vport[port].mac_idx = NO_INDX; } spin_lock_init(&s_state->lock); } + if (dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_QOS_VPP) { + for (port = 1; port <= dev->caps.num_ports; port++) { + if (mlx4_is_eth(dev, port)) { + mlx4_set_default_port_qos(dev, port); + mlx4_allocate_port_vpps(dev, port); + } + } + } + memset(&priv->mfunc.master.cmd_eqe, 0, dev->caps.eqe_size); priv->mfunc.master.cmd_eqe.type = MLX4_EVENT_TYPE_CMD; INIT_WORK(&priv->mfunc.master.comm_work, @@ -2679,6 +2783,103 @@ static int mlx4_slaves_closest_port(struct mlx4_dev *dev, int slave, int port) return port; } +static int mlx4_set_vport_qos(struct mlx4_priv *priv, int slave, int port, + int max_tx_rate) +{ + int i; + int err; + struct mlx4_qos_manager *port_qos; + struct mlx4_dev *dev = &priv->dev; + struct mlx4_vport_qos_param vpp_qos[MLX4_NUM_UP]; + + port_qos = &priv->mfunc.master.qos_ctl[port]; + memset(vpp_qos, 0, sizeof(struct mlx4_vport_qos_param) * MLX4_NUM_UP); + + if (slave > port_qos->num_of_qos_vfs) { + mlx4_info(dev, "No availible VPP resources for this VF\n"); + return -EINVAL; + } + + /* Query for default QoS values from Vport 0 is needed */ + err = mlx4_SET_VPORT_QOS_get(dev, port, 0, vpp_qos); + if (err) { + mlx4_info(dev, "Failed to query Vport 0 QoS values\n"); + return err; + } + + for (i = 0; i < MLX4_NUM_UP; i++) { + if (test_bit(i, port_qos->priority_bm) && max_tx_rate) { + vpp_qos[i].max_avg_bw = max_tx_rate; + vpp_qos[i].enable = 1; + } else { + /* if user supplied tx_rate == 0, meaning no rate limit + * configuration is required. so we are leaving the + * value of max_avg_bw as queried from Vport 0. + */ + vpp_qos[i].enable = 0; + } + } + + err = mlx4_SET_VPORT_QOS_set(dev, port, slave, vpp_qos); + if (err) { + mlx4_info(dev, "Failed to set Vport %d QoS values\n", slave); + return err; + } + + return 0; +} + +static bool mlx4_is_vf_vst_and_prio_qos(struct mlx4_dev *dev, int port, + struct mlx4_vport_state *vf_admin) +{ + struct mlx4_qos_manager *info; + struct mlx4_priv *priv = mlx4_priv(dev); + + if (!mlx4_is_master(dev) || + !(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_QOS_VPP)) + return false; + + info = &priv->mfunc.master.qos_ctl[port]; + + if (vf_admin->default_vlan != MLX4_VGT && + test_bit(vf_admin->default_qos, info->priority_bm)) + return true; + + return false; +} + +static bool mlx4_valid_vf_state_change(struct mlx4_dev *dev, int port, + struct mlx4_vport_state *vf_admin, + int vlan, int qos) +{ + struct mlx4_vport_state dummy_admin = {0}; + + if (!mlx4_is_vf_vst_and_prio_qos(dev, port, vf_admin) || + !vf_admin->tx_rate) + return true; + + dummy_admin.default_qos = qos; + dummy_admin.default_vlan = vlan; + + /* VF wants to move to other VST state which is valid with current + * rate limit. Either differnt default vlan in VST or other + * supported QoS priority. Otherwise we don't allow this change when + * the TX rate is still configured. + */ + if (mlx4_is_vf_vst_and_prio_qos(dev, port, &dummy_admin)) + return true; + + mlx4_info(dev, "Cannot change VF state to %s while rate is set\n", + (vlan == MLX4_VGT) ? "VGT" : "VST"); + + if (vlan != MLX4_VGT) + mlx4_info(dev, "VST priority %d not supported for QoS\n", qos); + + mlx4_info(dev, "Please set rate to 0 prior to this VF state change\n"); + + return false; +} + int mlx4_set_vf_mac(struct mlx4_dev *dev, int port, int vf, u64 mac) { struct mlx4_priv *priv = mlx4_priv(dev); @@ -2722,12 +2923,22 @@ int mlx4_set_vf_vlan(struct mlx4_dev *dev, int port, int vf, u16 vlan, u8 qos) port = mlx4_slaves_closest_port(dev, slave, port); vf_admin = &priv->mfunc.master.vf_admin[slave].vport[port]; + if (!mlx4_valid_vf_state_change(dev, port, vf_admin, vlan, qos)) + return -EPERM; + if ((0 == vlan) && (0 == qos)) vf_admin->default_vlan = MLX4_VGT; else vf_admin->default_vlan = vlan; vf_admin->default_qos = qos; + /* If rate was configured prior to VST, we saved the configured rate + * in vf_admin->rate and now, if priority supported we enforce the QoS + */ + if (mlx4_is_vf_vst_and_prio_qos(dev, port, vf_admin) && + vf_admin->tx_rate) + vf_admin->qos_vport = slave; + if (mlx4_master_immediate_activate_vlan_qos(priv, slave, port)) mlx4_info(dev, "updating vf %d port %d config will take effect on next VF restart\n", @@ -2736,6 +2947,69 @@ int mlx4_set_vf_vlan(struct mlx4_dev *dev, int port, int vf, u16 vlan, u8 qos) } EXPORT_SYMBOL_GPL(mlx4_set_vf_vlan); +int mlx4_set_vf_rate(struct mlx4_dev *dev, int port, int vf, int min_tx_rate, + int max_tx_rate) +{ + int err; + int slave; + struct mlx4_vport_state *vf_admin; + struct mlx4_priv *priv = mlx4_priv(dev); + + if (!mlx4_is_master(dev) || + !(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_QOS_VPP)) + return -EPROTONOSUPPORT; + + if (min_tx_rate) { + mlx4_info(dev, "Minimum BW share not supported\n"); + return -EPROTONOSUPPORT; + } + + slave = mlx4_get_slave_indx(dev, vf); + if (slave < 0) + return -EINVAL; + + port = mlx4_slaves_closest_port(dev, slave, port); + vf_admin = &priv->mfunc.master.vf_admin[slave].vport[port]; + + err = mlx4_set_vport_qos(priv, slave, port, max_tx_rate); + if (err) { + mlx4_info(dev, "vf %d failed to set rate %d\n", vf, + max_tx_rate); + return err; + } + + vf_admin->tx_rate = max_tx_rate; + /* if VF is not in supported mode (VST with supported prio), + * we do not change vport configuration for its QPs, but save + * the rate, so it will be enforced when it moves to supported + * mode next time. + */ + if (!mlx4_is_vf_vst_and_prio_qos(dev, port, vf_admin)) { + mlx4_info(dev, + "rate set for VF %d when not in valid state\n", vf); + + if (vf_admin->default_vlan != MLX4_VGT) + mlx4_info(dev, "VST priority not supported by QoS\n"); + else + mlx4_info(dev, "VF in VGT mode (needed VST)\n"); + + mlx4_info(dev, + "rate %d take affect when VF moves to valid state\n", + max_tx_rate); + return 0; + } + + /* If user sets rate 0 assigning default vport for its QPs */ + vf_admin->qos_vport = max_tx_rate ? slave : MLX4_VPP_DEFAULT_VPORT; + + if (priv->mfunc.master.slave_state[slave].active && + dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_UPDATE_QP) + mlx4_master_immediate_activate_vlan_qos(priv, slave, port); + + return 0; +} +EXPORT_SYMBOL_GPL(mlx4_set_vf_rate); + /* mlx4_get_slave_default_vlan - * return true if VST ( default vlan) * if VST, will return vlan & qos (if not NULL) @@ -2809,7 +3083,12 @@ int mlx4_get_vf_config(struct mlx4_dev *dev, int port, int vf, struct ifla_vf_in ivf->vlan = s_info->default_vlan; ivf->qos = s_info->default_qos; - ivf->max_tx_rate = s_info->tx_rate; + + if (mlx4_is_vf_vst_and_prio_qos(dev, port, s_info)) + ivf->max_tx_rate = s_info->tx_rate; + else + ivf->max_tx_rate = 0; + ivf->min_tx_rate = 0; ivf->spoofchk = s_info->spoofchk; ivf->linkstate = s_info->link_state; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c b/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c index 8e3260c0eaa5..f01918c63f28 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c @@ -35,6 +35,7 @@ #include <linux/math64.h> #include "mlx4_en.h" +#include "fw_qos.h" /* Definitions for QCN */ diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c index eba969b08dd1..3f44e2bbb982 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c @@ -1939,6 +1939,32 @@ static int mlx4_en_get_module_eeprom(struct net_device *dev, return 0; } +static int mlx4_en_set_phys_id(struct net_device *dev, + enum ethtool_phys_id_state state) +{ + int err; + u16 beacon_duration; + struct mlx4_en_priv *priv = netdev_priv(dev); + struct mlx4_en_dev *mdev = priv->mdev; + + if (!(mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_PORT_BEACON)) + return -EOPNOTSUPP; + + switch (state) { + case ETHTOOL_ID_ACTIVE: + beacon_duration = PORT_BEACON_MAX_LIMIT; + break; + case ETHTOOL_ID_INACTIVE: + beacon_duration = 0; + break; + default: + return -EOPNOTSUPP; + } + + err = mlx4_SET_PORT_BEACON(mdev->dev, priv->port, beacon_duration); + return err; +} + const struct ethtool_ops mlx4_en_ethtool_ops = { .get_drvinfo = mlx4_en_get_drvinfo, .get_settings = mlx4_en_get_settings, @@ -1948,6 +1974,7 @@ const struct ethtool_ops mlx4_en_ethtool_ops = { .get_sset_count = mlx4_en_get_sset_count, .get_ethtool_stats = mlx4_en_get_ethtool_stats, .self_test = mlx4_en_self_test, + .set_phys_id = mlx4_en_set_phys_id, .get_wol = mlx4_en_get_wol, .set_wol = mlx4_en_set_wol, .get_msglevel = mlx4_en_get_msglevel, diff --git a/drivers/net/ethernet/mellanox/mlx4/en_main.c b/drivers/net/ethernet/mellanox/mlx4/en_main.c index 58d5a07d0ff4..913b716ed2e1 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_main.c @@ -103,6 +103,11 @@ void mlx4_en_update_loopback_state(struct net_device *dev, { struct mlx4_en_priv *priv = netdev_priv(dev); + if (features & NETIF_F_LOOPBACK) + priv->ctrl_flags |= cpu_to_be32(MLX4_WQE_CTRL_FORCE_LOOPBACK); + else + priv->ctrl_flags &= cpu_to_be32(~MLX4_WQE_CTRL_FORCE_LOOPBACK); + priv->flags &= ~(MLX4_EN_FLAG_RX_FILTER_NEEDED| MLX4_EN_FLAG_ENABLE_HW_LOOPBACK); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 354e254b53cf..0f1afc085d58 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -2195,31 +2195,50 @@ static int mlx4_en_set_features(struct net_device *netdev, netdev_features_t features) { struct mlx4_en_priv *priv = netdev_priv(netdev); + bool reset = false; int ret = 0; + if (DEV_FEATURE_CHANGED(netdev, features, NETIF_F_RXFCS)) { + en_info(priv, "Turn %s RX-FCS\n", + (features & NETIF_F_RXFCS) ? "ON" : "OFF"); + reset = true; + } + + if (DEV_FEATURE_CHANGED(netdev, features, NETIF_F_RXALL)) { + u8 ignore_fcs_value = (features & NETIF_F_RXALL) ? 1 : 0; + + en_info(priv, "Turn %s RX-ALL\n", + ignore_fcs_value ? "ON" : "OFF"); + ret = mlx4_SET_PORT_fcs_check(priv->mdev->dev, + priv->port, ignore_fcs_value); + if (ret) + return ret; + } + if (DEV_FEATURE_CHANGED(netdev, features, NETIF_F_HW_VLAN_CTAG_RX)) { en_info(priv, "Turn %s RX vlan strip offload\n", (features & NETIF_F_HW_VLAN_CTAG_RX) ? "ON" : "OFF"); - ret = mlx4_en_reset_config(netdev, priv->hwtstamp_config, - features); - if (ret) - return ret; + reset = true; } if (DEV_FEATURE_CHANGED(netdev, features, NETIF_F_HW_VLAN_CTAG_TX)) en_info(priv, "Turn %s TX vlan strip offload\n", (features & NETIF_F_HW_VLAN_CTAG_TX) ? "ON" : "OFF"); - if (features & NETIF_F_LOOPBACK) - priv->ctrl_flags |= cpu_to_be32(MLX4_WQE_CTRL_FORCE_LOOPBACK); - else - priv->ctrl_flags &= - cpu_to_be32(~MLX4_WQE_CTRL_FORCE_LOOPBACK); + if (DEV_FEATURE_CHANGED(netdev, features, NETIF_F_LOOPBACK)) { + en_info(priv, "Turn %s loopback\n", + (features & NETIF_F_LOOPBACK) ? "ON" : "OFF"); + mlx4_en_update_loopback_state(netdev, features); + } - mlx4_en_update_loopback_state(netdev, features); + if (reset) { + ret = mlx4_en_reset_config(netdev, priv->hwtstamp_config, + features); + if (ret) + return ret; + } return 0; - } static int mlx4_en_set_vf_mac(struct net_device *dev, int queue, u8 *mac) @@ -2242,6 +2261,16 @@ static int mlx4_en_set_vf_vlan(struct net_device *dev, int vf, u16 vlan, u8 qos) return mlx4_set_vf_vlan(mdev->dev, en_priv->port, vf, vlan, qos); } +static int mlx4_en_set_vf_rate(struct net_device *dev, int vf, int min_tx_rate, + int max_tx_rate) +{ + struct mlx4_en_priv *en_priv = netdev_priv(dev); + struct mlx4_en_dev *mdev = en_priv->mdev; + + return mlx4_set_vf_rate(mdev->dev, en_priv->port, vf, min_tx_rate, + max_tx_rate); +} + static int mlx4_en_set_vf_spoofchk(struct net_device *dev, int vf, bool setting) { struct mlx4_en_priv *en_priv = netdev_priv(dev); @@ -2460,6 +2489,7 @@ static const struct net_device_ops mlx4_netdev_ops_master = { .ndo_vlan_rx_kill_vid = mlx4_en_vlan_rx_kill_vid, .ndo_set_vf_mac = mlx4_en_set_vf_mac, .ndo_set_vf_vlan = mlx4_en_set_vf_vlan, + .ndo_set_vf_rate = mlx4_en_set_vf_rate, .ndo_set_vf_spoofchk = mlx4_en_set_vf_spoofchk, .ndo_set_vf_link_state = mlx4_en_set_vf_link_state, .ndo_get_vf_config = mlx4_en_get_vf_config, @@ -2805,7 +2835,7 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, priv->msg_enable = MLX4_EN_MSG_LEVEL; #ifdef CONFIG_MLX4_EN_DCB if (!mlx4_is_slave(priv->mdev->dev)) { - if (mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_SET_ETH_SCHED) { + if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_ETS_CFG) { dev->dcbnl_ops = &mlx4_en_dcbnl_ops; } else { en_info(priv, "enabling only PFC DCB ops\n"); @@ -2892,6 +2922,12 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, dev->hw_features |= NETIF_F_LOOPBACK | NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; + if (mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_FCS_KEEP) + dev->hw_features |= NETIF_F_RXFCS; + + if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_IGNORE_FCS) + dev->hw_features |= NETIF_F_RXALL; + if (mdev->dev->caps.steering_mode == MLX4_STEERING_MODE_DEVICE_MANAGED && mdev->dev->caps.dmfs_high_steer_mode != MLX4_STEERING_DMFS_A0_STATIC) @@ -2917,13 +2953,6 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, netif_carrier_off(dev); mlx4_en_set_default_moderation(priv); - err = register_netdev(dev); - if (err) { - en_err(priv, "Netdev registration failed for port %d\n", port); - goto out; - } - priv->registered = 1; - en_warn(priv, "Using %d TX rings\n", prof->tx_ring_num); en_warn(priv, "Using %d RX rings\n", prof->rx_ring_num); @@ -2969,6 +2998,14 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, mdev->profile.prof[priv->port].tx_ppp, mdev->profile.prof[priv->port].tx_pause); + err = register_netdev(dev); + if (err) { + en_err(priv, "Netdev registration failed for port %d\n", port); + goto out; + } + + priv->registered = 1; + return 0; out: @@ -2987,7 +3024,8 @@ int mlx4_en_reset_config(struct net_device *dev, if (priv->hwtstamp_config.tx_type == ts_config.tx_type && priv->hwtstamp_config.rx_filter == ts_config.rx_filter && - !DEV_FEATURE_CHANGED(dev, features, NETIF_F_HW_VLAN_CTAG_RX)) + !DEV_FEATURE_CHANGED(dev, features, NETIF_F_HW_VLAN_CTAG_RX) && + !DEV_FEATURE_CHANGED(dev, features, NETIF_F_RXFCS)) return 0; /* Nothing to change */ if (DEV_FEATURE_CHANGED(dev, features, NETIF_F_HW_VLAN_CTAG_RX) && @@ -3026,6 +3064,13 @@ int mlx4_en_reset_config(struct net_device *dev, dev->features &= ~NETIF_F_HW_VLAN_CTAG_RX; } + if (DEV_FEATURE_CHANGED(dev, features, NETIF_F_RXFCS)) { + if (features & NETIF_F_RXFCS) + dev->features |= NETIF_F_RXFCS; + else + dev->features &= ~NETIF_F_RXFCS; + } + /* RX vlan offload and RX time-stamping can't co-exist ! * Regardless of the caller's choice, * Turn Off RX vlan offload in case of time-stamping is ON diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index 698d60de1255..79b1501e7951 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -1116,7 +1116,10 @@ static int mlx4_en_config_rss_qp(struct mlx4_en_priv *priv, int qpn, /* Cancel FCS removal if FW allows */ if (mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_FCS_KEEP) { context->param3 |= cpu_to_be32(1 << 29); - ring->fcs_del = ETH_FCS_LEN; + if (priv->dev->features & NETIF_F_RXFCS) + ring->fcs_del = 0; + else + ring->fcs_del = ETH_FCS_LEN; } else ring->fcs_del = 0; diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c index 264bc15c1ff2..6e70ffee8e87 100644 --- a/drivers/net/ethernet/mellanox/mlx4/eq.c +++ b/drivers/net/ethernet/mellanox/mlx4/eq.c @@ -153,12 +153,10 @@ void mlx4_gen_slave_eqe(struct work_struct *work) /* All active slaves need to receive the event */ if (slave == ALL_SLAVES) { - for (i = 0; i < dev->num_slaves; i++) { - if (i != dev->caps.function && - master->slave_state[i].active) - if (mlx4_GEN_EQE(dev, i, eqe)) - mlx4_warn(dev, "Failed to generate event for slave %d\n", - i); + for (i = 0; i <= dev->persist->num_vfs; i++) { + if (mlx4_GEN_EQE(dev, i, eqe)) + mlx4_warn(dev, "Failed to generate event for slave %d\n", + i); } } else { if (mlx4_GEN_EQE(dev, slave, eqe)) @@ -203,13 +201,11 @@ static void mlx4_slave_event(struct mlx4_dev *dev, int slave, struct mlx4_eqe *eqe) { struct mlx4_priv *priv = mlx4_priv(dev); - struct mlx4_slave_state *s_slave = - &priv->mfunc.master.slave_state[slave]; - if (!s_slave->active) { - /*mlx4_warn(dev, "Trying to pass event to inactive slave\n");*/ + if (slave < 0 || slave > dev->persist->num_vfs || + slave == dev->caps.function || + !priv->mfunc.master.slave_state[slave].active) return; - } slave_event(dev, slave, eqe); } diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 209a6171e59b..b9881fc1252f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -49,9 +49,9 @@ enum { extern void __buggy_use_of_MLX4_GET(void); extern void __buggy_use_of_MLX4_PUT(void); -static bool enable_qos; +static bool enable_qos = true; module_param(enable_qos, bool, 0444); -MODULE_PARM_DESC(enable_qos, "Enable Quality of Service support in the HCA (default: off)"); +MODULE_PARM_DESC(enable_qos, "Enable Enhanced QoS support (default: on)"); #define MLX4_GET(dest, source, offset) \ do { \ @@ -105,6 +105,7 @@ static void dump_dev_cap_flags(struct mlx4_dev *dev, u64 flags) [41] = "Unicast VEP steering support", [42] = "Multicast VEP steering support", [48] = "Counters support", + [52] = "RSS IP fragments support", [53] = "Port ETS Scheduler support", [55] = "Port link type sensing support", [59] = "Port management change event support", @@ -146,7 +147,11 @@ static void dump_dev_cap_flags2(struct mlx4_dev *dev, u64 flags) [21] = "Port Remap support", [22] = "QCN support", [23] = "QP rate limiting support", - [24] = "Ethernet Flow control statistics support" + [24] = "Ethernet Flow control statistics support", + [25] = "Granular QoS per VF support", + [26] = "Port ETS Scheduler support", + [27] = "Port beacon support", + [28] = "RX-ALL support", }; int i; @@ -644,6 +649,7 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) #define QUERY_DEV_CAP_RSS_OFFSET 0x2e #define QUERY_DEV_CAP_MAX_RDMA_OFFSET 0x2f #define QUERY_DEV_CAP_RSZ_SRQ_OFFSET 0x33 +#define QUERY_DEV_CAP_PORT_BEACON_OFFSET 0x34 #define QUERY_DEV_CAP_ACK_DELAY_OFFSET 0x35 #define QUERY_DEV_CAP_MTU_WIDTH_OFFSET 0x36 #define QUERY_DEV_CAP_VL_PORT_OFFSET 0x37 @@ -783,6 +789,9 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) if (field & 0x80) dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_FS_EN; dev_cap->fs_log_max_ucast_qp_range_size = field & 0x1f; + MLX4_GET(field, outbox, QUERY_DEV_CAP_PORT_BEACON_OFFSET); + if (field & 0x80) + dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_PORT_BEACON; MLX4_GET(field, outbox, QUERY_DEV_CAP_FLOW_STEERING_IPOIB_OFFSET); if (field & 0x80) dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_DMFS_IPOIB; @@ -870,6 +879,8 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) MLX4_GET(size, outbox, QUERY_DEV_CAP_MAX_DESC_SZ_RQ_OFFSET); dev_cap->max_rq_desc_sz = size; MLX4_GET(field, outbox, QUERY_DEV_CAP_CQ_EQ_CACHE_LINE_STRIDE); + if (field & (1 << 4)) + dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_QOS_VPP; if (field & (1 << 5)) dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_ETH_PROT_CTRL; if (field & (1 << 6)) @@ -883,6 +894,8 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) MLX4_GET(field, outbox, QUERY_DEV_CAP_CONFIG_DEV_OFFSET); if (field & 0x20) dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_CONFIG_DEV; + if (field & (1 << 2)) + dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_IGNORE_FCS; MLX4_GET(dev_cap->reserved_lkey, outbox, QUERY_DEV_CAP_RSVD_LKEY_OFFSET); MLX4_GET(field32, outbox, QUERY_DEV_CAP_ETH_BACKPL_OFFSET); @@ -896,6 +909,8 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) MLX4_GET(field, outbox, QUERY_DEV_CAP_VXLAN); if (field & 1<<3) dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_VXLAN_OFFLOADS; + if (field & (1 << 5)) + dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_ETS_CFG; MLX4_GET(dev_cap->max_icm_sz, outbox, QUERY_DEV_CAP_MAX_ICM_SZ_OFFSET); if (dev_cap->flags & MLX4_DEV_CAP_FLAG_COUNTERS) @@ -1138,6 +1153,9 @@ int mlx4_QUERY_DEV_CAP_wrapper(struct mlx4_dev *dev, int slave, } for (; slave_port < dev->caps.num_ports; ++slave_port) flags &= ~(MLX4_DEV_CAP_FLAG_WOL_PORT1 << slave_port); + + /* Not exposing RSS IP fragments to guests */ + flags &= ~MLX4_DEV_CAP_FLAG_RSS_IP_FRAG; MLX4_PUT(outbox->buf, flags, QUERY_DEV_CAP_EXT_FLAGS_OFFSET); MLX4_GET(field, outbox->buf, QUERY_DEV_CAP_VL_PORT_OFFSET); @@ -1150,11 +1168,16 @@ int mlx4_QUERY_DEV_CAP_wrapper(struct mlx4_dev *dev, int slave, field &= 0x7f; MLX4_PUT(outbox->buf, field, QUERY_DEV_CAP_CQ_TS_SUPPORT_OFFSET); - /* For guests, disable vxlan tunneling */ + /* For guests, disable vxlan tunneling and QoS support */ MLX4_GET(field, outbox->buf, QUERY_DEV_CAP_VXLAN); - field &= 0xf7; + field &= 0xd7; MLX4_PUT(outbox->buf, field, QUERY_DEV_CAP_VXLAN); + /* For guests, disable port BEACON */ + MLX4_GET(field, outbox->buf, QUERY_DEV_CAP_PORT_BEACON_OFFSET); + field &= 0x7f; + MLX4_PUT(outbox->buf, field, QUERY_DEV_CAP_PORT_BEACON_OFFSET); + /* For guests, report Blueflame disabled */ MLX4_GET(field, outbox->buf, QUERY_DEV_CAP_BF_OFFSET); field &= 0x7f; @@ -1195,6 +1218,16 @@ int mlx4_QUERY_DEV_CAP_wrapper(struct mlx4_dev *dev, int slave, field16 = 0; MLX4_PUT(outbox->buf, field16, QUERY_DEV_CAP_QP_RATE_LIMIT_NUM_OFFSET); + /* turn off QoS per VF support for guests */ + MLX4_GET(field, outbox->buf, QUERY_DEV_CAP_CQ_EQ_CACHE_LINE_STRIDE); + field &= 0xef; + MLX4_PUT(outbox->buf, field, QUERY_DEV_CAP_CQ_EQ_CACHE_LINE_STRIDE); + + /* turn off ignore FCS feature for guests */ + MLX4_GET(field, outbox->buf, QUERY_DEV_CAP_CONFIG_DEV_OFFSET); + field &= 0xfb; + MLX4_PUT(outbox->buf, field, QUERY_DEV_CAP_CONFIG_DEV_OFFSET); + return 0; } @@ -1694,13 +1727,17 @@ int mlx4_INIT_HCA(struct mlx4_dev *dev, struct mlx4_init_hca_param *param) *(inbox + INIT_HCA_FLAGS_OFFSET / 4) |= cpu_to_be32(1 << 3); /* Enable QoS support if module parameter set */ - if (enable_qos) + if (dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_ETS_CFG && enable_qos) *(inbox + INIT_HCA_FLAGS_OFFSET / 4) |= cpu_to_be32(1 << 2); /* enable counters */ if (dev->caps.flags & MLX4_DEV_CAP_FLAG_COUNTERS) *(inbox + INIT_HCA_FLAGS_OFFSET / 4) |= cpu_to_be32(1 << 4); + /* Enable RSS spread to fragmented IP packets when supported */ + if (dev->caps.flags & MLX4_DEV_CAP_FLAG_RSS_IP_FRAG) + *(inbox + INIT_HCA_FLAGS_OFFSET / 4) |= cpu_to_be32(1 << 13); + /* CX3 is capable of extending CQEs/EQEs from 32 to 64 bytes */ if (dev->caps.flags & MLX4_DEV_CAP_FLAG_64B_EQE) { *(inbox + INIT_HCA_EQE_CQE_OFFSETS / 4) |= cpu_to_be32(1 << 29); @@ -1889,6 +1926,10 @@ int mlx4_QUERY_HCA(struct mlx4_dev *dev, else param->steering_mode = MLX4_STEERING_MODE_A0; } + + if (dword_field & (1 << 13)) + param->rss_ip_frags = 1; + /* steering attributes */ if (param->steering_mode == MLX4_STEERING_MODE_DEVICE_MANAGED) { MLX4_GET(param->mc_base, outbox, INIT_HCA_FS_BASE_OFFSET); diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.h b/drivers/net/ethernet/mellanox/mlx4/fw.h index 863655bd3947..07cb7c2461ad 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.h +++ b/drivers/net/ethernet/mellanox/mlx4/fw.h @@ -203,6 +203,7 @@ struct mlx4_init_hca_param { u64 dev_cap_enabled; u16 cqe_size; /* For use only when CQE stride feature enabled */ u16 eqe_size; /* For use only when EQE stride feature enabled */ + u8 rss_ip_frags; }; struct mlx4_init_ib_param { diff --git a/drivers/net/ethernet/mellanox/mlx4/fw_qos.c b/drivers/net/ethernet/mellanox/mlx4/fw_qos.c new file mode 100644 index 000000000000..8f2fde0487c4 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx4/fw_qos.c @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. + * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. + * All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/export.h> +#include "fw_qos.h" +#include "fw.h" + +enum { + /* allocate vpp opcode modifiers */ + MLX4_ALLOCATE_VPP_ALLOCATE = 0x0, + MLX4_ALLOCATE_VPP_QUERY = 0x1 +}; + +enum { + /* set vport qos opcode modifiers */ + MLX4_SET_VPORT_QOS_SET = 0x0, + MLX4_SET_VPORT_QOS_QUERY = 0x1 +}; + +struct mlx4_set_port_prio2tc_context { + u8 prio2tc[4]; +}; + +struct mlx4_port_scheduler_tc_cfg_be { + __be16 pg; + __be16 bw_precentage; + __be16 max_bw_units; /* 3-100Mbps, 4-1Gbps, other values - reserved */ + __be16 max_bw_value; +}; + +struct mlx4_set_port_scheduler_context { + struct mlx4_port_scheduler_tc_cfg_be tc[MLX4_NUM_TC]; +}; + +/* Granular Qos (per VF) section */ +struct mlx4_alloc_vpp_param { + __be32 availible_vpp; + __be32 vpp_p_up[MLX4_NUM_UP]; +}; + +struct mlx4_prio_qos_param { + __be32 bw_share; + __be32 max_avg_bw; + __be32 reserved; + __be32 enable; + __be32 reserved1[4]; +}; + +struct mlx4_set_vport_context { + __be32 reserved[8]; + struct mlx4_prio_qos_param qos_p_up[MLX4_NUM_UP]; +}; + +int mlx4_SET_PORT_PRIO2TC(struct mlx4_dev *dev, u8 port, u8 *prio2tc) +{ + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_set_port_prio2tc_context *context; + int err; + u32 in_mod; + int i; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + + context = mailbox->buf; + + for (i = 0; i < MLX4_NUM_UP; i += 2) + context->prio2tc[i >> 1] = prio2tc[i] << 4 | prio2tc[i + 1]; + + in_mod = MLX4_SET_PORT_PRIO2TC << 8 | port; + err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT, + MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); + + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} +EXPORT_SYMBOL(mlx4_SET_PORT_PRIO2TC); + +int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw, + u8 *pg, u16 *ratelimit) +{ + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_set_port_scheduler_context *context; + int err; + u32 in_mod; + int i; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + + context = mailbox->buf; + + for (i = 0; i < MLX4_NUM_TC; i++) { + struct mlx4_port_scheduler_tc_cfg_be *tc = &context->tc[i]; + u16 r; + + if (ratelimit && ratelimit[i]) { + if (ratelimit[i] <= MLX4_MAX_100M_UNITS_VAL) { + r = ratelimit[i]; + tc->max_bw_units = + htons(MLX4_RATELIMIT_100M_UNITS); + } else { + r = ratelimit[i] / 10; + tc->max_bw_units = + htons(MLX4_RATELIMIT_1G_UNITS); + } + tc->max_bw_value = htons(r); + } else { + tc->max_bw_value = htons(MLX4_RATELIMIT_DEFAULT); + tc->max_bw_units = htons(MLX4_RATELIMIT_1G_UNITS); + } + + tc->pg = htons(pg[i]); + tc->bw_precentage = htons(tc_tx_bw[i]); + } + + in_mod = MLX4_SET_PORT_SCHEDULER << 8 | port; + err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT, + MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); + + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} +EXPORT_SYMBOL(mlx4_SET_PORT_SCHEDULER); + +int mlx4_ALLOCATE_VPP_get(struct mlx4_dev *dev, u8 port, + u16 *availible_vpp, u8 *vpp_p_up) +{ + int i; + int err; + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_alloc_vpp_param *out_param; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + + out_param = mailbox->buf; + + err = mlx4_cmd_box(dev, 0, mailbox->dma, port, + MLX4_ALLOCATE_VPP_QUERY, + MLX4_CMD_ALLOCATE_VPP, + MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + if (err) + goto out; + + /* Total number of supported VPPs */ + *availible_vpp = (u16)be32_to_cpu(out_param->availible_vpp); + + for (i = 0; i < MLX4_NUM_UP; i++) + vpp_p_up[i] = (u8)be32_to_cpu(out_param->vpp_p_up[i]); + +out: + mlx4_free_cmd_mailbox(dev, mailbox); + + return err; +} +EXPORT_SYMBOL(mlx4_ALLOCATE_VPP_get); + +int mlx4_ALLOCATE_VPP_set(struct mlx4_dev *dev, u8 port, u8 *vpp_p_up) +{ + int i; + int err; + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_alloc_vpp_param *in_param; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + + in_param = mailbox->buf; + + for (i = 0; i < MLX4_NUM_UP; i++) + in_param->vpp_p_up[i] = cpu_to_be32(vpp_p_up[i]); + + err = mlx4_cmd(dev, mailbox->dma, port, + MLX4_ALLOCATE_VPP_ALLOCATE, + MLX4_CMD_ALLOCATE_VPP, + MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} +EXPORT_SYMBOL(mlx4_ALLOCATE_VPP_set); + +int mlx4_SET_VPORT_QOS_get(struct mlx4_dev *dev, u8 port, u8 vport, + struct mlx4_vport_qos_param *out_param) +{ + int i; + int err; + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_set_vport_context *ctx; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + + ctx = mailbox->buf; + + err = mlx4_cmd_box(dev, 0, mailbox->dma, (vport << 8) | port, + MLX4_SET_VPORT_QOS_QUERY, + MLX4_CMD_SET_VPORT_QOS, + MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + if (err) + goto out; + + for (i = 0; i < MLX4_NUM_UP; i++) { + out_param[i].bw_share = be32_to_cpu(ctx->qos_p_up[i].bw_share); + out_param[i].max_avg_bw = + be32_to_cpu(ctx->qos_p_up[i].max_avg_bw); + out_param[i].enable = + !!(be32_to_cpu(ctx->qos_p_up[i].enable) & 31); + } + +out: + mlx4_free_cmd_mailbox(dev, mailbox); + + return err; +} +EXPORT_SYMBOL(mlx4_SET_VPORT_QOS_get); + +int mlx4_SET_VPORT_QOS_set(struct mlx4_dev *dev, u8 port, u8 vport, + struct mlx4_vport_qos_param *in_param) +{ + int i; + int err; + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_set_vport_context *ctx; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + + ctx = mailbox->buf; + + for (i = 0; i < MLX4_NUM_UP; i++) { + ctx->qos_p_up[i].bw_share = cpu_to_be32(in_param[i].bw_share); + ctx->qos_p_up[i].max_avg_bw = + cpu_to_be32(in_param[i].max_avg_bw); + ctx->qos_p_up[i].enable = + cpu_to_be32(in_param[i].enable << 31); + } + + err = mlx4_cmd(dev, mailbox->dma, (vport << 8) | port, + MLX4_SET_VPORT_QOS_SET, + MLX4_CMD_SET_VPORT_QOS, + MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} +EXPORT_SYMBOL(mlx4_SET_VPORT_QOS_set); diff --git a/drivers/net/ethernet/mellanox/mlx4/fw_qos.h b/drivers/net/ethernet/mellanox/mlx4/fw_qos.h new file mode 100644 index 000000000000..ac1f331878e6 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx4/fw_qos.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. + * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. + * All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MLX4_FW_QOS_H +#define MLX4_FW_QOS_H + +#include <linux/mlx4/cmd.h> +#include <linux/mlx4/device.h> + +#define MLX4_NUM_UP 8 +#define MLX4_NUM_TC 8 + +/* Default supported priorities for VPP allocation */ +#define MLX4_DEFAULT_QOS_PRIO (0) + +/* Derived from FW feature definition, 0 is the default vport fo all QPs */ +#define MLX4_VPP_DEFAULT_VPORT (0) + +struct mlx4_vport_qos_param { + u32 bw_share; + u32 max_avg_bw; + u8 enable; +}; + +/** + * mlx4_SET_PORT_PRIO2TC - This routine maps user priorities to traffic + * classes of a given port and device. + * + * @dev: mlx4_dev. + * @port: Physical port number. + * @prio2tc: Array of TC associated with each priorities. + * + * Returns 0 on success or a negative mlx4_core errno code. + **/ +int mlx4_SET_PORT_PRIO2TC(struct mlx4_dev *dev, u8 port, u8 *prio2tc); + +/** + * mlx4_SET_PORT_SCHEDULER - This routine configures the arbitration between + * traffic classes (ETS) and configured rate limit for traffic classes. + * tc_tx_bw, pg and ratelimit are arrays where each index represents a TC. + * The description for those parameters below refers to a single TC. + * + * @dev: mlx4_dev. + * @port: Physical port number. + * @tc_tx_bw: The percentage of the bandwidth allocated for traffic class + * within a TC group. The sum of the bw_percentage of all the traffic + * classes within a TC group must equal 100% for correct operation. + * @pg: The TC group the traffic class is associated with. + * @ratelimit: The maximal bandwidth allowed for the use by this traffic class. + * + * Returns 0 on success or a negative mlx4_core errno code. + **/ +int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw, + u8 *pg, u16 *ratelimit); +/** + * mlx4_ALLOCATE_VPP_get - Query port VPP availible resources and allocation. + * Before distribution of VPPs to priorities, only availible_vpp is returned. + * After initialization it returns the distribution of VPPs among priorities. + * + * @dev: mlx4_dev. + * @port: Physical port number. + * @availible_vpp: Pointer to variable where number of availible VPPs is stored + * @vpp_p_up: Distribution of VPPs to priorities is stored in this array + * + * Returns 0 on success or a negative mlx4_core errno code. + **/ +int mlx4_ALLOCATE_VPP_get(struct mlx4_dev *dev, u8 port, + u16 *availible_vpp, u8 *vpp_p_up); +/** + * mlx4_ALLOCATE_VPP_set - Distribution of VPPs among differnt priorities. + * The total number of VPPs assigned to all for a port must not exceed + * the value reported by availible_vpp in mlx4_ALLOCATE_VPP_get. + * VPP allocation is allowed only after the port type has been set, + * and while no QPs are open for this port. + * + * @dev: mlx4_dev. + * @port: Physical port number. + * @vpp_p_up: Allocation of VPPs to different priorities. + * + * Returns 0 on success or a negative mlx4_core errno code. + **/ +int mlx4_ALLOCATE_VPP_set(struct mlx4_dev *dev, u8 port, u8 *vpp_p_up); + +/** + * mlx4_SET_VPORT_QOS_get - Query QoS proporties of a Vport. + * Each priority allowed for the Vport is assigned with a share of the BW, + * and a BW limitation. This commands query the current QoS values. + * + * @dev: mlx4_dev. + * @port: Physical port number. + * @vport: Vport id. + * @out_param: Array of mlx4_vport_qos_param that will contain the values. + * + * Returns 0 on success or a negative mlx4_core errno code. + **/ +int mlx4_SET_VPORT_QOS_get(struct mlx4_dev *dev, u8 port, u8 vport, + struct mlx4_vport_qos_param *out_param); + +/** + * mlx4_SET_VPORT_QOS_set - Set QoS proporties of a Vport. + * QoS parameters can be modified at any time, but must be initialized + * before any QP is associated with the VPort. + * + * @dev: mlx4_dev. + * @port: Physical port number. + * @vport: Vport id. + * @out_param: Array of mlx4_vport_qos_param which holds the requested values. + * + * Returns 0 on success or a negative mlx4_core errno code. + **/ +int mlx4_SET_VPORT_QOS_set(struct mlx4_dev *dev, u8 port, u8 vport, + struct mlx4_vport_qos_param *in_param); + +#endif /* MLX4_FW_QOS_H */ diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 43aa76775b5f..acceb75e8c44 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -297,6 +297,25 @@ static int mlx4_dev_port(struct mlx4_dev *dev, int port, return err; } +static inline void mlx4_enable_ignore_fcs(struct mlx4_dev *dev) +{ + if (!(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_IGNORE_FCS)) + return; + + if (mlx4_is_mfunc(dev)) { + mlx4_dbg(dev, "SRIOV mode - Disabling Ignore FCS"); + dev->caps.flags2 &= ~MLX4_DEV_CAP_FLAG2_IGNORE_FCS; + return; + } + + if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_FCS_KEEP)) { + mlx4_dbg(dev, + "Keep FCS is not supported - Disabling Ignore FCS"); + dev->caps.flags2 &= ~MLX4_DEV_CAP_FLAG2_IGNORE_FCS; + return; + } +} + #define MLX4_A0_STEERING_TABLE_SIZE 256 static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) { @@ -528,10 +547,20 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) dev->caps.alloc_res_qp_mask = (dev->caps.bf_reg_size ? MLX4_RESERVE_ETH_BF_QP : 0) | MLX4_RESERVE_A0_QP; + + if (!(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_ETS_CFG) && + dev->caps.flags & MLX4_DEV_CAP_FLAG_SET_ETH_SCHED) { + mlx4_warn(dev, "Old device ETS support detected\n"); + mlx4_warn(dev, "Consider upgrading device FW.\n"); + dev->caps.flags2 |= MLX4_DEV_CAP_FLAG2_ETS_CFG; + } + } else { dev->caps.alloc_res_qp_mask = 0; } + mlx4_enable_ignore_fcs(dev); + return 0; } @@ -885,6 +914,8 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) mlx4_warn(dev, "Timestamping is not supported in slave mode\n"); slave_adjust_steering_mode(dev, &dev_cap, &hca_param); + mlx4_dbg(dev, "RSS support for IP fragments is %s\n", + hca_param.rss_ip_frags ? "on" : "off"); if (func_cap.extra_flags & MLX4_QUERY_FUNC_FLAGS_BF_RES_QP && dev->caps.bf_reg_size) diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index 0b16db015745..f30eeb730a86 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -50,6 +50,7 @@ #include <linux/mlx4/driver.h> #include <linux/mlx4/doorbell.h> #include <linux/mlx4/cmd.h> +#include "fw_qos.h" #define DRV_NAME "mlx4_core" #define PFX DRV_NAME ": " @@ -64,21 +65,6 @@ #define INIT_HCA_TPT_MW_ENABLE (1 << 7) -struct mlx4_set_port_prio2tc_context { - u8 prio2tc[4]; -}; - -struct mlx4_port_scheduler_tc_cfg_be { - __be16 pg; - __be16 bw_precentage; - __be16 max_bw_units; /* 3-100Mbps, 4-1Gbps, other values - reserved */ - __be16 max_bw_value; -}; - -struct mlx4_set_port_scheduler_context { - struct mlx4_port_scheduler_tc_cfg_be tc[MLX4_NUM_TC]; -}; - enum { MLX4_HCR_BASE = 0x80680, MLX4_HCR_SIZE = 0x0001c, @@ -512,6 +498,7 @@ struct mlx4_vport_state { u32 tx_rate; bool spoofchk; u32 link_state; + u8 qos_vport; }; struct mlx4_vf_admin_state { @@ -568,6 +555,11 @@ struct mlx4_slave_event_eq { struct mlx4_eqe event_eqe[SLAVE_EVENT_EQ_SIZE]; }; +struct mlx4_qos_manager { + int num_of_qos_vfs; + DECLARE_BITMAP(priority_bm, MLX4_NUM_UP); +}; + struct mlx4_master_qp0_state { int proxy_qp0_active; int qp0_active; @@ -592,6 +584,7 @@ struct mlx4_mfunc_master_ctx { struct mlx4_eqe cmd_eqe; struct mlx4_slave_event_eq slave_eq; struct mutex gen_eqe_mutex[MLX4_MFUNC_MAX]; + struct mlx4_qos_manager qos_ctl[MLX4_MAX_PORTS + 1]; }; struct mlx4_mfunc { @@ -644,6 +637,7 @@ struct mlx4_vf_immed_vlan_work { int orig_vlan_ix; u8 port; u8 qos; + u8 qos_vport; u16 vlan_id; u16 orig_vlan_id; }; @@ -769,9 +763,11 @@ enum { struct mlx4_set_port_general_context { - u8 reserved[3]; + u16 reserved1; + u8 v_ignore_fcs; u8 flags; - u16 reserved2; + u8 ignore_fcs; + u8 reserved2; __be16 mtu; u8 pptx; u8 pfctx; diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 67eeea244eff..9de30216b146 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -482,6 +482,7 @@ enum { MLX4_EN_FLAG_RX_CSUM_NON_TCP_UDP = (1 << 5), }; +#define PORT_BEACON_MAX_LIMIT (65535) #define MLX4_EN_MAC_HASH_SIZE (1 << BITS_PER_BYTE) #define MLX4_EN_MAC_HASH_IDX 5 diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c index b97f173ab062..c2b21313dba7 100644 --- a/drivers/net/ethernet/mellanox/mlx4/port.c +++ b/drivers/net/ethernet/mellanox/mlx4/port.c @@ -45,6 +45,14 @@ #define MLX4_VLAN_VALID (1u << 31) #define MLX4_VLAN_MASK 0xfff +#define MLX4_STATS_TRAFFIC_COUNTERS_MASK 0xfULL +#define MLX4_STATS_TRAFFIC_DROPS_MASK 0xc0ULL +#define MLX4_STATS_ERROR_COUNTERS_MASK 0x1ffc30ULL +#define MLX4_STATS_PORT_COUNTERS_MASK 0x1fe00000ULL + +#define MLX4_FLAG_V_IGNORE_FCS_MASK 0x2 +#define MLX4_IGNORE_FCS_MASK 0x1 + void mlx4_init_mac_table(struct mlx4_dev *dev, struct mlx4_mac_table *table) { int i; @@ -123,8 +131,9 @@ static int mlx4_set_port_mac_table(struct mlx4_dev *dev, u8 port, in_mod = MLX4_SET_PORT_MAC_TABLE << 8 | port; - err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT, - MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); + err = mlx4_cmd(dev, mailbox->dma, in_mod, MLX4_SET_PORT_ETH_OPCODE, + MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, + MLX4_CMD_NATIVE); mlx4_free_cmd_mailbox(dev, mailbox); return err; @@ -337,8 +346,9 @@ static int mlx4_set_port_vlan_table(struct mlx4_dev *dev, u8 port, memcpy(mailbox->buf, entries, MLX4_VLAN_TABLE_SIZE); in_mod = MLX4_SET_PORT_VLAN_TABLE << 8 | port; - err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT, - MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); + err = mlx4_cmd(dev, mailbox->dma, in_mod, MLX4_SET_PORT_ETH_OPCODE, + MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, + MLX4_CMD_NATIVE); mlx4_free_cmd_mailbox(dev, mailbox); @@ -625,9 +635,9 @@ static int mlx4_reset_roce_port_gids(struct mlx4_dev *dev, int slave, MLX4_ROCE_GID_ENTRY_SIZE); err = mlx4_cmd(dev, mailbox->dma, - ((u32)port) | (MLX4_SET_PORT_GID_TABLE << 8), 1, - MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, - MLX4_CMD_NATIVE); + ((u32)port) | (MLX4_SET_PORT_GID_TABLE << 8), + MLX4_SET_PORT_ETH_OPCODE, MLX4_CMD_SET_PORT, + MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); mutex_unlock(&(priv->port[port].gid_table.mutex)); return err; } @@ -833,6 +843,12 @@ static int mlx4_common_set_port(struct mlx4_dev *dev, int slave, u32 in_mod, MLX4_CMD_NATIVE); } + /* Slaves are not allowed to SET_PORT beacon (LED) blink */ + if (op_mod == MLX4_SET_PORT_BEACON_OPCODE) { + mlx4_warn(dev, "denying SET_PORT Beacon slave:%d\n", slave); + return -EPERM; + } + /* For IB, we only consider: * - The capability mask, which is set to the aggregate of all * slave function capabilities @@ -941,8 +957,9 @@ int mlx4_SET_PORT(struct mlx4_dev *dev, u8 port, int pkey_tbl_sz) (pkey_tbl_flag << MLX4_CHANGE_PORT_PKEY_TBL_SZ) | (dev->caps.port_ib_mtu[port] << MLX4_SET_PORT_MTU_CAP) | (vl_cap << MLX4_SET_PORT_VL_CAP)); - err = mlx4_cmd(dev, mailbox->dma, port, 0, MLX4_CMD_SET_PORT, - MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED); + err = mlx4_cmd(dev, mailbox->dma, port, + MLX4_SET_PORT_IB_OPCODE, MLX4_CMD_SET_PORT, + MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED); if (err != -ENOMEM) break; } @@ -971,8 +988,9 @@ int mlx4_SET_PORT_general(struct mlx4_dev *dev, u8 port, int mtu, context->pfcrx = pfcrx; in_mod = MLX4_SET_PORT_GENERAL << 8 | port; - err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT, - MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED); + err = mlx4_cmd(dev, mailbox->dma, in_mod, MLX4_SET_PORT_ETH_OPCODE, + MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, + MLX4_CMD_WRAPPED); mlx4_free_cmd_mailbox(dev, mailbox); return err; @@ -1008,84 +1026,40 @@ int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn, context->vlan_miss = MLX4_VLAN_MISS_IDX; in_mod = MLX4_SET_PORT_RQP_CALC << 8 | port; - err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT, - MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED); + err = mlx4_cmd(dev, mailbox->dma, in_mod, MLX4_SET_PORT_ETH_OPCODE, + MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, + MLX4_CMD_WRAPPED); mlx4_free_cmd_mailbox(dev, mailbox); return err; } EXPORT_SYMBOL(mlx4_SET_PORT_qpn_calc); -int mlx4_SET_PORT_PRIO2TC(struct mlx4_dev *dev, u8 port, u8 *prio2tc) +int mlx4_SET_PORT_fcs_check(struct mlx4_dev *dev, u8 port, u8 ignore_fcs_value) { struct mlx4_cmd_mailbox *mailbox; - struct mlx4_set_port_prio2tc_context *context; - int err; + struct mlx4_set_port_general_context *context; u32 in_mod; - int i; - - mailbox = mlx4_alloc_cmd_mailbox(dev); - if (IS_ERR(mailbox)) - return PTR_ERR(mailbox); - context = mailbox->buf; - for (i = 0; i < MLX4_NUM_UP; i += 2) - context->prio2tc[i >> 1] = prio2tc[i] << 4 | prio2tc[i + 1]; - - in_mod = MLX4_SET_PORT_PRIO2TC << 8 | port; - err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT, - MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); - - mlx4_free_cmd_mailbox(dev, mailbox); - return err; -} -EXPORT_SYMBOL(mlx4_SET_PORT_PRIO2TC); - -int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw, - u8 *pg, u16 *ratelimit) -{ - struct mlx4_cmd_mailbox *mailbox; - struct mlx4_set_port_scheduler_context *context; int err; - u32 in_mod; - int i; mailbox = mlx4_alloc_cmd_mailbox(dev); if (IS_ERR(mailbox)) return PTR_ERR(mailbox); context = mailbox->buf; + context->v_ignore_fcs |= MLX4_FLAG_V_IGNORE_FCS_MASK; + if (ignore_fcs_value) + context->ignore_fcs |= MLX4_IGNORE_FCS_MASK; + else + context->ignore_fcs &= ~MLX4_IGNORE_FCS_MASK; - for (i = 0; i < MLX4_NUM_TC; i++) { - struct mlx4_port_scheduler_tc_cfg_be *tc = &context->tc[i]; - u16 r; - - if (ratelimit && ratelimit[i]) { - if (ratelimit[i] <= MLX4_MAX_100M_UNITS_VAL) { - r = ratelimit[i]; - tc->max_bw_units = - htons(MLX4_RATELIMIT_100M_UNITS); - } else { - r = ratelimit[i]/10; - tc->max_bw_units = - htons(MLX4_RATELIMIT_1G_UNITS); - } - tc->max_bw_value = htons(r); - } else { - tc->max_bw_value = htons(MLX4_RATELIMIT_DEFAULT); - tc->max_bw_units = htons(MLX4_RATELIMIT_1G_UNITS); - } - - tc->pg = htons(pg[i]); - tc->bw_precentage = htons(tc_tx_bw[i]); - } - - in_mod = MLX4_SET_PORT_SCHEDULER << 8 | port; + in_mod = MLX4_SET_PORT_GENERAL << 8 | port; err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); mlx4_free_cmd_mailbox(dev, mailbox); return err; } -EXPORT_SYMBOL(mlx4_SET_PORT_SCHEDULER); +EXPORT_SYMBOL(mlx4_SET_PORT_fcs_check); enum { VXLAN_ENABLE_MODIFY = 1 << 7, @@ -1121,14 +1095,35 @@ int mlx4_SET_PORT_VXLAN(struct mlx4_dev *dev, u8 port, u8 steering, int enable) context->steering = steering; in_mod = MLX4_SET_PORT_VXLAN << 8 | port; - err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT, - MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); + err = mlx4_cmd(dev, mailbox->dma, in_mod, MLX4_SET_PORT_ETH_OPCODE, + MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, + MLX4_CMD_NATIVE); mlx4_free_cmd_mailbox(dev, mailbox); return err; } EXPORT_SYMBOL(mlx4_SET_PORT_VXLAN); +int mlx4_SET_PORT_BEACON(struct mlx4_dev *dev, u8 port, u16 time) +{ + int err; + struct mlx4_cmd_mailbox *mailbox; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + + *((__be32 *)mailbox->buf) = cpu_to_be32(time); + + err = mlx4_cmd(dev, mailbox->dma, port, MLX4_SET_PORT_BEACON_OPCODE, + MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, + MLX4_CMD_NATIVE); + + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} +EXPORT_SYMBOL(mlx4_SET_PORT_BEACON); + int mlx4_SET_MCAST_FLTR_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_vhcr *vhcr, struct mlx4_cmd_mailbox *inbox, diff --git a/drivers/net/ethernet/mellanox/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c index 69e4462e4ee4..b75214a80d0e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/qp.c +++ b/drivers/net/ethernet/mellanox/mlx4/qp.c @@ -447,6 +447,11 @@ int mlx4_update_qp(struct mlx4_dev *dev, u32 qpn, cmd->qp_context.rate_limit_params = cpu_to_be16((params->rate_unit << 14) | params->rate_val); } + if (attr & MLX4_UPDATE_QP_QOS_VPORT) { + qp_mask |= 1ULL << MLX4_UPD_QP_MASK_QOS_VPP; + cmd->qp_context.qos_vport = params->qos_vport; + } + cmd->primary_addr_path_mask = cpu_to_be64(pri_addr_path_mask); cmd->qp_mask = cpu_to_be64(qp_mask); diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index c258f8625aac..c7f28bf4b8e2 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -221,11 +221,6 @@ struct res_fs_rule { int qpn; }; -static int mlx4_is_eth(struct mlx4_dev *dev, int port) -{ - return dev->caps.port_mask[port] == MLX4_PORT_TYPE_IB ? 0 : 1; -} - static void *res_tracker_lookup(struct rb_root *root, u64 res_id) { struct rb_node *node = root->rb_node; @@ -770,6 +765,7 @@ static int update_vport_qp_param(struct mlx4_dev *dev, qpc->pri_path.feup |= MLX4_FEUP_FORCE_ETH_UP | MLX4_FVL_FORCE_ETH_VLAN; qpc->pri_path.sched_queue &= 0xC7; qpc->pri_path.sched_queue |= (vp_oper->state.default_qos) << 3; + qpc->qos_vport = vp_oper->state.qos_vport; } if (vp_oper->state.spoofchk) { qpc->pri_path.feup |= MLX4_FSM_FORCE_ETH_SRC_MAC; @@ -3099,6 +3095,12 @@ int mlx4_GEN_EQE(struct mlx4_dev *dev, int slave, struct mlx4_eqe *eqe) if (!priv->mfunc.master.slave_state) return -EINVAL; + /* check for slave valid, slave not PF, and slave active */ + if (slave < 0 || slave > dev->persist->num_vfs || + slave == dev->caps.function || + !priv->mfunc.master.slave_state[slave].active) + return 0; + event_eq = &priv->mfunc.master.slave_state[slave].event_eq[eqe->type]; /* Create the event only if the slave is registered */ @@ -4916,6 +4918,11 @@ void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work) qp->sched_queue & 0xC7; upd_context->qp_context.pri_path.sched_queue |= ((work->qos & 0x7) << 3); + upd_context->qp_mask |= + cpu_to_be64(1ULL << + MLX4_UPD_QP_MASK_QOS_VPP); + upd_context->qp_context.qos_vport = + work->qos_vport; } err = mlx4_cmd(dev, mailbox->dma, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/alloc.c b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c index 201ca6d76ce5..ac0f7bf4be95 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/alloc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -171,6 +171,9 @@ static int mlx5_alloc_db_from_pgdir(struct mlx5_db_pgdir *pgdir, db->db = pgdir->db_page + offset / sizeof(*pgdir->db_page); db->dma = pgdir->db_dma + offset; + db->db[0] = 0; + db->db[1] = 0; + return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index a2853057c779..e3273faf4568 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -125,7 +125,10 @@ static u8 alloc_token(struct mlx5_cmd *cmd) u8 token; spin_lock(&cmd->token_lock); - token = cmd->token++ % 255 + 1; + cmd->token++; + if (cmd->token == 0) + cmd->token++; + token = cmd->token; spin_unlock(&cmd->token_lock); return token; @@ -515,10 +518,11 @@ static void cmd_work_handler(struct work_struct *work) ent->ts1 = ktime_get_ns(); /* ring doorbell after the descriptor is valid */ + mlx5_core_dbg(dev, "writing 0x%x to command doorbell\n", 1 << ent->idx); wmb(); iowrite32be(1 << ent->idx, &dev->iseg->cmd_dbell); - mlx5_core_dbg(dev, "write 0x%x to command doorbell\n", 1 << ent->idx); mmiowb(); + /* if not in polling don't use ent after this point */ if (cmd->mode == CMD_MODE_POLLING) { poll_timeout(ent); /* make sure we read the descriptor after ownership is SW */ @@ -1236,7 +1240,8 @@ static int cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out, goto out_out; } - err = mlx5_copy_from_msg(out, outb, out_size); + if (!callback) + err = mlx5_copy_from_msg(out, outb, out_size); out_out: if (!callback) @@ -1319,6 +1324,45 @@ ex_err: return err; } +static int alloc_cmd_page(struct mlx5_core_dev *dev, struct mlx5_cmd *cmd) +{ + struct device *ddev = &dev->pdev->dev; + + cmd->cmd_alloc_buf = dma_zalloc_coherent(ddev, MLX5_ADAPTER_PAGE_SIZE, + &cmd->alloc_dma, GFP_KERNEL); + if (!cmd->cmd_alloc_buf) + return -ENOMEM; + + /* make sure it is aligned to 4K */ + if (!((uintptr_t)cmd->cmd_alloc_buf & (MLX5_ADAPTER_PAGE_SIZE - 1))) { + cmd->cmd_buf = cmd->cmd_alloc_buf; + cmd->dma = cmd->alloc_dma; + cmd->alloc_size = MLX5_ADAPTER_PAGE_SIZE; + return 0; + } + + dma_free_coherent(ddev, MLX5_ADAPTER_PAGE_SIZE, cmd->cmd_alloc_buf, + cmd->alloc_dma); + cmd->cmd_alloc_buf = dma_zalloc_coherent(ddev, + 2 * MLX5_ADAPTER_PAGE_SIZE - 1, + &cmd->alloc_dma, GFP_KERNEL); + if (!cmd->cmd_alloc_buf) + return -ENOMEM; + + cmd->cmd_buf = PTR_ALIGN(cmd->cmd_alloc_buf, MLX5_ADAPTER_PAGE_SIZE); + cmd->dma = ALIGN(cmd->alloc_dma, MLX5_ADAPTER_PAGE_SIZE); + cmd->alloc_size = 2 * MLX5_ADAPTER_PAGE_SIZE - 1; + return 0; +} + +static void free_cmd_page(struct mlx5_core_dev *dev, struct mlx5_cmd *cmd) +{ + struct device *ddev = &dev->pdev->dev; + + dma_free_coherent(ddev, cmd->alloc_size, cmd->cmd_alloc_buf, + cmd->alloc_dma); +} + int mlx5_cmd_init(struct mlx5_core_dev *dev) { int size = sizeof(struct mlx5_cmd_prot_block); @@ -1341,17 +1385,9 @@ int mlx5_cmd_init(struct mlx5_core_dev *dev) if (!cmd->pool) return -ENOMEM; - cmd->cmd_buf = (void *)__get_free_pages(GFP_ATOMIC, 0); - if (!cmd->cmd_buf) { - err = -ENOMEM; + err = alloc_cmd_page(dev, cmd); + if (err) goto err_free_pool; - } - cmd->dma = dma_map_single(&dev->pdev->dev, cmd->cmd_buf, PAGE_SIZE, - DMA_BIDIRECTIONAL); - if (dma_mapping_error(&dev->pdev->dev, cmd->dma)) { - err = -ENOMEM; - goto err_free; - } cmd_l = ioread32be(&dev->iseg->cmdq_addr_l_sz) & 0xff; cmd->log_sz = cmd_l >> 4 & 0xf; @@ -1360,13 +1396,13 @@ int mlx5_cmd_init(struct mlx5_core_dev *dev) dev_err(&dev->pdev->dev, "firmware reports too many outstanding commands %d\n", 1 << cmd->log_sz); err = -EINVAL; - goto err_map; + goto err_free_page; } if (cmd->log_sz + cmd->log_stride > MLX5_ADAPTER_PAGE_SHIFT) { dev_err(&dev->pdev->dev, "command queue size overflow\n"); err = -EINVAL; - goto err_map; + goto err_free_page; } cmd->checksum_disabled = 1; @@ -1378,7 +1414,7 @@ int mlx5_cmd_init(struct mlx5_core_dev *dev) dev_err(&dev->pdev->dev, "driver does not support command interface version. driver %d, firmware %d\n", CMD_IF_REV, cmd->cmdif_rev); err = -ENOTSUPP; - goto err_map; + goto err_free_page; } spin_lock_init(&cmd->alloc_lock); @@ -1394,7 +1430,7 @@ int mlx5_cmd_init(struct mlx5_core_dev *dev) if (cmd_l & 0xfff) { dev_err(&dev->pdev->dev, "invalid command queue address\n"); err = -ENOMEM; - goto err_map; + goto err_free_page; } iowrite32be(cmd_h, &dev->iseg->cmdq_addr_h); @@ -1410,7 +1446,7 @@ int mlx5_cmd_init(struct mlx5_core_dev *dev) err = create_msg_cache(dev); if (err) { dev_err(&dev->pdev->dev, "failed to create command cache\n"); - goto err_map; + goto err_free_page; } set_wqname(dev); @@ -1435,11 +1471,8 @@ err_wq: err_cache: destroy_msg_cache(dev); -err_map: - dma_unmap_single(&dev->pdev->dev, cmd->dma, PAGE_SIZE, - DMA_BIDIRECTIONAL); -err_free: - free_pages((unsigned long)cmd->cmd_buf, 0); +err_free_page: + free_cmd_page(dev, cmd); err_free_pool: pci_pool_destroy(cmd->pool); @@ -1455,9 +1488,7 @@ void mlx5_cmd_cleanup(struct mlx5_core_dev *dev) clean_debug_files(dev); destroy_workqueue(cmd->wq); destroy_msg_cache(dev); - dma_unmap_single(&dev->pdev->dev, cmd->dma, PAGE_SIZE, - DMA_BIDIRECTIONAL); - free_pages((unsigned long)cmd->cmd_buf, 0); + free_cmd_page(dev, cmd); pci_pool_destroy(cmd->pool); } EXPORT_SYMBOL(mlx5_cmd_cleanup); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cq.c b/drivers/net/ethernet/mellanox/mlx5/core/cq.c index 43c5f4809526..eb0cf81f5f45 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cq.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c b/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c index 4878025e231c..5210d92e6bc7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c index da82991239a8..dbf190d9b9ad 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw.c b/drivers/net/ethernet/mellanox/mlx5/core/fw.c index 06f9036acd83..4b4cda3bcc5f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fw.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/health.c b/drivers/net/ethernet/mellanox/mlx5/core/health.c index 3e6670c4a7cd..292d76f2a904 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/health.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/health.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mad.c b/drivers/net/ethernet/mellanox/mlx5/core/mad.c index fd80ecfa7195..ee1b0b965f34 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mad.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/mad.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 350c6297fe5d..28425e5ea91f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -48,11 +48,11 @@ #include "mlx5_core.h" #define DRIVER_NAME "mlx5_core" -#define DRIVER_VERSION "2.2-1" -#define DRIVER_RELDATE "Feb 2014" +#define DRIVER_VERSION "3.0" +#define DRIVER_RELDATE "January 2015" MODULE_AUTHOR("Eli Cohen <eli@mellanox.com>"); -MODULE_DESCRIPTION("Mellanox ConnectX-IB HCA core library"); +MODULE_DESCRIPTION("Mellanox Connect-IB, ConnectX-4 core driver"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_VERSION(DRIVER_VERSION); @@ -288,8 +288,6 @@ static void copy_rw_fields(void *to, struct mlx5_caps *from) MLX5_SET(cmd_hca_cap, to, log_max_ra_req_qp, from->gen.log_max_ra_req_qp); MLX5_SET(cmd_hca_cap, to, log_max_ra_res_qp, from->gen.log_max_ra_res_qp); MLX5_SET(cmd_hca_cap, to, pkey_table_size, from->gen.pkey_table_size); - MLX5_SET(cmd_hca_cap, to, log_max_ra_req_dc, from->gen.log_max_ra_req_dc); - MLX5_SET(cmd_hca_cap, to, log_max_ra_res_dc, from->gen.log_max_ra_res_dc); MLX5_SET(cmd_hca_cap, to, pkey_table_size, to_fw_pkey_sz(from->gen.pkey_table_size)); MLX5_SET(cmd_hca_cap, to, log_uar_page_sz, PAGE_SHIFT - 12); v64 = from->gen.flags & MLX5_CAP_BITS_RW_MASK; @@ -509,6 +507,87 @@ static int mlx5_core_disable_hca(struct mlx5_core_dev *dev) return 0; } +int mlx5_vector2eqn(struct mlx5_core_dev *dev, int vector, int *eqn, int *irqn) +{ + struct mlx5_eq_table *table = &dev->priv.eq_table; + struct mlx5_eq *eq, *n; + int err = -ENOENT; + + spin_lock(&table->lock); + list_for_each_entry_safe(eq, n, &table->comp_eqs_list, list) { + if (eq->index == vector) { + *eqn = eq->eqn; + *irqn = eq->irqn; + err = 0; + break; + } + } + spin_unlock(&table->lock); + + return err; +} +EXPORT_SYMBOL(mlx5_vector2eqn); + +static void free_comp_eqs(struct mlx5_core_dev *dev) +{ + struct mlx5_eq_table *table = &dev->priv.eq_table; + struct mlx5_eq *eq, *n; + + spin_lock(&table->lock); + list_for_each_entry_safe(eq, n, &table->comp_eqs_list, list) { + list_del(&eq->list); + spin_unlock(&table->lock); + if (mlx5_destroy_unmap_eq(dev, eq)) + mlx5_core_warn(dev, "failed to destroy EQ 0x%x\n", + eq->eqn); + kfree(eq); + spin_lock(&table->lock); + } + spin_unlock(&table->lock); +} + +static int alloc_comp_eqs(struct mlx5_core_dev *dev) +{ + struct mlx5_eq_table *table = &dev->priv.eq_table; + char name[MLX5_MAX_EQ_NAME]; + struct mlx5_eq *eq; + int ncomp_vec; + int nent; + int err; + int i; + + INIT_LIST_HEAD(&table->comp_eqs_list); + ncomp_vec = table->num_comp_vectors; + nent = MLX5_COMP_EQ_SIZE; + for (i = 0; i < ncomp_vec; i++) { + eq = kzalloc(sizeof(*eq), GFP_KERNEL); + if (!eq) { + err = -ENOMEM; + goto clean; + } + + snprintf(name, MLX5_MAX_EQ_NAME, "mlx5_comp%d", i); + err = mlx5_create_map_eq(dev, eq, + i + MLX5_EQ_VEC_COMP_BASE, nent, 0, + name, &dev->priv.uuari.uars[0]); + if (err) { + kfree(eq); + goto clean; + } + mlx5_core_dbg(dev, "allocated completion EQN %d\n", eq->eqn); + eq->index = i; + spin_lock(&table->lock); + list_add_tail(&eq->list, &table->comp_eqs_list); + spin_unlock(&table->lock); + } + + return 0; + +clean: + free_comp_eqs(dev); + return err; +} + static int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev) { struct mlx5_priv *priv = &dev->priv; @@ -645,6 +724,12 @@ static int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev) goto err_free_uar; } + err = alloc_comp_eqs(dev); + if (err) { + dev_err(&pdev->dev, "Failed to alloc completion EQs\n"); + goto err_stop_eqs; + } + MLX5_INIT_DOORBELL_LOCK(&priv->cq_uar_lock); mlx5_init_cq_table(dev); @@ -654,6 +739,9 @@ static int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev) return 0; +err_stop_eqs: + mlx5_stop_eqs(dev); + err_free_uar: mlx5_free_uuars(dev, &priv->uuari); @@ -705,6 +793,7 @@ static void mlx5_dev_cleanup(struct mlx5_core_dev *dev) mlx5_cleanup_srq_table(dev); mlx5_cleanup_qp_table(dev); mlx5_cleanup_cq_table(dev); + free_comp_eqs(dev); mlx5_stop_eqs(dev); mlx5_free_uuars(dev, &priv->uuari); mlx5_eq_cleanup(dev); @@ -819,6 +908,28 @@ void mlx5_unregister_interface(struct mlx5_interface *intf) } EXPORT_SYMBOL(mlx5_unregister_interface); +void *mlx5_get_protocol_dev(struct mlx5_core_dev *mdev, int protocol) +{ + struct mlx5_priv *priv = &mdev->priv; + struct mlx5_device_context *dev_ctx; + unsigned long flags; + void *result = NULL; + + spin_lock_irqsave(&priv->ctx_lock, flags); + + list_for_each_entry(dev_ctx, &mdev->priv.ctx_list, list) + if ((dev_ctx->intf->protocol == protocol) && + dev_ctx->intf->get_dev) { + result = dev_ctx->intf->get_dev(dev_ctx->context); + break; + } + + spin_unlock_irqrestore(&priv->ctx_lock, flags); + + return result; +} +EXPORT_SYMBOL(mlx5_get_protocol_dev); + static void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event, unsigned long param) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mcg.c b/drivers/net/ethernet/mellanox/mlx5/core/mcg.c index 44837640bd7c..d79fd85d1dd5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mcg.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/mcg.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index f0c9f9a7a361..a051b906afdf 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mr.c b/drivers/net/ethernet/mellanox/mlx5/core/mr.c index 184c3615f479..1adb300dd850 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mr.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/mr.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -141,7 +141,7 @@ EXPORT_SYMBOL(mlx5_core_destroy_mkey); int mlx5_core_query_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr, struct mlx5_query_mkey_mbox_out *out, int outlen) { - struct mlx5_destroy_mkey_mbox_in in; + struct mlx5_query_mkey_mbox_in in; int err; memset(&in, 0, sizeof(in)); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c index 4fdaae9b54d9..df2238372ea7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -243,8 +243,9 @@ static int alloc_system_page(struct mlx5_core_dev *dev, u16 func_id) struct page *page; u64 addr; int err; + int nid = dev_to_node(&dev->pdev->dev); - page = alloc_page(GFP_HIGHUSER); + page = alloc_pages_node(nid, GFP_HIGHUSER, 0); if (!page) { mlx5_core_warn(dev, "failed to allocate page\n"); return -ENOMEM; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pd.c b/drivers/net/ethernet/mellanox/mlx5/core/pd.c index 790da5c4ca4f..f2d3aee909e8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/pd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/pd.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c index 72c2d002c3b8..49e90f2612d8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/qp.c b/drivers/net/ethernet/mellanox/mlx5/core/qp.c index 575d853dbe05..dc7dbf7e9d98 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/qp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/qp.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/srq.c b/drivers/net/ethernet/mellanox/mlx5/core/srq.c index 38bce93f8314..f9d25dcd03c1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/srq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/srq.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/mellanox/mlx5/core/uar.c b/drivers/net/ethernet/mellanox/mlx5/core/uar.c index 06801d6f595e..5a89bb1d678a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/uar.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/uar.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. + * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/ethernet/neterion/vxge/vxge-ethtool.c b/drivers/net/ethernet/neterion/vxge/vxge-ethtool.c index b07d552a27d4..be916eb2f2e7 100644 --- a/drivers/net/ethernet/neterion/vxge/vxge-ethtool.c +++ b/drivers/net/ethernet/neterion/vxge/vxge-ethtool.c @@ -18,6 +18,25 @@ #include "vxge-ethtool.h" +static const char ethtool_driver_stats_keys[][ETH_GSTRING_LEN] = { + {"\n DRIVER STATISTICS"}, + {"vpaths_opened"}, + {"vpath_open_fail_cnt"}, + {"link_up_cnt"}, + {"link_down_cnt"}, + {"tx_frms"}, + {"tx_errors"}, + {"tx_bytes"}, + {"txd_not_free"}, + {"txd_out_of_desc"}, + {"rx_frms"}, + {"rx_errors"}, + {"rx_bytes"}, + {"rx_mcast"}, + {"pci_map_fail_cnt"}, + {"skb_alloc_fail_cnt"} +}; + /** * vxge_ethtool_sset - Sets different link parameters. * @dev: device pointer. diff --git a/drivers/net/ethernet/neterion/vxge/vxge-ethtool.h b/drivers/net/ethernet/neterion/vxge/vxge-ethtool.h index 6cf3044d7f43..065a2c0429a4 100644 --- a/drivers/net/ethernet/neterion/vxge/vxge-ethtool.h +++ b/drivers/net/ethernet/neterion/vxge/vxge-ethtool.h @@ -19,25 +19,6 @@ /* Ethtool related variables and Macros. */ static int vxge_ethtool_get_sset_count(struct net_device *dev, int sset); -static char ethtool_driver_stats_keys[][ETH_GSTRING_LEN] = { - {"\n DRIVER STATISTICS"}, - {"vpaths_opened"}, - {"vpath_open_fail_cnt"}, - {"link_up_cnt"}, - {"link_down_cnt"}, - {"tx_frms"}, - {"tx_errors"}, - {"tx_bytes"}, - {"txd_not_free"}, - {"txd_out_of_desc"}, - {"rx_frms"}, - {"rx_errors"}, - {"rx_bytes"}, - {"rx_mcast"}, - {"pci_map_fail_cnt"}, - {"skb_alloc_fail_cnt"} -}; - #define VXGE_TITLE_LEN 5 #define VXGE_HW_VPATH_STATS_LEN 27 #define VXGE_HW_AGGR_STATS_LEN 13 diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c index c9558e6d57ad..a87b177bd723 100644 --- a/drivers/net/ethernet/rocker/rocker.c +++ b/drivers/net/ethernet/rocker/rocker.c @@ -4937,10 +4937,16 @@ static int rocker_port_master_changed(struct net_device *dev) struct net_device *master = netdev_master_upper_dev_get(dev); int err = 0; + /* There are currently three cases handled here: + * 1. Joining a bridge + * 2. Leaving a previously joined bridge + * 3. Other, e.g. being added to or removed from a bond or openvswitch, + * in which case nothing is done + */ if (master && master->rtnl_link_ops && !strcmp(master->rtnl_link_ops->kind, "bridge")) err = rocker_port_bridge_join(rocker_port, master); - else + else if (rocker_port_is_bridged(rocker_port)) err = rocker_port_bridge_leave(rocker_port); return err; diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 309adee6e791..f0b8b3e0ed7c 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -130,7 +130,6 @@ struct hv_netvsc_packet { u32 status; bool part_of_skb; - struct hv_device *device; bool is_data_pkt; bool xmit_more; /* from skb */ u16 vlan_tci; @@ -189,6 +188,7 @@ int netvsc_send(struct hv_device *device, struct hv_netvsc_packet *packet); void netvsc_linkstatus_callback(struct hv_device *device_obj, struct rndis_message *resp); +void netvsc_xmit_completion(void *context); int netvsc_recv_callback(struct hv_device *device_obj, struct hv_netvsc_packet *packet, struct ndis_tcp_ip_checksum_info *csum_info); @@ -959,6 +959,10 @@ struct ndis_tcp_lso_info { #define NDIS_HASH_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \ sizeof(u32)) +/* Total size of all PPI data */ +#define NDIS_ALL_PPI_SIZE (NDIS_VLAN_PPI_SIZE + NDIS_CSUM_PPI_SIZE + \ + NDIS_LSO_PPI_SIZE + NDIS_HASH_PPI_SIZE) + /* Format of Information buffer passed in a SetRequest for the OID */ /* OID_GEN_RNDIS_CONFIG_PARAMETER. */ struct rndis_config_parameter_info { @@ -1171,6 +1175,8 @@ struct rndis_message { #define RNDIS_HEADER_SIZE (sizeof(struct rndis_message) - \ sizeof(union rndis_message_container)) +#define RNDIS_AND_PPI_SIZE (sizeof(struct rndis_message) + NDIS_ALL_PPI_SIZE) + #define NDIS_PACKET_TYPE_DIRECTED 0x00000001 #define NDIS_PACKET_TYPE_MULTICAST 0x00000002 #define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004 diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index f69923695b5b..4d4d497d5762 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -878,7 +878,9 @@ int netvsc_send(struct hv_device *device, packet->send_buf_index = section_index; packet->total_data_buflen += msd_len; - kfree(msdp->pkt); + if (msdp->pkt) + netvsc_xmit_completion(msdp->pkt); + if (packet->xmit_more) { msdp->pkt = packet; msdp->count++; @@ -902,7 +904,7 @@ int netvsc_send(struct hv_device *device, if (m_ret != 0) { netvsc_free_send_slot(net_device, msd_send->send_buf_index); - kfree(msd_send); + netvsc_xmit_completion(msd_send); } } @@ -1011,7 +1013,6 @@ static void netvsc_receive(struct netvsc_device *net_device, } count = vmxferpage_packet->range_cnt; - netvsc_packet->device = device; netvsc_packet->channel = channel; /* Each range represents 1 RNDIS pkt that contains 1 ethernet frame */ diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index f9db6bc513e9..e5fa094e6fe2 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -229,7 +229,7 @@ static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb, return q_idx; } -static void netvsc_xmit_completion(void *context) +void netvsc_xmit_completion(void *context) { struct hv_netvsc_packet *packet = (struct hv_netvsc_packet *)context; struct sk_buff *skb = (struct sk_buff *) @@ -401,10 +401,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) return NETDEV_TX_OK; } - pkt_sz = sizeof(struct hv_netvsc_packet) + - sizeof(struct rndis_message) + - NDIS_VLAN_PPI_SIZE + NDIS_CSUM_PPI_SIZE + - NDIS_LSO_PPI_SIZE + NDIS_HASH_PPI_SIZE; + pkt_sz = sizeof(struct hv_netvsc_packet) + RNDIS_AND_PPI_SIZE; if (head_room < pkt_sz) { packet = kmalloc(pkt_sz, GFP_ATOMIC); @@ -436,11 +433,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) packet->rndis_msg = (struct rndis_message *)((unsigned long)packet + sizeof(struct hv_netvsc_packet)); - memset(packet->rndis_msg, 0, sizeof(struct rndis_message) + - NDIS_VLAN_PPI_SIZE + - NDIS_CSUM_PPI_SIZE + - NDIS_LSO_PPI_SIZE + - NDIS_HASH_PPI_SIZE); + memset(packet->rndis_msg, 0, RNDIS_AND_PPI_SIZE); /* Set the completion routine */ packet->send_completion = netvsc_xmit_completion; @@ -872,9 +865,7 @@ static int netvsc_probe(struct hv_device *dev, return -ENOMEM; max_needed_headroom = sizeof(struct hv_netvsc_packet) + - sizeof(struct rndis_message) + - NDIS_VLAN_PPI_SIZE + NDIS_CSUM_PPI_SIZE + - NDIS_LSO_PPI_SIZE + NDIS_HASH_PPI_SIZE; + RNDIS_AND_PPI_SIZE; netif_carrier_off(net); diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index a1604376aee1..0d92efefd796 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -47,8 +47,6 @@ struct rndis_request { /* Simplify allocation by having a netvsc packet inline */ struct hv_netvsc_packet pkt; - /* Set 2 pages for rndis requests crossing page boundary */ - struct hv_page_buffer buf[2]; struct rndis_message request_msg; /* diff --git a/drivers/net/ipvlan/ipvlan.h b/drivers/net/ipvlan/ipvlan.h index 924ea98bd531..54549a6223dd 100644 --- a/drivers/net/ipvlan/ipvlan.h +++ b/drivers/net/ipvlan/ipvlan.h @@ -114,7 +114,9 @@ unsigned int ipvlan_mac_hash(const unsigned char *addr); rx_handler_result_t ipvlan_handle_frame(struct sk_buff **pskb); int ipvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev); void ipvlan_ht_addr_add(struct ipvl_dev *ipvlan, struct ipvl_addr *addr); -bool ipvlan_addr_busy(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6); +struct ipvl_addr *ipvlan_find_addr(const struct ipvl_dev *ipvlan, + const void *iaddr, bool is_v6); +bool ipvlan_addr_busy(struct ipvl_port *port, void *iaddr, bool is_v6); struct ipvl_addr *ipvlan_ht_addr_lookup(const struct ipvl_port *port, const void *iaddr, bool is_v6); void ipvlan_ht_addr_del(struct ipvl_addr *addr, bool sync); diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c index 2a175006028b..c30b5c300c05 100644 --- a/drivers/net/ipvlan/ipvlan_core.c +++ b/drivers/net/ipvlan/ipvlan_core.c @@ -81,19 +81,20 @@ void ipvlan_ht_addr_add(struct ipvl_dev *ipvlan, struct ipvl_addr *addr) hash = (addr->atype == IPVL_IPV6) ? ipvlan_get_v6_hash(&addr->ip6addr) : ipvlan_get_v4_hash(&addr->ip4addr); - hlist_add_head_rcu(&addr->hlnode, &port->hlhead[hash]); + if (hlist_unhashed(&addr->hlnode)) + hlist_add_head_rcu(&addr->hlnode, &port->hlhead[hash]); } void ipvlan_ht_addr_del(struct ipvl_addr *addr, bool sync) { - hlist_del_rcu(&addr->hlnode); + hlist_del_init_rcu(&addr->hlnode); if (sync) synchronize_rcu(); } -bool ipvlan_addr_busy(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6) +struct ipvl_addr *ipvlan_find_addr(const struct ipvl_dev *ipvlan, + const void *iaddr, bool is_v6) { - struct ipvl_port *port = ipvlan->port; struct ipvl_addr *addr; list_for_each_entry(addr, &ipvlan->addrs, anode) { @@ -101,12 +102,21 @@ bool ipvlan_addr_busy(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6) ipv6_addr_equal(&addr->ip6addr, iaddr)) || (!is_v6 && addr->atype == IPVL_IPV4 && addr->ip4addr.s_addr == ((struct in_addr *)iaddr)->s_addr)) - return true; + return addr; } + return NULL; +} + +bool ipvlan_addr_busy(struct ipvl_port *port, void *iaddr, bool is_v6) +{ + struct ipvl_dev *ipvlan; - if (ipvlan_ht_addr_lookup(port, iaddr, is_v6)) - return true; + ASSERT_RTNL(); + list_for_each_entry(ipvlan, &port->ipvlans, pnode) { + if (ipvlan_find_addr(ipvlan, iaddr, is_v6)) + return true; + } return false; } @@ -192,7 +202,8 @@ static void ipvlan_multicast_frame(struct ipvl_port *port, struct sk_buff *skb, if (skb->protocol == htons(ETH_P_PAUSE)) return; - list_for_each_entry(ipvlan, &port->ipvlans, pnode) { + rcu_read_lock(); + list_for_each_entry_rcu(ipvlan, &port->ipvlans, pnode) { if (local && (ipvlan == in_dev)) continue; @@ -219,6 +230,7 @@ static void ipvlan_multicast_frame(struct ipvl_port *port, struct sk_buff *skb, mcast_acct: ipvlan_count_rx(ipvlan, len, ret == NET_RX_SUCCESS, true); } + rcu_read_unlock(); /* Locally generated? ...Forward a copy to the main-device as * well. On the RX side we'll ignore it (wont give it to any @@ -330,7 +342,7 @@ static int ipvlan_process_v4_outbound(struct sk_buff *skb) struct rtable *rt; int err, ret = NET_XMIT_DROP; struct flowi4 fl4 = { - .flowi4_oif = dev->iflink, + .flowi4_oif = dev_get_iflink(dev), .flowi4_tos = RT_TOS(ip4h->tos), .flowi4_flags = FLOWI_FLAG_ANYSRC, .daddr = ip4h->daddr, diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index 2950c3780230..77b92a0fe557 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -114,7 +114,6 @@ static int ipvlan_init(struct net_device *dev) dev->features = phy_dev->features & IPVLAN_FEATURES; dev->features |= NETIF_F_LLTX; dev->gso_max_size = phy_dev->gso_max_size; - dev->iflink = phy_dev->ifindex; dev->hard_header_len = phy_dev->hard_header_len; ipvlan_set_lockdep_class(dev); @@ -305,6 +304,13 @@ static int ipvlan_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, return 0; } +static int ipvlan_get_iflink(const struct net_device *dev) +{ + struct ipvl_dev *ipvlan = netdev_priv(dev); + + return ipvlan->phy_dev->ifindex; +} + static const struct net_device_ops ipvlan_netdev_ops = { .ndo_init = ipvlan_init, .ndo_uninit = ipvlan_uninit, @@ -317,6 +323,7 @@ static const struct net_device_ops ipvlan_netdev_ops = { .ndo_get_stats64 = ipvlan_get_stats64, .ndo_vlan_rx_add_vid = ipvlan_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = ipvlan_vlan_rx_kill_vid, + .ndo_get_iflink = ipvlan_get_iflink, }; static int ipvlan_hard_header(struct sk_buff *skb, struct net_device *dev, @@ -504,7 +511,7 @@ static void ipvlan_link_delete(struct net_device *dev, struct list_head *head) if (ipvlan->ipv6cnt > 0 || ipvlan->ipv4cnt > 0) { list_for_each_entry_safe(addr, next, &ipvlan->addrs, anode) { ipvlan_ht_addr_del(addr, !dev->dismantle); - list_del_rcu(&addr->anode); + list_del(&addr->anode); } } list_del_rcu(&ipvlan->pnode); @@ -606,7 +613,7 @@ static int ipvlan_add_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr) { struct ipvl_addr *addr; - if (ipvlan_addr_busy(ipvlan, ip6_addr, true)) { + if (ipvlan_addr_busy(ipvlan->port, ip6_addr, true)) { netif_err(ipvlan, ifup, ipvlan->dev, "Failed to add IPv6=%pI6c addr for %s intf\n", ip6_addr, ipvlan->dev->name); @@ -619,9 +626,13 @@ static int ipvlan_add_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr) addr->master = ipvlan; memcpy(&addr->ip6addr, ip6_addr, sizeof(struct in6_addr)); addr->atype = IPVL_IPV6; - list_add_tail_rcu(&addr->anode, &ipvlan->addrs); + list_add_tail(&addr->anode, &ipvlan->addrs); ipvlan->ipv6cnt++; - ipvlan_ht_addr_add(ipvlan, addr); + /* If the interface is not up, the address will be added to the hash + * list by ipvlan_open. + */ + if (netif_running(ipvlan->dev)) + ipvlan_ht_addr_add(ipvlan, addr); return 0; } @@ -630,12 +641,12 @@ static void ipvlan_del_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr) { struct ipvl_addr *addr; - addr = ipvlan_ht_addr_lookup(ipvlan->port, ip6_addr, true); + addr = ipvlan_find_addr(ipvlan, ip6_addr, true); if (!addr) return; ipvlan_ht_addr_del(addr, true); - list_del_rcu(&addr->anode); + list_del(&addr->anode); ipvlan->ipv6cnt--; WARN_ON(ipvlan->ipv6cnt < 0); kfree_rcu(addr, rcu); @@ -674,7 +685,7 @@ static int ipvlan_add_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr) { struct ipvl_addr *addr; - if (ipvlan_addr_busy(ipvlan, ip4_addr, false)) { + if (ipvlan_addr_busy(ipvlan->port, ip4_addr, false)) { netif_err(ipvlan, ifup, ipvlan->dev, "Failed to add IPv4=%pI4 on %s intf.\n", ip4_addr, ipvlan->dev->name); @@ -687,9 +698,13 @@ static int ipvlan_add_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr) addr->master = ipvlan; memcpy(&addr->ip4addr, ip4_addr, sizeof(struct in_addr)); addr->atype = IPVL_IPV4; - list_add_tail_rcu(&addr->anode, &ipvlan->addrs); + list_add_tail(&addr->anode, &ipvlan->addrs); ipvlan->ipv4cnt++; - ipvlan_ht_addr_add(ipvlan, addr); + /* If the interface is not up, the address will be added to the hash + * list by ipvlan_open. + */ + if (netif_running(ipvlan->dev)) + ipvlan_ht_addr_add(ipvlan, addr); ipvlan_set_broadcast_mac_filter(ipvlan, true); return 0; @@ -699,12 +714,12 @@ static void ipvlan_del_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr) { struct ipvl_addr *addr; - addr = ipvlan_ht_addr_lookup(ipvlan->port, ip4_addr, false); + addr = ipvlan_find_addr(ipvlan, ip4_addr, false); if (!addr) return; ipvlan_ht_addr_del(addr, true); - list_del_rcu(&addr->anode); + list_del(&addr->anode); ipvlan->ipv4cnt--; WARN_ON(ipvlan->ipv4cnt < 0); if (!ipvlan->ipv4cnt) diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index b5e3320ca506..b227a13f6473 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -786,7 +786,6 @@ static int macvlan_init(struct net_device *dev) dev->hw_features |= NETIF_F_LRO; dev->vlan_features = lowerdev->vlan_features & MACVLAN_FEATURES; dev->gso_max_size = lowerdev->gso_max_size; - dev->iflink = lowerdev->ifindex; dev->hard_header_len = lowerdev->hard_header_len; macvlan_set_lockdep_class(dev); @@ -995,6 +994,13 @@ static void macvlan_dev_netpoll_cleanup(struct net_device *dev) } #endif /* CONFIG_NET_POLL_CONTROLLER */ +static int macvlan_dev_get_iflink(const struct net_device *dev) +{ + struct macvlan_dev *vlan = netdev_priv(dev); + + return vlan->lowerdev->ifindex; +} + static const struct ethtool_ops macvlan_ethtool_ops = { .get_link = ethtool_op_get_link, .get_settings = macvlan_ethtool_get_settings, @@ -1025,6 +1031,7 @@ static const struct net_device_ops macvlan_netdev_ops = { .ndo_netpoll_setup = macvlan_dev_netpoll_setup, .ndo_netpoll_cleanup = macvlan_dev_netpoll_cleanup, #endif + .ndo_get_iflink = macvlan_dev_get_iflink, }; void macvlan_common_setup(struct net_device *dev) diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index f80e19ac6704..fabf11d32d27 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -192,16 +192,17 @@ static int at803x_probe(struct phy_device *phydev) { struct device *dev = &phydev->dev; struct at803x_priv *priv; + struct gpio_desc *gpiod_reset; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - priv->gpiod_reset = devm_gpiod_get(dev, "reset"); - if (IS_ERR(priv->gpiod_reset)) - priv->gpiod_reset = NULL; - else - gpiod_direction_output(priv->gpiod_reset, 1); + gpiod_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(gpiod_reset)) + return PTR_ERR(gpiod_reset); + + priv->gpiod_reset = gpiod_reset; phydev->priv = priv; diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c index a08a3c78ba97..1960b46add65 100644 --- a/drivers/net/phy/fixed_phy.c +++ b/drivers/net/phy/fixed_phy.c @@ -183,6 +183,35 @@ int fixed_phy_set_link_update(struct phy_device *phydev, } EXPORT_SYMBOL_GPL(fixed_phy_set_link_update); +int fixed_phy_update_state(struct phy_device *phydev, + const struct fixed_phy_status *status, + const struct fixed_phy_status *changed) +{ + struct fixed_mdio_bus *fmb = &platform_fmb; + struct fixed_phy *fp; + + if (!phydev || !phydev->bus) + return -EINVAL; + + list_for_each_entry(fp, &fmb->phys, node) { + if (fp->addr == phydev->addr) { +#define _UPD(x) if (changed->x) \ + fp->status.x = status->x + _UPD(link); + _UPD(speed); + _UPD(duplex); + _UPD(pause); + _UPD(asym_pause); +#undef _UPD + fixed_phy_update_regs(fp); + return 0; + } + } + + return -ENOENT; +} +EXPORT_SYMBOL(fixed_phy_update_state); + int fixed_phy_add(unsigned int irq, int phy_addr, struct fixed_phy_status *status) { diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c index 724a9b50df7a..75d6f26729a3 100644 --- a/drivers/net/usb/asix_common.c +++ b/drivers/net/usb/asix_common.c @@ -189,7 +189,7 @@ struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, skb_put(skb, sizeof(padbytes)); } - usbnet_set_skb_tx_stats(skb, 1); + usbnet_set_skb_tx_stats(skb, 1, 0); return skb; } diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index 9311a08565be..4545e78840b0 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -522,6 +522,7 @@ static const struct driver_info wwan_info = { #define DELL_VENDOR_ID 0x413C #define REALTEK_VENDOR_ID 0x0bda #define SAMSUNG_VENDOR_ID 0x04e8 +#define LENOVO_VENDOR_ID 0x17ef static const struct usb_device_id products[] = { /* BLACKLIST !! @@ -702,6 +703,13 @@ static const struct usb_device_id products[] = { .driver_info = 0, }, +/* Lenovo Thinkpad USB 3.0 Ethernet Adapters (based on Realtek RTL8153) */ +{ + USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x7205, USB_CLASS_COMM, + USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), + .driver_info = 0, +}, + /* WHITELIST!!! * * CDC Ether uses two interfaces, not necessarily consecutive. diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 70cbea551139..c3e4da9e79ca 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -1177,13 +1177,12 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) ctx->tx_overhead += skb_out->len - ctx->tx_curr_frame_payload; ctx->tx_ntbs++; - /* usbnet has already counted all the framing overhead. + /* usbnet will count all the framing overhead by default. * Adjust the stats so that the tx_bytes counter show real * payload data instead. */ - dev->net->stats.tx_bytes -= skb_out->len - ctx->tx_curr_frame_payload; - - usbnet_set_skb_tx_stats(skb_out, n); + usbnet_set_skb_tx_stats(skb_out, n, + ctx->tx_curr_frame_payload - skb_out->len); return skb_out; diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 5065538dd03b..ac4d03b328b1 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -493,6 +493,7 @@ enum rtl8152_flags { /* Define these values to match your device */ #define VENDOR_ID_REALTEK 0x0bda #define VENDOR_ID_SAMSUNG 0x04e8 +#define VENDOR_ID_LENOVO 0x17ef #define MCU_TYPE_PLA 0x0100 #define MCU_TYPE_USB 0x0000 @@ -4114,6 +4115,7 @@ static struct usb_device_id rtl8152_table[] = { {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8152)}, {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8153)}, {REALTEK_USB_DEVICE(VENDOR_ID_SAMSUNG, 0xa101)}, + {REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x7205)}, {} }; diff --git a/drivers/net/usb/sr9800.c b/drivers/net/usb/sr9800.c index 7650cdc8fe6b..953de13267df 100644 --- a/drivers/net/usb/sr9800.c +++ b/drivers/net/usb/sr9800.c @@ -144,7 +144,7 @@ static struct sk_buff *sr_tx_fixup(struct usbnet *dev, struct sk_buff *skb, skb_put(skb, sizeof(padbytes)); } - usbnet_set_skb_tx_stats(skb, 1); + usbnet_set_skb_tx_stats(skb, 1, 0); return skb; } diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 0f3ff285f6a1..777757ae1973 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -1346,9 +1346,19 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb, } else urb->transfer_flags |= URB_ZERO_PACKET; } - entry->length = urb->transfer_buffer_length = length; - if (!(info->flags & FLAG_MULTI_PACKET)) - usbnet_set_skb_tx_stats(skb, 1); + urb->transfer_buffer_length = length; + + if (info->flags & FLAG_MULTI_PACKET) { + /* Driver has set number of packets and a length delta. + * Calculate the complete length and ensure that it's + * positive. + */ + entry->length += length; + if (WARN_ON_ONCE(entry->length <= 0)) + entry->length = length; + } else { + usbnet_set_skb_tx_stats(skb, 1, length); + } spin_lock_irqsave(&dev->txq.lock, flags); retval = usb_autopm_get_interface_async(dev->intf); diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 4cca36ebc4fb..c8186ffda1a3 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -263,6 +263,20 @@ static void veth_poll_controller(struct net_device *dev) } #endif /* CONFIG_NET_POLL_CONTROLLER */ +static int veth_get_iflink(const struct net_device *dev) +{ + struct veth_priv *priv = netdev_priv(dev); + struct net_device *peer; + int iflink; + + rcu_read_lock(); + peer = rcu_dereference(priv->peer); + iflink = peer ? peer->ifindex : 0; + rcu_read_unlock(); + + return iflink; +} + static const struct net_device_ops veth_netdev_ops = { .ndo_init = veth_dev_init, .ndo_open = veth_open, @@ -275,6 +289,7 @@ static const struct net_device_ops veth_netdev_ops = { #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = veth_poll_controller, #endif + .ndo_get_iflink = veth_get_iflink, }; #define VETH_FEATURES (NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_ALL_TSO | \ diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index a829930dac15..63c7810e1545 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -939,11 +939,15 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) skb_orphan(skb); nf_reset(skb); - /* It is better to stop queue if running out of space - * instead of forcing queuing layer to requeue the skb - * by returning TX_BUSY (and cause a BUG message). - * Since most packets only take 1 or 2 ring slots - * this means 16 slots are typically wasted. + /* If running out of space, stop queue to avoid getting packets that we + * are then unable to transmit. + * An alternative would be to force queuing layer to requeue the skb by + * returning NETDEV_TX_BUSY. However, NETDEV_TX_BUSY should not be + * returned in a normal path of operation: it means that driver is not + * maintaining the TX queue stop/start state properly, and causes + * the stack to do a non-trivial amount of useless work. + * Since most packets only take 1 or 2 ring slots, stopping the queue + * early means 16 slots are typically wasted. */ if (sq->vq->num_free < 2+MAX_SKB_FRAGS) { netif_stop_subqueue(dev, qnum); diff --git a/drivers/net/vmxnet3/vmxnet3_ethtool.c b/drivers/net/vmxnet3/vmxnet3_ethtool.c index 4c8a944d58b4..c1d0e7a9da04 100644 --- a/drivers/net/vmxnet3/vmxnet3_ethtool.c +++ b/drivers/net/vmxnet3/vmxnet3_ethtool.c @@ -104,7 +104,7 @@ vmxnet3_rq_driver_stats[] = { rx_buf_alloc_failure) }, }; -/* gloabl stats maintained by the driver */ +/* global stats maintained by the driver */ static const struct vmxnet3_stat_desc vmxnet3_global_stats[] = { /* description, offset */ @@ -272,7 +272,7 @@ int vmxnet3_set_features(struct net_device *netdev, netdev_features_t features) adapter->shared->devRead.misc.uptFeatures &= ~UPT1_F_RXCSUM; - /* update harware LRO capability accordingly */ + /* update hardware LRO capability accordingly */ if (features & NETIF_F_LRO) adapter->shared->devRead.misc.uptFeatures |= UPT1_F_LRO; diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index a8d345054d23..51baac725a48 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -989,7 +989,7 @@ out: /* Watch incoming packets to learn mapping between Ethernet address * and Tunnel endpoint. - * Return true if packet is bogus and should be droppped. + * Return true if packet is bogus and should be dropped. */ static bool vxlan_snoop(struct net_device *dev, union vxlan_addr *src_ip, const u8 *src_mac) @@ -1085,7 +1085,7 @@ void vxlan_sock_release(struct vxlan_sock *vs) EXPORT_SYMBOL_GPL(vxlan_sock_release); /* Update multicast group membership when first VNI on - * multicast asddress is brought up + * multicast address is brought up */ static int vxlan_igmp_join(struct vxlan_dev *vxlan) { @@ -1229,7 +1229,7 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb) * this as a malformed packet. This behavior diverges from * VXLAN RFC (RFC7348) which stipulates that bits in reserved * in reserved fields are to be ignored. The approach here - * maintains compatbility with previous stack code, and also + * maintains compatibility with previous stack code, and also * is more robust and provides a little more security in * adding extensions to VXLAN. */ @@ -1672,7 +1672,8 @@ static void vxlan_build_gbp_hdr(struct vxlanhdr *vxh, u32 vxflags, } #if IS_ENABLED(CONFIG_IPV6) -static int vxlan6_xmit_skb(struct dst_entry *dst, struct sk_buff *skb, +static int vxlan6_xmit_skb(struct dst_entry *dst, struct sock *sk, + struct sk_buff *skb, struct net_device *dev, struct in6_addr *saddr, struct in6_addr *daddr, __u8 prio, __u8 ttl, __be16 src_port, __be16 dst_port, @@ -1748,7 +1749,7 @@ static int vxlan6_xmit_skb(struct dst_entry *dst, struct sk_buff *skb, skb_set_inner_protocol(skb, htons(ETH_P_TEB)); - udp_tunnel6_xmit_skb(dst, skb, dev, saddr, daddr, prio, + udp_tunnel6_xmit_skb(dst, sk, skb, dev, saddr, daddr, prio, ttl, src_port, dst_port, !!(vxflags & VXLAN_F_UDP_ZERO_CSUM6_TX)); return 0; @@ -1758,7 +1759,7 @@ err: } #endif -int vxlan_xmit_skb(struct rtable *rt, struct sk_buff *skb, +int vxlan_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb, __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df, __be16 src_port, __be16 dst_port, struct vxlan_metadata *md, bool xnet, u32 vxflags) @@ -1827,7 +1828,7 @@ int vxlan_xmit_skb(struct rtable *rt, struct sk_buff *skb, skb_set_inner_protocol(skb, htons(ETH_P_TEB)); - return udp_tunnel_xmit_skb(rt, skb, src, dst, tos, + return udp_tunnel_xmit_skb(rt, sk, skb, src, dst, tos, ttl, df, src_port, dst_port, xnet, !(vxflags & VXLAN_F_UDP_CSUM)); } @@ -1882,6 +1883,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, struct vxlan_rdst *rdst, bool did_rsc) { struct vxlan_dev *vxlan = netdev_priv(dev); + struct sock *sk = vxlan->vn_sock->sock->sk; struct rtable *rt = NULL; const struct iphdr *old_iph; struct flowi4 fl4; @@ -1961,7 +1963,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, md.vni = htonl(vni << 8); md.gbp = skb->mark; - err = vxlan_xmit_skb(rt, skb, fl4.saddr, + err = vxlan_xmit_skb(rt, sk, skb, fl4.saddr, dst->sin.sin_addr.s_addr, tos, ttl, df, src_port, dst_port, &md, !net_eq(vxlan->net, dev_net(vxlan->dev)), @@ -2021,7 +2023,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, md.vni = htonl(vni << 8); md.gbp = skb->mark; - err = vxlan6_xmit_skb(ndst, skb, dev, &fl6.saddr, &fl6.daddr, + err = vxlan6_xmit_skb(ndst, sk, skb, dev, &fl6.saddr, &fl6.daddr, 0, ttl, src_port, dst_port, &md, !net_eq(vxlan->net, dev_net(vxlan->dev)), vxlan->flags); diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c index 88d121d43c08..bcfa01add7cc 100644 --- a/drivers/net/wan/cosa.c +++ b/drivers/net/wan/cosa.c @@ -579,6 +579,7 @@ static int cosa_probe(int base, int irq, int dma) /* Register the network interface */ if (!(chan->netdev = alloc_hdlcdev(chan))) { pr_warn("%s: alloc_hdlcdev failed\n", chan->name); + err = -ENOMEM; goto err_hdlcdev; } dev_to_hdlc(chan->netdev)->attach = cosa_net_attach; diff --git a/drivers/net/wan/lmc/lmc_main.c b/drivers/net/wan/lmc/lmc_main.c index bea0f313a7a8..317bc79cc8b9 100644 --- a/drivers/net/wan/lmc/lmc_main.c +++ b/drivers/net/wan/lmc/lmc_main.c @@ -850,6 +850,7 @@ static int lmc_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev = alloc_hdlcdev(sc); if (!dev) { printk(KERN_ERR "lmc:alloc_netdev for device failed\n"); + err = -ENOMEM; goto err_hdlcdev; } diff --git a/drivers/net/wireless/ath/ar5523/ar5523.c b/drivers/net/wireless/ath/ar5523/ar5523.c index f92050617ae6..5147ebe4cd05 100644 --- a/drivers/net/wireless/ath/ar5523/ar5523.c +++ b/drivers/net/wireless/ath/ar5523/ar5523.c @@ -779,8 +779,6 @@ static void ar5523_tx(struct ieee80211_hw *hw, ieee80211_stop_queues(hw); } - data->skb = skb; - spin_lock_irqsave(&ar->tx_data_list_lock, flags); list_add_tail(&data->list, &ar->tx_queue_pending); spin_unlock_irqrestore(&ar->tx_data_list_lock, flags); @@ -817,10 +815,13 @@ static void ar5523_tx_work_locked(struct ar5523 *ar) if (!data) break; - skb = data->skb; + txi = container_of((void *)data, struct ieee80211_tx_info, + driver_data); txqid = 0; - txi = IEEE80211_SKB_CB(skb); + + skb = container_of((void *)txi, struct sk_buff, cb); paylen = skb->len; + urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { ar5523_err(ar, "Failed to allocate TX urb\n"); diff --git a/drivers/net/wireless/ath/ar5523/ar5523.h b/drivers/net/wireless/ath/ar5523/ar5523.h index 00c6fd346d48..9a322a65cdb5 100644 --- a/drivers/net/wireless/ath/ar5523/ar5523.h +++ b/drivers/net/wireless/ath/ar5523/ar5523.h @@ -74,7 +74,6 @@ struct ar5523_tx_cmd { struct ar5523_tx_data { struct list_head list; struct ar5523 *ar; - struct sk_buff *skb; struct urb *urb; }; diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index 1eebe2ea3dfb..7e9481099a8e 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -131,6 +131,9 @@ struct ath_ops { void (*enable_write_buffer)(void *); void (*write_flush) (void *); u32 (*rmw)(void *, u32 reg_offset, u32 set, u32 clr); + void (*enable_rmw_buffer)(void *); + void (*rmw_flush) (void *); + }; struct ath_common; diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index 1ed7a88aeea9..7ca0d6f930fd 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -1283,6 +1283,7 @@ struct ath5k_hw { #define ATH_STAT_PROMISC 1 #define ATH_STAT_LEDSOFT 2 /* enable LED gpio status */ #define ATH_STAT_STARTED 3 /* opened & irqs enabled */ +#define ATH_STAT_RESET 4 /* hw reset */ unsigned int filter_flags; /* HW flags, AR5K_RX_FILTER_* */ unsigned int fif_filter_flags; /* Current FIF_* filter flags */ diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 57a80e89822d..a6131825c9f6 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1523,6 +1523,9 @@ ath5k_set_current_imask(struct ath5k_hw *ah) enum ath5k_int imask; unsigned long flags; + if (test_bit(ATH_STAT_RESET, ah->status)) + return; + spin_lock_irqsave(&ah->irqlock, flags); imask = ah->imask; if (ah->rx_pending) @@ -2858,10 +2861,12 @@ ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan, { struct ath_common *common = ath5k_hw_common(ah); int ret, ani_mode; - bool fast; + bool fast = chan && modparam_fastchanswitch ? 1 : 0; ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "resetting\n"); + __set_bit(ATH_STAT_RESET, ah->status); + ath5k_hw_set_imr(ah, 0); synchronize_irq(ah->irq); ath5k_stop_tasklets(ah); @@ -2876,11 +2881,29 @@ ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan, * so we should also free any remaining * tx buffers */ ath5k_drain_tx_buffs(ah); + + /* Stop PCU */ + ath5k_hw_stop_rx_pcu(ah); + + /* Stop DMA + * + * Note: If DMA didn't stop continue + * since only a reset will fix it. + */ + ret = ath5k_hw_dma_stop(ah); + + /* RF Bus grant won't work if we have pending + * frames + */ + if (ret && fast) { + ATH5K_DBG(ah, ATH5K_DEBUG_RESET, + "DMA didn't stop, falling back to normal reset\n"); + fast = false; + } + if (chan) ah->curchan = chan; - fast = ((chan != NULL) && modparam_fastchanswitch) ? 1 : 0; - ret = ath5k_hw_reset(ah, ah->opmode, ah->curchan, fast, skip_pcu); if (ret) { ATH5K_ERR(ah, "can't reset hardware (%d)\n", ret); @@ -2934,6 +2957,8 @@ ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan, */ /* ath5k_chan_change(ah, c); */ + __clear_bit(ATH_STAT_RESET, ah->status); + ath5k_beacon_config(ah); /* intrs are enabled by ath5k_beacon_config */ diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c index b9b651ea9851..99e62f99a182 100644 --- a/drivers/net/wireless/ath/ath5k/reset.c +++ b/drivers/net/wireless/ath/ath5k/reset.c @@ -1169,30 +1169,6 @@ ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, if (ah->ah_version == AR5K_AR5212) ath5k_hw_set_sleep_clock(ah, false); - /* - * Stop PCU - */ - ath5k_hw_stop_rx_pcu(ah); - - /* - * Stop DMA - * - * Note: If DMA didn't stop continue - * since only a reset will fix it. - */ - ret = ath5k_hw_dma_stop(ah); - - /* RF Bus grant won't work if we have pending - * frames */ - if (ret && fast) { - ATH5K_DBG(ah, ATH5K_DEBUG_RESET, - "DMA didn't stop, falling back to normal reset\n"); - fast = false; - /* Non fatal, just continue with - * normal reset */ - ret = 0; - } - mode = channel->hw_value; switch (mode) { case AR5K_MODE_11A: diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index 473972288a84..ecda613c2d54 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -46,7 +46,8 @@ ath9k_hw-y:= \ ath9k_hw-$(CONFIG_ATH9K_WOW) += ar9003_wow.o ath9k_hw-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += btcoex.o \ - ar9003_mci.o + ar9003_mci.o \ + ar9003_aic.o ath9k_hw-$(CONFIG_ATH9K_PCOEM) += ar9003_rtt.o diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index ca01d17d130f..25e45e4d1a60 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -107,11 +107,21 @@ static const struct ani_cck_level_entry cck_level_table[] = { static void ath9k_hw_update_mibstats(struct ath_hw *ah, struct ath9k_mib_stats *stats) { - stats->ackrcv_bad += REG_READ(ah, AR_ACK_FAIL); - stats->rts_bad += REG_READ(ah, AR_RTS_FAIL); - stats->fcs_bad += REG_READ(ah, AR_FCS_FAIL); - stats->rts_good += REG_READ(ah, AR_RTS_OK); - stats->beacons += REG_READ(ah, AR_BEACON_CNT); + u32 addr[5] = {AR_RTS_OK, AR_RTS_FAIL, AR_ACK_FAIL, + AR_FCS_FAIL, AR_BEACON_CNT}; + u32 data[5]; + + REG_READ_MULTI(ah, &addr[0], &data[0], 5); + /* AR_RTS_OK */ + stats->rts_good += data[0]; + /* AR_RTS_FAIL */ + stats->rts_bad += data[1]; + /* AR_ACK_FAIL */ + stats->ackrcv_bad += data[2]; + /* AR_FCS_FAIL */ + stats->fcs_bad += data[3]; + /* AR_BEACON_CNT */ + stats->beacons += data[4]; } static void ath9k_ani_restart(struct ath_hw *ah) diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index f273427fdd29..6c23d279525f 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -681,12 +681,13 @@ static void ar5008_hw_set_channel_regs(struct ath_hw *ah, phymode |= AR_PHY_FC_DYN2040_PRI_CH; } + ENABLE_REGWRITE_BUFFER(ah); REG_WRITE(ah, AR_PHY_TURBO, phymode); + /* This function do only REG_WRITE, so + * we can include it to REGWRITE_BUFFER. */ ath9k_hw_set11nmac2040(ah, chan); - ENABLE_REGWRITE_BUFFER(ah); - REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S); REG_WRITE(ah, AR_CST, 0xF << AR_CST_TIMEOUT_LIMIT_S); diff --git a/drivers/net/wireless/ath/ath9k/ar9002_calib.c b/drivers/net/wireless/ath/ath9k/ar9002_calib.c index 42190b67c671..50fcd343c41a 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c @@ -430,46 +430,43 @@ static void ar9271_hw_pa_cal(struct ath_hw *ah, bool is_reset) u32 regVal; unsigned int i; u32 regList[][2] = { - { 0x786c, 0 }, - { 0x7854, 0 }, - { 0x7820, 0 }, - { 0x7824, 0 }, - { 0x7868, 0 }, - { 0x783c, 0 }, - { 0x7838, 0 } , - { 0x7828, 0 } , + { AR9285_AN_TOP3, 0 }, + { AR9285_AN_RXTXBB1, 0 }, + { AR9285_AN_RF2G1, 0 }, + { AR9285_AN_RF2G2, 0 }, + { AR9285_AN_TOP2, 0 }, + { AR9285_AN_RF2G8, 0 }, + { AR9285_AN_RF2G7, 0 }, + { AR9285_AN_RF2G3, 0 }, }; - for (i = 0; i < ARRAY_SIZE(regList); i++) - regList[i][1] = REG_READ(ah, regList[i][0]); - - regVal = REG_READ(ah, 0x7834); - regVal &= (~(0x1)); - REG_WRITE(ah, 0x7834, regVal); - regVal = REG_READ(ah, 0x9808); - regVal |= (0x1 << 27); - REG_WRITE(ah, 0x9808, regVal); + REG_READ_ARRAY(ah, regList, ARRAY_SIZE(regList)); + ENABLE_REG_RMW_BUFFER(ah); + /* 7834, b1=0 */ + REG_CLR_BIT(ah, AR9285_AN_RF2G6, 1 << 0); + /* 9808, b27=1 */ + REG_SET_BIT(ah, 0x9808, 1 << 27); /* 786c,b23,1, pwddac=1 */ - REG_RMW_FIELD(ah, AR9285_AN_TOP3, AR9285_AN_TOP3_PWDDAC, 1); + REG_SET_BIT(ah, AR9285_AN_TOP3, AR9285_AN_TOP3_PWDDAC); /* 7854, b5,1, pdrxtxbb=1 */ - REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDRXTXBB1, 1); + REG_SET_BIT(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDRXTXBB1); /* 7854, b7,1, pdv2i=1 */ - REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDV2I, 1); + REG_SET_BIT(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDV2I); /* 7854, b8,1, pddacinterface=1 */ - REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDDACIF, 1); + REG_SET_BIT(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDDACIF); /* 7824,b12,0, offcal=0 */ - REG_RMW_FIELD(ah, AR9285_AN_RF2G2, AR9285_AN_RF2G2_OFFCAL, 0); + REG_CLR_BIT(ah, AR9285_AN_RF2G2, AR9285_AN_RF2G2_OFFCAL); /* 7838, b1,0, pwddb=0 */ - REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PWDDB, 0); + REG_CLR_BIT(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PWDDB); /* 7820,b11,0, enpacal=0 */ - REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_ENPACAL, 0); + REG_CLR_BIT(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_ENPACAL); /* 7820,b25,1, pdpadrv1=0 */ - REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV1, 0); + REG_CLR_BIT(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV1); /* 7820,b24,0, pdpadrv2=0 */ - REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV2, 0); + REG_CLR_BIT(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV2); /* 7820,b23,0, pdpaout=0 */ - REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPAOUT, 0); + REG_CLR_BIT(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPAOUT); /* 783c,b14-16,7, padrvgn2tab_0=7 */ REG_RMW_FIELD(ah, AR9285_AN_RF2G8, AR9285_AN_RF2G8_PADRVGN2TAB0, 7); /* @@ -477,8 +474,9 @@ static void ar9271_hw_pa_cal(struct ath_hw *ah, bool is_reset) * does not matter since we turn it off */ REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PADRVGN2TAB0, 0); - + /* 7828, b0-11, ccom=fff */ REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9271_AN_RF2G3_CCOMP, 0xfff); + REG_RMW_BUFFER_FLUSH(ah); /* Set: * localmode=1,bmode=1,bmoderxtx=1,synthon=1, @@ -490,15 +488,16 @@ static void ar9271_hw_pa_cal(struct ath_hw *ah, bool is_reset) /* find off_6_1; */ for (i = 6; i > 0; i--) { - regVal = REG_READ(ah, 0x7834); + regVal = REG_READ(ah, AR9285_AN_RF2G6); regVal |= (1 << (20 + i)); - REG_WRITE(ah, 0x7834, regVal); + REG_WRITE(ah, AR9285_AN_RF2G6, regVal); udelay(1); /* regVal = REG_READ(ah, 0x7834); */ regVal &= (~(0x1 << (20 + i))); - regVal |= (MS(REG_READ(ah, 0x7840), AR9285_AN_RXTXBB1_SPARE9) + regVal |= (MS(REG_READ(ah, AR9285_AN_RF2G9), + AR9285_AN_RXTXBB1_SPARE9) << (20 + i)); - REG_WRITE(ah, 0x7834, regVal); + REG_WRITE(ah, AR9285_AN_RF2G6, regVal); } regVal = (regVal >> 20) & 0x7f; @@ -515,15 +514,15 @@ static void ar9271_hw_pa_cal(struct ath_hw *ah, bool is_reset) ah->pacal_info.prev_offset = regVal; } - ENABLE_REGWRITE_BUFFER(ah); - regVal = REG_READ(ah, 0x7834); - regVal |= 0x1; - REG_WRITE(ah, 0x7834, regVal); - regVal = REG_READ(ah, 0x9808); - regVal &= (~(0x1 << 27)); - REG_WRITE(ah, 0x9808, regVal); + ENABLE_REG_RMW_BUFFER(ah); + /* 7834, b1=1 */ + REG_SET_BIT(ah, AR9285_AN_RF2G6, 1 << 0); + /* 9808, b27=0 */ + REG_CLR_BIT(ah, 0x9808, 1 << 27); + REG_RMW_BUFFER_FLUSH(ah); + ENABLE_REGWRITE_BUFFER(ah); for (i = 0; i < ARRAY_SIZE(regList); i++) REG_WRITE(ah, regList[i][0], regList[i][1]); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_aic.c b/drivers/net/wireless/ath/ath9k/ar9003_aic.c new file mode 100644 index 000000000000..1db119d77783 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/ar9003_aic.c @@ -0,0 +1,599 @@ +/* + * Copyright (c) 2015 Qualcomm Atheros Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "hw.h" +#include "hw-ops.h" +#include "ar9003_mci.h" +#include "ar9003_aic.h" +#include "ar9003_phy.h" +#include "reg_aic.h" + +static const u8 com_att_db_table[ATH_AIC_MAX_COM_ATT_DB_TABLE] = { + 0, 3, 9, 15, 21, 27 +}; + +static const u16 aic_lin_table[ATH_AIC_MAX_AIC_LIN_TABLE] = { + 8191, 7300, 6506, 5799, 5168, 4606, 4105, 3659, + 3261, 2906, 2590, 2309, 2057, 1834, 1634, 1457, + 1298, 1157, 1031, 919, 819, 730, 651, 580, + 517, 461, 411, 366, 326, 291, 259, 231, + 206, 183, 163, 146, 130, 116, 103, 92, + 82, 73, 65, 58, 52, 46, 41, 37, + 33, 29, 26, 23, 21, 18, 16, 15, + 13, 12, 10, 9, 8, 7, 7, 6, + 5, 5, 4, 4, 3 +}; + +static bool ar9003_hw_is_aic_enabled(struct ath_hw *ah) +{ + struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; + + /* + * Disable AIC for now, until we have all the + * HW code and the driver-layer support ready. + */ + return false; + + if (mci_hw->config & ATH_MCI_CONFIG_DISABLE_AIC) + return false; + + return true; +} + +static int16_t ar9003_aic_find_valid(struct ath_aic_sram_info *cal_sram, + bool dir, u8 index) +{ + int16_t i; + + if (dir) { + for (i = index + 1; i < ATH_AIC_MAX_BT_CHANNEL; i++) { + if (cal_sram[i].valid) + break; + } + } else { + for (i = index - 1; i >= 0; i--) { + if (cal_sram[i].valid) + break; + } + } + + if ((i >= ATH_AIC_MAX_BT_CHANNEL) || (i < 0)) + i = -1; + + return i; +} + +/* + * type 0: aic_lin_table, 1: com_att_db_table + */ +static int16_t ar9003_aic_find_index(u8 type, int16_t value) +{ + int16_t i = -1; + + if (type == 0) { + for (i = ATH_AIC_MAX_AIC_LIN_TABLE - 1; i >= 0; i--) { + if (aic_lin_table[i] >= value) + break; + } + } else if (type == 1) { + for (i = 0; i < ATH_AIC_MAX_COM_ATT_DB_TABLE; i++) { + if (com_att_db_table[i] > value) { + i--; + break; + } + } + + if (i >= ATH_AIC_MAX_COM_ATT_DB_TABLE) + i = -1; + } + + return i; +} + +static void ar9003_aic_gain_table(struct ath_hw *ah) +{ + u32 aic_atten_word[19], i; + + /* Config LNA gain difference */ + REG_WRITE(ah, AR_PHY_BT_COEX_4, 0x2c200a00); + REG_WRITE(ah, AR_PHY_BT_COEX_5, 0x5c4e4438); + + /* Program gain table */ + aic_atten_word[0] = (0x1 & 0xf) << 14 | (0x1f & 0x1f) << 9 | (0x0 & 0xf) << 5 | + (0x1f & 0x1f); /* -01 dB: 4'd1, 5'd31, 00 dB: 4'd0, 5'd31 */ + aic_atten_word[1] = (0x3 & 0xf) << 14 | (0x1f & 0x1f) << 9 | (0x2 & 0xf) << 5 | + (0x1f & 0x1f); /* -03 dB: 4'd3, 5'd31, -02 dB: 4'd2, 5'd31 */ + aic_atten_word[2] = (0x5 & 0xf) << 14 | (0x1f & 0x1f) << 9 | (0x4 & 0xf) << 5 | + (0x1f & 0x1f); /* -05 dB: 4'd5, 5'd31, -04 dB: 4'd4, 5'd31 */ + aic_atten_word[3] = (0x1 & 0xf) << 14 | (0x1e & 0x1f) << 9 | (0x0 & 0xf) << 5 | + (0x1e & 0x1f); /* -07 dB: 4'd1, 5'd30, -06 dB: 4'd0, 5'd30 */ + aic_atten_word[4] = (0x3 & 0xf) << 14 | (0x1e & 0x1f) << 9 | (0x2 & 0xf) << 5 | + (0x1e & 0x1f); /* -09 dB: 4'd3, 5'd30, -08 dB: 4'd2, 5'd30 */ + aic_atten_word[5] = (0x5 & 0xf) << 14 | (0x1e & 0x1f) << 9 | (0x4 & 0xf) << 5 | + (0x1e & 0x1f); /* -11 dB: 4'd5, 5'd30, -10 dB: 4'd4, 5'd30 */ + aic_atten_word[6] = (0x1 & 0xf) << 14 | (0xf & 0x1f) << 9 | (0x0 & 0xf) << 5 | + (0xf & 0x1f); /* -13 dB: 4'd1, 5'd15, -12 dB: 4'd0, 5'd15 */ + aic_atten_word[7] = (0x3 & 0xf) << 14 | (0xf & 0x1f) << 9 | (0x2 & 0xf) << 5 | + (0xf & 0x1f); /* -15 dB: 4'd3, 5'd15, -14 dB: 4'd2, 5'd15 */ + aic_atten_word[8] = (0x5 & 0xf) << 14 | (0xf & 0x1f) << 9 | (0x4 & 0xf) << 5 | + (0xf & 0x1f); /* -17 dB: 4'd5, 5'd15, -16 dB: 4'd4, 5'd15 */ + aic_atten_word[9] = (0x1 & 0xf) << 14 | (0x7 & 0x1f) << 9 | (0x0 & 0xf) << 5 | + (0x7 & 0x1f); /* -19 dB: 4'd1, 5'd07, -18 dB: 4'd0, 5'd07 */ + aic_atten_word[10] = (0x3 & 0xf) << 14 | (0x7 & 0x1f) << 9 | (0x2 & 0xf) << 5 | + (0x7 & 0x1f); /* -21 dB: 4'd3, 5'd07, -20 dB: 4'd2, 5'd07 */ + aic_atten_word[11] = (0x5 & 0xf) << 14 | (0x7 & 0x1f) << 9 | (0x4 & 0xf) << 5 | + (0x7 & 0x1f); /* -23 dB: 4'd5, 5'd07, -22 dB: 4'd4, 5'd07 */ + aic_atten_word[12] = (0x7 & 0xf) << 14 | (0x7 & 0x1f) << 9 | (0x6 & 0xf) << 5 | + (0x7 & 0x1f); /* -25 dB: 4'd7, 5'd07, -24 dB: 4'd6, 5'd07 */ + aic_atten_word[13] = (0x3 & 0xf) << 14 | (0x3 & 0x1f) << 9 | (0x2 & 0xf) << 5 | + (0x3 & 0x1f); /* -27 dB: 4'd3, 5'd03, -26 dB: 4'd2, 5'd03 */ + aic_atten_word[14] = (0x5 & 0xf) << 14 | (0x3 & 0x1f) << 9 | (0x4 & 0xf) << 5 | + (0x3 & 0x1f); /* -29 dB: 4'd5, 5'd03, -28 dB: 4'd4, 5'd03 */ + aic_atten_word[15] = (0x1 & 0xf) << 14 | (0x1 & 0x1f) << 9 | (0x0 & 0xf) << 5 | + (0x1 & 0x1f); /* -31 dB: 4'd1, 5'd01, -30 dB: 4'd0, 5'd01 */ + aic_atten_word[16] = (0x3 & 0xf) << 14 | (0x1 & 0x1f) << 9 | (0x2 & 0xf) << 5 | + (0x1 & 0x1f); /* -33 dB: 4'd3, 5'd01, -32 dB: 4'd2, 5'd01 */ + aic_atten_word[17] = (0x5 & 0xf) << 14 | (0x1 & 0x1f) << 9 | (0x4 & 0xf) << 5 | + (0x1 & 0x1f); /* -35 dB: 4'd5, 5'd01, -34 dB: 4'd4, 5'd01 */ + aic_atten_word[18] = (0x7 & 0xf) << 14 | (0x1 & 0x1f) << 9 | (0x6 & 0xf) << 5 | + (0x1 & 0x1f); /* -37 dB: 4'd7, 5'd01, -36 dB: 4'd6, 5'd01 */ + + /* Write to Gain table with auto increment enabled. */ + REG_WRITE(ah, (AR_PHY_AIC_SRAM_ADDR_B0 + 0x3000), + (ATH_AIC_SRAM_AUTO_INCREMENT | + ATH_AIC_SRAM_GAIN_TABLE_OFFSET)); + + for (i = 0; i < 19; i++) { + REG_WRITE(ah, (AR_PHY_AIC_SRAM_DATA_B0 + 0x3000), + aic_atten_word[i]); + } +} + +static u8 ar9003_aic_cal_start(struct ath_hw *ah, u8 min_valid_count) +{ + struct ath9k_hw_aic *aic = &ah->btcoex_hw.aic; + int i; + + /* Write to Gain table with auto increment enabled. */ + REG_WRITE(ah, (AR_PHY_AIC_SRAM_ADDR_B0 + 0x3000), + (ATH_AIC_SRAM_AUTO_INCREMENT | + ATH_AIC_SRAM_CAL_OFFSET)); + + for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) { + REG_WRITE(ah, (AR_PHY_AIC_SRAM_DATA_B0 + 0x3000), 0); + aic->aic_sram[i] = 0; + } + + REG_WRITE(ah, AR_PHY_AIC_CTRL_0_B0, + (SM(0, AR_PHY_AIC_MON_ENABLE) | + SM(127, AR_PHY_AIC_CAL_MAX_HOP_COUNT) | + SM(min_valid_count, AR_PHY_AIC_CAL_MIN_VALID_COUNT) | + SM(37, AR_PHY_AIC_F_WLAN) | + SM(1, AR_PHY_AIC_CAL_CH_VALID_RESET) | + SM(0, AR_PHY_AIC_CAL_ENABLE) | + SM(0x40, AR_PHY_AIC_BTTX_PWR_THR) | + SM(0, AR_PHY_AIC_ENABLE))); + + REG_WRITE(ah, AR_PHY_AIC_CTRL_0_B1, + (SM(0, AR_PHY_AIC_MON_ENABLE) | + SM(1, AR_PHY_AIC_CAL_CH_VALID_RESET) | + SM(0, AR_PHY_AIC_CAL_ENABLE) | + SM(0x40, AR_PHY_AIC_BTTX_PWR_THR) | + SM(0, AR_PHY_AIC_ENABLE))); + + REG_WRITE(ah, AR_PHY_AIC_CTRL_1_B0, + (SM(8, AR_PHY_AIC_CAL_BT_REF_DELAY) | + SM(0, AR_PHY_AIC_BT_IDLE_CFG) | + SM(1, AR_PHY_AIC_STDBY_COND) | + SM(37, AR_PHY_AIC_STDBY_ROT_ATT_DB) | + SM(5, AR_PHY_AIC_STDBY_COM_ATT_DB) | + SM(15, AR_PHY_AIC_RSSI_MAX) | + SM(0, AR_PHY_AIC_RSSI_MIN))); + + REG_WRITE(ah, AR_PHY_AIC_CTRL_1_B1, + (SM(15, AR_PHY_AIC_RSSI_MAX) | + SM(0, AR_PHY_AIC_RSSI_MIN))); + + REG_WRITE(ah, AR_PHY_AIC_CTRL_2_B0, + (SM(44, AR_PHY_AIC_RADIO_DELAY) | + SM(8, AR_PHY_AIC_CAL_STEP_SIZE_CORR) | + SM(12, AR_PHY_AIC_CAL_ROT_IDX_CORR) | + SM(2, AR_PHY_AIC_CAL_CONV_CHECK_FACTOR) | + SM(5, AR_PHY_AIC_ROT_IDX_COUNT_MAX) | + SM(0, AR_PHY_AIC_CAL_SYNTH_TOGGLE) | + SM(0, AR_PHY_AIC_CAL_SYNTH_AFTER_BTRX) | + SM(200, AR_PHY_AIC_CAL_SYNTH_SETTLING))); + + REG_WRITE(ah, AR_PHY_AIC_CTRL_3_B0, + (SM(2, AR_PHY_AIC_MON_MAX_HOP_COUNT) | + SM(1, AR_PHY_AIC_MON_MIN_STALE_COUNT) | + SM(1, AR_PHY_AIC_MON_PWR_EST_LONG) | + SM(2, AR_PHY_AIC_MON_PD_TALLY_SCALING) | + SM(10, AR_PHY_AIC_MON_PERF_THR) | + SM(2, AR_PHY_AIC_CAL_TARGET_MAG_SETTING) | + SM(1, AR_PHY_AIC_CAL_PERF_CHECK_FACTOR) | + SM(1, AR_PHY_AIC_CAL_PWR_EST_LONG))); + + REG_WRITE(ah, AR_PHY_AIC_CTRL_4_B0, + (SM(2, AR_PHY_AIC_CAL_ROT_ATT_DB_EST_ISO) | + SM(3, AR_PHY_AIC_CAL_COM_ATT_DB_EST_ISO) | + SM(0, AR_PHY_AIC_CAL_ISO_EST_INIT_SETTING) | + SM(2, AR_PHY_AIC_CAL_COM_ATT_DB_BACKOFF) | + SM(1, AR_PHY_AIC_CAL_COM_ATT_DB_FIXED))); + + REG_WRITE(ah, AR_PHY_AIC_CTRL_4_B1, + (SM(2, AR_PHY_AIC_CAL_ROT_ATT_DB_EST_ISO) | + SM(3, AR_PHY_AIC_CAL_COM_ATT_DB_EST_ISO) | + SM(0, AR_PHY_AIC_CAL_ISO_EST_INIT_SETTING) | + SM(2, AR_PHY_AIC_CAL_COM_ATT_DB_BACKOFF) | + SM(1, AR_PHY_AIC_CAL_COM_ATT_DB_FIXED))); + + ar9003_aic_gain_table(ah); + + /* Need to enable AIC reference signal in BT modem. */ + REG_WRITE(ah, ATH_AIC_BT_JUPITER_CTRL, + (REG_READ(ah, ATH_AIC_BT_JUPITER_CTRL) | + ATH_AIC_BT_AIC_ENABLE)); + + aic->aic_cal_start_time = REG_READ(ah, AR_TSF_L32); + + /* Start calibration */ + REG_CLR_BIT(ah, AR_PHY_AIC_CTRL_0_B1, AR_PHY_AIC_CAL_ENABLE); + REG_SET_BIT(ah, AR_PHY_AIC_CTRL_0_B1, AR_PHY_AIC_CAL_CH_VALID_RESET); + REG_SET_BIT(ah, AR_PHY_AIC_CTRL_0_B1, AR_PHY_AIC_CAL_ENABLE); + + aic->aic_caled_chan = 0; + aic->aic_cal_state = AIC_CAL_STATE_STARTED; + + return aic->aic_cal_state; +} + +static bool ar9003_aic_cal_post_process(struct ath_hw *ah) +{ + struct ath9k_hw_aic *aic = &ah->btcoex_hw.aic; + struct ath_aic_sram_info cal_sram[ATH_AIC_MAX_BT_CHANNEL]; + struct ath_aic_out_info aic_sram[ATH_AIC_MAX_BT_CHANNEL]; + u32 dir_path_gain_idx, quad_path_gain_idx, value; + u32 fixed_com_att_db; + int8_t dir_path_sign, quad_path_sign; + int16_t i; + bool ret = true; + + memset(&cal_sram, 0, sizeof(cal_sram)); + memset(&aic_sram, 0, sizeof(aic_sram)); + + for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) { + value = aic->aic_sram[i]; + + cal_sram[i].valid = + MS(value, AR_PHY_AIC_SRAM_VALID); + cal_sram[i].rot_quad_att_db = + MS(value, AR_PHY_AIC_SRAM_ROT_QUAD_ATT_DB); + cal_sram[i].vga_quad_sign = + MS(value, AR_PHY_AIC_SRAM_VGA_QUAD_SIGN); + cal_sram[i].rot_dir_att_db = + MS(value, AR_PHY_AIC_SRAM_ROT_DIR_ATT_DB); + cal_sram[i].vga_dir_sign = + MS(value, AR_PHY_AIC_SRAM_VGA_DIR_SIGN); + cal_sram[i].com_att_6db = + MS(value, AR_PHY_AIC_SRAM_COM_ATT_6DB); + + if (cal_sram[i].valid) { + dir_path_gain_idx = cal_sram[i].rot_dir_att_db + + com_att_db_table[cal_sram[i].com_att_6db]; + quad_path_gain_idx = cal_sram[i].rot_quad_att_db + + com_att_db_table[cal_sram[i].com_att_6db]; + + dir_path_sign = (cal_sram[i].vga_dir_sign) ? 1 : -1; + quad_path_sign = (cal_sram[i].vga_quad_sign) ? 1 : -1; + + aic_sram[i].dir_path_gain_lin = dir_path_sign * + aic_lin_table[dir_path_gain_idx]; + aic_sram[i].quad_path_gain_lin = quad_path_sign * + aic_lin_table[quad_path_gain_idx]; + } + } + + for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) { + int16_t start_idx, end_idx; + + if (cal_sram[i].valid) + continue; + + start_idx = ar9003_aic_find_valid(cal_sram, 0, i); + end_idx = ar9003_aic_find_valid(cal_sram, 1, i); + + if (start_idx < 0) { + /* extrapolation */ + start_idx = end_idx; + end_idx = ar9003_aic_find_valid(cal_sram, 1, start_idx); + + if (end_idx < 0) { + ret = false; + break; + } + + aic_sram[i].dir_path_gain_lin = + ((aic_sram[start_idx].dir_path_gain_lin - + aic_sram[end_idx].dir_path_gain_lin) * + (start_idx - i) + ((end_idx - i) >> 1)) / + (end_idx - i) + + aic_sram[start_idx].dir_path_gain_lin; + aic_sram[i].quad_path_gain_lin = + ((aic_sram[start_idx].quad_path_gain_lin - + aic_sram[end_idx].quad_path_gain_lin) * + (start_idx - i) + ((end_idx - i) >> 1)) / + (end_idx - i) + + aic_sram[start_idx].quad_path_gain_lin; + } + + if (end_idx < 0) { + /* extrapolation */ + end_idx = ar9003_aic_find_valid(cal_sram, 0, start_idx); + + if (end_idx < 0) { + ret = false; + break; + } + + aic_sram[i].dir_path_gain_lin = + ((aic_sram[start_idx].dir_path_gain_lin - + aic_sram[end_idx].dir_path_gain_lin) * + (i - start_idx) + ((start_idx - end_idx) >> 1)) / + (start_idx - end_idx) + + aic_sram[start_idx].dir_path_gain_lin; + aic_sram[i].quad_path_gain_lin = + ((aic_sram[start_idx].quad_path_gain_lin - + aic_sram[end_idx].quad_path_gain_lin) * + (i - start_idx) + ((start_idx - end_idx) >> 1)) / + (start_idx - end_idx) + + aic_sram[start_idx].quad_path_gain_lin; + + } else if (start_idx >= 0){ + /* interpolation */ + aic_sram[i].dir_path_gain_lin = + (((end_idx - i) * aic_sram[start_idx].dir_path_gain_lin) + + ((i - start_idx) * aic_sram[end_idx].dir_path_gain_lin) + + ((end_idx - start_idx) >> 1)) / + (end_idx - start_idx); + aic_sram[i].quad_path_gain_lin = + (((end_idx - i) * aic_sram[start_idx].quad_path_gain_lin) + + ((i - start_idx) * aic_sram[end_idx].quad_path_gain_lin) + + ((end_idx - start_idx) >> 1))/ + (end_idx - start_idx); + } + } + + /* From dir/quad_path_gain_lin to sram. */ + i = ar9003_aic_find_valid(cal_sram, 1, 0); + if (i < 0) { + i = 0; + ret = false; + } + fixed_com_att_db = com_att_db_table[cal_sram[i].com_att_6db]; + + for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) { + int16_t rot_dir_path_att_db, rot_quad_path_att_db; + + aic_sram[i].sram.vga_dir_sign = + (aic_sram[i].dir_path_gain_lin >= 0) ? 1 : 0; + aic_sram[i].sram.vga_quad_sign= + (aic_sram[i].quad_path_gain_lin >= 0) ? 1 : 0; + + rot_dir_path_att_db = + ar9003_aic_find_index(0, abs(aic_sram[i].dir_path_gain_lin)) - + fixed_com_att_db; + rot_quad_path_att_db = + ar9003_aic_find_index(0, abs(aic_sram[i].quad_path_gain_lin)) - + fixed_com_att_db; + + aic_sram[i].sram.com_att_6db = + ar9003_aic_find_index(1, fixed_com_att_db); + + aic_sram[i].sram.valid = 1; + + aic_sram[i].sram.rot_dir_att_db = + min(max(rot_dir_path_att_db, + (int16_t)ATH_AIC_MIN_ROT_DIR_ATT_DB), + ATH_AIC_MAX_ROT_DIR_ATT_DB); + aic_sram[i].sram.rot_quad_att_db = + min(max(rot_quad_path_att_db, + (int16_t)ATH_AIC_MIN_ROT_QUAD_ATT_DB), + ATH_AIC_MAX_ROT_QUAD_ATT_DB); + } + + for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) { + aic->aic_sram[i] = (SM(aic_sram[i].sram.vga_dir_sign, + AR_PHY_AIC_SRAM_VGA_DIR_SIGN) | + SM(aic_sram[i].sram.vga_quad_sign, + AR_PHY_AIC_SRAM_VGA_QUAD_SIGN) | + SM(aic_sram[i].sram.com_att_6db, + AR_PHY_AIC_SRAM_COM_ATT_6DB) | + SM(aic_sram[i].sram.valid, + AR_PHY_AIC_SRAM_VALID) | + SM(aic_sram[i].sram.rot_dir_att_db, + AR_PHY_AIC_SRAM_ROT_DIR_ATT_DB) | + SM(aic_sram[i].sram.rot_quad_att_db, + AR_PHY_AIC_SRAM_ROT_QUAD_ATT_DB)); + } + + return ret; +} + +static void ar9003_aic_cal_done(struct ath_hw *ah) +{ + struct ath9k_hw_aic *aic = &ah->btcoex_hw.aic; + + /* Disable AIC reference signal in BT modem. */ + REG_WRITE(ah, ATH_AIC_BT_JUPITER_CTRL, + (REG_READ(ah, ATH_AIC_BT_JUPITER_CTRL) & + ~ATH_AIC_BT_AIC_ENABLE)); + + if (ar9003_aic_cal_post_process(ah)) + aic->aic_cal_state = AIC_CAL_STATE_DONE; + else + aic->aic_cal_state = AIC_CAL_STATE_ERROR; +} + +static u8 ar9003_aic_cal_continue(struct ath_hw *ah, bool cal_once) +{ + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; + struct ath9k_hw_aic *aic = &ah->btcoex_hw.aic; + int i, num_chan; + + num_chan = MS(mci_hw->config, ATH_MCI_CONFIG_AIC_CAL_NUM_CHAN); + + if (!num_chan) { + aic->aic_cal_state = AIC_CAL_STATE_ERROR; + return aic->aic_cal_state; + } + + if (cal_once) { + for (i = 0; i < 10000; i++) { + if ((REG_READ(ah, AR_PHY_AIC_CTRL_0_B1) & + AR_PHY_AIC_CAL_ENABLE) == 0) + break; + + udelay(100); + } + } + + /* + * Use AR_PHY_AIC_CAL_ENABLE bit instead of AR_PHY_AIC_CAL_DONE. + * Sometimes CAL_DONE bit is not asserted. + */ + if ((REG_READ(ah, AR_PHY_AIC_CTRL_0_B1) & + AR_PHY_AIC_CAL_ENABLE) != 0) { + ath_dbg(common, MCI, "AIC cal is not done after 40ms"); + goto exit; + } + + REG_WRITE(ah, AR_PHY_AIC_SRAM_ADDR_B1, + (ATH_AIC_SRAM_CAL_OFFSET | ATH_AIC_SRAM_AUTO_INCREMENT)); + + for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) { + u32 value; + + value = REG_READ(ah, AR_PHY_AIC_SRAM_DATA_B1); + + if (value & 0x01) { + if (aic->aic_sram[i] == 0) + aic->aic_caled_chan++; + + aic->aic_sram[i] = value; + + if (!cal_once) + break; + } + } + + if ((aic->aic_caled_chan >= num_chan) || cal_once) { + ar9003_aic_cal_done(ah); + } else { + /* Start calibration */ + REG_CLR_BIT(ah, AR_PHY_AIC_CTRL_0_B1, AR_PHY_AIC_CAL_ENABLE); + REG_SET_BIT(ah, AR_PHY_AIC_CTRL_0_B1, + AR_PHY_AIC_CAL_CH_VALID_RESET); + REG_SET_BIT(ah, AR_PHY_AIC_CTRL_0_B1, AR_PHY_AIC_CAL_ENABLE); + } +exit: + return aic->aic_cal_state; + +} + +u8 ar9003_aic_calibration(struct ath_hw *ah) +{ + struct ath9k_hw_aic *aic = &ah->btcoex_hw.aic; + u8 cal_ret = AIC_CAL_STATE_ERROR; + + switch (aic->aic_cal_state) { + case AIC_CAL_STATE_IDLE: + cal_ret = ar9003_aic_cal_start(ah, 1); + break; + case AIC_CAL_STATE_STARTED: + cal_ret = ar9003_aic_cal_continue(ah, false); + break; + case AIC_CAL_STATE_DONE: + cal_ret = AIC_CAL_STATE_DONE; + break; + default: + break; + } + + return cal_ret; +} + +u8 ar9003_aic_start_normal(struct ath_hw *ah) +{ + struct ath9k_hw_aic *aic = &ah->btcoex_hw.aic; + int16_t i; + + if (aic->aic_cal_state != AIC_CAL_STATE_DONE) + return 1; + + ar9003_aic_gain_table(ah); + + REG_WRITE(ah, AR_PHY_AIC_SRAM_ADDR_B1, ATH_AIC_SRAM_AUTO_INCREMENT); + + for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) { + REG_WRITE(ah, AR_PHY_AIC_SRAM_DATA_B1, aic->aic_sram[i]); + } + + /* FIXME: Replace these with proper register names */ + REG_WRITE(ah, 0xa6b0, 0x80); + REG_WRITE(ah, 0xa6b4, 0x5b2df0); + REG_WRITE(ah, 0xa6b8, 0x10762cc8); + REG_WRITE(ah, 0xa6bc, 0x1219a4b); + REG_WRITE(ah, 0xa6c0, 0x1e01); + REG_WRITE(ah, 0xb6b4, 0xf0); + REG_WRITE(ah, 0xb6c0, 0x1e01); + REG_WRITE(ah, 0xb6b0, 0x81); + REG_WRITE(ah, AR_PHY_65NM_CH1_RXTX4, 0x40000000); + + aic->aic_enabled = true; + + return 0; +} + +u8 ar9003_aic_cal_reset(struct ath_hw *ah) +{ + struct ath9k_hw_aic *aic = &ah->btcoex_hw.aic; + + aic->aic_cal_state = AIC_CAL_STATE_IDLE; + return aic->aic_cal_state; +} + +u8 ar9003_aic_calibration_single(struct ath_hw *ah) +{ + struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; + u8 cal_ret; + int num_chan; + + num_chan = MS(mci_hw->config, ATH_MCI_CONFIG_AIC_CAL_NUM_CHAN); + + (void) ar9003_aic_cal_start(ah, num_chan); + cal_ret = ar9003_aic_cal_continue(ah, true); + + return cal_ret; +} + +void ar9003_hw_attach_aic_ops(struct ath_hw *ah) +{ + struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); + + priv_ops->is_aic_enabled = ar9003_hw_is_aic_enabled; +} diff --git a/drivers/net/wireless/ath/ath9k/ar9003_aic.h b/drivers/net/wireless/ath/ath9k/ar9003_aic.h new file mode 100644 index 000000000000..86f40644be43 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/ar9003_aic.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015 Qualcomm Atheros Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef AR9003_AIC_H +#define AR9003_AIC_H + +#define ATH_AIC_MAX_COM_ATT_DB_TABLE 6 +#define ATH_AIC_MAX_AIC_LIN_TABLE 69 +#define ATH_AIC_MIN_ROT_DIR_ATT_DB 0 +#define ATH_AIC_MIN_ROT_QUAD_ATT_DB 0 +#define ATH_AIC_MAX_ROT_DIR_ATT_DB 37 +#define ATH_AIC_MAX_ROT_QUAD_ATT_DB 37 +#define ATH_AIC_SRAM_AUTO_INCREMENT 0x80000000 +#define ATH_AIC_SRAM_GAIN_TABLE_OFFSET 0x280 +#define ATH_AIC_SRAM_CAL_OFFSET 0x140 +#define ATH_AIC_SRAM_OFFSET 0x00 +#define ATH_AIC_MEAS_MAG_THRESH 20 +#define ATH_AIC_BT_JUPITER_CTRL 0x66820 +#define ATH_AIC_BT_AIC_ENABLE 0x02 + +enum aic_cal_state { + AIC_CAL_STATE_IDLE = 0, + AIC_CAL_STATE_STARTED, + AIC_CAL_STATE_DONE, + AIC_CAL_STATE_ERROR +}; + +struct ath_aic_sram_info { + bool valid:1; + bool vga_quad_sign:1; + bool vga_dir_sign:1; + u8 rot_quad_att_db; + u8 rot_dir_att_db; + u8 com_att_6db; +}; + +struct ath_aic_out_info { + int16_t dir_path_gain_lin; + int16_t quad_path_gain_lin; + struct ath_aic_sram_info sram; +}; + +u8 ar9003_aic_calibration(struct ath_hw *ah); +u8 ar9003_aic_start_normal(struct ath_hw *ah); +u8 ar9003_aic_cal_reset(struct ath_hw *ah); +u8 ar9003_aic_calibration_single(struct ath_hw *ah); + +#endif /* AR9003_AIC_H */ diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index 4335ccbe7d7e..79fd3b2dcbde 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c @@ -195,16 +195,16 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) INIT_INI_ARRAY(&ah->iniCckfirJapan2484, ar9485_1_1_baseband_core_txfir_coeff_japan_2484); - if (ah->config.no_pll_pwrsave) { + if (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) { INIT_INI_ARRAY(&ah->iniPcieSerdes, - ar9485_1_1_pcie_phy_clkreq_disable_L1); + ar9485_1_1_pll_on_cdr_on_clkreq_disable_L1); INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower, - ar9485_1_1_pcie_phy_clkreq_disable_L1); + ar9485_1_1_pll_on_cdr_on_clkreq_disable_L1); } else { INIT_INI_ARRAY(&ah->iniPcieSerdes, - ar9485_1_1_pll_on_cdr_on_clkreq_disable_L1); + ar9485_1_1_pcie_phy_clkreq_disable_L1); INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower, - ar9485_1_1_pll_on_cdr_on_clkreq_disable_L1); + ar9485_1_1_pcie_phy_clkreq_disable_L1); } } else if (AR_SREV_9462_21(ah)) { INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], @@ -231,10 +231,20 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) ar9462_2p1_modes_fast_clock); INIT_INI_ARRAY(&ah->iniCckfirJapan2484, ar9462_2p1_baseband_core_txfir_coeff_japan_2484); - INIT_INI_ARRAY(&ah->iniPcieSerdes, - ar9462_2p1_pciephy_clkreq_disable_L1); - INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower, - ar9462_2p1_pciephy_clkreq_disable_L1); + + /* Awake -> Sleep Setting */ + if ((ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) && + (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_ON_D3)) { + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9462_2p1_pciephy_clkreq_disable_L1); + } + + /* Sleep -> Awake Setting */ + if ((ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) && + (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_ON_D0)) { + INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower, + ar9462_2p1_pciephy_clkreq_disable_L1); + } } else if (AR_SREV_9462_20(ah)) { INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], ar9462_2p0_mac_core); @@ -262,11 +272,18 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) ar9462_2p0_common_rx_gain); /* Awake -> Sleep Setting */ - INIT_INI_ARRAY(&ah->iniPcieSerdes, - ar9462_2p0_pciephy_clkreq_disable_L1); + if ((ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) && + (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_ON_D3)) { + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9462_2p0_pciephy_clkreq_disable_L1); + } + /* Sleep -> Awake Setting */ - INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower, - ar9462_2p0_pciephy_clkreq_disable_L1); + if ((ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) && + (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_ON_D0)) { + INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower, + ar9462_2p0_pciephy_clkreq_disable_L1); + } /* Fast clock modal settings */ INIT_INI_ARRAY(&ah->iniModesFastClock, @@ -456,10 +473,19 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9565_1p1_Modes_lowest_ob_db_tx_gain_table); - INIT_INI_ARRAY(&ah->iniPcieSerdes, - ar9565_1p1_pciephy_clkreq_disable_L1); - INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower, - ar9565_1p1_pciephy_clkreq_disable_L1); + /* Awake -> Sleep Setting */ + if ((ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) && + (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_ON_D3)) { + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9565_1p1_pciephy_clkreq_disable_L1); + } + + /* Sleep -> Awake Setting */ + if ((ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) && + (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_ON_D0)) { + INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower, + ar9565_1p1_pciephy_clkreq_disable_L1); + } INIT_INI_ARRAY(&ah->iniModesFastClock, ar9565_1p1_modes_fast_clock); @@ -491,10 +517,19 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9565_1p0_Modes_lowest_ob_db_tx_gain_table); - INIT_INI_ARRAY(&ah->iniPcieSerdes, - ar9565_1p0_pciephy_clkreq_disable_L1); - INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower, - ar9565_1p0_pciephy_clkreq_disable_L1); + /* Awake -> Sleep Setting */ + if ((ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) && + (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_ON_D3)) { + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9565_1p0_pciephy_clkreq_disable_L1); + } + + /* Sleep -> Awake Setting */ + if ((ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) && + (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_ON_D0)) { + INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower, + ar9565_1p0_pciephy_clkreq_disable_L1); + } INIT_INI_ARRAY(&ah->iniModesFastClock, ar9565_1p0_modes_fast_clock); @@ -1130,6 +1165,12 @@ void ar9003_hw_attach_ops(struct ath_hw *ah) struct ath_hw_ops *ops = ath9k_hw_ops(ah); ar9003_hw_init_mode_regs(ah); + + if (AR_SREV_9003_PCOEM(ah)) { + WARN_ON(!ah->iniPcieSerdes.ia_array); + WARN_ON(!ah->iniPcieSerdesLowPower.ia_array); + } + priv_ops->init_mode_gain_regs = ar9003_hw_init_mode_gain_regs; priv_ops->init_hang_checks = ar9003_hw_init_hang_checks; priv_ops->detect_mac_hang = ar9003_hw_detect_mac_hang; @@ -1139,4 +1180,5 @@ void ar9003_hw_attach_ops(struct ath_hw *ah) ar9003_hw_attach_phy_ops(ah); ar9003_hw_attach_calib_ops(ah); ar9003_hw_attach_mac_ops(ah); + ar9003_hw_attach_aic_ops(ah); } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index bd169fae32a1..af5ee416a560 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -19,6 +19,7 @@ #include "hw-ops.h" #include "ar9003_phy.h" #include "ar9003_mci.h" +#include "ar9003_aic.h" static void ar9003_mci_reset_req_wakeup(struct ath_hw *ah) { @@ -1016,6 +1017,9 @@ int ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g, if (en_int) ar9003_mci_enable_interrupt(ah); + if (ath9k_hw_is_aic_enabled(ah)) + ar9003_aic_start_normal(ah); + return 0; } @@ -1362,6 +1366,22 @@ u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type) value = (!mci->unhalt_bt_gpm && mci->need_flush_btinfo) ? 1 : 0; mci->need_flush_btinfo = false; break; + case MCI_STATE_AIC_CAL: + if (ath9k_hw_is_aic_enabled(ah)) + value = ar9003_aic_calibration(ah); + break; + case MCI_STATE_AIC_START: + if (ath9k_hw_is_aic_enabled(ah)) + ar9003_aic_start_normal(ah); + break; + case MCI_STATE_AIC_CAL_RESET: + if (ath9k_hw_is_aic_enabled(ah)) + value = ar9003_aic_cal_reset(ah); + break; + case MCI_STATE_AIC_CAL_SINGLE: + if (ath9k_hw_is_aic_enabled(ah)) + value = ar9003_aic_calibration_single(ah); + break; default: break; } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h index c311b2bfdb00..fc595b92ac56 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h @@ -640,16 +640,6 @@ #define AR_PHY_BB_THERM_ADC_4_LATEST_VOLT_VALUE 0x0000ff00 #define AR_PHY_BB_THERM_ADC_4_LATEST_VOLT_VALUE_S 8 -/* AIC Registers */ -#define AR_PHY_AIC_CTRL_0_B0 (AR_SM_BASE + 0x4b0) -#define AR_PHY_AIC_CTRL_1_B0 (AR_SM_BASE + 0x4b4) -#define AR_PHY_AIC_CTRL_2_B0 (AR_SM_BASE + 0x4b8) -#define AR_PHY_AIC_CTRL_3_B0 (AR_SM_BASE + 0x4bc) -#define AR_PHY_AIC_STAT_0_B0 (AR_SM_BASE + 0x4c4)) -#define AR_PHY_AIC_STAT_1_B0 (AR_SM_BASE + 0x4c8)) -#define AR_PHY_AIC_CTRL_4_B0 (AR_SM_BASE + 0x4c0) -#define AR_PHY_AIC_STAT_2_B0 (AR_SM_BASE + 0x4cc) - #define AR_PHY_65NM_CH0_TXRF3 0x16048 #define AR_PHY_65NM_CH0_TXRF3_CAPDIV2G 0x0000001e #define AR_PHY_65NM_CH0_TXRF3_CAPDIV2G_S 1 @@ -989,21 +979,6 @@ #define AR_PHY_TX_IQCAL_STATUS_B1 (AR_SM1_BASE + 0x48c) #define AR_PHY_TX_IQCAL_CORR_COEFF_B1(_i) (AR_SM1_BASE + 0x450 + ((_i) << 2)) -/* SM 1 AIC Registers */ - -#define AR_PHY_AIC_CTRL_0_B1 (AR_SM1_BASE + 0x4b0) -#define AR_PHY_AIC_CTRL_1_B1 (AR_SM1_BASE + 0x4b4) -#define AR_PHY_AIC_CTRL_2_B1 (AR_SM1_BASE + 0x4b8) -#define AR_PHY_AIC_STAT_0_B1 (AR_SM1_BASE + (AR_SREV_9462_10(ah) ? \ - 0x4c0 : 0x4c4)) -#define AR_PHY_AIC_STAT_1_B1 (AR_SM1_BASE + (AR_SREV_9462_10(ah) ? \ - 0x4c4 : 0x4c8)) -#define AR_PHY_AIC_CTRL_4_B1 (AR_SM1_BASE + 0x4c0) -#define AR_PHY_AIC_STAT_2_B1 (AR_SM1_BASE + 0x4cc) - -#define AR_PHY_AIC_SRAM_ADDR_B1 (AR_SM1_BASE + 0x5f0) -#define AR_PHY_AIC_SRAM_DATA_B1 (AR_SM1_BASE + 0x5f4) - #define AR_PHY_RTT_TABLE_SW_INTF_B(i) (0x384 + ((i) ? \ AR_SM1_BASE : AR_SM_BASE)) #define AR_PHY_RTT_TABLE_SW_INTF_1_B(i) (0x388 + ((i) ? \ diff --git a/drivers/net/wireless/ath/ath9k/ar9003_rtt.c b/drivers/net/wireless/ath/ath9k/ar9003_rtt.c index 934418872e8e..e4d11fa7fe8c 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_rtt.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_rtt.c @@ -106,7 +106,7 @@ void ar9003_hw_rtt_load_hist(struct ath_hw *ah) int chain, i; for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { - if (!(ah->rxchainmask & (1 << chain))) + if (!(ah->caps.rx_chainmask & (1 << chain))) continue; for (i = 0; i < MAX_RTT_TABLE_ENTRY; i++) { ar9003_hw_rtt_load_hist_entry(ah, chain, i, @@ -171,7 +171,7 @@ void ar9003_hw_rtt_fill_hist(struct ath_hw *ah) int chain, i; for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { - if (!(ah->rxchainmask & (1 << chain))) + if (!(ah->caps.rx_chainmask & (1 << chain))) continue; for (i = 0; i < MAX_RTT_TABLE_ENTRY; i++) { ah->caldata->rtt_table[chain][i] = @@ -193,7 +193,7 @@ void ar9003_hw_rtt_clear_hist(struct ath_hw *ah) int chain, i; for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { - if (!(ah->rxchainmask & (1 << chain))) + if (!(ah->caps.rx_chainmask & (1 << chain))) continue; for (i = 0; i < MAX_RTT_TABLE_ENTRY; i++) ar9003_hw_rtt_load_hist_entry(ah, chain, i, 0); diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 7e89236c0e13..a7a81b3969ce 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -184,12 +184,12 @@ struct ath_frame_info { struct ath_buf *bf; u16 framelen; s8 txq; - enum ath9k_key_type keytype; u8 keyix; u8 rtscts_rate; u8 retries : 7; u8 baw_tracked : 1; u8 tx_power; + enum ath9k_key_type keytype:2; }; struct ath_rxbuf { diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index cb366adc820b..f50a6bc5d06e 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -219,12 +219,15 @@ void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif) struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_vif *avp = (void *)vif->drv_priv; struct ath_buf *bf = avp->av_bcbuf; + struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon; ath_dbg(common, CONFIG, "Removing interface at beacon slot: %d\n", avp->av_bslot); tasklet_disable(&sc->bcon_tasklet); + cur_conf->enable_beacon &= ~BIT(avp->av_bslot); + if (bf && bf->bf_mpdu) { struct sk_buff *skb = bf->bf_mpdu; dma_unmap_single(sc->dev, bf->bf_buf_addr, @@ -521,8 +524,7 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc, } if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) { - if ((vif->type != NL80211_IFTYPE_AP) || - (sc->nbcnvifs > 1)) { + if (vif->type != NL80211_IFTYPE_AP) { ath_dbg(common, CONFIG, "An AP interface is already present !\n"); return false; @@ -616,12 +618,14 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif, * enabling/disabling SWBA. */ if (changed & BSS_CHANGED_BEACON_ENABLED) { - if (!bss_conf->enable_beacon && - (sc->nbcnvifs <= 1)) { - cur_conf->enable_beacon = false; - } else if (bss_conf->enable_beacon) { - cur_conf->enable_beacon = true; - ath9k_cache_beacon_config(sc, ctx, bss_conf); + bool enabled = cur_conf->enable_beacon; + + if (!bss_conf->enable_beacon) { + cur_conf->enable_beacon &= ~BIT(avp->av_bslot); + } else { + cur_conf->enable_beacon |= BIT(avp->av_bslot); + if (!enabled) + ath9k_cache_beacon_config(sc, ctx, bss_conf); } } diff --git a/drivers/net/wireless/ath/ath9k/btcoex.h b/drivers/net/wireless/ath/ath9k/btcoex.h index 5fe62ff2223b..cd2f0a2373cb 100644 --- a/drivers/net/wireless/ath/ath9k/btcoex.h +++ b/drivers/net/wireless/ath/ath9k/btcoex.h @@ -44,6 +44,9 @@ #define AR9300_NUM_BT_WEIGHTS 4 #define AR9300_NUM_WLAN_WEIGHTS 4 + +#define ATH_AIC_MAX_BT_CHANNEL 79 + /* Defines the BT AR_BT_COEX_WGHT used */ enum ath_stomp_type { ATH_BTCOEX_STOMP_ALL, @@ -93,9 +96,18 @@ struct ath9k_hw_mci { u32 last_recovery; }; +struct ath9k_hw_aic { + bool aic_enabled; + u8 aic_cal_state; + u8 aic_caled_chan; + u32 aic_sram[ATH_AIC_MAX_BT_CHANNEL]; + u32 aic_cal_start_time; +}; + struct ath_btcoex_hw { enum ath_btcoex_scheme scheme; struct ath9k_hw_mci mci; + struct ath9k_hw_aic aic; bool enabled; u8 wlanactive_gpio; u8 btactive_gpio; diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index e200a6e3aca5..3e2e24e4843f 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -238,7 +238,6 @@ int ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) { struct ath9k_nfcal_hist *h = NULL; unsigned i, j; - int32_t val; u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask; struct ath_common *common = ath9k_hw_common(ah); s16 default_nf = ath9k_hw_get_default_nf(ah, chan); @@ -246,6 +245,7 @@ int ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) if (ah->caldata) h = ah->caldata->nfCalHist; + ENABLE_REG_RMW_BUFFER(ah); for (i = 0; i < NUM_NF_READINGS; i++) { if (chainmask & (1 << i)) { s16 nfval; @@ -258,10 +258,8 @@ int ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) else nfval = default_nf; - val = REG_READ(ah, ah->nf_regs[i]); - val &= 0xFFFFFE00; - val |= (((u32) nfval << 1) & 0x1ff); - REG_WRITE(ah, ah->nf_regs[i], val); + REG_RMW(ah, ah->nf_regs[i], + (((u32) nfval << 1) & 0x1ff), 0x1ff); } } @@ -274,6 +272,7 @@ int ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF); REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); + REG_RMW_BUFFER_FLUSH(ah); /* * Wait for load to complete, should be fast, a few 10s of us. @@ -309,19 +308,17 @@ int ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) * by the median we just loaded. This will be initial (and max) value * of next noise floor calibration the baseband does. */ - ENABLE_REGWRITE_BUFFER(ah); + ENABLE_REG_RMW_BUFFER(ah); for (i = 0; i < NUM_NF_READINGS; i++) { if (chainmask & (1 << i)) { if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan)) continue; - val = REG_READ(ah, ah->nf_regs[i]); - val &= 0xFFFFFE00; - val |= (((u32) (-50) << 1) & 0x1ff); - REG_WRITE(ah, ah->nf_regs[i], val); + REG_RMW(ah, ah->nf_regs[i], + (((u32) (-50) << 1) & 0x1ff), 0x1ff); } } - REGWRITE_BUFFER_FLUSH(ah); + REG_RMW_BUFFER_FLUSH(ah); return 0; } diff --git a/drivers/net/wireless/ath/ath9k/common.h b/drivers/net/wireless/ath/ath9k/common.h index 2b79a568e803..d23737342f4f 100644 --- a/drivers/net/wireless/ath/ath9k/common.h +++ b/drivers/net/wireless/ath/ath9k/common.h @@ -54,7 +54,7 @@ struct ath_beacon_config { u16 dtim_period; u16 bmiss_timeout; u8 dtim_count; - bool enable_beacon; + u8 enable_beacon; bool ibss_creator; u32 nexttbtt; u32 intval; diff --git a/drivers/net/wireless/ath/ath9k/dfs.c b/drivers/net/wireless/ath/ath9k/dfs.c index 726271c7c330..e98a9eaba7ff 100644 --- a/drivers/net/wireless/ath/ath9k/dfs.c +++ b/drivers/net/wireless/ath/ath9k/dfs.c @@ -126,8 +126,19 @@ ath9k_postprocess_radar_event(struct ath_softc *sc, DFS_STAT_INC(sc, pulses_detected); return true; } -#undef PRI_CH_RADAR_FOUND -#undef EXT_CH_RADAR_FOUND + +static void +ath9k_dfs_process_radar_pulse(struct ath_softc *sc, struct pulse_event *pe) +{ + struct dfs_pattern_detector *pd = sc->dfs_detector; + DFS_STAT_INC(sc, pulses_processed); + if (pd == NULL) + return; + if (!pd->add_pulse(pd, pe)) + return; + DFS_STAT_INC(sc, radar_detected); + ieee80211_radar_detected(sc->hw); +} /* * DFS: check PHY-error for radar pulse and feed the detector @@ -176,18 +187,21 @@ void ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data, ard.pulse_length_pri = vdata_end[-3]; pe.freq = ah->curchan->channel; pe.ts = mactime; - if (ath9k_postprocess_radar_event(sc, &ard, &pe)) { - struct dfs_pattern_detector *pd = sc->dfs_detector; - ath_dbg(common, DFS, - "ath9k_dfs_process_phyerr: channel=%d, ts=%llu, " - "width=%d, rssi=%d, delta_ts=%llu\n", - pe.freq, pe.ts, pe.width, pe.rssi, - pe.ts - sc->dfs_prev_pulse_ts); - sc->dfs_prev_pulse_ts = pe.ts; - DFS_STAT_INC(sc, pulses_processed); - if (pd != NULL && pd->add_pulse(pd, &pe)) { - DFS_STAT_INC(sc, radar_detected); - ieee80211_radar_detected(sc->hw); - } + if (!ath9k_postprocess_radar_event(sc, &ard, &pe)) + return; + + ath_dbg(common, DFS, + "ath9k_dfs_process_phyerr: type=%d, freq=%d, ts=%llu, " + "width=%d, rssi=%d, delta_ts=%llu\n", + ard.pulse_bw_info, pe.freq, pe.ts, pe.width, pe.rssi, + pe.ts - sc->dfs_prev_pulse_ts); + sc->dfs_prev_pulse_ts = pe.ts; + if (ard.pulse_bw_info & PRI_CH_RADAR_FOUND) + ath9k_dfs_process_radar_pulse(sc, &pe); + if (ard.pulse_bw_info & EXT_CH_RADAR_FOUND) { + pe.freq += IS_CHAN_HT40PLUS(ah->curchan) ? 20 : -20; + ath9k_dfs_process_radar_pulse(sc, &pe); } } +#undef PRI_CH_RADAR_FOUND +#undef EXT_CH_RADAR_FOUND diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c index 971d770722cf..cc81482c934d 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.c +++ b/drivers/net/wireless/ath/ath9k/eeprom.c @@ -27,12 +27,7 @@ void ath9k_hw_analog_shift_regwrite(struct ath_hw *ah, u32 reg, u32 val) void ath9k_hw_analog_shift_rmw(struct ath_hw *ah, u32 reg, u32 mask, u32 shift, u32 val) { - u32 regVal; - - regVal = REG_READ(ah, reg) & ~mask; - regVal |= (val << shift) & mask; - - REG_WRITE(ah, reg, regVal); + REG_RMW(ah, reg, ((val << shift) & mask), mask); if (ah->config.analog_shiftreg) udelay(100); diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c index e5a78d4fd66e..4773da6dc6f2 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c @@ -389,6 +389,7 @@ static void ath9k_hw_set_4k_power_cal_table(struct ath_hw *ah, } } + ENABLE_REG_RMW_BUFFER(ah); REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_NUM_PD_GAIN, (numXpdGain - 1) & 0x3); REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_1, @@ -396,6 +397,7 @@ static void ath9k_hw_set_4k_power_cal_table(struct ath_hw *ah, REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_2, xpdGainValues[1]); REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_3, 0); + REG_RMW_BUFFER_FLUSH(ah); for (i = 0; i < AR5416_EEP4K_MAX_CHAINS; i++) { regChainOffset = i * 0x1000; @@ -770,15 +772,14 @@ static void ath9k_hw_4k_set_gain(struct ath_hw *ah, struct ar5416_eeprom_4k *eep, u8 txRxAttenLocal) { - REG_WRITE(ah, AR_PHY_SWITCH_CHAIN_0, - pModal->antCtrlChain[0]); + ENABLE_REG_RMW_BUFFER(ah); + REG_RMW(ah, AR_PHY_SWITCH_CHAIN_0, + pModal->antCtrlChain[0], 0); - REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0), - (REG_READ(ah, AR_PHY_TIMING_CTRL4(0)) & - ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF | - AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) | - SM(pModal->iqCalICh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) | - SM(pModal->iqCalQCh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF)); + REG_RMW(ah, AR_PHY_TIMING_CTRL4(0), + SM(pModal->iqCalICh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) | + SM(pModal->iqCalQCh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF), + AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF | AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF); if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= AR5416_EEP_MINOR_VER_3) { @@ -817,6 +818,7 @@ static void ath9k_hw_4k_set_gain(struct ath_hw *ah, AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal); REG_RMW_FIELD(ah, AR_PHY_RXGAIN + 0x1000, AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[0]); + REG_RMW_BUFFER_FLUSH(ah); } /* @@ -928,6 +930,7 @@ static void ath9k_hw_4k_set_board_values(struct ath_hw *ah, } } + ENABLE_REG_RMW_BUFFER(ah); if (AR_SREV_9271(ah)) { ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, @@ -1032,18 +1035,19 @@ static void ath9k_hw_4k_set_board_values(struct ath_hw *ah, AR9285_AN_RF2G4_DB2_4_S, db2[4]); } + REG_RMW_BUFFER_FLUSH(ah); - + ENABLE_REG_RMW_BUFFER(ah); REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH, pModal->switchSettling); REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_ADC, pModal->adcDesiredSize); - REG_WRITE(ah, AR_PHY_RF_CTL4, - SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAA_OFF) | - SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAB_OFF) | - SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAA_ON) | - SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAB_ON)); + REG_RMW(ah, AR_PHY_RF_CTL4, + SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAA_OFF) | + SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAB_OFF) | + SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAA_ON) | + SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAB_ON), 0); REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_A2_RX_ON, pModal->txEndToRxOn); @@ -1072,6 +1076,8 @@ static void ath9k_hw_4k_set_board_values(struct ath_hw *ah, pModal->swSettleHt40); } + REG_RMW_BUFFER_FLUSH(ah); + bb_desired_scale = (pModal->bb_scale_smrt_antenna & EEP_4K_BB_DESIRED_SCALE_MASK); if ((pBase->txGainType == 0) && (bb_desired_scale != 0)) { @@ -1080,6 +1086,7 @@ static void ath9k_hw_4k_set_board_values(struct ath_hw *ah, mask = BIT(0)|BIT(5)|BIT(10)|BIT(15)|BIT(20)|BIT(25); pwrctrl = mask * bb_desired_scale; clr = mask * 0x1f; + ENABLE_REG_RMW_BUFFER(ah); REG_RMW(ah, AR_PHY_TX_PWRCTRL8, pwrctrl, clr); REG_RMW(ah, AR_PHY_TX_PWRCTRL10, pwrctrl, clr); REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL12, pwrctrl, clr); @@ -1094,6 +1101,7 @@ static void ath9k_hw_4k_set_board_values(struct ath_hw *ah, clr = mask * 0x1f; REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL11, pwrctrl, clr); REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL13, pwrctrl, clr); + REG_RMW_BUFFER_FLUSH(ah); } } diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c index 098059039351..056f516bf017 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_def.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c @@ -466,6 +466,7 @@ static void ath9k_hw_def_set_gain(struct ath_hw *ah, struct ar5416_eeprom_def *eep, u8 txRxAttenLocal, int regChainOffset, int i) { + ENABLE_REG_RMW_BUFFER(ah); if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_3) { txRxAttenLocal = pModal->txRxAttenCh[i]; @@ -483,16 +484,12 @@ static void ath9k_hw_def_set_gain(struct ath_hw *ah, AR_PHY_GAIN_2GHZ_XATTEN2_DB, pModal->xatten2Db[i]); } else { - REG_WRITE(ah, AR_PHY_GAIN_2GHZ + regChainOffset, - (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) & - ~AR_PHY_GAIN_2GHZ_BSW_MARGIN) - | SM(pModal-> bswMargin[i], - AR_PHY_GAIN_2GHZ_BSW_MARGIN)); - REG_WRITE(ah, AR_PHY_GAIN_2GHZ + regChainOffset, - (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) & - ~AR_PHY_GAIN_2GHZ_BSW_ATTEN) - | SM(pModal->bswAtten[i], - AR_PHY_GAIN_2GHZ_BSW_ATTEN)); + REG_RMW(ah, AR_PHY_GAIN_2GHZ + regChainOffset, + SM(pModal-> bswMargin[i], AR_PHY_GAIN_2GHZ_BSW_MARGIN), + AR_PHY_GAIN_2GHZ_BSW_MARGIN); + REG_RMW(ah, AR_PHY_GAIN_2GHZ + regChainOffset, + SM(pModal->bswAtten[i], AR_PHY_GAIN_2GHZ_BSW_ATTEN), + AR_PHY_GAIN_2GHZ_BSW_ATTEN); } } @@ -504,17 +501,14 @@ static void ath9k_hw_def_set_gain(struct ath_hw *ah, AR_PHY_RXGAIN + regChainOffset, AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[i]); } else { - REG_WRITE(ah, - AR_PHY_RXGAIN + regChainOffset, - (REG_READ(ah, AR_PHY_RXGAIN + regChainOffset) & - ~AR_PHY_RXGAIN_TXRX_ATTEN) - | SM(txRxAttenLocal, AR_PHY_RXGAIN_TXRX_ATTEN)); - REG_WRITE(ah, - AR_PHY_GAIN_2GHZ + regChainOffset, - (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) & - ~AR_PHY_GAIN_2GHZ_RXTX_MARGIN) | - SM(pModal->rxTxMarginCh[i], AR_PHY_GAIN_2GHZ_RXTX_MARGIN)); + REG_RMW(ah, AR_PHY_RXGAIN + regChainOffset, + SM(txRxAttenLocal, AR_PHY_RXGAIN_TXRX_ATTEN), + AR_PHY_RXGAIN_TXRX_ATTEN); + REG_RMW(ah, AR_PHY_GAIN_2GHZ + regChainOffset, + SM(pModal->rxTxMarginCh[i], AR_PHY_GAIN_2GHZ_RXTX_MARGIN), + AR_PHY_GAIN_2GHZ_RXTX_MARGIN); } + REG_RMW_BUFFER_FLUSH(ah); } static void ath9k_hw_def_set_board_values(struct ath_hw *ah, diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h index 300d3671d0ef..e82a0d4ce23f 100644 --- a/drivers/net/wireless/ath/ath9k/htc.h +++ b/drivers/net/wireless/ath/ath9k/htc.h @@ -444,6 +444,10 @@ static inline void ath9k_htc_stop_btcoex(struct ath9k_htc_priv *priv) #define OP_BT_SCAN BIT(4) #define OP_TSF_RESET BIT(6) +enum htc_op_flags { + HTC_FWFLAG_NO_RMW, +}; + struct ath9k_htc_priv { struct device *dev; struct ieee80211_hw *hw; @@ -482,6 +486,7 @@ struct ath9k_htc_priv { bool reconfig_beacon; unsigned int rxfilter; unsigned long op_flags; + unsigned long fw_flags; struct ath9k_hw_cal_data caldata; struct ath_spec_scan_priv spec_priv; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index fd229409f676..d7beefe60683 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -376,17 +376,139 @@ static void ath9k_regwrite_flush(void *hw_priv) mutex_unlock(&priv->wmi->multi_write_mutex); } -static u32 ath9k_reg_rmw(void *hw_priv, u32 reg_offset, u32 set, u32 clr) +static void ath9k_reg_rmw_buffer(void *hw_priv, + u32 reg_offset, u32 set, u32 clr) +{ + struct ath_hw *ah = (struct ath_hw *) hw_priv; + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; + u32 rsp_status; + int r; + + mutex_lock(&priv->wmi->multi_rmw_mutex); + + /* Store the register/value */ + priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].reg = + cpu_to_be32(reg_offset); + priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].set = + cpu_to_be32(set); + priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].clr = + cpu_to_be32(clr); + + priv->wmi->multi_rmw_idx++; + + /* If the buffer is full, send it out. */ + if (priv->wmi->multi_rmw_idx == MAX_RMW_CMD_NUMBER) { + r = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID, + (u8 *) &priv->wmi->multi_rmw, + sizeof(struct register_write) * priv->wmi->multi_rmw_idx, + (u8 *) &rsp_status, sizeof(rsp_status), + 100); + if (unlikely(r)) { + ath_dbg(common, WMI, + "REGISTER RMW FAILED, multi len: %d\n", + priv->wmi->multi_rmw_idx); + } + priv->wmi->multi_rmw_idx = 0; + } + + mutex_unlock(&priv->wmi->multi_rmw_mutex); +} + +static void ath9k_reg_rmw_flush(void *hw_priv) { - u32 val; + struct ath_hw *ah = (struct ath_hw *) hw_priv; + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; + u32 rsp_status; + int r; + + if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags)) + return; + + atomic_dec(&priv->wmi->m_rmw_cnt); - val = ath9k_regread(hw_priv, reg_offset); - val &= ~clr; - val |= set; - ath9k_regwrite(hw_priv, val, reg_offset); + mutex_lock(&priv->wmi->multi_rmw_mutex); + + if (priv->wmi->multi_rmw_idx) { + r = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID, + (u8 *) &priv->wmi->multi_rmw, + sizeof(struct register_rmw) * priv->wmi->multi_rmw_idx, + (u8 *) &rsp_status, sizeof(rsp_status), + 100); + if (unlikely(r)) { + ath_dbg(common, WMI, + "REGISTER RMW FAILED, multi len: %d\n", + priv->wmi->multi_rmw_idx); + } + priv->wmi->multi_rmw_idx = 0; + } + + mutex_unlock(&priv->wmi->multi_rmw_mutex); +} + +static void ath9k_enable_rmw_buffer(void *hw_priv) +{ + struct ath_hw *ah = (struct ath_hw *) hw_priv; + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; + + if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags)) + return; + + atomic_inc(&priv->wmi->m_rmw_cnt); +} + +static u32 ath9k_reg_rmw_single(void *hw_priv, + u32 reg_offset, u32 set, u32 clr) +{ + struct ath_hw *ah = (struct ath_hw *) hw_priv; + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; + struct register_rmw buf, buf_ret; + int ret; + u32 val = 0; + + buf.reg = cpu_to_be32(reg_offset); + buf.set = cpu_to_be32(set); + buf.clr = cpu_to_be32(clr); + + ret = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID, + (u8 *) &buf, sizeof(buf), + (u8 *) &buf_ret, sizeof(buf_ret), + 100); + if (unlikely(ret)) { + ath_dbg(common, WMI, "REGISTER RMW FAILED:(0x%04x, %d)\n", + reg_offset, ret); + } return val; } +static u32 ath9k_reg_rmw(void *hw_priv, u32 reg_offset, u32 set, u32 clr) +{ + struct ath_hw *ah = (struct ath_hw *) hw_priv; + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; + + if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags)) { + u32 val; + + val = REG_READ(ah, reg_offset); + val &= ~clr; + val |= set; + REG_WRITE(ah, reg_offset, val); + + return 0; + } + + if (atomic_read(&priv->wmi->m_rmw_cnt)) + ath9k_reg_rmw_buffer(hw_priv, reg_offset, set, clr); + else + ath9k_reg_rmw_single(hw_priv, reg_offset, set, clr); + + return 0; +} + static void ath_usb_read_cachesize(struct ath_common *common, int *csz) { *csz = L1_CACHE_BYTES >> 2; @@ -501,6 +623,8 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv, ah->reg_ops.write = ath9k_regwrite; ah->reg_ops.enable_write_buffer = ath9k_enable_regwrite_buffer; ah->reg_ops.write_flush = ath9k_regwrite_flush; + ah->reg_ops.enable_rmw_buffer = ath9k_enable_rmw_buffer; + ah->reg_ops.rmw_flush = ath9k_reg_rmw_flush; ah->reg_ops.rmw = ath9k_reg_rmw; priv->ah = ah; @@ -686,6 +810,12 @@ static int ath9k_init_firmware_version(struct ath9k_htc_priv *priv) return -EINVAL; } + if (priv->fw_version_major == 1 && priv->fw_version_minor < 4) + set_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags); + + dev_info(priv->dev, "FW RMW support: %s\n", + test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags) ? "Off" : "On"); + return 0; } diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h index 88769b64b20b..232339b05540 100644 --- a/drivers/net/wireless/ath/ath9k/hw-ops.h +++ b/drivers/net/wireless/ath/ath9k/hw-ops.h @@ -108,6 +108,14 @@ static inline void ath9k_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable) ath9k_hw_ops(ah)->set_bt_ant_diversity(ah, enable); } +static inline bool ath9k_hw_is_aic_enabled(struct ath_hw *ah) +{ + if (ath9k_hw_private_ops(ah)->is_aic_enabled) + return ath9k_hw_private_ops(ah)->is_aic_enabled(ah); + + return false; +} + #endif /* Private hardware call ops */ diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 60aa8d71e753..5cdbdb038371 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -121,6 +121,36 @@ void ath9k_hw_write_array(struct ath_hw *ah, const struct ar5416IniArray *array, REGWRITE_BUFFER_FLUSH(ah); } +void ath9k_hw_read_array(struct ath_hw *ah, u32 array[][2], int size) +{ + u32 *tmp_reg_list, *tmp_data; + int i; + + tmp_reg_list = kmalloc(size * sizeof(u32), GFP_KERNEL); + if (!tmp_reg_list) { + dev_err(ah->dev, "%s: tmp_reg_list: alloc filed\n", __func__); + return; + } + + tmp_data = kmalloc(size * sizeof(u32), GFP_KERNEL); + if (!tmp_data) { + dev_err(ah->dev, "%s tmp_data: alloc filed\n", __func__); + goto error_tmp_data; + } + + for (i = 0; i < size; i++) + tmp_reg_list[i] = array[i][0]; + + REG_READ_MULTI(ah, tmp_reg_list, tmp_data, size); + + for (i = 0; i < size; i++) + array[i][1] = tmp_data[i]; + + kfree(tmp_data); +error_tmp_data: + kfree(tmp_reg_list); +} + u32 ath9k_hw_reverse_bits(u32 val, u32 n) { u32 retval; @@ -366,6 +396,9 @@ static void ath9k_hw_init_config(struct ath_hw *ah) ah->config.rimt_first = 700; } + if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) + ah->config.pll_pwrsave = 7; + /* * We need this for PCI devices only (Cardbus, PCI, miniPCI) * _and_ if on non-uniprocessor systems (Multiprocessor/HT). @@ -424,7 +457,7 @@ static void ath9k_hw_init_defaults(struct ath_hw *ah) ah->power_mode = ATH9K_PM_UNDEFINED; ah->htc_reset_init = true; - ah->tpc_enabled = true; + ah->tpc_enabled = false; ah->ani_function = ATH9K_ANI_ALL; if (!AR_SREV_9300_20_OR_LATER(ah)) @@ -1197,6 +1230,7 @@ static void ath9k_hw_set_operating_mode(struct ath_hw *ah, int opmode) u32 mask = AR_STA_ID1_STA_AP | AR_STA_ID1_ADHOC; u32 set = AR_STA_ID1_KSRCH_MODE; + ENABLE_REG_RMW_BUFFER(ah); switch (opmode) { case NL80211_IFTYPE_ADHOC: if (!AR_SREV_9340_13(ah)) { @@ -1218,6 +1252,7 @@ static void ath9k_hw_set_operating_mode(struct ath_hw *ah, int opmode) break; } REG_RMW(ah, AR_STA_ID1, set, mask); + REG_RMW_BUFFER_FLUSH(ah); } void ath9k_hw_get_delta_slope_vals(struct ath_hw *ah, u32 coef_scaled, @@ -1930,6 +1965,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, if (!ath9k_hw_mci_is_enabled(ah)) REG_WRITE(ah, AR_OBS, 8); + ENABLE_REG_RMW_BUFFER(ah); if (ah->config.rx_intr_mitigation) { REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, ah->config.rimt_last); REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, ah->config.rimt_first); @@ -1939,6 +1975,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, REG_RMW_FIELD(ah, AR_TIMT, AR_TIMT_LAST, 300); REG_RMW_FIELD(ah, AR_TIMT, AR_TIMT_FIRST, 750); } + REG_RMW_BUFFER_FLUSH(ah); ath9k_hw_init_bb(ah, chan); diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 29a25d92add7..92fab1a54697 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -100,6 +100,18 @@ (_ah)->reg_ops.write_flush((_ah)); \ } while (0) +#define ENABLE_REG_RMW_BUFFER(_ah) \ + do { \ + if ((_ah)->reg_ops.enable_rmw_buffer) \ + (_ah)->reg_ops.enable_rmw_buffer((_ah)); \ + } while (0) + +#define REG_RMW_BUFFER_FLUSH(_ah) \ + do { \ + if ((_ah)->reg_ops.rmw_flush) \ + (_ah)->reg_ops.rmw_flush((_ah)); \ + } while (0) + #define PR_EEP(_s, _val) \ do { \ len += scnprintf(buf + len, size - len, "%20s : %10d\n",\ @@ -126,6 +138,8 @@ #define REG_WRITE_ARRAY(iniarray, column, regWr) \ ath9k_hw_write_array(ah, iniarray, column, &(regWr)) +#define REG_READ_ARRAY(ah, array, size) \ + ath9k_hw_read_array(ah, array, size) #define AR_GPIO_OUTPUT_MUX_AS_OUTPUT 0 #define AR_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED 1 @@ -309,6 +323,12 @@ enum ath9k_hw_hang_checks { HW_MAC_HANG = BIT(5), }; +#define AR_PCIE_PLL_PWRSAVE_CONTROL BIT(0) +#define AR_PCIE_PLL_PWRSAVE_ON_D3 BIT(1) +#define AR_PCIE_PLL_PWRSAVE_ON_D0 BIT(2) +#define AR_PCIE_CDR_PWRSAVE_ON_D3 BIT(3) +#define AR_PCIE_CDR_PWRSAVE_ON_D0 BIT(4) + struct ath9k_ops_config { int dma_beacon_response_time; int sw_beacon_response_time; @@ -335,7 +355,7 @@ struct ath9k_ops_config { u32 ant_ctrl_comm2g_switch_enable; bool xatten_margin_cfg; bool alt_mingainidx; - bool no_pll_pwrsave; + u8 pll_pwrsave; bool tx_gain_buffalo; bool led_active_high; }; @@ -647,6 +667,10 @@ struct ath_hw_private_ops { /* ANI */ void (*ani_cache_ini_regs)(struct ath_hw *ah); + +#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT + bool (*is_aic_enabled)(struct ath_hw *ah); +#endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ }; /** @@ -1008,6 +1032,7 @@ void ath9k_hw_synth_delay(struct ath_hw *ah, struct ath9k_channel *chan, bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout); void ath9k_hw_write_array(struct ath_hw *ah, const struct ar5416IniArray *array, int column, unsigned int *writecnt); +void ath9k_hw_read_array(struct ath_hw *ah, u32 array[][2], int size); u32 ath9k_hw_reverse_bits(u32 val, u32 n); u16 ath9k_hw_computetxtime(struct ath_hw *ah, u8 phy, int kbps, @@ -1117,6 +1142,7 @@ void ath9k_hw_set_cts_timeout(struct ath_hw *ah, u32 us); void ath9k_hw_setslottime(struct ath_hw *ah, u32 us); #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT +void ar9003_hw_attach_aic_ops(struct ath_hw *ah); static inline bool ath9k_hw_btcoex_is_enabled(struct ath_hw *ah) { return ah->btcoex_hw.enabled; @@ -1134,6 +1160,9 @@ ath9k_hw_get_btcoex_scheme(struct ath_hw *ah) return ah->btcoex_hw.scheme; } #else +static inline void ar9003_hw_attach_aic_ops(struct ath_hw *ah) +{ +} static inline bool ath9k_hw_btcoex_is_enabled(struct ath_hw *ah) { return false; diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 6c6e88495394..f8d11efa7b0f 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -141,6 +141,16 @@ static unsigned int ath9k_ioread32(void *hw_priv, u32 reg_offset) return val; } +static void ath9k_multi_ioread32(void *hw_priv, u32 *addr, + u32 *val, u16 count) +{ + int i; + + for (i = 0; i < count; i++) + val[i] = ath9k_ioread32(hw_priv, addr[i]); +} + + static unsigned int __ath9k_reg_rmw(struct ath_softc *sc, u32 reg_offset, u32 set, u32 clr) { @@ -437,8 +447,15 @@ static void ath9k_init_pcoem_platform(struct ath_softc *sc) ath_info(common, "Enable WAR for ASPM D3/L1\n"); } + /* + * The default value of pll_pwrsave is 1. + * For certain AR9485 cards, it is set to 0. + * For AR9462, AR9565 it's set to 7. + */ + ah->config.pll_pwrsave = 1; + if (sc->driver_data & ATH9K_PCI_NO_PLL_PWRSAVE) { - ah->config.no_pll_pwrsave = true; + ah->config.pll_pwrsave = 0; ath_info(common, "Disable PLL PowerSave\n"); } @@ -530,6 +547,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, ah->hw = sc->hw; ah->hw_version.devid = devid; ah->reg_ops.read = ath9k_ioread32; + ah->reg_ops.multi_read = ath9k_multi_ioread32; ah->reg_ops.write = ath9k_iowrite32; ah->reg_ops.rmw = ath9k_reg_rmw; pCap = &ah->caps; @@ -763,7 +781,8 @@ static const struct ieee80211_iface_combination if_comb[] = { .num_different_channels = 1, .beacon_int_infra_match = true, .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | - BIT(NL80211_CHAN_WIDTH_20), + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40), } #endif }; diff --git a/drivers/net/wireless/ath/ath9k/reg_aic.h b/drivers/net/wireless/ath/ath9k/reg_aic.h new file mode 100644 index 000000000000..955147ab48a2 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/reg_aic.h @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2015 Qualcomm Atheros Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef REG_AIC_H +#define REG_AIC_H + +#define AR_SM_BASE 0xa200 +#define AR_SM1_BASE 0xb200 +#define AR_AGC_BASE 0x9e00 + +#define AR_PHY_AIC_CTRL_0_B0 (AR_SM_BASE + 0x4b0) +#define AR_PHY_AIC_CTRL_1_B0 (AR_SM_BASE + 0x4b4) +#define AR_PHY_AIC_CTRL_2_B0 (AR_SM_BASE + 0x4b8) +#define AR_PHY_AIC_CTRL_3_B0 (AR_SM_BASE + 0x4bc) +#define AR_PHY_AIC_CTRL_4_B0 (AR_SM_BASE + 0x4c0) + +#define AR_PHY_AIC_STAT_0_B0 (AR_SM_BASE + 0x4c4) +#define AR_PHY_AIC_STAT_1_B0 (AR_SM_BASE + 0x4c8) +#define AR_PHY_AIC_STAT_2_B0 (AR_SM_BASE + 0x4cc) + +#define AR_PHY_AIC_CTRL_0_B1 (AR_SM1_BASE + 0x4b0) +#define AR_PHY_AIC_CTRL_1_B1 (AR_SM1_BASE + 0x4b4) +#define AR_PHY_AIC_CTRL_4_B1 (AR_SM1_BASE + 0x4c0) + +#define AR_PHY_AIC_STAT_0_B1 (AR_SM1_BASE + 0x4c4) +#define AR_PHY_AIC_STAT_1_B1 (AR_SM1_BASE + 0x4c8) +#define AR_PHY_AIC_STAT_2_B1 (AR_SM1_BASE + 0x4cc) + +#define AR_PHY_AIC_SRAM_ADDR_B0 (AR_SM_BASE + 0x5f0) +#define AR_PHY_AIC_SRAM_DATA_B0 (AR_SM_BASE + 0x5f4) + +#define AR_PHY_AIC_SRAM_ADDR_B1 (AR_SM1_BASE + 0x5f0) +#define AR_PHY_AIC_SRAM_DATA_B1 (AR_SM1_BASE + 0x5f4) + +#define AR_PHY_BT_COEX_4 (AR_AGC_BASE + 0x60) +#define AR_PHY_BT_COEX_5 (AR_AGC_BASE + 0x64) + +/* AIC fields */ +#define AR_PHY_AIC_MON_ENABLE 0x80000000 +#define AR_PHY_AIC_MON_ENABLE_S 31 +#define AR_PHY_AIC_CAL_MAX_HOP_COUNT 0x7F000000 +#define AR_PHY_AIC_CAL_MAX_HOP_COUNT_S 24 +#define AR_PHY_AIC_CAL_MIN_VALID_COUNT 0x00FE0000 +#define AR_PHY_AIC_CAL_MIN_VALID_COUNT_S 17 +#define AR_PHY_AIC_F_WLAN 0x0001FC00 +#define AR_PHY_AIC_F_WLAN_S 10 +#define AR_PHY_AIC_CAL_CH_VALID_RESET 0x00000200 +#define AR_PHY_AIC_CAL_CH_VALID_RESET_S 9 +#define AR_PHY_AIC_CAL_ENABLE 0x00000100 +#define AR_PHY_AIC_CAL_ENABLE_S 8 +#define AR_PHY_AIC_BTTX_PWR_THR 0x000000FE +#define AR_PHY_AIC_BTTX_PWR_THR_S 1 +#define AR_PHY_AIC_ENABLE 0x00000001 +#define AR_PHY_AIC_ENABLE_S 0 +#define AR_PHY_AIC_CAL_BT_REF_DELAY 0x00F00000 +#define AR_PHY_AIC_CAL_BT_REF_DELAY_S 20 +#define AR_PHY_AIC_BT_IDLE_CFG 0x00080000 +#define AR_PHY_AIC_BT_IDLE_CFG_S 19 +#define AR_PHY_AIC_STDBY_COND 0x00060000 +#define AR_PHY_AIC_STDBY_COND_S 17 +#define AR_PHY_AIC_STDBY_ROT_ATT_DB 0x0001F800 +#define AR_PHY_AIC_STDBY_ROT_ATT_DB_S 11 +#define AR_PHY_AIC_STDBY_COM_ATT_DB 0x00000700 +#define AR_PHY_AIC_STDBY_COM_ATT_DB_S 8 +#define AR_PHY_AIC_RSSI_MAX 0x000000F0 +#define AR_PHY_AIC_RSSI_MAX_S 4 +#define AR_PHY_AIC_RSSI_MIN 0x0000000F +#define AR_PHY_AIC_RSSI_MIN_S 0 +#define AR_PHY_AIC_RADIO_DELAY 0x7F000000 +#define AR_PHY_AIC_RADIO_DELAY_S 24 +#define AR_PHY_AIC_CAL_STEP_SIZE_CORR 0x00F00000 +#define AR_PHY_AIC_CAL_STEP_SIZE_CORR_S 20 +#define AR_PHY_AIC_CAL_ROT_IDX_CORR 0x000F8000 +#define AR_PHY_AIC_CAL_ROT_IDX_CORR_S 15 +#define AR_PHY_AIC_CAL_CONV_CHECK_FACTOR 0x00006000 +#define AR_PHY_AIC_CAL_CONV_CHECK_FACTOR_S 13 +#define AR_PHY_AIC_ROT_IDX_COUNT_MAX 0x00001C00 +#define AR_PHY_AIC_ROT_IDX_COUNT_MAX_S 10 +#define AR_PHY_AIC_CAL_SYNTH_TOGGLE 0x00000200 +#define AR_PHY_AIC_CAL_SYNTH_TOGGLE_S 9 +#define AR_PHY_AIC_CAL_SYNTH_AFTER_BTRX 0x00000100 +#define AR_PHY_AIC_CAL_SYNTH_AFTER_BTRX_S 8 +#define AR_PHY_AIC_CAL_SYNTH_SETTLING 0x000000FF +#define AR_PHY_AIC_CAL_SYNTH_SETTLING_S 0 +#define AR_PHY_AIC_MON_MAX_HOP_COUNT 0x07F00000 +#define AR_PHY_AIC_MON_MAX_HOP_COUNT_S 20 +#define AR_PHY_AIC_MON_MIN_STALE_COUNT 0x000FE000 +#define AR_PHY_AIC_MON_MIN_STALE_COUNT_S 13 +#define AR_PHY_AIC_MON_PWR_EST_LONG 0x00001000 +#define AR_PHY_AIC_MON_PWR_EST_LONG_S 12 +#define AR_PHY_AIC_MON_PD_TALLY_SCALING 0x00000C00 +#define AR_PHY_AIC_MON_PD_TALLY_SCALING_S 10 +#define AR_PHY_AIC_MON_PERF_THR 0x000003E0 +#define AR_PHY_AIC_MON_PERF_THR_S 5 +#define AR_PHY_AIC_CAL_TARGET_MAG_SETTING 0x00000018 +#define AR_PHY_AIC_CAL_TARGET_MAG_SETTING_S 3 +#define AR_PHY_AIC_CAL_PERF_CHECK_FACTOR 0x00000006 +#define AR_PHY_AIC_CAL_PERF_CHECK_FACTOR_S 1 +#define AR_PHY_AIC_CAL_PWR_EST_LONG 0x00000001 +#define AR_PHY_AIC_CAL_PWR_EST_LONG_S 0 +#define AR_PHY_AIC_MON_DONE 0x80000000 +#define AR_PHY_AIC_MON_DONE_S 31 +#define AR_PHY_AIC_MON_ACTIVE 0x40000000 +#define AR_PHY_AIC_MON_ACTIVE_S 30 +#define AR_PHY_AIC_MEAS_COUNT 0x3F000000 +#define AR_PHY_AIC_MEAS_COUNT_S 24 +#define AR_PHY_AIC_CAL_ANT_ISO_EST 0x00FC0000 +#define AR_PHY_AIC_CAL_ANT_ISO_EST_S 18 +#define AR_PHY_AIC_CAL_HOP_COUNT 0x0003F800 +#define AR_PHY_AIC_CAL_HOP_COUNT_S 11 +#define AR_PHY_AIC_CAL_VALID_COUNT 0x000007F0 +#define AR_PHY_AIC_CAL_VALID_COUNT_S 4 +#define AR_PHY_AIC_CAL_BT_TOO_WEAK_ERR 0x00000008 +#define AR_PHY_AIC_CAL_BT_TOO_WEAK_ERR_S 3 +#define AR_PHY_AIC_CAL_BT_TOO_STRONG_ERR 0x00000004 +#define AR_PHY_AIC_CAL_BT_TOO_STRONG_ERR_S 2 +#define AR_PHY_AIC_CAL_DONE 0x00000002 +#define AR_PHY_AIC_CAL_DONE_S 1 +#define AR_PHY_AIC_CAL_ACTIVE 0x00000001 +#define AR_PHY_AIC_CAL_ACTIVE_S 0 + +#define AR_PHY_AIC_MEAS_MAG_MIN 0xFFC00000 +#define AR_PHY_AIC_MEAS_MAG_MIN_S 22 +#define AR_PHY_AIC_MON_STALE_COUNT 0x003F8000 +#define AR_PHY_AIC_MON_STALE_COUNT_S 15 +#define AR_PHY_AIC_MON_HOP_COUNT 0x00007F00 +#define AR_PHY_AIC_MON_HOP_COUNT_S 8 +#define AR_PHY_AIC_CAL_AIC_SM 0x000000F8 +#define AR_PHY_AIC_CAL_AIC_SM_S 3 +#define AR_PHY_AIC_SM 0x00000007 +#define AR_PHY_AIC_SM_S 0 +#define AR_PHY_AIC_SRAM_VALID 0x00000001 +#define AR_PHY_AIC_SRAM_VALID_S 0 +#define AR_PHY_AIC_SRAM_ROT_QUAD_ATT_DB 0x0000007E +#define AR_PHY_AIC_SRAM_ROT_QUAD_ATT_DB_S 1 +#define AR_PHY_AIC_SRAM_VGA_QUAD_SIGN 0x00000080 +#define AR_PHY_AIC_SRAM_VGA_QUAD_SIGN_S 7 +#define AR_PHY_AIC_SRAM_ROT_DIR_ATT_DB 0x00003F00 +#define AR_PHY_AIC_SRAM_ROT_DIR_ATT_DB_S 8 +#define AR_PHY_AIC_SRAM_VGA_DIR_SIGN 0x00004000 +#define AR_PHY_AIC_SRAM_VGA_DIR_SIGN_S 14 +#define AR_PHY_AIC_SRAM_COM_ATT_6DB 0x00038000 +#define AR_PHY_AIC_SRAM_COM_ATT_6DB_S 15 +#define AR_PHY_AIC_CAL_ROT_ATT_DB_EST_ISO 0x0000E000 +#define AR_PHY_AIC_CAL_ROT_ATT_DB_EST_ISO_S 13 +#define AR_PHY_AIC_CAL_COM_ATT_DB_EST_ISO 0x00001E00 +#define AR_PHY_AIC_CAL_COM_ATT_DB_EST_ISO_S 9 +#define AR_PHY_AIC_CAL_ISO_EST_INIT_SETTING 0x000001F8 +#define AR_PHY_AIC_CAL_ISO_EST_INIT_SETTING_S 3 +#define AR_PHY_AIC_CAL_COM_ATT_DB_BACKOFF 0x00000006 +#define AR_PHY_AIC_CAL_COM_ATT_DB_BACKOFF_S 1 +#define AR_PHY_AIC_CAL_COM_ATT_DB_FIXED 0x00000001 +#define AR_PHY_AIC_CAL_COM_ATT_DB_FIXED_S 0 + +#endif /* REG_AIC_H */ diff --git a/drivers/net/wireless/ath/ath9k/wmi.c b/drivers/net/wireless/ath/ath9k/wmi.c index 65c8894c5f81..67a2f8c88829 100644 --- a/drivers/net/wireless/ath/ath9k/wmi.c +++ b/drivers/net/wireless/ath/ath9k/wmi.c @@ -61,6 +61,8 @@ static const char *wmi_cmd_to_name(enum wmi_cmd_id wmi_cmd) return "WMI_REG_READ_CMDID"; case WMI_REG_WRITE_CMDID: return "WMI_REG_WRITE_CMDID"; + case WMI_REG_RMW_CMDID: + return "WMI_REG_RMW_CMDID"; case WMI_RC_STATE_CHANGE_CMDID: return "WMI_RC_STATE_CHANGE_CMDID"; case WMI_RC_RATE_UPDATE_CMDID: @@ -101,6 +103,7 @@ struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv) spin_lock_init(&wmi->event_lock); mutex_init(&wmi->op_mutex); mutex_init(&wmi->multi_write_mutex); + mutex_init(&wmi->multi_rmw_mutex); init_completion(&wmi->cmd_wait); INIT_LIST_HEAD(&wmi->pending_tx_events); tasklet_init(&wmi->wmi_event_tasklet, ath9k_wmi_event_tasklet, diff --git a/drivers/net/wireless/ath/ath9k/wmi.h b/drivers/net/wireless/ath/ath9k/wmi.h index 0db37f230018..aa84a335289a 100644 --- a/drivers/net/wireless/ath/ath9k/wmi.h +++ b/drivers/net/wireless/ath/ath9k/wmi.h @@ -112,6 +112,7 @@ enum wmi_cmd_id { WMI_TX_STATS_CMDID, WMI_RX_STATS_CMDID, WMI_BITRATE_MASK_CMDID, + WMI_REG_RMW_CMDID, }; enum wmi_event_id { @@ -125,12 +126,19 @@ enum wmi_event_id { }; #define MAX_CMD_NUMBER 62 +#define MAX_RMW_CMD_NUMBER 15 struct register_write { __be32 reg; __be32 val; }; +struct register_rmw { + __be32 reg; + __be32 set; + __be32 clr; +} __packed; + struct ath9k_htc_tx_event { int count; struct __wmi_event_txstatus txs; @@ -156,10 +164,18 @@ struct wmi { spinlock_t wmi_lock; + /* multi write section */ atomic_t mwrite_cnt; struct register_write multi_write[MAX_CMD_NUMBER]; u32 multi_write_idx; struct mutex multi_write_mutex; + + /* multi rmw section */ + atomic_t m_rmw_cnt; + struct register_rmw multi_rmw[MAX_RMW_CMD_NUMBER]; + u32 multi_rmw_idx; + struct mutex multi_rmw_mutex; + }; struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv); diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.c b/drivers/net/wireless/ath/dfs_pattern_detector.c index 3d57f8772389..c657ca26a71a 100644 --- a/drivers/net/wireless/ath/dfs_pattern_detector.c +++ b/drivers/net/wireless/ath/dfs_pattern_detector.c @@ -289,7 +289,7 @@ dpd_add_pulse(struct dfs_pattern_detector *dpd, struct pulse_event *event) "count=%d, count_false=%d\n", event->freq, pd->rs->type_id, ps->pri, ps->count, ps->count_falses); - channel_detector_reset(dpd, cd); + pd->reset(pd, dpd->last_pulse_ts); return true; } } diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 47d14db59b93..b97172667bc7 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -14,6 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <linux/etherdevice.h> #include "wil6210.h" #include "wmi.h" @@ -217,7 +218,7 @@ static int wil_cfg80211_dump_station(struct wiphy *wiphy, if (cid < 0) return -ENOENT; - memcpy(mac, wil->sta[cid].addr, ETH_ALEN); + ether_addr_copy(mac, wil->sta[cid].addr); wil_dbg_misc(wil, "%s(%pM) CID %d\n", __func__, mac, cid); rc = wil_cid_fill_sinfo(wil, cid, sinfo); @@ -478,8 +479,8 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, } conn.channel = ch - 1; - memcpy(conn.bssid, bss->bssid, ETH_ALEN); - memcpy(conn.dst_mac, bss->bssid, ETH_ALEN); + ether_addr_copy(conn.bssid, bss->bssid); + ether_addr_copy(conn.dst_mac, bss->bssid); set_bit(wil_status_fwconnecting, wil->status); @@ -782,8 +783,17 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy, rc = wmi_pcp_start(wil, info->beacon_interval, wmi_nettype, channel->hw_value); if (rc) - netif_carrier_off(ndev); + goto err_pcp_start; + rc = wil_bcast_init(wil); + if (rc) + goto err_bcast; + + goto out; /* success */ +err_bcast: + wmi_pcp_stop(wil); +err_pcp_start: + netif_carrier_off(ndev); out: mutex_unlock(&wil->mutex); return rc; @@ -917,6 +927,21 @@ static int wil_cfg80211_probe_client(struct wiphy *wiphy, return 0; } +static int wil_cfg80211_change_bss(struct wiphy *wiphy, + struct net_device *dev, + struct bss_parameters *params) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + + if (params->ap_isolate >= 0) { + wil_dbg_misc(wil, "%s(ap_isolate %d => %d)\n", __func__, + wil->ap_isolate, params->ap_isolate); + wil->ap_isolate = params->ap_isolate; + } + + return 0; +} + static struct cfg80211_ops wil_cfg80211_ops = { .scan = wil_cfg80211_scan, .connect = wil_cfg80211_connect, @@ -937,6 +962,7 @@ static struct cfg80211_ops wil_cfg80211_ops = { .stop_ap = wil_cfg80211_stop_ap, .del_station = wil_cfg80211_del_station, .probe_client = wil_cfg80211_probe_client, + .change_bss = wil_cfg80211_change_bss, }; static void wil_wiphy_init(struct wiphy *wiphy) diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 3830cc20d4fa..bbc22d88f78f 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -121,12 +121,18 @@ static int wil_vring_debugfs_show(struct seq_file *s, void *data) snprintf(name, sizeof(name), "tx_%2d", i); - seq_printf(s, - "\n%pM CID %d TID %d BACK([%d] %d TU A%s) [%3d|%3d] idle %s\n", - wil->sta[cid].addr, cid, tid, - txdata->agg_wsize, txdata->agg_timeout, - txdata->agg_amsdu ? "+" : "-", - used, avail, sidle); + if (cid < WIL6210_MAX_CID) + seq_printf(s, + "\n%pM CID %d TID %d BACK([%u] %u TU A%s) [%3d|%3d] idle %s\n", + wil->sta[cid].addr, cid, tid, + txdata->agg_wsize, + txdata->agg_timeout, + txdata->agg_amsdu ? "+" : "-", + used, avail, sidle); + else + seq_printf(s, + "\nBroadcast [%3d|%3d] idle %s\n", + used, avail, sidle); wil_print_vring(s, wil, name, vring, '_', 'H'); } @@ -1405,6 +1411,7 @@ static const struct dbg_off dbg_wil_off[] = { WIL_FIELD(fw_version, S_IRUGO, doff_u32), WIL_FIELD(hw_version, S_IRUGO, doff_x32), WIL_FIELD(recovery_count, S_IRUGO, doff_u32), + WIL_FIELD(ap_isolate, S_IRUGO, doff_u32), {}, }; diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index db74e811f5c4..c2a238426425 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -68,6 +68,7 @@ MODULE_PARM_DESC(mtu_max, " Max MTU value."); static uint rx_ring_order = WIL_RX_RING_SIZE_ORDER_DEFAULT; static uint tx_ring_order = WIL_TX_RING_SIZE_ORDER_DEFAULT; +static uint bcast_ring_order = WIL_BCAST_RING_SIZE_ORDER_DEFAULT; static int ring_order_set(const char *val, const struct kernel_param *kp) { @@ -216,6 +217,7 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, switch (wdev->iftype) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: + wil_bcast_fini(wil); netif_tx_stop_all_queues(ndev); netif_carrier_off(ndev); @@ -360,6 +362,35 @@ static int wil_find_free_vring(struct wil6210_priv *wil) return -EINVAL; } +int wil_bcast_init(struct wil6210_priv *wil) +{ + int ri = wil->bcast_vring, rc; + + if ((ri >= 0) && wil->vring_tx[ri].va) + return 0; + + ri = wil_find_free_vring(wil); + if (ri < 0) + return ri; + + rc = wil_vring_init_bcast(wil, ri, 1 << bcast_ring_order); + if (rc == 0) + wil->bcast_vring = ri; + + return rc; +} + +void wil_bcast_fini(struct wil6210_priv *wil) +{ + int ri = wil->bcast_vring; + + if (ri < 0) + return; + + wil->bcast_vring = -1; + wil_vring_fini_tx(wil, ri); +} + static void wil_connect_worker(struct work_struct *work) { int rc; @@ -407,6 +438,7 @@ int wil_priv_init(struct wil6210_priv *wil) init_completion(&wil->wmi_call); wil->pending_connect_cid = -1; + wil->bcast_vring = -1; setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil); setup_timer(&wil->scan_timer, wil_scan_timer_fn, (ulong)wil); @@ -656,6 +688,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) cancel_work_sync(&wil->disconnect_worker); wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false); + wil_bcast_fini(wil); /* prevent NAPI from being scheduled */ bitmap_zero(wil->status, wil_status_last); @@ -714,6 +747,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) /* init after reset */ wil->pending_connect_cid = -1; + wil->ap_isolate = 0; reinit_completion(&wil->wmi_ready); reinit_completion(&wil->wmi_call); @@ -723,6 +757,8 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) /* we just started MAC, wait for FW ready */ rc = wil_wait_for_fw_ready(wil); + if (rc == 0) /* check FW is responsive */ + rc = wmi_echo(wil); } return rc; diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index ace30c1b5c64..f2f7ea29558e 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -82,7 +82,7 @@ static int wil6210_netdev_poll_rx(struct napi_struct *napi, int budget) wil_rx_handle(wil, "a); done = budget - quota; - if (done <= 1) { /* burst ends - only one packet processed */ + if (done < budget) { napi_complete(napi); wil6210_unmask_irq_rx(wil); wil_dbg_txrx(wil, "NAPI RX complete\n"); @@ -110,7 +110,7 @@ static int wil6210_netdev_poll_tx(struct napi_struct *napi, int budget) tx_done += wil_tx_complete(wil, i); } - if (tx_done <= 1) { /* burst ends - only one packet processed */ + if (tx_done < budget) { napi_complete(napi); wil6210_unmask_irq_tx(wil); wil_dbg_txrx(wil, "NAPI TX complete\n"); diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index 25343cffe229..109986114abf 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -246,8 +246,6 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) wil6210_debugfs_init(wil); - /* check FW is alive */ - wmi_echo(wil); return 0; diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 7f2f560b8638..e8bd512d81a9 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -33,6 +33,15 @@ module_param(rtap_include_phy_info, bool, S_IRUGO); MODULE_PARM_DESC(rtap_include_phy_info, " Include PHY info in the radiotap header, default - no"); +bool rx_align_2; +module_param(rx_align_2, bool, S_IRUGO); +MODULE_PARM_DESC(rx_align_2, " align Rx buffers on 4*n+2, default - no"); + +static inline uint wil_rx_snaplen(void) +{ + return rx_align_2 ? 6 : 0; +} + static inline int wil_vring_is_empty(struct vring *vring) { return vring->swhead == vring->swtail; @@ -209,7 +218,7 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring, u32 i, int headroom) { struct device *dev = wil_to_dev(wil); - unsigned int sz = mtu_max + ETH_HLEN; + unsigned int sz = mtu_max + ETH_HLEN + wil_rx_snaplen(); struct vring_rx_desc dd, *d = ⅆ volatile struct vring_rx_desc *_d = &vring->va[i].rx; dma_addr_t pa; @@ -365,10 +374,12 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, struct vring_rx_desc *d; struct sk_buff *skb; dma_addr_t pa; - unsigned int sz = mtu_max + ETH_HLEN; + unsigned int snaplen = wil_rx_snaplen(); + unsigned int sz = mtu_max + ETH_HLEN + snaplen; u16 dmalen; u8 ftype; int cid; + int i = (int)vring->swhead; struct wil_net_stats *stats; BUILD_BUG_ON(sizeof(struct vring_rx_desc) > sizeof(skb->cb)); @@ -376,24 +387,28 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, if (unlikely(wil_vring_is_empty(vring))) return NULL; - _d = &vring->va[vring->swhead].rx; + _d = &vring->va[i].rx; if (unlikely(!(_d->dma.status & RX_DMA_STATUS_DU))) { /* it is not error, we just reached end of Rx done area */ return NULL; } - skb = vring->ctx[vring->swhead].skb; + skb = vring->ctx[i].skb; + vring->ctx[i].skb = NULL; + wil_vring_advance_head(vring, 1); + if (!skb) { + wil_err(wil, "No Rx skb at [%d]\n", i); + return NULL; + } d = wil_skb_rxdesc(skb); *d = *_d; pa = wil_desc_addr(&d->dma.addr); - vring->ctx[vring->swhead].skb = NULL; - wil_vring_advance_head(vring, 1); dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE); dmalen = le16_to_cpu(d->dma.length); - trace_wil6210_rx(vring->swhead, d); - wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", vring->swhead, dmalen); + trace_wil6210_rx(i, d); + wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", i, dmalen); wil_hex_dump_txrx("Rx ", DUMP_PREFIX_NONE, 32, 4, (const void *)d, sizeof(*d), false); @@ -433,7 +448,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, return NULL; } - if (unlikely(skb->len < ETH_HLEN)) { + if (unlikely(skb->len < ETH_HLEN + snaplen)) { wil_err(wil, "Short frame, len = %d\n", skb->len); /* TODO: process it (i.e. BAR) */ kfree_skb(skb); @@ -455,6 +470,17 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, */ } + if (snaplen) { + /* Packet layout + * +-------+-------+---------+------------+------+ + * | SA(6) | DA(6) | SNAP(6) | ETHTYPE(2) | DATA | + * +-------+-------+---------+------------+------+ + * Need to remove SNAP, shifting SA and DA forward + */ + memmove(skb->data + snaplen, skb->data, 2 * ETH_ALEN); + skb_pull(skb, snaplen); + } + return skb; } @@ -492,17 +518,71 @@ static int wil_rx_refill(struct wil6210_priv *wil, int count) */ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) { - gro_result_t rc; + gro_result_t rc = GRO_NORMAL; struct wil6210_priv *wil = ndev_to_wil(ndev); + struct wireless_dev *wdev = wil_to_wdev(wil); unsigned int len = skb->len; struct vring_rx_desc *d = wil_skb_rxdesc(skb); - int cid = wil_rxdesc_cid(d); + int cid = wil_rxdesc_cid(d); /* always 0..7, no need to check */ + struct ethhdr *eth = (void *)skb->data; + /* here looking for DA, not A1, thus Rxdesc's 'mcast' indication + * is not suitable, need to look at data + */ + int mcast = is_multicast_ether_addr(eth->h_dest); struct wil_net_stats *stats = &wil->sta[cid].stats; + struct sk_buff *xmit_skb = NULL; + static const char * const gro_res_str[] = { + [GRO_MERGED] = "GRO_MERGED", + [GRO_MERGED_FREE] = "GRO_MERGED_FREE", + [GRO_HELD] = "GRO_HELD", + [GRO_NORMAL] = "GRO_NORMAL", + [GRO_DROP] = "GRO_DROP", + }; skb_orphan(skb); - rc = napi_gro_receive(&wil->napi_rx, skb); + if (wdev->iftype == NL80211_IFTYPE_AP && !wil->ap_isolate) { + if (mcast) { + /* send multicast frames both to higher layers in + * local net stack and back to the wireless medium + */ + xmit_skb = skb_copy(skb, GFP_ATOMIC); + } else { + int xmit_cid = wil_find_cid(wil, eth->h_dest); + + if (xmit_cid >= 0) { + /* The destination station is associated to + * this AP (in this VLAN), so send the frame + * directly to it and do not pass it to local + * net stack. + */ + xmit_skb = skb; + skb = NULL; + } + } + } + if (xmit_skb) { + /* Send to wireless media and increase priority by 256 to + * keep the received priority instead of reclassifying + * the frame (see cfg80211_classify8021d). + */ + xmit_skb->dev = ndev; + xmit_skb->priority += 256; + xmit_skb->protocol = htons(ETH_P_802_3); + skb_reset_network_header(xmit_skb); + skb_reset_mac_header(xmit_skb); + wil_dbg_txrx(wil, "Rx -> Tx %d bytes\n", len); + dev_queue_xmit(xmit_skb); + } + if (skb) { /* deliver to local stack */ + + skb->protocol = eth_type_trans(skb, ndev); + rc = napi_gro_receive(&wil->napi_rx, skb); + wil_dbg_txrx(wil, "Rx complete %d bytes => %s\n", + len, gro_res_str[rc]); + } + /* statistics. rc set to GRO_NORMAL for AP bridging */ if (unlikely(rc == GRO_DROP)) { ndev->stats.rx_dropped++; stats->rx_dropped++; @@ -512,17 +592,8 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) stats->rx_packets++; ndev->stats.rx_bytes += len; stats->rx_bytes += len; - } - { - static const char * const gro_res_str[] = { - [GRO_MERGED] = "GRO_MERGED", - [GRO_MERGED_FREE] = "GRO_MERGED_FREE", - [GRO_HELD] = "GRO_HELD", - [GRO_NORMAL] = "GRO_NORMAL", - [GRO_DROP] = "GRO_DROP", - }; - wil_dbg_txrx(wil, "Rx complete %d bytes => %s\n", - len, gro_res_str[rc]); + if (mcast) + ndev->stats.multicast++; } } @@ -553,7 +624,6 @@ void wil_rx_handle(struct wil6210_priv *wil, int *quota) skb->protocol = htons(ETH_P_802_2); wil_netif_rx_any(skb, ndev); } else { - skb->protocol = eth_type_trans(skb, ndev); wil_rx_reorder(wil, skb); } } @@ -679,6 +749,72 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, return rc; } +int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size) +{ + int rc; + struct wmi_bcast_vring_cfg_cmd cmd = { + .action = cpu_to_le32(WMI_VRING_CMD_ADD), + .vring_cfg = { + .tx_sw_ring = { + .max_mpdu_size = + cpu_to_le16(wil_mtu2macbuf(mtu_max)), + .ring_size = cpu_to_le16(size), + }, + .ringid = id, + .encap_trans_type = WMI_VRING_ENC_TYPE_802_3, + }, + }; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_vring_cfg_done_event cmd; + } __packed reply; + struct vring *vring = &wil->vring_tx[id]; + struct vring_tx_data *txdata = &wil->vring_tx_data[id]; + + wil_dbg_misc(wil, "%s() max_mpdu_size %d\n", __func__, + cmd.vring_cfg.tx_sw_ring.max_mpdu_size); + + if (vring->va) { + wil_err(wil, "Tx ring [%d] already allocated\n", id); + rc = -EINVAL; + goto out; + } + + memset(txdata, 0, sizeof(*txdata)); + spin_lock_init(&txdata->lock); + vring->size = size; + rc = wil_vring_alloc(wil, vring); + if (rc) + goto out; + + wil->vring2cid_tid[id][0] = WIL6210_MAX_CID; /* CID */ + wil->vring2cid_tid[id][1] = 0; /* TID */ + + cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); + + rc = wmi_call(wil, WMI_BCAST_VRING_CFG_CMDID, &cmd, sizeof(cmd), + WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), 100); + if (rc) + goto out_free; + + if (reply.cmd.status != WMI_FW_STATUS_SUCCESS) { + wil_err(wil, "Tx config failed, status 0x%02x\n", + reply.cmd.status); + rc = -EINVAL; + goto out_free; + } + vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr); + + txdata->enabled = 1; + + return 0; + out_free: + wil_vring_free(wil, vring, 1); + out: + + return rc; +} + void wil_vring_fini_tx(struct wil6210_priv *wil, int id) { struct vring *vring = &wil->vring_tx[id]; @@ -702,7 +838,7 @@ void wil_vring_fini_tx(struct wil6210_priv *wil, int id) memset(txdata, 0, sizeof(*txdata)); } -static struct vring *wil_find_tx_vring(struct wil6210_priv *wil, +static struct vring *wil_find_tx_ucast(struct wil6210_priv *wil, struct sk_buff *skb) { int i; @@ -735,15 +871,6 @@ static struct vring *wil_find_tx_vring(struct wil6210_priv *wil, return NULL; } -static void wil_set_da_for_vring(struct wil6210_priv *wil, - struct sk_buff *skb, int vring_index) -{ - struct ethhdr *eth = (void *)skb->data; - int cid = wil->vring2cid_tid[vring_index][0]; - - memcpy(eth->h_dest, wil->sta[cid].addr, ETH_ALEN); -} - static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, struct sk_buff *skb); @@ -764,6 +891,9 @@ static struct vring *wil_find_tx_vring_sta(struct wil6210_priv *wil, continue; cid = wil->vring2cid_tid[i][0]; + if (cid >= WIL6210_MAX_CID) /* skip BCAST */ + continue; + if (!wil->sta[cid].data_port_open && (skb->protocol != cpu_to_be16(ETH_P_PAE))) break; @@ -778,17 +908,51 @@ static struct vring *wil_find_tx_vring_sta(struct wil6210_priv *wil, return NULL; } -/* - * Find 1-st vring and return it; set dest address for this vring in skb - * duplicate skb and send it to other active vrings +/* Use one of 2 strategies: + * + * 1. New (real broadcast): + * use dedicated broadcast vring + * 2. Old (pseudo-DMS): + * Find 1-st vring and return it; + * duplicate skb and send it to other active vrings; + * in all cases override dest address to unicast peer's address + * Use old strategy when new is not supported yet: + * - for PBSS + * - for secure link */ -static struct vring *wil_tx_bcast(struct wil6210_priv *wil, - struct sk_buff *skb) +static struct vring *wil_find_tx_bcast_1(struct wil6210_priv *wil, + struct sk_buff *skb) +{ + struct vring *v; + int i = wil->bcast_vring; + + if (i < 0) + return NULL; + v = &wil->vring_tx[i]; + if (!v->va) + return NULL; + + return v; +} + +static void wil_set_da_for_vring(struct wil6210_priv *wil, + struct sk_buff *skb, int vring_index) +{ + struct ethhdr *eth = (void *)skb->data; + int cid = wil->vring2cid_tid[vring_index][0]; + + ether_addr_copy(eth->h_dest, wil->sta[cid].addr); +} + +static struct vring *wil_find_tx_bcast_2(struct wil6210_priv *wil, + struct sk_buff *skb) { struct vring *v, *v2; struct sk_buff *skb2; int i; u8 cid; + struct ethhdr *eth = (void *)skb->data; + char *src = eth->h_source; /* find 1-st vring eligible for data */ for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { @@ -797,9 +961,15 @@ static struct vring *wil_tx_bcast(struct wil6210_priv *wil, continue; cid = wil->vring2cid_tid[i][0]; + if (cid >= WIL6210_MAX_CID) /* skip BCAST */ + continue; if (!wil->sta[cid].data_port_open) continue; + /* don't Tx back to source when re-routing Rx->Tx at the AP */ + if (0 == memcmp(wil->sta[cid].addr, src, ETH_ALEN)) + continue; + goto found; } @@ -817,9 +987,14 @@ found: if (!v2->va) continue; cid = wil->vring2cid_tid[i][0]; + if (cid >= WIL6210_MAX_CID) /* skip BCAST */ + continue; if (!wil->sta[cid].data_port_open) continue; + if (0 == memcmp(wil->sta[cid].addr, src, ETH_ALEN)) + continue; + skb2 = skb_copy(skb, GFP_ATOMIC); if (skb2) { wil_dbg_txrx(wil, "BCAST DUP -> ring %d\n", i); @@ -833,6 +1008,20 @@ found: return v; } +static struct vring *wil_find_tx_bcast(struct wil6210_priv *wil, + struct sk_buff *skb) +{ + struct wireless_dev *wdev = wil->wdev; + + if (wdev->iftype != NL80211_IFTYPE_AP) + return wil_find_tx_bcast_2(wil, skb); + + if (wil->privacy) + return wil_find_tx_bcast_2(wil, skb); + + return wil_find_tx_bcast_1(wil, skb); +} + static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len, int vring_index) { @@ -925,6 +1114,8 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, uint i = swhead; dma_addr_t pa; int used; + bool mcast = (vring_index == wil->bcast_vring); + uint len = skb_headlen(skb); wil_dbg_txrx(wil, "%s()\n", __func__); @@ -950,7 +1141,17 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, return -EINVAL; vring->ctx[i].mapped_as = wil_mapped_as_single; /* 1-st segment */ - wil_tx_desc_map(d, pa, skb_headlen(skb), vring_index); + wil_tx_desc_map(d, pa, len, vring_index); + if (unlikely(mcast)) { + d->mac.d[0] |= BIT(MAC_CFG_DESC_TX_0_MCS_EN_POS); /* MCS 0 */ + if (unlikely(len > WIL_BCAST_MCS0_LIMIT)) { + /* set MCS 1 */ + d->mac.d[0] |= (1 << MAC_CFG_DESC_TX_0_MCS_INDEX_POS); + /* packet mode 2 */ + d->mac.d[1] |= BIT(MAC_CFG_DESC_TX_1_PKT_MODE_EN_POS) | + (2 << MAC_CFG_DESC_TX_1_PKT_MODE_POS); + } + } /* Process TCP/UDP checksum offloading */ if (unlikely(wil_tx_desc_offload_cksum_set(wil, d, skb))) { wil_err(wil, "Tx[%2d] Failed to set cksum, drop packet\n", @@ -1056,6 +1257,7 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) { struct wil6210_priv *wil = ndev_to_wil(ndev); struct ethhdr *eth = (void *)skb->data; + bool bcast = is_multicast_ether_addr(eth->h_dest); struct vring *vring; static bool pr_once_fw; int rc; @@ -1083,10 +1285,8 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) /* in STA mode (ESS), all to same VRING */ vring = wil_find_tx_vring_sta(wil, skb); } else { /* direct communication, find matching VRING */ - if (is_unicast_ether_addr(eth->h_dest)) - vring = wil_find_tx_vring(wil, skb); - else - vring = wil_tx_bcast(wil, skb); + vring = bcast ? wil_find_tx_bcast(wil, skb) : + wil_find_tx_ucast(wil, skb); } if (unlikely(!vring)) { wil_dbg_txrx(wil, "No Tx VRING found for %pM\n", eth->h_dest); @@ -1149,7 +1349,7 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid) struct vring_tx_data *txdata = &wil->vring_tx_data[ringid]; int done = 0; int cid = wil->vring2cid_tid[ringid][0]; - struct wil_net_stats *stats = &wil->sta[cid].stats; + struct wil_net_stats *stats = NULL; volatile struct vring_tx_desc *_d; int used_before_complete; int used_new; @@ -1168,6 +1368,9 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid) used_before_complete = wil_vring_used_tx(vring); + if (cid < WIL6210_MAX_CID) + stats = &wil->sta[cid].stats; + while (!wil_vring_is_empty(vring)) { int new_swtail; struct wil_ctx *ctx = &vring->ctx[vring->swtail]; @@ -1209,12 +1412,15 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid) if (skb) { if (likely(d->dma.error == 0)) { ndev->stats.tx_packets++; - stats->tx_packets++; ndev->stats.tx_bytes += skb->len; - stats->tx_bytes += skb->len; + if (stats) { + stats->tx_packets++; + stats->tx_bytes += skb->len; + } } else { ndev->stats.tx_errors++; - stats->tx_errors++; + if (stats) + stats->tx_errors++; } wil_consume_skb(skb, d->dma.error == 0); } diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index b6e65c37d410..4310972c9e16 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -28,6 +28,7 @@ extern unsigned int mtu_max; extern unsigned short rx_ring_overflow_thrsh; extern int agg_wsize; extern u32 vring_idle_trsh; +extern bool rx_align_2; #define WIL_NAME "wil6210" #define WIL_FW_NAME "wil6210.fw" /* code */ @@ -49,6 +50,8 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1) #define WIL_TX_Q_LEN_DEFAULT (4000) #define WIL_RX_RING_SIZE_ORDER_DEFAULT (10) #define WIL_TX_RING_SIZE_ORDER_DEFAULT (10) +#define WIL_BCAST_RING_SIZE_ORDER_DEFAULT (7) +#define WIL_BCAST_MCS0_LIMIT (1024) /* limit for MCS0 frame size */ /* limit ring size in range [32..32k] */ #define WIL_RING_SIZE_ORDER_MIN (5) #define WIL_RING_SIZE_ORDER_MAX (15) @@ -542,6 +545,7 @@ struct wil6210_priv { u32 monitor_flags; u32 privacy; /* secure connection? */ int sinfo_gen; + u32 ap_isolate; /* no intra-BSS communication */ /* interrupt moderation */ u32 tx_max_burst_duration; u32 tx_interframe_timeout; @@ -593,6 +597,7 @@ struct wil6210_priv { struct vring_tx_data vring_tx_data[WIL6210_MAX_TX_RINGS]; u8 vring2cid_tid[WIL6210_MAX_TX_RINGS][2]; /* [0] - CID, [1] - TID */ struct wil_sta_info sta[WIL6210_MAX_CID]; + int bcast_vring; /* scan */ struct cfg80211_scan_request *scan_request; @@ -755,6 +760,9 @@ void wil_rx_fini(struct wil6210_priv *wil); int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, int cid, int tid); void wil_vring_fini_tx(struct wil6210_priv *wil, int id); +int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size); +int wil_bcast_init(struct wil6210_priv *wil); +void wil_bcast_fini(struct wil6210_priv *wil); netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev); int wil_tx_complete(struct wil6210_priv *wil, int ringid); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 021313524913..9fe2085be2c5 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -466,7 +466,7 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) /* FIXME FW can transmit only ucast frames to peer */ /* FIXME real ring_id instead of hard coded 0 */ - memcpy(wil->sta[evt->cid].addr, evt->bssid, ETH_ALEN); + ether_addr_copy(wil->sta[evt->cid].addr, evt->bssid); wil->sta[evt->cid].status = wil_sta_conn_pending; wil->pending_connect_cid = evt->cid; @@ -524,8 +524,8 @@ static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id, } eth = (struct ethhdr *)skb_put(skb, ETH_HLEN); - memcpy(eth->h_dest, ndev->dev_addr, ETH_ALEN); - memcpy(eth->h_source, evt->src_mac, ETH_ALEN); + ether_addr_copy(eth->h_dest, ndev->dev_addr); + ether_addr_copy(eth->h_source, evt->src_mac); eth->h_proto = cpu_to_be16(ETH_P_PAE); memcpy(skb_put(skb, eapol_len), evt->eapol, eapol_len); skb->protocol = eth_type_trans(skb, ndev); @@ -851,7 +851,7 @@ int wmi_set_mac_address(struct wil6210_priv *wil, void *addr) { struct wmi_set_mac_address_cmd cmd; - memcpy(cmd.mac, addr, ETH_ALEN); + ether_addr_copy(cmd.mac, addr); wil_dbg_wmi(wil, "Set MAC %pM\n", addr); @@ -1109,6 +1109,11 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring) */ cmd.l3_l4_ctrl |= (1 << L3_L4_CTRL_TCPIP_CHECKSUM_EN_POS); } + + if (rx_align_2) + cmd.l2_802_3_offload_ctrl |= + L2_802_3_OFFLOAD_CTRL_SNAP_KEEP_MSK; + /* typical time for secure PCP is 840ms */ rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd), WMI_CFG_RX_CHAIN_DONE_EVENTID, &evt, sizeof(evt), 2000); @@ -1157,7 +1162,8 @@ int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason) struct wmi_disconnect_sta_cmd cmd = { .disconnect_reason = cpu_to_le16(reason), }; - memcpy(cmd.dst_mac, mac, ETH_ALEN); + + ether_addr_copy(cmd.dst_mac, mac); wil_dbg_wmi(wil, "%s(%pM, reason %d)\n", __func__, mac, reason); diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h index 8a4af613e191..b29055315350 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.h +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -70,7 +70,6 @@ enum wmi_command_id { WMI_SET_UCODE_IDLE_CMDID = 0x0813, WMI_SET_WORK_MODE_CMDID = 0x0815, WMI_LO_LEAKAGE_CALIB_CMDID = 0x0816, - WMI_MARLON_R_ACTIVATE_CMDID = 0x0817, WMI_MARLON_R_READ_CMDID = 0x0818, WMI_MARLON_R_WRITE_CMDID = 0x0819, WMI_MARLON_R_TXRX_SEL_CMDID = 0x081a, @@ -80,6 +79,7 @@ enum wmi_command_id { WMI_RF_RX_TEST_CMDID = 0x081e, WMI_CFG_RX_CHAIN_CMDID = 0x0820, WMI_VRING_CFG_CMDID = 0x0821, + WMI_BCAST_VRING_CFG_CMDID = 0x0822, WMI_VRING_BA_EN_CMDID = 0x0823, WMI_VRING_BA_DIS_CMDID = 0x0824, WMI_RCP_ADDBA_RESP_CMDID = 0x0825, @@ -99,6 +99,7 @@ enum wmi_command_id { WMI_BF_TXSS_MGMT_CMDID = 0x0837, WMI_BF_SM_MGMT_CMDID = 0x0838, WMI_BF_RXSS_MGMT_CMDID = 0x0839, + WMI_BF_TRIG_CMDID = 0x083A, WMI_SET_SECTORS_CMDID = 0x0849, WMI_MAINTAIN_PAUSE_CMDID = 0x0850, WMI_MAINTAIN_RESUME_CMDID = 0x0851, @@ -596,6 +597,22 @@ struct wmi_vring_cfg_cmd { } __packed; /* + * WMI_BCAST_VRING_CFG_CMDID + */ +struct wmi_bcast_vring_cfg { + struct wmi_sw_ring_cfg tx_sw_ring; + u8 ringid; /* 0-23 vrings */ + u8 encap_trans_type; + u8 ds_cfg; /* 802.3 DS cfg */ + u8 nwifi_ds_trans_type; +} __packed; + +struct wmi_bcast_vring_cfg_cmd { + __le32 action; + struct wmi_bcast_vring_cfg vring_cfg; +} __packed; + +/* * WMI_VRING_BA_EN_CMDID */ struct wmi_vring_ba_en_cmd { @@ -687,6 +704,9 @@ struct wmi_cfg_rx_chain_cmd { #define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_POS (0) #define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_LEN (1) #define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_MSK (0x1) + #define L2_802_3_OFFLOAD_CTRL_SNAP_KEEP_POS (1) + #define L2_802_3_OFFLOAD_CTRL_SNAP_KEEP_LEN (1) + #define L2_802_3_OFFLOAD_CTRL_SNAP_KEEP_MSK (0x2) u8 l2_802_3_offload_ctrl; #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_POS (0) @@ -841,7 +861,6 @@ enum wmi_event_id { WMI_IQ_RX_CALIB_DONE_EVENTID = 0x1812, WMI_SET_WORK_MODE_DONE_EVENTID = 0x1815, WMI_LO_LEAKAGE_CALIB_DONE_EVENTID = 0x1816, - WMI_MARLON_R_ACTIVATE_DONE_EVENTID = 0x1817, WMI_MARLON_R_READ_DONE_EVENTID = 0x1818, WMI_MARLON_R_WRITE_DONE_EVENTID = 0x1819, WMI_MARLON_R_TXRX_SEL_DONE_EVENTID = 0x181a, diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index ea4843be773c..b2f9521fe551 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4866,7 +4866,7 @@ static int b43_wireless_core_init(struct b43_wldev *dev) switch (dev->dev->bus_type) { #ifdef CONFIG_B43_BCMA case B43_BUS_BCMA: - bcma_core_pci_irq_ctl(dev->dev->bdev->bus, + bcma_host_pci_irq_ctl(dev->dev->bdev->bus, dev->dev->bdev, true); bcma_host_pci_up(dev->dev->bdev->bus); break; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index c438ccdb6ed8..9b508bd3b839 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -29,6 +29,7 @@ #include <linux/mmc/host.h> #include <linux/platform_device.h> #include <linux/platform_data/brcmfmac-sdio.h> +#include <linux/pm_runtime.h> #include <linux/suspend.h> #include <linux/errno.h> #include <linux/module.h> @@ -1006,6 +1007,7 @@ static int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev) sg_free_table(&sdiodev->sgtable); sdiodev->sbwad = 0; + pm_runtime_allow(sdiodev->func[1]->card->host->parent); return 0; } @@ -1074,7 +1076,7 @@ static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) ret = -ENODEV; goto out; } - + pm_runtime_forbid(host->parent); out: if (ret) brcmf_sdiod_remove(sdiodev); @@ -1096,6 +1098,8 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = { BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43341), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43362), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4335_4339), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43430), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4345), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354), { /* end: all zeroes */ } }; @@ -1194,7 +1198,7 @@ static void brcmf_ops_sdio_remove(struct sdio_func *func) brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device); brcmf_dbg(SDIO, "Function: %d\n", func->num); - if (func->num != 1 && func->num != 2) + if (func->num != 1) return; bus_if = dev_get_drvdata(&func->dev); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index 04d2ca0d87d6..ab2fac8b2760 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -100,9 +100,6 @@ #define BCM4329_CORE_SOCRAM_BASE 0x18003000 /* ARM Cortex M3 core, ID 0x82a */ #define BCM4329_CORE_ARM_BASE 0x18002000 -#define BCM4329_RAMSIZE 0x48000 -/* bcm43143 */ -#define BCM43143_RAMSIZE 0x70000 #define CORE_SB(base, field) \ (base + SBCONFIGOFF + offsetof(struct sbconfig, field)) @@ -150,6 +147,78 @@ struct sbconfig { u32 sbidhigh; /* identification */ }; +/* bankidx and bankinfo reg defines corerev >= 8 */ +#define SOCRAM_BANKINFO_RETNTRAM_MASK 0x00010000 +#define SOCRAM_BANKINFO_SZMASK 0x0000007f +#define SOCRAM_BANKIDX_ROM_MASK 0x00000100 + +#define SOCRAM_BANKIDX_MEMTYPE_SHIFT 8 +/* socram bankinfo memtype */ +#define SOCRAM_MEMTYPE_RAM 0 +#define SOCRAM_MEMTYPE_R0M 1 +#define SOCRAM_MEMTYPE_DEVRAM 2 + +#define SOCRAM_BANKINFO_SZBASE 8192 +#define SRCI_LSS_MASK 0x00f00000 +#define SRCI_LSS_SHIFT 20 +#define SRCI_SRNB_MASK 0xf0 +#define SRCI_SRNB_SHIFT 4 +#define SRCI_SRBSZ_MASK 0xf +#define SRCI_SRBSZ_SHIFT 0 +#define SR_BSZ_BASE 14 + +struct sbsocramregs { + u32 coreinfo; + u32 bwalloc; + u32 extracoreinfo; + u32 biststat; + u32 bankidx; + u32 standbyctrl; + + u32 errlogstatus; /* rev 6 */ + u32 errlogaddr; /* rev 6 */ + /* used for patching rev 3 & 5 */ + u32 cambankidx; + u32 cambankstandbyctrl; + u32 cambankpatchctrl; + u32 cambankpatchtblbaseaddr; + u32 cambankcmdreg; + u32 cambankdatareg; + u32 cambankmaskreg; + u32 PAD[1]; + u32 bankinfo; /* corev 8 */ + u32 bankpda; + u32 PAD[14]; + u32 extmemconfig; + u32 extmemparitycsr; + u32 extmemparityerrdata; + u32 extmemparityerrcnt; + u32 extmemwrctrlandsize; + u32 PAD[84]; + u32 workaround; + u32 pwrctl; /* corerev >= 2 */ + u32 PAD[133]; + u32 sr_control; /* corerev >= 15 */ + u32 sr_status; /* corerev >= 15 */ + u32 sr_address; /* corerev >= 15 */ + u32 sr_data; /* corerev >= 15 */ +}; + +#define SOCRAMREGOFFS(_f) offsetof(struct sbsocramregs, _f) + +#define ARMCR4_CAP (0x04) +#define ARMCR4_BANKIDX (0x40) +#define ARMCR4_BANKINFO (0x44) +#define ARMCR4_BANKPDA (0x4C) + +#define ARMCR4_TCBBNB_MASK 0xf0 +#define ARMCR4_TCBBNB_SHIFT 4 +#define ARMCR4_TCBANB_MASK 0xf +#define ARMCR4_TCBANB_SHIFT 0 + +#define ARMCR4_BSZ_MASK 0x3f +#define ARMCR4_BSZ_MULT 8192 + struct brcmf_core_priv { struct brcmf_core pub; u32 wrapbase; @@ -419,13 +488,13 @@ static struct brcmf_core *brcmf_chip_add_core(struct brcmf_chip_priv *ci, return &core->pub; } -#ifdef DEBUG /* safety check for chipinfo */ static int brcmf_chip_cores_check(struct brcmf_chip_priv *ci) { struct brcmf_core_priv *core; bool need_socram = false; bool has_socram = false; + bool cpu_found = false; int idx = 1; list_for_each_entry(core, &ci->cores, list) { @@ -435,22 +504,24 @@ static int brcmf_chip_cores_check(struct brcmf_chip_priv *ci) switch (core->pub.id) { case BCMA_CORE_ARM_CM3: + cpu_found = true; need_socram = true; break; case BCMA_CORE_INTERNAL_MEM: has_socram = true; break; case BCMA_CORE_ARM_CR4: - if (ci->pub.rambase == 0) { - brcmf_err("RAM base not provided with ARM CR4 core\n"); - return -ENOMEM; - } + cpu_found = true; break; default: break; } } + if (!cpu_found) { + brcmf_err("CPU core not detected\n"); + return -ENXIO; + } /* check RAM core presence for ARM CM3 core */ if (need_socram && !has_socram) { brcmf_err("RAM core not provided with ARM CM3 core\n"); @@ -458,56 +529,164 @@ static int brcmf_chip_cores_check(struct brcmf_chip_priv *ci) } return 0; } -#else /* DEBUG */ -static inline int brcmf_chip_cores_check(struct brcmf_chip_priv *ci) + +static u32 brcmf_chip_core_read32(struct brcmf_core_priv *core, u16 reg) { - return 0; + return core->chip->ops->read32(core->chip->ctx, core->pub.base + reg); } -#endif -static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) +static void brcmf_chip_core_write32(struct brcmf_core_priv *core, + u16 reg, u32 val) { - switch (ci->pub.chip) { - case BRCM_CC_4329_CHIP_ID: - ci->pub.ramsize = BCM4329_RAMSIZE; - break; - case BRCM_CC_43143_CHIP_ID: - ci->pub.ramsize = BCM43143_RAMSIZE; - break; - case BRCM_CC_43241_CHIP_ID: - ci->pub.ramsize = 0x90000; - break; - case BRCM_CC_4330_CHIP_ID: - ci->pub.ramsize = 0x48000; - break; + core->chip->ops->write32(core->chip->ctx, core->pub.base + reg, val); +} + +static bool brcmf_chip_socram_banksize(struct brcmf_core_priv *core, u8 idx, + u32 *banksize) +{ + u32 bankinfo; + u32 bankidx = (SOCRAM_MEMTYPE_RAM << SOCRAM_BANKIDX_MEMTYPE_SHIFT); + + bankidx |= idx; + brcmf_chip_core_write32(core, SOCRAMREGOFFS(bankidx), bankidx); + bankinfo = brcmf_chip_core_read32(core, SOCRAMREGOFFS(bankinfo)); + *banksize = (bankinfo & SOCRAM_BANKINFO_SZMASK) + 1; + *banksize *= SOCRAM_BANKINFO_SZBASE; + return !!(bankinfo & SOCRAM_BANKINFO_RETNTRAM_MASK); +} + +static void brcmf_chip_socram_ramsize(struct brcmf_core_priv *sr, u32 *ramsize, + u32 *srsize) +{ + u32 coreinfo; + uint nb, banksize, lss; + bool retent; + int i; + + *ramsize = 0; + *srsize = 0; + + if (WARN_ON(sr->pub.rev < 4)) + return; + + if (!brcmf_chip_iscoreup(&sr->pub)) + brcmf_chip_resetcore(&sr->pub, 0, 0, 0); + + /* Get info for determining size */ + coreinfo = brcmf_chip_core_read32(sr, SOCRAMREGOFFS(coreinfo)); + nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; + + if ((sr->pub.rev <= 7) || (sr->pub.rev == 12)) { + banksize = (coreinfo & SRCI_SRBSZ_MASK); + lss = (coreinfo & SRCI_LSS_MASK) >> SRCI_LSS_SHIFT; + if (lss != 0) + nb--; + *ramsize = nb * (1 << (banksize + SR_BSZ_BASE)); + if (lss != 0) + *ramsize += (1 << ((lss - 1) + SR_BSZ_BASE)); + } else { + nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; + for (i = 0; i < nb; i++) { + retent = brcmf_chip_socram_banksize(sr, i, &banksize); + *ramsize += banksize; + if (retent) + *srsize += banksize; + } + } + + /* hardcoded save&restore memory sizes */ + switch (sr->chip->pub.chip) { case BRCM_CC_4334_CHIP_ID: - case BRCM_CC_43340_CHIP_ID: - ci->pub.ramsize = 0x80000; + if (sr->chip->pub.chiprev < 2) + *srsize = (32 * 1024); break; - case BRCM_CC_4335_CHIP_ID: - ci->pub.ramsize = 0xc0000; - ci->pub.rambase = 0x180000; + case BRCM_CC_43430_CHIP_ID: + /* assume sr for now as we can not check + * firmware sr capability at this point. + */ + *srsize = (64 * 1024); break; - case BRCM_CC_43362_CHIP_ID: - ci->pub.ramsize = 0x3c000; + default: break; + } +} + +/** Return the TCM-RAM size of the ARMCR4 core. */ +static u32 brcmf_chip_tcm_ramsize(struct brcmf_core_priv *cr4) +{ + u32 corecap; + u32 memsize = 0; + u32 nab; + u32 nbb; + u32 totb; + u32 bxinfo; + u32 idx; + + corecap = brcmf_chip_core_read32(cr4, ARMCR4_CAP); + + nab = (corecap & ARMCR4_TCBANB_MASK) >> ARMCR4_TCBANB_SHIFT; + nbb = (corecap & ARMCR4_TCBBNB_MASK) >> ARMCR4_TCBBNB_SHIFT; + totb = nab + nbb; + + for (idx = 0; idx < totb; idx++) { + brcmf_chip_core_write32(cr4, ARMCR4_BANKIDX, idx); + bxinfo = brcmf_chip_core_read32(cr4, ARMCR4_BANKINFO); + memsize += ((bxinfo & ARMCR4_BSZ_MASK) + 1) * ARMCR4_BSZ_MULT; + } + + return memsize; +} + +static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci) +{ + switch (ci->pub.chip) { + case BRCM_CC_4345_CHIP_ID: + return 0x198000; + case BRCM_CC_4335_CHIP_ID: case BRCM_CC_4339_CHIP_ID: case BRCM_CC_4354_CHIP_ID: case BRCM_CC_4356_CHIP_ID: case BRCM_CC_43567_CHIP_ID: case BRCM_CC_43569_CHIP_ID: case BRCM_CC_43570_CHIP_ID: - ci->pub.ramsize = 0xc0000; - ci->pub.rambase = 0x180000; - break; case BRCM_CC_43602_CHIP_ID: - ci->pub.ramsize = 0xf0000; - ci->pub.rambase = 0x180000; - break; + return 0x180000; default: brcmf_err("unknown chip: %s\n", ci->pub.name); break; } + return 0; +} + +static int brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) +{ + struct brcmf_core_priv *mem_core; + struct brcmf_core *mem; + + mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_ARM_CR4); + if (mem) { + mem_core = container_of(mem, struct brcmf_core_priv, pub); + ci->pub.ramsize = brcmf_chip_tcm_ramsize(mem_core); + ci->pub.rambase = brcmf_chip_tcm_rambase(ci); + if (!ci->pub.rambase) { + brcmf_err("RAM base not provided with ARM CR4 core\n"); + return -EINVAL; + } + } else { + mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_INTERNAL_MEM); + mem_core = container_of(mem, struct brcmf_core_priv, pub); + brcmf_chip_socram_ramsize(mem_core, &ci->pub.ramsize, + &ci->pub.srsize); + } + brcmf_dbg(INFO, "RAM: base=0x%x size=%d (0x%x) sr=%d (0x%x)\n", + ci->pub.rambase, ci->pub.ramsize, ci->pub.ramsize, + ci->pub.srsize, ci->pub.srsize); + + if (!ci->pub.ramsize) { + brcmf_err("RAM size is undetermined\n"); + return -ENOMEM; + } + return 0; } static u32 brcmf_chip_dmp_get_desc(struct brcmf_chip_priv *ci, u32 *eromaddr, @@ -660,6 +839,7 @@ static int brcmf_chip_recognition(struct brcmf_chip_priv *ci) struct brcmf_core *core; u32 regdata; u32 socitype; + int ret; /* Get CC core rev * Chipid is assume to be at offset 0 from SI_ENUM_BASE @@ -712,9 +892,13 @@ static int brcmf_chip_recognition(struct brcmf_chip_priv *ci) return -ENODEV; } - brcmf_chip_get_raminfo(ci); + ret = brcmf_chip_cores_check(ci); + if (ret) + return ret; - return brcmf_chip_cores_check(ci); + /* assure chip is passive for core access */ + brcmf_chip_set_passive(&ci->pub); + return brcmf_chip_get_raminfo(ci); } static void brcmf_chip_disable_arm(struct brcmf_chip_priv *chip, u16 id) @@ -778,12 +962,6 @@ static int brcmf_chip_setup(struct brcmf_chip_priv *chip) if (chip->ops->setup) ret = chip->ops->setup(chip->ctx, pub); - /* - * Make sure any on-chip ARM is off (in case strapping is wrong), - * or downloaded code was already running. - */ - brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CM3); - brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CR4); return ret; } @@ -799,7 +977,7 @@ struct brcmf_chip *brcmf_chip_attach(void *ctx, err = -EINVAL; if (WARN_ON(!ops->prepare)) err = -EINVAL; - if (WARN_ON(!ops->exit_dl)) + if (WARN_ON(!ops->activate)) err = -EINVAL; if (err < 0) return ERR_PTR(-EINVAL); @@ -897,9 +1075,10 @@ void brcmf_chip_resetcore(struct brcmf_core *pub, u32 prereset, u32 reset, } static void -brcmf_chip_cm3_enterdl(struct brcmf_chip_priv *chip) +brcmf_chip_cm3_set_passive(struct brcmf_chip_priv *chip) { struct brcmf_core *core; + struct brcmf_core_priv *sr; brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CM3); core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211); @@ -909,9 +1088,16 @@ brcmf_chip_cm3_enterdl(struct brcmf_chip_priv *chip) D11_BCMA_IOCTL_PHYCLOCKEN); core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_INTERNAL_MEM); brcmf_chip_resetcore(core, 0, 0, 0); + + /* disable bank #3 remap for this device */ + if (chip->pub.chip == BRCM_CC_43430_CHIP_ID) { + sr = container_of(core, struct brcmf_core_priv, pub); + brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankidx), 3); + brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankpda), 0); + } } -static bool brcmf_chip_cm3_exitdl(struct brcmf_chip_priv *chip) +static bool brcmf_chip_cm3_set_active(struct brcmf_chip_priv *chip) { struct brcmf_core *core; @@ -921,7 +1107,7 @@ static bool brcmf_chip_cm3_exitdl(struct brcmf_chip_priv *chip) return false; } - chip->ops->exit_dl(chip->ctx, &chip->pub, 0); + chip->ops->activate(chip->ctx, &chip->pub, 0); core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CM3); brcmf_chip_resetcore(core, 0, 0, 0); @@ -930,7 +1116,7 @@ static bool brcmf_chip_cm3_exitdl(struct brcmf_chip_priv *chip) } static inline void -brcmf_chip_cr4_enterdl(struct brcmf_chip_priv *chip) +brcmf_chip_cr4_set_passive(struct brcmf_chip_priv *chip) { struct brcmf_core *core; @@ -943,11 +1129,11 @@ brcmf_chip_cr4_enterdl(struct brcmf_chip_priv *chip) D11_BCMA_IOCTL_PHYCLOCKEN); } -static bool brcmf_chip_cr4_exitdl(struct brcmf_chip_priv *chip, u32 rstvec) +static bool brcmf_chip_cr4_set_active(struct brcmf_chip_priv *chip, u32 rstvec) { struct brcmf_core *core; - chip->ops->exit_dl(chip->ctx, &chip->pub, rstvec); + chip->ops->activate(chip->ctx, &chip->pub, rstvec); /* restore ARM */ core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CR4); @@ -956,7 +1142,7 @@ static bool brcmf_chip_cr4_exitdl(struct brcmf_chip_priv *chip, u32 rstvec) return true; } -void brcmf_chip_enter_download(struct brcmf_chip *pub) +void brcmf_chip_set_passive(struct brcmf_chip *pub) { struct brcmf_chip_priv *chip; struct brcmf_core *arm; @@ -966,14 +1152,14 @@ void brcmf_chip_enter_download(struct brcmf_chip *pub) chip = container_of(pub, struct brcmf_chip_priv, pub); arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4); if (arm) { - brcmf_chip_cr4_enterdl(chip); + brcmf_chip_cr4_set_passive(chip); return; } - brcmf_chip_cm3_enterdl(chip); + brcmf_chip_cm3_set_passive(chip); } -bool brcmf_chip_exit_download(struct brcmf_chip *pub, u32 rstvec) +bool brcmf_chip_set_active(struct brcmf_chip *pub, u32 rstvec) { struct brcmf_chip_priv *chip; struct brcmf_core *arm; @@ -983,9 +1169,9 @@ bool brcmf_chip_exit_download(struct brcmf_chip *pub, u32 rstvec) chip = container_of(pub, struct brcmf_chip_priv, pub); arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4); if (arm) - return brcmf_chip_cr4_exitdl(chip, rstvec); + return brcmf_chip_cr4_set_active(chip, rstvec); - return brcmf_chip_cm3_exitdl(chip); + return brcmf_chip_cm3_set_active(chip); } bool brcmf_chip_sr_capable(struct brcmf_chip *pub) @@ -1016,6 +1202,10 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub) addr = CORE_CC_REG(base, chipcontrol_data); reg = chip->ops->read32(chip->ctx, addr); return (reg & pmu_cc3_mask) != 0; + case BRCM_CC_43430_CHIP_ID: + addr = CORE_CC_REG(base, sr_control1); + reg = chip->ops->read32(chip->ctx, addr); + return reg != 0; default: addr = CORE_CC_REG(base, pmucapabilities_ext); reg = chip->ops->read32(chip->ctx, addr); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.h b/drivers/net/wireless/brcm80211/brcmfmac/chip.h index c32908da90c8..60dcb38fc77a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.h @@ -30,7 +30,8 @@ * @pmucaps: PMU capabilities. * @pmurev: PMU revision. * @rambase: RAM base address (only applicable for ARM CR4 chips). - * @ramsize: amount of RAM on chip. + * @ramsize: amount of RAM on chip including retention. + * @srsize: amount of retention RAM on chip. * @name: string representation of the chip identifier. */ struct brcmf_chip { @@ -41,6 +42,7 @@ struct brcmf_chip { u32 pmurev; u32 rambase; u32 ramsize; + u32 srsize; char name[8]; }; @@ -64,7 +66,7 @@ struct brcmf_core { * @write32: write 32-bit value over bus. * @prepare: prepare bus for core configuration. * @setup: bus-specific core setup. - * @exit_dl: exit download state. + * @active: chip becomes active. * The callback should use the provided @rstvec when non-zero. */ struct brcmf_buscore_ops { @@ -72,7 +74,7 @@ struct brcmf_buscore_ops { void (*write32)(void *ctx, u32 addr, u32 value); int (*prepare)(void *ctx); int (*setup)(void *ctx, struct brcmf_chip *chip); - void (*exit_dl)(void *ctx, struct brcmf_chip *chip, u32 rstvec); + void (*activate)(void *ctx, struct brcmf_chip *chip, u32 rstvec); }; struct brcmf_chip *brcmf_chip_attach(void *ctx, @@ -84,8 +86,8 @@ bool brcmf_chip_iscoreup(struct brcmf_core *core); void brcmf_chip_coredisable(struct brcmf_core *core, u32 prereset, u32 reset); void brcmf_chip_resetcore(struct brcmf_core *core, u32 prereset, u32 reset, u32 postreset); -void brcmf_chip_enter_download(struct brcmf_chip *ci); -bool brcmf_chip_exit_download(struct brcmf_chip *ci, u32 rstvec); +void brcmf_chip_set_passive(struct brcmf_chip *ci); +bool brcmf_chip_set_active(struct brcmf_chip *ci, u32 rstvec); bool brcmf_chip_sr_capable(struct brcmf_chip *pub); #endif /* BRCMF_AXIDMP_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c index defb7a44e0bc..7748a1ccf14f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c @@ -126,7 +126,8 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan"); if (drvr->bus_if->wowl_supported) brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl"); - brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0); + if (drvr->bus_if->chip != BRCM_CC_43362_CHIP_ID) + brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0); /* set chip related quirks */ switch (drvr->bus_if->chip) { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 6262612dec45..4ec9811f49c8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -481,10 +481,9 @@ static int brcmf_msgbuf_ioctl_resp_wait(struct brcmf_msgbuf *msgbuf) static void brcmf_msgbuf_ioctl_resp_wake(struct brcmf_msgbuf *msgbuf) { - if (waitqueue_active(&msgbuf->ioctl_resp_wait)) { - msgbuf->ctl_completed = true; + msgbuf->ctl_completed = true; + if (waitqueue_active(&msgbuf->ioctl_resp_wait)) wake_up(&msgbuf->ioctl_resp_wait); - } } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h index 77a51b8c1e12..3d513e407e3d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h @@ -17,11 +17,11 @@ #ifdef CONFIG_BRCMFMAC_PROTO_MSGBUF -#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM 20 -#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM 256 -#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE_MAX_ITEM 20 +#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM 64 +#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM 512 +#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE_MAX_ITEM 64 #define BRCMF_D2H_MSGRING_TX_COMPLETE_MAX_ITEM 1024 -#define BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM 256 +#define BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM 512 #define BRCMF_H2D_TXFLOWRING_MAX_ITEM 512 #define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_ITEMSIZE 40 diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index 61c053a729be..1831ecd0813e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -47,8 +47,6 @@ enum brcmf_pcie_state { #define BRCMF_PCIE_43602_FW_NAME "brcm/brcmfmac43602-pcie.bin" #define BRCMF_PCIE_43602_NVRAM_NAME "brcm/brcmfmac43602-pcie.txt" -#define BRCMF_PCIE_4354_FW_NAME "brcm/brcmfmac4354-pcie.bin" -#define BRCMF_PCIE_4354_NVRAM_NAME "brcm/brcmfmac4354-pcie.txt" #define BRCMF_PCIE_4356_FW_NAME "brcm/brcmfmac4356-pcie.bin" #define BRCMF_PCIE_4356_NVRAM_NAME "brcm/brcmfmac4356-pcie.txt" #define BRCMF_PCIE_43570_FW_NAME "brcm/brcmfmac43570-pcie.bin" @@ -187,8 +185,8 @@ enum brcmf_pcie_state { MODULE_FIRMWARE(BRCMF_PCIE_43602_FW_NAME); MODULE_FIRMWARE(BRCMF_PCIE_43602_NVRAM_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4354_FW_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4354_NVRAM_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4356_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4356_NVRAM_NAME); MODULE_FIRMWARE(BRCMF_PCIE_43570_FW_NAME); MODULE_FIRMWARE(BRCMF_PCIE_43570_NVRAM_NAME); @@ -509,8 +507,6 @@ static void brcmf_pcie_attach(struct brcmf_pciedev_info *devinfo) static int brcmf_pcie_enter_download_state(struct brcmf_pciedev_info *devinfo) { - brcmf_chip_enter_download(devinfo->ci); - if (devinfo->ci->chip == BRCM_CC_43602_CHIP_ID) { brcmf_pcie_select_core(devinfo, BCMA_CORE_ARM_CR4); brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKIDX, @@ -536,7 +532,7 @@ static int brcmf_pcie_exit_download_state(struct brcmf_pciedev_info *devinfo, brcmf_chip_resetcore(core, 0, 0, 0); } - return !brcmf_chip_exit_download(devinfo->ci, resetintr); + return !brcmf_chip_set_active(devinfo->ci, resetintr); } @@ -653,10 +649,9 @@ static void brcmf_pcie_bus_console_read(struct brcmf_pciedev_info *devinfo) console->log_str[console->log_idx] = ch; console->log_idx++; } - if (ch == '\n') { console->log_str[console->log_idx] = 0; - brcmf_dbg(PCIE, "CONSOLE: %s\n", console->log_str); + brcmf_dbg(PCIE, "CONSOLE: %s", console->log_str); console->log_idx = 0; } } @@ -1328,10 +1323,6 @@ static int brcmf_pcie_get_fwnames(struct brcmf_pciedev_info *devinfo) fw_name = BRCMF_PCIE_43602_FW_NAME; nvram_name = BRCMF_PCIE_43602_NVRAM_NAME; break; - case BRCM_CC_4354_CHIP_ID: - fw_name = BRCMF_PCIE_4354_FW_NAME; - nvram_name = BRCMF_PCIE_4354_NVRAM_NAME; - break; case BRCM_CC_4356_CHIP_ID: fw_name = BRCMF_PCIE_4356_FW_NAME; nvram_name = BRCMF_PCIE_4356_NVRAM_NAME; @@ -1566,8 +1557,8 @@ static int brcmf_pcie_buscoreprep(void *ctx) } -static void brcmf_pcie_buscore_exitdl(void *ctx, struct brcmf_chip *chip, - u32 rstvec) +static void brcmf_pcie_buscore_activate(void *ctx, struct brcmf_chip *chip, + u32 rstvec) { struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; @@ -1577,7 +1568,7 @@ static void brcmf_pcie_buscore_exitdl(void *ctx, struct brcmf_chip *chip, static const struct brcmf_buscore_ops brcmf_pcie_buscore_ops = { .prepare = brcmf_pcie_buscoreprep, - .exit_dl = brcmf_pcie_buscore_exitdl, + .activate = brcmf_pcie_buscore_activate, .read32 = brcmf_pcie_buscore_read32, .write32 = brcmf_pcie_buscore_write32, }; @@ -1856,7 +1847,6 @@ cleanup: PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 } static struct pci_device_id brcmf_pcie_devid_table[] = { - BRCMF_PCIE_DEVICE(BRCM_PCIE_4354_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID), diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index 257ee70feb5b..ab0c89833013 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -432,8 +432,6 @@ struct brcmf_sdio { struct brcmf_sdio_dev *sdiodev; /* sdio device handler */ struct brcmf_chip *ci; /* Chip info struct */ - u32 ramsize; /* Size of RAM in SOCRAM (bytes) */ - u32 hostintmask; /* Copy of Host Interrupt Mask */ atomic_t intstatus; /* Intstatus bits (events) pending */ atomic_t fcstate; /* State of dongle flow-control */ @@ -485,10 +483,9 @@ struct brcmf_sdio { #endif /* DEBUG */ uint clkstate; /* State of sd and backplane clock(s) */ - bool activity; /* Activity flag for clock down */ s32 idletime; /* Control for activity timeout */ - s32 idlecount; /* Activity timeout counter */ - s32 idleclock; /* How to set bus driver when idle */ + s32 idlecount; /* Activity timeout counter */ + s32 idleclock; /* How to set bus driver when idle */ bool rxflow_mode; /* Rx flow control mode */ bool rxflow; /* Is rx flow control on */ bool alp_only; /* Don't use HT clock (ALP only) */ @@ -510,7 +507,8 @@ struct brcmf_sdio { struct workqueue_struct *brcmf_wq; struct work_struct datawork; - atomic_t dpc_tskcnt; + bool dpc_triggered; + bool dpc_running; bool txoff; /* Transmit flow-controlled */ struct brcmf_sdio_count sdcnt; @@ -617,6 +615,10 @@ static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = { #define BCM43362_NVRAM_NAME "brcm/brcmfmac43362-sdio.txt" #define BCM4339_FIRMWARE_NAME "brcm/brcmfmac4339-sdio.bin" #define BCM4339_NVRAM_NAME "brcm/brcmfmac4339-sdio.txt" +#define BCM43430_FIRMWARE_NAME "brcm/brcmfmac43430-sdio.bin" +#define BCM43430_NVRAM_NAME "brcm/brcmfmac43430-sdio.txt" +#define BCM43455_FIRMWARE_NAME "brcm/brcmfmac43455-sdio.bin" +#define BCM43455_NVRAM_NAME "brcm/brcmfmac43455-sdio.txt" #define BCM4354_FIRMWARE_NAME "brcm/brcmfmac4354-sdio.bin" #define BCM4354_NVRAM_NAME "brcm/brcmfmac4354-sdio.txt" @@ -640,6 +642,10 @@ MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME); MODULE_FIRMWARE(BCM43362_NVRAM_NAME); MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4339_NVRAM_NAME); +MODULE_FIRMWARE(BCM43430_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43430_NVRAM_NAME); +MODULE_FIRMWARE(BCM43455_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43455_NVRAM_NAME); MODULE_FIRMWARE(BCM4354_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4354_NVRAM_NAME); @@ -669,6 +675,8 @@ static const struct brcmf_firmware_names brcmf_fwname_data[] = { { BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) }, { BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) }, { BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }, + { BRCM_CC_43430_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43430) }, + { BRCM_CC_4345_CHIP_ID, 0xFFFFFFC0, BRCMF_FIRMWARE_NVRAM(BCM43455) }, { BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } }; @@ -959,13 +967,8 @@ static int brcmf_sdio_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) brcmf_dbg(SDIO, "Enter\n"); /* Early exit if we're already there */ - if (bus->clkstate == target) { - if (target == CLK_AVAIL) { - brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); - bus->activity = true; - } + if (bus->clkstate == target) return 0; - } switch (target) { case CLK_AVAIL: @@ -974,8 +977,6 @@ static int brcmf_sdio_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) brcmf_sdio_sdclk(bus, true); /* Now request HT Avail on the backplane */ brcmf_sdio_htclk(bus, true, pendok); - brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); - bus->activity = true; break; case CLK_SDONLY: @@ -987,7 +988,6 @@ static int brcmf_sdio_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) else brcmf_err("request for %d -> %d\n", bus->clkstate, target); - brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); break; case CLK_NONE: @@ -996,7 +996,6 @@ static int brcmf_sdio_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) brcmf_sdio_htclk(bus, false, false); /* Now remove the SD clock */ brcmf_sdio_sdclk(bus, false); - brcmf_sdio_wd_timer(bus, 0); break; } #ifdef DEBUG @@ -1024,17 +1023,6 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) /* Going to sleep */ if (sleep) { - /* Don't sleep if something is pending */ - if (atomic_read(&bus->intstatus) || - atomic_read(&bus->ipend) > 0 || - bus->ctrl_frame_stat || - (!atomic_read(&bus->fcstate) && - brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && - data_ok(bus))) { - err = -EBUSY; - goto done; - } - clkcsr = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err); @@ -1045,11 +1033,7 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) SBSDIO_ALP_AVAIL_REQ, &err); } err = brcmf_sdio_kso_control(bus, false); - /* disable watchdog */ - if (!err) - brcmf_sdio_wd_timer(bus, 0); } else { - bus->idlecount = 0; err = brcmf_sdio_kso_control(bus, true); } if (err) { @@ -1066,6 +1050,7 @@ end: brcmf_sdio_clkctl(bus, CLK_NONE, pendok); } else { brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok); + brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); } bus->sleeping = sleep; brcmf_dbg(SDIO, "new state %s\n", @@ -1085,44 +1070,47 @@ static inline bool brcmf_sdio_valid_shared_address(u32 addr) static int brcmf_sdio_readshared(struct brcmf_sdio *bus, struct sdpcm_shared *sh) { - u32 addr; + u32 addr = 0; int rv; u32 shaddr = 0; struct sdpcm_shared_le sh_le; __le32 addr_le; - shaddr = bus->ci->rambase + bus->ramsize - 4; + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_bus_sleep(bus, false, false); /* * Read last word in socram to determine * address of sdpcm_shared structure */ - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_bus_sleep(bus, false, false); - rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4); - sdio_release_host(bus->sdiodev->func[1]); + shaddr = bus->ci->rambase + bus->ci->ramsize - 4; + if (!bus->ci->rambase && brcmf_chip_sr_capable(bus->ci)) + shaddr -= bus->ci->srsize; + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, + (u8 *)&addr_le, 4); if (rv < 0) - return rv; - - addr = le32_to_cpu(addr_le); - - brcmf_dbg(SDIO, "sdpcm_shared address 0x%08X\n", addr); + goto fail; /* * Check if addr is valid. * NVRAM length at the end of memory should have been overwritten. */ + addr = le32_to_cpu(addr_le); if (!brcmf_sdio_valid_shared_address(addr)) { - brcmf_err("invalid sdpcm_shared address 0x%08X\n", - addr); - return -EINVAL; + brcmf_err("invalid sdpcm_shared address 0x%08X\n", addr); + rv = -EINVAL; + goto fail; } + brcmf_dbg(INFO, "sdpcm_shared address 0x%08X\n", addr); + /* Read hndrte_shared structure */ rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le, sizeof(struct sdpcm_shared_le)); if (rv < 0) - return rv; + goto fail; + + sdio_release_host(bus->sdiodev->func[1]); /* Endianness */ sh->flags = le32_to_cpu(sh_le.flags); @@ -1139,8 +1127,13 @@ static int brcmf_sdio_readshared(struct brcmf_sdio *bus, sh->flags & SDPCM_SHARED_VERSION_MASK); return -EPROTO; } - return 0; + +fail: + brcmf_err("unable to obtain sdpcm_shared info: rv=%d (addr=0x%x)\n", + rv, addr); + sdio_release_host(bus->sdiodev->func[1]); + return rv; } static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus) @@ -2721,11 +2714,14 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) && data_ok(bus)) { sdio_claim_host(bus->sdiodev->func[1]); - err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf, - bus->ctrl_frame_len); + if (bus->ctrl_frame_stat) { + err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf, + bus->ctrl_frame_len); + bus->ctrl_frame_err = err; + wmb(); + bus->ctrl_frame_stat = false; + } sdio_release_host(bus->sdiodev->func[1]); - bus->ctrl_frame_err = err; - bus->ctrl_frame_stat = false; brcmf_sdio_wait_event_wakeup(bus); } /* Send queued frames (limit 1 if rx may still be pending) */ @@ -2740,12 +2736,22 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) if ((bus->sdiodev->state != BRCMF_SDIOD_DATA) || (err != 0)) { brcmf_err("failed backplane access over SDIO, halting operation\n"); atomic_set(&bus->intstatus, 0); + if (bus->ctrl_frame_stat) { + sdio_claim_host(bus->sdiodev->func[1]); + if (bus->ctrl_frame_stat) { + bus->ctrl_frame_err = -ENODEV; + wmb(); + bus->ctrl_frame_stat = false; + brcmf_sdio_wait_event_wakeup(bus); + } + sdio_release_host(bus->sdiodev->func[1]); + } } else if (atomic_read(&bus->intstatus) || atomic_read(&bus->ipend) > 0 || (!atomic_read(&bus->fcstate) && brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && data_ok(bus))) { - atomic_inc(&bus->dpc_tskcnt); + bus->dpc_triggered = true; } } @@ -2941,20 +2947,27 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) /* Send from dpc */ bus->ctrl_frame_buf = msg; bus->ctrl_frame_len = msglen; + wmb(); bus->ctrl_frame_stat = true; brcmf_sdio_trigger_dpc(bus); wait_event_interruptible_timeout(bus->ctrl_wait, !bus->ctrl_frame_stat, msecs_to_jiffies(CTL_DONE_TIMEOUT)); - - if (!bus->ctrl_frame_stat) { + ret = 0; + if (bus->ctrl_frame_stat) { + sdio_claim_host(bus->sdiodev->func[1]); + if (bus->ctrl_frame_stat) { + brcmf_dbg(SDIO, "ctrl_frame timeout\n"); + bus->ctrl_frame_stat = false; + ret = -ETIMEDOUT; + } + sdio_release_host(bus->sdiodev->func[1]); + } + if (!ret) { brcmf_dbg(SDIO, "ctrl_frame complete, err=%d\n", bus->ctrl_frame_err); + rmb(); ret = bus->ctrl_frame_err; - } else { - brcmf_dbg(SDIO, "ctrl_frame timeout\n"); - bus->ctrl_frame_stat = false; - ret = -ETIMEDOUT; } if (ret) @@ -3358,9 +3371,6 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, sdio_claim_host(bus->sdiodev->func[1]); brcmf_sdio_clkctl(bus, CLK_AVAIL, false); - /* Keep arm in reset */ - brcmf_chip_enter_download(bus->ci); - rstvec = get_unaligned_le32(fw->data); brcmf_dbg(SDIO, "firmware rstvec: %x\n", rstvec); @@ -3380,7 +3390,7 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, } /* Take arm out of reset */ - if (!brcmf_chip_exit_download(bus->ci, rstvec)) { + if (!brcmf_chip_set_active(bus->ci, rstvec)) { brcmf_err("error getting out of ARM core reset\n"); goto err; } @@ -3525,8 +3535,8 @@ done: void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus) { - if (atomic_read(&bus->dpc_tskcnt) == 0) { - atomic_inc(&bus->dpc_tskcnt); + if (!bus->dpc_triggered) { + bus->dpc_triggered = true; queue_work(bus->brcmf_wq, &bus->datawork); } } @@ -3557,11 +3567,11 @@ void brcmf_sdio_isr(struct brcmf_sdio *bus) if (!bus->intr) brcmf_err("isr w/o interrupt configured!\n"); - atomic_inc(&bus->dpc_tskcnt); + bus->dpc_triggered = true; queue_work(bus->brcmf_wq, &bus->datawork); } -static bool brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) +static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) { brcmf_dbg(TIMER, "Enter\n"); @@ -3577,7 +3587,7 @@ static bool brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) if (!bus->intr || (bus->sdcnt.intrcount == bus->sdcnt.lastintrs)) { - if (atomic_read(&bus->dpc_tskcnt) == 0) { + if (!bus->dpc_triggered) { u8 devpend; sdio_claim_host(bus->sdiodev->func[1]); @@ -3595,7 +3605,7 @@ static bool brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) bus->sdcnt.pollcnt++; atomic_set(&bus->ipend, 1); - atomic_inc(&bus->dpc_tskcnt); + bus->dpc_triggered = true; queue_work(bus->brcmf_wq, &bus->datawork); } } @@ -3622,22 +3632,25 @@ static bool brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) #endif /* DEBUG */ /* On idle timeout clear activity flag and/or turn off clock */ - if ((bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) { - if (++bus->idlecount >= bus->idletime) { - bus->idlecount = 0; - if (bus->activity) { - bus->activity = false; - brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); - } else { + if (!bus->dpc_triggered) { + rmb(); + if ((!bus->dpc_running) && (bus->idletime > 0) && + (bus->clkstate == CLK_AVAIL)) { + bus->idlecount++; + if (bus->idlecount > bus->idletime) { brcmf_dbg(SDIO, "idle\n"); sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_wd_timer(bus, 0); + bus->idlecount = 0; brcmf_sdio_bus_sleep(bus, true, false); sdio_release_host(bus->sdiodev->func[1]); } + } else { + bus->idlecount = 0; } + } else { + bus->idlecount = 0; } - - return (atomic_read(&bus->ipend) > 0); } static void brcmf_sdio_dataworker(struct work_struct *work) @@ -3645,10 +3658,14 @@ static void brcmf_sdio_dataworker(struct work_struct *work) struct brcmf_sdio *bus = container_of(work, struct brcmf_sdio, datawork); - while (atomic_read(&bus->dpc_tskcnt)) { - atomic_set(&bus->dpc_tskcnt, 0); + bus->dpc_running = true; + wmb(); + while (ACCESS_ONCE(bus->dpc_triggered)) { + bus->dpc_triggered = false; brcmf_sdio_dpc(bus); + bus->idlecount = 0; } + bus->dpc_running = false; if (brcmf_sdiod_freezing(bus->sdiodev)) { brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DOWN); brcmf_sdiod_try_freeze(bus->sdiodev); @@ -3771,8 +3788,8 @@ static int brcmf_sdio_buscoreprep(void *ctx) return 0; } -static void brcmf_sdio_buscore_exitdl(void *ctx, struct brcmf_chip *chip, - u32 rstvec) +static void brcmf_sdio_buscore_activate(void *ctx, struct brcmf_chip *chip, + u32 rstvec) { struct brcmf_sdio_dev *sdiodev = ctx; struct brcmf_core *core; @@ -3815,7 +3832,7 @@ static void brcmf_sdio_buscore_write32(void *ctx, u32 addr, u32 val) static const struct brcmf_buscore_ops brcmf_sdio_buscore_ops = { .prepare = brcmf_sdio_buscoreprep, - .exit_dl = brcmf_sdio_buscore_exitdl, + .activate = brcmf_sdio_buscore_activate, .read32 = brcmf_sdio_buscore_read32, .write32 = brcmf_sdio_buscore_write32, }; @@ -3869,13 +3886,6 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus) drivestrength = DEFAULT_SDIO_DRIVE_STRENGTH; brcmf_sdio_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength); - /* Get info on the SOCRAM cores... */ - bus->ramsize = bus->ci->ramsize; - if (!(bus->ramsize)) { - brcmf_err("failed to find SOCRAM memory!\n"); - goto fail; - } - /* Set card control so an SDIO card reset does a WLAN backplane reset */ reg_val = brcmf_sdiod_regrb(bus->sdiodev, SDIO_CCCR_BRCM_CARDCTRL, &err); @@ -4148,7 +4158,8 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) bus->watchdog_tsk = NULL; } /* Initialize DPC thread */ - atomic_set(&bus->dpc_tskcnt, 0); + bus->dpc_triggered = false; + bus->dpc_running = false; /* Assign bus interface call back */ bus->sdiodev->bus_if->dev = bus->sdiodev->dev; @@ -4243,14 +4254,14 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) if (bus->ci) { if (bus->sdiodev->state != BRCMF_SDIOD_NOMEDIUM) { sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_wd_timer(bus, 0); brcmf_sdio_clkctl(bus, CLK_AVAIL, false); /* Leave the device in state where it is - * 'quiet'. This is done by putting it in - * download_state which essentially resets - * all necessary cores. + * 'passive'. This is done by resetting all + * necessary cores. */ msleep(20); - brcmf_chip_enter_download(bus->ci); + brcmf_chip_set_passive(bus->ci); brcmf_sdio_clkctl(bus, CLK_NONE, false); sdio_release_host(bus->sdiodev->func[1]); } diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index c84af1dfc88f..369527e27689 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -4959,7 +4959,7 @@ static int brcms_b_up_prep(struct brcms_hardware *wlc_hw) * Configure pci/pcmcia here instead of in brcms_c_attach() * to allow mfg hotswap: down, hotswap (chip power cycle), up. */ - bcma_core_pci_irq_ctl(wlc_hw->d11core->bus, wlc_hw->d11core, + bcma_host_pci_irq_ctl(wlc_hw->d11core->bus, wlc_hw->d11core, true); /* diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h index 2124a17d0bfd..4efdd51af9c8 100644 --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h @@ -37,6 +37,8 @@ #define BRCM_CC_43362_CHIP_ID 43362 #define BRCM_CC_4335_CHIP_ID 0x4335 #define BRCM_CC_4339_CHIP_ID 0x4339 +#define BRCM_CC_43430_CHIP_ID 43430 +#define BRCM_CC_4345_CHIP_ID 0x4345 #define BRCM_CC_4354_CHIP_ID 0x4354 #define BRCM_CC_4356_CHIP_ID 0x4356 #define BRCM_CC_43566_CHIP_ID 43566 diff --git a/drivers/net/wireless/brcm80211/include/chipcommon.h b/drivers/net/wireless/brcm80211/include/chipcommon.h index d242333b7559..e1fd499930a0 100644 --- a/drivers/net/wireless/brcm80211/include/chipcommon.h +++ b/drivers/net/wireless/brcm80211/include/chipcommon.h @@ -183,7 +183,14 @@ struct chipcregs { u8 uart1lsr; u8 uart1msr; u8 uart1scratch; - u32 PAD[126]; + u32 PAD[62]; + + /* save/restore, corerev >= 48 */ + u32 sr_capability; /* 0x500 */ + u32 sr_control0; /* 0x504 */ + u32 sr_control1; /* 0x508 */ + u32 gpio_control; /* 0x50C */ + u32 PAD[60]; /* PMU registers (corerev >= 20) */ u32 pmucontrol; /* 0x600 */ diff --git a/drivers/net/wireless/cw1200/cw1200_spi.c b/drivers/net/wireless/cw1200/cw1200_spi.c index 964b64ab7fe3..7603546d2de3 100644 --- a/drivers/net/wireless/cw1200/cw1200_spi.c +++ b/drivers/net/wireless/cw1200/cw1200_spi.c @@ -447,7 +447,7 @@ static int cw1200_spi_disconnect(struct spi_device *func) } #ifdef CONFIG_PM -static int cw1200_spi_suspend(struct device *dev, pm_message_t state) +static int cw1200_spi_suspend(struct device *dev) { struct hwbus_priv *self = spi_get_drvdata(to_spi_device(dev)); @@ -458,10 +458,8 @@ static int cw1200_spi_suspend(struct device *dev, pm_message_t state) return 0; } -static int cw1200_spi_resume(struct device *dev) -{ - return 0; -} +static SIMPLE_DEV_PM_OPS(cw1200_pm_ops, cw1200_spi_suspend, NULL); + #endif static struct spi_driver spi_driver = { @@ -472,8 +470,7 @@ static struct spi_driver spi_driver = { .bus = &spi_bus_type, .owner = THIS_MODULE, #ifdef CONFIG_PM - .suspend = cw1200_spi_suspend, - .resume = cw1200_spi_resume, + .pm = &cw1200_pm_ops, #endif }, }; diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h index a6f22c32a279..3811878ab9cd 100644 --- a/drivers/net/wireless/iwlwifi/dvm/dev.h +++ b/drivers/net/wireless/iwlwifi/dvm/dev.h @@ -708,7 +708,6 @@ struct iwl_priv { unsigned long reload_jiffies; int reload_count; bool ucode_loaded; - bool init_ucode_run; /* Don't run init uCode again */ u8 plcp_delta_threshold; diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 5707ba5ce23f..5abd62ed8cb4 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -1114,16 +1114,17 @@ static void iwlagn_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, scd_queues &= ~(BIT(IWL_IPAN_CMD_QUEUE_NUM) | BIT(IWL_DEFAULT_CMD_QUEUE_NUM)); - if (vif) - scd_queues &= ~BIT(vif->hw_queue[IEEE80211_AC_VO]); - - IWL_DEBUG_TX_QUEUES(priv, "Flushing SCD queues: 0x%x\n", scd_queues); - if (iwlagn_txfifo_flush(priv, scd_queues)) { - IWL_ERR(priv, "flush request fail\n"); - goto done; + if (drop) { + IWL_DEBUG_TX_QUEUES(priv, "Flushing SCD queues: 0x%x\n", + scd_queues); + if (iwlagn_txfifo_flush(priv, scd_queues)) { + IWL_ERR(priv, "flush request fail\n"); + goto done; + } } + IWL_DEBUG_TX_QUEUES(priv, "wait transmit/flush all frames\n"); - iwl_trans_wait_tx_queue_empty(priv->trans, 0xffffffff); + iwl_trans_wait_tx_queue_empty(priv->trans, scd_queues); done: mutex_unlock(&priv->mutex); IWL_DEBUG_MAC80211(priv, "leave\n"); diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.c b/drivers/net/wireless/iwlwifi/dvm/rs.c index 32b78a66536d..3bd7c86e90d9 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rs.c +++ b/drivers/net/wireless/iwlwifi/dvm/rs.c @@ -3153,12 +3153,13 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, desc += sprintf(buff+desc, "lq type %s\n", (is_legacy(tbl->lq_type)) ? "legacy" : "HT"); if (is_Ht(tbl->lq_type)) { - desc += sprintf(buff+desc, " %s", + desc += sprintf(buff + desc, " %s", (is_siso(tbl->lq_type)) ? "SISO" : ((is_mimo2(tbl->lq_type)) ? "MIMO2" : "MIMO3")); - desc += sprintf(buff+desc, " %s", + desc += sprintf(buff + desc, " %s", (tbl->is_ht40) ? "40MHz" : "20MHz"); - desc += sprintf(buff+desc, " %s %s %s\n", (tbl->is_SGI) ? "SGI" : "", + desc += sprintf(buff + desc, " %s %s %s\n", + (tbl->is_SGI) ? "SGI" : "", (lq_sta->is_green) ? "GF enabled" : "", (lq_sta->is_agg) ? "AGG on" : ""); } diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index 1e40a12de077..275df12a6045 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -189,9 +189,9 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv, rate_flags |= RATE_MCS_CCK_MSK; /* Set up antennas */ - if (priv->lib->bt_params && - priv->lib->bt_params->advanced_bt_coexist && - priv->bt_full_concurrent) { + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist && + priv->bt_full_concurrent) { /* operated as 1x1 in full concurrency mode */ priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant, first_antenna(priv->nvm_data->valid_tx_ant)); diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/iwlwifi/dvm/ucode.c index 4dbef7e58c2e..5244e43bfafb 100644 --- a/drivers/net/wireless/iwlwifi/dvm/ucode.c +++ b/drivers/net/wireless/iwlwifi/dvm/ucode.c @@ -418,9 +418,6 @@ int iwl_run_init_ucode(struct iwl_priv *priv) if (!priv->fw->img[IWL_UCODE_INIT].sec[0].len) return 0; - if (priv->init_ucode_run) - return 0; - iwl_init_notification_wait(&priv->notif_wait, &calib_wait, calib_complete, ARRAY_SIZE(calib_complete), iwlagn_wait_calib, priv); @@ -440,8 +437,6 @@ int iwl_run_init_ucode(struct iwl_priv *priv) */ ret = iwl_wait_notification(&priv->notif_wait, &calib_wait, UCODE_CALIB_TIMEOUT); - if (!ret) - priv->init_ucode_run = true; goto out; diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c index 0597a9cfd2f6..36e786f0387b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/iwlwifi/iwl-7000.c @@ -69,12 +69,12 @@ #include "iwl-agn-hw.h" /* Highest firmware API version supported */ -#define IWL7260_UCODE_API_MAX 12 -#define IWL3160_UCODE_API_MAX 12 +#define IWL7260_UCODE_API_MAX 13 +#define IWL3160_UCODE_API_MAX 13 /* Oldest version we won't warn about */ -#define IWL7260_UCODE_API_OK 10 -#define IWL3160_UCODE_API_OK 10 +#define IWL7260_UCODE_API_OK 12 +#define IWL3160_UCODE_API_OK 12 /* Lowest firmware API version supported */ #define IWL7260_UCODE_API_MIN 10 diff --git a/drivers/net/wireless/iwlwifi/iwl-8000.c b/drivers/net/wireless/iwlwifi/iwl-8000.c index d8dfa6da6307..9c396a42aec8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-8000.c +++ b/drivers/net/wireless/iwlwifi/iwl-8000.c @@ -69,10 +69,10 @@ #include "iwl-agn-hw.h" /* Highest firmware API version supported */ -#define IWL8000_UCODE_API_MAX 12 +#define IWL8000_UCODE_API_MAX 13 /* Oldest version we won't warn about */ -#define IWL8000_UCODE_API_OK 10 +#define IWL8000_UCODE_API_OK 12 /* Lowest firmware API version supported */ #define IWL8000_UCODE_API_MIN 10 diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h index 684254553558..9bb36d79c2bd 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debug.h +++ b/drivers/net/wireless/iwlwifi/iwl-debug.h @@ -157,6 +157,7 @@ do { \ /* 0x0000F000 - 0x00001000 */ #define IWL_DL_ASSOC 0x00001000 #define IWL_DL_DROP 0x00002000 +#define IWL_DL_LAR 0x00004000 #define IWL_DL_COEX 0x00008000 /* 0x000F0000 - 0x00010000 */ #define IWL_DL_FW 0x00010000 @@ -219,5 +220,6 @@ do { \ #define IWL_DEBUG_POWER(p, f, a...) IWL_DEBUG(p, IWL_DL_POWER, f, ## a) #define IWL_DEBUG_11H(p, f, a...) IWL_DEBUG(p, IWL_DL_11H, f, ## a) #define IWL_DEBUG_RPM(p, f, a...) IWL_DEBUG(p, IWL_DL_RPM, f, ## a) +#define IWL_DEBUG_LAR(p, f, a...) IWL_DEBUG(p, IWL_DL_LAR, f, ## a) #endif diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index 141331d41abf..aefdd9b7c105 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -1014,34 +1014,34 @@ static int validate_sec_sizes(struct iwl_drv *drv, /* Verify that uCode images will fit in card's SRAM. */ if (get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST) > - cfg->max_inst_size) { + cfg->max_inst_size) { IWL_ERR(drv, "uCode instr len %Zd too large to fit in\n", get_sec_size(pieces, IWL_UCODE_REGULAR, - IWL_UCODE_SECTION_INST)); + IWL_UCODE_SECTION_INST)); return -1; } if (get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA) > - cfg->max_data_size) { + cfg->max_data_size) { IWL_ERR(drv, "uCode data len %Zd too large to fit in\n", get_sec_size(pieces, IWL_UCODE_REGULAR, - IWL_UCODE_SECTION_DATA)); + IWL_UCODE_SECTION_DATA)); return -1; } - if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST) > - cfg->max_inst_size) { + if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST) > + cfg->max_inst_size) { IWL_ERR(drv, "uCode init instr len %Zd too large to fit in\n", get_sec_size(pieces, IWL_UCODE_INIT, - IWL_UCODE_SECTION_INST)); + IWL_UCODE_SECTION_INST)); return -1; } if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA) > - cfg->max_data_size) { + cfg->max_data_size) { IWL_ERR(drv, "uCode init data len %Zd too large to fit in\n", get_sec_size(pieces, IWL_UCODE_REGULAR, - IWL_UCODE_SECTION_DATA)); + IWL_UCODE_SECTION_DATA)); return -1; } return 0; @@ -1319,6 +1319,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) op->name, err); #endif } + kfree(pieces); return; try_again: @@ -1546,6 +1547,10 @@ module_param_named(d0i3_disable, iwlwifi_mod_params.d0i3_disable, bool, S_IRUGO); MODULE_PARM_DESC(d0i3_disable, "disable d0i3 functionality (default: Y)"); +module_param_named(lar_disable, iwlwifi_mod_params.lar_disable, + bool, S_IRUGO); +MODULE_PARM_DESC(lar_disable, "disable LAR functionality (default: N)"); + module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable, bool, S_IRUGO | S_IWUSR); #ifdef CONFIG_IWLWIFI_UAPSD diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.h b/drivers/net/wireless/iwlwifi/iwl-drv.h index adf522c756e6..67a3a241b331 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.h +++ b/drivers/net/wireless/iwlwifi/iwl-drv.h @@ -68,7 +68,7 @@ /* for all modules */ #define DRV_NAME "iwlwifi" -#define DRV_COPYRIGHT "Copyright(c) 2003- 2014 Intel Corporation" +#define DRV_COPYRIGHT "Copyright(c) 2003- 2015 Intel Corporation" #define DRV_AUTHOR "<ilw@linux.intel.com>" /* radio config bits (actual values from NVM definition) */ diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h index f0548b8a64b0..5234a0bf11e4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h @@ -94,6 +94,7 @@ struct iwl_nvm_data { u32 nvm_version; s8 max_tx_pwr_half_dbm; + bool lar_enabled; struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; struct ieee80211_channel channels[]; }; diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h index 5ea381861d5d..291a3382aa3f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h @@ -240,10 +240,9 @@ enum iwl_ucode_tlv_flag { /** * enum iwl_ucode_tlv_api - ucode api * @IWL_UCODE_TLV_API_BT_COEX_SPLIT: new API for BT Coex - * @IWL_UCODE_TLV_API_DISABLE_STA_TX: ucode supports tx_disable bit. - * @IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF: ucode supports disabling dummy notif. * @IWL_UCODE_TLV_API_FRAGMENTED_SCAN: This ucode supports active dwell time * longer than the passive one, which is essential for fragmented scan. + * @IWL_UCODE_TLV_API_WIFI_MCC_UPDATE: ucode supports MCC updates with source. * IWL_UCODE_TLV_API_HDC_PHASE_0: ucode supports finer configuration of LTR * @IWL_UCODE_TLV_API_BASIC_DWELL: use only basic dwell time in scan command, * regardless of the band or the number of the probes. FW will calculate @@ -258,9 +257,8 @@ enum iwl_ucode_tlv_flag { */ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_BT_COEX_SPLIT = BIT(3), - IWL_UCODE_TLV_API_DISABLE_STA_TX = BIT(5), - IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF = BIT(7), IWL_UCODE_TLV_API_FRAGMENTED_SCAN = BIT(8), + IWL_UCODE_TLV_API_WIFI_MCC_UPDATE = BIT(9), IWL_UCODE_TLV_API_HDC_PHASE_0 = BIT(10), IWL_UCODE_TLV_API_BASIC_DWELL = BIT(13), IWL_UCODE_TLV_API_SCD_CFG = BIT(15), @@ -292,6 +290,7 @@ enum iwl_ucode_tlv_api { * @IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT: supports Hot Spot Command * @IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS: support radio and beacon statistics * @IWL_UCODE_TLV_CAPA_BT_COEX_PLCR: enabled BT Coex packet level co-running + * @IWL_UCODE_TLV_CAPA_BT_COEX_RRC: supports BT Coex RRC */ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_D0I3_SUPPORT = BIT(0), @@ -308,6 +307,7 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT = BIT(18), IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS = BIT(22), IWL_UCODE_TLV_CAPA_BT_COEX_PLCR = BIT(28), + IWL_UCODE_TLV_CAPA_BT_COEX_RRC = BIT(30), }; /* The default calibrate table size if not specified by firmware file */ diff --git a/drivers/net/wireless/iwlwifi/iwl-io.c b/drivers/net/wireless/iwlwifi/iwl-io.c index 03250a45272e..78cac43e2bcd 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/iwlwifi/iwl-io.c @@ -201,6 +201,8 @@ void iwl_force_nmi(struct iwl_trans *trans) } else { iwl_write_prph(trans, DEVICE_SET_NMI_8000B_REG, DEVICE_SET_NMI_8000B_VAL); + iwl_write_prph(trans, DEVICE_SET_NMI_REG, + DEVICE_SET_NMI_VAL_DRV); } } IWL_EXPORT_SYMBOL(iwl_force_nmi); diff --git a/drivers/net/wireless/iwlwifi/iwl-modparams.h b/drivers/net/wireless/iwlwifi/iwl-modparams.h index e8eabd21ccfe..ac2b90df8413 100644 --- a/drivers/net/wireless/iwlwifi/iwl-modparams.h +++ b/drivers/net/wireless/iwlwifi/iwl-modparams.h @@ -103,6 +103,7 @@ enum iwl_disable_11n { * @debug_level: levels are IWL_DL_* * @ant_coupling: antenna coupling in dB, default = 0 * @d0i3_disable: disable d0i3, default = 1, + * @lar_disable: disable LAR (regulatory), default = 0 * @fw_monitor: allow to use firmware monitor */ struct iwl_mod_params { @@ -121,6 +122,7 @@ struct iwl_mod_params { char *nvm_file; bool uapsd_disable; bool d0i3_disable; + bool lar_disable; bool fw_monitor; }; diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index c74f1a4edf23..774637746427 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c @@ -103,8 +103,16 @@ enum family_8000_nvm_offsets { SKU_FAMILY_8000 = 4, N_HW_ADDRS_FAMILY_8000 = 5, + /* NVM PHY-SKU-Section offset (in words) for B0 */ + RADIO_CFG_FAMILY_8000_B0 = 0, + SKU_FAMILY_8000_B0 = 2, + N_HW_ADDRS_FAMILY_8000_B0 = 3, + /* NVM REGULATORY -Section offset (in words) definitions */ NVM_CHANNELS_FAMILY_8000 = 0, + NVM_LAR_OFFSET_FAMILY_8000_OLD = 0x4C7, + NVM_LAR_OFFSET_FAMILY_8000 = 0x507, + NVM_LAR_ENABLED_FAMILY_8000 = 0x7, /* NVM calibration section offset (in words) definitions */ NVM_CALIB_SECTION_FAMILY_8000 = 0x2B8, @@ -146,7 +154,9 @@ static const u8 iwl_nvm_channels_family_8000[] = { #define NUM_2GHZ_CHANNELS_FAMILY_8000 14 #define FIRST_2GHZ_HT_MINUS 5 #define LAST_2GHZ_HT_PLUS 9 -#define LAST_5GHZ_HT 161 +#define LAST_5GHZ_HT 165 +#define LAST_5GHZ_HT_FAMILY_8000 181 +#define N_HW_ADDR_MASK 0xF /* rate data (static) */ static struct ieee80211_rate iwl_cfg80211_rates[] = { @@ -201,9 +211,57 @@ enum iwl_nvm_channel_flags { #define CHECK_AND_PRINT_I(x) \ ((ch_flags & NVM_CHANNEL_##x) ? # x " " : "") +static u32 iwl_get_channel_flags(u8 ch_num, int ch_idx, bool is_5ghz, + u16 nvm_flags, const struct iwl_cfg *cfg) +{ + u32 flags = IEEE80211_CHAN_NO_HT40; + u32 last_5ghz_ht = LAST_5GHZ_HT; + + if (cfg->device_family == IWL_DEVICE_FAMILY_8000) + last_5ghz_ht = LAST_5GHZ_HT_FAMILY_8000; + + if (!is_5ghz && (nvm_flags & NVM_CHANNEL_40MHZ)) { + if (ch_num <= LAST_2GHZ_HT_PLUS) + flags &= ~IEEE80211_CHAN_NO_HT40PLUS; + if (ch_num >= FIRST_2GHZ_HT_MINUS) + flags &= ~IEEE80211_CHAN_NO_HT40MINUS; + } else if (ch_num <= last_5ghz_ht && (nvm_flags & NVM_CHANNEL_40MHZ)) { + if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0) + flags &= ~IEEE80211_CHAN_NO_HT40PLUS; + else + flags &= ~IEEE80211_CHAN_NO_HT40MINUS; + } + if (!(nvm_flags & NVM_CHANNEL_80MHZ)) + flags |= IEEE80211_CHAN_NO_80MHZ; + if (!(nvm_flags & NVM_CHANNEL_160MHZ)) + flags |= IEEE80211_CHAN_NO_160MHZ; + + if (!(nvm_flags & NVM_CHANNEL_IBSS)) + flags |= IEEE80211_CHAN_NO_IR; + + if (!(nvm_flags & NVM_CHANNEL_ACTIVE)) + flags |= IEEE80211_CHAN_NO_IR; + + if (nvm_flags & NVM_CHANNEL_RADAR) + flags |= IEEE80211_CHAN_RADAR; + + if (nvm_flags & NVM_CHANNEL_INDOOR_ONLY) + flags |= IEEE80211_CHAN_INDOOR_ONLY; + + /* Set the GO concurrent flag only in case that NO_IR is set. + * Otherwise it is meaningless + */ + if ((nvm_flags & NVM_CHANNEL_GO_CONCURRENT) && + (flags & IEEE80211_CHAN_NO_IR)) + flags |= IEEE80211_CHAN_GO_CONCURRENT; + + return flags; +} + static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, struct iwl_nvm_data *data, - const __le16 * const nvm_ch_flags) + const __le16 * const nvm_ch_flags, + bool lar_supported) { int ch_idx; int n_channels = 0; @@ -228,9 +286,14 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, if (ch_idx >= num_2ghz_channels && !data->sku_cap_band_52GHz_enable) - ch_flags &= ~NVM_CHANNEL_VALID; + continue; - if (!(ch_flags & NVM_CHANNEL_VALID)) { + if (!lar_supported && !(ch_flags & NVM_CHANNEL_VALID)) { + /* + * Channels might become valid later if lar is + * supported, hence we still want to add them to + * the list of supported channels to cfg80211. + */ IWL_DEBUG_EEPROM(dev, "Ch. %d Flags %x [%sGHz] - No traffic\n", nvm_chan[ch_idx], @@ -250,45 +313,6 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, ieee80211_channel_to_frequency( channel->hw_value, channel->band); - /* TODO: Need to be dependent to the NVM */ - channel->flags = IEEE80211_CHAN_NO_HT40; - if (ch_idx < num_2ghz_channels && - (ch_flags & NVM_CHANNEL_40MHZ)) { - if (nvm_chan[ch_idx] <= LAST_2GHZ_HT_PLUS) - channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS; - if (nvm_chan[ch_idx] >= FIRST_2GHZ_HT_MINUS) - channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS; - } else if (nvm_chan[ch_idx] <= LAST_5GHZ_HT && - (ch_flags & NVM_CHANNEL_40MHZ)) { - if ((ch_idx - num_2ghz_channels) % 2 == 0) - channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS; - else - channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS; - } - if (!(ch_flags & NVM_CHANNEL_80MHZ)) - channel->flags |= IEEE80211_CHAN_NO_80MHZ; - if (!(ch_flags & NVM_CHANNEL_160MHZ)) - channel->flags |= IEEE80211_CHAN_NO_160MHZ; - - if (!(ch_flags & NVM_CHANNEL_IBSS)) - channel->flags |= IEEE80211_CHAN_NO_IR; - - if (!(ch_flags & NVM_CHANNEL_ACTIVE)) - channel->flags |= IEEE80211_CHAN_NO_IR; - - if (ch_flags & NVM_CHANNEL_RADAR) - channel->flags |= IEEE80211_CHAN_RADAR; - - if (ch_flags & NVM_CHANNEL_INDOOR_ONLY) - channel->flags |= IEEE80211_CHAN_INDOOR_ONLY; - - /* Set the GO concurrent flag only in case that NO_IR is set. - * Otherwise it is meaningless - */ - if ((ch_flags & NVM_CHANNEL_GO_CONCURRENT) && - (channel->flags & IEEE80211_CHAN_NO_IR)) - channel->flags |= IEEE80211_CHAN_GO_CONCURRENT; - /* Initialize regulatory-based run-time data */ /* @@ -297,6 +321,15 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, */ channel->max_power = IWL_DEFAULT_MAX_TX_POWER; is_5ghz = channel->band == IEEE80211_BAND_5GHZ; + + /* don't put limitations in case we're using LAR */ + if (!lar_supported) + channel->flags = iwl_get_channel_flags(nvm_chan[ch_idx], + ch_idx, is_5ghz, + ch_flags, cfg); + else + channel->flags = 0; + IWL_DEBUG_EEPROM(dev, "Ch. %d [%sGHz] %s%s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n", channel->hw_value, @@ -370,8 +403,8 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, struct iwl_nvm_data *data, - const __le16 *ch_section, bool enable_vht, - u8 tx_chains, u8 rx_chains) + const __le16 *ch_section, + u8 tx_chains, u8 rx_chains, bool lar_supported) { int n_channels; int n_used = 0; @@ -380,11 +413,12 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, if (cfg->device_family != IWL_DEVICE_FAMILY_8000) n_channels = iwl_init_channel_map( dev, cfg, data, - &ch_section[NVM_CHANNELS]); + &ch_section[NVM_CHANNELS], lar_supported); else n_channels = iwl_init_channel_map( dev, cfg, data, - &ch_section[NVM_CHANNELS_FAMILY_8000]); + &ch_section[NVM_CHANNELS_FAMILY_8000], + lar_supported); sband = &data->bands[IEEE80211_BAND_2GHZ]; sband->band = IEEE80211_BAND_2GHZ; @@ -403,7 +437,7 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, IEEE80211_BAND_5GHZ); iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ, tx_chains, rx_chains); - if (enable_vht) + if (data->sku_cap_11ac_enable) iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap, tx_chains, rx_chains); @@ -413,10 +447,15 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, } static int iwl_get_sku(const struct iwl_cfg *cfg, - const __le16 *nvm_sw) + const __le16 *nvm_sw, const __le16 *phy_sku, + bool is_family_8000_a_step) { if (cfg->device_family != IWL_DEVICE_FAMILY_8000) return le16_to_cpup(nvm_sw + SKU); + + if (!is_family_8000_a_step) + return le32_to_cpup((__le32 *)(phy_sku + + SKU_FAMILY_8000_B0)); else return le32_to_cpup((__le32 *)(nvm_sw + SKU_FAMILY_8000)); } @@ -432,23 +471,36 @@ static int iwl_get_nvm_version(const struct iwl_cfg *cfg, } static int iwl_get_radio_cfg(const struct iwl_cfg *cfg, - const __le16 *nvm_sw) + const __le16 *nvm_sw, const __le16 *phy_sku, + bool is_family_8000_a_step) { if (cfg->device_family != IWL_DEVICE_FAMILY_8000) return le16_to_cpup(nvm_sw + RADIO_CFG); + + if (!is_family_8000_a_step) + return le32_to_cpup((__le32 *)(phy_sku + + RADIO_CFG_FAMILY_8000_B0)); else return le32_to_cpup((__le32 *)(nvm_sw + RADIO_CFG_FAMILY_8000)); + } -#define N_HW_ADDRS_MASK_FAMILY_8000 0xF static int iwl_get_n_hw_addrs(const struct iwl_cfg *cfg, - const __le16 *nvm_sw) + const __le16 *nvm_sw, bool is_family_8000_a_step) { + int n_hw_addr; + if (cfg->device_family != IWL_DEVICE_FAMILY_8000) return le16_to_cpup(nvm_sw + N_HW_ADDRS); + + if (!is_family_8000_a_step) + n_hw_addr = le32_to_cpup((__le32 *)(nvm_sw + + N_HW_ADDRS_FAMILY_8000_B0)); else - return le32_to_cpup((__le32 *)(nvm_sw + N_HW_ADDRS_FAMILY_8000)) - & N_HW_ADDRS_MASK_FAMILY_8000; + n_hw_addr = le32_to_cpup((__le32 *)(nvm_sw + + N_HW_ADDRS_FAMILY_8000)); + + return n_hw_addr & N_HW_ADDR_MASK; } static void iwl_set_radio_cfg(const struct iwl_cfg *cfg, @@ -491,7 +543,8 @@ static void iwl_set_hw_address_family_8000(struct device *dev, const struct iwl_cfg *cfg, struct iwl_nvm_data *data, const __le16 *mac_override, - const __le16 *nvm_hw) + const __le16 *nvm_hw, + u32 mac_addr0, u32 mac_addr1) { const u8 *hw_addr; @@ -515,48 +568,17 @@ static void iwl_set_hw_address_family_8000(struct device *dev, } if (nvm_hw) { - /* read the MAC address from OTP */ - if (!dev_is_pci(dev) || (data->nvm_version < 0xE08)) { - /* read the mac address from the WFPM location */ - hw_addr = (const u8 *)(nvm_hw + - HW_ADDR0_WFPM_FAMILY_8000); - data->hw_addr[0] = hw_addr[3]; - data->hw_addr[1] = hw_addr[2]; - data->hw_addr[2] = hw_addr[1]; - data->hw_addr[3] = hw_addr[0]; - - hw_addr = (const u8 *)(nvm_hw + - HW_ADDR1_WFPM_FAMILY_8000); - data->hw_addr[4] = hw_addr[1]; - data->hw_addr[5] = hw_addr[0]; - } else if ((data->nvm_version >= 0xE08) && - (data->nvm_version < 0xE0B)) { - /* read "reverse order" from the PCIe location */ - hw_addr = (const u8 *)(nvm_hw + - HW_ADDR0_PCIE_FAMILY_8000); - data->hw_addr[5] = hw_addr[2]; - data->hw_addr[4] = hw_addr[1]; - data->hw_addr[3] = hw_addr[0]; - - hw_addr = (const u8 *)(nvm_hw + - HW_ADDR1_PCIE_FAMILY_8000); - data->hw_addr[2] = hw_addr[3]; - data->hw_addr[1] = hw_addr[2]; - data->hw_addr[0] = hw_addr[1]; - } else { - /* read from the PCIe location */ - hw_addr = (const u8 *)(nvm_hw + - HW_ADDR0_PCIE_FAMILY_8000); - data->hw_addr[5] = hw_addr[0]; - data->hw_addr[4] = hw_addr[1]; - data->hw_addr[3] = hw_addr[2]; - - hw_addr = (const u8 *)(nvm_hw + - HW_ADDR1_PCIE_FAMILY_8000); - data->hw_addr[2] = hw_addr[1]; - data->hw_addr[1] = hw_addr[2]; - data->hw_addr[0] = hw_addr[3]; - } + /* read the MAC address from HW resisters */ + hw_addr = (const u8 *)&mac_addr0; + data->hw_addr[0] = hw_addr[3]; + data->hw_addr[1] = hw_addr[2]; + data->hw_addr[2] = hw_addr[1]; + data->hw_addr[3] = hw_addr[0]; + + hw_addr = (const u8 *)&mac_addr1; + data->hw_addr[4] = hw_addr[1]; + data->hw_addr[5] = hw_addr[0]; + if (!is_valid_ether_addr(data->hw_addr)) IWL_ERR_DEV(dev, "mac address from hw section is not valid\n"); @@ -571,11 +593,15 @@ struct iwl_nvm_data * iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, const __le16 *nvm_hw, const __le16 *nvm_sw, const __le16 *nvm_calib, const __le16 *regulatory, - const __le16 *mac_override, u8 tx_chains, u8 rx_chains) + const __le16 *mac_override, const __le16 *phy_sku, + u8 tx_chains, u8 rx_chains, + bool lar_fw_supported, bool is_family_8000_a_step, + u32 mac_addr0, u32 mac_addr1) { struct iwl_nvm_data *data; u32 sku; u32 radio_cfg; + u16 lar_config; if (cfg->device_family != IWL_DEVICE_FAMILY_8000) data = kzalloc(sizeof(*data) + @@ -592,22 +618,25 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, data->nvm_version = iwl_get_nvm_version(cfg, nvm_sw); - radio_cfg = iwl_get_radio_cfg(cfg, nvm_sw); + radio_cfg = + iwl_get_radio_cfg(cfg, nvm_sw, phy_sku, is_family_8000_a_step); iwl_set_radio_cfg(cfg, data, radio_cfg); if (data->valid_tx_ant) tx_chains &= data->valid_tx_ant; if (data->valid_rx_ant) rx_chains &= data->valid_rx_ant; - sku = iwl_get_sku(cfg, nvm_sw); + sku = iwl_get_sku(cfg, nvm_sw, phy_sku, is_family_8000_a_step); data->sku_cap_band_24GHz_enable = sku & NVM_SKU_CAP_BAND_24GHZ; data->sku_cap_band_52GHz_enable = sku & NVM_SKU_CAP_BAND_52GHZ; data->sku_cap_11n_enable = sku & NVM_SKU_CAP_11N_ENABLE; - data->sku_cap_11ac_enable = sku & NVM_SKU_CAP_11AC_ENABLE; if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL) data->sku_cap_11n_enable = false; + data->sku_cap_11ac_enable = data->sku_cap_11n_enable && + (sku & NVM_SKU_CAP_11AC_ENABLE); - data->n_hw_addrs = iwl_get_n_hw_addrs(cfg, nvm_sw); + data->n_hw_addrs = + iwl_get_n_hw_addrs(cfg, nvm_sw, is_family_8000_a_step); if (cfg->device_family != IWL_DEVICE_FAMILY_8000) { /* Checking for required sections */ @@ -626,16 +655,23 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, iwl_set_hw_address(cfg, data, nvm_hw); iwl_init_sbands(dev, cfg, data, nvm_sw, - sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains, - rx_chains); + tx_chains, rx_chains, lar_fw_supported); } else { + u16 lar_offset = data->nvm_version < 0xE39 ? + NVM_LAR_OFFSET_FAMILY_8000_OLD : + NVM_LAR_OFFSET_FAMILY_8000; + + lar_config = le16_to_cpup(regulatory + lar_offset); + data->lar_enabled = !!(lar_config & + NVM_LAR_ENABLED_FAMILY_8000); + /* MAC address in family 8000 */ iwl_set_hw_address_family_8000(dev, cfg, data, mac_override, - nvm_hw); + nvm_hw, mac_addr0, mac_addr1); iwl_init_sbands(dev, cfg, data, regulatory, - sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains, - rx_chains); + tx_chains, rx_chains, + lar_fw_supported && data->lar_enabled); } data->calib_version = 255; @@ -643,3 +679,164 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, return data; } IWL_EXPORT_SYMBOL(iwl_parse_nvm_data); + +static u32 iwl_nvm_get_regdom_bw_flags(const u8 *nvm_chan, + int ch_idx, u16 nvm_flags, + const struct iwl_cfg *cfg) +{ + u32 flags = NL80211_RRF_NO_HT40; + u32 last_5ghz_ht = LAST_5GHZ_HT; + + if (cfg->device_family == IWL_DEVICE_FAMILY_8000) + last_5ghz_ht = LAST_5GHZ_HT_FAMILY_8000; + + if (ch_idx < NUM_2GHZ_CHANNELS && + (nvm_flags & NVM_CHANNEL_40MHZ)) { + if (nvm_chan[ch_idx] <= LAST_2GHZ_HT_PLUS) + flags &= ~NL80211_RRF_NO_HT40PLUS; + if (nvm_chan[ch_idx] >= FIRST_2GHZ_HT_MINUS) + flags &= ~NL80211_RRF_NO_HT40MINUS; + } else if (nvm_chan[ch_idx] <= last_5ghz_ht && + (nvm_flags & NVM_CHANNEL_40MHZ)) { + if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0) + flags &= ~NL80211_RRF_NO_HT40PLUS; + else + flags &= ~NL80211_RRF_NO_HT40MINUS; + } + + if (!(nvm_flags & NVM_CHANNEL_80MHZ)) + flags |= NL80211_RRF_NO_80MHZ; + if (!(nvm_flags & NVM_CHANNEL_160MHZ)) + flags |= NL80211_RRF_NO_160MHZ; + + if (!(nvm_flags & NVM_CHANNEL_ACTIVE)) + flags |= NL80211_RRF_NO_IR; + + if (nvm_flags & NVM_CHANNEL_RADAR) + flags |= NL80211_RRF_DFS; + + if (nvm_flags & NVM_CHANNEL_INDOOR_ONLY) + flags |= NL80211_RRF_NO_OUTDOOR; + + /* Set the GO concurrent flag only in case that NO_IR is set. + * Otherwise it is meaningless + */ + if ((nvm_flags & NVM_CHANNEL_GO_CONCURRENT) && + (flags & NL80211_RRF_NO_IR)) + flags |= NL80211_RRF_GO_CONCURRENT; + + return flags; +} + +struct ieee80211_regdomain * +iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, + int num_of_ch, __le32 *channels, u16 fw_mcc) +{ + int ch_idx; + u16 ch_flags, prev_ch_flags = 0; + const u8 *nvm_chan = cfg->device_family == IWL_DEVICE_FAMILY_8000 ? + iwl_nvm_channels_family_8000 : iwl_nvm_channels; + struct ieee80211_regdomain *regd; + int size_of_regd; + struct ieee80211_reg_rule *rule; + enum ieee80211_band band; + int center_freq, prev_center_freq = 0; + int valid_rules = 0; + bool new_rule; + int max_num_ch = cfg->device_family == IWL_DEVICE_FAMILY_8000 ? + IWL_NUM_CHANNELS_FAMILY_8000 : IWL_NUM_CHANNELS; + + if (WARN_ON_ONCE(num_of_ch > NL80211_MAX_SUPP_REG_RULES)) + return ERR_PTR(-EINVAL); + + if (WARN_ON(num_of_ch > max_num_ch)) + num_of_ch = max_num_ch; + + IWL_DEBUG_DEV(dev, IWL_DL_LAR, "building regdom for %d channels\n", + num_of_ch); + + /* build a regdomain rule for every valid channel */ + size_of_regd = + sizeof(struct ieee80211_regdomain) + + num_of_ch * sizeof(struct ieee80211_reg_rule); + + regd = kzalloc(size_of_regd, GFP_KERNEL); + if (!regd) + return ERR_PTR(-ENOMEM); + + for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) { + ch_flags = (u16)__le32_to_cpup(channels + ch_idx); + band = (ch_idx < NUM_2GHZ_CHANNELS) ? + IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; + center_freq = ieee80211_channel_to_frequency(nvm_chan[ch_idx], + band); + new_rule = false; + + if (!(ch_flags & NVM_CHANNEL_VALID)) { + IWL_DEBUG_DEV(dev, IWL_DL_LAR, + "Ch. %d Flags %x [%sGHz] - No traffic\n", + nvm_chan[ch_idx], + ch_flags, + (ch_idx >= NUM_2GHZ_CHANNELS) ? + "5.2" : "2.4"); + continue; + } + + /* we can't continue the same rule */ + if (ch_idx == 0 || prev_ch_flags != ch_flags || + center_freq - prev_center_freq > 20) { + valid_rules++; + new_rule = true; + } + + rule = ®d->reg_rules[valid_rules - 1]; + + if (new_rule) + rule->freq_range.start_freq_khz = + MHZ_TO_KHZ(center_freq - 10); + + rule->freq_range.end_freq_khz = MHZ_TO_KHZ(center_freq + 10); + + /* this doesn't matter - not used by FW */ + rule->power_rule.max_antenna_gain = DBI_TO_MBI(6); + rule->power_rule.max_eirp = + DBM_TO_MBM(IWL_DEFAULT_MAX_TX_POWER); + + rule->flags = iwl_nvm_get_regdom_bw_flags(nvm_chan, ch_idx, + ch_flags, cfg); + + /* rely on auto-calculation to merge BW of contiguous chans */ + rule->flags |= NL80211_RRF_AUTO_BW; + rule->freq_range.max_bandwidth_khz = 0; + + prev_ch_flags = ch_flags; + prev_center_freq = center_freq; + + IWL_DEBUG_DEV(dev, IWL_DL_LAR, + "Ch. %d [%sGHz] %s%s%s%s%s%s%s%s%s(0x%02x): Ad-Hoc %ssupported\n", + center_freq, + band == IEEE80211_BAND_5GHZ ? "5.2" : "2.4", + CHECK_AND_PRINT_I(VALID), + CHECK_AND_PRINT_I(ACTIVE), + CHECK_AND_PRINT_I(RADAR), + CHECK_AND_PRINT_I(WIDE), + CHECK_AND_PRINT_I(40MHZ), + CHECK_AND_PRINT_I(80MHZ), + CHECK_AND_PRINT_I(160MHZ), + CHECK_AND_PRINT_I(INDOOR_ONLY), + CHECK_AND_PRINT_I(GO_CONCURRENT), + ch_flags, + ((ch_flags & NVM_CHANNEL_ACTIVE) && + !(ch_flags & NVM_CHANNEL_RADAR)) + ? "" : "not "); + } + + regd->n_reg_rules = valid_rules; + + /* set alpha2 from FW. */ + regd->alpha2[0] = fw_mcc >> 8; + regd->alpha2[1] = fw_mcc & 0xff; + + return regd; +} +IWL_EXPORT_SYMBOL(iwl_parse_nvm_mcc_info); diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h index c9c45a39d212..c995d2cee3f6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h @@ -62,6 +62,7 @@ #ifndef __iwl_nvm_parse_h__ #define __iwl_nvm_parse_h__ +#include <net/cfg80211.h> #include "iwl-eeprom-parse.h" /** @@ -76,6 +77,22 @@ struct iwl_nvm_data * iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, const __le16 *nvm_hw, const __le16 *nvm_sw, const __le16 *nvm_calib, const __le16 *regulatory, - const __le16 *mac_override, u8 tx_chains, u8 rx_chains); + const __le16 *mac_override, const __le16 *phy_sku, + u8 tx_chains, u8 rx_chains, + bool lar_fw_supported, bool is_family_8000_a_step, + u32 mac_addr0, u32 mac_addr1); + +/** + * iwl_parse_mcc_info - parse MCC (mobile country code) info coming from FW + * + * This function parses the regulatory channel data received as a + * MCC_UPDATE_CMD command. It returns a newly allocation regulatory domain, + * to be fed into the regulatory core. An ERR_PTR is returned on error. + * If not given to the regulatory core, the user is responsible for freeing + * the regdomain returned here with kfree. + */ +struct ieee80211_regdomain * +iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, + int num_of_ch, __le32 *channels, u16 fw_mcc); #endif /* __iwl_nvm_parse_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index 6095088b88d9..bc962888c583 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -371,6 +371,33 @@ enum secure_load_status_reg { #define DBGC_IN_SAMPLE (0xa03c00) +/* enable the ID buf for read */ +#define WFPM_PS_CTL_CLR 0xA0300C +#define WFMP_MAC_ADDR_0 0xA03080 +#define WFMP_MAC_ADDR_1 0xA03084 +#define LMPM_PMG_EN 0xA01CEC +#define RADIO_REG_SYS_MANUAL_DFT_0 0xAD4078 +#define RFIC_REG_RD 0xAD0470 +#define WFPM_CTRL_REG 0xA03030 +enum { + ENABLE_WFPM = BIT(31), + WFPM_AUX_CTL_AUX_IF_MAC_OWNER_MSK = 0x80000000, +}; + +#define AUX_MISC_REG 0xA200B0 +enum { + HW_STEP_LOCATION_BITS = 24, +}; + +#define AUX_MISC_MASTER1_EN 0xA20818 +enum aux_misc_master1_en { + AUX_MISC_MASTER1_EN_SBE_MSK = 0x1, +}; + +#define AUX_MISC_MASTER1_SMPHR_STATUS 0xA20800 +#define RSA_ENABLE 0xA24B08 +#define PREG_AUX_BUS_WPROT_0 0xA04CC0 + /* FW chicken bits */ #define LMPM_CHICK 0xA01FF8 enum { diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 542a6810c81c..11ac5c58527f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -458,6 +458,8 @@ struct iwl_trans_txq_scd_cfg { * @txq_disable: de-configure a Tx queue to send AMPDUs * Must be atomic * @wait_tx_queue_empty: wait until tx queues are empty. May sleep. + * @freeze_txq_timer: prevents the timer of the queue from firing until the + * queue is set to awake. Must be atomic. * @dbgfs_register: add the dbgfs files under this directory. Files will be * automatically deleted. * @write8: write a u8 to a register at offset ofs from the BAR @@ -517,6 +519,8 @@ struct iwl_trans_ops { int (*dbgfs_register)(struct iwl_trans *trans, struct dentry* dir); int (*wait_tx_queue_empty)(struct iwl_trans *trans, u32 txq_bm); + void (*freeze_txq_timer)(struct iwl_trans *trans, unsigned long txqs, + bool freeze); void (*write8)(struct iwl_trans *trans, u32 ofs, u8 val); void (*write32)(struct iwl_trans *trans, u32 ofs, u32 val); @@ -873,6 +877,17 @@ void iwl_trans_ac_txq_enable(struct iwl_trans *trans, int queue, int fifo, iwl_trans_txq_enable_cfg(trans, queue, 0, &cfg, queue_wdg_timeout); } +static inline void iwl_trans_freeze_txq_timer(struct iwl_trans *trans, + unsigned long txqs, + bool freeze) +{ + if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) + IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); + + if (trans->ops->freeze_txq_timer) + trans->ops->freeze_txq_timer(trans, txqs, freeze); +} + static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans, u32 txqs) { diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c index 877f19bbae7e..13a0a03158de 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex.c @@ -72,158 +72,6 @@ #include "mvm.h" #include "iwl-debug.h" -const u32 iwl_bt_ctl_kill_msk[BT_KILL_MSK_MAX] = { - [BT_KILL_MSK_DEFAULT] = 0xfffffc00, - [BT_KILL_MSK_NEVER] = 0xffffffff, - [BT_KILL_MSK_ALWAYS] = 0, -}; - -const u8 iwl_bt_cts_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT] = { - { - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_ALWAYS, - }, - { - BT_KILL_MSK_NEVER, - BT_KILL_MSK_NEVER, - BT_KILL_MSK_NEVER, - }, - { - BT_KILL_MSK_NEVER, - BT_KILL_MSK_NEVER, - BT_KILL_MSK_NEVER, - }, - { - BT_KILL_MSK_DEFAULT, - BT_KILL_MSK_NEVER, - BT_KILL_MSK_DEFAULT, - }, -}; - -const u8 iwl_bt_ack_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT] = { - { - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_ALWAYS, - }, - { - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_ALWAYS, - }, - { - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_ALWAYS, - }, - { - BT_KILL_MSK_DEFAULT, - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_DEFAULT, - }, -}; - -static const __le32 iwl_bt_prio_boost[BT_COEX_BOOST_SIZE] = { - cpu_to_le32(0xf0f0f0f0), /* 50% */ - cpu_to_le32(0xc0c0c0c0), /* 25% */ - cpu_to_le32(0xfcfcfcfc), /* 75% */ - cpu_to_le32(0xfefefefe), /* 87.5% */ -}; - -static const __le32 iwl_single_shared_ant[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = { - { - cpu_to_le32(0x40000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x44000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x40000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x44000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0xc0004000), - cpu_to_le32(0xf0005000), - cpu_to_le32(0xc0004000), - cpu_to_le32(0xf0005000), - }, - { - cpu_to_le32(0x40000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x44000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x40000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x44000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0xc0004000), - cpu_to_le32(0xf0005000), - cpu_to_le32(0xc0004000), - cpu_to_le32(0xf0005000), - }, - { - cpu_to_le32(0x40000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x44000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x40000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x44000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0xc0004000), - cpu_to_le32(0xf0005000), - cpu_to_le32(0xc0004000), - cpu_to_le32(0xf0005000), - }, -}; - -static const __le32 iwl_combined_lookup[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = { - { - /* Tight */ - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaeaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xcc00ff28), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0xcc00aaaa), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0xc0004000), - cpu_to_le32(0x00004000), - cpu_to_le32(0xf0005000), - cpu_to_le32(0xf0005000), - }, - { - /* Loose */ - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xcc00ff28), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0xcc00aaaa), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0xf0005000), - cpu_to_le32(0xf0005000), - }, - { - /* Tx Tx disabled */ - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xeeaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xcc00ff28), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0xcc00aaaa), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0xc0004000), - cpu_to_le32(0xc0004000), - cpu_to_le32(0xf0005000), - cpu_to_le32(0xf0005000), - }, -}; - /* 20MHz / 40MHz below / 40Mhz above*/ static const __le64 iwl_ci_mask[][3] = { /* dummy entry for channel 0 */ @@ -596,14 +444,6 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm) goto send_cmd; } - bt_cmd->max_kill = cpu_to_le32(5); - bt_cmd->bt4_antenna_isolation_thr = - cpu_to_le32(IWL_MVM_BT_COEX_ANTENNA_COUPLING_THRS); - bt_cmd->bt4_tx_tx_delta_freq_thr = cpu_to_le32(15); - bt_cmd->bt4_tx_rx_max_freq0 = cpu_to_le32(15); - bt_cmd->override_primary_lut = cpu_to_le32(BT_COEX_INVALID_LUT); - bt_cmd->override_secondary_lut = cpu_to_le32(BT_COEX_INVALID_LUT); - mode = iwlwifi_mod_params.bt_coex_active ? BT_COEX_NW : BT_COEX_DISABLE; bt_cmd->mode = cpu_to_le32(mode); @@ -622,18 +462,6 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm) bt_cmd->enabled_modules |= cpu_to_le32(BT_COEX_HIGH_BAND_RET); - if (mvm->cfg->bt_shared_single_ant) - memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant, - sizeof(iwl_single_shared_ant)); - else - memcpy(&bt_cmd->decision_lut, iwl_combined_lookup, - sizeof(iwl_combined_lookup)); - - memcpy(&bt_cmd->mplut_prio_boost, iwl_bt_prio_boost, - sizeof(iwl_bt_prio_boost)); - bt_cmd->multiprio_lut[0] = cpu_to_le32(IWL_MVM_BT_COEX_MPLUT_REG0); - bt_cmd->multiprio_lut[1] = cpu_to_le32(IWL_MVM_BT_COEX_MPLUT_REG1); - send_cmd: memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif)); memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd)); @@ -644,48 +472,6 @@ send_cmd: return ret; } -static int iwl_mvm_bt_udpate_sw_boost(struct iwl_mvm *mvm) -{ - struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif; - u32 primary_lut = le32_to_cpu(notif->primary_ch_lut); - u32 secondary_lut = le32_to_cpu(notif->secondary_ch_lut); - u32 ag = le32_to_cpu(notif->bt_activity_grading); - struct iwl_bt_coex_sw_boost_update_cmd cmd = {}; - u8 ack_kill_msk[NUM_PHY_CTX] = {}; - u8 cts_kill_msk[NUM_PHY_CTX] = {}; - int i; - - lockdep_assert_held(&mvm->mutex); - - ack_kill_msk[0] = iwl_bt_ack_kill_msk[ag][primary_lut]; - cts_kill_msk[0] = iwl_bt_cts_kill_msk[ag][primary_lut]; - - ack_kill_msk[1] = iwl_bt_ack_kill_msk[ag][secondary_lut]; - cts_kill_msk[1] = iwl_bt_cts_kill_msk[ag][secondary_lut]; - - /* Don't send HCMD if there is no update */ - if (!memcmp(ack_kill_msk, mvm->bt_ack_kill_msk, sizeof(ack_kill_msk)) || - !memcmp(cts_kill_msk, mvm->bt_cts_kill_msk, sizeof(cts_kill_msk))) - return 0; - - memcpy(mvm->bt_ack_kill_msk, ack_kill_msk, - sizeof(mvm->bt_ack_kill_msk)); - memcpy(mvm->bt_cts_kill_msk, cts_kill_msk, - sizeof(mvm->bt_cts_kill_msk)); - - BUILD_BUG_ON(ARRAY_SIZE(ack_kill_msk) < ARRAY_SIZE(cmd.boost_values)); - - for (i = 0; i < ARRAY_SIZE(cmd.boost_values); i++) { - cmd.boost_values[i].kill_ack_msk = - cpu_to_le32(iwl_bt_ctl_kill_msk[ack_kill_msk[i]]); - cmd.boost_values[i].kill_cts_msk = - cpu_to_le32(iwl_bt_ctl_kill_msk[cts_kill_msk[i]]); - } - - return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_SW_BOOST, 0, - sizeof(cmd), &cmd); -} - static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, bool enable) { @@ -951,9 +737,6 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) IWL_ERR(mvm, "Failed to send BT_CI cmd\n"); memcpy(&mvm->last_bt_ci_cmd, &cmd, sizeof(cmd)); } - - if (iwl_mvm_bt_udpate_sw_boost(mvm)) - IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n"); } int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, @@ -1074,9 +857,6 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, ieee80211_iterate_active_interfaces_atomic( mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_bt_rssi_iterator, &data); - - if (iwl_mvm_bt_udpate_sw_boost(mvm)) - IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n"); } #define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) diff --git a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c index 5535ec9766cb..d954591e0be5 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c @@ -288,6 +288,65 @@ static const __le64 iwl_ci_mask[][3] = { }, }; +enum iwl_bt_kill_msk { + BT_KILL_MSK_DEFAULT, + BT_KILL_MSK_NEVER, + BT_KILL_MSK_ALWAYS, + BT_KILL_MSK_MAX, +}; + +static const u32 iwl_bt_ctl_kill_msk[BT_KILL_MSK_MAX] = { + [BT_KILL_MSK_DEFAULT] = 0xfffffc00, + [BT_KILL_MSK_NEVER] = 0xffffffff, + [BT_KILL_MSK_ALWAYS] = 0, +}; + +static const u8 iwl_bt_cts_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT] = { + { + BT_KILL_MSK_ALWAYS, + BT_KILL_MSK_ALWAYS, + BT_KILL_MSK_ALWAYS, + }, + { + BT_KILL_MSK_NEVER, + BT_KILL_MSK_NEVER, + BT_KILL_MSK_NEVER, + }, + { + BT_KILL_MSK_NEVER, + BT_KILL_MSK_NEVER, + BT_KILL_MSK_NEVER, + }, + { + BT_KILL_MSK_DEFAULT, + BT_KILL_MSK_NEVER, + BT_KILL_MSK_DEFAULT, + }, +}; + +static const u8 iwl_bt_ack_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT] = { + { + BT_KILL_MSK_ALWAYS, + BT_KILL_MSK_ALWAYS, + BT_KILL_MSK_ALWAYS, + }, + { + BT_KILL_MSK_ALWAYS, + BT_KILL_MSK_ALWAYS, + BT_KILL_MSK_ALWAYS, + }, + { + BT_KILL_MSK_ALWAYS, + BT_KILL_MSK_ALWAYS, + BT_KILL_MSK_ALWAYS, + }, + { + BT_KILL_MSK_DEFAULT, + BT_KILL_MSK_ALWAYS, + BT_KILL_MSK_DEFAULT, + }, +}; + struct corunning_block_luts { u8 range; __le32 lut20[BT_COEX_CORUN_LUT_SIZE]; @@ -633,7 +692,7 @@ int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm) if (IWL_MVM_BT_COEX_TTC) bt_cmd->flags |= cpu_to_le32(BT_COEX_TTC); - if (IWL_MVM_BT_COEX_RRC) + if (iwl_mvm_bt_is_rrc_supported(mvm)) bt_cmd->flags |= cpu_to_le32(BT_COEX_RRC); if (mvm->cfg->bt_shared_single_ant) diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index 9bdfa95d6ce7..5f8afa5f11a3 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c @@ -694,6 +694,9 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif, if (ret) IWL_ERR(mvm, "Failed to send quota: %d\n", ret); + if (iwl_mvm_is_lar_supported(mvm) && iwl_mvm_init_fw_regd(mvm)) + IWL_ERR(mvm, "Failed to initialize D3 LAR information\n"); + return 0; } @@ -1596,7 +1599,7 @@ iwl_mvm_get_wakeup_status(struct iwl_mvm *mvm, struct ieee80211_vif *vif) /* RF-kill already asserted again... */ if (!cmd.resp_pkt) { - ret = -ERFKILL; + fw_status = ERR_PTR(-ERFKILL); goto out_free_resp; } @@ -1605,7 +1608,7 @@ iwl_mvm_get_wakeup_status(struct iwl_mvm *mvm, struct ieee80211_vif *vif) len = iwl_rx_packet_payload_len(cmd.resp_pkt); if (len < status_size) { IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); - ret = -EIO; + fw_status = ERR_PTR(-EIO); goto out_free_resp; } @@ -1613,7 +1616,7 @@ iwl_mvm_get_wakeup_status(struct iwl_mvm *mvm, struct ieee80211_vif *vif) if (len != (status_size + ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4))) { IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); - ret = -EIO; + fw_status = ERR_PTR(-EIO); goto out_free_resp; } @@ -1621,7 +1624,7 @@ iwl_mvm_get_wakeup_status(struct iwl_mvm *mvm, struct ieee80211_vif *vif) out_free_resp: iwl_free_resp(&cmd); - return ret ? ERR_PTR(ret) : fw_status; + return fw_status; } /* releases the MVM mutex */ @@ -1874,6 +1877,12 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) /* query SRAM first in case we want event logging */ iwl_mvm_read_d3_sram(mvm); + /* + * Query the current location and source from the D3 firmware so we + * can play it back when we re-intiailize the D0 firmware + */ + iwl_mvm_update_changed_regdom(mvm); + if (mvm->net_detect) { iwl_mvm_query_netdetect_reasons(mvm, vif); /* has unlocked the mutex, so skip that */ @@ -1883,9 +1892,9 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) #ifdef CONFIG_IWLWIFI_DEBUGFS if (keep) mvm->keep_vif = vif; +#endif /* has unlocked the mutex, so skip that */ goto out_iterate; -#endif } out_unlock: diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index 8cbe77dc1dbb..8c5229892e57 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -562,11 +562,12 @@ static ssize_t iwl_dbgfs_bt_cmd_read(struct file *file, char __user *user_buf, "\tSecondary Channel Bitmap 0x%016llx\n", le64_to_cpu(cmd->bt_secondary_ci)); - pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n"); - pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill Mask 0x%08x\n", - iwl_bt_ctl_kill_msk[mvm->bt_ack_kill_msk[0]]); - pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill Mask 0x%08x\n", - iwl_bt_ctl_kill_msk[mvm->bt_cts_kill_msk[0]]); + pos += scnprintf(buf+pos, bufsz-pos, + "BT Configuration CMD - 0=default, 1=never, 2=always\n"); + pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill msk idx %d\n", + mvm->bt_ack_kill_msk[0]); + pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill msk idx %d\n", + mvm->bt_cts_kill_msk[0]); } else { struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd; @@ -579,21 +580,6 @@ static ssize_t iwl_dbgfs_bt_cmd_read(struct file *file, char __user *user_buf, pos += scnprintf(buf+pos, bufsz-pos, "\tSecondary Channel Bitmap 0x%016llx\n", le64_to_cpu(cmd->bt_secondary_ci)); - - pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n"); - pos += scnprintf(buf+pos, bufsz-pos, - "\tPrimary: ACK Kill Mask 0x%08x\n", - iwl_bt_ctl_kill_msk[mvm->bt_ack_kill_msk[0]]); - pos += scnprintf(buf+pos, bufsz-pos, - "\tPrimary: CTS Kill Mask 0x%08x\n", - iwl_bt_ctl_kill_msk[mvm->bt_cts_kill_msk[0]]); - pos += scnprintf(buf+pos, bufsz-pos, - "\tSecondary: ACK Kill Mask 0x%08x\n", - iwl_bt_ctl_kill_msk[mvm->bt_ack_kill_msk[1]]); - pos += scnprintf(buf+pos, bufsz-pos, - "\tSecondary: CTS Kill Mask 0x%08x\n", - iwl_bt_ctl_kill_msk[mvm->bt_cts_kill_msk[1]]); - } mutex_unlock(&mvm->mutex); diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h index f3b11897991e..d398a6102805 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h @@ -235,36 +235,12 @@ enum iwl_bt_coex_enabled_modules { * struct iwl_bt_coex_cmd - bt coex configuration command * @mode: enum %iwl_bt_coex_mode * @enabled_modules: enum %iwl_bt_coex_enabled_modules - * @max_kill: max count of Tx retries due to kill from PTA - * @override_primary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT - * should be set by default - * @override_secondary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT - * should be set by default - * @bt4_antenna_isolation_thr: antenna threshold value - * @bt4_tx_tx_delta_freq_thr: TxTx delta frequency - * @bt4_tx_rx_max_freq0: TxRx max frequency - * @multiprio_lut: multi priority LUT configuration - * @mplut_prio_boost: BT priority boost registers - * @decision_lut: PTA decision LUT, per Prio-Ch * * The structure is used for the BT_COEX command. */ struct iwl_bt_coex_cmd { __le32 mode; __le32 enabled_modules; - - __le32 max_kill; - __le32 override_primary_lut; - __le32 override_secondary_lut; - __le32 bt4_antenna_isolation_thr; - - __le32 bt4_tx_tx_delta_freq_thr; - __le32 bt4_tx_rx_max_freq0; - - __le32 multiprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE]; - __le32 mplut_prio_boost[BT_COEX_BOOST_SIZE]; - - __le32 decision_lut[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE]; } __packed; /* BT_COEX_CMD_API_S_VER_6 */ /** @@ -280,29 +256,6 @@ struct iwl_bt_coex_corun_lut_update_cmd { } __packed; /* BT_COEX_UPDATE_CORUN_LUT_API_S_VER_1 */ /** - * struct iwl_bt_coex_sw_boost - SW boost values - * @wifi_tx_prio_boost: SW boost of wifi tx priority - * @wifi_rx_prio_boost: SW boost of wifi rx priority - * @kill_ack_msk: kill ACK mask. 1 - Tx ACK, 0 - kill Tx of ACK. - * @kill_cts_msk: kill CTS mask. 1 - Tx CTS, 0 - kill Tx of CTS. - */ -struct iwl_bt_coex_sw_boost { - __le32 wifi_tx_prio_boost; - __le32 wifi_rx_prio_boost; - __le32 kill_ack_msk; - __le32 kill_cts_msk; -}; - -/** - * struct iwl_bt_coex_sw_boost_update_cmd - command to update the SW boost - * @boost_values: check struct %iwl_bt_coex_sw_boost - one for each channel - * primary / secondary / low priority - */ -struct iwl_bt_coex_sw_boost_update_cmd { - struct iwl_bt_coex_sw_boost boost_values[3]; -} __packed; /* BT_COEX_UPDATE_SW_BOOST_S_VER_1 */ - -/** * struct iwl_bt_coex_reduced_txp_update_cmd * @reduced_txp: bit BT_REDUCED_TX_POWER_BIT to enable / disable, rest of the * bits are the sta_id (value) diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index d95b47213731..aab68cbae754 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h @@ -212,6 +212,10 @@ enum { REPLY_RX_MPDU_CMD = 0xc1, BA_NOTIF = 0xc5, + /* Location Aware Regulatory */ + MCC_UPDATE_CMD = 0xc8, + MCC_CHUB_UPDATE_CMD = 0xc9, + MARKER_CMD = 0xcb, /* BT Coex */ @@ -362,7 +366,8 @@ enum { NVM_SECTION_TYPE_CALIBRATION = 4, NVM_SECTION_TYPE_PRODUCTION = 5, NVM_SECTION_TYPE_MAC_OVERRIDE = 11, - NVM_MAX_NUM_SECTIONS = 12, + NVM_SECTION_TYPE_PHY_SKU = 12, + NVM_MAX_NUM_SECTIONS = 13, }; /** @@ -1442,7 +1447,19 @@ enum iwl_sf_scenario { #define SF_W_MARK_LEGACY 4096 #define SF_W_MARK_SCAN 4096 -/* SF Scenarios timers for FULL_ON state (aligned to 32 uSec) */ +/* SF Scenarios timers for default configuration (aligned to 32 uSec) */ +#define SF_SINGLE_UNICAST_IDLE_TIMER_DEF 160 /* 150 uSec */ +#define SF_SINGLE_UNICAST_AGING_TIMER_DEF 400 /* 0.4 mSec */ +#define SF_AGG_UNICAST_IDLE_TIMER_DEF 160 /* 150 uSec */ +#define SF_AGG_UNICAST_AGING_TIMER_DEF 400 /* 0.4 mSec */ +#define SF_MCAST_IDLE_TIMER_DEF 160 /* 150 mSec */ +#define SF_MCAST_AGING_TIMER_DEF 400 /* 0.4 mSec */ +#define SF_BA_IDLE_TIMER_DEF 160 /* 150 uSec */ +#define SF_BA_AGING_TIMER_DEF 400 /* 0.4 mSec */ +#define SF_TX_RE_IDLE_TIMER_DEF 160 /* 150 uSec */ +#define SF_TX_RE_AGING_TIMER_DEF 400 /* 0.4 mSec */ + +/* SF Scenarios timers for BSS MAC configuration (aligned to 32 uSec) */ #define SF_SINGLE_UNICAST_IDLE_TIMER 320 /* 300 uSec */ #define SF_SINGLE_UNICAST_AGING_TIMER 2016 /* 2 mSec */ #define SF_AGG_UNICAST_IDLE_TIMER 320 /* 300 uSec */ @@ -1473,6 +1490,92 @@ struct iwl_sf_cfg_cmd { __le32 full_on_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES]; } __packed; /* SF_CFG_API_S_VER_2 */ +/*********************************** + * Location Aware Regulatory (LAR) API - MCC updates + ***********************************/ + +/** + * struct iwl_mcc_update_cmd - Request the device to update geographic + * regulatory profile according to the given MCC (Mobile Country Code). + * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain. + * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the + * MCC in the cmd response will be the relevant MCC in the NVM. + * @mcc: given mobile country code + * @source_id: the source from where we got the MCC, see iwl_mcc_source + * @reserved: reserved for alignment + */ +struct iwl_mcc_update_cmd { + __le16 mcc; + u8 source_id; + u8 reserved; +} __packed; /* LAR_UPDATE_MCC_CMD_API_S */ + +/** + * iwl_mcc_update_resp - response to MCC_UPDATE_CMD. + * Contains the new channel control profile map, if changed, and the new MCC + * (mobile country code). + * The new MCC may be different than what was requested in MCC_UPDATE_CMD. + * @status: see &enum iwl_mcc_update_status + * @mcc: the new applied MCC + * @cap: capabilities for all channels which matches the MCC + * @source_id: the MCC source, see iwl_mcc_source + * @n_channels: number of channels in @channels_data (may be 14, 39, 50 or 51 + * channels, depending on platform) + * @channels: channel control data map, DWORD for each channel. Only the first + * 16bits are used. + */ +struct iwl_mcc_update_resp { + __le32 status; + __le16 mcc; + u8 cap; + u8 source_id; + __le32 n_channels; + __le32 channels[0]; +} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S */ + +/** + * struct iwl_mcc_chub_notif - chub notifies of mcc change + * (MCC_CHUB_UPDATE_CMD = 0xc9) + * The Chub (Communication Hub, CommsHUB) is a HW component that connects to + * the cellular and connectivity cores that gets updates of the mcc, and + * notifies the ucode directly of any mcc change. + * The ucode requests the driver to request the device to update geographic + * regulatory profile according to the given MCC (Mobile Country Code). + * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain. + * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the + * MCC in the cmd response will be the relevant MCC in the NVM. + * @mcc: given mobile country code + * @source_id: identity of the change originator, see iwl_mcc_source + * @reserved1: reserved for alignment + */ +struct iwl_mcc_chub_notif { + u16 mcc; + u8 source_id; + u8 reserved1; +} __packed; /* LAR_MCC_NOTIFY_S */ + +enum iwl_mcc_update_status { + MCC_RESP_NEW_CHAN_PROFILE, + MCC_RESP_SAME_CHAN_PROFILE, + MCC_RESP_INVALID, + MCC_RESP_NVM_DISABLED, + MCC_RESP_ILLEGAL, + MCC_RESP_LOW_PRIORITY, +}; + +enum iwl_mcc_source { + MCC_SOURCE_OLD_FW = 0, + MCC_SOURCE_ME = 1, + MCC_SOURCE_BIOS = 2, + MCC_SOURCE_3G_LTE_HOST = 3, + MCC_SOURCE_3G_LTE_DEVICE = 4, + MCC_SOURCE_WIFI = 5, + MCC_SOURCE_RESERVED = 6, + MCC_SOURCE_DEFAULT = 7, + MCC_SOURCE_UNINITIALIZED = 8, + MCC_SOURCE_GET_CURRENT = 0x10 +}; + /* DTS measurements */ enum iwl_dts_measurement_flags { diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c index a81da4cde643..6cf7d9837ca5 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/iwlwifi/mvm/fw.c @@ -739,6 +739,16 @@ int iwl_mvm_up(struct iwl_mvm *mvm) if (ret) goto error; + /* + * RTNL is not taken during Ct-kill, but we don't need to scan/Tx + * anyway, so don't init MCC. + */ + if (!test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) { + ret = iwl_mvm_init_mcc(mvm); + if (ret) + goto error; + } + if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) { ret = iwl_mvm_config_scan(mvm); if (ret) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 7396b52262b5..302c8cc50f25 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -86,6 +86,7 @@ #include "iwl-fw-error-dump.h" #include "iwl-prph.h" #include "iwl-csr.h" +#include "iwl-nvm-parse.h" static const struct ieee80211_iface_limit iwl_mvm_limits[] = { { @@ -301,6 +302,109 @@ static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm) } } +struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy, + const char *alpha2, + enum iwl_mcc_source src_id, + bool *changed) +{ + struct ieee80211_regdomain *regd = NULL; + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mcc_update_resp *resp; + + IWL_DEBUG_LAR(mvm, "Getting regdomain data for %s from FW\n", alpha2); + + lockdep_assert_held(&mvm->mutex); + + resp = iwl_mvm_update_mcc(mvm, alpha2, src_id); + if (IS_ERR_OR_NULL(resp)) { + IWL_DEBUG_LAR(mvm, "Could not get update from FW %d\n", + PTR_RET(resp)); + goto out; + } + + if (changed) + *changed = (resp->status == MCC_RESP_NEW_CHAN_PROFILE); + + regd = iwl_parse_nvm_mcc_info(mvm->trans->dev, mvm->cfg, + __le32_to_cpu(resp->n_channels), + resp->channels, + __le16_to_cpu(resp->mcc)); + /* Store the return source id */ + src_id = resp->source_id; + kfree(resp); + if (IS_ERR_OR_NULL(regd)) { + IWL_DEBUG_LAR(mvm, "Could not get parse update from FW %d\n", + PTR_RET(regd)); + goto out; + } + + IWL_DEBUG_LAR(mvm, "setting alpha2 from FW to %s (0x%x, 0x%x) src=%d\n", + regd->alpha2, regd->alpha2[0], regd->alpha2[1], src_id); + mvm->lar_regdom_set = true; + mvm->mcc_src = src_id; + +out: + return regd; +} + +void iwl_mvm_update_changed_regdom(struct iwl_mvm *mvm) +{ + bool changed; + struct ieee80211_regdomain *regd; + + if (!iwl_mvm_is_lar_supported(mvm)) + return; + + regd = iwl_mvm_get_current_regdomain(mvm, &changed); + if (!IS_ERR_OR_NULL(regd)) { + /* only update the regulatory core if changed */ + if (changed) + regulatory_set_wiphy_regd(mvm->hw->wiphy, regd); + + kfree(regd); + } +} + +struct ieee80211_regdomain *iwl_mvm_get_current_regdomain(struct iwl_mvm *mvm, + bool *changed) +{ + return iwl_mvm_get_regdomain(mvm->hw->wiphy, "ZZ", + iwl_mvm_is_wifi_mcc_supported(mvm) ? + MCC_SOURCE_GET_CURRENT : + MCC_SOURCE_OLD_FW, changed); +} + +int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm) +{ + enum iwl_mcc_source used_src; + struct ieee80211_regdomain *regd; + const struct ieee80211_regdomain *r = + rtnl_dereference(mvm->hw->wiphy->regd); + + if (!r) + return 0; + + /* save the last source in case we overwrite it below */ + used_src = mvm->mcc_src; + if (iwl_mvm_is_wifi_mcc_supported(mvm)) { + /* Notify the firmware we support wifi location updates */ + regd = iwl_mvm_get_current_regdomain(mvm, NULL); + if (!IS_ERR_OR_NULL(regd)) + kfree(regd); + } + + /* Now set our last stored MCC and source */ + regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, r->alpha2, used_src, NULL); + if (IS_ERR_OR_NULL(regd)) + return -EIO; + + regulatory_set_wiphy_regd(mvm->hw->wiphy, regd); + kfree(regd); + + return 0; +} + int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) { struct ieee80211_hw *hw = mvm->hw; @@ -356,8 +460,12 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) BIT(NL80211_IFTYPE_ADHOC); hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; - hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | - REGULATORY_DISABLE_BEACON_HINTS; + hw->wiphy->regulatory_flags |= REGULATORY_ENABLE_RELAX_NO_IR; + if (iwl_mvm_is_lar_supported(mvm)) + hw->wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED; + else + hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | + REGULATORY_DISABLE_BEACON_HINTS; if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_GO_UAPSD) hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; @@ -1193,7 +1301,7 @@ static void iwl_mvm_restart_complete(struct iwl_mvm *mvm) clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); iwl_mvm_d0i3_enable_tx(mvm, NULL); - ret = iwl_mvm_update_quotas(mvm, NULL); + ret = iwl_mvm_update_quotas(mvm, false, NULL); if (ret) IWL_ERR(mvm, "Failed to update quotas after restart (%d)\n", ret); @@ -1872,7 +1980,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, sizeof(mvmvif->beacon_stats)); /* add quota for this interface */ - ret = iwl_mvm_update_quotas(mvm, NULL); + ret = iwl_mvm_update_quotas(mvm, true, NULL); if (ret) { IWL_ERR(mvm, "failed to update quotas\n"); return; @@ -1924,7 +2032,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; /* remove quota for this interface */ - ret = iwl_mvm_update_quotas(mvm, NULL); + ret = iwl_mvm_update_quotas(mvm, false, NULL); if (ret) IWL_ERR(mvm, "failed to update quotas\n"); @@ -2043,7 +2151,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, /* power updated needs to be done before quotas */ iwl_mvm_power_update_mac(mvm); - ret = iwl_mvm_update_quotas(mvm, NULL); + ret = iwl_mvm_update_quotas(mvm, false, NULL); if (ret) goto out_quota_failed; @@ -2109,7 +2217,7 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw, if (vif->p2p && mvm->p2p_device_vif) iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false, NULL); - iwl_mvm_update_quotas(mvm, NULL); + iwl_mvm_update_quotas(mvm, false, NULL); iwl_mvm_send_rm_bcast_sta(mvm, vif); iwl_mvm_binding_remove_vif(mvm, vif); @@ -2248,6 +2356,12 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); + if (iwl_mvm_is_lar_supported(mvm) && !mvm->lar_regdom_set) { + IWL_ERR(mvm, "scan while LAR regdomain is not set\n"); + ret = -EBUSY; + goto out; + } + if (mvm->scan_status != IWL_MVM_SCAN_NONE) { ret = -EBUSY; goto out; @@ -2328,25 +2442,35 @@ static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + unsigned long txqs = 0, tids = 0; int tid; + spin_lock_bh(&mvmsta->lock); + for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { + struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; + + if (tid_data->state != IWL_AGG_ON && + tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA) + continue; + + __set_bit(tid_data->txq_id, &txqs); + + if (iwl_mvm_tid_queued(tid_data) == 0) + continue; + + __set_bit(tid, &tids); + } + switch (cmd) { case STA_NOTIFY_SLEEP: if (atomic_read(&mvm->pending_frames[mvmsta->sta_id]) > 0) ieee80211_sta_block_awake(hw, sta, true); - spin_lock_bh(&mvmsta->lock); - for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { - struct iwl_mvm_tid_data *tid_data; - tid_data = &mvmsta->tid_data[tid]; - if (tid_data->state != IWL_AGG_ON && - tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA) - continue; - if (iwl_mvm_tid_queued(tid_data) == 0) - continue; + for_each_set_bit(tid, &tids, IWL_MAX_TID_COUNT) ieee80211_sta_set_buffered(sta, tid, true); - } - spin_unlock_bh(&mvmsta->lock); + + if (txqs) + iwl_trans_freeze_txq_timer(mvm->trans, txqs, true); /* * The fw updates the STA to be asleep. Tx packets on the Tx * queues to this station will not be transmitted. The fw will @@ -2356,11 +2480,15 @@ static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, case STA_NOTIFY_AWAKE: if (WARN_ON(mvmsta->sta_id == IWL_MVM_STATION_COUNT)) break; + + if (txqs) + iwl_trans_freeze_txq_timer(mvm->trans, txqs, false); iwl_mvm_sta_modify_ps_wake(mvm, sta); break; default: break; } + spin_unlock_bh(&mvmsta->lock); } static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw, @@ -2598,6 +2726,12 @@ static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); + if (iwl_mvm_is_lar_supported(mvm) && !mvm->lar_regdom_set) { + IWL_ERR(mvm, "sched-scan while LAR regdomain is not set\n"); + ret = -EBUSY; + goto out; + } + if (!vif->bss_conf.idle) { ret = -EBUSY; goto out; @@ -3159,14 +3293,14 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm, */ if (vif->type == NL80211_IFTYPE_MONITOR) { mvmvif->monitor_active = true; - ret = iwl_mvm_update_quotas(mvm, NULL); + ret = iwl_mvm_update_quotas(mvm, false, NULL); if (ret) goto out_remove_binding; } /* Handle binding during CSA */ if (vif->type == NL80211_IFTYPE_AP) { - iwl_mvm_update_quotas(mvm, NULL); + iwl_mvm_update_quotas(mvm, false, NULL); iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); } @@ -3190,7 +3324,7 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm, iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_CSA); - iwl_mvm_update_quotas(mvm, NULL); + iwl_mvm_update_quotas(mvm, false, NULL); } goto out; @@ -3263,7 +3397,7 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm, break; } - iwl_mvm_update_quotas(mvm, disabled_vif); + iwl_mvm_update_quotas(mvm, false, disabled_vif); iwl_mvm_binding_remove_vif(mvm, vif); out: @@ -3455,7 +3589,7 @@ static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm, mvm->noa_duration = noa_duration; mvm->noa_vif = vif; - return iwl_mvm_update_quotas(mvm, NULL); + return iwl_mvm_update_quotas(mvm, false, NULL); case IWL_MVM_TM_CMD_SET_BEACON_FILTER: /* must be associated client vif - ignore authorized */ if (!vif || vif->type != NL80211_IFTYPE_STATION || diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 95cad68ab069..4b5c8f66df8b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -810,6 +810,9 @@ struct iwl_mvm { /* system time of last beacon (for AP/GO interface) */ u32 ap_last_beacon_gp2; + bool lar_regdom_set; + enum iwl_mcc_source mcc_src; + u8 low_latency_agg_frame_limit; /* TDLS channel switch data */ @@ -910,6 +913,30 @@ static inline bool iwl_mvm_is_d0i3_supported(struct iwl_mvm *mvm) (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_D0I3_SUPPORT); } +static inline bool iwl_mvm_is_lar_supported(struct iwl_mvm *mvm) +{ + bool nvm_lar = mvm->nvm_data->lar_enabled; + bool tlv_lar = mvm->fw->ucode_capa.capa[0] & + IWL_UCODE_TLV_CAPA_LAR_SUPPORT; + + if (iwlwifi_mod_params.lar_disable) + return false; + + /* + * Enable LAR only if it is supported by the FW (TLV) && + * enabled in the NVM + */ + if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000) + return nvm_lar && tlv_lar; + else + return tlv_lar; +} + +static inline bool iwl_mvm_is_wifi_mcc_supported(struct iwl_mvm *mvm) +{ + return mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_WIFI_MCC_UPDATE; +} + static inline bool iwl_mvm_is_scd_cfg_supported(struct iwl_mvm *mvm) { return mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_SCD_CFG; @@ -921,6 +948,12 @@ static inline bool iwl_mvm_bt_is_plcr_supported(struct iwl_mvm *mvm) IWL_MVM_BT_COEX_CORUNNING; } +static inline bool iwl_mvm_bt_is_rrc_supported(struct iwl_mvm *mvm) +{ + return (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_BT_COEX_RRC) && + IWL_MVM_BT_COEX_RRC; +} + extern const u8 iwl_mvm_ac_to_tx_fifo[]; struct iwl_rate_info { @@ -1106,7 +1139,7 @@ int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); /* Quota management */ -int iwl_mvm_update_quotas(struct iwl_mvm *mvm, +int iwl_mvm_update_quotas(struct iwl_mvm *mvm, bool force_upload, struct ieee80211_vif *disabled_vif); /* Scanning */ @@ -1282,17 +1315,6 @@ int iwl_mvm_rx_ant_coupling_notif_old(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); -enum iwl_bt_kill_msk { - BT_KILL_MSK_DEFAULT, - BT_KILL_MSK_NEVER, - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_MAX, -}; - -extern const u8 iwl_bt_ack_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT]; -extern const u8 iwl_bt_cts_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT]; -extern const u32 iwl_bt_ctl_kill_msk[BT_KILL_MSK_MAX]; - /* beacon filtering */ #ifdef CONFIG_IWLWIFI_DEBUGFS void @@ -1389,6 +1411,23 @@ void iwl_mvm_tt_exit(struct iwl_mvm *mvm); void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state); int iwl_mvm_get_temp(struct iwl_mvm *mvm); +/* Location Aware Regulatory */ +struct iwl_mcc_update_resp * +iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, + enum iwl_mcc_source src_id); +int iwl_mvm_init_mcc(struct iwl_mvm *mvm); +int iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd); +struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy, + const char *alpha2, + enum iwl_mcc_source src_id, + bool *changed); +struct ieee80211_regdomain *iwl_mvm_get_current_regdomain(struct iwl_mvm *mvm, + bool *changed); +int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm); +void iwl_mvm_update_changed_regdom(struct iwl_mvm *mvm); + /* smart fifo */ int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *vif, bool added_vif); diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index 5383429d96c1..123e0a16aea8 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -63,12 +63,16 @@ * *****************************************************************************/ #include <linux/firmware.h> +#include <linux/rtnetlink.h> +#include <linux/pci.h> +#include <linux/acpi.h> #include "iwl-trans.h" #include "iwl-csr.h" #include "mvm.h" #include "iwl-eeprom-parse.h" #include "iwl-eeprom-read.h" #include "iwl-nvm-parse.h" +#include "iwl-prph.h" /* Default NVM size to read */ #define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024) @@ -262,7 +266,9 @@ static struct iwl_nvm_data * iwl_parse_nvm_sections(struct iwl_mvm *mvm) { struct iwl_nvm_section *sections = mvm->nvm_sections; - const __le16 *hw, *sw, *calib, *regulatory, *mac_override; + const __le16 *hw, *sw, *calib, *regulatory, *mac_override, *phy_sku; + bool is_family_8000_a_step = false, lar_enabled; + u32 mac_addr0, mac_addr1; /* Checking for required sections */ if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) { @@ -286,22 +292,43 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) "Can't parse mac_address, empty sections\n"); return NULL; } + + if (CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_A_STEP) + is_family_8000_a_step = true; + + /* PHY_SKU section is mandatory in B0 */ + if (!is_family_8000_a_step && + !mvm->nvm_sections[NVM_SECTION_TYPE_PHY_SKU].data) { + IWL_ERR(mvm, + "Can't parse phy_sku in B0, empty sections\n"); + return NULL; + } } if (WARN_ON(!mvm->cfg)) return NULL; + /* read the mac address from WFMP registers */ + mac_addr0 = iwl_trans_read_prph(mvm->trans, WFMP_MAC_ADDR_0); + mac_addr1 = iwl_trans_read_prph(mvm->trans, WFMP_MAC_ADDR_1); + hw = (const __le16 *)sections[mvm->cfg->nvm_hw_section_num].data; sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data; calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data; regulatory = (const __le16 *)sections[NVM_SECTION_TYPE_REGULATORY].data; mac_override = (const __le16 *)sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data; + phy_sku = (const __le16 *)sections[NVM_SECTION_TYPE_PHY_SKU].data; + + lar_enabled = !iwlwifi_mod_params.lar_disable && + (mvm->fw->ucode_capa.capa[0] & + IWL_UCODE_TLV_CAPA_LAR_SUPPORT); return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib, - regulatory, mac_override, - mvm->fw->valid_tx_ant, - mvm->fw->valid_rx_ant); + regulatory, mac_override, phy_sku, + mvm->fw->valid_tx_ant, mvm->fw->valid_rx_ant, + lar_enabled, is_family_8000_a_step, + mac_addr0, mac_addr1); } #define MAX_NVM_FILE_LEN 16384 @@ -570,3 +597,258 @@ int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic) return 0; } + +struct iwl_mcc_update_resp * +iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, + enum iwl_mcc_source src_id) +{ + struct iwl_mcc_update_cmd mcc_update_cmd = { + .mcc = cpu_to_le16(alpha2[0] << 8 | alpha2[1]), + .source_id = (u8)src_id, + }; + struct iwl_mcc_update_resp *mcc_resp, *resp_cp = NULL; + struct iwl_rx_packet *pkt; + struct iwl_host_cmd cmd = { + .id = MCC_UPDATE_CMD, + .flags = CMD_WANT_SKB, + .data = { &mcc_update_cmd }, + }; + + int ret; + u32 status; + int resp_len, n_channels; + u16 mcc; + + if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm))) + return ERR_PTR(-EOPNOTSUPP); + + cmd.len[0] = sizeof(struct iwl_mcc_update_cmd); + + IWL_DEBUG_LAR(mvm, "send MCC update to FW with '%c%c' src = %d\n", + alpha2[0], alpha2[1], src_id); + + ret = iwl_mvm_send_cmd(mvm, &cmd); + if (ret) + return ERR_PTR(ret); + + pkt = cmd.resp_pkt; + if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { + IWL_ERR(mvm, "Bad return from MCC_UPDATE_COMMAND (0x%08X)\n", + pkt->hdr.flags); + ret = -EIO; + goto exit; + } + + /* Extract MCC response */ + mcc_resp = (void *)pkt->data; + status = le32_to_cpu(mcc_resp->status); + + mcc = le16_to_cpu(mcc_resp->mcc); + + /* W/A for a FW/NVM issue - returns 0x00 for the world domain */ + if (mcc == 0) { + mcc = 0x3030; /* "00" - world */ + mcc_resp->mcc = cpu_to_le16(mcc); + } + + n_channels = __le32_to_cpu(mcc_resp->n_channels); + IWL_DEBUG_LAR(mvm, + "MCC response status: 0x%x. new MCC: 0x%x ('%c%c') change: %d n_chans: %d\n", + status, mcc, mcc >> 8, mcc & 0xff, + !!(status == MCC_RESP_NEW_CHAN_PROFILE), n_channels); + + resp_len = sizeof(*mcc_resp) + n_channels * sizeof(__le32); + resp_cp = kmemdup(mcc_resp, resp_len, GFP_KERNEL); + if (!resp_cp) { + ret = -ENOMEM; + goto exit; + } + + ret = 0; +exit: + iwl_free_resp(&cmd); + if (ret) + return ERR_PTR(ret); + return resp_cp; +} + +#ifdef CONFIG_ACPI +#define WRD_METHOD "WRDD" +#define WRDD_WIFI (0x07) +#define WRDD_WIGIG (0x10) + +static u32 iwl_mvm_wrdd_get_mcc(struct iwl_mvm *mvm, union acpi_object *wrdd) +{ + union acpi_object *mcc_pkg, *domain_type, *mcc_value; + u32 i; + + if (wrdd->type != ACPI_TYPE_PACKAGE || + wrdd->package.count < 2 || + wrdd->package.elements[0].type != ACPI_TYPE_INTEGER || + wrdd->package.elements[0].integer.value != 0) { + IWL_DEBUG_LAR(mvm, "Unsupported wrdd structure\n"); + return 0; + } + + for (i = 1 ; i < wrdd->package.count ; ++i) { + mcc_pkg = &wrdd->package.elements[i]; + + if (mcc_pkg->type != ACPI_TYPE_PACKAGE || + mcc_pkg->package.count < 2 || + mcc_pkg->package.elements[0].type != ACPI_TYPE_INTEGER || + mcc_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) { + mcc_pkg = NULL; + continue; + } + + domain_type = &mcc_pkg->package.elements[0]; + if (domain_type->integer.value == WRDD_WIFI) + break; + + mcc_pkg = NULL; + } + + if (mcc_pkg) { + mcc_value = &mcc_pkg->package.elements[1]; + return mcc_value->integer.value; + } + + return 0; +} + +static int iwl_mvm_get_bios_mcc(struct iwl_mvm *mvm, char *mcc) +{ + acpi_handle root_handle; + acpi_handle handle; + struct acpi_buffer wrdd = {ACPI_ALLOCATE_BUFFER, NULL}; + acpi_status status; + u32 mcc_val; + struct pci_dev *pdev = to_pci_dev(mvm->dev); + + root_handle = ACPI_HANDLE(&pdev->dev); + if (!root_handle) { + IWL_DEBUG_LAR(mvm, + "Could not retrieve root port ACPI handle\n"); + return -ENOENT; + } + + /* Get the method's handle */ + status = acpi_get_handle(root_handle, (acpi_string)WRD_METHOD, &handle); + if (ACPI_FAILURE(status)) { + IWL_DEBUG_LAR(mvm, "WRD method not found\n"); + return -ENOENT; + } + + /* Call WRDD with no arguments */ + status = acpi_evaluate_object(handle, NULL, NULL, &wrdd); + if (ACPI_FAILURE(status)) { + IWL_DEBUG_LAR(mvm, "WRDC invocation failed (0x%x)\n", status); + return -ENOENT; + } + + mcc_val = iwl_mvm_wrdd_get_mcc(mvm, wrdd.pointer); + kfree(wrdd.pointer); + if (!mcc_val) + return -ENOENT; + + mcc[0] = (mcc_val >> 8) & 0xff; + mcc[1] = mcc_val & 0xff; + mcc[2] = '\0'; + return 0; +} +#else /* CONFIG_ACPI */ +static int iwl_mvm_get_bios_mcc(struct iwl_mvm *mvm, char *mcc) +{ + return -ENOENT; +} +#endif + +int iwl_mvm_init_mcc(struct iwl_mvm *mvm) +{ + bool tlv_lar; + bool nvm_lar; + int retval; + struct ieee80211_regdomain *regd; + char mcc[3]; + + if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000) { + tlv_lar = mvm->fw->ucode_capa.capa[0] & + IWL_UCODE_TLV_CAPA_LAR_SUPPORT; + nvm_lar = mvm->nvm_data->lar_enabled; + if (tlv_lar != nvm_lar) + IWL_INFO(mvm, + "Conflict between TLV & NVM regarding enabling LAR (TLV = %s NVM =%s)\n", + tlv_lar ? "enabled" : "disabled", + nvm_lar ? "enabled" : "disabled"); + } + + if (!iwl_mvm_is_lar_supported(mvm)) + return 0; + + /* + * During HW restart, only replay the last set MCC to FW. Otherwise, + * queue an update to cfg80211 to retrieve the default alpha2 from FW. + */ + if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { + /* This should only be called during vif up and hold RTNL */ + return iwl_mvm_init_fw_regd(mvm); + } + + /* + * Driver regulatory hint for initial update, this also informs the + * firmware we support wifi location updates. + * Disallow scans that might crash the FW while the LAR regdomain + * is not set. + */ + mvm->lar_regdom_set = false; + + regd = iwl_mvm_get_current_regdomain(mvm, NULL); + if (IS_ERR_OR_NULL(regd)) + return -EIO; + + if (iwl_mvm_is_wifi_mcc_supported(mvm) && + !iwl_mvm_get_bios_mcc(mvm, mcc)) { + kfree(regd); + regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, mcc, + MCC_SOURCE_BIOS, NULL); + if (IS_ERR_OR_NULL(regd)) + return -EIO; + } + + retval = regulatory_set_wiphy_regd_sync_rtnl(mvm->hw->wiphy, regd); + kfree(regd); + return retval; +} + +int iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_mcc_chub_notif *notif = (void *)pkt->data; + enum iwl_mcc_source src; + char mcc[3]; + struct ieee80211_regdomain *regd; + + lockdep_assert_held(&mvm->mutex); + + if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm))) + return 0; + + mcc[0] = notif->mcc >> 8; + mcc[1] = notif->mcc & 0xff; + mcc[2] = '\0'; + src = notif->source_id; + + IWL_DEBUG_LAR(mvm, + "RX: received chub update mcc cmd (mcc '%s' src %d)\n", + mcc, src); + regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, mcc, src, NULL); + if (IS_ERR_OR_NULL(regd)) + return 0; + + regulatory_set_wiphy_regd(mvm->hw->wiphy, regd); + kfree(regd); + + return 0; +} diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index fe40922a6b0d..80121e41ca22 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -82,7 +82,6 @@ #include "rs.h" #include "fw-api-scan.h" #include "time-event.h" -#include "iwl-fw-error-dump.h" #define DRV_DESCRIPTION "The new Intel(R) wireless AGN driver for Linux" MODULE_DESCRIPTION(DRV_DESCRIPTION); @@ -234,6 +233,7 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { iwl_mvm_rx_ant_coupling_notif, true), RX_HANDLER(TIME_EVENT_NOTIFICATION, iwl_mvm_rx_time_event_notif, false), + RX_HANDLER(MCC_CHUB_UPDATE_CMD, iwl_mvm_rx_chub_update_mcc, true), RX_HANDLER(EOSP_NOTIFICATION, iwl_mvm_rx_eosp_notif, false), @@ -358,6 +358,7 @@ static const char *const iwl_mvm_cmd_strings[REPLY_MAX] = { CMD(TDLS_CHANNEL_SWITCH_CMD), CMD(TDLS_CHANNEL_SWITCH_NOTIFICATION), CMD(TDLS_CONFIG_CMD), + CMD(MCC_UPDATE_CMD), }; #undef CMD @@ -871,8 +872,8 @@ static void iwl_mvm_fw_error_dump_wk(struct work_struct *work) /* start recording again if the firmware is not crashed */ WARN_ON_ONCE((!test_bit(STATUS_FW_ERROR, &mvm->trans->status)) && - mvm->fw->dbg_dest_tlv && - iwl_mvm_start_fw_dbg_conf(mvm, mvm->fw_dbg_conf)); + mvm->fw->dbg_dest_tlv && + iwl_mvm_start_fw_dbg_conf(mvm, mvm->fw_dbg_conf)); mutex_unlock(&mvm->mutex); @@ -1270,6 +1271,10 @@ static void iwl_mvm_d0i3_exit_work(struct work_struct *wk) iwl_free_resp(&get_status_cmd); out: iwl_mvm_d0i3_enable_tx(mvm, qos_seq); + + /* the FW might have updated the regdomain */ + iwl_mvm_update_changed_regdom(mvm); + iwl_mvm_unref(mvm, IWL_MVM_REF_EXIT_WORK); mutex_unlock(&mvm->mutex); } diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c index 33bbdde0046f..d2c6ba9d326b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/iwlwifi/mvm/power.c @@ -358,7 +358,7 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK); if (!vif->bss_conf.ps || iwl_mvm_vif_low_latency(mvmvif) || - !mvmvif->pm_enabled || iwl_mvm_tdls_sta_count(mvm, vif)) + !mvmvif->pm_enabled) return; cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK); @@ -639,6 +639,10 @@ static void iwl_mvm_power_set_pm(struct iwl_mvm *mvm, if (vifs->ap_vif) ap_mvmvif = iwl_mvm_vif_from_mac80211(vifs->ap_vif); + /* don't allow PM if any TDLS stations exist */ + if (iwl_mvm_tdls_sta_count(mvm, NULL)) + return; + /* enable PM on bss if bss stand alone */ if (vifs->bss_active && !vifs->p2p_active && !vifs->ap_active) { bss_mvmvif->pm_enabled = true; diff --git a/drivers/net/wireless/iwlwifi/mvm/quota.c b/drivers/net/wireless/iwlwifi/mvm/quota.c index dbb2594390e9..509a66d05245 100644 --- a/drivers/net/wireless/iwlwifi/mvm/quota.c +++ b/drivers/net/wireless/iwlwifi/mvm/quota.c @@ -172,6 +172,7 @@ static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm, } int iwl_mvm_update_quotas(struct iwl_mvm *mvm, + bool force_update, struct ieee80211_vif *disabled_vif) { struct iwl_time_quota_cmd cmd = {}; @@ -309,7 +310,7 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, "zero quota on binding %d\n", i); } - if (!send) { + if (!send && !force_update) { /* don't send a practically unchanged command, the firmware has * to re-initialize a lot of state and that can have an adverse * impact on it diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 6578498dd5af..9140b0b701c7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -1065,6 +1065,37 @@ static inline bool rs_rate_column_match(struct rs_rate *a, && ant_match; } +static inline enum rs_column rs_get_column_from_rate(struct rs_rate *rate) +{ + if (is_legacy(rate)) { + if (rate->ant == ANT_A) + return RS_COLUMN_LEGACY_ANT_A; + + if (rate->ant == ANT_B) + return RS_COLUMN_LEGACY_ANT_B; + + goto err; + } + + if (is_siso(rate)) { + if (rate->ant == ANT_A || rate->stbc || rate->bfer) + return rate->sgi ? RS_COLUMN_SISO_ANT_A_SGI : + RS_COLUMN_SISO_ANT_A; + + if (rate->ant == ANT_B) + return rate->sgi ? RS_COLUMN_SISO_ANT_B_SGI : + RS_COLUMN_SISO_ANT_B; + + goto err; + } + + if (is_mimo(rate)) + return rate->sgi ? RS_COLUMN_MIMO2_SGI : RS_COLUMN_MIMO2; + +err: + return RS_COLUMN_INVALID; +} + static u8 rs_get_tid(struct ieee80211_hdr *hdr) { u8 tid = IWL_MAX_TID_COUNT; @@ -1106,17 +1137,43 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, return; } + /* This packet was aggregated but doesn't carry status info */ + if ((info->flags & IEEE80211_TX_CTL_AMPDU) && + !(info->flags & IEEE80211_TX_STAT_AMPDU)) + return; + + rs_rate_from_ucode_rate(tx_resp_hwrate, info->band, &tx_resp_rate); + #ifdef CONFIG_MAC80211_DEBUGFS - /* Disable last tx check if we are debugging with fixed rate */ + /* Disable last tx check if we are debugging with fixed rate but + * update tx stats */ if (lq_sta->pers.dbg_fixed_rate) { - IWL_DEBUG_RATE(mvm, "Fixed rate. avoid rate scaling\n"); + int index = tx_resp_rate.index; + enum rs_column column; + int attempts, success; + + column = rs_get_column_from_rate(&tx_resp_rate); + if (WARN_ONCE(column == RS_COLUMN_INVALID, + "Can't map rate 0x%x to column", + tx_resp_hwrate)) + return; + + if (info->flags & IEEE80211_TX_STAT_AMPDU) { + attempts = info->status.ampdu_len; + success = info->status.ampdu_ack_len; + } else { + attempts = info->status.rates[0].count; + success = !!(info->flags & IEEE80211_TX_STAT_ACK); + } + + lq_sta->pers.tx_stats[column][index].total += attempts; + lq_sta->pers.tx_stats[column][index].success += success; + + IWL_DEBUG_RATE(mvm, "Fixed rate 0x%x success %d attempts %d\n", + tx_resp_hwrate, success, attempts); return; } #endif - /* This packet was aggregated but doesn't carry status info */ - if ((info->flags & IEEE80211_TX_CTL_AMPDU) && - !(info->flags & IEEE80211_TX_STAT_AMPDU)) - return; if (time_after(jiffies, (unsigned long)(lq_sta->last_tx + @@ -1142,7 +1199,6 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, table = &lq_sta->lq; lq_hwrate = le32_to_cpu(table->rs_table[0]); rs_rate_from_ucode_rate(lq_hwrate, info->band, &lq_rate); - rs_rate_from_ucode_rate(tx_resp_hwrate, info->band, &tx_resp_rate); /* Here we actually compare this rate to the latest LQ command */ if (!rs_rate_equal(&tx_resp_rate, &lq_rate, allow_ant_mismatch)) { @@ -1280,6 +1336,9 @@ static void rs_mac80211_tx_status(void *mvm_r, struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + if (!iwl_mvm_sta_from_mac80211(sta)->vif) + return; + if (!ieee80211_is_data(hdr->frame_control) || info->flags & IEEE80211_TX_CTL_NO_ACK) return; @@ -2513,6 +2572,14 @@ static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct iwl_lq_sta *lq_sta = mvm_sta; + if (sta && !iwl_mvm_sta_from_mac80211(sta)->vif) { + /* if vif isn't initialized mvm doesn't know about + * this station, so don't do anything with the it + */ + sta = NULL; + mvm_sta = NULL; + } + /* TODO: handle rate_idx_mask and rate_idx_mcs_mask */ /* Treat uninitialized rate scaling data same as non-existing. */ @@ -2830,6 +2897,9 @@ static void rs_rate_update(void *mvm_r, (struct iwl_op_mode *)mvm_r; struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + if (!iwl_mvm_sta_from_mac80211(sta)->vif) + return; + /* Stop any ongoing aggregations as rs starts off assuming no agg */ for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) ieee80211_stop_tx_ba_session(sta, tid); @@ -3343,16 +3413,16 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, (is_legacy(rate)) ? "legacy" : is_vht(rate) ? "VHT" : "HT"); if (!is_legacy(rate)) { - desc += sprintf(buff+desc, " %s", + desc += sprintf(buff + desc, " %s", (is_siso(rate)) ? "SISO" : "MIMO2"); - desc += sprintf(buff+desc, " %s", - (is_ht20(rate)) ? "20MHz" : - (is_ht40(rate)) ? "40MHz" : - (is_ht80(rate)) ? "80Mhz" : "BAD BW"); - desc += sprintf(buff+desc, " %s %s %s\n", - (rate->sgi) ? "SGI" : "NGI", - (rate->ldpc) ? "LDPC" : "BCC", - (lq_sta->is_agg) ? "AGG on" : ""); + desc += sprintf(buff + desc, " %s", + (is_ht20(rate)) ? "20MHz" : + (is_ht40(rate)) ? "40MHz" : + (is_ht80(rate)) ? "80Mhz" : "BAD BW"); + desc += sprintf(buff + desc, " %s %s %s\n", + (rate->sgi) ? "SGI" : "NGI", + (rate->ldpc) ? "LDPC" : "BCC", + (lq_sta->is_agg) ? "AGG on" : ""); } desc += sprintf(buff+desc, "last tx rate=0x%X\n", lq_sta->last_rate_n_flags); @@ -3373,13 +3443,13 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, ss_params = le32_to_cpu(lq_sta->lq.ss_params); desc += sprintf(buff+desc, "single stream params: %s%s%s%s\n", (ss_params & LQ_SS_PARAMS_VALID) ? - "VALID," : "INVALID", + "VALID" : "INVALID", (ss_params & LQ_SS_BFER_ALLOWED) ? - "BFER," : "", + ", BFER" : "", (ss_params & LQ_SS_STBC_1SS_ALLOWED) ? - "STBC," : "", + ", STBC" : "", (ss_params & LQ_SS_FORCE) ? - "FORCE" : ""); + ", FORCE" : ""); desc += sprintf(buff+desc, "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n", lq_sta->lq.initial_rate_index[0], @@ -3603,9 +3673,15 @@ static ssize_t iwl_dbgfs_ss_force_write(struct iwl_lq_sta *lq_sta, char *buf, MVM_DEBUGFS_READ_WRITE_FILE_OPS(ss_force, 32); -static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir) +static void rs_add_debugfs(void *mvm, void *priv_sta, struct dentry *dir) { - struct iwl_lq_sta *lq_sta = mvm_sta; + struct iwl_lq_sta *lq_sta = priv_sta; + struct iwl_mvm_sta *mvmsta; + + mvmsta = container_of(lq_sta, struct iwl_mvm_sta, lq_sta); + + if (!mvmsta->vif) + return; debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir, lq_sta, &rs_sta_dbgfs_scale_table_ops); diff --git a/drivers/net/wireless/iwlwifi/mvm/sf.c b/drivers/net/wireless/iwlwifi/mvm/sf.c index 7eb78e2c240a..b0f59fdd287c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sf.c +++ b/drivers/net/wireless/iwlwifi/mvm/sf.c @@ -99,7 +99,35 @@ static void iwl_mvm_bound_iface_iterator(void *_data, u8 *mac, /* * Aging and idle timeouts for the different possible scenarios - * in SF_FULL_ON state. + * in default configuration + */ +static const +__le32 sf_full_timeout_def[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = { + { + cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER_DEF), + cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER_DEF) + }, + { + cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER_DEF), + cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER_DEF) + }, + { + cpu_to_le32(SF_MCAST_AGING_TIMER_DEF), + cpu_to_le32(SF_MCAST_IDLE_TIMER_DEF) + }, + { + cpu_to_le32(SF_BA_AGING_TIMER_DEF), + cpu_to_le32(SF_BA_IDLE_TIMER_DEF) + }, + { + cpu_to_le32(SF_TX_RE_AGING_TIMER_DEF), + cpu_to_le32(SF_TX_RE_IDLE_TIMER_DEF) + }, +}; + +/* + * Aging and idle timeouts for the different possible scenarios + * in single BSS MAC configuration. */ static const __le32 sf_full_timeout[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = { { @@ -124,7 +152,8 @@ static const __le32 sf_full_timeout[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = { }, }; -static void iwl_mvm_fill_sf_command(struct iwl_sf_cfg_cmd *sf_cmd, +static void iwl_mvm_fill_sf_command(struct iwl_mvm *mvm, + struct iwl_sf_cfg_cmd *sf_cmd, struct ieee80211_sta *sta) { int i, j, watermark; @@ -163,24 +192,38 @@ static void iwl_mvm_fill_sf_command(struct iwl_sf_cfg_cmd *sf_cmd, cpu_to_le32(SF_LONG_DELAY_AGING_TIMER); } } - BUILD_BUG_ON(sizeof(sf_full_timeout) != - sizeof(__le32) * SF_NUM_SCENARIO * SF_NUM_TIMEOUT_TYPES); - memcpy(sf_cmd->full_on_timeouts, sf_full_timeout, - sizeof(sf_full_timeout)); + if (sta || IWL_UCODE_API(mvm->fw->ucode_ver) < 13) { + BUILD_BUG_ON(sizeof(sf_full_timeout) != + sizeof(__le32) * SF_NUM_SCENARIO * + SF_NUM_TIMEOUT_TYPES); + + memcpy(sf_cmd->full_on_timeouts, sf_full_timeout, + sizeof(sf_full_timeout)); + } else { + BUILD_BUG_ON(sizeof(sf_full_timeout_def) != + sizeof(__le32) * SF_NUM_SCENARIO * + SF_NUM_TIMEOUT_TYPES); + + memcpy(sf_cmd->full_on_timeouts, sf_full_timeout_def, + sizeof(sf_full_timeout_def)); + } + } static int iwl_mvm_sf_config(struct iwl_mvm *mvm, u8 sta_id, enum iwl_sf_state new_state) { struct iwl_sf_cfg_cmd sf_cmd = { - .state = cpu_to_le32(new_state), + .state = cpu_to_le32(SF_FULL_ON), }; struct ieee80211_sta *sta; int ret = 0; - if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF && - mvm->cfg->disable_dummy_notification) + if (IWL_UCODE_API(mvm->fw->ucode_ver) < 13) + sf_cmd.state = cpu_to_le32(new_state); + + if (mvm->cfg->disable_dummy_notification) sf_cmd.state |= cpu_to_le32(SF_CFG_DUMMY_NOTIF_OFF); /* @@ -192,6 +235,8 @@ static int iwl_mvm_sf_config(struct iwl_mvm *mvm, u8 sta_id, switch (new_state) { case SF_UNINIT: + if (IWL_UCODE_API(mvm->fw->ucode_ver) >= 13) + iwl_mvm_fill_sf_command(mvm, &sf_cmd, NULL); break; case SF_FULL_ON: if (sta_id == IWL_MVM_STATION_COUNT) { @@ -206,11 +251,11 @@ static int iwl_mvm_sf_config(struct iwl_mvm *mvm, u8 sta_id, rcu_read_unlock(); return -EINVAL; } - iwl_mvm_fill_sf_command(&sf_cmd, sta); + iwl_mvm_fill_sf_command(mvm, &sf_cmd, sta); rcu_read_unlock(); break; case SF_INIT_OFF: - iwl_mvm_fill_sf_command(&sf_cmd, NULL); + iwl_mvm_fill_sf_command(mvm, &sf_cmd, NULL); break; default: WARN_ONCE(1, "Invalid state: %d. not sending Smart Fifo cmd\n", diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 5c23cddaaae3..50f9288368af 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -273,7 +273,7 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, else sta_id = mvm_sta->sta_id; - if (WARN_ON_ONCE(sta_id == IWL_MVM_STATION_COUNT)) + if (sta_id == IWL_MVM_STATION_COUNT) return -ENOSPC; spin_lock_init(&mvm_sta->lock); @@ -1681,9 +1681,6 @@ void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm, }; int ret; - if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_DISABLE_STA_TX)) - return; - ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd); if (ret) IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c index f8d6f306dd76..8d179ab67cc2 100644 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c @@ -197,6 +197,8 @@ iwl_mvm_te_handle_notify_csa(struct iwl_mvm *mvm, struct iwl_time_event_notif *notif) { if (!le32_to_cpu(notif->status)) { + if (te_data->vif->type == NL80211_IFTYPE_STATION) + ieee80211_connection_loss(te_data->vif); IWL_DEBUG_TE(mvm, "CSA time event failed to start\n"); iwl_mvm_te_clear_data(mvm, te_data); return; @@ -261,17 +263,23 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm, "TE ended - current time %lu, estimated end %lu\n", jiffies, te_data->end_jiffies); - if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) { + switch (te_data->vif->type) { + case NL80211_IFTYPE_P2P_DEVICE: ieee80211_remain_on_channel_expired(mvm->hw); iwl_mvm_roc_finished(mvm); + break; + case NL80211_IFTYPE_STATION: + /* + * By now, we should have finished association + * and know the dtim period. + */ + iwl_mvm_te_check_disconnect(mvm, te_data->vif, + "No association and the time event is over already..."); + break; + default: + break; } - /* - * By now, we should have finished association - * and know the dtim period. - */ - iwl_mvm_te_check_disconnect(mvm, te_data->vif, - "No association and the time event is over already..."); iwl_mvm_te_clear_data(mvm, te_data); } else if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_START) { te_data->running = true; diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index 7906b97c81b9..ba34dda1ae36 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -953,8 +953,10 @@ int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, mvmsta = iwl_mvm_sta_from_mac80211(sta); tid_data = &mvmsta->tid_data[tid]; - if (WARN_ONCE(tid_data->txq_id != scd_flow, "Q %d, tid %d, flow %d", - tid_data->txq_id, tid, scd_flow)) { + if (tid_data->txq_id != scd_flow) { + IWL_ERR(mvm, + "invalid BA notification: Q %d, tid %d, flow %d\n", + tid_data->txq_id, tid, scd_flow); rcu_read_unlock(); return 0; } diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index 2b9de63951e6..435faee0a28e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -857,7 +857,7 @@ int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif, mvmvif->low_latency = value; - res = iwl_mvm_update_quotas(mvm, NULL); + res = iwl_mvm_update_quotas(mvm, false, NULL); if (res) return res; diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c index dbd6bcf52205..b18569734922 100644 --- a/drivers/net/wireless/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/iwlwifi/pcie/drv.c @@ -368,10 +368,12 @@ static const struct pci_device_id iwl_hw_card_ids[] = { /* 3165 Series */ {IWL_PCI_DEVICE(0x3165, 0x4010, iwl3165_2ac_cfg)}, {IWL_PCI_DEVICE(0x3165, 0x4012, iwl3165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x3165, 0x4110, iwl3165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x3165, 0x4210, iwl3165_2ac_cfg)}, {IWL_PCI_DEVICE(0x3165, 0x4410, iwl3165_2ac_cfg)}, {IWL_PCI_DEVICE(0x3165, 0x4510, iwl3165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x3165, 0x4110, iwl3165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x3166, 0x4310, iwl3165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x3166, 0x4210, iwl3165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x3165, 0x8010, iwl3165_2ac_cfg)}, /* 7265 Series */ {IWL_PCI_DEVICE(0x095A, 0x5010, iwl7265_2ac_cfg)}, @@ -413,10 +415,35 @@ static const struct pci_device_id iwl_hw_card_ids[] = { /* 8000 Series */ {IWL_PCI_DEVICE(0x24F3, 0x0010, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x0004, iwl8260_2n_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x1010, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0110, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x1110, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0050, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0250, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x1050, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0150, iwl8260_2ac_cfg)}, {IWL_PCI_DEVICE(0x24F4, 0x0030, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F4, 0x1130, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F4, 0x1030, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0xC010, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0xD010, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F4, 0xC030, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F4, 0xD030, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0xC050, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0xD050, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x8010, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x9010, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F4, 0x8030, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F4, 0x9030, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x8050, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x9050, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0004, iwl8260_2n_cfg)}, {IWL_PCI_DEVICE(0x24F5, 0x0010, iwl4165_2ac_cfg)}, {IWL_PCI_DEVICE(0x24F6, 0x0030, iwl4165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0810, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0910, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0850, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0950, iwl8260_2ac_cfg)}, #endif /* CONFIG_IWLMVM */ {0} diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index cae0eb8835ce..01996c9d98a7 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -217,6 +217,8 @@ struct iwl_pcie_txq_scratch_buf { * @active: stores if queue is active * @ampdu: true if this queue is an ampdu queue for an specific RA/TID * @wd_timeout: queue watchdog timeout (jiffies) - per queue + * @frozen: tx stuck queue timer is frozen + * @frozen_expiry_remainder: remember how long until the timer fires * * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame * descriptors) and required locking structures. @@ -228,9 +230,11 @@ struct iwl_txq { dma_addr_t scratchbufs_dma; struct iwl_pcie_txq_entry *entries; spinlock_t lock; + unsigned long frozen_expiry_remainder; struct timer_list stuck_timer; struct iwl_trans_pcie *trans_pcie; bool need_update; + bool frozen; u8 active; bool ampdu; unsigned long wd_timeout; diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index f31a94160771..dc247325d8d7 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -682,6 +682,43 @@ static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num, return ret; } +/* + * Driver Takes the ownership on secure machine before FW load + * and prevent race with the BT load. + * W/A for ROM bug. (should be remove in the next Si step) + */ +static int iwl_pcie_rsa_race_bug_wa(struct iwl_trans *trans) +{ + u32 val, loop = 1000; + + /* Check the RSA semaphore is accessible - if not, we are in trouble */ + val = iwl_read_prph(trans, PREG_AUX_BUS_WPROT_0); + if (val & (BIT(1) | BIT(17))) { + IWL_ERR(trans, + "can't access the RSA semaphore it is write protected\n"); + return 0; + } + + /* take ownership on the AUX IF */ + iwl_write_prph(trans, WFPM_CTRL_REG, WFPM_AUX_CTL_AUX_IF_MAC_OWNER_MSK); + iwl_write_prph(trans, AUX_MISC_MASTER1_EN, AUX_MISC_MASTER1_EN_SBE_MSK); + + do { + iwl_write_prph(trans, AUX_MISC_MASTER1_SMPHR_STATUS, 0x1); + val = iwl_read_prph(trans, AUX_MISC_MASTER1_SMPHR_STATUS); + if (val == 0x1) { + iwl_write_prph(trans, RSA_ENABLE, 0); + return 0; + } + + udelay(10); + loop--; + } while (loop > 0); + + IWL_ERR(trans, "Failed to take ownership on secure machine\n"); + return -EIO; +} + static int iwl_pcie_load_cpu_sections_8000b(struct iwl_trans *trans, const struct fw_img *image, int cpu, @@ -901,6 +938,11 @@ static int iwl_pcie_load_given_ucode_8000b(struct iwl_trans *trans, if (trans->dbg_dest_tlv) iwl_pcie_apply_destination(trans); + /* TODO: remove in the next Si step */ + ret = iwl_pcie_rsa_race_bug_wa(trans); + if (ret) + return ret; + /* configure the ucode to be ready to get the secured image */ /* release CPU reset */ iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT); @@ -1462,6 +1504,60 @@ static int iwl_trans_pcie_write_mem(struct iwl_trans *trans, u32 addr, return ret; } +static void iwl_trans_pcie_freeze_txq_timer(struct iwl_trans *trans, + unsigned long txqs, + bool freeze) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int queue; + + for_each_set_bit(queue, &txqs, BITS_PER_LONG) { + struct iwl_txq *txq = &trans_pcie->txq[queue]; + unsigned long now; + + spin_lock_bh(&txq->lock); + + now = jiffies; + + if (txq->frozen == freeze) + goto next_queue; + + IWL_DEBUG_TX_QUEUES(trans, "%s TXQ %d\n", + freeze ? "Freezing" : "Waking", queue); + + txq->frozen = freeze; + + if (txq->q.read_ptr == txq->q.write_ptr) + goto next_queue; + + if (freeze) { + if (unlikely(time_after(now, + txq->stuck_timer.expires))) { + /* + * The timer should have fired, maybe it is + * spinning right now on the lock. + */ + goto next_queue; + } + /* remember how long until the timer fires */ + txq->frozen_expiry_remainder = + txq->stuck_timer.expires - now; + del_timer(&txq->stuck_timer); + goto next_queue; + } + + /* + * Wake a non-empty queue -> arm timer with the + * remainder before it froze + */ + mod_timer(&txq->stuck_timer, + now + txq->frozen_expiry_remainder); + +next_queue: + spin_unlock_bh(&txq->lock); + } +} + #define IWL_FLUSH_WAIT_MS 2000 static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, u32 txq_bm) @@ -1713,7 +1809,7 @@ static ssize_t iwl_dbgfs_tx_queue_read(struct file *file, int ret; size_t bufsz; - bufsz = sizeof(char) * 64 * trans->cfg->base_params->num_of_queues; + bufsz = sizeof(char) * 75 * trans->cfg->base_params->num_of_queues; if (!trans_pcie->txq) return -EAGAIN; @@ -1726,11 +1822,11 @@ static ssize_t iwl_dbgfs_tx_queue_read(struct file *file, txq = &trans_pcie->txq[cnt]; q = &txq->q; pos += scnprintf(buf + pos, bufsz - pos, - "hwq %.2d: read=%u write=%u use=%d stop=%d need_update=%d%s\n", + "hwq %.2d: read=%u write=%u use=%d stop=%d need_update=%d frozen=%d%s\n", cnt, q->read_ptr, q->write_ptr, !!test_bit(cnt, trans_pcie->queue_used), !!test_bit(cnt, trans_pcie->queue_stopped), - txq->need_update, + txq->need_update, txq->frozen, (cnt == trans_pcie->cmd_queue ? " HCMD" : "")); } ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); @@ -1961,24 +2057,25 @@ static const struct { { .start = 0x00a01c7c, .end = 0x00a01c7c }, { .start = 0x00a01c28, .end = 0x00a01c54 }, { .start = 0x00a01c5c, .end = 0x00a01c5c }, - { .start = 0x00a01c84, .end = 0x00a01c84 }, + { .start = 0x00a01c60, .end = 0x00a01cdc }, { .start = 0x00a01ce0, .end = 0x00a01d0c }, { .start = 0x00a01d18, .end = 0x00a01d20 }, { .start = 0x00a01d2c, .end = 0x00a01d30 }, { .start = 0x00a01d40, .end = 0x00a01d5c }, { .start = 0x00a01d80, .end = 0x00a01d80 }, - { .start = 0x00a01d98, .end = 0x00a01d98 }, + { .start = 0x00a01d98, .end = 0x00a01d9c }, + { .start = 0x00a01da8, .end = 0x00a01da8 }, + { .start = 0x00a01db8, .end = 0x00a01df4 }, { .start = 0x00a01dc0, .end = 0x00a01dfc }, { .start = 0x00a01e00, .end = 0x00a01e2c }, { .start = 0x00a01e40, .end = 0x00a01e60 }, + { .start = 0x00a01e68, .end = 0x00a01e6c }, + { .start = 0x00a01e74, .end = 0x00a01e74 }, { .start = 0x00a01e84, .end = 0x00a01e90 }, { .start = 0x00a01e9c, .end = 0x00a01ec4 }, - { .start = 0x00a01ed0, .end = 0x00a01ed0 }, - { .start = 0x00a01f00, .end = 0x00a01f14 }, - { .start = 0x00a01f44, .end = 0x00a01f58 }, - { .start = 0x00a01f80, .end = 0x00a01fa8 }, - { .start = 0x00a01fb0, .end = 0x00a01fbc }, - { .start = 0x00a01ff8, .end = 0x00a01ffc }, + { .start = 0x00a01ed0, .end = 0x00a01ee0 }, + { .start = 0x00a01f00, .end = 0x00a01f1c }, + { .start = 0x00a01f44, .end = 0x00a01ffc }, { .start = 0x00a02000, .end = 0x00a02048 }, { .start = 0x00a02068, .end = 0x00a020f0 }, { .start = 0x00a02100, .end = 0x00a02118 }, @@ -2305,6 +2402,7 @@ static const struct iwl_trans_ops trans_ops_pcie = { .dbgfs_register = iwl_trans_pcie_dbgfs_register, .wait_tx_queue_empty = iwl_trans_pcie_wait_txq_empty, + .freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer, .write8 = iwl_trans_pcie_write8, .write32 = iwl_trans_pcie_write32, @@ -2423,10 +2521,45 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, * "dash" value). To keep hw_rev backwards compatible - we'll store it * in the old format. */ - if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) + if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) { + unsigned long flags; + int ret; + trans->hw_rev = (trans->hw_rev & 0xfff0) | (CSR_HW_REV_STEP(trans->hw_rev << 2) << 2); + /* + * in-order to recognize C step driver should read chip version + * id located at the AUX bus MISC address space. + */ + iwl_set_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + udelay(2); + + ret = iwl_poll_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + 25000); + if (ret < 0) { + IWL_DEBUG_INFO(trans, "Failed to wake up the nic\n"); + goto out_pci_disable_msi; + } + + if (iwl_trans_grab_nic_access(trans, false, &flags)) { + u32 hw_step; + + hw_step = __iwl_read_prph(trans, WFPM_CTRL_REG); + hw_step |= ENABLE_WFPM; + __iwl_write_prph(trans, WFPM_CTRL_REG, hw_step); + hw_step = __iwl_read_prph(trans, AUX_MISC_REG); + hw_step = (hw_step >> HW_STEP_LOCATION_BITS) & 0xF; + if (hw_step == 0x3) + trans->hw_rev = (trans->hw_rev & 0xFFFFFFF3) | + (SILICON_C_STEP << 2); + iwl_trans_release_nic_access(trans, &flags); + } + } + trans->hw_id = (pdev->device << 16) + pdev->subsystem_device; snprintf(trans->hw_id_str, sizeof(trans->hw_id_str), "PCI ID: 0x%04X:0x%04X", pdev->device, pdev->subsystem_device); diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index af0bce736358..06952aadfd7b 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -725,33 +725,50 @@ void iwl_trans_pcie_tx_reset(struct iwl_trans *trans) iwl_pcie_tx_start(trans, 0); } +static void iwl_pcie_tx_stop_fh(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + unsigned long flags; + int ch, ret; + u32 mask = 0; + + spin_lock(&trans_pcie->irq_lock); + + if (!iwl_trans_grab_nic_access(trans, false, &flags)) + goto out; + + /* Stop each Tx DMA channel */ + for (ch = 0; ch < FH_TCSR_CHNL_NUM; ch++) { + iwl_write32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0); + mask |= FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch); + } + + /* Wait for DMA channels to be idle */ + ret = iwl_poll_bit(trans, FH_TSSR_TX_STATUS_REG, mask, mask, 5000); + if (ret < 0) + IWL_ERR(trans, + "Failing on timeout while stopping DMA channel %d [0x%08x]\n", + ch, iwl_read32(trans, FH_TSSR_TX_STATUS_REG)); + + iwl_trans_release_nic_access(trans, &flags); + +out: + spin_unlock(&trans_pcie->irq_lock); +} + /* * iwl_pcie_tx_stop - Stop all Tx DMA channels */ int iwl_pcie_tx_stop(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int ch, txq_id, ret; + int txq_id; /* Turn off all Tx DMA fifos */ - spin_lock(&trans_pcie->irq_lock); - iwl_scd_deactivate_fifos(trans); - /* Stop each Tx DMA channel, and wait for it to be idle */ - for (ch = 0; ch < FH_TCSR_CHNL_NUM; ch++) { - iwl_write_direct32(trans, - FH_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0); - ret = iwl_poll_direct_bit(trans, FH_TSSR_TX_STATUS_REG, - FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch), 1000); - if (ret < 0) - IWL_ERR(trans, - "Failing on timeout while stopping DMA channel %d [0x%08x]\n", - ch, - iwl_read_direct32(trans, - FH_TSSR_TX_STATUS_REG)); - } - spin_unlock(&trans_pcie->irq_lock); + /* Turn off all Tx DMA channels */ + iwl_pcie_tx_stop_fh(trans); /* * This function can be called before the op_mode disabled the @@ -912,10 +929,19 @@ error: static inline void iwl_pcie_txq_progress(struct iwl_txq *txq) { + lockdep_assert_held(&txq->lock); + if (!txq->wd_timeout) return; /* + * station is asleep and we send data - that must + * be uAPSD or PS-Poll. Don't rearm the timer. + */ + if (txq->frozen) + return; + + /* * if empty delete timer, otherwise move timer forward * since we're making progress on this queue */ @@ -1248,6 +1274,9 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id, SCD_TX_STTS_QUEUE_OFFSET(txq_id); static const u32 zero_val[4] = {}; + trans_pcie->txq[txq_id].frozen_expiry_remainder = 0; + trans_pcie->txq[txq_id].frozen = false; + /* * Upon HW Rfkill - we stop the device, and then stop the queues * in the op_mode. Just for the sake of the simplicity of the op_mode, diff --git a/drivers/net/wireless/libertas_tf/if_usb.c b/drivers/net/wireless/libertas_tf/if_usb.c index d576dd6665d3..1a20cee5febe 100644 --- a/drivers/net/wireless/libertas_tf/if_usb.c +++ b/drivers/net/wireless/libertas_tf/if_usb.c @@ -365,7 +365,6 @@ static int if_usb_reset_device(struct if_usb_card *cardp) return ret; } -EXPORT_SYMBOL_GPL(if_usb_reset_device); /** * usb_tx_block - transfer data to the device @@ -907,7 +906,6 @@ restart: lbtf_deb_leave_args(LBTF_DEB_USB, "ret %d", ret); return ret; } -EXPORT_SYMBOL_GPL(if_usb_prog_firmware); #define if_usb_suspend NULL diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c index 543148d27b01..433bd6837c79 100644 --- a/drivers/net/wireless/mwifiex/11n.c +++ b/drivers/net/wireless/mwifiex/11n.c @@ -159,6 +159,7 @@ int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv, int tid; struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp; struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl; + struct mwifiex_ra_list_tbl *ra_list; u16 block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set); add_ba_rsp->ssn = cpu_to_le16((le16_to_cpu(add_ba_rsp->ssn)) @@ -166,7 +167,13 @@ int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv, tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) >> BLOCKACKPARAM_TID_POS; + ra_list = mwifiex_wmm_get_ralist_node(priv, tid, add_ba_rsp-> + peer_mac_addr); if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) { + if (ra_list) { + ra_list->ba_status = BA_SETUP_NONE; + ra_list->amsdu_in_ampdu = false; + } mwifiex_del_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr, TYPE_DELBA_SENT, true); if (add_ba_rsp->add_rsp_result != BA_RESULT_TIMEOUT) @@ -185,6 +192,10 @@ int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv, tx_ba_tbl->amsdu = true; else tx_ba_tbl->amsdu = false; + if (ra_list) { + ra_list->amsdu_in_ampdu = tx_ba_tbl->amsdu; + ra_list->ba_status = BA_SETUP_COMPLETE; + } } else { dev_err(priv->adapter->dev, "BA stream not created\n"); } @@ -515,6 +526,7 @@ void mwifiex_create_ba_tbl(struct mwifiex_private *priv, u8 *ra, int tid, enum mwifiex_ba_status ba_status) { struct mwifiex_tx_ba_stream_tbl *new_node; + struct mwifiex_ra_list_tbl *ra_list; unsigned long flags; if (!mwifiex_get_ba_tbl(priv, tid, ra)) { @@ -522,7 +534,11 @@ void mwifiex_create_ba_tbl(struct mwifiex_private *priv, u8 *ra, int tid, GFP_ATOMIC); if (!new_node) return; - + ra_list = mwifiex_wmm_get_ralist_node(priv, tid, ra); + if (ra_list) { + ra_list->ba_status = ba_status; + ra_list->amsdu_in_ampdu = false; + } INIT_LIST_HEAD(&new_node->list); new_node->tid = tid; diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/mwifiex/11n.h index 8e2e39422ad8..afdd58aa90de 100644 --- a/drivers/net/wireless/mwifiex/11n.h +++ b/drivers/net/wireless/mwifiex/11n.h @@ -77,22 +77,6 @@ mwifiex_is_station_ampdu_allowed(struct mwifiex_private *priv, return (node->ampdu_sta[tid] != BA_STREAM_NOT_ALLOWED) ? true : false; } -/* This function checks whether AMSDU is allowed for BA stream. */ -static inline u8 -mwifiex_is_amsdu_in_ampdu_allowed(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ptr, int tid) -{ - struct mwifiex_tx_ba_stream_tbl *tx_tbl; - - if (is_broadcast_ether_addr(ptr->ra)) - return false; - tx_tbl = mwifiex_get_ba_tbl(priv, tid, ptr->ra); - if (tx_tbl) - return tx_tbl->amsdu; - - return false; -} - /* This function checks whether AMPDU is allowed or not for a particular TID. */ static inline u8 mwifiex_is_ampdu_allowed(struct mwifiex_private *priv, @@ -182,22 +166,6 @@ mwifiex_find_stream_to_delete(struct mwifiex_private *priv, int ptr_tid, } /* - * This function checks whether BA stream is set up or not. - */ -static inline int -mwifiex_is_ba_stream_setup(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ptr, int tid) -{ - struct mwifiex_tx_ba_stream_tbl *tx_tbl; - - tx_tbl = mwifiex_get_ba_tbl(priv, tid, ptr->ra); - if (tx_tbl && IS_BASTREAM_SETUP(tx_tbl)) - return true; - - return false; -} - -/* * This function checks whether associated station is 11n enabled */ static inline int mwifiex_is_sta_11n_enabled(struct mwifiex_private *priv, diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c index 9b983b5cebbd..6183e255e62a 100644 --- a/drivers/net/wireless/mwifiex/11n_aggr.c +++ b/drivers/net/wireless/mwifiex/11n_aggr.c @@ -170,7 +170,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, struct mwifiex_adapter *adapter = priv->adapter; struct sk_buff *skb_aggr, *skb_src; struct mwifiex_txinfo *tx_info_aggr, *tx_info_src; - int pad = 0, ret; + int pad = 0, aggr_num = 0, ret; struct mwifiex_tx_param tx_param; struct txpd *ptx_pd = NULL; struct timeval tv; @@ -184,7 +184,8 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, } tx_info_src = MWIFIEX_SKB_TXCB(skb_src); - skb_aggr = dev_alloc_skb(adapter->tx_buf_size); + skb_aggr = mwifiex_alloc_dma_align_buf(adapter->tx_buf_size, + GFP_ATOMIC | GFP_DMA); if (!skb_aggr) { dev_err(adapter->dev, "%s: alloc skb_aggr\n", __func__); spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, @@ -200,6 +201,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, if (tx_info_src->flags & MWIFIEX_BUF_FLAG_TDLS_PKT) tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; + tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_AGGR_PKT; skb_aggr->priority = skb_src->priority; do_gettimeofday(&tv); @@ -211,11 +213,9 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, break; skb_src = skb_dequeue(&pra_list->skb_head); - pra_list->total_pkt_count--; - atomic_dec(&priv->wmm.tx_pkts_queued); - + aggr_num++; spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); mwifiex_11n_form_amsdu_pkt(skb_aggr, skb_src, &pad); @@ -251,6 +251,12 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, ptx_pd = (struct txpd *)skb_aggr->data; skb_push(skb_aggr, headroom); + tx_info_aggr->aggr_num = aggr_num * 2; + if (adapter->data_sent || adapter->tx_lock_flag) { + atomic_add(aggr_num * 2, &adapter->tx_queued); + skb_queue_tail(&adapter->tx_data_q, skb_aggr); + return 0; + } if (adapter->iface_type == MWIFIEX_USB) { adapter->data_sent = true; diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c index a2e8817b56d8..f75f8acfaca0 100644 --- a/drivers/net/wireless/mwifiex/11n_rxreorder.c +++ b/drivers/net/wireless/mwifiex/11n_rxreorder.c @@ -659,6 +659,7 @@ mwifiex_del_ba_tbl(struct mwifiex_private *priv, int tid, u8 *peer_mac, { struct mwifiex_rx_reorder_tbl *tbl; struct mwifiex_tx_ba_stream_tbl *ptx_tbl; + struct mwifiex_ra_list_tbl *ra_list; u8 cleanup_rx_reorder_tbl; unsigned long flags; @@ -686,7 +687,11 @@ mwifiex_del_ba_tbl(struct mwifiex_private *priv, int tid, u8 *peer_mac, "event: TID, RA not found in table\n"); return; } - + ra_list = mwifiex_wmm_get_ralist_node(priv, tid, peer_mac); + if (ra_list) { + ra_list->amsdu_in_ampdu = false; + ra_list->ba_status = BA_SETUP_NONE; + } spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, ptx_tbl); spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 6f8993c12373..bf9020ff2d33 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -717,6 +717,9 @@ mwifiex_cfg80211_init_p2p_go(struct mwifiex_private *priv) static int mwifiex_deinit_priv_params(struct mwifiex_private *priv) { + struct mwifiex_adapter *adapter = priv->adapter; + unsigned long flags; + priv->mgmt_frame_mask = 0; if (mwifiex_send_cmd(priv, HostCmd_CMD_MGMT_FRAME_REG, HostCmd_ACT_GEN_SET, 0, @@ -727,6 +730,25 @@ static int mwifiex_deinit_priv_params(struct mwifiex_private *priv) } mwifiex_deauthenticate(priv, NULL); + + spin_lock_irqsave(&adapter->main_proc_lock, flags); + adapter->main_locked = true; + if (adapter->mwifiex_processing) { + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + flush_workqueue(adapter->workqueue); + } else { + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + } + + spin_lock_irqsave(&adapter->rx_proc_lock, flags); + adapter->rx_locked = true; + if (adapter->rx_processing) { + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + flush_workqueue(adapter->rx_workqueue); + } else { + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + } + mwifiex_free_priv(priv); priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; @@ -740,6 +762,9 @@ mwifiex_init_new_priv_params(struct mwifiex_private *priv, struct net_device *dev, enum nl80211_iftype type) { + struct mwifiex_adapter *adapter = priv->adapter; + unsigned long flags; + mwifiex_init_priv(priv); priv->bss_mode = type; @@ -770,6 +795,14 @@ mwifiex_init_new_priv_params(struct mwifiex_private *priv, return -EOPNOTSUPP; } + spin_lock_irqsave(&adapter->main_proc_lock, flags); + adapter->main_locked = false; + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + + spin_lock_irqsave(&adapter->rx_proc_lock, flags); + adapter->rx_locked = false; + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + return 0; } @@ -2733,24 +2766,71 @@ mwifiex_is_pattern_supported(struct cfg80211_pkt_pattern *pat, s8 *byte_seq, } #ifdef CONFIG_PM -static int mwifiex_set_mef_filter(struct mwifiex_private *priv, - struct cfg80211_wowlan *wowlan) +static void mwifiex_set_auto_arp_mef_entry(struct mwifiex_private *priv, + struct mwifiex_mef_entry *mef_entry) +{ + int i, filt_num = 0, num_ipv4 = 0; + struct in_device *in_dev; + struct in_ifaddr *ifa; + __be32 ips[MWIFIEX_MAX_SUPPORTED_IPADDR]; + struct mwifiex_adapter *adapter = priv->adapter; + + mef_entry->mode = MEF_MODE_HOST_SLEEP; + mef_entry->action = MEF_ACTION_AUTO_ARP; + + /* Enable ARP offload feature */ + memset(ips, 0, sizeof(ips)); + for (i = 0; i < MWIFIEX_MAX_BSS_NUM; i++) { + if (adapter->priv[i]->netdev) { + in_dev = __in_dev_get_rtnl(adapter->priv[i]->netdev); + if (!in_dev) + continue; + ifa = in_dev->ifa_list; + if (!ifa || !ifa->ifa_local) + continue; + ips[i] = ifa->ifa_local; + num_ipv4++; + } + } + + for (i = 0; i < num_ipv4; i++) { + if (!ips[i]) + continue; + mef_entry->filter[filt_num].repeat = 1; + memcpy(mef_entry->filter[filt_num].byte_seq, + (u8 *)&ips[i], sizeof(ips[i])); + mef_entry->filter[filt_num]. + byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = + sizeof(ips[i]); + mef_entry->filter[filt_num].offset = 46; + mef_entry->filter[filt_num].filt_type = TYPE_EQ; + if (filt_num) { + mef_entry->filter[filt_num].filt_action = + TYPE_OR; + } + filt_num++; + } + + mef_entry->filter[filt_num].repeat = 1; + mef_entry->filter[filt_num].byte_seq[0] = 0x08; + mef_entry->filter[filt_num].byte_seq[1] = 0x06; + mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = 2; + mef_entry->filter[filt_num].offset = 20; + mef_entry->filter[filt_num].filt_type = TYPE_EQ; + mef_entry->filter[filt_num].filt_action = TYPE_AND; +} + +static int mwifiex_set_wowlan_mef_entry(struct mwifiex_private *priv, + struct mwifiex_ds_mef_cfg *mef_cfg, + struct mwifiex_mef_entry *mef_entry, + struct cfg80211_wowlan *wowlan) { int i, filt_num = 0, ret = 0; bool first_pat = true; u8 byte_seq[MWIFIEX_MEF_MAX_BYTESEQ + 1]; const u8 ipv4_mc_mac[] = {0x33, 0x33}; const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e}; - struct mwifiex_ds_mef_cfg mef_cfg; - struct mwifiex_mef_entry *mef_entry; - mef_entry = kzalloc(sizeof(*mef_entry), GFP_KERNEL); - if (!mef_entry) - return -ENOMEM; - - memset(&mef_cfg, 0, sizeof(mef_cfg)); - mef_cfg.num_entries = 1; - mef_cfg.mef_entry = mef_entry; mef_entry->mode = MEF_MODE_HOST_SLEEP; mef_entry->action = MEF_ACTION_ALLOW_AND_WAKEUP_HOST; @@ -2767,20 +2847,19 @@ static int mwifiex_set_mef_filter(struct mwifiex_private *priv, if (!wowlan->patterns[i].pkt_offset) { if (!(byte_seq[0] & 0x01) && (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 1)) { - mef_cfg.criteria |= MWIFIEX_CRITERIA_UNICAST; + mef_cfg->criteria |= MWIFIEX_CRITERIA_UNICAST; continue; } else if (is_broadcast_ether_addr(byte_seq)) { - mef_cfg.criteria |= MWIFIEX_CRITERIA_BROADCAST; + mef_cfg->criteria |= MWIFIEX_CRITERIA_BROADCAST; continue; } else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) && (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 2)) || (!memcmp(byte_seq, ipv6_mc_mac, 3) && (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 3))) { - mef_cfg.criteria |= MWIFIEX_CRITERIA_MULTICAST; + mef_cfg->criteria |= MWIFIEX_CRITERIA_MULTICAST; continue; } } - mef_entry->filter[filt_num].repeat = 1; mef_entry->filter[filt_num].offset = wowlan->patterns[i].pkt_offset; @@ -2797,7 +2876,7 @@ static int mwifiex_set_mef_filter(struct mwifiex_private *priv, } if (wowlan->magic_pkt) { - mef_cfg.criteria |= MWIFIEX_CRITERIA_UNICAST; + mef_cfg->criteria |= MWIFIEX_CRITERIA_UNICAST; mef_entry->filter[filt_num].repeat = 16; memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr, ETH_ALEN); @@ -2818,6 +2897,34 @@ static int mwifiex_set_mef_filter(struct mwifiex_private *priv, mef_entry->filter[filt_num].filt_type = TYPE_EQ; mef_entry->filter[filt_num].filt_action = TYPE_OR; } + return ret; +} + +static int mwifiex_set_mef_filter(struct mwifiex_private *priv, + struct cfg80211_wowlan *wowlan) +{ + int ret = 0, num_entries = 1; + struct mwifiex_ds_mef_cfg mef_cfg; + struct mwifiex_mef_entry *mef_entry; + + if (wowlan->n_patterns || wowlan->magic_pkt) + num_entries++; + + mef_entry = kcalloc(num_entries, sizeof(*mef_entry), GFP_KERNEL); + if (!mef_entry) + return -ENOMEM; + + memset(&mef_cfg, 0, sizeof(mef_cfg)); + mef_cfg.criteria |= MWIFIEX_CRITERIA_BROADCAST | + MWIFIEX_CRITERIA_UNICAST; + mef_cfg.num_entries = num_entries; + mef_cfg.mef_entry = mef_entry; + + mwifiex_set_auto_arp_mef_entry(priv, &mef_entry[0]); + + if (wowlan->n_patterns || wowlan->magic_pkt) + ret = mwifiex_set_wowlan_mef_entry(priv, &mef_cfg, + &mef_entry[1], wowlan); if (!mef_cfg.criteria) mef_cfg.criteria = MWIFIEX_CRITERIA_BROADCAST | @@ -2825,8 +2932,8 @@ static int mwifiex_set_mef_filter(struct mwifiex_private *priv, MWIFIEX_CRITERIA_MULTICAST; ret = mwifiex_send_cmd(priv, HostCmd_CMD_MEF_CFG, - HostCmd_ACT_GEN_SET, 0, &mef_cfg, true); - + HostCmd_ACT_GEN_SET, 0, + &mef_cfg, true); kfree(mef_entry); return ret; } @@ -2836,27 +2943,33 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, { struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); struct mwifiex_ds_hs_cfg hs_cfg; - int ret = 0; - struct mwifiex_private *priv = - mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); + int i, ret = 0; + struct mwifiex_private *priv; + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + mwifiex_abort_cac(priv); + } + + mwifiex_cancel_all_pending_cmd(adapter); if (!wowlan) { dev_warn(adapter->dev, "None of the WOWLAN triggers enabled\n"); return 0; } + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); + if (!priv->media_connected) { dev_warn(adapter->dev, "Can not configure WOWLAN in disconnected state\n"); return 0; } - if (wowlan->n_patterns || wowlan->magic_pkt) { - ret = mwifiex_set_mef_filter(priv, wowlan); - if (ret) { - dev_err(adapter->dev, "Failed to set MEF filter\n"); - return ret; - } + ret = mwifiex_set_mef_filter(priv, wowlan); + if (ret) { + dev_err(adapter->dev, "Failed to set MEF filter\n"); + return ret; } if (wowlan->disconnect) { diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h index cf2fa110e251..38f24e0427d2 100644 --- a/drivers/net/wireless/mwifiex/decl.h +++ b/drivers/net/wireless/mwifiex/decl.h @@ -83,6 +83,7 @@ #define MWIFIEX_BUF_FLAG_TDLS_PKT BIT(2) #define MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS BIT(3) #define MWIFIEX_BUF_FLAG_ACTION_TX_STATUS BIT(4) +#define MWIFIEX_BUF_FLAG_AGGR_PKT BIT(5) #define MWIFIEX_BRIDGED_PKTS_THR_HIGH 1024 #define MWIFIEX_BRIDGED_PKTS_THR_LOW 128 @@ -111,6 +112,11 @@ #define MWIFIEX_A_BAND_START_FREQ 5000 +/* SDIO Aggr data packet special info */ +#define SDIO_MAX_AGGR_BUF_SIZE (256 * 255) +#define BLOCK_NUMBER_OFFSET 15 +#define SDIO_HEADER_OFFSET 28 + enum mwifiex_bss_type { MWIFIEX_BSS_TYPE_STA = 0, MWIFIEX_BSS_TYPE_UAP = 1, @@ -168,10 +174,11 @@ struct mwifiex_wait_queue { }; struct mwifiex_rxinfo { + struct sk_buff *parent; u8 bss_num; u8 bss_type; - struct sk_buff *parent; u8 use_count; + u8 buf_type; }; struct mwifiex_txinfo { @@ -179,6 +186,7 @@ struct mwifiex_txinfo { u8 flags; u8 bss_num; u8 bss_type; + u8 aggr_num; u32 pkt_len; u8 ack_frame_id; u64 cookie; diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index df553e86a0ad..59d8964dd0dc 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -197,6 +197,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define ISSUPP_11NENABLED(FwCapInfo) (FwCapInfo & BIT(11)) #define ISSUPP_TDLS_ENABLED(FwCapInfo) (FwCapInfo & BIT(14)) +#define ISSUPP_SDIO_SPA_ENABLED(FwCapInfo) (FwCapInfo & BIT(16)) #define MWIFIEX_DEF_HT_CAP (IEEE80211_HT_CAP_DSSSCCK40 | \ (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | \ @@ -353,6 +354,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define HostCmd_CMD_REMAIN_ON_CHAN 0x010d #define HostCmd_CMD_11AC_CFG 0x0112 #define HostCmd_CMD_TDLS_OPER 0x0122 +#define HostCmd_CMD_SDIO_SP_RX_AGGR_CFG 0x0223 #define PROTOCOL_NO_SECURITY 0x01 #define PROTOCOL_STATIC_WEP 0x02 @@ -523,9 +525,11 @@ enum P2P_MODES { #define TYPE_OR (MAX_OPERAND+5) #define MEF_MODE_HOST_SLEEP 1 #define MEF_ACTION_ALLOW_AND_WAKEUP_HOST 3 +#define MEF_ACTION_AUTO_ARP 0x10 #define MWIFIEX_CRITERIA_BROADCAST BIT(0) #define MWIFIEX_CRITERIA_UNICAST BIT(1) #define MWIFIEX_CRITERIA_MULTICAST BIT(3) +#define MWIFIEX_MAX_SUPPORTED_IPADDR 4 #define ACT_TDLS_DELETE 0x00 #define ACT_TDLS_CREATE 0x01 @@ -1240,6 +1244,12 @@ struct host_cmd_ds_chan_rpt_event { u8 tlvbuf[0]; } __packed; +struct host_cmd_sdio_sp_rx_aggr_cfg { + u8 action; + u8 enable; + __le16 block_size; +} __packed; + struct mwifiex_fixed_bcn_param { __le64 timestamp; __le16 beacon_period; @@ -1962,6 +1972,7 @@ struct host_cmd_ds_command { struct host_cmd_ds_coalesce_cfg coalesce_cfg; struct host_cmd_ds_tdls_oper tdls_oper; struct host_cmd_ds_chan_rpt_req chan_rpt_req; + struct host_cmd_sdio_sp_rx_aggr_cfg sdio_rx_aggr_cfg; } params; } __packed; diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index 0153ce6d5879..e12192f5cfad 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -266,18 +266,15 @@ static void mwifiex_init_adapter(struct mwifiex_adapter *adapter) mwifiex_wmm_init(adapter); - if (adapter->sleep_cfm) { - sleep_cfm_buf = (struct mwifiex_opt_sleep_confirm *) - adapter->sleep_cfm->data; - memset(sleep_cfm_buf, 0, adapter->sleep_cfm->len); - sleep_cfm_buf->command = - cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH); - sleep_cfm_buf->size = - cpu_to_le16(adapter->sleep_cfm->len); - sleep_cfm_buf->result = 0; - sleep_cfm_buf->action = cpu_to_le16(SLEEP_CONFIRM); - sleep_cfm_buf->resp_ctrl = cpu_to_le16(RESP_NEEDED); - } + sleep_cfm_buf = (struct mwifiex_opt_sleep_confirm *) + adapter->sleep_cfm->data; + memset(sleep_cfm_buf, 0, adapter->sleep_cfm->len); + sleep_cfm_buf->command = cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH); + sleep_cfm_buf->size = cpu_to_le16(adapter->sleep_cfm->len); + sleep_cfm_buf->result = 0; + sleep_cfm_buf->action = cpu_to_le16(SLEEP_CONFIRM); + sleep_cfm_buf->resp_ctrl = cpu_to_le16(RESP_NEEDED); + memset(&adapter->sleep_params, 0, sizeof(adapter->sleep_params)); memset(&adapter->sleep_period, 0, sizeof(adapter->sleep_period)); adapter->tx_lock_flag = false; @@ -481,6 +478,7 @@ int mwifiex_init_lock_list(struct mwifiex_adapter *adapter) spin_lock_init(&adapter->rx_proc_lock); skb_queue_head_init(&adapter->rx_data_q); + skb_queue_head_init(&adapter->tx_data_q); for (i = 0; i < adapter->priv_num; ++i) { INIT_LIST_HEAD(&adapter->bss_prio_tbl[i].bss_prio_head); @@ -688,6 +686,10 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter) } } + atomic_set(&adapter->tx_queued, 0); + while ((skb = skb_dequeue(&adapter->tx_data_q))) + mwifiex_write_data_complete(adapter, skb, 0, 0); + spin_lock_irqsave(&adapter->rx_proc_lock, flags); while ((skb = skb_dequeue(&adapter->rx_data_q))) { diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index d73a9217b9da..03a95c7d34bf 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -131,10 +131,39 @@ static int mwifiex_unregister(struct mwifiex_adapter *adapter) return 0; } +void mwifiex_queue_main_work(struct mwifiex_adapter *adapter) +{ + unsigned long flags; + + spin_lock_irqsave(&adapter->main_proc_lock, flags); + if (adapter->mwifiex_processing) { + adapter->more_task_flag = true; + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + } else { + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + queue_work(adapter->workqueue, &adapter->main_work); + } +} +EXPORT_SYMBOL_GPL(mwifiex_queue_main_work); + +static void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter) +{ + unsigned long flags; + + spin_lock_irqsave(&adapter->rx_proc_lock, flags); + if (adapter->rx_processing) { + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + } else { + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + queue_work(adapter->rx_workqueue, &adapter->rx_work); + } +} + static int mwifiex_process_rx(struct mwifiex_adapter *adapter) { unsigned long flags; struct sk_buff *skb; + struct mwifiex_rxinfo *rx_info; spin_lock_irqsave(&adapter->rx_proc_lock, flags); if (adapter->rx_processing || adapter->rx_locked) { @@ -154,9 +183,16 @@ static int mwifiex_process_rx(struct mwifiex_adapter *adapter) if (adapter->if_ops.submit_rem_rx_urbs) adapter->if_ops.submit_rem_rx_urbs(adapter); adapter->delay_main_work = false; - queue_work(adapter->workqueue, &adapter->main_work); + mwifiex_queue_main_work(adapter); + } + rx_info = MWIFIEX_SKB_RXCB(skb); + if (rx_info->buf_type == MWIFIEX_TYPE_AGGR_DATA) { + if (adapter->if_ops.deaggr_pkt) + adapter->if_ops.deaggr_pkt(adapter, skb); + dev_kfree_skb_any(skb); + } else { + mwifiex_handle_rx_packet(adapter, skb); } - mwifiex_handle_rx_packet(adapter, skb); } spin_lock_irqsave(&adapter->rx_proc_lock, flags); adapter->rx_processing = false; @@ -189,7 +225,7 @@ int mwifiex_main_process(struct mwifiex_adapter *adapter) spin_lock_irqsave(&adapter->main_proc_lock, flags); /* Check if already processing */ - if (adapter->mwifiex_processing) { + if (adapter->mwifiex_processing || adapter->main_locked) { adapter->more_task_flag = true; spin_unlock_irqrestore(&adapter->main_proc_lock, flags); goto exit_main_proc; @@ -214,9 +250,7 @@ process_start: if (atomic_read(&adapter->rx_pending) >= HIGH_RX_PENDING && adapter->iface_type != MWIFIEX_USB) { adapter->delay_main_work = true; - if (!adapter->rx_processing) - queue_work(adapter->rx_workqueue, - &adapter->rx_work); + mwifiex_queue_rx_work(adapter); break; } @@ -229,13 +263,14 @@ process_start: } if (adapter->rx_work_enabled && adapter->data_received) - queue_work(adapter->rx_workqueue, &adapter->rx_work); + mwifiex_queue_rx_work(adapter); /* Need to wake up the card ? */ if ((adapter->ps_state == PS_STATE_SLEEP) && (adapter->pm_wakeup_card_req && !adapter->pm_wakeup_fw_try) && (is_command_pending(adapter) || + !skb_queue_empty(&adapter->tx_data_q) || !mwifiex_wmm_lists_empty(adapter))) { adapter->pm_wakeup_fw_try = true; mod_timer(&adapter->wakeup_timer, jiffies + (HZ*3)); @@ -247,7 +282,7 @@ process_start: if (IS_CARD_RX_RCVD(adapter)) { adapter->data_received = false; adapter->pm_wakeup_fw_try = false; - del_timer_sync(&adapter->wakeup_timer); + del_timer(&adapter->wakeup_timer); if (adapter->ps_state == PS_STATE_SLEEP) adapter->ps_state = PS_STATE_AWAKE; } else { @@ -260,7 +295,8 @@ process_start: if ((!adapter->scan_chan_gap_enabled && adapter->scan_processing) || adapter->data_sent || - mwifiex_wmm_lists_empty(adapter)) { + (mwifiex_wmm_lists_empty(adapter) && + skb_queue_empty(&adapter->tx_data_q))) { if (adapter->cmd_sent || adapter->curr_cmd || (!is_command_pending(adapter))) break; @@ -312,6 +348,20 @@ process_start: if ((adapter->scan_chan_gap_enabled || !adapter->scan_processing) && + !adapter->data_sent && + !skb_queue_empty(&adapter->tx_data_q)) { + mwifiex_process_tx_queue(adapter); + if (adapter->hs_activated) { + adapter->is_hs_configured = false; + mwifiex_hs_activated_event + (mwifiex_get_priv + (adapter, MWIFIEX_BSS_ROLE_ANY), + false); + } + } + + if ((adapter->scan_chan_gap_enabled || + !adapter->scan_processing) && !adapter->data_sent && !mwifiex_wmm_lists_empty(adapter)) { mwifiex_wmm_process_tx(adapter); if (adapter->hs_activated) { @@ -325,7 +375,8 @@ process_start: if (adapter->delay_null_pkt && !adapter->cmd_sent && !adapter->curr_cmd && !is_command_pending(adapter) && - mwifiex_wmm_lists_empty(adapter)) { + (mwifiex_wmm_lists_empty(adapter) && + skb_queue_empty(&adapter->tx_data_q))) { if (!mwifiex_send_null_packet (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET | @@ -606,7 +657,7 @@ int mwifiex_queue_tx_pkt(struct mwifiex_private *priv, struct sk_buff *skb) atomic_inc(&priv->adapter->tx_pending); mwifiex_wmm_add_buf_txqueue(priv, skb); - queue_work(priv->adapter->workqueue, &priv->adapter->main_work); + mwifiex_queue_main_work(priv->adapter); return 0; } @@ -1098,9 +1149,6 @@ mwifiex_add_card(void *card, struct semaphore *sem, INIT_WORK(&adapter->rx_work, mwifiex_rx_work_queue); } - if (adapter->if_ops.iface_work) - INIT_WORK(&adapter->iface_work, adapter->if_ops.iface_work); - /* Register the device. Fill up the private data structure with relevant information from the card. */ if (adapter->if_ops.register_dev(adapter)) { diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index ad8db61aeeef..fe1256044a6c 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -35,6 +35,7 @@ #include <linux/ctype.h> #include <linux/of.h> #include <linux/idr.h> +#include <linux/inetdevice.h> #include "decl.h" #include "ioctl.h" @@ -58,6 +59,8 @@ enum { #define MWIFIEX_MAX_AP 64 +#define MWIFIEX_MAX_PKTS_TXQ 16 + #define MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT (5 * HZ) #define MWIFIEX_TIMER_10S 10000 @@ -118,6 +121,7 @@ enum { #define MWIFIEX_TYPE_CMD 1 #define MWIFIEX_TYPE_DATA 0 +#define MWIFIEX_TYPE_AGGR_DATA 10 #define MWIFIEX_TYPE_EVENT 3 #define MAX_BITMAP_RATES_SIZE 18 @@ -210,6 +214,12 @@ struct mwifiex_tx_aggr { u8 amsdu; }; +enum mwifiex_ba_status { + BA_SETUP_NONE = 0, + BA_SETUP_INPROGRESS, + BA_SETUP_COMPLETE +}; + struct mwifiex_ra_list_tbl { struct list_head list; struct sk_buff_head skb_head; @@ -218,6 +228,8 @@ struct mwifiex_ra_list_tbl { u16 max_amsdu; u16 ba_pkt_count; u8 ba_packet_thr; + enum mwifiex_ba_status ba_status; + u8 amsdu_in_ampdu; u16 total_pkt_count; bool tdls_link; }; @@ -601,11 +613,6 @@ struct mwifiex_private { struct mwifiex_11h_intf_state state_11h; }; -enum mwifiex_ba_status { - BA_SETUP_NONE = 0, - BA_SETUP_INPROGRESS, - BA_SETUP_COMPLETE -}; struct mwifiex_tx_ba_stream_tbl { struct list_head list; @@ -738,6 +745,7 @@ struct mwifiex_if_ops { int (*clean_pcie_ring) (struct mwifiex_adapter *adapter); void (*iface_work)(struct work_struct *work); void (*submit_rem_rx_urbs)(struct mwifiex_adapter *adapter); + void (*deaggr_pkt)(struct mwifiex_adapter *, struct sk_buff *); }; struct mwifiex_adapter { @@ -771,6 +779,7 @@ struct mwifiex_adapter { bool rx_processing; bool delay_main_work; bool rx_locked; + bool main_locked; struct mwifiex_bss_prio_tbl bss_prio_tbl[MWIFIEX_MAX_BSS_NUM]; /* spin lock for init/shutdown */ spinlock_t mwifiex_lock; @@ -780,6 +789,8 @@ struct mwifiex_adapter { u8 more_task_flag; u16 tx_buf_size; u16 curr_tx_buf_size; + bool sdio_rx_aggr_enable; + u16 sdio_rx_block_size; u32 ioport; enum MWIFIEX_HARDWARE_STATUS hw_status; u16 number_of_antenna; @@ -814,6 +825,8 @@ struct mwifiex_adapter { spinlock_t scan_pending_q_lock; /* spin lock for RX processing routine */ spinlock_t rx_proc_lock; + struct sk_buff_head tx_data_q; + atomic_t tx_queued; u32 scan_processing; u16 region_code; struct mwifiex_802_11d_domain_reg domain_reg; @@ -885,8 +898,6 @@ struct mwifiex_adapter { bool ext_scan; u8 fw_api_ver; u8 key_api_major_ver, key_api_minor_ver; - struct work_struct iface_work; - unsigned long iface_work_flags; struct memory_type_mapping *mem_type_mapping_tbl; u8 num_mem_types; u8 curr_mem_idx; @@ -900,6 +911,8 @@ struct mwifiex_adapter { bool auto_tdls; }; +void mwifiex_process_tx_queue(struct mwifiex_adapter *adapter); + int mwifiex_init_lock_list(struct mwifiex_adapter *adapter); void mwifiex_set_trans_start(struct net_device *dev); @@ -1422,7 +1435,8 @@ u8 mwifiex_adjust_data_rate(struct mwifiex_private *priv, u8 rx_rate, u8 ht_info); void mwifiex_dump_drv_info(struct mwifiex_adapter *adapter); -void *mwifiex_alloc_rx_buf(int rx_len, gfp_t flags); +void *mwifiex_alloc_dma_align_buf(int rx_len, gfp_t flags); +void mwifiex_queue_main_work(struct mwifiex_adapter *adapter); #ifdef CONFIG_DEBUG_FS void mwifiex_debugfs_init(void); diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index 4b463c3b9906..bcc7751d883c 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -234,8 +234,6 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev) if (!adapter || !adapter->priv_num) return; - cancel_work_sync(&adapter->iface_work); - if (user_rmmod) { #ifdef CONFIG_PM_SLEEP if (adapter->is_suspended) @@ -498,8 +496,8 @@ static int mwifiex_init_rxq_ring(struct mwifiex_adapter *adapter) for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { /* Allocate skb here so that firmware can DMA data from it */ - skb = mwifiex_alloc_rx_buf(MWIFIEX_RX_DATA_BUF_SIZE, - GFP_KERNEL | GFP_DMA); + skb = mwifiex_alloc_dma_align_buf(MWIFIEX_RX_DATA_BUF_SIZE, + GFP_KERNEL | GFP_DMA); if (!skb) { dev_err(adapter->dev, "Unable to allocate skb for RX ring.\n"); @@ -1298,8 +1296,8 @@ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter) } } - skb_tmp = mwifiex_alloc_rx_buf(MWIFIEX_RX_DATA_BUF_SIZE, - GFP_KERNEL | GFP_DMA); + skb_tmp = mwifiex_alloc_dma_align_buf(MWIFIEX_RX_DATA_BUF_SIZE, + GFP_KERNEL | GFP_DMA); if (!skb_tmp) { dev_err(adapter->dev, "Unable to allocate skb.\n"); @@ -2101,7 +2099,7 @@ static irqreturn_t mwifiex_pcie_interrupt(int irq, void *context) goto exit; mwifiex_interrupt_status(adapter); - queue_work(adapter->workqueue, &adapter->main_work); + mwifiex_queue_main_work(adapter); exit: return IRQ_HANDLED; @@ -2373,25 +2371,26 @@ done: adapter->curr_mem_idx = 0; } +static unsigned long iface_work_flags; +static struct mwifiex_adapter *save_adapter; static void mwifiex_pcie_work(struct work_struct *work) { - struct mwifiex_adapter *adapter = - container_of(work, struct mwifiex_adapter, iface_work); - if (test_and_clear_bit(MWIFIEX_IFACE_WORK_FW_DUMP, - &adapter->iface_work_flags)) - mwifiex_pcie_fw_dump_work(adapter); + &iface_work_flags)) + mwifiex_pcie_fw_dump_work(save_adapter); } +static DECLARE_WORK(pcie_work, mwifiex_pcie_work); /* This function dumps FW information */ static void mwifiex_pcie_fw_dump(struct mwifiex_adapter *adapter) { - if (test_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags)) + save_adapter = adapter; + if (test_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &iface_work_flags)) return; - set_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags); + set_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &iface_work_flags); - schedule_work(&adapter->iface_work); + schedule_work(&pcie_work); } /* @@ -2619,7 +2618,6 @@ static struct mwifiex_if_ops pcie_ops = { .init_fw_port = mwifiex_pcie_init_fw_port, .clean_pcie_ring = mwifiex_clean_pcie_ring_buf, .fw_dump = mwifiex_pcie_fw_dump, - .iface_work = mwifiex_pcie_work, }; /* @@ -2665,6 +2663,7 @@ static void mwifiex_pcie_cleanup_module(void) /* Set the flag as user is removing this module. */ user_rmmod = 1; + cancel_work_sync(&pcie_work); pci_unregister_driver(&mwifiex_pcie); } diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 57d85ab442bf..d10320f89bc1 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -47,6 +47,7 @@ static u8 user_rmmod; static struct mwifiex_if_ops sdio_ops; +static unsigned long iface_work_flags; static struct semaphore add_remove_card_sem; @@ -200,8 +201,6 @@ mwifiex_sdio_remove(struct sdio_func *func) if (!adapter || !adapter->priv_num) return; - cancel_work_sync(&adapter->iface_work); - if (user_rmmod) { if (adapter->is_suspended) mwifiex_sdio_resume(adapter->dev); @@ -1043,6 +1042,59 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter, } /* + * This function decode sdio aggreation pkt. + * + * Based on the the data block size and pkt_len, + * skb data will be decoded to few packets. + */ +static void mwifiex_deaggr_sdio_pkt(struct mwifiex_adapter *adapter, + struct sk_buff *skb) +{ + u32 total_pkt_len, pkt_len; + struct sk_buff *skb_deaggr; + u32 pkt_type; + u16 blk_size; + u8 blk_num; + u8 *data; + + data = skb->data; + total_pkt_len = skb->len; + + while (total_pkt_len >= (SDIO_HEADER_OFFSET + INTF_HEADER_LEN)) { + if (total_pkt_len < adapter->sdio_rx_block_size) + break; + blk_num = *(data + BLOCK_NUMBER_OFFSET); + blk_size = adapter->sdio_rx_block_size * blk_num; + if (blk_size > total_pkt_len) { + dev_err(adapter->dev, "%s: error in pkt,\t" + "blk_num=%d, blk_size=%d, total_pkt_len=%d\n", + __func__, blk_num, blk_size, total_pkt_len); + break; + } + pkt_len = le16_to_cpu(*(__le16 *)(data + SDIO_HEADER_OFFSET)); + pkt_type = le16_to_cpu(*(__le16 *)(data + SDIO_HEADER_OFFSET + + 2)); + if ((pkt_len + SDIO_HEADER_OFFSET) > blk_size) { + dev_err(adapter->dev, "%s: error in pkt,\t" + "pkt_len=%d, blk_size=%d\n", + __func__, pkt_len, blk_size); + break; + } + skb_deaggr = mwifiex_alloc_dma_align_buf(pkt_len, + GFP_KERNEL | GFP_DMA); + if (!skb_deaggr) + break; + skb_put(skb_deaggr, pkt_len); + memcpy(skb_deaggr->data, data + SDIO_HEADER_OFFSET, pkt_len); + skb_pull(skb_deaggr, INTF_HEADER_LEN); + + mwifiex_handle_rx_packet(adapter, skb_deaggr); + data += blk_size; + total_pkt_len -= blk_size; + } +} + +/* * This function decodes a received packet. * * Based on the type, the packet is treated as either a data, or @@ -1055,11 +1107,28 @@ static int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter, u8 *cmd_buf; __le16 *curr_ptr = (__le16 *)skb->data; u16 pkt_len = le16_to_cpu(*curr_ptr); + struct mwifiex_rxinfo *rx_info; - skb_trim(skb, pkt_len); - skb_pull(skb, INTF_HEADER_LEN); + if (upld_typ != MWIFIEX_TYPE_AGGR_DATA) { + skb_trim(skb, pkt_len); + skb_pull(skb, INTF_HEADER_LEN); + } switch (upld_typ) { + case MWIFIEX_TYPE_AGGR_DATA: + dev_dbg(adapter->dev, "info: --- Rx: Aggr Data packet ---\n"); + rx_info = MWIFIEX_SKB_RXCB(skb); + rx_info->buf_type = MWIFIEX_TYPE_AGGR_DATA; + if (adapter->rx_work_enabled) { + skb_queue_tail(&adapter->rx_data_q, skb); + atomic_inc(&adapter->rx_pending); + adapter->data_received = true; + } else { + mwifiex_deaggr_sdio_pkt(adapter, skb); + dev_kfree_skb_any(skb); + } + break; + case MWIFIEX_TYPE_DATA: dev_dbg(adapter->dev, "info: --- Rx: Data packet ---\n"); if (adapter->rx_work_enabled) { @@ -1127,17 +1196,17 @@ static int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter, * provided there is space left, processed and finally uploaded. */ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, - struct sk_buff *skb, u8 port) + u16 rx_len, u8 port) { struct sdio_mmc_card *card = adapter->card; s32 f_do_rx_aggr = 0; s32 f_do_rx_cur = 0; s32 f_aggr_cur = 0; + s32 f_post_aggr_cur = 0; struct sk_buff *skb_deaggr; - u32 pind; - u32 pkt_len, pkt_type, mport; + struct sk_buff *skb = NULL; + u32 pkt_len, pkt_type, mport, pind; u8 *curr_ptr; - u32 rx_len = skb->len; if ((card->has_control_mask) && (port == CTRL_PORT)) { /* Read the command Resp without aggr */ @@ -1164,12 +1233,12 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, dev_dbg(adapter->dev, "info: %s: not last packet\n", __func__); if (MP_RX_AGGR_IN_PROGRESS(card)) { - if (MP_RX_AGGR_BUF_HAS_ROOM(card, skb->len)) { + if (MP_RX_AGGR_BUF_HAS_ROOM(card, rx_len)) { f_aggr_cur = 1; } else { /* No room in Aggr buf, do rx aggr now */ f_do_rx_aggr = 1; - f_do_rx_cur = 1; + f_post_aggr_cur = 1; } } else { /* Rx aggr not in progress */ @@ -1182,7 +1251,7 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, if (MP_RX_AGGR_IN_PROGRESS(card)) { f_do_rx_aggr = 1; - if (MP_RX_AGGR_BUF_HAS_ROOM(card, skb->len)) + if (MP_RX_AGGR_BUF_HAS_ROOM(card, rx_len)) f_aggr_cur = 1; else /* No room in Aggr buf, do rx aggr now */ @@ -1195,7 +1264,7 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, if (f_aggr_cur) { dev_dbg(adapter->dev, "info: current packet aggregation\n"); /* Curr pkt can be aggregated */ - mp_rx_aggr_setup(card, skb, port); + mp_rx_aggr_setup(card, rx_len, port); if (MP_RX_AGGR_PKT_LIMIT_REACHED(card) || mp_rx_aggr_port_limit_reached(card)) { @@ -1238,16 +1307,29 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, curr_ptr = card->mpa_rx.buf; for (pind = 0; pind < card->mpa_rx.pkt_cnt; pind++) { + u32 *len_arr = card->mpa_rx.len_arr; /* get curr PKT len & type */ pkt_len = le16_to_cpu(*(__le16 *) &curr_ptr[0]); pkt_type = le16_to_cpu(*(__le16 *) &curr_ptr[2]); /* copy pkt to deaggr buf */ - skb_deaggr = card->mpa_rx.skb_arr[pind]; + skb_deaggr = mwifiex_alloc_dma_align_buf(len_arr[pind], + GFP_KERNEL | + GFP_DMA); + if (!skb_deaggr) { + dev_err(adapter->dev, "skb allocation failure drop pkt len=%d type=%d\n", + pkt_len, pkt_type); + curr_ptr += len_arr[pind]; + continue; + } - if ((pkt_type == MWIFIEX_TYPE_DATA) && (pkt_len <= - card->mpa_rx.len_arr[pind])) { + skb_put(skb_deaggr, len_arr[pind]); + + if ((pkt_type == MWIFIEX_TYPE_DATA || + (pkt_type == MWIFIEX_TYPE_AGGR_DATA && + adapter->sdio_rx_aggr_enable)) && + (pkt_len <= len_arr[pind])) { memcpy(skb_deaggr->data, curr_ptr, pkt_len); @@ -1257,13 +1339,15 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, mwifiex_decode_rx_packet(adapter, skb_deaggr, pkt_type); } else { - dev_err(adapter->dev, "wrong aggr pkt:" - " type=%d len=%d max_len=%d\n", + dev_err(adapter->dev, " drop wrong aggr pkt:\t" + "sdio_single_port_rx_aggr=%d\t" + "type=%d len=%d max_len=%d\n", + adapter->sdio_rx_aggr_enable, pkt_type, pkt_len, - card->mpa_rx.len_arr[pind]); + len_arr[pind]); dev_kfree_skb_any(skb_deaggr); } - curr_ptr += card->mpa_rx.len_arr[pind]; + curr_ptr += len_arr[pind]; } MP_RX_AGGR_BUF_RESET(card); } @@ -1273,28 +1357,46 @@ rx_curr_single: dev_dbg(adapter->dev, "info: RX: port: %d, rx_len: %d\n", port, rx_len); + skb = mwifiex_alloc_dma_align_buf(rx_len, GFP_KERNEL | GFP_DMA); + if (!skb) { + dev_err(adapter->dev, "single skb allocated fail,\t" + "drop pkt port=%d len=%d\n", port, rx_len); + if (mwifiex_sdio_card_to_host(adapter, &pkt_type, + card->mpa_rx.buf, rx_len, + adapter->ioport + port)) + goto error; + return 0; + } + + skb_put(skb, rx_len); + if (mwifiex_sdio_card_to_host(adapter, &pkt_type, skb->data, skb->len, adapter->ioport + port)) goto error; + if (!adapter->sdio_rx_aggr_enable && + pkt_type == MWIFIEX_TYPE_AGGR_DATA) { + dev_err(adapter->dev, "drop wrong pkt type %d\t" + "current SDIO RX Aggr not enabled\n", + pkt_type); + dev_kfree_skb_any(skb); + return 0; + } mwifiex_decode_rx_packet(adapter, skb, pkt_type); } + if (f_post_aggr_cur) { + dev_dbg(adapter->dev, "info: current packet aggregation\n"); + /* Curr pkt can be aggregated */ + mp_rx_aggr_setup(card, rx_len, port); + } return 0; - error: - if (MP_RX_AGGR_IN_PROGRESS(card)) { - /* Multiport-aggregation transfer failed - cleanup */ - for (pind = 0; pind < card->mpa_rx.pkt_cnt; pind++) { - /* copy pkt to deaggr buf */ - skb_deaggr = card->mpa_rx.skb_arr[pind]; - dev_kfree_skb_any(skb_deaggr); - } + if (MP_RX_AGGR_IN_PROGRESS(card)) MP_RX_AGGR_BUF_RESET(card); - } - if (f_do_rx_cur) + if (f_do_rx_cur && skb) /* Single transfer pending. Free curr buff also */ dev_kfree_skb_any(skb); @@ -1356,8 +1458,9 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) MWIFIEX_RX_DATA_BUF_SIZE) return -1; rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE); + dev_dbg(adapter->dev, "info: rx_len = %d\n", rx_len); - skb = mwifiex_alloc_rx_buf(rx_len, GFP_KERNEL | GFP_DMA); + skb = mwifiex_alloc_dma_align_buf(rx_len, GFP_KERNEL | GFP_DMA); if (!skb) return -1; @@ -1447,28 +1550,16 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) 1) / MWIFIEX_SDIO_BLOCK_SIZE; if (rx_len <= INTF_HEADER_LEN || (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) > - MWIFIEX_RX_DATA_BUF_SIZE) { + card->mpa_rx.buf_size) { dev_err(adapter->dev, "invalid rx_len=%d\n", rx_len); return -1; } - rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE); - - skb = mwifiex_alloc_rx_buf(rx_len, - GFP_KERNEL | GFP_DMA); - - if (!skb) { - dev_err(adapter->dev, "%s: failed to alloc skb", - __func__); - return -1; - } - skb_put(skb, rx_len); - - dev_dbg(adapter->dev, "info: rx_len = %d skb->len = %d\n", - rx_len, skb->len); + rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE); + dev_dbg(adapter->dev, "info: rx_len = %d\n", rx_len); - if (mwifiex_sdio_card_to_host_mp_aggr(adapter, skb, + if (mwifiex_sdio_card_to_host_mp_aggr(adapter, rx_len, port)) { dev_err(adapter->dev, "card_to_host_mpa failed:" " int status=%#x\n", sdio_ireg); @@ -1736,6 +1827,7 @@ static int mwifiex_alloc_sdio_mpa_buffers(struct mwifiex_adapter *adapter, u32 mpa_tx_buf_size, u32 mpa_rx_buf_size) { struct sdio_mmc_card *card = adapter->card; + u32 rx_buf_size; int ret = 0; card->mpa_tx.buf = kzalloc(mpa_tx_buf_size, GFP_KERNEL); @@ -1746,13 +1838,15 @@ static int mwifiex_alloc_sdio_mpa_buffers(struct mwifiex_adapter *adapter, card->mpa_tx.buf_size = mpa_tx_buf_size; - card->mpa_rx.buf = kzalloc(mpa_rx_buf_size, GFP_KERNEL); + rx_buf_size = max_t(u32, mpa_rx_buf_size, + (u32)SDIO_MAX_AGGR_BUF_SIZE); + card->mpa_rx.buf = kzalloc(rx_buf_size, GFP_KERNEL); if (!card->mpa_rx.buf) { ret = -1; goto error; } - card->mpa_rx.buf_size = mpa_rx_buf_size; + card->mpa_rx.buf_size = rx_buf_size; error: if (ret) { @@ -1951,6 +2045,7 @@ mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port) port, card->mp_data_port_mask); } +static struct mwifiex_adapter *save_adapter; static void mwifiex_sdio_card_reset_work(struct mwifiex_adapter *adapter) { struct sdio_mmc_card *card = adapter->card; @@ -2019,10 +2114,8 @@ rdwr_status mwifiex_sdio_rdwr_firmware(struct mwifiex_adapter *adapter, } /* This function dump firmware memory to file */ -static void mwifiex_sdio_fw_dump_work(struct work_struct *work) +static void mwifiex_sdio_fw_dump_work(struct mwifiex_adapter *adapter) { - struct mwifiex_adapter *adapter = - container_of(work, struct mwifiex_adapter, iface_work); struct sdio_mmc_card *card = adapter->card; int ret = 0; unsigned int reg, reg_start, reg_end; @@ -2144,36 +2237,36 @@ done: static void mwifiex_sdio_work(struct work_struct *work) { - struct mwifiex_adapter *adapter = - container_of(work, struct mwifiex_adapter, iface_work); - - if (test_and_clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET, - &adapter->iface_work_flags)) - mwifiex_sdio_card_reset_work(adapter); if (test_and_clear_bit(MWIFIEX_IFACE_WORK_FW_DUMP, - &adapter->iface_work_flags)) - mwifiex_sdio_fw_dump_work(work); + &iface_work_flags)) + mwifiex_sdio_fw_dump_work(save_adapter); + if (test_and_clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET, + &iface_work_flags)) + mwifiex_sdio_card_reset_work(save_adapter); } +static DECLARE_WORK(sdio_work, mwifiex_sdio_work); /* This function resets the card */ static void mwifiex_sdio_card_reset(struct mwifiex_adapter *adapter) { - if (test_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &adapter->iface_work_flags)) + save_adapter = adapter; + if (test_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &iface_work_flags)) return; - set_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &adapter->iface_work_flags); + set_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &iface_work_flags); - schedule_work(&adapter->iface_work); + schedule_work(&sdio_work); } /* This function dumps FW information */ static void mwifiex_sdio_fw_dump(struct mwifiex_adapter *adapter) { - if (test_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags)) + save_adapter = adapter; + if (test_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &iface_work_flags)) return; - set_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags); - schedule_work(&adapter->iface_work); + set_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &iface_work_flags); + schedule_work(&sdio_work); } /* Function to dump SDIO function registers and SDIO scratch registers in case @@ -2289,9 +2382,9 @@ static struct mwifiex_if_ops sdio_ops = { .cmdrsp_complete = mwifiex_sdio_cmdrsp_complete, .event_complete = mwifiex_sdio_event_complete, .card_reset = mwifiex_sdio_card_reset, - .iface_work = mwifiex_sdio_work, .fw_dump = mwifiex_sdio_fw_dump, .reg_dump = mwifiex_sdio_reg_dump, + .deaggr_pkt = mwifiex_deaggr_sdio_pkt, }; /* @@ -2328,6 +2421,7 @@ mwifiex_sdio_cleanup_module(void) /* Set the flag as user is removing this module. */ user_rmmod = 1; + cancel_work_sync(&sdio_work); sdio_unregister_driver(&mwifiex_sdio); } diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h index c636944c77bc..6f645cf47369 100644 --- a/drivers/net/wireless/mwifiex/sdio.h +++ b/drivers/net/wireless/mwifiex/sdio.h @@ -67,6 +67,8 @@ #define MWIFIEX_MP_AGGR_BUF_SIZE_16K (16384) #define MWIFIEX_MP_AGGR_BUF_SIZE_32K (32768) +/* we leave one block of 256 bytes for DMA alignment*/ +#define MWIFIEX_MP_AGGR_BUF_SIZE_MAX (65280) /* Misc. Config Register : Auto Re-enable interrupts */ #define AUTO_RE_ENABLE_INT BIT(4) @@ -458,8 +460,8 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = { .max_ports = 32, .mp_agg_pkt_limit = 16, .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, - .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K, - .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K, + .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, + .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, .supports_sdio_new_mode = true, .has_control_mask = false, .can_dump_fw = true, @@ -571,9 +573,9 @@ mp_tx_aggr_port_limit_reached(struct sdio_mmc_card *card) /* Prepare to copy current packet from card to SDIO Rx aggregation buffer */ static inline void mp_rx_aggr_setup(struct sdio_mmc_card *card, - struct sk_buff *skb, u8 port) + u16 rx_len, u8 port) { - card->mpa_rx.buf_len += skb->len; + card->mpa_rx.buf_len += rx_len; if (!card->mpa_rx.pkt_cnt) card->mpa_rx.start_port = port; @@ -586,8 +588,8 @@ static inline void mp_rx_aggr_setup(struct sdio_mmc_card *card, else card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt + 1); } - card->mpa_rx.skb_arr[card->mpa_rx.pkt_cnt] = skb; - card->mpa_rx.len_arr[card->mpa_rx.pkt_cnt] = skb->len; + card->mpa_rx.skb_arr[card->mpa_rx.pkt_cnt] = NULL; + card->mpa_rx.len_arr[card->mpa_rx.pkt_cnt] = rx_len; card->mpa_rx.pkt_cnt++; } #endif /* _MWIFIEX_SDIO_H */ diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index f7d204ffd6e9..49422f2a5380 100644 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/mwifiex/sta_cmd.c @@ -1370,22 +1370,29 @@ mwifiex_cmd_mef_cfg(struct mwifiex_private *priv, struct mwifiex_ds_mef_cfg *mef) { struct host_cmd_ds_mef_cfg *mef_cfg = &cmd->params.mef_cfg; + struct mwifiex_fw_mef_entry *mef_entry = NULL; u8 *pos = (u8 *)mef_cfg; + u16 i; cmd->command = cpu_to_le16(HostCmd_CMD_MEF_CFG); mef_cfg->criteria = cpu_to_le32(mef->criteria); mef_cfg->num_entries = cpu_to_le16(mef->num_entries); pos += sizeof(*mef_cfg); - mef_cfg->mef_entry->mode = mef->mef_entry->mode; - mef_cfg->mef_entry->action = mef->mef_entry->action; - pos += sizeof(*(mef_cfg->mef_entry)); - if (mwifiex_cmd_append_rpn_expression(priv, mef->mef_entry, &pos)) - return -1; + for (i = 0; i < mef->num_entries; i++) { + mef_entry = (struct mwifiex_fw_mef_entry *)pos; + mef_entry->mode = mef->mef_entry[i].mode; + mef_entry->action = mef->mef_entry[i].action; + pos += sizeof(*mef_cfg->mef_entry); + + if (mwifiex_cmd_append_rpn_expression(priv, + &mef->mef_entry[i], &pos)) + return -1; - mef_cfg->mef_entry->exprsize = - cpu_to_le16(pos - mef_cfg->mef_entry->expr); + mef_entry->exprsize = + cpu_to_le16(pos - mef_entry->expr); + } cmd->size = cpu_to_le16((u16) (pos - (u8 *)mef_cfg) + S_DS_GEN); return 0; @@ -1664,6 +1671,25 @@ mwifiex_cmd_tdls_oper(struct mwifiex_private *priv, return 0; } + +/* This function prepares command of sdio rx aggr info. */ +static int mwifiex_cmd_sdio_rx_aggr_cfg(struct host_cmd_ds_command *cmd, + u16 cmd_action, void *data_buf) +{ + struct host_cmd_sdio_sp_rx_aggr_cfg *cfg = + &cmd->params.sdio_rx_aggr_cfg; + + cmd->command = cpu_to_le16(HostCmd_CMD_SDIO_SP_RX_AGGR_CFG); + cmd->size = + cpu_to_le16(sizeof(struct host_cmd_sdio_sp_rx_aggr_cfg) + + S_DS_GEN); + cfg->action = cmd_action; + if (cmd_action == HostCmd_ACT_GEN_SET) + cfg->enable = *(u8 *)data_buf; + + return 0; +} + /* * This function prepares the commands before sending them to the firmware. * @@ -1901,6 +1927,10 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, ret = mwifiex_cmd_issue_chan_report_request(priv, cmd_ptr, data_buf); break; + case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: + ret = mwifiex_cmd_sdio_rx_aggr_cfg(cmd_ptr, cmd_action, + data_buf); + break; default: dev_err(priv->adapter->dev, "PREP_CMD: unknown cmd- %#x\n", cmd_no); @@ -1940,6 +1970,7 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) struct mwifiex_ds_auto_ds auto_ds; enum state_11d_t state_11d; struct mwifiex_ds_11n_tx_cfg tx_cfg; + u8 sdio_sp_rx_aggr_enable; if (first_sta) { if (priv->adapter->iface_type == MWIFIEX_PCIE) { @@ -1983,6 +2014,22 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) if (ret) return -1; + /** Set SDIO Single Port RX Aggr Info */ + if (priv->adapter->iface_type == MWIFIEX_SDIO && + ISSUPP_SDIO_SPA_ENABLED(priv->adapter->fw_cap_info)) { + sdio_sp_rx_aggr_enable = true; + ret = mwifiex_send_cmd(priv, + HostCmd_CMD_SDIO_SP_RX_AGGR_CFG, + HostCmd_ACT_GEN_SET, 0, + &sdio_sp_rx_aggr_enable, + true); + if (ret) { + dev_err(priv->adapter->dev, + "error while enabling SP aggregation..disable it"); + adapter->sdio_rx_aggr_enable = false; + } + } + /* Reconfigure tx buf size */ ret = mwifiex_send_cmd(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF, HostCmd_ACT_GEN_SET, 0, diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c index 5f8da5924666..88dc6b672ef4 100644 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c @@ -90,6 +90,10 @@ mwifiex_process_cmdresp_error(struct mwifiex_private *priv, case HostCmd_CMD_MAC_CONTROL: break; + case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: + dev_err(priv->adapter->dev, "SDIO RX single-port aggregation Not support\n"); + break; + default: break; } @@ -943,6 +947,20 @@ static int mwifiex_ret_cfg_data(struct mwifiex_private *priv, return 0; } +/** This Function handles the command response of sdio rx aggr */ +static int mwifiex_ret_sdio_rx_aggr_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct host_cmd_sdio_sp_rx_aggr_cfg *cfg = + &resp->params.sdio_rx_aggr_cfg; + + adapter->sdio_rx_aggr_enable = cfg->enable; + adapter->sdio_rx_block_size = le16_to_cpu(cfg->block_size); + + return 0; +} + /* * This function handles the command responses. * @@ -1124,6 +1142,9 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no, break; case HostCmd_CMD_CHAN_REPORT_REQUEST: break; + case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: + ret = mwifiex_ret_sdio_rx_aggr_cfg(priv, resp); + break; default: dev_err(adapter->dev, "CMD_RESP: unknown cmd response %#x\n", resp->command); diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c index 64c4223a1e1e..0dc7a1d3993d 100644 --- a/drivers/net/wireless/mwifiex/sta_event.c +++ b/drivers/net/wireless/mwifiex/sta_event.c @@ -312,7 +312,7 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) adapter->ps_state = PS_STATE_AWAKE; adapter->pm_wakeup_card_req = false; adapter->pm_wakeup_fw_try = false; - del_timer_sync(&adapter->wakeup_timer); + del_timer(&adapter->wakeup_timer); break; } if (!mwifiex_send_null_packet @@ -327,7 +327,7 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) adapter->ps_state = PS_STATE_AWAKE; adapter->pm_wakeup_card_req = false; adapter->pm_wakeup_fw_try = false; - del_timer_sync(&adapter->wakeup_timer); + del_timer(&adapter->wakeup_timer); break; diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/mwifiex/txrx.c index ea4549f0e0b9..a245f444aeec 100644 --- a/drivers/net/wireless/mwifiex/txrx.c +++ b/drivers/net/wireless/mwifiex/txrx.c @@ -92,6 +92,12 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, else head_ptr = mwifiex_process_sta_txpd(priv, skb); + if ((adapter->data_sent || adapter->tx_lock_flag) && head_ptr) { + skb_queue_tail(&adapter->tx_data_q, skb); + atomic_inc(&adapter->tx_queued); + return 0; + } + if (head_ptr) { if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) local_tx_pd = (struct txpd *)(head_ptr + hroom); @@ -142,6 +148,123 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, return ret; } +static int mwifiex_host_to_card(struct mwifiex_adapter *adapter, + struct sk_buff *skb, + struct mwifiex_tx_param *tx_param) +{ + struct txpd *local_tx_pd = NULL; + u8 *head_ptr = skb->data; + int ret = 0; + struct mwifiex_private *priv; + struct mwifiex_txinfo *tx_info; + + tx_info = MWIFIEX_SKB_TXCB(skb); + priv = mwifiex_get_priv_by_id(adapter, tx_info->bss_num, + tx_info->bss_type); + if (!priv) { + dev_err(adapter->dev, "data: priv not found. Drop TX packet\n"); + adapter->dbg.num_tx_host_to_card_failure++; + mwifiex_write_data_complete(adapter, skb, 0, 0); + return ret; + } + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) { + if (adapter->iface_type == MWIFIEX_USB) + local_tx_pd = (struct txpd *)head_ptr; + else + local_tx_pd = (struct txpd *) (head_ptr + + INTF_HEADER_LEN); + } + + if (adapter->iface_type == MWIFIEX_USB) { + adapter->data_sent = true; + ret = adapter->if_ops.host_to_card(adapter, + MWIFIEX_USB_EP_DATA, + skb, NULL); + } else { + ret = adapter->if_ops.host_to_card(adapter, + MWIFIEX_TYPE_DATA, + skb, tx_param); + } + switch (ret) { + case -ENOSR: + dev_err(adapter->dev, "data: -ENOSR is returned\n"); + break; + case -EBUSY: + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && + (adapter->pps_uapsd_mode) && + (adapter->tx_lock_flag)) { + priv->adapter->tx_lock_flag = false; + if (local_tx_pd) + local_tx_pd->flags = 0; + } + skb_queue_head(&adapter->tx_data_q, skb); + if (tx_info->flags & MWIFIEX_BUF_FLAG_AGGR_PKT) + atomic_add(tx_info->aggr_num, &adapter->tx_queued); + else + atomic_inc(&adapter->tx_queued); + dev_dbg(adapter->dev, "data: -EBUSY is returned\n"); + break; + case -1: + if (adapter->iface_type != MWIFIEX_PCIE) + adapter->data_sent = false; + dev_err(adapter->dev, "mwifiex_write_data_async failed: 0x%X\n", + ret); + adapter->dbg.num_tx_host_to_card_failure++; + mwifiex_write_data_complete(adapter, skb, 0, ret); + break; + case -EINPROGRESS: + if (adapter->iface_type != MWIFIEX_PCIE) + adapter->data_sent = false; + break; + case 0: + mwifiex_write_data_complete(adapter, skb, 0, ret); + break; + default: + break; + } + return ret; +} + +static int +mwifiex_dequeue_tx_queue(struct mwifiex_adapter *adapter) +{ + struct sk_buff *skb, *skb_next; + struct mwifiex_txinfo *tx_info; + struct mwifiex_tx_param tx_param; + + skb = skb_dequeue(&adapter->tx_data_q); + if (!skb) + return -1; + + tx_info = MWIFIEX_SKB_TXCB(skb); + if (tx_info->flags & MWIFIEX_BUF_FLAG_AGGR_PKT) + atomic_sub(tx_info->aggr_num, &adapter->tx_queued); + else + atomic_dec(&adapter->tx_queued); + + if (!skb_queue_empty(&adapter->tx_data_q)) + skb_next = skb_peek(&adapter->tx_data_q); + else + skb_next = NULL; + tx_param.next_pkt_len = ((skb_next) ? skb_next->len : 0); + if (!tx_param.next_pkt_len) { + if (!mwifiex_wmm_lists_empty(adapter)) + tx_param.next_pkt_len = 1; + } + return mwifiex_host_to_card(adapter, skb, &tx_param); +} + +void +mwifiex_process_tx_queue(struct mwifiex_adapter *adapter) +{ + do { + if (adapter->data_sent || adapter->tx_lock_flag) + break; + if (mwifiex_dequeue_tx_queue(adapter)) + break; + } while (!skb_queue_empty(&adapter->tx_data_q)); +} + /* * Packet send completion callback handler. * @@ -179,8 +302,11 @@ int mwifiex_write_data_complete(struct mwifiex_adapter *adapter, priv->stats.tx_errors++; } - if (tx_info->flags & MWIFIEX_BUF_FLAG_BRIDGED_PKT) + if (tx_info->flags & MWIFIEX_BUF_FLAG_BRIDGED_PKT) { atomic_dec_return(&adapter->pending_bridged_pkts); + if (tx_info->flags & MWIFIEX_BUF_FLAG_AGGR_PKT) + goto done; + } if (aggr) /* For skb_aggr, do not wake up tx queue */ diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c index 223873022ffe..fd8027f200a0 100644 --- a/drivers/net/wireless/mwifiex/usb.c +++ b/drivers/net/wireless/mwifiex/usb.c @@ -193,7 +193,7 @@ static void mwifiex_usb_rx_complete(struct urb *urb) dev_dbg(adapter->dev, "info: recv_length=%d, status=%d\n", recv_length, status); if (status == -EINPROGRESS) { - queue_work(adapter->workqueue, &adapter->main_work); + mwifiex_queue_main_work(adapter); /* urb for data_ep is re-submitted now; * urb for cmd_ep will be re-submitted in callback @@ -262,7 +262,7 @@ static void mwifiex_usb_tx_complete(struct urb *urb) urb->status ? -1 : 0); } - queue_work(adapter->workqueue, &adapter->main_work); + mwifiex_queue_main_work(adapter); return; } @@ -1006,7 +1006,7 @@ static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) { /* Simulation of HS_AWAKE event */ adapter->pm_wakeup_fw_try = false; - del_timer_sync(&adapter->wakeup_timer); + del_timer(&adapter->wakeup_timer); adapter->pm_wakeup_card_req = false; adapter->ps_state = PS_STATE_AWAKE; diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c index 2148a573396b..b8a45872354d 100644 --- a/drivers/net/wireless/mwifiex/util.c +++ b/drivers/net/wireless/mwifiex/util.c @@ -632,7 +632,7 @@ void mwifiex_hist_data_reset(struct mwifiex_private *priv) atomic_set(&phist_data->sig_str[ix], 0); } -void *mwifiex_alloc_rx_buf(int rx_len, gfp_t flags) +void *mwifiex_alloc_dma_align_buf(int rx_len, gfp_t flags) { struct sk_buff *skb; int buf_len, pad; @@ -653,4 +653,4 @@ void *mwifiex_alloc_rx_buf(int rx_len, gfp_t flags) return skb; } -EXPORT_SYMBOL_GPL(mwifiex_alloc_rx_buf); +EXPORT_SYMBOL_GPL(mwifiex_alloc_dma_align_buf); diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 0cd4f6bed9fc..b2e99569a0f8 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -157,6 +157,8 @@ void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra) ra_list->is_11n_enabled = 0; ra_list->tdls_link = false; + ra_list->ba_status = BA_SETUP_NONE; + ra_list->amsdu_in_ampdu = false; if (!mwifiex_queuing_ra_based(priv)) { if (mwifiex_get_tdls_link_status(priv, ra) == TDLS_SETUP_COMPLETE) { @@ -574,7 +576,7 @@ mwifiex_clean_txrx(struct mwifiex_private *priv) * This function retrieves a particular RA list node, matching with the * given TID and RA address. */ -static struct mwifiex_ra_list_tbl * +struct mwifiex_ra_list_tbl * mwifiex_wmm_get_ralist_node(struct mwifiex_private *priv, u8 tid, const u8 *ra_addr) { @@ -942,14 +944,11 @@ mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter, struct mwifiex_ra_list_tbl *ptr; struct mwifiex_tid_tbl *tid_ptr; atomic_t *hqp; - unsigned long flags_bss, flags_ra; + unsigned long flags_ra; int i, j; /* check the BSS with highest priority first */ for (j = adapter->priv_num - 1; j >= 0; --j) { - spin_lock_irqsave(&adapter->bss_prio_tbl[j].bss_prio_lock, - flags_bss); - /* iterate over BSS with the equal priority */ list_for_each_entry(adapter->bss_prio_tbl[j].bss_prio_cur, &adapter->bss_prio_tbl[j].bss_prio_head, @@ -985,19 +984,15 @@ mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter, } } - spin_unlock_irqrestore(&adapter->bss_prio_tbl[j].bss_prio_lock, - flags_bss); } return NULL; found: - /* holds bss_prio_lock / ra_list_spinlock */ + /* holds ra_list_spinlock */ if (atomic_read(hqp) > i) atomic_set(hqp, i); spin_unlock_irqrestore(&priv_tmp->wmm.ra_list_spinlock, flags_ra); - spin_unlock_irqrestore(&adapter->bss_prio_tbl[j].bss_prio_lock, - flags_bss); *priv = priv_tmp; *tid = tos_to_tid[i]; @@ -1179,6 +1174,14 @@ mwifiex_send_processed_packet(struct mwifiex_private *priv, skb = skb_dequeue(&ptr->skb_head); + if (adapter->data_sent || adapter->tx_lock_flag) { + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + skb_queue_tail(&adapter->tx_data_q, skb); + atomic_inc(&adapter->tx_queued); + return; + } + if (!skb_queue_empty(&ptr->skb_head)) skb_next = skb_peek(&ptr->skb_head); else @@ -1276,13 +1279,13 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter) } if (!ptr->is_11n_enabled || - mwifiex_is_ba_stream_setup(priv, ptr, tid) || - priv->wps.session_enable) { + ptr->ba_status || + priv->wps.session_enable) { if (ptr->is_11n_enabled && - mwifiex_is_ba_stream_setup(priv, ptr, tid) && - mwifiex_is_amsdu_in_ampdu_allowed(priv, ptr, tid) && - mwifiex_is_amsdu_allowed(priv, tid) && - mwifiex_is_11n_aggragation_possible(priv, ptr, + ptr->ba_status && + ptr->amsdu_in_ampdu && + mwifiex_is_amsdu_allowed(priv, tid) && + mwifiex_is_11n_aggragation_possible(priv, ptr, adapter->tx_buf_size)) mwifiex_11n_aggregate_pkt(priv, ptr, ptr_index, flags); /* ra_list_spinlock has been freed in @@ -1329,11 +1332,16 @@ void mwifiex_wmm_process_tx(struct mwifiex_adapter *adapter) { do { - /* Check if busy */ - if (adapter->data_sent || adapter->tx_lock_flag) - break; - if (mwifiex_dequeue_tx_packet(adapter)) break; + if (adapter->iface_type != MWIFIEX_SDIO) { + if (adapter->data_sent || + adapter->tx_lock_flag) + break; + } else { + if (atomic_read(&adapter->tx_queued) >= + MWIFIEX_MAX_PKTS_TXQ) + break; + } } while (!mwifiex_wmm_lists_empty(adapter)); } diff --git a/drivers/net/wireless/mwifiex/wmm.h b/drivers/net/wireless/mwifiex/wmm.h index 569bd73f33c5..48ece0b35591 100644 --- a/drivers/net/wireless/mwifiex/wmm.h +++ b/drivers/net/wireless/mwifiex/wmm.h @@ -127,4 +127,6 @@ mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, const u8 *ra_addr); u8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid); +struct mwifiex_ra_list_tbl *mwifiex_wmm_get_ralist_node(struct mwifiex_private + *priv, u8 tid, const u8 *ra_addr); #endif /* !_MWIFIEX_WMM_H_ */ diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index 8444313eabe2..6ec2466b52b6 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -233,6 +233,7 @@ static int rt2800usb_autorun_detect(struct rt2x00_dev *rt2x00dev) { __le32 *reg; u32 fw_mode; + int ret; reg = kmalloc(sizeof(*reg), GFP_KERNEL); if (reg == NULL) @@ -242,11 +243,14 @@ static int rt2800usb_autorun_detect(struct rt2x00_dev *rt2x00dev) * magic value USB_MODE_AUTORUN (0x11) to the device, thus the * returned value would be invalid. */ - rt2x00usb_vendor_request(rt2x00dev, USB_DEVICE_MODE, - USB_VENDOR_REQUEST_IN, 0, USB_MODE_AUTORUN, - reg, sizeof(*reg), REGISTER_TIMEOUT_FIRMWARE); + ret = rt2x00usb_vendor_request(rt2x00dev, USB_DEVICE_MODE, + USB_VENDOR_REQUEST_IN, 0, + USB_MODE_AUTORUN, reg, sizeof(*reg), + REGISTER_TIMEOUT_FIRMWARE); fw_mode = le32_to_cpu(*reg); kfree(reg); + if (ret < 0) + return ret; if ((fw_mode & 0x00000003) == 2) return 1; @@ -289,6 +293,7 @@ static int rt2800usb_write_firmware(struct rt2x00_dev *rt2x00dev, if (retval) { rt2x00_info(rt2x00dev, "Firmware loading not required - NIC in AutoRun mode\n"); + __clear_bit(REQUIRE_FIRMWARE, &rt2x00dev->cap_flags); } else { rt2x00usb_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE, data + offset, length); @@ -374,7 +379,6 @@ static int rt2800usb_enable_radio(struct rt2x00_dev *rt2x00dev) static void rt2800usb_disable_radio(struct rt2x00_dev *rt2x00dev) { rt2800_disable_radio(rt2x00dev); - rt2x00usb_disable_radio(rt2x00dev); } static int rt2800usb_set_state(struct rt2x00_dev *rt2x00dev, @@ -1040,6 +1044,7 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x07d1, 0x3c17) }, { USB_DEVICE(0x2001, 0x3317) }, { USB_DEVICE(0x2001, 0x3c1b) }, + { USB_DEVICE(0x2001, 0x3c25) }, /* Draytek */ { USB_DEVICE(0x07fa, 0x7712) }, /* DVICO */ diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.h b/drivers/net/wireless/rt2x00/rt2x00usb.h index 8f85fbd5f237..569363da00a2 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.h +++ b/drivers/net/wireless/rt2x00/rt2x00usb.h @@ -199,7 +199,7 @@ static inline void rt2x00usb_register_read(struct rt2x00_dev *rt2x00dev, const unsigned int offset, u32 *value) { - __le32 reg; + __le32 reg = 0; rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ, USB_VENDOR_REQUEST_IN, offset, ®, sizeof(reg)); @@ -219,7 +219,7 @@ static inline void rt2x00usb_register_read_lock(struct rt2x00_dev *rt2x00dev, const unsigned int offset, u32 *value) { - __le32 reg; + __le32 reg = 0; rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_READ, USB_VENDOR_REQUEST_IN, offset, ®, sizeof(reg), REGISTER_TIMEOUT); diff --git a/drivers/net/wireless/rtlwifi/base.h b/drivers/net/wireless/rtlwifi/base.h index dee4ac2f27e2..ff9a4bfd4515 100644 --- a/drivers/net/wireless/rtlwifi/base.h +++ b/drivers/net/wireless/rtlwifi/base.h @@ -123,7 +123,6 @@ bool rtl_tx_mgmt_proc(struct ieee80211_hw *hw, struct sk_buff *skb); u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx); void rtl_beacon_statistic(struct ieee80211_hw *hw, struct sk_buff *skb); -void rtl_watch_dog_timer_callback(unsigned long data); int rtl_tx_agg_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid, u16 *ssn); int rtl_tx_agg_stop(struct ieee80211_hw *hw, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c index a62170ea0481..8c45cf44ce24 100644 --- a/drivers/net/wireless/rtlwifi/pci.c +++ b/drivers/net/wireless/rtlwifi/pci.c @@ -1124,12 +1124,22 @@ static void _rtl_pci_prepare_bcn_tasklet(struct ieee80211_hw *hw) /*This is for new trx flow*/ struct rtl_tx_buffer_desc *pbuffer_desc = NULL; u8 temp_one = 1; + u8 *entry; memset(&tcb_desc, 0, sizeof(struct rtl_tcb_desc)); ring = &rtlpci->tx_ring[BEACON_QUEUE]; pskb = __skb_dequeue(&ring->queue); - if (pskb) + if (rtlpriv->use_new_trx_flow) + entry = (u8 *)(&ring->buffer_desc[ring->idx]); + else + entry = (u8 *)(&ring->desc[ring->idx]); + if (pskb) { + pci_unmap_single(rtlpci->pdev, + rtlpriv->cfg->ops->get_desc( + (u8 *)entry, true, HW_DESC_TXBUFF_ADDR), + pskb->len, PCI_DMA_TODEVICE); kfree_skb(pskb); + } /*NB: the beacon data buffer must be 32-bit aligned. */ pskb = ieee80211_beacon_get(hw, mac->vif); diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c b/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c index edc2cbb6253c..86ce5b1930e6 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c @@ -30,6 +30,7 @@ #include "../cam.h" #include "../ps.h" #include "../pci.h" +#include "../pwrseqcmd.h" #include "reg.h" #include "def.h" #include "phy.h" @@ -885,7 +886,7 @@ static bool _rtl88ee_init_mac(struct ieee80211_hw *hw) rtl_write_word(rtlpriv, REG_CR, 0x2ff); rtl_write_byte(rtlpriv, REG_CR+1, 0x06); - rtl_write_byte(rtlpriv, REG_CR+2, 0x00); + rtl_write_byte(rtlpriv, MSR, 0x00); if (!rtlhal->mac_func_enable) { if (_rtl88ee_llt_table_init(hw) == false) { @@ -1277,7 +1278,7 @@ static int _rtl88ee_set_media_status(struct ieee80211_hw *hw, mode); } - rtl_write_byte(rtlpriv, (MSR), bt_msr | mode); + rtl_write_byte(rtlpriv, MSR, bt_msr | mode); rtlpriv->cfg->ops->led_control(hw, ledaction); if (mode == MSR_AP) rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00); diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c index 0c20dd74d6ec..d310d55d800e 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c @@ -1364,7 +1364,7 @@ static int _rtl92cu_set_media_status(struct ieee80211_hw *hw, "Network type %d not supported!\n", type); goto error_out; } - rtl_write_byte(rtlpriv, (MSR), bt_msr); + rtl_write_byte(rtlpriv, MSR, bt_msr); rtlpriv->cfg->ops->led_control(hw, ledaction); if ((bt_msr & MSR_MASK) == MSR_AP) rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00); @@ -1471,8 +1471,7 @@ static void _InitBeaconParameters(struct ieee80211_hw *hw) rtl_write_word(rtlpriv, REG_BCNTCFG, 0x66FF); } -static void _beacon_function_enable(struct ieee80211_hw *hw, bool Enable, - bool Linked) +static void _beacon_function_enable(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -1517,7 +1516,7 @@ void rtl92cu_set_beacon_related_registers(struct ieee80211_hw *hw) rtl_write_byte(rtlpriv, REG_RXTSF_OFFSET_CCK, 0x50); rtl_write_byte(rtlpriv, REG_RXTSF_OFFSET_OFDM, 0x50); } - _beacon_function_enable(hw, true, true); + _beacon_function_enable(hw); } void rtl92cu_set_beacon_interval(struct ieee80211_hw *hw) diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c b/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c index 133e395b7401..adb810794eef 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c @@ -497,7 +497,7 @@ int rtl92c_set_network_type(struct ieee80211_hw *hw, enum nl80211_iftype type) "Network type %d not supported!\n", type); return -EOPNOTSUPP; } - rtl_write_byte(rtlpriv, (REG_CR + 2), value); + rtl_write_byte(rtlpriv, MSR, value); return 0; } diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c index 90a714c189a8..23806c243a53 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c @@ -321,6 +321,7 @@ static struct usb_device_id rtl8192c_usb_ids[] = { {RTL_USB_DEVICE(0x07b8, 0x8188, rtl92cu_hal_cfg)}, /*Abocom - Abocom*/ {RTL_USB_DEVICE(0x07b8, 0x8189, rtl92cu_hal_cfg)}, /*Funai - Abocom*/ {RTL_USB_DEVICE(0x0846, 0x9041, rtl92cu_hal_cfg)}, /*NetGear WNA1000M*/ + {RTL_USB_DEVICE(0x0b05, 0x17ba, rtl92cu_hal_cfg)}, /*ASUS-Edimax*/ {RTL_USB_DEVICE(0x0bda, 0x5088, rtl92cu_hal_cfg)}, /*Thinkware-CC&C*/ {RTL_USB_DEVICE(0x0df6, 0x0052, rtl92cu_hal_cfg)}, /*Sitecom - Edimax*/ {RTL_USB_DEVICE(0x0df6, 0x005c, rtl92cu_hal_cfg)}, /*Sitecom - Edimax*/ @@ -377,6 +378,7 @@ static struct usb_device_id rtl8192c_usb_ids[] = { {RTL_USB_DEVICE(0x2001, 0x3307, rtl92cu_hal_cfg)}, /*D-Link-Cameo*/ {RTL_USB_DEVICE(0x2001, 0x3309, rtl92cu_hal_cfg)}, /*D-Link-Alpha*/ {RTL_USB_DEVICE(0x2001, 0x330a, rtl92cu_hal_cfg)}, /*D-Link-Alpha*/ + {RTL_USB_DEVICE(0x2001, 0x330d, rtl92cu_hal_cfg)}, /*D-Link DWA-131 */ {RTL_USB_DEVICE(0x2019, 0xab2b, rtl92cu_hal_cfg)}, /*Planex -Abocom*/ {RTL_USB_DEVICE(0x20f4, 0x624d, rtl92cu_hal_cfg)}, /*TRENDNet*/ {RTL_USB_DEVICE(0x2357, 0x0100, rtl92cu_hal_cfg)}, /*TP-Link WN8200ND*/ diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/hw.c b/drivers/net/wireless/rtlwifi/rtl8192de/hw.c index 01bcc2d218dc..f49b60d31450 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/hw.c @@ -1126,7 +1126,7 @@ static int _rtl92de_set_media_status(struct ieee80211_hw *hw, break; } - rtl_write_byte(rtlpriv, REG_CR + 2, bt_msr); + rtl_write_byte(rtlpriv, MSR, bt_msr); rtlpriv->cfg->ops->led_control(hw, ledaction); if ((bt_msr & MSR_MASK) == MSR_AP) rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00); diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c index db230a3f0137..da0a6125f314 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c @@ -1510,7 +1510,7 @@ static int _rtl92ee_set_media_status(struct ieee80211_hw *hw, mode); } - rtl_write_byte(rtlpriv, (MSR), bt_msr | mode); + rtl_write_byte(rtlpriv, MSR, bt_msr | mode); rtlpriv->cfg->ops->led_control(hw, ledaction); if (mode == MSR_AP) rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00); diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/hw.c b/drivers/net/wireless/rtlwifi/rtl8192se/hw.c index dee88a80bee1..12b0978ba4fa 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/hw.c @@ -1204,7 +1204,7 @@ static int _rtl92se_set_media_status(struct ieee80211_hw *hw, if (type != NL80211_IFTYPE_AP && rtlpriv->mac80211.link_state < MAC80211_LINKED) bt_msr = rtl_read_byte(rtlpriv, MSR) & ~MSR_LINK_MASK; - rtl_write_byte(rtlpriv, (MSR), bt_msr); + rtl_write_byte(rtlpriv, MSR, bt_msr); temp = rtl_read_dword(rtlpriv, TCR); rtl_write_dword(rtlpriv, TCR, temp & (~BIT(8))); diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c index b3b094759f6d..67bb47d77b68 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c @@ -1183,7 +1183,7 @@ static int _rtl8723e_set_media_status(struct ieee80211_hw *hw, mode); } - rtl_write_byte(rtlpriv, (MSR), bt_msr | mode); + rtl_write_byte(rtlpriv, MSR, bt_msr | mode); rtlpriv->cfg->ops->led_control(hw, ledaction); if (mode == MSR_AP) rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00); diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/hw.c b/drivers/net/wireless/rtlwifi/rtl8723be/hw.c index b46998341c40..b681af3c7a35 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723be/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8723be/hw.c @@ -1558,7 +1558,7 @@ static int _rtl8723be_set_media_status(struct ieee80211_hw *hw, mode); } - rtl_write_byte(rtlpriv, (MSR), bt_msr | mode); + rtl_write_byte(rtlpriv, MSR, bt_msr | mode); rtlpriv->cfg->ops->led_control(hw, ledaction); if (mode == MSR_AP) rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00); diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c index 2a0a71bac00c..8704eee9f3a4 100644 --- a/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c @@ -423,7 +423,7 @@ void rtl8821ae_get_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) *((u16 *)(val+4)) = rtl_read_word(rtlpriv, REG_BSSID+4); break; case HW_VAR_MEDIA_STATUS: - val[0] = rtl_read_byte(rtlpriv, REG_CR+2) & 0x3; + val[0] = rtl_read_byte(rtlpriv, MSR) & 0x3; break; case HW_VAR_SLOT_TIME: *((u8 *)(val)) = mac->slot_time; @@ -2178,7 +2178,7 @@ static int _rtl8821ae_set_media_status(struct ieee80211_hw *hw, return 1; } - rtl_write_byte(rtlpriv, (MSR), bt_msr); + rtl_write_byte(rtlpriv, MSR, bt_msr); rtlpriv->cfg->ops->led_control(hw, ledaction); if ((bt_msr & 0xfc) == MSR_AP) rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00); diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/trx.c b/drivers/net/wireless/rtlwifi/rtl8821ae/trx.c index 72af4b9ee32b..174743aef943 100644 --- a/drivers/net/wireless/rtlwifi/rtl8821ae/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8821ae/trx.c @@ -64,6 +64,20 @@ static u16 odm_cfo(char value) return ret_val; } +static u8 _rtl8821ae_evm_dbm_jaguar(char value) +{ + char ret_val = value; + + /* -33dB~0dB to 33dB ~ 0dB*/ + if (ret_val == -128) + ret_val = 127; + else if (ret_val < 0) + ret_val = 0 - ret_val; + + ret_val = ret_val >> 1; + return ret_val; +} + static void query_rxphystatus(struct ieee80211_hw *hw, struct rtl_stats *pstatus, u8 *pdesc, struct rx_fwinfo_8821ae *p_drvinfo, @@ -246,7 +260,7 @@ static void query_rxphystatus(struct ieee80211_hw *hw, for (i = 0; i < max_spatial_stream; i++) { evm = rtl_evm_db_to_percentage(p_phystrpt->rxevm[i]); - evmdbm = rtl_evm_dbm_jaguar(p_phystrpt->rxevm[i]); + evmdbm = _rtl8821ae_evm_dbm_jaguar(p_phystrpt->rxevm[i]); if (bpacket_match_bssid) { /* Fill value in RFD, Get the first diff --git a/drivers/net/wireless/rtlwifi/stats.c b/drivers/net/wireless/rtlwifi/stats.c index 2d0736a09fc0..d8b30690b00d 100644 --- a/drivers/net/wireless/rtlwifi/stats.c +++ b/drivers/net/wireless/rtlwifi/stats.c @@ -39,15 +39,8 @@ EXPORT_SYMBOL(rtl_query_rxpwrpercentage); u8 rtl_evm_db_to_percentage(char value) { - char ret_val; - ret_val = value; + char ret_val = clamp(-value, 0, 33) * 3; - if (ret_val >= 0) - ret_val = 0; - if (ret_val <= -33) - ret_val = -33; - ret_val = 0 - ret_val; - ret_val *= 3; if (ret_val == 99) ret_val = 100; @@ -55,21 +48,6 @@ u8 rtl_evm_db_to_percentage(char value) } EXPORT_SYMBOL(rtl_evm_db_to_percentage); -u8 rtl_evm_dbm_jaguar(char value) -{ - char ret_val = value; - - /* -33dB~0dB to 33dB ~ 0dB*/ - if (ret_val == -128) - ret_val = 127; - else if (ret_val < 0) - ret_val = 0 - ret_val; - - ret_val = ret_val >> 1; - return ret_val; -} -EXPORT_SYMBOL(rtl_evm_dbm_jaguar); - static long rtl_translate_todbm(struct ieee80211_hw *hw, u8 signal_strength_index) { diff --git a/drivers/net/wireless/rtlwifi/stats.h b/drivers/net/wireless/rtlwifi/stats.h index aa4eec80ccf7..2b57dffef572 100644 --- a/drivers/net/wireless/rtlwifi/stats.h +++ b/drivers/net/wireless/rtlwifi/stats.h @@ -35,7 +35,6 @@ u8 rtl_query_rxpwrpercentage(char antpower); u8 rtl_evm_db_to_percentage(char value); -u8 rtl_evm_dbm_jaguar(char value); long rtl_signal_scale_mapping(struct ieee80211_hw *hw, long currsig); void rtl_process_phyinfo(struct ieee80211_hw *hw, u8 *buffer, struct rtl_stats *pstatus); diff --git a/drivers/net/wireless/ti/wl18xx/debugfs.c b/drivers/net/wireless/ti/wl18xx/debugfs.c index c93fae95baac..5fbd2230f372 100644 --- a/drivers/net/wireless/ti/wl18xx/debugfs.c +++ b/drivers/net/wireless/ti/wl18xx/debugfs.c @@ -139,7 +139,7 @@ WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, protection_filter, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, accum_arp_pend_requests, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, max_arp_queue_dep, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(rx_rate, rx_frames_per_rates, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(rx_rate, rx_frames_per_rates, 50); WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(aggr_size, tx_agg_vs_rate, AGGR_STATS_TX_AGG*AGGR_STATS_TX_RATE); diff --git a/drivers/net/wireless/ti/wlcore/debugfs.h b/drivers/net/wireless/ti/wlcore/debugfs.h index 0f2cfb0d2a9e..bf14676e6515 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.h +++ b/drivers/net/wireless/ti/wlcore/debugfs.h @@ -26,8 +26,8 @@ #include "wlcore.h" -int wl1271_format_buffer(char __user *userbuf, size_t count, - loff_t *ppos, char *fmt, ...); +__printf(4, 5) int wl1271_format_buffer(char __user *userbuf, size_t count, + loff_t *ppos, char *fmt, ...); int wl1271_debugfs_init(struct wl1271 *wl); void wl1271_debugfs_exit(struct wl1271 *wl); diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index e9b960f0ff32..720aaf6313d2 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -1008,8 +1008,7 @@ err: static int xennet_change_mtu(struct net_device *dev, int mtu) { - int max = xennet_can_sg(dev) ? - XEN_NETIF_MAX_TX_SIZE - MAX_TCP_HEADER : ETH_DATA_LEN; + int max = xennet_can_sg(dev) ? XEN_NETIF_MAX_TX_SIZE : ETH_DATA_LEN; if (mtu > max) return -EINVAL; @@ -1279,8 +1278,6 @@ static struct net_device *xennet_create_dev(struct xenbus_device *dev) netdev->ethtool_ops = &xennet_ethtool_ops; SET_NETDEV_DEV(netdev, &dev->dev); - netif_set_gso_max_size(netdev, XEN_NETIF_MAX_TX_SIZE - MAX_TCP_HEADER); - np->netdev = netdev; netif_carrier_off(netdev); diff --git a/drivers/of/address.c b/drivers/of/address.c index ad2906919d45..78a7dcbec7d8 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -450,12 +450,17 @@ static struct of_bus *of_match_bus(struct device_node *np) return NULL; } -static int of_empty_ranges_quirk(void) +static int of_empty_ranges_quirk(struct device_node *np) { if (IS_ENABLED(CONFIG_PPC)) { - /* To save cycles, we cache the result */ + /* To save cycles, we cache the result for global "Mac" setting */ static int quirk_state = -1; + /* PA-SEMI sdc DT bug */ + if (of_device_is_compatible(np, "1682m-sdc")) + return true; + + /* Make quirk cached */ if (quirk_state < 0) quirk_state = of_machine_is_compatible("Power Macintosh") || @@ -490,7 +495,7 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus, * This code is only enabled on powerpc. --gcl */ ranges = of_get_property(parent, rprop, &rlen); - if (ranges == NULL && !of_empty_ranges_quirk()) { + if (ranges == NULL && !of_empty_ranges_quirk(parent)) { pr_debug("OF: no ranges; cannot translate\n"); return 1; } diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c index 9205f433573c..18198316b6cf 100644 --- a/drivers/regulator/palmas-regulator.c +++ b/drivers/regulator/palmas-regulator.c @@ -1572,6 +1572,10 @@ static int palmas_regulators_probe(struct platform_device *pdev) if (!pmic) return -ENOMEM; + if (of_device_is_compatible(node, "ti,tps659038-pmic")) + palmas_generic_regs_info[PALMAS_REG_REGEN2].ctrl_addr = + TPS659038_REGEN2_CTRL; + pmic->dev = &pdev->dev; pmic->palmas = palmas; palmas->pmic = pmic; diff --git a/drivers/rtc/rtc-mrst.c b/drivers/rtc/rtc-mrst.c index e2436d140175..3a6fd3a8a2ec 100644 --- a/drivers/rtc/rtc-mrst.c +++ b/drivers/rtc/rtc-mrst.c @@ -413,8 +413,8 @@ static void rtc_mrst_do_remove(struct device *dev) mrst->dev = NULL; } -#ifdef CONFIG_PM -static int mrst_suspend(struct device *dev, pm_message_t mesg) +#ifdef CONFIG_PM_SLEEP +static int mrst_suspend(struct device *dev) { struct mrst_rtc *mrst = dev_get_drvdata(dev); unsigned char tmp; @@ -453,7 +453,7 @@ static int mrst_suspend(struct device *dev, pm_message_t mesg) */ static inline int mrst_poweroff(struct device *dev) { - return mrst_suspend(dev, PMSG_HIBERNATE); + return mrst_suspend(dev); } static int mrst_resume(struct device *dev) @@ -490,9 +490,11 @@ static int mrst_resume(struct device *dev) return 0; } +static SIMPLE_DEV_PM_OPS(mrst_pm_ops, mrst_suspend, mrst_resume); +#define MRST_PM_OPS (&mrst_pm_ops) + #else -#define mrst_suspend NULL -#define mrst_resume NULL +#define MRST_PM_OPS NULL static inline int mrst_poweroff(struct device *dev) { @@ -529,9 +531,8 @@ static struct platform_driver vrtc_mrst_platform_driver = { .remove = vrtc_mrst_platform_remove, .shutdown = vrtc_mrst_platform_shutdown, .driver = { - .name = (char *) driver_name, - .suspend = mrst_suspend, - .resume = mrst_resume, + .name = driver_name, + .pm = MRST_PM_OPS, } }; diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 9219953ee949..d9afc51af7d3 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -6815,7 +6815,8 @@ static struct ata_port_operations ipr_sata_ops = { }; static struct ata_port_info sata_port_info = { - .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA, + .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | + ATA_FLAG_SAS_HOST, .pio_mask = ATA_PIO4_ONLY, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 932d9cc98d2f..9c706d8c1441 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -547,7 +547,8 @@ static struct ata_port_operations sas_sata_ops = { }; static struct ata_port_info sata_port_info = { - .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ, + .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ | + ATA_FLAG_SAS_HOST, .pio_mask = ATA_PIO4, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 3ce39d10fafb..4f8c798e0633 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -108,7 +108,8 @@ static void dw_spi_dma_tx_done(void *arg) { struct dw_spi *dws = arg; - if (test_and_clear_bit(TX_BUSY, &dws->dma_chan_busy) & BIT(RX_BUSY)) + clear_bit(TX_BUSY, &dws->dma_chan_busy); + if (test_bit(RX_BUSY, &dws->dma_chan_busy)) return; dw_spi_xfer_done(dws); } @@ -156,7 +157,8 @@ static void dw_spi_dma_rx_done(void *arg) { struct dw_spi *dws = arg; - if (test_and_clear_bit(RX_BUSY, &dws->dma_chan_busy) & BIT(TX_BUSY)) + clear_bit(RX_BUSY, &dws->dma_chan_busy); + if (test_bit(TX_BUSY, &dws->dma_chan_busy)) return; dw_spi_xfer_done(dws); } diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c index ff9cdbdb6672..2b2c359f5a50 100644 --- a/drivers/spi/spi-qup.c +++ b/drivers/spi/spi-qup.c @@ -498,7 +498,7 @@ static int spi_qup_probe(struct platform_device *pdev) struct resource *res; struct device *dev; void __iomem *base; - u32 max_freq, iomode; + u32 max_freq, iomode, num_cs; int ret, irq, size; dev = &pdev->dev; @@ -550,10 +550,11 @@ static int spi_qup_probe(struct platform_device *pdev) } /* use num-cs unless not present or out of range */ - if (of_property_read_u16(dev->of_node, "num-cs", - &master->num_chipselect) || - (master->num_chipselect > SPI_NUM_CHIPSELECTS)) + if (of_property_read_u32(dev->of_node, "num-cs", &num_cs) || + num_cs > SPI_NUM_CHIPSELECTS) master->num_chipselect = SPI_NUM_CHIPSELECTS; + else + master->num_chipselect = num_cs; master->bus_num = pdev->id; master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP; diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index c64a3e59fce3..57a195041dc7 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1105,13 +1105,14 @@ void spi_finalize_current_message(struct spi_master *master) "failed to unprepare message: %d\n", ret); } } + + trace_spi_message_done(mesg); + master->cur_msg_prepared = false; mesg->state = NULL; if (mesg->complete) mesg->complete(mesg->context); - - trace_spi_message_done(mesg); } EXPORT_SYMBOL_GPL(spi_finalize_current_message); diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig index 24183028bd71..6d5b38d69578 100644 --- a/drivers/staging/iio/Kconfig +++ b/drivers/staging/iio/Kconfig @@ -38,6 +38,7 @@ config IIO_SIMPLE_DUMMY_EVENTS config IIO_SIMPLE_DUMMY_BUFFER bool "Buffered capture support" select IIO_BUFFER + select IIO_TRIGGER select IIO_KFIFO_BUF help Add buffered data capture to the simple dummy driver. diff --git a/drivers/staging/iio/magnetometer/hmc5843_core.c b/drivers/staging/iio/magnetometer/hmc5843_core.c index fd171d8b38fb..90cc18b703cf 100644 --- a/drivers/staging/iio/magnetometer/hmc5843_core.c +++ b/drivers/staging/iio/magnetometer/hmc5843_core.c @@ -592,6 +592,7 @@ int hmc5843_common_probe(struct device *dev, struct regmap *regmap, mutex_init(&data->lock); indio_dev->dev.parent = dev; + indio_dev->name = dev->driver->name; indio_dev->info = &hmc5843_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = data->variant->channels; diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index b1893f3f88f1..3ad1458bfeb0 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -921,6 +921,9 @@ static void lpuart_setup_watermark(struct lpuart_port *sport) writeb(val | UARTPFIFO_TXFE | UARTPFIFO_RXFE, sport->port.membase + UARTPFIFO); + /* explicitly clear RDRF */ + readb(sport->port.membase + UARTSR1); + /* flush Tx and Rx FIFO */ writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH, sport->port.membase + UARTCFIFO); @@ -1076,6 +1079,8 @@ static int lpuart_startup(struct uart_port *port) sport->txfifo_size = 0x1 << (((temp >> UARTPFIFO_TXSIZE_OFF) & UARTPFIFO_FIFOSIZE_MASK) + 1); + sport->port.fifosize = sport->txfifo_size; + sport->rxfifo_size = 0x1 << (((temp >> UARTPFIFO_RXSIZE_OFF) & UARTPFIFO_FIFOSIZE_MASK) + 1); diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index af821a908720..cf08876922f1 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -963,6 +963,7 @@ static void s3c24xx_serial_shutdown(struct uart_port *port) free_irq(ourport->tx_irq, ourport); tx_enabled(port) = 0; ourport->tx_claimed = 0; + ourport->tx_mode = 0; } if (ourport->rx_claimed) { diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index a7865c4b0498..0827d7c96527 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -387,6 +387,10 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, status = PORT_PLC; port_change_bit = "link state"; break; + case USB_PORT_FEAT_C_PORT_CONFIG_ERROR: + status = PORT_CEC; + port_change_bit = "config error"; + break; default: /* Should never happen */ return; @@ -588,6 +592,8 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, status |= USB_PORT_STAT_C_LINK_STATE << 16; if ((raw_port_status & PORT_WRC)) status |= USB_PORT_STAT_C_BH_RESET << 16; + if ((raw_port_status & PORT_CEC)) + status |= USB_PORT_STAT_C_CONFIG_ERROR << 16; } if (hcd->speed != HCD_USB3) { @@ -1005,6 +1011,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case USB_PORT_FEAT_C_OVER_CURRENT: case USB_PORT_FEAT_C_ENABLE: case USB_PORT_FEAT_C_PORT_LINK_STATE: + case USB_PORT_FEAT_C_PORT_CONFIG_ERROR: xhci_clear_port_change_bit(xhci, wValue, wIndex, port_array[wIndex], temp); break; @@ -1069,7 +1076,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) */ status = bus_state->resuming_ports; - mask = PORT_CSC | PORT_PEC | PORT_OCC | PORT_PLC | PORT_WRC; + mask = PORT_CSC | PORT_PEC | PORT_OCC | PORT_PLC | PORT_WRC | PORT_CEC; spin_lock_irqsave(&xhci->lock, flags); /* For each port, did anything change? If so, set that bit in buf. */ diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index fd53c9ebd662..2af32e26fafc 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -115,6 +115,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) if (pdev->vendor == PCI_VENDOR_ID_INTEL) { xhci->quirks |= XHCI_LPM_SUPPORT; xhci->quirks |= XHCI_INTEL_HOST; + xhci->quirks |= XHCI_AVOID_BEI; } if (pdev->vendor == PCI_VENDOR_ID_INTEL && pdev->device == PCI_DEVICE_ID_INTEL_PANTHERPOINT_XHCI) { @@ -130,7 +131,6 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) * PPT chipsets. */ xhci->quirks |= XHCI_SPURIOUS_REBOOT; - xhci->quirks |= XHCI_AVOID_BEI; } if (pdev->vendor == PCI_VENDOR_ID_INTEL && pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI) { diff --git a/drivers/usb/isp1760/isp1760-udc.c b/drivers/usb/isp1760/isp1760-udc.c index f32c292cc868..3fc4fe770253 100644 --- a/drivers/usb/isp1760/isp1760-udc.c +++ b/drivers/usb/isp1760/isp1760-udc.c @@ -1203,7 +1203,7 @@ static int isp1760_udc_start(struct usb_gadget *gadget, if (udc->driver) { dev_err(udc->isp->dev, "UDC already has a gadget driver\n"); - spin_unlock(&udc->lock); + spin_unlock_irqrestore(&udc->lock, flags); return -EBUSY; } diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 3086dec0ef53..8eb68a31cab6 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -604,6 +604,7 @@ static const struct usb_device_id id_table_combined[] = { .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(FTDI_VID, FTDI_NT_ORIONLXM_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE(FTDI_VID, FTDI_SYNAPSE_SS200_PID) }, /* * ELV devices: */ @@ -1883,8 +1884,12 @@ static int ftdi_8u2232c_probe(struct usb_serial *serial) { struct usb_device *udev = serial->dev; - if ((udev->manufacturer && !strcmp(udev->manufacturer, "CALAO Systems")) || - (udev->product && !strcmp(udev->product, "BeagleBone/XDS100V2"))) + if (udev->manufacturer && !strcmp(udev->manufacturer, "CALAO Systems")) + return ftdi_jtag_probe(serial); + + if (udev->product && + (!strcmp(udev->product, "BeagleBone/XDS100V2") || + !strcmp(udev->product, "SNAP Connect E10"))) return ftdi_jtag_probe(serial); return 0; diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 56b1b55c4751..4e4f46f3c89c 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -561,6 +561,12 @@ */ #define FTDI_NT_ORIONLXM_PID 0x7c90 /* OrionLXm Substation Automation Platform */ +/* + * Synapse Wireless product ids (FTDI_VID) + * http://www.synapse-wireless.com + */ +#define FTDI_SYNAPSE_SS200_PID 0x9090 /* SS200 - SNAP Stick 200 */ + /********************************/ /** third-party VID/PID combos **/ diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index dd97d8b572c3..4f7e072e4e00 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -61,6 +61,7 @@ struct keyspan_pda_private { /* For Xircom PGSDB9 and older Entrega version of the same device */ #define XIRCOM_VENDOR_ID 0x085a #define XIRCOM_FAKE_ID 0x8027 +#define XIRCOM_FAKE_ID_2 0x8025 /* "PGMFHUB" serial */ #define ENTREGA_VENDOR_ID 0x1645 #define ENTREGA_FAKE_ID 0x8093 @@ -70,6 +71,7 @@ static const struct usb_device_id id_table_combined[] = { #endif #ifdef XIRCOM { USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID) }, + { USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID_2) }, { USB_DEVICE(ENTREGA_VENDOR_ID, ENTREGA_FAKE_ID) }, #endif { USB_DEVICE(KEYSPAN_VENDOR_ID, KEYSPAN_PDA_ID) }, @@ -93,6 +95,7 @@ static const struct usb_device_id id_table_fake[] = { #ifdef XIRCOM static const struct usb_device_id id_table_fake_xircom[] = { { USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID) }, + { USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID_2) }, { USB_DEVICE(ENTREGA_VENDOR_ID, ENTREGA_FAKE_ID) }, { } }; diff --git a/drivers/watchdog/imgpdc_wdt.c b/drivers/watchdog/imgpdc_wdt.c index c8def68d9e4c..0deaa4f971f5 100644 --- a/drivers/watchdog/imgpdc_wdt.c +++ b/drivers/watchdog/imgpdc_wdt.c @@ -42,10 +42,10 @@ #define PDC_WDT_MIN_TIMEOUT 1 #define PDC_WDT_DEF_TIMEOUT 64 -static int heartbeat; +static int heartbeat = PDC_WDT_DEF_TIMEOUT; module_param(heartbeat, int, 0); -MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. " - "(default = " __MODULE_STRING(PDC_WDT_DEF_TIMEOUT) ")"); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds " + "(default=" __MODULE_STRING(PDC_WDT_DEF_TIMEOUT) ")"); static bool nowayout = WATCHDOG_NOWAYOUT; module_param(nowayout, bool, 0); @@ -191,6 +191,7 @@ static int pdc_wdt_probe(struct platform_device *pdev) pdc_wdt->wdt_dev.ops = &pdc_wdt_ops; pdc_wdt->wdt_dev.max_timeout = 1 << PDC_WDT_CONFIG_DELAY_MASK; pdc_wdt->wdt_dev.parent = &pdev->dev; + watchdog_set_drvdata(&pdc_wdt->wdt_dev, pdc_wdt); ret = watchdog_init_timeout(&pdc_wdt->wdt_dev, heartbeat, &pdev->dev); if (ret < 0) { @@ -232,7 +233,6 @@ static int pdc_wdt_probe(struct platform_device *pdev) watchdog_set_nowayout(&pdc_wdt->wdt_dev, nowayout); platform_set_drvdata(pdev, pdc_wdt); - watchdog_set_drvdata(&pdc_wdt->wdt_dev, pdc_wdt); ret = watchdog_register_device(&pdc_wdt->wdt_dev); if (ret) diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c index a87f6df6e85f..938b987de551 100644 --- a/drivers/watchdog/mtk_wdt.c +++ b/drivers/watchdog/mtk_wdt.c @@ -133,7 +133,7 @@ static int mtk_wdt_start(struct watchdog_device *wdt_dev) u32 reg; struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); void __iomem *wdt_base = mtk_wdt->wdt_base; - u32 ret; + int ret; ret = mtk_wdt_set_timeout(wdt_dev, wdt_dev->timeout); if (ret < 0) diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig index b812462083fc..94d96809e686 100644 --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig @@ -55,6 +55,23 @@ config XEN_BALLOON_MEMORY_HOTPLUG In that case step 3 should be omitted. +config XEN_BALLOON_MEMORY_HOTPLUG_LIMIT + int "Hotplugged memory limit (in GiB) for a PV guest" + default 512 if X86_64 + default 4 if X86_32 + range 0 64 if X86_32 + depends on XEN_HAVE_PVMMU + depends on XEN_BALLOON_MEMORY_HOTPLUG + help + Maxmium amount of memory (in GiB) that a PV guest can be + expanded to when using memory hotplug. + + A PV guest can have more memory than this limit if is + started with a larger maximum. + + This value is used to allocate enough space in internal + tables needed for physical memory administration. + config XEN_SCRUB_PAGES bool "Scrub pages before returning them to system" depends on XEN_BALLOON diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 0b52d92cb2e5..fd933695f232 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -229,6 +229,29 @@ static enum bp_state reserve_additional_memory(long credit) balloon_hotplug = round_up(balloon_hotplug, PAGES_PER_SECTION); nid = memory_add_physaddr_to_nid(hotplug_start_paddr); +#ifdef CONFIG_XEN_HAVE_PVMMU + /* + * add_memory() will build page tables for the new memory so + * the p2m must contain invalid entries so the correct + * non-present PTEs will be written. + * + * If a failure occurs, the original (identity) p2m entries + * are not restored since this region is now known not to + * conflict with any devices. + */ + if (!xen_feature(XENFEAT_auto_translated_physmap)) { + unsigned long pfn, i; + + pfn = PFN_DOWN(hotplug_start_paddr); + for (i = 0; i < balloon_hotplug; i++) { + if (!set_phys_to_machine(pfn + i, INVALID_P2M_ENTRY)) { + pr_warn("set_phys_to_machine() failed, no memory added\n"); + return BP_ECANCELED; + } + } + } +#endif + rc = add_memory(nid, hotplug_start_paddr, balloon_hotplug << PAGE_SHIFT); if (rc) { |